4.5.5 网页控制:智慧农场管理系统#

请注意,使用设备时不要让水从水池和土壤池中溢出。如果水洒到其他传感器上,会导致短路,影响设备正常工作。另外,如果水洒到电池上,会导致发热和爆炸等危险。因此,请在使用设备时格外小心,尤其是幼儿使用时一定要在家长的监护下进行。为确保设备的安全运行,请遵循相关使用指南和安全规范。

Img

4.5.5.1 简介#

本教程将为您详细介绍如何使用ESP32开发板实现WIFI网页控制智慧农场管理系统,系统包括土壤湿度传感器、水位传感器、XHT11温湿度传感器、水滴传感器继电器、水泵模块、光敏传感器(光照度检测)、LED模块、风扇模块和舵机(饲料盒开关)。教程内容包括ESP32的WiFi配置、网页设置以及如何编写代码等实现远程监控和控制。您将学习如何将ESP32连接到WIFI网页服务器,并通过发布和订阅消息来读取土壤湿度、水位、温湿度、雨水量和光照强度等相关数据,并根据需要控制水泵、LED、风扇和舵机的状态。此外,无论您是物联网初学者还是有经验的开发者,本教程都将帮助您掌握ESP32基于WiFi网页的远程大棚数据监测与控制系统开发,为您的项目增添智能的农场管理功能。

4.5.5.2 接线图#

⚠️ 特别注意:智慧农场已经组装好了,这里不需要把所有的传感器和模块都拆下来又重新组装和接线。由于传感器和模块较多,接线图中的接线复杂会导致传感器和模块的引脚接线看不清,所以使用表格来表示传感器和模块的引脚连接到ESP32主控板上的对应引脚,也是为了方便您编写代码!

编号

传感器模块

ESP32主板 S 引脚

ESP32主板 V 引脚

ESP32主板 G 引脚

1

风扇

io18(IN-) | io19(IN+)

V

G

2

LCD1602模块

I2C(SDA(io21) | SCL(io22))

V

G

3

XHT11温湿度传感器

io17

V

G

4

水滴传感器

io35

V

G

5

光敏传感器

io34

V

G

6

舵 机

io26

V

G

7

无源蜂鸣器

io16

V

G

8

LED模块

io27

V

G

9

水位传感器

io33

V

G

10

土壤湿度传感器

io32

V

G

11

继电器水泵

io25

V

G

4.5.5.3 代码流程图#

Img

4.5.5.4 实验代码#

代码文件在Arduino_代码文件夹中,代码文件为4_5_5_WiFi-HTML-Smart-Farm,如下图所示:

Img

鼠标双击4_5_5_WiFi-HTML-Smart-Farm.ino即可在Arduino IDE中打开。

⚠️ 特别提醒: 打开代码文件后,需要修改ESP32开发板需要连接的WiFi名称与密码,您需要分别将 REPLACE_WITH_YOUR_SSIDREPLACE_WITH_YOUR_PASSWORD 替换为您自己的 Wi-Fi 名称和 WiFi 密码。WiFi名称和WiFi密码修改后才能上传代码,否则你的ESP32开发板将无法连接网络。

const char* ssid = "REPLACE_WITH_YOUR_SSID";  //输入你自己的WiFi名称
const char* password = "REPLACE_WITH_YOUR_PASSWORD"; //输入你自己的WiFi密码

⚠️ 注意: 请确保代码中的WiFi名称和WiFi密码与连接到您的电脑、手机/平板、ESP32开发板和路由器的网络相同,它们必须在同一局域网(WiFi)内。

⚠️ 注意: WiFi必须是2.4Ghz频率的,否则ESP32无法连接WiFi。

/*  
 * 名称   : WiFi-HTML-Smart-Farm
 * 功能   : 使用wifi网页控制智慧农场
 * 编译IDE:ARDUINO 2.3.6
 * 作者   : https://www.keyesrobot.cn/ 
*/

#include <Arduino.h>
#include <WiFi.h>
#include <WebServer.h>
#include <LiquidCrystal_I2C.h>
#include <dht11.h>
#include <ESP32Servo.h>

