Merge pull request #855 from cyberman54/development

v3.2.0
This commit is contained in:
Verkehrsrot 2022-02-16 21:20:12 +01:00 committed by GitHub
commit 19ade61e66
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
52 changed files with 580 additions and 638 deletions

View File

@ -202,7 +202,7 @@ Output of sensor and peripheral data is internally switched by a bitmask registe
# Power saving mode # 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. 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 / SD-Card). Set *#define SLEEPCYCLE* in paxcounter.conf to enable power saving mode and to specify the duration of a sleep cycle. Power consumption in deep sleep mode depends on your hardware, i.e. if on board peripherals can be switched off or set to a chip specific sleep mode either by MCU or by power management unit (PMU) as found on TTGO T-BEAM v1.0/V1.1. See *power.cpp* for power management, and *reset.cpp* for sleep and wakeup logic.
# Time sync # Time sync
@ -216,22 +216,13 @@ Paxcounter can be used to sync a wall clock which has a DCF77 or IF482 time tele
This describes how to set up a mobile PaxCounter:<br> Follow all steps so far for preparing the device, selecting the packed payload format. In `paxcounter.conf` set PAYLOAD_OPENSENSEBOX to 1. Register a new sensebox on https://opensensemap.org/. In the sensor configuration select "TheThingsNetwork" and set decoding profile to "LoRa serialization". Enter your TTN Application and Device ID. Setup decoding option using `[{"decoder":"latLng"},{"decoder":"uint16",sensor_id":"yoursensorid"}]` This describes how to set up a mobile PaxCounter:<br> Follow all steps so far for preparing the device, selecting the packed payload format. In `paxcounter.conf` set PAYLOAD_OPENSENSEBOX to 1. Register a new sensebox on https://opensensemap.org/. In the sensor configuration select "TheThingsNetwork" and set decoding profile to "LoRa serialization". Enter your TTN Application and Device ID. Setup decoding option using `[{"decoder":"latLng"},{"decoder":"uint16",sensor_id":"yoursensorid"}]`
# Covid-19 Exposure Notification System beacon detection (currently NOT working with v3.0.x, use v2.4.x for this feature)
Bluetooth low energy service UUID 0xFD6F, used by Google/Apple COVID-19 Exposure Notification System, can be monitored and counted. By comparing with the total number of observed devices this <A HREF="https://linux-fuer-wi.blogspot.com/2020/10/suche-die-zahl-64879.html">gives an indication</A> how many people staying in proximity are using Apps for tracing COVID-19 exposures, e.g. in Germany the "Corona Warn App". To achive best results with this funcion, use following settings in `paxcounter.conf`:
#define COUNT_ENS 1 // enable ENS monitoring function
#define BLECOUNTER 1 // enable bluetooth sniffing
#define WIFICOUNTER 0 // disable wifi sniffing (improves BLE scan speed)
#define HAS_SENSOR_1 1 // optional, in board's hal file: transmit ENS counter data to server
# SD-card # SD-card
Data can be stored on an SD-card if one is availabe. Simply choose the file in src/hal and add the following lines to your hal-file: Data can be stored on SD-card if the board provides an SD card interface, either with SPI or MMC mode. To enable this feature, specify interface mode and hardware pins in board's hal file (src/hal/<board.h>):
#define HAS_SDCARD 1 // SD-card-reader/writer, using SPI interface #define HAS_SDCARD 1 // SD-card interface, using SPI mode
OR OR
#define HAS_SDCARD 2 // SD-card-reader/writer, using SDMMC interface #define HAS_SDCARD 2 // SD-card interface, using MMC mode
// Pins for SPI interface // Pins for SPI interface
#define SDCARD_CS (13) // fill in the correct numbers for your board #define SDCARD_CS (13) // fill in the correct numbers for your board
@ -239,31 +230,24 @@ Data can be stored on an SD-card if one is availabe. Simply choose the file in s
#define SDCARD_MISO (2) #define SDCARD_MISO (2)
#define SDCARD_SCLK (14) #define SDCARD_SCLK (14)
Please choose the correct number for the connection of the reader/writer. This is an example of a board with MMC SD-card interface: https://www.aliexpress.com/item/32915894264.html. For this board use file src/hal/ttgov21new.h and add the lines given above.
This is an example of a board with SD-card: https://www.aliexpress.com/item/32990008126.html
In this case you take the file src/hal/ttgov21new.h and add the lines given above (numbers given are for this board).
Another approach would be this tiny board: https://www.aliexpress.com/item/32424558182.html (needs 5V). Another approach would be this tiny board: https://www.aliexpress.com/item/32424558182.html (needs 5V).
In this case you choose the correct file for your ESP32-board in the src/hal-directory and add the lines given above to the correct h-file. Please correct the numbers given in the example to the numbers used corresponding to your wiring. In this case you choose the correct file for your ESP32-board in the src/hal-directory and add the lines given above. Edit the pin numbers given in the example, according to your wiring.
Some hints: Data is written on SD-card to a single file. After 3 write operations the data is flushed to the disk to minimize flash write cycles. Thus, up to the last 3 records of data will get lost when the PAXCOUNTER looses power during operation.
These cheap devices often handle SD-cards up to 32GB, not bigger ones. They can handle files in the old DOS-way, to say the filenames are in the 8.3-format. And they often cannot handle subdirectories.
The software included here writes data in a file named PAXCOUNT.xx, where xx can range from 00 to 99. The software starts with 00, checks to see if such a file already exists and if yes it will continue with the next number (up to 99 - in this case it will return no sd-card). So an existing file will not be overwritten. Format of the resulting file is CSV, thus easy import in LibreOffice, Excel, Influx, etc. Each record contains timestamp (in ISO8601 format), paxcount (wifi and ble) and battery voltage (optional). Voltage is logged if the device has a battery voltage sensor (to be configured in board hal file).
The data is written to the card and after 3 write-operations the data is flushed to the disk. So maybe the last 3 minutes of data get lost when you disconnect the PAXCOUNTER from power. File contents example:
And finally: this is the data written to the disk: timestamp,wifi,ble[,voltage]
2022-01-30T21:12:41Z,11,25[,4100]
2022-01-30T21:14:24Z,10,21[,4070]
2022-01-30T21:16:08Z,12,26[,4102]
2022-01-30T21:17:52Z,11,26[,4076]
date, time, wifi, bluet If you want to change this, modify src/sdcard.cpp and include/sdcard.h.
00.00.1970,00:01:09,2,0
00.00.1970,00:02:09,1,0
00.00.1970,00:03:09,2,0
Format of the data is CSV, which can easily imported into LibreOffice, Excel, .....
If you want to change this please look into src/sdcard.cpp and include/sdcard.h.
# Integration into "The Things Stack Community Edition" aka "The Things Stack V3" # Integration into "The Things Stack Community Edition" aka "The Things Stack V3"
@ -383,7 +367,7 @@ Hereafter described is the default *plain* format, which uses MSB bit numbering.
**Ports #10, #11, #12:** User sensor data **Ports #10, #11, #12:** User sensor data
Format is specified by user in function `sensor_read(uint8_t sensor)`, see `src/sensor.cpp`. Port #10 is also used for ENS counter (2 bytes = 16 bit), if ENS is compiled AND ENS data transfer is enabled Format is specified by user in function `sensor_read(uint8_t sensor)`, see `src/sensor.cpp`.
# Remote control # Remote control
@ -503,7 +487,7 @@ Send for example `83` `86` as Downlink on Port 2 to get battery status and time/
0x02 = RESERVED_DATA 0x02 = RESERVED_DATA
0x04 = MEMS_DATA 0x04 = MEMS_DATA
0x08 = GPS_DATA 0x08 = GPS_DATA
0x10 = SENSOR_1_DATA (also ENS counter) 0x10 = SENSOR_1_DATA
0x20 = SENSOR_2_DATA 0x20 = SENSOR_2_DATA
0x40 = SENSOR_3_DATA 0x40 = SENSOR_3_DATA
0x80 = BATT_DATA 0x80 = BATT_DATA
@ -524,10 +508,9 @@ Send for example `83` `86` as Downlink on Port 2 to get battery status and time/
0 = disabled 0 = disabled
1 = enabled [default] 1 = enabled [default]
0x18 set ENS counter on/off 0x18 reserved
0 = disabled [default] unused, does nothing
1 = enabled
0x19 set sleep cycle 0x19 set sleep cycle
@ -621,4 +604,4 @@ Thanks to
- [terrillmoore](https://github.com/mcci-catena) for maintaining the LMIC for arduino LoRaWAN stack - [terrillmoore](https://github.com/mcci-catena) for maintaining the LMIC for arduino LoRaWAN stack
- [sbamueller](https://github.com/sbamueller) for writing the tutorial in Make Magazine - [sbamueller](https://github.com/sbamueller) for writing the tutorial in Make Magazine
- [Stefan](https://github.com/nerdyscout) for paxcounter opensensebox integration - [Stefan](https://github.com/nerdyscout) for paxcounter opensensebox integration
- [August Quint](https://github.com/AugustQu) for adding SD card data logger, SDS011 and ENS support - [August Quint](https://github.com/AugustQu) for adding SD card data logger and SDS011 support

View File

@ -1,16 +0,0 @@
#ifndef _CORONA_h
#define _CORONA_H
// inspired by https://github.com/kmetz/BLEExposureNotificationBeeper
// (c) by Kaspar Metz
// modified for use in the Paxcounter by AQ
#include "globals.h"
#include <map>
bool cwa_init(void);
void cwa_mac_add(uint16_t hashedmac);
void cwa_clear(void);
uint16_t cwa_report(void);
#endif

View File

@ -5,10 +5,7 @@
#include "cyclic.h" #include "cyclic.h"
#include "qrcode.h" #include "qrcode.h"
#include "power.h" #include "power.h"
#include "timekeeper.h"
#if (COUNT_ENS)
#include "corona.h"
#endif
#if (HAS_DISPLAY) == 1 #if (HAS_DISPLAY) == 1
#include <OneBitDisplay.h> #include <OneBitDisplay.h>

View File

@ -71,7 +71,6 @@ typedef struct __attribute__((packed)) {
uint8_t wifiant; // 0=internal, 1=external (for LoPy/LoPy4) uint8_t wifiant; // 0=internal, 1=external (for LoPy/LoPy4)
uint8_t rgblum; // RGB Led luminosity (0..100%) uint8_t rgblum; // RGB Led luminosity (0..100%)
uint8_t payloadmask; // bitswitches for payload data uint8_t payloadmask; // bitswitches for payload data
uint8_t enscount; // 0=disabled 1= enabled
#ifdef HAS_BME680 #ifdef HAS_BME680
uint8_t uint8_t
@ -111,6 +110,5 @@ typedef struct {
} sdsStatus_t; } sdsStatus_t;
extern char clientId[20]; // unique clientID extern char clientId[20]; // unique clientID
extern time_t _COMPILETIME; // epoch build time
#endif #endif

View File

@ -5,13 +5,10 @@
#include <RtcDateTime.h> #include <RtcDateTime.h>
#include "timekeeper.h" #include "timekeeper.h"
#ifdef GPS_I2C // Needed for reading from I2C Bus #ifndef GPS_BAUDRATE
#include <Wire.h> #define GPS_BAUDRATE 115200UL
#endif #endif
#define NMEA_FRAME_SIZE 82 // NEMA has a maxium of 82 bytes per record
#define NMEA_COMPENSATION_FACTOR 480 // empiric for Ublox Neo 6M
extern TinyGPSPlus gps; // Make TinyGPS++ instance globally availabe extern TinyGPSPlus gps; // Make TinyGPS++ instance globally availabe
extern TaskHandle_t GpsTask; extern TaskHandle_t GpsTask;

View File

@ -24,7 +24,7 @@
void irqHandler(void *pvParameters); void irqHandler(void *pvParameters);
void mask_user_IRQ(); void mask_user_IRQ();
void unmask_user_IRQ(); void unmask_user_IRQ();
void doIRQ(int irq); void IRAM_ATTR doIRQ(int irq);
extern TaskHandle_t irqHandlerTask; extern TaskHandle_t irqHandlerTask;

View File

@ -8,6 +8,6 @@
void init_libpax(void); void init_libpax(void);
extern uint16_t volatile libpax_macs_ble, libpax_macs_wifi; // libpax values extern struct count_payload_t count_from_libpax; // libpax count storage
#endif #endif

View File

@ -7,6 +7,8 @@
#include <esp_coexist.h> // needed for coex version display #include <esp_coexist.h> // needed for coex version display
#include <esp_wifi.h> // needed for wifi init / deinit #include <esp_wifi.h> // needed for wifi init / deinit
#include <libpax_helpers.h>
#include "globals.h" #include "globals.h"
#include "reset.h" #include "reset.h"
#include "i2c.h" #include "i2c.h"
@ -18,9 +20,7 @@
#include "sensor.h" #include "sensor.h"
#include "lorawan.h" #include "lorawan.h"
#include "timekeeper.h" #include "timekeeper.h"
#include "corona.h"
#include "boot.h" #include "boot.h"
#include "libpax_helpers.h"
#include "power.h" #include "power.h"
#include "antenna.h" #include "antenna.h"

View File

@ -3,6 +3,7 @@
#include <rom/rtc.h> #include <rom/rtc.h>
#include "libpax_helpers.h"
#include "senddata.h" #include "senddata.h"
#include "cyclic.h" #include "cyclic.h"
#include "configmanager.h" #include "configmanager.h"

View File

@ -8,6 +8,8 @@
#include "lorawan.h" #include "lorawan.h"
#include "display.h" #include "display.h"
#include "power.h" #include "power.h"
#include "sdcard.h"
#include "sds011read.h"
void reset_rtc_vars(void); void reset_rtc_vars(void);
void do_reset(bool warmstart); void do_reset(bool warmstart);

View File

@ -12,7 +12,7 @@ extern RtcDS3231<TwoWire> Rtc; // make RTC instance globally available
uint8_t rtc_init(void); uint8_t rtc_init(void);
uint8_t set_rtctime(time_t t); uint8_t set_rtctime(time_t t);
void sync_rtctime(void); void sync_rtctime(void);
time_t get_rtctime(void); time_t get_rtctime(uint16_t *msec);
float get_rtctemp(void); float get_rtctemp(void);
#endif // _RTCTIME_H #endif // _RTCTIME_H

View File

@ -1,13 +1,13 @@
#ifndef _SDCARD_H #ifndef _SDCARD_H
#define _SDCARD_H #define _SDCARD_H
#include <globals.h> #include "globals.h"
#include <stdio.h> #include <stdio.h>
#include <SPI.h> #include <SPI.h>
#if (HAS_SDCARD) #if (HAS_SDCARD)
#if HAS_SDCARD == 1 #if HAS_SDCARD == 1
#include <mySD.h> #include <SD.h>
#elif HAS_SDCARD == 2 #elif HAS_SDCARD == 2
#include <SD_MMC.h> #include <SD_MMC.h>
#else #else
@ -54,14 +54,15 @@
#define SDCARD_DATA3 13 #define SDCARD_DATA3 13
#endif #endif
#define SDCARD_FILE_NAME "/paxcount.%02d" #define SDCARD_FILE_NAME clientId
#define SDCARD_FILE_HEADER "date, time, wifi, bluet" #define SDCARD_FILE_HEADER "timestamp,wifi,ble"
#if (COUNT_ENS) #if (defined BAT_MEASURE_ADC || defined HAS_PMU)
#define SDCARD_FILE_HEADER_CWA ",cwa" #define SDCARD_FILE_HEADER_VOLTAGE ",voltage"
#endif #endif
bool sdcard_init(void); bool sdcard_init(bool create = true);
void sdcard_close(void);
void sdcardWriteData(uint16_t, uint16_t, uint16_t = 0); void sdcardWriteData(uint16_t, uint16_t, uint16_t = 0);
#endif // _SDCARD_H #endif // _SDCARD_H

View File

@ -1,15 +1,24 @@
#ifndef _SDS011READ_H #ifndef _SDS011READ_H
#define _SDS011READ_H #define _SDS011READ_H
#include <SDS011.h> #include <SdsDustSensor.h>
#include "globals.h" #include "globals.h"
#define SDCARD_FILE_HEADER_SDS011 ", PM10,PM25" #define SDCARD_FILE_HEADER_SDS011 ", PM10,PM25"
// use original pins from HardwareSerial if none defined
#ifndef SDS_TX
#define SDS_TX -1
#endif
#ifndef SDS_RX
#define SDS_RX -1
#endif
extern bool isSDS011Active;
bool sds011_init(); bool sds011_init();
void sds011_loop(); void sds011_loop();
void sds011_sleep(void); void sds011_sleep(void);
void sds011_wakeup(void); void sds011_wakeup(void);
void sds011_store(sdsStatus_t *sds_store); void sds011_store(sdsStatus_t *sds_store);
#endif // _SDS011READ_H #endif // _SDS011READ_H

View File

@ -1,7 +1,6 @@
#ifndef _SENDDATA_H #ifndef _SENDDATA_H
#define _SENDDATA_H #define _SENDDATA_H
#include "libpax_helpers.h"
#include "spislave.h" #include "spislave.h"
#include "mqttclient.h" #include "mqttclient.h"
#include "cyclic.h" #include "cyclic.h"
@ -10,18 +9,13 @@
#include "display.h" #include "display.h"
#include "sdcard.h" #include "sdcard.h"
#if (COUNT_ENS)
#include "corona.h"
#endif
extern struct count_payload_t count_from_libpax;
void SendPayload(uint8_t port); void SendPayload(uint8_t port);
void sendData(void); void sendData(void);
void checkSendQueues(void); void checkSendQueues(void);
void flushQueues(void); void flushQueues(void);
bool allQueuesEmtpy(void); bool allQueuesEmtpy(void);
void setSendIRQ(TimerHandle_t xTimer = NULL); void setSendIRQ(TimerHandle_t xTimer);
void setSendIRQ(void);
void initSendDataTimer(uint8_t sendcycle); void initSendDataTimer(uint8_t sendcycle);
#endif // _SENDDATA_H_ #endif // _SENDDATA_H_

View File

@ -20,22 +20,22 @@ extern const char timeSetSymbols[];
extern Ticker timesyncer; extern Ticker timesyncer;
extern timesource_t timeSource; extern timesource_t timeSource;
extern TaskHandle_t ClockTask; extern TaskHandle_t ClockTask;
extern bool volatile TimePulseTick; // 1sec pps flag set by GPS or RTC extern DRAM_ATTR bool TimePulseTick; // 1sec pps flag set by GPS or RTC
extern DRAM_ATTR unsigned long lastPPS;
extern hw_timer_t *ppsIRQ; extern hw_timer_t *ppsIRQ;
void IRAM_ATTR CLOCKIRQ(void); void IRAM_ATTR CLOCKIRQ(void);
void IRAM_ATTR GPSIRQ(void);
void clock_init(void); void clock_init(void);
void clock_loop(void *pvParameters); void clock_loop(void *pvParameters);
void timepulse_start(void);
void setTimeSyncIRQ(void); void setTimeSyncIRQ(void);
uint8_t timepulse_init(void); uint8_t timepulse_init(void);
bool timeIsValid(time_t const t); bool timeIsValid(time_t const t);
void calibrateTime(void); void calibrateTime(void);
void IRAM_ATTR setMyTime(uint32_t t_sec, uint16_t t_msec, void IRAM_ATTR setMyTime(uint32_t t_sec, uint16_t t_msec,
timesource_t mytimesource); timesource_t mytimesource);
time_t compileTime(const String compile_date); time_t compileTime(void);
time_t mkgmtime(const struct tm *ptm); time_t mkgmtime(const struct tm *ptm);
TickType_t tx_Ticks(uint32_t framesize, unsigned long baud, uint32_t config, TickType_t tx_Ticks(uint32_t framesize, unsigned long baud, uint32_t config,
int8_t rxPin, int8_t txPins); int8_t rxPin, int8_t txPins);
#endif // _timekeeper_H #endif // _timekeeper_H

View File

@ -48,7 +48,7 @@ description = Paxcounter is a device for metering passenger flows in realtime. I
[common] [common]
; for release_version use max. 10 chars total, use any decimal format like "a.b.c" ; for release_version use max. 10 chars total, use any decimal format like "a.b.c"
release_version = 3.1.0 release_version = 3.2.0
; DEBUG LEVEL: For production run set to 0, otherwise device will leak RAM while running! ; DEBUG LEVEL: For production run set to 0, otherwise device will leak RAM while running!
; 0=None, 1=Error, 2=Warn, 3=Info, 4=Debug, 5=Verbose ; 0=None, 1=Error, 2=Warn, 3=Info, 4=Debug, 5=Verbose
debug_level = 3 debug_level = 3
@ -56,7 +56,7 @@ extra_scripts = pre:build.py
otakeyfile = ota.conf otakeyfile = ota.conf
lorakeyfile = loraconf.h lorakeyfile = loraconf.h
lmicconfigfile = lmic_config.h lmicconfigfile = lmic_config.h
platform_espressif32 = espressif32@3.4.0 platform_espressif32 = espressif32@3.5.0
monitor_speed = 115200 monitor_speed = 115200
upload_speed = 115200 ; set by build.py and taken from hal file upload_speed = 115200 ; set by build.py and taken from hal file
display_library = ; set by build.py and taken from hal file display_library = ; set by build.py and taken from hal file
@ -77,7 +77,7 @@ lib_deps_sensors =
adafruit/Adafruit BME280 Library @ ^2.2.1 adafruit/Adafruit BME280 Library @ ^2.2.1
adafruit/Adafruit BMP085 Library @ ^1.2.0 adafruit/Adafruit BMP085 Library @ ^1.2.0
boschsensortec/BSEC Software Library @ 1.6.1480 boschsensortec/BSEC Software Library @ 1.6.1480
https://github.com/ricki-z/SDS011.git https://github.com/cyberman54/sds-dust-sensors-arduino-library.git
lib_deps_basic = lib_deps_basic =
https://github.com/dbSuS/libpax.git @ ^1.0.0 https://github.com/dbSuS/libpax.git @ ^1.0.0
https://github.com/SukkoPera/Arduino-Rokkit-Hash.git https://github.com/SukkoPera/Arduino-Rokkit-Hash.git
@ -85,7 +85,6 @@ lib_deps_basic =
makuna/RTC @ ^2.3.5 makuna/RTC @ ^2.3.5
spacehuhn/SimpleButton spacehuhn/SimpleButton
lewisxhe/AXP202X_Library @ ^1.1.3 lewisxhe/AXP202X_Library @ ^1.1.3
geeksville/esp32-micro-sdcard @ ^0.1.1
256dpi/MQTT @ ^2.4.8 256dpi/MQTT @ ^2.4.8
lib_deps_all = lib_deps_all =
${common.lib_deps_basic} ${common.lib_deps_basic}

View File

@ -84,11 +84,6 @@ function Decoder(bytes, port) {
} }
} }
if (port === 10) {
// ENS count
return decode(bytes, [uint16], ['ens']);
}
} }

