Go Down

Topic: Reading serial binary data (Read 23150 times) previous topic - next topic

brian15co

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...

Code: [Select]
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

PaulS

Quote
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.

Code: [Select]
             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'.
The art of getting good answers lies in asking good questions.

brian15co

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.

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


What am I really looking at here then? :~

PaulS

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.

Quote
Does Serial.read handle binary data

Yes.

Quote
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.

Quote
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?
The art of getting good answers lies in asking good questions.

brian15co

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

brian15co

Here is the current code

Code: [Select]
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
        }

zoomkat

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

Code: [Select]

// 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="";
  }
}
Google forum search: Use Google Search box in upper right side of this page.
Why I like my 2005 Rio Yellow Honda S2000  https://www.youtube.com/watch?v=pWjMvrkUqX0

brian15co

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...

Code: [Select]
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);

zoomkat

Quote
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? 
Google forum search: Use Google Search box in upper right side of this page.
Why I like my 2005 Rio Yellow Honda S2000  https://www.youtube.com/watch?v=pWjMvrkUqX0

brian15co

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

PaulS

Quote
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.
The art of getting good answers lies in asking good questions.

brian15co

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

I cannot send strings with this model  :0

zoomkat

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.
Google forum search: Use Google Search box in upper right side of this page.
Why I like my 2005 Rio Yellow Honda S2000  https://www.youtube.com/watch?v=pWjMvrkUqX0

brian15co

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.

Code: [Select]
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;

PaulS

Quote
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.

Quote
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?
The art of getting good answers lies in asking good questions.

Go Up