2023年11月8日 星期三

ESP32 water level monitoring system

Purpose:

This project is constructed uses ESP32 with HC-SR04 ultrasonic sensor and water sensor to detect the position of water in the water storage tank. If the water level is lower than the set water level limit, use relay to start the pumping motor to pump water into the water storage tank. When the water sensor senses the water level stops pumping, and the value is sent to the MQTT server and displayed through the Node-red dashboard.

這個專題是使用 ESP322搭配HC-SR04 超音波感測器和water sensor來檢測儲水箱中水的位置, 若低於設定的水位就利用relay去開啟抽水馬達抽水進入儲水箱當water sensor感測到水位就停止抽水, 然後將值發送到 MQTT 伺服器並透過 Node-red 儀表板顯示其數值.

Fundamental:

Water Level Sensor

A water level sensor is a module designed to sense water, even a small amount of moisture. Such a function can be useful in detecting leaks, rain, and even other moisture sensing functions other than water levels. Looking at the water level sensor, it has a series of parallel copper strips on the length of the module. Five of these are power strips and the other five are sensor strips. When the module is not submerged in water, there is a high resistance between the strips. As the strips come into contact with water, the resistance is lowered and conductivity is increased. The sensor has three pins, one is the VCC pin, the other is the GND pin, and the third is the SIGNAL pin to be attached to the pin where we will take our reading. On the module, the pins are designated as “+”, ”-”, and “S” respectively. 

Water level Sensor Pin Description:
1. S: is an analog output.
2. VCC: The Supply voltage Pin is 3.3v to 5v.
3. GND: is simply a ground connection.

之前的文章列表(previously project list):
HC-SR04 Ultrasonic Sensor
ESP32-CAM video streaming with Node Red 
Node-RED with ESP32 communication via Serial Port

BOM(Bill Of Material):
圖一: Water Sensorter Sensor
圖二: LCD 20*2

圖三:HC-SR40

圖四:  Real


Circuit:

YouTub Demo:

ESP32 code:
#include <WiFi.h>
#include <PubSubClient.h>
#include <Wire.h>
#include <LiquidCrystal.h>
#include <ArduinoJson.h>
//--------- Flag structure --------------------------------------
typedef struct _vFlag
{
  uint8_t PumpFlag = 0;
  uint8_t HCSR04Flag = 1;
  uint8_t sensor_Flag = 1;
  uint8_t FunctionFlag = 0;
} vFlag;
vFlag *flag_Ptr;
vFlag flag;
//----------ssid---------------
// ------ 以下修改成你自己的WiFi帳號密碼 ------
char* ssid = "Winson_Y52";
char* password = "8888888888";
//---LCD -----------
//LiquidCrystal_I2C LCD = LiquidCrystal_I2C(0x27, 16, 2);
LiquidCrystal LCD(22,23,5,18,19,21);
//------LED------------------
#define LED_BUILTIN  2
//-----Relay------------------
#define RELAY1       15
//-----Water sensor------------------
#define POWER        34
#define SIGNAL       35
int value=0;
int level=0;
//-----switch------------------
#define MODE_PIN      27
#define LASER_PIN     26
//-----hcsr04 sensor------------------
#define TRIGPIN_PIN12 12
#define ECHO_PIN14    14
long duration;
int distance;
// ------ 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/container";

char* MQTTSubTopic1 = "winsondiy/ESP32/relay1";
long MQTTLastPublishTime;//此變數用來記錄推播時間
long MQTTPublishInterval = 1000;//每5秒推撥一次
WiFiClient WifiClient;
PubSubClient MQTTClient(WifiClient);
// Example MQTT Json message
const char* sensor = "HCSR04";
const char* exampleMQTT = "{\"sensor\":\"HCSR04\",\"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);
//--------- 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;
  char line[128];
  //char line1[128];
  char BTline[20];
  //char R_line[20];
  //char L_line[20];
  String inputString;
  String BTinputString;
  String S1inputString;
  int V[16];
  char ctemp[30];
  char I2C_Data[80];
  int DC_Spped = 50;
  float Voltage[16];
  int Buffer[128];
  int StartCnt = 0;
  int ReadCnt = 0;
  int sensorValue = 0;
} vUart;
vUart *Uart_Ptr;
vUart Uart;
//-------------------------------------
TaskHandle_t huart;
TaskHandle_t hfunction;

