这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » 活动中心 » 板卡试用 » M5STACKTAB5项目【室内智能环境监控系统】

共1条 1/1 1 跳转至

M5STACKTAB5项目【室内智能环境监控系统】

助工
2026-01-24 22:57:31   被打赏 50 分(兑奖)     打赏

一、硬件介绍

Tab5 集成了双芯片架构和丰富的硬件资源,其主控采用基于 RISC‑V 架构的 ESP32‑P4 SoC,并配备 16MB Flash 与 32MB PSRAM,无线模块则选用 ESP32-C6-MINI-1U,支持 Wi-Fi 6;

还配备5英寸(1280×720 IPS)触控屏幕,以及2MP摄像头(1600×1200)、双麦克风阵列,3.5mm耳机孔与扬声器;


内置BMI270六轴传感器、实时时钟,板载HY2.0-4P,M5-Bus,GPIO_EXT排母和microSD卡槽等;

底部兼容NP‑F550可拆卸锂电池(具备充放电与实时监测电路);

image-20260104213126322.png


image-20260104213126322

系统框图



image-20260104223329802.png



image-20260104223329802二、功能实现

1、Tab 5无线模块

Tab5的无线模块是ESP32-C6-MINI-1U,支持 Wi-Fi 6 / BLE 5,其天线系统可在内置 3D 天线与外部 MMCX 天线接口之间自由切换;



特性


ESP32-C6 是一款支持 2.4 GHz Wi-Fi 6、Bluetooth 5、Zigbee 3.0 及 Thread 1.3 系统级芯片 (SoC),集成了一 个高性能 RISC-V 32 位处理器和一个低功耗 RISC-V 32 位处理器、Wi-Fi、Bluetooth LE、802.15.4 基带和 MAC、 RF 模块及外设等;




硬件引脚连接

image.png


原理图


image-20260111204446286.png



2、多功能环境采集模块



采集模块为Arduino Nicla Sense ME,主控为nRF52832 (64 MHz) 具有蓝牙功能;

配备了Bosch Sensortec的具有高线性、高精度,可探测气压、湿度、和温度的 BME688 四合一气体传感器,可检测挥发性有机化合物 (VOC) 和其他气体;



image-20260116162509381.png


image-20260116162509381

特性

image.png










系统框图

image-20260116162546086.png


功能效果


主要功能效果

通过Nicla Sense ME模块,将采集室内相关的环境数据(温湿度、CO2当量、气压、室内空气质量IAQ),通过板载的BLE功能将数据发送至Tab5开发板上;

并在Tab5的屏幕UI界面上进行相关数据的显示以及通过实时折线图进行数据采集分析(历史最大值 / 最小值),且当IAQ数值处于不同范围值时,会触发不同的语音播报功能。


image-20260116165720477.png



1、数据参数解析


采集的数据有温度、湿度、CO2当量、气压、室内空气质量IAQ数值;


1、室内空气质量IAQ


室内空气质量(Indoor Air Quality,简称 IAQ)是指在一定时间和空间范围内,室内空气中所含各类物质(包括气体等)的浓度水平及其综合状态;


数值范围含义

    • 0 - 50: 极好 (Excellent)

    • 51 - 100: 良好 (Good)

    • 101 - 150: 轻度污染 (Lightly polluted)

    • 151 - 200: 中度污染 (Moderately polluted)

    • 201 - 250: 重度污染 (Heavily polluted)

    • > 250: 严重污染 (Severely polluted)




2、CO2当量 (ppm)

数值范围含义


image.png



























3、气压


测量的当前环境中的大气压力值,单位是百帕 (hPa)



4、温湿度

显示当前室内的环境温度(°C)、相对湿度(%)数据;





2、实物效果



实物搭建图


模块和开发板均采用锂电池的供电方式;

采用锂电池供电的好处,可以将开发板 / 采集模块置于任何地方(仅需在蓝牙范围内即可),增加了移动和扩展性;


image-20260123214301934.png



实物效果图


整体界面:分为两部分(上 / 下);

上半部分:项目标题(室内环境监控系统);

下半部分:分为两排数据进行显示(每个数据显示:分为两大部分);


左侧为:历史数据变化趋势折线统计图【上方为历史最大值 / 下方为历史最小值】

右侧为:不同数据UI图标【下方为当前实时检测值】


第一排:显示当前的温度、湿度、CO2当量数据;

第二排:显示当前气压、IAQ数据;



当IAQ处于不同范围值时,会通过Tab5的扬声器播报不同的语音提醒音频,用于提醒用户当前的室内环境空气质量水平;



