9. Adding a web server to EMIT (Arduino) - ControlBits/EMIT GitHub Wiki
Until now, we've been developing firmware to measure environmental data such as temperature and humidity and transmit it to a web application (such as the EMIT LIVE dashboard) using the MQTT protocol.
Sometimes, however, we just need to remotely access the data from the sensor itself. This can be easily achieved by adding a simple Web Server to EMIT.
As a starting point for this section, we're going to go back to our firmware as it was at the end of section: 4. Connecting EMIT to the Internet and build it up from there.
The first thing we need to do is include the 'WebServer' library into the include section. This library provides all functionality we'll need for our web server.
#include <WebServer.h>
then we will create a global instance of the Webserver class representing the server listening on port 80.
// create a web server object listening on port 80.
WebServer server(80);
The defined object is initialized with one argument which is the port listening to, the standard port for HTTP is port 80.
All that is left to do now is create the HTML web page, which would be put on a global constant array emit_html_template
.
const char* emit_html_template =
"<!DOCTYPE HTML>"
"<html>"
"<head>"
"<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">"
"<link rel=\"stylesheet\" href=\"https://use.fontawesome.com/releases/v5.7.2/css/all.css\" integrity=\"sha384-fnmOCqbTlWIlj8LyTjo7mOUStjsKC4pOpQbqyi7RrhN7udi9RwhKkMHpvLbHG9Sr\" crossorigin=\"anonymous\">"
"<style>"
"html {background-color: #258ECD; font-family: Arial; display: inline-block; margin: 0px auto; text-align: center; color: #333333;}"
"h1 {font-size: 1.8rem; }"
"p {font-size: 2.4rem; }"
"sup {font-size: 1.2rem; }"
"a:link, a:visited {text-decoration: none; color: #666666;}"
"a:hover {text-decoration: underline;}"
".container { background-color: rgba(255,255,255,0.90); margin: 20px 20px 0px 20px; padding: 10px; border: 2px solid #ffffff; border-radius: 6px;}"
".label {font-size: 1.2rem;}"
".tiny {font-size: 0.8rem;}"
"</style>"
"</head>"
"<body>"
"<div class=\"container\">"
"<div>"
"<img src=\"http://controlbits.com/images/ControlBits-logo.png\" alt=\"ControlBits.com logo\" style=\"width: 150px;\">"
"</div>"
"<hr><br>"
"<h1>EMIT Web Sensor</h1>"
"<p>"
"<span class=\"label\">Temperature:</span>"
"<i class=\"fas fa-thermometer-three-quarters\" style=\"color:#059e8a;\"></i>"
"<span>%.2f<sup>°C</sup></span>"
"</p>"
"<p>"
"<span class=\"label\">Humidity: </span>"
"<i class=\"fas fa-tint\" style=\"color:#00add6;\"></i>"
"<span>%.2f<sup>%RH</sup></span>"
"</p>"
"<br><hr>"
"<a class=\"tiny\" href=\"https://ControlBits.com\">Find out more at: https://ControlBits.com</a>"
"</div>"
"</body>"
"</html>";
Notice those two line "<span>%.2f<sup>°C</sup></span>"
and "<span>%.2f<sup>%RH</sup></span>"
in the HTML template which contains %.2f
for temperature and humidity values. those %.2f
would be updated with the temperature and humidity values for the DHT22 on request.
We will define a callback function handleRoot
that would be called later by the http server on handling the /
request from the browser. the function would copy the html template into a variable and add the real values for the temperature and humidity.
// callback function that handles the '/' (root) request.
void handleRoot(void) {
// string holding the rendered html
char emit_html[2000];
// insert the temperature and humidity values to the emit_html
snprintf(
emit_html, // rendered html
2000, // length of the rendered html
emit_html_template, // constant html template
tempC, // inserted temperature value in Celsius
humidity // inserted humidity value in (%)
);
// send the html to the web client (browser)
server.send(200, //200 indicating sucess response
"text/html", // content type is html
emit_html // data we are going to send.
);
}
Note that we will move to local tempC
and humidity
variables to be global so that they would be accessible by the webserver handleRoot
callback function.
the server.send()
API, takes three arguments:
- status, which is standard to be 200 on success, 404 on not found.
- Content type, in our case its HTML.
- string to be sent as a response.
Now on setup
we will initiate the webserver and also define the callback function for the /
request.
// tell the web server, that if you received a `/`
// request call this `handleRoot function.
server.on("/", handleRoot);
// start the webserver.
server.begin();
we will print a message to the Serial Monitor indicating that the web server has started listening on port 80.
// print a message to the Serial monitor indicating that the web server is started.
Serial.println("HTTP server started");
To use our new web server in our loop The only thing we need to do is calling server.handleClient()
that does all the magic needed to handle web clients requests.
// handle incoming web clients requests.
server.handleClient();
As we need the handleClient
function to be called on high frequency, so that it would catch all the requests without any delay. we will replace the delay(5000)
function with a timer. which would help us achieving the low reading frequency needed for the DHT22 sensor and also calling handleCLient
function.
we will need to create a global timer variable sensor_read_timer
.
// unsigned 32 bits integer.
// timer for dht22 reading frequency.
uint32_t sensor_read_timer;
then the loop
code would be wrapped on a timer check condition that compares the timer value with the millis
value if it's more than 5 seconds. as stated on a previous tutorial millis
function returns the number of milliseconds since the start of the program execution.
if(millis() - sensor_read_timer > 5000){
sensor_read_timer = millis();
// loop code for reading DHT22 sensor data...
}
this would replace the delay(5000)
function, so that the loop
code would look like this.
void loop() {
// handle incoming web clients requests.
server.handleClient();
// read from the DHT22 sensor every 5 seconds
if(millis() - sensor_read_timer > 5000){
sensor_read_timer = millis(); // update the timer value with the current millis.
// turn the red LED onma
digitalWrite(EMIT_RED_LED , HIGH);
// Print a debugging message on the Serial Terminal indicating the start of the read attempt.
Serial.println("Reading AM2302 ...");
// Reading temperature or humidity takes about 250 milliseconds!
// Sensor readings may also be up to 2 seconds
humidity = dht.readHumidity();
// Read temperature as Celsius (the default)
tempC = dht.readTemperature();
// Read temperature as Fahrenheit (isFahrenheit = true)
float tempF = dht.readTemperature(true);
// turn the red LED off indicating the end of the read cycle
digitalWrite(EMIT_RED_LED , LOW);
// Print the read values on the Serial Monitor.
Serial.print("Temperature (C): ");
Serial.println(tempC);
Serial.print("Temperature (F): ");
Serial.println(tempF);
Serial.print("Humidity (%RH): ");
Serial.println(humidity);
}
}
Finally after removing the delay and adding the webserver handler, the Sketch should look like this.
// WiFi library is essential for connecting to WIFI
#include <WiFi.h>
// library needed to create a web server
#include <WebServer.h>
// include the Adafruit DHT libraries to use its functions reading the temperature and humidity from the DHT22 sensor.
#include "DHT.h"
#define EMIT_RED_LED 16
#define EMIT_GREEN_LED 17
#define EMIT_DHT_PIN 14 // Digital pin connected to the DHT sensor
#define DHTTYPE DHT22 // DHT 22 (AM2302), AM2321
// Initialize DHT sensor.
DHT dht(EMIT_DHT_PIN, DHTTYPE);
float humidity;
float tempC;
// unsigned 32 bits integer.
// timer for dht22 reading frequency.
uint32_t sensor_read_timer;
// define Wi-Fi settings
#define WIFISSID "your-WiFi-SSID-goes-here"
#define WIFIPASSWORD "your-WiFi-password-goes-here"
// create a web server object listening on port 80.
WebServer server(80);
const char* emit_html_template =
"<!DOCTYPE HTML>"
"<html>"
"<head>"
"<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">"
"<link rel=\"stylesheet\" href=\"https://use.fontawesome.com/releases/v5.7.2/css/all.css\" integrity=\"sha384-fnmOCqbTlWIlj8LyTjo7mOUStjsKC4pOpQbqyi7RrhN7udi9RwhKkMHpvLbHG9Sr\" crossorigin=\"anonymous\">"
"<style>"
"html {background-color: #258ECD; font-family: Arial; display: inline-block; margin: 0px auto; text-align: center; color: #333333;}"
"h1 {font-size: 1.8rem; }"
"p {font-size: 2.4rem; }"
"sup {font-size: 1.2rem; }"
"a:link, a:visited {text-decoration: none; color: #666666;}"
"a:hover {text-decoration: underline;}"
".container { background-color: rgba(255,255,255,0.90); margin: 20px 20px 0px 20px; padding: 10px; border: 2px solid #ffffff; border-radius: 6px;}"
".label {font-size: 1.2rem;}"
".tiny {font-size: 0.8rem;}"
"</style>"
"</head>"
"<body>"
"<div class=\"container\">"
"<div>"
"<img src=\"http://controlbits.com/images/ControlBits-logo.png\" alt=\"ControlBits.com logo\" style=\"width: 150px;\">"
"</div>"
"<hr><br>"
"<h1>EMIT Web Sensor</h1>"
"<p>"
"<span class=\"label\">Temperature:</span>"
"<i class=\"fas fa-thermometer-three-quarters\" style=\"color:#059e8a;\"></i>"
"<span>%.2f<sup>°C</sup></span>"
"</p>"
"<p>"
"<span class=\"label\">Humidity: </span>"
"<i class=\"fas fa-tint\" style=\"color:#00add6;\"></i>"
"<span>%.2f<sup>%RH</sup></span>"
"</p>"
"<br><hr>"
"<a class=\"tiny\" href=\"https://ControlBits.com\">Find out more at: https://ControlBits.com</a>"
"</div>"
"</body>"
"</html>";
void wifi_connect(void){
// change the wifi interface mode to station
WiFi.mode(WIFI_STA);
// attempt a WiFi connection to the router with credentials of WIFISSID and WIFIPASSWORD
WiFi.begin(WIFISSID, WIFIPASSWORD);
// wait for the connection to be established.
while (WiFi.status() != WL_CONNECTED){
// flash the Green LED indication a wifi connection attempt.
// turn on the Green LED
digitalWrite(EMIT_GREEN_LED , HIGH);
// print 'trying..' message to Serial Monitor
Serial.print("trying WiFi connection ");
Serial.println(WIFISSID);
// wait 1 second (1000 milliseconds)
delay(1000);
// turn off the Green LED
digitalWrite(EMIT_GREEN_LED , LOW);
// wait 2 second (2000 milliseconds)
delay(2000);
}
// if connected ...
// turn on the Green LED
digitalWrite(EMIT_GREEN_LED , HIGH);
// print 'WiFi connection successful' message to Serial Monitor
Serial.println("WiFi connection successful") ;
// print WiFi network settings (inc. IP Address) to Serial Monitor.
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
}
// callback function that handles the '/' (root) request.
void handleRoot(void) {
// string holding the rendered html
char emit_html[2000];
// insert the temperature and humidity values to the emit_html
snprintf(
emit_html, // rendered html
2000, // length of the rendered html
emit_html_template, // constant html template
tempC, // inserted temperature value in Celsius
humidity // inserted humidity value in (%)
);
// send the html to the web client (browser)
server.send(200, //200 indicating success response
"text/html", // content type is html
emit_html // data we are going to send.
);
}
void setup() {
// Enable the serial terminal
// Useful for debugging messages
Serial.begin(115200);
// configure RED LED GPIO to be OUTPUT
// so that we would control the LED (ON , OFF)
pinMode(EMIT_RED_LED , OUTPUT);
// on the beginning we will turn OFF the RED LED
digitalWrite(EMIT_RED_LED , LOW);
// configure Green LED GPIO to be OUTPUT
// so that we would control the LED (ON , OFF)
pinMode(EMIT_GREEN_LED , OUTPUT);
// on the beginning we will turn OFF the GREEN LED
digitalWrite(EMIT_GREEN_LED , LOW);
// initialize the DHT communication
dht.begin();
// attempt a wifi connection
wifi_connect();
// tell the web server, that if you received a `/`
// request call this `handleRoot function.
server.on("/", handleRoot);
// start the webserver.
server.begin();
// print a message to the Serial monitor indicating that the web server is started.
Serial.println("HTTP Socket created .. listening to port 80");
}
void loop() {
// handle incoming web clients requests.
server.handleClient();
// read from the DHT22 sensor every 5 seconds
if(millis() - sensor_read_timer > 5000){
sensor_read_timer = millis(); // update the timer value with the current millis.
// turn the red LED onma
digitalWrite(EMIT_RED_LED , HIGH);
// Print a debugging message on the Serial Terminal indicating the start of the read attempt.
Serial.println("Reading AM2302 ...");
// Reading temperature or humidity takes about 250 milliseconds!
// Sensor readings may also be up to 2 seconds
humidity = dht.readHumidity();
// Read temperature as Celsius (the default)
tempC = dht.readTemperature();
// Read temperature as Fahrenheit (isFahrenheit = true)
float tempF = dht.readTemperature(true);
// turn the red LED off indicating the end of the read cycle
digitalWrite(EMIT_RED_LED , LOW);
// Print the read values on the Serial Monitor.
Serial.print("Temperature (C): ");
Serial.println(tempC);
Serial.print("Temperature (F): ");
Serial.println(tempF);
Serial.print("Humidity (%RH): ");
Serial.println(humidity);
}
}
After you've uploaded the Sketch created above and rebooted your EMIT, it should boot up and start listening for messages over your network. The Serial Monitor should display the reassuring message:
HTTP Socket created .. listening to port 80
In order to view EMIT's web page, all you need to do is enter your EMIT's IP address into the browser of any device connected to the same Wi-Fi network as your EMIT.
EMIT's IP address is the first IP address shown after the 'WiFi connection successful' message - shown here circled in red.
If everything works, you should now see EMIT's web page!