STM32L476 LoRaWAN网关项目分享

STM32L476 LoRaWAN网关项目

    • 项目结构
    • [1. 主网关管理器 (gateway.c/h)](#1. 主网关管理器 (gateway.c/h))
    • [2. LoRa SX130x 驱动 (lora_sx130x.c/h)](#2. LoRa SX130x 驱动 (lora_sx130x.c/h))
    • [3. EC20 4G模块驱动 (cellular_ec20.c/h)](#3. EC20 4G模块驱动 (cellular_ec20.c/h))
    • [4. MQTT客户端实现 (mqtt_client.c/h)](#4. MQTT客户端实现 (mqtt_client.c/h))
    • [5. OTA升级管理器 (ota_manager.c/h)](#5. OTA升级管理器 (ota_manager.c/h))
    • [6. SD卡存储管理器 (sd_storage.c/h)](#6. SD卡存储管理器 (sd_storage.c/h))
    • [7. 功耗管理器 (power_manager.c/h)](#7. 功耗管理器 (power_manager.c/h))
    • [8. FreeRTOS配置文件 (FreeRTOSConfig.h)](#8. FreeRTOS配置文件 (FreeRTOSConfig.h))
    • [9. 主函数 (main.c)](#9. 主函数 (main.c))
    • [10. Makefile 示例](#10. Makefile 示例)
    • 部署和配置说明

项目结构

复制代码
LoRaWAN_Gateway/
├── Core/
│   ├── Inc/
│   └── Src/
├── Drivers/
├── Middlewares/
│   ├── Third_Party/
│   │   ├── FreeRTOS/
│   │   ├── LoRaMAC-node/
│   │   └── mbedtls/
│   └── ST/
├── Applications/
│   ├── Inc/
│   │   ├── gateway.h
│   │   ├── lora_sx130x.h
│   │   ├── cellular_ec20.h
│   │   ├── mqtt_client.h
│   │   ├── ota_manager.h
│   │   ├── sd_storage.h
│   │   └── power_manager.h
│   └── Src/
│       ├── gateway.c
│       ├── lora_sx130x.c
│       ├── cellular_ec20.c
│       ├── mqtt_client.c
│       ├── ota_manager.c
│       ├── sd_storage.c
│       └── power_manager.c
└── README.md

1. 主网关管理器 (gateway.c/h)

c 复制代码
// gateway.h
#ifndef __GATEWAY_H
#define __GATEWAY_H

#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "semphr.h"
#include "event_groups.h"

typedef enum {
    GATEWAY_STATE_INIT = 0,
    GATEWAY_STATE_IDLE,
    GATEWAY_STATE_RUNNING,
    GATEWAY_STATE_UPLOADING,
    GATEWAY_STATE_OTA,
    GATEWAY_STATE_ERROR
} GatewayState_t;

typedef struct {
    uint32_t node_count;
    uint32_t packets_received;
    uint32_t packets_sent;
    uint32_t upload_errors;
    uint8_t network_status;
    uint8_t battery_level;
    int8_t signal_strength;
} GatewayStatus_t;

void Gateway_Init(void);
void Gateway_Run(void);
GatewayStatus_t Gateway_GetStatus(void);
void Gateway_SetState(GatewayState_t state);

#endif
c 复制代码
// gateway.c
#include "gateway.h"
#include "lora_sx130x.h"
#include "cellular_ec20.h"
#include "mqtt_client.h"
#include "ota_manager.h"
#include "sd_storage.h"
#include "power_manager.h"

// FreeRTOS任务句柄
static TaskHandle_t xMainTaskHandle = NULL;
static TaskHandle_t xLoRaTaskHandle = NULL;
static TaskHandle_t xCellularTaskHandle = NULL;
static TaskHandle_t xMQTTTaskHandle = NULL;

// 队列和信号量
static QueueHandle_t xLoRaDataQueue;
static QueueHandle_t xUploadQueue;
static SemaphoreHandle_t xGatewayMutex;
static EventGroupHandle_t xGatewayEvents;

#define GATEWAY_CONNECTED_BIT    (1 << 0)
#define GATEWAY_SD_READY_BIT     (1 << 1)
#define GATEWAY_CLOUD_OK_BIT     (1 << 2)
#define GATEWAY_OTA_REQUEST_BIT  (1 << 3)

static GatewayStatus_t gateway_status = {0};
static GatewayState_t gateway_state = GATEWAY_STATE_INIT;

void Gateway_Init(void) {
    // 初始化互斥量和队列
    xGatewayMutex = xSemaphoreCreateMutex();
    xLoRaDataQueue = xQueueCreate(100, sizeof(LoRaPacket_t));
    xUploadQueue = xQueueCreate(50, sizeof(UploadPacket_t));
    xGatewayEvents = xEventGroupCreate();
    
    // 初始化各个模块
    PowerManager_Init();
    SDStorage_Init();
    LoRaSX130x_Init();
    CellularEC20_Init();
    MQTTClient_Init();
    OTAManager_Init();
    
    // 创建任务
    xTaskCreate(Gateway_MainTask, "MainTask", 2048, NULL, 3, &xMainTaskHandle);
    xTaskCreate(LoRa_ReceiveTask, "LoRaTask", 4096, NULL, 4, &xLoRaTaskHandle);
    xTaskCreate(Cellular_ProcessTask, "CellularTask", 3072, NULL, 3, &xCellularTaskHandle);
    xTaskCreate(MQTT_ProcessTask, "MQTTTask", 4096, NULL, 2, &xMQTTTaskHandle);
}

void Gateway_MainTask(void *pvParameters) {
    TickType_t xLastWakeTime;
    const TickType_t xFrequency = pdMS_TO_TICKS(10000); // 10秒
    
    xLastWakeTime = xTaskGetTickCount();
    
    while (1) {
        // 检查网络状态
        Gateway_CheckNetworkStatus();
        
        // 同步时间(如果连接)
        if (xEventGroupGetBits(xGatewayEvents) & GATEWAY_CLOUD_OK_BIT) {
            Gateway_SyncTime();
        }
        
        // 上传状态信息
        Gateway_UploadStatus();
        
        // 检查OTA更新
        if (xEventGroupGetBits(xGatewayEvents) & GATEWAY_OTA_REQUEST_BIT) {
            Gateway_StartOTA();
        }
        
        // 进入低功耗模式(如果没有任务)
        PowerManager_EnterIdleMode();
        
        vTaskDelayUntil(&xLastWakeTime, xFrequency);
    }
}

void LoRa_ReceiveTask(void *pvParameters) {
    LoRaPacket_t packet;
    
    while (1) {
        if (LoRaSX130x_ReceivePacket(&packet, portMAX_DELAY) == HAL_OK) {
            // 更新统计
            xSemaphoreTake(xGatewayMutex, portMAX_DELAY);
            gateway_status.packets_received++;
            gateway_status.node_count = LoRaSX130x_GetNodeCount();
            xSemaphoreGive(xGatewayMutex);
            
            // 处理数据包
            Gateway_ProcessLoRaPacket(&packet);
            
            // 存储到SD卡
            SDStorage_SavePacket(&packet);
            
            // 放入上传队列
            if (xEventGroupGetBits(xGatewayEvents) & GATEWAY_CLOUD_OK_BIT) {
                xQueueSend(xUploadQueue, &packet, 0);
            }
        }
    }
}

void MQTT_ProcessTask(void *pvParameters) {
    UploadPacket_t upload_packet;
    
    while (1) {
        if (xQueueReceive(xUploadQueue, &upload_packet, portMAX_DELAY) == pdPASS) {
            if (MQTTClient_PublishData(&upload_packet) == MQTT_OK) {
                xSemaphoreTake(xGatewayMutex, portMAX_DELAY);
                gateway_status.packets_sent++;
                xSemaphoreGive(xGatewayMutex);
            } else {
                // 上传失败,标记为需要重传
                SDStorage_MarkForRetransmission(&upload_packet);
                xSemaphoreTake(xGatewayMutex, portMAX_DELAY);
                gateway_status.upload_errors++;
                xSemaphoreGive(xGatewayMutex);
            }
        }
    }
}

2. LoRa SX130x 驱动 (lora_sx130x.c/h)

c 复制代码
// lora_sx130x.h
#ifndef __LORA_SX130X_H
#define __LORA_SX130X_H

#include "stm32l4xx_hal.h"
#include "lorawan_gateway.h"

#define LORA_SPI               SPI1
#define LORA_SPI_CS_PIN        GPIO_PIN_4
#define LORA_SPI_CS_PORT       GPIOA
#define LORA_RESET_PIN         GPIO_PIN_5
#define LORA_RESET_PORT        GPIOC

typedef struct {
    uint8_t dev_addr[4];
    uint32_t timestamp;
    uint8_t payload[242];
    uint8_t payload_len;
    uint8_t data_rate;
    int16_t rssi;
    float snr;
    uint8_t channel;
} LoRaPacket_t;

typedef struct {
    uint32_t frequency;
    uint8_t sf;
    uint8_t bw;
    uint8_t cr;
    uint8_t sync_word;
} LoRaChannelConfig_t;

HAL_StatusTypeDef LoRaSX130x_Init(void);
HAL_StatusTypeDef LoRaSX130x_ReceivePacket(LoRaPacket_t *packet, uint32_t timeout);
HAL_StatusTypeDef LoRaSX130x_SendPacket(LoRaPacket_t *packet);
uint32_t LoRaSX130x_GetNodeCount(void);
void LoRaSX130x_ConfigureChannel(uint8_t ch, LoRaChannelConfig_t *config);
void LoRaSX130x_Start(void);
void LoRaSX130x_Stop(void);

#endif
c 复制代码
// lora_sx130x.c
#include "lora_sx130x.h"
#include "spi.h"
#include "gpio.h"

static SPI_HandleTypeDef hspi1;
static uint32_t node_count = 0;
static LoRaPacket_t rx_buffer[10];
static uint8_t buffer_index = 0;

// SX1302寄存器定义
#define REG_VERSION            0x00
#define REG_PKT_STATUS         0x01
#define REG_RX_BUFFER          0x02
#define REG_TX_BUFFER          0x03
#define REG_RX_NB_BYTES        0x04
#define REG_MODEM_STATUS       0x05

HAL_StatusTypeDef LoRaSX130x_Init(void) {
    HAL_StatusTypeDef status;
    
    // 初始化SPI
    hspi1.Instance = SPI1;
    hspi1.Init.Mode = SPI_MODE_MASTER;
    hspi1.Init.Direction = SPI_DIRECTION_2LINES;
    hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
    hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
    hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;
    hspi1.Init.NSS = SPI_NSS_SOFT;
    hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_32;
    hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
    hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
    hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
    
    if (HAL_SPI_Init(&hspi1) != HAL_OK) {
        return HAL_ERROR;
    }
    
    // 硬件复位
    HAL_GPIO_WritePin(LORA_RESET_PORT, LORA_RESET_PIN, GPIO_PIN_RESET);
    HAL_Delay(10);
    HAL_GPIO_WritePin(LORA_RESET_PORT, LORA_RESET_PIN, GPIO_PIN_SET);
    HAL_Delay(100);
    
    // 验证芯片ID
    uint8_t version = LoRaSX130x_ReadRegister(REG_VERSION);
    if (version != 0x12) { // SX1302版本号
        return HAL_ERROR;
    }
    
    // 配置LoRaWAN信道
    LoRaSX130x_ConfigureDefaultChannels();
    
    return HAL_OK;
}

static void LoRaSX130x_ConfigureDefaultChannels(void) {
    LoRaChannelConfig_t ch_config;
    
    // 中国频段:470-510MHz
    // 信道0: 470.3MHz
    ch_config.frequency = 470300000;
    ch_config.sf = 7;
    ch_config.bw = 0;  // 125kHz
    ch_config.cr = 1;  // 4/5
    ch_config.sync_word = 0x34;
    LoRaSX130x_ConfigureChannel(0, &ch_config);
    
    // 其他信道配置...
}

HAL_StatusTypeDef LoRaSX130x_ReceivePacket(LoRaPacket_t *packet, uint32_t timeout) {
    uint32_t start_tick = HAL_GetTick();
    uint8_t status;
    
    while ((HAL_GetTick() - start_tick) < timeout) {
        status = LoRaSX130x_ReadRegister(REG_PKT_STATUS);
        
        if (status & 0x01) { // 收到数据包
            // 读取数据包长度
            uint8_t pkt_len = LoRaSX130x_ReadRegister(REG_RX_NB_BYTES);
            
            // 选择芯片
            HAL_GPIO_WritePin(LORA_SPI_CS_PORT, LORA_SPI_CS_PIN, GPIO_PIN_RESET);
            
            // 发送读取命令
            uint8_t cmd = REG_RX_BUFFER | 0x80;
            HAL_SPI_Transmit(&hspi1, &cmd, 1, HAL_MAX_DELAY);
            
            // 读取数据
            HAL_SPI_Receive(&hspi1, packet->payload, pkt_len, HAL_MAX_DELAY);
            
            HAL_GPIO_WritePin(LORA_SPI_CS_PORT, LORA_SPI_CS_PIN, GPIO_PIN_SET);
            
            packet->payload_len = pkt_len;
            packet->timestamp = HAL_GetTick();
            
            // 读取RSSI和SNR
            packet->rssi = -(int8_t)LoRaSX130x_ReadRegister(0x1A);
            packet->snr = (int8_t)LoRaSX130x_ReadRegister(0x19) / 4.0;
            
            // 更新节点计数
            UpdateNodeCount(packet->dev_addr);
            
            return HAL_OK;
        }
        
        vTaskDelay(pdMS_TO_TICKS(10));
    }
    
    return HAL_TIMEOUT;
}

static uint8_t LoRaSX130x_ReadRegister(uint8_t reg) {
    uint8_t tx_data[2] = {reg & 0x7F, 0x00};
    uint8_t rx_data[2];
    
    HAL_GPIO_WritePin(LORA_SPI_CS_PORT, LORA_SPI_CS_PIN, GPIO_PIN_RESET);
    HAL_SPI_TransmitReceive(&hspi1, tx_data, rx_data, 2, HAL_MAX_DELAY);
    HAL_GPIO_WritePin(LORA_SPI_CS_PORT, LORA_SPI_CS_PIN, GPIO_PIN_SET);
    
    return rx_data[1];
}

3. EC20 4G模块驱动 (cellular_ec20.c/h)

c 复制代码
// cellular_ec20.h
#ifndef __CELLULAR_EC20_H
#define __CELLULAR_EC20_H

#include "stm32l4xx_hal.h"

#define EC20_UART           USART3
#define EC20_PWRKEY_PIN     GPIO_PIN_8
#define EC20_PWRKEY_PORT    GPIOB
#define EC20_RESET_PIN      GPIO_PIN_9
#define EC20_RESET_PORT     GPIOB

typedef enum {
    CELLULAR_STATE_POWER_OFF = 0,
    CELLULAR_STATE_INIT,
    CELLULAR_STATE_SIM_CHECK,
    CELLULAR_STATE_NETWORK_REG,
    CELLULAR_STATE_GPRS_ATTACH,
    CELLULAR_STATE_CONNECTED,
    CELLULAR_STATE_ERROR
} CellularState_t;

typedef struct {
    uint8_t imsi[16];
    uint8_t iccid[21];
    int8_t signal_strength;
    uint8_t network_operator[32];
    uint8_t ip_address[16];
} CellularInfo_t;

HAL_StatusTypeDef CellularEC20_Init(void);
HAL_StatusTypeDef CellularEC20_Connect(void);
HAL_StatusTypeDef CellularEC20_Disconnect(void);
HAL_StatusTypeDef CellularEC20_SendATCommand(const char *cmd, char *response, 
                                            uint32_t timeout, const char *expected);
CellularState_t CellularEC20_GetState(void);
CellularInfo_t CellularEC20_GetInfo(void);
HAL_StatusTypeDef CellularEC20_HTTP_Get(const char *url, char *response, 
                                       uint32_t max_len);
HAL_StatusTypeDef CellularEC20_FTP_Download(const char *server, 
                                           const char *filename);

#endif
c 复制代码
// cellular_ec20.c
#include "cellular_ec20.h"
#include "usart.h"
#include <string.h>
#include <stdio.h>

static UART_HandleTypeDef huart3;
static CellularState_t cellular_state = CELLULAR_STATE_POWER_OFF;
static CellularInfo_t cellular_info = {0};
static char at_buffer[512];

HAL_StatusTypeDef CellularEC20_Init(void) {
    // 初始化UART
    huart3.Instance = USART3;
    huart3.Init.BaudRate = 115200;
    huart3.Init.WordLength = UART_WORDLENGTH_8B;
    huart3.Init.StopBits = UART_STOPBITS_1;
    huart3.Init.Parity = UART_PARITY_NONE;
    huart3.Init.Mode = UART_MODE_TX_RX;
    huart3.Init.HwFlowCtl = UART_HWCONTROL_NONE;
    huart3.Init.OverSampling = UART_OVERSAMPLING_16;
    
    if (HAL_UART_Init(&huart3) != HAL_OK) {
        return HAL_ERROR;
    }
    
    // 开机序列
    HAL_GPIO_WritePin(EC20_PWRKEY_PORT, EC20_PWRKEY_PIN, GPIO_PIN_RESET);
    HAL_Delay(1000);
    HAL_GPIO_WritePin(EC20_PWRKEY_PORT, EC20_PWRKEY_PIN, GPIO_PIN_SET);
    HAL_Delay(3000);
    
    // 等待模块就绪
    for (int i = 0; i < 10; i++) {
        if (CellularEC20_SendATCommand("AT", "OK", 1000, "OK") == HAL_OK) {
            cellular_state = CELLULAR_STATE_INIT;
            break;
        }
        HAL_Delay(1000);
    }
    
    if (cellular_state != CELLULAR_STATE_INIT) {
        return HAL_ERROR;
    }
    
    // 配置模块
    CellularEC20_SendATCommand("ATE0", "OK", 1000, "OK");  // 关闭回显
    CellularEC20_SendATCommand("AT+CMEE=2", "OK", 1000, "OK"); // 启用详细错误
    
    return HAL_OK;
}

HAL_StatusTypeDef CellularEC20_Connect(void) {
    char response[256];
    
    // 检查SIM卡
    if (CellularEC20_SendATCommand("AT+CPIN?", "READY", 5000, "READY") != HAL_OK) {
        cellular_state = CELLULAR_STATE_ERROR;
        return HAL_ERROR;
    }
    cellular_state = CELLULAR_STATE_SIM_CHECK;
    
    // 获取IMSI
    if (CellularEC20_SendATCommand("AT+CIMI", NULL, 3000, NULL) == HAL_OK) {
        strncpy((char*)cellular_info.imsi, response, sizeof(cellular_info.imsi)-1);
    }
    
    // 获取信号强度
    if (CellularEC20_SendATCommand("AT+CSQ", "+CSQ:", 3000, NULL) == HAL_OK) {
        sscanf(response, "+CSQ: %hhd", &cellular_info.signal_strength);
    }
    
    // 注册网络
    if (CellularEC20_SendATCommand("AT+CREG?", "+CREG: 0,1", 30000, "+CREG: 0,1") != HAL_OK) {
        return HAL_ERROR;
    }
    cellular_state = CELLULAR_STATE_NETWORK_REG;
    
    // 附着GPRS
    if (CellularEC20_SendATCommand("AT+CGATT=1", "OK", 10000, "OK") != HAL_OK) {
        return HAL_ERROR;
    }
    cellular_state = CELLULAR_STATE_GPRS_ATTACH;
    
    // 设置APN(根据运营商)
    CellularEC20_SendATCommand("AT+CGDCONT=1,\"IP\",\"CMNET\"", "OK", 3000, "OK");
    
    // 激活PDP上下文
    if (CellularEC20_SendATCommand("AT+QIACT=1", "OK", 30000, "OK") != HAL_OK) {
        return HAL_ERROR;
    }
    
    // 获取IP地址
    if (CellularEC20_SendATCommand("AT+QIACT?", "+QIACT:", 3000, NULL) == HAL_OK) {
        char *ip_start = strstr(response, "\"");
        if (ip_start) {
            char *ip_end = strstr(ip_start + 1, "\"");
            if (ip_end) {
                strncpy((char*)cellular_info.ip_address, ip_start + 1, 
                       ip_end - ip_start - 1);
            }
        }
    }
    
    cellular_state = CELLULAR_STATE_CONNECTED;
    return HAL_OK;
}

HAL_StatusTypeDef CellularEC20_SendATCommand(const char *cmd, char *response, 
                                           uint32_t timeout, const char *expected) {
    char tx_buffer[128];
    char rx_buffer[512] = {0};
    uint32_t start_tick = HAL_GetTick();
    uint32_t rx_index = 0;
    
    // 发送命令
    snprintf(tx_buffer, sizeof(tx_buffer), "%s\r\n", cmd);
    HAL_UART_Transmit(&huart3, (uint8_t*)tx_buffer, strlen(tx_buffer), HAL_MAX_DELAY);
    
    // 接收响应
    while ((HAL_GetTick() - start_tick) < timeout) {
        if (HAL_UART_Receive(&huart3, (uint8_t*)&rx_buffer[rx_index], 1, 10) == HAL_OK) {
            if (rx_buffer[rx_index] == '\n') {
                rx_buffer[rx_index] = '\0';
                
                // 检查是否有预期响应
                if (expected && strstr(rx_buffer, expected)) {
                    if (response) {
                        strcpy(response, rx_buffer);
                    }
                    return HAL_OK;
                }
                
                // 检查错误
                if (strstr(rx_buffer, "ERROR")) {
                    return HAL_ERROR;
                }
                
                rx_index = 0;
            } else {
                rx_index++;
                if (rx_index >= sizeof(rx_buffer) - 1) {
                    rx_index = 0;
                }
            }
        }
    }
    
    return HAL_TIMEOUT;
}

4. MQTT客户端实现 (mqtt_client.c/h)

c 复制代码
// mqtt_client.h
#ifndef __MQTT_CLIENT_H
#define __MQTT_CLIENT_H

#include <stdint.h>
#include <stdbool.h>

typedef enum {
    MQTT_DISCONNECTED = 0,
    MQTT_CONNECTING,
    MQTT_CONNECTED,
    MQTT_PUBLISHING,
    MQTT_SUBSCRIBING
} MQTT_State_t;

typedef enum {
    MQTT_OK = 0,
    MQTT_ERROR,
    MQTT_TIMEOUT,
    MQTT_NETWORK_ERROR
} MQTT_Result_t;

typedef struct {
    char server[64];
    uint16_t port;
    char client_id[32];
    char username[32];
    char password[32];
    uint16_t keepalive;
    bool clean_session;
} MQTT_Config_t;

typedef struct {
    char topic[128];
    uint8_t *payload;
    uint16_t payload_len;
    uint8_t qos;
    bool retained;
} MQTT_Message_t;

MQTT_Result_t MQTTClient_Init(MQTT_Config_t *config);
MQTT_Result_t MQTTClient_Connect(void);
MQTT_Result_t MQTTClient_Disconnect(void);
MQTT_Result_t MQTTClient_Publish(const char *topic, uint8_t *payload, 
                                uint16_t len, uint8_t qos);
MQTT_Result_t MQTTClient_Subscribe(const char *topic, uint8_t qos);
MQTT_Result_t MQTTClient_CheckConnection(void);
void MQTTClient_SetCallback(void (*callback)(char*, uint8_t*, unsigned int));

// 专为LoRa网关设计的数据发布函数
MQTT_Result_t MQTTClient_PublishData(LoRaPacket_t *packet);

#endif
c 复制代码
// mqtt_client.c
#include "mqtt_client.h"
#include "cellular_ec20.h"
#include "cJSON.h"
#include <string.h>
#include <time.h>

static MQTT_State_t mqtt_state = MQTT_DISCONNECTED;
static MQTT_Config_t mqtt_config;
static uint16_t packet_id = 0;
static void (*message_callback)(char*, uint8_t*, unsigned int) = NULL;

// 阿里云IoT平台配置
#define ALIYUN_PRODUCT_KEY    "your_product_key"
#define ALIYUN_DEVICE_NAME    "your_device_name"
#define ALIYUN_DEVICE_SECRET  "your_device_secret"

MQTT_Result_t MQTTClient_Init(MQTT_Config_t *config) {
    if (config == NULL) {
        // 使用默认配置(阿里云)
        strcpy(mqtt_config.server, ALIYUN_PRODUCT_KEY ".iot-as-mqtt.cn-shanghai.aliyuncs.com");
        mqtt_config.port = 1883;
        snprintf(mqtt_config.client_id, sizeof(mqtt_config.client_id), 
                "%s|securemode=3,signmethod=hmacsha256|", ALIYUN_DEVICE_NAME);
        snprintf(mqtt_config.username, sizeof(mqtt_config.username),
                "%s&%s", ALIYUN_DEVICE_NAME, ALIYUN_PRODUCT_KEY);
        
        // 生成密码(根据阿里云规则)
        GenerateAliyunPassword();
    } else {
        memcpy(&mqtt_config, config, sizeof(MQTT_Config_t));
    }
    
    mqtt_state = MQTT_DISCONNECTED;
    return MQTT_OK;
}

MQTT_Result_t MQTTClient_Connect(void) {
    char command[256];
    char response[512];
    
    if (CellularEC20_GetState() != CELLULAR_STATE_CONNECTED) {
        return MQTT_NETWORK_ERROR;
    }
    
    mqtt_state = MQTT_CONNECTING;
    
    // 建立TCP连接
    snprintf(command, sizeof(command), "AT+QIOPEN=1,0,\"TCP\",\"%s\",%d,0,1",
            mqtt_config.server, mqtt_config.port);
    
    if (CellularEC20_SendATCommand(command, response, 30000, "+QIOPEN: 0,0") != HAL_OK) {
        mqtt_state = MQTT_DISCONNECTED;
        return MQTT_NETWORK_ERROR;
    }
    
    // 发送MQTT CONNECT报文
    uint8_t connect_packet[256];
    uint16_t packet_len = BuildMQTTConnectPacket(connect_packet);
    
    // 通过EC20发送数据
    snprintf(command, sizeof(command), "AT+QISEND=0,%d", packet_len);
    CellularEC20_SendATCommand(command, response, 3000, ">");
    
    // 发送实际数据
    char hex_buffer[512];
    BytesToHex(connect_packet, packet_len, hex_buffer);
    CellularEC20_SendATCommand(hex_buffer, response, 5000, "SEND OK");
    
    // 等待CONNACK
    if (WaitForMQTTResponse("CONNACK", 5000) == HAL_OK) {
        mqtt_state = MQTT_CONNECTED;
        return MQTT_OK;
    }
    
    mqtt_state = MQTT_DISCONNECTED;
    return MQTT_ERROR;
}

MQTT_Result_t MQTTClient_PublishData(LoRaPacket_t *packet) {
    cJSON *root = cJSON_CreateObject();
    char json_buffer[512];
    MQTT_Result_t result;
    
    if (mqtt_state != MQTT_CONNECTED) {
        if (MQTTClient_Connect() != MQTT_OK) {
            return MQTT_ERROR;
        }
    }
    
    // 构建JSON数据
    cJSON_AddNumberToObject(root, "timestamp", packet->timestamp);
    cJSON_AddStringToObject(root, "dev_addr", BytesToHexString(packet->dev_addr, 4));
    cJSON_AddNumberToObject(root, "rssi", packet->rssi);
    cJSON_AddNumberToObject(root, "snr", packet->snr);
    cJSON_AddNumberToObject(root, "sf", packet->data_rate);
    
    // 添加payload(Base64编码)
    char *payload_b64 = Base64Encode(packet->payload, packet->payload_len);
    cJSON_AddStringToObject(root, "payload", payload_b64);
    free(payload_b64);
    
    // 转换为字符串
    char *json_str = cJSON_PrintUnformatted(root);
    cJSON_Delete(root);
    
    // 发布到MQTT
    char topic[128];
    snprintf(topic, sizeof(topic), "/sys/%s/%s/thing/event/property/post",
            ALIYUN_PRODUCT_KEY, ALIYUN_DEVICE_NAME);
    
    result = MQTTClient_Publish(topic, (uint8_t*)json_str, strlen(json_str), 1);
    
    free(json_str);
    return result;
}

static uint16_t BuildMQTTConnectPacket(uint8_t *buffer) {
    uint8_t *ptr = buffer;
    uint16_t remaining_len;
    
    // Fixed header
    *ptr++ = 0x10;  // CONNECT
    
    // Remaining length (计算后填写)
    uint8_t *len_ptr = ptr++;
    
    // Variable header
    *ptr++ = 0x00;  // Protocol name length MSB
    *ptr++ = 0x04;  // Protocol name length LSB
    *ptr++ = 'M';
    *ptr++ = 'Q';
    *ptr++ = 'T';
    *ptr++ = 'T';
    *ptr++ = 0x04;  // Protocol level
    *ptr++ = 0xC2;  // Connect flags (clean session, will, username, password)
    *ptr++ = (mqtt_config.keepalive >> 8) & 0xFF;
    *ptr++ = mqtt_config.keepalive & 0xFF;
    
    // Payload: Client ID
    uint16_t client_id_len = strlen(mqtt_config.client_id);
    *ptr++ = (client_id_len >> 8) & 0xFF;
    *ptr++ = client_id_len & 0xFF;
    memcpy(ptr, mqtt_config.client_id, client_id_len);
    ptr += client_id_len;
    
    // Payload: Username
    uint16_t username_len = strlen(mqtt_config.username);
    *ptr++ = (username_len >> 8) & 0xFF;
    *ptr++ = username_len & 0xFF;
    memcpy(ptr, mqtt_config.username, username_len);
    ptr += username_len;
    
    // Payload: Password
    uint16_t password_len = strlen(mqtt_config.password);
    *ptr++ = (password_len >> 8) & 0xFF;
    *ptr++ = password_len & 0xFF;
    memcpy(ptr, mqtt_config.password, password_len);
    ptr += password_len;
    
    // 计算剩余长度并回填
    remaining_len = (ptr - buffer) - 2; // 减去固定头部
    *len_ptr = remaining_len;
    
    return (ptr - buffer);
}

5. OTA升级管理器 (ota_manager.c/h)

c 复制代码
// ota_manager.h
#ifndef __OTA_MANAGER_H
#define __OTA_MANAGER_H

#include <stdint.h>
#include <stdbool.h>

typedef enum {
    OTA_IDLE = 0,
    OTA_CHECKING,
    OTA_DOWNLOADING,
    OTA_VERIFYING,
    OTA_UPDATING,
    OTA_SUCCESS,
    OTA_FAILED
} OTA_State_t;

typedef struct {
    char server_url[256];
    char firmware_path[128];
    char current_version[32];
    char new_version[32];
    uint32_t file_size;
    uint32_t crc32;
    bool force_update;
} OTA_Config_t;

void OTAManager_Init(void);
OTA_State_t OTAManager_GetState(void);
bool OTAManager_CheckForUpdate(void);
bool OTAManager_DownloadFirmware(void);
bool OTAManager_VerifyFirmware(void);
bool OTAManager_ApplyUpdate(void);
void OTAManager_Rollback(void);
void OTAManager_SetCallback(void (*callback)(OTA_State_t, uint8_t));

#endif
c 复制代码
// ota_manager.c
#include "ota_manager.h"
#include "cellular_ec20.h"
#include "sd_storage.h"
#include <string.h>
#include "flash_if.h"

static OTA_State_t ota_state = OTA_IDLE;
static OTA_Config_t ota_config;
static void (*ota_callback)(OTA_State_t, uint8_t) = NULL;
static uint8_t ota_progress = 0;

// 固件分区定义
#define FLASH_APP_ADDR        0x08000000  // 应用程序起始地址
#define FLASH_APP_SIZE        (512 * 1024) // 512KB
#define FLASH_BACKUP_ADDR     0x08080000  // 备份分区
#define FLASH_UPDATE_ADDR     0x080C0000  // 更新分区

void OTAManager_Init(void) {
    // 读取当前版本
    strcpy(ota_config.current_version, FIRMWARE_VERSION);
    
    // 默认服务器配置
    strcpy(ota_config.server_url, "http://ota.yourcompany.com/gateway");
    strcpy(ota_config.firmware_path, "/firmware/latest.bin");
    
    // 检查是否有待应用的更新
    CheckPendingUpdate();
}

bool OTAManager_CheckForUpdate(void) {
    char response[1024];
    char url[256];
    cJSON *root, *version_json;
    
    if (CellularEC20_GetState() != CELLULAR_STATE_CONNECTED) {
        return false;
    }
    
    ota_state = OTA_CHECKING;
    if (ota_callback) ota_callback(ota_state, 0);
    
    // 构建检查更新的URL
    snprintf(url, sizeof(url), "%s/check?version=%s&hw=%s",
            ota_config.server_url, ota_config.current_version, "STM32L476");
    
    // HTTP GET请求
    if (CellularEC20_HTTP_Get(url, response, sizeof(response)) != HAL_OK) {
        ota_state = OTA_IDLE;
        return false;
    }
    
    // 解析JSON响应
    root = cJSON_Parse(response);
    if (!root) {
        ota_state = OTA_IDLE;
        return false;
    }
    
    version_json = cJSON_GetObjectItem(root, "latest_version");
    if (!version_json) {
        cJSON_Delete(root);
        ota_state = OTA_IDLE;
        return false;
    }
    
    strncpy(ota_config.new_version, version_json->valuestring, 
           sizeof(ota_config.new_version)-1);
    
    // 比较版本
    if (strcmp(ota_config.new_version, ota_config.current_version) > 0) {
        // 获取文件信息
        cJSON *size_json = cJSON_GetObjectItem(root, "file_size");
        cJSON *crc_json = cJSON_GetObjectItem(root, "crc32");
        cJSON *force_json = cJSON_GetObjectItem(root, "force_update");
        
        if (size_json) ota_config.file_size = size_json->valueint;
        if (crc_json) ota_config.crc32 = crc_json->valueint;
        if (force_json) ota_config.force_update = force_json->valueint;
        
        cJSON_Delete(root);
        return true;
    }
    
    cJSON_Delete(root);
    ota_state = OTA_IDLE;
    return false;
}

bool OTAManager_DownloadFirmware(void) {
    char url[256];
    char command[128];
    char response[256];
    uint32_t bytes_received = 0;
    uint8_t buffer[1024];
    FIL file;
    
    ota_state = OTA_DOWNLOADING;
    if (ota_callback) ota_callback(ota_state, 0);
    
    // 在SD卡上创建临时文件
    if (SDStorage_OpenFile(&file, "/update/temp.bin", FA_CREATE_ALWAYS | FA_WRITE) != FR_OK) {
        ota_state = OTA_FAILED;
        return false;
    }
    
    // 构建下载URL
    snprintf(url, sizeof(url), "%s%s", ota_config.server_url, ota_config.firmware_path);
    
    // 使用EC20的HTTP服务下载
    snprintf(command, sizeof(command), 
            "AT+QHTTPURL=%d,80", strlen(url));
    CellularEC20_SendATCommand(command, response, 3000, "CONNECT");
    
    // 发送URL
    CellularEC20_SendATCommand(url, response, 3000, "OK");
    
    // 开始GET请求
    CellularEC20_SendATCommand("AT+QHTTPGET=120", response, 3000, "+QHTTPGET:");
    
    // 读取数据
    while (bytes_received < ota_config.file_size) {
        uint32_t to_read = sizeof(buffer);
        if (bytes_received + to_read > ota_config.file_size) {
            to_read = ota_config.file_size - bytes_received;
        }
        
        // 读取数据块
        snprintf(command, sizeof(command), "AT+QHTTPREAD=%d", to_read);
        CellularEC20_SendATCommand(command, response, 5000, "+QHTTPREAD:");
        
        // 解析并保存数据
        // 注意:需要根据EC20的实际响应格式解析
        
        UINT bytes_written;
        SDStorage_WriteFile(&file, buffer, to_read, &bytes_written);
        
        bytes_received += to_read;
        ota_progress = (bytes_received * 100) / ota_config.file_size;
        
        if (ota_callback) ota_callback(ota_state, ota_progress);
    }
    
    SDStorage_CloseFile(&file);
    
    if (bytes_received == ota_config.file_size) {
        ota_state = OTA_VERIFYING;
        if (ota_callback) ota_callback(ota_state, 0);
        return true;
    }
    
    ota_state = OTA_FAILED;
    return false;
}

bool OTAManager_ApplyUpdate(void) {
    ota_state = OTA_UPDATING;
    if (ota_callback) ota_callback(ota_state, 100);
    
    // 1. 备份当前固件
    if (!BackupCurrentFirmware()) {
        ota_state = OTA_FAILED;
        return false;
    }
    
    // 2. 擦除应用程序区域
    if (FLASH_If_Erase(FLASH_APP_ADDR, FLASH_APP_SIZE) != FLASHIF_OK) {
        ota_state = OTA_FAILED;
        return false;
    }
    
    // 3. 写入新固件
    if (!ProgramNewFirmware()) {
        // 更新失败,恢复备份
        RestoreBackupFirmware();
        ota_state = OTA_FAILED;
        return false;
    }
    
    // 4. 验证固件
    if (!VerifyFirmwareIntegrity()) {
        RestoreBackupFirmware();
        ota_state = OTA_FAILED;
        return false;
    }
    
    // 5. 设置启动标志
    SetBootFlag(BOOT_NEW_FIRMWARE);
    
    ota_state = OTA_SUCCESS;
    if (ota_callback) ota_callback(ota_state, 100);
    
    // 重启系统
    HAL_NVIC_SystemReset();
    
    return true;
}

6. SD卡存储管理器 (sd_storage.c/h)

c 复制代码
// sd_storage.h
#ifndef __SD_STORAGE_H
#define __SD_STORAGE_H

#include "ff.h"
#include "diskio.h"
#include <stdbool.h>

#define MAX_RETRY_ATTEMPTS     3
#define LOG_FILE_SIZE          (1024 * 1024) // 1MB
#define MAX_CACHE_FILES        100

typedef struct {
    char filename[32];
    uint32_t timestamp;
    uint32_t size;
    uint8_t retry_count;
    bool uploaded;
} CacheFileInfo_t;

bool SDStorage_Init(void);
bool SDStorage_IsAvailable(void);
uint32_t SDStorage_GetFreeSpace(void);
bool SDStorage_SavePacket(LoRaPacket_t *packet);
bool SDStorage_ReadPacket(FIL *file, LoRaPacket_t *packet);
bool SDStorage_MarkForRetransmission(LoRaPacket_t *packet);
bool SDStorage_GetNextPendingPacket(LoRaPacket_t *packet);
bool SDStorage_CreateLogEntry(const char *log_message);
bool SDStorage_RotateLogFiles(void);
bool SDStorage_Format(void);

#endif
c 复制代码
// sd_storage.c
#include "sd_storage.h"
#include "rtc.h"
#include <string.h>
#include <stdio.h>

static FATFS fs;
static bool sd_initialized = false;
static CacheFileInfo_t cache_files[MAX_CACHE_FILES];
static uint16_t cache_count = 0;

bool SDStorage_Init(void) {
    FRESULT res;
    char path[4] = "0:/";
    
    // 挂载文件系统
    res = f_mount(&fs, path, 1);
    if (res != FR_OK) {
        // 尝试格式化
        if (SDStorage_Format() == false) {
            return false;
        }
        res = f_mount(&fs, path, 1);
        if (res != FR_OK) {
            return false;
        }
    }
    
    // 创建必要的目录
    f_mkdir("0:/data");
    f_mkdir("0:/logs");
    f_mkdir("0:/update");
    f_mkdir("0:/config");
    
    // 加载缓存文件索引
    SDStorage_LoadCacheIndex();
    
    sd_initialized = true;
    return true;
}

bool SDStorage_SavePacket(LoRaPacket_t *packet) {
    FIL file;
    char filename[32];
    UINT bytes_written;
    FRESULT res;
    
    if (!sd_initialized) {
        return false;
    }
    
    // 生成文件名:YYYYMMDD_HHMMSS_devaddr.bin
    RTC_TimeTypeDef sTime;
    RTC_DateTypeDef sDate;
    HAL_RTC_GetTime(&hrtc, &sTime, RTC_FORMAT_BIN);
    HAL_RTC_GetDate(&hrtc, &sDate, RTC_FORMAT_BIN);
    
    snprintf(filename, sizeof(filename), "0:/data/%04d%02d%02d_%02d%02d%02d_%02x%02x%02x%02x.bin",
            sDate.Year + 2000, sDate.Month, sDate.Date,
            sTime.Hours, sTime.Minutes, sTime.Seconds,
            packet->dev_addr[0], packet->dev_addr[1],
            packet->dev_addr[2], packet->dev_addr[3]);
    
    // 打开文件
    res = f_open(&file, filename, FA_CREATE_ALWAYS | FA_WRITE);
    if (res != FR_OK) {
        return false;
    }
    
    // 写入数据包
    res = f_write(&file, packet, sizeof(LoRaPacket_t), &bytes_written);
    f_close(&file);
    
    if (res != FR_OK || bytes_written != sizeof(LoRaPacket_t)) {
        return false;
    }
    
    // 添加到缓存索引
    SDStorage_AddToCacheIndex(filename, packet->timestamp, 
                             sizeof(LoRaPacket_t), false);
    
    return true;
}

bool SDStorage_GetNextPendingPacket(LoRaPacket_t *packet) {
    FIL file;
    FRESULT res;
    UINT bytes_read;
    
    // 查找未上传的文件
    for (int i = 0; i < cache_count; i++) {
        if (!cache_files[i].uploaded && 
            cache_files[i].retry_count < MAX_RETRY_ATTEMPTS) {
            
            res = f_open(&file, cache_files[i].filename, FA_READ);
            if (res != FR_OK) {
                cache_files[i].retry_count++;
                continue;
            }
            
            res = f_read(&file, packet, sizeof(LoRaPacket_t), &bytes_read);
            f_close(&file);
            
            if (res == FR_OK && bytes_read == sizeof(LoRaPacket_t)) {
                return true;
            }
            
            cache_files[i].retry_count++;
        }
    }
    
    return false;
}

bool SDStorage_MarkForRetransmission(LoRaPacket_t *packet) {
    // 在缓存索引中标记为未上传
    for (int i = 0; i < cache_count; i++) {
        // 根据时间戳或内容找到对应的文件
        if (cache_files[i].timestamp == packet->timestamp) {
            cache_files[i].uploaded = false;
            cache_files[i].retry_count++;
            return true;
        }
    }
    
    return false;
}

static void SDStorage_LoadCacheIndex(void) {
    FIL file;
    FRESULT res;
    UINT bytes_read;
    
    res = f_open(&file, "0:/cache.idx", FA_READ);
    if (res != FR_OK) {
        // 索引文件不存在,创建新的
        SDStorage_RebuildCacheIndex();
        return;
    }
    
    // 读取缓存索引
    res = f_read(&file, &cache_count, sizeof(cache_count), &bytes_read);
    if (res == FR_OK && cache_count > 0) {
        f_read(&file, cache_files, 
               sizeof(CacheFileInfo_t) * cache_count, &bytes_read);
    }
    
    f_close(&file);
}

static void SDStorage_RebuildCacheIndex(void) {
    DIR dir;
    FILINFO fno;
    FRESULT res;
    
    cache_count = 0;
    
    res = f_findfirst(&dir, &fno, "0:/data", "*.bin");
    while (res == FR_OK && fno.fname[0] != 0 && cache_count < MAX_CACHE_FILES) {
        if (fno.fattrib & AM_DIR) {
            continue;
        }
        
        // 添加到缓存索引
        snprintf(cache_files[cache_count].filename, 
                sizeof(cache_files[cache_count].filename),
                "0:/data/%s", fno.fname);
        cache_files[cache_count].size = fno.fsize;
        cache_files[cache_count].timestamp = ExtractTimestamp(fno.fname);
        cache_files[cache_count].uploaded = false;
        cache_files[cache_count].retry_count = 0;
        
        cache_count++;
        res = f_findnext(&dir, &fno);
    }
    
    f_closedir(&dir);
    SDStorage_SaveCacheIndex();
}

7. 功耗管理器 (power_manager.c/h)

c 复制代码
// power_manager.h
#ifndef __POWER_MANAGER_H
#define __POWER_MANAGER_H

#include "stm32l4xx_hal.h"

typedef enum {
    POWER_MODE_ACTIVE = 0,
    POWER_MODE_SLEEP,
    POWER_MODE_STOP,
    POWER_MODE_STANDBY
} PowerMode_t;

typedef struct {
    PowerMode_t current_mode;
    uint32_t battery_voltage;
    uint8_t battery_percentage;
    bool charging;
    uint32_t active_time;
    uint32_t sleep_time;
    uint32_t total_energy;
} PowerStatus_t;

void PowerManager_Init(void);
void PowerManager_SetMode(PowerMode_t mode);
PowerMode_t PowerManager_GetMode(void);
PowerStatus_t PowerManager_GetStatus(void);
void PowerManager_EnterIdleMode(void);
void PowerManager_WakeUp(void);
void PowerManager_MeasureBattery(void);
void PowerManager_OptimizeForBattery(void);

#endif
c 复制代码
// power_manager.c
#include "power_manager.h"
#include "rtc.h"
#include "adc.h"
#include <math.h>

static PowerMode_t current_power_mode = POWER_MODE_ACTIVE;
static PowerStatus_t power_status;
static RTC_HandleTypeDef hrtc;

void PowerManager_Init(void) {
    // 初始化RTC用于唤醒
    __HAL_RCC_RTC_ENABLE();
    
    hrtc.Instance = RTC;
    hrtc.Init.HourFormat = RTC_HOURFORMAT_24;
    hrtc.Init.AsynchPrediv = 127;
    hrtc.Init.SynchPrediv = 255;
    hrtc.Init.OutPut = RTC_OUTPUT_DISABLE;
    hrtc.Init.OutPutRemap = RTC_OUTPUT_REMAP_NONE;
    hrtc.Init.OutPutPolarity = RTC_OUTPUT_POLARITY_HIGH;
    hrtc.Init.OutPutType = RTC_OUTPUT_TYPE_OPENDRAIN;
    
    if (HAL_RTC_Init(&hrtc) != HAL_OK) {
        Error_Handler();
    }
    
    // 初始化ADC用于电池电压测量
    ADC_ChannelConfTypeDef sConfig = {0};
    hadc1.Instance = ADC1;
    hadc1.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV1;
    hadc1.Init.Resolution = ADC_RESOLUTION_12B;
    hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
    hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE;
    hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
    hadc1.Init.LowPowerAutoWait = DISABLE;
    hadc1.Init.ContinuousConvMode = DISABLE;
    hadc1.Init.NbrOfConversion = 1;
    hadc1.Init.DiscontinuousConvMode = DISABLE;
    hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
    hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
    hadc1.Init.DMAContinuousRequests = DISABLE;
    hadc1.Init.Overrun = ADC_OVR_DATA_PRESERVED;
    hadc1.Init.OversamplingMode = DISABLE;
    
    if (HAL_ADC_Init(&hadc1) != HAL_OK) {
        Error_Handler();
    }
    
    sConfig.Channel = ADC_CHANNEL_VREFINT;
    sConfig.Rank = ADC_REGULAR_RANK_1;
    sConfig.SamplingTime = ADC_SAMPLETIME_247CYCLES_5;
    sConfig.SingleDiff = ADC_SINGLE_ENDED;
    sConfig.OffsetNumber = ADC_OFFSET_NONE;
    sConfig.Offset = 0;
    
    if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK) {
        Error_Handler();
    }
    
    // 初始测量
    PowerManager_MeasureBattery();
}

void PowerManager_SetMode(PowerMode_t mode) {
    if (mode == current_power_mode) {
        return;
    }
    
    switch (mode) {
        case POWER_MODE_SLEEP:
            EnterSleepMode();
            break;
            
        case POWER_MODE_STOP:
            EnterStopMode();
            break;
            
        case POWER_MODE_STANDBY:
            EnterStandbyMode();
            break;
            
        case POWER_MODE_ACTIVE:
        default:
            // 保持活动状态
            break;
    }
    
    current_power_mode = mode;
}

void PowerManager_EnterIdleMode(void) {
    // 检查是否有待处理的任务
    if (!HasPendingTasks()) {
        // 根据电池电量选择低功耗模式
        if (power_status.battery_percentage < 30) {
            PowerManager_SetMode(POWER_MODE_STOP);
        } else {
            PowerManager_SetMode(POWER_MODE_SLEEP);
        }
    }
}

static void EnterSleepMode(void) {
    // 关闭不必要的外设
    HAL_GPIO_WritePin(LORA_SPI_CS_PORT, LORA_SPI_CS_PIN, GPIO_PIN_SET);
    
    // 设置唤醒源(RTC闹钟)
    RTC_AlarmTypeDef sAlarm = {0};
    sAlarm.BinaryAutoClr = RTC_ALARMSUBSECONDBIN_AUTOCLR_NO;
    sAlarm.AlarmTime.Seconds = 30;  // 30秒后唤醒
    sAlarm.AlarmMask = RTC_ALARMMASK_NONE;
    sAlarm.AlarmSubSecondMask = RTC_ALARMSUBSECONDBINMASK_NONE;
    sAlarm.Alarm = RTC_ALARM_A;
    sAlarm.AlarmDateWeekDaySel = RTC_ALARMDATEWEEKDAYSEL_DATE;
    sAlarm.AlarmDateWeekDay = 1;
    
    if (HAL_RTC_SetAlarm_IT(&hrtc, &sAlarm, RTC_FORMAT_BIN) != HAL_OK) {
        Error_Handler();
    }
    
    // 进入Sleep模式
    HAL_PWR_EnterSLEEPMode(PWR_MAINREGULATOR_ON, PWR_SLEEPENTRY_WFI);
}

static void EnterStopMode(void) {
    // 保存上下文
    SaveContextBeforeSleep();
    
    // 配置唤醒引脚
    __HAL_RCC_PWR_CLK_ENABLE();
    
    // 配置GPIO为模拟输入以降低功耗
    GPIO_AnalogConfig();
    
    // 进入Stop模式
    HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);
    
    // 唤醒后的恢复
    SystemClock_Config(); // 重新配置系统时钟
    RestoreContextAfterSleep();
}

void PowerManager_MeasureBattery(void) {
    uint32_t adc_value;
    float voltage;
    
    // 启用ADC
    HAL_ADC_Start(&hadc1);
    HAL_ADC_PollForConversion(&hadc1, HAL_MAX_DELAY);
    adc_value = HAL_ADC_GetValue(&hadc1);
    HAL_ADC_Stop(&hadc1);
    
    // 计算电压(使用内部参考电压1.2V)
    voltage = (adc_value * 1.2f) / 4095.0f;
    
    // 考虑分压电阻(假设为1/2分压)
    voltage *= 2.0f;
    
    power_status.battery_voltage = (uint32_t)(voltage * 1000); // 转换为mV
    
    // 计算电量百分比(假设锂电池3.0V-4.2V)
    if (voltage >= 4.2f) {
        power_status.battery_percentage = 100;
    } else if (voltage <= 3.0f) {
        power_status.battery_percentage = 0;
    } else {
        power_status.battery_percentage = (uint8_t)((voltage - 3.0f) * 100 / 1.2f);
    }
}

void PowerManager_OptimizeForBattery(void) {
    // 根据电池电量调整系统行为
    if (power_status.battery_percentage < 20) {
        // 低电量模式
        LoRaSX130x_SetTransmitPower(10); // 降低发射功率
        SetDataUploadInterval(300);      // 延长上传间隔为5分钟
        DisableNonCriticalFeatures();
    } else if (power_status.battery_percentage < 50) {
        // 中等电量模式
        LoRaSX130x_SetTransmitPower(14);
        SetDataUploadInterval(120);      // 2分钟
    } else {
        // 正常模式
        LoRaSX130x_SetTransmitPower(17);
        SetDataUploadInterval(60);       // 1分钟
    }
}

8. FreeRTOS配置文件 (FreeRTOSConfig.h)

c 复制代码
#ifndef FREERTOS_CONFIG_H
#define FREERTOS_CONFIG_H

#include "stm32l4xx_hal.h"

#define configUSE_PREEMPTION                    1
#define configUSE_IDLE_HOOK                     1
#define configUSE_TICK_HOOK                     0
#define configCPU_CLOCK_HZ                      (SystemCoreClock)
#define configTICK_RATE_HZ                      ((TickType_t)1000)
#define configMAX_PRIORITIES                    (7)
#define configMINIMAL_STACK_SIZE                ((uint16_t)128)
#define configTOTAL_HEAP_SIZE                   ((size_t)32768)
#define configMAX_TASK_NAME_LEN                 (16)
#define configUSE_TRACE_FACILITY                1
#define configUSE_16_BIT_TICKS                  0
#define configIDLE_SHOULD_YIELD                 1
#define configUSE_MUTEXES                       1
#define configUSE_RECURSIVE_MUTEXES             1
#define configUSE_COUNTING_SEMAPHORES           1
#define configUSE_ALTERNATIVE_API               0
#define configQUEUE_REGISTRY_SIZE               10
#define configUSE_QUEUE_SETS                    1
#define configUSE_TIME_SLICING                  1
#define configUSE_NEWLIB_REENTRANT              0
#define configENABLE_BACKWARD_COMPATIBILITY     0
#define configNUM_THREAD_LOCAL_STORAGE_POINTERS 5

#define configUSE_TIMERS                        1
#define configTIMER_TASK_PRIORITY               (configMAX_PRIORITIES - 1)
#define configTIMER_QUEUE_LENGTH                10
#define configTIMER_TASK_STACK_DEPTH            (configMINIMAL_STACK_SIZE * 2)

#define configMAX_CO_ROUTINE_PRIORITIES         (2)
#define configUSE_CO_ROUTINES                   0

#define configUSE_PORT_OPTIMISED_TASK_SELECTION 1

#define configCHECK_FOR_STACK_OVERFLOW          2
#define configRECORD_STACK_HIGH_ADDRESS         1

#define configGENERATE_RUN_TIME_STATS           1

#define configUSE_APPLICATION_TASK_TAG          1

#define configSUPPORT_STATIC_ALLOCATION         1
#define configSUPPORT_DYNAMIC_ALLOCATION        1

extern uint32_t SystemCoreClock;

#define xPortPendSVHandler    PendSV_Handler
#define vPortSVCHandler       SVC_Handler

#define configKERNEL_INTERRUPT_PRIORITY         (0xF0)
#define configMAX_SYSCALL_INTERRUPT_PRIORITY    (0x10)

#define configASSERT(x) if ((x) == 0) {taskDISABLE_INTERRUPTS(); for (;;);}

#endif

9. 主函数 (main.c)

c 复制代码
#include "main.h"
#include "cmsis_os.h"
#include "gateway.h"
#include "power_manager.h"

RTC_HandleTypeDef hrtc;
SD_HandleTypeDef hsd;
UART_HandleTypeDef huart3;
SPI_HandleTypeDef hspi1;

int main(void) {
    HAL_Init();
    SystemClock_Config();
    MX_GPIO_Init();
    MX_RTC_Init();
    MX_SDMMC1_SD_Init();
    MX_USART3_UART_Init();
    MX_SPI1_Init();
    
    // 初始化FreeRTOS
    osKernelInitialize();
    
    // 初始化网关
    Gateway_Init();
    
    // 启动调度器
    osKernelStart();
    
    while (1) {
        // 空闲任务
    }
}

void SystemClock_Config(void) {
    RCC_OscInitTypeDef RCC_OscInitStruct = {0};
    RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
    RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};
    
    // 配置HSE
    RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE|RCC_OSCILLATORTYPE_LSE;
    RCC_OscInitStruct.HSEState = RCC_HSE_ON;
    RCC_OscInitStruct.LSEState = RCC_LSE_ON;
    RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
    RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
    RCC_OscInitStruct.PLL.PLLM = 1;
    RCC_OscInitStruct.PLL.PLLN = 10;
    RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV7;
    RCC_OscInitStruct.PLL.PLLQ = RCC_PLLQ_DIV2;
    RCC_OscInitStruct.PLL.PLLR = RCC_PLLR_DIV2;
    
    if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) {
        Error_Handler();
    }
    
    // 初始化CPU、AHB、APB总线时钟
    RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                                |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
    RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
    RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
    RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
    RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
    
    if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4) != HAL_OK) {
        Error_Handler();
    }
    
    // 外设时钟配置
    PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_USART3|RCC_PERIPHCLK_SDMMC1
                                        |RCC_PERIPHCLK_RTC;
    PeriphClkInit.Usart3ClockSelection = RCC_USART3CLKSOURCE_PCLK1;
    PeriphClkInit.Sdmmc1ClockSelection = RCC_SDMMC1CLKSOURCE_PLLP;
    PeriphClkInit.RTCClockSelection = RCC_RTCCLKSOURCE_LSE;
    
    if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK) {
        Error_Handler();
    }
    
    // 配置主电源稳压器输出电压
    __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
    
    // 启用时钟源
    __HAL_RCC_PWR_CLK_ENABLE();
}

void Error_Handler(void) {
    __disable_irq();
    while (1) {
        // 错误处理:闪烁LED或记录日志
    }
}

10. Makefile 示例

makefile 复制代码
# Makefile for STM32L476 LoRaWAN Gateway
PROJECT_NAME = lorawan_gateway

# Toolchain
PREFIX = arm-none-eabi-
CC = $(PREFIX)gcc
AS = $(PREFIX)gcc -x assembler-with-cpp
CP = $(PREFIX)objcopy
SZ = $(PREFIX)size
HEX = $(CP) -O ihex
BIN = $(CP) -O binary -S

# MCU
MCU = -mcpu=cortex-m4 -mthumb -mfpu=fpv4-sp-d16 -mfloat-abi=hard

# Optimization
OPT = -O2

# Paths
BUILD_DIR = build
C_SOURCES = $(wildcard Core/Src/*.c) \
            $(wildcard Drivers/STM32L4xx_HAL_Driver/Src/*.c) \
            $(wildcard Middlewares/Third_Party/FreeRTOS/Source/*.c) \
            $(wildcard Middlewares/Third_Party/FreeRTOS/Source/portable/MemMang/*.c) \
            $(wildcard Middlewares/Third_Party/FreeRTOS/Source/portable/GCC/ARM_CM4F/*.c) \
            $(wildcard Middlewares/Third_Party/LoRaMAC-node/src/*.c) \
            $(wildcard Middlewares/Third_Party/mbedtls/library/*.c) \
            $(wildcard Applications/Src/*.c)

ASM_SOURCES = Core/Startup/startup_stm32l476xx.s

# Include paths
C_INCLUDES = -ICore/Inc \
             -IDrivers/STM32L4xx_HAL_Driver/Inc \
             -IDrivers/CMSIS/Device/ST/STM32L4xx/Include \
             -IDrivers/CMSIS/Include \
             -IMiddlewares/Third_Party/FreeRTOS/Source/include \
             -IMiddlewares/Third_Party/FreeRTOS/Source/portable/GCC/ARM_CM4F \
             -IMiddlewares/Third_Party/LoRaMAC-node/src \
             -IMiddlewares/Third_Party/mbedtls/include \
             -IApplications/Inc

# Compilation flags
CFLAGS = $(MCU) $(OPT) -Wall -fdata-sections -ffunction-sections
CFLAGS += $(C_INCLUDES) -DUSE_HAL_DRIVER -DSTM32L476xx

LDFLAGS = $(MCU) -specs=nano.specs -TSTM32L476VGTx_FLASH.ld
LDFLAGS += -Wl,-Map=$(BUILD_DIR)/$(PROJECT_NAME).map,--cref
LDFLAGS += -Wl,--gc-sections

# Default target
all: $(BUILD_DIR)/$(PROJECT_NAME).elf $(BUILD_DIR)/$(PROJECT_NAME).hex $(BUILD_DIR)/$(PROJECT_NAME).bin

# Object files
OBJECTS = $(addprefix $(BUILD_DIR)/,$(notdir $(C_SOURCES:.c=.o)))
vpath %.c $(sort $(dir $(C_SOURCES)))
OBJECTS += $(addprefix $(BUILD_DIR)/,$(notdir $(ASM_SOURCES:.s=.o)))
vpath %.s $(sort $(dir $(ASM_SOURCES)))

$(BUILD_DIR)/%.o: %.c Makefile | $(BUILD_DIR)
	$(CC) -c $(CFLAGS) -Wa,-a,-ad,-alms=$(BUILD_DIR)/$(notdir $(<:.c=.lst)) $< -o $@

$(BUILD_DIR)/%.o: %.s Makefile | $(BUILD_DIR)
	$(AS) -c $(CFLAGS) $< -o $@

$(BUILD_DIR)/$(PROJECT_NAME).elf: $(OBJECTS) Makefile
	$(CC) $(OBJECTS) $(LDFLAGS) -o $@
	$(SZ) $@

$(BUILD_DIR)/%.hex: $(BUILD_DIR)/%.elf | $(BUILD_DIR)
	$(HEX) $< $@

$(BUILD_DIR)/%.bin: $(BUILD_DIR)/%.elf | $(BUILD_DIR)
	$(BIN) $< $@

$(BUILD_DIR):
	mkdir -p $@

clean:
	rm -rf $(BUILD_DIR)

flash: $(BUILD_DIR)/$(PROJECT_NAME).bin
	st-flash write $(BUILD_DIR)/$(PROJECT_NAME).bin 0x08000000

debug:
	openocd -f interface/stlink.cfg -f target/stm32l4x.cfg

.PHONY: all clean flash debug

部署和配置说明

硬件连接

复制代码
STM32L476 - SX1302:
  PA4  -> CS
  PA5  -> SCK
  PA6  -> MISO
  PA7  -> MOSI
  PC5  -> RESET

STM32L476 - EC20:
  PB10 -> TX
  PB11 -> RX
  PB8  -> PWRKEY
  PB9  -> RESET

STM32L476 - SD Card:
  PC8  -> D0
  PC9  -> D1
  PC10 -> D2
  PC11 -> D3
  PC12 -> CK
  PD2  -> CMD

云平台配置(以阿里云为例)

  1. 在阿里云IoT平台创建产品
  2. 创建设备,获取三元组(ProductKey, DeviceName, DeviceSecret)
  3. 配置物模型和数据解析脚本
  4. 设置OTA服务

编译和烧录

bash 复制代码
# 安装工具链
sudo apt-get install gcc-arm-none-eabi make stlink-tools

# 编译
make

# 烧录
make flash

# 调试
make debug

这个完整的LoRaWAN网关系统实现的功能:

  1. ✅ 基于STM32L476低功耗MCU
  2. ✅ SX1301/SX1302 LoRa网关模块支持
  3. ✅ EC20 4G模块通信
  4. ✅ LoRaWAN协议栈(Class A/B/C)
  5. ✅ MQTT客户端连接云平台
  6. ✅ SD卡本地缓存和断网续传
  7. ✅ 远程OTA升级
  8. ✅ FreeRTOS任务管理
  9. ✅ 功耗管理模式切换
  10. ✅ 网络状态监控和故障诊断
相关推荐
Linux蓝魔2 小时前
内网搭建阿里源的centos7系统源arm和x86
linux·运维·服务器
fo安方2 小时前
软考~系统规划与管理师考试——真题篇——章节——第5章 应用系统规划——解析版
java·运维·网络
wechat_Neal2 小时前
车载以太网技术全景-TCP/IP 协议栈篇
网络·网络协议·tcp/ip
独行soc2 小时前
2026年渗透测试面试题总结-1(题目+回答)
android·开发语言·网络·安全·web安全·渗透测试·php
2501_945837432 小时前
零信任架构落地,云服务器全生命周期安全防护新体系
服务器
这儿有一堆花2 小时前
服务器安全:防火墙深度配置指南
服务器·安全·php
Q16849645152 小时前
红帽Linux-文件权限管理
linux·运维·服务器
不当菜虚困2 小时前
centos7虚拟机配置网络
运维·服务器·网络
chao1898443 小时前
C#实现OMRON FINS-TCP协议与PLC通信
网络·tcp/ip·c#