Measuring ambient air oxygen level via LuminOx Optical Oxygen Sensor

Dear all,

I have an LuminOx Optical Oxygen Sensor.

Luminox Oxygen Sensor

The sensor communicates via USART. My goal is to get every 10 minutes the values and to write it into a mariadb database. The sensor has 3 modes: Stream, Poll and Off. On startup the default is “Stream”. In Stream mode it gives values every second. This I got to work, but I don’t know how to reduce the data flood in a reliable way! Maybe someone knows how to do that? Or would it be better to use the Poll mode?
I found some discussion on that sensor here, but the examples didn’t work.

I’m new to Arduino and coding. Any help would be great!

An interesting sample with another sensor (but I don’t come along):

Arduino UART to Interface to COZIR Sensor

Here the code I got to work so far:

#include <SoftwareSerial.h>

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

void setup() 
{
  // Open serial communications and wait for port to open:
  Serial.begin(9600);

  mySerial.begin(9600);

}

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

Readings look like:

O 0201.6 T +22.8 P 0997 % 020.22 e 0000
O 0201.6 T +22.8 P 0997 % 020.22 e 0000
O 0201.1 T +21.7 P 0997 % 020.17 e 0000
O 0201.1 T +22.9 P 0997 % 020.17 e 0000
O 0201.1 T +22.8 P 0997 % 020.17 e 0000
O 0200.8 T +21.8 P 0997 % 020.14 e 0000
O 0200.8 T +22.9 P 0997 % 020.14 e 0000
O 0200.8 T +23.0 P 0997 % 020.14 e 0000
O 0200.8 T +22.9 P 0997 % 020.14 e 0000
O 0200.8 T +22.8 P 0997 % 020.14 e 0000
O 0200.8 T +23.0 P 0997 % 020.14 e 0000
O 0200.8 T +23.0 P 0997 % 020.14 e 0000
O 0200.8 T +23.0 P 0997 % 020.14 e 0000
O 0200.8 T +23.0 P 0997 % 020.15 e 0000
O 0200.9 T +23.0 P 0997 % 020.15 e 0000
O 0200.9 T +23.1 P 0997 % 020.15 e 0000
O 0200.9 T +23.1 P 0997 % 020.15 e 0000
O 0200.9 T +23.1 P 0997 % 020.15 e 0000
O 0200.9 T +23.2 P 0997 % 020.15 e 0000
O 0200.9 T +23.0 P 0997 % 020.15 e 0000
O 0200.9 T +23.0 P 0997 % 020.15 e 0000

I would like to write to mariadb: O, T, P and %.
The database connection I already got to work with another sensor, that’s not my topic now.
How to get reliable data in steps of 10 minutes to e.g. variables?

Thanx in advance!
Triaenodon

Are you using just the sensor or are you using the evaluation board? If the board, are you using RS-232 or RS-485 interface?

Paul

Hello Paul,

I'm just using the sensor. I connected the sensors
Pin 1 to Arduino 5V supply
Pin 2 to Arduino GND
Pin 3 to Arduino DIGITAL 10
Pin 4 to Arduino DIGITAL 11
using a Breadboard.
I use Arduino Mega 2560 with Ethernet Shield 2.

Sorry, forgot to mention this.

Thank you!

If you don't want the flood method, which is the default method, then you need to tell the device to use the poll method by sending it a message "M1\r\n". Then begin polling for data.

IT's all in the data sheet!

Paul

Yes, I also red the data sheet, but I'm that new to Arduino...
Need the

mySerial.println("M 1\r\n");

to be in the

void setup()

or in the

void loop()

?
How would the request for data look like?

mySerial.read("O T P %\r\n");

I tried several things, but it didn't work for me...

Triaenodon

If you use flood mode you can do some local averaging to reduce noise in the signal?

Triaenodon:
Yes, I also red the data sheet, but I'm that new to Arduino...
Need the

mySerial.println("M 1\r\n");

to be in the

void setup()

or in the

void loop()

?
How would the request for data look like?

mySerial.read("O T P %\r\n");

I tried several things, but it didn't work for me...

Triaenodon

Look at serial.write(). And the mode setting should be in setup. Only needs to be done once.

Paul

Hello Paul,

I stuck trying this:

#include <SoftwareSerial.h>

/*
sensor Pin3(TX) to Arduino Pin 19(RX)
sensor Pin4(RX) to Arduino Pin 18(TX)
*/

SoftwareSerial mySerial(19, 18); // RX, TX

char receivedData = '0'; //variable for received data from sensor

void setup() {
  Serial.begin(9600); // Open serial communications and wait for port to open
  mySerial.begin(9600);
  mySerial.write("M 1\r\n");
}

void loop() {
 if (mySerial.available())  { //checks, if connection available
    
    //mySerial.read(("O T P %\r\n"));
    
    Serial.write("O T P %\r\n");
    receivedData = mySerial.read();
    Serial.print(receivedData);
    //Serial.write(mySerial.read());

    delay(10000); //waits for 10 seconds --> later change to 10 minutes!
    }
}

Unfortunately I’m only a script kiddie, not knowing where I’m wrong, but step by step:
My “void setup()” is correct?
Is the request for data correct?
Is the way to print the data in the serial monitor correct?

Triaenodon

Hello MarkT,

