Merge pull request #660 from cyberman54/development

v2.0.2
This commit is contained in:
Verkehrsrot 2020-10-09 23:16:27 +02:00 committed by GitHub
commit 9405042110
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
32 changed files with 471 additions and 523 deletions

View File

@ -83,12 +83,16 @@ By default bluetooth sniffing not installed (#define *BLECOUNTER* 0 in paxcounte
# Preparing # Preparing
## Install Platformio
Install <A HREF="https://platformio.org/">PlatformIO IDE for embedded development</A> to make this project. Platformio integrates with your favorite IDE, choose eg. Visual Studio, Atom, Eclipse etc.
Compile time configuration is spread across several files. Before compiling the code, edit or create the following files: Compile time configuration is spread across several files. Before compiling the code, edit or create the following files:
## platformio_orig.ini ## platformio.ini
Edit `platformio_orig.ini` and select desired hardware target in section boards. To add a new board, create an appropriate hardware abstraction layer file in hal subdirectory, and add a pointer to this file in sections board. Copy or rename to `platformio.ini`. Edit `platformio_orig.ini` and select desired hardware target in section boards. To add a new board, create an appropriate hardware abstraction layer file in hal subdirectory, and add a pointer to this file in sections board. Copy or rename to `platformio.ini` in the root directory of the project. Now start Platformio. Note: Platformio is looking for `platformio.ini` in the root directory and won't start if it does not find this file.
## src/paxcounter_orig.conf ## src/paxcounter.conf
Edit `src/paxcounter_orig.conf` and tailor settings in this file according to your needs and use case. Please take care of the duty cycle regulations of the LoRaWAN network you're going to use. Copy or rename to `src/paxcounter.conf`. Edit `src/paxcounter_orig.conf` and tailor settings in this file according to your needs and use case. Please take care of the duty cycle regulations of the LoRaWAN network you're going to use. Copy or rename to `src/paxcounter.conf`.
If your device has a **real time clock** it can be updated bei either LoRaWAN network or GPS time, according to settings *TIME_SYNC_INTERVAL* and *TIME_SYNC_LORAWAN* in `paxcounter.conf`. If your device has a **real time clock** it can be updated bei either LoRaWAN network or GPS time, according to settings *TIME_SYNC_INTERVAL* and *TIME_SYNC_LORAWAN* in `paxcounter.conf`.
@ -205,9 +209,9 @@ Follow all steps so far for preparing the device, use the packed payload format.
There in the sensor configuration select "TheThingsNetwork" and set Decoding Profil to "LoRa serialization", enter your TTN Application and Device Id. Decoding option has to be There in the sensor configuration select "TheThingsNetwork" and set Decoding Profil to "LoRa serialization", enter your TTN Application and Device Id. Decoding option has to be
[{"decoder":"latLng"},{"decoder":"uint16","sensor_id":"yoursensorid"}] [{"decoder":"latLng"},{"decoder":"uint16","sensor_id":"yoursensorid"}]
# Covid-19 Exposure Notification System beacon detection (Germany: "Corona Warn App counter") # Covid-19 Exposure Notification System beacon detection
Bluetooth low energy service UUID 0xFD6F, used by Google/Apple COVID-19 Exposure Notification System, can be monitored and counted. By comparing with the total number of observed devices this gives an indication how many people staying in proximity are using Apps for tracing COVID-19 exposures, e.g. in Germany the "Corona Warn App". To achive best resulta withs this funcion, use following settings in [paxcounter.conf](src/paxcounter.conf): Bluetooth low energy service UUID 0xFD6F, used by Google/Apple COVID-19 Exposure Notification System, can be monitored and counted. By comparing with the total number of observed devices this <A HREF="https://linux-fuer-wi.blogspot.com/2020/10/suche-die-zahl-64879.html">gives an indication</A> how many people staying in proximity are using Apps for tracing COVID-19 exposures, e.g. in Germany the "Corona Warn App". To achive best results with this funcion, use following settings in [paxcounter.conf](src/paxcounter.conf):
#define COUNT_ENS 1 // enable ENS monitoring function #define COUNT_ENS 1 // enable ENS monitoring function
#define VENDORFILTER 0 // disable OUI filter (scans ALL device MACs) #define VENDORFILTER 0 // disable OUI filter (scans ALL device MACs)
@ -505,6 +509,11 @@ Send for example `8386` as Downlink on Port 2 to get battery status and time/dat
0 = disabled 0 = disabled
1 = enabled [default] 1 = enabled [default]
0x18 set ENS counter on/off
0 = disabled [default]
1 = enabled
0x80 get device configuration 0x80 get device configuration
Device answers with it's current configuration on Port 3. Device answers with it's current configuration on Port 3.

View File

@ -61,7 +61,7 @@ const uint8_t bsec_config_iaq[454] = {
// Helper functions declarations // Helper functions declarations
int bme_init(); int bme_init();
void bmecycle(void); void setBMEIRQ(void);
void bme_storedata(bmeStatus_t *bme_store); void bme_storedata(bmeStatus_t *bme_store);
int checkIaqSensorStatus(void); int checkIaqSensorStatus(void);
void loadState(void); void loadState(void);

View File

@ -1,11 +1,12 @@
#ifndef _CONFIGMANAGER_H #ifndef _CONFIGMANAGER_H
#define _CONFIGMANAGER_H #define _CONFIGMANAGER_H
#include <nvs.h> #include "globals.h"
#include <nvs_flash.h> #include <Preferences.h>
void saveConfig(bool erase = false);
bool loadConfig(void);
void eraseConfig(void); void eraseConfig(void);
void saveConfig(void); int version_compare(const String v1, const String v2);
void loadConfig(void);
#endif #endif

View File

@ -11,9 +11,9 @@
#include "sds011read.h" #include "sds011read.h"
#include "sdcard.h" #include "sdcard.h"
extern Ticker housekeeper; extern Ticker cyclicTimer;
void housekeeping(void); void setCyclicIRQ(void);
void doHousekeeping(void); void doHousekeeping(void);
uint64_t uptime(void); uint64_t uptime(void);
void reset_counters(void); void reset_counters(void);

View File

@ -60,7 +60,10 @@ enum runmode_t {
}; };
// Struct holding devices's runtime configuration // Struct holding devices's runtime configuration
typedef struct { // using packed to avoid compiler padding, because struct will be memcpy'd to
// byte array
typedef struct __attribute__((packed)) {
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
@ -79,9 +82,12 @@ typedef struct {
uint8_t monitormode; // 0=disabled, 1=enabled uint8_t monitormode; // 0=disabled, 1=enabled
uint8_t runmode; // 0=normal, 1=update uint8_t runmode; // 0=normal, 1=update
uint8_t payloadmask; // bitswitches for payload data uint8_t payloadmask; // bitswitches for payload data
char version[10]; // Firmware version uint8_t enscount; // 0=disabled 1= enabled
#ifdef HAS_BME680
uint8_t uint8_t
bsecstate[BSEC_MAX_STATE_BLOB_SIZE + 1]; // BSEC state for BME680 sensor bsecstate[BSEC_MAX_STATE_BLOB_SIZE + 1]; // BSEC state for BME680 sensor
#endif
} configData_t; } configData_t;
// Struct holding payload for data send queue // Struct holding payload for data send queue

View File

@ -26,7 +26,7 @@ extern TaskHandle_t mqttTask;
void mqtt_enqueuedata(MessageBuffer_t *message); void mqtt_enqueuedata(MessageBuffer_t *message);
void mqtt_queuereset(void); void mqtt_queuereset(void);
void mqtt_irq(void); void setMqttIRQ(void);
void mqtt_loop(void); void mqtt_loop(void);
void mqtt_client_task(void *param); void mqtt_client_task(void *param);
int mqtt_connect(const char *my_host, const uint16_t my_port); int mqtt_connect(const char *my_host, const uint16_t my_port);

View File

@ -6,17 +6,15 @@
#include "globals.h" #include "globals.h"
#include "led.h" #include "led.h"
#include "display.h" #include "display.h"
#include "configmanager.h"
#include <Update.h> #include <Update.h>
#include <WiFi.h> #include <WiFi.h>
#include <WiFiClientSecure.h> #include <WiFiClientSecure.h>
#include <BintrayClient.h> #include <BintrayClient.h>
#include <string>
#include <algorithm>
int do_ota_update(); int do_ota_update();
void start_ota_update(); void start_ota_update();
int version_compare(const String v1, const String v2);
void ota_display(const uint8_t row, const std::string status, void ota_display(const uint8_t row, const std::string status,
const std::string msg); const std::string msg);
void show_progress(unsigned long current, unsigned long size); void show_progress(unsigned long current, unsigned long size);

View File

@ -10,12 +10,12 @@
#include "sdcard.h" #include "sdcard.h"
#include "corona.h" #include "corona.h"
extern Ticker sendcycler; extern Ticker sendTimer;
void SendPayload(uint8_t port, sendprio_t prio); void SendPayload(uint8_t port, sendprio_t prio);
void sendData(void); void sendData(void);
void checkSendQueues(void); void checkSendQueues(void);
void flushQueues(); void flushQueues();
void sendcycle(void); void setSendIRQ(void);
#endif // _SENDDATA_H_ #endif // _SENDDATA_H_

View File

@ -17,7 +17,7 @@ void IRAM_ATTR CLOCKIRQ(void);
void clock_init(void); void clock_init(void);
void clock_loop(void *pvParameters); void clock_loop(void *pvParameters);
void timepulse_start(void); void timepulse_start(void);
void timeSync(void); void setTimeSyncIRQ(void);
uint8_t timepulse_init(void); uint8_t timepulse_init(void);
time_t timeIsValid(time_t const t); time_t timeIsValid(time_t const t);
void calibrateTime(void); void calibrateTime(void);

View File

@ -46,7 +46,7 @@ description = Paxcounter is a device for metering passenger flows in realtime. I
[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 = 2.0.15 release_version = 2.0.2
; 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
@ -61,7 +61,7 @@ display_library = ; set by build.py and taken from hal file
lib_deps_lora = lib_deps_lora =
mcci-catena/MCCI LoRaWAN LMIC library @ ^3.2.0 mcci-catena/MCCI LoRaWAN LMIC library @ ^3.2.0
lib_deps_display = lib_deps_display =
bitbank2/OneBitDisplay @ 1.5.0 bitbank2/OneBitDisplay @ 1.7.2
ricmoo/QRCode @ ^0.0.1 ricmoo/QRCode @ ^0.0.1
bodmer/TFT_eSPI @ ^2.2.20 bodmer/TFT_eSPI @ ^2.2.20
lib_deps_ledmatrix = lib_deps_ledmatrix =
@ -72,7 +72,7 @@ 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.0 adafruit/Adafruit BME280 Library @ ^2.1.1
adafruit/Adafruit BMP085 Library @ ^1.1.0 adafruit/Adafruit BMP085 Library @ ^1.1.0
boschsensortec/BSEC Software Library @ 1.5.1474 boschsensortec/BSEC Software Library @ 1.5.1474
https://github.com/ricki-z/SDS011.git https://github.com/ricki-z/SDS011.git
@ -95,10 +95,11 @@ lib_deps_all =
build_flags_basic = build_flags_basic =
-include "src/hal/${board.halfile}" -include "src/hal/${board.halfile}"
-include "src/paxcounter.conf" -include "src/paxcounter.conf"
-w
'-DCORE_DEBUG_LEVEL=${common.debug_level}' '-DCORE_DEBUG_LEVEL=${common.debug_level}'
'-DLOG_LOCAL_LEVEL=${common.debug_level}' '-DLOG_LOCAL_LEVEL=${common.debug_level}'
'-DPROGVERSION="${common.release_version}"' '-DPROGVERSION="${common.release_version}"'
'-Wno-unknown-pragmas'
'-Wno-unused-variable'
build_flags_sensors = build_flags_sensors =
-Llib/Bosch-BSEC/src/esp32/ -Llib/Bosch-BSEC/src/esp32/
-lalgobsec -lalgobsec

View File

@ -257,7 +257,7 @@ var bitmap2 = function (byte) {
} }
var i = bytesToInt(byte); var i = bytesToInt(byte);
var bm = ('00000000' + Number(i).toString(2)).substr(-8).split('').map(Number).map(Boolean); var bm = ('00000000' + Number(i).toString(2)).substr(-8).split('').map(Number).map(Boolean);
return ['gps', 'alarm', 'bme', 'counter', 'sensor1', 'sensor2', 'sensor3', 'battery'] return ['battery', 'sensor3', 'sensor2', 'sensor1', 'counter', 'bme', 'alarm', 'gps']
.reduce(function (obj, pos, index) { .reduce(function (obj, pos, index) {
obj[pos] = +bm[index]; obj[pos] = +bm[index];
return obj; return obj;

View File

@ -1,41 +1,4 @@
[ [
{
"id": "b8bd33fd.61caa",
"type": "function",
"z": "449c1517.e25f4c",
"name": "Timeserver Logic",
"func": "/* LoRaWAN Timeserver\n\nVERSION: 1.3\n\nconstruct 6 byte timesync_answer from gateway timestamp and node's time_sync_req\n\nbyte meaning\n1 sequence number (taken from node's time_sync_req)\n2..5 current second (from GPS epoch starting 1980)\n6 1/250ths fractions of current second\n\n*/\n\nfunction timecompare(a, b) {\n \n const timeA = a.time;\n const timeB = b.time;\n\n let comparison = 0;\n if (timeA > timeB) {\n comparison = 1;\n } else if (timeA < timeB) {\n comparison = -1;\n }\n return comparison;\n}\n\nlet confidence = 1000; // max millisecond diff gateway time to server time\n\n// guess if we have received a valid time_sync_req command\nif (msg.payload.payload_raw.length != 1)\n return;\n\nvar deviceMsg = { payload: msg.payload.dev_id };\nvar seqNo = msg.payload.payload_raw[0];\nvar seqNoMsg = { payload: seqNo };\nvar gateway_list = msg.payload.metadata.gateways;\n\n// filter all gateway timestamps that have milliseconds part (which we assume have a \".\")\nvar gateways = gateway_list.filter(function (element) {\n return (element.time.includes(\".\"));\n});\n\nvar gateway_time = gateways.map(gw => {\n return {\n time: new Date(gw.time),\n eui: gw.gtw_id,\n }\n });\nvar server_time = new Date(msg.payload.metadata.time);\n\n// validate all gateway timestamps against lorawan server_time (which is assumed to be recent)\nvar gw_timestamps = gateway_time.filter(function (element) {\n return ((element.time > (server_time - confidence) && element.time <= server_time));\n});\n\n// if no timestamp left, we have no valid one and exit\nif (gw_timestamps.length === 0) {\n var notavailMsg = { payload: \"n/a\" };\n var notimeMsg = { payload: 0xff }; \n var buf2 = Buffer.alloc(1);\n msg.payload = new Buffer(buf2.fill(0xff));\n msg.port = 9; // Paxcounter TIMEPORT\n return [notavailMsg, notavailMsg, deviceMsg, seqNoMsg, msg];}\n\n// sort time array in ascending order to find most recent timestamp for time answer\ngw_timestamps.sort(timecompare);\n\nvar timestamp = gw_timestamps[0].time;\nvar eui = gw_timestamps[0].eui;\nvar offset = server_time - timestamp;\n\nvar seconds = Math.floor(timestamp/1000);\nvar fractions = (timestamp % 1000) / 4;\n\nlet buf = new ArrayBuffer(6);\nnew DataView(buf).setUint8(0, seqNo);\nnew DataView(buf).setUint32(1, seconds);\nnew DataView(buf).setUint8(5, fractions);\n\nmsg.payload = new Buffer(new Uint8Array(buf));\nmsg.port = 9; // Paxcounter TIMEPORT\nvar euiMsg = { payload: eui };\nvar offsetMsg = { payload: offset };\n\nreturn [euiMsg, offsetMsg, deviceMsg, seqNoMsg, msg];",
"outputs": 5,
"noerr": 0,
"x": 330,
"y": 327,
"wires": [
[
"c9a83ac9.50fd18",
"6aeb3720.a89618",
"6ac55bbe.12ac54"
],
[
"de908e66.b6fd3"
],
[
"d5a35bab.44cb18"
],
[
"3a661f0a.c61b1"
],
[
"9b4f492d.fbfd18"
]
],
"outputLabels": [
"gw_eui",
"offset_ms",
"device",
"seq_no",
"time_sync_ans"
]
},
{ {
"id": "9b4f492d.fbfd18", "id": "9b4f492d.fbfd18",
"type": "change", "type": "change",
@ -214,12 +177,49 @@
"id": "15980d22.6f4663", "id": "15980d22.6f4663",
"type": "comment", "type": "comment",
"z": "449c1517.e25f4c", "z": "449c1517.e25f4c",
"name": "LoRaWAN Timeserver v1.3", "name": "LoRaWAN Timeserver v1.4",
"info": "PLEASE NOTE: There is a patent filed for the time sync algorithm used in the\ncode of this file. The shown implementation example is covered by the\nrepository's licencse, but you may not be eligible to deploy the applied\nalgorithm in applications without granted license by the patent holder.", "info": "PLEASE NOTE: There is a patent filed for the time sync algorithm used in the\ncode of this file. The shown implementation example is covered by the\nrepository's licencse, but you may not be eligible to deploy the applied\nalgorithm in applications without granted license by the patent holder.",
"x": 150, "x": 150,
"y": 47, "y": 47,
"wires": [] "wires": []
}, },
{
"id": "b8bd33fd.61caa",
"type": "function",
"z": "449c1517.e25f4c",
"name": "Timeserver Logic",
"func": "/* LoRaWAN Timeserver\n\nVERSION: 1.4\n\nconstruct 6 byte timesync_answer from gateway timestamp and node's time_sync_req\n\nbyte meaning\n1 sequence number (taken from node's time_sync_req)\n2..5 current second (from GPS epoch starting 1980)\n6 1/250ths fractions of current second\n\n*/\n\nfunction timecompare(a, b) {\n \n const timeA = a.time;\n const timeB = b.time;\n\n let comparison = 0;\n if (timeA > timeB) {\n comparison = 1;\n } else if (timeA < timeB) {\n comparison = -1;\n }\n return comparison;\n}\n\nlet confidence = 1000; // max millisecond diff gateway time to server time\nlet TIME_SYNC_END_FLAG = 255;\n\n// guess if we have received a valid time_sync_req command\nif (msg.payload.payload_raw.length != 1)\n return;\n\nvar deviceMsg = { payload: msg.payload.dev_id };\nvar seqNo = msg.payload.payload_raw[0];\nvar seqNoMsg = { payload: seqNo };\nvar gateway_list = msg.payload.metadata.gateways;\n\n// don't answer on TIME_SYNC_END_FLAG\nif (seqNo == TIME_SYNC_END_FLAG)\n return;\n\n// filter all gateway timestamps that have milliseconds part (which we assume have a \".\")\nvar gateways = gateway_list.filter(function (element) {\n return (element.time.includes(\".\"));\n});\n\nvar gateway_time = gateways.map(gw => {\n return {\n time: new Date(gw.time),\n eui: gw.gtw_id,\n }\n });\nvar server_time = new Date(msg.payload.metadata.time);\n\n// validate all gateway timestamps against lorawan server_time (which is assumed to be recent)\nvar gw_timestamps = gateway_time.filter(function (element) {\n return ((element.time > (server_time - confidence) && element.time <= server_time));\n});\n\n// if no timestamp left, we have no valid one and exit\nif (gw_timestamps.length === 0) {\n var notavailMsg = { payload: \"n/a\" };\n var notimeMsg = { payload: 0xff }; \n var buf2 = Buffer.alloc(1);\n msg.payload = new Buffer(buf2.fill(0xff));\n msg.port = 9; // Paxcounter TIMEPORT\n return [notavailMsg, notavailMsg, deviceMsg, seqNoMsg, msg];}\n\n// sort time array in ascending order to find most recent timestamp for time answer\ngw_timestamps.sort(timecompare);\n\nvar timestamp = gw_timestamps[0].time;\nvar eui = gw_timestamps[0].eui;\nvar offset = server_time - timestamp;\n\nvar seconds = Math.floor(timestamp/1000);\nvar fractions = (timestamp % 1000) / 4;\n\nlet buf = new ArrayBuffer(6);\nnew DataView(buf).setUint8(0, seqNo);\nnew DataView(buf).setUint32(1, seconds);\nnew DataView(buf).setUint8(5, fractions);\n\nmsg.payload = new Buffer(new Uint8Array(buf));\nmsg.port = 9; // Paxcounter TIMEPORT\nvar euiMsg = { payload: eui };\nvar offsetMsg = { payload: offset };\n\nreturn [euiMsg, offsetMsg, deviceMsg, seqNoMsg, msg];",
"outputs": 5,
"noerr": 0,
"x": 330,
"y": 327,
"wires": [
[
"c9a83ac9.50fd18",
"6aeb3720.a89618",
"6ac55bbe.12ac54"
],
[
"de908e66.b6fd3"
],
[
"d5a35bab.44cb18"
],
[
"3a661f0a.c61b1"
],
[
"9b4f492d.fbfd18"
]
],
"outputLabels": [
"gw_eui",
"offset_ms",
"device",
"seq_no",
"time_sync_ans"
]
},
{ {
"id": "c9a83ac9.50fd18", "id": "c9a83ac9.50fd18",
"type": "debug", "type": "debug",

View File

@ -1,6 +1,6 @@
/* LoRaWAN Timeserver /* LoRaWAN Timeserver
VERSION: 1.3 VERSION: 1.4
construct 6 byte timesync_answer from gateway timestamp and node's time_sync_req construct 6 byte timesync_answer from gateway timestamp and node's time_sync_req
@ -26,6 +26,7 @@ function timecompare(a, b) {
} }
let confidence = 1000; // max millisecond diff gateway time to server time let confidence = 1000; // max millisecond diff gateway time to server time
let TIME_SYNC_END_FLAG = 255;
// guess if we have received a valid time_sync_req command // guess if we have received a valid time_sync_req command
if (msg.payload.payload_raw.length != 1) if (msg.payload.payload_raw.length != 1)
@ -36,6 +37,10 @@ var seqNo = msg.payload.payload_raw[0];
var seqNoMsg = { payload: seqNo }; var seqNoMsg = { payload: seqNo };
var gateway_list = msg.payload.metadata.gateways; var gateway_list = msg.payload.metadata.gateways;
// don't answer on TIME_SYNC_END_FLAG
if (seqNo == TIME_SYNC_END_FLAG)
return;
// filter all gateway timestamps that have milliseconds part (which we assume have a ".") // filter all gateway timestamps that have milliseconds part (which we assume have a ".")
var gateways = gateway_list.filter(function (element) { var gateways = gateway_list.filter(function (element) {
return (element.time.includes(".")); return (element.time.includes("."));

View File

@ -169,9 +169,11 @@ IRAM_ATTR void gap_callback_handler(esp_gap_ble_cb_event_t event,
mac_add((uint8_t *)p->scan_rst.bda, p->scan_rst.rssi, MAC_SNIFF_BLE); mac_add((uint8_t *)p->scan_rst.bda, p->scan_rst.rssi, MAC_SNIFF_BLE);
#if (COUNT_ENS) #if (COUNT_ENS)
if (cfg.enscount) {
// check for ens signature // check for ens signature
if (NULL != strstr((const char *)p->scan_rst.ble_adv, ensMagicBytes)) if (NULL != strstr((const char *)p->scan_rst.ble_adv, ensMagicBytes))
cwa_mac_add(hashedmac); cwa_mac_add(hashedmac);
}
#endif #endif
/* to be improved in vendorfilter if: /* to be improved in vendorfilter if:

View File

@ -44,7 +44,7 @@ Adafruit_BMP085 bmp; // I2C
#endif #endif
void bmecycle() { xTaskNotify(irqHandlerTask, BME_IRQ, eSetBits); } void setBMEIRQ() { xTaskNotify(irqHandlerTask, BME_IRQ, eSetBits); }
// initialize BME680 sensor // initialize BME680 sensor
int bme_init(void) { int bme_init(void) {
@ -133,7 +133,7 @@ int bme_init(void) {
finish: finish:
I2C_MUTEX_UNLOCK(); // release i2c bus access I2C_MUTEX_UNLOCK(); // release i2c bus access
if (rc) if (rc)
bmecycler.attach(BMECYCLE, bmecycle); bmecycler.attach(BMECYCLE, setBMEIRQ);
return rc; return rc;
} // bme_init() } // bme_init()

View File

@ -4,362 +4,157 @@
#include "configmanager.h" #include "configmanager.h"
// Local logging tag // Local logging tag
static const char TAG[] = "flash"; static const char TAG[] = __FILE__;
nvs_handle my_handle;
esp_err_t err;
// default settings for device data to be sent
#define PAYLOADMASK \ #define PAYLOADMASK \
((GPS_DATA | ALARM_DATA | MEMS_DATA | COUNT_DATA | \ ((GPS_DATA | ALARM_DATA | MEMS_DATA | COUNT_DATA | SENSOR1_DATA | \
SENSOR1_DATA | SENSOR2_DATA | SENSOR3_DATA) & \ SENSOR2_DATA | SENSOR3_DATA) & \
(~BATT_DATA) ) (~BATT_DATA))
// populate cfg vars with factory settings // namespace for device runtime preferences
void defaultConfig() { #define DEVCONFIG "paxcntcfg"
cfg.loradr = LORADRDEFAULT; // 0-15, lora datarate, see paxcounter.conf
cfg.txpower = LORATXPOWDEFAULT; // 0-15, lora tx power Preferences nvram;
cfg.adrmode = 1; // 0=disabled, 1=enabled
cfg.screensaver = 0; // 0=disabled, 1=enabled static const uint8_t cfgMagicBytes[] = {0x21, 0x76, 0x87, 0x32, 0xf4};
cfg.screenon = 1; // 0=disabled, 1=enabled static const size_t cfgLen = sizeof(cfg), cfgLen2 = sizeof(cfgMagicBytes);
cfg.countermode = COUNTERMODE; // 0=cyclic, 1=cumulative, 2=cyclic confirmed static uint8_t buffer[cfgLen + cfgLen2];
cfg.rssilimit = 0; // threshold for rssilimiter, negative value!
cfg.sendcycle = SENDCYCLE; // payload send cycle [seconds/2] // populate runtime config with device factory settings
cfg.wifichancycle = //
// configuration frame structure in NVRAM;
// 1. version header [10 bytes, containing version string]
// 2. user settings [cfgLen bytes, containing default runtime settings
// (configData_t cfg)]
// 3. magicByte [cfgLen2 bytes, containing a fixed identifier]
static void defaultConfig(configData_t *myconfig) {
memcpy(myconfig->version, &PROGVERSION, 10); // Firmware version
// device factory settings
myconfig->loradr = LORADRDEFAULT; // 0-15, lora datarate, see paxcounter.conf
myconfig->txpower = LORATXPOWDEFAULT; // 0-15, lora tx power
myconfig->adrmode = 1; // 0=disabled, 1=enabled
myconfig->screensaver = 0; // 0=disabled, 1=enabled
myconfig->screenon = 1; // 0=disabled, 1=enabled
myconfig->countermode =
COUNTERMODE; // 0=cyclic, 1=cumulative, 2=cyclic confirmed
myconfig->rssilimit = 0; // threshold for rssilimiter, negative value!
myconfig->sendcycle = SENDCYCLE; // payload send cycle [seconds/2]
myconfig->wifichancycle =
WIFI_CHANNEL_SWITCH_INTERVAL; // wifi channel switch cycle [seconds/100] WIFI_CHANNEL_SWITCH_INTERVAL; // wifi channel switch cycle [seconds/100]
cfg.blescantime = myconfig->blescantime =
BLESCANINTERVAL / BLESCANINTERVAL /
10; // BT channel scan cycle [seconds/100], default 1 (= 10ms) 10; // BT channel scan cycle [seconds/100], default 1 (= 10ms)
cfg.blescan = 1; // 0=disabled, 1=enabled myconfig->blescan = 1; // 0=disabled, 1=enabled
cfg.wifiscan = 1; // 0=disabled, 1=enabled myconfig->wifiscan = 1; // 0=disabled, 1=enabled
cfg.wifiant = 0; // 0=internal, 1=external (for LoPy/LoPy4) myconfig->wifiant = 0; // 0=internal, 1=external (for LoPy/LoPy4)
cfg.vendorfilter = VENDORFILTER; // 0=disabled, 1=enabled myconfig->vendorfilter = VENDORFILTER; // 0=disabled, 1=enabled
cfg.rgblum = RGBLUMINOSITY; // RGB Led luminosity (0..100%) myconfig->rgblum = RGBLUMINOSITY; // RGB Led luminosity (0..100%)
cfg.monitormode = 0; // 0=disabled, 1=enabled myconfig->monitormode = 0; // 0=disabled, 1=enabled
cfg.payloadmask = PAYLOADMASK; // all payload switched on myconfig->payloadmask = PAYLOADMASK; // payloads as defined in default
cfg.bsecstate[BSEC_MAX_STATE_BLOB_SIZE] = { myconfig->enscount = COUNT_ENS; // 0=disabled, 1=enabled
0}; // init BSEC state for BME680 sensor
strncpy(cfg.version, PROGVERSION, sizeof(cfg.version) - 1); #ifdef HAS_BME680
// initial BSEC state for BME680 sensor
myconfig->bsecstate[BSEC_MAX_STATE_BLOB_SIZE] = {0};
#endif
} }
void open_storage() { // migrate runtime configuration from earlier to current version
err = nvs_flash_init(); static void migrateConfig(void) {
if (err == ESP_ERR_NVS_NO_FREE_PAGES) { // currently no configuration migration rules are implemented, we reset to
// NVS partition was truncated and needs to be erased // factory settings instead
// Retry nvs_flash_init eraseConfig();
ESP_ERROR_CHECK(nvs_flash_erase());
err = nvs_flash_init();
}
ESP_ERROR_CHECK(err);
// Open
ESP_LOGI(TAG, "Opening NVS");
err = nvs_open("config", NVS_READWRITE, &my_handle);
if (err != ESP_OK)
ESP_LOGI(TAG, "Error (%d) opening NVS handle", err);
else
ESP_LOGI(TAG, "Done");
}
// erase all keys and values in NVRAM
void eraseConfig() {
ESP_LOGI(TAG, "Clearing settings in NVS");
open_storage();
if (err == ESP_OK) {
nvs_erase_all(my_handle);
nvs_commit(my_handle);
nvs_close(my_handle);
ESP_LOGI(TAG, "Done");
} else {
ESP_LOGW(TAG, "NVS erase failed");
}
} }
// save current configuration from RAM to NVRAM // save current configuration from RAM to NVRAM
void saveConfig() { void saveConfig(bool erase) {
ESP_LOGI(TAG, "Storing settings in NVS"); ESP_LOGI(TAG, "Storing settings to NVRAM...");
open_storage();
if (err == ESP_OK) {
int8_t flash8 = 0;
int16_t flash16 = 0;
size_t required_size;
uint8_t bsecstate_buffer[BSEC_MAX_STATE_BLOB_SIZE + 1];
char storedversion[10];
if (nvs_get_blob(my_handle, "bsecstate", bsecstate_buffer, nvram.begin(DEVCONFIG, false);
&required_size) != ESP_OK ||
memcmp(bsecstate_buffer, cfg.bsecstate, BSEC_MAX_STATE_BLOB_SIZE + 1) !=
0)
nvs_set_blob(my_handle, "bsecstate", cfg.bsecstate,
BSEC_MAX_STATE_BLOB_SIZE + 1);
if (nvs_get_str(my_handle, "version", storedversion, &required_size) != if (erase) {
ESP_OK || ESP_LOGI(TAG, "Resetting device to factory settings");
strcmp(storedversion, cfg.version) != 0) nvram.clear();
nvs_set_str(my_handle, "version", cfg.version); defaultConfig(&cfg);
if (nvs_get_i8(my_handle, "loradr", &flash8) != ESP_OK ||
flash8 != cfg.loradr)
nvs_set_i8(my_handle, "loradr", cfg.loradr);
if (nvs_get_i8(my_handle, "txpower", &flash8) != ESP_OK ||
flash8 != cfg.txpower)
nvs_set_i8(my_handle, "txpower", cfg.txpower);
if (nvs_get_i8(my_handle, "adrmode", &flash8) != ESP_OK ||
flash8 != cfg.adrmode)
nvs_set_i8(my_handle, "adrmode", cfg.adrmode);
if (nvs_get_i8(my_handle, "screensaver", &flash8) != ESP_OK ||
flash8 != cfg.screensaver)
nvs_set_i8(my_handle, "screensaver", cfg.screensaver);
if (nvs_get_i8(my_handle, "screenon", &flash8) != ESP_OK ||
flash8 != cfg.screenon)
nvs_set_i8(my_handle, "screenon", cfg.screenon);
if (nvs_get_i8(my_handle, "countermode", &flash8) != ESP_OK ||
flash8 != cfg.countermode)
nvs_set_i8(my_handle, "countermode", cfg.countermode);
if (nvs_get_i8(my_handle, "sendcycle", &flash8) != ESP_OK ||
flash8 != cfg.sendcycle)
nvs_set_i8(my_handle, "sendcycle", cfg.sendcycle);
if (nvs_get_i8(my_handle, "wifichancycle", &flash8) != ESP_OK ||
flash8 != cfg.wifichancycle)
nvs_set_i8(my_handle, "wifichancycle", cfg.wifichancycle);
if (nvs_get_i8(my_handle, "blescantime", &flash8) != ESP_OK ||
flash8 != cfg.blescantime)
nvs_set_i8(my_handle, "blescantime", cfg.blescantime);
if (nvs_get_i8(my_handle, "blescanmode", &flash8) != ESP_OK ||
flash8 != cfg.blescan)
nvs_set_i8(my_handle, "blescanmode", cfg.blescan);
if (nvs_get_i8(my_handle, "wifiscanmode", &flash8) != ESP_OK ||
flash8 != cfg.wifiscan)
nvs_set_i8(my_handle, "wifiscanmode", cfg.wifiscan);
if (nvs_get_i8(my_handle, "wifiant", &flash8) != ESP_OK ||
flash8 != cfg.wifiant)
nvs_set_i8(my_handle, "wifiant", cfg.wifiant);
if (nvs_get_i8(my_handle, "vendorfilter", &flash8) != ESP_OK ||
flash8 != cfg.vendorfilter)
nvs_set_i8(my_handle, "vendorfilter", cfg.vendorfilter);
if (nvs_get_i8(my_handle, "rgblum", &flash8) != ESP_OK ||
flash8 != cfg.rgblum)
nvs_set_i8(my_handle, "rgblum", cfg.rgblum);
if (nvs_get_i8(my_handle, "payloadmask", &flash8) != ESP_OK ||
flash8 != cfg.payloadmask)
nvs_set_i8(my_handle, "payloadmask", cfg.payloadmask);
if (nvs_get_i8(my_handle, "monitormode", &flash8) != ESP_OK ||
flash8 != cfg.monitormode)
nvs_set_i8(my_handle, "monitormode", cfg.monitormode);
if (nvs_get_i16(my_handle, "rssilimit", &flash16) != ESP_OK ||
flash16 != cfg.rssilimit)
nvs_set_i16(my_handle, "rssilimit", cfg.rssilimit);
err = nvs_commit(my_handle);
nvs_close(my_handle);
if (err == ESP_OK) {
ESP_LOGI(TAG, "Done");
} else {
ESP_LOGW(TAG, "NVS config write failed");
} }
} else {
ESP_LOGW(TAG, "Error (%d) opening NVS handle", err);
}
}
// set and save cfg.version // Copy device runtime config cfg to byte array, padding it with magicBytes
void migrateVersion() { memcpy(buffer, &cfg, cfgLen);
snprintf(cfg.version, 10, "%s", PROGVERSION); memcpy(buffer + cfgLen, &cfgMagicBytes, cfgLen2);
ESP_LOGI(TAG, "version set to %s", cfg.version);
saveConfig(); // save byte array to NVRAM, padding with cfg magicbyes
if (nvram.putBytes(DEVCONFIG, buffer, cfgLen + cfgLen2))
ESP_LOGI(TAG, "Device settings saved");
else
ESP_LOGE(TAG, "NVRAM Error, device settings not saved");
nvram.end();
} }
// load configuration from NVRAM into RAM and make it current // load configuration from NVRAM into RAM and make it current
void loadConfig() { bool loadConfig() {
defaultConfig(); // start with factory settings
ESP_LOGI(TAG, "Reading settings from NVS");
open_storage();
if (err != ESP_OK) {
ESP_LOGW(TAG, "Error (%d) opening NVS handle, storing defaults", err);
saveConfig();
} // saves factory settings to NVRAM
else {
int8_t flash8 = 0;
int16_t flash16 = 0;
size_t required_size;
// check if configuration stored in NVRAM matches PROGVERSION ESP_LOGI(TAG, "Loading device configuration from NVRAM...");
if (nvs_get_str(my_handle, "version", NULL, &required_size) == ESP_OK) {
nvs_get_str(my_handle, "version", cfg.version, &required_size); if (!nvram.begin(DEVCONFIG, true)) {
ESP_LOGI(TAG, "NVRAM settings version = %s", cfg.version); ESP_LOGI(TAG, "NVRAM initialized, device starts with factory settings");
if (strcmp(cfg.version, PROGVERSION)) {
ESP_LOGI(TAG, "migrating NVRAM settings to new version %s",
PROGVERSION);
nvs_close(my_handle);
migrateVersion();
}
} else {
ESP_LOGI(TAG, "new version %s, deleting NVRAM settings", PROGVERSION);
nvs_close(my_handle);
eraseConfig(); eraseConfig();
migrateVersion();
} }
// populate pre set defaults with current values from NVRAM // simple check that runtime config data matches
// if (nvram.getBytesLength(DEVCONFIG) != (cfgLen + cfgLen2)) {
// ESP_LOGE(TAG, "Configuration invalid");
// return false;
//}
if (nvs_get_blob(my_handle, "bsecstate", NULL, &required_size) == ESP_OK) { // load device runtime config from nvram and copy it to byte array
nvs_get_blob(my_handle, "bsecstate", cfg.bsecstate, &required_size); nvram.getBytes(DEVCONFIG, buffer, cfgLen + cfgLen2);
ESP_LOGI(TAG, "bsecstate = %d", cfg.bsecstate[BSEC_MAX_STATE_BLOB_SIZE]); nvram.end();
};
if (nvs_get_i8(my_handle, "loradr", &flash8) == ESP_OK) { // validate loaded configuration by checking magic bytes at end of array
cfg.loradr = flash8; // if (memcmp(buffer + cfgLen, &cfgMagicBytes, cfgLen2) != 0) {
ESP_LOGI(TAG, "loradr = %d", flash8); // ESP_LOGW(TAG, "No configuration found");
} else { // return false;
ESP_LOGI(TAG, "loradr set to default %d", cfg.loradr); //}
saveConfig();
// copy loaded configuration into runtime cfg struct
memcpy(&cfg, buffer, cfgLen);
ESP_LOGI(TAG, "Runtime configuration v%s loaded", cfg.version);
// check if config version matches current firmware version
switch (version_compare(PROGVERSION, cfg.version)) {
case -1: // device configuration belongs to newer than current firmware
ESP_LOGE(TAG, "Incompatible device configuration");
return false;
case 1: // device configuration belongs to older than current firmware
ESP_LOGW(TAG, "Device was updated, attempt to migrate configuration");
migrateConfig();
return true;
default: // device configuration version matches current firmware version
return true;
} }
}
if (nvs_get_i8(my_handle, "txpower", &flash8) == ESP_OK) { // helper function to convert strings into lower case
cfg.txpower = flash8; bool comp(char s1, char s2) { return (tolower(s1) < tolower(s2)); }
ESP_LOGI(TAG, "txpower = %d", flash8);
} else {
ESP_LOGI(TAG, "txpower set to default %d", cfg.txpower);
saveConfig();
}
if (nvs_get_i8(my_handle, "adrmode", &flash8) == ESP_OK) { // helper function to lexicographically compare two versions. Returns 1 if v2
cfg.adrmode = flash8; // is smaller, -1 if v1 is smaller, 0 if equal
ESP_LOGI(TAG, "adrmode = %d", flash8); int version_compare(const String v1, const String v2) {
} else {
ESP_LOGI(TAG, "adrmode set to default %d", cfg.adrmode);
saveConfig();
}
if (nvs_get_i8(my_handle, "screensaver", &flash8) == ESP_OK) { if (v1 == v2)
cfg.screensaver = flash8; return 0;
ESP_LOGI(TAG, "screensaver = %d", flash8);
} else {
ESP_LOGI(TAG, "screensaver set to default %d", cfg.screensaver);
saveConfig();
}
if (nvs_get_i8(my_handle, "screenon", &flash8) == ESP_OK) { const char *a1 = v1.c_str(), *a2 = v2.c_str();
cfg.screenon = flash8;
ESP_LOGI(TAG, "screenon = %d", flash8);
} else {
ESP_LOGI(TAG, "screenon set to default %d", cfg.screenon);
saveConfig();
}
if (nvs_get_i8(my_handle, "countermode", &flash8) == ESP_OK) { if (std::lexicographical_compare(a1, a1 + strlen(a1), a2, a2 + strlen(a2),
cfg.countermode = flash8; comp))
ESP_LOGI(TAG, "countermode = %d", flash8); return -1;
} else { else
ESP_LOGI(TAG, "countermode set to default %d", cfg.countermode); return 1;
saveConfig(); }
}
if (nvs_get_i8(my_handle, "sendcycle", &flash8) == ESP_OK) { void eraseConfig(void) { saveConfig(true); }
cfg.sendcycle = flash8;
ESP_LOGI(TAG, "sendcycle = %d", flash8);
} else {
ESP_LOGI(TAG, "Payload send cycle set to default %d", cfg.sendcycle);
saveConfig();
}
if (nvs_get_i8(my_handle, "wifichancycle", &flash8) == ESP_OK) {
cfg.wifichancycle = flash8;
ESP_LOGI(TAG, "wifichancycle = %d", flash8);
} else {
ESP_LOGI(TAG, "WIFI channel cycle set to default %d", cfg.wifichancycle);
saveConfig();
}
if (nvs_get_i8(my_handle, "wifiant", &flash8) == ESP_OK) {
cfg.wifiant = flash8;
ESP_LOGI(TAG, "wifiantenna = %d", flash8);
} else {
ESP_LOGI(TAG, "WIFI antenna switch set to default %d", cfg.wifiant);
saveConfig();
}
if (nvs_get_i8(my_handle, "vendorfilter", &flash8) == ESP_OK) {
cfg.vendorfilter = flash8;
ESP_LOGI(TAG, "vendorfilter = %d", flash8);
} else {
ESP_LOGI(TAG, "Vendorfilter mode set to default %d", cfg.vendorfilter);
saveConfig();
}
if (nvs_get_i8(my_handle, "rgblum", &flash8) == ESP_OK) {
cfg.rgblum = flash8;
ESP_LOGI(TAG, "rgbluminosity = %d", flash8);
} else {
ESP_LOGI(TAG, "RGB luminosity set to default %d", cfg.rgblum);
saveConfig();
}
if (nvs_get_i8(my_handle, "blescantime", &flash8) == ESP_OK) {
cfg.blescantime = flash8;
ESP_LOGI(TAG, "blescantime = %d", flash8);
} else {
ESP_LOGI(TAG, "BLEscantime set to default %d", cfg.blescantime);
saveConfig();
}
if (nvs_get_i8(my_handle, "blescanmode", &flash8) == ESP_OK) {
cfg.blescan = flash8;
ESP_LOGI(TAG, "BLEscanmode = %d", flash8);
} else {
ESP_LOGI(TAG, "BLEscanmode set to default %d", cfg.blescan);
saveConfig();
}
if (nvs_get_i8(my_handle, "wifiscanmode", &flash8) == ESP_OK) {
cfg.wifiscan = flash8;
ESP_LOGI(TAG, "WIFIscanmode = %d", flash8);
} else {
ESP_LOGI(TAG, "WIFIscanmode set to default %d", cfg.wifiscan);
saveConfig();
}
if (nvs_get_i16(my_handle, "rssilimit", &flash16) == ESP_OK) {
cfg.rssilimit = flash16;
ESP_LOGI(TAG, "rssilimit = %d", flash16);
} else {
ESP_LOGI(TAG, "rssilimit set to default %d", cfg.rssilimit);
saveConfig();
}
if (nvs_get_i8(my_handle, "payloadmask", &flash8) == ESP_OK) {
cfg.payloadmask = flash8;
ESP_LOGI(TAG, "payloadmask = %hhu", flash8);
} else {
ESP_LOGI(TAG, "payloadmask set to default %hhu", cfg.payloadmask);
saveConfig();
}
if (nvs_get_i8(my_handle, "monitormode", &flash8) == ESP_OK) {
cfg.monitormode = flash8;
ESP_LOGI(TAG, "Monitor mode = %d", flash8);
} else {
ESP_LOGI(TAG, "Monitor mode set to default %d", cfg.monitormode);
saveConfig();
}
nvs_close(my_handle);
ESP_LOGI(TAG, "Done");
}
} // loadConfig()

View File

@ -7,13 +7,13 @@
// Local logging tag // Local logging tag
static const char TAG[] = __FILE__; static const char TAG[] = __FILE__;
Ticker housekeeper; Ticker cyclicTimer;
#if (HAS_SDS011) #if (HAS_SDS011)
extern boolean isSDS011Active; extern boolean isSDS011Active;
#endif #endif
void housekeeping() { void setCyclicIRQ() {
xTaskNotifyFromISR(irqHandlerTask, CYCLIC_IRQ, eSetBits, NULL); xTaskNotifyFromISR(irqHandlerTask, CYCLIC_IRQ, eSetBits, NULL);
} }

View File

@ -265,11 +265,10 @@ void dp_drawPage(time_t t, bool nextpage) {
else else
dp_printf("WIFI:off"); dp_printf("WIFI:off");
if (cfg.blescan) if (cfg.blescan)
#if (!COUNT_ENS) if (!cfg.enscount)
dp_printf("BLTH:%-5d", macs_ble); dp_printf("BLTH:%-5d", macs_ble);
#else else
dp_printf(" CWA:%-5d", cwa_report()); dp_printf(" CWA:%-5d", cwa_report());
#endif
else else
dp_printf(" BLTH:off"); dp_printf(" BLTH:off");
#elif ((WIFICOUNTER) && (!BLECOUNTER)) #elif ((WIFICOUNTER) && (!BLECOUNTER))
@ -280,10 +279,10 @@ void dp_drawPage(time_t t, bool nextpage) {
#elif ((!WIFICOUNTER) && (BLECOUNTER)) #elif ((!WIFICOUNTER) && (BLECOUNTER))
if (cfg.blescan) { if (cfg.blescan) {
dp_printf("BLTH:%-5d", macs_ble); dp_printf("BLTH:%-5d", macs_ble);
#if (COUNT_ENS) if (cfg.enscount)
dp_printf("(CWA:%d)", cwa_report()); dp_printf("(CWA:%d)", cwa_report());
#endif }
} else else
dp_printf("BLTH:off"); dp_printf("BLTH:off");
#else #else
dp_printf("Sniffer disabled"); dp_printf("Sniffer disabled");

View File

@ -12,7 +12,10 @@ static const char TAG[] = __FILE__;
// thus precision is only +/- 1 second // thus precision is only +/- 1 second
TinyGPSPlus gps; TinyGPSPlus gps;
TinyGPSCustom gpstime(gps, "GPZDA", 1); // field 1 = UTC time TinyGPSCustom gpstime(gps, "GPZDA", 1); // field 1 = UTC time (hhmmss.ss)
TinyGPSCustom gpsday(gps, "GPZDA", 2); // field 2 = day (01..31)
TinyGPSCustom gpsmonth(gps, "GPZDA", 3); // field 3 = month (01..12)
TinyGPSCustom gpsyear(gps, "GPZDA", 4); // field 4 = year (4-digit)
static const String ZDA_Request = "$EIGPQ,ZDA*39\r\n"; static const String ZDA_Request = "$EIGPQ,ZDA*39\r\n";
TaskHandle_t GpsTask; TaskHandle_t GpsTask;
@ -93,47 +96,49 @@ bool gps_hasfix() {
gps.altitude.age() < 4000); gps.altitude.age() < 4000);
} }
// function to fetch current time from struct; note: this is costly // function to poll current time from GPS data; note: this is costly
time_t get_gpstime(uint16_t *msec) { time_t get_gpstime(uint16_t *msec) {
time_t time_sec = 0;
// poll NMEA ZDA sentence // poll NMEA ZDA sentence
#ifdef GPS_SERIAL #ifdef GPS_SERIAL
GPS_Serial.print(ZDA_Request); GPS_Serial.print(ZDA_Request);
// wait for gps NMEA answer // wait for gps NMEA answer
vTaskDelay(tx_Ticks(NMEA_FRAME_SIZE, GPS_SERIAL)); // vTaskDelay(tx_Ticks(NMEA_FRAME_SIZE, GPS_SERIAL));
#elif defined GPS_I2C #elif defined GPS_I2C
Wire.print(ZDA_Request); Wire.print(ZDA_Request);
#endif #endif
// did we get a current time? // did we get a current date & time?
if (gpstime.isUpdated() && gpstime.isValid()) { if (gpstime.isValid() && gpsday.isValid()) {
time_t t = 0;
tmElements_t tm; tmElements_t tm;
String rawtime = gpstime.value();
uint32_t time_bcd = rawtime.toFloat() * 100;
uint32_t delay_ms = uint32_t delay_ms =
gpstime.age() + nmea_txDelay_ms + NMEA_COMPENSATION_FACTOR; gpstime.age() + nmea_txDelay_ms + NMEA_COMPENSATION_FACTOR;
uint8_t year = uint32_t zdatime = atof(gpstime.value());
CalendarYrToTm(gps.date.year()); // year offset from 1970 in microTime.h
ESP_LOGD(TAG, "time [bcd]: %u", time_bcd); // convert time to maketime format and make time
tm.Second = zdatime % 100; // second
tm.Minute = (zdatime / 100) % 100; // minute
tm.Hour = zdatime / 10000; // hour
tm.Day = atoi(gpsday.value()); // day
tm.Month = atoi(gpsmonth.value()); // month
tm.Year = CalendarYrToTm(atoi(gpsyear.value())); // year offset from 1970
t = makeTime(tm);
tm.Second = (time_bcd / 100) % 100; // second // ESP_LOGD(TAG, "GPS time/date = %2d:%2d:%2d / %2d.%2d.%2d", tm.Hour,
tm.Minute = (time_bcd / 10000) % 100; // minute // tm.Minute, tm.Second, tm.Day, tm.Month, tm.Year + 1970);
tm.Hour = time_bcd / 1000000; // hour
tm.Day = gps.date.day(); // day
tm.Month = gps.date.month(); // month
tm.Year = year; // year
// add protocol delay to time with millisecond precision // add protocol delay with millisecond precision
time_sec = makeTime(tm) + delay_ms / 1000; t += delay_ms / 1000 - 1; // whole seconds
*msec = (delay_ms % 1000) ? delay_ms % 1000 : 0; *msec = delay_ms % 1000; // fractional seconds
return t;
} }
return timeIsValid(time_sec); ESP_LOGD(TAG, "no valid GPS time");
return 0;
} // get_gpstime() } // get_gpstime()
@ -163,15 +168,16 @@ void gps_loop(void *pvParameters) {
} }
#endif #endif
// if time hasn't been synchronised yet, and we have a valid GPS time, // (only) while device time is not set or unsynched, and we have a valid
// update time from GPS. // GPS time, we trigger a device time update to poll time from GPS
if (timeSource == _unsynced && gpstime.isUpdated() && gpstime.isValid()) { if (timeSource == _unsynced && gpstime.isUpdated()) {
now();
calibrateTime(); calibrateTime();
} }
} // if } // if
// show NMEA data in verbose mode, useful for debugging GPS, bu tvery noisy // show NMEA data in verbose mode, useful only for debugging GPS, very noisy
// ESP_LOGV(TAG, "GPS NMEA data: passed %u / failed: %u / with fix: %u", // ESP_LOGV(TAG, "GPS NMEA data: passed %u / failed: %u / with fix: %u",
// gps.passedChecksum(), gps.failedChecksum(), // gps.passedChecksum(), gps.failedChecksum(),
// gps.sentencesWithFix()); // gps.sentencesWithFix());

View File

@ -84,7 +84,7 @@ void irqHandler(void *pvParameters) {
} }
// do we have a power event? // do we have a power event?
#if (HAS_PMU) #ifdef HAS_PMU
if (InterruptStatus & PMU_IRQ) { if (InterruptStatus & PMU_IRQ) {
AXP192_powerevent_IRQ(); AXP192_powerevent_IRQ();
InterruptStatus &= ~PMU_IRQ; InterruptStatus &= ~PMU_IRQ;

View File

@ -527,22 +527,16 @@ void myRxCallback(void *pUserData, uint8_t port, const uint8_t *pMsg,
// rcommand received -> call interpreter // rcommand received -> call interpreter
case RCMDPORT: case RCMDPORT:
rcommand(pMsg, nMsg); rcommand(pMsg, nMsg);
break;
// timeserver answer -> call timesync processor // timeserver answer -> call timesync processor
#if (TIME_SYNC_LORASERVER) #if (TIME_SYNC_LORASERVER)
case TIMEPORT: case TIMEPORT:
// get and store gwtime from payload // get and store gwtime from payload
timesync_serverAnswer(const_cast<uint8_t *>(pMsg), nMsg); timesync_serverAnswer(const_cast<uint8_t *>(pMsg), nMsg);
break;
#endif #endif
// decode any piggybacked downlink MAC commands if we want to print those
default:
#if (VERBOSE)
if (LMIC.dataBeg > 1)
mac_decode(LMIC.frame, LMIC.dataBeg - 1, true);
#endif // VERBOSE
break;
} // switch } // switch
} }

View File

@ -55,17 +55,24 @@ So don't do it if you do not own a digital oscilloscope.
// Interrupt routines // Interrupt routines
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
irqHandlerTask (Core 1), see irqhandler.cpp
fired by hardware fired by hardware
DisplayIRQ -> esp32 timer 0 -> irqHandlerTask (Core 1) DisplayIRQ -> esp32 timer 0
CLOCKIRQ -> esp32 timer 1 -> ClockTask (Core 1) ButtonIRQ -> external gpio
ButtonIRQ -> external gpio -> irqHandlerTask (Core 1) PMUIRQ -> PMU chip gpio
PMUIRQ -> PMU chip gpio -> irqHandlerTask (Core 1)
fired by software (Ticker.h) fired by software (Ticker.h)
TIMESYNC_IRQ -> timeSync() -> irqHandlerTask (Core 1) TIMESYNC_IRQ -> setTimeSyncIRQ()
CYCLIC_IRQ -> housekeeping() -> irqHandlerTask (Core 1) CYCLIC_IRQ -> setCyclicIRQ()
SENDCYCLE_IRQ -> sendcycle() -> irqHandlerTask (Core 1) SENDCYCLE_IRQ -> setSendIRQ()
BME_IRQ -> bmecycle() -> irqHandlerTask (Core 1) BME_IRQ -> setBMEIRQ()
MQTT_IRQ -> setMqttIRQ()
ClockTask (Core 1), see timekeeper.cpp
fired by hardware
CLOCKIRQ -> esp32 timer 1
// External RTC timer (if present) // External RTC timer (if present)
@ -196,7 +203,7 @@ void setup() {
#endif #endif
// read (and initialize on first run) runtime settings from NVRAM // read (and initialize on first run) runtime settings from NVRAM
loadConfig(); // includes initialize if necessary assert(loadConfig()); // includes initialize if necessary
// now that we are powered, we scan i2c bus for devices // now that we are powered, we scan i2c bus for devices
i2c_scan(); i2c_scan();
@ -478,8 +485,8 @@ void setup() {
#endif // HAS_BUTTON #endif // HAS_BUTTON
// cyclic function interrupts // cyclic function interrupts
sendcycler.attach(cfg.sendcycle * 2, sendcycle); sendTimer.attach(cfg.sendcycle * 2, setSendIRQ);
housekeeper.attach(HOMECYCLE, housekeeping); cyclicTimer.attach(HOMECYCLE, setCyclicIRQ);
#if (TIME_SYNC_INTERVAL) #if (TIME_SYNC_INTERVAL)

View File

@ -33,7 +33,7 @@ esp_err_t mqtt_init(void) {
SEND_QUEUE_SIZE * PAYLOAD_BUFFER_SIZE); SEND_QUEUE_SIZE * PAYLOAD_BUFFER_SIZE);
ESP_LOGI(TAG, "Starting MQTTloop..."); ESP_LOGI(TAG, "Starting MQTTloop...");
mqttTimer.attach(MQTT_KEEPALIVE, mqtt_irq); mqttTimer.attach(MQTT_KEEPALIVE, setMqttIRQ);
xTaskCreate(mqtt_client_task, "mqttloop", 4096, (void *)NULL, 1, &mqttTask); xTaskCreate(mqtt_client_task, "mqttloop", 4096, (void *)NULL, 1, &mqttTask);
return ESP_OK; return ESP_OK;
@ -183,6 +183,6 @@ void mqtt_loop(void) {
} }
void mqtt_queuereset(void) { xQueueReset(MQTTSendQueue); } void mqtt_queuereset(void) { xQueueReset(MQTTSendQueue); }
void mqtt_irq(void) { xTaskNotify(irqHandlerTask, MQTT_IRQ, eSetBits); } void setMqttIRQ(void) { xTaskNotify(irqHandlerTask, MQTT_IRQ, eSetBits); }
#endif // HAS_MQTT #endif // HAS_MQTT

View File

@ -328,22 +328,4 @@ void show_progress(unsigned long current, unsigned long size) {
#endif #endif
} }
// helper function to convert strings into lower case
bool comp(char s1, char s2) { return tolower(s1) < tolower(s2); }
// helper function to lexicographically compare two versions. Returns 1 if v2 is
// smaller, -1 if v1 is smaller, 0 if equal
int version_compare(const String v1, const String v2) {
if (v1 == v2)
return 0;
const char *a1 = v1.c_str(), *a2 = v2.c_str();
if (lexicographical_compare(a1, a1 + strlen(a1), a2, a2 + strlen(a2), comp))
return -1;
else
return 1;
}
#endif // USE_OTA #endif // USE_OTA

View File

@ -12,7 +12,7 @@
// Payload send cycle and encoding // Payload send cycle and encoding
#define SENDCYCLE 30 // payload send cycle [seconds/2], 0 .. 255 #define SENDCYCLE 30 // payload send cycle [seconds/2], 0 .. 255
#define PAYLOAD_ENCODER 2 // payload encoder: 1=Plain, 2=Packed, 3=Cayenne LPP dynamic, 4=Cayenne LPP packed #define PAYLOAD_ENCODER 2 // payload encoder: 1=Plain, 2=Packed, 3=Cayenne LPP dynamic, 4=Cayenne LPP packed
#define COUNTERMODE 1 // 0=cyclic, 1=cumulative, 2=cyclic confirmed #define COUNTERMODE 0 // 0=cyclic, 1=cumulative, 2=cyclic confirmed
// Set this to include BLE counting and vendor filter functions, or to switch off WIFI counting // Set this to include BLE counting and vendor filter functions, or to switch off WIFI counting
#define VENDORFILTER 0 // set to 0 if you want to scan all devices, not filtering smartphone OUIs #define VENDORFILTER 0 // set to 0 if you want to scan all devices, not filtering smartphone OUIs

View File

@ -181,14 +181,7 @@ void PayloadConvert::addConfig(configData_t value) {
value.blescan ? true : false, value.wifiant ? true : false, value.blescan ? true : false, value.wifiant ? true : false,
value.vendorfilter ? true : false, value.vendorfilter ? true : false,
value.monitormode ? true : false); value.monitormode ? true : false);
writeBitmap(value.payloadmask && GPS_DATA ? true : false, writeUint8(value.payloadmask);
value.payloadmask && ALARM_DATA ? true : false,
value.payloadmask && MEMS_DATA ? true : false,
value.payloadmask && COUNT_DATA ? true : false,
value.payloadmask && SENSOR1_DATA ? true : false,
value.payloadmask && SENSOR2_DATA ? true : false,
value.payloadmask && SENSOR3_DATA ? true : false,
value.payloadmask && BATT_DATA ? true : false);
writeVersion(value.version); writeVersion(value.version);
} }

135
src/platformio_orig.ini Normal file
View File

@ -0,0 +1,135 @@
; PlatformIO Project Configuration File
; NOTE: PlatformIO v4 is needed!
;
; Please visit documentation for the other options and examples
; http://docs.platformio.org/page/projectconf.html
; ---> SELECT THE TARGET PLATFORM HERE! <---
[board]
halfile = generic.h
;halfile = ebox.h
;halfile = eboxtube.h
;halfile = ecopower.h
;halfile = heltec.h
;halfile = heltecv2.h
;halfile = ttgov1.h
;halfile = ttgov2.h
;halfile = ttgov21old.h
;halfile = ttgov21new.h
;halfile = ttgofox.h
;halfile = ttgobeam.h
;halfile = ttgobeam10.h
;halfile = fipy.h
;halfile = lopy.h
;halfile = lopy4.h
;halfile = lolin32litelora.h
;halfile = lolin32lora.h
;halfile = lolin32lite.h
;halfile = wemos32oled.h
;halfile = wemos32matrix.h
;halfile = octopus32.h
;halfile = tinypico.h
;halfile = tinypicomatrix.h
;halfile = m5core.h
;halfile = m5fire.h
;halfile = olimexpoeiso.h
[platformio]
; upload firmware to board with usb cable
default_envs = usb
; upload firmware to a jfrog bintray repository
;default_envs = ota
; use latest versions of libraries
;default_envs = dev
description = Paxcounter is a device for metering passenger flows in realtime. It counts how many mobile devices are around.
[common]
; for release_version use max. 10 chars total, use any decimal format like "a.b.c"
release_version = 2.0.16
; 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
debug_level = 3
extra_scripts = pre:build.py
otakeyfile = ota.conf
lorakeyfile = loraconf.h
lmicconfigfile = lmic_config.h
platform_espressif32 = espressif32@2.0.0
monitor_speed = 115200
upload_speed = 115200 ; set by build.py and taken from hal file
display_library = ; set by build.py and taken from hal file
lib_deps_lora =
mcci-catena/MCCI LoRaWAN LMIC library @ ^3.2.0
lib_deps_display =
bitbank2/OneBitDisplay @ 1.7.2
ricmoo/QRCode @ ^0.0.1
bodmer/TFT_eSPI @ ^2.2.20
lib_deps_ledmatrix =
seeed-studio/Ultrathin_LED_Matrix @ ^1.0.0
lib_deps_rgbled =
roboticsbrno/SmartLeds @ ^1.2.1
lib_deps_gps =
mikalhart/TinyGPSPlus @ ^1.0.2
lib_deps_sensors =
adafruit/Adafruit Unified Sensor @ ^1.1.4
adafruit/Adafruit BME280 Library @ ^2.1.1
adafruit/Adafruit BMP085 Library @ ^1.1.0
boschsensortec/BSEC Software Library @ 1.5.1474
https://github.com/ricki-z/SDS011.git
lib_deps_basic =
bblanchon/ArduinoJson @ <6
jchristensen/Timezone @ ^1.2.4
makuna/RTC @ ^2.3.5
spacehuhn/SimpleButton
lewisxhe/AXP202X_Library @ ^1.1.2
geeksville/esp32-micro-sdcard @ ^0.1.1
256dpi/MQTT @ ^2.4.7
lib_deps_all =
${common.lib_deps_basic}
${common.lib_deps_lora}
${common.lib_deps_display}
${common.lib_deps_rgbled}
${common.lib_deps_gps}
${common.lib_deps_sensors}
${common.lib_deps_ledmatrix}
build_flags_basic =
-include "src/hal/${board.halfile}"
-include "src/paxcounter.conf"
-w
'-DCORE_DEBUG_LEVEL=${common.debug_level}'
'-DLOG_LOCAL_LEVEL=${common.debug_level}'
'-DPROGVERSION="${common.release_version}"'
build_flags_sensors =
-Llib/Bosch-BSEC/src/esp32/
-lalgobsec
build_flags_all =
${common.build_flags_basic}
${common.build_flags_sensors}
-mfix-esp32-psram-cache-issue
[env]
lib_ldf_mode = deep ; #632 Fixes compiler error with OneBitDisplay library
framework = arduino
board = esp32dev
board_build.partitions = min_spiffs.csv
upload_speed = ${common.upload_speed}
;upload_port = COM8
platform = ${common.platform_espressif32}
lib_deps = ${common.lib_deps_all}
build_flags = ${common.build_flags_all}
upload_protocol = ${common.upload_protocol}
extra_scripts = ${common.extra_scripts}
monitor_speed = ${common.monitor_speed}
monitor_filters = time, esp32_exception_decoder, default
[env:ota]
upload_protocol = custom
[env:usb]
upload_protocol = esptool
[env:dev]
upload_protocol = esptool
build_type = debug
platform = https://github.com/platformio/platform-espressif32.git#develop
platform_packages = framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32.git

View File

@ -48,8 +48,8 @@ void set_rssi(uint8_t val[]) {
void set_sendcycle(uint8_t val[]) { void set_sendcycle(uint8_t val[]) {
cfg.sendcycle = val[0]; cfg.sendcycle = val[0];
// update send cycle interrupt [seconds // update send cycle interrupt [seconds / 2]
sendcycler.attach(cfg.sendcycle * 2, sendcycle); sendTimer.attach(cfg.sendcycle * 2, setSendIRQ);
ESP_LOGI(TAG, "Remote command: set send cycle to %d seconds", ESP_LOGI(TAG, "Remote command: set send cycle to %d seconds",
cfg.sendcycle * 2); cfg.sendcycle * 2);
} }
@ -329,8 +329,8 @@ void get_time(uint8_t val[]) {
}; };
void set_time(uint8_t val[]) { void set_time(uint8_t val[]) {
ESP_LOGI(TAG, "Timesync requested by timeserver"); ESP_LOGI(TAG, "Remote command: timesync requested");
timeSync(); setTimeSyncIRQ();
}; };
void set_flush(uint8_t val[]) { void set_flush(uint8_t val[]) {
@ -339,6 +339,15 @@ void set_flush(uint8_t val[]) {
// used to open receive window on LoRaWAN class a nodes // used to open receive window on LoRaWAN class a nodes
}; };
void set_enscount(uint8_t val[]) {
ESP_LOGI(TAG, "Remote command: set ENS_COUNT to %s", val[0] ? "on" : "off");
cfg.enscount = val[0] ? 1 : 0;
if (val[0])
cfg.payloadmask |= SENSOR1_DATA;
else
cfg.payloadmask &= ~SENSOR1_DATA;
}
// assign previously defined functions to set of numeric remote commands // assign previously defined functions to set of numeric remote commands
// format: opcode, function, #bytes params, // format: opcode, function, #bytes params,
// flag (true = do make settings persistent / false = don't) // flag (true = do make settings persistent / false = don't)
@ -355,11 +364,11 @@ static const cmd_t table[] = {
{0x11, set_monitor, 1, true}, {0x12, set_beacon, 7, false}, {0x11, set_monitor, 1, true}, {0x12, set_beacon, 7, false},
{0x13, set_sensor, 2, true}, {0x14, set_payloadmask, 1, true}, {0x13, set_sensor, 2, true}, {0x14, set_payloadmask, 1, true},
{0x15, set_bme, 1, true}, {0x16, set_batt, 1, true}, {0x15, set_bme, 1, true}, {0x16, set_batt, 1, true},
{0x17, set_wifiscan, 1, true}, {0x80, get_config, 0, false}, {0x17, set_wifiscan, 1, true}, {0x18, set_enscount, 1, true},
{0x81, get_status, 0, false}, {0x83, get_batt, 0, false}, {0x80, get_config, 0, false}, {0x81, get_status, 0, false},
{0x84, get_gps, 0, false}, {0x85, get_bme, 0, false}, {0x83, get_batt, 0, false}, {0x84, get_gps, 0, false},
{0x86, get_time, 0, false}, {0x87, set_time, 0, false}, {0x85, get_bme, 0, false}, {0x86, get_time, 0, false},
{0x99, set_flush, 0, false}}; {0x87, set_time, 0, false}, {0x99, set_flush, 0, false}};
static const uint8_t cmdtablesize = static const uint8_t cmdtablesize =
sizeof(table) / sizeof(table[0]); // number of commands in command table sizeof(table) / sizeof(table[0]); // number of commands in command table

View File

@ -1,9 +1,9 @@
// Basic Config // Basic Config
#include "senddata.h" #include "senddata.h"
Ticker sendcycler; Ticker sendTimer;
void sendcycle() { void setSendIRQ() {
xTaskNotifyFromISR(irqHandlerTask, SENDCYCLE_IRQ, eSetBits, NULL); xTaskNotifyFromISR(irqHandlerTask, SENDCYCLE_IRQ, eSetBits, NULL);
} }
@ -154,6 +154,7 @@ void sendData() {
payload.addSensor(sensor_read(1)); payload.addSensor(sensor_read(1));
SendPayload(SENSOR1PORT, prio_normal); SendPayload(SENSOR1PORT, prio_normal);
#if (COUNT_ENS) #if (COUNT_ENS)
if (cfg.countermode != 1)
cwa_clear(); cwa_clear();
#endif #endif
break; break;

View File

@ -57,6 +57,7 @@ uint8_t *sensor_read(uint8_t sensor) {
// insert user specific sensor data frames here // insert user specific sensor data frames here
// note: Sensor1 fields are used for ENS count, if ENS detection enabled // note: Sensor1 fields are used for ENS count, if ENS detection enabled
#if (COUNT_ENS) #if (COUNT_ENS)
if (cfg.enscount)
payload.addCount(cwa_report(), MAC_SNIFF_BLE_CWA); payload.addCount(cwa_report(), MAC_SNIFF_BLE_CWA);
#else #else
buf[0] = length; buf[0] = length;
@ -65,7 +66,6 @@ uint8_t *sensor_read(uint8_t sensor) {
buf[3] = 0x03; buf[3] = 0x03;
#endif #endif
break; break;
case 2: case 2:
buf[0] = length; buf[0] = length;

View File

@ -23,7 +23,7 @@ HardwareSerial IF482(2); // use UART #2 (#1 may be in use for serial GPS)
Ticker timesyncer; Ticker timesyncer;
void timeSync() { xTaskNotify(irqHandlerTask, TIMESYNC_IRQ, eSetBits); } void setTimeSyncIRQ() { xTaskNotify(irqHandlerTask, TIMESYNC_IRQ, eSetBits); }
void calibrateTime(void) { void calibrateTime(void) {
ESP_LOGD(TAG, "[%0.3f] calibrateTime, timeSource == %d", millis() / 1000.0, ESP_LOGD(TAG, "[%0.3f] calibrateTime, timeSource == %d", millis() / 1000.0,
@ -85,7 +85,7 @@ void IRAM_ATTR setMyTime(uint32_t t_sec, uint16_t t_msec,
vTaskDelay(pdMS_TO_TICKS(1000 - t_msec % 1000)); vTaskDelay(pdMS_TO_TICKS(1000 - t_msec % 1000));
} }
ESP_LOGD(TAG, "[%0.3f] UTC time: %d.%03d sec", millis() / 1000.0, ESP_LOGI(TAG, "[%0.3f] UTC time: %d.%03d sec", millis() / 1000.0,
time_to_set, t_msec % 1000); time_to_set, t_msec % 1000);
// if we have got an external timesource, set RTC time and shift RTC_INT pulse // if we have got an external timesource, set RTC time and shift RTC_INT pulse
@ -104,12 +104,12 @@ void IRAM_ATTR setMyTime(uint32_t t_sec, uint16_t t_msec,
setTime(time_to_set); // set the time on top of second setTime(time_to_set); // set the time on top of second
timeSource = mytimesource; // set global variable timeSource = mytimesource; // set global variable
timesyncer.attach(TIME_SYNC_INTERVAL * 60, timeSync); timesyncer.attach(TIME_SYNC_INTERVAL * 60, setTimeSyncIRQ);
ESP_LOGI(TAG, "[%0.3f] Timesync finished, time was set | source: %c", ESP_LOGD(TAG, "[%0.3f] Timesync finished, time was set | source: %c",
millis() / 1000.0, timeSetSymbols[mytimesource]); millis() / 1000.0, timeSetSymbols[mytimesource]);
} else { } else {
timesyncer.attach(TIME_SYNC_INTERVAL_RETRY * 60, timeSync); timesyncer.attach(TIME_SYNC_INTERVAL_RETRY * 60, setTimeSyncIRQ);
ESP_LOGI(TAG, "[%0.3f] Timesync failed, invalid time fetched | source: %c", ESP_LOGD(TAG, "[%0.3f] Timesync failed, invalid time fetched | source: %c",
millis() / 1000.0, timeSetSymbols[mytimesource]); millis() / 1000.0, timeSetSymbols[mytimesource]);
} }
} }
@ -167,8 +167,8 @@ void timepulse_start(void) {
#endif #endif
// start cyclic time sync // start cyclic time sync
timeSync(); // init systime by RTC or GPS or LORA setTimeSyncIRQ(); // init systime by RTC or GPS or LORA
timesyncer.attach(TIME_SYNC_INTERVAL * 60, timeSync); timesyncer.attach(TIME_SYNC_INTERVAL * 60, setTimeSyncIRQ);
} }
// interrupt service routine triggered by either pps or esp32 hardware timer // interrupt service routine triggered by either pps or esp32 hardware timer

View File

@ -29,7 +29,7 @@ static uint32_t timesync_timestamp[TIME_SYNC_SAMPLES][no_of_timestamps];
static TaskHandle_t timeSyncProcTask; static TaskHandle_t timeSyncProcTask;
// create task for timeserver handshake processing, called from main.cpp // create task for timeserver handshake processing, called from main.cpp
void timesync_init() { void timesync_init(void) {
xTaskCreatePinnedToCore(timesync_processReq, // task function xTaskCreatePinnedToCore(timesync_processReq, // task function
"timesync_proc", // name of task "timesync_proc", // name of task
2048, // stack size of task 2048, // stack size of task
@ -69,8 +69,12 @@ void IRAM_ATTR timesync_processReq(void *taskparameter) {
// wait for kickoff // wait for kickoff
ulTaskNotifyTake(pdFALSE, portMAX_DELAY); ulTaskNotifyTake(pdFALSE, portMAX_DELAY);
// initialize flag and counters
timeSyncPending = true; timeSyncPending = true;
time_offset_ms = sample_idx = 0; time_offset_ms = sample_idx = 0;
if (++time_sync_seqNo > TIME_SYNC_MAX_SEQNO)
time_sync_seqNo = 0;
// wait until we are joined if we are not // wait until we are joined if we are not
while (!LMIC.devaddr) { while (!LMIC.devaddr) {
@ -81,7 +85,7 @@ void IRAM_ATTR timesync_processReq(void *taskparameter) {
for (uint8_t i = 0; i < TIME_SYNC_SAMPLES; i++) { for (uint8_t i = 0; i < TIME_SYNC_SAMPLES; i++) {
// send timesync request // send timesync request
#if (TIME_SYNC_LORASERVER) // aks user's timeserver (for LoRAWAN < 1.0.3) #if (TIME_SYNC_LORASERVER) // ask user's timeserver (for LoRAWAN < 1.0.3)
payload.reset(); payload.reset();
payload.addByte(time_sync_seqNo); payload.addByte(time_sync_seqNo);
SendPayload(TIMEPORT, prio_high); SendPayload(TIMEPORT, prio_high);
@ -111,9 +115,7 @@ void IRAM_ATTR timesync_processReq(void *taskparameter) {
timesync_timestamp[sample_idx][timesync_tx]; timesync_timestamp[sample_idx][timesync_tx];
#endif #endif
// increment sample_idx and time_sync_seqNo, keeping it in range // increment sample index
if (++time_sync_seqNo > TIME_SYNC_MAX_SEQNO)
time_sync_seqNo = 0;
sample_idx++; sample_idx++;
// if we are not in last cycle, pause until next cycle // if we are not in last cycle, pause until next cycle
@ -146,15 +148,17 @@ void IRAM_ATTR timesync_processReq(void *taskparameter) {
// send timesync end char to show timesync was successful // send timesync end char to show timesync was successful
payload.reset(); payload.reset();
payload.addByte(TIME_SYNC_END_FLAG); payload.addByte(TIME_SYNC_END_FLAG);
SendPayload(RCMDPORT, prio_high); SendPayload(TIMEPORT, prio_high);
goto Finish; goto Finish;
Fail: Fail:
// set retry timer // set retry timer
timesyncer.attach(TIME_SYNC_INTERVAL_RETRY * 60, timeSync); timesyncer.attach(TIME_SYNC_INTERVAL_RETRY * 60, setTimeSyncIRQ);
// intentionally fallthrough to Finish here
Finish: Finish:
// end of time critical section: release app irq lock // end of time critical section: release app irq lock
timeSyncPending = false;
unmask_user_IRQ(); unmask_user_IRQ();
} // infinite while(1) } // infinite while(1)
@ -179,6 +183,7 @@ void IRAM_ATTR timesync_serverAnswer(void *pUserData, int flag) {
// mask application irq to ensure accurate timing // mask application irq to ensure accurate timing
mask_user_IRQ(); mask_user_IRQ();
// return code: 0 = failed / 1 = success
int rc = 0; int rc = 0;
// cast back void parameter to a pointer // cast back void parameter to a pointer
uint8_t *p = (uint8_t *)pUserData, rcv_seqNo = *p; uint8_t *p = (uint8_t *)pUserData, rcv_seqNo = *p;