mirror of
https://github.com/adrcs/ip400.git
synced 2025-04-12 07:13:44 +03:00
Added V1.0 Node firmware
This commit is contained in:
parent
510f6c6bd7
commit
10ddfa65ca
61
Node Firmware/IP400/Inc/dataq.h
Normal file
61
Node Firmware/IP400/Inc/dataq.h
Normal file
|
@ -0,0 +1,61 @@
|
|||
/*---------------------------------------------------------------------------
|
||||
Project: WL33_NUCLEO_UART
|
||||
|
||||
Module: <module description here>
|
||||
|
||||
File Name: queue.h
|
||||
|
||||
Date Created: Jan 9, 2025
|
||||
|
||||
Author: MartinA
|
||||
|
||||
Description: <what it does>
|
||||
|
||||
Copyright © 2024-25, Alberta Digital Radio Communications Society,
|
||||
All rights reserved
|
||||
|
||||
|
||||
Revision History:
|
||||
|
||||
---------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef INC_DATAQ_H_
|
||||
#define INC_DATAQ_H_
|
||||
|
||||
#ifndef __CCALL
|
||||
#ifdef __cplusplus
|
||||
#define __CCALL extern "C"
|
||||
#else
|
||||
#define __CCALL
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define Q_TYPECAST (struct qelem *)
|
||||
|
||||
// queue structure
|
||||
typedef struct qelem {
|
||||
struct qelem *q_forw;
|
||||
struct qelem *q_back;
|
||||
} QUEUE_ELEM;
|
||||
|
||||
// frame data queue
|
||||
typedef struct frame_data_queue_t {
|
||||
struct frame_data_queue_t *q_forw; // forwared pointer
|
||||
struct frame_data_queue_t *q_back; // backward pointer
|
||||
void *frame; // IP400 Frame
|
||||
uint16_t length; // length (in some cases)
|
||||
} FRAME_QUEUE;
|
||||
|
||||
// queue functions
|
||||
BOOL enqueFrame(FRAME_QUEUE *que, IP400_FRAME *fr);
|
||||
IP400_FRAME *dequeFrame(FRAME_QUEUE *que);
|
||||
|
||||
// queue management
|
||||
void insque (struct qelem *elem, struct qelem *pred);
|
||||
void remque (struct qelem *elem);
|
||||
|
||||
// print memory stats
|
||||
void Print_Memory_Stats(void);
|
||||
|
||||
|
||||
#endif /* INC_DATAQ_H_ */
|
208
Node Firmware/IP400/Inc/frame.h
Normal file
208
Node Firmware/IP400/Inc/frame.h
Normal file
|
@ -0,0 +1,208 @@
|
|||
/*---------------------------------------------------------------------------
|
||||
Project: WL33_NUCLEO_UART
|
||||
|
||||
Module: <module description here>
|
||||
|
||||
File Name: frame.h
|
||||
|
||||
Date Created: Jan 8, 2025
|
||||
|
||||
Author: MartinA
|
||||
|
||||
Description: <what it does>
|
||||
|
||||
Copyright © 2024-25, Alberta Digital Radio Communications Society,
|
||||
All rights reserved
|
||||
|
||||
|
||||
Revision History:
|
||||
|
||||
---------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef FRAME_H_
|
||||
#define FRAME_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include "types.h"
|
||||
|
||||
// frame defines
|
||||
#define N_CALL 4 // octets in the excess-40 compressed callsign
|
||||
#define MAX_CALL 6 // max callsign size
|
||||
#define EXT_CALL 4*MAX_CALL // extended call sign field
|
||||
#define PAYLOAD_MIN 56 // min octets in payload
|
||||
#define PAYLOAD_MAX 1053 // max octets in payload
|
||||
#define MAX_CALL_BUFFER 20 // max buffering for callsign in payload
|
||||
#define N_FEC 4 // number of bytes in the FEC
|
||||
#define BROADCAST_ADDR 0xFF // broadcast address
|
||||
|
||||
#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
|
||||
#define PLL_CALAMP_ERR 0x0800 // VCO amplitude calibration error
|
||||
#define PLL_CALFREQ_ERR 0x0400 // VCO frequency calibration error
|
||||
#define PLL_UNLOCK_ERR 0x0200 // PLL is unlocked
|
||||
#define PLL_LOCK_FAIL 0x0100 // PLL lock failure
|
||||
#define DBM_FIFO_ERR 0x0020 // Data buffer failure
|
||||
#define N_RADIO_ERRS 7 // number of the above
|
||||
|
||||
// radio FSM states
|
||||
enum fsm_states_e {
|
||||
FSM_IDLE=0, // idle
|
||||
FSM_ENA_RF_REG, // enable RF registers
|
||||
FSM_WAIT_ACTIVE2, // wait for active 2
|
||||
FSM_ACTIVE2, // active 2
|
||||
FSM_ENA_CURR, // enable current
|
||||
FSM_SYNTH_SETUP, // synth setup
|
||||
FSM_CALIB_VCO, // VCO calibration
|
||||
FSM_LOCKRXTX, // lock Rx and Rx
|
||||
FSM_LOCKONTX, // lock on Rx
|
||||
FSM_EN_PA, // enable PA
|
||||
FSM_TX, // transmit
|
||||
FSM_PA_DWN_ANA, // Analog power down
|
||||
FSM_END_TX, // end transmit
|
||||
FSM_LOCKONRX, // lock on Rx
|
||||
FSM_EN_RX, // Enable Rx
|
||||
FSM_EN_LNA, // enable LNA
|
||||
FSM_RX, // recieve
|
||||
FSM_END_RX, // end rx
|
||||
FSM_SYNTH_PWDN, // synth power down
|
||||
FSM_N_FSM_STATES
|
||||
};
|
||||
|
||||
// header flags
|
||||
typedef struct frame_flags_t {
|
||||
unsigned hop_count: 4; // hop count
|
||||
unsigned coding: 4; // coding method
|
||||
unsigned compression: 2; // compression method
|
||||
unsigned hoptable: 1; // hop table is included
|
||||
unsigned srcExt: 1; // source call is extended
|
||||
unsigned destExt: 1; // dest call is extended
|
||||
unsigned command: 1; // command packet
|
||||
unsigned noconnect: 1; // connectionless
|
||||
unsigned repeat: 1; // repeat flag
|
||||
} IP400_FLAGS;
|
||||
|
||||
// callsign field
|
||||
typedef struct callsign_t {
|
||||
union {
|
||||
uint8_t bytes[N_CALL]; // compressed callsign
|
||||
uint32_t encoded;
|
||||
} callbytes;
|
||||
uint16_t port; // port information
|
||||
} IP400_CALL;
|
||||
|
||||
// hop table
|
||||
typedef struct hoptable_t {
|
||||
union {
|
||||
uint8_t bytes[N_CALL];
|
||||
uint32_t encoded;
|
||||
} hopAddr;
|
||||
} HOPTABLE;
|
||||
|
||||
// complete frame
|
||||
typedef struct ip400_frame_t {
|
||||
IP400_CALL source; // source call sign
|
||||
IP400_CALL dest; // destination call sign
|
||||
union {
|
||||
IP400_FLAGS flags; // flag bit field
|
||||
uint16_t allflags; // all flags
|
||||
} flagfld;
|
||||
uint32_t seqNum; // packet sequence number
|
||||
void *buf; // data to send
|
||||
uint16_t length; // data length
|
||||
void *hopTable; // hop table address
|
||||
} IP400_FRAME;
|
||||
|
||||
// min/max payload sizes
|
||||
#define IP_400_PORT_SIZE sizeof(uint16_t)
|
||||
#define IP_400_CALL_SIZE (N_CALL + IP_400_PORT_SIZE)
|
||||
#define IP_400_FLAG_SIZE sizeof(uint16_t)
|
||||
#define IP_400_HDR_SIZE (2*IP_400_CALL_SIZE + IP_400_FLAG_SIZE)
|
||||
#define IP_400_LEN_SIZE sizeof(uint16_t)
|
||||
#define MIN_FRAME_SIZE sizeof(IP400_FRAME) + PAYLOAD_MIN + N_FEC
|
||||
#define MAX_FRAME_SIZE MIN_FRAME_SIZE - PAYLOAD_MIN + PAYLOAD_MAX
|
||||
|
||||
// packet coding (type)
|
||||
typedef enum {
|
||||
UTF8_TEXT_PACKET=0, // Text packet (chat application)
|
||||
COMPRESSED_AUDIO, // compressed audio packet
|
||||
COMPREESSD_VIDEO, // compressed video packet
|
||||
DATA_PACKET, // data packet
|
||||
BEACON_PACKET, // beacon packet
|
||||
IP_ENCAPSULATED, // IP encapsulated packet
|
||||
AX_25_PACKET, // AX.25 encapsulated packet
|
||||
RFC4733_DTMF, // DTMF packet
|
||||
DMR_FRAME, // DMR Frame
|
||||
DSTAR_FRAME, // Dstar Frame
|
||||
P25_FRAME, // TIA project 25
|
||||
NXDN_FRAME, // NXDN
|
||||
M17_FRAME, // M17
|
||||
TBD_1,
|
||||
TBD_2,
|
||||
LOCAL_COMMAND // local command frame
|
||||
} IP400FrameType;
|
||||
|
||||
// audio compression types
|
||||
enum {
|
||||
AUDIO_RAW, // raw 16-bit PCM
|
||||
AUDIO_ULAW, // mu law compression
|
||||
AUDIO_CODEC2, // codec 2 encoded
|
||||
AUDIO_AMBE // AMBE encoded
|
||||
};
|
||||
|
||||
// H.246 Video compression
|
||||
enum {
|
||||
H264_240_180_24, // H.264: 240x180, 24FPS
|
||||
H264_320_240_24, // H.264: 320x240, 24FPS
|
||||
H264_480_360_12, // H.264: 480x360, 12FPS
|
||||
H264_640_480_6 // H.264: 640x480, 6FPS
|
||||
};
|
||||
|
||||
// callsign fields
|
||||
enum {
|
||||
SRC_CALLSIGN=0, // dest for encode is source callsign
|
||||
DEST_CALLSIGN // dest for encode is dest callsign
|
||||
};
|
||||
|
||||
// frame stats
|
||||
typedef struct frame_stats_t {
|
||||
uint32_t TxFrameCnt; // transmit frame count
|
||||
uint32_t RxFrameCnt; // good receive frame count
|
||||
uint32_t CRCErrors; // CRC Errors
|
||||
uint32_t TimeOuts; // Timeouts
|
||||
uint32_t lastRSSI; // last RSSI reading
|
||||
uint32_t framesOK; // processed frames
|
||||
uint32_t dropped; // rejected frames
|
||||
uint32_t duplicates; // duplicates
|
||||
uint32_t nBeacons; // number of beacons processed
|
||||
uint32_t nRepeated; // repeated frames
|
||||
} FRAME_STATS;
|
||||
|
||||
uint8_t getFrameStatus(void);
|
||||
|
||||
// references
|
||||
uint8_t callEncode(char *callsign, uint16_t port, IP400_FRAME *frame, uint8_t dest, uint8_t offset);
|
||||
BOOL callDecode(IP400_CALL *encCall, char *callsign, uint16_t *port);
|
||||
|
||||
// frame senders
|
||||
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);
|
||||
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
|
||||
//
|
||||
uint8_t getFrameStatus(void); // get the frame status
|
||||
|
||||
#endif /* FRAME_H_ */
|
75
Node Firmware/IP400/Inc/ip.h
Normal file
75
Node Firmware/IP400/Inc/ip.h
Normal file
|
@ -0,0 +1,75 @@
|
|||
/*---------------------------------------------------------------------------
|
||||
Project: WL33_NUCLEO_UART
|
||||
|
||||
File Name: ip.h
|
||||
|
||||
Author: MartinA
|
||||
|
||||
Creation Date: Jan 26, 2025
|
||||
|
||||
Description: Definitions for IP addressing
|
||||
|
||||
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) 2024-25 Alberta Digital Radio Communications Society
|
||||
|
||||
Revision History:
|
||||
|
||||
---------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef INC_IP_H_
|
||||
#define INC_IP_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "types.h"
|
||||
#include "config.h"
|
||||
|
||||
/* If your port already typedef's sa_family_t, define SA_FAMILY_T_DEFINED
|
||||
to prevent this code from redefining it. */
|
||||
#if !defined(sa_family_t) && !defined(SA_FAMILY_T_DEFINED)
|
||||
typedef uint8_t sa_family_t;
|
||||
#endif
|
||||
/* If your port already typedef's in_port_t, define IN_PORT_T_DEFINED
|
||||
to prevent this code from redefining it. */
|
||||
#if !defined(in_port_t) && !defined(IN_PORT_T_DEFINED)
|
||||
typedef uint16_t in_port_t;
|
||||
#endif
|
||||
|
||||
// in_addr struct
|
||||
struct in_addr_t {
|
||||
union {
|
||||
struct { uint8_t s_b1,s_b2,s_b3,s_b4; } S_un_b;
|
||||
struct { uint16_t s_w1,s_w2; } S_un_w;
|
||||
uint32_t S_addr;
|
||||
} S_un;
|
||||
};
|
||||
typedef struct in_addr_t IN_ADDR;
|
||||
|
||||
/* members are in network byte order */
|
||||
struct sockaddr_in {
|
||||
uint8_t sin_len;
|
||||
sa_family_t sin_family;
|
||||
in_port_t sin_port;
|
||||
IN_ADDR sin_addr;
|
||||
#define SIN_ZERO_LEN 8
|
||||
char sin_zero[SIN_ZERO_LEN];
|
||||
};
|
||||
|
||||
typedef struct sockaddr_in SOCKADDR_IN;
|
||||
|
||||
// convert packed frame to IP address
|
||||
void GetIP10Addr(IP400_CALL *fr, SOCKADDR_IN *ipaddr);
|
||||
void GetIP172Addr(IP400_CALL *fr, SOCKADDR_IN *ipaddr);
|
||||
|
||||
#if __IP_GROUP == IP_10_GROUP
|
||||
#define GetIPAddr GetIP10Add
|
||||
#else
|
||||
#define GetIPAddr GetIP172Addr
|
||||
#endif
|
||||
|
||||
#endif /* INC_IP_H_ */
|
43
Node Firmware/IP400/Inc/led.h
Normal file
43
Node Firmware/IP400/Inc/led.h
Normal file
|
@ -0,0 +1,43 @@
|
|||
/*---------------------------------------------------------------------------
|
||||
Project: WL33_NUCLEO_UART
|
||||
|
||||
File Name: led.h
|
||||
|
||||
Author: MartinA
|
||||
|
||||
Creation Date: Jan 26, 2025
|
||||
|
||||
Description: Definitions for the LED module
|
||||
|
||||
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) 2024-25 Alberta Digital Radio Communications Society
|
||||
|
||||
Revision History:
|
||||
|
||||
---------------------------------------------------------------------------*/
|
||||
#ifndef INC_LED_H_
|
||||
#define INC_LED_H_
|
||||
|
||||
// LED functions
|
||||
enum {
|
||||
|
||||
BICOLOR_OFF=0, // turn LED off
|
||||
BICOLOR_RED, // red mode steady
|
||||
BICOLOR_RED_FLASH, // red mode flashing
|
||||
BICOLOR_GREEN, // green mode steady
|
||||
BICOLOR_GREEN_FLASH, // green mode flashing
|
||||
BICOLOR_RED_GREEN, // alternate RED/GREEN
|
||||
TX_LED_ON, // tx LED on
|
||||
TX_LED_OFF, // tx LED OFF
|
||||
N_LED_MODE // modes1
|
||||
};
|
||||
|
||||
void SetLEDMode(uint8_t mode);
|
||||
|
||||
|
||||
#endif /* INC_LED_H_ */
|
62
Node Firmware/IP400/Inc/newlib-freertos.h
Normal file
62
Node Firmware/IP400/Inc/newlib-freertos.h
Normal file
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* FreeRTOS Kernel V10.6.2
|
||||
* Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* https://www.FreeRTOS.org
|
||||
* https://github.com/FreeRTOS
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef INC_NEWLIB_FREERTOS_H
|
||||
#define INC_NEWLIB_FREERTOS_H
|
||||
|
||||
/* Note Newlib support has been included by popular demand, but is not
|
||||
* used by the FreeRTOS maintainers themselves. FreeRTOS is not
|
||||
* responsible for resulting newlib operation. User must be familiar with
|
||||
* newlib and must provide system-wide implementations of the necessary
|
||||
* stubs. Be warned that (at the time of writing) the current newlib design
|
||||
* implements a system-wide malloc() that must be provided with locks.
|
||||
*
|
||||
* See the third party link http://www.nadler.com/embedded/newlibAndFreeRTOS.html
|
||||
* for additional information. */
|
||||
|
||||
#include <reent.h>
|
||||
|
||||
#define configUSE_C_RUNTIME_TLS_SUPPORT 1
|
||||
|
||||
#ifndef configTLS_BLOCK_TYPE
|
||||
#define configTLS_BLOCK_TYPE struct _reent
|
||||
#endif
|
||||
|
||||
#ifndef configINIT_TLS_BLOCK
|
||||
#define configINIT_TLS_BLOCK( xTLSBlock, pxTopOfStack ) _REENT_INIT_PTR( &( xTLSBlock ) )
|
||||
#endif
|
||||
|
||||
#ifndef configSET_TLS_BLOCK
|
||||
#define configSET_TLS_BLOCK( xTLSBlock ) ( _impure_ptr = &( xTLSBlock ) )
|
||||
#endif
|
||||
|
||||
#ifndef configDEINIT_TLS_BLOCK
|
||||
#define configDEINIT_TLS_BLOCK( xTLSBlock ) _reclaim_reent( &( xTLSBlock ) )
|
||||
#endif
|
||||
|
||||
#endif /* INC_NEWLIB_FREERTOS_H */
|
135
Node Firmware/IP400/Inc/setup.h
Normal file
135
Node Firmware/IP400/Inc/setup.h
Normal file
|
@ -0,0 +1,135 @@
|
|||
/*---------------------------------------------------------------------------
|
||||
Project: WL33_NUCLEO_UART
|
||||
|
||||
File Name: setup.h
|
||||
|
||||
Author: MartinA
|
||||
|
||||
Creation Date: Jan 13, 2025
|
||||
|
||||
Description: Definitions for setup data
|
||||
|
||||
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) 2024-25 Alberta Digital Radio Communications Society
|
||||
|
||||
Revision History:
|
||||
|
||||
---------------------------------------------------------------------------*/
|
||||
#ifndef INC_SETUP_H_
|
||||
#define INC_SETUP_H_
|
||||
|
||||
#include <stm32wl3x_hal_mrsubg.h>
|
||||
|
||||
#include "frame.h"
|
||||
#include "usart.h"
|
||||
|
||||
#define __USE_SETUP_PARAMS 1 // set to 1 to use setup parameters
|
||||
#define US 0 // station is in the US
|
||||
|
||||
// defined elsewhere
|
||||
extern char *modTypes[];
|
||||
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 repeat: 1; // repeat mode default
|
||||
unsigned ext: 1; // use extended callsign
|
||||
unsigned rate: 3; // data rate in use
|
||||
} SETUP_FLAGS;
|
||||
|
||||
// data rates in the flag field (FSK mode)
|
||||
enum {
|
||||
FSK_1200,
|
||||
FSK_9600,
|
||||
FSK_56K,
|
||||
FSK_100K,
|
||||
FSK_200K,
|
||||
FSK_400K,
|
||||
FSK_600K
|
||||
};
|
||||
|
||||
// setup data struct
|
||||
typedef struct setup_data_t {
|
||||
SETUP_FLAGS flags; // flags
|
||||
char stnCall[MAX_CALL]; // station call sign
|
||||
char extCall[EXT_CALL]; // extended call sign
|
||||
char latitude[10]; // latititude
|
||||
char longitude[10]; // longitude
|
||||
char gridSq[10]; // grid square
|
||||
uint16_t beaconInt; // beacon interval
|
||||
} SETUP_DATA;
|
||||
|
||||
// Radio setup struct
|
||||
typedef struct radio_setup_t {
|
||||
uint32_t lFrequencyBase; /*!< Specifies the base carrier frequency (in Hz) */
|
||||
MRSubGModSelect xModulationSelect; /*!< Specifies the modulation @ref MRSubGModSelect */
|
||||
uint32_t lDatarate; /*!< Specifies the datarate expressed in sps.*/
|
||||
uint32_t lFreqDev; /*!< Specifies the frequency deviation expressed in Hz. */
|
||||
uint32_t lBandwidth; /*!< Specifies the channel filter bandwidth expressed in Hz. */
|
||||
uint8_t dsssExp; /*!< Specifies the DSSS spreading exponent. Use 0 to disable DSSS. */
|
||||
uint8_t outputPower; /*!< PA value to write expressed in dBm. */
|
||||
MRSubG_PA_DRVMode PADrvMode; /*!< PA drive mode. */
|
||||
int16_t rxSquelch; // rx squelch level
|
||||
} RADIO_SETUP;
|
||||
|
||||
// setup struct
|
||||
typedef struct stn_params_t {
|
||||
SETUP_DATA setup_data; // basic setup data
|
||||
RADIO_SETUP radio_setup; // radio setup
|
||||
uint8_t FirmwareVerMajor; // firmware major rev
|
||||
uint8_t FirmwareVerMinor; // firmware minor vers
|
||||
uint32_t Magic; // magic number: "DEBEADEF"
|
||||
uint32_t SetupCRC; // CRC
|
||||
} STN_PARAMS;
|
||||
|
||||
// beacon header
|
||||
typedef union {
|
||||
struct beacon_hdr_t {
|
||||
SETUP_FLAGS flags;
|
||||
uint8_t txPower;
|
||||
uint8_t FirmwareMajor;
|
||||
uint8_t FirmwareMinor;
|
||||
} setup;
|
||||
uint8_t hdrBytes[sizeof(struct beacon_hdr_t)];
|
||||
} BEACON_HEADER;
|
||||
|
||||
#define SETUP_MAGIC 0xDEBEADEF // magic number
|
||||
|
||||
typedef union {
|
||||
STN_PARAMS params;
|
||||
uint8_t bytes[sizeof(STN_PARAMS)];
|
||||
uint32_t flashwords[sizeof(STN_PARAMS)/sizeof(uint32_t)];
|
||||
} SETUP_MEMORY;
|
||||
|
||||
extern SETUP_MEMORY setup_memory;
|
||||
extern SETUP_MEMORY def_params;
|
||||
extern CRC_HandleTypeDef hcrc;
|
||||
|
||||
// validations used by key entry
|
||||
#if US
|
||||
#define MIN_FREQ 420000000 // min freq (US only)
|
||||
#else
|
||||
#define MIN_FREQ 430000000 // min freq (CAN only)
|
||||
#endif
|
||||
|
||||
// links in
|
||||
void printStationSetup(void); // print setup struct
|
||||
void printRadioSetup(void); // print radio setup
|
||||
char *GetMyCall(void); // return the station's callsign
|
||||
STN_PARAMS *GetStationParams(void); // get the station params
|
||||
BOOL CompareToMyCall(char *call);
|
||||
//
|
||||
BOOL VerifySetup(void);
|
||||
BOOL ReadSetup(void);
|
||||
BOOL WriteSetup(void);
|
||||
void SetDefSetup(void);
|
||||
BOOL UpdateSetup(void);
|
||||
|
||||
#endif /* INC_SETUP_H_ */
|
71
Node Firmware/IP400/Inc/spi.h
Normal file
71
Node Firmware/IP400/Inc/spi.h
Normal file
|
@ -0,0 +1,71 @@
|
|||
/*---------------------------------------------------------------------------
|
||||
Project: WL33_NUCLEO_UART
|
||||
|
||||
File Name: spi.h
|
||||
|
||||
Author: MartinA
|
||||
|
||||
Creation Date: Jan 26, 2025
|
||||
|
||||
Description: Definitions for SPI mod
|
||||
|
||||
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) 2024-25 Alberta Digital Radio Communications Society
|
||||
|
||||
Revision History:
|
||||
|
||||
---------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef INC_SPI_H_
|
||||
#define INC_SPI_H_
|
||||
|
||||
#include "frame.h"
|
||||
|
||||
// SPI frame header
|
||||
typedef struct spi_hdr_t {
|
||||
uint8_t eye[4]; // 'IP4X'
|
||||
uint8_t status; // status byte
|
||||
uint8_t offset_hi; // high offset
|
||||
uint8_t offset_lo; // low offset
|
||||
uint8_t length_hi; // length hi
|
||||
uint8_t length_lo; // length lo
|
||||
uint8_t fromCall[N_CALL]; // from callsign
|
||||
uint8_t fromPort[IP_400_PORT_SIZE]; // from port
|
||||
uint8_t toCall[N_CALL]; // to callsign
|
||||
uint8_t toPort[IP_400_PORT_SIZE]; // to port
|
||||
uint8_t coding; // packet coding
|
||||
uint8_t hopCount; // hop count
|
||||
uint8_t flags; // remaining flags
|
||||
} SPI_HEADER;
|
||||
|
||||
#define SPI_BUFFER_LEN 400 // 400 bytes/transfer
|
||||
#define SPI_RAW_LEN SPI_BUFFER_LEN + sizeof(struct spi_hdr_t)
|
||||
|
||||
// status values
|
||||
typedef enum {
|
||||
NO_DATA=0, // no data available
|
||||
SINGLE_FRAME, // single frame
|
||||
FRAGMENT, // fragment
|
||||
LAST_FRAGMENT, // last fragment
|
||||
NUM_STATS // number of stats
|
||||
} spiFrameStatus;
|
||||
|
||||
// data buffer struct
|
||||
typedef union {
|
||||
struct {
|
||||
SPI_HEADER hdr; // header
|
||||
uint8_t buffer[SPI_BUFFER_LEN];
|
||||
} spiData;
|
||||
uint8_t rawData[SPI_RAW_LEN];
|
||||
} SPI_BUFFER;
|
||||
|
||||
#define SPI_TIMEOUT 100 // SPI timeout
|
||||
|
||||
BOOL EnqueSPIFrame(void *raw);
|
||||
|
||||
#endif /* INC_SPI_H_ */
|
18
Node Firmware/IP400/Inc/streambuffer.h
Normal file
18
Node Firmware/IP400/Inc/streambuffer.h
Normal file
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
* streambuffer.h
|
||||
*
|
||||
* Created on: Jan 11, 2025
|
||||
* Author: MartinA
|
||||
*/
|
||||
|
||||
#ifndef INC_STREAMBUFFER_H_
|
||||
#define INC_STREAMBUFFER_H_
|
||||
|
||||
// data buffer defs
|
||||
typedef uint8_t DATA_ELEMENT; // data element
|
||||
#define BUFFER_EMPTY(x) (x.nDataBytes == 0)
|
||||
#define BUFFER_NO_DATA 0
|
||||
#define bufferSIZE 512 // large buffer
|
||||
|
||||
|
||||
#endif /* INC_STREAMBUFFER_H_ */
|
40
Node Firmware/IP400/Inc/tod.h
Normal file
40
Node Firmware/IP400/Inc/tod.h
Normal file
|
@ -0,0 +1,40 @@
|
|||
/*---------------------------------------------------------------------------
|
||||
Project: WL33_NUCLEO_UART
|
||||
|
||||
File Name: tod.h
|
||||
|
||||
Author: MartinA
|
||||
|
||||
Creation Date: Jan 23, 2025
|
||||
|
||||
Description: <decription here?
|
||||
|
||||
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) 2024-25 Alberta Digital Radio Communications Society
|
||||
|
||||
Revision History:
|
||||
|
||||
---------------------------------------------------------------------------*/
|
||||
#ifndef INC_TOD_H_
|
||||
#define INC_TOD_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
// TOD struct
|
||||
typedef struct tod_t {
|
||||
uint8_t Seconds;
|
||||
uint8_t Minutes;
|
||||
uint8_t Hours;
|
||||
} TIMEOFDAY;
|
||||
|
||||
// links in
|
||||
void TOD_10SecTimer(void); // 10 second timer
|
||||
void getTOD(TIMEOFDAY *time);
|
||||
BOOL setTOD(char *todString);
|
||||
|
||||
#endif /* INC_TOD_H_ */
|
54
Node Firmware/IP400/Inc/types.h
Normal file
54
Node Firmware/IP400/Inc/types.h
Normal file
|
@ -0,0 +1,54 @@
|
|||
/*---------------------------------------------------------------------------
|
||||
Project: WL33_NUCLEO_UART
|
||||
|
||||
Module: Type definitions
|
||||
|
||||
File Name: types.h
|
||||
|
||||
Date Created: Jan 9, 2025
|
||||
|
||||
Author: MartinA
|
||||
|
||||
Description: Useful data types
|
||||
|
||||
Copyright © 2024-25, Alberta Digital Radio Communications Society,
|
||||
All rights reserved
|
||||
|
||||
|
||||
Revision History:
|
||||
|
||||
---------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef INC_TYPES_H_
|
||||
#define INC_TYPES_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
// boolean type
|
||||
#ifndef BOOL
|
||||
typedef uint8_t BOOL; // boolean
|
||||
typedef uint8_t BOOLEAN; // alternate
|
||||
#endif
|
||||
|
||||
#ifndef TRUE
|
||||
#define TRUE ((BOOL)1U)
|
||||
#endif
|
||||
|
||||
#ifndef FALSE
|
||||
#define FALSE ((BOOL)0U)
|
||||
#endif
|
||||
|
||||
#ifndef __CCALL
|
||||
#ifdef __cplusplus
|
||||
#define __CCALL extern "C"
|
||||
#else
|
||||
#define __CCALL
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// macros
|
||||
#define islower(c) ((c >= 'a') && (c <= 'z'))
|
||||
#define toupper(c) (c-0x20)
|
||||
|
||||
#endif /* INC_TYPES_H_ */
|
62
Node Firmware/IP400/Inc/usart.h
Normal file
62
Node Firmware/IP400/Inc/usart.h
Normal file
|
@ -0,0 +1,62 @@
|
|||
/*---------------------------------------------------------------------------
|
||||
Project: WL33_NUCLEO_UART
|
||||
|
||||
File Name: usart.h
|
||||
|
||||
Author: MartinA
|
||||
|
||||
Creation Date: Jan 12, 2025
|
||||
|
||||
Description: <decription here?
|
||||
|
||||
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) 2024-25 Alberta Digital Radio Communications Society
|
||||
|
||||
Revision History:
|
||||
|
||||
---------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef INC_USART_H_
|
||||
#define INC_USART_H_
|
||||
|
||||
#include "types.h"
|
||||
#include "main.h"
|
||||
|
||||
// definitions
|
||||
typedef uint32_t UART_TIMEOUT_T; // uart timer type
|
||||
typedef uint16_t BUFFER_SIZE_T; // buffer size type
|
||||
typedef uint8_t DATA_ELEMENT; // buffer data element
|
||||
|
||||
// uart HAL handle
|
||||
extern UART_HandleTypeDef huart1; // console UART
|
||||
extern UART_HandleTypeDef hlpuart1 ; // GPS UART
|
||||
|
||||
// logger defines
|
||||
#define LOG_NOTICE 0 // notice
|
||||
#define LOG_ERROR 1 // error
|
||||
#define LOG_SEVERE 2 // severe error
|
||||
|
||||
// logger
|
||||
void logger(int severity, char *format, ...);
|
||||
|
||||
// API calls
|
||||
void USART_RxBuffer_reset(void);
|
||||
size_t databuffer_bytesInBuffer(void);
|
||||
DATA_ELEMENT databuffer_get(UART_TIMEOUT_T timeout);
|
||||
BOOL databuffer_contains(const char *tag, UART_TIMEOUT_T rx_timeout, BOOL saveData, char *SaveBuffer);
|
||||
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
|
||||
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);
|
||||
|
||||
#endif /* INC_USART_H_ */
|
42
Node Firmware/IP400/Inc/utils.h
Normal file
42
Node Firmware/IP400/Inc/utils.h
Normal file
|
@ -0,0 +1,42 @@
|
|||
/*---------------------------------------------------------------------------
|
||||
Project: WL33_NUCLEO_UART
|
||||
|
||||
File Name: utils.h
|
||||
|
||||
Author: MartinA
|
||||
|
||||
Creation Date: Jan 20, 2025
|
||||
|
||||
Description: Utility routines
|
||||
|
||||
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) 2024-25 Alberta Digital Radio Communications Society
|
||||
|
||||
Revision History:
|
||||
|
||||
---------------------------------------------------------------------------*/
|
||||
#ifndef INC_UTILS_H_
|
||||
#define INC_UTILS_H_
|
||||
|
||||
// replacement for missing itoa
|
||||
int ascii2Dec(char *dec);
|
||||
|
||||
// and its double counterpart...
|
||||
double ascii2double(char *val);
|
||||
|
||||
// hex to ascii
|
||||
void hex2ascii(uint8_t hex, char *buf);
|
||||
|
||||
// check an entry for floating point
|
||||
BOOL isfloat(char *val);
|
||||
|
||||
// useful in parsing NMEA sentences
|
||||
int explode_string(char *str, char *strp[], int limit, char delim, char quote);
|
||||
|
||||
|
||||
#endif /* INC_UTILS_H_ */
|
402
Node Firmware/IP400/Src/beacon.c
Normal file
402
Node Firmware/IP400/Src/beacon.c
Normal file
|
@ -0,0 +1,402 @@
|
|||
/*---------------------------------------------------------------------------
|
||||
Project: WL33_NUCLEO_UART
|
||||
|
||||
File Name: beacon.c
|
||||
|
||||
Author: MartinA
|
||||
|
||||
Description: Beacon task. Sends a beacon frame periodically, based on timer in the setup
|
||||
structure. The beacon include position data in a readable format, which
|
||||
can come from a GPS receiver or the setup struct. Lat/Long data
|
||||
is sent in DDMM.MMMMM format.
|
||||
|
||||
Define __ENABLE_GPS to enable the code.
|
||||
|
||||
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 <math.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <malloc.h>
|
||||
#include <cmsis_os2.h>
|
||||
#include <FreeRTOS.h>
|
||||
#include <stm32wl3x_hal.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "frame.h"
|
||||
#include "tasks.h"
|
||||
#include "setup.h"
|
||||
#include "utils.h"
|
||||
#include "tod.h"
|
||||
#include "usart.h"
|
||||
|
||||
// config
|
||||
#define __SPEED_DAEMON 0 // send beacon every 5 seconds for testing purposes
|
||||
|
||||
// local defines
|
||||
#define MAX_BEACON 80 // max beacon string
|
||||
#define GPS_FIX_LEN 20 // gps fix length
|
||||
#define GPS_BFR_SIZE 140 // GPS buffer size
|
||||
|
||||
#if __ENABLE_GPS
|
||||
// NMEA GGA Sentence fields
|
||||
char *nmeaMsgTag = "RMC"; // sentence we are processing
|
||||
enum {
|
||||
NMEA_TAG=0, // message tag
|
||||
NMEA_TIMESTAMP, // time of fix'
|
||||
NMEA_STATUS, // status
|
||||
NMEA_LATITUDE, // latitude
|
||||
NMEA_NS_HEMI, // latitude hemisphere
|
||||
NMEA_LONGITUDE, // longitude
|
||||
NMEA_EW_HEMI, // longitude hemisphere
|
||||
NMEA_MIN_FLDS // minimum fields in GPS message
|
||||
};
|
||||
|
||||
// NMEA processing states
|
||||
enum {
|
||||
NMEA_STATE_SOM=0, // start of message
|
||||
NMEA_STATE_MSG, // in the message
|
||||
};
|
||||
|
||||
#define N_NMEA_SEN 14 // number of NMEA sentences
|
||||
enum {
|
||||
NMEA_CMD_DISABLED=0, // disabled
|
||||
NMEA_CMD_ONCE, // once per fix
|
||||
NMEA_CMD_2FIX, // once every 2 fixes
|
||||
NMEA_CMD_3FIX, // once every 3 fixes
|
||||
NMEA_CMD_4FIX, // once every 4 fixes
|
||||
NMEA_CMD_5FIX, // once every 5 fixes
|
||||
};
|
||||
|
||||
char *nmeaCmd = "$PMTK314"; // command
|
||||
uint8_t senCmds[N_NMEA_SEN] = {
|
||||
NMEA_CMD_DISABLED, // 0:GLL disabled
|
||||
NMEA_CMD_ONCE, // 1: RMC once
|
||||
NMEA_CMD_DISABLED, // 2: VTG disabled
|
||||
NMEA_CMD_DISABLED, // 3: GGA disabled
|
||||
NMEA_CMD_DISABLED, // 4: GSA disabled
|
||||
NMEA_CMD_DISABLED, // 5: GSV disabled
|
||||
NMEA_CMD_DISABLED, // 6 not used
|
||||
NMEA_CMD_DISABLED, // 7
|
||||
NMEA_CMD_DISABLED, // 13
|
||||
NMEA_CMD_DISABLED, // 14
|
||||
NMEA_CMD_DISABLED, // 15
|
||||
NMEA_CMD_DISABLED, // 16
|
||||
NMEA_CMD_DISABLED, // 17
|
||||
NMEA_CMD_DISABLED // 18
|
||||
};
|
||||
#endif
|
||||
|
||||
// hemispheres
|
||||
enum {
|
||||
N_HEMI=0, // North
|
||||
S_HEMI, // South
|
||||
E_HEMI, // East
|
||||
W_HEMI, // West
|
||||
N_HEMIS
|
||||
};
|
||||
|
||||
uint8_t hemispheres[N_HEMIS] = {
|
||||
'N', 'S', 'E', 'W'
|
||||
};
|
||||
|
||||
uint32_t timerInitValue; // value to initialize timer
|
||||
uint32_t timerCtrValue; // current counter
|
||||
BEACON_HEADER beacon_hdr; // beacon header
|
||||
uint8_t bcnPayload[MAX_BEACON]; // beacon payload
|
||||
BOOL gpsMessageRx = FALSE;
|
||||
BOOL GPSBusy=FALSE;
|
||||
TIMEOFDAY wallClockTime;
|
||||
|
||||
#if __ENABLE_GPS
|
||||
// data for GPS
|
||||
uint8_t GPSMsgBuf[GPS_BFR_SIZE];
|
||||
uint8_t GPSEchoBuf[GPS_BFR_SIZE];
|
||||
uint8_t GPSProcBuf[GPS_BFR_SIZE];
|
||||
uint8_t *GPSBufPtr;
|
||||
uint8_t GPSMsgSize;
|
||||
uint8_t NMEAState;
|
||||
char cmdBuf[MAX_BEACON];
|
||||
//
|
||||
BOOL haveGPSFix = FALSE;
|
||||
BOOL gpsEchoReady=FALSE;
|
||||
//
|
||||
char GPSLat[GPS_FIX_LEN];
|
||||
char GPSLong[GPS_FIX_LEN];
|
||||
char GPSFixTime[GPS_FIX_LEN];
|
||||
char *gpsFlds[GPS_BFR_SIZE];
|
||||
|
||||
// fwd Refs in this module in GPS mode
|
||||
BOOL processGPSMessage(uint8_t *GPSMsgBuf, uint8_t bufferSize);
|
||||
void sendGPSCmd(void);
|
||||
#endif
|
||||
|
||||
//fwd refs w/o GPS
|
||||
void GPSFormat(char *buffer, double value, uint8_t hePos, uint8_t heNeg);
|
||||
|
||||
// initialization: calculate the init value in
|
||||
// quanta of MAIN_TASK_SCHED
|
||||
void Beacon_Task_init(void)
|
||||
{
|
||||
// higher speed for testing...
|
||||
#if __SPEED_DAEMON
|
||||
timerInitValue = 5 *1000/MAIN_TASK_SCHED; // every 5 seconds for testing
|
||||
timerCtrValue = 0;
|
||||
#else
|
||||
uint32_t timerTick = 60 * 1000/MAIN_TASK_SCHED;
|
||||
timerInitValue = setup_memory.params.setup_data.beaconInt * timerTick;
|
||||
timerCtrValue = 0; // set to timerInitValue to wait
|
||||
#endif
|
||||
|
||||
// GPS Init
|
||||
#if __ENABLE_GPS
|
||||
gpsMessageRx = FALSE;
|
||||
haveGPSFix = FALSE;
|
||||
gpsEchoReady = FALSE;
|
||||
GPSBufPtr = GPSMsgBuf;
|
||||
GPSBusy = FALSE;
|
||||
NMEAState=NMEA_STATE_SOM;
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
// this runs every MAIN_TASK_SCHED ms
|
||||
void Beacon_Task_exec(void)
|
||||
{
|
||||
#if __ENABLE_GPS
|
||||
if(gpsMessageRx) {
|
||||
// process the message
|
||||
if(processGPSMessage(GPSProcBuf, (uint8_t)strlen((char *)GPSProcBuf))) {
|
||||
haveGPSFix = TRUE;
|
||||
}
|
||||
gpsMessageRx = FALSE;
|
||||
}
|
||||
#endif
|
||||
|
||||
if(timerCtrValue > 0) {
|
||||
timerCtrValue--;
|
||||
return;
|
||||
}
|
||||
timerCtrValue = timerInitValue;
|
||||
|
||||
#if __ENABLE_GPS
|
||||
// send a command to the GPS every beacon interval
|
||||
sendGPSCmd();
|
||||
#endif
|
||||
|
||||
// start with the header
|
||||
beacon_hdr.setup.flags = setup_memory.params.setup_data.flags;
|
||||
beacon_hdr.setup.txPower = setup_memory.params.radio_setup.outputPower;
|
||||
uint8_t *buf = bcnPayload;
|
||||
|
||||
// beacon header: flags, txpower and firmware version
|
||||
// brute force copy: compiler rounds SETUP_FLAGS to 32 bits
|
||||
*buf++ = beacon_hdr.hdrBytes[0];
|
||||
*buf++ = beacon_hdr.hdrBytes[4];
|
||||
|
||||
// firmware version
|
||||
*buf++ = def_params.params.FirmwareVerMajor + '0';
|
||||
*buf++ = def_params.params.FirmwareVerMinor + '0';
|
||||
|
||||
char *pPayload = (char *)buf;
|
||||
char *p2 = pPayload;
|
||||
|
||||
#if __ENABLE_GPS
|
||||
//GPS generated payload
|
||||
if(haveGPSFix) {
|
||||
strcpy(pPayload, "GPS,");
|
||||
strcat(pPayload, GPSLat);
|
||||
strcat(pPayload, ",");
|
||||
strcat(pPayload, GPSLong);
|
||||
strcat(pPayload, ",");
|
||||
strcat(pPayload, GPSFixTime);
|
||||
strcat(pPayload, ",");
|
||||
} else {
|
||||
#endif
|
||||
// setup struct generated payload
|
||||
strcpy(pPayload, "FXD,");
|
||||
pPayload += strlen(pPayload);
|
||||
double dlat = ascii2double(setup_memory.params.setup_data.latitude);
|
||||
GPSFormat(pPayload, dlat, N_HEMI, S_HEMI);
|
||||
strcat(pPayload, ",");
|
||||
pPayload += strlen(pPayload);
|
||||
double dlong = ascii2double(setup_memory.params.setup_data.longitude);
|
||||
GPSFormat(pPayload, dlong, E_HEMI, W_HEMI);
|
||||
strcat(pPayload, ",,");
|
||||
#if __ENABLE_GPS
|
||||
}
|
||||
#endif
|
||||
pPayload += strlen(pPayload);
|
||||
|
||||
// Use RTC for time
|
||||
getTOD(&wallClockTime);
|
||||
sprintf(pPayload, "%02d%02d%02d", wallClockTime.Hours, wallClockTime.Minutes, wallClockTime.Seconds);
|
||||
strcat(pPayload, ",");
|
||||
|
||||
// home grid square
|
||||
strcat(pPayload, setup_memory.params.setup_data.gridSq);
|
||||
|
||||
int pos = strlen((char *)p2);
|
||||
buf[pos++] = '\0'; // null terminated
|
||||
|
||||
pos += 2*sizeof(uint8_t); // account for firmware field
|
||||
|
||||
// time to send a beacon frame..
|
||||
SendBeaconFrame(setup_memory.params.setup_data.stnCall, bcnPayload, pos+1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Format the +/-ddd.dddd lat/long format into DDMM.MMMMM format
|
||||
*/
|
||||
void GPSFormat(char *buffer, double value, uint8_t hePos, uint8_t heNeg)
|
||||
{
|
||||
// Set hemi, get abs value
|
||||
uint8_t hemi = value > 0.00 ? hePos : heNeg;
|
||||
value = fabs(value);
|
||||
|
||||
// separate whole and fractional
|
||||
int whole = (int)value;
|
||||
double fract = value - (double)whole;
|
||||
|
||||
// calculate minutes and fraction
|
||||
double dmin = 60.0 * fract;
|
||||
dmin = round(dmin * 100.0)/100.0;
|
||||
|
||||
int min = floor(dmin);
|
||||
int ifract = (int)ceil((dmin-min) * 100000);
|
||||
|
||||
sprintf(buffer, "%d%02d.%05d%c", whole, min, ifract, hemispheres[hemi]);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Process GPS data from LPUART: runs at a higher priority
|
||||
*/
|
||||
void GPS_Task_exec(void)
|
||||
{
|
||||
#if __ENABLE_GPS
|
||||
char c;
|
||||
int nBytesinBuff;
|
||||
|
||||
if((nBytesinBuff=gpsbuffer_bytesInBuffer()) == 0)
|
||||
return;
|
||||
|
||||
for(int i=0;i<nBytesinBuff;i++) {
|
||||
c = (char)gpsbuffer_get(0);
|
||||
|
||||
switch(NMEAState) {
|
||||
|
||||
// waiting for a start of message
|
||||
case NMEA_STATE_SOM:
|
||||
if(c != '$')
|
||||
break;
|
||||
GPSBufPtr = GPSMsgBuf;
|
||||
NMEAState=NMEA_STATE_MSG;
|
||||
break;
|
||||
|
||||
// collecting chars in a message
|
||||
case NMEA_STATE_MSG:
|
||||
*GPSBufPtr++ = c;
|
||||
GPSMsgSize = GPSBufPtr - GPSMsgBuf;
|
||||
if((c == '*') || (GPSMsgSize >= GPS_BFR_SIZE)) {
|
||||
*GPSBufPtr = '\0';
|
||||
GPSMsgSize++;
|
||||
memcpy(GPSEchoBuf, GPSMsgBuf, GPSMsgSize);
|
||||
gpsMessageRx = TRUE;
|
||||
memcpy(GPSProcBuf, GPSMsgBuf, GPSMsgSize);
|
||||
memcpy(GPSEchoBuf, GPSMsgBuf, GPSMsgSize);
|
||||
gpsEchoReady = TRUE;
|
||||
NMEAState=NMEA_STATE_SOM;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
#else
|
||||
return; // unused code
|
||||
#endif
|
||||
}
|
||||
|
||||
#if __ENABLE_GPS
|
||||
/*
|
||||
* Send a command to the GPS device to limit traffic
|
||||
*/
|
||||
void sendGPSCmd(void)
|
||||
{
|
||||
uint8_t cksum = 0;
|
||||
char *buf = cmdBuf;
|
||||
strcpy(buf, nmeaCmd);
|
||||
buf += strlen(nmeaCmd);
|
||||
|
||||
for(int j=1;j<strlen(nmeaCmd);j++)
|
||||
cksum ^= nmeaCmd[j];
|
||||
|
||||
for(int i=0;i<N_NMEA_SEN;i++) {
|
||||
*buf = ',';
|
||||
cksum ^= *buf++;
|
||||
*buf = senCmds[i] + '0';
|
||||
cksum ^= *buf++;
|
||||
}
|
||||
|
||||
*buf++ = '*';
|
||||
hex2ascii(cksum, buf);
|
||||
buf += 2;
|
||||
|
||||
*buf++ = '\r';
|
||||
*buf++ = '\n';
|
||||
*buf = '\0';
|
||||
uint16_t buflen = buf - cmdBuf;
|
||||
|
||||
LPUART_Send_String(cmdBuf, buflen);
|
||||
}
|
||||
/*
|
||||
* Process an inbound GPS message
|
||||
*/
|
||||
BOOL processGPSMessage(uint8_t *GPSdata, uint8_t bufferSize)
|
||||
{
|
||||
char *msg = (char *)GPSdata;
|
||||
uint8_t nParams = explode_string(msg, gpsFlds, bufferSize, ',', '"');
|
||||
if ((nParams == 0) || (nParams < NMEA_MIN_FLDS))
|
||||
return FALSE;
|
||||
|
||||
// brute force position fix
|
||||
// check for message with GGA at the end
|
||||
char *gpsTag = gpsFlds[NMEA_TAG];
|
||||
gpsTag += strlen(gpsTag)-strlen(nmeaMsgTag);
|
||||
if(strcmp(gpsTag, nmeaMsgTag))
|
||||
return FALSE;
|
||||
|
||||
strcpy(GPSFixTime, gpsFlds[NMEA_TIMESTAMP]);
|
||||
|
||||
strcpy(GPSLat, gpsFlds[NMEA_LATITUDE]);
|
||||
strcat(GPSLat, gpsFlds[NMEA_NS_HEMI]);
|
||||
|
||||
strcpy(GPSLong, gpsFlds[NMEA_LONGITUDE]);
|
||||
strcat(GPSLong, gpsFlds[NMEA_EW_HEMI]);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/*
|
||||
* echo the last GPS message on the console
|
||||
*/
|
||||
void GPSEcho(void)
|
||||
{
|
||||
if(!gpsEchoReady)
|
||||
return;
|
||||
|
||||
USART_Print_string("%s\r\n", (char *)GPSEchoBuf);
|
||||
gpsEchoReady=FALSE;
|
||||
}
|
||||
#endif
|
194
Node Firmware/IP400/Src/callsign.c
Normal file
194
Node Firmware/IP400/Src/callsign.c
Normal file
|
@ -0,0 +1,194 @@
|
|||
/*---------------------------------------------------------------------------
|
||||
Project: IP400
|
||||
|
||||
Module: Compress and expand a callsign
|
||||
|
||||
File Name: callsign.c
|
||||
|
||||
Author: MartinA
|
||||
|
||||
Creation Date: Jan 9, 2025
|
||||
|
||||
Description: Comresses an ASCII string of callsign characters. A callsign can
|
||||
have up to 6 characters to fit into the four byte field, if longer
|
||||
the rest are placed in the payload of the data frame. Callsigns can
|
||||
be extended with a '-' character, in this case the call is removed
|
||||
and padded before conversion, and the remainder is also in the
|
||||
frame payload.
|
||||
|
||||
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 <string.h>
|
||||
|
||||
#include "types.h"
|
||||
#include "frame.h"
|
||||
|
||||
#define RADIX_40 40 // alphabet radix
|
||||
|
||||
// Radix 40 callsign alphabet
|
||||
char alphabet[RADIX_40] = {
|
||||
// 0 1 2 3 4 5 6 7 8 9
|
||||
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
|
||||
|
||||
// 10 11 12 13 14 15 16 17 18 19
|
||||
' ', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I',
|
||||
|
||||
// 20 21 22 23 24 25 26 27 28 29
|
||||
'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S',
|
||||
|
||||
// 30 31 32 33 34 35 36 37 38 39
|
||||
'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '(', ')', '-'
|
||||
};
|
||||
|
||||
// encode a char into the alphabet
|
||||
uint32_t alphaEncode(char byte)
|
||||
{
|
||||
byte = islower(byte) ? toupper(byte) : byte;
|
||||
|
||||
for(uint32_t i=0;i<RADIX_40;i++) {
|
||||
if(alphabet[i] == byte)
|
||||
return i;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// decode a byte back into ASCII
|
||||
char alphaDecode(uint32_t alpha)
|
||||
{
|
||||
// numeric
|
||||
if((alpha >= 0) && (alpha <= 9))
|
||||
return '0' + alpha;
|
||||
|
||||
// special cases: see alphabet table
|
||||
|
||||
switch(alpha) {
|
||||
|
||||
case 10:
|
||||
return ' ';
|
||||
|
||||
case 37:
|
||||
return '(';
|
||||
|
||||
case 38:
|
||||
return ')';
|
||||
|
||||
case 39:
|
||||
return '@';
|
||||
|
||||
default:
|
||||
return 'A' + (alpha - 11);
|
||||
}
|
||||
return ' ';
|
||||
}
|
||||
|
||||
void EncodeChunk(char *src, int len, uint32_t *enc)
|
||||
{
|
||||
uint32_t chunk=alphaEncode(src[0]);
|
||||
|
||||
// less than 2 characters
|
||||
if(len < 2) {
|
||||
*enc = chunk;
|
||||
return;
|
||||
}
|
||||
|
||||
// 2 or more
|
||||
for(int i=1;i<len;i++) {
|
||||
uint32_t current = alphaEncode(src[i]);
|
||||
chunk = current + chunk*RADIX_40;
|
||||
}
|
||||
*enc = chunk;
|
||||
}
|
||||
|
||||
// encode a callsign: if less than max_call, then just encode into the frame,
|
||||
// else continue in the data portion of the frame
|
||||
uint8_t callEncode(char *callsign, uint16_t port, IP400_FRAME *frame, uint8_t dest, uint8_t offset)
|
||||
{
|
||||
int len = strlen(callsign);
|
||||
|
||||
uint32_t encChunk;
|
||||
uint32_t *p = (uint32_t *)frame->buf;
|
||||
p += offset;
|
||||
|
||||
if(dest == DEST_CALLSIGN)
|
||||
frame->dest.port = port;
|
||||
else
|
||||
frame->source.port = port;
|
||||
|
||||
// broadcast address
|
||||
if(!strcmp(callsign, "FFFF")) {
|
||||
if(dest == DEST_CALLSIGN)
|
||||
frame->dest.callbytes.encoded = 0xFFFFFFFF;
|
||||
else
|
||||
frame->source.callbytes.encoded = 0xFFFFFFFF;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ensure the callsign is padded out to at least 6 characters
|
||||
char paddedCall[50];
|
||||
strcpy(paddedCall, callsign);
|
||||
strcat(paddedCall, " ");
|
||||
|
||||
// non-broadcast: break it up into chunks of 6 characters
|
||||
int nChunks = len/MAX_CALL;
|
||||
if ((len % MAX_CALL) > 0)
|
||||
nChunks++;
|
||||
|
||||
char *cll = paddedCall;
|
||||
|
||||
for(int k=0;k<nChunks;k++) {
|
||||
EncodeChunk(cll, MAX_CALL, &encChunk);
|
||||
if(k==0) {
|
||||
if(dest == DEST_CALLSIGN)
|
||||
frame->dest.callbytes.encoded = encChunk;
|
||||
else
|
||||
frame->source.callbytes.encoded = encChunk;
|
||||
// callsign less than or equal to MAX_CALL
|
||||
if(nChunks == 1) {
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
*p++ = encChunk;
|
||||
cll += MAX_CALL;
|
||||
if(dest == DEST_CALLSIGN)
|
||||
frame->flagfld.flags.destExt = TRUE;
|
||||
else
|
||||
frame->flagfld.flags.srcExt = TRUE;
|
||||
}
|
||||
}
|
||||
*p++ = 0xFF000000;
|
||||
// return the offset in the buffer
|
||||
return (p-(uint32_t *)frame->buf) + sizeof(uint32_t);
|
||||
}
|
||||
|
||||
// decode a callsign
|
||||
BOOL callDecode(IP400_CALL *encCall, char *callsign, uint16_t *port)
|
||||
{
|
||||
char tmpBuf[10], *p = tmpBuf;
|
||||
int i;
|
||||
|
||||
uint32_t encoded = encCall->callbytes.encoded;
|
||||
for(i=0;i<MAX_CALL;i++) {
|
||||
*p++ = alphaDecode(encoded % RADIX_40);
|
||||
encoded /= RADIX_40;
|
||||
}
|
||||
*p = '\0';
|
||||
for(i=strlen(tmpBuf)-1;i>=0;i--)
|
||||
*callsign++ = tmpBuf[i];
|
||||
|
||||
*callsign = '\0';
|
||||
*port = encCall->port;
|
||||
|
||||
return TRUE;
|
||||
}
|
301
Node Firmware/IP400/Src/chat.c
Normal file
301
Node Firmware/IP400/Src/chat.c
Normal file
|
@ -0,0 +1,301 @@
|
|||
/*---------------------------------------------------------------------------
|
||||
Project: WL33_NUCLEO_UART
|
||||
|
||||
File Name: chat.c
|
||||
|
||||
Author: MartinA
|
||||
|
||||
Description: This code contains the chat application. It gathers key until the CR
|
||||
is hit to send a packet. Two internal operations are exit and set the
|
||||
destination.
|
||||
|
||||
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 <stdint.h>
|
||||
#include <string.h>
|
||||
#include <malloc.h>
|
||||
|
||||
#include "setup.h"
|
||||
#include "frame.h"
|
||||
#include "types.h"
|
||||
#include "usart.h"
|
||||
#include "streambuffer.h"
|
||||
#include "dataq.h"
|
||||
|
||||
#define MAX_KEY 140 // max keys in a buffer
|
||||
#define MAX_DEST 20 // max chars in dest callsign
|
||||
#define BROADCAST "FFFF" // broadcast address
|
||||
|
||||
static char keyBuffer[MAX_KEY]; // buffer for keystrokes
|
||||
uint8_t keyPos; // current position
|
||||
|
||||
#define KEY_EOL 0x0D // carriage return
|
||||
#define KEY_RPT 0x12 // change repeat status
|
||||
#define KEY_EXIT 0x1A // exit key
|
||||
#define KEY_ESC 0x1B // escape key
|
||||
#define KEY_DEL 0x7F // delete key
|
||||
#define KEY_BKSP 0x08 // backspace key
|
||||
#define KEY_DUMP 0x04 // toggle dump mode
|
||||
|
||||
char dest_call[MAX_DEST]; // broadcast destination
|
||||
char *dp = dest_call; // pointer to dest call characters
|
||||
char *entCall = "Enter Destination Callsign";
|
||||
char *rptMode[] = {
|
||||
"Repeat mode->off",
|
||||
"Repeat mode->on"
|
||||
};
|
||||
|
||||
char *dumpStrings[] = {
|
||||
"Dump mode->off",
|
||||
"Dump mode->on"
|
||||
};
|
||||
|
||||
char *welcome = "Welcome to chat. ESC to set destination, CTRL/R to toggle repeat, CTRL/Z to exit";
|
||||
|
||||
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;
|
||||
|
||||
FRAME_QUEUE chatQueue; // queue for inbound frames
|
||||
|
||||
// fwd refs
|
||||
void sendLine(char *buffer, int len);
|
||||
void PrintFrame(IP400_FRAME *FrameBytes);
|
||||
|
||||
// init entry
|
||||
void Chat_Task_init(void)
|
||||
{
|
||||
strcpy(dest_call, BROADCAST);
|
||||
destEnt = FALSE;
|
||||
chatQueue.q_forw = &chatQueue;
|
||||
chatQueue.q_back = &chatQueue;
|
||||
keyPos = 0;
|
||||
}
|
||||
|
||||
void Chat_Task_welcome(void)
|
||||
{
|
||||
// start with welcome message
|
||||
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");
|
||||
else
|
||||
USART_Print_string("%s; Destination callsign->%s\r\n\n", dest_call);
|
||||
|
||||
welcomed = TRUE;
|
||||
}
|
||||
|
||||
/*
|
||||
* place a frame on the queue frame content is copied
|
||||
* to alloc'd memory
|
||||
*/
|
||||
BOOL EnqueChatFrame(void *raw)
|
||||
{
|
||||
IP400_FRAME *qFrame, *SrcFrame = (IP400_FRAME *)raw;
|
||||
uint8_t *frameBuffer;
|
||||
|
||||
// if the welcome mat is not out, then discard any pending frames
|
||||
if(!welcomed)
|
||||
return FALSE;
|
||||
|
||||
// allocate an IP400 frame
|
||||
if((qFrame=malloc(sizeof(IP400_FRAME)))== NULL)
|
||||
return FALSE;
|
||||
memcpy(qFrame, SrcFrame, sizeof(IP400_FRAME));
|
||||
|
||||
// alloc the data portion of the frame
|
||||
if((frameBuffer=malloc(SrcFrame->length)) == NULL) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
memcpy(frameBuffer, (uint8_t *)SrcFrame->buf, SrcFrame->length);
|
||||
qFrame->buf = frameBuffer;
|
||||
qFrame->length = SrcFrame->length;
|
||||
|
||||
if(!enqueFrame(&chatQueue, qFrame))
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/*
|
||||
* chat task main entry
|
||||
* return: TRUE if return to main menu is required
|
||||
* FALSE: more to go
|
||||
*/
|
||||
BOOL Chat_Task_exec(void)
|
||||
{
|
||||
if(!welcomed)
|
||||
Chat_Task_welcome();
|
||||
|
||||
char c;
|
||||
int nBytesinBuff;
|
||||
IP400_FRAME *fr;
|
||||
|
||||
// process any inbound frames first..
|
||||
if((fr=dequeFrame(&chatQueue)) != NULL) {
|
||||
PrintFrame(fr);
|
||||
free(fr->buf);
|
||||
free(fr);
|
||||
}
|
||||
|
||||
if((nBytesinBuff=databuffer_bytesInBuffer()) == 0)
|
||||
return FALSE;
|
||||
|
||||
for(int i=0;i<nBytesinBuff;i++) {
|
||||
|
||||
c=databuffer_get(0);
|
||||
|
||||
if(deleteMode) {
|
||||
if((c != KEY_DEL) && (c != KEY_BKSP)) {
|
||||
USART_Print_string("\\%c", c);
|
||||
if(keyPos < MAX_KEY)
|
||||
keyBuffer[keyPos++] = c;
|
||||
deleteMode = FALSE;
|
||||
} else {
|
||||
if(keyPos > 0) {
|
||||
USART_Print_string("%c", keyBuffer[--keyPos]);
|
||||
} else {
|
||||
USART_Print_string("\\\r\n");
|
||||
deleteMode = FALSE;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
} else {
|
||||
// processing a key
|
||||
|
||||
switch (c) {
|
||||
|
||||
// CTRL/R: change repeat flag
|
||||
case KEY_RPT:
|
||||
repeat = repeat ? FALSE : TRUE;
|
||||
char *r = rptMode[repeat];
|
||||
USART_Print_string("%s\r\n",r);
|
||||
break;
|
||||
|
||||
// CTRL/D: change dump mode
|
||||
case KEY_DUMP:
|
||||
dumpMode = dumpMode ? FALSE : TRUE;
|
||||
char *d = dumpStrings[dumpMode];
|
||||
USART_Print_string("%s\r\n",d);
|
||||
break;
|
||||
|
||||
// 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;
|
||||
destEnt = TRUE;
|
||||
}
|
||||
break;
|
||||
|
||||
// EOL key: sent the packet
|
||||
case KEY_EOL:
|
||||
USART_Print_string("\r\n");
|
||||
if(destEnt) {
|
||||
int cpyLen = keyPos > MAX_CALL ? MAX_CALL : keyPos;
|
||||
strncpy(dest_call, keyBuffer, cpyLen);
|
||||
destEnt = FALSE;
|
||||
} else {
|
||||
if(keyPos != 0) {
|
||||
keyBuffer[keyPos++] = '\0';
|
||||
sendLine(keyBuffer, keyPos);
|
||||
} else {
|
||||
USART_Print_string(">>>not sent\r\n");
|
||||
}
|
||||
}
|
||||
keyPos = 0;
|
||||
break;
|
||||
|
||||
case KEY_EXIT:
|
||||
welcomed = FALSE;
|
||||
return TRUE;
|
||||
|
||||
case KEY_DEL:
|
||||
case KEY_BKSP:
|
||||
if(keyPos > 0) {
|
||||
USART_Print_string("\\%c", keyBuffer[--keyPos]);
|
||||
deleteMode = TRUE;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
USART_Send_Char(c);
|
||||
if(keyPos < MAX_KEY)
|
||||
keyBuffer[keyPos++] = c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// send a line of text
|
||||
void sendLine(char *buffer, int len)
|
||||
{
|
||||
SendTextFrame(setup_memory.params.setup_data.stnCall, 0, dest_call, 0, buffer, len, repeat);
|
||||
}
|
||||
|
||||
/*
|
||||
* Print a received frame on the console
|
||||
*/
|
||||
void PrintFrame(IP400_FRAME *FrameBytes)
|
||||
{
|
||||
char printBuf[250];
|
||||
char decCall[100];
|
||||
uint16_t port;
|
||||
uint16_t dataLen = FrameBytes->length;
|
||||
|
||||
// dump mode for header debugging
|
||||
if(dumpMode) {
|
||||
/* print the received data */
|
||||
USART_Print_string("RX - Data received: [ ");
|
||||
|
||||
for(uint8_t i=0; i<sizeof(IP400_FRAME); i++)
|
||||
USART_Print_string("%02x ", FrameBytes[i]);
|
||||
|
||||
USART_Print_string("]\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// source call
|
||||
callDecode(&FrameBytes->source, decCall, &port);
|
||||
USART_Print_string("%s(%d)>", decCall, port);
|
||||
|
||||
// dest call
|
||||
if((FrameBytes->dest.callbytes.bytes[0] == BROADCAST_ADDR) &&
|
||||
(FrameBytes->dest.callbytes.bytes[1] == BROADCAST_ADDR)) {
|
||||
USART_Print_string("BROADCAST(%d)", FrameBytes->dest.port);
|
||||
} else {
|
||||
callDecode(&FrameBytes->dest, decCall, &port);
|
||||
USART_Print_string("%s(%d)", decCall, port);
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
}
|
66
Node Firmware/IP400/Src/dataq.c
Normal file
66
Node Firmware/IP400/Src/dataq.c
Normal file
|
@ -0,0 +1,66 @@
|
|||
/*---------------------------------------------------------------------------
|
||||
Project: WL33_NUCLEO_UART
|
||||
|
||||
File Name: dataq.c
|
||||
|
||||
Author: MartinA
|
||||
|
||||
Description: This module enques and deques an IP400 Frame type on a queue
|
||||
|
||||
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 <malloc.h>
|
||||
|
||||
#include "types.h"
|
||||
#include "frame.h"
|
||||
#include "dataq.h"
|
||||
|
||||
/*
|
||||
* Enque a frame. The frame data buffer must have
|
||||
* alredy been allocated
|
||||
*/
|
||||
BOOL enqueFrame(FRAME_QUEUE *que, IP400_FRAME *fr)
|
||||
{
|
||||
FRAME_QUEUE *f;
|
||||
if((f = malloc(sizeof(FRAME_QUEUE))) == NULL)
|
||||
return FALSE;
|
||||
|
||||
// set the frame buffer
|
||||
f->frame = fr;
|
||||
f->length = fr->length + sizeof(IP400_FRAME);
|
||||
|
||||
insque((QUEUE_ELEM *)f, (QUEUE_ELEM *)que->q_back);
|
||||
return TRUE;
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Deqeue a frame
|
||||
* Returns null if no frame is one the queue
|
||||
* Does NOT dealloc data or frame or q
|
||||
*/
|
||||
IP400_FRAME *dequeFrame(FRAME_QUEUE *que)
|
||||
{
|
||||
IP400_FRAME *ipFrame;
|
||||
|
||||
if(que->q_back == que)
|
||||
return NULL;
|
||||
|
||||
FRAME_QUEUE *f = que->q_forw;
|
||||
remque((struct qelem *)f);
|
||||
|
||||
ipFrame = f->frame;
|
||||
|
||||
free(f);
|
||||
return ipFrame;
|
||||
}
|
647
Node Firmware/IP400/Src/frame.c
Normal file
647
Node Firmware/IP400/Src/frame.c
Normal file
|
@ -0,0 +1,647 @@
|
|||
/*---------------------------------------------------------------------------
|
||||
Project: IP400
|
||||
|
||||
Module: Frame transmit and receive tasks
|
||||
|
||||
File Name: frame.c
|
||||
|
||||
Author: MartinA
|
||||
|
||||
Creation Date: Jan 8, 2025
|
||||
|
||||
Description: Handle the transmission and reception of frames
|
||||
|
||||
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 "setup.h"
|
||||
#include "tasks.h"
|
||||
#include "led.h"
|
||||
#include "spi.h"
|
||||
#include "usart.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
|
||||
uint32_t nextSeq; // next frame sequence number
|
||||
|
||||
// from setup...
|
||||
int16_t rxSquelch; // rx squlech
|
||||
IP400_CALL stnCall; // station callsign
|
||||
|
||||
|
||||
// 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_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
|
||||
*/
|
||||
BOOL SendTextFrame(char *srcCall, uint16_t srcPort, char *destCall, uint16_t dstPort, char *buf, uint16_t length, BOOL repeat)
|
||||
{
|
||||
IP400_FRAME *txFrame;
|
||||
|
||||
if((txFrame=malloc(sizeof(IP400_FRAME))) == NULL)
|
||||
return FALSE;
|
||||
|
||||
if((txFrame->buf=malloc(length + MAX_CALL_BUFFER)) == NULL)
|
||||
return FALSE;
|
||||
|
||||
txFrame->flagfld.allflags = 0; // start with all flags cleared
|
||||
|
||||
// format the header
|
||||
uint8_t offset = callEncode(srcCall, srcPort, txFrame, SRC_CALLSIGN, 0);
|
||||
offset = callEncode(destCall, dstPort, txFrame, DEST_CALLSIGN, offset);
|
||||
|
||||
// copy the payload in
|
||||
uint8_t *f = (uint8_t *)txFrame->buf;
|
||||
f += offset*sizeof(uint32_t);
|
||||
memcpy(f, (const uint8_t *)buf, length);
|
||||
|
||||
txFrame->length = length + offset;
|
||||
txFrame->flagfld.flags.hop_count = 0;
|
||||
txFrame->flagfld.flags.coding |= UTF8_TEXT_PACKET;
|
||||
txFrame->flagfld.flags.repeat = repeat;
|
||||
txFrame->flagfld.flags.hoptable = 0;
|
||||
txFrame->hopTable = NULL;
|
||||
txFrame->seqNum = nextSeq++;
|
||||
|
||||
QueueTxFrame(txFrame);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/*
|
||||
* compose and send a beacon frame (ping frame with broadcast destination
|
||||
*/
|
||||
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
|
||||
uint8_t offset = callEncode(srcCall, 0, bcnFrame, SRC_CALLSIGN, 0);
|
||||
callEncode("FFFF", 0, bcnFrame, DEST_CALLSIGN, 0);
|
||||
|
||||
// since this is the first frame out of the gate,
|
||||
// record the src callsign
|
||||
stnCall.callbytes.encoded = bcnFrame->source.callbytes.encoded;
|
||||
|
||||
// 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;
|
||||
|
||||
SPI_HEADER *spiHdr = (SPI_HEADER *)spi;
|
||||
|
||||
// validate the frame type
|
||||
switch(spiHdr->coding) {
|
||||
|
||||
// beacon packets are not sent
|
||||
case BEACON_PACKET:
|
||||
return;
|
||||
|
||||
// all others are
|
||||
}
|
||||
|
||||
if((spiFrame=malloc(sizeof(IP400_FRAME))) == NULL)
|
||||
return;
|
||||
|
||||
if((spiFrame->buf=malloc(len)) == NULL) {
|
||||
free(spiFrame);
|
||||
return;
|
||||
}
|
||||
|
||||
// hop table is in the first part of the payload buffer
|
||||
uint16_t nHops = spiHdr->hopCount;
|
||||
uint16_t htblSize = nHops * sizeof(HOPTABLE);
|
||||
HOPTABLE *hTable;
|
||||
if(nHops) {
|
||||
if((hTable=malloc(htblSize)) == NULL) {
|
||||
free(spiFrame->buf);
|
||||
free(spiFrame);
|
||||
return;
|
||||
}
|
||||
memcpy(hTable, payload, htblSize);
|
||||
payload += htblSize;
|
||||
len -= htblSize;
|
||||
spiFrame->hopTable = (void *)hTable;
|
||||
}
|
||||
|
||||
// debug option to use a supplied call instead of the station callsign
|
||||
// NB: use the station one in normal use, as a different call could cause havoc
|
||||
uint8_t *srcCall;
|
||||
#if DEBUG_RX_CALL
|
||||
srcCall = spiHdr->fromCall;
|
||||
#else
|
||||
srcCall = stnCall.callbytes.bytes;
|
||||
#endif
|
||||
|
||||
memcpy(spiFrame->source.callbytes.bytes, srcCall, N_CALL);
|
||||
spiFrame->source.port = (uint16_t)(spiHdr->fromPort[0]<<8) + (uint16_t)(spiHdr->fromPort[1]);
|
||||
|
||||
memcpy(spiFrame->dest.callbytes.bytes, spiHdr->toCall, N_CALL);
|
||||
spiFrame->dest.port = (uint16_t)(spiHdr->toPort[0]<<8) + (uint16_t)(spiHdr->toPort[1]);
|
||||
|
||||
spiFrame->flagfld.allflags = (uint16_t)(spiHdr->hopCount) + (uint16_t)(spiHdr->coding<<4) + (uint16_t)(spiHdr->flags<<8);
|
||||
if(spiFrame->flagfld.flags.hop_count)
|
||||
spiFrame->flagfld.flags.hoptable = TRUE;
|
||||
spiFrame->seqNum = nextSeq++;
|
||||
|
||||
memcpy(spiFrame->buf, payload, len);
|
||||
|
||||
QueueTxFrame(spiFrame);
|
||||
|
||||
}
|
||||
|
||||
// check to see if a frame came from me
|
||||
// returns true if I originated the frame
|
||||
BOOL FrameisMine(IP400_FRAME *frame)
|
||||
{
|
||||
char decCall[30];
|
||||
uint16_t port;;
|
||||
|
||||
// check if I am the originator call sign
|
||||
callDecode(&frame->source, decCall, &port);
|
||||
if(CompareToMyCall(decCall))
|
||||
return TRUE;
|
||||
|
||||
// now check to see if I repeated this frame
|
||||
if(frame->flagfld.flags.hoptable == 0)
|
||||
return FALSE;
|
||||
|
||||
// check my call is in the table
|
||||
HOPTABLE *htable = (HOPTABLE *)frame->hopTable;
|
||||
for(int i=0;i<frame->flagfld.flags.hop_count; i++)
|
||||
if(htable[i].hopAddr.encoded == stnCall.callbytes.encoded)
|
||||
return TRUE;
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Repeat a frame. Do not send it if we were the originator
|
||||
* Inbound frame is static, not malloc'd
|
||||
*/
|
||||
void RepeatFrame(IP400_FRAME *frame)
|
||||
{
|
||||
IP400_FRAME *rptFrame;
|
||||
|
||||
// copy the frame
|
||||
if((rptFrame=malloc(sizeof(IP400_FRAME))) == NULL)
|
||||
return;
|
||||
memcpy(rptFrame, frame, sizeof(IP400_FRAME));
|
||||
|
||||
// copy the data
|
||||
rptFrame->length = frame->length;
|
||||
if((rptFrame->buf=malloc(frame->length)) == NULL) {
|
||||
free(rptFrame);
|
||||
return;
|
||||
}
|
||||
memcpy(rptFrame->buf, frame->buf, frame->length);
|
||||
|
||||
// allocate a new hop table
|
||||
uint8_t hopCount= frame->flagfld.flags.hop_count;
|
||||
if((rptFrame->hopTable=malloc(sizeof(HOPTABLE)*(hopCount+1))) == NULL) {
|
||||
free(rptFrame->buf);
|
||||
free(rptFrame);
|
||||
}
|
||||
|
||||
// copy the existing one and add me to the end of it
|
||||
if(frame->flagfld.flags.hoptable) {
|
||||
memcpy(rptFrame->hopTable, frame->hopTable, sizeof(HOPTABLE)*hopCount);
|
||||
}
|
||||
HOPTABLE *table = (HOPTABLE *)rptFrame->hopTable;
|
||||
table[hopCount].hopAddr.encoded = stnCall.callbytes.encoded;
|
||||
rptFrame->flagfld.flags.hoptable = TRUE;
|
||||
rptFrame->flagfld.flags.hop_count = hopCount + 1;
|
||||
|
||||
Stats.nRepeated++;
|
||||
QueueTxFrame(rptFrame);
|
||||
}
|
||||
|
||||
/*
|
||||
* queue a frame for transmission by the tx task
|
||||
*/
|
||||
void QueueTxFrame(IP400_FRAME *txframe)
|
||||
{
|
||||
enqueFrame(&txQueue, txframe);
|
||||
}
|
||||
|
||||
/*
|
||||
* Enable the receiver
|
||||
*/
|
||||
void EnableRx(void)
|
||||
{
|
||||
__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;
|
||||
|
||||
// find a reason to reject a frame...
|
||||
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)) {
|
||||
|
||||
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 __DUMP_BEACON
|
||||
EnqueChatFrame((void *)&rFrame);
|
||||
#endif
|
||||
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++;
|
||||
}
|
||||
break;
|
||||
|
||||
// frames passed on to the host
|
||||
case COMPRESSED_AUDIO: // compressed audio packet
|
||||
case COMPREESSD_VIDEO: // compressed video packet
|
||||
case DATA_PACKET: // data packet
|
||||
case IP_ENCAPSULATED: // IP encapsulated packet
|
||||
case AX_25_PACKET: // AX.25 encapsulated packet
|
||||
case RFC4733_DTMF: // DTMF packet
|
||||
case DMR_FRAME: // DMR Frame
|
||||
case DSTAR_FRAME: // Dstar Frame
|
||||
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++;
|
||||
}
|
||||
break;
|
||||
|
||||
//reserved for future use
|
||||
case LOCAL_COMMAND: // local command frame
|
||||
break;
|
||||
|
||||
default:
|
||||
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(isMine)
|
||||
Stats.dropped++;
|
||||
|
||||
// restart the receiver
|
||||
EnableRx();
|
||||
|
||||
}
|
||||
// frame interrupt callback
|
||||
void HAL_MRSubG_IRQ_Callback(void)
|
||||
{
|
||||
subgIRQStatus = READ_REG(MR_SUBG_GLOB_STATUS->RFSEQ_IRQ_STATUS);
|
||||
|
||||
// Process transmitter interrupts
|
||||
if(subgIRQStatus & MR_SUBG_GLOB_STATUS_RFSEQ_IRQ_STATUS_TX_DONE_F) {
|
||||
__HAL_MRSUBG_CLEAR_RFSEQ_IRQ_FLAG(MR_SUBG_GLOB_STATUS_RFSEQ_IRQ_STATUS_TX_DONE_F);
|
||||
Stats.TxFrameCnt++;
|
||||
txDone = TRUE;
|
||||
}
|
||||
|
||||
// process receiver interrupts
|
||||
if(subgIRQStatus & MR_SUBG_GLOB_STATUS_RFSEQ_IRQ_STATUS_RX_CRC_ERROR_F) {
|
||||
Stats.CRCErrors++;
|
||||
__HAL_MRSUBG_CLEAR_RFSEQ_IRQ_FLAG(MR_SUBG_GLOB_STATUS_RFSEQ_IRQ_STATUS_RX_CRC_ERROR_F);
|
||||
}
|
||||
|
||||
if (subgIRQStatus & MR_SUBG_GLOB_STATUS_RFSEQ_IRQ_STATUS_RX_TIMEOUT_F) {
|
||||
Stats.TimeOuts++;
|
||||
__HAL_MRSUBG_CLEAR_RFSEQ_IRQ_FLAG(MR_SUBG_GLOB_STATUS_RFSEQ_IRQ_STATUS_RX_TIMEOUT_F);
|
||||
}
|
||||
|
||||
if (subgIRQStatus & MR_SUBG_GLOB_STATUS_RFSEQ_IRQ_STATUS_RX_OK_F ) {
|
||||
Stats.RxFrameCnt++;
|
||||
__HAL_MRSUBG_CLEAR_RFSEQ_IRQ_FLAG(MR_SUBG_GLOB_STATUS_RFSEQ_IRQ_STATUS_RX_OK_F);
|
||||
Stats.lastRSSI = READ_REG_FIELD(MR_SUBG_GLOB_STATUS->RX_INDICATOR, MR_SUBG_GLOB_STATUS_RX_INDICATOR_RSSI_LEVEL_ON_SYNC);
|
||||
rxDone = TRUE;
|
||||
}
|
||||
|
||||
EnableRx();
|
||||
}
|
48
Node Firmware/IP400/Src/insque.c
Normal file
48
Node Firmware/IP400/Src/insque.c
Normal file
|
@ -0,0 +1,48 @@
|
|||
/* insque(3C) routines
|
||||
This file is in the public domain. */
|
||||
|
||||
/*
|
||||
|
||||
@deftypefn Supplemental void insque (struct qelem *@var{elem}, @
|
||||
struct qelem *@var{pred})
|
||||
@deftypefnx Supplemental void remque (struct qelem *@var{elem})
|
||||
|
||||
Routines to manipulate queues built from doubly linked lists. The
|
||||
@code{insque} routine inserts @var{elem} in the queue immediately
|
||||
after @var{pred}. The @code{remque} routine removes @var{elem} from
|
||||
its containing queue. These routines expect to be passed pointers to
|
||||
structures which have as their first members a forward pointer and a
|
||||
back pointer, like this prototype (although no prototype is provided):
|
||||
|
||||
@example
|
||||
struct qelem @{
|
||||
struct qelem *q_forw;
|
||||
struct qelem *q_back;
|
||||
char q_data[];
|
||||
@};
|
||||
@end example
|
||||
|
||||
@end deftypefn
|
||||
|
||||
*/
|
||||
|
||||
struct qelem {
|
||||
struct qelem *q_forw;
|
||||
struct qelem *q_back;
|
||||
};
|
||||
|
||||
|
||||
void insque (struct qelem *elem, struct qelem *pred)
|
||||
{
|
||||
elem -> q_forw = pred -> q_forw;
|
||||
pred -> q_forw -> q_back = elem;
|
||||
elem -> q_back = pred;
|
||||
pred -> q_forw = elem;
|
||||
}
|
||||
|
||||
|
||||
void remque (struct qelem *elem)
|
||||
{
|
||||
elem -> q_forw -> q_back = elem -> q_back;
|
||||
elem -> q_back -> q_forw = elem -> q_forw;
|
||||
}
|
88
Node Firmware/IP400/Src/ip.c
Normal file
88
Node Firmware/IP400/Src/ip.c
Normal file
|
@ -0,0 +1,88 @@
|
|||
/*---------------------------------------------------------------------------
|
||||
Project: IP400
|
||||
|
||||
Module: Calculate IP address for a node
|
||||
|
||||
File Name: ip.c
|
||||
|
||||
Author: MartinA
|
||||
|
||||
Creation Date: Mar 3, 2025
|
||||
|
||||
Description: Calculate the IP address from an IP400 Frame structure
|
||||
|
||||
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 "types.h"
|
||||
#include "frame.h"
|
||||
#include "ip.h"
|
||||
|
||||
#define AF_INET 2 // internet address family
|
||||
#define IP_10_NETWORK 10 // ip network address
|
||||
#define IP_172_NETWORK 172 // alt network address
|
||||
#define IP_172_START 16 // start of 172 range
|
||||
|
||||
uint8_t netmask10[] = {
|
||||
0xFF, 0xFF, 0xFF, 0xF8
|
||||
};
|
||||
|
||||
uint8_t netmask172[] = {
|
||||
0xFF, 0x30, 0xFF, 0xFF
|
||||
};
|
||||
|
||||
#define NETMASK 0xF8 // mask for last byte
|
||||
|
||||
// return the IP address
|
||||
void GetIP10Addr(IP400_CALL *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 = fr->port;
|
||||
}
|
||||
|
||||
void GetIP172Addr(IP400_CALL *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 = fr->port;
|
||||
}
|
289
Node Firmware/IP400/Src/led.c
Normal file
289
Node Firmware/IP400/Src/led.c
Normal file
|
@ -0,0 +1,289 @@
|
|||
/*---------------------------------------------------------------------------
|
||||
Project: WL33_NUCLEO_UART
|
||||
|
||||
File Name: led.c
|
||||
|
||||
Author: MartinA
|
||||
|
||||
Description: Handler for the LED's on nucleo or PI boards
|
||||
|
||||
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:
|
||||
|
||||
---------------------------------------------------------------------------*/
|
||||
/*
|
||||
* notes for different implementations.
|
||||
* On the Pi there are two LED's a dedicated red and a bicolor red-green.
|
||||
* TX: On when transmitting
|
||||
* BICOLOR: Green solid when Rx is enabled
|
||||
* 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
|
||||
* GREEN (LD2) Duplicates the GREEN bi-color function
|
||||
* RED (LD3) Duplicates the RED Bi-color function
|
||||
*/
|
||||
|
||||
#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>
|
||||
#else
|
||||
#include <main.h>
|
||||
#endif
|
||||
|
||||
// config
|
||||
#define REVERSE_LEADS 1 // reverse Bi-color LED leads
|
||||
|
||||
// local defines
|
||||
#define FLASH_OFF 0 // LED flashing: off state
|
||||
#define FLASH_ON 1 // LED flashing: on state
|
||||
|
||||
// timer for 1/2 second when flashing
|
||||
#define LED_TIMER 5 // timer
|
||||
#define TEST_TIMER 20 // test timer
|
||||
|
||||
// internals
|
||||
void SetLEDMode(uint8_t mode);
|
||||
void LED_SetOff(void);
|
||||
void LED_SetRed(void);
|
||||
void LED_SetGreen(void);
|
||||
void LED_SetError(void);
|
||||
void setTxLED(BOOL state);
|
||||
|
||||
// vars
|
||||
BOOL ToggleEnable; // led toggling enabled
|
||||
uint8_t ledColour; // direction (colour)
|
||||
uint8_t ledMode; // led mode
|
||||
uint8_t ledState; // state on/off
|
||||
uint8_t ledTimer; // timer for led test
|
||||
uint8_t testNum; // test number
|
||||
uint8_t testTimer; // test timer
|
||||
uint8_t saveMode; // saved mode
|
||||
|
||||
//
|
||||
// Notes on timer setup for STM32H732
|
||||
// Prescaler value of 0 allows 64MHz clock to drive timer
|
||||
// 1 reduces it to 32MHz
|
||||
// Period divider of 6400 yeilds 100uSec time base
|
||||
//
|
||||
#define N_LED 5
|
||||
struct led_tests_t {
|
||||
char *testName;
|
||||
uint8_t testMode;
|
||||
} LEDTests[N_LED] = {
|
||||
{"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 }
|
||||
};
|
||||
// Initialization
|
||||
void Led_Task_Init(void)
|
||||
{
|
||||
ToggleEnable = FALSE;
|
||||
ledColour = BICOLOR_RED;
|
||||
ledState = BICOLOR_OFF;
|
||||
ledTimer = LED_TIMER;
|
||||
LED_SetOff();
|
||||
setTxLED(FALSE);
|
||||
testTimer = 0;
|
||||
testNum = 0;
|
||||
}
|
||||
|
||||
// We handle the flashing here...
|
||||
// for the bicolor led, we can be solid red, solid green,
|
||||
// or red-green flashing
|
||||
void Led_Task_Exec(void)
|
||||
{
|
||||
|
||||
if(ToggleEnable) {
|
||||
|
||||
if(ledTimer != 0) {
|
||||
ledTimer--;
|
||||
return;
|
||||
}
|
||||
|
||||
ledTimer--;
|
||||
|
||||
// blink LED if enabled
|
||||
switch(ledMode) {
|
||||
|
||||
case BICOLOR_RED_FLASH:
|
||||
case BICOLOR_GREEN_FLASH:
|
||||
ledState = (ledState == FLASH_ON) ? FLASH_OFF : FLASH_ON;
|
||||
break;
|
||||
|
||||
case BICOLOR_RED_GREEN:
|
||||
ledState = (ledState == FLASH_ON) ? FLASH_OFF : FLASH_ON;
|
||||
ledColour = (ledColour == BICOLOR_RED) ? BICOLOR_GREEN : BICOLOR_RED;
|
||||
break;
|
||||
}
|
||||
|
||||
// update the LED state
|
||||
if(ledState == FLASH_OFF) {
|
||||
LED_SetOff();
|
||||
} else {
|
||||
if(ledColour == BICOLOR_RED)
|
||||
LED_SetRed();
|
||||
else
|
||||
LED_SetGreen();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// led test mode
|
||||
BOOL LedTest(void)
|
||||
{
|
||||
|
||||
if(testTimer == 0) {
|
||||
if(testNum == 0)
|
||||
saveMode = ledMode;
|
||||
if(testNum == N_LED) {
|
||||
testNum = 0;
|
||||
SetLEDMode(saveMode);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
USART_Print_string("%s\r\n", LEDTests[testNum].testName);
|
||||
SetLEDMode(LEDTests[testNum].testMode);
|
||||
testTimer = TEST_TIMER;
|
||||
testNum++;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
testTimer--;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// API routines: set the LED mode
|
||||
void SetLEDMode(uint8_t mode)
|
||||
{
|
||||
ledMode = mode;
|
||||
|
||||
switch(ledMode) {
|
||||
|
||||
case BICOLOR_OFF:
|
||||
LED_SetOff();
|
||||
ToggleEnable = FALSE;
|
||||
ledState = FLASH_OFF;
|
||||
break;
|
||||
|
||||
case BICOLOR_RED:
|
||||
LED_SetRed();
|
||||
ToggleEnable = FALSE;
|
||||
ledColour = BICOLOR_RED;
|
||||
ledState = FLASH_ON;
|
||||
break;
|
||||
|
||||
case BICOLOR_RED_FLASH:
|
||||
LED_SetRed();
|
||||
ToggleEnable = TRUE;
|
||||
ledColour = BICOLOR_RED;
|
||||
ledState = FLASH_ON;
|
||||
break;
|
||||
|
||||
case BICOLOR_GREEN:
|
||||
LED_SetGreen();
|
||||
ToggleEnable = FALSE;
|
||||
ledColour = BICOLOR_GREEN;
|
||||
ledState = FLASH_ON;
|
||||
break;
|
||||
|
||||
case BICOLOR_GREEN_FLASH:
|
||||
LED_SetGreen();
|
||||
ToggleEnable = TRUE;
|
||||
ledColour = BICOLOR_GREEN;
|
||||
ledState = FLASH_ON;
|
||||
break;
|
||||
|
||||
case BICOLOR_RED_GREEN:
|
||||
LED_SetRed();
|
||||
ToggleEnable = TRUE;
|
||||
ledColour = BICOLOR_RED;
|
||||
ledState = FLASH_ON;
|
||||
break;
|
||||
|
||||
case TX_LED_ON:
|
||||
setTxLED(TRUE);
|
||||
break;
|
||||
|
||||
case TX_LED_OFF:
|
||||
setTxLED(FALSE);
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// do the actual updates
|
||||
void LED_SetOff(void)
|
||||
{
|
||||
#if _BOARD_TYPE
|
||||
BSP_LED_Off(LED_RED);
|
||||
BSP_LED_Off(LED_GREEN);
|
||||
BSP_LED_Off(LED_BLUE);
|
||||
#else
|
||||
HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_RESET);
|
||||
HAL_GPIO_WritePin(LED_A_GPIO_Port, LED_A_Pin, GPIO_PIN_RESET);
|
||||
HAL_GPIO_WritePin(LED_K_GPIO_Port, LED_K_Pin, GPIO_PIN_RESET);
|
||||
#endif
|
||||
}
|
||||
|
||||
void setTxLED(BOOL state)
|
||||
{
|
||||
#if _BOARD_TYPE
|
||||
if(state)
|
||||
BSP_LED_On(LED_BLUE);
|
||||
else
|
||||
BSP_LED_Off(LED_BLUE);
|
||||
#else
|
||||
if(state)
|
||||
HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_SET);
|
||||
else
|
||||
HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_RESET);
|
||||
#endif
|
||||
}
|
||||
// set fwd direction for bidir LED
|
||||
#if REVERSE_LEADS
|
||||
void LED_SetGreen(void)
|
||||
#else
|
||||
void LED_SetRed(void)
|
||||
#endif
|
||||
{
|
||||
#if _BOARD_TYPE
|
||||
BSP_LED_On(LED_GREEN);
|
||||
#else
|
||||
HAL_GPIO_WritePin(LED_A_GPIO_Port, LED_A_Pin, GPIO_PIN_SET);
|
||||
HAL_GPIO_WritePin(LED_K_GPIO_Port, LED_K_Pin, GPIO_PIN_RESET);
|
||||
#endif
|
||||
}
|
||||
|
||||
// set bwd direction for bidir LED
|
||||
#if REVERSE_LEADS
|
||||
void LED_SetRed(void)
|
||||
#else
|
||||
void LED_SetGreen(void)
|
||||
#endif
|
||||
{
|
||||
#if _BOARD_TYPE
|
||||
BSP_LED_On(LED_RED);
|
||||
#else
|
||||
HAL_GPIO_WritePin(LED_A_GPIO_Port, LED_A_Pin, GPIO_PIN_RESET);
|
||||
HAL_GPIO_WritePin(LED_K_GPIO_Port, LED_K_Pin, GPIO_PIN_SET);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
63
Node Firmware/IP400/Src/logger.c
Normal file
63
Node Firmware/IP400/Src/logger.c
Normal file
|
@ -0,0 +1,63 @@
|
|||
/*---------------------------------------------------------------------------
|
||||
Project: MMDLTX
|
||||
|
||||
Module: Logger
|
||||
|
||||
File Name: audio.c
|
||||
|
||||
Author: MartinA
|
||||
|
||||
Revision: 1.00
|
||||
|
||||
Description: Log an error message. On the nucleo, we send it out to the
|
||||
console UAR/T, on the E04, it goes out to the LPUART which
|
||||
is connected to the VCOM port.
|
||||
|
||||
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 <stdio.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "usart.h"
|
||||
|
||||
char errmsg[200];
|
||||
|
||||
// need to figure out how to talk to UART on board...
|
||||
void logger(int severity, char *format, ...)
|
||||
{
|
||||
|
||||
// process the arg list
|
||||
va_list argptr;
|
||||
va_start(argptr, format);
|
||||
vsprintf(errmsg,format, argptr);
|
||||
va_end(argptr);
|
||||
|
||||
switch(severity) {
|
||||
|
||||
case LOG_NOTICE:
|
||||
USART_Print_string("Notice: %s\r\n", errmsg);
|
||||
break;
|
||||
|
||||
case LOG_ERROR:
|
||||
USART_Print_string("Notice: %s\r\n", errmsg);
|
||||
break;
|
||||
|
||||
case LOG_SEVERE:
|
||||
USART_Print_string("Notice: %s\r\n", errmsg);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
839
Node Firmware/IP400/Src/menu.c
Normal file
839
Node Firmware/IP400/Src/menu.c
Normal file
|
@ -0,0 +1,839 @@
|
|||
/*---------------------------------------------------------------------------
|
||||
Project: WL33_NUCLEO_UART
|
||||
|
||||
File Name: menu.c
|
||||
|
||||
Author: MartinA
|
||||
|
||||
Description: Menu handler
|
||||
|
||||
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 <string.h>
|
||||
#include <malloc.h>
|
||||
|
||||
#include "types.h"
|
||||
#include "usart.h"
|
||||
#include "streambuffer.h"
|
||||
#include "setup.h"
|
||||
#include "tasks.h"
|
||||
#include "utils.h"
|
||||
#include "tod.h"
|
||||
#include "config.h"
|
||||
|
||||
// menu state
|
||||
uint8_t menuState; // menu state
|
||||
enum {
|
||||
MENU_OFF, // off
|
||||
MENU_SHOWING, // showing the menu
|
||||
MENU_SELECTING, // getting a selection
|
||||
MENU_SELECTED, // item has been selected
|
||||
MENU_PAUSED // pausing a while...
|
||||
};
|
||||
|
||||
// key entry state
|
||||
uint8_t entryState; // state of keyboard entry
|
||||
enum {
|
||||
NO_ENTRY=0, // no entry yet..
|
||||
ENTERING, // entering a value
|
||||
VALIDATING, // validating the entry
|
||||
};
|
||||
|
||||
// menu items
|
||||
#define NO_ITEM -1 // no item selected
|
||||
#define __MEM_DEBUG 1 // memory debug
|
||||
|
||||
// DEC VT100 Escape sequences
|
||||
#define ASCII_TAB 0x09
|
||||
#define ASCII_RET 0x0D
|
||||
#define ASCII_ESC 0x1B // escape char
|
||||
#define DEC_LEADIN '[' // lead-in character
|
||||
#define MAX_SEQ 6 // max sequence length
|
||||
|
||||
// control codes..
|
||||
enum {
|
||||
CONTROL_CLEAR=0, // clear screen
|
||||
CONTROL_HOME, // cursor home
|
||||
CONTROL_DOUBLE_TOP, // double wide and height (top)
|
||||
CONTROL_DOUBLE_BOTTOM, // double wide and height (bottom)
|
||||
CONTROL_SINGLE, // single wide and height
|
||||
NCONTROL_CODES // number of conrol codes
|
||||
};
|
||||
|
||||
struct controlCodes_t {
|
||||
char sequence[MAX_SEQ]; // sequence
|
||||
uint8_t len; // length
|
||||
} controlCodes[NCONTROL_CODES] = {
|
||||
{{ASCII_ESC, DEC_LEADIN, '2', 'J'}, 4},
|
||||
{{ASCII_ESC, DEC_LEADIN, 'H'}, 3},
|
||||
{{ASCII_ESC, '#', '3'}, 3},
|
||||
{{ASCII_ESC, '#', '4'}, 3},
|
||||
{{ASCII_ESC, '#', '5'}, 3},
|
||||
};
|
||||
|
||||
// strings common to all menu types
|
||||
char *whatItem = "??Try again->";
|
||||
char *menuSpacer = "\r\n\n";
|
||||
char *pauseString = "Hit enter to continue->";
|
||||
char *selectItem = "Select an item->";
|
||||
|
||||
static char menu[100]; // Buffer for file items
|
||||
int sel_item = 0; // selected item
|
||||
uint8_t activeMenu; // active menu
|
||||
|
||||
// forward refs in this module
|
||||
void printMenu(void); // print the menu
|
||||
int getMenuItem(void); // get a menu item
|
||||
void sendControlCode(uint8_t code);
|
||||
BOOL pause(void);
|
||||
void Print_Frame_stats(FRAME_STATS *stats);
|
||||
void Print_Memory_Stats(void);
|
||||
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);
|
||||
|
||||
// list of menus
|
||||
enum {
|
||||
MAIN_MENU=0, // main menu
|
||||
RADIO_MENU, // radio params menu
|
||||
STATION_MENU, // Station params menu
|
||||
N_MENUS // number of menus
|
||||
};
|
||||
|
||||
// menu return functions
|
||||
enum {
|
||||
RET_MORE=0, // more to do
|
||||
RET_DONE, // done
|
||||
RET_PAUSE // pause before leaving
|
||||
};
|
||||
|
||||
/*
|
||||
* The first part of this code contains the menus and processing routines
|
||||
* handle the various menu options
|
||||
* return TRUE if done, else FALSE
|
||||
*/
|
||||
uint8_t printAllSetup(void)
|
||||
{
|
||||
TIMEOFDAY tod;
|
||||
getTOD(&tod);
|
||||
|
||||
USART_Print_string("Current firmware is at %d.%d\r\n",def_params.params.FirmwareVerMajor,
|
||||
def_params.params.FirmwareVerMinor);
|
||||
USART_Print_string("System time is %02d:%02d:%02d\r\n\n", tod.Hours, tod.Minutes, tod.Seconds);
|
||||
|
||||
printStationSetup();
|
||||
printRadioSetup();
|
||||
return RET_PAUSE;
|
||||
}
|
||||
//
|
||||
uint8_t printStnSetup(void)
|
||||
{
|
||||
printStationSetup();
|
||||
return RET_PAUSE;
|
||||
}
|
||||
//
|
||||
uint8_t printRadSetup(void)
|
||||
{
|
||||
printRadioSetup();
|
||||
return RET_PAUSE;
|
||||
}
|
||||
// list mesh status
|
||||
uint8_t listMesh(void)
|
||||
{
|
||||
Mesh_ListStatus();
|
||||
return RET_PAUSE;
|
||||
}
|
||||
|
||||
// enter chat mode
|
||||
uint8_t chatMode(void)
|
||||
{
|
||||
if(Chat_Task_exec())
|
||||
return RET_PAUSE;
|
||||
return RET_MORE;
|
||||
}
|
||||
|
||||
// dump the rx stats
|
||||
uint8_t showstats(void)
|
||||
{
|
||||
Print_Frame_stats(GetFrameStats());
|
||||
Print_Radio_errors(GetRadioStatus());
|
||||
Print_FSM_state(GetFSMState());
|
||||
return RET_PAUSE;
|
||||
}
|
||||
|
||||
// set the radio entry mode
|
||||
uint8_t setRadio(void)
|
||||
{
|
||||
activeMenu = RADIO_MENU;
|
||||
menuState = MENU_OFF;
|
||||
return RET_DONE;
|
||||
}
|
||||
|
||||
// set the station entry mode
|
||||
uint8_t setStation(void)
|
||||
{
|
||||
activeMenu = STATION_MENU;
|
||||
menuState = MENU_OFF;
|
||||
return RET_DONE;
|
||||
}
|
||||
// enter chat mode
|
||||
uint8_t ledTest(void)
|
||||
{
|
||||
if(LedTest())
|
||||
return RET_PAUSE;
|
||||
return RET_MORE;
|
||||
}
|
||||
// write the flash memory
|
||||
uint8_t writeSetup(void)
|
||||
{
|
||||
if(!UpdateSetup()) {
|
||||
USART_Print_string("Error in writing flash: setup may be corrupt\r\n");
|
||||
} else {
|
||||
USART_Print_string("Flash written successfully\r\n");
|
||||
}
|
||||
menuState = MENU_OFF;
|
||||
return RET_PAUSE;
|
||||
}
|
||||
// exit the current menu
|
||||
uint8_t exitMenu(void)
|
||||
{
|
||||
activeMenu = MAIN_MENU;
|
||||
menuState = MENU_OFF;
|
||||
return RET_DONE;
|
||||
}
|
||||
// set a parameter value
|
||||
uint8_t setParam(void)
|
||||
{
|
||||
return(getEntry(activeMenu, sel_item));
|
||||
}
|
||||
/*
|
||||
* These are conditional
|
||||
*/
|
||||
#if __ENABLE_GPS
|
||||
uint8_t gpsEcho(void)
|
||||
{
|
||||
GPSEcho();
|
||||
return(getKeyEntry());
|
||||
}
|
||||
#endif
|
||||
#if __MEM_DEBUG
|
||||
// memory statistics
|
||||
uint8_t memStats(void)
|
||||
{
|
||||
Print_Memory_Stats();
|
||||
return RET_PAUSE;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* main menu definition
|
||||
* 1) define the number of line items
|
||||
* 2) create menuitms struct
|
||||
* 3) add to menucontents struct
|
||||
*/
|
||||
struct menuItems_t {
|
||||
char *menuLine; // text of menu line
|
||||
char selChar; // character to select it
|
||||
uint8_t (*func)(void); // processing function
|
||||
};
|
||||
|
||||
// main menu
|
||||
#if __ENABLE_GPS
|
||||
#define N_GPS 1 // additional menu item for GPS
|
||||
#else
|
||||
#define N_GPS 0
|
||||
#endif
|
||||
|
||||
#if __MEM_DEBUG
|
||||
#define N_MEM 1 // additional menu item for memory
|
||||
#else
|
||||
#define N_MEM 0
|
||||
#endif
|
||||
|
||||
#define N_MAINMENU (11+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 },
|
||||
#if __ENABLE_GPS
|
||||
{ "GPS Echo mode\r\n", 'G', gpsEcho },
|
||||
#endif
|
||||
{ "LED test\r\n", 'L', ledTest },
|
||||
#if __MEM_DEBUG
|
||||
{ "Memory Status\r\n", 'M', memStats },
|
||||
#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 }
|
||||
};
|
||||
|
||||
// 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 }
|
||||
};
|
||||
|
||||
// these need to correspond to the items above
|
||||
|
||||
// station menu
|
||||
#define N_STATIONMENU 8
|
||||
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 }
|
||||
};
|
||||
|
||||
// menu contents
|
||||
struct menuContents_t {
|
||||
char *title; // title of the menu
|
||||
int nMenuLines; // number of lines
|
||||
struct menuItems_t *menus; // menu items
|
||||
} menuContents[N_MENUS] = {
|
||||
#if _BOARD_TYPE==NUCLEO_BOARD
|
||||
{ " IP400 Nucleo Main menu\r\n", N_MAINMENU, mainMenu },
|
||||
#else
|
||||
{ " IP400 Pi menu\r\n", N_MAINMENU, mainMenu },
|
||||
#endif
|
||||
{ " Radio setup menu\r\n", N_RADIOMENU, radioMenu },
|
||||
{ " Station Setup menu\r\n", N_STATIONMENU, stationMenu }
|
||||
};
|
||||
|
||||
// menu (main) task
|
||||
void Menu_Task_Init(void)
|
||||
{
|
||||
// start off with the menu showing
|
||||
activeMenu = MAIN_MENU;
|
||||
menuState = MENU_SHOWING;
|
||||
entryState = NO_ENTRY;
|
||||
}
|
||||
|
||||
// send a control code
|
||||
void sendControlCode(uint8_t code)
|
||||
{
|
||||
USART_Send_String(controlCodes[code].sequence, controlCodes[code].len);
|
||||
}
|
||||
|
||||
// send a control code
|
||||
void sendTextString(char *string)
|
||||
{
|
||||
strcpy(menu, string);
|
||||
USART_Send_String(menu, strlen(string));
|
||||
}
|
||||
|
||||
// process our time slot
|
||||
void Menu_Task_Exec(void)
|
||||
{
|
||||
int nBytesinBuff = 0;
|
||||
char c;
|
||||
struct menuItems_t *m;
|
||||
|
||||
switch(menuState) {
|
||||
|
||||
// if there is a return in the console buffer,
|
||||
// bring up the menu
|
||||
case MENU_OFF:
|
||||
if((nBytesinBuff=databuffer_bytesInBuffer()) == 0)
|
||||
return;
|
||||
for(int i=0;i<nBytesinBuff;i++)
|
||||
if((c=databuffer_get(0)) == ASCII_RET)
|
||||
menuState = MENU_SHOWING;
|
||||
return;
|
||||
|
||||
case MENU_SHOWING:
|
||||
printMenu();
|
||||
menuState = MENU_SELECTING;
|
||||
return;
|
||||
|
||||
case MENU_SELECTING:
|
||||
if((sel_item=getMenuItem()) == NO_ITEM)
|
||||
return;
|
||||
menuState = MENU_SELECTED;
|
||||
break;
|
||||
|
||||
case MENU_SELECTED:
|
||||
m=menuContents[activeMenu].menus;
|
||||
m += sel_item;
|
||||
switch((*m->func)()) {
|
||||
|
||||
case RET_MORE:
|
||||
break;
|
||||
|
||||
case RET_PAUSE:
|
||||
sendTextString(pauseString);
|
||||
menuState = MENU_PAUSED;
|
||||
break;
|
||||
|
||||
case RET_DONE:
|
||||
menuState = MENU_SHOWING;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case MENU_PAUSED:
|
||||
if(pause())
|
||||
menuState = MENU_SHOWING;
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// print the main menu
|
||||
void printMenu(void)
|
||||
{
|
||||
struct menuItems_t *m;
|
||||
|
||||
// title
|
||||
sendControlCode(CONTROL_CLEAR);
|
||||
sendControlCode(CONTROL_HOME);
|
||||
|
||||
#if _BOARD_TYPE==NUCLEO_BOARD
|
||||
// only use double wide mode with Nucleo
|
||||
sendControlCode(CONTROL_DOUBLE_TOP);
|
||||
sendTextString(menuContents[activeMenu].title);
|
||||
sendControlCode(CONTROL_DOUBLE_BOTTOM);
|
||||
sendTextString(menuContents[activeMenu].title);
|
||||
sendControlCode(CONTROL_SINGLE);
|
||||
sendTextString(menuSpacer);
|
||||
#else
|
||||
sendTextString(menuContents[activeMenu].title);
|
||||
sendTextString(menuSpacer);
|
||||
#endif
|
||||
|
||||
// lines
|
||||
int nMenuLines = menuContents[activeMenu].nMenuLines;
|
||||
for(int i=0;i<nMenuLines;i++) {
|
||||
m=menuContents[activeMenu].menus;
|
||||
m += i;
|
||||
menu[0] = m->selChar;
|
||||
strcpy(&menu[1], ") ");
|
||||
strcat(menu, m->menuLine);
|
||||
USART_Send_String(menu, strlen(menu));
|
||||
}
|
||||
|
||||
// selection
|
||||
sendTextString(selectItem);
|
||||
}
|
||||
|
||||
// get a menu item and dispatch the correct processing routine
|
||||
int getMenuItem(void)
|
||||
{
|
||||
int nBytesinBuff =0;
|
||||
struct menuItems_t *m;
|
||||
char c;
|
||||
|
||||
if((nBytesinBuff=databuffer_bytesInBuffer()) == 0)
|
||||
return NO_ITEM;
|
||||
|
||||
for(int i=0;i<nBytesinBuff;i++) {
|
||||
if((c=databuffer_get(0)) != BUFFER_NO_DATA) {
|
||||
|
||||
// translate and echo
|
||||
c = islower(c) ? toupper(c) : c;
|
||||
USART_Send_Char(c);
|
||||
|
||||
// find the correct processing routine
|
||||
int nMenuLines = menuContents[activeMenu].nMenuLines;
|
||||
for(int j=0;j<nMenuLines;j++) {
|
||||
m=menuContents[activeMenu].menus;
|
||||
m += j;
|
||||
if(m->selChar == c) {
|
||||
sendTextString(menuSpacer);
|
||||
return j;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
sendTextString(whatItem);
|
||||
return NO_ITEM;
|
||||
}
|
||||
|
||||
BOOL pause(void)
|
||||
{
|
||||
if(databuffer_bytesInBuffer() == 0)
|
||||
return FALSE;
|
||||
|
||||
if(databuffer_get(0) == ASCII_RET)
|
||||
return TRUE;
|
||||
|
||||
return FALSE;
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* 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);
|
||||
|
||||
/*
|
||||
* Get a key entry: basically stolen from chat.c
|
||||
*/
|
||||
uint8_t getKeyEntry(void)
|
||||
{
|
||||
|
||||
char c;
|
||||
int nBytesinBuff;
|
||||
|
||||
if((nBytesinBuff=databuffer_bytesInBuffer()) == 0)
|
||||
return RET_MORE;
|
||||
|
||||
for(int i=0;i<nBytesinBuff;i++) {
|
||||
|
||||
c=databuffer_get(0);
|
||||
|
||||
if(delMode) {
|
||||
if((c != KEY_DEL) && (c != KEY_BKSP)) {
|
||||
USART_Print_string("\\%c", c);
|
||||
if(pos < MAX_ENTRY)
|
||||
keyBuffer[pos++] = c;
|
||||
delMode = FALSE;
|
||||
} else {
|
||||
if(pos > 0) {
|
||||
USART_Print_string("%c", keyBuffer[--pos]);
|
||||
} else {
|
||||
USART_Print_string("\\\r\n");
|
||||
delMode = FALSE;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
} else {
|
||||
// processing a key
|
||||
|
||||
switch (c) {
|
||||
|
||||
// EOL key: sent the packet
|
||||
case KEY_EOL:
|
||||
USART_Print_string("\r\n");
|
||||
keyBuffer[pos++] = '\0';
|
||||
return RET_DONE;
|
||||
break;
|
||||
|
||||
// escape key: abort the entry
|
||||
case KEY_ESC:
|
||||
return RET_PAUSE;
|
||||
break;
|
||||
|
||||
case KEY_DEL:
|
||||
case KEY_BKSP:
|
||||
USART_Print_string("\\%c", keyBuffer[--pos]);
|
||||
delMode = TRUE;
|
||||
break;
|
||||
|
||||
default:
|
||||
USART_Send_Char(c);
|
||||
if(pos < MAX_ENTRY)
|
||||
keyBuffer[pos++] = c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return RET_MORE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get an entry and validate it
|
||||
*/
|
||||
uint8_t getEntry(int activeMenu, int item)
|
||||
{
|
||||
struct menuItems_t *m;
|
||||
|
||||
switch(entryState) {
|
||||
|
||||
case NO_ENTRY:
|
||||
delMode = FALSE;
|
||||
pos = 0;
|
||||
entryState = ENTERING;
|
||||
m=menuContents[activeMenu].menus;
|
||||
m += item;
|
||||
USART_Print_string("%s->", m->menuLine);
|
||||
return RET_MORE;
|
||||
|
||||
case ENTERING:
|
||||
int keyStat = getKeyEntry();
|
||||
if(keyStat == RET_DONE) {
|
||||
entryState = VALIDATING;
|
||||
return RET_MORE;
|
||||
}
|
||||
return keyStat;
|
||||
|
||||
case VALIDATING:
|
||||
entryState = NO_ENTRY;
|
||||
return validateEntry(activeMenu, item, keyBuffer);
|
||||
}
|
||||
|
||||
return RET_MORE;
|
||||
}
|
||||
|
||||
// data types we are updating
|
||||
enum {
|
||||
uint8_type, // uint8 field
|
||||
int16_type, // int16 field
|
||||
uint32_type, // uint32 field
|
||||
float_type, // floating point
|
||||
char_type, // character type
|
||||
field_type // field type
|
||||
};
|
||||
|
||||
// struct to hold validation values
|
||||
typedef struct field_validator_t {
|
||||
int MinVal; // minimum value
|
||||
int MaxVal; // maximum value
|
||||
void *setupVal; // pointer to setup value
|
||||
int type; // type of entry
|
||||
uint32_t scalar; // scalar to convert to decimal
|
||||
} FIELD_VALIDATOR;
|
||||
|
||||
// validators for radio mmenu
|
||||
FIELD_VALIDATOR radioValidators[] = {
|
||||
{ MIN_FREQ, 450000000, &setup_memory.params.radio_setup.lFrequencyBase, uint32_type, 1000000 },
|
||||
{ 9600, 600000, &setup_memory.params.radio_setup.lDatarate, uint32_type, 1000 },
|
||||
{ 12500, 150000, &setup_memory.params.radio_setup.lFreqDev, uint32_type, 1000 },
|
||||
{ 2600, 1600000, &setup_memory.params.radio_setup.lBandwidth, uint32_type, 1000 },
|
||||
{ 0, 20, &setup_memory.params.radio_setup.outputPower, uint8_type, 1 },
|
||||
{ -115, 0, &setup_memory.params.radio_setup.rxSquelch, int16_type, 1 },
|
||||
};
|
||||
|
||||
FIELD_VALIDATOR stationValidators[] = {
|
||||
{ 4, 6, &setup_memory.params.setup_data.stnCall, 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 }
|
||||
};
|
||||
|
||||
uint8_t validateEntry(int activeMenu, int item, char *keyBuffer)
|
||||
{
|
||||
|
||||
int newValue, min, max;
|
||||
|
||||
//NB: the cases here must jive with the menu items
|
||||
switch(activeMenu) {
|
||||
|
||||
case MAIN_MENU: // the only menu item here is the clock
|
||||
setTOD(keyBuffer);
|
||||
break;
|
||||
|
||||
case RADIO_MENU:
|
||||
// convert a floating point entry to the required decimal
|
||||
if(isfloat(keyBuffer))
|
||||
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;
|
||||
}
|
||||
switch(radioValidators[item].type){
|
||||
|
||||
case uint8_type:
|
||||
uint8_t *v8 = (uint8_t *)radioValidators[item].setupVal;
|
||||
*v8 = (uint8_t)newValue;
|
||||
break;
|
||||
|
||||
case int16_type:
|
||||
int16_t *v16 = (int16_t *)radioValidators[item].setupVal;
|
||||
*v16 = (int16_t)newValue;
|
||||
break;
|
||||
|
||||
case uint32_type:
|
||||
uint32_t *v32 = (uint32_t *)radioValidators[item].setupVal;
|
||||
*v32 = (uint32_t)newValue;
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case STATION_MENU:
|
||||
switch(stationValidators[item].type){
|
||||
|
||||
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;
|
||||
}
|
||||
uint16_t *v8 = (uint16_t *)stationValidators[item].setupVal;
|
||||
*v8 = (uint16_t)newValue;
|
||||
break;
|
||||
|
||||
case char_type:
|
||||
size_t len = strlen(keyBuffer);
|
||||
max = stationValidators[item].MaxVal;
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
// falls to here when done...
|
||||
return RET_DONE;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Print the frame stats
|
||||
*/
|
||||
void Print_Frame_stats(FRAME_STATS *stats)
|
||||
{
|
||||
USART_Print_string("Frame Statistics\r\n\n");
|
||||
|
||||
USART_Print_string("Transmitted frames->%d\r\n", stats->TxFrameCnt);
|
||||
USART_Print_string("CRC Errors->%d\r\n", stats->CRCErrors);
|
||||
USART_Print_string("Rx Timeouts->%d\r\n", stats->TimeOuts);
|
||||
USART_Print_string("Frames with good CRC->%d\r\n", stats->RxFrameCnt);
|
||||
USART_Print_string("Beacon frames->%d\r\n", stats->nBeacons);
|
||||
USART_Print_string("Repeated frames->%d\r\n", stats->nRepeated);
|
||||
|
||||
USART_Print_string("Processed Frames->%d\r\n", stats->RxFrameCnt);
|
||||
USART_Print_string("Dropped frames->%d\r\n", stats->dropped);
|
||||
USART_Print_string("Duplicate frames->%d\r\n", stats->duplicates);
|
||||
USART_Print_string("Repeated frames->%d\r\n", stats->duplicates);
|
||||
|
||||
|
||||
}
|
||||
/*
|
||||
* Print the radio error stats
|
||||
*/
|
||||
struct radio_errs_t {
|
||||
uint32_t mask;
|
||||
char *errmsg;
|
||||
} radio_errs[N_RADIO_ERRS] = {
|
||||
{ SEQ_COMPLETE_ERR, "Sequencer Error" },
|
||||
{ SEQ_ACT_TIMEOUT, "Sequencer action timeout" },
|
||||
{ PLL_CALAMP_ERR, "VCO Amplitude calibration error" },
|
||||
{ PLL_CALFREQ_ERR, "VCO Frequency calibration error" },
|
||||
{ PLL_UNLOCK_ERR, "PLL Unlocked" },
|
||||
{ PLL_LOCK_FAIL, "PLL Lock failure" },
|
||||
{ DBM_FIFO_ERR, "Data buffer FIFO error" }
|
||||
};
|
||||
//
|
||||
void Print_Radio_errors(uint32_t errs)
|
||||
{
|
||||
if(errs == 0){
|
||||
USART_Print_string("No radio errors\r\n");
|
||||
return;
|
||||
}
|
||||
for(int i=0;i<N_RADIO_ERRS;i++)
|
||||
if(errs & radio_errs[i].mask)
|
||||
USART_Print_string("%s\r\n", radio_errs[i].errmsg);
|
||||
}
|
||||
/*
|
||||
* Print the FSM state
|
||||
*/
|
||||
char *fsm_states[FSM_N_FSM_STATES] = {
|
||||
"Idle",
|
||||
"Enable RF registers",
|
||||
"Wait for active 2",
|
||||
"Active 2",
|
||||
"Enable current",
|
||||
"Synth setup",
|
||||
"VCO calibration",
|
||||
"Lock Rx and Rx",
|
||||
"Llock on Rx",
|
||||
"Enable PA",
|
||||
"Transmit",
|
||||
"Analog power down",
|
||||
"End transmit",
|
||||
"lock on Rx",
|
||||
"Enable Rx",
|
||||
"Enable LNA",
|
||||
"Recieve",
|
||||
"End rx",
|
||||
"Synth power down"
|
||||
};
|
||||
void Print_FSM_state(uint8_t state)
|
||||
{
|
||||
if(state > FSM_N_FSM_STATES) {
|
||||
USART_Print_string("\r\n?Unknown FSM state\r\n");
|
||||
return;
|
||||
}
|
||||
USART_Print_string("\r\nRadio FSM State: %d: %s\r\n", state, fsm_states[state]);
|
||||
}
|
||||
|
||||
#if __MEM_DEBUG
|
||||
/*
|
||||
* dump memory statistics
|
||||
* uses the mallinfo structure
|
||||
*/
|
||||
void Print_Memory_Stats(void)
|
||||
{
|
||||
|
||||
struct mallinfo memStats;
|
||||
|
||||
memStats = mallinfo();
|
||||
|
||||
USART_Print_string("Memory Statistics\r\n\n");
|
||||
|
||||
USART_Print_string("Total non-mapped bytes (arena)->%ld\r\n", memStats.arena);
|
||||
USART_Print_string("Chunks not in use->%ld\r\n", memStats.ordblks);
|
||||
USART_Print_string("Free fast bin blocks->%ld\r\n", memStats.smblks);
|
||||
USART_Print_string("Mapped Regions->%ld\r\n", memStats.hblks);
|
||||
USART_Print_string("Bytes in mapped regions->%ld\r\n\n", memStats.hblkhd);
|
||||
|
||||
USART_Print_string("Free bytes in fast bins->%ld\r\n", memStats.fsmblks);
|
||||
USART_Print_string("Total Allocated Space->%ld\r\n", memStats.uordblks);
|
||||
USART_Print_string("Total Space not in use->%ld\r\n", memStats.fordblks);
|
||||
USART_Print_string("Topmost releasable block->%ld\r\n", memStats.keepcost);
|
||||
|
||||
}
|
||||
#endif
|
303
Node Firmware/IP400/Src/mesh.c
Normal file
303
Node Firmware/IP400/Src/mesh.c
Normal file
|
@ -0,0 +1,303 @@
|
|||
/*---------------------------------------------------------------------------
|
||||
Project: WL33_NUCLEO_UART
|
||||
|
||||
File Name: mesh.c
|
||||
|
||||
Author: MartinA
|
||||
|
||||
Description: This code builds and lists the mesh status
|
||||
|
||||
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 <sys/queue.h>
|
||||
#include <stdint.h>
|
||||
#include <FreeRTOS.h>
|
||||
#include <task.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "tasks.h"
|
||||
#include "frame.h"
|
||||
#include "usart.h"
|
||||
#include "dataq.h"
|
||||
#include "setup.h"
|
||||
#include "tod.h"
|
||||
#include "ip.h"
|
||||
|
||||
#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
|
||||
MESHTBL_VALID, // valid entry
|
||||
MESHTBL_LOST // lost in space
|
||||
} MeshTableStatus;
|
||||
|
||||
// mesh entry struct
|
||||
typedef struct mesh_entry_t {
|
||||
MeshTableStatus status; // status
|
||||
IP400_CALL encCall; // encoded callsign
|
||||
uint32_t nextSeq; // next sequence number
|
||||
int16_t lastRssi; // signal strength
|
||||
uint8_t txPower; // transmit power
|
||||
SETUP_FLAGS capabilities; // capabilities
|
||||
TIMEOFDAY lastHeard; // last heard
|
||||
uint8_t hopCount; // hop count
|
||||
uint32_t ipAddress; // IP address
|
||||
} MESH_ENTRY;
|
||||
|
||||
// mesh table definitions
|
||||
#define MAX_MESH_MEMORY 2048 // max amount of mesh memory used
|
||||
#define MAX_MESH_ENTRIES (MAX_MESH_MEMORY/sizeof(MESH_ENTRY))
|
||||
#define ENTRY_NOTFOUND -1 // not found in the table
|
||||
|
||||
// table size is limited to by memory defintion
|
||||
static MESH_ENTRY MeshTable[MAX_MESH_ENTRIES] __attribute__((section("MESHTABLE")));
|
||||
int nMeshEntries = 0;
|
||||
|
||||
BEACON_HEADER mesh_bcn_hdr; // beacon header
|
||||
uint32_t seqNum; // sequence number received
|
||||
|
||||
// forward refs in this module
|
||||
int findCall(IP400_CALL *call);
|
||||
void AddMeshEntry(IP400_FRAME *frameData, int16_t rssi, BOOL isBeacon);
|
||||
|
||||
// task initialization
|
||||
void Mesh_Task_Init(void)
|
||||
{
|
||||
for(int i=0;i<MAX_MESH_ENTRIES;i++)
|
||||
MeshTable[i].status = MESHTBL_UNUSED;
|
||||
}
|
||||
|
||||
// process a beacon packet: if we don't have it, enter it
|
||||
// otherwise update the last time heard
|
||||
void Mesh_ProcessBeacon(void *rxFrame, uint32_t rssi)
|
||||
{
|
||||
IP400_FRAME *frameData = (IP400_FRAME *)rxFrame;
|
||||
|
||||
TIMEOFDAY current;
|
||||
getTOD(¤t);
|
||||
|
||||
int16_t actRSSI = rssi/2 - RSSI_SCALAR;
|
||||
int entryNum;
|
||||
|
||||
// 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) {
|
||||
|
||||
// if it is repeated frame with a higher hop count, ignore it
|
||||
if(MeshTable[entryNum].hopCount < frameData->flagfld.flags.hop_count)
|
||||
return;
|
||||
|
||||
MeshTable[entryNum].lastHeard.Hours = current.Hours;
|
||||
MeshTable[entryNum].lastHeard.Minutes = current.Minutes;
|
||||
MeshTable[entryNum].lastHeard.Seconds = current.Seconds;
|
||||
MeshTable[entryNum].lastRssi = actRSSI;
|
||||
MeshTable[entryNum].nextSeq = frameData->seqNum == 0xFFFFFFFF ? 0 : ++frameData->seqNum;
|
||||
|
||||
//ugly, but necessary
|
||||
memset(mesh_bcn_hdr.hdrBytes, 0, sizeof(BEACON_HEADER));
|
||||
uint8_t *p = (uint8_t *)frameData->buf;
|
||||
mesh_bcn_hdr.hdrBytes[0] = *p++;
|
||||
mesh_bcn_hdr.hdrBytes[4] = *p;
|
||||
|
||||
MeshTable[entryNum].capabilities = mesh_bcn_hdr.setup.flags;
|
||||
MeshTable[entryNum].txPower = mesh_bcn_hdr.setup.txPower;
|
||||
|
||||
// all done
|
||||
return;
|
||||
}
|
||||
|
||||
// beacon from a new station
|
||||
AddMeshEntry(frameData, actRSSI, TRUE);
|
||||
}
|
||||
|
||||
/*
|
||||
* add a new station to the mesh table
|
||||
*/
|
||||
void AddMeshEntry(IP400_FRAME *frameData, int16_t actRSSI, BOOL isBeacon)
|
||||
{
|
||||
// check for room first
|
||||
if(nMeshEntries >= MAX_MESH_ENTRIES)
|
||||
return;
|
||||
|
||||
MESH_ENTRY newEntry;
|
||||
|
||||
TIMEOFDAY current;
|
||||
getTOD(¤t);
|
||||
SOCKADDR_IN ipAddr;
|
||||
|
||||
// grab call and capabilities
|
||||
newEntry.encCall = frameData->source;
|
||||
newEntry.lastHeard.Hours = current.Hours;
|
||||
newEntry.lastHeard.Minutes = current.Minutes;
|
||||
newEntry.lastHeard.Seconds = current.Seconds;
|
||||
newEntry.nextSeq = frameData->seqNum == 0xFFFFFFFF ? 0 : ++frameData->seqNum;
|
||||
newEntry.lastRssi = actRSSI;
|
||||
newEntry.txPower = 0;
|
||||
newEntry.hopCount = frameData->flagfld.flags.hop_count;
|
||||
|
||||
GetIPAddr(&newEntry.encCall, &ipAddr);
|
||||
newEntry.ipAddress = ipAddr.sin_addr.S_un.S_addr;
|
||||
|
||||
if(isBeacon) {
|
||||
memcpy(mesh_bcn_hdr.hdrBytes, (struct beacon_hdr_t *)frameData->buf, sizeof(BEACON_HEADER));
|
||||
newEntry.capabilities = mesh_bcn_hdr.setup.flags;
|
||||
newEntry.txPower = mesh_bcn_hdr.setup.txPower;
|
||||
} else {
|
||||
memset(&newEntry.capabilities, 0, sizeof(SETUP_FLAGS));
|
||||
}
|
||||
|
||||
// insert this at the end of the queue
|
||||
newEntry.status = MESHTBL_VALID;
|
||||
memcpy(&MeshTable[nMeshEntries++], &newEntry, sizeof(MESH_ENTRY));
|
||||
}
|
||||
|
||||
/*
|
||||
* reject a duplicate frame that may have been repeated
|
||||
* TRUE: keep it, FALSE: reject it
|
||||
*/
|
||||
BOOL Check_Sender_Address(void *rxFrame, uint32_t rssi)
|
||||
{
|
||||
IP400_FRAME *frameData = (IP400_FRAME *)rxFrame;
|
||||
int entryNum;
|
||||
|
||||
if((entryNum = findCall(&frameData->source)) != ENTRY_NOTFOUND) {
|
||||
// rebooted
|
||||
if(frameData->seqNum == 0xFFFFFFFF)
|
||||
MeshTable[entryNum].nextSeq = 0;
|
||||
// reject if the seq is lower
|
||||
if(frameData->seqNum < MeshTable[entryNum].nextSeq)
|
||||
return FALSE;
|
||||
MeshTable[entryNum].nextSeq = frameData->seqNum + 1;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// sender is unknown: add him for now..
|
||||
int16_t actRSSI = rssi/2 - RSSI_SCALAR;
|
||||
AddMeshEntry(frameData, actRSSI, FALSE);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if the frame can be accepted.
|
||||
* Accept a broadcast or my address if:
|
||||
* -the sender is not in the mesh table
|
||||
* -the sequence is not out of order
|
||||
* Else reject it
|
||||
*/
|
||||
BOOL Mesh_Accept_Frame(void *rxFrame, uint32_t rssi)
|
||||
{
|
||||
IP400_FRAME *frameData = (IP400_FRAME *)rxFrame;
|
||||
char decCall[30];
|
||||
uint16_t port;;
|
||||
|
||||
// frame is sent a broadcast address
|
||||
if((frameData->dest.callbytes.bytes[0] == BROADCAST_ADDR)
|
||||
&& (frameData->dest.callbytes.bytes[0] == BROADCAST_ADDR))
|
||||
return Check_Sender_Address(rxFrame, rssi);
|
||||
|
||||
callDecode(&frameData->dest, decCall, &port);
|
||||
if(CompareToMyCall(decCall))
|
||||
return Check_Sender_Address(rxFrame, rssi);
|
||||
|
||||
// not for me
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// find a callsign in the list
|
||||
int findCall(IP400_CALL *call)
|
||||
{
|
||||
for(int i=0;i<nMeshEntries;i++) {
|
||||
if(MeshTable[i].encCall.callbytes.encoded == call->callbytes.encoded)
|
||||
return i;
|
||||
}
|
||||
return ENTRY_NOTFOUND;
|
||||
}
|
||||
|
||||
char capabilties[50];
|
||||
// return the capabilities of a node
|
||||
char *GetCapabilities(SETUP_FLAGS cap)
|
||||
{
|
||||
mesh_bcn_hdr.setup.flags = cap;
|
||||
|
||||
if(mesh_bcn_hdr.hdrBytes[0] == 0) {
|
||||
strcpy(capabilties, "Unknown");
|
||||
}
|
||||
|
||||
// modes
|
||||
if(cap.fsk) {
|
||||
sprintf(capabilties, "%s", FSKSpeeds[cap.rate]);
|
||||
}
|
||||
else if(cap.ofdm) {
|
||||
strcat(capabilties, " OFDM");
|
||||
}
|
||||
else if(cap.aredn) {
|
||||
strcat(capabilties, "AREDN");
|
||||
}
|
||||
|
||||
// repeat mode is on..
|
||||
if(cap.repeat) {
|
||||
strcat(capabilties, " RPT");
|
||||
}
|
||||
|
||||
return capabilties;
|
||||
}
|
||||
|
||||
// list the mesh status: walk the mesh entries
|
||||
void Mesh_ListStatus(void)
|
||||
{
|
||||
USART_Print_string("Stations Heard: %d\r\n", nMeshEntries);
|
||||
if(nMeshEntries == 0)
|
||||
return;
|
||||
|
||||
// process the list
|
||||
char decodedCall[20];
|
||||
uint16_t port;
|
||||
SOCKADDR_IN ipAddr;
|
||||
|
||||
USART_Print_string("Call(Port)\tIP Addr\t\tRSSI\tNext Seq\tLast Heard\tHops\tCapabilities\r\n");
|
||||
|
||||
for(int i=0;i<nMeshEntries;i++) {
|
||||
if(MeshTable[i].status == MESHTBL_VALID) {
|
||||
|
||||
callDecode(&MeshTable[i].encCall, decodedCall, &port);
|
||||
GetIPAddr(&MeshTable[i].encCall, &ipAddr);
|
||||
|
||||
USART_Print_string("%s(%d)\t%d.%d.%d.%d\t%-03d\t%04d\t\t%02d:%02d:%02d\t%d\t%s %d dBm\r\n",
|
||||
decodedCall, port,
|
||||
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,
|
||||
MeshTable[i].lastRssi,
|
||||
MeshTable[i].nextSeq,
|
||||
MeshTable[i].lastHeard.Hours, MeshTable[i].lastHeard.Minutes, MeshTable[i].lastHeard.Seconds,
|
||||
MeshTable[i].hopCount,
|
||||
GetCapabilities(MeshTable[i].capabilities), MeshTable[i].txPower);
|
||||
}
|
||||
USART_Print_string("\r\n\n");
|
||||
}
|
||||
}
|
305
Node Firmware/IP400/Src/setup.c
Normal file
305
Node Firmware/IP400/Src/setup.c
Normal file
|
@ -0,0 +1,305 @@
|
|||
/*---------------------------------------------------------------------------
|
||||
Project: WL33_NUCLEO_UART
|
||||
|
||||
File Name: setup.c
|
||||
|
||||
Author: MartinA
|
||||
|
||||
Description: Holds and displays the setup data
|
||||
|
||||
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 <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <main.h>
|
||||
|
||||
#include "types.h"
|
||||
#include "setup.h"
|
||||
#include "usart.h"
|
||||
|
||||
#define USE_HAL
|
||||
|
||||
// flash stuff
|
||||
#define FLASH_PAGE_ADDR ((uint32_t)0x1007F800)
|
||||
#define FLASH_PAGE_NUM 127
|
||||
static FLASH_EraseInitTypeDef EraseInitStruct;
|
||||
|
||||
// Default Setup Data
|
||||
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.stnCall ="NOCALL",
|
||||
.params.setup_data.gridSq = "DO21vd",
|
||||
.params.setup_data.latitude = "51.08",
|
||||
.params.setup_data.longitude = "-114.10",
|
||||
.params.setup_data.beaconInt = 5,
|
||||
//
|
||||
.params.radio_setup.lFrequencyBase = 445750000,
|
||||
.params.radio_setup.xModulationSelect = MOD_4FSK,
|
||||
.params.radio_setup.lDatarate = 100000,
|
||||
.params.radio_setup.lFreqDev = 25000,
|
||||
.params.radio_setup.lBandwidth = 200000,
|
||||
.params.radio_setup.dsssExp = 0,
|
||||
.params.radio_setup.outputPower = 14,
|
||||
.params.radio_setup.PADrvMode = PA_DRV_TX_HP,
|
||||
.params.radio_setup.rxSquelch = -95,
|
||||
//
|
||||
.params.FirmwareVerMajor = 1, // current rev is 1.0
|
||||
.params.FirmwareVerMinor = 0,
|
||||
.params.Magic = SETUP_MAGIC,
|
||||
.params.SetupCRC = 0
|
||||
};
|
||||
|
||||
char prtBuf[100];
|
||||
|
||||
// enum fields in setup struct
|
||||
// modulation type
|
||||
char *modTypes[] = {
|
||||
"2FSK",
|
||||
"4FSK",
|
||||
"2GFSK05",
|
||||
"2GFSK1",
|
||||
"4GFSK05",
|
||||
"4GFSK1",
|
||||
"ASK",
|
||||
"OOK",
|
||||
"POLAR",
|
||||
"CW"
|
||||
};
|
||||
|
||||
// PA modes
|
||||
char *paModes[] = {
|
||||
"TX 10dBm Max",
|
||||
"HP 14dBm Max",
|
||||
"TX_HP 20dBm Max"
|
||||
};
|
||||
|
||||
// return the setup struct
|
||||
STN_PARAMS *GetStationParams(void) // get the station params
|
||||
{
|
||||
return &setup_memory.params;
|
||||
}
|
||||
|
||||
// compare call to station callsign
|
||||
// returns true if callsigns match
|
||||
BOOL CompareToMyCall(char *call)
|
||||
{
|
||||
char expCall[20];
|
||||
|
||||
// make sure call sign is padded out to 6 characters b4 comparison
|
||||
strcpy(expCall, setup_memory.params.setup_data.stnCall);
|
||||
strcat(expCall, " ");
|
||||
|
||||
if(!strncmp(call, expCall, MAX_CALL))
|
||||
return TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Print the setup struct
|
||||
*/
|
||||
void printStationSetup(void)
|
||||
{
|
||||
// station callsign 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");
|
||||
|
||||
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.repeat)
|
||||
USART_Print_string("\r\nRepeat mode on by default\r\n");
|
||||
else
|
||||
USART_Print_string("\r\nRepeat mode off by default\r\n");
|
||||
USART_Print_string("Beacon Interval->%d mins\r\n\n", setup_memory.params.setup_data.beaconInt);
|
||||
}
|
||||
|
||||
void printRadioSetup(void)
|
||||
{
|
||||
// dump the radio init struct
|
||||
uint16_t fWhole = setup_memory.params.radio_setup.lFrequencyBase/1e6;
|
||||
uint16_t fFract = setup_memory.params.radio_setup.lFrequencyBase/1e3 - fWhole*1e3;
|
||||
USART_Print_string("RF Frequency->%d.%d MHz\r\n", fWhole, fFract);
|
||||
|
||||
USART_Print_string("Modulation method->%s\r\n", modTypes[setup_memory.params.radio_setup.xModulationSelect]);
|
||||
|
||||
uint16_t dWhole = setup_memory.params.radio_setup.lDatarate/1000;
|
||||
uint16_t dFract = setup_memory.params.radio_setup.lDatarate - dWhole*1000;
|
||||
USART_Print_string("Data Rate->%d.%d Kbps\r\n", dWhole, dFract);
|
||||
|
||||
uint16_t pWhole = setup_memory.params.radio_setup.lFreqDev/1000;
|
||||
uint16_t pFract = setup_memory.params.radio_setup.lFreqDev - pWhole*1000;
|
||||
USART_Print_string("Peak Deviation->%d.%d KHz\r\n", pWhole, pFract);
|
||||
|
||||
uint16_t bWhole = setup_memory.params.radio_setup.lBandwidth/1000;
|
||||
uint16_t bFract = setup_memory.params.radio_setup.lBandwidth - bWhole*1000;
|
||||
USART_Print_string("Channel Filter Bandwidth->%d.%d KHz\r\n", bWhole, bFract);
|
||||
|
||||
USART_Print_string("Output Power->%d dBm\r\n", setup_memory.params.radio_setup.outputPower);
|
||||
USART_Print_string("PA Mode->%s\r\n", paModes[setup_memory.params.radio_setup.PADrvMode]);
|
||||
USART_Print_string("Rx Squelch->%d\r\n\n\n", setup_memory.params.radio_setup.rxSquelch);
|
||||
}
|
||||
|
||||
/*
|
||||
* This code manages saving and reading the setup params
|
||||
*/
|
||||
// internals
|
||||
uint32_t CalcSetupCRC(void);
|
||||
|
||||
BOOL UpdateSetup(void)
|
||||
{
|
||||
// update CRC before writing
|
||||
setup_memory.params.SetupCRC = CalcSetupCRC();
|
||||
|
||||
if(WriteSetup() != HAL_OK) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if(!ReadSetup()) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if(!VerifySetup()) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE; // only one iteration required
|
||||
}
|
||||
|
||||
// verify that the current setup record is valid
|
||||
BOOL VerifySetup(void)
|
||||
{
|
||||
// if the magic number matches, then all is well
|
||||
if(setup_memory.params.Magic != SETUP_MAGIC)
|
||||
return FALSE;
|
||||
|
||||
uint32_t SetupCRC = CalcSetupCRC();
|
||||
|
||||
if(SetupCRC == setup_memory.params.SetupCRC)
|
||||
return TRUE;
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// set the default setup
|
||||
void SetDefSetup(void)
|
||||
{
|
||||
memcpy((void *)&setup_memory.bytes, (const void *)&def_params.bytes, sizeof(SETUP_MEMORY));
|
||||
}
|
||||
|
||||
// Read setup from Flash memory
|
||||
// data is stored in the last flash page
|
||||
BOOL ReadSetup(void)
|
||||
{
|
||||
__IO uint32_t data32 = 0;;
|
||||
|
||||
uint32_t memAddr = FLASH_PAGE_ADDR;
|
||||
|
||||
uint32_t *dst_addr = setup_memory.flashwords;
|
||||
uint16_t nwords = sizeof(STN_PARAMS)/sizeof(uint32_t);
|
||||
|
||||
while(nwords--){
|
||||
data32 = *(__IO uint32_t *)memAddr;
|
||||
*dst_addr++ = data32;
|
||||
memAddr += sizeof(uint32_t);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// write the setup to OTP memory
|
||||
HAL_StatusTypeDef WriteSetup(void)
|
||||
{
|
||||
HAL_StatusTypeDef status=0;
|
||||
uint32_t PageError;
|
||||
|
||||
__HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_ALL_ERRORS);
|
||||
|
||||
// erase it first
|
||||
EraseInitStruct.TypeErase = FLASH_TYPEERASE_PAGES;
|
||||
EraseInitStruct.Page = FLASH_PAGE_NUM;
|
||||
EraseInitStruct.NbPages = 1;
|
||||
if ((status=HAL_FLASHEx_Erase(&EraseInitStruct, &PageError)) != HAL_OK)
|
||||
return status;
|
||||
|
||||
// waste some time...
|
||||
for(int i=0;i<1000;i++);
|
||||
|
||||
// now write
|
||||
uint32_t memAddr = FLASH_PAGE_ADDR;
|
||||
uint32_t *src_addr = setup_memory.flashwords;
|
||||
uint16_t nwords = sizeof(SETUP_MEMORY)/sizeof(uint32_t);
|
||||
|
||||
while(nwords--)
|
||||
{
|
||||
uint32_t data32 = *(__IO uint32_t *)src_addr++;
|
||||
|
||||
if ((status=HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, memAddr, data32)) != HAL_OK)
|
||||
return status;
|
||||
memAddr += sizeof(uint32_t);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
// calcuate the setup CRC
|
||||
uint32_t CalcSetupCRC(void)
|
||||
{
|
||||
uint32_t CRCValue;
|
||||
#ifdef USE_HAL
|
||||
CRCValue = HAL_CRC_Calculate(&hcrc, (uint32_t *)&setup_memory.bytes, sizeof(setup_memory) - sizeof(uint32_t));
|
||||
#else
|
||||
|
||||
uint32_t cnt, count = sizeof(setup_memory) - sizeof(uint32_t);
|
||||
uint8_t *arr = setup_memory.bytes;
|
||||
|
||||
/* Reset CRC data register if necessary */
|
||||
CRC->CR = CRC_CR_RESET;
|
||||
|
||||
|
||||
/* Calculate number of 32-bit blocks */
|
||||
cnt = count >> 2;
|
||||
|
||||
/* Calculate */
|
||||
while (cnt--) {
|
||||
/* Set new value */
|
||||
CRC->DR = *(uint32_t *)arr;
|
||||
|
||||
/* Increase by 4 */
|
||||
arr += 4;
|
||||
}
|
||||
|
||||
/* Calculate remaining data as 8-bit */
|
||||
cnt = count % 4;
|
||||
|
||||
/* Calculate */
|
||||
while (cnt--) {
|
||||
/* Set new value */
|
||||
*((uint8_t *)&CRC->DR) = *arr++;
|
||||
}
|
||||
|
||||
/* Return data */
|
||||
CRCValue = CRC->DR;
|
||||
#endif
|
||||
return(CRCValue);
|
||||
}
|
364
Node Firmware/IP400/Src/spi.c
Normal file
364
Node Firmware/IP400/Src/spi.c
Normal file
|
@ -0,0 +1,364 @@
|
|||
/*---------------------------------------------------------------------------
|
||||
Project: NucleoCC2
|
||||
|
||||
File Name: spi.c
|
||||
|
||||
Author: Martin, VE6VH
|
||||
|
||||
Description: SPI task. responds to commands from the host, implementing a quasi-
|
||||
memory device
|
||||
|
||||
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 <FreeRTOS.h>
|
||||
#include <semphr.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <malloc.h>
|
||||
|
||||
#include "types.h"
|
||||
#include "main.h"
|
||||
#include "spi.h"
|
||||
#include "dataq.h"
|
||||
#include "tasks.h"
|
||||
|
||||
#define SPI_MAX_TIME 200 // 200 ms max no activity timeout
|
||||
#define NO_SPI_TIMEOUT (SPI_MAX_TIME/SPI_TASK_SCHED) // timeout in schedule ticks
|
||||
|
||||
// transmitter states
|
||||
enum {
|
||||
SPITXIDLE=0, // idle
|
||||
SPITXFRAG // next fragment
|
||||
};
|
||||
|
||||
// receiver states
|
||||
enum {
|
||||
SPIRXIDLE=0, // idle
|
||||
SPIRXFRAG // next fragment
|
||||
};
|
||||
|
||||
uint8_t SPITxState; // transmitter state
|
||||
uint8_t SPIRxState; // receiver state
|
||||
|
||||
// frame queue
|
||||
FRAME_QUEUE spiTxQueue; // queue for outbound
|
||||
|
||||
// frame buffers
|
||||
#if USE_BUFFER_RAM
|
||||
static SPI_BUFFER spiTxBuffer __attribute__((section("BUFFERS"), aligned(4)));
|
||||
static SPI_BUFFER spiRxBuffer __attribute__((section("BUFFERS"), aligned(4)));
|
||||
static uint8_t rxFrameBuffer[PAYLOAD_MAX] __attribute__((section("BUFFERS"), aligned(4)));
|
||||
#else
|
||||
static SPI_BUFFER spiTxBuffer;
|
||||
static SPI_BUFFER spiRxBuffer;
|
||||
static uint8_t rxFrameBuffer[PAYLOAD_MAX];
|
||||
#endif
|
||||
|
||||
uint8_t SPI_State; // current state
|
||||
extern SPI_HandleTypeDef hspi1; // spi handle
|
||||
HAL_StatusTypeDef spiXfer; // last transfer status
|
||||
BOOL spiExchangeComplete; // spi exchange has been completed
|
||||
BOOL spiErrorOccurred;
|
||||
BOOL spiActive;
|
||||
uint16_t spiActivityTimer; // no activity timer
|
||||
|
||||
// validate an inbound frame
|
||||
BOOL isIP400Frame(uint8_t *eye);
|
||||
|
||||
/*
|
||||
* place a frame on the queue frame content is copied
|
||||
* to alloc'd memory
|
||||
*/
|
||||
BOOL EnqueSPIFrame(void *ip400frame)
|
||||
{
|
||||
IP400_FRAME *qFrame, *SrcFrame = (IP400_FRAME *)ip400frame;
|
||||
uint8_t *frameBuffer;
|
||||
|
||||
// spi is not running...
|
||||
if(!spiActive)
|
||||
return FALSE;
|
||||
|
||||
// allocate an IP400 frame
|
||||
if((qFrame=malloc(sizeof(IP400_FRAME)))== NULL)
|
||||
return FALSE;
|
||||
memcpy(qFrame, SrcFrame, sizeof(IP400_FRAME));
|
||||
|
||||
int16_t hopCount = qFrame->flagfld.flags.hop_count;
|
||||
int16_t hTblSize = hopCount * sizeof(HOPTABLE);
|
||||
|
||||
// hop table is in the first part of the payload
|
||||
// alloc enough for both
|
||||
if((frameBuffer=malloc(SrcFrame->length+hTblSize)) == NULL) {
|
||||
free(qFrame);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if(hopCount) {
|
||||
memcpy(frameBuffer, qFrame->hopTable, hTblSize);
|
||||
frameBuffer += hTblSize;
|
||||
}
|
||||
|
||||
memcpy(frameBuffer, (uint8_t *)SrcFrame->buf, SrcFrame->length);
|
||||
qFrame->buf = frameBuffer;
|
||||
qFrame->length = SrcFrame->length + hTblSize;
|
||||
|
||||
if(!enqueFrame(&spiTxQueue, qFrame))
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/*
|
||||
* clean up any queued frames
|
||||
*/
|
||||
void EmptySPIFrameQ(void)
|
||||
{
|
||||
while(dequeFrame(&spiTxQueue) != NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize the task
|
||||
*/
|
||||
void SPI_Task_init(void)
|
||||
{
|
||||
// create the rx completed semaphore
|
||||
spiExchangeComplete = FALSE;
|
||||
spiErrorOccurred = FALSE;
|
||||
|
||||
spiTxBuffer.spiData.hdr.eye[0] = 'I';
|
||||
spiTxBuffer.spiData.hdr.eye[1] = 'P';
|
||||
spiTxBuffer.spiData.hdr.eye[2] = '4';
|
||||
spiTxBuffer.spiData.hdr.eye[3] = 'C';
|
||||
|
||||
spiTxBuffer.spiData.hdr.status = NO_DATA;
|
||||
spiRxBuffer.spiData.hdr.status = NO_DATA;
|
||||
|
||||
SPITxState = SPITXIDLE;
|
||||
SPIRxState = SPIRXIDLE;
|
||||
|
||||
// tx (outbound) frame queue
|
||||
spiTxQueue.q_forw = &spiTxQueue;
|
||||
spiTxQueue.q_back = &spiTxQueue;
|
||||
|
||||
spiActive = FALSE; // no activity yet
|
||||
spiActivityTimer = 0;
|
||||
|
||||
// start the ball rolling..
|
||||
if((spiXfer = HAL_SPI_TransmitReceive_DMA(&hspi1, spiTxBuffer.rawData, spiRxBuffer.rawData, SPI_RAW_LEN)) != HAL_OK)
|
||||
spiErrorOccurred = TRUE;
|
||||
|
||||
}
|
||||
|
||||
// execute the task
|
||||
void SPI_Task_Exec(void)
|
||||
{
|
||||
static IP400_FRAME *txFrame;
|
||||
static uint16_t txSegLength, txPayloadRoom, hopSize;
|
||||
static uint8_t *prxData, *ptxData, nHops;
|
||||
static uint16_t rxSegLen;
|
||||
|
||||
// check the status first: repost Rx if an error occurred and it is now ready
|
||||
if(spiErrorOccurred) {
|
||||
if(hspi1.State == HAL_SPI_STATE_READY) {
|
||||
if((spiXfer = HAL_SPI_TransmitReceive_DMA(&hspi1, spiTxBuffer.rawData, spiRxBuffer.rawData, SPI_RAW_LEN)) == HAL_OK) {
|
||||
spiErrorOccurred = FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Here we wait for an exchange to be completed
|
||||
* If there is no activity for NO_SPI_TIMEOUT, then
|
||||
* the other end is probably dead, so clean up
|
||||
* any pending frames
|
||||
*/
|
||||
if(!spiExchangeComplete) {
|
||||
spiActivityTimer += 1;
|
||||
if(spiActivityTimer >= NO_SPI_TIMEOUT) {
|
||||
EmptySPIFrameQ();
|
||||
spiActive = FALSE;
|
||||
spiActivityTimer = 0;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
spiExchangeComplete = FALSE; // reset exchange done
|
||||
spiActive = TRUE; // indicate that the SPI is active..
|
||||
spiActivityTimer = 0; // reset no activity timer
|
||||
|
||||
// scope trigger on nucleo board
|
||||
#if _BOARD_TYPE==NUCLEO_BOARD // board type in use
|
||||
HAL_GPIO_TogglePin(SCOPE_GPIO_Port, SCOPE_Pin);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* process an outbound frame
|
||||
* Fragment it if longer that 500 bytes
|
||||
*/
|
||||
switch(SPITxState) {
|
||||
|
||||
case SPITXIDLE:
|
||||
if((txFrame=dequeFrame(&spiTxQueue)) == NULL) {
|
||||
spiTxBuffer.spiData.hdr.status = NO_DATA;
|
||||
break;
|
||||
}
|
||||
txSegLength = txFrame->length;
|
||||
nHops = txFrame->flagfld.flags.hop_count;
|
||||
hopSize = (uint16_t)nHops * sizeof(HOPTABLE);
|
||||
txPayloadRoom = SPI_BUFFER_LEN - hopSize;
|
||||
spiTxBuffer.spiData.hdr.status = SINGLE_FRAME;
|
||||
|
||||
// fragment the frame if needed
|
||||
if(txFrame->length > txPayloadRoom) {
|
||||
txSegLength = txPayloadRoom;
|
||||
spiTxBuffer.spiData.hdr.status = FRAGMENT;
|
||||
SPITxState = SPITXFRAG;
|
||||
}
|
||||
|
||||
// copy the address fields
|
||||
memcpy(&spiTxBuffer.spiData.hdr.fromCall, txFrame->source.callbytes.bytes, N_CALL);
|
||||
spiTxBuffer.spiData.hdr.fromPort[0] = (uint8_t)(txFrame->source.port << 8);
|
||||
spiTxBuffer.spiData.hdr.fromPort[1] = (uint8_t)(txFrame->source.port & 0xff);
|
||||
memcpy(spiTxBuffer.spiData.hdr.toCall, txFrame->dest.callbytes.bytes, N_CALL);
|
||||
spiTxBuffer.spiData.hdr.toPort[0] = (uint8_t)(txFrame->dest.port << 8);
|
||||
spiTxBuffer.spiData.hdr.toPort[1] = (uint8_t)(txFrame->dest.port & 0xff);
|
||||
|
||||
// flag fields
|
||||
spiTxBuffer.spiData.hdr.coding = txFrame->flagfld.flags.coding;
|
||||
spiTxBuffer.spiData.hdr.hopCount = txFrame->flagfld.flags.hop_count;
|
||||
spiTxBuffer.spiData.hdr.flags = (uint8_t)((txFrame->flagfld.allflags >> 8) & 0xFF);
|
||||
|
||||
ptxData = spiTxBuffer.spiData.buffer;
|
||||
|
||||
// hop table
|
||||
if(txFrame->flagfld.flags.hoptable) {
|
||||
memcpy(ptxData, txFrame->hopTable, hopSize);
|
||||
ptxData += hopSize;
|
||||
free(txFrame->hopTable);
|
||||
}
|
||||
|
||||
// and now the data
|
||||
memcpy(ptxData, txFrame->buf, txSegLength);
|
||||
spiTxBuffer.spiData.hdr.offset_hi = spiTxBuffer.spiData.hdr.offset_lo = 0;
|
||||
spiTxBuffer.spiData.hdr.length_hi = ((txSegLength + hopSize) >> 8);
|
||||
spiTxBuffer.spiData.hdr.length_lo = ((txSegLength + hopSize) & 0xFF);
|
||||
|
||||
// release memory if only a single frame
|
||||
if(spiTxBuffer.spiData.hdr.status == SINGLE_FRAME)
|
||||
{
|
||||
free(txFrame->buf);
|
||||
free(txFrame);
|
||||
}
|
||||
break;
|
||||
|
||||
case SPITXFRAG:
|
||||
uint16_t offset = ((uint16_t)(spiTxBuffer.spiData.hdr.offset_hi) << 8) + (uint16_t)spiTxBuffer.spiData.hdr.offset_lo;
|
||||
uint16_t prevLen = ((uint16_t)(spiTxBuffer.spiData.hdr.length_hi) << 8) + (uint16_t)spiTxBuffer.spiData.hdr.length_lo;
|
||||
offset += prevLen;
|
||||
txFrame->length -= prevLen;
|
||||
txSegLength = txFrame->length;
|
||||
if(txFrame->length > SPI_BUFFER_LEN) {
|
||||
spiTxBuffer.spiData.hdr.status = FRAGMENT;
|
||||
txSegLength = SPI_BUFFER_LEN;
|
||||
} else {
|
||||
spiTxBuffer.spiData.hdr.status = LAST_FRAGMENT;
|
||||
SPITxState = SPITXIDLE;
|
||||
}
|
||||
|
||||
// send the next fragment
|
||||
void *fragAddr = txFrame->buf + offset;
|
||||
memcpy(spiTxBuffer.spiData.buffer, fragAddr, txSegLength);
|
||||
spiTxBuffer.spiData.hdr.offset_hi = (offset >> 8);
|
||||
spiTxBuffer.spiData.hdr.offset_lo = (offset & 0xff);
|
||||
spiTxBuffer.spiData.hdr.length_hi = (txSegLength >> 8);
|
||||
spiTxBuffer.spiData.hdr.length_lo = (txSegLength & 0xFF);
|
||||
|
||||
// done with frame
|
||||
if(spiTxBuffer.spiData.hdr.status == LAST_FRAGMENT)
|
||||
{
|
||||
free(txFrame->buf);
|
||||
free(txFrame);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Inbound frame. Reassemble fragments if needed...
|
||||
*/
|
||||
|
||||
switch(SPIRxState) {
|
||||
|
||||
case SPIRXIDLE:
|
||||
spiFrameStatus rstat = spiRxBuffer.spiData.hdr.status;
|
||||
if((rstat == NO_DATA) || (rstat >= NUM_STATS))
|
||||
break;
|
||||
|
||||
if(!isIP400Frame(spiRxBuffer.spiData.hdr.eye))
|
||||
break;
|
||||
|
||||
rxSegLen = (spiRxBuffer.spiData.hdr.length_hi << 8) + spiRxBuffer.spiData.hdr.length_lo;
|
||||
if(rstat != SINGLE_FRAME) {
|
||||
prxData = rxFrameBuffer + (spiRxBuffer.spiData.hdr.offset_hi << 8) + spiRxBuffer.spiData.hdr.offset_lo;
|
||||
memcpy(prxData, spiRxBuffer.spiData.buffer, rxSegLen);
|
||||
SPIRxState = SPIRXFRAG;
|
||||
} else {
|
||||
SendSPIFrame(&spiRxBuffer.spiData.hdr, spiRxBuffer.spiData.buffer, rxSegLen);
|
||||
}
|
||||
break;
|
||||
|
||||
case SPIRXFRAG:
|
||||
uint8_t fragStat = spiRxBuffer.spiData.hdr.status;
|
||||
uint16_t offset = (spiTxBuffer.spiData.hdr.offset_hi << 8) + spiTxBuffer.spiData.hdr.offset_lo;
|
||||
prxData = rxFrameBuffer + offset;
|
||||
rxSegLen = (spiRxBuffer.spiData.hdr.length_hi << 8) + spiRxBuffer.spiData.hdr.length_lo;
|
||||
memcpy(prxData, spiRxBuffer.spiData.buffer, rxSegLen);
|
||||
|
||||
if(fragStat == LAST_FRAGMENT) {
|
||||
uint16_t frameLen = offset + rxSegLen;
|
||||
spiRxBuffer.spiData.hdr.length_hi = (frameLen >> 8);
|
||||
spiRxBuffer.spiData.hdr.length_lo = (frameLen & 0xFF);
|
||||
// process frame for tx here...
|
||||
SPIRxState = SPIRXIDLE; // placeholder
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// rx done callback
|
||||
void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef * hspi)
|
||||
{
|
||||
// set data valid and start a new transfer
|
||||
if(hspi->State == HAL_SPI_STATE_READY) {
|
||||
spiExchangeComplete = TRUE;
|
||||
spiXfer = HAL_SPI_TransmitReceive_DMA(&hspi1, spiTxBuffer.rawData, spiRxBuffer.rawData, SPI_RAW_LEN);
|
||||
if(spiXfer != HAL_OK)
|
||||
spiErrorOccurred = TRUE;
|
||||
return;
|
||||
}
|
||||
spiErrorOccurred = TRUE; // spi not ready
|
||||
}
|
||||
|
||||
// test an incoming frame
|
||||
BOOL isIP400Frame(uint8_t *eye)
|
||||
{
|
||||
if((eye[0] != 'I') || (eye[1] != 'P'))
|
||||
return FALSE;
|
||||
|
||||
if((eye[2] != '4') || (eye[3] != 'C'))
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
80
Node Firmware/IP400/Src/tod.c
Normal file
80
Node Firmware/IP400/Src/tod.c
Normal file
|
@ -0,0 +1,80 @@
|
|||
/*---------------------------------------------------------------------------
|
||||
Project: WL33_NUCLEO_UART
|
||||
|
||||
File Name: tod.c
|
||||
|
||||
Author: MartinA
|
||||
|
||||
Description: Time of Day clock
|
||||
|
||||
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 "types.h"
|
||||
#include "tod.h"
|
||||
#include "utils.h"
|
||||
|
||||
TIMEOFDAY tod = { 0, 0, 0 }; // where we hold the TOD
|
||||
|
||||
// routines
|
||||
void TOD_10SecTimer(void) // 10 second timer
|
||||
{
|
||||
// handle seconds
|
||||
tod.Seconds += 10;
|
||||
if(tod.Seconds >= 60) {
|
||||
tod.Minutes += tod.Seconds/60;
|
||||
tod.Seconds %= 60;
|
||||
}
|
||||
|
||||
// handle minutes
|
||||
if(tod.Minutes >= 60) {
|
||||
tod.Hours += tod.Minutes/60;
|
||||
tod.Minutes %= 60;
|
||||
}
|
||||
|
||||
// handle hours
|
||||
if(tod.Hours > 24)
|
||||
tod.Hours %= 24;
|
||||
}
|
||||
|
||||
// return pointer to Time of Day
|
||||
void getTOD(TIMEOFDAY *time)
|
||||
{
|
||||
time->Hours = tod.Hours;
|
||||
time->Minutes = tod.Minutes;
|
||||
time->Seconds = tod.Seconds;
|
||||
}
|
||||
|
||||
// set the TOD from HH:MM string
|
||||
// somewhat brute force parser
|
||||
BOOL setTOD(char *todString)
|
||||
{
|
||||
char *todValues[5];
|
||||
|
||||
int nParams = explode_string(todString, todValues, 5, ':', '"');
|
||||
if(nParams != 2)
|
||||
return FALSE;
|
||||
|
||||
int nHours = ascii2Dec(todValues[0]);
|
||||
if((nHours < 0) || (nHours > 24))
|
||||
return FALSE;
|
||||
|
||||
int nMins = ascii2Dec(todValues[1]);
|
||||
if((nHours < 0) || (nHours > 60))
|
||||
return FALSE;
|
||||
|
||||
tod.Hours = nHours;
|
||||
tod.Minutes = nMins;
|
||||
tod.Seconds = 0;
|
||||
|
||||
return TRUE;
|
||||
}
|
300
Node Firmware/IP400/Src/usart.c
Normal file
300
Node Firmware/IP400/Src/usart.c
Normal file
|
@ -0,0 +1,300 @@
|
|||
/*---------------------------------------------------------------------------
|
||||
Project: IP400 Modem
|
||||
|
||||
File Name: usart.c
|
||||
|
||||
Author: Martin C. Alcock, VE6VH
|
||||
|
||||
Revision: 1.05
|
||||
|
||||
Description: API for USART handling
|
||||
|
||||
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 <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#include <cmsis_os2.h>
|
||||
#include <FreeRTOS.h>
|
||||
#include <semphr.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "stream_buffer.h"
|
||||
#include "types.h"
|
||||
#include "streambuffer.h"
|
||||
#include "usart.h"
|
||||
|
||||
// local defines
|
||||
#define RX_TIMEOUT 10000 // 10 second rx timeout
|
||||
#define TX_TIMEOUT 1000 // 1s transmit timeout
|
||||
|
||||
// stream buffer
|
||||
StreamBufferHandle_t USART_RxBuffer; // handle to buffer
|
||||
StaticStreamBuffer_t USART_StreamBuffer;
|
||||
|
||||
// UART/T Support
|
||||
SemaphoreHandle_t txCompleted; // tx completed semaphore
|
||||
static DATA_ELEMENT USARTRxChar[10]; // last received character
|
||||
|
||||
uint8_t usart_data[bufferSIZE];
|
||||
char usartPrintBuffer[200];
|
||||
|
||||
// fwd refs here...
|
||||
void USART_Receive_char(void);
|
||||
|
||||
#if __ENABLE_GPS
|
||||
#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];
|
||||
#endif
|
||||
|
||||
/*
|
||||
* API for the rx data buffer
|
||||
*/
|
||||
void USART_API_init(void)
|
||||
{
|
||||
// create the tx completed semaphore
|
||||
txCompleted = xSemaphoreCreateBinary();
|
||||
|
||||
// start USART
|
||||
USART_RxBuffer = xStreamBufferCreateStatic(bufferSIZE, 1, usart_data, &USART_StreamBuffer);
|
||||
USART_RxBuffer_reset();
|
||||
USART_Receive_char();
|
||||
|
||||
#if __ENABLE_GPS
|
||||
LPUART_RxBuffer = xStreamBufferCreateStatic(bufferSIZE, 1, gps_data, &LPUART_StreamBuffer);
|
||||
LPUART_RxBuffer_reset();
|
||||
LPUART_Receive_char();
|
||||
#endif
|
||||
}
|
||||
|
||||
// reset the data buffer
|
||||
void USART_RxBuffer_reset(void)
|
||||
{
|
||||
xStreamBufferReset(USART_RxBuffer);
|
||||
return;
|
||||
}
|
||||
|
||||
#if __ENABLE_GPS
|
||||
// reset the LPUART Buffer
|
||||
void LPUART_RxBuffer_reset(void)
|
||||
{
|
||||
xStreamBufferReset(LPUART_RxBuffer);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
// return the number of byte in the buffer
|
||||
size_t databuffer_bytesInBuffer(void)
|
||||
{
|
||||
size_t nBytes = xStreamBufferBytesAvailable(USART_RxBuffer);
|
||||
return nBytes;
|
||||
}
|
||||
|
||||
// get a byte from the buffer: blocks if timeout is zero,
|
||||
// else returns BUFFER_NO_DATA if timeout exceeded
|
||||
DATA_ELEMENT databuffer_get(UART_TIMEOUT_T timeout)
|
||||
{
|
||||
DATA_ELEMENT retval;
|
||||
|
||||
TickType_t tickTimeout;
|
||||
|
||||
if(timeout == 0)
|
||||
tickTimeout = portMAX_DELAY;
|
||||
else
|
||||
tickTimeout = pdMS_TO_TICKS(timeout);
|
||||
|
||||
if(xStreamBufferReceive(USART_RxBuffer, &retval, 1, tickTimeout) == 0)
|
||||
return BUFFER_NO_DATA;
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*
|
||||
* Similar but from GPS buffer
|
||||
*/
|
||||
#if __ENABLE_GPS
|
||||
// return the number of byte in the buffer
|
||||
size_t gpsbuffer_bytesInBuffer(void)
|
||||
{
|
||||
size_t nBytes = xStreamBufferBytesAvailable(LPUART_RxBuffer);
|
||||
return nBytes;
|
||||
}
|
||||
// get a bytes
|
||||
DATA_ELEMENT gpsbuffer_get(UART_TIMEOUT_T timeout)
|
||||
{
|
||||
DATA_ELEMENT retval;
|
||||
|
||||
TickType_t tickTimeout;
|
||||
|
||||
if(timeout == 0)
|
||||
tickTimeout = portMAX_DELAY;
|
||||
else
|
||||
tickTimeout = pdMS_TO_TICKS(timeout);
|
||||
|
||||
if(xStreamBufferReceive(LPUART_RxBuffer, &retval, 1, tickTimeout) == 0)
|
||||
return BUFFER_NO_DATA;
|
||||
|
||||
return retval;
|
||||
}
|
||||
#endif
|
||||
|
||||
// see if the buffer contains a keyword, save data up to it if needed
|
||||
BOOL databuffer_contains(const char *tag, UART_TIMEOUT_T rx_timeout, BOOL saveData, char *SaveBuffer)
|
||||
{
|
||||
DATA_ELEMENT c;
|
||||
|
||||
uint8_t tagSize = (uint8_t)(strlen(tag) & 0xff);
|
||||
uint8_t tagLen = tagSize;
|
||||
|
||||
const char *tagAddr = tag;
|
||||
|
||||
while ((c = databuffer_get(rx_timeout)) != BUFFER_NO_DATA) {
|
||||
if (c == *tagAddr) {
|
||||
if (--tagLen == 0) {
|
||||
if(saveData) {
|
||||
*SaveBuffer++ = c;
|
||||
*SaveBuffer = '\0';
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
tagAddr++;
|
||||
}
|
||||
else {
|
||||
tagAddr = tag;
|
||||
tagLen = tagSize;
|
||||
}
|
||||
if(saveData)
|
||||
*SaveBuffer++ = c;
|
||||
}
|
||||
if(saveData)
|
||||
*SaveBuffer = '\0';
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Low level HAL interaction functions
|
||||
*/
|
||||
|
||||
// send a packet (with timeout)
|
||||
BOOL USART_Send_String(const char *string, size_t len)
|
||||
{
|
||||
// send using DMA
|
||||
uint16_t dataLen = (uint16_t)len;
|
||||
if((HAL_UART_Transmit_DMA(&huart1, (const uint8_t *)string, dataLen)) != HAL_OK)
|
||||
return FALSE;
|
||||
|
||||
// wait for completion..
|
||||
if((xSemaphoreTake(txCompleted, pdMS_TO_TICKS(TX_TIMEOUT))) == pdTRUE)
|
||||
return TRUE;
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// send a packet (with timeout)
|
||||
BOOL USART_Send_Char(const char c)
|
||||
{
|
||||
|
||||
// send using interrupt
|
||||
HAL_UART_Transmit_IT(&huart1, (const uint8_t *)&c, 1);
|
||||
|
||||
// wait for completion..
|
||||
if((xSemaphoreTake(txCompleted, pdMS_TO_TICKS(TX_TIMEOUT))) == pdTRUE)
|
||||
return TRUE;
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// print a string to the UAR/T
|
||||
void USART_Print_string(char *format, ...)
|
||||
{
|
||||
// process the arg list
|
||||
va_list argptr;
|
||||
va_start(argptr, format);
|
||||
vsprintf(usartPrintBuffer,format, argptr);
|
||||
va_end(argptr);
|
||||
|
||||
USART_Send_String(usartPrintBuffer, strlen(usartPrintBuffer));
|
||||
}
|
||||
|
||||
|
||||
// Transmit completed: trigger semaphore
|
||||
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
|
||||
{
|
||||
#if __ENABLE_GPS
|
||||
// Not interested in LPUART
|
||||
if(huart->Instance == LPUART1)
|
||||
return;
|
||||
#endif
|
||||
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
|
||||
xSemaphoreGiveFromISR(txCompleted, &xHigherPriorityTaskWoken);
|
||||
|
||||
}
|
||||
|
||||
// receive a byte with DMA, wait for DMA completed interrupt
|
||||
void USART_Receive_char(void)
|
||||
{
|
||||
HAL_UART_Receive_IT(&huart1,(uint8_t *)USARTRxChar,1);
|
||||
}
|
||||
|
||||
|
||||
// callback function when rx is completed: overrides previous
|
||||
// __weak definition
|
||||
// NB: Console USART and GPS LPUART share the same HAL routine
|
||||
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
|
||||
{
|
||||
#if __ENABLE_GPS
|
||||
// service the LPUART
|
||||
if(huart->Instance == LPUART1) {
|
||||
xStreamBufferSendFromISR(LPUART_RxBuffer, LPUARTRxChar, 1, NULL);
|
||||
HAL_UART_Receive_IT(&hlpuart1,(uint8_t *)LPUARTRxChar,1);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
// service the USART
|
||||
xStreamBufferSendFromISR(USART_RxBuffer, USARTRxChar, 1, NULL);
|
||||
HAL_UART_Receive_IT(&huart1,(uint8_t *)USARTRxChar,1);
|
||||
}
|
||||
|
||||
#if __ENABLE_GPS
|
||||
// send a string to the LPUART
|
||||
void LPUART_Send_String(char *str, uint16_t len)
|
||||
{
|
||||
// send using interrupt
|
||||
HAL_UART_Transmit_IT(&hlpuart1, (const uint8_t *)str, len);
|
||||
}
|
||||
|
||||
|
||||
// receive a byte with DMA, wait for DMA completed interrupt
|
||||
void LPUART_Receive_char(void)
|
||||
{
|
||||
HAL_UART_Receive_IT(&hlpuart1,(uint8_t *)LPUARTRxChar,1);
|
||||
// HAL_UARTEx_ReceiveToIdle_DMA(&hlpuart1,(uint8_t *)LPUARTRxChar,LPUART_DMA_SIZE);
|
||||
//__HAL_DMA_DISABLE_IT(&hlpuart1, DMA_IT_HT);
|
||||
}
|
||||
|
||||
|
||||
// callback from GPS Rx
|
||||
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
|
||||
{
|
||||
xStreamBufferSendFromISR(LPUART_RxBuffer, LPUARTRxChar, Size, NULL);
|
||||
HAL_UARTEx_ReceiveToIdle_DMA(&hlpuart1,(uint8_t *)LPUARTRxChar,LPUART_DMA_SIZE);
|
||||
}
|
||||
|
||||
#endif
|
146
Node Firmware/IP400/Src/utils.c
Normal file
146
Node Firmware/IP400/Src/utils.c
Normal file
|
@ -0,0 +1,146 @@
|
|||
/*---------------------------------------------------------------------------
|
||||
Project: WL33_NUCLEO_UART
|
||||
|
||||
File Name: utils.c
|
||||
|
||||
Author: MartinA
|
||||
|
||||
Description: utility routines
|
||||
|
||||
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 <math.h>
|
||||
|
||||
#include "types.h"
|
||||
#include "utils.h"
|
||||
|
||||
// ascii to decimal
|
||||
int ascii2Dec(char *dec)
|
||||
{
|
||||
int retval = 0;
|
||||
int sgn = 1;
|
||||
while (*dec) {
|
||||
if(*dec == '-')
|
||||
sgn = -1;
|
||||
else retval = retval*10 + (*dec - '0');
|
||||
dec++;
|
||||
}
|
||||
|
||||
return retval*sgn;
|
||||
}
|
||||
|
||||
// nibble to ascii
|
||||
char nib2ascii(uint8_t nib)
|
||||
{
|
||||
char anib = (char)(nib + '0');
|
||||
anib = (anib > '9') ? anib+('A'-'9') : anib;
|
||||
return anib;
|
||||
}
|
||||
|
||||
// hex to ascii
|
||||
void hex2ascii(uint8_t hex, char *buf)
|
||||
{
|
||||
*buf++= nib2ascii(hex>>4);
|
||||
*buf = nib2ascii(hex & 0xf);
|
||||
}
|
||||
|
||||
// see if an entry is floating point
|
||||
BOOL isfloat(char *val)
|
||||
{
|
||||
while(*val)
|
||||
if(*val++ == '.')
|
||||
return 1U;
|
||||
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
|
||||
//
|
||||
double ascii2double(char *val)
|
||||
{
|
||||
double retval = 0;
|
||||
int power = -1, inc = 1;
|
||||
int sgn = 1;
|
||||
|
||||
while(*val) {
|
||||
|
||||
switch(*val) {
|
||||
|
||||
case '-':
|
||||
sgn = -1;
|
||||
break;
|
||||
|
||||
case '.':
|
||||
power = -1;
|
||||
inc = -1;
|
||||
break;
|
||||
|
||||
default:
|
||||
if(inc > 0) {
|
||||
retval = retval*10 + (*val - '0');
|
||||
} else {
|
||||
retval += pow(10.0, power) * (*val - '0');
|
||||
power += inc;
|
||||
}
|
||||
break;
|
||||
}
|
||||
val++;
|
||||
}
|
||||
return retval * sgn;
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
int i,l,inquo;
|
||||
|
||||
inquo = 0;
|
||||
i = 0;
|
||||
strp[i++] = str;
|
||||
if (!*str)
|
||||
{
|
||||
strp[0] = 0;
|
||||
return(0);
|
||||
}
|
||||
for(l = 0; *str && (l < limit) ; str++)
|
||||
{
|
||||
if(quote)
|
||||
{
|
||||
if (*str == quote)
|
||||
{
|
||||
if (inquo)
|
||||
{
|
||||
*str = 0;
|
||||
inquo = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
strp[i - 1] = str + 1;
|
||||
inquo = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((*str == delim) && (!inquo))
|
||||
{
|
||||
*str = 0;
|
||||
l++;
|
||||
strp[i++] = str + 1;
|
||||
}
|
||||
}
|
||||
strp[i] = 0;
|
||||
return(i);
|
||||
|
||||
}
|
||||
|
BIN
Node Firmware/Platforms/NucleoCC2.zip
Normal file
BIN
Node Firmware/Platforms/NucleoCC2.zip
Normal file
Binary file not shown.
11
Node Firmware/Read.me
Normal file
11
Node Firmware/Read.me
Normal file
|
@ -0,0 +1,11 @@
|
|||
This directory contains the code sources. The current source code is at Rev level 1.0.
|
||||
|
||||
There are three sub-directories:
|
||||
Bin - precompiled binaries of the node code
|
||||
IP400 - current sources for the IP400 code
|
||||
Platforms - Zipped projects for the CubeIDE for different hardware platforms.
|
||||
|
||||
Currently supported platforms:
|
||||
Nucleo CC Experimenter node
|
||||
|
||||
Release notes are in the node software document in the documentation directory.
|
Loading…
Reference in a new issue