image-20260123215201369.png



主要运行流程图




流程图.png






三、代码编写





1、模块采集部分




采集模块通过BLE功能(ble_server),每秒发送采集到的相关数据到连接的BLE设备(ble_client)上;



数据示例

image-20260116224132458.png


主要相关代码

// UUID 格式
#define BLE_SENSE_UUID(ID) ("19b10000-" ID "-537e-4f6c-d104768a1214")
BLEService service(BLE_SENSE_UUID("0000"));

// 设置传感器特征值
BLEFloatCharacteristic temperatureCharacteristic(BLE_SENSE_UUID("1001"), BLERead | BLENotify);  //温度
BLEUnsignedIntCharacteristic humidityCharacteristic(BLE_SENSE_UUID("2001"), BLERead | BLENotify); //湿度
BLEFloatCharacteristic pressureCharacteristic(BLE_SENSE_UUID("3001"), BLERead | BLENotify); //气压
BLEUnsignedIntCharacteristic   co2Characteristic(BLE_SENSE_UUID("4002"), BLERead | BLENotify); //  CO2数值
BLEUnsignedIntCharacteristic bsecCharacteristic(BLE_SENSE_UUID("5001"), BLERead | BLENotify); // 空气质量指数

	...

void setup(){
	...

  //  蓝牙设备名
  name = "AirQuality-Module";

  //广播扫描时显示
  BLE.setLocalName(name.c_str());
  BLE.setDeviceName(name.c_str());
  BLE.setAdvertisedService(service);

  // 添加特征值
  service.addCharacteristic(temperatureCharacteristic); 
  service.addCharacteristic(humidityCharacteristic);
  service.addCharacteristic(pressureCharacteristic);
  service.addCharacteristic(bsecCharacteristic);
  service.addCharacteristic(co2Characteristic);

  // 断开连接事件
  BLE.setEventHandler(BLEDisconnected, blePeripheralDisconnectHandler);
  BLE.addService(service);
  // 广播
  BLE.advertise();
}

void loop(){
  BHY2.update(); 

  // 监听蓝牙设备连接
  BLEDevice central = BLE.central();
  // 有设备连接
  if (central) {
    Serial.println(central.address());
    while (central.connected()) {
      // 更新传感器数据
      BHY2.update();
      // 每秒发送一次数据
      unsigned long currentMillis = millis();
      if (currentMillis - previousMillis >= 1000) {
        previousMillis = currentMillis;
        // 连接时亮蓝灯
        nicla::leds.setColor(blue); 
        // 更新数据
        updateValues();
      }
    }
  } 
}

// 数据更新
void updateValues() {
  // 获取当前传感器数值
  float currentTemp = temperature.value();
  uint32_t currentHum = humidity.value();
  float currentPress = pressure.value() / 100.00;  // Pa 转 hPa
  uint32_t currentIAQ = bsec.iaq();
  uint32_t currentCO2 = bsec.co2_eq();
  // 写入特征值
  temperatureCharacteristic.writeValue(currentTemp);
  humidityCharacteristic.writeValue(currentHum);
  pressureCharacteristic.writeValue(currentPress);
  bsecCharacteristic.writeValue(currentIAQ);
  co2Characteristic.writeValue(currentCO2);
}




2、Tab5部分



原本是使用Arduino IDE完成相关功能的开发,但发现开发环境还存在一些问题,目前暂时无法实现;

  原因详见文末  

所以最后只好使用支持Micropython的官方工具uiflow2网页,来进行相关功能的开发了;

 相关硬件功能支持比较完善  





相关代码功能介绍


上传相关资源到板载设备的flash中(音频、字库、图像)

image-20260123222500661.png


1、存储相关的音频文件


根据不同的IAQ范围数值变化,会进行不同语音提醒

image-20260123222312356.png



2、生成的字库文件


将自定义的中文.ttf字库转换.vlw格式进行更好的UI显示适配,用于标题等内容显示


image-20260123222340353.png

image-20260123222340353

3、相关的UI界面图像

用于UI界面的图标显示


image-20260123222426308.png



主要相关功能


1、BLE功能(Ble_Central)

  • 扫描周围广播包,匹配 TARGET_UUID_ADV ;

  • 找到目标设备后自动连接;

  • 发现指定 Service / Characteristics;

  • 给各个特征的 CCCD 写 0x0001(开启订阅 Notify);

  • 接收 Notify 数据(温度/湿度/气压/CO2/IAQ);


