Send a float using sysex

Hi,

I want to send the reading from a dht22 sensor over sysex to my python program.

However, the reading is a float and I’m not sure how to break it down into 7 bit bytes. I am thinking that it is probably better to send it as an integer.

However, I am a bit stuck how to represent my float as an integer.

I am sure that it is possible and hopefully someone with more experience than me will be able to point me in the right direction.

Thanks

Martyn

Try to break it down into eight bit bytes.
Much easier.

I am a bit stuck how to represent my float as an integer.

In C/C++, just use "=".

For example:

float x=1.24;
int y = x;

To preserve a couple of decimal places, multiply by a suitable factor. For example:

float x=1.24;
int y = x*100.0;

Sorry

I want to get an integer representation of my 32 bit float. Sorry maybe I was not clear.

martynwheeler:
I want to get an integer representation of my 32 bit float. Sorry maybe I was not clear.

float your_float = 3.14;
uint32_t as_integer;
static_assert(sizeof(your_float) == sizeof(as_integer), "sizes don't match");
memcpy(&as_integer, &your_float, sizeof(as_integer));

Pieter

In the Arduino, the 32 bits can also be considered to be a long integer.

When transferring values between different computers, mind big versus little endian byte order, and possibly different representations of float values.

TheMemberFormerlyKnownAsAWOL:
Try to break it down into eight bit bytes.
Much easier.

Thanks,

Sysex uses 7 bit bytes. Which is why I asked for that. Dou you mean that if I break it down into 8 bit bytes I can then send as 7 bit bytes. How would I break it down into 8 bit bytes please?

Thanks again

What is sysex?
Why doesnt it use eight bit bytes, like everyone else?

TheMemberFormerlyKnownAsAWOL:
What is sysex?
Why doesnt it use eight bit bytes, like everyone else?

It is the communication protocol used by firmata, I have no idea why it uses 7 bits

TheMemberFormerlyKnownAsAWOL:
What is sysex?
Why doesnt it use eight bit bytes, like everyone else?

It's part of the MIDI standard.
It uses seven bits because the most significant bit of each byte is used to distinguish between header bytes and data bytes.

PieterP:

float your_float = 3.14;

uint32_t as_integer;
static_assert(sizeof(your_float) == sizeof(as_integer), "sizes don't match");
memcpy(&as_integer, &your_float, sizeof(as_integer));




Pieter

Thanks for that,

How do I go about splitting that into 7 bit chunks please?

I have this for a 14 bit number

int max_distance_LSB = max_distance & 0x7f ; // mask the lower byte

int max_distance_MSB = max_distance >> 7 ; // shift the high order byte

I am fairly new to C

martynwheeler:
How do I go about splitting that into 7 bit chunks please?

void sysex_encode(uint32_t in, uint8_t *out) {
  for (uint8_t i = 0; i < 5; ++i) {
    out[i] = in & 0x7F;
    in >>= 7;
  }
}

martynwheeler:
I am fairly new to C

Arduino uses C++, not C.

PieterP:

void sysex_encode(uint32_t in, uint8_t *out) {

for (uint8_t i = 0; i < 5; ++i) {
    out[i] = in & 0x7F;
    in >>= 7;
  }
}


Arduino uses C++, not C.

Thanks very much

PieterP:

void sysex_encode(uint32_t in, uint8_t *out) {

for (uint8_t i = 0; i < 5; ++i) {
    out[i] = in & 0x7F;
    in >>= 7;
  }
}


Arduino uses C++, not C.

Sorry, one last question

I have sent the data, now i need to convert it back to a float in python

so far i have

self.dht_read_temperature = (data[0] & 0x7f) + (data[1] << 7) + (data[2] << 7) + (data[3] << 7) + (data[4] << 7)

this gives me the integer representation, do you know how how I can get this back as float in python please??

Thanks again

Might it be simpler to serialise the float to a string and send that instead?

Yep. And that is trivial with the avr-libc function dtostrf().

The Arduino does not by default support formatting floats/doubles to C-strings using the standard sprintf() routine. And with standard AVR-based Arduinos, type "double" is treated as "float'.

It would be much faster and reliable to interface Python with your Arduino using the compatible libraries: pySerialTransfer and SerialTransfer.h.

