Skip to main content
Tweeted twitter.com/StackArduino/status/931513290617966593
edited tags
Link
user31481
user31481
improved
Source Link

Update! Using Wirewrap's suggestion to use read() which returns -1 if there is no data, I've used peek() which does almost the same, except it doesn't remove the character peeked at from the buffer. For reference here is the code used:

#define BAUDRATE 57600
#define DEBUGOUTPUT 1


// checksum variables
byte generatedChecksum = 0;
byte checksum = 0; 
int payloadLength = 0;
int payloadIndex;
byte payloadData[169] = {0};
byte poorQuality = 0;
byte attention = 0;
byte meditation = 0;

// system variables
long lastReceivedPacket = 0;
boolean bigPacket = false;

int state = 0;
const int STATE_WAIT_FOR_FIRST_A        = 0;
const int STATE_WAIT_FOR_SECOND_A       = 1;
const int STATE_WAIT_FOR_PAYLOAD_LENGTH = 2;
const int STATE_WAIT_FOR_PAYLOAD        = 3;
const int STATE_WAIT_FOR_CHECKSUM       = 4;

void setup() {
  Serial.begin(BAUDRATE);           // USB  
}
void loop() {}
void parsePayload(){
  poorQuality = 200;
  attention = 0;
  meditation = 0;

  for(int i = 0; i < payloadLength; i++) {    // Parse the payload
    switch (payloadData[i]) {
    case 2:
      i++;            
      poorQuality = payloadData[i];
      bigPacket = true;            
      break;
    case 4:
      i++;
      attention = payloadData[i];                        
      break;
    case 5:
      i++;
      meditation = payloadData[i];
      break;
    case 0x80:
      i = i + 3;
      break;
    case 0x83:
      i = i + 25;      
      break;
    default:
      break;
    } // switch
  } // for loop
  if(bigPacket) {
    Serial.print("PoorQuality: ");
    Serial.print(poorQuality, DEC);
    Serial.print(" Attention: ");
    Serial.print(attention, DEC);
    Serial.print(" Time since last packet: ");
    Serial.print(millis() - lastReceivedPacket, DEC);
    lastReceivedPacket = millis();
    Serial.print("\n");                     
  }
  bigPacket = false;   
}

void serialEvent(){
  if(Serial.peek() >= 0){
    switch(state){
      case STATE_WAIT_FOR_FIRST_A:
        if(Serial.read() == 170) state = STATE_WAIT_FOR_SECOND_A;
      break;
      case STATE_WAIT_FOR_SECOND_A:
        if(Serial.read() == 170) state = STATE_WAIT_FOR_PAYLOAD_LENGTH;
      break;
      case STATE_WAIT_FOR_PAYLOAD_LENGTH:
        payloadLength = Serial.read();
        if(payloadLength > 169){
          state = STATE_WAIT_FOR_FIRST_A;
          return;
        }
        generatedChecksum = payloadIndex = 0;
        state = STATE_WAIT_FOR_PAYLOAD;
      break;
      case STATE_WAIT_FOR_PAYLOAD:
        if(payloadIndex < payloadLength){
          payloadData[payloadIndex] = Serial.read();
          generatedChecksum += payloadData[payloadIndex];
          payloadIndex++;
        }else{
          state = STATE_WAIT_FOR_CHECKSUM;
        }
      break;
      case STATE_WAIT_FOR_CHECKSUM:
        checksum = Serial.read();
        generatedChecksum = 255 - generatedChecksum;
        if(checksum == generatedChecksum) {
          parsePayload();
          state = STATE_WAIT_FOR_FIRST_A;
        }else{
          state = STATE_WAIT_FOR_FIRST_A;
        }
      break;
    }
  }
}

Update! Using Wirewrap's suggestion to use read() which returns -1 if there is no data, I've used peek() which does almost the same, except it doesn't remove the character peeked at from the buffer. For reference here is the code used:

#define BAUDRATE 57600
#define DEBUGOUTPUT 1


// checksum variables
byte generatedChecksum = 0;
byte checksum = 0; 
int payloadLength = 0;
int payloadIndex;
byte payloadData[169] = {0};
byte poorQuality = 0;
byte attention = 0;
byte meditation = 0;

