As of CircuitPython 9, a mount point (folder) named /sd is required on the CIRCUITPY drive. Make sure to create that directory after upgrading CircuitPython.

Now we're going to take a look at the code we'll use for our data logging project.

Copy and paste the following into your, or copy the file to your Feather CIRCUITPY drive and rename it to to get started.

# SPDX-FileCopyrightText: 2018 Kattni Rembor for Adafruit Industries
# SPDX-License-Identifier: MIT

import time

import analogio
import board
import storage
import adafruit_am2320

vbat_voltage = analogio.AnalogIn(board.D9)

i2c = board.I2C()  # uses board.SCL and board.SDA
# i2c = board.STEMMA_I2C()  # For using the built-in STEMMA QT connector on a microcontroller
am2320 = adafruit_am2320.AM2320(i2c)

SD_CS = board.D10
spi = board.SPI()

    import sdcardio

    sd_card = sdcardio.SDCard(spi, SD_CS)
except ImportError:
    import adafruit_sdcard
    import digitalio

    cs = digitalio.DigitalInOut(SD_CS)
    sd_card = adafruit_sdcard.SDCard(spi, cs)

vfs = storage.VfsFat(sd_card)
storage.mount(vfs, "/sd_card")

def get_voltage(pin):
    return (pin.value * 3.3) / 65536 * 2

print("Logging temperature and humidity to log file")

initial_time = time.monotonic()

while True:
        with open("/sd_card/log.txt", "a") as sdc:
            temperature = am2320.temperature
            humidity = am2320.relative_humidity
            battery_voltage = get_voltage(vbat_voltage)
            current_time = time.monotonic()
            time_stamp = current_time - initial_time
            print("Seconds since current data log started:", int(time_stamp))
            print("Temperature:", temperature)
            print("Humidity:", humidity)
            print("VBat voltage: {:.2f}".format(battery_voltage))
                "{}, {}, {}, {:.2f}\n".format(
                    int(time_stamp), temperature, humidity, battery_voltage
    except OSError:
    except RuntimeError:


First we import the necessary libraries for our code.

Next, we create the battery voltage object. This will later allow us to log the voltage output from the battery, which can tell us when the battery is getting low.

Then we setup I2C and the AM2320 sensor object.

Next we setup the SD card. First we assign SD_CS = board.D10 which assigns the SD card chip select pin to the D10 pin. Next, we setup the SPI object. Then we create the chip select object and provide it with the pin we assigned. Next we create the SD Card object, and provide it the SPI and chip select (cs) objects we created. Then we create the file system object that creates the logging space on the SD card for CircuitPython to use. Last, we tell CircuitPython to mount the file system and to mount it at /sd_card.

We have one helper function in our code, the get_voltage() function. By default, analog readings will range from 0 (minimum) to 65535 (maximum). This helper will convert the 0-65535 reading from pin.value and convert it a 0-3.3V voltage reading. This allows us to read the battery voltage. It logs to the file along with the data from our sensor so we know when our battery is getting low.

Then we print to the serial console that we're logging temperature. This shows up once, the first time the code runs.

And last, we set initial_time = time.monotonic(). We use time.monotonic() to give us a time, in seconds, since we began logging. We need an initial time to compare to, so we assign this variable at the beginning. For more information about how time.monotonic() is used, check out the Passing Time section of the Hacking Ikea Lamps with Circuit Playground Express guide.

Main Loop

We start with while True:.

Notice that the bulk of the code is under a try, followed by an except. The sensor can intermittently fail to provide a reading. To avoid the code hanging if this occurs, we've included the try and except code. This allows the code to continue, regardless of the error. For more information about try and except, check out the try and except section of the LED Trampoline guide.

Then we have with open("/sd_card/log.txt", "a") as sdc:. We open a file called log.txt on our mounted SD card filesystem, and the "a" tells it to append to the end of the file each time, instead of overwriting it every time you restart the code. Using with, we set the open code to sdc so we can use the write attribute later to write to the file.

Now we assign a series of variables. We're going to be logging the temperature, the humidity, the battery voltage, and the seconds since we last began logging. First we assign temperature and humidity to their values: temperature = am2320.temperature, humidity = am2320.relative_humidity. Next we assign battery_voltage to the get_voltage() helper: battery_voltage = get_voltage(vbat_voltage). Last, we set time.monotonic() to current_time, and then create our time_stamp by subtracting initial_time from current_time.

Then we print the value of each of our variables on different lines. We have added int() to time_stamp. As we are taking time readings every 3 seconds, we chose to work with whole integers instead of decimals. We have also included a string format for the battery voltage. {:.2f}".format(battery_voltage) allows us to print the battery voltage results with 2 decimal places. Then we include a printed blank line so the results are easier to read in the serial console.

Now we use our sdc object to write data to our log file. We want our log file to be a series of comma separated values (CSV). So, we use another string format to print each variable, separated by a comma, exactly as we printed them to the serial console above. The \n causes it to do a new line each time so each set of values is on its own line.

Then we have a time.sleep(3) so we are only taking a reading every 3 seconds.

We end with the except parts of our try and except code.

It's time to start logging!

This guide was first published on Apr 30, 2018. It was last updated on Apr 16, 2024.

This page (CircuitPython Code) was last updated on Apr 16, 2024.

Text editor powered by tinymce.