UART refactored (#236)

`Uart` is now an abstract class with a `UartEsp` and a `UartPosix` implementation.
This commit is contained in:
Ken Van Hoeylandt
2025-02-26 17:13:37 +01:00
committed by GitHub
parent de46401d85
commit b85ef7a2e7
22 changed files with 867 additions and 592 deletions
+27 -29
View File
@@ -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;
+55 -55
View File
@@ -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;
}
+28 -28
View File
@@ -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;
}
}
+92 -91
View File
@@ -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!");