// system variables
long lastReceivedPacket = 0;
boolean bigPacket = false;

int state = 0;
const int STATE_WAIT_FOR_FIRST_A        = 0;
const int STATE_WAIT_FOR_SECOND_A       = 1;
const int STATE_WAIT_FOR_PAYLOAD_LENGTH = 2;
const int STATE_WAIT_FOR_PAYLOAD        = 3;
const int STATE_WAIT_FOR_CHECKSUM       = 4;

void setup() {
  Serial.begin(BAUDRATE);           // USB  
}
void loop() {}
void parsePayload(){
  poorQuality = 200;
  attention = 0;
  meditation = 0;

  for(int i = 0; i < payloadLength; i++) {    // Parse the payload
    switch (payloadData[i]) {
    case 2:
      i++;            
      poorQuality = payloadData[i];
      bigPacket = true;            
      break;
    case 4:
      i++;
      attention = payloadData[i];                        
      break;
    case 5:
      i++;
      meditation = payloadData[i];
      break;
    case 0x80:
      i = i + 3;
      break;
    case 0x83:
      i = i + 25;      
      break;
    default:
      break;
    } // switch
  } // for loop
  if(bigPacket) {
    Serial.print("PoorQuality: ");
    Serial.print(poorQuality, DEC);
    Serial.print(" Attention: ");
    Serial.print(attention, DEC);
    Serial.print(" Time since last packet: ");
    Serial.print(millis() - lastReceivedPacket, DEC);
    lastReceivedPacket = millis();
    Serial.print("\n");                     
  }
  bigPacket = false;   
}

void serialEvent(){
  if(Serial.peek() >= 0){
    switch(state){
      case STATE_WAIT_FOR_FIRST_A:
        if(Serial.read() == 170) state = STATE_WAIT_FOR_SECOND_A;
      break;
      case STATE_WAIT_FOR_SECOND_A:
        if(Serial.read() == 170) state = STATE_WAIT_FOR_PAYLOAD_LENGTH;
      break;
      case STATE_WAIT_FOR_PAYLOAD_LENGTH:
        payloadLength = Serial.read();
        if(payloadLength > 169){
          state = STATE_WAIT_FOR_FIRST_A;
          return;
        }
        generatedChecksum = payloadIndex = 0;
        state = STATE_WAIT_FOR_PAYLOAD;
      break;
      case STATE_WAIT_FOR_PAYLOAD:
        if(payloadIndex < payloadLength){
          payloadData[payloadIndex] = Serial.read();
          generatedChecksum += payloadData[payloadIndex];
          payloadIndex++;
        }else{
          state = STATE_WAIT_FOR_CHECKSUM;
        }
      break;
      case STATE_WAIT_FOR_CHECKSUM:
        checksum = Serial.read();
        generatedChecksum = 255 - generatedChecksum;
        if(checksum == generatedChecksum) {
          parsePayload();
          state = STATE_WAIT_FOR_FIRST_A;
        }else{
          state = STATE_WAIT_FOR_FIRST_A;
        }
      break;
    }
  }
}
Source Link

How to avoid blocking while loop reading Serial?

I am currently playing with a MindWave Mobile headset. I am parsing bluetooth data received from a connected BlueSMIRF Silver. The code I started from is a sample from the MindWave wiki and it works using hardware Serial. For reference here is the original code:

////////////////////////////////////////////////////////////////////////
// Arduino Bluetooth Interface with Mindwave
// 
// This is example code provided by NeuroSky, Inc. and is provided
// license free.
////////////////////////////////////////////////////////////////////////

#define LED 13
#define BAUDRATE 57600
#define DEBUGOUTPUT 1

#define GREENLED1  3
#define GREENLED2  4
#define GREENLED3  5
#define YELLOWLED1 6
#define YELLOWLED2 7
#define YELLOWLED3 8
#define YELLOWLED4 9
#define REDLED1    10
#define REDLED2    11
#define REDLED3    12

#define powercontrol 10

// checksum variables
byte generatedChecksum = 0;
byte checksum = 0; 
int payloadLength = 0;
byte payloadData[64] = {
  0};
byte poorQuality = 0;
byte attention = 0;
byte meditation = 0;

