logo elektroda
logo elektroda
X
logo elektroda

How to send float values between Arduino over UART?

omnixcrs 2436 52
ADVERTISEMENT
Treść została przetłumaczona polish » english Zobacz oryginalną wersję tematu
  • #1 18208876
    omnixcrs
    Level 11  
    Hello Ladies and Gentlemen,
    I need to send a value of e.g. 22.5 which is a float between two Arduino's over the UART. While I can use the following codes to send int data, I don't know how to do it as a float.
    The sending arduino uses these instructions:
    
    void setup() {
      Serial.begin(9600);
    }
    
    void loop()
     {
      Serial.write(45);
    }
    
    .
    Whereas in the receiving arduino :
    
    int incomingByte = 0; 
    
    void setup() {
      Serial.begin(9600); 
    }
    
    void loop() {
      
      if (Serial.available() > 0) {
     
        incomingByte = Serial.read();
        Serial.println(incomingByte, DEC);
      }
    }
    .
    Do you have a problem with Arduino? Ask question. Visit our forum Arduino.
  • ADVERTISEMENT
  • #2 18208944
    krisRaba
    Level 31  
    I don't know if there will be any nifty functions for this in Arduino, but in general a float like this is a 32bit variable, so you can dismember it and send it piece by piece, and glue it back together on the other side.
    This can easily be done by defining a union, which is a creation that points to the same range of data, but is interpreted depending on how you call it. I'm writing from memory, but I think it flew like this

    Code: C / C++
    Log in, to see the code
    .

    and then u .value behaves like a float, and u .bytes[] like an array. And the whole beauty of union is that you type something into a float and then access it byte by byte in an array, e.g.

    Code: C / C++
    Log in, to see the code
    .

    and on the other hand you glue it together analogously, i.e. you push the received bytes one by one into u.bytes[i], and then you use yourself u.value as a float....

    Of course, there will be a problem if your communication goes awry and the bytes move, because then the glued value will be nonsense ;)

    Ps. I don't know if this will go in Arduino, but I think it will work in C++ what is written in C ;) .
  • #3 18209021
    khoam
    Level 42  
    All in all, this can be sent without union and an extra loop, and additionally in C++ ;)
    Code: C / C++
    Log in, to see the code
    .
    where value is a variable of type float. The analogy can be received with the Serial.readBytes() function:
    Code: C / C++
    Log in, to see the code
    .
    Or use the Serial.parseFloat() function to receive - this is more difficult.

    https://www.arduino.cc/reference/en/language/functions/communication/serial/write/
    https://www.arduino.cc/reference/en/language/functions/communication/serial/readbytes/
    https://www.arduino.cc/reference/en/language/functions/communication/serial/parsefloat/
  • Helpful post
    #4 18209031
    krisRaba
    Level 31  
    Oh see, even better solution :) I thought this write() only ingests byte by by byte, hence the idea of an extra loop etc. :lol:
  • #5 18210991
    omnixcrs
    Level 11  
    Welcome,

    I have used your instructions khoam:
    broadcasting:
    Code: C / C++
    Log in, to see the code
    .
    Receiving:
    Code: C / C++
    Log in, to see the code
    .
    Unfortunately I have 0.00 in the serial, of course in the setup in one and the other device I have float value =0 set;
  • #6 18211014
    khoam
    Level 42  
    omnixcrs wrote:
    of course in setup in one and the other device I have set float value =0;
    .
    In setup()? You should set this variable declaration in the main block, before setup(). Show the code please.

    Added after 5 [minutes]:

    Alternatively, in the loop() itself.

    Added after 23 [minutes]:

    Are you using Serial simultaneously for communication between the Arduino and for the serial port monitor ?

    omnixcrs wrote:
    if (Serial.available() > 0) {
    Serial. readBytes ((byte *)&value, sizeof(value)));
    Serial. print (value);
    delay(2000);
    }
    .
  • #7 18213403
    omnixcrs
    Level 11  
    Hi khoam,
    sory for misleading the arduino does however send the float value correctly, but I don't know why it only works for some time around 3-5min. I wrote a test program to display the transmitted temp value on lcd2x16. After running it displays tamp 22,5 and 27,5 but for literally 3-5min then it is already 00,0. Why is this happening. I am pasting the codes:
    Broadcasting:
    Code: C / C++
    Log in, to see the code
    .

    Receiving
    Code: C / C++
    Log in, to see the code
    .
  • ADVERTISEMENT
  • #8 18213440
    khoam
    Level 42  
    Try a slightly modified version of the broadcast:
    Code: C / C++
    Log in, to see the code
    .
    And a slightly modified version of receiving:
    Code: C / C++
    Log in, to see the code
    .
  • ADVERTISEMENT
  • #9 18213493
    omnixcrs
    Level 11  
    Ok I will try it and let you know, and tell me if identical commands I can use for int ?
  • #10 18213498
    khoam
    Level 42  
    omnixcrs wrote:
    can I use identical commands for int ?
    .
    Yes, with a fix for Serial.available() - then you have to wait for 2 bytes, not 4.
    You could also rework the receive loop to be like this, more "universal":
    Code: C / C++
    Log in, to see the code
    .
    Then temp_pump can be float or int.
  • #11 18214001
    omnixcrs
    Level 11  
    Hello, well unfortunately it didn't work. The reason for the problem is rather my receiving code because when I reset the arduino with the display(receiving) then for a while temp_pump has a value and then again 0.00. Actually my code is a bit more elaborate but I didn't want to upload the whole thing to avoid clutter. This time I am uploading the whole pickup maybe I have something wrong:
    Code: C / C++
    Log in, to see the code
    .
  • #12 18214161
    khoam
    Level 42  
    omnixcrs wrote:
    when I reset the arduino with the display(receiving) then for a while temp_pump has a value and then 0.00 again
    .
    By the time it gets to the Serial read in the loop(), you have inserted delays for 10 s! It could be that the Serial buffer overflows (you can't keep up with receiving data) and hence this strange behaviour.
    I understand that this receiver is an ESP8266? ;) On the ESP8266, the UART receive buffer size is 128 bytes.
    I don't know why you inserted such a large delay() in loop(), but if they are needed, receiving data from Serial should then be implemented on an interrupt.
  • Helpful post
    #13 18214198
    krisRaba
    Level 31  
    Try in the transmit module to reduce the frequency of transmitting the measurement data since you display it every 30s, or receive the data and update the variable in the interrupt, or when receiving, read from the buffer everything you have available to free up space. Because if you transmit every 2 or 4 seconds and read every 30, it's no wonder it clogs up after a while.... By the way, in such a system you display the old data, because you sent one measurement, displayed, after two seconds another one... but it will wait another 28s to read from the buffer and display. After another 2 seconds you send another one, but it will display after 26+30=56s, the fourth one sent after another 2 seconds will only display after 24+30+30=84s etc. And the queue gets clogged up....

    All in all, it's good to send more often so you always have up-to-date info when you miss a carousel of screens, but you have to pull it from the buffer in time and display the latest one....


    Ps. Are you specifically delaying the start of the controller to display the startup caption to yourself? Is there actually something starting up in the background there? ;-)
    And why do you write Startup so many times with extra dots, when you only need to write once and then just position the cursor accordingly and add a dot? ;-)
  • Helpful post
    #14 18214225
    khoam
    Level 42  
    @omnixcrs I have this loose suggestion. Divide the receiver code into tasks: checking WiFi, readings from individual sensors etc. For each of these, define the required maximum time interval (better shorter than longer - the ESP8266 has a fast MCU). Use the TaskScheduler library to manage tasks - it runs on the ESP8266. See my footer for a link to an article on this. The code will be more readable and easier to extend, and as a bonus you will get rid of delay() ;) .
  • #15 18214226
    omnixcrs
    Level 11  
    The start-up delay is intentional because additional peripherals start up during this time.
    As for Serial I am unfortunately not strong in this. Would using millis instead of dealy help. I ask because I don't know if I can handle interrupts.
  • Helpful post
    #16 18214229
    khoam
    Level 42  
    omnixcrs wrote:
    Whether using millis instead of dealy will help.
    .
    You will complicate yourself even more by manually counting the individual intervals. See post #14.

    omnixcrs wrote:
    The startup delay is intentional because additional peripherals start up during this time.
    .
    The setup() function is not the problem, the problem is what is in loop().
  • #17 18232315
    omnixcrs
    Level 11  
    Hello Gentlemen,
    It took me a while because I was struggling with this program in all sorts of ways, but no luck.
    So yes: following your advice khoam, I gave up on delay() altogether and used SimpleTimer to split the program. Your suggestion with TaskScheduler is also interesting, but maybe I didn't have time to recognise it this time. SimpleTimer also has a similar action only less advanced, but to the point. I divided the program into sections. In Loop() I basically only have timer.run() the rest executes in time intervals. Now, here's the thing: in the beginning, when I tried it with one variable, receiving on the UART seemed to work, but I need to send and receive data (two-way communication) from several variables between two esp and it actually works and doesn't work. I don't think I fully understand how Serial works. I have tried different send and receive times (always intervals of more than 10s) and have tried sending and receiving at the same time, i.e. one esp sending every 20s and the other receiving every 20s. I have also tried receiving more often, e.g. every 10s, and transmitting less often, e.g. every 30s, and I also get 0.00. I have a maximum of 10 variables to transmit. I don't want to put in a whole lot of code, so here are the most important functions I have used:
    ESP1
    Code: C / C++
    Log in, to see the code
    .

    And on ESP2:

    Code: C / C++
    Log in, to see the code
    .

    And now yes: from the above codes the result is that e.g. ESP2 should send the value on_pump to esp1, unfortunately it doesn't. I have a 2x16 LCD connected to esp1 via I2C (so as not to use Serial.print) initially after applying power to the display I have on_pump = 35 but after the first UART send/receive I already have a value of 0.00. Why?
  • Helpful post
    #18 18232366
    khoam
    Level 42  
    It is not possible to "serialise" in this way to insert conditions on checking if something is in the Serial buffer:
    Code: C / C++
    Log in, to see the code
    .
    What happens if the first condition is not met but the second one is, or the next one ? Complete chaff, i.e. the data sent will go to the wrong variables. It is better in such a situation to check at once if there is a complete set of data for all expected variables in the Serial buffer and only then read them in turn. As I wrote, the size of the UART receive buffer in the ESP8266 is 128 bytes, so this also needs to be taken into account.

    Alternatively, you can create a struct variable to contain the individual simple variables and send/receive this struct variable. For example:
    Code: C / C++
    Log in, to see the code
    .
    You send and receive by analogy:
    Code: C / C++
    Log in, to see the code
    .
    Code: C / C++
    Log in, to see the code
  • #19 18235028
    omnixcrs
    Level 11  
    Hello,
    I have applied your idea khoam and as far as the UART is concerned it indeed works and rather works. But of course I encountered another problem. Generally this device of mine is a combination of 3 pcs ESP and 2x16 LCD over i2c + 2x PCF8574 expander. Well I think there is a problem on the i2C bus. All ESPs are connected to each other via UART and of course share the SDA and SCL lines, also the LCD is connected to these lines. Theoretically everything works but for a short time, i.e. after powering up for some time one of the esp displays on the lcd what is to be displayed the other one nicely manages the expander but after some time e.g. after 3-5 min on the LCD "bushes" appear or the esp from the LCD hangs up. I don't know if I'm getting it right, but I suspect this may be due to some conflict on the i2c line. E.g. when one esp is trying to display something on the LCD via i2c and the other esp is reading some status from the expander via i2c, is this possible?
  • #20 18235038
    krisRaba
    Level 31  
    Well you have made a multimaster ;-) The question is whether the soft supports this. Because in most cases you have one master and multiple slaves that only respond as you ask them.
    With multiple masters you still have to have arbitration of access to the I2C line so that when one starts transmission, the others wait politely....
    Electrically there is no problem because you have pullup and open-collector outputs, but the soft and preferably hardware must support arbitration. You can always split the I2C, and push the data between the MCU and the one that has the display will handle it ;-) .
  • #21 18235407
    khoam
    Level 42  
    omnixcrs wrote:
    I don't know if I'm combining things correctly, but I suspect it could be due to some conflict on the i2c line. E.g. when one esp is trying to display something on the LCD over i2c and the other esp is reading some status from the expander over i2c, is this possible ??
    .
    There can only be one I2C Master in such a configuration, the others are I2C Slaves (with their own addresses). This is not difficult to do on the ESP8266, but in your case there is the problem that each ESP individually controls a different device, which in turn is a slave.

    omnixcrs wrote:
    All ESPs are UART connected to each other and of course share common SDA and SCL lines pulled up to the power supply
    .
    I don't understand why you connected all the ESPs via I2C bus, when you already exchange data over UART between them 8-O .

    Here you have a pretty clear explanation of how to configure ESPs in Slave and Master mode:
    https://github.com/esp8266/Arduino/issues/3046 (further in this thread, there is confirmation that this already works starting with ESP8266 version 2.5.0)
    https://github.com/suculent/esp8266-I2C-slave

    Added after 4 [minutes]: .

    krisRaba wrote:
    Well, you've done a multimaster The question is whether the soft supports this.
    .
    Such tricks are possible in ESP32, but not in ESP8266.
  • ADVERTISEMENT
  • #22 18235525
    omnixcrs
    Level 11  
    Ok, I understand and thanks for the tips, but in my case the problem might be a bit different because in sum I can disconnect the two esp from the one esp but still at this point this one esp has to handle the lcd over i2c and the pcf8574 expander which is used for the buttons and I suspect this is where I have a problem. I have a timed program so every so often data is displayed on the lcd with a fixed time interval(5s) and the expander pin reading is done every 1s. Can this generate problems ? For pcf I use pcf8574_esp.h library and for lcd LiquidCristal_I2C
  • #23 18235581
    khoam
    Level 42  
    omnixcrs wrote:
    I have a time-divided program, i.e. every now and then data is displayed on the lcd with a fixed time interval(5s) and the expander pin reading is performed every 1s. Can this generate problems ? For pcf I use pcf8574_esp.h library and for lcd LiquidCristal_I2C
    .
    I will repeat myself: show the relevant code snippet please. I very much doubt that this "time division" is the cause of the problems. I am also assuming that one esp supports both I2C devices.

    Added after 4 [minutes]: .

    From what voltage are you supplying the LCD and I2C expander?
  • #24 18235706
    omnixcrs
    Level 11  
    khoam wrote:
    I also assume that one esp supports both I2C devices.
    .
    You are making a good assumption, as I wrote above:
    omnixcrs wrote:
    one esp must support LCD over i2c and expander pcf8574
    .
    And I am just explaining that the program in this esp supports both LCD I2C and PCF expander I2C
    I will abbreviate the code a bit but to me it looks more like this:

    Code: C / C++
    Log in, to see the code
    .
  • #25 18235712
    krisRaba
    Level 31  
    Well, unless there is somehow a cleverly done so-called Gate keeper, which makes one library wait until the other one finishes, it can happen that the soft is hammering on the hardware while the transmission is in progress....
    In my early days with the MCU I inadvertently did this on the SPI, as I would normally talk to the display, and when an interrupt came in from the peripheral I would read data from it. Occasionally such a service would bite into the state when the CS was already in the low state and add its own, meaning two could respond and generally fail ;-) .
    Now on RTOS I do things like this on semaphores etc. But if you have 2 independent libraries configured for the same hardware, one that they may not know about each other and two that they may be written in an authoritarian way that only I'm in charge and I don't even check what state the hardware is in ;-)
  • #26 18235719
    omnixcrs
    Level 11  
    I supply 5VDC
  • #27 18235814
    khoam
    Level 42  
    omnixcrs wrote:
    I supply 5VDC
    .
    Did you pull up the I2C line to 5V as well?

    If the expander and LCD are on the same I2C bus, why do you call lcd.begin() before testWire.begin(4, 5)?
    Are you using this version of the LiquidCrystal_I2C library for the ESP8266: https://github.com/lucasmaziero/LiquidCrystal_I2C

    testWire.setClock(100000L) is superfluous in the case of the ESP8266 and so at a higher speed than 100 kHz it won't work, which is the default.

    krisRaba wrote:
    Well, unless there's a cleverly done so-called Gate keeper in there, which makes one library wait until the other one finishes, then it can happen that the soft is hammering on the hardware while a transmission is in progress...
    .
    SimpleTimer does not operate on interrupts, but in cooperative multitasking mode using millis(). One task cannot be interrupted by the execution of another. This is, of course, assuming that the program does not use interrupts in some other way.
  • #28 18236210
    krisRaba
    Level 31  
    Well, if there is no expropriation here, the I2C functions are probably blocking, I2C transmissions are not triggered in interrupts, then theoretically one transmission should not hatch to another inside the ESP (because connecting multiple masters to I2C was not a very good idea ;) )
  • #29 18247068
    omnixcrs
    Level 11  
    Hi guys, I followed your tips and advice. Connecting three MCUs over i2c without prioritization was not the best idea. I have fixed this. Now the issue is that the first esp serves the first expander over i2c the second esp has nothing on i2c and the third esp serves itself the second pcf. Of course the i2c lines are routed separately and do not connect ! So that leaves me with the uart communication between the MCU and here I have some questions about how it works.
    You mentioned khoam that the serial buffer in esp is 128 bytes. Suppose I want to send data from two esp (2 and 3) to the first esp. If I send esp 2 a float variable named e.g. temperature to esp 1, it will take up 4 of the 128 bytes in the serial buffer of esp1. Then esp 3 sends a float variable named e.g. temperature2, it will take another 4 bytes out of the 124 remaining in the esp1 buffer. Well, and now I don't quite understand what the readout looks like. I.e. if I call for example:
    Code: C / C++
    Log in, to see the code
    .

    then I read the variable temperature or temperature1 ?? I mean, does this command give the ability to recognise data by name or only by byte size ? I hope I haven't messed up too much.
  • #30 18247088
    khoam
    Level 42  
    omnixcrs wrote:
    If esp 2 sends a float variable named e.g. temperature to esp 1, it will occupy 4 of the 128 bytes in esp1's serial buffer. Then esp 3 will send a float variable named e.g. temperature2 then it will occupy another 4 bytes of the 124 remaining in esp1's buffer.
    .
    I'm assuming you are supporting two serial ports (Serial and Serial1) in esp1, one for communication with esp2 and one for esp3. For each UART the buffer size is 128 bytes.

    omnixcrs wrote:
    What I mean is, does this command give you the possibility to recognize data by name or only by byte size ?
    .
    The serial knows nothing about the name of the variable whose bytes are being transferred.

Topic summary

The discussion revolves around sending float values between two Arduino devices over UART. The main challenge is how to transmit a float, such as 22.5, as Arduino's Serial.write() primarily handles byte data. Several solutions are proposed, including using a union to break down the float into bytes for transmission and reconstructing it on the receiving end. An alternative method involves using Serial.write() with a pointer to the float variable and the size of the float. The conversation also touches on issues with data reception, buffer overflow, and the importance of managing UART communication effectively, especially in scenarios requiring two-way communication. Suggestions include using Serial.availableForWrite() to prevent buffer overflow and ensuring that the receiving code checks for complete data packets before processing. Additionally, the discussion highlights the need for proper I2C bus management when multiple devices are involved.
Summary generated by the language model.
ADVERTISEMENT