ESP32超声波测距实战:HC-SR04与ATOM Matrix LED可视化

📅 发布时间:2026/8/19 4:34:36
ESP32超声波测距实战:HC-SR04与ATOM Matrix LED可视化
1. 项目概述当ATOM Matrix遇上超声波测距最近在捣鼓一个智能小车避障的项目手头正好有一块M5Stack的ATOM Matrix ESP32开发板还有几个吃灰的HC-SR04超声波模块。把它们组合起来做一个直观的超声波测距仪听起来是个不错的练手项目。这个项目不仅能让你熟悉ESP32的GPIO操作和定时器还能把ATOM Matrix那块5x5的LED点阵屏玩出花来用动态的像素点来可视化距离比单纯看串口打印的数字有趣多了。这个项目非常适合刚接触ESP32或物联网硬件的朋友。你不需要复杂的电路知识只需要几根杜邦线就能搭建一个可工作的测距系统。我们将使用Arduino IDE进行开发这是目前最流行、对新手最友好的ESP32开发环境。通过这个项目你将掌握如何驱动HC-SR04模块、如何处理传感器数据以及如何利用ATOM Matrix的LED矩阵进行创意显示。最终你会得到一个可以实时测量20cm到400cm范围内距离并用灯光效果直观反馈的小装置。2. 核心硬件与开发环境搭建2.1 硬件清单与选型解析我们先来清点一下需要用到的核心硬件并聊聊为什么选它们。ATOM Matrix ESP32开发板这是项目的核心大脑。它集成了ESP32-PICO-D4芯片性能足够强大同时板载了5x5 RGB LED矩阵、一个按键、一个红外发射管和一个六轴传感器MPU6886。选择它一方面是因为其小巧的尺寸24*24mm非常适合做集成化项目另一方面内置的LED矩阵为我们提供了无需额外接线即可实现丰富视觉反馈的绝佳条件。它的GPIO引脚虽然不多但驱动一个超声波模块绰绰有余。HC-SR04超声波测距模块这是最经典、性价比最高的超声波测距传感器之一。它通过发射40kHz的超声波并接收回波通过计算时间差来换算距离。其测量范围约2cm到400cm精度在3mm左右完全满足大多数非高精度测距场景。选择它是因为资料丰富、价格低廉且与3.3V逻辑电平的ESP32兼容性好注意有些老版本HC-SR04是5V逻辑需要电平转换但近年生产的多数已支持3.3V。连接线若干杜邦线母对母。用于连接ATOM Matrix和HC-SR04。接线方案 ATOM Matrix的GPIO引脚有限我们选择以下引脚进行连接HC-SR04的 Trig触发引脚接ATOM Matrix 的 GPIO22。这个引脚用于发送一个至少10微秒的高电平脉冲触发模块发射超声波。HC-SR04的 Echo回波引脚接ATOM Matrix 的 GPIO19。这个引脚会输出一个高电平脉冲其宽度与超声波往返时间成正比。HC-SR04的 VCC 和 GND分别接ATOM Matrix 的 5V或3.3V输出和GND。经过实测ATOM Matrix的3.3V输出足以驱动HC-SR04这样更安全。如果测量距离变短或不稳定再尝试接5V。注意在接线前最好用万用表确认一下你的HC-SR04模块的供电电压要求。直接将5V模块接到3.3V引脚可能导致工作不正常但将3.3V模块接到5V引脚则有烧毁风险。稳妥起见可以先从3.3V开始尝试。2.2 软件环境配置详解我们将使用Arduino IDE进行开发。以下是详细的配置步骤安装Arduino IDE从Arduino官网下载并安装最新版本的IDE1.8.x或2.0.x均可。添加ESP32开发板支持打开Arduino IDE进入“文件”-“首选项”。在“附加开发板管理器网址”中填入以下网址https://espressif.github.io/arduino-esp32/package_esp32_index.json如果已有其他网址用逗号分隔。点击“确定”保存。安装ESP32开发板包进入“工具”-“开发板”-“开发板管理器”。在搜索框中输入“esp32”找到由“Espressif Systems”提供的“ESP32”开发板包点击安装。这个过程可能需要几分钟取决于你的网络速度。安装ATOM Matrix库ATOM Matrix的LED矩阵需要专门的库来驱动。我们使用M5Stack官方提供的M5Atom库。在Arduino IDE中点击“项目”-“加载库”-“管理库...”。在库管理器中搜索“M5Atom”找到后安装。这个库包含了控制LED矩阵、按键和内置传感器的所有必要函数。选择开发板和端口用USB-C数据线将ATOM Matrix连接到电脑。在Arduino IDE的“工具”菜单下开发板选择“M5Stack-ATOM”。端口选择新出现的串口如COMx或/dev/cu.usbmodemXXX。其他设置保持默认即可。环境搭建好后我们可以写一个简单的LED闪烁程序来测试板子和环境是否工作正常。#include M5Atom.h void setup() { M5.begin(true, false, true); // 初始化M5Atom启用LED矩阵不启用串口启用I2C delay(10); } void loop() { M5.dis.drawpix(0, 0x00ff00); // 点亮中心LED为绿色 delay(500); M5.dis.clear(); // 关闭所有LED delay(500); }将代码上传到ATOM Matrix你应该能看到板子中央的绿色LED以1秒的间隔闪烁。这说明硬件和软件环境都已就绪。3. 超声波测距原理与代码实现3.1 HC-SR04工作原理深度剖析知其然更要知其所以然。HC-SR04模块的工作时序是编程的基础。它遵循一个非常标准的“触发-回波”协议触发阶段微控制器我们的ESP32向Trig引脚发送一个至少10微秒的高电平脉冲。这个脉冲就像一声“口令”告诉超声波模块“准备发射声波”发射与接收阶段模块内部电路收到触发信号后会自动发射8个40kHz的超声波脉冲同时将Echo引脚拉高。回波检测阶段超声波在空气中传播遇到障碍物后反射回来。模块接收到回波后会将Echo引脚拉低。距离计算Echo引脚高电平的持续时间就是超声波从发射到返回所经历的时间记为t单位微秒。 声音在空气中的速度约为340米/秒随温度变化常温下可近似。距离S的计算公式为S (t / 2) * 声速。除以2是因为t是往返时间。 换算成厘米公式为距离(cm) (t / 2) * 0.0343或者更常用的距离(cm) t * 0.01715即34000 cm/s * 1e-6 s/us / 2。实操心得公式中的声速是理想值。在实际应用中温度、湿度都会影响声速。对于精度要求不高的场景如避障这个误差可以接受。如果追求更高精度可以加入温湿度传感器如DHT11进行声速补偿公式会变为声速 331.4 0.6 * 温度(℃)米/秒。3.2 基础测距代码编写与调试理解了原理我们来编写最核心的测距函数。我们将使用ESP32的pulseIn()函数来测量Echo引脚高电平的持续时间。这个函数会阻塞程序直到脉冲结束所以不适合在需要快速响应的复杂主循环中使用但对于我们这个单任务项目来说完全没问题。#include M5Atom.h // 引脚定义 const int trigPin 22; const int echoPin 19; // 变量定义 long duration; float distance_cm; void setup() { M5.begin(true, false, true); // 初始化Atom Serial.begin(115200); // 初始化串口用于调试输出 pinMode(trigPin, OUTPUT); pinMode(echoPin, INPUT); digitalWrite(trigPin, LOW); // 初始确保Trig为低电平 delay(100); Serial.println(Ultrasonic Sensor Test Start); } float measureDistance() { // 1. 发送10us的高电平触发脉冲 digitalWrite(trigPin, LOW); delayMicroseconds(2); // 短暂低电平确保稳定 digitalWrite(trigPin, HIGH); delayMicroseconds(10); // 维持10us高电平 digitalWrite(trigPin, LOW); // 2. 读取回波脉冲宽度单位微秒 // pulseIn()会等待echoPin变为HIGH开始计时直到变回LOW duration pulseIn(echoPin, HIGH, 30000); // 超时时间设为30000us (30ms)对应约5米距离 // 3. 计算距离单位厘米 // 声速按340m/s计算时间除以2往返 distance_cm duration * 0.01715; // 如果超时或距离异常返回一个错误值如-1 if (duration 0 || distance_cm 400 || distance_cm 2) { return -1.0; } return distance_cm; } void loop() { float d measureDistance(); if (d 0) { Serial.print(Distance: ); Serial.print(d); Serial.println( cm); } else { Serial.println(Measurement error or out of range); } M5.update(); // 更新M5状态用于处理按键等 delay(200); // 每次测量间隔200ms给传感器留出“安静”时间避免上一次回波干扰 }将代码上传后打开串口监视器波特率设为115200用手在传感器前来回移动你应该能看到距离数据在变化。常见问题与排查读数一直是0或一个很小的固定值检查接线是否正确特别是Echo和Trig是否接反。确保Trig引脚确实输出了脉冲可以用逻辑分析仪或另一个Arduino的pulseIn检测。读数非常大或不稳定可能是传感器前方没有有效的障碍物回波太弱或没有回波导致pulseIn超时返回了错误值。检查pulseIn的超时参数是否合理。也可以尝试在传感器前方放置一个较大的平面障碍物。测量频率上不去pulseIn是阻塞函数测量间隔受限于声波往返最长时间约23ms对应4米。如果想提高频率可以考虑使用中断来检测Echo引脚边沿但这会复杂很多。对于本项目200ms的间隔是合理的。4. LED矩阵可视化方案设计4.1 显示策略规划让5x5的LED点阵显示距离信息需要一些巧思。直接显示数字不是它的强项但进行图形化、进度条式的可视化则非常合适。这里我设计了几种显示模式你可以根据喜好选择或组合进度条模式将25个LED视为一个从下到上或从左到右的进度条。距离越近点亮的LED数量越多或越少颜色可以从绿色安全渐变到红色危险。这是最直观的方式。中心扩散模式以矩阵中心为原点根据距离远近点亮不同半径的“圆环”。距离越近“圆环”越小。这能营造出一种障碍物逼近的压迫感。数值模拟模式用二进制或自定义的字体勉强显示两位数字。但这会牺牲美观性且可读性一般不推荐作为主要模式。阈值警报模式设定一个安全距离阈值如20cm。当距离大于阈值时显示一种平静的图案如蓝色呼吸灯当距离小于阈值时显示警报图案如红色闪烁。我们将主要实现进度条模式和阈值警报模式的结合体。4.2 可视化代码实现与优化我们需要一个函数将测量得到的距离值映射到LED矩阵的显示状态上。M5.dis.drawpix函数可以控制单个LED参数是LED索引0-24和颜色值24位RGB如0xFF0000是红色。首先定义一些颜色和阈值// 颜色定义 (格式: 0xRRGGBB) #define COLOR_SAFE 0x00FF00 // 绿色 #define COLOR_WARNING 0xFFFF00 // 黄色 #define COLOR_DANGER 0xFF0000 // 红色 #define COLOR_BG 0x101010 // 暗灰色背景 // 距离阈值 (单位: cm) const float DANGER_DISTANCE 10.0; const float WARNING_DISTANCE 30.0; const float MAX_DISPLAY_DISTANCE 100.0; // 超过此距离认为很远 void displayDistanceOnMatrix(float dist_cm) { M5.dis.clear(); // 清空屏幕 if (dist_cm 0) { // 测量错误显示错误图案例如所有LED闪烁红色 for (int i 0; i 25; i) { M5.dis.drawpix(i, COLOR_DANGER); } delay(100); M5.dis.clear(); delay(100); return; } // 将距离映射到LED点亮数量 (0-25) // 距离越近点亮数量越多。限制在MAX_DISPLAY_DISTANCE以内 float clampedDist (dist_cm MAX_DISPLAY_DISTANCE) ? MAX_DISPLAY_DISTANCE : dist_cm; int ledLevel map(clampedDist, 0, MAX_DISPLAY_DISTANCE, 25, 0); ledLevel constrain(ledLevel, 0, 25); // 根据距离选择颜色 uint32_t ledColor; if (dist_cm DANGER_DISTANCE) { ledColor COLOR_DANGER; } else if (dist_cm WARNING_DISTANCE) { ledColor COLOR_WARNING; } else { ledColor COLOR_SAFE; } // 绘制进度条从底部第一行开始点亮 for (int i 0; i ledLevel; i) { // 计算LED索引。从底部左下角(索引20)开始按行向上填充。 int row i / 5; int col i % 5; int index (4 - row) * 5 col; // 从底部开始算起 M5.dis.drawpix(index, ledColor); } }然后修改主循环调用这个显示函数void loop() { float d measureDistance(); if (d 0) { Serial.print(Distance: ); Serial.print(d); Serial.println( cm); displayDistanceOnMatrix(d); // 更新LED显示 } else { Serial.println(Error); displayDistanceOnMatrix(-1); // 显示错误状态 } M5.update(); // 加入按键交互按Atom上的按键切换显示模式后续扩展 if (M5.Btn.wasPressed()) { // 切换模式的代码可以加在这里 } delay(150); // 主循环延迟 }现在当你运行程序时ATOM Matrix的LED矩阵会像一个垂直的温度计或进度条一样随着障碍物的靠近从底部开始向上点亮LED并且颜色会根据距离的远近在绿、黄、红之间变化。优化技巧消抖与平滑超声波传感器读数可能会有偶尔的跳动。可以在代码中加入简单的软件滤波比如连续采样3次取中位数作为最终结果能有效消除野值。float getFilteredDistance() { float readings[3]; for (int i 0; i 3; i) { readings[i] measureDistance(); delay(50); // 每次测量间隔一点时间 } // 简单的排序取中值 if (readings[0] readings[1]) swap(readings[0], readings[1]); if (readings[1] readings[2]) swap(readings[1], readings[2]); if (readings[0] readings[1]) swap(readings[0], readings[1]); return readings[1]; // 返回中位数 }亮度调节在光线强的环境下LED可能显得太暗。M5Atom库允许设置全局亮度0-255。可以在setup()中加入M5.dis.setBrightness(100);进行调节。5. 项目进阶与功能扩展基础功能实现后我们可以让这个小项目变得更智能、更实用。这里提供几个扩展方向。5.1 无线数据传输与远程监控ESP32最大的优势之一就是集成了Wi-Fi和蓝牙。我们可以很容易地将测量到的距离数据发送出去。示例通过Wi-Fi将数据发送到串口监视器或网络服务器#include M5Atom.h #include WiFi.h const char* ssid 你的Wi-Fi名称; const char* password 你的Wi-Fi密码; void setup() { M5.begin(true, false, true); Serial.begin(115200); // 连接Wi-Fi WiFi.begin(ssid, password); Serial.print(Connecting to WiFi...); while (WiFi.status() ! WL_CONNECTED) { delay(500); Serial.print(.); } Serial.println(\nConnected! IP address: ); Serial.println(WiFi.localIP()); // ... 超声波引脚初始化代码 ... } void loop() { float d getFilteredDistance(); // 使用滤波后的距离 // 在串口打印同时可以附加IP地址等信息 Serial.printf([%lu] Distance: %.2f cm\n, millis(), d); // 未来可以在这里添加代码将数据发送到MQTT服务器、WebSocket服务器或HTTP API // 例如HTTPClient http; http.begin(http://your-server.com/api/data); ... displayDistanceOnMatrix(d); M5.update(); delay(300); // 无线传输时间隔可以稍长 }扩展思路MQTT协议使用PubSubClient库将距离数据发布到MQTT Broker如本地搭建的Mosquitto或云服务商提供的MQTT服务。这样你可以在手机或电脑上的MQTT客户端软件实时订阅查看数据。WebSocket服务器在ESP32上运行一个简单的WebSocket服务器。然后在同一局域网内的电脑浏览器中打开一个HTML页面通过JavaScript连接WebSocket实时接收并图表化显示距离数据实现一个简单的本地监控面板。蓝牙串口SPP对于短距离通信可以启用ESP32的蓝牙经典模式模拟一个串口。用手机上的蓝牙串口APP如“Serial Bluetooth Terminal”连接后就能像看串口监视器一样接收数据非常方便。5.2 利用内置传感器丰富交互ATOM Matrix内置了MPU6886六轴传感器加速度计陀螺仪。我们可以利用它来增加交互维度。示例通过晃动板子切换显示模式我们在前面预留了按键交互。现在增加一个姿态交互。假设我们定义两种显示模式0-进度条模式1-中心扩散模式。#include M5Atom.h #include utility/MPU6886.h // 需要包含MPU6886的头文件 MPU6886 IMU; // 定义IMU对象 int displayMode 0; // 当前显示模式 float accX, accY, accZ; // 加速度值 unsigned long lastShakeTime 0; const int SHAKE_COOLDOWN 1000; // 防抖时间1秒内不重复触发 void setup() { M5.begin(true, false, true); // 初始化IMU if (IMU.Init() ! 0) { Serial.println(MPU6886 Init Failed!); } else { Serial.println(MPU6886 Init Success.); } // ... 其他初始化代码 ... } void checkShake() { IMU.getAccelData(accX, accY, accZ); // 获取加速度数据 // 计算合加速度减去重力影响粗略判断 float totalAcc fabs(accX) fabs(accY) fabs(accZ - 1.0); unsigned long now millis(); // 如果晃动强度大于阈值且冷却时间已过 if (totalAcc 2.5 (now - lastShakeTime) SHAKE_COOLDOWN) { displayMode 1 - displayMode; // 在0和1之间切换 Serial.printf(Shake detected! Switch to Mode: %d\n, displayMode); lastShakeTime now; } } void displayMode0(float dist) { /* 进度条模式的代码 */ } void displayMode1(float dist) { // 中心扩散模式根据距离计算点亮半径 M5.dis.clear(); int radius map(constrain(dist, 0, MAX_DISPLAY_DISTANCE), 0, MAX_DISPLAY_DISTANCE, 2, 0); uint32_t color (dist DANGER_DISTANCE) ? COLOR_DANGER : (dist WARNING_DISTANCE) ? COLOR_WARNING : COLOR_SAFE; for (int x 0; x 5; x) { for (int y 0; y 5; y) { // 计算每个LED到中心(2,2)的距离 float dToCenter sqrt(pow(x-2, 2) pow(y-2, 2)); if (dToCenter radius) { int index y * 5 x; M5.dis.drawpix(index, color); } } } } void loop() { checkShake(); // 检测晃动 float d getFilteredDistance(); if (M5.Btn.wasPressed()) { // 按键也可以切换模式 displayMode 1 - displayMode; Serial.printf(Button pressed! Switch to Mode: %d\n, displayMode); } switch (displayMode) { case 0: displayMode0(d); break; case 1: displayMode1(d); break; } M5.update(); delay(150); }这样你的测距仪就有了两种显示风格并且可以通过晃动或按键来切换互动性大大增强。6. 系统优化与生产级考量如果你打算把这个小项目用作某个更大项目的一部分比如智能小车的核心避障模块或者需要它长时间稳定运行那么就需要考虑更多工程化的问题。6.1 电源管理与低功耗设计ATOM Matrix ESP32在持续点亮LED和频繁进行超声波测距时功耗并不低。如果使用电池供电续航会是个问题。优化策略动态测量频率当检测到距离长时间没有变化时可以自动降低测量频率比如从每秒5次降到每秒1次。当距离快速变化接近障碍物时再提高频率。LED亮度与开关控制在不需要查看时可以调低LED亮度甚至完全关闭LED显示。M5.dis.setBrightness(0)可以关闭。使用ESP32的深度睡眠模式这是最省电的方式。可以让ESP32每隔几秒唤醒一次测量距离如果距离安全则通过LED闪烁一下或根本不亮然后立刻再次进入深度睡眠。但这需要外部电路将Trig/Echo信号也拉低或者使用支持唤醒的传感器实现起来较复杂。优化代码效率避免在循环中使用delay()进行长时间等待。可以使用millis()进行非阻塞定时在等待传感器响应的同时处理器可以处理其他任务或进入轻睡眠。示例非阻塞定时测量unsigned long previousMeasureTime 0; const long measureInterval 200; // 测量间隔200ms void loop() { unsigned long currentMillis millis(); if (currentMillis - previousMeasureTime measureInterval) { previousMeasureTime currentMillis; float d getFilteredDistance(); // ... 处理显示和数据发送 ... } // 这里可以处理其他不依赖严格定时的事情比如检查网络连接、处理按键 checkShake(); M5.update(); // 没有delayloop运行很快CPU占用高但响应迅速。 // 如果为了省电可以在循环末尾加一个短暂的 delay(1) 或使用 yield()。 }6.2 稳定性提升与异常处理工业环境中传感器可能会受到干扰程序需要更健壮。信号去噪与滤波前面提到了中值滤波。还可以使用均值滤波或一阶低通滤波卡尔曼滤波对于这个应用有点杀鸡用牛刀。float filteredDistance 0.0; const float alpha 0.3; // 滤波系数越小越平滑但延迟越大 void loop() { float rawDist measureDistance(); if (rawDist 0) { // 只对有效数据进行滤波 filteredDistance alpha * rawDist (1 - alpha) * filteredDistance; } // 使用 filteredDistance }连接状态监测如果使用Wi-Fi需要定期检查连接状态并在断线时尝试重连。可以使用WiFi.status()函数。看门狗定时器ESP32内置了硬件看门狗。启用它可以在程序跑飞或陷入死循环时自动重启系统提高设备的自恢复能力。#include esp_task_wdt.h void setup() { esp_task_wdt_init(10, true); // 看门狗超时时间10秒 esp_task_wdt_add(NULL); // 将当前任务添加到看门狗监控 // ... 其他初始化 } void loop() { esp_task_wdt_reset(); // 喂狗必须在超时前调用 // ... 主循环代码 }错误恢复在measureDistance()函数中如果连续多次测量失败可以尝试重新初始化传感器引脚或者通过LED显示特定的错误代码比如让所有LED以某种特定频率闪烁便于现场调试。7. 项目总结与衍生应用这个基于ATOM Matrix和HC-SR04的超声波测距项目从硬件连接到软件编程从基础功能到进阶优化我们完整地走了一遍。它不仅仅是一个简单的测距仪更是一个学习ESP32嵌入式开发、传感器应用和创意交互的绝佳平台。我个人在多次调试中发现传感器的安装位置和角度对测量结果影响很大。尽量让传感器表面与待测平面平行并且避开周围的尖锐物体可以减少误反射。另外对于绒布、泡沫这种吸音材料或者角度非常倾斜的镜面HC-SR04可能会失效这是其物理原理决定的在选择应用场景时需要注意。这个小项目的代码和思路可以轻松移植到其他ESP32开发板上只需要调整一下引脚定义和显示部分如果没有LED矩阵可以用串口输出或者接一个OLED屏幕。它的衍生应用场景非常多智能小车避障这是最直接的应用将传感器安装在车前程序逻辑改为当距离小于某个阈值时向电机驱动板发送转向或停止命令。智能垃圾桶开盖安装在垃圾桶盖内侧当手靠近时触发舵机打开桶盖。液位高度检测将传感器垂直安装在容器顶部向下测量液面距离从而换算出液位高度。需要注意液体表面是否平整以及超声波在空气中的传播特性。简易安防报警器安装在门窗附近当有人经过导致距离骤变时通过Wi-Fi向手机发送通知或者触发本地声光报警。最后再分享一个调试小技巧在开发过程中除了使用串口打印还可以利用ATOM Matrix的LED矩阵本身来显示简单的状态码。比如让左上角的LED闪烁代表Wi-Fi连接中常亮代表连接成功右下角LED闪烁代表正在测量等等。这样即使不接电脑你也能对设备的工作状态一目了然。