// system variables
long lastReceivedPacket = 0;
boolean bigPacket = false;

//////////////////////////
// Microprocessor Setup //
//////////////////////////
void setup() {

  pinMode(GREENLED1, OUTPUT);
  pinMode(GREENLED2, OUTPUT);
  pinMode(GREENLED3, OUTPUT);
  pinMode(YELLOWLED1, OUTPUT);
  pinMode(YELLOWLED2, OUTPUT);
  pinMode(YELLOWLED3, OUTPUT);
  pinMode(YELLOWLED4, OUTPUT);
  pinMode(REDLED1, OUTPUT);
  pinMode(REDLED2, OUTPUT);
  pinMode(REDLED3, OUTPUT);

  pinMode(LED, OUTPUT);
  Serial.begin(BAUDRATE);           // USB

}

////////////////////////////////
// Read data from Serial UART //
////////////////////////////////
byte ReadOneByte() {
  int ByteRead;

  while(!Serial.available());
  ByteRead = Serial.read();

#if DEBUGOUTPUT  
  Serial.print((char)ByteRead);   // echo the same byte out the USB serial (for debug purposes)
#endif

  return ByteRead;
}

/////////////
//MAIN LOOP//
/////////////
void loop() {


  // Look for sync bytes
  if(ReadOneByte() == 170) {
    if(ReadOneByte() == 170) {

      payloadLength = ReadOneByte();
      if(payloadLength > 169)                      //Payload length can not be greater than 169
          return;

      generatedChecksum = 0;        
      for(int i = 0; i < payloadLength; i++) {  
        payloadData[i] = ReadOneByte();            //Read payload into memory
        generatedChecksum += payloadData[i];
      }   

      checksum = ReadOneByte();                      //Read checksum byte from stream      
      generatedChecksum = 255 - generatedChecksum;   //Take one's compliment of generated checksum

        if(checksum == generatedChecksum) {    

        poorQuality = 200;
        attention = 0;
        meditation = 0;

        for(int i = 0; i < payloadLength; i++) {    // Parse the payload
          switch (payloadData[i]) {
          case 2:
            i++;            
            poorQuality = payloadData[i];
            bigPacket = true;            
            break;
          case 4:
            i++;
            attention = payloadData[i];                        
            break;
          case 5:
            i++;
            meditation = payloadData[i];
            break;
          case 0x80:
            i = i + 3;
            break;
          case 0x83:
            i = i + 25;      
            break;
          default:
            break;
          } // switch
        } // for loop

#if !DEBUGOUTPUT

        // *** Add your code here ***

        if(bigPacket) {
          if(poorQuality == 0)
            digitalWrite(LED, HIGH);
          else
            digitalWrite(LED, LOW);
          Serial.print("PoorQuality: ");
          Serial.print(poorQuality, DEC);
          Serial.print(" Attention: ");
          Serial.print(attention, DEC);
          Serial.print(" Time since last packet: ");
          Serial.print(millis() - lastReceivedPacket, DEC);
          lastReceivedPacket = millis();
          Serial.print("\n");                     
        }
#endif        
        bigPacket = false;        
      }
      else {
        // Checksum Error
      }  // end if else for checksum
    } // end if read 0xAA byte
  } // end if read 0xAA byte
}

One problem I have is the blocking while loop in the ReadOneByte function:

byte ReadOneByte() {
  int ByteRead;

  while(!Serial.available());
  ByteRead = Serial.read();

#if DEBUGOUTPUT  
  Serial.print((char)ByteRead);   // echo the same byte out the USB serial (for debug purposes)
#endif

  return ByteRead;
}

I am trying to avoid this so I started drafting a basic state machine approach:

#define BAUDRATE 57600

// checksum variables
byte generatedChecksum = 0;
byte checksum = 0; 
int payloadLength = 0;
byte payloadData[169] = {0};
byte poorQuality = 0;
byte attention = 0;
byte meditation = 0;

// system variables
long lastReceivedPacket = 0;
boolean bigPacket = false;

int payloadIndex;

