Reading serial after interrupt -> corrupt data

My Arduino is in powerDown mode, incoming serial data drives INT0 to wake up.
The data comes in two bursts, maybe 5ms apart, with 9600 baud:
First "/nC," (a newline, C for celsius and a comma to seperate)
Second "xy.abc" (a temperature value)

However the first 3 chars get corrupted. I wouldnt care about that since I dont need those, but the corrupted data is "h±". I didnt find a way to replace that ±, I get errors that are multiple chars. So this is not working:
if (inChar == '±') {
inChar = '\n';
}

warning: multi-character character constant [-Wmultichar]

     if (inChar == '±')

I see 3 ways to solve the issue but I cant implement them:

  • Capture the data correctly, I gues the wakeup time is too high for that
  • Correct the corrupted data, its always identically shifted, but I dont know how to do that to the serial data
  • Somehow get rid of the ±, replace it with a linefeed
    Please give me some advice.
#include <SdFat.h>
#include <SPI.h>
#include <LowPower.h>
const int chipSelect = 10;
SdFat SD;
int val = 0;

const int dPin = 2; // interrupt 0


String inputString = "";         // a string to hold incoming data
boolean stringComplete = false;  // whether the string is complete
int warten;


void setup()  {
  pinMode(5, OUTPUT);
  Serial.begin(9600);                                   // open serial port, set the baud rate as 9600 bps

  inputString.reserve(512);         // reserve 512 bytes for the inputString:
  delay(250);

  pinMode(dPin, INPUT);
  Serial.print("an");
}

void loop() {
  if ((millis() - warten) > 50) {
    Serial.println();
    Serial.println("sl");
    Serial.flush();
    attachInterrupt(0, serialEvent, LOW);
    LowPower.adcNoiseReduction(SLEEP_FOREVER, ADC_OFF, TIMER2_OFF);
    detachInterrupt(0);
    Serial.print("wk");
    warten = millis();
  }

  if (stringComplete) {               // print the string when a newline arrives
    digitalWrite(5, HIGH);           //SD-card power on
    //    delay(10);
    if (!SD.begin(chipSelect)) {                            // see if the card is present and can be initialized
      Serial.println("Card failed or not present");
      delay(50);
      return;
    }
    File dataFile = SD.open("log.txt", FILE_WRITE);
    dataFile.print(inputString);
    dataFile.close();
    Serial.println(inputString);
    Serial.flush();
    inputString = "";                  // clear the string
    stringComplete = false;
  }

  else {
    digitalWrite(5, LOW);  //SD-card power off
  }


}
void serialEvent() {

  while (Serial.available()) {
    //        Serial.println("serial");
    char inChar = (char)Serial.read();       // get the new byte:
    Serial.print(inChar);

    if (inChar == '±') {                       
      inChar = '\n';
    }

    inputString += inChar;                    // add it to the inputString

    if (val == 10) {
      stringComplete = true;                 // so the main loop can do something about it:
      val = 0;
    }
  }
}

Does the data you want to receive cause the wake-up?

Is it possible to prefix the real data with some stuff that can be discarded?

Have a look at the 3rd example in Serial Input Basics and how it uses start- and end-markers.

...R

Does the data you want to receive cause the wake-up?

No problem here.

Is it possible to prefix the real data with some stuff that can be discarded?

That is already the case. But how to discard it?
The faulty char, that plusminus, it not actually a char but... check the error in my first post. If i could just reference to that "char" I simply replace it and everything is perfect.

Have a look at the 3rd example in Serial Input Basics and how it uses start- and end-markers.

The incoming signal is fixed, I cant change it.

PS: What is it with that 5min period? Very annoying. Does that stop at some number of posts?

Eheran:

Does the data you want to receive cause the wake-up?

No problem here.

That is not an answer to my question. But I assume from what you say that the answer is "yes"

The reason I asked is because if this was my project I would be inclined to send two messages. The first would wake the device up and it would not matter if it is garbage. The second would be sent after the Arduino is awake and could be expected to receive it accurately.

That is already the case. But how to discard it?
The faulty char, that plusminus, it not actually a char but... check the error in my first post. If i could just reference to that "char" I simply replace it and everything is perfect.
The incoming signal is fixed, I cant change it.

The fact that you can't change the signal is a nuisance.

What is the terminating character for the message? Suppose you read in the whole message and then count back from the end to the point where the useful data starts. That way there is no need to deal with the strange character.

PS: What is it with that 5min period? Very annoying. Does that stop at some number of posts?

This was introduced to combat spammers. It goes away after you have made 100 (?) posts.

...R

What is the terminating character for the message? Suppose you read in the whole message and then count back from the end to the point where the useful data starts. That way there is no need to deal with the strange character.

There is no termination character. Its just the last digit. The temperature could be from anywhere like 3.2°C up to something like 145.1938°C. So counting back is no option.
Right now ive made it work via counting up, it seems to be reliable with the "two" faulty chars.
Then I simply add a linefeed instead of these.
But now for some reason the linefeed is only present when I print the string to my serial monitor but its NOT there on the SD-data log, even tho thats the same string?!

void serialEvent() {

  while (Serial.available()) {
    char inChar = (char)Serial.read();       // get the new byte:
    charnum++;                                   //charnum gets reset in the loop

    if (charnum > 2 && !linefeed) {
      inputString += '\n';
      linefeed = 1;                                //linefeed gets reset in the loop
    }
    if (charnum > 2) {                          
      inputString += inChar;
    }
  }
}

