赞
踩
本项目芯片使用STM32F103ZET6,微信小程序开发使用微信开发者工具。
stm32作为下位机,功能是每过一段时间上传温湿度以及光照度给mqtt服务器,然后微信小程序从mqtt服务器订阅对应的主题来接收下位机发过来的数据并进行处理,并在微信小程序中设置LED开关,以及蜂鸣器开关。

如图,汽车上有联网芯片,可以充当客户端,汽车采集到的数据可以发送到mqtt服务器,然后再搭建其他mqtt客户端(电脑,手机等设备),这些设备就可以从mqtt服务器获取汽车的数据。
这里的单片机和微信小程序都是客服端,他们连接上同一个服务器,然后通过订阅/发布对应的主题,来进行信息的交互。
主题:
用来区别接收的数据。比如汽车采集的速度,要传给mqtt服务器,就要给数据打上一个标签,即主题,然后其他mqtt客户端可以根据主题来获取数据。
发布:
客服端发布数据给服务端,并标上主题。
订阅:
客户端订阅主题,然后获取主题对应的数据。
每一个客户端都可以订阅/发布数据,即数据可以互相发送。
main.c
#include "led.h" #include "delay.h" #include "sys.h" #include "lcd.h" #include "usart.h" #include "beep.h" #include "adc.h" #include "lsens.h" #include "esp8266.h" #include "MqttKit.h" #include "onenet.h" uint8_t Usart_String[50]; uint8_t * dataPtr; uint8_t tem_light_string[50]; const char *subtopics[] = {"/iot/2462/sub/wsy"};//订阅主题 const char pubtopics[] = {"/iot/2462/pub/wsy"};//发布主题 void dht11_exit()//判断dht11是否存在 { while(DHT11_Init()) //DHT11初始化 { LCD_ShowString(30,130,200,16,16,"DHT11 Error"); delay_ms(200); LCD_Fill(30,130,239,130+16,WHITE); delay_ms(200); } } void Hardware_Init(void) { NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //中断控制器分组设置 Usart1_Init(115200); //串口1,打印信息用 Usart2_Init(115200); //串口2,驱动ESP8266用 BEEP_Init(); //蜂鸣器初始化 UsartPrintf(USART_DEBUG, " Hardware init OK\r\n"); } int main(void) { u8 timeCount=0; u8 temperature; //存放温度的值 u8 humidity; //存放湿度的值 u8 adcx;//存放光照值 LED_Init(); //LED端口初始化 delay_init(); //延时函数初始化 Hardware_Init(); LCD_Init(); //LCD初始化 LCD_Clear(WHITE); Lsens_Init(); //初始化光敏传感器 LCD_ShowString(30,130,200,16,16,"DHT11 OK"); POINT_COLOR=BLUE;//设置字体为蓝色 LCD_ShowString(30,150,200,16,16,"Temp: C"); LCD_ShowString(30,170,200,16,16,"Humi: %"); LCD_ShowString(30,190,200,16,16,"LSENS_VAL:"); dht11_exit(); ESP8266_Init(); while(OneNet_DevLink()) //接入OneNET delay_ms(500); // BEEP=1; //鸣叫提示接入成功 // delay_ms(250); // BEEP=0; OneNet_Subscribe(subtopics, 1); while(1) { if(timeCount%40==0) //每100ms读取一次 { DHT11_Read_Data(&temperature,&humidity); //读取温湿度值 adcx=Lsens_Get_Val(); //读取光照强度 LCD_ShowNum(30+40,150,temperature,2,16); //显示温度 LCD_ShowNum(30+40,170,humidity,2,16); //显示湿度 LCD_ShowNum(30+10*8,190,adcx,3,16);//显示光照度 } if(++timeCount >= 200) //发送间隔5s 5000ms/25ms=200 { UsartPrintf(USART_DEBUG, "OneNet_Publish\r\n"); // OneNet_Publish((char *)pubtopics, "MQTT Publish Test"); sprintf((char *)tem_light_string,"{\"tem\":%02d,\"hum\":%02d,\"light\":%d}",temperature,humidity,adcx); OneNet_Publish((char *)pubtopics, tem_light_string); timeCount = 0; ESP8266_Clear(); } dataPtr = ESP8266_GetIPD(3); if(dataPtr != NULL) OneNet_RevPro(dataPtr); delay_ms(10); } }
这里移植了Onenet平台的stm32芯片esp8266连接服务器的代码。
有需要的小伙伴可进入连接获取添加链接描述
esp8266.c
/** ************************************************************ ************************************************************ ************************************************************ * 文件名: esp8266.c * * 作者: 张继瑞 * * 日期: 2017-05-08 * * 版本: V1.0 * * 说明: ESP8266的简单驱动 * * 修改记录: ************************************************************ ************************************************************ ************************************************************ **/ //单片机头文件 #include "stm32f10x.h" //网络设备驱动 #include "esp8266.h" //硬件驱动 #include "delay.h" #include "usart.h" //C库 #include <string.h> #include <stdio.h> #define ESP8266_WIFI_INFO "AT+CWJAP=\"xxx\",\"xxxxxx\"\r\n" #define ESP8266_ONENET_INFO "AT+CIPSTART=\"TCP\",\"xxxxxxx\",xxxxx\r\n" unsigned char esp8266_buf[128]; unsigned short esp8266_cnt = 0, esp8266_cntPre = 0; //========================================================== // 函数名称: ESP8266_Clear // // 函数功能: 清空缓存 // // 入口参数: 无 // // 返回参数: 无 // // 说明: //========================================================== void ESP8266_Clear(void) { memset(esp8266_buf, 0, sizeof(esp8266_buf)); esp8266_cnt = 0; } //========================================================== // 函数名称: ESP8266_WaitRecive // // 函数功能: 等待接收完成 // // 入口参数: 无 // // 返回参数: REV_OK-接收完成 REV_WAIT-接收超时未完成 // // 说明: 循环调用检测是否接收完成 //========================================================== _Bool ESP8266_WaitRecive(void) { if(esp8266_cnt == 0) //如果接收计数为0 则说明没有处于接收数据中,所以直接跳出,结束函数 return REV_WAIT; if(esp8266_cnt == esp8266_cntPre) //如果上一次的值和这次相同,则说明接收完毕 { esp8266_cnt = 0; //清0接收计数 return REV_OK; //返回接收完成标志 } esp8266_cntPre = esp8266_cnt; //置为相同 return REV_WAIT; //返回接收未完成标志 } //========================================================== // 函数名称: ESP8266_SendCmd // // 函数功能: 发送命令 // // 入口参数: cmd:命令 // res:需要检查的返回指令 // // 返回参数: 0-成功 1-失败 // // 说明: //========================================================== _Bool ESP8266_SendCmd(char *cmd, char *res) { unsigned char timeOut = 200; Usart_SendString(USART2, (unsigned char *)cmd, strlen((const char *)cmd)); while(timeOut--) { if(ESP8266_WaitRecive() == REV_OK) //如果收到数据 { if(strstr((const char *)esp8266_buf, res) != NULL) //如果检索到关键词 { ESP8266_Clear(); //清空缓存 return 0; } } delay_ms(10); } return 1; } //========================================================== // 函数名称: ESP8266_SendData // // 函数功能: 发送数据 // // 入口参数: data:数据 // len:长度 // // 返回参数: 无 // // 说明: //========================================================== void ESP8266_SendData(unsigned char *data, unsigned short len) { char cmdBuf[32]; ESP8266_Clear(); //清空接收缓存 sprintf(cmdBuf, "AT+CIPSEND=%d\r\n", len); //发送命令 if(!ESP8266_SendCmd(cmdBuf, ">")) //收到‘>’时可以发送数据 { Usart_SendString(USART2, data, len); //发送设备连接请求数据 } } //========================================================== // 函数名称: ESP8266_GetIPD // // 函数功能: 获取平台返回的数据 // // 入口参数: 等待的时间(乘以10ms) // // 返回参数: 平台返回的原始数据 // // 说明: 不同网络设备返回的格式不同,需要去调试 // 如ESP8266的返回格式为 "+IPD,x:yyy" x代表数据长度,yyy是数据内容 //========================================================== unsigned char *ESP8266_GetIPD(unsigned short timeOut) { char *ptrIPD = NULL; do { if(ESP8266_WaitRecive() == REV_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); } else return NULL; } } delay_ms(5); //延时等待 } while(timeOut--); return NULL; //超时还未找到,返回空指针 } //========================================================== // 函数名称: ESP8266_Init // // 函数功能: 初始化ESP8266 // // 入口参数: 无 // // 返回参数: 无 // // 说明: //========================================================== void ESP8266_Init(void) { GPIO_InitTypeDef GPIO_Initure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //ESP8266复位引脚 GPIO_Initure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_Initure.GPIO_Pin = GPIO_Pin_14; //GPIOC14-复位 GPIO_Initure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOC, &GPIO_Initure); GPIO_WriteBit(GPIOC, GPIO_Pin_14, Bit_RESET); delay_ms(250); GPIO_WriteBit(GPIOC, GPIO_Pin_14, Bit_SET); delay_ms(500); ESP8266_Clear(); UsartPrintf(USART_DEBUG, "0. AT\r\n"); while(ESP8266_SendCmd("AT\r\n", "OK")) delay_ms(500); UsartPrintf(USART_DEBUG, "1. RST\r\n"); ESP8266_SendCmd("AT+RST\r\n", ""); delay_ms(500); ESP8266_SendCmd("AT+CIPCLOSE\r\n", ""); delay_ms(500); UsartPrintf(USART_DEBUG, "2. CWMODE\r\n"); while(ESP8266_SendCmd("AT+CWMODE=1\r\n", "OK")) delay_ms(500); UsartPrintf(USART_DEBUG, "3. AT+CWDHCP\r\n"); while(ESP8266_SendCmd("AT+CWDHCP=1,1\r\n", "OK")) delay_ms(500); UsartPrintf(USART_DEBUG, "4. CWJAP\r\n"); while(ESP8266_SendCmd(ESP8266_WIFI_INFO, "GOT IP")) delay_ms(500); UsartPrintf(USART_DEBUG, "5. CIPSTART\r\n"); while(ESP8266_SendCmd(ESP8266_ONENET_INFO, "CONNECT")) delay_ms(500); UsartPrintf(USART_DEBUG, "6. ESP8266 Init OK\r\n"); } //========================================================== // 函数名称: USART2_IRQHandler // // 函数功能: 串口2收发中断 // // 入口参数: 无 // // 返回参数: 无 // // 说明: //========================================================== void USART2_IRQHandler(void) { if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET) //接收中断 { if(esp8266_cnt >= sizeof(esp8266_buf)) esp8266_cnt = 0; //防止串口被刷爆 esp8266_buf[esp8266_cnt++] = USART2->DR; USART_ClearFlag(USART2, USART_FLAG_RXNE); } }
#define ESP8266_WIFI_INFO “AT+CWJAP=“xxx”,“xxxxxx”\r\n” //填入你要连接的wifi名字和密码
#define ESP8266_ONENET_INFO “AT+CIPSTART=“TCP”,“xxxxxxx”,xxxxx\r\n”//填入你要连接的服务器域名和端口号
onenet.c
/** ************************************************************ ************************************************************ ************************************************************ * 文件名: onenet.c * * 作者: 张继瑞 * * 日期: 2017-05-08 * * 版本: V1.1 * * 说明: 与onenet平台的数据交互接口层 * * 修改记录: V1.0:协议封装、返回判断都在同一个文件,并且不同协议接口不同。 * V1.1:提供统一接口供应用层使用,根据不同协议文件来封装协议相关的内容。 ************************************************************ ************************************************************ ************************************************************ **/ //单片机头文件 #include "stm32f10x.h" //网络设备 #include "esp8266.h" //协议文件 #include "onenet.h" #include "mqttkit.h" //硬件驱动 #include "usart.h" #include "delay.h" #include "led.h" #include "beep.h" //C库 #include <string.h> #include <stdio.h> #include "cJSON.h" #define PROID "0a15a6464f0fee359eadb8992ebb72d7"//设备号ID #define AUTH_INFO "123456"//密码 #define DEVID "11"//随便写 extern unsigned char esp8266_buf[128]; //========================================================== // 函数名称: OneNet_DevLink // // 函数功能: 与onenet创建连接 // // 入口参数: 无 // // 返回参数: 1-成功 0-失败 // // 说明: 与onenet平台建立连接 //========================================================== _Bool OneNet_DevLink(void) { MQTT_PACKET_STRUCTURE mqttPacket = {NULL, 0, 0, 0}; //协议包 unsigned char *dataPtr; _Bool status = 1; UsartPrintf(USART_DEBUG, "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:UsartPrintf(USART_DEBUG, "Tips: 连接成功\r\n");status = 0;break; case 1:UsartPrintf(USART_DEBUG, "WARN: 连接失败:协议错误\r\n");break; case 2:UsartPrintf(USART_DEBUG, "WARN: 连接失败:非法的clientid\r\n");break; case 3:UsartPrintf(USART_DEBUG, "WARN: 连接失败:服务器失败\r\n");break; case 4:UsartPrintf(USART_DEBUG, "WARN: 连接失败:用户名或密码错误\r\n");break; case 5:UsartPrintf(USART_DEBUG, "WARN: 连接失败:非法链接(比如token非法)\r\n");break; default:UsartPrintf(USART_DEBUG, "ERR: 连接失败:未知错误\r\n");break; } } } MQTT_DeleteBuffer(&mqttPacket); //删包 } else UsartPrintf(USART_DEBUG, "WARN: MQTT_PacketConnect Failed\r\n"); return status; } //========================================================== // 函数名称: OneNet_Subscribe // // 函数功能: 订阅 // // 入口参数: topics:订阅的topic // topic_cnt:topic个数 // // 返回参数: SEND_TYPE_OK-成功 SEND_TYPE_SUBSCRIBE-需要重发 // // 说明: //========================================================== void OneNet_Subscribe(const char *topics[], unsigned char topic_cnt) { unsigned char i = 0; MQTT_PACKET_STRUCTURE mqttPacket = {NULL, 0, 0, 0}; //协议包 for(; i < topic_cnt; i++) UsartPrintf(USART_DEBUG, "Subscribe Topic: %s\r\n", topics[i]); if(MQTT_PacketSubscribe(MQTT_SUBSCRIBE_ID, MQTT_QOS_LEVEL2, topics, topic_cnt, &mqttPacket) == 0) { ESP8266_SendData(mqttPacket._data, mqttPacket._len); //向平台发送订阅请求 MQTT_DeleteBuffer(&mqttPacket); //删包 } } //========================================================== // 函数名称: OneNet_Publish // // 函数功能: 发布消息 // // 入口参数: topic:发布的主题 // msg:消息内容 // // 返回参数: SEND_TYPE_OK-成功 SEND_TYPE_PUBLISH-需要重送 // // 说明: //========================================================== void OneNet_Publish(const char *topic, const char *msg) { MQTT_PACKET_STRUCTURE mqttPacket = {NULL, 0, 0, 0}; //协议包 UsartPrintf(USART_DEBUG, "Publish Topic: %s, Msg: %s\r\n", topic, msg); if(MQTT_PacketPublish(MQTT_PUBLISH_ID, topic, msg, strlen(msg), MQTT_QOS_LEVEL2, 0, 1, &mqttPacket) == 0) { ESP8266_SendData(mqttPacket._data, mqttPacket._len); //向平台发送订阅请求 MQTT_DeleteBuffer(&mqttPacket); //删包 } } //========================================================== // 函数名称: 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 topic_len = 0; unsigned short req_len = 0; unsigned char type = 0; unsigned char qos = 0; static unsigned short pkt_id = 0; short result = 0; char *dataPtr = NULL; char numBuf[10]; int num = 0; cJSON * json,*json_value; type = MQTT_UnPacketRecv(cmd); switch(type) { case MQTT_PKT_CMD: //命令下发 result = MQTT_UnPacketCmd(cmd, &cmdid_topic, &req_payload, &req_len); //解出topic和消息体 if(result == 0) { UsartPrintf(USART_DEBUG, "cmdid: %s, req: %s, req_len: %d\r\n", cmdid_topic, req_payload, req_len); MQTT_DeleteBuffer(&mqttPacket); //删包 } break; case MQTT_PKT_PUBLISH: //接收的Publish消息 result = MQTT_UnPacketPublish(cmd, &cmdid_topic, &topic_len, &req_payload, &req_len, &qos, &pkt_id); if(result == 0) { UsartPrintf(USART_DEBUG, "topic: %s, topic_len: %d, payload: %s, payload_len: %d\r\n", cmdid_topic, topic_len, req_payload, req_len); //对数据包进行JSON格式解析 json = cJSON_Parse(req_payload); if(!json)UsartPrintf(USART_DEBUG,"Error before: [%s]\n",cJSON_GetErrorPtr()); else { // //解析开关值 // json_value = cJSON_GetObjectItem(json,"LED_SW"); // if(json_value -> valueint)//json_value大于等于0,且为整型 // { // LED0= 0;//打开LED0 // } // else // { // LED0= 1;//关闭LED0 // } //解析开关值 json_value=cJSON_GetObjectItem(json,"target"); UsartPrintf(USART_DEBUG,"json_value= %d\r\n",json_value->string);//先把键值输出来,就是看看叫什么名字 UsartPrintf(USART_DEBUG,"json_value= %d\r\n",json_value->valuestring);//接着看看数值是多少 //从value-int中获取结果 if(strstr(json_value->valuestring,"led")!=NULL)//标准字符串判断,这个就是判断是不是控制LED的,如果是,就是控制LED,如果不是就是控制蜂鸣器 { json_value=cJSON_GetObjectItem(json,"value"); if(json_value->valueint) LED1=0; else LED1=1; } else { json_value=cJSON_GetObjectItem(json,"value"); if(json_value->valueint) BEEP=1;//蜂鸣器鸣叫 else BEEP=0;//蜂鸣器停止鸣叫 } } cJSON_Delete(json); switch(qos) { case 1: //收到publish的qos为1,设备需要回复Ack if(MQTT_PacketPublishAck(pkt_id, &mqttPacket) == 0) { UsartPrintf(USART_DEBUG, "Tips: Send PublishAck\r\n"); ESP8266_SendData(mqttPacket._data, mqttPacket._len); MQTT_DeleteBuffer(&mqttPacket); } break; case 2: //收到publish的qos为2,设备先回复Rec //平台回复Rel,设备再回复Comp if(MQTT_PacketPublishRec(pkt_id, &mqttPacket) == 0) { UsartPrintf(USART_DEBUG, "Tips: Send PublishRec\r\n"); ESP8266_SendData(mqttPacket._data, mqttPacket._len); MQTT_DeleteBuffer(&mqttPacket); } break; default: break; } } break; case MQTT_PKT_PUBACK: //发送Publish消息,平台回复的Ack if(MQTT_UnPacketPublishAck(cmd) == 0) UsartPrintf(USART_DEBUG, "Tips: MQTT Publish Send OK\r\n"); break; case MQTT_PKT_PUBREC: //发送Publish消息,平台回复的Rec,设备需回复Rel消息 if(MQTT_UnPacketPublishRec(cmd) == 0) { UsartPrintf(USART_DEBUG, "Tips: Rev PublishRec\r\n"); if(MQTT_PacketPublishRel(MQTT_PUBLISH_ID, &mqttPacket) == 0) { UsartPrintf(USART_DEBUG, "Tips: Send PublishRel\r\n"); ESP8266_SendData(mqttPacket._data, mqttPacket._len); MQTT_DeleteBuffer(&mqttPacket); } } break; case MQTT_PKT_PUBREL: //收到Publish消息,设备回复Rec后,平台回复的Rel,设备需再回复Comp if(MQTT_UnPacketPublishRel(cmd, pkt_id) == 0) { UsartPrintf(USART_DEBUG, "Tips: Rev PublishRel\r\n"); if(MQTT_PacketPublishComp(MQTT_PUBLISH_ID, &mqttPacket) == 0) { UsartPrintf(USART_DEBUG, "Tips: Send PublishComp\r\n"); ESP8266_SendData(mqttPacket._data, mqttPacket._len); MQTT_DeleteBuffer(&mqttPacket); } } break; case MQTT_PKT_PUBCOMP: //发送Publish消息,平台返回Rec,设备回复Rel,平台再返回的Comp if(MQTT_UnPacketPublishComp(cmd) == 0) { UsartPrintf(USART_DEBUG, "Tips: Rev PublishComp\r\n"); } break; case MQTT_PKT_SUBACK: //发送Subscribe消息的Ack if(MQTT_UnPacketSubscribe(cmd) == 0) UsartPrintf(USART_DEBUG, "Tips: MQTT Subscribe OK\r\n"); else UsartPrintf(USART_DEBUG, "Tips: MQTT Subscribe Err\r\n"); break; case MQTT_PKT_UNSUBACK: //发送UnSubscribe消息的Ack if(MQTT_UnPacketUnSubscribe(cmd) == 0) UsartPrintf(USART_DEBUG, "Tips: MQTT UnSubscribe OK\r\n"); else UsartPrintf(USART_DEBUG, "Tips: MQTT UnSubscribe Err\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++; } num = atoi((const char *)numBuf); //转为数值形式 } if(type == MQTT_PKT_CMD || type == MQTT_PKT_PUBLISH) { MQTT_FreeBuffer(cmdid_topic); MQTT_FreeBuffer(req_payload); } }
#define PROID “0a15a6464f0fee359eadb8992ebb72d7”//一些私人的服务器需要设备号ID
#define AUTH_INFO “123456”//密码
#define DEVID “11”//随便写,这里没有要求
1.微信开发者工具添加链接描述
2.mqtt.js插件 密码6666
3.onenet移植代码用来给下位机连接服务器使用
赞
踩
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。