Capturing an analog signal with Arduino and python

Last week I salvaged a magnetic sensor from a stationary bike and connected it to my Arduino Uno in order to analyze the sensor’s behavior with the ADC converters.

I wanted to capture a good enough signal, so I searched about the Arduino capabilities:

From analogRead() reference description:

It takes about 100 microseconds (0.0001 s) to read an analog input, so the maximum reading rate is about 10,000 times a second.

In order to include the overhead of the code, I decided to read a value each 200μs. The ADC converters read a 10-bits value, so in order to transmit the readings through serial port, if I pack the 10-bits values in 2 bytes, I need 80.000bps. The right setting for the “ Serial.begin() ” Arduino function will then be 115200 , which is the smallest value bigger than 80000. With this information, I wrote the following sketch:

enum { ANALOG_INPUT_PIN = A0, UPDATE_INTERVAL_MICROS = 200, MODE_2_CONSECUTIVE_READS = 5000, }; unsigned long next_update; int reads; int mode; void setup() { Serial.begin(115200); analogReference(EXTERNAL); mode = 0; } void mode1() { int analog_value = analogRead(ANALOG_INPUT_PIN); Serial.println(analog_value); delay(100); } void mode2() { unsigned long current_micros; current_micros = micros(); if(current_micros >= next_update) { byte b1, b2; int analog_value; next_update = next_update + UPDATE_INTERVAL_MICROS; analog_value = analogRead(ANALOG_INPUT_PIN); reads--; b1 = analog_value&0xFF; b2 = ( analog_value >> 8 ) & 0xFF; Serial.write(b1); Serial.write(b2); if(reads <= 0) { mode = 0; Serial.write(0xFF); Serial.write(0xFF); } } } void loop() { if(Serial.available() > 0) { int rcv; rcv = Serial.read(); if(rcv == '1') { mode = 1; } else if(rcv == '2') { mode = 2; reads = MODE_2_CONSECUTIVE_READS; next_update = micros(); } else if(rcv == '0') { if(mode == 2) { Serial.write(0xFF); Serial.write(0xFF); } mode = 0; } } switch(mode) { case 1: mode1(); break; case 2: mode2(); break; default: delay(200); break; } }

The program has two main working modes that can be enabled/disabled by transmitting a character to the serial port. The mode “1” consists of reading the analog pin and sending the decimal value through the serial port, and it’s for rough readings and debug, the mode “2” reads 5000 times the analog pin, once every 200 microseconds, in order to have a full second of captured signal; it transmits the data through the serial port for each sample that is read.

From the PC side I setup a python script that uses the pySerial library (Debian/Ubuntu package: python-serial) to communicate with Arduino, and then displays the data on a plot using matplotlib (Debian/Ubuntu package: python-matplotlib). A tricky fact is that when pySerial opens the serial port, the Arduino is reset, and the script must wait some time (1.5 seconds is enough) before communicating.

#!/usr/bin/python import time import serial import matplotlib.pyplot as plt max_valid_read = 1023 reference_voltage = 3.3 update_interval = 2e-4 ser = serial.Serial( port = '/dev/ttyACM0', baudrate = 115200, ) time.sleep(1.5) ser.flush() ser.write('2') r = 0 r_time = 0 analog_reads = []; analog_times = []; while r <= max_valid_read: b1 = ord(ser.read(1)) b2 = ord(ser.read(1)) r = b1 + b2*256 if(r <= max_valid_read): analog_reads.append((r*reference_voltage)/max_valid_read) analog_times.append(r_time) r_time = r_time + update_interval plt.plot(analog_times, analog_reads) plt.show()

The scripts sends a character that enables mode “2” to the Arduino, which then sends the analog data back to the PC. The collected data is then plotted on a figure like in the screenshot.

The figure illustrates the data captured while I waved a magnet in front of the sensor. In the normal working condition the sensor’s connectors are open, so the readings are high because of the pull-up resistors that I put. When a magnetic field is applied, the connectors are closed, so the readings are low because the analog pin is connected to ground. According to its behavior this sensor can actually be connected to the digital inputs of Arduino, but in the meantime I got some useful tools ready to analyze analog input.