摘要
本次參加HMI-Boardt挑戰賽,提交的作品是天氣萬年歷,實現的功能主要有以下幾點:
顯示當前的日期,包括年月日,本地時間等信息;
顯示當前的天氣、溫度等信息;
選擇該項目的主要原因是該項目涉及到了Wifi、http客戶端、網絡數據抓取等,屬于典型的物聯網應用,同時天氣日歷的邏輯很簡單,很適合作為初學者的入門項目,能夠接觸到物聯網開發的各個關鍵組件,配合RT-Thread豐富的生態,在培養興趣的同時,可以將書本上的知識轉化為貼近生活的實際應用。
1.1 程序整體介紹
本項目使用到的外設主要有TFT屏幕、RTC模塊、RW007Wifi模塊。
屏幕的分辨率為480x272,屏幕上顯示的信息包括當前月份的日歷(以邊框形式在日歷中顯示當天的信息)、城市、溫度、天氣以及時間信息。程序的整體流程如下所示
項目中使用到的軟件包如下所示:
項目中的代碼主要集中在hal_entry.c以及lvgl_demo.c中,具體的文件分布如下圖所示:
1.2 模塊說明
1.2.1 顯示部分
RT-Thread提供的HMI-Board的SDK中已經為用戶編寫了顯示和觸摸的驅動程序,同時lvgl中以為RT-Thread做了適配,在RT-Thread的啟動序列中添加了lvgl線程的創建、調用lvgl初始化以及初始外設的函數,用戶只需要編寫lv_user_gui_init函數,設計應用相關的代碼即可。
本項目的顯示代碼如下。
//導入額外的字體
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指南中的RW007模塊使用,開啟RW007模塊的Wifi通訊功能,調用rt_wlan_connect函數連接到指定的Wifi,從而可以方位互聯網服務。
開啟物聯網服務中的NTP服務和本地的RTC服務,可以通過訪問NTP服務器,從而獲取網絡時間,對本地的時間進行校準。
相關的代碼如下所示:
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,表示無限循環),回調函數用于更新時間以及發送獲取天氣信息的信號量,具體代碼如下:
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字符,根據官方提供的數據格式,對其中的數據進行解析,提取出相應的字段,用于在標簽控件上顯示信息。相關的代碼如下所示:
#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);
}
-
物聯網
+關注
關注
2913文章
44935瀏覽量
377066 -
GUI
+關注
關注
3文章
662瀏覽量
39892 -
WLAN技術
+關注
關注
0文章
23瀏覽量
9300 -
wifi模塊
+關注
關注
60文章
380瀏覽量
73769 -
RT-Thread
+關注
關注
31文章
1305瀏覽量
40387
發布評論請先 登錄
相關推薦
評論