Firmware for GPS-tracked PM10 sensors on opensensemap.org
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

307 lines
8.0 KiB

  1. # LoRaWAN serialization/deserialization library for The Things Network
  2. [![Build Status](https://travis-ci.org/thesolarnomad/lora-serialization.svg?branch=master)](https://travis-ci.org/thesolarnomad/lora-serialization)
  3. [![Coverage Status](https://coveralls.io/repos/github/thesolarnomad/lora-serialization/badge.svg?branch=master)](https://coveralls.io/github/thesolarnomad/lora-serialization?branch=master)
  4. [![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)
  5. This 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.
  6. Since version 2.2.0 there is also an encoder for the TTN side.
  7. ## In short
  8. ### Encoding on Arduino, decoding in TTN
  9. Arduino side:
  10. ```cpp
  11. #include "LoraMessage.h"
  12. LoraMessage message;
  13. message
  14. .addUnixtime(1467632413)
  15. .addLatLng(-33.905052, 151.26641);
  16. lora_send_bytes(message.getBytes(), message.getLength());
  17. delete message;
  18. ```
  19. TTN side:
  20. ```javascript
  21. // include src/decoder.js
  22. var json = decode(bytes, [unixtime, latLng], ['time', 'coords']);
  23. // json == {time: unixtime, coords: [latitude, longitude]}
  24. ```
  25. ### Encoding in TTN
  26. TTN side:
  27. ```javascript
  28. // include src/encoder.js
  29. var bytes = encode([timestamp, [latitude, longitude]], [unixtime, latLng]);
  30. // bytes is of type Buffer
  31. ```
  32. #### With the convenience class
  33. ```javascript
  34. // include src/encoder.js
  35. // include src/LoraMessage.js
  36. var bytes = new LoraMessage(encoder)
  37. .addUnixtime(1467632413)
  38. .addLatLng(-33.905052, 151.26641)
  39. .addBitmap(true, true, false, true)
  40. .getBytes();
  41. // bytes = <Buffer 1d 4b 7a 57 64 a6 fa fd 6a 24 04 09 d0>
  42. ```
  43. and then decoding as usual:
  44. ```js
  45. var result = decoder.decode(
  46. bytes,
  47. [decoder.unixtime, decoder.latLng, decoder.bitmap],
  48. ['time', 'coords', 'heaters']
  49. );
  50. // result =
  51. // { time: 1467632413,
  52. // coords: [ -33.905052, 151.26641 ],
  53. // heaters:
  54. // { a: true,
  55. // b: true,
  56. // c: false,
  57. // d: true,
  58. // e: false,
  59. // f: false,
  60. // g: false,
  61. // h: false } }
  62. ```
  63. ## General Usage
  64. ### Unix time (4 bytes)
  65. Serializes/deserializes a unix time (seconds)
  66. ```cpp
  67. #include "LoraEncoder.h"
  68. byte buffer[4];
  69. LoraEncoder encoder(buffer);
  70. encoder.writeUnixtime(1467632413);
  71. // buffer == {0x1d, 0x4b, 0x7a, 0x57}
  72. ```
  73. and then in the TTN frontend, use the following method:
  74. ```javascript
  75. unixtime(bytes.slice(x, x + 4)) // 1467632413
  76. ```
  77. ### GPS coordinates (8 bytes)
  78. Serializes/deserializes coordinates (latitude/longitude) with a precision of 6 decimals.
  79. ```cpp
  80. #include "LoraEncoder.h"
  81. byte buffer[8];
  82. LoraEncoder encoder(buffer);
  83. encoder.writeLatLng(-33.905052, 151.26641);
  84. // buffer == {0x64, 0xa6, 0xfa, 0xfd, 0x6a, 0x24, 0x04, 0x09}
  85. ```
  86. and then in the TTN frontend, use the following method:
  87. ```javascript
  88. latLng(bytes.slice(x, x + 8)) // [-33.905052, 151.26641]
  89. ```
  90. ### Unsigned 8bit Integer (1 byte)
  91. Serializes/deserializes an unsigned 8bit integer.
  92. ```cpp
  93. #include "LoraEncoder.h"
  94. byte buffer[1];
  95. LoraEncoder encoder(buffer);
  96. uint8_t i = 10;
  97. encoder.writeUint8(i);
  98. // buffer == {0x0A}
  99. ```
  100. and then in the TTN frontend, use the following method:
  101. ```javascript
  102. uint8(bytes.slice(x, x + 1)) // 10
  103. ```
  104. ### Unsigned 16bit Integer (2 bytes)
  105. Serializes/deserializes an unsigned 16bit integer.
  106. ```cpp
  107. #include "LoraEncoder.h"
  108. byte buffer[2];
  109. LoraEncoder encoder(buffer);
  110. uint16_t i = 23453;
  111. encoder.writeUint16(i);
  112. // buffer == {0x9d, 0x5b}
  113. ```
  114. and then in the TTN frontend, use the following method:
  115. ```javascript
  116. uint16(bytes.slice(x, x + 2)) // 23453
  117. ```
  118. ### Temperature (2 bytes)
  119. Serializes/deserializes a temperature reading between -327.68 and +327.67 (inclusive) with a precision of 2 decimals.
  120. ```cpp
  121. #include "LoraEncoder.h"
  122. byte buffer[2];
  123. LoraEncoder encoder(buffer);
  124. encoder.writeTemperature(-123.45);
  125. // buffer == {0xcf, 0xc7}
  126. ```
  127. and then in the TTN frontend, use the following method:
  128. ```javascript
  129. temperature(bytes.slice(x, x + 2)) // -123.45
  130. ```
  131. ### Humidity (2 bytes)
  132. Serializes/deserializes a humidity reading between 0 and 100 (inclusive) with a precision of 2 decimals.
  133. ```cpp
  134. #include "LoraEncoder.h"
  135. byte buffer[2];
  136. LoraEncoder encoder(buffer);
  137. encoder.writeHumidity(99.99);
  138. // buffer == {0x0f, 0x27}
  139. ```
  140. and then in the TTN frontend, use the following method:
  141. ```javascript
  142. humidity(bytes.slice(x, x + 2)) // 99.99
  143. ```
  144. ### Bitmap (1 byte)
  145. Serializes/deserializes a bitmap containing between 0 and 8 different flags.
  146. ```cpp
  147. #include "LoraEncoder.h"
  148. byte buffer[1];
  149. LoraEncoder encoder(buffer);
  150. encoder.writeBitmap(true, false, false, false, false, false, false, false);
  151. // buffer == {0x80}
  152. ```
  153. and then in the TTN frontend, use the following method:
  154. ```javascript
  155. bitmap(bytes.slice(x, x + 1)) // { a: true, b: false, c: false, d: false, e: false, f: false, g: false, h: false }
  156. ```
  157. ## Composition
  158. ### On the Arduino side
  159. The decoder allows you to write more than one value to a byte array:
  160. ```cpp
  161. #include "LoraEncoder.h"
  162. byte buffer[19];
  163. LoraEncoder encoder(buffer);
  164. encoder.writeUnixtime(1467632413);
  165. encoder.writeLatLng(-33.905052, 151.26641);
  166. encoder.writeUint8(10);
  167. encoder.writeUint16(23453);
  168. encoder.writeTemperature(80.12);
  169. encoder.writeHumidity(99.99);
  170. encoder.writeBitmap(true, false, false, false, false, false, false, false);
  171. /* buffer == {
  172. 0x1d, 0x4b, 0x7a, 0x57, // Unixtime
  173. 0x64, 0xa6, 0xfa, 0xfd, 0x6a, 0x24, 0x04, 0x09, // latitude,longitude
  174. 0x0A, // Uint8
  175. 0x9d, 0x5b, // Uint16
  176. 0x1f, 0x4c, // temperature
  177. 0x0f, 0x27, // humidity
  178. 0x80 // bitmap
  179. }
  180. */
  181. ```
  182. #### Convenience class `LoraMessage`
  183. There is a convenience class that represents a LoraMessage that you can add readings to:
  184. ```cpp
  185. #include "LoraMessage.h"
  186. LoraMessage message;
  187. message
  188. .addUnixtime(1467632413)
  189. .addLatLng(-33.905052, 151.26641)
  190. .addUint8(10)
  191. .addUint16(23453)
  192. .addTemperature(80.12)
  193. .addHumidity(99.99)
  194. .addBitmap(false, false, false, false, false, false, true, false);
  195. send(message.getBytes(), message.getLength());
  196. /*
  197. getBytes() == {
  198. 0x1d, 0x4b, 0x7a, 0x57, // Unixtime
  199. 0x64, 0xa6, 0xfa, 0xfd, 0x6a, 0x24, 0x04, 0x09, // latitude,longitude
  200. 0x0A, // Uint8
  201. 0x9d, 0x5b, // Uint16
  202. 0x1f, 0x4c, // temperature
  203. 0x0f, 0x27, // humidity
  204. 0xfd // Bitmap
  205. }
  206. and
  207. getLength() == 20
  208. */
  209. ```
  210. ### Composition in the TTN decoder frontend with the `decode` method
  211. The `decode` method allows you to specify a mask for the incoming byte buffer (that was generated by this library) and apply decoding functions accordingly.
  212. ```javascript
  213. decode(byte Array, mask Array [,mapping Array])
  214. ```
  215. #### Example
  216. Paste everything from `src/decoder.js` into the decoder method and use like this:
  217. ```javascript
  218. function (bytes) {
  219. // code from src/decoder.js here
  220. return decode(bytes, [latLng, unixtime], ['coords', 'time']);
  221. }
  222. ```
  223. This 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`.
  224. You can use: `64 A6 FA FD 6A 24 04 09 1D 4B 7A 57` for testing, and it will result in:
  225. ```json
  226. {
  227. "coords": [
  228. -33.905052,
  229. 151.26641
  230. ],
  231. "time": 1467632413
  232. }
  233. ```
  234. ##### Example decoder in the TTN console
  235. Set up your decoder in the console:
  236. ![TTN console decoder example](https://cloud.githubusercontent.com/assets/188038/23703580/c136cc90-0454-11e7-9570-ae137136d7b5.png)
  237. ##### Example converter in the TTN console
  238. The decode method already does most of the necessary transformations, so in most cases you can just pass the data through:
  239. ![TTN console converter example](https://cloud.githubusercontent.com/assets/188038/23703587/c99021c0-0454-11e7-8670-9f77472a111d.png)
  240. ## Development
  241. * Install the dependencies via `yarn`
  242. * Run the unit tests (C) via `yarn run test:c`
  243. * Run the unit tests (JavaScript) via `yarn test`
  244. * Check the coverage (JavaScript) via `yarn coverage` (see `coverage/lcov-report`)
  245. The CI will kick off once you create a pull request automatically.