bits to bytes

General discussions and questions abound development of code with MicroPython that is not hardware specific.
Target audience: MicroPython Users.
martincho
Posts: 96
Joined: Mon May 16, 2022 9:59 pm

Re: bits to bytes

Post by martincho » Sat Jul 09, 2022 3:39 am

KJM wrote:
Sat Jul 09, 2022 2:06 am
Lastly is

Code: Select all

bytesin=b'\xf0\x00'
tup=struct.unpack('<H', bytesin)
bytesout=bin(tup[0])
lst=[int(i) for i in bytesout[2:]]
the best way to recover my original list?
is "bytesin" what the received data? And, is it actually sending you a 16 bit value in two bytes, low byte first?

And, if so, how many of these 16 bit values do you receive in one go? In other words, is the response something like 16 bytes long, with 6 words, each 16 bits, low byte first?

martincho
Posts: 96
Joined: Mon May 16, 2022 9:59 pm

Re: bits to bytes

Post by martincho » Sat Jul 09, 2022 4:51 am

I ask because there are so many ways to handle input contacts (going back to your original question). Sure, looking at using struct or some other mechanism might be good, however, sometimes, when you look at the entirety of the problem a different perspective might surface.

In this case you have to read the pins individually by using pin objects. If we step back to that layer, before even considering struct packing and unpacking, what can you do?

Here's one example. I am using simulated pin values to make it easier to experiment. If you uncomment the pin-object code and edit it to represent your setup it might work with the actual I/O as wired:

Code: Select all

_NUMBER_OF_CONTACTS = 12

# Hypothetical pin assignments; it's a tuple of input pin objects
# contact_pins = (machine.Pin(1, machine.Pin.IN),
#                 machine.Pin(2, machine.Pin.IN),
#                 machine.Pin(3, machine.Pin.IN),
#                 machine.Pin(4, machine.Pin.IN))

simulated_contact_pin_values = (0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0)

# This assigns each of the above pins to a bit in an integer
# This should be a one-hot table; meaning, only a single bit should be 1 per value
# You can assign any contact to any bit position
#
contact_pin_masks = (0b1000_0000_0000,
                     0b0100_0000_0000,
                     0b0010_0000_0000,
                     0b0001_0000_0000,
                     0b0000_1000_0000,
                     0b0000_0100_0000,
                     0b0000_0010_0000,
                     0b0000_0001_0000,
                     0b0000_0000_1000,
                     0b0000_0000_0100,
                     0b0000_0000_0010,
                     0b0000_0000_0001)

# Read the pin states and pack into an integer
# This really simple loop reads every single one of your input contacts and stores the 
# state of each of them at the bit location you selected using the binary mask for that contact
contact_states = 0
for i in range(_NUMBER_OF_CONTACTS):
    # contact_states |= contact_pins[i].value() and contact_pin_masks[i]
    contact_states |= simulated_contact_pin_values[i] and contact_pin_masks[i]

print(f"contacts encoded into integer:       {contact_states:012b}   the integer:{contact_states}")

# Test an integer for pin states
print("integer decoded into contact states: ", end="")
for i in range(_NUMBER_OF_CONTACTS):
    print(f"{1 * (0 != contact_states & contact_pin_masks[i])}", end="")
The output statement is just an example to show that you can easily determine contact state from this packed integer with simple logical operations. I would say that one of the advantages of such an approach is that you are not dynamically creating and modifying lists. Your inputs are (and should be) tuples with pin objects and, the other, integers that map each pin object to a position in the integer you'll use to store the state of each contact.

From there you can unpack one or all of the contact states any way you want using the same mask. In fact, in your application you might not need to have all the contact states unpacked at once. Maybe you check contact[0] here and, if set, you might check contact[5]. Well, this allows you to do this without having to unpack all 12 contacts.

Just a thought. Not the only way to do it.

User avatar
karfas
Posts: 193
Joined: Sat Jan 16, 2021 12:53 pm
Location: Vienna, Austria

Re: bits to bytes

Post by karfas » Sat Jul 09, 2022 5:53 am

