顯示具有 relay 標籤的文章。 顯示所有文章
顯示具有 relay 標籤的文章。 顯示所有文章

2023年12月5日 星期二

DIY Voice controlled ESP32+WS2812 Strip

Purpose:
利用天貓精靈的語音指令去控制ESP32上的WS2812的燈條, 實現改變燈條顏色和燈條的亮度.
Use Tmall Genie's voice commands to control the WS2812 light strip on the ESP32 to change the color and brightness of the light strip
.
Fundamental:
WS2812 RGB LED, it is an LED with a built-in driver chip function, and uses a 5050 LED package to add a driver chip, and the driving method adopts serial input and output.
We will learn how to install WS2812 on an ESP32 board and use it to control a string of addressable LEDs.

Adafruit_NeoPixel.h Library

天貓精靈APP:


Bemfa topic define:
Reference https://cloud.bemfa.com/docs/#/
topic字段值说明:
根据topic字段后三位判断主题类型
当主题名字后三位是001时为插座设备。
When the last three digits of the topic name are 001, it is a socket device.
当主题名字后三位是002时为灯泡设备。
When the last three digits of the topic name are 002, it is the light bulb device.
当主题名字后三位是003时为风扇设备。
当主题名字后三位是004时为传感器设备。
当主题名字后三位是005时为空调设备。
当主题名字后三位是006时为开关设备。
当主题名字后三位是009时为窗帘设备。

Reference the previous article link on blog:
ESP32與WS2812的繽紛世界

Architectures:


YouTube Demo:

ESP32 Code:
#include <WiFi.h>
#include <WiFiClient.h>
#include <Adafruit_NeoPixel.h>

//巴法云服务器地址默认即可
#define TCP_SERVER_ADDR "bemfa.com"
//服务器端口,tcp创客云端口8344
#define TCP_SERVER_PORT "8344"

//********************需要修改的部分*******************//
#define DEFAULT_STASSID  "ras_2.4"     //WIFI名称,区分大小写,不要写错
#define DEFAULT_STAPSW   "181415181415"  //WIFI密码

String UID = "your UID of bemfa";  //用户私钥,可在控制台获取,修改为自己的UID
String TOPIC =   "fan003";         //主题名字,可在控制台新建
String TOPIC1 =   "adapter001";         //主题名字,可在控制台新建
String TOPIC2 =   "led002";         //主题名字,可在控制台新建
//const int LED_Pin = D2;              //单片机引脚
//------------------------------------------------
// Which pin on the Arduino is connected to the NeoPixels?
// On a Trinket or Gemma we suggest changing this to 1:
#define LED_PIN    27
// How many NeoPixels are attached to the Arduino?
#define LED_COUNT  180
// NeoPixel brightness, 0 (min) to 255 (max)
#define BRIGHTNESS 200 // Set BRIGHTNESS to about 1/5 (max = 255)
// Declare our NeoPixel strip object:
Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);
// Argument 1 = Number of pixels in NeoPixel strip
// Argument 2 = Arduino pin number (most are valid)
// Argument 3 = Pixel type flags, add together as needed:
//   NEO_KHZ800  800 KHz bitstream (most NeoPixel products w/WS2812 LEDs)
//   NEO_KHZ400  400 KHz (classic 'v1' (not v2) FLORA pixels, WS2811 drivers)
//   NEO_GRB     Pixels are wired for GRB bitstream (most NeoPixel products)
//   NEO_RGB     Pixels are wired for RGB bitstream (v1 FLORA pixels, not v2)
//   NEO_RGBW    Pixels are wired for RGBW bitstream (NeoPixel RGBW products)
int colorIndex = 0;
int endIndex = 0;
String getcolor = "";
int colorvalue = 65280;
int beforecolorBrightness = 20;
int aftercolorBrightness = 20;
int colorflag = 0;
//-----Relay------------------
#define RELAY1       32
#define RELAY2       33
#define RELAY3       25
#define RELAY4       26
#define RELAY5       5
#define RELAY6       17
#define RELAY7       16
#define RELAY8       4
//**************************************************//
//最大字节数
#define MAX_PACKETSIZE 512
//设置心跳值30s
#define KEEPALIVEATIME 30*1000

//tcp客户端相关初始化,默认即可
WiFiClient TCPclient;
String TcpClient_Buff = "";
unsigned int TcpClient_BuffIndex = 0;
unsigned long TcpClient_preTick = 0;
unsigned long preHeartTick = 0;//心跳
unsigned long preTCPStartTick = 0;//连接
bool preTCPConnected = false;

//相关函数初始化
//连接WIFI
void doWiFiTick();
void startSTA();

//TCP初始化连接
void doTCPClientTick();
void startTCPClient();
void sendtoTCPServer(String p);

//风扇控制函数
void turnOn();
void turnOff();
int LEDFlag = 2;
#ifndef LED_BUILTIN
#define LED_BUILTIN 2
#endif
TaskHandle_t hled;
void initial()
{
  Serial.println(F("Create Task"));
  //--------------- create task----------------------------------
  xTaskCreatePinnedToCore(
    vLEDTask, "LEDTask" // A name just for humans
    ,
    1024 // This stack size can be checked & adjusted by reading the Stack Highwater
    ,
    NULL, 2 // Priority, with 3 (configMAX_PRIORITIES - 1) being the highest, and 0 being the lowest.
    ,
    &hled //handle
    ,
    0);
  //----------------------------------------
  //----------------------------------------------------------------------
  //vTaskSuspend(hfunction); //暫停TASK運行
  //----------------------------------------------------------------------
}
/*
  *发送数据到TCP服务器
 */
void sendtoTCPServer(String p){
 
  if (!TCPclient.connected())
  {
    Serial.println("Client is not readly");
    return;
  }
  TCPclient.print(p);
  Serial.println("[Send to TCPServer]:String");
  Serial.println(p);
}
/*
  *初始化和服务器建立连接
*/
void startTCPClient(){
  if(TCPclient.connect(TCP_SERVER_ADDR, atoi(TCP_SERVER_PORT))){
    Serial.print("\nConnected to server:");
    Serial.printf("%s:%d\r\n",TCP_SERVER_ADDR,atoi(TCP_SERVER_PORT));
   
    String tcpTemp="";  //初始化字符串
    String tcpTemp1="";  //初始化字符串
    String tcpTemp2="";  //初始化字符串
    tcpTemp = "cmd=1&uid="+UID+"&topic="+TOPIC+"\r\n"; //构建订阅指令
    sendtoTCPServer(tcpTemp); //发送订阅指令
    tcpTemp="";//清空
    tcpTemp1 = "cmd=1&uid="+UID+"&topic="+TOPIC1+"\r\n"; //构建订阅指令
    sendtoTCPServer(tcpTemp1); //发送订阅指令
    tcpTemp1="";//清空
    tcpTemp2 = "cmd=1&uid="+UID+"&topic="+TOPIC2+"\r\n"; //构建订阅指令
    sendtoTCPServer(tcpTemp2); //发送订阅指令
    tcpTemp2="";//清空

    preTCPConnected = true;
    preHeartTick = millis();
    TCPclient.setNoDelay(true);
  }
  else{
    Serial.print("Failed connected to server:");
    Serial.println(TCP_SERVER_ADDR);
    TCPclient.stop();
    preTCPConnected = false;
  }
  preTCPStartTick = millis();
}


