【平头哥RVB2601创意应用开发】实践7-U8g2库显示网络天气和时间
[复制链接]
本篇介绍了RVB2601连网获取天气以及RTC功能,将天气和时间显示到OLED屏幕中。
1 RTC时钟功能
参考YOC文档:https://yoc.docs.t-head.cn/yocbook/Chapter3-AliOS/CSI%E8%AE%BE%E5%A4%87%E9%A9%B1%E5%8A%A8%E6%8E%A5%E5%8F%A3/CSI1/RTC.html
RTC(Real_Time Clock)实时时钟为系统提供可靠的时间基准, 一般有独立的晶振和电源,保证主电源掉电时还可以工作。 RTC 一般可以提供日历格式的时间。
虽然RVB2601的RTC没有配置电池,掉电后时间就没了,但RVB2601的连网功能,可以在每次开机时,通过获取网络时间,来为RTC重新校准时间。
1.1 RTC时间结构体
csi_rtc_time_t
成员 |
类型 |
说明 |
tm_sec |
int |
秒 取值范围[0-59] |
tm_min |
int |
分 取值范围[0-59] |
tm_hour |
int |
小时 取值范围[0-23] |
tm_mday |
int |
天 取值范围[1-31] |
tm_mon |
int |
月 取值范围[0-11] |
tm_year |
int |
年 取值范围[70-199] |
tm_wday |
int |
周 取值范围[0-6] |
tm_yday |
int |
一年中的第几天 取值范围[0-365] |
例:设置2020年1月13日23点59分59秒则如下配置:
rtctime.tm_year = 120; //2020-1900 = 120
rtctime.tm_mon = 0; //0-11
rtctime.tm_mday = 13; //1-31
rtctime.tm_hour = 23; //0-23
rtctime.tm_min = 59; //0-59
rtctime.tm_sec = 59; //0-59
1.2 API函数
RTC的CSI接口在用户对接时是否必须适配的说明如下所示:
函数 |
功能 |
是否必须适配 |
csi_rtc_init |
初始化 |
必须 |
csi_rtc_uninit |
反初始化 |
必须 |
csi_rtc_set_time |
设置时间(需要同步时间) |
必须 |
csi_rtc_set_time_no_wait |
设置时间(不需要同步时间) |
非必须 |
csi_rtc_get_time |
获取当前时间 |
必须 |
csi_rtc_get_alarm_remaining_time |
获取距离闹钟剩余时间 |
非必须 |
csi_rtc_set_alarm |
设置闹钟 |
非必须 |
csi_rtc_cancel_alarm |
取消闹钟 |
非必须 |
csi_rtc_is_running |
获取工作状态 |
非必须 |
主要函数
参考官方文档,编写RTC时间获取任务。
static void rtc_clock_task(void * arg)
{
csi_rtc_time_t now_time;
int last_sec = -1;
rtc_clock_init();
if(rtc_clock_queue_init() != 0)
{
LOGD(TAG, "creat queue:rtc_clock_queue failed\r\n ");
}
LOGD(TAG, "GET weather_update_done_sem \r\n ");
aos_sem_wait(&weather_update_done_sem, AOS_WAIT_FOREVER);
while(1)
{
if(CSI_OK != csi_rtc_get_time(&g_rtc, &now_time))
{
LOGD(TAG, "===%s, %d", __FUNCTION__, __LINE__);
return ;
}
else
{
if (now_time.tm_sec != last_sec)
{
now_time.tm_year += 1900;
now_time.tm_mon += 1;
LOGD(TAG, "Time: %d-%d-%d %d:%d:%d\n", now_time.tm_year, now_time.tm_mon,
now_time.tm_mday, now_time.tm_hour, now_time.tm_min, now_time.tm_sec);
aos_queue_send(&rtc_clock_queue, &now_time, sizeof(csi_rtc_time_t));
last_sec = now_time.tm_sec;
}
}
aos_msleep(300);
}
}
注意这里我没300ms获取一次RTC时间,当发现“秒”数据变化时,通过消息队列发送给OLED模块使其更新时间,这样,OLED只需1s更新一次即可,避免不必要的循环刷新OLED显示。
2 NTP网络授时
参考YOC文档:https://yoc.docs.t-head.cn/yocbook/Chapter5-%E7%BB%84%E4%BB%B6/%E7%BD%91%E7%BB%9C%E7%BB%84%E4%BB%B6/NTP.html
文档里关于此部分的介绍较少,并且已有网友分享使用NTP时有许多坑,本篇暂不使用NTP功能。
那想要获取实际的时间,怎么办呢?在使用下面的连网获取天气的过程中,发现在获取天气时,同时会获取到一个天气更新的时间(实测发现,网上的天气信息差不多是每隔十多分钟更新一次),所以,可以使用这个时间作为RTC的校准时间(虽然会有几分钟的偏差,但作为测试使用还是可以的)。
3 连网获取天气
3.1 获取天气信息
获取网络天气,已经有许多网友尝试过了,我参考这篇:【平头哥RVB2601创意应用开发】RVB2601之获取天气小工具
需要在高德开发平台上注册开发者账号,通过自己Key,以及通过http协议获取网络天气,并通过json数据解析获取具体的天气和温度以及更新时间等信息。
按照网友的做法,通过连上网时获取一起天气,以及后续的按键发送信号量,触发一次网络天气获取,然后对json格式的天气数据进行解析。
static void parse_http_data(char *http_data)
{
app_weather_t app_weather_data;
cJSON *root;
cJSON *status;
cJSON *lives;
cJSON *tmp_obj;
cJSON *live_obj;
char *str_tmp;
int size;
root = cJSON_Parse(http_data);
if(root)
{
LOGD(TAG, "========>JSON_data:\n%s\n", cJSON_Print(root));
status = cJSON_GetObjectItem(root, STATUS);
if(status)
{
LOGD(TAG, "status:%d,%s\r\n",atoi(status->valuestring), status->valuestring);
lives = cJSON_GetObjectItem(root, LIVES);
if(lives)
{
size = cJSON_GetArraySize(lives);
LOGD(TAG, "!!size:%d\r\n",size);
for(int i=0;i<size;i++)
{
live_obj = cJSON_GetArrayItem(lives, i);
if(live_obj)
{
tmp_obj = cJSON_GetObjectItem(live_obj, WEATHER);
str_tmp = translate_weather_type_in_ascii(tmp_obj->valuestring);
sprintf(app_weather_data.info, "%s", str_tmp);
free(str_tmp);
LOGD(TAG, "weather: %s\r\n",app_weather_data.info);
tmp_obj = cJSON_GetObjectItem(live_obj, TIME);
sprintf(app_weather_data.time, "%s", tmp_obj->valuestring);
LOGD(TAG, "data: %s\r\n",app_weather_data.time);
int year, mon, day, h, m, s;
sscanf(tmp_obj->valuestring, "%d-%d-%d %d:%d:%d", &year, &mon, &day, &h, &m, &s);
LOGD(TAG, "year:%d mon:%d day:%d h:%d m:%d s:%d\r\n", year, mon, day, h, m, s);
rtc_update_time(year, mon, day, h, m, s); //更新RTC时间
aos_sem_signal(&weather_update_done_sem); //通知RTC时间已被同步
tmp_obj = cJSON_GetObjectItem(live_obj, TEMP);
app_weather_data.temperature = atoi(tmp_obj->valuestring);
LOGD(TAG, "temperature: %d\r\n",app_weather_data.temperature);
}
}
}
}
}
cJSON_Delete(root);
event_publish(EVENT_WEATHER_UPDATE, &app_weather_data); //发布天气更新的时间
}
这里我增加了一些功能:
-
在每次触发天气更新后,解析到时间数据后,更新一下RTC的校准时间,这样每次连网更新天气时,时间也被更新了
-
在每次触发天气更新后,更新完RTC的校准时间后,再触发一个信号量,表示RTC时间已被同步,这个的作用是用来在刚开始的时候,还没连上网之前,因为时间没同步,没法显示正确的时间,这时OLED屏幕上就可以先显示等待WIFI连接之类的提示,等RTC时间校准之后,再显示时间和天气
-
在每次触发天气更新后,解析到天气数据后,改为主题推送的方式来让OLED模块来更新天气数据(这位网友之前使用的是消息队列的方式)
3.2 OLED显示时间和天气
参考网上的一篇天气时间的UI设计:https://www.arduino.cn/thread-98403-1-1.html
利用U8g2库来显示不同大小的字体,以及天气图标(U8g2的字体库中自带了一些天气图标,可以直接用),另外,U8g2库也是有汉字库的,只是汉字库占用的空间比较大,使用之后就编译出错了,那就先不显示汉字吧。
UI界面的主要显示函数:
void testShowTimeAndWeather(u8g2_t *u8g2, csi_rtc_time_t *now_time)
{
u8g2_ClearBuffer(u8g2);
int year = now_time->tm_year;
int month = now_time->tm_mon;
int day = now_time->tm_mday;
int hour = now_time->tm_hour;
int minute = now_time->tm_min;
int sec = now_time->tm_sec;
//时分
char str_big_time[] = "";
my_strcat(str_big_time, hour);
strcat(str_big_time,":");
my_strcat(str_big_time, minute);
u8g2_SetFont(u8g2, u8g2_font_logisoso24_tf );
u8g2_DrawStr(u8g2, 0, 34, str_big_time);
//秒
char str_small_sec[] = "";
my_strcat(str_small_sec, sec);
u8g2_SetFont(u8g2, u8g2_font_wqy12_t_chinese1 );
u8g2_DrawStr(u8g2, 72, 34, str_small_sec);
//日期
char str_date[] = "";
char str_temp[6];
itoa(year,str_temp,10);
strcat(str_date,str_temp);
strcat(str_date,"-");
my_strcat(str_date, month);
strcat(str_date,"-");
my_strcat(str_date, day);
u8g2_SetFont(u8g2,u8g2_font_wqy12_t_chinese1);
u8g2_DrawStr(u8g2, 0, 54, str_date);
//分割线
u8g2_DrawLine(u8g2, 88, 2, 88, 61);
//天气
u8g2_SetFont(u8g2, u8g2_font_open_iconic_weather_4x_t );
u8g2_DrawStr(u8g2, 96, 34, icon_index[g_iWeatherIconIdx]);
u8g2_SetFont(u8g2, u8g2_font_wqy16_t_chinese1);
char b_b_tmp[25];
itoa(g_iTemperature, b_b_tmp, 10);
strcat(b_b_tmp," C");
u8g2_DrawStr(u8g2, 96, 55, b_b_tmp);
u8g2_SendBuffer(u8g2);
aos_msleep(1000);
}
4 测试效果
5 总结
本篇介绍了RVB2601连网获取天气,并更新RTC时间,以及最终将天气图形,温度数据和时间日期显示到OLED屏幕上。
|