KJM wrote:
Sat Jul 09, 2022 2:06 am
struct represents [1,1,1,1,0] as 0xf0 which is intuitive for me because it works in comfortable 4 bit nibbles. My problem with hex(int('11110') is that the 0x1e representation
It doesn't halp that you come up with different interpretations of the same data. "11110" should be 0x1e, written in a string or written as int array. If you split numbers to nibbles or thousands, you always start from the right...
A few hours of debugging might save you from minutes of reading the documentation! :D
My repositories: https://github.com/karfas

TheSilverBullet
Posts: 50
Joined: Thu Jul 07, 2022 7:40 am

Re: bits to bytes

Post by TheSilverBullet » Sat Jul 09, 2022 6:21 am

Six pages already?

Code: Select all

#!/usr/bin/python3
from machine import Pin
switches = (
    Pin( 1, Pin.IN), Pin( 8, Pin.IN),
    Pin( 2, Pin.IN), Pin( 7, Pin.IN),
    Pin( 3, Pin.IN), Pin( 5, Pin.IN),
    Pin( 4, Pin.IN), Pin( 6, Pin.IN),
    Pin(10, Pin.IN), Pin(11, Pin.IN),
    Pin( 9, Pin.IN), Pin(12, Pin.IN))

# collect status of all switches
n = 0
for switch in switches:
    n = (n << 1) | 1 if switch.value() == 1 else (n << 1) | 0  # the | 0 is obviously redundant
    
# put'em into two bytes
data_to_send = bytearray((n >> 8, n & 0xff))

# done. Let's see…

print(data_to_send)
print(f'binary: {data_to_send[0]:08b} {data_to_send[1]:08b}')
print(f'decimal:{data_to_send[0]:3d} {data_to_send[1]:3d}')
print(f'hex:    {data_to_send[0]:02x} {data_to_send[1]:02x}')

User avatar
Roberthh
Posts: 3667
Joined: Sat May 09, 2015 4:13 pm
Location: Rhineland, Europe

Re: bits to bytes

Post by Roberthh » Sat Jul 09, 2022 8:17 am

For such a trivial task!

User avatar
Roberthh
Posts: 3667
Joined: Sat May 09, 2015 4:13 pm
Location: Rhineland, Europe

Re: bits to bytes

Post by Roberthh » Sat Jul 09, 2022 8:18 am

For such a trivial task, where a perfect answer was given by @jimmo.

KJM
Posts: 158
Joined: Sun Nov 18, 2018 10:53 pm
Location: Sydney AU

Re: bits to bytes

Post by KJM » Sat Jul 09, 2022 8:58 am

If you're referring to data = bytes((0b11110111, 0b11111010,)) Rob, I didn't use that because it throws an error

Code: Select all

>>> data = bytes((0b11110111, 0b11111010,))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'bytes' object is not callable
I'm back where I started with Christians example of struct Martin

Code: Select all

>> bits=[1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0]
>>> struct.pack('<H', sum(b*(1<<(i^7)) for i, b in enumerate(bits)))
b'\xf0\x00'
& what I was wanting to know was is

Code: Select all

bytesin=b'\xf0\x00'
tup=struct.unpack('<H', bytesin)
bytesout=bin(tup[0])
lst=[int(i) for i in bytesout[2:]]
print(lst)
the most efficient way to unpack it at the receiver?

User avatar
karfas
Posts: 193
Joined: Sat Jan 16, 2021 12:53 pm
Location: Vienna, Austria

Re: bits to bytes

Post by karfas » Sat Jul 09, 2022 5:05 pm

KJM wrote:
Sat Jul 09, 2022 8:58 am
the most efficient way to unpack it at the receiver?
No, as this requires at least one memory allocation for the unneeded intermediary binary string.

I have no idea why you should care about efficiency here. You transmit over LoRa with a latency of (how much ? 200, 300 ms ?) and can't sacrifice some nanoseconds for a suboptimal decoding process?

Just get your application working and care about "efficiency" later.
A few hours of debugging might save you from minutes of reading the documentation! :D
My repositories: https://github.com/karfas

User avatar
jimmo
Posts: 2754
Joined: Tue Aug 08, 2017 1:57 am
Location: Sydney, Australia
Contact:

Re: bits to bytes

Post by jimmo » Sun Jul 10, 2022 12:14 am

KJM wrote:
Sat Jul 09, 2022 8:58 am
If you're referring to data = bytes((0b11110111, 0b11111010,)) Rob, I didn't use that because it throws an error
No that was just showing you how to make a bytes object from two individual bytes using binary literals. What Robert is referring to is this reply: viewtopic.php?f=2&t=12597&start=40#p68746

And specifically this part:
jimmo wrote:
Fri Jul 08, 2022 2:27 pm
BUT. We're going around in circles. If your problem is "take 16 open/closed sensors and turn it into a two byte value", then just use the code I wrote here: viewtopic.php?f=2&t=12597&start=20#p68627
Also FWIW

Code: Select all

data = bytes((0b11110111, 0b11111010,))
is totally valid. You've just overwritten the bytes object somewhere by trying to create a variable named "bytes". Try doing a soft reset.
KJM wrote:
Sat Jul 09, 2022 2:06 am
Martincho. I think you nailed it, the lora driver must massage the  symbol into the utf byte b'\xef\x9f\xba' before it sends it, a more likely explanation than my \u f7 fa grasping at straws idea
No! Seriously. There should be no need at any part of this to think about uft or unicode or strings.
KJM wrote:
Sat Jul 09, 2022 2:06 am
the best way to recover my original list?
When you get your message, you use struct.unpack to turn it back into an integer.

Then you can use bitwise AND to access the state of each contact.

Post Reply