在开始动手之前,我们需要明确项目的核心需求和功能模块。这个智能充电桩需要实现刷卡启动、电量计量、安全保护和远程监控四大核心功能。经过多次方案对比和成本考量,我最终选择了以下硬件组合:
核心控制器:STM32F103C8T6(蓝色pill开发板)
无线通信模块:ESP8266-01S
RFID读卡器:RC522模块
电力监测方案对比:
| 传感器类型 | 型号 | 精度 | 接口 | 成本 | 适用场景 |
|---|---|---|---|---|---|
| 霍尔传感器 | ACS712 | ±1% | 模拟量 | 中 | 中等精度直流测量 |
| 电流芯片 | INA226 | ±0.1% | I2C | 高 | 高精度电能计量 |
| 分流电阻 | 75mV/50A | ±2% | 模拟量 | 低 | 大电流低成本方案 |
考虑到DIY项目的成本因素,我选择了ACS712+分压电阻的方案,虽然精度稍逊于INA226,但完全能满足一般充电桩的需求。
充电桩的电源系统需要为各模块提供稳定工作电压:
code复制[220V AC] → [开关电源] → [12V DC] → [LM2596] → 5V → [AMS1117] → 3.3V
↓
[继电器控制端]
关键元件选型:
注意:强电部分务必做好绝缘处理,建议使用热缩管和端子排,避免裸露导线。
STM32与各模块的连接方式如下:
c复制// RC522 (SPI1)
#define RFID_CS_PIN PA4
#define RFID_RST_PIN PA3
#define RFID_SCK_PIN PA5
#define RFID_MISO_PIN PA6
#define RFID_MOSI_PIN PA7
// ESP8266 (USART2)
#define ESP_TX_PIN PA2
#define ESP_RX_PIN PA3
// ACS712 (ADC1)
#define VOLTAGE_ADC PA0
#define CURRENT_ADC PA1
// 继电器控制
#define RELAY_PIN PC13
实际焊接时,我推荐使用杜邦线先搭建原型,验证无误后再用焊锡固定。特别要注意的是:
我选择的是STM32CubeIDE + PlatformIO的组合,兼具图形化配置和命令行灵活性:
ini复制lib_deps =
miguelbalboa/MFRC522@^1.4.10
bblanchon/ArduinoJson@^6.19.4
系统软件架构采用多任务设计,各任务优先级如下:
| 任务名称 | 优先级 | 堆栈大小 | 功能描述 |
|---|---|---|---|
| SafetyMonitor | 4 | 256 | 实时监测电压电流温度 |
| RFIDHandler | 3 | 512 | 处理刷卡事件和计费逻辑 |
| NetworkManager | 2 | 1024 | MQTT通信和云端数据同步 |
| UIDisplay | 1 | 384 | 刷新OLED界面 |
任务间通信采用FreeRTOS的队列机制:
c复制// 定义事件消息结构体
typedef struct {
uint8_t event_type;
union {
float sensor_value;
uint8_t card_uid[10];
char mqtt_msg[64];
} data;
} EventMessage_t;
// 创建全局事件队列
QueueHandle_t xEventQueue = xQueueCreate(10, sizeof(EventMessage_t));
电压电流的采集需要特别注意滤波处理:
c复制#define SAMPLE_COUNT 20
float read_filtered_voltage(void) {
static float buffer[SAMPLE_COUNT];
static uint8_t index = 0;
// 原始ADC读取
float raw = (float)adc_read(VOLTAGE_ADC) * 3.3 / 4096.0;
// 转换为实际电压(分压比11:1)
float voltage = raw * 11.0;
// 移动平均滤波
buffer[index] = voltage;
index = (index + 1) % SAMPLE_COUNT;
float sum = 0;
for(int i=0; i<SAMPLE_COUNT; i++) {
sum += buffer[i];
}
return sum / SAMPLE_COUNT;
}
void SafetyMonitor_Task(void *pvParameters) {
const TickType_t xFrequency = pdMS_TO_TICKS(100);
TickType_t xLastWakeTime = xTaskGetTickCount();
for(;;) {
float v = read_filtered_voltage();
float i = read_filtered_current();
float t = read_temperature();
if(v > OVER_VOLTAGE || i > OVER_CURRENT) {
HAL_GPIO_WritePin(RELAY_GPIO_Port, RELAY_Pin, GPIO_PIN_RESET);
EventMessage_t msg = { .event_type = EVENT_FAULT };
xQueueSend(xEventQueue, &msg, 0);
}
vTaskDelayUntil(&xLastWakeTime, xFrequency);
}
}
刷卡逻辑需要考虑防重入和异常处理:
c复制void RFID_Task(void *pvParameters) {
MFRC522 mfrc;
MFRC522_Init(&mfrc, hspi1, RFID_CS_PIN, RFID_RST_PIN);
for(;;) {
if(MFRC522_Check(&mfrc)) {
uint8_t uid[10];
MFRC522_GetUID(&mfrc, uid);
EventMessage_t msg;
msg.event_type = EVENT_CARD;
memcpy(msg.data.card_uid, uid, 10);
if(xQueueSend(xEventQueue, &msg, 100) != pdPASS) {
printf("RFID: Queue full!\n");
}
// 防抖延时
vTaskDelay(pdMS_TO_TICKS(500));
}
vTaskDelay(pdMS_TO_TICKS(100));
}
}
计费算法支持时间和电量两种模式:
c复制typedef enum {
MODE_TIME, // 按时计费
MODE_ENERGY // 按电量计费
} ChargeMode;
typedef struct {
uint8_t uid[10];
float balance;
time_t start_time;
float start_energy;
ChargeMode mode;
} UserCard;
float calculate_charge(UserCard *card) {
float amount = 0;
time_t now = time(NULL);
if(card->mode == MODE_TIME) {
float hours = (now - card->start_time) / 3600.0;
amount = hours * TIME_RATE;
} else {
float energy = get_current_energy() - card->start_energy;
amount = energy * ENERGY_RATE;
}
return amount;
}
MQTT通信采用异步处理方式:
c复制void mqtt_callback(char* topic, byte* payload, unsigned int length) {
char msg[length+1];
memcpy(msg, payload, length);
msg[length] = '\0';
if(strcmp(topic, "charge/cmd") == 0) {
if(strstr(msg, "stop")) {
emergency_stop();
}
}
}
void Network_Task(void *pvParameters) {
WiFiClient espClient;
PubSubClient client(espClient);
client.setServer(MQTT_SERVER, 1883);
client.setCallback(mqtt_callback);
for(;;) {
if(!client.connected()) {
reconnect_mqtt(&client);
}
client.loop();
// 每5秒上报数据
static uint32_t last_report = 0;
if(HAL_GetTick() - last_report > 5000) {
report_status(&client);
last_report = HAL_GetTick();
}
vTaskDelay(pdMS_TO_TICKS(100));
}
}
数据上报采用JSON格式:
json复制{
"voltage": 54.2,
"current": 8.5,
"power": 460,
"energy": 1.2,
"status": "charging",
"card": "A1B2C3D4"
}
在开发过程中,我遇到了几个典型问题及解决方案:
问题1:ESP8266频繁断连
c复制void check_wifi_strength() {
int8_t rssi = WiFi.RSSI();
if(rssi < -80) {
WiFi.reconnect();
}
}
问题2:RFID读卡不稳定
c复制hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_16;
问题3:计量误差偏大
c复制void calibrate_current_sensor() {
float sum = 0;
for(int i=0; i<100; i++) {
sum += adc_read(CURRENT_ADC);
HAL_Delay(10);
}
zero_offset = sum / 100;
}
为了让项目更接近产品形态,我使用3D打印制作了防护外壳:
结构设计要点:
材料选择对比:
| 材料类型 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| PLA | 易打印 | 耐温性差 | 室内原型 |
| ABS | 强度高 | 需要加热床 | 一般户外 |
| PETG | 耐候性好 | 价格较高 | 严苛环境 |
最终我选择了PETG材料,虽然打印温度需要控制在230-250℃,但长期户外使用不易变形开裂。
基础版本完成后,可以考虑以下增强功能:
手机蓝牙控制:
能量回收显示:
c复制void calculate_energy_saving() {
float total = get_total_energy();
float green = get_solar_energy();
printf("绿色能源占比: %.1f%%\n", green/total*100);
}
语音提示功能:
离线支付系统:
在完成所有功能测试后,建议进行72小时连续老化测试,重点关注: