0

I have searched several questions and through google but could not find a solution. I am simply trying to send a float number from python3 with struct to arduino or if there is any other ways other than processing a char array at the arduino side because it is very slow in my opinion..

My arduino code:

void setup() {
  // initialize the serial communication:
  Serial.begin(9600);
}

void loop() {

  unsigned long t = millis();
  float x = analogRead(A3);
  float y = analogRead(A2);
  float c = Serial.read();
  Serial.println(c);
  //Serial.print(t/1000.0);
  //Serial.print(" , ");
  //Serial.print(x);
  //Serial.print(" , ");
  //Serial.println(y);



  // wait a bit for the analog-to-digital converter to stabilize after the last
  // reading:
  //delayMicroseconds(200);
  //delay(2);
  delay(250);
}

My python3 code :

import serial

import struct

arduino = serial.Serial('/dev/ttyACM0', 9600)
data = arduino.write(struct.pack('f', 111.32))
while True:
#    arduino.write(b"123")
    data = arduino.write(struct.pack('f', 111.32))

Getting the serial output as :

163.00
222.00
66.00
215.00
163.00
222.00
66.00
215.00
163.00
222.00
66.00
215.00
163.00

How can i solve my problem ?

EDIT : I changed the arduino code according to the answer the new arduino code is

float f;
void setup(){
  Serial.begin(9600);
}

void loop(){
  while(Serial.available() >= 3){

    f = Serial.read() << 24;
    f = ((unsigned long) f) | (Serial.read() << 16);
    f = ((unsigned long) f) | (Serial.read() << 8);
    f = ((unsigned long) f) | Serial.read();
    Serial.println(f);
  }
  Serial.println(f);
}

However, the output is now as such:

ovf
ovf
ovf
ovf
ovf
ovf
ovf
4294941184.00
4294941184.00
4294941184.00
4294941184.00
4294941184.00
4294941184.00
4294941184.00

2 Answers 2

0

I found a better way dealing with the conversion from char to float (note that you yourself have to add the setup() function for initiating Serial):

float f;
byte *fdata = (byte *) &f;
int num = 0;

void loop()
{
  if(Serial.available()){
    char c = Serial.read();
    if(c == 'B'){
      fdata[3] = c;
      num = 1;
    } else if(num == 1){
      fdata[2] = c;
      num++;
    } else if(num == 2){
      fdata[1] = c;
      num++;
    } else if(num == 3){
      fdata[0] = c;
      num = 0;
      Serial.write(fdata, 4);
      Serial.println(f);
    }
  }
}

First we are defining our float variable f and then a pointer of type byte named fdata. We are setting this pointer to point to our float variable. Now you can treat this pointer as an array. Since the type of the pointer is byte, we can access the first byte of f with typing fdata[0]. The other bytes can be accessed through fdata[1], fdata[2] and fdata[3].

In the loop example we are checking, if there is any byte in the buffer and read it. If it is the value of the character 'B' (which I used as a trigger, that your float starts, since the float number 111.32 starts with this value) we copy it to the last (most significant) byte of the array (aka float variable f). One after another we are doing the same with the other bytes.

When we received the last of the 4 bytes, we write the value back to Serial, once as pure byte data and once as ASCII encoded string.

In Python you can use the code, you already used:

arduino.write(struct.pack('>f',111.32))

You can read the output of the above sketch back with

arduino.readline()

PS: From my tries I guess the previous problem with getting ovf was, that the byte order was reversed (which simply would have been solved by using <f for the struct in python). But I personally like this way better, because it uses less casting.

1
  • Thank you for this and previous answers !! This will do i guess. Feb 28, 2018 at 19:16
0

There is no way around dealing with serial bytewise, since the communication over serial relies on bytes as a unit. You can write the received bytes directly to the float variable. That is faster, than save them to a char array first:

float f = Serial.read() << 24;
f = ((unsigned long) f) | (Serial.read() << 16);
f = ((unsigned long) f) | (Serial.read() << 8);
f = ((unsigned long) f) | Serial.read();

With this the bytes of the float value have to be send with the order from most significant to least significant. I don't know, how the data is formatted, if you send a struct over python. The casting to unsigned long is important here to do the bitwise OR (unsigned long and float have both 4 bytes). (I tried it and it works for me)

Also be aware, that you shouldn't read from Serial, when there is no received byte in the buffer. You can check this with Serial.available(). If there is no new byte in the buffer the Serial.read() function returns an integer with value -1

EDIT: The code above works, if you transmit the data directly as byte. In your comment you wrote, that you want to write arduino.write(b"111.32") in python. Here you are transmitting an ASCII string representation of the float number 111.32. This is not as fast as transmitting byte data (since you may need way more bytes for one float than 4). In this case you can use either Serial.parseFloat() to get the float from the serial buffer (but ensure, that all bytes for this number have been received with Serial.available()) or you can save the bytes to a string and using String.toFloat() to get the float out of it.

Since you wanted to do it the fast way, you should transmit the numbers as byte data. To do this in python, have a look at the answer of this question. It explains the python code for it (be sure, that you get the ordering of the bytes the same in python and Arduino).

5
  • When I try your code it gives an error like invalid operands of types 'float' and 'int' to binary 'operator| to all |= operators. I can send the float number as arduino.write(b"111.32") . Will this be better for your approach? I also added the Serial.available() part thank you. Feb 28, 2018 at 14:58
  • Ah, ok. Now I understand, what you are trying to do. I will edit my answer. And yes, I didn't anticipated, that you have to cast
    – chrisl
    Feb 28, 2018 at 16:08
  • Thank you for your reply. However, I am still stuck at a point. You say that i have to send it as byte data and the way I change a float number to a byte representation is data = bytes(23.2) right ? and then i try arduino.write(data) but data = bytes(23.2) gives an error which says float object is not iterable. I know I ask much but can you also provide a python sample code please ? Or when i try to do it with arduino.write(struct.pack('>f', 23.2)) it gives the serial output as ovf Feb 28, 2018 at 16:38
  • I added a link to a question, which answers this for you
    – chrisl
    Feb 28, 2018 at 16:43
  • Thank you for the link. I have already checked that question and got the struct way from there. However, when I use your code to read the data i got overflow. Couldn't solve it. :( Updated the question accordingly Feb 28, 2018 at 16:56

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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