improve RTC accuracy
This commit is contained in:
		
							parent
							
								
									43e6ec0cb6
								
							
						
					
					
						commit
						af01537a95
					
				| @ -111,6 +111,5 @@ typedef struct { | ||||
| } sdsStatus_t; | ||||
| 
 | ||||
| extern char clientId[20];   // unique clientID
 | ||||
| extern time_t _COMPILETIME; // epoch build time
 | ||||
| 
 | ||||
| #endif | ||||
| @ -12,7 +12,7 @@ extern RtcDS3231<TwoWire> Rtc; // make RTC instance globally available | ||||
| uint8_t rtc_init(void); | ||||
| uint8_t set_rtctime(time_t t); | ||||
| void sync_rtctime(void); | ||||
| time_t get_rtctime(void); | ||||
| time_t get_rtctime(uint16_t *msec); | ||||
| float get_rtctemp(void); | ||||
| 
 | ||||
| #endif // _RTCTIME_H
 | ||||
| @ -10,8 +10,8 @@ | ||||
| #include "dcf77.h" | ||||
| #include "esp_sntp.h" | ||||
| 
 | ||||
| #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 SECS_YR_2000 (946684800UL)    // the time at the start of y2k
 | ||||
| #define GPS_UTC_DIFF 315964800UL      // seconds diff between gps and utc epoch
 | ||||
| #define LEAP_SECS_SINCE_GPSEPOCH 18UL // state of 2021
 | ||||
| 
 | ||||
| enum timesource_t { _gps, _rtc, _lora, _unsynced, _set }; | ||||
| @ -33,7 +33,7 @@ bool timeIsValid(time_t const t); | ||||
| void calibrateTime(void); | ||||
| void IRAM_ATTR setMyTime(uint32_t t_sec, uint16_t t_msec, | ||||
|                          timesource_t mytimesource); | ||||
| time_t compileTime(const String compile_date); | ||||
| time_t compileTime(void); | ||||
| time_t mkgmtime(const struct tm *ptm); | ||||
| TickType_t tx_Ticks(uint32_t framesize, unsigned long baud, uint32_t config, | ||||
|                     int8_t rxPin, int8_t txPins); | ||||
|  | ||||
							
								
								
									
										35
									
								
								src/main.cpp
									
									
									
									
									
								
							
							
						
						
									
										35
									
								
								src/main.cpp
									
									
									
									
									
								
							| @ -25,26 +25,24 @@ licenses. Refer to LICENSE.txt file in repository for more details. | ||||
| 
 | ||||
| // Tasks and timers:
 | ||||
| 
 | ||||
| Task          Core  Prio  Purpose | ||||
| Task          	Core  Prio  Purpose | ||||
| ------------------------------------------------------------------------------- | ||||
| ledloop       0     3     blinks LEDs | ||||
| spiloop       0     2     reads/writes data on spi interface | ||||
| IDLE          0     0     ESP32 arduino scheduler -> runs wifi sniffer | ||||
| ledloop*      	0     3     blinks LEDs | ||||
| spiloop#      	0     2     reads/writes data on spi interface | ||||
| lmictask*     	1     2     MCCI LMiC LORAWAN stack | ||||
| clockloop#    	1     4     generates realtime telegrams for external clock | ||||
| mqttloop#     	1     2     reads/writes data on ETH interface | ||||
| timesync_proc#	1     3     processes realtime time sync requests | ||||
| irqhandler#   	1     2     cyclic tasks (i.e. displayrefresh) triggered by | ||||
| timers gpsloop*      	1     1     reads data from GPS via serial or i2c | ||||
| lorasendtask# 	1     1     feeds data from lora sendqueue to lmcic | ||||
| rmcd_process# 	1     1     Remote command interpreter loop | ||||
| 
 | ||||
| lmictask      1     2     MCCI LMiC LORAWAN stack | ||||
| clockloop     1     4     generates realtime telegrams for external clock | ||||
| mqttloop      1     2     reads/writes data on ETH interface | ||||
| timesync_proc 1     3     processes realtime time sync requests | ||||
| irqhandler    1     2     cyclic tasks (i.e. displayrefresh) triggered by timers | ||||
| gpsloop       1     1     reads data from GPS via serial or i2c | ||||
| lorasendtask  1     1     feeds data from lora sendqueue to lmcic | ||||
| rmcd_process  1     1     Remote command interpreter loop | ||||
| IDLE          1     0     ESP32 arduino scheduler -> runs wifi channel rotator | ||||
| * spinning task | ||||
| # blocked/waiting task | ||||
| 
 | ||||
