timeserver(experimental)
This commit is contained in:
		
							parent
							
								
									b742f888fd
								
							
						
					
					
						commit
						f9f5f499ff
					
				| @ -366,6 +366,10 @@ Note: all settings are stored in NVRAM and will be reloaded when device starts. | ||||
| 
 | ||||
| 	Device answers with it's local time/date (UTC Unix epoch) on Port 9. | ||||
| 
 | ||||
| 0x87 set time/date | ||||
| 
 | ||||
| 	Device synchronizes it's time/date by calling the preconfigured time source. | ||||
| 
 | ||||
| 	 | ||||
| # License | ||||
| 
 | ||||
|  | ||||
| @ -6,13 +6,10 @@ | ||||
| #include "timekeeper.h" | ||||
| 
 | ||||
| #define TIME_SYNC_SAMPLES 1    // number of time requests for averaging
 | ||||
| #define TIME_SYNC_CYCLE 60     // seconds between two time requests
 | ||||
| #define TIME_SYNC_CYCLE 30     // seconds between two time requests
 | ||||
| #define TIME_SYNC_TIMEOUT 120  // timeout seconds waiting for timeserver answer
 | ||||
| #define TIME_SYNC_TRIGGER 1.0f // time deviation threshold triggering time sync
 | ||||
| #define TIME_SYNC_START_OPCODE 0x90 // start time sync (server -> node)
 | ||||
| #define TIME_SYNC_STEP_OPCODE 0x91 // step to next sync request (node -> server)
 | ||||
| #define TIME_SYNC_REQ_OPCODE 0x92 // node request at timeserver (node -> server)
 | ||||
| #define TIME_SYNC_ANS_OPCODE 0x93 // timeserver answer to node (server -> node)
 | ||||
| #define TIME_SYNC_FRAME_LENGTH 0x06 // timeserver answer frame length
 | ||||
| 
 | ||||
