Merge remote-tracking branch 'upstream/master'

This commit is contained in:
Antonio Vanegas 2021-01-08 13:57:03 +01:00
commit 02b630176e
52 changed files with 1477 additions and 1148 deletions

View File

@ -16,15 +16,13 @@ Tutorial (in german language): https://www.heise.de/select/make/2019/1/155109923
# Use case # Use case
Paxcounter is a proof-of-concept device for metering passenger flows in realtime. It counts how many mobile devices are around. This gives an estimation how many people are around. Paxcounter detects Wifi and Bluetooth signals in the air, focusing on mobile devices by filtering vendor OUIs in the MAC adress. Paxcounter is an [ESP32](https://www.espressif.com/en/products/socs/esp32) MCU based device for metering passenger flows in realtime. It counts how many mobile devices are around. This gives an estimation how many people are around. Paxcounter detects Wifi and Bluetooth signals in the air, focusing on mobile devices by evaluating their MAC adresses.
Intention of this project is to do this without intrusion in privacy: You don't need to track people owned devices, if you just want to count them. Therefore, Paxcounter does not persistenly store MAC adresses and does no kind of fingerprinting the scanned devices. Intention of this project is to do this without intrusion in privacy: You don't need to track people owned devices, if you just want to count them. Therefore, Paxcounter does not persistenly store MAC adresses and does no kind of fingerprinting the scanned devices.
Data is transferred to a server via a LoRaWAN network, and/or a wired SPI slave interface. It can also be stored on a local SD-card. Data can either be be stored on a local SD-card, transferred to cloud using LoRaWAN network or MQTT over TCP/IP, or transmitted to a local host using serial (SPI) interface.
You can build this project battery powered and reach a full day uptime with a single 18650 Li-Ion cell. You can build this project battery powered using ESP32 deep sleep mode and reach long uptimes with a single 18650 Li-Ion cell.
This can all be done with a single small and cheap ESP32 board for less than $20.
# Hardware # Hardware
@ -52,6 +50,9 @@ LoLin32lite + [LoraNode32-Lite shield](https://github.com/hallard/LoLin32-Lite-L
- Generic ESP32 - Generic ESP32
Depending on board hardware following features are supported: Depending on board hardware following features are supported:
- LoRaWAN communication, supporting various payload formats (see enclosed .js converters)
- MQTT communication via TCP/IP and Ethernet interface (note: payload transmitted over MQTT will be base64 encoded)
- SPI serial communication to a local host
- LED (shows power & status) - LED (shows power & status)
- OLED Display (shows detailed status) - OLED Display (shows detailed status)
- RGB LED (shows colorized status) - RGB LED (shows colorized status)
@ -64,7 +65,6 @@ Depending on board hardware following features are supported:
- Switch external power / battery - Switch external power / battery
- LED Matrix display (similar to [this 64x16 model](https://www.instructables.com/id/64x16-RED-LED-Marquee/), can be ordered on [Aliexpress](https://www.aliexpress.com/item/P3-75-dot-matrix-led-module-3-75mm-high-clear-top1-for-text-display-304-60mm/32616683948.html)) - LED Matrix display (similar to [this 64x16 model](https://www.instructables.com/id/64x16-RED-LED-Marquee/), can be ordered on [Aliexpress](https://www.aliexpress.com/item/P3-75-dot-matrix-led-module-3-75mm-high-clear-top1-for-text-display-304-60mm/32616683948.html))
- SD-card (see section SD-card here) for logging pax data - SD-card (see section SD-card here) for logging pax data
- Ethernet interface for MQTT communication via TCP/IP
Target platform must be selected in `platformio.ini`.<br> Target platform must be selected in `platformio.ini`.<br>
Hardware dependent settings (pinout etc.) are stored in board files in /hal directory. If you want to use a ESP32 board which is not yet supported, use hal file generic.h and tailor pin mappings to your needs. Pull requests for new boards welcome.<br> Hardware dependent settings (pinout etc.) are stored in board files in /hal directory. If you want to use a ESP32 board which is not yet supported, use hal file generic.h and tailor pin mappings to your needs. Pull requests for new boards welcome.<br>
@ -183,20 +183,24 @@ Output of sensor and peripheral data is internally switched by a bitmask registe
| Bit | Sensordata | Default | Bit | Sensordata | Default
| --- | ------------- | ------- | --- | ------------- | -------
| 0 | GPS* | on | 0 | Paxcounter | on
| 1 | Beacon alarm | on | 1 | Beacon alarm | on
| 2 | BME280/680 | on | 2 | BME280/680 | on
| 3 | Paxcounter | on | 3 | GPS* | on
| 4 | User sensor 1 | on | 4 | User sensor 1 | on
| 5 | User sensor 2 | on | 5 | User sensor 2 | on
| 6 | User sensor 3 | on | 6 | User sensor 3 | on
| 7 | Batterylevel | off | 7 | Batterylevel | off
*) GPS data can also be combined with payload on port 1, *#define GPSPORT 1* in paxcounter.conf to enable *) GPS data can also be combined with paxcounter payload on port 1, *#define GPSPORT 1* in paxcounter.conf to enable
# Power saving mode
Paxcounter supports a battery friendly power saving mode. In this mode the device enters deep sleep, after all data is polled from all sensors and the dataset is completeley sent through all user configured channels (LORAWAN / SPI / MQTT). Set *#define SLEEPCYCLE* in paxcounter.conf to enable power saving mode and to specify the duration of a sleep cycle. Power consumption in deep sleep mode depends on your hardware, i.e. if on board peripherals can be switched off or set to a chip specific sleep mode either by MCU or by power management unit (PMU) as found on TTGO T-BEAM v1.0/V1.1. See *power.cpp* for power management, and *reset.cpp* for sleep and wakeup logic.
# Time sync # Time sync
Paxcounter can keep it's time-of-day synced with an external time source. Set *#define TIME_SYNC_INTERVAL* in paxcounter.conf to enable time sync. Supported external time sources are GPS, LORAWAN network time and LORAWAN application timeserver time. An on board DS3231 RTC is kept sycned as fallback time source. Time accuracy depends on board's time base which generates the pulse per second. Supported are GPS PPS, SQW output of RTC, and internal ESP32 hardware timer. Time base is selected by #defines in the board's hal file, see example in [**generic.h**](src/hal/generic.h). Bonus: If your LORAWAN network does not support network time, you can run a Node-Red timeserver application using the enclosed [**Timeserver code**](/src/Timeserver/Nodered-Timeserver.json). Configure MQTT nodes in Node-Red to the same LORAWAN application as paxocunter device is using. Paxcounter can keep it's time-of-day synced with an external time source. Set *#define TIME_SYNC_INTERVAL* in paxcounter.conf to enable time sync. Supported external time sources are GPS, LORAWAN network time and LORAWAN application timeserver time. An on board DS3231 RTC is kept sycned as fallback time source. Time accuracy depends on board's time base which generates the pulse per second. Supported are GPS PPS, SQW output of RTC, and internal ESP32 hardware timer. Time base is selected by #defines in the board's hal file, see example in [**generic.h**](src/hal/generic.h). Bonus: If your LORAWAN network does not support network time, you can run a Node-Red timeserver application using the enclosed [**Timeserver code**](/src/Node-RED/Timeserver.json). Configure MQTT nodes in Node-Red for the LORAWAN application used by paxocunter device.
# Wall clock controller # Wall clock controller
@ -211,7 +215,7 @@ This describes how to set up a mobile PaxCounter:<br> Follow all steps so far fo
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`: 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`:
#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 MACFILTER 0 // disable MAC filter
#define BLECOUNTER 1 // enable bluetooth sniffing #define BLECOUNTER 1 // enable bluetooth sniffing
#define WIFICOUNTER 0 // disable wifi sniffing (improves BLE scan speed) #define WIFICOUNTER 0 // disable wifi sniffing (improves BLE scan speed)
#define HAS_SENSOR_1 1 // optional: transmit ENS counter data to server #define HAS_SENSOR_1 1 // optional: transmit ENS counter data to server
@ -303,11 +307,11 @@ Hereafter described is the default *plain* format, which uses MSB bit numbering.
byte 6: Counter mode (0=cyclic unconfirmed, 1=cumulative, 2=cyclic confirmed) [default 0] byte 6: Counter mode (0=cyclic unconfirmed, 1=cumulative, 2=cyclic confirmed) [default 0]
bytes 7-8: RSSI limiter threshold value (negative) [default 0] bytes 7-8: RSSI limiter threshold value (negative) [default 0]
byte 9: Lora Payload send cycle in seconds/2 (0..255) [default 120] byte 9: Lora Payload send cycle in seconds/2 (0..255) [default 120]
byte 10: Wifi channel switch interval in seconds/100 (0..255) [default 50] byte 10: Wifi channel hopping interval in seconds/100 (0..255), 0 means no hopping [default 50]
byte 11: Bluetooth channel switch interval in seconds/100 (0..255) [efault 10] byte 11: Bluetooth channel switch interval in seconds/100 (0..255) [efault 10]
byte 12: Bluetooth scanner status (1=on, 0=0ff) [default 1] byte 12: Bluetooth scanner status (1=on, 0=0ff) [default 1]
byte 13: Wifi antenna switch (0=internal, 1=external) [default 0] byte 13: Wifi antenna switch (0=internal, 1=external) [default 0]
byte 14: Vendorfilter mode (0=disabled, 1=enabled) [default 0] byte 14: count randomizated MACs only (0=disabled, 1=enabled) [default 1]
byte 15: RGB LED luminosity (0..100 %) [default 30] byte 15: RGB LED luminosity (0..100 %) [default 30]
byte 16: Payload filter mask byte 16: Payload filter mask
byte 17: Beacon proximity alarm mode (1=on, 0=off) [default 0] byte 17: Beacon proximity alarm mode (1=on, 0=off) [default 0]
@ -360,9 +364,9 @@ Hereafter described is the default *plain* format, which uses MSB bit numbering.
# Remote control # Remote control
The device listenes for remote control commands on LoRaWAN Port 2. Multiple commands per downlink are possible by concatenating them. The device listenes for remote control commands on LoRaWAN Port 2. Multiple commands per downlink are possible by concatenating them, but must not exceed a maximum of 10 bytes per downlink.
Note: all settings are stored in NVRAM and will be reloaded when device starts. Note: settings can be stored in NVRAM to make them persistant (reloaded during device startup / restart). To store settings, use command 0x20.
Send for example `8386` as Downlink on Port 2 to get battery status and time/date from the device. Send for example `8386` as Downlink on Port 2 to get battery status and time/date from the device.
<img src="img/paxcounter_downlink_example.png"> <img src="img/paxcounter_downlink_example.png">
@ -437,17 +441,18 @@ Send for example `8386` as Downlink on Port 2 to get battery status and time/dat
0 ... 255 payload send cycle in seconds/2 0 ... 255 payload send cycle in seconds/2
e.g. 120 -> payload is transmitted each 240 seconds [default] e.g. 120 -> payload is transmitted each 240 seconds [default]
0x0B set Wifi channel switch interval timer 0x0B set Wifi channel hopping interval timer
0 ... 255 duration for scanning a wifi channel in seconds/100 0 ... 255 duration for scanning a wifi channel in seconds/100
e.g. 50 -> each channel is scanned for 500 milliseconds [default] e.g. 50 -> each channel is scanned for 500 milliseconds [default]
0 means no hopping, scanning on channel WIFI_CHANNEL_MIN only
0x0C set Bluetooth channel switch interval timer 0x0C set Bluetooth channel switch interval timer
0 ... 255 duration for scanning a bluetooth advertising channel in seconds/100 0 ... 255 duration for scanning a bluetooth advertising channel in seconds/100
e.g. 8 -> each channel is scanned for 80 milliseconds [default] e.g. 8 -> each channel is scanned for 80 milliseconds [default]
0x0D (NOT YET IMPLEMENTED) set BLE and WIFI vendorfilter mode 0x0D (NOT YET IMPLEMENTED) set BLE and WIFI MAC filter mode
0 = disabled (use to count devices, not people) 0 = disabled (use to count devices, not people)
1 = enabled [default] 1 = enabled [default]
@ -485,15 +490,15 @@ Send for example `8386` as Downlink on Port 2 to get battery status and time/dat
0x14 set payload mask 0x14 set payload mask
byte 1 = sensor data payload mask (0..255, meaning of bits see below) byte 1 = sensor data payload mask (0..255, meaning of bits see below)
0x01 = GPS_DATA 0x01 = COUNT_DATA
0x02 = ALARM_DATA 0x02 = ALARM_DATA
0x04 = MEMS_DATA 0x04 = MEMS_DATA
0x08 = COUNT_DATA (default) 0x08 = GPS_DATA
0x10 = SENSOR_1_DATA (ENS-COUNTS) 0x10 = SENSOR_1_DATA (also ENS counter)
0x20 = SENSOR_2_DATA 0x20 = SENSOR_2_DATA
0x40 = SENSOR_3_DATA 0x40 = SENSOR_3_DATA
0x80 = BATT_DATA 0x80 = BATT_DATA
bytes can be combined eg COUNT_DATA ;SENSOR_1_DATA ;BATT_DATA: `0x08 | 0x10 |0x80 = 0x98` bytes can be combined eg COUNT_DATA + SENSOR_1_DATA + BATT_DATA: `0x01 | 0x10 | 0x80 = 0x91`
0x15 set BME data on/off 0x15 set BME data on/off
@ -515,6 +520,19 @@ Send for example `8386` as Downlink on Port 2 to get battery status and time/dat
0 = disabled [default] 0 = disabled [default]
1 = enabled 1 = enabled
0x19 set sleep cycle
0 ... 255 device sleep cycle in seconds/2
e.g. 120 -> device sleeps 240 seconds after each send cycle [default = 0]
0x20 store device configuration
Current device runtime configuration is stored in NVRAM, will be reloaded after restart
0x21 load device configuration
Current device runtime configuration will be loaded from NVRAM, replacing current settings immediately (use with care!)
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

@ -21,43 +21,29 @@ extern Ticker bmecycler;
extern bmeStatus_t extern bmeStatus_t
bme_status; // Make struct for storing gps data globally available bme_status; // Make struct for storing gps data globally available
// --- Bosch BSEC v1.4.7.4 library configuration --- // --- Bosch BSEC library configuration ---
// 3,3V supply voltage; 3s max time between sensor_control calls; 4 days // We use 3,3V supply voltage; 3s max time between sensor_control calls; 4 days
// calibration. Change this const if not applicable for your application (see // calibration. Change this const if not applicable for your application (see
// BME680 datasheet) // BME680 datasheet) Note: 3s max time must not exceed BMECYCLE frequency set in
// Note: 3s max time not exceed BMECYCLE frequency set in paxcounter.conf! // paxcounter.conf!
const uint8_t bsec_config_iaq[454] = {
4, 7, 4, 1, 61, 0, 0, 0, 0, 0, 0, 0, 174, 1, 0, const uint8_t bsec_config_iaq[] = {
0, 48, 0, 1, 0, 0, 192, 168, 71, 64, 49, 119, 76, 0, 0, #include "config/generic_33v_3s_4d/bsec_iaq.txt"
225, 68, 137, 65, 0, 63, 205, 204, 204, 62, 0, 0, 64, 63, 205, };
204, 204, 62, 0, 0, 0, 0, 216, 85, 0, 100, 0, 0, 0, 0,
0, 0, 0, 0, 28, 0, 2, 0, 0, 244, 1, 225, 0, 25, 0, /* Configure the BSEC library with information about the sensor
0, 128, 64, 0, 0, 32, 65, 144, 1, 0, 0, 112, 65, 0, 0, 18v/33v = Voltage at Vdd. 1.8V or 3.3V
0, 63, 16, 0, 3, 0, 10, 215, 163, 60, 10, 215, 35, 59, 10, 3s/300s = BSEC operating mode, BSEC_SAMPLE_RATE_LP or BSEC_SAMPLE_RATE_ULP
215, 35, 59, 9, 0, 5, 0, 0, 0, 0, 0, 1, 88, 0, 9, 4d/28d = Operating age of the sensor in days
0, 229, 208, 34, 62, 0, 0, 0, 0, 0, 0, 0, 0, 218, 27, generic_18v_3s_4d
156, 62, 225, 11, 67, 64, 0, 0, 160, 64, 0, 0, 0, 0, 0, generic_18v_3s_28d
0, 0, 0, 94, 75, 72, 189, 93, 254, 159, 64, 66, 62, 160, 191, generic_18v_300s_4d
0, 0, 0, 0, 0, 0, 0, 0, 33, 31, 180, 190, 138, 176, 97, generic_18v_300s_28d
64, 65, 241, 99, 190, 0, 0, 0, 0, 0, 0, 0, 0, 167, 121, generic_33v_3s_4d
71, 61, 165, 189, 41, 192, 184, 30, 189, 64, 12, 0, 10, 0, 0, generic_33v_3s_28d
0, 0, 0, 0, 0, 0, 0, 229, 0, 254, 0, 2, 1, 5, 48, generic_33v_300s_4d
117, 100, 0, 44, 1, 112, 23, 151, 7, 132, 3, 197, 0, 92, 4, generic_33v_300s_28d
144, 1, 64, 1, 64, 1, 144, 1, 48, 117, 48, 117, 48, 117, 48, */
117, 100, 0, 100, 0, 100, 0, 48, 117, 48, 117, 48, 117, 100, 0,
100, 0, 48, 117, 48, 117, 100, 0, 100, 0, 100, 0, 100, 0, 48,
117, 48, 117, 48, 117, 100, 0, 100, 0, 100, 0, 48, 117, 48, 117,
100, 0, 100, 0, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44,
1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1,
44, 1, 8, 7, 8, 7, 8, 7, 8, 7, 8, 7, 8, 7, 8,
7, 8, 7, 8, 7, 8, 7, 8, 7, 8, 7, 8, 7, 8, 7,
112, 23, 112, 23, 112, 23, 112, 23, 112, 23, 112, 23, 112, 23, 112,
23, 112, 23, 112, 23, 112, 23, 112, 23, 112, 23, 112, 23, 255, 255,
255, 255, 255, 255, 255, 255, 220, 5, 220, 5, 220, 5, 255, 255, 255,
255, 255, 255, 220, 5, 220, 5, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 44, 1, 0, 0, 0, 0,
138, 80, 0, 0};
// Helper functions declarations // Helper functions declarations
int bme_init(); int bme_init();

View File

@ -10,12 +10,12 @@
#include "display.h" #include "display.h"
#include "sds011read.h" #include "sds011read.h"
#include "sdcard.h" #include "sdcard.h"
#include "macsniff.h"
extern Ticker cyclicTimer; extern Ticker cyclicTimer;
void setCyclicIRQ(void); void setCyclicIRQ(void);
void doHousekeeping(void); void doHousekeeping(void);
uint64_t uptime(void);
void reset_counters(void); void reset_counters(void);
uint32_t getFreeRAM(); uint32_t getFreeRAM();

View File

@ -18,25 +18,28 @@
#include "mallocator.h" #include "mallocator.h"
#include <bsec.h> #include <bsec.h>
#define _bit(b) (1U << (b))
#define _bitl(b) (1UL << (b))
// bits in payloadmask for filtering payload data // bits in payloadmask for filtering payload data
#define GPS_DATA (0x01) #define COUNT_DATA _bit(1)
#define ALARM_DATA (0x02) #define ALARM_DATA _bit(2)
#define MEMS_DATA (0x04) #define MEMS_DATA _bit(3)
#define COUNT_DATA (0x08) #define GPS_DATA _bit(4)
#define SENSOR1_DATA (0x10) #define SENSOR1_DATA _bit(5)
#define SENSOR2_DATA (0x20) #define SENSOR2_DATA _bit(6)
#define SENSOR3_DATA (0x40) #define SENSOR3_DATA _bit(7)
#define BATT_DATA (0x80) #define BATT_DATA _bit(8)
// bits in configmask for device runmode control // bits in configmask for device runmode control
#define GPS_MODE (0x01) #define GPS_MODE _bit(1)
#define ALARM_MODE (0x02) #define ALARM_MODE _bit(2)
#define BEACON_MODE (0x04) #define BEACON_MODE _bit(3)
#define UPDATE_MODE (0x08) #define UPDATE_MODE _bit(4)
#define FILTER_MODE (0x10) #define FILTER_MODE _bit(5)
#define ANTENNA_MODE (0x20) #define ANTENNA_MODE _bit(6)
#define BLE_MODE (0x40) #define BLE_MODE _bit(7)
#define SCREEN_MODE (0x80) #define SCREEN_MODE _bit(8)
// length of display buffer for lmic event messages // length of display buffer for lmic event messages
#define LMIC_EVENTMSG_LEN 17 #define LMIC_EVENTMSG_LEN 17
@ -56,19 +59,16 @@
; \ ; \
} }
// emulate millis to avoid rollovers #define _seconds() millis() / 1000.0
#define _millis() esp_timer_get_time() / 1000
#define _micros() esp_timer_get_time()
#define _seconds() _millis() / 1000.0
enum sendprio_t { prio_low, prio_normal, prio_high };
enum timesource_t { _gps, _rtc, _lora, _unsynced }; enum timesource_t { _gps, _rtc, _lora, _unsynced };
enum snifftype_t { MAC_SNIFF_WIFI, MAC_SNIFF_BLE, MAC_SNIFF_BLE_ENS }; enum snifftype_t { MAC_SNIFF_WIFI, MAC_SNIFF_BLE, MAC_SNIFF_BLE_ENS };
enum runmode_t { enum runmode_t {
RUNMODE_POWERCYCLE, RUNMODE_POWERCYCLE,
RUNMODE_NORMAL, RUNMODE_NORMAL,
RUNMODE_WAKEUP, RUNMODE_WAKEUP,
RUNMODE_UPDATE RUNMODE_UPDATE,
RUNMODE_SLEEP
}; };
// Struct holding devices's runtime configuration // Struct holding devices's runtime configuration
@ -84,12 +84,13 @@ typedef struct __attribute__((packed)) {
uint8_t countermode; // 0=cyclic unconfirmed, 1=cumulative, 2=cyclic confirmed uint8_t countermode; // 0=cyclic unconfirmed, 1=cumulative, 2=cyclic confirmed
int16_t rssilimit; // threshold for rssilimiter, negative value! int16_t rssilimit; // threshold for rssilimiter, negative value!
uint8_t sendcycle; // payload send cycle [seconds/2] uint8_t sendcycle; // payload send cycle [seconds/2]
uint8_t sleepcycle; // sleep cycle [seconds/2]
uint8_t wifichancycle; // wifi channel switch cycle [seconds/100] uint8_t wifichancycle; // wifi channel switch cycle [seconds/100]
uint8_t blescantime; // BLE scan cycle duration [seconds] uint8_t blescantime; // BLE scan cycle duration [seconds]
uint8_t blescan; // 0=disabled, 1=enabled uint8_t blescan; // 0=disabled, 1=enabled
uint8_t wifiscan; // 0=disabled, 1=enabled uint8_t wifiscan; // 0=disabled, 1=enabled
uint8_t wifiant; // 0=internal, 1=external (for LoPy/LoPy4) uint8_t wifiant; // 0=internal, 1=external (for LoPy/LoPy4)
uint8_t vendorfilter; // 0=disabled, 1=enabled uint8_t macfilter; // 0=disabled, 1=enabled
uint8_t rgblum; // RGB Led luminosity (0..100%) uint8_t rgblum; // RGB Led luminosity (0..100%)
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
@ -106,7 +107,6 @@ typedef struct __attribute__((packed)) {
typedef struct { typedef struct {
uint8_t MessageSize; uint8_t MessageSize;
uint8_t MessagePort; uint8_t MessagePort;
sendprio_t MessagePrio;
uint8_t Message[PAYLOAD_BUFFER_SIZE]; uint8_t Message[PAYLOAD_BUFFER_SIZE];
} MessageBuffer_t; } MessageBuffer_t;

View File

@ -2,8 +2,8 @@
#define _HASH_H #define _HASH_H
#include <Arduino.h> #include <Arduino.h>
#include <inttypes.h> #include <RokkitHash.h>
uint32_t IRAM_ATTR rokkit(const char *data, int len); uint32_t IRAM_ATTR hash(const char *data, int len);
#endif #endif

View File

@ -1,17 +1,16 @@
#ifndef _IRQHANDLER_H #ifndef _IRQHANDLER_H
#define _IRQHANDLER_H #define _IRQHANDLER_H
#define DISPLAY_IRQ 0x001 #define DISPLAY_IRQ _bitl(1)
#define BUTTON_IRQ 0x002 #define BUTTON_IRQ _bitl(2)
#define SENDCYCLE_IRQ 0x004 #define SENDCYCLE_IRQ _bitl(3)
#define CYCLIC_IRQ 0x008 #define CYCLIC_IRQ _bitl(4)
#define TIMESYNC_IRQ 0x010 #define TIMESYNC_IRQ _bitl(5)
#define MASK_IRQ 0x020 #define MASK_IRQ _bitl(6)
#define UNMASK_IRQ 0x040 #define UNMASK_IRQ _bitl(7)
#define BME_IRQ 0x080 #define BME_IRQ _bitl(8)
#define MATRIX_DISPLAY_IRQ 0x100 #define MATRIX_DISPLAY_IRQ _bitl(9)
#define PMU_IRQ 0x200 #define PMU_IRQ _bitl(10)
#define MQTT_IRQ 0x400
#include "globals.h" #include "globals.h"
#include "button.h" #include "button.h"
@ -21,7 +20,6 @@
#include "bmesensor.h" #include "bmesensor.h"
#include "power.h" #include "power.h"
#include "ledmatrixdisplay.h" #include "ledmatrixdisplay.h"
#include "mqttclient.h"
void irqHandler(void *pvParameters); void irqHandler(void *pvParameters);
void mask_user_IRQ(); void mask_user_IRQ();

View File

@ -20,9 +20,10 @@
extern TaskHandle_t lmicTask, lorasendTask; extern TaskHandle_t lmicTask, lorasendTask;
void lora_stack_reset(); esp_err_t lmic_init(void);
esp_err_t lora_stack_init(bool do_join);
void lora_setupForNetwork(bool preJoin); void lora_setupForNetwork(bool preJoin);
void SaveLMICToRTC(int deepsleep_sec);
void LoadLMICFromRTC();
void lmictask(void *pvParameters); void lmictask(void *pvParameters);
void gen_lora_deveui(uint8_t *pdeveui); void gen_lora_deveui(uint8_t *pdeveui);
void RevBytes(unsigned char *b, size_t c); void RevBytes(unsigned char *b, size_t c);
@ -33,6 +34,7 @@ void os_getDevEui(u1_t *buf);
void lora_send(void *pvParameters); 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);
uint8_t myBattLevelCb(void *pUserData); 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,

View File

@ -14,7 +14,7 @@
#include "corona.h" #include "corona.h"
#endif #endif
uint16_t get_salt(void); uint32_t renew_salt(void);
uint64_t macConvert(uint8_t *paddr); uint64_t macConvert(uint8_t *paddr);
esp_err_t macQueueInit(void); esp_err_t macQueueInit(void);
void mac_process(void *pvParameters); void mac_process(void *pvParameters);

View File

@ -5,34 +5,22 @@
#include "rcommand.h" #include "rcommand.h"
#include <MQTT.h> #include <MQTT.h>
#include <ETH.h> #include <ETH.h>
#include <mbedtls/base64.h>
#define MQTT_ETHERNET 0 // set to 0 to run on Wifi
#define MQTT_INTOPIC "paxin"
#define MQTT_OUTTOPIC "paxout"
#define MQTT_PORT 1883
#define MQTT_SERVER "broker.shiftr.io"
//#define MQTT_CLIENTNAME "arduino"
#define MQTT_USER "try"
#define MQTT_PASSWD "try"
#define MQTT_RETRYSEC 20 // retry reconnect every 20 seconds
#define MQTT_KEEPALIVE 10 // keep alive interval in seconds
#define MQTT_TIMEOUT 1000 // timeout for all mqtt commands in milliseconds
#ifndef MQTT_CLIENTNAME #ifndef MQTT_CLIENTNAME
#define MQTT_CLIENTNAME clientId.c_str() #define MQTT_CLIENTNAME clientId
#endif #endif
extern TaskHandle_t mqttTask; extern TaskHandle_t mqttTask;
void mqtt_enqueuedata(MessageBuffer_t *message); void mqtt_enqueuedata(MessageBuffer_t *message);
uint32_t mqtt_queuewaiting(void);
void mqtt_queuereset(void); void mqtt_queuereset(void);
void setMqttIRQ(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);
void mqtt_callback(MQTTClient *client, char topic[], char payload[], void mqtt_callback(MQTTClient *client, char *topic, char *payload, int length);
int length);
void NetworkEvent(WiFiEvent_t event); void NetworkEvent(WiFiEvent_t event);
esp_err_t mqtt_init(void); esp_err_t mqtt_init(void);
void mqtt_deinit(void);
#endif // _MQTTCLIENT_H #endif // _MQTTCLIENT_H

View File

@ -15,15 +15,28 @@
#include "timesync.h" #include "timesync.h"
#include "blescan.h" #include "blescan.h"
// maximum number of elements in rcommand interpreter queue
#define RCMD_QUEUE_SIZE 5
extern TaskHandle_t rcmdTask;
// table of remote commands and assigned functions // table of remote commands and assigned functions
typedef struct { typedef struct {
const uint8_t opcode; const uint8_t opcode;
void (*func)(uint8_t []); void (*func)(uint8_t[]);
const uint8_t params; const uint8_t params;
const bool store;
} cmd_t; } cmd_t;
void rcommand(const uint8_t cmd[], const uint8_t cmdlength); // Struct for remote command processing queue
void do_reset(bool warmstart); typedef struct {
uint8_t cmd[10];
uint8_t cmdLen;
} RcmdBuffer_t;
void IRAM_ATTR rcommand(const uint8_t *cmd, const size_t cmdlength);
void rcmd_queuereset(void);
uint32_t rcmd_queuewaiting(void);
void rcmd_deinit(void);
esp_err_t rcmd_init(void);
#endif #endif

View File

@ -10,7 +10,9 @@
#include "power.h" #include "power.h"
void do_reset(bool warmstart); void do_reset(bool warmstart);
void do_after_reset(int reason); void do_after_reset(void);
void enter_deepsleep(const int wakeup_sec, const gpio_num_t wakeup_gpio); void enter_deepsleep(const uint64_t wakeup_sec = 60,
const gpio_num_t wakeup_gpio = GPIO_NUM_MAX);
unsigned long long uptime(void);
#endif // _RESET_H #endif // _RESET_H

View File

@ -15,10 +15,11 @@
extern Ticker sendTimer; extern Ticker sendTimer;
void SendPayload(uint8_t port, sendprio_t prio); void SendPayload(uint8_t port);
void sendData(void); void sendData(void);
void checkSendQueues(void); void checkSendQueues(void);
void flushQueues(); void flushQueues(void);
bool allQueuesEmtpy(void);
void setSendIRQ(void); void setSendIRQ(void);
#endif // _SENDDATA_H_ #endif // _SENDDATA_H_

View File

@ -27,11 +27,12 @@ licenses. Refer to LICENSE.txt file in repository for more details.
#include "globals.h" #include "globals.h"
#include "rcommand.h" #include "rcommand.h"
esp_err_t spi_init();
extern TaskHandle_t spiTask; extern TaskHandle_t spiTask;
esp_err_t spi_init();
void spi_deinit(void);
void spi_enqueuedata(MessageBuffer_t *message); void spi_enqueuedata(MessageBuffer_t *message);
void spi_queuereset(); uint32_t spi_queuewaiting(void);
void spi_queuereset(void);
#endif // _SPISLAVE_H #endif // _SPISLAVE_H

View File

@ -1,243 +0,0 @@
std::array<uint32_t, 1683> vendors = {
0xa44519, 0x2446c8, 0xd0d003, 0x8cb84a, 0x608b0e, 0x88299c, 0x7c8956,
0x201742, 0xd09c7a, 0xc4e39f, 0x2479f3, 0x8c79f5, 0xccd281, 0x88b291,
0xc42ad0, 0xd81edd, 0xacf6f7, 0xc02e25, 0x64c2de, 0x58c6f0, 0x70bc10,
0x2ca9f0, 0x586b14, 0xbcb863, 0x1040f3, 0x44e66e, 0xc0e862, 0xf40616,
0x601d91, 0xd4c94b, 0x70bbe9, 0xf83880, 0x4c569d, 0x38539c, 0x402619,
0x6ce85c, 0xe4b2fb, 0xd003df, 0xb4cb57, 0xfc039f, 0x18d717, 0x0057c1,
0xa4d990, 0x20a60c, 0x08c5e1, 0x3c576c, 0xe4b021, 0x28ff3c, 0xf099b6,
0x1094bb, 0x88e9fe, 0x94bf2d, 0xf86fc1, 0x38892c, 0x749eaf, 0x8035c1,
0x2047da, 0xe4c483, 0xc048e6, 0x785dc8, 0x40cbc0, 0xc4618b, 0x08e689,
0xdc56e7, 0x38e60a, 0xdcbfe9, 0xcc6ea4, 0xa46cf1, 0x08aed6, 0xa816d0,
0x3408bc, 0x1c36bb, 0x3c2eff, 0xf06e0b, 0x24f677, 0xb0ca68, 0xc83c85,
0x5433cb, 0x149f3c, 0x9ce063, 0xd03169, 0x7c6456, 0x084acf, 0xb8634d,
0xa4e975, 0x3035ad, 0x844167, 0x9800c6, 0xac1f74, 0xa85c2c, 0x00db70,
0x4c49e3, 0x389af6, 0xe0aa96, 0x507705, 0xa89675, 0x00ec0a, 0xc8d7b0,
0x9097f3, 0x7c1c68, 0xc087eb, 0x388c50, 0xd4503f, 0x245ba7, 0x70f087,
0xf0d7aa, 0x3096fb, 0x4827ea, 0x7c787e, 0x28395e, 0x38295a, 0x8cf5a3,
0xd47ae2, 0xd8e0e1, 0xe44790, 0xb0702d, 0x6c19c0, 0xf01dbc, 0xf83f51,
0x404e36, 0xdc0b34, 0x60a4d0, 0x008701, 0x5c9960, 0x2c3361, 0x101dc0,
0x78471d, 0xa07591, 0x0cdfa4, 0x68ebae, 0x444e1a, 0x4844f7, 0x001377,
0x002454, 0xe81132, 0x50b7c3, 0x1c5a3e, 0xa02195, 0xf4d9fb, 0x3c6200,
0x00e3b2, 0x301966, 0x94350a, 0x0017d5, 0x001e7d, 0x001df6, 0xc06599,
0xbc79ad, 0xf0e77e, 0xf008f1, 0xe47cf9, 0x58c38b, 0x001d25, 0x08fd0e,
0x041bba, 0x889b39, 0xe432cb, 0x10d542, 0xa0821f, 0xf06bca, 0xac3613,
0xbc8ccd, 0xd022be, 0xec9bf3, 0xf409d8, 0x4c3c16, 0x0073e0, 0x343111,
0x4849c7, 0x849866, 0x9476b7, 0xc4438f, 0xa09169, 0x9893cc, 0x3ccd93,
0x2021a5, 0x6cd68a, 0x002483, 0x001fe3, 0x2c54cf, 0x485929, 0x58a2b5,
0x10f96f, 0x4c6641, 0xac3743, 0x28e31f, 0xecadb8, 0x9801a7, 0xb8bbaf,
0x60c5ad, 0x24f094, 0x086d41, 0xe4faed, 0x288335, 0xdccf96, 0xccb11a,
0xac61ea, 0x38b54d, 0x1c5cf2, 0xe0c767, 0x80ed2c, 0xa80600, 0xf05a09,
0x503275, 0x08fc88, 0xf8d0bd, 0x94b10a, 0x3cbbfd, 0xa48431, 0xa0b4a5,
0xe4f8ef, 0x78595e, 0x0c1420, 0x24f5aa, 0x988389, 0x84a466, 0xc4576e,
0x508569, 0x489d24, 0xb07994, 0xa470d6, 0xc8f230, 0x8c0ee3, 0x28ed6a,
0x38f23e, 0x902155, 0xd8b377, 0xa09347, 0xe8bba8, 0xb0aa36, 0xa4d18c,
0x241eeb, 0xcc25ef, 0x28cfe9, 0x00a040, 0x003065, 0x3cd0f8, 0x680927,
0x1093e9, 0x442a60, 0xc82a14, 0x3c0754, 0xa4b197, 0xa4d1d2, 0x28cfda,
0x6cc26b, 0x44d884, 0x64200c, 0x001451, 0x001e52, 0x0021e9, 0x002608,
0x0026b0, 0x0026bb, 0xd49a20, 0xf81edf, 0xcc08e0, 0xf0b479, 0x045453,
0xe88d28, 0x949426, 0x207d74, 0xf4f15a, 0xc86f1d, 0x18af8f, 0xc8b5b7,
0x90b21f, 0xb8e856, 0xd89695, 0x64a3cb, 0x30f7c5, 0x40b395, 0x44fb42,
0x98fe94, 0xd8004d, 0x542696, 0xc8334b, 0x64e682, 0x9c207b, 0xb065bd,
0x8c2daa, 0x848506, 0xf4f951, 0xc06394, 0x3090ab, 0x1499e2, 0xfce998,
0x0cbc9f, 0x34363b, 0xd0a637, 0x789f70, 0xe0accb, 0xa0999b, 0x24240e,
0x903c92, 0xd81d72, 0xd8bb2c, 0xd04f7e, 0x9cf387, 0xa85b78, 0xb418d1,
0xf0dbf8, 0x48746e, 0x341298, 0x70e72c, 0x70ece4, 0x54ae27, 0xc8f650,
0x68ae20, 0xac87a3, 0xa88e24, 0x2078f0, 0x70480f, 0xcc20e8, 0xf0b0e7,
0x0469f8, 0x205531, 0xe8b4c8, 0xd087e2, 0xf05b7b, 0xb047bf, 0x7c0bc6,
0x80c5e6, 0x5440ad, 0x804e81, 0xfc8f90, 0xe89120, 0x4480eb, 0x9cd35b,
0xa89fba, 0x4886e8, 0x30d587, 0xec59e7, 0x2c2997, 0xd059e4, 0x14a364,
0x7ced8d, 0x002538, 0x0022a1, 0x7cf31b, 0x0c2fb0, 0xf4d620, 0x8cf112,
0x44aeab, 0xa4f05e, 0x28167f, 0xf887f1, 0x305714, 0xc8b1cd, 0x1460cb,
0xb8f12a, 0x04c807, 0x20f478, 0x90735a, 0x00fa21, 0x7c2302, 0xd49dc0,
0xe0dcff, 0x846fce, 0x804a14, 0x703c69, 0x489dd1, 0xb06fe0, 0xc08c71,
0x60d0a9, 0x48fda3, 0xa45046, 0x007c2d, 0xb831b5, 0x14c213, 0xa4d931,
0xbcfed9, 0x808223, 0x5029f5, 0x007204, 0xbca58b, 0x80ceb9, 0xe06267,
0x082525, 0xf460e2, 0x1cc3eb, 0x00c3f4, 0x7836cc, 0x48605f, 0x2816a8,
0x9c2ea1, 0xbce143, 0x647033, 0x846878, 0xc8d083, 0x6030d4, 0xf895ea,
0x18f1d8, 0x30d9d9, 0x0cf346, 0x80ad16, 0xfc643a, 0xa8515b, 0xb4f7a1,
0x88bd45, 0x54fcf0, 0x306a85, 0x4cdd31, 0xd0817a, 0x98ca33, 0x68ab1e,
0x70ef00, 0xbcffeb, 0x0c1539, 0x88b4a6, 0x2c8a72, 0xd88f76, 0x409c28,
0xd0b128, 0xbc5451, 0xec51bc, 0xf079e8, 0x58e28f, 0x787b8a, 0x88365f,
0x2c4053, 0x3cf591, 0x602101, 0xacafb9, 0x041b6d, 0xecf342, 0x503da1,
0x4c1a3d, 0x503237, 0xb0481a, 0xb49cdf, 0x48bf6b, 0x3c0518, 0x900628,
0x9c84bf, 0x00b362, 0xe4e4ab, 0x60334b, 0xf4f524, 0xcc2d83, 0xfcd848,
0xec107b, 0x1c232c, 0x0021d1, 0x001fcc, 0xec8892, 0x60a10a, 0x8c71f8,
0xcc051b, 0x8c7712, 0x9463d1, 0x0021d2, 0x5c497d, 0x7825ad, 0xece09b,
0xbc20a4, 0x08d42b, 0x789ed0, 0xb0c4e7, 0xa00798, 0x001fcd, 0x38ece4,
0x945103, 0x002490, 0x0023d7, 0x549b12, 0xfca13e, 0x24c696, 0x94d771,
0xe84e84, 0x001632, 0xe4e0c5, 0xc81479, 0x1caf05, 0x0016db, 0x001ee2,
0x20d5bf, 0x5ce8eb, 0xc0bdd1, 0xb479a7, 0xb0df3a, 0x805719, 0x34be00,
0x78521a, 0x38256b, 0x205ef7, 0x141f78, 0xb47443, 0x30766f, 0xa8922c,
0xf80cf3, 0xc49a02, 0x001f6b, 0x0026e2, 0xa860b6, 0xc4b301, 0xe05f45,
0x483b38, 0x1c9148, 0x5c70a3, 0x64cc2e, 0xf877b8, 0x182195, 0x44783e,
0x30636b, 0xa4f1e8, 0x14bb6e, 0xe498d1, 0x6c2779, 0x28cc01, 0x6cf373,
0x9c3aaf, 0x781fdb, 0x4ca56d, 0xb86ce8, 0x0cb319, 0x183f47, 0xb46293,
0x50a4c8, 0x1867b0, 0x6c8336, 0x002557, 0x001ccc, 0xb4e1c4, 0xe0757d,
0x34bb26, 0x806c1b, 0x2082c0, 0xdc6dcd, 0x440010, 0x0056cd, 0x00cdfe,
0xe498d6, 0xf8db7f, 0x64a769, 0xe899c4, 0xbccfcc, 0xf431c3, 0x64a5c3,
0xbc3aea, 0x7c1dd9, 0xa086c6, 0x9c99a0, 0x584498, 0x002332, 0x00236c,
0x0023df, 0x002500, 0x0025bc, 0x0019e3, 0x001b63, 0x001ec2, 0x001ff3,
0x0010fa, 0x0050e4, 0x000d93, 0x7cfadf, 0x78a3e4, 0x148fc6, 0x286ab8,
0x28e02c, 0xe0b9ba, 0x00c610, 0xb8f6b1, 0x8cfaba, 0x7cd1c3, 0xf0dce2,
0x24ab81, 0xe0f847, 0x28e7cf, 0xe4ce8f, 0xa82066, 0xbc52b7, 0x5c5948,
0xc8bcc8, 0xe8040b, 0x145a05, 0x1caba7, 0xc0847a, 0x34159e, 0x58b035,
0xdc86d8, 0x90b931, 0xd0e140, 0x24a2e1, 0x80ea96, 0x600308, 0x04f13e,
0x98f0ab, 0x7831c1, 0x783a84, 0x5c8d4e, 0x8863df, 0x881fa1, 0xc8e0eb,
0x98b8e3, 0x885395, 0x786c1c, 0x4c8d79, 0x1ce62b, 0x0c3021, 0x0c3e9f,
0xfcfc48, 0x9c293f, 0x087402, 0x94f6a3, 0x98e0d9, 0xcc29f5, 0x285aeb,
0xf02475, 0x2c1f23, 0x549f13, 0xf0dbe2, 0x748114, 0x18f643, 0xa45e60,
0xa01828, 0xd0034b, 0x10417f, 0xa8667f, 0xd02598, 0x80be05, 0x24a074,
0x84788b, 0x587f57, 0x006d52, 0xacee9e, 0xb857d8, 0xb844d9, 0x1c56fe,
0xac5a14, 0x08eca9, 0xf8cfc5, 0x7840e4, 0xe09971, 0x10d38a, 0x206274,
0xd48f33, 0x20a99b, 0x6077e2, 0xfc1910, 0x3ca10d, 0x646cb2, 0x680571,
0x14b484, 0x3059b7, 0xa43d78, 0x00eebd, 0x502e5c, 0x980d2e, 0xd4206d,
0x7c1e52, 0xdcb4c4, 0x7c6f06, 0x749ef5, 0x68bfc4, 0x04b1a1, 0xcc464e,
0x507ac5, 0x6cd71f, 0x4c6be8, 0x8c861e, 0x542b8d, 0x8ce5c0, 0xf08a76,
0xecaa25, 0x9078b2, 0xbc7fa4, 0x687d6b, 0x485169, 0xa8db03, 0x60ab67,
0x4418fd, 0x005b94, 0xe0897e, 0xa8346a, 0x3c20f6, 0x7c38ad, 0x885a06,
0x2c5d34, 0x7c035e, 0xd467d3, 0xa41232, 0x64c753, 0x38f9d3, 0xfc183c,
0xf47def, 0x7c8bb5, 0x3830f9, 0x90e17b, 0xd81c79, 0x58e6ba, 0x1801f1,
0x9c0cdf, 0x14c697, 0x7c03ab, 0x00b5d0, 0x1496e5, 0xd07fa0, 0x7c6b9c,
0x108ee0, 0xfca621, 0xd832e3, 0x9487e0, 0x4466fc, 0x50a009, 0x50a67f,
0xd461da, 0xb841a4, 0x9ce65e, 0xc49880, 0xe0338e, 0xf01898, 0x881908,
0x5c0947, 0x14205e, 0x08f69c, 0x641cae, 0x003de8, 0x48c796, 0xf4c248,
0xf47190, 0x2802d8, 0x24181d, 0x641cb0, 0x54b802, 0xd4909c, 0xe4e0a6,
0x00e091, 0x503cea, 0xb4f1da, 0x80b03d, 0xe49adc, 0xace4b5, 0xf06d78,
0xd0d2b0, 0x448f17, 0x1cddea, 0x78886d, 0x20ee28, 0xb4f61c, 0x08f4ab,
0x8c8590, 0x6c96cf, 0x508f4c, 0xd463c6, 0xdc44b6, 0x1007b6, 0x342d0d,
0x54bd79, 0x30074d, 0x947be7, 0x5092b9, 0xdc74a8, 0x88d50c, 0xc4ae12,
0x989e63, 0x886b6e, 0xd4dccd, 0x484baa, 0xd4ae05, 0xdca904, 0x6cab31,
0x4c74bf, 0x5082d5, 0xf0ee10, 0xb81daa, 0x5caf06, 0x64b0a6, 0x7c04d0,
0x84fcac, 0xdc0c5c, 0x70700d, 0x186590, 0xf86214, 0x784f43, 0x404d7f,
0x1c48ce, 0x6c5c14, 0xcc61e5, 0x609ac1, 0x20dbab, 0x748d08, 0x9c8ba0,
0xcc088d, 0x38a4ed, 0x0007ab, 0xe8e5d6, 0xc87e75, 0x00265f, 0x00233a,
0x382dd1, 0x10ddb1, 0x00166c, 0x001599, 0x0012fb, 0xd0667b, 0x1c66aa,
0x1489fd, 0xbc851f, 0x002491, 0x3c8bfe, 0xd4e8b2, 0xe8039a, 0x30cda7,
0x2c4401, 0xb8d9ce, 0x002339, 0x5001bb, 0x001247, 0x0015b9, 0xb85e7b,
0x1c77f6, 0x742344, 0xc8ba94, 0x843838, 0x54880e, 0xf025b7, 0xe492fb,
0x6cb7f4, 0x181eb0, 0x5c3c27, 0xbc72b1, 0x78f7be, 0x684898, 0x3423ba,
0x400e85, 0x88797e, 0xd013fd, 0x888322, 0xe89309, 0x6cd032, 0x3cbdd8,
0x78c3e9, 0x64899a, 0xf8a9d0, 0xccfa00, 0xa816b2, 0x64bc0c, 0x74a722,
0xf01c13, 0x344df7, 0x583f54, 0xc01ada, 0x8c1abf, 0x30cbf8, 0xa0cbfd,
0xe45d75, 0x408805, 0x98398e, 0xd0fccc, 0xc09727, 0x84a134, 0x0c5101,
0x2cf0a2, 0x68fb7e, 0x6c2483, 0x5cca1a, 0xac0d1b, 0x0034da, 0x202d07,
0xb44bd2, 0xdc415f, 0x102ab3, 0xe8b2ac, 0xe49a79, 0xf45c89, 0x20768f,
0x300d43, 0x607edd, 0x08373d, 0xc488e5, 0x1077b1, 0x64b853, 0x389496,
0x5056bf, 0x90f1aa, 0x74458a, 0xfcc734, 0x8425db, 0xb0ec71, 0xe458b8,
0x088c2c, 0xa49a58, 0x08ee8b, 0x68ed43, 0x70aab2, 0x000f86, 0xf8e079,
0xccc3ea, 0x40786a, 0x38cada, 0x34ab37, 0x18af61, 0x5cf938, 0x002376,
0x38e7d8, 0x188796, 0xb4cef6, 0xcc4463, 0x6c72e7, 0x741bb2, 0xf8a45f,
0x640980, 0x185936, 0x60fec5, 0xe425e7, 0x7c6d62, 0x40d32d, 0xc42c03,
0x9027e4, 0xbc926b, 0x101c0c, 0x080007, 0x0016cb, 0x0017f2, 0x001f5b,
0x002436, 0x00254b, 0x6c3e6d, 0xbc6778, 0x20c9d0, 0x68967b, 0x581faa,
0x88c663, 0xa46706, 0x8c5877, 0xa8fad8, 0x008865, 0xbc3baf, 0x3ce072,
0x84fcfe, 0xe48b7f, 0xd8d1cb, 0xb817c2, 0x7c11be, 0x98d6bb, 0x283737,
0x50ead6, 0x189efc, 0x804971, 0x7cf05f, 0x109add, 0x848e0c, 0x3c15c2,
0x6c709f, 0x6476ba, 0x34e2fd, 0x04489a, 0x80e650, 0x90fd61, 0x2cf0ee,
0x5c97f3, 0x8c2937, 0xaccf5c, 0x80006e, 0xa4c361, 0xb09fba, 0x0c4de9,
0xe0f5c6, 0xa0edcd, 0x087045, 0xa88808, 0xc0f2fb, 0x24e314, 0xf0f61c,
0x38484c, 0x38c986, 0xd03311, 0xd4f46f, 0x48437c, 0x34a395, 0x787e61,
0x60f81d, 0x5cf5da, 0x18ee69, 0x649abe, 0xac293a, 0x9cfc01, 0x9c35eb,
0xf099bf, 0x94e96a, 0x507a55, 0x5882a8, 0x24da9b, 0xe83a12, 0x80656d,
0x38d40b, 0x209bcd, 0xfcf136, 0x18895b, 0x7cf90e, 0x50f0d3, 0x78bdbc,
0x84119e, 0xe4907e, 0x149a10, 0x485073, 0x244b03, 0x74e28c, 0x244b81,
0x60af6d, 0xb85a73, 0x981dfa, 0x102f6b, 0xc44202, 0x103047, 0xf884f2,
0x5c2e59, 0xe0cbee, 0x002248, 0x8031f0, 0xe0d083, 0xd80b9a, 0x5c666c,
0xc4e1a1, 0x1855e3, 0xe450eb, 0x886440, 0x6070c0, 0xf0c371, 0x987a14,
0xc83ddc, 0x845733, 0x1819d6, 0xbc98df, 0x70dda8, 0x4c6f9c, 0x4883b4,
0x0017fa, 0x941625, 0x34a8eb, 0xa483e7, 0xf4afe7, 0xac88fd, 0x20326c,
0x6489f1, 0x2034fb, 0xa89ced, 0xe458e7, 0xdc080f, 0xf8e94e, 0xec2ce2,
0x40bc60, 0xe83617, 0x9c648b, 0x344262, 0x14d00d, 0x703a51, 0x04e598,
0x6cc7ec, 0xc0bdc8, 0x647bce, 0xa887b3, 0x6c006b, 0x24fce5, 0x9caa1b,
0x70fd46, 0x8c83e1, 0x889f6f, 0x482ca0, 0x0ccb85, 0x08d46a, 0x68e7c2,
0x58b10f, 0x645aed, 0xc0b658, 0x48a91c, 0x50bc96, 0xfc2a9c, 0xa056f3,
0x549963, 0x90dd5d, 0xc819f7, 0x3880df, 0x3cdcbc, 0x804e70, 0xd4e6b7,
0x04d13a, 0xc48466, 0x347c25, 0xcc2db7, 0x0c9838, 0xd41a3f, 0x5c865c,
0x04b167, 0xec8350, 0x2c5491, 0xe42b34, 0x3c2ef9, 0xa04ea7, 0xf0989d,
0xd80831, 0x10f1f2, 0x982d68, 0xb019c6, 0x3866f0, 0x703eac, 0x7c2edd,
0x3cf7a4, 0x986f60, 0xc49ded, 0x9810e8, 0xc0d012, 0xbca920, 0x48a195,
0xf80377, 0x8058f8, 0xdca4ca, 0x8c8fe9, 0x5cba37, 0x2c200b, 0x8866a5,
0xacc1ee, 0x805a04, 0xbc8385, 0x2c598a, 0xe47dbd, 0x001cb3, 0x24920e,
0xfc4203, 0xa01081, 0xf07960, 0xa0d795, 0xbc4760, 0x04180f, 0x2013e0,
0x002566, 0xc09f05, 0x606bbd, 0x00214c, 0x0018af, 0x001ee1, 0x00166b,
0x0000f0, 0x8cc8cd, 0xa8f274, 0xd487d8, 0x184617, 0x380a94, 0xd0dfc7,
0xd0c1b1, 0x8018a7, 0xf47b5e, 0x70f927, 0xc45006, 0x88329b, 0x1449e0,
0xd02544, 0xbc4486, 0x20d390, 0x9401c2, 0x50fc9f, 0x380b40, 0xb8ff61,
0xf0728c, 0x34aa8b, 0x24dbed, 0x68c44d, 0x440444, 0x84b541, 0x006f64,
0xdc6672, 0xf8e61a, 0xcc2d8c, 0x98d6f7, 0x700514, 0xe892a4, 0x10683f,
0x40b0fa, 0x0025e5, 0x0021fb, 0x34fcef, 0xbcf5ac, 0x0c4885, 0x0022a9,
0x9c2a83, 0xa039f7, 0xb0e235, 0x14c913, 0xe09861, 0xa06090, 0xbc765e,
0xd85b2a, 0x90c1c6, 0x70a2b3, 0xf40f24, 0x4c57ca, 0xc46ab7, 0x48e9f1,
0xc83f26, 0x40163b, 0xc83870, 0x6c8fb5, 0x1c9e46, 0xc0ccf8, 0x9c4fda,
0x8489ad, 0x647791, 0x9ce6e7, 0x9c0298, 0x28987b, 0x54fa3e, 0x0c8910,
0x78abbb, 0xd8c4e9, 0xbcd11f, 0xf4428f, 0x446d6c, 0x00f46f, 0x0c715d,
0x34bb1f, 0x406f2a, 0x985fd3, 0x60beb5, 0xf8f1b6, 0xf4f1e1, 0x9cd917,
0x9068c3, 0x68dbca, 0x086698, 0xbc5436, 0x044bed, 0x6c8dc1, 0x0cd746,
0x60a37d, 0xd40b1a, 0xfc64ba, 0x9060f1, 0xa4516f, 0x68dfdd, 0x98fae3,
0xf0b429, 0xb8782e, 0x000502, 0x000a95, 0x00264a, 0x041e64, 0x001124,
0x002241, 0x7cc537, 0x78ca39, 0x18e7f4, 0x70cd60, 0x8c7b9d, 0xd89e3f,
0xb8c75d, 0x0c74c2, 0x403004, 0x842999, 0x74e2f5, 0xe0c97a, 0x68a86d,
0x7cc3a1, 0x7073cb, 0x90840d, 0xe80688, 0xec852f, 0x00f4b9, 0x5c95ae,
0x9803d8, 0x60c547, 0x685b35, 0x2cb43a, 0x689c70, 0x380f4a, 0x3010e4,
0xa886dd, 0x444c0c, 0xb4f0ab, 0x80929f, 0x9c04eb, 0x5c969d, 0x609217,
0x84b153, 0xe06678, 0x48d705, 0x041552, 0xcc785f, 0x88cb87, 0xf0c1f1,
0x843835, 0x8c006d, 0xa8968a, 0xf41ba1, 0x60d9c7, 0x3cab8e, 0xf82793,
0x907240, 0x908d6c, 0xb8098a, 0x4c7c5f, 0x68644b, 0xc81ee7, 0xa43135,
0x68d93c, 0x00f76f, 0xc88550, 0x7014a6, 0x985aeb, 0x78d75f, 0xe0b52d,
0x6c94f8, 0xc0cecd, 0x2cae2b, 0xc869cd, 0xa4b805, 0x5cadcf, 0xbc6c21,
0xf40e22, 0x544e90, 0xc01173, 0xbce63f, 0x7c9122, 0xacbc32, 0x2827bf,
0x800184, 0x8cbfa6, 0xc8a823, 0xb0c559, 0xbc1485, 0x9c6c15, 0xc0335e,
0xd0929e, 0x083d88, 0x608f5c, 0x109266, 0x281878, 0x847a88, 0xa0f450,
0xa826d9, 0x00155d, 0x00125a, 0x000d3a, 0x0003ff, 0x18d0c5, 0x1cccd6,
0x582059, 0x74e1b6, 0xf40e01, 0x1495ce, 0x50de06, 0xcc660a, 0xfc1d43,
0x0024e9, 0xb4c4fc, 0x7cd661, 0x98b8ba, 0x103025, 0xb8b2f8, 0x98460a,
0xb85d0a, 0x7c9a1d, 0x5462e2, 0x149d99, 0x489507, 0xd85575, 0xd411a3,
0x04ba8d, 0xe446da, 0x58d9c3, 0x3c8375, 0xb8bc5b, 0xd8ce3a, 0xb8c74a,
0x94f6d6, 0xf82d7c, 0xc09ad0, 0x94b01f, 0x90633b, 0xfcaab6, 0x782327,
0xdcf756, 0x74b587, 0xfcb6d8, 0x18810e, 0x608c4a, 0x241b7a, 0x8cfe57,
0xc0a600, 0xa823fe, 0x1c427d, 0xcc2119, 0x304b07, 0x000393, 0x702ad5,
0x74eb80, 0x0ce0dc, 0xd868c3, 0xc493d9, 0xa82bb9, 0x1c1ac0, 0x88ae07,
0x40831d, 0xdcd3a2, 0x5c1dd9, 0x68fef7, 0xd07714, 0x587a6a, 0x705aac,
0x685acf, 0x0ca8a7, 0x2c61f6, 0xd4a33d, 0xf0766f, 0x4098ad, 0x6c4d73,
0x68ef43, 0xd02b20, 0xd86375, 0x30b4b8, 0x9c8c6e, 0x18f0e4, 0x00bf61,
0xd00401, 0xdc5583, 0xb8c111, 0x9ce33f, 0x7867d7, 0x087808, 0x887598,
0xc0174d, 0xa407b6, 0x04d6aa, 0x08152f, 0xf4f5db, 0xc0a53e, 0xecd09f,
0xa8be27, 0x94d029, 0x308454, 0x5c5181, 0x608e08, 0x4c189a, 0x58c5cb,
0xb4bff6, 0x74f61c, 0x84c0ef, 0xa8b86e, 0xc40bcb, 0xb83765, 0x14bd61,
0xc0d3c0, 0x948bc1, 0x14568e, 0xd4619d, 0x7c5049, 0x64b473, 0x7451ba,
0x7802f8, 0xec01ee, 0x682737, 0x58404e, 0xd0c5f3, 0xbc9fef, 0x20ab37,
0x60f445, 0x2c0e3d, 0x88e87f, 0x9cf48e, 0x5cf7e6, 0xb853ac, 0x203cae,
0x2cbaba, 0x40d3ae, 0xa03be3, 0x4c3275, 0x3816d1, 0xd0176a, 0xd48890,
0x5492be, 0x949aa9, 0x001c43, 0x002399, 0x0017c9, 0x7cf854, 0xc4731e,
0xec1f72, 0xe4121d, 0xe8508b, 0xf8042e, 0x0023d6, 0x001b98, 0x001a8a,
0x3c5a37, 0xf49f54, 0x34c3ac, 0x44f459, 0x00265d, 0x002567, 0xbcb1f3,
0xc462ea, 0x182666, 0x30d6c9, 0xcc07ab, 0x1c62b8, 0xccf9e8, 0xd857ef,
0x18e2c2, 0x28bab5, 0xe440e2, 0x103b59, 0xd890e8, 0x9852b1, 0x14f42a,
0x0808c2, 0xccfe3c, 0xb43a28, 0x78a873, 0xd83062, 0xc041f6, 0x001e75,
0x001c62, 0xf895c7, 0x34145f, 0x0821ef, 0x505527, 0x88c9d0, 0x8c3ae3,
0x00aa70, 0x60e3ac, 0x4c0bbe, 0xb88d12, 0x845181, 0x348a7b, 0x78009e,
0xacc33a, 0x54f201, 0x70288b, 0x8c8ef2, 0x90b0ed, 0x04d3cf, 0x1c3ade,
0x509ea7, 0xa88195, 0x88add2, 0xac5f3e, 0xb48b19, 0xbcec5d, 0x28a02b,
0xb8c68e, 0x04fe31, 0x4cbca5, 0xd831cf, 0x188331, 0x9c65b0, 0x8455a5,
0xa87c01, 0x50f520, 0x64b310, 0xa4ebd3, 0xb0d09c, 0x50c8e5, 0x5cf6dc,
0x0026ff, 0xa4e4b8, 0xf40b93, 0x1c69a5, 0x94ebcd, 0x141aa3, 0x1430c6,
0x88074b, 0x3871de, 0x7081eb, 0x78f882, 0x606944, 0x807abf, 0x00092d,
0x7c6193, 0x0452f3, 0x90e7c4, 0xa81b5a, 0x2c5bb8, 0xacf7f3, 0xd4970b,
0x8cbebe, 0x14f65a, 0x009ec8, 0x0c1daf, 0x3480b3, 0xf48b32, 0x60fb42,
0x64b9e8, 0xd8a25e, 0x000a27, 0x001d4f, 0x002312, 0x60facd, 0x003ee1,
0xfc253f, 0x183451, 0x0c771a, 0x286aba, 0x3451c9, 0x406c8f, 0xd023db,
0x70dee2, 0x5855ca, 0xdc2b61, 0x40a6d9, 0x4cb199, 0xc09f42, 0x705681,
0x040cce, 0x403cfc, 0x4860bc, 0xf0cba1, 0x182032, 0x34c059, 0xf0d1a9,
0x14109f, 0x04f7e4, 0xdc9b9c, 0x54724f, 0x8c7c92, 0xb03495, 0x042665,
0xec3586, 0x54eaa8, 0x28e14c, 0xe4c63d, 0x54e43a, 0x04db56, 0x04e536,
0xe8802e, 0x006171, 0x6c4008, 0x7c6df8, 0x78fd94, 0x2cbe08, 0xac3c0b,
0x701124, 0xf437b7, 0xd8cf9c, 0xa8bbcf, 0xac7f3e, 0x280b5c, 0xacfdec,
0x28f076, 0x20a2e4, 0xbc4cc4, 0xdc3714, 0x40331a, 0xccc760, 0x7c0191,
0x84100d, 0x80d605, 0xdc2b2a, 0x48137e, 0x382de8, 0xc08997, 0x04c23e,
0x380195, 0xb4ae2b, 0x5c5188, 0x0ce725, 0xe0db10, 0x1432d1, 0x1816c9,
0x842e27, 0x0c413e, 0x9000db, 0xb4ef39, 0xc808e9, 0x183a2d, 0x8463d6,
0xb84fd5, 0xc0eefb, 0x206e9c, 0x6c2f2c, 0x18227e, 0x30c7ae, 0x501ac5,
0x6045bd, 0x1cb094, 0xe85b5b, 0x0025ae, 0x001dd8, 0x000a75, 0xa4c939,
0xc0dcda, 0x04b429, 0x48794d,
};

View File

@ -8,6 +8,8 @@
#include "antenna.h" // code for switching wifi antennas #include "antenna.h" // code for switching wifi antennas
#include "macsniff.h" #include "macsniff.h"
extern TimerHandle_t WifiChanTimer;
void wifi_sniffer_init(void); void wifi_sniffer_init(void);
void switch_wifi_sniffer(uint8_t state); void switch_wifi_sniffer(uint8_t state);
void IRAM_ATTR wifi_sniffer_packet_handler(void *buff, void IRAM_ATTR wifi_sniffer_packet_handler(void *buff,

View File

@ -113,16 +113,17 @@ String BintrayClient::getLatestVersion() const
ESP_LOGE(TAG, "Error: Firmware version data invalid."); ESP_LOGE(TAG, "Error: Firmware version data invalid.");
return version; return version;
} }
StaticJsonBuffer<bufferSize> jsonBuffer; StaticJsonDocument<bufferSize> doc;
DeserializationError err = deserializeJson(doc, jsonResult.c_str());
JsonObject &root = jsonBuffer.parseObject(jsonResult.c_str());
// Check for errors in parsing // Check for errors in parsing
if (!root.success()) if (err)
{ {
ESP_LOGE(TAG, "Error: Firmware version data not found."); ESP_LOGE(TAG, "Error: Firmware version data not found.");
return version; return version;
} }
return root.get<String>("name"); return doc["name"].as<String>();
} }
String BintrayClient::getBinaryPath(const String &version) const String BintrayClient::getBinaryPath(const String &version) const
@ -137,14 +138,15 @@ String BintrayClient::getBinaryPath(const String &version) const
ESP_LOGE(TAG, "Error: Firmware download path data invalid."); ESP_LOGE(TAG, "Error: Firmware download path data invalid.");
return path; return path;
} }
StaticJsonBuffer<bufferSize> jsonBuffer; StaticJsonDocument<bufferSize> doc;
JsonArray &root = jsonBuffer.parseArray(jsonResult.c_str()); DeserializationError err = deserializeJson(doc, jsonResult.c_str());
JsonObject &firstItem = root[0];
if (!root.success()) JsonObject firstItem = doc[0];
if (err)
{ //Check for errors in parsing { //Check for errors in parsing
ESP_LOGE(TAG, "Error: Firmware download path not found."); ESP_LOGE(TAG, "Error: Firmware download path not found.");
return path; return path;
} }
return "/" + getUser() + "/" + getRepository() + "/" + firstItem.get<String>("path"); return "/" + getUser() + "/" + getRepository() + "/" + firstItem["path"].as<String>();
} }

