前言

电源方向资料免费开源到QQ群1:280730348QQ群2:725438563

嵌入式方向资料免费开源到QQ群1:976387827

博客地址edadong.com,博文同步发布在知乎、bilibili,其中bilibili主要以视频为主。对开源项目的疑惑请尽量在b站下方留言,其次在群内商讨,所有开源项目均自己设计验证过。

由于个人精力有限,以后不做免费的私聊问题回答,但B站我会做最快的响应回答,群内看到了也会回答。如果确实是想私聊问问题,5-15元一次答疑,避免太多人找我,此初衷主要是想更多人看到问题的解决方法或者开源项目这么做的意义,在B站解答大家都能看得到,私人解答就丢失了开源解答的优势了。

更详细的讲解请移步Bilibili:EDAdong。

设计目标和方案

(1)基于Arduino的无线通信系统具备主机和分机模块,所有模块都以Arduino作为主控,两个模块之间通过ZigBee进行通信;

(2)分机可以实现对温度,湿度以及光照强度的检测,并上传至主机模块;

(3)主机模块实现显示,控制以及远程上传;

(4)主机具备WiFi通信模块,并上传至手机APP端,实现远程控制。

注意事项

WIFI功能属于附加功能,可加可不加,不影响功能实现

原理图及实物图

image-20250626202000231

image-20250626202246902

image-20250626203221409

代码

主机代码

#include <Wire.h>  
#include <U8g2lib.h>//u8g2库
#include <SoftwareSerial.h>
/*定义部分引脚*/
#define led 13               //定义LED的驱动引脚
#define rxPin 9
#define txPin 8
SoftwareSerial mySerial(rxPin, txPin);
/*定义运行过程中的参量*/
int wendu;                  //全局变量温度
int shidu;                  //全局变量湿度
int light_strength;         //全局变量光照强度
int led_status=0;           //定义LED灯的工作状态
int wendu_status=0;
char test;
/*新建iic对象以及显示屏对象*/
U8G2_SSD1306_128X64_NONAME_F_SW_I2C u8g2(U8G2_R0,10,11,U8X8_PIN_NONE); //配置OLED函数
//U8X8_SSD1306_128X64_NONAME_SW_I2C u8x8(/* clock=*/ SCL, /* data=*/ SDA, /* reset=*/ U8X8_PIN_NONE);
/************************存放oled汉字取模数据*********************************/
/*******取16X16汉字字模 逐行式 顺向高位在前 阴码,逐行,逆向,16进制,C51格式******/
static const unsigned char PROGMEM str0[] =
{
  0x00,0x00,0x00,0x00,0xBF,0x7F,0x08,0x08,0x08,0x08,0x08,0x04,0x08,0x04,0x3E,0x16,
  0x08,0x25,0x88,0x44,0x48,0x44,0x08,0x04,0x38,0x04,0x07,0x04,0x02,0x04,0x00,0x04};/*"环",0*/
static const unsigned char PROGMEM str1[] =
{
  0x04,0x01,0x04,0x02,0xC4,0x1F,0x84,0x08,0x1F,0x05,0xE4,0x7F,0x04,0x00,0xC4,0x1F,
  0x44,0x10,0xC4,0x1F,0x44,0x10,0xDC,0x1F,0x87,0x04,0x82,0x44,0x40,0x44,0x30,0x78};/*"境",1*/
static const unsigned char PROGMEM str2[] =
{
  0x00,0x00,0xC4,0x1F,0x48,0x10,0x48,0x10,0xC1,0x1F,0x42,0x10,0x42,0x10,0xC8,0x1F,
  0x08,0x00,0xE4,0x3F,0x27,0x25,0x24,0x25,0x24,0x25,0x24,0x25,0xF4,0x7F,0x00,0x00};/*"温",2*/
