Search code examples
pythonpayloadtransmissionloraadafruit-circuitpython

Adafruit LoRa RFM96W payload limit is 252 bytes but I want to send 512 bytes of data


So basically I have a numpy array, its shape is (8,64) after array.tobytes() and len(array) is 512. And the send() function in the adafruit_rfm9x library has a payload limit of 252 bytes. Is there any way to do it?

Parameter in both devices.

RADIO_FREQ_MHZ = 433.0
CS = digitalio.DigitalInOut(board.CE1)
RESET = digitalio.DigitalInOut(board.D25)

spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO)
rfm9x = adafruit_rfm9x.RFM9x(spi, CS, RESET, RADIO_FREQ_MHZ)

Send data Function in Device1:

def send_message(key, value):
    print(f"Sending {key} to Dev 2...")
    if not isinstance(value, bytes):
        value = bytes(value, 'utf-8')
    
    MAX_CHUNK_SIZE = 252  # Maximum chunk size
    num_chunks = (len(value) + MAX_CHUNK_SIZE - 1) 
    for i in range(num_chunks):
        chunk = value[i * MAX_CHUNK_SIZE: (i + 1) * MAX_CHUNK_SIZE]
        rfm9x.send_with_ack(chunk)  
    print(f"{key} has been sent")

Receive data Function in Device2

def receive_message(key):
    print(f"Receiving {key} from Dev 1 ...")
    MAX_CHUNK_SIZE = 252
    received_data = b''
    while True:
        data = rfm9x.receive(with_ack=True)
        if data is not None:
            received_data += data
            if len(data) < MAX_CHUNK_SIZE:  
                break
        time.sleep(0.1)  
    print(f"Received {key}")
    return received_data

Device 1:

send_message("Data",nparray.tobytes())

Device 2:

Data= receive_message("Data")
result= np.frombuffer(Data, dtype=np.int8).reshape(8, 64)

I get this error. It looks like the packet isn't even being received properly.

ValueError: cannot reshape array of size 8 into shape (8,64)


Solution

  • You cannot send 512 bytes in one packet – that's a physical impossibility. The maximum a LoRa chip can send is 256 bytes, and THAT includes the packet's overhead. 252 is even generous, and might not work, not reliably anyway. It's not a limitation from the library, but from the silicon itself.

    For your use case, there are a couple of possibilities.

    1. Compress the data. Not sure whether you can compress the array by 60% or more, but worth a try.
    2. Split the data into manageable chunks. For 512 bytes of data I'd use at least 3 packets.
    3. Do (1) and (2) together. Compress the data first, and split it into 2 packets.

    Looking at your code, I see you are doing (2), but with the max chunk size, which isn't ideal, as mentioned above. Try smaller chunks --> you'll end up sending 3 packets anyway (right now you are trying to send 252 x 2 + 8). Try 171, 171 and 170 bytes. BUT. And this is where you are doing it really wrong. If you send each packet without waiting, they will get lost. For example, at SF 10, BW 125, 252 bytes of data take 2.214 seconds. At SF 12, BW 125, it takes 7.545 seconds. See this calculator.

    Even with 170 bytes only, you will have to wait at least 1.5 to 5.25 seconds between sends, depending on your settings, and that's not even taking into account processing the packet on the other side – and you're using Python, so not exactly the fastest code either... Give both devices some time before sending the next chunk...

    Ideally, you should be using CAD (Channel Activity Detection) before sending, but I don't think there's a Python library that has it.