/*
  *检查数据,发送心跳
*/
void doTCPClientTick(){
 //检查是否断开,断开后重连
   if(WiFi.status() != WL_CONNECTED) return;

  if (!TCPclient.connected()) {//断开重连

  if(preTCPConnected == true){

    preTCPConnected = false;
    preTCPStartTick = millis();
    Serial.println();
    Serial.println("TCP Client disconnected.");
    TCPclient.stop();
  }
  else if(millis() - preTCPStartTick > 1*1000)//重新连接
    startTCPClient();
  }
  else
  {
    if (TCPclient.available()) {//收数据
      char c =TCPclient.read();
      TcpClient_Buff +=c;
      TcpClient_BuffIndex++;
      TcpClient_preTick = millis();
     
      if(TcpClient_BuffIndex>=MAX_PACKETSIZE - 1){
        TcpClient_BuffIndex = MAX_PACKETSIZE-2;
        TcpClient_preTick = TcpClient_preTick - 200;
      }
      preHeartTick = millis();
    }
    if(millis() - preHeartTick >= KEEPALIVEATIME){//保持心跳
      preHeartTick = millis();
      Serial.println("--Keep alive:");
      sendtoTCPServer("cmd=0&msg=keep\r\n");
    }
  }
  if((TcpClient_Buff.length() >= 1) && (millis() - TcpClient_preTick>=200))
  {//data ready
    TCPclient.flush();
    Serial.println("Buff");
    Serial.println(TcpClient_Buff);
   
    if((TcpClient_Buff.indexOf("fan003") > 0))
    {
      if((TcpClient_Buff.indexOf("&msg=on") > 0))
      {
        turnOn();
      }
      else if((TcpClient_Buff.indexOf("&msg=off") > 0))
      {
        turnOff();
      }
    }
    //-------------------------------------------------------------
    if((TcpClient_Buff.indexOf("adapter001") > 0))
    {
      if((TcpClient_Buff.indexOf("&msg=on") > 0))
      {
        turnAdapterOn();
      }
      else if((TcpClient_Buff.indexOf("&msg=off") > 0))
      {
        turnAdapterOff();
      }
    }
    //-------------------------------------------------------------
    if((TcpClient_Buff.indexOf("led002") > 0))
    {
      //--------------------------------
      if((TcpClient_Buff.indexOf("&msg=on") > 0))
      {
        turnLedOn();
        if(colorflag == 0)
        {
          for(int i=0;i<LED_COUNT;i++)
          {
            strip.setPixelColor(i, strip.Color(255, 255, 255, beforecolorBrightness)); // Set white
          }
          strip.show();
          colorflag=1;
        }
        //----------------------------------
        //cmd=2&uid=1de98fdcc4554cfa8525aa2e08b74736&topic=led002&msg=on#80
        if(TcpClient_Buff.length() > 65 )
        {
          colorIndex = TcpClient_Buff.indexOf('#');
          endIndex = TcpClient_Buff.lastIndexOf('/r');
          getcolor = "";
          //c语言字符串查找,查找&msg=位置
          //int msgIndex = TcpClient_Buff.indexOf("&msg=");
          getcolor= TcpClient_Buff.substring(colorIndex+1,endIndex);
          beforecolorBrightness= getcolor.toInt();
          Serial.print( " voice colorBrightness--");
          Serial.println(beforecolorBrightness);
          aftercolorBrightness=map(beforecolorBrightness, 0, 100, 0, 255);
          Serial.print( " after colorBrightness--");
          Serial.println(aftercolorBrightness);
          strip.setBrightness(aftercolorBrightness);
          strip.show();
        }
//-- cmd=2&uid=1de98fdcc4554cfa8525aa2e08b74736&topic=led002&msg=on#100#16711680
        if(TcpClient_Buff.length() > 69)
        {
          colorIndex = TcpClient_Buff.lastIndexOf('#');
          endIndex = TcpClient_Buff.lastIndexOf('/r');
          getcolor = "";
          //c语言字符串查找,查找&msg=位置
          //int msgIndex = TcpClient_Buff.indexOf("&msg=");
          getcolor= TcpClient_Buff.substring(colorIndex+1,endIndex);
          colorvalue=getcolor.toInt();
          Serial.print( " colorvalue--");
          Serial.println(colorvalue);
          for(int i=0;i<LED_COUNT;i++)
          {
            strip.setPixelColor(i, colorvalue);
          }
          strip.setBrightness(aftercolorBrightness);
          strip.show();
        }       
      }
      else if((TcpClient_Buff.indexOf("&msg=off") > 0))
      {
        turnLedOff();
      }
    }
    TcpClient_Buff="";
    TcpClient_BuffIndex = 0;
  }
}

void startSTA(){
  WiFi.disconnect();
  WiFi.mode(WIFI_STA);
  WiFi.begin(DEFAULT_STASSID, DEFAULT_STAPSW);
}

/**************************************************************************
                       WIFI
***************************************************************************/
/*
  WiFiTick
  检查是否需要初始化WiFi
  检查WiFi是否连接上,若连接成功启动TCP Client
  控制指示灯
*/
void doWiFiTick()
{
  static bool startSTAFlag = false;
  static bool taskStarted = false;
  static uint32_t lastWiFiCheckTick = 0;

  if (!startSTAFlag) {
    startSTAFlag = true;
    startSTA();
    Serial.printf("Heap size:%d\r\n", ESP.getFreeHeap());
  }

  //未连接1s重连
  if ( WiFi.status() != WL_CONNECTED )
  {
    if (millis() - lastWiFiCheckTick > 1000)
    {
      lastWiFiCheckTick = millis();
    }
  }
  //连接成功建立
  else {
    if (taskStarted == false) {
      taskStarted = true;
      Serial.print("\r\nGet IP Address: ");
      Serial.println(WiFi.localIP());
      startTCPClient();
    }
  }
}
//打開fan
void turnOn(){
  Serial.println("Turn ON");
  //digitalWrite(LED_Pin,LOW);
  digitalWrite(RELAY1,LOW);
}
//关闭灯泡
void turnOff(){
  Serial.println("Turn OFF");
  digitalWrite(RELAY1,HIGH);
}
//打开灯泡
void turnAdapterOn(){
  Serial.println("Turn1 ON");
  //digitalWrite(LED_Pin,LOW);
  digitalWrite(RELAY2,LOW);
}
//关闭灯泡
void turnAdapterOff(){
  Serial.println("Turn1 OFF");
  digitalWrite(RELAY2,HIGH);
}
//打开灯泡
void turnLedOn()
{
  Serial.println("Turn2 ON");
  //digitalWrite(LED_Pin,LOW);
  digitalWrite(RELAY3,LOW);
}
//关闭灯泡
void turnLedOff(){
  Serial.println("Turn2 OFF");
  digitalWrite(RELAY3,HIGH);
 
  strip.clear();
  strip.show();
}
// 初始化,相当于main 函数
void setup() {
  Serial.begin(9600);
  Serial.println(F("init"));
  initial();

  strip.begin();           // INITIALIZE NeoPixel strip object (REQUIRED)
  strip.setBrightness(beforecolorBrightness);
  strip.clear();          
  strip.show();
  delay(1000);

  pinMode(LED_BUILTIN, OUTPUT);
  pinMode(RELAY1,OUTPUT);
  digitalWrite(RELAY1,HIGH);
  pinMode(RELAY2,OUTPUT);
  digitalWrite(RELAY2,HIGH);
  pinMode(RELAY3,OUTPUT);
  digitalWrite(RELAY3,HIGH);
  pinMode(RELAY4,OUTPUT);
  digitalWrite(RELAY4,HIGH);
  pinMode(RELAY5,OUTPUT);
  digitalWrite(RELAY5,HIGH);
  pinMode(RELAY6,OUTPUT);
  digitalWrite(RELAY6,HIGH);
  pinMode(RELAY7,OUTPUT);
  digitalWrite(RELAY7,HIGH);
  pinMode(RELAY8,OUTPUT);
  digitalWrite(RELAY8,HIGH);

}

//循环
void loop() {
  if (WiFi.status() != WL_CONNECTED)
  {
    doWiFiTick();
  }
  doTCPClientTick();  
}
//-------------------------------------------------------------------------
static void vLEDTask(void *pvParameters)
{
  (void)pvParameters;
  int      head          = LED_COUNT - 1;
  int      tail          = 0;

  Serial.println(F("LEDTask at core:"));
  Serial.println(xPortGetCoreID());
  pinMode(LED_BUILTIN, OUTPUT);
  for (;;) // A Task shall never return or exit.
  {
    if(LEDFlag == 0)
    {
      digitalWrite(LED_BUILTIN, LOW); // turn the LED off by making the voltage LOW
      vTaskDelay(800);
    }
    else if(LEDFlag == 1)
    {
      digitalWrite(LED_BUILTIN, HIGH); // turn the LED on (HIGH is the voltage level)
      vTaskDelay(800);
      digitalWrite(LED_BUILTIN, LOW); // turn the LED off by making the voltage LOW
      vTaskDelay(800);
    }
    else if(LEDFlag == 2)
    {
      digitalWrite(LED_BUILTIN, HIGH); // turn the LED on (HIGH is the voltage level)
      vTaskDelay(300);
      digitalWrite(LED_BUILTIN, LOW); // turn the LED off by making the voltage LOW
      vTaskDelay(300);
    }
    else
    {
      vTaskDelay(10);
    }
  }
}



2023年12月2日 星期六

TmallGenie Voice control ESP32

Purpose:
天貓精靈(TmallGenie)+ESP32+巴法雲, 實現語音家電控制!
Tmall Genie + ESP32 + Bemfa Cloud, accomplish voice control of home appliances!




Architectures:
Fundamental:

Reference https://en.wikipedia.org/wiki/Tmall_Genie
天貓精靈是阿里巴巴人工智慧實驗室研發的智慧語音助理音箱,基於AliGenie,具有娛樂、生活、購物等多個領域的數百個功能,類似於Amazon Echo、Google Home、Apple HomePod。 天貓精靈於2017年7月5日發布,2017年8月8日正式開賣。
基本參數
產品類型 智慧音箱
揚聲器 內建4Ω揚聲器
輸出功率 3W
麥克風 支持,2個
產品功能 語音辨識控制功能,自訂喚醒詞,通話
無線 WiFi 2.4GB,藍牙4.2
其它參數
處理器 MTK MT8516
系統:AliGenie語音助理
顏色 紅色,白色
尺寸 134*65*59mm
重量 265g
供電方式 DC 12V/1A
電源性能 100-240V/50Hz
控制方式 按鍵,語音
介面 耳機介面
其它特點 內建功放:TI TAS5751M
支援智慧家庭控制、語音購物、手機儲值、叫外送、音頻音樂播放等功能,尤其是購物方面,只需要說出產品描述,然後透過聲紋驗證身份,就能直接下單
訂行程、備忘提醒、訂鬧鐘、查天氣、找手機、倒數、卡路里查詢、聽新聞、查星座運勢、查價格、查股票等功能
與普普藝術結合,波點酷設計,全屋場景體驗升級,就近換新
支援多台設備採用Multi-Room功能進行連動播放
連線方式 WiFi,藍牙
巴法云 C#/.NET SDK。支持TCP和MQTT协议接入,并提供超级简单的设备操作API。跨平台,支持Linux、Windows、OSX,支持树莓派。
Reference https://github.com/withsalt/BemfaCloud
                  https://cloud.bemfa.com/user/index.html


