编程技术分享

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

0%

简介:Type_c输入转USB输出。可以实现远程开启关闭,并且具备定时功能。

好的,作为一名高级嵌入式软件开发工程师,我很乐意为您详细阐述这个Type-C输入转USB输出的智能电源控制项目的代码设计架构,并提供相应的C代码实现。为了确保系统可靠、高效、可扩展,并且符合嵌入式系统开发的最佳实践,我将从需求分析开始,逐步深入到系统架构设计、模块划分、关键技术选择、代码实现、测试验证以及未来的维护升级。
关注微信公众号,提前获取相关推文

项目概述与需求分析

项目名称: 智能USB电源控制器

功能简介: 该项目旨在设计一个嵌入式系统,能够将Type-C接口的电源输入转换为USB-A接口的电源输出,并具备以下核心功能:

  1. 本地控制: 通过板载按钮或指示灯等方式,实现本地的USB电源输出的开启和关闭控制。
  2. 远程控制: 通过Wi-Fi或蓝牙等无线通信方式,实现远程的USB电源输出的开启和关闭控制。
  3. 定时控制: 允许用户设置定时任务,在预设的时间自动开启或关闭USB电源输出。
  4. 状态反馈: 能够实时反馈当前USB电源输出的状态(开启/关闭),以及定时任务的执行状态。
  5. 配置管理: 提供配置接口,允许用户配置Wi-Fi网络参数、定时任务等。
  6. 安全可靠: 系统运行稳定可靠,具备一定的安全防护机制,防止未授权访问和恶意操作。
  7. 低功耗: 在满足功能需求的前提下,尽可能降低系统功耗,延长使用寿命。
  8. 易于维护升级: 系统设计应具有良好的可维护性和可升级性,方便后续的功能扩展和bug修复。

硬件平台: 根据图片所示,硬件平台主要包括:

  • 主控芯片: ESP8266 或 ESP32 (基于图片上的ESP模块推测)。ESP系列芯片具有强大的Wi-Fi功能和处理能力,适合物联网应用。
  • 继电器: 用于控制USB电源输出的通断。
  • Type-C 输入接口: 用于接收电源输入。
  • USB-A 输出接口: 用于提供电源输出。
  • 指示灯 (LED): 用于指示系统状态和USB电源输出状态。
  • 按钮 (可选): 用于本地控制和配置。

软件架构设计

为了构建一个可靠、高效、可扩展的嵌入式系统,我将采用分层架构模块化设计相结合的方式。这种架构能够将系统功能分解为独立的模块,降低模块间的耦合度,提高代码的可维护性和可重用性。

系统分层架构:

  1. 硬件抽象层 (HAL - Hardware Abstraction Layer):

    • 功能: 封装底层硬件驱动,向上层提供统一的硬件接口。
    • 模块: GPIO 驱动、定时器驱动、UART 驱动、Wi-Fi 驱动、Flash 驱动、看门狗驱动等。
    • 目的: 屏蔽硬件差异,使得上层应用代码可以独立于具体的硬件平台,方便移植和维护。
  2. 板级支持包 (BSP - Board Support Package):

    • 功能: 初始化硬件平台,配置系统时钟、外设等,为操作系统和应用层提供基础支持。
    • 模块: 系统初始化模块、时钟配置模块、中断管理模块、内存管理模块等。
    • 目的: 为特定的硬件平台提供必要的初始化和配置,确保系统能够正常运行。
  3. 操作系统层 (OS - Operating System):

    • 功能: 提供任务调度、内存管理、进程间通信、同步机制等核心服务,提高系统的并发性和实时性。
    • 选择: FreeRTOS (轻量级实时操作系统) 或其他合适的嵌入式操作系统。
    • 目的: 简化多任务编程,提高系统效率和响应速度。
  4. 中间件层 (Middleware):

    • 功能: 提供常用的软件组件和服务,例如网络协议栈、数据存储、加密算法等。
    • 模块: TCP/IP 协议栈 (lwIP 或 ESP-IDF 自带)、MQTT 客户端、JSON 解析库、NTP 客户端、配置管理模块等。
    • 目的: 复用成熟的软件组件,减少重复开发,加速项目进度。
  5. 应用层 (Application Layer):

    • 功能: 实现系统的核心业务逻辑,包括本地控制、远程控制、定时控制、状态反馈等。
    • 模块: 电源控制模块、定时任务管理模块、远程控制模块、配置管理模块、状态监控模块、用户界面模块 (可选)。
    • 目的: 实现用户可见的功能,满足项目需求。

模块化设计:

