Arduino and RPi Bidirectional Comm with Pulse Sensor

I am working on a project that uses an Arduino to take in readings from a pulse sensor and temperature sensor for one minute. First, the Pi would send the Arduino an instruction ("s\n") to tell the Arduino to start the pulse sensor reading. As the pulse sensor data is coming in, the Pi stores it in an array. When there is no more incoming pulse sensor readings coming from the Arduino (i.e. one minute is over), the Pi sends the Arduino another instruction ("f\n") to tell the Arduino that the data collection and blinks the LED.

Right now I am just testing the pulse sensor part of it but it doesn't seem like the Pi even has any serial data to read. When I try to run the code on the Pi it gives an error after about 25s saying there is a zero division error in the avgbpm function i.e. datalist is empty. When I debug it, the code skips the body of the if statement in the while loop and just goes to pulse = avgbpm(pulsedata). Meaning that ser.in_waiting is not available. So I've written the getdata function incorrectly, but I don't see why. Here is my Arduino and Pi code, I appreciate all the help.

Arduino:

#define USE_ARDUINO_INTERRUPTS true
#include <PulseSensorPlayground.h>

const int led = 13; // led pin
const int pulsePin = A0; // pulse pin
unsigned long start;
unsigned long period;
const int threshold = 550; // use get started project to find this val
int pulse; 
String process;

PulseSensorPlayground pulseSensor;

void setup()
{
  Serial.begin(9600); 

  pulseSensor.analogInput(pulsePin);
  pulseSensor.setThreshold(threshold);
  pulseSensor.setSerial(Serial); // output serial pulse data
}

void loop()
{
  if (Serial.available())  // if there is instruction coming from the Pi
  {
    process = Serial.readStringUntil('\n'); // read the instruction
    process.trim();
    if (process.equals("r")) // recording starts, led on
    {
      digitalWrite(led, HIGH);
    }
    else if (process.equals("e")) // recording ends, led off
    {
      digitalWrite(led, LOW);
    }
    else if (process.equals("f")) // blink led when finish data collection
    {
      blinkfinish();
    }
    else if (process.equals("s")) // start data collection
    {
      start = millis(); // measure temp and bpm for one minute
      getdata();
    }
  }
}

void getdata() // function to get pulse sensor data
{
  while (millis() <= start + 60000) // will stop collecting data after one minute
  {
    pulse = pulseSensor.getBeatsPerMinute(); // bpm
    if (pulseSensor.sawStartOfBeat()) // check if a reading is detected
    {
    Serial.println(pulse); // if it is detected, output bpm
    }
    delay(20);
  }
  
}

void blinkfinish()
{
  digitalWrite(led, HIGH);
  delay(1000);
  digitalWrite(led, LOW);
  delay(1000);
  digitalWrite(led, HIGH);
  delay(1000);
  digitalWrite(led, LOW);
}

Pi:

import serial


def avgbpm(datalist):  # average bpm int
    return float(sum(datalist)/len(datalist))


ser = serial.Serial('/dev/ttyUSB0', 9600, timeout=1)  # open serial port

pulsedata = []

ser.write(b"s\n")  # tell arduino to start data collection
while True:
    if ser.in_waiting > 0: 
        data = ser.readline().decode('utf-8').rstrip()
        pulsedata.append(int(data))  
    else: 
        ser.write(b"f\n")  # tell arduino data collection is done
        break

pulse = avgbpm(pulsedata)

when you open the Serial port with

ser = serial.Serial('/dev/ttyUSB0', 9600, timeout=1)  # open serial port

the arduino likely reboots - which takes a bit of time to reach the setup() where you do

  Serial.begin(9600); 

it is likely that the

ser.write(b"s\n")  # tell arduino to start data collection

is never received because it's sent tenth of microseconds after opening the port and the arduino is not ready yet

try adding a 2s pause after opening the port, or best have the Arduino send a code in the setup after the begin() - could be just one byte - to say it's ready and wait for that signal before proceeding with your python script

My experience is that I found best to use tasks in python to listen to the Serial port, I gave an example in this post

1 Like

Hi,
Why does the Pi have to tell the Arduino (what model) when to take readings.
Can't the Arduino keep sending real time updated readings and the Pi just read its input when ever it needs it.

Can't the Pi do all the work, surely it can read some inputs and process the data on its own.

Thanks.. Tom.. :smiley: :+1: :coffee: :australia:

ser.write(b"s\n")  # tell arduino to start data collection
while True:
    if ser.in_waiting > 0: 
        data = ser.readline().decode('utf-8').rstrip()
        pulsedata.append(int(data))  
    else: 
        ser.write(b"f\n")  # tell arduino data collection is done
        break

pulse = avgbpm(pulsedata)

First of all, Arduino doesn't send data using UTF-8 encoding so that decode seems to be useless to me.
By the way, with the above RPi code I suspect that after sending "s" command Arduino hasn't the time to send any data before the "if" is executed, so in_waiting is zero, running the "else" making the while exit immediately with an empty pulsedata.
IMHO you should either wait for the first data after sending "s" command, and/or add a delay before the "if", to let Arduino send the expected values.
I also suggest you to raise serial speed, change the "getdata()" function by chaging the "while" with millis(), and use an "end" signal after last byte sent to let RPi properly exit from the while loop after received it. For example:

void getdata() // function to get pulse sensor data
{
  // Always do this to avoid millis overflows
  while (millis() - start <= 60000) // will stop collecting data after one minute
  {
    pulse = pulseSensor.getBeatsPerMinute(); // bpm
    if (pulseSensor.sawStartOfBeat()) // check if a reading is detected
    {
      Serial.println(pulse); // if it is detected, output bpm
    }
    delay(20);
  } 
  // Tell RPi the samples are over (and exit the data waiting loop)
  Serial.println("e");
}

thank you, i added the 2s pause after port opening and it solved that particular issue!

thank you for this suggestion because i went with this in the end. i think i was overcomplicating the process.

appreciate your explanation. i made those changes to the while loop. thank you!

Hi,
Is there a reason you have two controllers, when the Pi could do it all?

Thanks.. Tom.. :smiley: :+1: :coffee: :australia:

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.