// 设置引脚
#define DHT11PIN        17  // 温湿度传感器引脚
#define LEDPIN          27  // LED引脚
#define SERVOPIN        26  // 舵机引脚
#define MotorPIN1       19  // 电机模块IN+引脚
#define MotorPIN2       18  // 电机模块IN-引脚
#define STEAMPIN        35  // 水滴传感器引脚
#define LIGHTPIN        34  // 光敏传感器引脚
#define SOILHUMIDITYPIN 32  // 土壤湿度传感器引脚
#define WATERLEVELPIN   33  // 水位传感器引脚
#define RELAYPIN        25   // 继电器引脚
#define BUZZERPIN       16  // 无源蜂鸣器引脚

// 初始化模块
dht11 DHT11;
LiquidCrystal_I2C lcd(0x27, 16, 2);
Servo myservo;  // 设置舵机对象控制舵机

/*替换为您的网络凭据(输入您自己的WiFi名称和密码)*/
const char* ssid = "REPLACE_WITH_YOUR_SSID";  // 输入你自己的WiFi名称
const char* password = "REPLACE_WITH_YOUR_PASSWORD"; // 输入你自己的WiFi密码

// 创建WebServer对象
WebServer server(80);

// 控制状态的变量
static int A = 0;
static int B = 0;
static int C = 0;

// HTML网页内容
const char index_html[] PROGMEM = R"rawliteral(
<!DOCTYPE HTML>
<html>
<title>TEST HTML ESP32</title>
<head>
  <meta charset="utf-8">
  <style>
    html, body {
      margin: 0;
      width: 100%;
      height: 100%;
      display: flex;
      justify-content: center;
      align-items: center;
      flex-direction: column;
      background-color: #f0f0f0;
    }

    /* 主按钮容器 */
    .btn {
      display: flex;
      justify-content: center;  /* 按钮居中 */
      gap: 10px;  /* 增加按钮之间的空格 */
      width: 320px;  /* 设置宽度以确保按钮紧密排列 */
      flex-wrap: wrap; /* 如果需要,允许按钮换行 */
      margin-bottom: 20px;  /* 按钮和数据显示之间的空格 */
    }

    /* 按钮样式 */
    .btn button {
      width: 70px;  /* 设置按钮宽度 */
      height: 70px;  /* 设置按钮高度 */
      border: none;
      font-size: 16px;
      color: #fff;
      background-color: #89e689;
      cursor: pointer;
    }

    .btn button:active {
      top: 2px;
    }

    /* 数据显示区 */
    #dht {
      text-align: center;  /* 文字居中 */
      width: 320px;  /* 与按钮容器的宽度相同 */
      color: #fff;
      background-color: #47a047;
      font-size: 18px; /* 调整字体大小以提高可读性 */
      padding: 10px;
      border-radius: 10px;  /* 圆角 */
      box-sizing: border-box;
      margin-bottom: 10px; /* 在数据显示和按钮之间增加空格 */
    }

  </style>
</head>
<body>

  <!-- Display area for sensor data -->
  <div id="dht"></div>

  <!-- Button row -->
  <div class="btn">
    <button id="btn-led" onclick="setLED()">LED灯</button>
    <button id="btn-fan" onclick="setFan()">风扇</button>
    <button id="btn-feeding" onclick="setFeeding()">喂养</button>
    <button id="btn-watering" onclick="setWatering()">抽水</button>
    <button id="btn-music" onclick="setmusic()">音乐</button>
  </div>

  <script>
    function setLED() {
      var payload = "A"; 
      var xhr = new XMLHttpRequest();
      xhr.open("GET", "/set?value=" + payload, true);
      xhr.send();
    }
    function setFan() {
      var payload = "B"; 
      var xhr = new XMLHttpRequest();
      xhr.open("GET", "/set?value=" + payload, true);
      xhr.send();
    }
    function setFeeding() {
      var payload = "C"; 
      var xhr = new XMLHttpRequest();
      xhr.open("GET", "/set?value=" + payload, true);
      xhr.send();
    }
    function setWatering() {
      var payload = "D"; 
      var xhr = new XMLHttpRequest();
      xhr.open("GET", "/set?value=" + payload, true);
      xhr.send();
    }
    function setmusic() {
      var payload = "E"; 
      var xhr = new XMLHttpRequest();
      xhr.open("GET", "/set?value=" + payload, true);
      xhr.send();
    }

    setInterval(function () {
      var xhttp = new XMLHttpRequest();
      xhttp.onreadystatechange = function () {
        if (this.readyState == 4 && this.status == 200) {
          document.getElementById("dht").innerHTML = this.responseText;
        }
      };
      xhttp.open("GET", "/dht", true);
      xhttp.send();
    }, 1000)
  </script>

