課程大綱
【第一章】:物聯網簡介(什麼是物聯網)
【第二章】:物聯網十大應用場景
【第三章】:什麼是MCU?
【第四章】:MCU的應用範圍
【第五章】:我們要怎麼入門MCU開發?
【第六章】:如何使用STM32Cube MX進行STM32的快速開發
【第七章】:ESP8266+MQTT上阿里雲物聯網平臺實踐(附源碼)
STM32CubeMX AT指令實現MQTT協議並接入阿里雲IOT平臺 源碼解析
CSDN源代碼下載
Github源代碼下載
目錄
- 課程大綱
- 7. 實踐:STM32使用ESP8266+MQTT上阿里雲物聯網平臺實踐
7. 實踐:STM32使用ESP8266+MQTT上阿里雲物聯網平臺實踐
7.1 項目整體介紹
7.1.1 硬件資源
- STM32F429IGT6開發板:核心
- ST-Link下載器:下載程序用
- USB轉232串口線:串口通信,調試用
- USB供電線:給開發板供電
- DHT11溫溼度模塊:採集環境溫溼度
- ATK-ESP8266:聯網上傳數據
7.1.2 軟件資源
- 串口調試助手(調試用)
7.1.3 其它資源
- STM32F429IGT6開發板原理圖
- DHT11溫溼度傳感器編程手冊
- ATK-ESP8266編程手冊
- ESP8266樣例程序
- 阿里雲MQTT樣例程序
7.2 新建工程
- 選擇通過選擇MCU創建工程
- 選擇芯片
- 建立成功
7.3 基礎工程配置
7.3.1 時鐘配置
- 高速時鐘:選擇外部晶振
- 高速時鐘選擇Crystal/Ceramic Resonator
在用cube配置時鐘時,有下面兩個選項
BYPASS Clock Source(旁路時鐘源)
Crystal/Ceramic Resonator(晶體/陶瓷晶振)
下面來解釋一下:
所謂HSE旁路時鐘源,是指無需使用外部晶體時所需的芯片內部時鐘驅動組件,直接從外界導入時鐘信號。猶如芯片內部的驅動組件被旁路了。
外部晶體/陶瓷諧振器(HSE晶體)模式該時鐘源是由外部無源晶體與MCU內部時鐘驅動電路共同配合形成,有一定的啓動時間,精度較高。
- 配置時鐘爲180MHz
7.3.2 下載接口設置,設置爲串行下載
7.3.3 LED燈配置
-
查看原理圖
LED_R——PH10 紅燈
LED_G——PH11 綠燈
LED_B——PH12 藍燈
低電平有效,IO口要設置爲推輓輸出,上拉
-
工程配置
-
選擇引腳設置爲GPIO_Ooutput
-
配置標籤 LED_R LED_G LED_B,方便工程直接調用
-
設置上下拉
-
7.3.4 按鍵輸入中斷設置
- 查看原理圖
- 工程設置
- 設置中斷等級,不能太高
7.3.5 調試串口設置
- 查看開發板原理圖,找到RS232
- 修改跳帽方向
- 串口參數配置
- 修改PD5爲USART2_TX
- 修改PD6爲USART2_RX
- PA2 PA3自動取消定義
7.3.6 ES8266串口設置
7.3.7 DHT11數據採集IO
7.3.8 工程概覽
7.4 生成並驗證工程
7.4.1 生成工程
- 點擊Project Manager並設置工程
- 勾選上單獨生成.c.h文件
- 點擊生成工程
- 查看工程
- 平臺已經自動生成USART1、USART2驅動和GPIO驅動
- 用戶只用專注於程序邏輯代碼,實現快速開發
7.4.2 編寫USART2串口調試驗證代碼
- 在文件usart.h中引入標準庫stdio.h(裏面包含printf)
#include "stdio.h"
- 在文件usart.c中添加printf重定向函數
// 重定向printf函數
int fputc(int ch,FILE *f)
{
uint8_t temp[1]={ch};
HAL_UART_Transmit(&huart2,temp,1,2);
return 0;
}
- 爲了方便調試,在main.c中加入以下宏定義
#define USER_MAIN_DEBUG
#ifdef USER_MAIN_DEBUG
#define user_main_printf(format, ...) printf( format "\r\n",##__VA_ARGS__)
#define user_main_info(format, ...) printf("【main】info:" format "\r\n",##__VA_ARGS__)
#define user_main_debug(format, ...) printf("【main】debug:" format "\r\n",##__VA_ARGS__)
#define user_main_error(format, ...) printf("【main】error:" format "\r\n",##__VA_ARGS__)
#else
#define user_main_printf(format, ...)
#define user_main_info(format, ...)
#define user_main_debug(format, ...)
#define user_main_error(format, ...)
#endif
- 在主函數while(1)循環中添加以下測試代碼
user_main_debug("我是USART2測試代碼!\n");
HAL_Delay(1000);
- 設置下載後自動運行,編譯運行,連接串口調試助手,觀察現象
- 測試成功!!
7.4.3 編寫三色燈測試代碼
- 在main.c 主循環添加以下測試代碼
//紅燈亮,其它滅
HAL_GPIO_WritePin(LED_R_GPIO_Port,LED_R_Pin,GPIO_PIN_RESET);
HAL_GPIO_WritePin(LED_G_GPIO_Port,LED_G_Pin,GPIO_PIN_SET);
HAL_GPIO_WritePin(LED_B_GPIO_Port,LED_B_Pin,GPIO_PIN_SET);
HAL_Delay(500);
//綠燈亮,其它滅
HAL_GPIO_WritePin(LED_R_GPIO_Port,LED_R_Pin,GPIO_PIN_SET);
HAL_GPIO_WritePin(LED_G_GPIO_Port,LED_G_Pin,GPIO_PIN_RESET);
HAL_GPIO_WritePin(LED_B_GPIO_Port,LED_B_Pin,GPIO_PIN_SET);
HAL_Delay(500);
//藍燈亮,其它滅
HAL_GPIO_WritePin(LED_R_GPIO_Port,LED_R_Pin,GPIO_PIN_SET);
HAL_GPIO_WritePin(LED_G_GPIO_Port,LED_G_Pin,GPIO_PIN_SET);
HAL_GPIO_WritePin(LED_B_GPIO_Port,LED_B_Pin,GPIO_PIN_RESET);
HAL_Delay(500);
- 觀察LED燈,間隔500ms依次閃爍紅、綠、藍
圖片!!
- 測試成功!!
7.4.4 編寫按鍵中斷測試代碼
- 在main.c 底部添加以下測試代碼
/****************************** 按鍵中斷測試代碼 *****************************/
//KEY1按下動作執行函數
void KEY1_Pressed(void)
{
user_main_debug("我按下了KEY_1\r\n");
}
//KEY2按下動作執行函數
void KEY2_Pressed(void)
{
user_main_debug("我按下了KEY_2\r\n");
}
//按鍵中斷處理函數
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
switch(GPIO_Pin)
{
case KEY_1_Pin:KEY1_Pressed();break;
case KEY_2_Pin:KEY2_Pressed();break;
default:break;
}
}
- 編譯運行,分別按下按鍵,觀察串口助手
- 測試成功!!
工程驗證成功!!
7.5 移植DHT11溫溼度傳感器驅動程序
7.5.1 利用TIM1實現us級延時(編寫DHT11驅動時會用)
-
首先去工程中設置定時器1
-
重新生成工程,來到tim.c,在/* USER CODE BEGIN 1 */中添加以下代碼
//使用定時器1來做us級延時函數,溫溼度傳感器用,量程0-6553us
void TIM1_Delay_us(uint16_t n_us)
{
__HAL_TIM_SetCounter(&htim1, 0);//htim1
/* 開啓定時器1計數 */
__HAL_TIM_ENABLE(&htim1);
while(__HAL_TIM_GetCounter(&htim1) < (10 * n_us));//計數頻率10MHz,10次即爲1us
/* Disable the Peripheral */
__HAL_TIM_DISABLE(&htim1);
}
- 在tim.h中添加函數定義
void TIM1_Delay_us(uint16_t n_us);
- 到此,自定義us級延時函數添加完畢,在main.c 主循環中添加以下測試代碼
// 自定義us級延時函數測試
static uint16_t tim1_test;
//延時1000個1ms
for(tim1_test = 0;tim1_test<1000;tim1_test++)
{
//延時1ms
TIM1_Delay_us(1000);
}
user_main_debug("我是us級延時函數測試代碼,1s打印一次!\n");
- 觀察串口打印,非常精確比自帶的HAL_Delay還準!!
7.5.2 添加DHT11驅動文件到工程中
- 來到工程目錄下,添加BSP文件夾
- 將事先準備好的DHT11驅動文件夾拷貝進去
- 裏面包含這兩個文件
- Keil工程添加工程文件
- 添加頭文件地址
7.5.3 修改驅動.c .h程序文件
- 修改文件 “hal_temp_hum.c"
- 修改文件 “hal_temp_hum.h"
- 在main.c 中引入頭文件"hal_temp_hum.h"
#include "hal_temp_hum.h"
- 在main.c KEY1_Pressed函數中添加以下測試代碼
//KEY1按下動作執行函數
void KEY1_Pressed(void)
{
user_main_debug("按下KEY_1\r\n");
uint8_t temperature;
uint8_t humidity;
uint8_t get_times;
// 獲取溫溼度信息並用串口打印,獲取十次,直到成功跳出
for(get_times=0;get_times<10;get_times++)
{
if(!dht11Read(&temperature, &humidity))//Read DHT11 Value
{
user_main_info("temperature=%d,humidity=%d \n",temperature,humidity);
break;
}
}
}
- 編譯,下載運行,按下KEY_1,觀察串口現象
- 成功!!
7.6 移植ESP8266 AT指令WIFI驅動模塊
7.6.1 連接硬件
7.6.2 添加ESP8266串口驅動文件到工程中
- 與DHT11一樣,將ESP8266驅動文件放到BSP目錄下
- 工程中加入文件
- 添加頭文件路徑
- 在main.c中引入頭文件
#include "esp8266_at.h"
7.6.3 驅動程序分析
- 直接看代碼~
7.6.4 測試ESP8266驅動程序
- 添加WIFI熱點宏定義
//此處根據自己的wifi作調整
#define WIFI_NAME "HappyOneDay"
#define WIFI_PASSWD "1234567890"
- 在KEY1_Pressed函數中添加測試代碼
//KEY1按下動作執行函數
void KEY1_Pressed(void)
{
user_main_debug("我按下了KEY_1\r\n");
/* ESP8266測試代碼 */
uint8_t status=0;
//初始化
if(ESP8266_Init())
{
user_main_info("ESP8266初始化成功!\r\n");
status++;
}
//連接熱點
if(status==1)
{
if(ESP8266_ConnectAP(WIFI_NAME,WIFI_PASSWD))
{
user_main_info("ESP8266連接熱點成功!\r\n");
status++;
}
}
}
- 編譯,燒錄運行,按下KEY_1 觀察現象
- 測試成功!!
7.7 移植MQTT驅動
7.7.1 添加驅動文件到工程中
- 將驅動賦值到BSP文件目錄下
- 將文件加入工程
- 添加頭文件路徑
7.7.2 使能USART1接收中斷,添加接收函數
- 打開中斷
- 優先級修改爲3
- 生成工程,在main.c中/* USER CODE BEGIN 2 */處添加以下代碼
//開啓USART1接收中斷
HAL_UART_Receive_IT(&huart1,usart1_rxone,1);
- 在在main.c中/* USER CODE BEGIN 4 */處添加以下代碼(爲了接收服務器的響應)
//USART1 ES8266驅動串口接收中斷處理函數
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if(huart->Instance == USART1) // 判斷是由哪個串口觸發的中斷
{
//將接收到的數據放入接收usart1接收數組
usart1_rxbuf[usart1_rxcounter] = usart1_rxone[0];
usart1_rxcounter++; //接收數量+1
//重新使能串口1接收中斷
HAL_UART_Receive_IT(&huart1,usart1_rxone,1);
}
}
7.7.3 在main.c中引入頭文件,測試代碼
- 引入頭文件
#include "esp8266_mqtt.h"
- 使用我一開始配置好的阿里雲IOT進行測試,怎麼配置稍後介紹
//此處是阿里雲服務器的登陸配置
#define MQTT_BROKERADDRESS "a1lAoazdH1w.iot-as-mqtt.cn-shanghai.aliyuncs.com"
#define MQTT_CLIENTID "00001|securemode=3,signmethod=hmacsha1|"
#define MQTT_USARNAME "BZL01&a1lAoazdH1w"
#define MQTT_PASSWD "51A5BB10306E976D6C980F73037F2D9496D2813A"
#define MQTT_PUBLISH_TOPIC "/sys/a1lAoazdH1w/BZL01/thing/event/property/post"
#define MQTT_SUBSCRIBE_TOPIC "/sys/a1lAoazdH1w/BZL01/thing/service/property/set"
- 添加error報錯函數
//進入錯誤模式等待手動重啓
void Enter_ErrorMode(uint8_t mode)
{
HAL_GPIO_WritePin(LED_G_GPIO_Port,LED_G_Pin,GPIO_PIN_SET);
while(1)
{
switch(mode){
case 0:user_main_error("ESP8266初始化失敗!\r\n");break;
case 1:user_main_error("ESP8266連接熱點失敗!\r\n");break;
case 2:user_main_error("ESP8266連接阿里雲服務器失敗!\r\n");break;
case 3:user_main_error("ESP8266阿里雲MQTT登陸失敗!\r\n");break;
case 4:user_main_error("ESP8266阿里雲MQTT訂閱主題失敗!\r\n");break;
default:user_main_info("Nothing\r\n");break;
}
user_main_info("請重啓開發板");
//HAL_GPIO_TogglePin(LED_R_GPIO_Port,LED_R_Pin);
HAL_Delay(200);
}
}
- 修改KEY1_Pressed函數
/****************************** 按鍵中斷測試代碼 *****************************/
//KEY1按下動作執行函數
void KEY1_Pressed(void)
{
user_main_debug("我按下了KEY_1\r\n");
/* DHT11溫溼度傳感器測試 */
// uint8_t temperature;
// uint8_t humidity;
// uint8_t get_times;
// // 獲取溫溼度信息並用串口打印,獲取十次,直到成功跳出
// for(get_times=0;get_times<10;get_times++)
// {
// if(!dht11Read(&temperature, &humidity))//Read DHT11 Value
// {
// user_main_info("temperature=%d,humidity=%d \n",temperature,humidity);
// break;
// }
// }
/* ESP8266&MQTT測試代碼 */
uint8_t status=0;
//初始化
if(ESP8266_Init())
{
user_main_info("ESP8266初始化成功!\r\n");
status++;
}
else Enter_ErrorMode(0);
//連接熱點
if(status==1)
{
if(ESP8266_ConnectAP(WIFI_NAME,WIFI_PASSWD))
{
user_main_info("ESP8266連接熱點成功!\r\n");
status++;
}
else Enter_ErrorMode(1);
}
//連接阿里雲IOT服務器
if(status==2)
{
if(ESP8266_ConnectServer("TCP",MQTT_BROKERADDRESS,1883)!=0)
{
user_main_info("ESP8266連接阿里雲服務器成功!\r\n");
status++;
}
else Enter_ErrorMode(2);
}
//登陸MQTT
if(status==3)
{
if(MQTT_Connect(MQTT_CLIENTID, MQTT_USARNAME, MQTT_PASSWD) != 0)
{
user_main_info("ESP8266阿里雲MQTT登陸成功!\r\n");
status++;
}
else Enter_ErrorMode(3);
}
//訂閱主題
if(status==4)
{
if(MQTT_SubscribeTopic(MQTT_SUBSCRIBE_TOPIC,0,1) != 0)
{
user_main_info("ESP8266阿里雲MQTT訂閱主題成功!\r\n");
}
else Enter_ErrorMode(4);
}
}
- 編譯,下載運行,按下按鍵,觀察串口打印
- 測試成功!!!
7.8 MQTT協議簡單介紹
MQTT中文網:http://mqtt.p2hp.com/
7.9 阿里雲物聯網平臺介紹
官網地址:https://help.aliyun.com/product/30520.html
7.9.1 創建產品
- 點擊創建產品
- 按以下格式創建
- 查看產品詳情
7.9.2 定義產品功能
- 編輯草稿
- 添加自定義功能
- 添加溫度屬性
- 添加溼度屬性
- 添加紅燈控制開關
- 添加綠燈控制開關
- 添加藍燈控制開關
- 發佈上線
7.9.3 添加具體設備
- 添加設備
- 查看設備屬性
- 查看設備證書**(關鍵)**
7.9.4 使用MQTT.fx模擬硬件進行連接
- 使用MQTT.fx接入物聯網平臺:
https://help.aliyun.com/document_detail/140507.html?spm=a2c4g.11174283.6.565.3a8b16686rXYnj
- MQTT.fx官網:
https://mqttfx.jensd.de/index.php/download?spm=a2c4g.11186623.2.20.17b67908o3uJ8y
此處對應着官方文檔進行講解
參數 | 說明 |
---|---|
Profile Name | 輸入您的自定義名稱。 |
Profile Type | 選擇爲MQTT Broker。 |
Broker Address | 連接域名。格式:${YourProductKey}.iot-as-mqtt.${region}.aliyuncs.com 。其中,${region}需替換爲您物聯網平臺服務所在地域的代碼。地域代碼,請參見地域和可用區。如:alxxxxxxxxx.iot-as-mqtt.cn-shanghai.aliyuncs.com 。 |
Broker Port | 設置爲1883。 |
Client ID | 填寫mqttClientId,用於MQTT的底層協議報文。格式固定:${clientId}|securemode=3,signmethod=hmacsha1| 。完整示例:12345|securemode=3,signmethod=hmacsha1| 。其中,${clientId}爲設備的ID信息。可取任意值,長度在64字符以內。建議使用設備的MAC地址或SN碼。securemode爲安全模式,TCP直連模式設置爲securemode=3 ,TLS直連爲securemode=2 。signmethod爲算法類型,支持hmacmd5和hmacsha1。說明 輸入Client ID信息後,請勿單擊Generate。 |
地域名稱 | 所在城市 | Region ID | 可用區數量 |
---|---|---|---|
華北 1 | 青島 | cn-qingdao | 2 |
華北 2 | 北京 | cn-beijing | 8 |
華北 3 | 張家口 | cn-zhangjiakou | 2 |
華北 5 | 呼和浩特 | cn-huhehaote | 2 |
華東 1 | 杭州 | cn-hangzhou | 8 |
華東 2 | 上海 | cn-shanghai | 6 |
華南 1 | 深圳 | cn-shenzhen | 5 |
華南 2 | 河源 | cn-heyuan | 2 |
西南 1 | 成都 | cn-chengdu | 2 |
- 示例:
Broker Address: a1kvAFB5siA.iot-as-mqtt.cn-shanghai.aliyuncs.com
Broker Port: 1883
Client ID: 00001|securemode=3,signmethod=hmacsha1|
參數 | 說明 |
---|---|
User Name | 由設備名DeviceName、符號(&)和產品ProductKey組成。固定格式:${YourDeviceName}&${YourPrductKey} 。完整示例如:device&alxxxxxxxxx 。 |
Password | 密碼由參數值拼接加密而成。說明 如果您使用的MQTT.fx版本,在粘貼Password後不顯示具體的字符串,只要光標已從輸入框的前部移至了後部,則表示粘貼成功,請勿重複粘貼。您可以使用物聯網平臺提供的生成工具自動生成Password,也可以手動生成Password。單擊下載Password生成小工具。解壓縮下載包後,雙擊sign文件,即可使用。使用Password生成小工具的輸入參數:productKey:設備所屬產品Key。可在控制檯設備詳情頁查看。deviceName:設備名稱。可在控制檯設備詳情頁查看。deviceSecret:設備密鑰。可在控制檯設備詳情頁查看。timestamp:(可選)時間戳。clientId:設備的ID信息,與Client ID中${clientId}一致。method:選擇簽名算法類型,與Client ID中signmethod確定的加密方法一致。手動生成方法如下:拼接參數。提交給服務器的clientId、deviceName、productKey和timestamp(timestamp爲非必選參數)參數及參數值依次拼接。本例中,clientId值爲12345,deviceName值爲device,productKey值爲alxxxxxxxxx,拼接結果爲:clientId12345deviceNamedeviceproductKeyalxxxxxxxxx 加密。通過Client ID中確定的加密方法,使用設備deviceSecret,將拼接結果加密。假設設備的deviceSecret值爲abc123,加密計算格式爲hmacsha1(abc123,clientId12345deviceNamedeviceproductKeyalxxxxxxxxx) |
- password生成小工具(一個離線網頁)
- 示例:
User Name: TESTDEVICE01&a1kvAFB5siA
Password: E18BFBEE7686EC3FBC5EAB10BEB101FD0913CF39
- 填入MQTT.fx,進行測試
- 點擊connect
- 連接成功!!!
7.9.5 訂閱消息調試
- 來到設備屬性,物模型通信Topic
- MQTT.fx訂閱
/sys/a1kvAFB5siA/TESTDEVICE01/thing/service/property/set
- 訂閱消息成功,嘗試平臺下發消息
- 來到這裏進行下發
- 設備成功收到消息
7.9.6 使用MQTT.fx模擬設備進行消息推送
-
推送地址:
/sys/a1kvAFB5siA/TESTDEVICE01/thing/event/property/post
-
推送以下json消息
- 溫度:20.0攝氏度
- 溼度:60%
- 紅燈:開
- 綠燈:開
- 藍燈:關
{
"method":"thing.service.property.set",
"id":"354062502",
"params":{
"temperature":20.0,
"humidity":60.0,
"switch_led_r":1,
"switch_led_g":1,
"switch_led_b":0
},
"version":"1.0.0"
}
- 來看平臺收到的消息日誌
- 可視化查看設備上報的數據
- 至此,MQTT.fx調試完成!!!下一步,使用單片機進行該項工作
7.10 STM32 使用MQTT接入阿里雲平臺過程詳解
直接看代碼
7.11 STM32傳感器數據上傳、平臺數據下發效果展示
真機演示