MR16/User/module/mr16.c
2025-12-12 20:42:40 +08:00

1887 lines
86 KiB
C
Raw 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 */
}
/* 根据工作模式设置 PayloadLength */
Radio.SetStandby(STDBY_RC);
switch (mr16->param->format) {
case MR16_FORMAT_VARIABLE:
/* 可变长度模式: PayloadLength设置为最大允许接收长度
* 芯片会自动在包中添加Header(含长度字段),自动检测实际长度
* 这里设置的是"最大允许"值,用于接收时限制 */
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
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(&radio.param->packetParams);
if (radio.param->RadioRole == RadioRoleRX) {
SX1281_SetRXSingle(&radio);
// SX1281_SetRXSuccessive(&radio);
} else if (radio.param->RadioRole == RadioRoleTX) {
uint16_t txLen;
switch (mr16->param->format) {
case MR16_FORMAT_VARIABLE:
/* 可变长度: 使用UART当前接收长度 */
txLen = uart2_datalength;
break;
case MR16_FORMAT_FIXED:
/* 固定长度: Header(2) + 用户数据(fixedLength), 硬件CRC自动添加 */
txLen = MR16_HEADER_LEN + mr16->param->fixedLength;
break;
default:
txLen = 0;
break;
}
if (txLen > 0) {
SX1281_SetTX(&radio, mr16->txbuffer, txLen);
}
}
/* 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显示
*/bool isRCData = false;
int8_t MR16_Main(MR16_t *mr16) {
/* 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) {
/* Binary data - RC data, pack with TX_ID header */
MR16_PackTxBuffer(mr16, uart2_data, uart2_datalength);
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);
/* 恢复CLI处理前的状态 */
WS2812_SetStatus(savedStatus);
}
/* 数据处理完成,允许接收下一帧 */
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();
/* 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);
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 == 0 || userDataLen > MR16_GFSK_MAX_FIXED_LEN) {
return -1; /* Invalid parameters, max 250 bytes for GFSK/LoRa */
}
/* 1. 添加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 */
/* 2. 复制用户数据 */
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 = uart2_datalength;
} 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(65, 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(120, 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;
}
/*----------------------------------------------------------------------------
CLI Help Text Documentation
*---------------------------------------------------------------------------*/
static const char radio_help_en[] =
"============================================================================\r\n"
//"radio <subcmd> [args] - SX1281 radio control\r\n"
//"----------------------------------------------------------------------------\r\n"
//"Mode Configuration:\r\n"
//" mode <BLE|LORA|GFSK|FLRC> - Change working mode\r\n"
//" rffreq <Hz> - Set RF frequency\r\n"
//"----------------------------------------------------------------------------\r\n"
//"Parameter Configuration:\r\n"
//" modulation <field> <value> - Set modulation parameters\r\n"
//" packet <field> <value> - Set packet parameters\r\n"
//" baudrate [index] - Set baudrate (auto-detect mode)\r\n"
//" power <dBm> - Set TX power (-18..+13)\r\n"
//" ramptime <us> - Set ramp time (2|4|6|8|10|12|16|20)\r\n"
//"----------------------------------------------------------------------------\r\n"
//"Operation Commands:\r\n"
//" tx | rx - TX/RX operations\r\n"
//"----------------------------------------------------------------------------\r\n"
//"Help Commands:\r\n"
//" modulation help | packet help - Show detailed help\r\n"
//" baudrate help - Show baudrate details\r\n"
"============================================================================\r\n";
static const char modulation_help[] =
"============================================================================================================\r\n"
//"modulation <field> <value> - Set modulation parameters\r\n"
//" BLE/GFSK fields:\r\n"
//" br_bw : 0x04(2.0M/2.4M)||0x28(1.6M/2.4M)||0x4C(1.0M/2.4M)||0x45(1.0M/1.2M)\r\n"
//" 0x70(0.8M/2.4M)||0x69(0.8M/1.2M)||0x8D(0.5M/1.2M)||0x86(0.5M/0.6M)\r\n"
//" 0xB1(0.4M/1.2M)||0xAA(0.4M/0.6M)||0xCE(0.25M/0.6M)||0xC7(0.25M/0.3M)||0xEF(0.125M/0.3M)\r\n"
//" modindex : 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"
//" shaping : 0x00(OFF)||0x10(BT_1_0)||0x20(BT_0_5)\r\n"
//" LORA fields:\r\n"
//" sf : 0x50(SF5)||0x60(SF6)||0x70(SF7)||0x80(SF8)||0x90(SF9)||0xA0(SF10)||0xB0(SF11)||0xC0(SF12)\r\n"
//" bw : 0x34(BW_200K)||0x26(BW_400K)||0x18(BW_800K)||0x0A(BW_1600K)\r\n"
//" cr : 0x01(4/5)||0x02(4/6)||0x03(4/7)||0x04(4/8)||0x05(LI_4/5)||0x06(LI_4/6)||0x07(LI_4/7)\r\n"
//" FLRC fields:\r\n"
//" br_bw : 0x04(2.6M/2.4M)||0x28(2.08M/2.4M)||0x45(1.3M/1.2M)||0x69(1.04M/1.2M)\r\n"
//" 0x86(0.65M/0.6M)||0xAA(0.52M/0.6M)||0xC7(0.325M/0.3M)||0xEB(0.26M/0.3M)\r\n"
//" cr : 0x00(CR_1/2)||0x02(CR_3/4)||0x04(CR_1/0)\r\n"
//" shaping : 0x00(OFF)||0x10(BT_1_0)||0x20(BT_0_5)\r\n"
//"Examples: radio modulation br_bw 0x4C\r\n"
//" radio modulation sf 0x70\r\n"
"============================================================================================================\r\n";
static const char packet_help[] =
"================================================================================================\r\n"
//"packet <field> <value> - Set packet parameters (current PacketType based)\r\n"
//" BLE fields:\r\n"
//" ConnectionState : 0(MASTER_SLAVE)||1(ADVERTISER)||2(TX_TEST)||3(RX_TEST)||4(RXTX_TEST)\r\n"
//" CrcField : 0(OFF)||1(CRC_3B)\r\n"
//" BlePacketType : 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 : 0x00(ON)||0x08(OFF)\r\n"
//" GFSK fields:\r\n"
//" Preamble : 0x00(4bits)||0x10(8bits)||0x20(12bits)||0x30(16bits)\r\n"
//" 0x40(20bits)||0x50(24bits)||0x60(28bits)||0x70(32bits)\r\n"
//" SyncWordLength : 0x00(1byte)||0x02(2bytes)||0x04(3bytes)||0x06(4bytes)||0x08(5bytes)\r\n"
//" SyncWordMatch : 0x00(OFF)||0x10(1)||0x20(2)||0x30(1_2)||0x40(3)||0x50(1_3)||0x60(2_3)||0x70(1_2_3)\r\n"
//" SyncWord1/2/3 : <hex_bytes> - Set sync word 1/2/3 (max 5 bytes for GFSK)\r\n"
//" SyncWord : Show all sync words\r\n"
//" Header : 0x00(VARIABLE)||0x20(FIXED)\r\n"
//" Payload : 1-255 (bytes)\r\n"
//" Crc : 0x00(OFF)||0x10(1byte)||0x20(2bytes)||0x30(3bytes)\r\n"
//" Whitening : 0x00(ON)||0x08(OFF)\r\n"
//" LORA fields:\r\n"
//" Preamble : 0-255 (symbols count)\r\n"
//" Header : 0x00(VARIABLE/EXPLICIT)||0x80(FIXED/IMPLICIT)\r\n"
//" Payload : 1-255 (bytes)\r\n"
//" Crc : 0x20(ON)||0x00(OFF)\r\n"
//" InvertIQ : 0x40(NORMAL)||0x00(INVERTED)\r\n"
//" FLRC fields: Same as GFSK (Preamble/SyncWordLength/SyncWordMatch/SyncWord1/2/3/Header/Payload/Crc/Whitening)\r\n"
//" SyncWord1/2/3 : <hex_bytes> - Set sync word 1/2/3 (max 4 bytes for FLRC)\r\n"
//"Examples: radio packet Payload 64\r\n"
//" radio packet Crc 0x20\r\n"
//" radio packet SyncWord1 0x12345678\r\n"
//" radio packet SyncWord\r\n"
"================================================================================================\r\n";
static const char mr16_help[] =
"======================================================\r\n"
//"mr16 <subcmd> [args] - MR16 system commands\r\n"
//"------------------------------------------------------\r\n"
//"Configuration Commands:\r\n"
//" save : Save MR16 configuration to Flash\r\n"
//" get : Load configuration from Flash\r\n"
//" reset : Restore default configuration\r\n"
//" mode <arg> : Set/get data format (VARIABLE|FIXED)\r\n"
//" fixedlen <len> : Set user data length (1-123 bytes)\r\n"
//" !!!Only user data length, NOT packet length!\r\n"
//" !!!MR16 auto adds: Header(2)+Data+CRC(2)\r\n"
//" power <dBm> : Set/show TX power (-18 to +13 dBm)\r\n"
//" rxid1/2/3 <hex> : Set/get RX ID (hex value)\r\n"
//"------------------------------------------------------\r\n"
//"Operation Commands:\r\n"
//" tx : Transmit current TX buffer\r\n"
//" rx : Start continuous receive\r\n"
//"------------------------------------------------------\r\n"
//"Information Commands:\r\n"
//" show : Show all MR16 configuration\r\n"
//" rxid : Show all three RX IDs\r\n"
//"------------------------------------------------------\r\n"
//"Help Commands:\r\n"
//" help : Show this help (supports paging)\r\n"
//" mode help : Show mode details\r\n"
"======================================================\r\n";
static const char baudrate_help[] =
"=====================================================================\r\n"
//"baudrate [index] - Set baudrate for current radio mode\r\n"
//"---------------------------------------------------------------------\r\n"
//"BLE Mode:\r\n"
//" 0 = 250Kbps | 1 = 500Kbps | 2 = 1Mbps\r\n"
//"---------------------------------------------------------------------\r\n"
//"LoRa Mode:\r\n"
//" 0 = 216bps | 1 = 1Kbps | 2 = 5Kbps | 3 = 10Kbps\r\n"
//" 4 = 20Kbps | 5 = 61Kbps | 6 = 127Kbps | 7 = 203Kbps\r\n"
//"---------------------------------------------------------------------\r\n"
//"GFSK Mode:\r\n"
//" 0 = 125Kbps | 1 = 250Kbps | 2 = 500Kbps | 3 = 1Mbps\r\n"
//"---------------------------------------------------------------------\r\n"
//"FLRC Mode:\r\n"
//" 0 = 130Kbps | 1 = 260Kbps | 2 = 520Kbps | 3 = 1040Kbps\r\n"
//"---------------------------------------------------------------------\r\n"
//"Examples:\r\n"
//" radio baudrate - Show current baudrates for all modes\r\n"
//" radio baudrate 2 - Set baudrate index 2 for current mode\r\n"
//" radio baudrate help - Show this help\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 */
if (strcasecmp(arg1, "help") == 0) {
static size_t help_pos = 0;
/* 每次新命令调用时重置位置 */
if (help_pos >= strlen(mr16_help)) {
help_pos = 0;
}
BaseType_t result = outputHelpText(pcWriteBuffer, xWriteBufferLen, mr16_help, &help_pos);
/* 如果本次输出完成,重置位置为下次准备 */
if (result == pdFALSE) {
help_pos = 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();
MR16_Init(&mr16,&Config_Get()->mr16);
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);
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);
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: power - Set/show TX power */
} else if (strcasecmp(arg1, "power") == 0) {
if (arg2[0] == '\0') {
if (radio.param) {
snprintf(pcWriteBuffer, xWriteBufferLen, "Current power: %d dBm\r\n", radio.param->txOutputPower);
} else {
snprintf(pcWriteBuffer, xWriteBufferLen, "Current power: (radio not initialized)\r\n");
}
} else {
char *endptr = NULL;
long v = strtol(arg2, &endptr, 0);
if (endptr == arg2) {
snprintf(pcWriteBuffer, xWriteBufferLen, "Invalid power value: %s\r\n", arg2);
} else if (v < TX_POWER_MIN || v > TX_POWER_MAX) {
snprintf(pcWriteBuffer, xWriteBufferLen, "Power out of range: %ld dBm (valid: %d..%d)\r\n",
v, TX_POWER_MIN, TX_POWER_MAX);
} else {
int8_t p = (int8_t)v;
if (radio.param) {
radio.param->txOutputPower = p;
Radio.SetTxParams(radio.param->txOutputPower, radio.param->rampTime);
MR16_UI_UpdateStatus(&mr16);
}
snprintf(pcWriteBuffer, xWriteBufferLen, "Power set to %d dBm\r\n", p);
}
}
/* 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 from arg3 or arg4 */
unsigned long val = 0;
if (arg4[0] != '\0') {
val = strtoul(arg4, NULL, 0);
} else if (arg3[0] != '\0') {
val = strtoul(arg3, 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\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;
}