2

What is the best way (in Python) to convert 32-bit signed longs to 7-bit ints, in order to transmit them via Firmata/serial link? Converting into 8-bit is not a problem, just (long_val).to_bytes(4, 'little'). The final sequence should be like this:

No 1, bits 0-6
No 2, bits 7-13
No 3, bits 14-20
No 4, bits 21-27
No 5, bits 28-32

A backward conversion from a 5-item sequence of 7-bit ints into 32 bit signed longs would also be very helpful.

s = bin(pos)[2:].zfill(32)
cmd = bytearray([acc.ACCELSTEPPER_TO, dev_no,
int(s[28:32], 2), int(s[21:28], 2), int(s[14:21], 2),
int(s[7:14], 2), int(s[0:7], 2)])
brd.send_sysex(acc.ACCELSTEPPER_DATA, cmd)

My methods unfortunately produced wrong result, so I would like to discard them completely and restart from scratch. Thanks in advance for any suggestion(s).

CNCman
  • 43
  • 3
  • I doubt if there is some built-in method which you can use. However, do you know that `bin(long_val)` gives you a binary representation of _long_val_? Perhaps you can use this representation as a string to extract groups of 7 bits. – Ronald Jun 27 '20 at 10:11
  • This produced more meaningful result, decoded value is about 4x of reuqired. Still wrong. s = bin(pos)[2:].zfill(32) cmd = bytearray([acc.ACCELSTEPPER_TO, dev_no, int(s[28:32], 2), int(s[21:28], 2), int(s[14:21], 2), int(s[7:14], 2), int(s[0:7], 2)]) – CNCman Jun 27 '20 at 14:38

2 Answers2

1

Just do the conversions 7 bits at a time:

bytes = []
for i in range(5):
    bytes.append(long_value & 0x7f)
    long_value >>= 7
long_value = 0
for i in reversed(range(5)):
    long_value <<= 7
    long_value |= bytes[i]
CL.
  • 173,858
  • 17
  • 217
  • 259
0

It is not exactly 7 bits as such, but it sounds like a job for base64 coding. If all you need is to transmit the data over a 7-bit channel, then base64 should work just fine for you. Of course, your data stream will be slightly longer, i.e. 6 bytes instead of 5.

Here is how you might write it in Python:

import base64

def encode_number(n):
    """Takes an int and returns a bytes object in base64 encoding."""
    return base64.b64encode(n.to_bytes(4, byteorder='little'))

def decode_number(b):
    """Takes a bytes object in base64 encoding and returns an int."""
    return int.from_bytes(base64.b64decode(b), byteorder='little')

And if ever you need more than 32-bit numbers, just change the 4 in line 5 here.

Tobias
  • 1,321
  • 6
  • 11
  • sorry, its not base64. bin(int_32bit_signed)[2:] converts to binary, yet its slicing into 7-bit chunks for whatever reason don't work – CNCman Jun 27 '20 at 12:50
  • @CNCman I am not sure I understand what you are trying to do. Each byte in the base64 encoding only takes 7 bits (the highest bit is always 0). Perhaps you could tell us how exactly you are transmitting the data...? – Tobias Jun 27 '20 at 15:19
  • Its a data transfer via Firmata serial link. 32-bit signed long encoded like described in subject. I used this code and it dooen't work correctly (not decoded correctly at received side). `s = bin(pos)[2:].zfill(32) cmd = bytearray([acc.ACCELSTEPPER_TO, dev_no, int(s[28:32], 2), int(s[21:28], 2), int(s[14:21], 2), int(s[7:14], 2), int(s[0:7], 2)])` – CNCman Jun 27 '20 at 18:18