【開源】STM32+ESP8266+MQTT多傳感器數據上雲OneNET(易拓展)
文章目錄
簡介: STM32+ESP8266通過MQTT協議將多傳感器數據傳輸至OnenNet雲平臺並遠程控制單片機LED, 加入操作系統FreeRTOS進行多任務管理,降低模塊間耦合性,增刪模塊和功能簡單方便,提高開發效率,可以根據自己的需求快速增加其他傳感器模塊
拓展新模塊(新功能)簡單,幾乎不需要多少操作系統知識,後面有詳細教程(深入修改還是需要一定的操作系統知識的)
注:部分功能採用他人開源程序或在他人開源程序的基礎上修改。
1. 相關連接
1.1 本項目相關連接
- github(源碼):https://github.com/Mbwide/MQTT_ONENET_ESP8266_STM32_FREERTOS
- CSDN(圖文解析):
- Bilibili(視頻解析):https://www.bilibili.com/video/BV1Hf4y1k7U3
1.2 無操作系統簡易版(舊版,有OneNET雲平臺設備創建和應用配置,本文不再贅述):
- github(源碼): https://github.com/Mbwide/DHT11_ToOneNetByMqtt
- CSDN(圖文解析):https://blog.csdn.net/ssssadw/article/details/111584510
- Bilibili(視頻解析):https://www.bilibili.com/video/BV1Vi4y1w7U1
2. 具體功能
- 基於嵌入式操作系統FreeRTOS進行多任務管理,降低模塊間耦合性,增刪模塊和功能簡單方便,提高開發效率
- DHT11採集環境溫溼度數據,ESP8266模塊通過MQTT協議將溫溼度數據傳輸至OnenNet雲平臺
- BH1750採集光照強度數據,ESP8266模塊通過MQTT協議將光照強度數據傳輸至OnenNet雲平臺
- OneNET可以通過雲平臺遠程控制LED1的亮滅
- OneNET可以通過雲平臺遠程控制LED2功能任務是否執行
- 串口顯示相關數據信息
3. 硬件環境
- 正點原子STM32F103RCT6(正點原子MiniSTM32)
- DHT11溫溼度傳感器
- BH1750(GY30)光照強度傳感器
- ESP8266-01S無線模塊
4. 雲平臺環境配置
見1.2無操作系統簡易版(舊版,有OneNET雲平臺設備創建和應用配置,本文不再贅述)
5. 接線
-
ESP8266-01S(5根線)
- RX PA2
- TX PA3
- 復位 PA4
- VCC 3V3
- GND GND
-
DHT11(3根線)
- DATA PA6
- VCC 3V3
- GND GND
-
BH1750(5根線)
- SCL PC12
- SDA PC11
- ADDR GND
- VCC 5V
- GND GND
-
LED
- LED1 PD2
- LED2 PA8
6. 功能展示
6.1 數據流
6.2 APP應用管理
6.3 串口數據
7. 源碼詳解
本次代碼改寫目的是增強拓展性,降低開發難度,所以加入操作系統FreeRTOS進行多任務管理,降低模塊(傳感器,控制)間耦合性,增刪模塊和功能簡單方便,提高開發效率
7.1 源碼文件解析
- stm32f10x_it.c:中斷處理函數
- FreeRTOSConfig.h:FreeRTOS配置頭文件
- usart1.c:與串口住手通信
- usart2.c:與ESP8266通信
- timer3.c:定時器3中斷用來發送心跳包(ping,用於保持和服務器連接,長時間沒給服務器發送數據會被踢下線),2s和30s兩種模式
- timer4.c:將串口2接收到的服務器數據依次存放在MQTT接收緩存數組中,50ms沒有新數據收到執行
- control.c:發送控制設備數據,存放至發送緩衝區
- dht11.c:DHT11(溫溼度傳感器)驅動
- bh1750.c:BH1750(GY30,光照強度傳感器)驅動
- wifi.c:esp8266的wifi驅動
- mqtt.c:mqtt協議處理
- FreeRTOS_CORE:freeRTOS功能核心
- FreeRTOS_PORTABLE:freeRTOS板級支持包,和芯片相關,包括接口和內存分配
7.2 服務器與wifi配置相關
/*-------------------------------------------------------------*/
/* WIFI網絡與ONENET配置(配置) */
/*-------------------------------------------------------------*/
const char SSID[] = "PPP"; //路由器名稱
const char PASS[] = "qaz123qaz"; //路由器密碼
const char PRODUCTID[] = "394499"; //產品ID(改成自己的)
const char DEVICEID [] = "661126800"; //設備ID(改成自己的)
const char AUTHENTICATION[] = "123456"; //鑑權信息(改成自己的)
const char DATA_TOPIC_NAME[] = "$dp"; //topic,Onenet數據點上傳topic(不用改)
const char SERVER_IP[] = "183.230.40.39";//存放服務器IP或域名(不用改)
const int SERVER_PORT = 6002; //存放服務器的端口號(不用改)
-
具體OneNET雲平臺設備創建和應用配置見:
1.2 無操作系統簡易版(舊版,有OneNET雲平臺設備創建和應用配置,本文不再贅述)
Bilibili(視頻解析):https://www.bilibili.com/video/BV1Vi4y1w7U1
-
拓展傳感器模塊詳細演示見:
1.1 本項目相關連接 :
Bilibili(視頻解析)
7.3 源碼框架
-
括號裏有“配置”字樣的部分是用戶必須修改的部分(例程已經配置了LED控制,環境溫溼度檢測和光照強度監測)
-
紅色部分爲拓展功能模塊需要獨立編寫或者修改的地方
紅色虛線部分根據功能更改,設備控制更改創建MQTT命令緩衝處理任務,傳感器設備任務需要向消息隊列發送傳感器數據
- 初始化功能模塊:添加拓展模塊的初始化函數
- 創建用戶任務:添加拓展模塊任務(傳感器數據讀取或設備控制)
- 創建MQTT命令緩衝處理任務:添加設備控制命令,併發送設備狀態(傳感器設備無需修改)
- 創建其他模塊任務:實現傳感器數據採集功能或者設備控制相關功能,實現傳感器數據採集功能時需要紅色虛線部分,設備控制不需要。
-
要實現連接服務器時發送控制設備初始狀態的功能需要在創建MQTT數據接收發送緩衝處理任務的connect成功部分添加數據發送函數
-
其他部分可根據實際需求修改
7.4 main.c源碼及解析
/*------------------------------------------------------*/
/* */
/* 程序main函數,入口函數源文件 */
/* */
/*------------------------------------------------------*/
#include "sys.h"
#include "delay.h" //包含需要的頭文件
#include "usart1.h" //包含需要的頭文件
#include "usart2.h" //包含需要的頭文件
#include "timer3.h" //包含需要的頭文件
#include "timer4.h" //包含需要的頭文件
#include "FreeRTOS.h" //FreeRTOS配置頭文件
#include "semphr.h" //信號量
#include "queue.h" //隊列
#include "event_groups.h"//事件標誌組
#include "wifi.h" //包含需要的頭文件
#include "mqtt.h" //包含需要的頭文件
#include "control.h" //包含需要的頭文件 控制模塊相關數據發送給服務器
#include "led.h" //包含需要的頭文件 LED
#include "dht11.h" //包含需要的頭文件 空氣溫溼度
#include "bh1750.h" //包含需要的頭文件 光照傳感器
/*-------------------------------------------------------------*/
/* WIFI網絡與ONENET配置(配置) */
/*-------------------------------------------------------------*/
const char SSID[] = "PPP"; //路由器名稱
const char PASS[] = "qaz123qaz"; //路由器密碼
const char PRODUCTID[] = "394499"; //產品ID
const char DEVICEID [] = "661126800"; //設備ID
const char AUTHENTICATION[] = "123456"; //鑑權信息
const char DATA_TOPIC_NAME[] = "$dp"; //topic,Onenet數據點上傳topic(不用改)
const char SERVER_IP[] = "183.230.40.39";//存放服務器IP或域名(不用改)
const int SERVER_PORT = 6002; //存放服務器的端口號(不用改)
/*-------------------------------------------------------------*/
/* 控制命令以及控制模塊初始狀態設置(配置) */
/*-------------------------------------------------------------*/
/* 消息體:
* {
* "data_1":"value_1",
* "data_2":"value_2"
* }
* 消息體示例:
* {"led1_flag":"LED1ON"}
*/
const char *LED1_LABER = "led1_flag";//LED1標籤,發送給ONENET的數據流名稱
const char *CMD_LED1ON = "LED1ON"; //LED1打開
const char *CMD_LED1OFF = "LED1OFF"; //LED1關閉
char *led1_flag = "LED1OFF"; //LED1狀態,初始化爲關閉狀態
const char *LED2_LABER = "led2_flag";//LED2標籤
const char *CMD_LED2ON = "LED2ON"; //LED2打開
const char *CMD_LED2OFF = "LED2OFF"; //LED2關閉
char *led2_flag = "LED2ON"; //LED2狀態,初始化爲打開狀態
/*-------------------------------------------------------------*/
/* freerto任務通信控制(固定) */
/*-------------------------------------------------------------*/
/* 二值信號量句柄
* 作用:用於控制MQTT命令緩衝處理任務,在MQTT數據接收發送緩衝處理任務中發出
* 當命令緩衝區收到命令數據時,發出信號量
*/
SemaphoreHandle_t BinarySemaphore;
/* 事件標誌組
* 作用:標誌WIFI連接,PING心跳包發送模式控制wifi是否重啓連接,是否發送數據,傳感器是否運行
* 具體:1.事件標誌組位1爲0,位0爲1時,即0x03(0000 0001),wifi連接至服務器時位0置位1,此時connect報文還未發送。
* 2.事件標誌組位1爲1,位0爲1時,即0x03(0000 0011),connect報文發送,返回連接成功報文時位1置位1,PING心
* 跳包開啓30s發送模式,傳感器任務開啓,數據開始上傳,設備遠程控制(LED控制)功能開啓。
*/
EventGroupHandle_t Event_Handle = NULL; //事件標誌組(位0:WIFI連接狀態 位1:PING心跳包2S快速發送模式)
const int WIFI_CONECT = (0x01 << 0); //設置事件掩碼的位 0;服務器連接模式,值1表示已經連接,0表示未連接
const int PING_MODE = (0x01 << 1); //設置事件掩碼的位 1;PING心跳包發送模式,1表示開啓30S發送模式,0表示未開啓發送或開啓2S快速發送模式
/* 傳感器數據發送消息隊列
* 作用:將傳感器的數據發送到傳感器消息隊列
*/
QueueHandle_t Message_Queue; //消息隊列句柄
const UBaseType_t MESSAGE_DATA_TX_NUM = 5; //消息隊列最大消息數目
const UBaseType_t MESSAGE_DATA_TX_LEN = 100;//消息隊列單元大小,單位爲字節
/*-------------------------------------------------------------*/
/* 任務句柄及任務函數聲明1(配置) */
/*-------------------------------------------------------------*/
//開始任務
TaskHandle_t StartTask_Handler;
void my_start_task(void *pvParameters);
//LED任務
TaskHandle_t Led2_Task_Handler;
void my_led2_task(void *pvParameters);
//DHT11任務 溫溼度傳感器
TaskHandle_t DHT11_Task_Handler;
void my_dht11_task(void *pvParameters);
//SUN任務,光照傳感器
TaskHandle_t SUN_Task_Handler;
void my_sun_task(void *pvParameters);
//MQTT命令緩衝處理任務
TaskHandle_t MQTT_Cmd_Task_Handler;
void my_mqtt_buffer_cmd_task(void *pvParameters);
/*-------------------------------------------------------------*/
/* 任務句柄及任務函數聲明2(固定) */
/*-------------------------------------------------------------*/
//WIFI任務
TaskHandle_t WIFI_Task_Handler;
void wifi_task(void *pvParameters);
//MQTT數據接收發送緩衝處理任務
TaskHandle_t MQTT_RxTx_Task_Handler;
void mqtt_buffer_rx_tx_task(void *pvParameters);
//傳感器數據處理任務,處理待發送的傳感器數據,移入MQTT數據發送緩衝區
TaskHandle_t DATA_TX_Task_Handler;
void data_tx_to_buffer_task(void *pvParameters);
/*---------------------------------------------------------------*/
/*函數名:int main() */
/*功 能:主函數 */
/* 1.初始化各功能模塊 */
/* 2.創建開始任務(在開始任務裏創建所有其他任務) */
/* 3.開啓任務調度 */
/*參 數:無 */
/*返回值:無 */
/*---------------------------------------------------------------*/
int main()
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//設置系統中斷優先級分組4
delay_init(); //延時函數初始化
usart1_init(115200); //串口1功能初始化,波特率115200,與串口住手通信
usart2_init(115200); //串口2功能初始化,波特率115200,wifi通信
tim4_init(500,7200); //TIM4初始化,定時時間 500*7200*1000/72000000 = 50ms
led_init(); //初始化LED
dht11_init(); //初始化DHT11 溫溼度
iic_by30_init(); //初始化IIC接口 光照強度
wifi_reset_io_init(); //初始化esp8266
IoT_parameter_init(); //初始化OneNET平臺MQTT服務器的參數
//創建開始任務
xTaskCreate((TaskFunction_t ) my_start_task, //任務函數
(const char* )"my_start_task", //任務名稱
(uint16_t ) 128, //任務堆棧大小
(void* ) NULL, //傳遞給任務函數的參數
(UBaseType_t ) 1, //任務優先級
(TaskHandle_t* ) &StartTask_Handler); //任務控制塊
vTaskStartScheduler(); //開啓任務調度
}
/*---------------------------------------------------------------*/
/*函數名:void my_start_task(void *pvParameters) */
/*功 能:開始任務(配置) */
/* 1.創建信號量,消息隊列等任務通信方式 */
/* 2.創建所有任務 */
/* 3.刪除本身 */
/*參 數:無 */
/*返回值:無 */
/*---------------------------------------------------------------*/
void my_start_task(void *pvParameters)
{
taskENTER_CRITICAL(); //進入臨界區
//創建二值信號量
BinarySemaphore = xSemaphoreCreateBinary();
//事件標誌組,用於標誌wifi連接狀態以及ping發送狀態
Event_Handle = xEventGroupCreate();
//創建傳感器消息體消息隊列
Message_Queue = xQueueCreate(MESSAGE_DATA_TX_NUM, MESSAGE_DATA_TX_LEN);
//任務創建函數參數;1.任務函數 2.任務名稱 3.任務堆棧大小 3.傳遞給任務函數的參數 4.任務優先級 5.任務控制塊
//創建WIFI任務
xTaskCreate(wifi_task, "wifi_task", 128, NULL, 7, &WIFI_Task_Handler);
//創建MQTT命令緩衝處理任務
xTaskCreate(my_mqtt_buffer_cmd_task,"my_mqtt_buffer_cmd_task", 128, NULL, 6, &MQTT_Cmd_Task_Handler);
//創建MQTT數據接收發送緩衝處理任務
xTaskCreate(mqtt_buffer_rx_tx_task, "mqtt_buffer_rx_tx_task", 256, NULL, 5, &MQTT_RxTx_Task_Handler);
//創建led控制任務
xTaskCreate(my_led2_task, "my_led2_task", 128, NULL, 4, &Led2_Task_Handler);
//創建DHT11任務,溫溼度傳感器
xTaskCreate(my_dht11_task, "my_dht11_task", 128, NULL, 3, &DHT11_Task_Handler);
//創建SUN任務,光照傳感器
xTaskCreate(my_sun_task, "my_sun_task", 128, NULL, 3, &SUN_Task_Handler);
//創建傳感器數據處理任務,處理待發送的傳感器數據,移入MQTT數據發送緩衝區
xTaskCreate(data_tx_to_buffer_task, "data_tx_to_buffer_task", 512, NULL, 2, &DATA_TX_Task_Handler);
vTaskDelete(StartTask_Handler); //刪除開始任務
taskEXIT_CRITICAL(); //退出臨界區
}
/*---------------------------------------------------------------*/
/*函數名:void my_mqtt_buffer_cmd_task(void *pvParameters) */
/*功 能:MQTT命令緩衝處理任務(配置) */
/* 1.MQTT命令緩衝區處理,並執行相應命令 */
/* 2.將命令執行結果發送給服務器 */
/*參 數:無 */
/*返回值:無 */
/*其 他:獲取到二值信號量時執行(收到服務器命令) */
/*---------------------------------------------------------------*/
void my_mqtt_buffer_cmd_task(void *pvParameters)
{
while(1)
{
xSemaphoreTake(BinarySemaphore, portMAX_DELAY); //獲取信號量,獲取到信號量,繼續執行,否則進入阻塞態,等待執行
if(MQTT_CMDOutPtr != MQTT_CMDInPtr) //if成立的話,說明命令緩衝區有數據了
{
printf("命令:%s\r\n", &MQTT_CMDOutPtr[2]);
if(!memcmp(&MQTT_CMDOutPtr[2], CMD_LED1ON, strlen(CMD_LED1ON)))
{
led1_on(); //LED1開啓
led1_flag = "LED1ON"; //LED1狀態,用於發送給服務器
send_data(LED1_LABER, led1_flag);
}
else if(!memcmp(&MQTT_CMDOutPtr[2], CMD_LED1OFF, strlen(CMD_LED1OFF)))
{
led1_off(); //LED1關閉
led1_flag = "LED1OFF";//LED1狀態,用於發送給服務器
send_data(LED1_LABER, led1_flag);
}
if(!memcmp(&MQTT_CMDOutPtr[2], CMD_LED2ON, strlen(CMD_LED2ON)))
{
vTaskResume(Led2_Task_Handler); //LED2任務由掛起態轉爲就緒態,LED2任務運行
led2_flag = "LED2ON"; //LED2狀態,用於發送給服務器
send_data(LED2_LABER, led2_flag);
}
else if(!memcmp(&MQTT_CMDOutPtr[2], CMD_LED2OFF, strlen(CMD_LED2OFF)))
{
vTaskSuspend(Led2_Task_Handler); //LED2任務由就緒態(運行態)轉爲掛起態,LED2任務掛起(停止)
led2_flag = "LED2OFF"; //LED2狀態,用於發送給服務器
send_data(LED2_LABER, led2_flag);
}
//不做處理,後面會發送狀態
else printf("未知指令\r\n");
MQTT_CMDOutPtr += CBUFF_UNIT; //指針下移
if(MQTT_CMDOutPtr == MQTT_CMDEndPtr)//如果指針到緩衝區尾部了
MQTT_CMDOutPtr = MQTT_CMDBuf[0]; //指針歸位到緩衝區開頭
}
delay_ms(10);
}
}
/*---------------------------------------------------------------*/
/*函數名:void my_dht11_task(void *pvParameters) */
/*功 能:DHT11任務 溫溼度傳感器(配置) */
/* 1.檢測環境溫溼度數據 */
/* 2.將環境溫溼度數據放入傳感器數據消息隊列 */
/*參 數:無 */
/*返回值:無 */
/*其 他:服務器連接以及PING心跳包30S發送模式事件發生時執行此任務, */
/* 否則掛起任務 */
/*---------------------------------------------------------------*/
void my_dht11_task(void *pvParameters)
{
while(1)
{
char humidity; //定義一個變量,保存溼度值
char temperature; //定義一個變量,保存溫度值
char data_of_sensor[50] = {
0};
//服務器連接以及PING心跳包30S發送模式事件發生時執行此任務,否則掛起任務
xEventGroupWaitBits((EventGroupHandle_t )Event_Handle,
(EventBits_t )PING_MODE,
(BaseType_t )pdFALSE,
(BaseType_t )pdTRUE,
(TickType_t )portMAX_DELAY);
dht11_read_data(&temperature, &humidity);//讀取溫溼度值
//構建消息體
//消息體:"temperature":"%d","humidity":"%d",
sprintf(data_of_sensor, "\"temperature\":\"%d\",\"humidity\":\"%d\",", temperature, humidity);
xQueueSend(Message_Queue, &data_of_sensor, portMAX_DELAY);//向消息隊列發送消息體,將溫溼度數據放入傳感器數據消息隊列
//printf("temperature: %d, humidity: %d \r\n", temperature, humidity);
delay_ms(10 * 1000);//延時10s
}
}
/*---------------------------------------------------------------*/
/*函數名:void my_led2_task(void *pvParameters) */
/*功 能:LED任務(配置) */
/* 1.LED2任務執行 */
/*參 數:無 */
/*返回值:無 */
/*其 他:服務器連接以及ping心跳包30S發送模式事件發生時執行此任務, */
/* 否則掛起任務 */
/*---------------------------------------------------------------*/
void my_led2_task(void *pvParameters)
{
while(1)
{
//服務器連接以及ping心跳包30S發送模式事件發生時執行此任務,否則掛起任務
xEventGroupWaitBits((EventGroupHandle_t )Event_Handle,
(EventBits_t )PING_MODE,
(BaseType_t )pdFALSE,
(BaseType_t )pdTRUE,
(TickType_t )portMAX_DELAY);
led2_on();
delay_ms(500); //延時500ms
led2_off();
delay_ms(500); //延時500ms
}
}
/*---------------------------------------------------------------*/
/*函數名:void my_sun_task(void *pvParameters) */
/*功 能:SUN任務,光照傳感器(配置) */
/* 1.檢測光照強度 */
/* 2.將光照強度數據放入傳感器數據消息隊列 */
/*參 數:無 */
/*返回值:無 */
/*其 他:服務器連接以及PING心跳包30S發送模式事件發生時執行此任務, */
/* 否則掛起任務 */
/*---------------------------------------------------------------*/
void my_sun_task(void *pvParameters)
{
while(1)
{
int sun_light; //定義一個變量,保存光照強度
char data_of_sensor[50] = {
0};
//服務器連接以及ping心跳包30S發送模式事件發生時執行此任務,否則掛起任務
xEventGroupWaitBits((EventGroupHandle_t )Event_Handle,
(EventBits_t )PING_MODE,
(BaseType_t )pdFALSE,
(BaseType_t )pdTRUE,
(TickType_t )portMAX_DELAY);
sun_light = get_sunlight_value();
//構建消息體
//消息體:"sunlight":"%d",
sprintf(data_of_sensor, "\"sunlight\":\"%d\",", sun_light); //構建消息體的一部分
xQueueSend(Message_Queue, &data_of_sensor, portMAX_DELAY); //向消息隊列發送消息體,將光照強度數據放入傳感器數據消息隊列
//printf("sunlight: %d \r\n", sun_light);
delay_ms(10 * 1000); //延時10s
}
}
/*---------------------------------------------------------------*/
/*函數名:void wifi_task(void *pvParameters) */
/*功 能:WIFI任務(固定) */
/* 1.連接wifi以及雲服務器 */
/* 2.斷線重連 */
/*參 數:無 */
/*返回值:無 */
/*其 他:1.服務器連接前關閉發送ping包的定時器3,清除事件標誌位 */
/* 2.服務器已連接,拋出事件標誌,掛起自己,進入掛起態 */
/* 3.服務器或者wifi已斷開,清除事件標誌,繼續執行本任務重新 */
/* 連接 */
/*---------------------------------------------------------------*/
void wifi_task(void *pvParameters)
{
while(1)
{
printf("需要連接服務器\r\n");
TIM_Cmd(TIM4, DISABLE); //關閉TIM4
TIM_Cmd(TIM3, DISABLE); //關閉TIM3
xEventGroupClearBits(Event_Handle, PING_MODE);//關閉發送PING包的定時器3,清除事件標誌位
WiFi_RxCounter = 0; //WiFi接收數據量變量清零
memset(WiFi_RX_BUF, 0, WiFi_RXBUFF_SIZE); //清空WiFi接收緩衝區
if(WiFi_Connect_IoTServer() == 0) //如果WiFi連接雲服務器函數返回0,表示正確,進入if
{
printf("建立TCP連接成功\r\n");
WiFi_RxCounter = 0; //WiFi接收數據量變量清零
memset(WiFi_RX_BUF, 0, WiFi_RXBUFF_SIZE); //清空WiFi接收緩衝區
MQTT_Buff_Init(); //初始化發送緩衝區
xEventGroupSetBits(Event_Handle, WIFI_CONECT); //服務器已連接,拋出事件標誌
vTaskSuspend(NULL); //服務器已連接,掛起自己,進入掛起態(任務由掛起轉爲就緒態時在這繼續執行下去)
xEventGroupClearBits(Event_Handle, WIFI_CONECT);//服務器或者wifi已斷開,清除事件標誌,繼續執行本任務,重新連接
xEventGroupClearBits(Event_Handle, PING_MODE); //關閉發送PING包的定時器3,清除事件標誌位
}
delay_ms(10); //延時10s
}
}
/*---------------------------------------------------------------*/
/*函數名:void data_tx_to_buffer_task(void *pvParameters) */
/*功 能:傳感器數據處理任務(固定) */
/* 1.處理待發送的傳感器數據,移入MQTT數據發送緩衝區 */
/*參 數:無 */
/*返回值:無 */
/*其 他:服務器連接以及ping心跳包30S發送模式事件發生時執行此任務, */
/* 否則掛起任務 */
/*---------------------------------------------------------------*/
void data_tx_to_buffer_task(void *pvParameters)
{
while(1)
{
char data_buffer[255] = {
0};//數據包緩存區,初始化爲0
int data_len = 0; //數據包總長度,初始化爲0
int data_msg_len = 0; //消息體長度,初始化爲0
//服務器連接以及ping心跳包30S發送模式事件發生時執行此任務,否則掛起任務
xEventGroupWaitBits((EventGroupHandle_t )Event_Handle,
(EventBits_t )PING_MODE,
(BaseType_t )pdFALSE,
(BaseType_t )pdTRUE,
(TickType_t )portMAX_DELAY);
/* 數據包解析(數據類型3)
* 0:0x03 固定報頭(數據點類型,此處爲3)
* 1: 消息體長度高字節(消息體長度不超過255個字節則爲0x00)
* 2: 消息體長度低字節(消息體長度,即數據包長度,自己設置)
* 3-結尾 消息體
* 消息體:
* {
* "data_1":"value_1",
* "data_2":"value_2"
* }
* 消息體示例:
* {"temperature":"22","humidity":"33"}
*/
sprintf(data_buffer + 3, "{");//構建報文
if (xQueueReceive(Message_Queue, data_buffer + 4 + strlen(data_buffer + 4), 10))
{
while (xQueueReceive(Message_Queue, data_buffer + 4 + strlen(data_buffer + 4), 10))
{
}
sprintf(data_buffer + 3 + strlen(data_buffer + 4), "}");// "}"覆蓋消息體最後一個","
data_msg_len = strlen(data_buffer + 3); //消息體長度計算
data_buffer[0] = 0x03; //固定報頭
data_buffer[1] = data_msg_len >> 8 ; //消息體長度高字節
data_buffer[2] = data_msg_len & 0xFF; //消息體長度低字節
data_len = data_msg_len + 3; //數據包總長度
printf("%s\r\n", data_buffer + 3); //消息體通過串口回顯
taskENTER_CRITICAL(); //進入臨界區,防止中斷打斷
MQTT_PublishQs0(DATA_TOPIC_NAME, data_buffer, data_len);//添加數據,發佈給服務器
taskEXIT_CRITICAL(); //退出臨界區
}
delay_ms(10 * 1000); //延時10s
}
}
/*---------------------------------------------------------------*/
/*函數名:void mqtt_buffer_rx_tx_task(void *pvParameters) */
/*功 能:MQTT接收發送處理任務(固定) */
/* 1.處理髮送緩衝區數據 */
/* 2.處理接收緩衝區數據,並回顯給串口住手接收的數據;若接收 */
/* 緩衝區有服務器命令,則移至命令緩衝區 */
/*參 數:無 */
/*返回值:無 */
/*其 他:1.服務器連接事件發生執行此任務,否則掛起 */
/* 2.接收到服務器命令時給出二值信號量 */
/* 3.CONNECT報文成功,啓動30s的PING定時器,設置事件標誌位 */
/* 4.PING報文快速發送模式(2s)收到回覆,啓動30s的ping定時 */
/* 器,設置事件標誌位 */
/* 5.CONNECT報文失敗,WIFI連接服務器任務由掛起態轉爲就緒態, */
/* 重啓連接 */
/*---------------------------------------------------------------*/
void mqtt_buffer_rx_tx_task(void *pvParameters)
{
while(1)
{
//服務器連接事件發生執行此任務,否則掛起
xEventGroupWaitBits((EventGroupHandle_t )Event_Handle,
(EventBits_t )WIFI_CONECT,
(BaseType_t )pdFALSE,
(BaseType_t )pdTRUE,
(TickType_t )portMAX_DELAY);
/*-------------------------------------------------------------*/
/* 處理髮送緩衝區數據 */
/*-------------------------------------------------------------*/
if(MQTT_TxDataOutPtr != MQTT_TxDataInPtr) //if成立的話,說明發送緩衝區有數據了
{
//發送數據回顯
if(MQTT_TxDataOutPtr[2] == 0x30)
{
printf("發送數據:0x30,單片機數據推送至服務器\r\n");
}
else
{
printf("發送數據:0x%x\r\n", MQTT_TxDataOutPtr[2]);
}
MQTT_TxData(MQTT_TxDataOutPtr);
MQTT_TxDataOutPtr += TBUFF_UNIT;
if(MQTT_TxDataOutPtr == MQTT_TxDataEndPtr)
{
MQTT_TxDataOutPtr = MQTT_TxDataBuf[0];
}
}
/*-------------------------------------------------------------*/
/* 處理接收緩衝區數據 */
/*-------------------------------------------------------------*/
if(MQTT_RxDataOutPtr != MQTT_RxDataInPtr) //if成立的話,說明接收緩衝區有數據了
{
printf("接收到數據:");
//if判斷,如果第一個字節是0x30,表示收到的是服務器發來的推送數據
//我們要提取控制命令
if((MQTT_RxDataOutPtr[2] == 0x30))
{
printf("服務器等級0推送\r\n"); //串口輸出信息
MQTT_DealPushdata_Qs0(MQTT_RxDataOutPtr);//處理等級0推送數據
xSemaphoreGive(BinarySemaphore); //給出二值信號量,控制MQTT命令緩衝處理任務執行
}
//if判斷,如果第一個字節是0x20,表示收到的是CONNACK報文
//接着我們要判斷第4個字節,看看CONNECT報文是否成功
else if(MQTT_RxDataOutPtr[2] == 0x20)
{
switch(MQTT_RxDataOutPtr[5])
{
case 0x00: printf("CONNECT報文成功\r\n"); //CONNECT報文成功
TIM3_ENABLE_30S(); //啓動30s的PING定時器
xEventGroupSetBits(Event_Handle, PING_MODE); //啓動30s的PING定時器,設置事件標誌位
send_data(LED1_LABER,led1_flag); //發送控制模塊初始數據
send_data(LED2_LABER,led2_flag); //發送控制模塊初始數據
break;
case 0x01: printf("連接已拒絕,不支持的協議版本,準備重啓\r\n");
vTaskResume(WIFI_Task_Handler); //WIFI連接服務器任務由掛起態轉爲就緒態,重啓連接
break;
case 0x02: printf("連接已拒絕,不合格的客戶端標識符,準備重啓\r\n");
vTaskResume(WIFI_Task_Handler); //WIFI連接服務器任務由掛起態轉爲就緒態,重啓連接
break;
case 0x03: printf("連接已拒絕,服務端不可用,準備重啓\r\n");
vTaskResume(WIFI_Task_Handler); //WIFI連接服務器任務由掛起態轉爲就緒態,重啓連接
break;
case 0x04: printf("連接已拒絕,無效的用戶名或密碼,準備重啓\r\n");
vTaskResume(WIFI_Task_Handler); //WIFI連接服務器任務由掛起態轉爲就緒態,重啓連接
break;
case 0x05: printf("連接已拒絕,未授權,準備重啓\r\n");
vTaskResume(WIFI_Task_Handler); //WIFI連接服務器任務由掛起態轉爲就緒態,重啓連接
break;
default : printf("連接已拒絕,未知狀態,準備重啓\r\n");
vTaskResume(WIFI_Task_Handler); //WIFI連接服務器任務由掛起態轉爲就緒態,重啓連接
break;
}
}
//if判斷,第一個字節是0xD0,表示收到的是PINGRESP報文
else if(MQTT_RxDataOutPtr[2] == 0xD0)
{
printf("PING報文回覆\r\n");
if(pingFlag == 1)
{
//如果pingFlag=1,表示第一次發送
pingFlag = 0; //要清除pingFlag標誌
}
else if(pingFlag > 1)
{
//如果pingFlag>1,表示是多次發送了,而且是2s間隔的快速發送
pingFlag = 0; //要清除pingFlag標誌
TIM3_ENABLE_30S(); //PING定時器重回30s的時間
xEventGroupSetBits(Event_Handle, PING_MODE); //30s的PING定時器,設置事件標誌位
}
}
MQTT_RxDataOutPtr += RBUFF_UNIT; //指針下移
if(MQTT_RxDataOutPtr == MQTT_RxDataEndPtr) //如果指針到緩衝區尾部了
{
MQTT_RxDataOutPtr = MQTT_RxDataBuf[0]; //指針歸位到緩衝區開頭
}
}
delay_ms(100);//延時10ms
}
}
/*---------------------------------------------------------------*/
/*函數名:void stack_task(void *pvParameters) */
/*功 能:任務堆棧大小測試(固定) */
/* 1.查看任務運行時堆棧大小,用於調試 */
/*參 數:無 */
/*返回值:無 */
/*---------------------------------------------------------------*/
//void stack_task(void *pvParameters)
//{
// TaskHandle_t TaskHandle;
// TaskStatus_t TaskStatus;
// int i = 0;
// while(1)
// {
xEventGroupWaitBits((EventGroupHandle_t )Event_Handle,
(EventBits_t )WIFI_CONECT|PING_MODE,
(BaseType_t )pdFALSE,
(BaseType_t )pdTRUE,
(TickType_t )portMAX_DELAY);
LED_On();
delay_ms(500); //延時0.5s
LED_Off();
delay_ms(500); //延時0.5s
//
// for(i = 0; i < 5; i++)
// {
// if (i == 0)
// {
// TaskHandle = WIFI_Task_Handler; //根據任務名獲取任務句柄。
// }
// else if (i == 1)
// {
// TaskHandle = MQTT_Cmd_Task_Handler; //根據任務名獲取任務句柄。
// }
// else if (i == 2)
// {
// TaskHandle = MQTT_RxTx_Task_Handler; //根據任務名獲取任務句柄。
// }
// else if (i == 3)
// {
// TaskHandle = DHT11_Task_Handler; //根據任務名獲取任務句柄。
// }
// else if (i == 4)
// {
// TaskHandle = DATA_TX_Task_Handler; //根據任務名獲取任務句柄。
// }
//
// //獲取任務信息
// vTaskGetInfo((TaskHandle_t )TaskHandle, //任務句柄
// (TaskStatus_t* )&TaskStatus, //任務信息結構體
// (BaseType_t )pdTRUE, //允許統計任務堆棧歷史最小剩餘大小
// (eTaskState )eInvalid); //函數自己獲取任務運行壯態
// //通過串口打印出指定任務的有關信息。
// printf("任務名: %s\r\n",TaskStatus.pcTaskName);
// printf("任務編號: %d\r\n",(int)TaskStatus.xTaskNumber);
// printf("任務壯態: %d\r\n",TaskStatus.eCurrentState);
// printf("任務當前優先級: %d\r\n",(int)TaskStatus.uxCurrentPriority);
// printf("任務基優先級: %d\r\n",(int)TaskStatus.uxBasePriority);
// printf("任務堆棧基地址: %#x\r\n",(int)TaskStatus.pxStackBase);
// printf("任務堆棧歷史剩餘最小值:%d\r\n",TaskStatus.usStackHighWaterMark);
// }
// delay_ms(10 * 1000); //延時10s
// }
//}
8 總結
8.1 可以改進的地方
- 串口2接收esp8266發來的MQTT數據可使用DMA功能
- 定時器4處理串口2接收緩衝的MQTT數據可由中斷處理改爲任務處理
- 暫時無法兼容其他平臺,修改部分代碼(主要是傳感器數據處理任務)可實現兼容其他平臺,如百度雲。