STM32C8T6+DHT11显示温湿度//通过MQTT实现开发板与阿里云服务器收发(2)

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驱动代码。

  • Driver_net.c 部分代码
//发送指令
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”,如下图所示:

img

然后来到MCU选型界面,在序列号那里输入想要开发的芯片,例如STM32F103C8T6:

img

  • 配置时钟

先配置处理器的时钟,在“System Core”的“RCC”处选择外部高速时钟源和低速时钟源。DshanMCU-F103使用了外部高速时钟源,如下图所示:

img

另外,本实验使用了FreeRTOS,FreeRTOS的时基使用的是Systick,而STM32CubeMX中默认的HAL库时基也是Systick,为了避免可能的冲突,最好将HAL库的时基换做其它的硬件定时器:

img

最后去时钟配置界面配置系统时钟频率。直接在HCLK时钟那里输入MCU允许的最高时钟频率。F103的最高频率是72Mhz,所以直接在那里输入72然后按回车:

img

回车后,STM32CubeMX会自动计算得到各个分频系数和倍频系数:

img

在上图中点击“OK”,就开始自动配置时钟,配置成功后,结果如下图所示:

img

  • 配置GPIO

板载LED的使用的GPIO是PC13,如下图所示:

img

所以在STM32CubeMX的引脚配置界面,找到PC13:

img

在芯片图中,使用鼠标左键点击PC13,会弹出此IO支持的模式:

img

这里选择GPIO Output,让PC13配置为通用输出IO,以便用来驱动LED的亮灭。

  • 配置FreeRTOS

STM32CubeMX已经将FreeRTOS集成到工具中,并且将RTOS的接口进行了封装CMSIS-RTOS V1/V2,相较之于V1版本的CMSIS-RTOS API,V2版本的API的兼容性更高,为了将来的开发和移 植,建议开发者使用V2版本的API:

img

选择CMSIS V2接口后,还要进一步配置FreeRTOS的参数和功能。

  • 配置参数

FreeRTOS的参数包括时基频率、任务堆栈大小、是否使能互斥锁等等,需要开发者根据自己对FreeRTOS的了解以及项目开发的需求,来定制参数。 先如下图进行配置:

img

  • 添加任务

使用STM32CubeMX,可以手工添加任务、队列、信号量、互斥锁、定时器等等。但是本课程不想严重依赖STM32CubeMX,所以不会使用STM32CubeMX来添加这些对象,而是手写代码来使用这些对象。

使用STM32CubeMX时,有一个默认任务,此任务无法删除,只能修改其名称和函数类型,如下图所示:

img

  • 生成Keil MDK的工程

当对外设配置完成后,就去“Project Manager”中设置工程的名称、存储路径和开发IDE:

img

随后去同界面的“Code Generator”设置、生成工程:

image15

可能会有如下提示,选择“Yes”下载所依赖的文件即可:

img

一切正常的话,可以看到如下提示:

img

这样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有需要的可以按需查看!

