實驗硬件:STM32F103ZET6;0.96寸OLED(128×64);ESP8266,DHT11;LED;KEY
硬件實物圖:
效果圖:
引腳連接:
OLED模塊引腳:
VCC --> 3.3V
GND --> GND
SCL --> PB10
SDA --> PB11
ESP8266模塊引腳:
VCC --> 3.3V
GND --> GND
RX--> PB10
TX --> PB11
RST --> PB9
EN --> PB7
DHT11傳感器引腳:
VCC --> 3.3V
GND --> GND
DATA-->PE0
1.1 物聯(lián)網(wǎng)簡介
物聯(lián)網(wǎng)(Internet of Things,簡稱IoT)是指通過各種信息傳感器、射頻識別技術(shù)、全球定位系統(tǒng)、紅外感應(yīng)器、激光掃描器等各種裝置與技術(shù),實時采集任何需要監(jiān)控、 連接、互動的物體或過程,采集其聲、光、熱、電、力學(xué)、化學(xué)、生物、位置等各種需要的信息,通過各類可能的網(wǎng)絡(luò)接入,實現(xiàn)物與物、物與人的泛在連接,實現(xiàn)對物品和過程的智能化感知、識別和管理。物聯(lián)網(wǎng)是一個基于互聯(lián)網(wǎng)、傳統(tǒng)電信網(wǎng)等的信息承載體,它讓所有能夠被獨立尋址的普通物理對象形成互聯(lián)互通的網(wǎng)絡(luò) 。
總而言之,物聯(lián)網(wǎng)就是利用現(xiàn)代互聯(lián)網(wǎng)技術(shù)實現(xiàn)端對端的數(shù)據(jù)互聯(lián)與控制。
1.2 物聯(lián)網(wǎng)開發(fā)
目前,物聯(lián)網(wǎng)開發(fā)的形式是多種多樣的。總的來說,一般都需借助特定的網(wǎng)絡(luò)服務(wù)平臺為基礎(chǔ)實現(xiàn)數(shù)據(jù)的上傳與下發(fā)(如果只考慮內(nèi)網(wǎng)是可以不需要的,比如ESP32CAM)。
實力雄厚或者有一定背景的公司通常考慮可能自建網(wǎng)絡(luò)協(xié)議服務(wù)器,專屬服務(wù)自家的物聯(lián)網(wǎng)產(chǎn)品開發(fā)。當(dāng)然,也有不少企業(yè)會選擇借助他人網(wǎng)絡(luò)服務(wù)平臺去實現(xiàn)自家的物聯(lián)網(wǎng)開發(fā)。
這里比較著名的網(wǎng)絡(luò)服務(wù)平臺有:中國移動旗下的OneNet、阿里巴巴旗下阿里云以及機(jī)智云平臺。
平臺分析:
機(jī)智云:機(jī)智云作為物聯(lián)網(wǎng)開發(fā)服務(wù)平臺的元老,一直致力于完善和搭建快速高效的服務(wù)機(jī)制,其有一套自己快速開發(fā)適配的物聯(lián)網(wǎng)實現(xiàn)流程。但是,在筆者使用的過程中也存在著一些弊端。比如:
(1)其需要給ESP8266等WIFI模塊刷上自家的固件才可使用;
(2)狀態(tài)極其不穩(wěn)定,很容易斷聯(lián)或者死活連不上;
(3)受限于開發(fā)模式,對于產(chǎn)品自我開發(fā)有一定限制;
OneNet和阿里云平臺:這2大平臺背靠強(qiáng)大的資源和技術(shù)支持,其服務(wù)穩(wěn)定。設(shè)定的開發(fā)框架也更多樣化,可以提供開發(fā)者更多的發(fā)揮空間。
二、OneNet平臺使用
從多元化和產(chǎn)品穩(wěn)定性方面考慮,作者將以中國移動旗下的OneNet服務(wù)平臺為案例進(jìn)行教學(xué)講解。(其實本來打算以機(jī)智云出一篇案例的,結(jié)果后來發(fā)現(xiàn)之前能正常聯(lián)動的MCU和APP動不動就宕機(jī)。后來,索性直接就以O(shè)neNet這個框架更開放的平臺為案例教學(xué))
2.1 OneNet準(zhǔn)備
1、注冊O(shè)neNet平臺賬號(網(wǎng)址:OneNET - 中國移動物聯(lián)網(wǎng)開放平臺 (10086.cn));
2、 登入后選擇控制臺,進(jìn)入后點擊全部產(chǎn)品服務(wù),選擇多協(xié)議接入;(我們使用MQTT,既可以上傳數(shù)據(jù)也可以下發(fā)數(shù)據(jù)控制,而且都是免費的)
3、選擇MQTT(舊版)之后添加產(chǎn)品,按照自己實際需求填寫產(chǎn)品內(nèi)容;
4、點擊所創(chuàng)建的產(chǎn)品,添加幾個設(shè)備(免費版用戶上限10個設(shè)備)
5、注意設(shè)備ID,鑒權(quán)信息以及接入方式這3個屬性;
6、關(guān)于數(shù)據(jù)流模塊可以設(shè)置,可以不設(shè)置,反正最后通訊正常的情況下會收到需要的數(shù)據(jù)流;
2.2 OneNet調(diào)試
在設(shè)置好OneNet平臺設(shè)備后,其實可以借助該平臺自帶的API調(diào)試工具進(jìn)行調(diào)試檢測(前提:下位機(jī)已經(jīng)成果接通了)。
這里的調(diào)試使用API函數(shù)的介紹和使用可以參考文檔中心(一個合格的嵌入式工程師是一定需要學(xué)會自己去查看技術(shù)支持文檔,而且OneNet提供的文檔內(nèi)容還是非常詳盡的)。
OneNet技術(shù)文檔網(wǎng)址:OneNET - 中國移動物聯(lián)網(wǎng)開放平臺 (10086.cn)
網(wǎng)絡(luò)協(xié)議通訊關(guān)鍵函數(shù)
服務(wù)器或上位機(jī)查詢讀取設(shè)備歷史數(shù)據(jù):
API函數(shù):
請求方式:GET
URL:
http://api.heclouds.com/devices/device_id/datapoints
服務(wù)器或上位機(jī)下發(fā)主題報文(控制下位機(jī)):
API函數(shù):
請求方式:POST
URL: http://api.heclouds.com/mqtt?topic=xxx
以上2個網(wǎng)絡(luò)通訊的API函數(shù)至關(guān)重要,就是實現(xiàn)常規(guī)情況下OneNet物聯(lián)網(wǎng)開發(fā)的關(guān)鍵性技術(shù)支持。(情況允許的條件下,建議讀者朋友們?nèi)ズ煤醚凶x一下技術(shù)文檔,將會為之后的開發(fā)大大助力)
三、下位機(jī)外設(shè)驅(qū)動
3.1 ESP8266模塊
作者采用的ESP8266模塊為ESP8266NodeMCU,是需要進(jìn)行燒入AT固件,才能實現(xiàn)目標(biāo)網(wǎng)絡(luò)通訊。作為常見的物聯(lián)網(wǎng)開發(fā)模塊,ESP8266的出現(xiàn)大大降低了物聯(lián)網(wǎng)開發(fā)的難度系數(shù),也普及了物聯(lián)網(wǎng)的發(fā)展。
AT指令最早在藍(lán)牙模塊上接觸過,所謂AT指令實質(zhì)上就是一些起控制作用的特殊字符串。模塊可以通過AT指令控制搭配使用源代碼API函數(shù)開發(fā),總體開發(fā)速度快,難度較低。
不同廠商芯片的AT固件可能有所不同,但是指令基本一致(作者使用的是樂鑫的)。
說明:由于篇幅有限,這里就不和大家單獨詳細(xì)介紹AT指令。指令的詳細(xì)參數(shù)及使用說明請參考官方文檔:ESP8266 AT指令集。
3.2 OLED模塊
本項目中0.96寸OLED模塊的使用僅為顯示DHT11傳感器采集到的溫濕度信息,以此來對比是否和服務(wù)器端以及上位機(jī)APP端的數(shù)據(jù)一致性。對其使用有不是太了解的讀者朋友可以參考,作者另一篇基礎(chǔ)教學(xué)博客:(2條消息) 【強(qiáng)烈推薦】基于stm32的OLED各種顯示實現(xiàn)(含動態(tài)圖)_混分巨獸龍某某的博客-CSDN博客_oled顯示圖片程序
本項目的代碼都是基于作者以前基礎(chǔ)教學(xué)上的項目代碼搭建而成,保證讀者朋友可以實現(xiàn)快速復(fù)現(xiàn)。
3.3 DHT11模塊
本項目中DHT11為下位機(jī)MCU采集周圍環(huán)境溫度和濕度的傳感器,當(dāng)然,條件允許的情況下還可以附加很多環(huán)境傳感器(比如:煙霧傳感器,環(huán)境光傳感器,二氧化碳傳感器等等)。當(dāng)然得益于OneNet平臺的布局,本項目教學(xué)的底層邏輯支持讀者朋友的自我DIY,實現(xiàn)自主化的物聯(lián)網(wǎng)產(chǎn)品設(shè)計。
DHT11模塊驅(qū)動參考博客:基于stm32的太空人溫濕度時鐘項目——DHT11(HAL庫)_混分巨獸龍某某的博客-CSDN博客
3.4 KEY和LED
KEY和LED都是源于作者正點原子精英版開發(fā)板上自備的(如果和作者同款開發(fā)板移植開發(fā)將會特別簡單快速),屬于最基本的GPIO操作相信各位應(yīng)該都是掌握的
特別注意:
(1)這里的KEY按鍵從設(shè)計邏輯上就可以看出應(yīng)該是需要采用外部中斷的;
(2)KEY按下之后會改變LED的亮滅狀態(tài),為了同步上位機(jī)此時的LED狀態(tài),所以需要觸發(fā)串口通訊中斷(考慮嵌套中斷情況時候中斷優(yōu)先級的安排)。
四、CubeMX配置
1、RCC配置外部高速晶振(精度更高)——HSE;
2、SYS配置:Debug設(shè)置成Serial Wire(否則可能導(dǎo)致芯片自鎖);
3、TIM2配置:由上面可知DHT11的使用需要us級的延遲函數(shù),HAL庫自帶只有ms的,所以需要自己設(shè)計一個定時器;
4、I2C2配置:作為OLED的通訊方式;
5、UART1和UART3配置:MCU分別與電腦和ESP8266通訊(記得開啟串口通信中斷);
6、設(shè)置KEY0按鍵PE4為外部中斷(根據(jù)自己的開發(fā)板來確定)
7、GPIO配置:PE0設(shè)置為DHT11的DATA端,PE5為LED,并且設(shè)置ESP8266的EN和RST(PB7和PB9);
8、時鐘樹配置
五、代碼與解析
5.1 OLED與DHT11模塊代碼
受篇幅限制OLED與DHT11部分的代碼,這里就不展示了。如果有不懂這部分原理與代碼的讀者朋友可以參考本人的另一篇博客。博客地址:基于stm32的太空人溫濕度時鐘項目——DHT11(HAL庫)_混分巨獸龍某某的博客-CSDN博客
5.2 ESP8266模塊代碼
ESP8266部分的代碼主要是借助串口通訊AT指令與ESP8266模塊(刷入AT固件的)與OneNet平臺進(jìn)行信息交互(包含ESP8266初始化、數(shù)據(jù)發(fā)送,指令發(fā)送和數(shù)據(jù)緩存清除等)。
esp8266.h代碼:
#ifndef _ESP8266_H_
#define _ESP8266_H_
#include "main.h"
#include "usart.h"
#include
#include
#include
#define ESP8266_WIFI_INFO "AT+CWJAP="NJUST","768541ly"\r\n" //連接上自己的wifi熱點:WiFi名和密碼
#define ESP8266_ONENET_INFO "AT+CIPSTART="TCP","183.230.40.39",6002\r\n" //連接上OneNet的MQTT
#define OK 0 //接收完成標(biāo)志
#define OUTTIME 1 //接收未完成標(biāo)志
void ESP8266_Clear(void); //清空緩存
void ESP8266_Init(void); //esp8266初始化
_Bool ESP8266_SendCmd(char *cmd, char *res);//發(fā)送數(shù)據(jù)
unsigned char *ESP8266_GetIPD(unsigned short timeOut);
void ESP8266_SendData(unsigned char *data, unsigned short len);
#endif
esp8266.c代碼:
#include "esp8266.h"
unsigned char ESP8266_Buf[128]; //定義一個數(shù)組作為esp8266的數(shù)據(jù)緩沖區(qū)
unsigned short esp8266_cnt = 0, esp8266_cntPre = 0; //定義兩個計數(shù)值:此次和上一次
unsigned char a_esp8266_buf;
/**
* @brief esp8266初始化
* @param 無
* @retval 無
*/
void ESP8266_Init(void)
{
ESP8266_Clear();
printf("1. 測試AT啟動\r\n"); //AT:測試AT啟動
while(ESP8266_SendCmd("AT\r\n", "OK"))
HAL_Delay(500);
printf("2. 設(shè)置WiFi模式(CWMODE)\r\n"); //查詢/設(shè)置 Wi-Fi 模式:設(shè)置WiFi模式為Station模式
while(ESP8266_SendCmd("AT+CWMODE=1\r\n", "OK"))
HAL_Delay(500);
printf("3. AT+CWDHCP\r\n"); //啟用/禁用 DHCP
while(ESP8266_SendCmd("AT+CWDHCP=1,1\r\n", "OK"))
HAL_Delay(500);
printf("4. 連接WiFi熱點(CWJAP)\r\n");
while(ESP8266_SendCmd(ESP8266_WIFI_INFO, "GOT IP"))
HAL_Delay(500);
printf("5. 建立TCP連接(CIPSTART)\r\n");
while(ESP8266_SendCmd(ESP8266_ONENET_INFO, "CONNECT"))
HAL_Delay(500);
printf("6. ESP8266 Init OK\r\n");
}
/**
* @brief 清空緩存
* @param 無
* @retval 無
*/
void ESP8266_Clear(void)
{
memset(ESP8266_Buf, 0, sizeof(ESP8266_Buf)); //將數(shù)組中的元素全部初始化為0,
}
/**
* @brief 等待接收完成
* @param 無
* @retval OK:表示接收完成;OUTTIME:表示接收超時完成
* 進(jìn)行循環(huán)調(diào)用,檢測接收是否完成
*/
_Bool ESP8266_WaitRecive(void)
{
if(esp8266_cnt == 0) //如果當(dāng)前接收計數(shù)為0 則說明沒有處于接收數(shù)據(jù)中,所以直接跳出,結(jié)束函數(shù)
return OUTTIME;
if(esp8266_cnt == esp8266_cntPre) //如果上一次的值和這次相同,則說明接收完畢
{
esp8266_cnt = 0; //清0接收計數(shù)
return OK; //返回接收完成標(biāo)志
}
else //如果不相同,則將此次賦值給上一次,并返回接收未完成標(biāo)志
{
esp8266_cntPre = esp8266_cnt;
return OUTTIME;
}
}
/**
* @brief 發(fā)送命令
* @param cmd:表示命令;res:需要檢查的返回指令
* @retval 0:表示成功;1:表示失敗
*/
_Bool ESP8266_SendCmd(char *cmd, char *res)
{
unsigned char timeOut = 200;
HAL_UART_Transmit(&huart3, (unsigned char *)cmd, strlen((const char *)cmd),0xffff);
while(timeOut--)
{
if(ESP8266_WaitRecive() == OK) //如果收到數(shù)據(jù)
{
printf("%s",ESP8266_Buf);
if(strstr((const char *)ESP8266_Buf, res) != NULL) //如果檢索到關(guān)鍵詞,清空緩存
{
ESP8266_Clear();
return 0;
}
}
HAL_Delay(10);
}
return 1;
}
/**
* @brief 數(shù)據(jù)發(fā)送
* @param data:待發(fā)送的數(shù)據(jù);len:待發(fā)送的數(shù)據(jù)長度
* @retval 無
*/
void ESP8266_SendData(unsigned char *data, unsigned short len)
{
char cmdBuf[32];
ESP8266_Clear(); //清空接收緩存
sprintf(cmdBuf, "AT+CIPSEND=%d\r\n", len); //發(fā)送命令,sprintf()函數(shù)用于將格式化的數(shù)據(jù)寫入字符串
if(!ESP8266_SendCmd(cmdBuf, ">")) //收到‘>’時可以發(fā)送數(shù)據(jù)
{
HAL_UART_Transmit(&huart3, data, len,0xffff); //發(fā)送設(shè)備連接請求數(shù)據(jù)
}
}
/**
* @brief 獲取平臺返回的數(shù)據(jù)
* @param 等待的時間
* @retval 平臺返回的數(shù)據(jù),不同網(wǎng)絡(luò)設(shè)備返回的格式不同,需要進(jìn)行調(diào)試,如:ESP8266的返回格式為:"+IPD,x:yyy",x表示數(shù)據(jù)長度,yyy表示數(shù)據(jù)內(nèi)容
*/
unsigned char *ESP8266_GetIPD(unsigned short timeOut)
{
char *ptrIPD = NULL;
do
{
if(ESP8266_WaitRecive() == OK) //如果接收完成
{
ptrIPD = strstr((char *)ESP8266_Buf, "IPD,"); //搜索“IPD”頭
if(ptrIPD == NULL) //如果沒找到,可能是IPD頭的延遲,還是需要等待一會,但不會超過設(shè)定的時間
{
//UsartPrintf(USART_DEBUG, ""IPD" not found\r\n");
}
else
{
ptrIPD = strchr(ptrIPD, ':'); //找到':'
if(ptrIPD != NULL)
{
ptrIPD++;
return (unsigned char *)(ptrIPD);
}
else
return NULL;
}
}
HAL_Delay(5); //延時等待
} while(timeOut--);
return NULL; //超時還未找到,返回空指針
}
/**
* @brief 串口2收發(fā)中斷回調(diào)函數(shù)
* @param
* @retval
*/
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if(esp8266_cnt >= 255) //溢出判斷,超過一個字節(jié)
{
esp8266_cnt = 0;
memset(ESP8266_Buf,0x00,sizeof(ESP8266_Buf));
HAL_UART_Transmit(&huart3, (uint8_t *)"數(shù)據(jù)溢出", 10,0xFFFF);
}
else
{
ESP8266_Buf[esp8266_cnt++] = a_esp8266_buf; //接收數(shù)據(jù)轉(zhuǎn)存
}
HAL_UART_Receive_IT(&huart3, (uint8_t *)&a_esp8266_buf, 1); //再開啟接收中斷
}
代碼總結(jié):
ESP8266模塊的代碼基于HAL庫實現(xiàn),主要是利用AT指令去使下位機(jī)(STM32+ESP8266)連接上WIFI,并且與OneNet平臺進(jìn)行MQTT協(xié)議通信(TCP連接IP地址和對應(yīng)端口)。
特別注意:
使用ESP8266進(jìn)行通訊時,當(dāng)數(shù)據(jù)量較大的時候一定要編寫緩存清除代碼(否則,很有可能出現(xiàn)死機(jī)等情況)。當(dāng)然,這個時候可以搭配SD NAND(貼片式TF卡)去存儲傳輸?shù)臄?shù)據(jù)流。同時,利用這些保存在SD卡中的數(shù)據(jù),可以在下位機(jī)制作精美的數(shù)據(jù)歷史信息UI,極大的拓展了產(chǎn)品價值。
————————————————
【本文轉(zhuǎn)載自CSDN,作者:混分巨獸龍某某】
-
智能家居
+關(guān)注
關(guān)注
1928文章
9579瀏覽量
185539 -
SD
+關(guān)注
關(guān)注
1文章
163瀏覽量
33697
發(fā)布評論請先 登錄
相關(guān)推薦
評論