编程技术分享

分享编程知识,探讨技术创新

0%

此项目旨在利用ESP32强大的网络能力和丰富的外设接口,构建一个智能化的物联网设备,用于监控和管理家庭或小型办公室环境中的NAS(网络附加存储)设备。该设备能够解决传统NAS设备在网络连通性、硬件健康监测以及安全管理方面的一些痛点,提升NAS设备的使用体验和可靠性。

关注微信公众号,提前获取相关推文

项目目标:

  1. 网络连通性监控: 实时监测NAS设备的网络连接状态,并在网络异常时发出告警。
  2. 硬件健康监测: 监测NAS设备的硬件健康状态,包括但不限于CPU温度、硬盘温度、风扇转速等,预防硬件故障。
  3. 环境温湿度监测与调节建议: 通过外置温湿度传感器,监测NAS设备所在环境的温湿度,并根据设定的阈值,提供环境调节建议,确保NAS设备运行在健康的环境中。
  4. NAS设备安全管理: 实现基础的NAS设备远程管理功能,例如重启、关机、网络唤醒等,提升管理效率和安全性。
  5. 交互式用户界面: 通过显示屏(例如LCD或OLED),直观地展示NAS设备的各项监控数据和状态,并提供简单的用户交互功能。
  6. 可扩展性: 系统架构应具备良好的可扩展性,方便未来添加更多功能,例如功耗监测、智能告警、数据分析等。

系统架构设计

为了实现上述项目目标,我们需要设计一个可靠、高效、可扩展的系统架构。我将采用分层架构的设计思想,将系统划分为不同的模块,每个模块负责特定的功能,模块之间通过清晰的接口进行通信。这种架构方式能够提高代码的可维护性、可复用性和可测试性。

1. 硬件架构

  • 主控芯片: ESP32-WROOM-32E/UE (或其他ESP32模组),具备Wi-Fi、蓝牙功能,强大的处理能力和丰富的外设接口。
  • 显示屏: 2.4寸 SPI TFT LCD 彩色显示屏 (或其他合适的显示屏,如OLED),用于显示监控数据和用户交互界面。
  • 温湿度传感器: DHT22 或 AM2302,高精度温湿度传感器,用于环境温湿度监测。
  • 按键: 多个按键 (例如 3-5个),用于用户交互操作,例如菜单导航、参数设置等。
  • LED指示灯: 多个LED指示灯 (例如 RGB LED),用于状态指示,例如网络连接状态、告警状态等。
  • 电源管理: 5V DC 电源输入,通过稳压芯片为ESP32和其他外设供电。

2. 软件架构 (分层架构)

