一、環(huán)境介紹
上網(wǎng)方式:采用ESP8266,也可以使用其他設(shè)備代替,只要支持TCP協(xié)議即可。比如:GSM模塊、有線網(wǎng)卡等。
開(kāi)發(fā)軟件:keil5
硬件連接功能:ESP8266接在STM32的串口3上。通過(guò)AT指令與ESP8266進(jìn)行通信。
注意:本篇文章沒(méi)有貼ESP8266的底層編程代碼,如果不會(huì)ESP8266底層編程,請(qǐng)看這里:
https://blog.csdn.net/xiaolong1126626497/article/details/107379554
如果需要了解ESP8266+MQTT協(xié)議連接阿里云物聯(lián)網(wǎng)服務(wù)器請(qǐng)看這里:https://blog.csdn.net/xiaolong1126626497/article/details/107311897
二、功能介紹
2.1 功能說(shuō)明
通過(guò)OneNet物聯(lián)網(wǎng)服務(wù)器實(shí)現(xiàn)設(shè)備數(shù)據(jù)遠(yuǎn)程上傳、下發(fā),實(shí)現(xiàn)數(shù)據(jù)交互(不清楚OneNet物聯(lián)網(wǎng)服務(wù)器功能的可以百度一下進(jìn)入官網(wǎng)看簡(jiǎn)介)。之前的OneNet服務(wù)器不支持標(biāo)準(zhǔn)MQTT協(xié)議登錄的,現(xiàn)在官網(wǎng)更新之后支持標(biāo)準(zhǔn)的MQTT協(xié)議,本篇文章介紹使用STM32+ESP8266使用標(biāo)準(zhǔn)MQTT協(xié)議登錄Onenet服務(wù)器,實(shí)現(xiàn)數(shù)據(jù)交互。實(shí)現(xiàn)步驟OneNet官方提供了很詳細(xì)的文檔,可以參考一下。
文檔地址:OneNET-中國(guó)移動(dòng)物聯(lián)網(wǎng)開(kāi)放平臺(tái)
?
?
2.2 硬件資源
在當(dāng)前使用的開(kāi)發(fā)板上有4盞LED燈、一個(gè)蜂鳴器、4個(gè)按鍵,ESP8266型號(hào)是ESP-12F,STM32型號(hào)是:STM32F103C8T6。
?
三、OneNet支持的MQTT協(xié)議版本
目前OneNet服務(wù)器支持MQTT 3.1.1版本,MQTT協(xié)議官網(wǎng):MQTT - The Standard for IoT Messaging
報(bào)文支持情況: 支持connect、subscribe、publish、ping、unsubscribe、disconnect等報(bào)文,不支持pubrec、pubrel、pubcomp報(bào)文。
四、登錄OneNet服務(wù)器創(chuàng)建物聯(lián)網(wǎng)產(chǎn)品
沒(méi)有注冊(cè)賬號(hào)的,需要提前登錄官網(wǎng)注冊(cè)賬號(hào),再進(jìn)入下面步驟:
?
?
?
這里根據(jù)自己產(chǎn)品情況填寫。
?
?
?
產(chǎn)品創(chuàng)建成功之后,點(diǎn)擊產(chǎn)品名稱,跳轉(zhuǎn)頁(yè)面,繼續(xù)添加設(shè)備。
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
下面選擇儀表盤的數(shù)據(jù)來(lái)源,根據(jù)自己創(chuàng)建的數(shù)據(jù)點(diǎn)選擇。
?
創(chuàng)建一個(gè)文本控件,顯示數(shù)據(jù)點(diǎn)更新的時(shí)間,方便調(diào)試。
?
?
?
?
OneNte有手機(jī)版本的APP,登錄之后也可以看到該頁(yè)面。
下載地址:https://open.iot.10086.cn/doc/book/device-develop/multpro/sdk-doc-tool/APP.html
?
?
下面是手機(jī)上登錄APP看到的界面效果:
?
?
五、OneNet服務(wù)器MQTT登錄地址與訂閱主題相關(guān)格式介紹
官網(wǎng)介紹文檔地址:設(shè)備連接_開(kāi)發(fā)者文檔_OneNET
?
5.1 MQTT服務(wù)器登錄地址
?
目前MQTT協(xié)議支持兩個(gè)IP地址和端口號(hào),一個(gè)需要加密、一個(gè)不需要加密。
注意:?jiǎn)纹瑱C(jī)上移植加密算法很麻煩,這里采用不需要加密的端口。(IP地址: 183.230.40.96 端口: 1883)
?
5.2 MQTT登錄的:設(shè)備ID、用戶名稱、密碼 格式參數(shù)
?
?
?
上面圖片里說(shuō)明了,OneNet的設(shè)備參數(shù)與標(biāo)準(zhǔn)MQTT協(xié)議的登錄參數(shù)對(duì)應(yīng)關(guān)系。 OneNet的設(shè)備參數(shù),在設(shè)備頁(yè)面可以去查看。
登錄密碼生成看下面步驟:
?
?
?
注意:該工具在win10系統(tǒng)運(yùn)行可能會(huì)提示非信任程序,點(diǎn)擊任要運(yùn)行即可。
下面是生成MQTT登錄密匙的工具使用示例。
?
注意:工具中填的參數(shù)說(shuō)明請(qǐng)看文檔介紹。
res選項(xiàng)參數(shù)的格式:products/{產(chǎn)品ID}/devices/{設(shè)備名稱}
et是設(shè)置token過(guò)期時(shí)間:算出1970-1-1到你想要設(shè)置的到期時(shí)間,單位是秒,填入即可。
比如: 超時(shí)時(shí)間設(shè)置為2020-07-20 ,那么,這里填入的秒就是:1970-1-1到2020-07-20之間的秒單位時(shí)間。
Linux下代碼:
#include
#include
#include
int main()
{
time_t time_sec;
time_sec=time(NULL); //當(dāng)前的秒單位時(shí)間--UTC時(shí)間
printf("當(dāng)前時(shí)間(秒):%ldn",time_sec);
printf("加30天的時(shí)間(秒):%ldn",time_sec+30*24*60*60);
return 0;
}
key的參數(shù)格式: 就是設(shè)備創(chuàng)建之后,在設(shè)備詳情頁(yè)的key
工具生成的結(jié)果值,直接當(dāng)做MQTT登錄的密碼。
5.3 主題訂閱格式
文檔地址:協(xié)議規(guī)范_開(kāi)發(fā)者文檔_OneNET
?
?
5.4 設(shè)備保活時(shí)間
?
?
5.5 向服務(wù)器傳數(shù)據(jù)點(diǎn)
?
?
?
六、核心代碼
6.1 matt.c代碼
#include "mqtt.h"
u8 *mqtt_rxbuf;
u8 *mqtt_txbuf;
u16 mqtt_rxlen;
u16 mqtt_txlen;
u8 _mqtt_txbuf[256];//發(fā)送數(shù)據(jù)緩存區(qū)
u8 _mqtt_rxbuf[256];//接收數(shù)據(jù)緩存區(qū)
typedef enum
{
//名字 值 報(bào)文流動(dòng)方向 描述
M_RESERVED1 =0 , // 禁止 保留
M_CONNECT , // 客戶端到服務(wù)端 客戶端請(qǐng)求連接服務(wù)端
M_CONNACK , // 服務(wù)端到客戶端 連接報(bào)文確認(rèn)
M_PUBLISH , // 兩個(gè)方向都允許 發(fā)布消息
M_PUBACK , // 兩個(gè)方向都允許 QoS 1消息發(fā)布收到確認(rèn)
M_PUBREC , // 兩個(gè)方向都允許 發(fā)布收到(保證交付第一步)
M_PUBREL , // 兩個(gè)方向都允許 發(fā)布釋放(保證交付第二步)
M_PUBCOMP , // 兩個(gè)方向都允許 QoS 2消息發(fā)布完成(保證交互第三步)
M_SUBSCRIBE , // 客戶端到服務(wù)端 客戶端訂閱請(qǐng)求
M_SUBACK , // 服務(wù)端到客戶端 訂閱請(qǐng)求報(bào)文確認(rèn)
M_UNSUBSCRIBE , // 客戶端到服務(wù)端 客戶端取消訂閱請(qǐng)求
M_UNSUBACK , // 服務(wù)端到客戶端 取消訂閱報(bào)文確認(rèn)
M_PINGREQ , // 客戶端到服務(wù)端 心跳請(qǐng)求
M_PINGRESP , // 服務(wù)端到客戶端 心跳響應(yīng)
M_DISCONNECT , // 客戶端到服務(wù)端 客戶端斷開(kāi)連接
M_RESERVED2 , // 禁止 保留
}_typdef_mqtt_message;
//連接成功服務(wù)器回應(yīng) 20 02 00 00
//客戶端主動(dòng)斷開(kāi)連接 e0 00
const u8 parket_connetAck[] = {0x20,0x02,0x00,0x00};
const u8 parket_disconnet[] = {0xe0,0x00};
const u8 parket_heart[] = {0xc0,0x00};
const u8 parket_heart_reply[] = {0xc0,0x00};
const u8 parket_subAck[] = {0x90,0x03};
void MQTT_Init(void)
{
//緩沖區(qū)賦值
mqtt_rxbuf = _mqtt_rxbuf;
mqtt_rxlen = sizeof(_mqtt_rxbuf);
mqtt_txbuf = _mqtt_txbuf;
mqtt_txlen = sizeof(_mqtt_txbuf);
memset(mqtt_rxbuf,0,mqtt_rxlen);
memset(mqtt_txbuf,0,mqtt_txlen);
//無(wú)條件先主動(dòng)斷開(kāi)
MQTT_Disconnect();
delay_ms(100);
MQTT_Disconnect();
delay_ms(100);
}
/*
函數(shù)功能: 登錄服務(wù)器
函數(shù)返回值: 0表示成功 1表示失敗
*/
u8 MQTT_Connect(char *ClientID,char *Username,char *Password)
{
u8 i,j;
int ClientIDLen = strlen(ClientID);
int UsernameLen = strlen(Username);
int PasswordLen = strlen(Password);
int DataLen;
mqtt_txlen=0;
//可變報(bào)頭+Payload 每個(gè)字段包含兩個(gè)字節(jié)的長(zhǎng)度標(biāo)識(shí)
DataLen = 10 + (ClientIDLen+2) + (UsernameLen+2) + (PasswordLen+2);
//固定報(bào)頭
//控制報(bào)文類型
mqtt_txbuf[mqtt_txlen++] = 0x10; //MQTT Message Type CONNECT
//剩余長(zhǎng)度(不包括固定頭部)
do
{
u8 encodedByte = DataLen % 128;
DataLen = DataLen / 128;
// if there are more data to encode, set the top bit of this byte
if ( DataLen > 0 )
encodedByte = encodedByte | 128;
mqtt_txbuf[mqtt_txlen++] = encodedByte;
}while ( DataLen > 0 );
//可變報(bào)頭
//協(xié)議名
mqtt_txbuf[mqtt_txlen++] = 0; // Protocol Name Length MSB
mqtt_txbuf[mqtt_txlen++] = 4; // Protocol Name Length LSB
mqtt_txbuf[mqtt_txlen++] = 'M'; // ASCII Code for M
mqtt_txbuf[mqtt_txlen++] = 'Q'; // ASCII Code for Q
mqtt_txbuf[mqtt_txlen++] = 'T'; // ASCII Code for T
mqtt_txbuf[mqtt_txlen++] = 'T'; // ASCII Code for T
//協(xié)議級(jí)別
mqtt_txbuf[mqtt_txlen++] = 4; // MQTT Protocol version = 4 對(duì)于 3.1.1 版協(xié)議,協(xié)議級(jí)別字段的值是 4(0x04)
//連接標(biāo)志
mqtt_txbuf[mqtt_txlen++] = 0xc2; // conn flags
mqtt_txbuf[mqtt_txlen++] = 0; // Keep-alive Time Length MSB
mqtt_txbuf[mqtt_txlen++] = 100; // Keep-alive Time Length LSB 100S心跳包 ?;顣r(shí)間
mqtt_txbuf[mqtt_txlen++] = BYTE1(ClientIDLen);// Client ID length MSB
mqtt_txbuf[mqtt_txlen++] = BYTE0(ClientIDLen);// Client ID length LSB
memcpy(&mqtt_txbuf[mqtt_txlen],ClientID,ClientIDLen);
mqtt_txlen += ClientIDLen;
if(UsernameLen > 0)
{
mqtt_txbuf[mqtt_txlen++] = BYTE1(UsernameLen); //username length MSB
mqtt_txbuf[mqtt_txlen++] = BYTE0(UsernameLen); //username length LSB
memcpy(&mqtt_txbuf[mqtt_txlen],Username,UsernameLen);
mqtt_txlen += UsernameLen;
}
if(PasswordLen > 0)
{
mqtt_txbuf[mqtt_txlen++] = BYTE1(PasswordLen); //password length MSB
mqtt_txbuf[mqtt_txlen++] = BYTE0(PasswordLen); //password length LSB
memcpy(&mqtt_txbuf[mqtt_txlen],Password,PasswordLen);
mqtt_txlen += PasswordLen;
}
memset(mqtt_rxbuf,0,mqtt_rxlen);
MQTT_SendBuf(mqtt_txbuf,mqtt_txlen);
for(j=0;j<10;j++)
{
delay_ms(50);
if(USART3_RX_FLAG)
{
memcpy((char *)mqtt_rxbuf,USART3_RX_BUFFER,USART3_RX_CNT);
//memcpy
for(i=0;i;i++)usart1_printf("%#x>
6.2 mqtt.h代碼
#ifndef __FY_MQTT_H_
#define __FY_MQTT_H_
#include "stm32f10x.h"
#include "string.h"
#include "stdio.h"
#include "stdlib.h"
#include "stdarg.h"
#include "delay.h"
#include "usart.h"
#define BYTE0(dwTemp) (*( char *)(&dwTemp))
#define BYTE1(dwTemp) (*((char *)(&dwTemp) + 1))
#define BYTE2(dwTemp) (*((char *)(&dwTemp) + 2))
#define BYTE3(dwTemp) (*((char *)(&dwTemp) + 3))
//用戶名初始化
void OneNet_LoginInit(char *ProductKey,char *DeviceName,char *DeviceSecret);
//MQTT協(xié)議相關(guān)函數(shù)聲明
u8 MQTT_PublishData(char *topic, char *message, u8 qos);
u8 MQTT_SubscribeTopic(char *topic,u8 qos,u8 whether);
void MQTT_Init(void);
u8 MQTT_Connect(char *ClientID,char *Username,char *Password);
void MQTT_SentHeart(void);
void MQTT_Disconnect(void);
void MQTT_SendBuf(u8 *buf,u16 len);
#endif
6.3 main.c 主函數(shù)代碼
#include "stm32f10x.h"
#include "led.h"
#include "delay.h"
#include "key.h"
#include "usart.h"
#include
#include "timer.h"
#include "esp8266.h"
#include "mqtt.h"
/*
序號(hào) 符號(hào) 編碼
1 + %2B
2 空格%20
3 / %2F
4 ? %3F
5 % %25
6 # %23
7 & %26
8 = %3D
*/
//OneNet物聯(lián)網(wǎng)服務(wù)器的設(shè)備信息
#define MQTT_ClientID "mq2"
#define MQTT_UserName "361594"
#define MQTT_PassWord "version=2018-10-31&res=products%2F361594%2Fdevices%2Fmq2&et=1597492895&method=sha1&sign=uqvA0KkjXw0FlN01aT6fWrGBLGw%3D"
//訂閱與發(fā)布的主題
//格式:$sys/{產(chǎn)品ID}/{設(shè)備名稱}/#
#define SET_TOPIC "$sys/361594/mq2/#" //訂閱設(shè)備所有信息
//格式: $sys/{產(chǎn)品ID}/{設(shè)備名稱}/dp/post/json
#define POST_TOPIC "$sys/361594/mq2/dp/post/json" //發(fā)布
char mqtt_message[200];//上報(bào)數(shù)據(jù)緩存區(qū)
int main()
{
u32 time_cnt=0;
u32 i;
u8 key;
LED_Init();
BEEP_Init();
KEY_Init();
USART1_Init(115200);
TIMER1_Init(72,20000); //超時(shí)時(shí)間20ms
USART3_Init(115200);//串口-WIFI
TIMER3_Init(72,20000); //超時(shí)時(shí)間20ms
USART1_Printf("正在初始化WIFI請(qǐng)稍等.n");
if(ESP8266_Init())
{
USART1_Printf("ESP8266硬件檢測(cè)錯(cuò)誤.n");
}
else
{
//加密端口
//USART1_Printf("WIFI:%dn",ESP8266_STA_TCP_Client_Mode("OnePlus5T","1126626497","183.230.40.16",8883,1));
//非加密端口
USART1_Printf("WIFI:%dn",ESP8266_STA_TCP_Client_Mode("OnePlus5T","1126626497","183.230.40.96",1883,1));
}
//2. MQTT協(xié)議初始化
MQTT_Init();
//3. 連接OneNet服務(wù)器
while(MQTT_Connect(MQTT_ClientID,MQTT_UserName,MQTT_PassWord))
{
USART1_Printf("OneNet服務(wù)器連接失敗,正在重試...n");
delay_ms(500);
}
USART1_Printf("OneNet服務(wù)器連接成功.n");
//3. 訂閱主題
if(MQTT_SubscribeTopic(SET_TOPIC,0,1))
{
USART1_Printf("主題訂閱失敗.n");
}
else
{
USART1_Printf("主題訂閱成功.n");
}
while(1)
{
key=KEY_Scan(0);
if(key==2)
{
time_cnt=0;
sprintf(mqtt_message,"{"id":1,"dp":{"mq2":[{"v":50}]}}");
MQTT_PublishData(POST_TOPIC,mqtt_message,0);
USART1_Printf("發(fā)送狀態(tài)1rn");
}
else if(key==3)
{
time_cnt=0;
sprintf(mqtt_message,"{"id":1,"dp":{"mq2":[{"v":80}]}}");
MQTT_PublishData(POST_TOPIC,mqtt_message,0);
USART1_Printf("發(fā)送狀態(tài)0rn");
}
if(USART3_RX_FLAG)
{
USART3_RX_BUFFER[USART3_RX_CNT]='?';
for(i=0;i;i++)>
七、設(shè)備登錄運(yùn)行效果
登錄成功之后,網(wǎng)頁(yè)會(huì)顯示在線狀態(tài)。
?
按下開(kāi)發(fā)按鍵上傳煙霧數(shù)據(jù)到服務(wù)器效果:
?
工程完整源碼下載:https://download.csdn.net/download/xiaolong1126626497/15803518
審核編輯:符乾江
;i++)usart1_printf("%#x>-
STM32
+關(guān)注
關(guān)注
2270文章
10895瀏覽量
355721 -
OneNET
+關(guān)注
關(guān)注
1文章
51瀏覽量
12800
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論