diff --git a/README.md b/README.md
index 0241a3c4..79634f3a 100644
--- a/README.md
+++ b/README.md
@@ -16,15 +16,13 @@ Tutorial (in german language): https://www.heise.de/select/make/2019/1/155109923
# 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
@@ -183,16 +181,20 @@ 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
@@ -211,7 +213,7 @@ This describes how to set up a mobile PaxCounter:
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 gives an indication how many people staying in proximity are using Apps for tracing COVID-19 exposures, e.g. in Germany the "Corona Warn App". To achive best 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 +305,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]
@@ -437,17 +439,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 +488,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 +518,11 @@ 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]
+
0x80 get device configuration
Device answers with it's current configuration on Port 3.
diff --git a/include/cyclic.h b/include/cyclic.h
index 6bd9b2a7..8c7319be 100644
--- a/include/cyclic.h
+++ b/include/cyclic.h
@@ -15,7 +15,6 @@ extern Ticker cyclicTimer;
void setCyclicIRQ(void);
void doHousekeeping(void);
-uint64_t uptime(void);
void reset_counters(void);
uint32_t getFreeRAM();
diff --git a/include/globals.h b/include/globals.h
index 39a42f79..c8755412 100644
--- a/include/globals.h
+++ b/include/globals.h
@@ -18,25 +18,28 @@
#include "mallocator.h"
#include
+#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)
+#define COUNT_DATA _bit(1)
+#define ALARM_DATA _bit(2)
+#define MEMS_DATA _bit(3)
+#define GPS_DATA _bit(4)
+#define SENSOR1_DATA _bit(5)
+#define SENSOR2_DATA _bit(6)
+#define SENSOR3_DATA _bit(7)
+#define BATT_DATA _bit(8)
// 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 GPS_MODE _bit(1)
+#define ALARM_MODE _bit(2)
+#define BEACON_MODE _bit(3)
+#define UPDATE_MODE _bit(4)
+#define FILTER_MODE _bit(5)
+#define ANTENNA_MODE _bit(6)
+#define BLE_MODE _bit(7)
+#define SCREEN_MODE _bit(8)
// length of display buffer for lmic event messages
#define LMIC_EVENTMSG_LEN 17
@@ -56,19 +59,16 @@
; \
}
-// emulate millis to avoid rollovers
-#define _millis() esp_timer_get_time() / 1000
-#define _micros() esp_timer_get_time()
-#define _seconds() _millis() / 1000.0
+#define _seconds() millis() / 1000.0
-enum sendprio_t { prio_low, prio_normal, prio_high };
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,
RUNMODE_NORMAL,
RUNMODE_WAKEUP,
- RUNMODE_UPDATE
+ RUNMODE_UPDATE,
+ RUNMODE_SLEEP
};
// Struct holding devices's runtime configuration
@@ -84,12 +84,13 @@ typedef struct __attribute__((packed)) {
uint8_t countermode; // 0=cyclic unconfirmed, 1=cumulative, 2=cyclic confirmed
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
@@ -106,7 +107,6 @@ typedef struct __attribute__((packed)) {
typedef struct {
uint8_t MessageSize;
uint8_t MessagePort;
- sendprio_t MessagePrio;
uint8_t Message[PAYLOAD_BUFFER_SIZE];
} MessageBuffer_t;
diff --git a/include/irqhandler.h b/include/irqhandler.h
index 712e5e41..9e52ccbf 100644
--- a/include/irqhandler.h
+++ b/include/irqhandler.h
@@ -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(1)
+#define BUTTON_IRQ _bitl(2)
+#define SENDCYCLE_IRQ _bitl(3)
+#define CYCLIC_IRQ _bitl(4)
+#define TIMESYNC_IRQ _bitl(5)
+#define MASK_IRQ _bitl(6)
+#define UNMASK_IRQ _bitl(7)
+#define BME_IRQ _bitl(8)
+#define MATRIX_DISPLAY_IRQ _bitl(9)
+#define PMU_IRQ _bitl(10)
#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();
diff --git a/include/lorawan.h b/include/lorawan.h
index 3e54175d..e2239739 100644
--- a/include/lorawan.h
+++ b/include/lorawan.h
@@ -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,
diff --git a/include/mqttclient.h b/include/mqttclient.h
index 9b7ac649..6e351e2a 100644
--- a/include/mqttclient.h
+++ b/include/mqttclient.h
@@ -6,17 +6,15 @@
#include
#include
-#define MQTT_ETHERNET 0 // set to 0 to run on Wifi
+#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 "broker.shiftr.io"
-//#define MQTT_CLIENTNAME "arduino"
-#define MQTT_USER "try"
-#define MQTT_PASSWD "try"
+#define MQTT_SERVER "paxcounter.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_TIMEOUT 1000 // timeout for all mqtt commands in milliseconds
#ifndef MQTT_CLIENTNAME
#define MQTT_CLIENTNAME clientId.c_str()
@@ -25,14 +23,14 @@
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 NetworkEvent(WiFiEvent_t event);
esp_err_t mqtt_init(void);
+void mqtt_deinit(void);
#endif // _MQTTCLIENT_H
\ No newline at end of file
diff --git a/include/rcommand.h b/include/rcommand.h
index 488b7005..c26bce84 100644
--- a/include/rcommand.h
+++ b/include/rcommand.h
@@ -18,11 +18,13 @@
// 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;
+extern bool rcmd_busy;
+
void rcommand(const uint8_t cmd[], const uint8_t cmdlength);
void do_reset(bool warmstart);
diff --git a/include/reset.h b/include/reset.h
index be40bd3a..af9d6c50 100644
--- a/include/reset.h
+++ b/include/reset.h
@@ -10,7 +10,8 @@
#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, const gpio_num_t wakeup_gpio);
+unsigned long long uptime(void);
#endif // _RESET_H
\ No newline at end of file
diff --git a/include/senddata.h b/include/senddata.h
index dcebb809..c99b1f5a 100644
--- a/include/senddata.h
+++ b/include/senddata.h
@@ -15,10 +15,11 @@
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_
diff --git a/include/spislave.h b/include/spislave.h
index 6db35d00..78b7a737 100644
--- a/include/spislave.h
+++ b/include/spislave.h
@@ -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
diff --git a/include/vendor_array.h b/include/vendor_array.h
deleted file mode 100644
index b3bc7744..00000000
--- a/include/vendor_array.h
+++ /dev/null
@@ -1,243 +0,0 @@
-std::array 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,
-};
\ No newline at end of file
diff --git a/include/wifiscan.h b/include/wifiscan.h
index 0b212a9d..53a414c3 100644
--- a/include/wifiscan.h
+++ b/include/wifiscan.h
@@ -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,
diff --git a/platformio_orig.ini b/platformio_orig.ini
index 7f63d4e7..3e6a6b9d 100644
--- a/platformio_orig.ini
+++ b/platformio_orig.ini
@@ -47,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.43
+release_version = 2.1.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
@@ -60,7 +60,7 @@ 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.9.0
bitbank2/BitBang_I2C @ ^2.1.3
@@ -116,7 +116,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}
@@ -134,5 +134,4 @@ 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
+platform = https://github.com/platformio/platform-espressif32.git#develop
\ No newline at end of file
diff --git a/src/blecsan.cpp b/src/blecsan.cpp
index b06e78a0..8b8a8207 100644
--- a/src/blecsan.cpp
+++ b/src/blecsan.cpp
@@ -128,7 +128,7 @@ IRAM_ATTR void gap_callback_handler(esp_gap_ble_cb_event_t event,
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:
@@ -136,7 +136,7 @@ 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;
}
@@ -151,7 +151,7 @@ IRAM_ATTR void gap_callback_handler(esp_gap_ble_cb_event_t event,
ESP_LOGV(TAG, "RSSI : %d", p->scan_rst.rssi);
#endif
-#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
@@ -176,7 +176,7 @@ IRAM_ATTR void gap_callback_handler(esp_gap_ble_cb_event_t event,
mac_add((uint8_t *)p->scan_rst.bda, p->scan_rst.rssi, MAC_SNIFF_BLE);
#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()
//
@@ -187,7 +187,7 @@ IRAM_ATTR void gap_callback_handler(esp_gap_ble_cb_event_t event,
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 ...
+ 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
@@ -214,35 +214,53 @@ IRAM_ATTR void gap_callback_handler(esp_gap_ble_cb_event_t event,
} // 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 when gap event occurs, 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,35 @@ 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");
#endif // BLECOUNTER
+ } else {
+ ESP_LOGE(TAG, "Bluetooth controller start failed. Resetting device");
+ do_reset(true);
+ }
+
} // 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
diff --git a/src/bmesensor.cpp b/src/bmesensor.cpp
index 7d747c6d..9ba31fcf 100644
--- a/src/bmesensor.cpp
+++ b/src/bmesensor.cpp
@@ -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;
@@ -218,6 +217,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 +228,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++;
}
diff --git a/src/button.cpp b/src/button.cpp
index dbd34b07..7a64bd07 100644
--- a/src/button.cpp
+++ b/src/button.cpp
@@ -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
diff --git a/src/configmanager.cpp b/src/configmanager.cpp
index f947f383..8a9da4ce 100644
--- a/src/configmanager.cpp
+++ b/src/configmanager.cpp
@@ -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
diff --git a/src/corona.cpp b/src/corona.cpp
index 8a2fd979..a8dd85a4 100644
--- a/src/corona.cpp
+++ b/src/corona.cpp
@@ -44,7 +44,7 @@ bool cwa_init(void) {
}
void cwa_mac_add(uint16_t hashedmac) {
- cwaSeenNotifiers[hashedmac] = _millis(); // hash last seen at ....
+ cwaSeenNotifiers[hashedmac] = millis(); // hash last seen at ....
}
#endif
diff --git a/src/cyclic.cpp b/src/cyclic.cpp
index e65eaac2..a927a21e 100644
--- a/src/cyclic.cpp
+++ b/src/cyclic.cpp
@@ -20,8 +20,6 @@ void setCyclicIRQ() {
// 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
@@ -136,8 +134,6 @@ void doHousekeeping() {
} // doHousekeeping()
-uint64_t uptime() { return _millis(); }
-
uint32_t getFreeRAM() {
#ifndef BOARD_HAS_PSRAM
return ESP.getFreeHeap();
diff --git a/src/display.cpp b/src/display.cpp
index 43be8d80..4ccb1b7e 100644
--- a/src/display.cpp
+++ b/src/display.cpp
@@ -603,7 +603,6 @@ void dp_shutdown(void) {
if (!I2C_MUTEX_LOCK())
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
diff --git a/src/hal/olimexpoeiso.h b/src/hal/olimexpoeiso.h
index 4fcbdd6b..dcc32e43 100644
--- a/src/hal/olimexpoeiso.h
+++ b/src/hal/olimexpoeiso.h
@@ -8,15 +8,15 @@
#include
// 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
diff --git a/src/hal/ttgobeam10.h b/src/hal/ttgobeam10.h
index f7cae001..b014051b 100644
--- a/src/hal/ttgobeam10.h
+++ b/src/hal/ttgobeam10.h
@@ -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
diff --git a/src/i2c.cpp b/src/i2c.cpp
index dcecc797..226d08c8 100644
--- a/src/i2c.cpp
+++ b/src/i2c.cpp
@@ -7,12 +7,7 @@ static const char TAG[] = __FILE__;
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(); }
void i2c_scan(void) {
@@ -38,10 +33,13 @@ void i2c_scan(void) {
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"};
+ 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...");
diff --git a/src/irqhandler.cpp b/src/irqhandler.cpp
index 82b1690c..244910ad 100644
--- a/src/irqhandler.cpp
+++ b/src/irqhandler.cpp
@@ -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()
diff --git a/src/led.cpp b/src/led.cpp
index a0708558..b0b302d6 100644
--- a/src/led.cpp
+++ b/src/led.cpp
@@ -9,7 +9,7 @@ led_states previousLEDState =
TaskHandle_t ledLoopTask;
uint16_t LEDColor = COLOR_NONE, LEDBlinkDuration = 0; // state machine variables
-unsigned long LEDBlinkStarted = 0; // When (in _millis() led blink started)
+unsigned long LEDBlinkStarted = 0; // When (in millis() led blink started)
#ifdef HAS_RGB_LED
@@ -133,7 +133,7 @@ void blink_LED(uint16_t set_color, uint16_t set_blinkduration) {
#if (HAS_LED != NOT_A_PIN) || defined(HAS_RGB_LED)
LEDColor = set_color; // set color for RGB LED
LEDBlinkDuration = set_blinkduration; // duration
- LEDBlinkStarted = _millis(); // Time Start here
+ LEDBlinkStarted = millis(); // Time Start here
LEDState = LED_ON; // Let main set LED on
#endif
}
@@ -145,8 +145,8 @@ void ledLoop(void *parameter) {
// Custom blink running always have priority other LoRaWAN led
// management
if (LEDBlinkStarted && LEDBlinkDuration) {
- // Custom blink is finished, let this order, avoid _millis() overflow
- if ((_millis() - LEDBlinkStarted) >= LEDBlinkDuration) {
+ // Custom blink is finished, let this order, avoid millis() overflow
+ if ((long)(millis() - LEDBlinkStarted) >= LEDBlinkDuration) {
// Led becomes off, and stop blink
LEDState = LED_OFF;
LEDBlinkStarted = 0;
@@ -165,7 +165,7 @@ void ledLoop(void *parameter) {
LEDColor = COLOR_YELLOW;
// quick blink 20ms on each 1/5 second
LEDState =
- ((_millis() % 200) < 20) ? LED_ON : LED_OFF; // TX data pending
+ ((millis() % 200) < 20) ? LED_ON : LED_OFF; // TX data pending
} else if (LMIC.opmode & (OP_TXDATA | OP_TXRXPEND)) {
// select color to blink by message port
switch (LMIC.pendTxPort) {
@@ -180,13 +180,13 @@ void ledLoop(void *parameter) {
break;
}
// small blink 10ms on each 1/2sec (not when joining)
- LEDState = ((_millis() % 500) < 10) ? LED_ON : LED_OFF;
+ LEDState = ((millis() % 500) < 10) ? LED_ON : LED_OFF;
// This should not happen so indicate a problem
} else if (LMIC.opmode &
((OP_TXDATA | OP_TXRXPEND | OP_JOINING | OP_REJOIN) == 0)) {
LEDColor = COLOR_RED;
// heartbeat long blink 200ms on each 2 seconds
- LEDState = ((_millis() % 2000) < 200) ? LED_ON : LED_OFF;
+ LEDState = ((millis() % 2000) < 200) ? LED_ON : LED_OFF;
} else
#endif // HAS_LORA
{
diff --git a/src/lorawan.cpp b/src/lorawan.cpp
index 97865bf6..aa0e9ccd 100644
--- a/src/lorawan.cpp
+++ b/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,11 +19,6 @@ 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;
TaskHandle_t lmicTask = NULL, lorasendTask = NULL;
@@ -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
@@ -197,59 +193,47 @@ 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) {
+esp_err_t lmic_init(void) {
_ASSERT(SEND_QUEUE_SIZE > 0);
LoraSendQueue = xQueueCreate(SEND_QUEUE_SIZE, sizeof(MessageBuffer_t));
if (LoraSendQueue == 0) {
@@ -259,89 +243,6 @@ esp_err_t lora_stack_init(bool do_join) {
ESP_LOGI(TAG, "LORA send queue created, size %d Bytes",
SEND_QUEUE_SIZE * sizeof(MessageBuffer_t));
- // start lorawan stack
- ESP_LOGI(TAG, "Starting LMIC...");
- xTaskCreatePinnedToCore(lmictask, // task function
- "lmictask", // name of task
- 4096, // stack size of task
- (void *)1, // parameter of the task
- 2, // priority of the task
- &lmicTask, // task handle
- 1); // CPU core
-
-#ifdef LORA_ABP
- // Pass ABP parameters to LMIC_setSession
- lora_stack_reset();
- uint8_t appskey[sizeof(APPSKEY)];
- uint8_t nwkskey[sizeof(NWKSKEY)];
- memcpy_P(appskey, APPSKEY, sizeof(APPSKEY));
- memcpy_P(nwkskey, NWKSKEY, sizeof(NWKSKEY));
- LMIC_setSession(NETID, DEVADDR, nwkskey, appskey);
- // These parameters are defined as macro in loraconf.h
- setABPParameters();
-#else
- // Start join procedure if not already joined,
- // lora_setupForNetwork(true) is called by eventhandler when joined
- // else continue current session
- if (do_join) {
- if (!LMIC_startJoining())
- ESP_LOGI(TAG, "Already joined");
- } else {
- lora_stack_reset();
- LMIC_setSession(RTCnetid, RTCdevaddr, RTCnwkKey, RTCartKey);
- LMIC.seqnoUp = RTCseqnoUp;
- LMIC.seqnoDn = RTCseqnoDn;
- }
-#endif
- // start lmic send task
- xTaskCreatePinnedToCore(lora_send, // task function
- "lorasendtask", // name of task
- 3072, // stack size of task
- (void *)1, // parameter of the task
- 1, // priority of the task
- &lorasendTask, // task handle
- 1); // CPU core
-
- return ESP_OK;
-}
-
-void lora_enqueuedata(MessageBuffer_t *message) {
- // enqueue message in LORA send queue
- BaseType_t ret = pdFALSE;
- MessageBuffer_t DummyBuffer;
- sendprio_t prio = message->MessagePrio;
-
- switch (prio) {
- case prio_high:
- // clear some space in queue if full, then fallthrough to prio_normal
- if (uxQueueSpacesAvailable(LoraSendQueue) == 0) {
- xQueueReceive(LoraSendQueue, &DummyBuffer, (TickType_t)0);
- ESP_LOGW(TAG, "LORA sendqueue purged, data is lost");
- }
- case prio_normal:
- ret = xQueueSendToFront(LoraSendQueue, (void *)message, (TickType_t)0);
- break;
- case prio_low:
- default:
- ret = xQueueSendToBack(LoraSendQueue, (void *)message, (TickType_t)0);
- break;
- }
- if (ret != pdTRUE) {
- snprintf(lmic_event_msg + 14, LMIC_EVENTMSG_LEN - 14, "<>");
- ESP_LOGW(TAG, "LORA sendqueue is full");
- } else {
- // add Lora send queue length to display
- snprintf(lmic_event_msg + 14, LMIC_EVENTMSG_LEN - 14, "%2u",
- uxQueueMessagesWaiting(LoraSendQueue));
- }
-}
-
-void lora_queuereset(void) { xQueueReset(LoraSendQueue); }
-
-// LMIC lorawan stack task
-void lmictask(void *pvParameters) {
- _ASSERT((uint32_t)pvParameters == 1);
-
// setup LMIC stack
os_init_ex(&myPinmap); // initialize lmic run-time environment
@@ -355,7 +256,7 @@ void lmictask(void *pvParameters) {
// Reset the MAC state. Session and pending data transfers will be
// 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,
@@ -365,11 +266,83 @@ 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();
+ }
+ // otherwise start join procedure if not already joined
+ else {
+ 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) {
@@ -410,7 +383,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:
@@ -560,4 +533,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
\ No newline at end of file
diff --git a/src/macsniff.cpp b/src/macsniff.cpp
index 7a5063c7..6fefc07c 100644
--- a/src/macsniff.cpp
+++ b/src/macsniff.cpp
@@ -3,10 +3,6 @@
#include "globals.h"
#include "macsniff.h"
-#if (VENDORFILTER)
-#include "vendor_array.h"
-#endif
-
// Local logging tag
static const char TAG[] = __FILE__;
@@ -58,7 +54,7 @@ esp_err_t macQueueInit() {
xTaskCreatePinnedToCore(mac_process, // task function
"mac_process", // name of task
- 2048, // stack size of task
+ 3072, // stack size of task
(void *)1, // parameter of the task
1, // priority of the task
&macProcessTask, // task handle
@@ -126,19 +122,10 @@ uint16_t mac_analyze(MacBuffer_t MacBuffer) {
#endif
payload.reset();
payload.addAlarm(MacBuffer.rssi, beaconID);
- SendPayload(BEACONPORT, prio_high);
+ SendPayload(BEACONPORT);
}
};
-#if (VENDORFILTER)
- uint32_t *oui; // temporary buffer for vendor OUI
- oui = (uint32_t *)MacBuffer.mac;
- // if we find OUI on vendor filter list we don't analyze and return early
- if (std::find(vendors.begin(), vendors.end(), __builtin_bswap32(*oui) >> 8) ==
- vendors.end())
- return 0;
-#endif
-
char buff[10]; // temporary buffer for printf
uint32_t *mac; // temporary buffer for shortened MAC
diff --git a/src/main.cpp b/src/main.cpp
index 6a6d2d0b..b52ce977 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -68,7 +68,6 @@ TIMESYNC_IRQ -> setTimeSyncIRQ()
CYCLIC_IRQ -> setCyclicIRQ()
SENDCYCLE_IRQ -> setSendIRQ()
BME_IRQ -> setBMEIRQ()
-MQTT_IRQ -> setMqttIRQ()
ClockTask (Core 1), see timekeeper.cpp
@@ -88,8 +87,8 @@ 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 batt_level = 0; // display value
-uint8_t volatile channel = 0; // channel rotation counter
-uint8_t volatile rf_load = 0; // RF traffic indicator
+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;
@@ -138,7 +137,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)
@@ -208,7 +207,8 @@ void setup() {
_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
@@ -292,7 +292,7 @@ void setup() {
macQueueInit();
// 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) {
@@ -303,9 +303,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
@@ -348,9 +348,7 @@ 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
@@ -376,7 +374,7 @@ void setup() {
strcat_P(features, " SDS");
#endif
-#if (VENDORFILTER)
+#if (MACFILTER)
strcat_P(features, " FILTER");
#endif
@@ -414,16 +412,16 @@ 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();
+ // 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
diff --git a/src/mqttclient.cpp b/src/mqttclient.cpp
index 511f02b5..867f06db 100644
--- a/src/mqttclient.cpp
+++ b/src/mqttclient.cpp
@@ -11,13 +11,17 @@ Ticker mqttTimer;
WiFiClient netClient;
MQTTClient mqttClient;
+void mqtt_deinit(void) {
+ mqttClient.onMessageAdvanced(NULL);
+ mqttClient.disconnect();
+ vTaskDelete(mqttTask);
+}
+
esp_err_t mqtt_init(void) {
// setup network connection
WiFi.onEvent(NetworkEvent);
ETH.begin();
- // WiFi.mode(WIFI_STA);
- // WiFi.begin("SSID", "PASSWORD");
// setup mqtt client
mqttClient.begin(MQTT_SERVER, MQTT_PORT, netClient);
@@ -33,7 +37,6 @@ 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);
return ESP_OK;
@@ -42,8 +45,7 @@ esp_err_t mqtt_init(void) {
int mqtt_connect(const char *my_host, const uint16_t my_port) {
IPAddress mqtt_server_ip;
- // static String clientId = "paxcounter-" + ETH.macAddress();
- static String clientId = "paxcounter-" + String(random(0xffff), HEX);
+ static String clientId = "paxcounter-" + ETH.macAddress();
ESP_LOGI(TAG, "MQTT name is %s", MQTT_CLIENTNAME);
@@ -114,61 +116,43 @@ void mqtt_client_task(void *param) {
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()) {
+ // check for incoming messages
+ mqttClient.loop();
+
+ // 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;
+
+ // send data to mqtt server
char buffer[PAYLOAD_BUFFER_SIZE + 3];
snprintf(buffer, msg.MessageSize + 3, "%u/%s", msg.MessagePort,
msg.Message);
if (mqttClient.publish(MQTT_OUTTOPIC, buffer)) {
ESP_LOGI(TAG, "%d byte(s) sent to MQTT server", msg.MessageSize + 2);
- continue;
- } else {
- mqtt_enqueuedata(&msg); // postpone the undelivered message
- ESP_LOGD(TAG,
- "Couldn't sent message to MQTT server, message postponed");
- }
-
+ // delete sent item from queue
+ 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(),
+ ESP_LOGD(TAG, "MQTT error = %d / rc = %d", mqttClient.lastError(),
mqttClient.returnCode());
- mqtt_enqueuedata(&msg); // postpone the undelivered message
+ ESP_LOGD(TAG, "MQTT client reconnecting...");
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;
-
- 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;
- }
- if (ret != pdTRUE)
+ if (xQueueSendToBack(MQTTSendQueue, (void *)message, (TickType_t)0) != pdTRUE)
ESP_LOGW(TAG, "MQTT sendqueue is full");
}
@@ -178,13 +162,10 @@ void mqtt_callback(MQTTClient *client, char topic[], char payload[],
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); }
+
+uint32_t mqtt_queuewaiting(void) {
+ return uxQueueMessagesWaiting(MQTTSendQueue);
}
-void mqtt_queuereset(void) { xQueueReset(MQTTSendQueue); }
-void setMqttIRQ(void) { xTaskNotify(irqHandlerTask, MQTT_IRQ, eSetBits); }
-
#endif // HAS_MQTT
\ No newline at end of file
diff --git a/src/ota.cpp b/src/ota.cpp
index 70dea1a0..6b94a193 100644
--- a/src/ota.cpp
+++ b/src/ota.cpp
@@ -178,9 +178,9 @@ int do_ota_update() {
client.print("Cache-Control: no-cache\r\n");
client.print("Connection: close\r\n\r\n");
- unsigned long timeout = _millis();
+ 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;
diff --git a/src/paxcounter_orig.conf b/src/paxcounter_orig.conf
index 0a7b88d1..4c1a7225 100644
--- a/src/paxcounter_orig.conf
+++ b/src/paxcounter_orig.conf
@@ -11,12 +11,13 @@
// 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
// MAC sniffing parameters
-#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
+#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]
@@ -26,11 +27,11 @@
#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 to enable data transfer of user sensor #1 (also used as ENS counter) [default=0]
+#define HAS_SENSOR_1 1 // set to 1 to enable data transfer of user sensor #1 (also used as ENS counter) [default=0]
#define HAS_SENSOR_2 0 // set to 1 to enable data transfer of user sensor #2 [default=0]
#define HAS_SENSOR_3 0 // set to 1 to enable data transfer of user sensor #3 [default=0]
@@ -83,10 +84,10 @@
#define RESPONSE_TIMEOUT_MS 60000 // firmware binary server connection timeout [milliseconds]
// 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]
diff --git a/src/payload.cpp b/src/payload.cpp
index 112a544d..904e454c 100644
--- a/src/payload.cpp
+++ b/src/payload.cpp
@@ -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);
}
diff --git a/src/power.cpp b/src/power.cpp
index ee07aba9..a1fbea58 100644
--- a/src/power.cpp
+++ b/src/power.cpp
@@ -48,14 +48,13 @@ void AXP192_powerevent_IRQ(void) {
ESP_LOGI(TAG, "Battery low temperature.");
// short press -> esp32 deep sleep mode, can be exited by pressing user button
- if (pmu.isPEKShortPressIRQ() && (RTC_runmode == RUNMODE_NORMAL)) {
+ if (pmu.isPEKShortPressIRQ()) {
enter_deepsleep(0, HAS_BUTTON);
}
// 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();
@@ -69,27 +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
- pmu.setPowerOutPut(AXP192_DCDC2, AXP202_OFF); // unused on T-Beam v1.0
- pmu.setPowerOutPut(AXP192_EXTEN, AXP202_OFF); // unused 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,12 +124,22 @@ void AXP192_init(void) {
ESP_LOGI(TAG, "AXP192 PMU initialization failed");
else {
- // configure AXP192
- pmu.setDCDC1Voltage(3300); // for external OLED display
- pmu.setLDO2Voltage(3300); // LORA VDD 3v3
- pmu.setLDO3Voltage(3300); // GPS VDD 3v3
- 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);
@@ -132,16 +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_PEK_SHORTPRESS_IRQ |
- AXP202_PEK_LONGPRESS_IRQ,
+ AXP202_CHARGING_FINISHED_IRQ | AXP202_PEK_SHORTPRESS_IRQ,
1);
pmu.clearIRQ();
#endif // PMU_INT
@@ -153,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");
}
}
@@ -163,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(
@@ -202,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
diff --git a/src/rcommand.cpp b/src/rcommand.cpp
index 75899eca..d69bb0cc 100644
--- a/src/rcommand.cpp
+++ b/src/rcommand.cpp
@@ -5,6 +5,9 @@
// Local logging tag
static const char TAG[] = __FILE__;
+// global variable indicating if rcommand() is executing
+bool rcmd_busy = false;
+
// set of functions that can be triggered by remote commands
void set_reset(uint8_t val[]) {
switch (val[0]) {
@@ -54,14 +57,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[]) {
@@ -215,18 +234,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 +259,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 +292,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 +311,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 +322,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 +333,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 +344,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[]) {
@@ -359,16 +378,17 @@ static const cmd_t table[] = {
{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},
+ {0x0d, set_macfilter, 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}};
+ {0x19, set_sleepcycle, 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}};
static const uint8_t cmdtablesize =
sizeof(table) / sizeof(table[0]); // number of commands in command table
@@ -381,6 +401,7 @@ void rcommand(const uint8_t cmd[], const uint8_t cmdlength) {
uint8_t foundcmd[cmdlength], cursor = 0;
bool storeflag = false;
+ rcmd_busy = true;
while (cursor < cmdlength) {
@@ -397,10 +418,10 @@ void rcommand(const uint8_t cmd[], const uint8_t cmdlength) {
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);
+ 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
@@ -412,4 +433,7 @@ void rcommand(const uint8_t cmd[], const uint8_t cmdlength) {
if (storeflag)
saveConfig();
+
+ rcmd_busy = false;
+
} // rcommand()
diff --git a/src/reset.cpp b/src/reset.cpp
index 8c52bf35..472ed02c 100644
--- a/src/reset.cpp
+++ b/src/reset.cpp
@@ -5,103 +5,161 @@
// 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_DATA_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;
- case POWERON_RESET: // 0x01 Vbat power on reset
- case RTCWDT_BROWN_OUT_RESET: // 0x0f Reset when the vdd voltage is not
- // stable
- RTC_runmode = RUNMODE_POWERCYCLE;
- break;
+ switch (esp_sleep_get_wakeup_cause()) {
+ case ESP_SLEEP_WAKEUP_EXT0: // Wakeup caused by external signal using RTC_IO
+ case ESP_SLEEP_WAKEUP_EXT1: // Wakeup caused by external signal using
+ // RTC_CNTL
+ case ESP_SLEEP_WAKEUP_TIMER: // Wakeup caused by timer
+ case ESP_SLEEP_WAKEUP_TOUCHPAD: // Wakeup caused by touchpad
+ case ESP_SLEEP_WAKEUP_ULP: // Wakeup caused by ULP program
- case SW_CPU_RESET: // 0x0c Software reset CPU
- // keep previous runmode (could be RUNMODE_UPDATE)
- break;
+ // 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
- 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
break;
- case SW_RESET: // 0x03 Software reset digital core
- case OWDT_RESET: // 0x04 Legacy watch dog reset digital core
- case SDIO_RESET: // 0x06 Reset by SLC module, reset digital core
- case TG0WDT_SYS_RESET: // 0x07 Timer Group0 Watch dog reset digital core
- case TG1WDT_SYS_RESET: // 0x08 Timer Group1 Watch dog reset digital core
- case RTCWDT_SYS_RESET: // 0x09 RTC Watch dog Reset digital core
- case INTRUSION_RESET: // 0x0a Instrusion tested to reset CPU
- case TGWDT_CPU_RESET: // 0x0b Time Group reset CPU
- case RTCWDT_CPU_RESET: // 0x0d RTC Watch dog Reset CPU
- case EXT_CPU_RESET: // 0x0e for APP CPU, reseted by PRO CPU
- case RTCWDT_RTC_RESET: // 0x10 RTC Watch dog reset digital core and rtc mode
+ case ESP_SLEEP_WAKEUP_ALL:
+ case ESP_SLEEP_WAKEUP_GPIO:
+ case ESP_SLEEP_WAKEUP_UART:
+ case ESP_SLEEP_WAKEUP_UNDEFINED:
default:
+ // not a deep sleep reset
RTC_runmode = RUNMODE_POWERCYCLE;
break;
- }
+ } // switch
- 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 = 60,
+ gpio_num_t wakeup_gpio = GPIO_NUM_MAX) {
- if ((!wakeup_sec) && (!wakeup_gpio) && (RTC_runmode == RUNMODE_NORMAL))
- return;
-
-// assure LMIC is in safe state
#if (HAS_LORA)
- if (os_queryTimeCriticalJobs(ms2osticks(10000)))
+ if (!LMIC.devaddr) {
+ ESP_LOGI(TAG, "Can't go to sleep while joining");
return;
-
- // to be done: save LoRaWAN channel configuration here
-
+ }
#endif
- // set up power domains
- esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_SLOW_MEM, ESP_PD_OPTION_ON);
+ int i;
- // set wakeup timer
- if (wakeup_sec)
- esp_sleep_enable_timer_wakeup(wakeup_sec * 1000000);
+ // validate wake up pin, if we have
+ if (!GPIO_IS_VALID_GPIO(wakeup_gpio))
+ wakeup_gpio = GPIO_NUM_MAX;
- // 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);
- }
+ ESP_LOGI(TAG, "Preparing to sleep...");
+
+ RTC_runmode = RUNMODE_SLEEP;
+
+ // stop further enqueuing of senddata and MAC processing
+ sendTimer.detach();
+
+ // switch off radio
+#if (WIFICOUNTER)
+ switch_wifi_sniffer(0);
+#endif
+#if (BLECOUNTER)
+ stop_BLEscan();
+ btStop();
+#endif
+
+ // stop MAC processing
+ vTaskDelete(macProcessTask);
// halt interrupts accessing i2c bus
mask_user_IRQ();
-// switch off display
-#ifdef HAS_DISPLAY
- dp_shutdown();
+ // wait a while (max 100 sec) to clear send queues
+ ESP_LOGI(TAG, "Waiting until send queues are empty...");
+ for (i = 10; i > 0; i--) {
+ if (!allQueuesEmtpy())
+ vTaskDelay(pdMS_TO_TICKS(10000));
+ else
+ break;
+ }
+ if (i == 0)
+ goto Error;
+
+ // shutdown LMIC safely, waiting max 100 sec
+#if (HAS_LORA)
+ ESP_LOGI(TAG, "Waiting until LMIC is idle...");
+ for (i = 10; i > 0; i--) {
+ if ((LMIC.opmode & OP_TXRXPEND) ||
+ os_queryTimeCriticalJobs(sec2osticks(wakeup_sec)))
+ vTaskDelay(pdMS_TO_TICKS(10000));
+ else
+ break;
+ }
+ if (i == 0)
+ goto Error;
+#endif // (HAS_LORA)
+
+// shutdown MQTT safely
+#ifdef HAS_MQTT
+ mqtt_deinit();
#endif
-// switch off wifi & ble
-#if (BLECOUNTER)
- stop_BLEscan();
+// shutdown SPI safely
+#ifdef HAS_SPI
+ spi_deinit();
+#endif
+
+ // wait until rcommands are all done
+ for (i = 10; i > 0; i--) {
+ if (rcmd_busy)
+ vTaskDelay(pdMS_TO_TICKS(1000));
+ else
+ break;
+ }
+ if (i == 0)
+ goto Error;
+
+ // 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
// reduce power if has PMU
@@ -112,7 +170,29 @@ void enter_deepsleep(const int wakeup_sec, const gpio_num_t wakeup_gpio) {
// 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();
-}
\ No newline at end of file
+
+Error:
+ ESP_LOGE(TAG, "Can't go to sleep. Resetting.");
+ do_reset(true);
+}
+
+unsigned long long uptime() { return (RTC_millis + millis()); }
\ No newline at end of file
diff --git a/src/senddata.cpp b/src/senddata.cpp
index 1cc09503..6c10be82 100644
--- a/src/senddata.cpp
+++ b/src/senddata.cpp
@@ -8,13 +8,14 @@ void setSendIRQ() {
}
// 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) {
+
+ ESP_LOGD(TAG, "sending Payload for Port %d", port);
MessageBuffer_t
- SendBuffer; // contains MessageSize, MessagePort, MessagePrio, Message[]
+ SendBuffer; // contains MessageSize, MessagePort, Message[]
SendBuffer.MessageSize = payload.getSize();
- SendBuffer.MessagePrio = prio;
switch (PAYLOAD_ENCODER) {
case 1: // plain -> no mapping
@@ -110,7 +111,7 @@ 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
@@ -128,7 +129,7 @@ void sendData() {
case MEMS_DATA:
payload.reset();
payload.addBME(bme_status);
- SendPayload(BMEPORT, prio_normal);
+ SendPayload(BMEPORT);
break;
#endif
@@ -140,7 +141,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 +153,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 +164,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 +180,7 @@ void sendData() {
case BATT_DATA:
payload.reset();
payload.addVoltage(read_voltage());
- SendPayload(BATTPORT, prio_normal);
+ SendPayload(BATTPORT);
break;
#endif
@@ -187,10 +188,9 @@ void sendData() {
bitmask &= ~mask;
mask <<= 1;
} // while (bitmask)
-
} // sendData()
-void flushQueues() {
+void flushQueues(void) {
#if (HAS_LORA)
lora_queuereset();
#endif
@@ -201,3 +201,17 @@ void flushQueues() {
mqtt_queuereset();
#endif
}
+
+bool allQueuesEmtpy(void) {
+ uint32_t rc = 0;
+#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;
+}
diff --git a/src/spislave.cpp b/src/spislave.cpp
index 3e581318..d0778384 100644
--- a/src/spislave.cpp
+++ b/src/spislave.cpp
@@ -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,7 +106,9 @@ void spi_slave_task(void *param) {
}
}
-esp_err_t spi_init() {
+void spi_deinit(void) { vTaskDelete(spiTask); }
+
+esp_err_t spi_init(void) {
_ASSERT(SEND_QUEUE_SIZE > 0);
SPISendQueue = xQueueCreate(SEND_QUEUE_SIZE, sizeof(MessageBuffer_t));
if (SPISendQueue == 0) {
@@ -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
\ No newline at end of file
diff --git a/src/timekeeper.cpp b/src/timekeeper.cpp
index 1d4663ab..e4c08a89 100644
--- a/src/timekeeper.cpp
+++ b/src/timekeeper.cpp
@@ -26,7 +26,7 @@ Ticker timesyncer;
void setTimeSyncIRQ() { xTaskNotify(irqHandlerTask, TIMESYNC_IRQ, eSetBits); }
void calibrateTime(void) {
- ESP_LOGD(TAG, "[%0.3f] calibrateTime, timeSource == %d", _millis() / 1000.0,
+ ESP_LOGD(TAG, "[%0.3f] calibrateTime, timeSource == %d", millis() / 1000.0,
timeSource);
time_t t = 0;
uint16_t t_msec = 0;
diff --git a/src/timesync.cpp b/src/timesync.cpp
index bdeea67b..55a0d82f 100644
--- a/src/timesync.cpp
+++ b/src/timesync.cpp
@@ -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
@@ -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:
diff --git a/src/wifiscan.cpp b/src/wifiscan.cpp
index d6325fcd..c876803c 100644
--- a/src/wifiscan.cpp
+++ b/src/wifiscan.cpp
@@ -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;
@@ -35,61 +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;
- // process seen MAC
- mac_add((uint8_t *)hdr->addr2, ppkt->rx_ctrl.rssi, MAC_SNIFF_WIFI);
+// 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) {
- // static uint8_t channel = 0; // channel rotation counter
- _ASSERT(xTimer != NULL);
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 != NULL);
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();
}
}
\ No newline at end of file