STM32C8T6+DHT11显示温湿度//通过MQTT实现开发板与阿里云服务器收发(2)
本教程基于韦东山百问网出的 DShanMCU-F103开发板 进行编写,需要的朋友可以在这里获取: https://item.taobao.com/item.htm?id=724601559592
配套资料获取:https://rtos.100ask.net/zh/freeRTOS/DShanMCU-F103
freeRTOS系列教程之freeRTOS入门与工程实践章节汇总: https://blog.csdn.net/qq_35181236/article/details/132842016
通过MQTT实现开发板与阿里云服务器收发
目录
1.WIFI驱动模块
2.创建FreeRTOS工程
3.设备层&平台层&网络层
4.协议层MQTT
5.创建阿里云产品与设备
6.应用层MQTT移植并与阿里云服务器实现通信
1.WIFI驱动模块
在WIFI驱动之前先给大家简单介绍一下用到的AT指令:
- AT+RST—重启模块
- AT+CWMODE_CUR—设置当前 Wi-Fi 模式,不保存到 Flash
- AT+CWJAP_CUR—临时连接 AP
示例:
AT+CWJAP_CUR=“abc”,“0123456789”
例例如,⽬目标 AP 的 SSID 为 “ab,c”,password 为 “0123456789"”,则指令如下:
AT+CWJAP_CUR=“ab\,c”,“0123456789”\"
如果有多个 AP 的 SSID 均为 “abc”,可通过 BSSID 确定⽬目标 AP:
AT+CWJAP_CUR=“abc”,“0123456789”,“ca:d7:19:d8:a6:44”
-
AT+CWQAP—断开与 AP 的连接
-
AT+CIPSTART—建⽴ TCP 连接,UDP 传输或 SSL 连接
-
建⽴ TCP 连接
示例:
AT+CIPSTART=“TCP”,“iot.espressif.cn”,8000
AT+CIPSTART=“TCP”,“192.168.101.110”,1000
- 建⽴UDP 连接
示例:
AT+CIPSTART=“UDP”,“192.168.101.110”,1000,1002,2
⚠ 注意:
使⽤用 必须先填写 。
- AT+CIPCLOSE—关闭 TCP/UDP/SSL 传输
- AT+CIPSEND—发送数据
以上就是本实验会用到一些AT指令,接下来就是WIFI驱动代码。
//发送指令
static int Net_TransmitCmd(const char *cmd, const char *reply, uint16_t timeout)
{
uint8_t i = 0;
char buf[128] = {0};
strcat(buf, cmd);
if(strstr(buf, "\r\n") == NULL)
{
strcat(buf, "\r\n");
}
Buffer_Clean(&CmdRetBuffer);
HAL_UART_Transmit(&huart2,(uint8_t *) buf, strlen(buf), 500);
memset(buf, 0, 128);
while(timeout != 0)
{
if(Buffer_Read(&CmdRetBuffer, (uint8_t*)&buf[i]) == 0)
{
i = (i+1)%128;
if(strstr(buf, reply) != 0)
{
return 0;
}
}
else
{
timeout--;
HAL_Delay(1);
}
}
return -1;
}
//发送数据
int Net_TransmitSocket(const char *socket, int len, int timeout)
{
uint8_t i = 0;
char buf[64] = {0};
char cmd[16] = {0};
sprintf(cmd, "AT+CIPSEND=%d\r\n", len);
HAL_UART_Transmit(&huart2, (uint8_t *)cmd, strlen(cmd), 500);
HAL_Delay(1);
Buffer_Clean(&CmdRetBuffer);
HAL_UART_Transmit(&huart2, (uint8_t *)socket, len, 500);
while(timeout != 0)
{
if(Buffer_Read(&CmdRetBuffer, (uint8_t*)&buf[i]) == 0)
{
i = (i+1)%64;
if(strstr(buf, "SEND OK") != 0)
{
return 0;
}
}
else
{
timeout--;
HAL_Delay(1);
}
}
return -1;
}
int Net_RecvSocket(char *buf, int len, int timeout)
{
int tmp = 0;
while(timeout != 0)
{
tmp += Buffer_ReadBytes(&NetDataBuffer, (uint8_t *)&buf[tmp],len);
if(tmp == len) return 0;
timeout--;
HAL_Delay(1);
}
return -1;
}
//连接WIFI
int Net_ConnectWiFi(const char *ssid, const char *pwd, int timeout)
{
char buf[50] = "AT+CWJAP_CUR=\"";
strcat(buf, ssid);
strcat(buf, "\",\"");
strcat(buf, pwd);
strcat(buf, "\"");
return Net_TransmitCmd(buf, "GOT IP\r\n", timeout);
}
//断开WiFi
int Net_DisconnectWiFi(void)
{
return Net_TransmitCmd("AT+CWOAP", "OK\r\n", 500);
}
//连接TCP
int Net_ConnectTCP(const char *ip, int port, int timeout)
{
char buf[128] = "AT+CIPSTART=\"TCP\",\"";
sprintf(&buf[19], "%s\", %d", ip, port);
return Net_TransmitCmd(buf, "OK\r\n", timeout);
}
//断开TCP
int Net_Disconnect_TCP_UDP(void)
{
return Net_TransmitCmd("AT+CIPCLOSE", "OK\r\n", 500);
}
enum AT_STATUS {
INIT_STATUS,
LEN_STATUS,
DATA_STATUS,
};
static uint8_t g_DataBuff[256] = {0};
//解析网络数据
void NetDataProcess_Callback(uint8_t data)
{
uint8_t *buf = g_DataBuff;
static enum AT_STATUS g_status = INIT_STATUS;
static int g_DataBuffIndex = 0;
static int g_DataLen = 0;
int i = g_DataBuffIndex;
int m = 0;
buf[i] = data;
g_DataBuffIndex++;
switch(g_status)
{
case INIT_STATUS:
{
if(buf[0] !='+')
{
g_DataBuffIndex = 0;
}
else if (i == 4)
{
if(strncmp((char*)buf, "+IPD,", 5) == 0)
{
g_status = LEN_STATUS;
}
g_DataBuffIndex = 0;
}
break;
}
case LEN_STATUS:
{
if(buf[i] == ':')
{
for(m = 0; m < i; m++)
{
g_DataLen = g_DataLen * 10 + buf[m] - '0';
}
g_status = DATA_STATUS;
g_DataBuffIndex = 0;
}
else if (i >= 9) /* ESP8266数据buffer大小是2920, 4位数: +IPD,YYYY:xxxxxx */
{
/* 都没有接收到':' */
g_status = INIT_STATUS;
g_DataBuffIndex = 0;
}
break;
}
case DATA_STATUS:
{
if(g_DataBuffIndex == g_DataLen)
{
Buffer_WriteBytes(&NetDataBuffer, buf, g_DataLen);
g_status = INIT_STATUS;
g_DataBuffIndex = 0;
g_DataLen = 0;
}
break;
}
default:
break;
}
}
int Net_Init(void)
{
if(NET_UART_Init() != 0)
return -1;
Buffer_Init(&CmdRetBuffer, 128);
Buffer_Init(&NetDataBuffer, 1024);
Net_TransmitCmd("AT+RST", "OK\r\n", 10000);
HAL_Delay(500);
Net_TransmitCmd("AT+CWMODE_CUR=1", "OK\r\n", 500);
return 0;
}
以上是WIFI的驱动,我们对发送数据,发送指令,连接WIFI等进行了封装
2.创建FreeRTOS工程
双击运行STM32CubeMX,在首页面选择“Access to MCU Selector”,如下图所示:
然后来到MCU选型界面,在序列号那里输入想要开发的芯片,例如STM32F103C8T6:
- 配置时钟
先配置处理器的时钟,在“System Core”的“RCC”处选择外部高速时钟源和低速时钟源。DshanMCU-F103使用了外部高速时钟源,如下图所示:
另外,本实验使用了FreeRTOS,FreeRTOS的时基使用的是Systick,而STM32CubeMX中默认的HAL库时基也是Systick,为了避免可能的冲突,最好将HAL库的时基换做其它的硬件定时器:
最后去时钟配置界面配置系统时钟频率。直接在HCLK时钟那里输入MCU允许的最高时钟频率。F103的最高频率是72Mhz,所以直接在那里输入72然后按回车:
回车后,STM32CubeMX会自动计算得到各个分频系数和倍频系数:
在上图中点击“OK”,就开始自动配置时钟,配置成功后,结果如下图所示:
- 配置GPIO
板载LED的使用的GPIO是PC13,如下图所示:
所以在STM32CubeMX的引脚配置界面,找到PC13:
在芯片图中,使用鼠标左键点击PC13,会弹出此IO支持的模式:
这里选择GPIO Output,让PC13配置为通用输出IO,以便用来驱动LED的亮灭。
- 配置FreeRTOS
STM32CubeMX已经将FreeRTOS集成到工具中,并且将RTOS的接口进行了封装CMSIS-RTOS V1/V2,相较之于V1版本的CMSIS-RTOS API,V2版本的API的兼容性更高,为了将来的开发和移 植,建议开发者使用V2版本的API:
选择CMSIS V2接口后,还要进一步配置FreeRTOS的参数和功能。
- 配置参数
FreeRTOS的参数包括时基频率、任务堆栈大小、是否使能互斥锁等等,需要开发者根据自己对FreeRTOS的了解以及项目开发的需求,来定制参数。 先如下图进行配置:
- 添加任务
使用STM32CubeMX,可以手工添加任务、队列、信号量、互斥锁、定时器等等。但是本课程不想严重依赖STM32CubeMX,所以不会使用STM32CubeMX来添加这些对象,而是手写代码来使用这些对象。
使用STM32CubeMX时,有一个默认任务,此任务无法删除,只能修改其名称和函数类型,如下图所示:
- 生成Keil MDK的工程
当对外设配置完成后,就去“Project Manager”中设置工程的名称、存储路径和开发IDE:
随后去同界面的“Code Generator”设置、生成工程:
可能会有如下提示,选择“Yes”下载所依赖的文件即可:
一切正常的话,可以看到如下提示:
这样FreeRTOS中间件就创建成功啦!
3.设备层&平台层
(1)设备层&平台层输入输出设备的源文件和头文件
首先创建一个平台层头文件platform.h
- platform.h 部分代码
#ifndef __PLATFORM_H
#define __PLATFORM_H
#include "stm32f1xx_hal.h"
#endif /* __PLATFORM_H */
平台层就是配置我们使用的是什么平台,包含一些什么库文件,或者说定义了是用于这个平台的数据类型的名称别名。
接下来就是创建平台层输入输出设备的源文件和头文件platform_io.h/platform_io.c
- platform_io.c 部分代码
#include "platform_io.h"
#include <driver_led.h>
#include <driver_key.h>
#include <driver_uart.h>
void platform_io_init(struct IODev *dev)
{
}
int platform_io_write(struct IODev *dev, uint8_t *buf, uint16_t len)
{
}
int platform_io_read(struct IODev *dev, uint8_t *buf, uint16_t len)
{
}
- platform_io.h 部分代码
#ifndef __PLATFORM_IO_H
#define __PLATFORM_IO_H
#include "platform.h"
#include <dev_io.h>
void platform_io_init(struct IODev *dev);
int platform_io_write(struct IODev *dev, uint8_t *buf, uint16_t len);
int platform_io_read(struct IODev *dev, uint8_t *buf, uint16_t len);
#endif /* __PLATFORM_IO_H */
接下来就是创建设备层输入输出设备的源文件和头文件dev_io.h/dev_io.c
- dev_io.c 部分代码
#include "dev_io.h"
#include <platform_io.h>
static void IODev_Init(struct IODev *dev)
{
platform_io_init(dev);
}
static int IODev_Write(struct IODev *dev, unsigned char *buf, unsigned short len)
{
return platform_io_write(dev, buf, len);
}
static int IODev_Read(struct IODev *dev, unsigned char *buf, unsigned short len)
{
return platform_io_read(dev, buf, len);
}
static IODev g_tIODevs[3] = {{LED, IODev_Init, IODev_Write, IODev_Read}, \
{KEY, IODev_Init, IODev_Write, IODev_Read}, \
{DBGOUT, IODev_Init, IODev_Write, IODev_Read}};
ptIODev IODev_GetDev(IODevType type)
{
return &g_tIODevs[type];
}
封装平台层IO,要把平台层IO头文件包含进来才能完成调用
- dev_io.h 部分代码
#ifndef __DEV_IO_H
#define __DEV_IO_H
typedef enum{
LED = (0),
KEY = (1),
DBGOUT = (2),
}IODevType;
typedef struct{
unsigned short num; // 按键序号
unsigned short time; // 按键持续时间
}KeyEvent; /* 按键事件 */
typedef struct IODev{
IODevType Type;
void(*Init)(struct IODev *dev);
int(*Write)(struct IODev *dev, unsigned char *buf, unsigned short len);
int(*Read)(struct IODev *dev, unsigned char *buf, unsigned short len);
}IODev, *ptIODev;
ptIODev IODev_GetDev(IODevType type);
#endif /* __DEV_IO_H */
上边平台层源文件我们只写框架,再接下来就是对上边平台层设备的初始化,控制设备
- platform_io.c部分代码
//设备初始化
void platform_io_init(struct IODev *dev)
{
if(dev == NULL) return;
switch(dev->Type)
{
case LED:
{
LED_Init();
break;
}
case KEY:
{
Key_Init();
break;
}
case DBGOUT:
{
UART_Init();
break;
}
default:break;
}
}
//控制设备输出
int platform_io_write(struct IODev *dev, uint8_t *buf, uint16_t len)
{
if(dev == NULL || buf==NULL || len==0) return -1;
return LED_WriteStatus(buf[0]);
}
//控制设备输入
int platform_io_read(struct IODev *dev, uint8_t *buf, uint16_t len)
{
if(dev == NULL || buf==NULL || len==0) return -1;
return Key_Read(buf, len);
}
(2)网络设备层的输入输出源文件和头文件
首先创建网络层输出源文件和头文件dev_net.c/dev_net.h
- dev_net.c 部分代码
static int NetDev_Init(struct NetDev *net)
{
}
static int NetDev_Connect(struct NetDev *net, const char *arg, int timeout)
{
}
static int NetDev_Disconnect(struct NetDev *net, const char *arg, int timeout)
{
}
static int NetDev_Write(struct NetDev *net, char *buf, unsigned short len, int timeout)
{
}
static int NetDev_Read(struct NetDev *net, char *buf, unsigned short len, int timeout)
{
}
//注册一个ESP8266的设备
static NetDev g_tNetDev = {ESP8266, NetDev_Init, NetDev_Connect, NetDev_Disconnect, NetDev_Write, NetDev_Read};
//获取设备
ptNetDev NetDev_GetDev(NetDevType type)
{
if(ESP8266 == type)
return &g_tNetDev;
return NULL;
}
还是和上边一样建立一个网络设备层的架子
- dev_net.h 部分代码
typedef enum{
ESP8266 = (1<<0),
OTHERS = 0xFFFF,
}NetDevType;
typedef enum{
WiFi_ID = (1<<0),
UDP_ID = (1<<1),
TCP_ID = (1<<2)
}ConnectID;
typedef struct{
ConnectID id;
char *ssid;
char *pwd;
}WiFiInfo;
typedef struct{
ConnectID id;
char *IP;
unsigned short LocalPort;
unsigned short RemotePort;
}TCP_UDP_Info;
typedef struct NetDev{
unsigned char Type;
int(*Init)(struct NetDev *net);
int(*Connect)(struct NetDev *net, const char *arg, int timeout);
int(*Disconnect)(struct NetDev *net, const char *arg, int timeout);
int(*Write)(struct NetDev *net, char *buf, unsigned short len, int timeout);
int(*Read)(struct NetDev *net, char *buf, unsigned short len, int timeout);
}NetDev, *ptNetDev;
ptNetDev NetDev_GetDev(NetDevType type);
接下来再建立一个网络平台层源文件头文件platform_net.c/platform_net.h
- platform_net.c 部分代码
int platform_net_init(struct NetDev *net)
{
if(net==NULL) return -1;
return Net_Init();
}
int platform_net_connect(struct NetDev *net, const char *arg, int timeout)
{
int ret = -1;
if(net==NULL || arg==NULL) return -1;
switch(arg[0])
{
case WiFi_ID:
{
WiFiInfo *p = (WiFiInfo *)arg;
ret = Net_ConnectWiFi(p->ssid, p->pwd, timeout);
break;
}
case TCP_ID:
{
TCP_UDP_Info *p = (TCP_UDP_Info*)arg;
ret = Net_ConnectTCP(p->IP, p->RemotePort, 500);
}
default:break;
}
return ret;
}
int platform_net_disconnect(struct NetDev *net, const char *arg, int timeout)
{
int ret = -1;
if(net==NULL || arg==NULL) return -1;
switch(arg[0])
{
case WiFi_ID:
{
ret = Net_DisconnectWiFi();
break;
}
case TCP_ID:
{
ret = Net_Disconnect_TCP_UDP();
}
default:break;
}
return ret;
}
int platform_net_write(struct NetDev *net, char *buf, uint16_t len, int timeout)
{
if(net==NULL || buf==NULL || len==0) return -1;
return Net_TransmitSocket(buf, len, timeout);
}
int platform_net_read(struct NetDev *net, char *buf, uint16_t len, int timeout)
{
if(net==NULL || buf==NULL || len==0) return -1;
return Net_RecvSocket(buf, len, timeout);
}
注意:要根据不同的ID进行连接设备,我们传进来的有可能是WIFI也有可能是TCP
接下来再将网络平台层包含到网络设备层
- dev_net.c 部分代码
static int NetDev_Init(struct NetDev *net)
{
return platform_net_init(net);
}
static int NetDev_Connect(struct NetDev *net, const char *arg, int timeout)
{
return platform_net_connect(net, arg, timeout);
}
static int NetDev_Disconnect(struct NetDev *net, const char *arg, int timeout)
{
return platform_net_disconnect(net, arg, timeout);
}
static int NetDev_Write(struct NetDev *net, char *buf, unsigned short len, int timeout)
{
return platform_net_write(net, buf, len, timeout);
}
static int NetDev_Read(struct NetDev *net, char *buf, unsigned short len, int timeout)
{
return platform_net_read(net, buf, len, timeout);
}
注册一个ESP8266的设备
static NetDev g_tNetDev = {ESP8266, NetDev_Init, NetDev_Connect, NetDev_Disconnect, NetDev_Write, NetDev_Read};
获取设备
ptNetDev NetDev_GetDev(NetDevType type)
{
if(ESP8266 == type)
return &g_tNetDev;
return NULL;
}
这样就将我们的平台层设备层都封装好了,FreeRTOS中间件也弄好了,接下来我们就可以移植MQTT进行跟阿里云服务器的互动啦!
4.协议层MQTT
- MQTT基本原理
-
MQTT的协议中的方法
1.Connect: MQTT客户端与服务器创建连接
2.Disconnect: MQTT客户端完成正在做的工作后断开与服务器的连接
3.Subscribe: MQTT客户端订阅某个topic;
4.UnSubscribe: 客户端取消对某个主题的订阅;
5.Publish:客户端发送某个主题的消息给服务器: -
MQTT的客户端
一个使用MQTT协议的应用程序或者设备,总是建立到服务器的网络连接
发布其它客户端可能会订阅的消息
订阅其它客户端发布的消息
退订或者删除其它客户端的消息
断开和服务器的连接 -
MQTT的服务器
称之为代理“broker”可以是一个应用程序或者一个设备,它是连接消息发布者和订阅者的桥梁
接收来自客户端的网络连接
接收客户端发来的消息
处理来自客户端的订阅和退订请求
向订阅的客户转发应用程序消息 -
MQTT的消息:payload
- Connect的消息:payload
遗嘱topic和遗嘱消息的作用: 当客户端因某种原因导致和客户端断开连接时,服务器将发布一个topic为遗嘱topic的遗嘱消息,其它订阅了这个topic的客户端就会收到这个消息。
- MQTT的协议源码下载
MQTT官方网址: mgtt.org
客户端库(嵌入式应用):Eclipse Paho Embedded C
进入MQTT官网,点击Software,如图所示:
进入Software后,点击Client libraries,如图所示:
然后来到Client libraries之后我们选择C然后选择里边的嵌入式C,如图所示:
进入嵌入式C之后,我们往下滑找到Source,点击Github链接,如图所示:
然后来到git仓库,下载下来,这种下载方式比较方便,如图所示:
下载完之后,接下来我们再创建阿里云服务器产品与设备。
5.创建阿里云产品与设备
进入阿里云官网:aliyun.com
进入官网之后我们点击 产品 ,点击左边的 物联网 ,最后再点击 物联网平台 ,如图所示:
注意:没有阿里云账号的要先注册账号开通了物联网平台后就不能再开通生活物联网平台,如果在想建立只能重新注册阿里云账号。
进入物联网平台之后点击管理控制台,如图所示:
进来之后点击开通 公共示例 ,因为我已经开通过了就不再演示了:
开通之后进入点击设备管理,再点击产品,最后再点击 创建产品 ,如图所示:
创建完产品之后再点击设备,再 添加设备 ,如图所示:
接着创建 Topic 我们再回到产品点击我们创建的产品,再点击
这样就完成了产品,项目,Topic的创建,详细的我们到第6节在介绍;
6.应用层MQTT移植并与阿里云服务器实现通信
- MQTT移植
首先找到我们下载下来的源码,找到MQTTPacket和MQTTClinet-C,如图所示:
接着再将MQTTClinet-C里的src里的MQTTClinet.c/MQTTClinet.h,FreeRTOS里的MQTTFreeRTOS.c/MQTTFreeRTOS.h添加进工程
还有MQTTClinet-C里samples里的FreeROTS里的MQTTEcho.c添加进工程
最后再将MQTTPacket里除了txt文件外的所有文件添加到工程
- 修改 MQTTFreeRTOS.c
注意移植完后要把socket删除替换成我们网络层的设备
- 修改 MQTTFreeRTOS.h
再将MQTTFreeRTOS.下图画出来的两个删除,在将网络设备层dev_net.h头文件包含进来
再添加如下指针设备:
-
修改MQTTFreeRTOS.c
-
修改读函数
更改如下:
int FreeRTOS_read(Network* n, unsigned char* buffer, int len, int timeout_ms)
{
TickType_t xTicksToWait = timeout_ms / portTICK_PERIOD_MS; /* convert milliseconds to ticks */
TimeOut_t xTimeOut;
int recvLen = 0;
vTaskSetTimeOutState(&xTimeOut); /* Record the time at which this function was entered. */
do
{
recvLen = n->netdev->Read(n->netdev, (char*)buffer, len, timeout_ms);
// if(n->netdev->Read(n->netdev, (char*)buffer, len, timeout_ms) == 0) recvLen = len;
} while (recvLen < len && xTaskCheckForTimeOut(&xTimeOut, &xTicksToWait) == pdFALSE);
return recvLen;
}
- 修改写函数
更改如下:
int FreeRTOS_write(Network* n, unsigned char* buffer, int len, int timeout_ms)
{
// TickType_t xTicksToWait = timeout_ms / portTICK_PERIOD_MS; /* convert milliseconds to ticks */
// TimeOut_t xTimeOut;
int sentLen = 0;
// vTaskSetTimeOutState(&xTimeOut); /* Record the time at which this function was entered. */
// do
// {
if(n->netdev->Write(n->netdev, (char*)buffer, len, timeout_ms) == 0) sentLen = len;
// } while (sentLen < len && xTaskCheckForTimeOut(&xTimeOut, &xTicksToWait) == pdFALSE);
return sentLen;
}
接着再将断开连接,初始化,连接所有包含socket的都修改成我们自己的网络的初始化和连接,如下图:
修改如下:
void NetworkInit(Network* n)
{
const static WiFiInfo wifi = {WiFi_ID, "Programmers", "100asktech"};
n->netdev = NetDev_GetDev(ESP8266);
if(n->netdev != NULL)
{
if(n->netdev->Init(n->netdev) == 0)
{
if(n->netdev->Connect(n->netdev, (char*)&wifi, 5000) == 0)
{
printf("Connect WiFi success.\r\n");
n->mqttread = FreeRTOS_read;
n->mqttwrite = FreeRTOS_write;
n->disconnect = FreeRTOS_disconnect;
}
else
{
printf("Connect WiFi failed.\r\n");
}
}
else
{
printf("Net Device Init failed.\r\n");
}
}
else
{
printf("Net Device not found.\r\n");
}
}
int NetworkConnect(Network* n, char* addr, int port)
{
TCP_UDP_Info tcp = {TCP_ID, addr, 0, port};
return n->netdev->Connect(n->netdev, (char*)&tcp, 500);
}
这样我们的MQTT就移植更改完成啦,接下就来实现与阿里云服务器的交互!
- 建立通信
回到阿里云创建设备,输入产品名称,选则自定义品类,选择直连设备,连网方式选择WIFI ,其余默认即可,如图所示:
在添加设备,选择我们创建的产品,添加设备名称,如图所示:
产品名称和设备名称可根据自己喜好随意创建,创建完之后我们点击设备,查看设备信息,把设备信息复制到我们的MQTTEcho.c文件里,如图所示:
接下来我们通过服务器下发控制LED,先来创建Topic,点击Topic列表,选择自定义Topic,再点击自定义Topic类,如图所示:
我们自定义一个getLed获取LED的命令,我们定义一个只可以订阅的Topic, getLed,如图所示:
之后再订阅主题,把我们创建好的复制到MQTTEcho.c里,如图所示:
其中${deviceName}就是我们的MinBoard;
由于我在上边定义了所以替换成了LedTopic:
const static char LedTopic[] = "/k07wnm9qHfS/MiniBoard/user/getLed";
这样我们订阅设备就写好啦,接下来我们下载打开串口助手:
WIFI连接成功,MQTT连接也成功,阿里云服务器也在线啦,如果MQTT返回连接失败,我们把MQTTEcho.c里的buf变大就可以啦。
接下来我们就从服务器下发命令给我们的开发板,点击Topic列表,发布消息,如图所示:
之后我们打开串口工具看一下,我们可以多发几次验证一下,看看我们写的底层函数是否保险,稳定,效果如下:
之后发布也是和订阅一样的操作方法,我就不展示出来啦,这样我们就实现了与阿里云服务器的交互,希望以上的东西对你有所帮助!
代码我已上传Github有需要的可以按需查看!