Grove driver, I2C migrations, bugfixes and docs (#532)

This commit is contained in:
Ken Van Hoeylandt
2026-06-19 00:52:31 +02:00
committed by GitHub
parent 8dabda2b5b
commit a35c88c8fd
74 changed files with 937 additions and 483 deletions
@@ -0,0 +1,79 @@
#include <vector>
#include <lvgl.h>
#include <tactility/device.h>
#include <tactility/drivers/grove.h>
#include <tactility/lvgl_icon_shared.h>
#include <Tactility/Tactility.h>
#include <Tactility/lvgl/Toolbar.h>
namespace tt::app::grovesettings {
class GroveSettingsApp final : public App {
std::vector<::Device*> devices;
void collectDevices() {
devices.clear();
device_for_each_of_type(&GROVE_TYPE, &devices, [](auto* device, auto* context) {
auto* vec = static_cast<std::vector<::Device*>*>(context);
vec->push_back(device);
return true;
});
}
static void onModeChanged(lv_event_t* e) {
auto* device = static_cast<::Device*>(lv_event_get_user_data(e));
auto* dropdown = static_cast<lv_obj_t*>(lv_event_get_target(e));
auto mode = static_cast<GroveMode>(lv_dropdown_get_selected(dropdown));
grove_set_mode(device, mode);
}
public:
void onShow(AppContext& app, lv_obj_t* parent) override {
collectDevices();
lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN);
lv_obj_set_style_pad_row(parent, 0, LV_STATE_DEFAULT);
lvgl::toolbar_create(parent, app);
auto* main_wrapper = lv_obj_create(parent);
lv_obj_set_flex_flow(main_wrapper, LV_FLEX_FLOW_COLUMN);
lv_obj_set_width(main_wrapper, LV_PCT(100));
lv_obj_set_flex_grow(main_wrapper, 1);
for (auto* device : devices) {
auto* row = lv_obj_create(main_wrapper);
lv_obj_set_size(row, LV_PCT(100), LV_SIZE_CONTENT);
lv_obj_set_style_pad_all(row, 0, LV_STATE_DEFAULT);
lv_obj_set_style_border_width(row, 0, LV_STATE_DEFAULT);
auto* label = lv_label_create(row);
lv_label_set_text(label, device->name);
lv_obj_align(label, LV_ALIGN_LEFT_MID, 0, 0);
auto* dropdown = lv_dropdown_create(row);
lv_dropdown_set_options(dropdown, "Disabled\nUART\nI2C");
lv_obj_align(dropdown, LV_ALIGN_RIGHT_MID, 0, 0);
GroveMode current = GROVE_MODE_DISABLED;
grove_get_mode(device, &current);
lv_dropdown_set_selected(dropdown, static_cast<uint32_t>(current));
lv_obj_add_event_cb(dropdown, onModeChanged, LV_EVENT_VALUE_CHANGED, device);
}
}
};
extern const AppManifest manifest = {
.appId = "GroveSettings",
.appName = "Grove",
.appIcon = LVGL_ICON_SHARED_CABLE,
.appCategory = Category::Settings,
.createApp = create<GroveSettingsApp>
};
}
+26 -20
View File
@@ -1,7 +1,8 @@
#include "Tactility/app/i2cscanner/I2cHelpers.h"
#include <Tactility/StringUtils.h>
#include <Tactility/hal/i2c/I2c.h>
#include <tactility/drivers/i2c_controller.h>
#include <iomanip>
#include <vector>
@@ -19,31 +20,36 @@ std::string getAddressText(uint8_t address) {
std::string getPortNamesForDropdown() {
std::vector<std::string> config_names;
for (int port = 0; port < I2C_NUM_MAX; ++port) {
auto native_port = static_cast<i2c_port_t>(port);
if (hal::i2c::isStarted(native_port)) {
auto* name = hal::i2c::getName(native_port);
if (name != nullptr) {
config_names.push_back(name);
}
device_for_each_of_type(&I2C_CONTROLLER_TYPE, &config_names, [](auto* device, auto* context) {
auto* names = static_cast<std::vector<std::string>*>(context);
if (device_is_ready(device)) {
names->push_back(device->name);
}
}
return true;
});
return string::join(config_names, "\n");
}
bool getActivePortAtIndex(int32_t index, int32_t& out) {
int current_index = -1;
for (int port = 0; port < I2C_NUM_MAX; ++port) {
auto native_port = static_cast<i2c_port_t>(port);
if (hal::i2c::isStarted(native_port)) {
current_index++;
if (current_index == index) {
out = port;
return true;
bool getActivePortAtIndex(int32_t index, struct Device** out) {
struct Context {
int32_t targetIndex;
int32_t currentIndex;
struct Device* foundDevice;
} context = { index, 0, nullptr };
device_for_each_of_type(&I2C_CONTROLLER_TYPE, &context, [](auto* device, auto* ctx) {
auto* c = static_cast<Context*>(ctx);
if (device_is_ready(device)) {
if (c->currentIndex == c->targetIndex) {
c->foundDevice = device;
return false;
}
c->currentIndex++;
}
}
return false;
return true;
});
*out = context.foundDevice;
return context.foundDevice != nullptr;
}
}
+16 -34
View File
@@ -1,9 +1,8 @@
#include <Tactility/app/i2cscanner/I2cScannerPrivate.h>
#include <Tactility/app/i2cscanner/I2cHelpers.h>
#include <Tactility/Assets.h>
#include <Tactility/app/AppContext.h>
#include <Tactility/hal/i2c/I2cDevice.h>
#include <tactility/drivers/i2c_controller.h>
#include <Tactility/Logger.h>
#include <Tactility/LogMessages.h>
#include <Tactility/lvgl/LvglSync.h>
@@ -33,7 +32,7 @@ class I2cScannerApp final : public App {
std::unique_ptr<Timer> scanTimer = nullptr;
// State
ScanState scanState = ScanStateInitial;
i2c_port_t port = I2C_NUM_0;
struct Device* portDevice = nullptr;
std::vector<uint8_t> scannedAddresses;
// Widgets
lv_obj_t* scanButtonLabelWidget = nullptr;
@@ -47,14 +46,13 @@ class I2cScannerApp final : public App {
static void onSelectBusCallback(lv_event_t* event);
static void onPressScanCallback(lv_event_t* event);
static void onScanTimerCallback();
void onSelectBus(lv_event_t* event);
void onPressScan(lv_event_t* event);
void onScanTimer();
bool shouldStopScanTimer();
bool getPort(i2c_port_t* outPort);
bool getPort(struct Device** outPort);
bool addAddressToList(uint8_t address);
bool hasScanThread();
void startScanning();
@@ -140,8 +138,10 @@ void I2cScannerApp::onShow(AppContext& app, lv_obj_t* parent) {
lv_obj_add_flag(scan_list, LV_OBJ_FLAG_HIDDEN);
scanListWidget = scan_list;
int32_t first_port;
if (getActivePortAtIndex(0, first_port)) {
struct Device* dummy;
if (getActivePortAtIndex(selected_bus, &dummy)) {
selectBus(selected_bus);
} else if (getActivePortAtIndex(0, &dummy)) {
lv_dropdown_set_selected(port_dropdown, 0);
selectBus(0);
}
@@ -184,9 +184,9 @@ void I2cScannerApp::onPressScanCallback(lv_event_t* event) {
// endregion Callbacks
bool I2cScannerApp::getPort(i2c_port_t* outPort) {
bool I2cScannerApp::getPort(struct Device** outPort) {
if (mutex.lock(100 / portTICK_PERIOD_MS)) {
*outPort = this->port;
*outPort = this->portDevice;
mutex.unlock();
return true;
} else {
@@ -219,21 +219,21 @@ bool I2cScannerApp::shouldStopScanTimer() {
void I2cScannerApp::onScanTimer() {
logger.info("Scan thread started");
i2c_port_t safe_port;
Device* safe_port;
if (!getPort(&safe_port)) {
logger.error("Failed to get I2C port");
onScanTimerFinished();
return;
}
if (!hal::i2c::isStarted(safe_port)) {
if (!device_is_ready(safe_port)) {
logger.error("I2C port not started");
onScanTimerFinished();
return;
}
for (uint8_t address = 0; address < 128; ++address) {
if (hal::i2c::masterHasDeviceAtAddress(safe_port, address, 10 / portTICK_PERIOD_MS)) {
if (i2c_controller_has_device_at_address(safe_port, address, 10 / portTICK_PERIOD_MS) == ERROR_NONE) {
logger.info("Found device at address 0x{:02X}", address);
if (!shouldStopScanTimer()) {
addAddressToList(address);
@@ -305,14 +305,14 @@ void I2cScannerApp::onSelectBus(lv_event_t* event) {
}
void I2cScannerApp::selectBus(int32_t selected) {
int32_t found_port;
if (!getActivePortAtIndex(selected, found_port)) {
struct Device* found_device;
if (!getActivePortAtIndex(selected, &found_device)) {
return;
}
if (mutex.lock(100 / portTICK_PERIOD_MS)) {
scannedAddresses.clear();
port = static_cast<i2c_port_t>(found_port);
portDevice = found_device;
scanState = ScanStateInitial;
mutex.unlock();
}
@@ -334,16 +334,6 @@ void I2cScannerApp::onPressScan(lv_event_t* event) {
updateViews();
}
static bool findDeviceName(const std::vector<std::shared_ptr<hal::i2c::I2cDevice>>& devices, i2c_port_t port, uint8_t address, std::string& outName) {
for (auto& device : devices) {
if (device->getPort() == port && device->getAddress() == address) {
outName = device->getName();
return true;
}
}
return false;
}
void I2cScannerApp::updateViews() {
if (mutex.lock(100 / portTICK_PERIOD_MS)) {
if (scanState == ScanStateScanning) {
@@ -358,18 +348,10 @@ void I2cScannerApp::updateViews() {
if (scanState == ScanStateStopped) {
lv_obj_remove_flag(scanListWidget, LV_OBJ_FLAG_HIDDEN);
auto devices = hal::findDevices<hal::i2c::I2cDevice>(hal::Device::Type::I2c);
if (!scannedAddresses.empty()) {
for (auto address: scannedAddresses) {
std::string address_text = getAddressText(address);
std::string device_name;
if (findDeviceName(devices, port, address, device_name)) {
auto text = std::format("{} - {}", address_text, device_name);
lv_list_add_text(scanListWidget, text.c_str());
} else {
lv_list_add_text(scanListWidget, address_text.c_str());
}
lv_list_add_text(scanListWidget, address_text.c_str());
}
} else {
lv_list_add_text(scanListWidget, "No devices found");