好的,作为一名高级嵌入式软件开发工程师,我将针对安信可科技AiPi-LoRaBoard和AiPi-UNO-ET485扩展板项目,详细阐述最适合的代码设计架构,并提供具体的C代码实现。这个项目旨在构建一个可靠、高效、可扩展的嵌入式系统平台,用于LoRa自组网节点数据的采集、显示和管理。
关注微信公众号,提前获取相关推文

项目需求分析
在深入代码架构之前,我们首先要明确项目的需求,这对于后续的设计至关重要。基于项目简介和图片,我们可以总结出以下关键需求:
硬件平台:
- 安信可科技AiPi-LoRaBoard (主控板,通常基于ESP32或其他MCU)
- AiPi-UNO-ET485扩展板 (提供ET485接口,但主要功能是LoRa)
- LCD 显示屏 (用于节点数据可视化)
- LoRa 模块 (用于无线通信)
- 可能包含传感器 (例如温度、湿度、PM2.5等,图中显示Temp和PM2.5)
- 用户交互按钮 (图中按钮用于操作或配置)
功能需求:
- 节点数据采集: 从本地传感器(或通过ET485扩展,但此处重点是LoRa节点)采集数据,例如温度、湿度、PM2.5等环境参数。
- LoRa 自组网: 支持LoRa协议,实现设备自组网,形成一个无线传感器网络。
- 节点数据传输: 通过LoRa网络将采集到的数据传输到中心节点(或网关),并能接收来自中心节点的指令。
- 节点数据显示: 在本地LCD屏幕上实时显示采集到的节点数据,包括节点ID、传感器类型、数值等。
- 用户交互: 通过按钮进行简单的用户交互,例如切换显示节点、配置参数等。
- 系统管理: 具备基本的系统管理功能,例如节点注册、状态监控、错误处理、低功耗管理等。
- 维护升级: 预留固件升级接口,方便后续维护和功能扩展。
性能需求:
- 可靠性: 系统需要稳定可靠运行,数据传输准确无误,网络连接稳定。
- 高效性: 代码执行效率高,资源占用低,响应速度快,尤其是在数据采集和LoRa通信方面。
- 可扩展性: 系统架构应具有良好的可扩展性,方便后续增加节点数量、传感器类型、功能模块等。
- 低功耗: 考虑到嵌入式设备通常采用电池供电,系统需要具备低功耗特性,延长续航时间。
代码设计架构:分层架构与模块化设计
为了满足上述需求,并构建一个可靠、高效、可扩展的系统平台,我推荐采用分层架构与模块化设计相结合的代码架构。这种架构模式在嵌入式系统开发中非常成熟和有效,能够将复杂的系统分解为多个独立的、易于管理和维护的模块,同时提高代码的复用性和可移植性。
架构层次划分
我们的系统可以划分为以下几个层次:
硬件抽象层 (HAL - Hardware Abstraction Layer):
- 功能: 直接与硬件交互,提供统一的硬件接口,屏蔽底层硬件的差异性。
- 模块: GPIO 驱动、SPI 驱动、I2C 驱动、UART 驱动、LoRa 模块驱动、LCD 驱动、传感器驱动、定时器驱动、ADC 驱动、电源管理驱动等。
- 优点: 提高代码的可移植性,方便更换硬件平台,降低上层应用的开发难度。
板级支持包 (BSP - Board Support Package):
- 功能: 在 HAL 层之上,提供特定硬件平台相关的初始化和配置,例如时钟配置、中断管理、内存管理等。
- 模块: 系统初始化模块、时钟管理模块、中断管理模块、内存管理模块、启动代码等。
- 优点: 针对特定硬件平台进行优化,确保系统能够正确运行,并提供必要的系统级服务。
操作系统层 (OSAL - Operating System Abstraction Layer) (可选,但推荐):
- 功能: 如果使用 RTOS(实时操作系统),OSAL 层可以提供统一的操作系统接口,例如任务管理、线程同步、消息队列、定时器等,屏蔽不同 RTOS 的差异性。
- 模块: 任务管理模块、线程同步模块 (互斥锁、信号量、事件标志组)、消息队列模块、定时器模块等。
- 优点: 提高代码的跨 RTOS 可移植性,简化多任务并发编程,提高系统的实时性和响应速度。 如果项目简单,也可以不使用 RTOS,采用简单的事件循环或协作式调度。
通信协议层 (Communication Protocol Layer):
- 功能: 负责处理 LoRa 通信协议,包括数据包的封装、解封装、加密、解密、网络管理、路由等。
- 模块: LoRa 协议栈模块 (例如 LoRaWAN 或简化版 LoRa 协议)、数据链路层模块、网络层模块、安全模块等。
- 优点: 实现设备之间的可靠通信,支持自组网功能,确保数据传输的完整性和安全性。
数据处理层 (Data Processing Layer):
- 功能: 负责处理采集到的传感器数据,例如数据解析、数据校验、数据转换、数据滤波、数据存储等。
- 模块: 传感器数据解析模块、数据校验模块、数据转换模块 (单位转换)、数据滤波模块 (例如移动平均滤波)、数据存储模块 (如果需要本地存储)。
- 优点: 将原始传感器数据处理成可用的、有意义的信息,提高数据质量和应用价值。
应用逻辑层 (Application Logic Layer):
- 功能: 实现系统的核心业务逻辑,例如节点数据采集、LoRa 数据发送和接收、节点数据管理、用户交互逻辑、系统配置管理、电源管理等。
- 模块: 节点管理模块、数据采集任务模块、LoRa 通信任务模块、显示管理模块、用户界面模块、配置管理模块、电源管理模块、错误处理模块等。
- 优点: 实现系统的具体功能,满足用户需求,是整个系统的核心部分。
表示层 (Presentation Layer) / 用户界面层 (UI Layer):
- 功能: 负责用户界面的显示和交互,例如 LCD 屏幕的驱动、数据显示、菜单显示、按钮事件处理等。
- 模块: LCD 驱动模块、UI 元素管理模块 (例如窗口、文本框、按钮)、数据显示模块、用户输入处理模块等。
- 优点: 提供友好的用户界面,方便用户查看数据和操作系统。
模块化设计
在每个层次内部,我们还需要采用模块化设计,将功能进一步细分到更小的模块中。每个模块负责完成特定的功能,模块之间通过清晰定义的接口进行交互。模块化设计的优点包括:
- 高内聚低耦合: 模块内部功能高度相关,模块之间依赖性低,方便模块的独立开发、测试和维护。
- 代码复用性: 模块可以被多个项目或系统复用,减少重复开发工作。
- 易于维护和升级: 修改或升级某个模块不会影响到其他模块,降低维护成本和风险。
- 团队协作开发: 不同开发人员可以并行开发不同的模块,提高开发效率。
C 代码实现示例 (伪代码 + 关键代码片段,总计超过3000行代码框架)
为了详细说明架构和模块,并提供具体的C代码实现,以下将按照层次结构,给出每个层次和模块的伪代码框架和关键代码片段。由于实际项目代码量庞大,这里不可能提供完整的3000行可编译代码,但会尽可能详细地展示代码结构、接口定义和关键算法,帮助您理解代码架构和实现思路。
1. 硬件抽象层 (HAL)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| #ifndef HAL_GPIO_H #define HAL_GPIO_H
typedef enum { GPIO_MODE_INPUT, GPIO_MODE_OUTPUT } gpio_mode_t;
typedef enum { GPIO_LEVEL_LOW, GPIO_LEVEL_HIGH } gpio_level_t;
typedef uint32_t gpio_pin_t;
void hal_gpio_init(gpio_pin_t pin, gpio_mode_t mode);
void hal_gpio_write(gpio_pin_t pin, gpio_level_t level);
gpio_level_t hal_gpio_read(gpio_pin_t pin);
#endif
|
- hal_gpio.c (示例,针对特定 MCU,例如 ESP32):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| #include "hal_gpio.h" #include "esp_gpio.h"
void hal_gpio_init(gpio_pin_t pin, gpio_mode_t mode) { gpio_config_t io_conf; io_conf.intr_type = GPIO_INTR_DISABLE; io_conf.pin_bit_mask = pin; if (mode == GPIO_MODE_OUTPUT) { io_conf.mode = GPIO_MODE_OUTPUT; } else { io_conf.mode = GPIO_MODE_INPUT; } io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE; io_conf.pull_up_en = GPIO_PULLUP_DISABLE; gpio_config(&io_conf); }
void hal_gpio_write(gpio_pin_t pin, gpio_level_t level) { gpio_set_level(pin, (level == GPIO_LEVEL_HIGH) ? 1 : 0); }
gpio_level_t hal_gpio_read(gpio_pin_t pin) { return (gpio_get_level(pin) == 1) ? GPIO_LEVEL_HIGH : GPIO_LEVEL_LOW; }
|
- 类似地,需要实现
hal_spi.h/c
, hal_i2c.h/c
, hal_uart.h/c
, hal_lora.h/c
, hal_lcd.h/c
, hal_sensor.h/c
, hal_timer.h/c
, hal_adc.h/c
等 HAL 模块,提供统一的硬件操作接口。 例如 hal_lora.h
可能会包含 LoRa 模块的初始化、发送数据、接收数据、设置频率、设置功率等函数。hal_lcd.h
包含 LCD 的初始化、清屏、显示字符、显示字符串、显示图形等函数。hal_sensor.h
包含各种传感器的初始化、读取数据函数。
2. 板级支持包 (BSP)
1 2 3 4 5 6 7 8
| #ifndef BSP_H #define BSP_H
void bsp_init(); void bsp_delay_ms(uint32_t ms); uint32_t bsp_get_tick_ms();
#endif
|
- bsp.c (示例,针对 AiPi-LoRaBoard,假设基于 ESP32):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| #include "bsp.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "esp_system.h" #include "esp_spi_flash.h" #include "nvs_flash.h"
void bsp_init() { esp_err_t ret = nvs_flash_init(); if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { ESP_ERROR_CHECK(nvs_flash_erase()); ret = nvs_flash_init(); } ESP_ERROR_CHECK(ret);
}
void bsp_delay_ms(uint32_t ms) { vTaskDelay(ms / portTICK_PERIOD_MS); }
uint32_t bsp_get_tick_ms() { return esp_timer_get_time() / 1000; }
|
3. 操作系统层 (OSAL) (示例,简化版,不依赖特定 RTOS)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| #ifndef OSAL_H #define OSAL_H
typedef void (*osal_task_func_t)(void* param);
typedef struct { osal_task_func_t task_func; void* task_param; uint32_t task_period_ms; uint32_t last_exec_time_ms; } osal_task_t;
void osal_task_create(osal_task_t* task); void osal_task_schedule();
#endif
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| #include "osal.h" #include "bsp.h"
#define MAX_TASKS 10 osal_task_t tasks[MAX_TASKS]; uint8_t task_count = 0;
void osal_task_create(osal_task_t* task) { if (task_count < MAX_TASKS) { tasks[task_count++] = *task; } }
void osal_task_schedule() { while (1) { uint32_t current_time_ms = bsp_get_tick_ms(); for (int i = 0; i < task_count; i++) { if (current_time_ms - tasks[i].last_exec_time_ms >= tasks[i].task_period_ms) { tasks[i].task_func(tasks[i].task_param); tasks[i].last_exec_time_ms = current_time_ms; } } bsp_delay_ms(10); } }
|
4. 通信协议层 (LoRa 协议层,简化示例)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| #ifndef LORA_PROTOCOL_H #define LORA_PROTOCOL_H
#include <stdint.h>
#define LORA_NODE_ID_LEN 2 #define LORA_PAYLOAD_MAX_LEN 64
typedef struct { uint8_t dest_addr[LORA_NODE_ID_LEN]; uint8_t src_addr[LORA_NODE_ID_LEN]; uint8_t payload_len; uint8_t payload[LORA_PAYLOAD_MAX_LEN]; } lora_packet_t;
void lora_protocol_init();
bool lora_protocol_send_packet(lora_packet_t* packet);
bool lora_protocol_receive_packet(lora_packet_t* packet);
void lora_protocol_set_local_addr(const uint8_t addr[LORA_NODE_ID_LEN]);
void lora_protocol_get_local_addr(uint8_t addr[LORA_NODE_ID_LEN]);
#endif
|
- lora_protocol.c (简化 LoRa 协议实现,假设使用 SX1276/SX1278 LoRa 芯片):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
| #include "lora_protocol.h" #include "hal_lora.h" #include "bsp.h"
#define LOCAL_LORA_ADDR_KEY "lora_addr"
static uint8_t local_lora_addr[LORA_NODE_ID_LEN] = {0x00, 0x01};
void lora_protocol_init() { hal_lora_init();
hal_lora_set_frequency(433E6); hal_lora_set_tx_power(20); hal_lora_set_spreading_factor(7); hal_lora_set_bandwidth(125E3); }
bool lora_protocol_send_packet(lora_packet_t* packet) { memcpy(packet->src_addr, local_lora_addr, LORA_NODE_ID_LEN);
uint8_t tx_buffer[LORA_PAYLOAD_MAX_LEN + LORA_NODE_ID_LEN * 2 + 1]; memcpy(tx_buffer, packet->dest_addr, LORA_NODE_ID_LEN); memcpy(tx_buffer + LORA_NODE_ID_LEN, packet->src_addr, LORA_NODE_ID_LEN); tx_buffer[LORA_NODE_ID_LEN * 2] = packet->payload_len; memcpy(tx_buffer + LORA_NODE_ID_LEN * 2 + 1, packet->payload, packet->payload_len);
return hal_lora_send_data(tx_buffer, packet->payload_len + LORA_NODE_ID_LEN * 2 + 1); }
bool lora_protocol_receive_packet(lora_packet_t* packet) { uint8_t rx_buffer[LORA_PAYLOAD_MAX_LEN + LORA_NODE_ID_LEN * 2 + 1]; uint16_t rx_len;
if (hal_lora_receive_data_non_blocking(rx_buffer, &rx_len)) { if (rx_len > LORA_NODE_ID_LEN * 2 + 1) { memcpy(packet->dest_addr, rx_buffer, LORA_NODE_ID_LEN); memcpy(packet->src_addr, rx_buffer + LORA_NODE_ID_LEN, LORA_NODE_ID_LEN); packet->payload_len = rx_buffer[LORA_NODE_ID_LEN * 2]; if (packet->payload_len <= LORA_PAYLOAD_MAX_LEN && packet->payload_len <= rx_len - (LORA_NODE_ID_LEN * 2 + 1)) { memcpy(packet->payload, rx_buffer + LORA_NODE_ID_LEN * 2 + 1, packet->payload_len); return true; } } } return false; }
void lora_protocol_set_local_addr(const uint8_t addr[LORA_NODE_ID_LEN]) { memcpy(local_lora_addr, addr, LORA_NODE_ID_LEN); }
void lora_protocol_get_local_addr(uint8_t addr[LORA_NODE_ID_LEN]) { memcpy(addr, local_lora_addr, LORA_NODE_ID_LEN); }
|
5. 数据处理层 (Data Processing Layer)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| #ifndef SENSOR_DATA_PROCESS_H #define SENSOR_DATA_PROCESS_H
#include <stdint.h>
typedef struct { float temperature; float humidity; float pm25; } sensor_data_t;
bool sensor_data_parse(const uint8_t* raw_data, uint16_t raw_data_len, sensor_data_t* processed_data);
bool sensor_data_validate(const uint8_t* raw_data, uint16_t raw_data_len);
void sensor_data_filter(sensor_data_t* data);
#endif
|
- sensor_data_process.c (示例,假设传感器数据格式为:温度[2字节] + 湿度[2字节] + PM2.5[2字节] ):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| #include "sensor_data_process.h" #include <string.h>
bool sensor_data_parse(const uint8_t* raw_data, uint16_t raw_data_len, sensor_data_t* processed_data) { if (raw_data_len != 6) { return false; }
int16_t temp_raw = (raw_data[0] << 8) | raw_data[1]; int16_t humidity_raw = (raw_data[2] << 8) | raw_data[3]; int16_t pm25_raw = (raw_data[4] << 8) | raw_data[5];
processed_data->temperature = (float)temp_raw / 100.0f; processed_data->humidity = (float)humidity_raw / 100.0f; processed_data->pm25 = (float)pm25_raw / 100.0f;
return true; }
bool sensor_data_validate(const uint8_t* raw_data, uint16_t raw_data_len) { return raw_data_len > 0; }
void sensor_data_filter(sensor_data_t* data) { }
|
6. 应用逻辑层 (Application Logic Layer)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| #ifndef NODE_MANAGER_H #define NODE_MANAGER_H
#include "lora_protocol.h" #include "sensor_data_process.h"
#define MAX_NODES 6 #define NODE_DATA_UPDATE_INTERVAL_MS 5000
typedef struct { uint8_t node_addr[LORA_NODE_ID_LEN]; sensor_data_t last_sensor_data; uint32_t last_update_time_ms; bool online; } node_info_t;
extern node_info_t node_list[MAX_NODES]; extern uint8_t node_count;
void node_manager_init();
bool node_manager_add_node(const uint8_t node_addr[LORA_NODE_ID_LEN]);
bool node_manager_update_node_data(const uint8_t node_addr[LORA_NODE_ID_LEN], const sensor_data_t* sensor_data);
node_info_t* node_manager_get_node_info(const uint8_t node_addr[LORA_NODE_ID_LEN]);
void node_manager_task(void* param);
#endif
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
| #include "node_manager.h" #include "osal.h" #include "bsp.h" #include <string.h>
node_info_t node_list[MAX_NODES]; uint8_t node_count = 0; uint8_t local_node_addr[LORA_NODE_ID_LEN];
void node_manager_init() { lora_protocol_get_local_addr(local_node_addr); memset(node_list, 0, sizeof(node_list)); node_count = 0;
osal_task_t task; task.task_func = node_manager_task; task.task_param = NULL; task.task_period_ms = 100; osal_task_create(&task); }
bool node_manager_add_node(const uint8_t node_addr[LORA_NODE_ID_LEN]) { if (node_count < MAX_NODES) { memcpy(node_list[node_count].node_addr, node_addr, LORA_NODE_ID_LEN); node_list[node_count].online = false; node_count++; return true; } return false; }
bool node_manager_update_node_data(const uint8_t node_addr[LORA_NODE_ID_LEN], const sensor_data_t* sensor_data) { for (int i = 0; i < node_count; i++) { if (memcmp(node_list[i].node_addr, node_addr, LORA_NODE_ID_LEN) == 0) { node_list[i].last_sensor_data = *sensor_data; node_list[i].last_update_time_ms = bsp_get_tick_ms(); node_list[i].online = true; return true; } } return false; }
node_info_t* node_manager_get_node_info(const uint8_t node_addr[LORA_NODE_ID_LEN]) { for (int i = 0; i < node_count; i++) { if (memcmp(node_list[i].node_addr, node_addr, LORA_NODE_ID_LEN) == 0) { return &node_list[i]; } } return NULL; }
void node_manager_task(void* param) { lora_packet_t rx_packet; while (1) { if (lora_protocol_receive_packet(&rx_packet)) { if (rx_packet.payload_len > 0) { sensor_data_t sensor_data; if (sensor_data_parse(rx_packet.payload, rx_packet.payload_len, &sensor_data)) { node_manager_update_node_data(rx_packet.src_addr, &sensor_data); } } }
uint32_t current_time_ms = bsp_get_tick_ms(); for (int i = 0; i < node_count; i++) { if (node_list[i].online && (current_time_ms - node_list[i].last_update_time_ms > NODE_DATA_UPDATE_INTERVAL_MS * 2)) { node_list[i].online = false; } }
bsp_delay_ms(100); } }
|
- 类似地,需要实现
data_display.h/c
, ui_manager.h/c
, config_manager.h/c
, power_manager.h/c
, error_handler.h/c
等应用逻辑模块,实现系统的各种功能。 例如 data_display.c
负责将节点数据格式化后显示在 LCD 屏幕上,ui_manager.c
处理用户界面的逻辑,例如菜单切换、按钮事件响应等,config_manager.c
负责系统配置参数的加载和保存,power_manager.c
负责电源管理,例如低功耗模式切换等,error_handler.c
负责错误处理和日志记录。
7. 表示层 (UI Layer)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| #ifndef UI_DISPLAY_H #define UI_DISPLAY_H
#include "node_manager.h"
void ui_display_init();
void ui_display_update_node_data(const node_info_t* node_info);
void ui_display_show_node_list(const node_info_t* node_list, uint8_t node_count);
void ui_display_clear_screen();
#endif
|
- ui_display.c (示例,假设使用 SPI 接口 LCD):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| #include "ui_display.h" #include "hal_lcd.h" #include <stdio.h>
#define NODE_DISPLAY_X_START 10 #define NODE_DISPLAY_Y_START 10 #define NODE_DISPLAY_WIDTH 80 #define NODE_DISPLAY_HEIGHT 60 #define NODE_DISPLAY_SPACING_X 10 #define NODE_DISPLAY_SPACING_Y 10
void ui_display_init() { hal_lcd_init(); ui_display_clear_screen(); }
void ui_display_update_node_data(const node_info_t* node_info) { uint8_t node_id = node_info->node_addr[1]; uint8_t row = (node_id - 1) / 3; uint8_t col = (node_id - 1) % 3; uint16_t x = NODE_DISPLAY_X_START + col * (NODE_DISPLAY_WIDTH + NODE_DISPLAY_SPACING_X); uint16_t y = NODE_DISPLAY_Y_START + row * (NODE_DISPLAY_HEIGHT + NODE_DISPLAY_SPACING_Y);
char buffer[64]; sprintf(buffer, "Node %d\n", node_id); hal_lcd_draw_string(x, y, buffer);
sprintf(buffer, "Temp: %.2fC\n", node_info->last_sensor_data.temperature); hal_lcd_draw_string(x, y + 20, buffer);
sprintf(buffer, "PM2.5: %.2f\n", node_info->last_sensor_data.pm25); hal_lcd_draw_string(x, y + 40, buffer);
if (!node_info->online) { hal_lcd_draw_string(x, y + 50, "Offline"); } }
void ui_display_show_node_list(const node_info_t* node_list, uint8_t node_count) { ui_display_clear_screen(); for (int i = 0; i < node_count; i++) { ui_display_update_node_data(&node_list[i]); } }
void ui_display_clear_screen() { hal_lcd_clear_screen(); }
|
主程序 (main.c)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
| #include "bsp.h" #include "osal.h" #include "lora_protocol.h" #include "node_manager.h" #include "ui_display.h" #include "hal_sensor.h"
void system_init_task(void* param); void sensor_collect_task(void* param); void ui_update_task(void* param);
void app_main() { osal_task_t init_task; init_task.task_func = system_init_task; init_task.task_param = NULL; init_task.task_period_ms = 0; osal_task_create(&init_task);
osal_task_schedule(); }
void system_init_task(void* param) { bsp_init(); lora_protocol_init(); node_manager_init(); ui_display_init(); hal_sensor_init();
uint8_t node_addr1[LORA_NODE_ID_LEN] = {0x00, 0x02}; uint8_t node_addr2[LORA_NODE_ID_LEN] = {0x00, 0x03}; uint8_t node_addr3[LORA_NODE_ID_LEN] = {0x00, 0x04}; uint8_t node_addr4[LORA_NODE_ID_LEN] = {0x00, 0x05}; uint8_t node_addr5[LORA_NODE_ID_LEN] = {0x00, 0x06}; uint8_t node_addr6[LORA_NODE_ID_LEN] = {0x00, 0x07}; node_manager_add_node(node_addr1); node_manager_add_node(node_addr2); node_manager_add_node(node_addr3); node_manager_add_node(node_addr4); node_manager_add_node(node_addr5); node_manager_add_node(node_addr6);
osal_task_t sensor_task; sensor_task.task_func = sensor_collect_task; sensor_task.task_param = NULL; sensor_task.task_period_ms = 5000; osal_task_create(&sensor_task);
osal_task_t ui_task; ui_task.task_func = ui_update_task; ui_task.task_param = NULL; ui_task.task_period_ms = 1000; osal_task_create(&ui_task);
vTaskDelete(NULL); }
void sensor_collect_task(void* param) { sensor_data_t local_sensor_data; uint8_t raw_sensor_data[6]; uint16_t raw_data_len;
while (1) { if (hal_sensor_read_data(raw_sensor_data, &raw_data_len)) { if (sensor_data_parse(raw_sensor_data, raw_data_len, &local_sensor_data)) { lora_packet_t tx_packet; memset(&tx_packet, 0, sizeof(tx_packet)); memcpy(tx_packet.dest_addr, "\x00\x00", LORA_NODE_ID_LEN); tx_packet.payload_len = raw_data_len; memcpy(tx_packet.payload, raw_sensor_data, raw_data_len); lora_protocol_send_packet(&tx_packet); } } bsp_delay_ms(5000); } }
void ui_update_task(void* param) { while (1) { extern node_info_t node_list[MAX_NODES]; extern uint8_t node_count;
ui_display_show_node_list(node_list, node_count);
bsp_delay_ms(1000); } }
|
测试验证和维护升级
测试验证:
- 单元测试: 针对每个模块进行单元测试,验证模块功能的正确性。
- 集成测试: 将模块组合起来进行集成测试,验证模块之间的接口和协作是否正常。
- 系统测试: 进行系统级别的测试,验证整个系统的功能、性能、可靠性是否满足需求。
- 现场测试: 在实际应用环境中进行现场测试,验证系统的稳定性和适应性。
维护升级:
- 固件升级: 预留固件升级接口 (例如 OTA 无线升级或 UART 串口升级),方便后续功能升级和 bug 修复。
- 模块化维护: 由于采用模块化设计,可以方便地对单个模块进行维护和升级,而不会影响到其他模块。
- 日志记录和错误处理: 完善的日志记录和错误处理机制可以帮助快速定位和解决问题。
总结
以上代码架构和C代码示例提供了一个构建可靠、高效、可扩展的嵌入式 LoRa 自组网节点数据采集和显示系统的框架。这个架构基于分层和模块化设计,将系统分解为多个独立的层次和模块,每个模块负责特定的功能,层次之间和模块之间通过清晰定义的接口进行交互。这种架构具有良好的可移植性、可维护性和可扩展性,能够满足项目需求,并为后续的开发和维护奠定坚实的基础。
请注意,以上代码仅为示例和框架,实际项目中需要根据具体的硬件平台、传感器类型、LoRa 协议细节、UI 设计等进行详细的实现和优化。 实际的代码量肯定会超过3000行,因为包含了各个模块的完整实现,以及详细的注释、错误处理、边界条件考虑等等。 这个框架和示例代码可以作为您进行项目开发的起点和参考。