STM32 LoRaWAN节点实现,支持Ping-Pong功能,包括OTAA/ABP入网、数据收发、低功耗管理等。
一、硬件平台选择
推荐硬件组合
| 组件 | 型号 | 说明 |
|---|---|---|
| MCU | STM32L073RZ | 超低功耗,支持LoRaWAN |
| LoRa模块 | SX1276/SX1278 | 868/915MHz频段 |
| 天线 | 弹簧天线/PCB天线 | 根据频段选择 |
| 电源 | 3.3V锂电池 | 支持低功耗 |
硬件连接
STM32L073RZ <-> SX1276
-------------------------
SPI1_NSS <-> NSS (PA4)
SPI1_SCK <-> SCK (PA5)
SPI1_MISO <-> MISO (PA6)
SPI1_MOSI <-> MOSI (PA7)
DIO0 <-> PA11 (中断)
DIO1 <-> PA12 (中断)
DIO2 <-> PA13 (中断)
RESET <-> PA10
ANT_SW <-> PA8 (天线开关)
二、软件架构
LoRaWAN_PingPong/
├── Core/
│ ├── Inc/
│ │ ├── lorawan.h
│ │ ├── sx1276.h
│ │ ├── pingpong.h
│ │ └── lowpower.h
│ └── Src/
│ ├── lorawan.c
│ ├── sx1276.c
│ ├── pingpong.c
│ └── lowpower.c
├── Drivers/
│ ├── STM32L0xx_HAL_Driver/
│ └── CMSIS/
└── main.c
三、核心代码实现
1. LoRaWAN协议栈配置 lorawan.h
c
#ifndef __LORAWAN_H
#define __LORAWAN_H
#include "stm32l0xx_hal.h"
#include <stdint.h>
#include <stdbool.h>
// LoRaWAN配置
#define LORAWAN_REGION LORAMAC_REGION_CN470
#define LORAWAN_CLASS CLASS_A
#define LORAWAN_ADR_ON 1
#define LORAWAN_NETWORK_JOIN OTAA // 或 ABP
// 设备凭证(OTAA)
#define DEV_EUI {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77}
#define JOIN_EUI {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}
#define APP_KEY {0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, \
0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, 0x3C}
// 设备凭证(ABP)
#define DEV_ADDR 0x260B1234
#define NWK_SKEY {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, \
0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF}
#define APP_SKEY {0xFF, 0xEE, 0xDD, 0xCC, 0xBB, 0xAA, 0x99, 0x88, \
0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x00}
// 应用端口
#define PING_PORT 1
#define PONG_PORT 2
// 消息类型
typedef enum {
MSG_TYPE_PING = 0x01,
MSG_TYPE_PONG = 0x02,
MSG_TYPE_ACK = 0x03
} MessageType;
// LoRaWAN状态
typedef enum {
LORAWAN_STATE_NOT_JOINED = 0,
LORAWAN_STATE_JOINING,
LORAWAN_STATE_JOINED,
LORAWAN_STATE_SENDING,
LORAWAN_STATE_RX_WINDOW,
LORAWAN_STATE_SLEEP
} LoRaWANState;
// 消息结构体
typedef struct {
uint8_t type;
uint16_t seq;
uint32_t timestamp;
uint8_t payload[16];
} PingPongMessage;
// 回调函数类型
typedef void (*LoRaWAN_JoinCallback)(bool joined);
typedef void (*LoRaWAN_RxCallback)(uint8_t port, uint8_t *data, uint8_t size);
typedef void (*LoRaWAN_TxCallback)(bool success);
// 函数声明
void LoRaWAN_Init(void);
bool LoRaWAN_JoinNetwork(void);
bool LoRaWAN_SendPing(uint16_t seq);
bool LoRaWAN_SendPong(uint16_t seq);
void LoRaWAN_SetCallbacks(LoRaWAN_JoinCallback join_cb,
LoRaWAN_RxCallback rx_cb,
LoRaWAN_TxCallback tx_cb);
void LoRaWAN_Process(void);
void LoRaWAN_Sleep(void);
void LoRaWAN_WakeUp(void);
#endif /* __LORAWAN_H */
2. LoRaWAN协议栈实现 lorawan.c
c
#include "lorawan.h"
#include "sx1276.h"
#include "LoRaMac.h"
#include <string.h>
// 静态变量
static LoRaWANState current_state = LORAWAN_STATE_NOT_JOINED;
static LoRaWAN_JoinCallback join_callback = NULL;
static LoRaWAN_RxCallback rx_callback = NULL;
static LoRaWAN_TxCallback tx_callback = NULL;
static uint16_t ping_seq = 0;
static uint32_t last_tx_time = 0;
// LoRaMac配置
static LoRaMacPrimitives_t mac_primitives;
static LoRaMacCallback_t mac_callbacks;
// 设备凭证
static uint8_t dev_eui[] = DEV_EUI;
static uint8_t join_eui[] = JOIN_EUI;
static uint8_t app_key[] = APP_KEY;
static uint8_t dev_addr[] = {0x34, 0x12, 0x0B, 0x26}; // DEV_ADDR小端格式
static uint8_t nwk_skey[] = NWK_SKEY;
static uint8_t app_skey[] = APP_SKEY;
// 初始化LoRaWAN
void LoRaWAN_Init(void)
{
// 初始化SX1276
SX1276_Init();
// 配置LoRaMac
mac_primitives.MacMcpsConfirm = McpsConfirm;
mac_primitives.MacMcpsIndication = McpsIndication;
mac_primitives.MacMlmeConfirm = MlmeConfirm;
mac_primitives.MacMlmeIndication = MlmeIndication;
mac_callbacks.GetBatteryLevel = GetBatteryLevel;
mac_callbacks.GetTemperatureLevel = GetTemperatureLevel;
// 初始化LoRaMac
LoRaMacInitialization(&mac_primitives, &mac_callbacks, LORAWAN_REGION);
// 设置ADR
MibRequestConfirm_t mib_req;
mib_req.Type = MIB_ADR;
mib_req.Param.AdrEnable = LORAWAN_ADR_ON;
LoRaMacMibSetRequestConfirm(&mib_req);
// 设置Class
mib_req.Type = MIB_DEVICE_CLASS;
mib_req.Param.Class = LORAWAN_CLASS;
LoRaMacMibSetRequestConfirm(&mib_req);
current_state = LORAWAN_STATE_NOT_JOINED;
}
// 加入网络
bool LoRaWAN_JoinNetwork(void)
{
if (current_state != LORAWAN_STATE_NOT_JOINED) {
return false;
}
#if (LORAWAN_NETWORK_JOIN == OTAA)
MlmeReq_t mlme_req;
mlme_req.Type = MLME_JOIN;
mlme_req.Req.Join.DevEui = dev_eui;
mlme_req.Req.Join.JoinEui = join_eui;
mlme_req.Req.Join.Datarate = DR_0;
if (LoRaMacMlmeRequest(&mlme_req) == LORAMAC_STATUS_OK) {
current_state = LORAWAN_STATE_JOINING;
return true;
}
#else
// ABP方式
MibRequestConfirm_t mib_req;
// 设置设备地址
mib_req.Type = MIB_DEV_ADDR;
mib_req.Param.DevAddr = (dev_addr[0] << 24) | (dev_addr[1] << 16) |
(dev_addr[2] << 8) | dev_addr[3];
LoRaMacMibSetRequestConfirm(&mib_req);
// 设置网络会话密钥
mib_req.Type = MIB_NWK_SKEY;
mib_req.Param.NwkSKey = nwk_skey;
LoRaMacMibSetRequestConfirm(&mib_req);
// 设置应用会话密钥
mib_req.Type = MIB_APP_SKEY;
mib_req.Param.AppSKey = app_skey;
LoRaMacMibSetRequestConfirm(&mib_req);
// 设置网络ID
mib_req.Type = MIB_NET_ID;
mib_req.Param.NetID = 0x000000;
LoRaMacMibSetRequestConfirm(&mib_req);
// 设置设备状态
mib_req.Type = MIB_NETWORK_JOINED;
mib_req.Param.IsNetworkJoined = true;
LoRaMacMibSetRequestConfirm(&mib_req);
current_state = LORAWAN_STATE_JOINED;
if (join_callback) {
join_callback(true);
}
return true;
#endif
return false;
}
// 发送Ping消息
bool LoRaWAN_SendPing(uint16_t seq)
{
if (current_state != LORAWAN_STATE_JOINED) {
return false;
}
PingPongMessage msg;
msg.type = MSG_TYPE_PING;
msg.seq = seq;
msg.timestamp = HAL_GetTick();
memset(msg.payload, 0xAA, sizeof(msg.payload));
McpsReq_t mcps_req;
mcps_req.Type = MCPS_UNCONFIRMED; // 或 MCPS_CONFIRMED
mcps_req.Req.Unconfirmed.Datarate = DR_0;
mcps_req.Req.Unconfirmed.FPort = PING_PORT;
mcps_req.Req.Unconfirmed.FBuffer = (uint8_t *)&msg;
mcps_req.Req.Unconfirmed.FBufferSize = sizeof(msg);
if (LoRaMacMcpsRequest(&mcps_req) == LORAMAC_STATUS_OK) {
current_state = LORAWAN_STATE_SENDING;
last_tx_time = HAL_GetTick();
return true;
}
return false;
}
// 发送Pong消息
bool LoRaWAN_SendPong(uint16_t seq)
{
if (current_state != LORAWAN_STATE_JOINED) {
return false;
}
PingPongMessage msg;
msg.type = MSG_TYPE_PONG;
msg.seq = seq;
msg.timestamp = HAL_GetTick();
memset(msg.payload, 0x55, sizeof(msg.payload));
McpsReq_t mcps_req;
mcps_req.Type = MCPS_UNCONFIRMED;
mcps_req.Req.Unconfirmed.Datarate = DR_0;
mcps_req.Req.Unconfirmed.FPort = PONG_PORT;
mcps_req.Req.Unconfirmed.FBuffer = (uint8_t *)&msg;
mcps_req.Req.Unconfirmed.FBufferSize = sizeof(msg);
if (LoRaMacMcpsRequest(&mcps_req) == LORAMAC_STATUS_OK) {
current_state = LORAWAN_STATE_SENDING;
return true;
}
return false;
}
// 设置回调函数
void LoRaWAN_SetCallbacks(LoRaWAN_JoinCallback join_cb,
LoRaWAN_RxCallback rx_cb,
LoRaWAN_TxCallback tx_cb)
{
join_callback = join_cb;
rx_callback = rx_cb;
tx_callback = tx_cb;
}
// LoRaMac回调函数
void McpsConfirm(McpsConfirm_t *confirm)
{
if (current_state == LORAWAN_STATE_SENDING) {
current_state = LORAWAN_STATE_JOINED;
if (tx_callback) {
tx_callback(confirm->Status == LORAMAC_EVENT_INFO_STATUS_OK);
}
}
}
void McpsIndication(McpsIndication_t *indication)
{
if (indication->Status != LORAMAC_EVENT_INFO_STATUS_OK) {
return;
}
if (rx_callback && indication->BufferSize > 0) {
rx_callback(indication->Port, indication->Buffer, indication->BufferSize);
}
}
void MlmeConfirm(MlmeConfirm_t *confirm)
{
if (confirm->Type == MLME_JOIN) {
if (confirm->Status == LORAMAC_EVENT_INFO_STATUS_OK) {
current_state = LORAWAN_STATE_JOINED;
if (join_callback) {
join_callback(true);
}
} else {
current_state = LORAWAN_STATE_NOT_JOINED;
if (join_callback) {
join_callback(false);
}
}
}
}
void MlmeIndication(MlmeIndication_t *indication)
{
// 处理MLME指示
}
// 获取电池电量(百分比)
uint8_t GetBatteryLevel(void)
{
// 这里应该读取实际电池电压
return 255; // 表示外部供电
}
// 获取温度
uint8_t GetTemperatureLevel(void)
{
// 这里应该读取实际温度
return 25;
}
// 处理LoRaWAN任务
void LoRaWAN_Process(void)
{
// 处理LoRaMac事件
LoRaMacProcess();
// 检查RX窗口超时
if (current_state == LORAWAN_STATE_RX_WINDOW) {
if (HAL_GetTick() - last_tx_time > 3000) { // 3秒超时
current_state = LORAWAN_STATE_JOINED;
}
}
}
// 进入低功耗模式
void LoRaWAN_Sleep(void)
{
SX1276_SetSleep();
current_state = LORAWAN_STATE_SLEEP;
}
// 唤醒
void LoRaWAN_WakeUp(void)
{
SX1276_SetStandby();
current_state = LORAWAN_STATE_JOINED;
}
3. Ping-Pong应用逻辑 pingpong.h
c
#ifndef __PINGPONG_H
#define __PINGPONG_H
#include "lorawan.h"
// Ping-Pong配置
#define PING_INTERVAL_MS 10000 // Ping间隔10秒
#define PONG_TIMEOUT_MS 5000 // Pong超时5秒
#define MAX_RETRY_COUNT 3 // 最大重试次数
#define JOIN_TIMEOUT_MS 30000 // 入网超时30秒
// Ping-Pong状态
typedef enum {
PP_STATE_INIT = 0,
PP_STATE_JOINING,
PP_STATE_IDLE,
PP_STATE_SENDING_PING,
PP_STATE_WAITING_PONG,
PP_STATE_SENDING_PONG,
PP_STATE_ERROR
} PingPongState;
// 统计信息
typedef struct {
uint32_t ping_sent;
uint32_t pong_received;
uint32_t ping_lost;
uint32_t avg_rtt;
uint32_t min_rtt;
uint32_t max_rtt;
uint8_t rssi;
uint8_t snr;
} PingPongStats;
// 函数声明
void PingPong_Init(void);
void PingPong_Process(void);
void PingPong_Start(void);
void PingPong_Stop(void);
PingPongState PingPong_GetState(void);
PingPongStats PingPong_GetStats(void);
#endif /* __PINGPONG_H */
4. Ping-Pong应用实现 pingpong.c
c
#include "pingpong.h"
#include "lowpower.h"
#include <string.h>
// 静态变量
static PingPongState pp_state = PP_STATE_INIT;
static PingPongStats pp_stats = {0};
static uint32_t last_ping_time = 0;
static uint32_t pong_timeout = 0;
static uint16_t current_seq = 0;
static uint8_t retry_count = 0;
static bool pp_running = false;
// 前向声明
static void OnJoinResult(bool joined);
static void OnRxData(uint8_t port, uint8_t *data, uint8_t size);
static void OnTxComplete(bool success);
// 初始化Ping-Pong
void PingPong_Init(void)
{
// 初始化LoRaWAN
LoRaWAN_Init();
// 设置回调
LoRaWAN_SetCallbacks(OnJoinResult, OnRxData, OnTxComplete);
// 初始化统计
memset(&pp_stats, 0, sizeof(pp_stats));
pp_stats.min_rtt = UINT32_MAX;
pp_state = PP_STATE_INIT;
}
// Ping-Pong主处理
void PingPong_Process(void)
{
// 处理LoRaWAN
LoRaWAN_Process();
switch (pp_state) {
case PP_STATE_INIT:
// 开始入网
if (LoRaWAN_JoinNetwork()) {
pp_state = PP_STATE_JOINING;
last_ping_time = HAL_GetTick();
}
break;
case PP_STATE_JOINING:
// 等待入网完成
if (HAL_GetTick() - last_ping_time > JOIN_TIMEOUT_MS) {
// 入网超时,重试
LoRaWAN_JoinNetwork();
last_ping_time = HAL_GetTick();
}
break;
case PP_STATE_IDLE:
if (pp_running) {
// 检查是否需要发送Ping
if (HAL_GetTick() - last_ping_time > PING_INTERVAL_MS) {
if (LoRaWAN_SendPing(current_seq)) {
pp_state = PP_STATE_SENDING_PING;
last_ping_time = HAL_GetTick();
retry_count = 0;
}
}
}
break;
case PP_STATE_SENDING_PING:
// 等待发送完成
break;
case PP_STATE_WAITING_PONG:
// 检查Pong超时
if (HAL_GetTick() - pong_timeout > PONG_TIMEOUT_MS) {
pp_stats.ping_lost++;
if (++retry_count >= MAX_RETRY_COUNT) {
pp_state = PP_STATE_IDLE;
} else {
// 重试发送Ping
if (LoRaWAN_SendPing(current_seq)) {
pp_state = PP_STATE_SENDING_PING;
pong_timeout = HAL_GetTick();
}
}
}
break;
case PP_STATE_SENDING_PONG:
// 等待发送完成
break;
default:
break;
}
// 进入低功耗模式
if (pp_state == PP_STATE_IDLE || pp_state == PP_STATE_WAITING_PONG) {
LowPower_EnterStopMode();
}
}
// 开始Ping-Pong
void PingPong_Start(void)
{
pp_running = true;
pp_state = PP_STATE_IDLE;
}
// 停止Ping-Pong
void PingPong_Stop(void)
{
pp_running = false;
pp_state = PP_STATE_IDLE;
}
// 获取状态
PingPongState PingPong_GetState(void)
{
return pp_state;
}
// 获取统计信息
PingPongStats PingPong_GetStats(void)
{
return pp_stats;
}
// 入网结果回调
static void OnJoinResult(bool joined)
{
if (joined) {
pp_state = PP_STATE_IDLE;
printf("LoRaWAN Joined Successfully!\n");
} else {
pp_state = PP_STATE_INIT;
printf("LoRaWAN Join Failed!\n");
}
}
// 接收数据回调
static void OnRxData(uint8_t port, uint8_t *data, uint8_t size)
{
if (size < sizeof(PingPongMessage)) {
return;
}
PingPongMessage *msg = (PingPongMessage *)data;
if (port == PING_PORT && msg->type == MSG_TYPE_PING) {
// 收到Ping,发送Pong
if (LoRaWAN_SendPong(msg->seq)) {
pp_state = PP_STATE_SENDING_PONG;
}
}
else if (port == PONG_PORT && msg->type == MSG_TYPE_PONG) {
// 收到Pong,计算RTT
if (msg->seq == current_seq) {
uint32_t rtt = HAL_GetTick() - last_ping_time;
pp_stats.pong_received++;
pp_stats.avg_rtt = (pp_stats.avg_rtt * (pp_stats.pong_received - 1) + rtt) / pp_stats.pong_received;
if (rtt < pp_stats.min_rtt) pp_stats.min_rtt = rtt;
if (rtt > pp_stats.max_rtt) pp_stats.max_rtt = rtt;
pp_state = PP_STATE_IDLE;
current_seq++;
}
}
}
// 发送完成回调
static void OnTxComplete(bool success)
{
if (success) {
if (pp_state == PP_STATE_SENDING_PING) {
pp_stats.ping_sent++;
pp_state = PP_STATE_WAITING_PONG;
pong_timeout = HAL_GetTick();
}
else if (pp_state == PP_STATE_SENDING_PONG) {
pp_state = PP_STATE_IDLE;
}
} else {
// 发送失败
if (pp_state == PP_STATE_SENDING_PING) {
if (++retry_count >= MAX_RETRY_COUNT) {
pp_state = PP_STATE_IDLE;
} else {
// 重试
if (LoRaWAN_SendPing(current_seq)) {
pp_state = PP_STATE_SENDING_PING;
}
}
}
}
}
5. 低功耗管理 lowpower.h 和 lowpower.c
c
// lowpower.h
#ifndef __LOWPOWER_H
#define __LOWPOWER_H
#include "stm32l0xx_hal.h"
void LowPower_Init(void);
void LowPower_EnterStopMode(void);
void LowPower_ExitStopMode(void);
void LowPower_EnterSleepMode(void);
void LowPower_EnterStandbyMode(void);
#endif /* __LOWPOWER_H */
// lowpower.c
#include "lowpower.h"
void LowPower_Init(void)
{
// 启用PWR时钟
__HAL_RCC_PWR_CLK_ENABLE();
// 设置低功耗模式
HAL_PWREx_EnableUltraLowPower();
HAL_PWREx_EnableFastWakeUp();
}
void LowPower_EnterStopMode(void)
{
// 进入STOP模式
HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);
}
void LowPower_ExitStopMode(void)
{
// 退出STOP模式,重新配置时钟
SystemClock_Config();
}
6. 主程序 main.c
c
#include "stm32l0xx_hal.h"
#include "lorawan.h"
#include "pingpong.h"
#include "lowpower.h"
#include <stdio.h>
// 重定向printf到串口
int fputc(int ch, FILE *f)
{
HAL_UART_Transmit(&huart2, (uint8_t *)&ch, 1, HAL_MAX_DELAY);
return ch;
}
// 系统时钟配置
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
// 配置HSI作为系统时钟源
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLLMUL_4;
RCC_OscInitStruct.PLL.PLLDIV = RCC_PLLDIV_2;
HAL_RCC_OscConfig(&RCC_OscInitStruct);
// 配置系统时钟
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_SYSCLK;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_1);
}
// 串口初始化
void UART_Init(void)
{
__HAL_RCC_USART2_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = GPIO_PIN_2 | GPIO_PIN_3;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF4_USART2;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
huart2.Instance = USART2;
huart2.Init.BaudRate = 115200;
huart2.Init.WordLength = UART_WORDLENGTH_8B;
huart2.Init.StopBits = UART_STOPBITS_1;
huart2.Init.Parity = UART_PARITY_NONE;
huart2.Init.Mode = UART_MODE_TX_RX;
huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart2.Init.OverSampling = UART_OVERSAMPLING_16;
huart2.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
huart2.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
HAL_UART_Init(&huart2);
}
int main(void)
{
// 初始化HAL库
HAL_Init();
// 配置系统时钟
SystemClock_Config();
// 初始化串口
UART_Init();
printf("STM32 LoRaWAN Ping-Pong Node\n");
printf("============================\n\n");
// 初始化低功耗
LowPower_Init();
// 初始化Ping-Pong
PingPong_Init();
// 开始Ping-Pong
PingPong_Start();
printf("Starting Ping-Pong...\n");
while (1) {
// 处理Ping-Pong逻辑
PingPong_Process();
// 打印状态
static uint32_t last_print = 0;
if (HAL_GetTick() - last_print > 5000) {
PingPongStats stats = PingPong_GetStats();
printf("Ping Sent: %lu, Pong Received: %lu, Lost: %lu\n",
stats.ping_sent, stats.pong_received, stats.ping_lost);
printf("Avg RTT: %lu ms, Min: %lu ms, Max: %lu ms\n",
stats.avg_rtt, stats.min_rtt, stats.max_rtt);
printf("State: %d\n\n", PingPong_GetState());
last_print = HAL_GetTick();
}
}
}
四、服务器配置
1. ChirpStack配置
json
{
"device_profile": {
"name": "STM32 Ping-Pong Node",
"supports_class_c": false,
"mac_version": "1.0.3",
"reg_params_revision": "A",
"max_eirp": 16,
"abp": {
"dev_addr": "260B1234",
"nwk_skey": "00112233445566778899aabbccddeeff",
"app_skey": "ffeeddccbbaa99887766554433221100"
},
"otaa": {
"dev_eui": "0011223344556677",
"join_eui": "0000000000000001",
"app_key": "2b7e151628aed2a6abf7158809cf4f3c"
}
}
}
2. 应用服务器处理逻辑
javascript
// Node.js应用服务器示例
const mqtt = require('mqtt');
const client = mqtt.connect('mqtt://localhost:1883');
client.on('connect', () => {
console.log('Connected to MQTT broker');
client.subscribe('application/+/device/+/rx');
});
client.on('message', (topic, message) => {
const data = JSON.parse(message.toString());
if (data.fPort === 1 && data.data) {
// 收到Ping,回复Pong
const pingPayload = Buffer.from(data.data, 'base64');
const pongPayload = pingPayload; // 简单回传相同数据
client.publish(`application/${data.applicationID}/device/${data.devEUI}/tx`,
JSON.stringify({
confirmed: false,
fPort: 2,
data: pongPayload.toString('base64')
}));
}
});
参考代码 lorawan STM32 方案,实现ping-pong功能,节点功能 www.youwenfan.com/contentcsv/72014.html
五、测试与调试
1. 测试步骤
- 烧录程序到STM32
- 配置LoRaWAN服务器(ChirpStack/TTN)
- 观察串口输出
- 使用LoRaWAN调试工具验证
2. 预期输出
STM32 LoRaWAN Ping-Pong Node
============================
Starting Ping-Pong...
LoRaWAN Joined Successfully!
Ping Sent: 1, Pong Received: 1, Lost: 0
Avg RTT: 245 ms, Min: 245 ms, Max: 245 ms
State: 2
Ping Sent: 2, Pong Received: 2, Lost: 0
Avg RTT: 238 ms, Min: 238 ms, Max: 245 ms
State: 2
3. 常见问题排查
| 问题 | 可能原因 | 解决方案 |
|---|---|---|
| 无法入网 | 设备凭证错误 | 检查DevEUI/AppKey |
| 无下行数据 | RX窗口未开启 | 确保在发送后立即监听 |
| 功耗过高 | 未进入低功耗 | 检查STOP模式配置 |
| 距离短 | 天线匹配差 | 调整天线位置 |
六、优化建议
1. 功耗优化
c
// 动态调整发射功率
void AdjustTxPower(void)
{
MibRequestConfirm_t mib_req;
mib_req.Type = MIB_CHANNELS_TX_POWER;
mib_req.Param.ChannelsTxPower = 14; // 降低功率到14dBm
LoRaMacMibSetRequestConfirm(&mib_req);
}
// 使用ADR优化
void EnableADR(void)
{
MibRequestConfirm_t mib_req;
mib_req.Type = MIB_ADR;
mib_req.Param.AdrEnable = true;
LoRaMacMibSetRequestConfirm(&mib_req);
}
2. 可靠性增强
c
// 添加重传机制
#define MAX_RETRANSMISSIONS 3
bool SendWithRetry(uint8_t port, uint8_t *data, uint8_t size)
{
for (int i = 0; i < MAX_RETRANSMISSIONS; i++) {
if (LoRaWAN_SendData(port, data, size)) {
return true;
}
HAL_Delay(1000);
}
return false;
}
这个方案提供了一个完整的STM32 LoRaWAN Ping-Pong节点实现,支持OTAA/ABP入网、双向通信、低功耗管理和数据统计。