diff --git a/Node Firmware/IP400/Inc/dataq.h b/Node Firmware/IP400/Inc/dataq.h index eec2c2f..05a86c5 100644 --- a/Node Firmware/IP400/Inc/dataq.h +++ b/Node Firmware/IP400/Inc/dataq.h @@ -53,6 +53,7 @@ IP400_FRAME *dequeFrame(FRAME_QUEUE *que); // queue management void insque (struct qelem *elem, struct qelem *pred); void remque (struct qelem *elem); +BOOL quehasData(FRAME_QUEUE *que); // print memory stats void Print_Memory_Stats(void); diff --git a/Node Firmware/IP400/Inc/frame.h b/Node Firmware/IP400/Inc/frame.h index 7686818..ce71533 100644 --- a/Node Firmware/IP400/Inc/frame.h +++ b/Node Firmware/IP400/Inc/frame.h @@ -38,13 +38,6 @@ #define MAX_HOP_COUNT 15 // max hop count -// transmit states -enum { - TX_IDLE=0, // idle - waiting for work - TX_SENDING, // sending a frame - TX_DONE // done -}; - // radio error register #define SEQ_COMPLETE_ERR 0x8000 // Sequencer error #define SEQ_ACT_TIMEOUT 0x4000 // Sequencer action timeout @@ -56,7 +49,7 @@ enum { #define N_RADIO_ERRS 7 // number of the above // radio FSM states -enum fsm_states_e { +typedef enum fsm_states_e { FSM_IDLE=0, // idle FSM_ENA_RF_REG, // enable RF registers FSM_WAIT_ACTIVE2, // wait for active 2 @@ -77,7 +70,7 @@ enum fsm_states_e { FSM_END_RX, // end rx FSM_SYNTH_PWDN, // synth power down FSM_N_FSM_STATES -}; +} SubGFSMState; // header flags typedef struct frame_flags_t { @@ -99,9 +92,13 @@ typedef struct ip400_mac_t { uint32_t encoded; } callbytes; union { - uint8_t ip[N_IPBYTES]; - uint16_t encip; // encoded IP data - } ipbytes; + uint8_t vpn[N_IPBYTES]; + struct { + uint8_t ax25Marker; // marker byte + uint8_t ax25SSID; // SSID + } AX25; + uint16_t encvpn; // encoded vpn address + } vpnBytes; } IP400_MAC; // hop table @@ -117,9 +114,9 @@ typedef struct ip400_frame_t { IP400_FLAGS flags; // flag bit field uint16_t allflags; // all flags } flagfld; + uint16_t length; // data length uint32_t seqNum; // packet sequence number void *buf; // data to send - uint16_t length; // data length void *hopTable; // hop table address } IP400_FRAME; @@ -147,8 +144,8 @@ typedef enum { P25_FRAME, // TIA project 25 NXDN_FRAME, // NXDN M17_FRAME, // M17 - TBD_1, - TBD_2, + ECHO_REQUEST, // echo request frame + ECHO_RESPONSE, // echo response frame LOCAL_COMMAND // local command frame } IP400FrameType; @@ -188,7 +185,9 @@ typedef struct frame_stats_t { uint32_t nRepeated; // repeated frames } FRAME_STATS; +// links in uint8_t getFrameStatus(void); +void SendBeacon(void); // references uint8_t callEncode(char *callsign, uint16_t port, IP400_FRAME *frame, uint8_t dest, uint8_t offset); @@ -196,15 +195,27 @@ BOOL callDecode(IP400_MAC *encCall, char *callsign, uint16_t *port); void EncodeChunk(char *src, int len, uint32_t *enc); // frame senders +BOOL SendBeaconFrame(uint8_t *payload, int bcnlen); BOOL SendTextFrame(char *srcCall, uint16_t srcPort, char *destCall, uint16_t dstPort, char *buf, uint16_t length, BOOL repeat); -void SendBeaconFrame(char *srcCall, uint8_t *payload, int bcnlen); +BOOL SendDataFrame(char *srcCall, uint16_t srcIPAddr, char *destCall, uint16_t dstIPAddr, uint8_t *buf, uint16_t length, uint8_t coding, BOOL repeat); +BOOL SendEchoReqFrame(char *srcCall, uint16_t srcIPAddr, char *destCall, uint16_t dstIPAddr, char *buf, uint16_t length, BOOL repeat); +// void SendSPIFrame(void *spiHdr, uint8_t *payload, int len); // BOOL EnqueChatFrame(void *Frame); // queue a chat frame FRAME_STATS *GetFrameStats(void); // return the frame stats uint32_t GetRadioStatus(void); // get the radio status -uint8_t GetFSMState(void); // get FSM state +SubGFSMState GetFSMState(void); // get FSM state // uint8_t getFrameStatus(void); // get the frame status +BOOL FrameisMine(IP400_FRAME *frame); +void RepeatFrame(IP400_FRAME *frame); +void ProcessRxFrame(IP400_FRAME *rframe, int rawLength); +void QueueTxFrame(IP400_FRAME *txframe); + +// lookup a frame in the mesh table +int getNMeshEntries(char *dest_call, int len); +IP400_MAC *getMeshEntry(char *dest_call, int len); +IP400_MAC *getNextEntry(char *dest_call, int len); #endif /* FRAME_H_ */ diff --git a/Node Firmware/IP400/Inc/ip.h b/Node Firmware/IP400/Inc/ip.h index 2c1a63f..916a7f0 100644 --- a/Node Firmware/IP400/Inc/ip.h +++ b/Node Firmware/IP400/Inc/ip.h @@ -36,6 +36,7 @@ #define IP_172_ID 2 // use ip 172 from ID #define __IP_GROUP IP_172_ID +#define IP_BROADCAST 0xFFFF // broadcast MAC address /* If your port already typedef's sa_family_t, define SA_FAMILY_T_DEFINED to prevent this code from redefining it. */ @@ -75,7 +76,7 @@ void GetIP10Addr(IP400_MAC *fr, SOCKADDR_IN *ipaddr); void GetIP172Addr(IP400_MAC *fr, SOCKADDR_IN *ipaddr); void Get172AddrFromID(IP400_MAC *fr, SOCKADDR_IN *ipaddr); -void GetIPAddrFromMAC(IP400_MAC *fr, SOCKADDR_IN *ipAddr); +void GetVPNAddrFromMAC(IP400_MAC *fr, SOCKADDR_IN *ipAddr); #if __IP_GROUP == IP_172_ID #define GetIPAddr Get172AddrFromID @@ -90,8 +91,8 @@ void GetIPAddrFromMAC(IP400_MAC *fr, SOCKADDR_IN *ipAddr); #endif // Get IP address from setup data -void GetMyIP(SOCKADDR_IN **ipAddr); +void GetMyVPN(SOCKADDR_IN **ipAddr); void GetMyMAC(IP400_MAC **mac); -uint16_t GetIPLowerWord(void); +uint16_t GetVPNLowerWord(void); #endif /* INC_IP_H_ */ diff --git a/Node Firmware/IP400/Inc/led.h b/Node Firmware/IP400/Inc/led.h index 11a938d..424e077 100644 --- a/Node Firmware/IP400/Inc/led.h +++ b/Node Firmware/IP400/Inc/led.h @@ -24,6 +24,12 @@ #define INC_LED_H_ #include + +// led command modes +typedef enum { + LED_CMD_RF, // set RF indication mode + LED_CMD_TELEM // set telemetry mode +} LEDCommand; // LED functions enum { @@ -39,7 +45,7 @@ enum { N_LED_MODE // modes1 }; -#if _BOARD_TYPE == PI_BOARD +#if (_BOARD_TYPE == PI_BOARD) || (_BOARD_TYPE == IP400_MODULE) // bidirectional LED #define LED_Green_Pin GPIO_PIN_0 #define LED_Green_GPIO_Port GPIOB @@ -55,6 +61,12 @@ enum { #define PA_ENA_GPIO_Port GPIOA #endif +#if (_BOARD_TYPE == PI_BOARD) || (_BOARD_TYPE == IP400_MODULE) || (_BOARD_TYPE == NUCLEO_BOARD) +// Set Led Mode +void SetLEDState(uint8_t mode); +#define SetLEDMode SetLEDState +#endif + #if _BOARD_TYPE == TELEM_BOARD // define the LED ports #define LED_Green_Pin GPIO_PIN_14 @@ -69,9 +81,13 @@ enum { // PA enable pin #define PA_ENA_Pin GPIO_PIN_7 #define PA_ENA_GPIO_Port GPIOA + +// set LED mode +void SetCommLEDMode(LEDCommand cmd, uint8_t mode); +void SetLEDState(uint8_t mode); +uint8_t GetLEDMode(void); +void SetConnMode(BOOL mode); +#define SetLEDMode(c) SetCommLEDMode(LED_CMD_RF, c) #endif -void SetLEDMode(uint8_t mode); - - #endif /* INC_LED_H_ */ diff --git a/Node Firmware/IP400/Inc/setup.h b/Node Firmware/IP400/Inc/setup.h index f1b328c..e82d585 100644 --- a/Node Firmware/IP400/Inc/setup.h +++ b/Node Firmware/IP400/Inc/setup.h @@ -31,6 +31,9 @@ #define __USE_SETUP_PARAMS 1 // set to 1 to use setup parameters #define US 0 // station is in the US +#define MAX_DATAFLD 10 // max data field size +#define MAX_DESC 32 // description field + // defined elsewhere extern char *modTypes[]; extern char *paModes[]; @@ -38,10 +41,9 @@ extern char *paModes[]; typedef struct setup_flags_t { unsigned fsk: 1; // can run FSK unsigned ofdm: 1; // can run OFDM - unsigned aredn: 1; // is an AREDN node + unsigned AX25: 1; // is using AX.25 compatibility addressing unsigned repeat: 1; // repeat mode default - unsigned ext: 1; // use extended callsign - unsigned rate: 3; // data rate in use + unsigned SSID: 4; // AX.25 SSID } SETUP_FLAGS; // data rates in the flag field (FSK mode) @@ -60,6 +62,7 @@ typedef struct setup_data_t { SETUP_FLAGS flags; // flags char stnCall[MAX_CALL]; // station call sign char extCall[EXT_CALL]; // extended call sign + char Description[MAX_DESC]; // description of my radio char latitude[10]; // latititude char longitude[10]; // longitude char gridSq[10]; // grid square @@ -92,10 +95,12 @@ typedef struct stn_params_t { // beacon header typedef union { struct beacon_hdr_t { - SETUP_FLAGS flags; - uint8_t txPower; - uint8_t FirmwareMajor; - uint8_t FirmwareMinor; + SETUP_FLAGS flags; // setup flags + uint8_t txPower; // transmit power + uint8_t FirmwareMajor; // firmware major version + uint8_t FirmwareMinor; // firmware minor version + uint32_t txFrequency; // transmit frequency + uint32_t rxFrequency; // receive frequency } setup; uint8_t hdrBytes[sizeof(struct beacon_hdr_t)]; } BEACON_HEADER; @@ -135,5 +140,8 @@ BOOL UpdateSetup(void); // device ID uint32_t GetDevID0(void); uint32_t GetDevID1(void); +// build ID +char *getRevID(void); +char *getDateID(void); #endif /* INC_SETUP_H_ */ diff --git a/Node Firmware/IP400/Inc/tod.h b/Node Firmware/IP400/Inc/tod.h index 2867da6..2a84154 100644 --- a/Node Firmware/IP400/Inc/tod.h +++ b/Node Firmware/IP400/Inc/tod.h @@ -37,4 +37,6 @@ void TOD_10SecTimer(void); // 10 second timer void getTOD(TIMEOFDAY *time); BOOL setTOD(char *todString); +int getElapsed(TIMEOFDAY *time); + #endif /* INC_TOD_H_ */ diff --git a/Node Firmware/IP400/Inc/usart.h b/Node Firmware/IP400/Inc/usart.h index a823ff7..8179975 100644 --- a/Node Firmware/IP400/Inc/usart.h +++ b/Node Firmware/IP400/Inc/usart.h @@ -27,6 +27,13 @@ #include "types.h" #include "main.h" +// GPS and KISS mode need LPUART; so does the telemetry board +#if (__ENABLEGPS == 1) || (__INCLUDE_KISS == 1) || (_BOARD_TYPE == TELEM_BOARD) // board type in use +#define ENABLE_LPUART 1 +#else +#define ENABLE_LPUART 0 +#endif + // definitions typedef uint32_t UART_TIMEOUT_T; // uart timer type typedef uint16_t BUFFER_SIZE_T; // buffer size type @@ -53,10 +60,17 @@ BOOL USART_Send_String(const char *string, size_t len); BOOL USART_Send_Char(const char c); void USART_Print_string(char *format, ...); -// same as USART but for LPUART: no Tx functions +// same as USART but for LPUART void LPUART_RxBuffer_reset(void); -size_t gpsbuffer_bytesInBuffer(void); -DATA_ELEMENT gpsbuffer_get(UART_TIMEOUT_T timeout); void LPUART_Send_String(char *str, uint16_t len); +size_t lpuart_bytesInBuffer(void); +DATA_ELEMENT lpuart_buffer_get(UART_TIMEOUT_T timeout); + +#if ENABLE_LPUART && __ENABLE_GPS +#define gpsbuffer_bytesInBuffer lpuart_bytesInBuffer +#define gpsbuffer_get lpuart_buffer_get +#endif + + #endif /* INC_USART_H_ */ diff --git a/Node Firmware/IP400/Inc/utils.h b/Node Firmware/IP400/Inc/utils.h index 85ddb7d..99eac09 100644 --- a/Node Firmware/IP400/Inc/utils.h +++ b/Node Firmware/IP400/Inc/utils.h @@ -23,9 +23,22 @@ #ifndef INC_UTILS_H_ #define INC_UTILS_H_ +/* + * unsigned conversion routines + */ +uint8_t A2_uint8_t(char *s); +uint16_t A2_uint16_t(char *s); +uint32_t A2_uint32_t(char *s); + +/* + * Signed integer + */ // replacement for missing itoa int ascii2Dec(char *dec); +/* + * Signed double + */ // and its double counterpart... double ascii2double(char *val); @@ -35,6 +48,11 @@ void hex2ascii(uint8_t hex, char *buf); // check an entry for floating point BOOL isfloat(char *val); +// case/numeric checkers +BOOL isUpper(char c); +BOOL isLower(char c); +BOOL isNumeric(char c); + // useful in parsing NMEA sentences int explode_string(char *str, char *strp[], int limit, char delim, char quote); diff --git a/Node Firmware/IP400/Src/beacon.c b/Node Firmware/IP400/Src/beacon.c index 7d87577..7ecabbf 100644 --- a/Node Firmware/IP400/Src/beacon.c +++ b/Node Firmware/IP400/Src/beacon.c @@ -189,26 +189,42 @@ void Beacon_Task_exec(void) return; } timerCtrValue = timerInitValue; + SendBeacon(); #if __ENABLE_GPS // send a command to the GPS every beacon interval sendGPSCmd(); #endif +} + +void SendBeacon(void) +{ // start with the header beacon_hdr.setup.flags = setup_memory.params.setup_data.flags; beacon_hdr.setup.txPower = setup_memory.params.radio_setup.outputPower; + beacon_hdr.setup.txFrequency = setup_memory.params.radio_setup.lFrequencyBase; + beacon_hdr.setup.rxFrequency = setup_memory.params.radio_setup.lFrequencyBase; uint8_t *buf = bcnPayload; - // beacon header: flags, txpower and firmware version + // beacon header: flags, txpower // brute force copy: compiler rounds SETUP_FLAGS to 32 bits *buf++ = beacon_hdr.hdrBytes[0]; *buf++ = beacon_hdr.hdrBytes[4]; + // tx frequency + for(int i=0;idest.ipbytes.encip = ipAddr; + frame->dest.vpnBytes.encvpn = ipAddr; else - frame->source.ipbytes.encip = ipAddr; + frame->source.vpnBytes.encvpn = ipAddr; // broadcast address if(!strcmp(callsign, "FFFF")) { @@ -189,7 +189,7 @@ BOOL callDecode(IP400_MAC *encCall, char *callsign, uint16_t *ipAddr) *callsign = '\0'; if(ipAddr != NULL) - *ipAddr = encCall->ipbytes.encip; + *ipAddr = encCall->vpnBytes.encvpn; return TRUE; } diff --git a/Node Firmware/IP400/Src/chat.c b/Node Firmware/IP400/Src/chat.c index f646649..4a0c33d 100644 --- a/Node Firmware/IP400/Src/chat.c +++ b/Node Firmware/IP400/Src/chat.c @@ -32,15 +32,18 @@ #include "streambuffer.h" #include "dataq.h" #include "ip.h" +#include "utils.h" -#define MAX_KEY 140 // max keys in a buffer -#define MAX_DEST 20 // max chars in dest callsign -#define BROADCAST "FFFF" // broadcast address +#define MAX_KEY 140 // max keys in a buffer +#define MAX_DEST 20 // max chars in dest callsign +#define BROADCAST_CALL "FFFF" // broadcast address +#define BROADCAST_IP 0xFFFF // broadcast IP address static char keyBuffer[MAX_KEY]; // buffer for keystrokes uint8_t keyPos; // current position #define KEY_EOL 0x0D // carriage return +#define KEY_ECHO 0x05 // enter echo mode #define KEY_RPT 0x12 // change repeat status #define KEY_EXIT 0x1A // exit key #define KEY_ESC 0x1B // escape key @@ -48,7 +51,11 @@ uint8_t keyPos; // current position #define KEY_BKSP 0x08 // backspace key #define KEY_DUMP 0x04 // toggle dump mode +#define ECHO_TIME 50 // echo send time + char dest_call[MAX_DEST]; // broadcast destination +uint8_t dest_idx; // index +uint16_t dest_ip; // destination IP address char *dp = dest_call; // pointer to dest call characters char *entCall = "Enter Destination Callsign"; char *rptMode[] = { @@ -61,24 +68,43 @@ char *dumpStrings[] = { "Dump mode->on" }; -char *welcome = "Welcome to chat. ESC to set destination, CTRL/R to toggle repeat, CTRL/Z to exit"; +// destination MAC's +IP400_MAC *DestMacs; + +// echo modes +enum echoModes { + ECHO_MODE_OFF=0, // off + ECHO_MODE_MANUAL, // manual + ECHO_MODE_TIMED, // timed + N_ECHO_MODES // number of modes +}; + + +char *welcome = "Welcome to chat. \r\nESC to set destination, CTRL/R to toggle repeat,\r\nCTRL/E to enter echo mode, CTRL/Z to exit\r\n"; BOOL destEnt = FALSE; // not entering destination BOOL repeat = TRUE; // repeating by default BOOL deleteMode = FALSE; // delete mode BOOL welcomed = FALSE; // welcome mat is out -BOOL dumpMode = FALSE; +BOOL dumpMode = FALSE; // frame dump mode +uint8_t echoMode = ECHO_MODE_OFF; // echo mode +uint8_t echoTimer; // echo mode timer FRAME_QUEUE chatQueue; // queue for inbound frames // fwd refs void sendLine(char *buffer, int len); +void sendEchoreq(void); void PrintFrame(IP400_FRAME *FrameBytes); +void ListAllMeshEntries(char *call, int len, int nMACEntries); +BOOL splitCall(char *dest_call, uint8_t *dest_idx, char *keyBuffer, int keyLen, uint8_t *adjLen); +void GetNthMeshentry(char *dest_call, int cpyLen, uint8_t index, int nMACEntries, IP400_MAC **mac); // init entry void Chat_Task_init(void) { - strcpy(dest_call, BROADCAST); + strcpy(dest_call, BROADCAST_CALL); + dest_ip = BROADCAST_IP; destEnt = FALSE; chatQueue.q_forw = &chatQueue; chatQueue.q_back = &chatQueue; @@ -91,10 +117,10 @@ void Chat_Task_welcome(void) USART_Print_string("%s\r\n", welcome); USART_Print_string("%s; ", rptMode[repeat]); - if(!strcmp(dest_call, BROADCAST)) - USART_Print_string("%s; Destination callsign->(Broadcast)\r\n\n"); + if(!strcmp(dest_call, BROADCAST_CALL)) + USART_Print_string("Destination callsign->(Broadcast)\r\n\n"); else - USART_Print_string("%s; Destination callsign->%s\r\n\n", dest_call); + USART_Print_string("Destination callsign->%s\r\n\n", dest_call); welcomed = TRUE; } @@ -108,12 +134,17 @@ BOOL EnqueChatFrame(void *raw) IP400_FRAME *qFrame, *SrcFrame = (IP400_FRAME *)raw; uint8_t *frameBuffer; + if(SrcFrame->flagfld.flags.coding == ECHO_RESPONSE) { + PrintFrame(SrcFrame); + return TRUE; + } + // if the welcome mat is not out, then discard any pending frames if(!welcomed) - return FALSE; + return FALSE; // allocate an IP400 frame - if((qFrame=malloc(sizeof(IP400_FRAME)))== NULL) + if((qFrame=malloc(sizeof(IP400_FRAME))) == NULL) return FALSE; memcpy(qFrame, SrcFrame, sizeof(IP400_FRAME)); @@ -139,12 +170,20 @@ BOOL EnqueChatFrame(void *raw) */ BOOL Chat_Task_exec(void) { - if(!welcomed) + if(!welcomed) { + strcpy(dest_call, BROADCAST_CALL); + dest_ip = BROADCAST_IP; + echoMode = FALSE; Chat_Task_welcome(); + } char c; int nBytesinBuff; + int nMACEntries; IP400_FRAME *fr; + IP400_MAC *destMac; + BOOL hasIndex = FALSE; + uint8_t cpyLen; // process any inbound frames first.. if((fr=dequeFrame(&chatQueue)) != NULL) { @@ -153,6 +192,15 @@ BOOL Chat_Task_exec(void) free(fr); } + if(echoMode == ECHO_MODE_TIMED) { + echoTimer++; + if(echoTimer == ECHO_TIME) { + echoTimer = 0; + sendEchoreq(); + USART_Print_string("Timed echo request sent\r\n"); + } + } + if((nBytesinBuff=databuffer_bytesInBuffer()) == 0) return FALSE; @@ -180,6 +228,35 @@ BOOL Chat_Task_exec(void) switch (c) { + // CTRL/E: enter echo mode + case KEY_ECHO: + echoMode = (echoMode + 1) % N_ECHO_MODES; + if(echoMode != ECHO_MODE_OFF) { + if(!strcmp(dest_call, BROADCAST_CALL)) { + USART_Print_string("Echo cannot be sent to a broadcast address\r\n"); + echoMode = ECHO_MODE_OFF; + break; + } + } + + // set the echo mode + switch(echoMode) { + + case ECHO_MODE_OFF: // off + USART_Print_string("Echo mode off\r\n"); + break; + + case ECHO_MODE_MANUAL: + USART_Print_string("Manual echo mode on\r\n"); + break; + + case ECHO_MODE_TIMED: // timed + echoTimer = 0; + USART_Print_string("Timed echo mode on\r\n"); + break; + } + break; + // CTRL/R: change repeat flag case KEY_RPT: repeat = repeat ? FALSE : TRUE; @@ -196,12 +273,6 @@ BOOL Chat_Task_exec(void) // escape key: get a destination call sign case KEY_ESC: - if(destEnt) { - strcpy(dest_call, BROADCAST); - USART_Print_string("Destination set to broadcast\r\n"); - destEnt=FALSE; - break; - } if(keyPos == 0) { USART_Print_string("%s->", entCall); dp = dest_call; @@ -212,16 +283,64 @@ BOOL Chat_Task_exec(void) // EOL key: sent the packet case KEY_EOL: USART_Print_string("\r\n"); + // in destination entry mode if(destEnt) { - int cpyLen = keyPos > MAX_CALL ? MAX_CALL : keyPos; - strncpy(dest_call, keyBuffer, cpyLen); - destEnt = FALSE; + if(keyPos == 0) { + strcpy(dest_call, BROADCAST_CALL); + dest_ip = BROADCAST_IP; + USART_Print_string("Destination set to broadcast\r\n"); + destEnt=FALSE; + break; + } + memset(dest_call, 0, MAX_DEST); + hasIndex = splitCall(dest_call, &dest_idx, keyBuffer, keyPos, &cpyLen); + nMACEntries=getNMeshEntries(dest_call, cpyLen); + + switch(nMACEntries) { + + // not found.. + case 0: + USART_Print_string("Destination address %s not found in Mesh table\r\n", dest_call); + destEnt = FALSE; + break; + + // single entry found + case 1: + destMac = getMeshEntry(dest_call, cpyLen); + dest_ip = destMac->vpnBytes.encvpn; + USART_Print_string("Destination address set to %s\r\n", dest_call); + destEnt = FALSE; + break; + + // multiple entries found + default: + if(hasIndex) { + GetNthMeshentry(dest_call, cpyLen, dest_idx, nMACEntries, &destMac); + dest_ip = destMac->vpnBytes.encvpn; + } else { + destMac = getMeshEntry(dest_call, cpyLen); + dest_ip = IP_BROADCAST; + ListAllMeshEntries(dest_call, cpyLen, nMACEntries); + } + destEnt = FALSE; + break; + } } else { if(keyPos != 0) { - keyBuffer[keyPos++] = '\0'; - sendLine(keyBuffer, keyPos); + keyBuffer[keyPos++] = '\0'; + sendLine(keyBuffer, keyPos); } else { - USART_Print_string(">>>not sent\r\n"); + if(!echoMode) { + USART_Print_string("Nothing sent\r\n"); + } else { + + if(!strcmp(dest_call, BROADCAST_CALL)) { + USART_Print_string("Echo cannot be sent to a broadcast address\r\n"); + } else { + sendEchoreq(); + USART_Print_string("Echo request sent\r\n"); + } + } } } keyPos = 0; @@ -240,6 +359,8 @@ BOOL Chat_Task_exec(void) break; default: + if(destEnt) + c = isLower(c) ? toupper(c) : c; USART_Send_Char(c); if(keyPos < MAX_KEY) keyBuffer[keyPos++] = c; @@ -253,7 +374,114 @@ BOOL Chat_Task_exec(void) // send a line of text void sendLine(char *buffer, int len) { - SendTextFrame(setup_memory.params.setup_data.stnCall, GetIPLowerWord(), dest_call, 0xFFFF, buffer, len, repeat); + SendTextFrame(setup_memory.params.setup_data.stnCall, GetVPNLowerWord(), dest_call, dest_ip, buffer, len, repeat); +} + +void sendEchoreq(void) +{ + char buffer[50]; + + strcpy(buffer, "Echo request frame"); + int len = strlen(buffer); + + + SendEchoReqFrame(setup_memory.params.setup_data.stnCall, GetVPNLowerWord(), dest_call, dest_ip, buffer, len, FALSE); +} + +/* + * List all the mesh entries with a given callsign + */ +void ListAllMeshEntries(char *call, int len, int nMACEntries) +{ + struct ip400MAC_t { + IP400_MAC *MacEntry; + }; + + struct ip400MAC_t *MacEntries; + IP400_MAC *macEntry; + SOCKADDR_IN ipAddr; + + if((MacEntries = (struct ip400MAC_t *) malloc(nMACEntries * sizeof(struct ip400MAC_t))) == NULL) { + USART_Print_string("?An error has occurred, cannot allocate mesh table entries\r\n"); + return; + } + + // announce how many have been found + USART_Print_string("Call sign %s has %d mesh table entries\r\n", call, nMACEntries); + + // get the entries + int nEntries=0; + if((macEntry = getMeshEntry(call, len)) == NULL) { + USART_Print_string("?An error has occurred, cannot find any mesh table entries for %s\r\n", call); + free(MacEntries); + return; + } + MacEntries[nEntries++].MacEntry = macEntry; + + // get the rest of the entries + while((macEntry = getNextEntry(call, len)) != NULL) + MacEntries[nEntries++].MacEntry = macEntry; + + // now display them + for(int i=0;i nMACEntries)) { + USART_Print_string("Callsign index is out of range\r\n"); + return; + } + if((macEntry = getMeshEntry(dest_call, cpyLen)) == NULL) { + USART_Print_string("?An error has occurred, cannot find any mesh table entries for %s\r\n", dest_call); + return; + } + nEntries = 1; + while(nEntries < index) { + if((macEntry = getNextEntry(dest_call, cpyLen)) == NULL) { + USART_Print_string("Not enough entries to satisfy index %d\r\n", index); + return; + } + nEntries++; + } + + // announce the selection + GetVPNAddrFromMAC(macEntry, &ipAddr); + USART_Print_string("Destination address set to %s(%d.%d.%d.%d)\r\n\n", dest_call, + ipAddr.sin_addr.S_un.S_un_b.s_b1, ipAddr.sin_addr.S_un.S_un_b.s_b2, + ipAddr.sin_addr.S_un.S_un_b.s_b3, ipAddr.sin_addr.S_un.S_un_b.s_b4); + + *mac = macEntry; } /* @@ -280,7 +508,7 @@ void PrintFrame(IP400_FRAME *FrameBytes) // source call callDecode(&FrameBytes->source, decCall, NULL); - GetIPAddrFromMAC(&FrameBytes->source, &fromIP); + GetVPNAddrFromMAC(&FrameBytes->source, &fromIP); USART_Print_string("%s(%d.%d.%d.%d) ", decCall, fromIP.sin_addr.S_un.S_un_b.s_b1, fromIP.sin_addr.S_un.S_un_b.s_b2, @@ -292,15 +520,21 @@ void PrintFrame(IP400_FRAME *FrameBytes) USART_Print_string("BROADCAST"); } else { callDecode(&FrameBytes->dest, decCall, NULL); - USART_Print_string("%s", decCall);; + GetVPNAddrFromMAC(&FrameBytes->dest, &fromIP); + USART_Print_string("%s(%d.%d.%d.%d) ", decCall, + fromIP.sin_addr.S_un.S_un_b.s_b1, fromIP.sin_addr.S_un.S_un_b.s_b2, + fromIP.sin_addr.S_un.S_un_b.s_b3, fromIP.sin_addr.S_un.S_un_b.s_b4); } // flags USART_Print_string("[%d:%04d]:", FrameBytes->flagfld.flags.hop_count, FrameBytes->seqNum); // now dump the data - memcpy(printBuf, FrameBytes->buf, dataLen); - printBuf[dataLen] = '\0'; - USART_Print_string("%s\r\n", printBuf); - + if(FrameBytes->flagfld.flags.coding == ECHO_RESPONSE) { + USART_Print_string("Echo response\r\n"); + } else { + memcpy(printBuf, FrameBytes->buf, dataLen); + printBuf[dataLen] = '\0'; + USART_Print_string("%s\r\n", printBuf); + } } diff --git a/Node Firmware/IP400/Src/dataq.c b/Node Firmware/IP400/Src/dataq.c index 7ff73c9..aba656c 100644 --- a/Node Firmware/IP400/Src/dataq.c +++ b/Node Firmware/IP400/Src/dataq.c @@ -64,3 +64,14 @@ IP400_FRAME *dequeFrame(FRAME_QUEUE *que) free(f); return ipFrame; } + +/* + * Test to see if anything is queued + */ +BOOL quehasData(FRAME_QUEUE *que) +{ + if(que->q_back == que) + return FALSE; + + return TRUE; +} diff --git a/Node Firmware/IP400/Src/frame.c b/Node Firmware/IP400/Src/frame.c index 0f6c99b..1921e45 100644 --- a/Node Firmware/IP400/Src/frame.c +++ b/Node Firmware/IP400/Src/frame.c @@ -34,115 +34,106 @@ #include #endif -#include -#include -#include - #include "frame.h" #include "dataq.h" #include "setup.h" -#include "tasks.h" -#include "led.h" -#include "spi.h" -#include "usart.h" #include "ip.h" +#include "spi.h" +#include "tasks.h" -// local defines -#define RX_TIMEOUT 2 // return to OS after 5 ms timeout - -// conditionals -#define __DUMP_BEACON 0 // dump a beacon frame to console using chat - -// locals -uint8_t txState; // transmitter state -uint8_t radioCmd; // current radio command -uint8_t prevCmd; // previous state -FRAME_QUEUE txQueue; // transmitter frame queue -uint32_t subgIRQStatus; // interrupt status -BOOL txDone; // transmitter is done -BOOL rxDone; // rx is done... -BOOL rxReady; // frame ready for further processing -FRAME_STATS Stats; // collected stats +// local defn's uint32_t nextSeq; // next frame sequence number -// from setup... -int16_t rxSquelch; // rx squlech - - -// internal fwd references -void QueueTxFrame(IP400_FRAME *txframe); // send a frame -void EnableRx(void); // enable the rx - -// processed frames -static IP400_FRAME rFrame; -static HOPTABLE rxHopTable[MAX_HOP_COUNT]; - -/* - * These are in dedicated buffer memory - */ -#if USE_BUFFER_RAM -static uint8_t rawTxFrame[MAX_FRAME_SIZE] __attribute__((section("BUFFERS"), aligned(4))); -static uint8_t rawRxFrame[MAX_FRAME_SIZE] __attribute__((section("BUFFERS"), aligned(4))); -#else -static uint8_t rawTxFrame[MAX_FRAME_SIZE]; -static uint8_t rawRxFrame[MAX_FRAME_SIZE]; -#endif - -// intialize the transmit task void Frame_task_init(void) { - txState = TX_IDLE; - txQueue.q_forw = &txQueue; - txQueue.q_back = &txQueue; - - // init stats and counters - memset(&Stats, 0, sizeof(FRAME_STATS)); nextSeq = 0xFFFFFFFF; - - // set Rx threshold - STN_PARAMS *params = GetStationParams(); - rxSquelch = params->radio_setup.rxSquelch; - HAL_MRSubG_SetRSSIThreshold(rxSquelch); - - // enable the interrupt - __HAL_MRSUBG_SET_RFSEQ_IRQ_ENABLE( - MR_SUBG_GLOB_DYNAMIC_RFSEQ_IRQ_ENABLE_RX_OK_E - | MR_SUBG_GLOB_DYNAMIC_RFSEQ_IRQ_ENABLE_TX_DONE_E - | MR_SUBG_GLOB_DYNAMIC_RFSEQ_IRQ_ENABLE_RX_TIMEOUT_E - | MR_SUBG_GLOB_DYNAMIC_RFSEQ_IRQ_ENABLE_RX_CRC_ERROR_E - ); - HAL_NVIC_EnableIRQ(MR_SUBG_IRQn); - - // enable the Rx - rxDone = FALSE; - rxReady = FALSE; - EnableRx(); } /* - * return the stats + * ------------------------------------------------------------------------ + * Frame Transmission handlers + * ------------------------------------------------------------------------ */ -FRAME_STATS *GetFrameStats(void) -{ - return &Stats; -} -// return the radio status -uint32_t GetRadioStatus(void) -{ - uint32_t radioStats = READ_REG(MR_SUBG_GLOB_STATUS->RFSEQ_STATUS_DETAIL); - return radioStats; -} -// get the FSM state -uint8_t GetFSMState(void) -{ - uint32_t fsmState = READ_REG(MR_SUBG_GLOB_STATUS->RADIO_FSM_INFO); - return (uint8_t)(fsmState & MR_SUBG_GLOB_STATUS_RADIO_FSM_INFO_RADIO_FSM_STATE_Msk); -} /* - * Send a text frame. Buf is not malloc'ed, can be static in ram + * Send a text frame. Buffer is not malloc'ed, can be static in ram + * This function was deprecated and uses SendDataFrame instead, + * complete with the correct coding type */ BOOL SendTextFrame(char *srcCall, uint16_t srcIPAddr, char *destCall, uint16_t dstIPAddr, char *buf, uint16_t length, BOOL repeat) +{ + + return SendDataFrame(srcCall, srcIPAddr, destCall, dstIPAddr, (uint8_t *)buf, length, UTF8_TEXT_PACKET, repeat); + +} + +/* + * compose and send a beacon frame (ping frame with broadcast destination) + * Same comment about SendDataFrame. + */ +BOOL SendBeaconFrame(uint8_t *payload, int bcnlen) +{ + char *destCall = "FFFF"; + uint16_t destIP = 0xFFFF; // broadcast destination address + + uint16_t srcIPAddr = GetVPNLowerWord(); // get my IP lower bits + + return SendDataFrame(setup_memory.params.setup_data.stnCall, srcIPAddr, destCall, destIP, payload, bcnlen, BEACON_PACKET, TRUE); +} + +/* + * Send an echo request frame + */ +BOOL SendEchoReqFrame(char *srcCall, uint16_t srcIPAddr, char *destCall, uint16_t dstIPAddr, char *buf, uint16_t length, BOOL repeat) +{ + return SendDataFrame(srcCall, srcIPAddr, destCall, dstIPAddr, (uint8_t *)buf, length, ECHO_REQUEST, FALSE); +} + +/* + * send an echo response + */ +BOOL SendEchoRespFrame(IP400_FRAME *reqFrame) +{ + IP400_FRAME *echoFrame; + + uint16_t length = reqFrame->length; + + if((echoFrame=malloc(sizeof(IP400_FRAME))) == NULL) + return FALSE; + + if((echoFrame->buf=malloc(length + MAX_CALL_BUFFER)) == NULL) + return FALSE; + + // swap source/destination + echoFrame->source.callbytes.encoded = reqFrame->dest.callbytes.encoded; + echoFrame->source.vpnBytes.encvpn = reqFrame->dest.vpnBytes.encvpn; + + echoFrame->dest.callbytes.encoded = reqFrame->source.callbytes.encoded; + echoFrame->dest.vpnBytes.encvpn = reqFrame->source.vpnBytes.encvpn; + + // copy the payload in + uint8_t *f = (uint8_t *)echoFrame->buf; + memcpy(f, (const uint8_t *)reqFrame->buf, length); + + echoFrame->length = length; + echoFrame->flagfld.allflags = 0; // start with all flags cleared + echoFrame->flagfld.flags.hop_count = 0; + echoFrame->flagfld.flags.coding = ECHO_RESPONSE; + echoFrame->flagfld.flags.repeat = FALSE; + echoFrame->flagfld.flags.hoptable = 0; + echoFrame->hopTable = NULL; + echoFrame->seqNum = nextSeq++; + + QueueTxFrame(echoFrame); + + return TRUE; +} + + +/* + * Send a generic data frame + */ +BOOL SendDataFrame(char *srcCall, uint16_t srcIPAddr, char *destCall, uint16_t dstIPAddr, uint8_t *buf, uint16_t length, uint8_t coding, BOOL repeat) { IP400_FRAME *txFrame; @@ -156,7 +147,7 @@ BOOL SendTextFrame(char *srcCall, uint16_t srcIPAddr, char *destCall, uint16_t d // format the header uint8_t offset = callEncode(srcCall, srcIPAddr, txFrame, SRC_CALLSIGN, 0); - offset = callEncode(destCall, dstIPAddr, txFrame, DEST_CALLSIGN, offset); + offset += callEncode(destCall, dstIPAddr, txFrame, DEST_CALLSIGN, offset); // copy the payload in uint8_t *f = (uint8_t *)txFrame->buf; @@ -165,7 +156,7 @@ BOOL SendTextFrame(char *srcCall, uint16_t srcIPAddr, char *destCall, uint16_t d txFrame->length = length + offset; txFrame->flagfld.flags.hop_count = 0; - txFrame->flagfld.flags.coding |= UTF8_TEXT_PACKET; + txFrame->flagfld.flags.coding = coding; txFrame->flagfld.flags.repeat = repeat; txFrame->flagfld.flags.hoptable = 0; txFrame->hopTable = NULL; @@ -177,41 +168,9 @@ BOOL SendTextFrame(char *srcCall, uint16_t srcIPAddr, char *destCall, uint16_t d } /* - * compose and send a beacon frame (ping frame with broadcast destination + * Send a frame received on the SPI + * NB: input frame has a different format */ -void SendBeaconFrame(char *srcCall, uint8_t *payload, int bcnlen) -{ - IP400_FRAME *bcnFrame; - - if((bcnFrame=malloc(sizeof(IP400_FRAME))) == NULL) - return; - - if((bcnFrame->buf=malloc(bcnlen + MAX_CALL_BUFFER)) == NULL) - return; - - bcnFrame->flagfld.allflags = 0; // start with all flags cleared - - // broadcast frame - uint16_t ipLower = GetIPLowerWord(); - uint8_t offset = callEncode(srcCall, ipLower, bcnFrame, SRC_CALLSIGN, 0); - callEncode("FFFF", 0xFFFF, bcnFrame, DEST_CALLSIGN, 0); - - // adjust starting data point, add payload - uint8_t *f = (uint8_t *)bcnFrame->buf; - f += offset*sizeof(uint32_t); - memcpy(f, payload, bcnlen); - - bcnFrame->length = bcnlen + offset; - bcnFrame->flagfld.flags.hop_count = 0; - bcnFrame->flagfld.flags.coding |= BEACON_PACKET; - bcnFrame->flagfld.flags.repeat = TRUE; - bcnFrame->flagfld.flags.hoptable = 0; - bcnFrame->hopTable = NULL; - bcnFrame->seqNum = nextSeq++; - - QueueTxFrame(bcnFrame); -} - void SendSPIFrame(void *spi, uint8_t *payload, int len) { IP400_FRAME *spiFrame; @@ -259,10 +218,10 @@ void SendSPIFrame(void *spi, uint8_t *payload, int len) srcCall = myMac->callbytes.bytes; memcpy(spiFrame->source.callbytes.bytes, srcCall, N_CALL); - memcpy(spiFrame->source.ipbytes.ip, spiHdr->fromIP, N_IPBYTES); + memcpy(spiFrame->source.vpnBytes.vpn, spiHdr->fromIP, N_IPBYTES); memcpy(spiFrame->dest.callbytes.bytes, spiHdr->toCall, N_CALL); - memcpy(spiFrame->source.ipbytes.ip, spiHdr->toIP, N_IPBYTES); + memcpy(spiFrame->dest.vpnBytes.vpn, spiHdr->toIP, N_IPBYTES); spiFrame->flagfld.allflags = (uint16_t)(spiHdr->hopCount) + (uint16_t)(spiHdr->coding<<4) + (uint16_t)(spiHdr->flags<<8); if(spiFrame->flagfld.flags.hop_count) @@ -282,8 +241,9 @@ BOOL FrameisMine(IP400_FRAME *frame) char decCall[30]; // check if I am the originator call sign + uint16_t myIPAddr = GetVPNLowerWord(); callDecode(&frame->source, decCall, NULL); - if(CompareToMyCall(decCall)) + if(CompareToMyCall(decCall) && (frame->source.vpnBytes.encvpn == myIPAddr)) return TRUE; // now check to see if I repeated this frame @@ -295,7 +255,7 @@ BOOL FrameisMine(IP400_FRAME *frame) IP400_MAC *myMac; GetMyMAC(&myMac); for(int i=0;iflagfld.flags.hop_count; i++) - if(htable[i].hopAddr.callbytes.encoded == myMac->callbytes.encoded) + if((htable[i].hopAddr.callbytes.encoded == myMac->callbytes.encoded) && (htable[i].hopAddr.vpnBytes.encvpn == myIPAddr)) return TRUE; return FALSE; @@ -339,235 +299,57 @@ void RepeatFrame(IP400_FRAME *frame) IP400_MAC *myMac; GetMyMAC(&myMac); table[hopCount].hopAddr.callbytes.encoded = myMac->callbytes.encoded; - table[hopCount].hopAddr.ipbytes.encip = myMac->ipbytes.encip; + table[hopCount].hopAddr.vpnBytes.encvpn = myMac->vpnBytes.encvpn; rptFrame->flagfld.flags.hoptable = TRUE; rptFrame->flagfld.flags.hop_count = hopCount + 1; - Stats.nRepeated++; + FRAME_STATS *stats = GetFrameStats(); + stats->nRepeated++; + QueueTxFrame(rptFrame); } /* - * queue a frame for transmission by the tx task + * ------------------------------------------------------------------------ + * Frame Reception handlers + * ------------------------------------------------------------------------ */ -void QueueTxFrame(IP400_FRAME *txframe) -{ - enqueFrame(&txQueue, txframe); -} /* - * Enable the receiver + * Process a received frame */ -void EnableRx(void) +void ProcessRxFrame(IP400_FRAME *rFrame, int rawLength) { - __HAL_MRSUBG_SET_RX_MODE(RX_NORMAL); - __HAL_MRSUBG_SET_DATABUFFER_SIZE(MAX_FRAME_SIZE); - MR_SUBG_GLOB_STATIC->DATABUFFER0_PTR = (uint32_t)&rawRxFrame; - - // set the command - radioCmd = CMD_RX; - __HAL_MRSUBG_STROBE_CMD(radioCmd); - - SetLEDMode(BICOLOR_GREEN); -} - -/* - * main entry for tx task. Pick frames from the transmit queue - */ -void Frame_Txtask_exec(void) -{ - static IP400_FRAME *tFrame; - int frameLen = 0; - - switch(txState) { - - // idle: waiting for work - case TX_IDLE: - - tFrame = dequeFrame(&txQueue); - uint8_t *rawFrame = (uint8_t *)rawTxFrame; - - if(tFrame == NULL) - return; - - frameLen = tFrame->length; - - /* - * Build the raw frame bytes: see IP400_FRAME struct - */ - // Source call + port (6 bytes) - memcpy(rawFrame, (uint8_t *)&tFrame->source, IP_400_CALL_SIZE); - rawFrame += IP_400_CALL_SIZE; - // Dest call + port (6 bytes) - memcpy(rawFrame, (uint8_t *)&tFrame->dest, IP_400_CALL_SIZE); - rawFrame += IP_400_CALL_SIZE; - // flag byte (2 byte) - memcpy(rawFrame, (uint8_t *)&tFrame->flagfld, IP_400_FLAG_SIZE); - rawFrame += IP_400_FLAG_SIZE; - // frame sequence number (4 bytes) - memcpy(rawFrame, (uint32_t *)&tFrame->seqNum, sizeof(uint32_t)); - rawFrame += sizeof(uint32_t); - // frame length (2 bytes) - memcpy(rawFrame, (uint8_t *)&tFrame->length, sizeof(uint16_t)); - rawFrame += IP_400_LEN_SIZE; - - // add in the hop table - if(tFrame->flagfld.flags.hoptable) { - uint16_t hopLen = (uint16_t)(tFrame->flagfld.flags.hop_count) * sizeof(HOPTABLE); - memcpy(rawFrame, (uint8_t *)(tFrame->hopTable), hopLen); - rawFrame += hopLen; - free(tFrame->hopTable); - } - - // and now the data... - if((tFrame->buf != NULL) && (tFrame->length != 0)) - memcpy(rawFrame, tFrame->buf, tFrame->length); - - // free the allocations in the reverse order... - if(tFrame->buf != NULL) - free(tFrame->buf); - - free(tFrame); - - // ensure packet length is a multiple of 4 bytes - int pktLen = (rawFrame - rawTxFrame) + frameLen; - pktLen += (pktLen % 4); - - HAL_MRSubG_PktBasicSetPayloadLength(frameLen + pktLen); - - // abort the current rx operation - if(radioCmd == CMD_RX) { - __HAL_MRSUBG_STROBE_CMD(CMD_SABORT); - uint32_t reject=0, abortDone=0; - do { - subgIRQStatus = READ_REG(MR_SUBG_GLOB_STATUS->RFSEQ_IRQ_STATUS); - reject = subgIRQStatus & MR_SUBG_GLOB_STATUS_RFSEQ_IRQ_STATUS_COMMAND_REJECTED_F; - abortDone = subgIRQStatus & MR_SUBG_GLOB_STATUS_RFSEQ_IRQ_STATUS_SABORT_DONE_F; - } while ((abortDone == 0) && (reject == 0)); - if(abortDone) - __HAL_MRSUBG_CLEAR_RFSEQ_IRQ_FLAG(MR_SUBG_GLOB_STATUS_RFSEQ_IRQ_STATUS_SABORT_DONE_F); - if(reject) - __HAL_MRSUBG_CLEAR_RFSEQ_IRQ_FLAG(MR_SUBG_GLOB_STATUS_RFSEQ_IRQ_STATUS_COMMAND_REJECTED_F); - } - - __HAL_MRSUBG_SET_DATABUFFER0_POINTER((uint32_t)rawTxFrame); - __HAL_MRSUBG_SET_TX_MODE(TX_NORMAL); - - txDone = FALSE; - prevCmd = radioCmd; - radioCmd = CMD_TX; - __HAL_MRSUBG_STROBE_CMD(radioCmd); - - // set tx indication: bicolor off and Tx on - SetLEDMode(BICOLOR_OFF); - SetLEDMode(TX_LED_ON); - - txState = TX_SENDING; - break; - - // sending a frame - case TX_SENDING: - // still busy sending - if(!txDone) - return; - - txState = TX_DONE; - break; - - // done - case TX_DONE: - // restart the receiver, if needed - EnableRx(); - - SetLEDMode(TX_LED_OFF); - txState = TX_IDLE; - break; - } -} - -/* - * Main entry of the rx task - */ -void Frame_Rxtask_exec(void) -{ - // wait for completion.. - if(!rxDone) - return; - rxDone = FALSE; - - uint8_t *RxRaw = rawRxFrame; - uint8_t *cpyDest; - uint32_t rawLength = __HAL_MRSUBG_GET_DATABUFFER_SIZE(); - - /* - * Do the opposite of the transmitter... - */ - // Source call + port (6 bytes) - cpyDest = (uint8_t *)&rFrame.source.callbytes.bytes; - memcpy(cpyDest, RxRaw, IP_400_CALL_SIZE); - RxRaw += IP_400_CALL_SIZE; - - // Dest call + port (6 bytes) - cpyDest = (uint8_t *)&rFrame.dest.callbytes.bytes; - memcpy(cpyDest, RxRaw, IP_400_CALL_SIZE); - RxRaw += IP_400_CALL_SIZE; - - // flag byte (2 byte) - cpyDest = (uint8_t *)&rFrame.flagfld.allflags; - memcpy(cpyDest, RxRaw, IP_400_FLAG_SIZE); - RxRaw += IP_400_FLAG_SIZE; - - // frame sequence number (4 bytes) - cpyDest = (uint8_t *)&rFrame.seqNum; - memcpy(cpyDest, RxRaw, sizeof(uint32_t)); - RxRaw += sizeof(uint32_t); - - // frame length (2 bytes) - cpyDest = (uint8_t *)&rFrame.length; - memcpy(cpyDest, RxRaw, sizeof(uint16_t)); - RxRaw += IP_400_LEN_SIZE; - - // copy the hop table - uint8_t nHops = rFrame.flagfld.flags.hop_count; - if(nHops != 0) { - uint16_t hopLen = (uint16_t)nHops*sizeof(HOPTABLE); - memcpy(rxHopTable, RxRaw, hopLen); - RxRaw += hopLen; - rFrame.hopTable = rxHopTable; - } else { - rFrame.hopTable = NULL; - } - - rFrame.buf = RxRaw; + FRAME_STATS *stats = GetFrameStats(); // find a reason to reject a frame... - BOOL isMine = FrameisMine(&rFrame); + BOOL isMine = FrameisMine(rFrame); // process the frame if it is not mine and unique // do a sanity check on the length - if(!isMine && (rFrame.length < rawLength)) { + if(!isMine && (rFrame->length < rawLength)) { - IP400FrameType frameType = rFrame.flagfld.flags.coding; + IP400FrameType frameType = rFrame->flagfld.flags.coding; switch(frameType) { // process a beacon frame case BEACON_PACKET: - if(Mesh_Accept_Frame((void *)&rFrame, Stats.lastRSSI)) { - Mesh_ProcessBeacon((void *)&rFrame, Stats.lastRSSI); + if(Mesh_Accept_Frame((void *)rFrame, stats->lastRSSI)) { + Mesh_ProcessBeacon((void *)rFrame, stats->lastRSSI); #if __DUMP_BEACON EnqueChatFrame((void *)&rFrame); #endif - EnqueSPIFrame(&rFrame); - Stats.nBeacons++; + EnqueSPIFrame(rFrame); + stats->nBeacons++; } break; // process a local chat frame case UTF8_TEXT_PACKET: - if(Mesh_Accept_Frame((void *)&rFrame, Stats.lastRSSI)) { - EnqueChatFrame((void *)&rFrame); - Stats.framesOK++; + if(Mesh_Accept_Frame((void *)rFrame, stats->lastRSSI)) { + EnqueChatFrame((void *)rFrame); + stats->framesOK++; } break; @@ -583,9 +365,22 @@ void Frame_Rxtask_exec(void) case P25_FRAME: // TIA project 25 case NXDN_FRAME: // NXDN case M17_FRAME: // M17 - if(Mesh_Accept_Frame((void *)&rFrame, Stats.lastRSSI)) { - EnqueSPIFrame((void *)&rFrame); - Stats.framesOK++; + if(Mesh_Accept_Frame((void *)rFrame, stats->lastRSSI)) { + EnqueSPIFrame((void *)rFrame); + stats->framesOK++; + } + break; + + // echo request frame + case ECHO_REQUEST: + SendEchoRespFrame(rFrame); + break; + + // echo response: treat it like a chat frame + case ECHO_RESPONSE: + if(Mesh_Accept_Frame((void *)rFrame, stats->lastRSSI)) { + EnqueChatFrame((void *)rFrame); + stats->framesOK++; } break; @@ -594,54 +389,19 @@ void Frame_Rxtask_exec(void) break; default: - Stats.dropped++; - logger(LOG_ERROR, "Frame Received with unknown coding: %d\r\n", rFrame.flagfld.flags.coding); + stats->dropped++; + logger(LOG_ERROR, "Frame Received with unknown coding: %d\r\n", rFrame->flagfld.flags.coding); break; } } // repeat the frame if the repeat flag is set and the hop count is not exhausted if(!isMine) { - if(rFrame.flagfld.flags.repeat && (rFrame.flagfld.flags.hop_count < MAX_HOP_COUNT)) - RepeatFrame(&rFrame); + if(rFrame->flagfld.flags.repeat && (rFrame->flagfld.flags.hop_count < MAX_HOP_COUNT)) + RepeatFrame(rFrame); } if(isMine) - Stats.dropped++; - - // restart the receiver - EnableRx(); - + stats->dropped++; } -// frame interrupt callback -void HAL_MRSubG_IRQ_Callback(void) -{ - subgIRQStatus = READ_REG(MR_SUBG_GLOB_STATUS->RFSEQ_IRQ_STATUS); - // Process transmitter interrupts - if(subgIRQStatus & MR_SUBG_GLOB_STATUS_RFSEQ_IRQ_STATUS_TX_DONE_F) { - __HAL_MRSUBG_CLEAR_RFSEQ_IRQ_FLAG(MR_SUBG_GLOB_STATUS_RFSEQ_IRQ_STATUS_TX_DONE_F); - Stats.TxFrameCnt++; - txDone = TRUE; - } - - // process receiver interrupts - if(subgIRQStatus & MR_SUBG_GLOB_STATUS_RFSEQ_IRQ_STATUS_RX_CRC_ERROR_F) { - Stats.CRCErrors++; - __HAL_MRSUBG_CLEAR_RFSEQ_IRQ_FLAG(MR_SUBG_GLOB_STATUS_RFSEQ_IRQ_STATUS_RX_CRC_ERROR_F); - } - - if (subgIRQStatus & MR_SUBG_GLOB_STATUS_RFSEQ_IRQ_STATUS_RX_TIMEOUT_F) { - Stats.TimeOuts++; - __HAL_MRSUBG_CLEAR_RFSEQ_IRQ_FLAG(MR_SUBG_GLOB_STATUS_RFSEQ_IRQ_STATUS_RX_TIMEOUT_F); - } - - if (subgIRQStatus & MR_SUBG_GLOB_STATUS_RFSEQ_IRQ_STATUS_RX_OK_F ) { - Stats.RxFrameCnt++; - __HAL_MRSUBG_CLEAR_RFSEQ_IRQ_FLAG(MR_SUBG_GLOB_STATUS_RFSEQ_IRQ_STATUS_RX_OK_F); - Stats.lastRSSI = READ_REG_FIELD(MR_SUBG_GLOB_STATUS->RX_INDICATOR, MR_SUBG_GLOB_STATUS_RX_INDICATOR_RSSI_LEVEL_ON_SYNC); - rxDone = TRUE; - } - - EnableRx(); -} diff --git a/Node Firmware/IP400/Src/ip.c b/Node Firmware/IP400/Src/ip.c index f10ec9f..7741920 100644 --- a/Node Firmware/IP400/Src/ip.c +++ b/Node Firmware/IP400/Src/ip.c @@ -51,55 +51,6 @@ union { #define NETMASK 0xF8 // mask for last byte -#ifdef __OLDIPSCHEME -/* - * Create an IP 10.x.x.x from the compressed callsign field - */ -void GetIP10Addr(IP400_MAC *fr, SOCKADDR_IN *ipaddr) -{ - ipaddr->sin_family = AF_INET; - - // first byte is fixed - ipaddr->sin_addr.S_un.S_un_b.s_b1 = IP_10_NETWORK & netmask10[0]; - - // next two are sums of callsign bytes - ipaddr->sin_addr.S_un.S_un_b.s_b2 = (fr->callbytes.bytes[0] ^ fr->callbytes.bytes[2]) & netmask10[1]; - ipaddr->sin_addr.S_un.S_un_b.s_b3 = (fr->callbytes.bytes[1] ^ fr->callbytes.bytes[3]) & netmask10[2]; - - // last digit from sum - int macsum = 0; - for (int i = 0; i < N_CALL; i++) - macsum += fr->callbytes.bytes[i]; - ipaddr->sin_addr.S_un.S_un_b.s_b4 = (uint8_t)(macsum & netmask10[3]); - - // port number from source - ipaddr->sin_port = 0; -} - - -void GetIP172Addr(IP400_MAC *fr, SOCKADDR_IN *ipaddr) -{ - ipaddr->sin_family = AF_INET; - ipaddr->sin_addr.S_un.S_addr = 0; - - // first byte is fixed - ipaddr->sin_addr.S_un.S_un_b.s_b1 = IP_172_NETWORK & netmask172[0]; - - // next two are sums of callsign bytes - uint8_t b3 = (fr->callbytes.bytes[0] ^ fr->callbytes.bytes[2]) & netmask172[2]; - uint8_t b4 = (fr->callbytes.bytes[1] ^ fr->callbytes.bytes[3]) & netmask172[3]; - uint8_t b2 = (b3 + b4) & 0xf; - - // compose the address - ipaddr->sin_addr.S_un.S_un_b.s_b2 = b2 + IP_172_START; - ipaddr->sin_addr.S_un.S_un_b.s_b3 = b3; - ipaddr->sin_addr.S_un.S_un_b.s_b4 = b4; - - // port number from source - ipaddr->sin_port = 0; -} -#endif - /* * Create an IP 172.16.x.x from the compressed callsign field and unique ID */ @@ -131,7 +82,7 @@ void Get172AddrFromID(IP400_MAC *fr, SOCKADDR_IN *ipaddr) /* * return the IP Address in an IP_400 MAC */ -void GetIPAddrFromMAC(IP400_MAC *fr, SOCKADDR_IN *ipAddr) +void GetVPNAddrFromMAC(IP400_MAC *fr, SOCKADDR_IN *ipAddr) { ipAddr->sin_family = AF_INET; ipAddr->sin_addr.S_un.S_addr = 0; @@ -146,8 +97,8 @@ void GetIPAddrFromMAC(IP400_MAC *fr, SOCKADDR_IN *ipAddr) ipAddr->sin_addr.S_un.S_un_b.s_b2 = b2 + IP_172_START; // remainder from the ip Address field - ipAddr->sin_addr.S_un.S_un_b.s_b3 = fr->ipbytes.ip[1]; - ipAddr->sin_addr.S_un.S_un_b.s_b4 = fr->ipbytes.ip[0]; + ipAddr->sin_addr.S_un.S_un_b.s_b3 = fr->vpnBytes.vpn[1]; + ipAddr->sin_addr.S_un.S_un_b.s_b4 = fr->vpnBytes.vpn[0]; // port number from source ipAddr->sin_port = 0; @@ -155,7 +106,7 @@ void GetIPAddrFromMAC(IP400_MAC *fr, SOCKADDR_IN *ipAddr) } -uint16_t GetIPLowerWord(void) +uint16_t GetVPNLowerWord(void) { uniqueID.word = GetDevID0() ^ GetDevID1(); diff --git a/Node Firmware/IP400/Src/led.c b/Node Firmware/IP400/Src/led.c index a6b6cdf..4530eb8 100644 --- a/Node Firmware/IP400/Src/led.c +++ b/Node Firmware/IP400/Src/led.c @@ -27,22 +27,22 @@ * Red solid when HAL error or NMI occurred * * On the NUCLEO board, there are three, red, green and blue - * BLUE (LD1) Duplicats the TX LED + * BLUE (LD1) Duplicates the TX LED * GREEN (LD2) Duplicates the GREEN bi-color function * RED (LD3) Duplicates the RED Bi-color function */ +#include #include "types.h" #include "led.h" #include "usart.h" -#include + // include LED defs from the right place #if _BOARD_TYPE == NUCLEO_BOARD #include #endif - // local defines #define FLASH_OFF 0 // LED flashing: off state #define FLASH_ON 1 // LED flashing: on state @@ -52,11 +52,10 @@ #define TEST_TIMER 20 // test timer // internals -void SetLEDMode(uint8_t mode); +void SetLEDState(uint8_t mode); void LED_SetOff(void); void LED_SetRed(void); void LED_SetGreen(void); -void LED_SetError(void); void setTxLED(BOOL state); // vars @@ -80,11 +79,19 @@ struct led_tests_t { char *testName; uint8_t testMode; } LEDTests[N_LED] = { +#if _BOARD_TYPE == NUCLEO_BOARD + {"RED On", BICOLOR_RED }, + {"GREEN On", BICOLOR_GREEN }, + {"GREEN only", BICOLOR_OFF }, + {"BLUE On", TX_LED_ON }, + {"BLUE Off", TX_LED_OFF } +#else {"Bicolor RED On", BICOLOR_RED }, {"Bicolor GREEN On", BICOLOR_GREEN }, {"Bicolor off", BICOLOR_OFF }, {"Tx LED On", TX_LED_ON }, {"Tx LED Off", TX_LED_OFF } +#endif }; // Initialization void Led_Task_Init(void) @@ -149,12 +156,12 @@ BOOL LedTest(void) saveMode = ledMode; if(testNum == N_LED) { testNum = 0; - SetLEDMode(saveMode); + SetLEDState(saveMode); return TRUE; } USART_Print_string("%s\r\n", LEDTests[testNum].testName); - SetLEDMode(LEDTests[testNum].testMode); + SetLEDState(LEDTests[testNum].testMode); testTimer = TEST_TIMER; testNum++; return FALSE; @@ -164,8 +171,14 @@ BOOL LedTest(void) return FALSE; } -// API routines: set the LED mode -void SetLEDMode(uint8_t mode) +// API routine: get the LED mode +uint8_t GetLEDMode() +{ + return ledMode; +} + +// API routines: set the LED state for nodes only +void SetLEDState(uint8_t mode) { ledMode = mode; diff --git a/Node Firmware/IP400/Src/menu.c b/Node Firmware/IP400/Src/menu.c index 8429eff..29107b3 100644 --- a/Node Firmware/IP400/Src/menu.c +++ b/Node Firmware/IP400/Src/menu.c @@ -31,6 +31,11 @@ #include "tod.h" #include "config.h" #include "ip.h" +#include "frame.h" + +//macros +#define tolower(c) (c+0x20) +#define toupper(c) (c-0x20) // menu state uint8_t menuState; // menu state @@ -91,6 +96,8 @@ char *selectItem = "Select an item->"; static char menu[100]; // Buffer for file items int sel_item = 0; // selected item uint8_t activeMenu; // active menu +uint8_t editMode; // current entry editing mode +uint8_t maxEntry; // max entry length // forward refs in this module void printMenu(void); // print the menu @@ -103,6 +110,7 @@ void Print_Radio_errors(uint32_t errs); void Print_FSM_state(uint8_t state); uint8_t getEntry(int activeMenu, int item); uint8_t getKeyEntry(void); +char editEntry(char c); // list of menus enum { @@ -119,6 +127,35 @@ enum { RET_PAUSE // pause before leaving }; +// case values +enum { + ENTRY_ANYCASE, // upper and lower case + ENTRY_UPPERCASE, // upper case only + ENTRY_LOWERCASE, // lower case only + ENTRY_NUMERIC, // numeric only, 0-9 + '-' + ENTRY_FLOAT, // floating point 0-9 + '.' + '-' + ENTRY_TIME, // numeric + ':' + ENTRY_NONE // none of the above +}; + +// keyboard entry constants + +// keys in entry mode +#define KEY_EOL 0x0D // carriage return +#define KEY_ESC 0x1B // escape key +#define KEY_DEL 0x7F // delete key +#define KEY_BKSP 0x08 // backspace key + +#define MAX_ENTRY 40 // max entry chars +#define MAX_FLDSIZE MAX_ENTRY-2 // max field size entry +#define MAX_DATASIZE MAX_DATAFLD-2 // max data field size +#define MAX_FLOATSIZE MAX_DATASIZE+1 // max float entry size +#define MAX_TIMESIZE 5 // max timesize + +BOOL delMode = FALSE; +int pos = 0; +char keyBuffer[MAX_ENTRY]; + /* * The first part of this code contains the menus and processing routines * handle the various menu options @@ -130,13 +167,20 @@ uint8_t printAllSetup(void) getTOD(&tod); SOCKADDR_IN *ipAddr; - USART_Print_string("Current firmware is at %d.%d\r\n",def_params.params.FirmwareVerMajor, - def_params.params.FirmwareVerMinor); + strcpy(menu, getRevID()); + menu[strlen(menu)-1] = '\0'; + USART_Print_string("Firmware version: %d.%d, %s\r\n",def_params.params.FirmwareVerMajor, + def_params.params.FirmwareVerMinor, menu); + + strcpy(menu, getDateID()); + menu[strlen(menu)-1] = '\0'; + USART_Print_string("Build %s\r\n", menu); + USART_Print_string("System time is %02d:%02d:%02d\r\n", tod.Hours, tod.Minutes, tod.Seconds); USART_Print_string("Radio ID is %08x%08x\r\n", GetDevID0(), GetDevID1()); - GetMyIP(&ipAddr); - USART_Print_string("IP Address %d.%d.%d.%d\r\n\n",ipAddr->sin_addr.S_un.S_un_b.s_b1, ipAddr->sin_addr.S_un.S_un_b.s_b2, + GetMyVPN(&ipAddr); + USART_Print_string("VPN Address %d.%d.%d.%d\r\n\n",ipAddr->sin_addr.S_un.S_un_b.s_b1, ipAddr->sin_addr.S_un.S_un_b.s_b2, ipAddr->sin_addr.S_un.S_un_b.s_b3, ipAddr->sin_addr.S_un.S_un_b.s_b4); printStationSetup(); printRadioSetup(); @@ -174,10 +218,17 @@ uint8_t showstats(void) { Print_Frame_stats(GetFrameStats()); Print_Radio_errors(GetRadioStatus()); - Print_FSM_state(GetFSMState()); + Print_FSM_state((uint8_t )GetFSMState()); return RET_PAUSE; } +// Send a beacon frame now... +uint8_t sendBeacon(void) +{ + SendBeacon(); + return RET_DONE; +} + // set the radio entry mode uint8_t setRadio(void) { @@ -253,6 +304,8 @@ struct menuItems_t { char *menuLine; // text of menu line char selChar; // character to select it uint8_t (*func)(void); // processing function + uint8_t entryMode; // entry mode + uint8_t fldSize; // size of entry field }; // main menu @@ -268,53 +321,65 @@ struct menuItems_t { #define N_MEM 0 #endif -#define N_MAINMENU (11+N_GPS+N_MEM) // additional menu item for GPS +#define N_MAINMENU (12+N_GPS+N_MEM) // additional menu item for GPS struct menuItems_t mainMenu[N_MAINMENU] = { - { "List setup parameters\r\n", 'A', printAllSetup }, - { "Mesh Status\r\n", 'B', listMesh }, - { "Chat Mode\r\n", 'C', chatMode }, - { "Dump Frame stats\r\n", 'D', showstats }, + { "List setup parameters\r\n", 'A', printAllSetup, ENTRY_NONE, 0 }, + { "Mesh Status\r\n", 'B', listMesh, ENTRY_NONE, 0 }, + { "Chat/Echo Mode\r\n", 'C', chatMode, ENTRY_NONE, 0 }, + { "Dump Frame stats\r\n", 'D', showstats, ENTRY_NONE, 0 }, + { "Send Beacon\r\n", 'E', sendBeacon, ENTRY_NONE, 0 }, #if __ENABLE_GPS - { "GPS Echo mode\r\n", 'G', gpsEcho }, + { "GPS Echo mode\r\n", 'G', gpsEcho, ENTRY_NONE, 0 }, #endif - { "LED test\r\n", 'L', ledTest }, + { "LED test\r\n", 'L', ledTest, ENTRY_NONE, 0 }, #if __MEM_DEBUG - { "Memory Status\r\n", 'M', memStats }, + { "Memory Status\r\n", 'M', memStats, ENTRY_NONE, 0 }, #endif - { "Set Radio Parameters\r\n", 'R', setRadio }, - { "Set Station Parameters\r\n", 'S', setStation }, - { "Set clock (HH:MM)\r\n\n", 'T', setParam }, - { "Write Setup Values\r\n", 'W', writeSetup }, - { "Exit\r\n\n", 'X', exitMenu } + { "Set Radio Parameters\r\n", 'R', setRadio, ENTRY_NONE, 0 }, + { "Set Station Parameters\r\n", 'S', setStation, ENTRY_NONE, 0 }, + { "Set clock (HH:MM)\r\n\n", 'T', setParam, ENTRY_TIME, MAX_TIMESIZE }, + { "Write Setup Values\r\n", 'W', writeSetup, ENTRY_NONE, 0 }, + { "Exit\r\n\n", 'X', exitMenu, ENTRY_NONE, 0 } }; // radio menu #define N_RADIOMENU 8 struct menuItems_t radioMenu[N_RADIOMENU] = { - { "RF Frequency\r\n", 'A', setParam }, - { "Data Rate\r\n", 'B', setParam }, - { "Peak Deviation\r\n", 'C', setParam }, - { "Channel Filter BW\r\n", 'D', setParam }, - { "Output Power (dBm)\r\n", 'E', setParam }, - { "Rx Squelch (dBm)\r\n\n", 'F', setParam }, - { "List Settings\r\n", 'L', printRadSetup }, - { "Return to main menu\r\n\n", 'X', exitMenu } + { "RF Frequency\r\n", 'A', setParam, ENTRY_FLOAT, MAX_FLDSIZE }, + { "Data Rate\r\n", 'B', setParam, ENTRY_FLOAT, MAX_FLDSIZE }, + { "Peak Deviation\r\n", 'C', setParam, ENTRY_FLOAT, MAX_FLDSIZE }, + { "Channel Filter BW\r\n", 'D', setParam, ENTRY_FLOAT, MAX_FLDSIZE }, + { "Output Power (dBm)\r\n", 'E', setParam, ENTRY_NUMERIC, MAX_FLDSIZE }, + { "Rx Squelch (dBm)\r\n\n", 'F', setParam, ENTRY_NUMERIC, MAX_FLDSIZE }, + { "List Settings\r\n", 'L', printRadSetup, ENTRY_NONE, MAX_FLDSIZE }, + { "Return to main menu\r\n\n", 'X', exitMenu, ENTRY_NONE, MAX_FLDSIZE } }; // these need to correspond to the items above // station menu -#define N_STATIONMENU 8 +#if __AX25_COMPATIBILITY +#define NAX25 2 +#else +#define NAX25 0 +#endif + +#define N_STATIONMENU 11+NAX25 struct menuItems_t stationMenu[N_STATIONMENU] = { - { "Callsign\r\n", 'A', setParam }, - { "Latitude\r\n", 'B', setParam }, - { "Longitude\r\n", 'C', setParam }, - { "Grid Square\r\n", 'D', setParam }, - { "Repeat Default\r\n", 'E', setParam }, - { "Beacon Interval\r\n\n", 'F', setParam }, - { "List Settings\r\n", 'L', printStnSetup }, - { "Return to main menu\r\n\n", 'X', exitMenu } + { "Callsign\r\n", 'A', setParam, ENTRY_UPPERCASE, MAX_CALL }, + { "Description\r\n", 'B', setParam, ENTRY_ANYCASE, MAX_DESC }, + { "Latitude\r\n", 'C', setParam, ENTRY_FLOAT, MAX_FLOATSIZE }, + { "Longitude\r\n", 'D', setParam, ENTRY_FLOAT, MAX_FLOATSIZE }, + { "Grid Square\r\n", 'E', setParam, ENTRY_ANYCASE, MAX_CALL }, + { "Repeat Mode(Y/N)\r\n", 'F', setParam, ENTRY_UPPERCASE, 1 }, + { "Beacon Interval\r\n", 'G', setParam, ENTRY_NUMERIC, MAX_FLDSIZE }, +#if __AX25_COMPATIBILITY + { "AX.25 Compatibility Mode(Y/N)\r\n", 'H', setParam, ENTRY_UPPERCASE, 1 }, + { "AX.25 SSID\r\n\n", 'I', setParam, ENTRY_NUMERIC, MAX_FLDSIZE }, +#endif + { "List Settings\r\n", 'L', printStnSetup, ENTRY_NONE, 0 }, + { "Return to main menu\r\n\n", 'X', exitMenu, ENTRY_NONE, 0 } }; // menu contents @@ -387,6 +452,8 @@ void Menu_Task_Exec(void) case MENU_SELECTED: m=menuContents[activeMenu].menus; m += sel_item; + editMode = m->entryMode; + maxEntry = m->fldSize; switch((*m->func)()) { case RET_MORE: @@ -497,17 +564,6 @@ BOOL pause(void) * This part pertains to getting an entry and validating it.. */ -// keys in entry mode -#define KEY_EOL 0x0D // carriage return -#define KEY_ESC 0x1B // escape key -#define KEY_DEL 0x7F // delete key -#define KEY_BKSP 0x08 // backspace key - -#define MAX_ENTRY 40 // max entry chars - -BOOL delMode = FALSE; -int pos = 0; -char keyBuffer[MAX_ENTRY]; // forward refs uint8_t validateEntry(int activeMenu, int item, char *keyBuffer); @@ -519,6 +575,7 @@ uint8_t getKeyEntry(void) { char c; + char edited; int nBytesinBuff; if((nBytesinBuff=databuffer_bytesInBuffer()) == 0) @@ -531,14 +588,14 @@ uint8_t getKeyEntry(void) if(delMode) { if((c != KEY_DEL) && (c != KEY_BKSP)) { USART_Print_string("\\%c", c); - if(pos < MAX_ENTRY) + if(pos < maxEntry) keyBuffer[pos++] = c; delMode = FALSE; } else { if(pos > 0) { USART_Print_string("%c", keyBuffer[--pos]); } else { - USART_Print_string("\\\r\n"); + USART_Print_string("\\\r\n->"); delMode = FALSE; } } @@ -562,14 +619,23 @@ uint8_t getKeyEntry(void) case KEY_DEL: case KEY_BKSP: - USART_Print_string("\\%c", keyBuffer[--pos]); - delMode = TRUE; + if(pos > 0) { + USART_Print_string("\\%c", keyBuffer[--pos]); + delMode = TRUE; + } else { + delMode = FALSE; + } + break; default: - USART_Send_Char(c); - if(pos < MAX_ENTRY) - keyBuffer[pos++] = c; + if((edited=editEntry(c)) != 0) { + // don't echo the entry if it is over the field size + if(pos < maxEntry) { + USART_Send_Char(edited); + keyBuffer[pos++] = edited; + } + } break; } } @@ -577,6 +643,46 @@ uint8_t getKeyEntry(void) return RET_MORE; } +/* + * edit an entry in progress + */ +char editEntry(char c) +{ + switch(editMode) { + + case ENTRY_ANYCASE: // upper and lower case + case ENTRY_NONE: // none of the above + return (c); + + case ENTRY_UPPERCASE: // upper case only + if(isLower(c)) + return(toupper(c)); + return c; + + case ENTRY_LOWERCASE: // lower case only + if(isUpper(c)) + return(tolower(c)); + return c; + + case ENTRY_NUMERIC: // numeric only, 0-9 + '-' + if(isNumeric(c) || (c=='-')) + return c; + return 0; + + case ENTRY_FLOAT: // floating point 0-9 + '.' + '-' + if(isNumeric(c) || (c=='-') || (c=='.')) + return c; + return 0; + + case ENTRY_TIME: // numeric + ':' + if(isNumeric(c) || (c==':')) + return c; + return 0; + + } + return c; +} + /* * Get an entry and validate it */ @@ -613,18 +719,20 @@ uint8_t getEntry(int activeMenu, int item) // data types we are updating enum { + uint4_lo, // uint4 lsb + uint4_hi, // uint4 msb uint8_type, // uint8 field int16_type, // int16 field uint32_type, // uint32 field float_type, // floating point char_type, // character type - field_type // field type + yesno_type // yes/no type }; // struct to hold validation values typedef struct field_validator_t { - int MinVal; // minimum value - int MaxVal; // maximum value + int MinVal; // minimum value or string length + int MaxVal; // maximum value or string length void *setupVal; // pointer to setup value int type; // type of entry uint32_t scalar; // scalar to convert to decimal @@ -641,12 +749,15 @@ FIELD_VALIDATOR radioValidators[] = { }; FIELD_VALIDATOR stationValidators[] = { - { 4, 6, &setup_memory.params.setup_data.stnCall, char_type, 0 }, + { 4, MAX_CALL, &setup_memory.params.setup_data.stnCall, char_type, 0 }, + { 1, MAX_DESC, &setup_memory.params.setup_data.Description, char_type, 0 }, { 2, 14, &setup_memory.params.setup_data.latitude, char_type, 0 }, { 2, 14, &setup_memory.params.setup_data.longitude, char_type, 0 }, - { 6, 6, &setup_memory.params.setup_data.gridSq, char_type, 0 }, - { 0, 0x8, &setup_memory.params.setup_data.flags, field_type, 0 }, - { 1, 100, &setup_memory.params.setup_data.beaconInt, int16_type, 0 } + { 4, 6, &setup_memory.params.setup_data.gridSq, char_type, 0 }, + { 0x8, 0, &setup_memory.params.setup_data.flags, yesno_type, 0 }, + { 1, 100, &setup_memory.params.setup_data.beaconInt, int16_type, 0 }, + { 0x4, 0, &setup_memory.params.setup_data.flags, yesno_type, 0 }, + { 0, 15, &setup_memory.params.setup_data.flags, uint4_hi, 0 } }; uint8_t validateEntry(int activeMenu, int item, char *keyBuffer) @@ -664,13 +775,14 @@ uint8_t validateEntry(int activeMenu, int item, char *keyBuffer) case RADIO_MENU: // convert a floating point entry to the required decimal if(isfloat(keyBuffer)) - newValue = (int)(ascii2double(keyBuffer)*radioValidators[item].scalar); + newValue = (int)(ascii2double(keyBuffer)*radioValidators[item].scalar); else newValue = ascii2Dec(keyBuffer); max = radioValidators[item].MaxVal; min = radioValidators[item].MinVal; if((newValuemax)) { USART_Print_string("Must be in the range of %d to %d\r\n", min, max); - return RET_PAUSE; + USART_Print_string("Field not updated\r\n"); + return RET_PAUSE; } switch(radioValidators[item].type){ @@ -695,13 +807,26 @@ uint8_t validateEntry(int activeMenu, int item, char *keyBuffer) case STATION_MENU: switch(stationValidators[item].type){ + case uint4_lo: + newValue = ascii2Dec(keyBuffer); + uint8_t *v4lo = (uint8_t *)stationValidators[item].setupVal; + *v4lo = (*v4lo & 0xF0) | (newValue & 0x0F); + break; + + case uint4_hi: + newValue = ascii2Dec(keyBuffer); + uint8_t *v4hi = (uint8_t *)stationValidators[item].setupVal; + *v4hi = (*v4hi & 0x0F) | (newValue<<4); + break; + case int16_type: newValue = ascii2Dec(keyBuffer); max = stationValidators[item].MaxVal; min = stationValidators[item].MinVal; if((newValuemax)) { USART_Print_string("Must be in the range of %d to %d\r\n", min, max); - return RET_PAUSE; + USART_Print_string("Field not updated\r\n"); + return RET_PAUSE; } uint16_t *v8 = (uint16_t *)stationValidators[item].setupVal; *v8 = (uint16_t)newValue; @@ -713,22 +838,27 @@ uint8_t validateEntry(int activeMenu, int item, char *keyBuffer) min = stationValidators[item].MinVal; if((lenmax)) { USART_Print_string("String must be %d to %d in length\r\n", min, max); - return RET_PAUSE; + USART_Print_string("Field not updated\r\n"); + return RET_PAUSE; } strcpy((char *)stationValidators[item].setupVal, keyBuffer); break; - case field_type: - uint8_t val = keyBuffer[0] == 'T' ? 1 : 0; - uint8_t *f8= (uint8_t *)stationValidators[item].setupVal; - if(val) - *f8 |= (uint8_t)stationValidators[item].MinVal; - else - *f8 &= (uint8_t)stationValidators[item].MinVal; - break; + case yesno_type: + if((keyBuffer[0] == 'Y') || (keyBuffer[0] == 'N')) { + uint8_t val = keyBuffer[0] == 'Y' ? 1 : 0; + uint8_t *f8= (uint8_t *)stationValidators[item].setupVal; + if(val) + *f8 |= (uint8_t)stationValidators[item].MinVal; + else + *f8 &= ~((uint8_t)stationValidators[item].MinVal); + } else { + USART_Print_string("Please enter Y or N\r\n"); + USART_Print_string("Field not updated\r\n"); + return RET_PAUSE; + } } break; - } // falls to here when done... @@ -795,15 +925,15 @@ char *fsm_states[FSM_N_FSM_STATES] = { "Synth setup", "VCO calibration", "Lock Rx and Rx", - "Llock on Rx", + "Lock on Rx", "Enable PA", "Transmit", "Analog power down", "End transmit", - "lock on Rx", + "Lock on Rx", "Enable Rx", "Enable LNA", - "Recieve", + "Receive", "End rx", "Synth power down" }; diff --git a/Node Firmware/IP400/Src/mesh.c b/Node Firmware/IP400/Src/mesh.c index 2218ec8..98c5b26 100644 --- a/Node Firmware/IP400/Src/mesh.c +++ b/Node Firmware/IP400/Src/mesh.c @@ -38,17 +38,6 @@ #define EXPIRY_TIME 100 // seconds since last heard #define RSSI_SCALAR 161 // 95-Gain(rx), appros 65 -char *FSKSpeeds[] = { - "FSK 1200", - "FSK 9600", - "FSK_56K", - "FSK_100K", - "FSK_200K", - "FSK_300K", - "FSK_400K", - "FSK_600K" -}; - // table entry status typedef enum { MESHTBL_UNUSED=0, // unused entry @@ -68,6 +57,10 @@ typedef struct mesh_entry_t { uint8_t hopCount; // hop count } MESH_ENTRY; +// time constants +#define MAX_MISSING (30*60) // 30 minute max elapsed time +#define MAX_LOST (60*60) // one hour to gone... + // mesh table definitions #define MAX_MESH_MEMORY 2048 // max amount of mesh memory used #define MAX_MESH_ENTRIES (MAX_MESH_MEMORY/sizeof(MESH_ENTRY)) @@ -76,12 +69,13 @@ typedef struct mesh_entry_t { // table size is limited to by memory defintion static MESH_ENTRY MeshTable[MAX_MESH_ENTRIES] __attribute__((section("MESHTABLE"))); int nMeshEntries = 0; +int lastEntryNum = 0; BEACON_HEADER mesh_bcn_hdr; // beacon header uint32_t seqNum; // sequence number received // forward refs in this module -int findCall(IP400_MAC *macAddr); +int findCall(IP400_MAC *call, int start); void AddMeshEntry(IP400_FRAME *frameData, int16_t rssi, BOOL isBeacon); // task initialization @@ -105,8 +99,8 @@ void Mesh_ProcessBeacon(void *rxFrame, uint32_t rssi) // see if we already know about it // if so, just update last heard, expected sequence and rssi - // NB: firt frame is sent with an all '1's sequence number - if((entryNum = findCall(&frameData->source)) != ENTRY_NOTFOUND) { + // NB: first frame is sent with an all '1's sequence number + if((entryNum = findCall(&frameData->source, 0)) != ENTRY_NOTFOUND) { // if it is repeated frame with a higher hop count, ignore it if(MeshTable[entryNum].hopCount < frameData->flagfld.flags.hop_count) @@ -127,7 +121,8 @@ void Mesh_ProcessBeacon(void *rxFrame, uint32_t rssi) MeshTable[entryNum].capabilities = mesh_bcn_hdr.setup.flags; MeshTable[entryNum].txPower = mesh_bcn_hdr.setup.txPower; - // all done + // all done: change status to OK + MeshTable[entryNum].status = MESHTBL_VALID; return; } @@ -160,7 +155,7 @@ void AddMeshEntry(IP400_FRAME *frameData, int16_t actRSSI, BOOL isBeacon) newEntry.txPower = 0; newEntry.hopCount = frameData->flagfld.flags.hop_count; - GetIPAddrFromMAC(&newEntry.macAddr, &ipAddr); + GetVPNAddrFromMAC(&newEntry.macAddr, &ipAddr); if(isBeacon) { memcpy(mesh_bcn_hdr.hdrBytes, (struct beacon_hdr_t *)frameData->buf, sizeof(BEACON_HEADER)); @@ -184,7 +179,7 @@ BOOL Check_Sender_Address(void *rxFrame, uint32_t rssi) IP400_FRAME *frameData = (IP400_FRAME *)rxFrame; int entryNum; - if((entryNum = findCall(&frameData->source)) != ENTRY_NOTFOUND) { + if((entryNum = findCall(&frameData->source, 0)) != ENTRY_NOTFOUND) { // rebooted if(frameData->seqNum == 0xFFFFFFFF) MeshTable[entryNum].nextSeq = 0; @@ -201,6 +196,35 @@ BOOL Check_Sender_Address(void *rxFrame, uint32_t rssi) return TRUE; } +/* + * Update the status based on last heard + */ +void UpdateMeshStatus(void) +{ + int timeSinceLastBeacon; + + for(int i=0;i MAX_MISSING) + MeshTable[i].status = MESHTBL_LOST; + break; + + case MESHTBL_LOST: + timeSinceLastBeacon = getElapsed(&MeshTable[i].lastHeard); + if(timeSinceLastBeacon > MAX_LOST) + MeshTable[i].status = MESHTBL_UNUSED; + + } + } +} + /* * Check if the frame can be accepted. * Accept a broadcast or my address if: @@ -216,32 +240,126 @@ BOOL Mesh_Accept_Frame(void *rxFrame, uint32_t rssi) // frame is sent a broadcast address if((frameData->dest.callbytes.bytes[0] == BROADCAST_ADDR) - && (frameData->dest.callbytes.bytes[0] == BROADCAST_ADDR)) + && (frameData->dest.callbytes.bytes[1] == BROADCAST_ADDR)) return Check_Sender_Address(rxFrame, rssi); + uint16_t myIPAddr = GetVPNLowerWord(); callDecode(&frameData->dest, decCall, &port); - if(CompareToMyCall(decCall)) + if(CompareToMyCall(decCall)) { + // accept all broadcast frames to my callsign + if(frameData->dest.vpnBytes.encvpn == IP_BROADCAST) + return TRUE; + // otherwise check my IP + else if(frameData->dest.vpnBytes.encvpn == myIPAddr) return Check_Sender_Address(rxFrame, rssi); + } // not for me return FALSE; } -// find a callsign in the list -int findCall(IP400_MAC *call) +// encode a callsign: ensure length is correct +uint32_t EncodeCallSign(char *call, int len) { - for(int i=0;icallbytes.encoded) + IP400_MAC encoded; + + // ensure the call sign is padded out to at least 6 characters + char paddedCall[20]; + strcpy(paddedCall, call); + strcat(paddedCall, " "); + + EncodeChunk(paddedCall, MAX_CALL, &encoded.callbytes.encoded); + return(encoded.callbytes.encoded); +} + +// compare the IP addresses. FFFF is considered +// a broadcast to all with the same callsign +BOOL ipCompare(uint16_t tblent, uint16_t compareto) +{ + if(compareto == IP_BROADCAST) + return TRUE; + + if(tblent == compareto) + return TRUE; + + // AX,25 compatibility enables SSID instead of VPN address +#if __AX25_COMPATIBILITY + if(((tblent&0xFFF0) == 0xFFA0) && ((tblent&0xF) == setup_memory.params.setup_data.flags.SSID)) + return TRUE; +#endif + + return FALSE; +} + +// find a callsign in the list +int findCall(IP400_MAC *call, int start) +{ + for(int i=start;icallbytes.encoded) + && ipCompare(MeshTable[i].macAddr.vpnBytes.encvpn, call->vpnBytes.encvpn) + && (MeshTable[i].status == MESHTBL_VALID)) return i; } return ENTRY_NOTFOUND; } +int getNMeshEntries(char *dest_call, int len) +{ + IP400_MAC encoded; + int nEntries = 0; + + encoded.callbytes.encoded = EncodeCallSign(dest_call, len); + for(int i=0;i= nMeshEntries) + return NULL; + + // keep looking + encoded.callbytes.encoded = EncodeCallSign(dest_call, len); + encoded.vpnBytes.encvpn = IP_BROADCAST; + + if((lastEntryNum=findCall(&encoded, lastEntryNum)) == ENTRY_NOTFOUND) + return NULL; + + return(&MeshTable[lastEntryNum].macAddr); + +} + + char capabilties[50]; // return the capabilities of a node char *GetCapabilities(SETUP_FLAGS cap) { mesh_bcn_hdr.setup.flags = cap; + char tmpBuf[50]; if(mesh_bcn_hdr.hdrBytes[0] == 0) { strcpy(capabilties, "Unknown"); @@ -249,15 +367,18 @@ char *GetCapabilities(SETUP_FLAGS cap) // modes if(cap.fsk) { - sprintf(capabilties, "%s", FSKSpeeds[cap.rate]); + sprintf(capabilties, "FSK "); } else if(cap.ofdm) { strcat(capabilties, " OFDM"); } - else if(cap.aredn) { - strcat(capabilties, "AREDN"); + + if(cap.AX25) { + sprintf(tmpBuf, " AX.25 SSID %d", cap.SSID); + strcat(capabilties, tmpBuf); } + // repeat mode is on.. if(cap.repeat) { strcat(capabilties, " RPT"); @@ -266,10 +387,21 @@ char *GetCapabilities(SETUP_FLAGS cap) return capabilties; } + +// remove spaces in callsign string +void trim(char *string) +{ + while(*string) { + if(*string == ' ') + *string = '\0'; + string++; + } +} + // list the mesh status: walk the mesh entries void Mesh_ListStatus(void) { - USART_Print_string("Stations Heard: %d\r\n", nMeshEntries); + USART_Print_string("Nodes Heard: %d\r\n", nMeshEntries); if(nMeshEntries == 0) return; @@ -277,15 +409,22 @@ void Mesh_ListStatus(void) char decodedCall[20]; SOCKADDR_IN ipAddr; - USART_Print_string("Call\tIP Addr\t\tRSSI\tSeq\tLast Heard\tHops\tCapabilities\r\n"); + USART_Print_string("Call\tVPN Addr\tStatus\tRSSI\tSeq\tLast Heard\tHops\tCapabilities\r\n"); for(int i=0;i @@ -37,13 +37,17 @@ #define FLASH_PAGE_NUM 127 static FLASH_EraseInitTypeDef EraseInitStruct; +// current build number +char *revID = "$Revision: 37 $"; +char *dateID = "$Date: 2025-06-30 20:36:27 -0600 (Mon, 30 Jun 2025) $"; + /* * Default setup parameters */ SETUP_MEMORY setup_memory; SETUP_MEMORY def_params = { - .params.setup_data.flags = { 1, 0, 0 , 1, 0, FSK_100K }, // LSB specified first + .params.setup_data.flags = { 1, 0, 0 , 1, 0 }, // LSB specified first .params.setup_data.stnCall ="NOCALL", .params.setup_data.gridSq = "DO21vd", .params.setup_data.latitude = "51.08", @@ -60,8 +64,8 @@ SETUP_MEMORY def_params = { .params.radio_setup.PADrvMode = PA_DRV_TX_HP, .params.radio_setup.rxSquelch = -95, // - .params.FirmwareVerMajor = 1, // current rev is 1.1 - .params.FirmwareVerMinor = 1, + .params.FirmwareVerMajor = 1, // current rev is 1.3 + .params.FirmwareVerMinor = 3, .params.Magic = SETUP_MAGIC, .params.SetupCRC = 0 }; @@ -120,26 +124,31 @@ BOOL CompareToMyCall(char *call) */ void printStationSetup(void) { - // station callsign first + // station callsigns first USART_Print_string("Station Callsign->%s\r\n", setup_memory.params.setup_data.stnCall); - if(setup_memory.params.setup_data.flags.ext) - USART_Print_string("Extended Callsign->%s\r\n"); + if(setup_memory.params.setup_data.flags.AX25) { + USART_Print_string("AX.25 Address SSID: %s-%d\r\n", setup_memory.params.setup_data.stnCall, + setup_memory.params.setup_data.flags.SSID); + } else { + USART_Print_string("AX.25 Compatibility Not Enabled\r\n"); + } + USART_Print_string("Description->%s\r\n\n", setup_memory.params.setup_data.Description); + // station location USART_Print_string("Latitude->%s\r\n", setup_memory.params.setup_data.latitude); USART_Print_string("Longitude->%s\r\n", setup_memory.params.setup_data.longitude); USART_Print_string("Grid Square->%s\r\n", setup_memory.params.setup_data.gridSq); - USART_Print_string("Capabilities->"); if(setup_memory.params.setup_data.flags.fsk) USART_Print_string("FSK "); if(setup_memory.params.setup_data.flags.ofdm) USART_Print_string("OFDM "); - if(setup_memory.params.setup_data.flags.aredn) - USART_Print_string("AREDN "); + if(setup_memory.params.setup_data.flags.AX25) + USART_Print_string("AX.25 "); if(setup_memory.params.setup_data.flags.repeat) - USART_Print_string("\r\nRepeat mode on by default\r\n"); + USART_Print_string("\r\nRepeat mode on\r\n"); else - USART_Print_string("\r\nRepeat mode off by default\r\n"); + USART_Print_string("\r\nRepeat mode off\r\n"); USART_Print_string("Beacon Interval->%d mins\r\n\n", setup_memory.params.setup_data.beaconInt); } @@ -172,7 +181,7 @@ void printRadioSetup(void) /* * Manage the IP address */ - void GetMyIP(SOCKADDR_IN **ipAddr) + void GetMyVPN(SOCKADDR_IN **ipAddr) { *ipAddr = &myIP; } @@ -183,7 +192,7 @@ void printRadioSetup(void) } // set my IP Address - void SetMyIP(void) + void SetMyVPNAddr(void) { char paddedCall[20]; strncpy(paddedCall, setup_memory.params.setup_data.stnCall, MAX_CALL); @@ -192,8 +201,8 @@ void printRadioSetup(void) // encode my callsign EncodeChunk(paddedCall, MAX_CALL, &myMAC.callbytes.encoded); - myMAC.ipbytes.encip = GetIPLowerWord(); - GetIPAddrFromMAC(&myMAC, &myIP); + myMAC.vpnBytes.encvpn = GetVPNLowerWord(); + GetVPNAddrFromMAC(&myMAC, &myIP); } /* @@ -260,7 +269,7 @@ BOOL ReadSetup(void) memAddr += sizeof(uint32_t); } - SetMyIP(); + SetMyVPNAddr(); return TRUE; } @@ -274,6 +283,7 @@ HAL_StatusTypeDef WriteSetup(void) __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_ALL_ERRORS); // erase it first + USART_Print_string("Erasing..."); EraseInitStruct.TypeErase = FLASH_TYPEERASE_PAGES; EraseInitStruct.Page = FLASH_PAGE_NUM; EraseInitStruct.NbPages = 1; @@ -284,6 +294,7 @@ HAL_StatusTypeDef WriteSetup(void) for(int i=0;i<1000;i++); // now write + USART_Print_string("Writing..."); uint32_t memAddr = FLASH_PAGE_ADDR; uint32_t *src_addr = setup_memory.flashwords; uint16_t nwords = sizeof(SETUP_MEMORY)/sizeof(uint32_t); @@ -295,10 +306,24 @@ HAL_StatusTypeDef WriteSetup(void) if ((status=HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, memAddr, data32)) != HAL_OK) return status; memAddr += sizeof(uint32_t); + USART_Print_string("%02d..", nwords); } return status; } +/* + * build related stuff + */ +char *getRevID(void) +{ + return &revID[1]; +} + +char *getDateID(void) +{ + return &dateID[1]; +} + /* * HAL related functions diff --git a/Node Firmware/IP400/Src/spi.c b/Node Firmware/IP400/Src/spi.c index 04d6392..0b11fe7 100644 --- a/Node Firmware/IP400/Src/spi.c +++ b/Node Firmware/IP400/Src/spi.c @@ -249,10 +249,10 @@ void SPI_Task_Exec(void) // copy the address fields memcpy(&spiTxBuffer.spiData.hdr.fromCall, txFrame->source.callbytes.bytes, N_CALL); - memcpy(&spiTxBuffer.spiData.hdr.fromIP, txFrame->source.ipbytes.ip, N_IPBYTES); + memcpy(&spiTxBuffer.spiData.hdr.fromIP, txFrame->source.vpnBytes.vpn, N_IPBYTES); memcpy(spiTxBuffer.spiData.hdr.toCall, txFrame->dest.callbytes.bytes, N_CALL); - memcpy(&spiTxBuffer.spiData.hdr.toIP, txFrame->dest.ipbytes.ip, N_IPBYTES); + memcpy(&spiTxBuffer.spiData.hdr.toIP, txFrame->dest.vpnBytes.vpn, N_IPBYTES); // flag fields spiTxBuffer.spiData.hdr.coding = txFrame->flagfld.flags.coding; diff --git a/Node Firmware/IP400/Src/subg.c b/Node Firmware/IP400/Src/subg.c new file mode 100644 index 0000000..1e17e4f --- /dev/null +++ b/Node Firmware/IP400/Src/subg.c @@ -0,0 +1,486 @@ +/*--------------------------------------------------------------------------- + Project: IP400 + + Module: Frame transmit and receive tasks + + File Name: subg.c + + Author: MartinA + + Creation Date: Jan 8, 2025 + + Description: Manage the STMWL33 Sub-GHz radio physical connection + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version, provided this copyright notice + is included. + + Copyright (c) Alberta Digital Radio Communications Society + All rights reserved. + + + Revision History: + +---------------------------------------------------------------------------*/ +#include +#include +#include +#include + +#include +#if _BOARD_TYPE == NUCLEO_BOARD +#include +#endif + +#include +#include +#include + +#include "frame.h" +#include "dataq.h" +#include "led.h" +#include "usart.h" +#include "setup.h" + +// conditionals +#define __DUMP_BEACON 0 // dump a beacon frame to console using chat + +// SubG states +typedef enum { + IDLE=0, // idle - waiting for work + RX_ACTIVE, // rx is active + RX_ABORTING, // stopping Rx + TX_READY, // activated, no data yet + TX_SENDING, // sending a frame + TX_DONE // done +} SubGRxTxState; + +// locals +uint32_t subgIRQStatus; // interrupt status +int16_t rxSquelch; // rx squlech +uint8_t subGCmd; // current command + +SubGRxTxState subGState; // radio state +FRAME_QUEUE txQueue; // transmitter frame queue +FRAME_STATS Stats; // collected stats + +// processed frames +static IP400_FRAME rFrame; +static HOPTABLE rxHopTable[MAX_HOP_COUNT]; + +/* + * Buffer management + */ +typedef uint8_t RAWBUFFER; +// raw buffer memory +#if USE_BUFFER_RAM +static RAWBUFFER Buffer0[MAX_FRAME_SIZE] __attribute__((section("BUFFERS"), aligned(4))); +static RAWBUFFER Buffer1[MAX_FRAME_SIZE] __attribute__((section("BUFFERS"), aligned(4))); +#else +static RAWBUFFER Buffer0[MAX_FRAME_SIZE]; +static RAWBUFFER Buffer1[MAX_FRAME_SIZE]; +#endif +// definitions +#define SUBG_BUFFER_0 0 // buffer 0 +#define SUBG_BUFFER_1 1 // buffer 1 +#define N_SUBG_BUFFERS 2 // number of buffers +typedef enum { + SUBG_BUF_READY, // ready to tx/rx + SUBG_BUF_ACTIVE, // active: rx or tx + SUBG_BUF_FULL, // full: has RX data + SUBG_BUF_EMPTY // empty: data sent +} BufferState; + +// subg buffer state +typedef struct subg_bug_stat_t { + BufferState state; // subg buffer state + RAWBUFFER *addr; // address of local buffer + uint32_t length; // rx length +} SUBG_BUF_STATUS; +SUBG_BUF_STATUS subgBufState[N_SUBG_BUFFERS] = { + { SUBG_BUF_READY, Buffer0 }, + { SUBG_BUF_READY, Buffer1 } +}; +#define SUBG_BUF_STATUS(c,s) (subgBufState[c].state == s) +uint8_t activeTxBuffer; // active transmitter buffer + +/* + * Initialize the task + */ +void SubG_Task_init(void) +{ + subGState = IDLE; + txQueue.q_forw = &txQueue; + txQueue.q_back = &txQueue; + + // init stats and counters + memset(&Stats, 0, sizeof(FRAME_STATS)); + + // set comamnd to NOP + subGCmd = CMD_NOP; + + // set Rx threshold + STN_PARAMS *params = GetStationParams(); + rxSquelch = params->radio_setup.rxSquelch; + + // enable the interrupt + __HAL_MRSUBG_SET_RFSEQ_IRQ_ENABLE( + MR_SUBG_GLOB_DYNAMIC_RFSEQ_IRQ_ENABLE_RX_OK_E + | MR_SUBG_GLOB_DYNAMIC_RFSEQ_IRQ_ENABLE_TX_DONE_E + | MR_SUBG_GLOB_DYNAMIC_RFSEQ_IRQ_ENABLE_RX_TIMEOUT_E + | MR_SUBG_GLOB_DYNAMIC_RFSEQ_IRQ_ENABLE_RX_CRC_ERROR_E + ); + HAL_NVIC_EnableIRQ(MR_SUBG_IRQn); +} + +/* + * return the stats + */ +FRAME_STATS *GetFrameStats(void) +{ + return &Stats; +} +// return the radio status +uint32_t GetRadioStatus(void) +{ + uint32_t radioStats = READ_REG(MR_SUBG_GLOB_STATUS->RFSEQ_STATUS_DETAIL); + return radioStats; +} +// get the FSM state +SubGFSMState GetFSMState(void) +{ + uint32_t fsmState = READ_REG(MR_SUBG_GLOB_STATUS->RADIO_FSM_INFO); + return (uint8_t)(fsmState & MR_SUBG_GLOB_STATUS_RADIO_FSM_INFO_RADIO_FSM_STATE_Msk); +} + + +/* + * queue a frame for transmission by the tx task + */ +void QueueTxFrame(IP400_FRAME *txframe) +{ + enqueFrame(&txQueue, txframe); +} + +/* + * IP400 to buffer handlers + */ +int IP4002Buf(IP400_FRAME *tFrame, RAWBUFFER *rawFrame) +{ + int frameLen = tFrame->length; + + /* + * Build the raw frame bytes: see IP400_FRAME struct + */ + // Source call + port (6 bytes) + memcpy(rawFrame, (uint8_t *)&tFrame->source, IP_400_CALL_SIZE); + rawFrame += IP_400_CALL_SIZE; + // Dest call + port (6 bytes) + memcpy(rawFrame, (uint8_t *)&tFrame->dest, IP_400_CALL_SIZE); + rawFrame += IP_400_CALL_SIZE; + // flag byte (2 byte) + memcpy(rawFrame, (uint8_t *)&tFrame->flagfld, IP_400_FLAG_SIZE); + rawFrame += IP_400_FLAG_SIZE; + // frame sequence number (4 bytes) + memcpy(rawFrame, (uint32_t *)&tFrame->seqNum, sizeof(uint32_t)); + rawFrame += sizeof(uint32_t); + // frame length (2 bytes) + memcpy(rawFrame, (uint8_t *)&tFrame->length, sizeof(uint16_t)); + rawFrame += IP_400_LEN_SIZE; + + // add in the hop table + if(tFrame->flagfld.flags.hoptable) { + uint16_t hopLen = (uint16_t)(tFrame->flagfld.flags.hop_count) * sizeof(HOPTABLE); + memcpy(rawFrame, (uint8_t *)(tFrame->hopTable), hopLen); + rawFrame += hopLen; + free(tFrame->hopTable); + } + + // and now the data... + if((tFrame->buf != NULL) && (tFrame->length != 0)) + memcpy(rawFrame, tFrame->buf, tFrame->length); + + // free the allocations in the reverse order... + if(tFrame->buf != NULL) + free(tFrame->buf); + + free(tFrame); + + // ensure packet length is a multiple of 4 bytes + int pktLen = (rawFrame - rawFrame) + frameLen; + pktLen += (pktLen % 4); + + return pktLen; +} + +/* + * Do the opposite of the transmitter... + */ +void Buf2IP400(IP400_FRAME *rframe, RAWBUFFER *RxRaw) +{ + + uint8_t *cpyDest; + + // Source call + port (6 bytes) + cpyDest = (uint8_t *)&rFrame.source.callbytes.bytes; + memcpy(cpyDest, RxRaw, IP_400_CALL_SIZE); + RxRaw += IP_400_CALL_SIZE; + + // Dest call + port (6 bytes) + cpyDest = (uint8_t *)&rFrame.dest.callbytes.bytes; + memcpy(cpyDest, RxRaw, IP_400_CALL_SIZE); + RxRaw += IP_400_CALL_SIZE; + + // flag byte (2 byte) + cpyDest = (uint8_t *)&rFrame.flagfld.allflags; + memcpy(cpyDest, RxRaw, IP_400_FLAG_SIZE); + RxRaw += IP_400_FLAG_SIZE; + + // frame sequence number (4 bytes) + cpyDest = (uint8_t *)&rFrame.seqNum; + memcpy(cpyDest, RxRaw, sizeof(uint32_t)); + RxRaw += sizeof(uint32_t); + + // frame length (2 bytes) + cpyDest = (uint8_t *)&rFrame.length; + memcpy(cpyDest, RxRaw, sizeof(uint16_t)); + RxRaw += IP_400_LEN_SIZE; + + // copy the hop table + uint8_t nHops = rFrame.flagfld.flags.hop_count; + if(nHops != 0) { + uint16_t hopLen = (uint16_t)nHops*sizeof(HOPTABLE); + memcpy(rxHopTable, RxRaw, hopLen); + RxRaw += hopLen; + rFrame.hopTable = rxHopTable; + } else { + rFrame.hopTable = NULL; + } + + rFrame.buf = RxRaw; +} + +/* + * main entry for subg task. Pick frames from the transmit queue + */ +void SubG_Task_exec(void) +{ + static RAWBUFFER *rawFrame; + IP400_FRAME *tFrame; + uint8_t actBuffer; + int frameLen = 0; + + SubGFSMState fsmState = GetFSMState(); + + switch(subGState) { + + // idle: enable the receiver + case IDLE: + + // finish last abort command from Tx + if(subGCmd == CMD_SABORT) + subGCmd = CMD_NOP; + + // if the receiver is active, then go there... + if((fsmState == FSM_RX) && (subGCmd == CMD_RX)) + subGState = RX_ACTIVE; + + // ensure we are idle when entering here... + if((fsmState != FSM_IDLE) && (subGCmd == CMD_NOP)) + return; + + HAL_MRSubG_SetRSSIThreshold(rxSquelch); + __HAL_MRSUBG_SET_CS_BLANKING(); + + subgBufState[SUBG_BUFFER_0].state = SUBG_BUF_ACTIVE; + subgBufState[SUBG_BUFFER_0].addr = Buffer0; + + subgBufState[SUBG_BUFFER_1].state = SUBG_BUF_ACTIVE; + subgBufState[SUBG_BUFFER_1].addr = Buffer1; + + __HAL_MRSUBG_SET_RX_MODE(RX_NORMAL); + __HAL_MRSUBG_SET_DATABUFFER_SIZE(MAX_FRAME_SIZE); + __HAL_MRSUBG_SET_DATABUFFER0_POINTER((uint32_t) subgBufState[SUBG_BUFFER_0].addr); + __HAL_MRSUBG_SET_DATABUFFER1_POINTER((uint32_t) subgBufState[SUBG_BUFFER_1].addr); + + subGCmd = CMD_RX; + __HAL_MRSUBG_STROBE_CMD(subGCmd); + + SetLEDMode(BICOLOR_GREEN); + + break; + + // receiver is active + case RX_ACTIVE: + if((SUBG_BUF_STATUS(SUBG_BUFFER_0, SUBG_BUF_FULL) || SUBG_BUF_STATUS(SUBG_BUFFER_1, SUBG_BUF_FULL))) { + if(SUBG_BUF_STATUS(SUBG_BUFFER_0, SUBG_BUF_FULL)) + actBuffer = SUBG_BUFFER_0; + else actBuffer = SUBG_BUFFER_1; + } else { + // no data: check the transmitter + if(quehasData(&txQueue)) { + subGCmd = CMD_SABORT; + __HAL_MRSUBG_STROBE_CMD(subGCmd); + subGState = RX_ABORTING; + } + return; + } + // process a received frame + rawFrame = subgBufState[actBuffer].addr; + frameLen = subgBufState[actBuffer].length; + + // process the frame fields + Buf2IP400(&rFrame, rawFrame); + ProcessRxFrame(&rFrame, frameLen); + + subgBufState[actBuffer].state = SUBG_BUF_ACTIVE; + break; + + // shutting down receiver: ready to tx + case RX_ABORTING: + // finish last abort command from Tx + if(subGCmd == CMD_SABORT) + subGCmd = CMD_NOP; + + uint32_t reject=0, abortDone=0; + do { + subgIRQStatus = READ_REG(MR_SUBG_GLOB_STATUS->RFSEQ_IRQ_STATUS); + reject = subgIRQStatus & MR_SUBG_GLOB_STATUS_RFSEQ_IRQ_STATUS_COMMAND_REJECTED_F; + abortDone = subgIRQStatus & MR_SUBG_GLOB_STATUS_RFSEQ_IRQ_STATUS_SABORT_DONE_F; + } while ((abortDone == 0) && (reject == 0)); + if(abortDone) + __HAL_MRSUBG_CLEAR_RFSEQ_IRQ_FLAG(MR_SUBG_GLOB_STATUS_RFSEQ_IRQ_STATUS_SABORT_DONE_F); + if(reject) + __HAL_MRSUBG_CLEAR_RFSEQ_IRQ_FLAG(MR_SUBG_GLOB_STATUS_RFSEQ_IRQ_STATUS_COMMAND_REJECTED_F); + + // set buffer status + subgBufState[SUBG_BUFFER_0].state = SUBG_BUF_EMPTY; + subgBufState[SUBG_BUFFER_1].state = SUBG_BUF_EMPTY; + + SetLEDMode(BICOLOR_OFF); + + if(fsmState == FSM_IDLE) + subGState = TX_READY; + + break; + + // ready to start the tx + case TX_READY: + // try to fill both tx buffers + if((tFrame = dequeFrame(&txQueue)) != NULL) { + subgBufState[SUBG_BUFFER_0].length = IP4002Buf(tFrame, subgBufState[SUBG_BUFFER_0].addr); + subgBufState[SUBG_BUFFER_0].state = SUBG_BUF_FULL; + } + if((tFrame = dequeFrame(&txQueue)) != NULL) { + subgBufState[SUBG_BUFFER_1].length = IP4002Buf(tFrame, subgBufState[SUBG_BUFFER_1].addr); + subgBufState[SUBG_BUFFER_1].state = SUBG_BUF_FULL; + } + + // Send buffer zero first + activeTxBuffer = SUBG_BUFFER_0; + __HAL_MRSUBG_SET_DATABUFFER0_POINTER((uint32_t)subgBufState[activeTxBuffer].addr); + __HAL_MRSUBG_SET_DATABUFFER_SIZE(MAX_FRAME_SIZE); + __HAL_MRSUBG_SET_TX_MODE(TX_NORMAL); + subGCmd = CMD_TX; + __HAL_MRSUBG_STROBE_CMD(subGCmd); + + // set tx indication: bicolor off and Tx on + SetLEDMode(TX_LED_ON); + subGState = TX_SENDING; + break; + + // actively transmitting + case TX_SENDING: + // see if a buffer is still waiting... + if((SUBG_BUF_STATUS(SUBG_BUFFER_0, SUBG_BUF_FULL) || SUBG_BUF_STATUS(SUBG_BUFFER_1, SUBG_BUF_FULL))) { + activeTxBuffer = (SUBG_BUF_STATUS(SUBG_BUFFER_0, SUBG_BUF_FULL)) ? SUBG_BUFFER_0: SUBG_BUFFER_1; + __HAL_MRSUBG_SET_DATABUFFER0_POINTER((uint32_t)subgBufState[activeTxBuffer].addr); + __HAL_MRSUBG_STROBE_CMD(subGCmd); + } + + // see if we have an empty buffer to fill... + if((SUBG_BUF_STATUS(SUBG_BUFFER_0, SUBG_BUF_EMPTY) || SUBG_BUF_STATUS(SUBG_BUFFER_1, SUBG_BUF_EMPTY))) { + actBuffer = (SUBG_BUF_STATUS(SUBG_BUFFER_0, SUBG_BUF_EMPTY)) ? SUBG_BUFFER_0: SUBG_BUFFER_1; + + // is there a frame to send? + if((tFrame = dequeFrame(&txQueue)) != NULL) { + subgBufState[actBuffer].state = SUBG_BUF_FULL; + subgBufState[actBuffer].length = IP4002Buf(tFrame, subgBufState[SUBG_BUFFER_0].addr); + } else { + // nothing to send and both buffers empty + if((SUBG_BUF_STATUS(SUBG_BUFFER_0, SUBG_BUF_EMPTY) && SUBG_BUF_STATUS(SUBG_BUFFER_1, SUBG_BUF_EMPTY))) + subGState = TX_DONE; + } + } + break; + + // done + case TX_DONE: + subGCmd = CMD_SABORT; + __HAL_MRSUBG_STROBE_CMD(subGCmd); + SetLEDMode(TX_LED_OFF); + subGState = IDLE; + + break; + } +} + +// subg interrupt callback +void HAL_MRSubG_IRQ_Callback(void) +{ + subgIRQStatus = READ_REG(MR_SUBG_GLOB_STATUS->RFSEQ_IRQ_STATUS); + + // check for an error: leave buffer in current state for re-use + if(subgIRQStatus & ( + MR_SUBG_GLOB_STATUS_RFSEQ_IRQ_STATUS_RX_CRC_ERROR_F | + MR_SUBG_GLOB_STATUS_RFSEQ_IRQ_STATUS_RX_TIMEOUT_F)) + { + if(subgIRQStatus & MR_SUBG_GLOB_STATUS_RFSEQ_IRQ_STATUS_RX_CRC_ERROR_F) { + __HAL_MRSUBG_CLEAR_RFSEQ_IRQ_FLAG(MR_SUBG_GLOB_STATUS_RFSEQ_IRQ_STATUS_RX_CRC_ERROR_F); + Stats.CRCErrors++; + } + if (subgIRQStatus & MR_SUBG_GLOB_STATUS_RFSEQ_IRQ_STATUS_RX_TIMEOUT_F) { + __HAL_MRSUBG_CLEAR_RFSEQ_IRQ_FLAG(MR_SUBG_GLOB_STATUS_RFSEQ_IRQ_STATUS_RX_TIMEOUT_F); + Stats.TimeOuts++; + } + + // turn the Rx back on if still active + if(subGCmd == CMD_RX) + __HAL_MRSUBG_STROBE_CMD(subGCmd); + return; + } + + // Good Rx: mark the buffer full + if (subgIRQStatus & MR_SUBG_GLOB_STATUS_RFSEQ_IRQ_STATUS_RX_OK_F ) { + Stats.RxFrameCnt++; + Stats.lastRSSI = READ_REG_FIELD(MR_SUBG_GLOB_STATUS->RX_INDICATOR, MR_SUBG_GLOB_STATUS_RX_INDICATOR_RSSI_LEVEL_ON_SYNC); + __HAL_MRSUBG_CLEAR_RFSEQ_IRQ_FLAG(MR_SUBG_GLOB_STATUS_RFSEQ_IRQ_STATUS_RX_OK_F); + if(subgIRQStatus & MR_SUBG_GLOB_STATUS_RFSEQ_IRQ_STATUS_DATABUFFER0_USED_F ) { + subgBufState[SUBG_BUFFER_0].state = SUBG_BUF_FULL; + subgBufState[SUBG_BUFFER_0].length = __HAL_MRSUBG_GET_DATABUFFER_SIZE(); + } + if(subgIRQStatus & MR_SUBG_GLOB_STATUS_RFSEQ_IRQ_STATUS_DATABUFFER1_USED_F ) { + subgBufState[SUBG_BUFFER_1].state = SUBG_BUF_FULL; + subgBufState[SUBG_BUFFER_1].length = __HAL_MRSUBG_GET_DATABUFFER_SIZE(); + } + // turn the Rx back on if still active + if(subGCmd == CMD_RX) + __HAL_MRSUBG_STROBE_CMD(subGCmd); + } + + // TxDone: cannot do tx and rx at the same time + else if(subgIRQStatus & MR_SUBG_GLOB_STATUS_RFSEQ_IRQ_STATUS_TX_DONE_F) { + __HAL_MRSUBG_CLEAR_RFSEQ_IRQ_FLAG(MR_SUBG_GLOB_STATUS_RFSEQ_IRQ_STATUS_TX_DONE_F); + if(subgIRQStatus & MR_SUBG_GLOB_STATUS_RFSEQ_IRQ_STATUS_DATABUFFER0_USED_F ) { + subgBufState[activeTxBuffer].state = SUBG_BUF_EMPTY; + + } + // this will probably never happen, but we will check it anyway... + if(subgIRQStatus & MR_SUBG_GLOB_STATUS_RFSEQ_IRQ_STATUS_DATABUFFER1_USED_F ) { + subgBufState[activeTxBuffer].state = SUBG_BUF_EMPTY; + } + Stats.TxFrameCnt++; + } +} diff --git a/Node Firmware/IP400/Src/tod.c b/Node Firmware/IP400/Src/tod.c index eebc124..9f1aa42 100644 --- a/Node Firmware/IP400/Src/tod.c +++ b/Node Firmware/IP400/Src/tod.c @@ -78,3 +78,18 @@ BOOL setTOD(char *todString) return TRUE; } + +// get the elapsed time between two TOD and timestamp (in seconds) +int getElapsed(TIMEOFDAY *time) +{ + // convert current tod to seconds + int todSecs = (tod.Hours*3600) + (tod.Minutes * 60) + tod.Seconds; + int timeSecs = (time->Hours*3600) + (time->Minutes * 60) + time->Seconds; + + // calculate diff: if negative add one day + int diff = todSecs - timeSecs; + if(diff < 0) + diff += 23*3600 + 59*60 + 59; + + return diff; +} diff --git a/Node Firmware/IP400/Src/usart.c b/Node Firmware/IP400/Src/usart.c index b4f0d73..e58c3d1 100644 --- a/Node Firmware/IP400/Src/usart.c +++ b/Node Firmware/IP400/Src/usart.c @@ -28,8 +28,8 @@ #include #include #include +#include -#include "config.h" #include "stream_buffer.h" #include "types.h" #include "streambuffer.h" @@ -53,15 +53,15 @@ char usartPrintBuffer[200]; // fwd refs here... void USART_Receive_char(void); -#if __ENABLE_GPS +// otherwise, it is defined +#if ENABLE_LPUART #define LPUART_DMA_SIZE 32 static DATA_ELEMENT LPUARTRxChar[LPUART_DMA_SIZE]; void LPUART_Receive_char(void); - StreamBufferHandle_t LPUART_RxBuffer; // handle to buffer StaticStreamBuffer_t LPUART_StreamBuffer; -uint8_t gps_data[bufferSIZE]; +uint8_t lpuart_data[bufferSIZE]; #endif /* @@ -77,8 +77,8 @@ void USART_API_init(void) USART_RxBuffer_reset(); USART_Receive_char(); -#if __ENABLE_GPS - LPUART_RxBuffer = xStreamBufferCreateStatic(bufferSIZE, 1, gps_data, &LPUART_StreamBuffer); +#if ENABLE_LPUART + LPUART_RxBuffer = xStreamBufferCreateStatic(bufferSIZE, 1, lpuart_data, &LPUART_StreamBuffer); LPUART_RxBuffer_reset(); LPUART_Receive_char(); #endif @@ -91,7 +91,7 @@ void USART_RxBuffer_reset(void) return; } -#if __ENABLE_GPS +#if ENABLE_LPUART // reset the LPUART Buffer void LPUART_RxBuffer_reset(void) { @@ -129,15 +129,15 @@ DATA_ELEMENT databuffer_get(UART_TIMEOUT_T timeout) /* * Similar but from GPS buffer */ -#if __ENABLE_GPS +#if ENABLE_LPUART // return the number of byte in the buffer -size_t gpsbuffer_bytesInBuffer(void) +size_t lpuart_buffer_bytesInBuffer(void) { size_t nBytes = xStreamBufferBytesAvailable(LPUART_RxBuffer); return nBytes; } // get a bytes -DATA_ELEMENT gpsbuffer_get(UART_TIMEOUT_T timeout) +DATA_ELEMENT lpuart_buffer_get(UART_TIMEOUT_T timeout) { DATA_ELEMENT retval; @@ -237,7 +237,7 @@ void USART_Print_string(char *format, ...) // Transmit completed: trigger semaphore void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart) { -#if __ENABLE_GPS +#if ENABLE_LPUART // Not interested in LPUART if(huart->Instance == LPUART1) return; @@ -259,7 +259,7 @@ void USART_Receive_char(void) // NB: Console USART and GPS LPUART share the same HAL routine void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { -#if __ENABLE_GPS +#if ENABLE_LPUART // service the LPUART if(huart->Instance == LPUART1) { xStreamBufferSendFromISR(LPUART_RxBuffer, LPUARTRxChar, 1, NULL); @@ -272,7 +272,7 @@ void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) HAL_UART_Receive_IT(&huart1,(uint8_t *)USARTRxChar,1); } -#if __ENABLE_GPS +#if ENABLE_LPUART // send a string to the LPUART void LPUART_Send_String(char *str, uint16_t len) { diff --git a/Node Firmware/IP400/Src/utils.c b/Node Firmware/IP400/Src/utils.c index 6d5e370..3430434 100644 --- a/Node Firmware/IP400/Src/utils.c +++ b/Node Firmware/IP400/Src/utils.c @@ -24,7 +24,53 @@ #include "types.h" #include "utils.h" -// ascii to decimal +/* + * Unsigned conversion routines + */ +// convert an ascii string to a uint8 +uint8_t A2_uint8_t(char *s) +{ + uint8_t val = 0, newval; + + while(*s) { + newval = (uint8_t)(val << 3); //*8 + newval += val << 1; //*2 + *8 = *10 + val = newval + (*s++ - '0'); // *10 + new value + } + return val; +} + +// convert an ascii string to a uint16 +uint16_t A2_uint16_t(char *s) +{ + uint16_t val = 0, newval; + + while(*s) { + newval = val << 3; //*8 + newval += val << 1; //*2 + *8 = *10 + val = newval + (*s++ - '0'); // *10 + new value + } + return val; +} + +// convert an ascii string to a uint32 +// same code, wider int +uint32_t A2_uint32_t(char *s) +{ + uint32_t val = 0, newval; + + while(*s) { + newval = val << 3; //*8 + newval += val << 1; //*2 + *8 = *10 + val = newval + (*s++ - '0'); // *10 + new value + } + return val; +} + +/* + * Signed integer conversion + */ +// ascii to signed decimal int ascii2Dec(char *dec) { int retval = 0; @@ -63,10 +109,33 @@ BOOL isfloat(char *val) return 0U; } -// convert an ascii string to a double -// until we encounter the decimal place, treat it the same as an integer. -// after that, scale each digit down appropriately -// +// is an upper case character +BOOL isUpper(char c) +{ + if((c >= 'A') && (c <= 'Z')) + return 1U; + return 0U; +} + +// is lower case +BOOL isLower(char c) +{ + if((c >= 'a') && (c <= 'z')) + return 1U; + return 0U; +} + +// is numeric +BOOL isNumeric(char c) +{ + if((c >= '0') && (c <= '9')) + return 1U; + return 0U; +} + +/* + * signed double conversion + */ double ascii2double(char *val) { double retval = 0; @@ -100,6 +169,9 @@ double ascii2double(char *val) return retval * sgn; } +/* + * String manipulation + */ // Your basic linux-stle (argv, argc) parser based on delimiters // string is destroyed int explode_string(char *str, char *strp[], int limit, char delim, char quote)