From 729caa9e2c26b882297ac9e57cb67a5c7a85d725 Mon Sep 17 00:00:00 2001 From: Lee Leahy Date: Wed, 11 Jan 2023 13:40:06 -1000 Subject: [PATCH 01/11] Rename txSyncClockUsec to txSyncClocksUsec --- Firmware/LoRaSerial_Firmware/Commands.ino | 2 +- .../LoRaSerial_Firmware.ino | 2 +- Firmware/LoRaSerial_Firmware/Radio.ino | 4 +-- Firmware/LoRaSerial_Firmware/States.ino | 32 +++++++++---------- 4 files changed, 20 insertions(+), 20 deletions(-) diff --git a/Firmware/LoRaSerial_Firmware/Commands.ino b/Firmware/LoRaSerial_Firmware/Commands.ino index 2bd08c72..e0fd454b 100644 --- a/Firmware/LoRaSerial_Firmware/Commands.ino +++ b/Firmware/LoRaSerial_Firmware/Commands.ino @@ -417,7 +417,7 @@ bool commandAT(const char * commandString) systemPrint(txHeartbeatUsec); systemPrintln(" uSec"); systemPrint(" SYNC_CLOCKS Time: "); - systemPrint(txSyncClockUsec); + systemPrint(txSyncClocksUsec); systemPrintln(" uSec"); systemPrint(" Uptime: "); deltaMillis = millis(); diff --git a/Firmware/LoRaSerial_Firmware/LoRaSerial_Firmware.ino b/Firmware/LoRaSerial_Firmware/LoRaSerial_Firmware.ino index b3ab6c2e..d1edb583 100644 --- a/Firmware/LoRaSerial_Firmware/LoRaSerial_Firmware.ino +++ b/Firmware/LoRaSerial_Firmware/LoRaSerial_Firmware.ino @@ -590,7 +590,7 @@ uint32_t txSetChannelTimerMicros; //Timestamp when millis is read in TX routine uint32_t transactionCompleteMicros; //Timestamp at the beginning of the transactionCompleteIsr routine uint32_t txDataAckUsec; //Time in microseconds to transmit the DATA_ACK frame uint32_t txHeartbeatUsec; //Time in microseconds to transmit the HEARTBEAT frame -uint32_t txSyncClockUsec; //Time in microseconds to transmit the SYNC_CLOCKS frame +uint32_t txSyncClocksUsec; //Time in microseconds to transmit the SYNC_CLOCKS frame uint32_t txDatagramMicros; //Timestamp at the beginning of the transmitDatagram routine uint16_t maxFrameAirTime; //Air time of the maximum sized message unsigned long remoteSystemMillis; //Millis value contained in the received message diff --git a/Firmware/LoRaSerial_Firmware/Radio.ino b/Firmware/LoRaSerial_Firmware/Radio.ino index 541e4cce..9be983c8 100644 --- a/Firmware/LoRaSerial_Firmware/Radio.ino +++ b/Firmware/LoRaSerial_Firmware/Radio.ino @@ -892,8 +892,8 @@ bool xmitDatagramP2PSyncClocks() memcpy(endOfTxData, &txHeartbeatUsec, sizeof(txHeartbeatUsec)); endOfTxData += sizeof(txHeartbeatUsec); - memcpy(endOfTxData, &txSyncClockUsec, sizeof(txSyncClockUsec)); - endOfTxData += sizeof(txSyncClockUsec); + memcpy(endOfTxData, &txSyncClocksUsec, sizeof(txSyncClocksUsec)); + endOfTxData += sizeof(txSyncClocksUsec); memcpy(endOfTxData, &txDataAckUsec, sizeof(txDataAckUsec)); endOfTxData += sizeof(txDataAckUsec); diff --git a/Firmware/LoRaSerial_Firmware/States.ino b/Firmware/LoRaSerial_Firmware/States.ino index 8a160faf..758c0aa6 100644 --- a/Firmware/LoRaSerial_Firmware/States.ino +++ b/Firmware/LoRaSerial_Firmware/States.ino @@ -448,7 +448,7 @@ void updateRadioState() } //Compute the receive time - COMPUTE_RX_TIME(rxData + 1, 1, txSyncClockUsec); + COMPUTE_RX_TIME(rxData + 1, 1, txSyncClocksUsec); //Hop to the next channel hopChannel(); @@ -498,13 +498,13 @@ void updateRadioState() if (transactionComplete) { //Compute the SYNC_CLOCKS frame transmit frame - if ((!txSyncClockUsec) && (txControl.datagramType == DATAGRAM_SYNC_CLOCKS)) + if ((!txSyncClocksUsec) && (txControl.datagramType == DATAGRAM_SYNC_CLOCKS)) { - txSyncClockUsec = transactionCompleteMicros - txSetChannelTimerMicros; + txSyncClocksUsec = transactionCompleteMicros - txSetChannelTimerMicros; if (settings.debugSync) { - systemPrint("txSyncClockUsec: "); - systemPrintln(txSyncClockUsec); + systemPrint("txSyncClocksUsec: "); + systemPrintln(txSyncClocksUsec); } } @@ -1174,28 +1174,28 @@ void updateRadioState() } //Get the SYNC_CLOCKS TX time - if (!txSyncClockUsec) + if (!txSyncClocksUsec) { - memcpy(&txSyncClockUsec, rxData + 1 + 4 + 4, sizeof(txSyncClockUsec)); + memcpy(&txSyncClocksUsec, rxData + 1 + 4 + 4, sizeof(txSyncClocksUsec)); if (settings.debugSync) { - systemPrint("txSyncClockUsec: "); - systemPrintln(txSyncClockUsec); + systemPrint("txSyncClocksUsec: "); + systemPrintln(txSyncClocksUsec); } } //Ignore this frame when the times are not filled in - if ((!txHeartbeatUsec) || (!txSyncClockUsec)) + if ((!txHeartbeatUsec) || (!txSyncClocksUsec)) triggerEvent(TRIGGER_RX_SYNC_CLOCKS); else { - COMPUTE_TIMESTAMP_OFFSET(rxData + 1, 0, txSyncClockUsec); + COMPUTE_TIMESTAMP_OFFSET(rxData + 1, 0, txSyncClocksUsec); //Server has responded to FIND_PARTNER with SYNC_CLOCKS //Start and adjust freq hop ISR based on remote's remaining clock startChannelTimer(); channelTimerStart -= settings.maxDwellTime; - syncChannelTimer((txSyncClockUsec + TX_TO_RX_USEC + micros() - transactionCompleteMicros) / 1000); + syncChannelTimer((txSyncClocksUsec + TX_TO_RX_USEC + micros() - transactionCompleteMicros) / 1000); triggerEvent(TRIGGER_RX_SYNC_CLOCKS); //Switch to the proper frequency for the channel @@ -1463,13 +1463,13 @@ void updateRadioState() } //Compute the SYNC_CLOCKS frame transmit frame - else if ((!txSyncClockUsec) && (txControl.datagramType == DATAGRAM_SYNC_CLOCKS)) + else if ((!txSyncClocksUsec) && (txControl.datagramType == DATAGRAM_SYNC_CLOCKS)) { - txSyncClockUsec = transactionCompleteMicros - txSetChannelTimerMicros; + txSyncClocksUsec = transactionCompleteMicros - txSetChannelTimerMicros; if (settings.debugSync) { - systemPrint("txSyncClockUsec: "); - systemPrintln(txSyncClockUsec); + systemPrint("txSyncClocksUsec: "); + systemPrintln(txSyncClocksUsec); } } From 18bf578736d07a2ff06da9504e2569cda66868d2 Mon Sep 17 00:00:00 2001 From: Lee Leahy Date: Wed, 11 Jan 2023 09:37:47 -1000 Subject: [PATCH 02/11] RX control: Add the ignoreFrame bit --- Firmware/LoRaSerial_Firmware/Radio.ino | 17 +++++++++++++++++ Firmware/LoRaSerial_Firmware/settings.h | 2 +- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/Firmware/LoRaSerial_Firmware/Radio.ino b/Firmware/LoRaSerial_Firmware/Radio.ino index 9be983c8..745d6098 100644 --- a/Firmware/LoRaSerial_Firmware/Radio.ino +++ b/Firmware/LoRaSerial_Firmware/Radio.ino @@ -1932,6 +1932,20 @@ PacketType rcvDatagram() return (DATAGRAM_BAD); } + //Ignore this frame is requested + if (rxControl.ignoreFrame) + { + if (settings.debugReceive || settings.debugDatagrams) + { + systemPrintTimestamp(); + systemPrint("RX: Ignore this "); + systemPrintln(datagramType); + outputSerialData(true); + } + badFrames++; + return (DATAGRAM_BAD); + } + //Display the CRC if (settings.enableCRC16 && settings.debugReceive) { @@ -2940,6 +2954,9 @@ void printControl(uint8_t value) else systemPrintln("0"); + if (control->ignoreFrame) + systemPrintln(" Ignore Frame"); + outputSerialData(true); if (timeToHop == true) //If the channelTimer has expired, move to next frequency diff --git a/Firmware/LoRaSerial_Firmware/settings.h b/Firmware/LoRaSerial_Firmware/settings.h index 33e7257a..34eb4ef4 100644 --- a/Firmware/LoRaSerial_Firmware/settings.h +++ b/Firmware/LoRaSerial_Firmware/settings.h @@ -325,7 +325,7 @@ typedef struct _CONTROL_U8 PacketType datagramType: 4; uint8_t ackNumber : 2; uint8_t requestYield : 1; - uint8_t filler : 1; + uint8_t ignoreFrame : 1; } CONTROL_U8; typedef bool (* VALIDATION_ROUTINE)(void * value, uint32_t valMin, uint32_t valMax); From 72ced00d78453220dd7eb404a23f63dce2c37917 Mon Sep 17 00:00:00 2001 From: Lee Leahy Date: Wed, 11 Jan 2023 10:06:08 -1000 Subject: [PATCH 03/11] Transmit ignore frames to get TX times for SYNC_CLOCKS, HEARTBEAT, ACK --- Firmware/LoRaSerial_Firmware/Commands.ino | 4 + .../LoRaSerial_Firmware.ino | 1 + Firmware/LoRaSerial_Firmware/Radio.ino | 79 +++++++++++++++++-- Firmware/LoRaSerial_Firmware/States.ino | 25 +++++- .../Virtual_Circuit_Protocol.h | 1 + 5 files changed, 103 insertions(+), 7 deletions(-) diff --git a/Firmware/LoRaSerial_Firmware/Commands.ino b/Firmware/LoRaSerial_Firmware/Commands.ino index e0fd454b..e50dde1a 100644 --- a/Firmware/LoRaSerial_Firmware/Commands.ino +++ b/Firmware/LoRaSerial_Firmware/Commands.ino @@ -413,6 +413,10 @@ bool commandAT(const char * commandString) systemPrintln(" Clock Synchronization"); systemPrint(" ACK Time: "); systemPrint(txDataAckUsec); + systemPrintln(" uSec"); + systemPrint(" FIND_PARTNER Time: "); + systemPrint(txFindPartnerUsec); + systemPrintln(" uSec"); systemPrint(" HEARTBEAT Time: "); systemPrint(txHeartbeatUsec); systemPrintln(" uSec"); diff --git a/Firmware/LoRaSerial_Firmware/LoRaSerial_Firmware.ino b/Firmware/LoRaSerial_Firmware/LoRaSerial_Firmware.ino index d1edb583..40a52a50 100644 --- a/Firmware/LoRaSerial_Firmware/LoRaSerial_Firmware.ino +++ b/Firmware/LoRaSerial_Firmware/LoRaSerial_Firmware.ino @@ -589,6 +589,7 @@ uint16_t txRxTimeMsec; uint32_t txSetChannelTimerMicros; //Timestamp when millis is read in TX routine to set channel timer value uint32_t transactionCompleteMicros; //Timestamp at the beginning of the transactionCompleteIsr routine uint32_t txDataAckUsec; //Time in microseconds to transmit the DATA_ACK frame +uint32_t txFindPartnerUsec; //Time in microseconds to transmit the FIND_PARTNER frame uint32_t txHeartbeatUsec; //Time in microseconds to transmit the HEARTBEAT frame uint32_t txSyncClocksUsec; //Time in microseconds to transmit the SYNC_CLOCKS frame uint32_t txDatagramMicros; //Timestamp at the beginning of the transmitDatagram routine diff --git a/Firmware/LoRaSerial_Firmware/Radio.ino b/Firmware/LoRaSerial_Firmware/Radio.ino index 745d6098..a92f1105 100644 --- a/Firmware/LoRaSerial_Firmware/Radio.ino +++ b/Firmware/LoRaSerial_Firmware/Radio.ino @@ -1365,6 +1365,11 @@ bool xmitVcDatagram() } //Broadcast a HEARTBEAT to all of the VCs +bool xmitVcHeartbeat() +{ + return xmitVcHeartbeat(VC_IGNORE_TX, myUniqueId); +} + bool xmitVcHeartbeat(int8_t addr, uint8_t * id) { uint32_t currentMillis = millis(); @@ -2947,15 +2952,17 @@ void printControl(uint8_t value) systemPrintln(control->datagramType); } - systemPrintTimestamp(); - systemPrint(" requestYield "); if (control->requestYield) - systemPrintln("1"); - else - systemPrintln("0"); + { + systemPrintTimestamp(); + systemPrintln(" requestYield"); + } if (control->ignoreFrame) + { + systemPrintTimestamp(); systemPrintln(" Ignore Frame"); + } outputSerialData(true); @@ -3103,6 +3110,68 @@ bool retransmitDatagram(VIRTUAL_CIRCUIT * vc) return (true); //Transmission has started } +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +//Transmit Ignored Frames +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +bool getTxTime(bool (*transmitFrame)(), uint32_t * txFrameUsec, const char * frameName) +{ + bool txStatus; + uint32_t transmitDelay; + +#define IGNORE_TRANSMIT_DELAY_MSEC 100 + + //Transmit the frame + transmitDelay = 0; + do + { + //Delay between retries + if (transmitDelay) + delay(transmitDelay); + transmitDelay += IGNORE_TRANSMIT_DELAY_MSEC; + + //Fail transmission after 5 attempts + if (transmitDelay > (5 * IGNORE_TRANSMIT_DELAY_MSEC)) + { + if (settings.debugSync) + { + systemPrintTimestamp(); + systemPrint("TX ignore "); + systemPrint(frameName); + systemPrintln(" failed!"); + outputSerialData(true); + } + return false; + } + + //Attempt to transmit the requested frame + txControl.ignoreFrame = true; + txStatus = transmitFrame(); + txControl.ignoreFrame = false; + } while (!txStatus); + + //Wait for transmit completion + while (!transactionComplete) + petWDT(); + transactionComplete = false; + + //Compute the transmit time + *txFrameUsec = transactionCompleteMicros - txSetChannelTimerMicros; + if (settings.debugSync) + { + systemPrintTimestamp(); + systemPrint("TX "); + systemPrint(frameName); + systemPrint(": "); + systemPrint(*txFrameUsec); + systemPrintln(" mSec"); + outputSerialData(true); + } + return true; +} + +//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + //Use the maximum dwell setting to start the timer that indicates when to hop channels void startChannelTimer() { diff --git a/Firmware/LoRaSerial_Firmware/States.ino b/Firmware/LoRaSerial_Firmware/States.ino index 758c0aa6..6495ae1c 100644 --- a/Firmware/LoRaSerial_Firmware/States.ino +++ b/Firmware/LoRaSerial_Firmware/States.ino @@ -180,14 +180,20 @@ void updateRadioState() petWDT(); - returnToReceiving(); //Start receiving - //Stop the ACK timer STOP_ACK_TIMER(); //Start the link between the radios if (settings.operatingMode == MODE_POINT_TO_POINT) { + //Determine transmit frame times for SYNC_CLOCKS, ACK + getTxTime(xmitDatagramP2PFindPartner, &txFindPartnerUsec, "FIND_PARTNER"); + getTxTime(xmitDatagramP2PSyncClocks, &txSyncClocksUsec, "SYNC_CLOCKS"); + getTxTime(xmitDatagramP2PHeartbeat, &txHeartbeatUsec, "HEARTBEAT"); + getTxTime(xmitDatagramP2PAck, &txDataAckUsec, "ACK"); + + //Start receiving + returnToReceiving(); changeState(RADIO_P2P_LINK_DOWN); break; } @@ -195,6 +201,8 @@ void updateRadioState() //Virtual circuit mode if (settings.operatingMode == MODE_VIRTUAL_CIRCUIT) { + //Determine transmit frame time for HEARTBEAT + getTxTime(xmitVcHeartbeat, &txHeartbeatUsec, "HEARTBEAT"); if (settings.server) { //Reserve the server's address (0) @@ -218,13 +226,26 @@ void updateRadioState() } //Multipoint mode + //Determine transmit frame times for SYNC_CLOCKS, HEARTBEAT, ACK + getTxTime(xmitDatagramP2PSyncClocks, &txSyncClocksUsec, "SYNC_CLOCKS"); + getTxTime(xmitDatagramMpHeartbeat, &txHeartbeatUsec, "HEARTBEAT"); + getTxTime(xmitDatagramP2PAck, &txDataAckUsec, "ACK"); + + //Start receiving + returnToReceiving(); + if (settings.server == true) { clockSyncReceiver = false; //Multipoint server is clock source startChannelTimer(); //Start hopping - multipoint clock source + + //Start receiving + returnToReceiving(); changeState(RADIO_MP_STANDBY); } else + //Start receiving + returnToReceiving(); changeState(RADIO_DISCOVER_BEGIN); break; diff --git a/Firmware/LoRaSerial_Firmware/Virtual_Circuit_Protocol.h b/Firmware/LoRaSerial_Firmware/Virtual_Circuit_Protocol.h index 89594c4d..aaa4000a 100644 --- a/Firmware/LoRaSerial_Firmware/Virtual_Circuit_Protocol.h +++ b/Firmware/LoRaSerial_Firmware/Virtual_Circuit_Protocol.h @@ -28,6 +28,7 @@ #define VC_BROADCAST ((int8_t)(VC_RSVD_SPECIAL_VCS | VCAB_NUMBER_MASK)) #define VC_COMMAND (VC_BROADCAST - 1) //Command input and command response #define VC_UNASSIGNED (VC_COMMAND - 1) +#define VC_IGNORE_TX (VC_UNASSIGNED - 1) //Source and destinations reserved for the local host #define PC_COMMAND VC_RSVD_SPECIAL_VCS //Command input and command response From 831fd2978f5c3a4c4f6319bd5b318aad3c94eb5f Mon Sep 17 00:00:00 2001 From: Lee Leahy Date: Tue, 10 Jan 2023 16:21:35 -1000 Subject: [PATCH 04/11] VC: Fix computation for next HEARTBEAT interval --- .../LoRaSerial_Firmware.ino | 2 + Firmware/LoRaSerial_Firmware/Radio.ino | 8 ++-- Firmware/LoRaSerial_Firmware/States.ino | 43 ++++++++++++------- 3 files changed, 32 insertions(+), 21 deletions(-) diff --git a/Firmware/LoRaSerial_Firmware/LoRaSerial_Firmware.ino b/Firmware/LoRaSerial_Firmware/LoRaSerial_Firmware.ino index 40a52a50..13782497 100644 --- a/Firmware/LoRaSerial_Firmware/LoRaSerial_Firmware.ino +++ b/Firmware/LoRaSerial_Firmware/LoRaSerial_Firmware.ino @@ -596,6 +596,8 @@ uint32_t txDatagramMicros; //Timestamp at the beginning of the transmitDatagram uint16_t maxFrameAirTime; //Air time of the maximum sized message unsigned long remoteSystemMillis; //Millis value contained in the received message +#define VC_DELAY_HEARTBEAT_MSEC 5 + bool rxFirstAck; //Set true when first ACK is received bool txFirstAck; //Set true when first ACK is transmitted diff --git a/Firmware/LoRaSerial_Firmware/Radio.ino b/Firmware/LoRaSerial_Firmware/Radio.ino index a92f1105..4308a979 100644 --- a/Firmware/LoRaSerial_Firmware/Radio.ino +++ b/Firmware/LoRaSerial_Firmware/Radio.ino @@ -3555,7 +3555,7 @@ void setVcHeartbeatTimer() petWDT(); //Determine the delay before channel zero is reached - deltaMillis = mSecToChannelZero() - heartbeatTimer; + deltaMillis = mSecToChannelZero(); //Determine the delay before the next HEARTBEAT frame if ((!settings.server) || (deltaMillis > ((3 * settings.heartbeatTimeout) / 2)) @@ -3566,17 +3566,15 @@ void setVcHeartbeatTimer() else if (deltaMillis >= settings.heartbeatTimeout) heartbeatRandomTime = deltaMillis / 2; else - heartbeatRandomTime = deltaMillis; + heartbeatRandomTime = deltaMillis + VC_DELAY_HEARTBEAT_MSEC; //Display the next HEARTBEAT time interval if (settings.debugHeartbeat) { - systemPrint("deltaMillis: "); + systemPrint("mSecToChannelZero: "); systemPrintln(deltaMillis); systemPrint("heartbeatRandomTime: "); systemPrintln(heartbeatRandomTime); - outputSerialData(true); - petWDT(); } } diff --git a/Firmware/LoRaSerial_Firmware/States.ino b/Firmware/LoRaSerial_Firmware/States.ino index 6495ae1c..0ff02200 100644 --- a/Firmware/LoRaSerial_Firmware/States.ino +++ b/Firmware/LoRaSerial_Firmware/States.ino @@ -210,18 +210,15 @@ void updateRadioState() clockSyncReceiver = false; //VC server is clock source if (settings.frequencyHop) startChannelTimer(); - - //Start sending heartbeats - xmitVcHeartbeat(myVc, myUniqueId); - changeState(RADIO_VC_WAIT_TX_DONE); - break; } + else + //Unknown client address + myVc = VC_UNASSIGNED; - //Unknown client address - myVc = VC_UNASSIGNED; - - //Start sending heartbeats - changeState(RADIO_VC_WAIT_SERVER); + //Server: Start sending HEARTBEAT + //Client: Determine HEARTBEAT transmit time + xmitVcHeartbeat(myVc, myUniqueId); + changeState(RADIO_VC_WAIT_TX_DONE); break; } @@ -1853,7 +1850,8 @@ void updateRadioState() } //Stop the frequency hopping - stopChannelTimer(); + if (channelTimerMsec) + stopChannelTimer(); if (channelNumber != 0) { channelNumber = 0; @@ -1870,12 +1868,17 @@ void updateRadioState() //Process the received datagram if ((packetType == DATAGRAM_VC_HEARTBEAT) && (rxSrcVc == VC_SERVER)) { + virtualCircuitList[VC_SERVER].lastTrafficMillis = currentMillis; + //Start the channel timer startChannelTimer(); - channelTimerStart -= settings.maxDwellTime; + channelTimerStart -= settings.maxDwellTime >> 1; //Synchronize the channel timer with the server vcReceiveHeartbeat(millis() - currentMillis); + + //Delay for a while before sending the HEARTBEAT + heartbeatRandomTime = random((settings.heartbeatTimeout * 2) / 10, settings.heartbeatTimeout); changeState(RADIO_VC_WAIT_RECEIVE); } else @@ -1898,7 +1901,7 @@ void updateRadioState() transactionComplete = false; //Compute the HEARTBEAT frame transmit frame - if ((!txHeartbeatUsec) && (txControl.datagramType == DATAGRAM_HEARTBEAT)) + if ((!txHeartbeatUsec) && (txControl.datagramType == DATAGRAM_VC_HEARTBEAT)) { txHeartbeatUsec = transactionCompleteMicros - txSetChannelTimerMicros; if (settings.debugSync) @@ -1915,7 +1918,10 @@ void updateRadioState() returnToReceiving(); //Set the next state - changeState(RADIO_VC_WAIT_RECEIVE); + if (virtualCircuitList[VC_SERVER].vcState == VC_STATE_LINK_DOWN) + changeState(RADIO_VC_WAIT_SERVER); + else + changeState(RADIO_VC_WAIT_RECEIVE); } break; @@ -2123,7 +2129,11 @@ void updateRadioState() //Send another heartbeat if (xmitVcHeartbeat(myVc, myUniqueId)) { + if (settings.server) + blinkHeartbeatLed(true); triggerEvent(TRIGGER_TX_VC_HEARTBEAT); + if (settings.debugHeartbeat && settings.server) + systemPrintln(channelNumber); if (((uint8_t)myVc) < MAX_VC) virtualCircuitList[myVc].lastTrafficMillis = currentMillis; changeState(RADIO_VC_WAIT_TX_DONE); @@ -3124,8 +3134,9 @@ void vcReceiveHeartbeat(uint32_t rxMillis) uint32_t deltaMillis; int vcSrc; + //Adjust freq hop ISR based on server's remaining clock if ((rxSrcVc == VC_SERVER) || (memcmp(rxVcData, myUniqueId, sizeof(myUniqueId)) == 0)) - syncChannelTimer(txRxTimeMsec); //Adjust freq hop ISR based on server's remaining clock + syncChannelTimer((txHeartbeatUsec + TX_TO_RX_USEC + micros() - transactionCompleteMicros) / 1000); //Update the timestamp offset if (rxSrcVc == VC_SERVER) @@ -3137,7 +3148,7 @@ void vcReceiveHeartbeat(uint32_t rxMillis) //then the delay from reading the millisecond value on the server should //get offset by the transmit setup time and the receive overhead time. memcpy(×tampOffset, &rxVcData[UNIQUE_ID_BYTES], sizeof(timestampOffset)); - timestampOffset += vcTxHeartbeatMillis + rxMillis - millis(); + timestampOffset += (txHeartbeatUsec + TX_TO_RX_USEC + micros() - transactionCompleteMicros) / 1000; } triggerEvent(TRIGGER_RX_VC_HEARTBEAT); From 3b9a998de36dd9fd1f3edd6ab6e9ab67ce2c4a8b Mon Sep 17 00:00:00 2001 From: Lee Leahy Date: Wed, 11 Jan 2023 09:01:13 -1000 Subject: [PATCH 05/11] VC: Fix the timestamp offset calculations --- Firmware/LoRaSerial_Firmware/States.ino | 1 + 1 file changed, 1 insertion(+) diff --git a/Firmware/LoRaSerial_Firmware/States.ino b/Firmware/LoRaSerial_Firmware/States.ino index 0ff02200..21f84b1e 100644 --- a/Firmware/LoRaSerial_Firmware/States.ino +++ b/Firmware/LoRaSerial_Firmware/States.ino @@ -3148,6 +3148,7 @@ void vcReceiveHeartbeat(uint32_t rxMillis) //then the delay from reading the millisecond value on the server should //get offset by the transmit setup time and the receive overhead time. memcpy(×tampOffset, &rxVcData[UNIQUE_ID_BYTES], sizeof(timestampOffset)); + timestampOffset -= millis(); timestampOffset += (txHeartbeatUsec + TX_TO_RX_USEC + micros() - transactionCompleteMicros) / 1000; } triggerEvent(TRIGGER_RX_VC_HEARTBEAT); From b062e95e6ff88d80515fd90c0ea9512e78bff2ea Mon Sep 17 00:00:00 2001 From: Lee Leahy Date: Wed, 11 Jan 2023 14:25:05 -1000 Subject: [PATCH 06/11] Pass microseconds to syncChannelTimer instead of milliseconds --- Firmware/LoRaSerial_Firmware/Radio.ino | 4 +++- Firmware/LoRaSerial_Firmware/States.ino | 8 ++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/Firmware/LoRaSerial_Firmware/Radio.ino b/Firmware/LoRaSerial_Firmware/Radio.ino index 4308a979..16b84c64 100644 --- a/Firmware/LoRaSerial_Firmware/Radio.ino +++ b/Firmware/LoRaSerial_Firmware/Radio.ino @@ -3210,11 +3210,12 @@ void stopChannelTimer() //Given the remote unit's number of ms before its next hop, //adjust our own channelTimer interrupt to be synchronized with the remote unit -void syncChannelTimer(uint16_t frameAirTimeMsec) +void syncChannelTimer(uint32_t frameAirTimeUsec) { int16_t adjustment; unsigned long currentMillis; int8_t delayedHopCount; + uint16_t frameAirTimeMsec; int16_t lclHopTimeMsec; uint16_t msToNextHop; int16_t rmtHopTimeMsec; @@ -3293,6 +3294,7 @@ void syncChannelTimer(uint16_t frameAirTimeMsec) //Compute the remote system's channel timer firing time offset in milliseconds //using the channel timer value and the adjustments for transmit and receive //time (time of flight) + frameAirTimeMsec = (frameAirTimeUsec + TX_TO_RX_USEC + micros() - transactionCompleteMicros) / 1000; rmtHopTimeMsec = msToNextHopRemote - frameAirTimeMsec; //Compute the when the local system last hopped diff --git a/Firmware/LoRaSerial_Firmware/States.ino b/Firmware/LoRaSerial_Firmware/States.ino index 21f84b1e..b28a7b7e 100644 --- a/Firmware/LoRaSerial_Firmware/States.ino +++ b/Firmware/LoRaSerial_Firmware/States.ino @@ -826,7 +826,7 @@ void updateRadioState() COMPUTE_RX_TIME(rxData, 1, txDataAckUsec); //The datagram we are expecting - syncChannelTimer(txRxTimeMsec); //Adjust freq hop ISR based on remote's remaining clock + syncChannelTimer(txDataAckUsec); //Adjust freq hop ISR based on remote's remaining clock triggerEvent(TRIGGER_RX_ACK); @@ -1213,7 +1213,7 @@ void updateRadioState() //Start and adjust freq hop ISR based on remote's remaining clock startChannelTimer(); channelTimerStart -= settings.maxDwellTime; - syncChannelTimer((txSyncClocksUsec + TX_TO_RX_USEC + micros() - transactionCompleteMicros) / 1000); + syncChannelTimer(txSyncClocksUsec); triggerEvent(TRIGGER_RX_SYNC_CLOCKS); //Switch to the proper frequency for the channel @@ -1377,7 +1377,7 @@ void updateRadioState() uint16_t frameAirTimeMsec; //Adjust freq hop ISR based on server's remaining clock - syncChannelTimer((txHeartbeatUsec + TX_TO_RX_USEC + micros() - transactionCompleteMicros) / 1000); + syncChannelTimer(txHeartbeatUsec); systemPrint("HEARTBEAT TX mSec: "); systemPrintln(frameAirTime); } @@ -3136,7 +3136,7 @@ void vcReceiveHeartbeat(uint32_t rxMillis) //Adjust freq hop ISR based on server's remaining clock if ((rxSrcVc == VC_SERVER) || (memcmp(rxVcData, myUniqueId, sizeof(myUniqueId)) == 0)) - syncChannelTimer((txHeartbeatUsec + TX_TO_RX_USEC + micros() - transactionCompleteMicros) / 1000); + syncChannelTimer(txHeartbeatUsec); //Update the timestamp offset if (rxSrcVc == VC_SERVER) From 873fcf6d47013c16e2a13a89048a0315ef5b121b Mon Sep 17 00:00:00 2001 From: Lee Leahy Date: Wed, 11 Jan 2023 15:17:43 -1000 Subject: [PATCH 07/11] VC: Remove dead code --- .../LoRaSerial_Firmware/LoRaSerial_Firmware.ino | 1 - Firmware/LoRaSerial_Firmware/Radio.ino | 4 ---- Firmware/LoRaSerial_Firmware/States.ino | 14 +------------- 3 files changed, 1 insertion(+), 18 deletions(-) diff --git a/Firmware/LoRaSerial_Firmware/LoRaSerial_Firmware.ino b/Firmware/LoRaSerial_Firmware/LoRaSerial_Firmware.ino index 13782497..39206ee5 100644 --- a/Firmware/LoRaSerial_Firmware/LoRaSerial_Firmware.ino +++ b/Firmware/LoRaSerial_Firmware/LoRaSerial_Firmware.ino @@ -465,7 +465,6 @@ unsigned long linkDownTimer; unsigned long rcvTimeMillis; unsigned long xmitTimeMillis; long timestampOffset; -unsigned long vcTxHeartbeatMillis; //Transmit control uint8_t * endOfTxData; diff --git a/Firmware/LoRaSerial_Firmware/Radio.ino b/Firmware/LoRaSerial_Firmware/Radio.ino index 16b84c64..dcd02d8a 100644 --- a/Firmware/LoRaSerial_Firmware/Radio.ino +++ b/Firmware/LoRaSerial_Firmware/Radio.ino @@ -1419,10 +1419,6 @@ bool xmitVcHeartbeat(int8_t addr, uint8_t * id) txControl.datagramType = DATAGRAM_VC_HEARTBEAT; txControl.ackNumber = 0; - //Determine the time that it took to pass this frame to the radio - //This time is used to adjust the time offset - vcTxHeartbeatMillis = millis() - currentMillis; - //Select a random for the next heartbeat setVcHeartbeatTimer(); return (transmitDatagram()); diff --git a/Firmware/LoRaSerial_Firmware/States.ino b/Firmware/LoRaSerial_Firmware/States.ino index b28a7b7e..fe0f2b9c 100644 --- a/Firmware/LoRaSerial_Firmware/States.ino +++ b/Firmware/LoRaSerial_Firmware/States.ino @@ -1898,20 +1898,8 @@ void updateRadioState() //If dio0ISR has fired, we are done transmitting if (transactionComplete == true) { - transactionComplete = false; - - //Compute the HEARTBEAT frame transmit frame - if ((!txHeartbeatUsec) && (txControl.datagramType == DATAGRAM_VC_HEARTBEAT)) - { - txHeartbeatUsec = transactionCompleteMicros - txSetChannelTimerMicros; - if (settings.debugSync) - { - systemPrint("txHeartbeatUsec: "); - systemPrintln(txHeartbeatUsec); - } - } - //Indicate that the transmission is complete + transactionComplete = false; triggerEvent(TRIGGER_TX_DONE); //Start the receive operation From 1a84fca2b0b0f3b88b8db549737bd7a32f10bf6a Mon Sep 17 00:00:00 2001 From: Lee Leahy Date: Wed, 11 Jan 2023 15:27:56 -1000 Subject: [PATCH 08/11] Don't reference currentMillis in COMPUTE_TIMESTAMP_OFFSET --- Firmware/LoRaSerial_Firmware/States.ino | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Firmware/LoRaSerial_Firmware/States.ino b/Firmware/LoRaSerial_Firmware/States.ino index fe0f2b9c..26b2f6ac 100644 --- a/Firmware/LoRaSerial_Firmware/States.ino +++ b/Firmware/LoRaSerial_Firmware/States.ino @@ -51,8 +51,8 @@ #define COMPUTE_TIMESTAMP_OFFSET(millisBuffer, rShift, frameAirTimeUsec) \ { \ unsigned long deltaUsec = frameAirTimeUsec + micros() - transactionCompleteMicros; \ - memcpy(&remoteSystemMillis, millisBuffer, sizeof(currentMillis)); \ - timestampOffset = (remoteSystemMillis + (deltaUsec / 1000) - currentMillis); \ + memcpy(&remoteSystemMillis, millisBuffer, sizeof(remoteSystemMillis)); \ + timestampOffset = remoteSystemMillis + (deltaUsec / 1000) - millis(); \ timestampOffset >>= rShift; \ } From 72b44d98d7736b6164684785d29859070a606889 Mon Sep 17 00:00:00 2001 From: Lee Leahy Date: Wed, 11 Jan 2023 15:30:01 -1000 Subject: [PATCH 09/11] Multipoint: Use the measured TX times for channel timer synchronization --- Firmware/LoRaSerial_Firmware/States.ino | 98 +++++++------------------ 1 file changed, 27 insertions(+), 71 deletions(-) diff --git a/Firmware/LoRaSerial_Firmware/States.ino b/Firmware/LoRaSerial_Firmware/States.ino index 26b2f6ac..87a52128 100644 --- a/Firmware/LoRaSerial_Firmware/States.ino +++ b/Firmware/LoRaSerial_Firmware/States.ino @@ -1130,6 +1130,12 @@ void updateRadioState() //Walk through channel table backwards, transmitting a FIND_PARTNER and looking for an SYNC_CLOCKS //==================== case RADIO_DISCOVER_SCANNING: + if (settings.server) + { + changeState(RADIO_MP_STANDBY); + break; + } + stopChannelTimer(); //Stop hopping - multipoint discovery if (transactionComplete) @@ -1180,59 +1186,32 @@ void updateRadioState() //Change to the server's channel number channelNumber = rxVcData[0]; - //Get the HEARTBEAT TX time - if (!txHeartbeatUsec) - { - memcpy(&txHeartbeatUsec, rxData + 1 + 4, sizeof(txHeartbeatUsec)); - if (settings.debugSync) - { - systemPrint("txHeartbeatUsec: "); - systemPrintln(txHeartbeatUsec); - } - } + //Update the timestamp + COMPUTE_TIMESTAMP_OFFSET(rxData + 1, 0, txSyncClocksUsec); - //Get the SYNC_CLOCKS TX time - if (!txSyncClocksUsec) - { - memcpy(&txSyncClocksUsec, rxData + 1 + 4 + 4, sizeof(txSyncClocksUsec)); - if (settings.debugSync) - { - systemPrint("txSyncClocksUsec: "); - systemPrintln(txSyncClocksUsec); - } - } - - //Ignore this frame when the times are not filled in - if ((!txHeartbeatUsec) || (!txSyncClocksUsec)) - triggerEvent(TRIGGER_RX_SYNC_CLOCKS); - else - { - COMPUTE_TIMESTAMP_OFFSET(rxData + 1, 0, txSyncClocksUsec); + //Server has responded to FIND_PARTNER with SYNC_CLOCKS + //Start and adjust freq hop ISR based on remote's remaining clock + startChannelTimer(); + channelTimerStart -= settings.maxDwellTime; + syncChannelTimer(txSyncClocksUsec); + triggerEvent(TRIGGER_RX_SYNC_CLOCKS); - //Server has responded to FIND_PARTNER with SYNC_CLOCKS - //Start and adjust freq hop ISR based on remote's remaining clock - startChannelTimer(); - channelTimerStart -= settings.maxDwellTime; - syncChannelTimer(txSyncClocksUsec); - triggerEvent(TRIGGER_RX_SYNC_CLOCKS); + //Switch to the proper frequency for the channel + setRadioFrequency(false); - //Switch to the proper frequency for the channel - setRadioFrequency(false); - - if (settings.debugSync) - { - systemPrint(" Channel Number: "); - systemPrintln(channelNumber); - outputSerialData(true); - if (timeToHop == true) //If the channelTimer has expired, move to next frequency - hopChannel(); - } + if (settings.debugSync) + { + systemPrint(" Channel Number: "); + systemPrintln(channelNumber); + outputSerialData(true); + if (timeToHop == true) //If the channelTimer has expired, move to next frequency + hopChannel(); + } - frequencyCorrection += radio.getFrequencyError() / 1000000.0; + frequencyCorrection += radio.getFrequencyError() / 1000000.0; - lastPacketReceived = millis(); //Reset - changeState(RADIO_MP_STANDBY); - } + lastPacketReceived = millis(); //Reset + changeState(RADIO_MP_STANDBY); } break; } @@ -1468,29 +1447,6 @@ void updateRadioState() if (transactionComplete == true) { transactionComplete = false; //Reset ISR flag - - //Compute the HEARTBEAT frame transmit frame - if ((!txHeartbeatUsec) && (txControl.datagramType == DATAGRAM_HEARTBEAT)) - { - txHeartbeatUsec = transactionCompleteMicros - txSetChannelTimerMicros; - if (settings.debugSync) - { - systemPrint("txHeartbeatUsec: "); - systemPrintln(txHeartbeatUsec); - } - } - - //Compute the SYNC_CLOCKS frame transmit frame - else if ((!txSyncClocksUsec) && (txControl.datagramType == DATAGRAM_SYNC_CLOCKS)) - { - txSyncClocksUsec = transactionCompleteMicros - txSetChannelTimerMicros; - if (settings.debugSync) - { - systemPrint("txSyncClocksUsec: "); - systemPrintln(txSyncClocksUsec); - } - } - returnToReceiving(); changeState(RADIO_MP_STANDBY); } From bdb3595d5dbdf4779a481c14a5c3804d784d17ac Mon Sep 17 00:00:00 2001 From: Lee Leahy Date: Wed, 11 Jan 2023 16:10:36 -1000 Subject: [PATCH 10/11] Don't extend the SYNC_CLOCKS frame --- .../LoRaSerial_Firmware.ino | 2 +- Firmware/LoRaSerial_Firmware/Radio.ino | 25 ++++++------------- 2 files changed, 9 insertions(+), 18 deletions(-) diff --git a/Firmware/LoRaSerial_Firmware/LoRaSerial_Firmware.ino b/Firmware/LoRaSerial_Firmware/LoRaSerial_Firmware.ino index 39206ee5..7e727b11 100644 --- a/Firmware/LoRaSerial_Firmware/LoRaSerial_Firmware.ino +++ b/Firmware/LoRaSerial_Firmware/LoRaSerial_Firmware.ino @@ -66,7 +66,7 @@ const int FIRMWARE_VERSION_MINOR = 0; //Frame lengths #define MP_HEARTBEAT_BYTES 0 //Number of data bytes in the MP_HEARTBEAT frame #define P2P_FIND_PARTNER_BYTES sizeof(unsigned long) //Number of data bytes in the FIND_PARTNER frame -#define P2P_SYNC_CLOCKS_BYTES (sizeof(uint8_t) + sizeof(unsigned long) + sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint32_t)) //Number of data bytes in the SYNC_CLOCKS frame +#define P2P_SYNC_CLOCKS_BYTES (sizeof(uint8_t) + sizeof(unsigned long)) //Number of data bytes in the SYNC_CLOCKS frame #define P2P_ZERO_ACKS_BYTES sizeof(unsigned long) //Number of data bytes in the ZERO_ACKS frame #define P2P_HEARTBEAT_BYTES sizeof(unsigned long) //Number of data bytes in the HEARTBEAT frame #define P2P_ACK_BYTES sizeof(unsigned long) //Number of data bytes in the ACK frame diff --git a/Firmware/LoRaSerial_Firmware/Radio.ino b/Firmware/LoRaSerial_Firmware/Radio.ino index dcd02d8a..299e0043 100644 --- a/Firmware/LoRaSerial_Firmware/Radio.ino +++ b/Firmware/LoRaSerial_Firmware/Radio.ino @@ -889,24 +889,15 @@ bool xmitDatagramP2PSyncClocks() memcpy(endOfTxData, ¤tMillis, sizeof(currentMillis)); endOfTxData += sizeof(unsigned long); - memcpy(endOfTxData, &txHeartbeatUsec, sizeof(txHeartbeatUsec)); - endOfTxData += sizeof(txHeartbeatUsec); - - memcpy(endOfTxData, &txSyncClocksUsec, sizeof(txSyncClocksUsec)); - endOfTxData += sizeof(txSyncClocksUsec); - - memcpy(endOfTxData, &txDataAckUsec, sizeof(txDataAckUsec)); - endOfTxData += sizeof(txDataAckUsec); - /* - endOfTxData ---. - | - V - +----------+---------+----------+------------+---------+---------+-------------+-------------+-----------+----------+ - | Optional | | Optional | Optional | Channel | | HEARTBEAT | SYNC_CLOCKS | DATA_ACK | | - | NET ID | Control | C-Timer | SF6 Length | Number | Millis | Micros | Micros | Micros | Trailer | - | 8 bits | 8 bits | 2 bytes | 8 bits | 1 byte | 4 bytes | 4 Bytes | 4 Bytes | 4 Bytes | n Bytes | - +----------+---------+----------+------------+---------+---------+-------------+-------------+-----------+----------+ + endOfTxData ---. + | + V + +----------+---------+----------+------------+---------+---------+----------+ + | Optional | | Optional | Optional | Channel | | | + | NET ID | Control | C-Timer | SF6 Length | Number | Millis | Trailer | + | 8 bits | 8 bits | 2 bytes | 8 bits | 1 byte | 4 bytes | n Bytes | + +----------+---------+----------+------------+---------+---------+----------+ */ //Verify the data length From 74762b2b334646da0591263bfeca6d8cabb18b0a Mon Sep 17 00:00:00 2001 From: Lee Leahy Date: Wed, 11 Jan 2023 16:26:19 -1000 Subject: [PATCH 11/11] P2P: Fix error in link down/up synchronization --- Firmware/LoRaSerial_Firmware/States.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Firmware/LoRaSerial_Firmware/States.ino b/Firmware/LoRaSerial_Firmware/States.ino index 87a52128..6a0ab983 100644 --- a/Firmware/LoRaSerial_Firmware/States.ino +++ b/Firmware/LoRaSerial_Firmware/States.ino @@ -722,7 +722,7 @@ void updateRadioState() } //Determine if an ACK was transmitted - if (txControl.datagramType = DATAGRAM_DATA_ACK) + if (txControl.datagramType == DATAGRAM_DATA_ACK) { COMPUTE_TX_TIME(); }