零知派ESP32--基于HMC5883L磁力计、MPU6050六轴传感器制作电子指南针与水平仪设备
项目概述
本教程将指导你使用零知派ESP32、HMC5883L磁力计、MPU6050六轴传感器和ST7789 TFT屏幕,制作一款功能完善的电子指南针与水平仪设备。设备具备以下功能:
-
指南针模式:显示真北方位角(0~360°),带旋转罗盘玫瑰图,支持硬铁校准、磁偏角修正和用户一键归零。
-
水平仪模式:实时显示设备横滚角(Roll)和俯仰角(Pitch),以气泡水平仪的形式直观展示倾斜状态。
-
WiFi定位:首次进入指南针时自动获取当前位置(经纬度、海拔),并显示在屏幕底部。
-
按键交互:三个独立按键分别控制功能切换和校准操作。
项目采用模块化设计,包含传感器自动检测、非易失存储校准数据、界面局部刷新等优化,整体运行流畅,资源占用合理。
项目亮点
-
完整的硬铁校准机制
通过绕∞字形旋转10秒,自动计算磁力计各轴的偏移量并存入NVS,断电不丢失,确保指南针在任何环境下都能准确指向。 -
多种方向修正
-
磁偏角修正(基于地理位置,深圳地区约-2.7°)
-
用户归零偏移(一键将任意方向设为0°)
-
安装方向修正(通过
atan2(-y, x)适应传感器在PCB上的实际朝向)
-
-
智能化的界面刷新
指南针和水平仪均采用局部擦除重绘策略,仅更新动态变化的区域(数值、罗盘、气泡),大幅降低CPU负载并提高帧率。 -
防卡死与容错设计
-
所有I2C操作前检测设备在线状态
-
HTTP请求设置5秒超时
-
连续50次I2C读取失败(约5秒)自动退回主菜单
-
校准过程支持S3按键跳过
-
-
一阶低通滤波
水平仪气泡运动经过软件平滑,消除传感器噪声带来的抖动,显示更稳定。
项目难点及解决方案
| 难点 | 解决方案 |
|---|---|
| 磁力计受PCB固定磁场干扰,读数偏移 | 实现硬铁校准算法:旋转设备采集全周数据,计算各轴(max+min)/2作为偏移量,实时减去该值。 |
| 真北与磁北存在夹角(磁偏角) | 根据地理位置查询磁偏角(如深圳-2.7°),在计算航向后叠加修正值。 |
| 水平仪零点漂移 | 进入水平仪模式时采集20次角度平均值作为零偏,后续读数减去该偏移。 |
| 旋转罗盘时文字倒置 | 采用动态角度计算,每个方向字母独立旋转,始终正向显示(仅旋转位置,不旋转文字角度)。 |
| WiFi定位API可能失败或超时 | 设置5秒超时,失败后仅显示“No GPS module”,不影响指南针核心功能。 |
| 校准过程中用户想取消 | 检测S3按键按下,立即退出校准循环(保留已有数据)。 |
一、硬件系统部分
1.1 硬件清单
| 器件名称 | 型号/规格 | 数量 | 备注 |
|---|---|---|---|
| 主控板 | 零知派ESP32 | 1 | 烧录代码 |
| 扩展板 | 零知派ESP32扩展板 | 1 |
便于安装连接 |
| 磁力计 | HMC5883L模块 | 1 | I2C接口,地址0x1E |
| 六轴传感器 | MPU6050模块 | 1 | I2C接口,地址0x68 |
| TFT屏幕 | ST7789(240×240) | 1 | 代码可视化 |
| 杜邦线 | 公对公 | 若干 | 连接模块 |
| 面包板 | 通用 | 1 | 安装磁力计和六轴传感器 |
1.2 接线方案
| ESP32引脚 | HMC5883L和MPU6050 | 说明 |
|---|---|---|
| 3.3V | VCC | 统一供电 |
| GND | GND | 共地 |
| GPIO21 (SDA) | SDA | I2C数据线 |
| GPIO22 (SCL) | SCL | I2C时钟线 |
1.3 硬件连接图

1.4 实物连接图

