20 KiB
智能充电桩管理系统 (Charging Pile Management System)
1. 项目简介
本项目是一个基于 Spring Boot (后端) 和 Next.js (前端) 构建的智能充电桩管理与计费系统。旨在为用户提供便捷的充电服务,并为管理员提供高效的充电桩、用户及计费管理功能。支持通过 MQTT 协议与充电桩硬件(单片机)进行实时通信和控制。
核心功能
- 用户端:
- 用户注册与登录
- 查看账户余额、充电记录
- 扫码/输入ID启动充电
- 停止充电
- 激活码兑换充值
- 管理端:
- 用户管理(查询、添加、编辑、删除)
- 充电桩/车位管理(状态监控、添加、编辑、删除)
- 充电会话管理(查询、监控)
- 激活码管理(生成、查询、删除)
- MQTT通信日志查看(查询、审计所有MQTT消息)
- 系统概览与统计
- 硬件交互 (通过 MQTT):
- 充电桩状态上报 (空闲、连接中、充电中、故障等)
- 远程启动/停止充电指令下发
- 心跳维持
2. 技术栈
- 后端: Spring Boot, Spring Security, MyBatis Plus, MySQL, Mosquitto (MQTT Broker), Spring AOP (用于日志)
- 前端: Next.js, React, TypeScript, Ant Design, Axios
- 数据库: MySQL
- 通信协议: HTTP/HTTPS, MQTT
3. 项目部署与运行
3.1. 环境准备
- Java JDK 1.8 或更高版本
- Maven 3.x
- Node.js 16.x 或更高版本
- Yarn 或 npm
- MySQL 5.7 或更高版本
- MQTT Broker (例如 Mosquitto) 已安装并运行 (默认端口 1883)
3.2. 后端服务 (springboot-init-main)
- 数据库配置:
- 创建一个数据库 (例如
charging_system_db)。 - 修改
springboot-init-main/src/main/resources/application.yml(或对应环境的配置文件) 中的数据库连接信息:
spring: datasource: url: jdbc:mysql://localhost:3306/your_database_name?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai username: your_db_username password: your_db_password - 创建一个数据库 (例如
- MQTT Broker 配置:
- 确保 MQTT Broker 正在运行。
- 在
application.yml中配置 MQTT 连接参数 (如果需要修改默认值或添加认证):
mqtt: broker-url: tcp://localhost:1883 # 替换为您的 MQTT Broker 地址 client-id-publisher: backend-publisher-default client-id-subscriber: backend-subscriber-default default-topic: "charging/default/topic" # 示例,具体业务主题见单片机对接部分 # username: your_mqtt_username # 如果 Broker 需要认证 # password: your_mqtt_password # 如果 Broker 需要认证 - 启动后端服务:
- 在
springboot-init-main目录下,使用 Maven 运行:
mvn spring-boot:run- 或者打包成 jar 文件后运行:
mvn clean package java -jar target/springboot-init-main-0.0.1-SNAPSHOT.jar # 注意替换为实际生成的jar包名- 配置文件外部化:
application.yml在打包时已配置为外部化,并会复制到target/config/application.yml。在生产环境部署时,可以将此文件放置于 JAR 包同级目录的config文件夹下 (例如:your_app_dir/config/application.yml),Spring Boot 将自动加载。或者,您也可以通过--spring.config.location启动参数来指定外部配置文件的具体路径,例如:java -jar your_app.jar --spring.config.location=file:/path/to/your/external/application.yml。 - 服务默认运行在
http://localhost:7529(或您在application.yml中配置的端口)。
- 在
3.3. 前端应用 (charging_web_app)
-
安装依赖: 在
charging_web_app目录下运行:yarn install # 或者 # npm install -
API 代理配置: 前端应用通过 Next.js 的代理将
/api请求转发到后端服务。此配置在next.config.js中:// next.config.js (示例) /** @type {import('next').NextConfig} */ const nextConfig = { reactStrictMode: true, async rewrites() { return [ { source: '/api/:path*', destination: 'http://localhost:7529/api/:path*', // 后端服务地址 }, ]; }, }; module.exports = nextConfig;确保
destination指向您后端服务的正确地址和端口。 -
启动前端开发服务器:
yarn dev # 或者 # npm run dev前端应用默认运行在
http://localhost:3000。 -
构建生产版本:
yarn build yarn start # 或者 # npm run build # npm run start
4. 单片机 (充电桩硬件) 对接指南
智能充电桩硬件(在本例中为基于ESP32的移动机器人)需通过 MQTT 协议与本系统的后端服务进行通信。
注意: 本项目提供了一个新的、功能更完整的ESP32固件项目,位于 esp32_robot_firmware/ 文件夹下。本文档后续的对接指南主要基于此新固件的逻辑。旧的 mqtt_esp32_client.ino 主要作为简易模拟器,其功能已被新固件覆盖和扩展。
4.1. MQTT 连接配置
- Broker 地址: 单片机需配置连接到与后端服务相同的 MQTT Broker (例如
tcp://your_mqtt_broker_address:1883)。 - Client ID: 每个充电桩应使用唯一的 Client ID。对于新固件,这在
esp32_robot_firmware/config.h中通过DEVICE_UID配置,必须与后端系统中注册的机器人ID一致。 - 认证: 如果 MQTT Broker 配置了用户名/密码认证,单片机连接时也需提供 (在
config.h中配置)。
4.2. MQTT 主题 (Topics) 约定
根据后端服务的实际MQTT通信实现,主题约定如下:
4.2.1. 上行消息 (单片机 -> 后端)
所有类型的上行消息,包括设备状态更新、心跳以及对后端指令的执行回执 (ACK),都统一发送到以下主题:
-
统一上行主题格式:
yupi_mqtt_power_project/robot/status/{spotUid}{spotUid}: 充电桩/车位的唯一标识符 (例如ESP32_SPOT_001,即DEVICE_UID)。
-
消息格式 (Payload): JSON。 消息体结构应与后端
com.yupi.project.model.dto.mqtt.RobotStatusMessage类对应。通过消息体内的字段来区分具体的消息含义。示例 - 常规状态更新 (如移动中、充电中、空闲等) 或心跳包 (Heartbeat):
{ "robotUid": "ESP32_SPOT_001", "actualRobotStatus": "CHARGING", // 设备当前状态, 如 IDLE, MOVING, CHARGING, COMPLETED, FAULTED // 以下为可选的详细状态,根据实际需求和后端处理逻辑添加 "voltage": 0, // 充电时相关 (示例值,实际由固件模拟或测量) "current": 0, // 充电时相关 (示例值) "power": 0, // kW, 充电时相关 (示例值) "energyConsumed": 0, // kWh, 本次充电已消耗电量 (示例值) "batteryLevel": 85, // 机器人当前电量百分比 "currentLocation": "SPOT_ID_001", // 机器人当前所在或最后到达的位置ID "chargeDurationSeconds": 120, // 如果在充电状态,已充电时长(秒) "errorCode": 0, // 0表示无错误,其他值表示特定错误类型 "message": "Status normal", // 附加信息,例如错误描述 "activeTaskId": "session_xyz123" // 如果设备当前正在执行某个任务或会话,上报其ID }示例 - 对后端指令的ACK (成功, 以MOVE_TO_SPOT为例): 机器人成功到达目标车位并开始充电后,发送此ACK。 注意:后端期望的ACK格式包含
command_ack(指令的中文描述),task_id(数字类型), 和success(布尔类型)。{ "robotUid": "ESP32_SPOT_001", "command_ack": "移动到指定点", "task_id": 789, "success": true, "message": "Move to spot completed, charging started.", "actualRobotStatus": "CHARGING", "spotId": "SPOT_UID_001" }示例 - 对后端指令的ACK (失败):
{ "robotUid": "ESP32_SPOT_001", "command_ack": "移动到指定点", "task_id": 790, "success": false, "message": "Failed to reach spot, obstacle detected.", "actualRobotStatus": "FAULTED" }
4.2.2. 下行指令 (后端 -> 单片机)
后端服务向特定单片机下发指令时,使用以下主题格式。单片机应订阅其自身的这个专属指令主题。
-
统一下行指令主题格式:
yupi_mqtt_power_project/robot/command/{spotUid}{spotUid}: 目标充电桩的唯一标识符。
-
消息格式 (Payload): JSON。 JSON消息体内部包含了具体的指令类型和所需参数。
示例 - 移动到车位指令 (后端实际发送格式): 此指令指示机器人移动到指定车位。机器人到达后将自动进入充电状态并上报。 后端实际发送的指令可能使用
command字段。{ "command": "MOVE", "target_spot_uid": "SPOT_UID_001", "taskId": "backend_task_id_for_ack_789", "sessionId": "session_abc_123" }- 单片机固件 (
esp32_robot_firmware) 已兼容解析command或commandType字段作为指令类型。
示例 - 停止充电指令 (STOP_CHARGE):
{ "commandType": "STOP_CHARGE", "taskId": "backend_task_id_for_ack_000", "sessionId": "session_abc_123" // 关联的充电会话ID }示例 - 开始充电指令 (START_CHARGE): 注意: 此指令当前未被后端主动使用。业务逻辑已调整为机器人到达车位后自动开始充电 (
MOVE_TO_SPOT完成即充电)。保留此指令定义主要为了兼容性或未来可能的扩展。单片机固件目前仅简单ACK此指令。{ "commandType": "START_CHARGE", "taskId": "backend_task_id_for_ack_111", "sessionId": "session_def_456" // 关联的充电会话ID }- 单片机处理逻辑: 单片机收到消息后,需解析JSON负载,识别
commandType,提取taskId(用于后续ACK),并获取其他指令参数来执行相应操作。- 对于
MOVE_TO_SPOT(或后端发送的MOVE): 执行移动,到达后自动转换到CHARGING状态,并上报符合后端期望格式的ACK (包含command_ack,task_id,success等)及新状态。 - 对于
STOP_CHARGE: 停止充电(如断开继电器),更新状态为COMPLETED(或IDLE),并上报符合后端期望格式的ACK及新状态。 - 对于
START_CHARGE(当前): 仅发送符合后端期望格式的ACK表示收到指令。
- 对于
- 单片机固件 (
4.3. 单片机开发关键逻辑 (基于 esp32_robot_firmware)
- 配置 (
config.h): 仔细配置WiFi凭据、MQTT Broker、DEVICE_UID以及所有硬件引脚。 - MQTT 初始化与重连机制 (由
mqtt_handler.cpp处理)。 - 订阅指令主题 (由
mqtt_handler.cpp处理)。 - JSON 指令消息解析 (在
esp32_robot_firmware.ino的handle_mqtt_command中处理,mqtt_handler.cpp的callback负责通用解析并调用回调)。- 需能正确解析后端下发的指令,如
MOVE(实际) 或MOVE_TO_SPOT(枚举定义)。
- 需能正确解析后端下发的指令,如
- 移动控制 (由
motor_control.cpp和navigation.cpp实现)。 - 充电启停控制 (由
relay_control.cpp实现,停止充电时操作继电器)。 - 状态监测与实时上报 (主要在
esp32_robot_firmware.ino的loop()函数和handle_mqtt_command中管理和触发):- 收到
MOVE(或MOVE_TO_SPOT) 指令后,current_robot_status变为MOVING,target_spot_for_move被设置,并立即上报状态。 loop()函数中的导航逻辑 (navigation_move_to_spot()) 执行移动。- 成功到达目标车位后 (
navigation_move_to_spot()返回成功):current_robot_status变为CHARGING。robot_current_location更新为到达的车位ID。- 调用
relay_start_charging()。 - 向上行主题发送
MOVE指令的成功ACK。此ACK需符合后端期望格式: 包含robotUid,command_ack(值为"移动到指定点"),task_id(来自原指令, 类型为数字),success(true),message,actualRobotStatus("CHARGING"), 和spotId。 - 立即上报一次完整的
CHARGING状态。
- 导航失败后 (
navigation_move_to_spot()返回失败):current_robot_status变为FAULTED。- 向上行主题发送
MOVE指令的失败ACK。此ACK需符合后端期望格式: 包含command_ack(值为"移动到指定点"),task_id,success(false),message(错误信息),actualRobotStatus("FAULTED")。 - 立即上报一次完整的
FAULTED状态。
- 收到
STOP_CHARGE后:- 调用
relay_stop_charging()。 current_robot_status变为COMPLETED(或IDLE,根据固件逻辑)。- 向上行主题发送
STOP_CHARGE的成功ACK。此ACK需符合后端期望格式: 包含command_ack(值为"停止充电"),task_id,success(true),message,actualRobotStatus("COMPLETED")。 - 立即上报一次完整的状态。
- 调用
- 收到
- 心跳包定时发送 (由
esp32_robot_firmware.ino的loop()函数处理)。 - 故障检测与上报 (部分在导航逻辑中处理,状态上报时包含错误码)。
- 安全性: 确保
DEVICE_UID唯一性;推荐 MQTT 通信使用 TLS/SSL 加密 (当前固件未直接集成,需额外配置)。 - 电池电量模拟与上报 (由
esp32_robot_firmware.ino的simulate_battery_update()和状态上报逻辑处理)。
4.4. 示例流程:用户启动充电 (已更新为到达即充逻辑)
- 用户在前端 App 请求在车位
SPOT001进行充电。 - 后端服务验证,创建充电会话
sessionId=789,状态为REQUESTED。 - 后端分配机器人 (例如
DEVICE_UID=ESP32_BOT_001),创建MOVE_TO_SPOT任务 (例如taskId=task_move_123),并将机器人DB状态更新为ASSIGNED(或MOVING,取决于后端状态流转设计),会话状态更新为ROBOT_ASSIGNED。 - 后端向 MQTT 主题
yupi_mqtt_power_project/robot/command/ESP32_BOT_001发布MOVE_TO_SPOT指令,payload 包含taskId=task_move_123和target_spot_uid="SPOT001"。 - 单片机
ESP32_BOT_001接收指令: a. 将其内部状态current_robot_status更新为MOVING。 b. 向上行主题yupi_mqtt_power_project/robot/status/ESP32_BOT_001上报状态MOVING。 c. 执行导航到SPOT001。 d. 移动到达后,将其内部状态current_robot_status更新为CHARGING,robot_current_location更新为SPOT001,并启动充电 (闭合继电器)。 e. 向上行主题发送 ACK 消息,确认taskId=task_move_123完成,消息中actualRobotStatus为CHARGING,并包含spotId="SPOT001"。 f. (通常ACK消息已包含最新状态,但为确保) 再次向上行主题上报完整的CHARGING状态,包含sessionId和chargeDurationSeconds(初始为0)。 - 后端接收到
MOVE_TO_SPOT任务 (taskId=task_move_123) 的 ACK (其中机器人状态为CHARGING): a. 将RobotTask(taskId=task_move_123) 标记为COMPLETED。 b. 将机器人ESP32_BOT_001在数据库中的状态更新为CHARGING。 c. 将充电会话sessionId=789的状态更新为CHARGING_STARTED,并记录充电开始时间。 - 前端界面同步更新,显示充电已开始。
5. 项目结构 (简要)
. 项目根目录
├── charging_web_app/ # 前端 Next.js 应用
│ ├── src/
│ ├── public/
│ └── package.json
├── esp32_robot_firmware/ # ESP32 机器人固件项目 (推荐使用)
│ ├── esp32_robot_firmware.ino # 主程序文件
│ ├── config.h # 配置文件 (WiFi, MQTT, 引脚等)
│ ├── mqtt_handler.h/.cpp # MQTT 通信处理
│ ├── motor_control.h/.cpp # 电机与舵机控制
│ ├── sensor_handler.h/.cpp # 传感器数据读取
│ ├── navigation.h/.cpp # 导航与循迹逻辑
│ ├── relay_control.h/.cpp # 继电器控制 (模拟充电)
│ └── NEXT_STEPS.md # 后续开发与测试步骤指南
├── springboot-init-main/ # 后端 Spring Boot 应用
│ ├── src/
│ │ ├── main/
│ │ │ ├── java/com/yupi/project/
│ │ │ └── resources/
│ │ └── test/
│ ├── doc/ # 项目文档 (如数据库DDL, 阶段计划等)
│ └── pom.xml
├── mqtt_esp32_client.ino # 旧的、简易的ESP32 MQTT模拟器 (不推荐新开发使用)
└── README.md # 本文件
6. ESP32 固件 (esp32_robot_firmware) 使用入门
- 环境准备:
- 安装 Arduino IDE 或 PlatformIO IDE。
- 在IDE中安装 ESP32 开发板支持。
- 根据需要安装相关库 (如
PubSubClientfor MQTT,ESP32Servofor Servo control等,大部分已包含在ESP32核心库中或作为标准库提供)。
- 打开项目:
- 使用IDE打开
esp32_robot_firmware/esp32_robot_firmware.ino。IDE通常会自动加载文件夹内所有.h和.cpp文件。
- 使用IDE打开
- 配置
config.h:- 非常重要: 打开
config.h文件。 - 填写您的
WIFI_SSID和WIFI_PASSWORD。 - 配置
MQTT_BROKER_HOST,MQTT_BROKER_PORT, 以及可选的MQTT_USERNAME,MQTT_PASSWORD。 - 设置
DEVICE_UID: 此ID必须与您在后端系统中为该机器人注册的ID完全一致。 - 核对并修改所有硬件引脚定义 (
MOTOR_*_PIN,SERVO_PIN,ULTRASONIC_*_PIN,TCRT5000_*_PIN,RELAY_PIN) 以匹配您的实际硬件连接。 - 调整其他业务参数如
SIDE_ULTRASONIC_SPOT_THRESHOLD_CM,NUM_CHARGING_SPOTS等。
- 非常重要: 打开
- 编译与烧录:
- 选择正确的ESP32开发板型号和端口。
- 编译并上传固件到您的ESP32设备。
- 监控与调试:
- 打开串口监视器 (波特率在
config.h中SERIAL_BAUD_RATE定义,默认为 115200)。 - 观察串口输出,检查WiFi连接、MQTT连接状态、传感器读数、指令接收和状态上报等。
- 参考
esp32_robot_firmware/NEXT_STEPS.md文件进行详细的模块化测试和导航逻辑调试。
- 打开串口监视器 (波特率在
重要提示: navigation.cpp 中的循迹逻辑 (navigation_follow_line_once) 是一个非常基础的实现,您极有可能需要根据您的具体机器人平台、传感器和环境对其进行大幅修改和参数调优,甚至采用更高级的控制算法(如PID)才能获得满意的循迹效果。 NEXT_STEPS.md 提供了更详细的指导。
7. 后续工作与展望
- 完善各模块的单元测试和集成测试。
- 进一步优化 API 文档和错误处理机制。
- 根据实际运营需求,增加更细致的统计报表功能。
- MQTT通信日志模块:
- (可选) 进一步优化Payload的展示,例如对JSON格式的Payload进行美化或提供折叠/展开功能。
- (可选) 根据实际需求,考虑日志数据的定期归档或清理策略的实现。
- 考虑引入更高级的 MQTT 特性,如QoS、遗嘱消息等。
- 前端 UI/UX 持续打磨,提升用户体验。