</body>
</html>

)rawliteral";

// 将传感器数据合并为HTML格式
String Merge_Data(void) {
  String dataBuffer;
  String Humidity;
  String Temperature;
  String Steam;
  String Light;
  String SoilHumidity;
  String WaterLevel;
  
  // 读取DHT11传感器
  int chk = DHT11.read(DHT11PIN);
  
  // 读取其他传感器
  Steam = String(analogRead(STEAMPIN) / 4095.0 * 100);
  Light = String(analogRead(LIGHTPIN));
  int shvalue = analogRead(SOILHUMIDITYPIN) / 4095.0 * 100 * 2.3;
  shvalue = shvalue > 100 ? 100 : shvalue;
  SoilHumidity = String(shvalue);
  int wlvalue = analogRead(WATERLEVELPIN) / 4095.0 * 100 * 2.5;
  wlvalue = wlvalue > 100 ? 100 : wlvalue;
  WaterLevel = String(wlvalue);
  Temperature = String(DHT11.temperature);
  Humidity = String(DHT11.humidity);
  
  // 构建HTML内容
  dataBuffer += "<p>";
  dataBuffer += "<h1>传感器数据</h1>";
  dataBuffer += "<b>温度: </b><b>" + Temperature + "</b><b>℃</b><br/>";
  dataBuffer += "<b>湿度: </b><b>" + Humidity + "</b><b>%RH</b><br/>";
  dataBuffer += "<b>水位: </b><b>" + WaterLevel + "</b><b>%</b><br/>";
  dataBuffer += "<b>雨量: </b><b>" + Steam + "</b><b>%</b><br/>";
  dataBuffer += "<b>光强: </b><b>" + Light + "</b><br/>";
  dataBuffer += "<b>土壤湿度: </b><b>" + SoilHumidity + "</b><b>%</b><br/>";
  dataBuffer += "</p>";

  return dataBuffer;
}

// 根据收到的HTTP请求配置操作
void Config_Callback() {
  if (server.hasArg("value")) {
    String HTTP_Payload = server.arg("value");
    Serial.printf("[%lu]%s\r\n", millis(), HTTP_Payload.c_str());

    if (HTTP_Payload == "A") {
      if (A) {
        digitalWrite(LEDPIN, LOW);
        A = 0;
      } else {
        digitalWrite(LEDPIN, HIGH);
        A = 1;
      }
    }

    if (HTTP_Payload == "B") {
      if (B) {
        digitalWrite(MotorPIN1, LOW);
        digitalWrite(MotorPIN2, LOW);
        B = 0;
      } else {
        delay(500);
        digitalWrite(MotorPIN1, HIGH);
        digitalWrite(MotorPIN2, LOW);
        delay(500);
        B = 1;
      }
    }

    if (HTTP_Payload == "C") {
      if (C) {
        myservo.write(180);
        delay(1000);
        C = 0;
      } else {
        myservo.write(80);
        delay(1000);
        C = 1;
      }
    }

    if (HTTP_Payload == "D") {
      digitalWrite(RELAYPIN, HIGH);
      delay(400);
      digitalWrite(RELAYPIN, LOW);
      delay(700);
    }
    if (HTTP_Payload == "E") {
      ledcWriteTone(BUZZERPIN, 262);
      delay(200);
      ledcWriteTone(BUZZERPIN, 294);
      delay(200);
      ledcWriteTone(BUZZERPIN, 330);
      delay(200);
      ledcWriteTone(BUZZERPIN, 349);
      delay(200);
      ledcWriteTone(BUZZERPIN, 392);
      delay(200);
      ledcWriteTone(BUZZERPIN, 440);
      delay(200);
      ledcWriteTone(BUZZERPIN, 494);
      delay(200);
      ledcWriteTone(BUZZERPIN, 0);
      delay(500);
    }
  }
  server.send(200, "text/plain", "OK");
}

// 处理无效的URL访问
void notFound() {
  server.send(404, "text/plain", "Not found");
}