註冊你的帳號, 新增你的主題!

Reference the previous article link on blog:


YouTube Demo:


ESP32 Code:

#include <WiFi.h>
#include <WiFiClient.h>


//巴法云服务器地址默认即可
#define TCP_SERVER_ADDR "bemfa.com"
//服务器端口,tcp创客云端口8344
#define TCP_SERVER_PORT "8344"

//********************需要修改的部分*******************//
#define DEFAULT_STASSID  "Winson_Y52"     //WIFI名称,区分大小写,不要写错
#define DEFAULT_STAPSW   "8888888888"  //WIFI密码
String UID = "your ID";  //用户私钥,可在控制台获取,修改为自己的UID
String TOPIC =   "fan003";         //主题名字,可在控制台新建
String TOPIC1 =   "adapter001";         //主题名字,可在控制台新建
String TOPIC2 =   "led002";         //主题名字,可在控制台新建
//const int LED_Pin = D2;              //单片机引脚
//-----Relay------------------
//-----Relay------------------
#define RELAY1       32
#define RELAY2       33
#define RELAY3       25
#define RELAY4       26
#define RELAY5       5
#define RELAY6       17
#define RELAY7       16
#define RELAY8       4
//**************************************************//

//最大字节数
#define MAX_PACKETSIZE 512
//设置心跳值30s
#define KEEPALIVEATIME 30*1000

//tcp客户端相关初始化,默认即可
WiFiClient TCPclient;
String TcpClient_Buff = "";
unsigned int TcpClient_BuffIndex = 0;
unsigned long TcpClient_preTick = 0;
unsigned long preHeartTick = 0;//心跳
unsigned long preTCPStartTick = 0;//连接
bool preTCPConnected = false;

//相关函数初始化
//连接WIFI
void doWiFiTick();
void startSTA();

//TCP初始化连接
void doTCPClientTick();
void startTCPClient();
void sendtoTCPServer(String p);

//风扇控制函数
void turnOn();
void turnOff();



/*
  *发送数据到TCP服务器
 */
void sendtoTCPServer(String p){
 
  if (!TCPclient.connected())
  {
    Serial.println("Client is not readly");
    return;
  }
  TCPclient.print(p);
  Serial.println("[Send to TCPServer]:String");
  Serial.println(p);
}


/*
  *初始化和服务器建立连接
*/
void startTCPClient(){
  if(TCPclient.connect(TCP_SERVER_ADDR, atoi(TCP_SERVER_PORT))){
    Serial.print("\nConnected to server:");
    Serial.printf("%s:%d\r\n",TCP_SERVER_ADDR,atoi(TCP_SERVER_PORT));
   
    String tcpTemp="";  //初始化字符串
    String tcpTemp1="";  //初始化字符串
    String tcpTemp2="";  //初始化字符串
    tcpTemp = "cmd=1&uid="+UID+"&topic="+TOPIC+"\r\n"; //构建订阅指令
    sendtoTCPServer(tcpTemp); //发送订阅指令
    tcpTemp="";//清空
    tcpTemp1 = "cmd=1&uid="+UID+"&topic="+TOPIC1+"\r\n"; //构建订阅指令
    sendtoTCPServer(tcpTemp1); //发送订阅指令
    tcpTemp1="";//清空
    tcpTemp2 = "cmd=1&uid="+UID+"&topic="+TOPIC2+"\r\n"; //构建订阅指令
    sendtoTCPServer(tcpTemp2); //发送订阅指令
    tcpTemp2="";//清空

    preTCPConnected = true;
    preHeartTick = millis();
    TCPclient.setNoDelay(true);
  }
  else{
    Serial.print("Failed connected to server:");
    Serial.println(TCP_SERVER_ADDR);
    TCPclient.stop();
    preTCPConnected = false;
  }
  preTCPStartTick = millis();
}


/*
  *检查数据,发送心跳
*/
void doTCPClientTick(){
 //检查是否断开,断开后重连
   if(WiFi.status() != WL_CONNECTED) return;

  if (!TCPclient.connected()) {//断开重连

  if(preTCPConnected == true){

    preTCPConnected = false;
    preTCPStartTick = millis();
    Serial.println();
    Serial.println("TCP Client disconnected.");
    TCPclient.stop();
  }
  else if(millis() - preTCPStartTick > 1*1000)//重新连接
    startTCPClient();
  }
  else
  {
    if (TCPclient.available()) {//收数据
      char c =TCPclient.read();
      TcpClient_Buff +=c;
      TcpClient_BuffIndex++;
      TcpClient_preTick = millis();
     
      if(TcpClient_BuffIndex>=MAX_PACKETSIZE - 1){
        TcpClient_BuffIndex = MAX_PACKETSIZE-2;
        TcpClient_preTick = TcpClient_preTick - 200;
      }
      preHeartTick = millis();
    }
    if(millis() - preHeartTick >= KEEPALIVEATIME){//保持心跳
      preHeartTick = millis();
      Serial.println("--Keep alive:");
      sendtoTCPServer("cmd=0&msg=keep\r\n");
    }
  }
  if((TcpClient_Buff.length() >= 1) && (millis() - TcpClient_preTick>=200))
  {//data ready
    TCPclient.flush();
    Serial.println("Buff");
    Serial.println(TcpClient_Buff);
   
    if((TcpClient_Buff.indexOf("fan003") > 0))
    {
      if((TcpClient_Buff.indexOf("&msg=on") > 0))
      {
        turnOn();
      }
      else if((TcpClient_Buff.indexOf("&msg=off") > 0))
      {
        turnOff();
      }
    }
    //-------------------------------------------------------------
    if((TcpClient_Buff.indexOf("adapter001") > 0))
    {
      if((TcpClient_Buff.indexOf("&msg=on") > 0))
      {
        turnAdapterOn();
      }
      else if((TcpClient_Buff.indexOf("&msg=off") > 0))
      {
        turnAdapterOff();
      }
    }
    //-------------------------------------------------------------
    if((TcpClient_Buff.indexOf("led002") > 0))
    {
      if((TcpClient_Buff.indexOf("&msg=on") > 0))
      {
        turnLedOn();
      }
      else if((TcpClient_Buff.indexOf("&msg=off") > 0))
      {
        turnLedOff();
      }
    }
    /**
    if((TcpClient_Buff.indexOf("&msg=on") > 0)) {
      turnOn();
    }else if((TcpClient_Buff.indexOf("&msg=off") > 0)) {
      turnOff();
    }**/
   TcpClient_Buff="";
   TcpClient_BuffIndex = 0;
  }
}

void startSTA(){
  WiFi.disconnect();
  WiFi.mode(WIFI_STA);
  WiFi.begin(DEFAULT_STASSID, DEFAULT_STAPSW);
}

/**************************************************************************
                                 WIFI
***************************************************************************/
/*
  WiFiTick
  检查是否需要初始化WiFi
  检查WiFi是否连接上,若连接成功启动TCP Client
  控制指示灯
*/
void doWiFiTick(){
  static bool startSTAFlag = false;
  static bool taskStarted = false;
  static uint32_t lastWiFiCheckTick = 0;

  if (!startSTAFlag) {
    startSTAFlag = true;
    startSTA();
    Serial.printf("Heap size:%d\r\n", ESP.getFreeHeap());
  }

  //未连接1s重连
  if ( WiFi.status() != WL_CONNECTED ) {
    if (millis() - lastWiFiCheckTick > 1000) {
      lastWiFiCheckTick = millis();
    }
  }
  //连接成功建立
  else {
    if (taskStarted == false) {
      taskStarted = true;
      Serial.print("\r\nGet IP Address: ");
      Serial.println(WiFi.localIP());
      startTCPClient();
    }
  }
}
//打開fan
void turnOn(){
  Serial.println("Turn ON");
  //digitalWrite(LED_Pin,LOW);
  digitalWrite(RELAY1,LOW);
}
//关闭灯泡
void turnOff(){
  Serial.println("Turn OFF");
    digitalWrite(RELAY1,HIGH);
}
//打开灯泡
void turnAdapterOn(){
  Serial.println("Turn1 ON");
  //digitalWrite(LED_Pin,LOW);
  digitalWrite(RELAY2,LOW);
}
//关闭灯泡
void turnAdapterOff(){
  Serial.println("Turn1 OFF");
    digitalWrite(RELAY2,HIGH);
}
//打开灯泡
void turnLedOn(){
  Serial.println("Turn2 ON");
  //digitalWrite(LED_Pin,LOW);
  digitalWrite(RELAY3,LOW);
}
//关闭灯泡
void turnLedOff(){
  Serial.println("Turn2 OFF");
    digitalWrite(RELAY3,HIGH);
}


// 初始化,相当于main 函数
void setup() {
  Serial.begin(9600);
  pinMode(RELAY1,OUTPUT);
  digitalWrite(RELAY1,HIGH);
  pinMode(RELAY2,OUTPUT);
  digitalWrite(RELAY2,HIGH);
  pinMode(RELAY3,OUTPUT);
  digitalWrite(RELAY3,HIGH);
  pinMode(RELAY4,OUTPUT);
  digitalWrite(RELAY4,HIGH);
  pinMode(RELAY5,OUTPUT);
  digitalWrite(RELAY5,HIGH);
  pinMode(RELAY6,OUTPUT);
  digitalWrite(RELAY6,HIGH);
  pinMode(RELAY7,OUTPUT);
  digitalWrite(RELAY7,HIGH);
  pinMode(RELAY8,OUTPUT);
  digitalWrite(RELAY8,HIGH);

}

