Merge pull request #296 from cyberman54/development

Development
This commit is contained in:
Verkehrsrot 2019-03-06 23:43:59 +01:00 committed by GitHub
commit 667d7210e7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 215 additions and 168 deletions

4
.gitignore vendored
View File

@ -10,4 +10,6 @@
.clang_complete .clang_complete
.gcc-flags.json .gcc-flags.json
src/loraconf.h src/loraconf.h
src/ota.conf src/ota.conf
src/DBtimesync.cpp
include/DBtimesync.h

View File

@ -11,6 +11,9 @@
#include "bme680mems.h" #include "bme680mems.h"
#endif #endif
extern Ticker housekeeper;
void housekeeping(void);
void doHousekeeping(void); void doHousekeeping(void);
uint64_t uptime(void); uint64_t uptime(void);
void reset_counters(void); void reset_counters(void);

View File

@ -8,6 +8,7 @@
#include <Time.h> #include <Time.h>
#include <Timezone.h> #include <Timezone.h>
#include <RtcDateTime.h> #include <RtcDateTime.h>
#include <Ticker.h>
// std::set for unified array functions // std::set for unified array functions
#include <set> #include <set>
@ -110,7 +111,7 @@ extern uint16_t volatile macs_total, macs_wifi, macs_ble,
batt_voltage; // display values batt_voltage; // display values
extern bool volatile TimePulseTick; // 1sec pps flag set by GPS or RTC extern bool volatile TimePulseTick; // 1sec pps flag set by GPS or RTC
extern timesource_t timeSource; extern timesource_t timeSource;
extern hw_timer_t *sendCycle, *displaytimer, *clockCycle; extern hw_timer_t *displayIRQ, *ppsIRQ;
extern SemaphoreHandle_t I2Caccess, TimePulse; extern SemaphoreHandle_t I2Caccess, TimePulse;
extern TaskHandle_t irqHandlerTask, ClockTask; extern TaskHandle_t irqHandlerTask, ClockTask;
extern TimerHandle_t WifiChanTimer; extern TimerHandle_t WifiChanTimer;

View File

@ -16,7 +16,6 @@ extern TinyGPSPlus gps; // Make TinyGPS++ instance globally availabe
extern gpsStatus_t extern gpsStatus_t
gps_status; // Make struct for storing gps data globally available gps_status; // Make struct for storing gps data globally available
extern TaskHandle_t GpsTask; extern TaskHandle_t GpsTask;
extern TickType_t const gpsDelay_ticks; // time to NMEA arrival
int gps_init(void); int gps_init(void);
void gps_read(void); void gps_read(void);

View File

@ -3,8 +3,9 @@
#define DISPLAY_IRQ 0x01 #define DISPLAY_IRQ 0x01
#define BUTTON_IRQ 0x02 #define BUTTON_IRQ 0x02
#define SENDCOUNTER_IRQ 0x04 #define SENDCYCLE_IRQ 0x04
#define CYCLIC_IRQ 0x08 #define CYCLIC_IRQ 0x08
#define TIMESYNC_IRQ 0x10
#include "globals.h" #include "globals.h"
#include "cyclic.h" #include "cyclic.h"
@ -12,8 +13,6 @@
#include "timekeeper.h" #include "timekeeper.h"
void irqHandler(void *pvParameters); void irqHandler(void *pvParameters);
void IRAM_ATTR homeCycleIRQ();
void IRAM_ATTR SendCycleIRQ();
#ifdef HAS_DISPLAY #ifdef HAS_DISPLAY
#include "display.h" #include "display.h"

View File

@ -4,6 +4,7 @@
#include "globals.h" #include "globals.h"
#include "rcommand.h" #include "rcommand.h"
#include "timekeeper.h" #include "timekeeper.h"
#include "DBtimesync.h"
// LMIC-Arduino LoRaWAN Stack // LMIC-Arduino LoRaWAN Stack
#include <lmic.h> #include <lmic.h>

View File

@ -8,6 +8,8 @@
#include "macsniff.h" #include "macsniff.h"
#include <rom/rtc.h> #include <rom/rtc.h>
#include "cyclic.h" #include "cyclic.h"
#include "timekeeper.h"
#include "DBtimesync.h"
// table of remote commands and assigned functions // table of remote commands and assigned functions
typedef struct { typedef struct {

View File

@ -5,9 +5,12 @@
#include "lorawan.h" #include "lorawan.h"
#include "cyclic.h" #include "cyclic.h"
extern Ticker sendcycler;
void SendPayload(uint8_t port, sendprio_t prio); void SendPayload(uint8_t port, sendprio_t prio);
void sendCounter(void); void sendCounter(void);
void checkSendQueues(void); void checkSendQueues(void);
void flushQueues(); void flushQueues();
void sendcycle(void);
#endif // _SENDDATA_H_ #endif // _SENDDATA_H_

View File

@ -4,6 +4,7 @@
#include "globals.h" #include "globals.h"
#include "rtctime.h" #include "rtctime.h"
#include "TimeLib.h" #include "TimeLib.h"
#include "irqhandler.h"
#ifdef HAS_GPS #ifdef HAS_GPS
#include "gpsread.h" #include "gpsread.h"
@ -15,11 +16,13 @@
#endif #endif
extern const char timeSetSymbols[]; extern const char timeSetSymbols[];
extern Ticker timesyncer;
void IRAM_ATTR CLOCKIRQ(void); void IRAM_ATTR CLOCKIRQ(void);
void clock_init(void); void clock_init(void);
void clock_loop(void *pvParameters); void clock_loop(void *pvParameters);
void timepulse_start(void); void timepulse_start(void);
void timeSync(void);
uint8_t timepulse_init(void); uint8_t timepulse_init(void);
time_t timeIsValid(time_t const t); time_t timeIsValid(time_t const t);
time_t timeProvider(void); time_t timeProvider(void);
@ -28,5 +31,7 @@ time_t tmConvert(uint16_t YYYY, uint8_t MM, uint8_t DD, uint8_t hh, uint8_t mm,
uint8_t ss); uint8_t ss);
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);
time_t TimeSyncAns(uint8_t seqNo, uint64_t unixTime);
void TimeSyncReq(uint8_t seqNo);
#endif // _timekeeper_H #endif // _timekeeper_H

