一个适合单片机初学者的娱乐(三、esp32和OneNet)

        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();
  }
  
}


  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值