| Low priority numbers denote low priority tasks. | ||||
| 
 | ||||
| NOTE: Changing any timings will have impact on time accuracy of whole code. | ||||
| So don't do it if you do not own a digital oscilloscope. | ||||
| ------------------------------------------------------------------------------- | ||||
| 
 | ||||
| // ESP32 hardware timers
 | ||||
| ------------------------------------------------------------------------------- | ||||
| @ -126,7 +124,7 @@ void setup() { | ||||
|   snprintf(clientId, 20, "paxcounter_%08x", hashedmac); | ||||
|   ESP_LOGI(TAG, "Starting %s v%s (runmode=%d / restarts=%d)", clientId, | ||||
|            PROGVERSION, RTC_runmode, RTC_restarts); | ||||
|   ESP_LOGI(TAG, "code build date: %d", _COMPILETIME); | ||||
|   ESP_LOGI(TAG, "code build date: %d", compileTime()); | ||||
| 
 | ||||
|   // print chip information on startup if in verbose mode after coldstart
 | ||||
| #if (VERBOSE) | ||||
| @ -494,8 +492,7 @@ void setup() { | ||||
|   cyclicTimer.attach(HOMECYCLE, setCyclicIRQ); | ||||
| 
 | ||||
| // only if we have a timesource we do timesync
 | ||||
| #if ((TIME_SYNC_LORAWAN) || (TIME_SYNC_LORASERVER) || (HAS_GPS) ||             \ | ||||
|      defined HAS_RTC) | ||||
| #if ((TIME_SYNC_LORAWAN) || (TIME_SYNC_LORASERVER) || (HAS_GPS) || (HAS_RTC)) | ||||
| 
 | ||||
| #if (defined HAS_IF482 || defined HAS_DCF77) | ||||
|   ESP_LOGI(TAG, "Starting Clock Controller..."); | ||||
|  | ||||
| @ -25,14 +25,14 @@ uint8_t rtc_init(void) { | ||||
|     } | ||||
| 
 | ||||
| #if (TIME_SYNC_COMPILEDATE) | ||||
|     // initialize a blank RTC without battery backup with compiled time
 | ||||
|     // initialize a blank RTC without battery backup with build time
 | ||||
|     RtcDateTime tt = Rtc.GetDateTime(); | ||||
|     time_t t = tt.Epoch32Time(); // sec2000 -> epoch
 | ||||
| 
 | ||||
|     if (!Rtc.IsDateTimeValid() || !timeIsValid(t)) { | ||||
|       ESP_LOGW(TAG, "RTC has no recent time, setting to compiled time"); | ||||
|       Rtc.SetDateTime( | ||||
|           RtcDateTime(_COMPILETIME - SECS_YR_2000)); // epoch -> sec2000
 | ||||
|       ESP_LOGW(TAG, "RTC has no recent time, setting to compiletime"); | ||||
|       Rtc.SetDateTime(RtcDateTime(mkgmtime(compileTime()) - | ||||
|                                   SECS_YR_2000)); // epoch -> sec2000
 | ||||
|     } | ||||
| #endif | ||||
| 
 | ||||
| @ -62,15 +62,23 @@ uint8_t set_rtctime(time_t t) { // t is sec epoch time | ||||
|   } | ||||
| } // set_rtctime()
 | ||||
| 
 | ||||
