Go Down

Topic: Measuring ambient air oxygen level via LuminOx Optical Oxygen Sensor (Read 1 time) previous topic - next topic

Paul_KD7HB

Your program timing is only based on how often you send the poll message. Completely clean up the message reading code. The reading can be done as often and as fast as possible and nothing will happen.

If this was my program, I would use the logic of " doing many things at once", using the "millis" to watch the milliseconds go by and when your last poll message was sent more than 600,000 milliseconds ago, it's time to send another poll. And save the current millis value as the value for the last poll time.

Paul


Triaenodon

Ok, thanks!
New attempt:

I red the very good explained examples from Robin2 and tinkered based on this two posts ("Serial Input Basics - updated" and "Demonstration code for several things at the same time") the following code. In general it works fine as I want it. But there is still a bug inside, I can't find. Their are somehow randomly wrong reads (-->false data). First I thought to see a pattern, like every second read the value for pressure and percentage were incomplete, but the pattern changes with time. Maybe and hopefully one of you has an idea?

Here's the code and further down an example of the read.

Many thank's in advance!
Triaenodon

Code:
Code: [Select]
#include <SoftwareSerial.h>
/*
sensor Pin3(TX) to Arduino Pin 10(RX)
sensor Pin4(RX) to Arduino Pin 11(TX)
*/
SoftwareSerial mySerial(10, 11); // RX, TX

const byte numChars = 43;
char receivedChars[numChars];
char tempChars[numChars];     // temporary array for use when parsing
      
float oxygen = 0.0;  // variables to hold the parsed data
float temperature = 0.0;
int pressure = 0;
float percentage = 0.0;

boolean newData = false;

unsigned long currentMillis = 0;
unsigned long previousReadOxySenMillis = 0;
//unsigned long previousReadDHTMillis = 0;
const int ReadOxySenInterval = 5000;

//============

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

//============

void loop() {
    currentMillis = millis();  // capture the latest value of millis()
    updateReadOxySen();
}

//============

void recvWithStartEndMarkers() {
    static boolean recvInProgress = false;
    static byte ndx = 0;
    char startMarker = 'O';   //first sign from the data stream
    char endMarker = 'e';     //from here on I don't need the data
    char rc;

    while (mySerial.available() > 0 && newData == false) {
        rc = mySerial.read();

        if (recvInProgress == true) {
            if (rc != endMarker) {
                receivedChars[ndx] = rc;
                ndx++;
                if (ndx >= numChars) {
                    ndx = numChars - 1;
                }
            }
            else {
                receivedChars[ndx] = '\0'; // terminate the string
                recvInProgress = false;
                ndx = 0;
                newData = true;
            }
        }

        else if (rc == startMarker) {
            recvInProgress = true;
        }
    }
}

//============

void parseData() {      // split the data into its parts

    char * strtokIndx; // this is used by strtok() as an index

    //data from sensor send looks like: "O xxxx.x T yxx.x P xxxx % xxx.xx e xxxx\r\n"
    //e.g. "O 0020.1 T +19.3 P 1013 % 020.16 e 0001\r\n"

    strtokIndx = strtok(tempChars,"T");      // get the first part - the string
    oxygen = atof(strtokIndx); // copy it to oxygen
    
    strtokIndx = strtok(NULL, "P"); // this continues where the previous call left off
    temperature = atof(strtokIndx);     // convert this part to a float

    strtokIndx = strtok(NULL, "%"); // this continues where the previous call left off
    pressure = atoi(strtokIndx);     // convert this part to an integer

    strtokIndx = strtok(NULL, "e"); // this continues where the previous call left off
    percentage = atof(strtokIndx);     // convert this part to a float
}

//============

void showParsedData() {
    Serial.print("Oxygen ");
    Serial.println(oxygen);
    Serial.print("Temperature ");
    Serial.println(temperature);
    Serial.print("Pressure ");
    Serial.println(pressure);
    Serial.print("Percent ");
    Serial.println(percentage);    
}


void updateReadOxySen() {
    if (currentMillis - previousReadOxySenMillis >= ReadOxySenInterval) {
          // time is up, so make anew read
        recvWithStartEndMarkers();
        if (newData == true) {
            strcpy(tempChars, receivedChars);
                // this temporary copy is necessary to protect the original data
                //   because strtok() used in parseData() replaces the commas with \0
            parseData();
            showParsedData();
            newData = false;
        }          
          // and save the time of change
       previousReadOxySenMillis += ReadOxySenInterval;
    }
}



