I2C receiving strange characters at time

Greetings,

what would be a good strategy to exclude the readings where a strange character appeared ?

I have an Atlas Scientific stamp that reads ORP values. Every few hundreds readings a strange character will sneak in. I am charting these readings and the corrupted one will cause a drop to zero. Not a big deal, but annoying.

I can put the float values in a vector and run an average over a certain number of readings and graph the average, but the strange characters are nagging me :relaxed:

cheers,
MaLi

but the strange characters are nagging me

We'd need to see how you determine that there are "strange characters" and how you are dealing with the characters when they are not strange.

The usual advice would be to not store "strange" characters.

Thank you Paul.

Here is the code I am using:

#include <Wire.h>
#include <SoftwareSerial.h>

// --------------- Serial ports --------------------------------------
#define ORPrx 4                               //define what pin orp rx is going to be
#define ORPtx 3                               //define what pin orp Tx is going to be
SoftwareSerial ORPserial(ORPrx, ORPtx);       //define the ORP soft serial port

const byte ORP_ADDRESS = 42;
volatile byte* ORP_FloatPtr;

// --------------- Define ORP variables ------------------------------
char inData_ORP[18];          // receiving serial buffer
char string_ORP[18];          // transfer buffer
float ORP_val = 0;
byte holding_ORP;             // number of bytes received
byte j;                       

// --------------- Set measurement interval--------------------------- 
long ORPReadInterval = 2000; 
long previousMillisORP = 0; 

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

  Wire.begin(ORP_ADDRESS);
  Wire.onRequest(requestEvent); 
}  

void loop() 
{
  unsigned long currentMillisORP = millis(); 

  if(currentMillisORP - previousMillisORP > ORPReadInterval)
  { 
    previousMillisORP = currentMillisORP;
    
    memset(&inData_ORP[0], 0, sizeof(inData_ORP));   // clear/prepare receiving buffer
    holding_ORP = 0;
    
    ORPserial.print("read()\r");                     // send it the command to take a single reading.                
    delay(75);
    
    while(ORPserial.available() > 0) {               // wait until serial gets something
      holding_ORP = ORPserial.available();           // hold how many bytes were received from ORP stamp
      // Serial.println(holding_ORP);
      for(j=0; j <= holding_ORP; j++) {              // assemble stamp data
        inData_ORP[j] = ORPserial.read();
      }
    }

    inData_ORP[holding_ORP]='\0';                    // null terminate the string

    Serial.println(inData_ORP);
    sscanf(inData_ORP, "%*s %s %*s", string_ORP);    // select the usful part of string method #1
    // Serial.println(string_ORP);
    
    ORP_val = atof(string_ORP);                      // get the float value

    Serial.print("ORP Sensor output: ");
    Serial.println(ORP_val);    
  }          

}  // end of loop

void requestEvent()
{
  byte* Data;
  ORP_FloatPtr = (byte*) &ORP_val;
  Data[0] = ORP_FloatPtr[0]; 
  Data[1] = ORP_FloatPtr[1];
  Data[2] = ORP_FloatPtr[2];
  Data[3] = ORP_FloatPtr[3];   
  Wire.write(Data, 4);
}

I noticed that the delay after sending the read()\r command to the stamp is important. No delay increases the frequency of receiving bad characters, too much messes up the response.
The stamp always responds with 14 characters (ORP= xx.xx mv)

cheers,
MaLi

    delay(75);

while(ORPserial.available() > 0) {              // wait until serial gets something
      holding_ORP = ORPserial.available();          // hold how many bytes were received from ORP stamp
      // Serial.println(holding_ORP);
      for(j=0; j <= holding_ORP; j++) {              // assemble stamp data
        inData_ORP[j] = ORPserial.read();
      }
    }

There are a lot of problems there. For one thing, you shouldn't need a delay at all.

Next, if you receive holding_ORP characters you are storing holding_ORP+1 (because you are going from zero) so you are reading too many.

You should be using a state machine (see link above) and start a new sequence when receiving "ORP" and finish when receiving "mv".

  ORP_FloatPtr = (byte*) &ORP_val;

ORP_val should be volatile as it is being read in an ISR.

volatile byte* ORP_FloatPtr;

ORP_FloatPtr should not be volatile, nor do I see why it is a global variable.

void requestEvent()
{
  byte* Data;
  ORP_FloatPtr = (byte*) &ORP_val;
  Data[0] = ORP_FloatPtr[0]; 
  Data[1] = ORP_FloatPtr[1];
  Data[2] = ORP_FloatPtr[2];
  Data[3] = ORP_FloatPtr[3];   
  Wire.write(Data, 4);
}

See I2C_Anything:

That saves a lot of mucking around.

eg.

void requestEvent()
  {
  I2C_writeAnything (ORP_val);
  }
void requestEvent()
{
  byte* Data;
  ORP_FloatPtr = (byte*) &ORP_val;
  Data[0] = ORP_FloatPtr[0]; 
  Data[1] = ORP_FloatPtr[1];
  Data[2] = ORP_FloatPtr[2];
  Data[3] = ORP_FloatPtr[3];   
  Wire.write(Data, 4);
}

Is this even valid? You aren't allocating any memory for the data array, you're just declaring the pointer. Doesn't this have the potential to cause severe trouble?

You are right, it's not valid. Data is never assigned a value, so dereferencing it is undefined.

Thank you Nick and Ninja,

Nick - you are right about the variables, but for whatever reasons without that small delay, this stamp is misbehaving. I did try the I2C_Anything. I like your elegant solution, but I failed on the receiving event. I have three Arduino Nano acting as slaves sending over I2C floats to a master. How would you build the onReceive event on master ?
I never used state machine before. Would love to learn. Thank you. Checking for character "v" every receive event works for the time being. I see the reading being discharged whenever the character is not received.
And yes, I checked the the last character was alway empty. Many thanks for your effort.

Ninja, I don't understand your point (my knowledge is rather limited), but it works very well the way it is. How would you write it different ?

Many thanks,
MaLi

I see a bit of discussion on the software aspects of things.
But my question to you would be have your verified that you have the proper value of pull-up resistance in the data lines (SDA, SCL) it should be pretty much 4.7K. If you have two devices on line with both adding pull ups the signal will distort, Same thing if you have no pull ups the signal can corrupt..

MaLi:
Thank you Nick and Ninja,

Nick - you are right about the variables, but for whatever reasons without that small delay, this stamp is misbehaving. I did try the I2C_Anything. I like your elegant solution, but I failed on the receiving event.

Assuming it is a coding problem, you need to post the code for both the sending and receiving end. Otherwise everthing is guesswork. And can you define "failed"?