在每一层架构内部,以及应用层中,都将采用模块化设计,将功能划分为独立的模块。例如,应用层可以划分为以下模块:

  • 电源控制模块 (PowerControl): 负责直接控制继电器,实现USB电源输出的开启和关闭。
  • 定时任务管理模块 (TimerTask): 负责管理定时任务的创建、删除、启动、停止和执行。
  • 远程控制模块 (RemoteControl): 负责处理远程控制指令,例如通过 MQTT 协议接收和发送控制命令和状态信息。
  • 配置管理模块 (ConfigManager): 负责加载、保存和管理系统配置信息,例如 Wi-Fi 参数、定时任务设置等。
  • 状态监控模块 (StatusMonitor): 负责监控系统和电源状态,并将状态信息反馈给用户或远程控制端。
  • 用户界面模块 (UserInterface - 可选): 如果需要本地用户界面,例如通过按钮和指示灯进行交互,则需要用户界面模块来处理用户输入和显示系统状态。

关键技术选择与方法

  1. 实时操作系统 (RTOS): 选择 FreeRTOS 作为操作系统,可以有效地管理多个任务,例如电源控制任务、定时任务、网络通信任务等,提高系统的实时性和并发性。
  2. Wi-Fi 通信: 利用 ESP8266/ESP32 的 Wi-Fi 功能,实现远程控制和状态反馈。选择 MQTT 协议作为远程控制的通信协议,MQTT 协议轻量级、低功耗,适合物联网应用。
  3. 定时器: 使用硬件定时器或 FreeRTOS 软件定时器来实现定时任务功能。硬件定时器精度更高,但资源有限;软件定时器更灵活,但精度稍低。根据实际需求选择合适的定时器。
  4. GPIO 控制: 使用 GPIO 控制继电器的开关,实现 USB 电源输出的通断控制。
  5. 非易失性存储: 使用 Flash 存储器来保存系统配置信息和定时任务设置,保证数据在断电后不会丢失。可以使用 SPIFFS 或 LittleFS 等文件系统来管理 Flash 存储。
  6. 低功耗设计: 在软件设计中,尽量减少 CPU 的运行时间和外设的使用,例如使用低功耗模式、优化代码执行效率、减少 Wi-Fi 通信频率等,以降低系统功耗。
  7. 错误处理与异常处理: 在代码中加入必要的错误处理和异常处理机制,例如输入参数校验、硬件状态检查、网络连接错误处理等,提高系统的鲁棒性和可靠性。
  8. 代码风格与规范: 遵循良好的代码风格和编码规范,例如使用清晰的命名、合理的注释、模块化设计、代码审查等,提高代码的可读性和可维护性。

C 代码实现 (部分关键模块示例,完整代码超过3000行)

为了演示代码架构和关键模块的实现,我将提供部分核心模块的 C 代码示例。由于完整代码超过3000行,这里只展示关键部分,完整代码将包括所有模块的详细实现、头文件、配置文件、构建脚本等。

1. 硬件抽象层 (HAL) - hal_gpio.hhal_gpio.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
// hal_gpio.h
#ifndef HAL_GPIO_H
#define HAL_GPIO_H

typedef enum {
GPIO_PIN_0,
GPIO_PIN_1,
// ... 定义所有可用的 GPIO 引脚
GPIO_PIN_MAX
} gpio_pin_t;

typedef enum {
GPIO_MODE_INPUT,
GPIO_MODE_OUTPUT
} gpio_mode_t;

typedef enum {
GPIO_LEVEL_LOW,
GPIO_LEVEL_HIGH
} gpio_level_t;

typedef enum {
GPIO_PULL_NONE,
GPIO_PULL_UP,
GPIO_PULL_DOWN
} gpio_pull_mode_t;

// 初始化 GPIO 引脚
void hal_gpio_init(gpio_pin_t pin, gpio_mode_t mode, gpio_pull_mode_t pull_mode);

// 设置 GPIO 输出电平
void hal_gpio_set_level(gpio_pin_t pin, gpio_level_t level);

// 读取 GPIO 输入电平
gpio_level_t hal_gpio_get_level(gpio_pin_t pin);

#endif // HAL_GPIO_H
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// hal_gpio.c
#include "hal_gpio.h"
#include "esp_system.h" // 假设使用 ESP-IDF 框架

void hal_gpio_init(gpio_pin_t pin, 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 << pin); // 配置引脚
io_conf.mode = (mode == GPIO_MODE_OUTPUT) ? GPIO_MODE_OUTPUT : GPIO_MODE_INPUT;
io_conf.pull_down_en = (pull_mode == GPIO_PULL_DOWN);
io_conf.pull_up_en = (pull_mode == GPIO_PULL_UP);
gpio_config(&io_conf);
}