Sample read:

Quote
.87
Oxygen 195.00
Temperature 27.60
Pressure 984
Percent 19.82
Oxygen 195.00
Temperature 27.70
Pressure 98
Percent 0.00
Oxygen 195.30
Temperature 27.60
Pressure 984
Percent 19.84
Oxygen 195.30
Temperature 27.50
Pressure 98
Percent 0.00
Oxygen 195.40
Temperature 27.60
Pressure 984
Percent 19.86
Oxygen 195.50
Temperature 27.70
Pressure 98
Percent 0.00
Oxygen 195.50
Temperature 27.60
Pressure 984
Percent 19.87
Oxygen 195.50
Temperature 27.60
Pressure 98
Percent 0.00
Oxygen 195.50
Temperature 27.70
Pressure 984
Percent 19.87
Oxygen 195.50
Temperature 27.60
Pressure 98
Percent 0.00
Oxygen 195.50
Temperature 27.60
Pressure 984
Percent 19.87
Oxygen 195.50
Temperature 27.70
Pressure 98
Percent 0.00
Oxygen 195.60
Temperature 27.70
Pressure 984
Percent 19.88
Oxygen 195.60
Temperature 27.60
Pressure 98
Percent 0.00
Oxygen 195.70
Temperature 27.50
Pressure 984
Percent 19.88
Oxygen 195.70
Temperature 27.70
Pressure 98
Percent 0.00
Oxygen 195.70
Temperature 27.70
Pressure 984
Percent 19.89
Oxygen 195.70
Temperature 27.70
Pressure 0
Percent 19.89
Oxygen 195.70
Temperature 27.70
Pressure 0
Percent 19.89
Oxygen 19.00
Temperature 27.60
Pressure 984
Percent 19.89
Oxygen 195.70
Temperature 0.00
Pressure 984
Percent 19.89



Triaenodon

