1

I am new to Arduino and have tried all kinds of code for attempting to achieve this. None with any luck, however. I feel that it should be a relatively easy task and therefore, figured that someone out there with more proficiency with an Arduino than me, would be able to help.

Essentially, I have two commands sent to a device via the Leonardo serial port. One is the single character "R", and the other is the single character "O". The device then sends back 8 binary bytes (non ASCII) in floating point format per command. So if I send O, it will return 8 binary bytes, and then the same goes for if I sent R.

The commands O and R need to be simultaneously sent via the serial every 250ms and wait for their respective responses before sending the next command.

I also need to store the device's response to O and the device's response to R in respective variables that will have a new value every 250ms. I am not familiar with single point format and therefore am unaware of what variable system to use.

Here is my best, yet unsuccessful attempt:

unsigned long previousMillis = 0;
const long interval = 250;
int responseO;
int responseR;

void setup()
{
    Serial.begin(9600);

    while(!Serial);
}

void loop()
{
    unsigned long currentMillis = millis();

    if(currentMillis - previousMillis >= interval) { //timer for every 250ms
        previousMillis = currentMillis;
        Serial.println("R");               //Figured that "println" with a string for the character R should be ok

        if(Serial.available() > 0) {          //Don't know if I am correct here, I was hoping that the serial would only be read if the device responded to the command R
            responseR = Serial.read();       //Read serial for device's response to command R and save it in a variable
        }

        Serial.println("O");               //Do the same thing for the command O

        if(Serial.available() >0) {
          responseO = Serial.read();
          }
    }
}

However, whenever I test this, it appears that the program is unable to differentiate between which responses are due to one command, and treats one response as if both commands requested it when only one did. Also is a floating point format variable just a float?

Thank you so so much, I really need some help this has been burning away at me for days........

Cheers, Harry

1

The code shown in two previous answers is problematic.

For example, in Look Alterno's code, Serial.println("R") will trigger an R response. The while(!Serial.available()); construct will wait for the first byte of the response. Then responseR = Serial.read(); will read one byte. Next, Serial.println("O") will attempt to trigger an O response. The second while(!Serial.available()); will wait for a byte to be available, which should happen when the second byte of the R response arrives. Then responseO = Serial.read() will read that byte.

In ratchet freak's code, each quarter second bytesToReadFromR and bytesToReadFromO are set to 8. In the next several passes through loop(), if(bytesToReadFromR) and if(bytesToReadFromO) are satisfied, so while(bytesToReadFromR && Serial.available()) and while(bytesToReadFromO && Serial.available()) will be controlled by whether a character is available. At 9600 bps, about one character per millisecond will become available, so most of the time we are just spinning through loop() dozens of times per millisecond, waiting for characters. The if statement for bytesToReadFromR has a slightly higher chance of catching fresh characters, but which buffer (responseR or responseO) gets any given character still is left to chance.

Instead of the code previously shown, consider something like the following:

enum { dataBytes = 8, interval=250 };

float getResult (char trigger) {
  union { byte d[dataBytes], float f[2] } u;
  byte rin=0;
  Serial.print(trigger);
  while (rin < dataBytes) {
    if(Serial.available())
      u.d[rin++] = Serial.read();
  }
  return u.f[0];        // Assumes bytes in forward order
}

unsigned long prevMillis = millis();
float RValue, OValue;

void loop() {
  if(millis() - prevMillis >= interval) {
    prevMillis = millis();
    RValue = getResult ('R');
    OValue = getResult ('O');
    // put code here to consume the results
    // ...
  }
}

Note, this code will get stuck if too-few characters arrive from the R&O device, or if some characters get lost for any reason. One could add a timeout to the loop in getResult(); it could check for millis() - prevMillis >= interval and if so, break the loop and return a NAN.

Note, this code assumes the byte-order of floats from the R&O device matches that of the Arduino. That will be so if both are big-endian, or if both are little-endian. If they differ, store bytes in reverse order into the u.d array, and return u.f[1] instead of u.f[0].

Note, union is a C / C++ technique for accessing data multiple ways. The declaration union { byte d[dataBytes], float f[2] } u says that u is a structure where the array d starts at the same address as the array f. Thus, the float u.f[0] corresponds to the first four bytes of u.d, and u.f[1] to the next four.

