diff --git a/library.json b/library.json index 30b5c46..7e8cf3c 100644 --- a/library.json +++ b/library.json @@ -11,7 +11,7 @@ "url": "https://github.com/bcmi-labs/Arduino_RouterBridge", "maintainer": true }, - "version": "0.1.2", + "version": "0.1.3", "license": "MPL2.0", "frameworks": "arduino", "platforms": "*", diff --git a/library.properties b/library.properties index ca1c9ed..38562b0 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=Arduino_RouterBridge -version=0.1.2 +version=0.1.3 author=BCMI-labs maintainer=BCMI-labs sentence=A RPC bridge for Arduino UNO Q boards diff --git a/src/bridge.h b/src/bridge.h index 51482be..d377b28 100644 --- a/src/bridge.h +++ b/src/bridge.h @@ -33,25 +33,31 @@ class BridgeClass { RPCClient* client = nullptr; RPCServer* server = nullptr; HardwareSerial* serial_ptr = nullptr; - ITransport* transport; + ITransport* transport = nullptr; - struct k_mutex read_mutex; - struct k_mutex write_mutex; - - k_tid_t upd_tid; - k_thread_stack_t *upd_stack_area; - struct k_thread upd_thread_data; + struct k_mutex read_mutex{}; + struct k_mutex write_mutex{}; + + k_tid_t upd_tid{}; + k_thread_stack_t *upd_stack_area{}; + struct k_thread upd_thread_data{}; + + bool started = false; public: - BridgeClass(HardwareSerial& serial) { + explicit BridgeClass(HardwareSerial& serial) { serial_ptr = &serial; - transport = new SerialTransport(serial); + } + + operator bool() const { + return started; } // Initialize the bridge bool begin(unsigned long baud=DEFAULT_SERIAL_BAUD) { serial_ptr->begin(baud); + transport = new SerialTransport(*serial_ptr); k_mutex_init(&read_mutex); k_mutex_init(&write_mutex); @@ -67,7 +73,11 @@ class BridgeClass { UPDATE_THREAD_PRIORITY, 0, K_NO_WAIT); bool res; - return call(RESET_METHOD, res); + call(RESET_METHOD, res); + if (res) { + started = true; + } + return res; } template @@ -123,7 +133,7 @@ class BridgeClass { } template - bool call(const MsgPack::str_t method, RType& result, Args&&... args) { + bool call(const MsgPack::str_t& method, RType& result, Args&&... args) { uint32_t msg_id_wait; @@ -165,11 +175,15 @@ class BridgeClass { } String get_error_message() const { - return (String) client->lastError.traceback; + return static_cast(client->lastError.traceback); } uint8_t get_error_code() const { - return (uint8_t) client->lastError.code; + return static_cast(client->lastError.code); + } + + RpcError& get_last_client_error() const { + return client->lastError; } private: @@ -212,8 +226,10 @@ class BridgeClass { class BridgeClassUpdater { public: - static void safeUpdate(BridgeClass& bridge) { - bridge.update_safe(); // access private method + static void safeUpdate(BridgeClass* bridge) { + if (*bridge) { + bridge->update_safe(); + } } private: @@ -222,18 +238,20 @@ class BridgeClassUpdater { BridgeClass Bridge(Serial1); -void updateEntryPoint(void *, void *, void *){ - while(1){ - Bridge.update(); +inline void updateEntryPoint(void *, void *, void *){ + while (true) { + if (Bridge) { + Bridge.update(); + } k_msleep(1); } } static void safeUpdate(){ - BridgeClassUpdater::safeUpdate(Bridge); + BridgeClassUpdater::safeUpdate(&Bridge); } -void __loopHook(){ +inline void __loopHook(){ k_msleep(1); safeUpdate(); } diff --git a/src/monitor.h b/src/monitor.h index e4513b5..c0b7bec 100644 --- a/src/monitor.h +++ b/src/monitor.h @@ -27,21 +27,27 @@ template class BridgeMonitor: public Stream { -private: - BridgeClass& bridge; + BridgeClass* bridge; RingBufferN temp_buffer; - struct k_mutex monitor_mutex; + struct k_mutex monitor_mutex{}; bool is_connected = false; public: - BridgeMonitor(BridgeClass& bridge): bridge(bridge) {} + explicit BridgeMonitor(BridgeClass& bridge): bridge(&bridge) {} + + using Print::write; bool begin() { - return bridge.call(MON_CONNECTED_METHOD, is_connected); k_mutex_init(&monitor_mutex); + + bool bridge_started = (*bridge); + if (!bridge_started) { + bridge_started = bridge->begin(); + } + return bridge_started && bridge->call(MON_CONNECTED_METHOD, is_connected); } - bool isConnected() const { + explicit operator bool() const { return is_connected; } @@ -73,9 +79,11 @@ class BridgeMonitor: public Stream { int peek() override { k_mutex_lock(&monitor_mutex, K_FOREVER); if (temp_buffer.available()) { + k_mutex_unlock(&monitor_mutex); return temp_buffer.peek(); } k_mutex_unlock(&monitor_mutex); + return -1; } size_t write(uint8_t c) override { @@ -84,18 +92,14 @@ class BridgeMonitor: public Stream { size_t write(const uint8_t* buffer, size_t size) override { - MsgPack::str_t send_buffer; + String send_buffer; for (size_t i = 0; i < size; ++i) { -#ifdef ARDUINO - send_buffer += (char)buffer[i]; -#else - send_buffer.push_back(static_cast(buffer[i])); -#endif + send_buffer += static_cast(buffer[i]); } size_t written; - bool ret = bridge.call(MON_WRITE_METHOD, written, send_buffer); + const bool ret = bridge->call(MON_WRITE_METHOD, written, send_buffer); if (ret) { return written; } @@ -105,43 +109,34 @@ class BridgeMonitor: public Stream { bool reset() { bool res; - bool ok = bridge.call(MON_RESET_METHOD, res); + bool ok = bridge->call(MON_RESET_METHOD, res); if (ok && res) { is_connected = false; } return (ok && res); } - size_t write(String message) { - size_t size; - bool ok = bridge.call(MON_WRITE_METHOD, size, message); - - if (!ok) return 0; - - return size; - } +private: + void _read(size_t size) { - int _read(size_t size) { + if (size == 0) return; - if (size == 0) return 0; + k_mutex_lock(&monitor_mutex, K_FOREVER); MsgPack::arr_t message; - bool ret = bridge.call(MON_READ_METHOD, message, size); + bool ret = bridge->call(MON_READ_METHOD, message, size); - k_mutex_lock(&monitor_mutex, K_FOREVER); if (ret) { for (size_t i = 0; i < message.size(); ++i) { temp_buffer.store_char(static_cast(message[i])); } - return message.size(); } // if (bridge.lastError.code > NO_ERR) { // is_connected = false; // } - k_mutex_unlock(&monitor_mutex); - return 0; + k_mutex_unlock(&monitor_mutex); } diff --git a/src/tcp_client.h b/src/tcp_client.h new file mode 100644 index 0000000..7955bf2 --- /dev/null +++ b/src/tcp_client.h @@ -0,0 +1,178 @@ +/* + This file is part of the Arduino_RouterBridge library. + + Copyright (c) 2025 Arduino SA + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. + +*/ + +#pragma once + +#ifndef BRIDGE_TCP_CLIENT_H +#define BRIDGE_TCP_CLIENT_H + +#define TCP_CONNECT_METHOD "tcp/connect" +#define TCP_CLOSE_METHOD "tcp/close" +#define TCP_WRITE_METHOD "tcp/write" +#define TCP_READ_METHOD "tcp/read" + +#include +#include +#include "bridge.h" + +#define DEFAULT_TCP_CLIENT_BUF_SIZE 512 + +template +class BridgeTCPClient final: public Client { + + BridgeClass* bridge; + uint32_t connection_id{}; + RingBufferN temp_buffer; + struct k_mutex client_mutex{}; + bool _connected = false; + +public: + explicit BridgeTCPClient(BridgeClass& bridge): bridge(&bridge) {} + + bool begin() { + k_mutex_init(&client_mutex); + if (!(*bridge)) { + return bridge->begin(); + } + return true; + } + + int connect(IPAddress ip, uint16_t port) override { + return connect(ip.toString().c_str(), port); + } + + int connect(const char *host, uint16_t port) override { + + String send_buffer = host; + send_buffer += ":"; + send_buffer += String(port); + + k_mutex_lock(&client_mutex, K_FOREVER); + + const bool resp = bridge->call(TCP_CONNECT_METHOD, connection_id, send_buffer); + + if (!resp) { + _connected = false; + k_mutex_unlock(&client_mutex); + return -1; + } + _connected = true; + + k_mutex_unlock(&client_mutex); + + return 0; + } + + size_t write(uint8_t c) override { + return write(&c, 1); + } + + size_t write(const uint8_t *buf, size_t size) override { + String send_buffer; + + for (size_t i = 0; i < size; ++i) { + send_buffer += static_cast(buf[i]); + } + + size_t written; + const bool ret = bridge->call(TCP_WRITE_METHOD, written, send_buffer); + if (ret) { + return written; + } + + return 0; + } + + int available() override { + k_mutex_lock(&client_mutex, K_FOREVER); + const int size = temp_buffer.availableForStore(); + if (size > 0) _read(size); + const int available = temp_buffer.available(); + k_mutex_unlock(&client_mutex); + return available; + } + + int read() override { + uint8_t c; + read(&c, 1); + return c; + } + + int read(uint8_t *buf, size_t size) override { + k_mutex_lock(&client_mutex, K_FOREVER); + int i = 0; + while (temp_buffer.available() && i < size) { + buf[i++] = temp_buffer.read_char(); + } + k_mutex_unlock(&client_mutex); + return i; + } + + int peek() override { + k_mutex_lock(&client_mutex, K_FOREVER); + if (temp_buffer.available()) { + k_mutex_unlock(&client_mutex); + return temp_buffer.peek(); + } + k_mutex_unlock(&client_mutex); + return -1; + } + + void flush() override { + // No-op: flush is implemented for Client subclasses using an output buffer + } + + void stop() override { + k_mutex_lock(&client_mutex, K_FOREVER); + String msg; + const bool resp = bridge->call(TCP_CLOSE_METHOD, msg, connection_id); + if (resp) { + _connected = false; + } + k_mutex_unlock(&client_mutex); + } + + uint8_t connected() override { + if (_connected) return 1; + return 0; + } + + operator bool() override { + return available() || connected(); + } + +private: + void _read(size_t size) { + + if (size == 0 || !_connected) return; + + k_mutex_lock(&client_mutex, K_FOREVER); + + MsgPack::arr_t message; + const bool ret = bridge->call(TCP_READ_METHOD, message, size); + + if (ret) { + for (size_t i = 0; i < message.size(); ++i) { + temp_buffer.store_char(static_cast(message[i])); + } + } + + if (bridge->get_last_client_error().code > NO_ERR) { + _connected = false; + } + + k_mutex_unlock(&client_mutex); + } + +}; + + +#endif //BRIDGE_TCP_CLIENT_H