Introduction
In this tutorial we are going to learn how to display luminosity levels on a real-time web dashboard served by the ESP32. We will be using the ESP-DASH library and the Arduino core.
When we think about sensor dashboards, we usually picture numbers to represent the sensor values. This is very common if we are working with measurements such as temperature, which are easy for a user to quantify in relation to his experience. For example, if we think about 40ºC, most of us agree that it is hot, and if we thing about 0ºC, we also mentally infer that it is cold.
Nonetheless, we may want to display other measurements that a common person may not work with with everyday. So, in some cases, it may be more user friendly to display the values in a more subjective scale.
Let’s take as example luminosity. If we say that the luminous intensity is of 50 candela, probably most of us won’t know if that is bright or not. If we use lux, it probably won’t help much either. Nonetheless, if we say that a room ambient light is bright or dark, we all can relate to this. Of course that “bright” and “dark” are subjective, pretty much like “hot” or “cold”, but it is probably more user friendly that the other option.
As such, we are going combine the knowledge we have gathered from previous posts to setup a dashboard that will display the luminosity taken from an ambient light sensor and display it in a scale that has 3 values:
- Dark
- Medium
- Bright
Naturally, as you will see in the code section below, you can use other values for the scale, and even have more levels. The aim of this tutorial is just to introduce a simple way of converting and displaying such scale, so you can adapt to your actual use case. Take also in consideration that this can be applied to other sensor types.
As mentioned, the code below will be based on topics covered on previous tutorials. Although the code pieces are explained in the code section, going through these tutorials may help you better understand the basics behind:
- Ambient Light Sensor: Explains the basics on how to connect the analog ambient light sensor to the ESP32 (including a wiring diagram).
- Ticker Library: Explains how to use the Ticker library to setup the periodic execution of functions (in our tutorial, we use this to get the sensor measurements, avoiding introducing delays in the Arduino main loop).
- Real-time web dashboard: Teaches how to get started with the ESP-DASH library.
- Generic Card: Explains the usage of the generic card of the ESP-DASH library.
The tests shown below were performed on a ESP32-E FireBeetle board from DFRobot. The Arduino core version used was 2.0.0 and the Arduino IDE version was 1.8.15, working on Windows 8.1. The code was also tested on Platformio.
The dashboard code
We will start our code with the library includes. We will need the following libraries:
- Ticker.h: to setup the periodic execution of the function that will fetch the measurements from the light sensor and update the card.
- WiFi.h: to connect the ESP32 to a WiFi network.
- ESPAsyncWebServer.h: to setup an async web server to run on the ESP32. It is used under the hood by the ESP-DASH library.
- ESPDash.h: to be able to serve and update a real-time web dashboard from the ESP32.
#include<Ticker.h>
#include<WiFi.h>
#include<ESPAsyncWebServer.h>
#include<ESPDash.h>
To be able to connect the ESP32 to a WiFi network, we will need its credentials. More precisely, we will define a variable to hold the network name (SSID) and another to hold the password. Note that, below, I’ll be using placeholders that you should replace by the actual credentials of your network.
const char* ssid = "yourNetworkName";
const char* password = "yourNetworkPass";
We will now create an object of class AsyncWebServer, which is used under the hood by the dashboard library. The constructor of this class receives as input the number of the port where the server will be listening for incoming requests. We will set it to listen on port 80, which is the default HTTP port.
AsyncWebServer server(80);
Then we will create an object of class ESPDash. As input, the constructor of this class receives the address of an AsyncWebServer object. Naturally, we will pass the address of the object we have just created.
ESPDash dashboard(&server);
We will also define a global variable to hold the number of the ESP32 GPIO that is connected to the light sensor. I’ll be using pin 36, but you may use another analog pin if you prefer. Nonetheless, if you use a different pin, please take in consideration that when we are using WiFi (which is our case) we cannot make use of the analog pins of the ADC2 of the ESP32.
const int sensorPin = 36;
To be able to setup the periodic execution of the function that will get the measurements and update the dashboard card, we will create an object of class Ticker. We will later use this object, in the Arduino setup function, to specify which function will be executed and what is the interval between executions.
Ticker measurementTicker;
To finalize, we will create an object of class Card. This object will be used to add the luminosity card to the dashboard and also to perform the updates of the displayed value.
The constructor of this class receives the following inputs:
- The address of our ESPDash object;
- The type of card. Since there is no card dedicated to luminosity, we will use the generic card.
- The title of the card. We will simply call it “Luminosity”.
Card lightSensorCard(&dashboard, GENERIC_CARD, "Luminosity");
Moving on to the Arduino setup, we will start by opening a serial connection, so we are able to output content from our program.
Serial.begin(115200);
After that, we will take care of connecting the ESP32 to the WiFi network, using the previously defined global variables. After the procedure is finished, we will print the local IP address assigned to the ESP32, which will be needed for us to reach the server and get the dashboard.
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.println("Connecting to WiFi..");
}
Serial.println(WiFi.localIP());
We also need to initialize our server, so it starts listening to incoming HTTP requests.
server.begin();
To finalize the Arduino setup, we will take care of defining the periodic execution of the function that takes care of updating the card. We will call this function getMeasurement (we will analyze its implementation below) and setup the Ticker object to run it every 5 seconds.
measurementTicker.attach_ms(5000, getMeasurement);
The full Arduino setup function is available in the snippet below.
void setup(){
Serial.begin(115200);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.println("Connecting to WiFi..");
}
Serial.println(WiFi.localIP());
server.begin();
measurementTicker.attach_ms(5000, getMeasurement);
}
The Arduino main loop will be left empty since we are using the Ticker lib to take care of the periodic execution of the card updates. If you don’t want to have this dependency on the library, you can instead use the Arduino main loop and some delays.
void loop(){}
To finalize our code, we will analyze the implementation of the getMeasurement function, which will return void and receive no parameters.
void getMeasurement(){
// Get measurement
// Map to string
// Update card
}
The first thing we will do is obtaining the measurement with a call to the analogRead function. As input we will pass the number of the pin connected to the sensor and as output we will obtain the measurement, in a scale from 0 (no light) to 4095 (full brightness).
int analogVal = analogRead(sensorPin);
Now we are going to perform the conversion of this analog value to a string from the scale mentioned in the introductory section. Naturally, the levels of this scale and the mapping from the measured value are very subjective and you should adapt them to your use case.
I’ll be performing the conversion inside the getMeasurement function with some IF ELSE conditions since I only have 3 values in the scale I’m using. Nonetheless, if you have more levels or require a more complex mapping, it would be cleaner to create a dedicated function that receives the analog value and returns the mapped string, instead of doing this directly in the getMeasurement function.
std::string luminosityLevel;
if(analogVal < 500){
luminosityLevel = "Dark";
}else if(analogVal >3500){
luminosityLevel = "Bright";
}else{
luminosityLevel = "Medium";
}
After obtaining the mapped value, we can call the update method on our Card object, passing as input that value. Note however that, since we used a std::string to hold the value and the update method doesn’t accept this type as input, we need to call the c_str method before. This method returns the equivalent C string, which the update method accepts.
lightSensorCard.update(luminosityLevel.c_str());
Note that calling the update method on the Card object only stores the value but doesn’t send the update to the dashboard rendered in the browser of the user. To do so, we must finalize by calling the sendUpdates method on our dashboard object.
dashboard.sendUpdates();
The complete function is available below.
void getMeasurement(){
int analogVal = analogRead(sensorPin);
std::string luminosityLevel;
if(analogVal < 500){
luminosityLevel = "Dark";
}else if(analogVal >3500){
luminosityLevel = "Bright";
}else{
luminosityLevel = "Medium";
}
lightSensorCard.update(luminosityLevel.c_str());
dashboard.sendUpdates();
}
The full code is shown below.
#include<Ticker.h>
#include<WiFi.h>
#include<ESPAsyncWebServer.h>
#include<ESPDash.h>
const char* ssid = "yourNetworkName";
const char* password = "yourNetworkPass";
AsyncWebServer server(80);
ESPDash dashboard(&server);
const int sensorPin = 36;
Ticker measurementTicker;
Card lightSensorCard(&dashboard, GENERIC_CARD, "Luminosity");
void getMeasurement(){
int analogVal = analogRead(sensorPin);
std::string luminosityLevel;
if(analogVal < 500){
luminosityLevel = "Dark";
}else if(analogVal >3500){
luminosityLevel = "Bright";
}else{
luminosityLevel = "Medium";
}
lightSensorCard.update(luminosityLevel.c_str());
dashboard.sendUpdates();
}
void setup(){
Serial.begin(115200);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.println("Connecting to WiFi..");
}
Serial.println(WiFi.localIP());
server.begin();
measurementTicker.attach_ms(5000, getMeasurement);
}
void loop(){}
Testing the code
To test the code, first compile it and upload it to your device, using the tool of your choice (Arduino IDE or platformio, for example). Once the procedure finishes, open a serial monitor tool. You should see the IP address of the ESP32 getting printed, after it connects to the WiFi network. Copy that address.
On a web browser of your choice (I’ve used Google Chrome), put the following in the address bar, changing #yourIP# by the IP you have just copied:
http://#yourIP#/
After following the URL, you should see a result similar to figure 1. As can be observed, we obtain the web dashboard with a generic card displaying the luminosity level. If you vary the luminosity level that gets to the sensor, you should see it going through the 3 different levels we have defined in our code. In the case of the image, I had the sensor covered, which is why it is showing the “Dark” string.