2、UI显示

  • 固定布局:上排:温度/湿度/CO2;下排:气压/IAQ

  • 每个UI卡片:图标、当前数值、历史折线图(左侧历史最大/最小值);


3、IAQ分段语音提醒

  • 当IAQ数值在不同的范围区间时,播放对应wav音频文件;



# ============================================================
# IAQ 语音功能
# ============================================================
# 区间定义 / 相关音频文件
# (upper_bound, level_id, mp3_path)
IAQ_LEVELS = [
    (50,  0, '/flash/res/audio/iaq_excellent.wav'),
    (100, 1, '/flash/res/audio/iaq_good.wav'),
    (150, 2, '/flash/res/audio/iaq_lightly.wav'),
    (200, 3, '/flash/res/audio/iaq_moderately.wav'),
    (250, 4, '/flash/res/audio/iaq_heavily.wav'),
    (9999,5, '/flash/res/audio/iaq_severely.wav'),
]

_last_iaq_level_id = None  #只在区间变化时播放

...

def iaq_audio_on_update(iaq_value):
    global _last_iaq_level_id

    level_id, path = iaq_level_id_of(iaq_value)

    if _last_iaq_level_id is None:
        _last_iaq_level_id = level_id
    

    if level_id != _last_iaq_level_id:
        _last_iaq_level_id = level_id
        play_wav(path)


# ============================================================
# UI布局参数
# ============================================================
SCREEN_W = 1280
SCREEN_H = 720

X_PAD = 30
UI_TOP_SHIFT = 45

# 标题内容
TITLE_TEXT = "室内环境监控系统"

...

# UI 刷新
UI_REFRESH_MS = 120
CCCD_WRITE_INTERVAL_MS = 350

# ============================================================
# BLE_UUID 相关参数
# ============================================================
TARGET_UUID_ADV = bytes.fromhex("14128a7604d16c4f7e5300000000b119")

SERVICE_UUID = bluetooth.UUID("19b10000-0000-537e-4f6c-d104768a1214")
UUID_TEMP = bluetooth.UUID("19b10000-1001-537e-4f6c-d104768a1214")
UUID_HUM  = bluetooth.UUID("19b10000-2001-537e-4f6c-d104768a1214")
UUID_PRES = bluetooth.UUID("19b10000-3001-537e-4f6c-d104768a1214")
UUID_CO2  = bluetooth.UUID("19b10000-4002-537e-4f6c-d104768a1214")
UUID_IAQ  = bluetooth.UUID("19b10000-5001-537e-4f6c-d104768a1214")
CHAR_UUIDS = (UUID_TEMP, UUID_HUM, UUID_PRES, UUID_CO2, UUID_IAQ)

# IRQ constants
_IRQ_SCAN_RESULT = 5
_IRQ_SCAN_DONE = 6
_IRQ_PERIPHERAL_CONNECT = 7
_IRQ_PERIPHERAL_DISCONNECT = 8
_IRQ_GATTC_SERVICE_RESULT = 9
_IRQ_GATTC_SERVICE_DONE = 10
_IRQ_GATTC_CHARACTERISTIC_RESULT = 11
_IRQ_GATTC_CHARACTERISTIC_DONE = 12
_IRQ_GATTC_NOTIFY = 18

# ============================================================
# 数据格式布局
# ============================================================
vals = {"temp": "--", "hum": "--", "pres": "--", "co2": "--", "iaq": "--"}
_last_drawn = {"temp": None, "hum": None, "pres": None, "co2": None, "iaq": None}

history = {"temp": [], "hum": [], "pres": [], "co2": [], "iaq": []}
hist_dirty = {k: True for k in history}

def parse_float(b):
    return round(struct.unpack("<f", b)[0], 2)

def parse_u32(b):
    return int(struct.unpack("<I", b)[0])

def ticks_ms():
    try:
        return time.ticks_ms()
    except:
        return int(time.time() * 1000)

_CARD = {
    "temp": (0, 0),
    "hum":  (0, 1),
    "co2":  (0, 2),   
    "pres": (1, 0), 
    "iaq":  (1, 1),
}

CARD_W = SPARK_TEXT_W + SPARK_W + SPARK_GAP + ICON_SIZE + 14
CARD_H = max(ICON_SIZE, SPARK_H) + VALUE_BOX_H + 18

INNER_W = SCREEN_W - X_PAD * 2