二、软件架构设计
2.1 系统初始化
setup()函数按顺序执行以下操作:
-
串口与I2C总线初始化
Serial.begin(115200)、Wire.begin() -
按键引脚配置
将GPIO12/14/27设为输入上拉模式:pinMode(..., INPUT_PULLUP) -
TFT屏幕初始化
tft.init()、tft.setRotation(3)设置显示方向 -
检测HMC5883L并显示状态
调用initCompass(),若成功则compassOK=true,否则false。在主菜单显示绿色“OK”或红色“N/A” -
检测MPU6050并显示状态
调用initMPU(),类似处理 -
绘制主菜单界面
画出系统状态栏、两个传感器状态框、三个功能按钮
初始化流程图:
开机 ──→ 初始化串口/I2C/按键/TFT ──→ 检测HMC5883L
│
┌───────┴───────┐
↓ ↓
成功 失败
│ │
↓ ↓
标记OK 标记N/A
│ │
└───────┬───────┘
↓
检测MPU6050 (同理)
↓
绘制主菜单界面
↓
进入主循环loop()
2.2 主循环逻辑
主循环采用非阻塞定时(每100ms执行一次界面更新),并实时检测按键状态。逻辑结构如下:
loop():
读取三个按键当前电平
if (S1下降沿 && 不在指南针模式 && compassOK) → enterCompass()
if (S2下降沿 && 不在水平仪模式 && mpuOK) → enterLevel()
if (S3下降沿) → 返回主菜单 (重置所有状态)
if (距离上次刷新时间 < 100ms) → 短暂延迟后返回
if (compassActive):
if (S1当前按下且之前未按下) → 记录当前航向为归零偏移
读取原始航向 → 减去归零偏移 → 归一化
if (读数与上次不同):
连续失败计数清零
if (变化 > 0.5°) → 调用drawCompass()更新显示
else:
连续失败计数++
if (连续失败 > 50) → 退出到主菜单
if (levelActive && mpuOK):
读取加速度计 → 计算roll/pitch
if (读取成功):
连续失败计数清零
低通滤波: smooth = 0.7*smooth + 0.3*new
if (变化 > 0.5°) → 调用drawLevelBubble()
else:
连续失败计数++ → 超限则退出
更新按键状态变量用于下次检测
短暂延迟10ms
这种结构保证了:
-
按键响应灵敏(下降沿触发)
-
界面流畅刷新
-
I2C异常时能自动退出,防止死锁
三、代码拆分讲解
3.1 磁力计初始化与硬铁校准
初始化(initCompass()):
尝试5次I2C通信,成功后设置量程为±1.3Ga(适合地磁场)、连续测量模式、输出速率15Hz、采样平均8次。
硬铁校准(calibrateCompass()):
-
显示倒计时进度条,提示用户旋转设备画“∞”字形10秒
-
记录各轴的最大最小值
-
计算偏移量:
offset = (max + min) / 2 -
将偏移量和校准标志存入Preferences(NVS)
Preferences prefs;
prefs.begin("compass", false);
prefs.putFloat("offX", magOffX);
prefs.putFloat("offY", magOffY);
prefs.putFloat("offZ", magOffZ);
prefs.putBool("cal", true);
prefs.end();
获取航向(getHeading()):
-
读取原始磁场向量
-
减去硬铁偏移
-
计算夹角:
atan2(-y, x) -
根据
COMPASS_REVERSE决定是否反转 -
加上磁偏角
-
归一化到0~360°
3.2 加速度计读取与水平仪计算
MPU6050采用裸I2C读写(未使用现成库),从寄存器0x3B连续读取6字节得到AX、AY、AZ原始值。
roll = atan2(ay, az) * RAD_TO_DEG;
pitch = atan2(-ax, sqrt(ay*ay + az*az)) * RAD_TO_DEG;
水平仪进入时采集20次平均值作为零偏(rollOffset, pitchOffset),之后显示roll - rollOffset和pitch - pitchOffset。
气泡绘制算法:
将roll作为上下偏移,pitch作为左右偏移(经过旋转调整),气泡位置限制在圆形区域内。
3.3 指南针界面绘制
drawCompass() 只擦除y=20~203的区域(保留底部经纬度信息),然后绘制:
-
顶部大数字航向和方向缩写
-
调用
drawRotatingRose()绘制旋转罗盘
罗盘玫瑰图的核心:
-
每个刻度线的角度 = 固定方向角度 - 当前航向
-
8个方向字母也以相同角度偏移放置,但文字本身不旋转(保持正向)
-
红色指针始终指向正上方(代表北方)
3.4 WiFi定位
进入指南针模式时自动开启WiFi,连接指定SSID,通过HTTP GET请求免费API:
-
ip-api.com/json/?fields=lat,lon获取经纬度 -
api.open-elevation.com获取海拔(基于经纬度)
所有请求设置5秒超时,失败后继续但显示无效。获取完成后断开WiFi(WiFi.disconnect(true))以降低功耗。
3.5 按键防抖动与边缘检测
代码中未使用delay去抖,而是通过记录上一次状态,检测下降沿(btn == LOW && lastBtn == HIGH)来触发动作。主循环每100ms采样一次,已经天然滤除短时间抖动。若需要更严格去抖,可增加20ms延迟或状态机。
四、操作过程及数据展示
4.1 操作步骤
-
上电启动
屏幕显示“System Status”主菜单,自动检测传感器。HMC5883L和MPU6050状态分别显示OK或N/A。 -
使用指南针
-
按 S1 进入指南针模式。
-
首次进入会连接WiFi获取经纬度/海拔(约2~5秒),然后检查是否已有校准数据。若未校准,会提示“Rotate device in figure-8”,此时手持设备绕“∞”字形旋转10秒(可随时按S3跳过)。
-
校准完成后,屏幕显示当前航向角度(0~360°)、方向缩写(N/NE/E等)、旋转罗盘以及底部经纬度/海拔。
-
归零操作:在指南针模式下,按S1可将当前方向设为0°(例如将设备对准某个标记点,按S1,此后航向显示为该方向相对角度)。
-
按 S3 返回主菜单。
-
-
使用水平仪
-
将设备水平放置在桌面上,按 S2 进入水平仪模式。
-
设备会自动采集20次角度平均值作为零偏(请在此期间保持静止)。
-
之后屏幕上显示圆形气泡水平仪,以及当前的Roll/Pitch数值。
-
倾斜设备,气泡会向高侧移动,中心红色点为水平基准。
-
按 S3 返回主菜单。
-
-
查看传感器状态
主菜单实时显示两个模块是否正常,若某模块未连接或I2C故障,对应按钮(S1或S2)会变灰并不可用。
4.2 数据展示示例
菜单界面

