Merge pull request #862 from cyberman54/development

Development
This commit is contained in:
Verkehrsrot 2022-03-05 14:11:28 +01:00 committed by GitHub
commit 60c6e3c2e8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 264 additions and 368 deletions

View File

@ -30,11 +30,6 @@
// length of display buffer for lmic event messages // length of display buffer for lmic event messages
#define LMIC_EVENTMSG_LEN 17 #define LMIC_EVENTMSG_LEN 17
// I2C bus access control
#define I2C_MUTEX_LOCK() \
(xSemaphoreTake(I2Caccess, pdMS_TO_TICKS(DISPLAYREFRESH_MS)) == pdTRUE)
#define I2C_MUTEX_UNLOCK() (xSemaphoreGive(I2Caccess))
// pseudo system halt function, useful to prevent writeloops to NVRAM // pseudo system halt function, useful to prevent writeloops to NVRAM
#ifndef _ASSERT #ifndef _ASSERT
#define _ASSERT(cond) \ #define _ASSERT(cond) \

View File

@ -10,7 +10,8 @@
#include "dcf77.h" #include "dcf77.h"
#include "esp_sntp.h" #include "esp_sntp.h"
#define HAS_LORA_TIME ((HAS_LORA) && ((TIME_SYNC_LORASERVER) || (TIME_SYNC_LORAWAN))) #define HAS_LORA_TIME \
((HAS_LORA) && ((TIME_SYNC_LORASERVER) || (TIME_SYNC_LORAWAN)))
#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
@ -26,16 +27,14 @@ extern DRAM_ATTR bool TimePulseTick; // 1sec pps flag set by GPS or RTC
extern DRAM_ATTR unsigned long lastPPS; 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 IRAM_ATTR GPSIRQ(void);
void clock_init(void); //void clock_loop(void *pvParameters);
void clock_loop(void *pvParameters);
void setTimeSyncIRQ(void); void setTimeSyncIRQ(void);
uint8_t timepulse_init(void); void time_init(void);
bool timeIsValid(time_t const t); bool timeIsValid(time_t const t);
void calibrateTime(void); void calibrateTime(void);
bool setMyTime(uint32_t t_sec, uint16_t t_msec, bool setMyTime(uint32_t t_sec, uint16_t t_msec, timesource_t mytimesource);
timesource_t mytimesource);
time_t compileTime(void); 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,

View File

