前言

凡心所向,素履所往;生如逆旅,一苇以航。


一、ESP8266 介绍

  • ESP8266是一款超低功耗的UART-WiFi 透传模块,拥有业内极富竞争力的封装尺寸和超低能耗技术,专为移动设备和物联网应用设计,可将用户的物理设备连接到Wi-Fi 无线网络上,进行互联网或局域网通信,实现联网功能。
  • 硬件接口丰富,可支持 UART,IIC,PWM,GPIO,ADC 等,适用于各种物联网应用场合。
  • 如下我们使用的 USART 串口接口的 ESP8266 模块:

二、接线与引脚说明

  • 开发板STM32F103RBT6(正点原子的NANO开发板)
  • WiFi模块ESP8266MOD 型号 (如上图所示)

  • 接线图:
STM32开发板 ESP8266 模块 功能
VCC(3.3V) VCC 3.3V,模块供电
VCC(3.3V) CH_PD 模块使能端,高电平工作
VCC(3.3V) GPIO0 上拉(Flash Boot)工作模式,可接VCC,也可不接VCC,默认悬空
GND GND 接地
USART2_TX(PA2 RXD 串口数据通信
USART2_RX(PA3 TXD 串口数据通信
USART1_TX(PA9) - 串口通信,发送端
USART1_RX(PA10) - 串口通信,接收端
  • 主要的引脚介绍:

其他引脚默认悬空即可。

ESP8266引脚 功能
VCC 3.3V,模块供电
GND 接地
TXD 发送
RXD 接收
GPIO0 默认 WiFi 状态,1)上拉:Flash Boot,工作模式;2)下拉:UART Download,下载模式
CH_PD 使能端,高电平工作,低电平模块供电关闭


三、WiFi模块的使用

ESP8266 的一般使用顺序

  • 此时指的是ESP8266 连接当前环境中的 WiFi 热点,然后与服务器建立 TCP 连接,进行数据的传输。
  • 这里以连接 ONENETMQTT 协议为例。