static const unsigned char PROGMEM str3[] =
{
  0x00,0x00,0xE4,0x1F,0x28,0x10,0x28,0x10,0xE1,0x1F,0x22,0x10,0x22,0x10,0xE8,0x1F,
  0x88,0x04,0x84,0x04,0x97,0x24,0xA4,0x14,0xC4,0x0C,0x84,0x04,0xF4,0x7F,0x00,0x00};/*"湿",3*/
static const unsigned char PROGMEM str4[] =
{
  0x80,0x00,0x84,0x10,0x88,0x10,0x90,0x08,0x90,0x04,0x80,0x00,0xFF,0x7F,0x20,0x02,
  0x20,0x02,0x20,0x02,0x20,0x02,0x10,0x42,0x10,0x42,0x08,0x42,0x04,0x7C,0x03,0x00};/*"光",4*/
static const unsigned char PROGMEM str5[] =
{
  0x00,0x00,0xBE,0x3F,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x29,0xBE,0x10,0x22,0x3F,
  0x22,0x21,0x22,0x21,0x22,0x21,0x3E,0x3F,0x00,0x00,0x12,0x11,0x22,0x22,0x21,0x22};/*"照",5*/
static const unsigned char PROGMEM str6[] =
{
  0x00,0x00,0x9F,0x3F,0x90,0x20,0x90,0x20,0x90,0x3F,0x1E,0x04,0x02,0x04,0xC2,0x7F,
  0x42,0x44,0x5E,0x44,0xD0,0x7F,0x10,0x04,0x10,0x24,0x10,0x44,0xEA,0x7F,0x04,0x40};/*"强",6*/
static const unsigned char PROGMEM str7[] =
{
  0x80,0x00,0x00,0x01,0xFC,0x7F,0x44,0x04,0x44,0x04,0xFC,0x3F,0x44,0x04,0x44,0x04,
  0xC4,0x07,0x04,0x00,0xF4,0x0F,0x24,0x08,0x42,0x04,0x82,0x03,0x61,0x0C,0x1C,0x70};/*"度",7*/
static const unsigned char PROGMEM str8[] =
{
  0x08,0x00,0x08,0x00,0xC8,0x7F,0x08,0x04,0x2A,0x04,0x1A,0x04,0x0A,0x04,0x09,0x04,
  0x08,0x04,0x08,0x04,0x08,0x04,0x14,0x04,0x24,0x04,0x22,0x04,0x02,0x05,0x01,0x02};/*"灯",8*/
static const unsigned char PROGMEM str9[] =
{
  0x00,0x04,0x80,0x04,0xBF,0x04,0x88,0x3F,0x88,0x04,0x48,0x04,0x3E,0x04,0xC8,0x7F,
  0x08,0x0E,0x08,0x15,0x08,0x15,0xB8,0x24,0x87,0x24,0x42,0x44,0x00,0x04,0x00,0x04};/*"珠",9*/
static const unsigned char PROGMEM str10[] =
{
  0x00,0x00,0xFE,0x3F,0x10,0x04,0x10,0x04,0x10,0x04,0x10,0x04,0x10,0x04,0xFF,0x7F,
  0x10,0x04,0x10,0x04,0x10,0x04,0x10,0x04,0x08,0x04,0x08,0x04,0x04,0x04,0x02,0x04};/*"开",10*/
static const unsigned char PROGMEM str11[] =
{
  0x08,0x08,0x10,0x08,0x10,0x04,0x00,0x00,0xFC,0x1F,0x80,0x00,0x80,0x00,0x80,0x00,
  0xFF,0x7F,0x80,0x00,0x40,0x01,0x40,0x01,0x20,0x02,0x10,0x04,0x0C,0x18,0x03,0x60};/*"关",11*/

