USB Data logging - Seeeduino + Python

by

Setting the context for learning is a constant challenge for teachers.  Cries of "why are we learning xyz?" ring out when (for example) teaching the chemistry of alkanes and alkenes.  Likewise, pick any suitably abstract area of the curriculum and demonstrating the "why" can stretch the creative spark of many a colleague.

As a suitable segue, enter "Curriculum for Wales" where the subsidiarity of teachers is leveraged to allow them to design local curricula which respond to the needs of the young people the actually teach (as opposed to a centrally defined curriculum attempting to meet the needs of all learners).

It was whilst contemplating curriculum design that I happened to need some way of capturing voltage measurements over a protracted period of time (say, many hours).  Some active searching later and I was left with either purchasing a USB multimeter (£50+), purchasing some "data logging" kit designed for schools (and by designed, I mean "sanitised" with all the complexity hidden at the cost of £100+) or building something myself from equipment like the Arduino and its clones.  So as a use case, I chose the Arduino route.

A step back

Speaking of context, one thing that young people like to "investigate" is just about anything under the banner of "The best....".  The idea of pitting one item or brand against another seems to hit at some deep seated, visceral feeling that allows them to challenge the accepted authority.  There is nothing more passionate than a nine year old who has discovered that there is exactly the same medication in home brand (cheap) aspirin compared to the big brand (more expensive) alternatives --- especially if Mom / Dad swear by the big brand versions.  Talk about unleashing mini zealots onto the world.

Get building

With this in mind, I have been planning a sequence of learning investigating "The best battery"  Now, discussions over exactly what we mean by "best" (cheapest, longest lasting, least likely to leak, price to longevity ratio) the activity needed some way of measuring the lifespan of AA batteries.

We could have used devices such as torches and kept an eye on the bulb - but torches powered by a single AA tend be be LED with booster circuitry and can last for many many hours.  I have used those hand held fans in the past as they drain AA's quickly due to the power needs of the motors - but I don't have any to hand. 

Designing a circuit to load and drain a AA battery is quite easy.  Add a 10Ω resister across the battery and it will draw 150mV.  As AA batteries have a capacity of about 1500mAH (to keep the math easy) - a single 10Ω resister would drain it in about 10hrs.  4 x 10Ω in parallel would drain the battery in about 2.5hrs  - exactly what would work in class.

But I still needed something to monitor the voltage.  I could wire up a voltmeter and have the children check the voltage every 10mins or so over the course of 3hrs.  But this is 2021 and there must be a better way.

Arduino (Seeeduino XIAO)

Physical computing is part of Curriculum for Wales and ALL teachers in Primary are expected to be able to use "physical computing devices" with their children.  This is often Microbits and Lego Mindstorms, but in my world, this means Arduino and occasionally Raspberry PI.

The Seeduino is an Arduino compatible and highly capable device that cost about £6 at time of writing.  Out of the box it will read / write analogue (what I need) and digital voltages.

You will need:

1 x Seeeduino XIAO  (with the headers soldered on - so it can plug into the breadboard)

1 x breadboard

4 x 10Ω resistors  (as the "load" for the battery - higher Ω will drain the battery slower as less current will flow -- we want as much current as practical to flow to drain the battery quickest)

2 x 10k Ω resistors (1 connected to pin A1 (input) and one to GRD (ground)  of the Seeeduino -- these are for "safety" both to protect against too much current and from the battery being plugged in in reverse)

Make sure that the battery + is connected to the A1 input and the resistors -- this is easiest by connecting the + to the RED power rail of the breadboard.

Once you connect the battery into the circuit it will start to discharge and drain.  Even though the battery is rated at 1.5V, under this load, the first reading is likely to be 1.1V (or less)  (The 1.5V is the maximum, "open circuit" voltage and not what the battery delivers in actual use.  That is an interesting thing to discuss with young people -- is this "false" advertising?)

Build process

Seeeduino (available here > Pimoroni)

I now need to tell the Seeeduino what to actually do.   These devices are programmed via the Arduino IDE (available here). 

