Introduction
In the previous tutorial, we have built a simple linear regression model for predicting rain in C. Then, we have implemented the system in ESP32 Arduino. We have used humidity value fetched by DHT11 sensor as input in inference process (prediction).
System Overview
The following figure shows the block diagram of this tutorial. We have got the ESP32 DevKit v1 board connected to the WiFi access point (AP). Then, we have an embedded web server runs on the ESP32. The development host is connected to the the same AP. Then, we can use a web browser to make an HTTP request to the web server. After that, the web server sends an HTTP response which contains the web page. The web page displays the rain prediction result that is calculated on the ESP32 web server.
Schematic Diagram
The following figure shows the schematic diagram of the ESP32 (in this case, I use the ESP32 DevKit v1).
- First, connect the VCC and GND from ESP32 to DHT11.
- Second, connect a GPIO (in this case, I use D4, you can use other pins) from ESP32 to the data pin of DHT11.
- Third, connect a 10k resistor from the VCC to the GPIO.
Recommended reading: ESP32 DHT11/DHT22 Web Server – Temperature and Humidity using Arduino IDE
Arduino Library
In order to follow this tutorial, we need the following libraries installed:
- Adafruit unified sensor
- DHT sensor library
- ESPAsyncWebServer
- AsyncTCP for ESP32
Arduino Sketch
In this section, we are going to build an embedded web server that can predict whether it is going to rain or not based on real-time humidity value from DHT11 sensor. The Arduino sketch consists of three parts: logistic regression, DHT11 sensor, and web server. The following code shows the complete Arduino sketch.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 |
// Author: Erwin Ouyang, aiotedge.tech // Date : 25 Jan 2020 #include <DHT.h> #include <SPIFFS.h> #include <WiFi.h> #include <ESPAsyncWebServer.h> #define DHTPIN 4 #define DHTTYPE DHT11 const char* ssid = "Huawei-E5573"; const char* password = "huaweie5573"; DHT dht(DHTPIN, DHTTYPE); AsyncWebServer server(80); float humidity; void setup() { // Initialize serial port Serial.begin(115200); // Initialize DHT11 dht.begin(); // Initialize SPIFFS if(!SPIFFS.begin(true)) { Serial.println("An Error has occurred while mounting SPIFFS"); return; } // Connect to Wi-Fi WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(1000); Serial.println("Connecting to WiFi.."); } // Print ESP32 Local IP Address Serial.println(WiFi.localIP()); // Route for web page and libraries server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) { request->send(SPIFFS, "/index.html"); }); server.on("/bootstrap.min.css", HTTP_GET, [](AsyncWebServerRequest *request) { request->send(SPIFFS, "/bootstrap.min.css"); }); server.on("/jquery-3.3.1.min.js", HTTP_GET, [](AsyncWebServerRequest *request) { request->send(SPIFFS, "/jquery-3.3.1.min.js"); }); server.on("/bootstrap.min.js", HTTP_GET, [](AsyncWebServerRequest *request) { request->send(SPIFFS, "/bootstrap.min.js"); }); server.on("/popper.min.js", HTTP_GET, [](AsyncWebServerRequest *request) { request->send(SPIFFS, "/popper.min.js"); }); server.on("/a076d05399.js", HTTP_GET, [](AsyncWebServerRequest *request) { request->send(SPIFFS, "/a076d05399.js"); }); server.on("/predict_not_rain.png", HTTP_GET, [](AsyncWebServerRequest *request) { request->send(SPIFFS, "/predict_not_rain.png"); }); server.on("/predict_rain.png", HTTP_GET, [](AsyncWebServerRequest *request) { request->send(SPIFFS, "/predict_rain.png"); }); server.on("/humidity", HTTP_GET, [](AsyncWebServerRequest *request){ request->send_P(200, "text/plain", readDHT11Humidity().c_str()); }); server.on("/predict", HTTP_GET, [](AsyncWebServerRequest *request){ request->send_P(200, "text/plain", predictRain().c_str()); }); // Start server server.begin(); } void loop() { } String readDHT11Humidity() { float h = dht.readHumidity(); if (isnan(h)) humidity = humidity; else humidity = h; return String((int)humidity); } float sigmoid(float x) { return 1/(1 + exp(-x)); } String predictRain() { float theta_0 = -9.33; float theta_1 = 0.18; float h = sigmoid(theta_0 + theta_1*humidity); return String(round(h), 0); } |
Logistic Regression
First, let us define the sigmoid function and predictRain function in line 96-99 and line 101-108, respectively. In predictRain function, we define the \(\theta_{0}\) and \(\theta_{1}\) as theta_0 and theta_1. Then, we initialize them with pre-trained values in line 103-104. After that, we define the hypothesis function in line 105. Finally, we return the rounded prediction as string in line 107.
DHT11 Sensor
Second, let us configure the DHT11 and create a function for reading humidity value. In line 9-10, we define the DHT GPIO pin and DHT type. Then, in line 15 and 24, we initialize the DHT11. After that, we define a function called readDHT11Humidity. In this function, we read the humidity value from DHT11 by using readHumidity method, which is part of DHT library. The humidity global variable stores the humidity value.
Web Server
Third, let us create the web server and put the code all together. The web server consists of three parts: SPIFFS, WiFi, and the server itself. We use SPIFFS for storing the web files (HTML, CSS, and JS). The WiFi is configured as client that connects to the AP.
This is how to put the code all together:
- Firstly, we initialize the peripherals, such as serial, DHT, SPIFFS, and WiFi in line 22, 24, 27-31, and 34-39, respectively.
- Then, we define the server in line 16. It listens on port 80, the default HTTP port.
- After that, we define the handlers that correspond to each HTTP request.
- In line 45-68, the handles send the web files (HTML, CSS, JS, and image) to the web client.
- In line 69-74, the handles send the humidity value and prediction result to the web client.
- Finally, we start the server in line 77.
When the web server receives HTTP request on /humidity and /predict URLs, then it calls the readDHT11Humidity and predictRain functions, respectively. These functions return the humidity value and prediction result, respectively.
Web Page
In this section, we are going to build a web page that can display the humidity value and prediction result from the web server. We also use Bootstrap library for styling the HTML elements. The following code shows the complete HTML code.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <title>Rain Prediction</title> <link rel="stylesheet" href="bootstrap.min.css"> <script src="jquery-3.3.1.min.js"></script> <script src="popper.min.js"></script> <script src="bootstrap.min.js"></script> <script src='a076d05399.js'></script> </head> <body> <div class="container-fluid"> <div class="row h-100"> <div class="col-sm-8 col-lg-4 offset-sm-2 offset-lg-4 my-auto"> <div class="card"> <div class="card-header text-center"> <strong>Rain Prediction</strong> </div> <div class="card-body text-center"> <form> <h1 style="font-size: 4em;"><i class="fas fa-tint"></i> <span id="humidity">0</span>%</h1> <img id="result" src="predict_not_rain.png" style="width: 128px; height: 128px;"> </form> </div> </div> </div> </div> </div> <script> setInterval(function() { var xhttp = new XMLHttpRequest(); xhttp.onreadystatechange = function() { if (this.readyState == 4 && this.status == 200) { var humidity = parseInt(this.responseText); document.getElementById("humidity").innerHTML = humidity; } }; xhttp.open("GET", "/humidity", true); xhttp.send(); }, 5000); setInterval(function() { var xhttp = new XMLHttpRequest(); xhttp.onreadystatechange = function() { if (this.readyState == 4 && this.status == 200) { var rain = parseInt(this.responseText); if (rain == 1) document.getElementById("result").src = "predict_rain.png"; else document.getElementById("result").src = "predict_not_rain.png"; } }; xhttp.open("GET", "/predict", true); xhttp.send(); }, 5000); </script> </body> </html> |
HTML
First, let us build the layout. We use Bootstrap container-fluid class as the main container. Within the main container, we use Bootstrap grid system that consists of one vertically and horizontally centered column. Then, within the column, we use Bootstrap card class. Finally, within the body of the card, we create a text placeholder for displaying the humidity value (line 22) and use img tag to display an image that shows the rain prediction result (line 23). The humidity value and rain prediction result are updated using the JavaScript.
JavaScript
Second, let us build the functionality of the web page using JavaScript. We use setInterval function for requesting humidity value and rain prediction result every 5 seconds. So, every 5 seconds we send HTTP GET request on /humidity and /predict URLs to the web server. Then, the web server sends the response that will be displayed on the web page. In line 36-37, the code updates the humidity value. In line 48-52, the code updates the rain prediction result. It changes the src attribute of the img tag with the correct image.
Demonstration
First of all, we should compile and upload the sketch to the ESP32. After that, upload the sketch data (the web files). Then, wait till the ESP32 is connected to the AP. The ESP32 should get an IP address from AP, i.e. we use the DHCP feature on the AP.
Next, let us open a web browser, and the IP address of the ESP32 on the URL input. You should see the web page loaded to the web browser. After that, check that the humidity value and rain prediction are being read correctly from the web server.
Source Code
You can get the source code from this repository.
Summary
In this tutorial, we have learned how to build an embedded web server for rain prediction application. We have used real-time humidity value from DHT11 as input for the logistic regression model. Then, we have built a web UI for displaying the humidity value and rain prediction result.