The AM2320 is a fantastic little sensor that gathers temperature and humidity data. You can print it to the serial console to see the information live! But wouldn't it be great if you could gather that data and save it? You can! The Adalogger FeatherWing is a board with a real time clock and an SD card slot. Combine this with the AM2320, and we can save all that lovely data to the SD card and do whatever we like with it!

This project combines the Feather M0 Express, the Adalogger FeatherWing with Real Time Clock and SD Card, and the AM2320 Digital Temperature and Humidity Sensor. We use the short male and female Feather headers to keep the project slim. We add in a lithium ion polymer battery so the project can go anywhere. We'll use CircuitPython to write up a program to gather information from each of the sensors and save it to a log file on the SD card. Once we have some data to work with, we'll import it into a spreadsheet and turn it into an awesome graph!

Things You'll Need

Here are the items you'll need.

Adafruit Feather M0 Express - Designed for CircuitPython

We love all our Feathers equally, but this Feather is very special. It's our first Feather that is specifically designed for use with CircuitPython!...

Adalogger FeatherWing - RTC + SD Add-on For All Feather Boards

A Feather board without ambition is a Feather board without FeatherWings! This is the Adalogger FeatherWing: it adds both a battery-backed Real Time Clock and micro SD...

AM2320 Digital Temperature and Humidity Sensor

This little sensor looks an awful lot like the popular DHT11/DHT22 temperature and humidity sensors, but unlike classic DHT sensors, it has an I2C interface!...

Through-Hole Resistors - 10K ohm 5% 1/4W - Pack of 25

ΩMG! You're not going to be able to resist these handy resistor packs! Well, axially, they do all of the resisting for you!This is a 25 Pack of 10K...

Hook-up Wire Spool Set - 22AWG Solid Core - 6 x 25 ft

Perfect for bread-boarding, free wiring, etc. This box contains 6 spools of solid-core wire. The wire is easy to solder to and when bent it keeps its shape pretty well. We like to have...

Lithium Ion Battery - 3.7v 2000mAh

Lithium ion polymer (also known as 'lipo' or 'lipoly') batteries are thin, light and powerful. The output ranges from 4.2V when completely charged to 3.7V. This battery...

Handy To Have

  • Double sided tape - This will allow you to attach the battery to the bottom of the project. You can use any kind of double sided foam tape for this. We used adhesive hook and loop fasteners to allow for the battery to be easily removed if necessary. You could also wrap them all together with a cable tie. There are tons of options!

We want this project to be as compact and portable as possible. So, we're going to use the proto space on the Feather M0 Express to attach our sensor. Then, using the short Feather headers, we'll make a little Feather/FeatherWing sandwich with the Adalogger on top.

Let's get building!

Steps to Build the Data Logger Project

First solder the short male headers onto the Adalogger FeatherWing.

Solder the female headers on to the top of the Feather. (Remember the Adalogger FeatherWing will be sitting on top of the Feather).

Next, solder the 10K resistors between the 3V rail next to the proto space and the two holes shown in the images. They will be linking the SCL and SDA pins on the sensor to the 3V rail as the pull-up on each pin. To verify you're connecting the right spots, place your sensor through the holes on the end of the Feather proto area, and make sure that you're placing one end of the resistors on the same rows as the SCL and SDA pins on the sensor.


DO NOT TRIM THE RESISTOR LEADS IN THE PROTO SPACE. You will be using them to bridge three holes on the proto area together to link the resistors, the wires and the pins. You can trim the resistor leads on the 3V rail if you like.

Bend the resistor leads gently towards the middle of the Feather to get them out of the way as you continue to build your project.


Cut four pieces of wire at least 2" long. (This is much longer than needed but you can always trim it later.) We used solid core wire, 22AWG.


Strip the red and black wires (power and ground) to expose around 0.5in of wire. DO NOT TRIM THE EXCESS WIRE. We'll be using the ends of these two wires to bridge the power and ground pins on the sensor to the wires you just added.


Strip the yellow and blue wires (SCL and SDA) exposing enough to solder them in. If you're concerned, you can expose more wire, however, this end of these wires will not be used for anything further.


Now, solder each wire into the appropriate hole in the second set of holes in the proto area. Be sure to verify you're soldering them in the correct order and location! If you're unsure, place the sensor through the last set of holes and verify the correct order before continuing. The blue and yellow wires will end up next to one of the resistors.

Next, we're going to add the sensor.


First, bend the yellow and blue stripped wire flat towards the edge of the Feather, so they will be underneath the sensor. They should stick out past the edge of the Feather and out from beneath the sensor.

Next, place the sensor through the appropriate holes in the last section of the proto area. The sensor should be on top of the Feather - you should be soldering it from the bottom.


