I am trying to develop an arduino based sensor for respiratory rate in small animals. We get decent measurements of respiration via a force sensitive resistor (coin-sized) mounted on the back of the anesthetized animal. I managed to log the data via pyserial in Python with timestamps, but I would also like to live-plot the data for on-site monitoring of respiratory rate. I don't have much experience with this, but I managed to set up a script that gives a live plot - but it only plots 0 values, or higher values but after a significant delay (like 20s). The csv logging is also broken in this script (only 0 values, or significantly delayed higher values). Ideally, we would have a live plot with a refresh rate of ~100hz or more.
Any pointers on how I can achieve this with Python? Or if Python is too slow for this, any other way?
Arduino code:
#include <Streaming.h>
int fsrPin = 0; // the FSR and 10K pulldown are connected to a0
int fsrReading; // the analog reading from the FSR resistor divider
void setup(void) {
Serial.begin(115200);
}
void loop(void) {
fsrReading = analogRead(fsrPin);
Serial.print(fsrReading); // print the raw analog reading
Serial.println();
}
Python code:
import serial
import time
import matplotlib.pyplot as plt
from datetime import datetime, timedelta
import csv
def generate_output_filename():
now = datetime.now()
filename = now.strftime("output_%Y%m%d_%H%M%S.csv")
return filename
# Function to update the plot with new data
def update_plot(x_data, y_data, line, ax):
line.set_xdata(x_data)
line.set_ydata(y_data)
if len(x_data) > 0:
# Set x-axis to only show the last 10 seconds
max_time = max(x_data) # Latest timestamp
min_time = max_time - timedelta(seconds=10)
ax.set_xlim(min_time, max_time)
ax.set_ylim(0,1000)
ax.relim() # Recalculate limits
ax.autoscale_view(True, True, True) # Rescale the y-axis
plt.draw()
plt.pause(0.001) # A short pause to allow the plot to update
def readserial(comport, baudrate, timestamp=False):
# Setup for live plotting
plt.ion() # Start interactive mode
fig, ax = plt.subplots()
x_data, y_data = [], []
line, = ax.plot(x_data, y_data, 'r-') # Red line for the live data
plt.xlabel('Time')
plt.ylabel('Data')
plt.title('Live Data Plot')
ser = serial.Serial(comport, baudrate, timeout=None)
time.sleep(2) # give the arduino time to start.
ser.flushInput()
output_csv = generate_output_filename()
with open(output_csv, 'w', newline='') as csvfile:
csv_writer = csv.writer(csvfile)
csv_writer.writerow(['Timestamp', 'Data'])
try:
while True:
data = ser.readline().decode().strip() #strip newline characters.
if data and timestamp:
now = datetime.now()
formatted_timestamp = now.strftime('%H:%M:%S') + '.{:04d}'.format(int(now.microsecond / 1000))
csv_writer.writerow([formatted_timestamp, data])
print(f'{formatted_timestamp} > {data}') # Monitor in terminal.
# Update plot data
x_data.append(now) # Use datetime for x-axis
y_data.append(float(data)) # Convert the data to float for y-axis
update_plot(x_data, y_data, line, ax)
except KeyboardInterrupt:
print("Serial reading stopped by user (Ctrl-c in terminal)")
plt.ioff() # Turn off interactive mode.
plt.show() # Keep the window open even after the data stops coming in.
if __name__ == '__main__':
readserial('COM9', 115200, timestamp=True)
Python code without plotting, just logging to csv (this works in real-time).
import serial
import time
from datetime import datetime
import csv
def generate_output_filename():
now = datetime.now()
# Create a datetime string suitable for a filename, e.g., "output_20230405_123456.csv"
filename = now.strftime("output_%Y%m%d_%H%M%S.csv")
return filename
def readserial(comport, baudrate, timestamp=False):
ser = serial.Serial(comport, baudrate, timeout=None) # 1/timeout is the frequency at which the port is read
time.sleep(2) # give the arduino time to start.
ser.flushInput()
output_csv = generate_output_filename() # Generate filename with current datetime
with open(output_csv, 'w', newline='') as csvfile:
csv_writer = csv.writer(csvfile)
csv_writer.writerow(['Timestamp', 'Data']) # Write the header row
try:
while True:
data = ser.readline().decode().strip() #'utf-8', errors='ignore'
if data and timestamp:
now = datetime.now()
formatted_timestamp = now.strftime('%H:%M:%S') + '.{:04d}'.format(int(now.microsecond / 1000))
csv_writer.writerow([formatted_timestamp, data])
print(f'{formatted_timestamp} > {data}') # Optional: monitor in terminal.
except KeyboardInterrupt:
print("Serial reading stopped by user. (Ctrl-c in terminal)")
if __name__ == '__main__':
readserial('COM9', 115200, timestamp=True)```