//循环
void loop() {
  doWiFiTick();
  doTCPClientTick();
}





2023年11月15日 星期三

ESP32 IOT smart farm with WIFI mesh

Purpose:
利用ESP32 mesh網絡架構一個智慧農場的控制系統, 包含主控監視端為Node1 includes Xbox joystick, 並利用之前寫過的文章做成各監控端的Node節點並將資料發送到 MQTT 伺服器並透過 Node-red 儀表板顯示其數值., 整合成一個智慧農場的雛型.

Use the ESP32 mesh network to construct a control system for a smart farm, including the main monitoring terminal Node1 with Xbox joystick, and use previously written articles to create other nodes for each monitoring terminal, then send the value to the MQTT server and displayed through the Node-red dashboard. Integrating them into a prototype of a smart farm.

Fundamental:
Previous article :
ESP32 Wifi mesh Control another ESP32 with Relay
ESP32與DS18B20溫度感測器的C#圖形化資料收集
ESP32 WiFi painlessmesh network Application
ESP32 WiFi Mesh 控制 4 port Relay模組
ESP32 WiFi mesh中控介面的實踐
NodeMCU-32S (WiFi Mesh 與 BT BLE應用)

Architecture:



Circuit:
Node_1 Main


Node_2 RelayBoard


Node_3 DHT22


Node_4 Water Sensor


YouTube Demo:

ESP32 Main Node1 code:
#include "painlessMesh.h"
//#include <ArduinoJson.h>
#include <Arduino_JSON.h>
#include <ezButton.h>
//-----Global variable---------------------------------------
#define LED_BUILTIN 2
//--------joystick------------------------------------------
#define VRX_PIN_L  33 // ESP32 pin GPIO33 (ADC0)
#define VRY_PIN_L  32 // ESP32 pin GPIO32 (ADC0)
#define VRX_PIN_R  35 // ESP32 pin GPIO33 (ADC0)
#define VRY_PIN_R  34 // ESP32 pin GPIO32 (ADC0)
#define SW_X       18
#define SW_Y       16
#define SW_A       17
#define SW_B       19

#define LEFT_THRESHOLD_L  2200  
#define RIGHT_THRESHOLD_L 900
#define UP_THRESHOLD_L    2200  
#define DOWN_THRESHOLD_L  900  

#define LEFT_THRESHOLD_R  2200  
#define RIGHT_THRESHOLD_R 900
#define UP_THRESHOLD_R    2200  
#define DOWN_THRESHOLD_R  900  

#define COMMAND_NO_L     0x00
#define COMMAND_LEFT_L   0x01
#define COMMAND_RIGHT_L  0x02
#define COMMAND_UP_L     0x04
#define COMMAND_DOWN_L   0x08

#define COMMAND_NO_R     0x00
#define COMMAND_LEFT_R   0x01
#define COMMAND_RIGHT_R  0x02
#define COMMAND_UP_R     0x04
#define COMMAND_DOWN_R   0x08

int valueX_L = 0 ; // to store the X-axis value
int valueY_L = 0 ; // to store the Y-axis value
int command_L = COMMAND_NO_R;
int valueX_R = 0 ; // to store the X-axis value
int valueY_R = 0 ; // to store the Y-axis value
int command_R = COMMAND_NO_R;
//----------------------------------------------

int bValue_A = 0; // To store value of the button
int bValue_B = 0; // To store value of the button
int bValue_X = 0; // To store value of the button
int bValue_Y = 0; // To store value of the button
ezButton buttonA(SW_A);
ezButton buttonB(SW_B);
ezButton buttonX(SW_X);
ezButton buttonY(SW_Y);
//-----------painlessmesh------------------------------------
#define   MESH_PREFIX     "Peter1015"
#define   MESH_PASSWORD   "No18141814"
#define   MESH_PORT       5555
Scheduler userScheduler;  // to control your personal task
painlessMesh  mesh;

//Number for this node
int nodeNumber = 1;
void sendMessage() ;
String readings;
String getReadings();
Task taskSendMessage( TASK_SECOND * 1 , TASK_FOREVER, &sendMessage );
//--------------------Json data ------------------------------
StaticJsonDocument<200> json_doc;
char json_output[100];
DeserializationError json_error;
const char* payload_node;
const char* payload_function;
const char* payload_data;
//------------------------------------------------------------
//char line[16];
//--------- Flag structure --------------------------------------

#define LINE_BUFFER_LENGTH 1024
typedef struct _vFlag
{
  uint8_t LEDFlag=0;
  uint8_t BTFlag=0;
  uint8_t FunctionFlag=1;
  uint8_t SendFlag=0;
}vFlag;
vFlag *flag_Ptr;
vFlag flag;
//--------- uart structure --------------------------------------
//----------uart--------------
#define LINE_BUFFER_LENGTH 64
typedef struct _vUart
{
  char c;
  int lineIndex = 0;
  int line1Index = 0;
  int BTlineIndex = 0;
  bool lineIsComment;
  bool lineSemiColon;
  char line[128];
  char BTline[20];
  String inputString;
  String BTinputString;
} vUart;
vUart *Uart_Ptr;
vUart Uart;
//-------------------------------------
TaskHandle_t hled;
TaskHandle_t huart;

void vLEDFlashTask(void *pvParameters);
void vUARTTask(void *pvParameters);

void initial()
{
  Serial.println(F("Create Task"));
  //----------------------------------------------------------------------
  // Now set up two tasks to run independently.
  xTaskCreatePinnedToCore(
    vLEDFlashTask, "LEDTask" // A name just for humans
    ,
    1024 // This stack size can be checked & adjusted by reading the Stack Highwater
    ,
    NULL, 2 // Priority, with 3 (configMAX_PRIORITIES - 1) being the highest, and 0 being the lowest.
    ,
    &hled //handle
    ,
    0);

  xTaskCreatePinnedToCore(
    vUARTTask, "UARTTask" // A name just for humans
    ,
    1024 // This stack size can be checked & adjusted by reading the Stack Highwater
    ,
    NULL, 3 // Priority, with 3 (configMAX_PRIORITIES - 1) being the highest, and 0 being the lowest.
    ,
    &huart //handle
    ,
    0);

  //----------------------------------------------------------------------
}
String getReadings()
{
  JSONVar jsonReadings;
 
  //Serial.print(F("meshTask at core:"));
  //Serial.println(xPortGetCoreID());
  jsonReadings["node"] = nodeNumber;
  //jsonReadings["node"] = String(normAccel.XAxis);
  //jsonReadings["node ID"] = String(mesh.getNodeId());
  if(flag.FunctionFlag==1)
  {
    jsonReadings["function"] = "Joystick";
  }
 
  readings = JSON.stringify(jsonReadings);
  return readings;
}
void sendMessage()
{
  //String msg = "Hello from node 1 ";
  String msg = getReadings();
  //msg += mesh.getNodeId();

  if(flag.SendFlag ==1)
  {
    msg=Uart.BTinputString;
    Uart.BTinputString="";
    flag.SendFlag=0;
  }
  mesh.sendBroadcast( msg );
  taskSendMessage.setInterval( random( TASK_SECOND * 1, TASK_SECOND * 2 ));
}

void receivedCallback( uint32_t from, String &msg ) {
  Serial.printf("startHere: Received from %u msg=%s\n", from, msg.c_str());
  flag.LEDFlag=1;
}

void newConnectionCallback(uint32_t nodeId) {
    Serial.printf("--> startHere: New Connection, nodeId = %u\n", nodeId);  
}

void changedConnectionCallback() {
  Serial.printf("Changed connections\n");
  flag.LEDFlag=0;
}

void nodeTimeAdjustedCallback(int32_t offset) {
    Serial.printf("Adjusted time %u. Offset = %d\n", mesh.getNodeTime(),offset);
}

void setup() {
  Serial.begin(9600);
  /**240(default 240 160 80 40 20 and 10Mhz)***/
  setCpuFrequencyMhz(160);
  initial();
  //-----------------------------------------------------------------
  buttonA.setDebounceTime(50); // set debounce time to 50 milliseconds
  buttonB.setDebounceTime(50); // set debounce time to 50 milliseconds
  buttonX.setDebounceTime(50); // set debounce time to 50 milliseconds
  buttonY.setDebounceTime(50); // set debounce time to 50 milliseconds
  //------------------------------------------------
  //-------------------------------------------------------------------
  //mesh.setDebugMsgTypes( ERROR | MESH_STATUS | CONNECTION | SYNC | COMMUNICATION | GENERAL | MSG_TYPES | REMOTE ); // all types on
  mesh.setDebugMsgTypes( ERROR | STARTUP );  // set before init() so that you can see startup messages
  mesh.init( MESH_PREFIX, MESH_PASSWORD, &userScheduler, MESH_PORT );
  mesh.onReceive(&receivedCallback);
  mesh.onNewConnection(&newConnectionCallback);
  mesh.onChangedConnections(&changedConnectionCallback);
  mesh.onNodeTimeAdjusted(&nodeTimeAdjustedCallback);
  userScheduler.addTask( taskSendMessage );
  taskSendMessage.enable();
  //-------------------------------------------
  Serial.println(F("System On!"));
  //-------------------------------------------
}

