Help with serial UART communication between arduino and a sensor

Hi, I have an arduino UNO and a sensor. The sensor operates in 3.3 V logic and the arduino on 5v logic. I use a level converter. The sensor has a passive response, when it is sent a 9 byte string over uart it responds with a 13 byte string containing oxygen level, temperature level, and humidity level.

When I connect the arduino using the diagram below and run the script, nothing is outputted to the serial monitor. I tried running the output from the logic converter to an analog port on the arduino and the signal shows 3.3 volt constant. This seems like the signal is not being sent, but I’m not sure if it because of the way I am testing it. I plan to test the same method tommorow using an oscilloscope instead of the analog port but thought I would ask for advice in the meantime. I have attached the datasheet of the sensor, an image of the setup, and the arduino script. Please note the setup is not the same as the diagram because I took the picture after trying to trouble shoot. My main thought is if the way I’m trying to send the serial data request signal correct? Please let me know if any more data is needed. Sensor datasheet thanks for any and all help.

You never read from myserial and do you have a data sheet for the sensor

thank you.

This is the script I used to check if the signal is present at all, I modified my original script and didn’t save it :sweat_smile: but I was reading 13 bytes according to the manual. There is a manual for the sensor+ PCB in the original post, can you not open this?

Here is the original script I was running with the original setup

One question: do you expect us to type the code if we want to try it? Please post the code according to the Forum Rules, enclosed in <CODE/> tags.

On the other hand, code #1 is useless; it makes an analog reading of a pin whose connection does not exist in the schematic.

Sorry about the code I don’t have access to my computer until tommorow, I meant to post it to see if my overall idea was right or if there were any glaring issues. For the first code i sent, I had connected the TX output of the sensor directly to the A0 header on the arduino to try and troubleshoot. This is what resulted in the output of 3.3V over and over.

I can’t see a link in the original post..?
Can you try again …

Good luck.

@chogikyan
If you wish you can get rid of the active level shifter by the following passive level shifter only for RX line to simplify the wiring. Do you own the physical sensor? If yes, post the picture.

Hints for sketch:
1. Create Software UART Port: SUART(5, 6)
2. Issue command to senor to bring it into AUtomatic Upload Mode.

 uint8_t autoCmd[9] = {0xFF, 0x01, 0x78, 0x40, 0x00, 0x00, 0x00, 0x00, 0x47};
 for (byte i = 0; i < 9; i++) 
 {
    SUART.write(autoCmd[i]);
 }

3. Read 9-byte data coming up from sensor.

byte buf[9];
byte y = SUART.read();
if(y != 0)
{
    SUART.readBytes(buf, 9);
    if (buf[0] == 0xFF && buf[1] == 0x86)  //Frame Header AND Command ID
    {
        int rawO2   = (buf[2] << 8) | buf[3];
        int rawTemp = (buf[4] << 8) | buf[5];
        int rawHum  = (buf[6] << 8) | buf[7];

        float o2   = rawO2 / 10.0;
        float temp = rawTemp / 10.0;
        float hum  = rawHum / 10.0;

        Serial.print("Oxygen: ");
        Serial.print(o2, 1);
        Serial.println(" %");
 
        Serial.print("Temperature: ");
        Serial.print(temp, 1);
        Serial.println(" °C");

        Serial.print("Humidity: ");
        Serial.print(hum, 1);
       Serial.println(" %RH");

       Serial.println("------------------------");
       delay(1000);
    }
}

Or just

const uint8_t autoCmd[9] = {0xFF, 0x01, 0x78, 0x40, 0x00, 0x00, 0x00, 0x00, 0x47};
SUART.write(autoCmd, sizeof autoCmd);

On the reading side the function readBytes() can timeout so you should trap that as it returns the number of bytes placed in the buffer. A value of 0 indicates that no valid data was found. Best is to just check if you did actually read the expected number of bytes.

Typically we wait for data to start arriving and then read hoping the frame will be there without problem