也就是ESP 设备 Wi-Fi 的三种模式当中的:STA模式。
  1. AT:测试启动 ESP 设备,响应返回:OK
  2. AT+CWMODE=1:设置 WiFi 模式(STA 模式),响应返回:OK
  3. AT+CWDHCP=1,1:启用 SoftAPDHCP,响应返回:OK(本设置命令与设置静态 IP 地址的命令会相互影响,如 AT+CIPSTAAT+CIPAP
    • 若启用 DHCP,则 静态 IP 地址 会被禁用
    • 若启用 静态 IP,则 DHCP 会被禁用
    • 最后一次配置会覆盖上一次配置
      示例:
// 启用 Station DHCP,如果原 DHCP mode 为 2,则现 DHCP mode 为 3
AT+CWDHCP=1,1// 禁用 SoftAP DHCP,如果原 DHCP mode 为 3,则现 DHCP mode 为 1
AT+CWDHCP=0,2
  1. AT+CWJAP="WIFI 热点名称","密码":连接当前环境中的WiFi热点
  2. AT+CIPSTART="TCP","xxx.xxx.xxx.xxx",xxxx:建立 TCP 连接
- type:字符串参数,表示网络连接类型,"TCP""TCPv6",默认值:"TCP"
- remote host:字符串参数,表示远端 IPv4 地址、IPv6 地址,或域名
- remote port:远端端口值
- local IP:连接绑定的本机 IPv4 地址或 IPv6 地址,该参数在本地多网络接口时和本地多 IP 地址时非常有用。默认为禁用,如果您想使用,需自行设置,空值也为有效值
AT+CIPSTART=<"type">,<"remote host">,<remote port>[,<keep alive>][,<"local IP">]

完成以上步骤,即可差不多完成了 ESP8266 的初始化。


四、新建工程

1.打开STM32CubeMX软件,点击“新建工程”

2. 选择 MCU 和封装

3.配置时钟



具体学习可以参考:博客网站-RCC学习

4.配置调试模式

5.串口(USART1)配置

6.串口(USART2)配置


7.生成代码

输入项目名称和路径。(注:路径中不允许出现中文。)

选择应用的IDE,开发环境MDK-ARM V5

每个外设生成独立的 ’.c/.h’ 文件

  • 不勾: 所有初始化代码都生成在 main.c
  • 勾选: 初始化代码生成在对应的外设文件。 如 GPIO 初始化代码生成在 gpio.c 中。

    点击 GENERATE CODE 生成代码

8.构建工程

  • DeBug的模式根据不同的芯片进行选择:

四、编写代码

main.c文件中,添加一下代码:

  • 重写fgetfput函数:勾选微库(这个很重要),添加头文件<stdio.h>

代码移植:

main.c:

/* USER CODE BEGIN Header */
/********************************************************************************* @file           : main.c* @brief          : Main program body******************************************************************************* @attention** <h2><center>&copy; Copyright (c) 2021 STMicroelectronics.* All rights reserved.</center></h2>** This software component is licensed by ST under BSD 3-Clause license,* the "License"; You may not use this file except in compliance with the* License. You may obtain a copy of the License at:*                        opensource.org/licenses/BSD-3-Clause********************************************************************************/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "usart.h"
#include "gpio.h"/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "esp8266.h"
#include "onenet.h"
/* USER CODE END Includes *//* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
extern unsigned char a_esp8266_buf;
/* USER CODE END PTD *//* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD *//* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM *//* USER CODE END PM *//* Private variables ---------------------------------------------------------*//* USER CODE BEGIN PV *//* USER CODE END PV *//* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP *//* USER CODE END PFP *//* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 *//* USER CODE END 0 *//*** @brief  The application entry point.* @retval int*/
int main(void)
{/* USER CODE BEGIN 1 */
unsigned char *dataPtr = NULL;/* USER CODE END 1 *//* MCU Configuration--------------------------------------------------------*//* Reset of all peripherals, Initializes the Flash interface and the Systick. */HAL_Init();/* USER CODE BEGIN Init *//* USER CODE END Init *//* Configure the system clock */SystemClock_Config();/* USER CODE BEGIN SysInit *//* USER CODE END SysInit *//* Initialize all configured peripherals */MX_GPIO_Init();MX_USART1_UART_Init();MX_USART2_UART_Init();/* USER CODE BEGIN 2 */printf("This is a mqtt text \r\n");HAL_UART_Receive_IT(&huart2, (uint8_t *)&a_esp8266_buf, 1);ESP8266_Init();while(OneNet_DevLink())         //接入OneNETHAL_Delay(500);printf("接入onenet成功");OneNet_SendData();//发送数据给onenet//               dataPtr = ESP8266_GetIPD(0);//获取平台返回的数据
//      if(dataPtr != NULL)//如果返回数据不为空
//          OneNet_RevPro(dataPtr);//平台返回数据检测
//      printf("%s",dataPtr);/* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */while (1){dataPtr = ESP8266_GetIPD(0);//获取平台返回的数据if(dataPtr != NULL)//如果返回数据不为空OneNet_RevPro(dataPtr);//平台返回数据检测/* USER CODE END WHILE *//* USER CODE BEGIN 3 */}/* USER CODE END 3 */
}/*** @brief System Clock Configuration* @retval None*/
void SystemClock_Config(void)
{RCC_OscInitTypeDef RCC_OscInitStruct = {0};RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};/** Initializes the RCC Oscillators according to the specified parameters* in the RCC_OscInitTypeDef structure.*/RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;RCC_OscInitStruct.HSEState = RCC_HSE_ON;RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;RCC_OscInitStruct.HSIState = RCC_HSI_ON;RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK){Error_Handler();}/** Initializes the CPU, AHB and APB buses 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_DIV2;RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK){Error_Handler();}
}/* USER CODE BEGIN 4 *//* USER CODE END 4 *//*** @brief  This function is executed in case of error occurrence.* @retval None*/
void Error_Handler(void)
{/* USER CODE BEGIN Error_Handler_Debug *//* User can add his own implementation to report the HAL error return state */__disable_irq();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,ex: 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****/

ESP8266.c

#include "esp8266.h"/*** @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. 设置WiFi模式(CWMODE)\r\n");        //查询/设置 Wi-Fi 模式:设置WiFi模式为Station模式while(ESP8266_SendCmd("AT+CWMODE=1\r\n", "OK"))HAL_Delay(500);printf("3. AT+CWDHCP\r\n");     //启用/禁用 DHCPwhile(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));    //将数组中的元素全部初始化为0,
}    /*** @brief  等待接收完成* @param  无* @retval OK:表示接收完成;OUTTIME:表示接收超时完成*         进行循环调用,检测接收是否完成*/
_Bool ESP8266_WaitRecive(void)
{if(esp8266_cnt == 0)                             //如果当前接收计数为0 则说明没有处于接收数据中,所以直接跳出,结束函数return OUTTIME;if(esp8266_cnt == esp8266_cntPre)               //如果上一次的值和这次相同,则说明接收完毕{esp8266_cnt = 0;                         //清0接收计数return OK;                                  //返回接收完成标志}else                                            //如果不相同,则将此次赋值给上一次,并返回接收未完成标志{        esp8266_cntPre = esp8266_cnt;               return OUTTIME;                             }
}/*** @brief 发送命令* @param cmd:表示命令;res:需要检查的返回指令* @retval 0:表示成功;1:表示失败*/
_Bool ESP8266_SendCmd(char *cmd, char *res)
{unsigned char timeOut = 200;HAL_UART_Transmit(&huart2, (unsigned char *)cmd, strlen((const char *)cmd),0xffff);while(timeOut--){if(ESP8266_WaitRecive() == OK)                          //如果收到数据{printf("%s",ESP8266_Buf);if(strstr((const char *)ESP8266_Buf, res) != NULL)     //如果检索到关键词,清空缓存{ESP8266_Clear();                            return 0;}}HAL_Delay(10);}return 1;
}/*** @brief 数据发送* @param data:待发送的数据;len:待发送的数据长度* @retval 无*/
void ESP8266_SendData(unsigned char *data, unsigned short len)
{char cmdBuf[32];ESP8266_Clear();                               //清空接收缓存sprintf(cmdBuf, "AT+CIPSEND=%d\r\n", len);      //发送命令,sprintf()函数用于将格式化的数据写入字符串if(!ESP8266_SendCmd(cmdBuf, ">"))              //收到‘>’时可以发送数据{HAL_UART_Transmit(&huart2, data, len,0xffff);     //发送设备连接请求数据}
}/*** @brief 获取平台返回的数据* @param 等待的时间* @retval 平台返回的数据,不同网络设备返回的格式不同,需要进行调试,如:ESP8266的返回格式为:"+IPD,x:yyy",x表示数据长度,yyy表示数据内容*/
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头的延迟,还是需要等待一会,但不会超过设定的时间{//UsartPrintf(USART_DEBUG, "\"IPD\" not found\r\n");}else{ptrIPD = strchr(ptrIPD, ':');                          //找到':'if(ptrIPD != NULL){ptrIPD++;return (unsigned char *)(ptrIPD);}elsereturn NULL;}}HAL_Delay(5);                                                   //延时等待} while(timeOut--);return NULL;       //超时还未找到,返回空指针
}/*** @brief 串口2收发中断回调函数* @param* @retval*/
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{if(esp8266_cnt >= 255)  //溢出判断,超过一个字节{esp8266_cnt = 0;memset(ESP8266_Buf,0x00,sizeof(ESP8266_Buf));HAL_UART_Transmit(&huart2, (uint8_t *)"数据溢出", 10,0xFFFF);     }else{ESP8266_Buf[esp8266_cnt++] = a_esp8266_buf;   //接收数据转存}HAL_UART_Receive_IT(&huart2, (uint8_t *)&a_esp8266_buf, 1);   //再开启接收中断
}

ESP8266.h

#ifndef _ESP8266_H_
#define _ESP8266_H_#include "main.h"
#include "usart.h"
#include<string.h>
#include<stdio.h>
#include<stdbool.h>#define     ESP8266_WIFI_INFO      "AT+CWJAP=\"albbz\",\"2398953754\"\r\n"          //连接上自己的wifi热点:WiFi名和密码
#define     ESP8266_ONENET_INFO     "AT+CIPSTART=\"TCP\",\"183.230.40.39\",6002\r\n" //连接上OneNet的MQTT#define     OK             0       //接收完成标志
#define     OUTTIME         1       //接收未完成标志unsigned char ESP8266_Buf[128];                         //定义一个数组作为esp8266的数据缓冲区
unsigned short esp8266_cnt = 0, esp8266_cntPre = 0;     //定义两个计数值:此次和上一次
unsigned char a_esp8266_buf;void ESP8266_Clear(void);           //清空缓存void ESP8266_Init(void);            //esp8266初始化_Bool ESP8266_SendCmd(char *cmd, char *res);//发送数据unsigned char *ESP8266_GetIPD(unsigned short timeOut);#endif
在使用连接onenet平台时,我们使用的 mqtt 协议的 IP 和端口号,但在代码中写的是TCP协议。


ONENET.c

在onenet上在控制台中,点击多协议接入,添加产品,得到产品ID,鉴权信息和设备ID,连接到wifi模块上。

#include "onenet.h"
#include <string.h>
#include <stdio.h>#define PROID       "506530"          //产品ID
#define AUTH_INFO   "wzyyds20201012wd"    //鉴权信息
#define DEVID       "929236606"           //设备IDextern unsigned char esp8266_buf[128];float sht20_info_tempreture = 12;
float sht20_info_humidity = 15;
//==========================================================
//  函数名称:    OneNet_DevLink
//
//  函数功能:    与onenet创建连接
//
//  入口参数:    无
//
//  返回参数:    1-成功    0-失败
//
//  说明:      与onenet平台建立连接
//==========================================================
_Bool OneNet_DevLink(void)
{MQTT_PACKET_STRUCTURE mqttPacket = {NULL, 0, 0, 0};                   //协议包,协议类型初始化unsigned char *dataPtr;_Bool status = 1;printf("OneNet_DevLink\r\n""PROID: %s,  AUIF: %s,   DEVID:%s\r\n", PROID, AUTH_INFO, DEVID);if(MQTT_PacketConnect(PROID, AUTH_INFO, DEVID, 256, 0, MQTT_QOS_LEVEL0, NULL, NULL, 0, &mqttPacket) == 0){ESP8266_SendData(mqttPacket._data, mqttPacket._len);           //上传平台dataPtr = ESP8266_GetIPD(250);                                   //等待平台响应if(dataPtr != NULL){if(MQTT_UnPacketRecv(dataPtr) == MQTT_PKT_CONNACK){switch(MQTT_UnPacketConnectAck(dataPtr)){case 0:printf("Tips:    连接成功\r\n");status = 0;break;case 1:printf("WARN: 连接失败:协议错误\r\n");break;case 2:printf("WARN: 连接失败:非法的clientid\r\n");break;case 3:printf("WARN:  连接失败:服务器失败\r\n");break;case 4:printf("WARN:    连接失败:用户名或密码错误\r\n");break;case 5:printf("WARN: 连接失败:非法链接(比如token非法)\r\n");break;default:printf("ERR:  连接失败:未知错误\r\n");break;}}}MQTT_DeleteBuffer(&mqttPacket);                                //删包}elseprintf("WARN: MQTT_PacketConnect Failed\r\n");return status;}unsigned char OneNet_FillBuf(char *buf)
{char text[32];uint16_t LED0_FLAG = HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_0);memset(text, 0, sizeof(text));strcpy(buf, ",;");memset(text, 0, sizeof(text));sprintf(text, "Tempreture,%f;", sht20_info_tempreture);strcat(buf, text);memset(text, 0, sizeof(text));sprintf(text, "Humidity,%f;", sht20_info_humidity);strcat(buf, text);// memset(text, 0, sizeof(text));
//  sprintf(text, "key,%d;", LED0_FLAG);
//  strcat(buf, text);//memset(text, 0, sizeof(text));
//sprintf(text, "LED1,%d;", LED1_FLAG);
//strcat(buf, text);
//printf("buf_mqtt=%s\r\n",buf);return strlen(buf);}//==========================================================
//  函数名称:    OneNet_SendData
//
//  函数功能:    上传数据到平台
//
//  入口参数:    type:发送数据的格式
//
//  返回参数:    无
//
//  说明:
//==========================================================
void OneNet_SendData(void)
{MQTT_PACKET_STRUCTURE mqttPacket = {NULL, 0, 0, 0};                                               //协议包char buf[128];short body_len = 0, i = 0;printf("Tips:   OneNet_SendData-MQTT\r\n");memset(buf, 0, sizeof(buf));body_len = OneNet_FillBuf(buf);                                                                    if(body_len){if(MQTT_PacketSaveData(DEVID, body_len, NULL, 5, &mqttPacket) == 0)                          //封包{for(; i < body_len; i++)mqttPacket._data[mqttPacket._len++] = buf[i];ESP8266_SendData(mqttPacket._data, mqttPacket._len);                                  printf("Send %d Bytes\r\n", mqttPacket._len);MQTT_DeleteBuffer(&mqttPacket);                                                          //删包}elseprintf("WARN: EDP_NewBuffer Failed\r\n");}}//==========================================================
//  函数名称:    OneNet_RevPro
//
//  函数功能:    平台返回数据检测
//
//  入口参数:    dataPtr:平台返回的数据
//
//  返回参数:    无
//
//  说明:
//==========================================================
void OneNet_RevPro(unsigned char *cmd)
{MQTT_PACKET_STRUCTURE mqttPacket = {NULL, 0, 0, 0};                               //协议包char *req_payload = NULL;char *cmdid_topic = NULL;unsigned short req_len = 0;unsigned char type = 0;short result = 0;char *dataPtr = NULL;char numBuf[10];int num = 0;type = MQTT_UnPacketRecv(cmd);switch(type){case MQTT_PKT_CMD:                                                            //命令下发result = MQTT_UnPacketCmd(cmd, &cmdid_topic, &req_payload, &req_len);    //解出topic和消息体if(result == 0){printf("cmdid: %s, req: %s, req_len: %d\r\n", cmdid_topic, req_payload, req_len);if(MQTT_PacketCmdResp(cmdid_topic, req_payload, &mqttPacket) == 0)  //命令回复组包{printf("Tips: Send CmdResp\r\n");ESP8266_SendData(mqttPacket._data, mqttPacket._len);            //回复命令MQTT_DeleteBuffer(&mqttPacket);                                   //删包}}break;case MQTT_PKT_PUBACK:                                                       //发送Publish消息,平台回复的Ackif(MQTT_UnPacketPublishAck(cmd) == 0)printf("Tips:  MQTT Publish Send OK\r\n");break;default:result = -1;break;}ESP8266_Clear();                                  //清空缓存if(result == -1)return;dataPtr = strchr(req_payload, ':');                   //搜索':'才能够出现数字if(dataPtr != NULL && result != -1)                   //如果找到了{dataPtr++;while(*dataPtr >= '0' && *dataPtr <= '9')       //判断是否是下发的命令控制数据{numBuf[num++] = *dataPtr++;}numBuf[num] = 0;num = atoi((const char *)numBuf);               //转为数值形式/*处理云端下发命令的数据*/if(strstr((char *)req_payload, "key"))     //搜索"key"{if(num == 1)                              //控制数据如果为1,代表开{HAL_GPIO_WritePin(GPIOC, GPIO_PIN_0, GPIO_PIN_RESET);}else if(num == 0)                         //控制数据如果为0,代表关{HAL_GPIO_WritePin(GPIOC, GPIO_PIN_0, GPIO_PIN_SET);}}//下同else if(strstr((char *)req_payload, "key1")){if(num == 1){printf("1\r\n");}else if(num == 0){printf("0\r\n");}}}
/************************************************************/      if(type == MQTT_PKT_CMD || type == MQTT_PKT_PUBLISH){MQTT_FreeBuffer(cmdid_topic);MQTT_FreeBuffer(req_payload);}}

ONENET.h

#ifndef _ONENET_H_
#define _ONENET_H_#include "esp8266.h"
#include "mqttkit.h"
#include "usart.h"
#include "gpio.h"_Bool OneNet_DevLink(void);void OneNet_SendData(void);void OneNet_RevPro(unsigned char *cmd);
/******************************************************
主函数调用情况:
1、main函数里记得打开接收中断
2、ESP8266_Init();while(OneNet_DevLink())            //接入OneNETHAL_Delay(500);printf("接入onenet成功");OneNet_SendData();//发送数据给onenet3、dataPtr = ESP8266_GetIPD(0);//获取平台返回的数据if(dataPtr != NULL)//如果返回数据不为空OneNet_RevPro(dataPtr);//平台返回数据检测()while循环内
***********************************************************/
#endif

MqttKit.c

#include "MqttKit.h"//C库
#include <string.h>
#include <stdio.h>#define CMD_TOPIC_PREFIX        "$creq"//==========================================================
//  函数名称:    EDP_NewBuffer
//
//  函数功能:    申请内存
//
//  入口参数:    edpPacket:包结构体
//              size:大小
//
//  返回参数:    无
//
//  说明:      1.可使用动态分配来分配内存
//              2.可使用局部或全局数组来指定内存
//==========================================================
void MQTT_NewBuffer(MQTT_PACKET_STRUCTURE *mqttPacket, uint32 size)
{uint32 i = 0;if(mqttPacket->_data == NULL){mqttPacket->_memFlag = MEM_FLAG_ALLOC;mqttPacket->_data = (uint8 *)MQTT_MallocBuffer(size);if(mqttPacket->_data != NULL){mqttPacket->_len = 0;mqttPacket->_size = size;for(; i < mqttPacket->_size; i++)mqttPacket->_data[i] = 0;}}else{mqttPacket->_memFlag = MEM_FLAG_STATIC;for(; i < mqttPacket->_size; i++)mqttPacket->_data[i] = 0;mqttPacket->_len = 0;if(mqttPacket->_size < size)mqttPacket->_data = NULL;}}//==========================================================
//  函数名称:    MQTT_DeleteBuffer
//
//  函数功能:    释放数据内存
//
//  入口参数:    edpPacket:包结构体
//
//  返回参数:    无
//
//  说明:
//==========================================================
void MQTT_DeleteBuffer(MQTT_PACKET_STRUCTURE *mqttPacket)
{if(mqttPacket->_memFlag == MEM_FLAG_ALLOC)MQTT_FreeBuffer(mqttPacket->_data);mqttPacket->_data = NULL;mqttPacket->_len = 0;mqttPacket->_size = 0;mqttPacket->_memFlag = MEM_FLAG_NULL;}int32 MQTT_DumpLength(size_t len, uint8 *buf)
{int32 i = 0;for(i = 1; i <= 4; ++i){*buf = len % 128;len >>= 7;if(len > 0){*buf |= 128;++buf;}else{return i;}}return -1;
}int32 MQTT_ReadLength(const uint8 *stream, int32 size, uint32 *len)
{int32 i;const uint8 *in = stream;uint32 multiplier = 1;*len = 0;for(i = 0; i < size; ++i){*len += (in[i] & 0x7f) * multiplier;if(!(in[i] & 0x80)){return i + 1;}multiplier <<= 7;if(multiplier >= 2097152)      //128 * *128 * *128{return -2;                  // error, out of range}}return -1;                          // not complete}//==========================================================
//  函数名称:    MQTT_UnPacketRecv
//
//  函数功能:    MQTT数据接收类型判断
//
//  入口参数:    dataPtr:接收的数据指针
//
//  返回参数:    0-成功        其他-失败原因
//
//  说明:
//==========================================================
uint8 MQTT_UnPacketRecv(uint8 *dataPtr)
{uint8 status = 255;uint8 type = dataPtr[0] >> 4;               //类型检查if(type < 1 || type > 14)return status;if(type == MQTT_PKT_PUBLISH){uint8 *msgPtr;uint32 remain_len = 0;msgPtr = dataPtr + MQTT_ReadLength(dataPtr + 1, 4, &remain_len) + 1;if(remain_len < 2 || dataPtr[0] & 0x01)                   //retainreturn 255;if(remain_len < ((uint16)msgPtr[0] << 8 | msgPtr[1]) + 2)return 255;if(strstr((int8 *)msgPtr + 2, CMD_TOPIC_PREFIX) != NULL) //如果是命令下发status = MQTT_PKT_CMD;elsestatus = MQTT_PKT_PUBLISH;}elsestatus = type;return status;}//==========================================================
//  函数名称:    MQTT_PacketConnect
//
//  函数功能:    连接消息组包
//
//  入口参数:    user:用户名:产品ID
//              password:密码:鉴权信息或apikey
//              devid:设备ID
//              cTime:连接保持时间
//              clean_session:离线消息清除标志
//              qos:重发标志
//              will_topic:异常离线topic
//              will_msg:异常离线消息
//              will_retain:消息推送标志
//              mqttPacket:包指针
//
//  返回参数:    0-成功        其他-失败
//
//  说明:
//==========================================================
uint8 MQTT_PacketConnect(const int8 *user, const int8 *password, const int8 *devid,uint16 cTime, uint1 clean_session, uint1 qos,const int8 *will_topic, const int8 *will_msg, int32 will_retain,MQTT_PACKET_STRUCTURE *mqttPacket)
{uint8 flags = 0;uint8 will_topic_len = 0;uint16 total_len = 15;int16 len = 0, devid_len = strlen(devid);if(!devid)return 1;total_len += devid_len + 2;//断线后,是否清理离线消息:1-清理    0-不清理--------------------------------------------if(clean_session){flags |= MQTT_CONNECT_CLEAN_SESSION;}//异常掉线情况下,服务器发布的topic------------------------------------------------------if(will_topic){flags |= MQTT_CONNECT_WILL_FLAG;will_topic_len = strlen(will_topic);total_len += 4 + will_topic_len + strlen(will_msg);}//qos级别--主要用于PUBLISH(发布态)消息的,保证消息传递的次数-----------------------------switch((unsigned char)qos){case MQTT_QOS_LEVEL0:flags |= MQTT_CONNECT_WILL_QOS0;                           //最多一次break;case MQTT_QOS_LEVEL1:flags |= (MQTT_CONNECT_WILL_FLAG | MQTT_CONNECT_WILL_QOS1);   //最少一次break;case MQTT_QOS_LEVEL2:flags |= (MQTT_CONNECT_WILL_FLAG | MQTT_CONNECT_WILL_QOS2);   //只有一次break;default:return 2;}//主要用于PUBLISH(发布态)的消息,表示服务器要保留这次推送的信息,如果有新的订阅者出现,就把这消息推送给它。如果不设那么推送至当前订阅的就释放了if(will_retain){flags |= (MQTT_CONNECT_WILL_FLAG | MQTT_CONNECT_WILL_RETAIN);}//账号为空 密码为空---------------------------------------------------------------------if(!user || !password){return 3;}flags |= MQTT_CONNECT_USER_NAME | MQTT_CONNECT_PASSORD;total_len += strlen(user) + strlen(password) + 4;//分配内存-----------------------------------------------------------------------------MQTT_NewBuffer(mqttPacket, total_len);if(mqttPacket->_data == NULL)return 4;memset(mqttPacket->_data, 0, total_len);/*************************************固定头部***********************************************///固定头部----------------------连接请求类型---------------------------------------------mqttPacket->_data[mqttPacket->_len++] = MQTT_PKT_CONNECT << 4;//固定头部----------------------剩余长度值-----------------------------------------------len = MQTT_DumpLength(total_len - 5, mqttPacket->_data + mqttPacket->_len);if(len < 0){MQTT_DeleteBuffer(mqttPacket);return 5;}elsemqttPacket->_len += len;/*************************************可变头部***********************************************///可变头部----------------------协议名长度 和 协议名--------------------------------------mqttPacket->_data[mqttPacket->_len++] = 0;mqttPacket->_data[mqttPacket->_len++] = 4;mqttPacket->_data[mqttPacket->_len++] = 'M';mqttPacket->_data[mqttPacket->_len++] = 'Q';mqttPacket->_data[mqttPacket->_len++] = 'T';mqttPacket->_data[mqttPacket->_len++] = 'T';//可变头部----------------------protocol level 4-----------------------------------------mqttPacket->_data[mqttPacket->_len++] = 4;//可变头部----------------------连接标志(该函数开头处理的数据)-----------------------------mqttPacket->_data[mqttPacket->_len++] = flags;//可变头部----------------------保持连接的时间(秒)----------------------------------------mqttPacket->_data[mqttPacket->_len++] = MOSQ_MSB(cTime);mqttPacket->_data[mqttPacket->_len++] = MOSQ_LSB(cTime);/*************************************消息体************************************************///消息体----------------------------devid长度、devid-------------------------------------mqttPacket->_data[mqttPacket->_len++] = MOSQ_MSB(devid_len);mqttPacket->_data[mqttPacket->_len++] = MOSQ_LSB(devid_len);strncat((int8 *)mqttPacket->_data + mqttPacket->_len, devid, devid_len);mqttPacket->_len += devid_len;//消息体----------------------------will_flag 和 will_msg---------------------------------if(flags & MQTT_CONNECT_WILL_FLAG){unsigned short mLen = 0;if(!will_msg)will_msg = "";mLen = strlen(will_topic);mqttPacket->_data[mqttPacket->_len++] = MOSQ_MSB(mLen);mqttPacket->_data[mqttPacket->_len++] = MOSQ_LSB(mLen);strncat((int8 *)mqttPacket->_data + mqttPacket->_len, will_topic, mLen);mqttPacket->_len += mLen;mLen = strlen(will_msg);mqttPacket->_data[mqttPacket->_len++] = MOSQ_MSB(mLen);mqttPacket->_data[mqttPacket->_len++] = MOSQ_LSB(mLen);strncat((int8 *)mqttPacket->_data + mqttPacket->_len, will_msg, mLen);mqttPacket->_len += mLen;}//消息体----------------------------use---------------------------------------------------if(flags & MQTT_CONNECT_USER_NAME){unsigned short user_len = strlen(user);mqttPacket->_data[mqttPacket->_len++] = MOSQ_MSB(user_len);mqttPacket->_data[mqttPacket->_len++] = MOSQ_LSB(user_len);strncat((int8 *)mqttPacket->_data + mqttPacket->_len, user, user_len);mqttPacket->_len += user_len;}//消息体----------------------------password----------------------------------------------if(flags & MQTT_CONNECT_PASSORD){unsigned short psw_len = strlen(password);mqttPacket->_data[mqttPacket->_len++] = MOSQ_MSB(psw_len);mqttPacket->_data[mqttPacket->_len++] = MOSQ_LSB(psw_len);strncat((int8 *)mqttPacket->_data + mqttPacket->_len, password, psw_len);mqttPacket->_len += psw_len;}return 0;}//==========================================================
//  函数名称:    MQTT_PacketDisConnect
//
//  函数功能:    断开连接消息组包
//
//  入口参数:    mqttPacket:包指针
//
//  返回参数:    0-成功        1-失败
//
//  说明:
//==========================================================
uint1 MQTT_PacketDisConnect(MQTT_PACKET_STRUCTURE *mqttPacket)
{MQTT_NewBuffer(mqttPacket, 2);if(mqttPacket->_data == NULL)return 1;/*************************************固定头部***********************************************///固定头部----------------------头部消息-------------------------------------------------mqttPacket->_data[mqttPacket->_len++] = MQTT_PKT_DISCONNECT << 4;//固定头部----------------------剩余长度值-----------------------------------------------mqttPacket->_data[mqttPacket->_len++] = 0;return 0;}//==========================================================
//  函数名称:    MQTT_UnPacketConnectAck
//
//  函数功能:    连接消息解包
//
//  入口参数:    rev_data:接收的数据
//
//  返回参数:    1、255-失败        其他-平台的返回码
//
//  说明:
//==========================================================
uint8 MQTT_UnPacketConnectAck(uint8 *rev_data)
{if(rev_data[1] != 2)return 1;if(rev_data[2] == 0 || rev_data[2] == 1)return rev_data[3];elsereturn 255;}//==========================================================
//  函数名称:    MQTT_PacketSaveData
//
//  函数功能:    数据点上传组包
//
//  入口参数:    devid:设备ID(可为空)
//              send_buf:json缓存buf
//              send_len:json总长
//              type_bin_head:bin文件的消息头
//              type:类型
//
//  返回参数:    0-成功        1-失败
//
//  说明:
//==========================================================
uint1 MQTT_PacketSaveData(const int8 *devid, int16 send_len, int8 *type_bin_head, uint8 type, MQTT_PACKET_STRUCTURE *mqttPacket)
{if(MQTT_PacketPublish(MQTT_PUBLISH_ID, "$dp", NULL, send_len + 3, MQTT_QOS_LEVEL1, 0, 1, mqttPacket) == 0){mqttPacket->_data[mqttPacket->_len++] = type;                 //类型mqttPacket->_data[mqttPacket->_len++] = MOSQ_MSB(send_len);mqttPacket->_data[mqttPacket->_len++] = MOSQ_LSB(send_len);}elsereturn 1;return 0;}//==========================================================
//  函数名称:    MQTT_PacketSaveBinData
//
//  函数功能:    为禁止文件上传组包
//
//  入口参数:    name:数据流名字
//              file_len:文件长度
//              mqttPacket:包指针
//
//  返回参数:    0-成功        1-失败
//
//  说明:
//==========================================================
uint1 MQTT_PacketSaveBinData(const int8 *name, int16 file_len, MQTT_PACKET_STRUCTURE *mqttPacket)
{uint1 result = 1;int8 *bin_head = NULL;uint8 bin_head_len = 0;int8 *payload = NULL;int32 payload_size = 0;bin_head = (int8 *)MQTT_MallocBuffer(13 + strlen(name));if(bin_head == NULL)return result;sprintf(bin_head, "{\"ds_id\":\"%s\"}", name);bin_head_len = strlen(bin_head);payload_size = 7 + bin_head_len + file_len;payload = (int8 *)MQTT_MallocBuffer(payload_size - file_len);if(payload == NULL){MQTT_FreeBuffer(bin_head);return result;}payload[0] = 2;                      //类型payload[1] = MOSQ_MSB(bin_head_len);payload[2] = MOSQ_LSB(bin_head_len);memcpy(payload + 3, bin_head, bin_head_len);payload[bin_head_len + 3] = (file_len >> 24) & 0xFF;payload[bin_head_len + 4] = (file_len >> 16) & 0xFF;payload[bin_head_len + 5] = (file_len >> 8) & 0xFF;payload[bin_head_len + 6] = file_len & 0xFF;if(MQTT_PacketPublish(MQTT_PUBLISH_ID, "$dp", payload, payload_size, MQTT_QOS_LEVEL1, 0, 1, mqttPacket) == 0)result = 0;MQTT_FreeBuffer(bin_head);MQTT_FreeBuffer(payload);return result;}//==========================================================
//  函数名称:    MQTT_UnPacketCmd
//
//  函数功能:    命令下发解包
//
//  入口参数:    rev_data:接收的数据指针
//              cmdid:cmdid-uuid
//              req:命令
//
//  返回参数:    0-成功        其他-失败原因
//
//  说明:
//==========================================================
uint8 MQTT_UnPacketCmd(uint8 *rev_data, int8 **cmdid, int8 **req, uint16 *req_len)
{int8 *dataPtr = strchr((int8 *)rev_data + 6, '/'); //加6是跳过头信息uint32 remain_len = 0;if(dataPtr == NULL)                                  //未找到'/'return 1;dataPtr++;                                         //跳过'/'MQTT_ReadLength(rev_data + 1, 4, &remain_len);        //读取剩余字节*cmdid = (int8 *)MQTT_MallocBuffer(37);                //cmdid固定36字节,多分配一个结束符的位置if(*cmdid == NULL)return 2;memset(*cmdid, 0, 37);                             //全部清零memcpy(*cmdid, (const int8 *)dataPtr, 36);            //复制cmdiddataPtr += 36;*req_len = remain_len - 44;                           //命令长度 = 剩余长度(remain_len) - 2 - 5($creq) - 1(\) - cmdid长度*req = (int8 *)MQTT_MallocBuffer(*req_len + 1);     //分配命令长度+1if(*req == NULL){MQTT_FreeBuffer(*cmdid);return 3;}memset(*req, 0, *req_len + 1);                     //清零memcpy(*req, (const int8 *)dataPtr, *req_len);      //复制命令return 0;}//==========================================================
//  函数名称:    MQTT_PacketCmdResp
//
//  函数功能:    命令回复组包
//
//  入口参数:    cmdid:cmdid
//              req:命令
//              mqttPacket:包指针
//
//  返回参数:    0-成功        1-失败
//
//  说明:
//==========================================================
uint1 MQTT_PacketCmdResp(const int8 *cmdid, const int8 *req, MQTT_PACKET_STRUCTURE *mqttPacket)
{uint16 cmdid_len = strlen(cmdid);uint16 req_len = strlen(req);_Bool status = 0;int8 *payload = MQTT_MallocBuffer(cmdid_len + 7);if(payload == NULL)return 1;memset(payload, 0, cmdid_len + 7);memcpy(payload, "$crsp/", 6);strncat(payload, cmdid, cmdid_len);if(MQTT_PacketPublish(MQTT_PUBLISH_ID, payload, req, strlen(req), MQTT_QOS_LEVEL0, 0, 1, mqttPacket) == 0)status = 0;elsestatus = 1;MQTT_FreeBuffer(payload);return status;}//==========================================================
//  函数名称:    MQTT_PacketSubscribe
//
//  函数功能:    Subscribe消息组包
//
//  入口参数:    pkt_id:pkt_id
//              qos:消息重发次数
//              topics:订阅的消息
//              topics_cnt:订阅的消息个数
//              mqttPacket:包指针
//
//  返回参数:    0-成功        其他-失败
//
//  说明:
//==========================================================
uint8 MQTT_PacketSubscribe(uint16 pkt_id, enum MqttQosLevel qos, const int8 *topics[], uint8 topics_cnt, MQTT_PACKET_STRUCTURE *mqttPacket)
{uint32 topic_len = 0, remain_len = 0;int16 len = 0;uint8 i = 0;if(pkt_id == 0)return 1;//计算topic长度-------------------------------------------------------------------------for(; i < topics_cnt; i++){if(topics[i] == NULL)return 2;topic_len += strlen(topics[i]);}//2 bytes packet id + topic filter(2 bytes topic + topic length + 1 byte reserve)------remain_len = 2 + 3 * topics_cnt + topic_len;//分配内存------------------------------------------------------------------------------MQTT_NewBuffer(mqttPacket, remain_len + 5);if(mqttPacket->_data == NULL)return 3;/*************************************固定头部***********************************************///固定头部----------------------头部消息-------------------------------------------------mqttPacket->_data[mqttPacket->_len++] = MQTT_PKT_SUBSCRIBE << 4 | 0x02;//固定头部----------------------剩余长度值-----------------------------------------------len = MQTT_DumpLength(remain_len, mqttPacket->_data + mqttPacket->_len);if(len < 0){MQTT_DeleteBuffer(mqttPacket);return 4;}elsemqttPacket->_len += len;/*************************************payload***********************************************///payload----------------------pkt_id---------------------------------------------------mqttPacket->_data[mqttPacket->_len++] = MOSQ_MSB(pkt_id);mqttPacket->_data[mqttPacket->_len++] = MOSQ_LSB(pkt_id);//payload----------------------topic_name-----------------------------------------------for(i = 0; i < topics_cnt; i++){topic_len = strlen(topics[i]);mqttPacket->_data[mqttPacket->_len++] = MOSQ_MSB(topic_len);mqttPacket->_data[mqttPacket->_len++] = MOSQ_LSB(topic_len);strncat((int8 *)mqttPacket->_data + mqttPacket->_len, topics[i], topic_len);mqttPacket->_len += topic_len;mqttPacket->_data[mqttPacket->_len++] = qos & 0xFF;}return 0;}//==========================================================
//  函数名称:    MQTT_UnPacketSubscrebe
//
//  函数功能:    Subscribe的回复消息解包
//
//  入口参数:    rev_data:接收到的信息
//
//  返回参数:    0-成功        其他-失败
//
//  说明:
//==========================================================
uint8 MQTT_UnPacketSubscribe(uint8 *rev_data)
{uint8 result = 255;if(rev_data[2] == MOSQ_MSB(MQTT_SUBSCRIBE_ID) && rev_data[3] == MOSQ_LSB(MQTT_SUBSCRIBE_ID)){switch(rev_data[4]){case 0x00:case 0x01:case 0x02://MQTT Subscribe OKresult = 0;break;case 0x80://MQTT Subscribe Failedresult = 1;break;default://MQTT Subscribe UnKnown Errresult = 2;break;}}return result;}//==========================================================
//  函数名称:    MQTT_PacketUnSubscribe
//
//  函数功能:    UnSubscribe消息组包
//
//  入口参数:    pkt_id:pkt_id
//              qos:消息重发次数
//              topics:订阅的消息
//              topics_cnt:订阅的消息个数
//              mqttPacket:包指针
//
//  返回参数:    0-成功        其他-失败
//
//  说明:
//==========================================================
uint8 MQTT_PacketUnSubscribe(uint16 pkt_id, const int8 *topics[], uint8 topics_cnt, MQTT_PACKET_STRUCTURE *mqttPacket)
{uint32 topic_len = 0, remain_len = 0;int16 len = 0;uint8 i = 0;if(pkt_id == 0)return 1;//计算topic长度-------------------------------------------------------------------------for(; i < topics_cnt; i++){if(topics[i] == NULL)return 2;topic_len += strlen(topics[i]);}//2 bytes packet id, 2 bytes topic length + topic + 1 byte reserve---------------------remain_len = 2 + (topics_cnt << 1) + topic_len;//分配内存------------------------------------------------------------------------------MQTT_NewBuffer(mqttPacket, remain_len + 5);if(mqttPacket->_data == NULL)return 3;/*************************************固定头部***********************************************///固定头部----------------------头部消息-------------------------------------------------mqttPacket->_data[mqttPacket->_len++] = MQTT_PKT_UNSUBSCRIBE << 4 | 0x02;//固定头部----------------------剩余长度值-----------------------------------------------len = MQTT_DumpLength(remain_len, mqttPacket->_data + mqttPacket->_len);if(len < 0){MQTT_DeleteBuffer(mqttPacket);return 4;}elsemqttPacket->_len += len;/*************************************payload***********************************************///payload----------------------pkt_id---------------------------------------------------mqttPacket->_data[mqttPacket->_len++] = MOSQ_MSB(pkt_id);mqttPacket->_data[mqttPacket->_len++] = MOSQ_LSB(pkt_id);//payload----------------------topic_name-----------------------------------------------for(i = 0; i < topics_cnt; i++){topic_len = strlen(topics[i]);mqttPacket->_data[mqttPacket->_len++] = MOSQ_MSB(topic_len);mqttPacket->_data[mqttPacket->_len++] = MOSQ_LSB(topic_len);strncat((int8 *)mqttPacket->_data + mqttPacket->_len, topics[i], topic_len);mqttPacket->_len += topic_len;}return 0;}//==========================================================
//  函数名称:    MQTT_UnPacketUnSubscribe
//
//  函数功能:    UnSubscribe的回复消息解包
//
//  入口参数:    rev_data:接收到的信息
//
//  返回参数:    0-成功        其他-失败
//
//  说明:
//==========================================================
uint1 MQTT_UnPacketUnSubscribe(uint8 *rev_data)
{uint1 result = 1;if(rev_data[2] == MOSQ_MSB(MQTT_UNSUBSCRIBE_ID) && rev_data[3] == MOSQ_LSB(MQTT_UNSUBSCRIBE_ID)){result = 0;}return result;}//==========================================================
//  函数名称:    MQTT_PacketPublish
//
//  函数功能:    Pulish消息组包
//
//  入口参数:    pkt_id:pkt_id
//              topic:发布的topic
//              payload:消息体
//              payload_len:消息体长度
//              qos:重发次数
//              retain:离线消息推送
//              own:
//              mqttPacket:包指针
//
//  返回参数:    0-成功        其他-失败
//
//  说明:
//==========================================================
uint8 MQTT_PacketPublish(uint16 pkt_id, const int8 *topic,const int8 *payload, uint32 payload_len,enum MqttQosLevel qos, int32 retain, int32 own,MQTT_PACKET_STRUCTURE *mqttPacket)
{uint32 total_len = 0, topic_len = 0;uint32 data_len = 0;int32 len = 0;uint8 flags = 0;//pkt_id检查----------------------------------------------------------------------------if(pkt_id == 0)return 1;//$dp为系统上传数据点的指令--------------------------------------------------------------for(topic_len = 0; topic[topic_len] != '\0'; ++topic_len){if((topic[topic_len] == '#') || (topic[topic_len] == '+'))return 2;}//Publish消息---------------------------------------------------------------------------flags |= MQTT_PKT_PUBLISH << 4;//retain标志----------------------------------------------------------------------------if(retain)flags |= 0x01;//总长度--------------------------------------------------------------------------------total_len = topic_len + payload_len + 2;//qos级别--主要用于PUBLISH(发布态)消息的,保证消息传递的次数-----------------------------switch(qos){case MQTT_QOS_LEVEL0:flags |= MQTT_CONNECT_WILL_QOS0;    //最多一次break;case MQTT_QOS_LEVEL1:flags |= 0x02;                        //最少一次total_len += 2;break;case MQTT_QOS_LEVEL2:flags |= 0x04;                       //只有一次total_len += 2;break;default:return 3;}//分配内存------------------------------------------------------------------------------if(payload != NULL){if(payload[0] == 2){uint32 data_len_t = 0;while(payload[data_len_t++] != '}');data_len_t -= 3;data_len = data_len_t + 7;data_len_t = payload_len - data_len;MQTT_NewBuffer(mqttPacket, total_len + 3 - data_len_t);if(mqttPacket->_data == NULL)return 4;memset(mqttPacket->_data, 0, total_len + 3 - data_len_t);}else{MQTT_NewBuffer(mqttPacket, total_len + 5);if(mqttPacket->_data == NULL)return 4;memset(mqttPacket->_data, 0, total_len + 5);}}else{MQTT_NewBuffer(mqttPacket, total_len + 5);if(mqttPacket->_data == NULL)return 4;memset(mqttPacket->_data, 0, total_len + 5);}/*************************************固定头部***********************************************///固定头部----------------------头部消息-------------------------------------------------mqttPacket->_data[mqttPacket->_len++] = flags;//固定头部----------------------剩余长度值-----------------------------------------------len = MQTT_DumpLength(total_len, mqttPacket->_data + mqttPacket->_len);if(len < 0){MQTT_DeleteBuffer(mqttPacket);return 5;}elsemqttPacket->_len += len;/*************************************可变头部***********************************************///可变头部----------------------写入topic长度、topic-------------------------------------mqttPacket->_data[mqttPacket->_len++] = MOSQ_MSB(topic_len);mqttPacket->_data[mqttPacket->_len++] = MOSQ_LSB(topic_len);strncat((int8 *)mqttPacket->_data + mqttPacket->_len, topic, topic_len);mqttPacket->_len += topic_len;if(qos != MQTT_QOS_LEVEL0){mqttPacket->_data[mqttPacket->_len++] = MOSQ_MSB(pkt_id);mqttPacket->_data[mqttPacket->_len++] = MOSQ_LSB(pkt_id);}//可变头部----------------------写入payload----------------------------------------------if(payload != NULL){if(payload[0] == 2){memcpy((int8 *)mqttPacket->_data + mqttPacket->_len, payload, data_len);mqttPacket->_len += data_len;}else{memcpy((int8 *)mqttPacket->_data + mqttPacket->_len, payload, payload_len);mqttPacket->_len += payload_len;}}return 0;}//==========================================================
//  函数名称:    MQTT_UnPacketPublish
//
//  函数功能:    Publish消息解包
//
//  入口参数:    flags:MQTT相关标志信息
//              pkt:指向可变头部
//              size:固定头部中的剩余长度信息
//
//  返回参数:    0-成功        其他-失败原因
//
//  说明:
//==========================================================
uint8 MQTT_UnPacketPublish(uint8 *rev_data, int8 **topic, uint16 *topic_len, int8 **payload, uint16 *payload_len, uint8 *qos, uint16 *pkt_id)
{const int8 flags = rev_data[0] & 0x0F;uint8 *msgPtr;uint32 remain_len = 0;const int8 dup = flags & 0x08;*qos = (flags & 0x06) >> 1;msgPtr = rev_data + MQTT_ReadLength(rev_data + 1, 4, &remain_len) + 1;if(remain_len < 2 || flags & 0x01)                           //retainreturn 255;*topic_len = (uint16)msgPtr[0] << 8 | msgPtr[1];if(remain_len < *topic_len + 2)return 255;if(strstr((int8 *)msgPtr + 2, CMD_TOPIC_PREFIX) != NULL)  //如果是命令下发return MQTT_PKT_CMD;switch(*qos){case MQTT_QOS_LEVEL0:                                 // qos0 have no packet identifierif(0 != dup)return 255;*topic = MQTT_MallocBuffer(*topic_len + 1);          //为topic分配内存if(*topic == NULL)return 255;memset(*topic, 0, *topic_len + 1);memcpy(*topic, (int8 *)msgPtr + 2, *topic_len);      //复制数据*payload_len = remain_len - 2 - *topic_len;          //为payload分配内存*payload = MQTT_MallocBuffer(*payload_len + 1);if(*payload == NULL)                               //如果失败{MQTT_FreeBuffer(*topic);                     //则需要把topic的内存释放掉return 255;}memset(*payload, 0, *payload_len + 1);memcpy(*payload, (int8 *)msgPtr + 2 + *topic_len, *payload_len);break;case MQTT_QOS_LEVEL1:case MQTT_QOS_LEVEL2:if(*topic_len + 2 > remain_len)return 255;*pkt_id = (uint16)msgPtr[*topic_len + 2] << 8 | msgPtr[*topic_len + 3];if(pkt_id == 0)return 255;*topic = MQTT_MallocBuffer(*topic_len + 1);           //为topic分配内存if(*topic == NULL)return 255;memset(*topic, 0, *topic_len + 1);memcpy(*topic, (int8 *)msgPtr + 2, *topic_len);      //复制数据*payload_len = remain_len - 4 - *topic_len;*payload = MQTT_MallocBuffer(*payload_len + 1);     //为payload分配内存if(*payload == NULL)                                //如果失败{MQTT_FreeBuffer(*topic);                     //则需要把topic的内存释放掉return 255;}memset(*payload, 0, *payload_len + 1);memcpy(*payload, (int8 *)msgPtr + 4 + *topic_len, *payload_len);break;default:return 255;}if(strchr((int8 *)topic, '+') || strchr((int8 *)topic, '#'))return 255;return 0;}//==========================================================
//  函数名称:    MQTT_PacketPublishAck
//
//  函数功能:    Publish Ack消息组包
//
//  入口参数:    pkt_id:packet id
//              mqttPacket:包指针
//
//  返回参数:    0-成功        1-失败原因
//
//  说明:      当收到的Publish消息的QoS等级为1时,需要Ack回复
//==========================================================
uint1 MQTT_PacketPublishAck(uint16 pkt_id, MQTT_PACKET_STRUCTURE *mqttPacket)
{MQTT_NewBuffer(mqttPacket, 4);if(mqttPacket->_data == NULL)return 1;/*************************************固定头部***********************************************///固定头部----------------------头部消息-------------------------------------------------mqttPacket->_data[mqttPacket->_len++] = MQTT_PKT_PUBACK << 4;//固定头部----------------------剩余长度-------------------------------------------------mqttPacket->_data[mqttPacket->_len++] = 2;/*************************************可变头部***********************************************///可变头部----------------------pkt_id长度-----------------------------------------------mqttPacket->_data[mqttPacket->_len++] = pkt_id >> 8;mqttPacket->_data[mqttPacket->_len++] = pkt_id & 0xff;return 0;}//==========================================================
//  函数名称:    MQTT_UnPacketPublishAck
//
//  函数功能:    Publish Ack消息解包
//
//  入口参数:    rev_data:收到的数据
//
//  返回参数:    0-成功        1-失败原因
//
//  说明:
//==========================================================
uint1 MQTT_UnPacketPublishAck(uint8 *rev_data)
{if(rev_data[1] != 2)return 1;if(rev_data[2] == MOSQ_MSB(MQTT_PUBLISH_ID) && rev_data[3] == MOSQ_LSB(MQTT_PUBLISH_ID))return 0;elsereturn 1;}//==========================================================
//  函数名称:    MQTT_PacketPublishRec
//
//  函数功能:    Publish Rec消息组包
//
//  入口参数:    pkt_id:packet id
//              mqttPacket:包指针
//
//  返回参数:    0-成功        1-失败原因
//
//  说明:      当收到的Publish消息的QoS等级为2时,先收到rec
//==========================================================
uint1 MQTT_PacketPublishRec(uint16 pkt_id, MQTT_PACKET_STRUCTURE *mqttPacket)
{MQTT_NewBuffer(mqttPacket, 4);if(mqttPacket->_data == NULL)return 1;/*************************************固定头部***********************************************///固定头部----------------------头部消息-------------------------------------------------mqttPacket->_data[mqttPacket->_len++] = MQTT_PKT_PUBREC << 4;//固定头部----------------------剩余长度-------------------------------------------------mqttPacket->_data[mqttPacket->_len++] = 2;/*************************************可变头部***********************************************///可变头部----------------------pkt_id长度-----------------------------------------------mqttPacket->_data[mqttPacket->_len++] = pkt_id >> 8;mqttPacket->_data[mqttPacket->_len++] = pkt_id & 0xff;return 0;}//==========================================================
//  函数名称:    MQTT_UnPacketPublishRec
//
//  函数功能:    Publish Rec消息解包
//
//  入口参数:    rev_data:接收到的数据
//
//  返回参数:    0-成功        1-失败
//
//  说明:
//==========================================================
uint1 MQTT_UnPacketPublishRec(uint8 *rev_data)
{if(rev_data[1] != 2)return 1;if(rev_data[2] == MOSQ_MSB(MQTT_PUBLISH_ID) && rev_data[3] == MOSQ_LSB(MQTT_PUBLISH_ID))return 0;elsereturn 1;}//==========================================================
//  函数名称:    MQTT_PacketPublishRel
//
//  函数功能:    Publish Rel消息组包
//
//  入口参数:    pkt_id:packet id
//              mqttPacket:包指针
//
//  返回参数:    0-成功        1-失败原因
//
//  说明:      当收到的Publish消息的QoS等级为2时,先收到rec,再回复rel
//==========================================================
uint1 MQTT_PacketPublishRel(uint16 pkt_id, MQTT_PACKET_STRUCTURE *mqttPacket)
{MQTT_NewBuffer(mqttPacket, 4);if(mqttPacket->_data == NULL)return 1;/*************************************固定头部***********************************************///固定头部----------------------头部消息-------------------------------------------------mqttPacket->_data[mqttPacket->_len++] = MQTT_PKT_PUBREL << 4 | 0x02;//固定头部----------------------剩余长度-------------------------------------------------mqttPacket->_data[mqttPacket->_len++] = 2;/*************************************可变头部***********************************************///可变头部----------------------pkt_id长度-----------------------------------------------mqttPacket->_data[mqttPacket->_len++] = pkt_id >> 8;mqttPacket->_data[mqttPacket->_len++] = pkt_id & 0xff;return 0;}//==========================================================
//  函数名称:    MQTT_UnPacketPublishRel
//
//  函数功能:    Publish Rel消息解包
//
//  入口参数:    rev_data:接收到的数据
//
//  返回参数:    0-成功        1-失败
//
//  说明:
//==========================================================
uint1 MQTT_UnPacketPublishRel(uint8 *rev_data, uint16 pkt_id)
{if(rev_data[1] != 2)return 1;if(rev_data[2] == MOSQ_MSB(pkt_id) && rev_data[3] == MOSQ_LSB(pkt_id))return 0;elsereturn 1;}//==========================================================
//  函数名称:    MQTT_PacketPublishComp
//
//  函数功能:    Publish Comp消息组包
//
//  入口参数:    pkt_id:packet id
//              mqttPacket:包指针
//
//  返回参数:    0-成功        1-失败原因
//
//  说明:      当收到的Publish消息的QoS等级为2时,先收到rec,再回复rel
//==========================================================
uint1 MQTT_PacketPublishComp(uint16 pkt_id, MQTT_PACKET_STRUCTURE *mqttPacket)
{MQTT_NewBuffer(mqttPacket, 4);if(mqttPacket->_data == NULL)return 1;/*************************************固定头部***********************************************///固定头部----------------------头部消息-------------------------------------------------mqttPacket->_data[mqttPacket->_len++] = MQTT_PKT_PUBCOMP << 4;//固定头部----------------------剩余长度-------------------------------------------------mqttPacket->_data[mqttPacket->_len++] = 2;/*************************************可变头部***********************************************///可变头部----------------------pkt_id长度-----------------------------------------------mqttPacket->_data[mqttPacket->_len++] = pkt_id >> 8;mqttPacket->_data[mqttPacket->_len++] = pkt_id & 0xff;return 0;}//==========================================================
//  函数名称:    MQTT_UnPacketPublishComp
//
//  函数功能:    Publish Comp消息解包
//
//  入口参数:    rev_data:接收到的数据
//
//  返回参数:    0-成功        1-失败
//
//  说明:
//==========================================================
uint1 MQTT_UnPacketPublishComp(uint8 *rev_data)
{if(rev_data[1] != 2)return 1;if(rev_data[2] == MOSQ_MSB(MQTT_PUBLISH_ID) && rev_data[3] == MOSQ_LSB(MQTT_PUBLISH_ID))return 0;elsereturn 1;}//==========================================================
//  函数名称:    MQTT_PacketPing
//
//  函数功能:    心跳请求组包
//
//  入口参数:    mqttPacket:包指针
//
//  返回参数:    0-成功        1-失败
//
//  说明:
//==========================================================
uint1 MQTT_PacketPing(MQTT_PACKET_STRUCTURE *mqttPacket)
{MQTT_NewBuffer(mqttPacket, 2);if(mqttPacket->_data == NULL)return 1;/*************************************固定头部***********************************************///固定头部----------------------头部消息-------------------------------------------------mqttPacket->_data[mqttPacket->_len++] = MQTT_PKT_PINGREQ << 4;//固定头部----------------------剩余长度-------------------------------------------------mqttPacket->_data[mqttPacket->_len++] = 0;return 0;}

MqttKit.h

#ifndef _MQTTKIT_H_
#define _MQTTKIT_H_#include "Common.h"#include <stdlib.h>#define MQTT_MallocBuffer  malloc#define MQTT_FreeBuffer       free
//==========================================================#define MOSQ_MSB(A)         (uint8)((A & 0xFF00) >> 8)
#define MOSQ_LSB(A)         (uint8)(A & 0x00FF)/*--------------------------------内存分配方案标志--------------------------------*/
#define MEM_FLAG_NULL       0
#define MEM_FLAG_ALLOC      1
#define MEM_FLAG_STATIC     2typedef struct Buffer
{uint8  *_data;     //协议数据uint32    _len;       //写入的数据长度uint32 _size;      //缓存总大小uint8    _memFlag;   //内存使用的方案:0-未分配  1-使用的动态分配       2-使用的固定内存} MQTT_PACKET_STRUCTURE;/*--------------------------------固定头部消息类型--------------------------------*/
enum MqttPacketType
{MQTT_PKT_CONNECT = 1, /**< 连接请求数据包 */MQTT_PKT_CONNACK,     /**< 连接确认数据包 */MQTT_PKT_PUBLISH,     /**< 发布数据数据包 */MQTT_PKT_PUBACK,      /**< 发布确认数据包 */MQTT_PKT_PUBREC,      /**< 发布数据已接收数据包,Qos 2时,回复MQTT_PKT_PUBLISH */MQTT_PKT_PUBREL,      /**< 发布数据释放数据包, Qos 2时,回复MQTT_PKT_PUBREC */MQTT_PKT_PUBCOMP,     /**< 发布完成数据包, Qos 2时,回复MQTT_PKT_PUBREL */MQTT_PKT_SUBSCRIBE,   /**< 订阅数据包 */MQTT_PKT_SUBACK,      /**< 订阅确认数据包 */MQTT_PKT_UNSUBSCRIBE, /**< 取消订阅数据包 */MQTT_PKT_UNSUBACK,    /**< 取消订阅确认数据包 */MQTT_PKT_PINGREQ,     /**< ping 数据包 */MQTT_PKT_PINGRESP,    /**< ping 响应数据包 */MQTT_PKT_DISCONNECT,  /**< 断开连接数据包 *///新增MQTT_PKT_CMD          /**< 命令下发数据包 */};/*--------------------------------MQTT QOS等级--------------------------------*/
enum MqttQosLevel
{MQTT_QOS_LEVEL0,  /**< 最多发送一次 */MQTT_QOS_LEVEL1,  /**< 最少发送一次  */MQTT_QOS_LEVEL2   /**< 只发送一次 */};/*--------------------------------MQTT 连接请求标志位,内部使用--------------------------------*/
enum MqttConnectFlag
{MQTT_CONNECT_CLEAN_SESSION  = 0x02,MQTT_CONNECT_WILL_FLAG      = 0x04,MQTT_CONNECT_WILL_QOS0      = 0x00,MQTT_CONNECT_WILL_QOS1      = 0x08,MQTT_CONNECT_WILL_QOS2      = 0x10,MQTT_CONNECT_WILL_RETAIN    = 0x20,MQTT_CONNECT_PASSORD        = 0x40,MQTT_CONNECT_USER_NAME      = 0x80};/*--------------------------------消息的packet ID,可自定义--------------------------------*/
#define MQTT_PUBLISH_ID         10#define MQTT_SUBSCRIBE_ID     20#define MQTT_UNSUBSCRIBE_ID       30/*--------------------------------删包--------------------------------*/
void MQTT_DeleteBuffer(MQTT_PACKET_STRUCTURE *mqttPacket);/*--------------------------------解包--------------------------------*/
uint8 MQTT_UnPacketRecv(uint8 *dataPtr);/*--------------------------------登录组包--------------------------------*/
uint8 MQTT_PacketConnect(const int8 *user, const int8 *password, const int8 *devid,uint16 cTime, uint1 clean_session, uint1 qos,const int8 *will_topic, const int8 *will_msg, int32 will_retain,MQTT_PACKET_STRUCTURE *mqttPacket);/*--------------------------------断开连接组包--------------------------------*/
uint1 MQTT_PacketDisConnect(MQTT_PACKET_STRUCTURE *mqttPacket);/*--------------------------------连接响应解包--------------------------------*/
uint8 MQTT_UnPacketConnectAck(uint8 *rev_data);/*--------------------------------数据点上传组包--------------------------------*/
uint1 MQTT_PacketSaveData(const int8 *devid, int16 send_len, int8 *type_bin_head, uint8 type, MQTT_PACKET_STRUCTURE *mqttPacket);/*--------------------------------二进制文件上传组包--------------------------------*/
uint1 MQTT_PacketSaveBinData(const int8 *name, int16 file_len, MQTT_PACKET_STRUCTURE *mqttPacket);/*--------------------------------命令下发解包--------------------------------*/
uint8 MQTT_UnPacketCmd(uint8 *rev_data, int8 **cmdid, int8 **req, uint16 *req_len);/*--------------------------------命令回复组包--------------------------------*/
uint1 MQTT_PacketCmdResp(const int8 *cmdid, const int8 *req, MQTT_PACKET_STRUCTURE *mqttPacket);/*--------------------------------订阅主题组包--------------------------------*/
uint8 MQTT_PacketSubscribe(uint16 pkt_id, enum MqttQosLevel qos, const int8 *topics[], uint8 topics_cnt, MQTT_PACKET_STRUCTURE *mqttPacket);/*--------------------------------订阅主题回复解包--------------------------------*/
uint8 MQTT_UnPacketSubscribe(uint8 *rev_data);/*--------------------------------取消订阅组包--------------------------------*/
uint8 MQTT_PacketUnSubscribe(uint16 pkt_id, const int8 *topics[], uint8 topics_cnt, MQTT_PACKET_STRUCTURE *mqttPacket);/*--------------------------------取消订阅回复解包--------------------------------*/
uint1 MQTT_UnPacketUnSubscribe(uint8 *rev_data);/*--------------------------------发布主题组包--------------------------------*/
uint8 MQTT_PacketPublish(uint16 pkt_id, const int8 *topic,const int8 *payload, uint32 payload_len,enum MqttQosLevel qos, int32 retain, int32 own,MQTT_PACKET_STRUCTURE *mqttPacket);/*--------------------------------发布消息回复解包--------------------------------*/
uint8 MQTT_UnPacketPublish(uint8 *rev_data, int8 **topic, uint16 *topic_len, int8 **payload, uint16 *payload_len, uint8 *qos, uint16 *pkt_id);/*--------------------------------发布消息的Ack组包--------------------------------*/
uint1 MQTT_PacketPublishAck(uint16 pkt_id, MQTT_PACKET_STRUCTURE *mqttPacket);/*--------------------------------发布消息的Ack解包--------------------------------*/
uint1 MQTT_UnPacketPublishAck(uint8 *rev_data);/*--------------------------------发布消息的Rec组包--------------------------------*/
uint1 MQTT_PacketPublishRec(uint16 pkt_id, MQTT_PACKET_STRUCTURE *mqttPacket);/*--------------------------------发布消息的Rec解包--------------------------------*/
uint1 MQTT_UnPacketPublishRec(uint8 *rev_data);/*--------------------------------发布消息的Rel组包--------------------------------*/
uint1 MQTT_PacketPublishRel(uint16 pkt_id, MQTT_PACKET_STRUCTURE *mqttPacket);/*--------------------------------发布消息的Rel解包--------------------------------*/
uint1 MQTT_UnPacketPublishRel(uint8 *rev_data, uint16 pkt_id);/*--------------------------------发布消息的Comp组包--------------------------------*/
uint1 MQTT_PacketPublishComp(uint16 pkt_id, MQTT_PACKET_STRUCTURE *mqttPacket);/*--------------------------------发布消息的Comp解包--------------------------------*/
uint1 MQTT_UnPacketPublishComp(uint8 *rev_data);/*--------------------------------心跳请求组包--------------------------------*/
uint1 MQTT_PacketPing(MQTT_PACKET_STRUCTURE *mqttPacket);#endif

common.h

#ifndef __COMMON_H__
#define __COMMON_H__/*---------------------------------------------------------------------------*/
/* Type Definition Macros                                                    */
/*---------------------------------------------------------------------------*/typedef _Bool            uint1;typedef unsigned char   uint8;typedef char            int8;typedef unsigned short  uint16;typedef short           int16;typedef unsigned int    uint32;typedef int                int32;typedef unsigned int  size_t;#endif /* __COMMON_H__ */

实验结果:

STM32CubMX——ESP8266WiFi模块相关推荐

  1. 基于STM32F1单片机、ESP8266WIFI模块、DHT11温湿度传感的WIFI网络温湿度传输系统

    基于STM32F1单片机.ESP8266WIFI模块.DHT11温湿度传感的WIFI网络温湿度传输系统 功能说明 温湿度采集端 温湿度监控端 硬件材料 硬件连接 主要代码 温湿度采集端 温湿度监控端 ...

  2. STM32单片机通过ESP8266WiFi模块与Android APP实现数据传输(二)---上位机搭建

    事物的难度远远低于对事物的恐惧 完成对STM32单片机和ESP8266 WiFi模块的配置之后,接下来需要完成Android APP代码的编写以及实现. 1.添加网络权限 因为我们需要对WiFi进行操 ...

  3. ESP8266-WIFI模块使用AT指令连接外网服务器

    ESP8266-WIFI模块使用AT指令连接外网服务器 第一步用java代码写一个服务器,代码如下: 代码解释:我是用nio写了一个,异步通信,用到了线程池,比较简单.当wifi模块连接后,会自动创建 ...

  4. esp8266WiFi模块通过MQTT连接华为云

    esp8266WiFi模块通过MQTT连接华为云 总结: 一. MQTT透传AT固件烧录 二. 串口调试 2.1 设置模块为STA模式 2.2 连接WiFi 2.3 设置MQTT的登陆用户名与密码 2 ...

  5. ESP8266wifi模块与51单片机通信教程

    ** ESP8266wifi模块与51单片机通信教程 准备两个200欧左右的电阻 准备6根杜邦线 然后将ES8266通过如图这种方式连接起来 注意:必须先配置ESP8266模块,配置需要用到ESP82 ...

  6. ESP8266WiFi模块AT指令入门指南

    ESP8266WiFi模块AT指令入门指南      1.1.模块 AP 模式下做 TCP serve AT+CWMODE=2                        开启 AP 模式(串口助手 ...

  7. Arduino用esp8266WiFi模块连接到服务器

    昨天用串口工具测试了一波esp8266WiFi模块连接云服务.没有问题 今天我决定用芯片来控制它,本来想用51,后面感觉太lou,又决定要stm32, 这个感觉不错,但是32芯片查资料确实麻烦,后面我 ...

  8. android arduino wifi模块,零知开源分享-ESP8266wifi模块的使用

    本帖最后由 零知实验室-roc 于 2019-5-14 14:01 编辑 1.说明 MCU:零知开源开发板-标准板 wifi模块:ESP-12F WiFi模块.png (85.41 KB, 下载次数: ...

  9. ESP8266wifi模块连接原子云实现手机与单片机的通信

    买正点原子的开发板时送了一块ESP8266wifi模块,一直没使用,最近几天刚好有时间就拿出来玩了一下,现在实现过程分享出来. 用到的模块: 1.STM32F103C8T6最小系统板 2.正点原子ES ...

最新文章

  1. AI 真的会带来大规模失业吗?
  2. jquery点击后执行PHP加载div,PHP-将JQuery自动完成附加到由Ajax调用加载的文本字段...
  3. python json dumps 自定义_Python json.dumps 自定义序列化操作
  4. 湖南网络推广浅析外链怎么发才会更快的收录?
  5. 怎么学JavaScript?
  6. Docker:Docker的简介、安装、使用方法之详细攻略
  7. 前端学习(1672):前端系列实战课程之加速减速运动
  8. nginx做https跳转apache
  9. “中国式”盗版该何去何从?
  10. java 页面接收参数_详解SpringMVC——接收请求参数和页面传参
  11. 动态图制作软件设计(三)
  12. 【Oracle】恢复重做日志组
  13. linux麦克风增强软件,如何在Windows,Mac,Linux中消除背景麦克风噪音
  14. Python数据分析项目实例5: 分析某餐饮企业的订单详情表数据(基于matplotlib的python数据可视化分析)
  15. Windows 错误代码
  16. 大学物理——关于误差
  17. 给苹果电脑选机械键盘
  18. 2021年什么邮箱最好用,这款电子邮箱让你的工作效率翻倍!
  19. 【IntelliJ IDEA】idea修改文件的file is read-only
  20. 程序员副业之如何利用空余时间从博客中赚钱?

热门文章

  1. stressapptest memtester
  2. 鹤望兰GSC DARLING in the FRANXX 到货评测
  3. java求职怎么描述专业技能,持续更新~
  4. Java设计模式:策略模式
  5. 膳食平衡六大原则对营养补充至关重要!
  6. ssh服务器与本地文件互传
  7. linux查看docker版本,如何查看docker的版本号是多少
  8. Linux虚拟机修改静态IP地址
  9. 三.技能系统 [Unity_Learn_RPG_1]
  10. kubernete架构体系介绍