Be sure to leave enough space between the sensor and the Feather to bend the sensor outward.

Now we're going to begin bridging the pins to jumper them.


Using needle-nosed pliers, pull the first resistor lead past the yellow wire, straight towards the end of the Feather, and bend it around the appropriate sensor pin. We will use this lead to connect the three pins.


Use your soldering iron to bridge the three pins with solder, filling the space between with solder.


Then repeat for the second resistor lead, past the blue wire.


Flux is your friend! If you're having issues, try applying flux.


Once complete, you can trim the resistor leads if you like.

Next, you'll want to flip the board over. Carefully bend the sensor back and away from the Feather.


Use your soldering iron to bridge power and ground (red and black) to the appropriate pins on the sensor, using the excess wire we bent earlier to guide your solder.


Be careful not to melt the sensor while soldering these bridges.

Once you've bridged all four pins on their side of the board, flip the board over again. Carefully strip the red and black wires down to the appropriate size. Do not trim the wire yet! Red (power) will be connecting to the same 3V rail that the resistors are connected to. Black (ground) will be connecting to the ground rail opposite the 3V rail.


Using needle-nosed pliers, pull the red and black wires through their appropriate holes on the power and ground.


Then bend the SCL and SDA wires towards the SCL and SDA pins on the Feather, and strip them down to the appropriate size. When stripping, you'll want to bear in mind that you will be soldering these wires to those pins.


Carefully bend the yellow and blue wires into a tiny loop to fit around the pin.


Solder the blue wire to the SCL pin sticking through from the female header.


Solder the yellow wire to the SDA pin sticking through from the female header.

Flip the board over again so it's facing up. Your red and black wires should be poking through to the top.


Solder the red wire into the 3V rail.


Solder the black wire into the ground rail.

That's it! These images show what it should look like once completed.


And, now you can trim the rest of your wires. :)

Great job! Now that you have your data logger built, we will go through the CircuitPython code we'll be using.

Let's take a look!

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.

import time

import adafruit_am2320
import adafruit_sdcard
import analogio
import board
import busio
import digitalio
import storage

vbat_voltage = analogio.AnalogIn(board.D9)

i2c = busio.I2C(board.SCL, board.SDA)
am2320 = adafruit_am2320.AM2320(i2c)

SD_CS = board.D10
spi = busio.SPI(board.SCK, board.MOSI, board.MISO)
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))
            sdc.write("{}, {}, {}, {:.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!

You've built your data logger and put your code on the board. You kept it in the bathroom while you showered and put it in the freezer for a bit to get some varied data. Now what do we do with that data? We make graphs!

Remove the SD card from your data logger, and use an SD card reader to plug it into your computer. You should have a file called "log.txt" on it. Copy that file to your computer.

Note that some spreadsheet programs require you to change the extension to ".csv". Otherwise they don't know what to look for and may import incorrectly.

Next, open the file into a spreadsheet program of your choice. We've used Google Sheets. Open a new spreadsheet.

Click File and choose Import.

At the top of the Import File window, click Upload. Then click "Select a file from your computer" and choose your log.txt file.

No changes are needed in the Import File dialog box - the defaults work for our project.

If the data imports with empty columns, you can delete the empty columns.

Now we have our data! It's almost ready to graph.

For Google Sheets, the Chart Legend feature behaves a little strangely. There's no way to edit it once it's in the chart. However, you do have the option to "Use row 1 as headers". So a workaround is to forgo the first data point and instead change it to a title that reflects the data below it. As the chart will be using column A for the x-axis, it is unnecessary to change the first data point in that column. In fact, it seems to mess with the ability to use row 1 as headers, so we recommend changing the data as you see in the image.

It's time to graph! Click the Insert Chart icon located towards the right side of the toolbar.

Tah dah! A graph!

But, it looks a little plain. So let's dress it up a bit.

Click on the graph, find the three-dots-menu in the upper right corner, and click "Edit chart", then choose "Customize".


Click on "Chart & axis titles" and enter a title for your graph.


Click on "Legend" and choose where you'd like your legend to be.

And that's it! Now you've got an amazing graph of your data! Now you can have fun seeing what happens to temperature and humidity when you move your project to different environments.

Here's the explanation of our data. We put our data logger in the freezer in the beginning. Humidity levels in the freezer are level for the most part but drop initially before leveling out. When it's removed from the freezer, any moisture in the air condenses on the sensor (and the rest of the data logger, so be careful!) and causes the humidity to reach 99%. Then as the sensor warms up and the condensation clears, both temperature and humidity level off.

Try putting it next to the heat or air conditioning duct, near your humidifier, in your fridge, in the bathroom while you shower, in an open window on a cold day or anywhere else you can think of to get fun variable data to work with! Have fun!

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