void hal_gpio_set_level(gpio_pin_t pin, gpio_level_t level) {
gpio_set_level(pin, (level == GPIO_LEVEL_HIGH) ? 1 : 0);
}

gpio_level_t hal_gpio_get_level(gpio_pin_t pin) {
return (gpio_get_level(pin) == 1) ? GPIO_LEVEL_HIGH : GPIO_LEVEL_LOW;
}

2. 电源控制模块 (PowerControl) - power_control.hpower_control.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// power_control.h
#ifndef POWER_CONTROL_H
#define POWER_CONTROL_H

typedef enum {
POWER_STATE_OFF,
POWER_STATE_ON
} power_state_t;

// 初始化电源控制模块
void power_control_init(gpio_pin_t relay_pin);

// 设置电源状态 (开启/关闭)
void power_control_set_state(power_state_t state);

// 获取当前电源状态
power_state_t power_control_get_state(void);

#endif // POWER_CONTROL_H
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
// power_control.c
#include "power_control.h"
#include "hal_gpio.h"

static gpio_pin_t relay_gpio_pin;
static power_state_t current_power_state = POWER_STATE_OFF;

void power_control_init(gpio_pin_t relay_pin) {
relay_gpio_pin = relay_pin;
hal_gpio_init(relay_gpio_pin, GPIO_MODE_OUTPUT, GPIO_PULL_NONE);
power_control_set_state(POWER_STATE_OFF); // 默认关闭
}

void power_control_set_state(power_state_t state) {
if (state == POWER_STATE_ON) {
hal_gpio_set_level(relay_gpio_pin, GPIO_LEVEL_HIGH); // 假设高电平驱动继电器开启
current_power_state = POWER_STATE_ON;
} else {
hal_gpio_set_level(relay_gpio_pin, GPIO_LEVEL_LOW); // 低电平关闭
current_power_state = POWER_STATE_OFF;
}
}

power_state_t power_control_get_state(void) {
return current_power_state;
}

3. 定时任务管理模块 (TimerTask) - timer_task.htimer_task.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
// timer_task.h
#ifndef TIMER_TASK_H
#define TIMER_TASK_H

#include <stdint.h>

typedef struct {
uint32_t hour;
uint32_t minute;
power_state_t target_state; // 定时任务的目标状态 (ON/OFF)
bool enabled; // 定时任务是否启用
char name[32]; // 定时任务名称 (可选)
// ... 可以添加更多定时任务属性,例如重复周期、日期等
} timer_task_config_t;

typedef void (*timer_task_callback_t)(power_state_t state);

// 初始化定时任务管理模块
void timer_task_init(timer_task_callback_t callback);

// 创建定时任务
int timer_task_create(timer_task_config_t *config);

// 删除定时任务
int timer_task_delete(int task_id);

// 启动定时任务
int timer_task_start(int task_id);

// 停止定时任务
int timer_task_stop(int task_id);

// 获取所有定时任务配置
int timer_task_get_all(timer_task_config_t *tasks, int max_tasks, int *task_count);

// 加载定时任务配置 (从 Flash)
void timer_task_load_config(void);

// 保存定时任务配置 (到 Flash)
void timer_task_save_config(void);

#endif // TIMER_TASK_H
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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
// timer_task.c
#include "timer_task.h"
#include "FreeRTOS.h"
#include "timers.h"
#include "time.h"
#include "sys/time.h"
#include "esp_log.h"
#include "config_manager.h" // 假设配置管理模块负责 Flash 存储

#define MAX_TIMER_TASKS 5 // 最大定时任务数量

static const char *TAG = "TimerTask";

static TimerHandle_t timer_handles[MAX_TIMER_TASKS];
static timer_task_config_t task_configs[MAX_TIMER_TASKS];
static timer_task_callback_t power_state_callback;
static int task_count = 0;

static void timer_callback_function(TimerHandle_t xTimer);
static bool is_time_match(const timer_task_config_t *task);

void timer_task_init(timer_task_callback_t callback) {
power_state_callback = callback;
memset(timer_handles, 0, sizeof(timer_handles));
memset(task_configs, 0, sizeof(task_configs));
task_count = 0;
timer_task_load_config(); // 从 Flash 加载配置
}

