零知派ESP32--基于BMP388制作运动追踪器
项目概述
ESP32-BMP388 运动追踪器是一款基于气压高度测量的便携式运动监测设备,集成卡尔曼滤波、实时折线图、指针式高度表、WiFi Web 仪表盘与历史数据持久化。设备通过 BMP388 气压传感器实时采集大气压强,利用 ISA(国际标准大气)公式将气压转换为海拔,再经 2D 卡尔曼滤波器平滑后得到稳定的高度与垂直速度读数。用户可通过两个物理按键在彩色 TFT 屏上切换 5 个功能界面,记录运动过程并查看历史数据;同时设备开放 WiFi 热点,手机连接后即可访问 Web 仪表盘进行实时监控、远程控制与历史数据导出。
- 主控:零知ESP32
- 传感器:BMP388 数字气压传感器
- 显示屏:ST7789 TFT,240×240,SPI,6 引脚
- 交互:2 个轻触按键(GPIO27 / GPIO14,内部上拉)
- 无线:ESP32 内置 WiFi,开放 AP 热点供手机接入
- 存储:RTC 内存(深度睡眠保留) + SPIFFS 文件系统双备份
- 2D 卡尔曼传感器融合 — 同时估计海拔与垂直速度,参数可调(Q/R),显著抑制 BMP388 单次测量抖动(±0.5m → 平滑 ±0.05m)。
- 5 屏彩色 UI — 实时数据屏(带海拔+气压双曲线滚动图)、指针式高度表(半圆仪表盘 + 红色指针)、运动总结屏、设置屏、历史柱状图屏,全部基于 TFT_eSPI Sprite 帧缓冲防闪烁绘制。
- 指针式高度表 — 模拟飞机高度表的半圆弧 + 刻度 + 指针可视化,量程动态跟随当前海拔 ±50m。
- WiFi Web 仪表盘 — 深色渐变 UI,英雄卡 + 三色小卡布局,支持手机端实时监控、远程开始/停止/重置、一键 CSV 导出,刷新带闪光动画。
- 数据持久化 — 运动会话先写入
RTC_DATA_ATTR深度睡眠保留内存,再镜像到 SPIFFS/sessions.dat,重启不丢,最多保存 20 条,循环淘汰。 - 平滑数字滚动动画 — 海拔大字每帧 25% 趋近目标值,避免数字跳变;实时折线图采用 80 点环形缓冲,自动归一化双轴显示。
- 状态栏信息集成 — 顶部统一显示模式名 + 温度(青色)+ 电池图标(绿/黄/红三色)+ 录制红点。
- 可配置性 — 全部参数(引脚、滤波系数、按键时序、刷新率、WiFi 凭据)集中在
config.h,支持_IIR_FILTER、_OSR、CALMAN_Q/R调参。
问题:单次气压测量噪声导致海拔读数波动 ±0.5m,数字频繁跳动影响可读性。
解决:实现 2D 卡尔曼滤波器,状态向量 [altitude, vertical_speed],通过过程噪声 Q 与测量噪声 R 协调平滑度与响应速度;再对卡尔曼输出的速度做一阶低通(α=0.3)进一步抑制。
难点 2:16 位色 Sprite 占用 115KB,超过 ESP32 可分配堆
问题:TFT_eSPI 16 位色 Sprite 240×240 需 115KB,ESP32 单次分配失败,导致白屏。
解决:改用 8 位色深(setColorDepth(8)),占用降至 ~57KB,分配成功;flush() 检查 _sprite.created() 防御性推送。
目录
一、硬件系统部分
1.1 硬件清单
| 名称 | 型号 | 数量 | 用途 |
|---|---|---|---|
| 主控板 | 零知ESP32开发板 | 1 | MCU + WiFi |
| 扩展板 | 零知ESP32扩展板 | 1 | 方便接线 |
| 气压传感器 | BMP388 模块 | 1 | 气压/温度采集 |
| 彩色显示屏 | ST7789 TFT 240×240 | 1 | UI 显示 |
| 杜邦线 | 端子对杜邦线母头 | 若干 | 连接 |
| USB Type-C 线 | Type-C | 1 | 烧录/供电 |
可选:锂电池 + 充电模块(用于电量显示演示),否则电池图标静态。
1.2 接线方案
| BMP388 | ESP32 GPIO | 说明 |
|---|---|---|
| VCC | 3V3 | 3.3V 供电 |
| GND | GND | 共地 |
| SDA | 21 | I²C 数据 |
| SCL | 22 | I²C 时钟 |
| SDO | GND | 设定 I²C 地址 0x76(接 VCC 则为 0x77) |
1.3 硬件连接图

