In this project, weâll create a status indicator PCB shield for the ESP32 featuring two rows of addressable RGB neopixel LEDs, a BME280 sensor, and a pushbutton. We’ll program the board to display a web server with the BME280 sensor readings and show the temperature and humidity range on the LEDs (like two progress bars). We’ll also set up a Wi-Fi Managerâ âthe LEDs indicate whether it is already connected to a Wi-Fi network or if it is set in access point mode.
The Wi-Fi Manager allows you to connect the ESP32 boards to different Access Points (networks) without having to hard-code network credentials (SSID and password) and upload new code to the board. Your ESP will automatically join the last saved network or set up an Access Point that you can use to configure the network credentials.
By following this project, you’ll learn more about the following concepts:
- Controlling two addressable RGB LED “strips” individually with the ESP32;
- Build a Web Server with the ESP32 using Server-sent Events (SSE);
- Handle HTML input fields to save data on your board (SSID and password);
- Save variables permanently using files on the filesystem;
- Build your own Wi-Fi Manager using the ESPAsyncWebServer library;
- Switch between station mode and access point mode;
- And much more…
To better understand how this project works, we recommend taking a look at the following tutorials:
- Input Data on HTML Form ESP32/ESP8266 Web Server using Arduino IDE
- How to Set an ESP32 Access Point (AP) for Web Server
- ESP32 Static/Fixed IP Address
- ESP32 Web Server using Server-Sent Events (Update Sensor Readings Automatically)
Watch the Video Tutorial
Resources
You can find all the resources needed to build this project in the links below (or you can visit the GitHub project page):
- ESP32 Code (Arduino IDE)
- Gerber files
- EasyEDA project to edit the PCB
- Click here to download all the files
Project Overview
Before going straight to the project, let’s take a look at the PCB Shield features (hardware and software).
PCB Shield Features
The shield is designed with some headers pins to stack the ESP32 board. For this reason, if you want to build and use our PCB, you need to get the same ESP32 development board. Weâre using the ESP32 DEVKIT DOIT V1 board (the model with 36 GPIOs).
If you want to follow this project and have a different ESP32 model, you can assemble the circuit on a breadboard, or you can modify the PCB layout and wiring to match the pinout of your ESP32 board. Throughout this project, we provide all the necessary files if you need to modify the PCB.
Additionally, you can follow this project by assembling the circuit on a breadboard if you don’t want to build a PCB shield.
The shield consists of:
- BME280 temperature, humidity, and pressure sensor;
- Pushbutton;
- Two rows of 5 addressable RGB LEDs (WS2812B).
If you replicate this project on a breadboard, instead of individual WS2812B addressable RGB LEDs, you can use addressable RGB LED strips.
PCB Shield Pin Assignment
The following table shows the pin assignment for each component on the shield:
Component | ESP32 Pin Assignment |
BME280 | GPIO 21 (SDA), GPIO 22 (SCL) |
Pushbutton | GPIO 18 |
Addressable RGB LEDs (row 1) | GPIO 27 |
Addressable RGB LEDs (row 2) | GPIO 32 |
PCB Software Features
You can program the shield in several different ways. We’ll program the ESP32 to have the following features:
Web Server
Web server to display BME280 sensor readings: temperature, humidity, and pressure. It also displays the time of the last update. The readings update automatically every 30 seconds using server-sent events. Learn more about Server-Sent Events in this project.
Visual Interface (Addressable RGB LEDs)
The RGB LEDs on the shield behave like a progress bar showing the range of temperature and humidity values. The higher the temperature, the more LEDs will be litâthe same for the humidity readings. The temperature values are displayed in an orange/yellow color, and the humidity is displayed in a teal color.
Wi-Fi Manager
The Wi-Fi Manager allows you to connect the ESP32 board to different Access Points (networks) without having to hard-code network credentials (SSID and password) and upload new code to your board. Your ESP will automatically join the last saved network or set up an Access Point that you can use to configure the network credentials.
When the board is in Access Point mode (Wi-Fi Manager), all LEDs are lit in red. When the board is in station mode (Web Server with sensor readings), all LEDs are temporarily lit in green/teal color before showing the temperature and humidity range.
Testing the Circuit on a Breadboard
Before designing and building the PCB, itâs important to test the circuit on a breadboard. If you donât want to make a PCB, you can still follow this project by assembling the circuit on a breadboard.
Parts Required
To assemble the circuit on a breadboard you need the following parts (the parts for the PCB are shown in a later section):
- DOIT ESP32 DEVKIT V1 Board â read Best ESP32 Development Boards
- BME280Â (4 pins)
- 2x WS2812B Addressable RGB LED Strips
- Pushbutton*
- 10k Ohm resistor
- Breadboard
- Jumper wires
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!
After gathering all the parts, assemble the circuit by following the next schematic diagram:
*We ended up not using the pushbutton for this particular project, so, it is not necessary to include it in your circuit.
Designing the PCB
To design the circuit and PCB, we used EasyEDA, a browser-based software, to design PCBs. If you want to customize your PCB, you need to upload the following files:
I’m not an expert in PCB design. However, designing simple PCBs like the one we’re using in this tutorial is straightforward. Designing the circuit works like in any other circuit software tool, you place some components, and you wire them together. Then, you assign each component to a footprint.
Having the parts assigned, place each component. When youâre happy with the layout, make all the connections and route your PCB.
Save your project and export the Gerber files.
Note: you can grab the project files and edit them to customize the shield for your own needs.
Ordering the PCBs at PCBWay
This project is sponsored by PCBWay. PCBWay is a full feature Printed Circuit Board manufacturing service.
Turn your DIY breadboard circuits into professional PCBs – get 10 boards for approximately $5 + shipping (which will vary depending on your country).
Once you have your Gerber files, you can order the PCB. Follow the next steps.
1. Download the Gerber files â click here to download the .zip file
2. Go to PCBWay website and open the PCB Instant Quote page.
3. PCBWay can grab all the PCB details and automatically fills them for you. Use the âQuick-order PCB (Autofill parameters)â.
4. Press the â+ Add Gerber fileâ button to upload the provided Gerber files.
And thatâs it. You can also use the OnlineGerberViewer to check if your PCB is looking as it should.
If you arenât in a hurry, you can use the China Post shipping method to lower your cost significantly. In our opinion, we think they overestimate the China Post shipping time.
You can increase your PCB order quantity and change the solder mask color. As usual, weâve ordered the Blue color.
Once youâre ready, you can order the PCBs by clicking “Save to Cart” and complete your order.
Unboxing the PCBs
After approximately one week using the DHL shipping method, I received the PCBs at my office.
As usual, everything comes well packed, and the PCBs are really high-quality. The letters on the silkscreen are really well-printed and easy to read.
Weâre really satisfied with the PCBWay service. Here are some other projects weâve built using the PCBWay service:
- ESP32-CAM with Telegram: Take Photos, Control Outputs, Request Sensor Readings and Motion Notifications
- ESP32 IoT Shield PCB with Dashboard for Outputs and Sensors
Soldering the Components
In our PCB, weâve used SMD LEDs, SMD resistors, and SMD capacitors. These can be a bit difficult to solder, but the PCB looks much better. If youâve never soldered SMD before, we recommend watching a few videos to learn how it’s done. You can also get an SMD DIY soldering kit to practice a bit.
Hereâs a list of all the components needed to assemble the PCB:
- DOIT ESP32 DEVKIT V1 Board (36 GPIOs)
- 10x SMD WS2812B addressable RGB LEDs
- 1x 10k Ohm SMD resistor (1206)
- 10x 10nF capacitors (0805)
- Pushbutton (0.55 mm)
- Female pin header socket (2.54 mm)
- BME280 (4 pins)
Here are the soldering tools weâve used:
Start by soldering the SMD components. Then, solder the header pins. And finally, solder the other components or use header pins if you donât want to connect the components permanently.
Hereâs how the ESP32 Shield looks like after assembling all the parts.
The ESP32 board should stack perfectly on the header pins on the other side of the PCB.
Programming the Shield
As mentioned previously, we’ll program the board to have the following features:
- Web Server to display BME280 sensor readings (station mode);
- Visual Interface (Addressable RGB LEDs): the RGB LEDs on the shield behave like two progress bars showing the range of temperature and humidity values;
- Wi-Fi Manager: the ESP32 will automatically join the last saved network or set up an Access Point that you can use to configure the network credentials.
The following diagram summarizes how the project works.
- When the ESP first starts, it tries to read the ssid.txt, pass.txt, and ip.txt files (1);
- If the files are empty (2) (the first time you run the board, the files are empty), your board is set as an access point, and all LEDs are light up in red color (3);
- Using any Wi-Fi enabled device with a browser, you can connect to the newly created Access Point (default name ESP-WIFI-MANAGER);
- After establishing a connection with the ESP-WIFI-MANAGER, you can go to the default IP address 192.168.4.1 to open a web page that allows you to configure your SSID and password (4);
- The SSID, password, and IP address submitted on the form are saved on the corresponding files: ssid.txt, pass.txt, and ip.txt (5);
- After that, the ESP board restarts (6);
- This time, after restarting, the files are not empty, so the ESP will try to connect to the Wi-Fi network in station mode using the settings youâve inserted on the form (7);
- If it establishes a connection, the process is completed successfully (8) (all the LEDs are temporarily lit in green/teal color);
- You can access the main web page that shows sensor readings (9), and the LEDs are light up accordingly to the temperature and humidity range (10). Otherwise, it will set the Access Point (3), and you can access the default IP address (192.168.4.1) to add another SSID/password combination.
Prerequisites
Weâll program the ESP32 board using Arduino IDE. So make sure you have the ESP32 board add-on installed.
If you want to program the ESP32/ESP8266 using VS Code + PlatformIO, follow the next tutorial:
Installing Libraries (Arduino IDE)
For this project, you need to install all these libraries in your Arduino IDE.
- Adafruit Neopixel (Arduino Library Manager)
- Adafruit_BME280 library (Arduino Library Manager)
- Adafruit_Sensor library (Arduino Library Manager)
- Arduino_JSON library by Arduino version 0.1.0 (Arduino Library Manager)
- ESPAsyncWebServer (.zip folder)
- AsyncTCP (.zip folder)
You can install the first four libraries using the Arduino Library Manager. Go to Sketch > Include Library > Manage Libraries and search for the library name.
The ESPAsyncWebServer and AsynTCP 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, in your Arduino IDE, you can 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 ESP32 using PlatformIO, you should include the libraries on the platformio.ini file like this:
[env:esp32doit-devkit-v1]
platform = espressif32
board = esp32doit-devkit-v1
framework = arduino
monitor_speed = 115200
lib_deps = adafruit/Adafruit NeoPixel @ ^1.7.0
adafruit/Adafruit Unified Sensor @ ^1.1.4
adafruit/Adafruit BME280 Library @ ^2.1.2
arduino-libraries/Arduino_JSON @ 0.1.0
Filesystem Uploader
Before proceeding, you need to have the ESP32 Uploader Plugin installed in your Arduino IDE. Follow the next tutorial before proceeding:
If you’re using VS Code with PlatformIO, follow the next tutorial to learn how to upload files to the filesystem:
Organizing your Files
To keep the project organized and make it easier to understand, weâll create five different files to build the web server:
- Arduino sketch that handles the web server;
- index.html: to define the content of the web page in station mode to display sensor readings;
- style.css: to style the web page;
- script.js: to program the behavior of the web pageâhandle web server responses, events, update the time, etc.;
- wifimanager.html: to define the web page’s content to display the Wi-Fi Manager when the ESP32 is in access point mode.
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 ESP32 filesystem (SPIFFS).
You can download all project files:
Creating the HTML Files
For this project, you need two HTML files. One to build the main page that displays the sensor readings (index.html) and another to build the Wi-Fi Manager page (wifimanager.html).
index.html
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">
</head>
<body>
<div class="topnav">
<h1>ESP WEB SERVER SENSOR READINGS</h1>
</div>
<div class="content">
<div class="card-grid">
<div class="card">
<p class="card-title">BME280 Sensor Readings</p>
<p>
<table>
<tr>
<th>READING</th>
<th>VALUE</th>
</tr>
<tr>
<td>Temperature</td>
<td><span id="temp"></span> °C</td>
</tr>
<tr>
<td>Humidity</td>
<td><span id="hum"></span> %</td>
</tr>
<tr>
<td>Pressure</td>
<td><span id="pres"></span> hPa</td>
</tr>
</table>
</p>
<p class="update-time">Last update: <span id="update-time"></span></p>
</div>
</div>
</div>
<script src="script.js"></script>
</body>
</html>
This file creates a table to display the sensor readings. Here’s the paragraph that displays the table:
<p>
<table>
<tr>
<th>READING</th>
<th>VALUE</th>
</tr>
<tr>
<td>Temperature</td>
<td><span id="temp"></span> °C</td>
</tr>
<tr>
<td>Humidity</td>
<td><span id="hum"></span> %</td>
</tr>
<tr>
<td>Pressure</td>
<td><span id="pres"></span> hPa</td>
</tr>
</table>
</p>
To create a table in HTML, start with the <table> and </table> tags. This encloses the entire table. To create a row, use the <tr> and </tr> tags. The table is defined with a series of rows. Use the <tr></tr> pair to enclose each row of data. The table heading is defined using the <th> and </th> tags, and each table cell is defined using the <td> and </td> tags.
Notice that the cells to display the sensor readings have <span> tags with specific ids to manipulate them later using JavaScript to insert the updated readings. For example, the cell for the temperature value has the id temp.
<td><span id="temp"></span> °C</td>
Finally, thereâs a paragraph to display the last time the readings were updated:
<p class="update-time">Last update: <span id="update-time"></span></p>
Thereâs a <span> tag with the update-time id. This will be used later to insert the date and time using JavaScript.
wifimanager.html
Copy the following to the wifimanager.html file.
<!DOCTYPE html>
<html>
<head>
<title>ESP Wi-Fi Manager</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" href="data:,">
<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">
</head>
<body>
<div class="topnav">
<h1>ESP Wi-Fi Manager</h1>
</div>
<div class="content">
<div class="card-grid">
<div class="card">
<form action="/" method="POST">
<p>
<label for="ssid">SSID</label>
<input type="text" id ="ssid" name="ssid"><br>
<label for="pass">Password</label>
<input type="text" id ="pass" name="pass"><br>
<label for="ip">IP Address</label>
<input type="text" id ="ip" name="ip" value="192.168.1.200">
<input type ="submit" value ="Submit">
</p>
</form>
</div>
</div>
</div>
<script src="script.js"></script>
</body>
</html>
This file creates an HTML form to insert the SSID and password of the network you want the ESP32 to join. You can also insert the IP address that you want your ESP32 to have. The following image shows the Wi-Fi Manager web pageâ three input fields and a submit button.
We want to send the submitted values on the input fields to the server when we click on the Submit button.
Here’s the HTML form with the three input fields:
<form action="/" method="POST">
<p>
<label for="ssid">SSID</label>
<input type="text" id ="ssid" name="ssid"><br>
<label for="pass">Password</label>
<input type="text" id ="pass" name="pass"><br>
<label for="ip">IP Address</label>
<input type="text" id ="ip" name="ip" value="192.168.1.200">
<input type ="submit" value ="Submit">
</p>
</form>
This is the input field for the SSID:
<label for="ssid">SSID</label>
<input type="text" id ="ssid" name="ssid"><br>
The input field for the password:
<label for="pass">Password</label>
<input type="text" id ="pass" name="pass"><br>
The HTML form contains different form elements. All the form elements are enclosed inside this <form> tag. It contains controls (the input fields) and labels for those controls.
Additionally, the <form> tag must include the action attribute that specifies what you want to do when the form is submitted (it redirects to the / root URL, so that we remain on the same page). In our case, we want to send that data to the server (ESP32) when the user clicks the Submit button. The method attribute specifies the HTTP method (GET or POST) used when submitting the form data. In this case, weâll use HTTP POST method.
<form action="/" method="POST">
POST is used to send data to a server to create/update a resource. The data sent to the server with POST is stored in the request body of the HTTP request. In this case, after submitting the values in the input fields, the body of the HTTP POST request would look like this:
POST /
Host: localhost
ssid: YOUR-NETWORK-SSID
pass: YOUR-PASSWORD
ip: IP-ADDRESS
The <input type=”submit” value=”Submit”> creates a submit button with the text âSubmitâ. When you click this button, the data submitted in the form is sent to the server.
<input type ="submit" value ="Submit">
And finally, there is an input field for the IP address you want to attribute to the ESP in station mode. By default, we set it to 192.168.1.200. You can set another default IP address or delete the value parameterâit wonât have a default value, the network automatically assigns a valid IP address to your board.
<label for="ip">IP Address</label>
<input type="text" id ="ip" name="ip" value="192.168.1.200">
Creating the CSS File
Copy the following styles to your style.css file.
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: 50px;
}
.card-grid {
max-width: 800px;
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
}
th, td {
text-align: center;
padding: 8px;
}
tr:nth-child(even) {
background-color: #f2f2f2
}
tr:hover {
background-color: #ddd;
}
th {
background-color: #50b8b4;
color: white;
}
table {
margin: 0 auto;
width: 90%
}
.update-time {
font-size: 0.8rem;
color:#1282A2;
}
This file styles the previous web pages.
Creating the JavaScript File
Copy the following to the script.js file.
// Get current sensor readings when the page loads
window.addEventListener('load', getReadings);
//Function to add date and time of last update
function updateDateTime() {
var currentdate = new Date();
var datetime = currentdate.getDate() + "/"
+ (currentdate.getMonth()+1) + "/"
+ currentdate.getFullYear() + " at "
+ currentdate.getHours() + ":"
+ currentdate.getMinutes() + ":"
+ currentdate.getSeconds();
document.getElementById("update-time").innerHTML = datetime;
console.log(datetime);
}
// 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);
document.getElementById("temp").innerHTML = myObj.temperature;
document.getElementById("hum").innerHTML = myObj.humidity;
document.getElementById("pres").innerHTML = myObj.pressure;
updateDateTime();
}
};
xhr.open("GET", "/readings", true);
xhr.send();
}
// Create an Event Source to listen for events
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('new_readings', function(e) {
console.log("new_readings", e.data);
var obj = JSON.parse(e.data);
document.getElementById("temp").innerHTML = obj.temperature;
document.getElementById("hum").innerHTML = obj.humidity;
document.getElementById("pres").innerHTML = obj.pressure;
updateDateTime();
}, false);
}
This JavaScript handles the events sent by the server and updates the sensor readings on the corresponding places. It also requests date and time whenever a new reading is available. This file also makes a request to the latest sensor readings when you open a connection with the server.
Let’s take a click look at the JavaScript file and see how it works.
Get Readings
When you access the web page for the first time, it makes a request to 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.
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 pages loads (‘load’) to get the current sensor readings.
updateDateTime() function
The updateDateTime() function gets the current date and time and places it in the HTML element with the update-time id.
function updateDateTime() {
var currentdate = new Date();
var datetime = currentdate.getDate() + "/"
+ (currentdate.getMonth()+1) + "/"
+ currentdate.getFullYear() + " at "
+ currentdate.getHours() + ":"
+ currentdate.getMinutes() + ":"
+ currentdate.getSeconds();
document.getElementById("update-time").innerHTML = datetime;
console.log(datetime);
}
getReadings() function
Now, letâs take a look at the getReadings function. It sends a GET request to the server on the /readings URL and handles the responseâa JSON string containing the sensor readings. It also places the temperature, humidity and pressure values on the HTML elements with the corresponding ids.
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);
document.getElementById("temp").innerHTML = myObj.temperature;
document.getElementById("hum").innerHTML = myObj.humidity;
document.getElementById("pres").innerHTML = myObj.pressure;
updateDateTime();
}
};
xhr.open("GET", "/readings", true);
xhr.send();
}
Handle Events
Now, we need to handle the events sent by the server (Server-Sent Events).
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 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);
Then, add an event listener for the ‘new_readings’ event.
source.addEventListener('new_readings', function(e) {
console.log("new_readings", e.data);
var obj = JSON.parse(e.data);
document.getElementById("temp").innerHTML = obj.temperature;
document.getElementById("hum").innerHTML = obj.humidity;
document.getElementById("pres").innerHTML = obj.pressure;
updateDateTime();
}, false);
When new readings are available, the ESP sends an event (‘new_readings’) to the client with a JSON string that contains the sensor readings.
The following line prints the content of the message on the console:
console.log("new_readings", e.data);
Then, convert the data into a JSON object with the parse() method and save it in the obj variable.
var obj = JSON.parse(e.data);
The JSON string comes in the following format:
{
"temperature" : "25",
"humidity" : "50",
"pressure" : "1015"
}
You can get the temperature with obj.temperature, the humidity with obj.humidity and the pressure with obj.pressure.
The following lines put the received data into the elements with the corresponding ids (temp,hum and pres) on the web page.
document.getElementById("temp").innerHTML = obj.temperature;
document.getElementById("hum").innerHTML = obj.humidity;
document.getElementById("pres").innerHTML = obj.pressure;
Arduino Sketch
Copy the following code to your Arduino IDE or to the main.cpp file if your using PlatformIO.
/*********
Rui Santos
Complete instructions at https://RandomNerdTutorials.com/esp32-status-indicator-sensor-pcb/
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 <Adafruit_NeoPixel.h>
#include <WiFi.h>
#include <AsyncTCP.h>
#include <ESPAsyncWebServer.h>
#include "SPIFFS.h"
#include <Arduino_JSON.h>
#include <Adafruit_BME280.h>
#include <Adafruit_Sensor.h>
// Create AsyncWebServer object on port 80
AsyncWebServer server(80);
// Create an Event Source on /events
AsyncEventSource events("/events");
// Search for parameter in HTTP POST request
const char* PARAM_INPUT_1 = "ssid";
const char* PARAM_INPUT_2 = "pass";
const char* PARAM_INPUT_3 = "ip";
//Variables to save values from HTML form
String ssid;
String pass;
String ip;
// File paths to save input values permanently
const char* ssidPath = "/ssid.txt";
const char* passPath = "/pass.txt";
const char* ipPath = "/ip.txt";
IPAddress localIP;
//IPAddress localIP(192, 168, 1, 200); // hardcoded
// Set your Gateway IP address
IPAddress gateway(192, 168, 1, 1);
IPAddress subnet(255, 255, 0, 0);
// Timer variables (check wifi)
unsigned long previousMillis = 0;
const long interval = 10000; // interval to wait for Wi-Fi connection (milliseconds)
// WS2812B Addressable RGB LEDs
#define STRIP_1_PIN 27 // GPIO the LEDs are connected to
#define STRIP_2_PIN 32 // GPIO the LEDs are connected to
#define LED_COUNT 5 // Number of LEDs
#define BRIGHTNESS 50 // NeoPixel brightness, 0 (min) to 255 (max)
Adafruit_NeoPixel strip1(LED_COUNT, STRIP_1_PIN, NEO_GRB + NEO_KHZ800);
Adafruit_NeoPixel strip2(LED_COUNT, STRIP_2_PIN, NEO_GRB + NEO_KHZ800);
// Create a sensor object
Adafruit_BME280 bme; // BME280 connect to ESP32 I2C (GPIO 21 = SDA, GPIO 22 = SCL)
//Variables to hold sensor readings
float temp;
float hum;
float pres;
// Json Variable to Hold Sensor Readings
JSONVar readings;
// Timer variables (get sensor readings)
unsigned long lastTime = 0;
unsigned long timerDelay = 30000;
//-----------------FUNCTIONS TO HANDLE SENSOR READINGS-----------------//
// Init BME280
void initBME(){
if (!bme.begin(0x76)) {
Serial.println("Could not find a valid BME280 sensor, check wiring!");
while (1);
}
}
// Get Sensor Readings
void getSensorReadings(){
temp = bme.readTemperature();
hum = bme.readHumidity();
pres= bme.readPressure()/100.0F;
}
// Return JSON String from sensor Readings
String getJSONReadings(){
readings["temperature"] = String(temp);
readings["humidity"] = String(hum);
readings["pressure"] = String(pres);
String jsonString = JSON.stringify(readings);
return jsonString;
}
//Update RGB LED colors accordingly to temp and hum values
void updateColors(){
strip1.clear();
strip2.clear();
//Number of lit LEDs (temperature)
int tempLEDs;
if (temp<=0){
tempLEDs = 1;
}
else if (temp>0 && temp<=10){
tempLEDs = 2;
}
else if (temp>10 && temp<=20){
tempLEDs = 3;
}
else if (temp>20 && temp<=30){
tempLEDs = 4;
}
else{
tempLEDs = 5;
}
//Turn on LEDs for temperature
for(int i=0; i<tempLEDs; i++) {
strip1.setPixelColor(i, strip1.Color(255, 165, 0));
strip1.show();
}
//Number of lit LEDs (humidity)
int humLEDs = map(hum, 0, 100, 1, LED_COUNT);
//Turn on LEDs for humidity
for(int i=0; i<humLEDs; i++) { // For each pixel...
strip2.setPixelColor(i, strip2.Color(25, 140, 200));
strip2.show();
}
}
//-----------------FUNCTIONS TO HANDLE SPIFFS AND FILES-----------------//
// Initialize SPIFFS
void initSPIFFS() {
if (!SPIFFS.begin(true)) {
Serial.println("An error has occurred while mounting SPIFFS");
}
else{
Serial.println("SPIFFS mounted successfully");
}
}
// Read File from SPIFFS
String readFile(fs::FS &fs, const char * path){
Serial.printf("Reading file: %s\r\n", path);
File file = fs.open(path);
if(!file || file.isDirectory()){
Serial.println("- failed to open file for reading");
return String();
}
String fileContent;
while(file.available()){
fileContent = file.readStringUntil('\n');
break;
}
return fileContent;
}
// Write file to SPIFFS
void writeFile(fs::FS &fs, const char * path, const char * message){
Serial.printf("Writing file: %s\r\n", path);
File file = fs.open(path, FILE_WRITE);
if(!file){
Serial.println("- failed to open file for writing");
return;
}
if(file.print(message)){
Serial.println("- file written");
} else {
Serial.println("- frite failed");
}
}
// Initialize WiFi
bool initWiFi() {
if(ssid=="" || ip==""){
Serial.println("Undefined SSID or IP address.");
return false;
}
WiFi.mode(WIFI_STA);
localIP.fromString(ip.c_str());
if (!WiFi.config(localIP, gateway, subnet)){
Serial.println("STA Failed to configure");
return false;
}
WiFi.begin(ssid.c_str(), pass.c_str());
Serial.println("Connecting to WiFi...");
unsigned long currentMillis = millis();
previousMillis = currentMillis;
while(WiFi.status() != WL_CONNECTED) {
currentMillis = millis();
if (currentMillis - previousMillis >= interval) {
Serial.println("Failed to connect.");
return false;
}
}
Serial.println(WiFi.localIP());
return true;
}
void setup() {
// Serial port for debugging purposes
Serial.begin(115200);
// Initialize strips
strip1.begin();
strip2.begin();
// Set brightness
strip1.setBrightness(BRIGHTNESS);
strip2.setBrightness(BRIGHTNESS);
// Init BME280 senspr
initBME();
// Init SPIFFS
initSPIFFS();
// Load values saved in SPIFFS
ssid = readFile(SPIFFS, ssidPath);
pass = readFile(SPIFFS, passPath);
ip = readFile(SPIFFS, ipPath);
/*Serial.println(ssid);
Serial.println(pass);
Serial.println(ip);*/
if(initWiFi()) {
// If ESP32 inits successfully in station mode light up all pixels in a teal color
for(int i=0; i<LED_COUNT; i++) { // For each pixel...
strip1.setPixelColor(i, strip1.Color(0, 255, 128));
strip2.setPixelColor(i, strip2.Color(0, 255, 128));
strip1.show(); // Send the updated pixel colors to the hardware.
strip2.show(); // Send the updated pixel colors to the hardware.
}
//Handle the Web Server in Station Mode
// Route for root / web page
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
request->send(SPIFFS, "/index.html", "text/html");
});
server.serveStatic("/", SPIFFS, "/");
// Request for the latest sensor readings
server.on("/readings", HTTP_GET, [](AsyncWebServerRequest *request){
getSensorReadings();
String json = getJSONReadings();
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());
}
});
server.addHandler(&events);
server.begin();
}
else {
// else initialize the ESP32 in Access Point mode
// light up all pixels in a red color
for(int i=0; i<LED_COUNT; i++) { // For each pixel...
strip1.setPixelColor(i, strip1.Color(255, 0, 0));
strip2.setPixelColor(i, strip2.Color(255, 0, 0));
//strip1.setPixelColor(i, strip1.Color(128, 0, 21));
//strip2.setPixelColor(i, strip2.Color(128, 0, 21));
strip1.show(); // Send the updated pixel colors to the hardware.
strip2.show(); // Send the updated pixel colors to the hardware.
}
// Set Access Point
Serial.println("Setting AP (Access Point)");
// NULL sets an open Access Point
WiFi.softAP("ESP-WIFI-MANAGER", NULL);
IPAddress IP = WiFi.softAPIP();
Serial.print("AP IP address: ");
Serial.println(IP);
// Web Server Root URL For WiFi Manager Web Page
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
request->send(SPIFFS, "/wifimanager.html", "text/html");
});
server.serveStatic("/", SPIFFS, "/");
// Get the parameters submited on the form
server.on("/", HTTP_POST, [](AsyncWebServerRequest *request) {
int params = request->params();
for(int i=0;i<params;i++){
AsyncWebParameter* p = request->getParam(i);
if(p->isPost()){
// HTTP POST ssid value
if (p->name() == PARAM_INPUT_1) {
ssid = p->value().c_str();
Serial.print("SSID set to: ");
Serial.println(ssid);
// Write file to save value
writeFile(SPIFFS, ssidPath, ssid.c_str());
}
// HTTP POST pass value
if (p->name() == PARAM_INPUT_2) {
pass = p->value().c_str();
Serial.print("Password set to: ");
Serial.println(pass);
// Write file to save value
writeFile(SPIFFS, passPath, pass.c_str());
}
// HTTP POST ip value
if (p->name() == PARAM_INPUT_3) {
ip = p->value().c_str();
Serial.print("IP Address set to: ");
Serial.println(ip);
// Write file to save value
writeFile(SPIFFS, ipPath, ip.c_str());
}
//Serial.printf("POST[%s]: %s\n", p->name().c_str(), p->value().c_str());
}
}
request->send(200, "text/plain", "Done. ESP will restart, connect to your router and go to IP address: " + ip);
delay(3000);
// After saving the parameters, restart the ESP32
ESP.restart();
});
server.begin();
}
}
void loop() {
// If the ESP32 is set successfully in station mode...
if (WiFi.status() == WL_CONNECTED) {
//...Send Events to the client with sensor readins and update colors every 30 seconds
if (millis() - lastTime > timerDelay) {
getSensorReadings();
updateColors();
String message = getJSONReadings();
events.send(message.c_str(),"new_readings" ,millis());
lastTime = millis();
}
}
}
How the Code Works
Letâs take a look at the code and see how it works.
First, include all the necessary libraries:
#include <Arduino.h>
#include <Adafruit_NeoPixel.h>
#include <WiFi.h>
#include <AsyncTCP.h>
#include <ESPAsyncWebServer.h>
#include "SPIFFS.h"
#include <Arduino_JSON.h>
#include <Adafruit_BME280.h>
The following variables are used to search for the SSID, password, and IP address on the HTTP POST request made when the form is submitted.
// Search for parameter in HTTP POST request
const char* PARAM_INPUT_1 = "ssid";
const char* PARAM_INPUT_2 = "pass";
const char* PARAM_INPUT_3 = "ip";
The ssid, pass, and ip variables save the values of the SSID, password, and IP address submitted on the form.
// Variables to save values from HTML form
String ssid;
String pass;
String ip;
The SSID, password, and IP address, when submitted, are saved in files on the ESP filesystem. The following variables refer to the path of those files.
// File paths to save input values permanently
const char* ssidPath = "/ssid.txt";
const char* passPath = "/pass.txt";
const char* ipPath = "/ip.txt";
The station IP address is submitted on the Wi-Fi Manager form. However, you need to set the gateway and subnet in your code:
IPAddress localIP;
//IPAddress localIP(192, 168, 1, 200); // hardcoded
// Set your Gateway IP address
IPAddress gateway(192, 168, 1, 1);
IPAddress subnet(255, 255, 0, 0);
WS2812B Settings
Define the pins that control the RGB LEDs. In this case, we have two individual rows connected to GPIOs 27 and 32.
#define STRIP_1_PIN 27 // GPIO the LEDs are connected to
#define STRIP_2_PIN 32 // GPIO the LEDs are connected to
Define the number of LEDs and the brightness. You can change the brightness if you want.
#define LED_COUNT 5 // Number of LEDs
#define BRIGHTNESS 20 // NeoPixel brightness, 0 (min) to 255 (max)
Finally, initialize two Adafruit_Neopixel objects to control each strip: strip1 (temperature) and strip2 (humidity):
Adafruit_NeoPixel strip1(LED_COUNT, STRIP_1_PIN, NEO_GRB + NEO_KHZ800);
Adafruit_NeoPixel strip2(LED_COUNT, STRIP_2_PIN, NEO_GRB + NEO_KHZ800);
initBME()
The initBME() function initializes the BME280 sensor on the ESP32 default I2C pins:
void initBME(){
if (!bme.begin(0x76)) {
Serial.println("Could not find a valid BME280 sensor, check wiring!");
while (1);
}
}
Learn more about the BME280 sensor: ESP32 with BME280 Sensor using Arduino IDE (Pressure, Temperature, Humidity).
getSensorReadings()
The getSensorReadings() functions gets temperature, humidity, and pressure from the BME280 sensor and saves the values on the temp, hum, and pres variables.
// Get Sensor Readings
void getSensorReadings(){
temp = bme.readTemperature();
hum = bme.readHumidity();
pres= bme.readPressure()/100.0F;
}
getJSONReadings()
The getJSONReadings() function returns a JSON string from the current temperature, humidity, and pressure values.
// Return JSON String from sensor Readings
String getJSONReadings(){
readings["temperature"] = String(temp);
readings["humidity"] = String(hum);
readings["pressure"] = String(pres);
String jsonString = JSON.stringify(readings);
return jsonString;
}
updateColors()
The updateColors() function lights up the RGB LEDs accordingly to the temperature and humidity values range.
First, you need to clear the strips using the clear() method:
strip1.clear();
strip2.clear();
We need to determine how many LEDs we want to light up, taking into account the temperature and humidity values. We save the number of LEDs to lighten up on the tempLEDs and humLEDs variables.
For the temperature, if the temperature is equal to or smaller than zero degrees Celsius, we light up one LED:
if (temp<=0){
tempLEDs = 1;
}
Here are the other ranges:
- 0<temperature=<10 –> 2 LEDs
- 10<temperature=<20 –> 3 LEDs
- 20<temperature=<30 –> 4 LEDs
- 30<temperature–> 5 LEDs
//Number of lit LEDs (temperature)
int tempLEDs;
if (temp<=0){
tempLEDs = 1;
}
else if (temp>0 && temp<=10){
tempLEDs = 2;
}
else if (temp>10 && temp<=20){
tempLEDs = 3;
}
else if (temp>20 && temp<=30){
tempLEDs = 4;
}
else{
tempLEDs = 5;
}
After determining how many LEDs should be lit, we need to actually light up those LEDs. To light up an LED, we can use the setPixelColor() method on the strip1 object followed by the show() method. We need a for loop to light all LEDs.
//Turn on LEDs for temperature
for(int i=0; i<tempLEDs; i++) {
strip1.setPixelColor(i, strip1.Color(255, 165, 0));
strip1.show();
}
We follow a similar procedure for the humidity. First, determine how many LEDs should be lit:
//Number of lit LEDs (humidity)
int humLEDs = map(hum, 0, 100, 1, LED_COUNT);
And finally, light up the humidity LEDs (strip2):
for(int i=0; i<humLEDs; i++) { // For each pixel...
strip2.setPixelColor(i, strip2.Color(25, 140, 200));
strip2.show();
}
initSPIFFS()
This function initializes the ESP32 SPIFFS filesystem. In this project we save the HTML, CSS and JavaScript files to build the web server pages on the filesystem. We also have the .txt files to save the SSID, password and IP address.
void initSPIFFS() {
if (!SPIFFS.begin(true)) {
Serial.println("An error has occurred while mounting SPIFFS");
}
else{
Serial.println("SPIFFS mounted successfully");
}
}
readFile()
The readFile() function reads and returns the content of a file.
String readFile(fs::FS &fs, const char * path){
Serial.printf("Reading file: %s\r\n", path);
File file = fs.open(path);
if(!file || file.isDirectory()){
Serial.println("- failed to open file for reading");
return String();
}
String fileContent;
while(file.available()){
fileContent = file.readStringUntil('\n');
break;
}
return fileContent;
}
writeFile()
The writeFile() functions writes content to a file.
void writeFile(fs::FS &fs, const char * path, const char * message){
Serial.printf("Writing file: %s\r\n", path);
File file = fs.open(path, FILE_WRITE);
if(!file){
Serial.println("- failed to open file for writing");
return;
}
if(file.print(message)){
Serial.println("- file written");
} else {
Serial.println("- frite failed");
}
}
initWiFi()
The initWiFi() function returns a boolean value (either true or false) indicating if the ESP board connected successfully to a network.
First, it checks if the ssid and ip variables are empty. If they are, it wonât be able to connect to a network, so it returns false.
if(ssid=="" || ip==""){
Serial.println("Undefined SSID or IP address.");
return false;
}
If thatâs not the case, weâll try to connect to the network using the SSID and password saved on the ssid and pass variables and set the IP address.
WiFi.mode(WIFI_STA);
localIP.fromString(ip.c_str());
if (!WiFi.config(localIP, gateway, subnet)){
Serial.println("STA Failed to configure");
return false;
}
WiFi.begin(ssid.c_str(), pass.c_str());
Serial.println("Connecting to WiFi...");
If after 10 seconds (interval variable) , it is not able to connect to Wi-Fi, it will return false.
unsigned long currentMillis = millis();
previousMillis = currentMillis;
while(WiFi.status() != WL_CONNECTED) {
currentMillis = millis();
if (currentMillis - previousMillis >= interval) {
Serial.println("Failed to connect.");
return false;
}
}
Serial.println(WiFi.localIP());
If none of the previous conditions are met, it means that the ESP successfully connected to the network in station mode (returns true).
return true;
setup()
In the setup(), initialize the Serial Monitor.
Serial.begin(115200);
Initialize the rows of addressable RGB LEDs (strips):
// Initialize strips
strip1.begin();
strip2.begin();
Set the strips’ brightness. You can change the brightness on the BRIGTHNESS variable.
// Set brightness
strip1.setBrightness(BRIGHTNESS);
strip2.setBrightness(BRIGHTNESS);
Call the initBME() function to initialize the sensor:
// Init BME280 senspr
initBME();
Initialize the filesystem:
// Init SPIFFS
initSPIFFS();
Read the files to get the previously saved SSID, password and IP address.
// Load values saved in SPIFFS
ssid = readFile(SPIFFS, ssidPath);
pass = readFile(SPIFFS, passPath);
ip = readFile(SPIFFS, ipPath);
If the ESP connects successfully in station mode (initWiFi() function returns true):
if(initWiFi()) {
Light up all LEDs in a teal color, so that we know that the ESP32 successfully connected to a Wi-Fi network:
// If ESP32 inits successfully in station mode light up all pixels in a teal color
for(int i=0; i<LED_COUNT; i++) { // For each pixel...
strip1.setPixelColor(i, strip1.Color(0, 255, 128));
strip2.setPixelColor(i, strip2.Color(0, 255, 128));
strip1.show(); // Send the updated pixel colors to the hardware.
strip2.show(); // Send the updated pixel colors to the hardware.
}
Then, we can set the commands to handle the web server requests. Send the index.html file, when you access the root URL:
//Handle the Web Server in Station Mode
// Route for root / web page
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
request->send(SPIFFS, "/index.html", "text/html");
});
Send the CSS and JavaScript files requested by the HTML file (that are also saved in SPIFFS):
server.serveStatic("/", SPIFFS, "/");
When you access the web server page for the first time, it makes a request to the server on the /readings URL asking for the latest sensor readings. When that happens, send the JSON string with the readings:
// Request for the latest sensor readings
server.on("/readings", HTTP_GET, [](AsyncWebServerRequest *request){
getSensorReadings();
String json = getJSONReadings();
request->send(200, "application/json", json);
json = String();
});
Set up the server-sent events 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());
}
});
server.addHandler(&events);
Finally, start the server:
server.begin();
If the ESP32 can’t connect to a Wi-Fi network, the initWiFi() function returns false. In this case, light up all the LEDs in a red color, so that we know the ESP32 will be in access point mode:
for(int i=0; i<LED_COUNT; i++) { // For each pixel...
strip1.setPixelColor(i, strip1.Color(128, 0, 21));
strip2.setPixelColor(i, strip2.Color(128, 0, 21));
strip1.show(); // Send the updated pixel colors to the hardware.
strip2.show(); // Send the updated pixel colors to the hardware.
}
Set up the ESP will as an access point:
// Set Access Point
Serial.println("Setting AP (Access Point)");
// NULL sets an open Access Point
WiFi.softAP("ESP-WIFI-MANAGER", NULL);
IPAddress IP = WiFi.softAPIP();
Serial.print("AP IP address: ");
Serial.println(IP);
To set an access point, we use the softAP() method and pass as arguments the name for the access point and the password. We want the access point to be open, so we set the password to NULL. You can add a password if you want. To learn more about setting up an Access Point, read the following tutorial:
When you access the Access Point, it shows the web page to enter the network credentials on the form. So, the ESP must send the wifimanager.html file when it receives a request on the root / URL.
// Web Server Root URL For WiFi Manager Web Page
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
request->send(SPIFFS, "/wifimanager.html", "text/html");
});
We must also handle what happens when the form is submitted via HTTP POST request.
The following lines save the submitted values on the ssid, pass, and ip variables and save those variables on the corresponding files.
// Get the parameters submited on the form
server.on("/", HTTP_POST, [](AsyncWebServerRequest *request) {
int params = request->params();
for(int i=0;i<params;i++){
AsyncWebParameter* p = request->getParam(i);
if(p->isPost()){
// HTTP POST ssid value
if (p->name() == PARAM_INPUT_1) {
ssid = p->value().c_str();
Serial.print("SSID set to: ");
Serial.println(ssid);
// Write file to save value
writeFile(SPIFFS, ssidPath, ssid.c_str());
}
// HTTP POST pass value
if (p->name() == PARAM_INPUT_2) {
pass = p->value().c_str();
Serial.print("Password set to: ");
Serial.println(pass);
// Write file to save value
writeFile(SPIFFS, passPath, pass.c_str());
}
// HTTP POST ip value
if (p->name() == PARAM_INPUT_3) {
ip = p->value().c_str();
Serial.print("IP Address set to: ");
Serial.println(ip);
// Write file to save value
writeFile(SPIFFS, ipPath, ip.c_str());
}
//Serial.printf("POST[%s]: %s\n", p->name().c_str(), p->value().c_str());
}
}
After submitting the form, send a response with some text, so that we know that the ESP received the form details:
request->send(200, "text/plain", "Done. ESP will restart, connect to your router and go to IP address: " + ip);
After three seconds, restart the ESP board with ESP.restart():
delay(3000);
// After saving the parameters, restart the ESP32
ESP.restart();
After restarting, the board will have the SSID and password saved on the files, and it will successfully initialize in station mode.
loop()
In the loop(), check if the ESP32 is successfully connected to a wi-fi station:
if (WiFi.status() == WL_CONNECTED) {
If it is, do the following every 30 seconds (timerDelay variable):
- get the latest sensor readings: call the getSensorReadings() function;
- update the RGB LED colors to match the temperature and humidity values: call the updateColors() functions;
- send an event to the browser with the latest sensor readings in JSON format.
if (millis() - lastTime > timerDelay) {
getSensorReadings();
updateColors();
String message = getJSONReadings();
events.send(message.c_str(),"new_readings" ,millis());
lastTime = millis();
}
Demonstration
After successfully uploading all files, you can open the Serial Monitor. If it is running the code for the first time, it will try to read the ssid.txt, pass.txt, and ip.txt files and it wonât succeed because those files werenât created yet. So, it will start an Access Point.
All the LEDs on the shield will be lit in red, indicating that the board is set as an access point.
On your computer or smartphone, go to your network settings and connect to the ESP-WIFI-MANAGER access point.
Then, open your browser and go to 192.168.4.1. The Wi-Fi Manager web page should open.
Enter your network credentials: SSID and Password and an available IP address on your local network. After that, youâll be redirected to the following page:
At the same time, the ESP should print the following in the Serial Monitor indicating that the parameters youâve inserted were successfully saved on the corresponding files:
After a few seconds, the ESP will restart. And if youâve inserted the right SSID and password it will start in station mode:
All the LEDs on the shield will be lit in a teal color for 30 seconds.
This time, open a browser on your local network and insert the ESP IP address. You should get access to the web page that displays the sensor readings:
The LEDs on the shield will light up accordingly to the temperature and humidity range.
Note: the previous picture and the web server print screen were taken at different times (that’s why the values on the table don’t match the number of lit LEDs). You can also watch the video demonstration.
Wrapping Up
In this tutorial, youâve learned how to create a shield for the ESP32 with a BME280 sensor, two rows of addressable RGB LEDs, and a pushbutton. You’ve also learned how to set up a Wi-Fi Manager for your web server projects. With the Wi-Fi Manager, you can easily connect your ESP web servers to different networks without having to hard-code network credentials. You can apply the Wi-Fi Manager to any web server project.
If you like this tutorial, we have other similar projects that include building and designing PCBs:
- ESP32 Weather Station Interface PCB Shield (Temperature, Humidity, Pressure, Date and Time)
- ESP32-CAM with Telegram: Take Photos, Control Outputs, Request Sensor Readings and Motion Notifications
- ESP32 IoT Shield PCB with Dashboard for Outputs and Sensors
- Build an All-in-One ESP32 Weather Station Shield
- Build a Multisensor Shield for ESP8266
- EXTREME POWER SAVING with Microcontroller External Wake Up: Latching Power PCB
Learn more about the ESP32 with our resources:
- Build Web Servers with ESP32 and ESP8266 eBook
- Learn ESP32 with Arduino IDE (eBook + video course)
- More ESP32 tutorials and projectsâŠ
[Update] the bare PCB giveaway ended and the winners are: Hai, Hasse Lorentzon, Tuan Hazeem, Paul Smulders, and Sudhir Gupta.
Very useful.
Purists might be tempted to use one pin for both neopixel strops, but as there are pins enough using separate pins makes the programming easier
Thank you so much Rui.
What an awesome project.
Please help and modify your project with the ttgo esp32 sim800l to connect to the telegram with it’s gprs instead of Wi-Fi.
Thank you for sharing this brilliant project details âšâš . Hoping for new modifications as well…
How about an Audio VDU meter using some of the ESP32 A/D converter inputs?
Thank you for your nice tutorials, even if you do not follow them exactly. So it is an incredibly good tool for hobby engineers.
Very helpful/useful tutorial, always enjoy your tutorials.
Hello Rui & Sara
I have this code running on an ESP32-WROOM-32, and it configures the WiFi just fine, attemps to load it after a re-start, but goes back into AP mode after the restart. If I reset the controller, it will connect to my Router after a single reset, then if I reset again, it will not connect, instead going into AP mode again. Then, if I re-set again, it will connect, reset again, AP Mode, ETC.
With every alternate reset, it connects to the configured WiFi, every other reset, it will go into AP Mode.
I tried a sketch from another site, which stores the WiFi credentials in EEPROM, rather than SPIFFS, and it exibited the same behavior of connecting every second reset.
Any idea what is happening?
Hi Dave.
Do you have another board to experiment with?
Regards,
Sara
I do, I will try that this evening when I am at home. Pretty sure I tried it already, but, not 100% certain.
Well, something so simple, Thanks Sara, it is indeed a faulty ESP32! I have one at work, & one at home, that are both set up on breadboards, exactly the same. (I work on the same project, at work or at home, & have the same hardware available for testing) The one at work exibits the issue with the WiFi only working every other reset, while the one at home works as expected.
Great!
I’m glad you found the problem.
Thanks for following our work.
Regards,
Sara
Hi Sara,
The WS2812B & Serial Monitor display the correct readings, but when I go to the IP ‘192.168.1.200’, I just get a skeleton web page with no readings or time information. Have tried everything I can think of to cure this without success.
Please help.
Keep up the great work.
Alan
Hi.
Did you set your network credentials first by connecting to the ESP32 access point?
Regards,
Sara
Hi Sara,
Yes I did that. Have printed them on my Serial Monitor so I know they are correct.
The skeleton webpage appears, but no data.
Hi guys,
Been following your page for a few months now. Very interesting stuff, everything I’ve tried works beautifully. So a big thank you for all of this.
While browsing this project, I noticed that the link to the BME module is pointing to a display module. Would be interesting to see where you guys get yours from.
Thanks!
Hi Daniel.
Thanks for pointing that out.
The link is fixed now.
Here’s the correct link: https://makeradvisor.com/tools/bme280-sensor-module/
Regards,
Sara
Hi, Hope you are well
I have problems to find the ESP32 with 36 pin layout do you have a source where to get them?
Best regards
Hi
We usually get ours from Banggood.
You can get a link to it on our Maker Advisor page: https://makeradvisor.com/tools/esp32-dev-board-wi-fi-bluetooth/
Please note that some links to other stores go to boards with a different number of pins.
Regards,
Sara
Hi Sara
yes I used all links, but only 30 pin boards available, that’s why I asking…
br
The link to Banggood shows a 36-pin board.
Regards,
Sara
Hello Sara. Any way to work with ESP8266?
Hi.
This project can work with the ESP8266, but you need to make a few modifications to the code.
Additionally, if you want to use the shield, you need to redesign it.
Regards,
Sara
I don’t have the experience to develop this idea. case about a little of your time, could make a post about it. Thanks for the answer