Input buffer and altSoftSerial

I’m using altSoftSerial library on a project to interrogate a linbus sensor using an SKPang breakout board and a Mega 2560. Using the mega 'cos I had it available, and tried the standard serial ports but need better control of the timing to run linbus. The Tx Rx interrogation of the sensor works fine.

Problem is that when the sensor is detached, or even if started without a sensor, the receive buffer seems to get some values in it, and continues to store them, the sketch switches the serial on/off to control the linbus, but this doesn’t appear to clear the buffer, or its reading from somwhere else.

Any ideas? Need to clear it as we need determine when a sensor is there, and the buffer seems to hold last value, so CRC checks, so output stays as if a sensor is attached.

Latest output was 4e,00,ff,00,14,6a,08H; in HEX, which has no apparent relation to anything transmitted. Also put a 'scope on the Tx/Rx lines to check if anything was being injected there, it isn’t.

unsigned int TxRx(byte mode) {
  byte PID = 0x1A;        //PID [0x1A]
  unsigned int PM25 = 999;
  sensorTemp =  99;
  byte bytes[11];

  if (digitalRead(FaultPin) == LOW) {  //Breakout Fault
    sensorStatus = 99;
    PM25 = 999;
  } else {
    txMode(mode);
    unsigned long t = millis();
    while (millis() <= t + 50) {} //50mS delay
    mySerial.end();
    digitalWrite(TxPin, LOW);
    delayMicroseconds(13 * 1000000 / baud_rate); //13
    digitalWrite(TxPin, HIGH);
    delayMicroseconds(2 * 1000000 / baud_rate); //needs to be done because Serial wont work with it
    mySerial.begin(baud_rate, SERIAL_8N1);
    mySerial.write(0x55);        //synch byte
    mySerial.write(PID);

    mySerial.readBytes(bytes, 9);
    // bytes 0 and 1 are read as sych and PID
    byte message[6] = {bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7]};

    if (CRC(PID, message, 6) == bytes[8]) {      //CRC Check
      PM25 = ( bytes[3]  | ((bytes[4] & B00000011 ) << 8 )) ;
      sensorTemp =  bytes[7] - 40;
      sensorStatus = bytes[2] - 1  ;
    } else {
      PM25 = 998;
      sensorStatus = 98;
    }
  }
    for (int i = 2; i < 9 ; i++) HEXprint(bytes[i]); //prints message & CRC
    //HEXprint(CRC(PID, message, 6));                  //prints CRC calculation
    //BINprint(bytes[2]);
  return PM25;
}

Add:

define sampleFrequency 1000 //sample Frequency period in mS

define baud_rate 19200 //sensor baud rate

define TxPin 46

define RxPin 48

define CSPin 52

define FaultPin 50

AmphenolSensors: tried the standard serial ports but need better control of the timing to run linbus.

I can't imagine how any software serial could give you better control than hardware serial.

the receive buffer seems to get some values in it, and continues to store them,

The usual way I deal with that is

while (Serial.available() > 0) {
  dumpChar = Serial.read();
]

...R

OK, I don't know if it actually works better, but after a day of frustration with the hardware port, I got altSoftSerial going in 10 minutes, so something is different.

Tried your snippet thankyou Robin2, got this output (in Hex) 4e,00,ff,00,14,6a,08, and it is same after rebooting arduino. However, it does change after a power cycle. Is this a clue?

AmphenolSensors: Tried your snippet thankyou Robin2, got this output (in Hex) 4e,00,ff,00,14,6a,08, and it is same after rebooting arduino. However, it does change after a power cycle. Is this a clue?

Without seeing the complete program in which you tried my code I can't comment.

...R

#define sampleFrequency  1000     //sample Frequency period in mS
#define baud_rate 19200           //sensor baud rate
#define TxPin 46
#define RxPin 48
#define CSPin 52
#define FaultPin 50

#include <AltSoftSerial.h>

AltSoftSerial mySerial;               // Declare serial

unsigned long sampletime = millis();
long sensorTemp = 0;
byte sensorStatus;


void setup() {
  pinMode(CSPin, OUTPUT);         //CS High for linbreakout board
  digitalWrite(CSPin, HIGH);
  pinMode(FaultPin, INPUT);          //Breakout Board Failure

  pinMode(13, OUTPUT);        //switches screen power on
  digitalWrite(13, HIGH );

  pinMode(TxPin, OUTPUT);        //allow switching the Tx on/off for signal delay

  Serial.begin(19200);        //starts serial output to serial port.
  Serial.println(F("Sensor Temperature (°C), PM (µg/m3)"));

  sampletime = millis();  //sets timer
  txMode(0x01);   //disable - 0x00, enable - 0x01
}

