new payload converter TTNpacked
This commit is contained in:
parent
706f90453f
commit
a659168fc3
42
README.md
42
README.md
@ -20,23 +20,25 @@ This can all be done with a single small and cheap ESP32 board for less than $20
|
|||||||
# Hardware
|
# Hardware
|
||||||
|
|
||||||
Supported ESP32 based LoRa IoT boards:
|
Supported ESP32 based LoRa IoT boards:
|
||||||
- **Heltec LoRa-32** a)
|
- **Heltec LoRa-32**
|
||||||
- **TTGOv1** a)
|
- **TTGOv1**
|
||||||
- **TTGOv2** a,d)
|
- **TTGOv2**
|
||||||
- **TTGOv2.1** a),e)
|
- **TTGOv2.1**
|
||||||
- **TTGO T-Beam** d),e),f)
|
- **TTGO T-Beam**
|
||||||
- **Pycom LoPy** b),f)*
|
- **Pycom LoPy**
|
||||||
- **Pycom LoPy4** b),f)*
|
- **Pycom LoPy4**
|
||||||
- **Pycom FiPy** b),f)*
|
- **Pycom FiPy**
|
||||||
- **LoLin32** with [LoraNode32 shield](https://github.com/hallard/LoLin32-Lora) b),c)
|
- **LoLin32** + [LoraNode32 shield](https://github.com/hallard/LoLin32-Lora)
|
||||||
- **LoLin32 Lite** with [LoraNode32-Lite shield](https://github.com/hallard/LoLin32-Lite-Lora) b),c)
|
- **LoLin32 Lite** + [LoraNode32-Lite shield](https://github.com/hallard/LoLin32-Lite-Lora)
|
||||||
|
|
||||||
a) on board OLED Display supported;
|
Depending on board hardware following features are supported:
|
||||||
b) on board RGB LED supported;
|
- LED
|
||||||
c) on board Hardware unique DEVEUI supported;
|
- OLED Display
|
||||||
d) external wiring needed, see instructions in file /hal/<board>.h;
|
- RGB LED
|
||||||
e) battery voltage monitoring supported;
|
- button
|
||||||
f) on board GPS supported, *for Pycom devices with additional PyTrack board
|
- 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).<br>
|
Target platform must be selected in [platformio.ini](https://github.com/cyberman54/ESP32-Paxcounter/blob/master/platformio.ini).<br>
|
||||||
Hardware dependent settings (pinout etc.) are stored in board files in /hal directory.<br>
|
Hardware dependent settings (pinout etc.) are stored in board files in /hal directory.<br>
|
||||||
@ -106,7 +108,13 @@ Legend for RGB LED (LoPy/LoPy4/FiPy/Lolin32 only):
|
|||||||
|
|
||||||
# Payload
|
# 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:**
|
**LoRaWAN Port #1:**
|
||||||
|
|
||||||
|
@ -11,11 +11,11 @@
|
|||||||
|
|
||||||
; ---> SELECT TARGET PLATFORM HERE! <---
|
; ---> SELECT TARGET PLATFORM HERE! <---
|
||||||
[platformio]
|
[platformio]
|
||||||
env_default = heltec
|
;env_default = heltec
|
||||||
;env_default = ttgov1
|
;env_default = ttgov1
|
||||||
;env_default = ttgov2
|
;env_default = ttgov2
|
||||||
;env_default = ttgov21
|
;env_default = ttgov21
|
||||||
;env_default = ttgobeam
|
env_default = ttgobeam
|
||||||
;env_default = lopy
|
;env_default = lopy
|
||||||
;env_default = lopy4
|
;env_default = lopy4
|
||||||
;env_default = fipy
|
;env_default = fipy
|
||||||
@ -27,8 +27,7 @@ description = Paxcounter is a proof-of-concept ESP32 device for metering passeng
|
|||||||
[common_env_data]
|
[common_env_data]
|
||||||
platform_espressif32 = espressif32@>=1.0.2
|
platform_espressif32 = espressif32@>=1.0.2
|
||||||
board_build.partitions = no_ota.csv
|
board_build.partitions = no_ota.csv
|
||||||
lib_deps_all =
|
;lib_deps_all =
|
||||||
LoRa Serialization@>=3.0.0
|
|
||||||
lib_deps_display =
|
lib_deps_display =
|
||||||
U8g2@>=2.22.14
|
U8g2@>=2.22.14
|
||||||
lib_deps_rgbled =
|
lib_deps_rgbled =
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Converter for device payload encoder "SERIALIZED"
|
// Converter for device payload encoder "PACKED"
|
||||||
// copy&paste to TTN Console -> Applications -> PayloadFormat -> Converter
|
// copy&paste to TTN Console -> Applications -> PayloadFormat -> Converter
|
||||||
|
|
||||||
function Converter(decoded, port) {
|
function Converter(decoded, port) {
|
@ -1,4 +1,4 @@
|
|||||||
// Decoder for device payload encoder "SERIALIZED"
|
// Decoder for device payload encoder "PACKED"
|
||||||
// copy&paste to TTN Console -> Applications -> PayloadFormat -> Decoder
|
// copy&paste to TTN Console -> Applications -> PayloadFormat -> Decoder
|
||||||
|
|
||||||
function Decoder(bytes, port) {
|
function Decoder(bytes, port) {
|
@ -49,7 +49,7 @@ extern TinyGPSPlus gps; // Make TinyGPS++ instance globally availabe
|
|||||||
#if PAYLOAD_ENCODER == 1
|
#if PAYLOAD_ENCODER == 1
|
||||||
extern TTNplain payload;
|
extern TTNplain payload;
|
||||||
#elif PAYLOAD_ENCODER == 2
|
#elif PAYLOAD_ENCODER == 2
|
||||||
extern TTNserialized payload;
|
extern TTNpacked payload;
|
||||||
#elif PAYLOAD_ENCODER == 3
|
#elif PAYLOAD_ENCODER == 3
|
||||||
extern CayenneLPP payload;
|
extern CayenneLPP payload;
|
||||||
#else
|
#else
|
||||||
|
@ -74,7 +74,7 @@ std::set<uint16_t> macs; // associative container holds total of unique MAC
|
|||||||
#if PAYLOAD_ENCODER == 1
|
#if PAYLOAD_ENCODER == 1
|
||||||
TTNplain payload(PAYLOAD_BUFFER_SIZE);
|
TTNplain payload(PAYLOAD_BUFFER_SIZE);
|
||||||
#elif PAYLOAD_ENCODER == 2
|
#elif PAYLOAD_ENCODER == 2
|
||||||
TTNserialized payload(PAYLOAD_BUFFER_SIZE);
|
TTNpacked payload(PAYLOAD_BUFFER_SIZE);
|
||||||
#elif PAYLOAD_ENCODER == 3
|
#elif PAYLOAD_ENCODER == 3
|
||||||
CayenneLPP payload(PAYLOAD_BUFFER_SIZE);
|
CayenneLPP payload(PAYLOAD_BUFFER_SIZE);
|
||||||
#else
|
#else
|
||||||
@ -594,7 +594,7 @@ void setup() {
|
|||||||
#if PAYLOAD_ENCODER == 1
|
#if PAYLOAD_ENCODER == 1
|
||||||
strcat_P(features, " PAYLOAD_PLAIN");
|
strcat_P(features, " PAYLOAD_PLAIN");
|
||||||
#elif PAYLOAD_ENCODER == 2
|
#elif PAYLOAD_ENCODER == 2
|
||||||
strcat_P(features, " PAYLOAD_SERIALIZED");
|
strcat_P(features, " PAYLOAD_PACKED");
|
||||||
#elif PAYLOAD_ENCODER == 3
|
#elif PAYLOAD_ENCODER == 3
|
||||||
strcat_P(features, " PAYLOAD_CAYENNE");
|
strcat_P(features, " PAYLOAD_CAYENNE");
|
||||||
#endif
|
#endif
|
||||||
|
@ -36,12 +36,12 @@
|
|||||||
#define WIFI_MY_COUNTRY "EU" // select locale for Wifi RF settings
|
#define WIFI_MY_COUNTRY "EU" // select locale for Wifi RF settings
|
||||||
#define WIFI_CHANNEL_SWITCH_INTERVAL 50 // [seconds/100] -> 0,5 sec.
|
#define WIFI_CHANNEL_SWITCH_INTERVAL 50 // [seconds/100] -> 0,5 sec.
|
||||||
|
|
||||||
// LoRa payload send cycle --> take care of duty cycle of LoRaWAN network! <--
|
// LoRa payload parameters
|
||||||
#define SEND_SECS 120 // [seconds/2] -> 240 sec.
|
#define PAYLOAD_ENCODER 2 // select payload encoder: 1=Plain [default], 2=Packed, 3=CayenneLPP
|
||||||
//#define SEND_SECS 30 // [seconds/2] -> 60 sec.
|
//#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 MEM_LOW 2048 // [Bytes] low memory threshold triggering a send cycle
|
||||||
#define RETRANSMIT_RCMD 5 // [seconds] wait time before retransmitting rcommand results
|
#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
|
#define PAYLOAD_BUFFER_SIZE 51 // maximum size of payload block per transmit
|
||||||
|
|
||||||
// Default LoRa Spreadfactor
|
// Default LoRa Spreadfactor
|
||||||
|
128
src/payload.cpp
128
src/payload.cpp
@ -80,58 +80,116 @@ void TTNplain::addStatus(uint16_t voltage, uint64_t uptime, float cputemp) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* ---------------- packed format with LoRa serialization Encoder ---------- */
|
/* ---------------- packed format with LoRa serialization Encoder ---------- */
|
||||||
|
// derived from
|
||||||
|
// https://github.com/thesolarnomad/lora-serialization/blob/master/src/LoraEncoder.cpp
|
||||||
|
|
||||||
TTNserialized::TTNserialized(uint8_t size) {
|
TTNpacked::TTNpacked(uint8_t size) { buffer = (uint8_t *)malloc(size); }
|
||||||
buffer = (uint8_t *)malloc(size);
|
|
||||||
//LoraEncoder message(buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
TTNserialized::~TTNserialized(void) { free(buffer); }
|
TTNpacked::~TTNpacked(void) { free(buffer); }
|
||||||
|
|
||||||
void TTNserialized::reset(void) {
|
void TTNpacked::reset(void) { buffer = 0; }
|
||||||
} // buggy! to be done, we need to clear the buffer here, but how?
|
|
||||||
|
|
||||||
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) {
|
void TTNpacked::addCount(uint16_t value1, uint16_t value2) {
|
||||||
LoraEncoder message(buffer);
|
writeUint16(value1);
|
||||||
message.writeUint16(value1);
|
writeUint16(value2);
|
||||||
message.writeUint16(value2);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef HAS_GPS
|
#ifdef HAS_GPS
|
||||||
void TTNserialized::addGPS(gpsStatus_t value) {
|
void TTNpacked::addGPS(gpsStatus_t value) {
|
||||||
LoraEncoder message(buffer);
|
writeLatLng(value.latitude, value.longitude);
|
||||||
message.writeLatLng(value.latitude, value.longitude);
|
writeUint8(value.satellites);
|
||||||
message.writeUint8(value.satellites);
|
writeUint16(value.hdop);
|
||||||
message.writeUint16(value.hdop);
|
writeUint16(value.altitude);
|
||||||
message.writeUint16(value.altitude);
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void TTNserialized::addConfig(configData_t value) {
|
void TTNpacked::addConfig(configData_t value) {
|
||||||
LoraEncoder message(buffer);
|
writeUint8(value.lorasf);
|
||||||
message.writeUint8(value.lorasf);
|
writeUint16(value.rssilimit);
|
||||||
message.writeUint16(value.rssilimit);
|
writeUint8(value.sendcycle);
|
||||||
message.writeUint8(value.sendcycle);
|
writeUint8(value.wifichancycle);
|
||||||
message.writeUint8(value.wifichancycle);
|
writeUint8(value.blescantime);
|
||||||
message.writeUint8(value.blescantime);
|
writeUint8(value.rgblum);
|
||||||
message.writeUint8(value.rgblum);
|
writeBitmap(value.adrmode ? true : false, value.screensaver ? true : false,
|
||||||
message.writeBitmap(
|
|
||||||
value.adrmode ? true : false, value.screensaver ? true : false,
|
|
||||||
value.screenon ? true : false, value.countermode ? true : false,
|
value.screenon ? true : false, value.countermode ? true : false,
|
||||||
value.blescan ? true : false, value.wifiant ? true : false,
|
value.blescan ? true : false, value.wifiant ? true : false,
|
||||||
value.vendorfilter ? true : false, value.gpsmode ? true : false);
|
value.vendorfilter ? true : false, value.gpsmode ? true : false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TTNserialized::addStatus(uint16_t voltage, uint64_t uptime,
|
void TTNpacked::addStatus(uint16_t voltage, uint64_t uptime, float cputemp) {
|
||||||
float cputemp) {
|
writeUint16(voltage);
|
||||||
LoraEncoder message(buffer);
|
writeUnixtime(uptime);
|
||||||
message.writeUint16(voltage);
|
writeTemperature(cputemp);
|
||||||
message.writeUnixtime(uptime);
|
}
|
||||||
message.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 ---------- */
|
/* ---------------- Cayenne LPP format ---------- */
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
#define _PAYLOAD_H_
|
#define _PAYLOAD_H_
|
||||||
|
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include "LoraEncoder.h"
|
|
||||||
|
|
||||||
// MyDevices CayenneLPP channels
|
// MyDevices CayenneLPP channels
|
||||||
#define LPP_GPS_CHANNEL 20
|
#define LPP_GPS_CHANNEL 20
|
||||||
@ -42,10 +41,10 @@ private:
|
|||||||
uint8_t cursor;
|
uint8_t cursor;
|
||||||
};
|
};
|
||||||
|
|
||||||
class TTNserialized {
|
class TTNpacked {
|
||||||
public:
|
public:
|
||||||
TTNserialized(uint8_t size);
|
TTNpacked(uint8_t size);
|
||||||
~TTNserialized();
|
~TTNpacked();
|
||||||
|
|
||||||
void reset(void);
|
void reset(void);
|
||||||
uint8_t getSize(void);
|
uint8_t getSize(void);
|
||||||
@ -60,7 +59,15 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
uint8_t *buffer;
|
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 {
|
class CayenneLPP {
|
||||||
|
Loading…
Reference in New Issue
Block a user