基于 RT-Thread 的“数码小精灵”设计与实现是怎样的,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。
一、概述
本设计为基于 RT-Thread 的“数码小精灵”设计,硬件采用了以 BK7252 为主控芯片的麻雀一号开发板开发。BK7252 , 是一款高性能 WiFi 模块,采用高集成的无线射频芯片,内部集成 2.4GHz Wi-Fi 1T1R 先进技术,支持摄像头图像输出,拥有最佳的功耗性能、射频性能、稳定性、通用性和可靠性,适用于各种应用和不同产品需求。模块内部拥有 512KB 内嵌 RAM 和 4Mbyte Flash 空间,CPU 主频高达 180Mhz。
本设计在此基础上主要实现了以下功能,包括天气和疫情数据更新显示、MP3 音乐播放器以及数码拍照相机的功能。其中 MP3 播放器具有音量调节,播放/停止控制和歌曲切换的功能;数码相机将拍照图片进行 LCD 屏显示,同 时具有 SD 卡存储和 OneNet 云平台存储功能。该产品可以充当家庭数码助理的角色,因此取名“数码小精灵”。同时本作品完成的过程中参考了部分网络资料和网友的思路,在此一并表示感谢。
二、RT-Thread 使用情况概述
本设计基于麻雀一号开发板 SDK 进行开发,其 RT-Thread 为 3.1.0 版本。
图 1 RT-Thread 版本
在 RT-Thread 系统上的设备注册列表,其中主要使用了 rtc,sd0,w0,sound,uart1 等设备。使用到的 RT-Thread 组件包括了 FinSH 控制台,虚拟文件系统,POSIX 接口。在软件包上面涉及到网络工具及 NetUtils,WebClient,cJSON,EasyFlash,rt_ota,TJpgDec,Player 等。
图2设备注册列表
三、硬件框架
麻雀一号开发板外设资源丰富,但资源相当丰富,集成 WiFi、BLE、摄像头、音频扬声器、MIC 录音、TF 卡座、五向按键、还有一个 1.44 寸的 LCD 屏,使用常见的 TypeC 接口作为供电和调试串口,预留支持锂电池供电接口。
本设计的人机交互部分主要利用了开发板的五向按键和 LCD 显示屏,其中按键用于功能选择,数据刷新以及音乐播放控制功能。
普通模式下:“ ←”: 音乐播放 、“⭕”:天气和疫情数据刷新 、 “→”:相机拍照
音乐播放模式下:“↑”:音量加 、“↓”:音量减 、“←”:下一曲 、“⭕”:停止播放
四、软件框架说明
五、软件模块说明
1. 设备联网
设备联网主要使用到了 RT-Thread 组件中的 wlan 驱动程序实现,上电初始化完成后在主程序中查询 wlan 无线设备,并根据用户配置的 SSID 和PASSWORD 进行 WIFI 网络的连接。该部分的具体代码实现如下所示:
1static int iot_station_connect(char *ssid, char *passwd) {
2rt_err_t result = RT_EOK;
3struct rt_wlan_info info;
4struct rt_wlan_device *wlan;
5rt_tick_t tick = 0;
6wlan = (struct rt_wlan_device *)rt_device_find(WIFI_DEVICE_STA
7_NAME);
8if (!wlan)
9{
10rt_kprintf("no wlan:%s device/n", WIFI_DEVICE_STA_NAME);
11return -1; }
12result = rt_wlan_init(wlan, WIFI_STATION);
13rt_wlan_register_event_handler(wlan, WIFI_EVT_STA_CONNECTED, i
14ot_wlan_sta_connected_event);
15rt_wlan_register_event_handler(wlan, WIFI_EVT_STA_DISCONNECTED
16, iot_wlan_sta_disconnected_event);
17rt_wlan_info_init(&info, WIFI_STATION, SECURITY_WPA2_AES_PSK,
18ssid);
19result = rt_wlan_connect(wlan, &info, passwd);
20rt_wlan_info_deinit(&info);
21return result;
22}
2. NTP 网络时间同步
1{
2time_t cur_time = ntp_sync_to_rtc();
3if (cur_time)
4{
5rt_kprintf("Get local time from NTP server: %s", ctime((const t
6ime_t*) &cur_time));
7rt_kprintf("The system time is updated. Timezone is %d./n", NTP
8_TIMEZONE);
9} }
10static rt_thread_t tid1 = RT_NULL;
11static void ntcthread1_entry(void * parameter) {
12while ((1))
13{
14time_t cur_time = ntp_sync_to_rtc();
15if (cur_time)
16{
17rt_kprintf("Get local time from NTP server: %s", ctime((const tim
18e_t *) &cur_time));
19rt_kprintf("The system time is updated. Timezone is %d./n", NTP_T
20IMEZONE);
21break; }
22else
23{
24rt_thread_mdelay(1000);
25} } }
26void NTCThreadInit(void) {
27if (tid1 != RT_NULL)
28{
29rt_kprintf("ntc thread still run/n");
30return; }
31rt_kprintf("NTC thread init/n");
32tid1 = rt_thread_create("NTC",
33ntcthread1_entry, RT_NULL,
34THREAD_STACK_SIZE,
35THREAD_PRIORITY, THREAD_TIMESLICE);
36if (tid1 != RT_NULL)
37rt_thread_startup(tid1);
38}
该部分主要是利用了 webclient 工具包的功能,通过调用天气和疫情数据API 接口获取相关 Json 数据,并利用 CJson 工具包进行返回 Json 数据的解析。最后通过 LCD 进行数据显示。相关代码如下:
1#define GET_URI "http://www.weather.com.cn/data/sk/%s.html" //
2获取天气的 API
3#define GET_FY2020_URI "http://www.dzyong.top:3005/yiqing/total" //疫情
4数据 API
5void get_weather(int argc, char **argv) {
6rt_uint8_t *buffer = RT_NULL;
7int resp_status;
8struct webclient_session *session = RT_NULL;
9char *weather_url = RT_NULL;
10int content_length = -1, bytes_read = 0;
11int content_pos = 0;
12char *city_name = rt_calloc(1,255);
13/* 为 weather_url 分配空间 */
14weather_url = rt_calloc(1, GET_URL_LEN_MAX);
15if (weather_url == RT_NULL)
16{
17rt_kprintf("No memory for weather_url!/n");
18goto __exit;
19}
20if(argc == 1) {
21strcpy(city_name, AREA_ID);
22}
23else if (argc == 2) {
24strcpy(city_name, argv[1]);
25}
26/* 拼接 GET 网址 */
27rt_snprintf(weather_url, GET_URL_LEN_MAX, GET_URI, city_name);
28/* 创建会话并且设置响应的大小 */
29session = webclient_session_create(GET_HEADER_BUFSZ);
30if (session == RT_NULL)
31{
32rt_kprintf("No memory for get header!/n");
33goto __exit;
34}
35/* 发送 GET 请求使用默认的头部 */
36if ((resp_status = webclient_get(session, weather_url)) != 200) {
37rt_kprintf("webclient GET request failed, response(%d) error./n", resp_
38status);
39goto __exit;
40}
41/* 分配用于存放接收数据的缓冲 */
42buffer = rt_calloc(1, GET_RESP_BUFSZ);
43if (buffer == RT_NULL)
44{
45rt_kprintf("No memory for data receive buffer!/n");
46goto __exit;
47}
48content_length = webclient_content_length_get(session);
49if (content_length < 0) {
50/* 返回的数据是分块传输的. */
51do
52{
53bytes_read = webclient_read(session, buffer, GET_RESP_BUFSZ);
54if (bytes_read <= 0) {
55break; } }while (1); }
56else
57{
58do
59{
60bytes_read = webclient_read(session, buffer,
61content_length - content_pos > GET_RESP
62_BUFSZ ?
63GET_RESP_BUFSZ : content_length - conte
64nt_pos);
65if (bytes_read <= 0) {
66break; }
67content_pos += bytes_read;
68}while (content_pos < content_length);
69}
70/* 天气数据解析 */
71weather_data_parse(buffer);
72__exit:
73/* 释放网址空间 */
74if (weather_url != RT_NULL)
75rt_free(weather_url);
76/* 关闭会话 */
77if (session != RT_NULL)
78webclient_close(session);
79/* 释放缓冲区空间 */
80if (buffer != RT_NULL)
81rt_free(buffer);
82if(city_name != RT_NULL)
83rt_free(city_name);
84}
85/* 天气数据解析 */
86void weather_data_parse(rt_uint8_t *data) {
87uint8_t temp[100];
88cJSON *root = RT_NULL, *object = RT_NULL, *item = RT_NULL;
89root = cJSON_Parse((const char *)data);
90if (!root)
91{
92rt_kprintf("No memory for cJSON root!/n");
93return; }
94object = cJSON_GetObjectItem(root, "weatherinfo");
95item = cJSON_GetObjectItem(object, "city");
96rt_kprintf("/ncity :%s ", item->valuestring);
97lcd_clear(BLACK);
98item = cJSON_GetObjectItem(object, "temp");
99rt_kprintf("/ntemp :%s ", item->valuestring);
100rt_sprintf(temp,"温度: %s",item->valuestring);
101lcd_disp_str_en_ch(0,20,temp,BLACK,WHITE);
102item = cJSON_GetObjectItem(object, "WD");
103rt_kprintf("/nwd :%s ", item->valuestring);
104item = cJSON_GetObjectItem(object, "WS");
105rt_kprintf("/nws :%s ", item->valuestring);
106item = cJSON_GetObjectItem(object, "SD");
107rt_kprintf("/nsd :%s ", item->valuestring);
108rt_sprintf(temp,"湿度: %s",item->valuestring);
109lcd_disp_str_en_ch(0,40,temp,BLACK,WHITE);
110item = cJSON_GetObjectItem(object, "time");
111rt_kprintf("/ntime :%s /n", item->valuestring);
112item = cJSON_GetObjectItem(object, "AP");
113rt_kprintf("/nap :%s ", item->valuestring);
114rt_sprintf(temp,"气压: %s",item->valuestring);
115lcd_disp_str_en_ch(0,60,temp,BLACK,WHITE);
116item = cJSON_GetObjectItem(object, "WSE");
117rt_kprintf("/nwse :%s ", item->valuestring);
118rt_sprintf(temp,"风力: %s",item->valuestring);
119lcd_disp_str_en_ch(0,80,temp,BLACK,WHITE);
120if (root != RT_NULL)
121cJSON_Delete(root);
122}
123/* 疫情数据解析 */
124void fy2020_data_parse(rt_uint8_t *data) {
125uint8_t temp[100];
126cJSON *root = RT_NULL, *object = RT_NULL, *item = RT_NULL;
127root = cJSON_Parse((const char *)data);
128if (!root)
129{
130rt_kprintf("No memory for cJSON root!/n");
131return; }
1324. 音乐播放模块
133该部分的功能主要通过使用 player 软件包的接口函数实现,包括音乐播
134放,停止,歌曲切换以及音量控制等功能。该部分的部分代码如下图所示:
135cJSON *dataArray = cJSON_GetObjectItem(root,"data"); //取数组
136int arraySize = cJSON_GetArraySize(dataArray); //取数组大小
137cJSON *dataList = dataArray->child;
138while(dataList != RT_NULL)
139{
140rt_kprintf("/ndiagnosed :%d /n", cJSON_GetObjectItem(dataList,"diagn
141osed")->valueint);
142rt_kprintf("/ndeath :%d /n", cJSON_GetObjectItem(dataList,"death")->
143valueint);
144rt_kprintf("/ncured :%d /n", cJSON_GetObjectItem(dataList,"cured")->
145valueint);
146rt_kprintf("/ndate :%s /n", cJSON_GetObjectItem(dataList,"date")->va
147luestring);
148//LCD 屏打印信息
149rt_sprintf(temp,"累计确
150诊: %d",cJSON_GetObjectItem(dataList,"diagnosed")->valueint);
151lcd_disp_str_en_ch(0,120,temp,BLACK,WHITE);
152rt_sprintf(temp,"累计死
153亡: %d",cJSON_GetObjectItem(dataList,"death")->valueint);
154lcd_disp_str_en_ch(0,140,temp,BLACK,WHITE);
155rt_sprintf(temp,"累计治
156愈: %d",cJSON_GetObjectItem(dataList,"cured")->valueint);
157lcd_disp_str_en_ch(0,160,temp,BLACK,WHITE);
158rt_sprintf(temp,"更新时
159间: %s",cJSON_GetObjectItem(dataList,"date")->valuestring);
160lcd_disp_str_en_ch(0,180,temp,BLACK,WHITE);
161dataList = dataList->next;
162}
4. 音乐播放模块
1LCDShowMusic();
2rt_sprintf(music,"/sd/music/%d.mp3",count);
3rt_kprintf(" key left is press .../r/n");
4rt_kprintf("//////////////////////////// player_play /n");
5player_stop();
6player_set_uri(music);
7player_play();
8player_status = 1;
9count++;
10if(count >= 8)
11count = 0;
12rt_kprintf("//////////////////////////// player_play end /n");
该部分主要通过 IPC 事件获取相机采集图像数据,并将采集到的图像数据经过 TJpgDec 软件包进行解码后在 LCD 进行显示,同时会将相机数据通过写文件的形式存储到 SD 卡,通过 HTTP 协议推送到 OneNet 云平台。
1void take_photo(void) //手动拍照
2{
3//创建摄像头接收一帧图片的事件
4session.event = rt_event_create("vt_event", RT_IPC_FLAG_FIFO);
5camera_start(); //开启摄像头传输照片
6tvideo_capture(1);
7rt_event_recv(session.event, SEND_FRAME_EVENT, RT_EVENT_FLAG_OR | RT_EVE
8NT_FLAG_CLEAR, RT_WAITING_FOREVER, RT_NULL);
9int fd, res;
10time_t cur_time;
11struct tm *cur_tm;
12char time_now[50]; //保存文件到 sd 卡指定路径
13/* output current time */
14cur_time = time(RT_NULL);
15cur_tm = localtime(&cur_time);
16rt_sprintf(time_now, "%04d-%02d-%02d-%02d-%02d-%02d",cur_tm->tm_year + 19
1700, cur_tm->tm_mon + 1, cur_tm->tm_mday, cur_tm->tm_hour, cur_tm->tm_min, cur
18_tm->tm_sec);
19rt_sprintf(file_name, "/sd/images/%s.jpg", time_now);
20rt_kprintf("name = %s /n", file_name);
21fd = open(file_name, O_WRONLY | O_CREAT);
22if (fd >= 0) {
23write(fd, session.buf, session.total_len);
24close(fd);
25rt_kprintf("save %s ok!!!/n", file_name);
26res = Decode_Jpg(file_name);
27rt_kprintf("res = %d/n", res);
28}
29else
30{
31rt_kprintf("save pic failed!!!/n");
32}
33//拍照数据上传
34webclient_post_pic(session.buf, session.total_len);
35tvideo_capture(0);
36}
37MSH_CMD_EXPORT(take_photo,take_photo);
关于基于 RT-Thread 的“数码小精灵”设计与实现是怎样的问题的解答就分享到这里了,希望以上内容可以对大家有一定的帮助,如果你还有很多疑惑没有解开,可以关注亿速云行业资讯频道了解更多相关知识。
原创文章,作者:carmelaweatherly,如若转载,请注明出处:https://blog.ytso.com/219645.html