void loop()
{
  Serial.print(F("Main at core:"));
  Serial.println(xPortGetCoreID());
  while (1)
  {
    buttonA.loop(); // MUST call the loop() function first
    buttonB.loop(); // MUST call the loop() function first
    buttonX.loop(); // MUST call the loop() function first
    buttonY.loop(); // MUST call the loop() function first
    // Read the button value
    bValue_A = buttonA.getState();
    bValue_B = buttonB.getState();
    bValue_X = buttonX.getState();
    bValue_Y = buttonY.getState();

    if (buttonA.isPressed()) {
      Serial.println("The buttonA is pressed");
      Uart.BTinputString="{\"node\":10,\"ButtonA\":\"OFF\"}";
      flag.SendFlag=1;
    }
    if (buttonA.isReleased()) {
      Serial.println("The buttonA is released");
     
    }
    if (buttonB.isPressed()) {
      Serial.println("The buttonB is pressed");
      Uart.BTinputString="{\"node\":10,\"ButtonB\":\"OFF\"}";
      flag.SendFlag=1;
    }
    if (buttonB.isReleased()) {
      Serial.println("The buttonB is released");
      // TODO do something here
    }
    if (buttonX.isPressed()) {
      Serial.println("The buttonX is pressed");
      Uart.BTinputString="{\"node\":10,\"ButtonX\":\"OFF\"}";
      flag.SendFlag=1;
    }
    if (buttonX.isReleased()) {
      Serial.println("The buttonX is released");
      // TODO do something here
    }
    if (buttonY.isPressed()) {
      Serial.println("The buttonY is pressed");
      Uart.BTinputString="{\"node\":10,\"ButtonY\":\"OFF\"}";
      flag.SendFlag=1;
    }
    if (buttonY.isReleased()) {
      Serial.println("The buttonY is released");
      // TODO do something here
    }
    //--------------------------------------------------------
    valueX_L = analogRead(VRX_PIN_L);
    valueY_L = analogRead(VRY_PIN_L);
    valueX_R = analogRead(VRX_PIN_R);
    valueY_R = analogRead(VRY_PIN_R);
    // converts the analog value to commands
    // reset commands
    command_L = COMMAND_NO_L;
    command_R = COMMAND_NO_R;
    // check left/right commands
   
    if (valueX_L > LEFT_THRESHOLD_L)
      command_L = command_L | COMMAND_LEFT_L;
    else if (valueX_L < RIGHT_THRESHOLD_L)
      command_L = command_L | COMMAND_RIGHT_L;
   
    // check up/down commands
    if (valueY_L > UP_THRESHOLD_L)
      command_L = command_L | COMMAND_UP_L;
    else if (valueY_L < DOWN_THRESHOLD_L)
      command_L = command_L | COMMAND_DOWN_L;


    // print command to serial and process command
   
    if (command_L & COMMAND_LEFT_L) {
      Serial.println("COMMAND LEFT_L");
      Uart.BTinputString="{\"node\":10,\"ButtonX\":\"ON\"}";
      flag.SendFlag=1;
    }

    if (command_L & COMMAND_RIGHT_L) {
      Serial.println("COMMAND RIGHT_L");
      Uart.BTinputString="{\"node\":10,\"ButtonB\":\"ON\"}";
      flag.SendFlag=1;
    }

    if (command_L & COMMAND_UP_L) {
      Serial.println("COMMAND UP_L");
      Uart.BTinputString="{\"node\":10,\"ButtonY\":\"ON\"}";
      flag.SendFlag=1;
    }

    if (command_L & COMMAND_DOWN_L) {
      Serial.println("COMMAND DOWN_L");
      Uart.BTinputString="{\"node\":10,\"ButtonA\":\"ON\"}";
      flag.SendFlag=1;
    }
   
    //-----------------------------------------------------
    if (valueX_R > LEFT_THRESHOLD_R)
      command_R = command_R | COMMAND_LEFT_R;
    else if (valueX_R < RIGHT_THRESHOLD_R)
      command_R = command_R | COMMAND_RIGHT_R;
   
    // check up/down commands
    if (valueY_R > UP_THRESHOLD_R)
      command_R = command_R | COMMAND_UP_R;
    else if (valueY_R < DOWN_THRESHOLD_R)
      command_R = command_R | COMMAND_DOWN_R;


    // print command to serial and process command
   
    if (command_R & COMMAND_LEFT_R) {
      Serial.println("COMMAND LEFT_R");
      // TODO: add your task here
    }

    if (command_R & COMMAND_RIGHT_R) {
      Serial.println("COMMAND RIGHT_R");
      // TODO: add your task here
    }

    if (command_R & COMMAND_UP_R) {
      Serial.println("COMMAND UP_R");
      // TODO: add your task here
    }

    if (command_R & COMMAND_DOWN_R) {
      Serial.println("COMMAND DOWN_R");
      // TODO: add your task here
    }
    //-----------------------------------------------------
    //-----------painlessmesh------------------------------
    mesh.update();
  }//----while(1)-------------------------------------------

}
//--------------------------------------------------------
/*--------------------------------------------------*/
void vLEDFlashTask(void *pvParameters) // This is a task.
{
  (void)pvParameters;
 
  Serial.print(F("LEDTask at core:"));
  Serial.println(xPortGetCoreID());
  pinMode(LED_BUILTIN, OUTPUT);
  //oldMillis = millis();
  for (;;) // A Task shall never return or exit.
  {
    if(flag.LEDFlag==1)
    {
      digitalWrite(LED_BUILTIN, HIGH); // turn the LED on (HIGH is the voltage level)
      vTaskDelay(200);
      digitalWrite(LED_BUILTIN, LOW); // turn the LED off by making the voltage LOW
      vTaskDelay(200);
    }
    else{
      vTaskDelay(10);
    }
  }
}

//-------------------------------------------
void vUARTTask(void *pvParameters)
{
  (void)pvParameters;

  Serial.print(F("UARTTask at core:"));
  Serial.println(xPortGetCoreID());
  for (;;)
  {
    while (Serial.available() > 0)
    {
      Uart.c = Serial.read();
 
      if ((Uart.c == '\n') || (Uart.c == '\r'))
      { // End of line reached
        if (Uart.lineIndex > 0)
        { // Line is complete. Then execute!
          Uart.line[Uart.lineIndex] = '\0'; // Terminate string
          //Serial.println( F("Debug") );
          //Serial.println( Uart.inputString );
          processCommand(Uart.line); // do something with the command
          Uart.lineIndex = 0;
          Uart.inputString = "";
        }
        else
        {
          // Empty or comment line. Skip block.
        }
        Uart.lineIsComment = false;
        Uart.lineSemiColon = false;
        Serial.println(F("ok>"));
      }
      else
      {
        //Serial.println( c );
        if ((Uart.lineIsComment) || (Uart.lineSemiColon))
        {
          if (Uart.c == ')')
            Uart.lineIsComment = false; // End of comment. Resume line.
        }
        else
        {
          if (Uart.c == '/')
          { // Block delete not supported. Ignore character.
          }
          else if (Uart.c == '~')
          { // Enable comments flag and ignore all characters until ')' or EOL.
            Uart.lineIsComment = true;
          }
          else if (Uart.c == ';')
          {
            Uart.lineSemiColon = true;
          }
          else if (Uart.lineIndex >= LINE_BUFFER_LENGTH - 1)
          {
            Serial.println("ERROR - lineBuffer overflow");
            Uart.lineIsComment = false;
            Uart.lineSemiColon = false;
          }
          else if (Uart.c >= 'a' && Uart.c <= 'z')
          { // Upcase lowercase
            Uart.line[Uart.lineIndex] = Uart.c - 'a' + 'A';
            Uart.lineIndex = Uart.lineIndex + 1;
            Uart.inputString += (char)(Uart.c - 'a' + 'A');
          }
          else
          {
            Uart.line[Uart.lineIndex] = Uart.c;
            Uart.lineIndex = Uart.lineIndex + 1;
            Uart.inputString += Uart.c;
          }
        }
      }
    } //while (Serial.available() > 0)
    vTaskDelay(10);
  }
}
//------------------------------------------------------------
void processCommand(char *data)
{
  int len, xlen, ylen, zlen, alen;
  char ctemp[20];

  len = Uart.inputString.length();
  if (strstr(data, "VER") != NULL)
  {
    Serial.println(F("W_ATE_Board_20231109"));
  }

}


ESP32 RelayBiard Node2 code:
#include "painlessMesh.h"
#include <Arduino_JSON.h>
//-------------------------------------------------------------
#define   MESH_PREFIX     "Peter1015"
#define   MESH_PASSWORD   "No18141814"
#define   MESH_PORT       5555
//-----Global variable---------------------------------------
#define LED_BUILTIN 2
//-----Relay------------------
#define RELAY1       15
#define RELAY2       13
#define RELAY3       32
#define RELAY4       33
//--------- Flag structure --------------------------------------
typedef struct _vFlag
{
  uint8_t LEDFlag=0;
  uint8_t NodeFlag=0;
  uint8_t FunctionFlag=1;
  uint8_t SendFlag=0;
  uint8_t Relay1=0;
  uint8_t Relay2=0;
  uint8_t Relay3=0;
  uint8_t Relay4=0;
}vFlag;
vFlag *flag_Ptr;
vFlag flag;
typedef struct _vUart
{
  String inputString;
  String BTinputString;
} vUart;
vUart *Uart_Ptr;
vUart Uart;
//---------------------------------------------------
Scheduler userScheduler; // to control your personal task
painlessMesh  mesh;