int timer_task_create(timer_task_config_t *config) {
if (task_count >= MAX_TIMER_TASKS) {
ESP_LOGE(TAG, "Maximum timer tasks reached");
return -1; // 任务数量已满
}

int task_id = task_count;
task_configs[task_id] = *config;
task_configs[task_id].enabled = true; // 默认启用任务

timer_handles[task_id] = xTimerCreate(
config->name, // 定时器名称
pdMS_TO_TICKS(1000), // 定时周期 (1秒,用于周期性检查时间)
pdTRUE, // 自动重载
(void *)task_id, // 定时器 ID
timer_callback_function
);

if (timer_handles[task_id] == NULL) {
ESP_LOGE(TAG, "Failed to create timer for task %d", task_id);
return -2; // 定时器创建失败
}

if (xTimerStart(timer_handles[task_id], 0) != pdPASS) {
ESP_LOGE(TAG, "Failed to start timer for task %d", task_id);
vTimerDelete(timer_handles[task_id], 0);
timer_handles[task_id] = NULL;
return -3; // 定时器启动失败
}

task_count++;
timer_task_save_config(); // 保存配置到 Flash
return task_id;
}

int timer_task_delete(int task_id) {
if (task_id < 0 || task_id >= task_count || timer_handles[task_id] == NULL) {
ESP_LOGE(TAG, "Invalid task ID: %d", task_id);
return -1; // 无效的任务 ID
}

if (xTimerStop(timer_handles[task_id], 0) != pdPASS) {
ESP_LOGE(TAG, "Failed to stop timer for task %d", task_id);
}
if (xTimerDelete(timer_handles[task_id], 0) != pdPASS) {
ESP_LOGE(TAG, "Failed to delete timer for task %d", task_id);
return -2; // 定时器删除失败
}

timer_handles[task_id] = NULL;
memset(&task_configs[task_id], 0, sizeof(timer_task_config_t));

// 重新排列任务数组 (可选,如果需要紧凑的数组)
for (int i = task_id; i < task_count - 1; ++i) {
timer_handles[i] = timer_handles[i + 1];
task_configs[i] = task_configs[i + 1];
}
timer_handles[task_count - 1] = NULL;
memset(&task_configs[task_count - 1], 0, sizeof(timer_task_config_t));
task_count--;

timer_task_save_config(); // 保存配置到 Flash
return 0;
}

int timer_task_start(int task_id) {
if (task_id < 0 || task_id >= task_count || timer_handles[task_id] == NULL) {
ESP_LOGE(TAG, "Invalid task ID: %d", task_id);
return -1; // 无效的任务 ID
}
task_configs[task_id].enabled = true;
timer_task_save_config();
return 0;
}

int timer_task_stop(int task_id) {
if (task_id < 0 || task_id >= task_count || timer_handles[task_id] == NULL) {
ESP_LOGE(TAG, "Invalid task ID: %d", task_id);
return -1; // 无效的任务 ID
}
task_configs[task_id].enabled = false;
timer_task_save_config();
return 0;
}

int timer_task_get_all(timer_task_config_t *tasks, int max_tasks, int *task_count_out) {
if (tasks == NULL || max_tasks < task_count) {
return -1; // 参数错误或缓冲区不足
}
int count = 0;
for (int i = 0; i < task_count && count < max_tasks; ++i) {
if (timer_handles[i] != NULL) {
tasks[count++] = task_configs[i];
}
}
*task_count_out = count;
return 0;
}


static void timer_callback_function(TimerHandle_t xTimer) {
int task_id = (int)pvTimerGetTimerID(xTimer);
if (task_id >= 0 && task_id < task_count && task_configs[task_id].enabled) {
if (is_time_match(&task_configs[task_id])) {
ESP_LOGI(TAG, "Timer task %d triggered, target state: %s", task_id, task_configs[task_id].target_state == POWER_STATE_ON ? "ON" : "OFF");
if (power_state_callback != NULL) {
power_state_callback(task_configs[task_id].target_state);
}
}
}
}

static bool is_time_match(const timer_task_config_t *task) {
time_t now;
struct tm timeinfo;
time(&now);
localtime_r(&now, &timeinfo);

return (timeinfo.tm_hour == task->hour && timeinfo.tm_min == task->minute);
}


// 假设使用 ConfigManager 模块来处理 Flash 配置
void timer_task_load_config(void) {
config_manager_get_timer_tasks(task_configs, MAX_TIMER_TASKS, &task_count);
for(int i=0; i<task_count; ++i) {
if (task_configs[i].enabled) { // 重新创建并启动已启用的定时器
timer_handles[i] = xTimerCreate(
task_configs[i].name,
pdMS_TO_TICKS(1000),
pdTRUE,
(void *)i,
timer_callback_function
);
if (timer_handles[i] != NULL) {
xTimerStart(timer_handles[i], 0);
} else {
ESP_LOGE(TAG, "Failed to recreate timer %d from config", i);
}
}
}
}

void timer_task_save_config(void) {
config_manager_set_timer_tasks(task_configs, task_count);
}