View File

@ -1,5 +1,5 @@
; PlatformIO Project Configuration File ; PlatformIO Project Configuration File
; NOTE: PlatformIO v4 is needed! ; NOTE: PlatformIO v5 is needed!
; ;
; Please visit documentation for the other options and examples ; Please visit documentation for the other options and examples
; http://docs.platformio.org/page/projectconf.html ; http://docs.platformio.org/page/projectconf.html
@ -7,7 +7,7 @@
; ---> SELECT THE TARGET PLATFORM HERE! <--- ; ---> SELECT THE TARGET PLATFORM HERE! <---
[board] [board]
halfile = generic.h ;halfile = generic.h
;halfile = ebox.h ;halfile = ebox.h
;halfile = eboxtube.h ;halfile = eboxtube.h
;halfile = ecopower.h ;halfile = ecopower.h
@ -47,7 +47,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.4 release_version = 2.1.1
; 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
@ -55,17 +55,17 @@ 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@2.0.0 platform_espressif32 = espressif32@2.1.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.2.0 mcci-catena/MCCI LoRaWAN LMIC library @ ^3.3.0
lib_deps_display = lib_deps_display =
bitbank2/OneBitDisplay @ 1.9.0 bitbank2/OneBitDisplay @ 1.9.0
bitbank2/BitBang_I2C @ ^2.1.3 bitbank2/BitBang_I2C @ ^2.1.3
ricmoo/QRCode @ ^0.0.1 ricmoo/QRCode @ ^0.0.1
bodmer/TFT_eSPI @ ^2.2.20 bodmer/TFT_eSPI @ ^2.3.51
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 =
@ -76,10 +76,11 @@ 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.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.6.1480
https://github.com/ricki-z/SDS011.git https://github.com/ricki-z/SDS011.git
lib_deps_basic = lib_deps_basic =
bblanchon/ArduinoJson @ <6 https://github.com/SukkoPera/Arduino-Rokkit-Hash.git
bblanchon/ArduinoJson @ ^6
jchristensen/Timezone @ ^1.2.4 jchristensen/Timezone @ ^1.2.4
makuna/RTC @ ^2.3.5 makuna/RTC @ ^2.3.5
spacehuhn/SimpleButton spacehuhn/SimpleButton
@ -116,7 +117,7 @@ framework = arduino
board = esp32dev board = esp32dev
board_build.partitions = min_spiffs.csv board_build.partitions = min_spiffs.csv
upload_speed = ${common.upload_speed} upload_speed = ${common.upload_speed}
;upload_port = COM8 ;upload_port = COM3
platform = ${common.platform_espressif32} platform = ${common.platform_espressif32}
lib_deps = ${common.lib_deps_all} lib_deps = ${common.lib_deps_all}
build_flags = ${common.build_flags_all} build_flags = ${common.build_flags_all}
@ -135,4 +136,3 @@ upload_protocol = esptool
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#develop
platform_packages = framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32.git

