Merge TactilityHeadless into Tactility (#263)

There currently is no practical use to have TactilityHeadless as a subproject. I'm merging it with the Tactility project.
This commit is contained in:
Ken Van Hoeylandt
2025-03-30 10:54:36 +02:00
committed by GitHub
parent d0ca3b16f8
commit d72852a6e2
114 changed files with 30 additions and 105 deletions
+17
View File
@@ -0,0 +1,17 @@
#pragma once
#define TT_ASSET_FOLDER "A:/system/"
#define TT_ASSET(file) TT_ASSET_FOLDER file
// UI
#define TT_ASSETS_UI_SPINNER TT_ASSET("spinner.png")
// App icons
#define TT_ASSETS_APP_ICON_FALLBACK TT_ASSET("app_icon_fallback.png")
#define TT_ASSETS_APP_ICON_FILES TT_ASSET("app_icon_files.png")
#define TT_ASSETS_APP_ICON_DISPLAY_SETTINGS TT_ASSET("app_icon_display_settings.png")
#define TT_ASSETS_APP_ICON_POWER_SETTINGS TT_ASSET("app_icon_power_settings.png")
#define TT_ASSETS_APP_ICON_I2C_SETTINGS TT_ASSET("app_icon_i2c.png")
#define TT_ASSETS_APP_ICON_SETTINGS TT_ASSET("app_icon_settings.png")
#define TT_ASSETS_APP_ICON_SYSTEM_INFO TT_ASSET("app_icon_system_info.png")
#define TT_ASSETS_APP_ICON_TIME_DATE_SETTINGS TT_ASSET("app_icon_time_date_settings.png")
+21
View File
@@ -0,0 +1,21 @@
#pragma once
namespace tt {
#define SYSTEM_PARTITION_NAME "system"
#ifdef ESP_PLATFORM
#define MOUNT_POINT_SYSTEM "/system"
#else
#define MOUNT_POINT_SYSTEM "system"
#endif
#define DATA_PARTITION_NAME "data"
#ifdef ESP_PLATFORM
#define MOUNT_POINT_DATA "/data"
#else
#define MOUNT_POINT_DATA "data"
#endif
} // namespace
+34
View File
@@ -0,0 +1,34 @@
#pragma once
#include <cstdint>
#include <string>
namespace tt {
/**
* Settings that persist on NVS flash for ESP32.
* On simulator, the settings are only in-memory.
*/
class Preferences {
private:
const char* namespace_;
public:
explicit Preferences(const char* namespace_) {
this->namespace_ = namespace_;
}
bool hasBool(const std::string& key) const;
bool hasInt32(const std::string& key) const;
bool hasString(const std::string& key) const;
bool optBool(const std::string& key, bool& out) const;
bool optInt32(const std::string& key, int32_t& out) const;
bool optString(const std::string& key, std::string& out) const;
void putBool(const std::string& key, bool value);
void putInt32(const std::string& key, int32_t value);
void putString(const std::string& key, const std::string& value);
};
} // namespace
@@ -0,0 +1,26 @@
#pragma once
#include "Tactility/hal/Configuration.h"
#include <Tactility/TactilityCore.h>
#include <Tactility/Dispatcher.h>
namespace tt {
/** Initialize the hardware and started the internal services. */
void initHeadless(const hal::Configuration& config);
/** Provides access to the dispatcher that runs on the main task.
* @warning This dispatcher is used for WiFi and might block for some time during WiFi connection.
* @return the dispatcher
*/
Dispatcher& getMainDispatcher();
} // namespace
namespace tt::hal {
/** While technically this configuration is nullable, it's never null after initHeadless() is called. */
const Configuration* _Nullable getConfiguration();
} // namespace
@@ -0,0 +1,58 @@
#pragma once
#include "Tactility/hal/gps/GpsDevice.h"
#include "Tactility/hal/sdcard/SdCardDevice.h"
#include "Tactility/hal/spi/Spi.h"
#include "Tactility/hal/uart/Uart.h"
#include "i2c/I2c.h"
namespace tt::hal {
typedef bool (*InitBoot)();
namespace display { class DisplayDevice; }
namespace keyboard { class KeyboardDevice; }
namespace power { class PowerDevice; }
typedef std::shared_ptr<display::DisplayDevice> (*CreateDisplay)();
typedef std::shared_ptr<keyboard::KeyboardDevice> (*CreateKeyboard)();
typedef std::shared_ptr<power::PowerDevice> (*CreatePower)();
enum class LvglInit {
Default,
None
};
struct Configuration {
/**
* Called before I2C/SPI/etc is initialized.
* Used for powering on the peripherals manually.
*/
const InitBoot _Nullable initBoot = nullptr;
/** Init behaviour: default (esp_lvgl_port for ESP32, nothing for PC) or None (nothing on any platform). Only used in Tactility, not in TactilityHeadless. */
const LvglInit lvglInit = LvglInit::Default;
/** Display HAL functionality. */
const CreateDisplay _Nullable createDisplay = nullptr;
/** Keyboard HAL functionality. */
const CreateKeyboard _Nullable createKeyboard = nullptr;
/** An optional SD card interface. */
const std::shared_ptr<sdcard::SdCardDevice> _Nullable sdcard = nullptr;
/** An optional power interface for battery or other power delivery. */
const CreatePower _Nullable power = nullptr;
/** A list of I2C interface configurations */
const std::vector<i2c::Configuration> i2c = {};
/** A list of SPI interface configurations */
const std::vector<spi::Configuration> spi = {};
/** A list of UART interface configurations */
const std::vector<uart::Configuration> uart = {};
};
} // namespace
+110
View File
@@ -0,0 +1,110 @@
#pragma once
#include <functional>
#include <memory>
#include <ranges>
#include <string>
#include <vector>
#include <cassert>
namespace tt::hal {
/** Base class for HAL-related devices. */
class Device {
public:
enum class Type {
I2c,
Display,
Touch,
SdCard,
Keyboard,
Power,
Gps
};
typedef uint32_t Id;
private:
Id id;
public:
Device();
virtual ~Device() = default;
/** Unique identifier */
inline Id getId() const { return id; }
/** The type of device */
virtual Type getType() const = 0;
/** The part number or hardware name e.g. TdeckTouch, TdeckDisplay, BQ24295, etc. */
virtual std::string getName() const = 0;
/** A short description of what this device does.
* e.g. "USB charging controller with I2C interface."
*/
virtual std::string getDescription() const = 0;
};
/**
* Adds a device to the registry.
* @warning This will leak memory if you want to destroy a device and don't call deregisterDevice()!
*/
void registerDevice(const std::shared_ptr<Device>& device);
/** Remove a device from the registry. */
void deregisterDevice(const std::shared_ptr<Device>& device);
/** Find a single device with a custom filter */
std::shared_ptr<Device> _Nullable findDevice(const std::function<bool(const std::shared_ptr<Device>&)>& filterFunction);
/** Find devices with a custom filter */
std::vector<std::shared_ptr<Device>> findDevices(const std::function<bool(const std::shared_ptr<Device>&)>& filterFunction);
/** Find a device in the registry by its name. */
std::shared_ptr<Device> _Nullable findDevice(std::string name);
/** Find a device in the registry by its identifier. */
std::shared_ptr<Device> _Nullable findDevice(Device::Id id);
/** Find 0, 1 or more devices in the registry by type. */
std::vector<std::shared_ptr<Device>> findDevices(Device::Type type);
/** Get a copy of the entire device registry in its current state. */
std::vector<std::shared_ptr<Device>> getDevices();
/** Find devices of a certain type and cast them to the specified class */
template<class DeviceType>
std::vector<std::shared_ptr<DeviceType>> findDevices(Device::Type type) {
auto devices = findDevices(type);
if (devices.empty()) {
return {};
} else {
std::vector<std::shared_ptr<DeviceType>> result;
result.reserve(devices.size());
for (auto& device : devices) {
auto target_device = std::static_pointer_cast<DeviceType>(device);
assert(target_device != nullptr);
result.push_back(target_device);
}
return std::move(result);
}
}
/** Find the first device of the specified type and cast it to the specified class */
template<class DeviceType>
std::shared_ptr<DeviceType> findFirstDevice(Device::Type type) {
auto devices = findDevices(type);
if (devices.empty()) {
return {};
} else {
auto& first = devices[0];
return std::static_pointer_cast<DeviceType>(first);
}
}
}
+7
View File
@@ -0,0 +1,7 @@
#pragma once
#ifdef ESP_PLATFORM
#include <driver/gpio.h>
#else
typedef unsigned int gpio_num_t;
#endif
@@ -0,0 +1,40 @@
#pragma once
#include "../Device.h"
#include <lvgl.h>
namespace tt::hal::touch {
class TouchDevice;
}
namespace tt::hal::display {
class DisplayDevice : public Device {
public:
Type getType() const override { return Type::Display; }
virtual bool start() = 0;
virtual bool stop() = 0;
virtual void setPowerOn(bool turnOn) {}
virtual bool isPoweredOn() const { return true; }
virtual bool supportsPowerControl() const { return false; }
virtual std::shared_ptr<touch::TouchDevice> _Nullable createTouch() = 0;
/** Set a value in the range [0, 255] */
virtual void setBacklightDuty(uint8_t backlightDuty) { /* NO-OP */ }
virtual bool supportsBacklightDuty() const { return false; }
/** Set a value in the range [0, 255] */
virtual void setGammaCurve(uint8_t index) { /* NO-OP */ }
virtual uint8_t getGammaCurveCount() const { return 0; };
/** After start() returns true, this should return a valid pointer until stop() is called and returns true */
virtual lv_display_t* _Nullable getLvglDisplay() const = 0;
};
} // namespace tt::hal::display
@@ -0,0 +1,36 @@
#pragma once
#include <cstdint>
#include <vector>
#include <string>
namespace tt::hal::gps {
enum class GpsModel {
Unknown = 0,
AG3335,
AG3352,
ATGM336H, // Casic (might work with AT6558, Neoway N58 LTE Cat.1, Neoway G2, Neoway G7A)
LS20031,
MTK,
MTK_L76B,
MTK_PA1616S,
UBLOX6,
UBLOX7,
UBLOX8,
UBLOX9,
UBLOX10,
UC6580,
};
const char* toString(GpsModel model);
std::vector<std::string> getModels();
struct GpsConfiguration {
char uartName[32]; // e.g. "Internal" or "/dev/ttyUSB0"
uint32_t baudRate;
GpsModel model; // Choosing "Unknown" will result in a probe
};
}
@@ -0,0 +1,125 @@
#pragma once
#include "../Device.h"
#include "GpsConfiguration.h"
#include "Satellites.h"
#include <Tactility/Mutex.h>
#include <Tactility/Thread.h>
#include <minmea.h>
#include <utility>
namespace tt::hal::gps {
enum class GpsResponse {
None,
NotAck,
FrameErrors,
Ok,
};
class GpsDevice : public Device {
public:
typedef int GgaSubscriptionId;
typedef int RmcSubscriptionId;
enum class State {
PendingOn,
On,
Error,
PendingOff,
Off
};
private:
struct GgaSubscription {
GgaSubscriptionId id;
std::shared_ptr<std::function<void(Device::Id id, const minmea_sentence_gga&)>> onData;
};
struct RmcSubscription {
RmcSubscriptionId id;
std::shared_ptr<std::function<void(Device::Id id, const minmea_sentence_rmc&)>> onData;
};
const GpsConfiguration configuration;
Mutex mutex = Mutex(Mutex::Type::Recursive);
std::unique_ptr<Thread> _Nullable thread;
bool threadInterrupted = false;
std::vector<GgaSubscription> ggaSubscriptions;
std::vector<RmcSubscription> rmcSubscriptions;
GgaSubscriptionId lastSatelliteSubscriptionId = 0;
RmcSubscriptionId lastRmcSubscriptionId = 0;
GpsModel model = GpsModel::Unknown;
State state = State::Off;
static int32_t threadMainStatic(void* parameter);
int32_t threadMain();
bool isThreadInterrupted() const;
void setState(State newState);
public:
explicit GpsDevice(GpsConfiguration configuration) : configuration(std::move(configuration)) {}
~GpsDevice() override = default;
Type getType() const override { return Type::Gps; }
std::string getName() const override {
if (model != GpsModel::Unknown) {
return toString(model);
} else {
return "Unknown GPS";
}
}
std::string getDescription() const override { return ""; }
bool start();
bool stop();
GgaSubscriptionId subscribeGga(const std::function<void(Device::Id deviceId, const minmea_sentence_gga&)>& onData) {
auto lock = mutex.asScopedLock();
lock.lock();
ggaSubscriptions.push_back({
.id = ++lastSatelliteSubscriptionId,
.onData = std::make_shared<std::function<void(Device::Id, const minmea_sentence_gga&)>>(onData)
});
return lastSatelliteSubscriptionId;
}
void unsubscribeGga(GgaSubscriptionId subscriptionId) {
auto lock = mutex.asScopedLock();
lock.lock();
std::erase_if(ggaSubscriptions, [subscriptionId](auto& subscription) { return subscription.id == subscriptionId; });
}
RmcSubscriptionId subscribeRmc(const std::function<void(Device::Id deviceId, const minmea_sentence_rmc&)>& onData) {
auto lock = mutex.asScopedLock();
lock.lock();
rmcSubscriptions.push_back({
.id = ++lastRmcSubscriptionId,
.onData = std::make_shared<std::function<void(Device::Id, const minmea_sentence_rmc&)>>(onData)
});
return lastRmcSubscriptionId;
}
void unsubscribeRmc(GgaSubscriptionId subscriptionId) {
auto lock = mutex.asScopedLock();
lock.lock();
std::erase_if(rmcSubscriptions, [subscriptionId](auto& subscription) { return subscription.id == subscriptionId; });
}
GpsModel getModel() const;
State getState() const;
};
}
@@ -0,0 +1,59 @@
#pragma once
#include <Tactility/RtosCompat.h>
#include <Tactility/Mutex.h>
#include <minmea.h>
#include <ranges>
#include <memory>
namespace tt::hal::gps {
/** Thread-safe storage of recent satellites */
class SatelliteStorage {
public:
static constexpr size_t recordCount = 32;
private:
struct SatelliteRecord {
minmea_sat_info data {
.nr = 0,
.elevation = 0,
.azimuth = 0,
.snr = 0
};
TickType_t lastUpdated = 0;
bool inUse = false;
};
Mutex mutex = Mutex(Mutex::Type::Recursive);
std::array<SatelliteRecord, recordCount> records;
uint16_t recycleTimeSeconds;
uint16_t recentTimeSeconds;
SatelliteRecord* findRecord(int number);
SatelliteRecord* findUnusedRecord();
SatelliteRecord* findRecordToRecycle();
/** Tries to find an existing record, otherwise return a free one, otherwise return the oldest active one */
SatelliteRecord* findWithFallback(int number);
public:
explicit SatelliteStorage(
uint16_t recycleTimeSeconds = 120,
uint16_t recentTimeSeconds = 60
) : recycleTimeSeconds(recycleTimeSeconds), recentTimeSeconds(recentTimeSeconds) {}
void notify(const minmea_sat_info& info);
void getRecords(const std::function<void(const minmea_sat_info&)>& onRecord) const;
};
} // namespace tt::hal::gps
+94
View File
@@ -0,0 +1,94 @@
#pragma once
#include "./I2cCompat.h"
#include "Tactility/Lock.h"
#include <Tactility/RtosCompat.h>
#include <climits>
#include <string>
namespace tt::hal::i2c {
constexpr TickType_t defaultTimeout = 10 / portTICK_PERIOD_MS;
enum class InitMode {
ByTactility, // Tactility will initialize it in the correct bootup phase
ByExternal, // The device is already initialized and Tactility should assume it works
Disabled // Not initialized by default
};
struct Configuration {
std::string name;
/** The port to operate on */
i2c_port_t port;
/** Whether this bus should be initialized when device starts up */
InitMode initMode;
/**
* Whether this bus can be changed after booting.
* If the bus is internal and/or used for core features like touch screen, then it can be declared static.
*/
bool isMutable;
/** Configuration that must be valid when initAtBoot is set to true. */
i2c_config_t config;
};
enum class Status {
Started,
Stopped,
Unknown
};
/**
* Reconfigure a port with the provided settings.
* @warning This fails when the HAL Configuration is not mutable.
* @param[in] port the port to reconfigure
* @param[in] configuration the new configuration
* @return true on success
*/
bool configure(i2c_port_t port, const i2c_config_t& configuration);
/**
* Start the bus for the specified port.
* Devices might be started automatically at boot if their HAL configuration requires it.
*/
bool start(i2c_port_t port);
/** Stop the bus for the specified port. */
bool stop(i2c_port_t port);
/** @return true if the bus is started */
bool isStarted(i2c_port_t port);
/** Read bytes in master mode. */
bool masterRead(i2c_port_t port, uint8_t address, uint8_t* data, size_t dataSize, TickType_t timeout = defaultTimeout);
/** Read bytes from the specified register in master mode. */
bool masterReadRegister(i2c_port_t port, uint8_t address, uint8_t reg, uint8_t* data, size_t dataSize, TickType_t timeout = defaultTimeout);
/** Write bytes in master mode. */
bool masterWrite(i2c_port_t port, uint8_t address, const uint8_t* data, uint16_t dataSize, TickType_t timeout = defaultTimeout);
/** Write bytes to a register in master mode */
bool masterWriteRegister(i2c_port_t port, uint8_t address, uint8_t reg, const uint8_t* data, uint16_t dataSize, TickType_t timeout = defaultTimeout);
/**
* Write multiple values to multiple registers in master mode.
* The input is as follows: { register1, value1, register2, value2, ... }
* @return false if any of the write operations failed
*/
bool masterWriteRegisterArray(i2c_port_t port, uint8_t address, const uint8_t* data, uint16_t dataSize, TickType_t timeout = defaultTimeout);
/** Write bytes and then read the response bytes in master mode*/
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 = defaultTimeout);
/** @return true when a device is detected at the specified address */
bool masterHasDeviceAtAddress(i2c_port_t port, uint8_t address, TickType_t timeout = defaultTimeout);
/**
* The lock for the specified bus.
* This can be used when calling native I2C functionality outside of Tactility.
*/
Lock& getLock(i2c_port_t port);
} // namespace
@@ -0,0 +1,38 @@
#pragma once
#ifdef ESP_PLATFORM
#include <hal/i2c_types.h>
#include <driver/i2c.h>
#else
#include <cstdint>
enum i2c_port_t {
I2C_NUM_0 = 0,
I2C_NUM_1,
LP_I2C_NUM_0,
I2C_NUM_MAX,
};
enum i2c_mode_t {
I2C_MODE_MASTER,
I2C_MODE_MAX,
};
struct i2c_config_t {
i2c_mode_t mode;
int sda_io_num;
int scl_io_num;
bool sda_pullup_en;
bool scl_pullup_en;
union {
struct {
uint32_t clk_speed;
} master;
};
uint32_t clk_flags;
};
#endif
@@ -0,0 +1,44 @@
#pragma once
#include "I2c.h"
#include "../Device.h"
namespace tt::hal::i2c {
/**
* Represents an I2C peripheral at a specific port and address.
* It helps to read and write registers.
*
* All read and write calls are thread-safe.
*/
class I2cDevice : public Device {
protected:
i2c_port_t port;
uint8_t address;
static constexpr TickType_t DEFAULT_TIMEOUT = 1000 / portTICK_PERIOD_MS;
bool readRegister8(uint8_t reg, uint8_t& result) const;
bool writeRegister8(uint8_t reg, uint8_t value) const;
bool readRegister12(uint8_t reg, float& out) const;
bool readRegister14(uint8_t reg, float& out) const;
bool readRegister16(uint8_t reg, uint16_t& out) const;
bool bitOn(uint8_t reg, uint8_t bitmask) const;
bool bitOff(uint8_t reg, uint8_t bitmask) const;
bool bitOnByIndex(uint8_t reg, uint8_t index) const { return bitOn(reg, 1 << index); }
bool bitOffByIndex(uint8_t reg, uint8_t index) const { return bitOff(reg, 1 << index); }
public:
explicit I2cDevice(i2c_port_t port, uint32_t address) : port(port), address(address) {}
Type getType() const override { return Type::I2c; }
i2c_port_t getPort() const { return port; }
uint8_t getAddress() const { return address; }
};
}
@@ -0,0 +1,24 @@
#pragma once
#include "../Device.h"
#include <lvgl.h>
namespace tt::hal::keyboard {
class Display;
class KeyboardDevice : public Device {
public:
Type getType() const override { return Type::Keyboard; }
virtual bool start(lv_display_t* display) = 0;
virtual bool stop() = 0;
virtual bool isAttached() const = 0;
virtual lv_indev_t* _Nullable getLvglIndev() = 0;
};
}
@@ -0,0 +1,44 @@
#pragma once
#include "../Device.h"
#include <cstdint>
namespace tt::hal::power {
class PowerDevice : public Device {
public:
PowerDevice() = default;
~PowerDevice() override = default;
Type getType() const override { return Type::Power; }
enum class MetricType {
IsCharging, // bool
Current, // int32_t, mAh - battery current: either during charging (positive value) or discharging (negative value)
BatteryVoltage, // uint32_t, mV
ChargeLevel, // uint8_t [0, 100]
};
union MetricData {
int32_t valueAsInt32 = 0;
uint32_t valueAsUint32;
uint8_t valueAsUint8;
float valueAsFloat;
bool valueAsBool;
};
virtual bool supportsMetric(MetricType type) const = 0;
/**
* @return false when metric is not supported or (temporarily) not available.
*/
virtual bool getMetric(MetricType type, MetricData& data) = 0;
virtual bool supportsChargeControl() const { return false; }
virtual bool isAllowedToCharge() const { return false; }
virtual void setAllowedToCharge(bool canCharge) { /* NO-OP*/ }
};
} // namespace tt
@@ -0,0 +1,66 @@
#pragma once
#include "../Device.h"
#include <Tactility/TactilityCore.h>
namespace tt::hal::sdcard {
class SdCardDevice : public Device {
public:
enum class State {
Mounted,
Unmounted,
Error,
Unknown
};
enum class MountBehaviour {
AtBoot, /** Only mount at boot */
Anytime /** Mount/dismount any time */
};
private:
MountBehaviour mountBehaviour;
public:
explicit SdCardDevice(MountBehaviour mountBehaviour) : mountBehaviour(mountBehaviour) {}
~SdCardDevice() override = default;
Type getType() const final { return Type::SdCard; };
virtual bool mount(const std::string& mountPath) = 0;
virtual bool unmount() = 0;
virtual State getState() const = 0;
/** Return empty string when not mounted or the mount path if mounted */
virtual std::string getMountPath() const = 0;
virtual Lock& getLock() const = 0;
virtual MountBehaviour getMountBehaviour() const { return mountBehaviour; }
bool isMounted() const { return getState() == State::Mounted; }
};
/** Return the SdCard device if the path is within the SdCard mounted path (path std::string::starts_with() check)*/
std::shared_ptr<SdCardDevice> _Nullable find(const std::string& path);
/**
* Acquires an SD card lock if the path is an SD card path.
* Always calls the function, but doesn't lock if the path is not an SD card path.
*/
template<typename ReturnType>
inline ReturnType withSdCardLock(const std::string& path, std::function<ReturnType()> fn) {
auto sdcard = find(path);
if (sdcard != nullptr) {
auto scoped_lockable = sdcard->getLock().asScopedLock();
scoped_lockable.lock(portMAX_DELAY);
}
return fn();
}
} // namespace tt::hal
@@ -0,0 +1,99 @@
#ifdef ESP_PLATFORM
#pragma once
#include "SdCardDevice.h"
#include <Tactility/hal/spi/Spi.h>
#include <sd_protocol_types.h>
#include <utility>
#include <vector>
#include <hal/spi_types.h>
#include <soc/gpio_num.h>
namespace tt::hal::sdcard {
/**
* SD card interface at the default SPI interface
*/
class SpiSdCardDevice final : public SdCardDevice {
public:
struct Config {
Config(
gpio_num_t spiPinCs,
gpio_num_t spiPinCd,
gpio_num_t spiPinWp,
gpio_num_t spiPinInt,
MountBehaviour mountBehaviourAtBoot,
/** When custom lock is nullptr, use the SPI default one */
std::shared_ptr<Lock> _Nullable customLock = nullptr,
std::vector<gpio_num_t> csPinWorkAround = std::vector<gpio_num_t>(),
spi_host_device_t spiHost = SPI2_HOST,
int spiFrequencyKhz = SDMMC_FREQ_DEFAULT
) : spiFrequencyKhz(spiFrequencyKhz),
spiPinCs(spiPinCs),
spiPinCd(spiPinCd),
spiPinWp(spiPinWp),
spiPinInt(spiPinInt),
mountBehaviourAtBoot(mountBehaviourAtBoot),
customLock(customLock ? std::move(customLock) : nullptr),
csPinWorkAround(std::move(csPinWorkAround)),
spiHost(spiHost)
{}
int spiFrequencyKhz;
gpio_num_t spiPinCs; // Clock
gpio_num_t spiPinCd; // Card detect
gpio_num_t spiPinWp; // Write-protect
gpio_num_t spiPinInt; // Interrupt
SdCardDevice::MountBehaviour mountBehaviourAtBoot;
std::shared_ptr<Lock> _Nullable customLock;
std::vector<gpio_num_t> csPinWorkAround;
spi_host_device_t spiHost;
bool formatOnMountFailed = false;
uint16_t maxOpenFiles = 4;
uint16_t allocUnitSize = 16 * 1024;
bool statusCheckEnabled = false;
};
private:
std::string mountPath;
sdmmc_card_t* card = nullptr;
std::shared_ptr<Config> config;
bool applyGpioWorkAround();
bool mountInternal(const std::string& mountPath);
public:
explicit SpiSdCardDevice(std::unique_ptr<Config> config) : SdCardDevice(config->mountBehaviourAtBoot),
config(std::move(config))
{}
std::string getName() const final { return "SD Card"; }
std::string getDescription() const final { return "SD card via SPI interface"; }
bool mount(const std::string& mountPath) final;
bool unmount() final;
std::string getMountPath() const final { return mountPath; }
Lock& getLock() const final {
if (config->customLock) {
return *config->customLock;
} else {
return *spi::getLock(config->spiHost);
}
}
State getState() const override;
sdmmc_card_t* _Nullable getCard() { return card; }
};
}
#endif
+48
View File
@@ -0,0 +1,48 @@
#pragma once
#include "SpiCompat.h"
#include <Tactility/Lock.h>
#include <Tactility/RtosCompat.h>
namespace tt::hal::spi {
constexpr TickType_t defaultTimeout = 10 / portTICK_PERIOD_MS;
enum class InitMode {
ByTactility, // Tactility will initialize it in the correct bootup phase
ByExternal, // The device is already initialized and Tactility should assume it works
Disabled // Not initialized by default
};
struct Configuration {
spi_host_device_t device;
spi_common_dma_t dma;
spi_bus_config_t config;
/** Whether this bus should be initialized when device starts up */
InitMode initMode;
/** Whether configuration can be changed. */
bool isMutable;
/** Optional custom lock */
std::shared_ptr<Lock> _Nullable lock;
};
enum class Status {
Started,
Stopped,
Unknown
};
/** Start communications */
bool start(spi_host_device_t device);
/** Stop communications */
bool stop(spi_host_device_t device);
/** @return true if communications were started successfully */
bool isStarted(spi_host_device_t device);
/** @return the lock that represents the specified device. Can be used with third party SPI implementations or native API calls (e.g. ESP-IDF). */
std::shared_ptr<Lock> getLock(spi_host_device_t device);
} // namespace tt::hal::spi
@@ -0,0 +1,19 @@
#pragma once
#include "../Gpio.h"
#ifdef ESP_PLATFORM
#include <driver/spi_common.h>
#else
#define SPI_HOST_MAX 3
typedef int spi_host_device_t;
struct spi_bus_config_t {
gpio_num_t miso_io_num;
gpio_num_t mosi_io_num;
gpio_num_t sclk_io_num;
};
struct spi_common_dma_t {};
#endif
@@ -0,0 +1,23 @@
#pragma once
#include "../Device.h"
#include <lvgl.h>
namespace tt::hal::touch {
class Display;
class TouchDevice : public Device {
public:
Type getType() const override { return Type::Touch; }
virtual bool start(lv_display_t* display) = 0;
virtual bool stop() = 0;
virtual lv_indev_t* _Nullable getLvglIndev() = 0;
};
}
@@ -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
}
+126
View File
@@ -0,0 +1,126 @@
#pragma once
#include <Tactility/RtosCompat.h>
#include "../Gpio.h"
#include "Tactility/Lock.h"
#include "UartCompat.h"
#include "Tactility/hal/uart/Configuration.h"
#include <memory>
#include <vector>
namespace tt::hal::uart {
constexpr TickType_t defaultTimeout = 10 / portTICK_PERIOD_MS;
enum class InitMode {
ByTactility, // Tactility will initialize it in the correct bootup phase
ByExternal, // The device is already initialized and Tactility should assume it works
Disabled // Not initialized by default
};
enum class Status {
Started,
Stopped,
Unknown
};
class Uart {
private:
uint32_t id;
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<std::byte*>(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<std::byte*>(output), timeout);
}
inline bool readByte(uint8_t* output, TickType_t timeout = defaultTimeout) {
return readByte(reinterpret_cast<std::byte*>(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<const std::byte*>(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);
};
/**
* 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.
*/
std::unique_ptr<Uart> open(std::string name);
std::vector<std::string> getNames();
} // namespace tt::hal::uart
@@ -0,0 +1,17 @@
#pragma once
#ifdef ESP_PLATFORM
#include <driver/uart.h>
#include <driver/gpio.h>
#include <hal/uart_types.h>
#else
#define UART_NUM_MAX 3
typedef int uart_port_t;
typedef struct {
} uart_config_t;
#endif
+21
View File
@@ -0,0 +1,21 @@
#pragma once
namespace tt::hal::usb {
enum class Mode {
Default, // Default state of USB stack
None, // State after TinyUSB was used and (partially) deinitialized
MassStorageSdmmc
};
bool startMassStorageWithSdmmc();
void stop();
Mode getMode();
bool isSupported();
bool canRebootIntoMassStorageSdmmc();
void rebootIntoMassStorageSdmmc();
bool isUsbBootMode();
void resetUsbBootMode();
}
@@ -0,0 +1,38 @@
#pragma once
#include <cstdint>
#include <functional>
namespace tt::kernel {
enum class SystemEvent {
BootInitHalBegin,
BootInitHalEnd,
BootInitI2cBegin,
BootInitI2cEnd,
BootInitSpiBegin,
BootInitSpiEnd,
BootInitUartBegin,
BootInitUartEnd,
BootInitLvglBegin,
BootInitLvglEnd,
BootSplash,
/** Gained IP address */
NetworkConnected,
NetworkDisconnected,
/** An important system time-related event, such as NTP update or time-zone change */
Time,
};
/** Value 0 mean "no subscription" */
typedef uint32_t SystemEventSubscription;
typedef std::function<void(SystemEvent)> OnSystemEvent;
void systemEventPublish(SystemEvent event);
SystemEventSubscription systemEventAddListener(SystemEvent event, OnSystemEvent handler);
void systemEventRemoveListener(SystemEventSubscription subscription);
}
@@ -0,0 +1,24 @@
#pragma once
#include <memory>
namespace tt::service {
// Forward declaration
class ServiceContext;
class Service {
public:
Service() = default;
virtual ~Service() = default;
virtual void onStart(ServiceContext& serviceContext) {}
virtual void onStop(ServiceContext& serviceContext) {}
};
template<typename T>
std::shared_ptr<Service> create() { return std::shared_ptr<T>(new T); }
}
@@ -0,0 +1,93 @@
#pragma once
#include "ServiceManifest.h"
#include <Tactility/Mutex.h>
#include <memory>
namespace tt::service {
class Paths;
/**
* The public representation of a service instance.
* @warning Do not store references or pointers to these! You can retrieve them via the Loader service.
*/
class ServiceContext {
protected:
virtual ~ServiceContext() = default;
public:
/** @return a reference ot the service's manifest */
virtual const service::ServiceManifest& getManifest() const = 0;
/** Retrieve the paths that are relevant to this service */
virtual std::unique_ptr<Paths> getPaths() const = 0;
};
class Paths {
public:
Paths() = default;
virtual ~Paths() = default;
/**
* Returns the directory path for the data location for a service.
* The data directory is intended to survive OS upgrades.
* The path will not end with a "/".
*/
virtual std::string getDataDirectory() const = 0;
/**
* @see getDataDirectory(), but with LVGL prefix.
*/
virtual std::string getDataDirectoryLvgl() const = 0;
/**
* Returns the full path for an entry inside the data location for a service.
* The data directory is intended to survive OS upgrades.
* Configuration data should be stored here.
* @param[in] childPath the path without a "/" prefix
*/
virtual std::string getDataPath(const std::string& childPath) const = 0;
/**
* @see getDataPath(), but with LVGL prefix.
*/
virtual std::string getDataPathLvgl(const std::string& childPath) const = 0;
/**
* Returns the directory path for the system location for a service.
* The system directory is not intended to survive OS upgrades.
* You should not store configuration data here.
* The path will not end with a "/".
* This is mainly used for core services.
*/
virtual std::string getSystemDirectory() const = 0;
/**
* @see getSystemDirectory(), but with LVGL prefix.
*/
virtual std::string getSystemDirectoryLvgl() const = 0;
/**
* Returns the full path for an entry inside the system location for an app.
* The data directory is not intended to survive OS upgrades.
* You should not store configuration data here.
* This is mainly used for core apps (system/boot/settings type).
* @param[in] childPath the path without a "/" prefix
*/
virtual std::string getSystemPath(const std::string& childPath) const = 0;
/**
* @see getSystemPath(), but with LVGL prefix.
*/
virtual std::string getSystemPathLvgl(const std::string& childPath) const = 0;
};
} // namespace
@@ -0,0 +1,23 @@
#pragma once
#include "Tactility/service/Service.h"
#include <string>
namespace tt::service {
// Forward declarations
class ServiceContext;
typedef std::shared_ptr<Service>(*CreateService)();
/** A ledger that describes the main parts of a service. */
struct ServiceManifest {
/** The identifier by which the app is launched by the system and other apps. */
std::string id {};
/** Create the instance of the app */
CreateService createService = nullptr;
};
} // namespace
@@ -0,0 +1,59 @@
#pragma once
#include "ServiceManifest.h"
#include "Service.h"
#include <memory>
namespace tt::service {
/** Register a service.
* @param[in] the service manifest
*/
void addService(std::shared_ptr<const ServiceManifest> manifest, bool autoStart = true);
/** Register a service.
* @param[in] the service manifest
*/
void addService(const ServiceManifest& manifest, bool autoStart = true);
/** Start a service.
* @param[in] the service id as defined in its manifest
* @return true on success
*/
bool startService(const std::string& id);
/** Stop a service.
* @param[in] the service id as defined in its manifest
* @return true on success or false when service wasn't running.
*/
bool stopService(const std::string& id);
/** Find a service manifest by its id.
* @param[in] id the id as defined in the manifest
* @return the matching manifest or nullptr when it wasn't found
*/
std::shared_ptr<const ServiceManifest> _Nullable findManifestId(const std::string& id);
/** Find a ServiceContext by its manifest id.
* @param[in] id the id as defined in the manifest
* @return the service context or nullptr when it wasn't found
*/
std::shared_ptr<ServiceContext> _Nullable findServiceContextById(const std::string& id);
/** Find a Service by its manifest id.
* @param[in] id the id as defined in the manifest
* @return the service context or nullptr when it wasn't found
*/
std::shared_ptr<Service> _Nullable findServiceById(const std::string& id);
/** Find a Service by its manifest id.
* @param[in] id the id as defined in the manifest
* @return the service context or nullptr when it wasn't found
*/
template <typename T>
std::shared_ptr<T> _Nullable findServiceById(const std::string& id) {
return std::static_pointer_cast<T>(findServiceById(id));
}
} // namespace
@@ -0,0 +1,54 @@
#pragma once
#ifdef ESP_PLATFORM
#include <cstdint>
#include <cstring>
#include <esp_now.h>
#include <functional>
namespace tt::service::espnow {
typedef int ReceiverSubscription;
constexpr ReceiverSubscription NO_SUBSCRIPTION = -1;
enum class Mode {
Station,
AccessPoint
};
struct EspNowConfig {
uint8_t masterKey[ESP_NOW_KEY_LEN];
uint8_t address[ESP_NOW_ETH_ALEN];
Mode mode;
uint8_t channel;
bool longRange;
bool encrypt;
EspNowConfig(
uint8_t masterKey[16],
Mode mode,
uint8_t channel,
bool longRange,
bool encrypt
) : mode(mode), channel(channel), longRange(longRange), encrypt(encrypt) {
memcpy((void*)this->masterKey, (void*)masterKey, 16);
}
};
void enable(const EspNowConfig& config);
void disable();
bool isEnabled();
bool addPeer(const esp_now_peer_info_t& peer);
bool send(const uint8_t* address, const uint8_t* buffer, size_t bufferLength);
ReceiverSubscription subscribeReceiver(std::function<void(const esp_now_recv_info_t* receiveInfo, const uint8_t* data, int length)> onReceive);
void unsubscribeReceiver(ReceiverSubscription subscription);
}
#endif // ESP_PLATFORM
@@ -0,0 +1,69 @@
#pragma once
#include "Tactility/Mutex.h"
#include "Tactility/PubSub.h"
#include "Tactility/hal/gps/GpsDevice.h"
#include "Tactility/service/Service.h"
#include "Tactility/service/ServiceContext.h"
#include "Tactility/service/gps/GpsState.h"
namespace tt::service::gps {
class GpsService final : public Service {
private:
struct GpsDeviceRecord {
std::shared_ptr<hal::gps::GpsDevice> device = nullptr;
hal::gps::GpsDevice::GgaSubscriptionId satelliteSubscriptionId = -1;
hal::gps::GpsDevice::RmcSubscriptionId rmcSubscriptionId = -1;
};
minmea_sentence_rmc rmcRecord;
TickType_t rmcTime = 0;
Mutex mutex = Mutex(Mutex::Type::Recursive);
Mutex stateMutex;
std::vector<GpsDeviceRecord> deviceRecords;
std::shared_ptr<PubSub> statePubSub = std::make_shared<PubSub>();
std::unique_ptr<Paths> paths;
State state = State::Off;
bool startGpsDevice(GpsDeviceRecord& deviceRecord);
static bool stopGpsDevice(GpsDeviceRecord& deviceRecord);
GpsDeviceRecord* _Nullable findGpsRecord(const std::shared_ptr<hal::gps::GpsDevice>& record);
void onGgaSentence(hal::Device::Id deviceId, const minmea_sentence_gga& gga);
void onRmcSentence(hal::Device::Id deviceId, const minmea_sentence_rmc& rmc);
void setState(State newState);
void addGpsDevice(const std::shared_ptr<hal::gps::GpsDevice>& device);
void removeGpsDevice(const std::shared_ptr<hal::gps::GpsDevice>& device);
bool getConfigurationFilePath(std::string& output) const;
public:
void onStart(tt::service::ServiceContext &serviceContext) final;
void onStop(tt::service::ServiceContext &serviceContext) final;
bool addGpsConfiguration(hal::gps::GpsConfiguration configuration);
bool removeGpsConfiguration(hal::gps::GpsConfiguration configuration);
bool getGpsConfigurations(std::vector<hal::gps::GpsConfiguration>& configurations) const;
bool startReceiving();
void stopReceiving();
State getState() const;
bool hasCoordinates() const;
bool getCoordinates(minmea_sentence_rmc& rmc) const;
/** @return GPS service pubsub that broadcasts State* objects */
std::shared_ptr<PubSub> getStatePubsub() const { return statePubSub; }
};
std::shared_ptr<GpsService> findGpsService();
} // tt::service::gps
@@ -0,0 +1,12 @@
#pragma once
namespace tt::service::gps {
enum class State {
OnPending,
On,
OffPending,
Off
};
}
@@ -0,0 +1,10 @@
#pragma once
#include <minmea.h>
namespace tt::hal::gps {
/** @return true when the input float is valid (contains non-zero values) */
inline bool isValid(const minmea_float& inFloat) { return inFloat.value != 0 && inFloat.scale != 0; }
}
@@ -0,0 +1,133 @@
#pragma once
#include "./WifiGlobals.h"
#include "./WifiSettings.h"
#include <Tactility/PubSub.h>
#include <cstdio>
#include <string>
#include <vector>
#ifdef ESP_PLATFORM
#include "esp_wifi.h"
#include "WifiSettings.h"
#else
#include <cstdint>
// From esp_wifi_types.h in ESP-IDF 5.2
typedef enum {
WIFI_AUTH_OPEN = 0, /**< authenticate mode : open */
WIFI_AUTH_WEP, /**< authenticate mode : WEP */
WIFI_AUTH_WPA_PSK, /**< authenticate mode : WPA_PSK */
WIFI_AUTH_WPA2_PSK, /**< authenticate mode : WPA2_PSK */
WIFI_AUTH_WPA_WPA2_PSK, /**< authenticate mode : WPA_WPA2_PSK */
WIFI_AUTH_ENTERPRISE, /**< authenticate mode : WiFi EAP security */
WIFI_AUTH_WPA2_ENTERPRISE = WIFI_AUTH_ENTERPRISE, /**< authenticate mode : WiFi EAP security */
WIFI_AUTH_WPA3_PSK, /**< authenticate mode : WPA3_PSK */
WIFI_AUTH_WPA2_WPA3_PSK, /**< authenticate mode : WPA2_WPA3_PSK */
WIFI_AUTH_WAPI_PSK, /**< authenticate mode : WAPI_PSK */
WIFI_AUTH_OWE, /**< authenticate mode : OWE */
WIFI_AUTH_WPA3_ENT_192, /**< authenticate mode : WPA3_ENT_SUITE_B_192_BIT */
WIFI_AUTH_WPA3_EXT_PSK, /**< authenticate mode : WPA3_PSK_EXT_KEY */
WIFI_AUTH_WPA3_EXT_PSK_MIXED_MODE, /**< authenticate mode: WPA3_PSK + WPA3_PSK_EXT_KEY */
WIFI_AUTH_MAX
} wifi_auth_mode_t;
#endif
namespace tt::service::wifi {
enum class EventType {
/** Radio was turned on */
RadioStateOn,
/** Radio is turning on. */
RadioStateOnPending,
/** Radio is turned off */
RadioStateOff,
/** Radio is turning off */
RadioStateOffPending,
/** Started scanning for access points */
ScanStarted,
/** Finished scanning for access points */ // TODO: 1 second validity
ScanFinished,
Disconnected,
ConnectionPending,
ConnectionSuccess,
ConnectionFailed
};
enum class RadioState {
OnPending,
On,
ConnectionPending,
ConnectionActive,
OffPending,
Off,
};
struct Event {
EventType type;
};
struct ApRecord {
std::string ssid;
int8_t rssi;
int32_t channel;
wifi_auth_mode_t auth_mode;
};
/**
* @brief Get wifi pubsub that broadcasts Event objects
* @return PubSub
*/
std::shared_ptr<PubSub> getPubsub();
/** @return Get the current radio state */
RadioState getRadioState();
/** For logging purposes */
const char* radioStateToString(RadioState state);
/**
* @brief Request scanning update. Returns immediately. Results are through pubsub.
*/
void scan();
/** @return true if wifi is actively scanning */
bool isScanning();
/** @return true the ssid name or empty string */
std::string getConnectionTarget();
/** @return the access points from the last scan (if any). It only contains public APs. */
std::vector<ApRecord> getScanResults();
/**
* @brief Overrides the default scan result size of 16.
* @param[in] records the record limit for the scan result (84 bytes per record!)
*/
void setScanRecords(uint16_t records);
/**
* @brief Enable/disable the radio. Ignores input if desired state matches current state.
* @param[in] enabled
*/
void setEnabled(bool enabled);
/**
* @brief Connect to a network. Disconnects any existing connection.
* Returns immediately but runs in the background. Results are through pubsub.
* @param[in] ap
* @param[in] remember whether to save the ap data to the settings upon successful connection
*/
void connect(const settings::WifiApSettings* ap, bool remember);
/** @brief Disconnect from the access point. Doesn't have any effect when not connected. */
void disconnect();
/** @return true if the connection isn't unencrypted. */
bool isConnectionSecure();
/** @return the RSSI value (negative number) or return 1 when not connected. */
int getRssi();
} // namespace
@@ -0,0 +1,8 @@
#pragma once
#define TT_WIFI_AUTO_CONNECT true // Default setting for new Wi-Fi entries
#define TT_WIFI_SCAN_RECORD_LIMIT 16 // default, can be overridden
#define TT_WIFI_SSID_LIMIT 32 // 32 characters/octets, according to IEEE 802.11-2020 spec
#define TT_WIFI_CREDENTIALS_PASSWORD_LIMIT 64 // 64 characters/octets, according to IEEE 802.11-2020 spec
@@ -0,0 +1,33 @@
#pragma once
#include "WifiGlobals.h"
#include <cstdint>
namespace tt::service::wifi::settings {
/**
* This struct is stored as-is into NVS flash.
*
* The SSID and secret are increased by 1 byte to facilitate string null termination.
* This makes it easier to use the char array as a string in various places.
*/
struct WifiApSettings {
char ssid[TT_WIFI_SSID_LIMIT + 1] = { 0 };
char password[TT_WIFI_CREDENTIALS_PASSWORD_LIMIT + 1] = { 0 };
int32_t channel = 0;
bool auto_connect = true;
};
bool contains(const char* ssid);
bool load(const char* ssid, WifiApSettings* settings);
bool save(const WifiApSettings* settings);
bool remove(const char* ssid);
void setEnableOnBoot(bool enable);
bool shouldEnableOnBoot();
} // namespace
+32
View File
@@ -0,0 +1,32 @@
#pragma once
#include <string>
namespace tt::time {
/**
* Set the timezone
* @param[in] name human-readable name
* @param[in] code the technical code (from timezones.csv)
*/
void setTimeZone(const std::string& name, const std::string& code);
/**
* Get the name of the timezone
*/
std::string getTimeZoneName();
/**
* Get the code of the timezone (see timezones.csv)
*/
std::string getTimeZoneCode();
/** @return true when clocks should be shown as a 24 hours one instead of 12 hours */
bool isTimeFormat24Hour();
/** Set whether clocks should be shown as a 24 hours instead of 12 hours
* @param[in] show24Hour
*/
void setTimeFormat24Hour(bool show24Hour);
}