diff --git a/Firmware/LoRaSerial/Commands.ino b/Firmware/LoRaSerial/Commands.ino index 596104f6..80495997 100644 --- a/Firmware/LoRaSerial/Commands.ino +++ b/Firmware/LoRaSerial/Commands.ino @@ -226,11 +226,15 @@ bool commandAT(const char * commandString) systemPrintln(" ATI10 - Display radio metrics"); systemPrintln(" ATI11 - Display the system runtime"); systemPrintln(" ATI12 - Set programming complete"); + systemPrintln(" ATI13 - Display the random seed"); + systemPrintln(" ATI14 - Display the channel table by channel"); + systemPrintln(" ATI15 - Display the channel table by frequency"); //Virtual circuit information commands systemPrintln(" ATI30 - Return myVc value"); systemPrintln(" ATI31 - Display the VC details"); systemPrintln(" ATI32 - Dump the NVM unique ID table"); + systemPrintln(" ATI33 - Display the VC states"); return true; case ('0'): //ATI0 - Show user settable parameters @@ -637,6 +641,69 @@ bool commandAT(const char * commandString) systemWrite(*data++); } return true; + + case ('3'): //ATI13 - Display the random seed + systemPrint("myRandSeed: "); + systemPrintln(myRandSeed); + return true; + + case ('4'): //ATI14 - Display the channel table by channel + systemPrintln("Channel Table"); + if (!channels) + systemPrintln(" Channel table not allocated!"); + else + { + for (int index = 0; index < settings.numberOfChannels; index++) + { + systemPrint(" Channel "); + if ((index <= 9) && (settings.numberOfChannels >= 10)) + systemPrint(" "); + systemPrint(index); + systemPrint(": "); + systemPrint(channels[index],3); + systemPrintln(" MHz"); + } + } + return true; + + case ('5'): //ATI14 - Display the channel table by frequency + systemPrintln("Channel Table by Frequency"); + if (!channels) + systemPrintln(" Channel table not allocated!"); + else + { + //Initialize the channel array + uint8_t chanIndex[settings.numberOfChannels]; + for (int index = 0; index < settings.numberOfChannels; index++) + chanIndex[index] = index; + + //Sort the channel numbers by frequency + for (int index = 0; index < (settings.numberOfChannels - 1); index++) + { + for (int x = index + 1; x < settings.numberOfChannels; x++) + { + if (channels[chanIndex[index]] > channels[chanIndex[x]]) + { + uint8_t f = chanIndex[index]; + chanIndex[index] = chanIndex[x]; + chanIndex[x] = f; + } + } + } + + //Display the frequencies + for (int index = 0; index < settings.numberOfChannels; index++) + { + systemPrint(" Channel "); + if ((chanIndex[index] <= 9) && (settings.numberOfChannels >= 10)) + systemPrint(" "); + systemPrint(chanIndex[index]); + systemPrint(": "); + systemPrint(channels[chanIndex[index]],3); + systemPrintln(" MHz"); + } + } + return true; } } if ((commandString[2] == 'I') && (commandString[3] == '3') && (commandLength == 5)) @@ -769,6 +836,17 @@ bool commandAT(const char * commandString) systemPrintln("Empty"); } return true; + + case ('3'): //ATI33 - Display the VC states + for (int vcIndex = 0; vcIndex < MAX_VC; vcIndex++) + { + systemPrint("VC "); + systemPrint(vcIndex); + systemPrint(": "); + systemPrintln(vcStateNames[virtualCircuitList[vcIndex].vcState]); + } + return true; + } } if ((commandString[2] == 'I') && (commandString[3] == '5') && (commandLength == 5)) diff --git a/Firmware/LoRaSerial/LoRaSerial.ino b/Firmware/LoRaSerial/LoRaSerial.ino index 82561953..4f688231 100644 --- a/Firmware/LoRaSerial/LoRaSerial.ino +++ b/Firmware/LoRaSerial/LoRaSerial.ino @@ -152,6 +152,9 @@ float *channels; uint8_t channelNumber = 0; uint32_t airSpeed; +uint16_t myRandSeed; +bool myRandBit; + //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- //Encryption diff --git a/Firmware/LoRaSerial/Radio.ino b/Firmware/LoRaSerial/Radio.ino index 39ac2f80..7b24cb57 100644 --- a/Firmware/LoRaSerial/Radio.ino +++ b/Firmware/LoRaSerial/Radio.ino @@ -482,9 +482,6 @@ uint16_t calcMaxThroughput() return (mostBytesPerSecond); } -uint16_t myRandSeed; -bool myRandBit; - //Generate unique hop table based on radio settings void generateHopTable() { diff --git a/Firmware/LoRaSerial/Serial.ino b/Firmware/LoRaSerial/Serial.ino index 7b1f7e55..ef5392e5 100644 --- a/Firmware/LoRaSerial/Serial.ino +++ b/Firmware/LoRaSerial/Serial.ino @@ -302,7 +302,7 @@ void updateSerial() //Assert RTS when there is enough space in the receive buffer if ((!rtsAsserted) && (availableRXBytes() < (sizeof(serialReceiveBuffer) / 2)) - && (availableTXBytes() <= RTS_ON_BYTES)) + && (availableTXBytes() <= settings.rtsOnBytes)) updateRTS(true); //We're ready for more data //Attempt to empty the serialTransmitBuffer diff --git a/Firmware/Tools/VcServerTest.c b/Firmware/Tools/VcServerTest.c index b0c62a30..96071ef0 100644 --- a/Firmware/Tools/VcServerTest.c +++ b/Firmware/Tools/VcServerTest.c @@ -3,6 +3,7 @@ #include #include "settings.h" +#define ISSUE_COMMANDS_IN_PARALLEL 1 #ifndef POLL_TIMEOUT_USEC #define POLL_TIMEOUT_USEC 1000 #endif // POLL_TIMEOUT_USEC @@ -30,6 +31,7 @@ #define SET_PROGRAM_COMPLETE "ati12" #define START_3_WAY_HANDSHAKE "atc" +#define DEBUG_CMD_ISSUE 0 #define DEBUG_LOCAL_COMMANDS 0 #define DEBUG_PC_CMD_ISSUE 0 #define DEBUG_PC_TO_RADIO 0 @@ -64,28 +66,54 @@ { \ if (COMMAND_PENDING(queue, active)) \ { \ + if (DEBUG_CMD_ISSUE) \ + { \ + if (queue == pcCommandQueue) \ + printf("PC %s done\n", commandName[active]); \ + else \ + { \ + int vc = (&queue[0] - &virtualCircuitList[0].commandQueue[0]) \ + * sizeof(QUEUE_T) / sizeof(virtualCircuitList[0]); \ + printf("VC %d %s done\n", vc, commandName[active]); \ + } \ + } \ queue[active / QUEUE_T_BITS] &= ~(1 << (active & QUEUE_T_MASK)); \ active = CMD_LIST_SIZE; \ } \ } -#define COMMAND_ISSUE(queue, pollCount, cmd) \ -{ \ - /* Place the command in the queue */ \ - queue[cmd / QUEUE_T_BITS] |= 1 << (cmd & QUEUE_T_MASK); \ - \ - /* Timeout the command processor */ \ - if (!commandProcessorRunning) \ - commandProcessorRunning = STALL_CHECK_COUNT; \ - \ - /* Remember when this command was issued */ \ - if (!pollCount) \ - { \ - if (timeoutCount) \ - pollCount = timeoutCount; \ - else \ - pollCount = 1; \ - } \ +#define COMMAND_ISSUE(queue, pollCount, cmd) \ +{ \ + if (DEBUG_CMD_ISSUE) \ + { \ + if (!COMMAND_PENDING(queue, cmd)) \ + { \ + if (queue == pcCommandQueue) \ + printf("PC %s issued\n", commandName[cmd]); \ + else \ + { \ + int vc = (&queue[0] - &virtualCircuitList[0].commandQueue[0]) \ + * sizeof(QUEUE_T) / sizeof(virtualCircuitList[0]); \ + printf("VC %d %s issued\n", vc, commandName[cmd]); \ + } \ + } \ + } \ + \ + /* Place the command in the queue */ \ + queue[cmd / QUEUE_T_BITS] |= 1 << (cmd & QUEUE_T_MASK); \ + \ + /* Timeout the command processor */ \ + if (!commandProcessorRunning) \ + commandProcessorRunning = STALL_CHECK_COUNT; \ + \ + /* Remember when this command was issued */ \ + if (!pollCount) \ + { \ + if (timeoutCount) \ + pollCount = timeoutCount; \ + else \ + pollCount = 1; \ + } \ } #define COMMAND_PENDING(queue,cmd) ((queue[cmd / QUEUE_T_BITS] >> (cmd & QUEUE_T_MASK)) & 1) @@ -102,6 +130,7 @@ typedef enum //Connect to the remote radio CMD_AT_CMDVC, //Select target VC CMD_ATC, //Start the 3-way handshake + CMD_WAIT_CONNECTED, //Wait until the client is connected //Get remote radio connection status, type and ID @@ -443,6 +472,7 @@ int hostToStdout(VC_SERIAL_MESSAGE_HEADER * header, uint8_t * data, uint8_t byte //Write this data to stdout bytesSent = 0; status = 0; + fflush(stdout); while (bytesSent < bytesToSend) { bytesWritten = write(STDOUT, &data[bytesSent], bytesToSend - bytesSent); @@ -476,6 +506,12 @@ void radioToPcLinkStatus(VC_SERIAL_MESSAGE_HEADER * header, uint8_t * data, uint newState = vcMsg->vcState; virtualCircuitList[srcVc].vcState = newState; + //Display the state if requested + if (DISPLAY_STATE_TRANSITION || (newState == VC_STATE_LINK_DOWN) + || (previousState == VC_STATE_LINK_DOWN) + || ((newState != previousState) && (virtualCircuitList[srcVc].activeCommand < CMD_LIST_SIZE))) + printf("VC%d: %s --> %s\n", srcVc, vcStateNames[previousState], vcStateNames[newState]); + //Save the LoRaSerial radio's unique ID //Determine if the PC's value is valid memset(uniqueId, UNIQUE_ID_ERASE_VALUE, sizeof(uniqueId)); @@ -517,9 +553,6 @@ void radioToPcLinkStatus(VC_SERIAL_MESSAGE_HEADER * header, uint8_t * data, uint } } - //Display the state if requested - if (DISPLAY_STATE_TRANSITION) - printf("VC%d: %s --> %s\n", srcVc, vcStateNames[previousState], vcStateNames[newState]); switch (newState) { default: @@ -530,6 +563,9 @@ void radioToPcLinkStatus(VC_SERIAL_MESSAGE_HEADER * header, uint8_t * data, uint break; case VC_STATE_LINK_DOWN: + //Stop the command processing for this VC + virtualCircuitList[srcVc].activeCommand = CMD_LIST_SIZE; + virtualCircuitList[srcVc].commandTimer = 0; if (DEBUG_PC_CMD_ISSUE) printf("VC %d DOWN\n", srcVc); if (DISPLAY_VC_STATE) @@ -546,13 +582,13 @@ void radioToPcLinkStatus(VC_SERIAL_MESSAGE_HEADER * header, uint8_t * data, uint printf("VC %d ALIVE\n", srcVc); COMMAND_ISSUE(virtualCircuitList[srcVc].commandQueue, virtualCircuitList[srcVc].commandTimer, - CMD_AT_CMDVC); + CMD_WAIT_CONNECTED); COMMAND_ISSUE(virtualCircuitList[srcVc].commandQueue, virtualCircuitList[srcVc].commandTimer, CMD_ATC); COMMAND_ISSUE(virtualCircuitList[srcVc].commandQueue, virtualCircuitList[srcVc].commandTimer, - CMD_WAIT_CONNECTED); + CMD_AT_CMDVC); } if (DISPLAY_VC_STATE) @@ -585,38 +621,25 @@ void radioToPcLinkStatus(VC_SERIAL_MESSAGE_HEADER * header, uint8_t * data, uint break; case VC_STATE_CONNECTED: + if ((previousState == VC_STATE_LINK_DOWN) && (srcVc < MAX_VC) + && (!COMMAND_PENDING(virtualCircuitList[srcVc].commandQueue, CMD_WAIT_CONNECTED))) + { + //Issue the necessary commands when the link is connected + COMMAND_ISSUE(virtualCircuitList[srcVc].commandQueue, + virtualCircuitList[srcVc].commandTimer, + CMD_WAIT_CONNECTED); + } if (DEBUG_PC_CMD_ISSUE) printf("VC %d CONNECTED\n", srcVc); - if (COMMAND_PENDING(pcCommandQueue, CMD_ATC) - && (pcActiveCommand == CMD_ATC) && (srcVc == pcCommandVc)) + if ((pcActiveCommand == CMD_ATC) && COMMAND_PENDING(pcCommandQueue, CMD_ATC)) { - if (virtualCircuitList[srcVc].activeCommand == CMD_ATC) - COMMAND_COMPLETE(virtualCircuitList[srcVc].commandQueue, virtualCircuitList[srcVc].activeCommand); - COMMAND_COMPLETE(pcCommandQueue, pcActiveCommand); + if (srcVc == pcCommandVc) + COMMAND_COMPLETE(pcCommandQueue, pcActiveCommand); + if ((pcCommandVc < MAX_VC) && (virtualCircuitList[pcCommandVc].activeCommand == CMD_ATC)) + COMMAND_COMPLETE(virtualCircuitList[pcCommandVc].commandQueue, virtualCircuitList[srcVc].activeCommand); } if (DISPLAY_VC_STATE) printf("======= VC %d CONNECTED ======\n", srcVc); - if (srcVc != myVc) - { - COMMAND_ISSUE(virtualCircuitList[srcVc].commandQueue, - virtualCircuitList[srcVc].commandTimer, - CMD_AT_CMDVC_2); - COMMAND_ISSUE(virtualCircuitList[srcVc].commandQueue, - virtualCircuitList[srcVc].commandTimer, - CMD_ATI31); - COMMAND_ISSUE(virtualCircuitList[srcVc].commandQueue, - virtualCircuitList[srcVc].commandTimer, - CMD_ATI_2); - COMMAND_ISSUE(virtualCircuitList[srcVc].commandQueue, - virtualCircuitList[srcVc].commandTimer, - CMD_ATI8_2); - COMMAND_ISSUE(virtualCircuitList[srcVc].commandQueue, - virtualCircuitList[srcVc].commandTimer, - CMD_ATI11); - COMMAND_ISSUE(virtualCircuitList[srcVc].commandQueue, - virtualCircuitList[srcVc].commandTimer, - CHECK_FOR_UPDATE); - } break; } @@ -680,26 +703,54 @@ void radioRuntime(VC_SERIAL_MESSAGE_HEADER * header, uint8_t * data, uint8_t len void radioCommandComplete(VC_SERIAL_MESSAGE_HEADER * header, uint8_t * data, uint8_t length) { + int activeCommand; VC_COMMAND_COMPLETE_MESSAGE * vcMsg; uint8_t srcVc; //The command processor is still running commandProcessorRunning = STALL_CHECK_COUNT; - //Done with this command + //Validate the srcVc srcVc = header->radio.srcVc; + if (srcVc >= PC_REMOTE_COMMAND) + { + if (srcVc < (uint8_t)VC_RSVD_SPECIAL_VCS) + srcVc &= VCAB_NUMBER_MASK; + else + switch(srcVc) + { + default: + fprintf(stderr, "ERROR: Unknown VC: %d (0x%02x)\n", srcVc, srcVc); + exit(-2); + break; + + //Ignore this command + case (uint8_t)VC_UNASSIGNED: + return; + } + } + + //Done with this command if (srcVc == myVc) { if (pcActiveCommand < CMD_LIST_SIZE) { - if (pcCommandVc < MAX_VC) + activeCommand = pcActiveCommand; + + //Done with the PC command + COMMAND_COMPLETE(pcCommandQueue, pcActiveCommand); + + //Determine if a VC command moved to the PC queue + if ((pcCommandVc < MAX_VC) + && COMMAND_PENDING(virtualCircuitList[pcCommandVc].commandQueue, + activeCommand)) { + //Done with the VC command COMMAND_COMPLETE(virtualCircuitList[pcCommandVc].commandQueue, virtualCircuitList[pcCommandVc].activeCommand); } - COMMAND_COMPLETE(pcCommandQueue, pcActiveCommand); } - else if (virtualCircuitList[pcCommandVc].activeCommand < CMD_LIST_SIZE) + else if (virtualCircuitList[srcVc].activeCommand < CMD_LIST_SIZE) { //This was a VC command COMMAND_COMPLETE(virtualCircuitList[srcVc].commandQueue, @@ -1033,7 +1084,13 @@ void issuePcCommands() } } } + + //No more PC commands to process + if (DEBUG_CMD_ISSUE && pcCommandTimer) + printf("PC command list empty\n"); pcActiveCommand = CMD_LIST_SIZE; + pcCommandTimer = 0; + pcCommandVc = MAX_VC; } } @@ -1148,9 +1205,9 @@ bool issueVcCommands(int vcIndex) { if (DEBUG_PC_CMD_ISSUE) printf("Migrating AT-CMDVC=%d and ATC commands to PC command queue\n", vcIndex); - COMMAND_ISSUE(pcCommandQueue, pcCommandTimer, CMD_AT_CMDVC); if (COMMAND_PENDING(virtualCircuitList[vcIndex].commandQueue, CMD_ATC)) COMMAND_ISSUE(pcCommandQueue, pcCommandTimer, CMD_ATC); + COMMAND_ISSUE(pcCommandQueue, pcCommandTimer, CMD_AT_CMDVC); return true; } virtualCircuitList[vcIndex].activeCommand = CMD_LIST_SIZE; @@ -1161,7 +1218,7 @@ bool issueVcCommands(int vcIndex) case CMD_WAIT_CONNECTED: if ((virtualCircuitList[vcIndex].vcState != VC_STATE_CONNECTED) - || commandProcessorBusy(vcIndex)) + || (!commandProcessorIdle(vcIndex))) { //Mark the list as empty to allow this entry to be executed again virtualCircuitList[vcIndex].activeCommand = CMD_LIST_SIZE; @@ -1172,23 +1229,37 @@ bool issueVcCommands(int vcIndex) COMMAND_COMPLETE(virtualCircuitList[vcIndex].commandQueue, virtualCircuitList[vcIndex].activeCommand); + //Get the sprinkler controller information + if (vcIndex != myVc) + { + COMMAND_ISSUE(virtualCircuitList[vcIndex].commandQueue, + virtualCircuitList[vcIndex].commandTimer, + CHECK_FOR_UPDATE); + COMMAND_ISSUE(virtualCircuitList[vcIndex].commandQueue, + virtualCircuitList[vcIndex].commandTimer, + CMD_ATI11); + COMMAND_ISSUE(virtualCircuitList[vcIndex].commandQueue, + virtualCircuitList[vcIndex].commandTimer, + CMD_ATI8_2); + COMMAND_ISSUE(virtualCircuitList[vcIndex].commandQueue, + virtualCircuitList[vcIndex].commandTimer, + CMD_ATI_2); + } + //Get the VC state - COMMAND_ISSUE(pcCommandQueue, pcCommandTimer, CMD_AT_CMDVC_2); + if (vcIndex != myVc) + COMMAND_ISSUE(virtualCircuitList[vcIndex].commandQueue, + virtualCircuitList[vcIndex].commandTimer, + CMD_ATI31); COMMAND_ISSUE(pcCommandQueue, pcCommandTimer, CMD_ATI31); + if (vcIndex != myVc) + COMMAND_ISSUE(virtualCircuitList[vcIndex].commandQueue, + virtualCircuitList[vcIndex].commandTimer, + CMD_AT_CMDVC_2); + COMMAND_ISSUE(pcCommandQueue, pcCommandTimer, CMD_AT_CMDVC_2); break; case CMD_AT_CMDVC_2: - //Determine if the local command processor is idle - if (commandProcessorIdle(vcIndex)) - { - if (DEBUG_PC_CMD_ISSUE) - printf("Migrating AT-CMDVC_2 and ATI31 commands to PC command queue\n"); - COMMAND_ISSUE(pcCommandQueue, pcCommandTimer, CMD_AT_CMDVC_2); - if (COMMAND_PENDING(virtualCircuitList[vcIndex].commandQueue, CMD_ATI31)) - COMMAND_ISSUE(pcCommandQueue, pcCommandTimer, CMD_ATI31); - return true; - } - virtualCircuitList[vcIndex].activeCommand = CMD_LIST_SIZE; return true; case CMD_ATI31: @@ -1207,21 +1278,22 @@ bool issueVcCommands(int vcIndex) return true; case CHECK_FOR_UPDATE: + //Done with the CHECK_FOR_UPDATE command + COMMAND_COMPLETE(virtualCircuitList[vcIndex].commandQueue, + virtualCircuitList[vcIndex].activeCommand); + + //Determine if the sprinkler controller needs to be programmed if ((!virtualCircuitList[vcIndex].programUpdated) || (virtualCircuitList[vcIndex].programUpdated > virtualCircuitList[vcIndex].programmed)) { //Complete the programming COMMAND_ISSUE(virtualCircuitList[vcIndex].commandQueue, virtualCircuitList[vcIndex].commandTimer, - CMD_ATI12); + PROGRAMMING_COMPLETED); COMMAND_ISSUE(virtualCircuitList[vcIndex].commandQueue, virtualCircuitList[vcIndex].commandTimer, - PROGRAMMING_COMPLETED); + CMD_ATI12); } - - //Done with the CHECK_FOR_UPDATE command - COMMAND_COMPLETE(virtualCircuitList[vcIndex].commandQueue, - virtualCircuitList[vcIndex].activeCommand); return true; case PROGRAMMING_COMPLETED: @@ -1232,8 +1304,13 @@ bool issueVcCommands(int vcIndex) } } } - virtualCircuitList[vcIndex].activeCommand = CMD_LIST_SIZE; } + + //Done processing VC commands + if (DEBUG_CMD_ISSUE && virtualCircuitList[vcIndex].commandTimer) + printf ("VC %d command list empty\n", vcIndex); + virtualCircuitList[vcIndex].activeCommand = CMD_LIST_SIZE; + virtualCircuitList[vcIndex].commandTimer = 0; } } return false; @@ -1344,10 +1421,10 @@ int main(int argc, char **argv) //Perform the initialization commands pcCommandTimer = 1; - COMMAND_ISSUE(pcCommandQueue, pcCommandTimer, CMD_ATI30); //Get myVC - COMMAND_ISSUE(pcCommandQueue, pcCommandTimer, CMD_ATI); //Get Radio type - COMMAND_ISSUE(pcCommandQueue, pcCommandTimer, CMD_ATI8); //Get Radio unique ID COMMAND_ISSUE(pcCommandQueue, pcCommandTimer, CMD_ATA); //Get all the VC states + COMMAND_ISSUE(pcCommandQueue, pcCommandTimer, CMD_ATI8); //Get Radio unique ID + COMMAND_ISSUE(pcCommandQueue, pcCommandTimer, CMD_ATI); //Get Radio type + COMMAND_ISSUE(pcCommandQueue, pcCommandTimer, CMD_ATI30); //Get myVC //Break the links if requested if (breakLinks) @@ -1424,8 +1501,10 @@ int main(int argc, char **argv) break; if (issueVcCommands(vcIndex)) { - continue; -// break; + if (ISSUE_COMMANDS_IN_PARALLEL) + continue; + else + break; } } }