while (SUART.available() == 0) yield(): // block waiting for answer
// something started to arrive, read the payload
if (SUART.readBytes(myData, 13) == 13)  {
  // We got 13 bytes. Handle the payload
  …
} else {
  // handle error
  …
}

But this is not the ideal way as it’s blocking. This is an asynchronous protocol so best read with an asynchronous code. Just read data in a buffer as it comes back in and handle the rest of your code (monitoring buttons or whatever) until you got the payload - then deal with the payload .


@chogikyan

I would suggest to study Serial Input Basics to understand this

Also please, please never ever again post pictures of text... Not only they are not readable nor usable directly for copy&paste but they use up lots of storage and internet bandwidth which contributes to polluting the planet.

➜ do your part and do yourself a favour and please read How to get the best out of this forum and modify your post accordingly (including code tags and necessary documentation for your ask).

3 Likes
void loop()
{
  byte y = SUART.read();
  if (y != 0)   //checking that 1-byte data has arrived in Serial buffeer
  {
    byte m = SUART.readBytes(myData, 9);  //reading all the 9-byte of the frame
    if (m == 9)   //9-byte data hasve arrived
    {
      if (myData[0] == 0xFF && myData[1] == 0x86)  //validatae Header AND ID
      {
        //------------------------------
      }
    }
  }
}

read returns -1 as an int (so != 0) when there is nothing to read. even if you store that into into a byte you won't get 0, you'll get 0xFF when there was nothing to read and you don't know then if you actually did receive 255 as a byte or if it was an error.

I should have written SUART.available() in place of SUART.read().

void loop()
{
  byte y = SUART.available();
  if (y != 0)   //at least there is 1-byte data in Serial buffeer
  {
    byte m = SUART.readBytes(myData, 9);  //reading all the 9-byte of the frame
    if (m == 9)   //9-byte data hasve arrived
    {
      if (myData[0] == 0xFF && myData[1] == 0x86)  //validatae Header AND ID
      {
        //------------------------------
      }
    }
  }
}

Asynchronous/nonblocking version:

void loop()
{
  byte y = SUART.available();
  if (y != 0)   //at least there is 1-byte data in Serial buffeer
  {
    myData[i] = SUART.read();
    i++;
    if(i == 9)
    {
      if (myData[0] == 0xFF && myData[1] == 0x86)  //validatae Header AND ID
     {
        //------------------------------
        i = 0;
     }
    }
  }
}

https://sgxsensortech.com/sensor/ps1-o2-25-mod

https://sgxsensortech.com/uploads/f_note/DS-0458-PS1-O2-25per-MOD.pdf

// PS1-02-MOD
// Polls an O2 sensor module using a digital Serial interface
// Prints temperature, humidity, %O2 readings to the Serial Monitor.

#include <SoftwareSerial.h>

// Create a SoftwareSerial port on digital pins 5 (RX) and 6 (TX)
SoftwareSerial mySerial(5, 6);

// Buffer for storing the 13-byte response packet from the sensor
byte myData[13];

// Command 6 from datasheet
byte MeasurementRequest[] = {
  0xFF, 0x01, 0x87, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x79   // 0x79 is checksum
};

void setup() {
  Serial.begin(9600);       // Start USB serial (to PC)
  mySerial.begin(9600);     // Start SoftwareSerial (to sensor)
}

