Reading serial binary data

Hello All,

I am trying to read serial data sent from my PC (Simulink) and control a servo motor position with the data. Simulink transmits the data as binary, the Real-Life-Value is a short float. I have included some frankenstien code, I've been trying everything. Maybe that's the problem. I have searched my toes off through the forums... debugging is hard since the COM port is used and I can't see the built-in serial monitor

Sorry if this is terse, I lost my old post by using the back button. It was witty and everything XD. I am happy to fill in details I forgot. Arduino Uno, Windows xp, some wires...

if (Serial.available() > 0) { // only loop the serial structure if its serialing

          digitalWrite(8,LOW);  // red off // light green if serial data, red if NO serial  
          digitalWrite(9,HIGH); // green on

          byte inbyte = '/0'; 
          int timeout = 1000; // waits 1 second for a number
          long startTime = millis(); // now is the start time
          delay(1); // try a delay
          while (millis() - startTime < timeout && inbyte != '*') { // this is the header character in the Simulink bloc. the loop waits for it
             inbyte = Serial.read(); // change the inbyte to the waiting byte
          }
          
          if (inbyte == '*') { // this means its the start of a number
             
             startTime = millis(); // start time is now
             while (millis() - startTime < timeout && Serial.available() < 6) { // allow 6 at least bytes to fill the buffer
             }  
             
             int i = 0; // start the counter             
             while (Serial.peek() != '\r') {   // search till you see the terminator
                angl[i] = Serial.read(); // read each character into a character array
                ++i; // increment i?
                if (angl[i] < '0' || angl[i] > 9) {
                  tsv = false; // if one character is bad, its all bad
                }
             }
             if (tsv == true) {
               union u_tag {
                 byte b[4];
                 float fvalu;
               } u; // this is a union http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1207242838
               
               u.b[0] = angl[0];
               u.b[1] = angl[1];
               u.b[2] = angl[2];
               u.b[3] = angl[3]; // still part of the union

               angval = u.fvalu; // could it be a real value?
               
               float ang = angval*90; // convert the scaled angvalue (0 --> 1) to degrees
               int angLED = angval*255; // prepare for the PWM signal
               // int angLEDpwm = abs(angLED); // absolute value for PWM LED // truncated?
               // check if the value looks write
               analogWrite(6,angLED); // debug
               blue.write(ang); // write the servo
             }
          }
        }
        else {
          digitalWrite(8,HIGH); // red ON
          digitalWrite(9,LOW); // green OFF
        }
      }

This is my first project with the arduino, its amazing. I feel like everyone should know about these.

Brian

Simulink transmits the data as binary, the Real-Life-Value is a short float.

