Updated to Rev 1.3

This commit is contained in:
adrcs 2025-06-30 20:37:17 -06:00 committed by GitHub
parent 44a94538e3
commit df0a19e80c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
23 changed files with 1592 additions and 650 deletions

View file

@ -53,6 +53,7 @@ IP400_FRAME *dequeFrame(FRAME_QUEUE *que);
// queue management // queue management
void insque (struct qelem *elem, struct qelem *pred); void insque (struct qelem *elem, struct qelem *pred);
void remque (struct qelem *elem); void remque (struct qelem *elem);
BOOL quehasData(FRAME_QUEUE *que);
// print memory stats // print memory stats
void Print_Memory_Stats(void); void Print_Memory_Stats(void);

View file

@ -38,13 +38,6 @@
#define MAX_HOP_COUNT 15 // max hop count #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 // radio error register
#define SEQ_COMPLETE_ERR 0x8000 // Sequencer error #define SEQ_COMPLETE_ERR 0x8000 // Sequencer error
#define SEQ_ACT_TIMEOUT 0x4000 // Sequencer action timeout #define SEQ_ACT_TIMEOUT 0x4000 // Sequencer action timeout
@ -56,7 +49,7 @@ enum {
#define N_RADIO_ERRS 7 // number of the above #define N_RADIO_ERRS 7 // number of the above
// radio FSM states // radio FSM states
enum fsm_states_e { typedef enum fsm_states_e {
FSM_IDLE=0, // idle FSM_IDLE=0, // idle
FSM_ENA_RF_REG, // enable RF registers FSM_ENA_RF_REG, // enable RF registers
FSM_WAIT_ACTIVE2, // wait for active 2 FSM_WAIT_ACTIVE2, // wait for active 2
@ -77,7 +70,7 @@ enum fsm_states_e {
FSM_END_RX, // end rx FSM_END_RX, // end rx
FSM_SYNTH_PWDN, // synth power down FSM_SYNTH_PWDN, // synth power down
FSM_N_FSM_STATES FSM_N_FSM_STATES
}; } SubGFSMState;
// header flags // header flags
typedef struct frame_flags_t { typedef struct frame_flags_t {
@ -99,9 +92,13 @@ typedef struct ip400_mac_t {
uint32_t encoded; uint32_t encoded;
} callbytes; } callbytes;
union { union {
uint8_t ip[N_IPBYTES]; uint8_t vpn[N_IPBYTES];
uint16_t encip; // encoded IP data struct {
} ipbytes; uint8_t ax25Marker; // marker byte
uint8_t ax25SSID; // SSID
} AX25;
uint16_t encvpn; // encoded vpn address
} vpnBytes;
} IP400_MAC; } IP400_MAC;
// hop table // hop table
@ -117,9 +114,9 @@ typedef struct ip400_frame_t {
IP400_FLAGS flags; // flag bit field IP400_FLAGS flags; // flag bit field
uint16_t allflags; // all flags uint16_t allflags; // all flags
} flagfld; } flagfld;
uint16_t length; // data length
uint32_t seqNum; // packet sequence number uint32_t seqNum; // packet sequence number
void *buf; // data to send void *buf; // data to send
uint16_t length; // data length
void *hopTable; // hop table address void *hopTable; // hop table address
} IP400_FRAME; } IP400_FRAME;
@ -147,8 +144,8 @@ typedef enum {
P25_FRAME, // TIA project 25 P25_FRAME, // TIA project 25
NXDN_FRAME, // NXDN NXDN_FRAME, // NXDN
M17_FRAME, // M17 M17_FRAME, // M17
TBD_1, ECHO_REQUEST, // echo request frame
TBD_2, ECHO_RESPONSE, // echo response frame
LOCAL_COMMAND // local command frame LOCAL_COMMAND // local command frame
} IP400FrameType; } IP400FrameType;
@ -188,7 +185,9 @@ typedef struct frame_stats_t {
uint32_t nRepeated; // repeated frames uint32_t nRepeated; // repeated frames
} FRAME_STATS; } FRAME_STATS;
// links in
uint8_t getFrameStatus(void); uint8_t getFrameStatus(void);
void SendBeacon(void);
// references // references
uint8_t callEncode(char *callsign, uint16_t port, IP400_FRAME *frame, uint8_t dest, uint8_t offset); 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); void EncodeChunk(char *src, int len, uint32_t *enc);
// frame senders // 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); 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); void SendSPIFrame(void *spiHdr, uint8_t *payload, int len);
// //
BOOL EnqueChatFrame(void *Frame); // queue a chat frame BOOL EnqueChatFrame(void *Frame); // queue a chat frame
FRAME_STATS *GetFrameStats(void); // return the frame stats FRAME_STATS *GetFrameStats(void); // return the frame stats
uint32_t GetRadioStatus(void); // get the radio status 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 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_ */ #endif /* FRAME_H_ */

View file

