esp32是一款很适合初学者学习的芯片,集成了wifi和蓝牙功能,相对丰富的I/O资源。esp32开发板通常作为单片机初学者的学习首选,开发简单,集成环境好。通常开发esp32采用Arduino 、VS Code等作为其首选开发环境,具体软件的安装使用不在讲述。
之前功能介绍中介绍了希望通过wifi连接OneNet,上传温度数据以及OneNet下发led控制指令的功能。具体esp32 连接wifi ,连接Onenet 之前也不是很了解,在这里感谢此作者:ESP32连接新版OneNET完整教程_1属性上报_哔哩哔哩_bilibili
学习单片机,PCB设计,我们不会时可以吸取别人的经验,转换成能够完成自己项目的功能。没有人是天生会这些东西的,我们都是在前人的基础上一步一步走过来的,不是常说软件开发就是 C+V吗?自己不会又不借鉴别人的实现方式,那只能放弃了。学习单片机、PCB设计本来就很枯燥,别自己给自己设计障碍,当不会时就借鉴,然后理解转化为自己的经验积累。只有先实现功能,成功了看到结果了,才会极大的激发出我们的兴趣,才能激励我们一步一步的深入的探究和学习更深层次的东西。
调试stm32与esp32 uart接口自定义通讯,stm32与at24c02 I2c接口通信时,或者spi接口通讯时,我们要学会使用工具,万用表测量PCB板,示波器测量信号,测量uart ,I2C,SPI接口等,或者使用逻辑分析仪测量,这些工具在我们调试功能,处理问题中起到了关键性的作用,一定要学会使用工具分析问题,查找问题的能力。还要学会合理的使用打印log,这也是很有必要的。
esp32相对stm32开发比较简单,下面提供相应的代码,后续会演示整体的实现效果。
#include "Arduino.h"
#include <HardwareSerial.h>
#include <PubSubClient.h>
#include <ArduinoJson.h>
#include <WiFi.h>
#include <Ticker.h>
#include "time.h"
#define LEDPin 18
enum{ LEDON=0, LEDOFF};
#define U2RXDPin 16
#define U2TXDPin 17
#define PRODUCT_id "XXXX"
#define DEVICE_id "XXXX"
#define TOKEN "XXXXXXXXXXXX"
//1.设备属性上报 设备--> OneNet
#define ONENET_TOPIC_PROP_POST "$sys/"PRODUCT_id"/"DEVICE_id"/thing/property/post"
//响应Topic OneNet--> 设备
#define ONENET_TOPIC_PROP_POST_REPLY "$sys/"PRODUCT_id"/"DEVICE_id"/thing/property/post/reply"
//2.设备属性设置 OneNet-->设备
#define ONENET_TOPIC_PROP_SET "$sys/"PRODUCT_id"/"DEVICE_id"/thing/property/set"
//响应Topic 设备-->OneNet
#define ONENET_TOPIC_PROP_SET_REPLY "$sys/"PRODUCT_id"/"DEVICE_id"/thing/property/set_reply"
//3.设备属性获取 OneNet -->设备
#define ONENET_TOPIC_PROP_GET "$sys/"PRODUCT_id"/"DEVICE_id"/thing/property/get"
//响应Topic 设备-->OneNet
#define ONENET_TOPIC_PROP_GET_REPLY "$sys/"PRODUCT_id"/"DEVICE_id"/thing/property/get_reply"
#define ONENET_TOPIC_PROP_FORMAT "{\"id\":\"%u\",\"version\":\"1.0\",\"params\":%s}"
#define MQTT_SERVER "mqtts.heclouds.com" //mqtt服务器地址
#define MQTT_PORT 1883 //mqtt服务器端口
//定义Command
enum{
Send_SsidPassword = 0x00, //STM32 send wifi ssid password to ESP32
Get_SsidPassword = 0x01, //ESP32 send command to STM32 for get wifi ssid password
Set_LedOn = 0x02, //IOT net from ESP32 send command to STM32 let LED ON
Set_LedOff = 0x03, //IOT net from ESP32 send command to STM32 let LED OFF
Get_Temperature = 0x04, //IOT net from ESP32 send command to STM32 get Temperature
Up_Temperature = 0x05, //STM32 send Temperature to ESP32 ,Then ESP32 send Temperature to IOT net
Get_Time = 0x06, //STM32 send command to ESP32 for get time from NET
Down_Time = 0x07, //ESP32 send time data to STM32
Get_Weather = 0x08, //STM32 send command to ESP32 for get weather from NET
Down_Weather = 0x09, //ESP32 send Weather to STM32
};
#define WifiConfigDatMax 20
struct WifiConfigData{
char ssid[WifiConfigDatMax];
char password[WifiConfigDatMax];
bool wifiFlag;
};
char strCommand[256]={0}; //定义指令转换数组
struct WifiConfigData stWifiConfig={0}; //定义wifi数据结构体
struct tm timeinfo; //定义时间time结构体
float fTempreatureDat =0.0; //存放温度数据
int postMsgID = 0; //MsgID
bool led_status =0; //led on/off状态
WiFiClient espClient; //创建一个WiFiClient对象
PubSubClient client(espClient); //创建一个PubSubClient对象
Ticker tTicker; //创建一个Ticker对象
Ticker tNetTimeTicker; //创建一个Ticker对象
//函数声明
void SendGetTemperatureCommand(void);
void SendGetWifiSsidParsswordCommand(void);
void SendLedonOroffCommand(byte lednum, byte ledstate);
void TimeNetworkCOnfigration(void);
void GetNetTime(void);
void GetNetTimeThenSendTime(void);
void Wifi_Connect(void);
void LedFlash(int itime);
void OneNet_Connect(void);
void OneNet_PropPost(void);
void TopicCallback(char* topic, byte* payload, unsigned int length); //接收订阅后回调函数
void setup() {
pinMode(LEDPin, OUTPUT);
// initialize usart2 115200 8bit-data 1-stopbit no
//uart0默认 8位数据位 1位停止位 无奇偶校验位
Serial.begin(115200);
//uart2 设置
Serial2.begin(115200,SERIAL_8N1,U2RXDPin,U2TXDPin,false,1000);
//Wifi连接 : 无法连接wifi-5G :ssid - 名称 password - 密码
if( stWifiConfig.wifiFlag != true )
{
SendGetWifiSsidParsswordCommand(); //从stm32获取ssid password
}
//连接Wifi
Wifi_Connect();
//配置网络时间获取地址
TimeNetworkCOnfigration();
//中移onenet
OneNet_Connect();
//定时器,每5ms 执行一次OneNet_PropPost()
tTicker.attach(20,OneNet_PropPost);
//定时器,每5ms 执行一次 //循环检测是否需要联网获取时间
tNetTimeTicker.attach(3,GetNetTimeThenSendTime);
}
void loop() {
if(WiFi.status() != WL_CONNECTED)
{
Wifi_Connect();
}
if( !client.connected())
{
OneNet_Connect();
}
client.loop(); //保持MQTT连接
}
//Wifi连接
void Wifi_Connect(void)
{
WiFi.begin(stWifiConfig.ssid,stWifiConfig.password);
Serial.println("Connecting to wifi......");
while( WiFi.status() != WL_CONNECTED )
{
//LED灯闪烁 并打印信息
LedFlash(500);
Serial.print("...");
}
Serial.println("Connected to the wifi network !!!");
//打印IP地址
Serial.println(WiFi.localIP());
//LED亮
digitalWrite(LEDPin,HIGH);
}
//中移OneNet连接
void OneNet_Connect(void)
{
//设置连接服务器的地址和端口
client.setServer(MQTT_SERVER, MQTT_PORT);
//连接中移OneNet
client.connect(DEVICE_id, PRODUCT_id, TOKEN);
if(client.connected())
{
//LED灯闪烁 并打印信息
LedFlash(500);
Serial.println("Connected to OneNet !!!");
}
else{
Serial.println("Connect to OneNet Failed ......");
}
//设置订阅设备属性和请求
client.subscribe(ONENET_TOPIC_PROP_SET);
client.subscribe(ONENET_TOPIC_PROP_POST_REPLY);
//订阅命令下发的主题
client.setCallback(TopicCallback);
}
//上报温度to OneNet
void OneNet_PropPost(void)
{
if(client.connected())
{
char params[256];
char jsonBuf[256];
//获取stm32 M1601B温度 -- fTempreatureDat
SendGetTemperatureCommand();
sprintf(params,"{\"temp\":{\"value\":%.1f},\"led0\":{\"value\":%s}}", fTempreatureDat, led_status ? "true":"false");
Serial.println(params);
//组合json
sprintf(jsonBuf,ONENET_TOPIC_PROP_FORMAT, postMsgID++, params);
Serial.println(jsonBuf);
if(client.publish(ONENET_TOPIC_PROP_POST, jsonBuf))
{
//LED灯闪烁 并打印信息
LedFlash(100);
Serial.println("\nPost property success !!!");
}
}
}
void TopicCallback(char* topic, byte* payload, unsigned int length)
{
//打印topic
Serial.print("Message arrived [");
Serial.print(topic);
Serial.print("]");
//打印消息内容
for(unsigned int i=0; i<length; i++)
{
Serial.print((char)payload[i]);
}
Serial.println();
LedFlash(100);//标识接受到消息
//消息处理
if(strcmp(topic, ONENET_TOPIC_PROP_SET) == 0 )
{
DynamicJsonDocument Jsondoc(100);//创建一个JSON文档,用于解析消息内容
DeserializationError error = deserializeJson(Jsondoc,payload); //解析消息内容并存放于Jsondoc
if(error)
{
Serial.print(F("deserializeJson() failed: "));
Serial.println(error.c_str());
return;
}
//获取JSON文档的根对象
JsonObject setAlinkMsgObj = Jsondoc.as<JsonObject>();//获取JSON文档的根对象
JsonObject params = setAlinkMsgObj["params"];//获取params对象
if(params.containsKey("led0"))
{
//获取led0属性值
led_status = params["led0"];//获取led0属性值
Serial.print("Set led0 :");
Serial.println(led_status); //打印led0状态
//发送指令控制stm32 LED0 亮灭
if( true == led_status )
{
SendLedonOroffCommand(0, LEDON);
}
else{
SendLedonOroffCommand(0, LEDOFF);
}
}
//打印Json文档
serializeJsonPretty(setAlinkMsgObj, Serial);
//获取消息ID
String strID = setAlinkMsgObj["id"];
char JsendBuf[100];
//设置消息响应 : 格式如下{ "id":"123", "code":200, "msg":"success"}
//注意:一定要关注响应OneJson数据格式,否则会出现OneNet接收响应失败
sprintf(JsendBuf,"{\"id\":\"%s\",\"code\":200,\"msg\":\"success\"}",strID.c_str());
//打印响应数据
Serial.println(JsendBuf);
//发送响应
if( client.publish(ONENET_TOPIC_PROP_SET_REPLY, JsendBuf))
{
Serial.println("Send set_reply success !!!");
}
else{
Serial.println("Send set_reply failed ....");
}
}
}
//当uCommadOverFlag == 0x01 处理指令数据
void ProcessingCommand(void)
{
byte uTlen =0,uOffset=0;
switch(strCommand[2])
{
case Send_SsidPassword: //Wifi ssid password
//检测数组中'\0' 获取字符串 Wifi ssid and password : "WiFi" "123456" --字符串'\0'结束
while(strCommand[uTlen+3] !='\0')
{
uTlen++;
}
uOffset = uTlen+1;
//wifi ssid
memcpy(stWifiConfig.ssid,(char*)strCommand+3,uOffset);
//wifi password
memcpy(stWifiConfig.password,(char*)strCommand+3+uOffset, strCommand[1]-1-uOffset);
//wifi flag
stWifiConfig.wifiFlag = true; //标识wifi ssid and password save
break;
case Up_Temperature:
//赋值tempreature --- 注意大小端 --stm32小端模式
memcpy(&fTempreatureDat,strCommand+3, strCommand[1]-1);
//打印
Serial.print("The get Tempreatrue is:");
Serial.println(fTempreatureDat);
break;
case Get_Time:
//获取网络时间
GetNetTime();
//发送网络时间
SendCommandReassembly(Down_Time, NULL);
break;
default:
break;
}
}
//esp32发送stm32命令重装
void SendCommandReassembly(byte uCommandName, byte* upCommandData)
{
//初始化strCommand
memset(strCommand,0,sizeof(strCommand));
//Command
strCommand[0] = 0xA5;
switch(uCommandName)
{
case Get_SsidPassword:
case Get_Temperature :
strCommand[1] = 0x01;
strCommand[2] = uCommandName;
break;
case Set_LedOff:
case Set_LedOn :
strCommand[1] = 0x02;
strCommand[2] = uCommandName;
strCommand[3] = upCommandData[0];
break;
case Down_Time:
strCommand[1]= sizeof(timeinfo)+1;
strCommand[2]= uCommandName;
memcpy(strCommand+3, (char*)&timeinfo, sizeof(timeinfo));
break;
default:
break;
}
//uart2 send command
Serial2.write(strCommand,strCommand[1]+2);
}
//获取stm32传输的命令
bool GetCommand(void)
{
byte udatbuf[256]={0},uLen=0, uTime=0;
//检查串口uart2是否有可用数据
while( !Serial2.available() && uTime <20)
{
uTime++;
delay(50);
}
if(uTime < 20)
{
uLen = Serial2.available();
Serial.printf("\nGet the read data len is %02d\n",uLen);
if(uLen > 0)
{
Serial2.readBytes(udatbuf,uLen); //获取数据
if( udatbuf[0] == 0xA5 )
{
if(udatbuf[1] > 0 )
{
memcpy(strCommand, udatbuf, uLen);
}
}
}
}
else{
Serial.println("\n No read data ..........");
return false;
}
return true;
}
//LED闪烁
void LedFlash(int itime)
{
digitalWrite(LEDPin,HIGH);
delay(itime);
digitalWrite(LEDPin,LOW);
delay(itime);
}
//esp32 send command to stm32 for get ssid and password
void SendGetWifiSsidParsswordCommand(void)
{
//重载命令
SendCommandReassembly(Get_SsidPassword, NULL);
//接收数据
if(true == GetCommand())
{
//处理指令数据
ProcessingCommand();
}
}
//配置网络获取时间网址
void TimeNetworkCOnfigration(void)
{
//使用阿里云时间
configTime(60*60*8, 0,"ntp1.aliyun.com","ntp2.aliyun.com", "ntp3.aliyun.com");
}
//网络获取当前时间
void GetNetTime(void)
{
if(WiFi.status() == WL_CONNECTED)
{
if(!getLocalTime(&timeinfo))
{
Serial.println("Failed to obtain time");
}
Serial.println(&timeinfo, "%A, %Y-%m-%d %H:%M:%S");
}
}
void GetNetTimeThenSendTime(void)
{
if(true == GetCommand())
{
//处理指令数据
ProcessingCommand();
}
}
//传输LED控制命令
void SendLedonOroffCommand(byte lednum, byte ledstate)
{
// A5 02 01 00 -- led0 on or A5 02 01 80 --- led0 off
// bit0~bit6 ----led num , bit8--- 0 --led on ; 1---led off
byte ledDat;
if(ledstate == LEDON)
{
ledDat = lednum;
SendCommandReassembly(Set_LedOn, &ledDat);
}
else if(ledstate == LEDOFF){
ledDat = (lednum | 0x80);
SendCommandReassembly(Set_LedOff, &ledDat);
}
}
//获取当前温度命令
void SendGetTemperatureCommand(void)
{
//重载命令
SendCommandReassembly(Get_Temperature, NULL);
//接收数据
if(true == GetCommand())
{
//处理指令数据
ProcessingCommand();
}
}