Toggle navigation
首页
问答
文章
积分商城
专家
专区
更多专区...
文档中心
返回主站
搜索
提问
会员
中心
登录
注册
LittlevGL_LVGL
瑞萨_Renesas
Renesas GUI挑战赛——天气日历
发布于 2023-09-14 21:25:10 浏览:840
订阅该版
[tocm] ## 摘要 本次参加HMI-Boardt挑战赛,提交的作品是天气万年历,实现的功能主要有以下几点: 显示当前的日期,包括年月日,本地时间等信息; 显示当前的天气、温度等信息; 选择该项目的主要原因是该项目涉及到了Wifi、http客户端、网络数据抓取等,属于典型的物联网应用,同时天气日历的逻辑很简单,很适合作为初学者的入门项目,能够接触到物联网开发的各个关键组件,配合RT-Thread丰富的生态,在培养兴趣的同时,可以将书本上的知识转化为贴近生活的实际应用。 ## 1.1 程序整体介绍 本项目使用到的外设主要有TFT屏幕、RTC模块、RW007Wifi模块。 屏幕的分辨率为480x272,屏幕上显示的信息包括当前月份的日历(以边框形式在日历中显示当天的信息)、城市、温度、天气以及时间信息。程序的整体流程如下所示 ![screenshot_程序流程图.png](https://oss-club.rt-thread.org/uploads/20230914/8bb16d6b143caf0be685d3f011c4f411.png) 项目中使用到的软件包如下所示: ![screenshot_工程中使用到的包.png](https://oss-club.rt-thread.org/uploads/20230914/5f107829dfb61b5349c7af7a0894cfc1.png) 项目中的代码主要集中在hal_entry.c以及lvgl_demo.c中,具体的文件分布如下图所示: ![screenshot_工程代码结构说明.png](https://oss-club.rt-thread.org/uploads/20230914/41b0ee35f8d8b574f282fcb6d7dbd5e2.png) ## 1.2 模块说明 ### 1.2.1 显示部分 RT-Thread提供的HMI-Board的SDK中已经为用户编写了显示和触摸的驱动程序,同时lvgl中以为RT-Thread做了适配,在RT-Thread的启动序列中添加了lvgl线程的创建、调用lvgl初始化以及初始外设的函数,用户只需要编写lv_user_gui_init函数,设计应用相关的代码即可。 本项目的显示代码如下。 ```c //导入额外的字体 LV_FONT_DECLARE(font_calendar_han_bold_20); /* display demo; you may replace with your LVGL application at here */ //设定城市信息标签的外观和位置 city_label=lv_label_create(lv_scr_act()); lv_obj_set_style_text_font(city_label,&font_calendar_han_bold_20,0); //lv_label_set_text(city_label, weather_data.name); lv_obj_set_pos(city_label, 10,5); //设定温度信息标签的外观和位置 temp_label=lv_label_create(lv_scr_act()); lv_obj_set_style_text_font(temp_label,&font_calendar_han_bold_20,0); //lv_label_set_text(temp_label, weather_data.temp); lv_obj_set_pos(temp_label, 10, 30); //设定天气信息标签的外观和位置 weather_label=lv_label_create(lv_scr_act()); lv_obj_set_style_text_font(weather_label,&font_calendar_han_bold_20,0); //abel_set_text(weather_label, weather_data.text); lv_obj_set_pos(weather_label, 90, 30); //创建时间标签控件,用于显示当前的时间信息 time_label=lv_label_create(lv_scr_act()); lv_obj_set_style_text_font(time_label,&font_calendar_han_bold_20,0); lv_label_set_text(time_label, time_label_text); lv_obj_set_pos(time_label, 10, 55); //设定日历控件的外观和位置 calendar = lv_calendar_create(lv_scr_act()); lv_obj_set_x(calendar,10); lv_obj_set_y(calendar,80); lv_obj_set_size(calendar, 185, 185); lv_obj_add_event_cb(calendar, event_handler, LV_EVENT_ALL, NULL); lv_calendar_set_today_date(calendar, 2021, 02, 23); lv_calendar_set_showed_date(calendar, 2021, 02); /*Highlight a few days*/ static lv_calendar_date_t highlighted_days[3]; /*Only its pointer will be saved so should be static*/ highlighted_days[0].year = 2021; highlighted_days[0].month = 02; highlighted_days[0].day = 6; highlighted_days[1].year = 2021; highlighted_days[1].month = 02; highlighted_days[1].day = 11; highlighted_days[2].year = 2022; highlighted_days[2].month = 02; highlighted_days[2].day = 22; lv_calendar_set_highlighted_dates(calendar, highlighted_days, 3); #if LV_USE_CALENDAR_HEADER_DROPDOWN lv_calendar_header_dropdown_create(calendar); #elif LV_USE_CALENDAR_HEADER_ARROW lv_calendar_header_arrow_create(calendar); #endif lv_calendar_set_showed_date(calendar, 2021, 10); ``` 为了在屏幕上显示中文,首先准备需要用到的字体文件(项目中使用的是),在lvgl官方提供的字体转换工具中,根据自己的需要对字体的大小和字符集进行选择,最后得到字体文件,添加到项目中,使用lvgl的字体导入命令将要使用的字体导入,在需要使用的控件上使用该字体即可。 项目的界面效果如图所示。 ### 1.2.2 Wifi连接、NTP校时部分 开发板上带有RW007Wifi模块,通过其可以连接到互联网,从而可以获取互联上提供的信息服务,比如NTP(网络时间协议)用于更新本地的RTC时间。 根据[HMI-Board指南](https://docs.qq.com/doc/DQmVYUEN1dHVyd0hi "HMI-Board指南")中的RW007模块使用,开启RW007模块的Wifi通讯功能,调用rt_wlan_connect函数连接到指定的Wifi,从而可以方位互联网服务。 开启物联网服务中的NTP服务和本地的RTC服务,可以通过访问NTP服务器,从而获取网络时间,对本地的时间进行校准。 ![screenshot_image.png](https://oss-club.rt-thread.org/uploads/20230914/c730f6c78753aebbe72ba81f7eb576e5.png) 相关的代码如下所示: ```c rt_device_t device = RT_NULL; //连接到指定的Wifi app_connect_wifi(); /*寻找设备*/ device = rt_device_find(RTC_NAME); if (!device) { rt_kprintf("find %s failed!", RTC_NAME); return; } /*初始化RTC设备*/ if(rt_device_open(device, 0) != RT_EOK) { rt_kprintf("open %s failed!", RTC_NAME); return; } //同步NTP到RTC while(!ntp_sync_to_rtc(RT_NULL)); rt_sem_release(time_sync); ``` lvgl中提供了定时功能,初始化定时器后,设定定时器的回调函数以及寻魂次数(这里参数给-1,表示无限循环),回调函数用于更新时间以及发送获取天气信息的信号量,具体代码如下: ```c sec_timer=lv_timer_create(sec_timer_cb, 500, NULL); lv_timer_set_repeat_count(sec_timer, -1); void sec_timer_cb(lv_timer_t* timer) { static uint16_t count=0; count++; app_get_time(); lv_label_set_text(time_label, time_label_text); if(count==1800) { count=0; rt_sem_release(get_weather); } } ``` ### 1.2.3 获取天气信息并提取相应的信息 在main中hal_entry函数中,获取到访问天气数据的信号量后,创建WebClient,通过心知天气的API访问当前的天气信息,数据形式为JSON字符,根据官方提供的数据格式,对其中的数据进行解析,提取出相应的字段,用于在标签控件上显示信息。相关的代码如下所示: ```c #define GET_HEADER_BUFSZ 1024 //头部大小 #define GET_RESP_BUFSZ 1024 //响应缓冲区大小 static char *weather_url = "http://api.seniverse.com/v3/weather/now.json?key=SG-nLPzA3pyLEy9Tw&location=wuxi&language=zh-Hans&unit=c"; weather_t weather_data; /* 天气数据解析 */ void weather_data_parse(rt_uint8_t *data) { cJSON *root = RT_NULL, *array=RT_NULL,*object = RT_NULL, *element=RT_NULL, *item=RT_NULL; memset(&weather_data,0,sizeof(weather_data)); root = cJSON_Parse((const char *)data); if (!root) { rt_kprintf("No memory for cJSON root!\n"); return; } //获取结果对象 array = cJSON_GetObjectItem(root, "results"); //获取数组中的第一个元素 object = cJSON_GetArrayItem(array, 0); //提取location的信息 element=cJSON_GetObjectItem(object,"location"); item=cJSON_GetObjectItem(element,"id"); memcpy(weather_data.id,item->valuestring,strlen(item->valuestring)); item=cJSON_GetObjectItem(element,"name"); memcpy(weather_data.name,item->valuestring,strlen(item->valuestring)); item=cJSON_GetObjectItem(element,"country"); memcpy(weather_data.country,item->valuestring,strlen(item->valuestring)); item=cJSON_GetObjectItem(element,"path"); memcpy(weather_data.country,item->valuestring,strlen(item->valuestring)); item=cJSON_GetObjectItem(element,"timezone"); memcpy(weather_data.country,item->valuestring,strlen(item->valuestring)); item=cJSON_GetObjectItem(element,"tz_offset"); memcpy(weather_data.country,item->valuestring,strlen(item->valuestring)); //提取当前的天气信息 element=cJSON_GetObjectItem(object,"now"); item=cJSON_GetObjectItem(element,"text"); memcpy(weather_data.text,item->valuestring,strlen(item->valuestring)); item=cJSON_GetObjectItem(element,"code"); memcpy(weather_data.code,item->valuestring,strlen(item->valuestring)); item=cJSON_GetObjectItem(element,"temperature"); memcpy(weather_data.temp,item->valuestring,strlen(item->valuestring)); //提取上次天气更新的时间信息 element=cJSON_GetObjectItem(object,"last_update"); memcpy(weather_data.last_update,element->valuestring,strlen(element->valuestring)); if (root != RT_NULL) cJSON_Delete(root); } void weather(void) { rt_uint8_t *buffer = RT_NULL; int resp_status; struct webclient_session *session = RT_NULL; int content_length = -1, bytes_read = 0; int content_pos = 0; /* 创建会话并且设置响应的大小 */ session = webclient_session_create(GET_HEADER_BUFSZ); if (session == RT_NULL) { rt_kprintf("No memory for get header!\n"); goto __exit; } /* 发送 GET 请求使用默认的头部 */ if ((resp_status = webclient_get(session, weather_url)) != 200) { rt_kprintf("webclient GET request failed, response(%d) error.\n", resp_status); goto __exit; } /* 分配用于存放接收数据的缓冲 */ buffer = rt_calloc(1, GET_RESP_BUFSZ); if (buffer == RT_NULL) { rt_kprintf("No memory for data receive buffer!\n"); goto __exit; } content_length = webclient_content_length_get(session); if (content_length < 0) { /* 返回的数据是分块传输的. */ do { bytes_read = webclient_read(session, buffer, GET_RESP_BUFSZ); if (bytes_read <= 0) { break; } }while (1); } else { do { bytes_read = webclient_read(session, buffer, content_length - content_pos > GET_RESP_BUFSZ ? GET_RESP_BUFSZ : content_length - content_pos); if (bytes_read <= 0) { break; } content_pos += bytes_read; }while (content_pos < content_length); } /* 天气数据解析 */ weather_data_parse(buffer); __exit: /* 关闭会话 */ if (session != RT_NULL) webclient_close(session); /* 释放缓冲区空间 */ if (buffer != RT_NULL) rt_free(buffer); } ``` ## 1.3 演示视频 演示视频链接为:https://www.bilibili.com/video/BV16h4y1w7z5/ 视频中在上电后,会等待连接到网络上并获取到当前的时间后,会在屏幕上显示当前的天气和时间信息。 ## 1.4 活动感受 参加本次活动对瑞萨的FSP使用、制作HMI-Board的BSP、lvgl的使用以及RT-Thread的Kconfig配置工具和scons代码构建工具的使用有了详细的了解。通过实际的操作和代码编写,对各部分的代码的功能,如何将各部分代码整合到一起有了详细的认识。 这次项目中接触到了很多RT-Thread的模块,使用起来特别方便,不过从开发者的角度来看,对其中的原理不清晰,这给调试带来很多困难,需要对其内部机制进行详细的了解才能发挥出其完整的性能。
1
条评论
默认排序
按发布时间排序
登录
注册新账号
关于作者
EPTmachine
Big Maker
文章
4
回答
4
被采纳
0
关注TA
发私信
相关文章
1
LittlevGL + DMA2D 显示图案扭曲
2
LittleVGL2RTT软件包还有在维护吗,测试遇到一些问题求解
3
使用littlevgl2rtt软件包实例运行不成功,emwin正常
4
关于littlevgl2rtt软件包刷频慢的解决方案?
5
移植了littlevGUI之后,用动态 线程去跑例程会卡死
6
lvgl的字体、图片文件如何升级?
7
qemu-vexpress-a9bsp下的littvgl工程可以实现触屏操作吗?
8
LVGL控件刷新死机问题
9
在lvgl上设置一个时间显示的label,一段时间后所有控件消失。
10
littlevgl2rtt和littlevgl的pc模拟器源码不兼容吗?
推荐文章
1
RT-Thread应用项目汇总
2
玩转RT-Thread系列教程
3
国产MCU移植系列教程汇总,欢迎查看!
4
机器人操作系统 (ROS2) 和 RT-Thread 通信
5
五分钟玩转RT-Thread新社区
6
【技术三千问】之《玩转ART-Pi》,看这篇就够了!干货汇总
7
关于STM32H7开发板上使用SDIO接口驱动SD卡挂载文件系统的问题总结
8
STM32的“GPU”——DMA2D实例详解
9
RT-Thread隐藏的宝藏之completion
10
【ART-PI】RT-Thread 开启RTC 与 Alarm组件
热门标签
RT-Thread Studio
串口
Env
LWIP
SPI
AT
Bootloader
Hardfault
CAN总线
FinSH
ART-Pi
USB
DMA
文件系统
RT-Thread
SCons
RT-Thread Nano
线程
MQTT
STM32
RTC
FAL
rt-smart
I2C_IIC
ESP8266
UART
WIZnet_W5500
ota在线升级
PWM
cubemx
flash
freemodbus
BSP
packages_软件包
潘多拉开发板_Pandora
定时器
ADC
flashDB
GD32
socket
编译报错
中断
Debug
rt_mq_消息队列_msg_queue
SFUD
msh
keil_MDK
ulog
C++_cpp
MicroPython
本月问答贡献
xusiwei1236
8
个答案
2
次被采纳
踩姑娘的小蘑菇
1
个答案
2
次被采纳
用户名由3_15位
7
个答案
1
次被采纳
bernard
4
个答案
1
次被采纳
RTT_逍遥
3
个答案
1
次被采纳
本月文章贡献
聚散无由
2
篇文章
15
次点赞
catcatbing
2
篇文章
5
次点赞
Wade
2
篇文章
4
次点赞
Ghost_Girls
1
篇文章
6
次点赞
YZRD
1
篇文章
2
次点赞
回到
顶部
发布
问题
投诉
建议
回到
底部