4. 远程控制模块 (RemoteControl) - remote_control.hremote_control.c (MQTT 示例)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// remote_control.h
#ifndef REMOTE_CONTROL_H
#define REMOTE_CONTROL_H

#include "power_control.h"
#include "timer_task.h"

// 初始化远程控制模块 (例如 MQTT 客户端)
void remote_control_init(power_control_set_state_callback_t power_callback, timer_task_create_callback_t timer_create_callback, timer_task_delete_callback_t timer_delete_callback);

// 处理接收到的远程控制消息
void remote_control_process_message(const char *topic, const char *payload);

// 发送电源状态到远程控制端
void remote_control_send_power_state(power_state_t state);

#endif // REMOTE_CONTROL_H
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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
// remote_control.c
#include "remote_control.h"
#include "mqtt_client.h"
#include "esp_log.h"
#include "cJSON.h"

static const char *TAG = "RemoteControl";

#define MQTT_BROKER_URI "mqtt://your_broker_address:1883" // 替换为你的 MQTT Broker 地址
#define MQTT_CLIENT_ID "usb_power_controller"
#define MQTT_TOPIC_COMMAND "usb_power/command"
#define MQTT_TOPIC_STATUS "usb_power/status"

static esp_mqtt_client_handle_t mqtt_client = NULL;
static power_control_set_state_callback_t power_callback_func;
static timer_task_create_callback_t timer_create_callback_func;
static timer_task_delete_callback_t timer_delete_callback_func;


static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data);
static void handle_mqtt_command(const char *payload);
static void handle_mqtt_timer_command(const char *payload);


void remote_control_init(power_control_set_state_callback_t power_callback, timer_task_create_callback_t timer_create_callback, timer_task_delete_callback_t timer_delete_callback) {
power_callback_func = power_callback;
timer_create_callback_func = timer_create_callback;
timer_delete_callback_func = timer_delete_callback;

esp_mqtt_client_config_t mqtt_cfg = {
.uri = MQTT_BROKER_URI,
.client_id = MQTT_CLIENT_ID,
// ... 可以配置用户名密码等
};

mqtt_client = esp_mqtt_client_init(&mqtt_cfg);
esp_mqtt_client_register_event(mqtt_client, ESP_EVENT_ANY_ID, mqtt_event_handler, NULL);
esp_mqtt_client_start(mqtt_client);
}


static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data) {
ESP_LOGD(TAG, "Event dispatched from event loop base=%s, event_id=%d", base, event_id);
esp_mqtt_event_handle_t event = event_data;
esp_mqtt_client_handle_t client = event->client;
int msg_id;
switch ((esp_mqtt_event_id_t)event_id) {
case MQTT_EVENT_CONNECTED:
ESP_LOGI(TAG, "MQTT_EVENT_CONNECTED");
msg_id = esp_mqtt_client_subscribe(client, MQTT_TOPIC_COMMAND, 0);
ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id);
remote_control_send_power_state(power_control_get_state()); // 连接成功后发送当前状态
break;
case MQTT_EVENT_DISCONNECTED:
ESP_LOGI(TAG, "MQTT_EVENT_DISCONNECTED");
break;

case MQTT_EVENT_SUBSCRIBED:
ESP_LOGI(TAG, "MQTT_EVENT_SUBSCRIBED, msg_id=%d", event->msg_id);
break;
case MQTT_EVENT_UNSUBSCRIBED:
ESP_LOGI(TAG, "MQTT_EVENT_UNSUBSCRIBED, msg_id=%d", event->msg_id);
break;
case MQTT_EVENT_PUBLISHED:
ESP_LOGI(TAG, "MQTT_EVENT_PUBLISHED, msg_id=%d", event->msg_id);
break;
case MQTT_EVENT_DATA:
ESP_LOGI(TAG, "MQTT_EVENT_DATA");
ESP_LOGI(TAG, "TOPIC=%.*s\r\n", event->topic_len, event->topic);
ESP_LOGI(TAG, "DATA=%.*s\r\n", event->data_len, event->data);
if (strncmp(event->topic, MQTT_TOPIC_COMMAND, event->topic_len) == 0) {
handle_mqtt_command(event->data);
}
break;
case MQTT_EVENT_ERROR:
ESP_LOGI(TAG, "MQTT_EVENT_ERROR");
break;
default:
ESP_LOGI(TAG, "Other event id:%d", event->event_id);
break;
}
}


