commit
19ade61e66
57
README.md
57
README.md
@ -202,7 +202,7 @@ Output of sensor and peripheral data is internally switched by a bitmask registe
|
||||
|
||||
# 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
|
||||
|
||||
@ -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"}]`
|
||||
|
||||
# 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
|
||||
|
||||
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
|
||||
#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
|
||||
#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_SCLK (14)
|
||||
|
||||
Please choose the correct number for the connection of the reader/writer.
|
||||
|
||||
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).
|
||||
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.
|
||||
|
||||
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:
|
||||
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.
|
||||
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.
|
||||
|
||||
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
|
||||
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.
|
||||
If you want to change this, modify src/sdcard.cpp and include/sdcard.h.
|
||||
|
||||
# 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
|
||||
|
||||
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
|
||||
|
||||
@ -503,7 +487,7 @@ Send for example `83` `86` as Downlink on Port 2 to get battery status and time/
|
||||
0x02 = RESERVED_DATA
|
||||
0x04 = MEMS_DATA
|
||||
0x08 = GPS_DATA
|
||||
0x10 = SENSOR_1_DATA (also ENS counter)
|
||||
0x10 = SENSOR_1_DATA
|
||||
0x20 = SENSOR_2_DATA
|
||||
0x40 = SENSOR_3_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
|
||||
1 = enabled [default]
|
||||
|
||||
0x18 set ENS counter on/off
|
||||
0x18 reserved
|
||||
|
||||
0 = disabled [default]
|
||||
1 = enabled
|
||||
unused, does nothing
|
||||
|
||||
0x19 set sleep cycle
|
||||
|
||||
@ -621,4 +604,4 @@ Thanks to
|
||||
- [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
|
||||
- [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
|
||||
|
@ -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
|
@ -5,10 +5,7 @@
|
||||
#include "cyclic.h"
|
||||
#include "qrcode.h"
|
||||
#include "power.h"
|
||||
|
||||
#if (COUNT_ENS)
|
||||
#include "corona.h"
|
||||
#endif
|
||||
#include "timekeeper.h"
|
||||
|
||||
#if (HAS_DISPLAY) == 1
|
||||
#include <OneBitDisplay.h>
|
||||
|
@ -71,7 +71,6 @@ typedef struct __attribute__((packed)) {
|
||||
uint8_t wifiant; // 0=internal, 1=external (for LoPy/LoPy4)
|
||||
uint8_t rgblum; // RGB Led luminosity (0..100%)
|
||||
uint8_t payloadmask; // bitswitches for payload data
|
||||
uint8_t enscount; // 0=disabled 1= enabled
|
||||
|
||||
#ifdef HAS_BME680
|
||||
uint8_t
|
||||
@ -111,6 +110,5 @@ typedef struct {
|
||||
} sdsStatus_t;
|
||||
|
||||
extern char clientId[20]; // unique clientID
|
||||
extern time_t _COMPILETIME; // epoch build time
|
||||
|
||||
#endif
|
@ -5,13 +5,10 @@
|
||||
#include <RtcDateTime.h>
|
||||
#include "timekeeper.h"
|
||||
|
||||
#ifdef GPS_I2C // Needed for reading from I2C Bus
|
||||
#include <Wire.h>
|
||||
#ifndef GPS_BAUDRATE
|
||||
#define GPS_BAUDRATE 115200UL
|
||||
#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 TaskHandle_t GpsTask;
|
||||
|
||||
|
@ -24,7 +24,7 @@
|
||||
void irqHandler(void *pvParameters);
|
||||
void mask_user_IRQ();
|
||||
void unmask_user_IRQ();
|
||||
void doIRQ(int irq);
|
||||
void IRAM_ATTR doIRQ(int irq);
|
||||
|
||||
extern TaskHandle_t irqHandlerTask;
|
||||
|
||||
|
@ -8,6 +8,6 @@
|
||||
|
||||
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
|
@ -7,6 +7,8 @@
|
||||
#include <esp_coexist.h> // needed for coex version display
|
||||
#include <esp_wifi.h> // needed for wifi init / deinit
|
||||
|
||||
#include <libpax_helpers.h>
|
||||
|
||||
#include "globals.h"
|
||||
#include "reset.h"
|
||||
#include "i2c.h"
|
||||
@ -18,9 +20,7 @@
|
||||
#include "sensor.h"
|
||||
#include "lorawan.h"
|
||||
#include "timekeeper.h"
|
||||
#include "corona.h"
|
||||
#include "boot.h"
|
||||
#include "libpax_helpers.h"
|
||||
#include "power.h"
|
||||
#include "antenna.h"
|
||||
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
#include <rom/rtc.h>
|
||||
|
||||
#include "libpax_helpers.h"
|
||||
#include "senddata.h"
|
||||
#include "cyclic.h"
|
||||
#include "configmanager.h"
|
||||
|
@ -8,6 +8,8 @@
|
||||
#include "lorawan.h"
|
||||
#include "display.h"
|
||||
#include "power.h"
|
||||
#include "sdcard.h"
|
||||
#include "sds011read.h"
|
||||
|
||||
void reset_rtc_vars(void);
|
||||
void do_reset(bool warmstart);
|
||||
|
@ -12,7 +12,7 @@ extern RtcDS3231<TwoWire> Rtc; // make RTC instance globally available
|
||||
uint8_t rtc_init(void);
|
||||
uint8_t set_rtctime(time_t t);
|
||||
void sync_rtctime(void);
|
||||
time_t get_rtctime(void);
|
||||
time_t get_rtctime(uint16_t *msec);
|
||||
float get_rtctemp(void);
|
||||
|
||||
#endif // _RTCTIME_H
|
@ -1,13 +1,13 @@
|
||||
#ifndef _SDCARD_H
|
||||
#define _SDCARD_H
|
||||
|
||||
#include <globals.h>
|
||||
#include "globals.h"
|
||||
#include <stdio.h>
|
||||
#include <SPI.h>
|
||||
|
||||
#if (HAS_SDCARD)
|
||||
#if HAS_SDCARD == 1
|
||||
#include <mySD.h>
|
||||
#include <SD.h>
|
||||
#elif HAS_SDCARD == 2
|
||||
#include <SD_MMC.h>
|
||||
#else
|
||||
@ -54,14 +54,15 @@
|
||||
#define SDCARD_DATA3 13
|
||||
#endif
|
||||
|
||||
#define SDCARD_FILE_NAME "/paxcount.%02d"
|
||||
#define SDCARD_FILE_HEADER "date, time, wifi, bluet"
|
||||
#define SDCARD_FILE_NAME clientId
|
||||
#define SDCARD_FILE_HEADER "timestamp,wifi,ble"
|
||||
|
||||
#if (COUNT_ENS)
|
||||
#define SDCARD_FILE_HEADER_CWA ",cwa"
|
||||
#if (defined BAT_MEASURE_ADC || defined HAS_PMU)
|
||||
#define SDCARD_FILE_HEADER_VOLTAGE ",voltage"
|
||||
#endif
|
||||
|
||||
bool sdcard_init(void);
|
||||
bool sdcard_init(bool create = true);
|
||||
void sdcard_close(void);
|
||||
void sdcardWriteData(uint16_t, uint16_t, uint16_t = 0);
|
||||
|
||||
#endif // _SDCARD_H
|
||||
|
@ -1,15 +1,24 @@
|
||||
#ifndef _SDS011READ_H
|
||||
#define _SDS011READ_H
|
||||
|
||||
#include <SDS011.h>
|
||||
#include <SdsDustSensor.h>
|
||||
#include "globals.h"
|
||||
|
||||
#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();
|
||||
void sds011_loop();
|
||||
void sds011_sleep(void);
|
||||
void sds011_wakeup(void);
|
||||
void sds011_store(sdsStatus_t *sds_store);
|
||||
|
||||
#endif // _SDS011READ_H
|
||||
|
@ -1,7 +1,6 @@
|
||||
#ifndef _SENDDATA_H
|
||||
#define _SENDDATA_H
|
||||
|
||||
#include "libpax_helpers.h"
|
||||
#include "spislave.h"
|
||||
#include "mqttclient.h"
|
||||
#include "cyclic.h"
|
||||
@ -10,18 +9,13 @@
|
||||
#include "display.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 sendData(void);
|
||||
void checkSendQueues(void);
|
||||
void flushQueues(void);
|
||||
bool allQueuesEmtpy(void);
|
||||
void setSendIRQ(TimerHandle_t xTimer = NULL);
|
||||
void setSendIRQ(TimerHandle_t xTimer);
|
||||
void setSendIRQ(void);
|
||||
void initSendDataTimer(uint8_t sendcycle);
|
||||
|
||||
#endif // _SENDDATA_H_
|
||||
|
@ -20,22 +20,22 @@ extern const char timeSetSymbols[];
|
||||
extern Ticker timesyncer;
|
||||
extern timesource_t timeSource;
|
||||
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;
|
||||
|
||||
void IRAM_ATTR CLOCKIRQ(void);
|
||||
void IRAM_ATTR GPSIRQ(void);
|
||||
void clock_init(void);
|
||||
void clock_loop(void *pvParameters);
|
||||
void timepulse_start(void);
|
||||
void setTimeSyncIRQ(void);
|
||||
uint8_t timepulse_init(void);
|
||||
bool timeIsValid(time_t const t);
|
||||
void calibrateTime(void);
|
||||
void IRAM_ATTR setMyTime(uint32_t t_sec, uint16_t t_msec,
|
||||
timesource_t mytimesource);
|
||||
time_t compileTime(const String compile_date);
|
||||
time_t compileTime(void);
|
||||
time_t mkgmtime(const struct tm *ptm);
|
||||
TickType_t tx_Ticks(uint32_t framesize, unsigned long baud, uint32_t config,
|
||||
int8_t rxPin, int8_t txPins);
|
||||
|
||||
#endif // _timekeeper_H
|
@ -48,7 +48,7 @@ description = Paxcounter is a device for metering passenger flows in realtime. I
|
||||
|
||||
[common]
|
||||
; for release_version use max. 10 chars total, use any decimal format like "a.b.c"
|
||||
release_version = 3.1.0
|
||||
release_version = 3.2.0
|
||||
; DEBUG LEVEL: For production run set to 0, otherwise device will leak RAM while running!
|
||||
; 0=None, 1=Error, 2=Warn, 3=Info, 4=Debug, 5=Verbose
|
||||
debug_level = 3
|
||||
@ -56,7 +56,7 @@ extra_scripts = pre:build.py
|
||||
otakeyfile = ota.conf
|
||||
lorakeyfile = loraconf.h
|
||||
lmicconfigfile = lmic_config.h
|
||||
platform_espressif32 = espressif32@3.4.0
|
||||
platform_espressif32 = espressif32@3.5.0
|
||||
monitor_speed = 115200
|
||||
upload_speed = 115200 ; set by build.py and taken from hal file
|
||||
display_library = ; set by build.py and taken from hal file
|
||||
@ -77,7 +77,7 @@ lib_deps_sensors =
|
||||
adafruit/Adafruit BME280 Library @ ^2.2.1
|
||||
adafruit/Adafruit BMP085 Library @ ^1.2.0
|
||||
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 =
|
||||
https://github.com/dbSuS/libpax.git @ ^1.0.0
|
||||
https://github.com/SukkoPera/Arduino-Rokkit-Hash.git
|
||||
@ -85,7 +85,6 @@ lib_deps_basic =
|
||||
makuna/RTC @ ^2.3.5
|
||||
spacehuhn/SimpleButton
|
||||
lewisxhe/AXP202X_Library @ ^1.1.3
|
||||
geeksville/esp32-micro-sdcard @ ^0.1.1
|
||||
256dpi/MQTT @ ^2.4.8
|
||||
lib_deps_all =
|
||||
${common.lib_deps_basic}
|
||||
|
@ -84,11 +84,6 @@ function Decoder(bytes, port) {
|
||||
}
|
||||
}
|
||||
|
||||
if (port === 10) {
|
||||
// ENS count
|
||||
return decode(bytes, [uint16], ['ens']);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -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.port = input.fPort; // comment out if you do not want to inlude the port
|
||||
|
||||
|
@ -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) {
|
||||
data.hdop /= 100;
|
||||
data.latitude /= 1000000;
|
||||
|
@ -6,12 +6,6 @@
|
||||
// Local logging tag
|
||||
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
|
||||
#define DEVCONFIG "paxcntcfg"
|
||||
|
||||
@ -57,7 +51,6 @@ static void defaultConfig(configData_t *myconfig) {
|
||||
myconfig->wifiant = 0; // 0=internal, 1=external (for LoPy/LoPy4)
|
||||
myconfig->rgblum = RGBLUMINOSITY; // RGB Led luminosity (0..100%)
|
||||
myconfig->payloadmask = PAYLOADMASK; // payloads as defined in default
|
||||
myconfig->enscount = COUNT_ENS; // 0=disabled, 1=enabled
|
||||
|
||||
#ifdef HAS_BME680
|
||||
// initial BSEC state for BME680 sensor
|
||||
|
@ -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 ¬ifier : cwaSeenNotifiers) {
|
||||
ESP_LOGD(TAG, "CWA forget <%04X>", notifier.first);
|
||||
// }
|
||||
}
|
||||
#endif
|
||||
// clear everything, otherwise we would count the same device again, as in the
|
||||
// next cycle it likely will advertise with a different hash-value
|
||||
cwaSeenNotifiers.clear();
|
||||
}
|
||||
|
||||
// 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
|
@ -9,10 +9,6 @@ static const char TAG[] = __FILE__;
|
||||
|
||||
Ticker cyclicTimer;
|
||||
|
||||
#if (HAS_SDS011)
|
||||
extern boolean isSDS011Active;
|
||||
#endif
|
||||
|
||||
void setCyclicIRQ() { xTaskNotify(irqHandlerTask, CYCLIC_IRQ, eSetBits); }
|
||||
|
||||
// do all housekeeping
|
||||
@ -22,41 +18,59 @@ void doHousekeeping() {
|
||||
if ((RTC_runmode == RUNMODE_UPDATE) || (RTC_runmode == RUNMODE_MAINTENANCE))
|
||||
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.getFreeHeap(), ESP.getMinFreeHeap(), ESP.getHeapSize(),
|
||||
ESP.getMaxAllocHeap(), uxTaskGetStackHighWaterMark(NULL));
|
||||
|
||||
if (irqHandlerTask != NULL)
|
||||
ESP_LOGD(TAG, "IRQhandler %d bytes left | Taskstate = %d",
|
||||
uxTaskGetStackHighWaterMark(irqHandlerTask),
|
||||
eTaskGetState(irqHandlerTask));
|
||||
if (rcmdTask != NULL)
|
||||
ESP_LOGD(TAG, "Rcommand interpreter %d bytes left | Taskstate = %d",
|
||||
uxTaskGetStackHighWaterMark(rcmdTask), eTaskGetState(rcmdTask));
|
||||
|
||||
#if (HAS_LORA)
|
||||
if (lmicTask != NULL)
|
||||
ESP_LOGD(TAG, "LMiCtask %d bytes left | Taskstate = %d",
|
||||
uxTaskGetStackHighWaterMark(lmicTask), eTaskGetState(lmicTask));
|
||||
if (lorasendTask != NULL)
|
||||
ESP_LOGD(TAG, "Lorasendtask %d bytes left | Taskstate = %d",
|
||||
uxTaskGetStackHighWaterMark(lorasendTask),
|
||||
eTaskGetState(lorasendTask));
|
||||
#endif
|
||||
|
||||
#if (HAS_GPS)
|
||||
if (GpsTask != NULL)
|
||||
ESP_LOGD(TAG, "Gpsloop %d bytes left | Taskstate = %d",
|
||||
uxTaskGetStackHighWaterMark(GpsTask), eTaskGetState(GpsTask));
|
||||
#endif
|
||||
|
||||
#ifdef HAS_SPI
|
||||
if (spiTask != NULL)
|
||||
ESP_LOGD(TAG, "spiloop %d bytes left | Taskstate = %d",
|
||||
uxTaskGetStackHighWaterMark(spiTask), eTaskGetState(spiTask));
|
||||
#endif
|
||||
|
||||
#ifdef HAS_MQTT
|
||||
if (mqttTask != NULL)
|
||||
ESP_LOGD(TAG, "MQTTloop %d bytes left | Taskstate = %d",
|
||||
uxTaskGetStackHighWaterMark(mqttTask), eTaskGetState(mqttTask));
|
||||
#endif
|
||||
|
||||
#if (defined HAS_DCF77 || defined HAS_IF482)
|
||||
if (ClockTask != NULL)
|
||||
ESP_LOGD(TAG, "Clockloop %d bytes left | Taskstate = %d",
|
||||
uxTaskGetStackHighWaterMark(ClockTask), eTaskGetState(ClockTask));
|
||||
#endif
|
||||
|
||||
#if (HAS_LED != NOT_A_PIN) || defined(HAS_RGB_LED)
|
||||
if (ledLoopTask != NULL)
|
||||
ESP_LOGD(TAG, "LEDloop %d bytes left | Taskstate = %d",
|
||||
uxTaskGetStackHighWaterMark(ledLoopTask),
|
||||
eTaskGetState(ledLoopTask));
|
||||
@ -106,10 +120,8 @@ void doHousekeeping() {
|
||||
|
||||
#if (HAS_SDS011)
|
||||
if (isSDS011Active) {
|
||||
ESP_LOGD(TAG, "SDS011: go to sleep");
|
||||
sds011_loop();
|
||||
} else {
|
||||
ESP_LOGD(TAG, "SDS011: wakeup");
|
||||
sds011_wakeup();
|
||||
}
|
||||
#endif
|
||||
|
@ -51,18 +51,17 @@ void DCF77_Pulse(uint8_t bit) {
|
||||
|
||||
// helper function to convert decimal to bcd digit
|
||||
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;
|
||||
|
||||
*odd_parity = 0;
|
||||
*parity = 0;
|
||||
for (uint8_t i = startpos; i <= endpos; i++) {
|
||||
bcd += (data & 1) ? set_dcfbit(i) : 0;
|
||||
*odd_parity += (data & 1);
|
||||
bcd += data & 1 ? set_dcfbit(i) : 0;
|
||||
*parity ^= data & 1;
|
||||
data >>= 1;
|
||||
}
|
||||
*odd_parity %= 2;
|
||||
|
||||
return bcd;
|
||||
}
|
||||
@ -96,14 +95,14 @@ uint64_t DCF77_Frame(const struct tm t) {
|
||||
|
||||
// DATE (36..58)
|
||||
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);
|
||||
parity_sum += parity;
|
||||
parity_sum ^= 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);
|
||||
parity_sum += parity;
|
||||
frame += parity_sum % 2 ? set_dcfbit(58) : 0;
|
||||
parity_sum ^= parity;
|
||||
frame += parity_sum ? set_dcfbit(58) : 0;
|
||||
|
||||
return frame;
|
||||
|
||||
|
@ -174,16 +174,10 @@ void dp_init(bool verbose) {
|
||||
|
||||
void dp_refresh(bool nextPage) {
|
||||
|
||||
// update counter values from libpax
|
||||
libpax_counter_count(&count_from_libpax);
|
||||
|
||||
#ifndef HAS_BUTTON
|
||||
static uint32_t framecounter = 0;
|
||||
#endif
|
||||
|
||||
// update histogram
|
||||
dp_plotCurve(count_from_libpax.pax, false);
|
||||
|
||||
// if display is switched off we don't refresh it to relax cpu
|
||||
if (!DisplayIsOn && (DisplayIsOn == cfg.screenon))
|
||||
return;
|
||||
@ -207,7 +201,6 @@ void dp_refresh(bool nextPage) {
|
||||
#endif
|
||||
|
||||
dp_drawPage(nextPage);
|
||||
dp_dump(displaybuf);
|
||||
|
||||
I2C_MUTEX_UNLOCK(); // release i2c bus access
|
||||
|
||||
@ -219,6 +212,7 @@ void dp_drawPage(bool nextpage) {
|
||||
// write display content to display buffer
|
||||
// nextpage = true -> flip 1 page
|
||||
|
||||
struct count_payload_t count; // libpax count storage
|
||||
static uint8_t DisplayPage = 0;
|
||||
char timeState, strftime_buf[64];
|
||||
time_t now;
|
||||
@ -233,6 +227,9 @@ void dp_drawPage(bool nextpage) {
|
||||
dp_clear();
|
||||
}
|
||||
|
||||
// update counter values from libpax
|
||||
libpax_counter_count(&count);
|
||||
|
||||
// cursor home
|
||||
dp_setTextCursor(0, 0);
|
||||
|
||||
@ -240,7 +237,7 @@ void dp_drawPage(bool nextpage) {
|
||||
// display number of unique macs total Wifi + BLE
|
||||
if (DisplayPage < 5) {
|
||||
dp_setFont(MY_FONT_STRETCHED);
|
||||
dp_printf("%-5d", count_from_libpax.pax);
|
||||
dp_printf("%-5d", count.pax);
|
||||
}
|
||||
|
||||
switch (DisplayPage) {
|
||||
@ -264,31 +261,21 @@ void dp_drawPage(bool nextpage) {
|
||||
|
||||
#if ((WIFICOUNTER) && (BLECOUNTER))
|
||||
if (cfg.wifiscan)
|
||||
dp_printf("WIFI:%-5d", count_from_libpax.wifi_count);
|
||||
dp_printf("WIFI:%-5d", count.wifi_count);
|
||||
else
|
||||
dp_printf("WIFI:off");
|
||||
if (cfg.blescan)
|
||||
#if (COUNT_ENS)
|
||||
if (cfg.enscount)
|
||||
dp_printf(" CWA:%-5d", cwa_report());
|
||||
else
|
||||
#endif
|
||||
dp_printf("BLTH:%-5d", count_from_libpax.ble_count);
|
||||
dp_printf("BLTH:%-5d", count.ble_count);
|
||||
else
|
||||
dp_printf(" BLTH:off");
|
||||
#elif ((WIFICOUNTER) && (!BLECOUNTER))
|
||||
if (cfg.wifiscan)
|
||||
dp_printf("WIFI:%-5d", count_from_libpax.wifi_count);
|
||||
dp_printf("WIFI:%-5d", count.wifi_count);
|
||||
else
|
||||
dp_printf("WIFI:off");
|
||||
#elif ((!WIFICOUNTER) && (BLECOUNTER))
|
||||
if (cfg.blescan)
|
||||
dp_printf("BLTH:%-5d", count_from_libpax.ble_count);
|
||||
#if (COUNT_ENS)
|
||||
if (cfg.enscount)
|
||||
dp_printf("(CWA:%d)", cwa_report());
|
||||
else
|
||||
#endif
|
||||
dp_printf("BLTH:%-5d", count.ble_count);
|
||||
dp_printf("BLTH:off");
|
||||
#else
|
||||
dp_printf("Sniffer disabled");
|
||||
@ -327,7 +314,6 @@ void dp_drawPage(bool nextpage) {
|
||||
|
||||
#if (TIME_SYNC_INTERVAL)
|
||||
timeState = TimePulseTick ? ' ' : timeSetSymbols[timeSource];
|
||||
TimePulseTick = false;
|
||||
time(&now);
|
||||
localtime_r(&now, &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_setFont(MY_FONT_SMALL, 0);
|
||||
#endif // HAS_LORA
|
||||
|
||||
dp_dump(displaybuf);
|
||||
break;
|
||||
|
||||
// ---------- page 1: lorawan parameters ----------
|
||||
@ -380,6 +368,8 @@ void dp_drawPage(bool nextpage) {
|
||||
LMIC.seqnoDn ? LMIC.seqnoDn - 1 : 0);
|
||||
dp_println();
|
||||
dp_printf("SNR:%-5d RSSI:%-5d", (LMIC.snr + 2) / 4, LMIC.rssi);
|
||||
|
||||
dp_dump(displaybuf);
|
||||
break;
|
||||
#else // flip page if we are unattended
|
||||
DisplayPage++;
|
||||
@ -412,6 +402,8 @@ void dp_drawPage(bool nextpage) {
|
||||
dp_printf("No fix");
|
||||
wasnofix = true;
|
||||
}
|
||||
|
||||
dp_dump(displaybuf);
|
||||
break;
|
||||
#else // flip page if we are unattended
|
||||
DisplayPage++;
|
||||
@ -439,6 +431,8 @@ void dp_drawPage(bool nextpage) {
|
||||
// line 6-7: Pre
|
||||
dp_printf("PRE:%-2.1f", bme_status.pressure);
|
||||
#endif // HAS_BME680
|
||||
|
||||
dp_dump(displaybuf);
|
||||
break; // page 3
|
||||
#else // flip page if we are unattended
|
||||
DisplayPage++;
|
||||
@ -453,14 +447,15 @@ void dp_drawPage(bool nextpage) {
|
||||
localtime_r(&now, &timeinfo);
|
||||
strftime(strftime_buf, sizeof(strftime_buf), "%T", &timeinfo);
|
||||
dp_printf("%.8s", strftime_buf);
|
||||
|
||||
dp_dump(displaybuf);
|
||||
break;
|
||||
|
||||
// ---------- page 5: pax graph ----------
|
||||
case 5:
|
||||
|
||||
dp_setFont(MY_FONT_NORMAL);
|
||||
dp_setTextCursor(0, 0);
|
||||
dp_printf("Pax graph");
|
||||
// update histogram
|
||||
dp_plotCurve(count.pax, false);
|
||||
dp_dump(plotbuf);
|
||||
break;
|
||||
|
||||
|
267
src/gpsread.cpp
267
src/gpsread.cpp
@ -6,68 +6,147 @@
|
||||
// Local logging tag
|
||||
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;
|
||||
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;
|
||||
|
||||
#ifdef GPS_SERIAL
|
||||
HardwareSerial GPS_Serial(1); // use UART #1
|
||||
static uint16_t nmea_txDelay_ms =
|
||||
(tx_Ticks(NMEA_FRAME_SIZE, GPS_SERIAL) / portTICK_PERIOD_MS);
|
||||
#else
|
||||
static uint16_t nmea_txDelay_ms = 0;
|
||||
#endif
|
||||
|
||||
// Ublox UBX packet data
|
||||
|
||||
// UBX CFG-PRT packet
|
||||
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
|
||||
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");
|
||||
|
||||
GPS_Serial.begin(GPS_SERIAL);
|
||||
#elif defined GPS_I2C
|
||||
ESP_LOGI(TAG, "Opening I2C GPS");
|
||||
Wire.begin(GPS_I2C, 400000); // I2C connect to GPS device with 400 KHz
|
||||
Wire.beginTransmission(GPS_ADDR);
|
||||
Wire.write(0x00); // dummy write
|
||||
if (Wire.endTransmission()) {
|
||||
ESP_LOGE(TAG, "Quectel L76 GPS chip not found");
|
||||
return 0;
|
||||
} else
|
||||
ESP_LOGI(TAG, "Quectel L76 GPS chip found");
|
||||
#endif
|
||||
|
||||
restoreDefaults();
|
||||
delay(100);
|
||||
|
||||
changeBaudrate();
|
||||
delay(100);
|
||||
GPS_Serial.flush();
|
||||
GPS_Serial.updateBaudRate(GPS_BAUDRATE);
|
||||
|
||||
disableNmea();
|
||||
|
||||
return 1;
|
||||
|
||||
} // 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
|
||||
void gps_storelocation(gpsStatus_t *gps_store) {
|
||||
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
|
||||
time_t get_gpstime(uint16_t *msec) {
|
||||
time_t get_gpstime(uint16_t *msec = 0) {
|
||||
|
||||
// poll NMEA ZDA sentence
|
||||
#ifdef GPS_SERIAL
|
||||
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
|
||||
const uint16_t txDelay =
|
||||
70U * 1000 / (GPS_BAUDRATE / 9); // serial tx of 70 NMEA chars
|
||||
|
||||
// did we get a current date & time?
|
||||
if (gpstime.isValid()) {
|
||||
if (gps.time.age() < 1000) {
|
||||
|
||||
uint32_t delay_ms =
|
||||
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
|
||||
// convert tinygps time format to struct tm format
|
||||
struct tm gps_tm = {0};
|
||||
gps_tm.tm_sec = zdatime % 100; // second (UTC)
|
||||
gps_tm.tm_min = (zdatime / 100) % 100; // minute (UTC)
|
||||
gps_tm.tm_hour = zdatime / 10000; // hour (UTC)
|
||||
gps_tm.tm_mday = atoi(gpsday.value()); // day, 01 to 31
|
||||
gps_tm.tm_mon = atoi(gpsmonth.value()) - 1; // month, 01 to 12
|
||||
gps_tm.tm_year = atoi(gpsyear.value()) - 1900; // year, YYYY
|
||||
gps_tm.tm_sec = gps.time.second();
|
||||
gps_tm.tm_min = gps.time.minute();
|
||||
gps_tm.tm_hour = gps.time.hour();
|
||||
gps_tm.tm_mday = gps.date.day();
|
||||
gps_tm.tm_mon = gps.date.month() - 1; // 1-12 -> 0-11
|
||||
gps_tm.tm_year = gps.date.year() - 1900; // 2000+ -> years since 1900
|
||||
|
||||
// convert UTC tm to time_t epoch
|
||||
gps_tm.tm_isdst = 0; // UTC has no DST
|
||||
time_t t = mkgmtime(&gps_tm);
|
||||
|
||||
// add protocol delay with millisecond precision
|
||||
t += (time_t)(delay_ms / 1000);
|
||||
*msec = delay_ms % 1000; // fractional seconds
|
||||
#ifdef GPS_INT
|
||||
// if we have a recent GPS PPS pulse, sync on top of next second
|
||||
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;
|
||||
}
|
||||
|
||||
ESP_LOGD(TAG, "no valid GPS time");
|
||||
|
||||
return 0;
|
||||
|
||||
} // get_gpstime()
|
||||
@ -139,42 +217,17 @@ void gps_loop(void *pvParameters) {
|
||||
|
||||
_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
|
||||
while (GPS_Serial.available()) {
|
||||
while (1) {
|
||||
while (cfg.payloadmask & GPS_DATA) {
|
||||
|
||||
while (GPS_Serial.available())
|
||||
gps.encode(GPS_Serial.read());
|
||||
yield();
|
||||
|
||||
delay(5);
|
||||
}
|
||||
#elif defined GPS_I2C
|
||||
Wire.requestFrom(GPS_ADDR, 32); // caution: this is a blocking call
|
||||
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
|
||||
delay(1000);
|
||||
} // infinite while loop
|
||||
|
||||
} // gps_loop()
|
||||
|
||||
|
@ -18,7 +18,7 @@
|
||||
#define CFG_sx1276_radio 1
|
||||
|
||||
#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
|
||||
|
||||
// Pins for I2C interface of OLED Display
|
||||
|
@ -20,7 +20,7 @@
|
||||
#define CFG_sx1276_radio 1
|
||||
|
||||
#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 BAT_MEASURE_ADC ADC2_GPIO13_CHANNEL // battery probe GPIO pin
|
||||
|
@ -20,7 +20,7 @@
|
||||
#define CFG_sx1276_radio 1
|
||||
|
||||
#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 BAT_MEASURE_ADC ADC1_GPIO37_CHANNEL // battery probe GPIO pin
|
||||
|
@ -10,7 +10,7 @@
|
||||
// Hardware related definitions for lolin32lite (without LoRa shield)
|
||||
|
||||
#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_SCL SCL
|
||||
|
@ -15,7 +15,7 @@
|
||||
|
||||
#define HAS_DISPLAY 1 // OLED-Display on board
|
||||
//#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 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
|
||||
|
@ -20,11 +20,6 @@
|
||||
#define HAS_ANTENNA_SWITCH (16) // pin for switching wifi antenna
|
||||
#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
|
||||
//#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
|
||||
|
@ -30,11 +30,6 @@
|
||||
#define HAS_ANTENNA_SWITCH (21) // pin for switching wifi antenna (P12)
|
||||
#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
|
||||
//#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
|
||||
|
@ -14,7 +14,7 @@
|
||||
|
||||
#define HAS_DISPLAY 1 // OLED-Display on board
|
||||
//#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 HAS_BUTTON KEY_BUILTIN
|
||||
|
||||
|
@ -16,10 +16,12 @@
|
||||
#define CFG_sx1276_radio 1 // HPD13A LoRa SoC
|
||||
|
||||
// enable only if you want to store a local paxcount table on the device
|
||||
#define HAS_SDCARD 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_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_VOLTAGE_DIVIDER 2 // voltage divider 100k/100k on board
|
||||
|
||||
|
@ -86,18 +86,19 @@ static const char TAG[] = __FILE__;
|
||||
|
||||
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)
|
||||
mon = 'M'; // time had been set but sync not completed
|
||||
else
|
||||
mon = 'A'; // time has been set and was recently synced
|
||||
|
||||
// generate IF482 telegram
|
||||
// snprintf(out, sizeof(out), "O%cL%s\r", mon, myTZ.dateTime(t, UTC_TIME,
|
||||
// "ymdwHis").c_str());
|
||||
|
||||
// ESP_LOGD(TAG, "[%s] IF482 date/time: %s", ctime(time(NULL), out);
|
||||
// generate IF482 telegram for local time
|
||||
struct tm tt;
|
||||
localtime_r(&t, &tt);
|
||||
mktime(&tt);
|
||||
strftime(buf, sizeof(buf), "%y%m%d%u%H%M%S", &tt);
|
||||
snprintf(out, sizeof(out), "O%cL%s\r", mon, buf);
|
||||
|
||||
return out;
|
||||
}
|
||||
|
@ -212,7 +212,7 @@ void ledLoop(void *parameter) {
|
||||
previousLEDState = LEDState;
|
||||
}
|
||||
// give yield to CPU
|
||||
delay(2);
|
||||
delay(5);
|
||||
} // while(1)
|
||||
}; // ledloop()
|
||||
|
||||
|
@ -13,6 +13,7 @@ uint8_t MatrixDisplayIsOn = 0;
|
||||
static uint8_t displaybuf[LED_MATRIX_WIDTH * LED_MATRIX_HEIGHT / 8] = {0};
|
||||
static unsigned long ulLastNumMacs = 0;
|
||||
static time_t ulLastTime = time(NULL);
|
||||
static struct count_payload_t count; // libpax count storage
|
||||
|
||||
hw_timer_t *matrixDisplayIRQ = NULL;
|
||||
|
||||
@ -76,12 +77,12 @@ void refreshTheMatrixDisplay(bool nextPage) {
|
||||
case 0:
|
||||
|
||||
// update counter values from libpax
|
||||
libpax_counter_count(&count_from_libpax);
|
||||
libpax_counter_count(&count);
|
||||
|
||||
if (cfg.countermode == 1) {
|
||||
// cumulative counter mode -> display total number of pax
|
||||
if (ulLastNumMacs != count_from_libpax.pax) {
|
||||
ulLastNumMacs = count_from_libpax.pax;
|
||||
if (ulLastNumMacs != count.pax) {
|
||||
ulLastNumMacs = count.pax;
|
||||
matrix.clear();
|
||||
DrawNumber(String(ulLastNumMacs));
|
||||
}
|
||||
@ -89,10 +90,10 @@ void refreshTheMatrixDisplay(bool nextPage) {
|
||||
|
||||
else { // cyclic counter mode -> plot a line diagram
|
||||
|
||||
if (ulLastNumMacs != count_from_libpax.pax) {
|
||||
if (ulLastNumMacs != count.pax) {
|
||||
|
||||
// next count cycle?
|
||||
if (count_from_libpax.pax == 0) {
|
||||
if (count.pax == 0) {
|
||||
|
||||
// matrix full? then scroll left 1 dot, else increment column
|
||||
if (col < (LED_MATRIX_WIDTH - 1))
|
||||
@ -104,7 +105,7 @@ void refreshTheMatrixDisplay(bool nextPage) {
|
||||
matrix.drawPoint(col, row, 0); // clear current dot
|
||||
|
||||
// scale and set new dot
|
||||
ulLastNumMacs = count_from_libpax.pax;
|
||||
ulLastNumMacs = count.pax;
|
||||
level = ulLastNumMacs / LINE_DIAGRAM_DIVIDER;
|
||||
row = level <= LED_MATRIX_HEIGHT
|
||||
? LED_MATRIX_HEIGHT - 1 - level % LED_MATRIX_HEIGHT
|
||||
@ -120,7 +121,7 @@ void refreshTheMatrixDisplay(bool nextPage) {
|
||||
if (ulLastTime != t) {
|
||||
ulLastTime = t;
|
||||
matrix.clear();
|
||||
//DrawNumber(myTZ.dateTime("H:i:s").c_str());
|
||||
// DrawNumber(myTZ.dateTime("H:i:s").c_str());
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -5,18 +5,9 @@ static const char TAG[] = __FILE__;
|
||||
|
||||
// libpax payload
|
||||
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) {
|
||||
libpax_counter_init(process_count, &count_from_libpax, cfg.sendcycle * 2,
|
||||
libpax_counter_init(setSendIRQ, &count_from_libpax, cfg.sendcycle * 2,
|
||||
cfg.countermode);
|
||||
libpax_counter_start();
|
||||
}
|
@ -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.port = input.fPort; // comment out if you do not want to inlude the port
|
||||
|
||||
|
@ -179,10 +179,10 @@ void showLoraKeys(void) {
|
||||
// all EUI buffer so we do it here to a temp
|
||||
// buffer to be able to display them
|
||||
uint8_t buf[32];
|
||||
os_getDevEui((u1_t *)buf);
|
||||
printKey("DevEUI", buf, 8, true);
|
||||
os_getArtEui((u1_t *)buf);
|
||||
printKey("AppEUI", buf, 8, true);
|
||||
os_getDevEui((u1_t *)buf);
|
||||
printKey("DevEUI", buf, 8, true);
|
||||
os_getDevKey((u1_t *)buf);
|
||||
printKey("AppKey", buf, 16, false);
|
||||
}
|
||||
@ -314,7 +314,7 @@ esp_err_t lmic_init(void) {
|
||||
"lorasendtask", // name of task
|
||||
3072, // stack size of task
|
||||
(void *)1, // parameter of the task
|
||||
1, // priority of the task
|
||||
2, // priority of the task
|
||||
&lorasendTask, // task handle
|
||||
1); // CPU core
|
||||
|
||||
|
92
src/main.cpp
92
src/main.cpp
@ -27,60 +27,52 @@ licenses. Refer to LICENSE.txt file in repository for more details.
|
||||
|
||||
Task Core Prio Purpose
|
||||
-------------------------------------------------------------------------------
|
||||
ledloop 0 3 blinks LEDs
|
||||
spiloop 0 2 reads/writes data on spi interface
|
||||
IDLE 0 0 ESP32 arduino scheduler -> runs wifi sniffer
|
||||
ledloop* 1 1 blinks LEDs
|
||||
spiloop# 0 2 reads/writes data on spi interface
|
||||
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
|
||||
clockloop 1 4 generates realtime telegrams for external clock
|
||||
mqttloop 1 2 reads/writes data on ETH interface
|
||||
timesync_proc 1 3 processes realtime time sync requests
|
||||
irqhandler 1 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
|
||||
* spinning task, always ready
|
||||
# blocked/waiting task
|
||||
|
||||
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
|
||||
-------------------------------------------------------------------------------
|
||||
0 displayIRQ -> display refresh -> 40ms (DISPLAYREFRESH_MS)
|
||||
1 ppsIRQ -> pps clock irq -> 1sec
|
||||
2 (unused)
|
||||
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)
|
||||
-------------------------------------------------------------------------------
|
||||
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
|
||||
@ -126,7 +118,7 @@ void setup() {
|
||||
snprintf(clientId, 20, "paxcounter_%08x", hashedmac);
|
||||
ESP_LOGI(TAG, "Starting %s v%s (runmode=%d / restarts=%d)", clientId,
|
||||
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
|
||||
#if (VERBOSE)
|
||||
@ -244,9 +236,9 @@ void setup() {
|
||||
"ledloop", // name of task
|
||||
1024, // stack size of task
|
||||
(void *)1, // parameter of the task
|
||||
3, // priority of the task
|
||||
1, // priority of the task
|
||||
&ledLoopTask, // task handle
|
||||
0); // CPU core
|
||||
1); // CPU core
|
||||
#endif
|
||||
|
||||
// initialize wifi antenna
|
||||
@ -328,7 +320,7 @@ void setup() {
|
||||
ESP_LOGI(TAG, "Starting GPS Feed...");
|
||||
xTaskCreatePinnedToCore(gps_loop, // task function
|
||||
"gpsloop", // name of task
|
||||
4096, // stack size of task
|
||||
8192, // stack size of task
|
||||
(void *)1, // parameter of the task
|
||||
1, // priority of the task
|
||||
&GpsTask, // task handle
|
||||
@ -339,15 +331,9 @@ void setup() {
|
||||
// initialize sensors
|
||||
#if (HAS_SENSORS)
|
||||
#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)");
|
||||
sensor_init();
|
||||
#endif
|
||||
#endif
|
||||
#if (HAS_SENSOR_2)
|
||||
strcat_P(features, " SENS(2)");
|
||||
sensor_init();
|
||||
@ -432,7 +418,7 @@ void setup() {
|
||||
"irqhandler", // name of task
|
||||
4096, // stack size of task
|
||||
(void *)1, // parameter of the task
|
||||
2, // priority of the task
|
||||
4, // priority of the task
|
||||
&irqHandlerTask, // task handle
|
||||
1); // CPU core
|
||||
|
||||
@ -494,8 +480,7 @@ void setup() {
|
||||
cyclicTimer.attach(HOMECYCLE, setCyclicIRQ);
|
||||
|
||||
// only if we have a timesource we do timesync
|
||||
#if ((TIME_SYNC_LORAWAN) || (TIME_SYNC_LORASERVER) || (HAS_GPS) || \
|
||||
defined HAS_RTC)
|
||||
#if ((TIME_SYNC_LORAWAN) || (TIME_SYNC_LORASERVER) || (HAS_GPS) || (HAS_RTC))
|
||||
|
||||
#if (defined HAS_IF482 || defined HAS_DCF77)
|
||||
ESP_LOGI(TAG, "Starting Clock Controller...");
|
||||
@ -507,8 +492,7 @@ void setup() {
|
||||
#endif
|
||||
|
||||
ESP_LOGI(TAG, "Starting Timekeeper...");
|
||||
_ASSERT(timepulse_init()); // setup pps timepulse
|
||||
timepulse_start(); // starts pps and cyclic time sync
|
||||
_ASSERT(timepulse_init()); // starts pps and cyclic time sync
|
||||
strcat_P(features, " TIME");
|
||||
|
||||
#endif // timesync
|
||||
|
@ -37,7 +37,7 @@ esp_err_t mqtt_init(void) {
|
||||
SEND_QUEUE_SIZE * PAYLOAD_BUFFER_SIZE);
|
||||
|
||||
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);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
@ -18,6 +18,11 @@
|
||||
#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
|
||||
|
||||
// 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
|
||||
#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
|
||||
@ -27,10 +32,6 @@
|
||||
#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
|
||||
|
||||
// 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
|
||||
*
|
||||
* |< Scan Window > |< Scan Window > | ... |< Scan Window > |
|
||||
|
@ -1,7 +1,6 @@
|
||||
// Basic Config
|
||||
#include "globals.h"
|
||||
#include "rcommand.h"
|
||||
#include "libpax_helpers.h"
|
||||
|
||||
// Local logging tag
|
||||
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
|
||||
};
|
||||
|
||||
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[]) {
|
||||
ESP_LOGI(TAG, "Remote command: load config from NVRAM");
|
||||
loadConfig();
|
||||
@ -430,7 +420,7 @@ static const cmd_t table[] = {
|
||||
{0x10, set_rgblum, 1}, {0x13, set_sensor, 2},
|
||||
{0x14, set_payloadmask, 1}, {0x15, set_bme, 1},
|
||||
{0x16, set_batt, 1}, {0x17, set_wifiscan, 1},
|
||||
{0x18, set_enscount, 1}, {0x19, set_sleepcycle, 2},
|
||||
{0x18, set_flush, 0}, {0x19, set_sleepcycle, 2},
|
||||
{0x20, set_loadconfig, 0}, {0x21, set_saveconfig, 0},
|
||||
{0x80, get_config, 0}, {0x81, get_status, 0},
|
||||
{0x83, get_batt, 0}, {0x84, get_gps, 0},
|
||||
@ -493,7 +483,7 @@ void rcmd_process(void *pvParameters) {
|
||||
rcmd_execute(RcmdBuffer.cmd, RcmdBuffer.cmdLen);
|
||||
}
|
||||
|
||||
delay(2); // yield to CPU
|
||||
delay(5); // yield to CPU
|
||||
} // rcmd_process()
|
||||
|
||||
// enqueue remote command
|
||||
|
@ -126,6 +126,11 @@ void enter_deepsleep(const uint64_t wakeup_sec, gpio_num_t wakeup_gpio) {
|
||||
sds011_sleep();
|
||||
#endif
|
||||
|
||||
// flush & close sd card, if we have
|
||||
#if (HAS_SDCARD)
|
||||
sdcard_close();
|
||||
#endif
|
||||
|
||||
// wait a while (max 100 sec) to clear send queues
|
||||
ESP_LOGI(TAG, "Waiting until send queues are empty...");
|
||||
for (i = 100; i > 0; i--) {
|
||||
|
@ -25,14 +25,14 @@ uint8_t rtc_init(void) {
|
||||
}
|
||||
|
||||
#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();
|
||||
time_t t = tt.Epoch32Time(); // sec2000 -> epoch
|
||||
|
||||
if (!Rtc.IsDateTimeValid() || !timeIsValid(t)) {
|
||||
ESP_LOGW(TAG, "RTC has no recent time, setting to compiled time");
|
||||
Rtc.SetDateTime(
|
||||
RtcDateTime(_COMPILETIME - SECS_YR_2000)); // epoch -> sec2000
|
||||
ESP_LOGW(TAG, "RTC has no recent time, setting to compiletime");
|
||||
Rtc.SetDateTime(RtcDateTime(mkgmtime(compileTime()) -
|
||||
SECS_YR_2000)); // epoch -> sec2000
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -62,15 +62,23 @@ uint8_t set_rtctime(time_t t) { // t is sec epoch time
|
||||
}
|
||||
} // set_rtctime()
|
||||
|
||||
time_t get_rtctime(void) {
|
||||
time_t get_rtctime(uint16_t *msec) {
|
||||
time_t t = 0;
|
||||
*msec = 0;
|
||||
if (I2C_MUTEX_LOCK()) {
|
||||
if (Rtc.IsDateTimeValid() && Rtc.GetIsRunning()) {
|
||||
RtcDateTime tt = Rtc.GetDateTime();
|
||||
t = tt.Epoch32Time(); // sec2000 -> epoch
|
||||
}
|
||||
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 {
|
||||
ESP_LOGE(TAG, "RTC get time failure");
|
||||
return 0; // failure
|
||||
|
@ -1,4 +1,7 @@
|
||||
// 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
|
||||
static const char TAG[] = __FILE__;
|
||||
@ -8,20 +11,26 @@ static const char TAG[] = __FILE__;
|
||||
#ifdef HAS_SDCARD
|
||||
|
||||
static bool useSDCard;
|
||||
|
||||
static void createFile(void);
|
||||
static void openFile(void);
|
||||
|
||||
File fileSDCard;
|
||||
|
||||
bool sdcard_init() {
|
||||
ESP_LOGI(TAG, "looking for SD-card...");
|
||||
#if HAS_SDCARD == 1
|
||||
SPIClass sd_spi;
|
||||
#endif
|
||||
|
||||
bool sdcard_init(bool create) {
|
||||
|
||||
// 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/sdmmc_host.html
|
||||
|
||||
ESP_LOGI(TAG, "looking for SD-card...");
|
||||
#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
|
||||
// enable internal pullups of sd-data lines
|
||||
gpio_set_pull_mode(gpio_num_t(SDCARD_DATA0), GPIO_PULLUP_ONLY);
|
||||
@ -33,20 +42,27 @@ bool sdcard_init() {
|
||||
|
||||
if (useSDCard) {
|
||||
ESP_LOGI(TAG, "SD-card found");
|
||||
createFile();
|
||||
} else
|
||||
openFile();
|
||||
return true;
|
||||
} else {
|
||||
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,
|
||||
__attribute__((unused)) uint16_t noBleCWA) {
|
||||
__attribute__((unused)) uint16_t voltage) {
|
||||
static int counterWrites = 0;
|
||||
char tempBuffer[12 + 1];
|
||||
char tempBuffer[20 + 1];
|
||||
time_t t = time(NULL);
|
||||
struct tm tt;
|
||||
localtime_r(&t, &tt);
|
||||
mktime(&tt);
|
||||
gmtime_r(&t, &tt); // make UTC timestamp
|
||||
|
||||
#if (HAS_SDS011)
|
||||
sdsStatus_t sds;
|
||||
@ -55,40 +71,38 @@ void sdcardWriteData(uint16_t noWifi, uint16_t noBle,
|
||||
if (!useSDCard)
|
||||
return;
|
||||
|
||||
ESP_LOGD(TAG, "writing to SD-card");
|
||||
strftime(tempBuffer, sizeof(tempBuffer), "%d.%m.%Y", &tt);
|
||||
ESP_LOGI(TAG, "SD: writing data");
|
||||
strftime(tempBuffer, sizeof(tempBuffer), "%FT%TZ", &tt);
|
||||
fileSDCard.print(tempBuffer);
|
||||
strftime(tempBuffer, sizeof(tempBuffer), "%H.%M.%S", &tt);
|
||||
snprintf(tempBuffer, sizeof(tempBuffer), ",%d,%d", noWifi, noBle);
|
||||
fileSDCard.print(tempBuffer);
|
||||
sprintf(tempBuffer, "%d,%d", noWifi, noBle);
|
||||
fileSDCard.print(tempBuffer);
|
||||
#if (COUNT_ENS)
|
||||
sprintf(tempBuffer, ",%d", noBleCWA);
|
||||
#if (defined BAT_MEASURE_ADC || defined HAS_PMU)
|
||||
snprintf(tempBuffer, sizeof(tempBuffer), ",%d", voltage);
|
||||
fileSDCard.print(tempBuffer);
|
||||
#endif
|
||||
#if (HAS_SDS011)
|
||||
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);
|
||||
#endif
|
||||
fileSDCard.println();
|
||||
|
||||
if (++counterWrites > 2) {
|
||||
// force writing to SD-card
|
||||
ESP_LOGD(TAG, "flushing data to card");
|
||||
ESP_LOGI(TAG, "SD: flushing data");
|
||||
fileSDCard.flush();
|
||||
counterWrites = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void createFile(void) {
|
||||
char bufferFilename[8 + 1 + 3 + 1];
|
||||
void openFile(void) {
|
||||
char bufferFilename[30];
|
||||
|
||||
useSDCard = false;
|
||||
|
||||
for (int i = 0; i < 100; i++) {
|
||||
sprintf(bufferFilename, SDCARD_FILE_NAME, i);
|
||||
// ESP_LOGD(TAG, "SD: looking for file <%s>", bufferFilename);
|
||||
snprintf(bufferFilename, sizeof(bufferFilename), "/%s.csv", SDCARD_FILE_NAME);
|
||||
ESP_LOGI(TAG, "SD: looking for file <%s>", bufferFilename);
|
||||
|
||||
#if HAS_SDCARD == 1
|
||||
bool fileExists = SD.exists(bufferFilename);
|
||||
@ -96,8 +110,9 @@ void createFile(void) {
|
||||
bool fileExists = SD_MMC.exists(bufferFilename);
|
||||
#endif
|
||||
|
||||
// file not exists, create it
|
||||
if (!fileExists) {
|
||||
// ESP_LOGD(TAG, "SD: file does not exist: opening");
|
||||
ESP_LOGD(TAG, "SD: file not found, creating...");
|
||||
|
||||
#if HAS_SDCARD == 1
|
||||
fileSDCard = SD.open(bufferFilename, FILE_WRITE);
|
||||
@ -108,18 +123,33 @@ void createFile(void) {
|
||||
if (fileSDCard) {
|
||||
ESP_LOGD(TAG, "SD: name opened: <%s>", bufferFilename);
|
||||
fileSDCard.print(SDCARD_FILE_HEADER);
|
||||
#if (COUNT_ENS)
|
||||
fileSDCard.print(SDCARD_FILE_HEADER_CWA); // for Corona-data (CWA)
|
||||
#if (defined BAT_MEASURE_ADC || defined HAS_PMU)
|
||||
fileSDCard.print(SDCARD_FILE_HEADER_VOLTAGE); // for battery level data
|
||||
#endif
|
||||
#if (HAS_SDS011)
|
||||
fileSDCard.print(SDCARD_FILE_HEADER_SDS011);
|
||||
#endif
|
||||
fileSDCard.println();
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -10,35 +10,37 @@ static const char TAG[] = __FILE__;
|
||||
#if (HAS_IF482)
|
||||
#error cannot use IF482 together with SDS011 (both use UART#2)
|
||||
#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:
|
||||
static float pm10, pm25;
|
||||
boolean isSDS011Active;
|
||||
SdsDustSensor sds(Serial2);
|
||||
|
||||
bool isSDS011Active = false;
|
||||
static float pm10 = 0.0, pm25 = 0.0;
|
||||
|
||||
// init
|
||||
bool sds011_init() {
|
||||
pm25 = pm10 = 0.0;
|
||||
sdsSensor.begin(&sdsSerial, SDS_RX, SDS_TX);
|
||||
sds011_sleep(); // we do sleep/wakup by ourselves
|
||||
sds.begin(9600, SERIAL_8N1, SDS_RX, SDS_TX);
|
||||
sds011_wakeup();
|
||||
ESP_LOGI(TAG, "SDS011: %s", sds.queryFirmwareVersion().toString().c_str());
|
||||
sds.setQueryReportingMode();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// reading data:
|
||||
void sds011_loop() {
|
||||
if (isSDS011Active) {
|
||||
int sdsErrorCode = sdsSensor.read(&pm25, &pm10);
|
||||
if (sdsErrorCode) {
|
||||
pm25 = pm10 = 0.0;
|
||||
ESP_LOGI(TAG, "SDS011 error: %d", sdsErrorCode);
|
||||
PmResult pm = sds.queryPm();
|
||||
if (!pm.isOk()) {
|
||||
ESP_LOGE(TAG, "SDS011: query error %s", pm.statusToString().c_str());
|
||||
pm10 = pm25 = 0.0;
|
||||
} 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();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// retrieving stored data:
|
||||
@ -49,17 +51,16 @@ void sds011_store(sdsStatus_t *sds_store) {
|
||||
|
||||
// putting the SDS-sensor to sleep
|
||||
void sds011_sleep(void) {
|
||||
sdsSensor.sleep();
|
||||
isSDS011Active = false;
|
||||
WorkingStateResult state = sds.sleep();
|
||||
isSDS011Active = state.isWorking();
|
||||
}
|
||||
|
||||
// start the SDS-sensor
|
||||
// needs 30 seconds for warming up
|
||||
void sds011_wakeup() {
|
||||
if (!isSDS011Active) {
|
||||
sdsSensor.wakeup();
|
||||
isSDS011Active = true;
|
||||
}
|
||||
WorkingStateResult state = sds.wakeup();
|
||||
isSDS011Active = state.isWorking();
|
||||
ESP_LOGD(TAG, "SDS011: %s", state.toString().c_str());
|
||||
}
|
||||
|
||||
#endif // HAS_SDS011
|
||||
|
@ -5,6 +5,8 @@ void setSendIRQ(TimerHandle_t xTimer) {
|
||||
xTaskNotify(irqHandlerTask, SENDCYCLE_IRQ, eSetBits);
|
||||
}
|
||||
|
||||
void setSendIRQ(void) { setSendIRQ(NULL); }
|
||||
|
||||
void initSendDataTimer(uint8_t sendcycle) {
|
||||
static TimerHandle_t SendDataTimer = NULL;
|
||||
|
||||
@ -65,31 +67,26 @@ void SendPayload(uint8_t port) {
|
||||
mqtt_enqueuedata(&SendBuffer);
|
||||
#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
|
||||
|
||||
// interrupt triggered function to prepare payload to send
|
||||
// timer triggered function to prepare payload to send
|
||||
void sendData() {
|
||||
|
||||
uint8_t bitmask = cfg.payloadmask;
|
||||
uint8_t mask = 1;
|
||||
|
||||
#if (HAS_GPS)
|
||||
gpsStatus_t gps_status;
|
||||
#endif
|
||||
#if (HAS_SDS011)
|
||||
sdsStatus_t sds_status;
|
||||
#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) {
|
||||
switch (bitmask & mask) {
|
||||
@ -97,14 +94,14 @@ void sendData() {
|
||||
#if ((WIFICOUNTER) || (BLECOUNTER))
|
||||
case COUNT_DATA:
|
||||
payload.reset();
|
||||
|
||||
#if !(PAYLOAD_OPENSENSEBOX)
|
||||
ESP_LOGI(TAG, "Sending libpax wifi count: %d", libpax_macs_wifi);
|
||||
payload.addCount(libpax_macs_wifi, MAC_SNIFF_WIFI);
|
||||
payload.addCount(count.wifi_count, MAC_SNIFF_WIFI);
|
||||
if (cfg.blescan) {
|
||||
ESP_LOGI(TAG, "Sending libpax ble count: %d", libpax_macs_ble);
|
||||
payload.addCount(libpax_macs_ble, MAC_SNIFF_BLE);
|
||||
payload.addCount(count.ble_count, MAC_SNIFF_BLE);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if (HAS_GPS)
|
||||
if (GPSPORT == COUNTERPORT) {
|
||||
// send GPS position only if we have a fix
|
||||
@ -115,23 +112,35 @@ void sendData() {
|
||||
ESP_LOGD(TAG, "No valid GPS position");
|
||||
}
|
||||
#endif
|
||||
|
||||
#if (PAYLOAD_OPENSENSEBOX)
|
||||
ESP_LOGI(TAG, "Sending libpax wifi count: %d", libpax_macs_wifi);
|
||||
payload.addCount(libpax_macs_wifi, MAC_SNIFF_WIFI);
|
||||
payload.addCount(count.wifi_count, MAC_SNIFF_WIFI);
|
||||
if (cfg.blescan) {
|
||||
ESP_LOGI(TAG, "Sending libpax ble count: %d", libpax_macs_ble);
|
||||
payload.addCount(libpax_macs_ble, MAC_SNIFF_BLE);
|
||||
payload.addCount(count.ble_count, MAC_SNIFF_BLE);
|
||||
#endif
|
||||
|
||||
#if (HAS_SDS011)
|
||||
sds011_store(&sds_status);
|
||||
payload.addSDS(sds_status);
|
||||
#endif
|
||||
SendPayload(COUNTERPORT);
|
||||
|
||||
#ifdef HAS_DISPLAY
|
||||
dp_plotCurve(libpax_macs_ble + libpax_macs_wifi, true);
|
||||
dp_plotCurve(count.pax, true);
|
||||
#endif
|
||||
break;
|
||||
|
||||
#if (HAS_SDCARD)
|
||||
sdcardWriteData(count.wifi_count, count.ble_count
|
||||
#if (defined BAT_MEASURE_ADC || defined HAS_PMU)
|
||||
,
|
||||
read_voltage()
|
||||
#endif
|
||||
);
|
||||
#endif // HAS_SDCARD
|
||||
|
||||
SendPayload(COUNTERPORT);
|
||||
break; // case COUNTDATA
|
||||
|
||||
#endif // ((WIFICOUNTER) || (BLECOUNTER))
|
||||
|
||||
#if (HAS_BME)
|
||||
case MEMS_DATA:
|
||||
@ -162,10 +171,6 @@ void sendData() {
|
||||
payload.reset();
|
||||
payload.addSensor(sensor_read(1));
|
||||
SendPayload(SENSOR1PORT);
|
||||
#if (COUNT_ENS)
|
||||
if (cfg.countermode != 1)
|
||||
cwa_clear();
|
||||
#endif
|
||||
break;
|
||||
#endif
|
||||
#if (HAS_SENSOR_2)
|
||||
|
@ -2,12 +2,6 @@
|
||||
#include "globals.h"
|
||||
#include "sensor.h"
|
||||
|
||||
#if (COUNT_ENS)
|
||||
#include "payload.h"
|
||||
#include "corona.h"
|
||||
extern PayloadConvert payload;
|
||||
#endif
|
||||
|
||||
// Local logging tag
|
||||
static const char TAG[] = __FILE__;
|
||||
|
||||
@ -53,16 +47,10 @@ uint8_t *sensor_read(uint8_t sensor) {
|
||||
case 1:
|
||||
|
||||
// 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[1] = 0x01;
|
||||
buf[2] = 0x02;
|
||||
buf[3] = 0x03;
|
||||
#endif
|
||||
break;
|
||||
case 2:
|
||||
|
||||
|
@ -19,9 +19,9 @@ static const char TAG[] = __FILE__;
|
||||
// G = GPS / R = RTC / L = LORA / * = no sync / ? = never synced
|
||||
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;
|
||||
time_t _COMPILETIME = compileTime(__DATE__);
|
||||
TaskHandle_t ClockTask = NULL;
|
||||
hw_timer_t *ppsIRQ = NULL;
|
||||
|
||||
@ -40,8 +40,7 @@ Ticker timesyncer;
|
||||
void setTimeSyncIRQ() { xTaskNotify(irqHandlerTask, TIMESYNC_IRQ, eSetBits); }
|
||||
|
||||
void calibrateTime(void) {
|
||||
ESP_LOGD(TAG, "[%0.3f] calibrateTime, timeSource == %d", _seconds(),
|
||||
timeSource);
|
||||
|
||||
time_t t = 0;
|
||||
uint16_t t_msec = 0;
|
||||
|
||||
@ -57,7 +56,7 @@ void calibrateTime(void) {
|
||||
|
||||
// has RTC -> fallback to RTC time
|
||||
#ifdef HAS_RTC
|
||||
t = get_rtctime();
|
||||
t = get_rtctime(&t_msec);
|
||||
// set time from RTC - method will check if time is valid
|
||||
setMyTime((uint32_t)t, t_msec, _rtc);
|
||||
#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));
|
||||
}
|
||||
|
||||
// from here on we are on top of next second
|
||||
|
||||
tv.tv_sec = time_to_set;
|
||||
tv.tv_usec = 0;
|
||||
sntp_sync_time(&tv);
|
||||
|
||||
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 (ppsIRQ != NULL) {
|
||||
|
||||
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
|
||||
// if we have a precise time timesource, set RTC time and shift RTC_INT
|
||||
// pulse to top of second
|
||||
#ifdef HAS_RTC
|
||||
if ((mytimesource == _gps) || (mytimesource == _lora))
|
||||
set_rtctime(time_to_set);
|
||||
#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
|
||||
|
||||
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 "
|
||||
"obtained from source: %d | unix sec at program compilation: %d",
|
||||
_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() {
|
||||
|
||||
// set esp-idf API sntp sync mode
|
||||
//sntp_init();
|
||||
// sntp_init();
|
||||
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
|
||||
|
||||
// setup external interupt pin for rising edge GPS INT
|
||||
// setup external interupt pin for rising edge of GPS PPS
|
||||
pinMode(GPS_INT, INPUT_PULLDOWN);
|
||||
// setup external rtc 1Hz clock as pulse per second clock
|
||||
ESP_LOGI(TAG, "Timepulse: external (GPS)");
|
||||
return 1; // success
|
||||
attachInterrupt(digitalPinToInterrupt(GPS_INT), GPSIRQ, RISING);
|
||||
#endif
|
||||
|
||||
// use pulse from on board RTC chip as time base with fixed frequency
|
||||
#elif defined RTC_INT
|
||||
// if we have, use pulse from on board RTC chip as time base for calendar time
|
||||
#if defined RTC_INT
|
||||
|
||||
// setup external interupt pin for falling edge RTC INT
|
||||
pinMode(RTC_INT, INPUT_PULLUP);
|
||||
|
||||
// setup external rtc 1Hz clock as pulse per second clock
|
||||
// setup external rtc 1Hz clock pulse
|
||||
if (I2C_MUTEX_LOCK()) {
|
||||
Rtc.SetSquareWavePinClockFrequency(DS3231SquareWaveClock_1Hz);
|
||||
Rtc.SetSquareWavePin(DS3231SquareWavePin_ModeClock);
|
||||
I2C_MUTEX_UNLOCK();
|
||||
pinMode(RTC_INT, INPUT_PULLUP);
|
||||
attachInterrupt(digitalPinToInterrupt(RTC_INT), CLOCKIRQ, FALLING);
|
||||
ESP_LOGI(TAG, "Timepulse: external (RTC)");
|
||||
return 1; // success
|
||||
} else {
|
||||
@ -172,33 +169,39 @@ uint8_t timepulse_init() {
|
||||
return 1; // success
|
||||
|
||||
#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
|
||||
timerAlarmWrite(ppsIRQ, 10000, true); // 1000ms
|
||||
timerAttachInterrupt(ppsIRQ, &CLOCKIRQ, true);
|
||||
timerAlarmEnable(ppsIRQ);
|
||||
ESP_LOGI(TAG, "Timepulse: internal (ESP32 hardware timer)");
|
||||
return 1; // success
|
||||
|
||||
#endif
|
||||
} // timepulse_init
|
||||
|
||||
void timepulse_start(void) {
|
||||
#ifdef GPS_INT // start external clock gps pps line
|
||||
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
|
||||
// start cyclic time sync
|
||||
timesyncer.attach(TIME_SYNC_INTERVAL * 60, setTimeSyncIRQ);
|
||||
|
||||
// get time if we don't have one
|
||||
if (timeSource != _set)
|
||||
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) {
|
||||
|
||||
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
|
||||
@ -211,9 +214,7 @@ void IRAM_ATTR CLOCKIRQ(void) {
|
||||
|
||||
// flip time pulse ticker, if needed
|
||||
#ifdef HAS_DISPLAY
|
||||
#if (defined GPS_INT || defined RTC_INT)
|
||||
TimePulseTick = !TimePulseTick; // flip pulse ticker
|
||||
#endif
|
||||
TimePulseTick = !TimePulseTick; // flip global variable pulse ticker
|
||||
#endif
|
||||
|
||||
// yield only if we should
|
||||
@ -224,7 +225,9 @@ void IRAM_ATTR CLOCKIRQ(void) {
|
||||
// helper function to check plausibility of a given epoch time
|
||||
bool timeIsValid(time_t const t) {
|
||||
// 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
|
||||
@ -252,7 +255,7 @@ void clock_init(void) {
|
||||
"clockloop", // name of task
|
||||
3072, // stack size of task
|
||||
(void *)1, // task parameter
|
||||
4, // priority of the task
|
||||
6, // priority of the task
|
||||
&ClockTask, // task handle
|
||||
1); // CPU core
|
||||
|
||||
@ -283,7 +286,7 @@ void clock_loop(void *taskparameter) { // ClockTask
|
||||
// set calendar time for next second of clock output
|
||||
tt = (time_t)(current_time + 1);
|
||||
localtime_r(&tt, &t);
|
||||
mktime(&t);
|
||||
tt = mktime(&t);
|
||||
|
||||
#if defined HAS_IF482
|
||||
|
||||
@ -293,13 +296,13 @@ void clock_loop(void *taskparameter) { // ClockTask
|
||||
if (xTaskNotifyWait(0x00, ULONG_MAX, ¤t_time, txDelay) == pdTRUE) {
|
||||
tt = (time_t)(current_time + 1);
|
||||
localtime_r(&tt, &t);
|
||||
mktime(&t);
|
||||
tt = mktime(&t);
|
||||
}
|
||||
|
||||
// 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
|
||||
|
||||
@ -340,7 +343,7 @@ void clock_loop(void *taskparameter) { // ClockTask
|
||||
} // clock_loop()
|
||||
|
||||
// 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];
|
||||
int year;
|
||||
@ -353,10 +356,11 @@ time_t compileTime(const String compile_date) {
|
||||
if (secs == -1) {
|
||||
|
||||
// determine date
|
||||
// we go one day back to bypass unknown timezone of local time
|
||||
sscanf(compile_date.c_str(), "%s %d %d", s_month, &t.tm_mday - 1, &year);
|
||||
sscanf(__DATE__, "%s %d %d", s_month, &t.tm_mday, &year);
|
||||
t.tm_mon = (strstr(month_names, s_month) - month_names) / 3;
|
||||
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
|
||||
secs = mktime(&t);
|
||||
|
@ -34,7 +34,7 @@ void timesync_init(void) {
|
||||
"timesync_proc", // name of task
|
||||
4096, // stack size of task
|
||||
(void *)1, // task parameter
|
||||
3, // priority of the task
|
||||
7, // priority of the task
|
||||
&timeSyncProcTask, // task handle
|
||||
1); // CPU core
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user