做批發(fā)的網(wǎng)站seo投放
文章目錄
- 引言
- 一、STM32連接阿里云物聯(lián)網(wǎng)平臺(tái)思路
- 二、ESP8266燒錄固件
- 三、使用AT指令連接阿里云物聯(lián)網(wǎng)平臺(tái)
- 四、STM32環(huán)形串口緩沖區(qū)驅(qū)動(dòng)程序
- 五、STM32連接阿里云驅(qū)動(dòng)程序
引言
連續(xù)寫了兩篇關(guān)于阿里云連接的文章,都是使用Arduino ESP8266 & Arduino ESP32的方式:
1、Arduino ESP8266&ESP32使用AliyunIoTSDK.h連接阿里云物聯(lián)網(wǎng)平臺(tái)
2、使用ArduinoMqttClient庫(kù)連接阿里云,并實(shí)現(xiàn)發(fā)送接收數(shù)據(jù)(ESP8266)
但是,使用Arduino的方式處理阿里云的數(shù)據(jù)在簡(jiǎn)單的電子小制作、畢業(yè)設(shè)計(jì)完全可以勝任,但是Arduino的核心庫(kù)都是對(duì)底層進(jìn)行二次封裝,運(yùn)行效率難免會(huì)低一些。ESP8266與ESP32作為主控的MCU,功能有限,在這種情況下,就會(huì)想到使用STM32搭載一個(gè)可以聯(lián)網(wǎng)的芯片來做數(shù)據(jù)物聯(lián)網(wǎng)的開發(fā)了。
我在企業(yè)開發(fā)項(xiàng)目中,接觸過新能源汽車充電樁,核心是使用STM32F103VET6進(jìn)行主控,它通過搭載一個(gè)4G模塊來對(duì)云端數(shù)據(jù)進(jìn)行處理。設(shè)備上線時(shí),后臺(tái)有上線的提示;管理員可用戶都可以查看到充電樁的實(shí)時(shí)狀態(tài),如下圖所示,管理員坐在辦公室便可以查看到有兩個(gè)慢充樁處于空閑狀態(tài),有一個(gè)充電樁已連接車輛,但是并沒有開始充電,我們就可以猜測(cè)出用戶可能剛剛到達(dá),已將插槍連接車輛,正在進(jìn)行掃碼的操作。
平臺(tái)更會(huì)報(bào)一些簡(jiǎn)單的故障,如急停故障,充電樁安裝的環(huán)境多種多樣,有時(shí)會(huì)有小朋友誤觸到急停故障導(dǎo)致充電樁報(bào)故障,不能充電,這時(shí)用戶打電話給運(yùn)維的客服,便可以在運(yùn)維客服的提示下把急停按鍵復(fù)原,充電樁便可以正常使用。
通過物聯(lián)網(wǎng)技術(shù)來對(duì)新能源汽車充電樁進(jìn)行控制大大的減輕的運(yùn)營(yíng)的成本,在其他行業(yè)如加油站無人值守的自動(dòng)洗車機(jī)、學(xué)校教學(xué)樓的自動(dòng)販賣機(jī)等等,都是物聯(lián)網(wǎng)控制的一個(gè)范例,如今物聯(lián)網(wǎng)控制技術(shù)更是影響著我們生活的方方面面。
文章使用基礎(chǔ)型STM32F1作為主控芯片,搭載ESP8266模塊,使用環(huán)形串口緩沖區(qū)算法連接阿里云平臺(tái),實(shí)現(xiàn)數(shù)據(jù)的訂閱和發(fā)布。文章有一點(diǎn)點(diǎn)的深度,我盡量把每一個(gè)細(xì)節(jié)每一個(gè)步都驟掰開來,嚼碎去詳細(xì)說明。
有很多例程在數(shù)據(jù)收發(fā)這一塊都使用數(shù)組進(jìn)行數(shù)據(jù)的接收,對(duì)付一些簡(jiǎn)單的物聯(lián)網(wǎng)開發(fā)完全是沒有問題的,而環(huán)形串口緩沖區(qū)算法是在大規(guī)模數(shù)據(jù)接收時(shí)使用的,在這么簡(jiǎn)單的物聯(lián)網(wǎng)數(shù)據(jù)收發(fā)中使用難免會(huì)影響效率。
是的,在這么簡(jiǎn)單的物聯(lián)網(wǎng)數(shù)據(jù)的收發(fā)我還使用環(huán)形串口緩沖區(qū),影響一定的運(yùn)行的效率是一定。但是,長(zhǎng)期做開發(fā)的一定要留有有方便后期升級(jí)維護(hù)的一個(gè)思維,一些升級(jí)接口需要多預(yù)留一些,產(chǎn)品都不是一次做出來的,是一代一代逐漸完善,逐漸更新?lián)Q代的。
一、STM32連接阿里云物聯(lián)網(wǎng)平臺(tái)思路
連接思路其實(shí)很簡(jiǎn)單,只要兩步就好了:
1、燒錄MQTT的固件
2、STM32使能串口通過AT指令連接阿里云物聯(lián)網(wǎng)平臺(tái)
我使用的是ESP8266作為聯(lián)網(wǎng)設(shè)備,連接阿里云物聯(lián)網(wǎng)平臺(tái),之前我使用Arduino的方式連接阿里云平臺(tái)也是同樣的原理,但是Arduino將一些底層的函數(shù)進(jìn)行了封裝,我們只需要學(xué)會(huì)使用Arduino提供的一些庫(kù)便可以連接阿里云物聯(lián)網(wǎng)平臺(tái)進(jìn)行數(shù)據(jù)交換。
而STM32則沒有相關(guān)的庫(kù),我們需要燒錄好MQTT的固件,通過電腦串口助手先連接成功阿里云物聯(lián)網(wǎng)平臺(tái),測(cè)試好訂閱和發(fā)布的功能后,連接我們STM32串口,進(jìn)行簡(jiǎn)單的串口數(shù)據(jù)收發(fā)。
二、ESP8266燒錄固件
連接物聯(lián)網(wǎng)云平臺(tái)之前先需要燒錄相關(guān)固件,固件地址如下:
安信可固件匯總
我選擇第4個(gè)固件,固件的下載地址附有官方的使用說明。
因?yàn)榧夹g(shù)更新迭代的速度非???#xff0c;可能我的這一篇文章發(fā)布幾個(gè)月或半年后固件便開始更新,但都是換湯不換藥,掌握了連接的原理,我們也可以自行查看官方的文檔來進(jìn)行連接,此篇文章可以作為非常重要的參考使用。
下載完成后便可以燒錄固件,燒錄固件的軟件可以通過官網(wǎng)下載,我這里也提供一個(gè)網(wǎng)盤下載連接:
鏈接:https://pan.baidu.com/s/1H96ZbeG_ogslrJUz2Y162w
提取碼:wslw
軟件打開后,安裝提示很容易就可以將固件燒錄進(jìn)去,下圖是我燒錄成功的截圖界面:
三、使用AT指令連接阿里云物聯(lián)網(wǎng)平臺(tái)
固件燒錄成功之后,首先需要使用電腦的串口助手軟件,通過簡(jiǎn)單的AT指令連接WiFi,連接阿里云,然后完成訂閱和發(fā)布數(shù)據(jù),確認(rèn)好AT指令代碼沒有問題后,我們才把相關(guān)的AT指令封裝起來,寫進(jìn)程序中,連接的AT指令只需要熟悉常用的幾個(gè)即可。
指令 | 功能 |
---|---|
AT | 測(cè)試 |
AT+RST | 重啟 |
AT+CWMODE=1 | 設(shè)置為無線終端模式,可以連接WiFi |
AT+CWJAP=“wifi”,“passwd” | 連接熱點(diǎn) |
AT+CWSTATE? | 查詢WiFi連接講解狀態(tài) |
AT+MQTTUSERCFG=0,1,“clientId”,“username”,“password”,0,0,“” | 設(shè)置MQTT連接參數(shù) |
AT+MQTTCONN=0,“Broker Address”,1883,1 | 連接阿里云 |
AT+MQTTCONN? | 查詢MQTT連接狀態(tài) |
AT+MQTTSUB=0,“topic”,0 | 訂閱主題 |
AT+MQTTPUB=0,“topic”,“msg” | 發(fā)布主題 |
AT指令按照如下通過串口助手發(fā)送到ESP8266即可完成阿里云的連接
(1)判斷ESP8266是否存在
使用指令:AT\r\n
串口回復(fù):OK
備注:使用串口助手和STM32發(fā)送指令的時(shí)候,結(jié)尾加上\r\n,表示指令結(jié)束
(2)設(shè)置ESP8266為接入點(diǎn)模式
使用指令:AT+CWMODE=1\r\n
串口回復(fù):OK
(3)查詢WiFi是否連接成功
使用指令:AT+CWSTATE?
串口回復(fù):
+CWSTATE:2"CMCC-H3qz" //備注:CMCC-H3qz是我的WiFi名OK
(4)如果沒有連接WiF,則連接WiFi
使用指令:AT+CWJAP=“CMCC-H3qz”,“7xf47uxf”\r\n
備注:CMCC-H3qz是我的WiFi名,7xf47uxf是我的WiFi密碼
串口回復(fù):
AT+CWJAP="CMCC-H3qz""7xf47uxf"
WIFI DISCONNECT
WIFI CONNECTED
WIFI GOT IPOK
(5)設(shè)置MQTT參數(shù)
使用指令:
AT+MQTTUSERCFG=0,1,“clientId”,“username”,“password”,0,0,“”\r\n
示例:
AT+MQTTUSERCFG=0,1,"k0efkfcSwlt.Device_Demo|securemode=2\,signmethod=hmacsha256\,timestamp=1705149361131|","Device_Demo&k0efkfcSwlt","ea343ac1d6a4eec7b057bcab39a2ffdf88abc8b810ec0dc7ca3de91a56292ff9",0,0,""\r\n
備注:
1、指令中,遇到“,”字符時(shí),使用“\”隔開,避免報(bào)錯(cuò)。
2、clientId,username,password這些數(shù)據(jù)從阿里云物聯(lián)網(wǎng)平臺(tái)獲取
串口回復(fù):
AT+MQTTUSERCFG=01"k0efkfcSwlt.Device_Demo|securemode=2\signmethod=hmacsha256\timestamp=1705149361131|""Device_Demo&k0efkfcSwlt""ea343ac1d6a4eec7b057bcab39a2ffdf88abc8b810ec0dc7ca3de91a56292ff9"00""OK
(6)連接阿里云
使用指令:AT+MQTTCONN=0,“Broker Address”,1883,1\r\n
示例:
AT+MQTTCONN=0,"k0efkfcSwlt.iot-as-mqtt.cn-shanghai.aliyuncs.com",1883,1\r\n
備注:
Broker Address為MQTT域名,阿里云物聯(lián)網(wǎng)平臺(tái)開發(fā)者文檔有詳細(xì)的解釋,我在以前的文章也對(duì)這方面進(jìn)行了詳細(xì)的說明,大家可以嘗試一下使用MQTT.fx連接阿里云平臺(tái)就能明白MQTT域名的作用了。
附上這部分云文檔的地址:
使用MQTT.fx接入物聯(lián)網(wǎng)平臺(tái)
串口回復(fù):
AT+MQTTCONN=0"k0efkfcSwlt.iot-as-mqtt.cn-shanghai.aliyuncs.com"18831
+MQTTCONNECTED:01"k0efkfcSwlt.iot-as-mqtt.cn-shanghai.aliyuncs.com""1883"""1OK
這時(shí),我們登陸阿里云物聯(lián)網(wǎng)平臺(tái),可以看到設(shè)備已成功連接阿里云物聯(lián)網(wǎng)平臺(tái)。
(7)發(fā)布數(shù)據(jù)
使用指令:AT+MQTTPUB=0,“topic”,“msg”\r\n
示例:
AT+MQTTPUB=0,"/sys/k0efkfcSwlt/Device_Demo/thing/event/property/post","{\"params\": {\"temperature\": 10.5}\,\"version\": \"1.0\"}",0,0\r\n
備注:
1、topic從阿里云平臺(tái)獲取
2、msg有一定的格式,在阿里云平臺(tái)的開發(fā)者文檔里也有詳細(xì)的說明,附上文檔連接:
阿里云物聯(lián)網(wǎng)平臺(tái)設(shè)備上報(bào)屬性
示例中上報(bào)的屬性為:溫度10.5。
3、使用這個(gè)指令遇到" “ ”的時(shí)候,學(xué)會(huì)使用專業(yè)字符“\”。
串口回復(fù):
AT+MQTTPUB=0"/sys/k0efkfcSwlt/Device_Demo/thing/event/property/post""{\"params\": {\"temperature\": 10.5}\\"version\": \"1.0\"}"00OK
我們查看阿里云物聯(lián)網(wǎng)平臺(tái)物模型數(shù)據(jù),可以看到阿里云已成功接收數(shù)據(jù)。
(8)訂閱數(shù)據(jù)
使用指令:AT+MQTTSUB=0,“topic”,0\r\n
示例:AT+MQTTSUB=0,"/sys/k0efkfcSwlt/Device_Demo/thing/service/property/set",0
備注:topic從阿里云物聯(lián)網(wǎng)平臺(tái)獲取
串口回復(fù):
AT+MQTTSUB=0"/sys/k0efkfcSwlt/Device_Demo/thing/service/property/set"0OK
這時(shí),我們通過在線調(diào)試測(cè)試訂閱數(shù)據(jù)是否成功。
我們?cè)诎⒗镌破脚_(tái)自定義了一盞LED燈,通過在線調(diào)試的方式,發(fā)送開啟和關(guān)閉的命令,如果訂閱沒有問題的話,可以看到如下串口回復(fù):
+MQTTSUBRECV:0"/sys/k0efkfcSwlt/Device_Demo/thing/service/property/set"100{"method":"thing.service.property.set""id":"2001671834""params":{"LEDSwitch":0}"version":"1.0.0"}
+MQTTSUBRECV:0"/sys/k0efkfcSwlt/Device_Demo/thing/service/property/set"100{"method":"thing.service.property.set""id":"2046606498""params":{"LEDSwitch":1}"version":"1.0.0"}
可以看到j(luò)son數(shù)據(jù)里L(fēng)EDSwitch的變換,這時(shí),我們就可以在單片機(jī)上,處理這條指令來實(shí)現(xiàn)LED的亮滅,實(shí)現(xiàn)阿里云物聯(lián)網(wǎng)平臺(tái)控制led。
如果順利到這一步,那么恭喜你,現(xiàn)在距離成功僅剩一半的距離了,接下來只需將AT指令封裝到STM32的代碼中,進(jìn)行簡(jiǎn)單的STM32串口操作,就可以實(shí)現(xiàn)使用STM32連接阿里云的操作了。
四、STM32環(huán)形串口緩沖區(qū)驅(qū)動(dòng)程序
環(huán)形串口緩沖區(qū)的原理可以查看這篇文章:環(huán)形串口緩沖區(qū)
明白了環(huán)形串口緩沖區(qū)的原理,在STM32開發(fā)中,先寫好串口的驅(qū)動(dòng)程序,然后再編寫環(huán)形串口緩沖區(qū)程序,下面是驅(qū)動(dòng)程序,我都有注釋,學(xué)過STM32的都能看得懂。
usart.c
/********************************************************************************* @file usart.c* @author 小途* @version V1.0* @date 2022-2-21* @brief STM32F1的串口初始化設(shè)置******************************************************************************* @attention* 串口2配置環(huán)形串口緩沖區(qū)********************************************************************************/#include "usart.h"#define UART_RX_MAX 255 //數(shù)組最大值//環(huán)形緩沖區(qū)結(jié)構(gòu)體
typedef struct
{uint8_t head_count; //頭計(jì)數(shù)uint8_t tail_count; //尾計(jì)數(shù)uint8_t buf[UART_RX_MAX]; //緩沖區(qū)數(shù)組
}UART_S;UART_S uart;//定義串口緩沖區(qū)結(jié)構(gòu)體變量//串口環(huán)形緩沖區(qū)初始化
void uart_Buffer_Init(void)
{int i;uart.head_count = 0;uart.tail_count = 0;for(i=0; i<UART_RX_MAX; i++){uart.buf[i] = 0x00;}
}//往緩沖器里放數(shù)據(jù)
void uart_buf_put(uint8_t ch)
{uart.buf[uart.head_count++] = ch;if(uart.head_count == UART_RX_MAX){uart.head_count = 0;}
}//判斷緩沖區(qū)是否有數(shù)據(jù)
int fifo_is_empty(void)
{if(uart.head_count != uart.tail_count)//判斷如果頭不等于尾{return 0;//有數(shù)據(jù)}else return 1;//無數(shù)據(jù)
}//從緩沖區(qū)取數(shù)據(jù)
int uart_buf_get(uint8_t *ch)
{if(fifo_is_empty() == 0)//如果有數(shù)據(jù){*ch = uart.buf[uart.tail_count++];//取數(shù)據(jù)if(uart.tail_count == UART_RX_MAX){uart.tail_count = 0;} return 1;//返回成功}else return 0;//返回失敗
}//讀數(shù)據(jù),將數(shù)據(jù)通過串口發(fā)送出去
void debug_read(void)
{uint8_t ch;/*如果有數(shù)據(jù)就一直取,直到取完為至*/while(!fifo_is_empty()){if(uart_buf_get(&ch)){putchar(ch);}}
}/*** @brief 配置嵌套向量中斷控制器NVIC* @param 無* @retval 無*/
void NVIC_Configuration(void)
{NVIC_InitTypeDef NVIC_InitStructure;/* 嵌套向量中斷控制器組選擇 */NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);/* 配置USART為中斷源 */NVIC_InitStructure.NVIC_IRQChannel = DEBUG_USART_IRQ;/* 搶斷優(yōu)先級(jí)*/NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;/* 子優(yōu)先級(jí) */NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;/* 使能中斷 */NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;/* 初始化配置NVIC */NVIC_Init(&NVIC_InitStructure);
}//對(duì)串口2配置嵌套向量中斷控制器NVIC
void NVIC_USART2_Configuration(void)
{NVIC_InitTypeDef NVIC_InitStructure;/* 嵌套向量中斷控制器組選擇 */NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);/* 配置USART為中斷源 */NVIC_InitStructure.NVIC_IRQChannel = DEBUG_USART2_IRQ;/* 搶斷優(yōu)先級(jí)*/NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;/* 子優(yōu)先級(jí) */NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;/* 使能中斷 */NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;/* 初始化配置NVIC */NVIC_Init(&NVIC_InitStructure);
}/*** @brief USART GPIO 配置,工作參數(shù)配置* @param 無* @retval 無*/
void USART_Config(void)
{GPIO_InitTypeDef GPIO_InitStructure;USART_InitTypeDef USART_InitStructure;// 打開串口GPIO的時(shí)鐘DEBUG_USART_GPIO_APBxClkCmd(DEBUG_USART_GPIO_CLK, ENABLE);// 打開串口外設(shè)的時(shí)鐘DEBUG_USART_APBxClkCmd(DEBUG_USART_CLK, ENABLE);// 將USART Tx的GPIO配置為推挽復(fù)用模式GPIO_InitStructure.GPIO_Pin = DEBUG_USART_TX_GPIO_PIN;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(DEBUG_USART_TX_GPIO_PORT, &GPIO_InitStructure);// 將USART Rx的GPIO配置為浮空輸入模式GPIO_InitStructure.GPIO_Pin = DEBUG_USART_RX_GPIO_PIN;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;GPIO_Init(DEBUG_USART_RX_GPIO_PORT, &GPIO_InitStructure);// 配置串口的工作參數(shù)// 配置波特率USART_InitStructure.USART_BaudRate = DEBUG_USART_BAUDRATE;// 配置 針數(shù)據(jù)字長(zhǎng)USART_InitStructure.USART_WordLength = USART_WordLength_8b;// 配置停止位USART_InitStructure.USART_StopBits = USART_StopBits_1;// 配置校驗(yàn)位USART_InitStructure.USART_Parity = USART_Parity_No ;// 配置硬件流控制USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;// 配置工作模式,收發(fā)一起USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;// 完成串口的初始化配置USART_Init(DEBUG_USARTx, &USART_InitStructure);// 串口中斷優(yōu)先級(jí)配置//NVIC_Configuration();// 使能串口接收中斷//USART_ITConfig(DEBUG_USARTx, USART_IT_RXNE, ENABLE); // 使能串口USART_Cmd(DEBUG_USARTx, ENABLE);
}/*** @brief USART2 GPIO 配置,工作參數(shù)配置,配置環(huán)形串口緩沖區(qū)* @param 無* @retval 無*/
void USART2_Config(void)
{//在串口2配置環(huán)形串口緩沖區(qū)//初始化環(huán)形串口緩沖區(qū)uart_Buffer_Init();GPIO_InitTypeDef GPIO_InitStructure;USART_InitTypeDef USART_InitStructure;// 打開串口GPIO的時(shí)鐘DEBUG_USART2_GPIO_APBxClkCmd(DEBUG_USART2_GPIO_CLK, ENABLE);// 打開串口外設(shè)的時(shí)鐘DEBUG_USART2_APBxClkCmd(DEBUG_USART2_CLK, ENABLE);// 將USART Tx的GPIO配置為推挽復(fù)用模式GPIO_InitStructure.GPIO_Pin = DEBUG_USART2_TX_GPIO_PIN;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(DEBUG_USART2_TX_GPIO_PORT, &GPIO_InitStructure);// 將USART Rx的GPIO配置為浮空輸入模式GPIO_InitStructure.GPIO_Pin = DEBUG_USART2_RX_GPIO_PIN;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;GPIO_Init(DEBUG_USART2_RX_GPIO_PORT, &GPIO_InitStructure);// 配置串口的工作參數(shù)// 配置波特率USART_InitStructure.USART_BaudRate = DEBUG_USART2_BAUDRATE;// 配置 針數(shù)據(jù)字長(zhǎng)USART_InitStructure.USART_WordLength = USART_WordLength_8b;// 配置停止位USART_InitStructure.USART_StopBits = USART_StopBits_1;// 配置校驗(yàn)位USART_InitStructure.USART_Parity = USART_Parity_No ;// 配置硬件流控制USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;// 配置工作模式,收發(fā)一起USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;// 完成串口的初始化配置USART_Init(DEBUG_USART2, &USART_InitStructure); //串口2中斷優(yōu)先級(jí)配置NVIC_USART2_Configuration();//使能串口2接收中斷USART_ITConfig(DEBUG_USART2, USART_IT_RXNE, ENABLE);// 使能串口//USART_Cmd(DEBUG_USART2, ENABLE);
}/***************** 發(fā)送一個(gè)字符 **********************/
void Usart_SendByte( USART_TypeDef * pUSARTx, uint8_t ch)
{/* 發(fā)送一個(gè)字節(jié)數(shù)據(jù)到USART */USART_SendData(pUSARTx,ch);/* 等待發(fā)送數(shù)據(jù)寄存器為空 */while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);
}/***************** 發(fā)送字符串 **********************/
void Usart_SendString( USART_TypeDef * pUSARTx, char *str)
{unsigned int k=0;do {Usart_SendByte( pUSARTx, *(str + k) );k++;} while(*(str + k)!='\0');/* 等待發(fā)送完成 */while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TC)==RESET);
}//發(fā)送整形數(shù)據(jù)字符串(發(fā)送長(zhǎng)字符串)
void Usart_SendIntString( USART_TypeDef * pUSARTx, int *str)
{int k=0;do {Usart_SendByte( pUSARTx, *(str + k) );k++;} while(*(str + k)!='\0');/* 等待發(fā)送完成 */while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TC)==RESET);
}//重定向c庫(kù)函數(shù)printf到串口,重定向后可使用printf函數(shù),默認(rèn)使用串口1
int fputc(int ch, FILE *f)
{/* 發(fā)送一個(gè)字節(jié)數(shù)據(jù)到串口 */USART_SendData(DEBUG_USART1, (uint8_t) ch);/* 等待發(fā)送完畢 */while (USART_GetFlagStatus(DEBUG_USART1, USART_FLAG_TXE) == RESET); return (ch);
}//重定向c庫(kù)函數(shù)scanf到串口,重寫向后可使用scanf、getchar等函數(shù),默認(rèn)使用串口1
int fgetc(FILE *f)
{/* 等待串口輸入數(shù)據(jù) */while (USART_GetFlagStatus(DEBUG_USART1, USART_FLAG_RXNE) == RESET);return (int)USART_ReceiveData(DEBUG_USART1);
}
usart.h
#ifndef __USART_H
#define __USART_H#include "stm32f10x.h"
#include <stdio.h>/** 串口宏定義,不同的串口掛載的總線不一樣,移植時(shí)需要修改這幾個(gè)宏*/// 串口1-USART1
#define DEBUG_USARTx USART1
#define DEBUG_USART_CLK RCC_APB2Periph_USART1
#define DEBUG_USART_APBxClkCmd RCC_APB2PeriphClockCmd
#define DEBUG_USART_BAUDRATE 115200// USART GPIO 引腳宏定義
#define DEBUG_USART_GPIO_CLK (RCC_APB2Periph_GPIOA)
#define DEBUG_USART_GPIO_APBxClkCmd RCC_APB2PeriphClockCmd#define DEBUG_USART_TX_GPIO_PORT GPIOA
#define DEBUG_USART_TX_GPIO_PIN GPIO_Pin_9
#define DEBUG_USART_RX_GPIO_PORT GPIOA
#define DEBUG_USART_RX_GPIO_PIN GPIO_Pin_10#define DEBUG_USART_IRQ USART1_IRQn
#define DEBUG_USART_IRQHandler USART1_IRQHandler//------------------------------------------------------------------------// 串口1-USART1
#define DEBUG_USART1 USART1
#define DEBUG_USART1_CLK RCC_APB2Periph_USART1
#define DEBUG_USART1_APBxClkCmd RCC_APB2PeriphClockCmd
#define DEBUG_USART1_BAUDRATE 115200// USART GPIO 引腳宏定義
#define DEBUG_USART1_GPIO_CLK (RCC_APB2Periph_GPIOA)
#define DEBUG_USART1_GPIO_APBxClkCmd RCC_APB2PeriphClockCmd#define DEBUG_USART1_TX_GPIO_PORT GPIOA
#define DEBUG_USART1_TX_GPIO_PIN GPIO_Pin_9
#define DEBUG_USART1_RX_GPIO_PORT GPIOA
#define DEBUG_USART1_RX_GPIO_PIN GPIO_Pin_10#define DEBUG_USART1_IRQ USART1_IRQn
#define DEBUG_USART1_IRQHandler USART1_IRQHandler//------------------------------------------------------------------------// 串口2-USART2
#define DEBUG_USART2 USART2
#define DEBUG_USART2_CLK RCC_APB1Periph_USART2
#define DEBUG_USART2_APBxClkCmd RCC_APB1PeriphClockCmd
#define DEBUG_USART2_BAUDRATE 115200// USART GPIO 引腳宏定義
#define DEBUG_USART2_GPIO_CLK (RCC_APB2Periph_GPIOA)
#define DEBUG_USART2_GPIO_APBxClkCmd RCC_APB2PeriphClockCmd#define DEBUG_USART2_TX_GPIO_PORT GPIOA
#define DEBUG_USART2_TX_GPIO_PIN GPIO_Pin_2
#define DEBUG_USART2_RX_GPIO_PORT GPIOA
#define DEBUG_USART2_RX_GPIO_PIN GPIO_Pin_3#define DEBUG_USART2_IRQ USART2_IRQn
#define DEBUG_USART2_IRQHandler USART2_IRQHandler//使能串口2
#define USART2_ON USART_Cmd(DEBUG_USART2, ENABLE);
//失能串口2
#define USART2_OFF USART_Cmd(DEBUG_USART2, DISABLE);//環(huán)形串口緩沖區(qū)相關(guān)功能函數(shù)
void uart_buf_put(uint8_t ch);
void debug_read(void);
int fifo_is_empty(void);
int uart_buf_get(uint8_t *ch);//STM32串口相關(guān)功能函數(shù)
void USART_Config(void);
void USART2_Config(void);
void Usart_SendByte( USART_TypeDef * pUSARTx, uint8_t ch);
void Usart_SendString( USART_TypeDef * pUSARTx, char *str);
void Usart_SendIntString( USART_TypeDef * pUSARTx, int *str);#endif
stm32f10x_it.c
//串口2中斷服務(wù)函數(shù)
//將串口2接收到的數(shù)據(jù)回傳到串口1
void USART2_IRQHandler(void)
{uint8_t ucTemp;if(USART_GetITStatus(DEBUG_USART2,USART_IT_RXNE)!=RESET){ ucTemp = USART_ReceiveData(DEBUG_USART2);uart_buf_put(ucTemp);//往環(huán)形串口緩沖區(qū)存數(shù)據(jù)}
}
五、STM32連接阿里云驅(qū)動(dòng)程序
STM32連接阿里云程序邏輯與串口助手的一致,之前使用串口助手與我的電腦進(jìn)行數(shù)據(jù)交換,現(xiàn)在將STM32當(dāng)成一個(gè)小型的“電腦”,使用STM32來對(duì)ESP8266的數(shù)據(jù)進(jìn)行處理,達(dá)到物聯(lián)網(wǎng)控制的目的。
我使用的是STM32霸道的開發(fā)板做的測(cè)試?yán)?#xff0c; 使用杜邦線連接ESP8266模塊,以下的廠程序供大家參考,如有問題望指摘。
esp8266.c
/********************************************************************************* @file esp8266.c* @author fire* @version V1.0* @date 2024-1-16* @brief esp8266連接阿里云物聯(lián)網(wǎng)平臺(tái)******************************************************************************
**/#include "./ESP8266/esp8266.h"//測(cè)試esp8266是否存在
//向串口2發(fā)送“AT”,esp8266收到后會(huì)回復(fù)一個(gè)“Ok”
//STM32會(huì)將串口2收到的數(shù)據(jù)轉(zhuǎn)發(fā)到串口1,用于監(jiān)視串口2狀態(tài)
//函數(shù)需要提前初始化串口1,串口2,串口2中斷接收
//成功讀取到esp8266返回1,否則返回0
char esp8266_text(void)
{uint8_t ch;uint8_t str[20], i=0;char* pch;//發(fā)送測(cè)試命令Usart_SendString(DEBUG_USART2, "AT\r\n");//延時(shí)一段時(shí)間等待接收Delay_ms(50);//向串口環(huán)形緩沖區(qū)取數(shù)據(jù)while(!fifo_is_empty()){if(uart_buf_get(&ch)){str[i] = ch;i++;}}//STM32將連接在串口2上ESP8266發(fā)送過來的數(shù)據(jù)轉(zhuǎn)發(fā)到串口1
// Usart_SendString(DEBUG_USART1, "收到ESP8266傳來的數(shù)據(jù):\r\n");
// Usart_SendString(DEBUG_USART1, (char *)str);
// Usart_SendString(DEBUG_USART1, "\r\n");pch = strstr((char *)str, "OK");if(pch != NULL){Usart_SendString(DEBUG_USART1, "esp8266真實(shí)存在\r\n");return 1;}elsereturn 0;
}//esp8266重啟
void esp8266_restart(void)
{uint8_t ch;uint8_t str[255], i=0;//發(fā)送命令Usart_SendString(DEBUG_USART2, "AT+RST\r\n");//延時(shí)一段時(shí)間等待接收Delay_ms(50);//向串口環(huán)形緩沖區(qū)取數(shù)據(jù)while(!fifo_is_empty()){if(uart_buf_get(&ch)){str[i] = ch;i++;}}//STM32將連接在串口2上ESP8266發(fā)送過來的數(shù)據(jù)轉(zhuǎn)發(fā)到串口1Usart_SendString(DEBUG_USART1, "收到ESP8266傳來的數(shù)據(jù):\r\n");Usart_SendString(DEBUG_USART1, (char *)str);Usart_SendString(DEBUG_USART1, "\r\n");
}//將esp8266設(shè)置為接入點(diǎn)模式
//設(shè)置成功返回1,失敗返回0
char esp8266_setStation(void)
{uint8_t ch;uint8_t str[20], i=0; char* pch;//發(fā)送命令Usart_SendString(DEBUG_USART2, "AT+CWMODE=1\r\n");//延時(shí)一段時(shí)間等待接收Delay_ms(50);//向串口環(huán)形緩沖區(qū)取數(shù)據(jù)while(!fifo_is_empty()){if(uart_buf_get(&ch)){str[i] = ch;i++;}}//STM32將連接在串口2上ESP8266發(fā)送過來的數(shù)據(jù)轉(zhuǎn)發(fā)到串口1
// Usart_SendString(DEBUG_USART1, "收到ESP8266傳來的數(shù)據(jù):\r\n");
// Usart_SendString(DEBUG_USART1, (char *)str);
// Usart_SendString(DEBUG_USART1, "\r\n");pch = strstr((char *)str, "OK");if(pch != NULL)return 1;elsereturn 0;
}//查詢WiFi連接信息
//如果成功連接WiFi返回1,否則返回0
char esp8266_inquireWiFi_Data(void)
{uint8_t ch;uint8_t str[255], i=0;char* pch;//發(fā)送命令Usart_SendString(DEBUG_USART2, "AT+CWSTATE?\r\n");//延時(shí)一段時(shí)間等待接收Delay_ms(50);//向串口環(huán)形緩沖區(qū)取數(shù)據(jù)while(!fifo_is_empty()){if(uart_buf_get(&ch)){str[i] = ch;i++;}}//STM32將連接在串口2上ESP8266發(fā)送過來的數(shù)據(jù)轉(zhuǎn)發(fā)到串口1
// Usart_SendString(DEBUG_USART1, "收到ESP8266傳來的數(shù)據(jù):\r\n");
// Usart_SendString(DEBUG_USART1, (char *)str);
// Usart_SendString(DEBUG_USART1, "\r\n");pch = strchr((char *)str, '2');if(pch != NULL)return 1;elsereturn 0;
}//連接WiFi
void esp8266_connectWiFi(void)
{uint8_t ch, i=0;char str[255];//將要發(fā)送的數(shù)據(jù)拼接起來char wifi_data[255] = "AT+CWJAP=\"";strcat(wifi_data, WiFi_Name);strcat(wifi_data, "\",\"");strcat(wifi_data, WiFi_Password);strcat(wifi_data, "\"\r\n");//發(fā)送命令Usart_SendString(DEBUG_USART2, wifi_data);//延時(shí)一段時(shí)間等待接收Delay_ms(50);//向串口環(huán)形緩沖區(qū)取數(shù)據(jù)while(!fifo_is_empty()){if(uart_buf_get(&ch)){str[i] = ch;i++;}}//STM32將連接在串口2上ESP8266發(fā)送過來的數(shù)據(jù)轉(zhuǎn)發(fā)到串口1Usart_SendString(DEBUG_USART1, "收到ESP8266傳來的數(shù)據(jù):\r\n");Usart_SendString(DEBUG_USART1, str);Usart_SendString(DEBUG_USART1, "\r\n");
}//設(shè)置MQTT連接參數(shù)
//設(shè)置成功返回1,否則返回0
char esp8266_setMQTTData(void)
{uint8_t ch;uint8_t str[255], i=0;char* pch;//發(fā)送命令Usart_SendString(DEBUG_USART2, "AT+MQTTUSERCFG=0,1,\"");Usart_SendString(DEBUG_USART2, Aliyun_ClientId);Usart_SendString(DEBUG_USART2, "\",\"");Usart_SendString(DEBUG_USART2, Aliyun_Username);Usart_SendString(DEBUG_USART2, "\",\"");Usart_SendString(DEBUG_USART2, Aliyun_Password);Usart_SendString(DEBUG_USART2, "\",0,0,\"\"\r\n");//延時(shí)一段時(shí)間等待接收Delay_ms(50);//向串口環(huán)形緩沖區(qū)取數(shù)據(jù)while(!fifo_is_empty()){if(uart_buf_get(&ch)){str[i] = ch;i++;}}// Usart_SendString(DEBUG_USART1, "收到ESP8266傳來的數(shù)據(jù):\r\n");
// Usart_SendString(DEBUG_USART1, (char *)str);
// Usart_SendString(DEBUG_USART1, "\r\n");pch = strstr((char *)str, "OK");if(pch != NULL)return 1;elsereturn 0;
}//連接阿里云
//連接成功回復(fù)1,失敗回復(fù)0
char connect_Aliyun(void)
{uint8_t ch;uint8_t str[255], i=0;char* pch;//檢查esp8266是否存在if(esp8266_text() != 1){printf("連接錯(cuò)誤,代碼1:請(qǐng)檢測(cè)esp8266與芯片連接線路!\r\n");return 0;}//設(shè)置esp8266為接入點(diǎn)模式if(esp8266_setStation() != 1){printf("連接錯(cuò)誤,代碼2:esp8266 接入點(diǎn)模式設(shè)置失敗,請(qǐng)檢查!\r\n");return 0;}else{printf("接入點(diǎn)模式設(shè)置成功!\r\n");}//連接WiFi網(wǎng)絡(luò)//連接之前先檢查WiFi網(wǎng)絡(luò)是否連接//如果以連接則跳過此步驟if(esp8266_inquireWiFi_Data() != 1){//連接WiFiesp8266_connectWiFi();printf("正在連接WiFi");//5(s)*15=60(s)=1(min)//等待一段時(shí)間,待WiFi連接成功i=15;while(1){Delay_ms(5000);printf(".");//如果WiFi成功退出循環(huán)if(esp8266_inquireWiFi_Data() == 1){printf("WiFi連接成功!\r\n");break;}//超時(shí)連接退出程序i--;if(i == 0){printf("\r\n錯(cuò)誤代碼3:WiFi連接超時(shí),已退出!\r\n");return 0;}}}else{printf("WiFi連接成功!\r\n");}//設(shè)置MQTT連接參數(shù)if(esp8266_setMQTTData() != 1){printf("連接錯(cuò)誤,代碼4:esp8266 接入點(diǎn)模式設(shè)置失敗,請(qǐng)檢查!\r\n");return 0;}else{printf("MQTT連接參數(shù)設(shè)置成功!\r\n");}//-------------------------------------------------------------------------------------------for(i=0; i<255; i++){str[i] = '\0';}i=0;//下面開始連接阿里云//發(fā)送命令Usart_SendString(DEBUG_USART2, "AT+MQTTCONN=0,\"");Usart_SendString(DEBUG_USART2, BrokerAddress);Usart_SendString(DEBUG_USART2, "\",1883,1\r\n");Delay_ms(50);//向串口環(huán)形緩沖區(qū)取數(shù)據(jù)while(!fifo_is_empty()){if(uart_buf_get(&ch)){str[i] = ch;i++;}}// Usart_SendString(DEBUG_USART1, "收到ESP8266傳來的數(shù)據(jù):\r\n");
// Usart_SendString(DEBUG_USART1, (char *)str);
// Usart_SendString(DEBUG_USART1, "\r\n");// for(i=0; i<255; i++)
// {
// str[i] = '\0';
// }
//
// i=0;//查詢阿里云連接狀態(tài)Usart_SendString(DEBUG_USART2, "AT+MQTTCONN?");Delay_ms(50);//向串口環(huán)形緩沖區(qū)取數(shù)據(jù)while(!fifo_is_empty()){if(uart_buf_get(&ch)){str[i] = ch;i++;}}Usart_SendString(DEBUG_USART1, "阿里云的連接狀態(tài)為:\r\n");Usart_SendString(DEBUG_USART1, (char *)str);Usart_SendString(DEBUG_USART1, "\r\n");pch = strstr((char *)str, BrokerAddress);if(pch != NULL){printf("阿里云物聯(lián)網(wǎng)平臺(tái)連接成功!\r\n");return 1;}else{printf("錯(cuò)誤代碼6:阿里云物聯(lián)網(wǎng)平臺(tái)連接失敗,請(qǐng)檢查!\r\n");return 0;}
}//向阿里云上傳溫度數(shù)據(jù)
void AliyunSetData(float temperature)
{uint8_t ch;uint8_t str[255], i=0;//發(fā)送數(shù)據(jù)Usart_SendString(DEBUG_USART2, "AT+MQTTPUB=0,\"");Usart_SendString(DEBUG_USART2, IssueTOPIC);Usart_SendString(DEBUG_USART2, "\",\"{\\\"params\\\": {\\\"temperature\\\":");//將浮點(diǎn)型數(shù)據(jù)轉(zhuǎn)換成字符串并發(fā)送出去char str2[10];sprintf(str2,"%f", temperature);Usart_SendString(DEBUG_USART2, str2);Usart_SendString(DEBUG_USART2, "}\\,\\\"version\\\": \\\"1.0\\\"}\",0,0\r\n");Delay_ms(50);//向串口環(huán)形緩沖區(qū)取數(shù)據(jù)while(!fifo_is_empty()){if(uart_buf_get(&ch)){str[i] = ch;i++;}}Usart_SendString(DEBUG_USART1, "收到ESP8266傳來的數(shù)據(jù):\r\n");Usart_SendString(DEBUG_USART1, (char *)str);Usart_SendString(DEBUG_USART1, "\r\n");
}//阿里云訂閱數(shù)據(jù)
void AliyunSubscriptionData(void)
{ uint8_t ch;uint8_t str3[255], i=0;Usart_SendString(DEBUG_USART2, "AT+MQTTSUB=0,\"");Usart_SendString(DEBUG_USART2, SubscriptionTOPIC);Usart_SendString(DEBUG_USART2, "\",0\r\n");Delay_ms(50);//向串口環(huán)形緩沖區(qū)取數(shù)據(jù)while(!fifo_is_empty()){if(uart_buf_get(&ch)){str3[i] = ch;i++;}}Usart_SendString(DEBUG_USART1, "\r\n");Usart_SendString(DEBUG_USART1, "esp8266設(shè)置訂閱數(shù)據(jù)回復(fù):\r\n");Usart_SendString(DEBUG_USART1, (char *)str3);Usart_SendString(DEBUG_USART1, "\r\n");
}
esp8266.h
#ifndef __ESP8266_H
#define __ESP8266_H#include "stm32f10x.h"
#include "usart.h"
#include "delay.h"
#include <string.h>#define WiFi_Name "CMCC-H3qz"
#define WiFi_Password "7xf47uxf"
#define Aliyun_ClientId "k0efkfcSwlt.Device_Demo|securemode=2\\,signmethod=hmacsha256\\,timestamp=1705149361131|"
#define Aliyun_Username "Device_Demo&k0efkfcSwlt"
#define Aliyun_Password "ea343ac1d6a4eec7b057bcab39a2ffdf88abc8b810ec0dc7ca3de91a56292ff9"
#define BrokerAddress "k0efkfcSwlt.iot-as-mqtt.cn-shanghai.aliyuncs.com"//發(fā)布數(shù)據(jù)topic
#define IssueTOPIC "/sys/k0efkfcSwlt/Device_Demo/thing/event/property/post"
//訂閱數(shù)據(jù)topic
#define SubscriptionTOPIC "/sys/k0efkfcSwlt/Device_Demo/thing/service/property/set"char esp8266_text(void);
void esp8266_restart(void);
char esp8266_setStation(void);
char esp8266_inquireWiFi_Data(void);
void esp8266_connectWiFi(void);
char esp8266_setMQTTData(void);
char connect_Aliyun(void);
void AliyunSetData(float temperature);
void AliyunSubscriptionData(void);#endif
main.c
void RCC_Configuration(void)
{ //Install system time is 72MHZSystemInit();
}int main(void)
{ uint8_t ch, bufferFlag=0;char str[255], i=0;char *property = 0;char led_switch;RCC_Configuration(); //系統(tǒng)時(shí)鐘初始化Delay_Timer_Init(); //延時(shí)函數(shù)定時(shí)器初始化USART_Config(); //串口1初始化USART2_Config(); //串口2初始化//向串口1 發(fā)送一段數(shù)據(jù),測(cè)試串口1初始化成功printf("這是一個(gè)STM32掛載ESP8266連接阿里云的例程!\r\n");//初始化PB5為輸出gpio_OutputConfig(RCC_APB2Periph_GPIOB, GPIO_Pin_5, GPIOB);//并將LED燈熄滅(將引腳拉高)GPIO_SetBits(GPIOB, GPIO_Pin_5);//如下同理...gpio_OutputConfig(RCC_APB2Periph_GPIOB, GPIO_Pin_0, GPIOB);GPIO_SetBits(GPIOB, GPIO_Pin_0);//初始化PA0為按鍵功能(SW1)Key_GPIO_Config(RCC_APB2Periph_GPIOA, GPIO_Pin_0, GPIOA);//初始化PC13為按鍵功能(SW2)Key_GPIO_Config(RCC_APB2Periph_GPIOC, GPIO_Pin_13, GPIOC);while(1){//sw1按鍵按下功能if(Key_Scan(GPIOA, GPIO_Pin_0) == 1){//使能串口2USART2_ON;//連接阿里云if(connect_Aliyun() == 1){if(GPIO_ReadOutputDataBit(GPIOB,GPIO_Pin_5) == 1){GPIO_ResetBits(GPIOB, GPIO_Pin_5); //紅燈亮起AliyunSubscriptionData(); //訂閱數(shù)據(jù)} else{GPIO_SetBits(GPIOB, GPIO_Pin_5); //燈熄滅USART2_ON; //使能串口2}}}//sw2按鍵按下功能//判斷esp8266是否存在if(Key_Scan(GPIOC, GPIO_Pin_13) == 1){USART2_ON;esp8266_text();}//檢測(cè)到紅燈亮起時(shí),發(fā)布和訂閱數(shù)據(jù)if(GPIO_ReadOutputDataBit(GPIOB,GPIO_Pin_5) == 0){//每隔5秒發(fā)布數(shù)據(jù)Delay_ms(1000);AliyunSetData(1.2);//接收阿里云發(fā)送過來的數(shù)據(jù)for(int j=0; j<5; j++){//每隔1秒查看串口環(huán)形緩沖區(qū)是否有數(shù)據(jù)Delay_ms(1000);for(i=0; i<127; i++){str[i] = '\0';}i=0;//向串口環(huán)形緩沖區(qū)取數(shù)據(jù)while(!fifo_is_empty()){bufferFlag = 1;if(uart_buf_get(&ch)){str[i] = ch;i++;}}if(bufferFlag == 1){bufferFlag = 0;//STM32將連接在串口2上ESP8266發(fā)送過來的數(shù)據(jù)轉(zhuǎn)發(fā)到串口1Usart_SendString(DEBUG_USART1, "收到阿里云服務(wù)器發(fā)送過來的數(shù)據(jù):\r\n");Usart_SendString(DEBUG_USART1, str);Usart_SendString(DEBUG_USART1, "\r\n");//以下是通過阿里云控制一盞led燈例程property = strstr(str, "LEDSwitch");if(property != NULL){led_switch = *(property + 11);//Usart_SendByte(DEBUG_USART1, led_switch);led_switch = led_switch - 48;if(led_switch == 0){GPIO_SetBits(GPIOB, GPIO_Pin_0);}else if(led_switch == 1){GPIO_ResetBits(GPIOB, GPIO_Pin_0);}}}}}}
}