| typedef struct { | ||||
|   uint32_t seconds; | ||||
| @ -24,9 +21,8 @@ extern uint8_t time_sync_seqNo; | ||||
| extern bool time_sync_pending; | ||||
| 
 | ||||
| void send_Servertime_req(void); | ||||
| void recv_Servertime_ans(uint8_t val[]); | ||||
| void recv_Servertime_ans(uint8_t buf[], uint8_t buf_len); | ||||
| void process_Servertime_sync_req(void *taskparameter); | ||||
| void force_Servertime_sync(uint8_t val[]); | ||||
| void store_time_sync_req(time_t secs, uint32_t micros); | ||||
| 
 | ||||
| #endif | ||||
| @ -66,14 +66,10 @@ function Decoder(bytes, port) { | ||||
|     if (port === 9) { | ||||
|         // timesync request
 | ||||
|         if (bytes.length === 1) { | ||||
|         decoded.timesync_opcode = bytes[0]; | ||||
|         } | ||||
|         if (bytes.length === 2) { | ||||
|         decoded.timesync_opcode = bytes[0]; | ||||
|         decoded.timesync_seqno = bytes[1]; | ||||
|         decoded.timesync_seqno = bytes[0]; | ||||
|         } | ||||
|         return decoded; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -67,6 +67,12 @@ function Decoder(bytes, port) { | ||||
|     decoded.battery = (bytes[i++] << 8) | bytes[i++];} | ||||
|   } | ||||
| 
 | ||||
|   if (port === 9) { | ||||
|     if (bytes.length === 1) { | ||||
|     decoded.timesync_seqno = bytes[0]; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   return decoded; | ||||
| 
 | ||||
| } | ||||
|  | ||||
| @ -228,11 +228,10 @@ void onEvent(ev_t ev) { | ||||
|   case EV_TXCOMPLETE: | ||||
| 
 | ||||
| #if (TIME_SYNC_TIMESERVER) | ||||
|     // if last packet sent was a timesync request was sent, store TX timestamp
 | ||||
|     if ((LMIC.pendTxPort == TIMEPORT) && | ||||
|         (LMIC.pendTxData[0] == TIME_SYNC_REQ_OPCODE)) | ||||
|     // if last packet sent was a timesync request, store TX timestamp
 | ||||
|     if (LMIC.pendTxPort == TIMEPORT) | ||||
|       store_time_sync_req(now(now_micros), now_micros); | ||||
|       // maybe using more precise osticks2ms(LMIC.txend) here?
 | ||||
|       // maybe use more precise osticks2ms(LMIC.txend) here?
 | ||||
| #endif | ||||
| 
 | ||||
|     strcpy_P(buff, (LMIC.txrxFlags & TXRX_ACK) ? PSTR("RECEIVED_ACK") | ||||
| @ -244,7 +243,13 @@ void onEvent(ev_t ev) { | ||||
|                LMIC.dataLen, LMIC.rssi, LMIC.snr / 4); | ||||
|       sprintf(display_line6, "RSSI -%d SNR %d", LMIC.rssi, LMIC.snr / 4); | ||||
| 
 | ||||
|       // check if command is received on command port, then call interpreter
 | ||||
|       // check if this is a timesync answer, then call timesync processor
 | ||||
| #if (TIME_SYNC_TIMESERVER) | ||||
|       if ((LMIC.txrxFlags & TXRX_PORT) && | ||||
|           (LMIC.frame[LMIC.dataBeg - 1] == TIMEPORT)) | ||||
|         recv_Servertime_ans(LMIC.frame + LMIC.dataBeg, LMIC.dataLen); | ||||
| #endif | ||||
|       // check if this an opcode, then call rcommand interpreter
 | ||||
|       if ((LMIC.txrxFlags & TXRX_PORT) && | ||||
|           (LMIC.frame[LMIC.dataBeg - 1] == RCMDPORT)) | ||||
|         rcommand(LMIC.frame + LMIC.dataBeg, LMIC.dataLen); | ||||
| @ -422,11 +427,8 @@ void lora_enqueuedata(MessageBuffer_t *message, sendprio_t prio) { | ||||
|     ret = xQueueSendToBack(LoraSendQueue, (void *)message, (TickType_t)0); | ||||
|     break; | ||||
|   } | ||||
|   if (ret == pdTRUE) { | ||||
|     ESP_LOGI(TAG, "%d bytes enqueued for LORA interface", message->MessageSize); | ||||
|   } else { | ||||
|   if (ret != pdTRUE) | ||||
|     ESP_LOGW(TAG, "LORA sendqueue is full"); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void lora_queuereset(void) { xQueueReset(LoraSendQueue); } | ||||
|  | ||||
| @ -38,7 +38,7 @@ void set_reset(uint8_t val[]) { | ||||
|     break; | ||||
|   case 9: // reset and ask for software update via Wifi OTA
 | ||||
|     ESP_LOGI(TAG, "Remote command: software update via Wifi"); | ||||
| #if(USE_OTA) | ||||
| #if (USE_OTA) | ||||
|     sprintf(display_line6, "Software update"); | ||||
|     cfg.runmode = 1; | ||||
| #else | ||||
| @ -278,40 +278,35 @@ void get_time(uint8_t val[]) { | ||||
|   SendPayload(TIMEPORT, prio_high); | ||||
| }; | ||||
| 
 | ||||
| void set_time(uint8_t val[]) { | ||||
|   ESP_LOGI(TAG, "Timesync requested by timeserver"); | ||||
|   timeSync(); | ||||
| }; | ||||
| 
 | ||||
| void set_flush(uint8_t val[]) { | ||||
|   ESP_LOGI(TAG, "Remote command: flush"); | ||||
|   // does nothing
 | ||||
|   // used to open receive window on LoRaWAN class a nodes
 | ||||
| }; | ||||
| 
 | ||||
| // assign previously defined functions to set of numeric remote commands
 | ||||
| // format: opcode, function, #bytes params,
 | ||||
| // flag (true = do make settings persistent / false = don't)
 | ||||
| //
 | ||||
| cmd_t table[] = {{0x01, set_rssi, 1, true}, | ||||
|                  {0x02, set_countmode, 1, true}, | ||||
|                  {0x03, set_gps, 1, true}, | ||||
|                  {0x04, set_display, 1, true}, | ||||
|                  {0x05, set_lorasf, 1, true}, | ||||
|                  {0x06, set_lorapower, 1, true}, | ||||
|                  {0x07, set_loraadr, 1, true}, | ||||
|                  {0x08, set_screensaver, 1, true}, | ||||
|                  {0x09, set_reset, 1, true}, | ||||
|                  {0x0a, set_sendcycle, 1, true}, | ||||
|                  {0x0b, set_wifichancycle, 1, true}, | ||||
|                  {0x0c, set_blescantime, 1, true}, | ||||
|                  {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} | ||||
| #if(TIME_SYNC_TIMESERVER) | ||||
|                  , | ||||
|                  {TIME_SYNC_ANS_OPCODE, recv_Servertime_ans, 6, false}, | ||||
|                  {TIME_SYNC_START_OPCODE, force_Servertime_sync, 0, false} | ||||
| #endif | ||||
| }; | ||||
| cmd_t table[] = { | ||||
|     {0x01, set_rssi, 1, true},          {0x02, set_countmode, 1, true}, | ||||
|     {0x03, set_gps, 1, true},           {0x04, set_display, 1, true}, | ||||
|     {0x05, set_lorasf, 1, true},        {0x06, set_lorapower, 1, true}, | ||||
|     {0x07, set_loraadr, 1, true},       {0x08, set_screensaver, 1, true}, | ||||
|     {0x09, set_reset, 1, true},         {0x0a, set_sendcycle, 1, true}, | ||||
|     {0x0b, set_wifichancycle, 1, true}, {0x0c, set_blescantime, 1, true}, | ||||
|     {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}, | ||||
|     {0x87, set_time, 0, false},         {0x99, set_flush, 0, false}}; | ||||
| 
 | ||||
| const uint8_t cmdtablesize = | ||||
|     sizeof(table) / sizeof(table[0]); // number of commands in command table
 | ||||
|  | ||||
| @ -160,12 +160,8 @@ void spi_enqueuedata(MessageBuffer_t *message, sendprio_t prio) { | ||||
|     ret = xQueueSendToBack(SPISendQueue, (void *)message, (TickType_t)0); | ||||
|     break; | ||||
|   } | ||||
|   if (ret == pdTRUE) { | ||||
|     ESP_LOGI(TAG, "%d byte(s) enqueued for SPI interface", | ||||
|              message->MessageSize); | ||||
|   } else { | ||||
|   if (ret != pdTRUE) | ||||
|     ESP_LOGW(TAG, "SPI sendqueue is full"); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void spi_queuereset(void) { xQueueReset(SPISendQueue); } | ||||
|  | ||||
| @ -1,5 +1,13 @@ | ||||
| #include "timekeeper.h" | ||||
| 
 | ||||
| #ifndef HAS_LORA | ||||
| #if (TIME_SYNC_TIMESERVER) | ||||
| #error TIME_SYNC_TIMESERVER defined, but device has no LORA configured | ||||
| #elif (TIME_SYNC_LORAWAN) | ||||
| #error TIME_SYNC_LORAWAN defined, but device has no LORA configured | ||||
| #endif | ||||
| #endif | ||||
| 
 | ||||
| // Local logging tag
 | ||||
| static const char TAG[] = __FILE__; | ||||
| 
 | ||||
| @ -35,11 +43,11 @@ time_t timeProvider(void) { | ||||
|   } | ||||
| #endif | ||||
| 
 | ||||
| // kick off asychronous DB timesync if we have
 | ||||
| #if(TIME_SYNC_TIMESERVER) | ||||
| // kick off asychronous Lora timeserver timesync if we have
 | ||||
| #if (TIME_SYNC_TIMESERVER) | ||||
|   send_Servertime_req(); | ||||
| // kick off asychronous lora sync if we have
 | ||||
| #elif defined HAS_LORA && (TIME_SYNC_LORAWAN) | ||||
| // kick off asychronous lora network sync if we have
 | ||||
| #elif (TIME_SYNC_LORAWAN) | ||||
|   LMIC_requestNetworkTime(user_request_network_time_callback, &userUTCTime); | ||||
| #endif | ||||
| 
 | ||||
|  | ||||
| @ -28,7 +28,7 @@ void send_Servertime_req() { | ||||
| 
 | ||||
|   // if a timesync handshake is pending then exit
 | ||||
|   if (time_sync_pending) { | ||||
|     ESP_LOGI(TAG, "Timeserver sync request already running"); | ||||
|     ESP_LOGI(TAG, "Timeserver sync request already pending"); | ||||
|     return; | ||||
|   } else { | ||||
|     ESP_LOGI(TAG, "Timeserver sync request started"); | ||||
| @ -54,20 +54,20 @@ void send_Servertime_req() { | ||||
| } | ||||
| 
 | ||||
| // process timeserver timestamp response, called from rcommand.cpp
 | ||||
| void recv_Servertime_ans(uint8_t val[]) { | ||||
| void recv_Servertime_ans(uint8_t buf[], uint8_t buf_len) { | ||||
| 
 | ||||
|   // if no timesync handshake is pending then exit
 | ||||
|   if (!time_sync_pending) | ||||
|   // if no timesync handshake is pending or invalid buffer then exit
 | ||||
|   if ((!time_sync_pending) || (buf_len != TIME_SYNC_FRAME_LENGTH)) | ||||
|     return; | ||||
| 
 | ||||
|   uint8_t seq_no = val[0]; | ||||
|   uint8_t seq_no = buf[0]; | ||||
|   uint32_t timestamp_sec = 0, timestamp_ms = 0; | ||||
| 
 | ||||
|   for (int i = 1; i <= 4; i++) { | ||||
|     timestamp_sec = (timestamp_sec << 8) | val[i]; | ||||
|     timestamp_sec = (timestamp_sec << 8) | buf[i]; | ||||
|     time_sync_answers[seq_no].seconds = timestamp_sec; | ||||
|   } | ||||
|   timestamp_ms = 4 * val[5]; | ||||
|   timestamp_ms = 4 * buf[5]; | ||||
|   time_sync_answers[seq_no].fractions = timestamp_ms; | ||||
| 
 | ||||
|   ESP_LOGD(TAG, "Timeserver answer #%d received: timestamp=%d.%03d", seq_no, | ||||
| @ -93,14 +93,13 @@ void process_Servertime_sync_req(void *taskparameter) { | ||||
| 
 | ||||
|     // send sync request to server
 | ||||
|     payload.reset(); | ||||
|     payload.addWord(TIME_SYNC_REQ_OPCODE | time_sync_seqNo << 8); | ||||
|     payload.addByte(time_sync_seqNo); | ||||
|     SendPayload(TIMEPORT, prio_high); | ||||
|     ESP_LOGD(TAG, "Timeserver request #%d enqeued", time_sync_seqNo); | ||||
| 
 | ||||
|     // send dummy packet to trigger receive answer
 | ||||
|     payload.reset(); | ||||
|     payload.addByte(TIME_SYNC_STEP_OPCODE); | ||||
|     SendPayload(TIMEPORT, prio_low); // open receive slot for answer
 | ||||
|     payload.addByte(0x99);           // flush
 | ||||
|     SendPayload(RCMDPORT, prio_low); // open receive slot for answer
 | ||||
| 
 | ||||
|     // process answer
 | ||||
|     if ((xTaskNotifyWait(0x00, ULONG_MAX, &seq_no, | ||||
| @ -124,9 +123,8 @@ void process_Servertime_sync_req(void *taskparameter) { | ||||
|   time_offset = | ||||
|       (time_diff_sec + time_diff_ms / 1000.0f) / (TIME_SYNC_SAMPLES * 1.0f); | ||||
| 
 | ||||
|   // ESP_LOGD(TAG, "Timesync time offset=%.3f", time_offset);
 | ||||
|   ESP_LOGD(TAG, "Timesync time offset=%d.%03d", time_diff_sec / TIME_SYNC_SAMPLES, | ||||
|            time_diff_ms / TIME_SYNC_SAMPLES); | ||||
|   ESP_LOGD(TAG, "Timesync time offset=%d.%03d", | ||||
|            time_diff_sec / TIME_SYNC_SAMPLES, time_diff_ms / TIME_SYNC_SAMPLES); | ||||
| 
 | ||||
|   if (time_offset >= TIME_SYNC_TRIGGER) { | ||||
| 
 | ||||
| @ -172,9 +170,4 @@ void store_time_sync_req(time_t secs, uint32_t micros) { | ||||
|            time_sync_messages[k].seconds, time_sync_messages[k].fractions); | ||||
| } | ||||
| 
 | ||||
| void force_Servertime_sync(uint8_t val[]) { | ||||
|   ESP_LOGI(TAG, "Timesync requested by timeserver"); | ||||
|   timeSync(); | ||||
| }; | ||||
| 
 | ||||
| #endif | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user