void oled_image()
{
  u8g2.clearBuffer();//清屏
  u8g2.drawXBMP(0, 0, 16, 16, str0);u8g2.drawXBMP(16, 0, 16, 16, str1);u8g2.drawXBMP(32, 0, 16, 16, str2);u8g2.drawXBMP(48, 0, 16, 16, str7);//环境温度
  u8g2.drawXBMP(0, 16, 16, 16, str0);u8g2.drawXBMP(16, 16, 16, 16, str1);u8g2.drawXBMP(32, 16, 16, 16, str3);u8g2.drawXBMP(48, 16, 16, 16, str7);//环境湿度
  u8g2.drawXBMP(0, 32, 16, 16, str4);u8g2.drawXBMP(16, 32, 16, 16, str5);u8g2.drawXBMP(32, 32, 16, 16, str6);u8g2.drawXBMP(48, 32, 16, 16, str7);//光照强度
  u8g2.drawXBMP(0, 48, 16, 16, str8);u8g2.drawXBMP(16, 48, 16, 16, str9);u8g2.drawXBMP(32, 48, 16, 16, str10);u8g2.drawXBMP(48, 48, 16, 16, str11);//灯珠开关
  u8g2.setFont(u8g2_font_ncenB14_tr);//字体08
  u8g2.setCursor(64, 16);//设置显示位置
  u8g2.print(":");//输出字符
  u8g2.setCursor(64, 32);//设置显示位置
  u8g2.print(":");//输出字符
  u8g2.setCursor(64, 48);//设置显示位置
  u8g2.print(":");//输出字符
  u8g2.setCursor(64, 64);//设置显示位置
  u8g2.print(":");//输出字符

  u8g2.sendBuffer();//开显示 
}

/****************OLED显示***************/
void oleddisplay()
{  
  u8g2.clearBuffer();//清屏
  u8g2.drawXBMP(0, 0, 16, 16, str0);u8g2.drawXBMP(16, 0, 16, 16, str1);u8g2.drawXBMP(32, 0, 16, 16, str2);u8g2.drawXBMP(48, 0, 16, 16, str7);//环境温度
  u8g2.drawXBMP(0, 16, 16, 16, str0);u8g2.drawXBMP(16, 16, 16, 16, str1);u8g2.drawXBMP(32, 16, 16, 16, str3);u8g2.drawXBMP(48, 16, 16, 16, str7);//环境湿度
  u8g2.drawXBMP(0, 32, 16, 16, str4);u8g2.drawXBMP(16, 32, 16, 16, str5);u8g2.drawXBMP(32, 32, 16, 16, str6);u8g2.drawXBMP(48, 32, 16, 16, str7);//光照强度
  u8g2.drawXBMP(0, 48, 16, 16, str8);u8g2.drawXBMP(16, 48, 16, 16, str9);u8g2.drawXBMP(32, 48, 16, 16, str10);u8g2.drawXBMP(48, 48, 16, 16, str11);//灯珠开关
  u8g2.setFont(u8g2_font_ncenB14_tr);//字体08
  u8g2.setCursor(64, 16);//设置显示位置
  u8g2.print(":");//输出字符
  u8g2.setCursor(64, 32);//设置显示位置
  u8g2.print(":");//输出字符
  u8g2.setCursor(64, 48);//设置显示位置
  u8g2.print(":");//输出字符
  u8g2.setCursor(64, 64);//设置显示位置
  u8g2.print(":");//输出字符
  u8g2.setFont(u8g2_font_ncenB14_tr);  
  u8g2.setCursor(92, 32); 
  u8g2.print(wendu); 
  u8g2.setCursor(92, 16); 
  u8g2.print(shidu); 
  u8g2.setCursor(92, 48); 
  u8g2.print(light_strength); 
  if(led_status==1) 
    u8g2.drawXBMP(92, 48, 16, 16, str10);  
  else
    u8g2.drawXBMP(92, 48, 16, 16, str11);     
  u8g2.sendBuffer();//开显示
}