我们的软件架构将分为以下几个层次:

  • 硬件抽象层 (HAL - Hardware Abstraction Layer): HAL层负责屏蔽底层硬件的差异,为上层提供统一的硬件接口。这层包括:

    • GPIO驱动: 控制LED、按键、传感器等GPIO外设。
    • SPI驱动: 控制SPI显示屏和SPI接口的传感器 (如果使用)。
    • I2C驱动: 控制I2C接口的传感器 (如果使用)。
    • 定时器驱动: 用于定时任务,例如周期性传感器数据采集、网络状态检测等。
    • 串口驱动: 用于调试和可能的固件更新。
  • 设备驱动层 (Device Driver Layer): 设备驱动层构建在HAL层之上,负责具体外设的驱动和控制。这层包括:

    • 显示屏驱动: 初始化显示屏,提供画点、画线、显示字符、显示图片等接口。
    • 温湿度传感器驱动: 初始化传感器,提供读取温湿度数据的接口。
    • 按键驱动: 检测按键按下事件,提供按键事件处理接口。
    • LED驱动: 控制LED的亮灭和颜色。
  • 服务层 (Service Layer): 服务层构建在设备驱动层之上,提供各种业务逻辑服务。这层是系统的核心,包括:

    • 网络管理服务:
      • Wi-Fi 连接管理:连接到指定的Wi-Fi网络,断线重连。
      • DHCP客户端:获取IP地址。
      • DNS解析:解析NAS设备的域名或IP地址。
      • 网络状态检测:Ping NAS设备,检测网络连通性。
      • MQTT客户端 (可选):如果需要更高级的远程监控和控制,可以使用MQTT协议与云平台或管理中心通信。
      • HTTP客户端:用于与NAS设备进行HTTP API交互,例如获取NAS信息或发送管理指令。
    • NAS监控服务:
      • NAS网络状态监控:定期Ping NAS设备,判断网络是否可达。
      • NAS硬件健康监控 (模拟):由于ESP32无法直接访问NAS硬件,这里我们模拟NAS硬件健康数据获取,实际项目中可能需要通过NAS提供的API或SNMP协议获取真实数据。 模拟数据可以包括 CPU 负载、内存使用率、磁盘空间使用率等。
      • NAS服务状态监控 (可选):可以扩展为监控NAS上运行的关键服务,例如Web服务、文件共享服务等,通过端口扫描或协议探测实现。
    • 环境监测服务:
      • 温湿度数据采集:定期读取温湿度传感器数据。
      • 温湿度数据处理:数据滤波、单位转换等。
      • 环境健康建议:根据温湿度数据和预设阈值,提供环境调节建议。
    • 用户界面管理服务 (UI Service):
      • 界面元素管理:管理显示屏上的各种界面元素,例如文本、图标、图表等。
      • 界面布局管理:控制界面元素的布局和显示位置。
      • 用户交互处理:处理按键事件,响应用户操作,例如菜单切换、参数设置等。
      • 数据显示:将监控数据 (NAS状态、环境数据等) 格式化并显示在屏幕上。
    • 配置管理服务 (Config Service):
      • 配置参数存储:存储系统配置参数,例如Wi-Fi SSID/密码、NAS IP地址、监控阈值等。可以使用ESP32的NVS (Non-Volatile Storage) 或 SPIFFS 文件系统存储配置。
      • 配置参数加载和保存:提供加载和保存配置参数的接口。
      • 用户配置界面:提供用户界面用于配置参数。
    • 告警管理服务 (Alarm Service):
      • 告警事件定义:定义各种告警事件,例如网络断开告警、NAS硬件异常告警、温湿度超限告警等。
      • 告警阈值设置:设置告警阈值。
      • 告警触发和处理:检测告警事件,触发告警,并执行相应的告警处理动作,例如LED指示灯闪烁、屏幕告警信息显示、甚至通过网络发送告警通知 (例如邮件、短信)。
    • 系统管理服务 (System Service):
      • 系统初始化:系统启动时的初始化操作。
      • 定时任务管理:管理周期性任务的调度和执行。
      • 固件更新 (OTA - Over-The-Air) (可选):支持通过网络进行固件更新,方便系统维护和升级。
  • 应用层 (Application Layer): 应用层是最高层,负责整合各个服务,实现完整的应用逻辑。 通常包含 main() 函数和主循环,负责:

    • 系统初始化:调用各个服务模块的初始化函数。
    • 任务调度:创建和管理FreeRTOS任务,例如传感器数据采集任务、网络任务、UI更新任务等。
    • 事件处理:处理用户事件 (按键事件)、系统事件 (例如网络状态变化事件)、告警事件等。
    • 循环执行:在主循环中,周期性地更新UI显示、采集传感器数据、检测网络状态、处理告警等。