View File

@ -260,10 +260,9 @@ time_t sysUnsyncedTime = 0; // the time sysTime unadjusted by sync
#endif #endif
#ifdef usePPS #ifdef usePPS
time_t SyncToPPS() { void SyncToPPS() {
sysTime++; sysTime++;
prevMicros = micros(); prevMicros = micros();
return sysTime;
} }
#endif #endif

View File

@ -154,7 +154,7 @@ time_t now(uint32_t &sysTimeMicros); // return the current time as seconds and
#endif #endif
#ifdef usePPS #ifdef usePPS
time_t SyncToPPS(); void SyncToPPS();
#endif #endif
void setTime(time_t t); void setTime(time_t t);
void setTime(int hr, int min, int sec, int day, int month, int yr); void setTime(int hr, int min, int sec, int day, int month, int yr);

View File

@ -6,7 +6,7 @@
; ---> SELECT TARGET PLATFORM HERE! <--- ; ---> SELECT TARGET PLATFORM HERE! <---
[platformio] [platformio]
env_default = generic ;env_default = generic
;env_default = ebox ;env_default = ebox
;env_default = eboxtube ;env_default = eboxtube
;env_default = heltec ;env_default = heltec
@ -15,7 +15,7 @@ env_default = generic
;env_default = ttgov2 ;env_default = ttgov2
;env_default = ttgov21old ;env_default = ttgov21old
;env_default = ttgov21new ;env_default = ttgov21new
;env_default = ttgobeam env_default = ttgobeam
;env_default = ttgofox ;env_default = ttgofox
;env_default = lopy ;env_default = lopy
;env_default = lopy4 ;env_default = lopy4
@ -33,7 +33,7 @@ description = Paxcounter is a proof-of-concept ESP32 device for metering passeng
release_version = 1.7.324 release_version = 1.7.324
; 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 = 0 debug_level = 4
; UPLOAD MODE: select esptool to flash via USB/UART, select custom to upload to cloud for OTA ; UPLOAD MODE: select esptool to flash via USB/UART, select custom to upload to cloud for OTA
upload_protocol = esptool upload_protocol = esptool
;upload_protocol = custom ;upload_protocol = custom

View File