467
src/Node-RED/MQTT.json Normal file
View File

@ -0,0 +1,467 @@
[
{
"id": "7bc3eb4f.fe4b64",
"type": "mqtt in",
"z": "c26229c6.2d1ae8",
"name": "Olimex",
"topic": "paxout/#",
"qos": "2",
"datatype": "utf8",
"broker": "bd56b04d.db0ff",
"x": 190,
"y": 160,
"wires": [
[
"d0276db6.83c46"
]
]
},
{
"id": "4f59740b.296b7c",
"type": "debug",
"z": "c26229c6.2d1ae8",
"name": "data",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "payload",
"targetType": "msg",
"statusVal": "",
"statusType": "auto",
"x": 1050,
"y": 280,
"wires": []
},
{
"id": "f10f8199.25183",
"type": "binary",
"z": "c26229c6.2d1ae8",
"name": "wifi",
"property": "payload",
"pattern": "l16, x16",
"x": 630,
"y": 100,
"wires": [
[
"800f5faa.15155"
]
]
},
{
"id": "df4bdce5.dc219",
"type": "switch",
"z": "c26229c6.2d1ae8",
"name": "Ports",
"property": "topic",
"propertyType": "msg",
"rules": [
{
"t": "eq",
"v": "paxout/1",
"vt": "str"
},
{
"t": "eq",
"v": "paxout/2",
"vt": "str"
},
{
"t": "eq",
"v": "paxout/3",
"vt": "str"
},
{
"t": "eq",
"v": "paxout/4",
"vt": "str"
},
{
"t": "eq",
"v": "paxout/5",
"vt": "str"
},
{
"t": "eq",
"v": "paxout/6",
"vt": "str"
},
{
"t": "eq",
"v": "paxout/7",
"vt": "str"
},
{
"t": "eq",
"v": "paxout/8",
"vt": "str"
},
{
"t": "eq",
"v": "paxout/9",
"vt": "str"
},
{
"t": "eq",
"v": "paxout/10",
"vt": "str"
},
{
"t": "eq",
"v": "status",
"vt": "str"
}
],
"checkall": "true",
"repair": false,
"outputs": 11,
"x": 370,
"y": 260,
"wires": [
[
"f10f8199.25183"
],
[
"e2974f79.55d5b"
],
[
"5f6987dd.b89378"
],
[],
[
"eaca83c4.5fcaf"
],
[],
[],
[
"7b6cbc9d.18fe44"
],
[
"d269537.4738fb"
],
[
"409e4762.71ff68"
],
[
"13fbfc5d.40dcc4"
]
]
},
{
"id": "eaca83c4.5fcaf",
"type": "binary",
"z": "c26229c6.2d1ae8",
"name": "button",
"property": "payload",
"pattern": "b8 => button",
"x": 630,
"y": 280,
"wires": [
[
"a6037445.eb8ac8"
]
]
},
{
"id": "409e4762.71ff68",
"type": "binary",
"z": "c26229c6.2d1ae8",
"name": "cwa",
"property": "payload",
"pattern": "l16 => cwa",
"x": 630,
"y": 140,
"wires": [
[
"2966fd1c.0b33d2"
]
]
},
{
"id": "d0276db6.83c46",
"type": "base64",
"z": "c26229c6.2d1ae8",
"name": "Decode",
"action": "",
"property": "payload",
"x": 200,
"y": 260,
"wires": [
[
"df4bdce5.dc219"
]
]
},
{
"id": "13fbfc5d.40dcc4",
"type": "binary",
"z": "c26229c6.2d1ae8",
"name": "Connection",
"property": "payload",
"pattern": "b8[19]|str(\"ascii\")",
"x": 650,
"y": 440,
"wires": [
[
"4f59740b.296b7c"
]
]
},
{
"id": "800f5faa.15155",
"type": "ui_chart",
"z": "c26229c6.2d1ae8",
"name": "pax",
"group": "6838565d.06bf08",
"order": 1,
"width": 0,
"height": 0,
"label": "Pax [Wifi]",
"chartType": "line",
"legend": "false",
"xformat": "dd HH:mm",
"interpolate": "linear",
"nodata": "Noch keine Daten",
"dot": false,
"ymin": "",
"ymax": "",
"removeOlder": 1,
"removeOlderPoints": "",
"removeOlderUnit": "3600",
"cutout": 0,
"useOneColor": false,
"useUTC": false,
"colors": [
"#1f77b4",
"#aec7e8",
"#ff7f0e",
"#2ca02c",
"#98df8a",
"#d62728",
"#ff9896",
"#9467bd",
"#c5b0d5"
],
"outputs": 1,
"x": 810,
"y": 100,
"wires": [
[]
]
},
{
"id": "2966fd1c.0b33d2",
"type": "ui_chart",
"z": "c26229c6.2d1ae8",
"name": "cwa",
"group": "6838565d.06bf08",
"order": 1,
"width": 0,
"height": 0,
"label": "CWA",
"chartType": "line",
"legend": "false",
"xformat": "dd HH:mm",
"interpolate": "linear",
"nodata": "",
"dot": false,
"ymin": "",
"ymax": "",
"removeOlder": 1,
"removeOlderPoints": "",
"removeOlderUnit": "3600",
"cutout": 0,
"useOneColor": false,
"useUTC": false,
"colors": [
"#1f77b4",
"#aec7e8",
"#ff7f0e",
"#2ca02c",
"#98df8a",
"#d62728",
"#ff9896",
"#9467bd",
"#c5b0d5"
],
"outputs": 1,
"x": 810,
"y": 140,
"wires": [
[]
]
},
{
"id": "a6037445.eb8ac8",
"type": "ui_toast",
"z": "c26229c6.2d1ae8",
"position": "top right",
"displayTime": "3",
"highlight": "",
"sendall": true,
"outputs": 0,
"ok": "OK",
"cancel": "",
"raw": false,
"topic": "Button",
"name": "Popup",
"x": 810,
"y": 280,
"wires": []
},
{
"id": "b4478f8.f8b747",
"type": "mqtt out",
"z": "c26229c6.2d1ae8",
"name": "Olimex",
"topic": "paxin",
"qos": "",
"retain": "",
"broker": "bd56b04d.db0ff",
"x": 190,
"y": 480,
"wires": []
},
{
"id": "e2974f79.55d5b",
"type": "binary",
"z": "c26229c6.2d1ae8",
"name": "device status",
"property": "payload",
"pattern": "l16 => voltage,\nl64 => uptime,\nb8 => temperature,\nl32 => ram,\nb8 => reset0,\nb8 => reset1",
"x": 650,
"y": 200,
"wires": [
[
"4f59740b.296b7c"
]
]
},
{
"id": "5f6987dd.b89378",
"type": "binary",
"z": "c26229c6.2d1ae8",
"name": "device config",
"property": "payload",
"pattern": "b8 => loradr,\nb8 => txPower,\nl16 => rssilimit,\nb8 => sendcycle,\nb8 => wifichancycle,\nb8 => blescantime,\nb8 => rgblum,\nb8 => configmask,\nb8 => payloadmask,\nb8z|str(\"ascii\") => version",
"x": 650,
"y": 240,
"wires": [
[
"4f59740b.296b7c"
]
]
},
{
"id": "7b6cbc9d.18fe44",
"type": "binary",
"z": "c26229c6.2d1ae8",
"name": "battery",
"property": "payload",
"pattern": "l16 => voltage",
"x": 630,
"y": 360,
"wires": [
[
"4f59740b.296b7c"
]
]
},
{
"id": "d269537.4738fb",
"type": "binary",
"z": "c26229c6.2d1ae8",
"name": "time",
"property": "payload",
"pattern": "l32 => unixtime",
"x": 630,
"y": 400,
"wires": [
[
"4f59740b.296b7c"
]
]
},
{
"id": "ce0cb32b.b7b96",
"type": "base64",
"z": "c26229c6.2d1ae8",
"name": "Encode",
"action": "",
"property": "payload",
"x": 200,
"y": 420,
"wires": [
[
"b4478f8.f8b747"
]
]
},
{
"id": "c246a129.eeb5e",
"type": "inject",
"z": "c26229c6.2d1ae8",
"name": "rcmd",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "paxin",
"payload": "[128, 129]",
"payloadType": "bin",
"x": 190,
"y": 360,
"wires": [
[
"ce0cb32b.b7b96"
]
]
},
{
"id": "bd56b04d.db0ff",
"type": "mqtt-broker",
"name": "Shiftr.io",
"broker": "public.cloud.shiftr.io",
"port": "1883",
"clientid": "",
"usetls": false,
"compatmode": false,
"keepalive": "60",
"cleansession": true,
"birthTopic": "paxout/status",
"birthQos": "0",
"birthPayload": "on",
"closeTopic": "paxout/status",
"closeQos": "0",
"closePayload": "off",
"willTopic": "paxout/status",
"willQos": "0",
"willPayload": "unknown"
},
{
"id": "6838565d.06bf08",
"type": "ui_group",
"name": "MQTT",
"tab": "ad5894fa.466c58",
"order": 1,
"disp": true,
"width": "6",
"collapse": false
},
{
"id": "ad5894fa.466c58",
"type": "ui_tab",
"name": "Paxcounter",
"icon": "people",
"order": 4
}
]

View File

