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

RF24Mesh mesh.read() package size in python #184

Closed
hansendm opened this issue May 24, 2021 · 47 comments
Closed

RF24Mesh mesh.read() package size in python #184

hansendm opened this issue May 24, 2021 · 47 comments

Comments

@hansendm
Copy link

I love the work you guys are doing. Thanks for everything!

My question is, why can we not use something like sys.getsizeof in order to figure out the package size that is being received just like what an arduino would do in C with sizeof(payload)

I did see a post done by Aaron Hall (see below) and was wondering if something like this would work if the above function wouldn't?

import sys
from numbers import Number
from collections import deque
from collections.abc import Set, Mapping


ZERO_DEPTH_BASES = (str, bytes, Number, range, bytearray)


def getsize(obj_0):
    """Recursively iterate to sum size of object & members."""
    _seen_ids = set()
    def inner(obj):
        obj_id = id(obj)
        if obj_id in _seen_ids:
            return 0
        _seen_ids.add(obj_id)
        size = sys.getsizeof(obj)
        if isinstance(obj, ZERO_DEPTH_BASES):
            pass # bypass remaining control flow and return
        elif isinstance(obj, (tuple, list, Set, deque)):
            size += sum(inner(i) for i in obj)
        elif isinstance(obj, Mapping) or hasattr(obj, 'items'):
            size += sum(inner(k) + inner(v) for k, v in getattr(obj, 'items')())
        # Check for custom object instances - may subclass above too
        if hasattr(obj, '__dict__'):
            size += inner(vars(obj))
        if hasattr(obj, '__slots__'): # can have __slots__ with __dict__
            size += sum(inner(getattr(obj, s)) for s in obj.__slots__ if hasattr(obj, s))
        return size
    return inner(obj_0)

I'm building an intra-network and would have different sized payloads coming back and going out of the MasterNode. There has to be a more conventional way to dealing with the payload size than what I have been reading, which has been setting the sizes to be static.

Very Respectfully,
Daniel

@2bndy5
Copy link
Member

2bndy5 commented Jun 29, 2021

Sorry for the late reply.
In the python wrapper, the messages should be bytearrays, and the header is always 8 bytes. So

header_in, msg_in = network.read()
size_of_received_payload = len(msg_in) + 8  # +8 bytes for the header

The same technique could be used for outgoing payloads also.

The only time I've ever come across a usefulness of sys.getsizeof() is when I needed to get the allocated size of an instantiated driver object, but that doesn't seem to be what you're looking for. I doubt sys.getsizeof(network) would work since the python wrapper is really a c-extension using boost.python. The code you posted is meant for datatypes native to python.

@hansendm
Copy link
Author

hansendm commented Jul 16, 2021

Thanks for getting back with me. I'm actually in a time crunching mode getting this communication construct working between my arduino nano and raspberry pi. Here is what I am dealing with right now.

I am trying to pass a C++ struct from my arduino to my raspberry pi. I have a struct that looks like this:

struct node_status
{
  char *node_type = "incubator";
  char *sub_type;          // set the type of incubator
  int sub_type_id;
  bool sleep = false;         // set to sleep
  int check_in_time = 1000;   // set check in time
  bool LOCK = false;      // set if admin control is true/false
} nodeStatus;

I tried using the python module named struct

from RF24 import *
from RF24Network import *
from RF24Mesh import *
import RPi.GPIO as GPIO
import threading
from time import sleep, time
from struct import pack,unpack

# radio setup for RPi B Rev2: CS0=Pin 24
radio = RF24(22,0);
network = RF24Network(radio)
mesh = RF24Mesh(radio, network)

# # GPIO.cleanup()
# # CE Pin, CSN Pin, SPI Speed
# radio = RF24(22,0)
# radio.setChannel(3)
# network = RF24Network(radio)
# mesh = RF24Mesh(radio, network)		
mesh.setNodeID(0) #MasterNode
mesh.begin()
radio.setPALevel(RF24_PA_MAX) # Power Amplifier
radio.printDetails()
		
