为全志D1开发板移植LVGL日历控件和显示天气

利用TCP封装HTTP包请求天气信息

Linux还真是逐步熟悉中,现在才了解到Linux即没有原生的GUI,也没有应用层协议栈,所以要实现HTTP应用,必须利用TCP然后自己封装HTTP数据包。本篇即记录封装HTTP数据包,到心知天气请求天气信息的案例实现过程。

1、心知天气API说明
心知天气应该是当下国内使用很普遍的一个天气数据站点。相关注册和使用过程,这里就不再啰嗦了,不清楚的朋友可以自己到官网上查看(https://www.seniverse.com/)。

本例仅测试实时天气数据获取,天气相关数据只有“状态(晴朗之类)”和“气温”,请求接口地址如下:
在这里插入图片描述

可以看到请求地址给的是域名,TCP连接需要直接给IP地址,所以用ping来获取其IP为“116.62.81.138”,端口自然是80。

在这里插入图片描述

得到IP地址后,先不着急编程,通过网络助手实验一把,具体过程是:选择TCP Client,连接对方IP和端口(116.62.81.138:80),然后将请求地址前加上方法字串“GET”,结尾还要有两个回车换行“\r\n\r\n”。初次测试时,忘记了回车换行符没有成功,加上后就好了。

在这里插入图片描述

在这里插入图片描述

封装好的数据包是:“GET https://api.thinkpage.cn/v3/weather/now.json?key=yourkey&location=tianjin&language=en&unit=c\r\n\r\n”。

2、JSON分析
请求到的数据是JSON格式,贴到Json.cn(https://www.json.cn/)的在线工具里,可以更清晰的看到其结构。

在这里插入图片描述

可以看到请求实时数据(now.json),得到一个JSON对象,包含一个“results”引导的JSON数组,且数组只有一个元素,元素中又包含“location”、“now”和“last_update”三个JSON对象,内部还有键值对。

既然是开发Linux API的C程序,当然利用cJSON库来帮助进行数据解析了。本人使用的库是从网上搜到的一个百度网盘分享。

链接:https://pan.baidu.com/s/1DQynsdlNyIvsVXmf4W5b8Q
提取码:ww4z

3、请求天气案例
具体思路就是建立TCP Client连接心知天气的Server,然后发送请求包,得到响应包,解析并打印出结果,案例比较简单做成单次的——开启即运行到底,代码如下:

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>#include "cJSON.h"#define SERVER_IP      "116.62.81.138"
#define SERVER_PORT    80
#define NOW            "now.json"
#define DAILY          "daily.json"
#define API_KEY        "SK0LJ8FI2TP0L-IsQ"
#define CITY           "tianjin"
#define REQ_PACK       "GET https://api.thinkpage.cn/v3/weather/%s?key=%s&location=%s&language=en&unit=c\r\n\r\n"
#define N              1024
#define errlog(errmsg) do{ perror(errmsg);\printf("----%s----%s----%d----\n", __FILE__, __func__, __LINE__);\return -1;\} while(0)//struct for weather data
typedef struct {char id[16];char name[32];char country[16];char path[64];char timezone[32];char tz_offset[16];char text[16];char code[4];char temp[8];char last_update[32];
} weather_t;//parse function & print weather_t data function
void aita_ParseJsonNow(char *json, weather_t *w);
void aita_PrintWeather(weather_t *w);int main(int argc, const char *argv[]) {int sockfd;struct sockaddr_in serveraddr;socklen_t addrlen = sizeof(serveraddr);char sendbuf[N] = "";char recvbuf[N] = "";weather_t weather = {0};
//create socketif((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {errlog("socket error");}
//connect to server of seniverse.comserveraddr.sin_family = AF_INET;serveraddr.sin_addr.s_addr = inet_addr(SERVER_IP);serveraddr.sin_port = htons(SERVER_PORT);if((connect(sockfd, (struct sockaddr*)&serveraddr, addrlen)) < 0) {errlog("connect error");}
//build & send request packagesprintf(sendbuf, REQ_PACK, NOW, API_KEY, CITY);if(send(sockfd, sendbuf, N, 0) < 0) {errlog("send error");}
//waiting server responseif(recv(sockfd, recvbuf, N, 0) < 0) {errlog("recv error");}printf("recv: %s\n", recvbuf);
//parse & print dataaita_ParseJsonNow(recvbuf, &weather);aita_PrintWeather(&weather);close(sockfd);return 0;
}void aita_ParseJsonNow(char *msg, weather_t *w) {cJSON *json, *ja, *jo, *josub, *item;json = cJSON_Parse(msg); //parse string to cJSON typeif(json == NULL) {printf("json type cast error: %s", cJSON_GetErrorPtr());return;} else {printf("parse now pack\n");if((ja=cJSON_GetObjectItem(json, "results")) != NULL) { //get results arrayif((jo=cJSON_GetArrayItem(ja, 0)) != NULL) {        //get array[0](the only item)//get location objectif((josub=cJSON_GetObjectItem(jo, "location")) != NULL) {if((item=cJSON_GetObjectItem(josub, "id")) != NULL) {memcpy(w->id, item->valuestring, strlen(item->valuestring));}if((item=cJSON_GetObjectItem(josub, "name")) != NULL) {memcpy(w->name, item->valuestring, strlen(item->valuestring));}if((item=cJSON_GetObjectItem(josub, "country")) != NULL) {memcpy(w->country, item->valuestring, strlen(item->valuestring));}if((item=cJSON_GetObjectItem(josub, "path")) != NULL) {memcpy(w->path, item->valuestring, strlen(item->valuestring));}if((item=cJSON_GetObjectItem(josub, "timezone")) != NULL) {memcpy(w->timezone, item->valuestring, strlen(item->valuestring));}if((item=cJSON_GetObjectItem(josub, "timezone_offset")) != NULL) {memcpy(w->tz_offset, item->valuestring, strlen(item->valuestring));}}//get now objectif((josub=cJSON_GetObjectItem(jo, "now")) != NULL) {if((item=cJSON_GetObjectItem(josub, "text")) != NULL) {memcpy(w->text, item->valuestring, strlen(item->valuestring));}if((item=cJSON_GetObjectItem(josub, "code")) != NULL) {memcpy(w->code, item->valuestring, strlen(item->valuestring));}if((item=cJSON_GetObjectItem(josub, "temperature")) != NULL) {memcpy(w->temp, item->valuestring, strlen(item->valuestring));}}//get last_update objectif((josub=cJSON_GetObjectItem(jo, "last_update")) != NULL) {memcpy(w->last_update, josub->valuestring, strlen(josub->valuestring));                 }}}}//delete original json pack free memorycJSON_Delete(json);return;
}void aita_PrintWeather(weather_t *w) {printf("id: %s\n", w->id);printf("name: %s\n", w->name);printf("country: %s\n", w->country);printf("path: %s\n", w->path);printf("timezone: %s\n", w->timezone);printf("timezone_offset: %s\n", w->tz_offset);printf("text: %s\n", w->text);printf("code: %s\n", w->code);printf("temperature: %s\n", w->temp);printf("last_update: %s\n", w->last_update);
}

项目路径中建立了源文件main.c,编写上述代码,并导入cJSON.c和cJSON.h,编译命令为:“riscv64-unknown-linux-gnu-gcc main.c cJSON.c -o weather -lm”。因为cJSON会用到math库,而它需要“-lm”来动态链接。

在这里插入图片描述

在这里插入图片描述

lvgl显示图片和本地时间

1、lvgl的图片显示
lvgl框架中图片可以是一个文件也可以是一个变量(数组形式的图片码),当然文件还需要初始化lvgl对文件系统的接口,本例暂以变量形式提供。

应用要显示图片,则需要引入一个图片控件,然后设置它的数据源——使用“lv_img_set_src()”函数。示例如下:

lv_obj_t * icon = lv_img_create(lv_scr_act(), NULL);
/*From variable*/
lv_img_set_src(icon, &my_icon_dsc);

上述代码中“icon”是一个lvgl对象指针,通过“lv_img_create()”实例化,则对应图片控件。设置数据源时传入参数“my_icon_dsc”是lvgl中的图片描述符数据结构“lv_img_dsc_t”——本身是一个结构体类型,其定义源码如下:

//in “../lvgl/src/draw/lv_img_buf.h”
typedef struct {uint32_t cf : 5;          /*Color format: See `lv_img_color_format_t`*/uint32_t always_zero : 3; /*It the upper bits of the first byte. Always zero to look like anon-printable character*/uint32_t reserved : 2; /*Reserved to be used later*/uint32_t w : 11; /*Width of the image map*/uint32_t h : 11; /*Height of the image map*/
} lv_img_header_t;typedef struct {lv_img_header_t header; /**< A header describing the basics of the image*/uint32_t data_size;     /**< Size of the image in bytes*/const uint8_t * data;   /**< Pointer to the data of the image*/
} lv_img_dsc_t;

示例代码中,图片描述符变量的定义过程如下代码:

uint8_t my_icon_data[] = {0x00, 0x01, 0x02, ...};static lv_img_dsc_t my_icon_dsc = {![8.png](/assets/uploads/files/1653270047514-8.png) .header.always_zero = 0,.header.w = 80,.header.h = 60,.data_size = 80 * 60 * LV_COLOR_DEPTH / 8,.header.cf = LV_IMG_CF_TRUE_COLOR,          /*Set the color format*/.data = my_icon_data,
};

其中,枚举“LV_IMG_CF_TRUE_COLOR”是色彩格式定义,表示RGB格式。

宏“LV_COLOR_DEPTH”则定义色彩深度,它位于“lv_conf.h”,用户可以自定义。本例中设置为32,即4字节的ARGB8888格式。

2、时间获取
86板的Tina Linux可以通过C time库轻松地获得本地时间等数据。本例使用的API有:time()、localtime()、strftime()以及time_t、struct tm。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

3、图片和时间显示案例
本例继续使用线程管理lvgl刷新,创建1s周期的lvgl定时器,在定时器回调中获取本地时间并格式化输出。另外,系统初始时显示一个“天津”的Logo,而且初始即做一次时间获取和输出(如果不做,初始刹那label会显示默认“text”字样)。

图片码通过软件“Img2Lcd”获取,软件配置方式如下图所示。图片生成的数组有72008个字节,被放置到头文件“aita_logo.h”。

在这里插入图片描述

/* Includes ------------------------------------------------------- */
#include "lvgl/lvgl.h"
#include "lv_drivers/display/fbdev.h"
#include "lv_drivers/indev/evdev.h"
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <time.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "aita_logo.h"/* Private macro -------------------------------------------------- */
#define AITA_DISP_BUF_SIZE     (128 * 1024)
#define AITA_SCREEN_WIDTH      480
#define AITA_SCREEN_HEIGHT     480
#define AITA_TITLE_STRING      "AITA Weather for LicheeRV with LVGL"
#define SEND_PERIOD            1000
#define errlog(errmsg)         do{ perror(errmsg);\printf("----%s----%s----%d----\n", __FILE__, __func__, __LINE__);\return;\} while(0)/* Global variables ----------------------------------------------- */
lv_indev_t *aita_indev;   //pointer of indev
lv_obj_t *sys_scr;        //pointer of system screen instance
lv_obj_t *head_label;     //pointer of title label instance
lv_obj_t *main_label;     //pointer of main label instance
char main_label_text[32]; //main label text string for datetime
lv_obj_t *logo_img;       //pointer of city logo image instance
lv_timer_t *sec_timer;    //pointer of timer instance for tcp polling
pthread_t lvgl_tid;       //lvgl thread id
pthread_t tcprecv_tid;    //tcp receive thread id
pthread_mutex_t lvgl_mutex;  //mutex for lvgl tick
//image descriptor for logo_img
//ARGB8888 image 180*100 which code array is 'tj_logo' 
lv_img_dsc_t img_dsc_city = {.header.always_zero = 0,.header.w = 180,.header.h = 100,.data_size = 18000 * LV_COLOR_SIZE / 8,.header.cf = LV_IMG_CF_TRUE_COLOR,.data = tj_logo,
};/* Private function prototypes ------------------------------------ */
void aita_InitLVGL(void);
void aita_CreateMainUI(void);
void *thread_lvgl(void *arg);
void sec_timer_cb(lv_timer_t *timer);
void aita_InitTimer(void);
void aita_GetTime(void);/* Private functions ---------------------------------------------- */
int main(void) {void *retval;//by author. initialize lvgl including displaybuffer, device for disp & inputaita_InitLVGL();//by author. initialize and register event device
//these code must be in main(), otherwise the touch will fail.static lv_indev_drv_t indev_drv;lv_indev_drv_init(&indev_drv);indev_drv.type = LV_INDEV_TYPE_POINTER; //by author. choice touchpadindev_drv.read_cb = evdev_read;         //by author. input callbackaita_indev = lv_indev_drv_register(&indev_drv);//by author. create the main view when the demo starts up    aita_CreateMainUI();//by author. create a timeraita_InitTimer();//by author. create mutex for lvglif(pthread_mutex_init(&lvgl_mutex, NULL) != 0) {errlog("initialize mutex error");}//by author. create lvgl threadif(pthread_create(&lvgl_tid, NULL, thread_lvgl, (void *)0) != 0) {errlog("create lvgl thread error");}//by author. wait for thread exit, this demo should never be here.pthread_join(lvgl_tid, &retval);printf("lvgl thread exit, return value: %s\n", (char *)retval);pthread_mutex_destroy(&lvgl_mutex);return 0;
}/*Set in lv_conf.h as `LV_TICK_CUSTOM_SYS_TIME_EXPR`*/
uint32_t custom_tick_get(void)
{static uint64_t start_ms = 0;if(start_ms == 0) {struct timeval tv_start;gettimeofday(&tv_start, NULL);start_ms = (tv_start.tv_sec * 1000000 + tv_start.tv_usec) / 1000;}struct timeval tv_now;gettimeofday(&tv_now, NULL);uint64_t now_ms;now_ms = (tv_now.tv_sec * 1000000 + tv_now.tv_usec) / 1000;uint32_t time_ms = now_ms - start_ms;return time_ms;
}void aita_InitLVGL(void) {/*LittlevGL init*/lv_init();/*Linux frame buffer device init*/fbdev_init(); //by author. initialize framebuffer device for displayevdev_init(); //by author. initialize event device for touchpad/*A small buffer for LittlevGL to draw the screen's content*/static lv_color_t buf[AITA_DISP_BUF_SIZE];/*Initialize a descriptor for the buffer*/static lv_disp_draw_buf_t disp_buf;lv_disp_draw_buf_init(&disp_buf, buf, NULL, AITA_DISP_BUF_SIZE);/*Initialize and register a display driver*/static lv_disp_drv_t disp_drv;lv_disp_drv_init(&disp_drv);disp_drv.draw_buf   = &disp_buf;disp_drv.flush_cb   = fbdev_flush;disp_drv.hor_res    = 480;disp_drv.ver_res    = 480;lv_disp_drv_register(&disp_drv);
}void aita_CreateMainUI(void) {//by author. create system screen which is basic graphic levelsys_scr = lv_obj_create(lv_scr_act());lv_obj_set_size(sys_scr, AITA_SCREEN_WIDTH, AITA_SCREEN_HEIGHT);//by author. create the main title which is just a labelhead_label = lv_label_create(sys_scr);lv_label_set_text(head_label, AITA_TITLE_STRING);lv_obj_align(head_label, LV_ALIGN_TOP_MID, 0, 10);//by author. create the city logo imagelogo_img = lv_img_create(sys_scr);lv_img_set_src(logo_img, &img_dsc_city);lv_obj_align(logo_img, LV_ALIGN_TOP_LEFT, 10, 40);//by author. get local time and show stringaita_GetTime();main_label = lv_label_create(sys_scr);lv_label_set_text(main_label, main_label_text);lv_obj_align(main_label, LV_ALIGN_TOP_LEFT, 200, 40);lv_obj_set_style_text_font(main_label, &lv_font_montserrat_20, 0);
}//by author. lvgl core thread function
void *thread_lvgl(void *arg) {while(1) {pthread_mutex_lock(&lvgl_mutex);lv_task_handler();pthread_mutex_unlock(&lvgl_mutex);usleep(5000); /* sleep for 5 ms */}
}//by author. sec_timer callback which refresh date string
void sec_timer_cb(lv_timer_t *timer) {aita_GetTime();lv_label_set_text(main_label, main_label_text);  
}
//by author. initialize timer for 1s timing
void aita_InitTimer(void) {sec_timer = lv_timer_create(sec_timer_cb, 1000, NULL);lv_timer_set_repeat_count(sec_timer, -1);
}
//by author. get local time string
void aita_GetTime(void) {time_t    tsec;struct tm *tlocal;tsec = time(NULL);tlocal = localtime(&tsec);memset(main_label_text, 0, 32);strftime(main_label_text, 32, "%Y-%m-%d %a %H:%M:%S", tlocal);
}

在这里插入图片描述

lvgl日历控件和显示天气

本篇结合本人前两篇的HTTP请求天气数据(通过“心知天气”网站)和lvgl显示图片及时间,在案例主界面上增加了日历显示和实时天气显示,先直接上图。

在这里插入图片描述

1、lvgl日历控件
calendar是lvgl提供的“Extra widgets”组件之一,需要注意的是8.0版本后有几个API的传参发生了变化,本例使用8.3版本,设置日期是需要同时传递“年、月、日”三个参数。

本例使用的API有:lv_calendar_create()、lv_canlendar_set_today_date()、lv_calendar_set_showed_date()和lv_calendar_header_arrow_create()。

lv_calendar_create()函数用于实例化calendar控件,传参是控件的父容器指针,本例使用“lv_scr_act()”即系统屏幕。

在这里插入图片描述

lv_canlendar_set_today_date()函数用于设置当前日期,本人使用发现lvgl是附带万年历功能的,只要设置好当天的年月日,就可以自动生成正确的日历排布。函数传参分别是控件指针和年月日数据。

关于年月日参数有两点注意事项。一是v7版本中,传参通过lv_calendar_date_t结构体,其包含年月日三个成员。二是如果使用了C time库的struct tm,注意其中年份需要加上“1900”,而月份则需要加“1”。

在这里插入图片描述

lv_calendar_set_showed_date()函数用于设置日历当前显示页,也就是设置当前月份。本人实验的效果是当天日期框会自动高亮,如果想设置多个高亮日期,可以使用函数lv_calendar_set_highlighted_dates()。

在这里插入图片描述

lv_calendar_header_arrow_create()函数用于向日历控件顶部增加“左、右箭头”两个按钮用于日历翻页(一页是一月)。此外,还有函数lv_calendar_header_dropdown_create()则是设置两个下拉列表分别用于选择年份和月份。这两个函数都只用传递日历控件指针一个参数,且是8.1版本新增API。

2、日历和天气显示案例
本案例的思路是:1)在应用启动时,获取当前时间(上篇中已经实现),然后将时间保存在全局量“struct tm today”中,并利用变量“today”来初始化日历控件的日期数据。2)上篇实现的时间显示案例,通过lvgl定时器,每秒获取本地数据,此处在定时器回调中再增加一个每到正分钟发送“Linux条件变量”。3)同时,应用启动时建立两个线程——lvgl线程和请求天气线程,请求天气线程等待条件变量到来,开启一次天气数据请求过程。