| time_t get_rtctime(void) { | ||||
| time_t get_rtctime(uint16_t *msec) { | ||||
|   time_t t = 0; | ||||
|   *msec = 0; | ||||
|   if (I2C_MUTEX_LOCK()) { | ||||
|     if (Rtc.IsDateTimeValid() && Rtc.GetIsRunning()) { | ||||
|       RtcDateTime tt = Rtc.GetDateTime(); | ||||
|       t = tt.Epoch32Time(); // sec2000 -> epoch
 | ||||
|     } | ||||
|     I2C_MUTEX_UNLOCK(); | ||||
|     return timeIsValid(t); | ||||
| #ifdef RTC_INT | ||||
|     // adjust time to top of next second by waiting TimePulseTick to flip
 | ||||
|     bool lastTick = TimePulseTick; | ||||
|     while (TimePulseTick == lastTick) { | ||||
|     }; | ||||
|     t++; | ||||
| #endif | ||||
|     return t; | ||||
|   } else { | ||||
|     ESP_LOGE(TAG, "RTC get time failure"); | ||||
|     return 0; // failure
 | ||||
|  | ||||
| @ -21,7 +21,6 @@ const char timeSetSymbols[] = {'G', 'R', 'L', '*', '?'}; | ||||
| 
 | ||||
| bool volatile TimePulseTick = false; | ||||
| timesource_t timeSource = _unsynced; | ||||
| time_t _COMPILETIME = compileTime(__DATE__); | ||||
| TaskHandle_t ClockTask = NULL; | ||||
| hw_timer_t *ppsIRQ = NULL; | ||||
| 
 | ||||
| @ -40,8 +39,7 @@ Ticker timesyncer; | ||||
| void setTimeSyncIRQ() { xTaskNotify(irqHandlerTask, TIMESYNC_IRQ, eSetBits); } | ||||
| 
 | ||||
| void calibrateTime(void) { | ||||
|   ESP_LOGD(TAG, "[%0.3f] calibrateTime, timeSource == %d", _seconds(), | ||||
|            timeSource); | ||||
| 
 | ||||
|   time_t t = 0; | ||||
|   uint16_t t_msec = 0; | ||||
| 
 | ||||
| @ -57,7 +55,7 @@ void calibrateTime(void) { | ||||
| 
 | ||||
| // has RTC -> fallback to RTC time
 | ||||
| #ifdef HAS_RTC | ||||
|     t = get_rtctime(); | ||||
|     t = get_rtctime(&t_msec); | ||||
|     // set time from RTC - method will check if time is valid
 | ||||
|     setMyTime((uint32_t)t, t_msec, _rtc); | ||||
| #endif | ||||
| @ -101,26 +99,27 @@ void IRAM_ATTR setMyTime(uint32_t t_sec, uint16_t t_msec, | ||||
|       vTaskDelay(pdMS_TO_TICKS(1000 - t_msec % 1000)); | ||||
|     } | ||||
| 
 | ||||
|     // from here on we are on top of next second
 | ||||
| 
 | ||||
|     tv.tv_sec = time_to_set; | ||||
|     tv.tv_usec = 0; | ||||
|     sntp_sync_time(&tv); | ||||
| 
 | ||||
|     ESP_LOGI(TAG, "[%0.3f] UTC time: %d.000 sec", _seconds(), time_to_set); | ||||
| 
 | ||||
|     // if we have a software pps timer, shift it to top of second
 | ||||
|     if (ppsIRQ != NULL) { | ||||
| 
 | ||||
|       timerWrite(ppsIRQ, 0); // reset pps timer
 | ||||
|       CLOCKIRQ();            // fire clock pps, this advances time 1 sec
 | ||||
|     } | ||||
| 
 | ||||
| // if we have got an external timesource, set RTC time and shift RTC_INT pulse
 | ||||
| // to top of second
 | ||||
|     // if we have a precise time timesource, set RTC time and shift RTC_INT
 | ||||
|     // pulse to top of second
 | ||||
| #ifdef HAS_RTC | ||||
|     if ((mytimesource == _gps) || (mytimesource == _lora)) | ||||
|       set_rtctime(time_to_set); | ||||
| #endif | ||||
| 
 | ||||
|     // if we have a software pps timer, shift it to top of second
 | ||||
|     if (ppsIRQ != NULL) { | ||||
|       timerWrite(ppsIRQ, 0); // reset pps timer
 | ||||
|       CLOCKIRQ();            // fire clock pps to advance wall clock by 1 sec
 | ||||
|     } | ||||
| 
 | ||||
|     timeSource = mytimesource; // set global variable
 | ||||
| 
 | ||||
|     timesyncer.attach(TIME_SYNC_INTERVAL * 60, setTimeSyncIRQ); | ||||
| @ -132,7 +131,7 @@ void IRAM_ATTR setMyTime(uint32_t t_sec, uint16_t t_msec, | ||||
|              "[%0.3f] Failed to synchronise time from source %c | unix sec " | ||||
|              "obtained from source: %d | unix sec at program compilation: %d", | ||||
|              _seconds(), timeSetSymbols[mytimesource], time_to_set, | ||||
|              _COMPILETIME); | ||||
|              compileTime()); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| @ -140,7 +139,7 @@ void IRAM_ATTR setMyTime(uint32_t t_sec, uint16_t t_msec, | ||||
| uint8_t timepulse_init() { | ||||
| 
 | ||||
|   // set esp-idf API sntp sync mode
 | ||||
|   //sntp_init();
 | ||||
|   // sntp_init();
 | ||||
|   sntp_set_sync_mode(SNTP_SYNC_MODE_IMMED); | ||||
| 
 | ||||
| // use time pulse from GPS as time base with fixed 1Hz frequency
 | ||||
| @ -224,7 +223,9 @@ void IRAM_ATTR CLOCKIRQ(void) { | ||||
| // helper function to check plausibility of a given epoch time
 | ||||
| bool timeIsValid(time_t const t) { | ||||
|   // is t a time in the past? we use compile time to guess
 | ||||
|   return (t > _COMPILETIME); | ||||
|   // compile time is some local time, but we do not know it's time zone
 | ||||
|   // thus, we go 1 full day back to be sure to catch a time in the past
 | ||||
|   return (t > (compileTime() - 86400)); | ||||
| } | ||||
| 
 | ||||
| // helper function to calculate serial transmit time
 | ||||
| @ -283,7 +284,7 @@ void clock_loop(void *taskparameter) { // ClockTask | ||||
|     // set calendar time for next second of clock output
 | ||||
|     tt = (time_t)(current_time + 1); | ||||
|     localtime_r(&tt, &t); | ||||
|     mktime(&t); | ||||
|     tt = mktime(&t); | ||||
| 
 | ||||
| #if defined HAS_IF482 | ||||
| 
 | ||||
| @ -293,13 +294,13 @@ void clock_loop(void *taskparameter) { // ClockTask | ||||
|     if (xTaskNotifyWait(0x00, ULONG_MAX, ¤t_time, txDelay) == pdTRUE) { | ||||
|       tt = (time_t)(current_time + 1); | ||||
|       localtime_r(&tt, &t); | ||||
|       mktime(&t); | ||||
|       tt = mktime(&t); | ||||
|     } | ||||
| 
 | ||||
|     // send IF482 telegram
 | ||||
|     IF482.print(IF482_Frame(t)); // note: telegram is for *next* second
 | ||||
|     IF482.print(IF482_Frame(tt)); // note: telegram is for *next* second
 | ||||
| 
 | ||||
|     ESP_LOGD(TAG, "[%0.3f] IF482: %s", _seconds(), IF482_Frame(t)); | ||||
|     ESP_LOGD(TAG, "[%0.3f] IF482: %s", _seconds(), IF482_Frame(tt).c_str()); | ||||
| 
 | ||||
| #elif defined HAS_DCF77 | ||||
| 
 | ||||
| @ -340,7 +341,7 @@ void clock_loop(void *taskparameter) { // ClockTask | ||||
| } // clock_loop()
 | ||||
| 
 | ||||
| // we use compile date to create a time_t reference "in the past"
 | ||||
| time_t compileTime(const String compile_date) { | ||||
| time_t compileTime(void) { | ||||
| 
 | ||||
|   char s_month[5]; | ||||
|   int year; | ||||
| @ -353,10 +354,11 @@ time_t compileTime(const String compile_date) { | ||||
|   if (secs == -1) { | ||||
| 
 | ||||
|     // determine date
 | ||||
|     // we go one day back to bypass unknown timezone of local time
 | ||||
|     sscanf(compile_date.c_str(), "%s %d %d", s_month, &t.tm_mday - 1, &year); | ||||
|     sscanf(__DATE__, "%s %d %d", s_month, &t.tm_mday, &year); | ||||
|     t.tm_mon = (strstr(month_names, s_month) - month_names) / 3; | ||||
|     t.tm_year = year - 1900; | ||||
|     // determine time
 | ||||
|     sscanf(__TIME__, "%d:%d:%d", &t.tm_hour, &t.tm_min, &t.tm_sec); | ||||
| 
 | ||||
|     // convert to secs local time
 | ||||
|     secs = mktime(&t); | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user