AI编程实现ESP32 + MPU6050 风扇振动监控,硬件部分的几个小问题
问题的引出
最近有问到怎么用C#来写modbus协议。我提炼了一部分代码,写了一个完整的测试,能简单的模拟解modbus解析过程。但这只解决了解析的问题,如何能把整个链路跑通起来?让硬件,软件,网络,采集服务器都参与进来?
我想到用 AI 辅助做一个模拟工业风扇振动监控系统,实现数据采集。先通过网页,用webapi的形式把数据上传到服务器,实现数据显示,监控报警,转速控制。保证数据没问题之后,再实现modubs地址映射,用组态和C#来进行通讯来验证。
效果预览

网页监控数据

组态软件采集数据

非常简陋的硬件
项目背景
目标:做一个风扇振动监控系统,实时采集振动数据,上传到服务器,做异常检测。
硬件选型:
- ESP32 开发板(WiFi + 足够算力)
- MPU6050 加速度传感器(I2C 接口,便宜好用)
- PC 散热风扇(被测对象,12v供电,5v也能用)
- 面包板 + 杜邦线 +开发板电源模块
软件架构:
ESP32(采集) → HTTP POST → Flask 服务器 → SQLite 存储
↓
Web 看板(Chart.js)
看起来很简单对吧,结果有几个问题没注意到,还真被卡了几下。
I2C 初始化失败
第一版代码
#include <Wire.h>
#include <Adafruit_MPU6050.h>
Adafruit_MPU6050 mpu;
#define SDA_PIN 21
#define SCL_PIN 22
void setup() {
Serial.begin(115200);
Wire.begin(SDA_PIN, SCL_PIN);
if (!mpu.begin()) {
Serial.println("MPU6050 初始化失败!");
while (1) delay(10); // 卡死在这里
}
}
预期:串口打印”MPU6050 初始化成功” 实际:串口打印”MPU6050 初始化失败!”,然后 ESP32 的指示灯都不亮了
排查过程(踩坑记录)
问题1:Adafruit 库的”坑”
AI 告诉我,Adafruit_MPU6050 库的 begin() 函数内部会重新调用 Wire.begin()(不带参数),这会覆盖掉你之前设置的自定义引脚!
正确写法:
// 把已经配置好的 Wire 对象传给 mpu.begin()
if (!mpu.begin(MPU6050_I2CADDR_DEFAULT, &Wire)) {
Serial.println("初始化失败");
}
改完之后,还是失败了,把情况给AI说,让它去解决。
问题2:I2C 地址问题
MPU6050 的地址由 AD0 引脚决定:
- AD0 接地 → 地址 0x68
- AD0 接 3.3V → 地址 0x69
我的模块 AD0 引脚悬空,地址不确定。
解决:把 AD0 明确接到 GND,强制地址为 0x68。
改完之后,又失败了,这时候,我有点怀疑了,是不是库的版本和硬件不匹配呢?
问题3:上拉电阻问题
I2C 总线的 SDA 和 SCL 是开漏输出,要接上拉电阻到 3.3V 才能正常工作。
虽然 GY-521 模块理论上板载有上拉电阻,但有太便宜的,会省略掉。
验证方法:用万用表量 SDA/SCL 到 3.3V 的电阻,应该在 2kΩ~10kΩ。
最终解决:绕过库函数,直接读寄存器
折腾了一阵儿,最后用直接读取 WHO_AM_I 寄存器的方式确认硬件是否在线:
#include <Wire.h>
#define SDA_PIN 21
#define SCL_PIN 22
#define MPU_ADDR 0x68
void setup() {
Serial.begin(115200);
Wire.begin(SDA_PIN, SCL_PIN);
Wire.setClock(100000);
// 读取 WHO_AM_I 寄存器(地址 0x75)
Wire.beginTransmission(MPU_ADDR);
Wire.write(0x75);
Wire.endTransmission(false);
Wire.requestFrom(MPU_ADDR, 1);
byte whoami = Wire.read();
Serial.print("WHO_AM_I: 0x");
Serial.println(whoami, HEX); // 应该返回 0x68
}
如果返回 0x68,说明硬件在线,问题出在库函数。 如果返回 0xFF 或 0x00,说明 I2C 总线物理层不通。结果这里返回是正常,证明设备是上线了。
那就是mpu.begin() 在 ESP32 上可能和 Wire 对象的配合有问题,或者库版本有 bug。既然裸 I2C 能通信,那就绕过库的手动初始化,直接用 Wire 配置 MPU6050。
最终测试代码
#include <Wire.h>
#define SDA_PIN 21
#define SCL_PIN 22
#define MPU_ADDR 0x68
// MPU6050 寄存器地址
#define PWR_MGMT_1 0x6B
#define ACCEL_XOUT_H 0x3B
#define WHO_AM_I 0x75
void setup() {
Serial.begin(115200);
// 初始化 I2C
Wire.begin(SDA_PIN, SCL_PIN);
Wire.setClock(100000);
// 唤醒 MPU6050(默认睡眠模式)
Wire.beginTransmission(MPU_ADDR);
Wire.write(PWR_MGMT_1);
Wire.write(0x00);
Wire.endTransmission();
delay(100);
// 验证 WHO_AM_I
Wire.beginTransmission(MPU_ADDR);
Wire.write(WHO_AM_I);
Wire.endTransmission(false);
Wire.requestFrom(MPU_ADDR, 1);
byte whoami = Wire.read();
if (whoami == 0x68) {
Serial.println("MPU6050 初始化成功!");
} else {
Serial.print("WHO_AM_I 错误: 0x");
Serial.println(whoami, HEX);
}
}
void loop() {
// 读取加速度数据
Wire.beginTransmission(MPU_ADDR);
Wire.write(ACCEL_XOUT_H);
Wire.endTransmission(false);
Wire.requestFrom(MPU_ADDR, 6);
int16_t ax = (Wire.read() << 8) | Wire.read();
int16_t ay = (Wire.read() << 8) | Wire.read();
int16_t az = (Wire.read() << 8) | Wire.read();
// 转换为 g(量程 ±8g,16位有符号)
float ax_g = ax / 4096.0;
float ay_g = ay / 4096.0;
float az_g = az / 4096.0;
Serial.printf("X:%.3f Y:%.3f Z:%.3f\n", ax_g, ay_g, az_g);
delay(100);
}
这个代码运行没问题,硬件的事就算解决了。
经验总结
| 问题 | 原因 | 解决方法 |
|---|---|---|
| I2C 初始化失败 | Adafruit 库覆盖自定义引脚 | 传入 Wire 对象指针 |
| 地址不对 | AD0 引脚悬空 | 明确接 GND 或 3.3V |
| 总线不通 | 缺少上拉电阻 | 外接 4.7kΩ 上拉 |
| 库函数不工作 | 库版本兼容性问题 | 直接读寄存器 |
教训:
- 不要完全依赖库函数,理解底层原理很重要
- I2C 扫描和 WHO_AM_I 是最基础的诊断手段
- 上拉电阻是 I2C 最常见的坑
当然,这跟我不是专门搞硬件有关系,只是略懂一些。但并不重要,重要的是借助AI和自己的分析,可以快速解决这些问题。
下一步预告
硬件搞定之后,接下来要实现:
- WiFi 连接 + HTTP 数据上报
- Flask 服务器 + Web 看板
- 风扇 PWM 控制 + 转速采集
- 基线标定 + 异常检测
- Modbus TCP 集成(对接组态软件)
敬请期待后续文章!
更多推荐



所有评论(0)