@ -36,6 +36,7 @@
#define IP_172_ID 2 // use ip 172 from ID #define IP_172_ID 2 // use ip 172 from ID
#define __IP_GROUP IP_172_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 /* If your port already typedef's sa_family_t, define SA_FAMILY_T_DEFINED
to prevent this code from redefining it. */ 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 GetIP172Addr(IP400_MAC *fr, SOCKADDR_IN *ipaddr);
void Get172AddrFromID(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 #if __IP_GROUP == IP_172_ID
#define GetIPAddr Get172AddrFromID #define GetIPAddr Get172AddrFromID
@ -90,8 +91,8 @@ void GetIPAddrFromMAC(IP400_MAC *fr, SOCKADDR_IN *ipAddr);
#endif #endif
// Get IP address from setup data // Get IP address from setup data
void GetMyIP(SOCKADDR_IN **ipAddr); void GetMyVPN(SOCKADDR_IN **ipAddr);
void GetMyMAC(IP400_MAC **mac); void GetMyMAC(IP400_MAC **mac);
uint16_t GetIPLowerWord(void); uint16_t GetVPNLowerWord(void);
#endif /* INC_IP_H_ */ #endif /* INC_IP_H_ */

View file

@ -25,6 +25,12 @@
#include <config.h> #include <config.h>
// led command modes
typedef enum {
LED_CMD_RF, // set RF indication mode
LED_CMD_TELEM // set telemetry mode
} LEDCommand;
// LED functions // LED functions
enum { enum {
@ -39,7 +45,7 @@ enum {
N_LED_MODE // modes1 N_LED_MODE // modes1
}; };
#if _BOARD_TYPE == PI_BOARD #if (_BOARD_TYPE == PI_BOARD) || (_BOARD_TYPE == IP400_MODULE)
// bidirectional LED // bidirectional LED
#define LED_Green_Pin GPIO_PIN_0 #define LED_Green_Pin GPIO_PIN_0
#define LED_Green_GPIO_Port GPIOB #define LED_Green_GPIO_Port GPIOB
@ -55,6 +61,12 @@ enum {
#define PA_ENA_GPIO_Port GPIOA #define PA_ENA_GPIO_Port GPIOA
#endif #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 #if _BOARD_TYPE == TELEM_BOARD
// define the LED ports // define the LED ports
#define LED_Green_Pin GPIO_PIN_14 #define LED_Green_Pin GPIO_PIN_14
@ -69,9 +81,13 @@ enum {
// PA enable pin // PA enable pin
#define PA_ENA_Pin GPIO_PIN_7 #define PA_ENA_Pin GPIO_PIN_7
#define PA_ENA_GPIO_Port GPIOA #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 #endif
void SetLEDMode(uint8_t mode);
#endif /* INC_LED_H_ */ #endif /* INC_LED_H_ */

View file

@ -31,6 +31,9 @@
#define __USE_SETUP_PARAMS 1 // set to 1 to use setup parameters #define __USE_SETUP_PARAMS 1 // set to 1 to use setup parameters
#define US 0 // station is in the US #define US 0 // station is in the US
#define MAX_DATAFLD 10 // max data field size
#define MAX_DESC 32 // description field
// defined elsewhere // defined elsewhere
extern char *modTypes[]; extern char *modTypes[];
extern char *paModes[]; extern char *paModes[];
@ -38,10 +41,9 @@ extern char *paModes[];
typedef struct setup_flags_t { typedef struct setup_flags_t {
unsigned fsk: 1; // can run FSK unsigned fsk: 1; // can run FSK
unsigned ofdm: 1; // can run OFDM 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 repeat: 1; // repeat mode default
unsigned ext: 1; // use extended callsign unsigned SSID: 4; // AX.25 SSID
unsigned rate: 3; // data rate in use
} SETUP_FLAGS; } SETUP_FLAGS;
// data rates in the flag field (FSK mode) // data rates in the flag field (FSK mode)
@ -60,6 +62,7 @@ typedef struct setup_data_t {
SETUP_FLAGS flags; // flags SETUP_FLAGS flags; // flags
char stnCall[MAX_CALL]; // station call sign char stnCall[MAX_CALL]; // station call sign
char extCall[EXT_CALL]; // extended call sign char extCall[EXT_CALL]; // extended call sign
char Description[MAX_DESC]; // description of my radio
char latitude[10]; // latititude char latitude[10]; // latititude
char longitude[10]; // longitude char longitude[10]; // longitude
char gridSq[10]; // grid square char gridSq[10]; // grid square
@ -92,10 +95,12 @@ typedef struct stn_params_t {
// beacon header // beacon header
typedef union { typedef union {
struct beacon_hdr_t { struct beacon_hdr_t {
SETUP_FLAGS flags; SETUP_FLAGS flags; // setup flags
uint8_t txPower; uint8_t txPower; // transmit power
uint8_t FirmwareMajor; uint8_t FirmwareMajor; // firmware major version
uint8_t FirmwareMinor; uint8_t FirmwareMinor; // firmware minor version
uint32_t txFrequency; // transmit frequency
uint32_t rxFrequency; // receive frequency
} setup; } setup;
uint8_t hdrBytes[sizeof(struct beacon_hdr_t)]; uint8_t hdrBytes[sizeof(struct beacon_hdr_t)];
} BEACON_HEADER; } BEACON_HEADER;
@ -135,5 +140,8 @@ BOOL UpdateSetup(void);
// device ID // device ID
uint32_t GetDevID0(void); uint32_t GetDevID0(void);
uint32_t GetDevID1(void); uint32_t GetDevID1(void);
// build ID
char *getRevID(void);
char *getDateID(void);
#endif /* INC_SETUP_H_ */ #endif /* INC_SETUP_H_ */

View file

@ -37,4 +37,6 @@ void TOD_10SecTimer(void); // 10 second timer
void getTOD(TIMEOFDAY *time); void getTOD(TIMEOFDAY *time);
BOOL setTOD(char *todString); BOOL setTOD(char *todString);
int getElapsed(TIMEOFDAY *time);
#endif /* INC_TOD_H_ */ #endif /* INC_TOD_H_ */

View file

@ -27,6 +27,13 @@
#include "types.h" #include "types.h"
#include "main.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 // definitions
typedef uint32_t UART_TIMEOUT_T; // uart timer type typedef uint32_t UART_TIMEOUT_T; // uart timer type
typedef uint16_t BUFFER_SIZE_T; // buffer size 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); BOOL USART_Send_Char(const char c);
void USART_Print_string(char *format, ...); 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); 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); 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_ */ #endif /* INC_USART_H_ */

View file

@ -23,9 +23,22 @@
#ifndef INC_UTILS_H_ #ifndef INC_UTILS_H_
#define 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 // replacement for missing itoa
int ascii2Dec(char *dec); int ascii2Dec(char *dec);
/*
* Signed double
*/
// and its double counterpart... // and its double counterpart...
double ascii2double(char *val); double ascii2double(char *val);
@ -35,6 +48,11 @@ void hex2ascii(uint8_t hex, char *buf);
// check an entry for floating point // check an entry for floating point
BOOL isfloat(char *val); BOOL isfloat(char *val);
// case/numeric checkers
BOOL isUpper(char c);
BOOL isLower(char c);
BOOL isNumeric(char c);
// useful in parsing NMEA sentences // useful in parsing NMEA sentences
int explode_string(char *str, char *strp[], int limit, char delim, char quote); int explode_string(char *str, char *strp[], int limit, char delim, char quote);

View file

@ -189,26 +189,42 @@ void Beacon_Task_exec(void)
return; return;
} }
timerCtrValue = timerInitValue; timerCtrValue = timerInitValue;
SendBeacon();
#if __ENABLE_GPS #if __ENABLE_GPS
// send a command to the GPS every beacon interval // send a command to the GPS every beacon interval
sendGPSCmd(); sendGPSCmd();
#endif #endif
}
void SendBeacon(void)
{
// start with the header // start with the header
beacon_hdr.setup.flags = setup_memory.params.setup_data.flags; beacon_hdr.setup.flags = setup_memory.params.setup_data.flags;
beacon_hdr.setup.txPower = setup_memory.params.radio_setup.outputPower; 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; 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 // brute force copy: compiler rounds SETUP_FLAGS to 32 bits
*buf++ = beacon_hdr.hdrBytes[0]; *buf++ = beacon_hdr.hdrBytes[0];
*buf++ = beacon_hdr.hdrBytes[4]; *buf++ = beacon_hdr.hdrBytes[4];
// tx frequency
for(int i=0;i<sizeof(uint32_t);i++)
*buf++ = beacon_hdr.hdrBytes[8+i];
// rx frequency
for(int i=0;i<sizeof(uint32_t);i++)
*buf++ = beacon_hdr.hdrBytes[12+i];
// firmware version // firmware version
*buf++ = def_params.params.FirmwareVerMajor + '0'; *buf++ = def_params.params.FirmwareVerMajor + '0';
*buf++ = def_params.params.FirmwareVerMinor + '0'; *buf++ = def_params.params.FirmwareVerMinor + '0';
// station data
char *pPayload = (char *)buf; char *pPayload = (char *)buf;
char *p2 = pPayload; char *p2 = pPayload;
@ -246,14 +262,21 @@ void Beacon_Task_exec(void)
// home grid square // home grid square
strcat(pPayload, setup_memory.params.setup_data.gridSq); strcat(pPayload, setup_memory.params.setup_data.gridSq);
strcat(pPayload, ",");
// Description field
strcat(pPayload, setup_memory.params.setup_data.Description);
int pos = strlen((char *)p2); int pos = strlen((char *)p2);
buf[pos++] = '\0'; // null terminated buf[pos++] = '\0'; // null terminated
pos += 2*sizeof(uint8_t); // account for firmware field pos += 2*sizeof(uint8_t) + 2*sizeof(uint32_t); // account for firmware and freq fields
// time to send a beacon frame.. // time to send a beacon frame..
SendBeaconFrame(setup_memory.params.setup_data.stnCall, bcnPayload, pos+1); SendBeaconFrame(bcnPayload, pos+1);
// update the mesh table
UpdateMeshStatus();
} }
/* /*

View file

@ -122,9 +122,9 @@ uint8_t callEncode(char *callsign, uint16_t ipAddr, IP400_FRAME *frame, uint8_t
p += offset; p += offset;
if(dest == DEST_CALLSIGN) if(dest == DEST_CALLSIGN)
frame->dest.ipbytes.encip = ipAddr; frame->dest.vpnBytes.encvpn = ipAddr;
else else
frame->source.ipbytes.encip = ipAddr; frame->source.vpnBytes.encvpn = ipAddr;
// broadcast address // broadcast address
if(!strcmp(callsign, "FFFF")) { if(!strcmp(callsign, "FFFF")) {
@ -189,7 +189,7 @@ BOOL callDecode(IP400_MAC *encCall, char *callsign, uint16_t *ipAddr)
*callsign = '\0'; *callsign = '\0';
if(ipAddr != NULL) if(ipAddr != NULL)
*ipAddr = encCall->ipbytes.encip; *ipAddr = encCall->vpnBytes.encvpn;
return TRUE; return TRUE;
} }

View file

@ -32,15 +32,18 @@
#include "streambuffer.h" #include "streambuffer.h"
#include "dataq.h" #include "dataq.h"
#include "ip.h" #include "ip.h"
#include "utils.h"
#define MAX_KEY 140 // max keys in a buffer #define MAX_KEY 140 // max keys in a buffer
#define MAX_DEST 20 // max chars in dest callsign #define MAX_DEST 20 // max chars in dest callsign
#define BROADCAST "FFFF" // broadcast address #define BROADCAST_CALL "FFFF" // broadcast address
#define BROADCAST_IP 0xFFFF // broadcast IP address
static char keyBuffer[MAX_KEY]; // buffer for keystrokes static char keyBuffer[MAX_KEY]; // buffer for keystrokes
uint8_t keyPos; // current position uint8_t keyPos; // current position
#define KEY_EOL 0x0D // carriage return #define KEY_EOL 0x0D // carriage return
#define KEY_ECHO 0x05 // enter echo mode
#define KEY_RPT 0x12 // change repeat status #define KEY_RPT 0x12 // change repeat status
#define KEY_EXIT 0x1A // exit key #define KEY_EXIT 0x1A // exit key
#define KEY_ESC 0x1B // escape key #define KEY_ESC 0x1B // escape key
@ -48,7 +51,11 @@ uint8_t keyPos; // current position
#define KEY_BKSP 0x08 // backspace key #define KEY_BKSP 0x08 // backspace key
#define KEY_DUMP 0x04 // toggle dump mode #define KEY_DUMP 0x04 // toggle dump mode
#define ECHO_TIME 50 // echo send time
char dest_call[MAX_DEST]; // broadcast destination 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 *dp = dest_call; // pointer to dest call characters
char *entCall = "Enter Destination Callsign"; char *entCall = "Enter Destination Callsign";
char *rptMode[] = { char *rptMode[] = {
@ -61,24 +68,43 @@ char *dumpStrings[] = {
"Dump mode->on" "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 destEnt = FALSE; // not entering destination
BOOL repeat = TRUE; // repeating by default BOOL repeat = TRUE; // repeating by default
BOOL deleteMode = FALSE; // delete mode BOOL deleteMode = FALSE; // delete mode
BOOL welcomed = FALSE; // welcome mat is out 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 FRAME_QUEUE chatQueue; // queue for inbound frames
// fwd refs // fwd refs
void sendLine(char *buffer, int len); void sendLine(char *buffer, int len);
void sendEchoreq(void);
void PrintFrame(IP400_FRAME *FrameBytes); 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 // init entry
void Chat_Task_init(void) void Chat_Task_init(void)
{ {
strcpy(dest_call, BROADCAST); strcpy(dest_call, BROADCAST_CALL);
dest_ip = BROADCAST_IP;
destEnt = FALSE; destEnt = FALSE;
chatQueue.q_forw = &chatQueue; chatQueue.q_forw = &chatQueue;
chatQueue.q_back = &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\r\n", welcome);
USART_Print_string("%s; ", rptMode[repeat]); USART_Print_string("%s; ", rptMode[repeat]);
if(!strcmp(dest_call, BROADCAST)) if(!strcmp(dest_call, BROADCAST_CALL))
USART_Print_string("%s; Destination callsign->(Broadcast)\r\n\n"); USART_Print_string("Destination callsign->(Broadcast)\r\n\n");
else 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; welcomed = TRUE;
} }
@ -108,12 +134,17 @@ BOOL EnqueChatFrame(void *raw)
IP400_FRAME *qFrame, *SrcFrame = (IP400_FRAME *)raw; IP400_FRAME *qFrame, *SrcFrame = (IP400_FRAME *)raw;
uint8_t *frameBuffer; 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 the welcome mat is not out, then discard any pending frames
if(!welcomed) if(!welcomed)
return FALSE; return FALSE;
// allocate an IP400 frame // allocate an IP400 frame
if((qFrame=malloc(sizeof(IP400_FRAME)))== NULL) if((qFrame=malloc(sizeof(IP400_FRAME))) == NULL)
return FALSE; return FALSE;
memcpy(qFrame, SrcFrame, sizeof(IP400_FRAME)); memcpy(qFrame, SrcFrame, sizeof(IP400_FRAME));
@ -139,12 +170,20 @@ BOOL EnqueChatFrame(void *raw)
*/ */
BOOL Chat_Task_exec(void) BOOL Chat_Task_exec(void)
{ {
if(!welcomed) if(!welcomed) {
strcpy(dest_call, BROADCAST_CALL);
dest_ip = BROADCAST_IP;
echoMode = FALSE;
Chat_Task_welcome(); Chat_Task_welcome();
}
char c; char c;
int nBytesinBuff; int nBytesinBuff;
int nMACEntries;
IP400_FRAME *fr; IP400_FRAME *fr;
IP400_MAC *destMac;
BOOL hasIndex = FALSE;
uint8_t cpyLen;
// process any inbound frames first.. // process any inbound frames first..
if((fr=dequeFrame(&chatQueue)) != NULL) { if((fr=dequeFrame(&chatQueue)) != NULL) {
@ -153,6 +192,15 @@ BOOL Chat_Task_exec(void)
free(fr); 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) if((nBytesinBuff=databuffer_bytesInBuffer()) == 0)
return FALSE; return FALSE;
@ -180,6 +228,35 @@ BOOL Chat_Task_exec(void)
switch (c) { 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 // CTRL/R: change repeat flag
case KEY_RPT: case KEY_RPT:
repeat = repeat ? FALSE : TRUE; repeat = repeat ? FALSE : TRUE;
@ -196,12 +273,6 @@ BOOL Chat_Task_exec(void)
// escape key: get a destination call sign // escape key: get a destination call sign
case KEY_ESC: case KEY_ESC:
if(destEnt) {
strcpy(dest_call, BROADCAST);
USART_Print_string("Destination set to broadcast\r\n");
destEnt=FALSE;
break;
}
if(keyPos == 0) { if(keyPos == 0) {
USART_Print_string("%s->", entCall); USART_Print_string("%s->", entCall);
dp = dest_call; dp = dest_call;
@ -212,16 +283,64 @@ BOOL Chat_Task_exec(void)
// EOL key: sent the packet // EOL key: sent the packet
case KEY_EOL: case KEY_EOL:
USART_Print_string("\r\n"); USART_Print_string("\r\n");
// in destination entry mode
if(destEnt) { if(destEnt) {
int cpyLen = keyPos > MAX_CALL ? MAX_CALL : keyPos; if(keyPos == 0) {
strncpy(dest_call, keyBuffer, cpyLen); 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; 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 { } else {
if(keyPos != 0) { if(keyPos != 0) {
keyBuffer[keyPos++] = '\0'; keyBuffer[keyPos++] = '\0';
sendLine(keyBuffer, keyPos); sendLine(keyBuffer, keyPos);
} else { } 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; keyPos = 0;
@ -240,6 +359,8 @@ BOOL Chat_Task_exec(void)
break; break;
default: default:
if(destEnt)
c = isLower(c) ? toupper(c) : c;
USART_Send_Char(c); USART_Send_Char(c);
if(keyPos < MAX_KEY) if(keyPos < MAX_KEY)
keyBuffer[keyPos++] = c; keyBuffer[keyPos++] = c;
@ -253,7 +374,114 @@ BOOL Chat_Task_exec(void)
// send a line of text // send a line of text
void sendLine(char *buffer, int len) 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;i++) {
GetVPNAddrFromMAC(MacEntries[i].MacEntry, &ipAddr);
USART_Print_string("%s[%d]\t(%d.%d.%d.%d)\r\n",
call, i+1,
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);
}
USART_Print_string("Destination has been set to all instances\r\n");
USART_Print_string("For a specific address, use call[subscript] as listed\r\n\n");
free(MacEntries);
}
// split a callsign x-n into x and n
BOOL splitCall(char *dest_call, uint8_t *dest_idx, char *keyBuffer, int keyLen, uint8_t *adjLen)
{
for(int i=0;i<keyLen;i++) {
if(keyBuffer[i] == '[') {
strncpy(dest_call, keyBuffer, i);
*dest_idx = keyBuffer[i+1] - '0';
*adjLen = i;
return TRUE;
}
}
*adjLen = keyLen;
strncpy(dest_call, keyBuffer, keyLen);
return FALSE;
}
// get the nth entry
void GetNthMeshentry(char *dest_call, int cpyLen, uint8_t index, int nMACEntries, IP400_MAC **mac)
{
IP400_MAC *macEntry;
SOCKADDR_IN ipAddr;
int nEntries = 0;
if((index < 0) || (index > 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 // source call
callDecode(&FrameBytes->source, decCall, NULL); callDecode(&FrameBytes->source, decCall, NULL);
GetIPAddrFromMAC(&FrameBytes->source, &fromIP); GetVPNAddrFromMAC(&FrameBytes->source, &fromIP);
USART_Print_string("%s(%d.%d.%d.%d) ", decCall, 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_b1, fromIP.sin_addr.S_un.S_un_b.s_b2,
@ -292,15 +520,21 @@ void PrintFrame(IP400_FRAME *FrameBytes)
USART_Print_string("BROADCAST"); USART_Print_string("BROADCAST");
} else { } else {
callDecode(&FrameBytes->dest, decCall, NULL); 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 // flags
USART_Print_string("[%d:%04d]:", FrameBytes->flagfld.flags.hop_count, FrameBytes->seqNum); USART_Print_string("[%d:%04d]:", FrameBytes->flagfld.flags.hop_count, FrameBytes->seqNum);
// now dump the data // now dump the data
if(FrameBytes->flagfld.flags.coding == ECHO_RESPONSE) {
USART_Print_string("Echo response\r\n");
} else {
memcpy(printBuf, FrameBytes->buf, dataLen); memcpy(printBuf, FrameBytes->buf, dataLen);
printBuf[dataLen] = '\0'; printBuf[dataLen] = '\0';
USART_Print_string("%s\r\n", printBuf); USART_Print_string("%s\r\n", printBuf);
}
} }

View file

@ -64,3 +64,14 @@ IP400_FRAME *dequeFrame(FRAME_QUEUE *que)
free(f); free(f);
return ipFrame; return ipFrame;
} }
/*
* Test to see if anything is queued
*/
BOOL quehasData(FRAME_QUEUE *que)
{
if(que->q_back == que)
return FALSE;
return TRUE;
}

View file

@ -34,115 +34,106 @@
#include <stm32wl3x_nucleo.h> #include <stm32wl3x_nucleo.h>
#endif #endif
#include <cmsis_os2.h>
#include <FreeRTOS.h>
#include <semphr.h>
#include "frame.h" #include "frame.h"
#include "dataq.h" #include "dataq.h"
#include "setup.h" #include "setup.h"
#include "tasks.h"
#include "led.h"
#include "spi.h"
#include "usart.h"
#include "ip.h" #include "ip.h"
#include "spi.h"
#include "tasks.h"
// local defines // local defn's
#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
uint32_t nextSeq; // next frame sequence number 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) 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; 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) 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; IP400_FRAME *txFrame;
@ -156,7 +147,7 @@ BOOL SendTextFrame(char *srcCall, uint16_t srcIPAddr, char *destCall, uint16_t d
// format the header // format the header
uint8_t offset = callEncode(srcCall, srcIPAddr, txFrame, SRC_CALLSIGN, 0); 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 // copy the payload in
uint8_t *f = (uint8_t *)txFrame->buf; 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->length = length + offset;
txFrame->flagfld.flags.hop_count = 0; 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.repeat = repeat;
txFrame->flagfld.flags.hoptable = 0; txFrame->flagfld.flags.hoptable = 0;
txFrame->hopTable = NULL; 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) void SendSPIFrame(void *spi, uint8_t *payload, int len)
{ {
IP400_FRAME *spiFrame; IP400_FRAME *spiFrame;
@ -259,10 +218,10 @@ void SendSPIFrame(void *spi, uint8_t *payload, int len)
srcCall = myMac->callbytes.bytes; srcCall = myMac->callbytes.bytes;
memcpy(spiFrame->source.callbytes.bytes, srcCall, N_CALL); 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->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); spiFrame->flagfld.allflags = (uint16_t)(spiHdr->hopCount) + (uint16_t)(spiHdr->coding<<4) + (uint16_t)(spiHdr->flags<<8);
if(spiFrame->flagfld.flags.hop_count) if(spiFrame->flagfld.flags.hop_count)
@ -282,8 +241,9 @@ BOOL FrameisMine(IP400_FRAME *frame)
char decCall[30]; char decCall[30];
// check if I am the originator call sign // check if I am the originator call sign
uint16_t myIPAddr = GetVPNLowerWord();
callDecode(&frame->source, decCall, NULL); callDecode(&frame->source, decCall, NULL);
if(CompareToMyCall(decCall)) if(CompareToMyCall(decCall) && (frame->source.vpnBytes.encvpn == myIPAddr))
return TRUE; return TRUE;
// now check to see if I repeated this frame // now check to see if I repeated this frame
@ -295,7 +255,7 @@ BOOL FrameisMine(IP400_FRAME *frame)
IP400_MAC *myMac; IP400_MAC *myMac;
GetMyMAC(&myMac); GetMyMAC(&myMac);
for(int i=0;i<frame->flagfld.flags.hop_count; i++) for(int i=0;i<frame->flagfld.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 TRUE;
return FALSE; return FALSE;
@ -339,235 +299,57 @@ void RepeatFrame(IP400_FRAME *frame)
IP400_MAC *myMac; IP400_MAC *myMac;
GetMyMAC(&myMac); GetMyMAC(&myMac);
table[hopCount].hopAddr.callbytes.encoded = myMac->callbytes.encoded; 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.hoptable = TRUE;
rptFrame->flagfld.flags.hop_count = hopCount + 1; rptFrame->flagfld.flags.hop_count = hopCount + 1;
Stats.nRepeated++; FRAME_STATS *stats = GetFrameStats();
stats->nRepeated++;
QueueTxFrame(rptFrame); 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); FRAME_STATS *stats = GetFrameStats();
__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;
// find a reason to reject a frame... // 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 // process the frame if it is not mine and unique
// do a sanity check on the length // 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) { switch(frameType) {
// process a beacon frame // process a beacon frame
case BEACON_PACKET: case BEACON_PACKET:
if(Mesh_Accept_Frame((void *)&rFrame, Stats.lastRSSI)) { if(Mesh_Accept_Frame((void *)rFrame, stats->lastRSSI)) {
Mesh_ProcessBeacon((void *)&rFrame, Stats.lastRSSI); Mesh_ProcessBeacon((void *)rFrame, stats->lastRSSI);
#if __DUMP_BEACON #if __DUMP_BEACON
EnqueChatFrame((void *)&rFrame); EnqueChatFrame((void *)&rFrame);
#endif #endif
EnqueSPIFrame(&rFrame); EnqueSPIFrame(rFrame);
Stats.nBeacons++; stats->nBeacons++;
} }
break; break;
// process a local chat frame // process a local chat frame
case UTF8_TEXT_PACKET: case UTF8_TEXT_PACKET:
if(Mesh_Accept_Frame((void *)&rFrame, Stats.lastRSSI)) { if(Mesh_Accept_Frame((void *)rFrame, stats->lastRSSI)) {
EnqueChatFrame((void *)&rFrame); EnqueChatFrame((void *)rFrame);
Stats.framesOK++; stats->framesOK++;
} }
break; break;
@ -583,9 +365,22 @@ void Frame_Rxtask_exec(void)
case P25_FRAME: // TIA project 25 case P25_FRAME: // TIA project 25
case NXDN_FRAME: // NXDN case NXDN_FRAME: // NXDN
case M17_FRAME: // M17 case M17_FRAME: // M17
if(Mesh_Accept_Frame((void *)&rFrame, Stats.lastRSSI)) { if(Mesh_Accept_Frame((void *)rFrame, stats->lastRSSI)) {
EnqueSPIFrame((void *)&rFrame); EnqueSPIFrame((void *)rFrame);
Stats.framesOK++; 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; break;
@ -594,54 +389,19 @@ void Frame_Rxtask_exec(void)
break; break;
default: default:
Stats.dropped++; stats->dropped++;
logger(LOG_ERROR, "Frame Received with unknown coding: %d\r\n", rFrame.flagfld.flags.coding); logger(LOG_ERROR, "Frame Received with unknown coding: %d\r\n", rFrame->flagfld.flags.coding);
break; break;
} }
} }
// repeat the frame if the repeat flag is set and the hop count is not exhausted // repeat the frame if the repeat flag is set and the hop count is not exhausted
if(!isMine) { if(!isMine) {
if(rFrame.flagfld.flags.repeat && (rFrame.flagfld.flags.hop_count < MAX_HOP_COUNT)) if(rFrame->flagfld.flags.repeat && (rFrame->flagfld.flags.hop_count < MAX_HOP_COUNT))
RepeatFrame(&rFrame); RepeatFrame(rFrame);
} }
if(isMine) if(isMine)
Stats.dropped++; stats->dropped++;
// restart the receiver
EnableRx();
} }
// 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();
}

View file

@ -51,55 +51,6 @@ union {
#define NETMASK 0xF8 // mask for last byte #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 * 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 * 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_family = AF_INET;
ipAddr->sin_addr.S_un.S_addr = 0; 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; ipAddr->sin_addr.S_un.S_un_b.s_b2 = b2 + IP_172_START;
// remainder from the ip Address field // 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_b3 = fr->vpnBytes.vpn[1];
ipAddr->sin_addr.S_un.S_un_b.s_b4 = fr->ipbytes.ip[0]; ipAddr->sin_addr.S_un.S_un_b.s_b4 = fr->vpnBytes.vpn[0];
// port number from source // port number from source
ipAddr->sin_port = 0; 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(); uniqueID.word = GetDevID0() ^ GetDevID1();

View file

@ -27,22 +27,22 @@
* Red solid when HAL error or NMI occurred * Red solid when HAL error or NMI occurred
* *
* On the NUCLEO board, there are three, red, green and blue * 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 * GREEN (LD2) Duplicates the GREEN bi-color function
* RED (LD3) Duplicates the RED Bi-color function * RED (LD3) Duplicates the RED Bi-color function
*/ */
#include <config.h>
#include "types.h" #include "types.h"
#include "led.h" #include "led.h"
#include "usart.h" #include "usart.h"
#include <config.h>
// include LED defs from the right place // include LED defs from the right place
#if _BOARD_TYPE == NUCLEO_BOARD #if _BOARD_TYPE == NUCLEO_BOARD
#include <stm32wl3x_nucleo.h> #include <stm32wl3x_nucleo.h>
#endif #endif
// local defines // local defines
#define FLASH_OFF 0 // LED flashing: off state #define FLASH_OFF 0 // LED flashing: off state
#define FLASH_ON 1 // LED flashing: on state #define FLASH_ON 1 // LED flashing: on state
@ -52,11 +52,10 @@
#define TEST_TIMER 20 // test timer #define TEST_TIMER 20 // test timer
// internals // internals
void SetLEDMode(uint8_t mode); void SetLEDState(uint8_t mode);
void LED_SetOff(void); void LED_SetOff(void);
void LED_SetRed(void); void LED_SetRed(void);
void LED_SetGreen(void); void LED_SetGreen(void);
void LED_SetError(void);
void setTxLED(BOOL state); void setTxLED(BOOL state);
// vars // vars
@ -80,11 +79,19 @@ struct led_tests_t {
char *testName; char *testName;
uint8_t testMode; uint8_t testMode;
} LEDTests[N_LED] = { } 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 RED On", BICOLOR_RED },
{"Bicolor GREEN On", BICOLOR_GREEN }, {"Bicolor GREEN On", BICOLOR_GREEN },
{"Bicolor off", BICOLOR_OFF }, {"Bicolor off", BICOLOR_OFF },
{"Tx LED On", TX_LED_ON }, {"Tx LED On", TX_LED_ON },
{"Tx LED Off", TX_LED_OFF } {"Tx LED Off", TX_LED_OFF }
#endif
}; };
// Initialization // Initialization
void Led_Task_Init(void) void Led_Task_Init(void)
@ -149,12 +156,12 @@ BOOL LedTest(void)
saveMode = ledMode; saveMode = ledMode;
if(testNum == N_LED) { if(testNum == N_LED) {
testNum = 0; testNum = 0;
SetLEDMode(saveMode); SetLEDState(saveMode);
return TRUE; return TRUE;
} }
USART_Print_string("%s\r\n", LEDTests[testNum].testName); USART_Print_string("%s\r\n", LEDTests[testNum].testName);
SetLEDMode(LEDTests[testNum].testMode); SetLEDState(LEDTests[testNum].testMode);
testTimer = TEST_TIMER; testTimer = TEST_TIMER;
testNum++; testNum++;
return FALSE; return FALSE;
@ -164,8 +171,14 @@ BOOL LedTest(void)
return FALSE; return FALSE;
} }
// API routines: set the LED mode // API routine: get the LED mode
void SetLEDMode(uint8_t mode) uint8_t GetLEDMode()
{
return ledMode;
}
// API routines: set the LED state for nodes only
void SetLEDState(uint8_t mode)
{ {
ledMode = mode; ledMode = mode;

View file

@ -31,6 +31,11 @@
#include "tod.h" #include "tod.h"
#include "config.h" #include "config.h"
#include "ip.h" #include "ip.h"
#include "frame.h"
//macros
#define tolower(c) (c+0x20)
#define toupper(c) (c-0x20)
// menu state // menu state
uint8_t menuState; // menu state uint8_t menuState; // menu state
@ -91,6 +96,8 @@ char *selectItem = "Select an item->";
static char menu[100]; // Buffer for file items static char menu[100]; // Buffer for file items
int sel_item = 0; // selected item int sel_item = 0; // selected item
uint8_t activeMenu; // active menu uint8_t activeMenu; // active menu
uint8_t editMode; // current entry editing mode
uint8_t maxEntry; // max entry length
// forward refs in this module // forward refs in this module
void printMenu(void); // print the menu void printMenu(void); // print the menu
@ -103,6 +110,7 @@ void Print_Radio_errors(uint32_t errs);
void Print_FSM_state(uint8_t state); void Print_FSM_state(uint8_t state);
uint8_t getEntry(int activeMenu, int item); uint8_t getEntry(int activeMenu, int item);
uint8_t getKeyEntry(void); uint8_t getKeyEntry(void);
char editEntry(char c);
// list of menus // list of menus
enum { enum {
@ -119,6 +127,35 @@ enum {
RET_PAUSE // pause before leaving 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 * The first part of this code contains the menus and processing routines
* handle the various menu options * handle the various menu options
@ -130,13 +167,20 @@ uint8_t printAllSetup(void)
getTOD(&tod); getTOD(&tod);
SOCKADDR_IN *ipAddr; SOCKADDR_IN *ipAddr;
USART_Print_string("Current firmware is at %d.%d\r\n",def_params.params.FirmwareVerMajor, strcpy(menu, getRevID());
def_params.params.FirmwareVerMinor); 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("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()); USART_Print_string("Radio ID is %08x%08x\r\n", GetDevID0(), GetDevID1());
GetMyIP(&ipAddr); GetMyVPN(&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, 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); ipAddr->sin_addr.S_un.S_un_b.s_b3, ipAddr->sin_addr.S_un.S_un_b.s_b4);
printStationSetup(); printStationSetup();
printRadioSetup(); printRadioSetup();
@ -174,10 +218,17 @@ uint8_t showstats(void)
{ {
Print_Frame_stats(GetFrameStats()); Print_Frame_stats(GetFrameStats());
Print_Radio_errors(GetRadioStatus()); Print_Radio_errors(GetRadioStatus());
Print_FSM_state(GetFSMState()); Print_FSM_state((uint8_t )GetFSMState());
return RET_PAUSE; return RET_PAUSE;
} }
// Send a beacon frame now...
uint8_t sendBeacon(void)
{
SendBeacon();
return RET_DONE;
}
// set the radio entry mode // set the radio entry mode
uint8_t setRadio(void) uint8_t setRadio(void)
{ {
@ -253,6 +304,8 @@ struct menuItems_t {
char *menuLine; // text of menu line char *menuLine; // text of menu line
char selChar; // character to select it char selChar; // character to select it
uint8_t (*func)(void); // processing function uint8_t (*func)(void); // processing function
uint8_t entryMode; // entry mode
uint8_t fldSize; // size of entry field
}; };
// main menu // main menu
@ -268,53 +321,65 @@ struct menuItems_t {
#define N_MEM 0 #define N_MEM 0
#endif #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] = { struct menuItems_t mainMenu[N_MAINMENU] = {
{ "List setup parameters\r\n", 'A', printAllSetup }, { "List setup parameters\r\n", 'A', printAllSetup, ENTRY_NONE, 0 },
{ "Mesh Status\r\n", 'B', listMesh }, { "Mesh Status\r\n", 'B', listMesh, ENTRY_NONE, 0 },
{ "Chat Mode\r\n", 'C', chatMode }, { "Chat/Echo Mode\r\n", 'C', chatMode, ENTRY_NONE, 0 },
{ "Dump Frame stats\r\n", 'D', showstats }, { "Dump Frame stats\r\n", 'D', showstats, ENTRY_NONE, 0 },
{ "Send Beacon\r\n", 'E', sendBeacon, ENTRY_NONE, 0 },
#if __ENABLE_GPS #if __ENABLE_GPS
{ "GPS Echo mode\r\n", 'G', gpsEcho }, { "GPS Echo mode\r\n", 'G', gpsEcho, ENTRY_NONE, 0 },
#endif #endif
{ "LED test\r\n", 'L', ledTest }, { "LED test\r\n", 'L', ledTest, ENTRY_NONE, 0 },
#if __MEM_DEBUG #if __MEM_DEBUG
{ "Memory Status\r\n", 'M', memStats }, { "Memory Status\r\n", 'M', memStats, ENTRY_NONE, 0 },
#endif #endif
{ "Set Radio Parameters\r\n", 'R', setRadio }, { "Set Radio Parameters\r\n", 'R', setRadio, ENTRY_NONE, 0 },
{ "Set Station Parameters\r\n", 'S', setStation }, { "Set Station Parameters\r\n", 'S', setStation, ENTRY_NONE, 0 },
{ "Set clock (HH:MM)\r\n\n", 'T', setParam }, { "Set clock (HH:MM)\r\n\n", 'T', setParam, ENTRY_TIME, MAX_TIMESIZE },
{ "Write Setup Values\r\n", 'W', writeSetup }, { "Write Setup Values\r\n", 'W', writeSetup, ENTRY_NONE, 0 },
{ "Exit\r\n\n", 'X', exitMenu } { "Exit\r\n\n", 'X', exitMenu, ENTRY_NONE, 0 }
}; };
// radio menu // radio menu
#define N_RADIOMENU 8 #define N_RADIOMENU 8
struct menuItems_t radioMenu[N_RADIOMENU] = { struct menuItems_t radioMenu[N_RADIOMENU] = {
{ "RF Frequency\r\n", 'A', setParam }, { "RF Frequency\r\n", 'A', setParam, ENTRY_FLOAT, MAX_FLDSIZE },
{ "Data Rate\r\n", 'B', setParam }, { "Data Rate\r\n", 'B', setParam, ENTRY_FLOAT, MAX_FLDSIZE },
{ "Peak Deviation\r\n", 'C', setParam }, { "Peak Deviation\r\n", 'C', setParam, ENTRY_FLOAT, MAX_FLDSIZE },
{ "Channel Filter BW\r\n", 'D', setParam }, { "Channel Filter BW\r\n", 'D', setParam, ENTRY_FLOAT, MAX_FLDSIZE },
{ "Output Power (dBm)\r\n", 'E', setParam }, { "Output Power (dBm)\r\n", 'E', setParam, ENTRY_NUMERIC, MAX_FLDSIZE },
{ "Rx Squelch (dBm)\r\n\n", 'F', setParam }, { "Rx Squelch (dBm)\r\n\n", 'F', setParam, ENTRY_NUMERIC, MAX_FLDSIZE },
{ "List Settings\r\n", 'L', printRadSetup }, { "List Settings\r\n", 'L', printRadSetup, ENTRY_NONE, MAX_FLDSIZE },
{ "Return to main menu\r\n\n", 'X', exitMenu } { "Return to main menu\r\n\n", 'X', exitMenu, ENTRY_NONE, MAX_FLDSIZE }
}; };
// these need to correspond to the items above // these need to correspond to the items above
// station menu // 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] = { struct menuItems_t stationMenu[N_STATIONMENU] = {
{ "Callsign\r\n", 'A', setParam }, { "Callsign\r\n", 'A', setParam, ENTRY_UPPERCASE, MAX_CALL },
{ "Latitude\r\n", 'B', setParam }, { "Description\r\n", 'B', setParam, ENTRY_ANYCASE, MAX_DESC },
{ "Longitude\r\n", 'C', setParam }, { "Latitude\r\n", 'C', setParam, ENTRY_FLOAT, MAX_FLOATSIZE },
{ "Grid Square\r\n", 'D', setParam }, { "Longitude\r\n", 'D', setParam, ENTRY_FLOAT, MAX_FLOATSIZE },
{ "Repeat Default\r\n", 'E', setParam }, { "Grid Square\r\n", 'E', setParam, ENTRY_ANYCASE, MAX_CALL },
{ "Beacon Interval\r\n\n", 'F', setParam }, { "Repeat Mode(Y/N)\r\n", 'F', setParam, ENTRY_UPPERCASE, 1 },
{ "List Settings\r\n", 'L', printStnSetup }, { "Beacon Interval\r\n", 'G', setParam, ENTRY_NUMERIC, MAX_FLDSIZE },
{ "Return to main menu\r\n\n", 'X', exitMenu } #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 // menu contents
@ -387,6 +452,8 @@ void Menu_Task_Exec(void)
case MENU_SELECTED: case MENU_SELECTED:
m=menuContents[activeMenu].menus; m=menuContents[activeMenu].menus;
m += sel_item; m += sel_item;
editMode = m->entryMode;
maxEntry = m->fldSize;
switch((*m->func)()) { switch((*m->func)()) {
case RET_MORE: case RET_MORE:
@ -497,17 +564,6 @@ BOOL pause(void)
* This part pertains to getting an entry and validating it.. * 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 // forward refs
uint8_t validateEntry(int activeMenu, int item, char *keyBuffer); uint8_t validateEntry(int activeMenu, int item, char *keyBuffer);
@ -519,6 +575,7 @@ uint8_t getKeyEntry(void)
{ {
char c; char c;
char edited;
int nBytesinBuff; int nBytesinBuff;
if((nBytesinBuff=databuffer_bytesInBuffer()) == 0) if((nBytesinBuff=databuffer_bytesInBuffer()) == 0)
@ -531,14 +588,14 @@ uint8_t getKeyEntry(void)
if(delMode) { if(delMode) {
if((c != KEY_DEL) && (c != KEY_BKSP)) { if((c != KEY_DEL) && (c != KEY_BKSP)) {
USART_Print_string("\\%c", c); USART_Print_string("\\%c", c);
if(pos < MAX_ENTRY) if(pos < maxEntry)
keyBuffer[pos++] = c; keyBuffer[pos++] = c;
delMode = FALSE; delMode = FALSE;
} else { } else {
if(pos > 0) { if(pos > 0) {
USART_Print_string("%c", keyBuffer[--pos]); USART_Print_string("%c", keyBuffer[--pos]);
} else { } else {
USART_Print_string("\\\r\n"); USART_Print_string("\\\r\n->");
delMode = FALSE; delMode = FALSE;
} }
} }
@ -562,14 +619,23 @@ uint8_t getKeyEntry(void)
case KEY_DEL: case KEY_DEL:
case KEY_BKSP: case KEY_BKSP:
if(pos > 0) {
USART_Print_string("\\%c", keyBuffer[--pos]); USART_Print_string("\\%c", keyBuffer[--pos]);
delMode = TRUE; delMode = TRUE;
} else {
delMode = FALSE;
}
break; break;
default: default:
USART_Send_Char(c); if((edited=editEntry(c)) != 0) {
if(pos < MAX_ENTRY) // don't echo the entry if it is over the field size
keyBuffer[pos++] = c; if(pos < maxEntry) {
USART_Send_Char(edited);
keyBuffer[pos++] = edited;
}
}
break; break;
} }
} }
@ -577,6 +643,46 @@ uint8_t getKeyEntry(void)
return RET_MORE; 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 * Get an entry and validate it
*/ */
@ -613,18 +719,20 @@ uint8_t getEntry(int activeMenu, int item)
// data types we are updating // data types we are updating
enum { enum {
uint4_lo, // uint4 lsb
uint4_hi, // uint4 msb
uint8_type, // uint8 field uint8_type, // uint8 field
int16_type, // int16 field int16_type, // int16 field
uint32_type, // uint32 field uint32_type, // uint32 field
float_type, // floating point float_type, // floating point
char_type, // character type char_type, // character type
field_type // field type yesno_type // yes/no type
}; };
// struct to hold validation values // struct to hold validation values
typedef struct field_validator_t { typedef struct field_validator_t {
int MinVal; // minimum value int MinVal; // minimum value or string length
int MaxVal; // maximum value int MaxVal; // maximum value or string length
void *setupVal; // pointer to setup value void *setupVal; // pointer to setup value
int type; // type of entry int type; // type of entry
uint32_t scalar; // scalar to convert to decimal uint32_t scalar; // scalar to convert to decimal
@ -641,12 +749,15 @@ FIELD_VALIDATOR radioValidators[] = {
}; };
FIELD_VALIDATOR stationValidators[] = { 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.latitude, char_type, 0 },
{ 2, 14, &setup_memory.params.setup_data.longitude, 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 }, { 4, 6, &setup_memory.params.setup_data.gridSq, char_type, 0 },
{ 0, 0x8, &setup_memory.params.setup_data.flags, field_type, 0 }, { 0x8, 0, &setup_memory.params.setup_data.flags, yesno_type, 0 },
{ 1, 100, &setup_memory.params.setup_data.beaconInt, int16_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) uint8_t validateEntry(int activeMenu, int item, char *keyBuffer)
@ -670,6 +781,7 @@ uint8_t validateEntry(int activeMenu, int item, char *keyBuffer)
min = radioValidators[item].MinVal; min = radioValidators[item].MinVal;
if((newValue<min) || (newValue>max)) { if((newValue<min) || (newValue>max)) {
USART_Print_string("Must be in the range of %d to %d\r\n", min, max); USART_Print_string("Must be in the range of %d to %d\r\n", min, max);
USART_Print_string("Field not updated\r\n");
return RET_PAUSE; return RET_PAUSE;
} }
switch(radioValidators[item].type){ switch(radioValidators[item].type){
@ -695,12 +807,25 @@ uint8_t validateEntry(int activeMenu, int item, char *keyBuffer)
case STATION_MENU: case STATION_MENU:
switch(stationValidators[item].type){ 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: case int16_type:
newValue = ascii2Dec(keyBuffer); newValue = ascii2Dec(keyBuffer);
max = stationValidators[item].MaxVal; max = stationValidators[item].MaxVal;
min = stationValidators[item].MinVal; min = stationValidators[item].MinVal;
if((newValue<min) || (newValue>max)) { if((newValue<min) || (newValue>max)) {
USART_Print_string("Must be in the range of %d to %d\r\n", min, max); USART_Print_string("Must be in the range of %d to %d\r\n", min, max);
USART_Print_string("Field not updated\r\n");
return RET_PAUSE; return RET_PAUSE;
} }
uint16_t *v8 = (uint16_t *)stationValidators[item].setupVal; uint16_t *v8 = (uint16_t *)stationValidators[item].setupVal;
@ -713,22 +838,27 @@ uint8_t validateEntry(int activeMenu, int item, char *keyBuffer)
min = stationValidators[item].MinVal; min = stationValidators[item].MinVal;
if((len<min) || (len>max)) { if((len<min) || (len>max)) {
USART_Print_string("String must be %d to %d in length\r\n", min, max); USART_Print_string("String must be %d to %d in length\r\n", min, max);
USART_Print_string("Field not updated\r\n");
return RET_PAUSE; return RET_PAUSE;
} }
strcpy((char *)stationValidators[item].setupVal, keyBuffer); strcpy((char *)stationValidators[item].setupVal, keyBuffer);
break; break;
case field_type: case yesno_type:
uint8_t val = keyBuffer[0] == 'T' ? 1 : 0; if((keyBuffer[0] == 'Y') || (keyBuffer[0] == 'N')) {
uint8_t val = keyBuffer[0] == 'Y' ? 1 : 0;
uint8_t *f8= (uint8_t *)stationValidators[item].setupVal; uint8_t *f8= (uint8_t *)stationValidators[item].setupVal;
if(val) if(val)
*f8 |= (uint8_t)stationValidators[item].MinVal; *f8 |= (uint8_t)stationValidators[item].MinVal;
else else
*f8 &= (uint8_t)stationValidators[item].MinVal; *f8 &= ~((uint8_t)stationValidators[item].MinVal);
break; } else {
USART_Print_string("Please enter Y or N\r\n");
USART_Print_string("Field not updated\r\n");
return RET_PAUSE;
}
} }
break; break;
} }
// falls to here when done... // falls to here when done...
@ -795,15 +925,15 @@ char *fsm_states[FSM_N_FSM_STATES] = {
"Synth setup", "Synth setup",
"VCO calibration", "VCO calibration",
"Lock Rx and Rx", "Lock Rx and Rx",
"Llock on Rx", "Lock on Rx",
"Enable PA", "Enable PA",
"Transmit", "Transmit",
"Analog power down", "Analog power down",
"End transmit", "End transmit",
"lock on Rx", "Lock on Rx",
"Enable Rx", "Enable Rx",
"Enable LNA", "Enable LNA",
"Recieve", "Receive",
"End rx", "End rx",
"Synth power down" "Synth power down"
}; };

View file

@ -38,17 +38,6 @@
#define EXPIRY_TIME 100 // seconds since last heard #define EXPIRY_TIME 100 // seconds since last heard
#define RSSI_SCALAR 161 // 95-Gain(rx), appros 65 #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 // table entry status
typedef enum { typedef enum {
MESHTBL_UNUSED=0, // unused entry MESHTBL_UNUSED=0, // unused entry
@ -68,6 +57,10 @@ typedef struct mesh_entry_t {
uint8_t hopCount; // hop count uint8_t hopCount; // hop count
} MESH_ENTRY; } 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 // mesh table definitions
#define MAX_MESH_MEMORY 2048 // max amount of mesh memory used #define MAX_MESH_MEMORY 2048 // max amount of mesh memory used
#define MAX_MESH_ENTRIES (MAX_MESH_MEMORY/sizeof(MESH_ENTRY)) #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 // table size is limited to by memory defintion
static MESH_ENTRY MeshTable[MAX_MESH_ENTRIES] __attribute__((section("MESHTABLE"))); static MESH_ENTRY MeshTable[MAX_MESH_ENTRIES] __attribute__((section("MESHTABLE")));
int nMeshEntries = 0; int nMeshEntries = 0;
int lastEntryNum = 0;
BEACON_HEADER mesh_bcn_hdr; // beacon header BEACON_HEADER mesh_bcn_hdr; // beacon header
uint32_t seqNum; // sequence number received uint32_t seqNum; // sequence number received
// forward refs in this module // 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); void AddMeshEntry(IP400_FRAME *frameData, int16_t rssi, BOOL isBeacon);
// task initialization // task initialization
@ -105,8 +99,8 @@ void Mesh_ProcessBeacon(void *rxFrame, uint32_t rssi)
// see if we already know about it // see if we already know about it
// if so, just update last heard, expected sequence and rssi // if so, just update last heard, expected sequence and rssi
// NB: firt frame is sent with an all '1's sequence number // NB: first frame is sent with an all '1's sequence number
if((entryNum = findCall(&frameData->source)) != ENTRY_NOTFOUND) { if((entryNum = findCall(&frameData->source, 0)) != ENTRY_NOTFOUND) {
// if it is repeated frame with a higher hop count, ignore it // if it is repeated frame with a higher hop count, ignore it
if(MeshTable[entryNum].hopCount < frameData->flagfld.flags.hop_count) 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].capabilities = mesh_bcn_hdr.setup.flags;
MeshTable[entryNum].txPower = mesh_bcn_hdr.setup.txPower; MeshTable[entryNum].txPower = mesh_bcn_hdr.setup.txPower;
// all done // all done: change status to OK
MeshTable[entryNum].status = MESHTBL_VALID;
return; return;
} }
@ -160,7 +155,7 @@ void AddMeshEntry(IP400_FRAME *frameData, int16_t actRSSI, BOOL isBeacon)
newEntry.txPower = 0; newEntry.txPower = 0;
newEntry.hopCount = frameData->flagfld.flags.hop_count; newEntry.hopCount = frameData->flagfld.flags.hop_count;
GetIPAddrFromMAC(&newEntry.macAddr, &ipAddr); GetVPNAddrFromMAC(&newEntry.macAddr, &ipAddr);
if(isBeacon) { if(isBeacon) {
memcpy(mesh_bcn_hdr.hdrBytes, (struct beacon_hdr_t *)frameData->buf, sizeof(BEACON_HEADER)); 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; IP400_FRAME *frameData = (IP400_FRAME *)rxFrame;
int entryNum; int entryNum;
if((entryNum = findCall(&frameData->source)) != ENTRY_NOTFOUND) { if((entryNum = findCall(&frameData->source, 0)) != ENTRY_NOTFOUND) {
// rebooted // rebooted
if(frameData->seqNum == 0xFFFFFFFF) if(frameData->seqNum == 0xFFFFFFFF)
MeshTable[entryNum].nextSeq = 0; MeshTable[entryNum].nextSeq = 0;
@ -201,6 +196,35 @@ BOOL Check_Sender_Address(void *rxFrame, uint32_t rssi)
return TRUE; return TRUE;
} }
/*
* Update the status based on last heard
*/
void UpdateMeshStatus(void)
{
int timeSinceLastBeacon;
for(int i=0;i<nMeshEntries;i++) {
switch(MeshTable[i].status) {
case MESHTBL_UNUSED:
break;
case MESHTBL_VALID:
timeSinceLastBeacon = getElapsed(&MeshTable[i].lastHeard);
if(timeSinceLastBeacon > 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. * Check if the frame can be accepted.
* Accept a broadcast or my address if: * 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 // frame is sent a broadcast address
if((frameData->dest.callbytes.bytes[0] == BROADCAST_ADDR) 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); return Check_Sender_Address(rxFrame, rssi);
uint16_t myIPAddr = GetVPNLowerWord();
callDecode(&frameData->dest, decCall, &port); 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); return Check_Sender_Address(rxFrame, rssi);
}
// not for me // not for me
return FALSE; return FALSE;
} }
// find a callsign in the list // encode a callsign: ensure length is correct
int findCall(IP400_MAC *call) uint32_t EncodeCallSign(char *call, int len)
{ {
for(int i=0;i<nMeshEntries;i++) { IP400_MAC encoded;
if(MeshTable[i].macAddr.callbytes.encoded == call->callbytes.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;i<nMeshEntries;i++) {
if((MeshTable[i].macAddr.callbytes.encoded == call->callbytes.encoded)
&& ipCompare(MeshTable[i].macAddr.vpnBytes.encvpn, call->vpnBytes.encvpn)
&& (MeshTable[i].status == MESHTBL_VALID))
return i; return i;
} }
return ENTRY_NOTFOUND; 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;i++) {
if((MeshTable[i].macAddr.callbytes.encoded == encoded.callbytes.encoded) && (MeshTable[i].status == MESHTBL_VALID))
nEntries++;
}
return nEntries;
}
// return any MAC entry for a callsign
IP400_MAC *getMeshEntry(char *dest_call, int len)
{
IP400_MAC encoded;
encoded.callbytes.encoded = EncodeCallSign(dest_call, len);
encoded.vpnBytes.encvpn = IP_BROADCAST;
if((lastEntryNum=findCall(&encoded, 0)) == ENTRY_NOTFOUND)
return NULL;
return(&MeshTable[lastEntryNum].macAddr);
}
// find the next similar entry
IP400_MAC *getNextEntry(char *dest_call, int len)
{
IP400_MAC encoded;
// last was not found
if(lastEntryNum == ENTRY_NOTFOUND)
return NULL;
// fell off the end?
if(++lastEntryNum >= 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]; char capabilties[50];
// return the capabilities of a node // return the capabilities of a node
char *GetCapabilities(SETUP_FLAGS cap) char *GetCapabilities(SETUP_FLAGS cap)
{ {
mesh_bcn_hdr.setup.flags = cap; mesh_bcn_hdr.setup.flags = cap;
char tmpBuf[50];
if(mesh_bcn_hdr.hdrBytes[0] == 0) { if(mesh_bcn_hdr.hdrBytes[0] == 0) {
strcpy(capabilties, "Unknown"); strcpy(capabilties, "Unknown");
@ -249,15 +367,18 @@ char *GetCapabilities(SETUP_FLAGS cap)
// modes // modes
if(cap.fsk) { if(cap.fsk) {
sprintf(capabilties, "%s", FSKSpeeds[cap.rate]); sprintf(capabilties, "FSK ");
} }
else if(cap.ofdm) { else if(cap.ofdm) {
strcat(capabilties, " 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.. // repeat mode is on..
if(cap.repeat) { if(cap.repeat) {
strcat(capabilties, " RPT"); strcat(capabilties, " RPT");
@ -266,10 +387,21 @@ char *GetCapabilities(SETUP_FLAGS cap)
return capabilties; 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 // list the mesh status: walk the mesh entries
void Mesh_ListStatus(void) 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) if(nMeshEntries == 0)
return; return;
@ -277,15 +409,22 @@ void Mesh_ListStatus(void)
char decodedCall[20]; char decodedCall[20];
SOCKADDR_IN ipAddr; 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<nMeshEntries;i++) { for(int i=0;i<nMeshEntries;i++) {
if(MeshTable[i].status == MESHTBL_VALID) {
switch(MeshTable[i].status) {
case MESHTBL_UNUSED:
break;
case MESHTBL_VALID:
callDecode(&MeshTable[i].macAddr, decodedCall, NULL); callDecode(&MeshTable[i].macAddr, decodedCall, NULL);
GetIPAddrFromMAC(&MeshTable[i].macAddr, &ipAddr); trim(decodedCall);
GetVPNAddrFromMAC(&MeshTable[i].macAddr, &ipAddr);
USART_Print_string("%s\t%d.%d.%d.%d\t%-03d\t%04d\t%02d:%02d:%02d\t%d\t%s %d dBm\r\n", USART_Print_string("%s\t%d.%d.%d.%d\tOK\t%-03d\t%04d\t%02d:%02d:%02d\t%d\t%s %d dBm\r\n",
decodedCall, decodedCall,
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_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, ipAddr.sin_addr.S_un.S_un_b.s_b3, ipAddr.sin_addr.S_un.S_un_b.s_b4,
@ -295,7 +434,19 @@ void Mesh_ListStatus(void)
MeshTable[i].hopCount, MeshTable[i].hopCount,
GetCapabilities(MeshTable[i].capabilities), GetCapabilities(MeshTable[i].capabilities),
MeshTable[i].txPower); MeshTable[i].txPower);
break;
case MESHTBL_LOST:
callDecode(&MeshTable[i].macAddr, decodedCall, NULL);
GetVPNAddrFromMAC(&MeshTable[i].macAddr, &ipAddr);
USART_Print_string("%s\t%d.%d.%d.%d\tLOST\r\n",
decodedCall,
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
);
break;
}
} }
USART_Print_string("\r\n\n"); USART_Print_string("\r\n\n");
}
} }

View file

@ -16,7 +16,7 @@
Copyright (c) Alberta Digital Radio Communications Society Copyright (c) Alberta Digital Radio Communications Society
All rights reserved. All rights reserved.
Revision History: Revision History: Revised to Rev 34
---------------------------------------------------------------------------*/ ---------------------------------------------------------------------------*/
#include <stdlib.h> #include <stdlib.h>
@ -37,13 +37,17 @@
#define FLASH_PAGE_NUM 127 #define FLASH_PAGE_NUM 127
static FLASH_EraseInitTypeDef EraseInitStruct; 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 * Default setup parameters
*/ */
SETUP_MEMORY setup_memory; SETUP_MEMORY setup_memory;
SETUP_MEMORY def_params = { 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.stnCall ="NOCALL",
.params.setup_data.gridSq = "DO21vd", .params.setup_data.gridSq = "DO21vd",
.params.setup_data.latitude = "51.08", .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.PADrvMode = PA_DRV_TX_HP,
.params.radio_setup.rxSquelch = -95, .params.radio_setup.rxSquelch = -95,
// //
.params.FirmwareVerMajor = 1, // current rev is 1.1 .params.FirmwareVerMajor = 1, // current rev is 1.3
.params.FirmwareVerMinor = 1, .params.FirmwareVerMinor = 3,
.params.Magic = SETUP_MAGIC, .params.Magic = SETUP_MAGIC,
.params.SetupCRC = 0 .params.SetupCRC = 0
}; };
@ -120,26 +124,31 @@ BOOL CompareToMyCall(char *call)
*/ */
void printStationSetup(void) void printStationSetup(void)
{ {
// station callsign first // station callsigns first
USART_Print_string("Station Callsign->%s\r\n", setup_memory.params.setup_data.stnCall); USART_Print_string("Station Callsign->%s\r\n", setup_memory.params.setup_data.stnCall);
if(setup_memory.params.setup_data.flags.ext) if(setup_memory.params.setup_data.flags.AX25) {
USART_Print_string("Extended Callsign->%s\r\n"); 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("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("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("Grid Square->%s\r\n", setup_memory.params.setup_data.gridSq);
USART_Print_string("Capabilities->"); USART_Print_string("Capabilities->");
if(setup_memory.params.setup_data.flags.fsk) if(setup_memory.params.setup_data.flags.fsk)
USART_Print_string("FSK "); USART_Print_string("FSK ");
if(setup_memory.params.setup_data.flags.ofdm) if(setup_memory.params.setup_data.flags.ofdm)
USART_Print_string("OFDM "); USART_Print_string("OFDM ");
if(setup_memory.params.setup_data.flags.aredn) if(setup_memory.params.setup_data.flags.AX25)
USART_Print_string("AREDN "); USART_Print_string("AX.25 ");
if(setup_memory.params.setup_data.flags.repeat) 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 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); 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 * Manage the IP address
*/ */
void GetMyIP(SOCKADDR_IN **ipAddr) void GetMyVPN(SOCKADDR_IN **ipAddr)
{ {
*ipAddr = &myIP; *ipAddr = &myIP;
} }
@ -183,7 +192,7 @@ void printRadioSetup(void)
} }
// set my IP Address // set my IP Address
void SetMyIP(void) void SetMyVPNAddr(void)
{ {
char paddedCall[20]; char paddedCall[20];
strncpy(paddedCall, setup_memory.params.setup_data.stnCall, MAX_CALL); strncpy(paddedCall, setup_memory.params.setup_data.stnCall, MAX_CALL);
@ -192,8 +201,8 @@ void printRadioSetup(void)
// encode my callsign // encode my callsign
EncodeChunk(paddedCall, MAX_CALL, &myMAC.callbytes.encoded); EncodeChunk(paddedCall, MAX_CALL, &myMAC.callbytes.encoded);
myMAC.ipbytes.encip = GetIPLowerWord(); myMAC.vpnBytes.encvpn = GetVPNLowerWord();
GetIPAddrFromMAC(&myMAC, &myIP); GetVPNAddrFromMAC(&myMAC, &myIP);
} }
/* /*
@ -260,7 +269,7 @@ BOOL ReadSetup(void)
memAddr += sizeof(uint32_t); memAddr += sizeof(uint32_t);
} }
SetMyIP(); SetMyVPNAddr();
return TRUE; return TRUE;
} }
@ -274,6 +283,7 @@ HAL_StatusTypeDef WriteSetup(void)
__HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_ALL_ERRORS); __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_ALL_ERRORS);
// erase it first // erase it first
USART_Print_string("Erasing...");
EraseInitStruct.TypeErase = FLASH_TYPEERASE_PAGES; EraseInitStruct.TypeErase = FLASH_TYPEERASE_PAGES;
EraseInitStruct.Page = FLASH_PAGE_NUM; EraseInitStruct.Page = FLASH_PAGE_NUM;
EraseInitStruct.NbPages = 1; EraseInitStruct.NbPages = 1;
@ -284,6 +294,7 @@ HAL_StatusTypeDef WriteSetup(void)
for(int i=0;i<1000;i++); for(int i=0;i<1000;i++);
// now write // now write
USART_Print_string("Writing...");
uint32_t memAddr = FLASH_PAGE_ADDR; uint32_t memAddr = FLASH_PAGE_ADDR;
uint32_t *src_addr = setup_memory.flashwords; uint32_t *src_addr = setup_memory.flashwords;
uint16_t nwords = sizeof(SETUP_MEMORY)/sizeof(uint32_t); 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) if ((status=HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, memAddr, data32)) != HAL_OK)
return status; return status;
memAddr += sizeof(uint32_t); memAddr += sizeof(uint32_t);
USART_Print_string("%02d..", nwords);
} }
return status; return status;
} }
/*
* build related stuff
*/
char *getRevID(void)
{
return &revID[1];
}
char *getDateID(void)
{
return &dateID[1];
}
/* /*
* HAL related functions * HAL related functions

View file

@ -249,10 +249,10 @@ void SPI_Task_Exec(void)
// copy the address fields // copy the address fields
memcpy(&spiTxBuffer.spiData.hdr.fromCall, txFrame->source.callbytes.bytes, N_CALL); 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.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 // flag fields
spiTxBuffer.spiData.hdr.coding = txFrame->flagfld.flags.coding; spiTxBuffer.spiData.hdr.coding = txFrame->flagfld.flags.coding;

View file

@ -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 <cmsis_os2.h>
#include <stdlib.h>
#include <string.h>
#include <config.h>
#include <stm32wl3x_hal_mrsubg.h>
#if _BOARD_TYPE == NUCLEO_BOARD
#include <stm32wl3x_nucleo.h>
#endif
#include <cmsis_os2.h>
#include <FreeRTOS.h>
#include <semphr.h>
#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++;
}
}

View file

@ -78,3 +78,18 @@ BOOL setTOD(char *todString)
return TRUE; 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;
}

View file

@ -28,8 +28,8 @@
#include <cmsis_os2.h> #include <cmsis_os2.h>
#include <FreeRTOS.h> #include <FreeRTOS.h>
#include <semphr.h> #include <semphr.h>
#include <config.h>
#include "config.h"
#include "stream_buffer.h" #include "stream_buffer.h"
#include "types.h" #include "types.h"
#include "streambuffer.h" #include "streambuffer.h"
@ -53,15 +53,15 @@ char usartPrintBuffer[200];
// fwd refs here... // fwd refs here...
void USART_Receive_char(void); void USART_Receive_char(void);
#if __ENABLE_GPS // otherwise, it is defined
#if ENABLE_LPUART
#define LPUART_DMA_SIZE 32 #define LPUART_DMA_SIZE 32
static DATA_ELEMENT LPUARTRxChar[LPUART_DMA_SIZE]; static DATA_ELEMENT LPUARTRxChar[LPUART_DMA_SIZE];
void LPUART_Receive_char(void); void LPUART_Receive_char(void);
StreamBufferHandle_t LPUART_RxBuffer; // handle to buffer StreamBufferHandle_t LPUART_RxBuffer; // handle to buffer
StaticStreamBuffer_t LPUART_StreamBuffer; StaticStreamBuffer_t LPUART_StreamBuffer;
uint8_t gps_data[bufferSIZE]; uint8_t lpuart_data[bufferSIZE];
#endif #endif
/* /*
@ -77,8 +77,8 @@ void USART_API_init(void)
USART_RxBuffer_reset(); USART_RxBuffer_reset();
USART_Receive_char(); USART_Receive_char();
#if __ENABLE_GPS #if ENABLE_LPUART
LPUART_RxBuffer = xStreamBufferCreateStatic(bufferSIZE, 1, gps_data, &LPUART_StreamBuffer); LPUART_RxBuffer = xStreamBufferCreateStatic(bufferSIZE, 1, lpuart_data, &LPUART_StreamBuffer);
LPUART_RxBuffer_reset(); LPUART_RxBuffer_reset();
LPUART_Receive_char(); LPUART_Receive_char();
#endif #endif
@ -91,7 +91,7 @@ void USART_RxBuffer_reset(void)
return; return;
} }
#if __ENABLE_GPS #if ENABLE_LPUART
// reset the LPUART Buffer // reset the LPUART Buffer
void LPUART_RxBuffer_reset(void) void LPUART_RxBuffer_reset(void)
{ {
@ -129,15 +129,15 @@ DATA_ELEMENT databuffer_get(UART_TIMEOUT_T timeout)
/* /*
* Similar but from GPS buffer * Similar but from GPS buffer
*/ */
#if __ENABLE_GPS #if ENABLE_LPUART
// return the number of byte in the buffer // 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); size_t nBytes = xStreamBufferBytesAvailable(LPUART_RxBuffer);
return nBytes; return nBytes;
} }
// get a bytes // get a bytes
DATA_ELEMENT gpsbuffer_get(UART_TIMEOUT_T timeout) DATA_ELEMENT lpuart_buffer_get(UART_TIMEOUT_T timeout)
{ {
DATA_ELEMENT retval; DATA_ELEMENT retval;
@ -237,7 +237,7 @@ void USART_Print_string(char *format, ...)
// Transmit completed: trigger semaphore // Transmit completed: trigger semaphore
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart) void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{ {
#if __ENABLE_GPS #if ENABLE_LPUART
// Not interested in LPUART // Not interested in LPUART
if(huart->Instance == LPUART1) if(huart->Instance == LPUART1)
return; return;
@ -259,7 +259,7 @@ void USART_Receive_char(void)
// NB: Console USART and GPS LPUART share the same HAL routine // NB: Console USART and GPS LPUART share the same HAL routine
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{ {
#if __ENABLE_GPS #if ENABLE_LPUART
// service the LPUART // service the LPUART
if(huart->Instance == LPUART1) { if(huart->Instance == LPUART1) {
xStreamBufferSendFromISR(LPUART_RxBuffer, LPUARTRxChar, 1, NULL); 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); HAL_UART_Receive_IT(&huart1,(uint8_t *)USARTRxChar,1);
} }
#if __ENABLE_GPS #if ENABLE_LPUART
// send a string to the LPUART // send a string to the LPUART
void LPUART_Send_String(char *str, uint16_t len) void LPUART_Send_String(char *str, uint16_t len)
{ {

View file

@ -24,7 +24,53 @@
#include "types.h" #include "types.h"
#include "utils.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 ascii2Dec(char *dec)
{ {
int retval = 0; int retval = 0;
@ -63,10 +109,33 @@ BOOL isfloat(char *val)
return 0U; return 0U;
} }
// convert an ascii string to a double // is an upper case character
// until we encounter the decimal place, treat it the same as an integer. BOOL isUpper(char c)
// after that, scale each digit down appropriately {
// 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 ascii2double(char *val)
{ {
double retval = 0; double retval = 0;
@ -100,6 +169,9 @@ double ascii2double(char *val)
return retval * sgn; return retval * sgn;
} }
/*
* String manipulation
*/
// Your basic linux-stle (argv, argc) parser based on delimiters // Your basic linux-stle (argv, argc) parser based on delimiters
// string is destroyed // string is destroyed
int explode_string(char *str, char *strp[], int limit, char delim, char quote) int explode_string(char *str, char *strp[], int limit, char delim, char quote)