Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Simple Get Message #38

Closed
folksman opened this issue Jan 13, 2022 · 17 comments
Closed

Simple Get Message #38

folksman opened this issue Jan 13, 2022 · 17 comments
Labels
question Further information is requested

Comments

@folksman
Copy link

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
Copy link
Member

2bndy5 commented Jan 13, 2022

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.

@2bndy5 2bndy5 added the question Further information is requested label Jan 13, 2022
@folksman
Copy link
Author

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

@2bndy5
Copy link
Member

2bndy5 commented Jan 14, 2022

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
Copy link
Author

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

@folksman
Copy link
Author

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
Copy link
Member

2bndy5 commented Jan 14, 2022

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.

  • 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.

@folksman
Copy link
Author

folksman commented Jan 14, 2022 via email

@2bndy5
Copy link
Member

2bndy5 commented Jan 14, 2022

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
Copy link
Author

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
Copy link
Member

2bndy5 commented Jan 15, 2022

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
Copy link
Author

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
Copy link
Member

2bndy5 commented Jan 17, 2022

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
Copy link
Author

folksman commented Jan 18, 2022

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
Copy link
Member

2bndy5 commented Jan 18, 2022

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
Copy link
Member

2bndy5 commented Jan 18, 2022

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
Copy link
Author

folksman commented Jan 19, 2022

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
Copy link
Member

2bndy5 commented Jan 19, 2022

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

@2bndy5 2bndy5 closed this as completed Jan 19, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

2 participants