LoRa encoding/decoding



  • Hello everyone,

    I am trying to get some double values from my Lopy 4 into my PHP web-application. Based on some example scripts, I wrote the following code:

    # Imports
    import ubinascii
    import pycom
    from network import LoRa
    import socket
    import struct
    
    # Set LoRa properties
    lora = LoRa(mode=LoRa.LORAWAN, region=LoRa.EU868)
    
    dev_addr = struct.unpack(">l", ubinascii.unhexlify('addr'))[0]
    nwk_swkey = ubinascii.unhexlify('key')
    app_swkey = ubinascii.unhexlify('key')
    
    # Pack data
    buffer = bytearray()
    buffer.extend(struct.pack('d', 2.5))
    buffer.extend(struct.pack('d', 3.6))
    buffer.extend(struct.pack('d', 3.2))
    buffer.extend(struct.pack('d', 8.1))
    buffer.extend(struct.pack('d', 1.1))
    
    # Send data
    lora.join(activation=LoRa.ABP, auth=(dev_addr, nwk_swkey, app_swkey))
    s = socket.socket(socket.AF_LORA, socket.SOCK_RAW)
    s.setsockopt(socket.SOL_LORA, socket.SO_DR, 5)
    
    s.setblocking(True)
    s.send(bytes(buffer))
    s.setblocking(False)
    

    My LoRa provider (KPN) receives the data, decrypts it and sends the payload to my webserver. The received payload-hex looks like this:

    0f3a4676ba1808b9ebdc9bb8a79c89a93d6722cead7656610ee5344a7eeb03afffa63a50b2860495
    

    After trying many different approaches, I somehow can't manage to turn this hex back into the original double values. I've got two major questions:

    • Is this a recommended way to pack the original data values? Am I doing something wrong here?
    • What exact steps need to be taken in order to turn this payload-hex back into the original values?

    I need to be able to do the decoding using PHP. However, if someone can tell me about the steps that need to be taken, I might be able to figure out the syntax myself.

    Thanks in advance!



  • My apologies for the late response and any confusion I might have caused by switching to integers within the post. The big/little-endian format was indeed the issue. Adding a '>' sign in both the micropython and the php script solved the problem. This is what I ended up doing.

    Micropython encoding

    # Pack data
    data = struct.pack('>5i', 25, 36, 81, 11)
    
    # Send data
    print('Sending data')
    
    lora.join(activation=LoRa.ABP, auth=(dev_addr, nwk_swkey, app_swkey))
    s = socket.socket(socket.AF_LORA, socket.SOCK_RAW)
    s.setsockopt(socket.SOL_LORA, socket.SO_DR, 5)
    
    s.setblocking(True)
    s.send(bytes(data))
    s.setblocking(False)
    

    PHP decoding

    $struct = new Struct();
    $decoded_payload = $struct->unpack('>5i', hex2bin($decrypted_payload));
    
    $data = [
    	'var1' => $decoded_payload[0]/10,
    	'var2' => $decoded_payload[1]/10,
    	'var3' => $decoded_payload[2]/10,
    	'var4' => $decoded_payload[3]/10,
    	'var5' => $decoded_payload[4]/10
    ];
    

    The PHP struct class I used in the PHP decoding script can be found here.

    Thanks a lot for your help!



  • @mfalkvidd I think @vicluijkx switched to integers by multiplying the original value by some factor, and used values different from the ones in the original post.

    My guess is that the values are 5770,66,2,21,176 (5 x 32-bit ints in little-endian order).



  • 1.1 should be 0xcdcc8c3f or 0x3f8ccccd depending on endianness. There is nothing similar in the decrypted message, so something seems to be way way off.



  • @mfalkvidd I believe the format was switched from d (double) to i (a 32-bit integer) between the two posts.



  • @vicluijkx the decrypted hex is much shorter than the encrypted hex. That's strange. Could there be something wrong with the decryption?

    To save space, you could use the "e" pack format, which only uses 2 bytes and still has about three digits precision.



  • @vicluijkx there may be an issue with big/little-endian format. Try adding > or < before the format.



  • Thanks @jcaron! After running some more tests I figured out that the hex string, as you mentioned, isn't decrypted at all. This is weird as the provider's documentation clearly states: "Messages are forwarded unencrypted. The developer portal already decrypts the LoRa data using the randomly generated AppSKey. No further decryption is necessary."

    Anyway, I managed to decrypt the payload-hex which returns something like this:

    'A86100002400000020000000510000000B000000'
    

    I was able to get this string back into the original values using unhexlify and unpack in a micropython console.

    >> import ubinascii
    >> import struct
    
    >> hex = 'A86100002400000020000000510000000B000000'
    >> unhex = ubinascii.unhexlify(hex) # Step 1
    >> struct.unpack('5i', unhex) # Step 2
    

    Now I'm trying to figure out how to do the same using PHP. I tried doing step 1 using the php hex2bin() function. This however, resulted into the following:

    ������@������ @������ @���@33 @�������?

    In order to do step 2, I tried using this php struct package. It was no surprise that this returned the same kind of characters:

    �a��$��� ���Q��� ���

    If I try to do step 1 using micropython and step 2 using this php package, I get an array of 5 numbers which I can't seem to relate to the original numbers.

    It would be great if someone could see what's going on here.



  • @vicluijkx Not sure what the actual format is. Are you sure this is the decrypted payload? Try using the same number twice and check if the 16 digits for those two numbers match. You can also log the buffer on the LoPy to see what it is supposed to contain.

    In any case, I would recommend avoiding floats. The format can be implementation specific, and as you can see, that's a lot of data (8 bytes per double), so unless you really need to cover a very wide range of values, I would recommend switching to a shorter format based on integers.



Pycom on Twitter