static void handle_mqtt_command(const char *payload) {
cJSON *root = cJSON_Parse(payload);
if (root == NULL) {
ESP_LOGE(TAG, "Failed to parse JSON command: %s", payload);
return;
}

cJSON *command_type_json = cJSON_GetObjectItemCaseSensitive(root, "type");
if (!cJSON_IsString(command_type_json) || (command_type_json->valuestring == NULL)) {
ESP_LOGE(TAG, "Invalid command type in JSON: %s", payload);
cJSON_Delete(root);
return;
}

const char *command_type = command_type_json->valuestring;

if (strcmp(command_type, "power") == 0) {
cJSON *state_json = cJSON_GetObjectItemCaseSensitive(root, "state");
if (!cJSON_IsString(state_json) || (state_json->valuestring == NULL)) {
ESP_LOGE(TAG, "Invalid power state in JSON: %s", payload);
} else {
const char *state_str = state_json->valuestring;
if (strcmp(state_str, "on") == 0) {
power_callback_func(POWER_STATE_ON);
} else if (strcmp(state_str, "off") == 0) {
power_callback_func(POWER_STATE_OFF);
} else {
ESP_LOGE(TAG, "Unknown power state: %s", state_str);
}
}
} else if (strcmp(command_type, "timer") == 0) {
handle_mqtt_timer_command(payload);
} else {
ESP_LOGW(TAG, "Unknown command type: %s", command_type);
}

cJSON_Delete(root);
}


static void handle_mqtt_timer_command(const char *payload) {
cJSON *root = cJSON_Parse(payload);
if (root == NULL) {
ESP_LOGE(TAG, "Failed to parse JSON timer command: %s", payload);
return;
}

cJSON *action_json = cJSON_GetObjectItemCaseSensitive(root, "action");
if (!cJSON_IsString(action_json) || (action_json->valuestring == NULL)) {
ESP_LOGE(TAG, "Invalid timer action in JSON: %s", payload);
cJSON_Delete(root);
return;
}

const char *action = action_json->valuestring;

if (strcmp(action, "create") == 0) {
timer_task_config_t task_config;
memset(&task_config, 0, sizeof(task_config));

cJSON *hour_json = cJSON_GetObjectItemCaseSensitive(root, "hour");
cJSON *minute_json = cJSON_GetObjectItemCaseSensitive(root, "minute");
cJSON *target_state_json = cJSON_GetObjectItemCaseSensitive(root, "target_state");
cJSON *name_json = cJSON_GetObjectItemCaseSensitive(root, "name");

if (!cJSON_IsNumber(hour_json) || !cJSON_IsNumber(minute_json) || !cJSON_IsString(target_state_json)) {
ESP_LOGE(TAG, "Invalid timer parameters in JSON: %s", payload);
} else {
task_config.hour = hour_json->valueint;
task_config.minute = minute_json->valueint;
if (strcmp(target_state_json->valuestring, "on") == 0) {
task_config.target_state = POWER_STATE_ON;
} else if (strcmp(target_state_json->valuestring, "off") == 0) {
task_config.target_state = POWER_STATE_OFF;
} else {
ESP_LOGE(TAG, "Unknown target_state: %s", target_state_json->valuestring);
cJSON_Delete(root);
return;
}
if (name_json && cJSON_IsString(name_json)) {
strncpy(task_config.name, name_json->valuestring, sizeof(task_config.name) - 1);
} else {
snprintf(task_config.name, sizeof(task_config.name), "TimerTask_%d", (int)esp_random()); // 默认名称
}

if (timer_create_callback_func) {
timer_create_callback_func(&task_config);
}
}
} else if (strcmp(action, "delete") == 0) {
cJSON *task_id_json = cJSON_GetObjectItemCaseSensitive(root, "task_id");
if (!cJSON_IsNumber(task_id_json)) {
ESP_LOGE(TAG, "Invalid task_id in JSON for delete command: %s", payload);
} else {
if (timer_delete_callback_func) {
timer_delete_callback_func(task_id_json->valueint);
}
}
} else {
ESP_LOGW(TAG, "Unknown timer action: %s", action);
}

cJSON_Delete(root);
}


void remote_control_send_power_state(power_state_t state) {
if (mqtt_client == NULL) return;

cJSON *root = cJSON_CreateObject();
cJSON_AddStringToObject(root, "type", "power_status");
cJSON_AddStringToObject(root, "state", (state == POWER_STATE_ON) ? "on" : "off");
char *json_str = cJSON_PrintUnformatted(root);
if (json_str != NULL) {
esp_mqtt_client_publish(mqtt_client, MQTT_TOPIC_STATUS, json_str, 0, 1, 0); // QoS 1, 保留消息
free(json_str);
}
cJSON_Delete(root);
}

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
// main.c
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_spi_flash.h"
#include "nvs_flash.h"
#include "esp_netif.h"
#include "esp_event.h"
#include "esp_wifi.h"
#include "esp_log.h"
#include "lwip/err.h"
#include "lwip/sys.h"