@ -52,42 +52,28 @@ int bme_init(void) {
int rc = 0; int rc = 0;
#ifdef HAS_BME680 #ifdef HAS_BME680
// block i2c bus access Wire.begin(HAS_BME680);
if (I2C_MUTEX_LOCK()) { iaqSensor.begin(BME680_ADDR, Wire);
Wire.begin(HAS_BME680); ESP_LOGI(TAG, "BSEC v%d.%d.%d.%d", iaqSensor.version.major,
iaqSensor.begin(BME680_ADDR, Wire); iaqSensor.version.minor, iaqSensor.version.major_bugfix,
iaqSensor.version.minor_bugfix);
ESP_LOGI(TAG, "BSEC v%d.%d.%d.%d", iaqSensor.version.major, iaqSensor.setConfig(bsec_config_iaq);
iaqSensor.version.minor, iaqSensor.version.major_bugfix, loadState();
iaqSensor.version.minor_bugfix); iaqSensor.setTemperatureOffset((float)BME_TEMP_OFFSET);
iaqSensor.updateSubscription(sensorList, 10, BSEC_SAMPLE_RATE_LP);
iaqSensor.setConfig(bsec_config_iaq); rc = checkIaqSensorStatus();
loadState();
iaqSensor.setTemperatureOffset((float)BME_TEMP_OFFSET);
iaqSensor.updateSubscription(sensorList, 10, BSEC_SAMPLE_RATE_LP);
rc = checkIaqSensorStatus();
} else
ESP_LOGE(TAG, "I2c bus busy - BME680 initialization error");
#elif defined HAS_BME280 #elif defined HAS_BME280
if (I2C_MUTEX_LOCK()) { rc = bme.begin(BME280_ADDR);
rc = bme.begin(BME280_ADDR);
} else
ESP_LOGE(TAG, "I2c bus busy - BME280 initialization error");
#elif defined HAS_BMP180 #elif defined HAS_BMP180
if (I2C_MUTEX_LOCK()) { // Wire.begin(21, 22);
// Wire.begin(21, 22); rc = bmp.begin();
rc = bmp.begin();
} else
ESP_LOGE(TAG, "I2c bus busy - BMP180 initialization error");
#endif #endif
I2C_MUTEX_UNLOCK(); // release i2c bus access
if (rc) if (rc)
bmecycler.attach(BMECYCLE, setBMEIRQ); // start cyclic data transmit bmecycler.attach(BMECYCLE, setBMEIRQ); // start cyclic data transmit
return rc; return rc;
@ -123,8 +109,7 @@ int checkIaqSensorStatus(void) {
// store current BME sensor data in struct // store current BME sensor data in struct
void bme_storedata(bmeStatus_t *bme_store) { void bme_storedata(bmeStatus_t *bme_store) {
if ((cfg.payloadmask & MEMS_DATA) && if (cfg.payloadmask & MEMS_DATA)
(I2C_MUTEX_LOCK())) { // block i2c bus access
#ifdef HAS_BME680 #ifdef HAS_BME680
if (iaqSensor.run()) { // if new data is available if (iaqSensor.run()) { // if new data is available
@ -143,20 +128,17 @@ void bme_storedata(bmeStatus_t *bme_store) {
} }
#elif defined HAS_BME280 #elif defined HAS_BME280
bme_store->temperature = bme.readTemperature(); bme_store->temperature = bme.readTemperature();
bme_store->pressure = (bme.readPressure() / 100.0); // conversion Pa -> hPa bme_store->pressure = (bme.readPressure() / 100.0); // conversion Pa -> hPa
// bme.readAltitude(SEALEVELPRESSURE_HPA); // bme.readAltitude(SEALEVELPRESSURE_HPA);
bme_store->humidity = bme.readHumidity(); bme_store->humidity = bme.readHumidity();
bme_store->iaq = 0; // IAQ feature not present with BME280 bme_store->iaq = 0; // IAQ feature not present with BME280
#elif defined HAS_BMP180 #elif defined HAS_BMP180
bme_store->temperature = bmp.readTemperature(); bme_store->temperature = bmp.readTemperature();
bme_store->pressure = (bmp.readPressure() / 100.0); // conversion Pa -> hPa bme_store->pressure = (bmp.readPressure() / 100.0); // conversion Pa -> hPa
// bme.readAltitude(SEALEVELPRESSURE_HPA); // bme.readAltitude(SEALEVELPRESSURE_HPA);
bme_store->iaq = 0; // IAQ feature not present with BME280 bme_store->iaq = 0; // IAQ feature not present with BME280
#endif #endif
I2C_MUTEX_UNLOCK(); // release i2c bus access
}
} // bme_storedata() } // bme_storedata()
#ifdef HAS_BME680 #ifdef HAS_BME680

View File

@ -18,10 +18,6 @@ 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
// 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 // 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(),

View File

@ -90,85 +90,72 @@ void dp_setup(int contrast) {
void dp_init(bool verbose) { void dp_init(bool verbose) {
#if (HAS_DISPLAY) == 1 // i2c dp_setup(DISPLAYCONTRAST);
// block i2c bus access
if (!I2C_MUTEX_LOCK())
ESP_LOGV(TAG, "[%0.3f] i2c mutex lock failed", _seconds());
else {
#endif
dp_setup(DISPLAYCONTRAST); if (verbose) {
if (verbose) { // show startup screen
// to come -> display .bmp file with logo
// show startup screen
// to come -> display .bmp file with logo
// show chip information // show chip information
#if (VERBOSE) #if (VERBOSE)
esp_chip_info_t chip_info; esp_chip_info_t chip_info;
esp_chip_info(&chip_info); esp_chip_info(&chip_info);
dp_setFont(MY_FONT_NORMAL); dp_setFont(MY_FONT_NORMAL);
dp_printf("** PAXCOUNTER **"); dp_printf("** PAXCOUNTER **");
dp_println(); dp_println();
dp_printf("Software v%s", PROGVERSION); dp_printf("Software v%s", PROGVERSION);
dp_println(); dp_println();
dp_printf("ESP32 %d cores", chip_info.cores); dp_printf("ESP32 %d cores", chip_info.cores);
dp_println(); dp_println();
dp_printf("Chip Rev.%d", chip_info.revision); dp_printf("Chip Rev.%d", chip_info.revision);
dp_println(); dp_println();
dp_printf("WiFi%s%s", (chip_info.features & CHIP_FEATURE_BT) ? "/BT" : "", dp_printf("WiFi%s%s", (chip_info.features & CHIP_FEATURE_BT) ? "/BT" : "",
(chip_info.features & CHIP_FEATURE_BLE) ? "/BLE" : ""); (chip_info.features & CHIP_FEATURE_BLE) ? "/BLE" : "");
dp_println(); dp_println();
dp_printf("%dMB %s Flash", spi_flash_get_chip_size() / (1024 * 1024), dp_printf("%dMB %s Flash", spi_flash_get_chip_size() / (1024 * 1024),
(chip_info.features & CHIP_FEATURE_EMB_FLASH) ? "int." (chip_info.features & CHIP_FEATURE_EMB_FLASH) ? "int." : "ext.");
: "ext.");
// give user some time to read or take picture // give user some time to read or take picture
dp_dump(displaybuf); dp_dump(displaybuf);
delay(2000); delay(2000);
dp_clear(); dp_clear();
#endif // VERBOSE #endif // VERBOSE
#if (HAS_LORA) #if (HAS_LORA)
// generate DEVEUI as QR code and text // generate DEVEUI as QR code and text
uint8_t buf[8], *p = buf; uint8_t buf[8], *p = buf;
char deveui[17]; char deveui[17];
os_getDevEui((u1_t *)buf); os_getDevEui((u1_t *)buf);
snprintf(deveui, 17, "%016llX", (*(uint64_t *)(p))); snprintf(deveui, 17, "%016llX", (*(uint64_t *)(p)));
// display DEVEUI as QR code on the left // display DEVEUI as QR code on the left
dp_contrast(30); dp_contrast(30);
dp_printqr(3, 3, deveui); dp_printqr(3, 3, deveui);
// display DEVEUI as plain text on the right // display DEVEUI as plain text on the right
const int x_offset = QR_SCALEFACTOR * 29 + 14; const int x_offset = QR_SCALEFACTOR * 29 + 14;
dp_setTextCursor(x_offset, 0); dp_setTextCursor(x_offset, 0);
dp_setFont(MY_FONT_NORMAL); dp_setFont(MY_FONT_NORMAL);
dp_printf("DEVEUI"); dp_printf("DEVEUI");
dp_println(); dp_println();
for (uint8_t i = 0; i <= 3; i++) { for (uint8_t i = 0; i <= 3; i++) {
dp_setTextCursor(x_offset, i + 3); dp_setTextCursor(x_offset, i + 3);
dp_printf("%4.4s", deveui + i * 4); dp_printf("%4.4s", deveui + i * 4);
} }
// give user some time to read or take picture // give user some time to read or take picture
dp_dump(displaybuf); dp_dump(displaybuf);
#if !(BOOTMENU) #if !(BOOTMENU)
delay(8000); delay(8000);
#endif #endif
#endif // HAS_LORA #endif // HAS_LORA
} // verbose } // verbose
dp_power(cfg.screenon); // set display off if disabled dp_power(cfg.screenon); // set display off if disabled
#if (HAS_DISPLAY) == 1 // i2c
I2C_MUTEX_UNLOCK(); // release i2c bus access
} // mutex
#endif
} // dp_init } // dp_init
@ -182,29 +169,22 @@ void dp_refresh(bool nextPage) {
if (!DisplayIsOn && (DisplayIsOn == cfg.screenon)) if (!DisplayIsOn && (DisplayIsOn == cfg.screenon))
return; return;
// block i2c bus access // set display on/off according to current device configuration
if (!I2C_MUTEX_LOCK()) if (DisplayIsOn != cfg.screenon) {
ESP_LOGV(TAG, "[%0.3f] i2c mutex lock failed", _seconds()); DisplayIsOn = cfg.screenon;
else { dp_power(cfg.screenon);
// set display on/off according to current device configuration }
if (DisplayIsOn != cfg.screenon) {
DisplayIsOn = cfg.screenon;
dp_power(cfg.screenon);
}
#ifndef HAS_BUTTON #ifndef HAS_BUTTON
// auto flip page if we are in unattended mode // auto flip page if we are in unattended mode
if ((++framecounter) > (DISPLAYCYCLE * 1000 / DISPLAYREFRESH_MS)) { if ((++framecounter) > (DISPLAYCYCLE * 1000 / DISPLAYREFRESH_MS)) {
framecounter = 0; framecounter = 0;
nextPage = true; nextPage = true;
} }
#endif #endif
dp_drawPage(nextPage); dp_drawPage(nextPage);
I2C_MUTEX_UNLOCK(); // release i2c bus access
} // mutex
} // refreshDisplay() } // refreshDisplay()
void dp_drawPage(bool nextpage) { void dp_drawPage(bool nextpage) {
@ -600,14 +580,8 @@ void dp_power(uint8_t screenon) {
void dp_shutdown(void) { void dp_shutdown(void) {
#if (HAS_DISPLAY) == 1 #if (HAS_DISPLAY) == 1
// block i2c bus access obdPower(&ssoled, false);
if (!I2C_MUTEX_LOCK()) delay(DISPLAYREFRESH_MS / 1000 * 1.1);
ESP_LOGV(TAG, "[%0.3f] i2c mutex lock failed", _seconds());
else {
obdPower(&ssoled, false);
delay(DISPLAYREFRESH_MS / 1000 * 1.1);
I2C_MUTEX_UNLOCK(); // release i2c bus access
}
#elif (HAS_DISPLAY) == 2 #elif (HAS_DISPLAY) == 2
// to come // to come
#endif #endif

View File

@ -12,7 +12,7 @@ void i2c_init(void) {
Wire.begin(); Wire.begin();
} }
//void i2c_deinit(void) { Wire.end(); } // void i2c_deinit(void) { Wire.end(); }
void i2c_deinit(void) { Wire.~TwoWire(); } void i2c_deinit(void) { Wire.~TwoWire(); }
void i2c_scan(void) { void i2c_scan(void) {
@ -49,90 +49,71 @@ void i2c_scan(void) {
ESP_LOGI(TAG, "Starting I2C bus scan..."); ESP_LOGI(TAG, "Starting I2C bus scan...");
// block i2c bus access memset(&bbi2c, 0, sizeof(bbi2c));
if (I2C_MUTEX_LOCK()) { bbi2c.bWire = 0;
bbi2c.iSDA = MY_DISPLAY_SDA;
bbi2c.iSCL = MY_DISPLAY_SCL;
I2CInit(&bbi2c, 100000L); // Scan at 100KHz low speed
delay(100); // allow devices to power up
memset(&bbi2c, 0, sizeof(bbi2c)); uint8_t map[16];
bbi2c.bWire = 0; uint8_t i;
bbi2c.iSDA = MY_DISPLAY_SDA; int iDevice, iCount;
bbi2c.iSCL = MY_DISPLAY_SCL;
I2CInit(&bbi2c, 100000L); // Scan at 100KHz low speed
delay(100); // allow devices to power up
uint8_t map[16]; I2CScan(&bbi2c, map); // get bitmap of connected I2C devices
uint8_t i; if (map[0] == 0xfe) // something is wrong with the I2C bus
int iDevice, iCount; {
ESP_LOGI(TAG, "I2C pins are not correct or the bus is being pulled low "
I2CScan(&bbi2c, map); // get bitmap of connected I2C devices "by a bad device; unable to run scan");
if (map[0] == 0xfe) // something is wrong with the I2C bus } else {
iCount = 0;
for (i = 1; i < 128; i++) // skip address 0 (general call address) since
// more than 1 device can respond
{ {
ESP_LOGI(TAG, "I2C pins are not correct or the bus is being pulled low " if (map[i >> 3] & (1 << (i & 7))) // device found
"by a bad device; unable to run scan");
} else {
iCount = 0;
for (i = 1; i < 128; i++) // skip address 0 (general call address) since
// more than 1 device can respond
{ {
if (map[i >> 3] & (1 << (i & 7))) // device found iCount++;
{ iDevice = I2CDiscoverDevice(&bbi2c, i);
iCount++; ESP_LOGI(TAG, "Device found at 0x%X, type = %s", i,
iDevice = I2CDiscoverDevice(&bbi2c, i); szNames[iDevice]); // show the device name as a string
ESP_LOGI(TAG, "Device found at 0x%X, type = %s", i, }
szNames[iDevice]); // show the device name as a string } // for i
} ESP_LOGI(TAG, "%u I2C device(s) found", iCount);
} // for i }
ESP_LOGI(TAG, "%u I2C device(s) found", iCount);
}
I2C_MUTEX_UNLOCK(); // release i2c bus access
} else
ESP_LOGE(TAG, "I2C bus busy - scan error");
} }
// mutexed functions for i2c r/w access // functions for i2c r/w access, mutexing is done by Wire.cpp
int i2c_readBytes(uint8_t addr, uint8_t reg, uint8_t *data, uint8_t len) { int i2c_readBytes(uint8_t addr, uint8_t reg, uint8_t *data, uint8_t len) {
if (I2C_MUTEX_LOCK()) {
uint8_t ret = 0; uint8_t ret = 0;
Wire.beginTransmission(addr); Wire.beginTransmission(addr);
Wire.write(reg); Wire.write(reg);
Wire.endTransmission(false); Wire.endTransmission(false);
uint8_t cnt = Wire.requestFrom(addr, (uint8_t)len, (uint8_t)1); uint8_t cnt = Wire.requestFrom(addr, (uint8_t)len, (uint8_t)1);
if (!cnt) if (!cnt)
ret = 0xFF;
uint16_t index = 0;
while (Wire.available()) {
if (index > len) {
ret = 0xFF; ret = 0xFF;
uint16_t index = 0; goto finish;
while (Wire.available()) {
if (index > len) {
ret = 0xFF;
goto finish;
}
data[index++] = Wire.read();
} }
data[index++] = Wire.read();
finish:
I2C_MUTEX_UNLOCK(); // release i2c bus access
return ret;
} else {
ESP_LOGW(TAG, "[%0.3f] i2c mutex lock failed", _seconds());
return 0xFF;
} }
finish:
return ret ? ret : 0xFF;
} }
int i2c_writeBytes(uint8_t addr, uint8_t reg, uint8_t *data, uint8_t len) { int i2c_writeBytes(uint8_t addr, uint8_t reg, uint8_t *data, uint8_t len) {
if (I2C_MUTEX_LOCK()) {
uint8_t ret = 0; uint8_t ret = 0;
Wire.beginTransmission(addr); Wire.beginTransmission(addr);
Wire.write(reg); Wire.write(reg);
for (uint16_t i = 0; i < len; i++) { for (uint16_t i = 0; i < len; i++) {
Wire.write(data[i]); Wire.write(data[i]);
}
ret = Wire.endTransmission();
I2C_MUTEX_UNLOCK(); // release i2c bus access
return ret ? ret : 0xFF;
} else {
ESP_LOGW(TAG, "[%0.3f] i2c mutex lock failed", _seconds());
return 0xFF;
} }
ret = Wire.endTransmission();
return ret ? ret : 0xFF;
} }

View File

@ -87,11 +87,6 @@ void setup() {
char features[100] = ""; char features[100] = "";
// create some semaphores for syncing / mutexing tasks
I2Caccess = xSemaphoreCreateMutex(); // for access management of i2c bus
_ASSERT(I2Caccess != NULL);
I2C_MUTEX_UNLOCK();
// disable brownout detection // disable brownout detection
#ifdef DISABLE_BROWNOUT #ifdef DISABLE_BROWNOUT
// register with brownout is at address DR_REG_RTCCNTL_BASE + 0xd4 // register with brownout is at address DR_REG_RTCCNTL_BASE + 0xd4
@ -481,20 +476,8 @@ void setup() {
// only if we have a timesource we do timesync // only if we have a timesource we do timesync
#if ((HAS_LORA_TIME) || (HAS_GPS) || (HAS_RTC)) #if ((HAS_LORA_TIME) || (HAS_GPS) || (HAS_RTC))
time_init();
#if (defined HAS_IF482 || defined HAS_DCF77)
ESP_LOGI(TAG, "Starting Clock Controller...");
clock_init();
#endif
#if (HAS_LORA_TIME)
timesync_init(); // create loraserver time sync task
#endif
ESP_LOGI(TAG, "Starting Timekeeper...");
_ASSERT(timepulse_init()); // starts pps and cyclic time sync
strcat_P(features, " TIME"); strcat_P(features, " TIME");
#endif // timesync #endif // timesync
// show compiled features // show compiled features

View File

@ -10,88 +10,75 @@ RtcDS3231<TwoWire> Rtc(Wire); // RTC hardware i2c interface
// initialize RTC // initialize RTC
uint8_t rtc_init(void) { uint8_t rtc_init(void) {
if (I2C_MUTEX_LOCK()) { // block i2c bus access Wire.begin(HAS_RTC);
Rtc.Begin(MY_DISPLAY_SDA, MY_DISPLAY_SCL);
Wire.begin(HAS_RTC); // configure RTC chip
Rtc.Begin(MY_DISPLAY_SDA, MY_DISPLAY_SCL); Rtc.Enable32kHzPin(false);
Rtc.SetSquareWavePin(DS3231SquareWavePin_ModeNone);
// configure RTC chip if (!Rtc.GetIsRunning()) {
Rtc.Enable32kHzPin(false); ESP_LOGI(TAG, "RTC not running, starting now");
Rtc.SetSquareWavePin(DS3231SquareWavePin_ModeNone); Rtc.SetIsRunning(true);
}
if (!Rtc.GetIsRunning()) {
ESP_LOGI(TAG, "RTC not running, starting now");
Rtc.SetIsRunning(true);
}
#if (TIME_SYNC_COMPILEDATE) #if (TIME_SYNC_COMPILEDATE)
// initialize a blank RTC without battery backup with build 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 compiletime"); ESP_LOGW(TAG, "RTC has no recent time, setting to compiletime");
Rtc.SetDateTime(RtcDateTime(mkgmtime(compileTime()) - Rtc.SetDateTime(RtcDateTime(mkgmtime(compileTime()) -
SECS_YR_2000)); // epoch -> sec2000 SECS_YR_2000)); // epoch -> sec2000
} }
#endif #endif
I2C_MUTEX_UNLOCK(); // release i2c bus access ESP_LOGI(TAG, "RTC initialized");
ESP_LOGI(TAG, "RTC initialized"); return 1; // success
return 1; // success
} else { // failure
ESP_LOGE(TAG, "RTC initialization error, I2C bus busy"); // return 0
return 0; // failure
}
} // rtc_init() } // rtc_init()
uint8_t set_rtctime(time_t t) { // t is sec epoch time uint8_t set_rtctime(time_t t) { // t is sec epoch time
if (I2C_MUTEX_LOCK()) {
#ifdef RTC_INT // sync rtc 1Hz pulse on top of second #ifdef RTC_INT // sync rtc 1Hz pulse on top of second
Rtc.SetSquareWavePin(DS3231SquareWavePin_ModeNone); // off Rtc.SetSquareWavePin(DS3231SquareWavePin_ModeNone); // off
Rtc.SetSquareWavePin(DS3231SquareWavePin_ModeClock); // start Rtc.SetSquareWavePin(DS3231SquareWavePin_ModeClock); // start
#endif #endif
Rtc.SetDateTime(RtcDateTime(t - SECS_YR_2000)); // epoch -> sec2000 Rtc.SetDateTime(RtcDateTime(t - SECS_YR_2000)); // epoch -> sec2000
I2C_MUTEX_UNLOCK(); ESP_LOGI(TAG, "RTC time synced");
ESP_LOGI(TAG, "RTC time synced"); return 1; // success
return 1; // success
} else { // failure
ESP_LOGE(TAG, "RTC set time failure"); // return 0
return 0; // failure
}
} // set_rtctime() } // set_rtctime()
time_t get_rtctime(uint16_t *msec) { time_t get_rtctime(uint16_t *msec) {
time_t t = 0; time_t t = 0;
*msec = 0; *msec = 0;
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();
#ifdef RTC_INT
// adjust time to top of next second by waiting TimePulseTick to flip
bool lastTick = TimePulseTick;
while (TimePulseTick == lastTick) {
};
t++;
#endif
return t;
} else {
ESP_LOGE(TAG, "RTC get time failure");
return 0; // failure
} }
#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;
} // get_rtctime() } // get_rtctime()
float get_rtctemp(void) { float get_rtctemp(void) {
if (I2C_MUTEX_LOCK()) { RtcTemperature temp = Rtc.GetTemperature();
RtcTemperature temp = Rtc.GetTemperature(); return temp.AsFloatDegC();
I2C_MUTEX_UNLOCK();
return temp.AsFloatDegC();
}
return 0;
} // get_rtctemp() } // get_rtctemp()
#endif // HAS_RTC #endif // HAS_RTC

View File

@ -31,6 +31,40 @@ Ticker timesyncer;
void setTimeSyncIRQ() { xTaskNotify(irqHandlerTask, TIMESYNC_IRQ, eSetBits); } void setTimeSyncIRQ() { xTaskNotify(irqHandlerTask, TIMESYNC_IRQ, eSetBits); }
// 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 esp32 hardware timer
void IRAM_ATTR CLOCKIRQ(void) {
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
// advance wall clock, if we have
#if (defined HAS_IF482 || defined HAS_DCF77)
xTaskNotifyFromISR(ClockTask, uint32_t(time(NULL)), eSetBits,
&xHigherPriorityTaskWoken);
#endif
// flip time pulse ticker, if needed
#ifdef HAS_DISPLAY
TimePulseTick = !TimePulseTick; // flip global variable pulse ticker
#endif
// yield only if we should
if (xHigherPriorityTaskWoken)
portYIELD_FROM_ISR();
}
void calibrateTime(void) { void calibrateTime(void) {
// kick off asynchronous lora timesync if we have // kick off asynchronous lora timesync if we have
@ -63,8 +97,7 @@ void calibrateTime(void) {
} // calibrateTime() } // calibrateTime()
// set system time (UTC), calibrate RTC and RTC_INT pps // set system time (UTC), calibrate RTC and RTC_INT pps
bool setMyTime(uint32_t t_sec, uint16_t t_msec, bool setMyTime(uint32_t t_sec, uint16_t t_msec, timesource_t mytimesource) {
timesource_t mytimesource) {
struct timeval tv = {0}; struct timeval tv = {0};
@ -126,7 +159,7 @@ bool setMyTime(uint32_t t_sec, uint16_t t_msec,
} }
// helper function to setup a pulse per second for time synchronisation // helper function to setup a pulse per second for time synchronisation
uint8_t timepulse_init() { void timepulse_init(void) {
// set esp-idf API sntp sync mode // set esp-idf API sntp sync mode
// sntp_init(); // sntp_init();
@ -141,22 +174,12 @@ uint8_t timepulse_init() {
// if we have, use pulse from on board RTC chip as time base for calendar time // if we have, use pulse from on board RTC chip as time base for calendar time
#if defined RTC_INT #if defined RTC_INT
// setup external rtc 1Hz clock pulse // setup external rtc 1Hz clock pulse
if (I2C_MUTEX_LOCK()) { Rtc.SetSquareWavePinClockFrequency(DS3231SquareWaveClock_1Hz);
Rtc.SetSquareWavePinClockFrequency(DS3231SquareWaveClock_1Hz); Rtc.SetSquareWavePin(DS3231SquareWavePin_ModeClock);
Rtc.SetSquareWavePin(DS3231SquareWavePin_ModeClock); pinMode(RTC_INT, INPUT_PULLUP);
I2C_MUTEX_UNLOCK(); attachInterrupt(digitalPinToInterrupt(RTC_INT), CLOCKIRQ, FALLING);
pinMode(RTC_INT, INPUT_PULLUP); ESP_LOGI(TAG, "Timepulse: external (RTC)");
attachInterrupt(digitalPinToInterrupt(RTC_INT), CLOCKIRQ, FALLING);
ESP_LOGI(TAG, "Timepulse: external (RTC)");
return 1; // success
} else {
ESP_LOGE(TAG, "RTC initialization error, I2C bus busy");
return 0; // failure
}
return 1; // success
#else #else
// use ESP32 hardware timer as time base for calendar time // 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
@ -164,53 +187,17 @@ uint8_t timepulse_init() {
timerAttachInterrupt(ppsIRQ, &CLOCKIRQ, false); timerAttachInterrupt(ppsIRQ, &CLOCKIRQ, false);
timerAlarmEnable(ppsIRQ); timerAlarmEnable(ppsIRQ);
ESP_LOGI(TAG, "Timepulse: internal (ESP32 hardware timer)"); ESP_LOGI(TAG, "Timepulse: internal (ESP32 hardware timer)");
return 1; // success
#endif #endif
// start cyclic time sync
timesyncer.attach(TIME_SYNC_INTERVAL * 60, setTimeSyncIRQ);
// 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 } // 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 esp32 hardware timer
void IRAM_ATTR CLOCKIRQ(void) {
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
// advance wall clock, if we have
#if (defined HAS_IF482 || defined HAS_DCF77)
xTaskNotifyFromISR(ClockTask, uint32_t(time(NULL)), eSetBits,
&xHigherPriorityTaskWoken);
#endif
// flip time pulse ticker, if needed
#ifdef HAS_DISPLAY
TimePulseTick = !TimePulseTick; // flip global variable pulse ticker
#endif
// yield only if we should
if (xHigherPriorityTaskWoken)
portYIELD_FROM_ISR();
}
// 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
@ -231,26 +218,6 @@ TickType_t tx_Ticks(uint32_t framesize, unsigned long baud, uint32_t config,
return round(txTime); return round(txTime);
} }
void clock_init(void) {
// setup clock output interface
#ifdef HAS_IF482
IF482.begin(HAS_IF482);
#elif defined HAS_DCF77
pinMode(HAS_DCF77, OUTPUT);
#endif
xTaskCreatePinnedToCore(clock_loop, // task function
"clockloop", // name of task
3072, // stack size of task
(void *)1, // task parameter
6, // priority of the task
&ClockTask, // task handle
1); // CPU core
_ASSERT(ClockTask != NULL); // has clock task started?
} // clock_init
void clock_loop(void *taskparameter) { // ClockTask void clock_loop(void *taskparameter) { // ClockTask
uint32_t current_time = 0, previous_time = 0; uint32_t current_time = 0, previous_time = 0;
@ -333,6 +300,26 @@ void clock_loop(void *taskparameter) { // ClockTask
} // for } // for
} // clock_loop() } // clock_loop()
void clock_init(void) {
// setup clock output interface
#ifdef HAS_IF482
IF482.begin(HAS_IF482);
#elif defined HAS_DCF77
pinMode(HAS_DCF77, OUTPUT);
#endif
xTaskCreatePinnedToCore(clock_loop, // task function
"clockloop", // name of task
3072, // stack size of task
(void *)1, // task parameter
6, // priority of the task
&ClockTask, // task handle
1); // CPU core
_ASSERT(ClockTask != NULL); // has clock task started?
} // clock_init
// 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(void) { time_t compileTime(void) {
@ -391,4 +378,16 @@ time_t mkgmtime(const struct tm *ptm) {
secs += ptm->tm_min * SecondsPerMinute; secs += ptm->tm_min * SecondsPerMinute;
secs += ptm->tm_sec; secs += ptm->tm_sec;
return secs; return secs;
}
void time_init(void) {
#if (HAS_LORA_TIME)
timesync_init(); // create loraserver time sync task
#endif
ESP_LOGI(TAG, "Starting time pulse...");
timepulse_init(); // starts pps and cyclic time sync
#if (defined HAS_IF482 || defined HAS_DCF77)
ESP_LOGI(TAG, "Starting clock controller...");
clock_init();
#endif
} }