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