int start_flag;
u8 i;
u8 count;
u8 data_control[9];         //接收区存储数据
u8 recent_data[3];
void read_usart()
{
  i = mySerial.available();  //返回目前串口接收区内的已经接受的数据量
  if(i != 0)
  {
    //Serial.print("串口接收到的数据量为:");
    //Serial.println(mySerial.available());
    while(i--)
    {  
      recent_data[2]=recent_data[1];
      recent_data[1]=recent_data[0];
      recent_data[0]=mySerial.read();
      if(start_flag)
      {
        data_control[count]=recent_data[0]-48;
        count++;
        if(count>=9)
        {
          start_flag=0;
          count=0;
          //Serial.print("数据存储完毕!\r\n");
        }
      }
      if(recent_data[0]==49&&recent_data[1]==55&&recent_data[2]==49)
      {
        start_flag=1;
        //Serial.print("检测到起始位,准备开始接收数据\r\n");
      }
    }
  }
  else
  {
  }
}
/*初始化函数*/
void setup() 
{
  Serial.begin(9600);                       //初始化波特率115200
  Serial.print("大家好");
  mySerial.begin(9600);
  mySerial.println("T");
  /*初始化输出函数*/
  pinMode(led, OUTPUT);                       //设置led引脚为输出模式以便控制led
  /*初始化显示屏参数*/
  u8g2.begin();
  u8g2.enableUTF8Print();
  oled_image();
}
int main_zhiling;  
void loop() 
{ 
  read_usart();
  wendu=data_control[0]*100+data_control[1]*10+data_control[2];
  shidu=data_control[3]*100+data_control[4]*10+data_control[5];
  light_strength=data_control[6]*100+data_control[7]*10+data_control[8];

  Serial.print(0xAB);
  Serial.print((u8)shidu/100);Serial.print((u8)shidu/10%10);Serial.print((u8)shidu%10);
  Serial.print((u8)wendu/100);Serial.print((u8)wendu/10%10);Serial.print((u8)wendu%10);
  Serial.print((u8)light_strength/100);Serial.print((u8)light_strength/10%10);Serial.print((u8)light_strength%10);
  Serial.print(0xBA);

  Serial.available();
  main_zhiling=Serial.read();
  if(main_zhiling=='T')led_status=1;
  else if(main_zhiling=='F')led_status=0;

  if(led_status)digitalWrite(led,1),mySerial.print("T");
  else digitalWrite(led,0),mySerial.print("F");

  oleddisplay();
  delay(500);
}

从机代码:

#include "user_dht.h"
#include <SoftwareSerial.h>
#define analogPin A0         //定义光照强度检测的引脚
#define led 13
#define rxPin 9
#define txPin 8

DHT11 dht11(2);              //调用dht11函数,引用2引脚,实际检测温度湿度
SoftwareSerial mySerial(rxPin, txPin);

int main_zhiling;            //主机发送过来的指令
int light_val = 0;           //定义变量来存储检测到的光照强度值
int led_status=0;            //定义变量控制LED灯
extern float wendu;          //全局变量温度,从dht文件采集数据传输至主文件
extern float shidu;          //全局变量湿度,从dht文件采集数据传输至主文件
void setup()
{
  Serial.begin(115200);                       //初始化波特率115200
  pinMode(led, OUTPUT);                       //设置led引脚为输出模式以便控制led
  digitalWrite(led,0);                        //连接失败LED熄灭
  mySerial.begin(9600);
}

