{"id":13673743,"url":"https://github.com/thesolarnomad/lora-serialization","last_synced_at":"2025-04-09T16:18:34.027Z","repository":{"id":38291137,"uuid":"62647435","full_name":"thesolarnomad/lora-serialization","owner":"thesolarnomad","description":"LoraWAN serialization/deserialization library for The Things Network","archived":false,"fork":false,"pushed_at":"2025-03-18T13:33:49.000Z","size":721,"stargazers_count":167,"open_issues_count":5,"forks_count":27,"subscribers_count":14,"default_branch":"master","last_synced_at":"2025-04-09T16:18:27.579Z","etag":null,"topics":["arduino","arduino-library","bitarray","bitmap","deserialization","deserialization-library","deserialize","gps","humidity","lora","lorawan","serialization","serialization-library","serialize","serializer","temperature","thethingsnetwork","time","ttn","unixtime"],"latest_commit_sha":null,"homepage":null,"language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/thesolarnomad.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2016-07-05T15:19:17.000Z","updated_at":"2025-03-04T06:38:02.000Z","dependencies_parsed_at":"2024-01-17T04:41:24.491Z","dependency_job_id":"188b2e4c-f67b-4995-9d2a-4d4fe26a149d","html_url":"https://github.com/thesolarnomad/lora-serialization","commit_stats":{"total_commits":109,"total_committers":9,"mean_commits":12.11111111111111,"dds":"0.25688073394495414","last_synced_commit":"5bad121f925bcc09f328d2e5117a72ef065e3517"},"previous_names":[],"tags_count":6,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thesolarnomad%2Flora-serialization","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thesolarnomad%2Flora-serialization/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thesolarnomad%2Flora-serialization/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thesolarnomad%2Flora-serialization/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/thesolarnomad","download_url":"https://codeload.github.com/thesolarnomad/lora-serialization/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248065281,"owners_count":21041872,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["arduino","arduino-library","bitarray","bitmap","deserialization","deserialization-library","deserialize","gps","humidity","lora","lorawan","serialization","serialization-library","serialize","serializer","temperature","thethingsnetwork","time","ttn","unixtime"],"created_at":"2024-08-02T11:00:20.945Z","updated_at":"2025-04-09T16:18:34.006Z","avatar_url":"https://github.com/thesolarnomad.png","language":"C++","funding_links":[],"categories":["Libraries"],"sub_categories":[],"readme":"# LoRaWAN serialization/deserialization library for The Things Network\n\n[![Build Status](https://travis-ci.org/thesolarnomad/lora-serialization.svg?branch=master)](https://travis-ci.org/thesolarnomad/lora-serialization)\n[![Coverage Status](https://coveralls.io/repos/github/thesolarnomad/lora-serialization/badge.svg?branch=master)](https://coveralls.io/github/thesolarnomad/lora-serialization?branch=master)\n[![semantic-release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg)](https://github.com/semantic-release/semantic-release)\n\nThis fully unit-tested library allows you to encode your data on the Arduino side and decode it on the [TTN](https://console.thethingsnetwork.org/) side. It provides both a C-based encoder and a JavaScript-based decoder.\n\nSince version 2.2.0 there is also an encoder for the TTN side.\n\n## In short\n\n### Encoding on Arduino, decoding in TTN\nArduino side:\n```cpp\n#include \"LoraMessage.h\"\n\nLoraMessage message;\n\nmessage\n    .addUnixtime(1467632413)\n    .addLatLng(-33.905052, 151.26641);\n\nlora_send_bytes(message.getBytes(), message.getLength());\ndelete message;\n```\n\nTTN side:\n\n```javascript\nfunction decodeUplink(input)\n{\n    // decoder function according to https://www.thethingsindustries.com/docs/integrations/payload-formatters/javascript/uplink/\n    // input has the following structure:\n    // {\n    //   \"bytes\": [1, 2, 3], // FRMPayload (byte array)\n    //   \"fPort\": 1\n    // }\n    var data = decode(input.bytes, [unixtime, latLng], ['time', 'coords']);\n    return {data:data}\n}\n\n// include content from src/decoder.js:\nvar bytesToInt = function(bytes) {\n  var i = 0;\n  for (var x = 0; x \u003c bytes.length; x++) {\n    i |= +(bytes[x] \u003c\u003c (x * 8));\n  }\n  return i;\n};\n...\n```\n\n### Encoding in TTN\nTTN side:\n```javascript\n// include src/encoder.js\nvar bytes = encode([timestamp, [latitude, longitude]], [unixtime, latLng]);\n// bytes is of type Buffer\n```\n\n\n#### With the convenience class\n\n```javascript\n// include src/encoder.js\n// include src/LoraMessage.js\nvar bytes = new LoraMessage(encoder)\n    .addUnixtime(1467632413)\n    .addLatLng(-33.905052, 151.26641)\n    .addBitmap(true, true, false, true)\n    .getBytes();\n// bytes = \u003cBuffer 1d 4b 7a 57 64 a6 fa fd 6a 24 04 09 d0\u003e\n```\n\nand then decoding as usual:\n\n```js\nvar result = decoder.decode(\n    bytes,\n    [decoder.unixtime, decoder.latLng, decoder.bitmap],\n    ['time', 'coords', 'heaters']\n);\n// result =\n// { time: 1467632413,\n//  coords: [ -33.905052, 151.26641 ],\n//  heaters:\n//   { a: true,\n//     b: true,\n//     c: false,\n//     d: true,\n//     e: false,\n//     f: false,\n//     g: false,\n//     h: false } }\n```\n\n## General Usage\n\n### Unix time (4 bytes)\nSerializes/deserializes a unix time (seconds)\n\n```cpp\n#include \"LoraEncoder.h\"\n\nbyte buffer[4];\nLoraEncoder encoder(buffer);\nencoder.writeUnixtime(1467632413);\n// buffer == {0x1d, 0x4b, 0x7a, 0x57}\n```\nand then in the TTN frontend, use the following method:\n\n```javascript\nunixtime(input.bytes.slice(x, x + 4)) // 1467632413\n```\n\n### GPS coordinates (8 bytes)\nSerializes/deserializes coordinates (latitude/longitude) with a precision of 6 decimals.\n\n```cpp\n#include \"LoraEncoder.h\"\n\nbyte buffer[8];\nLoraEncoder encoder(buffer);\nencoder.writeLatLng(-33.905052, 151.26641);\n// buffer == {0x64, 0xa6, 0xfa, 0xfd, 0x6a, 0x24, 0x04, 0x09}\n```\nand then in the TTN frontend, use the following method:\n\n```javascript\nlatLng(input.bytes.slice(x, x + 8)) // [-33.905052, 151.26641]\n```\n\n### Unsigned 8bit Integer (1 byte)\nSerializes/deserializes an unsigned 8bit integer.\n\n```cpp\n#include \"LoraEncoder.h\"\n\nbyte buffer[1];\nLoraEncoder encoder(buffer);\nuint8_t i = 10;\nencoder.writeUint8(i);\n// buffer == {0x0A}\n```\nand then in the TTN frontend, use the following method:\n\n```javascript\nuint8(input.bytes.slice(x, x + 1)) // 10\n```\n\n### Unsigned 16bit Integer (2 bytes)\nSerializes/deserializes an unsigned 16bit integer.\n\n```cpp\n#include \"LoraEncoder.h\"\n\nbyte buffer[2];\nLoraEncoder encoder(buffer);\nuint16_t i = 23453;\nencoder.writeUint16(i);\n// buffer == {0x9d, 0x5b}\n```\nand then in the TTN frontend, use the following method:\n\n```javascript\nuint16(input.bytes.slice(x, x + 2)) // 23453\n```\n\n### Unsigned 32bit Integer (4 bytes)\nSerializes/deserializes an unsigned 32bit integer.\n\n```cpp\n#include \"LoraEncoder.h\"\n\nbyte buffer[4];\nLoraEncoder encoder(buffer);\nuint32_t i = 2864434397;\nencoder.writeUint32(i);\n// buffer == {0xdd, 0xcc, 0xbb, 0xaa}\n```\nand then in the TTN frontend, use the following method:\n\n```javascript\nuint32(input.bytes.slice(x, x + 4)) // 2864434397\n```\n\n### Temperature (2 bytes)\nSerializes/deserializes a temperature reading between -327.68 and +327.67 (inclusive) with a precision of 2 decimals.\n\n```cpp\n#include \"LoraEncoder.h\"\n\nbyte buffer[2];\nLoraEncoder encoder(buffer);\nencoder.writeTemperature(-123.45);\n// buffer == {0xcf, 0xc7}\n```\nand then in the TTN frontend, use the following method:\n\n```javascript\ntemperature(input.bytes.slice(x, x + 2)) // -123.45\n```\n\n### Humidity (2 bytes)\nSerializes/deserializes a humidity reading between 0 and 100 (inclusive) with a precision of 2 decimals.\n\n```cpp\n#include \"LoraEncoder.h\"\n\nbyte buffer[2];\nLoraEncoder encoder(buffer);\nencoder.writeHumidity(99.99);\n// buffer == {0x0f, 0x27}\n```\nand then in the TTN frontend, use the following method:\n\n```javascript\nhumidity(input.bytes.slice(x, x + 2)) // 99.99\n```\n\n### Full float (4 bytes)\nSerializes/deserializes a full 4-byte float.\n\n```cpp\n#include \"LoraEncoder.h\"\n\nbyte buffer[4];\nLoraEncoder encoder(buffer);\nencoder.writeRawFloat(99.99);\n// buffer == {0xe1, 0xfa, 0xc7, 0x42}\n```\nand then in the TTN frontend, use the following method:\n\n```javascript\nrawfloat(input.bytes.slice(x, x + 4)) // 99.99\n```\n\n### Bitmap (1 byte)\nSerializes/deserializes a bitmap containing between 0 and 8 different flags.\n\n```cpp\n#include \"LoraEncoder.h\"\n\nbyte buffer[1];\nLoraEncoder encoder(buffer);\nencoder.writeBitmap(true, false, false, false, false, false, false, false);\n// buffer == {0x80}\n```\nand then in the TTN frontend, use the following method:\n\n```javascript\nbitmap(input.bytes.slice(x, x + 1)) // { a: true, b: false, c: false, d: false, e: false, f: false, g: false, h: false }\n```\n\n## Composition\n\n### On the Arduino side\nThe decoder allows you to write more than one value to a byte array:\n```cpp\n#include \"LoraEncoder.h\"\n\nbyte buffer[19];\nLoraEncoder encoder(buffer);\n\nencoder.writeUnixtime(1467632413);\nencoder.writeLatLng(-33.905052, 151.26641);\nencoder.writeUint8(10);\nencoder.writeUint16(23453);\nencoder.writeUint32(2864434397);\nencoder.writeTemperature(80.12);\nencoder.writeHumidity(99.99);\nencoder.writeRawFloat(99.99);\nencoder.writeBitmap(true, false, false, false, false, false, false, false);\n/* buffer == {\n    0x1d, 0x4b, 0x7a, 0x57, // Unixtime\n    0x64, 0xa6, 0xfa, 0xfd, 0x6a, 0x24, 0x04, 0x09, // latitude,longitude\n    0x0A, // Uint8\n    0x9d, 0x5b, // Uint16\n    0xdd, 0xcc, 0xbb, 0xaa, // Uint32\n    0x1f, 0x4c, // temperature\n    0x0f, 0x27, // humidity\n    0xe1, 0xfa, 0xc7, 0x42, // 4-byte float\n    0x80 // bitmap\n}\n*/\n```\n\n#### Convenience class `LoraMessage`\nThere is a convenience class that represents a LoraMessage that you can add readings to:\n```cpp\n#include \"LoraMessage.h\"\n\nLoraMessage message;\n\nmessage\n    .addUnixtime(1467632413)\n    .addLatLng(-33.905052, 151.26641)\n    .addUint8(10)\n    .addUint16(23453)\n    .addUint32(2864434397)\n    .addTemperature(80.12)\n    .addHumidity(99.99)\n    .addRawFloat(99.99)\n    .addBitmap(false, false, false, false, false, false, true, false);\n\nsend(message.getBytes(), message.getLength());\n/*\ngetBytes() == {\n    0x1d, 0x4b, 0x7a, 0x57, // Unixtime\n    0x64, 0xa6, 0xfa, 0xfd, 0x6a, 0x24, 0x04, 0x09, // latitude,longitude\n    0x0A, // Uint8\n    0x9d, 0x5b, // Uint16\n    0xdd, 0xcc, 0xbb, 0xaa, // Uint32\n    0x1f, 0x4c, // temperature\n    0x0f, 0x27, // humidity\n    0xe1, 0xfa, 0xc7, 0x42, // 4-byte float\n    0xfd // Bitmap\n}\nand\ngetLength() == 28\n*/\n```\n\n### Composition in the TTN decoder frontend with the `decode` method\n\nThe `decode` method allows you to specify a mask for the incoming byte buffer (that was generated by this library) and apply decoding functions accordingly.\n\n```javascript\ndecode(byte Array, mask Array [,mapping Array])\n```\n\n#### Example\nPaste everything from `src/decoder.js` into the decoder method and use like this:\n\n```javascript\nfunction (bytes) {\n    // code from src/decoder.js here\n    return decode(bytes, [latLng, unixtime], ['coords', 'time']);\n}\n```\nThis maps the incoming byte buffer of 12 bytes to a sequence of one `latLng` (8 bytes) and one `unixtime` (4 bytes) sequence and maps the first one to a key `coords` and the second to a key `time`.\n\nYou can use: `64 A6 FA FD 6A 24 04 09 1D 4B 7A 57` for testing, and it will result in:\n\n```json\n{\n  \"coords\": [\n    -33.905052,\n    151.26641\n  ],\n  \"time\": 1467632413\n}\n```\n##### Example decoder in the TTN console\nSet up your decoder in the console:\n![TTN console decoder example](https://cloud.githubusercontent.com/assets/188038/23703580/c136cc90-0454-11e7-9570-ae137136d7b5.png)\n\n##### Example converter in the TTN console\nThe decode method already does most of the necessary transformations, so in most cases you can just pass the data through:\n![TTN console converter example](https://cloud.githubusercontent.com/assets/188038/23703587/c99021c0-0454-11e7-8670-9f77472a111d.png)\n\n## Development\n\n* Install the dependencies via `yarn`\n* Run the unit tests (C) via `yarn run test:c`\n* Run the unit tests (JavaScript) via `yarn test`\n* Check the coverage (JavaScript) via `yarn coverage` (see `coverage/lcov-report`)\n\nThe CI will kick off once you create a pull request automatically.\n\n# Installation\n\n## ESP-IDF\n\nAdd the `lora-serialization` dependency to the `main/idf_component.yml` file in your ESP-IDF project:\n\n```\ndependencies:\n  lora-serialization:\n    git: https://github.com/thesolarnomad/lora-serialization.git\n```\n\nAs well as to the `main/CMakeLists.txt`\n\n```\nidf_component_register( ...\n                        REQUIRES .... lora-serialization\n                      )\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthesolarnomad%2Flora-serialization","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fthesolarnomad%2Flora-serialization","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthesolarnomad%2Flora-serialization/lists"}