指南针界面

水平仪界面

演示视频
零知派ESP32--电子指南针与水平仪设备
五、技术原理
5.1 工作原理
指南针原理
地球磁场近似于一个磁偶极子,地磁场矢量在水平面上的投影指向磁北。HMC5883L通过测量X、Y、Z三个正交轴上的磁场强度,利用反正切函数计算出与磁北的夹角:
θ_mag = atan2(-Y, X) * (180/π)
由于地磁北极与地理北极存在磁偏角δ(深圳约为-2.7°),因此真北航向为:
θ_true = θ_mag + δ
设备自身PCB上的铁磁性物质会产生固定磁场(硬铁效应),导致测量曲线圆心偏移原点。通过全周旋转采集max/min,计算出偏移量并减去,即可消除该误差。
水平仪原理
MPU6050内部的加速度计检测重力加速度在三个轴上的分量。当设备水平时,Z轴感受1g,X、Y轴为0。倾斜时,重力分量会分配到各轴:
-
横滚角 Roll = atan2(Ay, Az)
-
俯仰角 Pitch = atan2(-Ax, sqrt(Ay²+Az²))
通过这两个角度即可知道设备相对于水平面的倾斜姿态。
5.2 工作模式配置
HMC5883L寄存器配置
| 寄存器地址 | 值 | 含义 |
|---|---|---|
| 0x00 (配置寄存器A) | 0x70 | 采样平均8次,输出速率15Hz |
| 0x01 (配置寄存器B) | 0x20 | 量程±1.3Ga(增益=0x20) |
| 0x02 (模式寄存器) | 0x00 | 连续测量模式 |
MPU6050寄存器配置
| 寄存器地址 | 值 | 含义 |
|---|---|---|
| 0x6B (电源管理1) | 0x00 | 退出睡眠,使用内部时钟 |
| 0x1C (加速度配置) | 0x00 | ±2g量程 |
显示刷新策略
指南针采用角度变化阈值0.5°才重绘,水平仪采用角度变化阈值0.5°且低通滤波后重绘,有效降低屏幕闪烁和CPU占用。
六、常见问题指引
Q1: 屏幕上指南针航向始终不变或跳动很大?
-
检查是否已完成硬铁校准(首次使用或周围磁场环境改变后需要校准)。按S1进入指南针时,如果未校准会自动提示旋转设备。
-
检查周围是否有强磁场(如金属桌面、音箱、电机),远离干扰源。
-
确认代码中的
COMPASS_REVERSE定义是否与传感器安装方向一致。若旋转设备时航向增减反向,修改该宏定义。
Q2: 水平仪气泡反应迟钝或反向运动?
-
检查MPU6050安装方向:代码中Roll和Pitch的计算基于X向前、Y向右、Z向上的坐标系。若模块旋转了90°或180°,需要调整计算公式。
-
进入水平仪时请确保设备绝对水平(最好用物理水平尺辅助)。零偏采集过程中不要移动。
-
若噪声过大,可适当增大低通滤波系数(例如0.85 * old + 0.15 * new)。
Q3: WiFi定位失败或获取不到数据?
-
确认WiFi SSID和密码正确,且路由器能上网。
-
ip-api.com和open-elevation为免费公共服务,可能受到访问频率限制,初次连接失败可稍后重试。
-
如果始终失败,可注释掉相关代码,指南针功能依然可用,只是不显示经纬度。
Q4: I2C通信失败(屏幕显示N/A)?
-
检查接线:SDA/SCL是否交叉接错,是否共地,电源电压是否为3.3V。
-
可扫描I2C地址:使用
Wire库的requestFrom示例确认0x1E和0x68是否存在。 -
有些MPU6050模块需要将AD0引脚接地(地址0x68)或接VCC(地址0x69),请核对。
Q5: 校准完成后航向依然偏差较大?
-
磁偏角是根据深圳地区设定的(-2.7°)。如果你在其他地区,请查询当地磁偏角并修改
MAG_DECLINATION宏。 -
硬铁校准只能修正固定磁场干扰,若附近有动态磁场(如电机运转),需在远离干扰源处校准。
Q6: 如何在水平仪模式下改变显示灵敏度?
-
修改
drawLevelBubble()中的scale变量(角度→像素系数)。当前为5.5,增大该值会使气泡移动更明显。
Q7: 代码中为什么使用了lastHeading = -1?
-
用于强制第一次进入指南针模式时无条件绘制界面。因为
lastHeading初始为-1,与任何有效航向(0~360)都不相等,从而触发首次绘制。
更多推荐



所有评论(0)