本例代码结合文章上半部分已经给出的案例,这里只给出改变部分。

/* Includes ------------------------------------------------------- */
// 增加头文件,cJSON用于解析JSON格式的天气数据
#include "cJSON.h"/* Private macro -------------------------------------------------- */
// 增加请求天气数据相关的宏定义
#define HTTP_IP                "116.62.81.138"
#define HTTP_PORT              80
#define NOW                    "now.json"
#define API_KEY                "SK0LJ8FI2TP0L-IsQ"
#define CITY                   "tianjin"
#define REQ_PACK               "GET https://api.thinkpage.cn/v3/weather/%s?key=%s&location=%s&language=en&unit=c\r\n\r\n"
#define N                      1024
// struct for weather data 建立结构体存储解析后的天气数据
typedef struct {char id[16];char name[32];char country[16];char path[64];char timezone[32];char tz_offset[16];char text[16];char code[4];char temp[8];char last_update[32];
} weather_t;/* Global variables ----------------------------------------------- */
// 增加显示天气的标签控件定义
lv_obj_t *weather_label;  //pointer of weather label instance
// 增加日历控件定义
lv_obj_t  *calendar;      //pointer of calendar instance
// 定义today变量存储当前日期,用于设置日历
struct tm today;          //
// 请求天气的线程ID
pthread_t reqweather_tid; //request weather thread id
// 请求天气线程等待的条件变量(min_cond)
// Linux中需要互斥量包含条件变量的使用,所以定义cond_mutex
pthread_mutex_t cond_mutex;  //mutex for 1-min cond
pthread_cond_t  min_cond; //1-min cond
/* Private functions ---------------------------------------------- */
int main(void) {
// other code from previous demo
// main()函数中创建互斥量、条件变量、请求天气线程
//by author. create mutex for 1-min condif(pthread_mutex_init(&cond_mutex, NULL) != 0) {errlog("initialize cond mutex error");}//by author. create condition for 1-minif(pthread_cond_init(&min_cond, NULL) != 0) {errlog("initialize 1 minute condition error");}//by author. create request weather threadif(pthread_create(&reqweather_tid, NULL, thread_reqweather, (void *)0) != 0) {errlog("create request weather thread error");}//by author. wait for thread exit, this demo should never be here.pthread_join(lvgl_tid, &retval);printf("lvgl thread exit, return value: %s\n", (char *)retval);pthread_join(reqweather_tid, &retval);printf("request weather thread exit, return value: %s\n", (char *)retval);pthread_mutex_destroy(&lvgl_mutex);pthread_mutex_destroy(&cond_mutex);pthread_cond_destroy(&min_cond);return 0;
}void aita_CreateMainUI(void) {
// other code from previous demo
// aita_CreateMainUI()被main()函数调用,初始化主界面。//by author. create the weather labelweather_label = lv_label_create(sys_scr);lv_label_set_text(weather_label, "         ");lv_obj_align(weather_label, LV_ALIGN_TOP_LEFT, 200, 120);//by author. create the calendarcalendar = lv_calendar_create(sys_scr);lv_obj_set_size(calendar, 235, 235);lv_obj_align(calendar, LV_ALIGN_BOTTOM_LEFT, 10, -50);lv_calendar_set_today_date(calendar, today.tm_year+1900, today.tm_mon+1, today.tm_mday);lv_calendar_set_showed_date(calendar, today.tm_year+1900, today.tm_mon+1);lv_calendar_header_arrow_create(calendar);
}// 增加正分钟发送条件变量
void sec_timer_cb(lv_timer_t *timer) {aita_GetTime();lv_label_set_text(main_label, main_label_text);if(today.tm_sec == 0) {//by author. send condition signal per whole minutepthread_cond_signal(&min_cond);}
}
// 增加对today的赋值
void aita_GetTime(void) {time_t    tsec;struct tm *tlocal;tsec = time(NULL);tlocal = localtime(&tsec);today = *tlocal;memset(main_label_text, 0, 32);strftime(main_label_text, 32, "%Y-%m-%d %a %H:%M:%S", tlocal);
}// 请求天气线程业务逻辑
void *thread_reqweather(void *arg) {int sockfd;struct sockaddr_in serveraddr;socklen_t addrlen = sizeof(serveraddr);char sendbuf[N] = "";char recvbuf[N] = "";weather_t weather = {0};char w_string[64] = "";while(1) {pthread_mutex_lock(&cond_mutex);pthread_cond_wait(&min_cond, &cond_mutex);pthread_mutex_unlock(&cond_mutex);     //create socketif((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {errlog("socket error");}//connect to server of seniverse.comserveraddr.sin_family = AF_INET;serveraddr.sin_addr.s_addr = inet_addr(HTTP_IP);serveraddr.sin_port = htons(HTTP_PORT);if((connect(sockfd, (struct sockaddr*)&serveraddr, addrlen)) < 0) {errlog("connect error");}//build & send request packagememset(sendbuf, 0, N);sprintf(sendbuf, REQ_PACK, NOW, API_KEY, CITY);if(send(sockfd, sendbuf, N, 0) < 0) {errlog("send error");}//waiting server responseif(recv(sockfd, recvbuf, N, 0) < 0) {errlog("recv error");}printf("recv: %s\n", recvbuf);//parse & print data,下面两个函数来自于“十三”案例aita_ParseJsonNow(recvbuf, &weather);aita_PrintWeather(&weather);close(sockfd);memset(recvbuf, 0, N);//show weather stringmemset(w_string, 0, 64);sprintf(w_string, "weather:%s temperatur:%s", weather.text, weather.temp);pthread_mutex_lock(&lvgl_mutex);lv_label_set_text(weather_label, w_string);pthread_mutex_unlock(&lvgl_mutex);       }
}

另外,本例在lvgl工程中增加了cJSON.c和cJSON.h文件,Makefile也做出了调整,具体如下所示。

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/695020.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Flutter Slider自定义滑块样式 Slider的label标签框常显示

1、自定义Slider滑块样式 Flutter Slider控件的滑块系统样式是一个圆点&#xff0c;thumbShape默认样式是RoundSliderThumbShape&#xff0c;如果想要使用其它的样式就需要自定义一下thumbShape&#xff1b; 例如需要一个上图样式的&#xff08;圆点半透明圆形边框&#xff09…

2024年天津高考数学备考:历年选择题真题练一练(2014~2023)

距离2024年高考还有不到四个月的时间&#xff0c;今天我们来看一下2014~2023年的天津市高考数学的选择题&#xff0c;从过去十年的真题中随机抽取5道题&#xff0c;并且提供解析。后附六分成长独家制作的在线练习集&#xff0c;科学、高效地反复刷这些真题&#xff0c;吃透真题…

ELK入门(四)-logstash

Logstash Logstash 是开源的服务器端数据处理管道&#xff0c;能够同时从多个来源采集数据&#xff0c;转换数据&#xff0c;然后将数据发送到您最喜欢的存储库中。 Logstash 能够动态地采集、转换和传输数据&#xff0c;不受格式或复杂度的影响。利用 Grok 从非结构化数据中…

打造纯Lua组件化开发模式:Unity xLua框架详解

在传统的Unity开发中&#xff0c;通常会使用C#来编写游戏逻辑和组件。但是&#xff0c;随着Lua在游戏开发中的应用越来越广泛&#xff0c;我们可以将游戏逻辑和组件完全用Lua来实现&#xff0c;实现纯Lua的组件化开发模式。这样做的好处是可以更加灵活地修改游戏逻辑&#xff0…

uni-app 开发调试自动打开手机屏幕大小界面(Aidex移动端开发项目)

上效果&#xff1a; 下载Aidex的移动端项目并打开&#xff1a; 若依-ruoyi-AiDex-Uniapp: 若依-Ruoyi APP 移动解决方案&#xff0c;基于uniappuView封装的一套基础模版&#xff0c;开箱即用&#xff0c;免费开源&#xff0c;一份代码多终端适配&#xff0c;支持H5、支付宝小程…

WordPress如何将后台右上角管理员头像去除并调整注销位置及启用注销确认功能?

WordPress后台默认情况下右上角可以看到管理员昵称和头像&#xff0c;将鼠标移动到该昵称上还会出现一个下拉菜单&#xff0c;点击下拉菜单中的“注销”无需我们再次确认就会自动退出。 现在我想将WordPress后台右上角的管理员头像和管理员昵称子菜单去除&#xff0c;并将“注销…

Retrofit2原理分析

Retrofit官网 GitHub上的Retrofit 使用Retrofit进行网络请求的主要步骤 创建一个接口 用于描述HTTP请求。接口里的方法使用注解来标记请求方式、API路径、请求参数等信息。使用Retrofit.Builder().build();配置和创建一个Retrofit实例&#xff1b;调用retrofit.create()方法获…

[C#]winform使用引导APSF和梯度自适应卷积增强夜间雾图像的可见性算法实现夜间雾霾图像的可见度增强

【算法介绍】 提升夜间雾霾图像可见度的技术研究&#xff1a;引导APSF与梯度自适应卷积的应用 随着城市化的快速发展&#xff0c;雾霾现象日益严重&#xff0c;尤其是在夜间&#xff0c;雾霾对图像的可见度造成了极大的影响。因此&#xff0c;提升夜间雾霾图像的可见度成为了…

SAP PP学习笔记03 - SAP中如何设定项目选择

上次这篇文章里面讲了界面的字段显示顺序及是否显示的设置。 并做了 事务代码 控制界面显示的例子。 SAP PP学习笔记02 - PP中配置品目Master时的顺序-CSDN博客 那么&#xff0c;每次控制界面显示什么都要这么挨个 这么设置一遍吗&#xff1f; 那岂不得烦死。 其实SAP里面参…

ABCDE联合创始人BMAN确认出席Hack .Summit() 2024香港Web3盛会

ABCDE联合创始人和普通合伙人BMAN确认出席Hack .Summit() 2024&#xff01; ABCDE联合创始人和普通合伙人BMAN确认出席由 Hack VC 主办&#xff0c;并由 AltLayer 和 Berachain 联合主办&#xff0c;与 SNZ 和数码港合作&#xff0c;由 Techub News 承办的Hack.Summit() 2024区…

使用python构建Android,探索跨平台应用开发Kivy框架

使用python构建Android&#xff0c;探索跨平台应用开发Kivy框架 1. 介绍Kivy框架 Kivy是什么&#xff1f; Kivy是一个开源的Python跨平台应用程序开发框架&#xff0c;旨在帮助开发者快速构建创新的、可扩展的移动应用和多点触控应用。Kivy采用MIT许可证&#xff0c;允许开发…

​【C语言】长篇详解,字符系列篇3-----strstr,strtok,strerror字符串函数的使用【图文详解​】

欢迎来CILMY23的博客喔&#xff0c;本期系列为​【C语言】长篇详解&#xff0c;字符系列篇3-----strstr&#xff0c;strtok&#xff0c;strerror字符串函数的使用【图文详解​】&#xff0c;图文讲解各种字符串函数&#xff0c;带大家更深刻理解C语言中各种字符串函数的应用&am…

CogCopyRegionTool

关于visionpro工具操作原理文章甚少&#xff0c;以下是本人自己查阅visionpro官方文档完成的&#xff1a; “复制区域”工具允许您对单个图像或两个独立的图像执行多个复制操作&#xff1a; 将输入图像的一部分复制到新的输出图像。 1、 将输入图像的一部分复制到现有的目标…

QT-模拟电梯上下楼

QT-模拟电梯上下楼 一、演示效果二、核心程序三、下载链接 一、演示效果 二、核心程序 #include "ElevatorController.h" #include <QGridLayout> #include <QLabel> #include <QGroupBox> #include <QGridLayout> #include <QPushButto…

尾矿库排洪系统结构仿真APP助力尾矿库本质安全

1、背景介绍 尾矿库作为重大危险源之一&#xff0c;在国际灾害事故排名中位列第18位&#xff0c;根据中国钼业2019年8月刊《中国尾矿库溃坝与泄漏事故统计及成因分析》的统计&#xff0c;在46起尾矿库泄漏事故中&#xff0c;由于排洪设施导致的尾矿泄漏事故占比高达1/3&#x…

linux下开发,stm32和arduino,我该何去何从?

linux下开发&#xff0c;stm32和arduino&#xff0c;我该何去何从&#xff1f; 在开始前我有一些资料&#xff0c;是我根据网友给的问题精心整理了一份「stm3的资料从专业入门到高级教程」&#xff0c; 点个关注在评论区回复“888”之后私信回复“888”&#xff0c;全部无偿共…

QT中的多线程有什么作用?

概述 在学习QT线程的时候我们首先要知道的是QT的主线程&#xff0c;也叫GUI线程&#xff0c;意如其名&#xff0c;也就是我们程序的最主要的一个线程&#xff0c;主要负责初始化界面并监听事件循环&#xff0c;并根据事件处理做出界面上的反馈。但是当我们只限于在一个主线程上…

密码学基本概念

密码学基本概念 密码学的安全目标至少包含三个方面&#xff1a; &#xff08;1&#xff09;保密性&#xff08;Confidentiality&#xff09;:信息仅被合法用户访问&#xff08;浏览、阅读、打印等&#xff09;&#xff0c;不被泄露给非授权的用户、实体或过程。 提高保密性的手…

电商+支付双系统项目------实现电商系统中分类模块的开发!

本篇文章主要介绍一下这个项目中电商系统的分类模块开发。电商系统有很多模块&#xff0c;除了分类模块&#xff0c;还有用户模块&#xff0c;购物车模块&#xff0c;订单模块等等。上一篇文章已经讲了用户模块&#xff0c;这篇文章我们讲讲项目中的分类模块。 有的人可能会很…

图文并茂手把手教你MAC运行.net项目(Visual Studio Code-vs code 配置c# .net环境 运行solution)

前提条件 下载安装vscode有一个完整项目 vscode下载插件 C# Dev Kit.NET Core Extension Packvscode-solution-explorer 下载安装.NET SDK 点此进入下载 以Download .NET 6.0为案例 查看mac是arm64还是x64 屏幕左上角苹果图标&#xff0c;点击关于本机处理器&#x…