#include "hal_gpio.h"
#include "power_control.h"
#include "timer_task.h"
#include "remote_control.h"
#include "config_manager.h" // 假设有配置管理模块

#define RELAY_GPIO_PIN GPIO_NUM_2 // 继电器控制引脚 (根据实际硬件连接修改)
#define STATUS_LED_GPIO GPIO_NUM_4 // 状态指示灯引脚 (可选)

static const char *TAG = "MainApp";

// 电源状态改变回调函数
static void power_state_changed_callback(power_state_t state) {
ESP_LOGI(TAG, "Power state changed to: %s", (state == POWER_STATE_ON) ? "ON" : "OFF");
// 可以添加状态指示灯控制代码
// hal_gpio_set_level(STATUS_LED_GPIO, (state == POWER_STATE_ON) ? GPIO_LEVEL_HIGH : GPIO_LEVEL_LOW);
remote_control_send_power_state(state); // 通知远程控制端
}

// 定时任务创建回调 (远程控制模块调用)
static int timer_task_create_callback(timer_task_config_t *config) {
return timer_task_create(config);
}

// 定时任务删除回调 (远程控制模块调用)
static int timer_task_delete_callback(int task_id) {
return timer_task_delete(task_id);
}


void app_main(void)
{
// 初始化 NVS (非易失性存储)
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);

// 初始化事件循环
ESP_ERROR_CHECK(esp_event_loop_create_default());

// 初始化 Wi-Fi (STA 模式) - 需要配置 Wi-Fi SSID 和 Password
wifi_init_sta(); // 假设在 wifi_config.c 中实现

// 初始化硬件模块
hal_gpio_init(STATUS_LED_GPIO, GPIO_MODE_OUTPUT, GPIO_PULL_NONE); // 初始化状态指示灯 (可选)
power_control_init(RELAY_GPIO_PIN);

// 初始化配置管理模块 (加载配置)
config_manager_init();

// 初始化定时任务管理模块
timer_task_init(power_state_changed_callback);

// 初始化远程控制模块 (MQTT)
remote_control_init(power_state_changed_callback, timer_task_create_callback, timer_task_delete_callback);


// 主应用循环 (可以添加本地控制逻辑,例如按钮检测)
while (1) {
vTaskDelay(pdMS_TO_TICKS(1000)); // 1秒循环一次
// ... 本地控制逻辑 (例如检测按钮状态,切换电源状态) ...
}
}

6. 配置管理模块 (ConfigManager) - 示例框架

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// config_manager.h
#ifndef CONFIG_MANAGER_H
#define CONFIG_MANAGER_H

#include "timer_task.h"

// 初始化配置管理模块
void config_manager_init(void);

// 获取 Wi-Fi 配置
void config_manager_get_wifi_config(char *ssid, size_t ssid_len, char *password, size_t password_len);

// 设置 Wi-Fi 配置
void config_manager_set_wifi_config(const char *ssid, const char *password);

// 获取定时任务配置
void config_manager_get_timer_tasks(timer_task_config_t *tasks, int max_tasks, int *task_count);

// 设置定时任务配置
void config_manager_set_timer_tasks(const timer_task_config_t *tasks, int task_count);

#endif // CONFIG_MANAGER_H
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// config_manager.c
#include "config_manager.h"
#include "nvs.h"
#include "nvs_flash.h"
#include "esp_log.h"

#define NVS_NAMESPACE "config"
#define WIFI_SSID_KEY "wifi_ssid"
#define WIFI_PASSWORD_KEY "wifi_password"
#define TIMER_TASKS_KEY "timer_tasks" // 存储定时任务配置的 key (可以使用 JSON 格式)

static const char *TAG = "ConfigManager";

void config_manager_init(void) {
// ... 初始化 NVS ...
}

// ... 实现 Wi-Fi 配置的 获取和设置 ...

// ... 实现 定时任务配置的 获取和设置 (需要序列化和反序列化定时任务数组到 Flash) ...
// 可以使用 JSON 格式来存储定时任务数组,并使用 cJSON 库进行解析和生成

7. Wi-Fi 配置模块 (wifi_config.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
// wifi_config.c
#include "wifi_config.h"
#include "esp_wifi.h"
#include "esp_log.h"
#include "esp_netif.h"
#include "esp_event.h"
#include "lwip/err.h"
#include "lwip/sys.h"
#include "config_manager.h" // 用于获取 Wi-Fi 配置

#define EXAMPLE_ESP_WIFI_SSID CONFIG_ESP_WIFI_SSID
#define EXAMPLE_ESP_WIFI_PASS CONFIG_ESP_WIFI_PASSWORD
#define EXAMPLE_ESP_MAXIMUM_RETRY CONFIG_ESP_MAXIMUM_RETRY

static const char *TAG = "wifi station";

static int s_retry_num = 0;

static void event_handler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data);

