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
|
# 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
|
||||||
|
@ -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 "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>
|
||||||
|
@ -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
|
@ -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;
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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
|
@ -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"
|
||||||
|
|
||||||
|
@ -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"
|
||||||
|
@ -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);
|
||||||
|
@ -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
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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_
|
||||||
|
@ -10,8 +10,8 @@
|
|||||||
#include "dcf77.h"
|
#include "dcf77.h"
|
||||||
#include "esp_sntp.h"
|
#include "esp_sntp.h"
|
||||||
|
|
||||||
#define SECS_YR_2000 (946684800UL) // the time at the start of y2k
|
#define SECS_YR_2000 (946684800UL) // the time at the start of y2k
|
||||||
#define GPS_UTC_DIFF 315964800UL // seconds diff between gps and utc epoch
|
#define GPS_UTC_DIFF 315964800UL // seconds diff between gps and utc epoch
|
||||||
#define LEAP_SECS_SINCE_GPSEPOCH 18UL // state of 2021
|
#define LEAP_SECS_SINCE_GPSEPOCH 18UL // state of 2021
|
||||||
|
|
||||||
enum timesource_t { _gps, _rtc, _lora, _unsynced, _set };
|
enum timesource_t { _gps, _rtc, _lora, _unsynced, _set };
|
||||||
@ -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
|
@ -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}
|
||||||
|
@ -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.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
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
@ -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
|
||||||
|
@ -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;
|
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,44 +18,62 @@ 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));
|
||||||
ESP_LOGD(TAG, "IRQhandler %d bytes left | Taskstate = %d",
|
|
||||||
uxTaskGetStackHighWaterMark(irqHandlerTask),
|
if (irqHandlerTask != NULL)
|
||||||
eTaskGetState(irqHandlerTask));
|
ESP_LOGD(TAG, "IRQhandler %d bytes left | Taskstate = %d",
|
||||||
ESP_LOGD(TAG, "Rcommand interpreter %d bytes left | Taskstate = %d",
|
uxTaskGetStackHighWaterMark(irqHandlerTask),
|
||||||
uxTaskGetStackHighWaterMark(rcmdTask), eTaskGetState(rcmdTask));
|
eTaskGetState(irqHandlerTask));
|
||||||
|
if (rcmdTask != NULL)
|
||||||
|
ESP_LOGD(TAG, "Rcommand interpreter %d bytes left | Taskstate = %d",
|
||||||
|
uxTaskGetStackHighWaterMark(rcmdTask), eTaskGetState(rcmdTask));
|
||||||
|
|
||||||
#if (HAS_LORA)
|
#if (HAS_LORA)
|
||||||
ESP_LOGD(TAG, "LMiCtask %d bytes left | Taskstate = %d",
|
if (lmicTask != NULL)
|
||||||
uxTaskGetStackHighWaterMark(lmicTask), eTaskGetState(lmicTask));
|
ESP_LOGD(TAG, "LMiCtask %d bytes left | Taskstate = %d",
|
||||||
ESP_LOGD(TAG, "Lorasendtask %d bytes left | Taskstate = %d",
|
uxTaskGetStackHighWaterMark(lmicTask), eTaskGetState(lmicTask));
|
||||||
uxTaskGetStackHighWaterMark(lorasendTask),
|
if (lorasendTask != NULL)
|
||||||
eTaskGetState(lorasendTask));
|
ESP_LOGD(TAG, "Lorasendtask %d bytes left | Taskstate = %d",
|
||||||
|
uxTaskGetStackHighWaterMark(lorasendTask),
|
||||||
|
eTaskGetState(lorasendTask));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if (HAS_GPS)
|
#if (HAS_GPS)
|
||||||
ESP_LOGD(TAG, "Gpsloop %d bytes left | Taskstate = %d",
|
if (GpsTask != NULL)
|
||||||
uxTaskGetStackHighWaterMark(GpsTask), eTaskGetState(GpsTask));
|
ESP_LOGD(TAG, "Gpsloop %d bytes left | Taskstate = %d",
|
||||||
|
uxTaskGetStackHighWaterMark(GpsTask), eTaskGetState(GpsTask));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef HAS_SPI
|
#ifdef HAS_SPI
|
||||||
ESP_LOGD(TAG, "spiloop %d bytes left | Taskstate = %d",
|
if (spiTask != NULL)
|
||||||
uxTaskGetStackHighWaterMark(spiTask), eTaskGetState(spiTask));
|
ESP_LOGD(TAG, "spiloop %d bytes left | Taskstate = %d",
|
||||||
|
uxTaskGetStackHighWaterMark(spiTask), eTaskGetState(spiTask));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef HAS_MQTT
|
#ifdef HAS_MQTT
|
||||||
ESP_LOGD(TAG, "MQTTloop %d bytes left | Taskstate = %d",
|
if (mqttTask != NULL)
|
||||||
uxTaskGetStackHighWaterMark(mqttTask), eTaskGetState(mqttTask));
|
ESP_LOGD(TAG, "MQTTloop %d bytes left | Taskstate = %d",
|
||||||
|
uxTaskGetStackHighWaterMark(mqttTask), eTaskGetState(mqttTask));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if (defined HAS_DCF77 || defined HAS_IF482)
|
#if (defined HAS_DCF77 || defined HAS_IF482)
|
||||||
ESP_LOGD(TAG, "Clockloop %d bytes left | Taskstate = %d",
|
if (ClockTask != NULL)
|
||||||
uxTaskGetStackHighWaterMark(ClockTask), eTaskGetState(ClockTask));
|
ESP_LOGD(TAG, "Clockloop %d bytes left | Taskstate = %d",
|
||||||
|
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)
|
||||||
ESP_LOGD(TAG, "LEDloop %d bytes left | Taskstate = %d",
|
if (ledLoopTask != NULL)
|
||||||
uxTaskGetStackHighWaterMark(ledLoopTask),
|
ESP_LOGD(TAG, "LEDloop %d bytes left | Taskstate = %d",
|
||||||
eTaskGetState(ledLoopTask));
|
uxTaskGetStackHighWaterMark(ledLoopTask),
|
||||||
|
eTaskGetState(ledLoopTask));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// read battery voltage into global variable
|
// read battery voltage into global variable
|
||||||
@ -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
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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,32 +261,22 @@ 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)
|
dp_printf("BLTH:off");
|
||||||
if (cfg.enscount)
|
|
||||||
dp_printf("(CWA:%d)", cwa_report());
|
|
||||||
else
|
|
||||||
#endif
|
|
||||||
dp_printf("BLTH:off");
|
|
||||||
#else
|
#else
|
||||||
dp_printf("Sniffer disabled");
|
dp_printf("Sniffer disabled");
|
||||||
#endif
|
#endif
|
||||||
@ -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++;
|
||||||
@ -435,10 +427,12 @@ void dp_drawPage(bool nextpage) {
|
|||||||
#ifdef HAS_BME680
|
#ifdef HAS_BME680
|
||||||
// line 6-7: IAQ
|
// line 6-7: IAQ
|
||||||
dp_printf("IAQ:%-3.0f", bme_status.iaq);
|
dp_printf("IAQ:%-3.0f", bme_status.iaq);
|
||||||
#else // is BME280 or BMP180
|
#else // is BME280 or BMP180
|
||||||
// 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;
|
||||||
|
|
||||||
|
265
src/gpsread.cpp
265
src/gpsread.cpp
@ -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
|
||||||
|
|
||||||
|
// feed GPS decoder with serial NMEA data from GPS device
|
||||||
while (1) {
|
while (1) {
|
||||||
|
while (cfg.payloadmask & GPS_DATA) {
|
||||||
|
|
||||||
if (cfg.payloadmask & GPS_DATA) {
|
while (GPS_Serial.available())
|
||||||
#ifdef GPS_SERIAL
|
|
||||||
// feed GPS decoder with serial NMEA data from GPS device
|
|
||||||
while (GPS_Serial.available()) {
|
|
||||||
gps.encode(GPS_Serial.read());
|
gps.encode(GPS_Serial.read());
|
||||||
yield();
|
|
||||||
}
|
|
||||||
#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
|
delay(5);
|
||||||
// GPS time, we trigger a device time update to poll time from GPS
|
}
|
||||||
if ((timeSource == _unsynced || timeSource == _set) &&
|
delay(1000);
|
||||||
(gpstime.isUpdated() && gpstime.isValid() && gpstime.age() < 1000)) {
|
} // infinite while loop
|
||||||
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()
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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()
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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();
|
||||||
}
|
}
|
@ -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
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
94
src/main.cpp
94
src/main.cpp
@ -25,62 +25,54 @@ licenses. Refer to LICENSE.txt file in repository for more details.
|
|||||||
|
|
||||||
// Tasks and timers:
|
// Tasks and timers:
|
||||||
|
|
||||||
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
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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 > |
|
||||||
|
@ -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
|
||||||
|
@ -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--) {
|
||||||
|
@ -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
|
||||||
|
114
src/sdcard.cpp
114
src/sdcard.cpp
@ -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,71 +71,85 @@ 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);
|
||||||
#elif HAS_SDCARD == 2
|
#elif HAS_SDCARD == 2
|
||||||
bool fileExists = SD_MMC.exists(bufferFilename);
|
bool fileExists = SD_MMC.exists(bufferFilename);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (!fileExists) {
|
// file not exists, create it
|
||||||
// ESP_LOGD(TAG, "SD: file does not exist: opening");
|
if (!fileExists) {
|
||||||
|
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);
|
||||||
#elif HAS_SDCARD == 2
|
#elif HAS_SDCARD == 2
|
||||||
fileSDCard = SD_MMC.open(bufferFilename, FILE_WRITE);
|
fileSDCard = SD_MMC.open(bufferFilename, FILE_WRITE);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
|
@ -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:
|
||||||
|
|
||||||
|
@ -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, ¤t_time, txDelay) == pdTRUE) {
|
if (xTaskNotifyWait(0x00, ULONG_MAX, ¤t_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);
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user