- Add app path get() functions to `TactilityC` - Improved `Dispatcher` and `DispatcherThread` - Improved `PubSub` (type safety) - Created test for `DispatcherThread` and `PubSub` - Save properties files on app exit (various apps) by posting it to the main dispatcher (fixes UI hanging briefly on app exit) - Fixed bug with `SystemSettings` being read from the wrong file path. - `loadPropertiesFile()` now uses `file::readLines()` instead of doing that manually - Increased timer task stack size (required due to issues when reading a properties file for the very first time) - General cleanup - Created `EstimatedPower` driver that uses an ADC pin to measure voltage and estimate the battery charge that is left. - Cleanup of T-Deck board (updated to new style)
175 lines
4.7 KiB
C++
175 lines
4.7 KiB
C++
#include "Tactility/hal/uart/Uart.h"
|
|
|
|
#include <Tactility/Log.h>
|
|
#include <Tactility/Mutex.h>
|
|
|
|
#include <ranges>
|
|
#include <cstring>
|
|
#include <Tactility/TactilityHeadless.h>
|
|
|
|
#ifdef ESP_PLATFORM
|
|
#include <Tactility/hal/uart/UartEsp.h>
|
|
#include <esp_check.h>
|
|
#else
|
|
#include <Tactility/hal/uart/UartPosix.h>
|
|
#include <dirent.h>
|
|
#endif
|
|
|
|
#define TAG "uart"
|
|
|
|
namespace tt::hal::uart {
|
|
|
|
constexpr uint32_t uartIdNotInUse = 0;
|
|
|
|
struct UartEntry {
|
|
uint32_t usageId = uartIdNotInUse;
|
|
Configuration configuration;
|
|
};
|
|
|
|
static std::vector<UartEntry> uartEntries = {};
|
|
static uint32_t lastUartId = uartIdNotInUse;
|
|
|
|
bool init(const std::vector<Configuration>& configurations) {
|
|
TT_LOG_I(TAG, "Init");
|
|
for (const auto& configuration: configurations) {
|
|
uartEntries.push_back({
|
|
.usageId = uartIdNotInUse,
|
|
.configuration = configuration
|
|
});
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool Uart::writeString(const char* buffer, TickType_t timeout) {
|
|
auto size = strlen(buffer);
|
|
writeBytes((std::byte*)buffer, size, timeout);
|
|
return true;
|
|
}
|
|
|
|
size_t Uart::readUntil(std::byte* buffer, size_t bufferSize, uint8_t untilByte, TickType_t timeout, bool addNullTerminator) {
|
|
TickType_t start_time = kernel::getTicks();
|
|
auto* buffer_write_ptr = reinterpret_cast<uint8_t*>(buffer);
|
|
uint8_t* buffer_limit = buffer_write_ptr + bufferSize - 1; // Keep 1 extra char as mull terminator
|
|
TickType_t timeout_left = timeout;
|
|
while (readByte(reinterpret_cast<std::byte*>(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) {
|
|
printf(">>");
|
|
}
|
|
#endif
|
|
|
|
if (*buffer_write_ptr == untilByte) {
|
|
// TODO: Fix when untilByte is null terminator char already
|
|
if (addNullTerminator) {
|
|
buffer_write_ptr++;
|
|
*buffer_write_ptr = 0x00U;
|
|
}
|
|
break;
|
|
}
|
|
|
|
#ifdef DEBUG_READ_UNTIL
|
|
printf("%c", *buffer_write_ptr);
|
|
#endif
|
|
|
|
buffer_write_ptr++;
|
|
|
|
TickType_t now = kernel::getTicks();
|
|
if (now > (start_time + timeout)) {
|
|
#ifdef DEBUG_READ_UNTIL
|
|
TT_LOG_W(TAG, "readUntil() timeout");
|
|
#endif
|
|
break;
|
|
} else {
|
|
timeout_left = timeout - (now - start_time);
|
|
}
|
|
}
|
|
|
|
#ifdef DEBUG_READ_UNTIL
|
|
// If we read data and it's not an empty response
|
|
if (buffer_write_ptr != buffer && *buffer != 0x00U && *buffer != untilByte) {
|
|
printf("\n");
|
|
}
|
|
#endif
|
|
|
|
if (addNullTerminator && (buffer_write_ptr > reinterpret_cast<uint8_t*>(buffer))) {
|
|
return reinterpret_cast<size_t>(buffer_write_ptr) - reinterpret_cast<size_t>(buffer) - 1UL;
|
|
} else {
|
|
return reinterpret_cast<size_t>(buffer_write_ptr) - reinterpret_cast<size_t>(buffer);
|
|
}
|
|
}
|
|
|
|
std::unique_ptr<Uart> open(std::string name) {
|
|
TT_LOG_I(TAG, "Open %s", name.c_str());
|
|
|
|
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();
|
|
TT_LOG_I(TAG, "Opened %lu", entry.usageId);
|
|
return uart;
|
|
}
|
|
|
|
void close(uint32_t uartId) {
|
|
TT_LOG_I(TAG, "Close %lu", 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");
|
|
}
|
|
}
|
|
|
|
std::vector<std::string> getNames() {
|
|
std::vector<std::string> names;
|
|
#ifdef ESP_PLATFORM
|
|
for (auto& config : hal::getConfiguration()->uart) {
|
|
names.push_back(config.name);
|
|
}
|
|
#else
|
|
DIR* dir = opendir("/dev");
|
|
if (dir == nullptr) {
|
|
TT_LOG_E(TAG, "Failed to read /dev");
|
|
return names;
|
|
}
|
|
struct dirent* current_entry;
|
|
while ((current_entry = readdir(dir)) != nullptr) {
|
|
auto name = std::string(current_entry->d_name);
|
|
if (name.starts_with("tty")) {
|
|
auto path = std::string("/dev/") + name;
|
|
names.push_back(path);
|
|
}
|
|
}
|
|
|
|
closedir(dir);
|
|
#endif
|
|
return names;
|
|
}
|
|
|
|
Uart::Uart() : id(++lastUartId) {}
|
|
|
|
Uart::~Uart() {
|
|
close(getId());
|
|
}
|
|
|
|
} // namespace tt::hal::uart
|