The Adafruit Feather ESP32-S2 with BME280 comes with a BME280 temperature, humidity and pressure sensor built right into the board. Both the Feather ESP32-S2 and the Feather ESP32-S2 with BME280 come with an LC709203 lithium ion polymer (lipoly) battery monitor. Both the BME280 and the LC709203 are available over I2C.
Each sensor comes with its own Adafruit CircuitPython library that makes it simple to write code to read data from them. These examples will be using, among other things, the Adafruit CircuitPython BME280 and the Adafruit CircuitPython LC709203F libraries, both of which are available in the Adafruit CircuitPython Library Bundle.
The first example simply reads data from the sensors and prints it to the serial console. The temperature data will be skewed high because of the physical proximity to the ESP32-S2 module which will heat up over time while in use. The example is designed to show you how to get data from the sensor.
The second example takes the sensor data and sends it to Adafruit IO. It is designed to run on battery power and utilises deep sleep to keep the board cool for temperature readings. You must have an Adafruit IO account and Wi-Fi access for this example to work.
- The BME280 temperature, humidity and pressure sensor (highlighted in green) is located above the center of the board, at the top right corner of the STEMMA QT connector. Its I2C address is 0x77.
- The LC709203 battery monitor (highlighted in red) is located slightly above and to the left of the BME280, directly under the USB pin label on the silk. Its I2C address is 0x0B. (0xb, which is returned by I2C scan, is the same as 0x0B, it simply has leading zeros.)
BME280 and LC709203 Simple Data Example
To run this example, you need to first install the BME280 and LC709203F libraries into the lib folder on your CIRCUITPY drive. Then you need to update code.py with the example script.
Thankfully, we can do this in one go. In the example below, click the Download Project Bundle button below to download the necessary libraries and the code.py file in a zip file. Extract the contents of the zip file, and copy the entire lib folder and the code.py file to your CIRCUITPY drive.
Your CIRCUITPY/lib folder should contain the following folder and file:
- adafruit_bme280/
- adafruit_lc709203f.mpy
# SPDX-FileCopyrightText: 2021 Kattni Rembor for Adafruit Industries # SPDX-License-Identifier: Unlicense """ CircuitPython Simple Example for BME280 and LC709203 Sensors """ import time import board from adafruit_bme280 import basic as adafruit_bme280 from adafruit_lc709203f import LC709203F, PackSize # Create sensor objects, using the board's default I2C bus. i2c = board.I2C() # uses board.SCL and board.SDA # i2c = board.STEMMA_I2C() # For using the built-in STEMMA QT connector on a microcontroller bme280 = adafruit_bme280.Adafruit_BME280_I2C(i2c) battery_monitor = LC709203F(i2c) battery_monitor.pack_size = PackSize.MAH400 # change this to match your location's pressure (hPa) at sea level bme280.sea_level_pressure = 1013.25 while True: print("\nTemperature: {:.1f} C".format(bme280.temperature)) print("Humidity: {:.1f} %".format(bme280.relative_humidity)) print("Pressure: {:.1f} hPa".format(bme280.pressure)) print("Altitude: {:.2f} meters".format(bme280.altitude)) print("Battery Percent: {:.2f} %".format(battery_monitor.cell_percent)) print("Battery Voltage: {:.2f} V".format(battery_monitor.cell_voltage)) time.sleep(2)
Now, connect to the serial console to see the sensor data printed out!
That's all there is to reading the BME280 and LC709203 data from the Feather ESP32-S2 using CircuitPython!
For more details, check out the guides for the BME280 and the LC709203.
You'll notice the BME280 temperature values are a bit high - anywhere up to 10-15 degrees F higher than your ambient temperature. Depending on whether you have a charging battery plugged in, the increased temperature is due to one or both of two things: the heat radiating from the ESP32-S2 module and/or the heat from the battery charging circuitry. How can you get reliable temperature data from your Feather ESP32-S2 with BME280? Easy: CircuitPython and Adafruit IO.
BME280 and LC709203 Adafruit IO Example
Adafruit IO gives you the option to disconnect the Feather ESP32-S2 from your computer and run it off of USB power or a battery, and still be able to see the data. This example takes the same data printed to the serial console in the example above, and also sends it to Adafruit IO.
Adafruit IO Example Secrets
This example requires you to provide your Wi-Fi credentials, and your Adafruit IO username and key. To do this, you'll want to create a secrets.py file on your CIRCUITPY drive, with the following structure.
To find your Adafruit IO key, follow the initial steps on this page.
Update the following with your info, keeping the "
around them:
- Replace
"your-wifi-ssid"
with your Wi-Fi SSID - Replace
"your-wifi-password"
with your Wi-Fi password - Replace
"your-aio-username"
with your Adafruit IO username - Replace
"your-super-long-aio-key"
with your Adafruit IO key
Your secrets.py file should look like the code below, but with the above updated info.
# This file is where you keep secret settings, passwords, and tokens! # If you put them in the code you risk committing that info or sharing it secrets = { "ssid": "your-wifi-ssid", "password": "your-wifi-password", "aio_username": "your-aio-username", "aio_key": "your-super-long-aio-key", }
Adafruit IO Example Code
To run this example, you need to first install the BME280, LC709203F, Adafruit IO, and Adafruit Requests libraries into the lib folder on your CIRCUITPY drive. Then you need to update code.py with the example script.
Thankfully, we can do this in one go. In the example below, click the Download Project Bundle button below to download the necessary libraries and the code.py file in a zip file. Extract the contents of the zip file, and copy the entire lib folder and the code.py file to your CIRCUITPY drive.
Your CIRCUITPY/lib folder should contain the following folders and files:
- adafruit_bme280/
- adafruit_io/
- adafruit_lc709203f.mpy
- adafruit_requests.mpy
# SPDX-FileCopyrightText: 2021 Kattni Rembor for Adafruit Industries # SPDX-License-Identifier: Unlicense """ CircuitPython Adafruit IO Example for BME280 and LC709203 Sensors """ import time import ssl import alarm import board import digitalio import wifi import socketpool import adafruit_requests from adafruit_io.adafruit_io import IO_HTTP, AdafruitIO_RequestError from adafruit_lc709203f import LC709203F, PackSize from adafruit_bme280 import basic as adafruit_bme280 try: from secrets import secrets except ImportError: print("WiFi and Adafruit IO credentials are kept in secrets.py, please add them there!") raise # Duration of sleep in seconds. Default is 600 seconds (10 minutes). # Feather will sleep for this duration between sensor readings / sending data to AdafruitIO sleep_duration = 600 # Update to match the mAh of your battery for more accurate readings. # Can be MAH100, MAH200, MAH400, MAH500, MAH1000, MAH2000, MAH3000. # Choose the closest match. Include "PackSize." before it, as shown. battery_pack_size = PackSize.MAH400 # Setup the little red LED led = digitalio.DigitalInOut(board.LED) led.switch_to_output() # Set up the BME280 and LC709203 sensors i2c = board.I2C() # uses board.SCL and board.SDA # i2c = board.STEMMA_I2C() # For using the built-in STEMMA QT connector on a microcontroller bme280 = adafruit_bme280.Adafruit_BME280_I2C(i2c) battery_monitor = LC709203F(i2c) battery_monitor.pack_size = battery_pack_size # Collect the sensor data values and format the data temperature = "{:.2f}".format(bme280.temperature) temperature_f = "{:.2f}".format((bme280.temperature * (9 / 5) + 32)) # Convert C to F humidity = "{:.2f}".format(bme280.relative_humidity) pressure = "{:.2f}".format(bme280.pressure) battery_voltage = "{:.2f}".format(battery_monitor.cell_voltage) battery_percent = "{:.1f}".format(battery_monitor.cell_percent) def go_to_sleep(sleep_period): # Turn off I2C power by setting it to input i2c_power = digitalio.DigitalInOut(board.I2C_POWER) i2c_power.switch_to_input() # Create a an alarm that will trigger sleep_period number of seconds from now. time_alarm = alarm.time.TimeAlarm(monotonic_time=time.monotonic() + sleep_period) # Exit and deep sleep until the alarm wakes us. alarm.exit_and_deep_sleep_until_alarms(time_alarm) # Fetch the feed of the provided name. If the feed does not exist, create it. def setup_feed(feed_name): try: # Get the feed of provided feed_name from Adafruit IO return io.get_feed(feed_name) except AdafruitIO_RequestError: # If no feed of that name exists, create it return io.create_new_feed(feed_name) # Send the data. Requires a feed name and a value to send. def send_io_data(feed, value): return io.send_data(feed["key"], value) # Wi-Fi connections can have issues! This ensures the code will continue to run. try: # Connect to Wi-Fi wifi.radio.connect(secrets["ssid"], secrets["password"]) print("Connected to {}!".format(secrets["ssid"])) print("IP:", wifi.radio.ipv4_address) pool = socketpool.SocketPool(wifi.radio) requests = adafruit_requests.Session(pool, ssl.create_default_context()) # Wi-Fi connectivity fails with error messages, not specific errors, so this except is broad. except Exception as e: # pylint: disable=broad-except print(e) go_to_sleep(60) # Set your Adafruit IO Username and Key in secrets.py # (visit io.adafruit.com if you need to create an account, # or if you need your Adafruit IO key.) aio_username = secrets["aio_username"] aio_key = secrets["aio_key"] # Initialize an Adafruit IO HTTP API object io = IO_HTTP(aio_username, aio_key, requests) # Turn on the LED to indicate data is being sent. led.value = True # Print data values to the serial console. Not necessary for Adafruit IO. print("Current BME280 temperature: {0} C".format(temperature)) print("Current BME280 temperature: {0} F".format(temperature_f)) print("Current BME280 humidity: {0} %".format(humidity)) print("Current BME280 pressure: {0} hPa".format(pressure)) print("Current battery voltage: {0} V".format(battery_voltage)) print("Current battery percent: {0} %".format(battery_percent)) # Adafruit IO sending can run into issues if the network fails! # This ensures the code will continue to run. try: print("Sending data to AdafruitIO...") # Send data to Adafruit IO send_io_data(setup_feed("bme280-temperature"), temperature) send_io_data(setup_feed("bme280-temperature-f"), temperature_f) send_io_data(setup_feed("bme280-humidity"), humidity) send_io_data(setup_feed("bme280-pressure"), pressure) send_io_data(setup_feed("battery-voltage"), battery_voltage) send_io_data(setup_feed("battery-percent"), battery_percent) print("Data sent!") # Turn off the LED to indicate data sending is complete. led.value = False # Adafruit IO can fail with multiple errors depending on the situation, so this except is broad. except Exception as e: # pylint: disable=broad-except print(e) go_to_sleep(60) go_to_sleep(sleep_duration)
You can connect to the serial console to see the current readings printed out.
Depending on when you first connect to the serial console, there may be a delay before anything is displayed. The readings are taken silently before engaging in the Wi-Fi connection and sending data to Adafruit IO. The data is printed to the serial console immediately before sending to Adafruit IO.
Customisation
This example is designed to run one time and then put the board to sleep for a period of time, before repeating the process. You can customise the period of time it sleeps between readings.
Update sleep_duration = 600
at the beginning of your code to the number of seconds you would like to have between data readings. The default is 600 seconds, or 10 minutes.
Be aware, to avoid the board heating up due to the Wi-Fi connection and data transmission to Adafruit IO, you must sleep for at least five minutes (300 seconds) between readings. Anything five minutes or longer will work.
Update battery_pack_size = PackSize.MAH400
to match your battery's mAh. This increases the accuracy of the battery monitor readings.
The options are MAH100
, MAH200
, MAH400
, MAH500
, MAH1000
, MAH2000
, or MAH3000
. You must include the PackSize.
before the MAHXXX
.
If none of the options match your battery size, choose the one that is the closest. For example, if your battery is 1200mAh, choose MAH1000
.
Code Walk-Through
First you import all of the necessary modules and libraries.
Next, you customise your sleep duration and battery pack size, as discussed above.
Then, you setup the hardware. The red LED is used to visually indicate when data is being sent to Adafruit IO. The I2C power pin is enabled and pulled low. The BME280 and the LC708203 are both setup using I2C. And finally, the battery pack size is set.
Following setup, you get the current data readings. It is important to obtain the data as early as possible in the code to ensure that the heat from the ESP32-S2 module has not affected the temperature readings. The Wi-Fi connection and sending data to Adafruit IO are done after the data is read to ensure the most accurate temperature data.
Then there are three functions.
The first function creates an alarm that will trigger after a number of seconds, and then tells the board to sleep until the alarm is triggered.
The second function sets up an Adafruit IO feed by first trying to get the feed, and if it does not exist, creating the feed on Adafruit IO.
The third function takes data and sends it to a specified feed on Adafruit IO.
The are two sections wrapped it in a try
/ except
blocks. This is because Wi-Fi can run into connection issues, and Adafruit IO can fail to send for various reasons. If either of these fail, your code would crash and stop running. The try
/ except
ensures your code will continue running. Each section tries to run the code under the try
, and if it fails, tells the board to to go to sleep for 60 seconds before waking up and running the code again.
The first try
/ except
block is around the Wi-Fi connection code. You try to connect to Wi-Fi using the credentials you provided in secrets.py, and setup for communication using the Wi-Fi.
Then, you get your Adafruit IO credentials from secrets.py, and initialise the Adafruit IO HTTP connection.
The next section begins with turning on the red LED to indicate the data is being sent to Adafruit IO. The data is first printed to the serial console.
The second try
/ except
block is around the Adafruit IO data sending code. Using the second and third functions described above, the code tries to send each piece of data to its own feed on Adafruit IO. The LED is then turned off to indicate sending data is completed.
Finally, the code tells the board to go to sleep for the sleep duration specified at the beginning of your code.
That covers using the Adafruit Feather ESP32-S2 with BME280 and CircuitPython with Adafruit IO to track temperature, humidity, pressure and battery data!
Page last edited January 21, 2025
Text editor powered by tinymce.