3. 软件模块关系图

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
+---------------------+
| Application Layer |
+---------------------+
|
| 调用服务接口
V
+---------------------+---------------------+---------------------+---------------------+---------------------+---------------------+
| Network Mgmt Service | NAS Monitor Service | Environment Mon Service | UI Mgmt Service | Config Mgmt Service | Alarm Mgmt Service | ... (System Mgmt, OTA)
+---------------------+---------------------+---------------------+---------------------+---------------------+---------------------+
| | | | | |
| | | | | | 调用驱动接口
V V V V V V
+---------------------+---------------------+---------------------+---------------------+---------------------+---------------------+
| Display Driver | DHT22 Sensor Driver | Button Driver | LED Driver | SPI Driver | I2C Driver | ... (Timer, UART)
+---------------------+---------------------+---------------------+---------------------+---------------------+---------------------+
| | | | | |
| | | | | | 硬件操作
V V V V V V
+-----------------------------------------------------------------------------------------------------------------------+
| Hardware Abstraction Layer (HAL) |
|-----------------------------------------------------------------------------------------------------------------------|
| GPIO | SPI | I2C | Timer | UART | ... |
+-----------------------------------------------------------------------------------------------------------------------+
| ESP32 Hardware |
+-----------------------------------------------------------------------------------------------------------------------+

C代码实现示例 (关键模块代码片段)

由于代码量较大,这里无法提供3000行完整的代码,但我将提供关键模块的代码片段,以展示架构设计和代码实现思路。 完整的项目代码将远超3000行,并且需要根据具体硬件选型和功能需求进行详细设计和实现。

1. HAL层 (GPIO驱动 - 示例)

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
// hal_gpio.h
#ifndef HAL_GPIO_H
#define HAL_GPIO_H

#include <stdint.h>
#include "driver/gpio.h" // ESP-IDF GPIO驱动头文件

typedef enum {
GPIO_MODE_INPUT,
GPIO_MODE_OUTPUT,
GPIO_MODE_INPUT_OUTPUT
} gpio_mode_t;

typedef enum {
GPIO_PULLUP,
GPIO_PULLDOWN,
GPIO_PULLNONE
} gpio_pull_mode_t;

typedef enum {
GPIO_LEVEL_LOW,
GPIO_LEVEL_HIGH
} gpio_level_t;

// 初始化GPIO
esp_err_t hal_gpio_init(gpio_num_t gpio_num, gpio_mode_t mode, gpio_pull_mode_t pull_mode);

// 设置GPIO输出电平
esp_err_t hal_gpio_set_level(gpio_num_t gpio_num, gpio_level_t level);

// 读取GPIO输入电平
gpio_level_t hal_gpio_get_level(gpio_num_t gpio_num);

#endif // HAL_GPIO_H


// hal_gpio.c
#include "hal_gpio.h"

esp_err_t hal_gpio_init(gpio_num_t gpio_num, gpio_mode_t mode, gpio_pull_mode_t pull_mode) {
gpio_config_t io_conf;
io_conf.intr_type = GPIO_INTR_DISABLE; // 禁止中断
io_conf.pin_bit_mask = (1ULL << gpio_num); // 配置GPIO引脚
if (mode == GPIO_MODE_INPUT) {
io_conf.mode = GPIO_MODE_INPUT;
} else if (mode == GPIO_MODE_OUTPUT) {
io_conf.mode = GPIO_MODE_OUTPUT;
} else { // GPIO_MODE_INPUT_OUTPUT
io_conf.mode = GPIO_MODE_INPUT_OUTPUT;
}
if (pull_mode == GPIO_PULLUP) {
io_conf.pull_up_en = GPIO_PULLUP_ENABLE;
io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
} else if (pull_mode == GPIO_PULLDOWN) {
io_conf.pull_up_en = GPIO_PULLUP_DISABLE;
io_conf.pull_down_en = GPIO_PULLDOWN_ENABLE;
} else { // GPIO_PULLNONE
io_conf.pull_up_en = GPIO_PULLUP_DISABLE;
io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
}
return gpio_config(&io_conf);
}

esp_err_t hal_gpio_set_level(gpio_num_t gpio_num, gpio_level_t level) {
return gpio_set_level(gpio_num, (level == GPIO_LEVEL_HIGH) ? 1 : 0);
}

