commit
a3286ada2b
@ -17,7 +17,6 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
void doHousekeeping(void);
|
void doHousekeeping(void);
|
||||||
void do_timesync(void);
|
|
||||||
uint64_t uptime(void);
|
uint64_t uptime(void);
|
||||||
void reset_counters(void);
|
void reset_counters(void);
|
||||||
int redirect_log(const char *fmt, va_list args);
|
int redirect_log(const char *fmt, va_list args);
|
||||||
|
@ -15,5 +15,6 @@ extern TaskHandle_t GpsTask;
|
|||||||
int gps_init(void);
|
int gps_init(void);
|
||||||
void gps_read(void);
|
void gps_read(void);
|
||||||
void gps_loop(void *pvParameters);
|
void gps_loop(void *pvParameters);
|
||||||
|
time_t get_gpstime(void);
|
||||||
|
|
||||||
#endif
|
#endif
|
@ -30,7 +30,7 @@ description = Paxcounter is a proof-of-concept ESP32 device for metering passeng
|
|||||||
|
|
||||||
[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 = 1.7.14
|
release_version = 1.7.141
|
||||||
; 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
|
||||||
|
@ -7,8 +7,8 @@
|
|||||||
// Local logging tag
|
// Local logging tag
|
||||||
static const char TAG[] = "main";
|
static const char TAG[] = "main";
|
||||||
|
|
||||||
uint32_t userUTCTime; // Seconds since the UTC epoch
|
time_t userUTCTime; // Seconds since the UTC epoch
|
||||||
unsigned long nextTimeSync = millis();
|
unsigned long nextLoraTimeSync = millis();
|
||||||
|
|
||||||
// do all housekeeping
|
// do all housekeeping
|
||||||
void doHousekeeping() {
|
void doHousekeeping() {
|
||||||
@ -23,12 +23,14 @@ void doHousekeeping() {
|
|||||||
spi_housekeeping();
|
spi_housekeeping();
|
||||||
lora_housekeeping();
|
lora_housekeeping();
|
||||||
|
|
||||||
// time sync once per TIME_SYNC_INTERVAL
|
// do cyclic time sync with LORA network
|
||||||
#ifdef TIME_SYNC_INTERVAL
|
#ifdef TIME_SYNC_INTERVAL_LORA
|
||||||
if (millis() >= nextTimeSync) {
|
if (millis() >= nextLoraTimeSync) {
|
||||||
nextTimeSync =
|
nextLoraTimeSync = millis() + TIME_SYNC_INTERVAL_LORA *
|
||||||
millis() + TIME_SYNC_INTERVAL * 60000; // set up next time sync period
|
60000; // set up next time sync period
|
||||||
do_timesync();
|
// Schedule a network time sync request at the next possible time
|
||||||
|
LMIC_requestNetworkTime(user_request_network_time_callback, &userUTCTime);
|
||||||
|
ESP_LOGI(TAG, "LORAWAN time request scheduled");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -120,37 +122,6 @@ void reset_counters() {
|
|||||||
macs_ble = 0;
|
macs_ble = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void do_timesync() {
|
|
||||||
#ifdef TIME_SYNC_INTERVAL
|
|
||||||
|
|
||||||
// set system time to time source GPS, if we have valid gps time
|
|
||||||
#ifdef HAS_GPS
|
|
||||||
if (gps.time.isValid()) {
|
|
||||||
setTime(gps.time.hour(), gps.time.minute(), gps.time.second(),
|
|
||||||
gps.date.day(), gps.date.month(), gps.date.year());
|
|
||||||
// set RTC time to time source GPS, if RTC is present
|
|
||||||
#ifdef HAS_RTC
|
|
||||||
if (!set_rtctime(RtcDateTime(now())))
|
|
||||||
ESP_LOGE(TAG, "RTC set time failure");
|
|
||||||
#endif
|
|
||||||
time_t tt = myTZ.toLocal(now());
|
|
||||||
ESP_LOGI(TAG, "GPS has set system time to %02d/%02d/%d %02d:%02d:%02d",
|
|
||||||
month(tt), day(tt), year(tt), hour(tt), minute(tt), second(tt));
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
ESP_LOGI(TAG, "No valid GPS time");
|
|
||||||
}
|
|
||||||
|
|
||||||
// set system time to time source LoRa Network, if network supports DevTimeReq
|
|
||||||
#elif defined LMIC_ENABLE_DeviceTimeReq
|
|
||||||
// Schedule a network time sync request at the next possible time
|
|
||||||
LMIC_requestNetworkTime(user_request_network_time_callback, &userUTCTime);
|
|
||||||
ESP_LOGI(TAG, "Network time request scheduled");
|
|
||||||
#endif // HAS_GPS
|
|
||||||
|
|
||||||
#endif // TIME_SYNC_INTERVAL
|
|
||||||
} // do_timesync()
|
|
||||||
|
|
||||||
#ifndef VERBOSE
|
#ifndef VERBOSE
|
||||||
int redirect_log(const char *fmt, va_list args) {
|
int redirect_log(const char *fmt, va_list args) {
|
||||||
// do nothing
|
// do nothing
|
||||||
|
@ -53,6 +53,32 @@ void gps_read() {
|
|||||||
gps.passedChecksum(), gps.failedChecksum(), gps.sentencesWithFix());
|
gps.passedChecksum(), gps.failedChecksum(), gps.sentencesWithFix());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// helper function to convert gps date/time into time_t
|
||||||
|
time_t tmConvert_t(uint16_t YYYY, uint8_t MM, uint8_t DD, uint8_t hh,
|
||||||
|
uint8_t mm, uint8_t ss) {
|
||||||
|
tmElements_t tm;
|
||||||
|
tm.Year = YYYY - 1970; // note year argument is offset from 1970 in time.h
|
||||||
|
tm.Month = MM;
|
||||||
|
tm.Day = DD;
|
||||||
|
tm.Hour = hh;
|
||||||
|
tm.Minute = mm;
|
||||||
|
tm.Second = ss;
|
||||||
|
return makeTime(tm);
|
||||||
|
}
|
||||||
|
|
||||||
|
// function to fetch current time from gps
|
||||||
|
time_t get_gpstime(void) {
|
||||||
|
// never call now() in this function, this would cause a recursion!
|
||||||
|
time_t t = 0;
|
||||||
|
if (gps.time.age() < 1500) {
|
||||||
|
t = tmConvert_t(gps.date.year(), gps.date.month(), gps.date.day(),
|
||||||
|
gps.time.hour(), gps.time.minute(), gps.time.second());
|
||||||
|
} else {
|
||||||
|
ESP_LOGW(TAG, "GPS has no confident time");
|
||||||
|
}
|
||||||
|
return t;
|
||||||
|
} // get_gpstime()
|
||||||
|
|
||||||
// GPS serial feed FreeRTos Task
|
// GPS serial feed FreeRTos Task
|
||||||
void gps_loop(void *pvParameters) {
|
void gps_loop(void *pvParameters) {
|
||||||
|
|
||||||
|
@ -41,7 +41,7 @@
|
|||||||
#define BOARD_HAS_PSRAM // use extra 4MB extern RAM
|
#define BOARD_HAS_PSRAM // use extra 4MB extern RAM
|
||||||
|
|
||||||
#define HAS_GPS 1 // use if board has GPS
|
#define HAS_GPS 1 // use if board has GPS
|
||||||
#define GPS_SERIAL 9600, SERIAL_8N1, GPIO_NUM_12, GPIO_NUM_15 // 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
|
||||||
|
|
||||||
// Pins for I2C interface of OLED Display
|
// Pins for I2C interface of OLED Display
|
||||||
#define MY_OLED_SDA (4)
|
#define MY_OLED_SDA (4)
|
||||||
@ -50,6 +50,10 @@
|
|||||||
|
|
||||||
// Pins for on board DS3231 RTC chip
|
// Pins 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
|
||||||
|
#define RTC_INT GPIO_NUM_34 // interrupt input from rtc
|
||||||
|
|
||||||
|
// Settings for IF482 interface
|
||||||
|
#define HAS_IF482 9600, SERIAL_7E1, GPIO_NUM_12, GPIO_NUM_14 // IF482 serial port parameters
|
||||||
|
|
||||||
// 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)
|
||||||
|
@ -1,7 +1,19 @@
|
|||||||
#if defined HAS_IF482 && defined HAS_RTC
|
#if defined HAS_IF482 && defined HAS_RTC
|
||||||
|
|
||||||
/*
|
/* NOTE:
|
||||||
|
The IF482 Generator needs an high precise 1 Hz clock signal which cannot be
|
||||||
|
acquired in suitable precision on the ESP32 SoC itself. Additional clocking
|
||||||
|
hardware is required, ususally the clock signal is generated by external RTC or
|
||||||
|
GPS chip or a GPS chip which can generate a precise clock signal (+/- 2ppm). In
|
||||||
|
this example code we use a Maxim DS3231 RTC chip, and configure it's interrupt
|
||||||
|
output as clock output. The clock signal triggers an interrupt on the ESP32,
|
||||||
|
which controls the realtime output of IF482 telegram. This is why code in
|
||||||
|
IF482.cpp depends on code in RTCTIME.cpp.
|
||||||
|
*/
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
/*
|
||||||
IF482 Generator to control clocks with IF482 telegram input (e.g. BÜRK BU190)
|
IF482 Generator to control clocks with IF482 telegram input (e.g. BÜRK BU190)
|
||||||
|
|
||||||
Example IF482 telegram: "OAL160806F170400"
|
Example IF482 telegram: "OAL160806F170400"
|
||||||
@ -63,6 +75,7 @@ L: Local Time
|
|||||||
not evaluated by model BU-190
|
not evaluated by model BU-190
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
#include "if482.h"
|
#include "if482.h"
|
||||||
|
|
||||||
@ -79,7 +92,7 @@ int if482_init(void) {
|
|||||||
// open serial interface
|
// open serial interface
|
||||||
IF482.begin(HAS_IF482);
|
IF482.begin(HAS_IF482);
|
||||||
|
|
||||||
// use rtc 1Hz clock for triggering IF482 telegram send
|
// use external rtc 1Hz clock for triggering IF482 telegram
|
||||||
Rtc.SetSquareWavePinClockFrequency(DS3231SquareWaveClock_1Hz);
|
Rtc.SetSquareWavePinClockFrequency(DS3231SquareWaveClock_1Hz);
|
||||||
Rtc.SetSquareWavePin(DS3231SquareWavePin_ModeClock);
|
Rtc.SetSquareWavePin(DS3231SquareWavePin_ModeClock);
|
||||||
pinMode(RTC_INT, INPUT_PULLUP);
|
pinMode(RTC_INT, INPUT_PULLUP);
|
||||||
@ -124,7 +137,7 @@ void if482_loop(void *pvParameters) {
|
|||||||
time_t t, tt;
|
time_t t, tt;
|
||||||
const TickType_t shotTime = pdMS_TO_TICKS(IF482_OFFSET);
|
const TickType_t shotTime = pdMS_TO_TICKS(IF482_OFFSET);
|
||||||
|
|
||||||
// wait until begin of a new second
|
// wait until begin of a new second to sync clock signal and absolute time
|
||||||
t = tt = now();
|
t = tt = now();
|
||||||
do {
|
do {
|
||||||
tt = now();
|
tt = now();
|
||||||
@ -146,7 +159,7 @@ void if482_loop(void *pvParameters) {
|
|||||||
// now we're synced to start of second t and wait
|
// now we're synced to start of second t and wait
|
||||||
// until it's time to start transmit telegram for t+1
|
// until it's time to start transmit telegram for t+1
|
||||||
vTaskDelayUntil(&wakeTime, shotTime);
|
vTaskDelayUntil(&wakeTime, shotTime);
|
||||||
IF482.print(if482Telegram(t+1));
|
IF482.print(if482Telegram(t + 1));
|
||||||
}
|
}
|
||||||
vTaskDelete(IF482Task); // shoud never be reached
|
vTaskDelete(IF482Task); // shoud never be reached
|
||||||
} // if482_loop()
|
} // if482_loop()
|
||||||
|
@ -453,7 +453,7 @@ void user_request_network_time_callback(void *pVoidUserUTCTime,
|
|||||||
uint32_t requestDelaySec = osticks2ms(ticksNow - ticksRequestSent) / 1000;
|
uint32_t requestDelaySec = osticks2ms(ticksNow - ticksRequestSent) / 1000;
|
||||||
*pUserUTCTime += requestDelaySec;
|
*pUserUTCTime += requestDelaySec;
|
||||||
|
|
||||||
// Update system time with time read from the network
|
// Update system time with time read from the network
|
||||||
setTime(*pUserUTCTime);
|
setTime(*pUserUTCTime);
|
||||||
#ifdef HAS_RTC
|
#ifdef HAS_RTC
|
||||||
if (!set_rtctime(*pUserUTCTime))
|
if (!set_rtctime(*pUserUTCTime))
|
||||||
|
18
src/main.cpp
18
src/main.cpp
@ -192,7 +192,13 @@ void setup() {
|
|||||||
#ifdef HAS_RTC
|
#ifdef HAS_RTC
|
||||||
strcat_P(features, " RTC");
|
strcat_P(features, " RTC");
|
||||||
assert(rtc_init());
|
assert(rtc_init());
|
||||||
sync_rtctime();
|
setSyncProvider(&get_rtctime);
|
||||||
|
if (timeStatus() != timeSet)
|
||||||
|
ESP_LOGI(TAG, "Unable to sync system time with RTC");
|
||||||
|
else
|
||||||
|
ESP_LOGI(TAG, "RTC has set the system time");
|
||||||
|
setSyncInterval(TIME_SYNC_INTERVAL_RTC);
|
||||||
|
|
||||||
#ifdef HAS_IF482
|
#ifdef HAS_IF482
|
||||||
strcat_P(features, " IF482");
|
strcat_P(features, " IF482");
|
||||||
assert(if482_init());
|
assert(if482_init());
|
||||||
@ -205,6 +211,7 @@ void setup() {
|
|||||||
&IF482Task, // task handle
|
&IF482Task, // task handle
|
||||||
0); // CPU core
|
0); // CPU core
|
||||||
#endif // HAS_IF482
|
#endif // HAS_IF482
|
||||||
|
|
||||||
#endif // HAS_RTC
|
#endif // HAS_RTC
|
||||||
|
|
||||||
// initialize wifi antenna
|
// initialize wifi antenna
|
||||||
@ -416,6 +423,15 @@ void setup() {
|
|||||||
#endif
|
#endif
|
||||||
#endif // HAS_BUTTON
|
#endif // HAS_BUTTON
|
||||||
|
|
||||||
|
#ifdef HAS_GPS
|
||||||
|
setSyncProvider(&get_gpstime);
|
||||||
|
if (timeStatus() != timeSet)
|
||||||
|
ESP_LOGI(TAG, "Unable to sync system time with GPS");
|
||||||
|
else
|
||||||
|
ESP_LOGI(TAG, "GPS has set the system time");
|
||||||
|
setSyncInterval(TIME_SYNC_INTERVAL_GPS);
|
||||||
|
#endif
|
||||||
|
|
||||||
// start RTC interrupt
|
// start RTC interrupt
|
||||||
#if defined HAS_IF482 && defined HAS_RTC
|
#if defined HAS_IF482 && defined HAS_RTC
|
||||||
// setup external interupt for active low RTC INT pin
|
// setup external interupt for active low RTC INT pin
|
||||||
|
@ -82,9 +82,11 @@
|
|||||||
#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 and external time sources
|
// settings for syncing time of node and external time sources
|
||||||
#define TIME_SYNC_INTERVAL 60 // sync time each ... minutes with external source [default = 60], comment out means off
|
#define TIME_SYNC_INTERVAL_GPS 5 // sync time each ... minutes with GPS [default = 5], comment out means off
|
||||||
#define TIME_SYNC_INTERVAL_RTC 5 // sync time each ... minutes with RTC [default = 5], comment out means off
|
#define TIME_SYNC_INTERVAL_RTC 5 // sync time each ... minutes with RTC [default = 5], comment out means off
|
||||||
|
//#define TIME_SYNC_INTERVAL_LORA 60 // sync time each ... minutes with LORA network [default = 60], comment out means off
|
||||||
#define IF482_OFFSET 984 // 1sec minus IF482 serial transmit time [ms]: e.g. 9 bits * 17 bytes * 1/9600 bps = 16ms
|
#define IF482_OFFSET 984 // 1sec minus IF482 serial transmit time [ms]: e.g. 9 bits * 17 bytes * 1/9600 bps = 16ms
|
||||||
|
|
||||||
// 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
|
||||||
#define STANDARD_TIME {"CET ", Last, Sun, Oct, 3, 60} // Central European Standard Time
|
#define STANDARD_TIME {"CET ", Last, Sun, Oct, 3, 60} // Central European Standard Time
|
||||||
|
@ -57,11 +57,11 @@ error:
|
|||||||
|
|
||||||
} // rtc_init()
|
} // rtc_init()
|
||||||
|
|
||||||
int set_rtctime(uint32_t UTCTime) {
|
int set_rtctime(uint32_t t) {
|
||||||
// return = 0 -> error / return = 1 -> success
|
// return = 0 -> error / return = 1 -> success
|
||||||
// block i2c bus access
|
// block i2c bus access
|
||||||
if (I2C_MUTEX_LOCK()) {
|
if (I2C_MUTEX_LOCK()) {
|
||||||
Rtc.SetDateTime(RtcDateTime(UTCTime));
|
Rtc.SetDateTime(RtcDateTime(t));
|
||||||
I2C_MUTEX_UNLOCK(); // release i2c bus access
|
I2C_MUTEX_UNLOCK(); // release i2c bus access
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@ -81,44 +81,20 @@ int set_rtctime(RtcDateTime t) {
|
|||||||
|
|
||||||
time_t get_rtctime(void) {
|
time_t get_rtctime(void) {
|
||||||
// never call now() in this function, this would cause a recursion!
|
// never call now() in this function, this would cause a recursion!
|
||||||
time_t tt = 0;
|
time_t t = 0;
|
||||||
// block i2c bus access
|
// block i2c bus access
|
||||||
if (I2C_MUTEX_LOCK()) {
|
if (I2C_MUTEX_LOCK()) {
|
||||||
if (!Rtc.IsDateTimeValid()) {
|
if (Rtc.IsDateTimeValid()) {
|
||||||
ESP_LOGW(TAG, "RTC has no confident time");
|
RtcDateTime tt = Rtc.GetDateTime();
|
||||||
|
t = tt.Epoch32Time();
|
||||||
} else {
|
} else {
|
||||||
RtcDateTime t = Rtc.GetDateTime();
|
ESP_LOGW(TAG, "RTC has no confident time");
|
||||||
tt = t.Epoch32Time();
|
|
||||||
}
|
}
|
||||||
I2C_MUTEX_UNLOCK(); // release i2c bus access
|
I2C_MUTEX_UNLOCK(); // release i2c bus access
|
||||||
return tt;
|
|
||||||
}
|
}
|
||||||
return tt;
|
return t;
|
||||||
} // get_rtctime()
|
} // get_rtctime()
|
||||||
|
|
||||||
void sync_rtctime(void) {
|
|
||||||
if (timeStatus() != timeSet) { // do we need time sync?
|
|
||||||
time_t t = get_rtctime();
|
|
||||||
if (t) { // have we got a valid time from RTC?
|
|
||||||
setTime(t);
|
|
||||||
time_t tt = myTZ.toLocal(t);
|
|
||||||
ESP_LOGI(TAG, "RTC has set system time to %02d/%02d/%d %02d:%02d:%02d",
|
|
||||||
month(tt), day(tt), year(tt), hour(tt), minute(tt), second(tt));
|
|
||||||
} else
|
|
||||||
ESP_LOGW(TAG, "System time was not synced");
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef TIME_SYNC_INTERVAL_RTC
|
|
||||||
setSyncProvider(&get_rtctime); // does not sync if callback function returns 0
|
|
||||||
if (timeStatus() != timeSet)
|
|
||||||
ESP_LOGI("Unable to sync with the RTC");
|
|
||||||
else
|
|
||||||
ESP_LOGI("RTC has set the system time");
|
|
||||||
setSyncInterval(TIME_SYNC_INTERVAL_RTC);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
} // sync_rtctime;
|
|
||||||
|
|
||||||
float get_rtctemp(void) {
|
float get_rtctemp(void) {
|
||||||
// block i2c bus access
|
// block i2c bus access
|
||||||
if (I2C_MUTEX_LOCK()) {
|
if (I2C_MUTEX_LOCK()) {
|
||||||
@ -127,6 +103,6 @@ float get_rtctemp(void) {
|
|||||||
return temp.AsFloatDegC();
|
return temp.AsFloatDegC();
|
||||||
} // while
|
} // while
|
||||||
return 0;
|
return 0;
|
||||||
} // get_rtc()
|
} // get_rtctemp()
|
||||||
|
|
||||||
#endif // HAS_RTC
|
#endif // HAS_RTC
|
Loading…
Reference in New Issue
Block a user