This AppNote is explaining use of serialization and deserialization using LUA pack library. Detailed description about LUA pack library is here.
This LUA module contains two methods pack.pack for serialization and pack.unpack for deserialization. Both of them use a "format" string to describe how to pack/unpack the data. The format string contains one or more data specifiers, each data specifier is applied to a single variable that must be packed/unpacked. The data specifier has the following general format:
[endianness]<format specifier>[count]
endianness can be chosen based on following table:
Endiannes symbol | Meaning |
---|---|
'<' | little endian |
'>' | big endian |
'=' | native endian (the platform's endian order, default). |
Format can be choosen based on following table:
Format specifier | Corresponding variable type |
---|---|
'z' | zero-terminated string |
'p' | string preceded by length byte |
'P' | string preceded by length word |
'a' | string preceded by length size_t |
'A' | string |
'f' | float |
'd' | double |
'n' | Lua number |
'c' | char |
'b' | byte = unsigned char |
'h' | short |
'H' | unsigned short |
'i' | int |
'I' | unsigned int |
'l' | long |
'L' | unsigned long |
Deserialization can be used for example when you receive data over LoRaWAN/NB and need to separate each byte to proccess the value. This function will extract each byte into separate variable.
nextpos, val1, val2, ..., valn = pack.unpack( string, format, [ init ] )
--string - the string to unpack.
--format - format specifier.
--init - (optional) marks where in string the unpacking should start (1 if not specified).
Lets say, we sent a simple command to the device using 1B, for example 0x01
(send ID) or 0x02
(send current data). We throw away first byte, as it contains value for nextpos and load cmd variable with received command.
received = "02"
--received data as string
toSend = 0
-- Example of an ID
-- instead you can use function to read ID
id = 0x92198717
-- received one byte
_, cmd = pack.unpack(received, "b", 1)
--first byte thrown away, loaded cmd
api.dumpArray(cmd)
--result 02
--show received cmd in console
Serialization can be used to send data over LoRa or NB. It will pack multiple variables into single string.
packed = pack.pack( format, val1, val2, ..., valn )
--format - format specifier.
--val1 - first variable to pack.
--val2 - second variable to pack.
--valn - nth variable to pack.
Example of packing six 1B variables into one string in little endian.
-- Example of data
t_hour = 9
t_min = 30
d1 = 0xA1
d2 = 0xC2
d3 = 0xE3
checksum = 0x22
print("Send current data")
toSend = pack.pack("<b6", t_hour, t_min, d1, d2, d3, checksum)
-- at this point, toSend = 0x091EA1C2E322 with format of little endiean byte
api.nbSend(ip, port, toSend, 1000, proto)
api.dumpArray(toSend)
Example of packing mutltiple different data formats into single string
t_hour = 9
t_min = 30
str = "test"
size_t = string.len (str)
d1 = 0xD1 --integer value
d2 = 0x4143eb85 --12.32 in IEEE-754 Floating Point format with big endian
d3 = 0xE3 --integer value
checksum = 22
print("Send current data")
toSend = pack.pack("<b3 <A1 <b1 >f <b1 <b1", t_hour, t_min, size_t, str, d1, d2, d3, checksum)
-- at this point, toSend = \x09\x1E\x04\x74\x65\x73\x74\xD1\xB8\x1E\x45\x41\xE3\x16 with format of little endiean byte
api.dumpArray(toSend)
print(toSend)
Unpacking previous data with python script
from construct import *
input_data = Struct(
"t_hour" / Int8ul,
"t_min" / Int8ul,
"str" / PascalString(VarInt,"utf8"),
"d1" / Int8ul,
"d2" / Float32b,
"d3" / Int8ul,
"checksum" / Int8ul,
)
result = input_data.parse(b'\x09\x1E\x04\x74\x65\x73\x74\xD1\xB8\x1E\x45\x41\xE3\x16')
>>> result.t_hour
9
>>> result.t_min
30
>>> result.str
'test'
>>> result.d1
209
>>> result.d2
12.319999694824219
>>> result.d3
227
>>> result.checksum
22
Informations about this lua package were used from http://www.eluaproject.net/doc/master/en_refman_gen_pack.html