Files

282 lines
8.1 KiB
C++
Raw Permalink Normal View History

2025-01-12 18:08:57 +01:00
#include "comms/SerialClient.h"
#include "comms/MeshEnvelope.h"
#include "util/ILog.h"
2024-04-17 20:22:49 +02:00
#include <time.h>
#ifdef ARCH_ESP32
#include "driver/uart.h"
#include "esp_sleep.h"
#include <assert.h>
#endif
#if defined(HAS_FREE_RTOS) || defined(ARCH_ESP32)
#include "esp_task_wdt.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#else
#include <thread>
#endif
#include "Arduino.h"
#ifndef SLEEP_TIME_IDLE
#define SLEEP_TIME_IDLE 50 // ms
#endif
#ifndef SLEEP_TIME_ACTIVE
#define SLEEP_TIME_ACTIVE 2 // ms
#endif
2024-04-17 20:22:49 +02:00
SerialClient *SerialClient::instance = nullptr;
SerialClient::SerialClient(const char *name)
: pb_size(0), notifyConnectionStatus(nullptr), connectionStatus(eDisconnected), clientStatus(eDisconnected),
connectionInfo(nullptr), shutdown(false), threadName(name)
2024-04-17 20:22:49 +02:00
{
buffer = new uint8_t[PB_BUFSIZE + MT_HEADER_SIZE];
instance = this;
}
void SerialClient::init(void)
{
ILOG_TRACE("SerialClient::init() creating %s task", threadName);
2024-04-17 20:22:49 +02:00
#if defined(HAS_FREE_RTOS) || defined(ARCH_ESP32)
xTaskCreateUniversal(task_loop, threadName, 8192, NULL, 1, NULL, 0);
2024-05-16 08:38:13 +02:00
#elif defined(ARCH_PORTDUINO)
new std::thread([] {
#ifdef __APPLE__
pthread_setname_np(threadName);
#else
pthread_setname_np(pthread_self(), instance->threadName);
#endif
instance->task_loop(nullptr);
});
2024-04-17 20:22:49 +02:00
#else
// #error "unsupported architecture"
2024-04-17 20:22:49 +02:00
#endif
}
/**
* @brief Do light sleep, wake on pin GPIO (touch IRQ or button press)
*
* @param pin
2026-01-29 16:18:56 +01:00
* @return true - wake reason was due to (button) pin GPIO
2024-04-17 20:22:49 +02:00
* @return false - other reason (received serial data)
*/
bool SerialClient::sleep(int16_t pin)
{
#ifdef ARCH_ESP32
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON);
#if defined(WAKE_ON_SERIAL) && defined(USE_SERIAL0)
// TODO: check: - can only work if using the refclock, which is limited to about 9600 bps
assert(uart_set_wakeup_threshold(UART_NUM_0, 3) == ESP_OK);
assert(esp_sleep_enable_uart_wakeup(UART_NUM_0) == ESP_OK);
2024-04-19 01:15:23 +02:00
#elif defined(WAKE_ON_SERIAL) && defined(USE_SERIAL1)
assert(uart_set_wakeup_threshold(UART_NUM_1, 3) == ESP_OK);
assert(esp_sleep_enable_uart_wakeup(UART_NUM_1) == ESP_OK);
#else
2024-04-19 01:15:23 +02:00
// serial2 is unsupported for wakeup
2024-04-17 20:22:49 +02:00
// TODO: set flag for possible re-syncronisation with device
#endif
#ifdef BUTTON_PIN
2024-07-05 08:23:32 +02:00
pin = (gpio_num_t)BUTTON_PIN;
2024-04-17 20:22:49 +02:00
#if defined(BUTTON_NEED_PULLUP)
2024-07-05 08:23:32 +02:00
gpio_pullup_en((gpio_num_t)pin);
2024-04-17 20:22:49 +02:00
#endif
2024-07-05 08:23:32 +02:00
gpio_intr_disable((gpio_num_t)pin);
gpio_wakeup_enable((gpio_num_t)pin, GPIO_INTR_LOW_LEVEL);
2024-04-17 20:22:49 +02:00
#else
gpio_wakeup_enable((gpio_num_t)pin, GPIO_INTR_LOW_LEVEL);
#endif
auto res = esp_sleep_enable_gpio_wakeup();
if (res != ESP_OK) {
2024-10-17 09:06:42 +02:00
ILOG_ERROR("esp_sleep_enable_gpio_wakeup result %d", res);
2024-04-17 20:22:49 +02:00
}
2024-10-17 09:06:42 +02:00
ILOG_INFO("going to sleep, wake up on GPIO%02d", pin);
delay(5);
2024-04-17 20:22:49 +02:00
res = esp_light_sleep_start();
if (res != ESP_OK) {
2024-10-17 09:06:42 +02:00
ILOG_ERROR("esp_light_sleep_start result %d", res);
2024-04-17 20:22:49 +02:00
}
2026-01-29 16:18:56 +01:00
esp_sleep_wakeup_cause_t cause = esp_sleep_get_wakeup_cause();
ILOG_INFO("Exit light sleep cause: %d, gpio=%d", (int)cause, (int)digitalRead((uint8_t)pin));
#ifdef BUTTON_PIN
gpio_wakeup_disable((gpio_num_t)pin);
#endif
#if defined(WAKE_ON_SERIAL)
#if defined(USE_SERIAL0)
esp_sleep_disable_uart_wakeup(UART_NUM_0);
#elif defined(USE_SERIAL1)
esp_sleep_disable_uart_wakeup(UART_NUM_1);
#endif
#endif
return cause == ESP_SLEEP_WAKEUP_GPIO;
2024-04-17 20:22:49 +02:00
#else
return false;
#endif
}
bool SerialClient::connect(void)
{
clientStatus = eConnected;
return clientStatus == eConnected;
2024-04-17 20:22:49 +02:00
}
bool SerialClient::disconnect(void)
{
clientStatus = eDisconnected;
return clientStatus == eDisconnected;
2024-04-17 20:22:49 +02:00
}
bool SerialClient::isConnected(void)
{
return clientStatus == eConnected;
}
void SerialClient::setConnectionStatus(ConnectionStatus status, const char *info)
{
ILOG_TRACE("SerialClient::setConnectionStatus() status=%d, info=%s", status, info);
this->clientStatus = status;
this->connectionInfo = info;
}
void SerialClient::setNotifyCallback(NotifyCallback notifyConnectionStatus)
{
this->notifyConnectionStatus = notifyConnectionStatus;
}
bool SerialClient::isStandalone(void)
{
return true;
2024-04-17 20:22:49 +02:00
}
bool SerialClient::send(meshtastic_ToRadio &&to)
{
static uint32_t id = 1;
ILOG_DEBUG("SerialClient::send() push packet %d to server", id);
2024-04-17 20:22:49 +02:00
queue.clientSend(DataPacket<meshtastic_ToRadio>(id++, to));
return false;
}
meshtastic_FromRadio SerialClient::receive(void)
{
if (queue.serverQueueSize() != 0) {
2024-10-17 09:06:42 +02:00
ILOG_TRACE("SerialClient::receive() got a packet from queue");
auto p = queue.clientReceive();
if (p) {
return static_cast<DataPacket<meshtastic_FromRadio> *>(p->move().get())->getData();
} else {
ILOG_ERROR("SerialClient::receive() no packet in queue");
}
2024-04-17 20:22:49 +02:00
}
return meshtastic_FromRadio();
}
void SerialClient::task_handler(void)
{
// check for connection status change
if (notifyConnectionStatus) {
if (connectionStatus != clientStatus || (connectionStatus == eConnected && !isConnected())) {
connectionStatus = clientStatus;
notifyConnectionStatus(connectionStatus, connectionInfo);
}
}
}
2024-04-17 20:22:49 +02:00
SerialClient::~SerialClient()
{
shutdown = true;
delete[] buffer;
2024-04-17 20:22:49 +02:00
};
// --- protected part ---
bool SerialClient::send(const uint8_t *buf, size_t len)
{
2024-10-17 09:06:42 +02:00
ILOG_ERROR("SerialClient::send not implemented");
2024-04-17 20:22:49 +02:00
return false;
}
size_t SerialClient::receive(uint8_t *buf, size_t space_left)
{
2024-10-17 09:06:42 +02:00
ILOG_ERROR("SerialClient::receive not implemented");
2024-04-17 20:22:49 +02:00
return 0;
}
/**
* @brief put received bytes into envelope and send to client queue
*
*/
void SerialClient::handlePacketReceived(void)
{
2024-10-17 09:06:42 +02:00
ILOG_TRACE("SerialClient::handlePacketReceived pb_size=%d", pb_size);
2024-04-17 20:22:49 +02:00
MeshEnvelope envelope(buffer, pb_size);
meshtastic_FromRadio fromRadio = envelope.decode();
if (fromRadio.which_payload_variant != 0) {
queue.serverSend(DataPacket<meshtastic_FromRadio>(fromRadio.id, fromRadio));
ILOG_TRACE("server queue size=%d", queue.serverQueueSize());
2024-04-17 20:22:49 +02:00
}
}
void SerialClient::handleSendPacket(void)
{
auto p = queue.serverReceive();
if (p) {
meshtastic_ToRadio toRadio = static_cast<DataPacket<meshtastic_ToRadio> *>(p->move().get())->getData();
// meshtastic_ToRadio toRadio{std::move(static_cast<DataPacket<meshtastic_ToRadio> *>(p.get())->getData())};
MeshEnvelope envelope;
const std::vector<uint8_t> &pb_buf = envelope.encode(toRadio);
if (pb_buf.size() > 0) {
send(&pb_buf[0], pb_buf.size());
}
} else {
ILOG_ERROR("SerialClient::handleSendPacket() no packet in queue!");
2024-04-17 20:22:49 +02:00
}
}
/**
* @brief Sending and receiving packets to/from packet queue
*
*/
void SerialClient::task_loop(void *)
{
delay(1000);
2024-10-17 09:06:42 +02:00
ILOG_TRACE("SerialClient::task_loop running");
2024-04-17 20:22:49 +02:00
while (!instance->shutdown) {
int sleep_time = SLEEP_TIME_IDLE;
size_t space_left = PB_BUFSIZE - instance->pb_size;
if (instance->clientStatus == eConnected) {
2024-04-17 20:22:49 +02:00
size_t bytes_read = instance->receive(&instance->buffer[instance->pb_size], space_left);
if (bytes_read > 0) {
instance->pb_size += bytes_read;
size_t payload_len;
bool valid = false;
do {
valid = MeshEnvelope::validate(instance->buffer, instance->pb_size, payload_len);
if (valid) {
instance->handlePacketReceived();
MeshEnvelope::invalidate(instance->buffer, instance->pb_size, payload_len);
}
} while (valid && instance->pb_size > 0);
sleep_time = SLEEP_TIME_ACTIVE;
2024-04-17 20:22:49 +02:00
}
}
if (instance->clientStatus == eConnected) {
2024-04-17 20:22:49 +02:00
// send a packet if available
if (instance->queue.clientQueueSize() > 0) {
2024-04-17 20:22:49 +02:00
instance->handleSendPacket();
}
}
#if defined(HAS_FREE_RTOS) || defined(ARCH_ESP32)
vTaskDelay((TickType_t)sleep_time); // yield, do not remove
#else
std::this_thread::sleep_for(std::chrono::milliseconds(sleep_time));
#endif
2024-04-17 20:22:49 +02:00
}
}