diff --git a/Documentation/IP400 Node Software.pdf b/Documentation/IP400 Node Software.pdf index dcf7cf5..c47ec66 100644 Binary files a/Documentation/IP400 Node Software.pdf and b/Documentation/IP400 Node Software.pdf differ diff --git a/Documentation/IP400 Pi HAT Mini Node.pdf b/Documentation/IP400 Pi HAT Mini Node.pdf new file mode 100644 index 0000000..08698bb Binary files /dev/null and b/Documentation/IP400 Pi HAT Mini Node.pdf differ diff --git a/Documentation/Read.me b/Documentation/Read.me deleted file mode 100644 index ece8765..0000000 --- a/Documentation/Read.me +++ /dev/null @@ -1,5 +0,0 @@ -This folder contains the currrent release documentation. - -IP400 Node Software Description of the node software and release notes. -IP400 SPI Specification SPI protocol and ethernet packet description. -IP400 Nucleo CC2 Experimenter Node Hardware overview and details for the CC2 Nucleo board. diff --git a/Documentation/Readme.md b/Documentation/Readme.md new file mode 100644 index 0000000..13767f5 --- /dev/null +++ b/Documentation/Readme.md @@ -0,0 +1,17 @@ +# Documentation + +This folder contains the recent documentation for the following: +1) IP400 Nucleo CC2 Experimenter node +2) IP400 Pi HAT Mini Node +3) IP400 SPI Specification +4) IP400 Node Software +5) Remote Power Kit Assembly Tips and Hints. + +The two node types, teh Nucleo and Mini-Node, both run the same firmware so the functionality is the same. The hardware differences are highlighted in the +relevant documentation. +The SPI specification describes the protocol for frames sent and received using the SPI interface, there is code to manage it on the SD card image, which +can be found on the IP400 project website, under hardware and 'getting started'. +The remote power kit assembly is a step-by-step set of instructions to assemble to power adapters. These are reasonably simple to assemble and can be done in +less that an hour. + +WARNING: The regulator on the node gets hot, try not to touch it and ensure that it has adequate ventilation. A heatsink may be a good addition. diff --git a/Documentation/Remote Power Kit Assembly Tips and Hints.pdf b/Documentation/Remote Power Kit Assembly Tips and Hints.pdf new file mode 100644 index 0000000..9d0e1fc Binary files /dev/null and b/Documentation/Remote Power Kit Assembly Tips and Hints.pdf differ diff --git a/Node Firmware/How to import code into STM32CubeIDE.pdf b/Node Firmware/How to import code into STM32CubeIDE.pdf new file mode 100644 index 0000000..63bee5c Binary files /dev/null and b/Node Firmware/How to import code into STM32CubeIDE.pdf differ 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) diff --git a/Node Firmware/NucleoCC2.zip b/Node Firmware/NucleoCC2.zip new file mode 100644 index 0000000..bd9432c Binary files /dev/null and b/Node Firmware/NucleoCC2.zip differ diff --git a/Node Firmware/PIZero_Ichiban_E04.bin b/Node Firmware/PIZero_Ichiban_E04.bin new file mode 100644 index 0000000..9aea244 Binary files /dev/null and b/Node Firmware/PIZero_Ichiban_E04.bin differ diff --git a/Node Firmware/PIZero_Ichiban_E04.zip b/Node Firmware/PIZero_Ichiban_E04.zip new file mode 100644 index 0000000..d86b440 Binary files /dev/null and b/Node Firmware/PIZero_Ichiban_E04.zip differ diff --git a/Node Firmware/Readme.md b/Node Firmware/Readme.md index 543d31c..5c5d908 100644 --- a/Node Firmware/Readme.md +++ b/Node Firmware/Readme.md @@ -1,9 +1,12 @@ # Node Firmware -This directory contains the code sources. The current source code is at Rev level 1.1. +This directory contains the code sources. The current source code is at Rev level 1.3. -Due to a new release of CubeIDE, the platform code is out of date and has been temporarily removed until -a new release is ready, which is imminent. +The two zip files are the Cube IDE projects, which can be imported directly. See the .pdf instructions for +more details. The IP400 code is common to both, and can be compiled for the two platforms. -If you need a copy rignt away, please email me VE6VH at ve6vh dot org +The precompiled .bin file is for the mini-nodes that were recently shipped, the module can be updated using +stm32flash, which is located in the 'code' directory on the SD card image. This can be downloaded from the IP400 +project site. To find it, go to ip400.adrcs.org, then to the Hardware page, scroll to the bottom to find the link +to the getting started page. diff --git a/README.md b/README.md index e07240c..1dd6743 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,12 @@ Welcome to the IP400 project. The directories here have been restructured. The Node Firmware directory contains the hardware abstraction for several platforms, -and the source directory contains code that is common to all. - -The platform code and precompiled binaries have been temporarily removed, due to a new repease of the CubeIDE from ST, and -a pending new release of the node firmware. +and the IP400 directory contains source code that is common to all node types. There are precompiled binary files that can be +uploaded using the STM flash utility. The Raspberry Pi code is now available on the disk image, which can be downloaded from the project website, ip400.adrcs.org, from the 'Getting Started' link on the hardware page. -The current revision level is 1.0 +The current revision level is 1.3 See the release notes in the 'IP400 Node Software' document.