def _row_start_x(card_count, gap_col):
    total = card_count * CARD_W + (card_count - 1) * gap_col
    return X_PAD + max(0, (INNER_W - total) // 2)

TOP_START_X = _row_start_x(3, GAP_COL_TOP)
BOT_START_X = _row_start_x(2, GAP_COL_BOT)

TOP_Y = GRID_TOP
BOT_Y = GRID_TOP + CARD_H + GAP_ROW

def card_xy(row, col):
    if row == 0:
        return TOP_START_X + col * (CARD_W + GAP_COL_TOP), TOP_Y
    else:
        return BOT_START_X + col * (CARD_W + GAP_COL_BOT), BOT_Y

def rects_for_metric(metric_key):
    row, col = _CARD[metric_key]
    cx, cy = card_xy(row, col)

    tx = cx
    ty = cy + 50

    sx = cx + SPARK_TEXT_W
    sy = cy + 6

    ix = sx + SPARK_W + SPARK_GAP
    iy = cy + 15

    vx = ix
    vy = cy + ICON_SIZE + 6

    return (tx, ty, SPARK_TEXT_W, SPARK_H), (sx, sy, SPARK_W, SPARK_H), (ix, iy), (vx, vy, ICON_SIZE, VALUE_BOX_H)

# ============================================================
# UI 图标数据绘制
# ============================================================
def ui_draw_title_center(text):
    x = max(X_PAD, (SCREEN_W - len(text) * TITLE_APPROX_CHAR_W) // 2)
    M5.Lcd.setTextColor(FG, BG)
    M5.Lcd.setCursor(x, TITLE_Y)
    M5.Lcd.print(text)

def ui_draw_static():
    M5.Lcd.clear(BG)
    M5.Lcd.setRotation(1)

    ui_draw_title_center(TITLE_TEXT)

    icon_paths = {
        "temp": "/flash/res/img/temp.png",
        "hum":  "/flash/res/img/hum.png",
        "pres": "/flash/res/img/pres.png",
        "co2":  "/flash/res/img/co2.png",
        "iaq":  "/flash/res/img/iaq.png",
    }

    for k in ("temp", "hum", "co2", "pres", "iaq"):
        (tx, ty, tw, th), (sx, sy, sw, sh), (ix, iy), (vx, vy, vw, vh) = rects_for_metric(k)

        if 0 <= ix <= SCREEN_W - ICON_SIZE and 0 <= iy <= SCREEN_H - ICON_SIZE:
            M5.Lcd.drawPng(icon_paths[k], ix, iy)

        M5.Lcd.fillRect(tx, ty, tw, th, BG)
        M5.Lcd.drawRect(sx, sy, sw, sh, SPARK_AXIS)

        M5.Lcd.fillRect(vx, vy, vw, vh, BG)

def _fmt_value(metric_key, v):
    if v == "--":
        return "--"
    if metric_key == "temp":
        return "{} ℃".format(v)
    if metric_key == "hum":
        return "{} %".format(v)
    if metric_key == "pres":
        return "{} hPa".format(v)
    if metric_key == "co2":
        return "{} ppm".format(v)
    return "{}".format(v)

def _fmt_minmax(metric_key, v):
    if v is None:
        return "--"
    if metric_key in ("temp", "pres"):
        return "{:.2f}".format(v)
    return "{}".format(int(v))

def ui_draw_value_if_changed(metric_key):
    if vals[metric_key] == _last_drawn[metric_key]:
        return

    (_, _, _, _), (_, _, _, _), (ix, iy), (vx, vy, vw, vh) = rects_for_metric(metric_key)
    M5.Lcd.fillRect(vx, vy, vw, vh, BG)

    txt = _fmt_value(metric_key, vals[metric_key])
    M5.Lcd.setTextColor(FG, BG)

    x = vx + max(0, (vw - len(txt) * VALUE_APPROX_CHAR_W) // 2)
    y = vy + VALUE_TEXT_Y
    M5.Lcd.setCursor(x, y)
    M5.Lcd.print(txt)

    _last_drawn[metric_key] = vals[metric_key]

def ui_draw_values_if_changed():
    for k in ("temp", "hum", "pres", "co2", "iaq"):
        ui_draw_value_if_changed(k)

def hist_add(metric_key, v):
    arr = history[metric_key]
    arr.append(v)
    if len(arr) > HIST_N:
        arr.pop(0)
    hist_dirty[metric_key] = True

def ui_draw_spark(metric_key):
    (tx, ty, tw, th), (sx, sy, sw, sh), (ix, iy), (vx, vy, vw, vh) = rects_for_metric(metric_key)

    M5.Lcd.fillRect(tx, ty, tw, th, BG)
    M5.Lcd.fillRect(sx + 1, sy + 1, sw - 2, sh - 2, BG)
    M5.Lcd.drawRect(sx, sy, sw, sh, SPARK_AXIS)

    arr = history[metric_key]
    M5.Lcd.setTextColor(SPARK_TEXT, BG)

    if len(arr) == 0:
        M5.Lcd.setCursor(tx, ty -85);        M5.Lcd.print("-")
        M5.Lcd.setCursor(tx, ty + sh -30);  M5.Lcd.print("-")
        return

    mn = min(arr)
    mx = max(arr)
    if mx == mn:
        mx = mn + 1

    M5.Lcd.setCursor(tx, ty -85)
    M5.Lcd.print(_fmt_minmax(metric_key, max(arr)))
    M5.Lcd.setCursor(tx, ty + sh -30)
    M5.Lcd.print(_fmt_minmax(metric_key, min(arr)))

    if len(arr) < 2:
        return

    n = len(arr)
    x0 = sx + 1
    y0 = sy + sh - 2

    prev_px = None
    prev_py = None
    for i, v in enumerate(arr):
        px = x0 + (i * (sw - 2)) // (n - 1)
        py = y0 - int((v - mn) * (sh - 3) / (mx - mn))
        if prev_px is not None:
            M5.Lcd.drawLine(prev_px, prev_py, px, py, SPARK_COLOR)
        prev_px, prev_py = px, py

def ui_draw_sparks_if_dirty():
    for k in ("temp", "hum", "pres", "co2", "iaq"):
        if hist_dirty[k]:
            ui_draw_spark(k)
            hist_dirty[k] = False

# ============================================================
# BLE 功能
# ============================================================
ble = bluetooth.BLE()
ble.active(True)

target_addr_type = None
target_addr = None
should_connect = False
scan_done = False

conn_handle = None
connected = False
svc_start = None
svc_end = None

value_handle_by_uuid = {}

pending_cccd_writes = []
subscribed = False

def reset_state():
    global target_addr_type, target_addr, should_connect, scan_done
    global conn_handle, connected, svc_start, svc_end
    global value_handle_by_uuid, pending_cccd_writes, subscribed

    target_addr_type = None
    target_addr = None
    should_connect = False
    scan_done = False

    conn_handle = None
    connected = False
    svc_start = None
    svc_end = None

    value_handle_by_uuid = {}
    pending_cccd_writes = []
    subscribed = False

def adv_has_target(payload):
    i = 0
    n = len(payload)
    while i + 1 < n:
        ln = payload[i]
        if ln == 0:
            break
        t = payload[i + 1]
        if t in (0x06, 0x07):
            ad = bytes(payload[i + 2 : i + 1 + ln])
            return (TARGET_UUID_ADV in ad)
        i += 1 + ln
    return False

def start_scan():
    gc.collect()
    reset_state()
    ble.gap_scan(6000, 60000, 30000)

def irq(event, data):
    global target_addr_type, target_addr, should_connect, scan_done
    global conn_handle, connected, svc_start, svc_end
    global pending_cccd_writes, subscribed

    try:
        if event == _IRQ_SCAN_RESULT:
            addr_type, addr, adv_type, rssi, payload = data
            if (not should_connect) and adv_has_target(payload):
                target_addr_type = addr_type
                target_addr = bytes(addr)
                should_connect = True
                ble.gap_scan(None)

        elif event == _IRQ_SCAN_DONE:
            scan_done = True

        elif event == _IRQ_PERIPHERAL_CONNECT:
            ch, addr_type, addr = data
            conn_handle = ch
            connected = True
            ble.gattc_discover_services(conn_handle)

        elif event == _IRQ_PERIPHERAL_DISCONNECT:
            connected = False
            subscribed = False
            should_connect = True

        elif event == _IRQ_GATTC_SERVICE_RESULT:
            ch, start, end, uuid = data
            if uuid == SERVICE_UUID:
                svc_start = start
                svc_end = end

        elif event == _IRQ_GATTC_SERVICE_DONE:
            if svc_start is not None:
                ble.gattc_discover_characteristics(conn_handle, svc_start, svc_end)

        elif event == _IRQ_GATTC_CHARACTERISTIC_RESULT:
            ch, def_h, val_h, props, uuid = data
            for u in CHAR_UUIDS:
                if uuid == u:
                    value_handle_by_uuid[u] = val_h

        elif event == _IRQ_GATTC_CHARACTERISTIC_DONE:
            pending_cccd_writes = []
            for u in (UUID_TEMP, UUID_HUM, UUID_PRES, UUID_IAQ, UUID_CO2):
                vh = value_handle_by_uuid.get(u)
                if vh is None:
                    continue
                pending_cccd_writes.append(vh + 1)
            subscribed = False

        elif event == _IRQ_GATTC_NOTIFY:
            ch, value_handle, notify_data = data
            b = bytes(notify_data)

            if value_handle == value_handle_by_uuid.get(UUID_TEMP) and len(b) >= 4:
                v = parse_float(b[:4])
                vals["temp"] = v
                hist_add("temp", v)

            elif value_handle == value_handle_by_uuid.get(UUID_HUM) and len(b) >= 4:
                v = parse_u32(b[:4])
                vals["hum"] = v
                hist_add("hum", v)

            elif value_handle == value_handle_by_uuid.get(UUID_PRES) and len(b) >= 4:
                v = parse_float(b[:4])
                vals["pres"] = v
                hist_add("pres", v)

            elif value_handle == value_handle_by_uuid.get(UUID_CO2) and len(b) >= 4:
                v = parse_u32(b[:4])
                vals["co2"] = v
                hist_add("co2", v)
            elif value_handle == value_handle_by_uuid.get(UUID_IAQ) and len(b) >= 4:
                v = parse_u32(b[:4])
                print("IAQ RAW:", v, "bytes:", b[:4])
                vals["iaq"] = v
                hist_add("iaq", v)
                iaq_audio_on_update(v)    

    except Exception as e:
        print("IRQ error:", e)

ble.irq(irq)

# ============================================================
# main
# ============================================================
M5.begin()
#加载字库
M5.Lcd.loadFont("/flash/res/font/font.vlw") 
#扬声器初始化
speaker_init()
#UI界面初始化
ui_draw_static()
ui_draw_values_if_changed()
ui_draw_sparks_if_dirty()
#开启BLE扫描
start_scan()

last_ui_refresh_ms = 0
last_cccd_write_ms = 0

while True:
    # 连接目标设备
    if should_connect and (not connected) and target_addr is not None:
        try:
            ble.gap_connect(target_addr_type, target_addr)
        except Exception as e:
            print("connect failed:", e)
            start_scan()
        should_connect = False
    # 开启订阅
    if connected and (not subscribed) and pending_cccd_writes:
        now = ticks_ms()
        if now - last_cccd_write_ms > CCCD_WRITE_INTERVAL_MS:
            cccd = pending_cccd_writes.pop(0)
            gc.collect()
            try:
                ble.gattc_write(conn_handle, cccd, b"\x01\x00", 1)
            except Exception:
                pending_cccd_writes.append(cccd)
            last_cccd_write_ms = now

        if not pending_cccd_writes:
            subscribed = True
    # 断开重新连接
    if scan_done and (not connected) and (target_addr is None) and (not should_connect):
        time.sleep(0.3)
        start_scan()

    # UI 刷新显示
    now = ticks_ms()
    if now - last_ui_refresh_ms > UI_REFRESH_MS:
        ui_draw_values_if_changed()
        ui_draw_sparks_if_dirty()
        last_ui_refresh_ms = now

    time.sleep(0.02)





四、程序烧录





1、连接USB数据线至开发板;

2、选择端口号对应的开发板;

3、点击 下载 烧录程序到开发板上;



image-20260123233914953.png


image-20260123233914953





五、效果演示




效果演示


项目.gif







六、总结


踩坑记录:使用Arduino IDE开发:

Tab5的M5官方库的所有BLE例程目前全部都无法使用,发现官方的库并没有实现蓝牙相关功能的API接口;

然后打算使用第三方NimBLE库进行开发,却发现还是无法使用;

最后查看 esp-hosted-mcu的功能实现,才发现目前关于BLE功能的相关Issues还没有解决 / 更新;


Tab5是 P4+C6(SDIO)方式进行通讯的,通过【esp-hosted-mcu】功能实现;



image-20260119155250599.png


image-20260119155250599

所以目前只是适配了P4的WIFI功能,BLE功能目前还没有适配(可能要在下个版本ESP32Core_3.3.6+后才开始适配)


image-20260119161401263.png




image-20260124214535503

image-20260124214535503.png



而且目前官方M5Stack的esp32Core依赖还是3.2.5的版本;


所以目前硬件功能支持比较好的,还是使用Micropython固件进行开发了


image-20260119200452820.png










关键词: M5STACK TAB5    

共1条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]