void loop()
{
  mySerial.available();
  main_zhiling=mySerial.read();
  if(main_zhiling=='T')led_status=1;
  else if(main_zhiling=='F')led_status=0;
  dht11.Get_DHT11_Value();                  //获取到DHT11的温湿度数据
  light_val = analogRead(analogPin);              //通过外部模拟输出得到光照强度值
  light_val = 100-(int)((float)light_val/1024*100);     //由于采集的是电压,从0-4095,于是要做一定的变换使其变成百分比值,另外光照强度最大时,采集到的电压最低,因此要做一定处理
  mySerial.print(0xAB);
  mySerial.print((u8)shidu/100);mySerial.print((u8)shidu/10%10);mySerial.print((u8)shidu%10);
  mySerial.print((u8)wendu/100);mySerial.print((u8)wendu/10%10);mySerial.print((u8)wendu%10);
  mySerial.print((u8)light_val/100);mySerial.print((u8)light_val/10%10);mySerial.print((u8)shidu%10);
  mySerial.print(0xBA);
  
	//Serial.print("Current humdity = "); Serial.println(shidu);Serial.print("\r\n");	//显示湿度的整数位; Serial.print('.');
	//Serial.print("Current temperature = "); Serial.println(wendu);Serial.print("\r\n");	//显示温度的整数位; Serial.print('.');
 // Serial.print("Current light = "); Serial.println(light_val);Serial.print("%\r\n");	//显示温度的整数位; Serial.print('.');
  if(led_status)digitalWrite(led,1);
  else digitalWrite(led,0);
	delay(1000);
}

wifi模块代码:

#include "ALIMQTT.C"
#include <Wire.h>  
//我们测试需要用到的串口,ESP32一共有3个串口,串口0应该是用于程序烧写和打印输出调试用的,所以我们只用1和2
HardwareSerial MySerial_stm32(1); 
ALIMQTT mqtt;                //定义mqtt的一些任务函数

#define led 2                //定义LED的驱动引脚

int ground_duty,light_duty,wendu_data;//土壤占空比,光照占空比

int hextoint(int i)
{
  if(i==48)return 0;
  else if(i==49)return 1;
  else if(i==50)return 2;
  else if(i==51)return 3;
  else if(i==52)return 4;
  else if(i==53)return 5;
  else if(i==54)return 6;
  else if(i==55)return 7;
  else if(i==56)return 8;
  else if(i==57)return 9;
  else if(i==65)return 10;
  else if(i==66)return 11;
  else if(i==67)return 12;
  else if(i==68)return 13;
  else if(i==69)return 14;
  else if(i==70)return 15;
}
//MQTT协议函数
void callback(char *topic, byte *payload, unsigned int length)
{
    payload[length]='\0';//payload是接收到的JSON格式数据,length是接收数据的字节数。
    StaticJsonDocument<4096>doc;DeserializationError error = deserializeJson(doc, payload);//反序列化JSON数据
    if(!error){mqttCommand(doc.as<JsonVariant>());}
}
void itemCommand(JsonVariant params)
{
  Serial.println(params["COMMAND"].as<String>());
  mqtt.add("COMMAND",params["COMMAND"].as<String>().c_str());mqtt.send();
}
void mqttCommand(JsonVariant params)
{
  if(params.containsKey("params"))
  {
    if(params["params"].containsKey("COMMAND"))
    {/*阿里平台里添加设备属性COMMAND*/
      if(params["params"]["COMMAND"].is<String>())
      {
        if(params["params"]["COMMAND"].as<String>()=="restart"||params["params"]["COMMAND"].as<String>()=="reset")
        {
          ESP.restart();//模块重启。
        }
      }
    }
    //这里可以添加其他属性设置代码。
    if(params["params"].containsKey("mode"))
    {/*阿里平台里添加设备属性LED*/
      if(params["params"]["mode"].is<int>())
      {
        if(params["params"]["mode"].as<int>()==0)
        { 
          digitalWrite(led,0),MySerial_stm32.print("F");
        }
        if(params["params"]["mode"].as<int>()==1)
        {
          digitalWrite(led,1),MySerial_stm32.print("T");
        }
      }
    }
  }
}