Floats are all the same size. No such thing as a short float.

             while (Serial.peek() != '\r') {   // search till you see the terminator
                angl[i] = Serial.read(); // read each character into a character array
                ++i; // increment i?
                if (angl[i] < '0' || angl[i] > 9) {
                  tsv = false; // if one character is bad, its all bad
                }

You are then reading character data...

A float is 4 bytes on the Arduino, not 6. The bytes are not all in the range '0' to '9'.

Good to know. I'm still learning about all the different types. I guess I was referring to the different types in Matlab (single precision vs double precision). If I can assemble each number into a 4-byte array, is there a built-in method to convert it back into a number (float)?

Does Serial.read handle binary data, or is it looking for characters only?

will I have to manually assemble these 4 bytes into a number? (I am referring to the way that Wikipedia tells me that floats are represented with 4 bytes.

The bytes are not all in the range '0' to '9'.

What am I really looking at here then? :~

The way a float is stored in memory is completely different from the way the value looks when it is printed out. When printed out, the characters '0' through '9' are used. As is the decimal point.

If matlab is writing binary data (4 bytes) to the serial port, you need to read 4 bytes, and reassemble them as a float.

Each byte will hold a value between 0 and 255. The values '0' and '9' define a very small subset of the total range of valid values.

Does Serial.read handle binary data

Yes.

will I have to manually assemble these 4 bytes into a number? (I am referring to the way that Wikipedia tells me that floats are represented with 4 bytes.

Yes. It's probably easier to do it programatically, though.

I am trying to read serial data sent from my PC (Simulink) and control a servo motor position with the data.

The Servo::write() and Servo::writeMicroseconds() function take integers as input. So, why are we talking about floats?

Thanks for the help so far!!

I am making this too hard for myself. I converted the float before its sent from the PC, so now I am sending an integer -90 < int < +90 that will be written to the servo with *servo.*write(int).

I can't seem to find how to convert the two bytes into the integer programatically. Do I need to create an array? what is the syntax for a conversion?

My brain is saturated on this problem. I have been at it for days :~I will definitely post my solution so that someone with this same problem can solve it faster in the future

Brian

Here is the current code

if (Serial.available() > 0) { // only loop the serial structure if its serialing

          digitalWrite(8,LOW);  // red off // light green if serial data, red if NO serial  
          digitalWrite(9,HIGH); // green on

          byte inbyte = '\0'; // null? 
          int timeout = 1000; // waits 1 second for a number
          long startTime = millis(); // now is the start time
          delay(1); // try a delay
          while (millis() - startTime < timeout && inbyte != '*') { // this is the header character in the Simulink bloc. the loop waits for it
             inbyte = Serial.read(); // change the inbyte to the waiting byte
          }
  
          if (inbyte == '*') { // this means its the start of a number
             
             tsv = true; // 
             startTime = millis(); // start time is now
             
             while (millis() - startTime < timeout && Serial.available() < 6) { // allow 6 at least bytes to fill the buffer
             }  
                 
             int angval = Serial.read(); // read an integer  
       
             if (tsv == true) {
               
               int angLED = (angval/90)*255; // prepare for the PWM signal
               // int angLEDpwm = abs(angLED); // absolute value for PWM LED // truncated?
               // check if the value looks write
               analogWrite(6,angLED); // debug
               blue.write(angval); // write servo?
               delay(30);
             }
          }
        }

        else {
          digitalWrite(8,HIGH); // red ON
          digitalWrite(9,LOW); // green OFF
        }

Some simple servo control code that might be of interest to verify the servo hardware is workng.

// zoomkat 10-4-10 serial servo test
// type servo position 0 to 180 in serial monitor
// for writeMicroseconds, use a value like 1500
// for IDE 0019 and later
// Powering a servo from the arduino usually DOES NOT WORK.

String readString;
#include <Servo.h> 
Servo myservo;  // create servo object to control a servo 

void setup() {
  Serial.begin(9600);
  myservo.attach(7);  //the pin for the servo control 
  Serial.println("servo-test-21"); // so I can keep track of what is loaded
}

void loop() {

  while (Serial.available()) {
    delay(1);  
    if (Serial.available() >0) {
      char c = Serial.read();  //gets one byte from serial buffer
      readString += c; //makes the string readString
    } 
  }

  if (readString.length() >0) {
    Serial.println(readString);  //so you can see the captured string 
    int n;
    char carray[6]; //converting string to number
    readString.toCharArray(carray, sizeof(carray));
    n = atoi(carray); 
    myservo.writeMicroseconds(n); // for microseconds
    //myservo.write(n); //for degees 0-180
    readString="";
  } 
}

hey zoomcat,

Thaks for the reply! I know my hardware is working from other codes, my problem is reading teh binary serial data (2 byte integer) and converting this data into something that I can use with servo.write. This is what I'm currently trying...

if (inbyte == '*') { // this means its the start of a number
             
             tsv = true; // 
             startTime = millis(); // start time is now
             
             while (millis() - startTime < timeout && Serial.available() < 4) { // allow 4 at least bytes to fill the buffer
             }
            
             for (int i=0; i<2; i++) {
               b[i] = Serial.read();
             }
               
             
                 
             angval = int(b); // read an integer  
       
             if (tsv == true) {
               
               int angLED = (angval/90)*255; // prepare for the PWM signal
               // int angLEDpwm = abs(angLED); // absolute value for PWM LED // truncated?
               // check if the value looks write
               analogWrite(6,angLED); // debug
               blue.write(angval); // write servo?
               angval = 0; // reset?
               Serial.print(angLED);
               //delay(30);

my problem is reading teh binary serial data (2 byte integer) and converting this data into something that I can use with servo.write.

Without knowing what the two bytes are or what they repersent, it is hard to think of what needs to be done to them to make them relate to a servo control signal. Is one of the bytes more significant than the other?

Sorry for the confusion, Simulink is sending me a 16 bit signed integer in binary format. its a desired servo angle -90 < num +90

Simulink is sending me a 16 bit signed integer in binary format.

Is it sending the most significant byte first, or the least significant byte?

The simplest thing to do is send the data as a string.

I have it sending data LittleEndian (least significant first), although I can change it.

I cannot send strings with this model :0

I would think one would do something like get the decimal value of the least significant byte (0-255), and get the decimal value of the most significant byte, multiply that by 255 (or 256?), then add the two together to get the total decimal equivelant.

Zoom, that solved it. for someone like me (very new), its a snakeoil solution, but it worked! You're the man.

Now I have a serial timing issue to deal with, but that will have to wait for tomorrow. Below is the code that worked, although the serial timing is off. The servo should be following a smooth sine wave 0 --> 180º but it very jumpy.

for (int i=0; i<2; i++) {
                b[i] = Serial.read();
             }
             int angval2 = b[0]+b[1]*256;    
             //angval = int(b); // read an integer  
             int new_ang = angval2;

for someone like me (very new), its a snakeoil solution, but it worked!

It's hardly a snakeoil solution. It simply reverses the process that the sender used to extract the least significant and most significant bytes from the integer prior to sending.

Now I have a serial timing issue to deal with

Perhaps if you'd show all of your code, instead of just snippets, we could help with that, too. What baud rate are you using? Are there any delay()s anywhere?

Hey guys, I have finally solved the issue of controlling a servo with serial communication. The PC (Simulink) is sending a 16 bit integer in binary format that is the desired servo position in degrees. Thanks to PaulS and Zoomkat for all the help :grin:.

The serial timing issue was solved by eliminating the Serial.flush command (commented out in the code)

Thanks again Forum,
Brian

// Control the small blue servo
// Brian Leach
// april 12 2011
// integer value from simulink. use the signed 16 bit integer from simulink with servo.write
// Simulink is sending a value (in degrees) to position the servo.

#include <Servo.h> 

Servo blue;

int ang; // create integer to hold servo angle
int new_ang = 90;
int old_ang = 90;
byte b[2]; // byte array

void setup() {
	blue.attach(5, 1025, 1975); // servoName.attach(pin, min, max) 
        Serial.begin(38400); // begin serial @ 38400 bits/sec
        
        pinMode(8,OUTPUT); // debug lights. red no serial
        pinMode(9,OUTPUT); // debug light green yea serial
        pinMode(6,OUTPUT); // output 6 LED 
        //pinMode(5,OUTPUT); // output 5 servo 
        byte homey = 90;
        blue.write(homey);  // set servo to mid-point
        delay(1000);;
}

void loop() {
  
  //Serial.flush(); // clear the serial buffer before reading new data *** this was the problem. clearing data unnecessarily 
  memset(b, '\0', 2);
  boolean tsv = false; // boolean to decide if the servo should be sent commands
  int angval = 0; // integer value of angle in degrees
  
	if (Serial.available() > 0) { // only loop the serial structure if its serialing

          digitalWrite(8,LOW);  // red off // light green if serial data, red if NO serial  
          digitalWrite(9,HIGH); // green on

          char inbyte = '\0'; // null? 
          int timeout = 1000; // sets timeout to wait 1 second for a number
          long startTime = millis(); // now is the start time
          //delay(1); // try a delay
          while (millis() - startTime < timeout && inbyte != '*') { // this is the header character in the Simulink bloc. the loop waits for it
             inbyte = Serial.read(); // change the inbyte to the waiting byte
          }
  
          if (inbyte == '*') { // this means its the start of a number
             
             tsv = true; // this might eventually be an error check
             startTime = millis(); // start time is now
             
             while (millis() - startTime < timeout && Serial.available() < 3) { // allow 2 at least bytes to fill the buffer
             }
            
             for (int i=0; i<2; i++) {
                b[i] = Serial.read();
             }
             
             int angval2 = b[0]+b[1]*256; // converts the two bytes to an integer. thanks to Zoomkat for this solution    
             int new_ang = angval2; // sets the angle value for this loop
             
             if (old_ang != new_ang) { // if new angle equals old angle don't write anything
                if (tsv == true) {
               
                  int angLED = (angval2/180)*255; // prepare for the PWM signal
                  // int angLEDpwm = abs(angLED); // absolute value for PWM LED // truncated?
                  // check if the value looks write
              
                  analogWrite(6,angLED); // debug
                  blue.write(angval2); // write servo?
                  old_ang = angval2; // reset?
                  Serial.print(angLED);
               
                }
             }
             //delay(.5);
          }
       }
       

        else {
          digitalWrite(8,HIGH); // red ON
          digitalWrite(9,LOW); // green OFF
          analogWrite(6,0); // servo lite OFF
        }
        delayMicroseconds(2);
}

Hey, I tried your code to run my motors and all it does is go straight to 100% and saturates. What I'm trying to do is run my usb throttle to send numerical values to my edf. My setup is throttle at (0-100%)*Motor bandwidth + deadzone in milliseconds. In other words it should start from deadzone and throttle from 0 to 100%. I sent it through a signed int16 block, then a saturation block (with inherited datatype) then to serial send. I basically coped and pasted your code and modified it for writeMicroseconds.

Any thoughts on this?

Running Arduino Mega w/ ATMega1280

edit: The code I used is here

//The script simply reads the serial data received as throttle
//in milliseconds

#include <Servo.h>
Servo motor; //create servo object
Servo motor1; //create servo object

int new_throttle = 0;
int old_throttle = 0;
byte b[2]; // byte array

//-----------------------------
//----------Activate-----------
void activate(Servo motor,int n)
{
 motor.attach(n);                  // attach motor to pin n
 motor.writeMicroseconds(1000);    // write zero throttle to activate
 delay(1000);
}

void setup() 
{
  // activate motor first
  activate(motor,3); // forward motor 3
  activate(motor,4); // forward motor 1
  
  //connect to the computer
  Serial.begin(38400); // initialize serial ports 0 & 3
}

void loop()
{
  if (Serial.available() > 0) { // only loop the serial structure if its serialing

  int tval12 = 1280; // create integer to hold throttle value
  boolean tsv = false;
  char inbyte = '\0'; // null? 
  int timeout = 20; // sets timeout to wait 20 milliseconds for a number
  long startTime = millis(); // now is the start time
          
   while (millis() - startTime < timeout && inbyte != '*') { // this is the header character in the Simulink block. the loop waits for it
     inbyte = Serial.read(); // change the inbyte to the waiting byte
   }
          
   //reading data now
   if (inbyte == '*') { // this means its the start of a number
          
     tsv = true; // this might eventually be an error check
     startTime = millis(); // start time is now
          
     while (millis() - startTime < timeout && Serial.available() < 3) { // allow 2 at least bytes to fill the buffer
     }
       
     for (int i=0; i<2; i++) {
       b[i] = Serial.read();
     }
            
     int tval2 = b[0]+b[1]*255;
     int new_throttle = tval2; // sets the throttle value for this loop
            
     if (old_throttle != new_throttle) { //if throttle has changed write new one
       if (tsv == true) {
         
         //saturtate thrust at max (2000ms)
         if (new_throttle>= 2000)
         {
           new_throttle = 2000;
         }
         //Serial.print(new_throttle);
         motor.writeMicroseconds(new_throttle); // write motor
         motor1.writeMicroseconds(new_throttle); // write motor
         old_throttle = new_throttle; //save last value
            
       }
     }
   }
 }
}