DIS 数字化实验

ESP32 + 蓝牙 BLE + 网页端实时数据采集与绘图

什么是 DIS 数字化实验?

蓝牙 BLE Web Bluetooth 兼容 phyphox DIS(Digital Information System)数字化实验系统通过传感器实时采集物理数据, 经蓝牙传输到网页端,自动绘制图像并进行分析。无需安装任何 App,手机浏览器即可使用。

工作原理:ESP32 开发板连接传感器采集数据 → 通过蓝牙 BLE GATT 协议发送到浏览器 → 网页使用 Web Bluetooth API 接收数据 → Chart.js 实时绘制曲线。

兼容说明:本页面使用 phyphox 标准 BLE UUID,你现有的 ESP32 phyphox 代码无需任何修改, 网页和 phyphox App 可以同时接收数据。

当前浏览器不支持 Web Bluetooth API。请使用 Android 版 ChromeChrome/Edge 桌面版(需开启 chrome://flags/#enable-experimental-web-platform-features)。iOS Safari 暂不支持。
超声波测距
楞次定律
电容器充放电
光电门测速
牛顿第三定律
查理定律
未连接
-- cm

ESP32 MicroPython 代码(超声波测距 + OLED,兼容 phyphox)

你不需要修改 ESP32 代码!本页面使用与 phyphox 相同的 BLE UUID,网页和 phyphox App 可以同时连接。 下方是最新版本:增加 0.91 寸 OLED 本地显示、多重反射过滤算法,采样率提升到 50 Hz,蓝牙名为 Distance

BLE 通信参数(与 phyphox 一致)

Service UUIDcddf1001-30f7-...
Characteristic UUIDcddf1002-30f7-...
数据格式float32 小端序
蓝牙设备名Distance
采样率约 50 Hz

超声波 + OLED 接线

超声波 VCC3V3
超声波 GNDGND
超声波 TRIGGPIO 5
超声波 ECHOGPIO 18
OLED SCLGPIO 22
OLED SDAGPIO 21

以下是 MicroPython + phyphoxBLE 库的超声波测距完整代码(直接刷入 ESP32 即可,phyphox App 和本网页可同时接收数据):

"""
超声波测距 + 0.91寸 OLED 显示 + phyphox BLE 传输
硬件: ESP32-WROOM / 超声波模块(内部 CS100A, 3.3V-5V 兼容) / SSD1306 0.91寸 OLED (I2C)
库:   phyphoxBLE (需上传到板子的 /lib/phyphoxBLE/ 或根目录)

用法: 在 Thonny 中打开本文件, 点运行; 手机端打开 phyphox -> 右上角 "+" -> 选择蓝牙名 Distance

接线 (CS100A 模块, 3.3V 供电方案):
  VCC -> 3V3
  GND -> GND
  TRIG-> GPIO5
  ECHO-> GPIO18  (直连, 不用分压; 仅当 VCC 接 5V 时才需要 1k+2k 分压)
"""

from machine import Pin, I2C, time_pulse_us
import time
import ssd1306
from phyphoxBLE import PhyphoxBLE, Experiment

# ---------------- 引脚 ----------------
TRIG_PIN = 5       # 超声波 TRIG
ECHO_PIN = 18      # 超声波 ECHO (CS100A 用 3.3V 供电时直连即可, 无需分压)
SCL_PIN  = 22      # OLED SCL
SDA_PIN  = 21      # OLED SDA

trig = Pin(TRIG_PIN, Pin.OUT)
echo = Pin(ECHO_PIN, Pin.IN)
trig.value(0)

# ---------------- OLED (0.91寸 SSD1306) ----------------
i2c = I2C(0, scl=Pin(SCL_PIN), sda=Pin(SDA_PIN), freq=400000)
oled = ssd1306.SSD1306_I2C(128, 32, i2c)

# ---------------- phyphox 实验定义 ----------------
p = PhyphoxBLE()

exp = Experiment()
exp.setTitle("超声波测距")
exp.setCategory("西大银翔DIS")
exp.setDescription("ESP32 + 超声波传感器测距,数据通过 BLE 实时传输到 phyphox")

# 视图1: 距离随时间变化的曲线 (X轴=时间CH0, Y轴=距离CH1)
view1 = Experiment.View()
view1.setLabel("距离-时间")
g = Experiment.Graph()
g.setLabel("距离")
g.setChannel(0, 1)
g.setLabelX("时间 t (s)")
g.setLabelY("距离 d (cm)")
g.setUnitX("s")
g.setUnitY("cm")
view1.addElement(g)
exp.addView(view1)

# 视图2: 实时数值
view2 = Experiment.View()
view2.setLabel("当前数值")
v = Experiment.Value()
v.setLabel("当前距离")
v.setUnit("cm")
v.setPrecision(1)
v.setChannel(1)
view2.addElement(v)
exp.addView(view2)

p.addExperiment(exp)
p.start(device_name="Distance")
print("BLE 已启动, 请在 phyphox 中连接 (蓝牙名: Distance)")

# ---------------- 超声波测距 ----------------
MAX_JUMP   = 30    # 相邻采样允许的最大正常变化(cm)
CONFIRM_TOL = 8    # 大跳变后, 连续接近新值的容差(cm)
last_good  = None  # 已确认的基准距离
pending    = None  # 大跳变待确认值
pending_n  = 0     # 待确认值连续出现的次数

def measure_distance():
    # 先等上一条回波电平完全结束, 避免高速测量时读到上一条的残留
    time_pulse_us(echo, 0, 1000)
    trig.value(1)
    time.sleep_us(10)
    trig.value(0)
    duration = time_pulse_us(echo, 1, 20000)   # 20ms 超时 ≈ 3.4m 量程, 超范围不拖慢循环
    if duration <= 0:                          # -1 超时 / -2 电平不对
        return None
    return duration / 2 / 29.1                 # 声速 343m/s, 往返除2, 换算 cm

def plausible(dist):
    """区分'真实的大移动'和'多重反射杂波':
    正常小变化直接采信; 大跳变需连续两帧都落在新值附近才采信,
    单帧尖峰(杂波)会被丢弃。这样用手/小车挡住传感器也能及时更新。"""
    global last_good, pending, pending_n
    if last_good is None:                 # 第一帧直接采信
        last_good = dist
        return True
    if abs(dist - last_good) <= MAX_JUMP: # 正常连续变化
        last_good = dist
        pending = None
        pending_n = 0
        return True
    # 大跳变: 进入"待确认"状态
    if pending is not None and abs(dist - pending) <= CONFIRM_TOL:
        pending_n += 1
        if pending_n >= 2:                # 连续两次确认 = 真实移动
            last_good = dist
            pending = None
            pending_n = 0
            return True
        return False                      # 才第一次, 再等一帧
    pending = dist                        # 记为大跳变候选
    pending_n = 1
    return False

def send(value):
    """发送距离到 phyphox; 连接断开时捕获错误, 不让程序崩溃"""
    try:
        p.write(value)
    except OSError:
        print("BLE 连接已断开, 请在 phyphox 重新连接")

while True:
    dist = measure_distance()
    if dist is not None and plausible(dist):
        send(dist)                             # 发送给 phyphox (写入 CH1)
        oled.fill(0)
        oled.text("Distance:", 0, 0)
        oled.text("{:.1f} cm".format(dist), 0, 14)
        oled.show()
        print("Distance: {:.1f} cm".format(dist))
    elif dist is not None:
        # 杂波跳变: 丢弃, 用上一次有效值保持曲线连续
        if last_good is not None:
            send(last_good)
        print("spike filtered: {:.1f} cm".format(dist))
    else:
        print("measurement timeout")
    time.sleep_ms(20)                          # 20ms ≈ 50Hz, 反应更快

说明:本版本使用 CS100A 超声波模块(3.3V 供电,ECHO 直连 GPIO18,无需分压电阻)。 若用老款 HC-SR04 且 VCC 接 5V,则 ECHO 必须接 1kΩ+2kΩ 分压后再到 GPIO18,否则会烧坏 ESP32。 如果你使用的是自己写的 BLE 代码(非 phyphox 库),只要 Service UUID = cddf1001-30f7-4671-8b43-5e40ba53514a、 Characteristic UUID = cddf1002-30f7-4671-8b43-5e40ba53514a,数据按 float32 小端序发送,网页同样可以接收。

使用步骤

1. 在上方选项卡选择实验类型(超声波测距楞次定律电容器充放电光电门测速牛顿第三定律查理定律

2. 将对应 ESP32 代码上传到开发板(现有 phyphox 代码无需修改)

3. 用 Android 手机 Chrome 浏览器打开本页面

4. 单实验点击「连接 ESP32」选择设备;牛顿第三定律点击「连接两块传感器」,按浏览器提示依次选 ForceA、ForceB

5. 连接成功后点击「开始采集」按钮,实时数据将自动显示

6. 点击「导出 CSV」可将数据保存为 Excel 可用格式