基于 STM32L431 设计的云端绿化管理系统 (ESP8266+ 腾讯物联网云平台)
- 2022 年 8 月 22 日 湖北
本文字数:12877 字
阅读完需:约 42 分钟
一、环境介绍
MCU: 采用意法半导体低功耗芯片 STM32L431RCT6
编译软件: Keil5 + CubeMX
云平台: 采用腾讯物联网云平台
二、功能与硬件介绍
2.1 功能介绍
这是采用 STM32L431 + ES8266 设计的云端绿化管理系统,可以通过 ESP8266 WIFI 连接腾讯云物联网平台,使用微信小程序远程进行绿化管理,比如:实时获取光照强度、温度、湿度、远程控制水泵进行浇水灌溉,在任何地方都可以给自己种的花花草草浇水,了解周边环境情况。
2.2 硬件介绍
开发板采用的是小熊开发板,包括完成绿化管理系统的所有功能都是采用小熊派开发板的配套套件完成。
小熊开发板板载了一个 stlink 调试器(就是 STM32F103C8T6 实现的),程序下载非常方便。串口 1 用来调试打印数据,ESP8266 是接在串口 LPUART1 上的。
小熊派开发板本身自带的例子程序也比较丰富,自带例子里采用的云平台是华为的物联网云平台,工程比较庞大使用了 LiteOS 操作系统。本文里的工程是重新编写的代码,使用裸机完成项目功能,没有跑操作系统,MQTT 协议和 ESP8266 驱动代码都是重新编写,框架、逻辑比较清晰,代码量也较少,适合初学者入门学习。
相关传感器模块型号: (采用的是小熊开发板配套的 E53_IA1 扩展板)
WIFI 采用: ESP8266
温湿度检测传感器采用: SHT30
光照强度检测传感器采用: BH1750
电机采用: 微型直流电机
三、腾讯物联网云平台
下面就截图介绍一下云端绿化管理系统用到的产品功能。
用到的功能属性:
小程序面板配置:
手机微信小程序运行效果:
串口打印的提示:
四、STM32 源代码
STM32 的代码主要分为以下几个部分:
(1) ESP8266 底层驱动代码: 完成 ESP8266 模式配置、数据发送,应答检测等底层网络接口。
(2). MQTT 协议代码:这是参考标准 MQTT 编写 C 语言版本 MQTT 协议框架代码,实现了重要的几个接口(主题订阅、主题发布、心跳包、登录 MQTT 服务器),底层采用 ESP8266 发送数据。 这个 MQTT 协议不是使用 ESP8266 本身的 SDK,是根据 MQTT 协议自己实现的,所以如果使用其他的网卡,移植也很方便,不挑网卡设备。
(3). 传感器初始化代码: 完成温湿度传感器、光照强度传感器的驱动代码编写。
(4). LCD 屏代码: LCD 是 SPI 接口的,可以显示温湿度、光照强度数据。
(5). main 函数: 完成整个逻辑代码编写,检测微信小程序是否有下发的指令,进行分析,完成水泵的开关控制;当温室和湿度到达某个阀值,自动控制水泵浇水,并上报给微信小程序;主程序里 1 秒检测一次温湿度、光照强度、电机状态主动上报给微信小程序;在设备端按下按键(模拟现场实体开关)也可以控制水泵浇水或者关闭,这些状态都会实时上报给云平台,微信小程序。
程序的模板是使用 CubeMX 生成的。
4.1 main.c 代码: 逻辑代码
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
** This notice applies to any and all portions of this file
* that are not between comment pairs USER CODE BEGIN and
* USER CODE END. Other portions of this file, whether
* inserted by the user or by software development tools
* are owned by their respective copyright owners.
*
* COPYRIGHT(c) 2019 STMicroelectronics
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of STMicroelectronics nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
******************************************************************************
*/
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "stm32l4xx_hal.h"
#include "i2c.h"
#include "usart.h"
#include "gpio.h"
#include "E53_IA1.h"
#include "lcd.h"
#include "spi.h"
#include "mqtt.h"
#include "esp8266.h"
/* USER CODE BEGIN Includes */
#include "stdio.h"
/* USER CODE END Includes */
void SystemClock_Config(void);
#define ESP8266_WIFI_AP_SSID "CMCC-Cqvn" //将要连接的路由器名称 --不要出现中文、空格等特殊字符
#define ESP8266_AP_PASSWORD "99pu58cb" //将要连接的路由器密码
//腾讯物联网服务器的设备信息
#define MQTT_ClientID "6142CX41XESmartAgriculture"
#define MQTT_UserName "6142CX41XESmartAgriculture;12010126;HUA2G;1624271589"
#define MQTT_PassWord "a8aadebe9721f70e6f9e14fe56ff1d2b5cac9625fa1f96af2f0e0098597fe78b;hmacsha256"
//订阅与发布的主题
#define SET_TOPIC "$thing/down/property/6142CX41XE/SmartAgriculture" //订阅
#define POST_TOPIC "$thing/up/property/6142CX41XE/SmartAgriculture" //发布
//保存温湿度、光照强度
E53_IA1_Data_TypeDef E53_IA1_Data;
//显示文本
char lcd_text_str[50];
UART_HandleTypeDef at_usart;
//低功耗串口初始化
int32_t at_usart_init(void)
{
at_usart.Instance = LPUART1;
at_usart.Init.BaudRate = 115200;
at_usart.Init.WordLength = UART_WORDLENGTH_8B;
at_usart.Init.StopBits = UART_STOPBITS_1;
at_usart.Init.Parity = UART_PARITY_NONE;
at_usart.Init.HwFlowCtl = UART_HWCONTROL_NONE;
at_usart.Init.Mode = UART_MODE_RX | UART_MODE_TX;
if(HAL_UART_Init(&at_usart) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
// __HAL_UART_CLEAR_FLAG(usart, UART_FLAG_TC);
__HAL_UART_ENABLE_IT(&at_usart, UART_IT_IDLE);
__HAL_UART_ENABLE_IT(&at_usart, UART_IT_RXNE);
HAL_NVIC_EnableIRQ(LPUART1_IRQn); //使能USART1中断通道
HAL_NVIC_SetPriority(LPUART1_IRQn, 3, 3); //抢占优先级3,子优先级3
return 0;
}
unsigned char ESP8266_RecvBuf[MAX_RECV_CNT];
unsigned int ESP8266_Recv_cnt=0;
unsigned int ESP8266_Recv_flag=0;
void LPUART1_IRQHandler()
{
//接收到数据
if(__HAL_UART_GET_FLAG(&at_usart, UART_FLAG_RXNE) != RESET)
{
if(ESP8266_Recv_cnt<MAX_RECV_CNT-1)
{
ESP8266_RecvBuf[ESP8266_Recv_cnt++] = (uint8_t)(at_usart.Instance->RDR & 0x00FF);
}
else
{
ESP8266_Recv_flag=1;
}
}
else if (__HAL_UART_GET_FLAG(&at_usart, UART_FLAG_IDLE) != RESET)
{
__HAL_UART_CLEAR_IDLEFLAG(&at_usart);
ESP8266_Recv_flag=1;
}
}
void AT_SendData(unsigned char *p,unsigned int len)
{
int i=0;
for(i=0;i<len;i++)
{
while((LPUART1->ISR & 0X40) == 0); //循环发送,直到发送完毕
LPUART1->TDR = p[i];
}
}
char mqtt_message[200];
int main(void)
{
int i=0;
int cnt=0;
int motor_state=0;
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_I2C1_Init();
MX_SPI2_Init();
MX_USART1_UART_Init();
at_usart_init();
//初始化硬件 STM32L431RC_BearPiBH1750_I2C1\STM32L431RC_BearPiBH1750_I2C1.axf: Error: L6218E: Undefined symbol printf (referred from main.o).
Init_E53_IA1();
LCD_Init();
LCD_Clear(BLACK);//清屏为黑色
LCD_ShowString(0, 00, 240, 32, 32, "Init ESP8266");//显示字符串,字体大小32*32
if(ESP8266_Init())
{
printf("ESP8266硬件检测错误.\n");
LCD_Clear(BLACK);//清屏为黑色
LCD_ShowString(0, 00, 240, 32, 32, "ESP8266 ERROR");//显示字符串,字体大小32*32
}
else
{
LCD_Clear(BLACK);//清屏为黑色
LCD_ShowString(0, 00, 240, 32, 32, "ESP8266 OK");//显示字符串,字体大小32*32
printf("准备连接到指定的服务器.\n");
//非加密端口
printf("WIFI:%d\r\n",ESP8266_STA_TCP_Client_Mode(ESP8266_WIFI_AP_SSID,ESP8266_AP_PASSWORD,"106.55.124.154",1883,1));
}
//2. MQTT协议初始化
MQTT_Init();
//3. 连接腾讯云IOT服务器
while(MQTT_Connect(MQTT_ClientID,MQTT_UserName,MQTT_PassWord))
{
printf("服务器连接失败,正在重试...\n");
HAL_Delay(500);
}
printf("服务器连接成功.\n");
//3. 订阅主题
if(MQTT_SubscribeTopic(SET_TOPIC,0,1))
{
printf("主题订阅失败.\n");
}
else
{
printf("主题订阅成功.\n");
}
while (1)
{
if(HAL_GPIO_ReadPin(KEY1_GPIO_Port,KEY1_Pin)==GPIO_PIN_RESET)//查询按键KEY1低电平
{
HAL_Delay(10);//消抖
if(HAL_GPIO_ReadPin(KEY1_GPIO_Port,KEY1_Pin)==GPIO_PIN_RESET)//查询按键KEY1低电平
{
HAL_GPIO_WritePin(LED_GPIO_Port,LED_Pin,GPIO_PIN_SET);//亮
//补光灯亮
HAL_GPIO_WritePin(IA1_Light_GPIO_Port, IA1_Light_Pin, GPIO_PIN_SET);
//电机转
HAL_GPIO_WritePin(IA1_Motor_GPIO_Port, IA1_Motor_Pin, GPIO_PIN_SET);
motor_state=1;
}
}
if(HAL_GPIO_ReadPin(KEY2_GPIO_Port,KEY2_Pin)==GPIO_PIN_RESET)//查询按键KEY2低电平
{
HAL_Delay(10);//消抖
if(HAL_GPIO_ReadPin(KEY2_GPIO_Port,KEY2_Pin)==GPIO_PIN_RESET)//查询按键KEY2低电平
{
HAL_GPIO_WritePin(LED_GPIO_Port,LED_Pin,GPIO_PIN_RESET);//灭
//补光灯灭
HAL_GPIO_WritePin(IA1_Light_GPIO_Port, IA1_Light_Pin, GPIO_PIN_RESET);
//电机停
HAL_GPIO_WritePin(IA1_Motor_GPIO_Port, IA1_Motor_Pin, GPIO_PIN_RESET);
motor_state=0;
}
}
cnt++;
HAL_Delay(10);
if(cnt>=100)
{
cnt=0;
E53_IA1_Read_Data();
printf("光照强度:%d %%\r\n", (int)E53_IA1_Data.Lux);
printf("湿度:%d %%\r\n",(int)E53_IA1_Data.Humidity);
printf("温度:%d ℃\r\n", (int)E53_IA1_Data.Temperature);
sprintf(lcd_text_str,"L: %d %%",(int)E53_IA1_Data.Lux);
LCD_ShowString(40, 50+10+32*1, 240, 32, 32,lcd_text_str);
sprintf(lcd_text_str,"H: %d %%",(int)E53_IA1_Data.Humidity);
LCD_ShowString(40, 50+10+32*2, 240, 32, 32,lcd_text_str);
sprintf(lcd_text_str,"T: %d C",(int)E53_IA1_Data.Temperature);
LCD_ShowString(40, 50+10+32*3, 240, 32, 32,lcd_text_str);
//切换引脚的状态
HAL_GPIO_TogglePin(LED_GPIO_Port,LED_Pin);
//上传数据
sprintf(mqtt_message,"{"method":"report","clientToken":"123","params":{"temperature":%d,"humidity":%d,"machine":%d,"illumination":%d}}",
(int)E53_IA1_Data.Temperature,(int)E53_IA1_Data.Humidity,motor_state,(int)E53_IA1_Data.Lux);
MQTT_PublishData(POST_TOPIC,mqtt_message,0);
//根据湿度自动灌溉
if((int)E53_IA1_Data.Humidity<50) //小于50自动灌溉
{
printf("自动灌溉....\n");
motor_state=1; //电机状态更新
//电机转
HAL_GPIO_WritePin(IA1_Motor_GPIO_Port, IA1_Motor_Pin, GPIO_PIN_SET);
}
}
//接收到数据
if(ESP8266_Recv_flag)
{
//如果是下发了属性,判断是开锁还是关锁
if(ESP8266_Recv_cnt>5)
{
ESP8266_RecvBuf[ESP8266_Recv_cnt]='\0';
//使用字符串查找函数
if(strstr((char*)&ESP8266_RecvBuf[5],""machine":1"))
{
motor_state=1; //电机状态更新
//电机转
HAL_GPIO_WritePin(IA1_Motor_GPIO_Port, IA1_Motor_Pin, GPIO_PIN_SET);
printf("开启电机...\n");
}
else if(strstr((char*)&ESP8266_RecvBuf[5],""machine":0"))
{
//电机停
HAL_GPIO_WritePin(IA1_Motor_GPIO_Port, IA1_Motor_Pin, GPIO_PIN_RESET);
motor_state=0;
printf("关闭电机...\n");
}
for(i=0;i<ESP8266_Recv_cnt;i++)printf("%c",ESP8266_RecvBuf[i]);
ESP8266_Recv_cnt=0;
}
ESP8266_Recv_flag=0;
}
}
}
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct;
RCC_ClkInitTypeDef RCC_ClkInitStruct;
RCC_PeriphCLKInitTypeDef PeriphClkInit;
/**Initializes the CPU, AHB and APB busses clocks
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI|RCC_OSCILLATORTYPE_MSI;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.HSICalibrationValue = 16;
RCC_OscInitStruct.MSIState = RCC_MSI_ON;
RCC_OscInitStruct.MSICalibrationValue = 0;
RCC_OscInitStruct.MSIClockRange = RCC_MSIRANGE_6;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_MSI;
RCC_OscInitStruct.PLL.PLLM = 1;
RCC_OscInitStruct.PLL.PLLN = 40;
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV7;
RCC_OscInitStruct.PLL.PLLQ = RCC_PLLQ_DIV2;
RCC_OscInitStruct.PLL.PLLR = RCC_PLLR_DIV2;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
/**Initializes the CPU, AHB and APB busses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_USART1|RCC_PERIPHCLK_I2C1;
PeriphClkInit.Usart1ClockSelection = RCC_USART1CLKSOURCE_PCLK2;
PeriphClkInit.I2c1ClockSelection = RCC_I2C1CLKSOURCE_HSI;
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
/**Configure the main internal regulator output voltage
*/
if (HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
/**Configure the Systick interrupt time
*/
HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000);
/**Configure the Systick
*/
HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);
/* SysTick_IRQn interrupt configuration */
HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0);
}
/* USER CODE BEGIN 4 */
/* USER CODE END 4 */
/**
* @brief This function is executed in case of error occurrence.
* @param file: The file name as string.
* @param line: The line in file as a number.
* @retval None
*/
void _Error_Handler(char *file, int line)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
while(1)
{
}
/* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t* file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
tex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
/**
* @}
*/
/**
* @}
*/
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
4.2 mqtt.c : MQTT 协议代码
#include "mqtt.h"
u8 *mqtt_rxbuf;
u8 *mqtt_txbuf;
u16 mqtt_rxlen;
u16 mqtt_txlen;
u8 _mqtt_txbuf[256];//发送数据缓存区
u8 _mqtt_rxbuf[256];//接收数据缓存区
typedef enum
{
//名字 值 报文流动方向 描述
M_RESERVED1 =0 , // 禁止 保留
M_CONNECT , // 客户端到服务端 客户端请求连接服务端
M_CONNACK , // 服务端到客户端 连接报文确认
M_PUBLISH , // 两个方向都允许 发布消息
M_PUBACK , // 两个方向都允许 QoS 1消息发布收到确认
M_PUBREC , // 两个方向都允许 发布收到(保证交付第一步)
M_PUBREL , // 两个方向都允许 发布释放(保证交付第二步)
M_PUBCOMP , // 两个方向都允许 QoS 2消息发布完成(保证交互第三步)
M_SUBSCRIBE , // 客户端到服务端 客户端订阅请求
M_SUBACK , // 服务端到客户端 订阅请求报文确认
M_UNSUBSCRIBE , // 客户端到服务端 客户端取消订阅请求
M_UNSUBACK , // 服务端到客户端 取消订阅报文确认
M_PINGREQ , // 客户端到服务端 心跳请求
M_PINGRESP , // 服务端到客户端 心跳响应
M_DISCONNECT , // 客户端到服务端 客户端断开连接
M_RESERVED2 , // 禁止 保留
}_typdef_mqtt_message;
//连接成功服务器回应 20 02 00 00
//客户端主动断开连接 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)
{
//缓冲区赋值
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);
// //无条件先主动断开
// MQTT_Disconnect();
// HAL_Delay(100);
// MQTT_Disconnect();
// HAL_Delay(100);
}
/*
函数功能: 登录服务器
函数返回值: 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;
//可变报头+Payload 每个字段包含两个字节的长度标识
DataLen = 10 + (ClientIDLen+2) + (UsernameLen+2) + (PasswordLen+2);
//固定报头
//控制报文类型
mqtt_txbuf[mqtt_txlen++] = 0x10; //MQTT Message Type CONNECT
//剩余长度(不包括固定头部)
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 );
//可变报头
//协议名
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
//协议级别
mqtt_txbuf[mqtt_txlen++] = 4; // MQTT Protocol version = 4 对于 3.1.1 版协议,协议级别字段的值是 4(0x04)
//连接标志
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心跳包 保活时间
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);
ESP8266_Recv_flag=0;
ESP8266_Recv_cnt=0;
MQTT_SendBuf(mqtt_txbuf,mqtt_txlen);
HAL_Delay(200);
memcpy((char *)mqtt_rxbuf,ESP8266_RecvBuf,ESP8266_Recv_cnt);
for(i=0;i<ESP8266_Recv_cnt;i++)printf("%#x ",ESP8266_RecvBuf[i]);
//CONNECT
if(mqtt_rxbuf[0]==parket_connetAck[0] && mqtt_rxbuf[1]==parket_connetAck[1]) //连接成功
{
return 0;//连接成功
}
return 1;
}
/*
函数功能: MQTT订阅/取消订阅数据打包函数
函数参数:
topic 主题
qos 消息等级 0:最多分发一次 1: 至少分发一次 2: 仅分发一次
whether 订阅/取消订阅请求包 (1表示订阅,0表示取消订阅)
返回值: 0表示成功 1表示失败
*/
u8 MQTT_SubscribeTopic(char *topic,u8 qos,u8 whether)
{
u8 i,j;
mqtt_txlen=0;
int topiclen = strlen(topic);
int DataLen = 2 + (topiclen+2) + (whether?1:0);//可变报头的长度(2字节)加上有效载荷的长度
//固定报头
//控制报文类型
if(whether)mqtt_txbuf[mqtt_txlen++] = 0x82; //消息类型和标志订阅
else mqtt_txbuf[mqtt_txlen++] = 0xA2; //取消订阅
//剩余长度
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 );
//可变报头
mqtt_txbuf[mqtt_txlen++] = 0; //消息标识符 MSB
mqtt_txbuf[mqtt_txlen++] = 0x0A; //消息标识符 LSB
//有效载荷
mqtt_txbuf[mqtt_txlen++] = BYTE1(topiclen);//主题长度 MSB
mqtt_txbuf[mqtt_txlen++] = BYTE0(topiclen);//主题长度 LSB
memcpy(&mqtt_txbuf[mqtt_txlen],topic,topiclen);
mqtt_txlen += topiclen;
if(whether)
{
mqtt_txbuf[mqtt_txlen++] = qos;//QoS级别
}
ESP8266_Recv_flag=0;
ESP8266_Recv_cnt=0;
MQTT_SendBuf(mqtt_txbuf,mqtt_txlen);
HAL_Delay(200);
memcpy((char *)mqtt_rxbuf,ESP8266_RecvBuf,ESP8266_Recv_cnt);
for(i=0;i<ESP8266_Recv_cnt;i++)printf("%#x ",ESP8266_RecvBuf[i]);
if(mqtt_rxbuf[0]==parket_subAck[0] && mqtt_rxbuf[1]==parket_subAck[1]) //订阅成功
{
return 0;//订阅成功
}
return 1; //失败
}
//MQTT发布数据打包函数
//topic 主题
//message 消息
//qos 消息等级
u8 MQTT_PublishData(char *topic, char *message, u8 qos)
{
int topicLength = strlen(topic);
int messageLength = strlen(message);
static u16 id=0;
int DataLen;
mqtt_txlen=0;
//有效载荷的长度这样计算:用固定报头中的剩余长度字段的值减去可变报头的长度
//QOS为0时没有标识符
//数据长度 主题名 报文标识符 有效载荷
if(qos) DataLen = (2+topicLength) + 2 + messageLength;
else DataLen = (2+topicLength) + messageLength;
//固定报头
//控制报文类型
mqtt_txbuf[mqtt_txlen++] = 0x30; // MQTT Message Type PUBLISH
//剩余长度
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 );
mqtt_txbuf[mqtt_txlen++] = BYTE1(topicLength);//主题长度MSB
mqtt_txbuf[mqtt_txlen++] = BYTE0(topicLength);//主题长度LSB
memcpy(&mqtt_txbuf[mqtt_txlen],topic,topicLength);//拷贝主题
mqtt_txlen += topicLength;
//报文标识符
if(qos)
{
mqtt_txbuf[mqtt_txlen++] = BYTE1(id);
mqtt_txbuf[mqtt_txlen++] = BYTE0(id);
id++;
}
memcpy(&mqtt_txbuf[mqtt_txlen],message,messageLength);
mqtt_txlen += messageLength;
MQTT_SendBuf(mqtt_txbuf,mqtt_txlen);
return mqtt_txlen;
}
void MQTT_SentHeart(void)
{
MQTT_SendBuf((u8 *)parket_heart,sizeof(parket_heart));
}
void MQTT_Disconnect(void)
{
MQTT_SendBuf((u8 *)parket_disconnet,sizeof(parket_disconnet));
}
void MQTT_SendBuf(u8 *buf,u16 len)
{
AT_SendData(buf,len);
}
版权声明: 本文为 InfoQ 作者【DS小龙哥】的原创文章。
原文链接:【http://xie.infoq.cn/article/5f60baad29dee0aa5eec88447】。文章转载请联系作者。
DS小龙哥
之所以觉得累,是因为说的比做的多。 2022.01.06 加入
熟悉C/C++、51单片机、STM32、Linux应用开发、Linux驱动开发、音视频开发、QT开发. 目前已经完成的项目涉及音视频、物联网、智能家居、工业控制领域
评论