diff --git a/Boards/LilygoTdeck/keyboard.cpp b/Boards/LilygoTdeck/keyboard.cpp index 7406c4a0..45688980 100644 --- a/Boards/LilygoTdeck/keyboard.cpp +++ b/Boards/LilygoTdeck/keyboard.cpp @@ -13,7 +13,7 @@ typedef struct { } KeyboardData; static inline bool keyboard_i2c_read(uint8_t* output) { - return tt::hal::i2c::masterRead(TDECK_KEYBOARD_I2C_BUS_HANDLE, TDECK_KEYBOARD_SLAVE_ADDRESS, output, 1); + return tt::hal::i2c::masterRead(TDECK_KEYBOARD_I2C_BUS_HANDLE, TDECK_KEYBOARD_SLAVE_ADDRESS, output, 1, 100 / portTICK_PERIOD_MS); } void keyboard_wait_for_response() { diff --git a/Boards/LilygoTdeck/lilygo_tdeck.cpp b/Boards/LilygoTdeck/lilygo_tdeck.cpp index e601b806..04b734cb 100644 --- a/Boards/LilygoTdeck/lilygo_tdeck.cpp +++ b/Boards/LilygoTdeck/lilygo_tdeck.cpp @@ -18,11 +18,11 @@ extern const tt::hal::Configuration lilygo_tdeck = { .power = nullptr, .i2c = { tt::hal::i2c::Configuration { + .name = "Internal", .port = I2C_NUM_0, .initMode = tt::hal::i2c::InitByTactility, .canReinit = false, .hasMutableConfiguration = false, - .timeout = 1000, .config = (i2c_config_t) { .mode = I2C_MODE_MASTER, .sda_io_num = GPIO_NUM_18, @@ -36,11 +36,11 @@ extern const tt::hal::Configuration lilygo_tdeck = { } }, tt::hal::i2c::Configuration { + .name = "External", .port = I2C_NUM_1, .initMode = tt::hal::i2c::InitByTactility, .canReinit = true, .hasMutableConfiguration = true, - .timeout = 1000, .config = (i2c_config_t) { .mode = I2C_MODE_MASTER, .sda_io_num = GPIO_NUM_43, diff --git a/Boards/M5stackCore2/Source/M5stackCore2.cpp b/Boards/M5stackCore2/Source/M5stackCore2.cpp index 66591747..23c1cbfa 100644 --- a/Boards/M5stackCore2/Source/M5stackCore2.cpp +++ b/Boards/M5stackCore2/Source/M5stackCore2.cpp @@ -13,7 +13,6 @@ extern const tt::hal::Configuration m5stack_core2 = { .initMode = tt::hal::i2c::InitByExternal, .canReinit = false, .hasMutableConfiguration = false, - .timeout = 1000, .config = (i2c_config_t) { .mode = I2C_MODE_MASTER, .sda_io_num = GPIO_NUM_21, @@ -32,7 +31,6 @@ extern const tt::hal::Configuration m5stack_core2 = { .initMode = tt::hal::i2c::InitByExternal, .canReinit = true, .hasMutableConfiguration = true, - .timeout = 1000, .config = (i2c_config_t) { .mode = I2C_MODE_MASTER, .sda_io_num = GPIO_NUM_32, diff --git a/Boards/M5stackCoreS3/Source/M5stackCoreS3.cpp b/Boards/M5stackCoreS3/Source/M5stackCoreS3.cpp index 68805008..8a0a4767 100644 --- a/Boards/M5stackCoreS3/Source/M5stackCoreS3.cpp +++ b/Boards/M5stackCoreS3/Source/M5stackCoreS3.cpp @@ -15,7 +15,6 @@ const tt::hal::Configuration m5stack_cores3 = { .initMode = tt::hal::i2c::InitByExternal, .canReinit = false, .hasMutableConfiguration = false, - .timeout = 1000, .config = (i2c_config_t) { .mode = I2C_MODE_MASTER, .sda_io_num = GPIO_NUM_12, @@ -34,7 +33,6 @@ const tt::hal::Configuration m5stack_cores3 = { .initMode = tt::hal::i2c::InitByExternal, .canReinit = true, .hasMutableConfiguration = true, - .timeout = 1000, .config = (i2c_config_t) { .mode = I2C_MODE_MASTER, .sda_io_num = GPIO_NUM_2, diff --git a/Boards/Simulator/Source/hardware_config.cpp b/Boards/Simulator/Source/hardware_config.cpp index 0ad09f2b..f95f0e1a 100644 --- a/Boards/Simulator/Source/hardware_config.cpp +++ b/Boards/Simulator/Source/hardware_config.cpp @@ -37,7 +37,6 @@ extern const tt::hal::Configuration sim_hardware = { .initMode = tt::hal::i2c::InitByTactility, .canReinit = false, .hasMutableConfiguration = false, - .timeout = 1000, .config = (i2c_config_t) { .mode = I2C_MODE_MASTER, .sda_io_num = 1, @@ -56,7 +55,6 @@ extern const tt::hal::Configuration sim_hardware = { .initMode = tt::hal::i2c::InitByTactility, .canReinit = true, .hasMutableConfiguration = true, - .timeout = 1000, .config = (i2c_config_t) { .mode = I2C_MODE_MASTER, .sda_io_num = 1, diff --git a/Boards/WaveshareS3Touch/waveshare_s3_touch.cpp b/Boards/WaveshareS3Touch/waveshare_s3_touch.cpp index ca84f83c..e51a9bd9 100644 --- a/Boards/WaveshareS3Touch/waveshare_s3_touch.cpp +++ b/Boards/WaveshareS3Touch/waveshare_s3_touch.cpp @@ -17,7 +17,6 @@ extern const tt::hal::Configuration waveshare_s3_touch = { .initMode = tt::hal::i2c::InitDisabled, .canReinit = true, .hasMutableConfiguration = true, - .timeout = 1000, .config = (i2c_config_t) { .mode = I2C_MODE_MASTER, .sda_io_num = GPIO_NUM_NC, @@ -36,7 +35,6 @@ extern const tt::hal::Configuration waveshare_s3_touch = { .initMode = tt::hal::i2c::InitDisabled, .canReinit = true, .hasMutableConfiguration = true, - .timeout = 1000, .config = (i2c_config_t) { .mode = I2C_MODE_MASTER, .sda_io_num = GPIO_NUM_NC, diff --git a/Boards/YellowBoard/yellow_board.cpp b/Boards/YellowBoard/yellow_board.cpp index ff99ccf3..fe4863df 100644 --- a/Boards/YellowBoard/yellow_board.cpp +++ b/Boards/YellowBoard/yellow_board.cpp @@ -21,7 +21,6 @@ const tt::hal::Configuration yellow_board_24inch_cap = { .initMode = tt::hal::i2c::InitDisabled, .canReinit = true, .hasMutableConfiguration = true, - .timeout = 1000, .config = (i2c_config_t) { .mode = I2C_MODE_MASTER, .sda_io_num = GPIO_NUM_NC, @@ -40,7 +39,6 @@ const tt::hal::Configuration yellow_board_24inch_cap = { .initMode = tt::hal::i2c::InitDisabled, .canReinit = true, .hasMutableConfiguration = true, - .timeout = 1000, .config = (i2c_config_t) { .mode = I2C_MODE_MASTER, .sda_io_num = GPIO_NUM_NC, diff --git a/Tactility/Source/Tactility.cpp b/Tactility/Source/Tactility.cpp index ec372fa3..f88db701 100644 --- a/Tactility/Source/Tactility.cpp +++ b/Tactility/Source/Tactility.cpp @@ -42,6 +42,7 @@ namespace app { namespace screenshot { extern const Manifest manifest; } namespace settings { extern const Manifest manifest; } namespace display { extern const Manifest manifest; } + namespace i2cscanner { extern const Manifest manifest; } namespace i2csettings { extern const Manifest manifest; } namespace power { extern const Manifest manifest; } namespace selectiondialog { extern const Manifest manifest; } @@ -60,6 +61,7 @@ static const std::vector system_apps = { &app::display::manifest, &app::files::manifest, &app::gpio::manifest, + &app::i2cscanner::manifest, &app::i2csettings::manifest, &app::imageviewer::manifest, &app::settings::manifest, diff --git a/Tactility/Source/app/Manifest.h b/Tactility/Source/app/Manifest.h index cb7d6afb..d78f7cf6 100644 --- a/Tactility/Source/app/Manifest.h +++ b/Tactility/Source/app/Manifest.h @@ -50,7 +50,7 @@ typedef struct Manifest { /** * Optional icon. */ - std::string icon; + std::string icon = {}; /** * App type affects launch behaviour. diff --git a/Tactility/Source/app/i2cscanner/I2cHelpers.cpp b/Tactility/Source/app/i2cscanner/I2cHelpers.cpp new file mode 100644 index 00000000..1098dfd2 --- /dev/null +++ b/Tactility/Source/app/i2cscanner/I2cHelpers.cpp @@ -0,0 +1,34 @@ +#include "I2cHelpers.h" +#include "Tactility.h" +#include "StringUtils.h" +#include +#include + +namespace tt::app::i2cscanner { + +std::string getAddressText(uint8_t address) { + std::stringstream stream; + stream << "0x" + << std::uppercase << std::setfill ('0') + << std::setw(2) << std::hex << (uint32_t)address; + return stream.str(); +} + + +std::string getPortNamesForDropdown() { + std::vector config_names; + size_t port_index = 0; + for (const auto& i2c_config: tt::getConfiguration()->hardware->i2c) { + if (!i2c_config.name.empty()) { + config_names.push_back(i2c_config.name); + } else { + std::stringstream stream; + stream << "Port " << std::to_string(port_index); + config_names.push_back(stream.str()); + } + port_index++; + } + return string_join(config_names, "\n"); +} + +} diff --git a/Tactility/Source/app/i2cscanner/I2cHelpers.h b/Tactility/Source/app/i2cscanner/I2cHelpers.h new file mode 100644 index 00000000..37872ba1 --- /dev/null +++ b/Tactility/Source/app/i2cscanner/I2cHelpers.h @@ -0,0 +1,12 @@ +#pragma once + +#include +#include + +namespace tt::app::i2cscanner { + +std::string getAddressText(uint8_t address); + +std::string getPortNamesForDropdown(); + +} diff --git a/Tactility/Source/app/i2cscanner/I2cScanner.cpp b/Tactility/Source/app/i2cscanner/I2cScanner.cpp new file mode 100644 index 00000000..6447f8f8 --- /dev/null +++ b/Tactility/Source/app/i2cscanner/I2cScanner.cpp @@ -0,0 +1,170 @@ +#include "I2cScanner.h" +#include "I2cScannerThread.h" +#include "I2cHelpers.h" + +#include "Assets.h" +#include "Tactility.h" +#include "app/App.h" +#include "lvgl/LvglSync.h" +#include "lvgl/Toolbar.h" + +#define START_SCAN_TEXT "Scan" +#define STOP_SCAN_TEXT "Stop scan" + +namespace tt::app::i2cscanner { + +static void updateViews(Data* data); + +static void onSelectBus(lv_event_t* event) { + auto* dropdown = static_cast(lv_event_get_target(event)); + uint32_t selected = lv_dropdown_get_selected(dropdown); + Data* data = (Data*) lv_event_get_user_data(event); + auto i2c_devices = tt::getConfiguration()->hardware->i2c; + assert(selected < i2c_devices.size()); + + if (data->mutex.acquire(100 / portTICK_PERIOD_MS) == TtStatusOk) { + data->scannedAddresses.clear(); + data->port = i2c_devices[selected].port; + data->scanState = ScanStateInitial; + tt_check(data->mutex.release() == TtStatusOk); + + updateViews(data); + } + + TT_LOG_I(TAG, "Selected %ld", selected); +} + +static void onPressScan(lv_event_t* event) { + auto* data = (Data*)lv_event_get_user_data(event); + if (data->scanState == ScanStateScanning) { + stopScanning(data); + } else { + startScanning(data); + } + updateViews(data); +} + +static void updateViews(Data* data) { + if (data->mutex.acquire(100 / portTICK_PERIOD_MS) == TtStatusOk) { + if (data->scanState == ScanStateScanning) { + lv_label_set_text(data->scanButtonLabelWidget, STOP_SCAN_TEXT); + lv_obj_remove_flag(data->portDropdownWidget, LV_OBJ_FLAG_CLICKABLE); + } else { + lv_label_set_text(data->scanButtonLabelWidget, START_SCAN_TEXT); + lv_obj_add_flag(data->portDropdownWidget, LV_OBJ_FLAG_CLICKABLE); + } + + lv_obj_clean(data->scanListWidget); + if (data->scanState == ScanStateStopped) { + lv_obj_remove_flag(data->scanListWidget, LV_OBJ_FLAG_HIDDEN); + if (!data->scannedAddresses.empty()) { + for (auto address: data->scannedAddresses) { + std::string address_text = getAddressText(address); + lv_list_add_text(data->scanListWidget, address_text.c_str()); + } + } else { + lv_list_add_text(data->scanListWidget, "No devices found"); + } + } else { + lv_obj_add_flag(data->scanListWidget, LV_OBJ_FLAG_HIDDEN); + } + + tt_check(data->mutex.release() == TtStatusOk); + } else { + TT_LOG_W(TAG, "updateViews lock"); + } +} + +static void updateViewsSafely(Data* data) { + if (lvgl::lock(100 / portTICK_PERIOD_MS)) { + updateViews(data); + lvgl::unlock(); + } else { + TT_LOG_W(TAG, "updateViewsSafely lock LVGL"); + } +} + +void onThreadFinished(Data* data) { + if (data->mutex.acquire(100 / portTICK_PERIOD_MS) == TtStatusOk) { + if (data->scanState == ScanStateScanning) { + data->scanState = ScanStateStopped; + updateViewsSafely(data); + } + tt_check(data->mutex.release() == TtStatusOk); + } else { + TT_LOG_W(TAG, "onThreadFinished lock"); + } +} + +static void onShow(App& app, lv_obj_t* parent) { + auto* data = (Data*)app.getData(); + lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN); + + lvgl::toolbar_create(parent, app); + + lv_obj_t* 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); + + lv_obj_t* wrapper = lv_obj_create(main_wrapper); + lv_obj_set_width(wrapper, LV_PCT(100)); + lv_obj_set_height(wrapper, LV_SIZE_CONTENT); + lv_obj_set_style_pad_all(wrapper, 0, 0); + lv_obj_set_style_border_width(wrapper, 0, 0); + + lv_obj_t* scan_button = lv_button_create(wrapper); + lv_obj_set_width(scan_button, LV_PCT(48)); + lv_obj_align(scan_button, LV_ALIGN_TOP_LEFT, 0, 1); // Shift 1 pixel to align with selection box + lv_obj_add_event_cb(scan_button, &onPressScan, LV_EVENT_CLICKED, data); + lv_obj_t* scan_button_label = lv_label_create(scan_button); + lv_obj_align(scan_button_label, LV_ALIGN_CENTER, 0, 0); + lv_label_set_text(scan_button_label, START_SCAN_TEXT); + data->scanButtonLabelWidget = scan_button_label; + + lv_obj_t* port_dropdown = lv_dropdown_create(wrapper); + std::string dropdown_items = getPortNamesForDropdown(); + lv_dropdown_set_options(port_dropdown, dropdown_items.c_str()); + lv_obj_set_width(port_dropdown, LV_PCT(48)); + lv_obj_align(port_dropdown, LV_ALIGN_TOP_RIGHT, 0, 0); + lv_obj_add_event_cb(port_dropdown, onSelectBus, LV_EVENT_VALUE_CHANGED, data); + lv_dropdown_set_selected(port_dropdown, 0); + data->portDropdownWidget = port_dropdown; + + lv_obj_t* scan_list = lv_list_create(main_wrapper); + lv_obj_set_style_margin_top(scan_list, 8, 0); + lv_obj_set_width(scan_list, LV_PCT(100)); + lv_obj_set_height(scan_list, LV_SIZE_CONTENT); + lv_obj_add_flag(scan_list, LV_OBJ_FLAG_HIDDEN); + data->scanListWidget = scan_list; +} + +static void onHide(App& app) { + auto* data = (Data*)app.getData(); + if (hasScanThread(data)) { + stopScanning(data); + } +} + +static void onStart(App& app) { + Data* data = new Data(); + app.setData(data); +} + +static void onStop(App& app) { + Data* data = (Data*)app.getData(); + delete data; +} + +extern const Manifest manifest = { + .id = "I2cScanner", + .name = "I2C Scanner", + .icon = TT_ASSETS_APP_ICON_I2C_SETTINGS, + .type = TypeSystem, + .onStart = onStart, + .onStop = onStop, + .onShow = onShow, + .onHide = onHide +}; + +} // namespace diff --git a/Tactility/Source/app/i2cscanner/I2cScanner.h b/Tactility/Source/app/i2cscanner/I2cScanner.h new file mode 100644 index 00000000..a1bf6cd4 --- /dev/null +++ b/Tactility/Source/app/i2cscanner/I2cScanner.h @@ -0,0 +1,35 @@ +#pragma once + +#include +#include +#include +#include "lvgl.h" +#include "hal/i2c/I2c.h" + +namespace tt::app::i2cscanner { + +#define TAG "i2cscanner" + +enum ScanState { + ScanStateInitial, + ScanStateScanning, + ScanStateStopped +}; + +struct Data { + // Core + Mutex mutex = Mutex(MutexTypeRecursive); + Thread* _Nullable scanThread = nullptr; + // State + ScanState scanState; + i2c_port_t port = I2C_NUM_0; + std::vector scannedAddresses; + // Widgets + lv_obj_t* scanButtonLabelWidget = nullptr; + lv_obj_t* portDropdownWidget = nullptr; + lv_obj_t* scanListWidget = nullptr; +}; + +void onThreadFinished(Data* data); + +} diff --git a/Tactility/Source/app/i2cscanner/I2cScannerThread.cpp b/Tactility/Source/app/i2cscanner/I2cScannerThread.cpp new file mode 100644 index 00000000..952f52b8 --- /dev/null +++ b/Tactility/Source/app/i2cscanner/I2cScannerThread.cpp @@ -0,0 +1,134 @@ +#include "I2cScannerThread.h" +#include "lvgl.h" +#include "service/gui/Gui.h" +#include + +namespace tt::app::i2cscanner { + +static bool shouldStopScanThread(Data* data) { + if (data->mutex.acquire(100 / portTICK_PERIOD_MS) == TtStatusOk) { + bool is_scanning = data->scanState == ScanStateScanning; + tt_check(data->mutex.release() == TtStatusOk); + return !is_scanning; + } else { + return true; + } +} + +static bool getPort(Data* data, i2c_port_t* port) { + if (data->mutex.acquire(100 / portTICK_PERIOD_MS) == TtStatusOk) { + *port = data->port; + tt_assert(data->mutex.release() == TtStatusOk); + return true; + } else { + TT_LOG_W(TAG, "getPort lock"); + return false; + } +} + +static bool addAddressToList(Data* data, uint8_t address) { + if (data->mutex.acquire(100 / portTICK_PERIOD_MS) == TtStatusOk) { + data->scannedAddresses.push_back(address); + tt_assert(data->mutex.release() == TtStatusOk); + return true; + } else { + TT_LOG_W(TAG, "addAddressToList lock"); + return false; + } +} + +static int32_t scanThread(void* context) { + Data* data = (Data*) context; + TT_LOG_I(TAG, "Scan thread started"); + + for (uint8_t address = 0; address < 128; ++address) { + i2c_port_t port; + if (getPort(data, &port)) { + if (hal::i2c::masterCheckAddressForDevice(port, address, 10 / portTICK_PERIOD_MS)) { + TT_LOG_I(TAG, "Found device at address %d", address); + if (!shouldStopScanThread(data)) { + addAddressToList(data, address); + } + } + } else { + TT_LOG_W(TAG, "scanThread lock"); + break; + } + + if (shouldStopScanThread(data)) { + break; + } + } + + TT_LOG_I(TAG, "Scan thread finalizing"); + + onThreadFinished(data); + + TT_LOG_I(TAG, "Scan thread stopped"); + + return 0; +} + +bool hasScanThread(Data* data) { + bool has_thread; + if (data->mutex.acquire(100 / portTICK_PERIOD_MS) == TtStatusOk) { + has_thread = data->scanThread != nullptr; + tt_check(data->mutex.release() == TtStatusOk); + return has_thread; + } else { + // Unsafe way + TT_LOG_W(TAG, "hasScanThread lock"); + return data->scanThread != nullptr; + } +} + +void startScanning(Data* data) { + if (hasScanThread(data)) { + stopScanning(data); + } + + if (data->mutex.acquire(100 / portTICK_PERIOD_MS) == TtStatusOk) { + data->scannedAddresses.clear(); + + lv_obj_add_flag(data->scanListWidget, LV_OBJ_FLAG_HIDDEN); + lv_obj_clean(data->scanListWidget); + + tt_assert(data->scanThread == nullptr); + data->scanState = ScanStateScanning; + data->scanThread = new Thread( + "i2c scanner", + 4096, + scanThread, + data + ); + data->scanThread->start(); + tt_check(data->mutex.release() == TtStatusOk); + } else { + TT_LOG_W(TAG, "startScanning lock"); + } +} + +void stopScanning(Data* data) { + bool sent_halt; + if (data->mutex.acquire(250 / portTICK_PERIOD_MS) == TtStatusOk) { + tt_assert(data->scanThread != nullptr); + data->scanState = ScanStateStopped; + tt_check(data->mutex.release() == TtStatusOk); + sent_halt = true; + } else { + sent_halt = false; + } + + if (sent_halt) { + tt_assert(data->scanThread != nullptr); + data->scanThread->join(); + + if (data->mutex.acquire(250 / portTICK_PERIOD_MS) == TtStatusOk) { + delete data->scanThread; + data->scanThread = nullptr; + tt_check(data->mutex.release() == TtStatusOk); + } + } +} + +} // namespace \ No newline at end of file diff --git a/Tactility/Source/app/i2cscanner/I2cScannerThread.h b/Tactility/Source/app/i2cscanner/I2cScannerThread.h new file mode 100644 index 00000000..45dc55ef --- /dev/null +++ b/Tactility/Source/app/i2cscanner/I2cScannerThread.h @@ -0,0 +1,11 @@ +#pragma once + +#include "I2cScanner.h" + +namespace tt::app::i2cscanner { + +bool hasScanThread(Data* data); +void startScanning(Data* data); +void stopScanning(Data* data); + +} diff --git a/TactilityCore/Source/Mutex.cpp b/TactilityCore/Source/Mutex.cpp index fc944fc5..ff887aad 100644 --- a/TactilityCore/Source/Mutex.cpp +++ b/TactilityCore/Source/Mutex.cpp @@ -35,7 +35,7 @@ Mutex::Mutex(MutexType type) : type(type) { tt_crash("Mutex type unknown/corrupted"); } - tt_check(semaphore != nullptr); + tt_assert(semaphore != nullptr); } Mutex::~Mutex() { @@ -47,7 +47,6 @@ Mutex::~Mutex() { TtStatus Mutex::acquire(uint32_t timeout) const { tt_assert(!TT_IS_IRQ_MODE()); tt_assert(semaphore); - TtStatus status = TtStatusOk; tt_mutex_info(mutex, "acquire"); @@ -55,52 +54,54 @@ TtStatus Mutex::acquire(uint32_t timeout) const { case MutexTypeNormal: if (xSemaphoreTake(semaphore, timeout) != pdPASS) { if (timeout != 0U) { - status = TtStatusErrorTimeout; + return TtStatusErrorTimeout; } else { - status = TtStatusErrorResource; + return TtStatusErrorResource; } + } else { + return TtStatusOk; } break; case MutexTypeRecursive: if (xSemaphoreTakeRecursive(semaphore, timeout) != pdPASS) { if (timeout != 0U) { - status = TtStatusErrorTimeout; + return TtStatusErrorTimeout; } else { - status = TtStatusErrorResource; + return TtStatusErrorResource; } + } else { + return TtStatusOk; } break; default: tt_crash("mutex type unknown/corrupted"); } - - return status; } TtStatus Mutex::release() const { assert(!TT_IS_IRQ_MODE()); tt_assert(semaphore); - TtStatus status = TtStatusOk; - tt_mutex_info(mutex, "release"); switch (type) { case MutexTypeNormal: { if (xSemaphoreGive(semaphore) != pdPASS) { - status = TtStatusErrorResource; + return TtStatusErrorResource; + } else { + return TtStatusOk; } break; } case MutexTypeRecursive: if (xSemaphoreGiveRecursive(semaphore) != pdPASS) { - status = TtStatusErrorResource; + return TtStatusErrorResource; + } else { + return TtStatusOk; } break; default: tt_crash("mutex type unknown/corrupted"); } - - return status; } ThreadId Mutex::getOwner() const { diff --git a/TactilityHeadless/Source/hal/i2c/I2c.cpp b/TactilityHeadless/Source/hal/i2c/I2c.cpp index 16362eb4..fd8fee7f 100644 --- a/TactilityHeadless/Source/hal/i2c/I2c.cpp +++ b/TactilityHeadless/Source/hal/i2c/I2c.cpp @@ -157,28 +157,36 @@ bool isStarted(i2c_port_t port) { return started; } -bool masterRead(i2c_port_t port, uint8_t address, uint8_t* data, size_t dataSize) { +bool masterRead(i2c_port_t port, uint8_t address, uint8_t* data, size_t dataSize, TickType_t timeout) { lock(port); - esp_err_t result = i2c_master_read_from_device(port, address, data, dataSize, dataArray[port].configuration.timeout); + esp_err_t result = i2c_master_read_from_device(port, address, data, dataSize, timeout); unlock(port); return result == ESP_OK; } -bool masterWrite(i2c_port_t port, uint16_t address, const uint8_t* data, uint16_t dataSize) { +bool masterWrite(i2c_port_t port, uint16_t address, const uint8_t* data, uint16_t dataSize, TickType_t timeout) { lock(port); - esp_err_t result = i2c_master_write_to_device(port, address, data, dataSize, dataArray[port].configuration.timeout); + esp_err_t result = i2c_master_write_to_device(port, address, data, dataSize, timeout); unlock(port); return result == ESP_OK; } -bool masterWriteRead(i2c_port_t port, uint8_t address, const uint8_t* writeData, size_t writeDataSize, uint8_t* readData, size_t readDataSize) { +bool masterWriteRead(i2c_port_t port, uint8_t address, const uint8_t* writeData, size_t writeDataSize, uint8_t* readData, size_t readDataSize, TickType_t timeout) { lock(port); - esp_err_t result = i2c_master_write_read_device(port, address, writeData, writeDataSize, readData, readDataSize, dataArray[port].configuration.timeout); + esp_err_t result = i2c_master_write_read_device(port, address, writeData, writeDataSize, readData, readDataSize, timeout); unlock(port); return result == ESP_OK; } -TtStatus lock(i2c_port_t port, uint32_t timeout) { +bool masterCheckAddressForDevice(i2c_port_t port, uint8_t address, TickType_t timeout) { + lock(port); + uint8_t message[2] = { 0, 0 }; + esp_err_t result = i2c_master_write_to_device(port, address, message, 2, timeout); + unlock(port); + return result == ESP_OK; +} + +TtStatus lock(i2c_port_t port, TickType_t timeout) { return dataArray[port].mutex.acquire(timeout); } diff --git a/TactilityHeadless/Source/hal/i2c/I2c.h b/TactilityHeadless/Source/hal/i2c/I2c.h index a183125c..0992851c 100644 --- a/TactilityHeadless/Source/hal/i2c/I2c.h +++ b/TactilityHeadless/Source/hal/i2c/I2c.h @@ -6,6 +6,12 @@ #include #include +#ifdef ESP_TARGET +#include "freertos/FreeRTOS.h" +#else +#include "FreeRTOS.h" +#endif + namespace tt::hal::i2c { typedef enum { @@ -24,8 +30,6 @@ typedef struct { bool canReinit; /** Whether configuration can be changed. */ bool hasMutableConfiguration; - /** Read/write timeout (not related to mutex locking mechanism) */ - unsigned long timeout; /** Configuration that must be valid when initAtBoot is set to true. */ i2c_config_t config; } Configuration; @@ -35,10 +39,11 @@ bool init(const std::vector& configurations); bool start(i2c_port_t port); bool stop(i2c_port_t port); bool isStarted(i2c_port_t port); -bool masterRead(i2c_port_t port, uint8_t address, uint8_t* data, size_t dataSize); -bool masterWrite(i2c_port_t port, uint16_t address, const uint8_t* data, uint16_t dataSize); -bool masterWriteRead(i2c_port_t port, uint8_t address, const uint8_t* writeData, size_t writeDataSize, uint8_t* readData, size_t readDataSize); -TtStatus lock(i2c_port_t port, uint32_t timeout = UINT_MAX); +bool masterRead(i2c_port_t port, uint8_t address, uint8_t* data, size_t dataSize, TickType_t timeout); +bool masterWrite(i2c_port_t port, uint16_t address, const uint8_t* data, uint16_t dataSize, TickType_t timeout); +bool masterWriteRead(i2c_port_t port, uint8_t address, const uint8_t* writeData, size_t writeDataSize, uint8_t* readData, size_t readDataSize, TickType_t timeout); +bool masterCheckAddressForDevice(i2c_port_t port, uint8_t address, TickType_t timeout); +TtStatus lock(i2c_port_t port, TickType_t timeout = UINT_MAX); TtStatus unlock(i2c_port_t port); } // namespace diff --git a/TactilityHeadless/Source/hal/i2c/I2cMock.cpp b/TactilityHeadless/Source/hal/i2c/I2cMock.cpp index 5116b72b..b256f272 100644 --- a/TactilityHeadless/Source/hal/i2c/I2cMock.cpp +++ b/TactilityHeadless/Source/hal/i2c/I2cMock.cpp @@ -3,6 +3,7 @@ /** * This code is based on i2c_manager from https://github.com/ropg/i2c_manager/blob/master/i2c_manager/i2c_manager.c (original has MIT license) */ +#include #include "I2c.h" #include "Log.h" #include "Mutex.h" @@ -81,15 +82,15 @@ bool isStarted(i2c_port_t port) { return started; } -bool read(i2c_port_t port, uint16_t address, uint32_t reg, uint8_t* buffer, uint16_t size) { +bool read(i2c_port_t port, uint16_t address, uint32_t reg, uint8_t* buffer, uint16_t size, TickType_t timeout) { return false; } -bool write(i2c_port_t port, uint16_t address, uint32_t reg, const uint8_t* buffer, uint16_t size) { +bool write(i2c_port_t port, uint16_t address, uint32_t reg, const uint8_t* buffer, uint16_t size, TickType_t timeout) { return false; } -TtStatus lock(i2c_port_t port, uint32_t timeout) { +TtStatus lock(i2c_port_t port, TickType_t timeout) { return dataArray[port].mutex.acquire(timeout); } @@ -97,6 +98,10 @@ TtStatus unlock(i2c_port_t port) { return dataArray[port].mutex.release(); } +bool masterCheckAddressForDevice(i2c_port_t port, uint8_t address, TickType_t timeout) { + return (rand()) % 25 == 0; +} + } // namespace #endif \ No newline at end of file