Project 36:
ESP32 Remote Sensor

 

 

v6.1 ESP32 (DOIT ESP32 DevKit-C v4): Schematic, Board Layout, PCB Top and Bottom

     

v6.1 Bill of Materials     Digikey BOM with Prices

 

         Demo of Breadboard Prototype &                                                                                                            

Fabricated Board with SH110x OLED Displays   Demo of Fab'd Board with IPS TFT Colour Display

                

 

Demo of Stats Displayed on ThingSpeak Web Server

 

Overview:

An ESP32 development unit measures temperature, humidity and barometric pressure which it displays on a mono OLED or colour IPS TFT display when nearby motion is detected, otherwise it writes the stats to a micro SD disk every minute. Additionally, WiFi MAC address and status as well as IP address are displayed.

A highly accurate USB-based voltmeter is attached to the fab'd PCB. In the beginning of the OLED video it registers about 21mA, of which 12mA are due to powering the two tiny voltmeters in the breadboard. The low value of less than 10mA is when the ESP32 is in Deep Sleep mode. Once it wakes up the current draw is about 100mA with both the OLED display and micro SD being powered. Current needs are higher with the colour IPS display but still appreciably small to aid battery life.

 

Components: v6.1: DOIT ESP32 DevKit-C v4 with AliExpress_2.0" Colour IPS_Display or 1.3" OLED_Display

 

Arduino Code:

Mono OLED Display:  BME280_OLED_RGB_SNTP_SD_PIR_Sleep_5.ino

Colour IPS Display:     _Stats_Server_ST7789__3.ino

Note: If your ESP32 ever locks up, try this code: Reset_ESP32.ino

 

ThingSpeak Web Server:

I opened a free (limited uploads) account on ThingSpeak and used the code, ThingSpeak_BME.ino, to push the stats content to it. As such, I could look at the graphed stats display from any browser, including my phone. Note however that only Temperature is being displayed; the other two stats sometimes display at erratic intervals and sometimes not at all like this morning.

 

The next revision will feature the board acting as a web server to view the stats.

HOW DOES IT ALL WORK?

We have designed a PCB to accommodate a number of devices. If you wish to learn how things work while assembling the PCB then I suggest you get yourself a quality breadboard, jumper wires and follow the directions below.

Note that this verbiage is for the code (BME280_OLED_RGB_SNTP_SD_PIR_Sleep_5.ino) written for the mono OLED display.

The colour IPS display code ( _Stats_Server_ST7789.ino) is very similar but new features have been added.

The PCB will contain the following:
- DOIT ESP32 Devkit-C v4 with WiFi
- 1.3" mono OLED display or 2.0" colour IPS display
- BME280 or AHT10 sensors (temperature, humidity, barometric pressure)
- RGB 3-colour LED
- Adafruit #4682 SD drive
- Adafruit #4667 PIR motion sensor
- Adafruit #2124 LiPoly battery charger

TOP

We'll start off by testing the  I2C Devices: If you install two rows of 19-pin sockets (or a single 38-pin double socket or cheaper 40-pin double socket) for each side of the ESP32, you can connect it via the spare pin sockets to a breadboard: the 38-pin ESP32 is too wide to be used on a standard breadboard.
We are going to test several modules separately. If successful we'll test a few together to ensure continued success. If something does not work we can quickly revert to the config where it just worked.
The schematic above contains a microcontroller pin inventory in green text, organized by device/module then pin number.
The 1.3" OLED displayBME280 sensor and AHT10 sensor use 3v3 and I2C. I use yellow jumper wire for SCL and blue jumper wire for SDA because they're easy to pick out on the breadboard.

SCL on the ESP32 is pin 22 and SDA is pin 21. If the two I2C devices you're using are designed "properly", they already contain pullup resistors on SCL and SDA. All of these type of sensors I have looked at had SMT resistors marked as 103 indicating they were 10k. They also had a 100nF noise coupling cap on the power supply pin.

 

Let's start by checking the I2C address of the OLED using the Serial Monitor and the scanner sketch I2C_Scan.ino
- OLED I2C address is 0x3c
Remove the OLED and install the BME280 sensor. Run the scanner sketch again.
- BME280 I2C address is 0x77

Remove the BME280 sensor and install the AHT10 sensor. Run the scanner sketch again.

- AHT10 I2C address is 0x38

If you install all 3 devices, you should be able to see them all listed in the Serial Monitor as shown here.

If one of the devices tested separately is not showing up, consider changing to one of the alternative I2C addresses. For the BME280 that could be 0x76 instead of 0x77.

I2C is now tested.


Remove the sensors. We'll use an Adafruit sketch, SH1106_128x64_i2c_QTPY.ino, to test the OLED for graphics and text.
Results should be seen on the OLED and on the Serial Monitor as shown here.  Here's another OLED test sketch as well as an RGB IPS test sketch.

