Sunday, 15 July 2018

Data Logging with Feather and CircuitPython


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!

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!

Building the Data Logger

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).





    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!


    CircuitPython Code

    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.
    1. import time
    3. import adafruit_am2320
    4. import adafruit_sdcard
    5. import analogio
    6. import board
    7. import busio
    8. import digitalio
    9. import storage
    11. vbat_voltage = analogio.AnalogIn(board.D9)
    13. i2c = busio.I2C(board.SCL, board.SDA)
    14. am2320 = adafruit_am2320.AM2320(i2c)
    16. SD_CS = board.D10
    17. spi = busio.SPI(board.SCK, board.MOSI, board.MISO)
    18. cs = digitalio.DigitalInOut(SD_CS)
    19. sd_card = adafruit_sdcard.SDCard(spi, cs)
    20. vfs = storage.VfsFat(sd_card)
    21. storage.mount(vfs, "/sd_card")
    24. def get_voltage(pin):
    25. return (pin.value * 3.3) / 65536 * 2
    28. print("Logging temperature and humidity to log file")
    30. initial_time = time.monotonic()
    32. while True:
    33. try:
    34. with open("/sd_card/log.txt", "a") as sdc:
    35. temperature = am2320.temperature
    36. humidity = am2320.relative_humidity
    37. battery_voltage = get_voltage(vbat_voltage)
    38. current_time = time.monotonic()
    39. time_stamp = current_time - initial_time
    40. print("Seconds since current data log started:", int(time_stamp))
    41. print("Temperature:", temperature)
    42. print("Humidity:", humidity)
    43. print("VBat voltage: {:.2f}".format(battery_voltage))
    44. print()
    45. sdc.write("{}, {}, {}, {:.2f}\n".format(
    46. int(time_stamp), temperature,
    47. humidity, battery_voltage)
    48. )
    49. time.sleep(3)
    50. except OSError:
    51. pass
    52. except RuntimeError:
    53. pass


    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!

    Graphing Your Data

    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!

    No comments:

    Post a Comment