STM32 LoRaWAN Ping-Pong 节点方案

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. 测试步骤

  1. 烧录程序到STM32
  2. 配置LoRaWAN服务器(ChirpStack/TTN)
  3. 观察串口输出
  4. 使用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入网、双向通信、低功耗管理和数据统计。

相关推荐
HQB20262 小时前
EG1198|屹晶微电子 120V 内置 MOS 异步降压 DC‑DC(高性价比)
嵌入式硬件·硬件工程
果壳science4 小时前
张祥前实验如何获取正电荷
笔记·科技·嵌入式硬件·硬件工程·制造
南山电子nscn4 小时前
中科芯CETC汽车电池管理系统BMS进口芯片国产型号替代指南
单片机·汽车·bms
沉淀的.晴天5 小时前
can总线
stm32
桀人5 小时前
认识AT24C02
stm32·单片机·嵌入式硬件
国科安芯6 小时前
星间激光通信终端CANFD控制网络的时延确定性与辐射可靠性
网络·嵌入式硬件·信息与通信·canfd·低轨卫星·商业航天·抗辐射
一直C6 小时前
嵌入式 Linux 驱动开发学习笔记:内核定时器、platform 总线与 I²C 驱动
linux·驱动开发·嵌入式硬件·学习·imx6ull
传感器与混合集成电路7 小时前
150MHz主频、8路DAC与6路PWM:JLH221360伺服控制SIP解析
嵌入式硬件
comyccccc7 小时前
STM32PID控制板设计全解析学习笔记
单片机·嵌入式硬件
碧海银沙音频科技研究院8 小时前
实现炬芯ATS308X的usb cdc 串口IAP 固件升级功能实现
人工智能·嵌入式硬件·深度学习·算法