From a659168fc3d105448eeb183be6cb210c6b9704e7 Mon Sep 17 00:00:00 2001 From: Klaus K Wilting Date: Sun, 17 Jun 2018 22:41:32 +0200 Subject: [PATCH] new payload converter TTNpacked --- README.md | 42 +++--- platformio.ini | 7 +- ...lized_converter.js => packed_converter.js} | 2 +- ...erialized_decoder.js => packed_decoder.js} | 2 +- src/globals.h | 2 +- src/main.cpp | 4 +- src/paxcounter.conf | 8 +- src/payload.cpp | 134 +++++++++++++----- src/payload.h | 17 ++- 9 files changed, 145 insertions(+), 73 deletions(-) rename src/TTN/{serialized_converter.js => packed_converter.js} (82%) rename src/TTN/{serialized_decoder.js => packed_decoder.js} (98%) diff --git a/README.md b/README.md index dccacae9..954676c9 100644 --- a/README.md +++ b/README.md @@ -20,23 +20,25 @@ This can all be done with a single small and cheap ESP32 board for less than $20 # Hardware Supported ESP32 based LoRa IoT boards: -- **Heltec LoRa-32** a) -- **TTGOv1** a) -- **TTGOv2** a,d) -- **TTGOv2.1** a),e) -- **TTGO T-Beam** d),e),f) -- **Pycom LoPy** b),f)* -- **Pycom LoPy4** b),f)* -- **Pycom FiPy** b),f)* -- **LoLin32** with [LoraNode32 shield](https://github.com/hallard/LoLin32-Lora) b),c) -- **LoLin32 Lite** with [LoraNode32-Lite shield](https://github.com/hallard/LoLin32-Lite-Lora) b),c) +- **Heltec LoRa-32** +- **TTGOv1** +- **TTGOv2** +- **TTGOv2.1** +- **TTGO T-Beam** +- **Pycom LoPy** +- **Pycom LoPy4** +- **Pycom FiPy** +- **LoLin32** + [LoraNode32 shield](https://github.com/hallard/LoLin32-Lora) +- **LoLin32 Lite** + [LoraNode32-Lite shield](https://github.com/hallard/LoLin32-Lite-Lora) -a) on board OLED Display supported; -b) on board RGB LED supported; -c) on board Hardware unique DEVEUI supported; -d) external wiring needed, see instructions in file /hal/.h; -e) battery voltage monitoring supported; -f) on board GPS supported, *for Pycom devices with additional PyTrack board +Depending on board hardware following features are supported: +- LED +- OLED Display +- RGB LED +- button +- silicon unique ID +- battery voltage monitoring +- GPS Target platform must be selected in [platformio.ini](https://github.com/cyberman54/ESP32-Paxcounter/blob/master/platformio.ini).
Hardware dependent settings (pinout etc.) are stored in board files in /hal directory.
@@ -106,7 +108,13 @@ Legend for RGB LED (LoPy/LoPy4/FiPy/Lolin32 only): # Payload -All data is represented in big-endian-format, as long not otherwise stated. +You can select between different payload formats in [paxcounter.conf](src/paxounter.conf#L40): + +*Plain* generates human readable json fields, e.g. useful for TTN console +*Packed* generates packed json fiels, e.g. useful for own backends +*[CayenneLPP]*(https://mydevices.com/cayenne/docs/lora/#lora-cayenne-low-power-payload-reference-implementation) + +Hereafter described is the *Plain* format. All data is represented in big-endian-format. **LoRaWAN Port #1:** diff --git a/platformio.ini b/platformio.ini index b2181971..459696d9 100644 --- a/platformio.ini +++ b/platformio.ini @@ -11,11 +11,11 @@ ; ---> SELECT TARGET PLATFORM HERE! <--- [platformio] -env_default = heltec +;env_default = heltec ;env_default = ttgov1 ;env_default = ttgov2 ;env_default = ttgov21 -;env_default = ttgobeam +env_default = ttgobeam ;env_default = lopy ;env_default = lopy4 ;env_default = fipy @@ -27,8 +27,7 @@ description = Paxcounter is a proof-of-concept ESP32 device for metering passeng [common_env_data] platform_espressif32 = espressif32@>=1.0.2 board_build.partitions = no_ota.csv -lib_deps_all = - LoRa Serialization@>=3.0.0 +;lib_deps_all = lib_deps_display = U8g2@>=2.22.14 lib_deps_rgbled = diff --git a/src/TTN/serialized_converter.js b/src/TTN/packed_converter.js similarity index 82% rename from src/TTN/serialized_converter.js rename to src/TTN/packed_converter.js index d9f3a79a..7c35ad41 100644 --- a/src/TTN/serialized_converter.js +++ b/src/TTN/packed_converter.js @@ -1,4 +1,4 @@ -// Converter for device payload encoder "SERIALIZED" +// Converter for device payload encoder "PACKED" // copy&paste to TTN Console -> Applications -> PayloadFormat -> Converter function Converter(decoded, port) { diff --git a/src/TTN/serialized_decoder.js b/src/TTN/packed_decoder.js similarity index 98% rename from src/TTN/serialized_decoder.js rename to src/TTN/packed_decoder.js index d928f031..f7e06f52 100644 --- a/src/TTN/serialized_decoder.js +++ b/src/TTN/packed_decoder.js @@ -1,4 +1,4 @@ -// Decoder for device payload encoder "SERIALIZED" +// Decoder for device payload encoder "PACKED" // copy&paste to TTN Console -> Applications -> PayloadFormat -> Decoder function Decoder(bytes, port) { diff --git a/src/globals.h b/src/globals.h index a0473663..21c7b1a8 100644 --- a/src/globals.h +++ b/src/globals.h @@ -49,7 +49,7 @@ extern TinyGPSPlus gps; // Make TinyGPS++ instance globally availabe #if PAYLOAD_ENCODER == 1 extern TTNplain payload; #elif PAYLOAD_ENCODER == 2 -extern TTNserialized payload; +extern TTNpacked payload; #elif PAYLOAD_ENCODER == 3 extern CayenneLPP payload; #else diff --git a/src/main.cpp b/src/main.cpp index ac1d5684..4808e7f7 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -74,7 +74,7 @@ std::set macs; // associative container holds total of unique MAC #if PAYLOAD_ENCODER == 1 TTNplain payload(PAYLOAD_BUFFER_SIZE); #elif PAYLOAD_ENCODER == 2 -TTNserialized payload(PAYLOAD_BUFFER_SIZE); +TTNpacked payload(PAYLOAD_BUFFER_SIZE); #elif PAYLOAD_ENCODER == 3 CayenneLPP payload(PAYLOAD_BUFFER_SIZE); #else @@ -594,7 +594,7 @@ void setup() { #if PAYLOAD_ENCODER == 1 strcat_P(features, " PAYLOAD_PLAIN"); #elif PAYLOAD_ENCODER == 2 - strcat_P(features, " PAYLOAD_SERIALIZED"); + strcat_P(features, " PAYLOAD_PACKED"); #elif PAYLOAD_ENCODER == 3 strcat_P(features, " PAYLOAD_CAYENNE"); #endif diff --git a/src/paxcounter.conf b/src/paxcounter.conf index 0e83789e..9c762a19 100644 --- a/src/paxcounter.conf +++ b/src/paxcounter.conf @@ -36,12 +36,12 @@ #define WIFI_MY_COUNTRY "EU" // select locale for Wifi RF settings #define WIFI_CHANNEL_SWITCH_INTERVAL 50 // [seconds/100] -> 0,5 sec. -// LoRa payload send cycle --> take care of duty cycle of LoRaWAN network! <-- -#define SEND_SECS 120 // [seconds/2] -> 240 sec. -//#define SEND_SECS 30 // [seconds/2] -> 60 sec. +// LoRa payload parameters +#define PAYLOAD_ENCODER 2 // select payload encoder: 1=Plain [default], 2=Packed, 3=CayenneLPP +//#define SEND_SECS 120 // payload send cycle [seconds/2] -> 240 sec. +#define SEND_SECS 30 // payload send cycle [seconds/2] -> 60 sec. #define MEM_LOW 2048 // [Bytes] low memory threshold triggering a send cycle #define RETRANSMIT_RCMD 5 // [seconds] wait time before retransmitting rcommand results -#define PAYLOAD_ENCODER 1 // select payload encoder: 1 = Plain [default], 2 = Lora-serialized, 3 = CayenneLPP #define PAYLOAD_BUFFER_SIZE 51 // maximum size of payload block per transmit // Default LoRa Spreadfactor diff --git a/src/payload.cpp b/src/payload.cpp index 54e32398..97d52e66 100644 --- a/src/payload.cpp +++ b/src/payload.cpp @@ -80,58 +80,116 @@ void TTNplain::addStatus(uint16_t voltage, uint64_t uptime, float cputemp) { } /* ---------------- packed format with LoRa serialization Encoder ---------- */ +// derived from +// https://github.com/thesolarnomad/lora-serialization/blob/master/src/LoraEncoder.cpp -TTNserialized::TTNserialized(uint8_t size) { - buffer = (uint8_t *)malloc(size); - //LoraEncoder message(buffer); -} +TTNpacked::TTNpacked(uint8_t size) { buffer = (uint8_t *)malloc(size); } -TTNserialized::~TTNserialized(void) { free(buffer); } +TTNpacked::~TTNpacked(void) { free(buffer); } -void TTNserialized::reset(void) { -} // buggy! to be done, we need to clear the buffer here, but how? +void TTNpacked::reset(void) { buffer = 0; } -uint8_t TTNserialized::getSize(void) { return sizeof(buffer); } +uint8_t TTNpacked::getSize(void) { return sizeof(buffer); } -uint8_t *TTNserialized::getBuffer(void) { return buffer; } +uint8_t *TTNpacked::getBuffer(void) { return buffer; } -void TTNserialized::addCount(uint16_t value1, uint16_t value2) { - LoraEncoder message(buffer); - message.writeUint16(value1); - message.writeUint16(value2); +void TTNpacked::addCount(uint16_t value1, uint16_t value2) { + writeUint16(value1); + writeUint16(value2); } #ifdef HAS_GPS -void TTNserialized::addGPS(gpsStatus_t value) { - LoraEncoder message(buffer); - message.writeLatLng(value.latitude, value.longitude); - message.writeUint8(value.satellites); - message.writeUint16(value.hdop); - message.writeUint16(value.altitude); +void TTNpacked::addGPS(gpsStatus_t value) { + writeLatLng(value.latitude, value.longitude); + writeUint8(value.satellites); + writeUint16(value.hdop); + writeUint16(value.altitude); } #endif -void TTNserialized::addConfig(configData_t value) { - LoraEncoder message(buffer); - message.writeUint8(value.lorasf); - message.writeUint16(value.rssilimit); - message.writeUint8(value.sendcycle); - message.writeUint8(value.wifichancycle); - message.writeUint8(value.blescantime); - message.writeUint8(value.rgblum); - message.writeBitmap( - value.adrmode ? true : false, value.screensaver ? true : false, - value.screenon ? true : false, value.countermode ? true : false, - value.blescan ? true : false, value.wifiant ? true : false, - value.vendorfilter ? true : false, value.gpsmode ? true : false); +void TTNpacked::addConfig(configData_t value) { + writeUint8(value.lorasf); + writeUint16(value.rssilimit); + writeUint8(value.sendcycle); + writeUint8(value.wifichancycle); + writeUint8(value.blescantime); + writeUint8(value.rgblum); + writeBitmap(value.adrmode ? true : false, value.screensaver ? true : false, + value.screenon ? true : false, value.countermode ? true : false, + value.blescan ? true : false, value.wifiant ? true : false, + value.vendorfilter ? true : false, value.gpsmode ? true : false); } -void TTNserialized::addStatus(uint16_t voltage, uint64_t uptime, - float cputemp) { - LoraEncoder message(buffer); - message.writeUint16(voltage); - message.writeUnixtime(uptime); - message.writeTemperature(cputemp); +void TTNpacked::addStatus(uint16_t voltage, uint64_t uptime, float cputemp) { + writeUint16(voltage); + writeUnixtime(uptime); + writeTemperature(cputemp); +} + +void TTNpacked::_intToBytes(byte *buf, int32_t i, uint8_t byteSize) { + for (uint8_t x = 0; x < byteSize; x++) { + buf[x] = (byte)(i >> (x * 8)); + } +} + +void TTNpacked::writeUnixtime(uint32_t unixtime) { + _intToBytes(buffer, unixtime, 4); + buffer += 4; +} + +void TTNpacked::writeLatLng(double latitude, double longitude) { + int32_t lat = latitude * 1e6; + int32_t lng = longitude * 1e6; + + _intToBytes(buffer, lat, 4); + _intToBytes(buffer + 4, lng, 4); + buffer += 8; +} + +void TTNpacked::writeUint16(uint16_t i) { + _intToBytes(buffer, i, 2); + buffer += 2; +} + +void TTNpacked::writeUint8(uint8_t i) { + _intToBytes(buffer, i, 1); + buffer += 1; +} + +void TTNpacked::writeHumidity(float humidity) { + int16_t h = (int16_t)(humidity * 100); + _intToBytes(buffer, h, 2); + buffer += 2; +} + +/** + * Uses a 16bit two's complement with two decimals, so the range is + * -327.68 to +327.67 degrees + */ +void TTNpacked::writeTemperature(float temperature) { + int16_t t = (int16_t)(temperature * 100); + if (temperature < 0) { + t = ~-t; + t = t + 1; + } + buffer[0] = (byte)((t >> 8) & 0xFF); + buffer[1] = (byte)t & 0xFF; + buffer += 2; +} + +void TTNpacked::writeBitmap(bool a, bool b, bool c, bool d, bool e, bool f, + bool g, bool h) { + uint8_t bitmap = 0; + // LSB first + bitmap |= (a & 1) << 7; + bitmap |= (b & 1) << 6; + bitmap |= (c & 1) << 5; + bitmap |= (d & 1) << 4; + bitmap |= (e & 1) << 3; + bitmap |= (f & 1) << 2; + bitmap |= (g & 1) << 1; + bitmap |= (h & 1) << 0; + writeUint8(bitmap); } /* ---------------- Cayenne LPP format ---------- */ diff --git a/src/payload.h b/src/payload.h index 13d3b8e1..8899c709 100644 --- a/src/payload.h +++ b/src/payload.h @@ -3,7 +3,6 @@ #define _PAYLOAD_H_ #include -#include "LoraEncoder.h" // MyDevices CayenneLPP channels #define LPP_GPS_CHANNEL 20 @@ -42,10 +41,10 @@ private: uint8_t cursor; }; -class TTNserialized { +class TTNpacked { public: - TTNserialized(uint8_t size); - ~TTNserialized(); + TTNpacked(uint8_t size); + ~TTNpacked(); void reset(void); uint8_t getSize(void); @@ -60,7 +59,15 @@ public: private: uint8_t *buffer; - LoraEncoder message(byte *buffer); + void _intToBytes(byte *buf, int32_t i, uint8_t byteSize); + void writeUnixtime(uint32_t unixtime); + void writeLatLng(double latitude, double longitude); + void writeUint16(uint16_t i); + void writeUint8(uint8_t i); + void writeHumidity(float humidity); + void writeTemperature(float temperature); + void writeBitmap(bool a, bool b, bool c, bool d, bool e, bool f, bool g, + bool h); }; class CayenneLPP {