See comments in the code...


// the setup routine runs once when you press reset:
void setup() {
  // initialize serial communication at 9600 bits per second:
  Serial.begin(9600);
}

// the loop routine runs over and over again forever:
void loop() {
  // read the input on analog pin 1:
  int sensorValue = analogRead(A1);
  // Convert the analogue reading (which goes from 0 - 1023) to a voltage (0 - 1.5V):
  float voltage = sensorValue * (3.2 / 1023.0);  //adjusted to give value seen on multimeter
  // print out the value you read:
  Serial.println(voltage);
  delay(1000);
}

So now when the device is powered up it will sense voltage on pin A1 and print it back to the serial port --- which I can monitor via USB.

Python

Now the Seeeduino is monitoring the voltage, I need some way of picking that up and displaying it on screen.  Python is my go to language for this - quick, easy and there is likely to be a library for that somewhere.

My Python development environment is Anaconda and JupyterLabs (but anything will work as long as you can install additional libraries).

My code:

  • Checks for the USB com port that the Seeeduino is broadcasting on (using Pyserial)
  • Opens a blank Excel work book (using openpyxl)
  • Loops through 5 minutes of data, computes an average and saves that to the Excel file
  • Completes these 5 minute loops for up to 12 hours (I'm likely to end the program before that).

"""
Simple demonstration program to read Arduino data (Seeduino XIAO in my case) and to save into Excel
The Seeeduino is programmed to send the data from analogue pin 1 once per second
Develped under Jupyter Notebook / Windows 10 - could run from console - possibly cross platform
"""

import serial
import serial.tools.list_ports
import time

from datetime import datetime
from openpyxl import load_workbook

myFileName=r'D:\battery.xlsx'                             #datafile  (r' added to allow \ without escaping)
wb = load_workbook(filename=myFileName)                   #load workbook
ws = wb['Sheet1']                                         #identify worksheet


#code to identify the port the Arduino is attached to. Assumes only 1 USB serial device attached
#Tested only under Windows10

ports = list(serial.tools.list_ports.comports())
for p in ports:
    if 'USB Serial Device' in p.description:  #"USB Serial Device" is how Windows refers to Arduin devices
        
        serial = serial.Serial(p.device,9600)  #establish the serial port, assumes only one present
        
time.sleep(1)                                  # allow 1s to setup, probably fine without.

calibration = 0.013                            # determined by running with no input (should be "constant" for the device)

#Read the data

for x in range(144):                 # outerloop for total groups of 5 mins (12=1hr, 144 = 12hours)
    
    data =[]                        # empty list to store the data
    for i in range(300):            # 300 readings = 5 mins
        b = serial.readline()       # read a byte string
        string_n = b.decode()       # decode byte string into Unicode  
        string = string_n.rstrip()  # remove \n and \r
        flt = float(string)         # convert string to float
    
        data.append(flt)            # add flt to the end of data list
        
    
    now = datetime.now()
    current_time = now.strftime("%H:%M:%S")             #convert time to H:M:S format
    
    data_point = (sum(data)/len(data))-calibration      # form average over 5 minutes and remove the zero error
    data_point = float(int((data_point*1000)+0.5)/1000) # round to 3dp
    data_point = current_time + "," + str(data_point)   # combine time and data
    
    print (data_point)                                  # print to console to show working
    
    #write data to Excel file
    newRowLocation = ws.max_row +1                           #identify the lastrow and move down 1
    ws.cell(column=1,row=newRowLocation, value=data_point)   #put data in cell
    wb.save(filename=myFileName)                             #for integrity save file after each write
    

#close serial and Excel  
serial.close()
wb.close()

Results

Questions to consider:

  1. What does this show?
  2. When did the battery "run out"?
  3. Why after 540 is is flat?

Battery technology is complex - the chemistry of what's happening here is beyond primary learners.  But I opine that doesn't matter. What matters is the process and the stimulation of thinking.  Of course, what I really need is other batteries to compare this to.

That's the next job.