电子工程师技术服务社区
公告
登录
|
注册
首页
技术问答
厂商活动
正点原子
板卡试用
资源库
下载
文章
社区首页
文章
基于RT-THREAD的桌面小工具
分 享
扫描二维码分享
基于RT-THREAD的桌面小工具
RTThread
小盒子
Rice嵌入式开发
关注
发布时间: 2021-04-07
丨
阅读: 244
## 摘要 - 这个桌面小盒子是之前的东西,一直放着没有整理好。最近有空了就把他整理整理。 - 小盒子主要用来显示时间和天气预报,功能比较简单,其实还有很多可以玩的,懒得弄,所以就把最简单的整理出来。 - 软件是基于rt-thread, UI采用lvgl。 ## 功能主要分为两个部分 ### 第一部分--功能 ##### 功能部分主要分为连个部分,一个是NTP获取实时时间,一个是天气等信息。 1. NTP比较简单,RTT提供了相关API。代码如下: ``` C void get_local_time(void) { time_t now; now = time(RT_NULL); tab_info.cur_tm = localtime(&now); rt_kprintf("time: %2d:%2d", tab_info.cur_tm->tm_hour, tab_info.cur_tm->tm_min); } ``` 2. 获取天气信息,这个也比较简单,通过调用tianqiapi既可以获得。代码如下: ``` C #define GET_WEATHER_URI "http://www.tianqiapi.com/api/?version=v6&cityid=101280601&appid=65251531&appsecret=Yl2bzCYb" struct weather_info { char *response; cJSON *root; cJSON *date; cJSON *cur_temp; cJSON *humidity; cJSON *wea_img; }; int rp_weather_info_get(void) { char *uri = RT_NULL; uri = web_strdup(GET_WEATHER_URI); if (webclient_request(uri, RT_NULL, RT_NULL, (unsigned char **)&info.response) < 0) { rt_kprintf("get weather fail!\n"); return -RT_ERROR; } info.root = cJSON_Parse(info.response); if(info.root != RT_NULL) { info.date = cJSON_GetObjectItem(info.root, "date"); info.cur_temp = cJSON_GetObjectItem(info.root, "tem"); info.humidity = cJSON_GetObjectItem(info.root, "humidity"); info.wea_img = cJSON_GetObjectItem(info.root, "wea_img"); rt_kprintf("date : %s, cur_temp : %s, humidity : %s, wea_img : %s\n", info.date->valuestring, info.cur_temp->valuestring, info.humidity->valuestring, info.wea_img->valuestring); } return RT_EOK; } void rp_weather_info_free(void) { if (info.response != RT_NULL) { web_free(info.response); } if(info.root != RT_NULL) { cJSON_Delete(info.root); info.root = RT_NULL; } } ``` ### 第二部分--UI 1. UI方面我使用了LVGL,这个开始比较方便的UI框架,可移植性搞。网上的教程也比较多。 2. 由于rt-thread的LVGL软件包比较旧,而且有些东西没有移植好,所以我自己重新根据我的平台重新移植一份。 3. 根据LVGL移植流程,需要对接porting下源文件的API回调。主要包含,disp(显示)、indev(输入设备,如触摸,按键)、fs(文件系统)。而本项目只对接disp(显示)、fs(文件系统, 读取SD卡的图片资源) ![](https://RiceChen0.gitee.io/picture/r_plan/24.png) - 其中fs的代码,文件系统我对接的dfs_posix的接口,因为我只使用读文件,所以直接对接部分接口,代码如下: ``` C static lv_fs_res_t fs_open(lv_fs_drv_t * drv, void * file_p, const char * path, lv_fs_mode_t mode) { lv_fs_res_t res = LV_FS_RES_NOT_IMP; int fd = 0; if(mode == LV_FS_MODE_RD) { if((fd = open(path, O_RDONLY)) > 0) { *(file_t *)file_p = fd; res = LV_FS_RES_OK; } else { res = LV_FS_RES_NOT_EX; } } return res; } static lv_fs_res_t fs_close (lv_fs_drv_t * drv, void * file_p) { lv_fs_res_t res = LV_FS_RES_UNKNOWN; int fd = *(file_t *)file_p; if (close(fd) == 0) { res = LV_FS_RES_OK; } return res; } static lv_fs_res_t fs_read (lv_fs_drv_t * drv, void * file_p, void * buf, uint32_t btr, uint32_t * br) { lv_fs_res_t res = LV_FS_RES_UNKNOWN; int fd = *(file_t *)file_p; int read_bytes = read(fd, buf, btr); if (read_bytes >= 0) { *br = read_bytes; res = LV_FS_RES_OK; } return res; } static lv_fs_res_t fs_seek (lv_fs_drv_t * drv, void * file_p, uint32_t pos) { lv_fs_res_t res = LV_FS_RES_UNKNOWN; int fd = *(file_t *)file_p; if (lseek(fd, pos, SEEK_SET) >= 0) { res = LV_FS_RES_OK; } return res; } static lv_fs_res_t fs_tell (lv_fs_drv_t * drv, void * file_p, uint32_t * pos_p) { lv_fs_res_t res = LV_FS_RES_UNKNOWN; int fd = *(file_t *)file_p; off_t pos = lseek(fd, 0, SEEK_CUR); if (pos >= 0) { *pos_p = pos; res = LV_FS_RES_OK; } return res; } void lv_port_fs_init(void) { lv_fs_drv_t fs_drv; fs_init(); lv_fs_drv_init(&fs_drv); fs_drv.file_size = sizeof(file_t); fs_drv.letter = 'S'; fs_drv.open_cb = fs_open; fs_drv.close_cb = fs_close; fs_drv.read_cb = fs_read; fs_drv.seek_cb = fs_seek; fs_drv.tell_cb = fs_tell; lv_fs_drv_register(&fs_drv); } ``` - disp代码,因为是软件SPI,所以我采用的是整屏刷新。 ``` C static void disp_init(void) { } static void disp_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p) { int32_t x; int32_t y; rt_uint8_t *frame_buffer = (rt_uint8_t *)psram_malloc(240 * 240 * 2); if(frame_buffer !=RT_NULL) { for(y = area->y1; y <= area->y2; y++) { for(x = area->x1; x <= area->x2; x++) { frame_buffer[(y * 240 * 2)+(x * 2)] = color_p->full >> 8; frame_buffer[(y * 240 * 2)+(x * 2 + 1)] = color_p->full; color_p++; } } lcd_show_page((rt_uint8_t *)frame_buffer); psram_free(frame_buffer); frame_buffer = RT_NULL; } lv_disp_flush_ready(disp_drv); } void lv_port_disp_init(void) { lv_disp_drv_t disp_drv; disp_init(); static lv_disp_buf_t draw_buf_dsc; static lv_color_t draw_buf[LV_HOR_RES_MAX * LV_VER_RES_MAX]; lv_disp_buf_init(&draw_buf_dsc, draw_buf, NULL, LV_HOR_RES_MAX * LV_VER_RES_MAX); lv_disp_drv_init(&disp_drv); disp_drv.hor_res = LV_HOR_RES_MAX; disp_drv.ver_res = LV_VER_RES_MAX; disp_drv.flush_cb = disp_flush; disp_drv.buffer = &draw_buf_dsc; lv_disp_drv_register(&disp_drv); } ``` 4. 这个盒子的UI设计比较简单,主要实现在main_tab_hander,会先调用同步一下时间和天气信息。然后创建了一个task,来更新页面信息。 ``` C void get_main_tab_info(void) { char hour_h[2] = {'0', '\0'}; char hour_l[2] = {'0', '\0'}; char min_h[2] = {'0', '\0'}; char min_l[2] = {'0', '\0'}; time_t now; now = time(RT_NULL); tab_info.cur_tm = localtime(&now); if(tab_info.cur_tm->tm_min == 0) { rp_weather_info_get(); rt_sprintf(tab_info.date, "%s", info.date->valuestring); rt_sprintf(tab_info.temp, "T: %s C", info.cur_temp->valuestring); rt_sprintf(tab_info.humi, "H: %s", info.humidity->valuestring); rt_sprintf(tab_info.wea_img, "S:/sd/weather_img/%s.bin", info.wea_img->valuestring); rp_weather_info_free(); lv_label_set_text(date_label, tab_info.date); lv_label_set_text(temp_label, tab_info.temp); lv_label_set_text(humi_label, tab_info.humi); lv_img_set_src(weather_img, tab_info.wea_img); } hour_h[0] = (tab_info.cur_tm->tm_hour/10) + '0'; hour_l[0] = (tab_info.cur_tm->tm_hour%10) + '0'; min_h[0] = (tab_info.cur_tm->tm_min/10) + '0'; min_l[0] = (tab_info.cur_tm->tm_min%10) + '0'; lv_label_set_text(time_label[0], hour_h); lv_label_set_text(time_label[1], hour_l); lv_label_set_text(time_label[2], min_h); lv_label_set_text(time_label[3], min_l); } void time_task_handle(lv_task_t* task) { task = NULL; lv_led_toggle(time_led[0]); lv_led_toggle(time_led[1]); get_main_tab_info(); } int main_tab_hander(lv_obj_t* parent, char *name) { rp_weather_info_get(); rt_sprintf(tab_info.date, "%s", info.date->valuestring); rt_sprintf(tab_info.temp, "T: %s C", info.cur_temp->valuestring); rt_sprintf(tab_info.humi, "H: %s", info.humidity->valuestring); rt_sprintf(tab_info.wea_img, "S:/sd/weather_img/%s.bin", info.wea_img->valuestring); rp_weather_info_free(); //日期 static lv_style_t date_style; lv_style_init(&date_style); lv_style_set_text_font(&date_style, LV_STATE_DEFAULT, &lv_font_montserrat_20); lv_style_set_text_color(&date_style, LV_STATE_DEFAULT, LV_COLOR_WHITE); date_label = lv_label_create(parent, NULL); lv_obj_add_style(date_label, LV_LABEL_PART_MAIN, &date_style); lv_obj_align(date_label, NULL, LV_ALIGN_IN_TOP_MID, -30, 5); lv_label_set_recolor(date_label, true); lv_label_set_text(date_label, tab_info.date); //时间 time_cont = lv_cont_create(parent, NULL); lv_obj_clean_style_list(time_cont, LV_OBJ_PART_MAIN); lv_obj_set_size(time_cont, 180, 70); lv_obj_align(time_cont, NULL, LV_ALIGN_IN_TOP_MID, 0, 30); lv_obj_set_style_local_bg_color(time_cont, LV_STATE_DEFAULT, LV_STATE_DEFAULT, LV_COLOR_MAKE(20, 20, 20)); for (int i = 0; i < (sizeof(time_led) / sizeof(time_led[0])); i++) { time_led[i] = lv_led_create(time_cont, NULL); lv_obj_set_size(time_led[i], 8, 10); lv_obj_align(time_led[i], NULL, LV_ALIGN_CENTER, 0, ((i == 0) ? (-10) : (10))); } static lv_style_t time_style; lv_style_init(&time_style); lv_style_set_text_font(&time_style, LV_STATE_DEFAULT, &lv_font_montserrat_48); lv_style_set_text_color(&time_style, LV_STATE_DEFAULT, LV_COLOR_WHITE); const lv_coord_t time_coord[4] = { -60, -25, 25, 60 }; for (int i = 0; i < (sizeof(time_label) / sizeof(time_label[0])); i++) { time_label[i] = lv_label_create(time_cont, NULL); lv_obj_add_style(time_label[i], LV_LABEL_PART_MAIN, &time_style); lv_label_set_text(time_label[i], "0"); lv_obj_align(time_label[i], NULL, LV_ALIGN_CENTER, time_coord[i], 0); } weather_img = lv_img_create(parent, NULL); lv_obj_align(weather_img, NULL, LV_ALIGN_IN_BOTTOM_MID, -90, -90); lv_img_set_src(weather_img, tab_info.wea_img); //温湿度 static lv_style_t humiture_style; lv_style_init(&humiture_style); lv_style_set_text_font(&humiture_style, LV_STATE_DEFAULT, &lv_font_montserrat_20); lv_style_set_text_color(&humiture_style, LV_STATE_DEFAULT, LV_COLOR_WHITE); temp_label = lv_label_create(parent, NULL); lv_obj_add_style(temp_label, LV_LABEL_PART_MAIN, &humiture_style); lv_obj_align(temp_label, NULL, LV_ALIGN_IN_BOTTOM_MID, 40, -70); // lv_label_set_recolor(temp_label, true); lv_label_set_text(temp_label, tab_info.temp); humi_label = lv_label_create(parent, NULL); lv_obj_add_style(humi_label, LV_LABEL_PART_MAIN, &humiture_style); lv_obj_align(humi_label, NULL, LV_ALIGN_IN_BOTTOM_MID, 40, -30); // lv_label_set_recolor(humi_label, true); lv_label_set_text(humi_label, tab_info.humi); time_task = lv_task_create(time_task_handle, 500, LV_TASK_PRIO_MID, NULL); time_task_handle(time_task); return 0; } ``` ## 效果: - 一个简单的桌面小工具: ![](https://RiceChen0.gitee.io/picture/r_plan/25.png) - 放在桌面,和PM2.5来实时监测工作环境。 ![](https://RiceChen0.gitee.io/picture/r_plan/26.png)
关注微信公众号『Rice嵌入式开发技术分享』,后台回复“微信”添加作者微信,备注”入群“,便可邀请进入技术交流群。 ![](https://RiceChen0.gitee.io/picture/logo/logo_.jpg)
原创作品,未经权利人授权禁止转载。详情见
转载须知
。
举报文章
点赞
(
0
)
Rice嵌入式开发
关注
评论
(0)
登录后可评论,请
登录
或
注册
相关文章推荐
MK-米客方德推出工业级存储卡
Beetle ESP32 C3 蓝牙数据收发
Beetle ESP32 C3 wifi联网获取实时天气信息
开箱测评Beetle ESP32-C3 (RISC-V芯片)模块
正点原子数控电源DP100测评
DP100试用评测-----开箱+初体验
Beetle ESP32 C3环境搭建
【花雕体验】16 使用Beetle ESP32 C3控制8X32位WS2812硬屏之二
X
你的打赏是对原创作者最大的认可
请选择打赏IC币的数量,一经提交无法退回 !
100IC币
500IC币
1000IC币
自定义
IC币
确定
X
提交成功 ! 谢谢您的支持
返回
我要举报该内容理由
×
广告及垃圾信息
抄袭或未经授权
其它举报理由
请输入您举报的理由(50字以内)
取消
提交