论坛元老
- 积分
- 6222
- 金钱
- 6222
- 注册时间
- 2018-12-2
- 在线时间
- 112 小时
|
本帖最后由 Sanjay 于 2019-3-27 21:37 编辑
原文博客链接:https://blog.csdn.net/Sanjay_Wu/article/details/88595565
觉得有帮助就回复支持鼓励一下,觉得有错误或可以完善的,麻烦指正指出,,感谢!
学习了RT-Thread的内核也有一段时间了,做了一个对接OneNET的历程,采用的是OneNET的EDP协议,关于OneNET的EDP协议可以点击跳转至OneNET EDP协议讲解与应用这篇博客看一下,这篇博客会比较详细的介绍对接EDP协议的思路。本篇博客会根据着重的将RT-Thread的应用思路,在本应用示例也只是用来软件定时器、线程管理、信号量而已,因为我用的是潘多拉开发板进行实验,所以AP6181驱动就直接用RT-Thread的,自己也只是在这个基础上做了应用。
使用AP6181 WiFi模组对接OneNET应用示例:STM32+AP6181(SDIO WiFi模组)对接OneNET,实现温湿度定时上报,控制上报周期、控制设备LED灯、蜂鸣器以及电机,掉线自动连接、WiFi自动连接。
一、移植需要修改的地方
1、在移植时,主要也就是修改几个地方,代码如下:
[mw_shl_code=c,true] #define ONENET_EDP_PRODUCT_ID "190254" /* 产品id */
#define ONENET_EDP_PRODUCT_APIKEY "cWPICK6PDU6cOHP=T0SqMcXWRc4=" /* api key */
#define ONENET_EDP_DEVICE_ID "504890772" /* 设备id */
#define ONENET_EDP_DEVICE_AUTHKEY "edp20181122" /* 设备鉴权信息 */[/mw_shl_code]
需要修改的为产品ID、APIKEY、设备ID、设备鉴权信息,将这些信息修改为你自己在OneNET开发者中心的产品信息或设备信息。
二、连接OneNET思路
1、开机后,RT-Thread完成一系列的初始化后进入main线程,首先进入main函数会进行执行onenet_edp_sample();函数进行信号量创建、软件定时器创建以及线程创建:
[mw_shl_code=c,true] rt_err_t onenet_edp_sample(void)
{
/* 连接OneNET信号量 */
link_onenet_sem = rt_sem_create("link_onenet_sem", 0, RT_IPC_FLAG_FIFO);
if(link_onenet_sem == RT_NULL)
{
rt_kprintf("[EDP]link_onenet_sem create failed\r\n");
return RT_ERROR;
}
/* 发送心跳信号量 */
send_heart_sem = rt_sem_create("send_heart_sem", 0, RT_IPC_FLAG_FIFO);
if(send_heart_sem == RT_NULL)
{
rt_kprintf("[EDP]send_heart_sem create failed\r\n");
return RT_ERROR;
}
/* 用于获取温湿度和发送温湿度信号量 */
humi_temp_sem = rt_sem_create("humi_temp_sem", 1, RT_IPC_FLAG_FIFO);
if(humi_temp_sem == RT_NULL)
{
rt_kprintf("[EDP]humi_temp_sem create failed\r\n");
return RT_ERROR;
}
/* 用于定时触发上报温湿度数据信号量 */
send_humi_temp_sem = rt_sem_create("send_humi_temp_sem", 0, RT_IPC_FLAG_FIFO);
if(send_humi_temp_sem == RT_NULL)
{
rt_kprintf("[EDP]send_humi_temp_sem create failed\r\n");
return RT_ERROR;
}
/* 软件定时器,周期执行,120秒 */
send_heart_timer = rt_timer_create("send_heart_timer", send_heart_timer_callback, RT_NULL, 120000, RT_TIMER_FLAG_SOFT_TIMER | RT_TIMER_FLAG_PERIODIC);
if(send_heart_timer == RT_NULL)
{
rt_kprintf("[EDP]send_heart_timer create failed\r\n");
return RT_ERROR;
}
/* 软件定时器,周期执行,300秒 */
send_humi_temp_timer = rt_timer_create("send_humi_temp_timer", send_humi_temp_timer_callback, RT_NULL, 30000, RT_TIMER_FLAG_SOFT_TIMER | RT_TIMER_FLAG_PERIODIC);
if(send_humi_temp_timer == RT_NULL)
{
rt_kprintf("[EDP]send_humi_temp_timer create failed\r\n");
return RT_ERROR;
}
/* 连接设备线程 */
link_dev_thread = rt_thread_create("link_dev_thread", onenet_edp_link_device_thread, RT_NULL, 2048, 4, 200);
if(link_dev_thread == RT_NULL)
{
rt_kprintf("[EDP]link_dev_thread create failed\r\n");
return RT_ERROR;
}
rt_thread_startup(link_dev_thread);
/* 发送心跳线程 */
send_heart_thread = rt_thread_create("send_heart_thread", onenet_edp_send_heart_thread, RT_NULL, 1024, 4, 50);
if(send_heart_thread == RT_NULL)
{
rt_kprintf("[EDP]send_heart_thread create failed\r\n");
return RT_ERROR;
}
rt_thread_startup(send_heart_thread);
/* 平台下发命令或结果处理线程 */
recv_data_pro_thread = rt_thread_create("recv_data_pro_thread",onenet_edp_receive_process_thread, RT_NULL, 4096, 4, 100);
if(recv_data_pro_thread == RT_NULL)
{
rt_kprintf("[EDP]recv_data_pro_thread create failed\r\n");
return RT_ERROR;
}
rt_thread_startup(recv_data_pro_thread);
/* 获取温湿度数据线程 */
get_humi_temp_thread = rt_thread_create("get_humi_temp_thread", onenet_edp_get_humi_temp_thread, RT_NULL, 4096, 4, 50);
if(get_humi_temp_thread == RT_NULL)
{
rt_kprintf("[EDP]get_humi_temp_thread create failed\r\n");
return RT_ERROR;
}
rt_thread_startup(get_humi_temp_thread);
/* 上报温湿度数据到平台线程 */
send_humi_temp_thread = rt_thread_create("send_humi_temp_thread", onenet_edp_send_humi_temp_thread, RT_NULL, 4096, 4, 100);
if(send_humi_temp_thread == RT_NULL)
{
rt_kprintf("[EDP]send_humi_temp_thread create failed\r\n");
return RT_ERROR;
}
rt_thread_startup(send_humi_temp_thread);
return RT_EOK;
}[/mw_shl_code]
2、完成信号量创建、软件定时器的线程创建后,会进行注册网络就绪事件回调、网络断开事件回调、以及连接网络、开启自动连接网络:
[mw_shl_code=c,true] /* 注册网络就绪回调、 */
rt_wlan_register_event_handler(RT_WLAN_EVT_READY, wlan_ready_handler, RT_NULL);
/* 注册网络断开事件回调 */
rt_wlan_register_event_handler(RT_WLAN_EVT_STA_DISCONNECTED, wlan_station_disconnect_handler, RT_NULL);
/* 连接路由器 */
rt_wlan_connect(WLAN_SSID, WLAN_PASSWORD);
rt_thread_mdelay(1000);
/* 开启自动连接 */
wlan_autoconnect_init();
rt_wlan_config_autoreconnect(RT_TRUE);[/mw_shl_code]
3、网络就绪回调函数用于释放连接OneNET信号量,告诉系统已经有网络了,可以去连接OneNET了:
[mw_shl_code=c,true] void wlan_ready_handler(int event, struct rt_wlan_buff *buff, void *parameter)
{
rt_kprintf("wlan ready\r\n");
if(link_onenet_sem != RT_NULL)
{
rt_sem_release(link_onenet_sem);
}
}[/mw_shl_code]
而另一方面,连接OneNET线程会移植挂起等待连接OneNET的信号量,指导获取到这个信号量才开始去连接OneNET:
[mw_shl_code=c,true] /**************************************************************
函数名称nenet_edp_link_device_thread
函数功能:连接OneNET平台设备线程
输入参数:parameter:线程入口参数
返 回 值:无
备 注:WiFi注册上网了后连接平台,连接成功则启动定时器发送心跳
**************************************************************/
void onenet_edp_link_device_thread(void *parameter)
{
rt_err_t result = RT_ERROR;
while(1)
{
result = rt_sem_take(link_onenet_sem, RT_WAITING_FOREVER); /* 等待时间:一直等 */
if(RT_EOK == result)
{
#ifdef USING_DEVICEID_APIKEY_LINK
onenet_edp_link_device(ONENET_EDP_SRV_ADDR, ONENET_EDP_SRV_PORT, ONENET_EDP_DEVICE_ID, ONENET_EDP_PRODUCT_APIKEY);
#else
onenet_edp_link_device(ONENET_EDP_SRV_ADDR, ONENET_EDP_SRV_PORT, ONENET_EDP_PRODUCT_ID, ONENET_EDP_DEVICE_AUTHKEY);
#endif
}
rt_thread_mdelay(1);
rt_thread_yield();/* 放弃剩余时间片,进行一次线程切换 */
}
}[/mw_shl_code]
三、数据上报和心跳发送思路
1、连接上OneNET之后,处理平台下发命令或结果的线程会得到连接成功返回结果并进行处理,启动软件定时器进行发送心跳和上报数据:
[mw_shl_code=c,true] case 0:
{
rt_kprintf("Tips: 连接成功\r\n");
if(send_heart_timer != RT_NULL)
{
rt_kprintf("[EDP]start send_heart_timer\r\n");
rt_timer_start(send_heart_timer);/* 启动软件定时器定时发送心跳 */
}
if(send_humi_temp_timer != RT_NULL)
{
rt_kprintf("[EDP]start send_humi_temp_timer\r\n");
rt_timer_start(send_humi_temp_timer);/* 启动软件定时器定时上报数据 */
}
break;
}[/mw_shl_code]
2、软件定时器回调函数用于释放发送心跳信号量:
[mw_shl_code=c,true] /**************************************************************
函数名称:send_heart_timer_callback
函数功能:软件定时器回调函数,用于定时发送信号量来触发发送心跳包
输入参数:parameter:入口参数
返 回 值:无
备 注:无
**************************************************************/
void send_heart_timer_callback(void *parameter)
{
if(send_heart_timer != RT_NULL)
{
rt_sem_release(send_heart_sem);
}
}[/mw_shl_code]
3、发送心跳线程获取到信号量之后执行发送心跳操作:
[mw_shl_code=c,true] /**************************************************************
函数名称nenet_edp_send_heart_thread
函数功能:发送心跳线程
输入参数:parameter:线程入口参数
返 回 值:无
备 注:无
**************************************************************/
void onenet_edp_send_heart_thread(void *parameter)
{
rt_err_t result = RT_ERROR;
while(1)
{
result = rt_sem_take(send_heart_sem, RT_WAITING_FOREVER); /* 等待时间:一直等 */
if(RT_EOK == result)
{
onenet_edp_send_heart();
}
rt_thread_mdelay(1);
rt_thread_yield();/* 放弃剩余时间片,进行一次线程切换 */
}
}[/mw_shl_code]
4、同理,上报数据也是等待信号量,一旦获取到就上报数据,为保证数据上报的同步,获取数据与上报数据用来二值信号量,实现代码:
[mw_shl_code=c,true] /**************************************************************
函数名称nenet_edp_get_humi_temp_thread
函数功能:获取温湿度数据线程
输入参数:parameter:线程入口参数
返 回 值:无
备 注:无
**************************************************************/
void onenet_edp_get_humi_temp_thread(void *parameter)
{
rt_err_t result = RT_ERROR;
while(1)
{
result = rt_sem_take(send_humi_temp_sem, RT_WAITING_FOREVER); /* 等待时间:一直等 */
if(RT_EOK == result)
{
result = rt_sem_take(humi_temp_sem, RT_WAITING_FOREVER); /* 等待时间:一直等 */
if(RT_EOK == result)
{
aht10_read_data(&g_temperature, &g_humidity);
}
rt_sem_release(humi_temp_sem); /* 释放信号量 */
}
rt_thread_mdelay(1);
rt_thread_yield();/* 放弃剩余时间片,进行一次线程切换 */
}
}
/**************************************************************
函数名称nenet_edp_send_humi_temp_thread
函数功能:发送温湿度数据线程
输入参数:parameter:线程入口参数
返 回 值:无
备 注:无
**************************************************************/
void onenet_edp_send_humi_temp_thread(void *parameter)
{
rt_err_t result = RT_ERROR;
while(1)
{
result = rt_sem_take(send_humi_temp_sem, RT_WAITING_FOREVER); /* 等待时间:一直等 */
if(RT_EOK == result)
{
result = rt_sem_take(humi_temp_sem, RT_WAITING_FOREVER); /* 等待时间:一直等 */
if(RT_EOK == result)
{
/* 上传温湿度数据到平台 */
if(RT_EOK == onenet_edp_send_data(FORMAT_TYPE3, ONENET_EDP_DEVICE_ID, ONENET_EDP_PRODUCT_APIKEY, humi_temp_data_stream, humi_temp_data_stream_cnt))
{
rt_kprintf("Humi_Temp Send success\n");
}
else
{
rt_kprintf("Humi_Temp Status Send failed\n");
}
}
rt_sem_release(humi_temp_sem); /* 释放信号量 */
}
rt_thread_mdelay(1);
rt_thread_yield();/* 放弃剩余时间片,进行一次线程切换 */
}
}[/mw_shl_code]
四、设备断开与网络断开处理
1、如果在有网络的情况下设备突然断开,那么就需要重新连接OneNET了,首先会停止所有定时器、接着重新发送连接OneNET信号量,处理如下:
[mw_shl_code=c,true] case DISCONNECT:
{
rt_kprintf("WARN:连接断开,准备重连\r\n");
if(send_heart_timer != RT_NULL)
{
rt_kprintf("[EDP]stop send_heart_timer\r\n");
rt_timer_stop(send_heart_timer);/* 关闭软件定时器 */
}
if(send_humi_temp_timer != RT_NULL)
{
rt_kprintf("[EDP]stop send_humi_temp_timer\r\n");
rt_timer_stop(send_humi_temp_timer);/* 关闭软件定时器 */
}
if(link_onenet_sem != RT_NULL)
{
rt_sem_release(link_onenet_sem);
}
break;
}[/mw_shl_code]
2、如果是网络断开,那么就需要停止软件定时器,处理如下:
[mw_shl_code=c,true] void wlan_station_disconnect_handler(int event, struct rt_wlan_buff *buff, void *parameter)
{
rt_kprintf("disconnect from the network!\n");
if(send_heart_timer != RT_NULL)
{
rt_kprintf("[EDP]stop send_heart_timer\r\n");
rt_timer_stop(send_heart_timer);/* 关闭软件定时器 */
}
if(send_humi_temp_timer != RT_NULL)
{
rt_kprintf("[EDP]stop send_humi_temp_timer\r\n");
rt_timer_stop(send_humi_temp_timer);/* 关闭软件定时器 */
}
}[/mw_shl_code]
五、在OneNET实现的应用示例
点击跳转至在OneNET实现的应用
六、对流过程Finsh打印的信息图片
1、连接成功
2、发送心跳成功
3、上报数据成功
4、接收命令执行操作以及上报状态成功:
5、数据流
|
|