Skip to content

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