MR16/DevCBT6/User/module/mr16.c
2026-01-01 14:59:47 +08:00

2171 lines
104 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* @file mr16.c
* @brief MR16 Module Implementation - SX1281 Radio Control with FreeRTOS CLI
*
* ============================================================================
* CODE ORGANIZATION
* ============================================================================
* This file is organized into THREE main sections:
*
* [PART 1] MR16 CORE FUNCTIONS (核心功能)
* - System initialization and main loop
* - Data packet processing (Header + CRC)
* - Radio event callbacks
* - Failsafe protection logic
*
* [PART 2] UI DISPLAY FUNCTIONS (界面显示)
* - LCD power-on animation
* - Main status display
* - Dynamic status updates
*
* [PART 3] CLI COMMAND FUNCTIONS (命令行接口)
* - FreeRTOS CLI registration
* - MR16 system commands (save/load/mode/etc)
* - Radio parameter commands (modulation/packet/etc)
* - Help text and documentation
*
* ============================================================================
*
* @author MR16 Development Team
* @date 2024
* @version 2.0
*/
/* Includes ----------------------------------------------------------------- */
#include "module/mr16.h"
#include "module/config.h"
/* STM32 HAL Headers */
#include <main.h>
#include "usart.h"
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
/* Device Driver Headers */
#include "bsp/flash.h"
#include "bsp/time.h"
#include "device/sx1281_driver/sx1281.h"
#include "device/sx1281_driver/radio.h"
#include "device/sx1281_driver/sx1281_driver.h"
#include "device/lcd_driver/lcd.h"
#include "device/ws2812.h"
#include "device/ws2812.h"
/* Component Headers */
/* crc16.h removed - hardware CRC is sufficient, no software CRC needed */
/* FreeRTOS Headers */
#include "component/FreeRTOS_CLI.h"
/* Private defines --------------------------------------------------------- */
#define RXID_MIN 0x0001
#define RXID_MAX 0xFFFE
#define RXID_INVALID_LOW 0x0000
#define RXID_INVALID_HIGH 0xFFFF
#define PAYLOAD_MIN 1
/* 各模式最大用户数据长度(不含Header):
* FLRC=122, GFSK=250, LoRa=251
* 使用最保守的FLRC限制作为默认值 */
#define PAYLOAD_MAX_FLRC MR16_FLRC_MAX_FIXED_LEN // 122字节
#define PAYLOAD_MAX_GFSK MR16_GFSK_MAX_FIXED_LEN // 250字节
#define PAYLOAD_MAX_LORA MR16_LORA_MAX_FIXED_LEN // 251字节
#define TX_POWER_MIN -18
#define TX_POWER_MAX 13
/* Private macro ------------------------------------------------------------ */
/* Private typedef ---------------------------------------------------------- */
/* Private variables -------------------------------------------------------- */
/* Radio instance and configuration */
SX1281_t radio;
/* External references */
extern const unsigned char logo_M[]; /* LCD logo bitmap */
extern const unsigned char logo_R[]; /* LCD logo bitmap */
extern uint8_t uart2_data[255], uart2_datalength; /* UART data buffer */
extern volatile bool uart2_data_ready; /* UART data ready flag */
extern MR16_t mr16; /* MR16 main instance */
/* Radio communication buffers */
uint8_t radioRXBuffer[BUFFER_SIZE];
uint8_t radioRXSize;
/* CLI buffers - Static allocation to prevent stack overflow */
static char cInputBuffer[64]; /* CLI input buffer */
static char cOutputBuffer[256]; /* CLI output buffer */
/* Private function prototypes ---------------------------------------------- */
int8_t MR16_CLI_Init(void);
static BaseType_t mr16CommandHandler(char *pcWriteBuffer, size_t xWriteBufferLen, const char *pcCommandString);
static BaseType_t radioCommandHandler(char *pcWriteBuffer, size_t xWriteBufferLen, const char *pcCommandString);
static BaseType_t outputHelpText(char *pcWriteBuffer, size_t xWriteBufferLen,
const char *helpText, size_t *position);
static void copyCLIArg(const char *param, BaseType_t len, char *output, size_t outputSize);
static const char* getFormatString(MR16_DataFormat_t format);
static const char* getRadioModeString(SX1281_RadioMode_t mode);
static bool isValidRXID(uint16_t rxid);
static int8_t setRXID(uint16_t *rxid, const char *value, char *response, size_t responseLen);
/* ========================================================================== */
/* ========================================================================== */
/* */
/* [PART 1] MR16 CORE FUNCTIONS */
/* (核心功能模块) */
/* */
/* ========================================================================== */
/* ========================================================================== */
/*----------------------------------------------------------------------------
System Initialization & Main Loop (系统初始化与主循环)
*---------------------------------------------------------------------------*/
/**
* @brief 初始化MR16系统(智能配置加载)
* @param mr16 MR16实例指针
* @param param 参数配置指针(将被配置覆盖)
* @return 0-成功, -1-失败
*
* 完整的初始化流程:
* 1. 从Flash智能加载配置(无效则使用默认值)
* 2. 初始化CLI系统
* 3. 使用加载的参数初始化SX1281无线模块
* 4. 初始化LCD显示并启动UI
*/
int8_t MR16_Init(MR16_t *mr16, MR16_Param_t *param) {
if (!mr16 || !param) {
return -1; /* Invalid parameters */
}
/* Smart config loading: try Flash first, fallback to defaults */
if (!Config_LoadMR16FromFlashBool()) {
/* Flash invalid or corrupt, use safe defaults */
Config_SetDefaults();
printf("[MR16] Flash config invalid, using defaults\r\n");
} else {
printf("[MR16] Config loaded from Flash successfully\r\n");
}
memset(&mr16->timer, 0, sizeof(MR16_Timer_t));
/* Initialize CLI system */
MR16_CLI_Init();
/* Initialize MR16 parameters - config should be loaded before calling this function */
mr16->param = &Config_Get()->mr16;
/* Initialize SX1281 radio using persistent parameters */
if (SX1281_Init(&radio, &mr16->param->radioParams, mr16->param->radioParams.radioMode) != DEVICE_OK) {
return -1; /* Radio initialization failed */
}
Radio.SetStandby( STDBY_RC );
switch (mr16->param->format) {
case MR16_FORMAT_VARIABLE:
/* 可变长度模式: PayloadLength设置为最大允许接收长度
* 芯片会自动在包中添加Header(含长度字段),自动检测实际长度
* 这里设置的是"最大允许"值,用于接收时限制 */
if(mr16->param->radioParams.RadioRole==RadioRoleTX) {
radio.param->packetParams.Params.LoRa.PayloadLength = SX1281_LORA_MAX_PAYLOAD; // 253
radio.param->packetParams.Params.Flrc.PayloadLength = SX1281_FLRC_MAX_PAYLOAD; // 124
radio.param->packetParams.Params.Gfsk.PayloadLength = SX1281_GFSK_MAX_PAYLOAD; // 252
} else if(mr16->param->radioParams.RadioRole==RadioRoleRX) {
radio.param->packetParams.Params.LoRa.PayloadLength = MR16_HEADER_LEN + mr16->param->fixedLength;
radio.param->packetParams.Params.Flrc.PayloadLength = MR16_HEADER_LEN + mr16->param->fixedLength;
radio.param->packetParams.Params.Gfsk.PayloadLength = MR16_HEADER_LEN + mr16->param->fixedLength;
}
break;
case MR16_FORMAT_FIXED:
/* 固定长度模式: PayloadLength设置为精确的包长度
* 射频芯片不添加长度字段Header收发双方必须预知长度
* 实际长度 = Header(2) + 用户数据(fixedLength) */
radio.param->packetParams.Params.LoRa.PayloadLength = MR16_HEADER_LEN + mr16->param->fixedLength;
radio.param->packetParams.Params.Flrc.PayloadLength = MR16_HEADER_LEN + mr16->param->fixedLength;
radio.param->packetParams.Params.Gfsk.PayloadLength = MR16_HEADER_LEN + mr16->param->fixedLength;
break;
default:
break;
}
Radio.SetPacketParams( &mr16->param->radioParams.packetParams );//包属性
if (radio.param->RadioRole == RadioRoleRX) {
SX1281_SetRXSingle(&radio);
// SX1281_SetRXSuccessive(&radio);
} else if (radio.param->RadioRole == RadioRoleTX) {
SX1281_SetTX(&radio, mr16->txbuffer, 2);
}
/* Initialize LCD display and show UI */
LCD_Init(1);
MR16_UI_PowerON();
HAL_Delay(1500);
MR16_UI_Home(mr16);
return 0; /* Success */
}
/**
* @brief MR16主循环函数
* @param mr16 MR16实例指针
* @return 0-成功
*
* 主要功能:
* 1. 处理UART接收数据(自动区分CLI命令和用户数据)
* 2. 根据数据格式(VARIABLE/FIXED)执行相应操作
* 3. 运行SX1281无线通信
* 4. 定期更新LCD显示
*/
int8_t MR16_Main(MR16_t *mr16) {
bool isRCData = false;
/* Handle UART data */
if (uart2_data_ready) {
/* Distinguish RC data from CLI commands by checking first byte:
* CLI commands start with ASCII letters (a-z, A-Z)
* RC data is binary and won't start with letter
* Skip leading whitespace/newlines for CLI detection
*/
uint8_t firstCharIndex = 0;
/* Skip leading whitespace, \r, \n */
while (firstCharIndex < uart2_datalength &&
(uart2_data[firstCharIndex] == ' ' ||
uart2_data[firstCharIndex] == '\t' ||
uart2_data[firstCharIndex] == '\r' ||
uart2_data[firstCharIndex] == '\n')) {
firstCharIndex++;
}
if (firstCharIndex < uart2_datalength &&
((uart2_data[firstCharIndex] >= 'a' && uart2_data[firstCharIndex] <= 'z') ||
(uart2_data[firstCharIndex] >= 'A' && uart2_data[firstCharIndex] <= 'Z'))) {
/* Starts with letter - CLI command */
isRCData = false;
} else if (uart2_datalength > 0) {
isRCData = true;
}
if(!isRCData) {
/* CLI Mode: Process as command */
if (uart2_datalength > 0 && uart2_datalength < sizeof(cInputBuffer)) {
memcpy(cInputBuffer, uart2_data, uart2_datalength);
cInputBuffer[uart2_datalength] = '\0';
/* Remove trailing \r\n for better compatibility */
int len = uart2_datalength;
while (len > 0 && (cInputBuffer[len-1] == '\r' || cInputBuffer[len-1] == '\n')) {
cInputBuffer[--len] = '\0';
}
} else {
cInputBuffer[0] = '\0';
}
/* 暂停无线收发 */
Radio.SetStandby(STDBY_RC);
/* CLI命令处理: 保存原状态→DEBUG黄灯→处理→恢复 */
WS2812_Status_t savedStatus = WS2812_GetStatus();
WS2812_SetStatus(WS2812_STATUS_DEBUG); /* CLI调试: 黄灯闪烁 */
WS2812_NotifyCommSuccess(); /* 重置超时计时器 */
cOutputBuffer[0] = '\0';
BaseType_t xMore = pdTRUE;
do {
xMore = FreeRTOS_CLIProcessCommand(cInputBuffer, cOutputBuffer, sizeof(cOutputBuffer));
if (cOutputBuffer[0] != '\0') {
printf("%s", cOutputBuffer);
}
cOutputBuffer[0] = '\0';
} while (xMore == pdTRUE);
MR16_UI_Home(mr16); /* 刷新主界面 */
}
/* 数据处理完成,允许接收下一帧 */
uart2_data_ready = false;
}
mr16->timer.now = BSP_TIME_Get_us() / 1000000.0f;
mr16->timer.dt = (BSP_TIME_Get_us() - mr16->timer.last_wakeup) / 1000000.0f;
mr16->timer.last_wakeup = BSP_TIME_Get_us();
/* 打包数据 - RC data, pack with TX_ID header */
MR16_PackTxBuffer(mr16, uart2_data, uart2_datalength);
/* RX端失控保护: 若100ms未收到数据,构造安全帧 */
if (radio.param->RadioRole == RadioRoleRX) {
if (mr16->timer.now - mr16->timer.lastDataReceived > 0.1f) {
/* 构造安全帧: Header + [0xFF 0x00 0xFF 0x00] + [0x00...] */
mr16->txbuffer[0] = (uint8_t)(mr16->param->TX_ID >> 8); // Header高字节
mr16->txbuffer[1] = (uint8_t)(mr16->param->TX_ID & 0xFF); // Header低字节
mr16->txbuffer[2] = MR16_FAILSAFE_BYTE0; // 0xFF
mr16->txbuffer[3] = MR16_FAILSAFE_BYTE1; // 0x00
mr16->txbuffer[4] = MR16_FAILSAFE_BYTE2; // 0xFF
mr16->txbuffer[5] = MR16_FAILSAFE_BYTE3; // 0x00
/* 其余字节置0 */
uint16_t totalLen;
if (mr16->param->format == MR16_FORMAT_VARIABLE) {
totalLen = MR16_HEADER_LEN + uart2_datalength;
} else {
totalLen = MR16_HEADER_LEN + mr16->param->fixedLength;
}
for (uint16_t i = 6; i < totalLen; i++) {
mr16->txbuffer[i] = 0x00;
}
}
}
/* Handle MR16 data format operations */
switch (mr16->param->format) {
case MR16_FORMAT_VARIABLE:
switch (radio.param->RadioRole) {
case RadioRoleTX:
SX1281_Running(&radio);
break;
case RadioRoleRX:
SX1281_Running(&radio);
if(mr16->timer.now-mr16->timer.lastUIupdata>=1.0f) {
MR16_UI_UpdateStatus(mr16);
mr16->timer.lastUIupdata=mr16->timer.now;
}
break;
default:
break;
}
break;
case MR16_FORMAT_FIXED:
/* Fixed length mode */
switch (radio.param->RadioRole) {
case RadioRoleTX:
SX1281_Running(&radio);
break;
case RadioRoleRX:
SX1281_Running(&radio);
if(mr16->timer.now-mr16->timer.lastUIupdata>=1.0f) {
MR16_UI_UpdateStatus(mr16);
mr16->timer.lastUIupdata=mr16->timer.now;
}
break;
default:
break;
}
break;
default:
/* Idle mode */
break;
}
/* LED状态指示任务: 使用HAL_GetTick()获取当前时间(ms) */
WS2812_StatusTask(HAL_GetTick());
return 0;
}
/*----------------------------------------------------------------------------
Data Packet Processing (数据包处理)
*---------------------------------------------------------------------------*/
/**
* @brief 封装数据包到TX缓冲区(添加Header和CRC)
* @param mr16 MR16实例指针
* @param userData 用户纯数据指针
* @param userDataLen 用户数据长度
* @return 0-成功, -1-失败
*
* 数据包格式 (硬件CRC自动添加,无需软件计算):
* [0] Header高字节 (TX_ID >> 8)
* [1] Header低字节 (TX_ID & 0xFF)
* [2..N+1] 用户数据
* 硬件自动添加CRC (FLRC/GFSK: 3字节, LoRa: 2字节)
*/
int8_t MR16_PackTxBuffer(MR16_t *mr16, const uint8_t *userData, uint16_t userDataLen) {
if (!mr16 || !userData || userDataLen > MR16_GFSK_MAX_FIXED_LEN) {
return -1; /* Invalid parameters, max 250 bytes for GFSK/LoRa */
}
/* 添加Header (TX_ID) */
mr16->txbuffer[0] = (uint8_t)(mr16->param->TX_ID >> 8); /* High byte */
mr16->txbuffer[1] = (uint8_t)(mr16->param->TX_ID & 0xFF); /* Low byte */
/* 复制用户数据 */
memcpy(&mr16->txbuffer[2], userData, userDataLen);
/* 硬件CRC自动添加,无需软件计算 */
return 0;
}
/*----------------------------------------------------------------------------
Radio Event Callbacks (无线事件回调)
*---------------------------------------------------------------------------*/
/**
* @brief SX1281无线模块事件回调函数
* @param source 事件源状态
* @return 0-成功, -1-错误
*
* 处理无线收发事件:
* - RX_DONE: 接收成功,验证ID和CRC,输出数据
* - RX_ERROR: 接收失败,重新开启接收
* - TX_DONE: 发送成功,继续发送
* - TX_TIMEOUT: 发送超时
*/
int8_t SX1281_Callback(SX1281_States_t source) {
switch (source) {
case RX_DONE:
printf("\r\n[RX] <<< Packet Received >>>\r\n");
/* Extract payload from radio buffer */
radioRXSize = 0;
Radio.GetPayload(radioRXBuffer, &radioRXSize, BUFFER_SIZE);
if (radioRXSize < BUFFER_SIZE) {
radioRXBuffer[radioRXSize] = 0; /* Null terminate */
} else {
radioRXBuffer[BUFFER_SIZE - 1] = 0;
}
/* Parse header and update packet counters */
uint16_t RXheader = (((uint16_t)radioRXBuffer[0]) << 8) | (uint16_t)radioRXBuffer[1];
for (int i = 0; i < 3; i++) {
/* Skip disabled RX_ID slots - 用于扩展自动配对功能,保留 */
if (mr16.param->RX_ID[i] == RXID_INVALID_LOW || mr16.param->RX_ID[i] == RXID_INVALID_HIGH) {
continue;
}
if (RXheader == mr16.param->RX_ID[i]) {
mr16.packetCount[i]++;
mr16.timer.lastDataReceived = mr16.timer.now; /* 更新接收时间戳 */
/* 更新LED状态指示: 接收成功 */
WS2812_SetStatus(WS2812_STATUS_RX_OK);
WS2812_NotifyCommSuccess();
printf("[ch%d] Header: 0x%04X | Size: %d bytes | Count: %d\r\n",
i+1, RXheader, radioRXSize, mr16.packetCount[i]);
break;
}
}
break;
case RX_TIMEOUT:
/* 打印状态 */
printf( "\r\n[RX] ... Timeout (Restarting RX)\r\n" );
/* 超时设置ERROR状态,不重置通信时间戳 */
WS2812_SetStatus(WS2812_STATUS_ERROR);
SX1281_SetRXSingle(&radio);
// SX1281_SetRXSuccessive(&radio);
break;
case RX_ERROR:
/* 打印状态 */
printf( "\r\n[RX] !!! ERROR - Reception Failed !!!\r\n" );
/* 接收错误设置ERROR状态,不重置通信时间戳 */
WS2812_SetStatus(WS2812_STATUS_ERROR);
SX1281_SetRXSingle(&radio);
break;
case TX_DONE:
/* 打印状态 */
printf( "\r\n[TX] <<< Packet Sent Successfully >>>\r\n" );
/* 更新LED状态指示: 发送成功 */
WS2812_SetStatus(WS2812_STATUS_TX_OK);
WS2812_NotifyCommSuccess();
/* 根据数据格式重新发送 */
uint16_t txLen;
if (mr16.param->format == MR16_FORMAT_VARIABLE) {
/* */
txLen = MR16_HEADER_LEN + uart2_datalength + 10;
} else {
/* FIXED: Header(2) + 用户数据(fixedLength), 硬件CRC自动添加 */
txLen = MR16_HEADER_LEN + mr16.param->fixedLength;
}
printf( "[TX] Length: %d bytes\r\n", txLen );
SX1281_SetTX(&radio, mr16.txbuffer, txLen);
break;
case TX_TIMEOUT:
/* 打印状态 */
printf( "\r\n[TX] !!! TIMEOUT - Transmission Failed !!!\r\n" );
/* 发送超时设置ERROR状态,不重置通信时间戳 */
WS2812_SetStatus(WS2812_STATUS_ERROR);
/* 重新尝试发送 */
uint16_t retryLen;
if (mr16.param->format == MR16_FORMAT_VARIABLE) {
retryLen = uart2_datalength;
} else {
/* FIXED: Header(2) + 用户数据(fixedLength), 硬件CRC自动添加 */
retryLen = MR16_HEADER_LEN + mr16.param->fixedLength;
}
SX1281_SetTX(&radio, mr16.txbuffer, retryLen);
break;
case LORA_CAD_DONE:
/* 打印状态 */
printf( "\r\n[CAD] Channel Activity Detected\r\n" );
break;
default:
break;
}
return 0;
}
/* ========================================================================== */
/* ========================================================================== */
/* */
/* [PART 2] UI DISPLAY FUNCTIONS */
/* (界面显示模块) */
/* */
/* ========================================================================== */
/* ========================================================================== */
/**
* @brief 显示LCD开机动画
* @return 0-成功
*
* 显示"MR16"标志和版本信息
*/
int8_t MR16_UI_PowerON() {
LCD_Clear(BLACK);
LCD_DrawString(110, 119, "Initializing...", CYAN, 16, LSB);
LCD_DrawBitmap(logo_M, 0, 0, 48, 32, CRIMSON, MSB);
HAL_Delay(100);
LCD_DrawBitmap(logo_R, 48, 4, 28, 28, CRIMSON, MSB);
HAL_Delay(100);
LCD_DrawString(0, 48, "MR16--Wireless", CRIMSON, 24, LSB);
LCD_DrawString(0, 72, "transmission tool", CRIMSON, 24, LSB);
return 0;
}
/**
* @brief 显示LCD主界面
* @param mr16 MR16实例指针
* @return 0-成功, -1-失败
*
* 显示内容:
* - TX模式: TX ID和发射功率
* - RX模式: 三个通道的接收计数
* - 工作模式、无线协议、频率等信息
*/
int8_t MR16_UI_Home(MR16_t *mr16) {
if (!mr16 || !mr16->param) return -1;
LCD_Clear(BLACK);
/* Title */
LCD_DrawString(5, 5, "MR16 Status", CRIMSON, 24, LSB);
/* TX/RX Indicator in top right corner */
if (radio.appMode == APPMODE_TX) {
LCD_DrawString(200, 5, "TX", ORANGE, 24, LSB);
} else {
LCD_DrawString(200, 5, "RX", LIME, 24, LSB);
}
LCD_DrawLine(0, 30, 135, 30, GRAY);
/* Mode Display */
LCD_DrawString(5, 40, "Mode:", CYAN, 16, LSB);
const char* mode_str = "UNKNOWN";
uint16_t mode_color = WHITE;
switch(mr16->param->format) {
case MR16_FORMAT_VARIABLE:
mode_str = "VARIABLE";
mode_color = WHITE;
break;
case MR16_FORMAT_FIXED:
mode_str = "FIXED";
mode_color = LIME;
break;
default:
mode_str = "NONE";
mode_color = DARKGRAY;
break;
}
LCD_DrawString(50, 40, mode_str, mode_color, 16, LSB);
/* Radio Mode */
LCD_DrawString(5, 65, "Radio:", CYAN, 16, LSB);
const char* radio_mode = "NONE";
switch(radio.param->packetParams.PacketType) {
case PACKET_TYPE_BLE: radio_mode = "BLE"; break;
case PACKET_TYPE_GFSK: radio_mode = "GFSK"; break;
case PACKET_TYPE_LORA: radio_mode = "LORA"; break;
case PACKET_TYPE_FLRC: radio_mode = "FLRC"; break;
default: break;
}
LCD_DrawString(65, 65, radio_mode, WHITE, 16, LSB);
/* RF Frequency */
LCD_DrawString(5, 90, "Freq:", CYAN, 16, LSB);
char freq_buf[15];
snprintf(freq_buf, sizeof(freq_buf), "%lluMHz", radio.param->rfFrequency / 1000000);
LCD_DrawString(50, 90, freq_buf, WHITE, 16, LSB);
uint16_t yPos = 35;
/* TX Mode: Show TXID and Power */
if (radio.appMode == APPMODE_TX) {
LCD_DrawString(5, 115, "TXID:", CYAN, 16, LSB);
char id_buf[10];
snprintf(id_buf, sizeof(id_buf), "0x%04X", mr16->param->TX_ID);
LCD_DrawString(65, 115, id_buf, WHITE, 16, LSB);
yPos += 25;
LCD_DrawString(120, 40, "Power:", CYAN, 16, LSB);
char pwr_buf[10];
snprintf(pwr_buf, sizeof(pwr_buf), "%ddBm", radio.param->txOutputPower);
LCD_DrawString(180, 40, pwr_buf, ORANGE, 16, LSB);
yPos += 25;
/* 如果是固定长度模式,显示数据长度 */
if (mr16->param->format == MR16_FORMAT_FIXED) {
LCD_DrawString(120, 65, "DataLen:", CYAN, 16, LSB);
char len_buf[10];
snprintf(len_buf, sizeof(len_buf), "%dB", mr16->param->fixedLength);
LCD_DrawString(190, 65, len_buf, ORANGE, 16, LSB);
}
}
/* RX Mode: Show packet counts per channel */
else {
/* RX Count Label */
LCD_DrawString(125, yPos, "rxcount:", CYAN, 16, LSB);
yPos += 20;
/* Channel counts */
char ch_buf[20];
snprintf(ch_buf, sizeof(ch_buf), "ch1:%lu", mr16->packetCount[0]);
LCD_DrawString(120, yPos, ch_buf, LIME, 16, LSB);
yPos += 20;
snprintf(ch_buf, sizeof(ch_buf), "ch2:%lu", mr16->packetCount[1]);
LCD_DrawString(120, yPos, ch_buf, LIME, 16, LSB);
yPos += 20;
snprintf(ch_buf, sizeof(ch_buf), "ch3:%lu", mr16->packetCount[2]);
LCD_DrawString(120, yPos, ch_buf, LIME, 16, LSB);
/* RX ID Label */
LCD_DrawString(5, 115, "rxid:", CYAN, 16, LSB);
snprintf(ch_buf, sizeof(ch_buf), "0x%04X", mr16->param->RX_ID[0]);
LCD_DrawString(65, 115, ch_buf, WHITE, 16, LSB);
snprintf(ch_buf, sizeof(ch_buf), "0x%04X", mr16->param->RX_ID[1]);
LCD_DrawString(125, 115, ch_buf, WHITE, 16, LSB);
snprintf(ch_buf, sizeof(ch_buf), "0x%04X", mr16->param->RX_ID[2]);
LCD_DrawString(185, 115, ch_buf, WHITE, 16, LSB);
}
return 0;
}
/**
* @brief 更新LCD屏幕上的动态状态显示
* @param mr16 MR16实例指针
* @return 0-成功, -1-失败
*
* 仅在RX模式下更新通道接收计数
*/
int8_t MR16_UI_UpdateStatus(MR16_t *mr16) {
if (!mr16) return -1;
/* Only update in RX mode */
if (radio.appMode == APPMODE_RXSUCCESSIVE) {
/* Clear and update channel counts */
uint16_t yPos = 35;
char ch_buf[20];
yPos += 20;
/* ch1 - use background color for auto-clear */
snprintf(ch_buf, sizeof(ch_buf), "ch1:%lu", mr16->packetCount[0]);
LCD_DrawStringBG(120, yPos, ch_buf, LIME, BLACK, 16, LSB);
yPos += 20;
/* ch2 */
snprintf(ch_buf, sizeof(ch_buf), "ch2:%lu", mr16->packetCount[1]);
LCD_DrawStringBG(120, yPos, ch_buf, LIME, BLACK, 16, LSB);
yPos += 20;
/* ch3 */
snprintf(ch_buf, sizeof(ch_buf), "ch3:%lu", mr16->packetCount[2]);
LCD_DrawStringBG(120, yPos, ch_buf, LIME, BLACK, 16, LSB);
}
return 0;
}
/* ========================================================================== */
/* ========================================================================== */
/* */
/* [PART 3] CLI COMMAND FUNCTIONS */
/* (命令行接口模块) */
/* */
/* ========================================================================== */
/* ========================================================================== */
/*----------------------------------------------------------------------------
CLI Initialization (CLI系统初始化)
*---------------------------------------------------------------------------*/
/**
* @brief 初始化MR16 CLI命令系统
* @return 0-成功, -1-失败
*
* 注册两个主要命令:
* - mr16: MR16系统控制命令
* - radio: SX1281无线参数配置命令
*/
int8_t MR16_CLI_Init(void) {
static const CLI_Command_Definition_t mr16Command = {
"mr16",
"mr16 <subcmd> [args] - MR16 system commands. Use 'mr16 help' for details\r\n",
mr16CommandHandler,
-1
};
static const CLI_Command_Definition_t radioCommand = {
"radio",
"radio <cmd> - SX1281 radio control commands. Use 'radio help' for details\r\n",
radioCommandHandler,
-1
};
/* Register commands */
if (FreeRTOS_CLIRegisterCommand(&mr16Command) != pdPASS) {
return -1;
}
if (FreeRTOS_CLIRegisterCommand(&radioCommand) != pdPASS) {
return -1;
}
return 0;
}
/*----------------------------------------------------------------------------
Optimized CLI Help Texts (English only for maximum compatibility)
*---------------------------------------------------------------------------*/
/* Radio Command Help */
static const char radio_help_en[] =
"==============================================================================\r\n"
"radio <subcmd> [args] - SX1281 Radio Control Commands\r\n"
"==============================================================================\r\n"
"Basic Operation Commands:\r\n"
" mode <BLE|LORA|GFSK|FLRC> - Change radio working mode\r\n"
" rffreq <Hz> - Set RF frequency in Hz\r\n"
" power <dBm> - Set TX power (-18 to +13 dBm)\r\n"
" ramptime <us> - Set ramp time: 2,4,6,8,10,12,16,20 microseconds\r\n"
" baudrate [index] - Set baudrate for current mode\r\n"
"\r\n"
"Parameter Configuration:\r\n"
" modulation <field> <value> - Set modulation parameters\r\n"
" packet <field> <value> - Set packet parameters\r\n"
"\r\n"
"Detailed Help Commands:\r\n"
" modulation help - Show detailed modulation parameters help\r\n"
" packet help - Show detailed packet parameters help\r\n"
" baudrate help - Show baudrate configuration details\r\n"
"\r\n"
"Usage Examples:\r\n"
" radio mode LORA - Switch to LoRa mode\r\n"
" radio rffreq 2400000000 - Set frequency to 2.4GHz\r\n"
" radio power 10 - Set TX power to +10dBm\r\n"
" radio baudrate 2 - Set baudrate index 2 for current mode\r\n"
"\r\n"
"For more details on a specific command, use: radio <command> help\r\n"
"==============================================================================\r\n"
"==============================================================================\r\n";
/* Modulation Parameters Help */
static const char modulation_help[] =
"==========================================================================================================\r\n"
"modulation <field> <value> - Set Modulation Parameters\r\n"
"==========================================================================================================\r\n"
"Available fields for each radio mode:\r\n"
"\r\n"
"BLE/GFSK Mode Parameters:\r\n"
" br_bw - Bitrate & Bandwidth\r\n"
" 0x04 = 2.0Mbps / 2.4MHz 0x28 = 1.6Mbps / 2.4MHz\r\n"
" 0x4C = 1.0Mbps / 2.4MHz 0x45 = 1.0Mbps / 1.2MHz\r\n"
" 0x70 = 0.8Mbps / 2.4MHz 0x69 = 0.8Mbps / 1.2MHz\r\n"
" 0x8D = 0.5Mbps / 1.2MHz 0x86 = 0.5Mbps / 0.6MHz\r\n"
" 0xB1 = 0.4Mbps / 1.2MHz 0xAA = 0.4Mbps / 0.6MHz\r\n"
" 0xCE = 0.25Mbps / 0.6MHz 0xC7 = 0.25Mbps / 0.3MHz\r\n"
" 0xEF = 0.125Mbps / 0.3MHz\r\n"
"\r\n"
" modindex - Modulation Index\r\n"
" 0=0.35, 1=0.50, 2=0.75, 3=1.00, 4=1.25, 5=1.50, 6=1.75, 7=2.00\r\n"
" 8=2.25, 9=2.50, 10=2.75, 11=3.00, 12=3.25, 13=3.50, 14=3.75, 15=4.00\r\n"
"\r\n"
" shaping - Modulation Shaping\r\n"
" 0x00 = OFF (no shaping)\r\n"
" 0x10 = BT=1.0 (Gaussian shaping)\r\n"
" 0x20 = BT=0.5 (Gaussian shaping)\r\n"
"-------------------------------------------------------------------------------------------\r\n"
"LoRa Mode Parameters:\r\n"
" sf - Spreading Factor\r\n"
" 0x50 = SF5, 0x60 = SF6, 0x70 = SF7, 0x80 = SF8\r\n"
" 0x90 = SF9, 0xA0 = SF10, 0xB0 = SF11, 0xC0 = SF12\r\n"
"\r\n"
" bw - Bandwidth\r\n"
" 0x34 = 200kHz, 0x26 = 400kHz, 0x18 = 800kHz, 0x0A = 1600kHz\r\n"
"\r\n"
" cr - Coding Rate\r\n"
" 0x01 = 4/5, 0x02 = 4/6, 0x03 = 4/7, 0x04 = 4/8\r\n"
" 0x05 = Long Interleaving 4/5, 0x06 = Long Interleaving 4/6\r\n"
" 0x07 = Long Interleaving 4/7\r\n"
"-------------------------------------------------------------------------------------------\r\n"
"FLRC Mode Parameters:\r\n"
" br_bw - Bitrate & Bandwidth\r\n"
" 0x04 = 2.6Mbps / 2.4MHz 0x28 = 2.08Mbps / 2.4MHz\r\n"
" 0x45 = 1.3Mbps / 1.2MHz 0x69 = 1.04Mbps / 1.2MHz\r\n"
" 0x86 = 0.65Mbps / 0.6MHz 0xAA = 0.52Mbps / 0.6MHz\r\n"
" 0xC7 = 0.325Mbps / 0.3MHz 0xEB = 0.26Mbps / 0.3MHz\r\n"
"\r\n"
" cr - Coding Rate\r\n"
" 0x00 = CR_1/2, 0x02 = CR_3/4, 0x04 = CR_1/0\r\n"
"\r\n"
" shaping - Modulation Shaping\r\n"
" 0x00 = OFF (no shaping)\r\n"
" 0x10 = BT=1.0 (Gaussian shaping)\r\n"
" 0x20 = BT=0.5 (Gaussian shaping)\r\n"
"-------------------------------------------------------------------------------------------\r\n"
"Usage Examples:\r\n"
" radio modulation br_bw 0x4C - Set GFSK bitrate to 1.0Mbps / 2.4MHz\r\n"
" radio modulation sf 0x70 - Set LoRa spreading factor to SF7\r\n"
" radio modulation br_bw 0x45 - Set FLRC bitrate to 1.3Mbps / 1.2MHz\r\n"
"-------------------------------------------------------------------------------------------\r\n"
"Note: Changes take effect immediately. Use 'radio modulation' to see current values.\r\n"
"==========================================================================================================\r\n"
"==========================================================================================================\r\n";
/* Packet Parameters Help */
static const char packet_help[] =
"==========================================================================================================\r\n"
"packet <field> <value> - Set Packet Parameters\r\n"
"==========================================================================================================\r\n"
"Available fields for each radio mode:\r\n"
"-----------------------------------------------------------------------------------\r\n"
"BLE Mode Parameters:\r\n"
" ConnectionState - Connection state\r\n"
" 0 = MASTER_SLAVE, 1 = ADVERTISER, 2 = TX_TEST\r\n"
" 3 = RX_TEST, 4 = RXTX_TEST\r\n"
" CrcField - CRC field configuration\r\n"
" 0 = OFF, 1 = CRC_3B (3-byte CRC)\r\n"
" BlePacketType - BLE packet type\r\n"
" 0 = PRBS_9, 1 = PRBS_15, 2 = EYELONG_1_0, 3 = EYELONG_0_1\r\n"
" 4 = EYESHORT_1_0, 5 = EYESHORT_0_1, 6 = ALL_1, 7 = ALL_0\r\n"
" Whitening - Whitening mode\r\n"
" 0x00 = ON, 0x08 = OFF\r\n"
"-----------------------------------------------------------------------------------\r\n"
"GFSK Mode Parameters:\r\n"
" Preamble - Preamble length\r\n"
" 0x00 = 4bits, 0x10 = 8bits, 0x20 = 12bits, 0x30 = 16bits\r\n"
" 0x40 = 20bits, 0x50 = 24bits, 0x60 = 28bits, 0x70 = 32bits\r\n"
" SyncWordLength - Sync word length\r\n"
" 0x00 = 1byte, 0x02 = 2bytes, 0x04 = 3bytes\r\n"
" 0x06 = 4bytes, 0x08 = 5bytes\r\n"
" SyncWordMatch - Sync word match\r\n"
" 0x00 = OFF, 0x10 = 1, 0x20 = 2, 0x30 = 1_2\r\n"
" 0x40 = 3, 0x50 = 1_3, 0x60 = 2_3, 0x70 = 1_2_3\r\n"
" SyncWord1/2/3 - Set sync word bytes\r\n"
" Format: <hex_bytes> (e.g., 0x12345678)\r\n"
" Maximum 5 bytes for GFSK\r\n"
" SyncWord - Show all configured sync words\r\n"
" Header - Header type\r\n"
" 0x00 = VARIABLE, 0x20 = FIXED\r\n"
" Payload - Payload length in bytes\r\n"
" Range: 1-255\r\n"
" Crc - CRC length\r\n"
" 0x00 = OFF, 0x10 = 1byte, 0x20 = 2bytes, 0x30 = 3bytes\r\n"
" Whitening - Whitening mode\r\n"
" 0x00 = ON, 0x08 = OFF\r\n"
"-----------------------------------------------------------------------------------\r\n"
"LoRa Mode Parameters:\r\n"
" Preamble - Preamble symbols count\r\n"
" Range: 0-255\r\n"
" Header - Header type\r\n"
" 0x00 = VARIABLE/EXPLICIT\r\n"
" 0x80 = FIXED/IMPLICIT\r\n"
" Payload - Payload length in bytes\r\n"
" Range: 1-255\r\n"
" Crc - CRC mode\r\n"
" 0x00 = OFF, 0x20 = ON\r\n"
" InvertIQ - IQ inversion\r\n"
" 0x40 = NORMAL, 0x00 = INVERTED\r\n"
"-----------------------------------------------------------------------------------\r\n"
"FLRC Mode Parameters:\r\n"
" Most parameters same as GFSK\r\n"
" SyncWord1/2/3 - Set sync word bytes\r\n"
" Maximum 4 bytes for FLRC\r\n"
"-----------------------------------------------------------------------------------\r\n"
"Usage Examples:\r\n"
" radio packet Payload 64 - Set payload length to 64 bytes\r\n"
" radio packet Crc 0x20 - Enable 2-byte CRC\r\n"
" radio packet SyncWord1 0x1234 - Set sync word 1 to 0x1234\r\n"
" radio packet SyncWord - Display all sync words\r\n"
"-----------------------------------------------------------------------------------\r\n"
"Note: Use 'radio packet' without arguments to view current parameters.\r\n"
"==========================================================================================================\r\n"
"==========================================================================================================\r\n";
/* MR16 System Commands Help */
static const char mr16_help[] =
"================================================================================\r\n"
"mr16 <subcmd> [args] - MR16 System Control Commands\r\n"
"================================================================================\r\n"
"Configuration Management:\r\n"
" save - Save current configuration to Flash\r\n"
" get - Load configuration from Flash\r\n"
" reset - Restore default configuration\r\n"
" mode <VARIABLE|FIXED> - Set/get data format\r\n"
" fixedlen <1-123> - Set user data length for FIXED mode\r\n"
"--------------------------------------------------------------------\r\n"
"Receiver ID Configuration:\r\n"
" rxid1 <hex> - Set/get RX ID for channel 1\r\n"
" rxid2 <hex> - Set/get RX ID for channel 2\r\n"
" rxid3 <hex> - Set/get RX ID for channel 3\r\n"
" rxid - Show all three RX IDs\r\n"
"--------------------------------------------------------------------\r\n"
"Operation Commands:\r\n"
" tx - Start transmission mode\r\n"
" rx - Start continuous receive mode\r\n"
"--------------------------------------------------------------------\r\n"
"Information Display:\r\n"
" show - Show all MR16 configuration\r\n"
"--------------------------------------------------------------------\r\n"
"Detailed Help:\r\n"
" mode help - Show detailed mode information\r\n"
"--------------------------------------------------------------------\r\n"
"Usage Examples:\r\n"
" mr16 mode FIXED - Switch to fixed length mode\r\n"
" mr16 fixedlen 32 - Set user data length to 32 bytes\r\n"
""
" mr16 rxid1 0x1234 - Set RX ID for channel 1 to 0x1234\r\n"
" mr16 tx - Start transmitting\r\n"
" mr16 rx - Start receiving\r\n"
"--------------------------------------------------------------------\r\n"
"Important Notes:\r\n"
"1. fixedlen sets ONLY user data length, NOT total packet length.\r\n"
"2. MR16 automatically adds: Header(2 bytes) + Data + CRC(2 bytes).\r\n"
"3. Valid RX ID range: 0x0001 to 0xFFFE (0x0000 and 0xFFFF are reserved).\r\n"
"===============================================================================\r\n"
"===============================================================================\r\n";
/* Baudrate Configuration Help */
static const char baudrate_help[] =
"==============================================================================\r\n"
"baudrate [index] - Set Baudrate for Current Radio Mode\r\n"
"==============================================================================\r\n"
"Available baudrate indices for each mode:\r\n"
"----------------------------------------------------------------------\r\n"
"BLE Mode Baudrates:\r\n"
" Index 0 = 250 Kbps\r\n"
" Index 1 = 500 Kbps\r\n"
" Index 2 = 1 Mbps\r\n"
"----------------------------------------------------------------------\r\n"
"LoRa Mode Baudrates:\r\n"
" Index 0 = 216 bps\r\n"
" Index 1 = 1 Kbps\r\n"
" Index 2 = 5 Kbps\r\n"
" Index 3 = 10 Kbps\r\n"
" Index 4 = 20 Kbps\r\n"
" Index 5 = 61 Kbps\r\n"
" Index 6 = 127 Kbps\r\n"
" Index 7 = 203 Kbps\r\n"
"----------------------------------------------------------------------\r\n"
"GFSK Mode Baudrates:\r\n"
" Index 0 = 125 Kbps\r\n"
" Index 1 = 250 Kbps\r\n"
" Index 2 = 500 Kbps\r\n"
" Index 3 = 1 Mbps\r\n"
"----------------------------------------------------------------------\r\n"
"FLRC Mode Baudrates:\r\n"
" Index 0 = 130 Kbps\r\n"
" Index 1 = 260 Kbps\r\n"
" Index 2 = 520 Kbps\r\n"
" Index 3 = 1040 Kbps\r\n"
"----------------------------------------------------------------------\r\n"
"Usage Examples:\r\n"
" radio baudrate - Show current baudrate for all modes\r\n"
" radio baudrate 2 - Set baudrate index 2 for current mode\r\n"
"----------------------------------------------------------------------\r\n"
"Important Notes:\r\n"
"1. Baudrate index must be valid for the current radio mode.\r\n"
"2. Higher baudrates provide faster data rates but reduced range.\r\n"
"3. Lower baudrates increase range but reduce data rate.\r\n"
"==============================================================================\r\n"
"==============================================================================\r\n";
/*----------------------------------------------------------------------------
CLI Command Handlers (命令处理器)
*---------------------------------------------------------------------------*/
/**
* @brief 复制CLI参数到缓冲区(带长度限制)
* @param param 源参数字符串指针
* @param len 参数长度
* @param output 目标缓冲区指针
* @param outputSize 目标缓冲区大小
*/
static void copyCLIArg(const char *param, BaseType_t len, char *output, size_t outputSize) {
if (!param || !output || outputSize == 0) return;
size_t copyLen = (len < (BaseType_t)(outputSize - 1)) ? (size_t)len : (outputSize - 1);
strncpy(output, param, copyLen);
output[copyLen] = '\0';
}
/**
* @brief 获取数据格式字符串(用于显示)
* @param format 数据格式枚举值
* @return 格式字符串 ("VARIABLE"/"FIXED"/"UNKNOWN")
*/
static const char* getFormatString(MR16_DataFormat_t format) {
switch (format) {
case MR16_FORMAT_VARIABLE: return "VARIABLE";
case MR16_FORMAT_FIXED: return "FIXED";
default: return "UNKNOWN";
}
}
/**
* @brief 获取无线模式字符串(用于显示)
* @param mode 无线模式枚举值
* @return 模式字符串 ("BLE"/"LORA"/"GFSK"/"FLRC"/"UNKNOWN")
*/
static const char* getRadioModeString(SX1281_RadioMode_t mode) {
switch (mode) {
case RADIOMODE_BLE: return "BLE";
case RADIOMODE_LORA: return "LORA";
case RADIOMODE_GFSK: return "GFSK";
case RADIOMODE_FLRC: return "FLRC";
default: return "UNKNOWN";
}
}
/**
* @brief 验证RX ID是否在有效范围内
* @param rxid 待验证的RX ID
* @return true-有效(0x0001-0xFFFE), false-无效
*/
static bool isValidRXID(uint16_t rxid) {
return (rxid >= RXID_MIN && rxid <= RXID_MAX);
}
/**
* @brief 设置RX ID(带校验)
* @param rxid RX ID变量指针
* @param value 十六进制字符串值
* @param response 响应消息缓冲区
* @param responseLen 响应缓冲区大小
* @return 0-成功, -1-失败
*/
static int8_t setRXID(uint16_t *rxid, const char *value, char *response, size_t responseLen) {
uint16_t newID;
sscanf(value, "%hx", &newID);
if (!isValidRXID(newID)) {
snprintf(response, responseLen, "RXID set failed: invalid value\r\nValid range: 0x%04X-0x%04X\r\n",
RXID_MIN, RXID_MAX);
return -1;
}
*rxid = newID;
snprintf(response, responseLen, "RXID set to: 0x%04X\r\n", *rxid);
return 0;
}
/**
* @brief 分块输出帮助信息(防止缓冲区溢出)
* @param pcWriteBuffer 输出缓冲区
* @param xWriteBufferLen 缓冲区长度
* @param helpText 待输出的帮助文本
* @param position 当前输出位置指针
* @return pdTRUE-还有更多内容, pdFALSE-输出完成
*/
static BaseType_t outputHelpText(char *pcWriteBuffer, size_t xWriteBufferLen,
const char *helpText, size_t *position) {
size_t help_len = strlen(helpText);
if (*position >= help_len) {
*position = 0;
return pdFALSE;
}
size_t chunk = (xWriteBufferLen > 1) ? (xWriteBufferLen - 1) : 0;
size_t remain = help_len - *position;
size_t to_copy = (remain < chunk) ? remain : chunk;
if (to_copy > 0) {
memcpy(pcWriteBuffer, helpText + *position, to_copy);
pcWriteBuffer[to_copy] = '\0';
*position += to_copy;
return (*position < help_len) ? pdTRUE : pdFALSE;
}
*position = 0;
return pdFALSE;
}
/**
* @brief MR16命令处理器(CLI)
* @param pcWriteBuffer CLI写入缓冲区
* @param xWriteBufferLen 缓冲区长度
* @param pcCommandString 完整命令字符串
* @return pdTRUE-还有输出, pdFALSE-命令处理完成
*
* 支持的命令: save, get, reset, mode, fixedlen, tx, rx,
* rxid1/2/3, power, show, help
*/
static BaseType_t mr16CommandHandler(char *pcWriteBuffer, size_t xWriteBufferLen,
const char *pcCommandString) {
BaseType_t len1 = 0, len2 = 0, len3 = 0;
const char *p1 = FreeRTOS_CLIGetParameter(pcCommandString, 1, &len1);
const char *p2 = FreeRTOS_CLIGetParameter(pcCommandString, 2, &len2);
const char *p3 = FreeRTOS_CLIGetParameter(pcCommandString, 3, &len3);
char arg1[32] = {0}, arg2[32] = {0}, arg3[32] = {0};
/* Extract parameters using common function */
if (p1) copyCLIArg(p1, len1, arg1, sizeof(arg1));
if (p2) copyCLIArg(p2, len2, arg2, sizeof(arg2));
if (p3) copyCLIArg(p3, len3, arg3, sizeof(arg3));
/* Built-in help support */
/* Built-in help support: staged output
Stage 0: static mr16_help text
Stage 1: dynamic parameter dump of MR16 + current radio params */
if (strcasecmp(arg1, "help") == 0) {
static int help_stage = 0;
static size_t help_pos = 0;
static char param_dump[1024] = {0};
/* Stage 0: output static help text */
if (help_stage == 0) {
if (help_pos >= strlen(mr16_help)) help_pos = 0;
BaseType_t result = outputHelpText(pcWriteBuffer, xWriteBufferLen, mr16_help, &help_pos);
if (result == pdFALSE) {
/* prepare parameter dump for next stage */
help_pos = 0;
help_stage = 1;
/* build dynamic parameter text */
param_dump[0] = '\0';
{
size_t off = 0, remain = sizeof(param_dump);
/* MR16 basic params */
off += snprintf(param_dump + off, remain > off ? remain - off : 0,
"======== MR16 PARAMETERS ========\r\n");
off += snprintf(param_dump + off, remain > off ? remain - off : 0,
"Data Format : %s\r\n"
"User Data Len: %u bytes\r\n"
"Total Packet : %u bytes (Header+Data)\r\n"
"TX ID : 0x%04X\r\n"
"RX ID 1 : 0x%04X\r\n"
"RX ID 2 : 0x%04X\r\n"
"RX ID 3 : 0x%04X\r\n",
getFormatString(mr16.param->format),
mr16.param->fixedLength,
MR16_HEADER_LEN + mr16.param->fixedLength,
mr16.param->TX_ID,
mr16.param->RX_ID[0], mr16.param->RX_ID[1], mr16.param->RX_ID[2]);
/* Radio basic params */
off += snprintf(param_dump + off, remain > off ? remain - off : 0,
"\r\n======== RADIO PARAMETERS ========\r\n"
"Radio Mode : %s\r\n"
"Radio Role : %s\r\n"
"RF Frequency : %llu Hz\r\n"
"TX Power : %d dBm\r\n"
"Ramp Time : %u us\r\n",
getRadioModeString(radio.param->radioMode),
(radio.param->RadioRole == RadioRoleTX) ? "TX" : "RX",
(unsigned long long)radio.param->rfFrequency,
radio.param->txOutputPower,
(unsigned int)(
(radio.param->rampTime==RADIO_RAMP_02_US)?2:
(radio.param->rampTime==RADIO_RAMP_04_US)?4:
(radio.param->rampTime==RADIO_RAMP_06_US)?6:
(radio.param->rampTime==RADIO_RAMP_08_US)?8:
(radio.param->rampTime==RADIO_RAMP_10_US)?10:
(radio.param->rampTime==RADIO_RAMP_12_US)?12:
(radio.param->rampTime==RADIO_RAMP_16_US)?16:20)
);
/* Baudrates */
off += snprintf(param_dump + off, remain > off ? remain - off : 0,
"Baudrates : BLE=%u | LORA=%u | GFSK=%u | FLRC=%u\r\n",
radio.param->baudrate.ble,
radio.param->baudrate.lora,
radio.param->baudrate.gfks,
radio.param->baudrate.flrc);
/* Modulation and packet params (mode dependent) */
switch (radio.param->modulationParams.PacketType) {
case PACKET_TYPE_BLE:
off += snprintf(param_dump + off, remain > off ? remain - off : 0,
"Modulation(BLE): br_bw=0x%X modindex=%u shaping=0x%X\r\n",
radio.param->modulationParams.Params.Ble.BitrateBandwidth,
radio.param->modulationParams.Params.Ble.ModulationIndex,
radio.param->modulationParams.Params.Ble.ModulationShaping);
break;
case PACKET_TYPE_GFSK:
off += snprintf(param_dump + off, remain > off ? remain - off : 0,
"Modulation(GFSK): br_bw=0x%X modindex=%u shaping=0x%X\r\n",
radio.param->modulationParams.Params.Gfsk.BitrateBandwidth,
radio.param->modulationParams.Params.Gfsk.ModulationIndex,
radio.param->modulationParams.Params.Gfsk.ModulationShaping);
break;
case PACKET_TYPE_LORA:
off += snprintf(param_dump + off, remain > off ? remain - off : 0,
"Modulation(LoRa): sf=0x%X bw=0x%X cr=0x%X\r\n",
radio.param->modulationParams.Params.LoRa.SpreadingFactor,
radio.param->modulationParams.Params.LoRa.Bandwidth,
radio.param->modulationParams.Params.LoRa.CodingRate);
break;
case PACKET_TYPE_FLRC:
off += snprintf(param_dump + off, remain > off ? remain - off : 0,
"Modulation(FLRC): br_bw=0x%X cr=0x%X shaping=0x%X\r\n",
radio.param->modulationParams.Params.Flrc.BitrateBandwidth,
radio.param->modulationParams.Params.Flrc.CodingRate,
radio.param->modulationParams.Params.Flrc.ModulationShaping);
break;
default:
off += snprintf(param_dump + off, remain > off ? remain - off : 0,
"Modulation: Unknown packet type\r\n");
break;
}
/* Packet params */
switch (radio.param->packetParams.PacketType) {
case PACKET_TYPE_GFSK:
off += snprintf(param_dump + off, remain > off ? remain - off : 0,
"Packet(GFSK): Preamble=0x%X SyncLen=0x%X SyncMatch=0x%X Header=0x%X Payload=%u Crc=0x%X Whitening=0x%X\r\n",
radio.param->packetParams.Params.Gfsk.PreambleLength,
radio.param->packetParams.Params.Gfsk.SyncWordLength,
radio.param->packetParams.Params.Gfsk.SyncWordMatch,
radio.param->packetParams.Params.Gfsk.HeaderType,
radio.param->packetParams.Params.Gfsk.PayloadLength,
radio.param->packetParams.Params.Gfsk.CrcLength,
radio.param->packetParams.Params.Gfsk.Whitening);
break;
case PACKET_TYPE_LORA:
off += snprintf(param_dump + off, remain > off ? remain - off : 0,
"Packet(LoRa): Preamble=%u Header=0x%X Payload=%u Crc=0x%X InvertIQ=0x%X\r\n",
radio.param->packetParams.Params.LoRa.PreambleLength,
radio.param->packetParams.Params.LoRa.HeaderType,
radio.param->packetParams.Params.LoRa.PayloadLength,
radio.param->packetParams.Params.LoRa.CrcMode,
radio.param->packetParams.Params.LoRa.InvertIQ);
break;
case PACKET_TYPE_FLRC:
off += snprintf(param_dump + off, remain > off ? remain - off : 0,
"Packet(FLRC): Preamble=0x%X SyncLen=0x%X SyncMatch=0x%X Header=0x%X Payload=%u Crc=0x%X Whitening=0x%X\r\n",
radio.param->packetParams.Params.Flrc.PreambleLength,
radio.param->packetParams.Params.Flrc.SyncWordLength,
radio.param->packetParams.Params.Flrc.SyncWordMatch,
radio.param->packetParams.Params.Flrc.HeaderType,
radio.param->packetParams.Params.Flrc.PayloadLength,
radio.param->packetParams.Params.Flrc.CrcLength,
radio.param->packetParams.Params.Flrc.Whitening);
break;
case PACKET_TYPE_BLE:
off += snprintf(param_dump + off, remain > off ? remain - off : 0,
"Packet(BLE): ConnectionState=%u CrcField=%u BlePacketType=%u Whitening=0x%X\r\n",
radio.param->packetParams.Params.Ble.ConnectionState,
radio.param->packetParams.Params.Ble.CrcField,
radio.param->packetParams.Params.Ble.BlePacketType,
radio.param->packetParams.Params.Ble.Whitening);
break;
default:
off += snprintf(param_dump + off, remain > off ? remain - off : 0,
"Packet: Unknown packet type\r\n");
break;
}
/* Sync words (if applicable) */
off += snprintf(param_dump + off, remain > off ? remain - off : 0, "\r\nSync Words:\r\n");
/* GFSK sync words (max 5 bytes each) */
off += snprintf(param_dump + off, remain > off ? remain - off : 0,
" GFSK Word1: %02X%02X%02X%02X%02X\r\n",
mr16.param->radioParams.syncWord.gfsk.first[0],
mr16.param->radioParams.syncWord.gfsk.first[1],
mr16.param->radioParams.syncWord.gfsk.first[2],
mr16.param->radioParams.syncWord.gfsk.first[3],
mr16.param->radioParams.syncWord.gfsk.first[4]);
off += snprintf(param_dump + off, remain > off ? remain - off : 0,
" GFSK Word2: %02X%02X%02X%02X%02X\r\n",
mr16.param->radioParams.syncWord.gfsk.second[0],
mr16.param->radioParams.syncWord.gfsk.second[1],
mr16.param->radioParams.syncWord.gfsk.second[2],
mr16.param->radioParams.syncWord.gfsk.second[3],
mr16.param->radioParams.syncWord.gfsk.second[4]);
off += snprintf(param_dump + off, remain > off ? remain - off : 0,
" GFSK Word3: %02X%02X%02X%02X%02X\r\n",
mr16.param->radioParams.syncWord.gfsk.third[0],
mr16.param->radioParams.syncWord.gfsk.third[1],
mr16.param->radioParams.syncWord.gfsk.third[2],
mr16.param->radioParams.syncWord.gfsk.third[3],
mr16.param->radioParams.syncWord.gfsk.third[4]);
/* FLRC sync words (4 bytes each) */
off += snprintf(param_dump + off, remain > off ? remain - off : 0,
" FLRC Word1: %02X%02X%02X%02X\r\n",
mr16.param->radioParams.syncWord.flrc.first[0],
mr16.param->radioParams.syncWord.flrc.first[1],
mr16.param->radioParams.syncWord.flrc.first[2],
mr16.param->radioParams.syncWord.flrc.first[3]);
off += snprintf(param_dump + off, remain > off ? remain - off : 0,
" FLRC Word2: %02X%02X%02X%02X\r\n",
mr16.param->radioParams.syncWord.flrc.second[0],
mr16.param->radioParams.syncWord.flrc.second[1],
mr16.param->radioParams.syncWord.flrc.second[2],
mr16.param->radioParams.syncWord.flrc.second[3]);
off += snprintf(param_dump + off, remain > off ? remain - off : 0,
" FLRC Word3: %02X%02X%02X%02X\r\n",
mr16.param->radioParams.syncWord.flrc.third[0],
mr16.param->radioParams.syncWord.flrc.third[1],
mr16.param->radioParams.syncWord.flrc.third[2],
mr16.param->radioParams.syncWord.flrc.third[3]);
off += snprintf(param_dump + off, remain > off ? remain - off : 0,
"====================================\r\n");
}
}
return pdTRUE; /* continue outputting (we prepared param_dump next) */
}
/* Stage 1: output dynamic parameter dump */
if (help_stage == 1) {
BaseType_t result = outputHelpText(pcWriteBuffer, xWriteBufferLen, param_dump, &help_pos);
if (result == pdFALSE) {
/* finished all stages, reset */
help_pos = 0;
help_stage = 0;
}
return result;
}
}
/* Command: save - Save configuration to Flash */
if (strcasecmp(arg1, "save") == 0) {
Config_Get()->earsed_count++;
STMFLASH_Write(FLASH_SAVE_ADDR, (uint16_t*)Config_Get(), sizeof(Config_t)/2);
snprintf(pcWriteBuffer, xWriteBufferLen, "Configuration saved to Flash!\r\n");
/* Command: get - Read configuration from Flash */
} else if (strcasecmp(arg1, "get") == 0) {
STMFLASH_Read(FLASH_SAVE_ADDR, (uint16_t*)Config_Get(), sizeof(Config_t)/2);
snprintf(pcWriteBuffer, xWriteBufferLen, "Configuration loaded from Flash\r\n");
/* Command: reset - Restore default configuration */
} else if (strcasecmp(arg1, "reset") == 0) {
Config_SetDefaults();
snprintf(pcWriteBuffer, xWriteBufferLen, "Configuration reset to defaults!\r\n");
/* Command: mode - Set MR16 data format */
} else if (strcasecmp(arg1, "mode") == 0) {
if (strcasecmp(arg2, "help") == 0) {
snprintf(pcWriteBuffer, xWriteBufferLen,
"mode: 'VARIABLE' - variable length (UART idle)\r\n"
" 'FIXED' - fixed length\r\n");
return pdFALSE;
}
if (arg2[0] != '\0') {
if (strcasecmp(arg2, "VARIABLE") == 0) {
mr16.param->format = MR16_FORMAT_VARIABLE;
MR16_UI_UpdateStatus(&mr16);
snprintf(pcWriteBuffer, xWriteBufferLen, "Data format set to: VARIABLE\r\n");
} else if (strcasecmp(arg2, "FIXED") == 0) {
mr16.param->format = MR16_FORMAT_FIXED;
MR16_UI_UpdateStatus(&mr16);
snprintf(pcWriteBuffer, xWriteBufferLen, "Data format set to: FIXED\r\n");
} else {
snprintf(pcWriteBuffer, xWriteBufferLen, "Invalid format: %s\r\n", arg2);
}
} else {
snprintf(pcWriteBuffer, xWriteBufferLen, "Current format: %s\r\n", getFormatString(mr16.param->format));
}
/* Command: fixedlen - Set/show fixed packet length */
} else if (strcasecmp(arg1, "fixedlen") == 0) {
if (arg2[0] != '\0') {
char *endptr = NULL;
unsigned long len = strtoul(arg2, &endptr, 0);
if (endptr == arg2) {
snprintf(pcWriteBuffer, xWriteBufferLen, "Invalid length value: %s\r\n", arg2);
} else if (len < 1 || len > MR16_FLRC_MAX_FIXED_LEN) { /* FLRC模式最保守限制 */
snprintf(pcWriteBuffer, xWriteBufferLen,
"Length out of range: %lu (valid: 1..%d)\r\n"
"Note: GFSK/LoRa support up to 250/251 bytes\r\n",
len, MR16_FLRC_MAX_FIXED_LEN);
} else {
mr16.param->fixedLength = (uint16_t)len;
MR16_UI_UpdateStatus(&mr16);
snprintf(pcWriteBuffer, xWriteBufferLen,
"Fixed length set to %u bytes (user data)\r\n"
"Total packet: %u bytes (Header[2]+Data[%u]), HW CRC auto-added\r\n",
mr16.param->fixedLength,
MR16_HEADER_LEN + mr16.param->fixedLength,
mr16.param->fixedLength);
}
} else {
snprintf(pcWriteBuffer, xWriteBufferLen,
"Current fixed length: %u bytes (user data)\r\n"
"Total packet: %u bytes (Header[2]+Data[%u]), HW CRC auto-added\r\n",
mr16.param->fixedLength,
MR16_HEADER_LEN + mr16.param->fixedLength,
mr16.param->fixedLength);
}
/* Command: tx - Transmit packet */
} else if (strcasecmp(arg1, "tx") == 0) {
radio.param->RadioRole=RadioRoleTX;
uint16_t txLen;
switch (mr16.param->format) {
case MR16_FORMAT_VARIABLE:
txLen = uart2_datalength;
break;
case MR16_FORMAT_FIXED:
txLen = MR16_HEADER_LEN + mr16.param->fixedLength;
break;
default:
snprintf(pcWriteBuffer, xWriteBufferLen, "Cannot transmit: invalid format\r\n");
return pdFALSE;
}
SX1281_SetTX(&radio, mr16.txbuffer, txLen);
MR16_UI_Home(&mr16);
MR16_UI_UpdateStatus(&mr16);
WS2812_SetStatus(WS2812_STATUS_IDLE);
snprintf(pcWriteBuffer, xWriteBufferLen, "Transmitting packet...\r\n");
/* Command: rx - Start continuous receive */
} else if (strcasecmp(arg1, "rx") == 0) {
radio.param->RadioRole=RadioRoleRX;
SX1281_SetRXSuccessive(&radio);
MR16_UI_Home(&mr16);
MR16_UI_UpdateStatus(&mr16);
WS2812_SetStatus(WS2812_STATUS_IDLE);
snprintf(pcWriteBuffer, xWriteBufferLen, "Starting continuous receive...\r\n");
/* Commands: rxid1/rxid2/rxid3 - Set RX IDs */
} else if (strcasecmp(arg1, "rxid1") == 0 || strcasecmp(arg1, "rxid2") == 0 || strcasecmp(arg1, "rxid3") == 0) {
int idx = arg1[4] - '1'; /* Extract index from "rxid1/2/3" */
if (arg2[0] != '\0') {
setRXID(&mr16.param->RX_ID[idx], arg2, pcWriteBuffer, xWriteBufferLen);
} else {
snprintf(pcWriteBuffer, xWriteBufferLen, "Current RXID%d: 0x%04X\r\n", idx + 1, mr16.param->RX_ID[idx]);
}
/* Command: rxid - Show all RX IDs */
} else if (strcasecmp(arg1, "rxid") == 0 && arg2[0] == '\0') {
snprintf(pcWriteBuffer, xWriteBufferLen,
"RX IDs:\r\n RXID1: 0x%04X\r\n RXID2: 0x%04X\r\n RXID3: 0x%04X\r\n",
mr16.param->RX_ID[0], mr16.param->RX_ID[1], mr16.param->RX_ID[2]);
/* Command: show - Display all MR16 configuration */
} else if (strcasecmp(arg1, "show") == 0) {
snprintf(pcWriteBuffer, xWriteBufferLen,
"======== MR16 Configuration ========\r\n"
"Data Format : %s\r\n"
"User Data Len: %u bytes (pure data)\r\n"
"Total Packet : %u bytes (Header+Data, HW CRC auto)\r\n"
"TX ID : 0x%04X\r\n"
"RX ID 1 : 0x%04X\r\n"
"RX ID 2 : 0x%04X\r\n"
"RX ID 3 : 0x%04X\r\n"
"TX Power : %d dBm\r\n"
"Radio Mode : %s\r\n"
"Radio Role : %s\r\n"
"=====================================\r\n",
getFormatString(mr16.param->format),
mr16.param->fixedLength,
MR16_HEADER_LEN + mr16.param->fixedLength,
mr16.param->TX_ID,
mr16.param->RX_ID[0],
mr16.param->RX_ID[1],
mr16.param->RX_ID[2],
radio.param->txOutputPower,
getRadioModeString(radio.param->radioMode),
(radio.param->RadioRole == RadioRoleTX) ? "TX" : "RX");
/* Invalid command */
} else {
snprintf(pcWriteBuffer, xWriteBufferLen, "Invalid command: %s\r\nUse 'mr16 help' for available commands\r\n", arg1);
}
return pdFALSE;
}
/**
* @brief Radio命令处理器(CLI) - 控制SX1281无线参数
* @param pcWriteBuffer CLI写入缓冲区
* @param xWriteBufferLen 缓冲区长度
* @param pcCommandString 完整命令字符串
* @return pdTRUE-还有输出, pdFALSE-命令处理完成
*
* 支持的命令: help, mode, modulation, packet, syncword, bandwidth, txid
* 可配置参数: 无线协议、调制参数、包参数、同步字等
*/
static BaseType_t radioCommandHandler(char *pcWriteBuffer, size_t xWriteBufferLen,
const char *pcCommandString) {
BaseType_t len1 = 0, len2 = 0, len3 = 0, len4 = 0;
const char *p1 = FreeRTOS_CLIGetParameter(pcCommandString, 1, &len1);
const char *p2 = FreeRTOS_CLIGetParameter(pcCommandString, 2, &len2);
const char *p3 = FreeRTOS_CLIGetParameter(pcCommandString, 3, &len3);
const char *p4 = FreeRTOS_CLIGetParameter(pcCommandString, 4, &len4);
char arg1[32] = {0}, arg2[32] = {0}, arg3[32] = {0}, arg4[32] = {0};
/* Extract parameters using common function */
if (p1) copyCLIArg(p1, len1, arg1, sizeof(arg1));
if (p2) copyCLIArg(p2, len2, arg2, sizeof(arg2));
if (p3) copyCLIArg(p3, len3, arg3, sizeof(arg3));
if (p4) copyCLIArg(p4, len4, arg4, sizeof(arg4));
/* Command: help - Display help information */
if (strcasecmp(arg1, "help") == 0) {
static size_t help_pos = 0;
if (help_pos >= strlen(radio_help_en)) help_pos = 0;
BaseType_t result = outputHelpText(pcWriteBuffer, xWriteBufferLen, radio_help_en, &help_pos);
if (result == pdFALSE) help_pos = 0;
return result;
} else if (strcasecmp(arg1, "modulation") == 0 && strcasecmp(arg2, "help") == 0) {
static size_t mod_help_pos = 0;
if (mod_help_pos >= strlen(modulation_help)) mod_help_pos = 0;
BaseType_t result = outputHelpText(pcWriteBuffer, xWriteBufferLen, modulation_help, &mod_help_pos);
if (result == pdFALSE) mod_help_pos = 0;
return result;
} else if (strcasecmp(arg1, "packet") == 0 && strcasecmp(arg2, "help") == 0) {
static size_t pkt_help_pos = 0;
if (pkt_help_pos >= strlen(packet_help)) pkt_help_pos = 0;
BaseType_t result = outputHelpText(pcWriteBuffer, xWriteBufferLen, packet_help, &pkt_help_pos);
if (result == pdFALSE) pkt_help_pos = 0;
return result;
/* Parameter processing commands */
} else {
/* Parse numeric value: prefer arg4, then arg3, then arg2 */
unsigned long val = 0;
if (arg4[0] != '\0') {
val = strtoul(arg4, NULL, 0);
} else if (arg3[0] != '\0') {
val = strtoul(arg3, NULL, 0);
} else if (arg2[0] != '\0') {
val = strtoul(arg2, NULL, 0);
}
/* Command: mode - Change or show radio mode */
if (strcasecmp(arg1, "mode") == 0) {
if (arg2[0] == '\0') {
snprintf(pcWriteBuffer, xWriteBufferLen, "Current radio mode: %s\r\n",
getRadioModeString(radio.param->radioMode));
} else {
SX1281_RadioMode_t proto;
if (strcasecmp(arg2, "BLE") == 0) {
proto = RADIOMODE_BLE;
} else if (strcasecmp(arg2, "LORA") == 0) {
proto = RADIOMODE_LORA;
} else if (strcasecmp(arg2, "GFSK") == 0) {
proto = RADIOMODE_GFSK;
} else if (strcasecmp(arg2, "FLRC") == 0) {
proto = RADIOMODE_FLRC;
} else {
snprintf(pcWriteBuffer, xWriteBufferLen, "Unknown radio mode: %s\r\n", arg2);
return pdFALSE;
}
if (SX1281_SetMode(&radio, proto) == DEVICE_OK) {
snprintf(pcWriteBuffer, xWriteBufferLen, "Radio mode set to %s\r\n", arg2);
} else {
snprintf(pcWriteBuffer, xWriteBufferLen, "Failed to set radio mode\r\n");
}
}
/* Command: modulation - Set modulation parameters */
} else if (strcasecmp(arg1, "modulation") == 0) {
/* Show current modulation params when no field provided */
if (arg2[0] == '\0') {
switch (radio.param->modulationParams.PacketType) {
case PACKET_TYPE_BLE:
snprintf(pcWriteBuffer, xWriteBufferLen, "Modulation(BLE): br_bw=0x%X modindex=%u shaping=0x%X\r\n",
radio.param->modulationParams.Params.Ble.BitrateBandwidth,
radio.param->modulationParams.Params.Ble.ModulationIndex,
radio.param->modulationParams.Params.Ble.ModulationShaping);
break;
case PACKET_TYPE_GFSK:
snprintf(pcWriteBuffer, xWriteBufferLen, "Modulation(GFSK): br_bw=0x%X modindex=%u shaping=0x%X\r\n",
radio.param->modulationParams.Params.Gfsk.BitrateBandwidth,
radio.param->modulationParams.Params.Gfsk.ModulationIndex,
radio.param->modulationParams.Params.Gfsk.ModulationShaping);
break;
case PACKET_TYPE_LORA:
snprintf(pcWriteBuffer, xWriteBufferLen, "Modulation(LoRa): sf=0x%X bw=0x%X cr=0x%X\r\n",
radio.param->modulationParams.Params.LoRa.SpreadingFactor,
radio.param->modulationParams.Params.LoRa.Bandwidth,
radio.param->modulationParams.Params.LoRa.CodingRate);
break;
case PACKET_TYPE_FLRC:
snprintf(pcWriteBuffer, xWriteBufferLen, "Modulation(FLRC): br_bw=0x%X cr=0x%X shaping=0x%X\r\n",
radio.param->modulationParams.Params.Flrc.BitrateBandwidth,
radio.param->modulationParams.Params.Flrc.CodingRate,
radio.param->modulationParams.Params.Flrc.ModulationShaping);
break;
default:
snprintf(pcWriteBuffer, xWriteBufferLen, "Unknown modulation packet type\r\n");
break;
}
return pdFALSE;
}
/* Handle different packet types for modulation */
switch (radio.param->modulationParams.PacketType) {
case PACKET_TYPE_BLE:
if (strcasecmp(arg2, "br_bw") == 0) {
if (val > 0xEF) {
snprintf(pcWriteBuffer, xWriteBufferLen, "Invalid br_bw value for BLE (max: 0xEF)\r\n");
return pdFALSE;
}
radio.param->modulationParams.Params.Ble.BitrateBandwidth = (RadioGfskBleBitrates_t)val;
} else if (strcasecmp(arg2, "modindex") == 0) {
if (val > 15) {
snprintf(pcWriteBuffer, xWriteBufferLen, "Invalid modindex value for BLE (max: 15)\r\n");
return pdFALSE;
}
radio.param->modulationParams.Params.Ble.ModulationIndex = (RadioGfskBleModIndexes_t)val;
} else if (strcasecmp(arg2, "shaping") == 0) {
if (val != 0x00 && val != 0x10 && val != 0x20) {
snprintf(pcWriteBuffer, xWriteBufferLen, "Invalid shaping value for BLE (0x00/0x10/0x20)\r\n");
return pdFALSE;
}
radio.param->modulationParams.Params.Ble.ModulationShaping = (RadioModShapings_t)val;
} else {
snprintf(pcWriteBuffer, xWriteBufferLen, "Unknown modulation field for BLE: %s\r\n", arg2);
return pdFALSE;
}
break;
case PACKET_TYPE_GFSK:
if (strcasecmp(arg2, "br_bw") == 0) {
if (val > 0xEF) { snprintf(pcWriteBuffer, xWriteBufferLen, "Invalid br_bw value for GFSK (max: 0xEF)\r\n"); return pdFALSE; }
radio.param->modulationParams.Params.Gfsk.BitrateBandwidth = (RadioGfskBleBitrates_t)val;
} else if (strcasecmp(arg2, "modindex") == 0) {
if (val > 15) { snprintf(pcWriteBuffer, xWriteBufferLen, "Invalid modindex value for GFSK (max: 15)\r\n"); return pdFALSE; }
radio.param->modulationParams.Params.Gfsk.ModulationIndex = (RadioGfskBleModIndexes_t)val;
} else if (strcasecmp(arg2, "shaping") == 0) {
if (val != 0x00 && val != 0x10 && val != 0x20) { snprintf(pcWriteBuffer, xWriteBufferLen, "Invalid shaping value for GFSK (0x00/0x10/0x20)\r\n"); return pdFALSE; }
radio.param->modulationParams.Params.Gfsk.ModulationShaping = (RadioModShapings_t)val;
} else {
snprintf(pcWriteBuffer, xWriteBufferLen, "Unknown modulation field for GFSK: %s\r\n", arg2);
return pdFALSE;
}
break;
case PACKET_TYPE_LORA:
if (strcasecmp(arg2, "sf") == 0) {
if (val != 0x50 && val != 0x60 && val != 0x70 && val != 0x80 && val != 0x90 && val != 0xA0 && val != 0xB0 && val != 0xC0) {
snprintf(pcWriteBuffer, xWriteBufferLen, "Invalid sf value for LORA\r\n"); return pdFALSE;
}
radio.param->modulationParams.Params.LoRa.SpreadingFactor = (RadioLoRaSpreadingFactors_t)val;
}
else if (strcasecmp(arg2, "bw") == 0) {
if (val != 0x34 && val != 0x26 && val != 0x18 && val != 0x0A) { snprintf(pcWriteBuffer, xWriteBufferLen, "Invalid bw value for LORA\r\n"); return pdFALSE; }
radio.param->modulationParams.Params.LoRa.Bandwidth = (RadioLoRaBandwidths_t)val;
}
else if (strcasecmp(arg2, "cr") == 0) {
if (val < 0x01 || val > 0x07) { snprintf(pcWriteBuffer, xWriteBufferLen, "Invalid cr value for LORA\r\n"); return pdFALSE; }
radio.param->modulationParams.Params.LoRa.CodingRate = (RadioLoRaCodingRates_t)val;
}
else { snprintf(pcWriteBuffer, xWriteBufferLen, "Unknown modulation field\r\n"); return pdFALSE; }
break;
case PACKET_TYPE_FLRC:
if (strcasecmp(arg2, "br_bw") == 0) {
if (val != 0x04 && val != 0x28 && val != 0x45 && val != 0x69 && val != 0x86 && val != 0xAA && val != 0xC7 && val != 0xEB) {
snprintf(pcWriteBuffer, xWriteBufferLen, "Invalid br_bw value for FLRC\r\n"); return pdFALSE;
}
radio.param->modulationParams.Params.Flrc.BitrateBandwidth = (RadioFlrcBitrates_t)val;
}
else if (strcasecmp(arg2, "cr") == 0) {
if (val != 0x00 && val != 0x02 && val != 0x04) { snprintf(pcWriteBuffer, xWriteBufferLen, "Invalid cr value for FLRC\r\n"); return pdFALSE; }
radio.param->modulationParams.Params.Flrc.CodingRate = (RadioFlrcCodingRates_t)val;
}
else if (strcasecmp(arg2, "shaping") == 0) {
if (val != 0x00 && val != 0x10 && val != 0x20) { snprintf(pcWriteBuffer, xWriteBufferLen, "Invalid shaping value for FLRC\r\n"); return pdFALSE; }
radio.param->modulationParams.Params.Flrc.ModulationShaping = (RadioModShapings_t)val;
}
else { snprintf(pcWriteBuffer, xWriteBufferLen, "Unknown modulation field\r\n"); return pdFALSE; }
break;
default:
snprintf(pcWriteBuffer, xWriteBufferLen, "Unknown current packet type\r\n");
return pdFALSE;
}
Radio.SetModulationParams(&radio.param->modulationParams);
snprintf(pcWriteBuffer, xWriteBufferLen, "Modulation updated\r\n");
} else if (strcasecmp(arg1, "packet") == 0) {
/* Show current packet params when no field provided */
if (arg2[0] == '\0') {
switch (radio.param->packetParams.PacketType) {
case PACKET_TYPE_BLE:
snprintf(pcWriteBuffer, xWriteBufferLen, "Packet(BLE): ConnectionState=%u CrcField=%u BlePacketType=%u Whitening=0x%X\r\n",
radio.param->packetParams.Params.Ble.ConnectionState,
radio.param->packetParams.Params.Ble.CrcField,
radio.param->packetParams.Params.Ble.BlePacketType,
radio.param->packetParams.Params.Ble.Whitening);
break;
case PACKET_TYPE_GFSK:
snprintf(pcWriteBuffer, xWriteBufferLen, "Packet(GFSK): Preamble=0x%X SyncLen=0x%X SyncMatch=0x%X Header=0x%X Payload=%u Crc=0x%X Whitening=0x%X\r\n",
radio.param->packetParams.Params.Gfsk.PreambleLength,
radio.param->packetParams.Params.Gfsk.SyncWordLength,
radio.param->packetParams.Params.Gfsk.SyncWordMatch,
radio.param->packetParams.Params.Gfsk.HeaderType,
radio.param->packetParams.Params.Gfsk.PayloadLength,
radio.param->packetParams.Params.Gfsk.CrcLength,
radio.param->packetParams.Params.Gfsk.Whitening);
break;
case PACKET_TYPE_LORA:
snprintf(pcWriteBuffer, xWriteBufferLen, "Packet(LoRa): Preamble=%u Header=0x%X Payload=%u Crc=0x%X InvertIQ=0x%X\r\n",
radio.param->packetParams.Params.LoRa.PreambleLength,
radio.param->packetParams.Params.LoRa.HeaderType,
radio.param->packetParams.Params.LoRa.PayloadLength,
radio.param->packetParams.Params.LoRa.CrcMode,
radio.param->packetParams.Params.LoRa.InvertIQ);
break;
case PACKET_TYPE_FLRC:
snprintf(pcWriteBuffer, xWriteBufferLen, "Packet(FLRC): Preamble=0x%X SyncLen=0x%X SyncMatch=0x%X Header=0x%X Payload=%u Crc=0x%X Whitening=0x%X\r\n",
radio.param->packetParams.Params.Flrc.PreambleLength,
radio.param->packetParams.Params.Flrc.SyncWordLength,
radio.param->packetParams.Params.Flrc.SyncWordMatch,
radio.param->packetParams.Params.Flrc.HeaderType,
radio.param->packetParams.Params.Flrc.PayloadLength,
radio.param->packetParams.Params.Flrc.CrcLength,
radio.param->packetParams.Params.Flrc.Whitening);
break;
default:
snprintf(pcWriteBuffer, xWriteBufferLen, "Unknown packet type\r\n");
break;
}
return pdFALSE;
}
/* packet <proto> <field> <value>
fields examples: payload | preamble | header | crc | whitening
*/
/* 根据当前 packet type 设置对应字段 */
switch (radio.param->packetParams.PacketType) {
case PACKET_TYPE_BLE:
/* BLE 使用 Ble 子结构 */
if (strcasecmp(arg2, "ConnectionState") == 0) {
if (val > 4) { snprintf(pcWriteBuffer, xWriteBufferLen, "Invalid BLE ConnectionState (0-4)\r\n"); return pdFALSE; }
radio.param->packetParams.Params.Ble.ConnectionState = (RadioBleConnectionStates_t)val;
} else if (strcasecmp(arg2, "CrcField") == 0) {
if (val > 1) { snprintf(pcWriteBuffer, xWriteBufferLen, "Invalid BLE CrcField (0-1)\r\n"); return pdFALSE; }
radio.param->packetParams.Params.Ble.CrcField = (RadioBleCrcFields_t)val;
} else if (strcasecmp(arg2, "BlePacketType") == 0) {
if (val > 7) { snprintf(pcWriteBuffer, xWriteBufferLen, "Invalid BLEPacketType (0-7)\r\n"); return pdFALSE; }
radio.param->packetParams.Params.Ble.BlePacketType = (RadioBlePacketTypes_t)val;
} else if (strcasecmp(arg2, "Whitening") == 0) {
if (val != 0x00 && val != 0x08) { snprintf(pcWriteBuffer, xWriteBufferLen, "Invalid BLE Whitening (0x00/0x08)\r\n"); return pdFALSE; }
radio.param->packetParams.Params.Ble.Whitening = (RadioWhiteningModes_t)val;
} else {
snprintf(pcWriteBuffer, xWriteBufferLen, "Unknown BLE packet field\r\n");
return pdFALSE;
}
break;
case PACKET_TYPE_GFSK:
/* GFSK 子结构 */
if (strcasecmp(arg2, "Preamble") == 0) {
if (val != 0x00 && val != 0x10 && val != 0x20 && val != 0x30 && val != 0x40 && val != 0x50 && val != 0x60 && val != 0x70) {
snprintf(pcWriteBuffer, xWriteBufferLen, "Invalid GFSK Preamble (0x00/0x10/0x20/0x30/0x40/0x50/0x60/0x70)\r\n"); return pdFALSE;
}
radio.param->packetParams.Params.Gfsk.PreambleLength = (RadioPreambleLengths_t)val;
} else if (strcasecmp(arg2, "SyncWordLength") == 0) {
if (val != 0x00 && val != 0x02 && val != 0x04 && val != 0x06 && val != 0x08) {
snprintf(pcWriteBuffer, xWriteBufferLen, "Invalid GFSK SyncWordLength (0x00/0x02/0x04/0x06/0x08)\r\n"); return pdFALSE;
}
radio.param->packetParams.Params.Gfsk.SyncWordLength = (RadioSyncWordLengths_t)val;
} else if (strcasecmp(arg2, "SyncWordMatch") == 0) {
if (val != 0x00 && val != 0x10 && val != 0x20 && val != 0x30 && val != 0x40 && val != 0x50 && val != 0x60 && val != 0x70) {
snprintf(pcWriteBuffer, xWriteBufferLen, "Invalid GFSK SyncWordMatch (0x00/0x10/0x20/0x30/0x40/0x50/0x60/0x70)\r\n"); return pdFALSE;
}
radio.param->packetParams.Params.Gfsk.SyncWordMatch = (RadioSyncWordRxMatchs_t)val;
} else if (strcasecmp(arg2, "SyncWord") == 0) {
/* Show all GFSK sync words */
snprintf(pcWriteBuffer, xWriteBufferLen,
"GFSK Sync Words:\r\n"
" Word 1: %02X%02X%02X%02X%02X\r\n"
" Word 2: %02X%02X%02X%02X%02X\r\n"
" Word 3: %02X%02X%02X%02X%02X\r\n",
mr16.param->radioParams.syncWord.gfsk.first[0],
mr16.param->radioParams.syncWord.gfsk.first[1],
mr16.param->radioParams.syncWord.gfsk.first[2],
mr16.param->radioParams.syncWord.gfsk.first[3],
mr16.param->radioParams.syncWord.gfsk.first[4],
mr16.param->radioParams.syncWord.gfsk.second[0],
mr16.param->radioParams.syncWord.gfsk.second[1],
mr16.param->radioParams.syncWord.gfsk.second[2],
mr16.param->radioParams.syncWord.gfsk.second[3],
mr16.param->radioParams.syncWord.gfsk.second[4],
mr16.param->radioParams.syncWord.gfsk.third[0],
mr16.param->radioParams.syncWord.gfsk.third[1],
mr16.param->radioParams.syncWord.gfsk.third[2],
mr16.param->radioParams.syncWord.gfsk.third[3],
mr16.param->radioParams.syncWord.gfsk.third[4]);
return pdFALSE;
} else if (strcasecmp(arg2, "SyncWord1") == 0) {
if (arg3[0] != '\0') {
/* Set GFSK sync word 1 */
uint32_t sync_val = (uint32_t)strtoul(arg3, NULL, 0);
mr16.param->radioParams.syncWord.gfsk.first[0] = (sync_val >> 24) & 0xFF;
mr16.param->radioParams.syncWord.gfsk.first[1] = (sync_val >> 16) & 0xFF;
mr16.param->radioParams.syncWord.gfsk.first[2] = (sync_val >> 8) & 0xFF;
mr16.param->radioParams.syncWord.gfsk.first[3] = sync_val & 0xFF;
mr16.param->radioParams.syncWord.gfsk.first[4] = 0;
snprintf(pcWriteBuffer, xWriteBufferLen, "GFSK sync word 1 set to 0x%08lX\r\n", (unsigned long)sync_val);
} else {
snprintf(pcWriteBuffer, xWriteBufferLen, "GFSK Sync Word 1: %02X%02X%02X%02X%02X\r\n",
mr16.param->radioParams.syncWord.gfsk.first[0],
mr16.param->radioParams.syncWord.gfsk.first[1],
mr16.param->radioParams.syncWord.gfsk.first[2],
mr16.param->radioParams.syncWord.gfsk.first[3],
mr16.param->radioParams.syncWord.gfsk.first[4]);
}
return pdFALSE;
} else if (strcasecmp(arg2, "SyncWord2") == 0) {
if (arg3[0] != '\0') {
/* Set GFSK sync word 2 */
uint32_t sync_val = (uint32_t)strtoul(arg3, NULL, 0);
mr16.param->radioParams.syncWord.gfsk.second[0] = (sync_val >> 24) & 0xFF;
mr16.param->radioParams.syncWord.gfsk.second[1] = (sync_val >> 16) & 0xFF;
mr16.param->radioParams.syncWord.gfsk.second[2] = (sync_val >> 8) & 0xFF;
mr16.param->radioParams.syncWord.gfsk.second[3] = sync_val & 0xFF;
mr16.param->radioParams.syncWord.gfsk.second[4] = 0;
snprintf(pcWriteBuffer, xWriteBufferLen, "GFSK sync word 2 set to 0x%08lX\r\n", (unsigned long)sync_val);
} else {
snprintf(pcWriteBuffer, xWriteBufferLen, "GFSK Sync Word 2: %02X%02X%02X%02X%02X\r\n",
mr16.param->radioParams.syncWord.gfsk.second[0],
mr16.param->radioParams.syncWord.gfsk.second[1],
mr16.param->radioParams.syncWord.gfsk.second[2],
mr16.param->radioParams.syncWord.gfsk.second[3],
mr16.param->radioParams.syncWord.gfsk.second[4]);
}
return pdFALSE;
} else if (strcasecmp(arg2, "SyncWord3") == 0) {
if (arg3[0] != '\0') {
/* Set GFSK sync word 3 */
uint32_t sync_val = (uint32_t)strtoul(arg3, NULL, 0);
mr16.param->radioParams.syncWord.gfsk.third[0] = (sync_val >> 24) & 0xFF;
mr16.param->radioParams.syncWord.gfsk.third[1] = (sync_val >> 16) & 0xFF;
mr16.param->radioParams.syncWord.gfsk.third[2] = (sync_val >> 8) & 0xFF;
mr16.param->radioParams.syncWord.gfsk.third[3] = sync_val & 0xFF;
mr16.param->radioParams.syncWord.gfsk.third[4] = 0;
snprintf(pcWriteBuffer, xWriteBufferLen, "GFSK sync word 3 set to 0x%08lX\r\n", (unsigned long)sync_val);
} else {
snprintf(pcWriteBuffer, xWriteBufferLen, "GFSK Sync Word 3: %02X%02X%02X%02X%02X\r\n",
mr16.param->radioParams.syncWord.gfsk.third[0],
mr16.param->radioParams.syncWord.gfsk.third[1],
mr16.param->radioParams.syncWord.gfsk.third[2],
mr16.param->radioParams.syncWord.gfsk.third[3],
mr16.param->radioParams.syncWord.gfsk.third[4]);
}
return pdFALSE;
} else if (strcasecmp(arg2, "Header") == 0) {
if (val != 0x00 && val != 0x20) { snprintf(pcWriteBuffer, xWriteBufferLen, "Invalid GFSK Header (0x00/0x20)\r\n"); return pdFALSE; }
radio.param->packetParams.Params.Gfsk.HeaderType = (RadioPacketLengthModes_t)val;
} else if (strcasecmp(arg2, "Payload") == 0) {
if (val < 1 || val > 255) { snprintf(pcWriteBuffer, xWriteBufferLen, "Invalid GFSK Payload (1-255)\r\n"); return pdFALSE; }
radio.param->packetParams.Params.Gfsk.PayloadLength = (uint8_t)val;
} else if (strcasecmp(arg2, "Crc") == 0) {
if (val != 0x00 && val != 0x10 && val != 0x20 && val != 0x30) { snprintf(pcWriteBuffer, xWriteBufferLen, "Invalid GFSK Crc (0x00/0x10/0x20/0x30)\r\n"); return pdFALSE; }
radio.param->packetParams.Params.Gfsk.CrcLength = (RadioCrcTypes_t)val;
} else if (strcasecmp(arg2, "Whitening") == 0) {
if (val != 0x00 && val != 0x08) { snprintf(pcWriteBuffer, xWriteBufferLen, "Invalid GFSK Whitening (0x00/0x08)\r\n"); return pdFALSE; }
radio.param->packetParams.Params.Gfsk.Whitening = (RadioWhiteningModes_t)val;
} else {
snprintf(pcWriteBuffer, xWriteBufferLen, "Unknown GFSK packet field\r\n");
return pdFALSE;
}
break;
case PACKET_TYPE_LORA:
/* LoRa 子结构 */
if (strcasecmp(arg2, "Preamble") == 0) {
if (val < 0 || val > 255) { snprintf(pcWriteBuffer, xWriteBufferLen, "Invalid LoRa Preamble (0-255)\r\n"); return pdFALSE; }
radio.param->packetParams.Params.LoRa.PreambleLength = (uint8_t)val;
} else if (strcasecmp(arg2, "Header") == 0) {
if (val != 0x00 && val != 0x80) { snprintf(pcWriteBuffer, xWriteBufferLen, "Invalid LoRa Header (0x00/0x80)\r\n"); return pdFALSE; }
radio.param->packetParams.Params.LoRa.HeaderType = (RadioLoRaPacketLengthsModes_t)val;
} else if (strcasecmp(arg2, "Payload") == 0) {
if (val < 1 || val > 255) { snprintf(pcWriteBuffer, xWriteBufferLen, "Invalid LoRa Payload (1-255)\r\n"); return pdFALSE; }
radio.param->packetParams.Params.LoRa.PayloadLength = (uint8_t)val;
} else if (strcasecmp(arg2, "Crc") == 0) {
if (val != 0x00 && val != 0x20) { snprintf(pcWriteBuffer, xWriteBufferLen, "Invalid LoRa Crc (0x00/0x20)\r\n"); return pdFALSE; }
radio.param->packetParams.Params.LoRa.CrcMode = (RadioLoRaCrcModes_t)val;
} else if (strcasecmp(arg2, "InvertIQ") == 0) {
if (val != 0x00 && val != 0x40) { snprintf(pcWriteBuffer, xWriteBufferLen, "Invalid LoRa InvertIQ (0x00/0x40)\r\n"); return pdFALSE; }
radio.param->packetParams.Params.LoRa.InvertIQ = (RadioLoRaIQModes_t)val;
} else {
snprintf(pcWriteBuffer, xWriteBufferLen, "Unknown LoRa packet field\r\n");
return pdFALSE;
}
break;
case PACKET_TYPE_FLRC:
/* FLRC 子结构 */
if (strcasecmp(arg2, "Preamble") == 0) {
if (val != 0x00 && val != 0x10 && val != 0x20 && val != 0x30 && val != 0x40 && val != 0x50 && val != 0x60 && val != 0x70) {
snprintf(pcWriteBuffer, xWriteBufferLen, "Invalid FLRC Preamble (0x00/0x10/0x20/0x30/0x40/0x50/0x60/0x70)\r\n"); return pdFALSE;
}
radio.param->packetParams.Params.Flrc.PreambleLength = (RadioPreambleLengths_t)val;
} else if (strcasecmp(arg2, "SyncWordLength") == 0) {
if (val != 0x00 && val != 0x02 && val != 0x04 && val != 0x06 && val != 0x08) {
snprintf(pcWriteBuffer, xWriteBufferLen, "Invalid FLRC SyncWordLength (0x00/0x02/0x04/0x06/0x08)\r\n"); return pdFALSE;
}
radio.param->packetParams.Params.Flrc.SyncWordLength = (RadioFlrcSyncWordLengths_t)val;
} else if (strcasecmp(arg2, "SyncWordMatch") == 0) {
if (val != 0x00 && val != 0x10 && val != 0x20 && val != 0x30 && val != 0x40 && val != 0x50 && val != 0x60 && val != 0x70) {
snprintf(pcWriteBuffer, xWriteBufferLen, "Invalid FLRC SyncWordMatch (0x00/0x10/0x20/0x30/0x40/0x50/0x60/0x70)\r\n"); return pdFALSE;
}
radio.param->packetParams.Params.Flrc.SyncWordMatch = (RadioSyncWordRxMatchs_t)val;
} else if (strcasecmp(arg2, "SyncWord") == 0) {
/* Show all FLRC sync words */
snprintf(pcWriteBuffer, xWriteBufferLen,
"FLRC Sync Words:\r\n"
" Word 1: %02X%02X%02X%02X\r\n"
" Word 2: %02X%02X%02X%02X\r\n"
" Word 3: %02X%02X%02X%02X\r\n",
mr16.param->radioParams.syncWord.flrc.first[0],
mr16.param->radioParams.syncWord.flrc.first[1],
mr16.param->radioParams.syncWord.flrc.first[2],
mr16.param->radioParams.syncWord.flrc.first[3],
mr16.param->radioParams.syncWord.flrc.second[0],
mr16.param->radioParams.syncWord.flrc.second[1],
mr16.param->radioParams.syncWord.flrc.second[2],
mr16.param->radioParams.syncWord.flrc.second[3],
mr16.param->radioParams.syncWord.flrc.third[0],
mr16.param->radioParams.syncWord.flrc.third[1],
mr16.param->radioParams.syncWord.flrc.third[2],
mr16.param->radioParams.syncWord.flrc.third[3]);
return pdFALSE;
} else if (strcasecmp(arg2, "SyncWord1") == 0) {
if (arg3[0] != '\0') {
/* Set FLRC sync word 1 */
uint32_t sync_val = (uint32_t)strtoul(arg3, NULL, 0);
mr16.param->radioParams.syncWord.flrc.first[0] = (sync_val >> 24) & 0xFF;
mr16.param->radioParams.syncWord.flrc.first[1] = (sync_val >> 16) & 0xFF;
mr16.param->radioParams.syncWord.flrc.first[2] = (sync_val >> 8) & 0xFF;
mr16.param->radioParams.syncWord.flrc.first[3] = sync_val & 0xFF;
snprintf(pcWriteBuffer, xWriteBufferLen, "FLRC sync word 1 set to 0x%08lX\r\n", (unsigned long)sync_val);
} else {
snprintf(pcWriteBuffer, xWriteBufferLen, "FLRC Sync Word 1: %02X%02X%02X%02X\r\n",
mr16.param->radioParams.syncWord.flrc.first[0],
mr16.param->radioParams.syncWord.flrc.first[1],
mr16.param->radioParams.syncWord.flrc.first[2],
mr16.param->radioParams.syncWord.flrc.first[3]);
}
return pdFALSE;
} else if (strcasecmp(arg2, "SyncWord2") == 0) {
if (arg3[0] != '\0') {
/* Set FLRC sync word 2 */
uint32_t sync_val = (uint32_t)strtoul(arg3, NULL, 0);
mr16.param->radioParams.syncWord.flrc.second[0] = (sync_val >> 24) & 0xFF;
mr16.param->radioParams.syncWord.flrc.second[1] = (sync_val >> 16) & 0xFF;
mr16.param->radioParams.syncWord.flrc.second[2] = (sync_val >> 8) & 0xFF;
mr16.param->radioParams.syncWord.flrc.second[3] = sync_val & 0xFF;
snprintf(pcWriteBuffer, xWriteBufferLen, "FLRC sync word 2 set to 0x%08lX\r\n", (unsigned long)sync_val);
} else {
snprintf(pcWriteBuffer, xWriteBufferLen, "FLRC Sync Word 2: %02X%02X%02X%02X\r\n",
mr16.param->radioParams.syncWord.flrc.second[0],
mr16.param->radioParams.syncWord.flrc.second[1],
mr16.param->radioParams.syncWord.flrc.second[2],
mr16.param->radioParams.syncWord.flrc.second[3]);
}
return pdFALSE;
} else if (strcasecmp(arg2, "SyncWord3") == 0) {
if (arg3[0] != '\0') {
/* Set FLRC sync word 3 */
uint32_t sync_val = (uint32_t)strtoul(arg3, NULL, 0);
mr16.param->radioParams.syncWord.flrc.third[0] = (sync_val >> 24) & 0xFF;
mr16.param->radioParams.syncWord.flrc.third[1] = (sync_val >> 16) & 0xFF;
mr16.param->radioParams.syncWord.flrc.third[2] = (sync_val >> 8) & 0xFF;
mr16.param->radioParams.syncWord.flrc.third[3] = sync_val & 0xFF;
snprintf(pcWriteBuffer, xWriteBufferLen, "FLRC sync word 3 set to 0x%08lX\r\n", (unsigned long)sync_val);
} else {
snprintf(pcWriteBuffer, xWriteBufferLen, "FLRC Sync Word 3: %02X%02X%02X%02X\r\n",
mr16.param->radioParams.syncWord.flrc.third[0],
mr16.param->radioParams.syncWord.flrc.third[1],
mr16.param->radioParams.syncWord.flrc.third[2],
mr16.param->radioParams.syncWord.flrc.third[3]);
}
return pdFALSE;
} else if (strcasecmp(arg2, "Header") == 0) {
if (val != 0x00 && val != 0x20) { snprintf(pcWriteBuffer, xWriteBufferLen, "Invalid FLRC Header (0x00/0x20)\r\n"); return pdFALSE; }
radio.param->packetParams.Params.Flrc.HeaderType = (RadioPacketLengthModes_t)val;
} else if (strcasecmp(arg2, "Payload") == 0) {
if (val < 1 || val > 255) { snprintf(pcWriteBuffer, xWriteBufferLen, "Invalid FLRC Payload (1-255)\r\n"); return pdFALSE; }
radio.param->packetParams.Params.Flrc.PayloadLength = (uint8_t)val;
} else if (strcasecmp(arg2, "Crc") == 0) {
if (val != 0x00 && val != 0x10 && val != 0x20 && val != 0x30) { snprintf(pcWriteBuffer, xWriteBufferLen, "Invalid FLRC Crc (0x00/0x10/0x20/0x30)\r\n"); return pdFALSE; }
radio.param->packetParams.Params.Flrc.CrcLength = (RadioCrcTypes_t)val;
} else if (strcasecmp(arg2, "Whitening") == 0) {
if (val != 0x00 && val != 0x08) { snprintf(pcWriteBuffer, xWriteBufferLen, "Invalid FLRC Whitening (0x00/0x08)\r\n"); return pdFALSE; }
radio.param->packetParams.Params.Flrc.Whitening = (RadioWhiteningModes_t)val;
} else {
snprintf(pcWriteBuffer, xWriteBufferLen, "Unknown FLRC packet field\r\n");
return pdFALSE;
}
break;
default:
snprintf(pcWriteBuffer, xWriteBufferLen, "Unknown current packet type\r\n");
return pdFALSE;
}
/* 应用并返回 */
Radio.SetPacketParams(&radio.param->packetParams);
snprintf(pcWriteBuffer, xWriteBufferLen, "Packet parameters updated\r\n");
} else if (strcasecmp(arg1, "baudrate") == 0) {
/* baudrate [index] - 根据当前模式自动设置波特率 */
/* 显示帮助 */
if (strcasecmp(arg2, "help") == 0) {
static size_t baudrate_help_pos = 0;
if (baudrate_help_pos >= strlen(baudrate_help)) baudrate_help_pos = 0;
BaseType_t result = outputHelpText(pcWriteBuffer, xWriteBufferLen, baudrate_help, &baudrate_help_pos);
if (result == pdFALSE) baudrate_help_pos = 0;
return result;
}
/* 无参数: 显示所有模式的波特率 */
if (arg2[0] == '\0') {
const char* current_mode = getRadioModeString(radio.param->radioMode);
snprintf(pcWriteBuffer, xWriteBufferLen,
"Current mode: %s\r\n"
"Baudrates: BLE=%u | LORA=%u | GFSK=%u | FLRC=%u --use \"radio baudrate help\" for help\r\n",
current_mode,
radio.param->baudrate.ble,
radio.param->baudrate.lora,
radio.param->baudrate.gfks,
radio.param->baudrate.flrc);
return pdFALSE;
}
/* 有参数: 根据当前模式设置波特率 */
int8_t ret = -1;
const char* mode_name = "";
switch (radio.param->radioMode) {
case RADIOMODE_BLE:
if (val > 2) {
snprintf(pcWriteBuffer, xWriteBufferLen, "Invalid BLE baudrate index: %lu (valid: 0-2)\r\n", (unsigned long)val);
return pdFALSE;
}
radio.param->baudrate.ble = (SX1281_BLEBaudrate_t)val;
ret = SX1281_SetBLEBaudrate(&radio, radio.param->baudrate.ble);
mode_name = "BLE";
break;
case RADIOMODE_LORA:
if (val > 7) {
snprintf(pcWriteBuffer, xWriteBufferLen, "Invalid LoRa baudrate index: %lu (valid: 0-7)\r\n", (unsigned long)val);
return pdFALSE;
}
radio.param->baudrate.lora = (SX1281_LORABaudrate_t)val;
ret = SX1281_SetLORABaudrate(&radio, radio.param->baudrate.lora);
mode_name = "LoRa";
break;
case RADIOMODE_GFSK:
if (val > 3) {
snprintf(pcWriteBuffer, xWriteBufferLen, "Invalid GFSK baudrate index: %lu (valid: 0-3)\r\n", (unsigned long)val);
return pdFALSE;
}
radio.param->baudrate.gfks = (SX1281_GFKSBaudrate_t)val;
ret = SX1281_SetGFSKBaudrate(&radio, radio.param->baudrate.gfks);
mode_name = "GFSK";
break;
case RADIOMODE_FLRC:
if (val > 3) {
snprintf(pcWriteBuffer, xWriteBufferLen, "Invalid FLRC baudrate index: %lu (valid: 0-3)\r\n", (unsigned long)val);
return pdFALSE;
}
radio.param->baudrate.flrc = (SX1281_FLRCBaudrate_t)val;
ret = SX1281_SetFLRCBaudrate(&radio, radio.param->baudrate.flrc);
mode_name = "FLRC";
break;
default:
snprintf(pcWriteBuffer, xWriteBufferLen, "Unknown radio mode\r\n");
return pdFALSE;
}
if (ret == DEVICE_OK) {
snprintf(pcWriteBuffer, xWriteBufferLen, "Baudrate updated for %s mode -> index %lu\r\n",
mode_name, (unsigned long)val);
} else {
snprintf(pcWriteBuffer, xWriteBufferLen, "Failed to update baudrate for %s mode\r\n", mode_name);
}
} else if (strcasecmp(arg1, "power") == 0) {
/* power <value> */
if (arg2[0] == '\0') {
snprintf(pcWriteBuffer, xWriteBufferLen, "Current TX power: %d dBm\r\n", radio.param->txOutputPower);
return pdFALSE;
}
int8_t p = (int8_t)val;
radio.param->txOutputPower = p;
Radio.SetTxParams(radio.param->txOutputPower, radio.param->rampTime);
snprintf(pcWriteBuffer, xWriteBufferLen, "TX power set to %d dBm\r\n", p);
} else if (strcasecmp(arg1, "ramptime") == 0) {
/* ramptime <us> - accepts 2/4/6/8/10/12/16/20 */
RadioRampTimes_t rt = RADIO_RAMP_02_US;
unsigned long us = val;
if (arg2[0] == '\0') {
/* print current ramptime */
unsigned long cur_us = 2;
switch (radio.param->rampTime) {
case RADIO_RAMP_02_US: cur_us = 2; break;
case RADIO_RAMP_04_US: cur_us = 4; break;
case RADIO_RAMP_06_US: cur_us = 6; break;
case RADIO_RAMP_08_US: cur_us = 8; break;
case RADIO_RAMP_10_US: cur_us = 10; break;
case RADIO_RAMP_12_US: cur_us = 12; break;
case RADIO_RAMP_16_US: cur_us = 16; break;
case RADIO_RAMP_20_US: cur_us = 20; break;
default: break;
}
snprintf(pcWriteBuffer, xWriteBufferLen, "Current ramp time: %lu us\r\n", cur_us);
return pdFALSE;
}
switch (us) {
case 2: rt = RADIO_RAMP_02_US; break;
case 4: rt = RADIO_RAMP_04_US; break;
case 6: rt = RADIO_RAMP_06_US; break;
case 8: rt = RADIO_RAMP_08_US; break;
case 10: rt = RADIO_RAMP_10_US; break;
case 12: rt = RADIO_RAMP_12_US; break;
case 16: rt = RADIO_RAMP_16_US; break;
case 20: rt = RADIO_RAMP_20_US; break;
default: snprintf(pcWriteBuffer, xWriteBufferLen, "Invalid ramptime\r\n"); return pdFALSE;
}
radio.param->rampTime = rt;
Radio.SetTxParams(radio.param->txOutputPower, radio.param->rampTime);
snprintf(pcWriteBuffer, xWriteBufferLen, "Ramp time set to %lu us\r\n", us);
} else if (strcasecmp(arg1, "rffreq") == 0 || strcasecmp(arg1, "rffrequency") == 0) {
/* rffreq <value in Hz> */
if (arg2[0] == '\0') {
snprintf(pcWriteBuffer, xWriteBufferLen, "Current RF freq: %llu Hz\r\n", (unsigned long long)radio.param->rfFrequency);
return pdFALSE;
}
uint32_t f = (uint32_t)val;
SX1281_SetRFFrequency(&radio, f);
snprintf(pcWriteBuffer, xWriteBufferLen, "RF freq set to %lu Hz\r\n", (unsigned long)f);
} else {
snprintf(pcWriteBuffer, xWriteBufferLen, "Unknown radio command\r\n");
}
}
return pdFALSE;
}