void wifi_init_sta(void)
{
// ... Wi-Fi 初始化代码 (基于 ESP-IDF 示例代码) ...
// 从 ConfigManager 获取 Wi-Fi SSID 和 Password
char ssid[32];
char password[64];
config_manager_get_wifi_config(ssid, sizeof(ssid), password, sizeof(password));

wifi_config_t wifi_config = {
.sta = {
.ssid = "", // 初始化时为空,后续复制
.password = "",
.threshold.authmode = WIFI_AUTH_WPA2_PSK,
.pmf_cfg = {
.capable = true,
.required = false
},
},
};
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);

// ... 配置和启动 Wi-Fi ...
}

static void event_handler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data)
{
// ... Wi-Fi 事件处理函数 (连接成功、连接失败、断开连接等) ...
}

测试验证

在完成代码编写后,需要进行全面的测试验证,以确保系统的功能和性能符合需求。测试阶段包括:

  1. 单元测试: 对每个模块进行单元测试,验证模块功能的正确性,例如电源控制模块、定时任务模块、远程控制模块等。可以使用 CUnit 或 Unity 等单元测试框架。
  2. 集成测试: 将各个模块集成起来进行测试,验证模块之间的协同工作是否正常,例如电源控制与定时任务的集成、远程控制与电源控制的集成等。
  3. 系统测试: 进行完整的系统测试,模拟实际使用场景,测试系统的所有功能,例如本地控制、远程控制、定时控制、状态反馈、配置管理等。
  4. 性能测试: 测试系统的性能指标,例如响应时间、功耗、并发处理能力等。
  5. 稳定性测试: 长时间运行系统,测试系统的稳定性,例如长时间定时任务执行、频繁远程控制操作等。
  6. 安全性测试: 测试系统的安全性,例如防止未授权访问、防止恶意操作等。

维护升级

嵌入式系统的维护升级是一个持续的过程。为了方便后续的维护升级,需要考虑以下方面:

  1. 固件升级 (OTA - Over-The-Air): 支持远程固件升级功能,方便后续的功能扩展和 bug 修复。可以使用 ESP-IDF 提供的 OTA 功能。
  2. 日志记录: 添加详细的日志记录功能,方便问题排查和系统监控。可以使用 ESP-IDF 的日志系统,并将日志输出到 UART 或 Flash 存储。
  3. 错误报告: 在系统发生错误时,能够生成错误报告,方便开发人员分析和解决问题。
  4. 配置管理: 提供友好的配置管理接口,方便用户配置系统参数,例如 Wi-Fi 参数、定时任务设置等。
  5. 文档编写: 编写详细的软件文档,包括架构设计文档、模块设计文档、API 文档、用户手册等,方便后续的维护和开发人员理解和使用代码。
  6. 版本控制: 使用版本控制系统 (例如 Git) 管理代码,方便代码的版本管理和协作开发。

代码行数说明

上述提供的代码示例只是关键模块的框架和部分实现,完整的项目代码将包括:

  • 更完善的 HAL 驱动 (GPIO, Timer, UART, Wi-Fi, Flash, 看门狗等)
  • BSP 初始化代码 (系统时钟配置、外设初始化等)
  • FreeRTOS 操作系统集成代码 (任务创建、调度、同步机制等)
  • 完整的中间件层实现 (TCP/IP 协议栈、MQTT 客户端、JSON 解析库、NTP 客户端、配置管理模块等)
  • 更详细的应用层代码 (本地控制逻辑、用户界面模块、状态监控模块、更完善的定时任务管理、更健壮的远程控制逻辑等)
  • 测试代码 (单元测试、集成测试、系统测试用例等)
  • 构建脚本 (Makefile 或 CMakeLists.txt)
  • 配置文件 (例如 Wi-Fi 配置文件、MQTT 配置文件等)
  • 详细的注释和文档

因此,一个功能完善、稳定可靠、可维护可升级的智能 USB 电源控制器项目的完整 C 代码,加上必要的注释、文档和测试代码,很容易超过 3000 行。

总结

这个智能 USB 电源控制器项目展示了一个典型的嵌入式系统开发流程,从需求分析到系统实现,再到测试验证和维护升级。通过采用分层架构、模块化设计、选择合适的关键技术和方法,并遵循良好的编码规范和开发流程,可以构建一个可靠、高效、可扩展的嵌入式系统平台。希望上述详细的架构设计和代码示例能够帮助您理解嵌入式系统开发的完整过程,并为您提供实际项目开发的参考。

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