diff --git a/Apps/M5UnitTest/main/Source/GroveLookup.cpp b/Apps/M5UnitTest/main/Source/GroveLookup.cpp new file mode 100644 index 0000000..aac9850 --- /dev/null +++ b/Apps/M5UnitTest/main/Source/GroveLookup.cpp @@ -0,0 +1,45 @@ +#include "GroveLookup.h" +#include +#include + +namespace { + +struct GroveChildSearch { + const DeviceType* childType; + Device* result; +}; + +bool onGroveDevice(Device* groveDevice, void* context) { + auto* search = static_cast(context); + + GroveMode mode; + if (grove_get_mode(groveDevice, &mode) != ERROR_NONE) return true; + if (mode != (search->childType == &I2C_CONTROLLER_TYPE ? GROVE_MODE_I2C : GROVE_MODE_UART)) return true; + + device_for_each_child(groveDevice, search, [](Device* child, void* ctx) -> bool { + auto* s = static_cast(ctx); + if (device_get_type(child) == s->childType && device_is_ready(child)) { + s->result = child; + return false; // stop - found it + } + return true; + }); + + return search->result == nullptr; // stop outer iteration once found +} + +Device* findGroveChild(const DeviceType* childType) { + GroveChildSearch search{childType, nullptr}; + device_for_each_of_type(&GROVE_TYPE, &search, onGroveDevice); + return search.result; +} + +} // namespace + +Device* findGroveI2cDevice() { + return findGroveChild(&I2C_CONTROLLER_TYPE); +} + +Device* findGroveUartDevice() { + return findGroveChild(&UART_CONTROLLER_TYPE); +} diff --git a/Apps/M5UnitTest/main/Source/GroveLookup.h b/Apps/M5UnitTest/main/Source/GroveLookup.h new file mode 100644 index 0000000..fd9cbb1 --- /dev/null +++ b/Apps/M5UnitTest/main/Source/GroveLookup.h @@ -0,0 +1,12 @@ +#pragma once + +#include +#include + +// Finds the I2C controller device exposed by the first grove port currently +// in GROVE_MODE_I2C, by checking each grove port's child device type +// (not by name - grove port names are not guaranteed, e.g. "port_a"). +Device* findGroveI2cDevice(); + +// Same as findGroveI2cDevice() but for UART mode. +Device* findGroveUartDevice(); diff --git a/Apps/M5UnitTest/main/Source/TestUnit8Encoder.cpp b/Apps/M5UnitTest/main/Source/TestUnit8Encoder.cpp index b05aefe..8975076 100644 --- a/Apps/M5UnitTest/main/Source/TestUnit8Encoder.cpp +++ b/Apps/M5UnitTest/main/Source/TestUnit8Encoder.cpp @@ -1,4 +1,5 @@ #include "TestUnit8Encoder.h" +#include "GroveLookup.h" #include "UiScale.h" #include #include @@ -93,9 +94,9 @@ void TestUnit8Encoder::onStart(lv_obj_t* parent, AppHandle handle, M5UnitTest* a lv_obj_set_flex_grow(lblSwitch_, 1); dotSwitch_ = makeDot(swCard); - Device* i2c = device_find_by_name("i2c1"); + Device* i2c = findGroveI2cDevice(); if (!i2c) { - lv_label_set_text(lblStatus_, "i2c1 not found"); + lv_label_set_text(lblStatus_, "grove0_i2c not found"); return; } diff --git a/Apps/M5UnitTest/main/Source/TestUnitByteButton.cpp b/Apps/M5UnitTest/main/Source/TestUnitByteButton.cpp index 8017bcd..6e82f15 100644 --- a/Apps/M5UnitTest/main/Source/TestUnitByteButton.cpp +++ b/Apps/M5UnitTest/main/Source/TestUnitByteButton.cpp @@ -1,4 +1,5 @@ #include "TestUnitByteButton.h" +#include "GroveLookup.h" #include "UiScale.h" #include #include @@ -56,10 +57,10 @@ void TestUnitByteButton::onStart(lv_obj_t* parent, AppHandle handle, M5UnitTest* lv_obj_set_style_text_font(hint, lvgl_get_text_font(FONT_SIZE_SMALL), 0); lv_label_set_text(hint, "Press buttons - LEDs toggle"); - Device* i2c = device_find_by_name("i2c1"); + Device* i2c = findGroveI2cDevice(); if (!i2c) { for (int i = 0; i < BTN_COUNT; i++) lv_obj_set_style_bg_color(indicators_[i], lv_color_hex(COLOR_ERROR), 0); - lv_label_set_text(hint, "i2c1 not found"); + lv_label_set_text(hint, "grove0_i2c not found"); return; } diff --git a/Apps/M5UnitTest/main/Source/TestUnitCardKB2.cpp b/Apps/M5UnitTest/main/Source/TestUnitCardKB2.cpp index 1319dc7..7ffb220 100644 --- a/Apps/M5UnitTest/main/Source/TestUnitCardKB2.cpp +++ b/Apps/M5UnitTest/main/Source/TestUnitCardKB2.cpp @@ -1,4 +1,5 @@ #include "TestUnitCardKB2.h" +#include "GroveLookup.h" #include "UiScale.h" #include #include @@ -116,10 +117,10 @@ void TestUnitCardKB2::connectI2C() { lv_obj_delete(connectOverlay_); connectOverlay_ = nullptr; - Device* i2c = device_find_by_name("i2c1"); + Device* i2c = findGroveI2cDevice(); if (!i2c) { buildMainUI(); - lv_label_set_text(lblHistory_, "i2c1 not found"); + lv_label_set_text(lblHistory_, "grove0_i2c not found"); return; } @@ -145,10 +146,10 @@ void TestUnitCardKB2::connectUart() { lv_obj_delete(connectOverlay_); connectOverlay_ = nullptr; - Device* uart = device_find_by_name("uart1"); + Device* uart = findGroveUartDevice(); buildMainUI(); if (!uart) { - lv_label_set_text(lblHistory_, "uart1 not found"); + lv_label_set_text(lblHistory_, "grove0_uart not found"); return; } if (!unit_.beginUart(uart)) { diff --git a/Apps/M5UnitTest/main/Source/TestUnitJoystick2.cpp b/Apps/M5UnitTest/main/Source/TestUnitJoystick2.cpp index d29525e..561d8d9 100644 --- a/Apps/M5UnitTest/main/Source/TestUnitJoystick2.cpp +++ b/Apps/M5UnitTest/main/Source/TestUnitJoystick2.cpp @@ -1,4 +1,5 @@ #include "TestUnitJoystick2.h" +#include "GroveLookup.h" #include "UiScale.h" #include #include @@ -56,9 +57,9 @@ void TestUnitJoystick2::onStart(lv_obj_t* parent, AppHandle handle, M5UnitTest* lv_obj_set_style_border_width(dot_, 0, 0); lv_obj_set_pos(dot_, (JOY_AREA - DOT_SIZE) / 2, (JOY_AREA - DOT_SIZE) / 2); - Device* i2c = device_find_by_name("i2c1"); + Device* i2c = findGroveI2cDevice(); if (!i2c) { - lv_label_set_text(lblXY_, "i2c1 not found"); + lv_label_set_text(lblXY_, "grove0_i2c not found"); return; } diff --git a/Apps/M5UnitTest/main/Source/TestUnitLcd.cpp b/Apps/M5UnitTest/main/Source/TestUnitLcd.cpp index 37f454b..35c4b53 100644 --- a/Apps/M5UnitTest/main/Source/TestUnitLcd.cpp +++ b/Apps/M5UnitTest/main/Source/TestUnitLcd.cpp @@ -1,4 +1,5 @@ #include "TestUnitLcd.h" +#include "GroveLookup.h" #include "UiScale.h" #include #include @@ -87,9 +88,9 @@ void TestUnitLcd::onStart(lv_obj_t* parent, AppHandle handle, M5UnitTest* app) { lv_label_set_text(lText, "Write Text"); lv_obj_set_style_text_font(lText, fnt, 0); - Device* i2c = device_find_by_name("i2c1"); + Device* i2c = findGroveI2cDevice(); if (!i2c) { - lv_label_set_text(lblStatus_, "i2c1 not found"); + lv_label_set_text(lblStatus_, "grove0_i2c not found"); return; } diff --git a/Apps/M5UnitTest/main/Source/TestUnitLcdGfx.cpp b/Apps/M5UnitTest/main/Source/TestUnitLcdGfx.cpp index 0460153..b02b1ff 100644 --- a/Apps/M5UnitTest/main/Source/TestUnitLcdGfx.cpp +++ b/Apps/M5UnitTest/main/Source/TestUnitLcdGfx.cpp @@ -1,4 +1,5 @@ #include "TestUnitLcdGfx.h" +#include "GroveLookup.h" #include "UiScale.h" #include #include @@ -65,8 +66,8 @@ void TestUnitLcdGfx::onStart(lv_obj_t* parent, AppHandle handle, M5UnitTest* app lv_obj_set_width(lblLog_, LV_PCT(100)); lv_label_set_text(lblLog_, ""); - Device* i2c = device_find_by_name("i2c1"); - if (!i2c) { lv_label_set_text(lblPhase_, "i2c1 not found"); return; } + Device* i2c = findGroveI2cDevice(); + if (!i2c) { lv_label_set_text(lblPhase_, "grove0_i2c not found"); return; } if (lcd_.begin(i2c)) { usingPaHub_ = false; diff --git a/Apps/M5UnitTest/main/Source/TestUnitMidi.cpp b/Apps/M5UnitTest/main/Source/TestUnitMidi.cpp index 677e723..672ae0c 100644 --- a/Apps/M5UnitTest/main/Source/TestUnitMidi.cpp +++ b/Apps/M5UnitTest/main/Source/TestUnitMidi.cpp @@ -1,4 +1,5 @@ #include "TestUnitMidi.h" +#include "GroveLookup.h" #include "UiScale.h" #include #include @@ -84,7 +85,7 @@ void TestUnitMidi::onStart(lv_obj_t* parent, AppHandle handle, M5UnitTest* app) lv_obj_t* lOff = lv_label_create(btnOff); lv_label_set_text(lOff, "Note Off"); lv_obj_set_style_text_font(lOff, fntS, 0); - Device* uart = device_find_by_name(UART_DEVICE); + Device* uart = findGroveUartDevice(); if (!uart || !device_is_ready(uart) || !unit_.begin(uart)) { lv_label_set_text(lblStatus_, "MIDI UART not available"); updateLabels(); diff --git a/Apps/M5UnitTest/main/Source/TestUnitMidi.h b/Apps/M5UnitTest/main/Source/TestUnitMidi.h index ee44d18..5bc513f 100644 --- a/Apps/M5UnitTest/main/Source/TestUnitMidi.h +++ b/Apps/M5UnitTest/main/Source/TestUnitMidi.h @@ -17,9 +17,6 @@ class TestUnitMidi final : public TestViewBase { uint8_t note_ = 60; // middle C bool notePlaying_= false; - // UART device name for MIDI unit (adjust to match your board wiring) - static constexpr const char* UART_DEVICE = "uart1"; - static void onNoteOnClicked(lv_event_t* e); static void onNoteOffClicked(lv_event_t* e); static void onChUp(lv_event_t* e); diff --git a/Apps/M5UnitTest/main/Source/TestUnitPaHub.cpp b/Apps/M5UnitTest/main/Source/TestUnitPaHub.cpp index ef26269..8ae9a11 100644 --- a/Apps/M5UnitTest/main/Source/TestUnitPaHub.cpp +++ b/Apps/M5UnitTest/main/Source/TestUnitPaHub.cpp @@ -1,4 +1,5 @@ #include "TestUnitPaHub.h" +#include "GroveLookup.h" #include "UiScale.h" #include #include @@ -60,7 +61,7 @@ void TestUnitPaHub::onStart(lv_obj_t* parent, AppHandle handle, M5UnitTest* app) lv_label_set_text_fmt(lblCh_[i], "CH%d: -", i); } - Device* i2c = device_find_by_name("i2c1"); + Device* i2c = findGroveI2cDevice(); if (!i2c || !hub_.begin(i2c)) { lv_label_set_text(lblStatus_, "PaHub not found"); return; @@ -95,16 +96,16 @@ void TestUnitPaHub::probeSelected() { hub_.select((uint8_t)selChannel_); - // Scan I2C addresses 0x08-0x77 on this channel - Device* i2c = device_find_by_name("i2c1"); + // Probe known unit addresses only (full-range scan can wedge the ESP-IDF i2c_master bus FSM) + Device* i2c = findGroveI2cDevice(); if (!i2c) { - lv_label_set_text_fmt(lblCh_[selChannel_], "CH%d: i2c1 not found", selChannel_); + lv_label_set_text_fmt(lblCh_[selChannel_], "CH%d: grove0_i2c not found", selChannel_); hub_.deselect(); return; } char found[256] = "Found: "; bool any = false; - for (uint8_t addr = 0x08; addr < 0x78; addr++) { + for (uint8_t addr : KNOWN_UNIT_ADDRS) { if (i2c_controller_has_device_at_address(i2c, addr, pdMS_TO_TICKS(10)) == ERROR_NONE) { size_t remaining = sizeof(found) - strlen(found) - 1; diff --git a/Apps/M5UnitTest/main/Source/TestUnitRfid2.cpp b/Apps/M5UnitTest/main/Source/TestUnitRfid2.cpp index a9bd17b..2586d0a 100644 --- a/Apps/M5UnitTest/main/Source/TestUnitRfid2.cpp +++ b/Apps/M5UnitTest/main/Source/TestUnitRfid2.cpp @@ -1,4 +1,5 @@ #include "TestUnitRfid2.h" +#include "GroveLookup.h" #include "UiScale.h" #include #include @@ -101,7 +102,7 @@ void TestUnitRfid2::onStart(lv_obj_t* parent, AppHandle handle, M5UnitTest* app) lv_label_set_text(btnLbl, "Clear"); // ── Device discovery ───────────────────────────────────────────────────── - Device* i2c = device_find_by_name("i2c1"); + Device* i2c = findGroveI2cDevice(); if (!i2c) return; if (unit_.begin(i2c)) { diff --git a/Apps/M5UnitTest/main/Source/TestUnitScroll.cpp b/Apps/M5UnitTest/main/Source/TestUnitScroll.cpp index eccacf8..e057636 100644 --- a/Apps/M5UnitTest/main/Source/TestUnitScroll.cpp +++ b/Apps/M5UnitTest/main/Source/TestUnitScroll.cpp @@ -1,4 +1,5 @@ #include "TestUnitScroll.h" +#include "GroveLookup.h" #include "UiScale.h" #include #include @@ -57,9 +58,9 @@ void TestUnitScroll::onStart(lv_obj_t* parent, AppHandle handle, M5UnitTest* app sliderB_ = makeSlider("B"); lv_label_set_text(lblLed_, "LED: #000000"); - Device* i2c = device_find_by_name("i2c1"); + Device* i2c = findGroveI2cDevice(); if (!i2c) { - lv_label_set_text(lblCounter_, "i2c1 not found"); + lv_label_set_text(lblCounter_, "grove0_i2c not found"); return; } diff --git a/Libraries/M5UnitModules/Include/UnitCommon.h b/Libraries/M5UnitModules/Include/UnitCommon.h index b701e5b..374d20e 100644 --- a/Libraries/M5UnitModules/Include/UnitCommon.h +++ b/Libraries/M5UnitModules/Include/UnitCommon.h @@ -21,6 +21,20 @@ static constexpr uint32_t UNIT_I2C_PROBE_TIMEOUT_MS = 10; // shorter timeout fo static constexpr uint32_t UNIT_STM32_READ_DELAY_MS = 2; // STM32 needs STOP + delay before read static constexpr uint16_t UNIT_MAX_WRITE_PAYLOAD = 32; // max bytes in a single I2C write +// I2C addresses of the unit modules this library can drive (excludes PaHub itself +// and UART-only units like MIDI). Used by PaHub channel probing instead of a full +// 0x08-0x77 bus scan: the ESP-IDF i2c_master driver's probe op can wedge the bus +// FSM after heavy NACK traffic, so scanning only known addresses avoids that. +static constexpr uint8_t KNOWN_UNIT_ADDRS[] = { + 0x28, // UnitRfid2 + 0x3E, // UnitLcd + 0x40, // UnitScroll + 0x41, // Unit8Encoder + 0x47, // UnitByteButton + 0x5F, // UnitCardKB2 + 0x63, // UnitJoystick2 +}; + // Probe whether a device exists at the given address on the given I2C bus. inline bool unitProbe(Device* dev, uint8_t addr) { return i2c_controller_has_device_at_address(