gpio_level_t hal_gpio_get_level(gpio_num_t gpio_num) {
return (gpio_get_level(gpio_num) == 1) ? GPIO_LEVEL_HIGH : GPIO_LEVEL_LOW;
}

2. 设备驱动层 (LED驱动 - 示例,基于HAL_GPIO)

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
// led_driver.h
#ifndef LED_DRIVER_H
#define LED_DRIVER_H

#include "hal_gpio.h"
#include "esp_err.h"

typedef enum {
LED_COLOR_RED,
LED_COLOR_GREEN,
LED_COLOR_BLUE,
LED_COLOR_YELLOW,
LED_COLOR_CYAN,
LED_COLOR_MAGENTA,
LED_COLOR_WHITE,
LED_COLOR_OFF
} led_color_t;

// 初始化LED驱动
esp_err_t led_driver_init(gpio_num_t red_pin, gpio_num_t green_pin, gpio_num_t blue_pin);

// 设置LED颜色
esp_err_t led_driver_set_color(led_color_t color);

#endif // LED_DRIVER_H


// led_driver.c
#include "led_driver.h"

static gpio_num_t red_led_pin;
static gpio_num_t green_led_pin;
static gpio_num_t blue_led_pin;

esp_err_t led_driver_init(gpio_num_t red_pin, gpio_num_t green_pin, gpio_num_t blue_pin) {
red_led_pin = red_pin;
green_led_pin = green_pin;
blue_led_pin = blue_pin;

// 初始化RGB LED引脚为输出模式
ESP_ERROR_CHECK(hal_gpio_init(red_led_pin, GPIO_MODE_OUTPUT, GPIO_PULLNONE));
ESP_ERROR_CHECK(hal_gpio_init(green_led_pin, GPIO_MODE_OUTPUT, GPIO_PULLNONE));
ESP_ERROR_CHECK(hal_gpio_init(blue_led_pin, GPIO_MODE_OUTPUT, GPIO_PULLNONE));

// 默认关闭LED
led_driver_set_color(LED_COLOR_OFF);
return ESP_OK;
}

esp_err_t led_driver_set_color(led_color_t color) {
switch (color) {
case LED_COLOR_RED:
hal_gpio_set_level(red_led_pin, GPIO_LEVEL_HIGH);
hal_gpio_set_level(green_led_pin, GPIO_LEVEL_LOW);
hal_gpio_set_level(blue_led_pin, GPIO_LEVEL_LOW);
break;
case LED_COLOR_GREEN:
hal_gpio_set_level(red_led_pin, GPIO_LEVEL_LOW);
hal_gpio_set_level(green_led_pin, GPIO_LEVEL_HIGH);
hal_gpio_set_level(blue_led_pin, GPIO_LEVEL_LOW);
break;
case LED_COLOR_BLUE:
hal_gpio_set_level(red_led_pin, GPIO_LEVEL_LOW);
hal_gpio_set_level(green_led_pin, GPIO_LEVEL_LOW);
hal_gpio_set_level(blue_led_pin, GPIO_LEVEL_HIGH);
break;
case LED_COLOR_YELLOW: // 红 + 绿
hal_gpio_set_level(red_led_pin, GPIO_LEVEL_HIGH);
hal_gpio_set_level(green_led_pin, GPIO_LEVEL_HIGH);
hal_gpio_set_level(blue_led_pin, GPIO_LEVEL_LOW);
break;
case LED_COLOR_CYAN: // 绿 + 蓝
hal_gpio_set_level(red_led_pin, GPIO_LEVEL_LOW);
hal_gpio_set_level(green_led_pin, GPIO_LEVEL_HIGH);
hal_gpio_set_level(blue_led_pin, GPIO_LEVEL_HIGH);
break;
case LED_COLOR_MAGENTA: // 红 + 蓝
hal_gpio_set_level(red_led_pin, GPIO_LEVEL_HIGH);
hal_gpio_set_level(green_led_pin, GPIO_LEVEL_LOW);
hal_gpio_set_level(blue_led_pin, GPIO_LEVEL_HIGH);
break;
case LED_COLOR_WHITE: // 红 + 绿 + 蓝
hal_gpio_set_level(red_led_pin, GPIO_LEVEL_HIGH);
hal_gpio_set_level(green_led_pin, GPIO_LEVEL_HIGH);
hal_gpio_set_level(blue_led_pin, GPIO_LEVEL_HIGH);
break;
case LED_COLOR_OFF:
default:
hal_gpio_set_level(red_led_pin, GPIO_LEVEL_LOW);
hal_gpio_set_level(green_led_pin, GPIO_LEVEL_LOW);
hal_gpio_set_level(blue_led_pin, GPIO_LEVEL_LOW);
break;
}
return ESP_OK;
}

