项目概述

        本教程将指导你使用零知派ESP32、HMC5883L磁力计、MPU6050六轴传感器和ST7789 TFT屏幕,制作一款功能完善的电子指南针与水平仪设备。设备具备以下功能:

  • 指南针模式:显示真北方位角(0~360°),带旋转罗盘玫瑰图,支持硬铁校准、磁偏角修正和用户一键归零。

  • 水平仪模式:实时显示设备横滚角(Roll)和俯仰角(Pitch),以气泡水平仪的形式直观展示倾斜状态。

  • WiFi定位:首次进入指南针时自动获取当前位置(经纬度、海拔),并显示在屏幕底部。

  • 按键交互:三个独立按键分别控制功能切换和校准操作。

项目采用模块化设计,包含传感器自动检测、非易失存储校准数据、界面局部刷新等优化,整体运行流畅,资源占用合理。

项目亮点

  1. 完整的硬铁校准机制
    通过绕∞字形旋转10秒,自动计算磁力计各轴的偏移量并存入NVS,断电不丢失,确保指南针在任何环境下都能准确指向。

  2. 多种方向修正

    • 磁偏角修正(基于地理位置,深圳地区约-2.7°)

    • 用户归零偏移(一键将任意方向设为0°)

    • 安装方向修正(通过atan2(-y, x)适应传感器在PCB上的实际朝向)

  3. 智能化的界面刷新
    指南针和水平仪均采用局部擦除重绘策略,仅更新动态变化的区域(数值、罗盘、气泡),大幅降低CPU负载并提高帧率。

  4. 防卡死与容错设计

    • 所有I2C操作前检测设备在线状态

    • HTTP请求设置5秒超时

    • 连续50次I2C读取失败(约5秒)自动退回主菜单

    • 校准过程支持S3按键跳过

  5. 一阶低通滤波
    水平仪气泡运动经过软件平滑,消除传感器噪声带来的抖动,显示更稳定。

项目难点及解决方案

难点 解决方案
磁力计受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()函数按顺序执行以下操作:

  1. 串口与I2C总线初始化
    Serial.begin(115200)Wire.begin()

  2. 按键引脚配置
    将GPIO12/14/27设为输入上拉模式:pinMode(..., INPUT_PULLUP)

  3. TFT屏幕初始化
    tft.init()tft.setRotation(3) 设置显示方向

  4. 检测HMC5883L并显示状态
    调用initCompass(),若成功则compassOK=true,否则false。在主菜单显示绿色“OK”或红色“N/A”

  5. 检测MPU6050并显示状态
    调用initMPU(),类似处理

  6. 绘制主菜单界面
    画出系统状态栏、两个传感器状态框、三个功能按钮

初始化流程图:

开机 ──→ 初始化串口/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()):

  1. 读取原始磁场向量

  2. 减去硬铁偏移

  3. 计算夹角:atan2(-y, x)

  4. 根据COMPASS_REVERSE决定是否反转

  5. 加上磁偏角

  6. 归一化到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次平均值作为零偏(rollOffsetpitchOffset),之后显示roll - rollOffsetpitch - 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 操作步骤

  1. 上电启动
    屏幕显示“System Status”主菜单,自动检测传感器。HMC5883L和MPU6050状态分别显示OK或N/A。

  2. 使用指南针

    • 按 S1 进入指南针模式。

    • 首次进入会连接WiFi获取经纬度/海拔(约2~5秒),然后检查是否已有校准数据。若未校准,会提示“Rotate device in figure-8”,此时手持设备绕“∞”字形旋转10秒(可随时按S3跳过)。

    • 校准完成后,屏幕显示当前航向角度(0~360°)、方向缩写(N/NE/E等)、旋转罗盘以及底部经纬度/海拔。

    • 归零操作:在指南针模式下,按S1可将当前方向设为0°(例如将设备对准某个标记点,按S1,此后航向显示为该方向相对角度)。

    • 按 S3 返回主菜单。

  3. 使用水平仪

    • 将设备水平放置在桌面上,按 S2 进入水平仪模式。

    • 设备会自动采集20次角度平均值作为零偏(请在此期间保持静止)。

    • 之后屏幕上显示圆形气泡水平仪,以及当前的Roll/Pitch数值。

    • 倾斜设备,气泡会向高侧移动,中心红色点为水平基准。

    • 按 S3 返回主菜单。

  4. 查看传感器状态
    主菜单实时显示两个模块是否正常,若某模块未连接或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)都不相等,从而触发首次绘制。

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