OLED is now tested.

 

Remove the OLED, install the BME280 sensor, load and run the sketch, BME280_Test.ino.  Results can be seen in the serial monitor and here.

Remove the BME280, install the AHT10 sensor,  load and run the sketch, AHT10_Test.ino.  Results can be seen in the serial monitor and here.
Troubleshooting: If you get serial monitor outputs with "nan" instead of a numerical value from the sensor, check your grounds. Or you may have the wrong I2C address: address 0x77 is commonly used but some offshore clones use address 0x76.
If the device is working but the values are erratic, then it may not be terminated properly. Consider adding a 10K resistor to VCC for SCL and another to VCC for SDA but this is a last resort as it is most likely not necessary.

Your sensors are now tested.

TOP

 

We have separately tested the OLED display and the BME280 or AHT10 sensors, so now let's test them at the same time. Load up and run the sketch BME280_OLED.ino
The same global variables and setup parameters from the two previous sketches, I2C_Scan.ino and BME280_Test.ino, can be found in BME280_OLED.ino
Some additional features have been added:
- Line 6: added WiFi driver so we can get the ESP32's wireless Ethernet (WiFi) MAC address and IP address
- Lines 16 to 20 are not needed until we attach an SPI-based microSD card interface but we'll keep them in for now as a way to reserve the ESP32 pin assignments
- Lines 27 to 29 are not needed until we install the PIR motion detection device
- Lines 36 and 37 will be used to automatically login to our preferred WiFi network
- Lines 55 to 59 show the WiFi network connection attempt. Periods will be displayed in the Serial Monitor until we are connected
- Lines 61 to 68 will print our wireless Ethernet (WiFi) MAC address, IP network address, and network name or SSID in the Serial Monitor
- Lines 69 and 70 have been commented out for now but we'll use them later to configure the ESP32 as a web server
In Loop() we have six functions performed sequentially that will display on both the 1.3" OLED and the serial monitor. You can see the Serial Monitor results here. You may notice in the code that Line4() has been commented out. That's because the formula for calculating Humidex or Heat Index (USA) is not working - it's only displaying the current temp.


BME_OLED_RGB.ino is our next test file. It contains everything from BME280_OLED.ino and we've added the following to test an RGB LED:
- Lines 22 to 27 in the global variables section above the setup() section, show the ESP32 pins that are assigned to the RGB LED as well as the 8-bit values associated with On and Off
- Lines 51 to 53 define the pins as outputs. Note how this is markedly different from line 28 of File|Examples|01.Basics|Blink.
- Lines 85 and 86 call two functions to first turn off the LEDs, then cycle them thru On and Off. The two functions are at the bottom of the Loop() code

An example from the Serial Monitor can be found here.

The RGB LED is now tested.

TOP

Troubleshooting:
If any sketch refuses to be uploaded to the ESP32, make sure Serial Monitor (SM) is running beforehand. If not then push the board's RESET button or restart the upload or both; the key point is we need SM running. Within SM you may see the ESP32 "going crazy". If so remove power to it briefly then upload the RGB.ino sketch to the ESP32 to stabilize it. It's best if the RGB.ino sketch is the ONLY one open at the time.

Now that it has stabilized close RGB.ino, upload and run OLED_BME_RGB.ino again. Make sure SM is running before you start the upload to the ESP32.

 

