Recieving multiple parts of information over serial

Hi all,

I'm working on learning how to interface PC software with embedded devices at the moment, working on my own board based around the SAM3X8E - but I'm using both my Due & Uno to test code snippets.

I've made a simple Windows app in Visual Studio 2010, with three buttons - one to switch on the onboard LED, one to switch off and another to flash the LED. I found some VB code online which I added to to make this work.

Imports System.IO
Imports System.IO.Ports
Imports System.Threading

Public Class Form1
    Shared _continue As Boolean
    Shared _serialPort As SerialPort

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        SerialPort1.Close()
        SerialPort1.PortName = "com4"
        SerialPort1.BaudRate = 9600
        SerialPort1.DataBits = 8
        SerialPort1.Parity = Parity.None
        SerialPort1.StopBits = StopBits.One
        SerialPort1.Handshake = Handshake.None
        SerialPort1.Encoding = System.Text.Encoding.Default
    End Sub


    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        SerialPort1.Open()
        SerialPort1.Write("1")
        SerialPort1.Close()
    End Sub


    Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
        SerialPort1.Open()
        SerialPort1.Write("0")
        SerialPort1.Close()
    End Sub

    Private Sub Button3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button3.Click
        SerialPort1.Open()
        SerialPort1.Write("2")
        SerialPort1.Close()
    End Sub

End Class

I then wrote the Arduino code which simply takes the value sent over serial, and does something with it, as below.

int led_state = 0;
byte led = 13;

void setup() {

  Serial.begin(9600);
  pinMode(led, OUTPUT);
}

void loop() {
  
  if (Serial.available() > 0){
    led_state = Serial.read();
  }
  
  if (led_state == 49){ // 49 == ASCII for 1
    digitalWrite(led, HIGH);
  }
  else if (led_state == 48){ // 48 == ASCII for 0
    digitalWrite(led, LOW);
  }
  else if (led_state == 50){ // 50 == ASCII for 2
  digitalWrite(led, HIGH);
  delay(500);
  digitalWrite(led, LOW);
  delay(500);
  }
  
}

// program works, although any value high than 2 switches LED off, why?

However, what I want to be able to achieve - is to send multiple values over serial, and have the Arduino decide what's what. For example, currently I'm just sending one byte of data and doing something based on the ASCII character.

Say I want to be able to switch the LED on and off, but also control the delay times for the flash - I guess I need a way to tell the Arduino "use this bit of data to do that".

I was thinking, perhaps - send the value of 1, and if the value of 1 is recieved, the next piece of data is regarding to that, if it were 2 for example, then it would be related to something else.