View File

@ -91,11 +91,6 @@ function decodeUplink(input) {
} }
} }
if (input.fPort === 10) {
// ENS count
data = decode(input.bytes, [uint16], ['ens']);
}
data.bytes = input.bytes; // comment out if you do not want to include the original payload data.bytes = input.bytes; // comment out if you do not want to include the original payload
data.port = input.fPort; // comment out if you do not want to inlude the port data.port = input.fPort; // comment out if you do not want to inlude the port

View File

@ -84,13 +84,6 @@ function decodeUplink(input) {
} }
} }
if (input.fPort === 10) {
var i = 0;
if (input.bytes.length >= 2) {
data.ens = (input.bytes[i++] << 8) | input.bytes[i++];
}
}
if (data.hdop) { if (data.hdop) {
data.hdop /= 100; data.hdop /= 100;
data.latitude /= 1000000; data.latitude /= 1000000;

View File

@ -6,12 +6,6 @@
// Local logging tag // Local logging tag
static const char TAG[] = __FILE__; static const char TAG[] = __FILE__;
// default settings for device data to be sent
#define PAYLOADMASK \
((GPS_DATA | MEMS_DATA | COUNT_DATA | SENSOR1_DATA | SENSOR2_DATA | \
SENSOR3_DATA) & \
(~BATT_DATA) & (~RESERVED_DATA))
// namespace for device runtime preferences // namespace for device runtime preferences
#define DEVCONFIG "paxcntcfg" #define DEVCONFIG "paxcntcfg"
@ -57,7 +51,6 @@ static void defaultConfig(configData_t *myconfig) {
myconfig->wifiant = 0; // 0=internal, 1=external (for LoPy/LoPy4) myconfig->wifiant = 0; // 0=internal, 1=external (for LoPy/LoPy4)
myconfig->rgblum = RGBLUMINOSITY; // RGB Led luminosity (0..100%) myconfig->rgblum = RGBLUMINOSITY; // RGB Led luminosity (0..100%)
myconfig->payloadmask = PAYLOADMASK; // payloads as defined in default myconfig->payloadmask = PAYLOADMASK; // payloads as defined in default
myconfig->enscount = COUNT_ENS; // 0=disabled, 1=enabled
#ifdef HAS_BME680 #ifdef HAS_BME680
// initial BSEC state for BME680 sensor // initial BSEC state for BME680 sensor

View File

@ -1,54 +0,0 @@
// routines for counting the number of devices which advertise Exposure
// Notification Service e.g. "Corona Warn App" in Germany
// copied from https://github.com/kmetz/BLEExposureNotificationBeeper
// (c) by Kaspar Metz
// modified for use in the Paxcounter by AQ
#if (COUNT_ENS) && !(BLECOUNTER)
#warning ENS-Counter needs Bluetooth, but Bluetooth compile option is disabled
#endif
#if (COUNT_ENS)
// Local logging tag
static const char TAG[] = __FILE__;
#define BT_BD_ADDR_HEX(addr) \
addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]
#include "corona.h"
// When to forget old senders ** currently not used **
#define FORGET_AFTER_MINUTES 2
// array of timestamps for seen notifiers: hash -> timestamp[ms]
static std::map<uint16_t, unsigned long> cwaSeenNotifiers;
// Remove notifiers last seen over FORGET_AFTER_MINUTES ago.
void cwa_clear() {
#ifdef VERBOSE
ESP_LOGV(TAG, "CWA: forget old notifier: %d", cwaSeenNotifiers.size());
for (auto const &notifier : cwaSeenNotifiers) {
ESP_LOGD(TAG, "CWA forget <%04X>", notifier.first);
// }
}
#endif
// clear everything, otherwise we would count the same device again, as in the
// next cycle it likely will advertise with a different hash-value
cwaSeenNotifiers.clear();
}
// return the total number of devices seen advertising ENS
uint16_t cwa_report(void) { return cwaSeenNotifiers.size(); }
bool cwa_init(void) {
ESP_LOGD(TAG, "init BLE-scanner for ENS");
return true;
}
void cwa_mac_add(uint16_t hashedmac) {
cwaSeenNotifiers[hashedmac] = millis(); // hash last seen at ....
}
#endif

View File

@ -9,10 +9,6 @@ static const char TAG[] = __FILE__;
Ticker cyclicTimer; Ticker cyclicTimer;
#if (HAS_SDS011)
extern boolean isSDS011Active;
#endif
void setCyclicIRQ() { xTaskNotify(irqHandlerTask, CYCLIC_IRQ, eSetBits); } void setCyclicIRQ() { xTaskNotify(irqHandlerTask, CYCLIC_IRQ, eSetBits); }
// do all housekeeping // do all housekeeping
@ -22,41 +18,59 @@ void doHousekeeping() {
if ((RTC_runmode == RUNMODE_UPDATE) || (RTC_runmode == RUNMODE_MAINTENANCE)) if ((RTC_runmode == RUNMODE_UPDATE) || (RTC_runmode == RUNMODE_MAINTENANCE))
do_reset(true); // warmstart do_reset(true); // warmstart
// heap and task storage debugging // try to get time if we don't yet have a recent timesource
if (timeSource == _unsynced || timeSource == _set)
calibrateTime();
// print heap and task storage information
ESP_LOGD(TAG, "Heap: Free:%d, Min:%d, Size:%d, Alloc:%d, StackHWM:%d", ESP_LOGD(TAG, "Heap: Free:%d, Min:%d, Size:%d, Alloc:%d, StackHWM:%d",
ESP.getFreeHeap(), ESP.getMinFreeHeap(), ESP.getHeapSize(), ESP.getFreeHeap(), ESP.getMinFreeHeap(), ESP.getHeapSize(),
ESP.getMaxAllocHeap(), uxTaskGetStackHighWaterMark(NULL)); ESP.getMaxAllocHeap(), uxTaskGetStackHighWaterMark(NULL));
if (irqHandlerTask != NULL)
ESP_LOGD(TAG, "IRQhandler %d bytes left | Taskstate = %d", ESP_LOGD(TAG, "IRQhandler %d bytes left | Taskstate = %d",
uxTaskGetStackHighWaterMark(irqHandlerTask), uxTaskGetStackHighWaterMark(irqHandlerTask),
eTaskGetState(irqHandlerTask)); eTaskGetState(irqHandlerTask));
if (rcmdTask != NULL)
ESP_LOGD(TAG, "Rcommand interpreter %d bytes left | Taskstate = %d", ESP_LOGD(TAG, "Rcommand interpreter %d bytes left | Taskstate = %d",
uxTaskGetStackHighWaterMark(rcmdTask), eTaskGetState(rcmdTask)); uxTaskGetStackHighWaterMark(rcmdTask), eTaskGetState(rcmdTask));
#if (HAS_LORA) #if (HAS_LORA)
if (lmicTask != NULL)
ESP_LOGD(TAG, "LMiCtask %d bytes left | Taskstate = %d", ESP_LOGD(TAG, "LMiCtask %d bytes left | Taskstate = %d",
uxTaskGetStackHighWaterMark(lmicTask), eTaskGetState(lmicTask)); uxTaskGetStackHighWaterMark(lmicTask), eTaskGetState(lmicTask));
if (lorasendTask != NULL)
ESP_LOGD(TAG, "Lorasendtask %d bytes left | Taskstate = %d", ESP_LOGD(TAG, "Lorasendtask %d bytes left | Taskstate = %d",
uxTaskGetStackHighWaterMark(lorasendTask), uxTaskGetStackHighWaterMark(lorasendTask),
eTaskGetState(lorasendTask)); eTaskGetState(lorasendTask));
#endif #endif
#if (HAS_GPS) #if (HAS_GPS)
if (GpsTask != NULL)
ESP_LOGD(TAG, "Gpsloop %d bytes left | Taskstate = %d", ESP_LOGD(TAG, "Gpsloop %d bytes left | Taskstate = %d",
uxTaskGetStackHighWaterMark(GpsTask), eTaskGetState(GpsTask)); uxTaskGetStackHighWaterMark(GpsTask), eTaskGetState(GpsTask));
#endif #endif
#ifdef HAS_SPI #ifdef HAS_SPI
if (spiTask != NULL)
ESP_LOGD(TAG, "spiloop %d bytes left | Taskstate = %d", ESP_LOGD(TAG, "spiloop %d bytes left | Taskstate = %d",
uxTaskGetStackHighWaterMark(spiTask), eTaskGetState(spiTask)); uxTaskGetStackHighWaterMark(spiTask), eTaskGetState(spiTask));
#endif #endif
#ifdef HAS_MQTT #ifdef HAS_MQTT
if (mqttTask != NULL)
ESP_LOGD(TAG, "MQTTloop %d bytes left | Taskstate = %d", ESP_LOGD(TAG, "MQTTloop %d bytes left | Taskstate = %d",
uxTaskGetStackHighWaterMark(mqttTask), eTaskGetState(mqttTask)); uxTaskGetStackHighWaterMark(mqttTask), eTaskGetState(mqttTask));
#endif #endif
#if (defined HAS_DCF77 || defined HAS_IF482) #if (defined HAS_DCF77 || defined HAS_IF482)
if (ClockTask != NULL)
ESP_LOGD(TAG, "Clockloop %d bytes left | Taskstate = %d", ESP_LOGD(TAG, "Clockloop %d bytes left | Taskstate = %d",
uxTaskGetStackHighWaterMark(ClockTask), eTaskGetState(ClockTask)); uxTaskGetStackHighWaterMark(ClockTask), eTaskGetState(ClockTask));
#endif #endif
#if (HAS_LED != NOT_A_PIN) || defined(HAS_RGB_LED) #if (HAS_LED != NOT_A_PIN) || defined(HAS_RGB_LED)
if (ledLoopTask != NULL)
ESP_LOGD(TAG, "LEDloop %d bytes left | Taskstate = %d", ESP_LOGD(TAG, "LEDloop %d bytes left | Taskstate = %d",
uxTaskGetStackHighWaterMark(ledLoopTask), uxTaskGetStackHighWaterMark(ledLoopTask),
eTaskGetState(ledLoopTask)); eTaskGetState(ledLoopTask));
@ -106,10 +120,8 @@ void doHousekeeping() {
#if (HAS_SDS011) #if (HAS_SDS011)
if (isSDS011Active) { if (isSDS011Active) {
ESP_LOGD(TAG, "SDS011: go to sleep");
sds011_loop(); sds011_loop();
} else { } else {
ESP_LOGD(TAG, "SDS011: wakeup");
sds011_wakeup(); sds011_wakeup();
} }
#endif #endif

View File

@ -51,18 +51,17 @@ void DCF77_Pulse(uint8_t bit) {
// helper function to convert decimal to bcd digit // helper function to convert decimal to bcd digit
uint64_t dec2bcd(uint8_t const dec, uint8_t const startpos, uint64_t dec2bcd(uint8_t const dec, uint8_t const startpos,
uint8_t const endpos, uint8_t *odd_parity) { uint8_t const endpos, uint8_t *parity) {
uint8_t data = (dec < 10) ? dec : ((dec / 10) << 4) + (dec % 10); uint8_t data = dec < 10 ? dec : ((dec / 10) << 4) + dec % 10;
uint64_t bcd = 0; uint64_t bcd = 0;
*odd_parity = 0; *parity = 0;
for (uint8_t i = startpos; i <= endpos; i++) { for (uint8_t i = startpos; i <= endpos; i++) {
bcd += (data & 1) ? set_dcfbit(i) : 0; bcd += data & 1 ? set_dcfbit(i) : 0;
*odd_parity += (data & 1); *parity ^= data & 1;
data >>= 1; data >>= 1;
} }
*odd_parity %= 2;
return bcd; return bcd;
} }
@ -96,14 +95,14 @@ uint64_t DCF77_Frame(const struct tm t) {
// DATE (36..58) // DATE (36..58)
frame += dec2bcd(t.tm_mday, 36, 41, &parity); frame += dec2bcd(t.tm_mday, 36, 41, &parity);
parity_sum += parity; parity_sum ^= parity;
frame += dec2bcd((t.tm_wday == 0) ? 7 : t.tm_wday, 42, 44, &parity); frame += dec2bcd((t.tm_wday == 0) ? 7 : t.tm_wday, 42, 44, &parity);
parity_sum += parity; parity_sum ^= parity;
frame += dec2bcd(t.tm_mon + 1, 45, 49, &parity); frame += dec2bcd(t.tm_mon + 1, 45, 49, &parity);
parity_sum += parity; parity_sum ^= parity;
frame += dec2bcd(t.tm_year + 1900 - 2000, 50, 57, &parity); frame += dec2bcd(t.tm_year + 1900 - 2000, 50, 57, &parity);
parity_sum += parity; parity_sum ^= parity;
frame += parity_sum % 2 ? set_dcfbit(58) : 0; frame += parity_sum ? set_dcfbit(58) : 0;
return frame; return frame;

View File

@ -174,16 +174,10 @@ void dp_init(bool verbose) {
void dp_refresh(bool nextPage) { void dp_refresh(bool nextPage) {
// update counter values from libpax
libpax_counter_count(&count_from_libpax);
#ifndef HAS_BUTTON #ifndef HAS_BUTTON
static uint32_t framecounter = 0; static uint32_t framecounter = 0;
#endif #endif
// update histogram
dp_plotCurve(count_from_libpax.pax, false);
// if display is switched off we don't refresh it to relax cpu // if display is switched off we don't refresh it to relax cpu
if (!DisplayIsOn && (DisplayIsOn == cfg.screenon)) if (!DisplayIsOn && (DisplayIsOn == cfg.screenon))
return; return;
@ -207,7 +201,6 @@ void dp_refresh(bool nextPage) {
#endif #endif
dp_drawPage(nextPage); dp_drawPage(nextPage);
dp_dump(displaybuf);
I2C_MUTEX_UNLOCK(); // release i2c bus access I2C_MUTEX_UNLOCK(); // release i2c bus access
@ -219,6 +212,7 @@ void dp_drawPage(bool nextpage) {
// write display content to display buffer // write display content to display buffer
// nextpage = true -> flip 1 page // nextpage = true -> flip 1 page
struct count_payload_t count; // libpax count storage
static uint8_t DisplayPage = 0; static uint8_t DisplayPage = 0;
char timeState, strftime_buf[64]; char timeState, strftime_buf[64];
time_t now; time_t now;
@ -233,6 +227,9 @@ void dp_drawPage(bool nextpage) {
dp_clear(); dp_clear();
} }
// update counter values from libpax
libpax_counter_count(&count);
// cursor home // cursor home
dp_setTextCursor(0, 0); dp_setTextCursor(0, 0);
@ -240,7 +237,7 @@ void dp_drawPage(bool nextpage) {
// display number of unique macs total Wifi + BLE // display number of unique macs total Wifi + BLE
if (DisplayPage < 5) { if (DisplayPage < 5) {
dp_setFont(MY_FONT_STRETCHED); dp_setFont(MY_FONT_STRETCHED);
dp_printf("%-5d", count_from_libpax.pax); dp_printf("%-5d", count.pax);
} }
switch (DisplayPage) { switch (DisplayPage) {
@ -264,31 +261,21 @@ void dp_drawPage(bool nextpage) {
#if ((WIFICOUNTER) && (BLECOUNTER)) #if ((WIFICOUNTER) && (BLECOUNTER))
if (cfg.wifiscan) if (cfg.wifiscan)
dp_printf("WIFI:%-5d", count_from_libpax.wifi_count); dp_printf("WIFI:%-5d", count.wifi_count);
else else
dp_printf("WIFI:off"); dp_printf("WIFI:off");
if (cfg.blescan) if (cfg.blescan)
#if (COUNT_ENS) dp_printf("BLTH:%-5d", count.ble_count);
if (cfg.enscount)
dp_printf(" CWA:%-5d", cwa_report());
else
#endif
dp_printf("BLTH:%-5d", count_from_libpax.ble_count);
else else
dp_printf(" BLTH:off"); dp_printf(" BLTH:off");
#elif ((WIFICOUNTER) && (!BLECOUNTER)) #elif ((WIFICOUNTER) && (!BLECOUNTER))
if (cfg.wifiscan) if (cfg.wifiscan)
dp_printf("WIFI:%-5d", count_from_libpax.wifi_count); dp_printf("WIFI:%-5d", count.wifi_count);
else else
dp_printf("WIFI:off"); dp_printf("WIFI:off");
#elif ((!WIFICOUNTER) && (BLECOUNTER)) #elif ((!WIFICOUNTER) && (BLECOUNTER))
if (cfg.blescan) if (cfg.blescan)
dp_printf("BLTH:%-5d", count_from_libpax.ble_count); dp_printf("BLTH:%-5d", count.ble_count);
#if (COUNT_ENS)
if (cfg.enscount)
dp_printf("(CWA:%d)", cwa_report());
else
#endif
dp_printf("BLTH:off"); dp_printf("BLTH:off");
#else #else
dp_printf("Sniffer disabled"); dp_printf("Sniffer disabled");
@ -327,7 +314,6 @@ void dp_drawPage(bool nextpage) {
#if (TIME_SYNC_INTERVAL) #if (TIME_SYNC_INTERVAL)
timeState = TimePulseTick ? ' ' : timeSetSymbols[timeSource]; timeState = TimePulseTick ? ' ' : timeSetSymbols[timeSource];
TimePulseTick = false;
time(&now); time(&now);
localtime_r(&now, &timeinfo); localtime_r(&now, &timeinfo);
strftime(strftime_buf, sizeof(strftime_buf), "%c", &timeinfo); strftime(strftime_buf, sizeof(strftime_buf), "%c", &timeinfo);
@ -355,6 +341,8 @@ void dp_drawPage(bool nextpage) {
dp_printf("%-4s", getSfName(updr2rps(LMIC.datarate))); dp_printf("%-4s", getSfName(updr2rps(LMIC.datarate)));
dp_setFont(MY_FONT_SMALL, 0); dp_setFont(MY_FONT_SMALL, 0);
#endif // HAS_LORA #endif // HAS_LORA
dp_dump(displaybuf);
break; break;
// ---------- page 1: lorawan parameters ---------- // ---------- page 1: lorawan parameters ----------
@ -380,6 +368,8 @@ void dp_drawPage(bool nextpage) {
LMIC.seqnoDn ? LMIC.seqnoDn - 1 : 0); LMIC.seqnoDn ? LMIC.seqnoDn - 1 : 0);
dp_println(); dp_println();
dp_printf("SNR:%-5d RSSI:%-5d", (LMIC.snr + 2) / 4, LMIC.rssi); dp_printf("SNR:%-5d RSSI:%-5d", (LMIC.snr + 2) / 4, LMIC.rssi);
dp_dump(displaybuf);
break; break;
#else // flip page if we are unattended #else // flip page if we are unattended
DisplayPage++; DisplayPage++;
@ -412,6 +402,8 @@ void dp_drawPage(bool nextpage) {
dp_printf("No fix"); dp_printf("No fix");
wasnofix = true; wasnofix = true;
} }
dp_dump(displaybuf);
break; break;
#else // flip page if we are unattended #else // flip page if we are unattended
DisplayPage++; DisplayPage++;
@ -439,6 +431,8 @@ void dp_drawPage(bool nextpage) {
// line 6-7: Pre // line 6-7: Pre
dp_printf("PRE:%-2.1f", bme_status.pressure); dp_printf("PRE:%-2.1f", bme_status.pressure);
#endif // HAS_BME680 #endif // HAS_BME680
dp_dump(displaybuf);
break; // page 3 break; // page 3
#else // flip page if we are unattended #else // flip page if we are unattended
DisplayPage++; DisplayPage++;
@ -453,14 +447,15 @@ void dp_drawPage(bool nextpage) {
localtime_r(&now, &timeinfo); localtime_r(&now, &timeinfo);
strftime(strftime_buf, sizeof(strftime_buf), "%T", &timeinfo); strftime(strftime_buf, sizeof(strftime_buf), "%T", &timeinfo);
dp_printf("%.8s", strftime_buf); dp_printf("%.8s", strftime_buf);
dp_dump(displaybuf);
break; break;
// ---------- page 5: pax graph ---------- // ---------- page 5: pax graph ----------
case 5: case 5:
dp_setFont(MY_FONT_NORMAL); // update histogram
dp_setTextCursor(0, 0); dp_plotCurve(count.pax, false);
dp_printf("Pax graph");
dp_dump(plotbuf); dp_dump(plotbuf);
break; break;

View File

@ -6,68 +6,147 @@
// Local logging tag // Local logging tag
static const char TAG[] = __FILE__; static const char TAG[] = __FILE__;
// we use NMEA ZDA sentence field 1 for time synchronization
// ZDA gives time for preceding pps pulse
// downsight is that it does not have a constant offset
// thus precision is only +/- 1 second
TinyGPSPlus gps; TinyGPSPlus gps;
TinyGPSCustom gpstime(gps, "GPZDA", 1); // field 1 = UTC time (hhmmss.ss)
TinyGPSCustom gpsday(gps, "GPZDA", 2); // field 2 = day (01..31)
TinyGPSCustom gpsmonth(gps, "GPZDA", 3); // field 3 = month (01..12)
TinyGPSCustom gpsyear(gps, "GPZDA", 4); // field 4 = year (4-digit)
static const String ZDA_Request = "$EIGPQ,ZDA*39\r\n";
TaskHandle_t GpsTask; TaskHandle_t GpsTask;
#ifdef GPS_SERIAL
HardwareSerial GPS_Serial(1); // use UART #1 HardwareSerial GPS_Serial(1); // use UART #1
static uint16_t nmea_txDelay_ms =
(tx_Ticks(NMEA_FRAME_SIZE, GPS_SERIAL) / portTICK_PERIOD_MS); // Ublox UBX packet data
#else
static uint16_t nmea_txDelay_ms = 0; // UBX CFG-PRT packet
#endif byte CFG_PRT[] = {
0xB5, // sync char 1
0x62, // sync char 2
0x06, // class
0x00, // id
0x14, // length
0x00, // .
0x01, // portID (UART 1)
0x00, // reserved
0x00, // txReady
0x00, // .
0b11010000, // UART mode: 8N1
0b00001000, // .
0x00, // .
0x00, // .
(byte)GPS_BAUDRATE, // baudrate
(byte)(GPS_BAUDRATE >> 8), // .
(byte)(GPS_BAUDRATE >> 16), // .
(byte)(GPS_BAUDRATE >> 24), // .
0b00000011, // input protocols: NMEA + UBX
0b00000000, // .
0b00000010, // output protocols: NMEA
0x00000000, // .
0x00, // reserved
0x00, // .
0x00, // .
0x00 // .
};
// Array of two bytes for CFG-MSG packets payload.
byte CFG_MSG_CID[][2] = {{0xF0, 0x01}, {0xF0, 0x02}, {0xF0, 0x03}, {0xF0, 0x05},
{0xF0, 0x06}, {0xF0, 0x07}, {0xF0, 0x08}, {0xF0, 0x09},
{0xF0, 0x0A}, {0xF0, 0x0E}, {0xF1, 0x00}, {0xF1, 0x03},
{0xF1, 0x04}, {0xF1, 0x05}, {0xF1, 0x06}};
// UBX CFG-MSG packet
byte CFG_MSG[] = {
0xB5, // sync char 1
0x62, // sync char 2
0x06, // class
0x01, // id
0x03, // length
0x00, // .
0x00, // payload (first byte from messages array element)
0x00, // payload (second byte from messages array element)
0x00 // payload (zero to disable message)
};
// UBX CFG-CFG packet
byte CFG_CFG[] = {
0xB5, // sync char 1
0x62, // sync char 2
0x06, // class
0x09, // id
0x0D, // length
0x00, // .
0b00011111, // clearmask
0b00000110, // .
0x00, // .
0x00, // .
0x00, // savemask
0x00, // .
0x00, // .
0x00, // .
0b00011111, // loadmask
0b00000110, // .
0x00, // .
0x00, // .
0b00010001 // devicemask
};
// helper functions to send UBX commands to ublox gps chip
void sendPacket(byte *packet, byte len) {
uint8_t CK_A = 0;
uint8_t CK_B = 0;
for (int i = 0; i < len; i++)
GPS_Serial.write(packet[i]);
// calculate and send Fletcher checksum
for (int i = 2; i < len; i++) {
CK_A += packet[i];
CK_B += CK_A;
}
GPS_Serial.write(CK_A);
GPS_Serial.write(CK_B);
}
void restoreDefaults() { sendPacket(CFG_CFG, sizeof(CFG_CFG)); }
void changeBaudrate() { sendPacket(CFG_PRT, sizeof(CFG_PRT)); }
void disableNmea() {
// tinygps++ processes only $GPGGA/$GNGGA and $GPRMC/$GNRMC
// thus, we disable all other NMEA messages
byte packetSize = sizeof(CFG_MSG);
// Offset to the place where payload starts.
byte payloadOffset = 6;
// Iterate over the messages array.
for (byte i = 0; i < sizeof(CFG_MSG_CID) / sizeof(*CFG_MSG_CID); i++) {
// Copy two bytes of payload to the packet buffer.
for (byte j = 0; j < sizeof(*CFG_MSG_CID); j++) {
CFG_MSG[payloadOffset + j] = CFG_MSG_CID[i][j];
}
sendPacket(CFG_MSG, packetSize);
}
}
// initialize and configure GPS // initialize and configure GPS
int gps_init(void) { int gps_init(void) {
if (!gps_config()) {
ESP_LOGE(TAG, "GPS chip initializiation error");
return 0;
}
#ifdef GPS_SERIAL
ESP_LOGI(TAG, "Opening serial GPS"); ESP_LOGI(TAG, "Opening serial GPS");
GPS_Serial.begin(GPS_SERIAL); GPS_Serial.begin(GPS_SERIAL);
#elif defined GPS_I2C
ESP_LOGI(TAG, "Opening I2C GPS"); restoreDefaults();
Wire.begin(GPS_I2C, 400000); // I2C connect to GPS device with 400 KHz delay(100);
Wire.beginTransmission(GPS_ADDR);
Wire.write(0x00); // dummy write changeBaudrate();
if (Wire.endTransmission()) { delay(100);
ESP_LOGE(TAG, "Quectel L76 GPS chip not found"); GPS_Serial.flush();
return 0; GPS_Serial.updateBaudRate(GPS_BAUDRATE);
} else
ESP_LOGI(TAG, "Quectel L76 GPS chip found"); disableNmea();
#endif
return 1; return 1;
} // gps_init() } // gps_init()
// detect gps chipset type and configure it with device specific settings
int gps_config() {
int rslt = 1; // success
#if defined GPS_SERIAL
/* insert user configuration here, if needed */
#elif defined GPS_I2C
/* insert user configuration here, if needed */
#endif
return rslt;
}
// store current GPS location data in struct // store current GPS location data in struct
void gps_storelocation(gpsStatus_t *gps_store) { void gps_storelocation(gpsStatus_t *gps_store) {
if (gps.location.isUpdated() && gps.location.isValid() && if (gps.location.isUpdated() && gps.location.isValid() &&
@ -90,46 +169,45 @@ bool gps_hasfix() {
} }
// function to poll UTC time from GPS NMEA data; note: this is costly // function to poll UTC time from GPS NMEA data; note: this is costly
time_t get_gpstime(uint16_t *msec) { time_t get_gpstime(uint16_t *msec = 0) {
// poll NMEA ZDA sentence const uint16_t txDelay =
#ifdef GPS_SERIAL 70U * 1000 / (GPS_BAUDRATE / 9); // serial tx of 70 NMEA chars
GPS_Serial.print(ZDA_Request);
// wait for gps NMEA answer
// vTaskDelay(tx_Ticks(NMEA_FRAME_SIZE, GPS_SERIAL));
#elif defined GPS_I2C
Wire.print(ZDA_Request);
#endif
// did we get a current date & time? // did we get a current date & time?
if (gpstime.isValid()) { if (gps.time.age() < 1000) {
uint32_t delay_ms = // convert tinygps time format to struct tm format
gpstime.age() + nmea_txDelay_ms + NMEA_COMPENSATION_FACTOR;
uint32_t zdatime = atof(gpstime.value());
// convert UTC time from gps NMEA ZDA sentence to tm format
struct tm gps_tm = {0}; struct tm gps_tm = {0};
gps_tm.tm_sec = zdatime % 100; // second (UTC) gps_tm.tm_sec = gps.time.second();
gps_tm.tm_min = (zdatime / 100) % 100; // minute (UTC) gps_tm.tm_min = gps.time.minute();
gps_tm.tm_hour = zdatime / 10000; // hour (UTC) gps_tm.tm_hour = gps.time.hour();
gps_tm.tm_mday = atoi(gpsday.value()); // day, 01 to 31 gps_tm.tm_mday = gps.date.day();
gps_tm.tm_mon = atoi(gpsmonth.value()) - 1; // month, 01 to 12 gps_tm.tm_mon = gps.date.month() - 1; // 1-12 -> 0-11
gps_tm.tm_year = atoi(gpsyear.value()) - 1900; // year, YYYY gps_tm.tm_year = gps.date.year() - 1900; // 2000+ -> years since 1900
// convert UTC tm to time_t epoch // convert UTC tm to time_t epoch
gps_tm.tm_isdst = 0; // UTC has no DST gps_tm.tm_isdst = 0; // UTC has no DST
time_t t = mkgmtime(&gps_tm); time_t t = mkgmtime(&gps_tm);
// add protocol delay with millisecond precision #ifdef GPS_INT
t += (time_t)(delay_ms / 1000); // if we have a recent GPS PPS pulse, sync on top of next second
*msec = delay_ms % 1000; // fractional seconds uint16_t ppsDiff = millis() - lastPPS;
if (ppsDiff < 1000)
*msec = ppsDiff;
else {
ESP_LOGD(TAG, "no PPS from GPS");
return 0;
}
#else
// best guess for sync on top of next second
*msec = gps.time.centisecond() * 10U + txDelay;
#endif
return t; return t;
} }
ESP_LOGD(TAG, "no valid GPS time"); ESP_LOGD(TAG, "no valid GPS time");
return 0; return 0;
} // get_gpstime() } // get_gpstime()
@ -139,42 +217,17 @@ void gps_loop(void *pvParameters) {
_ASSERT((uint32_t)pvParameters == 1); // FreeRTOS check _ASSERT((uint32_t)pvParameters == 1); // FreeRTOS check
while (1) {
if (cfg.payloadmask & GPS_DATA) {
#ifdef GPS_SERIAL
// feed GPS decoder with serial NMEA data from GPS device // feed GPS decoder with serial NMEA data from GPS device
while (GPS_Serial.available()) { while (1) {
while (cfg.payloadmask & GPS_DATA) {
while (GPS_Serial.available())
gps.encode(GPS_Serial.read()); gps.encode(GPS_Serial.read());
yield();
delay(5);
} }
#elif defined GPS_I2C delay(1000);
Wire.requestFrom(GPS_ADDR, 32); // caution: this is a blocking call } // infinite while loop
while (Wire.available()) {
gps.encode(Wire.read());
delay(2); // 2ms delay according L76 datasheet
yield();
}
#endif
// (only) while device time is not set or unsynched, and we have a valid
// GPS time, we trigger a device time update to poll time from GPS
if ((timeSource == _unsynced || timeSource == _set) &&
(gpstime.isUpdated() && gpstime.isValid() && gpstime.age() < 1000)) {
calibrateTime();
}
} // if
// show NMEA data in verbose mode, useful only for debugging GPS, very
// noisy ESP_LOGV(TAG, "GPS NMEA data: passed %u / failed: %u / with fix:
// %u",
// gps.passedChecksum(), gps.failedChecksum(),
// gps.sentencesWithFix());
yield(); // yield to CPU
} // end of infinite loop
} // gps_loop() } // gps_loop()

View File

@ -18,7 +18,7 @@
#define CFG_sx1276_radio 1 #define CFG_sx1276_radio 1
#define HAS_DISPLAY 1 // OLED-Display on board #define HAS_DISPLAY 1 // OLED-Display on board
#define HAS_LED LED_BUILTIN // white LED on board #define HAS_LED LED_BUILTIN // white LED on board (set to NOT_A_PIN to disable)
#define HAS_BUTTON KEY_BUILTIN // button "PROG" on board #define HAS_BUTTON KEY_BUILTIN // button "PROG" on board
// Pins for I2C interface of OLED Display // Pins for I2C interface of OLED Display

View File

@ -20,7 +20,7 @@
#define CFG_sx1276_radio 1 #define CFG_sx1276_radio 1
#define HAS_DISPLAY 1 // OLED-Display on board #define HAS_DISPLAY 1 // OLED-Display on board
#define HAS_LED LED_BUILTIN // white LED on board #define HAS_LED LED_BUILTIN // white LED on board (set to NOT_A_PIN to disable)
#define HAS_BUTTON KEY_BUILTIN // button "PROG" on board #define HAS_BUTTON KEY_BUILTIN // button "PROG" on board
#define BAT_MEASURE_ADC ADC2_GPIO13_CHANNEL // battery probe GPIO pin #define BAT_MEASURE_ADC ADC2_GPIO13_CHANNEL // battery probe GPIO pin

View File

@ -20,7 +20,7 @@
#define CFG_sx1276_radio 1 #define CFG_sx1276_radio 1
#define HAS_DISPLAY 1 // OLED-Display on board #define HAS_DISPLAY 1 // OLED-Display on board
#define HAS_LED LED_BUILTIN // white LED on board #define HAS_LED LED_BUILTIN // white LED on board (set to NOT_A_PIN to disable)
#define HAS_BUTTON KEY_BUILTIN // button "PROG" on board #define HAS_BUTTON KEY_BUILTIN // button "PROG" on board
#define BAT_MEASURE_ADC ADC1_GPIO37_CHANNEL // battery probe GPIO pin #define BAT_MEASURE_ADC ADC1_GPIO37_CHANNEL // battery probe GPIO pin

View File

@ -10,7 +10,7 @@
// Hardware related definitions for lolin32lite (without LoRa shield) // Hardware related definitions for lolin32lite (without LoRa shield)
#define HAS_LED LED_BUILTIN // on board LED on GPIO5 #define HAS_LED LED_BUILTIN // on board LED on GPIO5
#define LED_ACTIVE_LOW 1 // Onboard LED is active when pin is LOW #define LED_ACTIVE_LOW 1 // Onboard LED is active when pin is LOW (set to NOT_A_PIN to disable)
#define MY_SDA SDA #define MY_SDA SDA
#define MY_SCL SCL #define MY_SCL SCL

View File

@ -15,7 +15,7 @@
#define HAS_DISPLAY 1 // OLED-Display on board #define HAS_DISPLAY 1 // OLED-Display on board
//#define MY_DISPLAY_FLIP 1 // uncomment this for rotated display //#define MY_DISPLAY_FLIP 1 // uncomment this for rotated display
#define HAS_LED 22 // ESP32 GPIO12 (pin22) On Board LED #define HAS_LED 22 // ESP32 GPIO12 (pin22) On Board LED (set to NOT_A_PIN to disable)
#define LED_ACTIVE_LOW 1 // Onboard LED is active when pin is LOW #define LED_ACTIVE_LOW 1 // Onboard LED is active when pin is LOW
#define HAS_RGB_LED SmartLed rgb_led(LED_WS2812, 1, GPIO_NUM_13) // ESP32 GPIO13 (pin13) On Board Shield WS2812B RGB LED #define HAS_RGB_LED SmartLed rgb_led(LED_WS2812, 1, GPIO_NUM_13) // ESP32 GPIO13 (pin13) On Board Shield WS2812B RGB LED
#define HAS_BUTTON 15 // ESP32 GPIO15 (pin15) Button is on the LoraNode32 shield #define HAS_BUTTON 15 // ESP32 GPIO15 (pin15) Button is on the LoraNode32 shield

View File

@ -20,11 +20,6 @@
#define HAS_ANTENNA_SWITCH (16) // pin for switching wifi antenna #define HAS_ANTENNA_SWITCH (16) // pin for switching wifi antenna
#define WIFI_ANTENNA 0 // 0 = internal, 1 = external #define WIFI_ANTENNA 0 // 0 = internal, 1 = external
// uncomment this only if your LoPy runs on a PYTRACK BOARD
#define HAS_GPS 1
#define GPS_I2C GPIO_NUM_25, GPIO_NUM_26 // SDA (P22), SCL (P21)
#define GPS_ADDR 0x10
// uncomment this only if your LoPy runs on a EXPANSION BOARD // uncomment this only if your LoPy runs on a EXPANSION BOARD
//#define HAS_LED (12) // use if LoPy is on Expansion Board, this has a user LED //#define HAS_LED (12) // use if LoPy is on Expansion Board, this has a user LED
//#define LED_ACTIVE_LOW 1 // use if LoPy is on Expansion Board, this has a user LED //#define LED_ACTIVE_LOW 1 // use if LoPy is on Expansion Board, this has a user LED

View File

@ -30,11 +30,6 @@
#define HAS_ANTENNA_SWITCH (21) // pin for switching wifi antenna (P12) #define HAS_ANTENNA_SWITCH (21) // pin for switching wifi antenna (P12)
#define WIFI_ANTENNA 0 // 0 = internal, 1 = external #define WIFI_ANTENNA 0 // 0 = internal, 1 = external
// uncomment defines in this section ONLY if your LoPy lives on a PYTRACK BOARD
//#define HAS_GPS 1
//#define GPS_I2C GPIO_NUM_25, GPIO_NUM_26 // SDA (P22), SCL (P21)
//#define GPS_ADDR 0x10
// uncomment defines in this section ONLY if your LoPy lives on a EXPANSION BOARD // uncomment defines in this section ONLY if your LoPy lives on a EXPANSION BOARD
//#define HAS_LED (12) // use if LoPy is on Expansion Board, this has a user LED //#define HAS_LED (12) // use if LoPy is on Expansion Board, this has a user LED
//#define LED_ACTIVE_LOW 1 // use if LoPy is on Expansion Board, this has a user LED //#define LED_ACTIVE_LOW 1 // use if LoPy is on Expansion Board, this has a user LED

View File

@ -14,7 +14,7 @@
#define HAS_DISPLAY 1 // OLED-Display on board #define HAS_DISPLAY 1 // OLED-Display on board
//#define MY_DISPLAY_FLIP 1 // uncomment this for rotated display //#define MY_DISPLAY_FLIP 1 // uncomment this for rotated display
#define HAS_LED LED_BUILTIN #define HAS_LED LED_BUILTIN // set to NOT_A_PIN to disable
#define LED_ACTIVE_LOW 1 // Onboard LED is active when pin is LOW #define LED_ACTIVE_LOW 1 // Onboard LED is active when pin is LOW
#define HAS_BUTTON KEY_BUILTIN #define HAS_BUTTON KEY_BUILTIN

View File

@ -16,10 +16,12 @@
#define CFG_sx1276_radio 1 // HPD13A LoRa SoC #define CFG_sx1276_radio 1 // HPD13A LoRa SoC
// enable only if you want to store a local paxcount table on the device // enable only if you want to store a local paxcount table on the device
#define HAS_SDCARD 2 // // this board has a SDMMC card-reader/writer #define HAS_SDCARD 2 // this board has a SDMMC card-reader/writer
#define HAS_DISPLAY 1 #define HAS_DISPLAY 1
#define HAS_LED (25) // green on board LED #define HAS_LED (25) // green on board LED
//#define HAS_LED NOT_A_PIN
#define BAT_MEASURE_ADC ADC1_GPIO35_CHANNEL // battery probe GPIO pin -> ADC1_CHANNEL_7 #define BAT_MEASURE_ADC ADC1_GPIO35_CHANNEL // battery probe GPIO pin -> ADC1_CHANNEL_7
#define BAT_VOLTAGE_DIVIDER 2 // voltage divider 100k/100k on board #define BAT_VOLTAGE_DIVIDER 2 // voltage divider 100k/100k on board

View File

@ -86,18 +86,19 @@ static const char TAG[] = __FILE__;
String IF482_Frame(time_t t) { String IF482_Frame(time_t t) {
char mon, out[IF482_FRAME_SIZE + 1]; char mon, out[IF482_FRAME_SIZE + 1], buf[IF482_FRAME_SIZE - 3];
if (sntp_get_sync_status() == SNTP_SYNC_STATUS_IN_PROGRESS) if (sntp_get_sync_status() == SNTP_SYNC_STATUS_IN_PROGRESS)
mon = 'M'; // time had been set but sync not completed mon = 'M'; // time had been set but sync not completed
else else
mon = 'A'; // time has been set and was recently synced mon = 'A'; // time has been set and was recently synced
// generate IF482 telegram // generate IF482 telegram for local time
// snprintf(out, sizeof(out), "O%cL%s\r", mon, myTZ.dateTime(t, UTC_TIME, struct tm tt;
// "ymdwHis").c_str()); localtime_r(&t, &tt);
mktime(&tt);
// ESP_LOGD(TAG, "[%s] IF482 date/time: %s", ctime(time(NULL), out); strftime(buf, sizeof(buf), "%y%m%d%u%H%M%S", &tt);
snprintf(out, sizeof(out), "O%cL%s\r", mon, buf);
return out; return out;
} }

View File

@ -212,7 +212,7 @@ void ledLoop(void *parameter) {
previousLEDState = LEDState; previousLEDState = LEDState;
} }
// give yield to CPU // give yield to CPU
delay(2); delay(5);
} // while(1) } // while(1)
}; // ledloop() }; // ledloop()

View File

@ -13,6 +13,7 @@ uint8_t MatrixDisplayIsOn = 0;
static uint8_t displaybuf[LED_MATRIX_WIDTH * LED_MATRIX_HEIGHT / 8] = {0}; static uint8_t displaybuf[LED_MATRIX_WIDTH * LED_MATRIX_HEIGHT / 8] = {0};
static unsigned long ulLastNumMacs = 0; static unsigned long ulLastNumMacs = 0;
static time_t ulLastTime = time(NULL); static time_t ulLastTime = time(NULL);
static struct count_payload_t count; // libpax count storage
hw_timer_t *matrixDisplayIRQ = NULL; hw_timer_t *matrixDisplayIRQ = NULL;
@ -76,12 +77,12 @@ void refreshTheMatrixDisplay(bool nextPage) {
case 0: case 0:
// update counter values from libpax // update counter values from libpax
libpax_counter_count(&count_from_libpax); libpax_counter_count(&count);
if (cfg.countermode == 1) { if (cfg.countermode == 1) {
// cumulative counter mode -> display total number of pax // cumulative counter mode -> display total number of pax
if (ulLastNumMacs != count_from_libpax.pax) { if (ulLastNumMacs != count.pax) {
ulLastNumMacs = count_from_libpax.pax; ulLastNumMacs = count.pax;
matrix.clear(); matrix.clear();
DrawNumber(String(ulLastNumMacs)); DrawNumber(String(ulLastNumMacs));
} }
@ -89,10 +90,10 @@ void refreshTheMatrixDisplay(bool nextPage) {
else { // cyclic counter mode -> plot a line diagram else { // cyclic counter mode -> plot a line diagram
if (ulLastNumMacs != count_from_libpax.pax) { if (ulLastNumMacs != count.pax) {
// next count cycle? // next count cycle?
if (count_from_libpax.pax == 0) { if (count.pax == 0) {
// matrix full? then scroll left 1 dot, else increment column // matrix full? then scroll left 1 dot, else increment column
if (col < (LED_MATRIX_WIDTH - 1)) if (col < (LED_MATRIX_WIDTH - 1))
@ -104,7 +105,7 @@ void refreshTheMatrixDisplay(bool nextPage) {
matrix.drawPoint(col, row, 0); // clear current dot matrix.drawPoint(col, row, 0); // clear current dot
// scale and set new dot // scale and set new dot
ulLastNumMacs = count_from_libpax.pax; ulLastNumMacs = count.pax;
level = ulLastNumMacs / LINE_DIAGRAM_DIVIDER; level = ulLastNumMacs / LINE_DIAGRAM_DIVIDER;
row = level <= LED_MATRIX_HEIGHT row = level <= LED_MATRIX_HEIGHT
? LED_MATRIX_HEIGHT - 1 - level % LED_MATRIX_HEIGHT ? LED_MATRIX_HEIGHT - 1 - level % LED_MATRIX_HEIGHT
@ -120,7 +121,7 @@ void refreshTheMatrixDisplay(bool nextPage) {
if (ulLastTime != t) { if (ulLastTime != t) {
ulLastTime = t; ulLastTime = t;
matrix.clear(); matrix.clear();
//DrawNumber(myTZ.dateTime("H:i:s").c_str()); // DrawNumber(myTZ.dateTime("H:i:s").c_str());
} }
break; break;

View File

@ -5,18 +5,9 @@ static const char TAG[] = __FILE__;
// libpax payload // libpax payload
struct count_payload_t count_from_libpax; struct count_payload_t count_from_libpax;
uint16_t volatile libpax_macs_ble, libpax_macs_wifi;
void process_count(void) {
ESP_LOGD(TAG, "pax: %d / %d / %d", count_from_libpax.pax,
count_from_libpax.wifi_count, count_from_libpax.ble_count);
libpax_macs_ble = count_from_libpax.ble_count;
libpax_macs_wifi = count_from_libpax.wifi_count;
setSendIRQ();
}
void init_libpax(void) { void init_libpax(void) {
libpax_counter_init(process_count, &count_from_libpax, cfg.sendcycle * 2, libpax_counter_init(setSendIRQ, &count_from_libpax, cfg.sendcycle * 2,
cfg.countermode); cfg.countermode);
libpax_counter_start(); libpax_counter_start();
} }

View File

@ -91,11 +91,6 @@ function decodeUplink(input) {
} }
} }
if (input.fPort === 10) {
// ENS count
data = decode(input.bytes, [uint16], ['ens']);
}
data.bytes = input.bytes; // comment out if you do not want to include the original payload data.bytes = input.bytes; // comment out if you do not want to include the original payload
data.port = input.fPort; // comment out if you do not want to inlude the port data.port = input.fPort; // comment out if you do not want to inlude the port

View File

@ -179,10 +179,10 @@ void showLoraKeys(void) {
// all EUI buffer so we do it here to a temp // all EUI buffer so we do it here to a temp
// buffer to be able to display them // buffer to be able to display them
uint8_t buf[32]; uint8_t buf[32];
os_getDevEui((u1_t *)buf);
printKey("DevEUI", buf, 8, true);
os_getArtEui((u1_t *)buf); os_getArtEui((u1_t *)buf);
printKey("AppEUI", buf, 8, true); printKey("AppEUI", buf, 8, true);
os_getDevEui((u1_t *)buf);
printKey("DevEUI", buf, 8, true);
os_getDevKey((u1_t *)buf); os_getDevKey((u1_t *)buf);
printKey("AppKey", buf, 16, false); printKey("AppKey", buf, 16, false);
} }
@ -314,7 +314,7 @@ esp_err_t lmic_init(void) {
"lorasendtask", // name of task "lorasendtask", // name of task
3072, // stack size of task 3072, // stack size of task
(void *)1, // parameter of the task (void *)1, // parameter of the task
1, // priority of the task 2, // priority of the task
&lorasendTask, // task handle &lorasendTask, // task handle
1); // CPU core 1); // CPU core

View File

@ -27,60 +27,52 @@ licenses. Refer to LICENSE.txt file in repository for more details.
Task Core Prio Purpose Task Core Prio Purpose
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
ledloop 0 3 blinks LEDs ledloop* 1 1 blinks LEDs
spiloop 0 2 reads/writes data on spi interface spiloop# 0 2 reads/writes data on spi interface
IDLE 0 0 ESP32 arduino scheduler -> runs wifi sniffer lmictask* 1 2 MCCI LMiC LORAWAN stack
clockloop# 1 6 generates realtime telegrams for external clock
mqttloop# 1 5 reads/writes data on ETH interface
timesync_proc# 1 7 processes realtime time sync requests
irqhandler# 1 4 application IRQ (i.e. displayrefresh)
gpsloop* 1 1 reads data from GPS via serial or i2c
lorasendtask# 1 2 feeds data from lora sendqueue to lmcic
rmcd_process# 1 1 Remote command interpreter loop
lmictask 1 2 MCCI LMiC LORAWAN stack * spinning task, always ready
clockloop 1 4 generates realtime telegrams for external clock # blocked/waiting task
mqttloop 1 2 reads/writes data on ETH interface
timesync_proc 1 3 processes realtime time sync requests
irqhandler 1 2 cyclic tasks (i.e. displayrefresh) triggered by timers
gpsloop 1 1 reads data from GPS via serial or i2c
lorasendtask 1 1 feeds data from lora sendqueue to lmcic
rmcd_process 1 1 Remote command interpreter loop
IDLE 1 0 ESP32 arduino scheduler -> runs wifi channel rotator
Low priority numbers denote low priority tasks. Low priority numbers denote low priority tasks.
-------------------------------------------------------------------------------
NOTE: Changing any timings will have impact on time accuracy of whole code.
So don't do it if you do not own a digital oscilloscope.
// ESP32 hardware timers // ESP32 hardware timers
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
0 displayIRQ -> display refresh -> 40ms (DISPLAYREFRESH_MS) 0 displayIRQ -> display refresh -> 40ms (DISPLAYREFRESH_MS)
1 ppsIRQ -> pps clock irq -> 1sec 1 ppsIRQ -> pps clock irq -> 1sec
2 (unused)
3 MatrixDisplayIRQ -> matrix mux cycle -> 0,5ms (MATRIX_DISPLAY_SCAN_US) 3 MatrixDisplayIRQ -> matrix mux cycle -> 0,5ms (MATRIX_DISPLAY_SCAN_US)
// Interrupt routines
-------------------------------------------------------------------------------
irqHandlerTask (Core 1), see irqhandler.cpp
fired by hardware
DisplayIRQ -> esp32 timer 0
CLOCKIRQ -> esp32 timer 1 or external GPIO (RTC_INT or GPS_INT)
MatrixDisplayIRQ-> esp32 timer 3
ButtonIRQ -> external GPIO
PMUIRQ -> PMU chip GPIO
fired by software
TIMESYNC_IRQ -> setTimeSyncIRQ() -> Ticker.h
CYCLIC_IRQ -> setCyclicIRQ() -> Ticker.h
SENDCYCLE_IRQ -> setSendIRQ() -> xTimer
BME_IRQ -> setBMEIRQ() -> Ticker.h
ClockTask (Core 1), see timekeeper.cpp
fired by hardware
CLOCKIRQ -> esp32 timer 1
// External RTC timer (if present) // External RTC timer (if present)
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
triggers pps 1 sec impulse triggers pps 1 sec impulse
// Interrupt routines
-------------------------------------------------------------------------------
ISRs fired by CPU or GPIO:
DisplayIRQ <- esp32 timer 0
CLOCKIRQ <- esp32 timer 1 or GPIO (RTC_INT)
MatrixDisplayIRQ<- esp32 timer 3
ButtonIRQ <- GPIO <- Button
PMUIRQ <- GPIO <- PMU chip
Application IRQs fired by software:
TIMESYNC_IRQ <- setTimeSyncIRQ() <- Ticker.h
CYCLIC_IRQ <- setCyclicIRQ() <- Ticker.h
SENDCYCLE_IRQ <- setSendIRQ() <- xTimer or libpax callback
BME_IRQ <- setBMEIRQ() <- Ticker.h
*/ */
// Basic Config // Basic Config
@ -126,7 +118,7 @@ void setup() {
snprintf(clientId, 20, "paxcounter_%08x", hashedmac); snprintf(clientId, 20, "paxcounter_%08x", hashedmac);
ESP_LOGI(TAG, "Starting %s v%s (runmode=%d / restarts=%d)", clientId, ESP_LOGI(TAG, "Starting %s v%s (runmode=%d / restarts=%d)", clientId,
PROGVERSION, RTC_runmode, RTC_restarts); PROGVERSION, RTC_runmode, RTC_restarts);
ESP_LOGI(TAG, "code build date: %d", _COMPILETIME); ESP_LOGI(TAG, "code build date: %d", compileTime());
// print chip information on startup if in verbose mode after coldstart // print chip information on startup if in verbose mode after coldstart
#if (VERBOSE) #if (VERBOSE)
@ -244,9 +236,9 @@ void setup() {
"ledloop", // name of task "ledloop", // name of task
1024, // stack size of task 1024, // stack size of task
(void *)1, // parameter of the task (void *)1, // parameter of the task
3, // priority of the task 1, // priority of the task
&ledLoopTask, // task handle &ledLoopTask, // task handle
0); // CPU core 1); // CPU core
#endif #endif
// initialize wifi antenna // initialize wifi antenna
@ -328,7 +320,7 @@ void setup() {
ESP_LOGI(TAG, "Starting GPS Feed..."); ESP_LOGI(TAG, "Starting GPS Feed...");
xTaskCreatePinnedToCore(gps_loop, // task function xTaskCreatePinnedToCore(gps_loop, // task function
"gpsloop", // name of task "gpsloop", // name of task
4096, // stack size of task 8192, // stack size of task
(void *)1, // parameter of the task (void *)1, // parameter of the task
1, // priority of the task 1, // priority of the task
&GpsTask, // task handle &GpsTask, // task handle
@ -339,15 +331,9 @@ void setup() {
// initialize sensors // initialize sensors
#if (HAS_SENSORS) #if (HAS_SENSORS)
#if (HAS_SENSOR_1) #if (HAS_SENSOR_1)
#if (COUNT_ENS)
ESP_LOGI(TAG, "init CWA-counter");
if (cwa_init())
strcat_P(features, " CWA");
#else
strcat_P(features, " SENS(1)"); strcat_P(features, " SENS(1)");
sensor_init(); sensor_init();
#endif #endif
#endif
#if (HAS_SENSOR_2) #if (HAS_SENSOR_2)
strcat_P(features, " SENS(2)"); strcat_P(features, " SENS(2)");
sensor_init(); sensor_init();
@ -432,7 +418,7 @@ void setup() {
"irqhandler", // name of task "irqhandler", // name of task
4096, // stack size of task 4096, // stack size of task
(void *)1, // parameter of the task (void *)1, // parameter of the task
2, // priority of the task 4, // priority of the task
&irqHandlerTask, // task handle &irqHandlerTask, // task handle
1); // CPU core 1); // CPU core
@ -494,8 +480,7 @@ void setup() {
cyclicTimer.attach(HOMECYCLE, setCyclicIRQ); cyclicTimer.attach(HOMECYCLE, setCyclicIRQ);
// only if we have a timesource we do timesync // only if we have a timesource we do timesync
#if ((TIME_SYNC_LORAWAN) || (TIME_SYNC_LORASERVER) || (HAS_GPS) || \ #if ((TIME_SYNC_LORAWAN) || (TIME_SYNC_LORASERVER) || (HAS_GPS) || (HAS_RTC))
defined HAS_RTC)
#if (defined HAS_IF482 || defined HAS_DCF77) #if (defined HAS_IF482 || defined HAS_DCF77)
ESP_LOGI(TAG, "Starting Clock Controller..."); ESP_LOGI(TAG, "Starting Clock Controller...");
@ -507,8 +492,7 @@ void setup() {
#endif #endif
ESP_LOGI(TAG, "Starting Timekeeper..."); ESP_LOGI(TAG, "Starting Timekeeper...");
_ASSERT(timepulse_init()); // setup pps timepulse _ASSERT(timepulse_init()); // starts pps and cyclic time sync
timepulse_start(); // starts pps and cyclic time sync
strcat_P(features, " TIME"); strcat_P(features, " TIME");
#endif // timesync #endif // timesync

View File

@ -37,7 +37,7 @@ esp_err_t mqtt_init(void) {
SEND_QUEUE_SIZE * PAYLOAD_BUFFER_SIZE); SEND_QUEUE_SIZE * PAYLOAD_BUFFER_SIZE);
ESP_LOGI(TAG, "Starting MQTTloop..."); ESP_LOGI(TAG, "Starting MQTTloop...");
xTaskCreatePinnedToCore(mqtt_client_task, "mqttloop", 4096, (void *)NULL, 1, xTaskCreatePinnedToCore(mqtt_client_task, "mqttloop", 4096, (void *)NULL, 5,
&mqttTask, 1); &mqttTask, 1);
return ESP_OK; return ESP_OK;
} }

View File

@ -18,6 +18,11 @@
#define PAYLOAD_ENCODER 2 // payload encoder: 1=Plain, 2=Packed, 3=Cayenne LPP dynamic, 4=Cayenne LPP packed #define PAYLOAD_ENCODER 2 // payload encoder: 1=Plain, 2=Packed, 3=Cayenne LPP dynamic, 4=Cayenne LPP packed
#define COUNTERMODE 0 // 0=cyclic, 1=cumulative, 2=cyclic confirmed #define COUNTERMODE 0 // 0=cyclic, 1=cumulative, 2=cyclic confirmed
// default settings for transmission of sensor data (first list = data on / second line = data off)
#define PAYLOADMASK \
((GPS_DATA | MEMS_DATA | COUNT_DATA | SENSOR1_DATA | SENSOR2_DATA | SENSOR3_DATA) & \
(~BATT_DATA) & (~RESERVED_DATA))
// MAC sniffing parameters // MAC sniffing parameters
#define BLECOUNTER 1 // set to 0 if you do not want to install the BLE sniffer #define BLECOUNTER 1 // set to 0 if you do not want to install the BLE sniffer
#define WIFICOUNTER 1 // set to 0 if you do not want to install the WIFI sniffer #define WIFICOUNTER 1 // set to 0 if you do not want to install the WIFI sniffer
@ -27,10 +32,6 @@
#define BLESCANWINDOW 80 // [milliseconds] scan window, see below, 3 .. 10240, default 80ms #define BLESCANWINDOW 80 // [milliseconds] scan window, see below, 3 .. 10240, default 80ms
#define BLESCANINTERVAL 80 // [illiseconds] scan interval, see below, 3 .. 10240, default 80ms = 100% duty cycle #define BLESCANINTERVAL 80 // [illiseconds] scan interval, see below, 3 .. 10240, default 80ms = 100% duty cycle
// Corona Exposure Notification Service(ENS) counter
#define COUNT_ENS 0 // count found number of devices which advertise Exposure Notification Service
// set to 1 if you want to enable this function [default=0]
/* Note: guide for setting bluetooth parameters /* Note: guide for setting bluetooth parameters
* *
* |< Scan Window > |< Scan Window > | ... |< Scan Window > | * |< Scan Window > |< Scan Window > | ... |< Scan Window > |

View File

@ -1,7 +1,6 @@
// Basic Config // Basic Config
#include "globals.h" #include "globals.h"
#include "rcommand.h" #include "rcommand.h"
#include "libpax_helpers.h"
// Local logging tag // Local logging tag
static const char TAG[] = __FILE__; static const char TAG[] = __FILE__;
@ -397,15 +396,6 @@ void set_flush(uint8_t val[]) {
// used to open receive window on LoRaWAN class a nodes // used to open receive window on LoRaWAN class a nodes
}; };
void set_enscount(uint8_t val[]) {
ESP_LOGI(TAG, "Remote command: set ENS_COUNT to %s", val[0] ? "on" : "off");
cfg.enscount = val[0] ? 1 : 0;
if (val[0])
cfg.payloadmask |= (uint8_t)SENSOR1_DATA;
else
cfg.payloadmask &= (uint8_t)~SENSOR1_DATA;
}
void set_loadconfig(uint8_t val[]) { void set_loadconfig(uint8_t val[]) {
ESP_LOGI(TAG, "Remote command: load config from NVRAM"); ESP_LOGI(TAG, "Remote command: load config from NVRAM");
loadConfig(); loadConfig();
@ -430,7 +420,7 @@ static const cmd_t table[] = {
{0x10, set_rgblum, 1}, {0x13, set_sensor, 2}, {0x10, set_rgblum, 1}, {0x13, set_sensor, 2},
{0x14, set_payloadmask, 1}, {0x15, set_bme, 1}, {0x14, set_payloadmask, 1}, {0x15, set_bme, 1},
{0x16, set_batt, 1}, {0x17, set_wifiscan, 1}, {0x16, set_batt, 1}, {0x17, set_wifiscan, 1},
{0x18, set_enscount, 1}, {0x19, set_sleepcycle, 2}, {0x18, set_flush, 0}, {0x19, set_sleepcycle, 2},
{0x20, set_loadconfig, 0}, {0x21, set_saveconfig, 0}, {0x20, set_loadconfig, 0}, {0x21, set_saveconfig, 0},
{0x80, get_config, 0}, {0x81, get_status, 0}, {0x80, get_config, 0}, {0x81, get_status, 0},
{0x83, get_batt, 0}, {0x84, get_gps, 0}, {0x83, get_batt, 0}, {0x84, get_gps, 0},
@ -493,7 +483,7 @@ void rcmd_process(void *pvParameters) {
rcmd_execute(RcmdBuffer.cmd, RcmdBuffer.cmdLen); rcmd_execute(RcmdBuffer.cmd, RcmdBuffer.cmdLen);
} }
delay(2); // yield to CPU delay(5); // yield to CPU
} // rcmd_process() } // rcmd_process()
// enqueue remote command // enqueue remote command

View File

@ -126,6 +126,11 @@ void enter_deepsleep(const uint64_t wakeup_sec, gpio_num_t wakeup_gpio) {
sds011_sleep(); sds011_sleep();
#endif #endif
// flush & close sd card, if we have
#if (HAS_SDCARD)
sdcard_close();
#endif
// wait a while (max 100 sec) to clear send queues // wait a while (max 100 sec) to clear send queues
ESP_LOGI(TAG, "Waiting until send queues are empty..."); ESP_LOGI(TAG, "Waiting until send queues are empty...");
for (i = 100; i > 0; i--) { for (i = 100; i > 0; i--) {

View File

@ -25,14 +25,14 @@ uint8_t rtc_init(void) {
} }
#if (TIME_SYNC_COMPILEDATE) #if (TIME_SYNC_COMPILEDATE)
// initialize a blank RTC without battery backup with compiled time // initialize a blank RTC without battery backup with build time
RtcDateTime tt = Rtc.GetDateTime(); RtcDateTime tt = Rtc.GetDateTime();
time_t t = tt.Epoch32Time(); // sec2000 -> epoch time_t t = tt.Epoch32Time(); // sec2000 -> epoch
if (!Rtc.IsDateTimeValid() || !timeIsValid(t)) { if (!Rtc.IsDateTimeValid() || !timeIsValid(t)) {
ESP_LOGW(TAG, "RTC has no recent time, setting to compiled time"); ESP_LOGW(TAG, "RTC has no recent time, setting to compiletime");
Rtc.SetDateTime( Rtc.SetDateTime(RtcDateTime(mkgmtime(compileTime()) -
RtcDateTime(_COMPILETIME - SECS_YR_2000)); // epoch -> sec2000 SECS_YR_2000)); // epoch -> sec2000
} }
#endif #endif
@ -62,15 +62,23 @@ uint8_t set_rtctime(time_t t) { // t is sec epoch time
} }
} // set_rtctime() } // set_rtctime()
time_t get_rtctime(void) { time_t get_rtctime(uint16_t *msec) {
time_t t = 0; time_t t = 0;
*msec = 0;
if (I2C_MUTEX_LOCK()) { if (I2C_MUTEX_LOCK()) {
if (Rtc.IsDateTimeValid() && Rtc.GetIsRunning()) { if (Rtc.IsDateTimeValid() && Rtc.GetIsRunning()) {
RtcDateTime tt = Rtc.GetDateTime(); RtcDateTime tt = Rtc.GetDateTime();
t = tt.Epoch32Time(); // sec2000 -> epoch t = tt.Epoch32Time(); // sec2000 -> epoch
} }
I2C_MUTEX_UNLOCK(); I2C_MUTEX_UNLOCK();
return timeIsValid(t); #ifdef RTC_INT
// adjust time to top of next second by waiting TimePulseTick to flip
bool lastTick = TimePulseTick;
while (TimePulseTick == lastTick) {
};
t++;
#endif
return t;
} else { } else {
ESP_LOGE(TAG, "RTC get time failure"); ESP_LOGE(TAG, "RTC get time failure");
return 0; // failure return 0; // failure

View File

@ -1,4 +1,7 @@
// routines for writing data to an SD-card, if present // routines for writing data to an SD-card, if present
// use FAT32 formatted card
// check whether your card reader supports SPI oder SDMMC and select appropriate
// SD low level driver in board hal file
// Local logging tag // Local logging tag
static const char TAG[] = __FILE__; static const char TAG[] = __FILE__;
@ -8,20 +11,26 @@ static const char TAG[] = __FILE__;
#ifdef HAS_SDCARD #ifdef HAS_SDCARD
static bool useSDCard; static bool useSDCard;
static void openFile(void);
static void createFile(void);
File fileSDCard; File fileSDCard;
bool sdcard_init() { #if HAS_SDCARD == 1
ESP_LOGI(TAG, "looking for SD-card..."); SPIClass sd_spi;
#endif
bool sdcard_init(bool create) {
// for usage of SD drivers on ESP32 platform see // for usage of SD drivers on ESP32 platform see
// https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/sdspi_host.html // https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/sdspi_host.html
// https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/sdmmc_host.html // https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/sdmmc_host.html
ESP_LOGI(TAG, "looking for SD-card...");
#if HAS_SDCARD == 1 // use SD SPI host driver #if HAS_SDCARD == 1 // use SD SPI host driver
useSDCard = SD.begin(SDCARD_CS, SDCARD_MOSI, SDCARD_MISO, SDCARD_SCLK); digitalWrite(SDCARD_CS, HIGH);
sd_spi.begin(SDCARD_SCLK, SDCARD_MISO, SDCARD_MOSI, SDCARD_CS);
digitalWrite(SDCARD_CS, LOW);
useSDCard = SD.begin(SDCARD_CS, sd_spi);
#elif HAS_SDCARD == 2 // use SD MMC host driver #elif HAS_SDCARD == 2 // use SD MMC host driver
// enable internal pullups of sd-data lines // enable internal pullups of sd-data lines
gpio_set_pull_mode(gpio_num_t(SDCARD_DATA0), GPIO_PULLUP_ONLY); gpio_set_pull_mode(gpio_num_t(SDCARD_DATA0), GPIO_PULLUP_ONLY);
@ -33,20 +42,27 @@ bool sdcard_init() {
if (useSDCard) { if (useSDCard) {
ESP_LOGI(TAG, "SD-card found"); ESP_LOGI(TAG, "SD-card found");
createFile(); openFile();
} else return true;
} else {
ESP_LOGI(TAG, "SD-card not found"); ESP_LOGI(TAG, "SD-card not found");
return useSDCard; return false;
}
}
void sdcard_close(void) {
ESP_LOGI(TAG, "closing SD-card");
fileSDCard.flush();
fileSDCard.close();
} }
void sdcardWriteData(uint16_t noWifi, uint16_t noBle, void sdcardWriteData(uint16_t noWifi, uint16_t noBle,
__attribute__((unused)) uint16_t noBleCWA) { __attribute__((unused)) uint16_t voltage) {
static int counterWrites = 0; static int counterWrites = 0;
char tempBuffer[12 + 1]; char tempBuffer[20 + 1];
time_t t = time(NULL); time_t t = time(NULL);
struct tm tt; struct tm tt;
localtime_r(&t, &tt); gmtime_r(&t, &tt); // make UTC timestamp
mktime(&tt);
#if (HAS_SDS011) #if (HAS_SDS011)
sdsStatus_t sds; sdsStatus_t sds;
@ -55,40 +71,38 @@ void sdcardWriteData(uint16_t noWifi, uint16_t noBle,
if (!useSDCard) if (!useSDCard)
return; return;
ESP_LOGD(TAG, "writing to SD-card"); ESP_LOGI(TAG, "SD: writing data");
strftime(tempBuffer, sizeof(tempBuffer), "%d.%m.%Y", &tt); strftime(tempBuffer, sizeof(tempBuffer), "%FT%TZ", &tt);
fileSDCard.print(tempBuffer); fileSDCard.print(tempBuffer);
strftime(tempBuffer, sizeof(tempBuffer), "%H.%M.%S", &tt); snprintf(tempBuffer, sizeof(tempBuffer), ",%d,%d", noWifi, noBle);
fileSDCard.print(tempBuffer); fileSDCard.print(tempBuffer);
sprintf(tempBuffer, "%d,%d", noWifi, noBle); #if (defined BAT_MEASURE_ADC || defined HAS_PMU)
fileSDCard.print(tempBuffer); snprintf(tempBuffer, sizeof(tempBuffer), ",%d", voltage);
#if (COUNT_ENS)
sprintf(tempBuffer, ",%d", noBleCWA);
fileSDCard.print(tempBuffer); fileSDCard.print(tempBuffer);
#endif #endif
#if (HAS_SDS011) #if (HAS_SDS011)
sds011_store(&sds); sds011_store(&sds);
sprintf(tempBuffer, ",%5.1f,%4.1f", sds.pm10, sds.pm25); snprintf(tempBuffer, sizeof(tempBuffer), ",%5.1f,%4.1f", sds.pm10 / 10,
sds.pm25 / 10);
fileSDCard.print(tempBuffer); fileSDCard.print(tempBuffer);
#endif #endif
fileSDCard.println(); fileSDCard.println();
if (++counterWrites > 2) { if (++counterWrites > 2) {
// force writing to SD-card // force writing to SD-card
ESP_LOGD(TAG, "flushing data to card"); ESP_LOGI(TAG, "SD: flushing data");
fileSDCard.flush(); fileSDCard.flush();
counterWrites = 0; counterWrites = 0;
} }
} }
void createFile(void) { void openFile(void) {
char bufferFilename[8 + 1 + 3 + 1]; char bufferFilename[30];
useSDCard = false; useSDCard = false;
for (int i = 0; i < 100; i++) { snprintf(bufferFilename, sizeof(bufferFilename), "/%s.csv", SDCARD_FILE_NAME);
sprintf(bufferFilename, SDCARD_FILE_NAME, i); ESP_LOGI(TAG, "SD: looking for file <%s>", bufferFilename);
// ESP_LOGD(TAG, "SD: looking for file <%s>", bufferFilename);
#if HAS_SDCARD == 1 #if HAS_SDCARD == 1
bool fileExists = SD.exists(bufferFilename); bool fileExists = SD.exists(bufferFilename);
@ -96,8 +110,9 @@ void createFile(void) {
bool fileExists = SD_MMC.exists(bufferFilename); bool fileExists = SD_MMC.exists(bufferFilename);
#endif #endif
// file not exists, create it
if (!fileExists) { if (!fileExists) {
// ESP_LOGD(TAG, "SD: file does not exist: opening"); ESP_LOGD(TAG, "SD: file not found, creating...");
#if HAS_SDCARD == 1 #if HAS_SDCARD == 1
fileSDCard = SD.open(bufferFilename, FILE_WRITE); fileSDCard = SD.open(bufferFilename, FILE_WRITE);
@ -108,18 +123,33 @@ void createFile(void) {
if (fileSDCard) { if (fileSDCard) {
ESP_LOGD(TAG, "SD: name opened: <%s>", bufferFilename); ESP_LOGD(TAG, "SD: name opened: <%s>", bufferFilename);
fileSDCard.print(SDCARD_FILE_HEADER); fileSDCard.print(SDCARD_FILE_HEADER);
#if (COUNT_ENS) #if (defined BAT_MEASURE_ADC || defined HAS_PMU)
fileSDCard.print(SDCARD_FILE_HEADER_CWA); // for Corona-data (CWA) fileSDCard.print(SDCARD_FILE_HEADER_VOLTAGE); // for battery level data
#endif #endif
#if (HAS_SDS011) #if (HAS_SDS011)
fileSDCard.print(SDCARD_FILE_HEADER_SDS011); fileSDCard.print(SDCARD_FILE_HEADER_SDS011);
#endif #endif
fileSDCard.println(); fileSDCard.println();
useSDCard = true; useSDCard = true;
break;
} }
} }
// file exists, append data
else {
ESP_LOGD(TAG, "SD: file found, opening...");
#if HAS_SDCARD == 1
fileSDCard = SD.open(bufferFilename, FILE_APPEND);
#elif HAS_SDCARD == 2
fileSDCard = SD_MMC.open(bufferFilename, FILE_APPEND);
#endif
if (fileSDCard) {
ESP_LOGD(TAG, "SD: name opened: <%s>", bufferFilename);
useSDCard = true;
} }
}
return; return;
} }

View File

@ -10,35 +10,37 @@ static const char TAG[] = __FILE__;
#if (HAS_IF482) #if (HAS_IF482)
#error cannot use IF482 together with SDS011 (both use UART#2) #error cannot use IF482 together with SDS011 (both use UART#2)
#endif #endif
// UART(2) is unused in this project
static HardwareSerial sdsSerial(2); // so we use it here
static SDS011 sdsSensor; // fine dust sensor
// the results of the sensor: SdsDustSensor sds(Serial2);
static float pm10, pm25;
boolean isSDS011Active; bool isSDS011Active = false;
static float pm10 = 0.0, pm25 = 0.0;
// init // init
bool sds011_init() { bool sds011_init() {
pm25 = pm10 = 0.0; sds.begin(9600, SERIAL_8N1, SDS_RX, SDS_TX);
sdsSensor.begin(&sdsSerial, SDS_RX, SDS_TX); sds011_wakeup();
sds011_sleep(); // we do sleep/wakup by ourselves ESP_LOGI(TAG, "SDS011: %s", sds.queryFirmwareVersion().toString().c_str());
sds.setQueryReportingMode();
return true; return true;
} }
// reading data: // reading data:
void sds011_loop() { void sds011_loop() {
if (isSDS011Active) { if (isSDS011Active) {
int sdsErrorCode = sdsSensor.read(&pm25, &pm10); PmResult pm = sds.queryPm();
if (sdsErrorCode) { if (!pm.isOk()) {
pm25 = pm10 = 0.0; ESP_LOGE(TAG, "SDS011: query error %s", pm.statusToString().c_str());
ESP_LOGI(TAG, "SDS011 error: %d", sdsErrorCode); pm10 = pm25 = 0.0;
} else { } else {
ESP_LOGI(TAG, "fine-dust-values: %5.1f,%4.1f", pm10, pm25); ESP_LOGI(TAG, "SDS011: %s", pm.toString().c_str());
pm10 = pm.pm10;
pm25 = pm.pm25;
} }
ESP_LOGD(TAG, "SDS011: go to sleep");
sds011_sleep(); sds011_sleep();
} }
return;
} }
// retrieving stored data: // retrieving stored data:
@ -49,17 +51,16 @@ void sds011_store(sdsStatus_t *sds_store) {
// putting the SDS-sensor to sleep // putting the SDS-sensor to sleep
void sds011_sleep(void) { void sds011_sleep(void) {
sdsSensor.sleep(); WorkingStateResult state = sds.sleep();
isSDS011Active = false; isSDS011Active = state.isWorking();
} }
// start the SDS-sensor // start the SDS-sensor
// needs 30 seconds for warming up // needs 30 seconds for warming up
void sds011_wakeup() { void sds011_wakeup() {
if (!isSDS011Active) { WorkingStateResult state = sds.wakeup();
sdsSensor.wakeup(); isSDS011Active = state.isWorking();
isSDS011Active = true; ESP_LOGD(TAG, "SDS011: %s", state.toString().c_str());
}
} }
#endif // HAS_SDS011 #endif // HAS_SDS011

View File

@ -5,6 +5,8 @@ void setSendIRQ(TimerHandle_t xTimer) {
xTaskNotify(irqHandlerTask, SENDCYCLE_IRQ, eSetBits); xTaskNotify(irqHandlerTask, SENDCYCLE_IRQ, eSetBits);
} }
void setSendIRQ(void) { setSendIRQ(NULL); }
void initSendDataTimer(uint8_t sendcycle) { void initSendDataTimer(uint8_t sendcycle) {
static TimerHandle_t SendDataTimer = NULL; static TimerHandle_t SendDataTimer = NULL;
@ -65,31 +67,26 @@ void SendPayload(uint8_t port) {
mqtt_enqueuedata(&SendBuffer); mqtt_enqueuedata(&SendBuffer);
#endif #endif
// write data to sdcard, if present
#if (HAS_SDCARD)
if (port == COUNTERPORT) {
sdcardWriteData(libpax_macs_wifi, libpax_macs_ble
#if (COUNT_ENS)
,
cwa_report()
#endif
);
}
#endif
} // SendPayload } // SendPayload
// interrupt triggered function to prepare payload to send // timer triggered function to prepare payload to send
void sendData() { void sendData() {
uint8_t bitmask = cfg.payloadmask; uint8_t bitmask = cfg.payloadmask;
uint8_t mask = 1; uint8_t mask = 1;
#if (HAS_GPS) #if (HAS_GPS)
gpsStatus_t gps_status; gpsStatus_t gps_status;
#endif #endif
#if (HAS_SDS011) #if (HAS_SDS011)
sdsStatus_t sds_status; sdsStatus_t sds_status;
#endif #endif
#if ((WIFICOUNTER) || (BLECOUNTER))
struct count_payload_t count =
count_from_libpax; // copy values from global libpax var
ESP_LOGD(TAG, "Sending count results: pax=%d / wifi=%d / ble=%d", count.pax,
count.wifi_count, count.ble_count);
#endif
while (bitmask) { while (bitmask) {
switch (bitmask & mask) { switch (bitmask & mask) {
@ -97,14 +94,14 @@ void sendData() {
#if ((WIFICOUNTER) || (BLECOUNTER)) #if ((WIFICOUNTER) || (BLECOUNTER))
case COUNT_DATA: case COUNT_DATA:
payload.reset(); payload.reset();
#if !(PAYLOAD_OPENSENSEBOX) #if !(PAYLOAD_OPENSENSEBOX)
ESP_LOGI(TAG, "Sending libpax wifi count: %d", libpax_macs_wifi); payload.addCount(count.wifi_count, MAC_SNIFF_WIFI);
payload.addCount(libpax_macs_wifi, MAC_SNIFF_WIFI);
if (cfg.blescan) { if (cfg.blescan) {
ESP_LOGI(TAG, "Sending libpax ble count: %d", libpax_macs_ble); payload.addCount(count.ble_count, MAC_SNIFF_BLE);
payload.addCount(libpax_macs_ble, MAC_SNIFF_BLE);
} }
#endif #endif
#if (HAS_GPS) #if (HAS_GPS)
if (GPSPORT == COUNTERPORT) { if (GPSPORT == COUNTERPORT) {
// send GPS position only if we have a fix // send GPS position only if we have a fix
@ -115,23 +112,35 @@ void sendData() {
ESP_LOGD(TAG, "No valid GPS position"); ESP_LOGD(TAG, "No valid GPS position");
} }
#endif #endif
#if (PAYLOAD_OPENSENSEBOX) #if (PAYLOAD_OPENSENSEBOX)
ESP_LOGI(TAG, "Sending libpax wifi count: %d", libpax_macs_wifi); payload.addCount(count.wifi_count, MAC_SNIFF_WIFI);
payload.addCount(libpax_macs_wifi, MAC_SNIFF_WIFI);
if (cfg.blescan) { if (cfg.blescan) {
ESP_LOGI(TAG, "Sending libpax ble count: %d", libpax_macs_ble); payload.addCount(count.ble_count, MAC_SNIFF_BLE);
payload.addCount(libpax_macs_ble, MAC_SNIFF_BLE);
#endif #endif
#if (HAS_SDS011) #if (HAS_SDS011)
sds011_store(&sds_status); sds011_store(&sds_status);
payload.addSDS(sds_status); payload.addSDS(sds_status);
#endif #endif
SendPayload(COUNTERPORT);
#ifdef HAS_DISPLAY #ifdef HAS_DISPLAY
dp_plotCurve(libpax_macs_ble + libpax_macs_wifi, true); dp_plotCurve(count.pax, true);
#endif #endif
break;
#if (HAS_SDCARD)
sdcardWriteData(count.wifi_count, count.ble_count
#if (defined BAT_MEASURE_ADC || defined HAS_PMU)
,
read_voltage()
#endif #endif
);
#endif // HAS_SDCARD
SendPayload(COUNTERPORT);
break; // case COUNTDATA
#endif // ((WIFICOUNTER) || (BLECOUNTER))
#if (HAS_BME) #if (HAS_BME)
case MEMS_DATA: case MEMS_DATA:
@ -162,10 +171,6 @@ void sendData() {
payload.reset(); payload.reset();
payload.addSensor(sensor_read(1)); payload.addSensor(sensor_read(1));
SendPayload(SENSOR1PORT); SendPayload(SENSOR1PORT);
#if (COUNT_ENS)
if (cfg.countermode != 1)
cwa_clear();
#endif
break; break;
#endif #endif
#if (HAS_SENSOR_2) #if (HAS_SENSOR_2)

View File

@ -2,12 +2,6 @@
#include "globals.h" #include "globals.h"
#include "sensor.h" #include "sensor.h"
#if (COUNT_ENS)
#include "payload.h"
#include "corona.h"
extern PayloadConvert payload;
#endif
// Local logging tag // Local logging tag
static const char TAG[] = __FILE__; static const char TAG[] = __FILE__;
@ -53,16 +47,10 @@ uint8_t *sensor_read(uint8_t sensor) {
case 1: case 1:
// insert user specific sensor data frames here // insert user specific sensor data frames here
// note: Sensor1 fields are used for ENS count, if ENS detection enabled
#if (COUNT_ENS)
if (cfg.enscount)
payload.addCount(cwa_report(), MAC_SNIFF_BLE_ENS);
#else
buf[0] = length; buf[0] = length;
buf[1] = 0x01; buf[1] = 0x01;
buf[2] = 0x02; buf[2] = 0x02;
buf[3] = 0x03; buf[3] = 0x03;
#endif
break; break;
case 2: case 2:

View File

@ -19,9 +19,9 @@ static const char TAG[] = __FILE__;
// G = GPS / R = RTC / L = LORA / * = no sync / ? = never synced // G = GPS / R = RTC / L = LORA / * = no sync / ? = never synced
const char timeSetSymbols[] = {'G', 'R', 'L', '*', '?'}; const char timeSetSymbols[] = {'G', 'R', 'L', '*', '?'};
bool volatile TimePulseTick = false; DRAM_ATTR bool TimePulseTick = false;
DRAM_ATTR unsigned long lastPPS = millis();
timesource_t timeSource = _unsynced; timesource_t timeSource = _unsynced;
time_t _COMPILETIME = compileTime(__DATE__);
TaskHandle_t ClockTask = NULL; TaskHandle_t ClockTask = NULL;
hw_timer_t *ppsIRQ = NULL; hw_timer_t *ppsIRQ = NULL;
@ -40,8 +40,7 @@ Ticker timesyncer;
void setTimeSyncIRQ() { xTaskNotify(irqHandlerTask, TIMESYNC_IRQ, eSetBits); } void setTimeSyncIRQ() { xTaskNotify(irqHandlerTask, TIMESYNC_IRQ, eSetBits); }
void calibrateTime(void) { void calibrateTime(void) {
ESP_LOGD(TAG, "[%0.3f] calibrateTime, timeSource == %d", _seconds(),
timeSource);
time_t t = 0; time_t t = 0;
uint16_t t_msec = 0; uint16_t t_msec = 0;
@ -57,7 +56,7 @@ void calibrateTime(void) {
// has RTC -> fallback to RTC time // has RTC -> fallback to RTC time
#ifdef HAS_RTC #ifdef HAS_RTC
t = get_rtctime(); t = get_rtctime(&t_msec);
// set time from RTC - method will check if time is valid // set time from RTC - method will check if time is valid
setMyTime((uint32_t)t, t_msec, _rtc); setMyTime((uint32_t)t, t_msec, _rtc);
#endif #endif
@ -101,26 +100,27 @@ void IRAM_ATTR setMyTime(uint32_t t_sec, uint16_t t_msec,
vTaskDelay(pdMS_TO_TICKS(1000 - t_msec % 1000)); vTaskDelay(pdMS_TO_TICKS(1000 - t_msec % 1000));
} }
// from here on we are on top of next second
tv.tv_sec = time_to_set; tv.tv_sec = time_to_set;
tv.tv_usec = 0; tv.tv_usec = 0;
sntp_sync_time(&tv); sntp_sync_time(&tv);
ESP_LOGI(TAG, "[%0.3f] UTC time: %d.000 sec", _seconds(), time_to_set); ESP_LOGI(TAG, "[%0.3f] UTC time: %d.000 sec", _seconds(), time_to_set);
// if we have a software pps timer, shift it to top of second // if we have a precise time timesource, set RTC time and shift RTC_INT
if (ppsIRQ != NULL) { // pulse to top of second
timerWrite(ppsIRQ, 0); // reset pps timer
CLOCKIRQ(); // fire clock pps, this advances time 1 sec
}
// if we have got an external timesource, set RTC time and shift RTC_INT pulse
// to top of second
#ifdef HAS_RTC #ifdef HAS_RTC
if ((mytimesource == _gps) || (mytimesource == _lora)) if ((mytimesource == _gps) || (mytimesource == _lora))
set_rtctime(time_to_set); set_rtctime(time_to_set);
#endif #endif
// if we have a software pps timer, shift it to top of second
if (ppsIRQ != NULL) {
timerWrite(ppsIRQ, 0); // reset pps timer
CLOCKIRQ(); // fire clock pps to advance wall clock by 1 sec
}
timeSource = mytimesource; // set global variable timeSource = mytimesource; // set global variable
timesyncer.attach(TIME_SYNC_INTERVAL * 60, setTimeSyncIRQ); timesyncer.attach(TIME_SYNC_INTERVAL * 60, setTimeSyncIRQ);
@ -132,7 +132,7 @@ void IRAM_ATTR setMyTime(uint32_t t_sec, uint16_t t_msec,
"[%0.3f] Failed to synchronise time from source %c | unix sec " "[%0.3f] Failed to synchronise time from source %c | unix sec "
"obtained from source: %d | unix sec at program compilation: %d", "obtained from source: %d | unix sec at program compilation: %d",
_seconds(), timeSetSymbols[mytimesource], time_to_set, _seconds(), timeSetSymbols[mytimesource], time_to_set,
_COMPILETIME); compileTime());
} }
} }
@ -140,29 +140,26 @@ void IRAM_ATTR setMyTime(uint32_t t_sec, uint16_t t_msec,
uint8_t timepulse_init() { uint8_t timepulse_init() {
// set esp-idf API sntp sync mode // set esp-idf API sntp sync mode
//sntp_init(); // sntp_init();
sntp_set_sync_mode(SNTP_SYNC_MODE_IMMED); sntp_set_sync_mode(SNTP_SYNC_MODE_IMMED);
// use time pulse from GPS as time base with fixed 1Hz frequency // if we have, use PPS time pulse from GPS for syncing time on top of second
#ifdef GPS_INT #ifdef GPS_INT
// setup external interupt pin for rising edge of GPS PPS
// setup external interupt pin for rising edge GPS INT
pinMode(GPS_INT, INPUT_PULLDOWN); pinMode(GPS_INT, INPUT_PULLDOWN);
// setup external rtc 1Hz clock as pulse per second clock attachInterrupt(digitalPinToInterrupt(GPS_INT), GPSIRQ, RISING);
ESP_LOGI(TAG, "Timepulse: external (GPS)"); #endif
return 1; // success
// use pulse from on board RTC chip as time base with fixed frequency // if we have, use pulse from on board RTC chip as time base for calendar time
#elif defined RTC_INT #if defined RTC_INT
// setup external interupt pin for falling edge RTC INT // setup external rtc 1Hz clock pulse
pinMode(RTC_INT, INPUT_PULLUP);
// setup external rtc 1Hz clock as pulse per second clock
if (I2C_MUTEX_LOCK()) { if (I2C_MUTEX_LOCK()) {
Rtc.SetSquareWavePinClockFrequency(DS3231SquareWaveClock_1Hz); Rtc.SetSquareWavePinClockFrequency(DS3231SquareWaveClock_1Hz);
Rtc.SetSquareWavePin(DS3231SquareWavePin_ModeClock); Rtc.SetSquareWavePin(DS3231SquareWavePin_ModeClock);
I2C_MUTEX_UNLOCK(); I2C_MUTEX_UNLOCK();
pinMode(RTC_INT, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(RTC_INT), CLOCKIRQ, FALLING);
ESP_LOGI(TAG, "Timepulse: external (RTC)"); ESP_LOGI(TAG, "Timepulse: external (RTC)");
return 1; // success return 1; // success
} else { } else {
@ -172,33 +169,39 @@ uint8_t timepulse_init() {
return 1; // success return 1; // success
#else #else
// use ESP32 hardware timer as time base with adjustable frequency // use ESP32 hardware timer as time base for calendar time
ppsIRQ = timerBegin(1, 8000, true); // set 80 MHz prescaler to 1/10000 sec ppsIRQ = timerBegin(1, 8000, true); // set 80 MHz prescaler to 1/10000 sec
timerAlarmWrite(ppsIRQ, 10000, true); // 1000ms timerAlarmWrite(ppsIRQ, 10000, true); // 1000ms
timerAttachInterrupt(ppsIRQ, &CLOCKIRQ, true);
timerAlarmEnable(ppsIRQ);
ESP_LOGI(TAG, "Timepulse: internal (ESP32 hardware timer)"); ESP_LOGI(TAG, "Timepulse: internal (ESP32 hardware timer)");
return 1; // success return 1; // success
#endif #endif
} // timepulse_init
void timepulse_start(void) { // start cyclic time sync
#ifdef GPS_INT // start external clock gps pps line timesyncer.attach(TIME_SYNC_INTERVAL * 60, setTimeSyncIRQ);
attachInterrupt(digitalPinToInterrupt(GPS_INT), CLOCKIRQ, RISING);
#elif defined RTC_INT // start external clock rtc
attachInterrupt(digitalPinToInterrupt(RTC_INT), CLOCKIRQ, FALLING);
#else // start internal clock esp32 hardware timer
timerAttachInterrupt(ppsIRQ, &CLOCKIRQ, true);
timerAlarmEnable(ppsIRQ);
#endif
// get time if we don't have one // get time if we don't have one
if (timeSource != _set) if (timeSource != _set)
setTimeSyncIRQ(); // init systime by RTC or GPS or LORA setTimeSyncIRQ(); // init systime by RTC or GPS or LORA
// start cyclic time sync
timesyncer.attach(TIME_SYNC_INTERVAL * 60, setTimeSyncIRQ); } // timepulse_init
// interrupt service routine triggered by GPS PPS
void IRAM_ATTR GPSIRQ(void) {
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
// take timestamp
lastPPS = millis(); // last time of pps
// yield only if we should
if (xHigherPriorityTaskWoken)
portYIELD_FROM_ISR();
} }
// interrupt service routine triggered by either pps or esp32 hardware timer // interrupt service routine triggered by esp32 hardware timer
void IRAM_ATTR CLOCKIRQ(void) { void IRAM_ATTR CLOCKIRQ(void) {
BaseType_t xHigherPriorityTaskWoken = pdFALSE; BaseType_t xHigherPriorityTaskWoken = pdFALSE;
@ -211,9 +214,7 @@ void IRAM_ATTR CLOCKIRQ(void) {
// flip time pulse ticker, if needed // flip time pulse ticker, if needed
#ifdef HAS_DISPLAY #ifdef HAS_DISPLAY
#if (defined GPS_INT || defined RTC_INT) TimePulseTick = !TimePulseTick; // flip global variable pulse ticker
TimePulseTick = !TimePulseTick; // flip pulse ticker
#endif
#endif #endif
// yield only if we should // yield only if we should
@ -224,7 +225,9 @@ void IRAM_ATTR CLOCKIRQ(void) {
// helper function to check plausibility of a given epoch time // helper function to check plausibility of a given epoch time
bool timeIsValid(time_t const t) { bool timeIsValid(time_t const t) {
// is t a time in the past? we use compile time to guess // is t a time in the past? we use compile time to guess
return (t > _COMPILETIME); // compile time is some local time, but we do not know it's time zone
// thus, we go 1 full day back to be sure to catch a time in the past
return (t > (compileTime() - 86400));
} }
// helper function to calculate serial transmit time // helper function to calculate serial transmit time
@ -252,7 +255,7 @@ void clock_init(void) {
"clockloop", // name of task "clockloop", // name of task
3072, // stack size of task 3072, // stack size of task
(void *)1, // task parameter (void *)1, // task parameter
4, // priority of the task 6, // priority of the task
&ClockTask, // task handle &ClockTask, // task handle
1); // CPU core 1); // CPU core
@ -283,7 +286,7 @@ void clock_loop(void *taskparameter) { // ClockTask
// set calendar time for next second of clock output // set calendar time for next second of clock output
tt = (time_t)(current_time + 1); tt = (time_t)(current_time + 1);
localtime_r(&tt, &t); localtime_r(&tt, &t);
mktime(&t); tt = mktime(&t);
#if defined HAS_IF482 #if defined HAS_IF482
@ -293,13 +296,13 @@ void clock_loop(void *taskparameter) { // ClockTask
if (xTaskNotifyWait(0x00, ULONG_MAX, &current_time, txDelay) == pdTRUE) { if (xTaskNotifyWait(0x00, ULONG_MAX, &current_time, txDelay) == pdTRUE) {
tt = (time_t)(current_time + 1); tt = (time_t)(current_time + 1);
localtime_r(&tt, &t); localtime_r(&tt, &t);
mktime(&t); tt = mktime(&t);
} }
// send IF482 telegram // send IF482 telegram
IF482.print(IF482_Frame(t)); // note: telegram is for *next* second IF482.print(IF482_Frame(tt)); // note: telegram is for *next* second
ESP_LOGD(TAG, "[%0.3f] IF482: %s", _seconds(), IF482_Frame(t)); ESP_LOGD(TAG, "[%0.3f] IF482: %s", _seconds(), IF482_Frame(tt).c_str());
#elif defined HAS_DCF77 #elif defined HAS_DCF77
@ -340,7 +343,7 @@ void clock_loop(void *taskparameter) { // ClockTask
} // clock_loop() } // clock_loop()
// we use compile date to create a time_t reference "in the past" // we use compile date to create a time_t reference "in the past"
time_t compileTime(const String compile_date) { time_t compileTime(void) {
char s_month[5]; char s_month[5];
int year; int year;
@ -353,10 +356,11 @@ time_t compileTime(const String compile_date) {
if (secs == -1) { if (secs == -1) {
// determine date // determine date
// we go one day back to bypass unknown timezone of local time sscanf(__DATE__, "%s %d %d", s_month, &t.tm_mday, &year);
sscanf(compile_date.c_str(), "%s %d %d", s_month, &t.tm_mday - 1, &year);
t.tm_mon = (strstr(month_names, s_month) - month_names) / 3; t.tm_mon = (strstr(month_names, s_month) - month_names) / 3;
t.tm_year = year - 1900; t.tm_year = year - 1900;
// determine time
sscanf(__TIME__, "%d:%d:%d", &t.tm_hour, &t.tm_min, &t.tm_sec);
// convert to secs local time // convert to secs local time
secs = mktime(&t); secs = mktime(&t);

View File

@ -34,7 +34,7 @@ void timesync_init(void) {
"timesync_proc", // name of task "timesync_proc", // name of task
4096, // stack size of task 4096, // stack size of task
(void *)1, // task parameter (void *)1, // task parameter
3, // priority of the task 7, // priority of the task
&timeSyncProcTask, // task handle &timeSyncProcTask, // task handle
1); // CPU core 1); // CPU core
} }