@ -19,7 +19,7 @@ void defaultConfig() {
cfg.screenon = 1; // 0=disabled, 1=enabled cfg.screenon = 1; // 0=disabled, 1=enabled
cfg.countermode = 0; // 0=cyclic, 1=cumulative, 2=cyclic confirmed cfg.countermode = 0; // 0=cyclic, 1=cumulative, 2=cyclic confirmed
cfg.rssilimit = 0; // threshold for rssilimiter, negative value! cfg.rssilimit = 0; // threshold for rssilimiter, negative value!
cfg.sendcycle = SEND_SECS; // payload send cycle [seconds/2] cfg.sendcycle = SENDCYCLE; // payload send cycle [seconds/2]
cfg.wifichancycle = cfg.wifichancycle =
WIFI_CHANNEL_SWITCH_INTERVAL; // wifi channel switch cycle [seconds/100] WIFI_CHANNEL_SWITCH_INTERVAL; // wifi channel switch cycle [seconds/100]
cfg.blescantime = cfg.blescantime =

View File

@ -7,6 +7,10 @@
// Local logging tag // Local logging tag
static const char TAG[] = __FILE__; static const char TAG[] = __FILE__;
Ticker housekeeper;
void housekeeping() { xTaskNotify(irqHandlerTask, CYCLIC_IRQ, eSetBits); }
// do all housekeeping // do all housekeeping
void doHousekeeping() { void doHousekeeping() {

View File

@ -11,10 +11,6 @@ TaskHandle_t GpsTask;
#ifdef GPS_SERIAL #ifdef GPS_SERIAL
HardwareSerial GPS_Serial(1); // use UART #1 HardwareSerial GPS_Serial(1); // use UART #1
TickType_t const gpsDelay_ticks = pdMS_TO_TICKS(1000 - NMEA_BUFFERTIME) -
tx_Ticks(NMEA_FRAME_SIZE, GPS_SERIAL);
#else
TickType_t const gpsDelay_ticks = pdMS_TO_TICKS(1000 - NMEA_BUFFERTIME);
#endif #endif
// initialize and configure GPS // initialize and configure GPS
@ -27,13 +23,6 @@ int gps_init(void) {
return 0; return 0;
} }
// set timeout for reading recent time from GPS
#ifdef GPS_SERIAL // serial GPS
#else // I2C GPS
#endif
#if defined GPS_SERIAL #if defined GPS_SERIAL
GPS_Serial.begin(GPS_SERIAL); GPS_Serial.begin(GPS_SERIAL);
ESP_LOGI(TAG, "Using serial GPS"); ESP_LOGI(TAG, "Using serial GPS");
@ -88,18 +77,23 @@ void gps_read() {
time_t get_gpstime(void) { time_t get_gpstime(void) {
// set time to wait for arrive next recent NMEA time record // set time to wait for arrive next recent NMEA time record
static const uint32_t gpsDelay_ms = gpsDelay_ticks / portTICK_PERIOD_MS; static const uint32_t gpsDelay_ms = 500;
time_t t = 0; time_t t = 0;
if ((gps.time.age() < gpsDelay_ms) && (gps.time.isValid()) && (gps.date.isValid())) { if ((gps.time.age() < gpsDelay_ms) && gps.time.isValid() &&
gps.date.isValid() && gps.time.isUpdated()) {
ESP_LOGD(TAG, "GPS time age: %dms, second: %d, is valid: %s", gps.time.age(), gps.time.second(), gps.time.value(); // trigger isUpdated()
gps.time.isValid() ? "yes" : "no");
t = tmConvert(gps.date.year(), gps.date.month(), gps.date.day(), ESP_LOGD(TAG, "GPS time age: %dms, is valid: %s, second: %d",
gps.time.hour(), gps.time.minute(), gps.time.second()); gps.time.age(),
} (gps.time.isValid() && gps.date.isValid()) ? "yes" : "no",
gps.time.second());
t = tmConvert(gps.date.year(), gps.date.month(), gps.date.day(),
gps.time.hour(), gps.time.minute(), gps.time.second());
}
return timeIsValid(t); return timeIsValid(t);
} // get_gpstime() } // get_gpstime()

View File

@ -5,19 +5,21 @@
#include <stdint.h> #include <stdint.h>
//#define HAS_BME 0x77 // BME680 sensor on I2C bus (SDI=21/SCL=22); comment out // Hardware related definitions for Heltec V2 LoRa-32 Board
//if not present
//#define HAS_BME GPIO_NUM_21, GPIO_NUM_22 // SDA, SCL
//#define BME_ADDR BME680_I2C_ADDR_PRIMARY // connect SDIO of BME680 to GND
// Hardware related definitions for Heltec LoRa-32 Board
#define HAS_LORA 1 // comment out if device shall not send data via LoRa #define HAS_LORA 1 // comment out if device shall not send data via LoRa
#define CFG_sx1276_radio 1 #define CFG_sx1276_radio 1
#define HAS_DISPLAY U8X8_SSD1306_128X64_NONAME_HW_I2C // OLED-Display on board #define HAS_DISPLAY U8X8_SSD1306_128X64_NONAME_HW_I2C // OLED-Display on board
#define HAS_LED LED_BUILTIN // white LED on board #define HAS_LED LED_BUILTIN // white LED on board
#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
#define MY_OLED_SDA (4) #define MY_OLED_SDA (21)
#define MY_OLED_SCL (15) #define MY_OLED_SCL (22)
#define MY_OLED_RST (16) #define MY_OLED_RST (16)
// Pins for LORA chip SPI interface come from board file, we need some // Pins for LORA chip SPI interface come from board file, we need some

View File

@ -7,16 +7,8 @@
// Hardware related definitions for Heltec V2 LoRa-32 Board // Hardware related definitions for Heltec V2 LoRa-32 Board
// BME680 sensor on I2C bus (SDI=21/SCL=22); comment out if not present //#define HAS_BME GPIO_NUM_4, GPIO_NUM_15 // SDA, SCL
//#define HAS_BME GPIO_NUM_21, GPIO_NUM_22 // SDA, SCL //#define BME_ADDR BME680_I2C_ADDR_PRIMARY // connect SDIO of BME680 to GND
//#define BME_ADDR BME680_I2C_ADDR_PRIMARY // connect SDIO of BME680 to GND
// Of cause, by default the board has no BME680 mounted
// a BME680 sensor board maybe connected to I2C (SDA = 4 , and SLC = 15)
// second it worked if SDIO left unconnected and 0x77 as address was used
//#define HAS_BME 4, 15 // SDA, SCL
//#define BME_ADDR BME680_I2C_ADDR_SECONDARY // leave SDIO of BME680 unconnected
#define HAS_LORA 1 // comment out if device shall not send data via LoRa #define HAS_LORA 1 // comment out if device shall not send data via LoRa
#define CFG_sx1276_radio 1 #define CFG_sx1276_radio 1
@ -26,8 +18,8 @@
#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
#define MY_OLED_SDA (4) // original = 21 #define MY_OLED_SDA (4)
#define MY_OLED_SCL (15) // original = 22 #define MY_OLED_SCL (15)
#define MY_OLED_RST (16) #define MY_OLED_RST (16)
// Pins for LORA chip SPI interface come from board file, we need some // Pins for LORA chip SPI interface come from board file, we need some

View File

@ -23,7 +23,7 @@
// GPS settings // GPS settings
#define HAS_GPS 1 // use on board GPS #define HAS_GPS 1 // use on board GPS
#define GPS_SERIAL 9600, SERIAL_8N1, GPIO_NUM_12, GPIO_NUM_15 // UBlox NEO 6M #define GPS_SERIAL 9600, SERIAL_8N1, GPIO_NUM_12, GPIO_NUM_15 // UBlox NEO 6M
//#define GPS_INT GPIO_NUM_34 // 30ns accurary timepulse, to be external wired on pcb: NEO 6M Pin#3 -> GPIO34 #define GPS_INT GPIO_NUM_34 // 30ns accurary timepulse, to be external wired on pcb: NEO 6M Pin#3 -> GPIO34
// Settings for on board DS3231 RTC chip // Settings for on board DS3231 RTC chip
//#define HAS_RTC MY_OLED_SDA, MY_OLED_SCL // SDA, SCL //#define HAS_RTC MY_OLED_SDA, MY_OLED_SCL // SDA, SCL

View File

@ -25,16 +25,16 @@
#define RTC_INT GPIO_NUM_34 // timepulse with accuracy +/- 2*e-6 [microseconds] = 0,1728sec / day #define RTC_INT GPIO_NUM_34 // timepulse with accuracy +/- 2*e-6 [microseconds] = 0,1728sec / day
// Settings for IF482 interface // Settings for IF482 interface
//#define HAS_IF482 9600, SERIAL_7E1, GPIO_NUM_12, GPIO_NUM_14 // IF482 serial port parameters #define HAS_IF482 9600, SERIAL_7E1, GPIO_NUM_12, GPIO_NUM_14 // IF482 serial port parameters
// Settings for DCF77 interface // Settings for DCF77 interface
//#define HAS_DCF77 GPIO_NUM_14 //#define HAS_DCF77 GPIO_NUM_14
//#define DCF77_ACTIVE_LOW 1 //#define DCF77_ACTIVE_LOW 1
// Settings for external GPS chip // Settings for external GPS chip
//#define HAS_GPS 1 // use on board GPS #define HAS_GPS 1 // use on board GPS
//#define GPS_SERIAL 9600, SERIAL_8N1, GPIO_NUM_17, GPIO_NUM_16 // UBlox NEO 6M or 7M with default configuration #define GPS_SERIAL 9600, SERIAL_8N1, GPIO_NUM_17, GPIO_NUM_16 // UBlox NEO 6M or 7M with default configuration
//#define GPS_INT GPIO_NUM_13 #define GPS_INT GPIO_NUM_13
// Pins for LORA chip SPI interface, reset line and interrupt lines // Pins for LORA chip SPI interface, reset line and interrupt lines
#define LORA_SCK (5) #define LORA_SCK (5)

View File

@ -72,7 +72,7 @@ U: UTC time (not supported by all systems),
L: Local Time L: Local Time
***) Day of week: ***) Day of week:
not evaluated by model BU-190 not evaluated by model BU-190, use "F" instead for this model
*/ */
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
@ -92,16 +92,14 @@ void IF482_Pulse(time_t t) {
static const TickType_t txDelay = static const TickType_t txDelay =
pdMS_TO_TICKS(IF482_PULSE_LENGTH - tx_Ticks(IF482_FRAME_SIZE, HAS_IF482)); pdMS_TO_TICKS(IF482_PULSE_LENGTH - tx_Ticks(IF482_FRAME_SIZE, HAS_IF482));
//TickType_t startTime = xTaskGetTickCount(); vTaskDelay(txDelay); // wait until moment to fire
//vTaskDelayUntil(&startTime, txDelay); // wait until moment to fire
vTaskDelay(txDelay); // wait until moment to fire
IF482.print(IF482_Frame(t + 1)); // note: if482 telegram for *next* second IF482.print(IF482_Frame(t + 1)); // note: if482 telegram for *next* second
} }
String IRAM_ATTR IF482_Frame(time_t startTime) { String IRAM_ATTR IF482_Frame(time_t startTime) {
time_t t = myTZ.toLocal(startTime); time_t t = myTZ.toLocal(startTime);
char mon, out[IF482_FRAME_SIZE]; char mon, out[IF482_FRAME_SIZE + 1];
switch (timeStatus()) { // indicates if time has been set and recently synced switch (timeStatus()) { // indicates if time has been set and recently synced
case timeSet: // time is set and is synced case timeSet: // time is set and is synced

View File

@ -30,12 +30,17 @@ void irqHandler(void *pvParameters) {
#endif #endif
// are cyclic tasks due? // are cyclic tasks due?
if (InterruptStatus & CYCLIC_IRQ) { if (InterruptStatus & CYCLIC_IRQ)
doHousekeeping(); doHousekeeping();
}
#ifdef TIME_SYNC_INTERVAL
// is time to be synced?
if (InterruptStatus & TIMESYNC_IRQ)
setTime(timeProvider());
#endif
// is time to send the payload? // is time to send the payload?
if (InterruptStatus & SENDCOUNTER_IRQ) if (InterruptStatus & SENDCYCLE_IRQ)
sendCounter(); sendCounter();
} }
vTaskDelete(NULL); // shoud never be reached vTaskDelete(NULL); // shoud never be reached
@ -44,26 +49,28 @@ void irqHandler(void *pvParameters) {
// esp32 hardware timer triggered interrupt service routines // esp32 hardware timer triggered interrupt service routines
// they notify the irq handler task // they notify the irq handler task
void IRAM_ATTR homeCycleIRQ() {
xTaskNotifyFromISR(irqHandlerTask, CYCLIC_IRQ, eSetBits, NULL);
portYIELD_FROM_ISR();
}
void IRAM_ATTR SendCycleIRQ() {
xTaskNotifyFromISR(irqHandlerTask, SENDCOUNTER_IRQ, eSetBits, NULL);
portYIELD_FROM_ISR();
}
#ifdef HAS_DISPLAY #ifdef HAS_DISPLAY
void IRAM_ATTR DisplayIRQ() { void IRAM_ATTR DisplayIRQ() {
xTaskNotifyFromISR(irqHandlerTask, DISPLAY_IRQ, eSetBits, NULL); BaseType_t xHigherPriorityTaskWoken;
portYIELD_FROM_ISR(); xHigherPriorityTaskWoken = pdFALSE;
xTaskNotifyFromISR(irqHandlerTask, DISPLAY_IRQ, eSetBits,
&xHigherPriorityTaskWoken);
if (xHigherPriorityTaskWoken)
portYIELD_FROM_ISR();
} }
#endif #endif
#ifdef HAS_BUTTON #ifdef HAS_BUTTON
void IRAM_ATTR ButtonIRQ() { void IRAM_ATTR ButtonIRQ() {
xTaskNotifyFromISR(irqHandlerTask, BUTTON_IRQ, eSetBits, NULL); BaseType_t xHigherPriorityTaskWoken;
portYIELD_FROM_ISR(); xHigherPriorityTaskWoken = pdFALSE;
xTaskNotifyFromISR(irqHandlerTask, BUTTON_IRQ, eSetBits,
&xHigherPriorityTaskWoken);
if (xHigherPriorityTaskWoken)
portYIELD_FROM_ISR();
} }
#endif #endif

View File

@ -224,6 +224,12 @@ void onEvent(ev_t ev) {
break; break;
case EV_TXCOMPLETE: case EV_TXCOMPLETE:
#ifdef DBTIMESYNC
if (!(LMIC.txrxFlags & TXRX_ACK) && time_sync_seqNo)
time_sync_messages[time_sync_seqNo - 1] = LMIC.txend;
#endif
strcpy_P(buff, (LMIC.txrxFlags & TXRX_ACK) ? PSTR("RECEIVED_ACK") strcpy_P(buff, (LMIC.txrxFlags & TXRX_ACK) ? PSTR("RECEIVED_ACK")
: PSTR("TX_COMPLETE")); : PSTR("TX_COMPLETE"));
sprintf(display_line6, " "); // clear previous lmic status sprintf(display_line6, " "); // clear previous lmic status
@ -465,9 +471,9 @@ void user_request_network_time_callback(void *pVoidUserUTCTime,
// Update system time with time read from the network // Update system time with time read from the network
if (timeIsValid(*pUserUTCTime)) { if (timeIsValid(*pUserUTCTime)) {
xSemaphoreTake(TimePulse, pdMS_TO_TICKS(1000)); // wait for pps setTime(*pUserUTCTime);
setTime(*pUserUTCTime + 1);
timeSource = _lora; timeSource = _lora;
timesyncer.attach(TIME_SYNC_INTERVAL * 60, timeSync); // regular repeat
ESP_LOGI(TAG, "Received recent time from LoRa"); ESP_LOGI(TAG, "Received recent time from LoRa");
} else } else
ESP_LOGI(TAG, "Invalid time received from LoRa"); ESP_LOGI(TAG, "Invalid time received from LoRa");

View File

@ -23,19 +23,21 @@ licenses. Refer to LICENSE.txt file in repository for more details.
//////////////////////// ESP32-Paxcounter \\\\\\\\\\\\\\\\\\\\\\\\\\ //////////////////////// ESP32-Paxcounter \\\\\\\\\\\\\\\\\\\\\\\\\\
Uused tasks and timers: // Tasks and timers:
Task Core Prio Purpose Task Core Prio Purpose
==================================================================================== -------------------------------------------------------------------------------
ledloop 0 3 blinks LEDs ledloop 0 3 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 IDLE 0 0 ESP32 arduino scheduler -> runs wifi sniffer
clockloop 1 4 generates realtime telegrams for external clock clockloop 1 4 generates realtime telegrams for external clock
looptask 1 1 arduino core -> runs the LMIC LoRa stack looptask 1 1 arduino core -> runs the LMIC LoRa stack
irqhandler 1 1 executes tasks triggered by hw irq, see table below irqhandler 1 1 executes tasks triggered by timer irq
gpsloop 1 2 reads data from GPS via serial or i2c gpsloop 1 2 reads data from GPS via serial or i2c
bmeloop 1 1 reads data from BME sensor via i2c bmeloop 1 1 reads data from BME sensor via i2c
timesync_ans 1 0 temporary task for receiving time sync requests
timesync_req 1 0 temporary task for sending time sync requests
IDLE 1 0 ESP32 arduino scheduler -> runs wifi channel rotator IDLE 1 0 ESP32 arduino scheduler -> runs wifi channel rotator
Low priority numbers denote low priority tasks. Low priority numbers denote low priority tasks.
@ -43,16 +45,29 @@ Low priority numbers denote low priority tasks.
Tasks using i2c bus all must have same priority, because using mutex semaphore Tasks using i2c bus all must have same priority, because using mutex semaphore
(irqhandler, bmeloop) (irqhandler, bmeloop)
ESP32 hardware irq timers // ESP32 hardware timers
================================ -------------------------------------------------------------------------------
0 triggers display refresh 0 displayIRQ -> display refresh -> 40ms (DISPLAYREFRESH_MS in
1 triggers DCF77 clock signal paxcounter.conf) 1 ppsIRQ -> pps clock irq -> 1sec 2 unused 3 unused
2 triggers send payload cycle
3 triggers housekeeping cycle
RTC hardware timer (if present)
================================ // Interrupt routines
triggers pps 1 sec impulse -------------------------------------------------------------------------------
fired by hardware
DisplayIRQ -> esp32 timer 0 -> irqhandler.cpp
CLOCKIRQ -> esp32 timer 1 -> timekeeper.cpp
ButtonIRQ -> external gpio -> irqhandler.cpp
fired by software (Ticker.h)
TIMESYNC_IRQ -> timeSync() -> timerkeeper.cpp
CYLCIC_IRQ -> housekeeping() -> cyclic.cpp
SENDCYCLE_IRQ -> sendcycle() -> senddata.cpp
// External RTC timer (if present)
-------------------------------------------------------------------------------
triggers pps 1 sec impulse
*/ */
@ -65,8 +80,7 @@ uint8_t volatile channel = 0; // channel rotation counter
uint16_t volatile macs_total = 0, macs_wifi = 0, macs_ble = 0, uint16_t volatile macs_total = 0, macs_wifi = 0, macs_ble = 0,
batt_voltage = 0; // globals for display batt_voltage = 0; // globals for display
hw_timer_t *sendCycle = NULL, *homeCycle = NULL, *clockCycle = NULL, hw_timer_t *ppsIRQ = NULL, *displayIRQ = NULL;
*displaytimer = NULL;
TaskHandle_t irqHandlerTask, ClockTask; TaskHandle_t irqHandlerTask, ClockTask;
SemaphoreHandle_t I2Caccess, TimePulse; SemaphoreHandle_t I2Caccess, TimePulse;
@ -302,27 +316,8 @@ void setup() {
strcat_P(features, " OLED"); strcat_P(features, " OLED");
DisplayState = cfg.screenon; DisplayState = cfg.screenon;
init_display(PRODUCTNAME, PROGVERSION); // note: blocking call init_display(PRODUCTNAME, PROGVERSION); // note: blocking call
// setup display refresh trigger IRQ using esp32 hardware timer
// https://techtutorialsx.com/2017/10/07/esp32-arduino-timer-interrupts/
// prescaler 80 -> divides 80 MHz CPU freq to 1 MHz, timer 0, count up
displaytimer = timerBegin(0, 80, true);
// interrupt handler DisplayIRQ, triggered by edge
timerAttachInterrupt(displaytimer, &DisplayIRQ, true);
// reload interrupt after each trigger of display refresh cycle
timerAlarmWrite(displaytimer, DISPLAYREFRESH_MS * 1000, true);
#endif #endif
// setup send cycle trigger IRQ using esp32 hardware timer 2
sendCycle = timerBegin(2, 8000, true);
timerAttachInterrupt(sendCycle, &SendCycleIRQ, true);
timerAlarmWrite(sendCycle, cfg.sendcycle * 2 * 10000, true);
// setup house keeping cycle trigger IRQ using esp32 hardware timer 3
homeCycle = timerBegin(3, 8000, true);
timerAttachInterrupt(homeCycle, &homeCycleIRQ, true);
timerAlarmWrite(homeCycle, HOMECYCLE * 10000, true);
// show payload encoder // show payload encoder
#if PAYLOAD_ENCODER == 1 #if PAYLOAD_ENCODER == 1
strcat_P(features, " PLAIN"); strcat_P(features, " PLAIN");
@ -358,19 +353,6 @@ void setup() {
#endif #endif
#endif #endif
#if defined HAS_IF482 || defined HAS_DCF77
// start pps timepulse
ESP_LOGI(TAG, "Starting Timekeeper...");
assert(timepulse_init()); // setup timepulse
timepulse_start();
#endif
#ifdef TIME_SYNC_INTERVAL
// set time source and sync time
setSyncInterval(TIME_SYNC_INTERVAL * 60);
setSyncProvider(&timeProvider);
#endif
// start wifi in monitor mode and start channel rotation timer // start wifi in monitor mode and start channel rotation timer
ESP_LOGI(TAG, "Starting Wifi..."); ESP_LOGI(TAG, "Starting Wifi...");
wifi_sniffer_init(); wifi_sniffer_init();
@ -404,16 +386,25 @@ void setup() {
} }
#endif #endif
// starting timers and interrupts
assert(irqHandlerTask != NULL); // has interrupt handler task started? assert(irqHandlerTask != NULL); // has interrupt handler task started?
// start timer triggered interrupts ESP_LOGI(TAG, "Starting Timers...");
ESP_LOGI(TAG, "Starting Interrupts...");
#ifdef HAS_DISPLAY
timerAlarmEnable(displaytimer);
#endif
timerAlarmEnable(sendCycle);
timerAlarmEnable(homeCycle);
// start button interrupt // display interrupt
#ifdef HAS_DISPLAY
// https://techtutorialsx.com/2017/10/07/esp32-arduino-timer-interrupts/
// prescaler 80 -> divides 80 MHz CPU freq to 1 MHz, timer 0, count up
displayIRQ = timerBegin(0, 80, true);
timerAttachInterrupt(displayIRQ, &DisplayIRQ, true);
timerAlarmWrite(displayIRQ, DISPLAYREFRESH_MS * 1000, true);
timerAlarmEnable(displayIRQ);
#endif
// cyclic function interrupts
sendcycler.attach(SENDCYCLE * 2, sendcycle);
housekeeper.attach(HOMECYCLE, housekeeping);
// button interrupt
#ifdef HAS_BUTTON #ifdef HAS_BUTTON
#ifdef BUTTON_PULLUP #ifdef BUTTON_PULLUP
attachInterrupt(digitalPinToInterrupt(HAS_BUTTON), ButtonIRQ, RISING); attachInterrupt(digitalPinToInterrupt(HAS_BUTTON), ButtonIRQ, RISING);
@ -422,7 +413,19 @@ void setup() {
#endif #endif
#endif // HAS_BUTTON #endif // HAS_BUTTON
#ifdef TIME_SYNC_INTERVAL
// start pps timepulse
ESP_LOGI(TAG, "Starting Timekeeper...");
assert(timepulse_init()); // setup timepulse
timepulse_start();
timeSync(); // init systime
timesyncer.attach(TIME_SYNC_INTERVAL * 60, timeSync);
#endif
#if defined HAS_IF482 || defined HAS_DCF77 #if defined HAS_IF482 || defined HAS_DCF77
#ifndef TIME_SYNC_INTERVAL
#error for clock controller function TIME_SNYC_INTERVAL must be defined in paxcounter.conf
#endif
ESP_LOGI(TAG, "Starting Clock Controller..."); ESP_LOGI(TAG, "Starting Clock Controller...");
clock_init(); clock_init();
#endif #endif

View File

@ -10,7 +10,7 @@
#define VERBOSE 1 // comment out to silence the device, for mute use build option #define VERBOSE 1 // comment out to silence the device, for mute use build option
// Payload send cycle and encoding // Payload send cycle and encoding
#define SEND_SECS 30 // payload send cycle [seconds/2] -> 60 sec. #define SENDCYCLE 30 // payload send cycle [seconds/2], 0 .. 255
#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
// Set this to include BLE counting and vendor filter functions // Set this to include BLE counting and vendor filter functions
@ -66,8 +66,9 @@
#define RESPONSE_TIMEOUT_MS 60000 // firmware binary server connection timeout [milliseconds] #define RESPONSE_TIMEOUT_MS 60000 // firmware binary server connection timeout [milliseconds]
// settings for syncing time of node with external time source // settings for syncing time of node with external time source
//#define TIME_SYNC_INTERVAL 2 // sync time attempt each .. minutes from time source (GPS/LORA/RTC) [default = 60], comment out means off #define TIME_SYNC_INTERVAL 2 // sync time attempt each .. minutes from time source (GPS/LORA/RTC) [default = 60], comment out means off
//#define TIME_SYNC_LORA 1 // use LORA network as time source, comment out means off [default = off] //#define TIME_SYNC_LORA 1 // use LORA network as time source, comment out means off [default = off]
#define DBTIMESYNC 1 // use DB LORA timeserver with patented sync algorithm [default = off]
// time zone, see https://github.com/JChristensen/Timezone/blob/master/examples/WorldClock/WorldClock.ino // time zone, see https://github.com/JChristensen/Timezone/blob/master/examples/WorldClock/WorldClock.ino
#define DAYLIGHT_TIME {"CEST", Last, Sun, Mar, 2, 120} // Central European Summer Time #define DAYLIGHT_TIME {"CEST", Last, Sun, Mar, 2, 120} // Central European Summer Time

View File

@ -290,11 +290,12 @@ void PayloadConvert::writeBitmap(bool a, bool b, bool c, bool d, bool e, bool f,
} }
/* ---------------- Cayenne LPP 2.0 format ---------- */ /* ---------------- Cayenne LPP 2.0 format ---------- */
// see specs // see specs
// http://community.mydevices.com/t/cayenne-lpp-2-0/7510 (LPP 2.0) // http://community.mydevices.com/t/cayenne-lpp-2-0/7510 (LPP 2.0)
// https://github.com/myDevicesIoT/cayenne-docs/blob/master/docs/LORA.md (LPP 1.0) // https://github.com/myDevicesIoT/cayenne-docs/blob/master/docs/LORA.md
// PAYLOAD_ENCODER == 3 -> Dynamic Sensor Payload, using channels -> FPort 1 // (LPP 1.0) PAYLOAD_ENCODER == 3 -> Dynamic Sensor Payload, using channels ->
// PAYLOAD_ENCODER == 4 -> Packed Sensor Payload, not using channels -> FPort 2 // FPort 1 PAYLOAD_ENCODER == 4 -> Packed Sensor Payload, not using channels ->
// FPort 2
#elif (PAYLOAD_ENCODER == 3 || PAYLOAD_ENCODER == 4) #elif (PAYLOAD_ENCODER == 3 || PAYLOAD_ENCODER == 4)
@ -456,7 +457,7 @@ void PayloadConvert::addButton(uint8_t value) {
void PayloadConvert::addTime(time_t value) { void PayloadConvert::addTime(time_t value) {
#if (PAYLOAD_ENCODER == 4) #if (PAYLOAD_ENCODER == 4)
uint32_t t = (uint32_t)value; uint32_t t = (uint32_t)value;
uint32_t tx_period = (uint32_t)SEND_SECS * 2; uint32_t tx_period = (uint32_t)SENDCYCLE * 2;
buffer[cursor++] = 0x03; // set config mask to UTCTime + TXPeriod buffer[cursor++] = 0x03; // set config mask to UTCTime + TXPeriod
// UTCTime in seconds // UTCTime in seconds
buffer[cursor++] = (byte)((t & 0xFF000000) >> 24); buffer[cursor++] = (byte)((t & 0xFF000000) >> 24);

View File

@ -58,9 +58,8 @@ void set_rssi(uint8_t val[]) {
void set_sendcycle(uint8_t val[]) { void set_sendcycle(uint8_t val[]) {
cfg.sendcycle = val[0]; cfg.sendcycle = val[0];
// update send cycle interrupt // update send cycle interrupt [seconds
timerAlarmWrite(sendCycle, cfg.sendcycle * 2 * 10000, true); sendcycler.attach(cfg.sendcycle * 2, sendcycle);
// reload interrupt after each trigger of channel switch cycle
ESP_LOGI(TAG, "Remote command: set send cycle to %d seconds", ESP_LOGI(TAG, "Remote command: set send cycle to %d seconds",
cfg.sendcycle * 2); cfg.sendcycle * 2);
} }
@ -283,19 +282,35 @@ void get_time(uint8_t val[]) {
// format: opcode, function, #bytes params, // format: opcode, function, #bytes params,
// flag (true = do make settings persistent / false = don't) // flag (true = do make settings persistent / false = don't)
// //
cmd_t table[] = { cmd_t table[] = {{0x01, set_rssi, 1, true},
{0x01, set_rssi, 1, true}, {0x02, set_countmode, 1, true}, {0x02, set_countmode, 1, true},
{0x03, set_gps, 1, true}, {0x04, set_display, 1, true}, {0x03, set_gps, 1, true},
{0x05, set_lorasf, 1, true}, {0x06, set_lorapower, 1, true}, {0x04, set_display, 1, true},
{0x07, set_loraadr, 1, true}, {0x08, set_screensaver, 1, true}, {0x05, set_lorasf, 1, true},
{0x09, set_reset, 1, true}, {0x0a, set_sendcycle, 1, true}, {0x06, set_lorapower, 1, true},
{0x0b, set_wifichancycle, 1, true}, {0x0c, set_blescantime, 1, true}, {0x07, set_loraadr, 1, true},
{0x0d, set_vendorfilter, 1, false}, {0x0e, set_blescan, 1, true}, {0x08, set_screensaver, 1, true},
{0x0f, set_wifiant, 1, true}, {0x10, set_rgblum, 1, true}, {0x09, set_reset, 1, true},
{0x11, set_monitor, 1, true}, {0x12, set_beacon, 7, false}, {0x0a, set_sendcycle, 1, true},
{0x13, set_sensor, 2, true}, {0x80, get_config, 0, false}, {0x0b, set_wifichancycle, 1, true},
{0x81, get_status, 0, false}, {0x84, get_gps, 0, false}, {0x0c, set_blescantime, 1, true},
{0x85, get_bme, 0, false}, {0x86, get_time, 0, false}, {0x0d, set_vendorfilter, 1, false},
{0x0e, set_blescan, 1, true},
{0x0f, set_wifiant, 1, true},
{0x10, set_rgblum, 1, true},
{0x11, set_monitor, 1, true},
{0x12, set_beacon, 7, false},
{0x13, set_sensor, 2, true},
{0x80, get_config, 0, false},
{0x81, get_status, 0, false},
{0x84, get_gps, 0, false},
{0x85, get_bme, 0, false},
{0x86, get_time, 0, false}
#ifdef DBTIMESYNC
,
{TIME_ANS_OPCODE, recv_DBtime_ans, 0, false},
{TIME_SYNC_OPCODE, force_DBtime_sync, 0, false}
#endif
}; };
const uint8_t cmdtablesize = const uint8_t cmdtablesize =

View File

@ -1,6 +1,10 @@
// Basic Config // Basic Config
#include "senddata.h" #include "senddata.h"
Ticker sendcycler;
void sendcycle() { xTaskNotify(irqHandlerTask, SENDCYCLE_IRQ, eSetBits); }
// put data to send in RTos Queues used for transmit over channels Lora and SPI // put data to send in RTos Queues used for transmit over channels Lora and SPI
void SendPayload(uint8_t port, sendprio_t prio) { void SendPayload(uint8_t port, sendprio_t prio) {

View File

@ -6,12 +6,12 @@ static const char TAG[] = __FILE__;
// symbol to display current time source // symbol to display current time source
const char timeSetSymbols[] = {'G', 'R', 'L', '?'}; const char timeSetSymbols[] = {'G', 'R', 'L', '?'};
getExternalTime TimeSourcePtr; // pointer to time source function Ticker timesyncer;
void timeSync() { xTaskNotify(irqHandlerTask, TIMESYNC_IRQ, eSetBits); }
time_t timeProvider(void) { time_t timeProvider(void) {
ESP_LOGD(TAG, "time synched");
time_t t = 0; time_t t = 0;
#ifdef HAS_GPS #ifdef HAS_GPS
@ -21,6 +21,7 @@ time_t timeProvider(void) {
set_rtctime(t); // calibrate RTC set_rtctime(t); // calibrate RTC
#endif #endif
timeSource = _gps; timeSource = _gps;
timesyncer.attach(TIME_SYNC_INTERVAL * 60, timeSync); // regular repeat
return t; return t;
} }
#endif #endif
@ -30,16 +31,22 @@ time_t timeProvider(void) {
t = get_rtctime(); t = get_rtctime();
if (t) { if (t) {
timeSource = _rtc; timeSource = _rtc;
timesyncer.attach(60, timeSync); // short retry
} }
#endif #endif
// kick off asychron lora sync if we have // kick off asychronous DB timesync if we have
#if defined HAS_LORA && defined TIME_SYNC_LORA #ifdef DBTIMESYNC
send_DBtime_req();
// kick off asychronous lora sync if we have
#elif defined HAS_LORA && defined TIME_SYNC_LORA
LMIC_requestNetworkTime(user_request_network_time_callback, &userUTCTime); LMIC_requestNetworkTime(user_request_network_time_callback, &userUTCTime);
#endif #endif
if (!t) if (!t) {
timeSource = _unsynced; timeSource = _unsynced;
timesyncer.attach(60, timeSync); // short retry
}
return t; return t;
@ -78,8 +85,8 @@ uint8_t timepulse_init() {
#else #else
// use ESP32 hardware timer as time base with adjustable frequency // use ESP32 hardware timer as time base with adjustable frequency
clockCycle = 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(clockCycle, 10000, true); // 1000ms timerAlarmWrite(ppsIRQ, 10000, true); // 1000ms
ESP_LOGI(TAG, "Timepulse: internal (ESP32 hardware timer)"); ESP_LOGI(TAG, "Timepulse: internal (ESP32 hardware timer)");
return 1; // success return 1; // success
@ -92,8 +99,8 @@ void timepulse_start(void) {
#elif defined RTC_INT // start external clock rtc #elif defined RTC_INT // start external clock rtc
attachInterrupt(digitalPinToInterrupt(RTC_INT), CLOCKIRQ, FALLING); attachInterrupt(digitalPinToInterrupt(RTC_INT), CLOCKIRQ, FALLING);
#else // start internal clock esp32 hardware timer #else // start internal clock esp32 hardware timer
timerAttachInterrupt(clockCycle, &CLOCKIRQ, true); timerAttachInterrupt(ppsIRQ, &CLOCKIRQ, true);
timerAlarmEnable(clockCycle); timerAlarmEnable(ppsIRQ);
#endif #endif
} }
@ -101,12 +108,11 @@ void timepulse_start(void) {
void IRAM_ATTR CLOCKIRQ(void) { void IRAM_ATTR CLOCKIRQ(void) {
BaseType_t xHigherPriorityTaskWoken; BaseType_t xHigherPriorityTaskWoken;
SyncToPPS(); // calibrates UTC systime, see Time.h
time_t t = SyncToPPS(); // calibrates UTC systime, see Time.h
xHigherPriorityTaskWoken = pdFALSE; xHigherPriorityTaskWoken = pdFALSE;
if (ClockTask != NULL) if (ClockTask != NULL)
xTaskNotifyFromISR(ClockTask, uint32_t(t), eSetBits, xTaskNotifyFromISR(ClockTask, uint32_t(now()), eSetBits,
&xHigherPriorityTaskWoken); &xHigherPriorityTaskWoken);
#if defined GPS_INT || defined RTC_INT #if defined GPS_INT || defined RTC_INT
@ -214,7 +220,7 @@ void clock_loop(void *taskparameter) { // ClockTask
#if defined HAS_IF482 #if defined HAS_IF482
IF482_Pulse(t); IF482_Pulse(nextsec(t));
#elif defined HAS_DCF77 #elif defined HAS_DCF77