mirror of
https://github.com/ByteWelder/Tactility.git
synced 2026-02-19 03:13:14 +00:00
UART refactored (#236)
`Uart` is now an abstract class with a `UartEsp` and a `UartPosix` implementation.
This commit is contained in:
parent
de46401d85
commit
b85ef7a2e7
@ -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,
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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<SimulatorSdCard>(),
|
||||
.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
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "../Device.h"
|
||||
#include "../uart/Uart.h"
|
||||
#include "Satellites.h"
|
||||
|
||||
#include <Tactility/Mutex.h>
|
||||
@ -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> thread;
|
||||
std::unique_ptr<Thread> _Nullable thread;
|
||||
bool threadInterrupted = false;
|
||||
std::vector<GgaSubscription> ggaSubscriptions;
|
||||
std::vector<RmcSubscription> rmcSubscriptions;
|
||||
|
||||
40
TactilityHeadless/Include/Tactility/hal/uart/Configuration.h
Normal file
40
TactilityHeadless/Include/Tactility/hal/uart/Configuration.h
Normal file
@ -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
|
||||
|
||||
}
|
||||
@ -5,6 +5,7 @@
|
||||
#include "../Gpio.h"
|
||||
#include "Tactility/Lock.h"
|
||||
#include "UartCompat.h"
|
||||
#include "Tactility/hal/uart/Configuration.h"
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
@ -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<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);
|
||||
};
|
||||
|
||||
/**
|
||||
* 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<Uart> open(std::string name);
|
||||
|
||||
} // namespace tt::hal::uart
|
||||
|
||||
@ -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<GpsDevice::Configuration>& 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);
|
||||
|
||||
}
|
||||
|
||||
@ -5,8 +5,6 @@
|
||||
|
||||
namespace tt::hal::gps {
|
||||
|
||||
struct GpsInfo;
|
||||
|
||||
GpsModel probe(uart_port_t port);
|
||||
GpsModel probe(uart::Uart& iart);
|
||||
|
||||
}
|
||||
|
||||
@ -17,11 +17,11 @@ template<size_t DataSize>
|
||||
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
|
||||
|
||||
38
TactilityHeadless/Private/Tactility/hal/uart/UartEsp.h
Normal file
38
TactilityHeadless/Private/Tactility/hal/uart/UartEsp.h
Normal file
@ -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<Uart> create(const Configuration& configuration);
|
||||
|
||||
} // namespace tt::hal::uart
|
||||
|
||||
#endif
|
||||
47
TactilityHeadless/Private/Tactility/hal/uart/UartPosix.h
Normal file
47
TactilityHeadless/Private/Tactility/hal/uart/UartPosix.h
Normal file
@ -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 <termios.h>
|
||||
|
||||
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<FILE, AutoCloseFileDeleter> 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<Uart> create(const Configuration& configuration);
|
||||
|
||||
} // namespace tt::hal::uart
|
||||
|
||||
#endif
|
||||
@ -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 <cstring>
|
||||
#include <minmea.h>
|
||||
|
||||
@ -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<std::byte*>(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;
|
||||
|
||||
@ -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<GpsDevice::Configuration>& 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<int>(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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,20 +1,21 @@
|
||||
#include "Tactility/hal/gps/Ublox.h"
|
||||
#include "Tactility/hal/gps/UbloxMessages.h"
|
||||
#include "Tactility/hal/uart/Uart.h"
|
||||
#include <cstring>
|
||||
|
||||
#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!");
|
||||
|
||||
@ -1,226 +1,46 @@
|
||||
#include "Tactility/hal/uart/Uart.h"
|
||||
|
||||
#include <Tactility/Log.h>
|
||||
#include <Tactility/LogMessages.h>
|
||||
#include <Tactility/Mutex.h>
|
||||
|
||||
#include <sstream>
|
||||
#include <ranges>
|
||||
|
||||
#ifdef ESP_PLATFORM
|
||||
#include <esp_check.h>
|
||||
#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<UartEntry> uartEntries = {};
|
||||
static uint32_t lastUartId = uartIdNotInUse;
|
||||
|
||||
bool init(const std::vector<uart::Configuration>& 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<const std::byte*>(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<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(port, buffer_write_ptr, timeout_left) && buffer_write_ptr < buffer_limit) {
|
||||
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) {
|
||||
@ -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<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) {
|
||||
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
|
||||
|
||||
146
TactilityHeadless/Source/hal/uart/UartEsp.cpp
Normal file
146
TactilityHeadless/Source/hal/uart/UartEsp.cpp
Normal file
@ -0,0 +1,146 @@
|
||||
#ifdef ESP_PLATFORM
|
||||
|
||||
#include "Tactility/hal/uart/UartEsp.h"
|
||||
|
||||
#include <Tactility/Log.h>
|
||||
#include <Tactility/Mutex.h>
|
||||
|
||||
#include <sstream>
|
||||
#include <esp_check.h>
|
||||
|
||||
#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<Uart> create(const Configuration& configuration) {
|
||||
return std::make_unique<UartEsp>(configuration);
|
||||
}
|
||||
|
||||
} // namespace tt::hal::uart
|
||||
|
||||
#endif
|
||||
193
TactilityHeadless/Source/hal/uart/UartPosix.cpp
Normal file
193
TactilityHeadless/Source/hal/uart/UartPosix.cpp
Normal file
@ -0,0 +1,193 @@
|
||||
#ifndef ESP_PLATFORM
|
||||
|
||||
#include "Tactility/hal/uart/UartPosix.h"
|
||||
#include "Tactility/hal/uart/Uart.h"
|
||||
|
||||
#include <Tactility/Log.h>
|
||||
|
||||
#include <cstring>
|
||||
#include <sstream>
|
||||
#include <sys/ioctl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#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, AutoCloseFileDeleter>(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<Uart> create(const Configuration& configuration) {
|
||||
return std::make_unique<UartPosix>(configuration);
|
||||
}
|
||||
|
||||
} // namespace tt::hal::uart
|
||||
|
||||
#endif
|
||||
Loading…
x
Reference in New Issue
Block a user