@ -128,7 +128,7 @@ IRAM_ATTR void gap_callback_handler(esp_gap_ble_cb_event_t event,
switch (event) { switch (event) {
case ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT: case ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT:
// restart scan // restart scan
ESP_ERROR_CHECK(esp_ble_gap_start_scanning(BLESCANTIME)); esp_ble_gap_start_scanning(BLESCANTIME);
break; break;
case ESP_GAP_BLE_SCAN_RESULT_EVT: case ESP_GAP_BLE_SCAN_RESULT_EVT:
@ -136,7 +136,7 @@ IRAM_ATTR void gap_callback_handler(esp_gap_ble_cb_event_t event,
if (p->scan_rst.search_evt == if (p->scan_rst.search_evt ==
ESP_GAP_SEARCH_INQ_CMPL_EVT) // Inquiry complete, scan is done ESP_GAP_SEARCH_INQ_CMPL_EVT) // Inquiry complete, scan is done
{ // restart scan { // restart scan
ESP_ERROR_CHECK(esp_ble_gap_start_scanning(BLESCANTIME)); esp_ble_gap_start_scanning(BLESCANTIME);
return; return;
} }
@ -151,7 +151,7 @@ IRAM_ATTR void gap_callback_handler(esp_gap_ble_cb_event_t event,
ESP_LOGV(TAG, "RSSI : %d", p->scan_rst.rssi); ESP_LOGV(TAG, "RSSI : %d", p->scan_rst.rssi);
#endif #endif
#if (VENDORFILTER) #if (MACFILTER)
if ((p->scan_rst.ble_addr_type == BLE_ADDR_TYPE_RANDOM) || if ((p->scan_rst.ble_addr_type == BLE_ADDR_TYPE_RANDOM) ||
(p->scan_rst.ble_addr_type == BLE_ADDR_TYPE_RPA_RANDOM)) { (p->scan_rst.ble_addr_type == BLE_ADDR_TYPE_RPA_RANDOM)) {
#ifdef VERBOSE #ifdef VERBOSE
@ -176,7 +176,7 @@ 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);
#endif #endif
/* to be improved in vendorfilter if: /* to be improved in macfilter:
// you can search for elements in the payload using the // you can search for elements in the payload using the
// function esp_ble_resolve_adv_data() // function esp_ble_resolve_adv_data()
// //
@ -187,7 +187,7 @@ IRAM_ATTR void gap_callback_handler(esp_gap_ble_cb_event_t event,
ESP_BLE_AD_TYPE_NAME_CMPL, &len); ESP_BLE_AD_TYPE_NAME_CMPL, &len);
filter BLE devices using their advertisements to get filter alternative filter BLE devices using their advertisements to get filter alternative
to vendor OUI if vendorfiltering is on, we ... to vendor OUI if macfiltering is on, we ...
- want to count: mobile phones and tablets - want to count: mobile phones and tablets
- don't want to count: beacons, peripherals (earphones, headsets, - don't want to count: beacons, peripherals (earphones, headsets,
printers), cars and machines see printers), cars and machines see
@ -214,35 +214,53 @@ IRAM_ATTR void gap_callback_handler(esp_gap_ble_cb_event_t event,
} // switch } // switch
} // gap_callback_handler } // gap_callback_handler
esp_err_t register_ble_callback(void) { esp_err_t register_ble_callback(bool unregister = false) {
ESP_LOGI(TAG, "Register GAP callback");
// This function is called when gap event occurs, such as scan result. if (unregister) {
// register the scan callback function to the gap module
ESP_ERROR_CHECK(esp_ble_gap_register_callback(&gap_callback_handler));
static esp_ble_scan_params_t ble_scan_params = { ESP_LOGI(TAG, "Unregister GAP callback...");
.scan_type = BLE_SCAN_TYPE_PASSIVE, esp_ble_gap_stop_scanning();
.own_addr_type = BLE_ADDR_TYPE_RANDOM, esp_ble_gap_register_callback(NULL);
#if (VENDORFILTER) }
.scan_filter_policy = BLE_SCAN_FILTER_ALLOW_WLIST_PRA_DIR,
// ADV_IND, ADV_NONCONN_IND, ADV_SCAN_IND packets are used for broadcasting
// data in broadcast applications (e.g., Beacons), so we don't want them in
// vendorfilter mode
#else
.scan_filter_policy = BLE_SCAN_FILTER_ALLOW_ALL,
#endif
.scan_interval = else {
(uint16_t)(cfg.blescantime * 10 / 0.625), // Time = N * 0.625 msec
.scan_window = (uint16_t)(BLESCANWINDOW / 0.625) // Time = N * 0.625 msec
};
ESP_LOGI(TAG, "Set GAP scan parameters"); ESP_LOGI(TAG, "Register GAP callback...");
// This function is called to set scan parameters. // This function is called when gap event occurs, such as scan result.
ESP_ERROR_CHECK(esp_ble_gap_set_scan_params(&ble_scan_params)); // register the scan callback function to the gap module
esp_ble_gap_register_callback(&gap_callback_handler);
static esp_ble_scan_params_t ble_scan_params = {
.scan_type = BLE_SCAN_TYPE_PASSIVE,
.own_addr_type = BLE_ADDR_TYPE_RANDOM,
.scan_filter_policy = BLE_SCAN_FILTER_ALLOW_ALL,
/*
#if (MACFILTER)
.scan_filter_policy = BLE_SCAN_FILTER_ALLOW_WLIST_PRA_DIR,
// ADV_IND, ADV_NONCONN_IND, ADV_SCAN_IND packets are used
for broadcasting
// data in broadcast applications (e.g., Beacons), so we
don't want them in
// macfilter mode
#else
.scan_filter_policy = BLE_SCAN_FILTER_ALLOW_ALL,
#endif
*/
.scan_interval =
(uint16_t)(cfg.blescantime * 10 / 0.625), // Time = N * 0.625 msec
.scan_window =
(uint16_t)(BLESCANWINDOW / 0.625), // Time = N * 0.625 msec
.scan_duplicate = BLE_SCAN_DUPLICATE_ENABLE};
ESP_LOGI(TAG, "Set GAP scan parameters");
// This function is called to set scan parameters.
esp_ble_gap_set_scan_params(&ble_scan_params);
}
return ESP_OK; return ESP_OK;
@ -251,29 +269,34 @@ esp_err_t register_ble_callback(void) {
void start_BLEscan(void) { void start_BLEscan(void) {
#if (BLECOUNTER) #if (BLECOUNTER)
ESP_LOGI(TAG, "Initializing bluetooth scanner ..."); ESP_LOGI(TAG, "Initializing bluetooth scanner ...");
// Initialize BT controller to allocate task and other resource. // Initialize BT controller to allocate task and other resource.
ESP_ERROR_CHECK(esp_coex_preference_set(ESP_COEX_PREFER_BT)); if (btStart()) { // enable bt_controller
esp_coex_preference_set(ESP_COEX_PREFER_BT);
btStart(); esp_bluedroid_init();
ESP_ERROR_CHECK(esp_bluedroid_init()); esp_bluedroid_enable();
ESP_ERROR_CHECK(esp_bluedroid_enable()); // Register callback function for capturing bluetooth packets
register_ble_callback(false);
// Register callback function for capturing bluetooth packets ESP_LOGI(TAG, "Bluetooth scanner started");
ESP_ERROR_CHECK(register_ble_callback()); } else {
ESP_LOGE(TAG, "Bluetooth controller start failed. Resetting device");
ESP_LOGI(TAG, "Bluetooth scanner started"); do_reset(true);
}
#endif // BLECOUNTER #endif // BLECOUNTER
} // start_BLEscan } // start_BLEscan
void stop_BLEscan(void) { void stop_BLEscan(void) {
#if (BLECOUNTER) #if (BLECOUNTER)
ESP_LOGI(TAG, "Shutting down bluetooth scanner ..."); ESP_LOGI(TAG, "Shutting down bluetooth scanner ...");
ESP_ERROR_CHECK(esp_ble_gap_register_callback(NULL)); register_ble_callback(true); // unregister capture function
ESP_ERROR_CHECK(esp_bluedroid_disable()); ESP_LOGD(TAG, "bluedroid disable...");
ESP_ERROR_CHECK(esp_bluedroid_deinit()); esp_bluedroid_disable();
btStop(); // disable bt_controller ESP_LOGD(TAG, "bluedroid deinit...");
ESP_ERROR_CHECK(esp_coex_preference_set(ESP_COEX_PREFER_WIFI)); esp_bluedroid_deinit();
if (!btStop()) { // disable bt_controller
ESP_LOGE(TAG, "Bluetooth controller stop failed. Resetting device");
do_reset(true);
}
esp_coex_preference_set(ESP_COEX_PREFER_WIFI);
ESP_LOGI(TAG, "Bluetooth scanner stopped"); ESP_LOGI(TAG, "Bluetooth scanner stopped");
#endif // BLECOUNTER #endif // BLECOUNTER
} // stop_BLEscan } // stop_BLEscan

View File

@ -5,7 +5,7 @@
// Local logging tag // Local logging tag
static const char TAG[] = __FILE__; static const char TAG[] = __FILE__;
bmeStatus_t bme_status = {0}; bmeStatus_t bme_status = {0, 0, 0, 0, 0, 0, 0, 0};
Ticker bmecycler; Ticker bmecycler;
@ -26,7 +26,6 @@ bsec_virtual_sensor_t sensorList[10] = {
}; };
uint8_t bsecstate_buffer[BSEC_MAX_STATE_BLOB_SIZE] = {0}; uint8_t bsecstate_buffer[BSEC_MAX_STATE_BLOB_SIZE] = {0};
uint16_t stateUpdateCounter = 0;
Bsec iaqSensor; Bsec iaqSensor;
@ -47,10 +46,10 @@ Adafruit_BMP085 bmp; // I2C
void setBMEIRQ() { xTaskNotify(irqHandlerTask, BME_IRQ, eSetBits); } void setBMEIRQ() { xTaskNotify(irqHandlerTask, BME_IRQ, eSetBits); }
// initialize MEMS sensor // initialize MEMS sensor
// return = 0 -> error / return = 1 -> success
int bme_init(void) { int bme_init(void) {
// return = 0 -> error / return = 1 -> success int rc = 0;
int rc = 1;
#ifdef HAS_BME680 #ifdef HAS_BME680
// block i2c bus access // block i2c bus access
@ -64,76 +63,33 @@ int bme_init(void) {
iaqSensor.version.minor_bugfix); iaqSensor.version.minor_bugfix);
iaqSensor.setConfig(bsec_config_iaq); iaqSensor.setConfig(bsec_config_iaq);
if (checkIaqSensorStatus())
ESP_LOGI(TAG, "BME680 sensor found and initialized");
else {
ESP_LOGE(TAG, "BME680 sensor not found");
rc = 0;
goto finish;
}
loadState(); loadState();
iaqSensor.setTemperatureOffset((float)BME_TEMP_OFFSET); iaqSensor.setTemperatureOffset((float)BME_TEMP_OFFSET);
iaqSensor.updateSubscription(sensorList, 10, BSEC_SAMPLE_RATE_LP); iaqSensor.updateSubscription(sensorList, 10, BSEC_SAMPLE_RATE_LP);
if (checkIaqSensorStatus()) rc = checkIaqSensorStatus();
ESP_LOGI(TAG, "BSEC subscription succesful");
else { } else
ESP_LOGE(TAG, "BSEC subscription error");
rc = 0;
goto finish;
}
} else {
ESP_LOGE(TAG, "I2c bus busy - BME680 initialization error"); ESP_LOGE(TAG, "I2c bus busy - BME680 initialization error");
rc = 0;
goto finish;
}
#elif defined HAS_BME280 #elif defined HAS_BME280
bool status;
// block i2c bus access
if (I2C_MUTEX_LOCK()) { if (I2C_MUTEX_LOCK()) {
rc = bme.begin(BME280_ADDR);
status = bme.begin(BME280_ADDR); } else
if (!status) {
ESP_LOGE(TAG, "BME280 sensor not found");
rc = 0;
goto finish;
}
ESP_LOGI(TAG, "BME280 sensor found and initialized");
} else {
ESP_LOGE(TAG, "I2c bus busy - BME280 initialization error"); ESP_LOGE(TAG, "I2c bus busy - BME280 initialization error");
rc = 0;
goto finish;
}
#elif defined HAS_BMP180 #elif defined HAS_BMP180
bool status;
// block i2c bus access
if (I2C_MUTEX_LOCK()) { if (I2C_MUTEX_LOCK()) {
// Wire.begin(21, 22); // Wire.begin(21, 22);
status = bmp.begin(); rc = bmp.begin();
if (!status) { } else
ESP_LOGE(TAG, "BMP180 sensor not found");
rc = 0;
goto finish;
}
ESP_LOGI(TAG, "BMP180 sensor found and initialized");
} else {
ESP_LOGE(TAG, "I2c bus busy - BMP180 initialization error"); ESP_LOGE(TAG, "I2c bus busy - BMP180 initialization error");
rc = 0;
goto finish;
}
#endif #endif
finish:
I2C_MUTEX_UNLOCK(); // release i2c bus access I2C_MUTEX_UNLOCK(); // release i2c bus access
if (rc) if (rc)
bmecycler.attach(BMECYCLE, setBMEIRQ); bmecycler.attach(BMECYCLE, setBMEIRQ); // start cyclic data transmit
return rc; return rc;
} // bme_init() } // bme_init()
@ -218,6 +174,7 @@ void loadState(void) {
void updateState(void) { void updateState(void) {
bool update = false; bool update = false;
static uint16_t stateUpdateCounter = 0;
if (stateUpdateCounter == 0) { if (stateUpdateCounter == 0) {
// first state update when IAQ accuracy is >= 1 // first state update when IAQ accuracy is >= 1
@ -228,7 +185,7 @@ void updateState(void) {
} else { } else {
/* Update every STATE_SAVE_PERIOD minutes */ /* Update every STATE_SAVE_PERIOD minutes */
if ((stateUpdateCounter * STATE_SAVE_PERIOD) < _millis()) { if ((long)(millis() - stateUpdateCounter * STATE_SAVE_PERIOD) >= 0) {
update = true; update = true;
stateUpdateCounter++; stateUpdateCounter++;
} }

View File

@ -33,7 +33,7 @@ void button_init(int pin) {
b->setOnHolding([]() { b->setOnHolding([]() {
payload.reset(); payload.reset();
payload.addButton(0x01); payload.addButton(0x01);
SendPayload(BUTTONPORT, prio_normal); SendPayload(BUTTONPORT);
}); });
// attach interrupt to the button // attach interrupt to the button

View File

@ -42,19 +42,20 @@ static void defaultConfig(configData_t *myconfig) {
COUNTERMODE; // 0=cyclic, 1=cumulative, 2=cyclic confirmed COUNTERMODE; // 0=cyclic, 1=cumulative, 2=cyclic confirmed
myconfig->rssilimit = 0; // threshold for rssilimiter, negative value! myconfig->rssilimit = 0; // threshold for rssilimiter, negative value!
myconfig->sendcycle = SENDCYCLE; // payload send cycle [seconds/2] myconfig->sendcycle = SENDCYCLE; // payload send cycle [seconds/2]
myconfig->sleepcycle = SLEEPCYCLE; // sleep cycle [seconds/2]
myconfig->wifichancycle = myconfig->wifichancycle =
WIFI_CHANNEL_SWITCH_INTERVAL; // wifi channel switch cycle [seconds/100] WIFI_CHANNEL_SWITCH_INTERVAL; // wifi channel switch cycle [seconds/100]
myconfig->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)
myconfig->blescan = 1; // 0=disabled, 1=enabled myconfig->blescan = 1; // 0=disabled, 1=enabled
myconfig->wifiscan = 1; // 0=disabled, 1=enabled myconfig->wifiscan = 1; // 0=disabled, 1=enabled
myconfig->wifiant = 0; // 0=internal, 1=external (for LoPy/LoPy4) myconfig->wifiant = 0; // 0=internal, 1=external (for LoPy/LoPy4)
myconfig->vendorfilter = VENDORFILTER; // 0=disabled, 1=enabled myconfig->macfilter = MACFILTER; // 0=disabled, 1=enabled
myconfig->rgblum = RGBLUMINOSITY; // RGB Led luminosity (0..100%) myconfig->rgblum = RGBLUMINOSITY; // RGB Led luminosity (0..100%)
myconfig->monitormode = 0; // 0=disabled, 1=enabled myconfig->monitormode = 0; // 0=disabled, 1=enabled
myconfig->payloadmask = PAYLOADMASK; // payloads as defined in default myconfig->payloadmask = PAYLOADMASK; // payloads as defined in default
myconfig->enscount = COUNT_ENS; // 0=disabled, 1=enabled myconfig->enscount = COUNT_ENS; // 0=disabled, 1=enabled
#ifdef HAS_BME680 #ifdef HAS_BME680
// initial BSEC state for BME680 sensor // initial BSEC state for BME680 sensor

View File

@ -5,6 +5,10 @@
// (c) by Kaspar Metz // (c) by Kaspar Metz
// modified for use in the Paxcounter by AQ // modified for use in the Paxcounter by AQ
#if (COUNT_ENS) && !(BLECOUNTER)
#warning ENS-Counter needs Bluetooth, but Bluetooth compile option is disabled
#endif
#if (COUNT_ENS) #if (COUNT_ENS)
// Local logging tag // Local logging tag
@ -44,7 +48,7 @@ bool cwa_init(void) {
} }
void cwa_mac_add(uint16_t hashedmac) { void cwa_mac_add(uint16_t hashedmac) {
cwaSeenNotifiers[hashedmac] = _millis(); // hash last seen at .... cwaSeenNotifiers[hashedmac] = millis(); // hash last seen at ....
} }
#endif #endif

View File

@ -14,24 +14,15 @@ extern boolean isSDS011Active;
#endif #endif
void setCyclicIRQ() { void setCyclicIRQ() {
xTaskNotifyFromISR(irqHandlerTask, CYCLIC_IRQ, eSetBits, NULL); xTaskNotify(irqHandlerTask, CYCLIC_IRQ, eSetBits);
} }
// do all housekeeping // do all housekeeping
void doHousekeeping() { void doHousekeeping() {
// update uptime counter // check if update mode trigger switch was set by rcommand
uptime(); if (RTC_runmode == RUNMODE_UPDATE)
// check if update mode trigger switch was set do_reset(true);
if (RTC_runmode == RUNMODE_UPDATE) {
// check battery status if we can before doing ota
if (batt_sufficient()) {
do_reset(true); // warmstart to runmode update
} else {
ESP_LOGE(TAG, "Battery level %d%% is too low for OTA", batt_level);
RTC_runmode = RUNMODE_NORMAL; // keep running in normal mode
}
}
// heap and task storage debugging // heap and task storage debugging
ESP_LOGD(TAG, "Heap: Free:%d, Min:%d, Size:%d, Alloc:%d, StackHWM:%d", ESP_LOGD(TAG, "Heap: Free:%d, Min:%d, Size:%d, Alloc:%d, StackHWM:%d",
@ -43,6 +34,8 @@ void doHousekeeping() {
ESP_LOGD(TAG, "MACprocessor %d bytes left | Taskstate = %d", ESP_LOGD(TAG, "MACprocessor %d bytes left | Taskstate = %d",
uxTaskGetStackHighWaterMark(macProcessTask), uxTaskGetStackHighWaterMark(macProcessTask),
eTaskGetState(macProcessTask)); eTaskGetState(macProcessTask));
ESP_LOGD(TAG, "Rcommand interpreter %d bytes left | Taskstate = %d",
uxTaskGetStackHighWaterMark(rcmdTask), eTaskGetState(rcmdTask));
#if (HAS_LORA) #if (HAS_LORA)
ESP_LOGD(TAG, "LMiCtask %d bytes left | Taskstate = %d", ESP_LOGD(TAG, "LMiCtask %d bytes left | Taskstate = %d",
uxTaskGetStackHighWaterMark(lmicTask), eTaskGetState(lmicTask)); uxTaskGetStackHighWaterMark(lmicTask), eTaskGetState(lmicTask));
@ -106,7 +99,6 @@ void doHousekeeping() {
"free heap = %d bytes)", "free heap = %d bytes)",
ESP.getMinFreeHeap(), ESP.getFreeHeap()); ESP.getMinFreeHeap(), ESP.getFreeHeap());
reset_counters(); // clear macs container and reset all counters reset_counters(); // clear macs container and reset all counters
get_salt(); // get new salt for salting hashes
if (ESP.getMinFreeHeap() <= MEM_LOW) // check again if (ESP.getMinFreeHeap() <= MEM_LOW) // check again
do_reset(true); // memory leak, reset device do_reset(true); // memory leak, reset device
@ -117,7 +109,6 @@ void doHousekeeping() {
if (ESP.getMinFreePsram() <= MEM_LOW) { if (ESP.getMinFreePsram() <= MEM_LOW) {
ESP_LOGI(TAG, "PSRAM full, counter cleared"); ESP_LOGI(TAG, "PSRAM full, counter cleared");
reset_counters(); // clear macs container and reset all counters reset_counters(); // clear macs container and reset all counters
get_salt(); // get new salt for salting hashes
if (ESP.getMinFreePsram() <= MEM_LOW) // check again if (ESP.getMinFreePsram() <= MEM_LOW) // check again
do_reset(true); // memory leak, reset device do_reset(true); // memory leak, reset device
@ -136,8 +127,6 @@ void doHousekeeping() {
} // doHousekeeping() } // doHousekeeping()
uint64_t uptime() { return _millis(); }
uint32_t getFreeRAM() { uint32_t getFreeRAM() {
#ifndef BOARD_HAS_PSRAM #ifndef BOARD_HAS_PSRAM
return ESP.getFreeHeap(); return ESP.getFreeHeap();
@ -151,6 +140,7 @@ void reset_counters() {
macs.clear(); // clear all macs container macs.clear(); // clear all macs container
macs_wifi = 0; macs_wifi = 0;
macs_ble = 0; macs_ble = 0;
renew_salt(); // get new salt
#ifdef HAS_DISPLAY #ifdef HAS_DISPLAY
dp_plotCurve(0, true); dp_plotCurve(0, true);
#endif #endif

View File

@ -400,7 +400,7 @@ void dp_drawPage(time_t t, bool nextpage) {
gps.location.lat()); gps.location.lat());
// line 6-7: GPS longitude // line 6-7: GPS longitude
dp_printf("%c%07.4f", gps.location.rawLat().negative ? 'W' : 'E', dp_printf("%c%07.4f", gps.location.rawLng().negative ? 'W' : 'E',
gps.location.lng()); gps.location.lng());
} else { } else {
@ -603,7 +603,6 @@ void dp_shutdown(void) {
if (!I2C_MUTEX_LOCK()) if (!I2C_MUTEX_LOCK())
ESP_LOGV(TAG, "[%0.3f] i2c mutex lock failed", _seconds()); ESP_LOGV(TAG, "[%0.3f] i2c mutex lock failed", _seconds());
else { else {
cfg.screenon = 0;
obdPower(&ssoled, false); obdPower(&ssoled, false);
delay(DISPLAYREFRESH_MS / 1000 * 1.1); delay(DISPLAYREFRESH_MS / 1000 * 1.1);
I2C_MUTEX_UNLOCK(); // release i2c bus access I2C_MUTEX_UNLOCK(); // release i2c bus access

View File

@ -30,34 +30,27 @@ static uint16_t nmea_txDelay_ms = 0;
// initialize and configure GPS // initialize and configure GPS
int gps_init(void) { int gps_init(void) {
int ret = 1;
if (!gps_config()) { if (!gps_config()) {
ESP_LOGE(TAG, "GPS chip initializiation error"); ESP_LOGE(TAG, "GPS chip initializiation error");
return 0; return 0;
} }
#ifdef GPS_SERIAL #ifdef GPS_SERIAL
ESP_LOGI(TAG, "Opening serial GPS");
GPS_Serial.begin(GPS_SERIAL); GPS_Serial.begin(GPS_SERIAL);
ESP_LOGI(TAG, "Using serial GPS");
#elif defined GPS_I2C #elif defined GPS_I2C
ESP_LOGI(TAG, "Opening I2C GPS");
Wire.begin(GPS_I2C, 400000); // I2C connect to GPS device with 400 KHz Wire.begin(GPS_I2C, 400000); // I2C connect to GPS device with 400 KHz
Wire.beginTransmission(GPS_ADDR); Wire.beginTransmission(GPS_ADDR);
Wire.write(0x00); // dummy write Wire.write(0x00); // dummy write
ret = Wire.endTransmission(); // check if chip is seen on i2c bus if (Wire.endTransmission()) {
ESP_LOGE(TAG, "Quectel L76 GPS chip not found");
if (ret) { return 0;
ESP_LOGE(TAG, } else
"Quectel L76 GPS chip not found on i2c bus, bus error %d. "
"Stopping GPS-Task.",
ret);
ret = 0;
} else {
ESP_LOGI(TAG, "Quectel L76 GPS chip found"); ESP_LOGI(TAG, "Quectel L76 GPS chip found");
}
#endif #endif
return ret; return 1;
} // gps_init() } // gps_init()
// detect gps chipset type and configure it with device specific settings // detect gps chipset type and configure it with device specific settings
@ -109,7 +102,7 @@ time_t get_gpstime(uint16_t *msec) {
#endif #endif
// did we get a current date & time? // did we get a current date & time?
if (gpstime.isValid() && gpsday.isValid()) { if (gpstime.isValid()) {
time_t t = 0; time_t t = 0;
tmElements_t tm; tmElements_t tm;
@ -188,4 +181,4 @@ void gps_loop(void *pvParameters) {
} // gps_loop() } // gps_loop()
#endif // HAS_GPS #endif // HAS_GPS

View File

@ -10,7 +10,6 @@
// Hardware related definitions for Pycom LoPy4 Board // Hardware related definitions for Pycom LoPy4 Board
#define HAS_LORA 1 // comment out if device shall not send data via LoRa #define HAS_LORA 1 // comment out if device shall not send data via LoRa
#define LORA_RST LMIC_UNUSED_PIN // reset pin of lora chip is not wired von LoPy4
//#defin HAS_SPI 1 // comment out if device shall not send data via SPI //#defin HAS_SPI 1 // comment out if device shall not send data via SPI
// pin definitions for local wired SPI slave interface // pin definitions for local wired SPI slave interface

View File

@ -8,15 +8,15 @@
#include <stdint.h> #include <stdint.h>
// 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 2 // this board has a SDMMC card-reader/writer //#define HAS_SDCARD 2 // this board has a SDMMC card-reader/writer
// enable only if you want to send paxcount via ethernet port to mqtt server // enable only if you want to send paxcount via ethernet port to mqtt server
#define HAS_MQTT 1 // use MQTT on ethernet interface #define HAS_MQTT 1 // use MQTT on ethernet interface
//#define BAT_MEASURE_ADC ADC1_GPIO35_CHANNEL // battery probe GPIO pin -> ADC1_CHANNEL_7 //#define BAT_MEASURE_ADC ADC1_GPIO35_CHANNEL // battery measurement
//#define BAT_VOLTAGE_DIVIDER 2 // voltage divider 470k/470k on board //#define BAT_VOLTAGE_DIVIDER 2 // voltage divider 470k/470k on board
#define BAT_MEASURE_ADC ADC1_GPIO39_CHANNEL // external power probe GPIO pin #define BAT_MEASURE_ADC ADC1_GPIO39_CHANNEL // external power sense
#define BAT_VOLTAGE_DIVIDER 2.1277f // voltage divider 47k/442k on board #define BAT_VOLTAGE_DIVIDER 2.1277f // voltage divider 47k/100k on board
#define HAS_BUTTON KEY_BUILTIN // on board button #define HAS_BUTTON KEY_BUILTIN // on board button
#define HAS_LED NOT_A_PIN // no on board LED #define HAS_LED NOT_A_PIN // no on board LED

View File

@ -13,10 +13,10 @@ for T-Beam version T22_V10 + T22_V11
pinouts taken from https://github.com/lewisxhe/TTGO-T-Beam pinouts taken from https://github.com/lewisxhe/TTGO-T-Beam
/// Button functions: /// /// Button functions: ///
Power, short press -> set device on (toggles display while device is on) Power, short press -> set device on / while device is on: goto sleep
Power, long press -> set device off Power, long press -> set device off
User, short press -> flip display page User, short press -> flip display page
User, long press -> send LORA message User, long press -> send a button message
Reset -> reset device Reset -> reset device
*/ */
@ -29,7 +29,8 @@ Reset -> reset device
#define HAS_LORA 1 // comment out if device shall not send data via LoRa #define HAS_LORA 1 // comment out if device shall not send data via LoRa
#define CFG_sx1276_radio 1 // HPD13A LoRa SoC #define CFG_sx1276_radio 1 // HPD13A LoRa SoC
#define HAS_BUTTON GPIO_NUM_38 // middle on board button #define HAS_BUTTON GPIO_NUM_38 // middle on board button
#define HAS_LED NOT_A_PIN #define HAS_LED GPIO_NUM_4 // not present on all T-Beam 1.0 boards
#define LED_ACTIVE_LOW 1
// power management settings // power management settings
#define HAS_PMU 1 // has AXP192 chip #define HAS_PMU 1 // has AXP192 chip
@ -41,6 +42,12 @@ Reset -> reset device
// possible values (V): // possible values (V):
// 4_1/4_15/4_2/4_36 // 4_1/4_15/4_2/4_36
// blue onboard led settings
// possible values:
// AXP20X_LED_OFF / AXP20X_LED_LOW_LEVEL (means LED ON) / AXP20X_LED_BLINK_1HZ / AXP20X_LED_BLINK_4HZ
#define PMU_LED_RUN_MODE AXP20X_LED_LOW_LEVEL
#define PMU_LED_SLEEP_MODE AXP20X_LED_OFF
// GPS settings // GPS settings
#define HAS_GPS 1 // use on board GPS #define HAS_GPS 1 // use on board GPS
#define GPS_SERIAL 9600, SERIAL_8N1, GPIO_NUM_34, GPIO_NUM_12 // UBlox NEO 6M #define GPS_SERIAL 9600, SERIAL_8N1, GPIO_NUM_34, GPIO_NUM_12 // UBlox NEO 6M

View File

@ -36,52 +36,10 @@
#include "hash.h" #include "hash.h"
uint32_t IRAM_ATTR rokkit(const char *data, int len) { #ifdef ROKKIT_ENABLE_8BIT_OPTIMIZATIONS
uint32_t hash, tmp; #undef ROKKIT_ENABLE_8BIT_OPTIMIZATIONS
int rem; #endif
if (len <= 0 || data == 0) uint32_t IRAM_ATTR hash(const char *data, int len) {
return 0; return rokkit(data, len);
hash = len;
rem = len & 3;
len >>= 2;
/* Main loop */
while (len > 0) {
hash += *((uint16_t *)data);
tmp = (*((uint16_t *)(data + 2)) << 11) ^ hash;
hash = (hash << 16) ^ tmp;
data += 2 * 2;
hash += hash >> 11;
len--;
}
/* Handle end cases */
switch (rem) {
case 3:
hash += *((uint16_t *)data);
hash ^= hash << 16;
hash ^= ((signed char)data[2]) << 18;
hash += hash >> 11;
break;
case 2:
hash += *((uint16_t *)data);
hash ^= hash << 11;
hash += hash >> 17;
break;
case 1:
hash += (signed char)*data;
hash ^= hash << 10;
hash += hash >> 1;
}
/* Force "avalanching" of final 127 bits */
hash ^= hash << 3;
hash += hash >> 5;
hash ^= hash << 4;
hash += hash >> 17;
hash ^= hash << 25;
hash += hash >> 6;
return hash;
} }

View File

@ -7,12 +7,7 @@ static const char TAG[] = __FILE__;
void i2c_init(void) { Wire.begin(MY_DISPLAY_SDA, MY_DISPLAY_SCL, 100000); } void i2c_init(void) { Wire.begin(MY_DISPLAY_SDA, MY_DISPLAY_SCL, 100000); }
void i2c_deinit(void) { void i2c_deinit(void) { Wire.~TwoWire(); }
Wire.~TwoWire(); // shutdown/power off I2C hardware
// configure pins as input to save power, because Wire.end() enables pullups
pinMode(MY_DISPLAY_SDA, INPUT);
pinMode(MY_DISPLAY_SCL, INPUT);
}
void i2c_scan(void) { void i2c_scan(void) {
@ -38,10 +33,13 @@ void i2c_scan(void) {
BBI2C bbi2c; BBI2C bbi2c;
const char *szNames[] = {"Unknown","SSD1306","SH1106","VL53L0X","BMP180", "BMP280","BME280", const char *szNames[] = {
"MPU-60x0", "MPU-9250", "MCP9808","LSM6DS3", "ADXL345", "ADS1115","MAX44009", "Unknown", "SSD1306", "SH1106", "VL53L0X", "BMP180", "BMP280",
"MAG3110", "CCS811", "HTS221", "LPS25H", "LSM9DS1","LM8330", "DS3231", "LIS3DH", "BME280", "MPU-60x0", "MPU-9250", "MCP9808", "LSM6DS3", "ADXL345",
"LIS3DSH","INA219","SHT3X","HDC1080","MPU6886","BME680", "AXP202", "AXP192", "24AA02XEXX", "DS1307"}; "ADS1115", "MAX44009", "MAG3110", "CCS811", "HTS221", "LPS25H",
"LSM9DS1", "LM8330", "DS3231", "LIS3DH", "LIS3DSH", "INA219",
"SHT3X", "HDC1080", "MPU6886", "BME680", "AXP202", "AXP192",
"24AA02XEXX", "DS1307"};
ESP_LOGI(TAG, "Starting I2C bus scan..."); ESP_LOGI(TAG, "Starting I2C bus scan...");

View File

@ -69,14 +69,6 @@ void irqHandler(void *pvParameters) {
} }
#endif #endif
// MQTT loop due?
#if (HAS_MQTT)
if (InterruptStatus & MQTT_IRQ) {
mqtt_loop();
InterruptStatus &= ~MQTT_IRQ;
}
#endif
// are cyclic tasks due? // are cyclic tasks due?
if (InterruptStatus & CYCLIC_IRQ) { if (InterruptStatus & CYCLIC_IRQ) {
doHousekeeping(); doHousekeeping();
@ -95,6 +87,13 @@ void irqHandler(void *pvParameters) {
if (InterruptStatus & SENDCYCLE_IRQ) { if (InterruptStatus & SENDCYCLE_IRQ) {
sendData(); sendData();
InterruptStatus &= ~SENDCYCLE_IRQ; InterruptStatus &= ~SENDCYCLE_IRQ;
// goto sleep if we have a sleep cycle
if (cfg.sleepcycle)
#ifdef HAS_BUTTON
enter_deepsleep(cfg.sleepcycle * 2, (gpio_num_t)HAS_BUTTON);
#else
enter_deepsleep(cfg.sleepcycle * 2);
#endif
} }
} // for } // for
} // irqHandler() } // irqHandler()

View File

@ -9,7 +9,7 @@ led_states previousLEDState =
TaskHandle_t ledLoopTask; TaskHandle_t ledLoopTask;
uint16_t LEDColor = COLOR_NONE, LEDBlinkDuration = 0; // state machine variables uint16_t LEDColor = COLOR_NONE, LEDBlinkDuration = 0; // state machine variables
unsigned long LEDBlinkStarted = 0; // When (in _millis() led blink started) unsigned long LEDBlinkStarted = 0; // When (in millis() led blink started)
#ifdef HAS_RGB_LED #ifdef HAS_RGB_LED
@ -133,7 +133,7 @@ void blink_LED(uint16_t set_color, uint16_t set_blinkduration) {
#if (HAS_LED != NOT_A_PIN) || defined(HAS_RGB_LED) #if (HAS_LED != NOT_A_PIN) || defined(HAS_RGB_LED)
LEDColor = set_color; // set color for RGB LED LEDColor = set_color; // set color for RGB LED
LEDBlinkDuration = set_blinkduration; // duration LEDBlinkDuration = set_blinkduration; // duration
LEDBlinkStarted = _millis(); // Time Start here LEDBlinkStarted = millis(); // Time Start here
LEDState = LED_ON; // Let main set LED on LEDState = LED_ON; // Let main set LED on
#endif #endif
} }
@ -145,8 +145,8 @@ void ledLoop(void *parameter) {
// Custom blink running always have priority other LoRaWAN led // Custom blink running always have priority other LoRaWAN led
// management // management
if (LEDBlinkStarted && LEDBlinkDuration) { if (LEDBlinkStarted && LEDBlinkDuration) {
// Custom blink is finished, let this order, avoid _millis() overflow // Custom blink is finished, let this order, avoid millis() overflow
if ((_millis() - LEDBlinkStarted) >= LEDBlinkDuration) { if ((long)(millis() - LEDBlinkStarted) >= LEDBlinkDuration) {
// Led becomes off, and stop blink // Led becomes off, and stop blink
LEDState = LED_OFF; LEDState = LED_OFF;
LEDBlinkStarted = 0; LEDBlinkStarted = 0;
@ -165,7 +165,7 @@ void ledLoop(void *parameter) {
LEDColor = COLOR_YELLOW; LEDColor = COLOR_YELLOW;
// quick blink 20ms on each 1/5 second // quick blink 20ms on each 1/5 second
LEDState = LEDState =
((_millis() % 200) < 20) ? LED_ON : LED_OFF; // TX data pending ((millis() % 200) < 20) ? LED_ON : LED_OFF; // TX data pending
} else if (LMIC.opmode & (OP_TXDATA | OP_TXRXPEND)) { } else if (LMIC.opmode & (OP_TXDATA | OP_TXRXPEND)) {
// select color to blink by message port // select color to blink by message port
switch (LMIC.pendTxPort) { switch (LMIC.pendTxPort) {
@ -180,13 +180,13 @@ void ledLoop(void *parameter) {
break; break;
} }
// small blink 10ms on each 1/2sec (not when joining) // small blink 10ms on each 1/2sec (not when joining)
LEDState = ((_millis() % 500) < 10) ? LED_ON : LED_OFF; LEDState = ((millis() % 500) < 10) ? LED_ON : LED_OFF;
// This should not happen so indicate a problem // This should not happen so indicate a problem
} else if (LMIC.opmode & } else if (LMIC.opmode &
((OP_TXDATA | OP_TXRXPEND | OP_JOINING | OP_REJOIN) == 0)) { ((OP_TXDATA | OP_TXRXPEND | OP_JOINING | OP_REJOIN) == 0)) {
LEDColor = COLOR_RED; LEDColor = COLOR_RED;
// heartbeat long blink 200ms on each 2 seconds // heartbeat long blink 200ms on each 2 seconds
LEDState = ((_millis() % 2000) < 200) ? LED_ON : LED_OFF; LEDState = ((millis() % 2000) < 200) ? LED_ON : LED_OFF;
} else } else
#endif // HAS_LORA #endif // HAS_LORA
{ {

View File

@ -18,9 +18,10 @@
// use interrupts only if LORA_IRQ and LORA_DIO are connected to interrupt // use interrupts only if LORA_IRQ and LORA_DIO are connected to interrupt
// capable and separate GPIO pins on your board, if not don't enable // capable and separate GPIO pins on your board, if not don't enable
#if (LORA_IRQ) != (LORA_IO1) // note: this feature can't be used on ESP32 unless PR #556 of MCCI LMIC was merged
#define LMIC_USE_INTERRUPTS 1 //#if (LORA_IRQ) != (LORA_IO1)
#endif //#define LMIC_USE_INTERRUPTS 1
//#endif
// avoid lmic warning if we don't configure radio in case we haven't one // avoid lmic warning if we don't configure radio in case we haven't one
#if !(defined(CFG_sx1272_radio) || defined(CFG_sx1276_radio)) #if !(defined(CFG_sx1272_radio) || defined(CFG_sx1276_radio))

View File

@ -6,6 +6,9 @@
// 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
@ -16,12 +19,7 @@ static const char TAG[] = "lora";
#endif #endif
#endif #endif
// variable keep its values after restart or wakeup from sleep static QueueHandle_t LoraSendQueue;
RTC_NOINIT_ATTR u4_t RTCnetid, RTCdevaddr;
RTC_NOINIT_ATTR u1_t RTCnwkKey[16], RTCartKey[16];
RTC_NOINIT_ATTR int RTCseqnoUp, RTCseqnoDn;
QueueHandle_t LoraSendQueue;
TaskHandle_t lmicTask = NULL, lorasendTask = NULL; TaskHandle_t lmicTask = NULL, lorasendTask = NULL;
class MyHalConfig_t : public Arduino_LMIC::HalConfiguration_t { class MyHalConfig_t : public Arduino_LMIC::HalConfiguration_t {
@ -83,8 +81,6 @@ void lora_setupForNetwork(bool preJoin) {
getSfName(updr2rps(LMIC.datarate)), getSfName(updr2rps(LMIC.datarate)),
getBwName(updr2rps(LMIC.datarate)), getBwName(updr2rps(LMIC.datarate)),
getCrName(updr2rps(LMIC.datarate))); getCrName(updr2rps(LMIC.datarate)));
// store LMIC keys and counters in RTC memory
LMIC_getSessionKeys(&RTCnetid, &RTCdevaddr, RTCnwkKey, RTCartKey);
} }
} }
@ -110,7 +106,7 @@ breaking change
// DevEUI generator using devices's MAC address // DevEUI generator using devices's MAC address
void gen_lora_deveui(uint8_t *pdeveui) { void gen_lora_deveui(uint8_t *pdeveui) {
uint8_t *p = pdeveui, dmac[6]; uint8_t *p = pdeveui, dmac[6];
ESP_ERROR_CHECK(esp_efuse_mac_get_default(dmac)); esp_efuse_mac_get_default(dmac);
// deveui is LSB, we reverse it so TTN DEVEUI display // deveui is LSB, we reverse it so TTN DEVEUI display
// will remain the same as MAC address // will remain the same as MAC address
// MAC is 6 bytes, devEUI 8, set middle 2 ones // MAC is 6 bytes, devEUI 8, set middle 2 ones
@ -197,59 +193,47 @@ void lora_send(void *pvParameters) {
} }
// fetch next or wait for payload to send from queue // fetch next or wait for payload to send from queue
if (xQueueReceive(LoraSendQueue, &SendBuffer, portMAX_DELAY) != pdTRUE) { // do not delete item from queue until it is transmitted
if (xQueuePeek(LoraSendQueue, &SendBuffer, portMAX_DELAY) != pdTRUE) {
ESP_LOGE(TAG, "Premature return from xQueueReceive() with no data!"); ESP_LOGE(TAG, "Premature return from xQueueReceive() with no data!");
continue; continue;
} }
// attempt to transmit payload // attempt to transmit payload
else { switch (LMIC_setTxData2_strict(SendBuffer.MessagePort, SendBuffer.Message,
SendBuffer.MessageSize,
switch (LMIC_setTxData2_strict(SendBuffer.MessagePort, SendBuffer.Message, (cfg.countermode & 0x02))) {
SendBuffer.MessageSize,
(cfg.countermode & 0x02))) {
case LMIC_ERROR_SUCCESS:
// save current Fcnt to RTC RAM
RTCseqnoUp = LMIC.seqnoUp;
RTCseqnoDn = LMIC.seqnoDn;
case LMIC_ERROR_SUCCESS:
#if (TIME_SYNC_LORASERVER) #if (TIME_SYNC_LORASERVER)
// if last packet sent was a timesync request, store TX timestamp // if last packet sent was a timesync request, store TX timestamp
if (SendBuffer.MessagePort == TIMEPORT) if (SendBuffer.MessagePort == TIMEPORT)
// store LMIC time when we started transmit of timesync request // store LMIC time when we started transmit of timesync request
timesync_store(osticks2ms(os_getTime()), timesync_tx); timesync_store(osticks2ms(os_getTime()), timesync_tx);
#endif #endif
ESP_LOGI(TAG, "%d byte(s) sent to LORA", SendBuffer.MessageSize);
// delete sent item from queue
xQueueReceive(LoraSendQueue, &SendBuffer, (TickType_t)0);
break;
case LMIC_ERROR_TX_BUSY: // LMIC already has a tx message pending
case LMIC_ERROR_TX_FAILED: // message was not sent
vTaskDelay(pdMS_TO_TICKS(500 + random(400))); // wait a while
break;
case LMIC_ERROR_TX_TOO_LARGE: // message size exceeds LMIC buffer size
case LMIC_ERROR_TX_NOT_FEASIBLE: // message too large for current
// datarate
ESP_LOGI(TAG, "Message too large to send, message not sent and deleted");
// we need some kind of error handling here -> to be done
break;
default: // other LMIC return code
ESP_LOGE(TAG, "LMIC error, message not sent and deleted");
ESP_LOGI(TAG, "%d byte(s) sent to LORA", SendBuffer.MessageSize); } // switch
break;
case LMIC_ERROR_TX_BUSY: // LMIC already has a tx message pending
case LMIC_ERROR_TX_FAILED: // message was not sent
// ESP_LOGD(TAG, "LMIC busy, message re-enqueued"); // very noisy
vTaskDelay(pdMS_TO_TICKS(1000 + random(500))); // wait a while
lora_enqueuedata(&SendBuffer); // re-enqueue the undelivered message
break;
case LMIC_ERROR_TX_TOO_LARGE: // message size exceeds LMIC buffer size
case LMIC_ERROR_TX_NOT_FEASIBLE: // message too large for current
// datarate
ESP_LOGI(TAG,
"Message too large to send, message not sent and deleted");
// we need some kind of error handling here -> to be done
break;
default: // other LMIC return code
ESP_LOGE(TAG, "LMIC error, message not sent and deleted");
} // switch
}
delay(2); // yield to CPU delay(2); // yield to CPU
} } // while(1)
} }
void lora_stack_reset() { esp_err_t lmic_init(void) {
LMIC_reset(); // reset LMIC MAC
}
esp_err_t lora_stack_init(bool do_join) {
_ASSERT(SEND_QUEUE_SIZE > 0); _ASSERT(SEND_QUEUE_SIZE > 0);
LoraSendQueue = xQueueCreate(SEND_QUEUE_SIZE, sizeof(MessageBuffer_t)); LoraSendQueue = xQueueCreate(SEND_QUEUE_SIZE, sizeof(MessageBuffer_t));
if (LoraSendQueue == 0) { if (LoraSendQueue == 0) {
@ -259,89 +243,6 @@ esp_err_t lora_stack_init(bool do_join) {
ESP_LOGI(TAG, "LORA send queue created, size %d Bytes", ESP_LOGI(TAG, "LORA send queue created, size %d Bytes",
SEND_QUEUE_SIZE * sizeof(MessageBuffer_t)); SEND_QUEUE_SIZE * sizeof(MessageBuffer_t));
// start lorawan stack
ESP_LOGI(TAG, "Starting LMIC...");
xTaskCreatePinnedToCore(lmictask, // task function
"lmictask", // name of task
4096, // stack size of task
(void *)1, // parameter of the task
2, // priority of the task
&lmicTask, // task handle
1); // CPU core
#ifdef LORA_ABP
// Pass ABP parameters to LMIC_setSession
lora_stack_reset();
uint8_t appskey[sizeof(APPSKEY)];
uint8_t nwkskey[sizeof(NWKSKEY)];
memcpy_P(appskey, APPSKEY, sizeof(APPSKEY));
memcpy_P(nwkskey, NWKSKEY, sizeof(NWKSKEY));
LMIC_setSession(NETID, DEVADDR, nwkskey, appskey);
// These parameters are defined as macro in loraconf.h
setABPParameters();
#else
// Start join procedure if not already joined,
// lora_setupForNetwork(true) is called by eventhandler when joined
// else continue current session
if (do_join) {
if (!LMIC_startJoining())
ESP_LOGI(TAG, "Already joined");
} else {
lora_stack_reset();
LMIC_setSession(RTCnetid, RTCdevaddr, RTCnwkKey, RTCartKey);
LMIC.seqnoUp = RTCseqnoUp;
LMIC.seqnoDn = RTCseqnoDn;
}
#endif
// start lmic send task
xTaskCreatePinnedToCore(lora_send, // task function
"lorasendtask", // name of task
3072, // stack size of task
(void *)1, // parameter of the task
1, // priority of the task
&lorasendTask, // task handle
1); // CPU core
return ESP_OK;
}
void lora_enqueuedata(MessageBuffer_t *message) {
// enqueue message in LORA send queue
BaseType_t ret = pdFALSE;
MessageBuffer_t DummyBuffer;
sendprio_t prio = message->MessagePrio;
switch (prio) {
case prio_high:
// clear some space in queue if full, then fallthrough to prio_normal
if (uxQueueSpacesAvailable(LoraSendQueue) == 0) {
xQueueReceive(LoraSendQueue, &DummyBuffer, (TickType_t)0);
ESP_LOGW(TAG, "LORA sendqueue purged, data is lost");
}
case prio_normal:
ret = xQueueSendToFront(LoraSendQueue, (void *)message, (TickType_t)0);
break;
case prio_low:
default:
ret = xQueueSendToBack(LoraSendQueue, (void *)message, (TickType_t)0);
break;
}
if (ret != pdTRUE) {
snprintf(lmic_event_msg + 14, LMIC_EVENTMSG_LEN - 14, "<>");
ESP_LOGW(TAG, "LORA sendqueue is full");
} else {
// add Lora send queue length to display
snprintf(lmic_event_msg + 14, LMIC_EVENTMSG_LEN - 14, "%2u",
uxQueueMessagesWaiting(LoraSendQueue));
}
}
void lora_queuereset(void) { xQueueReset(LoraSendQueue); }
// LMIC lorawan stack task
void lmictask(void *pvParameters) {
_ASSERT((uint32_t)pvParameters == 1);
// setup LMIC stack // setup LMIC stack
os_init_ex(&myPinmap); // initialize lmic run-time environment os_init_ex(&myPinmap); // initialize lmic run-time environment
@ -355,7 +256,7 @@ void lmictask(void *pvParameters) {
// Reset the MAC state. Session and pending data transfers will be // Reset the MAC state. Session and pending data transfers will be
// discarded. // discarded.
lora_stack_reset(); LMIC_reset();
// This tells LMIC to make the receive windows bigger, in case your clock is // This tells LMIC to make the receive windows bigger, in case your clock is
// faster or slower. This causes the transceiver to be earlier switched on, // faster or slower. This causes the transceiver to be earlier switched on,
@ -365,11 +266,79 @@ void lmictask(void *pvParameters) {
LMIC_setClockError(CLOCK_ERROR_PROCENTAGE * MAX_CLOCK_ERROR / 1000); LMIC_setClockError(CLOCK_ERROR_PROCENTAGE * MAX_CLOCK_ERROR / 1000);
#endif #endif
// Pass ABP parameters to LMIC_setSession
#ifdef LORA_ABP
setABPParameters(); // These parameters are defined as macro in loraconf.h
// load saved session from RTC, if we have one
if (RTC_runmode == RUNMODE_WAKEUP) {
LoadLMICFromRTC();
} else {
uint8_t appskey[sizeof(APPSKEY)];
uint8_t nwkskey[sizeof(NWKSKEY)];
memcpy_P(appskey, APPSKEY, sizeof(APPSKEY));
memcpy_P(nwkskey, NWKSKEY, sizeof(NWKSKEY));
LMIC_setSession(NETID, DEVADDR, nwkskey, appskey);
}
// Pass OTA parameters to LMIC_setSession
#else
// load saved session from RTC, if we have one
if (RTC_runmode == RUNMODE_WAKEUP)
LoadLMICFromRTC();
if (!LMIC_startJoining())
ESP_LOGI(TAG, "Already joined");
#endif
// start lmic loop task
ESP_LOGI(TAG, "Starting LMIC...");
xTaskCreatePinnedToCore(lmictask, // task function
"lmictask", // name of task
4096, // stack size of task
(void *)1, // parameter of the task
2, // priority of the task
&lmicTask, // task handle
1); // CPU core
// start lora send task
xTaskCreatePinnedToCore(lora_send, // task function
"lorasendtask", // name of task
3072, // stack size of task
(void *)1, // parameter of the task
1, // priority of the task
&lorasendTask, // task handle
1); // CPU core
return ESP_OK;
}
void lora_enqueuedata(MessageBuffer_t *message) {
// enqueue message in LORA send queue
if (xQueueSendToBack(LoraSendQueue, (void *)message, (TickType_t)0) !=
pdTRUE) {
snprintf(lmic_event_msg + 14, LMIC_EVENTMSG_LEN - 14, "<>");
ESP_LOGW(TAG, "LORA sendqueue is full");
} else {
// add Lora send queue length to display
snprintf(lmic_event_msg + 14, LMIC_EVENTMSG_LEN - 14, "%2u",
uxQueueMessagesWaiting(LoraSendQueue));
}
}
void lora_queuereset(void) { xQueueReset(LoraSendQueue); }
uint32_t lora_queuewaiting(void) {
return uxQueueMessagesWaiting(LoraSendQueue);
}
// LMIC loop task
void lmictask(void *pvParameters) {
_ASSERT((uint32_t)pvParameters == 1);
while (1) { while (1) {
os_runloop_once(); // execute lmic scheduled jobs and events os_runloop_once(); // execute lmic scheduled jobs and events
delay(2); // yield to CPU delay(2); // yield to CPU
} }
} // lmictask }
// lmic event handler // lmic event handler
void myEventCallback(void *pUserData, ev_t ev) { void myEventCallback(void *pUserData, ev_t ev) {
@ -410,7 +379,7 @@ void myEventCallback(void *pUserData, ev_t ev) {
case EV_JOIN_FAILED: case EV_JOIN_FAILED:
// must call LMIC_reset() to stop joining // must call LMIC_reset() to stop joining
// otherwise join procedure continues. // otherwise join procedure continues.
lora_stack_reset(); LMIC_reset();
break; break;
case EV_JOIN_TXCOMPLETE: case EV_JOIN_TXCOMPLETE:
@ -560,4 +529,44 @@ void mac_decode(const uint8_t cmd[], const uint8_t cmdlen, bool is_down) {
} // mac_decode() } // mac_decode()
#endif // VERBOSE #endif // VERBOSE
// following code snippet was taken from
// https://github.com/JackGruber/ESP32-LMIC-DeepSleep-example/blob/master/src/main.cpp
void SaveLMICToRTC(int deepsleep_sec) {
RTC_LMIC = LMIC;
// ESP32 can't track millis during DeepSleep and no option to advance
// millis after DeepSleep. Therefore reset DutyCyles
unsigned long now = millis();
// EU Like Bands
#if defined(CFG_LMIC_EU_like)
for (int i = 0; i < MAX_BANDS; i++) {
ostime_t correctedAvail =
RTC_LMIC.bands[i].avail -
((now / 1000.0 + deepsleep_sec) * OSTICKS_PER_SEC);
if (correctedAvail < 0) {
correctedAvail = 0;
}
RTC_LMIC.bands[i].avail = correctedAvail;
}
RTC_LMIC.globalDutyAvail = RTC_LMIC.globalDutyAvail -
((now / 1000.0 + deepsleep_sec) * OSTICKS_PER_SEC);
if (RTC_LMIC.globalDutyAvail < 0) {
RTC_LMIC.globalDutyAvail = 0;
}
#else
ESP_LOGW(TAG, "No DutyCycle recalculation function!");
#endif
ESP_LOGI(TAG, "LMIC state saved");
}
void LoadLMICFromRTC() {
LMIC = RTC_LMIC;
ESP_LOGI(TAG, "LMIC state loaded");
}
#endif // HAS_LORA #endif // HAS_LORA

View File

@ -3,20 +3,17 @@
#include "globals.h" #include "globals.h"
#include "macsniff.h" #include "macsniff.h"
#if (VENDORFILTER)
#include "vendor_array.h"
#endif
// Local logging tag // Local logging tag
static const char TAG[] = __FILE__; static const char TAG[] = __FILE__;
QueueHandle_t MacQueue; static QueueHandle_t MacQueue;
TaskHandle_t macProcessTask; TaskHandle_t macProcessTask;
uint16_t salt = 0; static uint32_t salt = renew_salt();
uint16_t get_salt(void) { uint32_t renew_salt(void) {
salt = (uint16_t)random(65536); // get new 16bit random for salting hashes salt = esp_random();
ESP_LOGV(TAG, "new salt = %04X", salt);
return salt; return salt;
} }
@ -58,7 +55,7 @@ esp_err_t macQueueInit() {
xTaskCreatePinnedToCore(mac_process, // task function xTaskCreatePinnedToCore(mac_process, // task function
"mac_process", // name of task "mac_process", // name of task
2048, // stack size of task 3072, // stack size of task
(void *)1, // parameter of the task (void *)1, // parameter of the task
1, // priority of the task 1, // priority of the task
&macProcessTask, // task handle &macProcessTask, // task handle
@ -105,8 +102,9 @@ void IRAM_ATTR mac_add(uint8_t *paddr, int8_t rssi, snifftype_t sniff_type) {
uint16_t mac_analyze(MacBuffer_t MacBuffer) { uint16_t mac_analyze(MacBuffer_t MacBuffer) {
if (salt == 0) // ensure we have salt (appears after radio is turned on) uint32_t *mac; // pointer to shortened 4 byte MAC
return 0; uint32_t saltedmac;
uint16_t hashedmac;
if ((cfg.rssilimit) && if ((cfg.rssilimit) &&
(MacBuffer.rssi < cfg.rssilimit)) { // rssi is negative value (MacBuffer.rssi < cfg.rssilimit)) { // rssi is negative value
@ -121,27 +119,13 @@ uint16_t mac_analyze(MacBuffer_t MacBuffer) {
int8_t beaconID = isBeacon(macConvert(MacBuffer.mac)); int8_t beaconID = isBeacon(macConvert(MacBuffer.mac));
if (beaconID >= 0) { if (beaconID >= 0) {
ESP_LOGI(TAG, "Beacon ID#%d detected", beaconID); ESP_LOGI(TAG, "Beacon ID#%d detected", beaconID);
#if (HAS_LED != NOT_A_PIN) || defined(HAS_RGB_LED)
blink_LED(COLOR_WHITE, 2000); blink_LED(COLOR_WHITE, 2000);
#endif
payload.reset(); payload.reset();
payload.addAlarm(MacBuffer.rssi, beaconID); payload.addAlarm(MacBuffer.rssi, beaconID);
SendPayload(BEACONPORT, prio_high); SendPayload(BEACONPORT);
} }
}; };
#if (VENDORFILTER)
uint32_t *oui; // temporary buffer for vendor OUI
oui = (uint32_t *)MacBuffer.mac;
// if we find OUI on vendor filter list we don't analyze and return early
if (std::find(vendors.begin(), vendors.end(), __builtin_bswap32(*oui) >> 8) ==
vendors.end())
return 0;
#endif
char buff[10]; // temporary buffer for printf
uint32_t *mac; // temporary buffer for shortened MAC
// only last 3 MAC Address bytes are used for MAC address anonymization // only last 3 MAC Address bytes are used for MAC address anonymization
// but since it's uint32 we take 4 bytes to avoid 1st value to be 0. // but since it's uint32 we take 4 bytes to avoid 1st value to be 0.
// this gets MAC in msb (= reverse) order, but doesn't matter for hashing it. // this gets MAC in msb (= reverse) order, but doesn't matter for hashing it.
@ -151,11 +135,15 @@ uint16_t mac_analyze(MacBuffer_t MacBuffer) {
// and increment counter on display // and increment counter on display
// https://en.wikipedia.org/wiki/MAC_Address_Anonymization // https://en.wikipedia.org/wiki/MAC_Address_Anonymization
snprintf(buff, sizeof(buff), "%08X", // reversed 4 byte MAC added to current salt
*mac + (uint32_t)salt); // convert unsigned 32-bit salted MAC saltedmac = *mac + salt;
// to 8 digit hex string
uint16_t hashedmac = rokkit(&buff[3], 5); // hash MAC 8 digit -> 5 digit // hashed 4 byte MAC
auto newmac = macs.insert(hashedmac); // add hashed MAC, if new unique // to save RAM, we use only lower 2 bytes of hash, since collisions don't
// matter in our use case
hashedmac = hash((const char *)&saltedmac, 4);
auto newmac = macs.insert(hashedmac); // add hashed MAC, if new unique
bool added = bool added =
newmac.second ? true : false; // true if hashed MAC is unique in container newmac.second ? true : false; // true if hashed MAC is unique in container
@ -166,37 +154,30 @@ uint16_t mac_analyze(MacBuffer_t MacBuffer) {
case MAC_SNIFF_WIFI: case MAC_SNIFF_WIFI:
macs_wifi++; // increment Wifi MACs counter macs_wifi++; // increment Wifi MACs counter
#if (HAS_LED != NOT_A_PIN) || defined(HAS_RGB_LED)
blink_LED(COLOR_GREEN, 50); blink_LED(COLOR_GREEN, 50);
#endif
break; break;
#if (BLECOUNTER)
case MAC_SNIFF_BLE: case MAC_SNIFF_BLE:
macs_ble++; // increment BLE Macs counter macs_ble++; // increment BLE Macs counter
#if (HAS_LED != NOT_A_PIN) || defined(HAS_RGB_LED)
blink_LED(COLOR_MAGENTA, 50); blink_LED(COLOR_MAGENTA, 50);
#endif
break; break;
#if (COUNT_ENS) #if (COUNT_ENS)
case MAC_SNIFF_BLE_ENS: case MAC_SNIFF_BLE_ENS:
macs_ble++; // increment BLE Macs counter macs_ble++; // increment BLE Macs counter
cwa_mac_add(hashedmac); // process ENS beacon cwa_mac_add(hashedmac); // process ENS beacon
#if (HAS_LED != NOT_A_PIN) || defined(HAS_RGB_LED)
blink_LED(COLOR_WHITE, 50); blink_LED(COLOR_WHITE, 50);
#endif
break; break;
#endif
#endif // COUNT_ENS default:
#endif // BLECOUNTER break;
} // switch } // switch
} // added } // added
// Log scan result // Log scan result
ESP_LOGV(TAG, ESP_LOGV(TAG,
"%s %s RSSI %ddBi -> salted MAC %s -> Hash %04X -> WiFi:%d " "%s %s RSSI %ddBi -> MAC %0x:%0x:%0x:%0x:%0x:%0x -> salted %04X"
" -> hashed %04X -> WiFi:%d "
"BLTH:%d " "BLTH:%d "
#if (COUNT_ENS) #if (COUNT_ENS)
"(CWA:%d)" "(CWA:%d)"
@ -204,7 +185,9 @@ uint16_t mac_analyze(MacBuffer_t MacBuffer) {
"-> %d Bytes left", "-> %d Bytes left",
added ? "new " : "known", added ? "new " : "known",
MacBuffer.sniff_type == MAC_SNIFF_WIFI ? "WiFi" : "BLTH", MacBuffer.sniff_type == MAC_SNIFF_WIFI ? "WiFi" : "BLTH",
MacBuffer.rssi, buff, hashedmac, macs_wifi, macs_ble, MacBuffer.rssi, MacBuffer.mac[0], MacBuffer.mac[1], MacBuffer.mac[2],
MacBuffer.mac[3], MacBuffer.mac[4], MacBuffer.mac[5], saltedmac,
hashedmac, macs_wifi, macs_ble,
#if (COUNT_ENS) #if (COUNT_ENS)
cwa_report(), cwa_report(),
#endif #endif

View File

@ -29,16 +29,17 @@ Task Core Prio Purpose
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
ledloop 0 3 blinks LEDs ledloop 0 3 blinks LEDs
spiloop 0 2 reads/writes data on spi interface spiloop 0 2 reads/writes data on spi interface
mqttloop 0 2 reads/writes data on ETH interface
IDLE 0 0 ESP32 arduino scheduler -> runs wifi sniffer IDLE 0 0 ESP32 arduino scheduler -> runs wifi sniffer
lmictask 1 2 MCCI LMiC LORAWAN stack lmictask 1 2 MCCI LMiC LORAWAN stack
clockloop 1 4 generates realtime telegrams for external clock clockloop 1 4 generates realtime telegrams for external clock
mqttloop 1 2 reads/writes data on ETH interface
timesync_proc 1 3 processes realtime time sync requests timesync_proc 1 3 processes realtime time sync requests
irqhandler 1 2 cyclic tasks (i.e. displayrefresh) triggered by timers irqhandler 1 2 cyclic tasks (i.e. displayrefresh) triggered by timers
gpsloop 1 1 reads data from GPS via serial or i2c gpsloop 1 1 reads data from GPS via serial or i2c
lorasendtask 1 1 feeds data from lora sendqueue to lmcic lorasendtask 1 1 feeds data from lora sendqueue to lmcic
macprocess 1 1 analyzes sniffed MACs macprocess 1 1 MAC analyzer loop
rmcd_process 1 1 Remote command interpreter loop
IDLE 1 0 ESP32 arduino scheduler -> runs wifi channel rotator IDLE 1 0 ESP32 arduino scheduler -> runs wifi channel rotator
Low priority numbers denote low priority tasks. Low priority numbers denote low priority tasks.
@ -60,15 +61,16 @@ irqHandlerTask (Core 1), see irqhandler.cpp
fired by hardware fired by hardware
DisplayIRQ -> esp32 timer 0 DisplayIRQ -> esp32 timer 0
ButtonIRQ -> external gpio CLOCKIRQ -> esp32 timer 1 or external GPIO (RTC_INT or GPS_INT)
PMUIRQ -> PMU chip gpio MatrixDisplayIRQ-> esp32 timer 3
ButtonIRQ -> external GPIO
PMUIRQ -> PMU chip GPIO
fired by software (Ticker.h) fired by software (Ticker.h)
TIMESYNC_IRQ -> setTimeSyncIRQ() TIMESYNC_IRQ -> setTimeSyncIRQ()
CYCLIC_IRQ -> setCyclicIRQ() CYCLIC_IRQ -> setCyclicIRQ()
SENDCYCLE_IRQ -> setSendIRQ() SENDCYCLE_IRQ -> setSendIRQ()
BME_IRQ -> setBMEIRQ() BME_IRQ -> setBMEIRQ()
MQTT_IRQ -> setMqttIRQ()
ClockTask (Core 1), see timekeeper.cpp ClockTask (Core 1), see timekeeper.cpp
@ -88,8 +90,8 @@ triggers pps 1 sec impulse
configData_t cfg; // struct holds current device configuration configData_t cfg; // struct holds current device configuration
char lmic_event_msg[LMIC_EVENTMSG_LEN]; // display buffer for LMIC event message char lmic_event_msg[LMIC_EVENTMSG_LEN]; // display buffer for LMIC event message
uint8_t batt_level = 0; // display value uint8_t batt_level = 0; // display value
uint8_t volatile channel = 0; // channel rotation counter uint8_t volatile channel = WIFI_CHANNEL_MIN; // channel rotation counter
uint8_t volatile rf_load = 0; // RF traffic indicator uint8_t volatile rf_load = 0; // RF traffic indicator
uint16_t volatile macs_wifi = 0, macs_ble = 0; // globals for display uint16_t volatile macs_wifi = 0, macs_ble = 0; // globals for display
hw_timer_t *ppsIRQ = NULL, *displayIRQ = NULL, *matrixDisplayIRQ = NULL; hw_timer_t *ppsIRQ = NULL, *displayIRQ = NULL, *matrixDisplayIRQ = NULL;
@ -101,7 +103,7 @@ timesource_t timeSource = _unsynced;
// container holding unique MAC address hashes with Memory Alloctor using PSRAM, // container holding unique MAC address hashes with Memory Alloctor using PSRAM,
// if present // if present
std::set<uint16_t, std::less<uint16_t>, Mallocator<uint16_t>> macs; DRAM_ATTR std::set<uint16_t, std::less<uint16_t>, Mallocator<uint16_t>> macs;
// initialize payload encoder // initialize payload encoder
PayloadConvert payload(PAYLOAD_BUFFER_SIZE); PayloadConvert payload(PAYLOAD_BUFFER_SIZE);
@ -138,7 +140,7 @@ void setup() {
esp_log_level_set("*", ESP_LOG_NONE); esp_log_level_set("*", ESP_LOG_NONE);
#endif #endif
do_after_reset(rtc_get_reset_reason(0)); do_after_reset();
// print chip information on startup if in verbose mode after coldstart // print chip information on startup if in verbose mode after coldstart
#if (VERBOSE) #if (VERBOSE)
@ -208,7 +210,8 @@ void setup() {
_ASSERT(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(); if (RTC_runmode == RUNMODE_POWERCYCLE)
i2c_scan();
// initialize display // initialize display
#ifdef HAS_DISPLAY #ifdef HAS_DISPLAY
@ -291,8 +294,12 @@ void setup() {
ESP_LOGI(TAG, "Starting MAC processor..."); ESP_LOGI(TAG, "Starting MAC processor...");
macQueueInit(); macQueueInit();
// start rcommand processing task
ESP_LOGI(TAG, "Starting rcommand interpreter...");
rcmd_init();
// start BLE scan callback if BLE function is enabled in NVRAM configuration // start BLE scan callback if BLE function is enabled in NVRAM configuration
// or switch off bluetooth, if not compiled // or remove bluetooth stack from RAM, if option bluetooth is not compiled
#if (BLECOUNTER) #if (BLECOUNTER)
strcat_P(features, " BLE"); strcat_P(features, " BLE");
if (cfg.blescan) { if (cfg.blescan) {
@ -303,9 +310,9 @@ void setup() {
#else #else
// remove bluetooth stack to gain more free memory // remove bluetooth stack to gain more free memory
btStop(); btStop();
ESP_ERROR_CHECK(esp_bt_mem_release(ESP_BT_MODE_BTDM)); esp_bt_mem_release(ESP_BT_MODE_BTDM);
ESP_ERROR_CHECK(esp_coex_preference_set( esp_coex_preference_set(
ESP_COEX_PREFER_WIFI)); // configure Wifi/BT coexist lib ESP_COEX_PREFER_WIFI); // configure Wifi/BT coexist lib
#endif #endif
// initialize gps // initialize gps
@ -348,9 +355,7 @@ void setup() {
// initialize LoRa // initialize LoRa
#if (HAS_LORA) #if (HAS_LORA)
strcat_P(features, " LORA"); strcat_P(features, " LORA");
// kick off join, except we come from sleep _ASSERT(lmic_init() == ESP_OK);
_ASSERT(lora_stack_init(RTC_runmode == RUNMODE_WAKEUP ? false : true) ==
ESP_OK);
#endif #endif
// initialize SPI // initialize SPI
@ -376,7 +381,7 @@ void setup() {
strcat_P(features, " SDS"); strcat_P(features, " SDS");
#endif #endif
#if (VENDORFILTER) #if (MACFILTER)
strcat_P(features, " FILTER"); strcat_P(features, " FILTER");
#endif #endif
@ -414,23 +419,23 @@ void setup() {
#if (WIFICOUNTER) #if (WIFICOUNTER)
strcat_P(features, " WIFI"); strcat_P(features, " WIFI");
// start wifi in monitor mode and start channel rotation timer // install wifi driver in RAM and start channel hopping
wifi_sniffer_init(); wifi_sniffer_init();
if (cfg.blescan) { // start wifi sniffing, if enabled
if (cfg.wifiscan) {
ESP_LOGI(TAG, "Starting Wifi..."); ESP_LOGI(TAG, "Starting Wifi...");
switch_wifi_sniffer(1); switch_wifi_sniffer(1);
} else } else
switch_wifi_sniffer(0); switch_wifi_sniffer(0);
#else #else
// switch off wifi // remove wifi driver from RAM, if option wifi not compiled
esp_wifi_deinit(); esp_wifi_deinit();
#endif #endif
// initialize salt value using esp_random() called by random() in // initialize salt value using esp_random() called by random() in
// arduino-esp32 core. Note: do this *after* wifi has started, since // arduino-esp32 core. Note: do this *after* wifi has started, since
// function gets it's seed from RF noise // function gets it's seed from RF noise
get_salt(); // get new 16bit for salting hashes reset_counters();
// start state machine // start state machine
ESP_LOGI(TAG, "Starting Interrupt Handler..."); ESP_LOGI(TAG, "Starting Interrupt Handler...");
@ -453,8 +458,10 @@ void setup() {
#endif #endif
if (bme_init()) if (bme_init())
ESP_LOGI(TAG, "BME sensor initialized"); ESP_LOGI(TAG, "BME sensor initialized");
else else {
ESP_LOGE(TAG, "BME sensor could not be initialized"); ESP_LOGE(TAG, "BME sensor could not be initialized");
cfg.payloadmask &= ~MEMS_DATA; // switch off transmit of BME data
}
#endif #endif
// starting timers and interrupts // starting timers and interrupts
@ -526,4 +533,4 @@ void setup() {
} // setup() } // setup()
void loop() { vTaskDelete(NULL); } void loop() { vTaskDelete(NULL); }

View File

@ -4,23 +4,26 @@
static const char TAG[] = __FILE__; static const char TAG[] = __FILE__;
QueueHandle_t MQTTSendQueue; static QueueHandle_t MQTTSendQueue;
TaskHandle_t mqttTask; TaskHandle_t mqttTask;
Ticker mqttTimer; Ticker mqttTimer;
WiFiClient netClient; WiFiClient netClient;
MQTTClient mqttClient; MQTTClient mqttClient;
void mqtt_deinit(void) {
mqttClient.unsubscribe(MQTT_INTOPIC);
mqttClient.onMessageAdvanced(NULL);
mqttClient.disconnect();
vTaskDelete(mqttTask);
}
esp_err_t mqtt_init(void) { esp_err_t mqtt_init(void) {
// setup network connection // setup network connection and MQTT client
WiFi.onEvent(NetworkEvent);
ETH.begin(); ETH.begin();
// WiFi.mode(WIFI_STA);
// WiFi.begin("SSID", "PASSWORD");
// setup mqtt client
mqttClient.begin(MQTT_SERVER, MQTT_PORT, netClient); mqttClient.begin(MQTT_SERVER, MQTT_PORT, netClient);
mqttClient.setKeepAlive(MQTT_KEEPALIVE);
mqttClient.onMessageAdvanced(mqtt_callback); mqttClient.onMessageAdvanced(mqtt_callback);
_ASSERT(SEND_QUEUE_SIZE > 0); _ASSERT(SEND_QUEUE_SIZE > 0);
@ -33,17 +36,20 @@ 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, setMqttIRQ); xTaskCreatePinnedToCore(mqtt_client_task, "mqttloop", 4096, (void *)NULL, 1,
xTaskCreate(mqtt_client_task, "mqttloop", 4096, (void *)NULL, 1, &mqttTask); &mqttTask, 1);
return ESP_OK; return ESP_OK;
} }
int mqtt_connect(const char *my_host, const uint16_t my_port) { int mqtt_connect(const char *my_host, const uint16_t my_port) {
IPAddress mqtt_server_ip; IPAddress mqtt_server_ip;
uint8_t mac[6];
char clientId[20];
// static String clientId = "paxcounter-" + ETH.macAddress(); // hash 6 byte MAC to 4 byte hash
static String clientId = "paxcounter-" + String(random(0xffff), HEX); esp_eth_get_mac(mac);
const uint32_t hashedmac = hash((const char *)mac, 6);
snprintf(clientId, 20, "paxcounter_%08x", hashedmac);
ESP_LOGI(TAG, "MQTT name is %s", MQTT_CLIENTNAME); ESP_LOGI(TAG, "MQTT name is %s", MQTT_CLIENTNAME);
@ -73,118 +79,80 @@ int mqtt_connect(const char *my_host, const uint16_t my_port) {
return 0; return 0;
} }
void NetworkEvent(WiFiEvent_t event) {
switch (event) {
case SYSTEM_EVENT_ETH_START:
case SYSTEM_EVENT_STA_START:
ESP_LOGI(TAG, "Network link layer started");
// ETH.setHostname(ETH.macAddress().c_str());
break;
case SYSTEM_EVENT_ETH_STOP:
case SYSTEM_EVENT_STA_STOP:
ESP_LOGI(TAG, "Network link layer stopped");
break;
case SYSTEM_EVENT_ETH_CONNECTED:
case SYSTEM_EVENT_STA_CONNECTED:
ESP_LOGI(TAG, "Network link connected");
break;
case SYSTEM_EVENT_ETH_DISCONNECTED:
case SYSTEM_EVENT_STA_DISCONNECTED:
ESP_LOGI(TAG, "Network link disconnected");
break;
case SYSTEM_EVENT_ETH_GOT_IP:
ESP_LOGI(TAG, "IP: %s", ETH.localIP().toString().c_str());
ESP_LOGI(TAG, "Link Speed: %d Mbps %s", ETH.linkSpeed(),
ETH.fullDuplex() ? "full duplex" : "half duplex");
mqtt_connect(MQTT_SERVER, MQTT_PORT);
break;
case SYSTEM_EVENT_STA_GOT_IP:
ESP_LOGI(TAG, "IP: %s", WiFi.localIP().toString().c_str());
mqtt_connect(MQTT_SERVER, MQTT_PORT);
break;
default:
break;
}
}
void mqtt_client_task(void *param) { void mqtt_client_task(void *param) {
MessageBuffer_t msg; MessageBuffer_t msg;
while (1) { while (1) {
// fetch next or wait for payload to send from queue
if (xQueueReceive(MQTTSendQueue, &msg, portMAX_DELAY) != pdTRUE) {
ESP_LOGE(TAG, "Premature return from xQueueReceive() with no data!");
continue;
}
// send data to mqtt server, if we are connected
if (mqttClient.connected()) { if (mqttClient.connected()) {
char buffer[PAYLOAD_BUFFER_SIZE + 3]; // check for incoming messages
snprintf(buffer, msg.MessageSize + 3, "%u/%s", msg.MessagePort, mqttClient.loop();
msg.Message);
if (mqttClient.publish(MQTT_OUTTOPIC, buffer)) { // fetch next or wait for payload to send from queue
ESP_LOGI(TAG, "%d byte(s) sent to MQTT server", msg.MessageSize + 2); // do not delete item from queue until it is transmitted
// consider mqtt timeout while waiting
if (xQueuePeek(MQTTSendQueue, &msg,
MQTT_KEEPALIVE * 1000 / portTICK_PERIOD_MS) != pdTRUE)
continue; continue;
} else {
mqtt_enqueuedata(&msg); // postpone the undelivered message
ESP_LOGD(TAG,
"Couldn't sent message to MQTT server, message postponed");
}
// prepare mqtt topic
char topic[16];
snprintf(topic, 16, "%s/%u", MQTT_OUTTOPIC, msg.MessagePort);
size_t out_len = 0;
// get length of base64 encoded message
mbedtls_base64_encode(NULL, 0, &out_len, (unsigned char *)msg.Message,
msg.MessageSize);
// base64 encode the message
unsigned char encoded[out_len];
mbedtls_base64_encode(encoded, out_len, &out_len,
(unsigned char *)msg.Message, msg.MessageSize);
// send encoded message to mqtt server and delete it from queue
if (mqttClient.publish(topic, (const char *)encoded, out_len)) {
ESP_LOGD(TAG, "%u bytes sent to MQTT server", out_len);
xQueueReceive(MQTTSendQueue, &msg, (TickType_t)0);
} else
ESP_LOGD(TAG, "Couldn't sent message to MQTT server");
} else { } else {
// attempt to reconnect to MQTT server // attempt to reconnect to MQTT server
ESP_LOGD(TAG, "MQTT client reconnecting..."); ESP_LOGD(TAG, "MQTT client reconnecting...");
ESP_LOGD(TAG, "MQTT last_error = %d / rc = %d", mqttClient.lastError(),
mqttClient.returnCode());
mqtt_enqueuedata(&msg); // postpone the undelivered message
delay(MQTT_RETRYSEC * 1000); delay(MQTT_RETRYSEC * 1000);
mqtt_connect(MQTT_SERVER, MQTT_PORT); mqtt_connect(MQTT_SERVER, MQTT_PORT);
} }
} // while (1)
} // while(1)
} }
void mqtt_enqueuedata(MessageBuffer_t *message) { // process incoming MQTT messages
// enqueue message in MQTT send queue void mqtt_callback(MQTTClient *client, char *topic, char *payload, int length) {
BaseType_t ret; if (strcmp(topic, MQTT_INTOPIC) == 0) {
MessageBuffer_t DummyBuffer;
sendprio_t prio = message->MessagePrio;
switch (prio) { // get length of base64 encoded message
case prio_high: size_t out_len = 0;
// clear space in queue if full, then fallthrough to normal mbedtls_base64_decode(NULL, 0, &out_len, (unsigned char *)payload, length);
if (!uxQueueSpacesAvailable(MQTTSendQueue))
xQueueReceive(MQTTSendQueue, &DummyBuffer, (TickType_t)0); // decode the base64 message
case prio_normal: unsigned char decoded[out_len];
ret = xQueueSendToFront(MQTTSendQueue, (void *)message, (TickType_t)0); mbedtls_base64_decode(decoded, out_len, &out_len, (unsigned char *)payload,
break; length);
case prio_low:
default: rcommand(decoded, out_len);
ret = xQueueSendToBack(MQTTSendQueue, (void *)message, (TickType_t)0);
break;
} }
if (ret != pdTRUE) }
// enqueue outgoing messages in MQTT send queue
void mqtt_enqueuedata(MessageBuffer_t *message) {
if (xQueueSendToBack(MQTTSendQueue, (void *)message, (TickType_t)0) != pdTRUE)
ESP_LOGW(TAG, "MQTT sendqueue is full"); ESP_LOGW(TAG, "MQTT sendqueue is full");
} }
void mqtt_callback(MQTTClient *client, char topic[], char payload[],
int length) {
if (strcmp(topic, MQTT_INTOPIC) == 0)
rcommand((const uint8_t *)payload, (const uint8_t)length);
}
void mqtt_loop(void) {
if (!mqttClient.loop())
ESP_LOGD(TAG, "MQTT last_error = %d / rc = %d", mqttClient.lastError(),
mqttClient.returnCode());
}
void mqtt_queuereset(void) { xQueueReset(MQTTSendQueue); } void mqtt_queuereset(void) { xQueueReset(MQTTSendQueue); }
void setMqttIRQ(void) { xTaskNotify(irqHandlerTask, MQTT_IRQ, eSetBits); }
uint32_t mqtt_queuewaiting(void) {
return uxQueueMessagesWaiting(MQTTSendQueue);
}
#endif // HAS_MQTT #endif // HAS_MQTT

View File

@ -178,9 +178,9 @@ int do_ota_update() {
client.print("Cache-Control: no-cache\r\n"); client.print("Cache-Control: no-cache\r\n");
client.print("Connection: close\r\n\r\n"); client.print("Connection: close\r\n\r\n");
unsigned long timeout = _millis(); unsigned long timeout = millis();
while (client.available() == 0) { while (client.available() == 0) {
if ((_millis() - timeout) > (RESPONSE_TIMEOUT_MS)) { if ((long)(millis() - timeout) > (RESPONSE_TIMEOUT_MS)) {
ESP_LOGI(TAG, "Client timeout"); ESP_LOGI(TAG, "Client timeout");
ota_display(3, " E", "client timeout"); ota_display(3, " E", "client timeout");
goto abort; goto abort;

View File

@ -11,12 +11,13 @@
// 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 SLEEPCYCLE 0 // sleep time after a send cycle [seconds/2], 0 .. 255; 0 means no sleep [default = 0]
#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 0 // 0=cyclic, 1=cumulative, 2=cyclic confirmed #define COUNTERMODE 0 // 0=cyclic, 1=cumulative, 2=cyclic confirmed
// MAC sniffing parameters // MAC sniffing parameters
#define VENDORFILTER 0 // set to 0 if you want to scan all devices, not filtering smartphone OUIs #define MACFILTER 1 // set to 0 if you want to scan all devices, 1 to scan only devices with random MACs (aka smartphones) [default = 1]
#define BLECOUNTER 0 // set to 0 if you do not want to install the BLE sniffer #define BLECOUNTER 1 // set to 0 if you do not want to install the BLE sniffer
#define WIFICOUNTER 1 // set to 0 if you do not want to install the WIFI sniffer #define WIFICOUNTER 1 // set to 0 if you do not want to install the WIFI sniffer
#define MAC_QUEUE_SIZE 50 // size of MAC processing buffer (number of MACs) [default = 50] #define MAC_QUEUE_SIZE 50 // size of MAC processing buffer (number of MACs) [default = 50]
@ -26,11 +27,11 @@
#define BLESCANINTERVAL 80 // [illiseconds] scan interval, see below, 3 .. 10240, default 80ms = 100% duty cycle #define BLESCANINTERVAL 80 // [illiseconds] scan interval, see below, 3 .. 10240, default 80ms = 100% duty cycle
// Corona Exposure Notification Service(ENS) counter // Corona Exposure Notification Service(ENS) counter
#define COUNT_ENS 0 // count found number of devices which advertise Exposure Notification Service #define COUNT_ENS 1 // count found number of devices which advertise Exposure Notification Service
// set to 0 if you do not want to enable this function // set to 1 if you want to enable this function [default=0]
// for additional sensors (added by some user) // for additional sensors (added by some user)
#define HAS_SENSOR_1 0 // set to 1 to enable data transfer of user sensor #1 (also used as ENS counter) [default=0] #define HAS_SENSOR_1 1 // set to 1 to enable data transfer of user sensor #1 (also used as ENS counter) [default=0]
#define HAS_SENSOR_2 0 // set to 1 to enable data transfer of user sensor #2 [default=0] #define HAS_SENSOR_2 0 // set to 1 to enable data transfer of user sensor #2 [default=0]
#define HAS_SENSOR_3 0 // set to 1 to enable data transfer of user sensor #3 [default=0] #define HAS_SENSOR_3 0 // set to 1 to enable data transfer of user sensor #3 [default=0]
@ -83,10 +84,10 @@
#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 1 // set to 1 to use LORA network as time source, 0 means off [default = 1] #define TIME_SYNC_LORAWAN 0 // 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
#define TIME_SYNC_SAMPLES 1 // number of time requests for averaging, max. 255 #define TIME_SYNC_SAMPLES 1 // number of time requests for averaging, max. 255
#define TIME_SYNC_CYCLE 60 // delay between two time samples [seconds] #define TIME_SYNC_CYCLE 60 // delay between two time samples [seconds]
#define TIME_SYNC_TIMEOUT 400 // timeout waiting for timeserver answer [seconds] #define TIME_SYNC_TIMEOUT 400 // timeout waiting for timeserver answer [seconds]
@ -120,3 +121,15 @@
#define CAYENNE_DEVICECONFIG 11 // device period configuration #define CAYENNE_DEVICECONFIG 11 // device period configuration
#define CAYENNE_SENSORREAD 13 // sensor period configuration #define CAYENNE_SENSORREAD 13 // sensor period configuration
#define CAYENNE_SENSORENABLE 14 // sensor enable configuration #define CAYENNE_SENSORENABLE 14 // sensor enable configuration
// MQTT settings, only needed if MQTT is used (#define HAS_MQTT in board hal file)
#define MQTT_ETHERNET 0 // select PHY: set 0 for Wifi, 1 for ethernet
#define MQTT_INTOPIC "paxin"
#define MQTT_OUTTOPIC "paxout"
#define MQTT_PORT 1883
#define MQTT_SERVER "public.cloud.shiftr.io"
#define MQTT_USER "public"
#define MQTT_PASSWD "public"
#define MQTT_RETRYSEC 20 // retry reconnect every 20 seconds
#define MQTT_KEEPALIVE 10 // keep alive interval in seconds
//#define MQTT_CLIENTNAME "my_paxcounter" // generated by default

View File

@ -49,7 +49,7 @@ void PayloadConvert::addConfig(configData_t value) {
buffer[cursor++] = value.blescantime; buffer[cursor++] = value.blescantime;
buffer[cursor++] = value.blescan; buffer[cursor++] = value.blescan;
buffer[cursor++] = value.wifiant; buffer[cursor++] = value.wifiant;
buffer[cursor++] = value.vendorfilter; buffer[cursor++] = value.macfilter;
buffer[cursor++] = value.rgblum; buffer[cursor++] = value.rgblum;
buffer[cursor++] = value.payloadmask; buffer[cursor++] = value.payloadmask;
buffer[cursor++] = value.monitormode; buffer[cursor++] = value.monitormode;
@ -179,8 +179,7 @@ void PayloadConvert::addConfig(configData_t value) {
writeBitmap(value.adrmode ? true : false, value.screensaver ? true : false, writeBitmap(value.adrmode ? true : false, value.screensaver ? true : false,
value.screenon ? true : false, value.countermode ? true : false, value.screenon ? true : false, value.countermode ? true : false,
value.blescan ? true : false, value.wifiant ? true : false, value.blescan ? true : false, value.wifiant ? true : false,
value.vendorfilter ? true : false, value.macfilter ? true : false, value.monitormode ? true : false);
value.monitormode ? true : false);
writeUint8(value.payloadmask); writeUint8(value.payloadmask);
writeVersion(value.version); writeVersion(value.version);
} }

View File

@ -47,17 +47,14 @@ void AXP192_powerevent_IRQ(void) {
if (pmu.isBattTempHighIRQ()) if (pmu.isBattTempHighIRQ())
ESP_LOGI(TAG, "Battery low temperature."); ESP_LOGI(TAG, "Battery low temperature.");
// short press -> esp32 deep sleep mode, can be exited by pressing user button // short press -> esp32 deep sleep mode, can be exited by pressing user button
#ifdef HAS_BUTTON if (pmu.isPEKShortPressIRQ()) {
if (pmu.isPEKShortPressIRQ() && (RTC_runmode == RUNMODE_NORMAL)) {
enter_deepsleep(0, HAS_BUTTON); enter_deepsleep(0, HAS_BUTTON);
} }
#endif
// long press -> shutdown power, can be exited by another longpress // long press -> shutdown power, can be exited by another longpress
if (pmu.isPEKLongtPressIRQ()) { if (pmu.isPEKLongtPressIRQ()) {
AXP192_power(pmu_power_off); // switch off Lora, GPS, display AXP192_power(pmu_power_off); // switch off Lora, GPS, display
pmu.shutdown(); // switch off device
} }
pmu.clearIRQ(); pmu.clearIRQ();
@ -71,25 +68,33 @@ void AXP192_power(pmu_power_t powerlevel) {
switch (powerlevel) { switch (powerlevel) {
case pmu_power_off: case pmu_power_off:
pmu.setChgLEDMode(AXP20X_LED_OFF); pmu.shutdown();
pmu.setPowerOutPut(AXP192_DCDC1, AXP202_OFF);
pmu.setPowerOutPut(AXP192_LDO3, AXP202_OFF);
pmu.setPowerOutPut(AXP192_LDO2, AXP202_OFF);
// pmu.setPowerOutPut(AXP192_DCDC3, AXP202_OFF);
break; break;
case pmu_power_sleep: case pmu_power_sleep:
pmu.setChgLEDMode(AXP20X_LED_BLINK_1HZ); #ifdef PMU_LED_SLEEP_MODE
// we don't cut off DCDC1, because then display blocks i2c bus pmu.setChgLEDMode(PMU_LED_SLEEP_MODE);
#else
pmu.setChgLEDMode(AXP20X_LED_OFF);
#endif
// we don't cut off DCDC1, because OLED display will then block i2c bus
// pmu.setPowerOutPut(AXP192_DCDC1, AXP202_OFF); // OLED off
pmu.setPowerOutPut(AXP192_LDO3, AXP202_OFF); // gps off pmu.setPowerOutPut(AXP192_LDO3, AXP202_OFF); // gps off
pmu.setPowerOutPut(AXP192_LDO2, AXP202_OFF); // lora off pmu.setPowerOutPut(AXP192_LDO2, AXP202_OFF); // lora off
break; break;
default: // all rails power on case pmu_power_on:
pmu.setPowerOutPut(AXP192_LDO2, AXP202_ON); // Lora on T-Beam V1.0 default:
pmu.setPowerOutPut(AXP192_LDO3, AXP202_ON); // Gps on T-Beam V1.0 pmu.setPowerOutPut(AXP192_LDO2, AXP202_ON); // Lora on T-Beam V1.0/1.1
pmu.setPowerOutPut(AXP192_DCDC1, AXP202_ON); // OLED on T-Beam v1.0 pmu.setPowerOutPut(AXP192_LDO3, AXP202_ON); // Gps on T-Beam V1.0/1.1
pmu.setPowerOutPut(AXP192_DCDC1, AXP202_ON); // OLED on T-Beam v1.0/1.1
pmu.setPowerOutPut(AXP192_DCDC2, AXP202_OFF); // unused on T-Beam v1.0/1.1
pmu.setPowerOutPut(AXP192_EXTEN, AXP202_OFF); // unused on T-Beam v1.0/1.1
#ifdef PMU_LED_RUN_MODE
pmu.setChgLEDMode(PMU_LED_RUN_MODE);
#else
pmu.setChgLEDMode(AXP20X_LED_LOW_LEVEL); pmu.setChgLEDMode(AXP20X_LED_LOW_LEVEL);
#endif
break; break;
} }
} }
@ -119,10 +124,22 @@ void AXP192_init(void) {
ESP_LOGI(TAG, "AXP192 PMU initialization failed"); ESP_LOGI(TAG, "AXP192 PMU initialization failed");
else { else {
// configure AXP192 // configure voltages
pmu.setDCDC1Voltage(3300); // for external OLED display pmu.setDCDC1Voltage(3300); // for external OLED display
pmu.setTimeOutShutdown(false); // no automatic shutdown pmu.setLDO2Voltage(3300); // LORA VDD 3v3
pmu.setTSmode(AXP_TS_PIN_MODE_DISABLE); // TS pin mode off to save power pmu.setLDO3Voltage(3300); // GPS VDD 3v3
// configure PEK button settings
pmu.setTimeOutShutdown(false); // forced shutdown by PEK enabled
pmu.setShutdownTime(
AXP_POWER_OFF_TIME_65); // 6 sec button press for shutdown
pmu.setlongPressTime(
AXP_LONGPRESS_TIME_1S5); // 1.5 sec button press for long press
pmu.setStartupTime(
AXP192_STARTUP_TIME_1S); // 1 sec button press for startup
// set battery temperature sensing pin off to save power
pmu.setTSmode(AXP_TS_PIN_MODE_DISABLE);
// switch ADCs on // switch ADCs on
pmu.adc1Enable(AXP202_BATT_VOL_ADC1, true); pmu.adc1Enable(AXP202_BATT_VOL_ADC1, true);
@ -130,15 +147,12 @@ void AXP192_init(void) {
pmu.adc1Enable(AXP202_VBUS_VOL_ADC1, true); pmu.adc1Enable(AXP202_VBUS_VOL_ADC1, true);
pmu.adc1Enable(AXP202_VBUS_CUR_ADC1, true); pmu.adc1Enable(AXP202_VBUS_CUR_ADC1, true);
// switch power rails on
AXP192_power(pmu_power_on);
#ifdef PMU_INT #ifdef PMU_INT
pinMode(PMU_INT, INPUT_PULLUP); pinMode(PMU_INT, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(PMU_INT), PMUIRQ, FALLING); attachInterrupt(digitalPinToInterrupt(PMU_INT), PMUIRQ, FALLING);
pmu.enableIRQ(AXP202_VBUS_REMOVED_IRQ | AXP202_VBUS_CONNECT_IRQ | pmu.enableIRQ(AXP202_VBUS_REMOVED_IRQ | AXP202_VBUS_CONNECT_IRQ |
AXP202_BATT_REMOVED_IRQ | AXP202_BATT_CONNECT_IRQ | AXP202_BATT_REMOVED_IRQ | AXP202_BATT_CONNECT_IRQ |
AXP202_CHARGING_FINISHED_IRQ, AXP202_CHARGING_FINISHED_IRQ | AXP202_PEK_SHORTPRESS_IRQ,
1); 1);
pmu.clearIRQ(); pmu.clearIRQ();
#endif // PMU_INT #endif // PMU_INT
@ -150,6 +164,9 @@ void AXP192_init(void) {
pmu.enableChargeing(true); pmu.enableChargeing(true);
#endif #endif
// switch power rails on
AXP192_power(pmu_power_on);
ESP_LOGI(TAG, "AXP192 PMU initialized"); ESP_LOGI(TAG, "AXP192 PMU initialized");
} }
} }
@ -160,11 +177,11 @@ void calibrate_voltage(void) {
#ifdef BAT_MEASURE_ADC #ifdef BAT_MEASURE_ADC
// configure ADC // configure ADC
#ifndef BAT_MEASURE_ADC_UNIT // ADC1 #ifndef BAT_MEASURE_ADC_UNIT // ADC1
ESP_ERROR_CHECK(adc1_config_width(ADC_WIDTH_BIT_12)); adc1_config_width(ADC_WIDTH_BIT_12);
ESP_ERROR_CHECK(adc1_config_channel_atten(adc_channel, atten)); adc1_config_channel_atten(adc_channel, atten);
#else // ADC2 #else // ADC2
// ESP_ERROR_CHECK(adc2_config_width(ADC_WIDTH_BIT_12)); // adc2_config_width(ADC_WIDTH_BIT_12);
ESP_ERROR_CHECK(adc2_config_channel_atten(adc_channel, atten)); adc2_config_channel_atten(adc_channel, atten);
#endif #endif
// calibrate ADC // calibrate ADC
esp_adc_cal_value_t val_type = esp_adc_cal_characterize( esp_adc_cal_value_t val_type = esp_adc_cal_characterize(
@ -199,7 +216,7 @@ uint16_t read_voltage(void) {
#else // ADC2 #else // ADC2
int adc_buf = 0; int adc_buf = 0;
for (int i = 0; i < NO_OF_SAMPLES; i++) { for (int i = 0; i < NO_OF_SAMPLES; i++) {
ESP_ERROR_CHECK(adc2_get_raw(adc_channel, ADC_WIDTH_BIT_12, &adc_buf)); adc2_get_raw(adc_channel, ADC_WIDTH_BIT_12, &adc_buf);
adc_reading += adc_buf; adc_reading += adc_buf;
} }
#endif // BAT_MEASURE_ADC_UNIT #endif // BAT_MEASURE_ADC_UNIT
@ -234,10 +251,11 @@ uint8_t read_battlevel(mapFn_t mapFunction) {
bool batt_sufficient() { bool batt_sufficient() {
#if (defined HAS_PMU || defined BAT_MEASURE_ADC || defined HAS_IP5306) #if (defined HAS_PMU || defined BAT_MEASURE_ADC || defined HAS_IP5306)
return (batt_level > OTA_MIN_BATT); if (batt_level) // we have a battery voltage
#else return (batt_level > OTA_MIN_BATT);
return true; // we don't know batt level else
#endif #endif
return true; // we don't know batt level
} }
#ifdef HAS_IP5306 #ifdef HAS_IP5306

View File

@ -5,6 +5,9 @@
// Local logging tag // Local logging tag
static const char TAG[] = __FILE__; static const char TAG[] = __FILE__;
static QueueHandle_t RcmdQueue;
TaskHandle_t rcmdTask;
// set of functions that can be triggered by remote commands // set of functions that can be triggered by remote commands
void set_reset(uint8_t val[]) { void set_reset(uint8_t val[]) {
switch (val[0]) { switch (val[0]) {
@ -15,7 +18,6 @@ void set_reset(uint8_t val[]) {
case 1: // reset MAC counter case 1: // reset MAC counter
ESP_LOGI(TAG, "Remote command: reset MAC counter"); ESP_LOGI(TAG, "Remote command: reset MAC counter");
reset_counters(); // clear macs reset_counters(); // clear macs
get_salt(); // get new salt
break; break;
case 2: // reset device to factory settings case 2: // reset device to factory settings
ESP_LOGI(TAG, "Remote command: reset device to factory settings"); ESP_LOGI(TAG, "Remote command: reset device to factory settings");
@ -32,7 +34,11 @@ void set_reset(uint8_t val[]) {
case 9: // reset and ask for software update via Wifi OTA case 9: // reset and ask for software update via Wifi OTA
ESP_LOGI(TAG, "Remote command: software update via Wifi"); ESP_LOGI(TAG, "Remote command: software update via Wifi");
#if (USE_OTA) #if (USE_OTA)
RTC_runmode = RUNMODE_UPDATE; // check power status before scheduling ota update
if (batt_sufficient())
RTC_runmode = RUNMODE_UPDATE;
else
ESP_LOGE(TAG, "Battery level %d%% is too low for OTA", batt_level);
#endif // USE_OTA #endif // USE_OTA
break; break;
@ -54,14 +60,30 @@ void set_sendcycle(uint8_t val[]) {
cfg.sendcycle * 2); cfg.sendcycle * 2);
} }
void set_sleepcycle(uint8_t val[]) {
cfg.sleepcycle = val[0];
ESP_LOGI(TAG, "Remote command: set sleep cycle to %d seconds",
cfg.sleepcycle * 2);
}
void set_wifichancycle(uint8_t val[]) { void set_wifichancycle(uint8_t val[]) {
cfg.wifichancycle = val[0]; cfg.wifichancycle = val[0];
// update Wifi channel rotation timer period // update Wifi channel rotation timer period
xTimerChangePeriod(WifiChanTimer, pdMS_TO_TICKS(cfg.wifichancycle * 10), 100); if (cfg.wifichancycle > 0) {
if (xTimerIsTimerActive(WifiChanTimer) == pdFALSE)
ESP_LOGI(TAG, xTimerStart(WifiChanTimer, (TickType_t)0);
"Remote command: set Wifi channel switch interval to %.1f seconds", xTimerChangePeriod(WifiChanTimer, pdMS_TO_TICKS(cfg.wifichancycle * 10),
cfg.wifichancycle / float(100)); 100);
ESP_LOGI(
TAG,
"Remote command: set Wifi channel hopping interval to %.1f seconds",
cfg.wifichancycle / float(100));
} else {
xTimerStop(WifiChanTimer, (TickType_t)0);
esp_wifi_set_channel(WIFI_CHANNEL_MIN, WIFI_SECOND_CHAN_NONE);
channel = WIFI_CHANNEL_MIN;
ESP_LOGI(TAG, "Remote command: set Wifi channel hopping to off");
}
} }
void set_blescantime(uint8_t val[]) { void set_blescantime(uint8_t val[]) {
@ -96,7 +118,6 @@ void set_countmode(uint8_t val[]) {
return; return;
} }
reset_counters(); // clear macs reset_counters(); // clear macs
get_salt(); // get new salt
} }
void set_screensaver(uint8_t val[]) { void set_screensaver(uint8_t val[]) {
@ -215,18 +236,18 @@ void set_loraadr(uint8_t val[]) {
void set_blescan(uint8_t val[]) { void set_blescan(uint8_t val[]) {
ESP_LOGI(TAG, "Remote command: set BLE scanner to %s", val[0] ? "on" : "off"); ESP_LOGI(TAG, "Remote command: set BLE scanner to %s", val[0] ? "on" : "off");
macs_ble = 0; // clear BLE counter
cfg.blescan = val[0] ? 1 : 0; cfg.blescan = val[0] ? 1 : 0;
if (cfg.blescan) if (cfg.blescan)
start_BLEscan(); start_BLEscan();
else { else
macs_ble = 0; // clear BLE counter
stop_BLEscan(); stop_BLEscan();
}
} }
void set_wifiscan(uint8_t val[]) { void set_wifiscan(uint8_t val[]) {
ESP_LOGI(TAG, "Remote command: set WIFI scanner to %s", ESP_LOGI(TAG, "Remote command: set WIFI scanner to %s",
val[0] ? "on" : "off"); val[0] ? "on" : "off");
macs_wifi = 0; // clear WIFI counter
cfg.wifiscan = val[0] ? 1 : 0; cfg.wifiscan = val[0] ? 1 : 0;
switch_wifi_sniffer(cfg.wifiscan); switch_wifi_sniffer(cfg.wifiscan);
} }
@ -240,15 +261,15 @@ void set_wifiant(uint8_t val[]) {
#endif #endif
} }
void set_vendorfilter(uint8_t val[]) { void set_macfilter(uint8_t val[]) {
ESP_LOGI(TAG, "Remote command: set vendorfilter mode to %s", ESP_LOGI(TAG, "Remote command: set macfilter mode to %s",
val[0] ? "on" : "off"); val[0] ? "on" : "off");
cfg.vendorfilter = val[0] ? 1 : 0; cfg.macfilter = val[0] ? 1 : 0;
} }
void set_rgblum(uint8_t val[]) { void set_rgblum(uint8_t val[]) {
// Avoid wrong parameters // Avoid wrong parameters
cfg.rgblum = (val[0] >= 0 && val[0] <= 100) ? (uint8_t)val[0] : RGBLUMINOSITY; cfg.rgblum = (val[0] <= 100) ? (uint8_t)val[0] : RGBLUMINOSITY;
ESP_LOGI(TAG, "Remote command: set RGB Led luminosity %d", cfg.rgblum); ESP_LOGI(TAG, "Remote command: set RGB Led luminosity %d", cfg.rgblum);
}; };
@ -273,16 +294,16 @@ void get_config(uint8_t val[]) {
ESP_LOGI(TAG, "Remote command: get device configuration"); ESP_LOGI(TAG, "Remote command: get device configuration");
payload.reset(); payload.reset();
payload.addConfig(cfg); payload.addConfig(cfg);
SendPayload(CONFIGPORT, prio_high); SendPayload(CONFIGPORT);
}; };
void get_status(uint8_t val[]) { void get_status(uint8_t val[]) {
ESP_LOGI(TAG, "Remote command: get device status"); ESP_LOGI(TAG, "Remote command: get device status");
payload.reset(); payload.reset();
payload.addStatus(read_voltage(), uptime() / 1000, temperatureRead(), payload.addStatus(read_voltage(), (uint64_t)(uptime() / 1000ULL),
getFreeRAM(), rtc_get_reset_reason(0), temperatureRead(), getFreeRAM(), rtc_get_reset_reason(0),
rtc_get_reset_reason(1)); rtc_get_reset_reason(1));
SendPayload(STATUSPORT, prio_high); SendPayload(STATUSPORT);
}; };
void get_gps(uint8_t val[]) { void get_gps(uint8_t val[]) {
@ -292,7 +313,7 @@ void get_gps(uint8_t val[]) {
gps_storelocation(&gps_status); gps_storelocation(&gps_status);
payload.reset(); payload.reset();
payload.addGPS(gps_status); payload.addGPS(gps_status);
SendPayload(GPSPORT, prio_high); SendPayload(GPSPORT);
#else #else
ESP_LOGW(TAG, "GPS function not supported"); ESP_LOGW(TAG, "GPS function not supported");
#endif #endif
@ -303,7 +324,7 @@ void get_bme(uint8_t val[]) {
#if (HAS_BME) #if (HAS_BME)
payload.reset(); payload.reset();
payload.addBME(bme_status); payload.addBME(bme_status);
SendPayload(BMEPORT, prio_high); SendPayload(BMEPORT);
#else #else
ESP_LOGW(TAG, "BME sensor not supported"); ESP_LOGW(TAG, "BME sensor not supported");
#endif #endif
@ -314,7 +335,7 @@ void get_batt(uint8_t val[]) {
#if (defined BAT_MEASURE_ADC || defined HAS_PMU) #if (defined BAT_MEASURE_ADC || defined HAS_PMU)
payload.reset(); payload.reset();
payload.addVoltage(read_voltage()); payload.addVoltage(read_voltage());
SendPayload(BATTPORT, prio_normal); SendPayload(BATTPORT);
#else #else
ESP_LOGW(TAG, "Battery voltage not supported"); ESP_LOGW(TAG, "Battery voltage not supported");
#endif #endif
@ -325,7 +346,7 @@ void get_time(uint8_t val[]) {
payload.reset(); payload.reset();
payload.addTime(now()); payload.addTime(now());
payload.addByte(timeStatus() << 4 | timeSource); payload.addByte(timeStatus() << 4 | timeSource);
SendPayload(TIMEPORT, prio_high); SendPayload(TIMEPORT);
}; };
void set_time(uint8_t val[]) { void set_time(uint8_t val[]) {
@ -348,39 +369,49 @@ void set_enscount(uint8_t val[]) {
cfg.payloadmask &= ~SENSOR1_DATA; cfg.payloadmask &= ~SENSOR1_DATA;
} }
void set_loadconfig(uint8_t val[]) {
ESP_LOGI(TAG, "Remote command: load config from NVRAM");
loadConfig();
};
void set_saveconfig(uint8_t val[]) {
ESP_LOGI(TAG, "Remote command: save config to NVRAM");
saveConfig(false);
};
// 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, number of function arguments}
// flag (true = do make settings persistent / false = don't)
//
static const cmd_t table[] = { static const cmd_t table[] = {
{0x01, set_rssi, 1, true}, {0x02, set_countmode, 1, true}, {0x01, set_rssi, 1}, {0x02, set_countmode, 1},
{0x03, set_gps, 1, true}, {0x04, set_display, 1, true}, {0x03, set_gps, 1}, {0x04, set_display, 1},
{0x05, set_loradr, 1, true}, {0x06, set_lorapower, 1, true}, {0x05, set_loradr, 1}, {0x06, set_lorapower, 1},
{0x07, set_loraadr, 1, true}, {0x08, set_screensaver, 1, true}, {0x07, set_loraadr, 1}, {0x08, set_screensaver, 1},
{0x09, set_reset, 1, false}, {0x0a, set_sendcycle, 1, true}, {0x09, set_reset, 1}, {0x0a, set_sendcycle, 1},
{0x0b, set_wifichancycle, 1, true}, {0x0c, set_blescantime, 1, true}, {0x0b, set_wifichancycle, 1}, {0x0c, set_blescantime, 1},
{0x0d, set_vendorfilter, 1, false}, {0x0e, set_blescan, 1, true}, {0x0d, set_macfilter, 1}, {0x0e, set_blescan, 1},
{0x0f, set_wifiant, 1, true}, {0x10, set_rgblum, 1, true}, {0x0f, set_wifiant, 1}, {0x10, set_rgblum, 1},
{0x11, set_monitor, 1, true}, {0x12, set_beacon, 7, false}, {0x11, set_monitor, 1}, {0x12, set_beacon, 7},
{0x13, set_sensor, 2, true}, {0x14, set_payloadmask, 1, true}, {0x13, set_sensor, 2}, {0x14, set_payloadmask, 1},
{0x15, set_bme, 1, true}, {0x16, set_batt, 1, true}, {0x15, set_bme, 1}, {0x16, set_batt, 1},
{0x17, set_wifiscan, 1, true}, {0x18, set_enscount, 1, true}, {0x17, set_wifiscan, 1}, {0x18, set_enscount, 1},
{0x80, get_config, 0, false}, {0x81, get_status, 0, false}, {0x19, set_sleepcycle, 1}, {0x20, set_loadconfig, 0},
{0x83, get_batt, 0, false}, {0x84, get_gps, 0, false}, {0x21, set_saveconfig, 0}, {0x80, get_config, 0},
{0x85, get_bme, 0, false}, {0x86, get_time, 0, false}, {0x81, get_status, 0}, {0x83, get_batt, 0},
{0x87, set_time, 0, false}, {0x99, set_flush, 0, false}}; {0x84, get_gps, 0}, {0x85, get_bme, 0},
{0x86, get_time, 0}, {0x87, set_time, 0},
{0x99, set_flush, 0}};
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
// check and execute remote command // check and execute remote command
void rcommand(const uint8_t cmd[], const uint8_t cmdlength) { void rcmd_execute(const uint8_t cmd[], const uint8_t cmdlength) {
if (cmdlength == 0) if (cmdlength == 0)
return; return;
uint8_t foundcmd[cmdlength], cursor = 0; uint8_t foundcmd[cmdlength], cursor = 0;
bool storeflag = false;
while (cursor < cmdlength) { while (cursor < cmdlength) {
@ -392,24 +423,82 @@ void rcommand(const uint8_t cmd[], const uint8_t cmdlength) {
memmove(foundcmd, cmd + cursor, memmove(foundcmd, cmd + cursor,
table[i].params); // strip opcode from cmd array table[i].params); // strip opcode from cmd array
cursor += table[i].params; cursor += table[i].params;
if (table[i].store) // ceck if function needs to store configuration
storeflag = true;
table[i].func( table[i].func(
foundcmd); // execute assigned function with given parameters foundcmd); // execute assigned function with given parameters
} else } else
ESP_LOGI( ESP_LOGI(TAG,
TAG, "Remote command x%02X called with missing parameter(s), "
"Remote command x%02X called with missing parameter(s), skipped", "skipped",
table[i].opcode); table[i].opcode);
break; // command found -> exit table lookup loop break; // command found -> exit table lookup loop
} // end of command validation } // end of command validation
} // end of command table lookup loop } // end of command table lookup loop
if (i < 0) { // command not found -> exit parser if (i < 0) { // command not found -> exit parser
ESP_LOGI(TAG, "Unknown remote command x%02X, ignored", cmd[cursor]); ESP_LOGI(TAG, "Unknown remote command x%02X, ignored", cmd[cursor]);
break; break;
} }
} // command parsing loop } // command parsing loop
if (storeflag) } // rcmd_execute()
saveConfig();
// remote command processing task
void rcmd_process(void *pvParameters) {
_ASSERT((uint32_t)pvParameters == 1); // FreeRTOS check
RcmdBuffer_t RcmdBuffer;
while (1) {
// fetch next or wait for incoming rcommand from queue
if (xQueueReceive(RcmdQueue, &RcmdBuffer, portMAX_DELAY) != pdTRUE) {
ESP_LOGE(TAG, "Premature return from xQueueReceive() with no data!");
continue;
}
rcmd_execute(RcmdBuffer.cmd, RcmdBuffer.cmdLen);
}
delay(2); // yield to CPU
} // rcmd_process()
// enqueue remote command
void IRAM_ATTR rcommand(const uint8_t *cmd, const size_t cmdlength) {
RcmdBuffer_t rcmd = {0};
rcmd.cmdLen = cmdlength;
memcpy(rcmd.cmd, cmd, cmdlength);
if (xQueueSendToBack(RcmdQueue, (void *)&rcmd, (TickType_t)0) != pdTRUE)
ESP_LOGW(TAG, "Remote command queue is full");
} // rcommand() } // rcommand()
void rcmd_queuereset(void) { xQueueReset(RcmdQueue); }
uint32_t rcmd_queuewaiting(void) { return uxQueueMessagesWaiting(RcmdQueue); }
void rcmd_deinit(void) {
rcmd_queuereset();
vTaskDelete(rcmdTask);
}
esp_err_t rcmd_init(void) {
_ASSERT(RCMD_QUEUE_SIZE > 0);
RcmdQueue = xQueueCreate(RCMD_QUEUE_SIZE, sizeof(RcmdBuffer_t));
if (RcmdQueue == 0) {
ESP_LOGE(TAG, "Could not create rcommand send queue. Aborting.");
return ESP_FAIL;
}
ESP_LOGI(TAG, "Rcommand send queue created, size %d Bytes",
RCMD_QUEUE_SIZE * sizeof(RcmdBuffer_t));
xTaskCreatePinnedToCore(rcmd_process, // task function
"rcmdloop", // name of task
3072, // stack size of task
(void *)1, // parameter of the task
1, // priority of the task
&rcmdTask, // task handle
1); // CPU core
return ESP_OK;
} // rcmd_init()

View File

@ -5,28 +5,38 @@
// Local logging tag // Local logging tag
static const char TAG[] = __FILE__; static const char TAG[] = __FILE__;
// variable keep its values after restart or wakeup from sleep // Conversion factor for micro seconds to seconds
RTC_NOINIT_ATTR runmode_t RTC_runmode; #define uS_TO_S_FACTOR 1000000ULL
// variables keep its values after a wakeup from sleep
RTC_NOINIT_ATTR runmode_t RTC_runmode = RUNMODE_POWERCYCLE;
RTC_DATA_ATTR struct timeval RTC_sleep_start_time;
RTC_DATA_ATTR unsigned long long RTC_millis = 0;
timeval sleep_stop_time;
const char *runmode[5] = {"powercycle", "normal", "wakeup", "update", "sleep"};
void do_reset(bool warmstart) { void do_reset(bool warmstart) {
if (warmstart) { if (warmstart) {
// store LMIC keys and counters in RTC memory ESP_LOGI(TAG, "restarting device (warmstart)");
ESP_LOGI(TAG, "restarting device (warmstart), keeping runmode %d",
RTC_runmode);
} else { } else {
#if (HAS_LORA) #if (HAS_LORA)
if (RTC_runmode == RUNMODE_NORMAL) if (RTC_runmode == RUNMODE_NORMAL) {
LMIC_shutdown(); LMIC_shutdown();
}
#endif #endif
RTC_runmode = RUNMODE_POWERCYCLE; RTC_runmode = RUNMODE_POWERCYCLE;
ESP_LOGI(TAG, "restarting device (coldstart), set runmode %d", RTC_runmode); ESP_LOGI(TAG, "restarting device (coldstart)");
} }
esp_restart(); esp_restart();
} }
void do_after_reset(int reason) { void do_after_reset(void) {
switch (reason) { struct timeval sleep_stop_time;
uint64_t sleep_time_ms;
switch (rtc_get_reset_reason(0)) {
case POWERON_RESET: // 0x01 Vbat power on reset case POWERON_RESET: // 0x01 Vbat power on reset
case RTCWDT_BROWN_OUT_RESET: // 0x0f Reset when the vdd voltage is not case RTCWDT_BROWN_OUT_RESET: // 0x0f Reset when the vdd voltage is not
@ -39,10 +49,16 @@ void do_after_reset(int reason) {
break; break;
case DEEPSLEEP_RESET: // 0x05 Deep Sleep reset digital core case DEEPSLEEP_RESET: // 0x05 Deep Sleep reset digital core
RTC_runmode = RUNMODE_WAKEUP; // calculate time spent in deep sleep
#if (HAS_LORA) gettimeofday(&sleep_stop_time, NULL);
// to be done: restore LoRaWAN channel configuration and datarate here sleep_time_ms =
#endif (sleep_stop_time.tv_sec - RTC_sleep_start_time.tv_sec) * 1000 +
(sleep_stop_time.tv_usec - RTC_sleep_start_time.tv_usec) / 1000;
ESP_LOGI(TAG, "Time spent in deep sleep: %d ms", sleep_time_ms);
RTC_millis += sleep_time_ms; // increment system monotonic time
// set wakeup state, not if we have pending OTA update
if (RTC_runmode == RUNMODE_SLEEP)
RTC_runmode = RUNMODE_WAKEUP;
break; break;
case SW_RESET: // 0x03 Software reset digital core case SW_RESET: // 0x03 Software reset digital core
@ -61,58 +77,111 @@ void do_after_reset(int reason) {
break; break;
} }
ESP_LOGI(TAG, "Starting Software v%s, runmode %d", PROGVERSION, RTC_runmode); ESP_LOGI(TAG, "Starting Software v%s, runmode %s", PROGVERSION,
runmode[RTC_runmode]);
} }
void enter_deepsleep(const int wakeup_sec, const gpio_num_t wakeup_gpio) { void enter_deepsleep(const uint64_t wakeup_sec, gpio_num_t wakeup_gpio) {
if ((!wakeup_sec) && (!wakeup_gpio) && (RTC_runmode == RUNMODE_NORMAL)) ESP_LOGI(TAG, "Preparing to sleep...");
return;
// assure LMIC is in safe state RTC_runmode = RUNMODE_SLEEP;
#if (HAS_LORA) int i;
if (os_queryTimeCriticalJobs(ms2osticks(10000)))
return;
// to be done: save LoRaWAN channel configuration here // validate wake up pin, if we have
if (!GPIO_IS_VALID_GPIO(wakeup_gpio))
wakeup_gpio = GPIO_NUM_MAX;
// stop further enqueuing of senddata and MAC processing
sendTimer.detach();
// switch off radio and other power consuming hardware
#if (WIFICOUNTER)
switch_wifi_sniffer(0);
#endif
#if (BLECOUNTER)
stop_BLEscan();
btStop();
#endif
#if (HAS_SDS011)
sds011_sleep(void);
#endif #endif
// set up power domains // stop MAC processing
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_SLOW_MEM, ESP_PD_OPTION_ON); vTaskDelete(macProcessTask);
// set wakeup timer
if (wakeup_sec)
esp_sleep_enable_timer_wakeup(wakeup_sec * 1000000);
// set wakeup gpio
if (wakeup_gpio != NOT_A_PIN) {
rtc_gpio_isolate(wakeup_gpio);
esp_sleep_enable_ext1_wakeup(1ULL << wakeup_gpio, ESP_EXT1_WAKEUP_ALL_LOW);
}
// halt interrupts accessing i2c bus // halt interrupts accessing i2c bus
mask_user_IRQ(); mask_user_IRQ();
// switch off display // wait a while (max 100 sec) to clear send queues
ESP_LOGI(TAG, "Waiting until send queues are empty...");
for (i = 100; i > 0; i--) {
if (allQueuesEmtpy())
break;
vTaskDelay(pdMS_TO_TICKS(1000));
}
// shutdown LMIC safely, waiting max 100 sec
#if (HAS_LORA)
ESP_LOGI(TAG, "Waiting until LMIC is idle...");
for (i = 100; i > 0; i--) {
if ((LMIC.opmode & OP_TXRXPEND) ||
os_queryTimeCriticalJobs(sec2osticks(wakeup_sec)))
vTaskDelay(pdMS_TO_TICKS(1000));
else
break;
}
#endif // (HAS_LORA)
// shutdown MQTT safely
#ifdef HAS_MQTT
mqtt_deinit();
#endif
// shutdown SPI safely
#ifdef HAS_SPI
spi_deinit();
#endif
// save LMIC state to RTC RAM
#if (HAS_LORA)
SaveLMICToRTC(wakeup_sec);
#endif // (HAS_LORA)
// set display to power save mode
#ifdef HAS_DISPLAY #ifdef HAS_DISPLAY
dp_shutdown(); dp_shutdown();
#endif #endif
// switch off wifi & ble // reduce power if has PMU or VEXT
#if (BLECOUNTER)
stop_BLEscan();
#endif
// reduce power if has PMU
#ifdef HAS_PMU #ifdef HAS_PMU
AXP192_power(pmu_power_sleep); AXP192_power(pmu_power_sleep);
#elif EXT_POWER_SW
digitalWrite(EXT_POWER_SW, EXT_POWER_OFF);
#endif #endif
// shutdown i2c bus // shutdown i2c bus
i2c_deinit(); i2c_deinit();
// enter sleep mode // configure wakeup sources
ESP_LOGI(TAG, "Going to sleep..."); // https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/system/sleep_modes.html
// set up RTC wakeup timer, if we have
if (wakeup_sec > 0) {
esp_sleep_enable_timer_wakeup(wakeup_sec * uS_TO_S_FACTOR);
}
// set wakeup gpio, if we have
if (wakeup_gpio != GPIO_NUM_MAX) {
rtc_gpio_isolate(wakeup_gpio); // minimize deep sleep current
esp_sleep_enable_ext1_wakeup(1ULL << wakeup_gpio, ESP_EXT1_WAKEUP_ALL_LOW);
}
// time stamp sleep start time and save system monotonic time. Deep sleep.
gettimeofday(&RTC_sleep_start_time, NULL);
RTC_millis += millis();
ESP_LOGI(TAG, "Going to sleep, good bye.");
esp_deep_sleep_start(); esp_deep_sleep_start();
} }
unsigned long long uptime() { return (RTC_millis + millis()); }

View File

@ -4,17 +4,17 @@
Ticker sendTimer; Ticker sendTimer;
void setSendIRQ() { void setSendIRQ() {
xTaskNotifyFromISR(irqHandlerTask, SENDCYCLE_IRQ, eSetBits, NULL); xTaskNotify(irqHandlerTask, SENDCYCLE_IRQ, eSetBits);
} }
// put data to send in RTos Queues used for transmit over channels Lora and SPI // put data to send in RTos Queues used for transmit over channels Lora and SPI
void SendPayload(uint8_t port, sendprio_t prio) { void SendPayload(uint8_t port) {
MessageBuffer_t ESP_LOGD(TAG, "sending Payload for Port %d", port);
SendBuffer; // contains MessageSize, MessagePort, MessagePrio, Message[]
MessageBuffer_t SendBuffer; // contains MessageSize, MessagePort, Message[]
SendBuffer.MessageSize = payload.getSize(); SendBuffer.MessageSize = payload.getSize();
SendBuffer.MessagePrio = prio;
switch (PAYLOAD_ENCODER) { switch (PAYLOAD_ENCODER) {
case 1: // plain -> no mapping case 1: // plain -> no mapping
@ -110,11 +110,10 @@ void sendData() {
sds011_store(&sds_status); sds011_store(&sds_status);
payload.addSDS(sds_status); payload.addSDS(sds_status);
#endif #endif
SendPayload(COUNTERPORT, prio_normal); SendPayload(COUNTERPORT);
// clear counter if not in cumulative counter mode // clear counter if not in cumulative counter mode
if (cfg.countermode != 1) { if (cfg.countermode != 1) {
reset_counters(); // clear macs container and reset all counters reset_counters(); // clear macs container and reset all counters
get_salt(); // get new salt for salting hashes
ESP_LOGI(TAG, "Counter cleared"); ESP_LOGI(TAG, "Counter cleared");
} }
#ifdef HAS_DISPLAY #ifdef HAS_DISPLAY
@ -128,7 +127,7 @@ void sendData() {
case MEMS_DATA: case MEMS_DATA:
payload.reset(); payload.reset();
payload.addBME(bme_status); payload.addBME(bme_status);
SendPayload(BMEPORT, prio_normal); SendPayload(BMEPORT);
break; break;
#endif #endif
@ -140,7 +139,7 @@ void sendData() {
gps_storelocation(&gps_status); gps_storelocation(&gps_status);
payload.reset(); payload.reset();
payload.addGPS(gps_status); payload.addGPS(gps_status);
SendPayload(GPSPORT, prio_high); SendPayload(GPSPORT);
} else } else
ESP_LOGD(TAG, "No valid GPS position"); ESP_LOGD(TAG, "No valid GPS position");
} }
@ -152,7 +151,7 @@ void sendData() {
case SENSOR1_DATA: case SENSOR1_DATA:
payload.reset(); payload.reset();
payload.addSensor(sensor_read(1)); payload.addSensor(sensor_read(1));
SendPayload(SENSOR1PORT, prio_normal); SendPayload(SENSOR1PORT);
#if (COUNT_ENS) #if (COUNT_ENS)
if (cfg.countermode != 1) if (cfg.countermode != 1)
cwa_clear(); cwa_clear();
@ -163,14 +162,14 @@ void sendData() {
case SENSOR2_DATA: case SENSOR2_DATA:
payload.reset(); payload.reset();
payload.addSensor(sensor_read(2)); payload.addSensor(sensor_read(2));
SendPayload(SENSOR2PORT, prio_normal); SendPayload(SENSOR2PORT);
break; break;
#endif #endif
#if (HAS_SENSOR_3) #if (HAS_SENSOR_3)
case SENSOR3_DATA: case SENSOR3_DATA:
payload.reset(); payload.reset();
payload.addSensor(sensor_read(3)); payload.addSensor(sensor_read(3));
SendPayload(SENSOR3PORT, prio_normal); SendPayload(SENSOR3PORT);
break; break;
#endif #endif
#endif #endif
@ -179,7 +178,7 @@ void sendData() {
case BATT_DATA: case BATT_DATA:
payload.reset(); payload.reset();
payload.addVoltage(read_voltage()); payload.addVoltage(read_voltage());
SendPayload(BATTPORT, prio_normal); SendPayload(BATTPORT);
break; break;
#endif #endif
@ -187,10 +186,10 @@ void sendData() {
bitmask &= ~mask; bitmask &= ~mask;
mask <<= 1; mask <<= 1;
} // while (bitmask) } // while (bitmask)
} // sendData() } // sendData()
void flushQueues() { void flushQueues(void) {
rcmd_queuereset();
#if (HAS_LORA) #if (HAS_LORA)
lora_queuereset(); lora_queuereset();
#endif #endif
@ -201,3 +200,17 @@ void flushQueues() {
mqtt_queuereset(); mqtt_queuereset();
#endif #endif
} }
bool allQueuesEmtpy(void) {
uint32_t rc = rcmd_queuewaiting();
#if (HAS_LORA)
rc += lora_queuewaiting();
#endif
#ifdef HAS_SPI
rc += spi_queuewaiting();
#endif
#ifdef HAS_MQTT
rc += mqtt_queuewaiting();
#endif
return (rc == 0) ? true : false;
}

View File

@ -43,7 +43,7 @@ static const char TAG[] = __FILE__;
DMA_ATTR uint8_t txbuf[BUFFER_SIZE]; DMA_ATTR uint8_t txbuf[BUFFER_SIZE];
DMA_ATTR uint8_t rxbuf[BUFFER_SIZE]; DMA_ATTR uint8_t rxbuf[BUFFER_SIZE];
QueueHandle_t SPISendQueue; static QueueHandle_t SPISendQueue;
TaskHandle_t spiTask; TaskHandle_t spiTask;
@ -57,7 +57,8 @@ void spi_slave_task(void *param) {
memset(rxbuf, 0, sizeof(rxbuf)); memset(rxbuf, 0, sizeof(rxbuf));
// fetch next or wait for payload to send from queue // fetch next or wait for payload to send from queue
if (xQueueReceive(SPISendQueue, &msg, portMAX_DELAY) != pdTRUE) { // do not delete item from queue until it is transmitted
if (xQueuePeek(SPISendQueue, &msg, portMAX_DELAY) != pdTRUE) {
ESP_LOGE(TAG, "Premature return from xQueueReceive() with no data!"); ESP_LOGE(TAG, "Premature return from xQueueReceive() with no data!");
continue; continue;
} }
@ -90,12 +91,14 @@ void spi_slave_task(void *param) {
// wait until spi master clocks out the data, and read results in rx buffer // wait until spi master clocks out the data, and read results in rx buffer
ESP_LOGI(TAG, "Prepared SPI transaction for %zu byte(s)", transaction_size); ESP_LOGI(TAG, "Prepared SPI transaction for %zu byte(s)", transaction_size);
ESP_LOG_BUFFER_HEXDUMP(TAG, txbuf, transaction_size, ESP_LOG_DEBUG); ESP_LOG_BUFFER_HEXDUMP(TAG, txbuf, transaction_size, ESP_LOG_DEBUG);
ESP_ERROR_CHECK_WITHOUT_ABORT( spi_slave_transmit(HSPI_HOST, &spi_transaction, portMAX_DELAY);
spi_slave_transmit(HSPI_HOST, &spi_transaction, portMAX_DELAY));
ESP_LOG_BUFFER_HEXDUMP(TAG, rxbuf, transaction_size, ESP_LOG_DEBUG); ESP_LOG_BUFFER_HEXDUMP(TAG, rxbuf, transaction_size, ESP_LOG_DEBUG);
ESP_LOGI(TAG, "Transaction finished with size %zu bits", ESP_LOGI(TAG, "Transaction finished with size %zu bits",
spi_transaction.trans_len); spi_transaction.trans_len);
// delete sent item from queue
xQueueReceive(SPISendQueue, &msg, (TickType_t)0);
// check if command was received, then call interpreter with command payload // check if command was received, then call interpreter with command payload
if ((spi_transaction.trans_len) && ((rxbuf[2]) == RCMDPORT)) { if ((spi_transaction.trans_len) && ((rxbuf[2]) == RCMDPORT)) {
rcommand(rxbuf + HEADER_SIZE, spi_transaction.trans_len - HEADER_SIZE); rcommand(rxbuf + HEADER_SIZE, spi_transaction.trans_len - HEADER_SIZE);
@ -103,7 +106,9 @@ void spi_slave_task(void *param) {
} }
} }
esp_err_t spi_init() { void spi_deinit(void) { vTaskDelete(spiTask); }
esp_err_t spi_init(void) {
_ASSERT(SEND_QUEUE_SIZE > 0); _ASSERT(SEND_QUEUE_SIZE > 0);
SPISendQueue = xQueueCreate(SEND_QUEUE_SIZE, sizeof(MessageBuffer_t)); SPISendQueue = xQueueCreate(SEND_QUEUE_SIZE, sizeof(MessageBuffer_t));
if (SPISendQueue == 0) { if (SPISendQueue == 0) {
@ -149,27 +154,12 @@ esp_err_t spi_init() {
void spi_enqueuedata(MessageBuffer_t *message) { void spi_enqueuedata(MessageBuffer_t *message) {
// enqueue message in SPI send queue // enqueue message in SPI send queue
BaseType_t ret; if (xQueueSendToBack(SPISendQueue, (void *)message, (TickType_t)0) != pdTRUE)
MessageBuffer_t DummyBuffer;
sendprio_t prio = message->MessagePrio;
switch (prio) {
case prio_high:
// clear space in queue if full, then fallthrough to normal
if (!uxQueueSpacesAvailable(SPISendQueue))
xQueueReceive(SPISendQueue, &DummyBuffer, (TickType_t)0);
case prio_normal:
ret = xQueueSendToFront(SPISendQueue, (void *)message, (TickType_t)0);
break;
case prio_low:
default:
ret = xQueueSendToBack(SPISendQueue, (void *)message, (TickType_t)0);
break;
}
if (ret != pdTRUE)
ESP_LOGW(TAG, "SPI sendqueue is full"); ESP_LOGW(TAG, "SPI sendqueue is full");
} }
void spi_queuereset(void) { xQueueReset(SPISendQueue); } void spi_queuereset(void) { xQueueReset(SPISendQueue); }
uint32_t spi_queuewaiting(void) { return uxQueueMessagesWaiting(SPISendQueue); }
#endif // HAS_SPI #endif // HAS_SPI

View File

@ -26,7 +26,7 @@ Ticker timesyncer;
void setTimeSyncIRQ() { 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,
timeSource); timeSource);
time_t t = 0; time_t t = 0;
uint16_t t_msec = 0; uint16_t t_msec = 0;

View File

@ -82,13 +82,13 @@ void IRAM_ATTR timesync_processReq(void *taskparameter) {
} }
// collect timestamp samples in timestamp array // collect timestamp samples in timestamp array
for (uint8_t i = 0; i < TIME_SYNC_SAMPLES; i++) { for (int8_t i = 0; i < TIME_SYNC_SAMPLES; i++) {
// send timesync request // send timesync request
#if (TIME_SYNC_LORASERVER) // ask 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);
#elif (TIME_SYNC_LORAWAN) // ask network (requires LoRAWAN >= 1.0.3) #elif (TIME_SYNC_LORAWAN) // ask network (requires LoRAWAN >= 1.0.3)
LMIC_requestNetworkTime(timesync_serverAnswer, &time_sync_seqNo); LMIC_requestNetworkTime(timesync_serverAnswer, &time_sync_seqNo);
// trigger to immediately get DevTimeAns from class A device // trigger to immediately get DevTimeAns from class A device
@ -148,7 +148,7 @@ 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(TIMEPORT, prio_high); SendPayload(TIMEPORT);
goto Finish; goto Finish;
Fail: Fail:

View File

@ -7,10 +7,6 @@ static const char TAG[] = "wifi";
TimerHandle_t WifiChanTimer; TimerHandle_t WifiChanTimer;
static wifi_country_t wifi_country = {WIFI_MY_COUNTRY, WIFI_CHANNEL_MIN,
WIFI_CHANNEL_MAX, 100,
WIFI_COUNTRY_POLICY_MANUAL};
typedef struct { typedef struct {
unsigned frame_ctrl : 16; unsigned frame_ctrl : 16;
unsigned duration_id : 16; unsigned duration_id : 16;
@ -35,61 +31,70 @@ IRAM_ATTR void wifi_sniffer_packet_handler(void *buff,
(wifi_ieee80211_packet_t *)ppkt->payload; (wifi_ieee80211_packet_t *)ppkt->payload;
const wifi_ieee80211_mac_hdr_t *hdr = &ipkt->hdr; const wifi_ieee80211_mac_hdr_t *hdr = &ipkt->hdr;
// process seen MAC // process seen MAC
mac_add((uint8_t *)hdr->addr2, ppkt->rx_ctrl.rssi, MAC_SNIFF_WIFI); #if MACFILTER
// we guess it's a smartphone, if U/L bit #2 of MAC is set
// bit #2 = 1 -> local mac (randomized) / bit #2 = 0 -> universal mac
if ((hdr->addr2[0] & 0b10) == 0)
return;
else
#endif
mac_add((uint8_t *)hdr->addr2, ppkt->rx_ctrl.rssi, MAC_SNIFF_WIFI);
} }
// Software-timer driven Wifi channel rotation callback function // Software-timer driven Wifi channel rotation callback function
void switchWifiChannel(TimerHandle_t xTimer) { void switchWifiChannel(TimerHandle_t xTimer) {
// static uint8_t channel = 0; // channel rotation counter
_ASSERT(xTimer != NULL);
channel = channel =
(channel % WIFI_CHANNEL_MAX) + 1; // rotate channel 1..WIFI_CHANNEL_MAX (channel % WIFI_CHANNEL_MAX) + 1; // rotate channel 1..WIFI_CHANNEL_MAX
esp_wifi_set_channel(channel, WIFI_SECOND_CHAN_NONE); esp_wifi_set_channel(channel, WIFI_SECOND_CHAN_NONE);
} }
void wifi_sniffer_init(void) { void wifi_sniffer_init(void) {
wifi_init_config_t wificfg = WIFI_INIT_CONFIG_DEFAULT();
wificfg.nvs_enable = 0; // we don't need any wifi settings from NVRAM
wificfg.wifi_task_core_id = 0; // we want wifi task running on core 0
// wifi_promiscuous_filter_t filter = { wifi_country_t wifi_country = {WIFI_MY_COUNTRY, WIFI_CHANNEL_MIN,
// .filter_mask = WIFI_PROMIS_FILTER_MASK_MGMT}; // only MGMT frames WIFI_CHANNEL_MAX, 100,
// .filter_mask = WIFI_PROMIS_FILTER_MASK_ALL}; // we use all frames WIFI_COUNTRY_POLICY_MANUAL};
wifi_promiscuous_filter_t filter = {.filter_mask = wifi_init_config_t wifi_cfg = WIFI_INIT_CONFIG_DEFAULT();
WIFI_PROMIS_FILTER_MASK_MGMT | wifi_cfg.event_handler = NULL; // we don't need a wifi event handler
WIFI_PROMIS_FILTER_MASK_DATA}; wifi_cfg.nvs_enable = 0; // we don't need any wifi settings from NVRAM
wifi_cfg.wifi_task_core_id = 0; // we want wifi task running on core 0
ESP_ERROR_CHECK(esp_wifi_init(&wificfg)); // configure Wifi with cfg wifi_promiscuous_filter_t wifi_filter = {.filter_mask =
ESP_ERROR_CHECK( WIFI_PROMIS_FILTER_MASK_MGMT |
esp_wifi_set_country(&wifi_country)); // set locales for RF and channels WIFI_PROMIS_FILTER_MASK_DATA};
ESP_ERROR_CHECK(
esp_wifi_set_storage(WIFI_STORAGE_RAM)); // we don't need NVRAM
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_NULL));
ESP_ERROR_CHECK(esp_wifi_set_ps(WIFI_PS_NONE)); // no modem power saving
ESP_ERROR_CHECK(esp_wifi_set_promiscuous_filter(&filter)); // set frame filter
ESP_ERROR_CHECK(esp_wifi_set_promiscuous_rx_cb(&wifi_sniffer_packet_handler));
ESP_ERROR_CHECK(esp_wifi_set_promiscuous(true)); // now switch on monitor mode
// setup wifi channel rotation timer esp_wifi_init(&wifi_cfg); // start Wifi task
esp_wifi_set_country(&wifi_country); // set locales for RF and channels
esp_wifi_set_storage(WIFI_STORAGE_RAM);
esp_wifi_set_mode(WIFI_MODE_NULL);
esp_wifi_set_ps(WIFI_PS_NONE); // no modem power saving
esp_wifi_set_promiscuous_filter(&wifi_filter); // set frame filter
esp_wifi_set_promiscuous_rx_cb(&wifi_sniffer_packet_handler);
// setup wifi channel hopping timer
WifiChanTimer = WifiChanTimer =
xTimerCreate("WifiChannelTimer", pdMS_TO_TICKS(cfg.wifichancycle * 10), xTimerCreate("WifiChannelTimer",
(cfg.wifichancycle > 0) ? pdMS_TO_TICKS(cfg.wifichancycle)
: pdMS_TO_TICKS(50),
pdTRUE, (void *)0, switchWifiChannel); pdTRUE, (void *)0, switchWifiChannel);
} }
void switch_wifi_sniffer(uint8_t state) { void switch_wifi_sniffer(uint8_t state) {
_ASSERT(WifiChanTimer != NULL);
if (state) { if (state) {
// switch wifi sniffer on // start sniffer
ESP_ERROR_CHECK(esp_wifi_start()); esp_wifi_start();
xTimerStart(WifiChanTimer, 0);
esp_wifi_set_promiscuous(true); esp_wifi_set_promiscuous(true);
esp_wifi_set_channel(WIFI_CHANNEL_MIN, WIFI_SECOND_CHAN_NONE);
// start channel hopping timer
if (cfg.wifichancycle > 0)
xTimerStart(WifiChanTimer, (TickType_t)0);
} else { } else {
// switch wifi sniffer off // start channel hopping timer
xTimerStop(WifiChanTimer, 0); if (xTimerIsTimerActive(WifiChanTimer) != pdFALSE)
xTimerStop(WifiChanTimer, (TickType_t)0);
// stop sniffer
esp_wifi_set_promiscuous(false); esp_wifi_set_promiscuous(false);
ESP_ERROR_CHECK(esp_wifi_stop()); esp_wifi_stop();
macs_wifi = 0; // clear WIFI counter
} }
} }