1.4 实物连接图

二、软件架构设计
2.1 系统初始化
setup() 执行顺序(位于 MotionTracker.ino 末尾):
void setup() {
disableBrownout(); // 1. 禁用欠压复位(避免 WiFi 启动时重启)
Serial.begin(115200); // 2. 串口初始化, 波特率 115200
display.begin(); // 3. ST7789 + Sprite 帧缓冲初始化
display.showSplash(); // 显示启动动画 (进度条)
bmp388.begin(); // 4. BMP388 I²C + 过采样 + IIR 配置
altTracker.begin(); // 5. 卡尔曼滤波器复位
ui.begin(); // 6. 两按键 GPIO 初始化 + 消抖
logger.begin(); // 7. RTC 内存恢复 + SPIFFS 加载历史
wifi.begin(); // 8. 开放 AP 热点 "MotionTracker" + 启动 WebServer
// 9. 预热 20 次采样建立基准海拔
for(int i=0;i<20;i++){ bmp388.readData(p,t); altTracker.update(p,t,0.1f); delay(50); }
}
关键初始化参数:
- BMP388:温度/压力过采样 8X、IIR 滤波系数 3、ODR 50Hz;
- Sprite:8 位色深 240×240 ≈ 57KB;
- WiFi AP:SSID=
MotionTracker密码=12345678,IP 默认192.168.4.1。
2.2 主循环逻辑
loop() 采用 10Hz 主节拍 + 5Hz 显示节拍 + 每帧 WiFi 处理:
loop() 每帧 (~5ms 间隔):
├─ 1. 按键处理
│ ├─ button1: 检测短/长/超长按 → 调用 handleEvent(1)
│ ├─ button2: 检测短/长按 → 调用 handleEvent(2) 切屏
│ └─ WiFi 开关: 检测设置项变化 → wifi.setEnabled()
│
├─ 2. 传感器更新 (10Hz, 每 100ms):
│ ├─ bmp388.readData(&p, &t)
│ ├─ altTracker.update(p, t, dt) # 卡尔曼滤波
│ ├─ display.addChartSample(altitude) # 折线图采样
│ └─ display.addPressureSample(p/100) # 气压曲线采样
│
├─ 3. 海拔平滑动画 (每帧):
│ └─ display.tickAnimation(targetAlt) # 25%/帧 趋近
│
├─ 4. 显示刷新 (5Hz, 每 200ms):
│ └─ updateDisplay() # 根据当前屏绘制
│
├─ 5. WiFi (每帧, ~100Hz):
│ ├─ wifi.updateData(...) # 推送最新数据
│ ├─ wifi.notify() # 处理 HTTP 客户端
│ └─ if(wifi.hasCommand()) handleWiFiCommand(...)
│
└─ delay(5)
设计要点:
- 传感器 10Hz、显示 5Hz:传感器更新更快以保证滤波器收敛,显示降速避免画面过快刷新;
- WiFi 每帧 notify:HTTP 请求近乎即时响应,不受显示或传感器节拍影响;
- 每帧
tickAnimation:让海拔数字平滑变化,与 5Hz 显示刷新独立,动画更连续。
三、代码拆分讲解
MotionTracker.ino 单文件结构(从上到下):
3.1 系统库 + 配置
#include <Adafruit_BMP3XX.h>
#define GFXFF 1
#include <TFT_eSPI.h> // FreeFont 支持
#include <WiFi.h>
#include <FS.h>
using namespace fs; // 解决 Lingzhi core 命名空间冲突
#include <WebServer.h>
#include "config.h" // 用户可编辑参数
3.2 SessionData 数据结构
struct SessionData {
float totalAscent, totalDescent, maxAltitude, minAltitude;
float avgSpeed, startAltitude;
uint32_t duration, floorCount, sampleCount, timestamp;
bool valid;
};
贯穿日志记录、历史柱状图、CSV 导出。
3.3 KalmanFilter2D
2 状态卡尔曼滤波器,状态向量 [海拔, 垂直速度]:
class KalmanFilter2D {
void update(float z_alt, float dt); // 预测 + 修正
float getAltitude() const;
float getVelocity() const;
void reset(float alt=0);
private:
float _x[2], _covar[2][2], _Q[2], _R;
};
update() 先用速度做时间预测(x0 += dt·v),再读气压做修正:
_x[0] = x0 + K0 * y; // 海拔更新
_x[1] = x1 + K1 * y; // 速度更新
Q/R 越大平滑越强但响应越慢,默认 Q=[0.1, 0.5],R=0.25。
3.4 AltitudeTracker
封装滤波后的高度追踪,含会话管理:
- 气压→海拔:ISA 公式
44330·(1−(P/P0)^0.190295) - 会话生命周期:
startSession()记录起始海拔与时间;update()累计爬升/下降/楼层;stopSession()计算总时长 - 楼层计数:累计正位移达
_floorCount × FLOOR_HEIGHT即 +1 - 平均速度 = (爬升 + 下降) / 时长
3.5 BMP388Driver
封装 Adafruit_BMP3XX:
bool begin(); // I²C 0x76 + 过采样 8X + IIR 3 + ODR 50Hz
bool readData(float &p, float &t); // 一次气压+温度读取
读取失败自动打印警告但不阻塞。
3.6 DisplayManager
所有 TFT 绘制,使用 TFT_eSPI + TFT_eSprite 帧缓冲:
class DisplayManager {
TFT_eSPI _tft;
TFT_eSprite _sprite; // 帧缓冲
float _chartBuf[80]; // 海拔环形缓冲
float _presBuf[80]; // 气压环形缓冲
public:
void drawRealtimeScreen(...); // 实时屏 - 含双曲线
void drawGaugeScreen(...); // 指针式高度表屏
void drawSummaryScreen(...); // 运动总结屏
void drawSettingsScreen(...); // 设置屏 - Clear History 两行
void drawHistoryScreen(...); // 历史屏 - 柱状图
void addChartSample(float); // 采样
void addPressureSample(float);
void tickAnimation(float); // 25%/帧平滑
};
关键辅助:_drawStatusBar() 状态栏(温度/电池/录制点)、_drawGauge() 半圆弧+指针绘制、_drawLineChart() 双曲线自动缩放。
四、操作过程及数据展示
4.1 操作步骤
选择开发板 “零知ESP32”,上传 MotionTracker.ino。
- 屏幕显示 进度条启动动画 约 2 秒;
- 串口(115200)输出
[BMP388] OK、[Display] Sprite OK、[WiFi] IP: 192.168.4.1; - 自动预热采样 20 次,串口输出
基准海拔: xxx.xx m。
| 按键 | 短按 (<800ms) | 长按 (800~3000ms) | 超长按 (≥3000ms) |
|---|---|---|---|
| KEY1 (GPIO27) | 实时屏:开始/停止记录 设置屏:调整当前项 |
重置会话(实时屏) | 同长按(重置) |
| KEY2 (GPIO14) | 切到下一屏 | 切到上一屏 | — |
按键 2 短按循环切换 5 个屏幕:
REALTIME → GAUGE → SUMMARY → SETTINGS → HISTORY → REALTIME
- REALTIME 实时屏:大字海拔 + 方向箭头(↑红/↓蓝/→灰) + 速度/爬升/下降数据行 + 海拔(深蓝)+气压(橙)双曲线滚动图 + 底部提示
- GAUGE 高度表屏:半圆弧 + 5 刻度(动态 ±50m 量程) + 红指针 + 数字读数 + 底部气压/速度
- SUMMARY 总结屏:本次会话的爬升/下降/最高/平均速度/楼层/时长
- SETTINGS 设置屏:4 项可调:海平面气压(±0.5 hPa) / 楼层高度(±0.1m) / WiFi 开关 / 清除历史(“Clear History” 标签 + “[Execute]” 分两行显示)
- HISTORY 历史屏:最近 5 次会话柱状图,按最大爬升归一缩放,柱顶标注数值,下方显示 #编号
- 在 REALTIME 屏 短按 KEY1 → 状态栏右侧出现红色脉冲圆点,记录开始;
- 屏幕实时滚动双曲线,爬升/下降数字逐步累加;
- 再次短按 KEY1 → 停止记录,会话自动保存到 RTC+SPIFFS。
切到 SUMMARY 屏即可查看本次会话的总爬升、总下降、最高海拔、平均速度、楼层数、时长。
切到 HISTORY 屏,柱状图对比最近 5 次会话的总爬升量,柱顶显示精确数值。
- 手机搜索并连接 WiFi:
- SSID:
MotionTracker - 密码:
12345678
- SSID:
- 浏览器打开
http://192.168.4.1; - 页面显示:
- 英雄卡:当前海拔(cm 单位,×100 换算)
- 3 个彩色顶条小卡:速度(m/s) / 爬升(cm) / 下降(cm)
- 3 按钮:开始(绿)/ 停止(红)/ 重置(灰)
- 页面 1 秒自动刷新,数值变化时触发闪光动画;
- 点击 “导出历史数据 (CSV)” → 下载
motiontracker.csv,含所有会话的 10 列数据。
4.2 演示视频
零知派ESP32-基于BMP388制作运动追踪器
五、技术原理
5.1 工作原理
BMP388 测得大气压 P 后,代入 ISA 公式:
altitude = 44330 × (1 − (P / P0)^0.190295)
其中 P0 = 海平面气压(默认 1013.25 hPa,可在设置屏调整 ±0.5 hPa/步)。原理是大气压随高度按指数衰减(等温层模型),系数 0.190295 = (R·L)/(g·M) 推导自气体状态方程。
状态向量 x = [altitude, velocity],每步:
- 预测:
altitude_predicted = altitude + dt·velocity(匀速模型) - 更新(读气压 z 后):
- 残差
y = z − altitude_predicted - 卡尔曼增益
K = P / (P + R)(P=估计协方差, R=测量噪声) altitude += K·y,velocity += K·velocity·y
- 残差
- 协方差更新:
P = (1−K)·P + Q(Q=过程噪声,注入不确定性)
效果:单次测量噪声 ±0.5m → 平滑后约 ±0.05m。速度由海拔变化反推,无需加速度计。
累计垂直正位移(在 smoothSpeed > MIN_SPEED_THRESHOLD 阈值时才计数,过滤噪声):
_floorAccumulator += (新海拔 − 旧海拔)
floorCount = floor(_floorAccumulator / FLOOR_HEIGHT)
默认 FLOOR_HEIGHT=3.0m,即每爬升 3m 一个楼层。
- RTC 内存 (
RTC_DATA_ATTR):ESP32 深度睡眠保留区域,断电不丢但复位会清; - SPIFFS:Flash 文件系统
/sessions.dat,永久保存,重启自动加载; - 双备份策略:运行时优先写 RTC(速度快),空闲时镜像到 SPIFFS。
5.2 工作模式配置
| 宏 | 默认值 | 作用 |
|---|---|---|
BMP_ODR |
50 | 输出数据率 Hz |
BMP_OSR |
8 | 过采样倍数 |
BMP_IIR_FILTER |
4 | IIR 滤波系数 |
BMP_I2C_ADDR |
0x76 | I²C 地址 |
| 宏 | 默认值 | 作用 |
|---|---|---|
KALMAN_Q_ALT |
0.1 | 海拔过程噪声(越大跟随越快,越抖) |
KALMAN_Q_VEL |
0.5 | 速度过程噪声 |
KALMAN_R_ALT |
0.25 | 测量噪声(越大越平滑,越滞后) |
| 宏 | 默认值 | 作用 |
|---|---|---|
MIN_SPEED_THRESHOLD |
0.1 | 最小有效速度 m/s(过滤静止时的轻微波动) |
FLOOR_HEIGHT |
3.0 | 楼层高度(用于楼层计数) |
SEA_LEVEL_PRESSURE |
1013.25 | 海平面基准气压 hPa |
| 宏 | 默认值 | 作用 |
|---|---|---|
UPDATE_RATE_HZ |
10 | 传感器采样率 |
DISPLAY_REFRESH_MS |
200 | 显示刷新间隔 |
BUTTON_DEBOUNCE_MS |
50 | 按键消抖 |
LONG_PRESS_MS |
800 | 长按判定 |
EXTRA_LONG_PRESS_MS |
3000 | 超长按判定 |
| 宏 | 默认值 | 作用 |
|---|---|---|
ENABLE_WIFI |
1 | 是否启用 WiFi(0=禁用) |
WIFI_AP_SSID |
“MotionTracker” | AP 名称 |
WIFI_AP_PASSWORD |
“12345678” | AP 密码(最少 8 位) |
- 海拔读数抖动大:增大
KALMAN_R_ALT(0.25→0.5)或减小KALMAN_Q_ALT; - 响应滞后明显:减小
KALMAN_R_ALT或增大KALMAN_Q_ALT; - 实际海拔偏差:在设置屏短按调整海平面气压到当地实际值(气象网站查询);
- 楼层计数不准:调整
FLOOR_HEIGHT匹配实际楼层高度。
六、常见问题指引
Q1:串口输出 [BMP388] 传感器未找到!
排查:
- 检查 SDA=21、SCL=22 接线;
- 用 I²C 扫描程序确认地址(默认 0x76,SDO 拉高时 0x77);
- 在
config.h修改BMP_I2C_ADDR为实际地址; - 确认 3V3 供电正常,I²C 上拉电阻存在(主控板通常已集成)。
Q2:WiFi 热点连不上或打不开192.168.4.1
排查:
- 串口查看
[WiFi] 热点: MotionTracker是否输出; - 密码必须 ≥ 8 位(WPA2 要求),当前
12345678满足; - 确认
config.h中ENABLE_WIFI=1; - 2.4G WiFi 频段,5G 手机需切换到兼容模式。
- 确认手机已获取 IP(应为 192.168.4.x);
- 部分手机会检测到无网络后切回移动数据,需关闭移动数据再访问;
- 浏览器试试
http://192.168.4.1(必须带 http:// 前缀)。
Q3:重启后历史数据丢失
原因:
- 冷启动(断电)会清空 RTC 内存;
- SPIFFS 应保留。
排查:
- 串口查看
[Logger] SPIFFS加载 N 条; - 若显示 0 条:检查
SPIFFS.begin(true)是否成功(可能需先格式化); - 可在设置屏短按 “Clear History” → “[Execute]” 主动清除后再测试。
Q4:海拔数字明显偏高/偏低
原因:未校准海平面气压。
解决:
- 查询当地实时海平面气压(气象网站/天气 App);
- 进入 设置屏 → 短按切换到 Sea Level 项 → 短按调整 ±0.5 hPa;
- 长度按确认后实时屏读数会立即更新。
更多推荐



所有评论(0)