void loop() {

  // Send the read-measurement command to the sensor
  mySerial.write(MeasurementRequest, sizeof(MeasurementRequest));

  // Wait until 13 bytes are available.
  // The module is expected to reply with a 13-byte data frame.
  while (mySerial.available() != 13) {
    ; // Busy-wait until all bytes arrive
  }

  // Read all 13 bytes into the buffer
  for (int i = 0; i < 13; i++) {
    myData[i] = mySerial.read();
  }

  // =====================
  // Oxygen (two bytes)
  // =====================

  byte highO = myData[6];  
  byte lowO  = myData[7];
  float OxLevel = (highO * 256) + lowO; // concentration formula

  Serial.print("Oxygen Level: ");
  Serial.print(OxLevel);
  Serial.println(" ppb");
  Serial.println();

  // =====================
  // Temperature
  // Datasheet: Temp = (byte8 << 8 | byte9) / 100
  // =====================

  float Temp = (float)(myData[8] << 8 | myData[9]) / 100.0;

  Serial.print("Measuring Temperature: ");
  Serial.print(Temp, 2);
  Serial.println(" degC");
  Serial.println();

  // =====================
  // Humidity
  // Datasheet: Hum = (byte10 << 8 | byte11) / 100
  // =====================

  float Hum = (float)(myData[10] << 8 | myData[11]) / 100.0;

  Serial.print("Measuring Humidity: ");
  Serial.print(Hum, 2);
  Serial.println(" % rH");
  Serial.println("++++++++++++++++++ +");
  Serial.println();

  delay(1000); // Wait 1 second before taking next measurement
}

Here is the original code. Sorry for the images above

I am going back to the lab now i will post a picture of the sensor. My plan of attack is to set up the original schematic but replace the logic converter with a voltage divider per @GolamMostafa ‘s advice. If that does not work with the original code (from post 14) i will try implementing the code sent by @J-M-L because to me it seems like my code should have worked anyway, so it must be a problem with the setup.

Any advice on how I should troubleshoot if the voltage divider setup doesn’t work would be greatly appreciated. My first thought would to be measuring each digital signal with an oscilloscope and trying to work backwards from sensor output to sensor input to arduino output to see which signal is flat (always a 1).

1. The passive level shifter using voltage divider had worked well for me in UNOR3/ESP32 UART operation.

2. Try the following sketch to acquire Gas concentration, Temperatue, and Humidity in passive mode. The Serial Monitor shows only Temperature and Humidity.

#include<SoftwareSerial.h>
SoftwareSerial SUART(5, 6);
byte myData[13];

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

  //-------passive mode---------
  uint8_t autoCmd[9] = {0xFF, 0x01, 0x78, 0x40, 0x00, 0x00, 0x00, 0x00, 0x46};
  for (byte i = 0; i < 9; i++)
  {
    SUART.write(autoCmd[i]);
  }
}

void loop()
{
  //---- read command--------
  uint8_t gthReadCmd[9] = {0xFF, 0x00, 0x87, 0x40, 0x00, 0x00, 0x00, 0x00, 0x79};
  for (byte i = 0; i < 9; i++)
  {
    SUART.write(gthReadCmd[i]);
  }

  //----read gas, temp, humidity data-------------
  byte y = SUART.available();
  if (y != 0)
  {
    byte m = SUART.readBytes(myData, 13);
    if (m == 13)   //13-byte data hasve arrived
    {
      if (myData[0] == 0xFF && myData[1] == 0x87)  //Frame Header AND Command ID
      {
        int rawTemp = (myData[8] << 8) | myData[9];
        int rawHum  = (myData[10] << 8) | myData[11];

        float temp = rawTemp / 10.0;
        float hum  = rawHum / 10.0;

        Serial.print("Temperature: ");
        Serial.print(temp, 1);
        Serial.println(" °C");

        Serial.print("Humidity: ");
        Serial.print(hum, 1);
        Serial.println(" %RH");

        Serial.println("------------------------");
        delay(1000);
      }
    }
  }
}

@GolamMostafa

Hi I think I’m messing up something really stupid. When no ground or 5 volt lines connected, I messed the correct resistance values. When I plug arduino in (unpowered) I get a resistance of 1.5 kohm over the first resistor as if there is a resistor in parallel. I thought maybe there is some connection between the arduino (ground and 5v) when powered off.i tried with power on and measure only 1.6 V on the right power line where I should have 3.3. What am I doing wrong?

Never mind I turned it off and on and the voltage divider works now :sweat_smile:. I will try sending the signal over this divider instead of the prebuilt logic converter

And now it is not working again ;(. Any ideas why I might have the 1.6v instead of 3.3? None of the resistors are physically touching

What resistors do you have? Tell me their values.