def millis():
    return int((time()-start)*1000) % (2 ** 32)

def delay(ms):
    ms = ms % (2**32)
    sleep(ms/1000.0)

class RF24_Monitor(threading.Thread):
	def __init__(self):
		print('init')

	def check_network(self):      
		mesh.update()
		mesh.DHCP()
		while network.available():
			print('checking network')
			header, payload = network.read(10)
			nodeID = oct(header.from_node)
			if chr(header.type) == 127:
				print("Rcvd ERROR msg from 0{:o}".format(header.from_node))
				print("ERROR: {}".format(unpack("s",payload[0]))) #ERROR
				ERROR= unpack("s",payload[0])
				
			elif chr(header.type) == 126:
				print("Rcvd Node Status msg from 0{:o}".format(header.from_node))
				print("node_type: {}".format(unpack("10s",payload[0]))) #node_type
				node_type = unpack("10s",payload[0])
				print("sub_type: {}".format(unpack("10s",payload[1]), header.from_node))	#sub_type
				sub_type = unpack("10s",payload[1])
				print("sub_type_id: {}".format(unpack("b",payload[2])))
				sub_type_id = unpack("b",payload[2])
				print("sleep: {}".format(unpack("?",payload)[3]))	#sleep
				sleep = unpack("?",payload[3])
				print("check_in_time: {}".format(unpack("l",payload[4])))	#check_in_time
				check_in_time = unpack("l",payload[4])
				print("Lock: {}".format(unpack("?",payload[5])))	#LOCK
				Lock = unpack("?",payload[5])
				
			elif chr(header.type) == 111:
				print("Rcvd COUNT msg from 0{:o}".format(header.from_node))
				print("Count: {}".format(unpack("b",payload[0])))	#Count
				Count = unpack("b",payload[0])
				print("start: {}".format(unpack("?",payload[1])))	#start
				start = unpack("?",payload[1])
				print("end: {}".format(unpack("?",payload[2])))	#end
				end = unpack("?",payload[2])
				
			elif chr(header.type) == 2:
				print("Rcvd DHT msg from 0{:o}".format(header.from_node))
				print("Temperature: {}".format(unpack("f",payload[0])))	#Temperature
				Temperature = unpack("f",payload[0])
				print("Fahreneit: {}".format(unpack("?",payload[1])))	#TempUnit
				TempUnit = unpack("?",payload[1])
				print("Humidity: {}".format(unpack("f",payload[2])))	#Humidity
				Humidity = unpack("f",payload[2])
				
			elif chr(header.type) == 1:
				print("Rcvd PIR msg from 0{:o}".format(header.from_node))
				print("Triggered: {".format(unpack("?",payload[0])))	#Triggered
				Triggered = unpack("?",payload[0])
				print("Silent: {".format(unpack("?",payload[1])))	#Silent
				Silent = unpack("?",payload[1])
			else:
				print("Rcv bad type {} from 0{:o}".format(header.type,header.from_node))
	
	def run(self):
		while True:
			self.check_network()


############################################################
#  Main Entry
############################################################
if __name__ == "__main__":
	RF24_Monitor_Controller = RF24_Monitor()
	RF24_Monitor_Controller.run()

but I am not having much luck. I was even looking at just using ctypes module but seem to not be going anywhere..

from ctypes import *

