通信协议学习-WiFi
前言
参考文章/博主
WiFi
- 常用AT命令
AT+GMR ---- 查看模块版本信息
AT+CIFSR ---- 查看IP地址
AT+CIOBAUD=9600 ---- 波特率修改为9600
AT+CWMODE=2 ---- 开启AP模式
AT+CWSAP=“ESP8266”,“0123456789”,11,3 ---- 设置WiFi名称,密码
AT+RST ---- 重启生效
- 特殊返回值
配网成功后会返回:
WIFI CONNECTED WIFI GOT IP OK
ESP-01S
阿里云连接
基于STM32F103ZET6连接阿里云
- 模块信息
使用USB转TTL模块连接WiFi模块在上位机进行测试
模块3.3V的电源一定不能由USB转TTL模块提供,它带不动
项目遇到的问题:
- ESP01S模块有时候连接成功了但是如果此时按复位的话会一直发送AT命令失败,原因是连接成功的那次最后是进入了透传模式的,所以复位的话它还是处于透传模式所以发AT命令没用,解决方法是在连接时第一句应该是退出透传模式指令,然后再写其他的,然后又有其他问题就是使用AT命令复位的话经常失败,后面我使用了直接把RST引脚写低电平然后延时100ms再写高电平这样就解决了
查看模块的信息
- 硬件电路
- MX配置
PC12设置成开漏输出即可,默认高电平,因为低电平复位
使用UART3进行通信
然后使用一个定时器用作接收计数
- 阿里云
- 程序编写
用到几个文件夹
- esp8266
- hmac(无需改动)
- iot
- mqtt(默认即可,只需要改transport.c)
把这几个文件夹放到一个名称为
aliyun的文件夹即可部分改动代码如下:
esp8266文件夹
esp8266.h
#ifndef __ESP8266_H #define __ESP8266_H #include "AllHead.h" //【网络连接信息】在下方修改设置您的路由器热点和物联网平台IP地址+端口号信息 #define SSID "yang520" // 无线路由器热点名称【必须按您的实际情况修改】 #define PASS "00000000" // 无线路由器热点密码【必须按您的实际情况修改】 #define IOT_DOMAIN_NAME "iot-06z00b2xuy7fxl9.mqtt.iothub.aliyuncs.com" // 云服务器IP地址【必须按您的实际情况修改】 #define IOT_PORTNUM "1883" // 云服务器端口号 typedef struct { uint8_t Connect_Server_Flag; // 连接成功服务器标志位 void (*ESP8266_Init)(void); // ESP8266初始化 uint8_t (*ESP8266_Send_Cmd)(char *, char *, uint16_t); // ESP8266发送指令(底层函数) uint8_t* (*ESP8266_Check_Cmd)(uint8_t *); // ESP8266检查指令(底层函数) uint8_t (*ESP8266_Connect_IOTServer)(void); // ESP8266连接到物联网平台服务器 uint8_t (*ESP8266_Quit_Trans)(void); // ESP8266退出透传模式(底层函数) uint8_t (*ESP8266_Connect_Server)(void); // ESP8266连接到服务器 uint8_t (*ESP8266_Connect_AP)(void); // ESP8266连接AP设备(无线路由器) void (*ESP8266_Send_Heart)(void); // 发送心跳包 }ESP8266_t; extern ESP8266_t ESP8266; #endif#ifndef __ESP8266_H #define __ESP8266_H #include "AllHead.h" //【网络连接信息】在下方修改设置您的路由器热点和物联网平台IP地址+端口号信息 #define SSID "yang520" // 无线路由器热点名称【必须按您的实际情况修改】 #define PASS "00000000" // 无线路由器热点密码【必须按您的实际情况修改】 #define IOT_DOMAIN_NAME "iot-06z00b2xuy7fxl9.mqtt.iothub.aliyuncs.com" // 云服务器IP地址【必须按您的实际情况修改】 #define IOT_PORTNUM "1883" // 云服务器端口号 typedef struct { uint8_t Connect_Server_Flag; // 连接成功服务器标志位 void (*ESP8266_Init)(void); // ESP8266初始化 uint8_t (*ESP8266_Send_Cmd)(char *, char *, uint16_t); // ESP8266发送指令(底层函数) uint8_t* (*ESP8266_Check_Cmd)(uint8_t *); // ESP8266检查指令(底层函数) uint8_t (*ESP8266_Connect_IOTServer)(void); // ESP8266连接到物联网平台服务器 uint8_t (*ESP8266_Quit_Trans)(void); // ESP8266退出透传模式(底层函数) uint8_t (*ESP8266_Connect_Server)(void); // ESP8266连接到服务器 uint8_t (*ESP8266_Connect_AP)(void); // ESP8266连接AP设备(无线路由器) void (*ESP8266_Send_Heart)(void); // 发送心跳包 }ESP8266_t; extern ESP8266_t ESP8266; #endifesp8266.c
/*************************************************************************** * File: esp8266.c * Author: Luckys. * Date: 2023/06/30 * description: ESP8266底层 ****************************************************************************/ #include "AllHead.h" /*====================================static function declaration area BEGIN====================================*/ static void ESP8266_Init(void); static uint8_t ESP8266_Send_Cmd(char *cmd, char *ack, uint16_t waittime); static uint8_t *ESP8266_Check_Cmd(uint8_t *str); static uint8_t ESP8266_Connect_IOTServer(void); static uint8_t ESP8266_Quit_Trans(void); static uint8_t ESP8266_Connect_Server(void); static uint8_t ESP8266_Connect_AP(void); static void ESP8266_Send_Heart(void); /*====================================static function declaration area END====================================*/ ESP8266_t ESP8266 = { FALSE, ESP8266_Init, ESP8266_Send_Cmd, ESP8266_Check_Cmd, ESP8266_Connect_IOTServer, ESP8266_Quit_Trans, ESP8266_Connect_Server, ESP8266_Connect_AP, ESP8266_Send_Heart }; /* * @function: ESP8266_Init * @param: None * @retval: None * @brief: ESP8266初始化 */ static void ESP8266_Init(void) { uint8_t rx_error_num; rx_error_num = ESP8266.ESP8266_Connect_IOTServer(); if (0 == rx_error_num) { while (IOT.IOT_Connect()); ESP8266.Connect_Server_Flag = TRUE; // 标志位置1(心跳包才开始发送) printf("连接服务器成功!\r\n"); HAL_Delay(100); IOT.IOT_Subscribe(); } else { printf("WiFi错误,错误码:%d!\r\n", rx_error_num); } } /* * @function: ESP8266_Send_Cmd * @param: cmd -> 命令 ack -> 回应值 waittime -> 等待时间 * @retval: 0 --- 成功 1 --- 失败 * @brief: ESP8266发送指令(底层函数) */ static uint8_t ESP8266_Send_Cmd(char *cmd, char *ack, uint16_t waittime) { uint8_t res = 0; USART3_RX_STA = 0; memset(USART3_RX_BUF, 0, USART3_REC_LEN); // 将串口3的缓存空间清0 WIFI_printf("%s\r\n", cmd); // 调用WIFI模块专用的发送函数 if (waittime) // 需要等待应答 { while (--waittime) // 等待倒计时 { Public.Public_Delay_ms(10); if (USART3_RX_STA & 0x8000) // 接收到期待的应答结果 { if (ESP8266_Check_Cmd((uint8_t *)ack)) { printf("回复信息:%s\r\n", (uint8_t *)ack); // 反馈应答信息 break; // 得到有效数据 } USART3_RX_STA = 0; // 串口3标志位清0 } } if (waittime == 0) res = 1; } return res; } /* * @function: ESP8266_Check_Cmd * @param: str -> 需要寻找的子串字符串 * @retval: 非0 -> 返回子串找到的位置 0 -> 没找到 * @brief: ESP8266检查指令(底层函数) */ static uint8_t *ESP8266_Check_Cmd(uint8_t *str) { char *strx = 0; if (USART3_RX_STA & 0X8000) // 接收到一次数据了 { USART3_RX_BUF[USART3_RX_STA & 0X7FFF] = 0; // 添加结束符 printf("%s\r\n", (char *)USART3_RX_BUF); strx = strstr((const char *)USART3_RX_BUF, (const char *)str); } return (uint8_t *)strx; } /* * @function: ESP8266_Quit_Trans * @param: None * @retval: 0 --- 成功 1 --- 失败 * @brief: ESP8266退出透传模式(底层函数) */ static uint8_t ESP8266_Quit_Trans(void) { while ((USART3->SR & 0X40) == 0); // 等待发送空 USART3->DR = '+'; HAL_Delay(15); // 大于串口组帧时间(10ms) while ((USART3->SR & 0X40) == 0); // 等待发送空 USART3->DR = '+'; HAL_Delay(15); // 大于串口组帧时间(10ms) while ((USART3->SR & 0X40) == 0); // 等待发送空 USART3->DR = '+'; Public.Public_Delay_ms(500); return ESP8266_Send_Cmd("AT", "OK", 20); // 退出透传判断 } /* * @function: ESP8266_Connect_IOTServer * @param: None * @retval: 0 --- 成功 1 --- 失败 * @brief: ESP8266连接到物联网平台服务器 */ static uint8_t ESP8266_Connect_IOTServer(void) { /*状态检测*/ // 【1】 printf("准备配置模块\r\n"); Public.Public_Delay_ms(100); ESP8266_Send_Cmd("AT", "OK", 50); // 【2】 printf("准备退出透传模式\n"); if (ESP8266_Quit_Trans()) { printf("退出透传模式失败,准备重启\r\n"); return 6; } else printf("退出透传模式成功\r\n"); // 【3】 printf("准备关闭回显\r\n"); if (ESP8266_Send_Cmd("ATE0", "OK", 50)) { printf("关闭回显失败准备重启\r\n"); return 1; } else printf("关闭回显成功\r\n"); // 【4】 printf("查询模块是否在线\r\n"); if (ESP8266_Send_Cmd("AT", "OK", 50)) { printf("模块不在线准备重启\r\n"); return 1; } else printf("设置查询在线成功\r\n"); // 【5】 printf("准备设置STA模式\r\n"); if (ESP8266_Send_Cmd("AT+CWMODE=1", "OK", 50)) { printf("设置STA模式失败准备重启\r\n"); return 1; } else printf("设置STA模式成功\r\n"); // 【6】 printf("准备重启\r\n"); if (ESP8266_Send_Cmd("AT+RST", "OK", 50)) { printf("重启失败,准备重启\r\n"); return 2; } else printf("重启成功,等待三秒\r\n"); Public.Public_Delay_ms(3000); // 【7】 printf("准备取消自动连接\r\n"); if (ESP8266_Send_Cmd("AT+CWAUTOCONN=0", "OK", 50)) { printf("取消自动连接失败,准备重启\r\n"); return 3; } else printf("取消自动连接成功\r\n"); // 【8】 printf("准备链接路由器\r\n"); if (ESP8266_Connect_AP()) { printf("连接路由器失败,热点没找到\r\n"); return 4; } else printf("连接路由器成功\r\n"); Public.Public_Delay_ms(4000); // 【9】 printf("准备开启DHCP\r\n"); if (ESP8266_Send_Cmd("AT+CWDHCP=1,1", "OK", 100)) { printf("开启DHCP失败,准备重启\r\n"); return 7; } else printf("设置DHCP成功\r\n"); // 【10】 printf("设置为关闭多路连接\r\n"); if (ESP8266_Send_Cmd("AT+CIPMUX=0", "OK", 100)) { printf("关闭多路连接失败,准备重启\r\n"); return 7; } else printf("设置关闭多路连接成功\r\n"); // 【11】 printf("准备链接服务器\r\n"); if (ESP8266_Connect_Server()) { printf("连接服务器失败,等待重启\r\n"); return 8; } else printf("连接服务器成功\r\n"); // 【12】 printf("准备退出透传模式\n"); if (ESP8266_Quit_Trans()) { printf("退出透传模式失败,准备重启\r\n"); return 6; } else printf("退出透传模式成功\r\n"); // 【13】 printf("设置为透传模式\r\n"); if (ESP8266_Send_Cmd("AT+CIPMODE=1", "OK", 50)) { printf("设置透传失败,准备重启\r\n"); return 6; } else printf("设置透传成功\r\n"); // 【14】 printf("设置开启透传模式\r\n"); if (ESP8266_Send_Cmd("AT+CIPSEND", "OK", 1000)) { printf("开启透传失败,准备重启\r\n"); return 9; } else printf("开启透传成功\r\n"); USART3_RX_STA = 0; Public.Memory_Clear(USART3_RX_BUF,USART3_REC_LEN); return 0; // 一切顺利返回0 } /* * @function: ESP8266_Connect_AP * @param: None * @retval: 0 -> 成功 1 -> 失败 * @brief: ESP8266连接AP设备(无线路由器) */ static uint8_t ESP8266_Connect_AP(void) { uint8_t i = 5; char *p = (char *)malloc(50); // 分配存储空间的指针 sprintf((char *)p, "AT+CWJAP=\"%s\",\"%s\"", SSID, PASS); // 发送连接AT指令 while (ESP8266_Send_Cmd(p, "WIFI GOT IP", 500) && i) // 循环判断等待连接AP的结果 { printf("链接AP失败,尝试重新连接\r\n"); // 连接失败的反馈信息 i--; } free(p); // 释放分配的空间和指针 if (i) return 0; else return 1; } /* * @function: ESP8266_Connect_Server * @param: None * @retval: 0 -> 成功 1 -> 失败 * @brief: ESP8266连接到服务器 */ static uint8_t ESP8266_Connect_Server(void) { uint8_t i = 10; char *p = (char *)malloc(50); // 分配存储空间的指针 sprintf((char *)p, "AT+CIPSTART=\"TCP\",\"%s\",\%s", IOT_DOMAIN_NAME, IOT_PORTNUM); while (ESP8266_Send_Cmd(p, "CONNECT", 1000) && i) { printf("链接服务器失败,尝试重新连接\r\n"); i--; } free(p); // 释放分配的空间和指针 if (i) return 0; else return 1; } /* * @function: ESP8266_Send_Heart * @param: None * @retval: None * @brief: 发送心跳包 */ static void ESP8266_Send_Heart(void) { static uint8_t send_Cnt; // 记录连续发送心跳包失败的数量 uint8_t i; if (ESP8266.Connect_Server_Flag) { IOT.IOT_Ping(); // 发送Ping心跳包 if (MQTTPacket_read(IOT.Buf, IOT.Buflen, transport_getdata) == PINGRESP) // 判断心跳包是不回复确认 { printf("心跳成功\r\n"); // 回复0xD0,0x00时表示心跳成功的回复 send_Cnt = 0; } else { send_Cnt++; printf("心跳失败\r\n"); // 无回复表示失败 for (i = 0; i < 30; i++) { IOT.IOT_Ping(); if (MQTTPacket_read(IOT.Buf, IOT.Buflen, transport_getdata) == PINGRESP) // 判断心跳包是不回复确认 { printf("心跳成功\r\n"); // 回复0xD0,0x00时表示心跳成功的回复 send_Cnt = 0; break; } } } if (send_Cnt != 0) // 如果快速发送心跳包20次后无回复,则复位WIFI模块重新连接 { while (ESP8266.ESP8266_Connect_IOTServer()); // AT指令连接TCP连接云服务器(IP和端口参数在esp8266.h文件内修改设置) while (IOT.IOT_Connect()); // 用MQTT协议+三元组信息连接阿里云物联网平台(三元组参数在iot.h文件内修改设置) send_Cnt = 0; // 计算标志清0 } USART3_RX_STA = 0; // 串口3接收标志位清0 } }iot文件夹
iot.h
#ifndef __IOT_H #define __IOT_H //【三元组信息】在下方修改设置您的物联网云平台提供的三元组信息 #define PRODUCTKEY "ikjyg0aBLnO" // 产品ID(ProductKey)【必须按您的实际情况修改】 #define PRODUCTKEY_LEN strlen(PRODUCTKEY) // 产品ID长度 #define DEVICENAME "ESP8266" // 设备名(DeviceName)【必须按您的实际情况修改】 #define DEVICENAME_LEN strlen(DEVICENAME) // 设备名长度 #define DEVICESECRE "ddca5d04d49afc1d4ed6effe579d73a3" // 设备秘钥(DeviceSecret)【必须按您的实际情况修改】 #define DEVICESECRE_LEN strlen(DEVICESECRE) // 设备秘钥长度 #define TOPIC_SUBSCRIBE "/ikjyg0aBLnO/ESP8266/user/get" // 订阅权限的地址【必须按您的实际情况修改】 #define TOPIC_QOS 0 // QoS服务质量数值(0/1) #define MSGID 1 // 信息识别ID #define TOPIC_PUBLISH "/ikjyg0aBLnO/ESP8266/user/update/error" // 发布权限的地址【必须按您的实际情况修改】 #define MQTTVERSION 4 // MQTT协议版本号(3表示V3.1,4表示V3.1.1) #define KEEPALIVEINTERVAL 120 // 保活计时器,服务器收到客户端消息(含心跳包)的最大间隔(单位是秒) typedef struct { uint16_t Buflen; uint8_t Buf[200]; char ClientID[128]; char Username[128]; char Password[128]; uint8_t (*IOT_Connect)(void); // IOT物联网平台连接 void (*IOT_Ping)(void); // 发送心跳包PING(保持与云服务器的连接) uint8_t (*IOT_Subscribe)(void); // subscribe主题订阅(订阅成功后才能接收订阅消息) uint8_t (*IOT_Publish)(char*); // publish主题发布(参数是发布信息内容,用双引号包含) }IOT_t; extern IOT_t IOT; #endifiot.c
/*************************************************************************** * File: iot.c * Author: Luckys. * Date: 2023/06/30 * description: 开发板ESP8266应用层 ****************************************************************************/ #include "AllHead.h" /*====================================static function declaration area BEGIN====================================*/ static uint8_t IOT_Connect(void); static void IOT_Ping(void); static uint8_t IOT_Subscribe(void); static uint8_t IOT_Publish(char*); /*====================================static function declaration area END====================================*/ IOT_t IOT = { 200, {0}, {0}, {0}, {0}, IOT_Connect, IOT_Ping, IOT_Subscribe, IOT_Publish }; /* * @function: IOT_Connect * @param: None * @retval: None * @brief: IOT物联网平台连接 */ static uint8_t IOT_Connect(void) { uint16_t a; uint32_t len; char temp[128]; printf("开始连接云端服务器\r\n"); MQTTPacket_connectData data = MQTTPacket_connectData_initializer;//配置部分可变头部的值 IOT.Buflen = sizeof(IOT.Buf); memset(IOT.Buf,0,IOT.Buflen); memset(IOT.ClientID,0,128);//客户端ID的缓冲区全部清零 sprintf(IOT.ClientID,"%s|securemode=3,signmethod=hmacsha1|",DEVICENAME);//构建客户端ID,并存入缓冲区 memset(IOT.Username,0,128);//用户名的缓冲区全部清零 sprintf(IOT.Username,"%s&%s",DEVICENAME,PRODUCTKEY);//构建用户名,并存入缓冲区 // Username_len = strlen(IOT.Username); memset(temp,0,128);//临时缓冲区全部清零 sprintf(temp,"clientId%sdeviceName%sproductKey%s",DEVICENAME,DEVICENAME,PRODUCTKEY);//构建加密时的明文 utils_hmac_sha1(temp,strlen(temp),IOT.Password,DEVICESECRE,DEVICESECRE_LEN);//以DeviceSecret为秘钥对temp中的明文,进行hmacsha1加密,结果就是密码,并保存到缓冲区中 // Password_len = strlen(IOT.Password);//计算用户名的长度 printf("ClientId:%s\r\n",IOT.ClientID); printf("Username:%s\r\n",IOT.Username); printf("Password:%s\r\n",IOT.Password); //【重要参数设置】可修改版本号、保活时间 data.MQTTVersion = MQTTVERSION; //MQTT协议版本号 data.clientID.cstring = IOT.ClientID; //客户端标识,用于区分每个客户端xxx为自定义,后面为固定格式 data.keepAliveInterval = KEEPALIVEINTERVAL; //保活计时器,定义了服务器收到客户端消息的最大时间间隔,单位是秒 data.cleansession = 1; //该标志置1服务器必须丢弃之前保持的客户端的信息,将该连接视为“不存在” data.username.cstring = IOT.Username; //用户名 DeviceName&ProductKey data.password.cstring = IOT.Password; //密码,工具生成 len = MQTTSerialize_connect(IOT.Buf, IOT.Buflen, &data);//构造连接的报文 transport_sendPacketBuffer(0,IOT.Buf, len);//发送连接请求 unsigned char sessionPresent, connack_rc; a=0; while(MQTTPacket_read(IOT.Buf, IOT.Buflen, transport_getdata) != CONNACK || a>1000)//等待胳回复 { HAL_Delay(10);//必要的延时等待 a++;//超时计数加1 } if(a>1000)NVIC_SystemReset();//当计数超时,则复位单片机 while(MQTTDeserialize_connack(&sessionPresent, &connack_rc, IOT.Buf, IOT.Buflen) != 1 || connack_rc != 0); if(connack_rc != 0) { printf("连接回复:%uc\r\n",connack_rc); } printf("连接成功!\r\n"); return 0;//执行成功返回0 } /* * @function: IOT_Ping * @param: None * @retval: None * @brief: 发送心跳包PING(保持与云服务器的连接) */ static void IOT_Ping(void) { uint32_t len; len = MQTTSerialize_pingreq(IOT.Buf, IOT.Buflen); // 计算数据长度 transport_sendPacketBuffer(0, IOT.Buf, len); // 发送数据 HAL_Delay(200); // 必要的延时等待 printf("发送心跳包Ping... "); } /* * @function: IOT_Subscribe * @param: None * @retval: 0 -> 成功 1 -> 失败 * @brief: subscribe主题订阅(订阅成功后才能接收订阅消息) */ static uint8_t IOT_Subscribe(void) { uint32_t len; int req_qos = TOPIC_QOS; MQTTString topicString = MQTTString_initializer; // 定义Topic结构体并初始化 topicString.cstring = TOPIC_SUBSCRIBE; len = MQTTSerialize_subscribe(IOT.Buf, IOT.Buflen, 0, MSGID, 1, &topicString, &req_qos); // 订阅发送数据编码 transport_sendPacketBuffer(0, IOT.Buf, len); HAL_Delay(100); // 必要的延时等待 if (MQTTPacket_read(IOT.Buf, IOT.Buflen, transport_getdata) == SUBACK) // 等待订阅回复 { unsigned short submsgid; int subcount; int granted_qos; MQTTDeserialize_suback(&submsgid, 1, &subcount, &granted_qos, IOT.Buf, IOT.Buflen); // 回复的订阅确认数据解码 if (granted_qos != 0) // qos不为0表示订阅成功 { printf("订阅成功 GrantedQoS=%d\r\n", granted_qos); return 0; // 订阅成功 } } printf("订阅失败\r\n"); return 1; // 订阅失败 } /* * @function: IOT_Publish * @param: None * @retval: * @brief: publish主题发布(参数是发布信息内容,用双引号包含) */ static uint8_t IOT_Publish(char* payload) { uint32_t len; MQTTString topicString = MQTTString_initializer; // 定义Topic结构体并初始化 topicString.cstring = TOPIC_PUBLISH; int payloadlen = strlen(payload); // 用函数计算发布信息内容的长度 printf("发布信息:%.*s\r\n", payloadlen, payload); // 将要发送的信息payload通过MQTTSerialize_publish编码后用transport_sendPacketBuffer发送给云服务器 len = MQTTSerialize_publish(IOT.Buf, IOT.Buflen, 0, 0, 0, 0, topicString, (unsigned char *)payload, payloadlen); // 发布数据编码 transport_sendPacketBuffer(0, IOT.Buf, len); // 发送编码好的最终数据 HAL_Delay(100); // 必要的延时等待 return 1; }mqtt文件夹
transport.h
#ifndef _TRAN_S #define _TRAN_S int transport_sendPacketBuffer(int sock, unsigned char* buf, int buflen); int transport_getdata(unsigned char* buf, int count); int transport_getdatanb(void *sck, unsigned char* buf, int count); int transport_open(char* host, int port); int transport_close(int sock); #endiftransport.c
/*************************************************************************** * File: transport.c * Author: Luckys. * Date: 2023/06/30 * description: 传输层 ----------------------------------- 注意: 这个只需要把下面的串口换成当前WiFi模块的串口即可 ----------------------------------- ****************************************************************************/ #include "AllHead.h" #if !defined(SOCKET_ERROR) /** error in socket operation */ #define SOCKET_ERROR -1 #endif int transport_sendPacketBuffer(int sock, unsigned char* buf, int buflen) { USART3_RX_STA = 0; Public.Memory_Clear(USART3_RX_BUF, USART3_REC_LEN); HAL_UART_Transmit(&huart3, buf, buflen, 1000); // 调用串口3发送HAL库函数 return buflen; } int transport_getdata(unsigned char* buf, int count) { memcpy(buf, (const char *)USART3_RX_BUF, count); USART3_RX_STA = 0; // 接收标志位清0 Public.Memory_Clear(USART3_RX_BUF, USART3_REC_LEN); // 缓存清0 return count; } int transport_getdatanb(void *sck, unsigned char* buf, int count) { return 0; } /** return >=0 for a socket descriptor, <0 for an error code @todo Basically moved from the sample without changes, should accomodate same usage for 'sock' for clarity, removing indirections */ int transport_open(char* addr, int port) { return 0; } int transport_close(int sock) { return 0; }
注意:其余文件在工程里找
- 我的程序
心跳的话放任务里即可,10s发送一次
static void TasksHandle_10S(void)
{
ESP8266.ESP8266_Send_Heart();
}
初始化,定时器是接收所需要的,打开串口3接收,ESP8266初始化
HAL_TIM_Base_Start_IT(&htim6);
HAL_UART_Receive_IT(&huart3,(uint8_t*)&USART3_NewData,1);
ESP8266.ESP8266_Init();
定时器6中断回调
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) // 定时器中断回调函数
{
if (htim == &htim6) // 判断是否是定时器2中断(定时器到时表示一组字符串接收结束)
{
USART3_RX_BUF[USART3_RX_STA & 0X7FFF] = 0; // 添加结束符
USART3_RX_STA |= 0x8000; // 接收标志位最高位置1表示接收完成
__HAL_TIM_CLEAR_FLAG(&htim6, TIM_EVENTSOURCE_UPDATE); // 清除TIM2更新中断标志
__HAL_TIM_DISABLE(&htim6); // 关闭定时器2
}
if (htim == &htim7)
{
System.Task_Marks_Handler();
}
}
串口3相关
UART3.h
#ifndef __USART3_H_
#define __USART3_H_
#include "AllHead.h"
#define USART3_REC_LEN 200 // 定义USART1最大接收字节数
typedef struct
{
uint8_t USART3_RX_BUF[USART3_REC_LEN]; // 接收缓冲,最大USART_REC_LEN个字节
uint16_t USART3_RX_STA; // 接收状态标记(bit15:接收完成标志,bit14:接收到0x0d,bit13~0:接收到的有效字节数目)
uint8_t USART3_NewData; // 当前串口中断接收的1个字节数据的缓存
void (*WIFI_printf)(char *, ...); // WIFI模块通信,使用UART3,这是专用的printf函数
void (*WIFI_TCP_SEND)(char *, ...); // WIFI模块在TCP模式下的数据发送:TCP发送的规定是先发AT+CIPSEND=数量,等待返回“>“后再发送数据内容
}UART3_t;
extern UART3_t UART3;
#endif
UART3.c
/***************************************************************************
* File: UART3.c
* Author: Luckys.
* Date: 2023/06/30
* description: 串口3
****************************************************************************/
#include "AllHead.h"
/*====================================static function declaration area BEGIN====================================*/
static void WIFI_printf(char *, ...);
static void WIFI_TCP_SEND(char *, ...);
/*====================================static function declaration area END====================================*/
UART3_t UART3 =
{
{0},
0,
0,
WIFI_printf,
WIFI_TCP_SEND
};
/*
* @function: HAL_UART_RxCpltCallback
* @param: None
* @retval: None
* @brief: 串口接收中断回调函数
*/
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if (huart == &huart3) // 接收完的一批数据,还没有被处理,则不再接收其他数据
{
if (UART3.USART3_RX_STA < USART3_REC_LEN) // 还可以接收数据
{
__HAL_TIM_SET_COUNTER(&htim6, 0); // 计数器清空
if (0 == UART3.USART3_RX_STA) // 使能定时器2的中断
{
__HAL_TIM_ENABLE(&htim6); // 使能定时器2
}
UART3.USART3_RX_BUF[UART3.USART3_RX_STA++] = UART3.USART3_NewData; // 最新接收数据放入数组
}
else
{
UART3.USART3_RX_STA |= 0x8000; // 强制标记接收完成
}
HAL_UART_Receive_IT(&huart3, (uint8_t *)&UART3.USART3_NewData, 1); // 再开启串口3接收中断
}
}
/*
* @function: WIFI_printf
* @param: None
* @retval: None
* @brief: WIFI模块通信,使用UART3,这是专用的printf函数
*/
static void WIFI_printf(char *fmt, ...)
{
char buff[USART3_REC_LEN + 1]; // 用于存放转换后的数据 [长度]
uint16_t i = 0;
va_list arg_ptr;
va_start(arg_ptr, fmt);
vsnprintf(buff, USART3_REC_LEN + 1, fmt, arg_ptr); // 数据转换
i = strlen(buff); // 得出数据长度
if (strlen(buff) > USART3_REC_LEN)
i = USART3_REC_LEN; // 如果长度大于最大值,则长度等于最大值(多出部分忽略)
HAL_UART_Transmit(&huart3, (uint8_t *)buff, i, 0xffff); // 串口发送函数(串口号,内容,数量,溢出时间)
va_end(arg_ptr);
}
/*
* @function: WIFI_TCP_SEND
* @param: None
* @retval: None
* @brief: WIFI模块在TCP模式下的数据发送:TCP发送的规定是先发AT+CIPSEND=数量,等待返回“>“后再发送数据内容。
*/
static void WIFI_TCP_SEND(char *fmt, ...)
{
char buff[USART3_REC_LEN + 1]; // 用于存放转换后的数据 [长度]
uint16_t i = 0;
va_list arg_ptr;
va_start(arg_ptr, fmt);
vsnprintf(buff, USART3_REC_LEN + 1, fmt, arg_ptr); // 数据转换
i = strlen(buff); // 得出数据长度
if (strlen(buff) > USART3_REC_LEN)
i = USART3_REC_LEN; // 如果长度大于最大值,则长度等于最大值(多出部分忽略)
UART3.WIFI_printf("AT+CIPSEND=%d\r\n", i); // 先发送AT指令和数据数量
HAL_Delay(100); // 毫秒延时等待WIFI模块返回">",此处没做返回是不是">"的判断。稳定性要求高的项目要另加判断。
HAL_UART_Transmit(&huart3, (uint8_t *)buff, i, 0xffff); // 发送数据内容(串口号,内容,数量,溢出时间)
va_end(arg_ptr);
}
实验现象
通过阿里云发布消息,单片机会收到
单片机发布消息,阿里云会收到
- 上传温湿度/其他状态 到阿里云
- 首先在阿里云【设备】那创建自定义功能,这里示例创建一个温度,一个开关,
标识符唯一的每一个设备,不能中文,创建完点击发布即可
- 然后我们需要上传则需要去看上传的头,这两个,一个是上报对应温度一个是设置对应按钮,把这个头复制到程序里即可
- 调试,查看格式,需要设备在线才能调试,可以先使用 MQTT.fx连接阿里云先,然后在阿里云里进行调试即可,然后可以把获得的格式复制到MQTT.fx进行测试,改一下数值发送看看,这个格式就是单片机发送的格式我们需要复制到单片机即
单独设置,这样的消息格式则只有温度,开关则不在里面
也可以自由组合,像这样的话,则温度和湿度一起组合,格式就不一样了,id也不一样这个需要注意,得到这个格式就可以像之前那样去MQTT.fx进行粘贴调试看看效果,然后再粘贴到单片机程序里
实验现象
- 上传温湿度/其他状态 到阿里云,且手机APP也能同时收到
这个需要创建两个设备,一个是单片机连接用,一个是手机APP连接用,然后手机端订阅主题,单片机负责发送数据到模型显示和发送主题那,这样就实现了模型那会显示数据,手机APP也能同时显示【在上面的基础上搞,已经存在一个名为【ESP8266】的设备了
用产品里的自定义Topic,其中的发布类Topic来发布数据,用订阅类Topic来订阅数据,来进行多个设备之间的数据的传递
- 创建第二个设备【my_app】,然后推荐使用
技小新-MQTT单片机编程小工具,直接把三元组复制到那生成即可
- 安卓程序里修改,主要修改这几个,然后下面那个是数据的接收处理,看实际情况修改即可,host那需要注意前面必须要有
tcp://,不能去掉,不然连接不上阿里云!,只需要替换它后面部分即可,订阅主题那写get
- 开通一个云产品流转服务,按要求设置,注意要选 【get】,操作那如果不止2个设备可以多加几个设备,最后开启服务即可
- 单片机程序
连接巴法云
- 前往 巴法云注册,新建主题即可
- 打开 MQTT.fx,配置文件填写信息,TLS/SSL 那不要勾,Use Username/Password那也不需要勾
- 连接,订阅主题即可
- 程序编写
待加


