3. 服务层 (网络管理服务 - Wi-Fi 连接 - 示例)

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
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
// network_service.h
#ifndef NETWORK_SERVICE_H
#define NETWORK_SERVICE_H

#include <stdbool.h>
#include "esp_err.h"

// 初始化网络服务
esp_err_t network_service_init();

// 连接到Wi-Fi网络
esp_err_t network_service_connect_wifi(const char *ssid, const char *password);

// 断开Wi-Fi连接
esp_err_t network_service_disconnect_wifi();

// 获取Wi-Fi连接状态
bool network_service_is_wifi_connected();

// 获取本机IP地址 (如果已连接)
char* network_service_get_local_ip();

#endif // NETWORK_SERVICE_H


// network_service.c
#include "network_service.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "string.h"
#include "lwip/netif.h"
#include "lwip/ip4_addr.h"

static const char *TAG = "network_service";
static bool wifi_connected = false;
static char local_ip_str[16] = {0}; // 存储本地IP地址字符串

static void wifi_event_handler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data) {
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
esp_wifi_connect();
} else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
wifi_connected = false;
ESP_LOGI(TAG, "Wi-Fi disconnected, retrying...");
esp_wifi_connect(); // 尝试重新连接
} else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data;
ESP_LOGI(TAG, "Got IP address:" IPSTR, IP2STR(&event->ip_info.ip));
wifi_connected = true;
// 保存本地IP地址
sprintf(local_ip_str, IPSTR, IP2STR(&event->ip_info.ip));
}
}

esp_err_t network_service_init() {
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
esp_netif_create_default_wifi_sta();

wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));

esp_event_handler_instance_t wifi_event_handler_inst;
esp_event_handler_instance_t ip_event_handler_inst;
ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &wifi_event_handler, NULL, &wifi_event_handler_inst));
ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &wifi_event_handler, NULL, &ip_event_handler_inst));
ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT, IP_EVENT_STA_LOST_IP, &wifi_event_handler, NULL, &ip_event_handler_inst)); // 添加 IP_EVENT_STA_LOST_IP 处理

ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
ESP_ERROR_CHECK(esp_wifi_start());

ESP_LOGI(TAG, "Wi-Fi initialized");
return ESP_OK;
}

esp_err_t network_service_connect_wifi(const char *ssid, const char *password) {
wifi_config_t wifi_config = {
.sta = {
.ssid = "",
.password = "",
.threshold.authmode = WIFI_AUTH_WPA2_PSK,
.pmf_cfg = {
.capable = true,
.mgmt_frame_prot = WIFI_PMF_REQUIRED
},
},
};
strncpy((char *)wifi_config.sta.ssid, ssid, sizeof(wifi_config.sta.ssid) - 1);
strncpy((char *)wifi_config.sta.password, password, sizeof(wifi_config.sta.password) - 1);

ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));
ESP_ERROR_CHECK(esp_wifi_connect());
return ESP_OK;
}

esp_err_t network_service_disconnect_wifi() {
ESP_ERROR_CHECK(esp_wifi_disconnect());
return ESP_OK;
}