pySerialTransfer is pip-installable and cross-platform compatible. SerialTransfer.h runs on the Arduino platform and can be installed through the Arduino IDE's Libraries Manager.

Both of these libraries have highly efficient and robust packetizing/parsing algorithms with easy to use APIs.

Example Python Script:

import time
from pySerialTransfer import pySerialTransfer as txfer


if __name__ == '__main__':
    try:
        link = txfer.SerialTransfer('COM17')
        
        link.open()
        time.sleep(2) # allow some time for the Arduino to completely reset
        
        while True:
            send_size = 0
            
            ###################################################################
            # Send a list
            ###################################################################
            list_ = [1, 3]
            list_size = link.tx_obj(list_)
            send_size += list_size
            
            ###################################################################
            # Send a string
            ###################################################################
            str_ = 'hello'
            str_size = link.tx_obj(str_, send_size) - send_size
            send_size += str_size
            
            ###################################################################
            # Send a float
            ###################################################################
            float_ = 5.234
            float_size = link.tx_obj(float_, send_size) - send_size
            send_size += float_size
            
            ###################################################################
            # Transmit all the data to send in a single packet
            ###################################################################
            link.send(send_size)
            
            ###################################################################
            # Wait for a response and report any errors while receiving packets
            ###################################################################
            while not link.available():
                if link.status < 0:
                    if link.status == -1:
                        print('ERROR: CRC_ERROR')
                    elif link.status == -2:
                        print('ERROR: PAYLOAD_ERROR')
                    elif link.status == -3:
                        print('ERROR: STOP_BYTE_ERROR')
            
            ###################################################################
            # Parse response list
            ###################################################################
            rec_list_  = link.rx_obj(obj_type=type(list_),
                                     obj_byte_size=list_size,
                                     list_format='i')
            
            ###################################################################
            # Parse response string
            ###################################################################
            rec_str_   = link.rx_obj(obj_type=type(str_),
                                     obj_byte_size=str_size,
                                     start_pos=list_size)
            
            ###################################################################
            # Parse response float
            ###################################################################
            rec_float_ = link.rx_obj(obj_type=type(float_),
                                     obj_byte_size=float_size,
                                     start_pos=(list_size + str_size))
            
            ###################################################################
            # Display the received data
            ###################################################################
            print('SENT: {} {} {}'.format(list_, str_, float_))
            print('RCVD: {} {} {}'.format(rec_list_, rec_str_, rec_float_))
            print(' ')
    
    except KeyboardInterrupt:
        link.close()
    
    except:
        import traceback
        traceback.print_exc()
        
        link.close()

Example Arduino Sketch:

#include "SerialTransfer.h"


SerialTransfer myTransfer;


void setup()
{
  Serial.begin(115200);
  myTransfer.begin(Serial);
}


void loop()
{
  if(myTransfer.available())
  {
    // send all received data back to Python
    for(uint16_t i=0; i < myTransfer.bytesRead; i++)
      myTransfer.txBuff[i] = myTransfer.rxBuff[i];
    
    myTransfer.sendData(myTransfer.bytesRead);
  }
}

On the Arduino side, you can use myTransfer.txObj() and myTransfer.rxObj() to copy values to the library's RX buffer and parse multi-byte variables out of the library's TX buffer.

For theory behind robust serial communication, check out the tutorials Serial Input Basics and Serial Input Advanced.

Thank you for your detailed answer. I guess I need to think about the process in more detail.

I am basically using the arduino as a data capture device which is then polled by another computer at intervals of 10 seconds.

I was under the impression that firmata was a good way of doing this which is what lead me to here.

My thinking was that if I just looped and sent serial data constantly this could cause a problem with overflowing buffers.

Maybe I am not correct in my thinking?

Thank you

Martyn

martynwheeler:
My thinking was that if I just looped and sent serial data constantly this could cause a problem with overflowing buffers.

Using pySerialTransfer and SerialTransfer.h you can stream large amounts of data without overflow (provided your code doesn't have any delay()s).

I helped another user transfer an entire 24K-byte image from one Arduino to another using SerialTransfer.h. You can easily match or exceed this throughput pairing SerialTransfer.h with Python using pySerialTransfer.