diff --git a/Boards/ElecrowCrowpanelAdvance28/Source/CrowPanelAdvance28.cpp b/Boards/ElecrowCrowpanelAdvance28/Source/CrowPanelAdvance28.cpp index 71bed8d6..2a3ad72b 100644 --- a/Boards/ElecrowCrowpanelAdvance28/Source/CrowPanelAdvance28.cpp +++ b/Boards/ElecrowCrowpanelAdvance28/Source/CrowPanelAdvance28.cpp @@ -95,10 +95,8 @@ extern const Configuration crowpanel_advance_28 = { .uart { // "UART0-IN" uart::Configuration { + .name = "UART0", .port = UART_NUM_1, - .initMode = uart::InitMode::Disabled, // Manual init - .canReinit = true, - .hasMutableConfiguration = false, .rxPin = GPIO_NUM_44, .txPin = GPIO_NUM_43, .rtsPin = GPIO_NUM_NC, @@ -121,10 +119,8 @@ extern const Configuration crowpanel_advance_28 = { }, // "UART1-OUT" uart::Configuration { - .port = UART_NUM_1, - .initMode = uart::InitMode::Disabled, // Manual init - .canReinit = true, - .hasMutableConfiguration = false, + .name = "UART1", + .port = UART_NUM_2, .rxPin = GPIO_NUM_18, .txPin = GPIO_NUM_17, .rtsPin = GPIO_NUM_NC, diff --git a/Boards/ElecrowCrowpanelAdvance35/Source/CrowPanelAdvance35.cpp b/Boards/ElecrowCrowpanelAdvance35/Source/CrowPanelAdvance35.cpp index be5267ac..1bede9ad 100644 --- a/Boards/ElecrowCrowpanelAdvance35/Source/CrowPanelAdvance35.cpp +++ b/Boards/ElecrowCrowpanelAdvance35/Source/CrowPanelAdvance35.cpp @@ -95,10 +95,8 @@ extern const Configuration crowpanel_advance_35 = { .uart { // "UART0-IN" uart::Configuration { + .name = "UART0", .port = UART_NUM_1, - .initMode = uart::InitMode::Disabled, // Manual init - .canReinit = true, - .hasMutableConfiguration = false, .rxPin = GPIO_NUM_44, .txPin = GPIO_NUM_43, .rtsPin = GPIO_NUM_NC, @@ -121,10 +119,8 @@ extern const Configuration crowpanel_advance_35 = { }, // "UART1-OUT" uart::Configuration { - .port = UART_NUM_1, - .initMode = uart::InitMode::Disabled, // Manual init - .canReinit = true, - .hasMutableConfiguration = false, + .name = "UART1", + .port = UART_NUM_2, .rxPin = GPIO_NUM_18, .txPin = GPIO_NUM_17, .rtsPin = GPIO_NUM_NC, diff --git a/Boards/ElecrowCrowpanelBasic28/Source/CrowPanelBasic28.cpp b/Boards/ElecrowCrowpanelBasic28/Source/CrowPanelBasic28.cpp index 494fd9a3..26b8a74c 100644 --- a/Boards/ElecrowCrowpanelBasic28/Source/CrowPanelBasic28.cpp +++ b/Boards/ElecrowCrowpanelBasic28/Source/CrowPanelBasic28.cpp @@ -97,10 +97,8 @@ extern const Configuration crowpanel_basic_28 = { .uart { // "UART1" uart::Configuration { + .name = "UART1", .port = UART_NUM_1, - .initMode = uart::InitMode::Disabled, // Manual init - .canReinit = true, - .hasMutableConfiguration = false, .rxPin = GPIO_NUM_16, .txPin = GPIO_NUM_17, .rtsPin = GPIO_NUM_NC, diff --git a/Boards/ElecrowCrowpanelBasic35/Source/CrowPanelBasic35.cpp b/Boards/ElecrowCrowpanelBasic35/Source/CrowPanelBasic35.cpp index f70287e2..b456cc2c 100644 --- a/Boards/ElecrowCrowpanelBasic35/Source/CrowPanelBasic35.cpp +++ b/Boards/ElecrowCrowpanelBasic35/Source/CrowPanelBasic35.cpp @@ -97,10 +97,8 @@ extern const Configuration crowpanel_basic_35 = { .uart { // "UART1" uart::Configuration { + .name = "UART1", .port = UART_NUM_1, - .initMode = uart::InitMode::Disabled, // Manual init - .canReinit = true, - .hasMutableConfiguration = false, .rxPin = GPIO_NUM_3, .txPin = GPIO_NUM_1, .rtsPin = GPIO_NUM_NC, diff --git a/Boards/LilygoTdeck/Source/LilygoTdeck.cpp b/Boards/LilygoTdeck/Source/LilygoTdeck.cpp index feac2976..cfab11ed 100644 --- a/Boards/LilygoTdeck/Source/LilygoTdeck.cpp +++ b/Boards/LilygoTdeck/Source/LilygoTdeck.cpp @@ -85,10 +85,8 @@ extern const Configuration lilygo_tdeck = { }, .uart { uart::Configuration { + .name = "Grove", .port = UART_NUM_1, - .initMode = uart::InitMode::Disabled, // Let GPS driver control this interface - .canReinit = true, - .hasMutableConfiguration = false, .rxPin = GPIO_NUM_44, .txPin = GPIO_NUM_43, .rtsPin = GPIO_NUM_NC, @@ -113,7 +111,7 @@ extern const Configuration lilygo_tdeck = { .gps = { gps::GpsDevice::Configuration { .name = "Internal", - .uartPort = UART_NUM_1, + .uartName = "Grove", .baudRate = 38400, .model = gps::GpsModel::UBLOX10 } diff --git a/Boards/Simulator/Source/Simulator.cpp b/Boards/Simulator/Source/Simulator.cpp index c16c23b6..07beac0e 100644 --- a/Boards/Simulator/Source/Simulator.cpp +++ b/Boards/Simulator/Source/Simulator.cpp @@ -9,6 +9,8 @@ #define TAG "hardware" +using namespace tt::hal; + static bool initBoot() { lv_init(); lvgl_task_start(); @@ -26,17 +28,17 @@ TT_UNUSED static void deinitPower() { #endif } -extern const tt::hal::Configuration hardware = { +extern const Configuration hardware = { .initBoot = initBoot, .createDisplay = createDisplay, .createKeyboard = createKeyboard, .sdcard = std::make_shared(), .power = simulatorPower, .i2c = { - tt::hal::i2c::Configuration { + i2c::Configuration { .name = "Internal", .port = I2C_NUM_0, - .initMode = tt::hal::i2c::InitMode::ByTactility, + .initMode = i2c::InitMode::ByTactility, .canReinit = false, .hasMutableConfiguration = false, .config = (i2c_config_t) { @@ -51,15 +53,15 @@ extern const tt::hal::Configuration hardware = { .clk_flags = 0 } }, - tt::hal::i2c::Configuration { + i2c::Configuration { .name = "External", .port = I2C_NUM_1, - .initMode = tt::hal::i2c::InitMode::ByTactility, + .initMode = i2c::InitMode::ByTactility, .canReinit = true, .hasMutableConfiguration = true, .config = (i2c_config_t) { .mode = I2C_MODE_MASTER, - .sda_io_num = 1, + .sda_io_num = 3, .scl_io_num = 2, .sda_pullup_en = false, .scl_pullup_en = false, @@ -69,5 +71,23 @@ extern const tt::hal::Configuration hardware = { .clk_flags = 0 } } + }, + .uart = { + uart::Configuration { + .name = "/dev/ttyUSB0", + .baudRate = 115200 + }, + uart::Configuration { + .name = "/dev/ttyACM0", + .baudRate = 115200 + } + }, + .gps = { + gps::GpsDevice::Configuration { + .name = "Internal", + .uartName = "/dev/ttyACM0", + .baudRate = 115200, + .model = gps::GpsModel::UBLOX10 + } } }; diff --git a/Tactility/Source/app/gpssettings/GpsSettings.cpp b/Tactility/Source/app/gpssettings/GpsSettings.cpp index 11b1555f..8da3ddda 100644 --- a/Tactility/Source/app/gpssettings/GpsSettings.cpp +++ b/Tactility/Source/app/gpssettings/GpsSettings.cpp @@ -77,25 +77,25 @@ private: // Update toolbar switch (state) { case service::gps::State::OnPending: - TT_LOG_I(TAG, "OnPending"); + TT_LOG_D(TAG, "OnPending"); lv_obj_remove_flag(spinnerWidget, LV_OBJ_FLAG_HIDDEN); lv_obj_add_state(switchWidget, LV_STATE_CHECKED); lv_obj_add_state(switchWidget, LV_STATE_DISABLED); break; case service::gps::State::On: - TT_LOG_I(TAG, "On"); + TT_LOG_D(TAG, "On"); lv_obj_add_flag(spinnerWidget, LV_OBJ_FLAG_HIDDEN); lv_obj_add_state(switchWidget, LV_STATE_CHECKED); lv_obj_remove_state(switchWidget, LV_STATE_DISABLED); break; case service::gps::State::OffPending: - TT_LOG_I(TAG, "OffPending"); + TT_LOG_D(TAG, "OffPending"); lv_obj_remove_flag(spinnerWidget, LV_OBJ_FLAG_HIDDEN); lv_obj_remove_state(switchWidget, LV_STATE_CHECKED); lv_obj_add_state(switchWidget, LV_STATE_DISABLED); break; case service::gps::State::Off: - TT_LOG_I(TAG, "Off"); + TT_LOG_D(TAG, "Off"); lv_obj_add_flag(spinnerWidget, LV_OBJ_FLAG_HIDDEN); lv_obj_remove_state(switchWidget, LV_STATE_CHECKED); lv_obj_remove_state(switchWidget, LV_STATE_DISABLED); diff --git a/TactilityHeadless/Include/Tactility/hal/gps/GpsDevice.h b/TactilityHeadless/Include/Tactility/hal/gps/GpsDevice.h index 0c4aa8e4..ddfd600d 100644 --- a/TactilityHeadless/Include/Tactility/hal/gps/GpsDevice.h +++ b/TactilityHeadless/Include/Tactility/hal/gps/GpsDevice.h @@ -1,7 +1,6 @@ #pragma once #include "../Device.h" -#include "../uart/Uart.h" #include "Satellites.h" #include @@ -12,7 +11,6 @@ namespace tt::hal::gps { - enum class GpsResponse { None, NotAck, @@ -24,7 +22,7 @@ enum class GpsModel { Unknown = 0, AG3335, AG3352, - ATGM336H, + ATGM336H, // Casic (might work with AT6558, Neoway N58 LTE Cat.1, Neoway G2, Neoway G7A) LS20031, MTK, MTK_L76B, @@ -48,7 +46,7 @@ public: struct Configuration { std::string name; - uart_port_t uartPort; + std::string uartName; // e.g. "Internal" or "/dev/ttyUSB0" uint32_t baudRate; GpsModel model; }; @@ -75,7 +73,7 @@ private: const Configuration configuration; Mutex mutex = Mutex(Mutex::Type::Recursive); - std::unique_ptr thread; + std::unique_ptr _Nullable thread; bool threadInterrupted = false; std::vector ggaSubscriptions; std::vector rmcSubscriptions; diff --git a/TactilityHeadless/Include/Tactility/hal/uart/Configuration.h b/TactilityHeadless/Include/Tactility/hal/uart/Configuration.h new file mode 100644 index 00000000..72d0942a --- /dev/null +++ b/TactilityHeadless/Include/Tactility/hal/uart/Configuration.h @@ -0,0 +1,40 @@ +#pragma once + +namespace tt::hal::uart { + +#ifdef ESP_PLATFORM + +struct Configuration { + /** The unique name for this UART */ + std::string name; + /** The port idenitifier (e.g. UART_NUM_0) */ + uart_port_t port; + /** Receive GPIO pin */ + gpio_num_t rxPin; + /** Transmit GPIO pin */ + gpio_num_t txPin; + /** Read-To-Send GPIO pin */ + gpio_num_t rtsPin; + /** Clear-To-Send Send GPIO pin */ + gpio_num_t ctsPin; + /** Receive buffer size in bytes */ + unsigned int rxBufferSize; + /** Transmit buffer size in bytes */ + unsigned int txBufferSize; + /** Native configuration */ + uart_config_t config; +}; + + +#else + +struct Configuration { + /** The unique name for this UART */ + std::string name; + /** Initial baud rate in bits per second */ + uint32_t baudRate; +}; + +#endif + +} \ No newline at end of file diff --git a/TactilityHeadless/Include/Tactility/hal/uart/Uart.h b/TactilityHeadless/Include/Tactility/hal/uart/Uart.h index e630359b..1dc18127 100644 --- a/TactilityHeadless/Include/Tactility/hal/uart/Uart.h +++ b/TactilityHeadless/Include/Tactility/hal/uart/Uart.h @@ -5,6 +5,7 @@ #include "../Gpio.h" #include "Tactility/Lock.h" #include "UartCompat.h" +#include "Tactility/hal/uart/Configuration.h" #include #include @@ -19,102 +20,105 @@ enum class InitMode { Disabled // Not initialized by default }; -struct Configuration { - uart_port_t port; - /** Whether this bus should be initialized when device starts up */ - InitMode initMode; - /** Whether this bus can stopped and re-started. */ - bool canReinit; - /** Whether .config can be changed. */ - bool hasMutableConfiguration; - /** Receive GPIO pin */ - gpio_num_t rxPin; - /** Transmit GPIO pin */ - gpio_num_t txPin; - /** Read-To-Send GPIO pin */ - gpio_num_t rtsPin; - /** Clear-To-Send Send GPIO pin */ - gpio_num_t ctsPin; - /** Receive buffer size in bytes */ - unsigned int rxBufferSize; - /** Transmit buffer size in bytes */ - unsigned int txBufferSize; - /** Native configuration */ - uart_config_t config; -}; - enum class Status { Started, Stopped, Unknown }; -/** Start communications */ -bool start(uart_port_t port); +class Uart { -/** Stop communications */ -bool stop(uart_port_t port); +private: -/** @return true when communications were successfully started */ -bool isStarted(uart_port_t port); + uint32_t id; -/** @return a lock that is usable for using ESP-IDF directly, or for use with third party APIs */ -Lock& getLock(uart_port_t port); +public: + + Uart(); + virtual ~Uart(); + + inline uint32_t getId() const { return id; } + + virtual bool start() = 0; + virtual bool isStarted() const = 0; + + virtual bool stop() = 0; + + /** + * Read up to a specified amount of bytes + * @param[out] buffer + * @param[in] bufferSize + * @param[in] timeout + * @return the amount of bytes that were read + */ + virtual size_t readBytes(std::byte* buffer, size_t bufferSize, TickType_t timeout = defaultTimeout) = 0; + + size_t readBytes(std::uint8_t* buffer, size_t bufferSize, TickType_t timeout = defaultTimeout) { + return readBytes(reinterpret_cast(buffer), bufferSize, timeout); + } + + /** Read a single byte */ + virtual bool readByte(std::byte* output, TickType_t timeout = defaultTimeout) = 0; + + inline bool readByte(char* output, TickType_t timeout = defaultTimeout) { + return readByte(reinterpret_cast(output), timeout); + } + + inline bool readByte(uint8_t* output, TickType_t timeout = defaultTimeout) { + return readByte(reinterpret_cast(output), timeout); + } + + /** + * Read up to a specified amount of bytes + * @param[in] buffer + * @param[in] bufferSize + * @param[in] timeout + * @return the amount of bytes that were read + */ + virtual size_t writeBytes(const std::byte* buffer, size_t bufferSize, TickType_t timeout = defaultTimeout) = 0; + + inline size_t writeBytes(const std::uint8_t* buffer, size_t bufferSize, TickType_t timeout = defaultTimeout) { + return writeBytes(reinterpret_cast(buffer), bufferSize, timeout); + } + + /** @return the amount of bytes available for reading */ + virtual size_t available(TickType_t timeout = defaultTimeout) = 0; + + /** Set the baud rate for the specified port */ + virtual bool setBaudRate(uint32_t baudRate, TickType_t timeout = defaultTimeout) = 0; + + /** Get the baud rate for the specified port */ + virtual uint32_t getBaudRate() = 0; + + /** Flush input buffers */ + virtual void flushInput() = 0; + + /** + * Write a string (excluding null terminator character) + * @param[in] buffer + * @param[in] timeout + * @return the amount of bytes that were written + */ + bool writeString(const char* buffer, TickType_t timeout = defaultTimeout); + + /** + * Read a buffer as a string until the specified character (the "untilChar" is included in the result) + * @warning if the input data doesn't return "untilByte" then the device goes out of memory + */ + std::string readStringUntil(char untilChar, TickType_t timeout = defaultTimeout); + + /** + * Read a buffer as a byte array until the specified character (the "untilChar" is included in the result) + * @return the amount of bytes read from UART + */ + size_t readUntil(std::byte* buffer, size_t bufferSize, uint8_t untilByte, TickType_t timeout = defaultTimeout, bool addNullTerminator = true); +}; /** - * Read up to a specified amount of bytes - * @param[in] port - * @param[out] buffer - * @param[in] bufferSize - * @param[in] timeout - * @return the amount of bytes that were read + * Opens a UART. + * @param[in] name the UART name as specified in the board configuration. + * @return the UART when it was successfully opened, or nullptr when it is in use. */ -size_t readBytes(uart_port_t port, uint8_t* buffer, size_t bufferSize, TickType_t timeout = defaultTimeout); - -/** Read a single byte */ -bool readByte(uart_port_t port, uint8_t* output, TickType_t timeout = defaultTimeout); - -/** - * Read up to a specified amount of bytes - * @param[in] port - * @param[in] buffer - * @param[in] bufferSize - * @param[in] timeout - * @return the amount of bytes that were read - */ -size_t writeBytes(uart_port_t port, const uint8_t* buffer, size_t bufferSize, TickType_t timeout = defaultTimeout); - -/** - * Write a string (excluding null terminator character) - * @param[in] port - * @param[in] buffer - * @param[in] timeout - * @return the amount of bytes that were written - */ -bool writeString(uart_port_t port, const char* buffer, TickType_t timeout = defaultTimeout); - -/** @return the amount of bytes available for reading */ -size_t available(uart_port_t port, TickType_t timeout = defaultTimeout); - -/** Set the baud rate for the specified port */ -bool setBaudRate(uart_port_t port, uint32_t baudRate, TickType_t timeout = defaultTimeout); - -/** Get the baud rate for the specified port */ -uint32_t getBaudRate(uart_port_t port); - -/** Flush input buffers */ -void flushInput(uart_port_t port); - -/** - * Read a buffer as a string until the specified character (the "untilChar" is included in the result) - * @warning if the input data doesn't return "untilByte" then the device goes out of memory - */ -std::string readStringUntil(uart_port_t port, char untilChar, TickType_t timeout = defaultTimeout); - -/** - * Read a buffer as a byte array until the specified character (the "untilChar" is included in the result) - * @return the amount of bytes read from UART - */ -size_t readUntil(uart_port_t port, uint8_t* buffer, size_t bufferSize, uint8_t untilByte, TickType_t timeout = defaultTimeout, bool addNullTerminator = true); +std::unique_ptr open(std::string name); } // namespace tt::hal::uart diff --git a/TactilityHeadless/Private/Tactility/hal/gps/GpsInit.h b/TactilityHeadless/Private/Tactility/hal/gps/GpsInit.h index 56c0d192..97e099fc 100644 --- a/TactilityHeadless/Private/Tactility/hal/gps/GpsInit.h +++ b/TactilityHeadless/Private/Tactility/hal/gps/GpsInit.h @@ -2,6 +2,8 @@ #include "Tactility/hal/gps/GpsDevice.h" +namespace tt::hal::uart { class Uart; } + namespace tt::hal::gps { /** @@ -14,6 +16,6 @@ bool init(const std::vector& configurations); /** * Init sequence on UART for a specific GPS model. */ -bool init(uart_port_t port, GpsModel type); +bool init(uart::Uart& uart, GpsModel type); } diff --git a/TactilityHeadless/Private/Tactility/hal/gps/Probe.h b/TactilityHeadless/Private/Tactility/hal/gps/Probe.h index b8dcdcf9..b9236e26 100644 --- a/TactilityHeadless/Private/Tactility/hal/gps/Probe.h +++ b/TactilityHeadless/Private/Tactility/hal/gps/Probe.h @@ -5,8 +5,6 @@ namespace tt::hal::gps { -struct GpsInfo; - -GpsModel probe(uart_port_t port); +GpsModel probe(uart::Uart& iart); } diff --git a/TactilityHeadless/Private/Tactility/hal/gps/Ublox.h b/TactilityHeadless/Private/Tactility/hal/gps/Ublox.h index 15015576..bb6745d3 100644 --- a/TactilityHeadless/Private/Tactility/hal/gps/Ublox.h +++ b/TactilityHeadless/Private/Tactility/hal/gps/Ublox.h @@ -17,11 +17,11 @@ template inline void sendPacket(uart_port_t port, uint8_t type, uint8_t id, uint8_t data[DataSize], const char* errorMessage, TickType_t timeout) { static uint8_t buffer[250] = {0}; size_t length = makePacket(type, id, data, DataSize, buffer); - hal::uart::writeBytes(port, buffer, length); +// hal::uart::writeBytes(port, buffer, length); } -GpsModel probe(uart_port_t port); +GpsModel probe(uart::Uart& uart); -bool init(uart_port_t port, GpsModel model); +bool init(uart::Uart& uart, GpsModel model); } // namespace tt::service::gps diff --git a/TactilityHeadless/Private/Tactility/hal/uart/UartEsp.h b/TactilityHeadless/Private/Tactility/hal/uart/UartEsp.h new file mode 100644 index 00000000..ac20293e --- /dev/null +++ b/TactilityHeadless/Private/Tactility/hal/uart/UartEsp.h @@ -0,0 +1,38 @@ +#pragma once +#ifdef ESP_PLATFORM + +#include "Tactility/Mutex.h" +#include "Tactility/hal/uart/Uart.h" +#include "Tactility/hal/uart/Configuration.h" + +namespace tt::hal::uart { + +class UartEsp final : public Uart { + +private: + + Mutex mutex; + const Configuration& configuration; + bool started = false; + +public: + + explicit UartEsp(const Configuration& configuration) : configuration(configuration) {} + + bool start() final; + bool isStarted() const final; + bool stop() final; + size_t readBytes(std::byte* buffer, size_t bufferSize, TickType_t timeout) final; + bool readByte(std::byte* output, TickType_t timeout) final; + size_t writeBytes(const std::byte* buffer, size_t bufferSize, TickType_t timeout) final; + size_t available(TickType_t timeout) final; + bool setBaudRate(uint32_t baudRate, TickType_t timeout) final; + uint32_t getBaudRate() final; + void flushInput() final; +}; + +std::unique_ptr create(const Configuration& configuration); + +} // namespace tt::hal::uart + +#endif \ No newline at end of file diff --git a/TactilityHeadless/Private/Tactility/hal/uart/UartPosix.h b/TactilityHeadless/Private/Tactility/hal/uart/UartPosix.h new file mode 100644 index 00000000..471bd2f7 --- /dev/null +++ b/TactilityHeadless/Private/Tactility/hal/uart/UartPosix.h @@ -0,0 +1,47 @@ +#pragma once +#ifndef ESP_PLATFORM + +#include "Tactility/Mutex.h" +#include "Tactility/hal/uart/Configuration.h" +#include "Tactility/hal/uart/Uart.h" + +#include + +namespace tt::hal::uart { + +class UartPosix final : public Uart { + +private: + struct AutoCloseFileDeleter { + void operator()(FILE* file) { + fclose(file); + } + }; + + Mutex mutex; + const Configuration& configuration; + std::unique_ptr device; + + bool awaitAvailable(TickType_t timeout); + +public: + + explicit UartPosix(const Configuration& configuration) : configuration(configuration) {} + + bool start() final; + bool isStarted() const final; + bool stop() final; + size_t readBytes(std::byte* buffer, size_t bufferSize, TickType_t timeout) final; + bool readByte(std::byte* output, TickType_t timeout) final; + size_t writeBytes(const std::byte* buffer, size_t bufferSize, TickType_t timeout) final; + size_t available(TickType_t timeout) final; + bool setBaudRate(uint32_t baudRate, TickType_t timeout) final; + uint32_t getBaudRate() final; + void flushInput() final; +}; + +std::unique_ptr create(const Configuration& configuration); + +} // namespace tt::hal::uart + +#endif \ No newline at end of file diff --git a/TactilityHeadless/Source/hal/gps/GpsDevice.cpp b/TactilityHeadless/Source/hal/gps/GpsDevice.cpp index 382edeee..cc90b62e 100644 --- a/TactilityHeadless/Source/hal/gps/GpsDevice.cpp +++ b/TactilityHeadless/Source/hal/gps/GpsDevice.cpp @@ -1,6 +1,7 @@ #include "Tactility/hal/gps/GpsDevice.h" #include "Tactility/hal/gps/GpsInit.h" #include "Tactility/hal/gps/Probe.h" +#include "Tactility/hal/uart/Uart.h" #include #include @@ -51,15 +52,25 @@ int32_t GpsDevice::threadMainStatic(void* parameter) { int32_t GpsDevice::threadMain() { uint8_t buffer[GPS_UART_BUFFER_SIZE]; - if (!uart::setBaudRate(configuration.uartPort, (int)configuration.baudRate)) { - TT_LOG_E(TAG, "Failed to set baud rate to %lu", configuration.baudRate); + auto uart = uart::open(configuration.uartName); + if (uart == nullptr) { + TT_LOG_E(TAG, "Failed to open UART %s", configuration.uartName.c_str()); return -1; } + if (!uart->start()) { + TT_LOG_E(TAG, "Failed to start UART %s", configuration.uartName.c_str()); + return -1; + } + + if (!uart->setBaudRate((int)configuration.baudRate)) { + TT_LOG_E(TAG, "Failed to set baud rate to %lu for UART %s", configuration.baudRate, configuration.uartName.c_str()); + return -1; + } GpsModel model = configuration.model; if (model == GpsModel::Unknown) { - model = probe(configuration.uartPort); + model = probe(*uart); if (model == GpsModel::Unknown) { TT_LOG_E(TAG, "Probe failed"); setState(State::Error); @@ -71,7 +82,7 @@ int32_t GpsDevice::threadMain() { this->model = model; mutex.unlock(); - if (!init(configuration.uartPort, model)) { + if (!init(*uart, model)) { TT_LOG_E(TAG, "Init failed"); setState(State::Error); return -1; @@ -81,15 +92,16 @@ int32_t GpsDevice::threadMain() { // Reference: https://gpsd.gitlab.io/gpsd/NMEA.html while (!isThreadInterrupted()) { - size_t bytes_read = uart::readUntil(configuration.uartPort, (uint8_t*)buffer, GPS_UART_BUFFER_SIZE, '\n', 100 / portTICK_PERIOD_MS); + size_t bytes_read = uart->readUntil(reinterpret_cast(buffer), GPS_UART_BUFFER_SIZE, '\n', 100 / portTICK_PERIOD_MS); + + // Thread might've been interrupted in the meanwhile + if (isThreadInterrupted()) { + break; + } + if (bytes_read > 0U) { - // Thread might've been interrupted in the meanwhile - if (isThreadInterrupted()) { - break; - } - - TT_LOG_D(TAG, "%s", buffer); + TT_LOG_I(TAG, "%s", buffer); switch (minmea_sentence_id((char*)buffer, false)) { case MINMEA_SENTENCE_RMC: @@ -124,6 +136,10 @@ int32_t GpsDevice::threadMain() { } } + if (uart->isStarted() && !uart->stop()) { + TT_LOG_W(TAG, "Failed to stop UART %s", configuration.uartName.c_str()); + } + return 0; } @@ -136,16 +152,6 @@ bool GpsDevice::start() { return true; } - if (uart::isStarted(configuration.uartPort)) { - TT_LOG_E(TAG, "UART %d already in use", configuration.uartPort); - return false; - } - - if (!uart::start(configuration.uartPort)) { - TT_LOG_E(TAG, "UART %d failed to start", configuration.uartPort); - return false; - } - threadInterrupted = false; TT_LOG_I(TAG, "Starting thread"); @@ -186,14 +192,6 @@ bool GpsDevice::stop() { } } - if (uart::isStarted(configuration.uartPort)) { - if (!uart::stop(configuration.uartPort)) { - TT_LOG_E(TAG, "UART %d failed to stop", configuration.uartPort); - setState(State::Error); - return false; - } - } - setState(State::Off); return true; diff --git a/TactilityHeadless/Source/hal/gps/GpsInit.cpp b/TactilityHeadless/Source/hal/gps/GpsInit.cpp index 955e3a74..954abf66 100644 --- a/TactilityHeadless/Source/hal/gps/GpsInit.cpp +++ b/TactilityHeadless/Source/hal/gps/GpsInit.cpp @@ -8,12 +8,12 @@ namespace tt::hal::gps { -bool initMtk(uart_port_t port); -bool initMtkL76b(uart_port_t port); -bool initMtkPa1616s(uart_port_t port); -bool initAtgm336h(uart_port_t port); -bool initUc6580(uart_port_t port); -bool initAg33xx(uart_port_t port); +bool initMtk(uart::Uart& uart); +bool initMtkL76b(uart::Uart& uart); +bool initMtkPa1616s(uart::Uart& uart); +bool initAtgm336h(uart::Uart& uart); +bool initUc6580(uart::Uart& uart); +bool initAg33xx(uart::Uart& uart); // region CAS @@ -70,7 +70,7 @@ static uint8_t makeCASPacket(uint8_t* buffer, uint8_t class_id, uint8_t msg_id, return (payload_size + 10); } -GpsResponse getACKCas(uart_port_t port, uint8_t class_id, uint8_t msg_id, uint32_t waitMillis) +GpsResponse getACKCas(uart::Uart& uart, uint8_t class_id, uint8_t msg_id, uint32_t waitMillis) { uint32_t startTime = kernel::getMillis(); uint8_t buffer[CAS_ACK_NACK_MSG_SIZE] = {0}; @@ -84,8 +84,8 @@ GpsResponse getACKCas(uart_port_t port, uint8_t class_id, uint8_t msg_id, uint32 // ACK-ACK | 0xBA | 0xCE | 0x04 | 0x00 | 0x05 | 0x01 | 0xXX | 0xXX | 0x00 | 0x00 | 0xXX | 0xXX | 0xXX | 0xXX | while (kernel::getTicks() - startTime < waitMillis) { - if (uart::available(port)) { - uart::readByte(port, &buffer[bufferPos++]); + if (uart.available()) { + uart.readByte(&buffer[bufferPos++]); // keep looking at the first two bytes of buffer until // we have found the CAS frame header (0xBA, 0xCE), if not @@ -144,90 +144,90 @@ bool init(const std::vector& configurations) { return true; } -bool init(uart_port_t port, GpsModel type) { +bool init(uart::Uart& uart, GpsModel type) { switch (type) { case GpsModel::Unknown: tt_crash(); case GpsModel::AG3335: case GpsModel::AG3352: - return initAg33xx(port); + return initAg33xx(uart); case GpsModel::ATGM336H: - return initAtgm336h(port); + return initAtgm336h(uart); case GpsModel::LS20031: - break; + return true; case GpsModel::MTK: - return initMtk(port); + return initMtk(uart); case GpsModel::MTK_L76B: - return initMtkL76b(port); + return initMtkL76b(uart); case GpsModel::MTK_PA1616S: - return initMtkPa1616s(port); + return initMtkPa1616s(uart); case GpsModel::UBLOX6: case GpsModel::UBLOX7: case GpsModel::UBLOX8: case GpsModel::UBLOX9: case GpsModel::UBLOX10: - return ublox::init(port, type); + return ublox::init(uart, type); case GpsModel::UC6580: - return initUc6580(port); + return initUc6580(uart); } TT_LOG_I(TAG, "Init not implemented %d", static_cast(type)); return false; } -bool initAg33xx(uart_port_t port) { - uart::writeString(port, "$PAIR066,1,0,1,0,0,1*3B\r\n"); // Enable GPS+GALILEO+NAVIC +bool initAg33xx(uart::Uart& uart) { + uart.writeString("$PAIR066,1,0,1,0,0,1*3B\r\n"); // Enable GPS+GALILEO+NAVIC // Configure NMEA (sentences will output once per fix) - uart::writeString(port, "$PAIR062,0,1*3F\r\n"); // GGA ON - uart::writeString(port, "$PAIR062,1,0*3F\r\n"); // GLL OFF - uart::writeString(port, "$PAIR062,2,0*3C\r\n"); // GSA OFF - uart::writeString(port, "$PAIR062,3,0*3D\r\n"); // GSV OFF - uart::writeString(port, "$PAIR062,4,1*3B\r\n"); // RMC ON - uart::writeString(port, "$PAIR062,5,0*3B\r\n"); // VTG OFF - uart::writeString(port, "$PAIR062,6,0*38\r\n"); // ZDA ON + uart.writeString("$PAIR062,0,1*3F\r\n"); // GGA ON + uart.writeString("$PAIR062,1,0*3F\r\n"); // GLL OFF + uart.writeString("$PAIR062,2,0*3C\r\n"); // GSA OFF + uart.writeString("$PAIR062,3,0*3D\r\n"); // GSV OFF + uart.writeString("$PAIR062,4,1*3B\r\n"); // RMC ON + uart.writeString("$PAIR062,5,0*3B\r\n"); // VTG OFF + uart.writeString("$PAIR062,6,0*38\r\n"); // ZDA ON kernel::delayMillis(250); - uart::writeString(port, "$PAIR513*3D\r\n"); // save configuration + uart.writeString("$PAIR513*3D\r\n"); // save configuration return true; } -bool initUc6580(uart_port_t port) { +bool initUc6580(uart::Uart& uart) { // The Unicore UC6580 can use a lot of sat systems, enable it to // use GPS L1 & L5 + BDS B1I & B2a + GLONASS L1 + GALILEO E1 & E5a + SBAS + QZSS // This will reset the receiver, so wait a bit afterwards // The paranoid will wait for the OK*04 confirmation response after each command. - uart::writeString(port, "$CFGSYS,h35155\r\n"); + uart.writeString("$CFGSYS,h35155\r\n"); kernel::delayMillis(750); // Must be done after the CFGSYS command // Turn off GSV messages, we don't really care about which and where the sats are, maybe someday. - uart::writeString(port, "$CFGMSG,0,3,0\r\n"); + uart.writeString("$CFGMSG,0,3,0\r\n"); kernel::delayMillis(250); // Turn off GSA messages, TinyGPS++ doesn't use this message. - uart::writeString(port, "$CFGMSG,0,2,0\r\n"); + uart.writeString("$CFGMSG,0,2,0\r\n"); kernel::delayMillis(250); // Turn off NOTICE __TXT messages, these may provide Unicore some info but we don't care. - uart::writeString(port, "$CFGMSG,6,0,0\r\n"); + uart.writeString("$CFGMSG,6,0,0\r\n"); kernel::delayMillis(250); - uart::writeString(port, "$CFGMSG,6,1,0\r\n"); + uart.writeString("$CFGMSG,6,1,0\r\n"); kernel::delayMillis(250); return true; } -bool initAtgm336h(uart_port_t port) { +bool initAtgm336h(uart::Uart& uart) { uint8_t buffer[256]; // Set the intial configuration of the device - these _should_ work for most AT6558 devices int msglen = makeCASPacket(buffer, 0x06, 0x07, sizeof(_message_CAS_CFG_NAVX_CONF), _message_CAS_CFG_NAVX_CONF); - uart::writeBytes(port, buffer, msglen); - if (getACKCas(port, 0x06, 0x07, 250) != GpsResponse::Ok) { + uart.writeBytes(buffer, msglen); + if (getACKCas(uart, 0x06, 0x07, 250) != GpsResponse::Ok) { TT_LOG_W(TAG, "ATGM336H: Could not set Config"); } // Set the update frequence to 1Hz msglen = makeCASPacket(buffer, 0x06, 0x04, sizeof(_message_CAS_CFG_RATE_1HZ), _message_CAS_CFG_RATE_1HZ); - uart::writeBytes(port, buffer, msglen); - if (getACKCas(port, 0x06, 0x04, 250) != GpsResponse::Ok) { + uart.writeBytes(buffer, msglen); + if (getACKCas(uart, 0x06, 0x04, 250) != GpsResponse::Ok) { TT_LOG_W(TAG, "ATGM336H: Could not set Update Frequency"); } @@ -238,61 +238,61 @@ bool initAtgm336h(uart_port_t port) { // Construct a CAS-CFG-MSG packet uint8_t cas_cfg_msg_packet[] = {0x4e, fields[i], 0x01, 0x00}; msglen = makeCASPacket(buffer, 0x06, 0x01, sizeof(cas_cfg_msg_packet), cas_cfg_msg_packet); - uart::writeBytes(port, buffer, msglen); - if (getACKCas(port, 0x06, 0x01, 250) != GpsResponse::Ok) { + uart.writeBytes(buffer, msglen); + if (getACKCas(uart, 0x06, 0x01, 250) != GpsResponse::Ok) { TT_LOG_W(TAG, "ATGM336H: Could not enable NMEA MSG: %d", fields[i]); } } return true; } -bool initMtkPa1616s(uart_port_t port) { +bool initMtkPa1616s(uart::Uart& uart) { // PA1616S is used in some GPS breakout boards from Adafruit // PA1616S does not have GLONASS capability. PA1616D does, but is not implemented here. - uart::writeString(port, "$PMTK353,1,0,0,0,0*2A\r\n"); + uart.writeString("$PMTK353,1,0,0,0,0*2A\r\n"); // Above command will reset the GPS and takes longer before it will accept new commands kernel::delayMillis(1000); // Only ask for RMC and GGA (GNRMC and GNGGA) - uart::writeString(port, "$PMTK314,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0*28\r\n"); + uart.writeString("$PMTK314,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0*28\r\n"); kernel::delayMillis(250); // Enable SBAS / WAAS - uart::writeString(port, "$PMTK301,2*2E\r\n"); + uart.writeString("$PMTK301,2*2E\r\n"); kernel::delayMillis(250); return true; } -bool initMtkL76b(uart_port_t port) { +bool initMtkL76b(uart::Uart& uart) { // Waveshare Pico-GPS hat uses the L76B with 9600 baud // Initialize the L76B Chip, use GPS + GLONASS // See note in L76_Series_GNSS_Protocol_Specification, chapter 3.29 - uart::writeString(port, "$PMTK353,1,1,0,0,0*2B\r\n"); + uart.writeString("$PMTK353,1,1,0,0,0*2B\r\n"); // Above command will reset the GPS and takes longer before it will accept new commands kernel::delayMillis(1000); // only ask for RMC and GGA (GNRMC and GNGGA) // See note in L76_Series_GNSS_Protocol_Specification, chapter 2.1 - uart::writeString(port, "$PMTK314,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0*28\r\n"); + uart.writeString("$PMTK314,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0*28\r\n"); kernel::delayMillis(250); // Enable SBAS - uart::writeString(port, "$PMTK301,2*2E\r\n"); + uart.writeString("$PMTK301,2*2E\r\n"); kernel::delayMillis(250); // Enable PPS for 2D/3D fix only - uart::writeString(port, "$PMTK285,3,100*3F\r\n"); + uart.writeString("$PMTK285,3,100*3F\r\n"); kernel::delayMillis(250); // Switch to Fitness Mode, for running and walking purpose with low speed (<5 m/s) - uart::writeString(port, "$PMTK886,1*29\r\n"); + uart.writeString("$PMTK886,1*29\r\n"); kernel::delayMillis(250); return true; } -bool initMtk(uart_port_t port) { +bool initMtk(uart::Uart& uart) { // Initialize the L76K Chip, use GPS + GLONASS + BEIDOU - uart::writeString(port, "$PCAS04,7*1E\r\n"); + uart.writeString("$PCAS04,7*1E\r\n"); kernel::delayMillis(250); // only ask for RMC and GGA - uart::writeString(port, "$PCAS03,1,0,0,0,1,0,0,0,0,0,,,0,0*02\r\n"); + uart.writeString("$PCAS03,1,0,0,0,1,0,0,0,0,0,,,0,0*02\r\n"); kernel::delayMillis(250); // Switch to Vehicle Mode, since SoftRF enables Aviation < 2g - uart::writeString(port, "$PCAS11,3*1E\r\n"); + uart.writeString("$PCAS11,3*1E\r\n"); kernel::delayMillis(250); return true; } diff --git a/TactilityHeadless/Source/hal/gps/Probe.cpp b/TactilityHeadless/Source/hal/gps/Probe.cpp index 46b94310..71142c37 100644 --- a/TactilityHeadless/Source/hal/gps/Probe.cpp +++ b/TactilityHeadless/Source/hal/gps/Probe.cpp @@ -39,7 +39,7 @@ char* strnstr(const char* s, const char* find, size_t slen) { /** * From: https://github.com/meshtastic/firmware/blob/f81d3b045dd1b7e3ca7870af3da915ff4399ea98/src/gps/GPS.cpp */ -GpsResponse getAck(uart_port_t port, const char* message, uint32_t waitMillis) { +GpsResponse getAck(uart::Uart& uart, const char* message, uint32_t waitMillis) { uint8_t buffer[768] = {0}; uint8_t b; int bytesRead = 0; @@ -48,8 +48,8 @@ GpsResponse getAck(uart_port_t port, const char* message, uint32_t waitMillis) { std::string debugmsg = ""; #endif while (kernel::getMillis() < startTimeout) { - if (uart::available(port)) { - uart::readByte(port, &b); + if (uart.available()) { + uart.readByte(&b); #ifdef GPS_DEBUG debugmsg += vformat("%c", (b >= 32 && b <= 126) ? b : '.'); @@ -77,12 +77,12 @@ GpsResponse getAck(uart_port_t port, const char* message, uint32_t waitMillis) { /** * From: https://github.com/meshtastic/firmware/blob/f81d3b045dd1b7e3ca7870af3da915ff4399ea98/src/gps/GPS.cpp */ -#define PROBE_SIMPLE(PORT, CHIP, TOWRITE, RESPONSE, DRIVER, TIMEOUT, ...) \ +#define PROBE_SIMPLE(UART, CHIP, TOWRITE, RESPONSE, DRIVER, TIMEOUT, ...) \ do { \ TT_LOG_I(TAG, "Probing for %s (%s)", CHIP, TOWRITE); \ - uart::flushInput(PORT); \ - uart::writeString(PORT, TOWRITE "\r\n", TIMEOUT); \ - if (getAck(PORT, RESPONSE, TIMEOUT) == GpsResponse::Ok) { \ + UART.flushInput(); \ + UART.writeString(TOWRITE "\r\n", TIMEOUT); \ + if (getAck(UART, RESPONSE, TIMEOUT) == GpsResponse::Ok) { \ TT_LOG_I(TAG, "Probe detected %s %s", CHIP, #DRIVER); \ return DRIVER; \ } \ @@ -91,48 +91,48 @@ GpsResponse getAck(uart_port_t port, const char* message, uint32_t waitMillis) { /** * From: https://github.com/meshtastic/firmware/blob/f81d3b045dd1b7e3ca7870af3da915ff4399ea98/src/gps/GPS.cpp */ -GpsModel probe(uart_port_t port) { +GpsModel probe(uart::Uart& uart) { // Close all NMEA sentences, valid for L76K, ATGM336H (and likely other AT6558 devices) - uart::writeString(port, "$PCAS03,0,0,0,0,0,0,0,0,0,0,,,0,0*02\r\n"); + uart.writeString("$PCAS03,0,0,0,0,0,0,0,0,0,0,,,0,0*02\r\n"); kernel::delayMillis(20); // Close NMEA sequences on Ublox - uart::writeString(port, "$PUBX,40,GLL,0,0,0,0,0,0*5C\r\n"); - uart::writeString(port, "$PUBX,40,GSV,0,0,0,0,0,0*59\r\n"); - uart::writeString(port, "$PUBX,40,VTG,0,0,0,0,0,0*5E\r\n"); + uart.writeString("$PUBX,40,GLL,0,0,0,0,0,0*5C\r\n"); + uart.writeString("$PUBX,40,GSV,0,0,0,0,0,0*59\r\n"); + uart.writeString("$PUBX,40,VTG,0,0,0,0,0,0*5E\r\n"); kernel::delayMillis(20); // Unicore UFirebirdII Series: UC6580, UM620, UM621, UM670A, UM680A, or UM681A - PROBE_SIMPLE(port, "UC6580", "$PDTINFO", "UC6580", GpsModel::UC6580, 500); - PROBE_SIMPLE(port, "UM600", "$PDTINFO", "UM600", GpsModel::UC6580, 500); - PROBE_SIMPLE(port, "ATGM336H", "$PCAS06,1*1A", "$GPTXT,01,01,02,HW=ATGM336H", GpsModel::ATGM336H, 500); + PROBE_SIMPLE(uart, "UC6580", "$PDTINFO", "UC6580", GpsModel::UC6580, 500); + PROBE_SIMPLE(uart, "UM600", "$PDTINFO", "UM600", GpsModel::UC6580, 500); + PROBE_SIMPLE(uart, "ATGM336H", "$PCAS06,1*1A", "$GPTXT,01,01,02,HW=ATGM336H", GpsModel::ATGM336H, 500); /* ATGM332D series (-11(GPS), -21(BDS), -31(GPS+BDS), -51(GPS+GLONASS), -71-0(GPS+BDS+GLONASS)) based on AT6558 */ - PROBE_SIMPLE(port, "ATGM332D", "$PCAS06,1*1A", "$GPTXT,01,01,02,HW=ATGM332D", GpsModel::ATGM336H, 500); + PROBE_SIMPLE(uart, "ATGM332D", "$PCAS06,1*1A", "$GPTXT,01,01,02,HW=ATGM332D", GpsModel::ATGM336H, 500); /* Airoha (Mediatek) AG3335A/M/S, A3352Q, Quectel L89 2.0, SimCom SIM65M */ - uart::writeString(port, "$PAIR062,2,0*3C\r\n"); // GSA OFF to reduce volume - uart::writeString(port, "$PAIR062,3,0*3D\r\n"); // GSV OFF to reduce volume - uart::writeString(port, "$PAIR513*3D\r\n"); // save configuration - PROBE_SIMPLE(port, "AG3335", "$PAIR021*39", "$PAIR021,AG3335", GpsModel::AG3335, 500); - PROBE_SIMPLE(port, "AG3352", "$PAIR021*39", "$PAIR021,AG3352", GpsModel::AG3352, 500); - PROBE_SIMPLE(port, "LC86", "$PQTMVERNO*58", "$PQTMVERNO,LC86", GpsModel::AG3352, 500); + uart.writeString("$PAIR062,2,0*3C\r\n"); // GSA OFF to reduce volume + uart.writeString("$PAIR062,3,0*3D\r\n"); // GSV OFF to reduce volume + uart.writeString("$PAIR513*3D\r\n"); // save configuration + PROBE_SIMPLE(uart, "AG3335", "$PAIR021*39", "$PAIR021,AG3335", GpsModel::AG3335, 500); + PROBE_SIMPLE(uart, "AG3352", "$PAIR021*39", "$PAIR021,AG3352", GpsModel::AG3352, 500); + PROBE_SIMPLE(uart, "LC86", "$PQTMVERNO*58", "$PQTMVERNO,LC86", GpsModel::AG3352, 500); - PROBE_SIMPLE(port, "L76K", "$PCAS06,0*1B", "$GPTXT,01,01,02,SW=", GpsModel::MTK, 500); + PROBE_SIMPLE(uart, "L76K", "$PCAS06,0*1B", "$GPTXT,01,01,02,SW=", GpsModel::MTK, 500); // Close all NMEA sentences, valid for L76B MTK platform (Waveshare Pico GPS) - uart::writeString(port, "$PMTK514,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0*2E\r\n"); + uart.writeString("$PMTK514,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0*2E\r\n"); kernel::delayMillis(20); - PROBE_SIMPLE(port, "L76B", "$PMTK605*31", "Quectel-L76B", GpsModel::MTK_L76B, 500); - PROBE_SIMPLE(port, "PA1616S", "$PMTK605*31", "1616S", GpsModel::MTK_PA1616S, 500); + PROBE_SIMPLE(uart, "L76B", "$PMTK605*31", "Quectel-L76B", GpsModel::MTK_L76B, 500); + PROBE_SIMPLE(uart, "PA1616S", "$PMTK605*31", "1616S", GpsModel::MTK_PA1616S, 500); - auto ublox_result = ublox::probe(port); + auto ublox_result = ublox::probe(uart); if (ublox_result != GpsModel::Unknown) { return ublox_result; } else { - TT_LOG_W(TAG, "No GNSS Module (baudrate %lu)", uart::getBaudRate(port)); + TT_LOG_W(TAG, "No GNSS Module (baudrate %lu)", uart.getBaudRate()); return GpsModel::Unknown; } } diff --git a/TactilityHeadless/Source/hal/gps/Ublox.cpp b/TactilityHeadless/Source/hal/gps/Ublox.cpp index 6ffbc775..84e85578 100644 --- a/TactilityHeadless/Source/hal/gps/Ublox.cpp +++ b/TactilityHeadless/Source/hal/gps/Ublox.cpp @@ -1,20 +1,21 @@ #include "Tactility/hal/gps/Ublox.h" #include "Tactility/hal/gps/UbloxMessages.h" +#include "Tactility/hal/uart/Uart.h" #include #define TAG "ublox" namespace tt::hal::gps::ublox { -bool initUblox6(uart_port_t port); -bool initUblox789(uart_port_t port, GpsModel model); -bool initUblox10(uart_port_t port); +bool initUblox6(uart::Uart& uart); +bool initUblox789(uart::Uart& uart, GpsModel model); +bool initUblox10(uart::Uart& uart); -#define SEND_UBX_PACKET(PORT, BUFFER, TYPE, ID, DATA, ERRMSG, TIMEOUT) \ +#define SEND_UBX_PACKET(UART, BUFFER, TYPE, ID, DATA, ERRMSG, TIMEOUT) \ do { \ auto msglen = makePacket(TYPE, ID, DATA, sizeof(DATA), BUFFER); \ - uart::writeBytes(PORT, BUFFER, sizeof(BUFFER)); \ - if (getAck(PORT, TYPE, ID, TIMEOUT) != GpsResponse::Ok) { \ + UART.writeBytes(BUFFER, sizeof(BUFFER)); \ + if (getAck(UART, TYPE, ID, TIMEOUT) != GpsResponse::Ok) { \ TT_LOG_I(TAG, "Sending packet failed: %s", #ERRMSG); \ } \ } while (0) @@ -52,7 +53,7 @@ uint8_t makePacket(uint8_t classId, uint8_t messageId, const uint8_t* payload, u return (payloadSize + 8U); } -GpsResponse getAck(uart_port_t port, uint8_t class_id, uint8_t msg_id, uint32_t waitMillis) { +GpsResponse getAck(uart::Uart& uart, uint8_t class_id, uint8_t msg_id, uint32_t waitMillis) { uint8_t b; uint8_t ack = 0; const uint8_t ackP[2] = {class_id, msg_id}; @@ -82,8 +83,8 @@ GpsResponse getAck(uart_port_t port, uint8_t class_id, uint8_t msg_id, uint32_t #endif return GpsResponse::Ok; // ACK received } - if (uart::available(port)) { - uart::readByte(port, &b); + if (uart.available()) { + uart.readByte(&b); if (b == frame_errors[sCounter]) { sCounter++; if (sCounter == 26) { @@ -120,15 +121,15 @@ GpsResponse getAck(uart_port_t port, uint8_t class_id, uint8_t msg_id, uint32_t return GpsResponse::None; // No response received within timeout } -static int getAck(uart_port_t port, uint8_t* buffer, uint16_t size, uint8_t requestedClass, uint8_t requestedId, TickType_t timeout) { +static int getAck(uart::Uart& uart, uint8_t* buffer, uint16_t size, uint8_t requestedClass, uint8_t requestedId, TickType_t timeout) { uint16_t ubxFrameCounter = 0; uint32_t startTime = kernel::getTicks(); uint16_t needRead = 0; while (kernel::getTicks() - startTime < timeout) { - while (uart::available(port)) { + while (uart.available()) { uint8_t c; - uart::readByte(port, &c); + uart.readByte(&c); switch (ubxFrameCounter) { case 0: @@ -170,7 +171,7 @@ static int getAck(uart_port_t port, uint8_t* buffer, uint16_t size, uint8_t requ ubxFrameCounter = 0; break; } - auto read_bytes = uart::readBytes(port, buffer, needRead, 250 / portTICK_PERIOD_MS); + auto read_bytes = uart.readBytes(buffer, needRead, 250 / portTICK_PERIOD_MS); if (read_bytes != needRead) { ubxFrameCounter = 0; } else { @@ -201,20 +202,20 @@ static struct uBloxGnssModelInfo { uint8_t protocol_version; } ublox_info; -GpsModel probe(uart_port_t port) { +GpsModel probe(uart::Uart& uart) { TT_LOG_I(TAG, "Probing for U-blox"); uint8_t cfg_rate[] = {0xB5, 0x62, 0x06, 0x08, 0x00, 0x00, 0x00, 0x00}; checksum(cfg_rate, sizeof(cfg_rate)); - uart::flushInput(port); - uart::writeBytes(port, cfg_rate, sizeof(cfg_rate)); + uart.flushInput(); + uart.writeBytes(cfg_rate, sizeof(cfg_rate)); // Check that the returned response class and message ID are correct - GpsResponse response = getAck(port, 0x06, 0x08, 750); + GpsResponse response = getAck(uart, 0x06, 0x08, 750); if (response == GpsResponse::None) { - TT_LOG_W(TAG, "No GNSS Module (baudrate %lu)", uart::getBaudRate(port)); + TT_LOG_W(TAG, "No GNSS Module (baudrate %lu)", uart.getBaudRate()); return GpsModel::Unknown; } else if (response == GpsResponse::FrameErrors) { - TT_LOG_W(TAG, "UBlox Frame Errors (baudrate %lu)", uart::getBaudRate(port)); + TT_LOG_W(TAG, "UBlox Frame Errors (baudrate %lu)", uart.getBaudRate()); } uint8_t buffer[256]; @@ -227,10 +228,10 @@ GpsModel probe(uart_port_t port) { }; // Get Ublox gnss module hardware and software info checksum(_message_MONVER, sizeof(_message_MONVER)); - uart::flushInput(port); - uart::writeBytes(port, _message_MONVER, sizeof(_message_MONVER)); + uart.flushInput(); + uart.writeBytes(_message_MONVER, sizeof(_message_MONVER)); - uint16_t ack_response_len = getAck(port, buffer, sizeof(buffer), 0x0A, 0x04, 1200); + uint16_t ack_response_len = getAck(uart, buffer, sizeof(buffer), 0x0A, 0x04, 1200); if (ack_response_len) { uint16_t position = 0; for (char& i: ublox_info.swVersion) { @@ -300,67 +301,67 @@ GpsModel probe(uart_port_t port) { return GpsModel::Unknown; } -bool init(uart_port_t port, GpsModel model) { +bool init(uart::Uart& uart, GpsModel model) { TT_LOG_I(TAG, "U-blox init"); switch (model) { case GpsModel::UBLOX6: - return initUblox6(port); + return initUblox6(uart); case GpsModel::UBLOX7: case GpsModel::UBLOX8: case GpsModel::UBLOX9: - return initUblox789(port, model); + return initUblox789(uart, model); case GpsModel::UBLOX10: - return initUblox10(port); + return initUblox10(uart); default: TT_LOG_E(TAG, "Unknown or unsupported U-blox model"); return false; } } -bool initUblox10(uart_port_t port) { +bool initUblox10(uart::Uart& uart) { uint8_t buffer[256]; kernel::delayMillis(1000); - uart::flushInput(port); - SEND_UBX_PACKET(port, buffer, 0x06, 0x8A, _message_VALSET_DISABLE_NMEA_RAM, "disable NMEA messages in M10 RAM", 300); + uart.flushInput(); + SEND_UBX_PACKET(uart, buffer, 0x06, 0x8A, _message_VALSET_DISABLE_NMEA_RAM, "disable NMEA messages in M10 RAM", 300); kernel::delayMillis(750); - uart::flushInput(port); - SEND_UBX_PACKET(port, buffer, 0x06, 0x8A, _message_VALSET_DISABLE_NMEA_BBR, "disable NMEA messages in M10 BBR", 300); + uart.flushInput(); + SEND_UBX_PACKET(uart, buffer, 0x06, 0x8A, _message_VALSET_DISABLE_NMEA_BBR, "disable NMEA messages in M10 BBR", 300); kernel::delayMillis(750); - uart::flushInput(port); - SEND_UBX_PACKET(port, buffer, 0x06, 0x8A, _message_VALSET_DISABLE_TXT_INFO_RAM, "disable Info messages for M10 GPS RAM", 300); + uart.flushInput(); + SEND_UBX_PACKET(uart, buffer, 0x06, 0x8A, _message_VALSET_DISABLE_TXT_INFO_RAM, "disable Info messages for M10 GPS RAM", 300); kernel::delayMillis(750); - uart::flushInput(port); - SEND_UBX_PACKET(port, buffer, 0x06, 0x8A, _message_VALSET_DISABLE_TXT_INFO_BBR, "disable Info messages for M10 GPS BBR", 300); + uart.flushInput(); + SEND_UBX_PACKET(uart, buffer, 0x06, 0x8A, _message_VALSET_DISABLE_TXT_INFO_BBR, "disable Info messages for M10 GPS BBR", 300); kernel::delayMillis(750); - SEND_UBX_PACKET(port, buffer, 0x06, 0x8A, _message_VALSET_PM_RAM, "enable powersave for M10 GPS RAM", 300); + SEND_UBX_PACKET(uart, buffer, 0x06, 0x8A, _message_VALSET_PM_RAM, "enable powersave for M10 GPS RAM", 300); kernel::delayMillis(750); - SEND_UBX_PACKET(port, buffer, 0x06, 0x8A, _message_VALSET_PM_BBR, "enable powersave for M10 GPS BBR", 300); + SEND_UBX_PACKET(uart, buffer, 0x06, 0x8A, _message_VALSET_PM_BBR, "enable powersave for M10 GPS BBR", 300); kernel::delayMillis(750); - SEND_UBX_PACKET(port, buffer, 0x06, 0x8A, _message_VALSET_ITFM_RAM, "enable jam detection M10 GPS RAM", 300); + SEND_UBX_PACKET(uart, buffer, 0x06, 0x8A, _message_VALSET_ITFM_RAM, "enable jam detection M10 GPS RAM", 300); kernel::delayMillis(750); - SEND_UBX_PACKET(port, buffer, 0x06, 0x8A, _message_VALSET_ITFM_BBR, "enable jam detection M10 GPS BBR", 300); + SEND_UBX_PACKET(uart, buffer, 0x06, 0x8A, _message_VALSET_ITFM_BBR, "enable jam detection M10 GPS BBR", 300); kernel::delayMillis(750); // Here is where the init commands should go to do further M10 initialization. - SEND_UBX_PACKET(port, buffer, 0x06, 0x8A, _message_VALSET_DISABLE_SBAS_RAM, "disable SBAS M10 GPS RAM", 300); + SEND_UBX_PACKET(uart, buffer, 0x06, 0x8A, _message_VALSET_DISABLE_SBAS_RAM, "disable SBAS M10 GPS RAM", 300); kernel::delayMillis(750); // will cause a receiver restart so wait a bit - SEND_UBX_PACKET(port, buffer, 0x06, 0x8A, _message_VALSET_DISABLE_SBAS_BBR, "disable SBAS M10 GPS BBR", 300); + SEND_UBX_PACKET(uart, buffer, 0x06, 0x8A, _message_VALSET_DISABLE_SBAS_BBR, "disable SBAS M10 GPS BBR", 300); kernel::delayMillis(750); // will cause a receiver restart so wait a bit // Done with initialization // Enable wanted NMEA messages in BBR layer so they will survive a periodic sleep - SEND_UBX_PACKET(port, buffer, 0x06, 0x8A, _message_VALSET_ENABLE_NMEA_BBR, "enable messages for M10 GPS BBR", 300); + SEND_UBX_PACKET(uart, buffer, 0x06, 0x8A, _message_VALSET_ENABLE_NMEA_BBR, "enable messages for M10 GPS BBR", 300); kernel::delayMillis(750); // Enable wanted NMEA messages in RAM layer - SEND_UBX_PACKET(port, buffer, 0x06, 0x8A, _message_VALSET_ENABLE_NMEA_RAM, "enable messages for M10 GPS RAM", 500); + SEND_UBX_PACKET(uart, buffer, 0x06, 0x8A, _message_VALSET_ENABLE_NMEA_RAM, "enable messages for M10 GPS RAM", 500); kernel::delayMillis(750); // As the M10 has no flash, the best we can do to preserve the config is to set it in RAM and BBR. // BBR will survive a restart, and power off for a while, but modules with small backup // batteries or super caps will not retain the config for a long power off time. auto packet_size = makePacket(0x06, 0x09, _message_SAVE_10, sizeof(_message_SAVE_10), buffer); - uart::writeBytes(port, buffer, packet_size); - if (getAck(port, 0x06, 0x09, 2000) != GpsResponse::Ok) { + uart.writeBytes(buffer, packet_size); + if (getAck(uart, 0x06, 0x09, 2000) != GpsResponse::Ok) { TT_LOG_W(TAG, "Unable to save GNSS module config"); } else { TT_LOG_I(TAG, "GNSS module configuration saved!"); @@ -368,18 +369,18 @@ bool initUblox10(uart_port_t port) { return true; } -bool initUblox789(uart_port_t port, GpsModel model) { +bool initUblox789(uart::Uart& uart, GpsModel model) { uint8_t buffer[256]; if (model == GpsModel::UBLOX7) { TT_LOG_D(TAG, "Set GPS+SBAS"); auto msglen = makePacket(0x06, 0x3e, _message_GNSS_7, sizeof(_message_GNSS_7), buffer); - uart::writeBytes(port, buffer, msglen); + uart.writeBytes(buffer, msglen); } else { // 8,9 auto msglen = makePacket(0x06, 0x3e, _message_GNSS_8, sizeof(_message_GNSS_8), buffer); - uart::writeBytes(port, buffer, msglen); + uart.writeBytes(buffer, msglen); } - if (getAck(port, 0x06, 0x3e, 800) == GpsResponse::NotAck) { + if (getAck(uart, 0x06, 0x3e, 800) == GpsResponse::NotAck) { // It's not critical if the module doesn't acknowledge this configuration. TT_LOG_D(TAG, "reconfigure GNSS - defaults maintained. Is this module GPS-only?"); } else { @@ -393,48 +394,48 @@ bool initUblox789(uart_port_t port, GpsModel model) { kernel::delayMillis(1000); } - uart::flushInput(port); + uart.flushInput(); - SEND_UBX_PACKET(port, buffer, 0x06, 0x02, _message_DISABLE_TXT_INFO, "disable text info messages", 500); + SEND_UBX_PACKET(uart, buffer, 0x06, 0x02, _message_DISABLE_TXT_INFO, "disable text info messages", 500); if (model == GpsModel::UBLOX8) { // 8 - uart::flushInput(port); - SEND_UBX_PACKET(port, buffer, 0x06, 0x39, _message_JAM_8, "enable interference resistance", 500); + uart.flushInput(); + SEND_UBX_PACKET(uart, buffer, 0x06, 0x39, _message_JAM_8, "enable interference resistance", 500); - uart::flushInput(port); - SEND_UBX_PACKET(port, buffer, 0x06, 0x23, _message_NAVX5_8, "configure NAVX5_8 settings", 500); + uart.flushInput(); + SEND_UBX_PACKET(uart, buffer, 0x06, 0x23, _message_NAVX5_8, "configure NAVX5_8 settings", 500); } else { // 6,7,9 - SEND_UBX_PACKET(port, buffer, 0x06, 0x39, _message_JAM_6_7, "enable interference resistance", 500); - SEND_UBX_PACKET(port, buffer, 0x06, 0x23, _message_NAVX5, "configure NAVX5 settings", 500); + SEND_UBX_PACKET(uart, buffer, 0x06, 0x39, _message_JAM_6_7, "enable interference resistance", 500); + SEND_UBX_PACKET(uart, buffer, 0x06, 0x23, _message_NAVX5, "configure NAVX5 settings", 500); } // Turn off unwanted NMEA messages, set update rate - SEND_UBX_PACKET(port, buffer, 0x06, 0x08, _message_1HZ, "set GPS update rate", 500); - SEND_UBX_PACKET(port, buffer, 0x06, 0x01, _message_GLL, "disable NMEA GLL", 500); - SEND_UBX_PACKET(port, buffer, 0x06, 0x01, _message_GSA, "enable NMEA GSA", 500); - SEND_UBX_PACKET(port, buffer, 0x06, 0x01, _message_GSV, "disable NMEA GSV", 500); - SEND_UBX_PACKET(port, buffer, 0x06, 0x01, _message_VTG, "disable NMEA VTG", 500); - SEND_UBX_PACKET(port, buffer, 0x06, 0x01, _message_RMC, "enable NMEA RMC", 500); - SEND_UBX_PACKET(port, buffer, 0x06, 0x01, _message_GGA, "enable NMEA GGA", 500); + SEND_UBX_PACKET(uart, buffer, 0x06, 0x08, _message_1HZ, "set GPS update rate", 500); + SEND_UBX_PACKET(uart, buffer, 0x06, 0x01, _message_GLL, "disable NMEA GLL", 500); + SEND_UBX_PACKET(uart, buffer, 0x06, 0x01, _message_GSA, "enable NMEA GSA", 500); + SEND_UBX_PACKET(uart, buffer, 0x06, 0x01, _message_GSV, "disable NMEA GSV", 500); + SEND_UBX_PACKET(uart, buffer, 0x06, 0x01, _message_VTG, "disable NMEA VTG", 500); + SEND_UBX_PACKET(uart, buffer, 0x06, 0x01, _message_RMC, "enable NMEA RMC", 500); + SEND_UBX_PACKET(uart, buffer, 0x06, 0x01, _message_GGA, "enable NMEA GGA", 500); if (ublox_info.protocol_version >= 18) { - uart::flushInput(port); - SEND_UBX_PACKET(port, buffer, 0x06, 0x86, _message_PMS, "enable powersave for GPS", 500); - SEND_UBX_PACKET(port, buffer, 0x06, 0x3B, _message_CFG_PM2, "enable powersave details for GPS", 500); + uart.flushInput(); + SEND_UBX_PACKET(uart, buffer, 0x06, 0x86, _message_PMS, "enable powersave for GPS", 500); + SEND_UBX_PACKET(uart, buffer, 0x06, 0x3B, _message_CFG_PM2, "enable powersave details for GPS", 500); // For M8 we want to enable NMEA version 4.10 so we can see the additional satellites. if (model == GpsModel::UBLOX8) { - uart::flushInput(port); - SEND_UBX_PACKET(port, buffer, 0x06, 0x17, _message_NMEA, "enable NMEA 4.10", 500); + uart.flushInput(); + SEND_UBX_PACKET(uart, buffer, 0x06, 0x17, _message_NMEA, "enable NMEA 4.10", 500); } } else { - SEND_UBX_PACKET(port, buffer, 0x06, 0x11, _message_CFG_RXM_PSM, "enable powersave mode for GPS", 500); - SEND_UBX_PACKET(port, buffer, 0x06, 0x3B, _message_CFG_PM2, "enable powersave details for GPS", 500); + SEND_UBX_PACKET(uart, buffer, 0x06, 0x11, _message_CFG_RXM_PSM, "enable powersave mode for GPS", 500); + SEND_UBX_PACKET(uart, buffer, 0x06, 0x3B, _message_CFG_PM2, "enable powersave details for GPS", 500); } auto packet_size = makePacket(0x06, 0x09, _message_SAVE, sizeof(_message_SAVE), buffer); - uart::writeBytes(port, buffer, packet_size); - if (getAck(port, 0x06, 0x09, 2000) != GpsResponse::Ok) { + uart.writeBytes(buffer, packet_size); + if (getAck(uart, 0x06, 0x09, 2000) != GpsResponse::Ok) { TT_LOG_W(TAG, "Unable to save GNSS module config"); } else { TT_LOG_I(TAG, "GNSS module configuration saved!"); @@ -442,33 +443,33 @@ bool initUblox789(uart_port_t port, GpsModel model) { return true; } -bool initUblox6(uart_port_t port) { +bool initUblox6(uart::Uart& uart) { uint8_t buffer[256]; - uart::flushInput(port); + uart.flushInput(); - SEND_UBX_PACKET(port, buffer, 0x06, 0x02, _message_DISABLE_TXT_INFO, "disable text info messages", 500); - SEND_UBX_PACKET(port, buffer, 0x06, 0x39, _message_JAM_6_7, "enable interference resistance", 500); - SEND_UBX_PACKET(port, buffer, 0x06, 0x23, _message_NAVX5, "configure NAVX5 settings", 500); + SEND_UBX_PACKET(uart, buffer, 0x06, 0x02, _message_DISABLE_TXT_INFO, "disable text info messages", 500); + SEND_UBX_PACKET(uart, buffer, 0x06, 0x39, _message_JAM_6_7, "enable interference resistance", 500); + SEND_UBX_PACKET(uart, buffer, 0x06, 0x23, _message_NAVX5, "configure NAVX5 settings", 500); // Turn off unwanted NMEA messages, set update rate - SEND_UBX_PACKET(port, buffer, 0x06, 0x08, _message_1HZ, "set GPS update rate", 500); - SEND_UBX_PACKET(port, buffer, 0x06, 0x01, _message_GLL, "disable NMEA GLL", 500); - SEND_UBX_PACKET(port, buffer, 0x06, 0x01, _message_GSA, "enable NMEA GSA", 500); - SEND_UBX_PACKET(port, buffer, 0x06, 0x01, _message_GSV, "disable NMEA GSV", 500); - SEND_UBX_PACKET(port, buffer, 0x06, 0x01, _message_VTG, "disable NMEA VTG", 500); - SEND_UBX_PACKET(port, buffer, 0x06, 0x01, _message_RMC, "enable NMEA RMC", 500); - SEND_UBX_PACKET(port, buffer, 0x06, 0x01, _message_GGA, "enable NMEA GGA", 500); + SEND_UBX_PACKET(uart, buffer, 0x06, 0x08, _message_1HZ, "set GPS update rate", 500); + SEND_UBX_PACKET(uart, buffer, 0x06, 0x01, _message_GLL, "disable NMEA GLL", 500); + SEND_UBX_PACKET(uart, buffer, 0x06, 0x01, _message_GSA, "enable NMEA GSA", 500); + SEND_UBX_PACKET(uart, buffer, 0x06, 0x01, _message_GSV, "disable NMEA GSV", 500); + SEND_UBX_PACKET(uart, buffer, 0x06, 0x01, _message_VTG, "disable NMEA VTG", 500); + SEND_UBX_PACKET(uart, buffer, 0x06, 0x01, _message_RMC, "enable NMEA RMC", 500); + SEND_UBX_PACKET(uart, buffer, 0x06, 0x01, _message_GGA, "enable NMEA GGA", 500); - uart::flushInput(port); + uart.flushInput(); - SEND_UBX_PACKET(port, buffer, 0x06, 0x11, _message_CFG_RXM_ECO, "enable powersave ECO mode for Neo-6", 500); - SEND_UBX_PACKET(port, buffer, 0x06, 0x3B, _message_CFG_PM2, "enable powersave details for GPS", 500); - SEND_UBX_PACKET(port, buffer, 0x06, 0x01, _message_AID, "disable UBX-AID", 500); + SEND_UBX_PACKET(uart, buffer, 0x06, 0x11, _message_CFG_RXM_ECO, "enable powersave ECO mode for Neo-6", 500); + SEND_UBX_PACKET(uart, buffer, 0x06, 0x3B, _message_CFG_PM2, "enable powersave details for GPS", 500); + SEND_UBX_PACKET(uart, buffer, 0x06, 0x01, _message_AID, "disable UBX-AID", 500); auto packet_size = makePacket(0x06, 0x09, _message_SAVE, sizeof(_message_SAVE), buffer); - uart::writeBytes(port, buffer, packet_size); - if (getAck(port, 0x06, 0x09, 2000) != GpsResponse::Ok) { + uart.writeBytes(buffer, packet_size); + if (getAck(uart, 0x06, 0x09, 2000) != GpsResponse::Ok) { TT_LOG_W(TAG, "Unable to save GNSS module config"); } else { TT_LOG_I(TAG, "GNSS module config saved!"); diff --git a/TactilityHeadless/Source/hal/uart/Uart.cpp b/TactilityHeadless/Source/hal/uart/Uart.cpp index 3b71f0ba..ab05a8d1 100644 --- a/TactilityHeadless/Source/hal/uart/Uart.cpp +++ b/TactilityHeadless/Source/hal/uart/Uart.cpp @@ -1,226 +1,46 @@ #include "Tactility/hal/uart/Uart.h" #include -#include #include -#include +#include #ifdef ESP_PLATFORM #include +#include "Tactility/hal/uart/UartEsp.h" +#else +#include "Tactility/hal/uart/UartPosix.h" #endif #define TAG "uart" namespace tt::hal::uart { -struct Data { - Mutex mutex; - bool isConfigured = false; - bool isStarted = false; +constexpr uint32_t uartIdNotInUse = 0; + +struct UartEntry { + uint32_t usageId = uartIdNotInUse; Configuration configuration; }; -static Data dataArray[UART_NUM_MAX]; - -static const char* initModeToString(InitMode mode) { - switch (mode) { - using enum InitMode; - case ByTactility: - return TT_STRINGIFY(InitMode::ByTactility); - case ByExternal: - return TT_STRINGIFY(InitMode::ByExternal); - case Disabled: - return TT_STRINGIFY(InitMode::Disabled); - } - tt_crash("not implemented"); -} - -static void printInfo(const Data& data) { - TT_LOG_D(TAG, "UART info for port %d", data.configuration.port); - TT_LOG_D(TAG, " isStarted: %d", data.isStarted); - TT_LOG_D(TAG, " isConfigured: %d", data.isConfigured); - TT_LOG_D(TAG, " initMode: %s", initModeToString(data.configuration.initMode)); - TT_LOG_D(TAG, " canReinit: %d", data.configuration.canReinit); - TT_LOG_D(TAG, " hasMutableConfiguration: %d", data.configuration.hasMutableConfiguration); - TT_LOG_D(TAG, " RX pin: %d", data.configuration.rxPin); - TT_LOG_D(TAG, " TX pin: %d", data.configuration.txPin); - TT_LOG_D(TAG, " RTS pin: %d", data.configuration.rtsPin); - TT_LOG_D(TAG, " CTS pin: %d", data.configuration.ctsPin); -} +static std::vector uartEntries = {}; +static uint32_t lastUartId = uartIdNotInUse; bool init(const std::vector& configurations) { TT_LOG_I(TAG, "Init"); for (const auto& configuration: configurations) { - Data& data = dataArray[configuration.port]; - data.configuration = configuration; - data.isConfigured = true; - } - - for (const auto& config: configurations) { - printInfo(dataArray[config.port]); - if (config.initMode == InitMode::ByTactility) { - if (!start(config.port)) { - return false; - } - } else if (config.initMode == InitMode::ByExternal) { - dataArray[config.port].isStarted = true; - } + uartEntries.push_back({ + .usageId = uartIdNotInUse, + .configuration = configuration + }); } return true; } -bool configure(uart_port_t port, const uart_config_t& configuration) { - auto lock = getLock(port).asScopedLock(); - lock.lock(); - - Data& data = dataArray[port]; - if (data.isStarted) { - TT_LOG_E(TAG, "(%d) Cannot reconfigure while interface is started", port); - return false; - } else if (!data.configuration.hasMutableConfiguration) { - TT_LOG_E(TAG, "(%d) Mutation not allowed by original configuration", port); - return false; - } else { - data.configuration.config = configuration; - return true; - } -} - -bool start(uart_port_t port) { - auto lock = getLock(port).asScopedLock(); - lock.lock(); - - Data& data = dataArray[port]; - printInfo(data); - - if (data.isStarted) { - TT_LOG_E(TAG, "(%d) Starting: Already started", port); - return false; - } - - if (!data.isConfigured) { - TT_LOG_E(TAG, "(%d) Starting: Not configured", port); - return false; - } - -#ifdef ESP_PLATFORM - - Configuration& config = data.configuration; - - int intr_alloc_flags; -#if CONFIG_UART_ISR_IN_IRAM - intr_alloc_flags = ESP_INTR_FLAG_IRAM; -#else - intr_alloc_flags = 0; -#endif - - esp_err_t result = uart_param_config(config.port, &config.config); - if (result != ESP_OK) { - TT_LOG_E(TAG, "(%d) Starting: Failed to configure: %s", port, esp_err_to_name(result)); - return false; - } - - result = uart_set_pin(config.port, config.txPin, config.rxPin, config.rtsPin, config.ctsPin); - if (result != ESP_OK) { - TT_LOG_E(TAG, "(%d) Starting: Failed set pins: %s", port, esp_err_to_name(result)); - return false; - } - - result = uart_driver_install(config.port, (int)config.rxBufferSize, (int)config.txBufferSize, 0, nullptr, intr_alloc_flags); - if (result != ESP_OK) { - TT_LOG_E(TAG, "(%d) Starting: Failed to install driver: %s", port, esp_err_to_name(result)); - return false; - } - -#endif // ESP_PLATFORM - - data.isStarted = true; - - TT_LOG_I(TAG, "(%d) Started", port); - return true; -} - -bool stop(uart_port_t port) { - auto lock = getLock(port).asScopedLock(); - lock.lock(); - - Data& data = dataArray[port]; - Configuration& config = data.configuration; - - if (!config.canReinit) { - TT_LOG_E(TAG, "(%d) Stopping: Not allowed to re-init", port); - return false; - } - - if (!data.isStarted) { - TT_LOG_E(TAG, "(%d) Stopping: Not started", port); - return false; - } - -#ifdef ESP_PLATFORM - esp_err_t result = uart_driver_delete(port); - if (result != ESP_OK) { - TT_LOG_E(TAG, "(%d) Stopping: Failed to delete driver: %s", port, esp_err_to_name(result)); - return false; - } -#endif // ESP_PLATFORM - - data.isStarted = false; - - TT_LOG_I(TAG, "(%d) Stopped", port); - return true; -} - -bool isStarted(uart_port_t port) { - auto lock = getLock(port).asScopedLock(); - lock.lock(); - - return dataArray[port].isStarted; -} - -Lock& getLock(uart_port_t port) { - return dataArray[port].mutex; -} - -size_t readBytes(uart_port_t port, uint8_t* buffer, size_t bufferSize, TickType_t timeout) { - auto lock = getLock(port).asScopedLock(); - if (!lock.lock(timeout)) { - TT_LOG_E(TAG, "(%d) Mutex timeout", port); - return false; - } - -#ifdef ESP_PLATFORM - auto start_time = kernel::getTicks(); - auto lock_time = kernel::getTicks() - start_time; - auto remaining_timeout = std::max(timeout - lock_time, 0UL); - auto result = uart_read_bytes(port, buffer, bufferSize, remaining_timeout); - return result; -#endif // ESP_PLATFORM - return 0; -} - -bool readByte(uart_port_t port, uint8_t* output, TickType_t timeout) { - return readBytes(port, output, 1, timeout) == 1; -} - -size_t writeBytes(uart_port_t port, const uint8_t* buffer, size_t bufferSize, TickType_t timeout) { - auto lock = getLock(port).asScopedLock(); - if (!lock.lock(timeout)) { - TT_LOG_E(TAG, "(%d) Mutex timeout", port); - return false; - } - -#ifdef ESP_PLATFORM - return uart_write_bytes(port, buffer, bufferSize); -#endif // ESP_PLATFORM - return 0; -} - -bool writeString(uart_port_t port, const char* buffer, TickType_t timeout) { +bool Uart::writeString(const char* buffer, TickType_t timeout) { while (*buffer != 0) { - if (writeBytes(port, (const uint8_t*)buffer, 1, timeout)) { + if (writeBytes(reinterpret_cast(buffer), 1, timeout)) { buffer++; } else { TT_LOG_E(TAG, "Failed to write - breaking off"); @@ -231,69 +51,12 @@ bool writeString(uart_port_t port, const char* buffer, TickType_t timeout) { return true; } -size_t available(uart_port_t port, TickType_t timeout) { - auto lock = getLock(port).asScopedLock(); - if (!lock.lock(timeout)) { - TT_LOG_E(TAG, "(%d) Mutex timeout", port); - return false; - } - -#ifdef ESP_PLATFORM - size_t size = 0; - uart_get_buffered_data_len(port, &size); - return size; -#else - return 0; -#endif // ESP_PLATFORM -} - -void flush(uart_port_t port) { -#ifdef ESP_PLATFORM - uart_flush(port); -#endif // ESP_PLATFORM -} - -void flushInput(uart_port_t port) { -#ifdef ESP_PLATFORM - uart_flush_input(port); -#endif // ESP_PLATFORM -} - -uint32_t getBaudRate(uart_port_t port) { -#ifdef ESP_PLATFORM - uint32_t baud_rate = 0; - auto result = uart_get_baudrate(port, &baud_rate); - ESP_ERROR_CHECK_WITHOUT_ABORT(result); - return baud_rate; -#else - return 0; -#endif -} - -bool setBaudRate(uart_port_t port, uint32_t baudRate, TickType_t timeout) { - auto lock = getLock(port).asScopedLock(); - if (!lock.lock(timeout)) { - TT_LOG_E(TAG, "(%d) Mutex timeout", port); - return false; - } - -#ifdef ESP_PLATFORM - auto result = uart_set_baudrate(port, baudRate); - ESP_ERROR_CHECK_WITHOUT_ABORT(result); - return result == ESP_OK; -#else - return true; -#endif // ESP_PLATFORM -} - -// #define DEBUG_READ_UNTIL - -size_t readUntil(uart_port_t port, uint8_t* buffer, size_t bufferSize, uint8_t untilByte, TickType_t timeout, bool addNullTerminator) { +size_t Uart::readUntil(std::byte* buffer, size_t bufferSize, uint8_t untilByte, TickType_t timeout, bool addNullTerminator) { TickType_t start_time = kernel::getTicks(); - uint8_t* buffer_write_ptr = buffer; - uint8_t* buffer_limit = buffer + bufferSize - 1; // Keep 1 extra char as mull terminator + auto* buffer_write_ptr = reinterpret_cast(buffer); + uint8_t* buffer_limit = buffer_write_ptr + bufferSize - 1; // Keep 1 extra char as mull terminator TickType_t timeout_left = timeout; - while (readByte(port, buffer_write_ptr, timeout_left) && buffer_write_ptr < buffer_limit) { + while (readByte(reinterpret_cast(buffer_write_ptr), timeout_left) && buffer_write_ptr < buffer_limit) { #ifdef DEBUG_READ_UNTIL // If first successful read and we're not receiving an empty response if (buffer_write_ptr == buffer && *buffer_write_ptr != 0x00U && *buffer_write_ptr != untilByte) { @@ -334,11 +97,52 @@ size_t readUntil(uart_port_t port, uint8_t* buffer, size_t bufferSize, uint8_t u } #endif - if (addNullTerminator && (buffer_write_ptr > buffer)) { + if (addNullTerminator && (buffer_write_ptr > reinterpret_cast(buffer))) { return reinterpret_cast(buffer_write_ptr) - reinterpret_cast(buffer) - 1UL; } else { return reinterpret_cast(buffer_write_ptr) - reinterpret_cast(buffer); } } +std::unique_ptr open(std::string name) { + auto result = std::views::filter(uartEntries, [&name](auto& entry) { + return entry.configuration.name == name; + }); + + if (result.empty()) { + TT_LOG_E(TAG, "UART not found: %s", name.c_str()); + return nullptr; + } + + auto& entry = *result.begin(); + if (entry.usageId != uartIdNotInUse) { + TT_LOG_E(TAG, "UART in use: %s", name.c_str()); + return nullptr; + } + + auto uart = create(entry.configuration); + assert(uart != nullptr); + entry.usageId = uart->getId(); + return uart; +} + +void close(uint32_t uartId) { + auto result = std::views::filter(uartEntries, [&uartId](auto& entry) { + return entry.usageId == uartId; + }); + + if (!result.empty()) { + auto& entry = *result.begin(); + entry.usageId = uartIdNotInUse; + } else { + TT_LOG_W(TAG, "Auto-closing UART, but can't find it"); + } +} + +Uart::Uart() : id(++lastUartId) {} + +Uart::~Uart() { + close(getId()); +} + } // namespace tt::hal::uart diff --git a/TactilityHeadless/Source/hal/uart/UartEsp.cpp b/TactilityHeadless/Source/hal/uart/UartEsp.cpp new file mode 100644 index 00000000..9715864c --- /dev/null +++ b/TactilityHeadless/Source/hal/uart/UartEsp.cpp @@ -0,0 +1,146 @@ +#ifdef ESP_PLATFORM + +#include "Tactility/hal/uart/UartEsp.h" + +#include +#include + +#include +#include + +#define TAG "uart" + +namespace tt::hal::uart { + +bool UartEsp::start() { + auto lock = mutex.asScopedLock(); + lock.lock(); + + if (started) { + TT_LOG_E(TAG, "(%d) Starting: Already started", configuration.port); + return false; + } + + int intr_alloc_flags; +#if CONFIG_UART_ISR_IN_IRAM + intr_alloc_flags = ESP_INTR_FLAG_IRAM; +#else + intr_alloc_flags = 0; +#endif + + esp_err_t result = uart_param_config(configuration.port, &configuration.config); + if (result != ESP_OK) { + TT_LOG_E(TAG, "(%d) Starting: Failed to configure: %s", configuration.port, esp_err_to_name(result)); + return false; + } + + result = uart_set_pin(configuration.port, configuration.txPin, configuration.rxPin, configuration.rtsPin, configuration.ctsPin); + if (result != ESP_OK) { + TT_LOG_E(TAG, "(%d) Starting: Failed set pins: %s", configuration.port, esp_err_to_name(result)); + return false; + } + + result = uart_driver_install(configuration.port, (int)configuration.rxBufferSize, (int)configuration.txBufferSize, 0, nullptr, intr_alloc_flags); + if (result != ESP_OK) { + TT_LOG_E(TAG, "(%d) Starting: Failed to install driver: %s", configuration.port, esp_err_to_name(result)); + return false; + } + + started = true; + + TT_LOG_I(TAG, "(%d) Started", configuration.port); + return true; +} + +bool UartEsp::stop() { + auto lock = mutex.asScopedLock(); + lock.lock(); + + if (!started) { + TT_LOG_E(TAG, "(%d) Stopping: Not started", configuration.port); + return false; + } + + esp_err_t result = uart_driver_delete(configuration.port); + if (result != ESP_OK) { + TT_LOG_E(TAG, "(%d) Stopping: Failed to delete driver: %s", configuration.port, esp_err_to_name(result)); + return false; + } + + started = false; + + TT_LOG_I(TAG, "(%d) Stopped", configuration.port); + return true; +} + +bool UartEsp::isStarted() const { + auto lock = mutex.asScopedLock(); + lock.lock(); + return started; +} + +size_t UartEsp::readBytes(std::byte* buffer, size_t bufferSize, TickType_t timeout) { + auto lock = mutex.asScopedLock(); + if (!lock.lock(timeout)) { + return false; + } + + auto start_time = kernel::getTicks(); + auto lock_time = kernel::getTicks() - start_time; + auto remaining_timeout = std::max(timeout - lock_time, 0UL); + auto result = uart_read_bytes(configuration.port, buffer, bufferSize, remaining_timeout); + return result; +} + +bool UartEsp::readByte(std::byte* output, TickType_t timeout) { + return readBytes(output, 1, timeout) == 1; +} + +size_t UartEsp::writeBytes(const std::byte* buffer, size_t bufferSize, TickType_t timeout) { + if (!mutex.lock(timeout)) { + return false; + } + + return uart_write_bytes(configuration.port, buffer, bufferSize); +} + +size_t UartEsp::available(TickType_t timeout) { + auto lock = mutex.asScopedLock(); + if (!lock.lock(timeout)) { + return false; + } + + size_t size = 0; + uart_get_buffered_data_len(configuration.port, &size); + return size; +} + +void UartEsp::flushInput() { + uart_flush_input(configuration.port); +} + +uint32_t UartEsp::getBaudRate() { + uint32_t baud_rate = 0; + auto result = uart_get_baudrate(configuration.port, &baud_rate); + ESP_ERROR_CHECK_WITHOUT_ABORT(result); + return baud_rate; +} + +bool UartEsp::setBaudRate(uint32_t baudRate, TickType_t timeout) { + auto lock = mutex.asScopedLock(); + if (!lock.lock(timeout)) { + return false; + } + + auto result = uart_set_baudrate(configuration.port, baudRate); + ESP_ERROR_CHECK_WITHOUT_ABORT(result); + return result == ESP_OK; +} + +std::unique_ptr create(const Configuration& configuration) { + return std::make_unique(configuration); +} + +} // namespace tt::hal::uart + +#endif diff --git a/TactilityHeadless/Source/hal/uart/UartPosix.cpp b/TactilityHeadless/Source/hal/uart/UartPosix.cpp new file mode 100644 index 00000000..d8416fe7 --- /dev/null +++ b/TactilityHeadless/Source/hal/uart/UartPosix.cpp @@ -0,0 +1,193 @@ +#ifndef ESP_PLATFORM + +#include "Tactility/hal/uart/UartPosix.h" +#include "Tactility/hal/uart/Uart.h" + +#include + +#include +#include +#include +#include + +#define TAG "uart" + +namespace tt::hal::uart { + +bool UartPosix::start() { + auto lock = mutex.asScopedLock(); + lock.lock(); + + if (device != nullptr) { + TT_LOG_E(TAG, "(%s) Starting: Already started", configuration.name.c_str()); + return false; + } + + auto file = fopen(configuration.name.c_str(), "w"); + if (file == nullptr) { + TT_LOG_E(TAG, "(%s) failed to open", configuration.name.c_str()); + return false; + } + + auto new_device = std::unique_ptr(file); + + struct termios tty; + if (tcgetattr(fileno(file), &tty) < 0) { + printf("(%s) tcgetattr failed: %s\n", configuration.name.c_str(), strerror(errno)); + return false; + } + + if (cfsetospeed(&tty, (speed_t)configuration.baudRate) == -1) { + TT_LOG_E(TAG, "(%s) failed to set output speed", configuration.name.c_str()); + return false; + } + + if (cfsetispeed(&tty, (speed_t)configuration.baudRate) == -1) { + TT_LOG_E(TAG, "(%s) failed to set input speed", configuration.name.c_str()); + return false; + } + + tty.c_cflag |= (CLOCAL | CREAD); /* ignore modem controls */ + tty.c_cflag &= ~CSIZE; + tty.c_cflag |= CS8; /* 8-bit characters */ + tty.c_cflag &= ~PARENB; /* no parity bit */ + tty.c_cflag &= ~CSTOPB; /* only need 1 stop bit */ + tty.c_cflag &= ~CRTSCTS; /* no hardware flowcontrol */ + + tty.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON); + tty.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN); + tty.c_oflag &= ~OPOST; + + /* fetch bytes as they become available */ + tty.c_cc[VMIN] = 1; + tty.c_cc[VTIME] = 1; + + if (tcsetattr(fileno(file), TCSANOW, &tty) != 0) { + printf("(%s) tcsetattr failed: %s\n", configuration.name.c_str(), strerror(errno)); + return false; + } + + device = std::move(new_device); + + TT_LOG_I(TAG, "(%s) Started", configuration.name.c_str()); + return true; +} + +bool UartPosix::stop() { + auto lock = mutex.asScopedLock(); + lock.lock(); + + if (device == nullptr) { + TT_LOG_E(TAG, "(%s) Stopping: Not started", configuration.name.c_str()); + return false; + } + + device = nullptr; + + TT_LOG_I(TAG, "(%s) Stopped", configuration.name.c_str()); + return true; +} + +bool UartPosix::isStarted() const { + auto lock = mutex.asScopedLock(); + lock.lock(); + return device != nullptr; +} + +size_t UartPosix::readBytes(std::byte* buffer, size_t bufferSize, TickType_t timeout) { + auto lock = mutex.asScopedLock(); + if (!lock.lock(timeout)) { + return false; + } + + if (awaitAvailable(timeout)) { + return read(fileno(device.get()), buffer, bufferSize); + } else { + return 0; + } +} + +bool UartPosix::readByte(std::byte* output, TickType_t timeout) { + if (awaitAvailable(timeout)) { + return read(fileno(device.get()), output, 1) == 1; + } else { + return false; + } +} + +size_t UartPosix::writeBytes(const std::byte* buffer, size_t bufferSize, TickType_t timeout) { + if (!mutex.lock(timeout)) { + return false; + } + + return write(fileno(device.get()), buffer, bufferSize); +} + +size_t UartPosix::available(TickType_t timeout) { + auto lock = mutex.asScopedLock(); + if (!lock.lock(timeout)) { + return false; + } + + uint32_t bytes_available = 0; + ioctl(fileno(device.get()), FIONREAD, bytes_available); + return bytes_available; +} + +void UartPosix::flushInput() { + // TODO +} + +uint32_t UartPosix::getBaudRate() { + struct termios tty; + if (tcgetattr(fileno(device.get()), &tty) < 0) { + printf("(%s) tcgetattr failed: %s\n", configuration.name.c_str(), strerror(errno)); + return false; + } else { + return (uint32_t)cfgetispeed(&tty); + } +} + +bool UartPosix::setBaudRate(uint32_t baudRate, TickType_t timeout) { + auto lock = mutex.asScopedLock(); + if (!lock.lock(timeout)) { + return false; + } + + struct termios tty; + if (tcgetattr(fileno(device.get()), &tty) < 0) { + printf("(%s) tcgetattr failed: %s\n", configuration.name.c_str(), strerror(errno)); + return false; + } + + if (cfsetospeed(&tty, (speed_t)configuration.baudRate) == -1) { + TT_LOG_E(TAG, "(%s) failed to set output speed", configuration.name.c_str()); + return false; + } + + if (cfsetispeed(&tty, (speed_t)configuration.baudRate) == -1) { + TT_LOG_E(TAG, "(%s) failed to set input speed", configuration.name.c_str()); + return false; + } + + return true; +} + +bool UartPosix::awaitAvailable(TickType_t timeout) { + auto start_time = kernel::getTicks(); + do { + if (available(timeout) > 0) { + return true; + } + kernel::delayTicks(timeout / 10); + } while ((kernel::getTicks() - start()) < timeout); + return false; +} + +std::unique_ptr create(const Configuration& configuration) { + return std::make_unique(configuration); +} + +} // namespace tt::hal::uart + +#endif