bool network_service_is_wifi_connected() {
return wifi_connected;
}

char* network_service_get_local_ip() {
if (wifi_connected) {
return local_ip_str;
} else {
return NULL; // 或者返回一个错误提示字符串
}
}

4. 服务层 (NAS监控服务 - NAS网络状态监控 - Ping - 示例)

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
// nas_monitor_service.h
#ifndef NAS_MONITOR_SERVICE_H
#define NAS_MONITOR_SERVICE_H

#include <stdbool.h>
#include "esp_err.h"

// 初始化NAS监控服务
esp_err_t nas_monitor_service_init();

// 设置NAS IP地址或域名
esp_err_t nas_monitor_service_set_nas_address(const char *nas_address);

// 获取NAS网络状态 (是否可达)
bool nas_monitor_service_is_nas_reachable();

#endif // NAS_MONITOR_SERVICE_H


// nas_monitor_service.c
#include "nas_monitor_service.h"
#include "ping/ping_sock.h"
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "string.h"

static const char *TAG = "nas_monitor_service";
static char nas_address[64] = {0}; // 存储NAS IP地址或域名
static bool nas_reachable = false;

static void ping_result_cb(ping_result_t *result) {
if (result->total_count > 0 && result->recv_count > 0) {
nas_reachable = true;
ESP_LOGI(TAG, "NAS is reachable, latency=%d ms", result->delay_time_ms);
} else {
nas_reachable = false;
ESP_LOGW(TAG, "NAS is unreachable");
}
}

static void ping_task(void *pvParameters) {
ping_config_t ping_config = PING_DEFAULT_CONFIG();
ping_config.target_host = nas_address;
ping_config.count = 1; // 只ping一次
ping_config.timeout_ms = 1000; // 1秒超时
ping_config.interval_ms = 1000; // 间隔1秒 (虽然只ping一次,但这里设置一下)
ping_config.task_stack_size = 2048; // 增加ping任务的堆栈大小,避免堆栈溢出
ping_config.result_cb = ping_result_cb;

while (1) {
esp_ping(&ping_config);
vTaskDelay(pdMS_TO_TICKS(10000)); // 每10秒ping一次NAS
}
vTaskDelete(NULL);
}

esp_err_t nas_monitor_service_init() {
// 默认NAS地址可以设置为一个占位符或空字符串,等待用户配置
strcpy(nas_address, "192.168.1.100"); // 示例默认地址

// 创建ping任务
BaseType_t task_created = xTaskCreate(ping_task, "ping_task", 4096, NULL, 5, NULL); // 增加任务堆栈大小
if (task_created != pdPASS) {
ESP_LOGE(TAG, "Failed to create ping task");
return ESP_FAIL;
}
return ESP_OK;
}

esp_err_t nas_monitor_service_set_nas_address(const char *address) {
strncpy(nas_address, address, sizeof(nas_address) - 1);
ESP_LOGI(TAG, "NAS address set to: %s", nas_address);
return ESP_OK;
}

bool nas_monitor_service_is_nas_reachable() {
return nas_reachable;
}

