diff --git a/App/CMakeLists.txt b/App/CMakeLists.txt index a17ae984..97296e93 100644 --- a/App/CMakeLists.txt +++ b/App/CMakeLists.txt @@ -28,7 +28,6 @@ else() add_executable(AppSim ${SOURCES}) target_link_libraries(AppSim PRIVATE Tactility - PRIVATE TactilityC PRIVATE TactilityCore PRIVATE TactilityHeadless PRIVATE Simulator diff --git a/App/Source/Main.cpp b/App/Source/Main.cpp index 34486635..2ff4dbce 100644 --- a/App/Source/Main.cpp +++ b/App/Source/Main.cpp @@ -1,12 +1,9 @@ #include "Boards.h" - -// Apps #include "Tactility.h" -#include "tt_init.h" -namespace tt::service::wifi { - extern void wifi_task(void*); -} +#ifdef ESP_PLATFORM +#include "tt_init.h" +#endif extern const tt::app::AppManifest hello_world_app; @@ -26,7 +23,9 @@ void app_main() { .autoStartAppId = nullptr }; +#ifdef ESP_PLATFORM tt_init_tactility_c(); // ELF bindings for side-loading on ESP32 +#endif tt::run(config); } diff --git a/Boards/LilygoTdeck/Source/hal/TdeckKeyboard.cpp b/Boards/LilygoTdeck/Source/hal/TdeckKeyboard.cpp index b1fe6ea3..427d56ae 100644 --- a/Boards/LilygoTdeck/Source/hal/TdeckKeyboard.cpp +++ b/Boards/LilygoTdeck/Source/hal/TdeckKeyboard.cpp @@ -59,7 +59,7 @@ bool TdeckKeyboard::stop() { } bool TdeckKeyboard::isAttached() const { - return tt::hal::i2c::masterCheckAddressForDevice(TDECK_KEYBOARD_I2C_BUS_HANDLE, TDECK_KEYBOARD_SLAVE_ADDRESS, 100); + return tt::hal::i2c::masterHasDeviceAtAddress(TDECK_KEYBOARD_I2C_BUS_HANDLE, TDECK_KEYBOARD_SLAVE_ADDRESS, 100); } tt::hal::Keyboard* createKeyboard() { diff --git a/Documentation/ideas.md b/Documentation/ideas.md index d9a05816..9f4814b7 100644 --- a/Documentation/ideas.md +++ b/Documentation/ideas.md @@ -1,17 +1,15 @@ # Bugs -- I2C Scanner is on M5Stack devices is broken - WiFi bug: when pressing disconnect while between `WIFI_EVENT_STA_START` and `IP_EVENT_STA_GOT_IP`, then auto-connect becomes active again. - ESP32 (CYD) memory issues (or any device without PSRAM): - Boot app doesn't show logo - WiFi is on and navigating back to Desktop makes desktop icons disappear - - WiFi might fail quiety when trying to enable it: this shows no feedback (force it by increasing LVGL buffers to 100kB) + - WiFi might fail quietly when trying to enable it: this shows no feedback (force it by increasing LVGL buffers to 100kB) Possible mitigations: - When no PSRAM is available, use simplified desktop buttons - Add statusbar icon for memory pressure. - Show error in WiFi screen (e.g. AlertDialog when SPI is not enabled and available memory is below a certain amount) - Clean up static_cast when casting to base class. - M5Stack CoreS3 SD card mounts, but cannot be read. There is currently a notice about it [here](https://github.com/espressif/esp-bsp/blob/master/bsp/m5stack_core_s3/README.md). -- hal::I2c has inconsistent return values for read/write functions # TODOs - Call tt::lvgl::isSyncSet after HAL init and show error (and crash?) when it is not set. diff --git a/Documentation/pics/screenshot-Settings.png b/Documentation/pics/screenshot-Settings.png index 0a173faa..917749a2 100644 Binary files a/Documentation/pics/screenshot-Settings.png and b/Documentation/pics/screenshot-Settings.png differ diff --git a/Documentation/pics/screenshot-WifiManage.png b/Documentation/pics/screenshot-WifiManage.png index 919d7724..0c1478fc 100644 Binary files a/Documentation/pics/screenshot-WifiManage.png and b/Documentation/pics/screenshot-WifiManage.png differ diff --git a/Tactility/Source/app/i2cscanner/I2cScannerThread.cpp b/Tactility/Source/app/i2cscanner/I2cScannerThread.cpp index 996442bd..008dc411 100644 --- a/Tactility/Source/app/i2cscanner/I2cScannerThread.cpp +++ b/Tactility/Source/app/i2cscanner/I2cScannerThread.cpp @@ -49,7 +49,7 @@ static void onScanTimer(TT_UNUSED std::shared_ptr context) { 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)) { + if (hal::i2c::masterHasDeviceAtAddress(port, address, 10 / portTICK_PERIOD_MS)) { TT_LOG_I(TAG, "Found device at address %d", address); if (!shouldStopScanTimer(data)) { addAddressToList(data, address); diff --git a/TactilityC/Source/tt_hal_i2c.cpp b/TactilityC/Source/tt_hal_i2c.cpp new file mode 100644 index 00000000..85e7c4d6 --- /dev/null +++ b/TactilityC/Source/tt_hal_i2c.cpp @@ -0,0 +1,50 @@ +#include "tt_hal_i2c.h" +#include "hal/i2c/I2c.h" + +extern "C" { + +bool tt_hal_i2c_start(i2c_port_t port) { + return tt::hal::i2c::start(port); +} + +bool tt_hal_i2c_stop(i2c_port_t port) { + return tt::hal::i2c::stop(port); +} + +bool tt_hal_i2c_is_started(i2c_port_t port) { + return tt::hal::i2c::isStarted(port); +} + +bool tt_hal_i2c_master_read(i2c_port_t port, uint8_t address, uint8_t* data, size_t dataSize, TickType_t timeout) { + return tt::hal::i2c::masterRead(port, address, data, dataSize, timeout); +} + +bool tt_hal_i2c_master_read_register(i2c_port_t port, uint8_t address, uint8_t reg, uint8_t* data, size_t dataSize, TickType_t timeout) { + return tt::hal::i2c::masterRead(port, address, reg, data, dataSize, timeout); +} + +bool tt_hal_i2c_master_write(i2c_port_t port, uint16_t address, const uint8_t* data, uint16_t dataSize, TickType_t timeout) { + return tt::hal::i2c::masterWrite(port, address, data, dataSize, timeout); +} + +bool tt_hal_i2c_master_write_register(i2c_port_t port, uint16_t address, uint8_t reg, const uint8_t* data, uint16_t dataSize, TickType_t timeout) { + return tt::hal::i2c::masterWrite(port, address, reg, data, dataSize, timeout); +} + +bool tt_hal_i2c_master_write_read(i2c_port_t port, uint8_t address, const uint8_t* writeData, size_t writeDataSize, uint8_t* readData, size_t readDataSize, TickType_t timeout) { + return tt::hal::i2c::masterWriteRead(port, address, writeData, writeDataSize, readData, readDataSize, timeout); +} + +bool tt_hal_i2c_master_has_device_at_address(i2c_port_t port, uint8_t address, TickType_t timeout) { + return tt::hal::i2c::masterHasDeviceAtAddress(port, address, timeout); +} + +bool tt_hal_i2c_lock(i2c_port_t port, TickType_t timeout) { + return tt::hal::i2c::lock(port, timeout); +} + +bool tt_hal_i2c_unlock(i2c_port_t port) { + return tt::hal::i2c::unlock(port); +} + +} \ No newline at end of file diff --git a/TactilityC/Source/tt_hal_i2c.h b/TactilityC/Source/tt_hal_i2c.h new file mode 100644 index 00000000..e0696f20 --- /dev/null +++ b/TactilityC/Source/tt_hal_i2c.h @@ -0,0 +1,27 @@ +#pragma once + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +bool tt_hal_i2c_start(i2c_port_t port); +bool tt_hal_i2c_stop(i2c_port_t port); +bool tt_hal_i2c_is_started(i2c_port_t port); + +bool tt_hal_i2c_master_read(i2c_port_t port, uint8_t address, uint8_t* data, size_t dataSize, TickType_t timeout); +bool tt_hal_i2c_master_read_register(i2c_port_t port, uint8_t address, uint8_t reg, uint8_t* data, size_t dataSize, TickType_t timeout); +bool tt_hal_i2c_master_write(i2c_port_t port, uint16_t address, const uint8_t* data, uint16_t dataSize, TickType_t timeout); +bool tt_hal_i2c_master_write_register(i2c_port_t port, uint16_t address, uint8_t reg, const uint8_t* data, uint16_t dataSize, TickType_t timeout); +bool tt_hal_i2c_master_write_read(i2c_port_t port, uint8_t address, const uint8_t* writeData, size_t writeDataSize, uint8_t* readData, size_t readDataSize, TickType_t timeout); +bool tt_hal_i2c_master_has_device_at_address(i2c_port_t port, uint8_t address, TickType_t timeout); + +bool tt_hal_i2c_lock(i2c_port_t port, TickType_t timeout); +bool tt_hal_i2c_unlock(i2c_port_t port); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/TactilityC/Source/tt_init.cpp b/TactilityC/Source/tt_init.cpp index a7d49e64..36564048 100644 --- a/TactilityC/Source/tt_init.cpp +++ b/TactilityC/Source/tt_init.cpp @@ -7,6 +7,7 @@ #include "tt_app_alertdialog.h" #include "tt_app_selectiondialog.h" #include "tt_bundle.h" +#include "tt_hal_i2c.h" #include "tt_lvgl_spinner.h" #include "tt_lvgl_toolbar.h" #include "tt_message_queue.h" @@ -39,6 +40,17 @@ const struct esp_elfsym elf_symbols[] { ESP_ELFSYM_EXPORT(tt_bundle_put_int32), ESP_ELFSYM_EXPORT(tt_bundle_put_string), ESP_ELFSYM_EXPORT(tt_set_app_manifest), + ESP_ELFSYM_EXPORT(tt_hal_i2c_start), + ESP_ELFSYM_EXPORT(tt_hal_i2c_stop), + ESP_ELFSYM_EXPORT(tt_hal_i2c_is_started), + ESP_ELFSYM_EXPORT(tt_hal_i2c_master_read), + ESP_ELFSYM_EXPORT(tt_hal_i2c_master_read_register), + ESP_ELFSYM_EXPORT(tt_hal_i2c_master_write), + ESP_ELFSYM_EXPORT(tt_hal_i2c_master_write_register), + ESP_ELFSYM_EXPORT(tt_hal_i2c_master_write_read), + ESP_ELFSYM_EXPORT(tt_hal_i2c_master_has_device_at_address), + ESP_ELFSYM_EXPORT(tt_hal_i2c_lock), + ESP_ELFSYM_EXPORT(tt_hal_i2c_unlock), ESP_ELFSYM_EXPORT(tt_lvgl_toolbar_create), ESP_ELFSYM_EXPORT(tt_lvgl_toolbar_create_simple), ESP_ELFSYM_EXPORT(tt_message_queue_alloc), diff --git a/TactilityHeadless/Source/hal/i2c/I2c.cpp b/TactilityHeadless/Source/hal/i2c/I2c.cpp index 1eebf8cf..4cfb9558 100644 --- a/TactilityHeadless/Source/hal/i2c/I2c.cpp +++ b/TactilityHeadless/Source/hal/i2c/I2c.cpp @@ -58,7 +58,7 @@ bool init(const std::vector& configurations) { return true; } -static bool configure_locked(i2c_port_t port, const i2c_config_t& configuration) { +static bool configureLocked(i2c_port_t port, const i2c_config_t& configuration) { Data& data = dataArray[port]; if (data.isStarted) { TT_LOG_E(TAG, "(%d) Cannot reconfigure while interface is started", port); @@ -73,10 +73,14 @@ static bool configure_locked(i2c_port_t port, const i2c_config_t& configuration) } bool configure(i2c_port_t port, const i2c_config_t& configuration) { - lock(port); - bool result = configure_locked(port, configuration); - unlock(port); - return result; + if (lock(port)) { + bool result = configureLocked(port, configuration); + unlock(port); + return result; + } else { + TT_LOG_E(TAG, "(%d) Mutex timeout", port); + return false; + } } static bool startLocked(i2c_port_t port) { @@ -113,10 +117,14 @@ static bool startLocked(i2c_port_t port) { } bool start(i2c_port_t port) { - lock(port); - bool result = startLocked(port); - unlock(port); - return result; + if (lock(port)) { + bool result = startLocked(port); + unlock(port); + return result; + } else { + TT_LOG_E(TAG, "(%d) Mutex timeout", port); + return false; + } } static bool stopLocked(i2c_port_t port) { @@ -146,30 +154,45 @@ static bool stopLocked(i2c_port_t port) { } bool stop(i2c_port_t port) { - lock(port); - bool result = stopLocked(port); - unlock(port); - return result; + if (lock(port)) { + bool result = stopLocked(port); + unlock(port); + return result; + } else { + TT_LOG_E(TAG, "(%d) Mutex timeout", port); + return false; + } } bool isStarted(i2c_port_t port) { - lock(port); - bool started = dataArray[port].isStarted; - unlock(port); - return started; + if (lock(port, 50 / portTICK_PERIOD_MS)) { + bool started = dataArray[port].isStarted; + unlock(port); + return started; + } else { + // If we can't get a lock, we assume the device is busy and thus has started + return true; + } } 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, timeout); - unlock(port); - return result == ESP_OK; + if (lock(port)) { + esp_err_t result = i2c_master_read_from_device(port, address, data, dataSize, timeout); + unlock(port); + return result == ESP_OK; + } else { + TT_LOG_E(TAG, "(%d) Mutex timeout", port); + return false; + } } -esp_err_t masterRead(i2c_port_t port, uint8_t address, uint8_t reg, uint8_t* data, size_t dataSize, TickType_t timeout) { +bool masterRead(i2c_port_t port, uint8_t address, uint8_t reg, uint8_t* data, size_t dataSize, TickType_t timeout) { tt_check(reg != 0); - lock(port); + if (!lock(port)) { + TT_LOG_E(TAG, "(%d) Mutex timeout", port); + return false; + } i2c_cmd_handle_t cmd = i2c_cmd_link_create(); // Set address pointer @@ -187,24 +210,32 @@ esp_err_t masterRead(i2c_port_t port, uint8_t address, uint8_t reg, uint8_t* dat esp_err_t result = i2c_master_cmd_begin(port, cmd, timeout); i2c_cmd_link_delete(cmd); + unlock(port); + ESP_LOG_BUFFER_HEX_LEVEL(TAG, data, dataSize, ESP_LOG_DEBUG); ESP_ERROR_CHECK_WITHOUT_ABORT(result); - unlock(port); - return result; -} - -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, timeout); - unlock(port); return result == ESP_OK; } -esp_err_t masterWrite(i2c_port_t port, uint16_t address, uint8_t reg, const uint8_t* data, uint16_t dataSize, TickType_t timeout) { +bool masterWrite(i2c_port_t port, uint16_t address, const uint8_t* data, uint16_t dataSize, TickType_t timeout) { + if (lock(port)) { + esp_err_t result = i2c_master_write_to_device(port, address, data, dataSize, timeout); + unlock(port); + return result == ESP_OK; + } else { + TT_LOG_E(TAG, "(%d) Mutex timeout", port); + return false; + } +} + +bool masterWrite(i2c_port_t port, uint16_t address, uint8_t reg, const uint8_t* data, uint16_t dataSize, TickType_t timeout) { tt_check(reg != 0); - lock(port); + if (!lock(port)) { + TT_LOG_E(TAG, "(%d) Mutex timeout", port); + return false; + } i2c_cmd_handle_t cmd = i2c_cmd_link_create(); i2c_master_start(cmd); @@ -215,34 +246,42 @@ esp_err_t masterWrite(i2c_port_t port, uint16_t address, uint8_t reg, const uint esp_err_t result = i2c_master_cmd_begin(port, cmd, timeout); i2c_cmd_link_delete(cmd); - ESP_ERROR_CHECK_WITHOUT_ABORT(result); - unlock(port); - return result; + ESP_ERROR_CHECK_WITHOUT_ABORT(result); + + 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, TickType_t timeout) { - lock(port); - esp_err_t result = i2c_master_write_read_device(port, address, writeData, writeDataSize, readData, readDataSize, timeout); - unlock(port); - return result == ESP_OK; + if (lock(port)) { + esp_err_t result = i2c_master_write_read_device(port, address, writeData, writeDataSize, readData, readDataSize, timeout); + unlock(port); + return result == ESP_OK; + } else { + TT_LOG_E(TAG, "(%d) Mutex timeout", port); + return false; + } } -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; +bool masterHasDeviceAtAddress(i2c_port_t port, uint8_t address, TickType_t timeout) { + if (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; + } else { + TT_LOG_E(TAG, "(%d) Mutex timeout", port); + return false; + } } -TtStatus lock(i2c_port_t port, TickType_t timeout) { - return dataArray[port].mutex.acquire(timeout); +bool lock(i2c_port_t port, TickType_t timeout) { + return dataArray[port].mutex.lock(timeout); } -TtStatus unlock(i2c_port_t port) { - return dataArray[port].mutex.release(); +bool unlock(i2c_port_t port) { + return dataArray[port].mutex.unlock(); } } // namespace diff --git a/TactilityHeadless/Source/hal/i2c/I2c.h b/TactilityHeadless/Source/hal/i2c/I2c.h index a04e43e4..648271c3 100644 --- a/TactilityHeadless/Source/hal/i2c/I2c.h +++ b/TactilityHeadless/Source/hal/i2c/I2c.h @@ -15,7 +15,7 @@ typedef enum { InitDisabled // Not initialized by default } InitMode; -typedef struct { +struct Configuration { std::string name; /** The port to operate on */ i2c_port_t port; @@ -27,20 +27,29 @@ typedef struct { bool hasMutableConfiguration; /** Configuration that must be valid when initAtBoot is set to true. */ i2c_config_t config; -} Configuration; +}; + +enum Status { + STARTED, + STOPPED, + UNKNOWN +}; + 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, TickType_t timeout); -esp_err_t masterRead(i2c_port_t port, uint8_t address, uint8_t reg, uint8_t* data, size_t dataSize, TickType_t timeout); +bool masterRead(i2c_port_t port, uint8_t address, uint8_t reg, 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); -esp_err_t masterWrite(i2c_port_t port, uint16_t address, uint8_t reg, const uint8_t* data, uint16_t dataSize, TickType_t timeout); +bool masterWrite(i2c_port_t port, uint16_t address, uint8_t reg, 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); +bool masterHasDeviceAtAddress(i2c_port_t port, uint8_t address, TickType_t timeout); + +bool lock(i2c_port_t port, TickType_t timeout = 10 / portTICK_PERIOD_MS); +bool unlock(i2c_port_t port); } // namespace diff --git a/TactilityHeadless/Source/hal/i2c/I2cMock.cpp b/TactilityHeadless/Source/hal/i2c/I2cMock.cpp index f4f0e88f..1d6a3378 100644 --- a/TactilityHeadless/Source/hal/i2c/I2cMock.cpp +++ b/TactilityHeadless/Source/hal/i2c/I2cMock.cpp @@ -88,15 +88,15 @@ bool write(i2c_port_t port, uint16_t address, uint32_t reg, const uint8_t* buffe return false; } -TtStatus lock(i2c_port_t port, TickType_t timeout) { - return dataArray[port].mutex.acquire(timeout); +bool lock(i2c_port_t port, TickType_t timeout) { + return dataArray[port].mutex.lock(timeout); } -TtStatus unlock(i2c_port_t port) { - return dataArray[port].mutex.release(); +bool unlock(i2c_port_t port) { + return dataArray[port].mutex.unlock(); } -bool masterCheckAddressForDevice(i2c_port_t port, uint8_t address, TickType_t timeout) { +bool masterHasDeviceAtAddress(i2c_port_t port, uint8_t address, TickType_t timeout) { return (rand()) % 25 == 0; }