New 5-byte time sync protocol

This commit is contained in:
Verkehrsrot 2019-04-06 16:43:12 +02:00
parent 11f184e67a
commit dd661254b5
5 changed files with 38 additions and 410 deletions

View File

@ -7,11 +7,11 @@
#include "timekeeper.h"
//#define TIME_SYNC_TRIGGER 100 // threshold for time sync [milliseconds]
#define TIME_SYNC_FRAME_LENGTH 0x06 // timeserver answer frame length [bytes]
#define TIME_SYNC_FRAME_LENGTH 0x05 // timeserver answer frame length [bytes]
#define TIME_SYNC_FIXUP 6 // calibration to fixup processing time [milliseconds]
void send_timesync_req(void);
int recv_timesync_ans(uint8_t buf[], uint8_t buf_len);
int recv_timesync_ans(uint8_t seq_no, uint8_t buf[], uint8_t buf_len);
void process_timesync_req(void *taskparameter);
void store_time_sync_req(uint32_t t_millisec);
void IRAM_ATTR setMyTime(uint32_t t_sec, uint16_t t_msec);

File diff suppressed because one or more lines are too long

View File

@ -256,16 +256,24 @@ void onEvent(ev_t ev) {
sprintf(display_line6, "RSSI %d SNR %d", LMIC.rssi, LMIC.snr / 4);
if (LMIC.txrxFlags & TXRX_PORT) { // FPort -> use to switch
switch (LMIC.frame[LMIC.dataBeg - 1]) {
#if (TIME_SYNC_LORASERVER)
case TIMEPORT: // timesync answer -> call timesync processor
recv_timesync_ans(LMIC.frame + LMIC.dataBeg, LMIC.dataLen);
break;
#endif
case RCMDPORT: // opcode -> call rcommand interpreter
rcommand(LMIC.frame + LMIC.dataBeg, LMIC.dataLen);
break;
default: // unknown port -> display info
#if (TIME_SYNC_LORASERVER)
// timesync answer -> call timesync processor
if ((LMIC.frame[LMIC.dataBeg - 1] >= TIMEANSWERPORT_MIN) &&
(LMIC.frame[LMIC.dataBeg - 1] <= TIMEANSWERPORT_MAX)) {
recv_timesync_ans(LMIC.frame[LMIC.dataBeg - 1],
LMIC.frame + LMIC.dataBeg, LMIC.dataLen);
break;
}
#endif
ESP_LOGI(TAG, "Received data on unsupported port #%d",
LMIC.frame[LMIC.dataBeg - 1]);
break;

View File

@ -91,7 +91,9 @@
#define BEACONPORT 6 // beacon alarms
#define BMEPORT 7 // BME680 sensor
#define BATTPORT 8 // battery voltage
#define TIMEPORT 9 // time
#define TIMEPORT 9 // time query
#define TIMEANSWERPORT_MIN 0xA0 // time answer, start of port range
#define TIMEANSWERPORT_MAX 0xDF // time answer, end of port range
#define SENSOR1PORT 10 // user sensor #1
#define SENSOR2PORT 11 // user sensor #2
#define SENSOR3PORT 12 // user sensor #3

View File

@ -20,7 +20,7 @@ static const char TAG[] = __FILE__;
TaskHandle_t timeSyncReqTask;
static uint8_t time_sync_seqNo = 0;
static uint8_t time_sync_seqNo = TIMEANSWERPORT_MIN;
static bool lora_time_sync_pending = false;
typedef std::chrono::system_clock myClock;
@ -75,9 +75,6 @@ void process_timesync_req(void *taskparameter) {
// enqueue timestamp samples in lora sendqueue
for (uint8_t i = 0; i < TIME_SYNC_SAMPLES; i++) {
// wrap around seqNo 0 .. 254
time_sync_seqNo = (time_sync_seqNo < 255) ? time_sync_seqNo + 1 : 0;
// send sync request to server
payload.reset();
payload.addByte(time_sync_seqNo);
@ -96,6 +93,11 @@ void process_timesync_req(void *taskparameter) {
time_offset_ms += time_point_cast<milliseconds>(time_sync_rx[k]) -
time_point_cast<milliseconds>(time_sync_tx[k]);
// wrap around seqNo keeping it in time port range
time_sync_seqNo = (time_sync_seqNo < TIMEANSWERPORT_MAX)
? time_sync_seqNo + 1
: TIMEANSWERPORT_MIN;
if (i < TIME_SYNC_SAMPLES - 1) {
// wait until next cycle
vTaskDelay(pdMS_TO_TICKS(TIME_SYNC_CYCLE * 1000));
@ -149,19 +151,21 @@ error:
// called from lorawan.cpp after time_sync_req was sent
void store_time_sync_req(uint32_t timestamp) {
uint8_t k = time_sync_seqNo % TIME_SYNC_SAMPLES;
if (lora_time_sync_pending) {
time_sync_tx[k] += milliseconds(timestamp);
uint8_t k = time_sync_seqNo % TIME_SYNC_SAMPLES;
time_sync_tx[k] += milliseconds(timestamp);
ESP_LOGD(TAG, "[%0.3f] Timesync request #%d sent at %d.%03d",
millis() / 1000.0, time_sync_seqNo, timestamp / 1000,
timestamp % 1000);
ESP_LOGD(TAG, "[%0.3f] Timesync request #%d sent at %d.%03d",
millis() / 1000.0, time_sync_seqNo, timestamp / 1000,
timestamp % 1000);
}
}
// process timeserver timestamp answer, called from lorawan.cpp
int recv_timesync_ans(uint8_t buf[], uint8_t buf_len) {
int recv_timesync_ans(uint8_t seq_no, uint8_t buf[], uint8_t buf_len) {
// if no timesync handshake is pending or spurious buffer then exit
// if no timesync handshake is pending then exit
if (!lora_time_sync_pending)
return 0; // failure
@ -178,18 +182,18 @@ int recv_timesync_ans(uint8_t buf[], uint8_t buf_len) {
else { // we received a probably valid time frame
uint8_t seq_no = buf[0], k = seq_no % TIME_SYNC_SAMPLES;
uint8_t k = seq_no % TIME_SYNC_SAMPLES;
uint16_t timestamp_msec; // convert 1/250th sec fractions to ms
uint32_t timestamp_sec;
// fetch timeserver time from 4 bytes containing the UTC seconds since
// unix epoch. Octet order is big endian. Casts are necessary, because buf
// is an array of single byte values, and they might overflow when shifted
timestamp_sec = ((uint32_t)buf[4]) | (((uint32_t)buf[3]) << 8) |
(((uint32_t)buf[2]) << 16) | (((uint32_t)buf[1]) << 24);
timestamp_sec = ((uint32_t)buf[3]) | (((uint32_t)buf[2]) << 8) |
(((uint32_t)buf[1]) << 16) | (((uint32_t)buf[0]) << 24);
// the 5th byte contains the fractional seconds in 2^-8 second steps
timestamp_msec = 4 * buf[5];
timestamp_msec = 4 * buf[4];
// construct the timepoint when message was seen on gateway
time_sync_rx[k] += seconds(timestamp_sec) + milliseconds(timestamp_msec);