void loop() {
  unsigned int PM25;

  if (millis() >= (sampleFrequency + sampletime))
  {
    sampletime = millis();  //resets timer

    PM25 = TxRx(0x01);        // transmit mode (disable - 0x00, enable - 0x01)
    // & read sensor values and interpret
    Serial.print (sensorTemp); Serial.print(", "); Serial.println(PM25);

  }
}
//____________________________________sub routines___________________________________
void txMode(byte mode) {    //transmit drive mode (UNIT4) to sensor
  byte PID = 0xDD;            //PID added parity bits to 0x1D
  mySerial.end();
  digitalWrite(TxPin, LOW);
  delayMicroseconds(13 * 1000000 / baud_rate); //13
  digitalWrite(TxPin, HIGH);
  delayMicroseconds(2 * 1000000 / baud_rate); //needs to be done because Serial wont work with it
  mySerial.begin(baud_rate, SERIAL_8N1);
  mySerial.write(0x55);
  mySerial.write(PID);
  mySerial.write(mode);
  byte message[1] = {mode};
  mySerial.write(CRC(PID, message, 1)); //CRC (0x21 is CRC for mode 1
}


unsigned int TxRx(byte mode) {
  byte PID = 0x1A;        //PID [0x1A]
  unsigned int PM25 = 999;
  sensorTemp =  99;
  byte bytes[11];

  if (digitalRead(FaultPin) == LOW) {  //Breakout Fault
    sensorStatus = 99;
    PM25 = 999;
  } else {
    txMode(mode);
    unsigned long t = millis();
    while (millis() <= t + 50) {} //50mS delay
    mySerial.end();
    digitalWrite(TxPin, LOW);
    delayMicroseconds(13 * 1000000 / baud_rate); //13
    digitalWrite(TxPin, HIGH);
    delayMicroseconds(2 * 1000000 / baud_rate); //needs to be done because Serial wont work with it
    mySerial.begin(baud_rate, SERIAL_8N1);
    mySerial.write(0x55);        //synch byte
    mySerial.write(PID);

    mySerial.readBytes(bytes, 9);
    // bytes 0 and 1 are read as sych and PID
    byte message[6] = {bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7]};

    if (CRC(PID, message, 6) == bytes[8]) {      //CRC Check
      PM25 = ( bytes[3]  | ((bytes[4] & B00000011 ) << 8 )) ;
      sensorTemp =  bytes[7] - 40;
      sensorStatus = bytes[2] - 1  ;
    } else {
      PM25 = 998;
      sensorStatus = 98;
    }
  }
  for (int i = 2; i < 9 ; i++) HEXprint(bytes[i]); //prints message & CRC
  //HEXprint(CRC(PID, message, 6));                  //prints CRC calculation
  //BINprint(bytes[2]);
  return PM25;
}

byte CRC(byte PID, unsigned char data[], byte data_size) {
  byte checksum = PID;
  for (int i = 0; i < data_size; i++)
    if (checksum + data[i] >= 256) checksum = checksum + data[i] - 255;
    else checksum = checksum + data[i];
  return 255 - checksum;
}

void HEXprint(byte PrintValue)   //prints variable as Hexadecimal putting in leading 0x0
{
  String stringOne;
  //Serial.print("0x");
  if (PrintValue < 16) {
    stringOne = "0" + String(PrintValue, HEX) + ",";
    //Serial.print("0");
  } else {
    stringOne = String(PrintValue, HEX) + ",";
  }
  stringOne.toLowerCase();
  Serial.print(stringOne);
}

Abridged code above.

AmphenolSensors: Abridged code above.

I can't see anything in that program that looks like the code I suggested in Reply #2

Also, please tell us exactly what happens when you run the abridged program, and what you want it to do that is different.

...R

When the program starts some values are returned to the byte array, even if no sensor is attached, these, to my thinking shouldn't be there. If a sensor is attached then the values alter as the sensor parameter changes, CRC checks etc. However, if the sensor is then detached, the last values remain in the buffer / byte array, so the sketch checks the CRC, which works, and continues as if the sensor is there - which it isn't.

My understanding was that end() and begin() would clear the buffer, and similarly only declaring the byte variable within the sub routine starts it at zeros. It appears not to be so.

I've tried your snippet in different places, aslo putting 0 values into the buffer prior to the read, putting zero values at the end of the sub routine, but I keep getting values returned, so where are they coming from? To my thinking it should be empty! Running it this morning returned 76,f2,ff,00,14,6a,08. A reboot returned same values, and a power cycle 6e,fe,ff,00,14,6a,08 ! Where are they coming from when there is nothing attached?

My expectation is that if nothing is transmitted by the sensor and received by the Arduino, nothing should be returned in the byte array.

AmphenolSensors: I've tried your snippet in different places,

If you don't post the program in which you tried that I can't help. Also, post a sample of the output from the program.

...R

Output (print to serial) looks like this:
4e,00,ff,00,14,6a,08,99, 999
4e,00,ff,00,14,6a,08,99, 999
4e,00,ff,00,14,6a,08,99, 999
4e,00,ff,00,14,6a,08,99, 999
when there is nothing connected using the code posted above. Rebooting the Arduino the output does not change. Power cycling the Arduino reults in different hex values (1st 7 values).

modified your code slightly

while (Serial.available() > 0) {
 byte dumpChar = Serial.read(); }

and inserted in the sub routine after line “while (millis() <= t + 50) {} //50mS delay”, and before return, it made no difference.

AmphenolSensors: modified your code slightly

See Reply #4

I am not going to try to patch your code and probably introduce errors in the process

...R

One way to deal with a possibly disconnected sensor is to record millis() each time you got a valid message from it. Then, before using the sensor result, check that it is not too old.