代码获取:https://github.com/myj020329/MySmartHome

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: 要用STM32C8T6DHT11将温度、湿度显示在OLED屏幕上,我们需要编写一些程序代码。下面是可能的代码示例: ``` #include <DHT11.h> #include <Adafruit_SSD1306.h> #define SCREEN_WIDTH 128 #define SCREEN_HEIGHT 64 Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1); DHT11 dht11(D1); void setup() { display.begin(SSD1306_SWITCHCAPVCC, 0x3C); display.clearDisplay(); display.display(); } void loop() { int chk = dht11.read(DHT11_PIN); if (chk == DHT11_OK) { display.clearDisplay(); display.setTextSize(1); display.setTextColor(WHITE); display.setCursor(10,10); display.print("Temperature: "); display.print(dht11.temperature); display.print("C"); display.setCursor(10,20); display.print("Humidity: "); display.print(dht11.humidity); display.print("%"); display.display(); } delay(2000); } ``` 在这个程序中,我们首先定义了OLED显示器的宽度和高度,然后初始化了显示器。在循环中,我们读取DHT11传感器的温度和湿度值,将它们显示显示器上。显示文本的位置可以调整,以适应您的OLED尺寸。我们还包括一个2秒的延迟,以避免在显示屏上刷新文本太快。 请注意,此代码示例使用了DHT11和OLED库。库应该先下载并在代码中导入,否则编译时会出现错误。 ### 回答2: 首先,需要准备好以下材料: 1. STM32C8T6主控板 2. DHT11温度湿度传感器 3. OLED显示屏 4. 接线材料 在开始编写源代码之前,需要将上述材料进行连接。具体的连接方式如下: DHT11的接线方式如下: 1. VCC接STM32C8T6板的5V电源端口 2. GND接STM32C8T6板的GND电源端口 3. DATA接STM32C8T6的GPIO口 OLED显示屏的接线方式如下: 1. VCC接STM32C8T6的5V电源端口 2. GND接STM32C8T6的GND电源端口 3. SCL接STM32C8T6的GPIO端口 4. SDA接STM32C8T6的GPIO端口 5. RES接STM32C8T6的GPIO端口 6. DC接STM32C8T6的GPIO端口 7. CS接STM32C8T6的GPIO端口 接线完成后,可以开始编写源代码。以下是参考代码,可以根据实际情况进行修改: #include "stm32f10x.h" #include "oled.h" #include "dht11.h" int main(void) { //初始化OLED显示屏 OLED_Init(); //初始化DHT11传感器 DHT11_Init(); //定义变量,保存温度和湿度的值 int temperature, humidity; while(1) { //获取温度和湿度的值 DHT11_Read_Data(&temperature, &humidity); //将获取到的温度和湿度值进行显示 OLED_ShowNum(0, 0, temperature, 2, 16); OLED_ShowString(2 * 8, 0, "℃", 16); OLED_ShowNum(0, 2 * 8, humidity, 2, 16); OLED_ShowString(2 * 8, 2 * 8, "%", 16); //延时一段时间,避免数据频繁更新 delay_ms(2000); } } 在此代码中,首先进行OLED显示屏和DHT11温度湿度传感器的初始化。然后,定义变量保存温度和湿度的值。使用DHT11_Read_Data函数获取温度和湿度的值,并且将获取到的温度和湿度值进行显示。最后,通过delay_ms函数延时一段时间,避免数据频繁更新导致屏幕闪烁。 需要注意的是,以上代码仅为参考,具体情况可能需要根据实际情况进行修改。例如,具体的GPIO口需要根据实际的连接情况进行选择。同时,需要根据实际需求自行调整显示内容的位置、大小等参数。 ### 回答3: STM32C8T6是一款高性能的32位单片机DHT11是一个数字湿度温度传感器,OLED是一种小型的有机发光显示器。将这三个设备结合起来,可以制作一个简单的温湿度显示器。 以下是基于STM32C8T6DHT11和OLED的温湿度显示源代码: 首先,我们需要引用必要的库头文件: #include "stm32f10x.h" #include "dht11.h" #include "oled.h" 然后,我们需要定义一些常量和变量来初始化设备和存储输出结果: #define DHT11_DATA_PIN GPIO_Pin_0 #define DHT11_DATA_PORT GPIOA #define OLED_WIDTH 128 #define OLED_HEIGHT 64 uint8_t humi_int, humi_deci, temp_int, temp_deci; char text[20]; 接下来,我们需要实现初始化函数来初始化STM32C8T6和OLED: void init() { RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB, ENABLE); DHT11_Init(DHT11_DATA_PORT, DHT11_DATA_PIN); OLED_Init(); } 然后,我们可以实现主函数,以读取温湿度传感器并在OLED上显示结果: int main(void) { init(); while (1) { // 读取温湿度 if (DHT11_Read_Pin(DHT11_DATA_PORT, DHT11_DATA_PIN) == 1) { DHT11_Read_Data(&humi_int, &humi_deci, &temp_int, &temp_deci); } // 渲染温湿度到屏幕 sprintf(text, "Temp: %d.%dC", temp_int, temp_deci); OLED_Clear(); OLED_ShowString(0, 0, text, 1); sprintf(text, "Humi: %d.%d%%", humi_int, humi_deci); OLED_ShowString(0, 20, text, 1); OLED_Refresh(); // 等待1s for (volatile int i = 0; i < 1000000; i++); } } 以上就是基于STM32C8T6DHT11和OLED的温湿度显示代码。需要注意的一些细节是: - 在渲染文本字符串之前,我们需要先清除OLED屏幕上的所有内容。 - 在渲染文本字符串之后,我们需要调用OLED_Refresh()函数才能将结果渲染到实际屏幕上。 - 我们使用sprintf()函数来将温湿度转换为文本字符串。这个函数可以将格式化的字符串输出到一个字符数组中。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值