commit
bbc9cc735b
3
.github/FUNDING.yml
vendored
Normal file
3
.github/FUNDING.yml
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# These are supported funding model platforms
|
||||||
|
|
||||||
|
github: cyberman54
|
26
README.md
26
README.md
@ -12,7 +12,7 @@ Tutorial (in german language): https://www.heise.de/select/make/2019/1/155109923
|
|||||||
<img src="img/TTGO-case.jpg">
|
<img src="img/TTGO-case.jpg">
|
||||||
<img src="img/TTGO-curves.jpg">
|
<img src="img/TTGO-curves.jpg">
|
||||||
<img src="img/Paxcounter-LEDmatrix.jpg">
|
<img src="img/Paxcounter-LEDmatrix.jpg">
|
||||||
<img src="img/Paxcounter-Clock.png">
|
<img src="img/Paxcounter-Clock2.png">
|
||||||
<img src="img/Paxcounter-ttgo-twristband.jpg">
|
<img src="img/Paxcounter-ttgo-twristband.jpg">
|
||||||
|
|
||||||
|
|
||||||
@ -33,7 +33,7 @@ You can build this project battery powered using ESP32 deep sleep mode and reach
|
|||||||
*LoRa & SPI*:
|
*LoRa & SPI*:
|
||||||
|
|
||||||
- Heltec: LoRa-32 v1 and v2
|
- Heltec: LoRa-32 v1 and v2
|
||||||
- TTGO: T1*, T2*, T3*, T-Beam, T-Fox
|
- TTGO: [Paxcounter-Board*](https://www.aliexpress.com/item/32915894264.html?spm=a2g0o.productlist.0.0.3d656325QrcfQc&algo_pvid=4a150199-63e7-4d21-bdb1-b48164537744&algo_exp_id=4a150199-63e7-4d21-bdb1-b48164537744-2&pdp_ext_f=%7B%22sku_id%22%3A%2212000023374441919%22%7D), T1*, T2*, T3*, T-Beam, T-Fox
|
||||||
- Pycom: LoPy, LoPy4, FiPy
|
- Pycom: LoPy, LoPy4, FiPy
|
||||||
- Radioshuttle.de: [ECO Power Board](https://www.radioshuttle.de/esp32-eco-power/esp32-eco-power-board/)
|
- Radioshuttle.de: [ECO Power Board](https://www.radioshuttle.de/esp32-eco-power/esp32-eco-power-board/)
|
||||||
- WeMos: LoLin32 + [LoraNode32 shield](https://github.com/hallard/LoLin32-Lora),
|
- WeMos: LoLin32 + [LoraNode32 shield](https://github.com/hallard/LoLin32-Lora),
|
||||||
@ -140,6 +140,8 @@ If option *BOOTMENU* is defined in `paxcounter.conf`, the ESP32 board will try t
|
|||||||
|
|
||||||
(e.g. Citizens in the the Netherlands and EU may want to read [this article](https://www.ivir.nl/publicaties/download/PrivacyInformatie_2016_6.pdf) and [this article](https://autoriteitpersoonsgegevens.nl/nl/nieuws/europese-privacytoezichthouders-publiceren-opinie-eprivacyverordening)) and [this decision](https://edpb.europa.eu/news/national-news/2021/dutch-dpa-fines-municipality-wi-fi-tracking_en)
|
(e.g. Citizens in the the Netherlands and EU may want to read [this article](https://www.ivir.nl/publicaties/download/PrivacyInformatie_2016_6.pdf) and [this article](https://autoriteitpersoonsgegevens.nl/nl/nieuws/europese-privacytoezichthouders-publiceren-opinie-eprivacyverordening)) and [this decision](https://edpb.europa.eu/news/national-news/2021/dutch-dpa-fines-municipality-wi-fi-tracking_en)
|
||||||
|
|
||||||
|
(e.g. Citizens in Germany may want to read [this article of Wissenschaftliche Dienste des Deutschen Bundestages](https://www.bundestag.de/resource/blob/538890/3dfae197d2c930693aa16d1619204f58/WD-3-206-17-pdf-data.pdf)
|
||||||
|
|
||||||
Note: If you use this software you do this at your own risk. That means that you alone - not the authors of this software - are responsible for the legal compliance of an application using this or build from this software and/or usage of a device created using this software. You should take special care and get prior legal advice if you plan metering passengers in public areas and/or publish data drawn from doing so.
|
Note: If you use this software you do this at your own risk. That means that you alone - not the authors of this software - are responsible for the legal compliance of an application using this or build from this software and/or usage of a device created using this software. You should take special care and get prior legal advice if you plan metering passengers in public areas and/or publish data drawn from doing so.
|
||||||
|
|
||||||
# Privacy disclosure
|
# Privacy disclosure
|
||||||
@ -263,6 +265,22 @@ Format of the data is CSV, which can easily imported into LibreOffice, Excel, ..
|
|||||||
|
|
||||||
If you want to change this please look into src/sdcard.cpp and include/sdcard.h.
|
If you want to change this please look into src/sdcard.cpp and include/sdcard.h.
|
||||||
|
|
||||||
|
# Integration into "The Things Stack Community Edition" aka "The Things Stack V3"
|
||||||
|
|
||||||
|
To use the ESP32-Paxcounter in The Things Stack Community Edition you need an account to reach the console. Go to:
|
||||||
|
- [The Things Stack Community Edition Console](https://console.cloud.thethings.network/)
|
||||||
|
- choose your region and go to applications
|
||||||
|
- create an application by clicking "**+ Add application**" and give it a id, name, etc.
|
||||||
|
- create a device by clicking "**+ Add end device**"
|
||||||
|
- Select the end device: choose the Brand "**Open Source Community Projects**" and the Model "**ESP32-Paxcounter**", leave Hardware Version to "**Unknown**" and select your **Firmware Version** and **Profile (Region)**
|
||||||
|
- Enter registration data: choose the **frequency plan** (for EU choose the recommended), set the **AppEUI** (Fill with zeros), set the **DeviceEUI** (generate), set the **AppKey** (generate), choose a **device ID** and hit "Register end device"
|
||||||
|
- got to Applications -> "your App ID" -> Payload formatters -> Uplink, choose "**Repository**" and hit "Save changes"
|
||||||
|
|
||||||
|
The "Repository" payload decoder uses the packed format, explained below. If you want to use MyDevices from Cayenne you should use the Cayenne payload decoder instead.
|
||||||
|
|
||||||
|
# TTN Mapper
|
||||||
|
|
||||||
|
If you want your devices to be feeding the [TTN Mapper](https://ttnmapper.org/), just follow this manual: https://docs.ttnmapper.org/integration/tts-integration-v3.html - different than indicated in the manual you can leave the payload decoder to "Repository" for the ESP32-Paxcounter and you are fine.
|
||||||
|
|
||||||
# Payload format
|
# Payload format
|
||||||
|
|
||||||
@ -274,12 +292,16 @@ You can select different payload formats in `paxcounter.conf`:
|
|||||||
|
|
||||||
- [***CayenneLPP***](https://mydevices.com/cayenne/docs/lora/#lora-cayenne-low-power-payload-reference-implementation) generates MyDevices Cayenne readable fields
|
- [***CayenneLPP***](https://mydevices.com/cayenne/docs/lora/#lora-cayenne-low-power-payload-reference-implementation) generates MyDevices Cayenne readable fields
|
||||||
|
|
||||||
|
**Decrepated information from the things network v2 >>**
|
||||||
|
|
||||||
If you're using [TheThingsNetwork](https://www.thethingsnetwork.org/) (TTN) you may want to use a payload converter. Go to TTN Console - Application - Payload Formats and paste the code example below in tabs Decoder and Converter. This way your MQTT application can parse the fields `pax`, `ble` and `wifi`.
|
If you're using [TheThingsNetwork](https://www.thethingsnetwork.org/) (TTN) you may want to use a payload converter. Go to TTN Console - Application - Payload Formats and paste the code example below in tabs Decoder and Converter. This way your MQTT application can parse the fields `pax`, `ble` and `wifi`.
|
||||||
|
|
||||||
To add your device to myDevices Cayenne platform select "Cayenne-LPP" from Lora device list and use the CayenneLPP payload encoder.
|
To add your device to myDevices Cayenne platform select "Cayenne-LPP" from Lora device list and use the CayenneLPP payload encoder.
|
||||||
|
|
||||||
To track a paxcounter device with on board GPS and at the same time contribute to TTN coverage mapping, you simply activate the [TTNmapper integration](https://www.thethingsnetwork.org/docs/applications/ttnmapper/) in TTN Console. Both formats *plain* and *packed* generate the fields `latitude`, `longitude` and `hdop` required by ttnmapper. Important: set TTN mapper port filter to '4' (paxcounter GPS Port).
|
To track a paxcounter device with on board GPS and at the same time contribute to TTN coverage mapping, you simply activate the [TTNmapper integration](https://www.thethingsnetwork.org/docs/applications/ttnmapper/) in TTN Console. Both formats *plain* and *packed* generate the fields `latitude`, `longitude` and `hdop` required by ttnmapper. Important: set TTN mapper port filter to '4' (paxcounter GPS Port).
|
||||||
|
|
||||||
|
**<< Decrepated information from the things network v2**
|
||||||
|
|
||||||
Hereafter described is the default *plain* format, which uses MSB bit numbering. Under /TTN in this repository you find some ready-to-go decoders which you may copy to your TTN console:
|
Hereafter described is the default *plain* format, which uses MSB bit numbering. Under /TTN in this repository you find some ready-to-go decoders which you may copy to your TTN console:
|
||||||
|
|
||||||
[**plain_decoder.js**](src/TTN/plain_decoder.js) |
|
[**plain_decoder.js**](src/TTN/plain_decoder.js) |
|
||||||
|
2
build.py
2
build.py
@ -20,7 +20,7 @@ config = configparser.ConfigParser()
|
|||||||
config.read("platformio.ini")
|
config.read("platformio.ini")
|
||||||
|
|
||||||
# get platformio source path
|
# get platformio source path
|
||||||
srcdir = env.get("PROJECTSRC_DIR")
|
srcdir = env.get("PROJECT_SRC_DIR")
|
||||||
|
|
||||||
# get hal path
|
# get hal path
|
||||||
haldir = os.path.join (srcdir, "hal")
|
haldir = os.path.join (srcdir, "hal")
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 87 KiB |
BIN
img/Paxcounter-Clock2.png
Normal file
BIN
img/Paxcounter-Clock2.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 35 KiB |
@ -8,7 +8,7 @@
|
|||||||
extern configData_t cfg;
|
extern configData_t cfg;
|
||||||
|
|
||||||
void saveConfig(bool erase);
|
void saveConfig(bool erase);
|
||||||
bool loadConfig(void);
|
void loadConfig(void);
|
||||||
void eraseConfig(void);
|
void eraseConfig(void);
|
||||||
int version_compare(const String v1, const String v2);
|
int version_compare(const String v1, const String v2);
|
||||||
|
|
||||||
|
@ -55,7 +55,7 @@ enum snifftype_t { MAC_SNIFF_WIFI, MAC_SNIFF_BLE, MAC_SNIFF_BLE_ENS };
|
|||||||
// using packed to avoid compiler padding, because struct will be memcpy'd to
|
// using packed to avoid compiler padding, because struct will be memcpy'd to
|
||||||
// byte array
|
// byte array
|
||||||
typedef struct __attribute__((packed)) {
|
typedef struct __attribute__((packed)) {
|
||||||
char version[10]; // Firmware version
|
char version[10] = ""; // Firmware version
|
||||||
uint8_t loradr; // 0-15, lora datarate
|
uint8_t loradr; // 0-15, lora datarate
|
||||||
uint8_t txpower; // 2-15, lora tx power
|
uint8_t txpower; // 2-15, lora tx power
|
||||||
uint8_t adrmode; // 0=disabled, 1=enabled
|
uint8_t adrmode; // 0=disabled, 1=enabled
|
||||||
|
@ -36,7 +36,6 @@ void lora_send(void *pvParameters);
|
|||||||
void lora_enqueuedata(MessageBuffer_t *message);
|
void lora_enqueuedata(MessageBuffer_t *message);
|
||||||
void lora_queuereset(void);
|
void lora_queuereset(void);
|
||||||
uint32_t lora_queuewaiting(void);
|
uint32_t lora_queuewaiting(void);
|
||||||
uint8_t myBattLevelCb(void *pUserData);
|
|
||||||
void IRAM_ATTR myEventCallback(void *pUserData, ev_t ev);
|
void IRAM_ATTR myEventCallback(void *pUserData, ev_t ev);
|
||||||
void IRAM_ATTR myRxCallback(void *pUserData, uint8_t port, const uint8_t *pMsg,
|
void IRAM_ATTR myRxCallback(void *pUserData, uint8_t port, const uint8_t *pMsg,
|
||||||
size_t nMsg);
|
size_t nMsg);
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
#define _MAIN_H
|
#define _MAIN_H
|
||||||
|
|
||||||
#include <esp_spi_flash.h> // needed for reading ESP32 chip attributes
|
#include <esp_spi_flash.h> // needed for reading ESP32 chip attributes
|
||||||
#include <esp_event_loop.h> // needed for Wifi event handler
|
#include <esp_event.h> // needed for Wifi event handler
|
||||||
#include <esp32-hal-timer.h> // needed for timers
|
#include <esp32-hal-timer.h> // needed for timers
|
||||||
#include <esp_coexist.h> // needed for coex version display
|
#include <esp_coexist.h> // needed for coex version display
|
||||||
#include <esp_wifi.h> // needed for wifi init / deinit
|
#include <esp_wifi.h> // needed for wifi init / deinit
|
||||||
|
@ -3,7 +3,8 @@
|
|||||||
|
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include <esp_adc_cal.h>
|
#include <esp_adc_cal.h>
|
||||||
//#include <esp32-hal-adc.h>
|
//include <esp32-hal-adc.h>
|
||||||
|
#include <soc/adc_channel.h>
|
||||||
|
|
||||||
#include "i2c.h"
|
#include "i2c.h"
|
||||||
#include "reset.h"
|
#include "reset.h"
|
||||||
|
@ -6,10 +6,11 @@
|
|||||||
#include "timekeeper.h"
|
#include "timekeeper.h"
|
||||||
|
|
||||||
#define TIME_SYNC_FRAME_LENGTH 6 // timeserver answer frame length [bytes]
|
#define TIME_SYNC_FRAME_LENGTH 6 // timeserver answer frame length [bytes]
|
||||||
#define TIME_SYNC_FIXUP 16 // compensation for processing time [milliseconds]
|
#define TIME_SYNC_FIXUP 25 // compensation for processing time [milliseconds]
|
||||||
#define TIME_SYNC_MAX_SEQNO 0xfe // threshold for wrap around time_sync_seqNo
|
#define TIME_SYNC_MAX_SEQNO 0xfe // threshold for wrap around time_sync_seqNo
|
||||||
#define TIME_SYNC_END_FLAG (TIME_SYNC_MAX_SEQNO + 1) // end of handshake marker
|
#define TIME_SYNC_END_FLAG (TIME_SYNC_MAX_SEQNO + 1) // end of handshake marker
|
||||||
#define GPS_UTC_DIFF 315964800 // seconds diff between gps and utc epoch
|
#define GPS_UTC_DIFF 315964800UL // seconds diff between gps and utc epoch
|
||||||
|
#define LEAP_SECS_SINCE_GPSEPOCH 18UL // state of 2021
|
||||||
|
|
||||||
enum timesync_t {
|
enum timesync_t {
|
||||||
timesync_tx,
|
timesync_tx,
|
||||||
|
@ -42,13 +42,13 @@ halfile = ttgobeam10.h
|
|||||||
default_envs = usb
|
default_envs = usb
|
||||||
; upload firmware to a paxexpress repository
|
; upload firmware to a paxexpress repository
|
||||||
;default_envs = ota
|
;default_envs = ota
|
||||||
; use latest versions of libraries
|
; use upstream version of arduino-espressif32 framework
|
||||||
;default_envs = dev
|
;default_envs = dev
|
||||||
description = Paxcounter is a device for metering passenger flows in realtime. It counts how many mobile devices are around.
|
description = Paxcounter is a device for metering passenger flows in realtime. It counts how many mobile devices are around.
|
||||||
|
|
||||||
[common]
|
[common]
|
||||||
; for release_version use max. 10 chars total, use any decimal format like "a.b.c"
|
; for release_version use max. 10 chars total, use any decimal format like "a.b.c"
|
||||||
release_version = 3.0.0
|
release_version = 3.0.3
|
||||||
; DEBUG LEVEL: For production run set to 0, otherwise device will leak RAM while running!
|
; DEBUG LEVEL: For production run set to 0, otherwise device will leak RAM while running!
|
||||||
; 0=None, 1=Error, 2=Warn, 3=Info, 4=Debug, 5=Verbose
|
; 0=None, 1=Error, 2=Warn, 3=Info, 4=Debug, 5=Verbose
|
||||||
debug_level = 3
|
debug_level = 3
|
||||||
@ -56,31 +56,30 @@ extra_scripts = pre:build.py
|
|||||||
otakeyfile = ota.conf
|
otakeyfile = ota.conf
|
||||||
lorakeyfile = loraconf.h
|
lorakeyfile = loraconf.h
|
||||||
lmicconfigfile = lmic_config.h
|
lmicconfigfile = lmic_config.h
|
||||||
platform_espressif32 = espressif32@3.2.0
|
platform_espressif32 = espressif32@3.4.0
|
||||||
monitor_speed = 115200
|
monitor_speed = 115200
|
||||||
upload_speed = 115200 ; set by build.py and taken from hal file
|
upload_speed = 115200 ; set by build.py and taken from hal file
|
||||||
display_library = ; set by build.py and taken from hal file
|
display_library = ; set by build.py and taken from hal file
|
||||||
lib_deps_lora =
|
lib_deps_lora =
|
||||||
;mcci-catena/MCCI LoRaWAN LMIC library @ ^3.3.0
|
mcci-catena/MCCI LoRaWAN LMIC library @ ^4.1.1
|
||||||
https://github.com/mcci-catena/arduino-lmic.git
|
|
||||||
lib_deps_display =
|
lib_deps_display =
|
||||||
bitbank2/OneBitDisplay @ ^1.10.0
|
bitbank2/OneBitDisplay @ ^1.11.0
|
||||||
bitbank2/BitBang_I2C @ ^2.1.3
|
|
||||||
ricmoo/QRCode @ ^0.0.1
|
ricmoo/QRCode @ ^0.0.1
|
||||||
bodmer/TFT_eSPI @ ^2.3.58
|
bodmer/TFT_eSPI @ ^2.3.84
|
||||||
lib_deps_ledmatrix =
|
lib_deps_ledmatrix =
|
||||||
seeed-studio/Ultrathin_LED_Matrix @ ^1.0.0
|
seeed-studio/Ultrathin_LED_Matrix @ ^1.0.0
|
||||||
lib_deps_rgbled =
|
lib_deps_rgbled =
|
||||||
roboticsbrno/SmartLeds @ ^1.2.1
|
https://github.com/RoboticsBrno/SmartLeds.git
|
||||||
lib_deps_gps =
|
lib_deps_gps =
|
||||||
mikalhart/TinyGPSPlus @ ^1.0.2
|
mikalhart/TinyGPSPlus @ ^1.0.2
|
||||||
lib_deps_sensors =
|
lib_deps_sensors =
|
||||||
adafruit/Adafruit Unified Sensor @ ^1.1.4
|
adafruit/Adafruit Unified Sensor @ ^1.1.4
|
||||||
adafruit/Adafruit BME280 Library @ ^2.1.1
|
adafruit/Adafruit BME280 Library @ ^2.2.1
|
||||||
adafruit/Adafruit BMP085 Library @ ^1.2.0
|
adafruit/Adafruit BMP085 Library @ ^1.2.0
|
||||||
boschsensortec/BSEC Software Library @ 1.6.1480
|
boschsensortec/BSEC Software Library @ 1.6.1480
|
||||||
https://github.com/ricki-z/SDS011.git
|
https://github.com/ricki-z/SDS011.git
|
||||||
lib_deps_basic =
|
lib_deps_basic =
|
||||||
|
https://github.com/dbSuS/libpax.git @ ^1.0.0
|
||||||
https://github.com/SukkoPera/Arduino-Rokkit-Hash.git
|
https://github.com/SukkoPera/Arduino-Rokkit-Hash.git
|
||||||
bblanchon/ArduinoJson @ ^6
|
bblanchon/ArduinoJson @ ^6
|
||||||
ropg/ezTime @ ^0.8.3
|
ropg/ezTime @ ^0.8.3
|
||||||
@ -89,7 +88,6 @@ lib_deps_basic =
|
|||||||
lewisxhe/AXP202X_Library @ ^1.1.3
|
lewisxhe/AXP202X_Library @ ^1.1.3
|
||||||
geeksville/esp32-micro-sdcard @ ^0.1.1
|
geeksville/esp32-micro-sdcard @ ^0.1.1
|
||||||
256dpi/MQTT @ ^2.4.8
|
256dpi/MQTT @ ^2.4.8
|
||||||
https://github.com/cyberman54/libpax.git
|
|
||||||
lib_deps_all =
|
lib_deps_all =
|
||||||
${common.lib_deps_basic}
|
${common.lib_deps_basic}
|
||||||
${common.lib_deps_lora}
|
${common.lib_deps_lora}
|
||||||
@ -114,7 +112,6 @@ build_flags_all =
|
|||||||
-mfix-esp32-psram-cache-issue
|
-mfix-esp32-psram-cache-issue
|
||||||
|
|
||||||
[env]
|
[env]
|
||||||
lib_ldf_mode = deep ; #632 Fixes compiler error with OneBitDisplay library
|
|
||||||
framework = arduino
|
framework = arduino
|
||||||
board = esp32dev
|
board = esp32dev
|
||||||
board_build.partitions = min_spiffs.csv
|
board_build.partitions = min_spiffs.csv
|
||||||
@ -137,7 +134,4 @@ upload_protocol = esptool
|
|||||||
[env:dev]
|
[env:dev]
|
||||||
upload_protocol = esptool
|
upload_protocol = esptool
|
||||||
build_type = debug
|
build_type = debug
|
||||||
platform = https://github.com/platformio/platform-espressif32.git#develop
|
platform = https://github.com/platformio/platform-espressif32.git#feature/arduino-upstream
|
||||||
platform_packages =
|
|
||||||
; use upstream Git version
|
|
||||||
framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32.git
|
|
@ -32,7 +32,9 @@ static uint8_t buffer[cfgLen + cfgLen2];
|
|||||||
// 3. magicByte [cfgLen2 bytes, containing a fixed identifier]
|
// 3. magicByte [cfgLen2 bytes, containing a fixed identifier]
|
||||||
|
|
||||||
static void defaultConfig(configData_t *myconfig) {
|
static void defaultConfig(configData_t *myconfig) {
|
||||||
memcpy(myconfig->version, &PROGVERSION, 10); // Firmware version
|
|
||||||
|
strncpy(myconfig->version, PROGVERSION,
|
||||||
|
sizeof(myconfig->version) - 1); // Firmware version
|
||||||
|
|
||||||
// device factory settings
|
// device factory settings
|
||||||
myconfig->loradr = LORADRDEFAULT; // 0-15, lora datarate, see paxcounter.conf
|
myconfig->loradr = LORADRDEFAULT; // 0-15, lora datarate, see paxcounter.conf
|
||||||
@ -96,30 +98,33 @@ void saveConfig(bool erase) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// load configuration from NVRAM into RAM and make it current
|
// load configuration from NVRAM into RAM and make it current
|
||||||
bool loadConfig() {
|
void loadConfig(void) {
|
||||||
|
|
||||||
|
int readBytes = 0;
|
||||||
|
|
||||||
ESP_LOGI(TAG, "Loading device configuration from NVRAM...");
|
ESP_LOGI(TAG, "Loading device configuration from NVRAM...");
|
||||||
|
|
||||||
if (!nvram.begin(DEVCONFIG, true)) {
|
if (nvram.begin(DEVCONFIG, true)) {
|
||||||
|
// load device runtime config from nvram and copy it to byte array
|
||||||
|
readBytes = nvram.getBytes(DEVCONFIG, buffer, cfgLen + cfgLen2);
|
||||||
|
nvram.end();
|
||||||
|
|
||||||
|
// check that runtime config data length matches
|
||||||
|
if (readBytes != cfgLen + cfgLen2) {
|
||||||
|
ESP_LOGE(TAG, "No valid configuration found");
|
||||||
|
migrateConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
ESP_LOGI(TAG, "NVRAM initialized, device starts with factory settings");
|
ESP_LOGI(TAG, "NVRAM initialized, device starts with factory settings");
|
||||||
eraseConfig();
|
eraseConfig();
|
||||||
}
|
}
|
||||||
|
|
||||||
// simple check that runtime config data matches
|
|
||||||
// if (nvram.getBytesLength(DEVCONFIG) != (cfgLen + cfgLen2)) {
|
|
||||||
// ESP_LOGE(TAG, "Configuration invalid");
|
|
||||||
// return false;
|
|
||||||
//}
|
|
||||||
|
|
||||||
// load device runtime config from nvram and copy it to byte array
|
|
||||||
nvram.getBytes(DEVCONFIG, buffer, cfgLen + cfgLen2);
|
|
||||||
nvram.end();
|
|
||||||
|
|
||||||
// validate loaded configuration by checking magic bytes at end of array
|
// validate loaded configuration by checking magic bytes at end of array
|
||||||
// if (memcmp(buffer + cfgLen, &cfgMagicBytes, cfgLen2) != 0) {
|
if (memcmp(buffer + cfgLen, &cfgMagicBytes, cfgLen2) != 0) {
|
||||||
// ESP_LOGW(TAG, "No configuration found");
|
ESP_LOGE(TAG, "Configuration data corrupt");
|
||||||
// return false;
|
eraseConfig();
|
||||||
//}
|
}
|
||||||
|
|
||||||
// copy loaded configuration into runtime cfg struct
|
// copy loaded configuration into runtime cfg struct
|
||||||
memcpy(&cfg, buffer, cfgLen);
|
memcpy(&cfg, buffer, cfgLen);
|
||||||
@ -130,13 +135,13 @@ bool loadConfig() {
|
|||||||
case -1: // device configuration belongs to newer than current firmware
|
case -1: // device configuration belongs to newer than current firmware
|
||||||
ESP_LOGE(TAG, "Incompatible device configuration");
|
ESP_LOGE(TAG, "Incompatible device configuration");
|
||||||
eraseConfig();
|
eraseConfig();
|
||||||
return true;
|
break;
|
||||||
case 1: // device configuration belongs to older than current firmware
|
case 1: // device configuration belongs to older than current firmware
|
||||||
ESP_LOGW(TAG, "Device was updated, attempt to migrate configuration");
|
ESP_LOGW(TAG, "Device was updated, attempt to migrate configuration");
|
||||||
migrateConfig();
|
migrateConfig();
|
||||||
return true;
|
break;
|
||||||
default: // device configuration version matches current firmware version
|
default: // device configuration version matches current firmware version
|
||||||
return true;
|
break; // nothing to do here
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -120,7 +120,7 @@ time_t get_gpstime(uint16_t *msec) {
|
|||||||
t = makeTime(tm);
|
t = makeTime(tm);
|
||||||
|
|
||||||
ESP_LOGD(TAG, "GPS date/time: %s",
|
ESP_LOGD(TAG, "GPS date/time: %s",
|
||||||
UTC.dateTime(t, "d.M Y H:i:s T").c_str());
|
UTC.dateTime(t, "d.M Y H:i:s.v T").c_str());
|
||||||
|
|
||||||
// add protocol delay with millisecond precision
|
// add protocol delay with millisecond precision
|
||||||
t += delay_ms / 1000 - 1; // whole seconds
|
t += delay_ms / 1000 - 1; // whole seconds
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
#define HAS_BUTTON KEY_BUILTIN
|
#define HAS_BUTTON KEY_BUILTIN
|
||||||
|
|
||||||
// enable only if you want to store a local paxcount table on the device
|
// enable only if you want to store a local paxcount table on the device
|
||||||
#define HAS_SDCARD 1 // this board has an SD-card-reader/writer
|
// #define HAS_SDCARD 1 // this board has an SD-card-reader/writer
|
||||||
// Pins for SD-card
|
// Pins for SD-card
|
||||||
#define SDCARD_CS (13)
|
#define SDCARD_CS (13)
|
||||||
#define SDCARD_MOSI (15)
|
#define SDCARD_MOSI (15)
|
||||||
|
@ -10,20 +10,20 @@ void irqHandler(void *pvParameters) {
|
|||||||
|
|
||||||
_ASSERT((uint32_t)pvParameters == 1); // FreeRTOS check
|
_ASSERT((uint32_t)pvParameters == 1); // FreeRTOS check
|
||||||
|
|
||||||
uint32_t InterruptStatus;
|
uint32_t irqSource;
|
||||||
|
|
||||||
// task remains in blocked state until it is notified by an irq
|
// task remains in blocked state until it is notified by an irq
|
||||||
for (;;) {
|
for (;;) {
|
||||||
xTaskNotifyWait(0x00, // Don't clear any bits on entry
|
xTaskNotifyWait(0x00, // Don't clear any bits on entry
|
||||||
ULONG_MAX, // Clear all bits on exit
|
ULONG_MAX, // Clear all bits on exit
|
||||||
&InterruptStatus, // Receives the notification value
|
&irqSource, // Receives the notification value
|
||||||
portMAX_DELAY); // wait forever
|
portMAX_DELAY); // wait forever
|
||||||
|
|
||||||
if (InterruptStatus & UNMASK_IRQ) // interrupt handler to be enabled?
|
if (irqSource & UNMASK_IRQ) // interrupt handler to be enabled?
|
||||||
InterruptStatus &= ~MASK_IRQ; // then clear irq mask flag
|
irqSource &= ~MASK_IRQ; // then clear irq mask flag
|
||||||
// else suppress processing if interrupt handler is disabled
|
// else suppress processing if interrupt handler is disabled
|
||||||
// or time critical lmic jobs are pending in next 100ms
|
// or time critical lmic jobs are pending in next 100ms
|
||||||
else if ((InterruptStatus & MASK_IRQ)
|
else if ((irqSource & MASK_IRQ)
|
||||||
#if (HAS_LORA)
|
#if (HAS_LORA)
|
||||||
|| os_queryTimeCriticalJobs(ms2osticks(100))
|
|| os_queryTimeCriticalJobs(ms2osticks(100))
|
||||||
#endif
|
#endif
|
||||||
@ -32,63 +32,49 @@ void irqHandler(void *pvParameters) {
|
|||||||
|
|
||||||
// button pressed?
|
// button pressed?
|
||||||
#ifdef HAS_BUTTON
|
#ifdef HAS_BUTTON
|
||||||
if (InterruptStatus & BUTTON_IRQ) {
|
if (irqSource & BUTTON_IRQ)
|
||||||
readButton();
|
readButton();
|
||||||
InterruptStatus &= ~BUTTON_IRQ;
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// display needs refresh?
|
// display needs refresh?
|
||||||
#ifdef HAS_DISPLAY
|
#ifdef HAS_DISPLAY
|
||||||
if (InterruptStatus & DISPLAY_IRQ) {
|
if (irqSource & DISPLAY_IRQ)
|
||||||
dp_refresh();
|
dp_refresh();
|
||||||
InterruptStatus &= ~DISPLAY_IRQ;
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// LED Matrix display needs refresh?
|
// LED Matrix display needs refresh?
|
||||||
#ifdef HAS_MATRIX_DISPLAY
|
#ifdef HAS_MATRIX_DISPLAY
|
||||||
if (InterruptStatus & MATRIX_DISPLAY_IRQ) {
|
if (irqSource & MATRIX_DISPLAY_IRQ)
|
||||||
refreshTheMatrixDisplay();
|
refreshTheMatrixDisplay();
|
||||||
InterruptStatus &= ~MATRIX_DISPLAY_IRQ;
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if (TIME_SYNC_INTERVAL)
|
#if (TIME_SYNC_INTERVAL)
|
||||||
// is time to be synced?
|
// is time to be synced?
|
||||||
if (InterruptStatus & TIMESYNC_IRQ) {
|
if (irqSource & TIMESYNC_IRQ) {
|
||||||
now(); // ensure sysTime is recent
|
now(); // ensure sysTime is recent
|
||||||
calibrateTime();
|
calibrateTime();
|
||||||
InterruptStatus &= ~TIMESYNC_IRQ;
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// BME sensor data to be read?
|
// BME sensor data to be read?
|
||||||
#if (HAS_BME)
|
#if (HAS_BME)
|
||||||
if (InterruptStatus & BME_IRQ) {
|
if (irqSource & BME_IRQ)
|
||||||
bme_storedata(&bme_status);
|
bme_storedata(&bme_status);
|
||||||
InterruptStatus &= ~BME_IRQ;
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// are cyclic tasks due?
|
// are cyclic tasks due?
|
||||||
if (InterruptStatus & CYCLIC_IRQ) {
|
if (irqSource & CYCLIC_IRQ)
|
||||||
doHousekeeping();
|
doHousekeeping();
|
||||||
InterruptStatus &= ~CYCLIC_IRQ;
|
|
||||||
}
|
|
||||||
|
|
||||||
// do we have a power event?
|
// do we have a power event?
|
||||||
#ifdef HAS_PMU
|
#ifdef HAS_PMU
|
||||||
if (InterruptStatus & PMU_IRQ) {
|
if (irqSource & PMU_IRQ)
|
||||||
AXP192_powerevent_IRQ();
|
AXP192_powerevent_IRQ();
|
||||||
InterruptStatus &= ~PMU_IRQ;
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// is time to send the payload?
|
// is time to send the payload?
|
||||||
if (InterruptStatus & SENDCYCLE_IRQ) {
|
if (irqSource & SENDCYCLE_IRQ) {
|
||||||
sendData();
|
sendData();
|
||||||
InterruptStatus &= ~SENDCYCLE_IRQ;
|
|
||||||
// goto sleep if we have a sleep cycle
|
// goto sleep if we have a sleep cycle
|
||||||
if (cfg.sleepcycle)
|
if (cfg.sleepcycle)
|
||||||
#ifdef HAS_BUTTON
|
#ifdef HAS_BUTTON
|
||||||
|
@ -16,7 +16,7 @@ void process_count(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void init_libpax(void) {
|
void init_libpax(void) {
|
||||||
libpax_counter_init(process_count, &count_from_libpax,
|
libpax_counter_init(process_count, &count_from_libpax, cfg.sendcycle * 2,
|
||||||
cfg.sendcycle * 2 * 1000, cfg.countermode);
|
cfg.countermode);
|
||||||
libpax_counter_start();
|
libpax_counter_start();
|
||||||
}
|
}
|
@ -43,7 +43,7 @@
|
|||||||
// enable more verbose output. Make sure that printf is actually
|
// enable more verbose output. Make sure that printf is actually
|
||||||
// configured (e.g. on AVR it is not by default), otherwise using it can
|
// configured (e.g. on AVR it is not by default), otherwise using it can
|
||||||
// cause crashing.
|
// cause crashing.
|
||||||
//#define LMIC_DEBUG_LEVEL 2
|
//#define LMIC_DEBUG_LEVEL 1
|
||||||
|
|
||||||
// Enable this to allow using printf() to print to the given serial port
|
// Enable this to allow using printf() to print to the given serial port
|
||||||
// (or any other Print object). This can be easy for debugging. The
|
// (or any other Print object). This can be easy for debugging. The
|
||||||
|
12
src/lorawan-devices-repo/index.yaml
Normal file
12
src/lorawan-devices-repo/index.yaml
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
vendors:
|
||||||
|
- id: example
|
||||||
|
name: Example
|
||||||
|
vendorID: 0
|
||||||
|
draft: true
|
||||||
|
|
||||||
|
#SNIP - a lot of other vendors, just attach the lower lines to the index.yaml
|
||||||
|
#SNIP - could add a logo with "logo: filename"
|
||||||
|
|
||||||
|
- id: opensource
|
||||||
|
name: Open Source Community Projects
|
||||||
|
website: https://en.wikipedia.org/wiki/Open_source
|
21
src/lorawan-devices-repo/readme.md
Normal file
21
src/lorawan-devices-repo/readme.md
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
# lorawan-devices repo for The Things Network & The Things Stack V3
|
||||||
|
|
||||||
|
To add bigger payload decoders than 4k (via web ui) we provide the metadata to the lorawan-devices repo on github. For this we create a vendor "opensource" and specify a device "esp32-paxcounter" via this files:
|
||||||
|
|
||||||
|
> /
|
||||||
|
> - index.yaml (include the marked lines at the bottom of the original file)
|
||||||
|
>
|
||||||
|
> /vendor (existing folder in the repo)
|
||||||
|
>
|
||||||
|
> - /opensource (new folder with all the good stuff inside)
|
||||||
|
>
|
||||||
|
> /vendor/opensource
|
||||||
|
>
|
||||||
|
> - index.yaml (name of the device inside)
|
||||||
|
> - esp32-paxcounter.yaml (metadata for the software)
|
||||||
|
> - esp32-paxcounter-profile-eu868.yaml (profile for europe)
|
||||||
|
> - esp32-paxcounter-profile-us915.yaml (profile for north america)
|
||||||
|
> - esp32-paxcounter-codec.yaml (examples for the payload decoder)
|
||||||
|
> - esp32-paxcounter-packed_decodeUplink.js (payload decoder as provided before for the web ui, now for the lorawan devices repo)
|
||||||
|
|
||||||
|
With these files, we can make a pull request at the lorawan-devices repo: https://github.com/TheThingsNetwork/lorawan-devices and hope to be included in the future. Cool thing about it would be that then the users need only to choose "Open Source Community Projects" from the vendor list and "EXP32-Paxcounter" as device and most of the compex things is taking care of (like choosing the right LoRaWAN version and installing the payload decoder).
|
143
src/lorawan-devices-repo/vendor/opensource/esp32-paxcounter-codec.yaml
vendored
Normal file
143
src/lorawan-devices-repo/vendor/opensource/esp32-paxcounter-codec.yaml
vendored
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
# Uplink decoder decodes binary data uplink into a JSON object (optional)
|
||||||
|
# For documentation on writing encoders and decoders, see: https://thethingsstack.io/integrations/payload-formatters/javascript/
|
||||||
|
uplinkDecoder:
|
||||||
|
fileName: esp32-paxcounter-packed.js
|
||||||
|
examples:
|
||||||
|
- description: Paxcount data
|
||||||
|
input:
|
||||||
|
fPort: 1
|
||||||
|
bytes: [0x07, 0x00, 0x03, 0x00]
|
||||||
|
output:
|
||||||
|
data:
|
||||||
|
bytes: [0x07, 0x00, 0x03, 0x00]
|
||||||
|
port: 1
|
||||||
|
wifi: 7
|
||||||
|
ble: 3
|
||||||
|
pax: 10
|
||||||
|
errors: []
|
||||||
|
warnings: []
|
||||||
|
- description: Device status query result
|
||||||
|
input:
|
||||||
|
fPort: 2
|
||||||
|
bytes: [0x2F, 0x01, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x4B, 0x03, 0x00, 0x2D, 0xC0, 0x4B, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
|
||||||
|
output:
|
||||||
|
data:
|
||||||
|
bytes: [0x2F, 0x01, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x4B, 0x03, 0x00, 0x2D, 0xC0, 0x4B, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
|
||||||
|
port: 2
|
||||||
|
voltage: 303
|
||||||
|
uptime: 216000
|
||||||
|
cputemp: 45
|
||||||
|
memory: 216000
|
||||||
|
reset0: 0
|
||||||
|
restarts: 0
|
||||||
|
errors: []
|
||||||
|
warnings: []
|
||||||
|
- description: Device config data
|
||||||
|
input:
|
||||||
|
fPort: 3
|
||||||
|
bytes: [0x09, 0x0F, 0x00, 0x00, 0x78, 0x32, 0x0A, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
|
||||||
|
output:
|
||||||
|
data:
|
||||||
|
bytes: [0x09, 0x0F, 0x00, 0x00, 0x78, 0x32, 0x0A, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
|
||||||
|
port: 3
|
||||||
|
loradr: 9
|
||||||
|
txpower: 15
|
||||||
|
rssilimit: 0
|
||||||
|
sendcycle: 120
|
||||||
|
wifichancycle: 50
|
||||||
|
blescantime: 10
|
||||||
|
rgblum: 30
|
||||||
|
flags:
|
||||||
|
adr: 0
|
||||||
|
antenna: 0
|
||||||
|
blescan: 0
|
||||||
|
countermode: 0
|
||||||
|
reserved: 0
|
||||||
|
screen: 0
|
||||||
|
screensaver: 0
|
||||||
|
payloadmask:
|
||||||
|
battery: 0
|
||||||
|
bme: 0
|
||||||
|
counter: 0
|
||||||
|
gps: 0
|
||||||
|
reserved: 0
|
||||||
|
sensor1: 0
|
||||||
|
sensor2: 0
|
||||||
|
sensor3: 0
|
||||||
|
version: ''
|
||||||
|
errors: []
|
||||||
|
warnings: []
|
||||||
|
- description: GPS data
|
||||||
|
input:
|
||||||
|
fPort: 4
|
||||||
|
bytes: [0x65, 0xCA, 0x06, 0x03, 0x05, 0x19, 0x6F, 0x00, 0x05, 0xC6, 0x00, 0x42, 0x00]
|
||||||
|
output:
|
||||||
|
data:
|
||||||
|
bytes: [0x65, 0xCA, 0x06, 0x03, 0x05, 0x19, 0x6F, 0x00, 0x05, 0xC6, 0x00, 0x42, 0x00]
|
||||||
|
port: 4
|
||||||
|
latitude: 50.776677
|
||||||
|
longitude: 7.280901
|
||||||
|
sats: 5
|
||||||
|
hdop: 1.98
|
||||||
|
altitude: 66
|
||||||
|
errors: []
|
||||||
|
warnings: []
|
||||||
|
- description: Button data
|
||||||
|
input:
|
||||||
|
fPort: 5
|
||||||
|
bytes: [0x01]
|
||||||
|
output:
|
||||||
|
data:
|
||||||
|
bytes: [0x01]
|
||||||
|
port: 5
|
||||||
|
button: 1
|
||||||
|
errors: []
|
||||||
|
warnings: []
|
||||||
|
- description: Environmental sensor data
|
||||||
|
input:
|
||||||
|
fPort: 7
|
||||||
|
bytes: [0x08, 0x34, 0x10, 0x27, 0x40, 0x1F, 0x10, 0x27]
|
||||||
|
output:
|
||||||
|
data:
|
||||||
|
bytes: [0x08, 0x34, 0x10, 0x27, 0x40, 0x1F, 0x10, 0x27]
|
||||||
|
port: 7
|
||||||
|
temperature: 21.00
|
||||||
|
pressure: 1000.0
|
||||||
|
humidity: 80.00
|
||||||
|
air: 100.00
|
||||||
|
errors: []
|
||||||
|
warnings: []
|
||||||
|
- description: Battery voltage data
|
||||||
|
input:
|
||||||
|
fPort: 8
|
||||||
|
bytes: [0x2F, 0x01]
|
||||||
|
output:
|
||||||
|
data:
|
||||||
|
bytes: [0x2F, 0x01]
|
||||||
|
port: 8
|
||||||
|
voltage: 303
|
||||||
|
errors: []
|
||||||
|
warnings: []
|
||||||
|
- description: Time/Date
|
||||||
|
input:
|
||||||
|
fPort: 9
|
||||||
|
bytes: [0x90, 0x86, 0xC8, 0x60, 0x00]
|
||||||
|
output:
|
||||||
|
data:
|
||||||
|
bytes: [0x90, 0x86, 0xC8, 0x60, 0x00]
|
||||||
|
port: 9
|
||||||
|
time: 1623754384
|
||||||
|
timestatus: 0
|
||||||
|
errors: []
|
||||||
|
warnings: []
|
||||||
|
- description: User sensor data
|
||||||
|
input:
|
||||||
|
fPort: 10
|
||||||
|
bytes: [0x00, 0x00]
|
||||||
|
output:
|
||||||
|
data:
|
||||||
|
bytes: [0x00, 0x00]
|
||||||
|
port: 10
|
||||||
|
ens: 0
|
||||||
|
errors: []
|
||||||
|
warnings: []
|
344
src/lorawan-devices-repo/vendor/opensource/esp32-paxcounter-packed.js
vendored
Normal file
344
src/lorawan-devices-repo/vendor/opensource/esp32-paxcounter-packed.js
vendored
Normal file
@ -0,0 +1,344 @@
|
|||||||
|
// Decoder for device payload encoder "PACKED"
|
||||||
|
// copy&paste to TTN Console V3 -> Applications -> Payload formatters -> Uplink -> Javascript
|
||||||
|
// modified for The Things Stack V3 by Caspar Armster, dasdigidings e.V.
|
||||||
|
|
||||||
|
function decodeUplink(input) {
|
||||||
|
var data = {};
|
||||||
|
|
||||||
|
if (input.fPort === 1) {
|
||||||
|
// only wifi counter data, no gps
|
||||||
|
if (input.bytes.length === 2) {
|
||||||
|
data = decode(input.bytes, [uint16], ['wifi']);
|
||||||
|
}
|
||||||
|
// wifi + ble counter data, no gps
|
||||||
|
if (input.bytes.length === 4) {
|
||||||
|
data = decode(input.bytes, [uint16, uint16], ['wifi', 'ble']);
|
||||||
|
}
|
||||||
|
// combined wifi + ble + SDS011
|
||||||
|
if (input.bytes.length === 8) {
|
||||||
|
data = decode(input.bytes, [uint16, uint16, uint16, uint16], ['wifi', 'ble', 'PM10', 'PM25']);
|
||||||
|
}
|
||||||
|
// combined wifi counter and gps data, used by https://opensensemap.org
|
||||||
|
if (input.bytes.length === 10) {
|
||||||
|
data = decode(input.bytes, [latLng, latLng, uint16], ['latitude', 'longitude', 'wifi']);
|
||||||
|
}
|
||||||
|
// combined wifi + ble counter and gps data, used by https://opensensemap.org
|
||||||
|
if (input.bytes.length === 12) {
|
||||||
|
data = decode(input.bytes, [latLng, latLng, uint16, uint16], ['latitude', 'longitude', 'wifi', 'ble']);
|
||||||
|
}
|
||||||
|
// combined wifi counter and gps data
|
||||||
|
if (input.bytes.length === 15) {
|
||||||
|
data = decode(input.bytes, [uint16, latLng, latLng, uint8, hdop, altitude], ['wifi', 'latitude', 'longitude', 'sats', 'hdop', 'altitude']);
|
||||||
|
}
|
||||||
|
// combined wifi + ble counter and gps data
|
||||||
|
if (input.bytes.length === 17) {
|
||||||
|
data = decode(input.bytes, [uint16, uint16, latLng, latLng, uint8, hdop, altitude], ['wifi', 'ble', 'latitude', 'longitude', 'sats', 'hdop', 'altitude']);
|
||||||
|
}
|
||||||
|
|
||||||
|
data.pax = 0;
|
||||||
|
if ('wifi' in data) {
|
||||||
|
data.pax += data.wifi;
|
||||||
|
}
|
||||||
|
if ('ble' in data) {
|
||||||
|
data.pax += data.ble;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input.fPort === 2) {
|
||||||
|
// device status data
|
||||||
|
if (input.bytes.length === 20) {
|
||||||
|
data = decode(input.bytes, [uint16, uptime, uint8, uint32, uint8, uint32], ['voltage', 'uptime', 'cputemp', 'memory', 'reset0', 'restarts']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input.fPort === 3) {
|
||||||
|
// device config data
|
||||||
|
data = decode(input.bytes, [uint8, uint8, int16, uint8, uint8, uint8, uint8, bitmap1, bitmap2, version], ['loradr', 'txpower', 'rssilimit', 'sendcycle', 'wifichancycle', 'blescantime', 'rgblum', 'flags', 'payloadmask', 'version']);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input.fPort === 4) {
|
||||||
|
// gps data
|
||||||
|
if (input.bytes.length === 8) {
|
||||||
|
data = decode(input.bytes, [latLng, latLng], ['latitude', 'longitude']);
|
||||||
|
} else {
|
||||||
|
data = decode(input.bytes, [latLng, latLng, uint8, hdop, altitude], ['latitude', 'longitude', 'sats', 'hdop', 'altitude']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input.fPort === 5) {
|
||||||
|
// button pressed
|
||||||
|
data = decode(input.bytes, [uint8], ['button']);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input.fPort === 7) {
|
||||||
|
// BME680 sensor data
|
||||||
|
data = decode(input.bytes, [float, pressure, ufloat, ufloat], ['temperature', 'pressure', 'humidity', 'air']);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input.fPort === 8) {
|
||||||
|
// battery voltage
|
||||||
|
data = decode(input.bytes, [uint16], ['voltage']);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input.fPort === 9) {
|
||||||
|
// timesync request
|
||||||
|
if (input.bytes.length === 1) {
|
||||||
|
data.timesync_seqno = input.bytes[0];
|
||||||
|
}
|
||||||
|
// epoch time answer
|
||||||
|
if (input.bytes.length === 5) {
|
||||||
|
data = decode(input.bytes, [uint32, uint8], ['time', 'timestatus']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input.fPort === 10) {
|
||||||
|
// ENS count
|
||||||
|
data = decode(input.bytes, [uint16], ['ens']);
|
||||||
|
}
|
||||||
|
|
||||||
|
data.bytes = input.bytes; // comment out if you do not want to include the original payload
|
||||||
|
data.port = input.fPort; // comment out if you do not want to inlude the port
|
||||||
|
|
||||||
|
return {
|
||||||
|
data: data,
|
||||||
|
warnings: [],
|
||||||
|
errors: []
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function encodeDownlink(input) {
|
||||||
|
return {
|
||||||
|
data: {
|
||||||
|
bytes: input.bytes
|
||||||
|
},
|
||||||
|
warnings: ["Encoding of downlink is not supported by the JS decoder."],
|
||||||
|
errors: []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function decodeDownlink(input) {
|
||||||
|
return {
|
||||||
|
data: {
|
||||||
|
bytes: input.bytes
|
||||||
|
},
|
||||||
|
warnings: ["Decoding of downlink is not supported by the JS decoder."],
|
||||||
|
errors: []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----- contents of /src/decoder.js --------------------------------------------
|
||||||
|
// https://github.com/thesolarnomad/lora-serialization/blob/master/src/decoder.js
|
||||||
|
|
||||||
|
var bytesToInt = function (bytes) {
|
||||||
|
var i = 0;
|
||||||
|
for (var x = 0; x < bytes.length; x++) {
|
||||||
|
i |= (bytes[x] << (x * 8));
|
||||||
|
}
|
||||||
|
return i;
|
||||||
|
};
|
||||||
|
|
||||||
|
var version = function (bytes) {
|
||||||
|
if (bytes.length !== version.BYTES) {
|
||||||
|
throw new Error('version must have exactly 10 bytes');
|
||||||
|
}
|
||||||
|
return String.fromCharCode.apply(null, bytes).split('\u0000')[0];
|
||||||
|
};
|
||||||
|
version.BYTES = 10;
|
||||||
|
|
||||||
|
var uint8 = function (bytes) {
|
||||||
|
if (bytes.length !== uint8.BYTES) {
|
||||||
|
throw new Error('uint8 must have exactly 1 byte');
|
||||||
|
}
|
||||||
|
return bytesToInt(bytes);
|
||||||
|
};
|
||||||
|
uint8.BYTES = 1;
|
||||||
|
|
||||||
|
var uint16 = function (bytes) {
|
||||||
|
if (bytes.length !== uint16.BYTES) {
|
||||||
|
throw new Error('uint16 must have exactly 2 bytes');
|
||||||
|
}
|
||||||
|
return bytesToInt(bytes);
|
||||||
|
};
|
||||||
|
uint16.BYTES = 2;
|
||||||
|
|
||||||
|
var uint32 = function (bytes) {
|
||||||
|
if (bytes.length !== uint32.BYTES) {
|
||||||
|
throw new Error('uint32 must have exactly 4 bytes');
|
||||||
|
}
|
||||||
|
return bytesToInt(bytes);
|
||||||
|
};
|
||||||
|
uint32.BYTES = 4;
|
||||||
|
|
||||||
|
var uint64 = function (bytes) {
|
||||||
|
if (bytes.length !== uint64.BYTES) {
|
||||||
|
throw new Error('uint64 must have exactly 8 bytes');
|
||||||
|
}
|
||||||
|
return bytesToInt(bytes);
|
||||||
|
};
|
||||||
|
uint64.BYTES = 8;
|
||||||
|
|
||||||
|
var int8 = function (bytes) {
|
||||||
|
if (bytes.length !== int8.BYTES) {
|
||||||
|
throw new Error('int8 must have exactly 1 byte');
|
||||||
|
}
|
||||||
|
var value = +(bytesToInt(bytes));
|
||||||
|
if (value > 127) {
|
||||||
|
value -= 256;
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
};
|
||||||
|
int8.BYTES = 1;
|
||||||
|
|
||||||
|
var int16 = function (bytes) {
|
||||||
|
if (bytes.length !== int16.BYTES) {
|
||||||
|
throw new Error('int16 must have exactly 2 bytes');
|
||||||
|
}
|
||||||
|
var value = +(bytesToInt(bytes));
|
||||||
|
if (value > 32767) {
|
||||||
|
value -= 65536;
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
};
|
||||||
|
int16.BYTES = 2;
|
||||||
|
|
||||||
|
var int32 = function (bytes) {
|
||||||
|
if (bytes.length !== int32.BYTES) {
|
||||||
|
throw new Error('int32 must have exactly 4 bytes');
|
||||||
|
}
|
||||||
|
var value = +(bytesToInt(bytes));
|
||||||
|
if (value > 2147483647) {
|
||||||
|
value -= 4294967296;
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
};
|
||||||
|
int32.BYTES = 4;
|
||||||
|
|
||||||
|
var latLng = function (bytes) {
|
||||||
|
return +(int32(bytes) / 1e6).toFixed(6);
|
||||||
|
};
|
||||||
|
latLng.BYTES = int32.BYTES;
|
||||||
|
|
||||||
|
var uptime = function (bytes) {
|
||||||
|
return uint64(bytes);
|
||||||
|
};
|
||||||
|
uptime.BYTES = uint64.BYTES;
|
||||||
|
|
||||||
|
var hdop = function (bytes) {
|
||||||
|
return +(uint16(bytes) / 100).toFixed(2);
|
||||||
|
};
|
||||||
|
hdop.BYTES = uint16.BYTES;
|
||||||
|
|
||||||
|
var altitude = function (bytes) {
|
||||||
|
// Option to increase altitude resolution (also on encoder side)
|
||||||
|
// return +(int16(bytes) / 4 - 1000).toFixed(1);
|
||||||
|
return +(int16(bytes));
|
||||||
|
};
|
||||||
|
altitude.BYTES = int16.BYTES;
|
||||||
|
|
||||||
|
|
||||||
|
var float = function (bytes) {
|
||||||
|
if (bytes.length !== float.BYTES) {
|
||||||
|
throw new Error('Float must have exactly 2 bytes');
|
||||||
|
}
|
||||||
|
var isNegative = bytes[0] & 0x80;
|
||||||
|
var b = ('00000000' + Number(bytes[0]).toString(2)).slice(-8)
|
||||||
|
+ ('00000000' + Number(bytes[1]).toString(2)).slice(-8);
|
||||||
|
if (isNegative) {
|
||||||
|
var arr = b.split('').map(function (x) { return !Number(x); });
|
||||||
|
for (var i = arr.length - 1; i > 0; i--) {
|
||||||
|
arr[i] = !arr[i];
|
||||||
|
if (arr[i]) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
b = arr.map(Number).join('');
|
||||||
|
}
|
||||||
|
var t = parseInt(b, 2);
|
||||||
|
if (isNegative) {
|
||||||
|
t = -t;
|
||||||
|
}
|
||||||
|
return +(t / 100).toFixed(2);
|
||||||
|
};
|
||||||
|
float.BYTES = 2;
|
||||||
|
|
||||||
|
var ufloat = function (bytes) {
|
||||||
|
return +(uint16(bytes) / 100).toFixed(2);
|
||||||
|
};
|
||||||
|
ufloat.BYTES = uint16.BYTES;
|
||||||
|
|
||||||
|
var pressure = function (bytes) {
|
||||||
|
return +(uint16(bytes) / 10).toFixed(1);
|
||||||
|
};
|
||||||
|
pressure.BYTES = uint16.BYTES;
|
||||||
|
|
||||||
|
var bitmap1 = function (byte) {
|
||||||
|
if (byte.length !== bitmap1.BYTES) {
|
||||||
|
throw new Error('Bitmap must have exactly 1 byte');
|
||||||
|
}
|
||||||
|
var i = bytesToInt(byte);
|
||||||
|
var bm = ('00000000' + Number(i).toString(2)).substr(-8).split('').map(Number).map(Boolean);
|
||||||
|
return ['adr', 'screensaver', 'screen', 'countermode', 'blescan', 'antenna', 'reserved', 'reserved']
|
||||||
|
.reduce(function (obj, pos, index) {
|
||||||
|
obj[pos] = +bm[index];
|
||||||
|
return obj;
|
||||||
|
}, {});
|
||||||
|
};
|
||||||
|
bitmap1.BYTES = 1;
|
||||||
|
|
||||||
|
var bitmap2 = function (byte) {
|
||||||
|
if (byte.length !== bitmap2.BYTES) {
|
||||||
|
throw new Error('Bitmap must have exactly 1 byte');
|
||||||
|
}
|
||||||
|
var i = bytesToInt(byte);
|
||||||
|
var bm = ('00000000' + Number(i).toString(2)).substr(-8).split('').map(Number).map(Boolean);
|
||||||
|
return ['battery', 'sensor3', 'sensor2', 'sensor1', 'gps', 'bme', 'reserved', 'counter']
|
||||||
|
.reduce(function (obj, pos, index) {
|
||||||
|
obj[pos] = +bm[index];
|
||||||
|
return obj;
|
||||||
|
}, {});
|
||||||
|
};
|
||||||
|
bitmap2.BYTES = 1;
|
||||||
|
|
||||||
|
var decode = function (bytes, mask, names) {
|
||||||
|
|
||||||
|
var maskLength = mask.reduce(function (prev, cur) {
|
||||||
|
return prev + cur.BYTES;
|
||||||
|
}, 0);
|
||||||
|
if (bytes.length < maskLength) {
|
||||||
|
throw new Error('Mask length is ' + maskLength + ' whereas input is ' + bytes.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
names = names || [];
|
||||||
|
var offset = 0;
|
||||||
|
return mask
|
||||||
|
.map(function (decodeFn) {
|
||||||
|
var current = bytes.slice(offset, offset += decodeFn.BYTES);
|
||||||
|
return decodeFn(current);
|
||||||
|
})
|
||||||
|
.reduce(function (prev, cur, idx) {
|
||||||
|
prev[names[idx] || idx] = cur;
|
||||||
|
return prev;
|
||||||
|
}, {});
|
||||||
|
};
|
||||||
|
|
||||||
|
if (typeof module === 'object' && typeof module.exports !== 'undefined') {
|
||||||
|
module.exports = {
|
||||||
|
uint8: uint8,
|
||||||
|
uint16: uint16,
|
||||||
|
uint32: uint32,
|
||||||
|
int8: int8,
|
||||||
|
int16: int16,
|
||||||
|
int32: int32,
|
||||||
|
uptime: uptime,
|
||||||
|
float: float,
|
||||||
|
ufloat: ufloat,
|
||||||
|
pressure: pressure,
|
||||||
|
latLng: latLng,
|
||||||
|
hdop: hdop,
|
||||||
|
altitude: altitude,
|
||||||
|
bitmap1: bitmap1,
|
||||||
|
bitmap2: bitmap2,
|
||||||
|
version: version,
|
||||||
|
decode: decode
|
||||||
|
};
|
||||||
|
}
|
24
src/lorawan-devices-repo/vendor/opensource/esp32-paxcounter-profile-eu868.yaml
vendored
Normal file
24
src/lorawan-devices-repo/vendor/opensource/esp32-paxcounter-profile-eu868.yaml
vendored
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
# Vendor profile ID, can be freely issued by the vendor
|
||||||
|
# This vendor profile ID is also used on the QR code for LoRaWAN devices, see
|
||||||
|
# https://lora-alliance.org/sites/default/files/2020-10/LoRa_Alliance_Vendor_ID_for_QR_Code.pdf
|
||||||
|
#vendorProfileID: 0
|
||||||
|
# Whether the end device supports class B
|
||||||
|
supportsClassB: false
|
||||||
|
# Whether the end device supports class C
|
||||||
|
supportsClassC: false
|
||||||
|
# LoRaWAN MAC version: 1.0, 1.0.1, 1.0.2, 1.0.3, 1.0.4 or 1.1
|
||||||
|
macVersion: 1.0.3
|
||||||
|
# LoRaWAN Regional Parameters version. Values depend on the LoRaWAN version:
|
||||||
|
# 1.0: TS001-1.0
|
||||||
|
# 1.0.1: TS001-1.0.1
|
||||||
|
# 1.0.2: RP001-1.0.2 or RP001-1.0.2-RevB
|
||||||
|
# 1.0.3: RP001-1.0.3-RevA
|
||||||
|
# 1.0.4: RP002-1.0.0 or RP002-1.0.1
|
||||||
|
# 1.1: RP001-1.1-RevA or RP001-1.1-RevB
|
||||||
|
regionalParametersVersion: RP001-1.0.3-RevA
|
||||||
|
# Whether the end device supports join (OTAA) or not (ABP)
|
||||||
|
supportsJoin: true
|
||||||
|
# Maximum EIRP
|
||||||
|
maxEIRP: 16
|
||||||
|
# Whether the end device supports 32-bit frame counters
|
||||||
|
supports32bitFCnt: true
|
24
src/lorawan-devices-repo/vendor/opensource/esp32-paxcounter-profile-us915.yaml
vendored
Normal file
24
src/lorawan-devices-repo/vendor/opensource/esp32-paxcounter-profile-us915.yaml
vendored
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
# Vendor profile ID, can be freely issued by the vendor
|
||||||
|
# This vendor profile ID is also used on the QR code for LoRaWAN devices, see
|
||||||
|
# https://lora-alliance.org/sites/default/files/2020-10/LoRa_Alliance_Vendor_ID_for_QR_Code.pdf
|
||||||
|
#vendorProfileID: 0
|
||||||
|
# Whether the end device supports class B
|
||||||
|
supportsClassB: false
|
||||||
|
# Whether the end device supports class C
|
||||||
|
supportsClassC: false
|
||||||
|
# LoRaWAN MAC version: 1.0, 1.0.1, 1.0.2, 1.0.3, 1.0.4 or 1.1
|
||||||
|
macVersion: 1.0.3
|
||||||
|
# LoRaWAN Regional Parameters version. Values depend on the LoRaWAN version:
|
||||||
|
# 1.0: TS001-1.0
|
||||||
|
# 1.0.1: TS001-1.0.1
|
||||||
|
# 1.0.2: RP001-1.0.2 or RP001-1.0.2-RevB
|
||||||
|
# 1.0.3: RP001-1.0.3-RevA
|
||||||
|
# 1.0.4: RP002-1.0.0 or RP002-1.0.1
|
||||||
|
# 1.1: RP001-1.1-RevA or RP001-1.1-RevB
|
||||||
|
regionalParametersVersion: RP001-1.0.3-RevA
|
||||||
|
# Whether the end device supports join (OTAA) or not (ABP)
|
||||||
|
supportsJoin: true
|
||||||
|
# Maximum EIRP
|
||||||
|
maxEIRP: 30
|
||||||
|
# Whether the end device supports 32-bit frame counters
|
||||||
|
supports32bitFCnt: true
|
137
src/lorawan-devices-repo/vendor/opensource/esp32-paxcounter.yaml
vendored
Normal file
137
src/lorawan-devices-repo/vendor/opensource/esp32-paxcounter.yaml
vendored
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
name: ESP32-Paxcounter
|
||||||
|
description: Software providing a basis on the esp32 platform for multiple sensors, including pax (ble/wifi), gps, temperature, humidity, pressure, pm2.5, pm10 and more.
|
||||||
|
|
||||||
|
# Hardware versions (optional, use when you have revisions)
|
||||||
|
#hardwareVersions:
|
||||||
|
# - version: '1.0'
|
||||||
|
# numeric: 1
|
||||||
|
|
||||||
|
# Firmware versions (at least one is mandatory)
|
||||||
|
# LoRaWAN Device Profiles per region
|
||||||
|
# Supported regions are EU863-870, US902-928, AU915-928, AS923, CN779-787, EU433, CN470-510, KR920-923, IN865-867, RU864-870
|
||||||
|
firmwareVersions:
|
||||||
|
- version: '2.4.0'
|
||||||
|
numeric: 240
|
||||||
|
profiles:
|
||||||
|
EU863-870:
|
||||||
|
id: esp32-paxcounter-profile-eu868
|
||||||
|
#lorawanCertified: true
|
||||||
|
codec: esp32-paxcounter-codec
|
||||||
|
US902-928:
|
||||||
|
id: esp32-paxcounter-profile-us915
|
||||||
|
#lorawanCertified: true
|
||||||
|
codec: esp32-paxcounter-codec
|
||||||
|
|
||||||
|
- version: '3.0.0'
|
||||||
|
numeric: 300
|
||||||
|
profiles:
|
||||||
|
EU863-870:
|
||||||
|
id: esp32-paxcounter-profile-eu868
|
||||||
|
#lorawanCertified: true
|
||||||
|
codec: esp32-paxcounter-codec
|
||||||
|
US902-928:
|
||||||
|
id: esp32-paxcounter-profile-us915
|
||||||
|
#lorawanCertified: true
|
||||||
|
codec: esp32-paxcounter-codec
|
||||||
|
|
||||||
|
# Sensors that this device features (optional)
|
||||||
|
# 4-20 ma, accelerometer, altitude, analog input, auxiliary, barometer, battery, button, bvoc, co, co2, conductivity,
|
||||||
|
# current, digital input, dissolved oxygen, distance, dust, energy, gps, gyroscope, h2s, humidity, iaq, level, light,
|
||||||
|
# lightning, link, magnetometer, moisture, motion, no, no2, o3, particulate matter, ph, pir, pm2.5, pm10, potentiometer,
|
||||||
|
# power, precipitation, pressure, proximity, pulse count, pulse frequency, radar, rainfall, rssi, smart valve, snr, so2,
|
||||||
|
# solar radiation, sound, strain, surface temperature, temperature, tilt, time, tvoc, uv, vapor pressure, velocity,
|
||||||
|
# vibration, voltage, water potential, water, weight, wifi ssid, wind direction, wind speed.
|
||||||
|
sensors:
|
||||||
|
- battery
|
||||||
|
# - ble
|
||||||
|
# - wifi
|
||||||
|
# - pax
|
||||||
|
- gps
|
||||||
|
- altitude
|
||||||
|
# - latitude
|
||||||
|
# - longitude
|
||||||
|
# - hdop
|
||||||
|
# - sats
|
||||||
|
- temperature
|
||||||
|
- humidity
|
||||||
|
- barometer
|
||||||
|
- pm2.5
|
||||||
|
- pm10
|
||||||
|
|
||||||
|
# Additional radios that this device has (optional)
|
||||||
|
# Valid values are: ble, nfc, wifi, cellular.
|
||||||
|
additionalRadios:
|
||||||
|
- ble
|
||||||
|
- wifi
|
||||||
|
|
||||||
|
# Dimensions in mm (optional)
|
||||||
|
# Use width, height, length and/or diameter
|
||||||
|
#dimensions:
|
||||||
|
# width: 22.5
|
||||||
|
# length: 119
|
||||||
|
# height: 101
|
||||||
|
|
||||||
|
# Weight in grams (optional)
|
||||||
|
#weight: 160
|
||||||
|
|
||||||
|
## Operating conditions (optional)
|
||||||
|
#operatingConditions:
|
||||||
|
# Temperature (Celsius)
|
||||||
|
# temperature:
|
||||||
|
# min: -30
|
||||||
|
# max: 60
|
||||||
|
# Relative humidity (fraction of 1)
|
||||||
|
# relativeHumidity:
|
||||||
|
# min: 0
|
||||||
|
# max: 0.9
|
||||||
|
|
||||||
|
# IP rating (optional)
|
||||||
|
#ipCode: IP20
|
||||||
|
|
||||||
|
# Key provisioning (optional)
|
||||||
|
# Valid values are: custom (user can configure keys), join server and manifest.
|
||||||
|
keyProvisioning:
|
||||||
|
- custom
|
||||||
|
- join server
|
||||||
|
|
||||||
|
# Key security (optional)
|
||||||
|
# Valid values are: none, read protected and secure element.
|
||||||
|
keySecurity: none
|
||||||
|
|
||||||
|
# Product and data sheet URLs (optional)
|
||||||
|
productURL: https://github.com/cyberman54/ESP32-Paxcounter
|
||||||
|
dataSheetURL: https://github.com/cyberman54/ESP32-Paxcounter
|
||||||
|
#resellerURLs:
|
||||||
|
# - name: 'Reseller 1'
|
||||||
|
# region:
|
||||||
|
# - European Union
|
||||||
|
# url: https://example.org/reseller1
|
||||||
|
# - name: 'Reseller 2'
|
||||||
|
# region:
|
||||||
|
# - United States
|
||||||
|
# - Canada
|
||||||
|
# url: https://example.org/reseller2
|
||||||
|
|
||||||
|
# Photos
|
||||||
|
#photos:
|
||||||
|
# main: Paxcounter-title.jpg
|
||||||
|
# other:
|
||||||
|
# - Paxcounter-title.jpg
|
||||||
|
# Youtube or Vimeo Video (optional)
|
||||||
|
###video: https://www.youtube.com/watch?v=JHzxcD2oEn8
|
||||||
|
|
||||||
|
# Regulatory compliances (optional)
|
||||||
|
#compliances:
|
||||||
|
# safety:
|
||||||
|
# - body: IEC
|
||||||
|
# norm: EN
|
||||||
|
# standard: 62368-1
|
||||||
|
# radioEquipment:
|
||||||
|
# - body: ETSI
|
||||||
|
# norm: EN
|
||||||
|
# standard: 301 489-1
|
||||||
|
# version: 2.2.0
|
||||||
|
# - body: ETSI
|
||||||
|
# norm: EN
|
||||||
|
# standard: 301 489-3
|
||||||
|
# version: 2.1.0
|
3
src/lorawan-devices-repo/vendor/opensource/index.yaml
vendored
Normal file
3
src/lorawan-devices-repo/vendor/opensource/index.yaml
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
endDevices:
|
||||||
|
# Unique identifier of the end device (lowercase, alphanumeric with dashes, max 36 characters)
|
||||||
|
- esp32-paxcounter # look in esp32-paxcounter.yaml for the end device definition
|
113
src/lorawan.cpp
113
src/lorawan.cpp
@ -6,9 +6,6 @@
|
|||||||
// Local logging Tag
|
// Local logging Tag
|
||||||
static const char TAG[] = "lora";
|
static const char TAG[] = "lora";
|
||||||
|
|
||||||
// Saves the LMIC structure during deep sleep
|
|
||||||
RTC_DATA_ATTR lmic_t RTC_LMIC;
|
|
||||||
|
|
||||||
#if CLOCK_ERROR_PROCENTAGE > 7
|
#if CLOCK_ERROR_PROCENTAGE > 7
|
||||||
#warning CLOCK_ERROR_PROCENTAGE value in lmic_config.h is too high; values > 7 will cause side effects
|
#warning CLOCK_ERROR_PROCENTAGE value in lmic_config.h is too high; values > 7 will cause side effects
|
||||||
#endif
|
#endif
|
||||||
@ -265,7 +262,6 @@ esp_err_t lmic_init(void) {
|
|||||||
LMIC_registerRxMessageCb(myRxCallback, NULL);
|
LMIC_registerRxMessageCb(myRxCallback, NULL);
|
||||||
LMIC_registerEventCb(myEventCallback, NULL);
|
LMIC_registerEventCb(myEventCallback, NULL);
|
||||||
// to come with future LMIC version
|
// to come with future LMIC version
|
||||||
// LMIC_registerBattLevelCb(myBattLevelCb, NULL);
|
|
||||||
|
|
||||||
// Reset the MAC state. Session and pending data transfers will be
|
// Reset the MAC state. Session and pending data transfers will be
|
||||||
// discarded.
|
// discarded.
|
||||||
@ -412,34 +408,6 @@ void myEventCallback(void *pUserData, ev_t ev) {
|
|||||||
ESP_LOGD(TAG, "%s", lmic_event_msg);
|
ESP_LOGD(TAG, "%s", lmic_event_msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t myBattLevelCb(void *pUserData) {
|
|
||||||
|
|
||||||
// set the battery value to send by LMIC in MAC Command
|
|
||||||
// DevStatusAns. Available defines in lorabase.h:
|
|
||||||
// MCMD_DEVS_EXT_POWER = 0x00, // external power supply
|
|
||||||
// MCMD_DEVS_BATT_MIN = 0x01, // min battery value
|
|
||||||
// MCMD_DEVS_BATT_MAX = 0xFE, // max battery value
|
|
||||||
// MCMD_DEVS_BATT_NOINFO = 0xFF, // unknown battery level
|
|
||||||
// we calculate the applicable value from MCMD_DEVS_BATT_MIN to
|
|
||||||
// MCMD_DEVS_BATT_MAX from bat_percent value
|
|
||||||
|
|
||||||
uint8_t const batt_percent = read_battlevel();
|
|
||||||
|
|
||||||
if (batt_percent == 0)
|
|
||||||
return MCMD_DEVS_BATT_NOINFO;
|
|
||||||
else
|
|
||||||
|
|
||||||
#ifdef HAS_PMU
|
|
||||||
if (pmu.isVBUSPlug())
|
|
||||||
return MCMD_DEVS_EXT_POWER;
|
|
||||||
#elif defined HAS_IP5306
|
|
||||||
if (IP5306_GetPowerSource())
|
|
||||||
return MCMD_DEVS_EXT_POWER;
|
|
||||||
#endif // HAS_PMU
|
|
||||||
|
|
||||||
return (batt_percent / 100.0 * (MCMD_DEVS_BATT_MAX - MCMD_DEVS_BATT_MIN + 1));
|
|
||||||
}
|
|
||||||
|
|
||||||
// event EV_RXCOMPLETE message handler
|
// event EV_RXCOMPLETE message handler
|
||||||
void myRxCallback(void *pUserData, uint8_t port, const uint8_t *pMsg,
|
void myRxCallback(void *pUserData, uint8_t port, const uint8_t *pMsg,
|
||||||
size_t nMsg) {
|
size_t nMsg) {
|
||||||
@ -484,14 +452,70 @@ const char *getCrName(rps_t rps) {
|
|||||||
return t[getCr(rps)];
|
return t[getCr(rps)];
|
||||||
}
|
}
|
||||||
|
|
||||||
// following code snippet was taken from
|
/*******************************************************************************
|
||||||
|
*
|
||||||
|
* ttn-esp32 - The Things Network device library for ESP-IDF / SX127x
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018-2021 Manuel Bleichenbacher
|
||||||
|
*
|
||||||
|
* Licensed under MIT License
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*
|
||||||
|
* Functions for storing and retrieving TTN communication state from RTC memory.
|
||||||
|
*******************************************************************************/
|
||||||
|
|
||||||
|
#define LMIC_OFFSET(field) __builtin_offsetof(struct lmic_t, field)
|
||||||
|
#define LMIC_DIST(field1, field2) (LMIC_OFFSET(field2) - LMIC_OFFSET(field1))
|
||||||
|
#define TTN_RTC_MEM_SIZE \
|
||||||
|
(sizeof(struct lmic_t) - LMIC_OFFSET(radio) - MAX_LEN_PAYLOAD - MAX_LEN_FRAME)
|
||||||
|
|
||||||
|
#define TTN_RTC_FLAG_VALUE 0xf8025b8a
|
||||||
|
|
||||||
|
RTC_DATA_ATTR uint8_t ttn_rtc_mem_buf[TTN_RTC_MEM_SIZE];
|
||||||
|
RTC_DATA_ATTR uint32_t ttn_rtc_flag;
|
||||||
|
|
||||||
|
void ttn_rtc_save() {
|
||||||
|
// Copy LMIC struct except client, osjob, pendTxData and frame
|
||||||
|
size_t len1 = LMIC_DIST(radio, pendTxData);
|
||||||
|
memcpy(ttn_rtc_mem_buf, &LMIC.radio, len1);
|
||||||
|
size_t len2 = LMIC_DIST(pendTxData, frame) - MAX_LEN_PAYLOAD;
|
||||||
|
memcpy(ttn_rtc_mem_buf + len1, (u1_t *)&LMIC.pendTxData + MAX_LEN_PAYLOAD,
|
||||||
|
len2);
|
||||||
|
size_t len3 = sizeof(struct lmic_t) - LMIC_OFFSET(frame) - MAX_LEN_FRAME;
|
||||||
|
memcpy(ttn_rtc_mem_buf + len1 + len2, (u1_t *)&LMIC.frame + MAX_LEN_FRAME,
|
||||||
|
len3);
|
||||||
|
|
||||||
|
ttn_rtc_flag = TTN_RTC_FLAG_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ttn_rtc_restore() {
|
||||||
|
if (ttn_rtc_flag != TTN_RTC_FLAG_VALUE)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Restore data
|
||||||
|
size_t len1 = LMIC_DIST(radio, pendTxData);
|
||||||
|
memcpy(&LMIC.radio, ttn_rtc_mem_buf, len1);
|
||||||
|
memset(LMIC.pendTxData, 0, MAX_LEN_PAYLOAD);
|
||||||
|
size_t len2 = LMIC_DIST(pendTxData, frame) - MAX_LEN_PAYLOAD;
|
||||||
|
memcpy((u1_t *)&LMIC.pendTxData + MAX_LEN_PAYLOAD, ttn_rtc_mem_buf + len1,
|
||||||
|
len2);
|
||||||
|
memset(LMIC.frame, 0, MAX_LEN_FRAME);
|
||||||
|
size_t len3 = sizeof(struct lmic_t) - LMIC_OFFSET(frame) - MAX_LEN_FRAME;
|
||||||
|
memcpy((u1_t *)&LMIC.frame + MAX_LEN_FRAME, ttn_rtc_mem_buf + len1 + len2,
|
||||||
|
len3);
|
||||||
|
|
||||||
|
ttn_rtc_flag = 0xffffffff; // invalidate RTC data
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// following code includes snippets taken from
|
||||||
// https://github.com/JackGruber/ESP32-LMIC-DeepSleep-example/blob/master/src/main.cpp
|
// https://github.com/JackGruber/ESP32-LMIC-DeepSleep-example/blob/master/src/main.cpp
|
||||||
|
|
||||||
void SaveLMICToRTC(int deepsleep_sec) {
|
void SaveLMICToRTC(int deepsleep_sec) {
|
||||||
RTC_LMIC = LMIC;
|
|
||||||
|
|
||||||
// ESP32 can't track millis during DeepSleep and no option to advance
|
// ESP32 can't track millis during DeepSleep and no option to advance
|
||||||
// millis after DeepSleep. Therefore reset DutyCyles
|
// millis after DeepSleep. Therefore reset DutyCyles before saving LMIC struct
|
||||||
|
|
||||||
unsigned long now = millis();
|
unsigned long now = millis();
|
||||||
|
|
||||||
@ -499,29 +523,34 @@ void SaveLMICToRTC(int deepsleep_sec) {
|
|||||||
#if CFG_LMIC_EU_like
|
#if CFG_LMIC_EU_like
|
||||||
for (int i = 0; i < MAX_BANDS; i++) {
|
for (int i = 0; i < MAX_BANDS; i++) {
|
||||||
ostime_t correctedAvail =
|
ostime_t correctedAvail =
|
||||||
RTC_LMIC.bands[i].avail -
|
LMIC.bands[i].avail -
|
||||||
((now / 1000.0 + deepsleep_sec) * OSTICKS_PER_SEC);
|
((now / 1000.0 + deepsleep_sec) * OSTICKS_PER_SEC);
|
||||||
if (correctedAvail < 0) {
|
if (correctedAvail < 0) {
|
||||||
correctedAvail = 0;
|
correctedAvail = 0;
|
||||||
}
|
}
|
||||||
RTC_LMIC.bands[i].avail = correctedAvail;
|
LMIC.bands[i].avail = correctedAvail;
|
||||||
}
|
}
|
||||||
|
|
||||||
RTC_LMIC.globalDutyAvail = RTC_LMIC.globalDutyAvail -
|
LMIC.globalDutyAvail =
|
||||||
((now / 1000.0 + deepsleep_sec) * OSTICKS_PER_SEC);
|
LMIC.globalDutyAvail - ((now / 1000.0 + deepsleep_sec) * OSTICKS_PER_SEC);
|
||||||
if (RTC_LMIC.globalDutyAvail < 0) {
|
if (LMIC.globalDutyAvail < 0) {
|
||||||
RTC_LMIC.globalDutyAvail = 0;
|
LMIC.globalDutyAvail = 0;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
ESP_LOGW(TAG, "No DutyCycle recalculation function!");
|
ESP_LOGW(TAG, "No DutyCycle recalculation function!");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
ttn_rtc_save();
|
||||||
ESP_LOGI(TAG, "LMIC state saved");
|
ESP_LOGI(TAG, "LMIC state saved");
|
||||||
}
|
}
|
||||||
|
|
||||||
void LoadLMICFromRTC() {
|
void LoadLMICFromRTC() {
|
||||||
LMIC = RTC_LMIC;
|
if (ttn_rtc_restore())
|
||||||
ESP_LOGI(TAG, "LMIC state loaded");
|
ESP_LOGI(TAG, "LMIC state loaded");
|
||||||
|
else {
|
||||||
|
ESP_LOGE(TAG, "LMIC state not found - resetting device");
|
||||||
|
do_reset(false); // coldstart
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // HAS_LORA
|
#endif // HAS_LORA
|
@ -118,14 +118,10 @@ void setup() {
|
|||||||
// load device configuration from NVRAM and set runmode
|
// load device configuration from NVRAM and set runmode
|
||||||
do_after_reset();
|
do_after_reset();
|
||||||
|
|
||||||
// set time zone to user value from paxcounter.conf
|
|
||||||
#ifdef TIME_SYNC_TIMEZONE
|
|
||||||
myTZ.setPosix(TIME_SYNC_TIMEZONE);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// hash 6 byte device MAC to 4 byte clientID
|
// hash 6 byte device MAC to 4 byte clientID
|
||||||
uint8_t mac[6];
|
uint8_t mac[6];
|
||||||
esp_eth_get_mac(mac);
|
esp_read_mac(mac, ESP_MAC_WIFI_STA);
|
||||||
|
|
||||||
const uint32_t hashedmac = myhash((const char *)mac, 6);
|
const uint32_t hashedmac = myhash((const char *)mac, 6);
|
||||||
snprintf(clientId, 20, "paxcounter_%08x", hashedmac);
|
snprintf(clientId, 20, "paxcounter_%08x", hashedmac);
|
||||||
ESP_LOGI(TAG, "Starting %s v%s (runmode=%d / restarts=%d)", clientId,
|
ESP_LOGI(TAG, "Starting %s v%s (runmode=%d / restarts=%d)", clientId,
|
||||||
@ -302,6 +298,7 @@ void setup() {
|
|||||||
// configure BLE sniffing
|
// configure BLE sniffing
|
||||||
configuration.blecounter = cfg.blescan;
|
configuration.blecounter = cfg.blescan;
|
||||||
configuration.blescantime = cfg.blescantime;
|
configuration.blescantime = cfg.blescantime;
|
||||||
|
configuration.ble_rssi_threshold = cfg.rssilimit;
|
||||||
ESP_LOGI(TAG, "BLESCAN: %s", cfg.blescan ? "on" : "off");
|
ESP_LOGI(TAG, "BLESCAN: %s", cfg.blescan ? "on" : "off");
|
||||||
|
|
||||||
int config_update = libpax_update_config(&configuration);
|
int config_update = libpax_update_config(&configuration);
|
||||||
|
@ -80,7 +80,7 @@
|
|||||||
#define RESPONSE_TIMEOUT_MS 60000 // firmware binary server connection timeout [milliseconds]
|
#define RESPONSE_TIMEOUT_MS 60000 // firmware binary server connection timeout [milliseconds]
|
||||||
|
|
||||||
// settings for syncing time of node with a time source (network / gps / rtc / timeserver)
|
// settings for syncing time of node with a time source (network / gps / rtc / timeserver)
|
||||||
#define TIME_SYNC_LORAWAN 0 // set to 1 to use LORA network as time source, 0 means off [default = 1]
|
#define TIME_SYNC_LORAWAN 1 // set to 1 to use LORA network as time source, 0 means off [default = 1]
|
||||||
#define TIME_SYNC_LORASERVER 0 // set to 1 to use LORA timeserver as time source, 0 means off [default = 0]
|
#define TIME_SYNC_LORASERVER 0 // set to 1 to use LORA timeserver as time source, 0 means off [default = 0]
|
||||||
#define TIME_SYNC_INTERVAL 60 // sync time attempt each .. minutes from time source [default = 60], 0 means off
|
#define TIME_SYNC_INTERVAL 60 // sync time attempt each .. minutes from time source [default = 60], 0 means off
|
||||||
#define TIME_SYNC_INTERVAL_RETRY 10 // retry time sync after lost sync each .. minutes [default = 10], 0 means off
|
#define TIME_SYNC_INTERVAL_RETRY 10 // retry time sync after lost sync each .. minutes [default = 10], 0 means off
|
||||||
|
@ -258,17 +258,48 @@ uint16_t read_voltage(void) {
|
|||||||
|
|
||||||
uint8_t read_battlevel(mapFn_t mapFunction) {
|
uint8_t read_battlevel(mapFn_t mapFunction) {
|
||||||
// returns the estimated battery level in values 0 ... 100 [percent]
|
// returns the estimated battery level in values 0 ... 100 [percent]
|
||||||
|
uint8_t batt_percent = 0;
|
||||||
#ifdef HAS_IP5306
|
#ifdef HAS_IP5306
|
||||||
return IP5306_GetBatteryLevel();
|
batt_percent = IP5306_GetBatteryLevel();
|
||||||
#else
|
#else
|
||||||
const uint16_t batt_voltage = read_voltage();
|
const uint16_t batt_voltage = read_voltage();
|
||||||
if (batt_voltage <= BAT_MIN_VOLTAGE)
|
if (batt_voltage <= BAT_MIN_VOLTAGE)
|
||||||
return 0;
|
batt_percent = 0;
|
||||||
else if (batt_voltage >= BAT_MAX_VOLTAGE)
|
else if (batt_voltage >= BAT_MAX_VOLTAGE)
|
||||||
return 100;
|
batt_percent = 100;
|
||||||
else
|
else
|
||||||
return (*mapFunction)(batt_voltage, BAT_MIN_VOLTAGE, BAT_MAX_VOLTAGE);
|
batt_percent =
|
||||||
|
(*mapFunction)(batt_voltage, BAT_MIN_VOLTAGE, BAT_MAX_VOLTAGE);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if (HAS_LORA)
|
||||||
|
// set the battery status value to send by LMIC in MAC Command
|
||||||
|
// DevStatusAns. Available defines in lorabase.h:
|
||||||
|
// MCMD_DEVS_EXT_POWER = 0x00, // external power supply
|
||||||
|
// MCMD_DEVS_BATT_MIN = 0x01, // min battery value
|
||||||
|
// MCMD_DEVS_BATT_MAX = 0xFE, // max battery value
|
||||||
|
// MCMD_DEVS_BATT_NOINFO = 0xFF, // unknown battery level
|
||||||
|
// we calculate the applicable value from MCMD_DEVS_BATT_MIN to
|
||||||
|
// MCMD_DEVS_BATT_MAX from batt_percent value
|
||||||
|
|
||||||
|
if (batt_percent == 0)
|
||||||
|
LMIC_setBatteryLevel(MCMD_DEVS_BATT_NOINFO);
|
||||||
|
else
|
||||||
|
LMIC_setBatteryLevel(batt_percent / 100.0 *
|
||||||
|
(MCMD_DEVS_BATT_MAX - MCMD_DEVS_BATT_MIN + 1));
|
||||||
|
|
||||||
|
// overwrite calculated value if we have external power
|
||||||
|
#ifdef HAS_PMU
|
||||||
|
if (pmu.isVBUSPlug())
|
||||||
|
LMIC_setBatteryLevel(MCMD_DEVS_EXT_POWER);
|
||||||
|
#elif defined HAS_IP5306
|
||||||
|
if (IP5306_GetPowerSource())
|
||||||
|
LMIC_setBatteryLevel(MCMD_DEVS_EXT_POWER);
|
||||||
|
#endif // HAS_PMU
|
||||||
|
|
||||||
|
#endif // HAS_LORA
|
||||||
|
|
||||||
|
return batt_percent;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool batt_sufficient() {
|
bool batt_sufficient() {
|
||||||
|
@ -60,6 +60,7 @@ void set_rssi(uint8_t val[]) {
|
|||||||
libpax_config_t current_config;
|
libpax_config_t current_config;
|
||||||
libpax_get_current_config(¤t_config);
|
libpax_get_current_config(¤t_config);
|
||||||
current_config.wifi_rssi_threshold = cfg.rssilimit;
|
current_config.wifi_rssi_threshold = cfg.rssilimit;
|
||||||
|
current_config.ble_rssi_threshold = cfg.rssilimit;
|
||||||
libpax_update_config(¤t_config);
|
libpax_update_config(¤t_config);
|
||||||
init_libpax();
|
init_libpax();
|
||||||
#endif
|
#endif
|
||||||
|
@ -47,6 +47,12 @@ void do_after_reset(void) {
|
|||||||
// read (and initialize on first run) runtime settings from NVRAM
|
// read (and initialize on first run) runtime settings from NVRAM
|
||||||
loadConfig();
|
loadConfig();
|
||||||
|
|
||||||
|
// set time zone to user value from paxcounter.conf
|
||||||
|
#ifdef TIME_SYNC_TIMEZONE
|
||||||
|
myTZ.setPosix(TIME_SYNC_TIMEZONE);
|
||||||
|
ESP_LOGD(TAG, "Timezone set to %s", myTZ.getPosix().c_str());
|
||||||
|
#endif
|
||||||
|
|
||||||
switch (rtc_get_reset_reason(0)) {
|
switch (rtc_get_reset_reason(0)) {
|
||||||
|
|
||||||
case POWERON_RESET: // 0x01 Vbat power on reset
|
case POWERON_RESET: // 0x01 Vbat power on reset
|
||||||
@ -69,8 +75,8 @@ void do_after_reset(void) {
|
|||||||
RTC_millis += sleep_time_ms; // increment system monotonic time
|
RTC_millis += sleep_time_ms; // increment system monotonic time
|
||||||
ESP_LOGI(TAG, "Time spent in deep sleep: %d ms", sleep_time_ms);
|
ESP_LOGI(TAG, "Time spent in deep sleep: %d ms", sleep_time_ms);
|
||||||
|
|
||||||
// set time
|
// restore time
|
||||||
setMyTime(RTC_time + sleep_time_ms / 1000, sleep_time_ms % 1000, _set);
|
setMyTime(RTC_time, sleep_time_ms, _set);
|
||||||
|
|
||||||
// set wakeup state, not if we have pending OTA update
|
// set wakeup state, not if we have pending OTA update
|
||||||
if (RTC_runmode == RUNMODE_SLEEP)
|
if (RTC_runmode == RUNMODE_SLEEP)
|
||||||
@ -132,7 +138,7 @@ void enter_deepsleep(const uint64_t wakeup_sec, gpio_num_t wakeup_gpio) {
|
|||||||
#if (HAS_LORA)
|
#if (HAS_LORA)
|
||||||
ESP_LOGI(TAG, "Waiting until LMIC is idle...");
|
ESP_LOGI(TAG, "Waiting until LMIC is idle...");
|
||||||
for (i = 100; i > 0; i--) {
|
for (i = 100; i > 0; i--) {
|
||||||
if ((LMIC.opmode & OP_TXRXPEND) ||
|
if ((LMIC.opmode & (OP_JOINING | OP_TXDATA | OP_POLL | OP_TXRXPEND)) ||
|
||||||
os_queryTimeCriticalJobs(sec2osticks(wakeup_sec)))
|
os_queryTimeCriticalJobs(sec2osticks(wakeup_sec)))
|
||||||
vTaskDelay(pdMS_TO_TICKS(1000));
|
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||||
else
|
else
|
||||||
|
@ -121,7 +121,7 @@ void IRAM_ATTR setMyTime(uint32_t t_sec, uint16_t t_msec,
|
|||||||
_seconds(), mytimesource);
|
_seconds(), mytimesource);
|
||||||
} else {
|
} else {
|
||||||
timesyncer.attach(TIME_SYNC_INTERVAL_RETRY * 60, setTimeSyncIRQ);
|
timesyncer.attach(TIME_SYNC_INTERVAL_RETRY * 60, setTimeSyncIRQ);
|
||||||
ESP_LOGD(TAG,
|
ESP_LOGV(TAG,
|
||||||
"[%0.3f] Failed to synchronise time from source %c | unix sec "
|
"[%0.3f] Failed to synchronise time from source %c | unix sec "
|
||||||
"obtained from source: %d | unix sec at program compilation: %d",
|
"obtained from source: %d | unix sec at program compilation: %d",
|
||||||
_seconds(), timeSetSymbols[mytimesource], time_to_set,
|
_seconds(), timeSetSymbols[mytimesource], time_to_set,
|
||||||
@ -180,8 +180,10 @@ void timepulse_start(void) {
|
|||||||
timerAlarmEnable(ppsIRQ);
|
timerAlarmEnable(ppsIRQ);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// start cyclic time sync
|
// get time if we don't have one
|
||||||
|
if (timeSource != _set)
|
||||||
setTimeSyncIRQ(); // init systime by RTC or GPS or LORA
|
setTimeSyncIRQ(); // init systime by RTC or GPS or LORA
|
||||||
|
// start cyclic time sync
|
||||||
timesyncer.attach(TIME_SYNC_INTERVAL * 60, setTimeSyncIRQ);
|
timesyncer.attach(TIME_SYNC_INTERVAL * 60, setTimeSyncIRQ);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -131,19 +131,19 @@ void IRAM_ATTR timesync_processReq(void *taskparameter) {
|
|||||||
// calculate average time offset over the summed up difference
|
// calculate average time offset over the summed up difference
|
||||||
time_offset_ms /= TIME_SYNC_SAMPLES;
|
time_offset_ms /= TIME_SYNC_SAMPLES;
|
||||||
|
|
||||||
|
// add milliseconds from latest gateway time, and apply a compensation
|
||||||
|
// constant for processing times on node and gateway, strip full seconds
|
||||||
|
time_offset_ms += timesync_timestamp[sample_idx - 1][gwtime_msec];
|
||||||
|
time_offset_ms -= TIME_SYNC_FIXUP;
|
||||||
|
time_offset_ms %= 1000;
|
||||||
|
|
||||||
// take latest timestamp received from gateway
|
// take latest timestamp received from gateway
|
||||||
// and add time difference rounded to whole seconds
|
// and add time difference rounded to whole seconds
|
||||||
time_offset_sec = timesync_timestamp[sample_idx - 1][gwtime_sec];
|
time_offset_sec = timesync_timestamp[sample_idx - 1][gwtime_sec];
|
||||||
time_offset_sec += time_offset_ms / 1000;
|
time_offset_sec += time_offset_ms / 1000;
|
||||||
|
|
||||||
// add milliseconds from latest gateway time, and apply a compensation
|
|
||||||
// constant for processing times on node and gateway, strip full seconds
|
|
||||||
time_offset_ms += timesync_timestamp[sample_idx - 1][gwtime_msec];
|
|
||||||
time_offset_ms += TIME_SYNC_FIXUP;
|
|
||||||
time_offset_ms %= 1000;
|
|
||||||
|
|
||||||
ESP_LOGD(TAG, "LORA date/time: %s",
|
ESP_LOGD(TAG, "LORA date/time: %s",
|
||||||
myTZ.dateTime(time_offset_sec, "d.M Y H:i:s T").c_str());
|
myTZ.dateTime(time_offset_sec, "d.M Y H:i:s.v T").c_str());
|
||||||
|
|
||||||
setMyTime(time_offset_sec, time_offset_ms, _lora);
|
setMyTime(time_offset_sec, time_offset_ms, _lora);
|
||||||
|
|
||||||
@ -168,7 +168,7 @@ void IRAM_ATTR timesync_processReq(void *taskparameter) {
|
|||||||
|
|
||||||
// store incoming timestamps
|
// store incoming timestamps
|
||||||
void timesync_store(uint32_t timestamp, timesync_t timestamp_type) {
|
void timesync_store(uint32_t timestamp, timesync_t timestamp_type) {
|
||||||
ESP_LOGD(TAG, "[%0.3f] seq#%d[%d]: t%d=%d", _seconds(), time_sync_seqNo,
|
ESP_LOGD(TAG, "[%0.3f] seq#%d[%d]: t%d=%d", _seconds(), time_sync_seqNo - 1,
|
||||||
sample_idx, timestamp_type, timestamp);
|
sample_idx, timestamp_type, timestamp);
|
||||||
timesync_timestamp[sample_idx][timestamp_type] = timestamp;
|
timesync_timestamp[sample_idx][timestamp_type] = timestamp;
|
||||||
}
|
}
|
||||||
@ -230,7 +230,7 @@ void IRAM_ATTR timesync_serverAnswer(void *pUserData, int flag) {
|
|||||||
// pUserData: contains pointer to SeqNo (not needed here)
|
// pUserData: contains pointer to SeqNo (not needed here)
|
||||||
// flag: indicates if we got a recent time from the network
|
// flag: indicates if we got a recent time from the network
|
||||||
|
|
||||||
uint32_t delay_msec;
|
int32_t delay_msec;
|
||||||
lmic_time_reference_t lmicTime;
|
lmic_time_reference_t lmicTime;
|
||||||
|
|
||||||
if (flag != 1) {
|
if (flag != 1) {
|
||||||
@ -239,14 +239,16 @@ void IRAM_ATTR timesync_serverAnswer(void *pUserData, int flag) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Populate lmic_time_reference
|
// Populate lmic_time_reference
|
||||||
if ((LMIC_getNetworkTimeReference(&lmicTime)) != 1) {
|
flag = LMIC_getNetworkTimeReference(&lmicTime);
|
||||||
|
if (flag != 1) {
|
||||||
ESP_LOGW(TAG, "[%0.3f] Network time request failed", _seconds());
|
ESP_LOGW(TAG, "[%0.3f] Network time request failed", _seconds());
|
||||||
goto Exit;
|
goto Exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate UTCTime, considering the difference between GPS and UTC time
|
// Calculate UTCTime, considering the difference between GPS and UTC time
|
||||||
// epoch, and the leap seconds
|
// epoch, and the leap seconds
|
||||||
timestamp_sec = lmicTime.tNetwork + GPS_UTC_DIFF;
|
timestamp_sec = lmicTime.tNetwork + GPS_UTC_DIFF - LEAP_SECS_SINCE_GPSEPOCH;
|
||||||
|
ESP_LOGD(TAG, "lmicTime.tNetwork = %d", timestamp_sec);
|
||||||
|
|
||||||
// Add the delay between the instant the time was transmitted and
|
// Add the delay between the instant the time was transmitted and
|
||||||
// the current time
|
// the current time
|
||||||
|
Loading…
Reference in New Issue
Block a user