That would be great, but different from pol mode. I tried that, but I wasn't able to get it run... :confused:
So, step by step, till I got the overview.

Thank you so far!

You seem to be putting a space between characters in the message you are sending. There are no spaces according to the data sheet.

Paul

Ok, thank you. But if I try this

    Serial.write("O\r\n");
    receivedData = mySerial.read();
    Serial.print(receivedData);
    //Serial.write(receivedData);

    delay(10000); //waits for 10 seconds --> later change to 10 minutes!

I get readings like this:
O
EO
O
0O
1O

O

O
OO
O
0O
1O
9O
8O
.O
6O
O
TO
O
+O
2

Commenting the delay out, I get something like this, but in seconds:

O
EO
O
0O
0O

O

O
OO
O
0O
1O
9O
8O
.O
3O
O
TO
O
+O
2O
5O
.O
6O
O
PO
O
0O
9O
9O
9O
O
%O
O
0O
1O
9O
.O
8O
5O
O
eO
O
0O
0O
0O
0O

O

O
OO
O
0O
1O
9O
8O
.O
3O
O
TO
O
+O
2O
6O
.O
8O
O
PO
O
0O
9O
9O
9O
O
%O
O
0O
1O
9O
.O
8O
5O
O
eO
O
0O
0O
0O
0O

O

O

Study Robins's examples of reading serial data here Robin's examples

Then you will discover what your code is actually doing.

Paul

Thank you Paul,

it helped me a lot!
Pol mode I wasn’t able to initialize. Every try gave me E1 or E2, working in stream mode further.

First: Robin made a more compact overview, which is found here.

Second: It took me nearly the hole night to get it run, but I learned a lot!

Third: I still have a problem. Depending on where I put the wanted delay, I get some interesting behavior. Sometimes (every second read) the sketch starts again with the “void setup()”, what I can identify by printing “Ardunio O2 Sensor\n\n” to the serial monitor (which is the case e.g. in the example further down). In other cases the read breaks every third read.
Where would you put the delay?? :confused:
Do anybody see potential for improvements?

Thanks in advance for any hint.
Triaenodon

#include <SoftwareSerial.h>
/*
This sketch reads the measured values of a LuminOx Optical Oxygen Sensor wich is quiet the same
as the XYO oxygen sensor from first-sensor.
sensor Pin3(TX) to Arduino Pin 10(RX)
sensor Pin4(RX) to Arduino Pin 11(TX)
*/

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

int o2; 
uint8_t buffer[41]; //get values from stream mode including \r
uint8_t ind =0; 
uint8_t index =0; 
int timer = 2000;     // The higher the number, the slower the timing.
int arrayCount = 41; // the length of the data array excluding "\n"
int fill_buffer();   // initialisation of needed functions 
int format_output();

void setup() {   
  Serial.begin(9600);    // Open serial communications
  Serial.print("\n\n");   
  Serial.println("Ardunio O2 Sensor\n\n");    
  mySerial.begin(9600); // Start serial communications with sensor   
}


void loop() { 
    //delay(timer);
  fill_buffer();  // function that reeds O2 sensor and fills buffer     
  //Serial.print("Buffer contains: ");   
  //for(int j=0; j<ind; j++)Serial.print(buffer[j],HEX);
    //delay(timer);
    Serial.println("");
    //delay(timer);
  for(int i=0; i<arrayCount; i++) { // loop from the lowest index to the highest
    index = i;
    format_output();
    }
    //delay(timer);
    //Serial.println("");
}   

int fill_buffer(void) { // Fill buffer with sensor ascii data 
    //delay(timer);
  ind = 0;
  while(buffer[ind-1] != 0x0A){  // Read sensor and fill buffer up to 0XA = CR
    if(mySerial.available()){     
      buffer[ind] = mySerial.read();     
      ind++;     
      } 
      //delay(timer);   
    } 
    //delay(timer);  
    // buffer() now filled with sensor ascii data   
    // ind contains the number of characters loaded into buffer up to 0xA = CR   
    ind = ind -2; // decrement buffer to exactly match last numerical character 
    //delay(timer);  
}

 int format_output(void){ // read buffer, extract ASCII chars and print    
  o2 = buffer[index],HEX;   
  Serial.print((char)o2); 
  delay(timer);  
}

The actual output looks like:

Ardunio O2 Sensor

O 0196.1 T +27.1 P 0988 % 019.85 e 0000

O 0196.1 T +27.1 P 0988 % 019.85 e 0000

Ardunio O2 Sensor

O 0196.6 T +27.3 P 0987 % 019.92 e 0000

O 0196.6 T +27.3 P 0988 % 019.90 e 0000

Ardunio O2 Sensor

O 0196.6 T +27.5 P 0988 % 019.90 e 0000

O 0196.6 T +27.4 P 0987 % 019.92 e 0000

There is NO need for any delay. This line takes care of waiting for something to arrive from the sensor:

if(mySerial.available()){

I would reverse what you are doing with the array and do it only when you have received a character, not do it before you have received a character.

Paul

My idea was to generate a read only once in 10 minutes not to flush the mariadb, otherwise I could work with the very first code posted. Am I wrong?
Still confused from generating my very first code. :sweat_smile:

Triaenodon

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

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:

#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:

.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

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

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

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

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