In the loop:

    dataFile.print(inputString);
    dataFile.close();
    Serial.print(inputString);
    Serial.flush();

These two strings are NOT identical now. They used to be identical when I didnt add the linefeed myself but used that of the incoming data and are not anymore when I add \n myself. I dont get it.

Eheran:
There is no termination character. Its just the last digit.

Are you quite sure there is no linefeed or carriage-return character?

if not, the best way to detect the final character is to measure the time between characters and if it exceeds some reasonable figure (perhaps 3 millisecs if you are using 9600 baud) then you know there will be no more.

This is not a robust way to receive data

while (Serial.available()) {

because the Arduino can empty the buffer much faster than data arrives - even at a high baud rate.
Have a look at the examples in the link I gave you in Reply #1.

You should add some code to print out the ascii values of the offending characters - convert the characters to bytes and print them. When you know what the Arduino sees you may be able to write some code to specifically ignore them.

...R

Are you quite sure there is no linefeed or carriage-return character?

It was a unkown signal, I decoded it with my scope. Im sure that its as stated in the first post, yes.
However the problem with missing newlines on the SD-card was the carriage return, I didnt include it, its in the signal. Works now. Just got to tune the timing now.

How is that not robust?
Ur link gives that while function too: "If you need to ensure the Serial input buffer is empty you can do so like this"

convert the characters to bytes and print them.

Great idea.
It happens to be "177", some sort of filled rectangle. No idea why it displays a plusminus which is 241.
But I cant filter it out via:

    if (inChar == byte(177)) {                    
      inChar = '\n';
    }

Now im counting the incoming chars and simply start at 3. But it would still be better to be able to relate to that char directly and/or know it for the future.

What about this

if ( (byte) inChar == 177) {

The word byte in brackets cause the compiler to cast the char variable to a byte before it makes the comparison.

However it has just occurred to me that it would be more sensible to receive all your data as bytes (rather than as chars) if some of the values will exceed 127. You can later cast them to chars if necessary.

...R

As of now everything is working as intended. Thanks for you help.
It would be another option to simply remove every byte >100, might be more fail save. I will check it now if I get any problems, if there are any I might change it to a byte filter.
Or even limit the chars to numbers and the decimal point only. Hm...

This works and prints the weird plusminus:

   if ( (byte) inChar == 177) {
      Serial.println(inChar);
    }

This is the current code, Ill add a RTC to compensate the not working millis() timer in the sleep mode:

#include <SdFat.h>
#include <SPI.h>
#include <LowPower.h>
const int chipSelect = 10;
SdFat SD;
int val = 0;

const int dPin = 2; // interrupt 0


String inputString = "";         // a string to hold incoming data
boolean stringComplete = false;  // whether the string is complete
bool fertig = 1;
int warten = 0;
bool linefeed = 0;
bool an = 1;
int num;

char inChar;
boolean newData = false;


void setup()  {
  pinMode(5, OUTPUT);
  Serial.begin(9600);                                   // open serial port, set the baud rate as 9600 bps

  inputString.reserve(512);         // reserve 512 bytes for the inputString:
  delay(250);

  pinMode(dPin, INPUT);
  Serial.println("an");
  Serial.flush();
}

void loop() {

  recvoneChar();


  if (an) {
    attachInterrupt(0, recvoneChar, CHANGE);
    LowPower.powerDown(SLEEP_FOREVER, ADC_OFF, BOD_OFF);
    detachInterrupt(0);
    an = 0;
  }


  if ((millis() - warten) > 50) {
    val++;
    if (val == 10) {
      stringComplete = true;
      val = 0;
    }
    if (stringComplete) {
      digitalWrite(5, HIGH);
      if (!SD.begin(chipSelect)) {                            // see if the card is present and can be initialized:
        Serial.println("Card failed or not present");
        delay(50);
        return;
      }
      File dataFile = SD.open("log.txt", FILE_WRITE);
      dataFile.print(inputString);
      dataFile.close();
//      Serial.println();
      Serial.print(inputString);
//      Serial.println();
      Serial.flush();
      inputString = "";                  // clear the string:
      stringComplete = false;
      digitalWrite(5, LOW);
    }


//    Serial.println(",  sl");
//    Serial.flush();
    attachInterrupt(0, recvoneChar, LOW);
    LowPower.powerDown(SLEEP_FOREVER, ADC_OFF, BOD_OFF);
    detachInterrupt(0);
    warten = millis();
//    Serial.print("wk  ,");
    linefeed = 0;
    num = 0;

  }


}


void recvoneChar() {


  if (Serial.available() > 0) {
    inChar = Serial.read();
    newData = true;
    num++;
//   if ( (byte) inChar == 177) {
//     Serial.println();
//      Serial.println(inChar);
//    }
    if (num > 2) {
      if (!linefeed) {
        //       val++;
        linefeed = 1;
        inputString += '\r';
        inputString += '\n';
        inputString += millis();                   // add timestamp
        inputString += ",";                   // add timestamp
      }
      inputString += inChar;                   // add it to the inputString:
    }
  }

  if (newData == true) {
//    Serial.print(inChar);
    //  Serial.println(byte(inChar));
    newData = false;
  }

  digitalWrite(5, LOW);

}

Eheran:
This works and prints the weird plusminus:

   if ( (byte) inChar == 177) {

Serial.println(inChar);
    }

You must understand that it is only a weird plusminus when your PC tries to display it. Within the Arduino it is a perfectly normal byte with a value of 177.

...R