//Number for this node
int nodeNumber = 2;
// User stub
void sendMessage() ; // Prototype so PlatformIO doesn't complain
String readings;
String getReadings();
Task taskSendMessage( TASK_SECOND * 1 , TASK_FOREVER, &sendMessage );

TaskHandle_t hled;

void vLEDFlashTask(void *pvParameters);

void initial()
{
  Serial.println(F("Create Task"));
  //----------------------------------------------------------------------
  // Now set up two tasks to run independently.
  xTaskCreatePinnedToCore(
    vLEDFlashTask, "LEDTask" // A name just for humans
    ,
    1024 // This stack size can be checked & adjusted by reading the Stack Highwater
    ,
    NULL, 2 // Priority, with 3 (configMAX_PRIORITIES - 1) being the highest, and 0 being the lowest.
    ,
    &hled //handle
    ,
    0);
  //----------------------------------------------------------------------
}
String getReadings()
{
  JSONVar jsonReadings;
 
  //Serial.print(F("meshTask at core:"));
  //Serial.println(xPortGetCoreID());
  jsonReadings["node"] = nodeNumber;
  //jsonReadings["node"] = String(normAccel.XAxis);
  //jsonReadings["node ID"] = String(mesh.getNodeId());
  if(flag.FunctionFlag==1)
  {
    jsonReadings["function"] = "RelayBoard";
  }
  if(flag.Relay1==1)
  {
    jsonReadings["Relay1"] = "ON";
  }
  else
  {  
    jsonReadings["Relay1"] = "OFF";
  }
  if(flag.Relay2==1)
  {
    jsonReadings["Relay2"] = "ON";
  }
  else
  {  
    jsonReadings["Relay2"] = "OFF";
  }
  if(flag.Relay3==1)
  {
    jsonReadings["Relay3"] = "ON";
  }
  else
  {  
    jsonReadings["Relay3"] = "OFF";
  }
  if(flag.Relay4==1)
  {
    jsonReadings["Relay4"] = "ON";
  }
  else
  {  
    jsonReadings["Relay4"] = "OFF";
  }

  readings = JSON.stringify(jsonReadings);
  return readings;
}
void sendMessage() {
  String msg = getReadings();
  //msg += mesh.getNodeId();

  if(flag.SendFlag ==1)
  {
    msg=Uart.BTinputString;
    Uart.BTinputString="";
    flag.SendFlag=0;
  }
  mesh.sendBroadcast( msg );
  taskSendMessage.setInterval( random( TASK_SECOND * 1, TASK_SECOND * 2 ));
}

// Needed for painless library
void receivedCallback( uint32_t from, String &msg )
{
  JSONVar myObject = JSON.parse(msg.c_str());
  if (myObject.hasOwnProperty("node")) {
    //Serial.print("myObject[\"node\"] = ");
    //Serial.println((int) myObject["node"]);
    if((int) myObject["node"]==10)
    {
      flag.NodeFlag=1;
    }
  }
  if (myObject.hasOwnProperty("function")) {
    //Serial.print("myObject[\"function\"] = ");
    //Serial.println((const char*) myObject["function"]);
  }
  if (myObject.hasOwnProperty("ButtonA")) {
   
    if(flag.NodeFlag==1)
    {
      //Serial.print("myObject[\"ButtonA\"] = ");
      //Serial.println((const char*) myObject["ButtonA"]);
      String str=(const char*)myObject["ButtonA"];
      if(str=="ON")
      {
        digitalWrite(RELAY1, LOW);
        flag.Relay1=1;
      }
      if(str=="OFF")
      {
        digitalWrite(RELAY1, HIGH);
        flag.Relay1=0;
      }
    }
  }
  if (myObject.hasOwnProperty("ButtonB")) {
   
    if(flag.NodeFlag==1)
    {
      //Serial.print("myObject[\"ButtonA\"] = ");
      //Serial.println((const char*) myObject["ButtonA"]);
      String str=(const char*)myObject["ButtonB"];
      if(str=="ON")
      {
        digitalWrite(RELAY2, LOW);
        flag.Relay2=1;
      }
      if(str=="OFF")
      {
        digitalWrite(RELAY2, HIGH);
        flag.Relay2=0;
      }
    }
  }
  if (myObject.hasOwnProperty("ButtonY")) {
   
    if(flag.NodeFlag==1)
    {
      //Serial.print("myObject[\"ButtonA\"] = ");
      //Serial.println((const char*) myObject["ButtonA"]);
      String str=(const char*)myObject["ButtonY"];
      if(str=="ON")
      {
        digitalWrite(RELAY3, LOW);
        flag.Relay3=1;
      }
      if(str=="OFF")
      {
        digitalWrite(RELAY3, HIGH);
        flag.Relay3=0;
      }
    }
  }
  if (myObject.hasOwnProperty("ButtonX")) {
   
    if(flag.NodeFlag==1)
    {
      //Serial.print("myObject[\"ButtonA\"] = ");
      //Serial.println((const char*) myObject["ButtonA"]);
      String str=(const char*)myObject["ButtonX"];
      if(str=="ON")
      {
        digitalWrite(RELAY4, LOW);
        flag.Relay4=1;
      }
      if(str=="OFF")
      {
        digitalWrite(RELAY4, HIGH);
        flag.Relay4=0;
      }
    }
  }

  Serial.printf("startHere: Received from %u msg=%s\n", from, msg.c_str());
  flag.LEDFlag=1;
}

void newConnectionCallback(uint32_t nodeId) {
    Serial.printf("--> startHere: New Connection, nodeId = %u\n", nodeId);
   
}

void changedConnectionCallback() {
  Serial.printf("Changed connections\n");
  flag.LEDFlag=0;
}

void nodeTimeAdjustedCallback(int32_t offset) {
    Serial.printf("Adjusted time %u. Offset = %d\n", mesh.getNodeTime(),offset);
}

void setup() {
  Serial.begin(9600);
  setCpuFrequencyMhz(160);
  initial();
  //---------------------------------------------------
  pinMode(RELAY1, OUTPUT);
  digitalWrite(RELAY1, HIGH);
  pinMode(RELAY2, OUTPUT);
  digitalWrite(RELAY2, HIGH);
  pinMode(RELAY3, OUTPUT);
  digitalWrite(RELAY3, HIGH);
  pinMode(RELAY4, OUTPUT);
  digitalWrite(RELAY4, HIGH);
  //---------------------------------------------------
  //mesh.setDebugMsgTypes( ERROR | MESH_STATUS | CONNECTION | SYNC | COMMUNICATION | GENERAL | MSG_TYPES | REMOTE ); // all types on
  mesh.setDebugMsgTypes( ERROR | STARTUP );  // set before init() so that you can see startup messages
  mesh.init( MESH_PREFIX, MESH_PASSWORD, &userScheduler, MESH_PORT );
  mesh.onReceive(&receivedCallback);
  mesh.onNewConnection(&newConnectionCallback);
  mesh.onChangedConnections(&changedConnectionCallback);
  mesh.onNodeTimeAdjusted(&nodeTimeAdjustedCallback);
  userScheduler.addTask( taskSendMessage );
  taskSendMessage.enable();
}

void loop()
{
  Serial.print(F("Main at core:"));
  Serial.println(xPortGetCoreID());
  while (1)
  {
    mesh.update();
  }

}

/*--------------------------------------------------*/
void vLEDFlashTask(void *pvParameters) // This is a task.
{
  (void)pvParameters;
 
  Serial.print(F("LEDTask at core:"));
  Serial.println(xPortGetCoreID());
  pinMode(LED_BUILTIN, OUTPUT);
  //oldMillis = millis();
  for (;;) // A Task shall never return or exit.
  {
    if(flag.LEDFlag==1)
    {
      digitalWrite(LED_BUILTIN, HIGH); // turn the LED on (HIGH is the voltage level)
      vTaskDelay(200);
      digitalWrite(LED_BUILTIN, LOW); // turn the LED off by making the voltage LOW
      vTaskDelay(200);
    }
    else{
      vTaskDelay(10);
    }
  }
}

//-------------------------------------------


ESP32 DHT22 Node3 code:
#include "painlessMesh.h"
#include <Arduino_JSON.h>
#include "DHT.h"
//-------------------------------------------------------------
#define   MESH_PREFIX     "Peter1015"
#define   MESH_PASSWORD   "No18141814"
#define   MESH_PORT       5555
//-----Global variable---------------------------------------
#define LED_BUILTIN 2
//---------------DHT22--------------------
#define DHTPIN 14
#define DHTTYPE DHT22   // DHT 22  (AM2302), AM2321
DHT dht(DHTPIN, DHTTYPE);
float Humidity=0;
float Temperature=0;
//--------- Flag structure --------------------------------------
typedef struct _vFlag
{
  uint8_t LEDFlag=0;
  uint8_t NodeFlag=0;
  uint8_t FunctionFlag=1;
  uint8_t SendFlag=0;
  uint8_t dht22=1;

}vFlag;
vFlag *flag_Ptr;
vFlag flag;
typedef struct _vUart
{
  String inputString;
  String BTinputString;
} vUart;
vUart *Uart_Ptr;
vUart Uart;
//---------------------------------------------------
Scheduler userScheduler; // to control your personal task
painlessMesh  mesh;