BME_OLED_RGB_Time.ino is our next sketch. Here is an example of it working on the Serial Monitor.
Thus far we have sequentially installed and tested 3 hardware modules: OLED display, BME280 or AHT10 sensor and RGB LED. Now we'll add in a request for the local time, not the global time which will require the use of SNTP and HTTP communication protocols over WiFi to get the current time.
The sketch (File|Examples|Examples from Custom Libraries|SD|SD_time) found in the Examples menu or the modified file SD_time_TEST.ino contain the content we'll graft into a new file, BME_OLED_RGB_Time.ino
Changes to the new file include:
- Reorganized the global variables section above setup() to keep all of the related drivers and parameters together
- Line 29 was added for the local time drivers (https://www.tutorialspoint.com/c_standard_library/time_h.htm)
- Line 32 shows the sntp.h file being included for when we perform a global time lookup at a later time
- In setup() both the Serial Monitor and the OLED are given a one-second startup time: lines 55 and 58
- The MAC (WiFi physical) address as well as the WiFi network name and IP address are printed on the Serial Monitor in lines 80 to 87
The main code loop aptly named loop() contains several routines:
- In the first function, Line0(), "Tiny Sensor Server" is displayed on the OLED and SM (Serial Monitor)
- In the second function, smTime(), the current local time is only displayed on the SM because there is not enough room. We will display it on the IPS display later
- Functions Line(1) through Line(4) are the same as earlier

- The next function EthIP displays both the WiFi MAC and IP addresses on both the OLED and SM

- In the next function, RGBLEDs_Off(), the three RGB LEDs are turned off.

- In the final function, RGBLEDs_OnOff(), the three LED colours are cycled on and off

The local Time function is now tested.

 

BME280_OLED_RGB_SNTP.ino contains all of the previous code. Code has been added so that the network time protocol (SNTP protocol) is used to accurately update the current time. Here is an example of it working on the Serial Monitor.

The following lines were added:

- 29 to 32: drivers, time zone and daylight savings time offset

- 95 to 100:  Can get IP address from DHCP server or the others configured in configTime(). When current time is updated via SNTP, another function timeavailable() can run. It is found in lines 221 to 223.

ConfigTime() specifies offset from UTC/GMT, daylight savings time, and two NTP time servers. Lines 36 and 99 accomplish the same thing

- 105: function netTimeOLED() of lines 208 to 219 is called which displays the time in the Serial Monitor and on the OLED after clearing it

The SNTP network time function is now tested.

TOP

 

BME280_OLED_RGB_SNTP_SD.ino contains additional SD drive code. You MUST use the ESP32 SD and FS drivers or expect trouble during compilation. The driver set can be found here: https://github.com/espressif/arduino-esp32   Info on installing the Arduino-ESP32 support files is found here

NOTE: You may already have SD and FS libraries on your PC located in:

1) C:\Program Files (x86)\Arduino\libraries

2) C:\Users\Your_Name\AppData\Local\Arduino15\libraries

3) C:\Users\Your_Name\Documents\Arduino\libraries

Personally, I moved them to private folders, zipped them up and moved them away from the Arduino IDE. This way only the ESP32 drivers will be used.

Some of the code related to displaying the date and time has been cleaned up so the line numbers in BME280_OLED_RGB_SNTP_SD.ino won't be the same as our earlier file, BME280_OLED_RGB_SNTP.ino .

 

Code changes for the SD drive are listed below:

- Lines 22 to 25 define the SD drivers and file system drivers needed to run the micro SD card drive

- Lines 106 to 115 start the SD drive with the ESP32 DevKit-C v4 specific pins

- Lines 290 to 304 contains a list of the File System calls for the SD drive. The calls or functions follow.

- In loop() most of the code is the same but line 258 contains the data as a numerical string, e.g., 2024.09.07,16:01:02. This will make it easier to log the temperature/humidity/BP on the SD drive

 

String Formatting (BME280_OLED_RGB_SNTP_SD.ino )

This was the first time I have worked with formatting time to be used as a string that could be written as a line of data to an SD drive.

The line numbers involved in the procedure start with line 215 in the function Line5SD().

I used http://gemini.google.com/app to get hints on how to manipulate time data to a character string. I listed the two web links on line 225.

The comments that follow the code should hopefully be helpful.

Here is an example of it working on the Serial Monitor.

The micro SD drive is now tested.

TOP

PIR Motion Sensor:

The program we have been building is ready to go except that the OLED display is always on which means the power needed to run the display will drain the LiPo battery too soon. If we only turn the display on for a short time when there is movement nearby, then the battery should last a lot longer.

Open the test file, monPIR.ino and ensure the LEDs' connection to the ESP32 match those of code lines 10 to 12, and that the PIR pin connection matches the code in line line 17. Run the program with the Serial Monitor active. Move your hand near the PIR and you should see the Serial Monitor output change and the RGB LEDs flash; the flashing should cease after 9 seconds if there is no further movement.

The next step will be to integrate most of this code into our main program.

 

Integrating monPIR.ino into BME280_OLED_RGB_SNTP_SD_PIR_2.ino requires a few changes. They are:

- Lines 41 and 42 define the PIR device and its initial operational state

- Lines 44 to 56 are used to measure how long the OLED is lit, and the time interval between SD writes to disk

- Line 105 defines the PIR as an input. The pin is pulled low by default with the directive "INPUT_PULLDOWN"

- Line 144, the main loop() function, contains only one line: "monPIR();" which is short for "monitor PIR". It determines if there is nearby activity in order to light up the display. It also ensures measurements are taken every two seconds and written to disk every one minute. Both of these parameters can be easily changed

There are 5 comparison components of monPIR():

- Part 0: Display the amount of time the OLED has been lit, and the total elapsed time since the last disk write.

- Parts 1 & 2: Measure activity to determine if the PIR is activated

- Part 3: The PIR indicates there is activity. The RGB is flashed, the OLED is lit only long enough for viewing, and the current stats (Date, Time, Tc, Hu, BP) are written to the SD disk

