Count characters.
From the 'N' in SN, which is unique, SPO data starts with the 18th character following and the last character of BPM is the 29th.
Assuming the data is always formatted consistently, the value of SPO starts at the 38th character, BPM at the 47th. The entire line of text, excluding the final newline character (which gets stripped off if you use the code from Serial Input Basics) is 126 characters long, any other length can be considered invalid. Parsing as such is not needed when you have such a nicely formatted input, because you already know the position of all the numbers.
you could use sscanf(), e.g.
void setup() {
Serial.begin(115200);
char text[]="12/04/22 16:53:29 SN=00000000 SP02=099% BPM=071";
int SP02=0, BPM=0;
if(sscanf(text, "%*d/%*d/%*d %*d:%*d:%*d SN=%*d SP02=%d%% BPM=%d", &SP02, &BPM)==2)
Serial.println("read two parameters OK");
Serial.print("SP02 "); Serial.println(SP02);
Serial.print("BPM "); Serial.println(BPM);
}
void loop() {}
when run the serial monitor displays
06:20:10.022 -> read two parametersOK
06:20:10.022 -> SP02 99
06:20:10.022 -> BPM 71
the %*d conversion specification to sscanf() indicates the conversion is carried out and the result discarded
if the format is constant the sscanf can be simplified to
if(sscanf(&text[33], "SP02=%d%% BPM=%d", &SP02, &BPM)==2)
I was able to parse the string in isolation but where I keep getting stuck is turning the incoming transmission from the pulseox to a variable so it can be parsed. I have tried so many different ways but get errors. Here is my latest (still learning, please forgive my amateur ways):
#include <SoftwareSerial.h>
SoftwareSerial mySerial(2, 3); // RX, TX
void setup() {
Serial.begin(115200);
mySerial.begin(9600);
}
void loop() {
{
if (mySerial.available())
char serialOutput = mySerial.read();
int SP02=0, BPM=0;
sscanf(&serialOutput[33], "SP02=%d%% BPM=%d", &SP02, &BPM)==2);
Serial.print("SP02 "); Serial.println(SP02);
Serial.print("BPM "); Serial.println(BPM);
}
}
By the way, I am incredibly grateful for this community. Its been such a great help. Much appreciated!
tl;dr: Gather the characters one by one as they come in.
There is documentation and a crap-ton of example code floating around.
Take a look at this verbose but gentlemdiscussion:
Serial.read and Arduino: Here's the advanced stuff you should know
Serial.read() returns the next character in the input buffer, or -1 if there isn't any. That's why it returns an integer, to make room for -1 as well as the 256 legitimate character values.
One character. You need the entire string of characters, which is why ppl have pointed out and reminded you that the device transmissions end with specific known characters. This would be your clue to stop gathering characters, and treat the entire array of them what you gathered to any scrutiny to extract the values you are looking for.
You can also use Serial.available() to check first rather than after trying and seeing you failed. Oh, I see you did.
That is outlined in the page I linked above.
HTH
a7
you need to read a complete string upto the newline character
this reads from Serial but just change it to mySerial
#include <SoftwareSerial.h>
SoftwareSerial mySerial(2, 3); // RX, TX
void setup() {
Serial.begin(115200);
mySerial.begin(9600);
}
void loop() {
{
if (Serial.available()) {
char serialOutput[100] = {0};
Serial.readBytesUntil('\n', serialOutput, 100);
Serial.println(serialOutput);
int SP02 = 0, BPM = 0;
if (sscanf(serialOutput, "%*d/%*d/%*d %*d:%*d:%*d SN=%*d SP02=%d%% BPM=%d", &SP02, &BPM) == 2)
// if (sscanf(&serialOutput[33], "SP02=%d%% BPM=%d", &SP02, &BPM) == 2) // does not work???
Serial.println("two conversions OK");
Serial.print("SP02 "); Serial.println(SP02);
Serial.print("BPM "); Serial.println(BPM);
}
}
}
if I enter "12/04/22 16:53:29 SN=00000000 SP02=099% BPM=071" on the serial monitor the output is
18:21:39.137 -> 12/04/22 16:53:29 SN=00000000 SP02=099% BPM=071
18:21:39.137 -> two conversions OK
18:21:39.137 -> SP02 99
18:21:39.137 -> BPM 71
Edit: changed seconf sscanf() to
if (sscanf(&serialOutput[30], "SP02=%d%% BPM=%d", &SP02, &BPM) == 2)
and it worked
spoke too soon: Never mind, I see you are using readUntil, which I don't because it blocks.
That gets renewed every time you loop. Your code depends on the very rapid delivery of all the characters you expect.
True when you load up the serial monitor window and send a line of characters.
Less true or at least don't rely on it if characters are going to be arriving on their own sweet schedule…
a7
I'm getting some output on the serial monitor but it's not the actual data.
I am getting this output on the serial monitor:
But the actual PulseOx is showing 98 for SPO2 and 67 for BPM.
I changed the code to "mySerial" to ensure it was pulling from the software serial. Thoughts?
#include <SoftwareSerial.h>
SoftwareSerial mySerial(2, 3); // RX, TX
void setup() {
Serial.begin(115200);
mySerial.begin(9600);
}
void loop() {
{
if (mySerial.available()) {
char serialOutput[100] = {0};
mySerial.readBytesUntil('\n', serialOutput, 100);
mySerial.println(serialOutput);
int SP02 = 0, BPM = 0;
if (sscanf(serialOutput, "%*d/%*d/%*d %*d:%*d:%*d SN=%*d SP02=%d%% BPM=%d", &SP02, &BPM) == 2)
// if (sscanf(&serialOutput[33], "SP02=%d%% BPM=%d", &SP02, &BPM) == 2) // does not work???
Serial.println("two conversions OK");
Serial.print("SP02 "); Serial.println(SP02);
Serial.print("BPM "); Serial.println(BPM);
}
}
}
I zeroed the array because readByteUntil() does not put the terminator character '\n' or a zero byte on the end of the input and without zeroing the
Serial.println(serialOutput);
printed garbage on the end of the text - the zeroing could probably be removed in a final version where a printout was not required
the program of post #46 does assume the line of text arrives as a continuous stream - if it did arrive in bursts and readBytesUntil() could timeout a different approach would have to be taken, e.g. read a byte at time into an array until '\n' received then parse it or possibly parse it as it arrives?
change the above to
Serial.println(serialOutput);
so we can see the text which arrived
note that SoftwareSerial cannot transmit and receive at the same time so the above statement could be corrupting your received text
one reason why I don't use SoftwareSerial but devices with hardware serial ports
Here is what is coming out:
Here is the code:
#include <SoftwareSerial.h>
SoftwareSerial mySerial(2, 3); // RX, TX
void setup() {
Serial.begin(115200);
mySerial.begin(9600);
}
void loop() {
{
if (mySerial.available()) {
char serialOutput[100] = {0};
mySerial.readBytesUntil('\n', serialOutput, 100);
Serial.println(serialOutput);
int SP02 = 0, BPM = 0;
if (sscanf(serialOutput, "%*d/%*d/%*d %*d:%*d:%*d SN=%*d SP02=%d%% BPM=%d", &SP02, &BPM) == 2)
// if (sscanf(&serialOutput[33], "SP02=%d%% BPM=%d", &SP02, &BPM) == 2) // does not work???
Serial.println("two conversions OK");
Serial.print("SP02 "); Serial.println(SP02);
Serial.print("BPM "); Serial.println(BPM);
}
}
}
could you upload the serial monitor output in text form rather than an image
it is very difficult to test with information from an image
I refer to examples from Nick Gammon when it comes to non blocking serial, google his name he has lots of interesting stuff.
The following is an adaptation of his code to collect a message, at the end I used strok() to separate the string message at each space character into an array of 12 character strings that can be accessed by index.
The strings I chose to display on the serial monitor were the time and the SPO2 value, this was just for example.
#include <SoftwareSerial.h>
SoftwareSerial mySerial(2, 3); // RX, TX
const unsigned int MAX_MESSAGE_LENGTH = 200;
static char message[MAX_MESSAGE_LENGTH];
static unsigned int message_pos = 0;
char *pointer[12];
void setup() {
Serial.begin(115200);
mySerial.begin(9600);
}
void loop() {
//Check to see if anything is available in the serial receive buffer
while (myserial.available() > 0)
{
//Read the next available byte in the serial receive buffer
char inByte = myserial.read();
//Message coming in (check not terminating character) and guard for over message size
if ( inByte != '\n' && (message_pos < MAX_MESSAGE_LENGTH - 1) )
{
//Add the incoming byte to our message
message[message_pos] = inByte;
message_pos++;
}
//Full message received...
else
{
//Add null character to string
message[message_pos] = '\0';
char* ptr = strtok(message, " ");
byte i = 0;
while (ptr) {
pointer[i]=ptr;
ptr = strtok(NULL, " ");
i++;
}
Serial.println(pointer[3]);
Serial.println(pointer[1]);
//Reset for the next message
message_pos = 0;
}
}
}
Here you go!
12/05/22 14:29:52 SN=0000000000 SPO2=---% BPM=--- PI=--.--% SPCO=--.-% SPMET=--.-% DESAT=-- PIDELTA=⸮
SP02 0
BPM 0
+-- ALARM=0000 EXC=000824SP02 0
BPM 0
12/05/22 14:29:53 SN=0000000000 SPO2=---% BPM=--- PI=--.--% SPCO=--.-% SPMET=--.-% DESAT=-- PIDELTA=
SP02 0
BPM 0
+-- ALARM=0000 EXC=000824SP02 0
BPM 0
12/05/22 14:29:54 SN=0000000000 SPO2=---% BPM=--- PI=--.--% SPCO=--.-% SPMET=--.-% DESAT=-- PIDELTA=
SP02 0
BPM 0
+-- ALARM=0000 EXC=000824SP02 0
BPM 0
12/05/22 14:29:55 SN=0000000000 SPO2=---% BPM=--- PI=--.--% SPCO=--.-% SPMET=--.-% DESAT=-- PIDELTA=
SP02 0
BPM 0
+-- ALARM=0000 EXC=000824SP02 0
BPM 0
12/05/22 14:29:56 SN=0000000000 SPO2=---% BPM=--- PI=--.--% SPCO=--.-% SPMET=--.-% DESAT=-- PIDELTA=
SP02 0
BPM 0
+-- ALARM=0000 EXC=000824SP02 0
BPM 0
12/05/22 14:29:57 SN=0000000000 SPO2=---% BPM=--- PI=--.--% SPCO=--.-% SPMET=--.-% DESAT=-- PIDELTA=
SP02 0
BPM 0
+-- ALARM=0000 EXC=000824SP02 0
BPM 0
12/05/22 14:29:58 SN=0000000000 SPO2=---% BPM=--- PI=--.--% SPCO=--.-% SPMET=--.-% DESAT=-- PIDELTA=
SP02 0
BPM 0
+-- ALARM=0000 EXC=000824SP02 0
BPM 0
12/05/22 14:29:59 SN=0000000000 SPO2=---% BPM=--- PI=--.--% SPCO=--.-% SPMET=--.-% DESAT=-- PIDELTA=
SP02 0
BPM 0
+-- ALARM=0000 EXC=000824SP02 0
BPM 0
12/05/22 14:30:00 SN=0000000000 SPO2=---% BPM=--- PI=--.--% SPCO=--.-% SPMET=--.-% DESAT=-- PIDELTA=
SP02 0
BPM 0
+-- ALARM=0000 EXC=000824SP02 0
BPM 0
12/05/22 14:30:01 SN=0000000000 SPO2=---% BPM=--- PI=--.--% SPCO=--.-% SPMET=--.-% DESAT=-- PIDELTA=
SP02 0
BPM 0
+-- ALARM=0000 EXC=000824SP02 0
BPM 0
12/05/22 14:30:02 SN=0000000000 SPO2=---% BPM=--- PI=--.--% SPCO=--.-% SPMET=--.-% DESAT=-- PIDELTA=
SP02 0
BPM 0
+-- ALARM=0000 EXC=000824SP02 0
BPM 0
12/05/22 14:30:03 SN=0000000000 SPO2=---% BPM=--- PI=--.--% SPCO=--.-% SPMET=--.-% DESAT=-- PIDELTA=
SP02 0
BPM 0
+-- ALARM=0000 EXC=000824SP02 0
BPM 0
It looks like it's cutting off the incoming data.
there is no valid SP02 or BPM values in the above
also either the lines of received text are much longer than originally stated or there is corruption of incomming data - try making the receive buffer larger
char serialOutput[500] = {0};
or try the technique suggested by @sumguy in post #53
there is no valid SP02 or BPM values in the above
Correct. What happened to the output shown in Post #51?
This worked! Pretty cool to see how you did this too! It took me a few minutes to figure out what was going on. After you isolate the incoming message, you parsed it by the spaces and then stored each piece (or token?—still learning) in an array. Awesome! I am going to mess with this a bit more and see if I can isolate the O2 and BPM values as their own int variables. I think at that point I can switch boards to something like an Arduino MKR WiFi 1010, Particle Photon or ESP8266 and get the data up to a cloud service. I am thinking I should be able to use something like Blynk (blynk.io) to display the values on my phone. Aside from being able to see the data live when I am away, it will be awesome to log the data for trends. Thoughts or suggestions?
Slow your roll.
Don't start adding ingredients to this basic recipe until you are tots sure it is without flaws, that you understand it and are confident that it will not be the source of distractions.
The steps you outline are very much larger than the few you've taken to get this far.
And get all that new stuff working on its own with fake data that looks like it came through the process involved in this current sketch.
a7
Thoughts on an alternative approach, with rationale.
While I would commonly buffer the whole line, then parse it, in this case the monitor is verbose, the content very small. So I'd start by counting incoming characters, zeroing my counter on the CRLF of the previous line. Only extract those two items you're interested in, as you receive and discard the characters of the line. When you've buffered and processed both, simply read and discard the rest until you reset your counter.
This avoids having to store a very long string, process chaff to get at the wheat, and generally should slim your whole loop down nicely.
found the problem with scanf()
reading from the images of serial monitor output I read "SPO2=" (with capital O) as "SP02=" (with a zero character)
if (sscanf(serialOutput, "%*d/%*d/%*d %*d:%*d:%*d SN=%*d SPO2=%d%% BPM=%d", &SP02, &BPM) == 2)
e.g. sample test
05:51:49.270 -> 12/05/22 14:30:02 SN=0000000000 SPO2=088% BPM=009 PI=--.--% SPCO=--.-% SPMET=--.-% DESAT=-- PIDELTA=⸮
05:51:49.270 -> two conversions OK
05:51:49.270 -> SP02 88
05:51:49.270 -> BPM 9
this shows the importance of not uploading images of output but the actual text