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
云平台配置(以阿里云为例)
- 在阿里云IoT平台创建产品
- 创建设备,获取三元组(ProductKey, DeviceName, DeviceSecret)
- 配置物模型和数据解析脚本
- 设置OTA服务
编译和烧录
bash
# 安装工具链
sudo apt-get install gcc-arm-none-eabi make stlink-tools
# 编译
make
# 烧录
make flash
# 调试
make debug
这个完整的LoRaWAN网关系统实现的功能:
- ✅ 基于STM32L476低功耗MCU
- ✅ SX1301/SX1302 LoRa网关模块支持
- ✅ EC20 4G模块通信
- ✅ LoRaWAN协议栈(Class A/B/C)
- ✅ MQTT客户端连接云平台
- ✅ SD卡本地缓存和断网续传
- ✅ 远程OTA升级
- ✅ FreeRTOS任务管理
- ✅ 功耗管理模式切换
- ✅ 网络状态监控和故障诊断