Learn how to build a web server with the ESP8266 NodeMCU to display sensor readings in gauges. As an example, we’ll display temperature and humidity from a BME280 sensor in two different gauges: linear and radial. You can easily modify the project to plot any other data. To build the gauges, we’ll use the canvas-gauges JavaScript library.
We have a similar tutorial for the ESP32 board: Web Server – Display Sensor Readings in Gauges
Project Overview
This project will build a web server with the ESP8266 that displays temperature and humidity readings from a BME280 sensor. We’ll create a linear gauge that looks like a thermometer to display the temperature, and a radial gauge to display the humidity.
Server-Sent Events
The readings are updated automatically on the web page using Server-Sent Events (SSE).
To learn more about SSE, you can read:
Files Saved on the Filesystem
To keep our project better organized and easier to understand, we’ll save the HTML, CSS, and JavaScript files to build the web page on the board’s filesystem (LittleFS).
Prerequisites
Make sure you check all the prerequisites in this section before continuing with the project.
1. Install ESP8266 Board in Arduino IDE
We’ll program the ESP8266 using Arduino IDE. So, you must have the ESP8266 add-on installed. Follow the next tutorial if you haven’t already:
If you want to use VS Code with the PlatformIO extension, follow the next tutorial instead to learn how to program the ESP8266:
2. Filesystem Uploader Plugin
To upload the HTML, CSS, and JavaScript files to the ESP8266 filesystem (LittleFS), we’ll use a plugin for Arduino IDE: LittleFS Filesystem Uploader. Follow the next tutorial to install the filesystem uploader plugin:
If you’re using VS Code with the PlatformIO extension, read the following tutorial to learn how to upload files to the filesystem:
3. Installing Libraries
To build this project, you need to install the following libraries:
- Adafruit_BME280 (Arduino Library Manager)
- Adafruit_Sensor library (Arduino Library Manager)
- Arduino_JSON library by Arduino version 0.1.0 (Arduino Library Manager)
- ESPAsyncWebServer (.zip folder);
- ESPAsyncTCP (.zip folder).
You can install the first three libraries using the Arduino Library Manager. Go to Sketch > Include Library > Manage Libraries and search for the libraries’ names.
The ESPAsyncWebServer and ESPAsynTCP libraries aren’t available to install through the Arduino Library Manager, so you need to copy the library files to the Arduino Installation Libraries folder. Alternatively, download the libraries’ .zip folders, and then, in your Arduino IDE, go to Sketch > Include Library > Add .zip Library and select the libraries you’ve just downloaded.
Installing Libraries (VS Code + PlatformIO)
If you’re programming the ESP8266 using PlatformIO, you should add the following lines to the platformio.ini file to include the libraries, change the Serial Monitor speed to 115200, and use LittleFS for the filesystem:
monitor_speed = 115200
lib_deps = ESP Async WebServer
arduino-libraries/Arduino_JSON @ 0.1.0
adafruit/Adafruit BME280 Library @ ^2.1.0
adafruit/Adafruit Unified Sensor @ ^1.1.4
board_build.filesystem = littlefs
Parts Required
To follow this tutorial you need the following parts:
You can use any other sensor, or display any other values that are useful for your project. If you don’t have the sensor, you can also experiment with random values to learn how the project works.
You can use the preceding links or go directly to MakerAdvisor.com/tools to find all the parts for your projects at the best price!
Schematic Diagram
We’ll send temperature and humidity readings from a BME280 sensor. We’re going to use I2C communication with the BME280 sensor module. For that, wire the sensor to the default ESP8266 SCL (GPIO 5) and SDA (GPIO 4) pins, as shown in the following schematic diagram.
Recommended reading: ESP8266 Pinout Reference: Which GPIO pins should you use?
Organizing Your Files
To keep the project organized and make it easier to understand, we’ll create four files to build the web server:
- Arduino sketch that handles the web server;
- index.html: to define the content of the web page;
- sytle.css: to style the web page;
- script.js: to program the behavior of the web page—handle web server responses, events, create the gauges, etc.
You should save the HTML, CSS, and JavaScript files inside a folder called data inside the Arduino sketch folder, as shown in the previous diagram. We’ll upload these files to the ESP8266 filesystem (LittleFS).
You can download all project files:
HTML File
Copy the following to the index.html file.
<!DOCTYPE html>
<html>
<head>
<title>ESP IOT DASHBOARD</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/png" href="favicon.png">
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.7.2/css/all.css" integrity="sha384-fnmOCqbTlWIlj8LyTjo7mOUStjsKC4pOpQbqyi7RrhN7udi9RwhKkMHpvLbHG9Sr" crossorigin="anonymous">
<link rel="stylesheet" type="text/css" href="style.css">
<script src="http://cdn.rawgit.com/Mikhus/canvas-gauges/gh-pages/download/2.1.7/all/gauge.min.js"></script>
</head>
<body>
<div class="topnav">
<h1>ESP WEB SERVER GAUGES</h1>
</div>
<div class="content">
<div class="card-grid">
<div class="card">
<p class="card-title">Temperature</p>
<canvas id="gauge-temperature"></canvas>
</div>
<div class="card">
<p class="card-title">Humidity</p>
<canvas id="gauge-humidity"></canvas>
</div>
</div>
</div>
<script src="script.js"></script>
</body>
</html>
The HTML file for this project is very simple. It includes the JavaScript canvas-gauges library in the head of the HTML file:
<script src="https://cdn.rawgit.com/Mikhus/canvas-gauges/gh-pages/download/2.1.7/all/gauge.min.js"></script>
There is a <canvas> tag with the id gauge-temperature where we’ll render the temperature gauge later on.
<canvas id="gauge-temperature"></canvas>
There is also another <canvas> tag with the id gauge-humidity, where we’ll render the humidity gauge later on.
<canvas id="gauge-humidity"></canvas>
CSS File
Copy the following styles to your style.css file. It styles the web page with simple colors and styles.
html {
font-family: Arial, Helvetica, sans-serif;
display: inline-block;
text-align: center;
}
h1 {
font-size: 1.8rem;
color: white;
}
p {
font-size: 1.4rem;
}
.topnav {
overflow: hidden;
background-color: #0A1128;
}
body {
margin: 0;
}
.content {
padding: 5%;
}
.card-grid {
max-width: 1200px;
margin: 0 auto;
display: grid;
grid-gap: 2rem;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
}
.card {
background-color: white;
box-shadow: 2px 2px 12px 1px rgba(140,140,140,.5);
}
.card-title {
font-size: 1.2rem;
font-weight: bold;
color: #034078
}
JavaScript File (creating the gauges)
Copy the following to the script.js file.
// Get current sensor readings when the page loads
window.addEventListener('load', getReadings);
// Create Temperature Gauge
var gaugeTemp = new LinearGauge({
renderTo: 'gauge-temperature',
width: 120,
height: 400,
units: "Temperature C",
minValue: 0,
startAngle: 90,
ticksAngle: 180,
maxValue: 40,
colorValueBoxRect: "#049faa",
colorValueBoxRectEnd: "#049faa",
colorValueBoxBackground: "#f1fbfc",
valueDec: 2,
valueInt: 2,
majorTicks: [
"0",
"5",
"10",
"15",
"20",
"25",
"30",
"35",
"40"
],
minorTicks: 4,
strokeTicks: true,
highlights: [
{
"from": 30,
"to": 40,
"color": "rgba(200, 50, 50, .75)"
}
],
colorPlate: "#fff",
colorBarProgress: "#CC2936",
colorBarProgressEnd: "#049faa",
borderShadowWidth: 0,
borders: false,
needleType: "arrow",
needleWidth: 2,
needleCircleSize: 7,
needleCircleOuter: true,
needleCircleInner: false,
animationDuration: 1500,
animationRule: "linear",
barWidth: 10,
}).draw();
// Create Humidity Gauge
var gaugeHum = new RadialGauge({
renderTo: 'gauge-humidity',
width: 300,
height: 300,
units: "Humidity (%)",
minValue: 0,
maxValue: 100,
colorValueBoxRect: "#049faa",
colorValueBoxRectEnd: "#049faa",
colorValueBoxBackground: "#f1fbfc",
valueInt: 2,
majorTicks: [
"0",
"20",
"40",
"60",
"80",
"100"
],
minorTicks: 4,
strokeTicks: true,
highlights: [
{
"from": 80,
"to": 100,
"color": "#03C0C1"
}
],
colorPlate: "#fff",
borderShadowWidth: 0,
borders: false,
needleType: "line",
colorNeedle: "#007F80",
colorNeedleEnd: "#007F80",
needleWidth: 2,
needleCircleSize: 3,
colorNeedleCircleOuter: "#007F80",
needleCircleOuter: true,
needleCircleInner: false,
animationDuration: 1500,
animationRule: "linear"
}).draw();
// Function to get current readings on the webpage when it loads for the first time
function getReadings(){
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
var myObj = JSON.parse(this.responseText);
console.log(myObj);
var temp = myObj.temperature;
var hum = myObj.humidity;
gaugeTemp.value = temp;
gaugeHum.value = hum;
}
};
xhr.open("GET", "/readings", true);
xhr.send();
}
if (!!window.EventSource) {
var source = new EventSource('/events');
source.addEventListener('open', function(e) {
console.log("Events Connected");
}, false);
source.addEventListener('error', function(e) {
if (e.target.readyState != EventSource.OPEN) {
console.log("Events Disconnected");
}
}, false);
source.addEventListener('message', function(e) {
console.log("message", e.data);
}, false);
source.addEventListener('new_readings', function(e) {
console.log("new_readings", e.data);
var myObj = JSON.parse(e.data);
console.log(myObj);
gaugeTemp.value = myObj.temperature;
gaugeHum.value = myObj.humidity;
}, false);
}
Here’s a summary of what this code does:
- initializing the event source protocol;
- adding an event listener for the new_readings event;
- creating the gauges;
- getting the latest sensor readings from the new_readings event and display them in the corresponding gauges;
- making an HTTP GET request for the current sensor readings when you access the web page for the first time.
Get Readings
When you access the web page for the first time, we’ll request the server to get the current sensor readings. Otherwise, we would have to wait for new sensor readings to arrive (via Server-Sent Events), which can take some time depending on the interval that you set on the server.
Add an event listener that calls the getReadings function when the web page loads.
// Get current sensor readings when the page loads
window.addEventListener('load', getReadings);
The window object represents an open window in a browser. The addEventListener() method sets up a function to be called when a certain event happens. In this case, we’ll call the getReadings function when the page loads (‘load’) to get the current sensor readings.
Now, let’s take a look at the getReadings function. Create a new XMLHttpRequest object. Then, send a GET request to the server on the /readings URL using the open() and send() methods.
function getReadings() {
var xhr = new XMLHttpRequest();
xhr.open("GET", "/readings", true);
xhr.send();
}
When we send that request, the ESP will send a response with the required information. So, we need to handle what happens when we receive the response. We’ll use the onreadystatechange property that defines a function to be executed when the readyState property changes. The readyState property holds the status of the XMLHttpRequest. The response of the request is ready when the readyState is 4, and the status is 200.
- readyState = 4 means that the request finished and the response is ready;
- status = 200 means “OK”
So, the request should look something like this:
function getStates(){
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
… DO WHATEVER YOU WANT WITH THE RESPONSE …
}
};
xhr.open("GET", "/states", true);
xhr.send();
}
The response sent by the ESP is the following text in JSON format (those are just arbitrary values).
{
"temperature" : "25.02",
"humidity" : "64.01",
}
We need to convert the JSON string into a JSON object using the parse() method. The result is saved on the myObj variable.
var myObj = JSON.parse(this.responseText);
The myObj variable is a JSON object that contains the temperature and humidity readings. We want to update the gauges values with the corresponding readings.
Updating the value of a gauge is straightforward. For example, our temperature gauge is called gaugeTemp (as we’ll see later on), to update a value, we can simply call: gaugeTemp.value = NEW_VALUE. In our case, the new value is the temperature reading saved on the myObj JSON object.
gaugeTemp.value = myObj.temperature;
It is similar for the humidity (our humidity gauge is called gaugeHum).
gaugeHum.value = myObj.humidity;
Here’s the complete getReadings() function.
function getReadings(){
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
var myObj = JSON.parse(this.responseText);
console.log(myObj);
var temp = myObj.temperature;
var hum = myObj.humidity;
gaugeTemp.value = temp;
gaugeHum.value = hum;
}
};
xhr.open("GET", "/readings", true);
xhr.send();
}
Creating the Gauges
The canvas-charts library allows you to build linear and radial gauges to display your readings. It provides several examples, and it is very simple to use. We recommend taking a look at the documentation and exploring all the gauges functionalities:
Temperature Gauge
The following lines create the gauge to display the temperature.
// Create Temperature Gauge
var gaugeTemp = new LinearGauge({
renderTo: 'gauge-temperature',
width: 120,
height: 400,
units: "Temperature C",
minValue: 0,
startAngle: 90,
ticksAngle: 180,
maxValue: 40,
colorValueBoxRect: "#049faa",
colorValueBoxRectEnd: "#049faa",
colorValueBoxBackground: "#f1fbfc",
valueDec: 2,
valueInt: 2,
majorTicks: [
"0",
"5",
"10",
"15",
"20",
"25",
"30",
"35",
"40"
],
minorTicks: 4,
strokeTicks: true,
highlights: [
{
"from": 30,
"to": 40,
"color": "rgba(200, 50, 50, .75)"
}
],
colorPlate: "#fff",
colorBarProgress: "#CC2936",
colorBarProgressEnd: "#049faa",
borderShadowWidth: 0,
borders: false,
needleType: "arrow",
needleWidth: 2,
needleCircleSize: 7,
needleCircleOuter: true,
needleCircleInner: false,
animationDuration: 1500,
animationRule: "linear",
barWidth: 10,
}).draw();
To create a new linear gauge, use the new LinearGauge() method and pass as an argument the properties of the gauge.
var gaugeTemp = new LinearGauge({
In the next line, define where you want to put the chart (it must be a <canvas> element). In our example, we want to place it in the <canvas> HTML element with the gauge-temperature id—see the HTML file section.
renderTo: 'gauge-temperature',
Then, we define other properties to customize our gauge. The names are self-explanatory, but we recommend taking a look at all possible configurations and changing the gauge to meet your needs.
In the end, you need to apply the draw() method to actually display the gauge on the canvas.
}).draw();
Special attention that if you need to change the gauge range, you need to change the minValue and maxValue properties:
minValue: 0,
maxValue: 40,
You also need to adjust the majorTicks values for the values displayed on the axis.
majorTicks: [
"0",
"5",
"10",
"15",
"20",
"25",
"30",
"35",
"40"
],
Humidity Gauge
Creating the humidity gauge is similar, but we use the new RadialGauge() function instead and it is rendered to the <canvas> with the gauge-humidity id. Notice that we apply the draw() method on the gauge so that it is drawn on the canvas.
// Create Humidity Gauge
var gaugeHum = new RadialGauge({
renderTo: 'gauge-humidity',
width: 300,
height: 300,
units: "Humidity (%)",
minValue: 0,
maxValue: 100,
colorValueBoxRect: "#049faa",
colorValueBoxRectEnd: "#049faa",
colorValueBoxBackground: "#f1fbfc",
valueInt: 2,
majorTicks: [
"0",
"20",
"40",
"60",
"80",
"100"
],
minorTicks: 4,
strokeTicks: true,
highlights: [
{
"from": 80,
"to": 100,
"color": "#03C0C1"
}
],
colorPlate: "#fff",
borderShadowWidth: 0,
borders: false,
needleType: "line",
colorNeedle: "#007F80",
colorNeedleEnd: "#007F80",
needleWidth: 2,
needleCircleSize: 3,
colorNeedleCircleOuter: "#007F80",
needleCircleOuter: true,
needleCircleInner: false,
animationDuration: 1500,
animationRule: "linear"
}).draw();
Handle events
Update the readings on the gauge when the client receives the readings on the new_readings event
Create a new EventSource object and specify the URL of the page sending the updates. In our case, it’s /events.
if (!!window.EventSource) {
var source = new EventSource('/events');
Once you’ve instantiated an event source, you can start listening for messages from the server with addEventListener().
These are the default event listeners, as shown here in the AsyncWebServer documentation.
source.addEventListener('open', function(e) {
console.log("Events Connected");
}, false);
source.addEventListener('error', function(e) {
if (e.target.readyState != EventSource.OPEN) {
console.log("Events Disconnected");
}
}, false);
source.addEventListener('message', function(e) {
console.log("message", e.data);
}, false);
Then, add the event listener for new_readings.
source.addEventListener('new_readings', function(e) {
When new readings are available, the ESP8266 sends an event (new_readings) to the client. The following lines handle what happens when the browser receives that event.
source.addEventListener('new_readings', function(e) {
console.log("new_readings", e.data);
var myObj = JSON.parse(e.data);
console.log(myObj);
gaugeTemp.value = myObj.temperature;
gaugeHum.value = myObj.humidity;
}, false);
Basically, print the new readings on the browser console, convert the data into a JSON object and display the readings on the corresponding gauges.
Arduino Sketch
Copy the following code to your Arduino IDE or to the main.cpp file if you’re using PlatformIO.
You can also download all the files here.
/*********
Rui Santos
Complete instructions at https://RandomNerdTutorials.com/esp8266-web-server-gauges/
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files.
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*********/
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <ESPAsyncTCP.h>
#include <ESPAsyncWebServer.h>
#include "LittleFS.h"
#include <Arduino_JSON.h>
#include <Adafruit_BME280.h>
#include <Adafruit_Sensor.h>
// Replace with your network credentials
const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";
// Create AsyncWebServer object on port 80
AsyncWebServer server(80);
// Create an Event Source on /events
AsyncEventSource events("/events");
// Json Variable to Hold Sensor Readings
JSONVar readings;
// Timer variables
unsigned long lastTime = 0;
unsigned long timerDelay = 30000;
// Create a sensor object
Adafruit_BME280 bme; // BME280 connect to ESP32 I2C (GPIO 21 = SDA, GPIO 22 = SCL)
// Init BME280
void initBME(){
if (!bme.begin(0x76)) {
Serial.println("Could not find a valid BME280 sensor, check wiring!");
while (1);
}
}
// Get Sensor Readings and return JSON object
String getSensorReadings(){
readings["temperature"] = String(bme.readTemperature());
readings["humidity"] = String(bme.readHumidity());
String jsonString = JSON.stringify(readings);
return jsonString;
}
// Initialize LittleFS
void initFS() {
if (!LittleFS.begin()) {
Serial.println("An error has occurred while mounting LittleFS");
}
Serial.println("LittleFS mounted successfully");
}
// Initialize WiFi
void initWiFi() {
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
Serial.print("Connecting to WiFi ..");
while (WiFi.status() != WL_CONNECTED) {
Serial.print('.');
delay(1000);
}
Serial.println(WiFi.localIP());
}
void setup() {
Serial.begin(115200);
initBME();
initWiFi();
initFS();
// Web Server Root URL
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
request->send(LittleFS, "/index.html", "text/html");
});
server.serveStatic("/", LittleFS, "/");
// Request for the latest sensor readings
server.on("/readings", HTTP_GET, [](AsyncWebServerRequest *request){
String json = getSensorReadings();
request->send(200, "application/json", json);
json = String();
});
events.onConnect([](AsyncEventSourceClient *client){
if(client->lastId()){
Serial.printf("Client reconnected! Last message ID that it got is: %u\n", client->lastId());
}
// send event with message "hello!", id current millis
// and set reconnect delay to 1 second
client->send("hello!", NULL, millis(), 10000);
});
server.addHandler(&events);
// Start server
server.begin();
}
void loop() {
if ((millis() - lastTime) > timerDelay) {
// Send Events to the client with the Sensor Readings Every 30 seconds
events.send("ping",NULL,millis());
events.send(getSensorReadings().c_str(),"new_readings" ,millis());
lastTime = millis();
}
}
How the code works
Let’s take a look at the code and see how it works to send readings to the client using server-sent events.
Including Libraries
The Adafruit_Sensor and Adafruit_BME280 libraries are needed to interface with the BME280 sensor.
#include <Adafruit_BME280.h>
#include <Adafruit_Sensor.h>
The ESP8266WiFi, ESPAsyncWebServer, and ESPAsyncTCP libraries are used to create the web server.
#include <ESP8266WiFi.h>
#include <ESPAsyncTCP.h>
#include <ESPAsyncWebServer.h>
We’ll use LittleFS to save the files to build the web server.
#include "LittleFS.h"
You also need to include the Arduino_JSON library to make it easier to handle JSON strings.
#include <Arduino_JSON.h>
Network Credentials
Insert your network credentials in the following variables, so that the ESP8266 can connect to your local network using Wi-Fi.
const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";
AsyncWebServer and AsyncEventSource
Create an AsyncWebServer object on port 80.
AsyncWebServer server(80);
The following line creates a new event source on /events.
AsyncEventSource events("/events");
Declaring Variables
The readings variable is a JSON variable to hold the sensor readings in JSON format.
JSONVar readings;
The lastTime and the timerDelay variables will be used to update sensor readings every X number of seconds. As an example, we’ll get new sensor readings every 30 seconds (30000 milliseconds). You can change that delay time in the timerDelay variable.
unsigned long lastTime = 0;
unsigned long timerDelay = 30000;
Create an Adafruit_BME280 object called bme on the default ESP I2C pins.
Adafruit_BME280 bme;
Initialize BME280 Sensor
The following function can be called to initialize the BME280 sensor.
// Init BME280
void initBME(){
if (!bme.begin(0x76)) {
Serial.println("Could not find a valid BME280 sensor, check wiring!");
while (1);
}
}
Get BME280 Readings
To get temperature and humidity from the BME280 temperature, use the following methods on the bme object:
- bme.readTemperature()
- bme.readHumidity()
The getSensorReadings() function gets the sensor readings and saves them on the readings JSON array.
// Get Sensor Readings and return JSON object
String getSensorReadings(){
readings["temperature"] = String(bme.readTemperature());
readings["humidity"] = String(bme.readHumidity());
String jsonString = JSON.stringify(readings);
return jsonString;
}
The readings array is then converted into a JSON string variable using the stringify() method and saved on the jsonString variable.
The function returns the jsonString variable with the current sensor readings. The JSON string has the following format (the values are just arbitrary numbers for explanation purposes).
{
"temperature" : "25",
"humidity" : "50"
}
setup()
In the setup(), initialize the Serial Monitor, Wi-Fi, filesystem, and the BME280 sensor.
void setup() {
// Serial port for debugging purposes
Serial.begin(115200);
initBME();
initWiFi();
initFS();
Handle Requests
When you access the ESP8266 IP address on the root / URL, send the text that is stored on the index.html file to build the web page.
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
request->send(LittleFS, "/index.html", "text/html");
});
Serve the other static files requested by the client (style.css and script.js).
server.serveStatic("/", LittleFS, "/");
Send the JSON string with the current sensor readings when you receive a request on the /readings URL.
// Request for the latest sensor readings
server.on("/readings", HTTP_GET, [](AsyncWebServerRequest *request){
String json = getSensorReadings();
request->send(200, "application/json", json);
json = String();
});
The json variable holds the return from the getSensorReadings() function. To send a JSON string as response, the send() method accepts as first argument the response code (200), the second is the content type (“application/json”) and finally the content (json variable).
Server Event Source
Set up the event source on the server.
events.onConnect([](AsyncEventSourceClient *client){
if(client->lastId()){
Serial.printf("Client reconnected! Last message ID that it got is: %u\n", client->lastId());
}
// send event with message "hello!", id current millis
// and set reconnect delay to 1 second
client->send("hello!", NULL, millis(), 10000);
});
server.addHandler(&events);
Finally, start the server.
server.begin();
loop()
In the loop(), send events to the browser with the newest sensor readings to update the web page every 30 seconds.
events.send("ping",NULL,millis());
events.send(getSensorReadings().c_str(),"new_readings" ,millis());
Use the send() method on the events object and pass as an argument the content you want to send and the name of the event. In this case, we want to send the JSON string returned by the getSensorReadings() function. The send() method accepts a variable of type char, so we need to use the c_str() method to convert the variable. The name of the events is new_readings.
Usually, we also send a ping message every X number of seconds. That line is not mandatory. It is used to check on the client side that the server is alive.
events.send("ping",NULL,millis());
Uploading Code and Files
After inserting your network credentials, save the code. Go to Sketch > Show Sketch Folder, and create a folder called data.
Inside that folder, you should save the HTML, CSS, and JavaScript files.
Then, upload the code to your ESP8266 board. Make sure you have the right board and COM port selected. Also, make sure you’ve added your network credentials.
After uploading the code, you need to upload the files. Go to Tools > ESP8266 LittleFS Data Upload and wait for the files to be uploaded.
When everything is successfully uploaded, open the Serial Monitor at a baud rate of 115200. Press the ESP8266 EN/RST button, and it should print the ESP8266 IP address.
Demonstration
Open your browser and type the ESP8266 IP address. You should get access to the web page that shows the gauges with the latest sensor readings.
You can also check your gauges using your smartphone (the web page is mobile responsive).
Wrapping Up
In this tutorial you’ve learned how to create a web server to display sensor readings in linear and radial gauges. As an example, we displayed temperature and humidity from a BME280 sensor. You can use those gauges to display any other values that may make sense for your project.
You might also like reading:
- ESP32/ESP8266 Plot Sensor Readings in Real Time Charts – Web Server
- ESP8266 Web Server using Server-Sent Events (Update Sensor Readings Automatically)
Learn more about the ESP8266 with our resources:
Thank you for reading.
I just was done converting the esp32 code tp esp8266 …and then your article popped up 🙂
Anyway great project and an interesting library. I need to check that library out a bit more as for now it seems to have problems with negative value. Yes, it is possible to add a ‘major tick’ of say “-10”, but then a value of +3 degrees, starts counting from ‘-10’ and is shown on the thermometer at the “-7” tick. I am pretty sure it can be done but I need to dive in it a bit more
found it. need to adapt minValue
Hi.
Yes, that’s right.
It’s explained in the tutorial how to change the range.
I’m glad you found it!
Regards,
Sara
Tnx. Probably went over that too fast
Not all BME280 sensors work for me with this sketch.
With the BME280I2C.h lib they are recognized on the 8266 or esp32 but
with Adafruit_BME280.h not.
All sensors I have have the same i2c address (0x76) but still
the 6 pin BME280 sensor is not recognized ( VIN-GND-SCL-SDA-CSB-SDO).
But they do work on an Arduino uno, for example.
In your sketch only the model with ( VIN-GND-SCL-SDA) connections works.
That is kinda odd. Does the adafruit libraru maybe accedentally expect an SPI device or is your BME SPI only perhaps?
On a different note, the adafruit BME280 library is in fact very inefficient. It reads the temperature, humidity and pressure with 3 seperate readings,but in order to read humidity and pressure one needs to start a conversion by reading the temperature. So if you read all 3, the library does 5 readings and discards 2. There are some advantages in that coz if you just want the humidity, one always has a fresh reading but imho it would be better to have a ‘start conversion’ command and then pick whatever fresh value you want from the results
Thanks
Thank you for another great tutorial. I live in North America where we do not use the metric system. Are these the lines I need to change for frahenheit readings? Are there any other lines that need changing?
Thanks
// Get Sensor Readings and return JSON object
String getSensorReadings(){
readings[“temperature”] = String(bme.readTemperature());
readings[“humidity”] = String(bme.readHumidity());
String jsonString = JSON.stringify(readings);
return jsonString;
}
Hi.
Yes, you need to convert to Fahrenheit as follows:
readings[“temperature”] = String(1.8*bme.readTemperature()+32);
Then, you need to change the script.js file to adjust the temperature range. It is explained in the tutorial how to change the range.
Regards,
Sara
// Get Sensor Readings and return JSON object
String getSensorReadings(){
readings[“temperature”] = String(bme.readTemperature());
readings[“humidity”] = String(bme.readHumidity());
String jsonString = JSON.stringify(readings);
return jsonString;
}
I would like to use AHT10 instead of the expensive BME, but I get lost in the “Get Sensor Readings and return JSON object” section. Could I count on help?
#include <Adafruit_AHTX0.h>
…
Adafruit_AHTX0 aht;
sensors_event_t humidity, temp;
…
void initAHT() {
if (!aht.begin()) {
Serial.println(“Could not find AHT20? Check wiring!”);
while (1);
}
Serial.println(“AHT20 found”);
bool status;
status = aht.begin();
if (!status) {
Serial.println(“Could not find AHT20? Check wiring!”);
while (1);
}
}
…
String getSensorReadings(){
readings[“temperature”] =String(temp.temperature);
readings[“humidity”] = String(humidity.relative_humidity);
…
void setup() {
Serial.begin(115200);
initAHT();
/// This is basically what you should add (or replace) in the code above. Hope that helps!
…
I am ot sure what library you use for the AHT10 but it will probably do a reading like:
hum=aht10.readHumidity(AHTXX_USE_READ_DATA).
The BME280 has a similar instruction:
hum=BME280.readHumidity, so it is just a matter of substitution
Thank you! For those in need:
String getSensorReadings(){
sensors_event_t humidity, temp;
aht.getEvent(&humidity, &temp);
readings[“temperature”] = String(temp.temperature);
readings[“humidity”] = String(humidity.relative_humidity);
String jsonString = JSON.stringify(readings);
return jsonString;
For me it was impossibble di see any data in Webserver, while I see in the serial monitor.
Now i followed the suggestion of Wijnand November 22, 2019 and downgraded ESP8266 to the 2.6.0 version.
Now I see data in Webserber, on the smatphone too, but I dont see guages.
Any suggestion.
Thanks
Renzo
Hi.
That’s weird.
Did you upload all the necessary files to the filesystem?
What web browser are you using?
Regards.
Sara
thanks for the amazing project
whether a switch can be connected to this project so that I can control it through the page, say turn on the led.
thank you,
Hi.
Yes.
You can add a switch. I recommend using websocket for that: https://randomnerdtutorials.com/esp8266-nodemcu-websocket-server-arduino/
Regards,
Sara
Hello, thanks for the tutorial. I hope I get a really fast response… I’m using 4 ultrasonic sensors, on arduino uno board and trying to send the mean value to node mcu which I would love to display in place of the humidity gauge above, but its been a week without luck. Need help please…. The data displays on my nodemcu serial monitor but the gauge doesn’t respond on the webpage.
heres what my code looks like;
String getSensorReadings(){
readings[“Distance”] = String(Serial.read());
String jsonString = JSON.stringify(readings);
return jsonString;
Hi.
Don’t forget that you need to change the function getReadings() on the Javascript file to get the distance, as well as the addEventListener.
Regards,
Sara
Thank you very much… That worked.
Now, the new problem is that… I’m getting either “-1” or flunctuating readings like “13 to 58 to 10 to 208” and so on reading on my gauge.
On my arduino sketch, using software serial I have;
SoftwareSerial espSerial(2, 3);
Void setup(){
espSerial.begin(115200);
}
Vood loop(){
distance = ultrasonic.read();
espSerial.println(distance);
}
While nodemcu has;
SoftwareSerial readerSerial(D6, D5)
String getSensorReadings(){
readings[“Distance”] = String(readerSerial.read());
String jsonString = JSON.stringify(readings);
return jsonString;
Hi !
i try to modifi this for BMP280 sensor.
This is modified code: https://pastebin.com/8xQWKTta
The code is uploading succesfully on my esp8266, but in browser i have this: https://i.imgur.com/hOQdFa3.png
In serial monitor after reboot the ESP8266 i have this: https://i.imgur.com/PBOACp0.png
How can i fix this ?
Hi.
You probably didn’t upload the files to the filesystem.
Follow all the instructions carefully and it will work: https://i1.wp.com/randomnerdtutorials.com/wp-content/uploads/2021/04/esp8266-tools-littlefs-data-upload-arduino-ide.png
Regards,
Sara
i don’t have this option in arduino ide
https://i.imgur.com/OLEJvPy.png
Trying this https://randomnerdtutorials.com/install-esp8266-nodemcu-littlefs-arduino/ but not have this in my Arduino Ide https://i1.wp.com/randomnerdtutorials.com/wp-content/uploads/2021/04/esp8266-tools-littlefs-data-upload-arduino-ide.png
Hi.
You need to install the previous version of Arduino IDE (1.8.19) via ZIP file and then install the Filesystem image uploader.
https://www.arduino.cc/en/software
https://randomnerdtutorials.com/install-esp8266-nodemcu-littlefs-arduino/
Regards,
Sara
I succeeded with Arduino IDE version 1.8.19!
You are the best !
With version Arduino ide 2.0.0-rc3 I failed.
have esp32 version?
https://randomnerdtutorials.com/esp32-web-server-gauges/
Hi! I have a ESP01 with 512KB. When I try to upload any file on data folder, clicking on “ESP8266 LittleFS Data Upload”, I have this report “LittleFS Not Defined for Generic ESP8266 Module, 80 MHz, Flash, Disabled (new aborts on oom), Disabled, All SSL ciphers (most compatible), 32KB cache + 32KB IRAM (balanced), Use pgm_read macros for IRAM/PROGMEM, dtr (aka nodemcu), 26 MHz, 40MHz, DOUT (compatible), 512KB (FS:none OTA:~246KB), 0, nonos-sdk 2.2.1+100 (190703), v2 Lower Memory, Disabled, None, Only Sketch, 115200
“
Hi.
In that error, you can see that the board doesn’t allocate any memory for the filesystem: (FS:none
Go to Tools > Flash size and select one of the 512KB options with some memory for FS.
I hope this helps.
Regards,
Sara
Hummm. Thank you very much! I have read something about that, but do not understood fully. In “Flash size”, we select how the space be divided between FileSystem and Over The Air. I lost 5 days with that…How that is the simple thing, I not finded the answer. Thank you very much Sara!
Hummm. Muito obrigado! Li algo sobre isso, mas não entendi completamente. Em “Flash Size”, selecionamos como o espaço será dividido entre FileSystem e Over The Air. Perdi 5 dias com isso… Como isso é básico, não encontrei a resposta. Muito obrigado Sara!
Forgot to write that it worked. I changed the option and it worked.
Esqueci de escrever que funcionou. Mudei a opção e funcionou.
Great!
I’m glad it helped.
Regards,
Sara
Hi there,
this is a very good example for enviroment-testings. We use a MQ-2 and a SHT-30 to get 3 values with temp, humidity and gas. All works fine, startup ist OK and LittleFS working well with all 3 files from your tutorial.
When we startup the system it will booting perfect and start reading the sensor-values. We opened the serial-monitor for checking and placed few serial.print() to check if sensor readings are good. Up to this point there comes a Error as an exeption error “Panic core_esp8266_main.cpp:137 __yield” with a near endless stack-listing. Every time we reboot the system it starts up correctly. At the moment we start a browser (PC, Tablet or SmartPhone) an type in the given IP of the board (or type F5 refreshing) the whole system will crash again.
The only way we found in endless tries is as follow:
– First start up the browser
– then startup the board – plugin power-supply
– startup will be OK – then F5 on Browser – crasing again
A realy sporadic situation. Only (often plugin/plugout) the power-supply will sometimes (maybe all 20-30 tries) produce a startup with reaction in the opened browser. This is a realy big and frustating situation.
Does anyone have an idea or solution for this problem? How could the Browser-Start produce such a exeption on a 8266-12F?
Have some screenprints to show Arduino-IDE an Serial-Monitor outputs but can’t added to this comment.
Summary:
System will work fine in Arduino-IDE and serial-monitor. Specific serial.prints() show that sht-30 and mq-2 works well an supply values that could sown in serial-output.
This time i start up a browser (fireox, edge, chrome) and log in to ID-Adress of the board the system will crash and startup again. But at this time there a no longer values from the sensors. Only at reconnect power-supply will produce a clear startup in serial-monitor.
With an opened browser and many tries to plugi/plugout power will produce in a random case a situation where the opened browser shows up the sensor-values. If then is a second client-connect e.g. with smartphone on the same ip the system creases again.
Thanks a lot for readings this and we hope there is some help on the line.
Thanks, best regrads and stay healthy,
Manfred
Hi.
It may be the case that the libraries you use to read from your sensors are messing up with the AsyncWebServer library because of delays in the library.
So, what’s probably messing up your project are the following lines:
server.on(“/readings”, HTTP_GET, [](AsyncWebServerRequest *request){
String json = getSensorReadings();
request->send(200, “application/json”, json);
json = String();
});
The easiest workaround is to remove that part.
Regards,
Sara
Hi Sara,
thanks for this fast reply. I tried to comment out this code-part (server.on….) but dosent effect in any way. I think this is not a time-problem with the sensor librarys. A few days ago i played arround with a project on your tut-site that using “one” SendHTML()-Method to build up a webpage to show up some sensor-values (but i cant find it again – please help to find it). In this method is some ajax-scripting refreshing the
– Elements for temperature and humidity. This example works realy well and straight.
Now i stay in trouble with the SSE-Version of reading the SHT-30 and the MQ-2. If i work “only” with the serial monitor all parts acting straight and correct. The output shows all sensor-values at any time. When i open a browser and type the ip-adress to start gauges-view the system crashes with exception, restart and no more values shown in serial-monitor and gauges webview.
What the hell breaks the server down to reboot in case of an exception? In some cases the system act correct but this is not reproduceable (after 50 sheets a4-paper with notices ;-). I hope there is a chance to solve this problem. Any suggestions to test the getSensorReading() or the setup() befor calling server.begin()? Maybe the fault is in loop().
The only question left is: What breaks down the server and throw an exception which breaks down the whole system and cut off any further sensor readings. I thought the readings should work in any way.
Thanks for further thoughts and help….
Best regards,
Manfred
Hi again.
I do think it is an issue with timers with the libraries you’re using. But I can’t be sure because I don’t have a way to test it.
This tutorial doesn’t use server-sent events, but it refreshes the web page every 10 seconds to update the readings: https://randomnerdtutorials.com/esp8266-dht11dht22-temperature-and-humidity-web-server-with-arduino-ide/
Regards,
Sara
Hello, I have an encoder on interrupt pins of one ESP8266.
It correctly give me back the angle values form 0 to 360. I store it in a variable int called pos.
I draw a gauge by myself using canvas, I have this code for drawing the hand:
function drawHand(ctx, pos, length, width) {
ctx.beginPath();
ctx.lineWidth = width;
ctx.lineCap = “round”;
ctx.moveTo(0,0);
ctx.rotate(pos);
ctx.lineTo(0, -100);
ctx.stroke();
ctx.rotate(-pos);
}
I can’t pass the pos value from esp8266 code to script.js.
I tryed with this tutorial but you use
gaugeTemp.value = temp;
how can I set drawHand.pos for my hand?
Thank you
Massimo
Hi there,
your workshops are realy great and some of them could be installed with no problems – thanks for that good stuff.
We have one great question to WiFi-System on the ESP8266 / ESP32 as follow:
To use WiFi the SOC uses the 2.4GHz Band to operate. The question is if this 2.4GHZ-Frequency could be changed? We think that this frequency is generated by PLL-Oscillator in the SOC and driven by operating-system values.
Could this be changed to a lower frequency band width for example with an OS-Update?
Thanks for reply to this question.
Best regards,
Manfred
Hi again,
as we ask for the timer-problem few days ago we now found the problem. In setup() there is a function-call getSensorReadings() to update the values for the first time. This call is embedded in the server.on() call within an request.send() method.
This method-call crashes the function call and the whole setup()-call. By the way we think this is not a timing-problem at all, only the function call (for testing we cleared the getsensorreadings-method to deliver a simle “Hello” so that it couldn’t have timing-delays) kill the setup.
Our solution is to initiate the ESP setup() with hardcoded startup-values that updated with the first (and following) loop() call at all. Mess but simple and clear.
Thanks,
Manfred
Hi Manfred,
I’ve got the same problem: The thing crashes as soon as the Website request an update.
It seem that you found the solution. Could you post the piece of code you changed / inserted ?
Would be a great help!
Hi.
Can you better describe your issue?
What happens exactly to the board? Do you get any errors on the Serial Monitor?
Regards,
Sara
Hi Sara, sorry for the delay in answering your post.
I did get the usual messages after an ESP restart and a very long stack list.
Restart gave 2 as Reset cause and 3,6 as boot mode.
Worked fine when the following lines were commented out:
// Request for the latest sensor readings
// server.on(“/readings”, HTTP_GET, [](AsyncWebServerRequest * request) {
// String json = getSensorReadings();
// request->send(200, “application/json”, json);
// json = String();
// });
One had to wait for the first values to be display for max. 30 seconds but that would be okay.
I did try to reproduce the error yesterday. But, funny thing, now it works fine with the above lines uncommented. Maybe one of the many recent updates fixed the problem?
There is still a minor thing. After some time (about an hour or so), the gauges are displayed with an offset (still within there little windows) to the top and the left and are only partially visible. Only the gauge with the offset is updated. After F5 / refresh display everything is fine again for a while. Tested with Firefox (Windows), might try others browsers (on Windows and Android) later. I’d like to add a screenshot, but now idea how 🙁
Hi.
To share an image, upload the image to google drive, Imgur, or dropbox, and then, share a link to the image source.
Regards,
Sara
Hi Sara, I tried again over the weekend and the crash-effect is back. I have not changed a thing but to add a few serial outputs to get infos about the SDK, reset reason etc.
As long as one doesn’t refresh the browser window (F5), everything is fine. But after a refresh there is this output :
User exception (panic/abort/assert)
————— CUT HERE FOR EXCEPTION DECODER —————
Panic core_esp8266_main.cpp:137 __yield
ctx: sys
sp: 3fffec70 end: 3fffffb0 offset: 0000
3fffec70: 00000000 3ffe85d4 3fff0d64 40205c98
3fffec80: 00000001 000000fe 00000000 00000000
3fffec90: 00000000 00000000 00000000 00000000
3fffeca0: 00000000 00000000 4bc6a7f0 00000000
3fffecb0: 000002ed 0000be0c 3ffef20c 40214516
3fffecc0: 0000be0c 00000000 00000100 40214583
3fffecd0: 3f000039 0000be0c 3ffef20c 40213d55
3fffece0: 000002ed 0000be0c 3ffef20c 4021151d
3fffecf0: 3fffed54 3ffef20c 3ffef20c 40211570
3fffed00: 3fffed54 3ffef0f0 3ffef13c 4020662a
… more stack listings…
————— CUT HERE FOR EXCEPTION DECODER —————
ets Jan 8 2013,rst cause:2, boot mode:(3,6)
load 0x4010f000, len 3460, room 16
tail 4
chksum 0xcc
load 0x3fff20b8, len 40, room 4
tail 4
chksum 0xc9
csum 0xc9
v00059660
~ld
Sdk version: 2.2.2-dev(38a443e)
Core Version: 3.0.2
Boot Version: 31
Boot Mode: 1
CPU Frequency: 80 MHz
Reset reason: Software/System restart
Hi again.
What sensor are you using?
Instead of getting the readings inside the on() method, call the getSensorReadings outside the function and save the results in a global variable. Then, use that variable inside the on() method instead of calling the function. I believe it’s that that is crashing your board.
Regards,
Sara
Hi Greece2001,
your description of the boot-message (Reset-code 2 – BootMode 3,6) is still OK an means in what case the ESP restarted. Best way is to use directly ESP.restart() before using any messaging-loops or -calls.
The visual mess with the gauges are also on our project. At this time we have no solution where this “visual error” comes from. This could be a problem from the gauges code itself. With all browsers (firefox, chrome, edge …) we have the same problem. We think this is a timing-problem in drawing-routines at the gauges-lib. Seems that the redraw-refresh-time is longer as the message-respone-time and therefore comes the destroyed drawing.
Hope someone find the problem soon. This time the gauges-drawing-fault is not for production systems.
Best regards,
Manfred
Hi!
I found this info at the autors github:
https://github.com/Mikhus/canvas-gauges/issues/251#issuecomment-1059965179
Hello
Is there a way to replace the BNE280 with DS18B20 to measure the temperature of a swimming pool and stay only with the web page that has the thermometer?
I know you have a project with the DS18B20, but this website is nicer and I wanted to keep the image of the thermometer
Hi.
Yes, you can use only the thermometer part.
Make sure you replace all BME280 parts with the DS18B20.
In the JSON string, send only the temperature reading.
Regards,
Sara
Is it possible to have the gauge show the value of the ADC pin as a value on 0-100% on the ESP8266?
Hello
All codes on this site and other sites are designed for on and off switches.
I want a code that can control the output instantly with a button.
This means that when my finger is on the button, the output is active and when I remove my finger from the button, the output is disabled.
Please guide me.
Hi.
There are many different ways to achieve that.
We have this example: https://randomnerdtutorials.com/esp32-esp8266-web-server-outputs-momentary-switch/
Regards,
Sara
I’m not having a good time here.
having spent ages soldering pins on to the BMEs and winning the fight to get littleFS to show as a plugin, I copied the web files and uploaded the code.
My ESP8266NodeMCU’s web server is invisible. It neither shows up as a network, nor can I force my device to connect by inputting the SSID and password in the networks dialogue of my computer, iPhone or iPad.
Also the Serial Monitor reports that neither of my BME 280s are attached when they are.
So has anybody got any suggestions on how to force a client to see the ESP- like minimum range or something basic I may have missed?
Also is there any troubleshooting to test whether the BME chips work at all?
Ta
Fritigern
Hi.
I recommend taking a look at this article about BME sensors: https://randomnerdtutorials.com/solved-could-not-find-a-valid-bme280-sensor/
Regards,
Sara
Ok thanks, I’m on that.
Still can’t get any of my ESP8266 boards to be seen by any of my wifi enabled devices as a server.
To save me repeating the same steps for two networks, when I write an SSID and password into the IDE, do I put my current router’s ssid so the esp8266 joins that network as a client and is allocated an IP address (that I can find by interrogating my router), or do I make up an SSID and password for the ESP8266 so it becomes a network server and allocates IP addresses to clients that log on to it? (this IP may be found by interrogating the client like my iPhone after I have set it to join the ESP8266’s hosted network).
at the moment I am doing both and the esp neither shows as a network beacon, nor does it connect to my router if I attach it to either my network or a guest network.
Having spent days troubleshooting I have an esp8266 that I know can connect to my network and allows me to turn on/off its led remotely.
I used https://randomnerdtutorials.com/solved-could-not-find-a-valid-bme280-sensor/ to test the same esp8266 and BME, and they passed.
When I copy the data files and upload the gauges code my esp can’t connect to the network and the serial monitor prints-
⸮Could not find a valid BME280 sensor, check wiring!
16:41:47.459 ->
16:41:47.459 -> ————— CUT HERE FOR EXCEPTION DECODER —————
16:41:47.459 ->
16:41:47.459 -> Soft WDT reset
16:41:47.459 ->
16:41:47.459 -> Exception (4):
16:41:47.459 -> epc1=0x4020641e epc2=0x00000000 epc3=0x00000000 excvaddr=0x00000000 depc=0x00000000
16:41:47.494 ->
16:41:47.494 -> >>>stack>>>
16:41:47.494 ->
16:41:47.494 -> ctx: cont
16:41:47.494 -> sp: 3ffffe30 end: 3fffffd0 offset: 0160
16:41:47.494 -> 3fffff90: 3fffdad0 00000000 3ffef38c 40206528
16:41:47.494 -> 3fffffa0: feefeffe feefeffe feefeffe feefeffe
16:41:47.494 -> 3fffffb0: feefeffe 00000000 3ffef38c 40212684
16:41:47.494 -> 3fffffc0: feefeffe feefeffe 3fffdab0 401010c5
16:41:47.494 -> <<<stack<<<
16:41:47.494 ->
16:41:47.494 -> ————— CUT HERE FOR EXCEPTION DECODER —————
16:41:47.527 ->
16:41:47.527 -> ets Jan 8 2013,rst cause:2, boot mode:(3,7)
16:41:47.527 ->
16:41:47.527 -> load 0x4010f000, len 3424, room 16
16:41:47.527 -> tail 0
16:41:47.527 -> chksum 0x2e
16:41:47.527 -> load 0x3fff20b8, len 40, room 8
16:41:47.527 -> tail 0
16:41:47.527 -> chksum 0x2b
16:41:47.527 -> csum 0x2b
16:41:47.527 -> v00057960
16:41:47.527 -> ~ld
My computer clock is correct so the mad date and other text has come from something in the library.
Any advice from anybody who has got this to work?
Hi.
Try to call the
getSensorReadings() function outside the send() function.
For example:
String jsonReadings = getSensorReadings();
events.send(jsonReadings.c_str(),"new_readings" ,millis());
Let me know if this solves the issue.
Regards,
Sara
does this use my sql database?
Hi.
This example doesn’t use database.
Regards,
Sara
Hello and respect for your very useful content
How can I directly save gauge.min.js in spiffs and load it from spiffs ?
is it possible?
PLEASE HELP ME!!!