nRF24 / CircuitPython_nRF24L01

CircuitPython driver library for the nRF24L01 transceiver.
http://circuitpython-nrf24l01.rtfd.io/
MIT License
45 stars 11 forks source link

Simple Get Message #38

Closed folksman closed 2 years ago

folksman commented 2 years ago

I stuck in simple get message with different sizes in Mesh sample.

I want send from a slave node a simple text message (for own protocol usage) and on console master print out the node id, and just the message. The message from slave nodes can have different sizes.

Can you may give me here a small example?

2bndy5 commented 2 years ago

The node ID and corresponding node address are saved on the master node's dhcp_dict. Any mesh node can look up the node ID for an assigned node address by calling lookup_node_id

I have not tested this code snippet.

# assuming nrf.available() == True
rx_frame = nrf.read()
rx_id = nrf.lookup_node_id(
    rx_frame.header.from_node
)
print("node ID {} sent msg {}".format(
    rx_id,
    rx_frame.message.decode()
))

Some of this answer relates to the data structure that the mesh/network API use to transport data.

folksman commented 2 years ago

It give a error.. looks like rx_frame.message.decode() is wrong

2bndy5 commented 2 years ago

rx_frame.message should always be a bytearray or bytes object, so try str(rx_frame.message) . TBH, this is a basic python problem and not a lib problem.

It would also help if you actually posted the error message instead of interpreting the error as you understand it.

folksman commented 2 years ago

this prints "5b'H\xee\x00\x00'" but i want see the real message instead

folksman commented 2 years ago

btw i simple use the c++ mesh example which sends the time loop 1000,2000 etc.

on normal idle function i get Received payload (2000,) from 0o5 to 0o0 type 77 id 4 reserved 240 length 4

i want only the 2000

2bndy5 commented 2 years ago

this prints "5b'H\xee\x00\x00'" but i want see the real message instead

What prints that? The code I suggested in this issue? Is this the output from str(rx_frame.message) or from rx_frame.message.decode()? BTW, b'H\xee\x00\x00' is the representation of a bytes object, and IDK where the 5 in the beginning is from.


on normal idle function i get Received payload (2000,) from 0o5 to 0o0 type 77 id 4 reserved 240 length 4

i want only the 2000

All frames' messages are in the form of bytearray or bytes object. To get the number representation of data contained in the message, you need to unpack it from the bytearray form. This is demonstrated in the examples by using struct.unpack(). To learn more about how to use struct.unpack(), you should read the python docs as that is not a function from this lib.

None of this info is specific to this lib. It's all basic python.

folksman commented 2 years ago

difficult to understand as python beginner but got it. sorry to border you for non lib specific questions. i was think there are a simple function to get message content .. why not?

is there a way to can get the pure message without know length and knowing datatype of it? Am 14. Jan. 2022, 15:50 +0100 schrieb Brendan @.***>:

this prints "5b'H\xee\x00\x00'" but i want see the real message instead What prints that? The code I suggested in this issue? Is this the output from str(rx_frame.message) or from rx_frame.message.decode()? BTW, b'H\xee\x00\x00' is the representation of a bytes object, and IDK where the 5 in the beginning is from. on normal idle function i get Received payload (2000,) from 0o5 to 0o0 type 77 id 4 reserved 240 length 4 i want only the 2000 All frames' messages are in the form of bytearray or bytes object. To get the number representation of data contained in the message, you need to unpack it from the bytearray form. This is demonstrated in the examples by using struct.unpack(). To learn more about how too use struct.unpack() you should read the python docs as that is not a function from this lib.

• to get a number that takes up the first 2 bytes of the message, you would use unsigned_short_int = struct.unpack("H", rx_frame.message[:2])[0] The format characters used as the first argument follows C++ datatypes, so it helps to know what is being sent (int/char/short/etc).

None of this info is specific to this lib. It's all basic python. — Reply to this email directly, view it on GitHub, or unsubscribe. Triage notifications on the go with GitHub Mobile for iOS or Android. You are receiving this because you authored the thread.Message ID: @.***>

2bndy5 commented 2 years ago

i was think there are a simple function to get message content .. why not?

Because it would require an assumption that the app you're writing will only use certain data types and in a certain arrangement/order. Assumptions are what make "smart" programming dumber; it's never a good idea to assume when writing code.

