{"id":102612,"date":"2021-03-25T14:10:27","date_gmt":"2021-03-25T14:10:27","guid":{"rendered":"https:\/\/randomnerdtutorials.com\/?p=102612"},"modified":"2025-03-14T16:20:28","modified_gmt":"2025-03-14T16:20:28","slug":"esp32-status-indicator-sensor-pcb","status":"publish","type":"post","link":"https:\/\/randomnerdtutorials.com\/esp32-status-indicator-sensor-pcb\/","title":{"rendered":"ESP32 Neopixel Status Indicator and Sensor PCB Shield with Wi-Fi Manager"},"content":{"rendered":"\n<p>In this project, we\u2019ll create a status indicator PCB shield for the ESP32 featuring two rows of addressable RGB neopixel LEDs, a BME280 sensor, and a pushbutton. We&#8217;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&#8217;ll also set up a Wi-Fi Manager\u2060\u2014the LEDs indicate whether it is already connected to a Wi-Fi network or if it is set in access point mode.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" fetchpriority=\"high\" decoding=\"async\" width=\"1200\" height=\"675\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/03\/ESP32-Status-Indicator-Sensor-Shield-Wi-Fi-Manager.jpg?resize=1200%2C675&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 Neopixel Status Indicator and Sensor PCB Shield with Wi-Fi Manager\" class=\"wp-image-102779\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/03\/ESP32-Status-Indicator-Sensor-Shield-Wi-Fi-Manager.jpg?w=1280&amp;quality=100&amp;strip=all&amp;ssl=1 1280w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/03\/ESP32-Status-Indicator-Sensor-Shield-Wi-Fi-Manager.jpg?resize=300%2C169&amp;quality=100&amp;strip=all&amp;ssl=1 300w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/03\/ESP32-Status-Indicator-Sensor-Shield-Wi-Fi-Manager.jpg?resize=1024%2C576&amp;quality=100&amp;strip=all&amp;ssl=1 1024w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/03\/ESP32-Status-Indicator-Sensor-Shield-Wi-Fi-Manager.jpg?resize=768%2C432&amp;quality=100&amp;strip=all&amp;ssl=1 768w\" sizes=\"(max-width: 1200px) 100vw, 1200px\" \/><\/figure><\/div>\n\n\n<p>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.<\/p>\n\n\n\n<p>By following this project, you&#8217;ll learn more about the following concepts:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Controlling two addressable RGB LED &#8220;strips&#8221; individually with the ESP32;<\/li>\n\n\n\n<li>Build a Web Server with the ESP32 using Server-sent Events (SSE);<\/li>\n\n\n\n<li>Handle HTML input fields to save data on your board (SSID and password);<\/li>\n\n\n\n<li>Save variables permanently using files on the filesystem;<\/li>\n\n\n\n<li>Build your own Wi-Fi Manager using the ESPAsyncWebServer library;<\/li>\n\n\n\n<li>Switch between station mode and access point mode;<\/li>\n\n\n\n<li>And much more&#8230;<\/li>\n<\/ul>\n\n\n\n<p>To better understand how this project works, we recommend taking a look at the following tutorials:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/randomnerdtutorials.com\/esp32-esp8266-input-data-html-form\/\">Input Data on HTML Form ESP32\/ESP8266 Web Server using Arduino IDE<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/randomnerdtutorials.com\/esp32-access-point-ap-web-server\/\">How to Set an ESP32 Access Point (AP) for Web Server<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/randomnerdtutorials.com\/esp32-static-fixed-ip-address-arduino-ide\/\">ESP32 Static\/Fixed IP Address<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/randomnerdtutorials.com\/esp32-web-server-sent-events-sse\/\">ESP32 Web Server using Server-Sent Events (Update Sensor Readings Automatically)<\/a><\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"watch-video-demonstration\">Watch the Video Tutorial<\/h2>\n\n\n<p style=\"text-align:center\"><iframe width=\"720\" height=\"405\" src=\"https:\/\/www.youtube.com\/embed\/JrhQ1RCEa54?rel=0\" frameborder=\"0\" allowfullscreen><\/iframe><\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Resources<\/h3>\n\n\n\n<p>You can find all the resources needed to build this project in the links below (or you can visit the&nbsp;<a href=\"https:\/\/github.com\/RuiSantosdotme\/esp32-status-pcb\/\" target=\"_blank\" rel=\"noreferrer noopener\">GitHub project&nbsp;page<\/a>):<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/github.com\/RuiSantosdotme\/esp32-status-pcb\/raw\/main\/ESP32_WiFi_Manager_Neopixel.zip\" target=\"_blank\" rel=\"noreferrer noopener\">ESP32 Code (Arduino IDE)<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/github.com\/RuiSantosdotme\/esp32-status-pcb\/raw\/main\/Gerber_PCB_ESP32_WiFi_Manager_Neopixel_Shield_2021-03-23.zip\" target=\"_blank\" rel=\"noreferrer noopener\">Gerber files<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/github.com\/RuiSantosdotme\/esp32-status-pcb\/raw\/main\/Project_ESP32_WiFi_Manager_Neopixel_Shield_2021-03-24_10-55-16.zip\" target=\"_blank\" rel=\"noreferrer noopener\">EasyEDA project to edit the PCB<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/github.com\/RuiSantosdotme\/esp32-status-pcb\/archive\/refs\/heads\/main.zip\" target=\"_blank\" rel=\"noreferrer noopener\"><strong>Click here to download all the files<\/strong><\/a><\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Project Overview<\/h2>\n\n\n\n<p>Before going straight to the project, let&#8217;s take a look at the PCB Shield features (hardware and software).<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">PCB Shield Features<\/h3>\n\n\n\n<p>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\u2019re using the&nbsp;<a rel=\"noreferrer noopener\" href=\"https:\/\/makeradvisor.com\/tools\/esp32-dev-board-wi-fi-bluetooth\/\" target=\"_blank\">ESP32 DEVKIT DOIT V1<\/a>&nbsp;board (the model with&nbsp;<strong>36 GPIOs<\/strong>).<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" decoding=\"async\" width=\"750\" height=\"421\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/03\/Status-Indicator-and-Sensor-Shield.jpg?resize=750%2C421&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 Status Indicator and Sensor Shield PCB\" class=\"wp-image-102729\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/03\/Status-Indicator-and-Sensor-Shield.jpg?w=750&amp;quality=100&amp;strip=all&amp;ssl=1 750w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/03\/Status-Indicator-and-Sensor-Shield.jpg?resize=300%2C168&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 750px) 100vw, 750px\" \/><\/figure><\/div>\n\n\n<p>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.<\/p>\n\n\n\n<p>Additionally, you can follow this project by assembling the circuit on a breadboard if you don&#8217;t want to build a PCB shield. <\/p>\n\n\n\n<p>The shield consists of:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>BME280 temperature, humidity, and pressure sensor;<\/li>\n\n\n\n<li>Pushbutton;<\/li>\n\n\n\n<li>Two rows of 5 addressable RGB LEDs (WS2812B).<\/li>\n<\/ul>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large\"><img data-recalc-dims=\"1\" decoding=\"async\" width=\"783\" height=\"758\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/03\/ESP32-Status-Indicator-Sensor-Shield-RGB-LEDs_Shield-overview.png?resize=783%2C758&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 Status Indicator Sensor Shield RGB LEDs Overview\" class=\"wp-image-102664\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/03\/ESP32-Status-Indicator-Sensor-Shield-RGB-LEDs_Shield-overview.png?w=783&amp;quality=100&amp;strip=all&amp;ssl=1 783w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/03\/ESP32-Status-Indicator-Sensor-Shield-RGB-LEDs_Shield-overview.png?resize=300%2C290&amp;quality=100&amp;strip=all&amp;ssl=1 300w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/03\/ESP32-Status-Indicator-Sensor-Shield-RGB-LEDs_Shield-overview.png?resize=768%2C743&amp;quality=100&amp;strip=all&amp;ssl=1 768w\" sizes=\"(max-width: 783px) 100vw, 783px\" \/><\/figure><\/div>\n\n\n<p>If you replicate this project on a breadboard, instead of individual WS2812B addressable RGB LEDs, you can use <a href=\"https:\/\/makeradvisor.com\/tools\/ws2812b-addressable-rgb-led-strip\/\" target=\"_blank\" rel=\"noreferrer noopener\">addressable RGB LED strips<\/a>.&nbsp;<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">PCB Shield Pin Assignment<\/h3>\n\n\n\n<p>The following table shows the pin assignment for each component on the shield:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><tbody><tr><td><strong>Component<\/strong><\/td><td><strong>ESP32 Pin Assignment<\/strong><\/td><\/tr><tr><td>BME280<\/td><td>GPIO 21 (SDA), GPIO 22 (SCL)<\/td><\/tr><tr><td>Pushbutton<\/td><td>GPIO 18<\/td><\/tr><tr><td>Addressable RGB LEDs (row 1)<\/td><td>GPIO 27<\/td><\/tr><tr><td>Addressable RGB LEDs (row 2)<\/td><td>GPIO 32<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">PCB Software Features<\/h3>\n\n\n\n<p>You can program the shield in several different ways. We&#8217;ll program the ESP32 to have the following features:<\/p>\n\n\n\n<p><strong>Web Server<\/strong><\/p>\n\n\n\n<p>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. <a href=\"https:\/\/randomnerdtutorials.com\/esp32-web-server-sent-events-sse\/\">Learn more about Server-Sent Events in this project<\/a>. <\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"786\" height=\"518\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/03\/ESP32-BME280-Sensor-Readings-Web-Server-Table.png?resize=786%2C518&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 BME280 Sensor Readings Web Server Table\" class=\"wp-image-102616\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/03\/ESP32-BME280-Sensor-Readings-Web-Server-Table.png?w=786&amp;quality=100&amp;strip=all&amp;ssl=1 786w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/03\/ESP32-BME280-Sensor-Readings-Web-Server-Table.png?resize=300%2C198&amp;quality=100&amp;strip=all&amp;ssl=1 300w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/03\/ESP32-BME280-Sensor-Readings-Web-Server-Table.png?resize=768%2C506&amp;quality=100&amp;strip=all&amp;ssl=1 768w\" sizes=\"(max-width: 786px) 100vw, 786px\" \/><\/figure><\/div>\n\n\n<p><strong>Visual Interface (Addressable RGB LEDs)<\/strong><\/p>\n\n\n\n<p>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\u2014the same for the humidity readings. The temperature values are displayed in an orange\/yellow color, and the humidity is displayed in a teal color.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"750\" height=\"724\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/03\/ESP32-Status-Indicator-Sensor-Shield-RGB-LEDs-Overview-2.png?resize=750%2C724&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 Status Indicator Sensor Shield RGB LEDs Overview Sensor Readings\" class=\"wp-image-102638\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/03\/ESP32-Status-Indicator-Sensor-Shield-RGB-LEDs-Overview-2.png?w=750&amp;quality=100&amp;strip=all&amp;ssl=1 750w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/03\/ESP32-Status-Indicator-Sensor-Shield-RGB-LEDs-Overview-2.png?resize=300%2C290&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 750px) 100vw, 750px\" \/><\/figure><\/div>\n\n\n<p><strong>Wi-Fi Manager<\/strong><\/p>\n\n\n\n<p>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.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"643\" height=\"409\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/03\/ESP32-Wi-Fi-Manager-Web-Page.png?resize=643%2C409&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 Wi-Fi Manager Web Page web server SSID password\" class=\"wp-image-102617\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/03\/ESP32-Wi-Fi-Manager-Web-Page.png?w=643&amp;quality=100&amp;strip=all&amp;ssl=1 643w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/03\/ESP32-Wi-Fi-Manager-Web-Page.png?resize=300%2C191&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 643px) 100vw, 643px\" \/><\/figure><\/div>\n\n\n<p>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.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"750\" height=\"806\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/03\/ESP32-Status-Indicator-Sensor-Shield-RGB-LEDs-Wi-Fi-Manager.png?resize=750%2C806&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 Status Indicator Sensor Shield RGB LEDs Wi-Fi Manager\" class=\"wp-image-102639\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/03\/ESP32-Status-Indicator-Sensor-Shield-RGB-LEDs-Wi-Fi-Manager.png?w=750&amp;quality=100&amp;strip=all&amp;ssl=1 750w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/03\/ESP32-Status-Indicator-Sensor-Shield-RGB-LEDs-Wi-Fi-Manager.png?resize=279%2C300&amp;quality=100&amp;strip=all&amp;ssl=1 279w\" sizes=\"(max-width: 750px) 100vw, 750px\" \/><\/figure><\/div>\n\n\n<h2 class=\"wp-block-heading\">Testing the Circuit on a Breadboard<\/h2>\n\n\n\n<p>Before designing and building the PCB, it\u2019s important to test the circuit on a breadboard. If you don\u2019t want to make a PCB, you can still follow this project by assembling the circuit on a breadboard.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Parts Required<\/h3>\n\n\n\n<p>To assemble the circuit on a breadboard you need the following parts (the parts for the PCB are shown in a later section):<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/makeradvisor.com\/tools\/esp32-dev-board-wi-fi-bluetooth\/\" target=\"_blank\" rel=\"noreferrer noopener\">DOIT ESP32 DEVKIT V1 Board<\/a>\u00a0\u2013 read\u00a0<a href=\"https:\/\/makeradvisor.com\/esp32-development-boards-review-comparison\/\" target=\"_blank\" rel=\"noreferrer noopener\">Best ESP32 Development Boards<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/makeradvisor.com\/tools\/bme280-sensor-module\/\" target=\"_blank\" rel=\"noreferrer noopener\">BME280<\/a>\u00a0(4 pins)<\/li>\n\n\n\n<li>2x <a href=\"https:\/\/makeradvisor.com\/tools\/ws2812b-addressable-rgb-led-strip\/\" target=\"_blank\" rel=\"noreferrer noopener\">WS2812B Addressable RGB LED Strips<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/makeradvisor.com\/tools\/pushbuttons-kit\/\" target=\"_blank\" rel=\"noreferrer noopener\">Pushbutton<\/a>*<\/li>\n\n\n\n<li><a href=\"https:\/\/makeradvisor.com\/tools\/resistors-kits\/\" target=\"_blank\" rel=\"noreferrer noopener\">10k Ohm resistor<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/makeradvisor.com\/tools\/mb-102-solderless-breadboard-830-points\/\" target=\"_blank\" rel=\"noreferrer noopener\">Breadboard<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/makeradvisor.com\/tools\/jumper-wires-kit-120-pieces\/\" target=\"_blank\" rel=\"noreferrer noopener\">Jumper wires<\/a><\/li>\n<\/ul>\n\n\n<p>You can use the preceding links or go directly to <a href=\"https:\/\/makeradvisor.com\/tools\/?utm_source=rnt&utm_medium=post&utm_campaign=post\" target=\"_blank\">MakerAdvisor.com\/tools<\/a> to find all the parts for your projects at the best price!<\/p><p style=\"text-align:center;\"><a href=\"https:\/\/makeradvisor.com\/tools\/?utm_source=rnt&utm_medium=post&utm_campaign=post\" target=\"_blank\"><img data-recalc-dims=\"1\" decoding=\"async\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2017\/10\/header-200.png?w=1200&#038;quality=100&#038;strip=all&#038;ssl=1\"><\/a><\/p>\n\n\n\n<p>After gathering all the parts, assemble the circuit by following the next schematic diagram:<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full is-resized\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"900\" height=\"957\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/03\/ESP32-Status-Indicator-Sensor-Shield-RGB-LEDs-Shield-Breadboard-Diagram.jpg?resize=900%2C957&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 Status Indicator Sensor Shield RGB LEDs Shield Breadboard Diagram\" class=\"wp-image-102780\" style=\"width:812px;height:863px\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/03\/ESP32-Status-Indicator-Sensor-Shield-RGB-LEDs-Shield-Breadboard-Diagram.jpg?w=900&amp;quality=100&amp;strip=all&amp;ssl=1 900w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/03\/ESP32-Status-Indicator-Sensor-Shield-RGB-LEDs-Shield-Breadboard-Diagram.jpg?resize=282%2C300&amp;quality=100&amp;strip=all&amp;ssl=1 282w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/03\/ESP32-Status-Indicator-Sensor-Shield-RGB-LEDs-Shield-Breadboard-Diagram.jpg?resize=768%2C817&amp;quality=100&amp;strip=all&amp;ssl=1 768w\" sizes=\"(max-width: 900px) 100vw, 900px\" \/><\/figure><\/div>\n\n\n<p>*We ended up not using the pushbutton for this particular project, so, it is not necessary to include it in your circuit.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-css-opacity is-style-wide\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Designing the PCB<\/h2>\n\n\n\n<p>To design the circuit and PCB, we used&nbsp;<a href=\"https:\/\/easyeda.com\/\" target=\"_blank\" rel=\"noreferrer noopener nofollow\">EasyEDA<\/a>,&nbsp;a browser-based software, to design PCBs. If you want to customize your PCB, you need to upload the following files:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/github.com\/RuiSantosdotme\/esp32-status-pcb\/raw\/main\/Project_ESP32_WiFi_Manager_Neopixel_Shield_2021-03-24_10-55-16.zip\" target=\"_blank\" rel=\"noreferrer noopener\">EasyEDA project files to edit the PCB<\/a><\/li>\n<\/ul>\n\n\n\n<p>I&#8217;m not an expert in PCB design. However, designing simple PCBs like the one we&#8217;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.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"953\" height=\"896\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/03\/easyeda-circuit-diagram-neopixels-pcb-shield.png?resize=953%2C896&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"EasyEDA Circuit Diagram ESP32 Neopixel Shield PCB\" class=\"wp-image-102797\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/03\/easyeda-circuit-diagram-neopixels-pcb-shield.png?w=953&amp;quality=100&amp;strip=all&amp;ssl=1 953w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/03\/easyeda-circuit-diagram-neopixels-pcb-shield.png?resize=300%2C282&amp;quality=100&amp;strip=all&amp;ssl=1 300w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/03\/easyeda-circuit-diagram-neopixels-pcb-shield.png?resize=768%2C722&amp;quality=100&amp;strip=all&amp;ssl=1 768w\" sizes=\"(max-width: 953px) 100vw, 953px\" \/><\/figure><\/div>\n\n\n<p>Having the parts assigned, place each component. When you\u2019re happy with the layout, make all the connections and route your PCB.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"677\" height=\"298\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/03\/easyeda-ESP32-pcb-neopixels-pcb-shield.png?resize=677%2C298&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"Easyeda ESP32 Neopixels PCB Shield\" class=\"wp-image-102796\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/03\/easyeda-ESP32-pcb-neopixels-pcb-shield.png?w=677&amp;quality=100&amp;strip=all&amp;ssl=1 677w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/03\/easyeda-ESP32-pcb-neopixels-pcb-shield.png?resize=300%2C132&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 677px) 100vw, 677px\" \/><\/figure><\/div>\n\n\n<p>Save your project and export the Gerber files.<\/p>\n\n\n\n<p><strong>Note:&nbsp;<\/strong>you can grab the project files and edit them to customize the shield for your own needs.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong><a href=\"https:\/\/github.com\/RuiSantosdotme\/esp32-status-pcb\/raw\/main\/Gerber_PCB_ESP32_WiFi_Manager_Neopixel_Shield_2021-03-23.zip\" target=\"_blank\" rel=\"noreferrer noopener\">Download Gerber .zip file<\/a><\/strong><\/li>\n\n\n\n<li><a href=\"https:\/\/github.com\/RuiSantosdotme\/esp32-status-pcb\/raw\/main\/Project_ESP32_WiFi_Manager_Neopixel_Shield_2021-03-24_10-55-16.zip\" target=\"_blank\" rel=\"noreferrer noopener\">EasyEDA project to edit the PCB<\/a><\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Ordering the PCBs at PCBWay<\/h2>\n\n\n\n<p>This project is sponsored by PCBWay. <a href=\"https:\/\/randomnerdtutorials.com\/pcbway\" target=\"_blank\" rel=\"noreferrer noopener\">PCBWay<\/a>&nbsp;is a full feature Printed Circuit Board manufacturing service. <\/p>\n\n\n<div class=\"wp-block-image is-style-default\">\n<figure class=\"aligncenter size-full\"><a href=\"https:\/\/makeradvisor.com\/pcbway\" target=\"_blank\" rel=\"noopener noreferrer\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"750\" height=\"291\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/06\/PCBWay-banner.jpg?resize=750%2C291&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"Ordering the PCBs at PCBWay\" class=\"wp-image-97545\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/06\/PCBWay-banner.jpg?w=750&amp;quality=100&amp;strip=all&amp;ssl=1 750w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/06\/PCBWay-banner.jpg?resize=300%2C116&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 750px) 100vw, 750px\" \/><\/a><\/figure><\/div>\n\n\n<p>Turn your DIY breadboard circuits into <a href=\"https:\/\/randomnerdtutorials.com\/pcbway\" target=\"_blank\" rel=\"noreferrer noopener\">professional PCBs<\/a> &#8211; get 10 boards for approximately $5 + shipping (which will vary depending on your country).<\/p>\n\n\n\n<p>Once you have your Gerber files, you can order the PCB. Follow the next steps.<\/p>\n\n\n\n<p>1. Download the Gerber files \u2013&nbsp;<strong><a href=\"https:\/\/github.com\/RuiSantosdotme\/esp32-status-pcb\/raw\/main\/Gerber_PCB_ESP32_WiFi_Manager_Neopixel_Shield_2021-03-23.zip\" target=\"_blank\" rel=\"noreferrer noopener\" title=\"https:\/\/github.com\/RuiSantosdotme\/ESP32-Weather-Station-PCB\/raw\/main\/Gerber_PCB_ESP32%20Weather%20Station%20Interface_2020-12-06_17-30-24.zip\">click here to download the .zip file<\/a><\/strong><\/p>\n\n\n\n<p>2. Go to <a href=\"https:\/\/randomnerdtutorials.com\/pcbway\" target=\"_blank\" rel=\"noreferrer noopener\">PCBWay website<\/a> and open the PCB Instant Quote page.&nbsp;<\/p>\n\n\n<div class=\"wp-block-image is-style-default\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"900\" height=\"504\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/06\/1-PCBWay-Order-PCB.png?resize=900%2C504&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"PCBWay Order PCB open instant quote page\" class=\"wp-image-97554\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/06\/1-PCBWay-Order-PCB.png?w=900&amp;quality=100&amp;strip=all&amp;ssl=1 900w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/06\/1-PCBWay-Order-PCB.png?resize=300%2C168&amp;quality=100&amp;strip=all&amp;ssl=1 300w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/06\/1-PCBWay-Order-PCB.png?resize=768%2C430&amp;quality=100&amp;strip=all&amp;ssl=1 768w\" sizes=\"(max-width: 900px) 100vw, 900px\" \/><\/figure><\/div>\n\n\n<p>3. PCBWay can grab all the PCB details and automatically fills them for you. Use the \u201cQuick-order PCB (Autofill parameters)\u201d.<\/p>\n\n\n<div class=\"wp-block-image is-style-default\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"900\" height=\"469\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/06\/2-PCBWay-Order-PCB-autofill-parameters.png?resize=900%2C469&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"PCBWay Order PCB autofill parameters\" class=\"wp-image-97555\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/06\/2-PCBWay-Order-PCB-autofill-parameters.png?w=900&amp;quality=100&amp;strip=all&amp;ssl=1 900w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/06\/2-PCBWay-Order-PCB-autofill-parameters.png?resize=300%2C156&amp;quality=100&amp;strip=all&amp;ssl=1 300w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/06\/2-PCBWay-Order-PCB-autofill-parameters.png?resize=768%2C400&amp;quality=100&amp;strip=all&amp;ssl=1 768w\" sizes=\"(max-width: 900px) 100vw, 900px\" \/><\/figure><\/div>\n\n\n<p>4. Press the \u201c+ Add Gerber file\u201d button to upload the provided Gerber files.<\/p>\n\n\n<div class=\"wp-block-image is-style-default\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"939\" height=\"521\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/06\/3-PCBWay-Order-PCB-gerbers-files.png?resize=939%2C521&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"PCBWay Order PCB add gerber file button\" class=\"wp-image-97556\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/06\/3-PCBWay-Order-PCB-gerbers-files.png?w=939&amp;quality=100&amp;strip=all&amp;ssl=1 939w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/06\/3-PCBWay-Order-PCB-gerbers-files.png?resize=300%2C166&amp;quality=100&amp;strip=all&amp;ssl=1 300w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/06\/3-PCBWay-Order-PCB-gerbers-files.png?resize=768%2C426&amp;quality=100&amp;strip=all&amp;ssl=1 768w\" sizes=\"(max-width: 939px) 100vw, 939px\" \/><\/figure><\/div>\n\n\n<p>And that\u2019s it. You can also use the OnlineGerberViewer to check if your PCB is looking as it should.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"938\" height=\"817\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/03\/ESP32-Neopixel-PCB-Shield-PCBWay-Gerber-Viewer.png?resize=938%2C817&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 Neopixel PCB Shield PCBWay Gerber Viewer\" class=\"wp-image-102793\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/03\/ESP32-Neopixel-PCB-Shield-PCBWay-Gerber-Viewer.png?w=938&amp;quality=100&amp;strip=all&amp;ssl=1 938w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/03\/ESP32-Neopixel-PCB-Shield-PCBWay-Gerber-Viewer.png?resize=300%2C261&amp;quality=100&amp;strip=all&amp;ssl=1 300w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/03\/ESP32-Neopixel-PCB-Shield-PCBWay-Gerber-Viewer.png?resize=768%2C669&amp;quality=100&amp;strip=all&amp;ssl=1 768w\" sizes=\"(max-width: 938px) 100vw, 938px\" \/><\/figure><\/div>\n\n\n<p>If you aren\u2019t 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.<\/p>\n\n\n<div class=\"wp-block-image is-style-default\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"325\" height=\"627\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/03\/Neopixel-PCB-Shield-PCBWay-2.png?resize=325%2C627&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"PCBWay Order PCB China post shipping method\" class=\"wp-image-102794\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/03\/Neopixel-PCB-Shield-PCBWay-2.png?w=325&amp;quality=100&amp;strip=all&amp;ssl=1 325w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/03\/Neopixel-PCB-Shield-PCBWay-2.png?resize=156%2C300&amp;quality=100&amp;strip=all&amp;ssl=1 156w\" sizes=\"(max-width: 325px) 100vw, 325px\" \/><\/figure><\/div>\n\n\n<p>You can increase your PCB order quantity and change the solder mask color. As usual, we\u2019ve ordered the Blue color.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"1089\" height=\"588\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/03\/Neopixel-PCB-Shield-PCBWay-3.png?resize=1089%2C588&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 Neopixel PCB Shield PCBWay Details\" class=\"wp-image-102795\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/03\/Neopixel-PCB-Shield-PCBWay-3.png?w=1089&amp;quality=100&amp;strip=all&amp;ssl=1 1089w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/03\/Neopixel-PCB-Shield-PCBWay-3.png?resize=300%2C162&amp;quality=100&amp;strip=all&amp;ssl=1 300w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/03\/Neopixel-PCB-Shield-PCBWay-3.png?resize=1024%2C553&amp;quality=100&amp;strip=all&amp;ssl=1 1024w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/03\/Neopixel-PCB-Shield-PCBWay-3.png?resize=768%2C415&amp;quality=100&amp;strip=all&amp;ssl=1 768w\" sizes=\"(max-width: 1089px) 100vw, 1089px\" \/><\/figure><\/div>\n\n\n<p>Once you\u2019re ready, you can order the PCBs by clicking &#8220;Save to Cart&#8221; and complete your order.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Unboxing the PCBs<\/h2>\n\n\n\n<p>After approximately one week using the DHL shipping method, I received the PCBs at my office.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"750\" height=\"422\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/03\/Status-Indicator-and-Sensor-Shield-Unboxing-PCBWay.jpg?resize=750%2C422&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"Status Indicator and Sensor Shield Unboxing PCBWay\" class=\"wp-image-102781\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/03\/Status-Indicator-and-Sensor-Shield-Unboxing-PCBWay.jpg?w=750&amp;quality=100&amp;strip=all&amp;ssl=1 750w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/03\/Status-Indicator-and-Sensor-Shield-Unboxing-PCBWay.jpg?resize=300%2C169&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 750px) 100vw, 750px\" \/><\/figure><\/div>\n\n\n<p>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.<\/p>\n\n\n\n<p>We\u2019re really satisfied with the PCBWay service. Here are some other projects we\u2019ve built using the PCBWay service:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/randomnerdtutorials.com\/esp32-cam-shield-pcb-telegram\/\">ESP32-CAM with Telegram: Take Photos, Control Outputs, Request Sensor Readings and Motion Notifications<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/randomnerdtutorials.com\/esp32-iot-shield-pcb-dashboard\/\">ESP32 IoT Shield PCB with Dashboard for Outputs and Sensors<\/a><\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Soldering the Components<\/h2>\n\n\n\n<p>In our PCB, we\u2019ve used SMD LEDs, SMD resistors, and SMD capacitors. These can be a bit difficult to solder, but the PCB looks much better. If you\u2019ve never soldered SMD before, we recommend watching a few videos to learn how it&#8217;s done. You can also get an SMD DIY soldering kit to practice a bit.<\/p>\n\n\n\n<p>Here\u2019s a list of all the components needed to assemble the PCB:<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"750\" height=\"422\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/03\/ESP32-Status-Indicator-and-Sensor-Shield-parts.jpg?resize=750%2C422&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 Status Indicator and Sensor Shield parts\" class=\"wp-image-102782\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/03\/ESP32-Status-Indicator-and-Sensor-Shield-parts.jpg?w=750&amp;quality=100&amp;strip=all&amp;ssl=1 750w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/03\/ESP32-Status-Indicator-and-Sensor-Shield-parts.jpg?resize=300%2C169&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 750px) 100vw, 750px\" \/><\/figure><\/div>\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/makeradvisor.com\/tools\/esp32-dev-board-wi-fi-bluetooth\/\" target=\"_blank\" rel=\"noreferrer noopener\">DOIT ESP32 DEVKIT V1 Board<\/a>\u00a0(<strong>36 GPIOs<\/strong>)<\/li>\n\n\n\n<li>10x SMD WS2812B addressable RGB LEDs<\/li>\n\n\n\n<li><a href=\"https:\/\/makeradvisor.com\/tools\/smd-resistors\/\" target=\"_blank\" rel=\"noreferrer noopener\">1x 10k Ohm SMD resistor<\/a>\u00a0(1206)<\/li>\n\n\n\n<li>10x 10nF capacitors (0805)<\/li>\n\n\n\n<li>Pushbutton (0.55 mm)<\/li>\n\n\n\n<li><a href=\"https:\/\/makeradvisor.com\/tools\/female-pin-header-socket\/\" target=\"_blank\" rel=\"noreferrer noopener\">Female pin header socket<\/a>\u00a0(2.54 mm)<\/li>\n\n\n\n<li><a href=\"https:\/\/makeradvisor.com\/tools\/bme280-sensor-module\/\" target=\"_blank\" rel=\"noreferrer noopener\">BME280<\/a>\u00a0(4 pins)<\/li>\n<\/ul>\n\n\n\n<p>Here are the soldering tools we\u2019ve used:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/makeradvisor.com\/ts80-soldering-iron-review\/\" target=\"_blank\" rel=\"noreferrer noopener\">TS80 mini portable soldering iron<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/www.banggood.com\/0_5mm-500g-Soldering-Wires-Welding-Iron-Rosin-Core-6040-Lead-Tin-Flux-2_0-Percent-p-1023387.html?p=MA240439985285201910\" target=\"_blank\" rel=\"noreferrer noopener nofollow\">Solder 60\/40 0.5mm diameter<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/makeradvisor.com\/soldering-mats-review\/\" target=\"_blank\" rel=\"noreferrer noopener\">Soldering mat<\/a><\/li>\n<\/ul>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full is-resized\"><a href=\"https:\/\/makeradvisor.com\/ts80-soldering-iron-review\/\" target=\"_blank\" rel=\"noopener\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"750\" height=\"421\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/06\/TS80-Soldering-Iron-Review-Best-Portable-Soldering-Iron.jpg?resize=750%2C421&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"TS80 mini portable soldering iron\" class=\"wp-image-97549\" style=\"width:750px;height:421px\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/06\/TS80-Soldering-Iron-Review-Best-Portable-Soldering-Iron.jpg?w=750&amp;quality=100&amp;strip=all&amp;ssl=1 750w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/06\/TS80-Soldering-Iron-Review-Best-Portable-Soldering-Iron.jpg?resize=300%2C168&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 750px) 100vw, 750px\" \/><\/a><\/figure><\/div>\n\n\n<p>Start by soldering the SMD components. Then, solder the header pins. And finally, solder the other components or use header pins if you don\u2019t want to connect the components permanently.<\/p>\n\n\n\n<p>Here\u2019s how the ESP32 Shield looks like after assembling all the parts.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"750\" height=\"422\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/03\/ESP32-Status-Indicator-Sensor-PCB-Shield-Soldered.jpg?resize=750%2C422&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 Board PCB Shield Neopixels\" class=\"wp-image-102788\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/03\/ESP32-Status-Indicator-Sensor-PCB-Shield-Soldered.jpg?w=750&amp;quality=100&amp;strip=all&amp;ssl=1 750w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/03\/ESP32-Status-Indicator-Sensor-PCB-Shield-Soldered.jpg?resize=300%2C169&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 750px) 100vw, 750px\" \/><\/figure><\/div>\n\n\n<p>The ESP32 board should stack perfectly on the header pins on the other side of the PCB.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"750\" height=\"422\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/03\/Status-Indicator-PCB-Shield-Stack-ESP32.jpg?resize=750%2C422&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"Status Indicator PCB Shield Stack ESP32\" class=\"wp-image-102787\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/03\/Status-Indicator-PCB-Shield-Stack-ESP32.jpg?w=750&amp;quality=100&amp;strip=all&amp;ssl=1 750w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/03\/Status-Indicator-PCB-Shield-Stack-ESP32.jpg?resize=300%2C169&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 750px) 100vw, 750px\" \/><\/figure><\/div>\n\n\n<hr class=\"wp-block-separator has-css-opacity is-style-wide\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Programming the Shield<\/h2>\n\n\n\n<p>As mentioned previously, we&#8217;ll program the board to have the following features:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Web Server<\/strong> to display BME280 sensor readings (station mode);<\/li>\n<\/ul>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Visual Interface (Addressable RGB LEDs)<\/strong>: the RGB LEDs on the shield behave like two progress bars showing the range of temperature and humidity values;<\/li>\n<\/ul>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Wi-Fi Manager<\/strong>: the ESP32 will automatically join the last saved network or set up an Access Point that you can use to configure the network credentials.<\/li>\n<\/ul>\n\n\n\n<p>The following diagram summarizes how the project works.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"800\" height=\"1063\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/03\/ESP32-Shield-WiFi-Manager-How-it-Works.png?resize=800%2C1063&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 Shield WiFi Manager How it Works\" class=\"wp-image-102654\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/03\/ESP32-Shield-WiFi-Manager-How-it-Works.png?w=800&amp;quality=100&amp;strip=all&amp;ssl=1 800w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/03\/ESP32-Shield-WiFi-Manager-How-it-Works.png?resize=226%2C300&amp;quality=100&amp;strip=all&amp;ssl=1 226w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/03\/ESP32-Shield-WiFi-Manager-How-it-Works.png?resize=771%2C1024&amp;quality=100&amp;strip=all&amp;ssl=1 771w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/03\/ESP32-Shield-WiFi-Manager-How-it-Works.png?resize=768%2C1020&amp;quality=100&amp;strip=all&amp;ssl=1 768w\" sizes=\"(max-width: 800px) 100vw, 800px\" \/><\/figure><\/div>\n\n\n<ul class=\"wp-block-list\">\n<li>When the ESP first starts, it tries to read the <span class=\"rnthl rntliteral\">ssid.txt<\/span>, <span class=\"rnthl rntliteral\">pass.txt<\/span>, and <span class=\"rnthl rntliteral\">ip.txt<\/span> files (<strong>1<\/strong>);<\/li>\n\n\n\n<li>If the files are empty  (<strong>2<\/strong>) (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 (<strong>3<\/strong>);<\/li>\n\n\n\n<li>Using any Wi-Fi enabled device with a browser, you can connect to the newly created Access Point (default name <span class=\"rnthl rntliteral\">ESP-WIFI-MANAGER<\/span>);<\/li>\n\n\n\n<li>After establishing a connection with the <span class=\"rnthl rntliteral\">ESP-WIFI-MANAGER<\/span>, 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 (<strong>4<\/strong>);<\/li>\n\n\n\n<li>The SSID, password, and IP address submitted on the form are saved on the corresponding files: <span class=\"rnthl rntliteral\">ssid.txt<\/span>, <span class=\"rnthl rntliteral\">pass.txt<\/span>, and <span class=\"rnthl rntliteral\">ip.txt<\/span> (<strong>5<\/strong>);<\/li>\n\n\n\n<li>After that, the ESP board restarts (<strong>6<\/strong>);<\/li>\n\n\n\n<li>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\u2019ve inserted on the form (<strong>7<\/strong>);<\/li>\n\n\n\n<li>If it establishes a connection, the process is completed successfully (<strong>8<\/strong>) (all the LEDs are temporarily lit in green\/teal color); <\/li>\n\n\n\n<li>You can access the main web page that shows sensor readings (<strong>9<\/strong>), and the LEDs are light up accordingly to the temperature and humidity range (<strong>10<\/strong>). Otherwise, it will set the Access Point (<strong>3<\/strong>), and you can access the default IP address (192.168.4.1) to add another SSID\/password combination.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Prerequisites<\/h3>\n\n\n\n<p>We\u2019ll program the ESP32 board using Arduino IDE. So make sure you have the ESP32 board add-on installed.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/randomnerdtutorials.com\/installing-the-esp32-board-in-arduino-ide-windows-instructions\/\">Installing ESP32 Board in Arduino IDE (Windows, Mac OS X, and Linux)<\/a><\/li>\n<\/ul>\n\n\n\n<p>If you want to program the ESP32\/ESP8266 using VS Code + PlatformIO, follow the next tutorial:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/randomnerdtutorials.com\/vs-code-platformio-ide-esp32-esp8266-arduino\/\">Getting Started with VS Code and PlatformIO IDE for ESP32 and ESP8266 (Windows, Mac OS X, Linux Ubuntu)<\/a><\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Installing Libraries (Arduino IDE)<\/h3>\n\n\n\n<p>For this project, you need to install all these libraries in your Arduino IDE.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/github.com\/adafruit\/Adafruit_NeoPixel\" target=\"_blank\" rel=\"noreferrer noopener\">Adafruit Neopixel<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/github.com\/adafruit\/Adafruit_BME280_Library\" target=\"_blank\" rel=\"noreferrer noopener\">Adafruit_BME280 library<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/github.com\/adafruit\/Adafruit_Sensor\" target=\"_blank\" rel=\"noreferrer noopener\">Adafruit_Sensor library<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/github.com\/arduino-libraries\/Arduino_JSON\" target=\"_blank\" rel=\"noreferrer noopener\">Arduino_JSON library by Arduino version 0.1.0<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/github.com\/ESP32Async\/ESPAsyncWebServer\" target=\"_blank\" rel=\"noopener\" title=\"\">ESPAsyncWebServer&nbsp;<\/a>by ESP32Async<\/li>\n\n\n\n<li><a href=\"https:\/\/github.com\/ESP32Async\/AsyncTCP\" target=\"_blank\" rel=\"noopener\" title=\"\">AsyncTCP<\/a> by ESP32Async<\/li>\n<\/ul>\n\n\n\n<p>You can install the first four libraries using the Arduino Library Manager. Go to <strong>Sketch <\/strong>> <strong>Include Library<\/strong> > <strong>Manage Libraries<\/strong> and search for the libraries&#8217; names.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Filesystem Uploader<\/h3>\n\n\n\n<p>Before proceeding, you need to have the ESP32 Uploader Plugin installed in your Arduino IDE. Follow the next tutorial before proceeding:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/randomnerdtutorials.com\/install-esp32-filesystem-uploader-arduino-ide\/\">Install ESP32 Filesystem Uploader in Arduino IDE<\/a><\/li>\n<\/ul>\n\n\n\n<p>If you&#8217;re using VS Code with PlatformIO, follow the next tutorial to learn how to upload files to the filesystem:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/randomnerdtutorials.com\/esp32-vs-code-platformio-spiffs\/\">ESP32 with VS Code and PlatformIO: Upload Files to Filesystem (SPIFFS)<\/a><\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Organizing your Files<\/h3>\n\n\n\n<p>To keep the project organized and make it easier to understand, we\u2019ll create five different files to build the web server:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Arduino sketch <\/strong>that handles the web server;<\/li>\n\n\n\n<li><strong>index.html<\/strong>: to define the content of the web page in station mode to display sensor readings;<\/li>\n\n\n\n<li><strong>style.css<\/strong>: to style the web page;<\/li>\n\n\n\n<li><strong>script.js<\/strong>: to program the behavior of the web page\u2014handle web server responses, events, update the time, etc.;<\/li>\n\n\n\n<li><strong>wifimanager.html<\/strong>: to define the web page&#8217;s content to display the Wi-Fi Manager when the ESP32 is in access point mode.<\/li>\n<\/ul>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"613\" height=\"346\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/03\/ESP32-Web-Server-Wi-Fi-Manager-Files.png?resize=613%2C346&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 Web Server Wi-Fi Manager Files\" class=\"wp-image-102666\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/03\/ESP32-Web-Server-Wi-Fi-Manager-Files.png?w=613&amp;quality=100&amp;strip=all&amp;ssl=1 613w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/03\/ESP32-Web-Server-Wi-Fi-Manager-Files.png?resize=300%2C169&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 613px) 100vw, 613px\" \/><\/figure><\/div>\n\n\n<p>You should save the HTML, CSS, and JavaScript files inside a folder called&nbsp;<em><strong>data<\/strong>&nbsp;<\/em>inside the Arduino sketch folder, as shown in the previous diagram. We&#8217;ll upload these files to the ESP32 filesystem (SPIFFS).<\/p>\n\n\n\n<p><strong>You can download all project files:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/github.com\/RuiSantosdotme\/esp32-status-pcb\/raw\/main\/ESP32_WiFi_Manager_Neopixel.zip\" target=\"_blank\" rel=\"noreferrer noopener\">Download All the Arduino Project Files<\/a><\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Creating the HTML Files<\/h3>\n\n\n\n<p>For this project, you need two HTML files. One to build the main page that displays the sensor readings (<span class=\"rnthl rntliteral\">index.html<\/span>) and another to build the Wi-Fi Manager page (<span class=\"rnthl rntliteral\">wifimanager.html<\/span>).<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">index.html<\/h4>\n\n\n\n<p>Copy the following to the <span class=\"rnthl rntliteral\">index.html<\/span> file.<\/p>\n\n\n<pre style=\"max-height: 40em; margin-bottom: 20px;\"><code class=\"language-html\">&lt;!DOCTYPE html&gt;\r\n&lt;html&gt;\r\n  &lt;head&gt;\r\n    &lt;title&gt;ESP IOT DASHBOARD&lt;\/title&gt;\r\n    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1&quot;&gt;\r\n    &lt;link rel=&quot;icon&quot; type=&quot;image\/png&quot; href=&quot;favicon.png&quot;&gt;\r\n      &lt;link rel=&quot;stylesheet&quot; href=&quot;https:\/\/use.fontawesome.com\/releases\/v5.7.2\/css\/all.css&quot; integrity=&quot;sha384-fnmOCqbTlWIlj8LyTjo7mOUStjsKC4pOpQbqyi7RrhN7udi9RwhKkMHpvLbHG9Sr&quot; crossorigin=&quot;anonymous&quot;&gt;\r\n    &lt;link rel=&quot;stylesheet&quot; type=&quot;text\/css&quot; href=&quot;style.css&quot;&gt;\r\n  &lt;\/head&gt;\r\n  &lt;body&gt;\r\n    &lt;div class=&quot;topnav&quot;&gt;\r\n      &lt;h1&gt;ESP WEB SERVER SENSOR READINGS&lt;\/h1&gt;\r\n    &lt;\/div&gt;\r\n    &lt;div class=&quot;content&quot;&gt;\r\n      &lt;div class=&quot;card-grid&quot;&gt;\r\n        &lt;div class=&quot;card&quot;&gt;\r\n          &lt;p class=&quot;card-title&quot;&gt;BME280 Sensor Readings&lt;\/p&gt;\r\n          &lt;p&gt;\r\n            &lt;table&gt;\r\n              &lt;tr&gt;\r\n                &lt;th&gt;READING&lt;\/th&gt;\r\n                &lt;th&gt;VALUE&lt;\/th&gt;\r\n              &lt;\/tr&gt;\r\n              &lt;tr&gt;\r\n                &lt;td&gt;Temperature&lt;\/td&gt;\r\n                &lt;td&gt;&lt;span id=&quot;temp&quot;&gt;&lt;\/span&gt; &amp;deg;C&lt;\/td&gt;\r\n              &lt;\/tr&gt;\r\n              &lt;tr&gt;\r\n                &lt;td&gt;Humidity&lt;\/td&gt;\r\n                &lt;td&gt;&lt;span id=&quot;hum&quot;&gt;&lt;\/span&gt; &amp;percnt;&lt;\/td&gt;\r\n              &lt;\/tr&gt;\r\n              &lt;tr&gt;\r\n                &lt;td&gt;Pressure&lt;\/td&gt;\r\n                &lt;td&gt;&lt;span id=&quot;pres&quot;&gt;&lt;\/span&gt; hPa&lt;\/td&gt;\r\n              &lt;\/tr&gt;\r\n            &lt;\/table&gt;\r\n          &lt;\/p&gt;\r\n          &lt;p class=&quot;update-time&quot;&gt;Last update: &lt;span id=&quot;update-time&quot;&gt;&lt;\/span&gt;&lt;\/p&gt;  \r\n        &lt;\/div&gt;\r\n      &lt;\/div&gt;\r\n    &lt;\/div&gt;\r\n    &lt;script src=&quot;script.js&quot;&gt;&lt;\/script&gt;\r\n  &lt;\/body&gt;\r\n&lt;\/html&gt;<\/code><\/pre>\n\t<p style=\"text-align:center\"><a class=\"rntwhite\" href=\"https:\/\/github.com\/RuiSantosdotme\/esp32-status-pcb\/raw\/main\/ESP32_WiFi_Manager_Neopixel\/data\/index.html\" target=\"_blank\">View raw code<\/a><\/p>\n\n\n\n<p>This file creates a table to display the sensor readings. Here&#8217;s the paragraph that displays the table:<\/p>\n\n\n\n<pre class=\"wp-block-code language-html\"><code>&lt;p&gt;\n  &lt;table&gt;\n    &lt;tr&gt;\n      &lt;th&gt;READING&lt;\/th&gt;\n      &lt;th&gt;VALUE&lt;\/th&gt;\n    &lt;\/tr&gt;\n    &lt;tr&gt;\n      &lt;td&gt;Temperature&lt;\/td&gt;\n      &lt;td&gt;&lt;span id=\"temp\"&gt;&lt;\/span&gt; &amp;deg;C&lt;\/td&gt;\n    &lt;\/tr&gt;\n    &lt;tr&gt;\n       &lt;td&gt;Humidity&lt;\/td&gt;\n       &lt;td&gt;&lt;span id=\"hum\"&gt;&lt;\/span&gt; &amp;percnt;&lt;\/td&gt;\n    &lt;\/tr&gt;\n    &lt;tr&gt;\n      &lt;td&gt;Pressure&lt;\/td&gt;\n      &lt;td&gt;&lt;span id=\"pres\"&gt;&lt;\/span&gt; hPa&lt;\/td&gt;\n    &lt;\/tr&gt;\n  &lt;\/table&gt;\n&lt;\/p&gt;<\/code><\/pre>\n\n\n\n<p>To create a table in HTML, start with the <span class=\"rnthl rntliteral\"><span style=\"color: #333399;\">&lt;table&gt;<\/span><\/span> and <span class=\"rnthl rntliteral\"><span style=\"color: #333399;\">&lt;\/table&gt;<\/span><\/span> tags. This encloses the entire table. To create a row, use the <span class=\"rnthl rntliteral\"><span style=\"color: #333399;\">&lt;tr&gt;<\/span><\/span> and <span class=\"rnthl rntliteral\"><span style=\"color: #333399;\">&lt;\/tr&gt;<\/span><\/span> tags. The table is defined with a series of rows. Use the <span class=\"rnthl rntliteral\"><span style=\"color: #333399;\">&lt;tr&gt;<\/span><\/span><span class=\"rnthl rntliteral\"><span style=\"color: #333399;\">&lt;\/tr&gt;<\/span><\/span> pair to enclose each row of data. The table heading is defined using the <span class=\"rnthl rntliteral\"><span style=\"color: #333399;\">&lt;th&gt;<\/span><\/span> and <span class=\"rnthl rntliteral\"><span style=\"color: #333399;\">&lt;\/th&gt;<\/span><\/span> tags, and each table cell is defined using the <span class=\"rnthl rntliteral\"><span style=\"color: #333399;\">&lt;td&gt;<\/span><\/span> and <span class=\"rnthl rntliteral\"><span style=\"color: #333399;\">&lt;\/td&gt;<\/span><\/span> tags.<\/p>\n\n\n\n<p>Notice that the cells to display the sensor readings have <span class=\"rnthl rntliteral\"><span style=\"color: #333399;\">&lt;span&gt;<\/span><\/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 <span class=\"rnthl rntliteral\">temp<\/span>.<\/p>\n\n\n\n<pre class=\"wp-block-code language-html\"><code>&lt;td&gt;&lt;span id=\"temp\"&gt;&lt;\/span&gt; &amp;deg;C&lt;\/td&gt;<\/code><\/pre>\n\n\n\n<p>Finally, there\u2019s a paragraph to display the last time the readings were updated:<\/p>\n\n\n\n<pre class=\"wp-block-code language-html\"><code>&lt;p class=\"update-time\"&gt;Last update: &lt;span id=\"update-time\"&gt;&lt;\/span&gt;&lt;\/p&gt;<\/code><\/pre>\n\n\n\n<p>There\u2019s a <span class=\"rnthl rntliteral\"><span style=\"color: #333399;\">&lt;span&gt;<\/span><\/span> tag with the <span class=\"rnthl rntliteral\">update-time<\/span> id. This will be used later to insert the date and time using JavaScript.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">wifimanager.html<\/h4>\n\n\n\n<p>Copy the following to the <span class=\"rnthl rntliteral\">wifimanager.html<\/span> file.<\/p>\n\n\n<pre style=\"max-height: 40em; margin-bottom: 20px;\"><code class=\"language-html\">&lt;!DOCTYPE html&gt;\r\n&lt;html&gt;\r\n&lt;head&gt;\r\n  &lt;title&gt;ESP Wi-Fi Manager&lt;\/title&gt;\r\n  &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1&quot;&gt;\r\n  &lt;link rel=&quot;icon&quot; href=&quot;data:,&quot;&gt;\r\n  &lt;link rel=&quot;stylesheet&quot; href=&quot;https:\/\/use.fontawesome.com\/releases\/v5.7.2\/css\/all.css&quot; integrity=&quot;sha384-fnmOCqbTlWIlj8LyTjo7mOUStjsKC4pOpQbqyi7RrhN7udi9RwhKkMHpvLbHG9Sr&quot; crossorigin=&quot;anonymous&quot;&gt;\r\n  &lt;link rel=&quot;stylesheet&quot; type=&quot;text\/css&quot; href=&quot;style.css&quot;&gt;\r\n&lt;\/head&gt;\r\n&lt;body&gt;\r\n  &lt;div class=&quot;topnav&quot;&gt;\r\n    &lt;h1&gt;ESP Wi-Fi Manager&lt;\/h1&gt;\r\n  &lt;\/div&gt;\r\n  &lt;div class=&quot;content&quot;&gt;\r\n    &lt;div class=&quot;card-grid&quot;&gt;\r\n      &lt;div class=&quot;card&quot;&gt;\r\n        &lt;form action=&quot;\/&quot; method=&quot;POST&quot;&gt;\r\n          &lt;p&gt;\r\n            &lt;label for=&quot;ssid&quot;&gt;SSID&lt;\/label&gt;\r\n            &lt;input type=&quot;text&quot; id =&quot;ssid&quot; name=&quot;ssid&quot;&gt;&lt;br&gt;\r\n            &lt;label for=&quot;pass&quot;&gt;Password&lt;\/label&gt;\r\n            &lt;input type=&quot;text&quot; id =&quot;pass&quot; name=&quot;pass&quot;&gt;&lt;br&gt;\r\n            &lt;label for=&quot;ip&quot;&gt;IP Address&lt;\/label&gt;\r\n            &lt;input type=&quot;text&quot; id =&quot;ip&quot; name=&quot;ip&quot; value=&quot;192.168.1.200&quot;&gt;\r\n            &lt;input type =&quot;submit&quot; value =&quot;Submit&quot;&gt;\r\n          &lt;\/p&gt;\r\n        &lt;\/form&gt;\r\n      &lt;\/div&gt;\r\n    &lt;\/div&gt;\r\n  &lt;\/div&gt;\r\n  &lt;script src=&quot;script.js&quot;&gt;&lt;\/script&gt;\r\n&lt;\/body&gt;\r\n&lt;\/html&gt;<\/code><\/pre>\n\t<p style=\"text-align:center\"><a class=\"rntwhite\" href=\"https:\/\/github.com\/RuiSantosdotme\/esp32-status-pcb\/raw\/main\/ESP32_WiFi_Manager_Neopixel\/data\/wifimanager.html\" target=\"_blank\">View raw code<\/a><\/p>\n\n\n\n<p>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\u2014 three input fields and a submit button.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><a href=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/03\/ESP32-Wi-Fi-Manager-Web-Page.png?quality=100&#038;strip=all&#038;ssl=1\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"643\" height=\"409\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/03\/ESP32-Wi-Fi-Manager-Web-Page.png?resize=643%2C409&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 Wi-Fi Manager Web Page\" class=\"wp-image-102617\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/03\/ESP32-Wi-Fi-Manager-Web-Page.png?w=643&amp;quality=100&amp;strip=all&amp;ssl=1 643w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/03\/ESP32-Wi-Fi-Manager-Web-Page.png?resize=300%2C191&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 643px) 100vw, 643px\" \/><\/a><\/figure><\/div>\n\n\n<p>We want to send the submitted values on the input fields to the server when we click on the Submit button.<\/p>\n\n\n\n<p>Here&#8217;s the HTML form with the three input fields:<\/p>\n\n\n\n<pre class=\"wp-block-code language-html\"><code>&lt;form action=\"\/\" method=\"POST\"&gt;\n  &lt;p&gt;\n    &lt;label for=\"ssid\"&gt;SSID&lt;\/label&gt;\n    &lt;input type=\"text\" id =\"ssid\" name=\"ssid\"&gt;&lt;br&gt;\n    &lt;label for=\"pass\"&gt;Password&lt;\/label&gt;\n    &lt;input type=\"text\" id =\"pass\" name=\"pass\"&gt;&lt;br&gt;\n    &lt;label for=\"ip\"&gt;IP Address&lt;\/label&gt;\n    &lt;input type=\"text\" id =\"ip\" name=\"ip\" value=\"192.168.1.200\"&gt;\n    &lt;input type =\"submit\" value =\"Submit\"&gt;\n  &lt;\/p&gt;\n&lt;\/form&gt;<\/code><\/pre>\n\n\n\n<p>This is the input field for the SSID:<\/p>\n\n\n\n<pre class=\"wp-block-code language-html\"><code>&lt;label for=\"ssid\"&gt;SSID&lt;\/label&gt;\n&lt;input type=\"text\" id =\"ssid\" name=\"ssid\"&gt;&lt;br&gt;<\/code><\/pre>\n\n\n\n<p>The input field for the password:<\/p>\n\n\n\n<pre class=\"wp-block-code language-html\"><code>&lt;label for=\"pass\"&gt;Password&lt;\/label&gt;\n&lt;input type=\"text\" id =\"pass\" name=\"pass\"&gt;&lt;br&gt;<\/code><\/pre>\n\n\n\n<p>The HTML form contains different form elements. All the form elements are enclosed inside this <span class=\"rnthl rntliteral\"><span style=\"color: #333399;\">&lt;form&gt;<\/span><\/span> tag. It contains controls (the input fields) and labels for those controls.<\/p>\n\n\n\n<p>Additionally, the <span class=\"rnthl rntliteral\"><span style=\"color: #333399;\">&lt;form&gt;<\/span><\/span> tag must include the <span class=\"rnthl rntliteral\">action<\/span> attribute that specifies what you want to do when the form is submitted (it redirects to the <span class=\"rnthl rntliteral\">\/<\/span> 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 <strong>Submit<\/strong> button. The <span class=\"rnthl rntliteral\">method<\/span> attribute specifies the HTTP method (GET or POST) used when submitting the form data. In this case, we\u2019ll use HTTP POST method.<\/p>\n\n\n\n<pre class=\"wp-block-code language-html\"><code>&lt;form action=\"\/\" method=\"POST\"&gt;<\/code><\/pre>\n\n\n\n<p>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:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>POST \/\nHost: localhost\nssid: YOUR-NETWORK-SSID\npass: YOUR-PASSWORD\nip: IP-ADDRESS<\/code><\/pre>\n\n\n\n<p>The <span class=\"rnthl rntliteral\"><span style=\"color: #333399;\">&lt;input&nbsp;type=&#8221;submit&#8221;&nbsp;value=&#8221;Submit&#8221;&gt;<\/span><\/span> creates a submit button with the text \u201c<strong>Submit<\/strong>\u201d. When you click this button, the data submitted in the form is sent to the server.<\/p>\n\n\n\n<pre class=\"wp-block-code language-html\"><code>&lt;input type =\"submit\" value =\"Submit\"&gt;<\/code><\/pre>\n\n\n\n<p>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 <span class=\"rnthl rntliteral\">value<\/span> parameter\u2014it won\u2019t have a default value, the network automatically assigns a valid IP address to your board.<\/p>\n\n\n\n<pre class=\"wp-block-code language-html\"><code>&lt;label for=\"ip\"&gt;IP Address&lt;\/label&gt;\n&lt;input type=\"text\" id =\"ip\" name=\"ip\" value=\"192.168.1.200\"&gt;<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Creating the CSS File<\/h3>\n\n\n\n<p>Copy the following styles to your <span class=\"rnthl rntliteral\">style.css<\/span> file.<\/p>\n\n\n<pre style=\"max-height: 40em; margin-bottom: 20px;\"><code class=\"language-css\">html {\r\n  font-family: Arial, Helvetica, sans-serif; \r\n  display: inline-block; \r\n  text-align: center;\r\n}\r\nh1 {\r\n  font-size: 1.8rem; \r\n  color: white;\r\n}\r\np { \r\n  font-size: 1.4rem;\r\n}\r\n.topnav { \r\n  overflow: hidden; \r\n  background-color: #0A1128;\r\n}\r\nbody {  \r\n  margin: 0;\r\n}\r\n.content { \r\n  padding: 50px;\r\n}\r\n.card-grid { \r\n  max-width: 800px; \r\n  margin: 0 auto; \r\n  display: grid; \r\n  grid-gap: 2rem; \r\n  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));\r\n}\r\n.card { \r\n  background-color: white; \r\n  box-shadow: 2px 2px 12px 1px rgba(140,140,140,.5);\r\n}\r\n.card-title { \r\n  font-size: 1.2rem;\r\n  font-weight: bold;\r\n  color: #034078\r\n}\r\nth, td {\r\n  text-align: center;\r\n  padding: 8px;\r\n}\r\ntr:nth-child(even) {\r\n  background-color: #f2f2f2\r\n}\r\ntr:hover {\r\n  background-color: #ddd;\r\n}  \r\nth {\r\n  background-color: #50b8b4;\r\n  color: white;\r\n}\r\ntable {\r\n  margin: 0 auto;\r\n  width: 90%\r\n}\r\n.update-time {\r\n  font-size: 0.8rem;\r\n  color:#1282A2;\r\n}<\/code><\/pre>\n\t<p style=\"text-align:center\"><a class=\"rntwhite\" href=\"https:\/\/github.com\/RuiSantosdotme\/esp32-status-pcb\/raw\/main\/ESP32_WiFi_Manager_Neopixel\/data\/style.css\" target=\"_blank\">View raw code<\/a><\/p>\n\n\n\n<p>This file styles the previous web pages.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Creating the JavaScript File<\/h3>\n\n\n\n<p>Copy the following to the <span class=\"rnthl rntliteral\">script.js<\/span> file.<\/p>\n\n\n<pre style=\"max-height: 40em; margin-bottom: 20px;\"><code class=\"language-javascript\">\/\/ Get current sensor readings when the page loads  \r\nwindow.addEventListener('load', getReadings);\r\n\r\n\/\/Function to add date and time of last update\r\nfunction updateDateTime() {\r\n  var currentdate = new Date(); \r\n  var datetime =  currentdate.getDate() + &quot;\/&quot;\r\n  + (currentdate.getMonth()+1)  + &quot;\/&quot; \r\n  + currentdate.getFullYear() + &quot; at &quot;  \r\n  + currentdate.getHours() + &quot;:&quot;  \r\n  + currentdate.getMinutes() + &quot;:&quot; \r\n  + currentdate.getSeconds();\r\n  document.getElementById(&quot;update-time&quot;).innerHTML = datetime;\r\n  console.log(datetime);\r\n}\r\n\r\n\/\/ Function to get current readings on the webpage when it loads for the first time\r\nfunction getReadings() {\r\n  var xhr = new XMLHttpRequest();\r\n  xhr.onreadystatechange = function() {\r\n    if (this.readyState == 4 &amp;&amp; this.status == 200) {\r\n      var myObj = JSON.parse(this.responseText);\r\n      console.log(myObj);\r\n      document.getElementById(&quot;temp&quot;).innerHTML = myObj.temperature;\r\n      document.getElementById(&quot;hum&quot;).innerHTML = myObj.humidity;\r\n      document.getElementById(&quot;pres&quot;).innerHTML = myObj.pressure;\r\n      updateDateTime();\r\n    }\r\n  };\r\n  xhr.open(&quot;GET&quot;, &quot;\/readings&quot;, true);\r\n  xhr.send();\r\n}\r\n\r\n\/\/ Create an Event Source to listen for events\r\nif (!!window.EventSource) {\r\n  var source = new EventSource('\/events');\r\n\r\n  source.addEventListener('open', function(e) {\r\n    console.log(&quot;Events Connected&quot;);\r\n  }, false);\r\n\r\n  source.addEventListener('error', function(e) {\r\n    if (e.target.readyState != EventSource.OPEN) {\r\n      console.log(&quot;Events Disconnected&quot;);\r\n    }\r\n  }, false);\r\n\r\n  source.addEventListener('new_readings', function(e) {\r\n    console.log(&quot;new_readings&quot;, e.data);\r\n    var obj = JSON.parse(e.data);\r\n    document.getElementById(&quot;temp&quot;).innerHTML = obj.temperature;\r\n    document.getElementById(&quot;hum&quot;).innerHTML = obj.humidity;\r\n    document.getElementById(&quot;pres&quot;).innerHTML = obj.pressure;\r\n    updateDateTime();\r\n  }, false);\r\n}<\/code><\/pre>\n\t<p style=\"text-align:center\"><a class=\"rntwhite\" href=\"https:\/\/github.com\/RuiSantosdotme\/esp32-status-pcb\/raw\/main\/ESP32_WiFi_Manager_Neopixel\/data\/script.js\" target=\"_blank\">View raw code<\/a><\/p>\n\n\n\n<p>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.<\/p>\n\n\n\n<p>Let&#8217;s take a click look at the JavaScript file and see how it works.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Get Readings<\/h4>\n\n\n\n<p>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.<\/p>\n\n\n\n<p>Add an event listener that calls the <span class=\"rnthl rntliteral\">getReadings<\/span> function when the web page loads.<\/p>\n\n\n\n<pre class=\"wp-block-code language-javascript\"><code>window.addEventListener('load', getReadings);<\/code><\/pre>\n\n\n\n<p>The <span class=\"rnthl rntliteral\">window<\/span> object represents an open window in a browser. The <span class=\"rnthl rntliteral\">addEventListener()<\/span> method sets up a function to be called when a certain event happens. In this case, we\u2019ll call the <span class=\"rnthl rntliteral\">getReadings<\/span> function when the pages loads (<span class=\"rnthl rntliteral\">&#8216;load&#8217;<\/span>) to get the current sensor readings.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">updateDateTime() function<\/h4>\n\n\n\n<p>The <span class=\"rnthl rntliteral\">updateDateTime()<\/span> function gets the current date and time and places it in the HTML element with the <span class=\"rnthl rntliteral\">update-time<\/span> id.<\/p>\n\n\n\n<pre class=\"wp-block-code language-javascript\"><code>function updateDateTime() {\n  var currentdate = new Date(); \n  var datetime =  currentdate.getDate() + \"\/\"\n  + (currentdate.getMonth()+1)  + \"\/\" \n  + currentdate.getFullYear() + \" at \"  \n  + currentdate.getHours() + \":\"  \n  + currentdate.getMinutes() + \":\" \n  + currentdate.getSeconds();\n  document.getElementById(\"update-time\").innerHTML = datetime;\n  console.log(datetime);\n}<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">getReadings() function<\/h4>\n\n\n\n<p>Now, let\u2019s take a look at the <span class=\"rnthl rntliteral\">getReadings<\/span> function. It sends a GET request to the server on the <span class=\"rnthl rntliteral\">\/readings<\/span> URL and handles the response\u2014a JSON string containing the sensor readings. It also places the temperature, humidity and pressure values on the HTML elements with the corresponding ids.<\/p>\n\n\n\n<pre class=\"wp-block-code language-javascript\"><code>function getReadings() {\n  var xhr = new XMLHttpRequest();\n  xhr.onreadystatechange = function() {\n    if (this.readyState == 4 &amp;&amp; this.status == 200) {\n      var myObj = JSON.parse(this.responseText);\n      console.log(myObj);\n      document.getElementById(\"temp\").innerHTML = myObj.temperature;\n      document.getElementById(\"hum\").innerHTML = myObj.humidity;\n      document.getElementById(\"pres\").innerHTML = myObj.pressure;\n      updateDateTime();\n    }\n  };\n  xhr.open(\"GET\", \"\/readings\", true);\n  xhr.send();\n}<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">Handle Events<\/h4>\n\n\n\n<p>Now, we need to handle the events sent by the server (Server-Sent Events).<\/p>\n\n\n\n<p>Create a new&nbsp;<span class=\"rnthl rntliteral\">EventSource<\/span>&nbsp;object and specify the URL of the page sending the updates. In our case, it\u2019s&nbsp;<span class=\"rnthl rntliteral\">\/events<\/span>.<\/p>\n\n\n\n<pre class=\"wp-block-code language-javascript\"><code>if (!!window.EventSource) {\n  var source = new EventSource('\/events');<\/code><\/pre>\n\n\n\n<p>Once you\u2019ve instantiated an event source, you can start listening for messages from the server with&nbsp;<span class=\"rnthl rntliteral\">addEventListener()<\/span>.<\/p>\n\n\n\n<p>These are the default event listeners, as shown in the AsyncWebServer&nbsp;<a href=\"https:\/\/github.com\/me-no-dev\/ESPAsyncWebServer#setup-event-source-in-the-browser\" target=\"_blank\" rel=\"noreferrer noopener\">documentation<\/a>.<\/p>\n\n\n\n<pre class=\"wp-block-code language-javascript\"><code>source.addEventListener('open', function(e) {\n  console.log(\"Events Connected\");\n}, false);\n\nsource.addEventListener('error', function(e) {\n  if (e.target.readyState != EventSource.OPEN) {\n    console.log(\"Events Disconnected\");\n  }\n}, false);<\/code><\/pre>\n\n\n\n<p>Then, add an event listener for the <span class=\"rnthl rntliteral\">&#8216;new_readings&#8217;<\/span> event.<\/p>\n\n\n\n<pre class=\"wp-block-code language-javascript\"><code>source.addEventListener('new_readings', function(e) {\n  console.log(\"new_readings\", e.data);\n  var obj = JSON.parse(e.data);\n  document.getElementById(\"temp\").innerHTML = obj.temperature;\n  document.getElementById(\"hum\").innerHTML = obj.humidity;\n  document.getElementById(\"pres\").innerHTML = obj.pressure;\n  updateDateTime();\n}, false);<\/code><\/pre>\n\n\n\n<p>When new readings are available, the ESP sends an event (<span class=\"rnthl rntliteral\">&#8216;new_readings&#8217;<\/span>) to the client with a JSON string that contains the sensor readings.<\/p>\n\n\n\n<p>The following line prints the content of the message on the console:<\/p>\n\n\n\n<pre class=\"wp-block-code language-javascript\"><code>console.log(\"new_readings\", e.data);<\/code><\/pre>\n\n\n\n<p>Then, convert the data into a JSON object with the <span class=\"rnthl rntliteral\">parse()<\/span> method and save it in the <span class=\"rnthl rntliteral\">obj<\/span> variable.<\/p>\n\n\n\n<pre class=\"wp-block-code language-javascript\"><code>var obj = JSON.parse(e.data);<\/code><\/pre>\n\n\n\n<p>The JSON string comes in the following format:<\/p>\n\n\n\n<pre class=\"wp-block-code language-javascript\"><code>{\n  \"temperature\" : \"25\",\n  \"humidity\" : \"50\",\n  \"pressure\" : \"1015\"\n}<\/code><\/pre>\n\n\n\n<p>You can get the temperature with <span class=\"rnthl rntliteral\">obj.temperature<\/span>, the humidity with <span class=\"rnthl rntliteral\">obj.humidity<\/span> and the pressure with <span class=\"rnthl rntliteral\">obj.pressure<\/span>.<\/p>\n\n\n\n<p>The following lines put the received data into the elements with the corresponding ids (<span class=\"rnthl rntliteral\">temp<\/span>,<span class=\"rnthl rntliteral\">hum<\/span> and <span class=\"rnthl rntliteral\">pres<\/span>) on the web page.<\/p>\n\n\n\n<pre class=\"wp-block-code language-javascript\"><code>document.getElementById(\"temp\").innerHTML = obj.temperature;\ndocument.getElementById(\"hum\").innerHTML = obj.humidity;\ndocument.getElementById(\"pres\").innerHTML = obj.pressure;<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Arduino Sketch<\/h3>\n\n\n\n<p>Copy the following code to your Arduino IDE or to the <span class=\"rnthl rntliteral\">main.cpp<\/span> file if your using PlatformIO.<\/p>\n\n\n<pre style=\"max-height: 40em; margin-bottom: 20px;\"><code class=\"language-c\">\/*********\r\n  Rui Santos\r\n  Complete instructions at https:\/\/RandomNerdTutorials.com\/esp32-status-indicator-sensor-pcb\/\r\n  \r\n  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files.\r\n  The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\r\n*********\/\r\n\r\n#include &lt;Arduino.h&gt;\r\n#include &lt;Adafruit_NeoPixel.h&gt;\r\n#include &lt;WiFi.h&gt;\r\n#include &lt;AsyncTCP.h&gt;\r\n#include &lt;ESPAsyncWebServer.h&gt;\r\n#include &quot;SPIFFS.h&quot;\r\n#include &lt;Arduino_JSON.h&gt;\r\n#include &lt;Adafruit_BME280.h&gt;\r\n#include &lt;Adafruit_Sensor.h&gt;\r\n\r\n\/\/ Create AsyncWebServer object on port 80\r\nAsyncWebServer server(80);\r\n\r\n\/\/ Create an Event Source on \/events\r\nAsyncEventSource events(&quot;\/events&quot;);\r\n\r\n\/\/ Search for parameter in HTTP POST request\r\nconst char* PARAM_INPUT_1 = &quot;ssid&quot;;\r\nconst char* PARAM_INPUT_2 = &quot;pass&quot;;\r\nconst char* PARAM_INPUT_3 = &quot;ip&quot;;\r\n\r\n\/\/Variables to save values from HTML form\r\nString ssid;\r\nString pass;\r\nString ip;\r\n\r\n\/\/ File paths to save input values permanently\r\nconst char* ssidPath = &quot;\/ssid.txt&quot;;\r\nconst char* passPath = &quot;\/pass.txt&quot;;\r\nconst char* ipPath = &quot;\/ip.txt&quot;;\r\n\r\nIPAddress localIP;\r\n\/\/IPAddress localIP(192, 168, 1, 200); \/\/ hardcoded\r\n\r\n\/\/ Set your Gateway IP address\r\nIPAddress gateway(192, 168, 1, 1);\r\nIPAddress subnet(255, 255, 0, 0);\r\n\r\n\/\/ Timer variables (check wifi)\r\nunsigned long previousMillis = 0;\r\nconst long interval = 10000;  \/\/ interval to wait for Wi-Fi connection (milliseconds)\r\n\r\n\/\/ WS2812B Addressable RGB LEDs\r\n#define STRIP_1_PIN    27  \/\/ GPIO the LEDs are connected to\r\n#define STRIP_2_PIN    32  \/\/ GPIO the LEDs are connected to\r\n#define LED_COUNT  5  \/\/ Number of LEDs\r\n#define BRIGHTNESS 50  \/\/ NeoPixel brightness, 0 (min) to 255 (max)\r\nAdafruit_NeoPixel strip1(LED_COUNT, STRIP_1_PIN, NEO_GRB + NEO_KHZ800);\r\nAdafruit_NeoPixel strip2(LED_COUNT, STRIP_2_PIN, NEO_GRB + NEO_KHZ800);\r\n\r\n\/\/ Create a sensor object\r\nAdafruit_BME280 bme;         \/\/ BME280 connect to ESP32 I2C (GPIO 21 = SDA, GPIO 22 = SCL)\r\n\r\n\/\/Variables to hold sensor readings\r\nfloat temp;\r\nfloat hum;\r\nfloat pres;\r\n\r\n\/\/ Json Variable to Hold Sensor Readings\r\nJSONVar readings;\r\n\r\n\/\/ Timer variables (get sensor readings)\r\nunsigned long lastTime = 0;\r\nunsigned long timerDelay = 30000;\r\n\r\n\/\/-----------------FUNCTIONS TO HANDLE SENSOR READINGS-----------------\/\/\r\n\r\n\/\/ Init BME280\r\nvoid initBME(){\r\n  if (!bme.begin(0x76)) {\r\n    Serial.println(&quot;Could not find a valid BME280 sensor, check wiring!&quot;);\r\n    while (1);\r\n  }\r\n}\r\n\r\n\/\/ Get Sensor Readings\r\nvoid getSensorReadings(){\r\n  temp = bme.readTemperature();\r\n  hum = bme.readHumidity();\r\n  pres= bme.readPressure()\/100.0F;\r\n}\r\n\r\n\/\/ Return JSON String from sensor Readings\r\nString getJSONReadings(){\r\n  readings[&quot;temperature&quot;] = String(temp);\r\n  readings[&quot;humidity&quot;] =  String(hum);\r\n  readings[&quot;pressure&quot;] = String(pres);\r\n  String jsonString = JSON.stringify(readings);\r\n  return jsonString;\r\n}\r\n\r\n\/\/Update RGB LED colors accordingly to temp and hum values\r\nvoid updateColors(){\r\n  strip1.clear();\r\n  strip2.clear();\r\n\r\n  \/\/Number of lit LEDs (temperature)\r\n  int tempLEDs;\r\n  if (temp&lt;=0){\r\n    tempLEDs = 1;\r\n  }\r\n  else if (temp&gt;0 &amp;&amp; temp&lt;=10){\r\n    tempLEDs = 2;\r\n  }\r\n  else if (temp&gt;10 &amp;&amp; temp&lt;=20){\r\n    tempLEDs = 3;\r\n  }\r\n  else if (temp&gt;20 &amp;&amp; temp&lt;=30){\r\n    tempLEDs = 4;\r\n  }\r\n  else{\r\n    tempLEDs = 5;\r\n  }\r\n\r\n  \/\/Turn on LEDs for temperature\r\n  for(int i=0; i&lt;tempLEDs; i++) {\r\n    strip1.setPixelColor(i, strip1.Color(255, 165, 0));\r\n    strip1.show();   \r\n  }\r\n  \r\n  \/\/Number of lit LEDs (humidity)\r\n  int humLEDs = map(hum, 0, 100, 1, LED_COUNT);\r\n\r\n  \/\/Turn on LEDs for humidity\r\n  for(int i=0; i&lt;humLEDs; i++) { \/\/ For each pixel...\r\n    strip2.setPixelColor(i, strip2.Color(25, 140, 200));\r\n    strip2.show();\r\n  }\r\n}\r\n\r\n\/\/-----------------FUNCTIONS TO HANDLE SPIFFS AND FILES-----------------\/\/\r\n\r\n\/\/ Initialize SPIFFS\r\nvoid initSPIFFS() {\r\n  if (!SPIFFS.begin(true)) {\r\n    Serial.println(&quot;An error has occurred while mounting SPIFFS&quot;);\r\n  }\r\n  else{\r\n    Serial.println(&quot;SPIFFS mounted successfully&quot;);\r\n  }\r\n}\r\n\r\n\/\/ Read File from SPIFFS\r\nString readFile(fs::FS &amp;fs, const char * path){\r\n  Serial.printf(&quot;Reading file: %s\\r\\n&quot;, path);\r\n\r\n  File file = fs.open(path);\r\n  if(!file || file.isDirectory()){\r\n    Serial.println(&quot;- failed to open file for reading&quot;);\r\n    return String();\r\n  }\r\n  \r\n  String fileContent;\r\n  while(file.available()){\r\n    fileContent = file.readStringUntil('\\n');\r\n    break;     \r\n  }\r\n  return fileContent;\r\n}\r\n\r\n\/\/ Write file to SPIFFS\r\nvoid writeFile(fs::FS &amp;fs, const char * path, const char * message){\r\n  Serial.printf(&quot;Writing file: %s\\r\\n&quot;, path);\r\n\r\n  File file = fs.open(path, FILE_WRITE);\r\n  if(!file){\r\n    Serial.println(&quot;- failed to open file for writing&quot;);\r\n    return;\r\n  }\r\n  if(file.print(message)){\r\n    Serial.println(&quot;- file written&quot;);\r\n  } else {\r\n    Serial.println(&quot;- frite failed&quot;);\r\n  }\r\n}\r\n\r\n\/\/ Initialize WiFi\r\nbool initWiFi() {\r\n  if(ssid==&quot;&quot; || ip==&quot;&quot;){\r\n    Serial.println(&quot;Undefined SSID or IP address.&quot;);\r\n    return false;\r\n  }\r\n\r\n  WiFi.mode(WIFI_STA);\r\n  localIP.fromString(ip.c_str());\r\n\r\n  if (!WiFi.config(localIP, gateway, subnet)){\r\n    Serial.println(&quot;STA Failed to configure&quot;);\r\n    return false;\r\n  }\r\n  WiFi.begin(ssid.c_str(), pass.c_str());\r\n  Serial.println(&quot;Connecting to WiFi...&quot;);\r\n\r\n  unsigned long currentMillis = millis();\r\n  previousMillis = currentMillis;\r\n\r\n  while(WiFi.status() != WL_CONNECTED) {\r\n    currentMillis = millis();\r\n    if (currentMillis - previousMillis &gt;= interval) {\r\n      Serial.println(&quot;Failed to connect.&quot;);\r\n      return false;\r\n    }\r\n  }\r\n\r\n  Serial.println(WiFi.localIP());\r\n  return true;\r\n}\r\n\r\nvoid setup() {\r\n  \/\/ Serial port for debugging purposes\r\n  Serial.begin(115200);\r\n  \r\n  \/\/ Initialize strips\r\n  strip1.begin();\r\n  strip2.begin();\r\n  \r\n  \/\/ Set brightness \r\n  strip1.setBrightness(BRIGHTNESS);\r\n  strip2.setBrightness(BRIGHTNESS);\r\n  \r\n  \/\/ Init BME280 senspr\r\n  initBME();\r\n  \r\n  \/\/ Init SPIFFS\r\n  initSPIFFS();\r\n\r\n  \/\/ Load values saved in SPIFFS\r\n  ssid = readFile(SPIFFS, ssidPath);\r\n  pass = readFile(SPIFFS, passPath);\r\n  ip = readFile(SPIFFS, ipPath);\r\n  \/*Serial.println(ssid);\r\n  Serial.println(pass);\r\n  Serial.println(ip);*\/\r\n\r\n  if(initWiFi()) {\r\n    \/\/ If ESP32 inits successfully in station mode light up all pixels in a teal color\r\n    for(int i=0; i&lt;LED_COUNT; i++) { \/\/ For each pixel...\r\n      strip1.setPixelColor(i, strip1.Color(0, 255, 128));\r\n      strip2.setPixelColor(i, strip2.Color(0, 255, 128));\r\n\r\n      strip1.show();   \/\/ Send the updated pixel colors to the hardware.\r\n      strip2.show();   \/\/ Send the updated pixel colors to the hardware.\r\n    }\r\n\r\n    \/\/Handle the Web Server in Station Mode\r\n    \/\/ Route for root \/ web page\r\n    server.on(&quot;\/&quot;, HTTP_GET, [](AsyncWebServerRequest *request) {\r\n      request-&gt;send(SPIFFS, &quot;\/index.html&quot;, &quot;text\/html&quot;);\r\n    });\r\n    server.serveStatic(&quot;\/&quot;, SPIFFS, &quot;\/&quot;);\r\n\r\n    \/\/ Request for the latest sensor readings\r\n    server.on(&quot;\/readings&quot;, HTTP_GET, [](AsyncWebServerRequest *request){\r\n      getSensorReadings();\r\n      String json = getJSONReadings();\r\n      request-&gt;send(200, &quot;application\/json&quot;, json);\r\n      json = String();\r\n    });\r\n\r\n    events.onConnect([](AsyncEventSourceClient *client){\r\n      if(client-&gt;lastId()){\r\n        Serial.printf(&quot;Client reconnected! Last message ID that it got is: %u\\n&quot;, client-&gt;lastId());\r\n      }\r\n    });\r\n    server.addHandler(&amp;events);\r\n    \r\n    server.begin();\r\n  }\r\n  else {\r\n    \/\/ else initialize the ESP32 in Access Point mode\r\n    \/\/ light up all pixels in a red color\r\n    for(int i=0; i&lt;LED_COUNT; i++) { \/\/ For each pixel...\r\n      strip1.setPixelColor(i, strip1.Color(255, 0, 0));\r\n      strip2.setPixelColor(i, strip2.Color(255, 0, 0));\r\n      \/\/strip1.setPixelColor(i, strip1.Color(128, 0, 21));\r\n      \/\/strip2.setPixelColor(i, strip2.Color(128, 0, 21));\r\n\r\n      strip1.show();   \/\/ Send the updated pixel colors to the hardware.\r\n      strip2.show();   \/\/ Send the updated pixel colors to the hardware.\r\n    }\r\n\r\n    \/\/ Set Access Point\r\n    Serial.println(&quot;Setting AP (Access Point)&quot;);\r\n    \/\/ NULL sets an open Access Point\r\n    WiFi.softAP(&quot;ESP-WIFI-MANAGER&quot;, NULL);\r\n\r\n    IPAddress IP = WiFi.softAPIP();\r\n    Serial.print(&quot;AP IP address: &quot;);\r\n    Serial.println(IP); \r\n\r\n    \/\/ Web Server Root URL For WiFi Manager Web Page\r\n    server.on(&quot;\/&quot;, HTTP_GET, [](AsyncWebServerRequest *request){\r\n      request-&gt;send(SPIFFS, &quot;\/wifimanager.html&quot;, &quot;text\/html&quot;);\r\n    });\r\n    \r\n    server.serveStatic(&quot;\/&quot;, SPIFFS, &quot;\/&quot;);\r\n    \r\n    \/\/ Get the parameters submited on the form \r\n    server.on(&quot;\/&quot;, HTTP_POST, [](AsyncWebServerRequest *request) {\r\n      int params = request-&gt;params();\r\n      for(int i=0;i&lt;params;i++){\r\n        AsyncWebParameter* p = request-&gt;getParam(i);\r\n        if(p-&gt;isPost()){\r\n          \/\/ HTTP POST ssid value\r\n          if (p-&gt;name() == PARAM_INPUT_1) {\r\n            ssid = p-&gt;value().c_str();\r\n            Serial.print(&quot;SSID set to: &quot;);\r\n            Serial.println(ssid);\r\n            \/\/ Write file to save value\r\n            writeFile(SPIFFS, ssidPath, ssid.c_str());\r\n          }\r\n          \/\/ HTTP POST pass value\r\n          if (p-&gt;name() == PARAM_INPUT_2) {\r\n            pass = p-&gt;value().c_str();\r\n            Serial.print(&quot;Password set to: &quot;);\r\n            Serial.println(pass);\r\n            \/\/ Write file to save value\r\n            writeFile(SPIFFS, passPath, pass.c_str());\r\n          }\r\n          \/\/ HTTP POST ip value\r\n          if (p-&gt;name() == PARAM_INPUT_3) {\r\n            ip = p-&gt;value().c_str();\r\n            Serial.print(&quot;IP Address set to: &quot;);\r\n            Serial.println(ip);\r\n            \/\/ Write file to save value\r\n            writeFile(SPIFFS, ipPath, ip.c_str());\r\n          }\r\n          \/\/Serial.printf(&quot;POST[%s]: %s\\n&quot;, p-&gt;name().c_str(), p-&gt;value().c_str());\r\n        }\r\n      }\r\n      request-&gt;send(200, &quot;text\/plain&quot;, &quot;Done. ESP will restart, connect to your router and go to IP address: &quot; + ip);\r\n      delay(3000);\r\n      \/\/ After saving the parameters, restart the ESP32\r\n      ESP.restart();\r\n    });\r\n    server.begin();\r\n  }\r\n}\r\n\r\nvoid loop() {\r\n  \/\/ If the ESP32 is set successfully in station mode...\r\n  if (WiFi.status() == WL_CONNECTED) {\r\n\r\n    \/\/...Send Events to the client with sensor readins and update colors every 30 seconds\r\n    if (millis() - lastTime &gt; timerDelay) {\r\n      getSensorReadings();\r\n      updateColors();\r\n      \r\n      String message = getJSONReadings();\r\n      events.send(message.c_str(),&quot;new_readings&quot; ,millis());\r\n      lastTime = millis();\r\n    }\r\n  }\r\n}\r\n<\/code><\/pre>\n\t<p style=\"text-align:center\"><a class=\"rntwhite\" href=\"https:\/\/github.com\/RuiSantosdotme\/esp32-status-pcb\/raw\/main\/ESP32_WiFi_Manager_Neopixel\/ESP32_WiFi_Manager_Neopixel.ino\" target=\"_blank\">View raw code<\/a><\/p>\n\n\n\n<h3 class=\"wp-block-heading\">How the Code Works<\/h3>\n\n\n\n<p>Let\u2019s take a look at the code and see how it works.<\/p>\n\n\n\n<p>First, include all the necessary libraries:<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>#include &lt;Arduino.h&gt;\n#include &lt;Adafruit_NeoPixel.h&gt;\n#include &lt;WiFi.h&gt;\n#include &lt;AsyncTCP.h&gt;\n#include &lt;ESPAsyncWebServer.h&gt;\n#include \"SPIFFS.h\"\n#include &lt;Arduino_JSON.h&gt;\n#include &lt;Adafruit_BME280.h&gt;<\/code><\/pre>\n\n\n\n<p>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.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>\/\/ Search for parameter in HTTP POST request\nconst char* PARAM_INPUT_1 = \"ssid\";\nconst char* PARAM_INPUT_2 = \"pass\";\nconst char* PARAM_INPUT_3 = \"ip\";<\/code><\/pre>\n\n\n\n<p>The <span class=\"rnthl rntliteral\">ssid<\/span>, <span class=\"rnthl rntliteral\">pass,<\/span> and <span class=\"rnthl rntliteral\">ip<\/span> variables save the values of the SSID, password, and IP address submitted on the form.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>\/\/ Variables to save values from HTML form\nString ssid;\nString pass;\nString ip;<\/code><\/pre>\n\n\n\n<p>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.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>\/\/ File paths to save input values permanently\nconst char* ssidPath = \"\/ssid.txt\";\nconst char* passPath = \"\/pass.txt\";\nconst char* ipPath = \"\/ip.txt\";<\/code><\/pre>\n\n\n\n<p>The station IP address is submitted on the Wi-Fi Manager form. However, you need to set the gateway and subnet in your code:<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>IPAddress localIP;\n\/\/IPAddress localIP(192, 168, 1, 200); \/\/ hardcoded\n\n\/\/ Set your Gateway IP address\nIPAddress gateway(192, 168, 1, 1);\nIPAddress subnet(255, 255, 0, 0);<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">WS2812B Settings<\/h4>\n\n\n\n<p>Define the pins that control the RGB LEDs. In this case, we have two individual rows connected to GPIOs 27 and 32.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>#define STRIP_1_PIN    27  \/\/ GPIO the LEDs are connected to\n#define STRIP_2_PIN    32  \/\/ GPIO the LEDs are connected to<\/code><\/pre>\n\n\n\n<p>Define the number of LEDs and the brightness. You can change the brightness if you want.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>#define LED_COUNT  5  \/\/ Number of LEDs\n#define BRIGHTNESS 20  \/\/ NeoPixel brightness, 0 (min) to 255 (max)<\/code><\/pre>\n\n\n\n<p>Finally, initialize two <span class=\"rnthl rntliteral\">Adafruit_Neopixel<\/span> objects to control each strip: <span class=\"rnthl rntliteral\">strip1<\/span> (temperature) and <span class=\"rnthl rntliteral\">strip2<\/span> (humidity):<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>Adafruit_NeoPixel strip1(LED_COUNT, STRIP_1_PIN, NEO_GRB + NEO_KHZ800);\nAdafruit_NeoPixel strip2(LED_COUNT, STRIP_2_PIN, NEO_GRB + NEO_KHZ800);<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">initBME()<\/h4>\n\n\n\n<p>The <span class=\"rnthl rntliteral\">initBME()<\/span> function initializes the BME280 sensor on the ESP32 default I2C pins:<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>void initBME(){\n  if (!bme.begin(0x76)) {\n    Serial.println(\"Could not find a valid BME280 sensor, check wiring!\");\n    while (1);\n  }\n}<\/code><\/pre>\n\n\n\n<p class=\"rntbox rntclblue\">Learn more about the BME280 sensor: <a href=\"https:\/\/randomnerdtutorials.com\/esp32-bme280-arduino-ide-pressure-temperature-humidity\/\">ESP32 with BME280 Sensor using Arduino IDE (Pressure, Temperature, Humidity)<\/a>.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">getSensorReadings()<\/h4>\n\n\n\n<p>The <span class=\"rnthl rntliteral\">getSensorReadings()<\/span> functions gets temperature, humidity, and pressure from the BME280 sensor and saves the values on the <span class=\"rnthl rntliteral\">temp<\/span>, <span class=\"rnthl rntliteral\">hum<\/span>, and <span class=\"rnthl rntliteral\">pres<\/span> variables.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>\/\/ Get Sensor Readings\nvoid getSensorReadings(){\n  temp = bme.readTemperature();\n  hum = bme.readHumidity();\n  pres= bme.readPressure()\/100.0F;\n}<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">getJSONReadings()<\/h4>\n\n\n\n<p>The <span class=\"rnthl rntliteral\">getJSONReadings()<\/span> function returns a JSON string from the current temperature, humidity, and pressure values.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>\/\/ Return JSON String from sensor Readings\nString getJSONReadings(){\n  readings&#091;\"temperature\"] = String(temp);\n  readings&#091;\"humidity\"] =  String(hum);\n  readings&#091;\"pressure\"] = String(pres);\n  String jsonString = JSON.stringify(readings);\n  return jsonString;\n}<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">updateColors()<\/h4>\n\n\n\n<p>The <span class=\"rnthl rntliteral\">updateColors()<\/span> function lights up the RGB LEDs accordingly to the temperature and humidity values range.<\/p>\n\n\n\n<p>First, you need to clear the strips using the <span class=\"rnthl rntliteral\">clear()<\/span> method:<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>strip1.clear();\nstrip2.clear();<\/code><\/pre>\n\n\n\n<p>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 <span class=\"rnthl rntliteral\">tempLEDs<\/span> and <span class=\"rnthl rntliteral\">humLEDs<\/span> variables.<\/p>\n\n\n\n<p>For the temperature, if the temperature is equal to or smaller than zero degrees Celsius, we light up one LED:<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>if (temp&lt;=0){\n  tempLEDs = 1;\n}<\/code><\/pre>\n\n\n\n<p>Here are the other ranges:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>0&lt;temperature=&lt;10 &#8211;> 2 LEDs<\/li>\n\n\n\n<li>10&lt;temperature=&lt;20 &#8211;> 3 LEDs<\/li>\n\n\n\n<li>20&lt;temperature=&lt;30 &#8211;> 4 LEDs<\/li>\n\n\n\n<li>30&lt;temperature&#8211;> 5 LEDs<\/li>\n<\/ul>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>\/\/Number of lit LEDs (temperature)\nint tempLEDs;\nif (temp&lt;=0){\n  tempLEDs = 1;\n}\nelse if (temp&gt;0 &amp;&amp; temp&lt;=10){\n  tempLEDs = 2;\n}\nelse if (temp&gt;10 &amp;&amp; temp&lt;=20){\n  tempLEDs = 3;\n}\nelse if (temp&gt;20 &amp;&amp; temp&lt;=30){\n      tempLEDs = 4;\n}\nelse{\n  tempLEDs = 5;\n}<\/code><\/pre>\n\n\n\n<p>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 <span class=\"rnthl rntliteral\">setPixelColor()<\/span> method on the <span class=\"rnthl rntliteral\">strip1<\/span> object followed by the <span class=\"rnthl rntliteral\">show()<\/span> method. We need a <span class=\"rnthl rntliteral\">for<\/span> loop to light all LEDs.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>\/\/Turn on LEDs for temperature\nfor(int i=0; i&lt;tempLEDs; i++) {\n  strip1.setPixelColor(i, strip1.Color(255, 165, 0));\n  strip1.show();   \n}<\/code><\/pre>\n\n\n\n<p>We follow a similar procedure for the humidity. First, determine how many LEDs should be lit:<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>\/\/Number of lit LEDs (humidity)\nint humLEDs = map(hum, 0, 100, 1, LED_COUNT);<\/code><\/pre>\n\n\n\n<p>And finally, light up the humidity LEDs (<span class=\"rnthl rntliteral\">strip2<\/span>):<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>for(int i=0; i&lt;humLEDs; i++) { \/\/ For each pixel...\n  strip2.setPixelColor(i, strip2.Color(25, 140, 200));\n  strip2.show();\n}<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">initSPIFFS()<\/h4>\n\n\n\n<p>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 <em>.txt<\/em> files to save the SSID, password and IP address.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>void initSPIFFS() {\n  if (!SPIFFS.begin(true)) {\n    Serial.println(\"An error has occurred while mounting SPIFFS\");\n  }\n  else{\n    Serial.println(\"SPIFFS mounted successfully\");\n  }\n}<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">readFile()<\/h4>\n\n\n\n<p>The <span class=\"rnthl rntliteral\">readFile()<\/span> function reads and returns the content of a file.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>String readFile(fs::FS &amp;fs, const char * path){\n  Serial.printf(\"Reading file: %s\\r\\n\", path);\n\n  File file = fs.open(path);\n  if(!file || file.isDirectory()){\n    Serial.println(\"- failed to open file for reading\");\n    return String();\n  }\n  \n  String fileContent;\n  while(file.available()){\n    fileContent = file.readStringUntil('\\n');\n    break;     \n  }\n  return fileContent;\n}<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">writeFile()<\/h4>\n\n\n\n<p>The <span class=\"rnthl rntliteral\">writeFile()<\/span> functions writes content to a file.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>void writeFile(fs::FS &amp;fs, const char * path, const char * message){\n  Serial.printf(\"Writing file: %s\\r\\n\", path);\n\n  File file = fs.open(path, FILE_WRITE);\n  if(!file){\n    Serial.println(\"- failed to open file for writing\");\n    return;\n  }\n  if(file.print(message)){\n    Serial.println(\"- file written\");\n  } else {\n    Serial.println(\"- frite failed\");\n  }\n}<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">initWiFi()<\/h4>\n\n\n\n<p>The <span class=\"rnthl rntliteral\">initWiFi()<\/span> function returns a boolean value (either <span class=\"rnthl rntliteral\">true<\/span> or <span class=\"rnthl rntliteral\">false<\/span>) indicating if the ESP board connected successfully to a network.<\/p>\n\n\n\n<p>First, it checks if the <span class=\"rnthl rntliteral\">ssid<\/span> and <span class=\"rnthl rntliteral\">ip<\/span> variables are empty. If they are, it won\u2019t be able to connect to a network, so it returns <span class=\"rnthl rntliteral\">false<\/span>.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>if(ssid==\"\" || ip==\"\"){\n  Serial.println(\"Undefined SSID or IP address.\");\n  return false;\n}<\/code><\/pre>\n\n\n\n<p>If that\u2019s not the case, we\u2019ll try to connect to the network using the SSID and password saved on the <span class=\"rnthl rntliteral\">ssid<\/span> and <span class=\"rnthl rntliteral\">pass<\/span> variables and set the IP address.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>WiFi.mode(WIFI_STA);\nlocalIP.fromString(ip.c_str());\n\nif (!WiFi.config(localIP, gateway, subnet)){\n  Serial.println(\"STA Failed to configure\");\n  return false;\n}\nWiFi.begin(ssid.c_str(), pass.c_str());\nSerial.println(\"Connecting to WiFi...\");<\/code><\/pre>\n\n\n\n<p>If after 10 seconds (<span class=\"rnthl rntliteral\">interval<\/span> variable) , it is not able to connect to Wi-Fi, it will return <span class=\"rnthl rntliteral\">false<\/span>.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>unsigned long currentMillis = millis();\npreviousMillis = currentMillis;\n\nwhile(WiFi.status() != WL_CONNECTED) {\n  currentMillis = millis();\n  if (currentMillis - previousMillis &gt;= interval) {\n    Serial.println(\"Failed to connect.\");\n    return false;\n  }\n}\n\nSerial.println(WiFi.localIP());<\/code><\/pre>\n\n\n\n<p>If none of the previous conditions are met, it means that the ESP successfully connected to the network in station mode (returns <span class=\"rnthl rntliteral\">true<\/span>).<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>return true;<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">setup()<\/h4>\n\n\n\n<p>In the <span class=\"rnthl rntliteral\">setup()<\/span>, initialize the Serial Monitor.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>Serial.begin(115200);<\/code><\/pre>\n\n\n\n<p>Initialize the rows of addressable RGB LEDs (strips):<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>\/\/ Initialize strips\nstrip1.begin();\nstrip2.begin();<\/code><\/pre>\n\n\n\n<p>Set the strips&#8217; brightness. You can change the brightness on the <span class=\"rnthl rntliteral\">BRIGTHNESS<\/span> variable.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>\/\/ Set brightness \nstrip1.setBrightness(BRIGHTNESS);\nstrip2.setBrightness(BRIGHTNESS);<\/code><\/pre>\n\n\n\n<p>Call the <span class=\"rnthl rntliteral\">initBME()<\/span> function to initialize the sensor:<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>\/\/ Init BME280 senspr\ninitBME();<\/code><\/pre>\n\n\n\n<p>Initialize the filesystem:<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>\/\/ Init SPIFFS\ninitSPIFFS();<\/code><\/pre>\n\n\n\n<p>Read the files to get the previously saved SSID, password and IP address.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>\/\/ Load values saved in SPIFFS\nssid = readFile(SPIFFS, ssidPath);\npass = readFile(SPIFFS, passPath);\nip = readFile(SPIFFS, ipPath);<\/code><\/pre>\n\n\n\n<p>If the ESP connects successfully in station mode (<span class=\"rnthl rntliteral\">initWiFi()<\/span> function returns <span class=\"rnthl rntliteral\">true<\/span>):<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>if(initWiFi()) {<\/code><\/pre>\n\n\n\n<p>Light up all LEDs in a teal color, so that we know that the ESP32 successfully connected to a Wi-Fi network:<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>\/\/ If ESP32 inits successfully in station mode light up all pixels in a teal color\nfor(int i=0; i&lt;LED_COUNT; i++) { \/\/ For each pixel...\n  strip1.setPixelColor(i, strip1.Color(0, 255, 128));\n  strip2.setPixelColor(i, strip2.Color(0, 255, 128));\n\n  strip1.show();   \/\/ Send the updated pixel colors to the hardware.\n  strip2.show();   \/\/ Send the updated pixel colors to the hardware.\n}<\/code><\/pre>\n\n\n\n<p>Then, we can set the commands to handle the web server requests. Send the <span class=\"rnthl rntliteral\">index.html<\/span> file, when you access the root URL:<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>\/\/Handle the Web Server in Station Mode\n\/\/ Route for root \/ web page\nserver.on(\"\/\", HTTP_GET, &#091;](AsyncWebServerRequest *request) {\n  request-&gt;send(SPIFFS, \"\/index.html\", \"text\/html\");\n});<\/code><\/pre>\n\n\n\n<p>Send the CSS and JavaScript files requested by the HTML file (that are also saved in SPIFFS):<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>server.serveStatic(\"\/\", SPIFFS, \"\/\");<\/code><\/pre>\n\n\n\n<p>When you access the web server page for the first time, it makes a request to the server on the <span class=\"rnthl rntliteral\">\/readings<\/span> URL asking for the latest sensor readings. When that happens, send the JSON string with the readings:<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>\/\/ Request for the latest sensor readings\nserver.on(\"\/readings\", HTTP_GET, &#091;](AsyncWebServerRequest *request){\n  getSensorReadings();\n  String json = getJSONReadings();\n  request-&gt;send(200, \"application\/json\", json);\n  json = String();\n});<\/code><\/pre>\n\n\n\n<p>Set up the server-sent events on the server:<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>events.onConnect(&#091;](AsyncEventSourceClient *client){\n  if(client-&gt;lastId()){\n    Serial.printf(\"Client reconnected! Last message ID that it got is: %u\\n\", client-&gt;lastId());\n  }\n});\nserver.addHandler(&amp;events);<\/code><\/pre>\n\n\n\n<p>Finally, start the server:<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>server.begin();<\/code><\/pre>\n\n\n\n<p>If the ESP32 can&#8217;t connect to a Wi-Fi network, the <span class=\"rnthl rntliteral\">initWiFi()<\/span> function returns <span class=\"rnthl rntliteral\">false<\/span>. In this case, light up all the LEDs in a red color, so that we know the ESP32 will be in access point mode:<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>for(int i=0; i&lt;LED_COUNT; i++) { \/\/ For each pixel...\n  strip1.setPixelColor(i, strip1.Color(128, 0, 21));\n  strip2.setPixelColor(i, strip2.Color(128, 0, 21));\n\n  strip1.show();   \/\/ Send the updated pixel colors to the hardware.\n  strip2.show();   \/\/ Send the updated pixel colors to the hardware.\n}<\/code><\/pre>\n\n\n\n<p>Set up the ESP will as an access point:<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>\/\/ Set Access Point\nSerial.println(\"Setting AP (Access Point)\");\n\/\/ NULL sets an open Access Point\nWiFi.softAP(\"ESP-WIFI-MANAGER\", NULL);\n\nIPAddress IP = WiFi.softAPIP();\nSerial.print(\"AP IP address: \");\nSerial.println(IP); <\/code><\/pre>\n\n\n\n<p>To set an access point, we use the <span class=\"rnthl rntliteral\">softAP()<\/span> 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 <span class=\"rnthl rntliteral\">NULL<\/span>. You can add a password if you want. To learn more about setting up an Access Point, read the following tutorial:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/randomnerdtutorials.com\/esp32-access-point-ap-web-server\/\">How to Set an ESP32 Access Point (AP) for Web Server<\/a><\/li>\n<\/ul>\n\n\n\n<p>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 <span class=\"rnthl rntliteral\">wifimanager.html<\/span> file when it receives a request on the root <span class=\"rnthl rntliteral\">\/<\/span> URL.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>\/\/ Web Server Root URL For WiFi Manager Web Page\nserver.on(\"\/\", HTTP_GET, &#091;](AsyncWebServerRequest *request){\n  request-&gt;send(SPIFFS, \"\/wifimanager.html\", \"text\/html\");\n});<\/code><\/pre>\n\n\n\n<p>We must also handle what happens when the form is submitted via HTTP POST request.<\/p>\n\n\n\n<p>The following lines save the submitted values on the <span class=\"rnthl rntliteral\">ssid<\/span>, <span class=\"rnthl rntliteral\">pass,<\/span> and <span class=\"rnthl rntliteral\">ip<\/span> variables and save those variables on the corresponding files.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>\/\/ Get the parameters submited on the form \nserver.on(\"\/\", HTTP_POST, &#091;](AsyncWebServerRequest *request) {\n  int params = request-&gt;params();\n  for(int i=0;i&lt;params;i++){\n    AsyncWebParameter* p = request-&gt;getParam(i);\n    if(p-&gt;isPost()){\n      \/\/ HTTP POST ssid value\n      if (p-&gt;name() == PARAM_INPUT_1) {\n        ssid = p-&gt;value().c_str();\n        Serial.print(\"SSID set to: \");\n        Serial.println(ssid);\n        \/\/ Write file to save value\n        writeFile(SPIFFS, ssidPath, ssid.c_str());\n      }\n      \/\/ HTTP POST pass value\n      if (p-&gt;name() == PARAM_INPUT_2) {\n        pass = p-&gt;value().c_str();\n        Serial.print(\"Password set to: \");\n        Serial.println(pass);\n        \/\/ Write file to save value\n        writeFile(SPIFFS, passPath, pass.c_str());\n      }\n      \/\/ HTTP POST ip value\n      if (p-&gt;name() == PARAM_INPUT_3) {\n        ip = p-&gt;value().c_str();\n        Serial.print(\"IP Address set to: \");\n        Serial.println(ip);\n        \/\/ Write file to save value\n        writeFile(SPIFFS, ipPath, ip.c_str());\n      }\n      \/\/Serial.printf(\"POST&#091;%s]: %s\\n\", p-&gt;name().c_str(), p-&gt;value().c_str());\n    }\n  }<\/code><\/pre>\n\n\n\n<p>After submitting the form, send a response with some text, so that we know that the ESP received the form details:<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>request-&gt;send(200, \"text\/plain\", \"Done. ESP will restart, connect to your router and go to IP address: \" + ip);<\/code><\/pre>\n\n\n\n<p>After three seconds, restart the ESP board with <span class=\"rnthl rntliteral\">ESP.restart()<\/span>:<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>delay(3000);\n\/\/ After saving the parameters, restart the ESP32\nESP.restart();<\/code><\/pre>\n\n\n\n<p>After restarting, the board will have the SSID and password saved on the files, and it will successfully initialize in station mode.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">loop()<\/h4>\n\n\n\n<p>In the <span class=\"rnthl rntliteral\">loop()<\/span>, check if the ESP32 is successfully connected to a wi-fi station:<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>if (WiFi.status() == WL_CONNECTED) {<\/code><\/pre>\n\n\n\n<p>If it is, do the following every 30 seconds (<span class=\"rnthl rntliteral\">timerDelay<\/span> variable):<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>get the latest sensor readings: call the <span class=\"rnthl rntliteral\">getSensorReadings()<\/span> function;<\/li>\n\n\n\n<li>update the RGB LED colors to match the temperature and humidity values: call the <span class=\"rnthl rntliteral\">updateColors()<\/span> functions;<\/li>\n\n\n\n<li>send an event to the browser with the latest sensor readings in JSON format.<\/li>\n<\/ul>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>if (millis() - lastTime &gt; timerDelay) {\n  getSensorReadings();\n  updateColors();\n      \n  String message = getJSONReadings();\n  events.send(message.c_str(),\"new_readings\" ,millis());\n  lastTime = millis();\n}<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Demonstration<\/h2>\n\n\n\n<p>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 <span class=\"rnthl rntliteral\">ssid.txt<\/span>, <span class=\"rnthl rntliteral\">pass.txt,<\/span> and <span class=\"rnthl rntliteral\">ip.txt<\/span> files and it won\u2019t succeed because those files weren\u2019t created yet. So, it will start an Access Point.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"767\" height=\"525\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/03\/Serial-Monitor-ESP32-Access-Point-Mode.png?resize=767%2C525&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"Serial Monitor ESP32 Access Point Mode\" class=\"wp-image-102721\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/03\/Serial-Monitor-ESP32-Access-Point-Mode.png?w=767&amp;quality=100&amp;strip=all&amp;ssl=1 767w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/03\/Serial-Monitor-ESP32-Access-Point-Mode.png?resize=300%2C205&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 767px) 100vw, 767px\" \/><\/figure><\/div>\n\n\n<p>All the LEDs on the shield will be lit in red, indicating that the board is set as an access point.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"750\" height=\"422\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/03\/ESP32-Status-Indicator-Sensor-PCB-Shield-Access-Point-Mode.jpg?resize=750%2C422&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 Status Indicator Sensor PCB Shield Access Point Mode\" class=\"wp-image-102791\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/03\/ESP32-Status-Indicator-Sensor-PCB-Shield-Access-Point-Mode.jpg?w=750&amp;quality=100&amp;strip=all&amp;ssl=1 750w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/03\/ESP32-Status-Indicator-Sensor-PCB-Shield-Access-Point-Mode.jpg?resize=300%2C169&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 750px) 100vw, 750px\" \/><\/figure><\/div>\n\n\n<p>On your computer or smartphone, go to your network settings and connect to the <strong>ESP-WIFI-MANAGER<\/strong> access point.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full is-resized\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"750\" height=\"466\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/03\/Connect-to-ESP32-Wi-Fi-Manager.png?resize=750%2C466&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 board Wi-Fi Manager Access Point web server SSID password\" class=\"wp-image-102724\" style=\"width:563px;height:350px\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/03\/Connect-to-ESP32-Wi-Fi-Manager.png?w=750&amp;quality=100&amp;strip=all&amp;ssl=1 750w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/03\/Connect-to-ESP32-Wi-Fi-Manager.png?resize=300%2C186&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 750px) 100vw, 750px\" \/><\/figure><\/div>\n\n\n<p>Then, open your browser and go to <span class=\"rnthl rntliteral\">192.168.4.1<\/span>. The Wi-Fi Manager web page should open.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"643\" height=\"409\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/03\/ESP32-Wi-Fi-Manager-Web-Page.png?resize=643%2C409&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 Wi-Fi Manager Web Page web server\" class=\"wp-image-102617\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/03\/ESP32-Wi-Fi-Manager-Web-Page.png?w=643&amp;quality=100&amp;strip=all&amp;ssl=1 643w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/03\/ESP32-Wi-Fi-Manager-Web-Page.png?resize=300%2C191&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 643px) 100vw, 643px\" \/><\/figure><\/div>\n\n\n<p>Enter your network credentials: SSID and Password and an available IP address on your local network. After that, you\u2019ll be redirected to the following page:<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"643\" height=\"309\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/03\/ESP32-connected-to-station-success-Wi-Fi-Manager.png?resize=643%2C309&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 connected to station success Wi-Fi Manager\" class=\"wp-image-102725\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/03\/ESP32-connected-to-station-success-Wi-Fi-Manager.png?w=643&amp;quality=100&amp;strip=all&amp;ssl=1 643w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/03\/ESP32-connected-to-station-success-Wi-Fi-Manager.png?resize=300%2C144&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 643px) 100vw, 643px\" \/><\/figure><\/div>\n\n\n<p>At the same time, the ESP should print the following in the Serial Monitor indicating that the parameters you\u2019ve inserted were successfully saved on the corresponding files:<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"767\" height=\"525\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/03\/Serial-Monitor-ESP32-Wi-Fi-Manager-Save-Credentials-1.png?resize=767%2C525&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 Wi-Fi Manager Web Page web server Arduino IDE serial monitor demonstration\" class=\"wp-image-102723\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/03\/Serial-Monitor-ESP32-Wi-Fi-Manager-Save-Credentials-1.png?w=767&amp;quality=100&amp;strip=all&amp;ssl=1 767w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/03\/Serial-Monitor-ESP32-Wi-Fi-Manager-Save-Credentials-1.png?resize=300%2C205&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 767px) 100vw, 767px\" \/><\/figure><\/div>\n\n\n<p>After a few seconds, the ESP will restart. And if you\u2019ve inserted the right SSID and password it will start in station mode:<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"767\" height=\"525\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/03\/Serial-Monitor-ESP32-Station-Mode-Success.png?resize=767%2C525&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 Wi-Fi Manager Web Page web server Arduino IDE serial monitor demonstration\" class=\"wp-image-102722\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/03\/Serial-Monitor-ESP32-Station-Mode-Success.png?w=767&amp;quality=100&amp;strip=all&amp;ssl=1 767w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/03\/Serial-Monitor-ESP32-Station-Mode-Success.png?resize=300%2C205&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 767px) 100vw, 767px\" \/><\/figure><\/div>\n\n\n<p>All the LEDs on the shield will be lit in a teal color for 30 seconds.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"750\" height=\"422\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/03\/ESP32-Stauts-Indicator-Sensor-PCB-Shield-Station-Mode.jpg?resize=750%2C422&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 Status Indicator Sensor PCB Shield Station Mode\" class=\"wp-image-102790\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/03\/ESP32-Stauts-Indicator-Sensor-PCB-Shield-Station-Mode.jpg?w=750&amp;quality=100&amp;strip=all&amp;ssl=1 750w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/03\/ESP32-Stauts-Indicator-Sensor-PCB-Shield-Station-Mode.jpg?resize=300%2C169&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 750px) 100vw, 750px\" \/><\/figure><\/div>\n\n\n<p>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:<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"786\" height=\"518\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/03\/ESP32-BME280-Sensor-Readings-Web-Server-Table.png?resize=786%2C518&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 BME280 Sensor Readings Web Server Table\" class=\"wp-image-102616\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/03\/ESP32-BME280-Sensor-Readings-Web-Server-Table.png?w=786&amp;quality=100&amp;strip=all&amp;ssl=1 786w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/03\/ESP32-BME280-Sensor-Readings-Web-Server-Table.png?resize=300%2C198&amp;quality=100&amp;strip=all&amp;ssl=1 300w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/03\/ESP32-BME280-Sensor-Readings-Web-Server-Table.png?resize=768%2C506&amp;quality=100&amp;strip=all&amp;ssl=1 768w\" sizes=\"(max-width: 786px) 100vw, 786px\" \/><\/figure><\/div>\n\n\n<p>The LEDs on the shield will light up accordingly to the temperature and humidity range.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"750\" height=\"421\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/03\/ESP32-Status-Indicator-Sensor-Shield-Temperature-Humidity-Range-RGB-LEDs.jpg?resize=750%2C421&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 Status Indicator Sensor PCB Shield Station Mode temperature humidity\" class=\"wp-image-102785\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/03\/ESP32-Status-Indicator-Sensor-Shield-Temperature-Humidity-Range-RGB-LEDs.jpg?w=750&amp;quality=100&amp;strip=all&amp;ssl=1 750w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/03\/ESP32-Status-Indicator-Sensor-Shield-Temperature-Humidity-Range-RGB-LEDs.jpg?resize=300%2C168&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 750px) 100vw, 750px\" \/><\/figure><\/div>\n\n\n<p><strong>Note:<\/strong> the previous picture and the web server print screen were taken at different times (that&#8217;s why the values on the table don&#8217;t match the number of lit LEDs). You can also <a href=\"#watch-video-demonstration\">watch the video demonstration<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Wrapping Up<\/h2>\n\n\n\n<p>In this tutorial, you\u2019ve learned how to create a shield for the ESP32 with a BME280 sensor, two rows of addressable RGB LEDs, and a pushbutton. You&#8217;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.<\/p>\n\n\n\n<p>If you like this tutorial, we have other similar projects that include building and designing PCBs:<\/p>\n\n\n\n<ul id=\"block-b3c06e54-c97f-4420-9271-42065e15e540\" class=\"wp-block-list\">\n<li><a href=\"https:\/\/randomnerdtutorials.com\/esp32-weather-station-pcb\/\">ESP32 Weather Station Interface PCB Shield (Temperature, Humidity, Pressure, Date and Time)<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/randomnerdtutorials.com\/esp32-cam-shield-pcb-telegram\/\">ESP32-CAM with Telegram: Take Photos, Control Outputs, Request Sensor Readings and Motion Notifications<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/randomnerdtutorials.com\/esp32-iot-shield-pcb-dashboard\/\">ESP32 IoT Shield PCB with Dashboard for Outputs and Sensors<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/randomnerdtutorials.com\/build-an-all-in-one-esp32-weather-station-shield\/\">Build an All-in-One ESP32 Weather Station Shield<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/randomnerdtutorials.com\/esp8266-multisensor-shield\/\">Build a Multisensor Shield for ESP8266<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/randomnerdtutorials.com\/power-saving-latching-circuit\/\">EXTREME POWER SAVING with Microcontroller External Wake Up: Latching Power PCB<\/a><\/li>\n<\/ul>\n\n\n\n<p>Learn more about the ESP32 with our resources:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/randomnerdtutorials.com\/build-web-servers-esp32-esp8266-ebook\/\"><strong>Build Web Servers with ESP32 and ESP8266 eBook<\/strong><\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/randomnerdtutorials.com\/learn-esp32-with-arduino-ide\/\">Learn ESP32 with Arduino IDE (eBook + video course)<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/randomnerdtutorials.com\/projects-esp32\/\">More ESP32 tutorials and projects\u2026<\/a><\/li>\n<\/ul>\n\n\n\n<p>[Update] the bare PCB giveaway ended and the winners are: Hai, Hasse Lorentzon, Tuan Hazeem, Paul Smulders, and Sudhir Gupta.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In this project, we\u2019ll create a status indicator PCB shield for the ESP32 featuring two rows of addressable RGB neopixel LEDs, a BME280 sensor, and a pushbutton. We&#8217;ll program the &#8230; <\/p>\n<p class=\"read-more-container\"><a title=\"ESP32 Neopixel Status Indicator and Sensor PCB Shield with Wi-Fi Manager\" class=\"read-more button\" href=\"https:\/\/randomnerdtutorials.com\/esp32-status-indicator-sensor-pcb\/#more-102612\" aria-label=\"Read more about ESP32 Neopixel Status Indicator and Sensor PCB Shield with Wi-Fi Manager\">CONTINUE READING \u00bb<\/a><\/p>\n","protected":false},"author":5,"featured_media":102779,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[276,281,277,299,264],"tags":[],"class_list":["post-102612","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-esp32","category-esp32-project","category-esp32-arduino-ide","category-0-esp32","category-project"],"aioseo_notices":[],"jetpack_featured_media_url":"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/03\/ESP32-Status-Indicator-Sensor-Shield-Wi-Fi-Manager.jpg?fit=1280%2C720&quality=100&strip=all&ssl=1","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/posts\/102612","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/users\/5"}],"replies":[{"embeddable":true,"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/comments?post=102612"}],"version-history":[{"count":2,"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/posts\/102612\/revisions"}],"predecessor-version":[{"id":167941,"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/posts\/102612\/revisions\/167941"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/media\/102779"}],"wp:attachment":[{"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/media?parent=102612"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/categories?post=102612"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/tags?post=102612"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}