Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions Apps/M5UnitTest/main/Source/GroveLookup.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#include "GroveLookup.h"
#include <tactility/drivers/i2c_controller.h>
#include <tactility/drivers/uart_controller.h>

namespace {

struct GroveChildSearch {
const DeviceType* childType;
Device* result;
};

bool onGroveDevice(Device* groveDevice, void* context) {
auto* search = static_cast<GroveChildSearch*>(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<GroveChildSearch*>(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);
}
12 changes: 12 additions & 0 deletions Apps/M5UnitTest/main/Source/GroveLookup.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#pragma once

#include <tactility/device.h>
#include <tactility/drivers/grove.h>

// 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();
5 changes: 3 additions & 2 deletions Apps/M5UnitTest/main/Source/TestUnit8Encoder.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include "TestUnit8Encoder.h"
#include "GroveLookup.h"
#include "UiScale.h"
#include <tactility/device.h>
#include <tactility/lvgl_fonts.h>
Expand Down Expand Up @@ -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;
}

Expand Down
5 changes: 3 additions & 2 deletions Apps/M5UnitTest/main/Source/TestUnitByteButton.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include "TestUnitByteButton.h"
#include "GroveLookup.h"
#include "UiScale.h"
#include <tactility/device.h>
#include <tactility/lvgl_fonts.h>
Expand Down Expand Up @@ -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;
}

Expand Down
9 changes: 5 additions & 4 deletions Apps/M5UnitTest/main/Source/TestUnitCardKB2.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include "TestUnitCardKB2.h"
#include "GroveLookup.h"
#include "UiScale.h"
#include <tactility/device.h>
#include <tactility/drivers/uart_controller.h>
Expand Down Expand Up @@ -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;
}

Expand All @@ -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)) {
Expand Down
5 changes: 3 additions & 2 deletions Apps/M5UnitTest/main/Source/TestUnitJoystick2.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include "TestUnitJoystick2.h"
#include "GroveLookup.h"
#include "UiScale.h"
#include <tactility/device.h>
#include <tactility/lvgl_fonts.h>
Expand Down Expand Up @@ -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;
}

Expand Down
5 changes: 3 additions & 2 deletions Apps/M5UnitTest/main/Source/TestUnitLcd.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include "TestUnitLcd.h"
#include "GroveLookup.h"
#include "UiScale.h"
#include <tactility/device.h>
#include <tactility/lvgl_fonts.h>
Expand Down Expand Up @@ -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;
}

Expand Down
5 changes: 3 additions & 2 deletions Apps/M5UnitTest/main/Source/TestUnitLcdGfx.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include "TestUnitLcdGfx.h"
#include "GroveLookup.h"
#include "UiScale.h"
#include <tactility/device.h>
#include <tactility/lvgl_fonts.h>
Expand Down Expand Up @@ -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;
Expand Down
3 changes: 2 additions & 1 deletion Apps/M5UnitTest/main/Source/TestUnitMidi.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include "TestUnitMidi.h"
#include "GroveLookup.h"
#include "UiScale.h"
#include <tactility/device.h>
#include <tactility/lvgl_fonts.h>
Expand Down Expand Up @@ -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();
Expand Down
3 changes: 0 additions & 3 deletions Apps/M5UnitTest/main/Source/TestUnitMidi.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
11 changes: 6 additions & 5 deletions Apps/M5UnitTest/main/Source/TestUnitPaHub.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include "TestUnitPaHub.h"
#include "GroveLookup.h"
#include "UiScale.h"
#include <tactility/device.h>
#include <tactility/drivers/i2c_controller.h>
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
3 changes: 2 additions & 1 deletion Apps/M5UnitTest/main/Source/TestUnitRfid2.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include "TestUnitRfid2.h"
#include "GroveLookup.h"
#include "UiScale.h"
#include <tactility/device.h>
#include <tactility/lvgl_fonts.h>
Expand Down Expand Up @@ -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)) {
Expand Down
5 changes: 3 additions & 2 deletions Apps/M5UnitTest/main/Source/TestUnitScroll.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include "TestUnitScroll.h"
#include "GroveLookup.h"
#include "UiScale.h"
#include <tactility/device.h>
#include <tactility/lvgl_fonts.h>
Expand Down Expand Up @@ -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;
}

Expand Down
14 changes: 14 additions & 0 deletions Libraries/M5UnitModules/Include/UnitCommon.h
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
Loading