is there a way to can get the pure message without know length and knowing datatype of it?

Generally, no. Setting aside what I just said about assumptions, the radio itself is designed to only transmit binary data. Meaning, it is up to the user how the binary data is constructed and deciphered.

This lib isn't meant to do all the work for you; it's only meant to do the interactions between the user's app and the radio's hardware.

folksman commented 2 years ago

I think i got it now. thx for your excellent explanation! It means the message is not always a string.. it will be always a specific data type, thats why rx need to know it to not lost the set data type from tx? Is that correct?

2bndy5 commented 2 years ago

It means the message is not always a string.. it will be always a specific data type, thats why rx need to know it to not lost the set data type from tx? Is that correct?

I'm not sure what you mean here. The English is so confusing (the word "lost" seems like the wrong choice). I feel like if I try to answer, I could cause you even more confusion. But I think you are working with the correct idea about the message now.

folksman commented 2 years ago

i now fully understand it. Thank you! For what is the message_type exactly used? Can i set them free as i wish? If yes i can use them to know on RX side which kind of message will comes and how to use struct as example.

2bndy5 commented 2 years ago

Do not confuse message_type with data type. message_type is specific to the networking API. It is only meant to help the receiving node filter out messages or give special treatment to a certain message_type.

It can be a single an int in range 0-255 or a single letter for which the ASCII value is used as the message_type. Users should not use a message_type greater than 127 because this lib reserves certain message types to do network-specific things.

You can, however, relate a certain message_type to an arrangement of datatypes.

if rx_frame.header.message_type == ord('F'):
    # use "HiB" for message_type "F" (or 0x46 or 70)
    (my_short, my_int, my_bool) = struct.unpack("HiB", rx_frame.message[:7])
folksman commented 2 years ago

i understand. good to know and very helpful!

i stuck in other point. sending strings. as example on c++ i have char[] my_string = "Hello"; and send it as 'x' message_type. Now the problem is on python side.. i not get it read out :( Thats what i try but not working

if rx_frame.header.message_type == ord('x'):
    print(struct.unpack("s", rx_frame.message[:2])[0])

RuntimeError: puffer size does not equal the format (its translated)

I know its again not a Lib issue but your help would be great.

2bndy5 commented 2 years ago

To unpack a string using struct.unpack(), you need to specify the length of the expected string. I have not tested this code.

# this is a c-string in bytes object form
rx_msg = b'Hello\0' # length is 6 bytes

# in python we don't care about the terminating '\0'
rx_str = struct.unpack("5s", rx_msg[:5])[0]

Here's the info you need from the python docs:

For the 's' format character, the count is interpreted as the length of the bytes, not a repeat count like for the other format characters; for example, '10s' means a single 10-byte string, while '10c' means 10 characters. If a count is not given, it defaults to 1. For packing, the string is truncated or padded with null bytes as appropriate to make it fit. For unpacking, the resulting bytes object always has exactly the specified number of bytes. As a special case, '0s' means a single, empty string (while '0c' means 0 characters).

You could also use the decode() method available to all bytearray and bytes objects.

rx_msg = bytearray(b'Hello\0')
rx_str = rx_msg[:5].decode() # assumes UTF-8 encoding
2bndy5 commented 2 years ago

Another basic python trick you don't seem to understand is "slicing". This happens when we use : inside the square brackets (like arr[:2]). More/better info on that in tutorials like this StackOverflow answer

folksman commented 2 years ago

thanks to your great description i now fully understand and get all working as expected. Can you please me tell me for what excactly stay the [0] on end of rx_str = struct.unpack("5s", rx_msg[:5])[0] ?

2bndy5 commented 2 years ago

struct.unpack() will always return a tuple containing all the unpacked data as individual elements, even if there is only 1 element that was unpacked. By using a [0] at the end, we only store the first element to a single variable, not the tuple of a single element.

>>> data = struct.unpack("B", b"\0")
>>> print(data)
(0,)
>>> data = struct.unpack("B", b"\0")[0]
>>> print(data)
0

We don't need the [0] when "expanding" multiple elements of a tuple into multiple variables

>>> data = struct.unpack("3B", b"\0\1\2")
>>> print(data)
(0, 1, 2)
>>> byte_1, byte_2, byte_3 = data
>>> print(byte_1, byte_2, byte_3)
0 1 2