14. Interface and application programming¶
We have done the group assignement here. I participate to the communication with ESP32 part.
Hero shot¶
My main objective during this week was to retrieve data (temperature and light) and display them graphically in a web interface.
1. Data retrieval¶
To retrieve the data, I used my board created in week 13.
1.1 Temperature¶
To retrieve the temperature, I first had to look in the datasheet. I then used a code from ChatGPT 3.5 and asked :
Give me a code to retrieve temperature values with a NHQ103B375T5 in the ardwino IDE.
After a few modifications, in particular concerning the maximum and minimum temperatures (provided in the datasheet), I was able to obtain an acceptable result.
const int TempPin = A1; //Connect the Pin
void setup() {
Serial.begin(9600);
}
void loop() {
// Read the analogic value
int valeurTemp = analogRead(TempPin);
Serial.println(valeurTemp);
//Convertion
//float intensiteTemp = map(valeurTemp, -40, 3750, 0, 125); //Use this method for an int
float intensiteTemp = (valeurTemp - (-40.0)) * (125.0 - 0.0) / (3750.0 - (-40.0)) + 0.0; //Use this method for a float
//Print the value
Serial.print("T : ");
Serial.print(100-intensiteTemp, 2);
Serial.println(" C");
delay(1000);
}
It’s worth noting that we can use this method because temperature is a linear function, so the step between each degree is always the same, which is not necessarily the case for other physical values.
1.2 Light intensity¶
To retrieve this value, I used the same method as for temperature. But unfortunately, as you can see from the datasheet light intensity is logarithmic. You can also see that although the sensor is indicated for 950nm, it can actually read from 300nm to 1100nm. The code then looks like this:
const int lumierePin = A0; // Connect the Pin
void setup() {
Serial.begin(9600);
}
void loop() {
// Read the analogic value
int valeurLumiere = analogRead(lumierePin);
Serial.println(valeurLumiere);
// Convertion
float intensiteLumineuse = map(valeurLumiere, 300, 4095, 0, 100);
// APrint the value
Serial.print("Intensite lumineuse a 660 nm : ");
Serial.print(100-intensiteLumineuse);
Serial.println(" %");*/
delay(1000);
}
In this code, I had to modify the calculation values. I obtained the values empirically this time, by analyzing the values that came out raw. My light intensity value is therefore true only relatively, not absolutely.
2. Putting the data online¶
To begin with, I looked at how to send the values from the ESP32 to the web page. I went back to the old code from my week 13. With the help of ChatGPT 3.5, the result is as follows:
#include <WiFi.h>
#include <WebServer.h>
const char *ssid = "Android***";
const char *password = "*****";
WebServer server(80);
const int led = 4;
bool etatLed = 0;
int speed = 1000;
const int PinLight = A0;
const int PinTemp = A1;
void handleRoot()
{
int valeurLumiere = analogRead(PinLight);
int valeurTemperature = analogRead(PinTemp);
float LightIntensity = 100-(map(valeurLumiere, 300, 4095, 0, 100));
float TempIntensity = (valeurTemperature - (-40.0)) * (125.0 - 0.0) / (3750.0 - (-40.0)) + 0.0;
String page = "<!DOCTYPE html>";
page += "<html lang='fr'>";
page += "<head>";
page += " <title>Serveur ESP32</title>";
page += " <meta http-equiv='refresh' content='2' name='viewport' content='width=device-width, initial-scale=1' charset='UTF-8' />";
page += " <link rel='stylesheet' href='https://www.w3schools.com/w3css/4/w3.css'>";
page += "</head>";
page += "<body>";
page += " <div class='w3-card w3-green w3-padding-small w3-jumbo w3-center'>";
page += " <p>LED speed : "; page += speed; + "</p>";
page += " </div>";
page += " <div class='w3-bar'>";
page += " <a href='/on' class='w3-bar-item w3-button w3-border w3-jumbo' style='width:50%; height:50%;'>Slower</a>";
page += " <a href='/off' class='w3-bar-item w3-button w3-border w3-jumbo' style='width:50%; height:50%;'>Faster</a>";
page += " </div>";
page += " <div class='w3-center w3-padding-16'>";
page += " <p>Light intensity : "; page += LightIntensity; page += "%"; + "</p>"; // Ajoutez la valeur de la lumiere
page += " <p>Temperature : "; page += 100-TempIntensity; page +="C"; + "</p>"; // Ajoutez la valeur de la temperature
page += " </div>";
page += "</body>";
page += "</html>";
server.setContentLength(page.length());
server.send(200, "text/html", page);
}
void handleOn()
{
speed += 100;
server.sendHeader("Location","/");
server.send(303);
}
void handleOff()
{
if (speed > 100)
speed -= 100;
server.sendHeader("Location","/");
server.send(303);
}
void handleNotFound()
{
server.send(404, "text/plain", "404: Not found");
}
void setup()
{
Serial.begin(115200);
delay(1000);
Serial.println("\n");
pinMode(led, OUTPUT);
digitalWrite(led, 1);
WiFi.persistent(false);
WiFi.begin(ssid, password);
Serial.print("Tentative de connexion...");
while (WiFi.status() != WL_CONNECTED)
{
Serial.print(".");
delay(100);
}
Serial.println("\n");
Serial.println("Connexion etablie!");
Serial.print("Adresse IP: ");
Serial.println(WiFi.localIP());
server.on("/", handleRoot);
server.on("/on", handleOn);
server.on("/off", handleOff);
server.onNotFound(handleNotFound);
server.begin();
Serial.println("Serveur web actif!");
}
void loop()
{
server.handleClient();
digitalWrite(led, 1);
delay(speed/2);
server.handleClient();
delay(speed/2);
server.handleClient();
digitalWrite(led, 0);
delay(speed/2);
server.handleClient();
delay(speed/2);
}
Here the values are sent, and I took the opportunity to modify the LED display, in this case controlling the blinking speed. Now that I’ve succeeded in transmitting data, I’ve created a graphical interface, again with the help of ChatGPT 3.5. I went step by step through the queries: first how to create a graph, then how to put a series of data in it and finally the graphical details like setting them to the right size and adding titles. I also played with the refresh time (in this case 2 seconds) to get a better view.
#include <WiFi.h>
#include <WebServer.h>
#include <deque>
const char *ssid = "Android****";
const char *password = "*******";
WebServer server(80);
const int PinLight = A0;
const int PinTemp = A1;
const int led = 4;
bool etatLed = 0;
int speed = 1000;
// Definir la taille maximale des donnees a stocker (5 dernieres minutes a un taux d'echantillonnage de 1 mesure par seconde)
const int maxDataPoints = 300;
// Utiliser une double file d'attente pour stocker les valeurs de lumiere et de temperature
std::deque<float> lightData;
std::deque<float> tempData;
void handleRoot()
{
// Lire les valeurs de la lumiere et de la temperature
int valeurLumiere = analogRead(PinLight);
int valeurTemperature = analogRead(PinTemp);
// Convertir les valeurs en intensite lumineuse et temperature
float LightIntensity = 100 - (map(valeurLumiere, 300, 4095, 0, 100));
float TempIntensity = 100 - (valeurTemperature - (-40.0)) * (125.0 - 0.0) / (3750.0 - (-40.0)) + 0.0;
// Ajouter les nouvelles donnees a la file d'attente
lightData.push_back(LightIntensity);
tempData.push_back(TempIntensity);
// Supprimer les donnees les plus anciennes si la file d'attente depasse la taille maximale
if (lightData.size() > maxDataPoints)
lightData.pop_front();
if (tempData.size() > maxDataPoints)
tempData.pop_front();
// Generer les labels pour les graphiques
String lightLabels = "[";
String tempLabels = "[";
for (int i = 0; i < lightData.size(); i++)
{
lightLabels += "'" + String(i) + "',";
tempLabels += "'" + String(i) + "',";
}
lightLabels.remove(lightLabels.length() - 1); // Supprimer la virgule finale
tempLabels.remove(tempLabels.length() - 1); // Supprimer la virgule finale
lightLabels += "]";
tempLabels += "]";
// Generer les donnees pour les graphiques
String lightValues = "[";
String tempValues = "[";
for (int i = 0; i < lightData.size(); i++)
{
lightValues += String(lightData[i]) + ",";
tempValues += String(tempData[i]) + ",";
}
lightValues.remove(lightValues.length() - 1); // Supprimer la virgule finale
tempValues.remove(tempValues.length() - 1); // Supprimer la virgule finale
lightValues += "]";
tempValues += "]";
String page = "<!DOCTYPE html>";
page += "<html lang='fr'>";
page += "<head>";
page += " <title>Serveur ESP32</title>";
page += " <meta charset='UTF-8' />";
page += " <meta http-equiv='refresh' content='2' name='viewport' content='width=device-width, initial-scale=1' />";
page += " <link rel='stylesheet' href='https://www.w3schools.com/w3css/4/w3.css'>";
page += " <style>";
page += " .container {";
page += " width: 50%;";
page += " margin: 0 auto;";
page += " text-align: center;";
page += " }";
page += " </style>";
page += "</head>";
page += "<body>";
/*page += " <div class='w3-card w3-green w3-padding-small w3-jumbo w3-center'>";
page += " <p>LED speed : "; page += speed; + "</p>";
page += " </div>";
page += " <div class='w3-bar'>";
page += " <a href='/on' class='w3-bar-item w3-button w3-border w3-jumbo' style='width:50%; height:50%;'>Slower</a>";
page += " <a href='/off' class='w3-bar-item w3-button w3-border w3-jumbo' style='width:50%; height:50%;'>Faster</a>";
page += " </div>";*/
page += " <div class='container'>";
page += " <canvas id='lightChart' width='100' height='50'></canvas>"; // Definir la taille du graphique lumiere
page += " <canvas id='tempChart' width='100' height='50'></canvas>"; // Definir la taille du graphique temperature
page += " <script src='https://cdn.jsdelivr.net/npm/chart.js'></script>"; // Inclure Chart.js
page += " <script>"; // Debut du script JavaScript
page += " var lightCtx = document.getElementById('lightChart').getContext('2d');"; // Recuperer le contexte du graphique lumiere
page += " var lightChart = new Chart(lightCtx, {"; // Creer le graphique lumiere
page += " type: 'line',"; // Type de graphique : ligne
page += " data: {"; // Donnees du graphique
page += " labels: " + lightLabels + ","; // Labels (indices)
page += " datasets: [{"; // Ensemble de donnees
page += " label: 'Light Intensity',"; // Label du dataset
page += " data: " + lightValues + ","; // Donnees du dataset
page += " borderColor: 'blue',"; // Couleur de la ligne
page += " fill: false"; // Ne pas remplir sous la ligne
page += " }]";
page += " }";
page += " });";
page += " var tempCtx = document.getElementById('tempChart').getContext('2d');"; // Recuperer le contexte du graphique temperature
page += " var tempChart = new Chart(tempCtx, {"; // Creer le graphique temperature
page += " type: 'line',"; // Type de graphique : ligne
page += " data: {"; // Donnees du graphique
page += " labels: " + tempLabels + ","; // Labels (indices)
page += " datasets: [{"; // Ensemble de donnees
page += " label: 'Temperature',"; // Label du dataset
page += " data: " + tempValues + ","; // Donnees du dataset
page += " borderColor: 'red',"; // Couleur de la ligne
page += " fill: false"; // Ne pas remplir sous la ligne
page += " }]";
page += " }";
page += " });";
page += " </script>"; // Fin du script JavaScript
page += " </div>";
page += "</body>";
page += "</html>";
server.setContentLength(page.length());
server.send(200, "text/html", page);
}
void handleOn()
{
speed += 100;
server.sendHeader("Location","/");
server.send(303);
}
void handleOff()
{
if (speed > 100)
speed -= 100;
server.sendHeader("Location","/");
server.send(303);
}
void handleNotFound()
{
server.send(404, "text/plain", "404: Not found");
}
void setup()
{
Serial.begin(115200);
delay(1000);
Serial.println("\n");
pinMode(led, OUTPUT);
digitalWrite(led, 1);
WiFi.persistent(false);
WiFi.begin(ssid, password);
Serial.print("Tentative de connexion...");
while (WiFi.status() != WL_CONNECTED)
{
Serial.print(".");
delay(100);
}
Serial.println("\n");
Serial.println("Connexion etablie!");
Serial.print("Adresse IP: ");
Serial.println(WiFi.localIP());
server.on("/", handleRoot);
server.on("/on", handleOn);
server.on("/off", handleOff);
server.onNotFound(handleNotFound);
server.begin();
Serial.println("Serveur web actif!");
}
void loop()
{
server.handleClient();
}
This code displays the two values as follows:
The files¶
Here you can find the temperature file
Here you can find the light file
Here you can find the send data file
Here you can find the graph file