void setup() {
  Serial.begin(9600);
  
  // 连接WiFi
  WiFi.begin(ssid, password);
  while (!WiFi.isConnected()) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("WiFi connected..!");
  Serial.println("Got IP: ");
  Serial.println(WiFi.localIP());

  // 设置引脚模式
  pinMode(LEDPIN, OUTPUT);
  pinMode(STEAMPIN, INPUT);
  pinMode(LIGHTPIN, INPUT);
  pinMode(SOILHUMIDITYPIN, INPUT);
  pinMode(WATERLEVELPIN, INPUT);
  pinMode(RELAYPIN, OUTPUT);
  pinMode(MotorPIN1, OUTPUT);
  pinMode(MotorPIN2, OUTPUT);
  pinMode(BUZZERPIN, OUTPUT);

  delay(1000);

  // 连接舵机引脚
  myservo.attach(SERVOPIN);
  myservo.write(180);
  delay(1000);

 //配置LEDC渠道
  ledcAttachChannel(BUZZERPIN, 1000, 8, 4);

  // 初始化LCD
  lcd.init();
  lcd.backlight();
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("IP:");
  lcd.setCursor(0, 1);
  lcd.print(WiFi.localIP());

  // 设置服务器路由器
  server.on("/", HTTP_GET, []() {
    server.send(200, "text/html", index_html);
  });

  server.on("/dht", HTTP_GET, []() {
    server.send(200, "text/plain", Merge_Data().c_str());
  });

  server.on("/set", HTTP_GET, Config_Callback);
  server.onNotFound(notFound);

  // 启动服务器
  server.begin();
}

void loop() {
  server.handleClient();
}

4.5.5.5 实验结果#

按照接线图接好线,外接电源,选择好正确的开发板板型(ESP32 Dev Module)和 适当的串口端口(COMxx),然后单击按钮Img上传代码,外接电源。

⚠️ 注意:手机/平板和电脑一定要与ESP32开发板连接的是同一个WiFi,否则将无法进入控制页面,还有就是ESP32开发板在使用WiFi功能时功耗很大需要外接DC电源才能满足它的工作电力需求,如果达不到它的工作电力需求ESP32板将会一直复位导致代码无法正常运行。

上传代码成功后,单击Arduino IDE右上角的Sarial打开串口监视器窗口并设置串口波特率为9600,串口监视器窗口便会显示连接成功后的IP地址(如果看不到可以按复位按键重新连接一次),同时LCD显示屏也会显示IP地址。

Img

Img

然后,您可以在手机端/平板端的浏览器输入串口监视器打印的 ESP32 IP地址 或 LCD显示屏显示的 ESP32 IP地址,使用浏览器打开ESP32的IP地址,并访问网页。在本例中,您可以在浏览器中输入你自己的ESP32 IP地址(这里是以192.168.36.189为例,而你需要将IP地址:192.168.36.189 修改成你自己的 ESP32 IP地址),在浏览器中打开ESP32的IP地址来查看传感器数据显示和模块控制网页(如果发现网页很小,可以用两只手指接触手机/平板的屏幕滑动放大)。网页中显示当前的温度、湿度、水池水位和土壤湿度等设备,还有LED灯、风扇、饲料盒、抽水泵和音乐等设备。而且,我们可以通过手机/平板操作这些设备。

Img

⚠️ 注意:当电脑、手机/平板和ESP32开发板,连接到同一个网络(WiFi)时,可在手机端/平板端打开这个网页。此处是你自己ESP32的IP地址

在手机端/平板端的浏览器中打开对应的IP地址,即可查看到对应传感器的数据,还可以控制LED、风扇、饲养盒、继电器抽水和音乐播放等功能。

Img

传感器数据

可控制

温度(℃)

LED 开/关

湿度(%RH)

风扇 开/关

水位(%)

饲料盒 开/关

雨量(%)

抽水泵 开/关

光照(0~4095)

播放音乐

土壤湿度(%)

在以上的Arduino教程中,我们学会了如何使用ESP32开发板,创建一个网页并将传感器数据显示出来。例如,我们可以显示当前的温度、湿度、水池水位和土壤湿度等等。我们也可以使用这个开发板来控制LED灯、风扇、饲料盒和抽水泵等设备。而且,我们可以通过手机或电脑远程操作这些设备。

Img

通过这个教程,我们可以模拟真实的农场,并实现农场的智能化控制,使物联网、信息化、自动化和智能化成为可能。这样的技术可以让我们更方便地控制农场设备,提高农业生产的效率和质量。