//Number for this node
int nodeNumber = 3;
// User stub
void sendMessage() ; // Prototype so PlatformIO doesn't complain
String readings;
String getReadings();
Task taskSendMessage( TASK_SECOND * 1 , TASK_FOREVER, &sendMessage );

TaskHandle_t hled;

void vLEDFlashTask(void *pvParameters);

void initial()
{
  Serial.println(F("Create Task"));
  //----------------------------------------------------------------------
  // Now set up two tasks to run independently.
  xTaskCreatePinnedToCore(
    vLEDFlashTask, "LEDTask" // A name just for humans
    ,
    1024 // This stack size can be checked & adjusted by reading the Stack Highwater
    ,
    NULL, 2 // Priority, with 3 (configMAX_PRIORITIES - 1) being the highest, and 0 being the lowest.
    ,
    &hled //handle
    ,
    0);
  //----------------------------------------------------------------------
}
String getReadings()
{
  JSONVar jsonReadings;
 
  //Serial.print(F("meshTask at core:"));
  //Serial.println(xPortGetCoreID());
  jsonReadings["node"] = nodeNumber;

  if(flag.FunctionFlag==1)
  {
    jsonReadings["function"] = "DHT22";
  }
  jsonReadings["Temperature"] = round(100*Temperature + 0.5)/100;//小數點後兩位
  jsonReadings["Humidity"] = round(100*Humidity + 0.5)/100;//小數點後兩位
 
  readings = JSON.stringify(jsonReadings);
  return readings;
}
void sendMessage() {
  String msg = getReadings();
  //msg += mesh.getNodeId();

  if(flag.SendFlag ==1)
  {
    msg=Uart.BTinputString;
    Uart.BTinputString="";
    flag.SendFlag=0;
  }
  mesh.sendBroadcast( msg );
  taskSendMessage.setInterval( random( TASK_SECOND * 1, TASK_SECOND * 2 ));
}

// Needed for painless library
void receivedCallback( uint32_t from, String &msg )
{
  JSONVar myObject = JSON.parse(msg.c_str());
  if (myObject.hasOwnProperty("node")) {
    //Serial.print("myObject[\"node\"] = ");
    //Serial.println((int) myObject["node"]);
    if((int) myObject["node"]==10)
    {
      flag.NodeFlag=1;
    }
  }
  if (myObject.hasOwnProperty("function")) {
    //Serial.print("myObject[\"function\"] = ");
    //Serial.println((const char*) myObject["function"]);
  }
 
  Serial.printf("startHere: Received from %u msg=%s\n", from, msg.c_str());
  flag.LEDFlag=1;
}

void newConnectionCallback(uint32_t nodeId) {
    Serial.printf("--> startHere: New Connection, nodeId = %u\n", nodeId);
   
}

void changedConnectionCallback() {
  Serial.printf("Changed connections\n");
  flag.LEDFlag=0;
}

void nodeTimeAdjustedCallback(int32_t offset) {
    Serial.printf("Adjusted time %u. Offset = %d\n", mesh.getNodeTime(),offset);
}

void setup() {
  Serial.begin(9600);
  setCpuFrequencyMhz(160);
  initial();
  //---------------------------------------------------
  dht.begin();
  //---------------------------------------------------
  //mesh.setDebugMsgTypes( ERROR | MESH_STATUS | CONNECTION | SYNC | COMMUNICATION | GENERAL | MSG_TYPES | REMOTE ); // all types on
  mesh.setDebugMsgTypes( ERROR | STARTUP );  // set before init() so that you can see startup messages
  mesh.init( MESH_PREFIX, MESH_PASSWORD, &userScheduler, MESH_PORT );
  mesh.onReceive(&receivedCallback);
  mesh.onNewConnection(&newConnectionCallback);
  mesh.onChangedConnections(&changedConnectionCallback);
  mesh.onNodeTimeAdjusted(&nodeTimeAdjustedCallback);
  userScheduler.addTask( taskSendMessage );
  taskSendMessage.enable();
}

void loop()
{
  Serial.print(F("Main at core:"));
  Serial.println(xPortGetCoreID());
  while (1)
  {
    if(flag.dht22==1)
    {    
      float h = dht.readHumidity();
      Humidity = dht.readHumidity();
      // Read temperature as Celsius (the default)
      float t = dht.readTemperature();
      Temperature = dht.readTemperature();
      // Read temperature as Fahrenheit (isFahrenheit = true)
      float f = dht.readTemperature(true);
     
      // Check if any reads failed and exit early (to try again).
      if (isnan(h) || isnan(t) || isnan(f))
      {
        Serial.println("Failed to read from DHT sensor!");
        return;
      }

      // Compute heat index in Fahrenheit (the default)
      float hif = dht.computeHeatIndex(f, h);
      // Compute heat index in Celsius (isFahreheit = false)
      float hic = dht.computeHeatIndex(t, h, false);

      Serial.print("Humidity: ");
      Serial.print(h);
      Serial.print(" % ");
      Serial.print("Temperature: ");
      Serial.print(t);
      Serial.print(" *C ");
      Serial.print(f);
      Serial.print(" *F ");
      Serial.print("Heat index: ");
      Serial.print(hic);
      Serial.print(" *C ");
      Serial.print(hif);
      Serial.println(" *F");
      vTaskDelay(100);
    }
   
    mesh.update();
  }

}

/*--------------------------------------------------*/
void vLEDFlashTask(void *pvParameters) // This is a task.
{
  (void)pvParameters;
 
  Serial.print(F("LEDTask at core:"));
  Serial.println(xPortGetCoreID());
  pinMode(LED_BUILTIN, OUTPUT);
  //oldMillis = millis();
  for (;;) // A Task shall never return or exit.
  {
    if(flag.LEDFlag==1)
    {
      digitalWrite(LED_BUILTIN, HIGH); // turn the LED on (HIGH is the voltage level)
      vTaskDelay(200);
      digitalWrite(LED_BUILTIN, LOW); // turn the LED off by making the voltage LOW
      vTaskDelay(200);
    }
    else{
      vTaskDelay(10);
    }
   
  }
}

//-------------------------------------------

ESP32 Water Sensor Node4 code:
#include <WiFi.h>
//#include <WiFiClient.h>
#include <PubSubClient.h>
#include "painlessMesh.h"
#include <Arduino_JSON.h>
//-------------------------------------------------------------
#define   MESH_PREFIX     "Peter1015"
#define   MESH_PASSWORD   "No18141814"
#define   MESH_PORT       5555
//----------ssid---------------
// ------ 以下修改成你自己的WiFi帳號密碼 ------
char* ssid = "Winson_Y52";
char* password = "8888888888";
//char* ssid = "ras_2.4";
//char* password = "181415181415";
// ------ MQTT setting------
//char* MQTTServer = "broker.mqttgo.io";//https://broker.mqttgo.io/
char* MQTTServer = "test.mosquitto.org";
int MQTTPort = 1883;//MQTT Port
char* MQTTUser = "";//
char* MQTTPassword = "";//
//main level
char* MQTTPubTopic1 = "winsondiy/ESP32/WaterSensor";
char* MQTTPubTopic2 = "winsondiy/ESP32/DHT22";
char* MQTTSubTopic1 = "winsondiy/ESP32/relay1";
long MQTTLastPublishTime;//此變數用來記錄推播時間
long MQTTPublishInterval = 1000;//每5秒推撥一次
WiFiClient WifiClient;
PubSubClient MQTTClient(WifiClient);
// Example MQTT Json message
const char* sensor = "WaterLevel";
const char* exampleMQTT = "{\"sensor\":\"WaterLevel\",\"data\":[20,2]}";
//const char* exampleMQTT = "{\"data\":[20,3]}";
// Calculate needed JSON document bytes with example message
const size_t CAPACITY = JSON_OBJECT_SIZE(sizeof(exampleMQTT) + 20);

//-----Global variable---------------------------------------
#define LED_BUILTIN 2
//-----Water sensor------------------
#define POWER        34
#define SIGNAL       35
int value=0;
int level=0;
int maplevel=0;
//-------DHT22-----------------------------------------
double Humidity=0;
double Temperature=0;
//--------- Flag structure --------------------------------------
typedef struct _vFlag
{
  uint8_t LEDFlag=0;
  uint8_t NodeFlag=0;
  uint8_t FunctionFlag=1;
  uint8_t SendFlag=0;
  uint8_t sensor_Flag=1;
}vFlag;
vFlag *flag_Ptr;
vFlag flag;
typedef struct _vUart
{
  String inputString;
  String BTinputString;
} vUart;
vUart *Uart_Ptr;
vUart Uart;
//---------------------------------------------------
Scheduler userScheduler; // to control your personal task
painlessMesh  mesh;

//Number for this node
int nodeNumber = 4;
// User stub
void sendMessage() ; // Prototype so PlatformIO doesn't complain
String readings;
String getReadings();
Task taskSendMessage( TASK_SECOND * 1 , TASK_FOREVER, &sendMessage );