int state = 0;
const int STATE_WAIT_FOR_FIRST_A        = 0;
const int STATE_WAIT_FOR_SECOND_A       = 1;
const int STATE_WAIT_FOR_PAYLOAD_LENGTH = 2;
const int STATE_WAIT_FOR_PAYLOAD        = 3;
const int STATE_WAIT_FOR_CHECKSUM       = 4;
const String stateNames[5] = {"waiting for first A","waiting for second A","waiting for payload length","accumulating payload","waiting for checksum"};

void setup() {
  Serial.begin(BAUDRATE);           // USB
}
void loop() {}
void parsePayload(){
  poorQuality = 200;
  attention = 0;
  meditation = 0;

  for(int i = 0; i < payloadLength; i++) {    // Parse the payload
    switch (payloadData[i]) {
    case 2:
      i++;            
      poorQuality = payloadData[i];
      bigPacket = true;            
      break;
    case 4:
      i++;
      attention = payloadData[i];                        
      break;
    case 5:
      i++;
      meditation = payloadData[i];
      break;
    case 0x80:
      i = i + 3;
      break;
    case 0x83:
      i = i + 25;      
      break;
    default:
      break;
    } // switch
  } // for loop
  Serial.print("bigPacket:");
  Serial.println(bigPacket);
  if(bigPacket) {
    Serial.print("PoorQuality: ");
    Serial.print(poorQuality, DEC);
    Serial.print(" Attention: ");
    Serial.print(attention, DEC);
    Serial.print(" Time since last packet: ");
    Serial.print(millis() - lastReceivedPacket, DEC);
    lastReceivedPacket = millis();
    Serial.print("\n");                     
  }
  bigPacket = false;   
}

void printState(){
  Serial.print("state:");
  Serial.println(stateNames[state]);
}
void serialEvent(){
  if(Serial.available() > 0){
    switch(state){
      case STATE_WAIT_FOR_FIRST_A:
        printState();
        if(Serial.read() == 170) state = STATE_WAIT_FOR_SECOND_A;
      break;
      case STATE_WAIT_FOR_SECOND_A:
        printState();
        if(Serial.read() == 170) state = STATE_WAIT_FOR_PAYLOAD_LENGTH;
      break;
      case STATE_WAIT_FOR_PAYLOAD_LENGTH:
        printState();
        payloadLength = Serial.read();
        Serial.print("payloadLength:");Serial.println(payloadLength);
        if(payloadLength > 169){
          Serial.println(payloadLength > 169);
          state = STATE_WAIT_FOR_FIRST_A;
          return;
        }
        generatedChecksum = payloadIndex = 0;
        state = STATE_WAIT_FOR_PAYLOAD;
      break;
      case STATE_WAIT_FOR_PAYLOAD:
        printState();
        if(payloadIndex < payloadLength){
          payloadData[payloadIndex] = Serial.read();
          generatedChecksum += payloadData[payloadIndex];
          Serial.print("payloadData[");Serial.print(payloadIndex);Serial.print(" of ");Serial.print(payloadLength);Serial.print("]: ");
          Serial.println(payloadData[payloadIndex]);
        }else{
          state = STATE_WAIT_FOR_CHECKSUM;
        }
      break;
      case STATE_WAIT_FOR_CHECKSUM:
        printState();
        checksum = Serial.read();
        generatedChecksum = 255 - generatedChecksum;
        if(checksum == generatedChecksum) {
          Serial.println("checksum MATCH! parsing payload");
          parsePayload();
          state = STATE_WAIT_FOR_FIRST_A;
        }else{
          Serial.println("checksum FAIL!");
          state = STATE_WAIT_FOR_FIRST_A;
        }
      break;
    }
  }
}

As far as I can understand from the serialEvent() reference this function would be called only when a new byte is available. I've also added a condition to check if Serial.available() > 0.

I can see the messages I expect when parsing the data, but only small packets(usually 4 bytes long) end up having a correct checksum and never receive a payload with the useful EEG data I'm looking for.

How can I check that my approach is correct or not/ I'm not loosing bytes using serialEvent() instead of the blocking while(!Serial.available()) ? If so, how can I rewrite the while loop in a non blocking way ?

I've not super experienced with Arduino, but I started reading on interrupts. Would a USART_RX interrupt help at all ? (or would it do the same as serialEvent -> trigger when a new byte is available?)