Merge branch 'master' of github.com:cyberman54/ESP32-Paxcounter
This commit is contained in:
commit
e12716c146
69
README.md
69
README.md
@ -13,18 +13,18 @@ Tutorial (in german language): https://www.heise.de/select/make/2019/1/155109923
|
||||
<img src="img/TTGO-curves.jpg">
|
||||
<img src="img/Paxcounter-LEDmatrix.jpg">
|
||||
<img src="img/Paxcounter-Clock.png">
|
||||
<img src="img/Paxcounter-ttgo-twristband.jpg">
|
||||
|
||||
|
||||
# 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.
|
||||
|
||||
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.
|
||||
|
||||
This can all be done with a single small and cheap ESP32 board for less than $20.
|
||||
You can build this project battery powered using ESP32 deep sleep mode and reach long uptimes with a single 18650 Li-Ion cell.
|
||||
|
||||
# Hardware
|
||||
|
||||
@ -48,14 +48,18 @@ LoLin32lite + [LoraNode32-Lite shield](https://github.com/hallard/LoLin32-Lite-L
|
||||
- Pyom: WiPy
|
||||
- WeMos: LoLin32, LoLin32 Lite, WeMos D32, [Wemos32 Oled](https://www.instructables.com/id/ESP32-With-Integrated-OLED-WEMOSLolin-Getting-Star/)
|
||||
- Crowdsupply: [TinyPICO](https://www.crowdsupply.com/unexpected-maker/tinypico)
|
||||
- TTGO: [T-Display](https://www.aliexpress.com/item/33048962331.html)
|
||||
- TTGO: [T-Wristband](https://www.aliexpress.com/item/4000527495064.html)
|
||||
- Generic ESP32
|
||||
|
||||
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)
|
||||
- OLED Display (shows detailed status)
|
||||
- RGB LED (shows colorized status)
|
||||
- Button (short press: flip display page / long press: send alarm message)
|
||||
- Silicon unique ID
|
||||
- Battery voltage monitoring (analog read / AXP192 / IP5306)
|
||||
- GPS (Generic serial NMEA, or Quectel L76 I2C)
|
||||
- Environmental sensors (Bosch BMP180/BME280/BME680 I2C; SDS011 serial)
|
||||
@ -64,7 +68,6 @@ Depending on board hardware following features are supported:
|
||||
- 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))
|
||||
- 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>
|
||||
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>
|
||||
@ -175,7 +178,7 @@ by pressing the button of the device.
|
||||
|
||||
# Sensors and Peripherals
|
||||
|
||||
You can add up to 3 user defined sensors. Insert sensor's payload scheme in [*sensor.cpp*](src/sensor.cpp). Bosch BMP180 / BME280 / BME680 environment sensors are supported. Enable flag *lib_deps_sensors* for your board in `platformio.ini` and configure BME in board's hal file before build. If you need Bosch's proprietary BSEC libraray (e.g. to get indoor air quality value from BME680) further enable *build_flags_sensors*, which comes on the price of reduced RAM and increased build size. Furthermore, SDS011, RTC DS3231, generic serial NMEA GPS, I2C LoPy GPS are supported, and to be configured in board's hal file. See [*generic.h*](src/hal/generic.h) for all options and for proper configuration of BME280/BME680.
|
||||
You can add up to 3 user defined sensors. Insert your sensor's payload scheme in [*sensor.cpp*](src/sensor.cpp). Bosch BMP180 / BME280 / BME680 environment sensors are supported, to activate configure BME in board's hal file before build. If you need Bosch's proprietary BSEC libraray (e.g. to get indoor air quality value from BME680) further enable *build_flags_sensors*, which comes on the price of reduced RAM and increased build size. Furthermore, SDS011, RTC DS3231, generic serial NMEA GPS, I2C LoPy GPS are supported, and to be configured in board's hal file. See [*generic.h*](src/hal/generic.h) for all options and for proper configuration of BME280/BME680.
|
||||
|
||||
Output of user sensor data can be switched by user remote control command 0x14 sent to Port 2.
|
||||
|
||||
@ -183,20 +186,24 @@ Output of sensor and peripheral data is internally switched by a bitmask registe
|
||||
|
||||
| Bit | Sensordata | Default
|
||||
| --- | ------------- | -------
|
||||
| 0 | GPS* | on
|
||||
| 0 | Paxcounter | on
|
||||
| 1 | Beacon alarm | on
|
||||
| 2 | BME280/680 | on
|
||||
| 3 | Paxcounter | on
|
||||
| 3 | GPS* | on
|
||||
| 4 | User sensor 1 | on
|
||||
| 5 | User sensor 2 | on
|
||||
| 6 | User sensor 3 | on
|
||||
| 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
|
||||
|
||||
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
|
||||
|
||||
@ -211,7 +218,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`:
|
||||
|
||||
#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 WIFICOUNTER 0 // disable wifi sniffing (improves BLE scan speed)
|
||||
#define HAS_SENSOR_1 1 // optional: transmit ENS counter data to server
|
||||
@ -303,11 +310,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]
|
||||
bytes 7-8: RSSI limiter threshold value (negative) [default 0]
|
||||
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 12: Bluetooth scanner status (1=on, 0=0ff) [default 1]
|
||||
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 16: Payload filter mask
|
||||
byte 17: Beacon proximity alarm mode (1=on, 0=off) [default 0]
|
||||
@ -360,9 +367,9 @@ Hereafter described is the default *plain* format, which uses MSB bit numbering.
|
||||
|
||||
# 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.
|
||||
<img src="img/paxcounter_downlink_example.png">
|
||||
@ -423,7 +430,7 @@ Send for example `8386` as Downlink on Port 2 to get battery status and time/dat
|
||||
|
||||
useful to clear pending commands from LoRaWAN server quere, or to check RSSI on device
|
||||
|
||||
0x09 reset functions (send this command with confirmed ack only to avoid boot loops!)
|
||||
0x09 reset functions (send this command UNconfirmed only to avoid boot loops!)
|
||||
|
||||
0 = restart device (coldstart)
|
||||
1 = reset MAC counter to zero
|
||||
@ -437,17 +444,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
|
||||
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
|
||||
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
|
||||
|
||||
0 ... 255 duration for scanning a bluetooth advertising channel in seconds/100
|
||||
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)
|
||||
1 = enabled [default]
|
||||
@ -485,15 +493,15 @@ Send for example `8386` as Downlink on Port 2 to get battery status and time/dat
|
||||
0x14 set payload mask
|
||||
|
||||
byte 1 = sensor data payload mask (0..255, meaning of bits see below)
|
||||
0x01 = GPS_DATA
|
||||
0x01 = COUNT_DATA
|
||||
0x02 = ALARM_DATA
|
||||
0x04 = MEMS_DATA
|
||||
0x08 = COUNT_DATA (default)
|
||||
0x10 = SENSOR_1_DATA (ENS-COUNTS)
|
||||
0x08 = GPS_DATA
|
||||
0x10 = SENSOR_1_DATA (also ENS counter)
|
||||
0x20 = SENSOR_2_DATA
|
||||
0x40 = SENSOR_3_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
|
||||
|
||||
@ -515,6 +523,19 @@ Send for example `8386` as Downlink on Port 2 to get battery status and time/dat
|
||||
0 = disabled [default]
|
||||
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
|
||||
|
||||
Device answers with it's current configuration on Port 3.
|
||||
|
BIN
img/Paxcounter-ttgo-tdisplay.jpg
Normal file
BIN
img/Paxcounter-ttgo-tdisplay.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 292 KiB |
BIN
img/Paxcounter-ttgo-twristband.jpg
Normal file
BIN
img/Paxcounter-ttgo-twristband.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 229 KiB |
@ -21,43 +21,29 @@ extern Ticker bmecycler;
|
||||
extern bmeStatus_t
|
||||
bme_status; // Make struct for storing gps data globally available
|
||||
|
||||
// --- Bosch BSEC v1.4.7.4 library configuration ---
|
||||
// 3,3V supply voltage; 3s max time between sensor_control calls; 4 days
|
||||
// --- Bosch BSEC library configuration ---
|
||||
// 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
|
||||
// BME680 datasheet)
|
||||
// Note: 3s max time not exceed BMECYCLE frequency set in paxcounter.conf!
|
||||
const uint8_t bsec_config_iaq[454] = {
|
||||
4, 7, 4, 1, 61, 0, 0, 0, 0, 0, 0, 0, 174, 1, 0,
|
||||
0, 48, 0, 1, 0, 0, 192, 168, 71, 64, 49, 119, 76, 0, 0,
|
||||
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,
|
||||
0, 128, 64, 0, 0, 32, 65, 144, 1, 0, 0, 112, 65, 0, 0,
|
||||
0, 63, 16, 0, 3, 0, 10, 215, 163, 60, 10, 215, 35, 59, 10,
|
||||
215, 35, 59, 9, 0, 5, 0, 0, 0, 0, 0, 1, 88, 0, 9,
|
||||
0, 229, 208, 34, 62, 0, 0, 0, 0, 0, 0, 0, 0, 218, 27,
|
||||
156, 62, 225, 11, 67, 64, 0, 0, 160, 64, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 94, 75, 72, 189, 93, 254, 159, 64, 66, 62, 160, 191,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 33, 31, 180, 190, 138, 176, 97,
|
||||
64, 65, 241, 99, 190, 0, 0, 0, 0, 0, 0, 0, 0, 167, 121,
|
||||
71, 61, 165, 189, 41, 192, 184, 30, 189, 64, 12, 0, 10, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 229, 0, 254, 0, 2, 1, 5, 48,
|
||||
117, 100, 0, 44, 1, 112, 23, 151, 7, 132, 3, 197, 0, 92, 4,
|
||||
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};
|
||||
// BME680 datasheet) Note: 3s max time must not exceed BMECYCLE frequency set in
|
||||
// paxcounter.conf!
|
||||
|
||||
const uint8_t bsec_config_iaq[] = {
|
||||
#include "config/generic_33v_3s_4d/bsec_iaq.txt"
|
||||
};
|
||||
|
||||
/* Configure the BSEC library with information about the sensor
|
||||
18v/33v = Voltage at Vdd. 1.8V or 3.3V
|
||||
3s/300s = BSEC operating mode, BSEC_SAMPLE_RATE_LP or BSEC_SAMPLE_RATE_ULP
|
||||
4d/28d = Operating age of the sensor in days
|
||||
generic_18v_3s_4d
|
||||
generic_18v_3s_28d
|
||||
generic_18v_300s_4d
|
||||
generic_18v_300s_28d
|
||||
generic_33v_3s_4d
|
||||
generic_33v_3s_28d
|
||||
generic_33v_300s_4d
|
||||
generic_33v_300s_28d
|
||||
*/
|
||||
|
||||
// Helper functions declarations
|
||||
int bme_init();
|
||||
|
@ -10,12 +10,12 @@
|
||||
#include "display.h"
|
||||
#include "sds011read.h"
|
||||
#include "sdcard.h"
|
||||
#include "macsniff.h"
|
||||
|
||||
extern Ticker cyclicTimer;
|
||||
|
||||
void setCyclicIRQ(void);
|
||||
void doHousekeeping(void);
|
||||
uint64_t uptime(void);
|
||||
void reset_counters(void);
|
||||
uint32_t getFreeRAM();
|
||||
|
||||
|
@ -4,6 +4,10 @@
|
||||
#include "cyclic.h"
|
||||
#include "qrcode.h"
|
||||
|
||||
#if (COUNT_ENS)
|
||||
#include "corona.h"
|
||||
#endif
|
||||
|
||||
#if (HAS_DISPLAY) == 1
|
||||
#include <OneBitDisplay.h>
|
||||
#elif (HAS_DISPLAY) == 2
|
||||
@ -24,7 +28,7 @@
|
||||
#else
|
||||
#define OLED_ADDR -1
|
||||
#endif
|
||||
#ifndef USW_HW_I2C
|
||||
#ifndef USE_HW_I2C
|
||||
#define USE_HW_I2C 1
|
||||
#endif
|
||||
#ifndef OLED_FREQUENCY
|
||||
|
@ -1,5 +1,6 @@
|
||||
#ifndef _GLOBALS_H
|
||||
#define _GLOBALS_H
|
||||
#endif
|
||||
|
||||
// The mother of all embedded development...
|
||||
#include <Arduino.h>
|
||||
@ -17,29 +18,18 @@
|
||||
#include "mallocator.h"
|
||||
#include <bsec.h>
|
||||
|
||||
// sniffing types
|
||||
#define MAC_SNIFF_WIFI 0
|
||||
#define MAC_SNIFF_BLE 1
|
||||
#define _bit(b) (1U << (b))
|
||||
#define _bitl(b) (1UL << (b))
|
||||
|
||||
// bits in payloadmask for filtering payload data
|
||||
#define GPS_DATA (0x01)
|
||||
#define ALARM_DATA (0x02)
|
||||
#define MEMS_DATA (0x04)
|
||||
#define COUNT_DATA (0x08)
|
||||
#define SENSOR1_DATA (0x10)
|
||||
#define SENSOR2_DATA (0x20)
|
||||
#define SENSOR3_DATA (0x40)
|
||||
#define BATT_DATA (0x80)
|
||||
|
||||
// bits in configmask for device runmode control
|
||||
#define GPS_MODE (0x01)
|
||||
#define ALARM_MODE (0x02)
|
||||
#define BEACON_MODE (0x04)
|
||||
#define UPDATE_MODE (0x08)
|
||||
#define FILTER_MODE (0x10)
|
||||
#define ANTENNA_MODE (0x20)
|
||||
#define BLE_MODE (0x40)
|
||||
#define SCREEN_MODE (0x80)
|
||||
#define COUNT_DATA _bit(0)
|
||||
#define ALARM_DATA _bit(1)
|
||||
#define MEMS_DATA _bit(2)
|
||||
#define GPS_DATA _bit(3)
|
||||
#define SENSOR1_DATA _bit(4)
|
||||
#define SENSOR2_DATA _bit(5)
|
||||
#define SENSOR3_DATA _bit(6)
|
||||
#define BATT_DATA _bit(7)
|
||||
|
||||
// length of display buffer for lmic event messages
|
||||
#define LMIC_EVENTMSG_LEN 17
|
||||
@ -49,14 +39,26 @@
|
||||
(xSemaphoreTake(I2Caccess, pdMS_TO_TICKS(DISPLAYREFRESH_MS)) == pdTRUE)
|
||||
#define I2C_MUTEX_UNLOCK() (xSemaphoreGive(I2Caccess))
|
||||
|
||||
enum sendprio_t { prio_low, prio_normal, prio_high };
|
||||
enum timesource_t { _gps, _rtc, _lora, _unsynced };
|
||||
// pseudo system halt function, useful to prevent writeloops to NVRAM
|
||||
#ifndef _ASSERT
|
||||
#define _ASSERT(cond) \
|
||||
if ((cond) == 0) { \
|
||||
ESP_LOGE(TAG, "FAILURE in %s:%d", __FILE__, __LINE__); \
|
||||
mask_user_IRQ(); \
|
||||
for (;;) \
|
||||
; \
|
||||
}
|
||||
|
||||
#define _seconds() millis() / 1000.0
|
||||
|
||||
enum timesource_t { _gps, _rtc, _lora, _unsynced };
|
||||
enum snifftype_t { MAC_SNIFF_WIFI, MAC_SNIFF_BLE, MAC_SNIFF_BLE_ENS };
|
||||
enum runmode_t {
|
||||
RUNMODE_POWERCYCLE = 0,
|
||||
RUNMODE_POWERCYCLE,
|
||||
RUNMODE_NORMAL,
|
||||
RUNMODE_WAKEUP,
|
||||
RUNMODE_UPDATE
|
||||
RUNMODE_UPDATE,
|
||||
RUNMODE_SLEEP
|
||||
};
|
||||
|
||||
// Struct holding devices's runtime configuration
|
||||
@ -72,12 +74,13 @@ typedef struct __attribute__((packed)) {
|
||||
uint8_t countermode; // 0=cyclic unconfirmed, 1=cumulative, 2=cyclic confirmed
|
||||
int16_t rssilimit; // threshold for rssilimiter, negative value!
|
||||
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 blescantime; // BLE scan cycle duration [seconds]
|
||||
uint8_t blescan; // 0=disabled, 1=enabled
|
||||
uint8_t wifiscan; // 0=disabled, 1=enabled
|
||||
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 monitormode; // 0=disabled, 1=enabled
|
||||
uint8_t runmode; // 0=normal, 1=update
|
||||
@ -94,10 +97,16 @@ typedef struct __attribute__((packed)) {
|
||||
typedef struct {
|
||||
uint8_t MessageSize;
|
||||
uint8_t MessagePort;
|
||||
sendprio_t MessagePrio;
|
||||
uint8_t Message[PAYLOAD_BUFFER_SIZE];
|
||||
} MessageBuffer_t;
|
||||
|
||||
// Struct for MAC processing queue
|
||||
typedef struct {
|
||||
uint8_t mac[6];
|
||||
int8_t rssi;
|
||||
snifftype_t sniff_type;
|
||||
} MacBuffer_t;
|
||||
|
||||
typedef struct {
|
||||
int32_t latitude;
|
||||
int32_t longitude;
|
||||
@ -129,13 +138,14 @@ extern std::array<uint64_t, 0xff> beacons;
|
||||
extern configData_t cfg; // current device configuration
|
||||
extern char lmic_event_msg[LMIC_EVENTMSG_LEN]; // display buffer
|
||||
extern uint8_t volatile channel; // wifi channel rotation counter
|
||||
extern uint8_t volatile rf_load; // RF traffic indicator
|
||||
extern uint8_t batt_level; // display value
|
||||
extern uint16_t volatile macs_wifi, macs_ble; // display values
|
||||
extern bool volatile TimePulseTick; // 1sec pps flag set by GPS or RTC
|
||||
extern timesource_t timeSource;
|
||||
extern hw_timer_t *displayIRQ, *matrixDisplayIRQ, *ppsIRQ;
|
||||
extern SemaphoreHandle_t I2Caccess;
|
||||
extern TaskHandle_t irqHandlerTask, ClockTask;
|
||||
extern TaskHandle_t irqHandlerTask, ClockTask, macProcessTask;
|
||||
extern TimerHandle_t WifiChanTimer;
|
||||
extern Timezone myTZ;
|
||||
extern RTC_DATA_ATTR runmode_t RTC_runmode;
|
||||
|
@ -2,8 +2,8 @@
|
||||
#define _HASH_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
|
@ -2,6 +2,7 @@
|
||||
#define _I2C_H
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <BitBang_I2C.h>
|
||||
|
||||
#define SSD1306_PRIMARY_ADDRESS (0x3D)
|
||||
#define SSD1306_SECONDARY_ADDRESS (0x3C)
|
||||
@ -22,7 +23,7 @@
|
||||
|
||||
void i2c_init(void);
|
||||
void i2c_deinit(void);
|
||||
int i2c_scan(void);
|
||||
void i2c_scan(void);
|
||||
uint8_t i2c_readBytes(uint8_t addr, uint8_t reg, uint8_t *data, uint8_t len);
|
||||
uint8_t i2c_writeBytes(uint8_t addr, uint8_t reg, uint8_t *data, uint8_t len);
|
||||
|
||||
|
@ -1,17 +1,16 @@
|
||||
#ifndef _IRQHANDLER_H
|
||||
#define _IRQHANDLER_H
|
||||
|
||||
#define DISPLAY_IRQ 0x001
|
||||
#define BUTTON_IRQ 0x002
|
||||
#define SENDCYCLE_IRQ 0x004
|
||||
#define CYCLIC_IRQ 0x008
|
||||
#define TIMESYNC_IRQ 0x010
|
||||
#define MASK_IRQ 0x020
|
||||
#define UNMASK_IRQ 0x040
|
||||
#define BME_IRQ 0x080
|
||||
#define MATRIX_DISPLAY_IRQ 0x100
|
||||
#define PMU_IRQ 0x200
|
||||
#define MQTT_IRQ 0x400
|
||||
#define DISPLAY_IRQ _bitl(0)
|
||||
#define BUTTON_IRQ _bitl(1)
|
||||
#define SENDCYCLE_IRQ _bitl(2)
|
||||
#define CYCLIC_IRQ _bitl(3)
|
||||
#define TIMESYNC_IRQ _bitl(4)
|
||||
#define MASK_IRQ _bitl(5)
|
||||
#define UNMASK_IRQ _bitl(6)
|
||||
#define BME_IRQ _bitl(7)
|
||||
#define MATRIX_DISPLAY_IRQ _bitl(8)
|
||||
#define PMU_IRQ _bitl(9)
|
||||
|
||||
#include "globals.h"
|
||||
#include "button.h"
|
||||
@ -21,7 +20,6 @@
|
||||
#include "bmesensor.h"
|
||||
#include "power.h"
|
||||
#include "ledmatrixdisplay.h"
|
||||
#include "mqttclient.h"
|
||||
|
||||
void irqHandler(void *pvParameters);
|
||||
void mask_user_IRQ();
|
||||
|
@ -20,9 +20,10 @@
|
||||
|
||||
extern TaskHandle_t lmicTask, lorasendTask;
|
||||
|
||||
void lora_stack_reset();
|
||||
esp_err_t lora_stack_init(bool do_join);
|
||||
esp_err_t lmic_init(void);
|
||||
void lora_setupForNetwork(bool preJoin);
|
||||
void SaveLMICToRTC(int deepsleep_sec);
|
||||
void LoadLMICFromRTC();
|
||||
void lmictask(void *pvParameters);
|
||||
void gen_lora_deveui(uint8_t *pdeveui);
|
||||
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_enqueuedata(MessageBuffer_t *message);
|
||||
void lora_queuereset(void);
|
||||
uint32_t lora_queuewaiting(void);
|
||||
uint8_t myBattLevelCb(void *pUserData);
|
||||
void IRAM_ATTR myEventCallback(void *pUserData, ev_t ev);
|
||||
void IRAM_ATTR myRxCallback(void *pUserData, uint8_t port, const uint8_t *pMsg,
|
||||
|
@ -10,13 +10,16 @@
|
||||
#include "cyclic.h"
|
||||
#include "led.h"
|
||||
|
||||
#define MAC_SNIFF_WIFI 0
|
||||
#define MAC_SNIFF_BLE 1
|
||||
#define MAC_SNIFF_BLE_CWA 2
|
||||
#if (COUNT_ENS)
|
||||
#include "corona.h"
|
||||
#endif
|
||||
|
||||
uint16_t get_salt(void);
|
||||
uint32_t renew_salt(void);
|
||||
uint64_t macConvert(uint8_t *paddr);
|
||||
uint16_t mac_add(uint8_t *paddr, int8_t rssi, bool sniff_type);
|
||||
esp_err_t macQueueInit(void);
|
||||
void mac_process(void *pvParameters);
|
||||
void IRAM_ATTR mac_add(uint8_t *paddr, int8_t rssi, snifftype_t sniff_type);
|
||||
uint16_t mac_analyze(MacBuffer_t MacBuffer);
|
||||
void printKey(const char *name, const uint8_t *key, uint8_t len, bool lsb);
|
||||
|
||||
#endif
|
||||
|
@ -21,4 +21,4 @@
|
||||
#include "timekeeper.h"
|
||||
#include "corona.h"
|
||||
|
||||
#endif
|
||||
#endif
|
@ -5,34 +5,22 @@
|
||||
#include "rcommand.h"
|
||||
#include <MQTT.h>
|
||||
#include <ETH.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
|
||||
#include <mbedtls/base64.h>
|
||||
|
||||
#ifndef MQTT_CLIENTNAME
|
||||
#define MQTT_CLIENTNAME clientId.c_str()
|
||||
#define MQTT_CLIENTNAME clientId
|
||||
#endif
|
||||
|
||||
extern TaskHandle_t mqttTask;
|
||||
|
||||
void mqtt_enqueuedata(MessageBuffer_t *message);
|
||||
uint32_t mqtt_queuewaiting(void);
|
||||
void mqtt_queuereset(void);
|
||||
void setMqttIRQ(void);
|
||||
void mqtt_loop(void);
|
||||
void mqtt_client_task(void *param);
|
||||
int mqtt_connect(const char *my_host, const uint16_t my_port);
|
||||
void mqtt_callback(MQTTClient *client, char topic[], char payload[],
|
||||
int length);
|
||||
void mqtt_callback(MQTTClient *client, char *topic, char *payload, int length);
|
||||
void NetworkEvent(WiFiEvent_t event);
|
||||
esp_err_t mqtt_init(void);
|
||||
void mqtt_deinit(void);
|
||||
|
||||
#endif // _MQTTCLIENT_H
|
@ -15,15 +15,28 @@
|
||||
#include "timesync.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
|
||||
typedef struct {
|
||||
const uint8_t opcode;
|
||||
void (*func)(uint8_t []);
|
||||
void (*func)(uint8_t[]);
|
||||
const uint8_t params;
|
||||
const bool store;
|
||||
} cmd_t;
|
||||
|
||||
void rcommand(const uint8_t cmd[], const uint8_t cmdlength);
|
||||
void do_reset(bool warmstart);
|
||||
// Struct for remote command processing queue
|
||||
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
|
||||
|
@ -10,7 +10,9 @@
|
||||
#include "power.h"
|
||||
|
||||
void do_reset(bool warmstart);
|
||||
void do_after_reset(int reason);
|
||||
void enter_deepsleep(const int wakeup_sec, const gpio_num_t wakeup_gpio);
|
||||
void do_after_reset(void);
|
||||
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
|
@ -35,6 +35,25 @@
|
||||
#define SDCARD_SCLK SCK
|
||||
#endif
|
||||
|
||||
// Default config for SDMMC_HOST_DEFAULT (4-bit bus width, slot 1)
|
||||
// https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/sdmmc_host.html
|
||||
|
||||
#ifndef SDCARD_DATA0
|
||||
#define SDCARD_DATA0 2
|
||||
#endif
|
||||
|
||||
#ifndef SDCARD_DATA1
|
||||
#define SDCARD_DATA1 4
|
||||
#endif
|
||||
|
||||
#ifndef SDCARD_DATA2
|
||||
#define SDCARD_DATA2 12
|
||||
#endif
|
||||
|
||||
#ifndef SDCARD_DATA3
|
||||
#define SDCARD_DATA3 13
|
||||
#endif
|
||||
|
||||
#define SDCARD_FILE_NAME "/paxcount.%02d"
|
||||
#define SDCARD_FILE_HEADER "date, time, wifi, bluet"
|
||||
|
||||
|
@ -8,14 +8,18 @@
|
||||
#include "lorawan.h"
|
||||
#include "display.h"
|
||||
#include "sdcard.h"
|
||||
|
||||
#if (COUNT_ENS)
|
||||
#include "corona.h"
|
||||
#endif
|
||||
|
||||
extern Ticker sendTimer;
|
||||
|
||||
void SendPayload(uint8_t port, sendprio_t prio);
|
||||
void SendPayload(uint8_t port);
|
||||
void sendData(void);
|
||||
void checkSendQueues(void);
|
||||
void flushQueues();
|
||||
void flushQueues(void);
|
||||
bool allQueuesEmtpy(void);
|
||||
void setSendIRQ(void);
|
||||
|
||||
#endif // _SENDDATA_H_
|
||||
|
@ -1,8 +1,10 @@
|
||||
#ifndef _SENSOR_H
|
||||
#define _SENSOR_H
|
||||
|
||||
#define HAS_SENSORS (HAS_SENSOR_1 || HAS_SENSOR_2 || HAS_SENSOR_3)
|
||||
|
||||
uint8_t sensor_mask(uint8_t sensor_no);
|
||||
uint8_t * sensor_read(uint8_t sensor);
|
||||
uint8_t *sensor_read(uint8_t sensor);
|
||||
void sensor_init(void);
|
||||
|
||||
#endif
|
@ -27,11 +27,12 @@ licenses. Refer to LICENSE.txt file in repository for more details.
|
||||
#include "globals.h"
|
||||
#include "rcommand.h"
|
||||
|
||||
esp_err_t spi_init();
|
||||
|
||||
extern TaskHandle_t spiTask;
|
||||
|
||||
esp_err_t spi_init();
|
||||
void spi_deinit(void);
|
||||
void spi_enqueuedata(MessageBuffer_t *message);
|
||||
void spi_queuereset();
|
||||
uint32_t spi_queuewaiting(void);
|
||||
void spi_queuereset(void);
|
||||
|
||||
#endif // _SPISLAVE_H
|
||||
|
@ -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,
|
||||
};
|
@ -8,6 +8,8 @@
|
||||
#include "antenna.h" // code for switching wifi antennas
|
||||
#include "macsniff.h"
|
||||
|
||||
extern TimerHandle_t WifiChanTimer;
|
||||
|
||||
void wifi_sniffer_init(void);
|
||||
void switch_wifi_sniffer(uint8_t state);
|
||||
void IRAM_ATTR wifi_sniffer_packet_handler(void *buff,
|
||||
|
@ -113,16 +113,17 @@ String BintrayClient::getLatestVersion() const
|
||||
ESP_LOGE(TAG, "Error: Firmware version data invalid.");
|
||||
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
|
||||
if (!root.success())
|
||||
if (err)
|
||||
{
|
||||
ESP_LOGE(TAG, "Error: Firmware version data not found.");
|
||||
return version;
|
||||
}
|
||||
return root.get<String>("name");
|
||||
return doc["name"].as<String>();
|
||||
}
|
||||
|
||||
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.");
|
||||
return path;
|
||||
}
|
||||
StaticJsonBuffer<bufferSize> jsonBuffer;
|
||||
StaticJsonDocument<bufferSize> doc;
|
||||
|
||||
JsonArray &root = jsonBuffer.parseArray(jsonResult.c_str());
|
||||
JsonObject &firstItem = root[0];
|
||||
if (!root.success())
|
||||
DeserializationError err = deserializeJson(doc, jsonResult.c_str());
|
||||
|
||||
JsonObject firstItem = doc[0];
|
||||
if (err)
|
||||
{ //Check for errors in parsing
|
||||
ESP_LOGE(TAG, "Error: Firmware download path not found.");
|
||||
return path;
|
||||
}
|
||||
return "/" + getUser() + "/" + getRepository() + "/" + firstItem.get<String>("path");
|
||||
return "/" + getUser() + "/" + getRepository() + "/" + firstItem["path"].as<String>();
|
||||
}
|
||||
|
Binary file not shown.
@ -1,5 +1,5 @@
|
||||
; PlatformIO Project Configuration File
|
||||
; NOTE: PlatformIO v4 is needed!
|
||||
; NOTE: PlatformIO v5 is needed!
|
||||
;
|
||||
; Please visit documentation for the other options and examples
|
||||
; http://docs.platformio.org/page/projectconf.html
|
||||
@ -7,7 +7,7 @@
|
||||
|
||||
; ---> SELECT THE TARGET PLATFORM HERE! <---
|
||||
[board]
|
||||
halfile = generic.h
|
||||
;halfile = generic.h
|
||||
;halfile = ebox.h
|
||||
;halfile = eboxtube.h
|
||||
;halfile = ecopower.h
|
||||
@ -19,7 +19,8 @@ halfile = generic.h
|
||||
;halfile = ttgov21new.h
|
||||
;halfile = ttgofox.h
|
||||
;halfile = ttgobeam.h
|
||||
;halfile = ttgobeam10.h
|
||||
halfile = ttgobeam10.h
|
||||
;halfile = ttgotdisplay.h
|
||||
;halfile = fipy.h
|
||||
;halfile = lopy.h
|
||||
;halfile = lopy4.h
|
||||
@ -46,7 +47,7 @@ description = Paxcounter is a device for metering passenger flows in realtime. I
|
||||
|
||||
[common]
|
||||
; for release_version use max. 10 chars total, use any decimal format like "a.b.c"
|
||||
release_version = 2.0.2
|
||||
release_version = 2.2.0
|
||||
; DEBUG LEVEL: For production run set to 0, otherwise device will leak RAM while running!
|
||||
; 0=None, 1=Error, 2=Warn, 3=Info, 4=Debug, 5=Verbose
|
||||
debug_level = 3
|
||||
@ -54,16 +55,17 @@ extra_scripts = pre:build.py
|
||||
otakeyfile = ota.conf
|
||||
lorakeyfile = loraconf.h
|
||||
lmicconfigfile = lmic_config.h
|
||||
platform_espressif32 = espressif32@2.0.0
|
||||
platform_espressif32 = espressif32@2.1.0
|
||||
monitor_speed = 115200
|
||||
upload_speed = 115200 ; set by build.py and taken from hal file
|
||||
display_library = ; set by build.py and taken from hal file
|
||||
lib_deps_lora =
|
||||
mcci-catena/MCCI LoRaWAN LMIC library @ ^3.2.0
|
||||
mcci-catena/MCCI LoRaWAN LMIC library @ ^3.3.0
|
||||
lib_deps_display =
|
||||
bitbank2/OneBitDisplay @ 1.7.2
|
||||
bitbank2/OneBitDisplay @ 1.9.0
|
||||
bitbank2/BitBang_I2C @ ^2.1.3
|
||||
ricmoo/QRCode @ ^0.0.1
|
||||
bodmer/TFT_eSPI @ ^2.2.20
|
||||
bodmer/TFT_eSPI @ ^2.3.51
|
||||
lib_deps_ledmatrix =
|
||||
seeed-studio/Ultrathin_LED_Matrix @ ^1.0.0
|
||||
lib_deps_rgbled =
|
||||
@ -74,10 +76,11 @@ lib_deps_sensors =
|
||||
adafruit/Adafruit Unified Sensor @ ^1.1.4
|
||||
adafruit/Adafruit BME280 Library @ ^2.1.1
|
||||
adafruit/Adafruit BMP085 Library @ ^1.1.0
|
||||
boschsensortec/BSEC Software Library @ 1.5.1474
|
||||
boschsensortec/BSEC Software Library @ 1.6.1480
|
||||
https://github.com/ricki-z/SDS011.git
|
||||
lib_deps_basic =
|
||||
bblanchon/ArduinoJson @ <6
|
||||
https://github.com/SukkoPera/Arduino-Rokkit-Hash.git
|
||||
bblanchon/ArduinoJson @ ^6
|
||||
jchristensen/Timezone @ ^1.2.4
|
||||
makuna/RTC @ ^2.3.5
|
||||
spacehuhn/SimpleButton
|
||||
@ -114,7 +117,7 @@ framework = arduino
|
||||
board = esp32dev
|
||||
board_build.partitions = min_spiffs.csv
|
||||
upload_speed = ${common.upload_speed}
|
||||
;upload_port = COM8
|
||||
;upload_port = COM3
|
||||
platform = ${common.platform_espressif32}
|
||||
lib_deps = ${common.lib_deps_all}
|
||||
build_flags = ${common.build_flags_all}
|
||||
@ -133,4 +136,3 @@ upload_protocol = esptool
|
||||
upload_protocol = esptool
|
||||
build_type = debug
|
||||
platform = https://github.com/platformio/platform-espressif32.git#develop
|
||||
platform_packages = framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32.git
|
||||
|
467
src/Node-RED/MQTT.json
Normal file
467
src/Node-RED/MQTT.json
Normal 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
|
||||
}
|
||||
]
|
@ -262,7 +262,7 @@ var bitmap2 = function (byte) {
|
||||
}
|
||||
var i = bytesToInt(byte);
|
||||
var bm = ('00000000' + Number(i).toString(2)).substr(-8).split('').map(Number).map(Boolean);
|
||||
return ['battery', 'sensor3', 'sensor2', 'sensor1', 'counter', 'bme', 'alarm', 'gps']
|
||||
return ['battery', 'sensor3', 'sensor2', 'sensor1', 'gps', 'bme', 'alarm', 'counter']
|
||||
.reduce(function (obj, pos, index) {
|
||||
obj[pos] = +bm[index];
|
||||
return obj;
|
||||
|
163
src/blecsan.cpp
163
src/blecsan.cpp
@ -6,14 +6,10 @@
|
||||
#define BT_BD_ADDR_HEX(addr) \
|
||||
addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]
|
||||
|
||||
// UUID of Exposure Notification Service (ENS)
|
||||
// see
|
||||
// https://blog.google/documents/70/Exposure_Notification_-_Bluetooth_Specification_v1.2.2.pdf
|
||||
static const char ensMagicBytes[] = "\x16\x6f\xfd";
|
||||
|
||||
// local Tag for logging
|
||||
static const char TAG[] = "bluetooth";
|
||||
|
||||
#ifdef VERBOSE
|
||||
const char *bt_addr_t_to_string(esp_ble_addr_type_t type) {
|
||||
switch (type) {
|
||||
case BLE_ADDR_TYPE_PUBLIC:
|
||||
@ -110,20 +106,29 @@ const char *btsig_gap_type(uint32_t gap_type) {
|
||||
return "Unknown type";
|
||||
}
|
||||
} // btsig_gap_type
|
||||
#endif
|
||||
|
||||
// using IRAM_:ATTR here to speed up callback function
|
||||
// using IRAM_ATTR here to speed up callback function
|
||||
IRAM_ATTR void gap_callback_handler(esp_gap_ble_cb_event_t event,
|
||||
esp_ble_gap_cb_param_t *param) {
|
||||
|
||||
esp_ble_gap_cb_param_t *p = (esp_ble_gap_cb_param_t *)param;
|
||||
|
||||
#if (COUNT_ENS)
|
||||
// UUID of Exposure Notification Service (ENS)
|
||||
// https://blog.google/documents/70/Exposure_Notification_-_Bluetooth_Specification_v1.2.2.pdf
|
||||
static const char ensMagicBytes[] = "\x16\x6f\xfd";
|
||||
#endif
|
||||
|
||||
#ifdef VERBOSE
|
||||
ESP_LOGV(TAG, "BT payload rcvd -> type: 0x%.2x -> %s", *p->scan_rst.ble_adv,
|
||||
btsig_gap_type(*p->scan_rst.ble_adv));
|
||||
#endif
|
||||
|
||||
switch (event) {
|
||||
case ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT:
|
||||
// restart scan
|
||||
ESP_ERROR_CHECK(esp_ble_gap_start_scanning(BLESCANTIME));
|
||||
esp_ble_gap_start_scanning(BLESCANTIME);
|
||||
break;
|
||||
|
||||
case ESP_GAP_BLE_SCAN_RESULT_EVT:
|
||||
@ -131,52 +136,47 @@ IRAM_ATTR void gap_callback_handler(esp_gap_ble_cb_event_t event,
|
||||
if (p->scan_rst.search_evt ==
|
||||
ESP_GAP_SEARCH_INQ_CMPL_EVT) // Inquiry complete, scan is done
|
||||
{ // restart scan
|
||||
ESP_ERROR_CHECK(esp_ble_gap_start_scanning(BLESCANTIME));
|
||||
esp_ble_gap_start_scanning(BLESCANTIME);
|
||||
return;
|
||||
}
|
||||
|
||||
if (p->scan_rst.search_evt ==
|
||||
ESP_GAP_SEARCH_INQ_RES_EVT) // Inquiry result for a peer device
|
||||
{ // evaluate sniffed packet
|
||||
#ifdef VERBOSE
|
||||
ESP_LOGV(TAG, "Device address (bda): %02x:%02x:%02x:%02x:%02x:%02x",
|
||||
BT_BD_ADDR_HEX(p->scan_rst.bda));
|
||||
ESP_LOGV(TAG, "Addr_type : %s",
|
||||
bt_addr_t_to_string(p->scan_rst.ble_addr_type));
|
||||
ESP_LOGV(TAG, "RSSI : %d", p->scan_rst.rssi);
|
||||
#endif
|
||||
|
||||
if ((cfg.rssilimit) &&
|
||||
(p->scan_rst.rssi < cfg.rssilimit)) { // rssi is negative value
|
||||
ESP_LOGI(TAG, "BLTH RSSI %d -> ignoring (limit: %d)", p->scan_rst.rssi,
|
||||
cfg.rssilimit);
|
||||
break;
|
||||
}
|
||||
|
||||
#if (VENDORFILTER)
|
||||
#if (MACFILTER)
|
||||
if ((p->scan_rst.ble_addr_type == BLE_ADDR_TYPE_RANDOM) ||
|
||||
(p->scan_rst.ble_addr_type == BLE_ADDR_TYPE_RPA_RANDOM)) {
|
||||
#ifdef VERBOSE
|
||||
ESP_LOGV(TAG, "BT device filtered");
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
// hash and add this device and show new count total if it was not
|
||||
// previously added
|
||||
|
||||
#if (COUNT_ENS)
|
||||
uint16_t hashedmac =
|
||||
#endif
|
||||
|
||||
mac_add((uint8_t *)p->scan_rst.bda, p->scan_rst.rssi, MAC_SNIFF_BLE);
|
||||
// add this device mac to processing queue
|
||||
|
||||
#if (COUNT_ENS)
|
||||
// check for ens signature
|
||||
if (cfg.enscount) {
|
||||
// check for ens signature
|
||||
if (NULL != strstr((const char *)p->scan_rst.ble_adv, ensMagicBytes))
|
||||
cwa_mac_add(hashedmac);
|
||||
if (strstr((const char *)p->scan_rst.ble_adv, ensMagicBytes) != NULL)
|
||||
mac_add((uint8_t *)p->scan_rst.bda, p->scan_rst.rssi,
|
||||
MAC_SNIFF_BLE_ENS);
|
||||
else
|
||||
mac_add((uint8_t *)p->scan_rst.bda, p->scan_rst.rssi, MAC_SNIFF_BLE);
|
||||
}
|
||||
#else
|
||||
mac_add((uint8_t *)p->scan_rst.bda, p->scan_rst.rssi, MAC_SNIFF_BLE);
|
||||
#endif
|
||||
|
||||
/* to be improved in vendorfilter if:
|
||||
/* to be improved in macfilter:
|
||||
// you can search for elements in the payload using the
|
||||
// function esp_ble_resolve_adv_data()
|
||||
//
|
||||
@ -186,8 +186,8 @@ IRAM_ATTR void gap_callback_handler(esp_gap_ble_cb_event_t event,
|
||||
// uint8_t *data = esp_ble_resolve_adv_data(p->scan_rst.ble_adv,
|
||||
ESP_BLE_AD_TYPE_NAME_CMPL, &len);
|
||||
|
||||
filter BLE devices using their advertisements to get filter alternative to
|
||||
vendor OUI if vendorfiltering is on, we ...
|
||||
filter BLE devices using their advertisements to get filter alternative
|
||||
to vendor OUI if macfiltering is on, we ...
|
||||
- want to count: mobile phones and tablets
|
||||
- don't want to count: beacons, peripherals (earphones, headsets,
|
||||
printers), cars and machines see
|
||||
@ -211,38 +211,56 @@ IRAM_ATTR void gap_callback_handler(esp_gap_ble_cb_event_t event,
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} // switch
|
||||
} // gap_callback_handler
|
||||
|
||||
esp_err_t register_ble_callback(void) {
|
||||
ESP_LOGI(TAG, "Register GAP callback");
|
||||
esp_err_t register_ble_callback(bool unregister = false) {
|
||||
|
||||
// This function is called to occur gap event, such as scan result.
|
||||
// register the scan callback function to the gap module
|
||||
ESP_ERROR_CHECK(esp_ble_gap_register_callback(&gap_callback_handler));
|
||||
if (unregister) {
|
||||
|
||||
static esp_ble_scan_params_t ble_scan_params = {
|
||||
.scan_type = BLE_SCAN_TYPE_PASSIVE,
|
||||
.own_addr_type = BLE_ADDR_TYPE_RANDOM,
|
||||
ESP_LOGI(TAG, "Unregister GAP callback...");
|
||||
esp_ble_gap_stop_scanning();
|
||||
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 =
|
||||
(uint16_t)(cfg.blescantime * 10 / 0.625), // Time = N * 0.625 msec
|
||||
.scan_window = (uint16_t)(BLESCANWINDOW / 0.625) // Time = N * 0.625 msec
|
||||
};
|
||||
else {
|
||||
|
||||
ESP_LOGI(TAG, "Set GAP scan parameters");
|
||||
ESP_LOGI(TAG, "Register GAP callback...");
|
||||
|
||||
// This function is called to set scan parameters.
|
||||
ESP_ERROR_CHECK(esp_ble_gap_set_scan_params(&ble_scan_params));
|
||||
// This function is called when gap event occurs, such as scan result.
|
||||
// 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;
|
||||
|
||||
@ -251,29 +269,34 @@ esp_err_t register_ble_callback(void) {
|
||||
void start_BLEscan(void) {
|
||||
#if (BLECOUNTER)
|
||||
ESP_LOGI(TAG, "Initializing bluetooth scanner ...");
|
||||
|
||||
// Initialize BT controller to allocate task and other resource.
|
||||
ESP_ERROR_CHECK(esp_coex_preference_set(ESP_COEX_PREFER_BT));
|
||||
|
||||
btStart();
|
||||
ESP_ERROR_CHECK(esp_bluedroid_init());
|
||||
ESP_ERROR_CHECK(esp_bluedroid_enable());
|
||||
|
||||
// Register callback function for capturing bluetooth packets
|
||||
ESP_ERROR_CHECK(register_ble_callback());
|
||||
|
||||
ESP_LOGI(TAG, "Bluetooth scanner started");
|
||||
if (btStart()) { // enable bt_controller
|
||||
esp_coex_preference_set(ESP_COEX_PREFER_BT);
|
||||
esp_bluedroid_init();
|
||||
esp_bluedroid_enable();
|
||||
// Register callback function for capturing bluetooth packets
|
||||
register_ble_callback(false);
|
||||
ESP_LOGI(TAG, "Bluetooth scanner started");
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Bluetooth controller start failed. Resetting device");
|
||||
do_reset(true);
|
||||
}
|
||||
#endif // BLECOUNTER
|
||||
} // start_BLEscan
|
||||
|
||||
void stop_BLEscan(void) {
|
||||
#if (BLECOUNTER)
|
||||
ESP_LOGI(TAG, "Shutting down bluetooth scanner ...");
|
||||
ESP_ERROR_CHECK(esp_ble_gap_register_callback(NULL));
|
||||
ESP_ERROR_CHECK(esp_bluedroid_disable());
|
||||
ESP_ERROR_CHECK(esp_bluedroid_deinit());
|
||||
btStop(); // disable bt_controller
|
||||
ESP_ERROR_CHECK(esp_coex_preference_set(ESP_COEX_PREFER_WIFI));
|
||||
register_ble_callback(true); // unregister capture function
|
||||
ESP_LOGD(TAG, "bluedroid disable...");
|
||||
esp_bluedroid_disable();
|
||||
ESP_LOGD(TAG, "bluedroid deinit...");
|
||||
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");
|
||||
#endif // BLECOUNTER
|
||||
} // stop_BLEscan
|
||||
|
@ -5,7 +5,7 @@
|
||||
// Local logging tag
|
||||
static const char TAG[] = __FILE__;
|
||||
|
||||
bmeStatus_t bme_status = {0};
|
||||
bmeStatus_t bme_status = {0, 0, 0, 0, 0, 0, 0, 0};
|
||||
|
||||
Ticker bmecycler;
|
||||
|
||||
@ -26,7 +26,6 @@ bsec_virtual_sensor_t sensorList[10] = {
|
||||
};
|
||||
|
||||
uint8_t bsecstate_buffer[BSEC_MAX_STATE_BLOB_SIZE] = {0};
|
||||
uint16_t stateUpdateCounter = 0;
|
||||
|
||||
Bsec iaqSensor;
|
||||
|
||||
@ -46,11 +45,11 @@ Adafruit_BMP085 bmp; // I2C
|
||||
|
||||
void setBMEIRQ() { xTaskNotify(irqHandlerTask, BME_IRQ, eSetBits); }
|
||||
|
||||
// initialize BME680 sensor
|
||||
// initialize MEMS sensor
|
||||
// return = 0 -> error / return = 1 -> success
|
||||
int bme_init(void) {
|
||||
|
||||
// return = 0 -> error / return = 1 -> success
|
||||
int rc = 1;
|
||||
int rc = 0;
|
||||
|
||||
#ifdef HAS_BME680
|
||||
// block i2c bus access
|
||||
@ -64,76 +63,33 @@ int bme_init(void) {
|
||||
iaqSensor.version.minor_bugfix);
|
||||
|
||||
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();
|
||||
|
||||
iaqSensor.setTemperatureOffset((float)BME_TEMP_OFFSET);
|
||||
iaqSensor.updateSubscription(sensorList, 10, BSEC_SAMPLE_RATE_LP);
|
||||
|
||||
if (checkIaqSensorStatus())
|
||||
ESP_LOGI(TAG, "BSEC subscription succesful");
|
||||
else {
|
||||
ESP_LOGE(TAG, "BSEC subscription error");
|
||||
rc = 0;
|
||||
goto finish;
|
||||
}
|
||||
} else {
|
||||
rc = checkIaqSensorStatus();
|
||||
|
||||
} else
|
||||
ESP_LOGE(TAG, "I2c bus busy - BME680 initialization error");
|
||||
rc = 0;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
#elif defined HAS_BME280
|
||||
|
||||
bool status;
|
||||
|
||||
// block i2c bus access
|
||||
if (I2C_MUTEX_LOCK()) {
|
||||
|
||||
status = bme.begin(BME280_ADDR);
|
||||
if (!status) {
|
||||
ESP_LOGE(TAG, "BME280 sensor not found");
|
||||
rc = 0;
|
||||
goto finish;
|
||||
}
|
||||
ESP_LOGI(TAG, "BME280 sensor found and initialized");
|
||||
} else {
|
||||
rc = bme.begin(BME280_ADDR);
|
||||
} else
|
||||
ESP_LOGE(TAG, "I2c bus busy - BME280 initialization error");
|
||||
rc = 0;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
#elif defined HAS_BMP180
|
||||
bool status;
|
||||
// block i2c bus access
|
||||
if (I2C_MUTEX_LOCK()) {
|
||||
// Wire.begin(21, 22);
|
||||
status = bmp.begin();
|
||||
if (!status) {
|
||||
ESP_LOGE(TAG, "BMP180 sensor not found");
|
||||
rc = 0;
|
||||
goto finish;
|
||||
}
|
||||
ESP_LOGI(TAG, "BMP180 sensor found and initialized");
|
||||
} else {
|
||||
rc = bmp.begin();
|
||||
} else
|
||||
ESP_LOGE(TAG, "I2c bus busy - BMP180 initialization error");
|
||||
rc = 0;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
finish:
|
||||
I2C_MUTEX_UNLOCK(); // release i2c bus access
|
||||
if (rc)
|
||||
bmecycler.attach(BMECYCLE, setBMEIRQ);
|
||||
bmecycler.attach(BMECYCLE, setBMEIRQ); // start cyclic data transmit
|
||||
return rc;
|
||||
|
||||
} // bme_init()
|
||||
@ -218,6 +174,7 @@ void loadState(void) {
|
||||
|
||||
void updateState(void) {
|
||||
bool update = false;
|
||||
static uint16_t stateUpdateCounter = 0;
|
||||
|
||||
if (stateUpdateCounter == 0) {
|
||||
// first state update when IAQ accuracy is >= 1
|
||||
@ -228,7 +185,7 @@ void updateState(void) {
|
||||
} else {
|
||||
|
||||
/* Update every STATE_SAVE_PERIOD minutes */
|
||||
if ((stateUpdateCounter * STATE_SAVE_PERIOD) < millis()) {
|
||||
if ((long)(millis() - stateUpdateCounter * STATE_SAVE_PERIOD) >= 0) {
|
||||
update = true;
|
||||
stateUpdateCounter++;
|
||||
}
|
||||
|
@ -33,7 +33,7 @@ void button_init(int pin) {
|
||||
b->setOnHolding([]() {
|
||||
payload.reset();
|
||||
payload.addButton(0x01);
|
||||
SendPayload(BUTTONPORT, prio_normal);
|
||||
SendPayload(BUTTONPORT);
|
||||
});
|
||||
|
||||
// attach interrupt to the button
|
||||
|
@ -42,19 +42,20 @@ static void defaultConfig(configData_t *myconfig) {
|
||||
COUNTERMODE; // 0=cyclic, 1=cumulative, 2=cyclic confirmed
|
||||
myconfig->rssilimit = 0; // threshold for rssilimiter, negative value!
|
||||
myconfig->sendcycle = SENDCYCLE; // payload send cycle [seconds/2]
|
||||
myconfig->sleepcycle = SLEEPCYCLE; // sleep cycle [seconds/2]
|
||||
myconfig->wifichancycle =
|
||||
WIFI_CHANNEL_SWITCH_INTERVAL; // wifi channel switch cycle [seconds/100]
|
||||
myconfig->blescantime =
|
||||
BLESCANINTERVAL /
|
||||
10; // BT channel scan cycle [seconds/100], default 1 (= 10ms)
|
||||
myconfig->blescan = 1; // 0=disabled, 1=enabled
|
||||
myconfig->wifiscan = 1; // 0=disabled, 1=enabled
|
||||
myconfig->wifiant = 0; // 0=internal, 1=external (for LoPy/LoPy4)
|
||||
myconfig->vendorfilter = VENDORFILTER; // 0=disabled, 1=enabled
|
||||
myconfig->rgblum = RGBLUMINOSITY; // RGB Led luminosity (0..100%)
|
||||
myconfig->monitormode = 0; // 0=disabled, 1=enabled
|
||||
myconfig->payloadmask = PAYLOADMASK; // payloads as defined in default
|
||||
myconfig->enscount = COUNT_ENS; // 0=disabled, 1=enabled
|
||||
myconfig->blescan = 1; // 0=disabled, 1=enabled
|
||||
myconfig->wifiscan = 1; // 0=disabled, 1=enabled
|
||||
myconfig->wifiant = 0; // 0=internal, 1=external (for LoPy/LoPy4)
|
||||
myconfig->macfilter = MACFILTER; // 0=disabled, 1=enabled
|
||||
myconfig->rgblum = RGBLUMINOSITY; // RGB Led luminosity (0..100%)
|
||||
myconfig->monitormode = 0; // 0=disabled, 1=enabled
|
||||
myconfig->payloadmask = PAYLOADMASK; // payloads as defined in default
|
||||
myconfig->enscount = COUNT_ENS; // 0=disabled, 1=enabled
|
||||
|
||||
#ifdef HAS_BME680
|
||||
// initial BSEC state for BME680 sensor
|
||||
|
@ -5,6 +5,10 @@
|
||||
// (c) by Kaspar Metz
|
||||
// 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)
|
||||
|
||||
// Local logging tag
|
||||
@ -23,18 +27,13 @@ static std::map<uint16_t, unsigned long> cwaSeenNotifiers;
|
||||
|
||||
// Remove notifiers last seen over FORGET_AFTER_MINUTES ago.
|
||||
void cwa_clear() {
|
||||
/*
|
||||
|
||||
#ifdef SOME_FORM_OF_DEBUG
|
||||
ESP_LOGD(TAG, "CWA: forget old notifier: %d", cwaSeenNotifiers.size());
|
||||
for (auto const ¬ifier : cwaSeenNotifiers) {
|
||||
ESP_LOGD(TAG, "CWA forget <%X>", notifier.first);
|
||||
// }
|
||||
}
|
||||
#endif
|
||||
|
||||
*/
|
||||
|
||||
#ifdef VERBOSE
|
||||
ESP_LOGV(TAG, "CWA: forget old notifier: %d", cwaSeenNotifiers.size());
|
||||
for (auto const ¬ifier : cwaSeenNotifiers) {
|
||||
ESP_LOGD(TAG, "CWA forget <%04X>", notifier.first);
|
||||
// }
|
||||
}
|
||||
#endif
|
||||
// clear everything, otherwise we would count the same device again, as in the
|
||||
// next cycle it likely will advertise with a different hash-value
|
||||
cwaSeenNotifiers.clear();
|
||||
|
@ -14,29 +14,28 @@ extern boolean isSDS011Active;
|
||||
#endif
|
||||
|
||||
void setCyclicIRQ() {
|
||||
xTaskNotifyFromISR(irqHandlerTask, CYCLIC_IRQ, eSetBits, NULL);
|
||||
xTaskNotify(irqHandlerTask, CYCLIC_IRQ, eSetBits);
|
||||
}
|
||||
|
||||
// do all housekeeping
|
||||
void doHousekeeping() {
|
||||
|
||||
// update uptime counter
|
||||
uptime();
|
||||
// check if update mode trigger switch was set
|
||||
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
|
||||
}
|
||||
}
|
||||
// check if update mode trigger switch was set by rcommand
|
||||
if (RTC_runmode == RUNMODE_UPDATE)
|
||||
do_reset(true);
|
||||
|
||||
// task storage debugging //
|
||||
// heap and task storage debugging
|
||||
ESP_LOGD(TAG, "Heap: Free:%d, Min:%d, Size:%d, Alloc:%d, StackHWM:%d",
|
||||
ESP.getFreeHeap(), ESP.getMinFreeHeap(), ESP.getHeapSize(),
|
||||
ESP.getMaxAllocHeap(), uxTaskGetStackHighWaterMark(NULL));
|
||||
ESP_LOGD(TAG, "IRQhandler %d bytes left | Taskstate = %d",
|
||||
uxTaskGetStackHighWaterMark(irqHandlerTask),
|
||||
eTaskGetState(irqHandlerTask));
|
||||
ESP_LOGD(TAG, "MACprocessor %d bytes left | Taskstate = %d",
|
||||
uxTaskGetStackHighWaterMark(macProcessTask),
|
||||
eTaskGetState(macProcessTask));
|
||||
ESP_LOGD(TAG, "Rcommand interpreter %d bytes left | Taskstate = %d",
|
||||
uxTaskGetStackHighWaterMark(rcmdTask), eTaskGetState(rcmdTask));
|
||||
#if (HAS_LORA)
|
||||
ESP_LOGD(TAG, "LMiCtask %d bytes left | Taskstate = %d",
|
||||
uxTaskGetStackHighWaterMark(lmicTask), eTaskGetState(lmicTask));
|
||||
@ -100,7 +99,6 @@ void doHousekeeping() {
|
||||
"free heap = %d bytes)",
|
||||
ESP.getMinFreeHeap(), ESP.getFreeHeap());
|
||||
reset_counters(); // clear macs container and reset all counters
|
||||
get_salt(); // get new salt for salting hashes
|
||||
|
||||
if (ESP.getMinFreeHeap() <= MEM_LOW) // check again
|
||||
do_reset(true); // memory leak, reset device
|
||||
@ -111,7 +109,6 @@ void doHousekeeping() {
|
||||
if (ESP.getMinFreePsram() <= MEM_LOW) {
|
||||
ESP_LOGI(TAG, "PSRAM full, counter cleared");
|
||||
reset_counters(); // clear macs container and reset all counters
|
||||
get_salt(); // get new salt for salting hashes
|
||||
|
||||
if (ESP.getMinFreePsram() <= MEM_LOW) // check again
|
||||
do_reset(true); // memory leak, reset device
|
||||
@ -130,16 +127,6 @@ void doHousekeeping() {
|
||||
|
||||
} // doHousekeeping()
|
||||
|
||||
// uptime counter 64bit to prevent millis() rollover after 49 days
|
||||
uint64_t uptime() {
|
||||
static uint32_t low32, high32;
|
||||
uint32_t new_low32 = millis();
|
||||
if (new_low32 < low32)
|
||||
high32++;
|
||||
low32 = new_low32;
|
||||
return (uint64_t)high32 << 32 | low32;
|
||||
}
|
||||
|
||||
uint32_t getFreeRAM() {
|
||||
#ifndef BOARD_HAS_PSRAM
|
||||
return ESP.getFreeHeap();
|
||||
@ -153,6 +140,7 @@ void reset_counters() {
|
||||
macs.clear(); // clear all macs container
|
||||
macs_wifi = 0;
|
||||
macs_ble = 0;
|
||||
renew_salt(); // get new salt
|
||||
#ifdef HAS_DISPLAY
|
||||
dp_plotCurve(0, true);
|
||||
#endif
|
||||
|
@ -53,7 +53,7 @@ QRCode qrcode;
|
||||
#if (HAS_DISPLAY) == 1
|
||||
OBDISP ssoled;
|
||||
#elif (HAS_DISPLAY) == 2
|
||||
TFT_eSPI tft = TFT_eSPI();
|
||||
TFT_eSPI tft = TFT_eSPI(MY_DISPLAY_WIDTH, MY_DISPLAY_HEIGHT);
|
||||
#else
|
||||
#error Unknown display type specified in hal file
|
||||
#endif
|
||||
@ -67,7 +67,7 @@ void dp_setup(int contrast) {
|
||||
MY_DISPLAY_INVERT, USE_HW_I2C, MY_DISPLAY_SDA,
|
||||
MY_DISPLAY_SCL, MY_DISPLAY_RST,
|
||||
OLED_FREQUENCY); // use standard I2C bus at 400Khz
|
||||
assert(rc != OLED_NOT_FOUND);
|
||||
_ASSERT(rc != OLED_NOT_FOUND);
|
||||
|
||||
// set display buffer
|
||||
obdSetBackBuffer(&ssoled, displaybuf);
|
||||
@ -94,7 +94,7 @@ void dp_init(bool verbose) {
|
||||
#if (HAS_DISPLAY) == 1 // i2c
|
||||
// block i2c bus access
|
||||
if (!I2C_MUTEX_LOCK())
|
||||
ESP_LOGV(TAG, "[%0.3f] i2c mutex lock failed", millis() / 1000.0);
|
||||
ESP_LOGV(TAG, "[%0.3f] i2c mutex lock failed", _seconds());
|
||||
else {
|
||||
#endif
|
||||
|
||||
@ -190,7 +190,7 @@ void dp_refresh(bool nextPage) {
|
||||
|
||||
// block i2c bus access
|
||||
if (!I2C_MUTEX_LOCK())
|
||||
ESP_LOGV(TAG, "[%0.3f] i2c mutex lock failed", millis() / 1000.0);
|
||||
ESP_LOGV(TAG, "[%0.3f] i2c mutex lock failed", _seconds());
|
||||
else {
|
||||
// set display on/off according to current device configuration
|
||||
if (DisplayIsOn != cfg.screenon) {
|
||||
@ -265,10 +265,12 @@ void dp_drawPage(time_t t, bool nextpage) {
|
||||
else
|
||||
dp_printf("WIFI:off");
|
||||
if (cfg.blescan)
|
||||
if (!cfg.enscount)
|
||||
dp_printf("BLTH:%-5d", macs_ble);
|
||||
else
|
||||
dp_printf(" CWA:%-5d", cwa_report());
|
||||
#if (COUNT_ENS)
|
||||
if (cfg.enscount)
|
||||
dp_printf(" CWA:%-5d", cwa_report());
|
||||
else
|
||||
#endif
|
||||
dp_printf("BLTH:%-5d", macs_ble);
|
||||
else
|
||||
dp_printf(" BLTH:off");
|
||||
#elif ((WIFICOUNTER) && (!BLECOUNTER))
|
||||
@ -277,12 +279,13 @@ void dp_drawPage(time_t t, bool nextpage) {
|
||||
else
|
||||
dp_printf("WIFI:off");
|
||||
#elif ((!WIFICOUNTER) && (BLECOUNTER))
|
||||
if (cfg.blescan) {
|
||||
if (cfg.blescan)
|
||||
dp_printf("BLTH:%-5d", macs_ble);
|
||||
if (cfg.enscount)
|
||||
dp_printf("(CWA:%d)", cwa_report());
|
||||
}
|
||||
#if (COUNT_ENS)
|
||||
if (cfg.enscount)
|
||||
dp_printf("(CWA:%d)", cwa_report());
|
||||
else
|
||||
#endif
|
||||
dp_printf("BLTH:off");
|
||||
#else
|
||||
dp_printf("Sniffer disabled");
|
||||
@ -308,6 +311,7 @@ void dp_drawPage(time_t t, bool nextpage) {
|
||||
dp_printf(" ");
|
||||
#endif
|
||||
dp_printf(" ch:%02d", channel);
|
||||
// dp_printf(" due:%02d", rf_load);
|
||||
dp_println();
|
||||
|
||||
// line 5: RSSI limiter + free memory
|
||||
@ -341,10 +345,10 @@ void dp_drawPage(time_t t, bool nextpage) {
|
||||
|
||||
#if (HAS_LORA)
|
||||
// LMiC event display
|
||||
dp_printf("%-16s", lmic_event_msg);
|
||||
dp_printf("%-16s ", lmic_event_msg);
|
||||
// LORA datarate, display inverse if ADR disabled
|
||||
dp_setFont(MY_FONT_SMALL, !cfg.adrmode);
|
||||
dp_printf(" %-4s", getSfName(updr2rps(LMIC.datarate)));
|
||||
dp_printf("%-4s", getSfName(updr2rps(LMIC.datarate)));
|
||||
dp_setFont(MY_FONT_SMALL, 0);
|
||||
dp_println();
|
||||
#endif // HAS_LORA
|
||||
@ -396,7 +400,7 @@ void dp_drawPage(time_t t, bool nextpage) {
|
||||
gps.location.lat());
|
||||
|
||||
// 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());
|
||||
|
||||
} else {
|
||||
@ -597,9 +601,8 @@ void dp_shutdown(void) {
|
||||
#if (HAS_DISPLAY) == 1
|
||||
// block i2c bus access
|
||||
if (!I2C_MUTEX_LOCK())
|
||||
ESP_LOGV(TAG, "[%0.3f] i2c mutex lock failed", millis() / 1000.0);
|
||||
ESP_LOGV(TAG, "[%0.3f] i2c mutex lock failed", _seconds());
|
||||
else {
|
||||
cfg.screenon = 0;
|
||||
obdPower(&ssoled, false);
|
||||
delay(DISPLAYREFRESH_MS / 1000 * 1.1);
|
||||
I2C_MUTEX_UNLOCK(); // release i2c bus access
|
||||
|
@ -30,34 +30,27 @@ static uint16_t nmea_txDelay_ms = 0;
|
||||
// initialize and configure GPS
|
||||
int gps_init(void) {
|
||||
|
||||
int ret = 1;
|
||||
|
||||
if (!gps_config()) {
|
||||
ESP_LOGE(TAG, "GPS chip initializiation error");
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef GPS_SERIAL
|
||||
ESP_LOGI(TAG, "Opening serial GPS");
|
||||
GPS_Serial.begin(GPS_SERIAL);
|
||||
ESP_LOGI(TAG, "Using serial GPS");
|
||||
#elif defined GPS_I2C
|
||||
ESP_LOGI(TAG, "Opening I2C GPS");
|
||||
Wire.begin(GPS_I2C, 400000); // I2C connect to GPS device with 400 KHz
|
||||
Wire.beginTransmission(GPS_ADDR);
|
||||
Wire.write(0x00); // dummy write
|
||||
ret = Wire.endTransmission(); // check if chip is seen on i2c bus
|
||||
|
||||
if (ret) {
|
||||
ESP_LOGE(TAG,
|
||||
"Quectel L76 GPS chip not found on i2c bus, bus error %d. "
|
||||
"Stopping GPS-Task.",
|
||||
ret);
|
||||
ret = 0;
|
||||
} else {
|
||||
Wire.write(0x00); // dummy write
|
||||
if (Wire.endTransmission()) {
|
||||
ESP_LOGE(TAG, "Quectel L76 GPS chip not found");
|
||||
return 0;
|
||||
} else
|
||||
ESP_LOGI(TAG, "Quectel L76 GPS chip found");
|
||||
}
|
||||
#endif
|
||||
|
||||
return ret;
|
||||
return 1;
|
||||
} // gps_init()
|
||||
|
||||
// detect gps chipset type and configure it with device specific settings
|
||||
@ -109,7 +102,7 @@ time_t get_gpstime(uint16_t *msec) {
|
||||
#endif
|
||||
|
||||
// did we get a current date & time?
|
||||
if (gpstime.isValid() && gpsday.isValid()) {
|
||||
if (gpstime.isValid()) {
|
||||
|
||||
time_t t = 0;
|
||||
tmElements_t tm;
|
||||
@ -126,8 +119,8 @@ time_t get_gpstime(uint16_t *msec) {
|
||||
tm.Year = CalendarYrToTm(atoi(gpsyear.value())); // year offset from 1970
|
||||
t = makeTime(tm);
|
||||
|
||||
// ESP_LOGD(TAG, "GPS time/date = %2d:%2d:%2d / %2d.%2d.%2d", tm.Hour,
|
||||
// tm.Minute, tm.Second, tm.Day, tm.Month, tm.Year + 1970);
|
||||
ESP_LOGD(TAG, "GPS time/date = %2d:%2d:%2d / %2d.%2d.%2d", tm.Hour,
|
||||
tm.Minute, tm.Second, tm.Day, tm.Month, tm.Year + 1970);
|
||||
|
||||
// add protocol delay with millisecond precision
|
||||
t += delay_ms / 1000 - 1; // whole seconds
|
||||
@ -150,7 +143,7 @@ time_t get_gpstime(void) {
|
||||
// GPS serial feed FreeRTos Task
|
||||
void gps_loop(void *pvParameters) {
|
||||
|
||||
configASSERT(((uint32_t)pvParameters) == 1); // FreeRTOS check
|
||||
_ASSERT((uint32_t)pvParameters == 1); // FreeRTOS check
|
||||
|
||||
while (1) {
|
||||
|
||||
@ -188,4 +181,4 @@ void gps_loop(void *pvParameters) {
|
||||
|
||||
} // gps_loop()
|
||||
|
||||
#endif // HAS_GPS
|
||||
#endif // HAS_GPS
|
||||
|
@ -38,7 +38,7 @@
|
||||
#define RTC_INT GPIO_NUM_0 //
|
||||
|
||||
// Settings for IF482 interface
|
||||
#define HAS_IF482 9600, SERIAL_7E1, GPIO_NUM_3, GPIO_NUM_1 // RX, TX
|
||||
//#define HAS_IF482 9600, SERIAL_7E1, GPIO_NUM_3, GPIO_NUM_1 // RX, TX
|
||||
|
||||
// Settings for DCF77 interface
|
||||
//#define HAS_DCF77 GPIO_NUM_14 // JP8 #13
|
||||
|
@ -55,8 +55,10 @@
|
||||
#define SDS_TX 19 // connect to RX on the SDS011
|
||||
#define SDS_RX 23 // connect to TX on the SDS011
|
||||
|
||||
// user defined sensors
|
||||
#define HAS_SENSORS 1 // comment out if device has user defined sensors
|
||||
// up to three user defined sensors (if connected)
|
||||
//#define HAS_SENSOR_1 1 // comment out if device has user defined sensor #1
|
||||
//#define HAS_SENSOR_2 1 // comment out if device has user defined sensor #2
|
||||
//#define HAS_SENSOR_3 1 // comment out if device has user defined sensor #3
|
||||
|
||||
#define CFG_sx1276_radio 1 // select LoRa chip
|
||||
//#define CFG_sx1272_radio 1 // select LoRa chip
|
||||
@ -103,8 +105,4 @@
|
||||
#define LORA_IO1 (33)
|
||||
#define LORA_IO2 LMIC_UNUSED_PIN
|
||||
|
||||
// I2C config for Microchip 24AA02E64 DEVEUI unique address
|
||||
#define MCP_24AA02E64_I2C_ADDRESS 0x50 // I2C address for the 24AA02E64
|
||||
#define MCP_24AA02E64_MAC_ADDRESS 0xF8 // Memory adress of unique deveui 64 bits
|
||||
|
||||
#endif
|
||||
|
@ -10,7 +10,6 @@
|
||||
// Hardware related definitions for Pycom LoPy4 Board
|
||||
|
||||
#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
|
||||
// pin definitions for local wired SPI slave interface
|
||||
@ -31,18 +30,18 @@
|
||||
#define HAS_ANTENNA_SWITCH (21) // pin for switching wifi antenna (P12)
|
||||
#define WIFI_ANTENNA 0 // 0 = internal, 1 = external
|
||||
|
||||
// uncomment this only if your LoPy runs on a PYTRACK BOARD
|
||||
// uncomment defines in this section ONLY if your LoPy lives on a PYTRACK BOARD
|
||||
//#define HAS_GPS 1
|
||||
//#define GPS_I2C GPIO_NUM_25, GPIO_NUM_26 // SDA (P22), SCL (P21)
|
||||
//#define GPS_ADDR 0x10
|
||||
|
||||
// uncomment this only if your LoPy runs on a EXPANSION BOARD
|
||||
#define HAS_LED (12) // use if LoPy is on Expansion Board, this has a user LED
|
||||
#define LED_ACTIVE_LOW 1 // use if LoPy is on Expansion Board, this has a user LED
|
||||
#define HAS_BUTTON (13) // user button on expansion board
|
||||
#define BUTTON_PULLUP 1 // Button need pullup instead of default pulldown
|
||||
#define BAT_MEASURE_ADC ADC1_GPIO39_CHANNEL // battery probe GPIO pin -> ADC1_CHANNEL_7
|
||||
#define BAT_VOLTAGE_DIVIDER 2 // voltage divider 1MOhm/1MOhm -> expansion board 3.0
|
||||
// uncomment defines in this section ONLY if your LoPy lives on a EXPANSION BOARD
|
||||
//#define HAS_LED (12) // use if LoPy is on Expansion Board, this has a user LED
|
||||
//#define LED_ACTIVE_LOW 1 // use if LoPy is on Expansion Board, this has a user LED
|
||||
//#define HAS_BUTTON (13) // user button on expansion board
|
||||
//#define BUTTON_PULLUP 1 // Button need pullup instead of default pulldown
|
||||
//#define BAT_MEASURE_ADC ADC1_GPIO39_CHANNEL // battery probe GPIO pin -> ADC1_CHANNEL_7
|
||||
//#define BAT_VOLTAGE_DIVIDER 2 // voltage divider 1MOhm/1MOhm -> expansion board 3.0
|
||||
//#define BAT_VOLTAGE_DIVIDER 4 // voltage divider 115kOhm/56kOhm -> expansion board 2.0
|
||||
|
||||
#endif
|
@ -25,9 +25,6 @@
|
||||
#define SDCARD_MISO MISO
|
||||
#define SDCARD_SCLK SCK
|
||||
|
||||
// user defined sensors
|
||||
//#define HAS_SENSORS 1 // comment out if device has user defined sensors
|
||||
|
||||
#define CFG_sx1276_radio 1 // select LoRa chip
|
||||
#define DISABLE_BROWNOUT 1 // comment out if you want to keep brownout feature
|
||||
|
||||
|
@ -28,9 +28,6 @@
|
||||
#define SDCARD_MISO MISO
|
||||
#define SDCARD_SCLK SCK
|
||||
|
||||
// user defined sensors
|
||||
//#define HAS_SENSORS 1 // comment out if device has user defined sensors
|
||||
|
||||
#define CFG_sx1276_radio 1 // select LoRa chip
|
||||
#define BOARD_HAS_PSRAM // use if board has external PSRAM
|
||||
#define DISABLE_BROWNOUT 1 // comment out if you want to keep brownout feature
|
||||
|
@ -20,9 +20,6 @@
|
||||
#define HAS_BME680 GPIO_NUM_23, GPIO_NUM_22 // SDA, SCL
|
||||
#define BME680_ADDR BME680_I2C_ADDR_PRIMARY // connect SDIO of BME680 to GND
|
||||
|
||||
// user defined sensors
|
||||
//#define HAS_SENSORS 1 // comment out if device has user defined sensors
|
||||
|
||||
#define HAS_LED 13 // ESP32 GPIO12 (pin22) On Board LED
|
||||
//#define LED_ACTIVE_LOW 1 // Onboard LED is active when pin is LOW
|
||||
//#define HAS_RGB_LED SmartLed rgb_led(LED_WS2812, 1, GPIO_NUM_13) // ESP32 GPIO13 (pin13) On Board Shield WS2812B RGB LED
|
||||
|
@ -8,15 +8,15 @@
|
||||
#include <stdint.h>
|
||||
|
||||
// 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
|
||||
#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_MEASURE_ADC ADC1_GPIO39_CHANNEL // external power probe GPIO pin
|
||||
#define BAT_VOLTAGE_DIVIDER 2.1277f // voltage divider 47k/442k on board
|
||||
#define BAT_MEASURE_ADC ADC1_GPIO39_CHANNEL // external power sense
|
||||
#define BAT_VOLTAGE_DIVIDER 2.1277f // voltage divider 47k/100k on board
|
||||
|
||||
#define HAS_BUTTON KEY_BUILTIN // on board button
|
||||
#define HAS_LED NOT_A_PIN // no on board LED
|
||||
|
@ -39,9 +39,6 @@
|
||||
#define MY_DISPLAY_RST NOT_A_PIN
|
||||
//#define MY_DISPLAY_FLIP 1 // use if display is rotated
|
||||
|
||||
// user defined sensors (if connected)
|
||||
//#define HAS_SENSORS 1 // comment out if device has user defined sensors
|
||||
|
||||
//#define DISABLE_BROWNOUT 1 // comment out if you want to keep brownout feature
|
||||
|
||||
#endif
|
||||
|
@ -13,10 +13,10 @@ for T-Beam version T22_V10 + T22_V11
|
||||
pinouts taken from https://github.com/lewisxhe/TTGO-T-Beam
|
||||
|
||||
/// Button functions: ///
|
||||
Power, short press -> set device on (toggles display while device is on)
|
||||
Power, long press -> set device off
|
||||
User, short press -> flip display page
|
||||
User, long press -> send LORA message
|
||||
Power, short press -> set device on / while device is on: goto sleep
|
||||
Power, long press -> set device off
|
||||
User, short press -> flip display page
|
||||
User, long press -> send a button message
|
||||
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 CFG_sx1276_radio 1 // HPD13A LoRa SoC
|
||||
#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
|
||||
#define HAS_PMU 1 // has AXP192 chip
|
||||
@ -41,6 +42,12 @@ Reset -> reset device
|
||||
// possible values (V):
|
||||
// 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
|
||||
#define HAS_GPS 1 // use on board GPS
|
||||
#define GPS_SERIAL 9600, SERIAL_8N1, GPIO_NUM_34, GPIO_NUM_12 // UBlox NEO 6M
|
||||
@ -52,9 +59,6 @@ Reset -> reset device
|
||||
//#define HAS_BME680 SDA, SCL
|
||||
//#define BME680_ADDR BME680_I2C_ADDR_PRIMARY // !! connect SDIO of BME680 to GND !!
|
||||
|
||||
// user defined sensors (if connected)
|
||||
//#define HAS_SENSORS 1 // comment out if device has user defined sensors
|
||||
|
||||
//#define DISABLE_BROWNOUT 1 // comment out if you want to keep brownout feature
|
||||
|
||||
#endif
|
||||
|
55
src/hal/ttgotdisplay.h
Normal file
55
src/hal/ttgotdisplay.h
Normal file
@ -0,0 +1,55 @@
|
||||
// clang-format off
|
||||
// upload_speed 1500000
|
||||
// board esp32dev
|
||||
|
||||
#ifndef _TTGOTDISPLAY_H
|
||||
#define _TTGOTDISPLAY_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define DISABLE_BROWNOUT 1 // comment out if you want to keep brownout feature
|
||||
|
||||
#define HAS_DISPLAY 2 // TFT-LCD, support work in progess, not ready yet
|
||||
#define MY_DISPLAY_FLIP 1 // use if display is rotated
|
||||
|
||||
#define HAS_LED NOT_A_PIN // no on board LED (?)
|
||||
#define HAS_BUTTON (35) // on board button A
|
||||
|
||||
// power management settings
|
||||
#define BAT_MEASURE_ADC ADC1_GPIO34_CHANNEL // battery probe GPIO pin -> ADC1_CHANNEL_6
|
||||
#define BAT_VOLTAGE_DIVIDER 2.605f // voltage divider
|
||||
|
||||
// Display Settings
|
||||
#define MY_DISPLAY_WIDTH 135
|
||||
#define MY_DISPLAY_HEIGHT 240
|
||||
#define MY_DISPLAY_INVERT 1
|
||||
|
||||
// setting for TTGO T-display
|
||||
#define USER_SETUP_LOADED 1
|
||||
#define ST7789_DRIVER 1
|
||||
|
||||
#define CGRAM_OFFSET
|
||||
|
||||
#define TFT_MOSI GPIO_NUM_19 // SPI
|
||||
#define TFT_SCLK GPIO_NUM_18 // SPI
|
||||
#define TFT_CS GPIO_NUM_5 // Chip select control
|
||||
#define TFT_DC GPIO_NUM_16 // Data Command control
|
||||
#define TFT_RST GPIO_NUM_23 // Reset
|
||||
#define TFT_BL GPIO_NUM_4 // LED back-light
|
||||
|
||||
#define TFT_RGB_ORDER TFT_BGR // Colour order Blue-Green-Red
|
||||
|
||||
#define LOAD_GLCD // Font 1. Original Adafruit 8 pixel font needs ~1820 bytes in FLASH
|
||||
#define LOAD_FONT2 // Font 2. Small 16 pixel high font, needs ~3534 bytes in FLASH, 96 characters
|
||||
#define LOAD_FONT4 // Font 4. Medium 26 pixel high font, needs ~5848 bytes in FLASH, 96 characters
|
||||
#define LOAD_FONT6 // Font 6. Large 48 pixel font, needs ~2666 bytes in FLASH, only characters 1234567890:-.apm
|
||||
#define LOAD_FONT7 // Font 7. 7 segment 48 pixel font, needs ~2438 bytes in FLASH, only characters 1234567890:-.
|
||||
#define LOAD_FONT8 // Font 8. Large 75 pixel font needs ~3256 bytes in FLASH, only characters 1234567890:-.
|
||||
//#define LOAD_FONT8N // Font 8. Alternative to Font 8 above, slightly narrower, so 3 digits fit a 160 pixel TFT
|
||||
#define LOAD_GFXFF // FreeFonts. Include access to the 48 Adafruit_GFX free fonts FF1 to FF48 and custom fonts
|
||||
#define SMOOTH_FONT
|
||||
|
||||
#define SPI_FREQUENCY 40000000
|
||||
#define SPI_READ_FREQUENCY 6000000
|
||||
|
||||
#endif
|
58
src/hal/ttgotwristband.h
Normal file
58
src/hal/ttgotwristband.h
Normal file
@ -0,0 +1,58 @@
|
||||
// clang-format off
|
||||
// upload_speed 1500000
|
||||
// board pico32
|
||||
|
||||
#ifndef _TTGOTWRISTBAND_H
|
||||
#define _TTGOTWRISTBAND_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define DISABLE_BROWNOUT 1 // comment out if you want to keep brownout feature
|
||||
|
||||
#define HAS_DISPLAY 2 // TFT-LCD, support work in progess, not ready yet
|
||||
#define MY_DISPLAY_FLIP 1 // use if display is rotated
|
||||
|
||||
#define HAS_LED NOT_A_PIN // no on board LED (?)
|
||||
#define HAS_BUTTON (33) // on board button A
|
||||
|
||||
// power management settings
|
||||
#define BAT_MEASURE_ADC ADC1_GPIO34_CHANNEL // battery probe GPIO pin -> ADC1_CHANNEL_6
|
||||
#define BAT_VOLTAGE_DIVIDER 2.605f // voltage divider
|
||||
|
||||
// Display Settings
|
||||
#define MY_DISPLAY_WIDTH 80
|
||||
#define MY_DISPLAY_HEIGHT 160
|
||||
#define MY_DISPLAY_INVERT 1
|
||||
|
||||
// setting for TTGO T-display
|
||||
#define USER_SETUP_LOADED 1
|
||||
#define ST7735_DRIVER 1
|
||||
|
||||
#define CGRAM_OFFSET
|
||||
|
||||
#define TFT_MISO -1
|
||||
#define TFT_MOSI GPIO_NUM_19 // SPI
|
||||
#define TFT_SCLK GPIO_NUM_18 // SPI
|
||||
#define TFT_CS GPIO_NUM_5 // Chip select control
|
||||
#define TFT_DC GPIO_NUM_23 // Data Command control
|
||||
#define TFT_RST GPIO_NUM_26 // Reset
|
||||
#define TFT_BL GPIO_NUM_27 // LED back-light
|
||||
#define TFT_BACKLIGHT_ON 1
|
||||
#define ST7735_GREENTAB160x80
|
||||
|
||||
#define TFT_RGB_ORDER TFT_BGR // Colour order Blue-Green-Red
|
||||
|
||||
#define LOAD_GLCD // Font 1. Original Adafruit 8 pixel font needs ~1820 bytes in FLASH
|
||||
#define LOAD_FONT2 // Font 2. Small 16 pixel high font, needs ~3534 bytes in FLASH, 96 characters
|
||||
#define LOAD_FONT4 // Font 4. Medium 26 pixel high font, needs ~5848 bytes in FLASH, 96 characters
|
||||
#define LOAD_FONT6 // Font 6. Large 48 pixel font, needs ~2666 bytes in FLASH, only characters 1234567890:-.apm
|
||||
#define LOAD_FONT7 // Font 7. 7 segment 48 pixel font, needs ~2438 bytes in FLASH, only characters 1234567890:-.
|
||||
#define LOAD_FONT8 // Font 8. Large 75 pixel font needs ~3256 bytes in FLASH, only characters 1234567890:-.
|
||||
#define LOAD_GFXFF // FreeFonts. Include access to the 48 Adafruit_GFX free fonts FF1 to FF48 and custom fonts
|
||||
#define SMOOTH_FONT
|
||||
|
||||
#define SPI_FREQUENCY 27000000
|
||||
#define SPI_READ_FREQUENCY 6000000
|
||||
|
||||
#endif
|
||||
|
@ -16,7 +16,7 @@
|
||||
#define CFG_sx1276_radio 1 // HPD13A LoRa SoC
|
||||
|
||||
// enable only if you want to store a local paxcount table on the device
|
||||
#define HAS_SDCARD 1 // this board has an SD-card-reader/writer
|
||||
#define HAS_SDCARD 2 // // this board has a SDMMC card-reader/writer
|
||||
|
||||
#define HAS_DISPLAY 1
|
||||
#define HAS_LED (25) // green on board LED
|
||||
|
52
src/hash.cpp
52
src/hash.cpp
@ -36,52 +36,10 @@
|
||||
|
||||
#include "hash.h"
|
||||
|
||||
uint32_t IRAM_ATTR rokkit(const char *data, int len) {
|
||||
uint32_t hash, tmp;
|
||||
int rem;
|
||||
#ifdef ROKKIT_ENABLE_8BIT_OPTIMIZATIONS
|
||||
#undef ROKKIT_ENABLE_8BIT_OPTIMIZATIONS
|
||||
#endif
|
||||
|
||||
if (len <= 0 || data == 0)
|
||||
return 0;
|
||||
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;
|
||||
uint32_t IRAM_ATTR hash(const char *data, int len) {
|
||||
return rokkit(data, len);
|
||||
}
|
||||
|
126
src/i2c.cpp
126
src/i2c.cpp
@ -5,82 +5,82 @@
|
||||
// Local logging tag
|
||||
static const char TAG[] = __FILE__;
|
||||
|
||||
void i2c_init(void) { Wire.begin(MY_DISPLAY_SDA, MY_DISPLAY_SCL, 400000); }
|
||||
void i2c_init(void) { Wire.begin(MY_DISPLAY_SDA, MY_DISPLAY_SCL, 100000); }
|
||||
|
||||
void i2c_deinit(void) {
|
||||
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_deinit(void) { Wire.~TwoWire(); }
|
||||
|
||||
int i2c_scan(void) {
|
||||
void i2c_scan(void) {
|
||||
|
||||
int i2c_ret, addr;
|
||||
int devices = 0;
|
||||
// parts of the code in this function were taken from:
|
||||
//
|
||||
// Copyright (c) 2019 BitBank Software, Inc.
|
||||
// Written by Larry Bank
|
||||
// email: bitbank@pobox.com
|
||||
// Project started 25/02/2019
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
BBI2C bbi2c;
|
||||
|
||||
const char *szNames[] = {
|
||||
"Unknown", "SSD1306", "SH1106", "VL53L0X", "BMP180", "BMP280",
|
||||
"BME280", "MPU-60x0", "MPU-9250", "MCP9808", "LSM6DS3", "ADXL345",
|
||||
"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...");
|
||||
|
||||
// block i2c bus access
|
||||
if (I2C_MUTEX_LOCK()) {
|
||||
|
||||
// Scan at 100KHz low speed
|
||||
Wire.setClock(100000);
|
||||
memset(&bbi2c, 0, sizeof(bbi2c));
|
||||
bbi2c.bWire = 1; // use wire library, no bitbanging
|
||||
bbi2c.iSDA = MY_DISPLAY_SDA;
|
||||
bbi2c.iSCL = MY_DISPLAY_SCL;
|
||||
I2CInit(&bbi2c, 100000L); // Scan at 100KHz low speed
|
||||
delay(100); // allow devices to power up
|
||||
|
||||
for (addr = 8; addr <= 119; addr++) {
|
||||
uint8_t map[16];
|
||||
uint8_t i;
|
||||
int iDevice, iCount;
|
||||
|
||||
Wire.beginTransmission(addr);
|
||||
Wire.write(addr);
|
||||
i2c_ret = Wire.endTransmission();
|
||||
|
||||
if (i2c_ret == 0) {
|
||||
devices++;
|
||||
|
||||
switch (addr) {
|
||||
|
||||
case SSD1306_PRIMARY_ADDRESS:
|
||||
case SSD1306_SECONDARY_ADDRESS:
|
||||
ESP_LOGI(TAG, "0x%X: SSD1306 Display controller", addr);
|
||||
break;
|
||||
|
||||
case BME_PRIMARY_ADDRESS:
|
||||
case BME_SECONDARY_ADDRESS:
|
||||
ESP_LOGI(TAG, "0x%X: Bosch BME MEMS", addr);
|
||||
break;
|
||||
|
||||
case AXP192_PRIMARY_ADDRESS:
|
||||
ESP_LOGI(TAG, "0x%X: AXP192 power management", addr);
|
||||
break;
|
||||
|
||||
case IP5306_PRIMARY_ADDRESS:
|
||||
ESP_LOGI(TAG, "0x%X: IP5306 power management", addr);
|
||||
break;
|
||||
|
||||
case QUECTEL_GPS_PRIMARY_ADDRESS:
|
||||
ESP_LOGI(TAG, "0x%X: Quectel GPS", addr);
|
||||
break;
|
||||
|
||||
case MCP_24AA02E64_PRIMARY_ADDRESS:
|
||||
ESP_LOGI(TAG, "0x%X: 24AA02E64 serial EEPROM", addr);
|
||||
break;
|
||||
|
||||
default:
|
||||
ESP_LOGI(TAG, "0x%X: Unknown device", addr);
|
||||
break;
|
||||
I2CScan(&bbi2c, map); // get bitmap of connected I2C devices
|
||||
if (map[0] == 0xfe) // something is wrong with the I2C bus
|
||||
{
|
||||
ESP_LOGI(TAG, "I2C pins are not correct or the bus is being pulled low "
|
||||
"by a bad device; unable to run scan");
|
||||
} else {
|
||||
iCount = 0;
|
||||
for (i = 1; i < 128; i++) // skip address 0 (general call address) since
|
||||
// more than 1 device can respond
|
||||
{
|
||||
if (map[i >> 3] & (1 << (i & 7))) // device found
|
||||
{
|
||||
iCount++;
|
||||
iDevice = I2CDiscoverDevice(&bbi2c, i);
|
||||
ESP_LOGI(TAG, "Device found at 0x%X, type = %s", i,
|
||||
szNames[iDevice]); // show the device name as a string
|
||||
}
|
||||
} // switch
|
||||
} // for loop
|
||||
|
||||
ESP_LOGI(TAG, "I2C scan done, %u devices found.", devices);
|
||||
|
||||
// Set back to 400KHz
|
||||
Wire.setClock(400000);
|
||||
} // for i
|
||||
ESP_LOGI(TAG, "%u I2C device(s) found", iCount);
|
||||
}
|
||||
|
||||
I2C_MUTEX_UNLOCK(); // release i2c bus access
|
||||
} else
|
||||
ESP_LOGE(TAG, "I2c bus busy - scan error");
|
||||
|
||||
return devices;
|
||||
ESP_LOGE(TAG, "I2C bus busy - scan error");
|
||||
}
|
||||
|
||||
// mutexed functions for i2c r/w access
|
||||
@ -107,7 +107,7 @@ uint8_t i2c_readBytes(uint8_t addr, uint8_t reg, uint8_t *data, uint8_t len) {
|
||||
I2C_MUTEX_UNLOCK(); // release i2c bus access
|
||||
return ret;
|
||||
} else {
|
||||
ESP_LOGW(TAG, "[%0.3f] i2c mutex lock failed", millis() / 1000.0);
|
||||
ESP_LOGW(TAG, "[%0.3f] i2c mutex lock failed", _seconds());
|
||||
return 0xFF;
|
||||
}
|
||||
}
|
||||
@ -126,7 +126,7 @@ uint8_t i2c_writeBytes(uint8_t addr, uint8_t reg, uint8_t *data, uint8_t len) {
|
||||
I2C_MUTEX_UNLOCK(); // release i2c bus access
|
||||
return ret ? ret : 0xFF;
|
||||
} else {
|
||||
ESP_LOGW(TAG, "[%0.3f] i2c mutex lock failed", millis() / 1000.0);
|
||||
ESP_LOGW(TAG, "[%0.3f] i2c mutex lock failed", _seconds());
|
||||
return 0xFF;
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ static const char TAG[] = __FILE__;
|
||||
// irq handler task, handles all our application level interrupts
|
||||
void irqHandler(void *pvParameters) {
|
||||
|
||||
configASSERT(((uint32_t)pvParameters) == 1); // FreeRTOS check
|
||||
_ASSERT((uint32_t)pvParameters == 1); // FreeRTOS check
|
||||
|
||||
uint32_t InterruptStatus;
|
||||
|
||||
@ -69,14 +69,6 @@ void irqHandler(void *pvParameters) {
|
||||
}
|
||||
#endif
|
||||
|
||||
// MQTT loop due?
|
||||
#if (HAS_MQTT)
|
||||
if (InterruptStatus & MQTT_IRQ) {
|
||||
mqtt_loop();
|
||||
InterruptStatus &= ~MQTT_IRQ;
|
||||
}
|
||||
#endif
|
||||
|
||||
// are cyclic tasks due?
|
||||
if (InterruptStatus & CYCLIC_IRQ) {
|
||||
doHousekeeping();
|
||||
@ -95,6 +87,13 @@ void irqHandler(void *pvParameters) {
|
||||
if (InterruptStatus & SENDCYCLE_IRQ) {
|
||||
sendData();
|
||||
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
|
||||
} // irqHandler()
|
||||
|
@ -146,7 +146,7 @@ void ledLoop(void *parameter) {
|
||||
// management
|
||||
if (LEDBlinkStarted && LEDBlinkDuration) {
|
||||
// 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
|
||||
LEDState = LED_OFF;
|
||||
LEDBlinkStarted = 0;
|
||||
|
@ -18,13 +18,13 @@
|
||||
|
||||
// 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
|
||||
#if (LORA_IRQ) != (LORA_IO1)
|
||||
#define LMIC_USE_INTERRUPTS 1
|
||||
#endif
|
||||
// note: this feature can't be used on ESP32 unless PR #556 of MCCI LMIC was merged
|
||||
//#if (LORA_IRQ) != (LORA_IO1)
|
||||
//#define LMIC_USE_INTERRUPTS 1
|
||||
//#endif
|
||||
|
||||
// avoid lmic warning if we don't configure radio because we don't have one
|
||||
#define CFG_sx1276_radio 1
|
||||
#if ! (defined(CFG_sx1272_radio) || defined(CFG_sx1276_radio))
|
||||
// avoid lmic warning if we don't configure radio in case we haven't one
|
||||
#if !(defined(CFG_sx1272_radio) || defined(CFG_sx1276_radio))
|
||||
#define CFG_sx1276_radio 1
|
||||
#endif
|
||||
|
||||
|
344
src/lorawan.cpp
344
src/lorawan.cpp
@ -6,6 +6,9 @@
|
||||
// Local logging Tag
|
||||
static const char TAG[] = "lora";
|
||||
|
||||
// Saves the LMIC structure during deep sleep
|
||||
RTC_DATA_ATTR lmic_t RTC_LMIC;
|
||||
|
||||
#if CLOCK_ERROR_PROCENTAGE > 7
|
||||
#warning CLOCK_ERROR_PROCENTAGE value in lmic_config.h is too high; values > 7 will cause side effects
|
||||
#endif
|
||||
@ -16,12 +19,7 @@ static const char TAG[] = "lora";
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// variable keep its values after restart or wakeup from sleep
|
||||
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;
|
||||
static QueueHandle_t LoraSendQueue;
|
||||
TaskHandle_t lmicTask = NULL, lorasendTask = NULL;
|
||||
|
||||
class MyHalConfig_t : public Arduino_LMIC::HalConfiguration_t {
|
||||
@ -83,8 +81,6 @@ void lora_setupForNetwork(bool preJoin) {
|
||||
getSfName(updr2rps(LMIC.datarate)),
|
||||
getBwName(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
|
||||
void gen_lora_deveui(uint8_t *pdeveui) {
|
||||
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
|
||||
// will remain the same as MAC address
|
||||
// MAC is 6 bytes, devEUI 8, set middle 2 ones
|
||||
@ -162,49 +158,7 @@ void os_getDevEui(u1_t *buf) {
|
||||
} else {
|
||||
gen_lora_deveui(buf); // generate DEVEUI from device's MAC
|
||||
}
|
||||
|
||||
// Get MCP 24AA02E64 hardware DEVEUI (override default settings if found)
|
||||
#ifdef MCP_24AA02E64_I2C_ADDRESS
|
||||
get_hard_deveui(buf);
|
||||
RevBytes(buf, 8); // swap bytes to LSB format
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
void get_hard_deveui(uint8_t *pdeveui) {
|
||||
// read DEVEUI from Microchip 24AA02E64 2Kb serial eeprom if present
|
||||
#ifdef MCP_24AA02E64_I2C_ADDRESS
|
||||
|
||||
uint8_t i2c_ret;
|
||||
|
||||
// Init this just in case, no more to 100KHz
|
||||
Wire.begin(SDA, SCL, 100000);
|
||||
Wire.beginTransmission(MCP_24AA02E64_I2C_ADDRESS);
|
||||
Wire.write(MCP_24AA02E64_MAC_ADDRESS);
|
||||
i2c_ret = Wire.endTransmission();
|
||||
|
||||
// check if device was seen on i2c bus
|
||||
if (i2c_ret == 0) {
|
||||
char deveui[32] = "";
|
||||
uint8_t data;
|
||||
|
||||
Wire.beginTransmission(MCP_24AA02E64_I2C_ADDRESS);
|
||||
Wire.write(MCP_24AA02E64_MAC_ADDRESS);
|
||||
Wire.endTransmission();
|
||||
|
||||
Wire.requestFrom(MCP_24AA02E64_I2C_ADDRESS, 8);
|
||||
while (Wire.available()) {
|
||||
data = Wire.read();
|
||||
sprintf(deveui + strlen(deveui), "%02X ", data);
|
||||
*pdeveui++ = data;
|
||||
}
|
||||
ESP_LOGI(TAG, "Serial EEPROM found, read DEVEUI %s", deveui);
|
||||
} else
|
||||
ESP_LOGI(TAG, "Could not read DEVEUI from serial EEPROM");
|
||||
|
||||
// Set back to 400KHz to speed up OLED
|
||||
Wire.setClock(400000);
|
||||
#endif // MCP 24AA02E64
|
||||
}
|
||||
|
||||
#if (VERBOSE)
|
||||
@ -227,7 +181,7 @@ void showLoraKeys(void) {
|
||||
|
||||
// LMIC send task
|
||||
void lora_send(void *pvParameters) {
|
||||
configASSERT(((uint32_t)pvParameters) == 1); // FreeRTOS check
|
||||
_ASSERT((uint32_t)pvParameters == 1); // FreeRTOS check
|
||||
|
||||
MessageBuffer_t SendBuffer;
|
||||
|
||||
@ -239,60 +193,48 @@ void lora_send(void *pvParameters) {
|
||||
}
|
||||
|
||||
// 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!");
|
||||
continue;
|
||||
}
|
||||
|
||||
// attempt to transmit payload
|
||||
else {
|
||||
|
||||
switch (LMIC_setTxData2_strict(SendBuffer.MessagePort, SendBuffer.Message,
|
||||
SendBuffer.MessageSize,
|
||||
(cfg.countermode & 0x02))) {
|
||||
|
||||
case LMIC_ERROR_SUCCESS:
|
||||
// save current Fcnt to RTC RAM
|
||||
RTCseqnoUp = LMIC.seqnoUp;
|
||||
RTCseqnoDn = LMIC.seqnoDn;
|
||||
switch (LMIC_setTxData2_strict(SendBuffer.MessagePort, SendBuffer.Message,
|
||||
SendBuffer.MessageSize,
|
||||
(cfg.countermode & 0x02))) {
|
||||
|
||||
case LMIC_ERROR_SUCCESS:
|
||||
#if (TIME_SYNC_LORASERVER)
|
||||
// if last packet sent was a timesync request, store TX timestamp
|
||||
if (SendBuffer.MessagePort == TIMEPORT)
|
||||
// store LMIC time when we started transmit of timesync request
|
||||
timesync_store(osticks2ms(os_getTime()), timesync_tx);
|
||||
// if last packet sent was a timesync request, store TX timestamp
|
||||
if (SendBuffer.MessagePort == TIMEPORT)
|
||||
// store LMIC time when we started transmit of timesync request
|
||||
timesync_store(osticks2ms(os_getTime()), timesync_tx);
|
||||
#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);
|
||||
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
|
||||
}
|
||||
} // switch
|
||||
delay(2); // yield to CPU
|
||||
}
|
||||
} // while(1)
|
||||
}
|
||||
|
||||
void lora_stack_reset() {
|
||||
LMIC_reset(); // reset LMIC MAC
|
||||
}
|
||||
|
||||
esp_err_t lora_stack_init(bool do_join) {
|
||||
assert(SEND_QUEUE_SIZE);
|
||||
esp_err_t lmic_init(void) {
|
||||
_ASSERT(SEND_QUEUE_SIZE > 0);
|
||||
LoraSendQueue = xQueueCreate(SEND_QUEUE_SIZE, sizeof(MessageBuffer_t));
|
||||
if (LoraSendQueue == 0) {
|
||||
ESP_LOGE(TAG, "Could not create LORA send queue. Aborting.");
|
||||
@ -301,89 +243,6 @@ esp_err_t lora_stack_init(bool do_join) {
|
||||
ESP_LOGI(TAG, "LORA send queue created, size %d Bytes",
|
||||
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) {
|
||||
configASSERT(((uint32_t)pvParameters) == 1);
|
||||
|
||||
// setup LMIC stack
|
||||
os_init_ex(&myPinmap); // initialize lmic run-time environment
|
||||
|
||||
@ -397,7 +256,7 @@ void lmictask(void *pvParameters) {
|
||||
|
||||
// Reset the MAC state. Session and pending data transfers will be
|
||||
// discarded.
|
||||
lora_stack_reset();
|
||||
LMIC_reset();
|
||||
|
||||
// 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,
|
||||
@ -407,11 +266,79 @@ void lmictask(void *pvParameters) {
|
||||
LMIC_setClockError(CLOCK_ERROR_PROCENTAGE * MAX_CLOCK_ERROR / 1000);
|
||||
#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) {
|
||||
os_runloop_once(); // execute lmic scheduled jobs and events
|
||||
delay(2); // yield to CPU
|
||||
}
|
||||
} // lmictask
|
||||
}
|
||||
|
||||
// lmic event handler
|
||||
void myEventCallback(void *pUserData, ev_t ev) {
|
||||
@ -452,7 +379,7 @@ void myEventCallback(void *pUserData, ev_t ev) {
|
||||
case EV_JOIN_FAILED:
|
||||
// must call LMIC_reset() to stop joining
|
||||
// otherwise join procedure continues.
|
||||
lora_stack_reset();
|
||||
LMIC_reset();
|
||||
break;
|
||||
|
||||
case EV_JOIN_TXCOMPLETE:
|
||||
@ -540,23 +467,6 @@ void myRxCallback(void *pUserData, uint8_t port, const uint8_t *pMsg,
|
||||
} // switch
|
||||
}
|
||||
|
||||
/*
|
||||
// event EV_TXCOMPLETE message handler
|
||||
void myTxCallback(void *pUserData, int fSuccess) {
|
||||
|
||||
uint8_t *const pMsg = (uint8_t *)pUserData;
|
||||
|
||||
// LMIC did successful transmit data
|
||||
if (fSuccess) {
|
||||
RTCseqnoUp = LMIC.seqnoUp;
|
||||
RTCseqnoDn = LMIC.seqnoDn;
|
||||
} else {
|
||||
// LMIC could not transmit data
|
||||
// -> error handling yet to come
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
const char *getSfName(rps_t rps) {
|
||||
const char *const t[] = {"FSK", "SF7", "SF8", "SF9",
|
||||
"SF10", "SF11", "SF12", "SF?"};
|
||||
@ -619,4 +529,44 @@ void mac_decode(const uint8_t cmd[], const uint8_t cmdlen, bool is_down) {
|
||||
} // mac_decode()
|
||||
#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
|
223
src/macsniff.cpp
223
src/macsniff.cpp
@ -3,17 +3,17 @@
|
||||
#include "globals.h"
|
||||
#include "macsniff.h"
|
||||
|
||||
#if (VENDORFILTER)
|
||||
#include "vendor_array.h"
|
||||
#endif
|
||||
|
||||
// Local logging tag
|
||||
static const char TAG[] = __FILE__;
|
||||
|
||||
uint16_t salt = 0;
|
||||
static QueueHandle_t MacQueue;
|
||||
TaskHandle_t macProcessTask;
|
||||
|
||||
uint16_t get_salt(void) {
|
||||
salt = (uint16_t)random(65536); // get new 16bit random for salting hashes
|
||||
static uint32_t salt = renew_salt();
|
||||
|
||||
uint32_t renew_salt(void) {
|
||||
salt = esp_random();
|
||||
ESP_LOGV(TAG, "new salt = %04X", salt);
|
||||
return salt;
|
||||
}
|
||||
|
||||
@ -43,103 +43,156 @@ uint64_t macConvert(uint8_t *paddr) {
|
||||
return (__builtin_bswap64(*mac) >> 16);
|
||||
}
|
||||
|
||||
uint16_t mac_add(uint8_t *paddr, int8_t rssi, bool sniff_type) {
|
||||
esp_err_t macQueueInit() {
|
||||
_ASSERT(MAC_QUEUE_SIZE > 0);
|
||||
MacQueue = xQueueCreate(MAC_QUEUE_SIZE, sizeof(MacBuffer_t));
|
||||
if (MacQueue == 0) {
|
||||
ESP_LOGE(TAG, "Could not create MAC processing queue. Aborting.");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
ESP_LOGI(TAG, "MAC processing queue created, size %d Bytes",
|
||||
MAC_QUEUE_SIZE * sizeof(MacBuffer_t));
|
||||
|
||||
if (salt == 0) // ensure we have salt (appears after radio is turned on)
|
||||
xTaskCreatePinnedToCore(mac_process, // task function
|
||||
"mac_process", // name of task
|
||||
3072, // stack size of task
|
||||
(void *)1, // parameter of the task
|
||||
1, // priority of the task
|
||||
&macProcessTask, // task handle
|
||||
1); // CPU core
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
// sniffed MAC processing task
|
||||
void mac_process(void *pvParameters) {
|
||||
_ASSERT((uint32_t)pvParameters == 1); // FreeRTOS check
|
||||
|
||||
MacBuffer_t MacBuffer;
|
||||
|
||||
while (1) {
|
||||
|
||||
// fetch next or wait for incoming MAC from sniffing queue
|
||||
if (xQueueReceive(MacQueue, &MacBuffer, portMAX_DELAY) != pdTRUE) {
|
||||
ESP_LOGE(TAG, "Premature return from xQueueReceive() with no data!");
|
||||
continue;
|
||||
}
|
||||
|
||||
// update traffic indicator
|
||||
rf_load = uxQueueMessagesWaiting(MacQueue);
|
||||
// process fetched mac
|
||||
mac_analyze(MacBuffer);
|
||||
}
|
||||
delay(2); // yield to CPU
|
||||
}
|
||||
|
||||
// enqueue message in MAC processing queue
|
||||
void IRAM_ATTR mac_add(uint8_t *paddr, int8_t rssi, snifftype_t sniff_type) {
|
||||
|
||||
MacBuffer_t MacBuffer;
|
||||
|
||||
MacBuffer.rssi = rssi;
|
||||
MacBuffer.sniff_type = sniff_type;
|
||||
memcpy(MacBuffer.mac, paddr, 6);
|
||||
|
||||
if (xQueueSendToBackFromISR(MacQueue, (void *)&MacBuffer, (TickType_t)0) !=
|
||||
pdPASS)
|
||||
ESP_LOGW(TAG, "Dense radio traffic, packet lost!");
|
||||
}
|
||||
|
||||
uint16_t mac_analyze(MacBuffer_t MacBuffer) {
|
||||
|
||||
uint32_t *mac; // pointer to shortened 4 byte MAC
|
||||
uint32_t saltedmac;
|
||||
uint16_t hashedmac;
|
||||
|
||||
if ((cfg.rssilimit) &&
|
||||
(MacBuffer.rssi < cfg.rssilimit)) { // rssi is negative value
|
||||
ESP_LOGI(TAG, "%s RSSI %d -> ignoring (limit: %d)",
|
||||
(MacBuffer.sniff_type == MAC_SNIFF_WIFI) ? "WIFI" : "BLTH",
|
||||
MacBuffer.rssi, cfg.rssilimit);
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint16_t hashedmac = 0; // temporary buffer for generated hash value
|
||||
char buff[10]; // temporary buffer for printf
|
||||
bool added = false;
|
||||
int8_t beaconID; // beacon number in test monitor mode
|
||||
uint32_t *mac; // temporary buffer for shortened MAC
|
||||
// in beacon monitor mode check if seen MAC is a known beacon
|
||||
if (cfg.monitormode) {
|
||||
int8_t beaconID = isBeacon(macConvert(MacBuffer.mac));
|
||||
if (beaconID >= 0) {
|
||||
ESP_LOGI(TAG, "Beacon ID#%d detected", beaconID);
|
||||
blink_LED(COLOR_WHITE, 2000);
|
||||
payload.reset();
|
||||
payload.addAlarm(MacBuffer.rssi, beaconID);
|
||||
SendPayload(BEACONPORT);
|
||||
}
|
||||
};
|
||||
|
||||
// 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.
|
||||
// this gets MAC in msb (= reverse) order, but doesn't matter for hashing it.
|
||||
mac = (uint32_t *)(paddr + 2);
|
||||
mac = (uint32_t *)(MacBuffer.mac + 2);
|
||||
|
||||
#if (VENDORFILTER)
|
||||
uint32_t *oui; // temporary buffer for vendor OUI
|
||||
oui = (uint32_t *)paddr;
|
||||
// salt and hash MAC, and if new unique one, store identifier in container
|
||||
// and increment counter on display
|
||||
// https://en.wikipedia.org/wiki/MAC_Address_Anonymization
|
||||
|
||||
// use OUI vendor filter list only on Wifi, not on BLE
|
||||
if ((sniff_type == MAC_SNIFF_BLE) ||
|
||||
std::find(vendors.begin(), vendors.end(), __builtin_bswap32(*oui) >> 8) !=
|
||||
vendors.end()) {
|
||||
#endif
|
||||
// reversed 4 byte MAC added to current salt
|
||||
saltedmac = *mac + salt;
|
||||
|
||||
// salt and hash MAC, and if new unique one, store identifier in container
|
||||
// and increment counter on display
|
||||
// https://en.wikipedia.org/wiki/MAC_Address_Anonymization
|
||||
// hashed 4 byte MAC
|
||||
// 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);
|
||||
|
||||
snprintf(buff, sizeof(buff), "%08X",
|
||||
*mac + (uint32_t)salt); // convert unsigned 32-bit salted MAC
|
||||
// to 8 digit hex string
|
||||
hashedmac = rokkit(&buff[3], 5); // hash MAC 8 digit -> 5 digit
|
||||
auto newmac = macs.insert(hashedmac); // add hashed MAC, if new unique
|
||||
added = newmac.second ? true
|
||||
: false; // true if hashed MAC is unique in container
|
||||
auto newmac = macs.insert(hashedmac); // add hashed MAC, if new unique
|
||||
bool added =
|
||||
newmac.second ? true : false; // true if hashed MAC is unique in container
|
||||
|
||||
// Count only if MAC was not yet seen
|
||||
if (added) {
|
||||
// increment counter and one blink led
|
||||
if (sniff_type == MAC_SNIFF_WIFI) {
|
||||
macs_wifi++; // increment Wifi MACs counter
|
||||
#if (HAS_LED != NOT_A_PIN) || defined(HAS_RGB_LED)
|
||||
blink_LED(COLOR_GREEN, 50);
|
||||
#endif
|
||||
}
|
||||
#if (BLECOUNTER)
|
||||
else if (sniff_type == MAC_SNIFF_BLE) {
|
||||
macs_ble++; // increment BLE Macs counter
|
||||
#if (HAS_LED != NOT_A_PIN) || defined(HAS_RGB_LED)
|
||||
blink_LED(COLOR_MAGENTA, 50);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
// Count only if MAC was not yet seen
|
||||
if (added) {
|
||||
|
||||
// in beacon monitor mode check if seen MAC is a known beacon
|
||||
if (cfg.monitormode) {
|
||||
beaconID = isBeacon(macConvert(paddr));
|
||||
if (beaconID >= 0) {
|
||||
ESP_LOGI(TAG, "Beacon ID#%d detected", beaconID);
|
||||
#if (HAS_LED != NOT_A_PIN) || defined(HAS_RGB_LED)
|
||||
blink_LED(COLOR_WHITE, 2000);
|
||||
#endif
|
||||
payload.reset();
|
||||
payload.addAlarm(rssi, beaconID);
|
||||
SendPayload(BEACONPORT, prio_high);
|
||||
}
|
||||
};
|
||||
switch (MacBuffer.sniff_type) {
|
||||
|
||||
} // added
|
||||
case MAC_SNIFF_WIFI:
|
||||
macs_wifi++; // increment Wifi MACs counter
|
||||
blink_LED(COLOR_GREEN, 50);
|
||||
break;
|
||||
|
||||
// Log scan result
|
||||
ESP_LOGV(TAG,
|
||||
"%s %s RSSI %ddBi -> salted MAC %s -> Hash %04X -> WiFi:%d "
|
||||
"BLTH:%d "
|
||||
case MAC_SNIFF_BLE:
|
||||
macs_ble++; // increment BLE Macs counter
|
||||
blink_LED(COLOR_MAGENTA, 50);
|
||||
break;
|
||||
#if (COUNT_ENS)
|
||||
"(CWA:%d)"
|
||||
case MAC_SNIFF_BLE_ENS:
|
||||
macs_ble++; // increment BLE Macs counter
|
||||
cwa_mac_add(hashedmac); // process ENS beacon
|
||||
blink_LED(COLOR_WHITE, 50);
|
||||
break;
|
||||
#endif
|
||||
"-> %d Bytes left",
|
||||
added ? "new " : "known",
|
||||
sniff_type == MAC_SNIFF_WIFI ? "WiFi" : "BLTH", rssi, buff,
|
||||
hashedmac, macs_wifi, macs_ble,
|
||||
default:
|
||||
break;
|
||||
|
||||
} // switch
|
||||
} // added
|
||||
|
||||
// Log scan result
|
||||
ESP_LOGV(TAG,
|
||||
"%s %s RSSI %ddBi -> MAC %0x:%0x:%0x:%0x:%0x:%0x -> salted %04X"
|
||||
" -> hashed %04X -> WiFi:%d "
|
||||
"BLTH:%d "
|
||||
#if (COUNT_ENS)
|
||||
cwa_report(),
|
||||
"(CWA:%d)"
|
||||
#endif
|
||||
getFreeRAM());
|
||||
|
||||
#if (VENDORFILTER)
|
||||
} else {
|
||||
// Very noisy
|
||||
// ESP_LOGD(TAG, "Filtered MAC %02X:%02X:%02X:%02X:%02X:%02X",
|
||||
// paddr[0],paddr[1],paddr[2],paddr[3],paddr[5],paddr[5]);
|
||||
}
|
||||
"-> %d Bytes left",
|
||||
added ? "new " : "known",
|
||||
MacBuffer.sniff_type == MAC_SNIFF_WIFI ? "WiFi" : "BLTH",
|
||||
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)
|
||||
cwa_report(),
|
||||
#endif
|
||||
getFreeRAM());
|
||||
|
||||
// if a new and unique Wifi or BLE mac was counted, returs hash of this mac,
|
||||
// else 0
|
||||
return hashedmac;
|
||||
// if an unknown Wifi or BLE mac was counted, return hash of this mac, else 0
|
||||
return (added ? hashedmac : 0);
|
||||
}
|
||||
|
95
src/main.cpp
95
src/main.cpp
@ -29,15 +29,17 @@ Task Core Prio Purpose
|
||||
-------------------------------------------------------------------------------
|
||||
ledloop 0 3 blinks LEDs
|
||||
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
|
||||
|
||||
lmictask 1 2 MCCI LMiC LORAWAN stack
|
||||
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
|
||||
irqhandler 1 1 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
|
||||
lorasendtask 1 1 feeds data from lora sendqueue to lmcic
|
||||
macprocess 1 1 MAC analyzer loop
|
||||
rmcd_process 1 1 Remote command interpreter loop
|
||||
IDLE 1 0 ESP32 arduino scheduler -> runs wifi channel rotator
|
||||
|
||||
Low priority numbers denote low priority tasks.
|
||||
@ -59,15 +61,16 @@ irqHandlerTask (Core 1), see irqhandler.cpp
|
||||
|
||||
fired by hardware
|
||||
DisplayIRQ -> esp32 timer 0
|
||||
ButtonIRQ -> external gpio
|
||||
PMUIRQ -> PMU chip gpio
|
||||
CLOCKIRQ -> esp32 timer 1 or external GPIO (RTC_INT or GPS_INT)
|
||||
MatrixDisplayIRQ-> esp32 timer 3
|
||||
ButtonIRQ -> external GPIO
|
||||
PMUIRQ -> PMU chip GPIO
|
||||
|
||||
fired by software (Ticker.h)
|
||||
TIMESYNC_IRQ -> setTimeSyncIRQ()
|
||||
CYCLIC_IRQ -> setCyclicIRQ()
|
||||
SENDCYCLE_IRQ -> setSendIRQ()
|
||||
BME_IRQ -> setBMEIRQ()
|
||||
MQTT_IRQ -> setMqttIRQ()
|
||||
SENDCYCLE_IRQ -> setSendIRQ()
|
||||
BME_IRQ -> setBMEIRQ()
|
||||
|
||||
ClockTask (Core 1), see timekeeper.cpp
|
||||
|
||||
@ -86,8 +89,9 @@ triggers pps 1 sec impulse
|
||||
|
||||
configData_t cfg; // struct holds current device configuration
|
||||
char lmic_event_msg[LMIC_EVENTMSG_LEN]; // display buffer for LMIC event message
|
||||
uint8_t volatile channel = 0; // channel rotation counter
|
||||
uint8_t batt_level = 0; // display value
|
||||
uint8_t volatile channel = WIFI_CHANNEL_MIN; // channel rotation counter
|
||||
uint8_t volatile rf_load = 0; // RF traffic indicator
|
||||
uint16_t volatile macs_wifi = 0, macs_ble = 0; // globals for display
|
||||
|
||||
hw_timer_t *ppsIRQ = NULL, *displayIRQ = NULL, *matrixDisplayIRQ = NULL;
|
||||
@ -99,7 +103,7 @@ timesource_t timeSource = _unsynced;
|
||||
|
||||
// container holding unique MAC address hashes with Memory Alloctor using PSRAM,
|
||||
// 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
|
||||
PayloadConvert payload(PAYLOAD_BUFFER_SIZE);
|
||||
@ -118,7 +122,7 @@ void setup() {
|
||||
|
||||
// create some semaphores for syncing / mutexing tasks
|
||||
I2Caccess = xSemaphoreCreateMutex(); // for access management of i2c bus
|
||||
assert(I2Caccess != NULL);
|
||||
_ASSERT(I2Caccess != NULL);
|
||||
I2C_MUTEX_UNLOCK();
|
||||
|
||||
// disable brownout detection
|
||||
@ -136,7 +140,7 @@ void setup() {
|
||||
esp_log_level_set("*", ESP_LOG_NONE);
|
||||
#endif
|
||||
|
||||
do_after_reset(rtc_get_reset_reason(0));
|
||||
do_after_reset();
|
||||
|
||||
// print chip information on startup if in verbose mode after coldstart
|
||||
#if (VERBOSE)
|
||||
@ -203,10 +207,11 @@ void setup() {
|
||||
#endif
|
||||
|
||||
// read (and initialize on first run) runtime settings from NVRAM
|
||||
assert(loadConfig()); // includes initialize if necessary
|
||||
_ASSERT(loadConfig()); // includes initialize if necessary
|
||||
|
||||
// now that we are powered, we scan i2c bus for devices
|
||||
i2c_scan();
|
||||
if (RTC_runmode == RUNMODE_POWERCYCLE)
|
||||
i2c_scan();
|
||||
|
||||
// initialize display
|
||||
#ifdef HAS_DISPLAY
|
||||
@ -217,7 +222,7 @@ void setup() {
|
||||
#endif
|
||||
|
||||
#ifdef BOARD_HAS_PSRAM
|
||||
assert(psramFound());
|
||||
_ASSERT(psramFound());
|
||||
ESP_LOGI(TAG, "PSRAM found and initialized");
|
||||
strcat_P(features, " PSRAM");
|
||||
#endif
|
||||
@ -285,8 +290,16 @@ void setup() {
|
||||
start_ota_update();
|
||||
#endif
|
||||
|
||||
// start mac processing task
|
||||
ESP_LOGI(TAG, "Starting MAC processor...");
|
||||
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
|
||||
// or switch off bluetooth, if not compiled
|
||||
// or remove bluetooth stack from RAM, if option bluetooth is not compiled
|
||||
#if (BLECOUNTER)
|
||||
strcat_P(features, " BLE");
|
||||
if (cfg.blescan) {
|
||||
@ -297,9 +310,9 @@ void setup() {
|
||||
#else
|
||||
// remove bluetooth stack to gain more free memory
|
||||
btStop();
|
||||
ESP_ERROR_CHECK(esp_bt_mem_release(ESP_BT_MODE_BTDM));
|
||||
ESP_ERROR_CHECK(esp_coex_preference_set(
|
||||
ESP_COEX_PREFER_WIFI)); // configure Wifi/BT coexist lib
|
||||
esp_bt_mem_release(ESP_BT_MODE_BTDM);
|
||||
esp_coex_preference_set(
|
||||
ESP_COEX_PREFER_WIFI); // configure Wifi/BT coexist lib
|
||||
#endif
|
||||
|
||||
// initialize gps
|
||||
@ -342,21 +355,19 @@ void setup() {
|
||||
// initialize LoRa
|
||||
#if (HAS_LORA)
|
||||
strcat_P(features, " LORA");
|
||||
// kick off join, except we come from sleep
|
||||
assert(lora_stack_init(RTC_runmode == RUNMODE_WAKEUP ? false : true) ==
|
||||
ESP_OK);
|
||||
_ASSERT(lmic_init() == ESP_OK);
|
||||
#endif
|
||||
|
||||
// initialize SPI
|
||||
#ifdef HAS_SPI
|
||||
strcat_P(features, " SPI");
|
||||
assert(spi_init() == ESP_OK);
|
||||
_ASSERT(spi_init() == ESP_OK);
|
||||
#endif
|
||||
|
||||
// initialize MQTT
|
||||
#ifdef HAS_MQTT
|
||||
strcat_P(features, " MQTT");
|
||||
assert(mqtt_init() == ESP_OK);
|
||||
_ASSERT(mqtt_init() == ESP_OK);
|
||||
#endif
|
||||
|
||||
#if (HAS_SDCARD)
|
||||
@ -370,7 +381,7 @@ void setup() {
|
||||
strcat_P(features, " SDS");
|
||||
#endif
|
||||
|
||||
#if (VENDORFILTER)
|
||||
#if (MACFILTER)
|
||||
strcat_P(features, " FILTER");
|
||||
#endif
|
||||
|
||||
@ -395,7 +406,7 @@ void setup() {
|
||||
// initialize RTC
|
||||
#ifdef HAS_RTC
|
||||
strcat_P(features, " RTC");
|
||||
assert(rtc_init());
|
||||
_ASSERT(rtc_init());
|
||||
#endif
|
||||
|
||||
#if defined HAS_DCF77
|
||||
@ -408,23 +419,23 @@ void setup() {
|
||||
|
||||
#if (WIFICOUNTER)
|
||||
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();
|
||||
if (cfg.blescan) {
|
||||
// start wifi sniffing, if enabled
|
||||
if (cfg.wifiscan) {
|
||||
ESP_LOGI(TAG, "Starting Wifi...");
|
||||
switch_wifi_sniffer(1);
|
||||
} else
|
||||
switch_wifi_sniffer(0);
|
||||
#else
|
||||
// switch off wifi
|
||||
// remove wifi driver from RAM, if option wifi not compiled
|
||||
esp_wifi_deinit();
|
||||
#endif
|
||||
|
||||
// initialize salt value using esp_random() called by random() in
|
||||
// arduino-esp32 core. Note: do this *after* wifi has started, since
|
||||
// function gets it's seed from RF noise
|
||||
get_salt(); // get new 16bit for salting hashes
|
||||
reset_counters();
|
||||
|
||||
// start state machine
|
||||
ESP_LOGI(TAG, "Starting Interrupt Handler...");
|
||||
@ -446,11 +457,15 @@ void setup() {
|
||||
strcat_P(features, " BMP180");
|
||||
#endif
|
||||
if (bme_init())
|
||||
ESP_LOGI(TAG, "Starting BME sensor...");
|
||||
ESP_LOGI(TAG, "BME sensor initialized");
|
||||
else {
|
||||
ESP_LOGE(TAG, "BME sensor could not be initialized");
|
||||
cfg.payloadmask &= ~MEMS_DATA; // switch off transmit of BME data
|
||||
}
|
||||
#endif
|
||||
|
||||
// starting timers and interrupts
|
||||
assert(irqHandlerTask != NULL); // has interrupt handler task started?
|
||||
_ASSERT(irqHandlerTask != NULL); // has interrupt handler task started?
|
||||
ESP_LOGI(TAG, "Starting Timers...");
|
||||
|
||||
// display interrupt
|
||||
@ -488,12 +503,9 @@ void setup() {
|
||||
sendTimer.attach(cfg.sendcycle * 2, setSendIRQ);
|
||||
cyclicTimer.attach(HOMECYCLE, setCyclicIRQ);
|
||||
|
||||
#if (TIME_SYNC_INTERVAL)
|
||||
|
||||
#if (!(TIME_SYNC_LORAWAN) && !(TIME_SYNC_LORASERVER) && !defined HAS_GPS && \
|
||||
!defined HAS_RTC)
|
||||
#warning you did not specify a time source, time will not be synched
|
||||
#endif
|
||||
// only if we have a timesource we do timesync
|
||||
#if ((TIME_SYNC_LORAWAN) || (TIME_SYNC_LORASERVER) || (HAS_GPS) || \
|
||||
defined HAS_RTC)
|
||||
|
||||
#if (defined HAS_IF482 || defined HAS_DCF77)
|
||||
ESP_LOGI(TAG, "Starting Clock Controller...");
|
||||
@ -505,10 +517,11 @@ void setup() {
|
||||
#endif
|
||||
|
||||
ESP_LOGI(TAG, "Starting Timekeeper...");
|
||||
assert(timepulse_init()); // setup pps timepulse
|
||||
timepulse_start(); // starts pps and cyclic time sync
|
||||
_ASSERT(timepulse_init()); // setup pps timepulse
|
||||
timepulse_start(); // starts pps and cyclic time sync
|
||||
strcat_P(features, " TIME");
|
||||
|
||||
#endif // TIME_SYNC_INTERVAL
|
||||
#endif // timesync
|
||||
|
||||
// show compiled features
|
||||
ESP_LOGI(TAG, "Features:%s", features);
|
||||
|
@ -4,26 +4,29 @@
|
||||
|
||||
static const char TAG[] = __FILE__;
|
||||
|
||||
QueueHandle_t MQTTSendQueue;
|
||||
static QueueHandle_t MQTTSendQueue;
|
||||
TaskHandle_t mqttTask;
|
||||
|
||||
Ticker mqttTimer;
|
||||
WiFiClient netClient;
|
||||
MQTTClient mqttClient;
|
||||
|
||||
void mqtt_deinit(void) {
|
||||
mqttClient.unsubscribe(MQTT_INTOPIC);
|
||||
mqttClient.onMessageAdvanced(NULL);
|
||||
mqttClient.disconnect();
|
||||
vTaskDelete(mqttTask);
|
||||
}
|
||||
|
||||
esp_err_t mqtt_init(void) {
|
||||
|
||||
// setup network connection
|
||||
WiFi.onEvent(NetworkEvent);
|
||||
// setup network connection and MQTT client
|
||||
ETH.begin();
|
||||
// WiFi.mode(WIFI_STA);
|
||||
// WiFi.begin("SSID", "PASSWORD");
|
||||
|
||||
// setup mqtt client
|
||||
mqttClient.begin(MQTT_SERVER, MQTT_PORT, netClient);
|
||||
mqttClient.setKeepAlive(MQTT_KEEPALIVE);
|
||||
mqttClient.onMessageAdvanced(mqtt_callback);
|
||||
|
||||
assert(SEND_QUEUE_SIZE);
|
||||
_ASSERT(SEND_QUEUE_SIZE > 0);
|
||||
MQTTSendQueue = xQueueCreate(SEND_QUEUE_SIZE, sizeof(MessageBuffer_t));
|
||||
if (MQTTSendQueue == 0) {
|
||||
ESP_LOGE(TAG, "Could not create MQTT send queue. Aborting.");
|
||||
@ -33,17 +36,20 @@ esp_err_t mqtt_init(void) {
|
||||
SEND_QUEUE_SIZE * PAYLOAD_BUFFER_SIZE);
|
||||
|
||||
ESP_LOGI(TAG, "Starting MQTTloop...");
|
||||
mqttTimer.attach(MQTT_KEEPALIVE, setMqttIRQ);
|
||||
xTaskCreate(mqtt_client_task, "mqttloop", 4096, (void *)NULL, 1, &mqttTask);
|
||||
|
||||
xTaskCreatePinnedToCore(mqtt_client_task, "mqttloop", 4096, (void *)NULL, 1,
|
||||
&mqttTask, 1);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
int mqtt_connect(const char *my_host, const uint16_t my_port) {
|
||||
IPAddress mqtt_server_ip;
|
||||
uint8_t mac[6];
|
||||
char clientId[20];
|
||||
|
||||
// static String clientId = "paxcounter-" + ETH.macAddress();
|
||||
static String clientId = "paxcounter-" + String(random(0xffff), HEX);
|
||||
// hash 6 byte MAC to 4 byte hash
|
||||
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);
|
||||
|
||||
@ -73,118 +79,80 @@ int mqtt_connect(const char *my_host, const uint16_t my_port) {
|
||||
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) {
|
||||
|
||||
MessageBuffer_t msg;
|
||||
|
||||
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()) {
|
||||
|
||||
char buffer[PAYLOAD_BUFFER_SIZE + 3];
|
||||
snprintf(buffer, msg.MessageSize + 3, "%s/%u", msg.MessagePort,
|
||||
msg.Message);
|
||||
// check for incoming messages
|
||||
mqttClient.loop();
|
||||
|
||||
if (mqttClient.publish(MQTT_OUTTOPIC, buffer)) {
|
||||
ESP_LOGI(TAG, "%d byte(s) sent to MQTT server", msg.MessageSize + 2);
|
||||
// fetch next or wait for payload to send from queue
|
||||
// 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;
|
||||
} 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 {
|
||||
// attempt to reconnect to MQTT server
|
||||
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);
|
||||
mqtt_connect(MQTT_SERVER, MQTT_PORT);
|
||||
}
|
||||
|
||||
} // while(1)
|
||||
} // while (1)
|
||||
}
|
||||
|
||||
void mqtt_enqueuedata(MessageBuffer_t *message) {
|
||||
// enqueue message in MQTT send queue
|
||||
BaseType_t ret;
|
||||
MessageBuffer_t DummyBuffer;
|
||||
sendprio_t prio = message->MessagePrio;
|
||||
// process incoming MQTT messages
|
||||
void mqtt_callback(MQTTClient *client, char *topic, char *payload, int length) {
|
||||
if (strcmp(topic, MQTT_INTOPIC) == 0) {
|
||||
|
||||
switch (prio) {
|
||||
case prio_high:
|
||||
// clear space in queue if full, then fallthrough to normal
|
||||
if (!uxQueueSpacesAvailable(MQTTSendQueue))
|
||||
xQueueReceive(MQTTSendQueue, &DummyBuffer, (TickType_t)0);
|
||||
case prio_normal:
|
||||
ret = xQueueSendToFront(MQTTSendQueue, (void *)message, (TickType_t)0);
|
||||
break;
|
||||
case prio_low:
|
||||
default:
|
||||
ret = xQueueSendToBack(MQTTSendQueue, (void *)message, (TickType_t)0);
|
||||
break;
|
||||
// get length of base64 encoded message
|
||||
size_t out_len = 0;
|
||||
mbedtls_base64_decode(NULL, 0, &out_len, (unsigned char *)payload, length);
|
||||
|
||||
// decode the base64 message
|
||||
unsigned char decoded[out_len];
|
||||
mbedtls_base64_decode(decoded, out_len, &out_len, (unsigned char *)payload,
|
||||
length);
|
||||
|
||||
rcommand(decoded, out_len);
|
||||
}
|
||||
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");
|
||||
}
|
||||
|
||||
void mqtt_callback(MQTTClient *client, char topic[], char payload[],
|
||||
int length) {
|
||||
if (topic == MQTT_INTOPIC)
|
||||
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 setMqttIRQ(void) { xTaskNotify(irqHandlerTask, MQTT_IRQ, eSetBits); }
|
||||
|
||||
uint32_t mqtt_queuewaiting(void) {
|
||||
return uxQueueMessagesWaiting(MQTTSendQueue);
|
||||
}
|
||||
|
||||
#endif // HAS_MQTT
|
@ -180,7 +180,7 @@ int do_ota_update() {
|
||||
|
||||
unsigned long timeout = millis();
|
||||
while (client.available() == 0) {
|
||||
if ((millis() - timeout) > (RESPONSE_TIMEOUT_MS)) {
|
||||
if ((long)(millis() - timeout) > (RESPONSE_TIMEOUT_MS)) {
|
||||
ESP_LOGI(TAG, "Client timeout");
|
||||
ota_display(3, " E", "client timeout");
|
||||
goto abort;
|
||||
|
@ -11,13 +11,15 @@
|
||||
|
||||
// Payload send cycle and encoding
|
||||
#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 COUNTERMODE 0 // 0=cyclic, 1=cumulative, 2=cyclic confirmed
|
||||
|
||||
// Set this to include BLE counting and vendor filter functions, or to switch off WIFI counting
|
||||
#define VENDORFILTER 0 // set to 0 if you want to scan all devices, not filtering smartphone OUIs
|
||||
#define BLECOUNTER 0 // set to 0 if you do not want to install the BLE sniffer
|
||||
// MAC sniffing parameters
|
||||
#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 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 MAC_QUEUE_SIZE 50 // size of MAC processing buffer (number of MACs) [default = 50]
|
||||
|
||||
// BLE scan parameters
|
||||
#define BLESCANTIME 0 // [seconds] scan duration, 0 means infinite [default], see note below
|
||||
@ -25,14 +27,13 @@
|
||||
#define BLESCANINTERVAL 80 // [illiseconds] scan interval, see below, 3 .. 10240, default 80ms = 100% duty cycle
|
||||
|
||||
// Corona Exposure Notification Service(ENS) counter
|
||||
#define COUNT_ENS 0 // count found number of devices which advertise Exposure Notification Service
|
||||
// set to 0 if you do not want to enable this function
|
||||
#define COUNT_ENS 1 // count found number of devices which advertise Exposure Notification Service
|
||||
// set to 1 if you want to enable this function [default=0]
|
||||
|
||||
// for additional sensors (added by some user)
|
||||
#define HAS_SENSOR_1 0 // set to 1 if you want to transmit CWA counter
|
||||
#define HAS_SENSOR_2 0 // not used
|
||||
#define HAS_SENSOR_3 0 // not used
|
||||
#define HAS_SENSORS (HAS_SENSOR_1 || HAS_SENSOR_2 || HAS_SENSOR_3) // to simplify things
|
||||
#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_3 0 // set to 1 to enable data transfer of user sensor #3 [default=0]
|
||||
|
||||
/* Note: guide for setting bluetooth parameters
|
||||
*
|
||||
@ -83,10 +84,10 @@
|
||||
#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)
|
||||
#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_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_CYCLE 60 // delay between two time samples [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_SENSORREAD 13 // sensor period 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
|
||||
|
@ -49,7 +49,7 @@ void PayloadConvert::addConfig(configData_t value) {
|
||||
buffer[cursor++] = value.blescantime;
|
||||
buffer[cursor++] = value.blescan;
|
||||
buffer[cursor++] = value.wifiant;
|
||||
buffer[cursor++] = value.vendorfilter;
|
||||
buffer[cursor++] = value.macfilter;
|
||||
buffer[cursor++] = value.rgblum;
|
||||
buffer[cursor++] = value.payloadmask;
|
||||
buffer[cursor++] = value.monitormode;
|
||||
@ -179,8 +179,7 @@ void PayloadConvert::addConfig(configData_t value) {
|
||||
writeBitmap(value.adrmode ? true : false, value.screensaver ? true : false,
|
||||
value.screenon ? true : false, value.countermode ? true : false,
|
||||
value.blescan ? true : false, value.wifiant ? true : false,
|
||||
value.vendorfilter ? true : false,
|
||||
value.monitormode ? true : false);
|
||||
value.macfilter ? true : false, value.monitormode ? true : false);
|
||||
writeUint8(value.payloadmask);
|
||||
writeVersion(value.version);
|
||||
}
|
||||
|
@ -1,135 +0,0 @@
|
||||
; PlatformIO Project Configuration File
|
||||
; NOTE: PlatformIO v4 is needed!
|
||||
;
|
||||
; Please visit documentation for the other options and examples
|
||||
; http://docs.platformio.org/page/projectconf.html
|
||||
|
||||
|
||||
; ---> SELECT THE TARGET PLATFORM HERE! <---
|
||||
[board]
|
||||
halfile = generic.h
|
||||
;halfile = ebox.h
|
||||
;halfile = eboxtube.h
|
||||
;halfile = ecopower.h
|
||||
;halfile = heltec.h
|
||||
;halfile = heltecv2.h
|
||||
;halfile = ttgov1.h
|
||||
;halfile = ttgov2.h
|
||||
;halfile = ttgov21old.h
|
||||
;halfile = ttgov21new.h
|
||||
;halfile = ttgofox.h
|
||||
;halfile = ttgobeam.h
|
||||
;halfile = ttgobeam10.h
|
||||
;halfile = fipy.h
|
||||
;halfile = lopy.h
|
||||
;halfile = lopy4.h
|
||||
;halfile = lolin32litelora.h
|
||||
;halfile = lolin32lora.h
|
||||
;halfile = lolin32lite.h
|
||||
;halfile = wemos32oled.h
|
||||
;halfile = wemos32matrix.h
|
||||
;halfile = octopus32.h
|
||||
;halfile = tinypico.h
|
||||
;halfile = tinypicomatrix.h
|
||||
;halfile = m5core.h
|
||||
;halfile = m5fire.h
|
||||
;halfile = olimexpoeiso.h
|
||||
|
||||
[platformio]
|
||||
; upload firmware to board with usb cable
|
||||
default_envs = usb
|
||||
; upload firmware to a jfrog bintray repository
|
||||
;default_envs = ota
|
||||
; use latest versions of libraries
|
||||
;default_envs = dev
|
||||
description = Paxcounter is a device for metering passenger flows in realtime. It counts how many mobile devices are around.
|
||||
|
||||
[common]
|
||||
; for release_version use max. 10 chars total, use any decimal format like "a.b.c"
|
||||
release_version = 2.0.16
|
||||
; DEBUG LEVEL: For production run set to 0, otherwise device will leak RAM while running!
|
||||
; 0=None, 1=Error, 2=Warn, 3=Info, 4=Debug, 5=Verbose
|
||||
debug_level = 3
|
||||
extra_scripts = pre:build.py
|
||||
otakeyfile = ota.conf
|
||||
lorakeyfile = loraconf.h
|
||||
lmicconfigfile = lmic_config.h
|
||||
platform_espressif32 = espressif32@2.0.0
|
||||
monitor_speed = 115200
|
||||
upload_speed = 115200 ; set by build.py and taken from hal file
|
||||
display_library = ; set by build.py and taken from hal file
|
||||
lib_deps_lora =
|
||||
mcci-catena/MCCI LoRaWAN LMIC library @ ^3.2.0
|
||||
lib_deps_display =
|
||||
bitbank2/OneBitDisplay @ 1.7.2
|
||||
ricmoo/QRCode @ ^0.0.1
|
||||
bodmer/TFT_eSPI @ ^2.2.20
|
||||
lib_deps_ledmatrix =
|
||||
seeed-studio/Ultrathin_LED_Matrix @ ^1.0.0
|
||||
lib_deps_rgbled =
|
||||
roboticsbrno/SmartLeds @ ^1.2.1
|
||||
lib_deps_gps =
|
||||
mikalhart/TinyGPSPlus @ ^1.0.2
|
||||
lib_deps_sensors =
|
||||
adafruit/Adafruit Unified Sensor @ ^1.1.4
|
||||
adafruit/Adafruit BME280 Library @ ^2.1.1
|
||||
adafruit/Adafruit BMP085 Library @ ^1.1.0
|
||||
boschsensortec/BSEC Software Library @ 1.5.1474
|
||||
https://github.com/ricki-z/SDS011.git
|
||||
lib_deps_basic =
|
||||
bblanchon/ArduinoJson @ <6
|
||||
jchristensen/Timezone @ ^1.2.4
|
||||
makuna/RTC @ ^2.3.5
|
||||
spacehuhn/SimpleButton
|
||||
lewisxhe/AXP202X_Library @ ^1.1.2
|
||||
geeksville/esp32-micro-sdcard @ ^0.1.1
|
||||
256dpi/MQTT @ ^2.4.7
|
||||
lib_deps_all =
|
||||
${common.lib_deps_basic}
|
||||
${common.lib_deps_lora}
|
||||
${common.lib_deps_display}
|
||||
${common.lib_deps_rgbled}
|
||||
${common.lib_deps_gps}
|
||||
${common.lib_deps_sensors}
|
||||
${common.lib_deps_ledmatrix}
|
||||
build_flags_basic =
|
||||
-include "src/hal/${board.halfile}"
|
||||
-include "src/paxcounter.conf"
|
||||
-w
|
||||
'-DCORE_DEBUG_LEVEL=${common.debug_level}'
|
||||
'-DLOG_LOCAL_LEVEL=${common.debug_level}'
|
||||
'-DPROGVERSION="${common.release_version}"'
|
||||
build_flags_sensors =
|
||||
-Llib/Bosch-BSEC/src/esp32/
|
||||
-lalgobsec
|
||||
build_flags_all =
|
||||
${common.build_flags_basic}
|
||||
${common.build_flags_sensors}
|
||||
-mfix-esp32-psram-cache-issue
|
||||
|
||||
[env]
|
||||
lib_ldf_mode = deep ; #632 Fixes compiler error with OneBitDisplay library
|
||||
framework = arduino
|
||||
board = esp32dev
|
||||
board_build.partitions = min_spiffs.csv
|
||||
upload_speed = ${common.upload_speed}
|
||||
;upload_port = COM8
|
||||
platform = ${common.platform_espressif32}
|
||||
lib_deps = ${common.lib_deps_all}
|
||||
build_flags = ${common.build_flags_all}
|
||||
upload_protocol = ${common.upload_protocol}
|
||||
extra_scripts = ${common.extra_scripts}
|
||||
monitor_speed = ${common.monitor_speed}
|
||||
monitor_filters = time, esp32_exception_decoder, default
|
||||
|
||||
[env:ota]
|
||||
upload_protocol = custom
|
||||
|
||||
[env:usb]
|
||||
upload_protocol = esptool
|
||||
|
||||
[env:dev]
|
||||
upload_protocol = esptool
|
||||
build_type = debug
|
||||
platform = https://github.com/platformio/platform-espressif32.git#develop
|
||||
platform_packages = framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32.git
|
@ -47,17 +47,14 @@ void AXP192_powerevent_IRQ(void) {
|
||||
if (pmu.isBattTempHighIRQ())
|
||||
ESP_LOGI(TAG, "Battery low temperature.");
|
||||
|
||||
// short press -> esp32 deep sleep mode, can be exited by pressing user button
|
||||
#ifdef HAS_BUTTON
|
||||
if (pmu.isPEKShortPressIRQ() && (RTC_runmode == RUNMODE_NORMAL)) {
|
||||
// short press -> esp32 deep sleep mode, can be exited by pressing user button
|
||||
if (pmu.isPEKShortPressIRQ()) {
|
||||
enter_deepsleep(0, HAS_BUTTON);
|
||||
}
|
||||
#endif
|
||||
|
||||
// long press -> shutdown power, can be exited by another longpress
|
||||
if (pmu.isPEKLongtPressIRQ()) {
|
||||
AXP192_power(pmu_power_off); // switch off Lora, GPS, display
|
||||
pmu.shutdown(); // switch off device
|
||||
}
|
||||
|
||||
pmu.clearIRQ();
|
||||
@ -71,25 +68,33 @@ void AXP192_power(pmu_power_t powerlevel) {
|
||||
switch (powerlevel) {
|
||||
|
||||
case pmu_power_off:
|
||||
pmu.setChgLEDMode(AXP20X_LED_OFF);
|
||||
pmu.setPowerOutPut(AXP192_DCDC1, AXP202_OFF);
|
||||
pmu.setPowerOutPut(AXP192_LDO3, AXP202_OFF);
|
||||
pmu.setPowerOutPut(AXP192_LDO2, AXP202_OFF);
|
||||
// pmu.setPowerOutPut(AXP192_DCDC3, AXP202_OFF);
|
||||
pmu.shutdown();
|
||||
break;
|
||||
|
||||
case pmu_power_sleep:
|
||||
pmu.setChgLEDMode(AXP20X_LED_BLINK_1HZ);
|
||||
// we don't cut off DCDC1, because then display blocks i2c bus
|
||||
#ifdef PMU_LED_SLEEP_MODE
|
||||
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_LDO2, AXP202_OFF); // lora off
|
||||
break;
|
||||
|
||||
default: // all rails power on
|
||||
pmu.setPowerOutPut(AXP192_LDO2, AXP202_ON); // Lora on T-Beam V1.0
|
||||
pmu.setPowerOutPut(AXP192_LDO3, AXP202_ON); // Gps on T-Beam V1.0
|
||||
pmu.setPowerOutPut(AXP192_DCDC1, AXP202_ON); // OLED on T-Beam v1.0
|
||||
case pmu_power_on:
|
||||
default:
|
||||
pmu.setPowerOutPut(AXP192_LDO2, AXP202_ON); // Lora on T-Beam V1.0/1.1
|
||||
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);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -119,10 +124,22 @@ void AXP192_init(void) {
|
||||
ESP_LOGI(TAG, "AXP192 PMU initialization failed");
|
||||
else {
|
||||
|
||||
// configure AXP192
|
||||
pmu.setDCDC1Voltage(3300); // for external OLED display
|
||||
pmu.setTimeOutShutdown(false); // no automatic shutdown
|
||||
pmu.setTSmode(AXP_TS_PIN_MODE_DISABLE); // TS pin mode off to save power
|
||||
// configure voltages
|
||||
pmu.setDCDC1Voltage(3300); // for external OLED display
|
||||
pmu.setLDO2Voltage(3300); // LORA VDD 3v3
|
||||
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
|
||||
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_CUR_ADC1, true);
|
||||
|
||||
// switch power rails on
|
||||
AXP192_power(pmu_power_on);
|
||||
|
||||
#ifdef PMU_INT
|
||||
pinMode(PMU_INT, INPUT_PULLUP);
|
||||
attachInterrupt(digitalPinToInterrupt(PMU_INT), PMUIRQ, FALLING);
|
||||
pmu.enableIRQ(AXP202_VBUS_REMOVED_IRQ | AXP202_VBUS_CONNECT_IRQ |
|
||||
AXP202_BATT_REMOVED_IRQ | AXP202_BATT_CONNECT_IRQ |
|
||||
AXP202_CHARGING_FINISHED_IRQ,
|
||||
AXP202_CHARGING_FINISHED_IRQ | AXP202_PEK_SHORTPRESS_IRQ,
|
||||
1);
|
||||
pmu.clearIRQ();
|
||||
#endif // PMU_INT
|
||||
@ -150,6 +164,9 @@ void AXP192_init(void) {
|
||||
pmu.enableChargeing(true);
|
||||
#endif
|
||||
|
||||
// switch power rails on
|
||||
AXP192_power(pmu_power_on);
|
||||
|
||||
ESP_LOGI(TAG, "AXP192 PMU initialized");
|
||||
}
|
||||
}
|
||||
@ -160,11 +177,11 @@ void calibrate_voltage(void) {
|
||||
#ifdef BAT_MEASURE_ADC
|
||||
// configure ADC
|
||||
#ifndef BAT_MEASURE_ADC_UNIT // ADC1
|
||||
ESP_ERROR_CHECK(adc1_config_width(ADC_WIDTH_BIT_12));
|
||||
ESP_ERROR_CHECK(adc1_config_channel_atten(adc_channel, atten));
|
||||
adc1_config_width(ADC_WIDTH_BIT_12);
|
||||
adc1_config_channel_atten(adc_channel, atten);
|
||||
#else // ADC2
|
||||
// ESP_ERROR_CHECK(adc2_config_width(ADC_WIDTH_BIT_12));
|
||||
ESP_ERROR_CHECK(adc2_config_channel_atten(adc_channel, atten));
|
||||
// adc2_config_width(ADC_WIDTH_BIT_12);
|
||||
adc2_config_channel_atten(adc_channel, atten);
|
||||
#endif
|
||||
// calibrate ADC
|
||||
esp_adc_cal_value_t val_type = esp_adc_cal_characterize(
|
||||
@ -199,7 +216,7 @@ uint16_t read_voltage(void) {
|
||||
#else // ADC2
|
||||
int adc_buf = 0;
|
||||
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;
|
||||
}
|
||||
#endif // BAT_MEASURE_ADC_UNIT
|
||||
@ -234,10 +251,11 @@ uint8_t read_battlevel(mapFn_t mapFunction) {
|
||||
|
||||
bool batt_sufficient() {
|
||||
#if (defined HAS_PMU || defined BAT_MEASURE_ADC || defined HAS_IP5306)
|
||||
return (batt_level > OTA_MIN_BATT);
|
||||
#else
|
||||
return true; // we don't know batt level
|
||||
if (batt_level) // we have a battery voltage
|
||||
return (batt_level > OTA_MIN_BATT);
|
||||
else
|
||||
#endif
|
||||
return true; // we don't know batt level
|
||||
}
|
||||
|
||||
#ifdef HAS_IP5306
|
||||
|
199
src/rcommand.cpp
199
src/rcommand.cpp
@ -5,6 +5,9 @@
|
||||
// Local logging tag
|
||||
static const char TAG[] = __FILE__;
|
||||
|
||||
static QueueHandle_t RcmdQueue;
|
||||
TaskHandle_t rcmdTask;
|
||||
|
||||
// set of functions that can be triggered by remote commands
|
||||
void set_reset(uint8_t val[]) {
|
||||
switch (val[0]) {
|
||||
@ -15,7 +18,6 @@ void set_reset(uint8_t val[]) {
|
||||
case 1: // reset MAC counter
|
||||
ESP_LOGI(TAG, "Remote command: reset MAC counter");
|
||||
reset_counters(); // clear macs
|
||||
get_salt(); // get new salt
|
||||
break;
|
||||
case 2: // 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
|
||||
ESP_LOGI(TAG, "Remote command: software update via Wifi");
|
||||
#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
|
||||
break;
|
||||
|
||||
@ -54,14 +60,30 @@ void set_sendcycle(uint8_t val[]) {
|
||||
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[]) {
|
||||
cfg.wifichancycle = val[0];
|
||||
// update Wifi channel rotation timer period
|
||||
xTimerChangePeriod(WifiChanTimer, pdMS_TO_TICKS(cfg.wifichancycle * 10), 100);
|
||||
|
||||
ESP_LOGI(TAG,
|
||||
"Remote command: set Wifi channel switch interval to %.1f seconds",
|
||||
cfg.wifichancycle / float(100));
|
||||
if (cfg.wifichancycle > 0) {
|
||||
if (xTimerIsTimerActive(WifiChanTimer) == pdFALSE)
|
||||
xTimerStart(WifiChanTimer, (TickType_t)0);
|
||||
xTimerChangePeriod(WifiChanTimer, pdMS_TO_TICKS(cfg.wifichancycle * 10),
|
||||
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[]) {
|
||||
@ -96,7 +118,6 @@ void set_countmode(uint8_t val[]) {
|
||||
return;
|
||||
}
|
||||
reset_counters(); // clear macs
|
||||
get_salt(); // get new salt
|
||||
}
|
||||
|
||||
void set_screensaver(uint8_t val[]) {
|
||||
@ -215,18 +236,18 @@ void set_loraadr(uint8_t val[]) {
|
||||
|
||||
void set_blescan(uint8_t val[]) {
|
||||
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;
|
||||
if (cfg.blescan)
|
||||
start_BLEscan();
|
||||
else {
|
||||
macs_ble = 0; // clear BLE counter
|
||||
else
|
||||
stop_BLEscan();
|
||||
}
|
||||
}
|
||||
|
||||
void set_wifiscan(uint8_t val[]) {
|
||||
ESP_LOGI(TAG, "Remote command: set WIFI scanner to %s",
|
||||
val[0] ? "on" : "off");
|
||||
macs_wifi = 0; // clear WIFI counter
|
||||
cfg.wifiscan = val[0] ? 1 : 0;
|
||||
switch_wifi_sniffer(cfg.wifiscan);
|
||||
}
|
||||
@ -240,15 +261,15 @@ void set_wifiant(uint8_t val[]) {
|
||||
#endif
|
||||
}
|
||||
|
||||
void set_vendorfilter(uint8_t val[]) {
|
||||
ESP_LOGI(TAG, "Remote command: set vendorfilter mode to %s",
|
||||
void set_macfilter(uint8_t val[]) {
|
||||
ESP_LOGI(TAG, "Remote command: set macfilter mode to %s",
|
||||
val[0] ? "on" : "off");
|
||||
cfg.vendorfilter = val[0] ? 1 : 0;
|
||||
cfg.macfilter = val[0] ? 1 : 0;
|
||||
}
|
||||
|
||||
void set_rgblum(uint8_t val[]) {
|
||||
// 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);
|
||||
};
|
||||
|
||||
@ -273,16 +294,16 @@ void get_config(uint8_t val[]) {
|
||||
ESP_LOGI(TAG, "Remote command: get device configuration");
|
||||
payload.reset();
|
||||
payload.addConfig(cfg);
|
||||
SendPayload(CONFIGPORT, prio_high);
|
||||
SendPayload(CONFIGPORT);
|
||||
};
|
||||
|
||||
void get_status(uint8_t val[]) {
|
||||
ESP_LOGI(TAG, "Remote command: get device status");
|
||||
payload.reset();
|
||||
payload.addStatus(read_voltage(), uptime() / 1000, temperatureRead(),
|
||||
getFreeRAM(), rtc_get_reset_reason(0),
|
||||
payload.addStatus(read_voltage(), (uint64_t)(uptime() / 1000ULL),
|
||||
temperatureRead(), getFreeRAM(), rtc_get_reset_reason(0),
|
||||
rtc_get_reset_reason(1));
|
||||
SendPayload(STATUSPORT, prio_high);
|
||||
SendPayload(STATUSPORT);
|
||||
};
|
||||
|
||||
void get_gps(uint8_t val[]) {
|
||||
@ -292,7 +313,7 @@ void get_gps(uint8_t val[]) {
|
||||
gps_storelocation(&gps_status);
|
||||
payload.reset();
|
||||
payload.addGPS(gps_status);
|
||||
SendPayload(GPSPORT, prio_high);
|
||||
SendPayload(GPSPORT);
|
||||
#else
|
||||
ESP_LOGW(TAG, "GPS function not supported");
|
||||
#endif
|
||||
@ -303,7 +324,7 @@ void get_bme(uint8_t val[]) {
|
||||
#if (HAS_BME)
|
||||
payload.reset();
|
||||
payload.addBME(bme_status);
|
||||
SendPayload(BMEPORT, prio_high);
|
||||
SendPayload(BMEPORT);
|
||||
#else
|
||||
ESP_LOGW(TAG, "BME sensor not supported");
|
||||
#endif
|
||||
@ -314,7 +335,7 @@ void get_batt(uint8_t val[]) {
|
||||
#if (defined BAT_MEASURE_ADC || defined HAS_PMU)
|
||||
payload.reset();
|
||||
payload.addVoltage(read_voltage());
|
||||
SendPayload(BATTPORT, prio_normal);
|
||||
SendPayload(BATTPORT);
|
||||
#else
|
||||
ESP_LOGW(TAG, "Battery voltage not supported");
|
||||
#endif
|
||||
@ -325,7 +346,7 @@ void get_time(uint8_t val[]) {
|
||||
payload.reset();
|
||||
payload.addTime(now());
|
||||
payload.addByte(timeStatus() << 4 | timeSource);
|
||||
SendPayload(TIMEPORT, prio_high);
|
||||
SendPayload(TIMEPORT);
|
||||
};
|
||||
|
||||
void set_time(uint8_t val[]) {
|
||||
@ -348,39 +369,49 @@ void set_enscount(uint8_t val[]) {
|
||||
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
|
||||
// format: opcode, function, #bytes params,
|
||||
// flag (true = do make settings persistent / false = don't)
|
||||
//
|
||||
// format: {opcode, function, number of function arguments}
|
||||
|
||||
static const cmd_t table[] = {
|
||||
{0x01, set_rssi, 1, true}, {0x02, set_countmode, 1, true},
|
||||
{0x03, set_gps, 1, true}, {0x04, set_display, 1, true},
|
||||
{0x05, set_loradr, 1, true}, {0x06, set_lorapower, 1, true},
|
||||
{0x07, set_loraadr, 1, true}, {0x08, set_screensaver, 1, true},
|
||||
{0x09, set_reset, 1, false}, {0x0a, set_sendcycle, 1, true},
|
||||
{0x0b, set_wifichancycle, 1, true}, {0x0c, set_blescantime, 1, true},
|
||||
{0x0d, set_vendorfilter, 1, false}, {0x0e, set_blescan, 1, true},
|
||||
{0x0f, set_wifiant, 1, true}, {0x10, set_rgblum, 1, true},
|
||||
{0x11, set_monitor, 1, true}, {0x12, set_beacon, 7, false},
|
||||
{0x13, set_sensor, 2, true}, {0x14, set_payloadmask, 1, true},
|
||||
{0x15, set_bme, 1, true}, {0x16, set_batt, 1, true},
|
||||
{0x17, set_wifiscan, 1, true}, {0x18, set_enscount, 1, true},
|
||||
{0x80, get_config, 0, false}, {0x81, get_status, 0, false},
|
||||
{0x83, get_batt, 0, false}, {0x84, get_gps, 0, false},
|
||||
{0x85, get_bme, 0, false}, {0x86, get_time, 0, false},
|
||||
{0x87, set_time, 0, false}, {0x99, set_flush, 0, false}};
|
||||
{0x01, set_rssi, 1}, {0x02, set_countmode, 1},
|
||||
{0x03, set_gps, 1}, {0x04, set_display, 1},
|
||||
{0x05, set_loradr, 1}, {0x06, set_lorapower, 1},
|
||||
{0x07, set_loraadr, 1}, {0x08, set_screensaver, 1},
|
||||
{0x09, set_reset, 1}, {0x0a, set_sendcycle, 1},
|
||||
{0x0b, set_wifichancycle, 1}, {0x0c, set_blescantime, 1},
|
||||
{0x0d, set_macfilter, 1}, {0x0e, set_blescan, 1},
|
||||
{0x0f, set_wifiant, 1}, {0x10, set_rgblum, 1},
|
||||
{0x11, set_monitor, 1}, {0x12, set_beacon, 7},
|
||||
{0x13, set_sensor, 2}, {0x14, set_payloadmask, 1},
|
||||
{0x15, set_bme, 1}, {0x16, set_batt, 1},
|
||||
{0x17, set_wifiscan, 1}, {0x18, set_enscount, 1},
|
||||
{0x19, set_sleepcycle, 1}, {0x20, set_loadconfig, 0},
|
||||
{0x21, set_saveconfig, 0}, {0x80, get_config, 0},
|
||||
{0x81, get_status, 0}, {0x83, get_batt, 0},
|
||||
{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 =
|
||||
sizeof(table) / sizeof(table[0]); // number of commands in command table
|
||||
|
||||
// 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)
|
||||
return;
|
||||
|
||||
uint8_t foundcmd[cmdlength], cursor = 0;
|
||||
bool storeflag = false;
|
||||
|
||||
while (cursor < cmdlength) {
|
||||
|
||||
@ -392,24 +423,82 @@ void rcommand(const uint8_t cmd[], const uint8_t cmdlength) {
|
||||
memmove(foundcmd, cmd + cursor,
|
||||
table[i].params); // strip opcode from cmd array
|
||||
cursor += table[i].params;
|
||||
if (table[i].store) // ceck if function needs to store configuration
|
||||
storeflag = true;
|
||||
table[i].func(
|
||||
foundcmd); // execute assigned function with given parameters
|
||||
} else
|
||||
ESP_LOGI(
|
||||
TAG,
|
||||
"Remote command x%02X called with missing parameter(s), skipped",
|
||||
table[i].opcode);
|
||||
break; // command found -> exit table lookup loop
|
||||
} // end of command validation
|
||||
} // end of command table lookup loop
|
||||
ESP_LOGI(TAG,
|
||||
"Remote command x%02X called with missing parameter(s), "
|
||||
"skipped",
|
||||
table[i].opcode);
|
||||
break; // command found -> exit table lookup loop
|
||||
} // end of command validation
|
||||
} // end of command table lookup loop
|
||||
|
||||
if (i < 0) { // command not found -> exit parser
|
||||
ESP_LOGI(TAG, "Unknown remote command x%02X, ignored", cmd[cursor]);
|
||||
break;
|
||||
}
|
||||
} // command parsing loop
|
||||
|
||||
if (storeflag)
|
||||
saveConfig();
|
||||
} // rcmd_execute()
|
||||
|
||||
// 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()
|
||||
|
||||
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()
|
157
src/reset.cpp
157
src/reset.cpp
@ -5,28 +5,38 @@
|
||||
// Local logging tag
|
||||
static const char TAG[] = __FILE__;
|
||||
|
||||
// variable keep its values after restart or wakeup from sleep
|
||||
RTC_NOINIT_ATTR runmode_t RTC_runmode;
|
||||
// Conversion factor for micro seconds to seconds
|
||||
#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) {
|
||||
if (warmstart) {
|
||||
// store LMIC keys and counters in RTC memory
|
||||
ESP_LOGI(TAG, "restarting device (warmstart), keeping runmode %d",
|
||||
RTC_runmode);
|
||||
ESP_LOGI(TAG, "restarting device (warmstart)");
|
||||
} else {
|
||||
#if (HAS_LORA)
|
||||
if (RTC_runmode == RUNMODE_NORMAL)
|
||||
if (RTC_runmode == RUNMODE_NORMAL) {
|
||||
LMIC_shutdown();
|
||||
}
|
||||
#endif
|
||||
RTC_runmode = RUNMODE_POWERCYCLE;
|
||||
ESP_LOGI(TAG, "restarting device (coldstart), set runmode %d", RTC_runmode);
|
||||
ESP_LOGI(TAG, "restarting device (coldstart)");
|
||||
}
|
||||
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 RTCWDT_BROWN_OUT_RESET: // 0x0f Reset when the vdd voltage is not
|
||||
@ -39,10 +49,16 @@ void do_after_reset(int reason) {
|
||||
break;
|
||||
|
||||
case DEEPSLEEP_RESET: // 0x05 Deep Sleep reset digital core
|
||||
RTC_runmode = RUNMODE_WAKEUP;
|
||||
#if (HAS_LORA)
|
||||
// to be done: restore LoRaWAN channel configuration and datarate here
|
||||
#endif
|
||||
// calculate time spent in deep sleep
|
||||
gettimeofday(&sleep_stop_time, NULL);
|
||||
sleep_time_ms =
|
||||
(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;
|
||||
|
||||
case SW_RESET: // 0x03 Software reset digital core
|
||||
@ -61,58 +77,111 @@ void do_after_reset(int reason) {
|
||||
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))
|
||||
return;
|
||||
ESP_LOGI(TAG, "Preparing to sleep...");
|
||||
|
||||
// assure LMIC is in safe state
|
||||
#if (HAS_LORA)
|
||||
if (os_queryTimeCriticalJobs(ms2osticks(10000)))
|
||||
return;
|
||||
RTC_runmode = RUNMODE_SLEEP;
|
||||
int i;
|
||||
|
||||
// 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
|
||||
|
||||
// set up power domains
|
||||
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_SLOW_MEM, ESP_PD_OPTION_ON);
|
||||
|
||||
// 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);
|
||||
}
|
||||
// stop MAC processing
|
||||
vTaskDelete(macProcessTask);
|
||||
|
||||
// halt interrupts accessing i2c bus
|
||||
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
|
||||
dp_shutdown();
|
||||
#endif
|
||||
|
||||
// switch off wifi & ble
|
||||
#if (BLECOUNTER)
|
||||
stop_BLEscan();
|
||||
#endif
|
||||
|
||||
// reduce power if has PMU
|
||||
// reduce power if has PMU or VEXT
|
||||
#ifdef HAS_PMU
|
||||
AXP192_power(pmu_power_sleep);
|
||||
#elif EXT_POWER_SW
|
||||
digitalWrite(EXT_POWER_SW, EXT_POWER_OFF);
|
||||
#endif
|
||||
|
||||
// shutdown i2c bus
|
||||
i2c_deinit();
|
||||
|
||||
// enter sleep mode
|
||||
ESP_LOGI(TAG, "Going to sleep...");
|
||||
// configure wakeup sources
|
||||
// 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();
|
||||
}
|
||||
}
|
||||
|
||||
unsigned long long uptime() { return (RTC_millis + millis()); }
|
@ -22,11 +22,12 @@ bool sdcard_init() {
|
||||
|
||||
#if HAS_SDCARD == 1 // use SD SPI host driver
|
||||
useSDCard = SD.begin(SDCARD_CS, SDCARD_MOSI, SDCARD_MISO, SDCARD_SCLK);
|
||||
//SPI.begin(SDCARD_SCLK, SDCARD_MSO, SDCARD_MOSI, SDCARD_CS);
|
||||
//delay(10);
|
||||
//useSDCard = SD.begin(SDCARD_CS, SPI, 40000000, "/sd");
|
||||
|
||||
#elif HAS_SDCARD == 2 // use SD MMC host driver
|
||||
// enable internal pullups of sd-data lines
|
||||
gpio_set_pull_mode(gpio_num_t(SDCARD_DATA0), GPIO_PULLUP_ONLY);
|
||||
gpio_set_pull_mode(gpio_num_t(SDCARD_DATA1), GPIO_PULLUP_ONLY);
|
||||
gpio_set_pull_mode(gpio_num_t(SDCARD_DATA2), GPIO_PULLUP_ONLY);
|
||||
gpio_set_pull_mode(gpio_num_t(SDCARD_DATA3), GPIO_PULLUP_ONLY);
|
||||
useSDCard = SD_MMC.begin();
|
||||
#endif
|
||||
|
||||
@ -38,7 +39,8 @@ bool sdcard_init() {
|
||||
return useSDCard;
|
||||
}
|
||||
|
||||
void sdcardWriteData(uint16_t noWifi, uint16_t noBle, __attribute__((unused)) uint16_t noBleCWA) {
|
||||
void sdcardWriteData(uint16_t noWifi, uint16_t noBle,
|
||||
__attribute__((unused)) uint16_t noBleCWA) {
|
||||
static int counterWrites = 0;
|
||||
char tempBuffer[12 + 1];
|
||||
time_t t = now();
|
||||
@ -103,7 +105,7 @@ void createFile(void) {
|
||||
ESP_LOGD(TAG, "SD: name opened: <%s>", bufferFilename);
|
||||
fileSDCard.print(SDCARD_FILE_HEADER);
|
||||
#if (COUNT_ENS)
|
||||
fileSDCard.print(SDCARD_FILE_HEADER_CWA); // for Corona-data (CWA)
|
||||
fileSDCard.print(SDCARD_FILE_HEADER_CWA); // for Corona-data (CWA)
|
||||
#endif
|
||||
#if (HAS_SDS011)
|
||||
fileSDCard.print(SDCARD_FILE_HEADER_SDS011);
|
||||
|
@ -4,17 +4,17 @@
|
||||
Ticker sendTimer;
|
||||
|
||||
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
|
||||
void SendPayload(uint8_t port, sendprio_t prio) {
|
||||
void SendPayload(uint8_t port) {
|
||||
|
||||
MessageBuffer_t
|
||||
SendBuffer; // contains MessageSize, MessagePort, MessagePrio, Message[]
|
||||
ESP_LOGD(TAG, "sending Payload for Port %d", port);
|
||||
|
||||
MessageBuffer_t SendBuffer; // contains MessageSize, MessagePort, Message[]
|
||||
|
||||
SendBuffer.MessageSize = payload.getSize();
|
||||
SendBuffer.MessagePrio = prio;
|
||||
|
||||
switch (PAYLOAD_ENCODER) {
|
||||
case 1: // plain -> no mapping
|
||||
@ -110,11 +110,10 @@ void sendData() {
|
||||
sds011_store(&sds_status);
|
||||
payload.addSDS(sds_status);
|
||||
#endif
|
||||
SendPayload(COUNTERPORT, prio_normal);
|
||||
SendPayload(COUNTERPORT);
|
||||
// clear counter if not in cumulative counter mode
|
||||
if (cfg.countermode != 1) {
|
||||
reset_counters(); // clear macs container and reset all counters
|
||||
get_salt(); // get new salt for salting hashes
|
||||
ESP_LOGI(TAG, "Counter cleared");
|
||||
}
|
||||
#ifdef HAS_DISPLAY
|
||||
@ -128,7 +127,7 @@ void sendData() {
|
||||
case MEMS_DATA:
|
||||
payload.reset();
|
||||
payload.addBME(bme_status);
|
||||
SendPayload(BMEPORT, prio_normal);
|
||||
SendPayload(BMEPORT);
|
||||
break;
|
||||
#endif
|
||||
|
||||
@ -140,7 +139,7 @@ void sendData() {
|
||||
gps_storelocation(&gps_status);
|
||||
payload.reset();
|
||||
payload.addGPS(gps_status);
|
||||
SendPayload(GPSPORT, prio_high);
|
||||
SendPayload(GPSPORT);
|
||||
} else
|
||||
ESP_LOGD(TAG, "No valid GPS position");
|
||||
}
|
||||
@ -152,7 +151,7 @@ void sendData() {
|
||||
case SENSOR1_DATA:
|
||||
payload.reset();
|
||||
payload.addSensor(sensor_read(1));
|
||||
SendPayload(SENSOR1PORT, prio_normal);
|
||||
SendPayload(SENSOR1PORT);
|
||||
#if (COUNT_ENS)
|
||||
if (cfg.countermode != 1)
|
||||
cwa_clear();
|
||||
@ -163,14 +162,14 @@ void sendData() {
|
||||
case SENSOR2_DATA:
|
||||
payload.reset();
|
||||
payload.addSensor(sensor_read(2));
|
||||
SendPayload(SENSOR2PORT, prio_normal);
|
||||
SendPayload(SENSOR2PORT);
|
||||
break;
|
||||
#endif
|
||||
#if (HAS_SENSOR_3)
|
||||
case SENSOR3_DATA:
|
||||
payload.reset();
|
||||
payload.addSensor(sensor_read(3));
|
||||
SendPayload(SENSOR3PORT, prio_normal);
|
||||
SendPayload(SENSOR3PORT);
|
||||
break;
|
||||
#endif
|
||||
#endif
|
||||
@ -179,7 +178,7 @@ void sendData() {
|
||||
case BATT_DATA:
|
||||
payload.reset();
|
||||
payload.addVoltage(read_voltage());
|
||||
SendPayload(BATTPORT, prio_normal);
|
||||
SendPayload(BATTPORT);
|
||||
break;
|
||||
#endif
|
||||
|
||||
@ -187,10 +186,10 @@ void sendData() {
|
||||
bitmask &= ~mask;
|
||||
mask <<= 1;
|
||||
} // while (bitmask)
|
||||
|
||||
} // sendData()
|
||||
|
||||
void flushQueues() {
|
||||
void flushQueues(void) {
|
||||
rcmd_queuereset();
|
||||
#if (HAS_LORA)
|
||||
lora_queuereset();
|
||||
#endif
|
||||
@ -201,3 +200,17 @@ void flushQueues() {
|
||||
mqtt_queuereset();
|
||||
#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;
|
||||
}
|
||||
|
@ -6,7 +6,6 @@
|
||||
#include "payload.h"
|
||||
#include "corona.h"
|
||||
#include "macsniff.h"
|
||||
|
||||
extern PayloadConvert payload;
|
||||
#endif
|
||||
|
||||
@ -58,7 +57,7 @@ uint8_t *sensor_read(uint8_t sensor) {
|
||||
// note: Sensor1 fields are used for ENS count, if ENS detection enabled
|
||||
#if (COUNT_ENS)
|
||||
if (cfg.enscount)
|
||||
payload.addCount(cwa_report(), MAC_SNIFF_BLE_CWA);
|
||||
payload.addCount(cwa_report(), MAC_SNIFF_BLE_ENS);
|
||||
#else
|
||||
buf[0] = length;
|
||||
buf[1] = 0x01;
|
||||
|
@ -43,7 +43,7 @@ static const char TAG[] = __FILE__;
|
||||
DMA_ATTR uint8_t txbuf[BUFFER_SIZE];
|
||||
DMA_ATTR uint8_t rxbuf[BUFFER_SIZE];
|
||||
|
||||
QueueHandle_t SPISendQueue;
|
||||
static QueueHandle_t SPISendQueue;
|
||||
|
||||
TaskHandle_t spiTask;
|
||||
|
||||
@ -57,7 +57,8 @@ void spi_slave_task(void *param) {
|
||||
memset(rxbuf, 0, sizeof(rxbuf));
|
||||
|
||||
// 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!");
|
||||
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
|
||||
ESP_LOGI(TAG, "Prepared SPI transaction for %zu byte(s)", transaction_size);
|
||||
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_LOGI(TAG, "Transaction finished with size %zu bits",
|
||||
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
|
||||
if ((spi_transaction.trans_len) && ((rxbuf[2]) == RCMDPORT)) {
|
||||
rcommand(rxbuf + HEADER_SIZE, spi_transaction.trans_len - HEADER_SIZE);
|
||||
@ -103,8 +106,10 @@ void spi_slave_task(void *param) {
|
||||
}
|
||||
}
|
||||
|
||||
esp_err_t spi_init() {
|
||||
assert(SEND_QUEUE_SIZE);
|
||||
void spi_deinit(void) { vTaskDelete(spiTask); }
|
||||
|
||||
esp_err_t spi_init(void) {
|
||||
_ASSERT(SEND_QUEUE_SIZE > 0);
|
||||
SPISendQueue = xQueueCreate(SEND_QUEUE_SIZE, sizeof(MessageBuffer_t));
|
||||
if (SPISendQueue == 0) {
|
||||
ESP_LOGE(TAG, "Could not create SPI send queue. Aborting.");
|
||||
@ -149,27 +154,12 @@ esp_err_t spi_init() {
|
||||
|
||||
void spi_enqueuedata(MessageBuffer_t *message) {
|
||||
// enqueue message in SPI send queue
|
||||
BaseType_t ret;
|
||||
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)
|
||||
if (xQueueSendToBack(SPISendQueue, (void *)message, (TickType_t)0) != pdTRUE)
|
||||
ESP_LOGW(TAG, "SPI sendqueue is full");
|
||||
}
|
||||
|
||||
void spi_queuereset(void) { xQueueReset(SPISendQueue); }
|
||||
|
||||
uint32_t spi_queuewaiting(void) { return uxQueueMessagesWaiting(SPISendQueue); }
|
||||
|
||||
#endif // HAS_SPI
|
@ -85,7 +85,7 @@ void IRAM_ATTR setMyTime(uint32_t t_sec, uint16_t t_msec,
|
||||
vTaskDelay(pdMS_TO_TICKS(1000 - t_msec % 1000));
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "[%0.3f] UTC time: %d.%03d sec", millis() / 1000.0,
|
||||
ESP_LOGI(TAG, "[%0.3f] UTC time: %d.%03d sec", _seconds(),
|
||||
time_to_set, t_msec % 1000);
|
||||
|
||||
// if we have got an external timesource, set RTC time and shift RTC_INT pulse
|
||||
@ -106,11 +106,12 @@ void IRAM_ATTR setMyTime(uint32_t t_sec, uint16_t t_msec,
|
||||
timeSource = mytimesource; // set global variable
|
||||
timesyncer.attach(TIME_SYNC_INTERVAL * 60, setTimeSyncIRQ);
|
||||
ESP_LOGD(TAG, "[%0.3f] Timesync finished, time was set | source: %c",
|
||||
millis() / 1000.0, timeSetSymbols[mytimesource]);
|
||||
_seconds(), timeSetSymbols[mytimesource]);
|
||||
} else {
|
||||
timesyncer.attach(TIME_SYNC_INTERVAL_RETRY * 60, setTimeSyncIRQ);
|
||||
ESP_LOGD(TAG, "[%0.3f] Timesync failed, invalid time fetched | source: %c",
|
||||
millis() / 1000.0, timeSetSymbols[mytimesource]);
|
||||
time_t unix_sec_at_compilation = compiledUTC();
|
||||
ESP_LOGD(TAG, "[%0.3f] Failed to synchronise time from source %c | unix sec obtained from source: %d | unix sec at program compilation: %d",
|
||||
_seconds(), timeSetSymbols[mytimesource], time_to_set, unix_sec_at_compilation);
|
||||
}
|
||||
}
|
||||
|
||||
@ -245,7 +246,7 @@ void clock_init(void) {
|
||||
&ClockTask, // task handle
|
||||
1); // CPU core
|
||||
|
||||
assert(ClockTask); // has clock task started?
|
||||
_ASSERT(ClockTask != NULL); // has clock task started?
|
||||
} // clock_init
|
||||
|
||||
void clock_loop(void *taskparameter) { // ClockTask
|
||||
|
@ -47,7 +47,7 @@ void timesync_request(void) {
|
||||
// start timesync handshake
|
||||
else {
|
||||
ESP_LOGI(TAG, "[%0.3f] Timeserver sync request started, seqNo#%d",
|
||||
millis() / 1000.0, time_sync_seqNo);
|
||||
_seconds(), time_sync_seqNo);
|
||||
xTaskNotifyGive(timeSyncProcTask); // unblock timesync task
|
||||
}
|
||||
}
|
||||
@ -82,13 +82,13 @@ void IRAM_ATTR timesync_processReq(void *taskparameter) {
|
||||
}
|
||||
|
||||
// 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
|
||||
#if (TIME_SYNC_LORASERVER) // ask user's timeserver (for LoRAWAN < 1.0.3)
|
||||
payload.reset();
|
||||
payload.addByte(time_sync_seqNo);
|
||||
SendPayload(TIMEPORT, prio_high);
|
||||
SendPayload(TIMEPORT);
|
||||
#elif (TIME_SYNC_LORAWAN) // ask network (requires LoRAWAN >= 1.0.3)
|
||||
LMIC_requestNetworkTime(timesync_serverAnswer, &time_sync_seqNo);
|
||||
// trigger to immediately get DevTimeAns from class A device
|
||||
@ -98,14 +98,14 @@ void IRAM_ATTR timesync_processReq(void *taskparameter) {
|
||||
if (xTaskNotifyWait(0x00, ULONG_MAX, &rcv_seqNo,
|
||||
pdMS_TO_TICKS(TIME_SYNC_TIMEOUT * 1000)) == pdFALSE) {
|
||||
ESP_LOGW(TAG, "[d%0.3f] Timesync aborted: timed out",
|
||||
millis() / 1000.0);
|
||||
_seconds());
|
||||
goto Fail; // no timestamp received before timeout
|
||||
}
|
||||
|
||||
// check if we are in handshake with server
|
||||
if (rcv_seqNo != time_sync_seqNo) {
|
||||
ESP_LOGW(TAG, "[%0.3f] Timesync aborted: handshake out of sync",
|
||||
millis() / 1000.0);
|
||||
_seconds());
|
||||
goto Fail;
|
||||
}
|
||||
|
||||
@ -148,7 +148,7 @@ void IRAM_ATTR timesync_processReq(void *taskparameter) {
|
||||
// send timesync end char to show timesync was successful
|
||||
payload.reset();
|
||||
payload.addByte(TIME_SYNC_END_FLAG);
|
||||
SendPayload(TIMEPORT, prio_high);
|
||||
SendPayload(TIMEPORT);
|
||||
goto Finish;
|
||||
|
||||
Fail:
|
||||
@ -166,7 +166,7 @@ void IRAM_ATTR timesync_processReq(void *taskparameter) {
|
||||
|
||||
// store incoming timestamps
|
||||
void timesync_store(uint32_t timestamp, timesync_t timestamp_type) {
|
||||
ESP_LOGD(TAG, "[%0.3f] seq#%d[%d]: t%d=%d", millis() / 1000.0,
|
||||
ESP_LOGD(TAG, "[%0.3f] seq#%d[%d]: t%d=%d", _seconds(),
|
||||
time_sync_seqNo, sample_idx, timestamp_type, timestamp);
|
||||
timesync_timestamp[sample_idx][timestamp_type] = timestamp;
|
||||
}
|
||||
@ -214,10 +214,10 @@ void IRAM_ATTR timesync_serverAnswer(void *pUserData, int flag) {
|
||||
if (flag != TIME_SYNC_FRAME_LENGTH) {
|
||||
if (rcv_seqNo == TIME_SYNC_END_FLAG)
|
||||
ESP_LOGI(TAG, "[%0.3f] Timeserver error: no confident time available",
|
||||
millis() / 1000.0);
|
||||
_seconds());
|
||||
else
|
||||
ESP_LOGW(TAG, "[%0.3f] Timeserver error: spurious data received",
|
||||
millis() / 1000.0);
|
||||
_seconds());
|
||||
goto Exit; // failure
|
||||
}
|
||||
|
||||
@ -233,13 +233,13 @@ void IRAM_ATTR timesync_serverAnswer(void *pUserData, int flag) {
|
||||
|
||||
if (flag != 1) {
|
||||
ESP_LOGW(TAG, "[%0.3f] Network did not answer time request",
|
||||
millis() / 1000.0);
|
||||
_seconds());
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
// Populate lmic_time_reference
|
||||
if ((LMIC_getNetworkTimeReference(&lmicTime)) != 1) {
|
||||
ESP_LOGW(TAG, "[%0.3f] Network time request failed", millis() / 1000.0);
|
||||
ESP_LOGW(TAG, "[%0.3f] Network time request failed", _seconds());
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
@ -267,7 +267,7 @@ Finish:
|
||||
rc = 1;
|
||||
} else {
|
||||
ESP_LOGW(TAG, "[%0.3f] Timeserver error: outdated time received",
|
||||
millis() / 1000.0);
|
||||
_seconds());
|
||||
}
|
||||
|
||||
Exit:
|
||||
|
@ -7,10 +7,6 @@ static const char TAG[] = "wifi";
|
||||
|
||||
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 {
|
||||
unsigned frame_ctrl : 16;
|
||||
unsigned duration_id : 16;
|
||||
@ -26,7 +22,7 @@ typedef struct {
|
||||
uint8_t payload[0]; // network data ended with 4 bytes csum (CRC32)
|
||||
} wifi_ieee80211_packet_t;
|
||||
|
||||
// using IRAM_:ATTR here to speed up callback function
|
||||
// using IRAM_ATTR here to speed up callback function
|
||||
IRAM_ATTR void wifi_sniffer_packet_handler(void *buff,
|
||||
wifi_promiscuous_pkt_type_t type) {
|
||||
|
||||
@ -35,64 +31,70 @@ IRAM_ATTR void wifi_sniffer_packet_handler(void *buff,
|
||||
(wifi_ieee80211_packet_t *)ppkt->payload;
|
||||
const wifi_ieee80211_mac_hdr_t *hdr = &ipkt->hdr;
|
||||
|
||||
if ((cfg.rssilimit) &&
|
||||
(ppkt->rx_ctrl.rssi < cfg.rssilimit)) // rssi is negative value
|
||||
ESP_LOGD(TAG, "WiFi RSSI %d -> ignoring (limit: %d)", ppkt->rx_ctrl.rssi,
|
||||
cfg.rssilimit);
|
||||
else // count seen MAC
|
||||
// process seen MAC
|
||||
#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
|
||||
void switchWifiChannel(TimerHandle_t xTimer) {
|
||||
configASSERT(xTimer);
|
||||
channel =
|
||||
(channel % WIFI_CHANNEL_MAX) + 1; // rotate channel 1..WIFI_CHANNEL_MAX
|
||||
esp_wifi_set_channel(channel, WIFI_SECOND_CHAN_NONE);
|
||||
}
|
||||
|
||||
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 = {
|
||||
// .filter_mask = WIFI_PROMIS_FILTER_MASK_MGMT}; // only MGMT frames
|
||||
// .filter_mask = WIFI_PROMIS_FILTER_MASK_ALL}; // we use all frames
|
||||
wifi_country_t wifi_country = {WIFI_MY_COUNTRY, WIFI_CHANNEL_MIN,
|
||||
WIFI_CHANNEL_MAX, 100,
|
||||
WIFI_COUNTRY_POLICY_MANUAL};
|
||||
|
||||
wifi_promiscuous_filter_t filter = {.filter_mask =
|
||||
WIFI_PROMIS_FILTER_MASK_MGMT |
|
||||
WIFI_PROMIS_FILTER_MASK_DATA};
|
||||
wifi_init_config_t wifi_cfg = WIFI_INIT_CONFIG_DEFAULT();
|
||||
wifi_cfg.event_handler = NULL; // we don't need a wifi event handler
|
||||
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
|
||||
ESP_ERROR_CHECK(
|
||||
esp_wifi_set_country(&wifi_country)); // set locales for RF and channels
|
||||
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
|
||||
wifi_promiscuous_filter_t wifi_filter = {.filter_mask =
|
||||
WIFI_PROMIS_FILTER_MASK_MGMT |
|
||||
WIFI_PROMIS_FILTER_MASK_DATA};
|
||||
|
||||
// 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 =
|
||||
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);
|
||||
}
|
||||
|
||||
void switch_wifi_sniffer(uint8_t state) {
|
||||
assert(WifiChanTimer);
|
||||
if (state) {
|
||||
// switch wifi sniffer on
|
||||
ESP_ERROR_CHECK(esp_wifi_start());
|
||||
xTimerStart(WifiChanTimer, 0);
|
||||
// start sniffer
|
||||
esp_wifi_start();
|
||||
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 {
|
||||
// switch wifi sniffer off
|
||||
xTimerStop(WifiChanTimer, 0);
|
||||
// start channel hopping timer
|
||||
if (xTimerIsTimerActive(WifiChanTimer) != pdFALSE)
|
||||
xTimerStop(WifiChanTimer, (TickType_t)0);
|
||||
// stop sniffer
|
||||
esp_wifi_set_promiscuous(false);
|
||||
ESP_ERROR_CHECK(esp_wifi_stop());
|
||||
macs_wifi = 0; // clear WIFI counter
|
||||
esp_wifi_stop();
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user