TaskHandle_t hled;

void vLEDFlashTask(void *pvParameters);

void initial()
{
  Serial.println(F("Create Task"));
  //----------------------------------------------------------------------
  // Now set up two tasks to run independently.
  xTaskCreatePinnedToCore(
    vLEDFlashTask, "LEDTask" // A name just for humans
    ,
    1024 // This stack size can be checked & adjusted by reading the Stack Highwater
    ,
    NULL, 2 // Priority, with 3 (configMAX_PRIORITIES - 1) being the highest, and 0 being the lowest.
    ,
    &hled //handle
    ,
    0);
  //----------------------------------------------------------------------
}
String getReadings()
{
  JSONVar jsonReadings;
 
  //Serial.print(F("meshTask at core:"));
  //Serial.println(xPortGetCoreID());
  jsonReadings["node"] = nodeNumber;
  //jsonReadings["node"] = String(normAccel.XAxis);
  //jsonReadings["node ID"] = String(mesh.getNodeId());
  if(flag.FunctionFlag==1)
  {
    jsonReadings["function"] = "WaterLevel";
  }
  jsonReadings["Level"] = maplevel;
 
  readings = JSON.stringify(jsonReadings);
  return readings;
}
void sendMessage() {
  String msg = getReadings();
  //msg += mesh.getNodeId();

  if(flag.SendFlag ==1)
  {
    msg=Uart.BTinputString;
    Uart.BTinputString="";
    flag.SendFlag=0;
  }
  mesh.sendBroadcast( msg );
  taskSendMessage.setInterval( random( TASK_SECOND * 1, TASK_SECOND * 2 ));
}

// Needed for painless library
void receivedCallback( uint32_t from, String &msg )
{
  JSONVar myObject = JSON.parse(msg.c_str());
  if (myObject.hasOwnProperty("node")) {
    //Serial.print("myObject[\"node\"] = ");
    //Serial.println((int) myObject["node"]);
    if((int) myObject["node"]==3)
    {
      flag.NodeFlag=1;
    }
  }

  if (myObject.hasOwnProperty("function")) {
    //Serial.print("myObject[\"function\"] = ");
    //Serial.println((const char*) myObject["function"]);
  }
  //---------------------------------------
  if(flag.NodeFlag==1)
  {
    if (myObject.hasOwnProperty("Temperature")) {
      //Serial.print("myObject[\"function\"] = ");
      //Serial.println((const char*) myObject["function"]);
      //Temperature=(int) myObject["Temperature"];
      Temperature=myObject["Temperature"];
     
    }
    if (myObject.hasOwnProperty("Humidity")) {
      //Serial.print("myObject[\"function\"] = ");
      //Serial.println((const char*) myObject["function"]);
      //Humidity=(int) myObject["Humidity"];
      Humidity=myObject["Humidity"];
    }
    Serial.print("T:");
    Serial.print(Temperature);
    Serial.print("H:");
    Serial.println(Humidity);



  }
 
 




  Serial.printf("startHere: Received from %u msg=%s\n", from, msg.c_str());
  flag.LEDFlag=1;
}

void newConnectionCallback(uint32_t nodeId) {
    Serial.printf("--> startHere: New Connection, nodeId = %u\n", nodeId);
   
}

void changedConnectionCallback() {
  Serial.printf("Changed connections\n");
  flag.LEDFlag=0;
}

void nodeTimeAdjustedCallback(int32_t offset) {
    Serial.printf("Adjusted time %u. Offset = %d\n", mesh.getNodeTime(),offset);
}

void setup() {
  Serial.begin(9600);
  setCpuFrequencyMhz(160);
  initial();
  //---------------------------------------------------
 
  //---------------------------------------------------
  //mesh.setDebugMsgTypes( ERROR | MESH_STATUS | CONNECTION | SYNC | COMMUNICATION | GENERAL | MSG_TYPES | REMOTE ); // all types on
  mesh.setDebugMsgTypes( ERROR | STARTUP );  // set before init() so that you can see startup messages
  mesh.init( MESH_PREFIX, MESH_PASSWORD, &userScheduler, MESH_PORT, WIFI_AP_STA, 6 );
  //-----------------------------------------------------------------
  mesh.onReceive(&receivedCallback);
  //--------------------------
  mesh.stationManual(ssid, password);  
  //mesh.setHostname(HOSTNAME);
  mesh.setRoot(true);
  mesh.setContainsRoot(true);
  //-------------------------
  mesh.onNewConnection(&newConnectionCallback);
  mesh.onChangedConnections(&changedConnectionCallback);
  mesh.onNodeTimeAdjusted(&nodeTimeAdjustedCallback);
  userScheduler.addTask( taskSendMessage );
  taskSendMessage.enable();
}

void loop()
{
  Serial.print(F("Main at core:"));
  Serial.println(xPortGetCoreID());
  while (1)
  {
    if (WiFi.status() != WL_CONNECTED)
    {
      WifiConnect();
    }
   
    if (!MQTTClient.connected())
    {
      MQTTConnect();
    }
    //如果距離上次傳輸已經超過10秒,則Publish溫溼度
    if ((millis() - MQTTLastPublishTime) >= MQTTPublishInterval )
    {
      StaticJsonDocument<CAPACITY> doc, doc1;
      doc["Level"] = maplevel;
      // Serialize JSON doc to char buffer with variable capacity (MQTT client needs char / char*)
      char JSONmessageBuffer[CAPACITY];
      //serializeJson(doc, Serial);
      serializeJson(doc, JSONmessageBuffer);
      MQTTClient.publish(MQTTPubTopic1, JSONmessageBuffer);
      //Serial.println("STATUS: Sent data via MQTT");
      Serial.println("water Data Publish to MQTT Broker");
      doc1["Temperature"] = Temperature;
      doc1["Humidity"] = Humidity;
      char JSONmessageBuffer1[CAPACITY];
      serializeJson(doc1, JSONmessageBuffer1);
      MQTTClient.publish(MQTTPubTopic2, JSONmessageBuffer1);

      doc = NULL;
      doc1 = NULL;
      MQTTLastPublishTime = millis();
    }
    MQTTClient.loop();//update status
    delay(50);
    //---------mesh----------------------
    mesh.update();
  }

}
void WifiConnect()
{
  //WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED)
  {
    delay(500);
    Serial.print(".");
  }
  Serial.println("WiFi連線成功");
  Serial.print("IP Address:");
  Serial.println(WiFi.localIP());
}
//-----------------------------------------------
void MQTTConnect()
{
  MQTTClient.setServer(MQTTServer, MQTTPort);
  MQTTClient.setCallback(MQTTCallback);
  while (!MQTTClient.connected())
  {
    //以亂數為ClietID
    String MQTTClientid = "esp32-" + String(random(1000000, 9999999));
    if (MQTTClient.connect(MQTTClientid.c_str(), MQTTUser, MQTTPassword))
    {
      //連結成功,顯示「已連線」。
      Serial.println("MQTT已連線");
      //訂閱SubTopic1主題
      MQTTClient.subscribe(MQTTSubTopic1);
    }
    else
    {
      //若連線不成功,則顯示錯誤訊息,並重新連線
      Serial.print("MQTT連線失敗,狀態碼=");
      Serial.println(MQTTClient.state());
      Serial.println("五秒後重新連線");
      delay(5000);
    }
  }
}
//------------------------------------------
//接收到訂閱時
void MQTTCallback(char* topic, byte* payload, unsigned int length)
{
  Serial.print(topic); Serial.print("訂閱通知:");
  String payloadString;//將接收的payload轉成字串
  //顯示訂閱內容
  for (int i = 0; i < length; i++)
  {
    payloadString = payloadString + (char)payload[i];
  }
  Serial.println(payloadString);
  //比對主題是否為訂閱主題1
  if (strcmp(topic, MQTTSubTopic1) == 0)
  {
    Serial.println("改變燈號:" + payloadString);
    if (payloadString == "ON")
    {
      digitalWrite(16, HIGH);
    }
    if (payloadString == "OFF")
    {
      digitalWrite(16, LOW);
    }
  }
}
/*--------------------------------------------------*/
void vLEDFlashTask(void *pvParameters) // This is a task.
{
  (void)pvParameters;
 
  Serial.print(F("LEDTask at core:"));
  Serial.println(xPortGetCoreID());
  pinMode(LED_BUILTIN, OUTPUT);
  //oldMillis = millis();
  for (;;) // A Task shall never return or exit.
  {
    if(flag.LEDFlag==1)
    {
      digitalWrite(LED_BUILTIN, HIGH); // turn the LED on (HIGH is the voltage level)
      vTaskDelay(200);
      digitalWrite(LED_BUILTIN, LOW); // turn the LED off by making the voltage LOW
      vTaskDelay(200);
    }
    else{
      vTaskDelay(10);
    }
    if(flag.sensor_Flag==1)
    {
      level=waterSensor();
      maplevel=map(level, 0, 1060, 0, 4);
      Serial.print("Water Level:");
      Serial.println(maplevel);
      delay(10);
    }
  }
}
//--------------------------------------------
int waterSensor()
{
  //digitalWrite(POWER,HIGH);
  //delay(10);
  value=analogRead(SIGNAL);
  //delay(10);
  //digitalWrite(POWER,LOW);
  return value;
}
//-------------------------------------------