[solved] Two sensors do the same but are interpreted differently

Hi all,
I have encountered a strange phenomenon I have no explanation for. On an Arduino mega I read the input from a 6-channel spectrophotometric sensor via Serial1 (6 integer values separated by a comma and terminated by a "new line" character (0x0A), only the relevant portion of the code is shown):

void setup(){
begin.Serial1(9600);
}
void loop() {
  if (Serial1.available()) {
    vi = Serial1.parseFloat(); vi = (vi * f_vi);     // correction factors (f_vi etc.) plus another
    bl = Serial1.parseFloat(); bl = (bl * f_bl);            // correction (experimental)
    gn = Serial1.parseFloat(); gn = (gn * f_gn);
    yl = Serial1.parseFloat(); yl = (yl * f_yl);
    og = Serial1.parseFloat(); og = (og * f_og);
    rd = Serial1.parseFloat(); rd = (rd * f_rd);
    if (Serial1.read() == '\n') { // here follow the instructions what to do with these numbers}
// parseFloat instead of parseInt because the values are processed with float values

I have two nearly identical sensors: a AS7162 breakout board, connected via I2C with a Arduini Mini Pro. This device is connected with a cable to the Arduino Mega. Sensor 1 uses the AS7162 board from Pimoroni and has a 30 m cable with 4 lines, sensor 2 uses the Adafruit board and a 10 m cable with 3 lines plus shield (GND). When connected to a serial terminal both sensors do the same (as expected): they send 6 numbers, separated by a comma (0x2C) and as last bit \n (0x0A).
But connected with the Mega, only sensor 1 behaves as expected, sensor 2 does not. It stops at the second if. When changing the condition to

(Serial1.read() != '\n')

it works with sensor 2, but not with sensor 1. But how is that possible? The hex-output of my terminal shows with no doubt, that both sensors send a 0x0A!

Frowning

-richard

I can only take a guess. If possible slow the baud down a notch. I have seen similar problems way in the past and it was caused by baud clock skew. The fact that it works on one board and not on the other assuming the code is the same points to a hard problem.

Yeah, the idea of too fast transmission popped up by me, too. Therefore I connected the sensors with an oscilloscope to see, if the signals were damaged due to too long cables (but 30 m ist't that far...), but the oscilloscope showed in both cases two identical pictures of a nice rectangle curve. Therefore I got away from this idea. But because of your post I'll slow the baud rate down (tomorrow...)
Thanks
-richard

In my first post the numbers of the sensors which work resp. do not with the Mega were inverted by mistake. I edited the post, now it is correct.

I changed the baud rate the sensors with the Pro Mini send and the Mega receives to 4800 - no success. The misbehaviour is still the same.

I would try just echoing what you get to the serial monitor as an aid to debugging.

I'm confused where the Arduino's are, which part is I2C and which is Serial and how long all the wires are. Can you make a drawing ?

Pimoroni AS7262: AS7262 6-channel Spectral Sensor (Spectrometer) Breakout – Pimoroni.
It has level shifters for I2C, you can use it with 3.3V and 5V boards.
This AS7262 breakout cannot be used in serial mode.

Adafruit AS7262: Adafruit AS7262 6-Channel Visible Light / Color Sensor Breakout : ID 3779 : $19.95 : Adafruit Industries, Unique & fun DIY electronics and kits.
It has level shifters for I2C, you can use it with 3.3V and 5V boards.
This breakout uses the I2C interface on the chip by default, but a UART interface that accepts AT commands is also selectable.

I think you get this problem because your sketch is not fail-safe. If you don't understand my explanation, please ask.
With 9600 baud, the data is incoming very slow. That means you are already converting data that is still being received at that moment.
The parseFloat() waits for data, and it has a timeout, so that is okay, but the read() for '\n' is not okay.

  1. When expecting the '\n', it might not have been received yet. The sketch can easily be too fast with 9600 baud.
  2. The parseFloat() is finished when something is received which is not part of a float number. The trailing '\n' is a such a thing. Do you know if that '\n' is read or not by the parseFloat() ?
  3. What if there is a space before the '\n' ?

When reading serial data, it is common to read a full line, including the '\n'. When everything is received correctly, then the data is converted.

Please specify the serial data. Are they whole numbers or do they have a dot with decimals ? I think you should not use parseFloat to convert integers to float.

Thanks for your thoughts. Drawing in the attachment. Both answers helped, though I’m not through with it. The sensors send “sentences”, for example: 1345,23689,45317,3245,136784,2346\n
Should I receive it as string and trim it to the 6 values?

For an echo and to see what is processed, I added some "Serial.print"s:

void loop() {
  while (Serial1.available()) {
    vi = Serial1.parseFloat(); vi = (vi * f_vi) / 135;Serial.println(vi);            // correction factors (f_vi etc.) plus another
    bl = Serial1.parseFloat(); bl = (bl * f_bl) / 128;            // correction (experimental)
    gn = Serial1.parseFloat(); gn = (gn * f_gn) / 128;
    yl = Serial1.parseFloat(); yl = (yl * f_yl) / 125;
    og = Serial1.parseFloat(); og = (og * f_og) / 129;
    rd = Serial1.parseFloat(); rd = (rd * f_rd) / 123;
    if (Serial1.read() == '\n') {Serial.println("newline");

The results were as follows:
Sensor 1 (the strange one):

<Serial Monitor>
newline
0.64
1.28
1.29
... (the values shown are correct)

→ \n is only read one time
And sensor 2 (the one that is OK):

newline
0.33     // why read 4 times???
1.20
0.67
0.33
newline     // from here on it looks OK
0.33
newline
0.33
newline
0.33

As pointed out by koepel, there may be timing problems.
@ koepel: better use parseInt() and then convert to float? As float() converts every value to float, I think I can use
float vi = Serial1.parseInt();
?

Doc Nov 07 2020.pdf (449 KB)

How do you control which sensor is sending? It seems that it would be far better to read them on separate serial ports.

Misunderstanding: It is only planned to use sensor 1 at Serial1, and sensor 2 at Serial2. But momentarily I only want to find out, if they work correctly at all. Therefore I use them alternatively at Serial1. I control them by my eyes: If sensor1 is plugged in, then sensor 1 is sending, if sensor2 is plugged in, then obviously sensor 2 is sending :wink:

Makes sense. I'd read until you see the linefeed then & capture the string so you can print it for debugging and then parse it. For best results, read and throw away until the first linefeed and then process for real.

You can not do a if (Serial1.read() == '\n'), because the bits of the '\n' could still be halfway receiving.

It is not nice to receive an integer with a parseFloat(), that is the wrong function for that purpose. The parseInt() returns a long, so it can be a large number.
https://www.arduino.cc/en/Reference/ParseInt

A simple solution is a delay(10) before the Serial1.read().
A better solution is to put the whole line in a buffer. Then you can deal with every problem, for example not enough numbers. Do you know the scanf() function ?

What about a '#' or '*' as the first character of a line ? Then you don't have to throw away the first data to get in sync.
In the old days there was a 'STX' as first character and a 'ETX' as last character and probably a checksum byte before the 'ETX'.

The drawing from your pdf file is this:

Thank you all for the discussion. The idea to put things into a buffer and then read it at once has convinced me. I found an example by zoomkat (https://forum.arduino.cc/index.php?topic=121454.0) which was a little modified:

/*
//zoomkat 11-12-13 String capture and parsing
https://forum.arduino.cc/index.php?topic=121454.0
modified Nov, 2020 RMü
*/

String readString; //main captured String
String viStr; //data String
String blStr;
String gnStr;
String ylStr;
String ogStr;
String rdStr;
float vi;
float bl;
float gn;
float yl;
float og;
float rd;

int ind1; // where is the comma?
int ind2;
int ind3;
int ind4;
int ind5;
int ind6;

// constants for computing mW's:
float f_vi = 1 / 138.86;
float f_bl = 1 / 109.10;
float f_gn = 1 / 92.85;
float f_yl = 1 / 87.69;
float f_og = 1 / 80.83;
float f_rd = 1 / 77.12;


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

void loop() {

 
  if (Serial1.available())  {
    char c = Serial1.read();  //gets one byte from serial buffer
        if (c == '*') {
      //do stuff

      Serial.println();
      Serial.print("captured String is : ");
      Serial.println(readString); //prints string to serial port out

      ind1 = readString.indexOf(',');  //finds location of first ,
      vi = ((readString.substring(0, ind1)).toInt()) * f_vi /135;   //captures first data String
      ind2 = readString.indexOf(',', ind1 + 1 ); //finds location of second ,
      bl = ((readString.substring(ind1 + 1, ind2)).toInt()) * f_bl / 128; //captures second data String
      ind3 = readString.indexOf(',', ind2 + 1 );
      gn = ((readString.substring(ind2 + 1, ind3)).toInt()) * f_gn / 128;
      ind4 = readString.indexOf(',', ind3 + 1 );
      yl = ((readString.substring(ind3 + 1, ind4)).toInt()) * f_yl / 125;
      ind5 = readString.indexOf(',', ind4 + 1);
      og = ((readString.substring(ind4 + 1, ind5)).toInt()) * f_og / 129;
      ind6 = readString.indexOf(',', ind5 + 1);
      rd = ((readString.substring(ind5 + 1)).toInt()) * f_rd / 123; //captures remain part of data after last ,

      /*vi = viStr.toInt();
      bl = blStr.toInt();
      gn = gnStr.toInt();
      yl = ylStr.toInt();
      og = ogStr.toInt();
      rd = rdStr.toInt();*/

      Serial.print("vi = ");
      Serial.println(vi);
      Serial.print("bl = ");
      Serial.println(bl);
      Serial.print("gn = ");
      Serial.println(gn);
      Serial.print("yl = ");
      Serial.println(yl);
      Serial.print("og = ");
      Serial.println(og);
      Serial.print("rd = ");
      Serial.println(rd);
      Serial.println();
      Serial.println();

      readString = ""; //clears variable for new input
    }
    else {
      readString += c; //makes the string readString
    }
  }
}

Now it works as it should.
-richard