为全志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://xiahunao.cn/news/2799759.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

【Linux】再谈进程地址空间

目录 一、引入 二、物理内存和外设空间的交互 三、解决页表过大问题 一、引入 我们在往期的博客中有讲解过进程地址空间&#xff1a;【Linux】进程地址空间 但是在上述博客中我们只是对进程地址空间的左边部分详细进行了讲解&#xff0c;下面我们就来谈谈右边的部分&#…

git版本回退在eclipse和命令中的操作

一.背景 老程序员了&#xff0c;熟悉eclipsesvn&#xff0c;git用的不溜。近几年用了git&#xff0c;偶尔修改了某个文件希望放弃本次修改重新恢复到最新版本重新修改。或者回退到某个版本&#xff0c;再修改。记录一下Eclipse中的操作&#xff0c;和命令操作的情况。 二.Ecli…

操作系统导论-课后作业-ch19

1. 本书在第6章中有过介绍&#xff0c;gettimeofday函数最多精确到us&#xff0c;并且大致精确&#xff08;并不完全精确&#xff09;&#xff0c;需要多迭代几次减少误差&#xff0c;循环次数太多也会导致结束时间小于开始时间&#xff08;即回滚&#xff09;的现象&#xff…

每日shell脚本之超级整合程序3.0

每日shell脚本之超级整合程序3.0 本期带来之前的升级版2.0整合脚本程序,学习工作小利器,同时模块化构建方便二次开发。 上图 上源码 #!/usr/bin/bash # *******************************************# # * CDDN : M乔木 # # * qq邮箱 …

社区分享|中华保险基于MeterSphere开展接口自动化测试

中华联合保险集团股份有限公司&#xff08;以下简称为“中华保险”&#xff09;始创于1986年&#xff0c;是全国唯一一家以“中华”冠名的国有控股保险公司。截至2022年12月底&#xff0c;中华保险总资产为1006.06亿元&#xff0c;在全国拥有超过2900个营业网点&#xff0c;员工…

06 分频器设计

分频器简介 实现分频一般有两种方法&#xff0c;一种方法是直接使用 PLL 进行分频&#xff0c;比如在 FPGA 或者 ASIC 设计中&#xff0c;都可以直接使用 PLL 进行分频。但是这种分频有时候受限于 PLL 本身的特性&#xff0c;无法得到频率很低的时钟信号&#xff0c;比如输入 …

Windows安装PHP及在VScode中配置插件,使用PHP输出HelloWorld

安装PHP PHP官网下载地址(8.3版本)&#xff1a;PHP For Windows&#xff1a;二进制文件和源代码发布 点击下载.zip格式压缩包&#xff1a; 历史版本在Old archives中下载。推荐在Documentation download中下载官方文档&#xff0c;方便学习。 下载完成后在一个顺眼的地方解压压…

Point.java

Point.java 让我们来找茬&#xff0c;(⊙o⊙)看看哪里不一样咯&#xff01;&#xff01;&#xff01; package algorithm_graphics_2;/** Copyright (c) 1995, 2008, Oracle and/or its affiliates. All rights reserved.* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject t…

Zabbix 6.2.1 安装

目录 1、监控介绍 监控的重要性 网站的可用性 监控范畴 如何监控 2、Zabbix 介绍 zabbix 简介 zabbix 主要功能 zabbix 监控范畴 Zabbix 监控组件 zabbix 常见进程 zabbix agentd 工作模式 zabbix 环境监控中概念 3、搭建LNMP 拓扑规划 安装MySQL 安装 Nginx …

【视频编解码】M-JPEG压缩、H.264压缩 对比

简介 参考这篇文章&#xff1a;https://blog.csdn.net/qq_41248872/article/details/83590337 写的比较好&#xff0c;这里就不赘述了。 我们在视频传输的时候&#xff0c;需要压缩&#xff0c;常见的压缩包括: jpeg 压缩h264 压缩 当然使用最多的还是 264, 毕竟他的压缩比…

【Flink经济】Flink 内存管理

