Files
Manuel d234bd98c7 feat: Serial connection from portduino (#256)
* fix standalone reboot

* add LinuxSerialClient

* trunk fmt

* debug log levels
2025-12-29 13:06:31 +01:00

160 lines
4.2 KiB
C++

#include "comms/UARTClient.h"
#include "Arduino.h"
#include "util/ILog.h"
#ifndef SERIAL_BAUD
#define SERIAL_BAUD 38400
#endif
#define CONNECTION_TIMEOUT 60 // seconds
#define RX_BUFFER 1024
#define TIMEOUT 250 // ms
#define MAX_PACKET_SIZE 284
extern const uint8_t MT_MAGIC_0;
UARTClient::UARTClient(const char *name) : SerialClient(name), isActive(false), _serial(nullptr) {}
/**
* @brief init serial interface
* Defaults:
* ESP32: RX0=3, TX0=1, RX1=9, TX1=10, RX2=16, TX2=17
* ESP32S2: RX0=44, TX0=43, RX1=18, TX1=17
* ESP32C3: RX0=20, TX0=21, RX1=18, TX1=19
* ESP32S3: RX0=44, TX0=43, RX1=15, TX1=16, RX2=19, TX2=20
*/
void UARTClient::init(void)
{
#if SOC_UART_NUM > 2 && defined(USE_SERIAL2)
ILOG_INFO("UARTClient::init SERIAL2 baud=%d", SERIAL_BAUD);
_serial = &Serial2;
#elif SOC_UART_NUM > 1 && defined(USE_SERIAL1)
ILOG_INFO("UARTClient::init SERIAL1 baud=%d", SERIAL_BAUD);
_serial = &Serial1;
#elif defined(ARDUINO_USB_CDC_ON_BOOT) && ARDUINO_USB_CDC_ON_BOOT
_serial = &Serial0;
#elif !defined(ARCH_PORTDUINO)
ILOG_INFO("UARTClient::init SERIAL1 baud=%d", SERIAL_BAUD);
_serial = &Serial;
#else
_serial = nullptr;
#endif
#if defined(ARCH_ESP32)
_serial->setTimeout(TIMEOUT);
_serial->setRxBufferSize(RX_BUFFER);
_serial->begin(SERIAL_BAUD);
#else
// not supported
#endif
#ifdef SERIAL_RX
_serial->setPins(SERIAL_RX, SERIAL_TX);
ILOG_INFO("UARTClient::setPins rx=%d, tx=%d with %d baud", SERIAL_RX, SERIAL_TX, SERIAL_BAUD);
#endif
time(&lastReceived);
SerialClient::init();
}
bool UARTClient::connect(void)
{
if (clientStatus != eConnected) {
ILOG_DEBUG("serial client connecting...");
setConnectionStatus(eConnecting, "Connecting...");
time_t timeout = millis();
while (!*_serial) {
if ((millis() - timeout) > 5) {
setConnectionStatus(eError, "Connection failed!");
ILOG_WARN("serial client connection failed!");
return false;
}
}
// skip some available bytes until we see magic header
int skipped = 0;
while (_serial->available()) {
if (_serial->peek() != MT_MAGIC_0) {
_serial->read();
skipped++;
delay(1);
}
isActive = true;
}
if (isActive) {
setConnectionStatus(eConnected, "Connected!");
ILOG_INFO("serial client connected! (skipped %d bytes)", skipped);
} else {
// pretend to be connected and start sending data
clientStatus = eConnected;
}
}
return clientStatus == eConnected;
}
bool UARTClient::disconnect(void)
{
ILOG_DEBUG("serial client disconnecting...");
isActive = false;
setConnectionStatus(eDisconnected, "Disconnected");
return SerialClient::disconnect();
}
bool UARTClient::isConnected(void)
{
time_t now;
time(&now);
if (now - lastReceived > CONNECTION_TIMEOUT && isActive) {
isActive = false;
setConnectionStatus(eDisconnected, "Disconnected");
}
return *_serial && (clientStatus == eConnected || isActive);
}
meshtastic_FromRadio UARTClient::receive(void)
{
return SerialClient::receive();
}
UARTClient::~UARTClient()
{
if (_serial) {
_serial->end();
}
};
// --- protected part ---
// raw write to serial UART interface
bool UARTClient::send(const uint8_t *buf, size_t len)
{
ILOG_TRACE("sending %d bytes to radio", len);
size_t wrote = _serial->write(buf, len);
if (wrote != len) {
ILOG_ERROR("only %d bytes were sent this time", wrote);
}
return wrote == len;
}
// raw read from serial UART interface
size_t UARTClient::receive(uint8_t *buf, size_t space_left)
{
size_t bytes_read = 0;
while (_serial->available() && bytes_read < MAX_PACKET_SIZE) {
uint8_t byte = _serial->read();
*buf++ = byte;
if (++bytes_read >= space_left) {
// no error, but serial thread is too slow (-> reduce sleep_time)
ILOG_WARN("Serial overflow!");
break;
}
}
if (bytes_read > 0) {
ILOG_TRACE("received %d bytes from serial", bytes_read);
time(&lastReceived);
isActive = true;
}
return bytes_read;
}