5. 应用层 (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
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "network_service.h"
#include "nas_monitor_service.h"
#include "led_driver.h"
#include "dhtxx_driver.h" // 假设有DHTXX传感器驱动
#include "display_driver.h" // 假设有显示屏驱动
#include "button_driver.h" // 假设有按键驱动
#include "config_service.h" // 假设有配置服务

static const char *TAG = "app_main";

void app_main(void) {
ESP_LOGI(TAG, "ESP32 NAS Monitor Device Started!");

// 初始化各个服务模块
ESP_ERROR_CHECK(network_service_init());
ESP_ERROR_CHECK(nas_monitor_service_init());
ESP_ERROR_CHECK(led_driver_init(GPIO_NUM_2, GPIO_NUM_4, GPIO_NUM_5)); // 示例LED引脚
ESP_ERROR_CHECK(dhtxx_driver_init(DHTXX_TYPE_DHT22, GPIO_NUM_17)); // 示例DHT22引脚
ESP_ERROR_CHECK(display_driver_init()); // 初始化显示屏
ESP_ERROR_CHECK(button_driver_init()); // 初始化按键
ESP_ERROR_CHECK(config_service_init()); // 初始化配置服务

// 从配置服务加载Wi-Fi SSID/密码和 NAS IP 地址
char wifi_ssid[32] = {0};
char wifi_password[64] = {0};
char nas_ip_address[32] = {0};
config_service_get_wifi_config(wifi_ssid, wifi_password);
config_service_get_nas_ip(nas_ip_address);

// 连接Wi-Fi
ESP_LOGI(TAG, "Connecting to Wi-Fi: %s", wifi_ssid);
ESP_ERROR_CHECK(network_service_connect_wifi(wifi_ssid, wifi_password));

// 设置NAS IP地址
ESP_ERROR_CHECK(nas_monitor_service_set_nas_address(nas_ip_address));

// 主循环
while (1) {
// 获取NAS网络状态
bool nas_reachable = nas_monitor_service_is_nas_reachable();

// 获取温湿度数据
float temperature, humidity;
if (dhtxx_driver_read_data(&temperature, &humidity) == ESP_OK) {
ESP_LOGI(TAG, "Temperature: %.2f °C, Humidity: %.2f %%", temperature, humidity);
} else {
ESP_LOGW(TAG, "Failed to read DHT22 data");
}

// 更新LED状态 (例如,网络连接状态指示)
if (network_service_is_wifi_connected()) {
led_driver_set_color(LED_COLOR_GREEN); // Wi-Fi connected: 绿色
} else {
led_driver_set_color(LED_COLOR_YELLOW); // Wi-Fi connecting/disconnected: 黄色
}
if (!nas_reachable) {
led_driver_set_color(LED_COLOR_RED); // NAS unreachable: 红色 (优先级更高)
}


// 更新显示屏数据 (例如,显示 NAS 状态、温湿度数据)
display_driver_clear_screen();
display_driver_draw_string(10, 20, "NAS Monitor", COLOR_WHITE);
if (nas_reachable) {
display_driver_draw_string(10, 40, "NAS Reachable", COLOR_GREEN);
} else {
display_driver_draw_string(10, 40, "NAS Unreachable", COLOR_RED);
}
char temp_str[32], humidity_str[32];
sprintf(temp_str, "Temp: %.1f C", temperature);
sprintf(humidity_str, "Humidity: %.1f %%", humidity);
display_driver_draw_string(10, 60, temp_str, COLOR_WHITE);
display_driver_draw_string(10, 80, humidity_str, COLOR_WHITE);
display_driver_update_screen();


// 处理按键事件 (示例,可以扩展菜单和交互逻辑)
if (button_driver_is_pressed(BUTTON_1)) {
ESP_LOGI(TAG, "Button 1 Pressed!");
// 执行某些操作,例如切换显示界面
}
if (button_driver_is_pressed(BUTTON_2)) {
ESP_LOGI(TAG, "Button 2 Pressed!");
// 执行某些操作,例如重启NAS (需要实现NAS远程管理功能)
}


vTaskDelay(pdMS_TO_TICKS(1000)); // 1秒循环一次
}
}

项目中采用的技术和方法 (实践验证过的):

  1. 分层架构设计: 如上所述,分层架构是嵌入式系统开发的常用方法,提高了代码的可维护性和可复用性。
  2. 模块化编程: 将系统分解为多个模块,每个模块负责特定的功能,降低了系统的复杂性,方便开发和测试。
  3. 硬件抽象层 (HAL): HAL层隔离了硬件差异,使得上层代码可以独立于具体的硬件平台,提高了代码的可移植性。
  4. 设备驱动开发: 针对不同的外设编写驱动程序,实现对硬件的控制和数据交互。
  5. FreeRTOS实时操作系统: 使用FreeRTOS进行多任务管理,可以并发执行多个任务,例如数据采集、网络通信、UI更新等,提高系统效率和实时性。
  6. 事件驱动编程: 使用事件驱动的方式处理用户输入 (按键事件)、系统事件 (网络状态变化) 和告警事件,提高系统的响应性和效率。
  7. Wi-Fi 网络通信: 利用ESP32的Wi-Fi功能实现设备联网,进行数据传输和远程管理。
  8. TCP/IP 协议栈: 使用ESP-IDF提供的 lwIP TCP/IP 协议栈进行网络通信。
  9. Ping 协议: 使用Ping协议检测NAS设备的网络连通性。
  10. HTTP 客户端 (可选): 可以使用HTTP客户端与NAS设备进行 REST API 交互,获取NAS信息或发送管理指令。
  11. MQTT 协议 (可选): 可以使用MQTT协议与云平台或管理中心进行更高级的远程监控和控制。
  12. SPI 和 I2C 通信: 使用SPI和I2C接口与显示屏和传感器等外设进行通信。
  13. NVS (Non-Volatile Storage) 或 SPIFFS 文件系统: 使用NVS或SPIFFS存储配置参数,实现掉电数据保持。
  14. OTA (Over-The-Air) 固件更新 (可选): 支持OTA固件更新,方便系统维护和升级。
  15. 错误处理和日志记录: 在代码中加入完善的错误处理机制,并使用日志记录功能进行调试和问题排查。
  16. 代码版本控制 (Git): 使用Git进行代码版本控制,方便团队协作和代码管理。
  17. 单元测试和集成测试: 进行单元测试和集成测试,确保代码质量和系统稳定性。

系统开发流程 (完整嵌入式系统开发流程)

  1. 需求分析: 明确项目目标、功能需求、性能指标、用户场景等,例如本文档开头的项目目标。
  2. 系统设计: 根据需求分析结果,进行硬件架构设计和软件架构设计,包括模块划分、接口定义、技术选型等,如本文档的系统架构设计部分。
  3. 详细设计: 对每个模块进行详细设计,包括数据结构设计、算法设计、接口设计、流程设计等。
  4. 编码实现: 根据详细设计文档,编写C代码实现各个模块的功能,如本文档提供的代码示例。
  5. 单元测试: 对每个模块进行单元测试,验证模块功能的正确性。
  6. 集成测试: 将各个模块集成在一起进行集成测试,验证模块之间的协同工作是否正常,系统功能是否完整。
  7. 系统测试: 对整个系统进行系统测试,包括功能测试、性能测试、稳定性测试、可靠性测试、用户体验测试等,验证系统是否满足需求。
  8. 硬件调试和软件调试: 在开发过程中,需要进行硬件调试和软件调试,解决硬件和软件方面的问题。可以使用JTAG调试器、串口调试工具、日志记录等手段进行调试。
  9. 系统优化: 根据测试结果,对系统进行性能优化、功耗优化、代码优化等。
  10. 版本发布: 将测试通过的系统版本发布给用户。
  11. 维护升级: 在系统发布后,需要进行维护和升级,修复bug,添加新功能,优化性能等。可以使用OTA固件更新等方式进行升级。

总结

这个项目展示了一个基于ESP32的物联网NAS监控设备的完整开发流程和代码架构设计。 通过分层架构、模块化编程、HAL层、设备驱动、FreeRTOS、网络通信等技术和方法,我们可以构建一个可靠、高效、可扩展的嵌入式系统平台。 提供的C代码示例只是项目的一部分,实际项目中需要根据具体需求进行更详细的设计和实现。 希望这份详细的说明和代码示例能够帮助您理解嵌入式系统开发流程和代码架构设计,并为您自己的项目提供参考。 如果您有任何进一步的问题,欢迎随时提问。

欢迎关注我的其它发布渠道