Oh no!
If I set ReadOxySenInterval to e.g. 60000, I don't get data anymore. I'm disappointed.  :(

Paul_KD7HB

Oh no!
If I set ReadOxySenInterval to e.g. 60000, I don't get data anymore. I'm disappointed.  :(
Look at what you did!!!

const int ReadOxySenInterval = 5000;

What is the largest value that can go into an int? Why is this not also a "long"?

Paul

Triaenodon

Hello Paul,

thanks a very lot! I was blind to that and I wonder that I didn't got an error message while compiling.
Changed it to long and now its working again, but still have the problem, that every second read goes wrong.

Oxygen 200.20
Temperature 22.10
Pressure 988
Percent 20.27                correct!       
Oxygen 200.30
Temperature 22.00
Pressure 98                   wrong
Percent 0.00                  wrong
Oxygen 198.50
Temperature 23.70
Pressure 988
Percent 20.09                correct
Oxygen 198.40
Temperature 23.80
Pressure 98                   wrong
Percent 0.00                  wrong
Oxygen 197.60
Temperature 24.90
Pressure 988
Percent 20.00                correct
Oxygen 197.60
Temperature 24.80
Pressure 98                   wrong
Percent 0.00                  wrong

Any suggestions, maybe?

Thanks
Triaenodon

Triaenodon

AHHH,

found another issue maybe:
The sensor is sending and expecting 3.3 V USART. I connected simply to Pin 10 and 11 which work with 5 V. Can that cause the misread? And when, is there a simple tinker solution or do I have to buy a level shifter like e.g. BSS138 to solve that?

Best regards
Triaenodon

Triaenodon

Hello,

thanks to your help. It works now very reliable. The poll mode did not work for me.
I had a bug in the function "void recvWithStartEndMarkers()".
Additionally I installed the mentioned level shifter.

That's what my code looks like now:

Code: [Select]

#include <SoftwareSerial.h>
/*
sensor Pin3(TX) to Arduino Pin 10(RX)
sensor Pin4(RX) to Arduino Pin 11(TX)
*/
SoftwareSerial mySerial(10, 11); // RX, TX

const byte numChars = 41;
char receivedChars[numChars];
char tempChars[numChars];     // temporary array for use when parsing
      
float oxygen = 0.0;  // variables to hold the parsed data
float temperature = 0.0;
int pressure = 0;
float percentage = 0.0;

boolean newData = false;

unsigned long currentMillis = 0;
unsigned long previousReadOxySenMillis = 0;
//unsigned long previousReadDHTMillis = 0;
const long ReadOxySenInterval = 10000;

//============

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

//============

void loop() {
    currentMillis = millis();  // capture the latest value of millis()
    updateReadOxySen();
}

//============

void recvWithStartEndMarkers() {
    static boolean recvInProgress = false;
    static byte ndx = 0;
    char startMarker = 'O';
    char endMarker = 'e';
    char rc;

    while (mySerial.available() > 0 && newData == false) {
        rc = mySerial.read();

        if (recvInProgress == true) {
            if (rc != endMarker) {
                receivedChars[ndx] = rc;
                ndx++;
                if (ndx >= numChars) {
                    ndx = numChars - 1;
                }
            }
            else {
                receivedChars[ndx] = '\0'; // terminate the string
                recvInProgress = false;
                ndx = 0;
                newData = true;
            }
        }

        else if (rc == startMarker) {
            recvInProgress = true;
        }
    }
}

//============

void parseData() {      // split the data into its parts

    char * strtokIndx; // this is used by strtok() as an index

    //data from sensor send looks like: "O xxxx.x T yxx.x P xxxx % xxx.xx e xxxx\r\n"
    //e.g. "O 0020.1 T +19.3 P 1013 % 020.16 e 0001\r\n"

    strtokIndx = strtok(tempChars,"T");      // get the first part - the string
    oxygen = atof(strtokIndx); // copy it to oxygen
    
    strtokIndx = strtok(NULL, "P"); // this continues where the previous call left off
    temperature = atof(strtokIndx);     // convert this part to a float

    strtokIndx = strtok(NULL, "%"); // this continues where the previous call left off
    pressure = atoi(strtokIndx);     // convert this part to an integer

    strtokIndx = strtok(NULL, "e"); // this continues where the previous call left off
    percentage = atof(strtokIndx);     // convert this part to a float
}

//============

void showParsedData() {
    Serial.print("Oxygen ");
    Serial.println(oxygen);
    Serial.print("Temperature ");
    Serial.println(temperature);
    Serial.print("Pressure ");
    Serial.println(pressure);
    Serial.print("Percent ");
    Serial.println(percentage);    
}


void updateReadOxySen() {
    if (currentMillis - previousReadOxySenMillis >= ReadOxySenInterval) {
          // time is up, so make anew read
        recvWithStartEndMarkers();
        if (newData == true) {
            strcpy(tempChars, receivedChars);
                // this temporary copy is necessary to protect the original data
                //   because strtok() used in parseData() replaces the commas with \0
            parseData();
            showParsedData();
            newData = false;
        }          
          // and save the time of change
       previousReadOxySenMillis += ReadOxySenInterval;
    }
}

Triaenodon

Hello, it's me again, sorry!


I was to rashly  :smiley-confuse:

In general the above code is working fine, but there must be still a bug inside, I can't find out.
Every second read the value for pressure and percentage is wrong.

Quote
Output:
15:29:15.344 -> Oxygen 197.40
15:29:15.377 -> Temperature 22.90
15:29:15.377 -> Pressure 985
15:29:15.411 -> Percent 20.04
15:29:17.337 -> Oxygen 197.40
15:29:17.370 -> Temperature 22.80
15:29:17.370 -> Pressure 98
15:29:17.403 -> Percent 0.00
15:29:19.330 -> Oxygen 197.40
15:29:19.363 -> Temperature 22.80
15:29:19.397 -> Pressure 985
15:29:19.397 -> Percent 20.04
15:29:21.357 -> Oxygen 197.40
15:29:21.357 -> Temperature 22.90
15:29:21.390 -> Pressure 98
15:29:21.390 -> Percent 0.00

Both values are read from the sensor. It looks like the sensor is sending wrong values every second time.
If I try another sensor (I have two of them), it's the same behavior. After a while the wrong readings become randomly.
If I read in a very simple way, the sensors are sending correct data.

Simple way:

Code: [Select]


#include <SoftwareSerial.h>

SoftwareSerial mySerial(10, 11); // RX, TX

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

void loop() {
 if (mySerial.available())  {
    Serial.write(mySerial.read());
    }
}



Maybe someone has an idea, where I should search for a solution? I ran out of ideas. :(

Many thanks in advance!
Triaenodon

wildbill

The '0000' on the end of your data is confusing subsequent attempts to read and parse data. You need to read it and throw it away.

Triaenodon


Triaenodon

If I change "char endMarker = 'e';" to "char endMarker = '\n';" in the function "recvWithStartEndMarkers()" and add

strtokIndx = strtok(NULL, "\n"); // this continues where the previous call left off
errorox = atoi(strtokIndx);     // convert this part to an integer

to the function "parseData()", I can't notice any change in the missreadings.


Triaenodon

Code: [Select]

#include <SoftwareSerial.h>
/*
sensor Pin3 OxySen (TX) to Arduino Pin 10(RX)
sensor Pin4 OxySen (RX) to Arduino Pin 11(TX)
*/
SoftwareSerial mySerial(10, 11); // RX, TX

const byte numChars = 43;
char receivedChars[numChars];
char tempChars[numChars];     // temporary array for use when parsing
     
float oxygen = 0.0;  // variables to hold the parsed data
float tempo = 0.0;
int pressure = 0;
float percentage = 0.0;
int errorox = 0;

boolean newData = false;

unsigned long currentMillis = 0;
unsigned long previousReadOxySenMillis = 0;
const long ReadOxySenInterval = 2000;

//============

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

//============

void loop() {
    currentMillis = millis();  // capture the latest value of millis()
    updateReadOxySen();
}

//============

void recvWithStartEndMarkers() {
    static boolean recvInProgress = false;
    static byte ndx = 0;
    char startMarker = 'O';
    //char endMarker = 'e';
    char endMarker = '\n';
    char rc;

    while (mySerial.available() > 0 && newData == false) {
        rc = mySerial.read();

        if (recvInProgress == true) {
            if (rc != endMarker) {
                receivedChars[ndx] = rc;
                ndx++;
                if (ndx >= numChars) {
                    ndx = numChars - 1;
                }
            }
            else {
                receivedChars[ndx] = '\0'; // terminate the string
                recvInProgress = false;
                ndx = 0;
                newData = true;
            }
        }

        else if (rc == startMarker) {
            recvInProgress = true;
        }
    }
}

//============

void parseData() {      // split the data into its parts

    char * strtokIndx; // this is used by strtok() as an index

    //data from sensor send looks like: "O xxxx.x T yxx.x P xxxx % xxx.xx e xxxx\r\n"
    //e.g. "O 0020.1 T +19.3 P 1013 % 020.16 e 0001\r\n"

    strtokIndx = strtok(tempChars,"T");      // get the first part - the string
    oxygen = atof(strtokIndx); // copy it to oxygen
   
    strtokIndx = strtok(NULL, "P"); // this continues where the previous call left off
    tempo = atof(strtokIndx);     // convert this part to a float

    strtokIndx = strtok(NULL, "%"); // this continues where the previous call left off
    pressure = atoi(strtokIndx);     // convert this part to an integer

    strtokIndx = strtok(NULL, "e"); // this continues where the previous call left off
    percentage = atof(strtokIndx);     // convert this part to a float

    strtokIndx = strtok(NULL, "\n"); // this continues where the previous call left off
    errorox = atoi(strtokIndx);     // convert this part to an integer
   
}

//============

void showParsedData() {
    Serial.print("Oxygen ");
    Serial.println(oxygen);
    Serial.print("TemperatureO ");
    Serial.println(tempo);
    Serial.print("Pressure ");
    Serial.println(pressure);
    Serial.print("Percent ");
    Serial.println(percentage);
    Serial.print("Error from sensor ");
    Serial.println(errorox);   
}


void updateReadOxySen() {
    if (currentMillis - previousReadOxySenMillis >= ReadOxySenInterval) {
          // time is up, so make anew read
        recvWithStartEndMarkers();
        if (newData == true) {
            strcpy(tempChars, receivedChars);
                // this temporary copy is necessary to protect the original data
                //   because strtok() used in parseData() replaces the commas with \0
            parseData();
            showParsedData();
            newData = false;
        }           
          // and save the time of change
       previousReadOxySenMillis += ReadOxySenInterval;
    }
}


output looks like:

17:58:56.402 -> Oxygen 197.70
17:58:56.436 -> TemperatureO 23.00
17:58:56.436 -> Pressure 988
17:58:56.469 -> Percent 20.01
17:58:56.469 -> Error from sensor 0
17:58:58.428 -> Oxygen 197.70
17:58:58.428 -> TemperatureO 23.00
17:58:58.461 -> Pressure 98
17:58:58.461 -> Percent 0.00
17:58:58.495 -> Error from sensor 0
17:59:00.421 -> Oxygen 197.70
17:59:00.421 -> TemperatureO 23.10
17:59:00.454 -> Pressure 988
17:59:00.454 -> Percent 20.01
17:59:00.488 -> Error from sensor 0
17:59:02.413 -> Oxygen 197.70
17:59:02.413 -> TemperatureO 23.00
17:59:02.481 -> Pressure 98
17:59:02.481 -> Percent 0.00
17:59:02.481 -> Error from sensor 0
17:59:04.406 -> Oxygen 197.70
17:59:04.406 -> TemperatureO 22.90
17:59:04.439 -> Pressure 988
17:59:04.439 -> Percent 20.01
17:59:04.473 -> Error from sensor 0
17:59:06.399 -> Oxygen 197.70
17:59:06.432 -> TemperatureO 23.00
17:59:06.432 -> Pressure 98
17:59:06.465 -> Percent 0.00
17:59:06.465 -> Error from sensor 0
17:59:08.391 -> Oxygen 197.70
17:59:08.425 -> TemperatureO 23.10
17:59:08.425 -> Pressure 988
17:59:08.458 -> Percent 20.01
17:59:08.458 -> Error from sensor 0


wildbill

That's odd. Can you try printing what you receive before you start using strtok on it?

Also,my apologies- the start char of 'O' should have taken care of the final 0000. My analysis was flawed.

Triaenodon

I added
    Serial.print(tempChars);
    Serial.print(receivedChars);
to the function showParsedData() and it gives readings like this:

18:36:07.804 ->  0198.2  0198.2 T +22.8 P 0989 % 020.04 e 0000
Oxygen 198.20
18:36:07.871 -> TemperatureO 22.80
18:36:07.871 -> Pressure 989
18:36:07.904 -> Percent 20.04
18:36:07.904 -> Error from sensor 0
18:36:09.801 ->  0198.2  0198.2 T +22.8 P 098O 0198.2 T +22.8 P 09Oxygen 198.20
18:36:09.868 -> TemperatureO 22.80
18:36:09.901 -> Pressure 98
18:36:09.901 -> Percent 0.00
18:36:09.901 -> Error from sensor 0
18:36:11.794 ->  0198.1  0198.1 T +22.8 P 0989 % 020.03 e 0000
Oxygen 198.10
18:36:11.861 -> TemperatureO 22.80
18:36:11.894 -> Pressure 989
18:36:11.894 -> Percent 20.03
18:36:11.927 -> Error from sensor 0
18:36:13.787 ->  0198.1  0198.1 T +22.8 P 098O 0198.1 T +22.8 P 09Oxygen 198.10
18:36:13.854 -> TemperatureO 22.80
18:36:13.958 -> Pressure 98
18:36:13.958 -> Percent 0.00
18:36:13.958 -> Error from sensor 0
18:36:15.780 ->  0198.1  0198.1 T +22.7 P 0989 % 020.03 e 0000
Oxygen 198.10
18:36:15.847 -> TemperatureO 22.70
18:36:15.881 -> Pressure 989
18:36:15.881 -> Percent 20.03
18:36:15.913 -> Error from sensor 0
18:36:17.773 ->  0198.1  0198.1 T +22.8 P 098O 0198.1 T +22.8 P 09Oxygen 198.10
18:36:17.840 -> TemperatureO 22.80
18:36:17.873 -> Pressure 98
18:36:17.873 -> Percent 0.00
18:36:17.906 -> Error from sensor 0
18:36:19.799 ->  0198.1  0198.1 T +22.8 P 0990 % 020.01 e 0000
Oxygen 198.10
18:36:19.865 -> TemperatureO 22.80
18:36:19.865 -> Pressure 990
18:36:19.865 -> Percent 20.01
18:36:19.899 -> Error from sensor 0
18:36:21.792 ->  0198.1  0198.1 T +22.8 P 098O 0198.1 T +22.7 P 09Oxygen 198.10
18:36:21.859 -> TemperatureO 22.80
18:36:21.859 -> Pressure 98
18:36:21.892 -> Percent 0.00
18:36:21.892 -> Error from sensor 0

Go Up