面临的问题 目前&#xff0c; 大数据计算引擎主要用 Java 或是基于 JVM 的编程语言实现的&#xff0c;例如 Apache Hadoop、 Apache Spark、 Apache Drill、 Apache Flink 等。 Java 语言的好处在于程序员不需要太关注底层内存资源的管理&#xff0c;但同样会面临一个问题&…

【快速搞定Webpack5】修改输出文件目录及自动清理上次打包文件(五)

介绍 默认情况下webpack打包后&#xff0c;我们的图片和js等文件都会被打包到dist目录下&#xff0c;文件多了混淆在一起一方面不利于文件的查找和管理&#xff0c;另外一方面看上去也不美观。 所以今天我们学习的内容就是控制输出后的文件进入不同的目录。 一、配置 新增4…

小米标准模组+MCU 快速上手开发(二)——之模组串口调试

小米标准模组MCU 开发笔记之固件调试 背景技术名词简介● 小米IoT开发者平台● 小米IoT 模组● 固件● OTA● CRC32 固件双串口调试● MHCWB6S-IB 模组资料下载● MHCWB6S-IB 模组管脚图● 上电调试 背景 小米标准模组MCU的开发过程中&#xff0c;由于部分官方资料较为古早&am…

压缩感知常用的重建算法

重建算法的基本概念 在压缩感知&#xff08;Compressed Sensing, CS&#xff09;框架中&#xff0c;重建算法是指将从原始信号中以低于奈奎斯特率采集得到的压缩测量值恢复成完整信号的数学和计算过程。由于信号在采集过程中被压缩&#xff0c;因此重建算法的目标是找到最符合…

MATLAB 导出可编辑的eps格式图像

任务描述&#xff1a;部分期刊要求提交可编辑的eps格式图像&#xff0c;方便美工编辑对图像进行美化 我试了直接print或者在figure窗口导出&#xff0c;发现导出的文件放到Adobe AI中并不能编辑&#xff0c;经Google找到解决办法&#xff1a; %EPS exportgraphics(gcf,myVect…

微信小程序 ---- 慕尚花坊 项目初始化

目录 项目介绍 01. 项目概述 02. 项目演示 03. 项目技术栈 04. 接口文档 申请开发权限 项目初始化 01. 创建项目与项目初始化 02. 自定义构建 npm 集成Sass 03. 集成项目页面文件 04. VsCode 开发小程序项目 项目介绍 01. 项目概述 [慕尚花坊] 是一款 同城鲜花订购…

js设计模式:依赖注入模式

作用: 在对象外部完成两个对象的注入绑定等操作 这样可以将代码解耦,方便维护和扩展 vue中使用use注册其他插件就是在外部创建依赖关系的 示例: class App{constructor(appName,appFun){this.appName appNamethis.appFun appFun}}class Phone{constructor(app) {this.nam…

【C++初阶】类和对象(中)

目录 一.类的6个默认成员函数 1.知识引入 ​编辑 2.构造函数 (1)概念 (2)语法特性 (3)特征 ①问题引入1 ②问题引入2 &#xff08;缺少默认构造函数&#xff09; 3.析构函数 (1)概念 (2)特性 4.拷贝构造函数 (1)概念 (2)特征 ①拷贝构造函数是构造函数的一…

pclpy SOR去除异常值(统计滤波)

pclpy SOR去除异常值-统计滤波 一、算法原理1.背景2.原理 二、代码三、结果1.原点云2.sor处理后的点云&#xff08;内点&#xff09;3.sor处理后的点云&#xff08;外点&#xff09; 四、相关数据 一、算法原理 1.背景 激光扫描通常会生成不同点密度的点云数据集。此外&#…

【OpenSSH+Jenkins搭建项目自动化部署】

OpenSSHJenkins搭建项目自动化部署 一、Windows安装OpenSSH1.下载2.解压3.安装4.启停服务5.SSH免密登录 二、Jenkins安装1.下载2.安装启动3.登录 三、项目自动化部署1.SSH配置2.项目配置3.权限控制 一、Windows安装OpenSSH 1.下载 https://github.com/PowerShell/Win32-0penS…