Tips anybody? Thanks

  if (led_state == 49){ // 49 == ASCII for 1

Just write:

  if (led_state == '1'){

As for the rest, I'm not sure I understand, but does this help?

Yes, absolutely! 'State machine' behaviour is exactly what I'm trying to achieve - sending a string of data and taking each part to do something different with - i.e. setting values of variables in the code.

However, when I tried using 1 instead of 49, nothing happened when I sent 0,1 or 2.

The main issue with this I presume, is the need to have a check sum to check all the data came in...

jtw11:
However, when I tried using 1 instead of 49, nothing happened when I sent 0,1 or 2.

So don't use 1, use '1'.

Ah - I didn't notice the single quotes. Are these a C++ operator to tell the compiler not to read characters as ASCII?

jtw11:
Ah - I didn't notice the single quotes. Are these a C++ operator to tell the compiler not to read characters as ASCII?

Single quotes tell the compiler to get the value from the ASCII table. Without the single quotes (or any other specifier), the values is assumed to be decimal.

Thanks for the single quotes reference - that has simplified some other bits too.

Regarding Nick's example state machine - I can't say I follow the flow of it at all. Firstly, having copy and pasted it and run it on both an Uno and a Due, it's behaviour is not what I expected.

If you send it R4500S80G3 for example, it sends back RPM and Speed, but not gear until you sent it something else?

I think something I'm hung up on, is the actual data flow - how many bytes of data is 'R4500S80G3' anyway? The Serial.available part is something I'm hung up on, it dosen't check if Serial.available is greater than 0?

When you read something from the serial buffer using Serial.read, does it then erase that byte it's read?

So say you had 2 bytes in the buffer, and you called Serial.read once, would the buffer now only contain 1 byte?

Thank you!

jtw11:
If you send it R4500S80G3 for example, it sends back RPM and Speed, but not gear until you sent it something else?

Post the code you're using.

I think something I'm hung up on, is the actual data flow - how many bytes of data is 'R4500S80G3' anyway?

Count the number of characeters. Add 1 if you are appending new line or carriage return to it, 2 if you are doing both.

it dosen't check if Serial.available is greater than 0?

What doesn't?

When you read something from the serial buffer using Serial.read, does it then erase that byte it's read?

Erase from where, the buffer? Yes. When a byte is read, it is taken out of the buffer.

So say you had 2 bytes in the buffer, and you called Serial.read once, would the buffer now only contain 1 byte?

Yes.

Post the code you're using.

// Example state machine reading serial input
// Author: Nick Gammon
// Date: 17 December 2011

// the possible states of the state-machine
typedef enum {  NONE, GOT_R, GOT_S, GOT_G } states;

// current state-machine state
states state = NONE;
// current partial number
unsigned int currentValue;

void setup ()
{
  Serial.begin (115200);
  state = NONE;
}  // end of setup

void processRPM (const unsigned int value)
{
  // do something with RPM 
  Serial.print ("RPM = ");
  Serial.println (value);
} // end of processRPM

void processSpeed (const unsigned int value)
{
  // do something with speed 
  Serial.print ("Speed = ");
  Serial.println (value);
} // end of processSpeed

void processGear (const unsigned int value)
{
  // do something with gear 
  Serial.print ("Gear = ");
  Serial.println (value);  
} // end of processGear

void handlePreviousState ()
{
  switch (state)
  {
  case GOT_R:
    processRPM (currentValue);
    break;
  case GOT_S:
    processSpeed (currentValue);
    break;
  case GOT_G:
    processGear (currentValue);
    break;
  }  // end of switch  

  currentValue = 0; 
}  // end of handlePreviousState

void processIncomingByte (const byte c)
{
  if (isdigit (c))
  {
    currentValue *= 10;
    currentValue += c - '0';
  }  // end of digit
  else 
  {

    // The end of the number signals a state change
    handlePreviousState ();

    // set the new state, if we recognize it
    switch (c)
    {
    case 'R':
      state = GOT_R;
      break;
    case 'S':
      state = GOT_S;
      break;
    case 'G':
      state = GOT_G;
      break;
    default:
      state = NONE;
      break;
    }  // end of switch on incoming byte
  } // end of not digit  
  
} // end of processIncomingByte

void loop ()
{
  if (Serial.available ())
    processIncomingByte (Serial.read ());

  // do other stuff in loop as required
  
}  // end of loop

Count the number of characeters. Add 1 if you are appending new line or carriage return to it, 2 if you are doing both.

That'll be 10 then? Not to sound stupid, but - a new line? Is that a specific ASCII character, same with carriage return?

What doesn't?

This dosen't - it dosen't check is the serial buffer has anything greater than 0 in it? Surely that would be if (Serial.available() > 0)

  if (Serial.available ())
    processIncomingByte (Serial.read ());
    // The end of the number signals a state change

This is the reason why it didn't process the gear input. It has no idea whether you are done with the gear number or not. All it know is that you have sent it 3 so far. Is that 3? 33? 31002134? When it receives a non-numeric character, it then decides that you are done sending it the number. The easiest way to correct this would be to select "Newline" from the line ending drop down.

That'll be 10 then? Not to sound stupid, but - a new line? Is that a specific ASCII character, same with carriage return?

Yes, 10. A new line is also an ASCII character ('\n'). Same with carriage return ('\r').

This dosen't - it dosen't check is the serial buffer has anything greater than 0 in it? Surely that would be if (Serial.available() > 0)

That's the proper way to do it, but it still works because any non-zero if condition is evaluated as true.

Ah, I've stuck a random character after the input and got all three bits of data back - i'll try and work the code out some more, and write myself a framework that I understand a little better.

As for non-zero if statements evaluating true, that makes much more sense. Cheers!

However, what I want to be able to achieve - is to send multiple values over serial, and have the Arduino decide what's what.

Look at this...
http://forum.arduino.cc/index.php?topic=147550.0
Essentially, I send a verb and then numeric values to satisfy the argument list. However, you could exploit same concept to build about anything from turning on LEDs to returning the amount of free SRAM.

Ray

Simple code that uses a , as a delimiter to indicate that the end of the data packet has arrived and processing of the data packet can begin.

//zoomkat 3-5-12 simple delimited ',' string parse 
//from serial port input (via serial monitor)
//and print result out serial port
// CR/LF could also be a delimiter
//send on, or off, from the serial monitor to operate LED

int ledPin = 13;
String readString;

void setup() {
  Serial.begin(9600);
  pinMode(ledPin, OUTPUT); 
  Serial.println("serial LED on/off test with , delimiter"); // so I can keep track
}

void loop() {

  if (Serial.available())  {
    char c = Serial.read();  //gets one byte from serial buffer
    if (c == ',') {
      Serial.println(readString); //prints string to serial port out
      //do stuff with the captured readString
      if(readString.indexOf("on") >=0)
      {
        digitalWrite(ledPin, HIGH);
        Serial.println("LED ON");
      }
      if(readString.indexOf("off") >=0)
      {
        digitalWrite(ledPin, LOW);
        Serial.println("LED OFF");
      }       
      readString=""; //clears variable for new input
    }  
    else {     
      readString += c; //makes the string readString
    }
  }
}