【開源】STM32+ESP8266+MQTT多傳感器數據上雲OneNET(易拓展,帶操作系統FreeRTOS)

【開源】STM32+ESP8266+MQTT多傳感器數據上雲OneNET(易拓展)


簡介: STM32+ESP8266通過MQTT協議將多傳感器數據傳輸至OnenNet雲平臺並遠程控制單片機LED, 加入操作系統FreeRTOS進行多任務管理,降低模塊間耦合性,增刪模塊和功能簡單方便,提高開發效率,可以根據自己的需求快速增加其他傳感器模塊

拓展新模塊(新功能)簡單,幾乎不需要多少操作系統知識,後面有詳細教程(深入修改還是需要一定的操作系統知識的)

注:部分功能採用他人開源程序或在他人開源程序的基礎上修改。

1. 相關連接

1.1 本項目相關連接
  1. github(源碼):https://github.com/Mbwide/MQTT_ONENET_ESP8266_STM32_FREERTOS
  2. CSDN(圖文解析):
  3. Bilibili(視頻解析):https://www.bilibili.com/video/BV1Hf4y1k7U3
1.2 無操作系統簡易版(舊版,有OneNET雲平臺設備創建和應用配置,本文不再贅述):
  1. github(源碼): https://github.com/Mbwide/DHT11_ToOneNetByMqtt
  2. CSDN(圖文解析):https://blog.csdn.net/ssssadw/article/details/111584510
  3. Bilibili(視頻解析):https://www.bilibili.com/video/BV1Vi4y1w7U1

2. 具體功能

  1. 基於嵌入式操作系統FreeRTOS進行多任務管理,降低模塊間耦合性,增刪模塊和功能簡單方便,提高開發效率
  2. DHT11採集環境溫溼度數據,ESP8266模塊通過MQTT協議將溫溼度數據傳輸至OnenNet雲平臺
  3. BH1750採集光照強度數據,ESP8266模塊通過MQTT協議將光照強度數據傳輸至OnenNet雲平臺
  4. OneNET可以通過雲平臺遠程控制LED1的亮滅
  5. OneNET可以通過雲平臺遠程控制LED2功能任務是否執行
  6. 串口顯示相關數據信息

3. 硬件環境

  1. 正點原子STM32F103RCT6(正點原子MiniSTM32)
  2. DHT11溫溼度傳感器
  3. BH1750(GY30)光照強度傳感器
  4. ESP8266-01S無線模塊

4. 雲平臺環境配置

見1.2無操作系統簡易版(舊版,有OneNET雲平臺設備創建和應用配置,本文不再贅述)

5. 接線

  1. ESP8266-01S(5根線)

    • RX PA2
    • TX PA3
    • 復位 PA4
    • VCC 3V3
    • GND GND
  2. DHT11(3根線)

    • DATA PA6
    • VCC 3V3
    • GND GND
  3. BH1750(5根線)

    • SCL PC12
    • SDA PC11
    • ADDR GND
    • VCC 5V
    • GND GND
  4. 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;		   //存放服務器的端口號(不用改)

  1. 具體OneNET雲平臺設備創建和應用配置見:

    1.2 無操作系統簡易版(舊版,有OneNET雲平臺設備創建和應用配置,本文不再贅述

    ​ Bilibili(視頻解析):https://www.bilibili.com/video/BV1Vi4y1w7U1

  2. 拓展傳感器模塊詳細演示見:

    1.1 本項目相關連接

    ​ Bilibili(視頻解析)

7.3 源碼框架
  1. 括號裏有“配置”字樣的部分是用戶必須修改的部分(例程已經配置了LED控制,環境溫溼度檢測和光照強度監測)

  2. 紅色部分爲拓展功能模塊需要獨立編寫或者修改的地方

    紅色虛線部分根據功能更改,設備控制更改創建MQTT命令緩衝處理任務,傳感器設備任務需要向消息隊列發送傳感器數據

    • 初始化功能模塊:添加拓展模塊的初始化函數
    • 創建用戶任務:添加拓展模塊任務(傳感器數據讀取或設備控制)
    • 創建MQTT命令緩衝處理任務:添加設備控制命令,併發送設備狀態(傳感器設備無需修改
    • 創建其他模塊任務:實現傳感器數據採集功能或者設備控制相關功能,實現傳感器數據採集功能時需要紅色虛線部分,設備控制不需要。
  3. 要實現連接服務器時發送控制設備初始狀態的功能需要在創建MQTT數據接收發送緩衝處理任務的connect成功部分添加數據發送函數

  4. 其他部分可根據實際需求修改

在這裏插入圖片描述

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 可以改進的地方
  1. 串口2接收esp8266發來的MQTT數據可使用DMA功能
  2. 定時器4處理串口2接收緩衝的MQTT數據可由中斷處理改爲任務處理
  3. 暫時無法兼容其他平臺,修改部分代碼(主要是傳感器數據處理任務)可實現兼容其他平臺,如百度雲。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章