int start_flag;
int i;
int count;
int data_control[9];         //接收区存储数据
int recent_data[3];
void read_usart()
{
  i = MySerial_stm32.available();  //返回目前串口接收区内的已经接受的数据量
  if(i != 0)
  {
    Serial.print("串口接收到的数据量为:");
    Serial.println(MySerial_stm32.available());
    while(i--)
    {  
      recent_data[2]=recent_data[1];
      recent_data[1]=recent_data[0];
      recent_data[0]=MySerial_stm32.read();
      Serial.println(recent_data[0]);
      if(start_flag)
      {
        data_control[count]=recent_data[0]-48;
        count++;
        if(count>=9)
        {
          start_flag=0;
          count=0;
          Serial.print("数据存储完毕!\r\n");
        }
      }
      if(recent_data[0]==49&&recent_data[1]==55&&recent_data[2]==49)
      {
        start_flag=1;
        Serial.print("检测到起始位,准备开始接收数据\r\n");
      }
    }
  }
}

/*初始化函数*/
void setup() 
{
  uint8_t Index;
  Serial.begin(115200);                       //初始化波特率115200
  /*初始化输出函数*/
  MySerial_stm32.begin(9600, SERIAL_8N1, 14, 15);

  pinMode(led,OUTPUT);                        //定义LEDIO口为输出  
  digitalWrite(led,LOW);                      //初始化灯光不亮

  /*初始化WIFI参数*/
  WiFi.mode(WIFI_STA);                        //设定WIFI的模式为STA
  WiFi.begin("dong","hjsygdsgb");             //这里请使用你自己的WIFI名和密码,第一个是WIFI名,第二个是密码
  Index=60;                                   //初始化要用到的参数
  Serial.print("\r\n连接WIFI...");             //在串口显示正在连接中
  while(WiFi.status()!=WL_CONNECTED && Index!=0)//判断WIFI是否连接
  {
    Serial.print(".");Index--;delay(500);
    digitalWrite(led,not digitalRead(led));   //LED慢速闪烁表示正在连接WIFI.
  }
  if(Index!=0)                                //如果Index不等于0,代表WIFI连接成功   
  {                              
    Serial.println("成功!");
    Serial.print("\r\n本机IP地址:");Serial.print(WiFi.localIP());Serial.print("\t\t路由器IP地址:");Serial.println( WiFi.gatewayIP());
    digitalWrite(led,1);//LED常亮表示WIFI连接成功.
    mqtt.begin("a10YyF1m2qH","Device","be3f7cad81ca344ad2553c5715d095c7","cn-shanghai");//这里的参数请使用你自己在阿里平台申请的设备三元组。最后一个表示站点(阿里云物联网平台有很多站点,请使用你创建设备实例的站点域名称,这里使用的是上海站)。
    mqtt.addItemCallback("COMMAND",itemCommand);                                       //属性回调.(全局回调和属性回调是互斥的,以最后注册为准).
    mqtt.setCallback(callback);                                                        //全局回调.(如果不带参数调用或不调用,则默认为属性回调).
  }
  else
  {
    digitalWrite(led,0);                     //连接失败LED熄灭.
    Serial.println("失败!");                 
  }
}

void loop() 
{
  read_usart();
  wendu_data=data_control[0]*100+data_control[1]*10+data_control[2];
  ground_duty=data_control[3]*100+data_control[4]*10+data_control[5];
  light_duty=data_control[6]*100+data_control[7]*10+data_control[8];

  mqtt.loop();                                    //mqtt的回调函数,用来不断的循环处理数据
  mqtt.add("light",light_duty);mqtt.send();       //输送光照数据至物联网上
  mqtt.add("wendu",wendu_data);mqtt.send();       //输送温度数据至物联网上
  mqtt.add("humi",ground_duty);mqtt.send();       //输送湿度数据至物联网上
  delay(500);                     

}

总结

通过网盘分享的文件:post21资料合集 链接: https://pan.baidu.com/s/1bzNsPYzqIQXEAcqwu-e-Yg?pwd=zrw9 提取码: zrw9 —来自百度网盘超级会员v6的分享