class interpret_nodes_status(Structure):
    _fields_ = [('node_type',c_char_p),
                ('sub_type',c_char_p),
                ('sub_type_id',c_int),
                ('sleep',c_bool),
                (check_in_time',c_int),
                ('LOCK',c_bool)]

nodestatus = translate_nodes_status(payload)

but that just gives me an error

TypeError: bytes or integer address expected instead of bytearray instance
What can I do? WHERE am I going wrong with this?

@2bndy5
Copy link
Member

2bndy5 commented Jul 16, 2021

Firstly, it's important that you make sure the c-string transmitted ends in a null terminating char ('\0'). If you know the c-string will have a fixed length, then you can manually add this terminating char to a payload's c-string on the receiving end, but it might be easier to make sure the terminating char is present before transmitting the c-string if it is not a fixed length.

example:

struct node_status
{
  char node_type[10] = "incubator";
  char sub_type[10];          // set the type of incubator
  int sub_type_id;
  bool sleep = false;         // set to sleep
  int check_in_time = 1000;   // set check in time
  bool LOCK = false;      // set if admin control is true/false
} nodeStatus;

could be unpacked in python like

import struct

# let `payload` be the received message
(
    node_type,
    sub_type,
    sub_type_id,
    sleep,
    check_in_time,
    LOCK,
) = struct.unpack("<10s10si?i?", payload)

Last time I looked into ctypes, it was meant for wrapping C/C++ source code into something the python interpreter understands (at runtime - not compile time). So I doubt that extension module would do what you're hoping.

BTW, to covert a python bytearray into a python bytes object, all you need to do is

bytes_obj = bytes(payload)  # where `payload` would be a bytearray

@2bndy5
Copy link
Member

2bndy5 commented Jul 16, 2021

@hansendm It would help to know the exact error that you're trying to troubleshoot. Because I don't know this info, I'm guessing that read()ing 10 bytes isn't enough. A message composed of 2 10-byte strings, 2 4-byte integers, and 2 1-byte booleans total to 30 bytes (which coincidentally invokes using RF24Network's message fragmentation).

@2bndy5
Copy link
Member

2bndy5 commented Jul 16, 2021

@TMRh20 I think I figured out the actual question being posed here. It would seem that the RF24NetworkFrame struct isn't exposed in the python wrapper. Since that data structure is where the message_size member lives, users have no way of knowing what to pass to network.read() (or peek()) without pre-existing knowledge of the message being transmitted.

This should easily be solved (for best backward compatibility) by making the max_len argument optional in the python wrapper's RF24Network::read() and RF24Network::peek(). There really isn't any need to expose the whole RF24NetworkFrame structure if all they need is the message's size (which could be inferred from the returned bytearray message).

@hansendm Is this what you were originally trying to ask?

ps - on the C++ side, RF24Network could also use a new method similar to RF24::getDynamicPayloadSize() called RF24Network::getMessageSize() (or maybe peekMessageSize())

@2bndy5
Copy link
Member

2bndy5 commented Jul 17, 2021

@hansendm have you tried using network.peek(RF24NetworkHeader()) it should return the message size that you can then use in read()

head  = RF24NetworkHeader()
msg_size = network.peek(head)
head, payload = network.read(msg_size)

If this works, then my previous comment should be disregarded. I can't seem to test the python wrapper for RF24Network on RPi OS (using latest stable release) for python3 because I keep getting

Traceback (most recent call last):
  File "examples/helloworld_rx.py", line 7, in <module>
    from RF24Network import RF24Network
ImportError: /usr/local/lib/librf24network.so.1: undefined symbol: _ZN4RF2410setAutoAckEhb

@hansendm
Copy link
Author

hansendm commented Jul 17, 2021

Oh hey Brandon, THANK YOU so much for replying and trying to figure this out. And yes, that was what I was originally trying to ask. We must be on different time zones, else I would have answered immediately.

I was successful at using the latest stable release but I had to install the individual libraries in order of RF24, RF24Network, and then RF24Mesh. It was then a bit more complicated getting the SPI fully enabled.

So, the struct would have string variables (node_type and sub_type) that I had made to length 10 bytes. The sub_type would vary ('chicken','turkey','quail','peacock', etc.). Are you saying that before I send out the message with the struct I need to add char ('\0') at the end of each string variable inside the struct?

This is my code now:

////////////////////////////////////////////////////////////////////////////
// structure for current node status
struct node_status
{
  char *node_type = "incubator";
  char *sub_type;          // set the type of incubator
  int sub_type_id;
  bool sleep = false;         // set to sleep
  int check_in_time = 1000;   // set check in time
  bool LOCK = false;      // set if admin control is true/false
} nodeStatus;

// Payload from MASTER
struct MASTER_node_command
{
  char *sub_type;         // set the type of incubator
  bool sleep = false;         // set to sleep
  int  check_in_time = 10000;   // set check in time
  bool LOCK = false;      // set if admin control is true/false
} MasterCommand;

/**
   temp and humidity control points
*/
struct chicken_config
{
  char *node_type = "incubator";
  char *sub_type = "chicken";   // set the type of incubator
  int sub_type_id = 1;
  int hot = 102;              //set hot parameter
  float optimum_temp = 99.5;    // set optimum_temp temp to go to b4 turning off
  float cold = 89.9;            //set cold parameter
  int high_hum = 65;          //set high humidity parameter
  int optimum_hum = 60;       // set optimum humidity parameter to go to b4 turning off
  int low_hum = 55;           // set low humidity parameter
} chickenConfig;

struct turkey_config
{
  char *node_type = "incubator";
  char *sub_type = "turkey";    // set the type of incubator
  int sub_type_id = 2;
  int hot = 102;              //set hot parameter
  float optimum_temp = 99.5;    // set optimum_temp temp to go to b4 turning off
  float cold = 89.9;            //set cold parameter
  int high_hum = 65;          //set high humidity parameter
  int optimum_hum = 60;       // set optimum humidity parameter to go to b4 turning off
  int low_hum = 55;           // set low humidity parameter
} turkeyConfig;

struct peacock_config
{
  char *node_type = "incubator";
  char *sub_type = "peacock";   // set the type of incubator
  int sub_type_id = 3;
  int hot = 101;              //set hot parameter
  float optimum_temp = 99.5;    // set optimum_temp temp to go to b4 turning off
  float cold = 98.9;            //set cold parameter
  int high_hum = 65;          //set high humidity parameter
  int optimum_hum = 60;       // set optimum humidity parameter to go to b4 turning off
  int low_hum = 55;           // set low humidity parameter
} peacockConfig;

struct chameleon_config
{
  char *node_type = "incubator";
  char *sub_type = "chameleon";   // set the type of incubator
  int sub_type_id = 4;
  int hot = 81;               //set hot parameter
  float optimum_temp = 77.5;    // set optimum_temp temp to go to b4 turning off
  float cold = 76.5;            //set cold parameter
  int high_hum = 95;          //set high humidity parameter
  int optimum_hum = 85;       // set optimum humidity parameter to go to b4 turning off
  int low_hum = 75;           // set low humidity parameter
} chameleonConfig;


// structure for current incubator configuration
struct current_config
{
  char *node_type = "incubator";
  char *sub_type;         // set the type of incubator
  int sub_type_id;
  int hot;              //set hot parameter
  float optimum_temp;     // set optimum_temp temp to go to b4 turning off
  float cold;             //set cold parameter
  int high_hum;         //set high humidity parameter
  int optimum_hum;      // set optimum humidity parameter to go to b4 turning off
  int low_hum;          // set low humidity parameter
} currentConfig;

////////////////////////////////////////////////////////////////////////////////////
// setup directed sub-type
void switch_sub_type(int sub_type)
{
  switch (sub_type)
  {
    // assign the bird type params
    case 1:
      currentConfig.sub_type = chickenConfig.sub_type;
      currentConfig.sub_type_id = chickenConfig.sub_type_id;
      currentConfig.hot = chickenConfig.hot;
      currentConfig.optimum_temp = chickenConfig.optimum_temp;
      currentConfig.cold = chickenConfig.cold;
      currentConfig.high_hum = chickenConfig.high_hum;
      currentConfig.optimum_hum = chickenConfig.optimum_hum;
      currentConfig.low_hum = chickenConfig.low_hum;
      break;
    case 2:
      currentConfig.sub_type = turkeyConfig.sub_type;
      currentConfig.sub_type_id = turkeyConfig.sub_type_id;
      currentConfig.hot = turkeyConfig.hot;
      currentConfig.optimum_temp = turkeyConfig.optimum_temp;
      currentConfig.cold = turkeyConfig.cold;
      currentConfig.high_hum = turkeyConfig.high_hum;
      currentConfig.optimum_hum = turkeyConfig.optimum_hum;
      currentConfig.low_hum = turkeyConfig.low_hum;
      break;
    case 3:
      currentConfig.sub_type = peacockConfig.sub_type;
      currentConfig.sub_type_id = peacockConfig.sub_type_id;
      currentConfig.hot = peacockConfig.hot;
      currentConfig.optimum_temp = peacockConfig.optimum_temp;
      currentConfig.cold = peacockConfig.cold;
      currentConfig.high_hum = peacockConfig.high_hum;
      currentConfig.optimum_hum = peacockConfig.optimum_hum;
      currentConfig.low_hum = peacockConfig.low_hum;
      break;
    case 4:
      currentConfig.sub_type = chameleonConfig.sub_type;
      currentConfig.sub_type_id = chameleonConfig.sub_type_id;
      currentConfig.hot = chameleonConfig.hot;
      currentConfig.optimum_temp = chameleonConfig.optimum_temp;
      currentConfig.cold = chameleonConfig.cold;
      currentConfig.high_hum = chameleonConfig.high_hum;
      currentConfig.optimum_hum = chameleonConfig.optimum_hum;
      currentConfig.low_hum = chameleonConfig.low_hum;
      break;
  }
  return;
}


bool send_node_status()
{
  nodeStatus.sub_type = currentConfig.sub_type;
  nodeStatus.sub_type_id = currentConfig.sub_type_id;
  nodeStatus.sleep = MasterCommand.sleep;
  nodeStatus.check_in_time = MasterCommand.check_in_time;
  nodeStatus.LOCK = MasterCommand.LOCK;

  RF24NetworkHeader header();

  if (!mesh.write(&nodeStatus, /*type*/ 126, sizeof(nodeStatus), /*to node*/ 000))
  { // Send the data
    if ( !mesh.checkConnection() )
    {
      Serial.println("Renewing Address");
      mesh.renewAddress();

    }
  }
  else
  {
    Serial.println("node status msg Sent");
    return;
  }

Now, you're saying that I would have to change it to be like this:

bool send_node_status()
{
  nodeStatus.sub_type = currentConfig.sub_type +char ('\0');
  nodeStatus.sub_type_id = currentConfig.sub_type_id +char ('\0');
  nodeStatus.sleep = MasterCommand.sleep;
  nodeStatus.check_in_time = MasterCommand.check_in_time;
  nodeStatus.LOCK = MasterCommand.LOCK;

  RF24NetworkHeader header();

  if (!mesh.write(&nodeStatus, /*type*/ 126, sizeof(nodeStatus), /*to node*/ 000))
  { // Send the data
    if ( !mesh.checkConnection() )
    {
      Serial.println("Renewing Address");
      mesh.renewAddress();

    }
  }
  else
  {
    Serial.println("node status msg Sent");
    return;
  }

And on the python side I could use:

import struct

head  = RF24NetworkHeader()
msg_size = network.peek(head)
head, payload = network.read(msg_size)

# let `payload` be the received message
(
    node_type,
    sub_type,
    sub_type_id,
    sleep,
    check_in_time,
    LOCK,
) = struct.unpack("<10s10si?i?", payload)

Is that correct? would the '<10s' formatting still work for the sub_type even though I may technically have different word lengths, like I mentioned before? I mean, I did set that variable length to be 10.

@hansendm
Copy link
Author

BTW my full code is located at https://github.com/hansendm/DANA and I'm using the comms_test.py for the raspi side to figure this out. I hope that you can try my code out and maybe have some success.

@hansendm
Copy link
Author

Just added my Node001 code.

@2bndy5
Copy link
Member

2bndy5 commented Jul 17, 2021

from the python docs about packing/unpacking char[] (the s format specifier):

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

your sub_type and node_type strings should have allocated the max bytes needed for all possible scenarios. Meaning any bytes in the strings not occupied by char data should be set to 0 (shouldn't need to concatenate string with another character). If your maximum needed length is 9 characters, you need to allocate 10.

I'm also interested in the actual error messages you're trying to resolve.

@hansendm
Copy link
Author

TypeError: bytes or integer address expected instead of bytearray instance

@2bndy5
Copy link
Member

2bndy5 commented Jul 17, 2021

I thought that was from the ctypes attempt.

@hansendm
Copy link
Author

That was coming from the node_status interpreter on the python side whenever it got to

print("node_type: {}".format(unpack("10s",payload[0]))) #node_type

@hansendm
Copy link
Author

Actually...you might be right..I'm at work right now. So I can't verify whether I was getting the same message. I know I had multiple error messages depending on what changes I made to figure things out. I think I was having an error stating something about needing the correct byte length.

@2bndy5
Copy link
Member

2bndy5 commented Jul 17, 2021

oh, that's because payload[0] equals the integer for the ASCII character i as in "incubator".

@hansendm
Copy link
Author

you have to be kidding me.. so I would have done something like payload[0:10] ?

@2bndy5
Copy link
Member

2bndy5 commented Jul 17, 2021

yep. payload[:10] would be a 10 byte buffer needed to unpack as a 10-char string

@hansendm
Copy link
Author

and that would be enough for the 'incubator'+char('\0') ?

I'm still learning all of this.

@hansendm
Copy link
Author

But I wouldn't have to worry about that if I were to break down the payloads like you mentioned?

head  = RF24NetworkHeader()
msg_size = network.peek(head)
head, payload = network.read(msg_size)

# let `payload` be the received message
(
    node_type,
    sub_type,
    sub_type_id,
    sleep,
    check_in_time,
    LOCK,
) = struct.unpack("<10s10si?i?", payload)

@2bndy5
Copy link
Member

2bndy5 commented Jul 17, 2021

I'm still learning all of this.

yeah, I figured.

But I wouldn't have to worry about that if I were to break down the payloads like you mentioned?

exactly why I posted the code like that

@hansendm
Copy link
Author

You're awesome! I will test this out when I get home.

@hansendm
Copy link
Author

But wait..you said I have to make the lengths of the subt_type string length equal to being 10.. would that mean I have to do something like:


  char *node_type = "incubator"char('\0');
  char *sub_type = "turkey00"+char('\0');    // set the type of incubator

@2bndy5
Copy link
Member

2bndy5 commented Jul 17, 2021

char * is actually a little different than char []. I would go with char str_type[10] instead. The terminating char 0 should be included with the c-string data.

Let me know how the peek() idea works out for getting the message size.

I'm currently porting this lib to pure python (literally testing it now). We are definitely in different time zones (I should have gone to sleep a few hours ago).

@2bndy5
Copy link
Member

2bndy5 commented Jul 17, 2021

BTW the zeroes in "turkey00" are actually the ASCII integer 48. You should use "turkey\0\0"

@hansendm
Copy link
Author

I've been in AF and S.K. . I know how that can be. I will let you know about the peek() . I am implementing the changes to my git repository so that I have it ready to go once I get home.

Ok but you are saying that I have to fill up the strings to their max size? Then remove those characters on the python side?

@2bndy5
Copy link
Member

2bndy5 commented Jul 17, 2021

Ok but you are saying that I have to fill up the strings to their max size?

yes

Then remove those characters on the python side?

I think struct.unpack() does that for you. If not, then the resulting string would look like turkey\x00\x00\x00

@hansendm
Copy link
Author

If this works I'm sponsoring you

@2bndy5
Copy link
Member

2bndy5 commented Jul 17, 2021

did a quick test in python3 REPL:

>>> b = b'turkey\0\0\0\0'
>>> struct.unpack("<10s", b)[0]
b'turkey\x00\x00\x00\x00'
>>> struct.unpack("<10s", b)[0].decode()
'turkey\x00\x00\x00\x00'

looks like you have to remove the null bytes on the receiving end.

@hansendm
Copy link
Author

Interesting, I did not think about using .decode() while originally testing out struct on the python side.

@2bndy5
Copy link
Member

2bndy5 commented Jul 17, 2021

it converts UTF-8 encoded buffers to a str (very useful)

@2bndy5
Copy link
Member

2bndy5 commented Jul 17, 2021

tip:

>>> 'turkey\x00\x00\x00\x00'.rstrip('\0')
'turkey'

@hansendm
Copy link
Author

b.E.a.utiful.

@hansendm
Copy link
Author

Hey, Sorry it took so long but I had to do a lot of doubles at work.

It seems that 'RF24Network' object has no attribute 'peek'

@hansendm
Copy link
Author

hansendm commented Jul 21, 2021 via email

@2bndy5
Copy link
Member

2bndy5 commented Jul 21, 2021

Pass 144 to read() every time. It should only read as much as it needs to pop the header & payload from the queue. I can't seem to get the python wrapper to work for me. Can you post the output of

dir(network) # where `network` is the instantiated RF24Network object

There is a couple peek() methods in the wrapper, but the wrapper is written weird. So the methods might be named something different (like peekInt()).

@hansendm
Copy link
Author

it is saying
struct.error: unpack requires a buffer of 30 bytes

when it comes to

(
    node_type,
    sub_type,
    sub_type_id,
    sleep,
    check_in_time,
    LOCK,
) = struct.unpack("<10s10si?i?", payload)

Now, to be clear, I removed the

head  = RF24NetworkHeader()
msg_size = network.peek(head)
head, payload = network.read(msg_size)

and instead placed

head, payload = network.read(144)

@2bndy5
Copy link
Member

2bndy5 commented Jul 21, 2021

This is the peek() function that should exist. It should return the message size that can be used. If you run dir(network), it should display all the methods/attributes that belong to a RF24Network object.

Unfortunately, it looks like read() returns the payload with a length of what you passed to the read() function. This is unintuitive and should be improved. Not to mention, the parameter specifying the length of the payload to return should be optional.

Until we get a fix for this (which means I have to get the python wrapper working on my RPi), you'll have to slice the payload to only the length that struct.pack() needs (which can be determined based on the format specifier string). So, basically

# get the max sized payload despite what was actually received
head, payload = network.read(144)

# unpack 30 bytes
(
    node_type,
    sub_type,
    sub_type_id,
    sleep,
    check_in_time,
    LOCK,
) = struct.unpack("<10s10si?i?", payload[:30])

Alternatively, you could pass a number that would be the max sized payload that any of your network nodes transmit, but you still need to slice the payload to only what unpack() needs. Hint: the python docs will tell you what length each specifier expects

@hansendm
Copy link
Author

Unfortunately, it is still giving the same error.

@hansendm
Copy link
Author

hansendm commented Jul 22, 2021

I tried

header, payload = network.read(144)
size_payload = len(payload)+8
print(size_payload)

and that printed 18. Is this due to fracturing that you mentioned before?

This is the message I'm sending:

struct node_status
{
  char *node_type = "incubator\0";
  char *sub_type = "chicken\0\0\0";   // set the type of incubator
  int sub_type_id = 1;
  bool sleep = false;         // set to sleep
  int check_in_time = 1000;   // set check in time
  bool LOCK = false;      // set if admin control is true/false
} nodeStatus;

when I

print(payload)

I get

bytearray(b"\x08\x014\x01\x04\x00\x00\x10\'\x00")

and I printed dir(network) and I don't see a peek in there. I do, however, see __sizeof__ , which was 0 and __instance_size__ which was 180

@2bndy5
Copy link
Member

2bndy5 commented Jul 22, 2021

I've said this before:

char * is actually a little different than char []. I would go with char str_type[10] instead. The terminating char 0 should be included with the c-string data.

I see that you're still using char *. Please read this StackOverflow answer. You are literally transmitting the pointer (memory address) of where a c-string is allocated. You need to transmit the c-string itself. Allocate the c-string using char name[<const size>]. If you declare and initialize the value, you can use char name[] = "incubator";

As for the small payload size, I'm can"t say why that is without more context.

@hansendm
Copy link
Author

I tried but then again I've never used that before. I kept getting an error. You are saying this is how it should look like?

struct node_status
{
  char node_type[10] = "incubator";
  char sub_type[10] = "chicken";   // set the type of incubator
  int sub_type_id = 1;
  bool sleep = false;         // set to sleep
  int check_in_time = 1000;   // set check in time
  bool LOCK = false;      // set if admin control is true/false
} nodeStatus;

@2bndy5
Copy link
Member

2bndy5 commented Jul 22, 2021

yep. this is how I use c-strings in the RF24 lib's AckPayload examples

If you're getting an error about the number in the square brackets while declaring the variable with a initial value, then you can leave out the number and just use

char node_type[] = "incubator"; // has to be a 9 char string to allocate 10 bytes

@2bndy5
Copy link
Member

2bndy5 commented Jul 22, 2021

@hansendm I finally got the RF24Network python wrapper working! I had to link the librf24.so to the extension module (via setup.py). I can confirm that the RF24Network class has a peek() function.

(env) pi@rpi4:~/github/RF24Network/RPi/pyRF24Network $ python
Python 3.7.3 (default, Jan 22 2021, 20:04:44)
[GCC 8.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import RF24
>>> import RF24Network
>>> radio = RF24.RF24(22, 0)
>>> network = RF24Network.RF24Network(radio)
>>> "peek" in dir(network)
True
>>> help(network.peek)
Help on method peek in module RF24Network:

peek(...) method of RF24Network.RF24Network instance
    peek( (RF24Network)arg1, (RF24NetworkHeader)header) -> int :

        C++ signature :
            unsigned short peek(RF24Network {lvalue},RF24NetworkHeader {lvalue})

    peek( (RF24Network)arg1, (int)maxlen) -> tuple :

        C++ signature :
            boost::python::tuple peek(RF24Network {lvalue},unsigned int)

press q to quit the help dialogue. Remember that the RF24Network {lvalue} doesn't need to be passed as it is the implicit self parameter used for all instance methods of a python class

@hansendm
Copy link
Author

It worked! I had to get rid of the sub_type and just depend on the sub_type_id due to not being able to assign the

case 1:
      currentConfig.sub_type = chickenConfig.sub_type;

It seems that variable was blank after trying to add [10] to all of the variables. Other times it gave me an incompatible issue.
I was able to read the messages beautifully after I removed that variable.

@hansendm hansendm reopened this Jul 23, 2021
@hansendm
Copy link
Author

Do you want to keep this issue open until the modifications of making the max_len argument optional in the python wrapper's RF24Network::read() and RF24Network::peek().?

@2bndy5
Copy link
Member

2bndy5 commented Jul 23, 2021

Awesome that you figured it out. Alternatively, I should've suggested a "coded" identifying number to replace the strings; meaning send the number 0 for "incubator", 1 for "chicken", ... Then use a python dict to translate the number back to the equivalent string value on the receiving end. But this suggestion is entirely dependent on the strings using a predefined group of possibilities.

Do you want to keep this issue open until the modifications of making the max_len argument optional in the python wrapper's RF24Network::read() and RF24Network::peek()?

Nah. I will tag you in the release that follows the resolution of that issue. BTW, you can also keep the dialogue going after the issue is closed.

@2bndy5 2bndy5 closed this as completed Jul 23, 2021
@hansendm
Copy link
Author

hansendm commented Jul 23, 2021 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants