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.hlowpower.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入网、双向通信、低功耗管理和数据统计。

相关推荐
模拟IC攻城狮1 小时前
(最新)华为 2025届秋招-硬件技术工程师-单板硬件开发—机试题—(共12套)(每套四十题)
嵌入式硬件·华为·硬件架构·pcb工艺·模拟芯片
我先去打把游戏先1 小时前
Ubuntu虚拟机(服务器版本)Git安装教程(附常用命令)——从零开始掌握版本控制
服务器·c语言·c++·git·嵌入式硬件·物联网·ubuntu
安生生申2 小时前
uni-app 连接 JDY-31 蓝牙串口模块实践
c语言·前端·javascript·stm32·单片机·嵌入式硬件·uni-app
熙芯XiChip2 小时前
CPLD核心原理与结构
单片机
番茄灭世神2 小时前
Vscode开发/调试ARM单片机最新教程
c语言·arm开发·vscode·stm32·嵌入式·gd32
于小猿Sup12 小时前
VMware在Ubuntu22.04驱动Livox Mid360s
linux·c++·嵌入式硬件·自动驾驶
chao18984414 小时前
STM32 HAL库驱动AT24C02 EEPROM例程
stm32·单片机·嵌入式硬件
不会武功的火柴15 小时前
SystemVerilog语法(8)-有限状态机(FSM)
嵌入式硬件·fpga开发·自动化·ic验证·rtl·uvm方法学
猫猫的小茶馆17 小时前
【Python】函数与模块化编程
linux·开发语言·arm开发·驱动开发·python·stm32