void vUARTTask(void *pvParameters);
void vFunctionTask(void *pvParameters);
//------------------------------------------------------------------------------
void initial()
{
  Serial.println(F("Create Task"));
  //----------------------------------------------------------------------
  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);

  xTaskCreatePinnedToCore(
    vFunctionTask, "FunctionTask"
    ,
    1024 // Stack size
    ,
    NULL, 1 // Priority
    ,
    &hfunction
    ,
    0);

  //----------------------------------------------------------------------
  //vTaskSuspend(hfunction); //暫停TASK運行
  //----------------------------------------------------------------------
}
//-------------------------------------------------
void setup()
{
  Serial.begin(9600);
  Serial.println(F("init"));
  initial();
  pinMode(LED_BUILTIN, OUTPUT);
  //LCD.init();
  LCD.begin(20, 2);
  LCD.clear();
  //LCD.backlight();
  LCD.setCursor(0, 0);
  LCD.print("Distance (cm) ");
  LCD.setCursor(0, 1);
  LCD.print("------------- ");
  pinMode(TRIGPIN_PIN12, OUTPUT);
  pinMode(ECHO_PIN14, INPUT);
  pinMode(LASER_PIN, OUTPUT);
  pinMode(MODE_PIN, INPUT_PULLUP);
  pinMode(RELAY1, OUTPUT);
  digitalWrite(RELAY1, HIGH);
  pinMode(POWER,OUTPUT);
  digitalWrite(POWER,LOW);
}
//-----------------------------------------
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;
      doc["HCSR04"] = distance;
      doc["Water_sensor"] = level;
      doc["Relay1"] = flag.PumpFlag;
      // 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");
      doc = NULL;
      MQTTLastPublishTime = millis();
    }
    MQTTClient.loop();//update status
    delay(50);
    if(flag.sensor_Flag==1)
    {
      if(flag.PumpFlag==1)
      {
        if(level>0)
        {
          digitalWrite(RELAY1, HIGH);
          flag.PumpFlag=0;
        }
      }
    }

    if(flag.HCSR04Flag==1)
    {
      if(flag.PumpFlag==0)
      {
        if(distance >= 5)
        {
          digitalWrite(RELAY1, LOW);
          flag.PumpFlag=1;
        }
      }    
    }
    //vTaskDelay(5);
  }
}
//----------------------------------------
void WifiConnect()
{
  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 processCommand(char *data)
{
  int len, xlen, ylen, zlen, alen;
  int tempDIO;
  String stemp;

  len = Uart.inputString.length();
  //---------------------------------------
  if (strstr(data, "VER") != NULL)
  {
    Serial.println(F("ESP32_20230710"));
  }
  //-------------- I2C --------------------
  if (strstr(data, "HCSR04_ON") != NULL)
  {
    flag.HCSR04Flag = 1;
    digitalWrite(LASER_PIN, HIGH);
    Serial.println(F("HCSR04_ON"));
  }
  if (strstr(data, "HCSR04_OFF") != NULL)
  {
    flag.HCSR04Flag = 0;
    digitalWrite(LASER_PIN, LOW);
    Serial.println(F("HCSR04_OFF"));
  }  
  if (strstr(data, "WATER_ON") != NULL)
  {
    flag.sensor_Flag = 1;
    Serial.println(F("WATER_ON"));
  }
  if (strstr(data, "WATER_OFF") != NULL)
  {
    flag.sensor_Flag = 0;
    Serial.println(F("WATER_OFF"));
  }
}
//-----------------------------------------
//-------------------------------------------
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 vFunctionTask(void *pvParameters)
{
  (void)pvParameters;

  Serial.print(F("FunctionTask at core:"));
  Serial.println(xPortGetCoreID());
  for (;;) // A Task shall never return or exit.
  {
    if(flag.sensor_Flag==1)
    {
      level=waterSensor();
      level=map(level, 0, 1060, 0, 4);
      Serial.print("Water Level:");
      Serial.println(level);
      delay(10);
    }
    if(flag.HCSR04Flag==1)
    {
      digitalWrite(TRIGPIN_PIN12, LOW);  //關閉超音波
      delayMicroseconds(5);  //sustain at least 10us HIGH pulse
      digitalWrite(TRIGPIN_PIN12, HIGH); //啟動超音波
      delayMicroseconds(10);  //sustain at least 10us HIGH pulse
      digitalWrite(TRIGPIN_PIN12, LOW);  //關閉超音波
      duration= pulseIn(ECHO_PIN14, HIGH);
      distance= duration/29/2;

      if (duration==0)
      {
        Serial.println("No pulse is from sensor");
      }
      else {
        Serial.print("Ultrasonic sensor is shown distance:");
        Serial.print(distance);
        Serial.println("cm");
        LCD.clear();
        LCD.setCursor(0, 0);
        LCD.print("Distance (cm) -- ");
        LCD.print(distance);
        LCD.setCursor(0, 1);
        LCD.print("Water Sensor -- ");
        LCD.print(level);
      }
      delay(10);
    }
    vTaskDelay(1);
  }
}
//--------------------------------------------
int waterSensor()
{
  value=analogRead(SIGNAL);
  return value;
}


沒有留言:

張貼留言