- Part 4: The PIR indicates there is activity and that it's time to turn off the OLED

- Part 5: A time interval (1 minute) has been reached so whether there is activity or not the stats are written to the SD disk.

The PIR motion sensor is now tested.

 

Sleep Modes:

If we wish to conserve battery power then we should make use of ESP32's Deep Sleep function. This eBook contains a good chapter on the subject.

On a separate breadboard we'll setup an ESP32 with GPIO pin 33 connected to a button switch and GPIO pin 25 connected to a 3v3 PIR motion sensor.

One side of the button switch should be connected to ground via a 10k resistor and the other side to 3v3 power. The GPIO pin is connected between the resistor and the switch.

Load and run the demo file, ExternalWakeUpMultiple_3.ino. Make note of the current time.  With Arduino's Serial Monitor (SM) running, place your finger near the button switch and don't move. You should see several instances of the PIR (that detects movement) firing on pin 25. Slowly move your finger to push the button switch and with any luck you won't trigger the PIR again. When you press the push button, the SM should report the the completion of the electrical circuit so the signal level to pin 29 should change from a low to a high; this will be reflected as GPIO waking up the ESP32.

When 60 seconds have elapsed since the microcontroller was reset or downloaded, the timer will expire and SM will report the wakeup was caused by the timer expiring. Then the system will return to deep sleep.

TOP

Now to implement Deep Sleep:

Our sketch, BME280_OLED_RGB_SNTP_SD_PIR_Sleep_5.ino, has been modified a fair amount to accommodate the ESP32 sleep function. The PIR motion detector is connected to GPIO pin 25. When there is motion, the ESP32 system wakes up and starts displaying the weather stats on the OLED or IPS display. Once finished it returns to deep sleep. After a predetermined time (30s for testing, 10 minutes normally) the system wakes up and writes the stats to the SD disk, then returns to Deep Sleep. Current draw during Deep Sleep in less than 10mA. I'll know the exact value when my USB tester arrives next week.

Key points in the sketch:

- line 73 shows the real time clock (RTC) driver being loaded so that the GPIO pin activity can be read while in deep sleep.

- line 75 indicates the GPIO wake up pin will be 25 attached to the PIR

- line 76 defines the wake up time. It's set to 30s for testing, otherwise it will be set for 10 minutes (600s). The system will wakeup in line 202 of the code and perform the tasks outlined in line 204 before going back to deep sleep in line 209

- line 81: EXT1 permits one or more GPIO wakeups. Originally we set this up for a PIR and a button switch as inputs but now it is just a PIR on GPIO25. We kept the same command line: GPIO25 or GPIO25 will cause an external wakeup for category EXT1.

- line 82: prefix of "RTC_DATA_ATTR" allows the data to be saved in non-volatile storage between boots

- line 84 works with line 76 for the time wakeup

- line 85: we're working with deep sleep and not light sleep so we'll set the parameter to TRUE otherwise it will be light sleep

Two functions were added:

- lines 95 to 100 prints the GPIO number that triggered the wakeup

- lines 102 to 133 prints the wakeup reason

- lines 157 to 167: startup WiFi and print the Connect Status. If it fails to startup, Disconnect WiFi and try again. Continue this process until WiFi is successfully started and ESP32 is connected

- line 188: increment the ESP32 boot counter. Because the count is kept in EEPROM, it will continue to increment with each reboot

- line 190: display the wakeup reason

- line 191: these are the parameters for setting the wakeup type: bitmask specifies the GPIO pins, EXT1 indicates more than one pin, wakeup when any of the GPIO pins in the bitmask go high

- line 193 to 194: the PIR is normally low and goes high when activated so we enable a pulldown resistor and disable a pullup resistor

- lines 196 to 202: these are our normal display functions for the current weather: they will execute and then the sleep timer will start. It is based on the value set in line 76

- line 204: save the stats to the SD card

- line 207: Espressif recommends flushing the Serial Monitor buffer before sleep

- line 209: and now Deep Sleep starts. Nothing else will execute - Loop() is blank. If GPIO25 goes high from detecting motion OR the sleep timer (set to 30s when testing, 10 minutes otherwise) expires, then the system will wakeup but it will appear as if the ESP32 is booting up and the entire sketch will run again

WiFi Status:

Lines 224 to 254 will give us all state conditions for the WiFi. Ideally, we want to see condition "3, WL_CONNECTED" but recognize that problems can occur. The code in lines 157 to 167 handle that situation.

 

Final Note:

This configuration allows the ESP32 to boot, take and display measurements, then save them to disk. It is not yet been configured as a server so that remote measurements can be taken. The WiFi Server mode would be enabled in line 92.

 

 

TOP

 

HOME

 

Updated 2024-12-08 @ 8am