Note, if the R&O device requires an end-of-line character, change Serial.print(trigger) to Serial.println(trigger).

0

After printing a char in the Serial, you have to wait until some response is received. For that, you use a while

void loop()
{
    unsigned long currentMillis = millis();

    if(currentMillis - previousMillis >= interval) { //timer for every 250ms
        previousMillis = currentMillis;

        Serial.println("R");               //Figured that "println" with a string for the character R should be ok
        while(!Serial.available());
        responseR = Serial.read();       //Read serial for device's response to command R and save it in a variable

        Serial.println("O");               //Do the same thing for the command O
        while(!Serial.available());    
        responseO = Serial.read();
    }    
}

Now, you have to tell us which floating point format are you using.

0

each call to Serial.read will read a single byte. So to read multiple bytes it needs to be called multiple times.

int bytesToReadFromR;
int bytesToReadFromO;

void loop()
{
    unsigned long currentMillis = millis();

    if(currentMillis - previousMillis >= interval) { //timer for every 250ms
        previousMillis = currentMillis;

        Serial.println("R");               
        bytesToReadFromR = 8;              //mark that there is data to read from Serial for R

        Serial.println("O");               
        bytesToReadFromO = 8;              //Do the same thing for the command O
    }   

    if(bytesToReadFromR){
        while(bytesToReadFromR && Serial.available()){
           responseR[--bytesToReadFromR] = Serial.read();       //Read serial for device's response to command R and save it in a char array
        }
        if(bytesToReadFromR){
             //finished transfer, trigger what you need
        }
    }
    if(bytesToReadFromO){
        while(bytesToReadFromO && Serial.available()){
           responseO[--bytesToReadFromR] = Serial.read();       //Read serial for device's response to command O and save it in a char array
        }
        if(bytesToReadFromO){
             //finished transfer, trigger what you need
        }
    }
}
  • Cheers for the quick reply! The floating point format is IEEE. The device essentially sends 8 bytes in response. 4 bytes of which equal a number, and the other 4 equal another number. (For what it's worth, it's just returning coordinates as 8 bytes). Can I somehow interpret those 8 bytes into the two numbers, which would likely be decimals? – Harry Stuart Aug 17 '17 at 14:53
0

As others have mentioned, you have to read the serial input byte by byte. 4 bytes means you have to read it four times and store the values in an array. Then do your calculations with the array.

After a little googling I found this: https://stackoverflow.com/questions/3991478/building-a-32bit-float-out-of-its-4-composite-bytes-c

Which is basically what you want to do. Try this code:

unsigned long previousMillis = 0;
const long interval = 250;

void setup() {
  Serial.begin(9600);
  while (!Serial);
}

void loop() {

  unsigned long currentMillis = millis();

  if (currentMillis - previousMillis >= interval) //timer for every 250ms
  {

    previousMillis = currentMillis;

    Serial.println("R");               //maybe substitute with
    if(Serial.available() >0)          //Serial.println('R'); or 
    {                                  //Serial.write("R");   or 
      byte buff1[4];                   //Serial.write('R');
      byte buff2[4];
      float firstNumber;
      float secondNumber;

      for(int i=3; i>=0; i--){
        buff1[i] = Serial.read();
      }
      for(int i=3; i>=0; i--){
        buff2[i] = Serial.read();
      }

      memcpy(&firstNumber, &buff1, sizeof(firstNumber));
      memcpy(&secondNumber, &buff2, sizeof(secondNumber));

      Serial.print("first number is: ");
      Serial.println(firstNumber);
      Serial.print("second number is: ");
      Serial.println(secondNumber);

    }

    //do the same thing with your "O" command, or write a function

  }

}

There are two things to consider though:

  • As you can see I am looping the buff1 and buff2 arrays backwards. This is because the endianness of the float depends on how your device sends them. If you were receiving gibberish change the loops to count from 0 to 4.

  • You said your device expects a single 'R' character but what you are doing is sending a "R" string and then a new line character after that(because you are using println()). It's possible that you will have to substitute that with one of the options I mentioned in the code comment.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.