From b0800a9e9ead59b55146e98d9fae31356e95fb0b Mon Sep 17 00:00:00 2001 From: Lucio Rossi Date: Wed, 6 Aug 2025 18:05:18 +0200 Subject: [PATCH 01/26] feat: tcp_server.h stub --- .gitignore | 4 +- src/tcp_client.h | 7 ++- src/tcp_server.h | 114 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 123 insertions(+), 2 deletions(-) create mode 100644 src/tcp_server.h diff --git a/.gitignore b/.gitignore index d48c759..8bc50b1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ .idea -.vscode \ No newline at end of file +.vscode + +CMakeLists.txt \ No newline at end of file diff --git a/src/tcp_client.h b/src/tcp_client.h index 7955bf2..108e41e 100644 --- a/src/tcp_client.h +++ b/src/tcp_client.h @@ -25,6 +25,7 @@ #define DEFAULT_TCP_CLIENT_BUF_SIZE 512 + template class BridgeTCPClient final: public Client { @@ -149,6 +150,10 @@ class BridgeTCPClient final: public Client { return available() || connected(); } + friend class BridgeTCPServer; + + using Print::write; + private: void _read(size_t size) { @@ -171,7 +176,7 @@ class BridgeTCPClient final: public Client { k_mutex_unlock(&client_mutex); } - + }; diff --git a/src/tcp_server.h b/src/tcp_server.h new file mode 100644 index 0000000..3c16cd0 --- /dev/null +++ b/src/tcp_server.h @@ -0,0 +1,114 @@ +/* + 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_SERVER_H +#define BRIDGE_TCP_SERVER_H + +#define TCP_LISTEN_METHOD "tcp/listen" +#define TCP_ACCEPT_METHOD "tcp/accept" + +#include +#include +#include "bridge.h" +#include "tcp_client.h" + +#define DEFAULT_TCP_SERVER_BUF_SIZE 512 + + +template +class BridgeTCPServer final: public Server { + BridgeClass* bridge; + IPAddress _addr{}; + uint16_t _port; + bool _listening = false; + uint32_t listener_id; + struct k_mutex server_mutex{}; + +public: + explicit BridgeTCPServer(BridgeClass& bridge, const IPAddress& addr, uint16_t port): bridge(&bridge), _addr(addr), _port(port) {} + + explicit BridgeTCPServer(BridgeClass& bridge, uint16_t port): bridge(&bridge), _addr(IP_ANY_TYPE), _port(port) {} + + bool begin() { + k_mutex_init(&server_mutex); + if (!(*bridge)) { + if (!bridge->begin()) { + return false; + } + } + + k_mutex_lock(&server_mutex, K_FOREVER); + + String conn_str = addr.toString() + String(_port); + const bool ret = bridge->call(TCP_LISTEN_METHOD, listener_id, conn_str); + // TODO is listener_id one per server obj? + + if (ret) { + _listening = true; + } + + k_mutex_unlock(&server_mutex); + + return ret; + } + + void begin(uint16_t port) { + _port = port; + begin(); + } + + + BridgeTCPClient accept() { + k_mutex_lock(&server_mutex, K_FOREVER); + + uint32_t connection_id = 0; + const bool ret = bridge->call(TCP_ACCEPT_METHOD, connection_id, listener_id); + + k_mutex_unlock(&server_mutex); + + if (ret && connection_id != 0) { // TODO is connection_id 0 acceptable??? + return BridgeTCPClient(*bridge, connection_id); + } + + // Return invalid client + return BridgeTCPClient(*bridge, 0); + } + + size_t write(uint8_t c) override { + return write(&c, 1); + } + + size_t write(const uint8_t *buf, size_t size) override { + // Broadcasting to all clients would require tracking them + // For now, this is not implemented + // TODO a logic to resolve which port-socket is the target of the write + return 0; + } + + bool is_listening() const { + return _listening; + } + + uint16_t getPort() const { + return _port; + } + + operator bool() const { + return _listening; + } + + using Print::write; + +}; + +#endif //BRIDGE_TCP_SERVER_H \ No newline at end of file From 7a455d2c4d44273f41db996bf5e06be400958503 Mon Sep 17 00:00:00 2001 From: Lucio Rossi Date: Wed, 10 Sep 2025 12:44:39 +0200 Subject: [PATCH 02/26] mod: various TBT --- src/tcp_client.h | 46 +++++++++++++++++++++++++++++++------- src/tcp_server.h | 57 ++++++++++++++++++++++++++++-------------------- 2 files changed, 71 insertions(+), 32 deletions(-) diff --git a/src/tcp_client.h b/src/tcp_client.h index 108e41e..0ab876a 100644 --- a/src/tcp_client.h +++ b/src/tcp_client.h @@ -15,6 +15,7 @@ #define BRIDGE_TCP_CLIENT_H #define TCP_CONNECT_METHOD "tcp/connect" +#define TCP_CONNECT_SSL_METHOD "tcp/connectSSL" #define TCP_CLOSE_METHOD "tcp/close" #define TCP_WRITE_METHOD "tcp/write" #define TCP_READ_METHOD "tcp/read" @@ -27,7 +28,7 @@ template -class BridgeTCPClient final: public Client { +class BridgeTCPClient : public Client { BridgeClass* bridge; uint32_t connection_id{}; @@ -52,13 +53,13 @@ class BridgeTCPClient final: public Client { int connect(const char *host, uint16_t port) override { - String send_buffer = host; - send_buffer += ":"; - send_buffer += String(port); + if (_connected) return 0; + + String hostname = host; k_mutex_lock(&client_mutex, K_FOREVER); - const bool resp = bridge->call(TCP_CONNECT_METHOD, connection_id, send_buffer); + const bool resp = bridge->call(TCP_CONNECT_METHOD, connection_id, hostname, port); if (!resp) { _connected = false; @@ -72,19 +73,44 @@ class BridgeTCPClient final: public Client { return 0; } + int connectSSL(const char *host, uint16_t port, const char *ca_cert) { + + if (_connected) return 0; + + String hostname = host; + String ca_cert_str = ca_cert; + + k_mutex_lock(&client_mutex, K_FOREVER); + + const bool resp = bridge->call(TCP_CONNECT_SSL_METHOD, connection_id, hostname, port, ca_cert_str); + + 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; + + if (!_connected) return 0; + + MsgPack::arr_t payload; for (size_t i = 0; i < size; ++i) { - send_buffer += static_cast(buf[i]); + payload.push_back(buf[i]); } size_t written; - const bool ret = bridge->call(TCP_WRITE_METHOD, written, send_buffer); + const bool ret = bridge->call(TCP_WRITE_METHOD, written, connection_id, payload); if (ret) { return written; } @@ -131,6 +157,10 @@ class BridgeTCPClient final: public Client { // No-op: flush is implemented for Client subclasses using an output buffer } + void close() { + stop(); + } + void stop() override { k_mutex_lock(&client_mutex, K_FOREVER); String msg; diff --git a/src/tcp_server.h b/src/tcp_server.h index 3c16cd0..465dc58 100644 --- a/src/tcp_server.h +++ b/src/tcp_server.h @@ -31,52 +31,44 @@ class BridgeTCPServer final: public Server { IPAddress _addr{}; uint16_t _port; bool _listening = false; - uint32_t listener_id; + uint32_t listener_id = 0; + uint32_t connection_id = 0; struct k_mutex server_mutex{}; public: explicit BridgeTCPServer(BridgeClass& bridge, const IPAddress& addr, uint16_t port): bridge(&bridge), _addr(addr), _port(port) {} - explicit BridgeTCPServer(BridgeClass& bridge, uint16_t port): bridge(&bridge), _addr(IP_ANY_TYPE), _port(port) {} + // explicit BridgeTCPServer(BridgeClass& bridge, uint16_t port): bridge(&bridge), _addr(INADDR_NONE), _port(port) {} - bool begin() { + void begin() override { k_mutex_init(&server_mutex); + if (!(*bridge)) { - if (!bridge->begin()) { - return false; - } + while (!bridge->begin()); } k_mutex_lock(&server_mutex, K_FOREVER); - String conn_str = addr.toString() + String(_port); - const bool ret = bridge->call(TCP_LISTEN_METHOD, listener_id, conn_str); - // TODO is listener_id one per server obj? - - if (ret) { - _listening = true; - } + String hostname = _addr.toString(); + _listening = bridge->call(TCP_LISTEN_METHOD, listener_id, hostname, _port); k_mutex_unlock(&server_mutex); - return ret; } - void begin(uint16_t port) { - _port = port; - begin(); - } + BridgeTCPClient accept() { + if (connection_id != 0) { + return BridgeTCPClient(*bridge, connection_id); + } - BridgeTCPClient accept() { k_mutex_lock(&server_mutex, K_FOREVER); - uint32_t connection_id = 0; const bool ret = bridge->call(TCP_ACCEPT_METHOD, connection_id, listener_id); k_mutex_unlock(&server_mutex); - if (ret && connection_id != 0) { // TODO is connection_id 0 acceptable??? + if (ret && connection_id != 0) { // connection_id 0 marks an invalid connection return BridgeTCPClient(*bridge, connection_id); } @@ -89,12 +81,29 @@ class BridgeTCPServer final: public Server { } size_t write(const uint8_t *buf, size_t size) override { - // Broadcasting to all clients would require tracking them - // For now, this is not implemented - // TODO a logic to resolve which port-socket is the target of the write + + BridgeTCPClient client = accept(); + + if (client) { + return client.write(buf, size); + } + return 0; } + void close() { + k_mutex_lock(&server_mutex, K_FOREVER); + + String msg; + const bool ret = bridge->call(TCP_CLOSE_METHOD, msg, listener_id); + + if (ret) { + _listening = false; + } + + k_mutex_unlock(&server_mutex); + } + bool is_listening() const { return _listening; } From 88057f33de6f2d7c31a9b31460529aa341c2586e Mon Sep 17 00:00:00 2001 From: Lucio Rossi Date: Mon, 15 Sep 2025 13:07:36 +0200 Subject: [PATCH 03/26] fix: main .h not including tcp classes fix: client _read not passing connection_id impr: server tcp/closeListener method examples: client and clientSSL --- examples/client/client.ino | 51 +++++++++++++++ examples/clientSSL/clientSSL.ino | 106 +++++++++++++++++++++++++++++++ src/Arduino_RouterBridge.h | 2 + src/tcp_client.h | 8 +-- src/tcp_server.h | 4 +- 5 files changed, 166 insertions(+), 5 deletions(-) create mode 100644 examples/client/client.ino create mode 100644 examples/clientSSL/clientSSL.ino diff --git a/examples/client/client.ino b/examples/client/client.ino new file mode 100644 index 0000000..a6e37d3 --- /dev/null +++ b/examples/client/client.ino @@ -0,0 +1,51 @@ +#include + +BridgeTCPClient<> client(Bridge); + +void setup() { + Serial.begin(115200); + + if (!Bridge.begin()) { + Serial.println("cannot setup Bridge"); + while (true) {} + } + + +} + +void loop() { + + Serial.println("\nStarting connection to server..."); + /* if you get a connection, report back via serial: */ + if (client.connect("arduino.tips", 80) < 0) { + Serial.println("unable to connect to server"); + return; + } + + Serial.println("connected to server"); + /* Make an HTTP request: */ + size_t w = client.println("GET /asciilogo.txt HTTP/1.1"); + w += client.println("Host: arduino.tips"); + w += client.println("User-Agent: Arduino"); + w += client.println("Connection: close"); + w += client.println(); + + /* if there are incoming bytes available from the server, + * read them and print them: + */ + while (client.connected()) { + size_t len = client.available(); + if (len) { + uint8_t buff[len]; + client.read(buff, len); + Serial.write(buff, len); + } + delay(0); + } + + /* if the server's disconnected, stop the client: */ + Serial.println(); + Serial.println("disconnecting from server."); + client.stop(); + delay(1000); +} diff --git a/examples/clientSSL/clientSSL.ino b/examples/clientSSL/clientSSL.ino new file mode 100644 index 0000000..c850acc --- /dev/null +++ b/examples/clientSSL/clientSSL.ino @@ -0,0 +1,106 @@ +#include + +static const char iot[] = { +/* https://iot.arduino.cc:8885 */ +"-----BEGIN CERTIFICATE-----\n" +"MIIB0DCCAXagAwIBAgIUb62eK/Vv1baaPAaY5DADBUbxB1owCgYIKoZIzj0EAwIw\n" +"RTELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkFyZHVpbm8gTExDIFVTMQswCQYDVQQL\n" +"EwJJVDEQMA4GA1UEAxMHQXJkdWlubzAgFw0yNTAxMTAxMDUzMjJaGA8yMDU1MDEw\n" +"MzEwNTMyMlowRTELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkFyZHVpbm8gTExDIFVT\n" +"MQswCQYDVQQLEwJJVDEQMA4GA1UEAxMHQXJkdWlubzBZMBMGByqGSM49AgEGCCqG\n" +"SM49AwEHA0IABKHhU2w1UhozDegrrFsSwY9QN7M+ZJug7icCNceNWhBF0Mr1UuyX\n" +"8pr/gcbieZc/0znG16HMa2GFcPY7rmIdccijQjBAMA8GA1UdEwEB/wQFMAMBAf8w\n" +"DgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBRCZSmE0ASI0cYD9AmzeOM7EijgPjAK\n" +"BggqhkjOPQQDAgNIADBFAiEAz6TLYP9eiVOr/cVU/11zwGofe/FoNe4p1BlzMl7G\n" +"VVACIG8tL3Ta2WbIOaUVpBL2gfLuI9WSW1sR++zXP+zFhmen\n" +"-----END CERTIFICATE-----\n" +}; + +static const char ubi[] = { +/* https://ubidefeo.com:443 */ +"-----BEGIN CERTIFICATE-----\n" +"MIIF3jCCA8agAwIBAgIQAf1tMPyjylGoG7xkDjUDLTANBgkqhkiG9w0BAQwFADCB\n" +"iDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0pl\n" +"cnNleSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNV\n" +"BAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAw\n" +"MjAxMDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMCVVMxEzARBgNV\n" +"BAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVU\n" +"aGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBSU0EgQ2Vy\n" +"dGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK\n" +"AoICAQCAEmUXNg7D2wiz0KxXDXbtzSfTTK1Qg2HiqiBNCS1kCdzOiZ/MPans9s/B\n" +"3PHTsdZ7NygRK0faOca8Ohm0X6a9fZ2jY0K2dvKpOyuR+OJv0OwWIJAJPuLodMkY\n" +"tJHUYmTbf6MG8YgYapAiPLz+E/CHFHv25B+O1ORRxhFnRghRy4YUVD+8M/5+bJz/\n" +"Fp0YvVGONaanZshyZ9shZrHUm3gDwFA66Mzw3LyeTP6vBZY1H1dat//O+T23LLb2\n" +"VN3I5xI6Ta5MirdcmrS3ID3KfyI0rn47aGYBROcBTkZTmzNg95S+UzeQc0PzMsNT\n" +"79uq/nROacdrjGCT3sTHDN/hMq7MkztReJVni+49Vv4M0GkPGw/zJSZrM233bkf6\n" +"c0Plfg6lZrEpfDKEY1WJxA3Bk1QwGROs0303p+tdOmw1XNtB1xLaqUkL39iAigmT\n" +"Yo61Zs8liM2EuLE/pDkP2QKe6xJMlXzzawWpXhaDzLhn4ugTncxbgtNMs+1b/97l\n" +"c6wjOy0AvzVVdAlJ2ElYGn+SNuZRkg7zJn0cTRe8yexDJtC/QV9AqURE9JnnV4ee\n" +"UB9XVKg+/XRjL7FQZQnmWEIuQxpMtPAlR1n6BB6T1CZGSlCBst6+eLf8ZxXhyVeE\n" +"Hg9j1uliutZfVS7qXMYoCAQlObgOK6nyTJccBz8NUvXt7y+CDwIDAQABo0IwQDAd\n" +"BgNVHQ4EFgQUU3m/WqorSs9UgOHYm8Cd8rIDZsswDgYDVR0PAQH/BAQDAgEGMA8G\n" +"A1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAFzUfA3P9wF9QZllDHPF\n" +"Up/L+M+ZBn8b2kMVn54CVVeWFPFSPCeHlCjtHzoBN6J2/FNQwISbxmtOuowhT6KO\n" +"VWKR82kV2LyI48SqC/3vqOlLVSoGIG1VeCkZ7l8wXEskEVX/JJpuXior7gtNn3/3\n" +"ATiUFJVDBwn7YKnuHKsSjKCaXqeYalltiz8I+8jRRa8YFWSQEg9zKC7F4iRO/Fjs\n" +"8PRF/iKz6y+O0tlFYQXBl2+odnKPi4w2r78NBc5xjeambx9spnFixdjQg3IM8WcR\n" +"iQycE0xyNN+81XHfqnHd4blsjDwSXWXavVcStkNr/+XeTWYRUc+ZruwXtuhxkYze\n" +"Sf7dNXGiFSeUHM9h4ya7b6NnJSFd5t0dCy5oGzuCr+yDZ4XUmFF0sbmZgIn/f3gZ\n" +"XHlKYC6SQK5MNyosycdiyA5d9zZbyuAlJQG03RoHnHcAP9Dc1ew91Pq7P8yF1m9/\n" +"qS3fuQL39ZeatTXaw2ewh0qpKJ4jjv9cJ2vhsE/zB+4ALtRZh8tSQZXq9EfX7mRB\n" +"VXyNWQKV3WKdwrnuWih0hKWbt5DHDAff9Yk2dDLWKMGwsAvgnEzDHNb842m1R0aB\n" +"L6KCq9NjRHDEjf8tM7qtj3u1cIiuPhnPQCjY/MiQu12ZIvVS5ljFH4gxQ+6IHdfG\n" +"jjxDah2nGN59PRbxYvnKkKj9\n" +"-----END CERTIFICATE-----\n" +}; + +BridgeTCPClient<> client(Bridge); + +void setup() { + /* Initialize Serial */ + Serial.begin(115200); + /* Initialize RPC transport Serial1 */ + //Serial1.begin(115200); + if (!Bridge.begin()) { + Serial.println("cannot setup Bridge"); + while (true) {} + } + /* Configure TLS CA */ + //client.setCACert(ubi); +} + +void loop() { + Serial.println("\nStarting connection to server..."); + /* if you get a connection, report back via serial: */ + if (client.connectSSL("ubidefeo.com", 443, ubi) < 0) { + Serial.println("unable to connect to server"); + return; + } + + Serial.println("connected to server"); + /* Make aHTTP request: */ + size_t w = client.write("GET /files/supsi.txt HTTP/1.1\n"); + w += client.println("Host: ubidefeo.com"); + w += client.println("User-Agent: Arduino"); + w += client.println("Connection: close"); + w += client.println(); + + /* if there are incoming bytes available from the server, + * read them and print them: + */ + while (client.connected()) { + size_t len = client.available(); + if (len) { + uint8_t buff[len]; + client.read(buff, len); + Serial.write(buff, len); + } + delay(0); + } + + /* if the server's disconnected, stop the client: */ + Serial.println(); + Serial.println("disconnecting from server."); + client.stop(); + delay(1000); +} diff --git a/src/Arduino_RouterBridge.h b/src/Arduino_RouterBridge.h index 26f6b51..3174d0f 100644 --- a/src/Arduino_RouterBridge.h +++ b/src/Arduino_RouterBridge.h @@ -15,5 +15,7 @@ #include "Arduino.h" #include "bridge.h" #include "monitor.h" +#include "tcp_client.h" +#include "tcp_server.h" #endif //ARDUINO_ROUTER_BRIDGE_H diff --git a/src/tcp_client.h b/src/tcp_client.h index 0ab876a..a0b559b 100644 --- a/src/tcp_client.h +++ b/src/tcp_client.h @@ -122,9 +122,9 @@ class BridgeTCPClient : public Client { k_mutex_lock(&client_mutex, K_FOREVER); const int size = temp_buffer.availableForStore(); if (size > 0) _read(size); - const int available = temp_buffer.available(); + const int _available = temp_buffer.available(); k_mutex_unlock(&client_mutex); - return available; + return _available; } int read() override { @@ -180,7 +180,7 @@ class BridgeTCPClient : public Client { return available() || connected(); } - friend class BridgeTCPServer; + //friend class BridgeTCPServer; using Print::write; @@ -192,7 +192,7 @@ class BridgeTCPClient : public Client { k_mutex_lock(&client_mutex, K_FOREVER); MsgPack::arr_t message; - const bool ret = bridge->call(TCP_READ_METHOD, message, size); + const bool ret = bridge->call(TCP_READ_METHOD, message, connection_id, size); if (ret) { for (size_t i = 0; i < message.size(); ++i) { diff --git a/src/tcp_server.h b/src/tcp_server.h index 465dc58..b12786c 100644 --- a/src/tcp_server.h +++ b/src/tcp_server.h @@ -16,6 +16,8 @@ #define TCP_LISTEN_METHOD "tcp/listen" #define TCP_ACCEPT_METHOD "tcp/accept" +#define TCP_CLOSE_LISTENER_METHOD "tcp/closeListener" + #include #include @@ -95,7 +97,7 @@ class BridgeTCPServer final: public Server { k_mutex_lock(&server_mutex, K_FOREVER); String msg; - const bool ret = bridge->call(TCP_CLOSE_METHOD, msg, listener_id); + const bool ret = bridge->call(TCP_CLOSE_LISTENER_METHOD, msg, listener_id); if (ret) { _listening = false; From 75b73ce57300f0009db9235aaf5b6b43c382eed0 Mon Sep 17 00:00:00 2001 From: Lucio Rossi Date: Mon, 15 Sep 2025 19:29:19 +0200 Subject: [PATCH 04/26] feat: BridgeTCPClient constructor for established connections feat: tcp_client getId method feat: server-side client connection/disconnection examples: server.ino with python test script --- examples/server/python/main.py | 27 ++++++++++++++++++++ examples/server/server.ino | 45 ++++++++++++++++++++++++++++++++++ src/tcp_client.h | 6 +++++ src/tcp_server.h | 21 ++++++++++++---- 4 files changed, 94 insertions(+), 5 deletions(-) create mode 100644 examples/server/python/main.py create mode 100644 examples/server/server.ino diff --git a/examples/server/python/main.py b/examples/server/python/main.py new file mode 100644 index 0000000..2a9148f --- /dev/null +++ b/examples/server/python/main.py @@ -0,0 +1,27 @@ +# A python sketch that uses RPC Bridge to test the server.ino + +import time +from arduino.app_utils import * + + +def log(msg): + with open("./log.log", "a") as f: + f.write(str(msg) + "\n") + +def main(): + res = Bridge.call("tcp/connect", "127.0.0.1", 5678) + log(f"Connection attempt id: {res}") + + written = Bridge.call("tcp/write", res, "Hello friend") + log(f"Written msg of len: {written}") + + time.sleep(1) + + ok = Bridge.call("tcp/close", res) + log(f"Closed connection: {ok}") + +if __name__ == "__main__": + + while True: + main() + time.sleep(1) diff --git a/examples/server/server.ino b/examples/server/server.ino new file mode 100644 index 0000000..92a5db9 --- /dev/null +++ b/examples/server/server.ino @@ -0,0 +1,45 @@ +#include + +IPAddress localhost(127, 0, 0, 1); +BridgeTCPServer<> server(Bridge, localhost, 5678); + +void setup() { + Serial.begin(115200); + + if (!Bridge.begin()) { + Serial.println("cannot setup Bridge"); + while (true) {} + } + + server.begin(); + +} + +void loop() { + Serial.println("loop"); + + BridgeTCPClient<> client = server.accept(); + + if (client.connected() == 1){ + Serial.print("client "); + Serial.print(client.getId()); + Serial.println(" connected"); + } + + if (client) { + Serial.println("A client established a connection"); + } + + while (client.connected()) { + size_t len = client.available(); + if (len) { + Serial.println("Message received from client"); + uint8_t buff[len]; + client.read(buff, len); + Serial.write(buff, len); + } + } + + server.disconnect(); // Disconnects the client server-side + +} diff --git a/src/tcp_client.h b/src/tcp_client.h index a0b559b..8874314 100644 --- a/src/tcp_client.h +++ b/src/tcp_client.h @@ -39,6 +39,8 @@ class BridgeTCPClient : public Client { public: explicit BridgeTCPClient(BridgeClass& bridge): bridge(&bridge) {} + BridgeTCPClient(BridgeClass& bridge, uint32_t connection_id, bool connected=true): bridge(&bridge), connection_id(connection_id), _connected {connected} {} + bool begin() { k_mutex_init(&client_mutex); if (!(*bridge)) { @@ -95,6 +97,10 @@ class BridgeTCPClient : public Client { return 0; } + uint32_t getId() const { + return connection_id; + } + size_t write(uint8_t c) override { return write(&c, 1); } diff --git a/src/tcp_server.h b/src/tcp_server.h index b12786c..4e242f5 100644 --- a/src/tcp_server.h +++ b/src/tcp_server.h @@ -35,6 +35,7 @@ class BridgeTCPServer final: public Server { bool _listening = false; uint32_t listener_id = 0; uint32_t connection_id = 0; + bool _connected = false; struct k_mutex server_mutex{}; public: @@ -60,7 +61,7 @@ class BridgeTCPServer final: public Server { BridgeTCPClient accept() { - if (connection_id != 0) { + if (_connected) { return BridgeTCPClient(*bridge, connection_id); } @@ -70,12 +71,15 @@ class BridgeTCPServer final: public Server { k_mutex_unlock(&server_mutex); - if (ret && connection_id != 0) { // connection_id 0 marks an invalid connection + if (ret) { // connection_id 0 marks an invalid connection + _connected = true; return BridgeTCPClient(*bridge, connection_id); + } else { + _connected = false; + // Return invalid client + return BridgeTCPClient(*bridge, 0, false); } - // Return invalid client - return BridgeTCPClient(*bridge, 0); } size_t write(uint8_t c) override { @@ -86,7 +90,7 @@ class BridgeTCPServer final: public Server { BridgeTCPClient client = accept(); - if (client) { + if (client && _connected) { return client.write(buf, size); } @@ -106,6 +110,13 @@ class BridgeTCPServer final: public Server { k_mutex_unlock(&server_mutex); } + void disconnect() { + k_mutex_lock(&server_mutex, K_FOREVER); + _connected = false; + connection_id = 0; + k_mutex_unlock(&server_mutex); + } + bool is_listening() const { return _listening; } From 216937ec225d1df13b15951c40a121002b9568a7 Mon Sep 17 00:00:00 2001 From: Lucio Rossi Date: Tue, 16 Sep 2025 11:48:19 +0200 Subject: [PATCH 05/26] mod: unused import in tcp_server --- src/tcp_server.h | 1 - 1 file changed, 1 deletion(-) diff --git a/src/tcp_server.h b/src/tcp_server.h index 4e242f5..68a39d8 100644 --- a/src/tcp_server.h +++ b/src/tcp_server.h @@ -20,7 +20,6 @@ #include -#include #include "bridge.h" #include "tcp_client.h" From 47da3c9d0a8304f04d071681af364b95af35e5cb Mon Sep 17 00:00:00 2001 From: Lucio Rossi Date: Tue, 16 Sep 2025 18:16:44 +0200 Subject: [PATCH 06/26] mod: k_yield instead of k_msleep --- src/bridge.h | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/src/bridge.h b/src/bridge.h index 90cfe3a..dc2c2b6 100644 --- a/src/bridge.h +++ b/src/bridge.h @@ -122,10 +122,9 @@ class BridgeClass { if (k_mutex_lock(&write_mutex, K_MSEC(10)) == 0){ server->send_response(req); k_mutex_unlock(&write_mutex); - k_msleep(1); break; } else { - k_msleep(1); + k_yield(); } } @@ -142,10 +141,9 @@ class BridgeClass { if (k_mutex_lock(&write_mutex, K_MSEC(10)) == 0) { client->send_rpc(method, msg_id_wait, std::forward(args)...); k_mutex_unlock(&write_mutex); - k_msleep(1); break; } else { - k_msleep(1); + k_yield(); } } @@ -154,13 +152,12 @@ class BridgeClass { if (k_mutex_lock(&read_mutex, K_MSEC(10)) == 0 ) { if (client->get_response(msg_id_wait, result)) { k_mutex_unlock(&read_mutex); - k_msleep(1); break; } k_mutex_unlock(&read_mutex); k_msleep(1); } else { - k_msleep(1); + k_yield(); } } @@ -210,10 +207,9 @@ class BridgeClass { if (k_mutex_lock(&write_mutex, K_MSEC(10)) == 0){ server->send_response(req); k_mutex_unlock(&write_mutex); - k_msleep(1); break; } else { - k_msleep(1); + k_yield(); } } @@ -243,7 +239,7 @@ inline void updateEntryPoint(void *, void *, void *){ if (Bridge) { Bridge.update(); } - k_msleep(1); + k_yield(); } } @@ -253,7 +249,7 @@ static void safeUpdate(){ // leave as is void __loopHook(void){ - k_msleep(1); + k_yield(); safeUpdate(); } From 58f86d6a6cdf715c7837d68162fb34ad2da11e3a Mon Sep 17 00:00:00 2001 From: Lucio Rossi Date: Wed, 17 Sep 2025 15:30:35 +0200 Subject: [PATCH 07/26] fix: tcp_server must not accept if it is not listening --- src/tcp_server.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/tcp_server.h b/src/tcp_server.h index 68a39d8..ad4c0a5 100644 --- a/src/tcp_server.h +++ b/src/tcp_server.h @@ -60,6 +60,10 @@ class BridgeTCPServer final: public Server { BridgeTCPClient accept() { + if (!_listening) { + return BridgeTCPClient(*bridge, 0, false); + } + if (_connected) { return BridgeTCPClient(*bridge, connection_id); } From aaf220ad66c39d78cf69cae2784cc70309b82755 Mon Sep 17 00:00:00 2001 From: Lucio Rossi Date: Wed, 17 Sep 2025 15:42:08 +0200 Subject: [PATCH 08/26] examples: Serial->Monitor debug prints; rem ubi's website --- examples/client/client.ino | 17 +++--- examples/clientSSL/clientSSL.ino | 96 ++++++++++---------------------- examples/server/server.ino | 19 ++++--- 3 files changed, 48 insertions(+), 84 deletions(-) diff --git a/examples/client/client.ino b/examples/client/client.ino index a6e37d3..7fccb58 100644 --- a/examples/client/client.ino +++ b/examples/client/client.ino @@ -3,26 +3,27 @@ BridgeTCPClient<> client(Bridge); void setup() { - Serial.begin(115200); if (!Bridge.begin()) { - Serial.println("cannot setup Bridge"); while (true) {} } + if (!Monitor.begin()) { + while (true) {} + } } void loop() { - Serial.println("\nStarting connection to server..."); + Monitor.println("\nStarting connection to server..."); /* if you get a connection, report back via serial: */ if (client.connect("arduino.tips", 80) < 0) { - Serial.println("unable to connect to server"); + Monitor.println("unable to connect to server"); return; } - Serial.println("connected to server"); + Monitor.println("connected to server"); /* Make an HTTP request: */ size_t w = client.println("GET /asciilogo.txt HTTP/1.1"); w += client.println("Host: arduino.tips"); @@ -38,14 +39,14 @@ void loop() { if (len) { uint8_t buff[len]; client.read(buff, len); - Serial.write(buff, len); + Monitor.write(buff, len); } delay(0); } /* if the server's disconnected, stop the client: */ - Serial.println(); - Serial.println("disconnecting from server."); + Monitor.println(); + Monitor.println("disconnecting from server."); client.stop(); delay(1000); } diff --git a/examples/clientSSL/clientSSL.ino b/examples/clientSSL/clientSSL.ino index c850acc..9e8c269 100644 --- a/examples/clientSSL/clientSSL.ino +++ b/examples/clientSSL/clientSSL.ino @@ -1,86 +1,48 @@ #include -static const char iot[] = { -/* https://iot.arduino.cc:8885 */ +static const char ca_cert[] = { "-----BEGIN CERTIFICATE-----\n" -"MIIB0DCCAXagAwIBAgIUb62eK/Vv1baaPAaY5DADBUbxB1owCgYIKoZIzj0EAwIw\n" -"RTELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkFyZHVpbm8gTExDIFVTMQswCQYDVQQL\n" -"EwJJVDEQMA4GA1UEAxMHQXJkdWlubzAgFw0yNTAxMTAxMDUzMjJaGA8yMDU1MDEw\n" -"MzEwNTMyMlowRTELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkFyZHVpbm8gTExDIFVT\n" -"MQswCQYDVQQLEwJJVDEQMA4GA1UEAxMHQXJkdWlubzBZMBMGByqGSM49AgEGCCqG\n" -"SM49AwEHA0IABKHhU2w1UhozDegrrFsSwY9QN7M+ZJug7icCNceNWhBF0Mr1UuyX\n" -"8pr/gcbieZc/0znG16HMa2GFcPY7rmIdccijQjBAMA8GA1UdEwEB/wQFMAMBAf8w\n" -"DgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBRCZSmE0ASI0cYD9AmzeOM7EijgPjAK\n" -"BggqhkjOPQQDAgNIADBFAiEAz6TLYP9eiVOr/cVU/11zwGofe/FoNe4p1BlzMl7G\n" -"VVACIG8tL3Ta2WbIOaUVpBL2gfLuI9WSW1sR++zXP+zFhmen\n" -"-----END CERTIFICATE-----\n" -}; - -static const char ubi[] = { -/* https://ubidefeo.com:443 */ -"-----BEGIN CERTIFICATE-----\n" -"MIIF3jCCA8agAwIBAgIQAf1tMPyjylGoG7xkDjUDLTANBgkqhkiG9w0BAQwFADCB\n" -"iDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0pl\n" -"cnNleSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNV\n" -"BAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAw\n" -"MjAxMDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMCVVMxEzARBgNV\n" -"BAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVU\n" -"aGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBSU0EgQ2Vy\n" -"dGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK\n" -"AoICAQCAEmUXNg7D2wiz0KxXDXbtzSfTTK1Qg2HiqiBNCS1kCdzOiZ/MPans9s/B\n" -"3PHTsdZ7NygRK0faOca8Ohm0X6a9fZ2jY0K2dvKpOyuR+OJv0OwWIJAJPuLodMkY\n" -"tJHUYmTbf6MG8YgYapAiPLz+E/CHFHv25B+O1ORRxhFnRghRy4YUVD+8M/5+bJz/\n" -"Fp0YvVGONaanZshyZ9shZrHUm3gDwFA66Mzw3LyeTP6vBZY1H1dat//O+T23LLb2\n" -"VN3I5xI6Ta5MirdcmrS3ID3KfyI0rn47aGYBROcBTkZTmzNg95S+UzeQc0PzMsNT\n" -"79uq/nROacdrjGCT3sTHDN/hMq7MkztReJVni+49Vv4M0GkPGw/zJSZrM233bkf6\n" -"c0Plfg6lZrEpfDKEY1WJxA3Bk1QwGROs0303p+tdOmw1XNtB1xLaqUkL39iAigmT\n" -"Yo61Zs8liM2EuLE/pDkP2QKe6xJMlXzzawWpXhaDzLhn4ugTncxbgtNMs+1b/97l\n" -"c6wjOy0AvzVVdAlJ2ElYGn+SNuZRkg7zJn0cTRe8yexDJtC/QV9AqURE9JnnV4ee\n" -"UB9XVKg+/XRjL7FQZQnmWEIuQxpMtPAlR1n6BB6T1CZGSlCBst6+eLf8ZxXhyVeE\n" -"Hg9j1uliutZfVS7qXMYoCAQlObgOK6nyTJccBz8NUvXt7y+CDwIDAQABo0IwQDAd\n" -"BgNVHQ4EFgQUU3m/WqorSs9UgOHYm8Cd8rIDZsswDgYDVR0PAQH/BAQDAgEGMA8G\n" -"A1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAFzUfA3P9wF9QZllDHPF\n" -"Up/L+M+ZBn8b2kMVn54CVVeWFPFSPCeHlCjtHzoBN6J2/FNQwISbxmtOuowhT6KO\n" -"VWKR82kV2LyI48SqC/3vqOlLVSoGIG1VeCkZ7l8wXEskEVX/JJpuXior7gtNn3/3\n" -"ATiUFJVDBwn7YKnuHKsSjKCaXqeYalltiz8I+8jRRa8YFWSQEg9zKC7F4iRO/Fjs\n" -"8PRF/iKz6y+O0tlFYQXBl2+odnKPi4w2r78NBc5xjeambx9spnFixdjQg3IM8WcR\n" -"iQycE0xyNN+81XHfqnHd4blsjDwSXWXavVcStkNr/+XeTWYRUc+ZruwXtuhxkYze\n" -"Sf7dNXGiFSeUHM9h4ya7b6NnJSFd5t0dCy5oGzuCr+yDZ4XUmFF0sbmZgIn/f3gZ\n" -"XHlKYC6SQK5MNyosycdiyA5d9zZbyuAlJQG03RoHnHcAP9Dc1ew91Pq7P8yF1m9/\n" -"qS3fuQL39ZeatTXaw2ewh0qpKJ4jjv9cJ2vhsE/zB+4ALtRZh8tSQZXq9EfX7mRB\n" -"VXyNWQKV3WKdwrnuWih0hKWbt5DHDAff9Yk2dDLWKMGwsAvgnEzDHNb842m1R0aB\n" -"L6KCq9NjRHDEjf8tM7qtj3u1cIiuPhnPQCjY/MiQu12ZIvVS5ljFH4gxQ+6IHdfG\n" -"jjxDah2nGN59PRbxYvnKkKj9\n" +"MIICCTCCAY6gAwIBAgINAgPlwGjvYxqccpBQUjAKBggqhkjOPQQDAzBHMQswCQYD\n" +"VQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIG\n" +"A1UEAxMLR1RTIFJvb3QgUjQwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAwMDAw\n" +"WjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2Vz\n" +"IExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjQwdjAQBgcqhkjOPQIBBgUrgQQAIgNi\n" +"AATzdHOnaItgrkO4NcWBMHtLSZ37wWHO5t5GvWvVYRg1rkDdc/eJkTBa6zzuhXyi\n" +"QHY7qca4R9gq55KRanPpsXI5nymfopjTX15YhmUPoYRlBtHci8nHc8iMai/lxKvR\n" +"HYqjQjBAMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQW\n" +"BBSATNbrdP9JNqPV2Py1PsVq8JQdjDAKBggqhkjOPQQDAwNpADBmAjEA6ED/g94D\n" +"9J+uHXqnLrmvT/aDHQ4thQEd0dlq7A/Cr8deVl5c1RxYIigL9zC2L7F8AjEA8GE8\n" +"p/SgguMh1YQdc4acLa/KNJvxn7kjNuK8YAOdgLOaVsjh4rsUecrNIdSUtUlD\n" "-----END CERTIFICATE-----\n" }; BridgeTCPClient<> client(Bridge); void setup() { - /* Initialize Serial */ - Serial.begin(115200); - /* Initialize RPC transport Serial1 */ - //Serial1.begin(115200); + if (!Bridge.begin()) { - Serial.println("cannot setup Bridge"); while (true) {} } - /* Configure TLS CA */ - //client.setCACert(ubi); + + if (!Monitor.begin()) { + while (true) {} + } + } void loop() { - Serial.println("\nStarting connection to server..."); - /* if you get a connection, report back via serial: */ - if (client.connectSSL("ubidefeo.com", 443, ubi) < 0) { - Serial.println("unable to connect to server"); + Monitor.println("\nStarting connection to server..."); + + /* if you get a connection, report back via monitor: */ + if (client.connectSSL("arduino.tips", 443, ca_cert) < 0) { + Monitor.println("unable to connect to server"); return; } - Serial.println("connected to server"); + Monitor.println("connected to server"); /* Make aHTTP request: */ - size_t w = client.write("GET /files/supsi.txt HTTP/1.1\n"); - w += client.println("Host: ubidefeo.com"); + size_t w = client.println("GET /asciilogo.txt HTTP/1.1"); + w += client.println("Host: arduino.tips"); w += client.println("User-Agent: Arduino"); w += client.println("Connection: close"); w += client.println(); @@ -93,14 +55,14 @@ void loop() { if (len) { uint8_t buff[len]; client.read(buff, len); - Serial.write(buff, len); + Monitor.write(buff, len); } delay(0); } /* if the server's disconnected, stop the client: */ - Serial.println(); - Serial.println("disconnecting from server."); + Monitor.println(); + Monitor.println("disconnecting from server."); client.stop(); delay(1000); } diff --git a/examples/server/server.ino b/examples/server/server.ino index 92a5db9..b141034 100644 --- a/examples/server/server.ino +++ b/examples/server/server.ino @@ -4,10 +4,12 @@ IPAddress localhost(127, 0, 0, 1); BridgeTCPServer<> server(Bridge, localhost, 5678); void setup() { - Serial.begin(115200); if (!Bridge.begin()) { - Serial.println("cannot setup Bridge"); + while (true) {} + } + + if (!Monitor.begin()) { while (true) {} } @@ -16,27 +18,26 @@ void setup() { } void loop() { - Serial.println("loop"); BridgeTCPClient<> client = server.accept(); if (client.connected() == 1){ - Serial.print("client "); - Serial.print(client.getId()); - Serial.println(" connected"); + Monitor.print("client "); + Monitor.print(client.getId()); + Monitor.println(" connected"); } if (client) { - Serial.println("A client established a connection"); + Monitor.println("A client established a connection"); } while (client.connected()) { size_t len = client.available(); if (len) { - Serial.println("Message received from client"); + Monitor.println("Message received from client"); uint8_t buff[len]; client.read(buff, len); - Serial.write(buff, len); + Monitor.write(buff, len); } } From b74dad1d24e7a5e165865d54273afa8874c3f117 Mon Sep 17 00:00:00 2001 From: Lucio Rossi Date: Fri, 19 Sep 2025 16:42:45 +0200 Subject: [PATCH 09/26] mod: monitor.begin has baud param that can be passed on to the bridge --- src/monitor.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/monitor.h b/src/monitor.h index c0b7bec..c655aff 100644 --- a/src/monitor.h +++ b/src/monitor.h @@ -6,7 +6,7 @@ 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 @@ -37,12 +37,12 @@ class BridgeMonitor: public Stream { using Print::write; - bool begin() { + bool begin(unsigned long baudrate=DEFAULT_SERIAL_BAUD) { k_mutex_init(&monitor_mutex); bool bridge_started = (*bridge); if (!bridge_started) { - bridge_started = bridge->begin(); + bridge_started = bridge->begin(baudrate); } return bridge_started && bridge->call(MON_CONNECTED_METHOD, is_connected); } From 6642d9882b227977909be0262a77b1ad659bceb4 Mon Sep 17 00:00:00 2001 From: Lucio Rossi Date: Fri, 19 Sep 2025 17:06:47 +0200 Subject: [PATCH 10/26] mod: revert to fake --- src/monitor.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/monitor.h b/src/monitor.h index c655aff..99154d0 100644 --- a/src/monitor.h +++ b/src/monitor.h @@ -37,12 +37,12 @@ class BridgeMonitor: public Stream { using Print::write; - bool begin(unsigned long baudrate=DEFAULT_SERIAL_BAUD) { + bool begin(unsigned long _legacy_baud=0, uint16_t _legacy_config=0) { k_mutex_init(&monitor_mutex); bool bridge_started = (*bridge); if (!bridge_started) { - bridge_started = bridge->begin(baudrate); + bridge_started = bridge->begin(); } return bridge_started && bridge->call(MON_CONNECTED_METHOD, is_connected); } From 3226cc2a16ec5f39ca42bfe82affcc0f3e6cefb2 Mon Sep 17 00:00:00 2001 From: Lucio Rossi Date: Wed, 24 Sep 2025 09:52:00 +0200 Subject: [PATCH 11/26] fix: bridge.notify not acquiring mutex --- src/bridge.h | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/bridge.h b/src/bridge.h index dc2c2b6..95faaab 100644 --- a/src/bridge.h +++ b/src/bridge.h @@ -145,7 +145,7 @@ class BridgeClass { } else { k_yield(); } - } + } // Lock read mutex while(true) { @@ -168,7 +168,14 @@ class BridgeClass { template void notify(const MsgPack::str_t method, Args&&... args) { - client->notify(method, std::forward(args)...); + while (true) { + if (k_mutex_lock(&write_mutex, K_MSEC(10)) == 0) { + client->notify(method, std::forward(args)...); + k_mutex_unlock(&write_mutex); + break; + } + k_yield(); + } } String get_error_message() const { From c966151255e337952c03a9d9580fbc4519860233 Mon Sep 17 00:00:00 2001 From: Martino Facchin Date: Wed, 24 Sep 2025 09:40:03 +0200 Subject: [PATCH 12/26] Bridge.call: move result retrieval to separate API New calls will be in the form: Bridge.call("test", 1, 2) -> returns true if the call succeeded, ignore the return Bridge.call("test", 1, 2).result(c) -> stores the result in variable c, returns true if succeeded Bridge.call("test", 1, 2).timeout(20) -> waits 20ms for the result, then returns fals if timeout is reached Bridge.call("test", 1, 2).timeout(20).result(c) -> same but storing the result to variable c Bridge.call("test", 1, 2).result(c, 20) -> same as previous call * Usages that do not held the expected behaviour: Bridge.call("test", 1, 2).result(c).timeout(20) -> doesn't apply the timeout, waits forever --- src/bridge.h | 60 +++++++++++++++++++++++++++++++++++----------------- 1 file changed, 41 insertions(+), 19 deletions(-) diff --git a/src/bridge.h b/src/bridge.h index dc2c2b6..cead08c 100644 --- a/src/bridge.h +++ b/src/bridge.h @@ -28,6 +28,43 @@ void updateEntryPoint(void *, void *, void *); +class RpcResult { +public: + RpcResult(uint32_t id, RPCClient* c, struct k_mutex* m) : msg_id_wait(id), client(c), read_mutex(m) {} + + template bool result(RType& result, int timeout_ms = -1) { + // Lock read mutex + if (_timeout < 0) _timeout = timeout_ms; + int start = millis(); + while(true && (_timeout < 0 || (millis() - start) < _timeout)) { + if (k_mutex_lock(read_mutex, K_MSEC(10)) == 0 ) { + if (client->get_response(msg_id_wait, result)) { + k_mutex_unlock(read_mutex); + break; + } + k_mutex_unlock(read_mutex); + k_msleep(1); + } else { + k_yield(); + } + } + return (client->lastError.code == NO_ERR) && (_timeout < 0 || (millis() - start) < _timeout); + } + operator bool() { + char c; + return result(c); + } + RpcResult& timeout(int ms) { + _timeout = ms; + return *this; + } +private: + uint32_t msg_id_wait; + RPCClient* client; + struct k_mutex* read_mutex; + int _timeout = -1; +}; + class BridgeClass { RPCClient* client = nullptr; @@ -131,8 +168,8 @@ class BridgeClass { } - template - bool call(const MsgPack::str_t& method, RType& result, Args&&... args) { + template + RpcResult call(const MsgPack::str_t& method, Args&&... args) { uint32_t msg_id_wait; @@ -146,25 +183,10 @@ class BridgeClass { k_yield(); } } + return RpcResult{msg_id_wait, client, &read_mutex}; + } - // Lock read mutex - while(true) { - if (k_mutex_lock(&read_mutex, K_MSEC(10)) == 0 ) { - if (client->get_response(msg_id_wait, result)) { - k_mutex_unlock(&read_mutex); - break; - } - k_mutex_unlock(&read_mutex); - k_msleep(1); - } else { - k_yield(); - } - - } - - return (client->lastError.code == NO_ERR); - } template void notify(const MsgPack::str_t method, Args&&... args) { From 7932a8181543445277c56d3eae6ae85b30576718 Mon Sep 17 00:00:00 2001 From: Martino Facchin Date: Wed, 24 Sep 2025 10:56:36 +0200 Subject: [PATCH 13/26] Change all Bridge.call in library --- README.md | 2 +- examples/simple_bridge/simple_bridge.ino | 2 +- src/bridge.h | 6 +++--- src/monitor.h | 8 ++++---- src/tcp_client.h | 10 +++++----- src/tcp_server.h | 6 +++--- 6 files changed, 17 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 8eed4d7..e877494 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ void setup() { void loop() { float res; - if (!Bridge.call("multiply", res, 1.0, 2.0)) { + if (!Bridge.call("multiply", 1.0, 2.0).result(res)) { Serial.println("Error calling method: multiply"); Serial.println(Bridge.get_error_code()); Serial.println(Bridge.get_error_message()); diff --git a/examples/simple_bridge/simple_bridge.ino b/examples/simple_bridge/simple_bridge.ino index 6a1eccb..18dee49 100644 --- a/examples/simple_bridge/simple_bridge.ino +++ b/examples/simple_bridge/simple_bridge.ino @@ -47,7 +47,7 @@ void setup() { void loop() { float res; - if (!Bridge.call("multiply", res, 1.0, 2.0)) { + if (!Bridge.call("multiply", 1.0, 2.0).result(res)) { Serial.println("Error calling method: multiply"); Serial.println(Bridge.get_error_code()); Serial.println(Bridge.get_error_message()); diff --git a/src/bridge.h b/src/bridge.h index cead08c..97d84a4 100644 --- a/src/bridge.h +++ b/src/bridge.h @@ -110,7 +110,7 @@ class BridgeClass { UPDATE_THREAD_PRIORITY, 0, K_NO_WAIT); bool res; - call(RESET_METHOD, res); + call(RESET_METHOD).result(res); if (res) { started = true; } @@ -120,7 +120,7 @@ class BridgeClass { template bool provide(const MsgPack::str_t& name, F&& func) { bool res; - if (!call(BIND_METHOD, res, name)) { + if (!call(BIND_METHOD, name).result(res)) { return false; } return server->bind(name, func); @@ -129,7 +129,7 @@ class BridgeClass { template bool provide_safe(const MsgPack::str_t& name, F&& func) { bool res; - if (!call(BIND_METHOD, res, name)) { + if (!call(BIND_METHOD, name).result(res)) { return false; } diff --git a/src/monitor.h b/src/monitor.h index 99154d0..d091b04 100644 --- a/src/monitor.h +++ b/src/monitor.h @@ -44,7 +44,7 @@ class BridgeMonitor: public Stream { if (!bridge_started) { bridge_started = bridge->begin(); } - return bridge_started && bridge->call(MON_CONNECTED_METHOD, is_connected); + return bridge_started && bridge->call(MON_CONNECTED_METHOD).result(is_connected); } explicit operator bool() const { @@ -99,7 +99,7 @@ class BridgeMonitor: public Stream { } size_t written; - const bool ret = bridge->call(MON_WRITE_METHOD, written, send_buffer); + const bool ret = bridge->call(MON_WRITE_METHOD, send_buffer).result(written); if (ret) { return written; } @@ -109,7 +109,7 @@ class BridgeMonitor: public Stream { bool reset() { bool res; - bool ok = bridge->call(MON_RESET_METHOD, res); + bool ok = bridge->call(MON_RESET_METHOD).result(res); if (ok && res) { is_connected = false; } @@ -124,7 +124,7 @@ class BridgeMonitor: public Stream { 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, size).result(message); if (ret) { for (size_t i = 0; i < message.size(); ++i) { diff --git a/src/tcp_client.h b/src/tcp_client.h index 8874314..62da5e7 100644 --- a/src/tcp_client.h +++ b/src/tcp_client.h @@ -61,7 +61,7 @@ class BridgeTCPClient : public Client { k_mutex_lock(&client_mutex, K_FOREVER); - const bool resp = bridge->call(TCP_CONNECT_METHOD, connection_id, hostname, port); + const bool resp = bridge->call(TCP_CONNECT_METHOD, hostname, port).result(connection_id); if (!resp) { _connected = false; @@ -84,7 +84,7 @@ class BridgeTCPClient : public Client { k_mutex_lock(&client_mutex, K_FOREVER); - const bool resp = bridge->call(TCP_CONNECT_SSL_METHOD, connection_id, hostname, port, ca_cert_str); + const bool resp = bridge->call(TCP_CONNECT_SSL_METHOD, hostname, port, ca_cert_str).result(connection_id); if (!resp) { _connected = false; @@ -116,7 +116,7 @@ class BridgeTCPClient : public Client { } size_t written; - const bool ret = bridge->call(TCP_WRITE_METHOD, written, connection_id, payload); + const bool ret = bridge->call(TCP_WRITE_METHOD, connection_id, payload).result(written); if (ret) { return written; } @@ -170,7 +170,7 @@ class BridgeTCPClient : public Client { void stop() override { k_mutex_lock(&client_mutex, K_FOREVER); String msg; - const bool resp = bridge->call(TCP_CLOSE_METHOD, msg, connection_id); + const bool resp = bridge->call(TCP_CLOSE_METHOD, connection_id).result(msg); if (resp) { _connected = false; } @@ -198,7 +198,7 @@ class BridgeTCPClient : public Client { k_mutex_lock(&client_mutex, K_FOREVER); MsgPack::arr_t message; - const bool ret = bridge->call(TCP_READ_METHOD, message, connection_id, size); + const bool ret = bridge->call(TCP_READ_METHOD, connection_id, size).result(message); if (ret) { for (size_t i = 0; i < message.size(); ++i) { diff --git a/src/tcp_server.h b/src/tcp_server.h index ad4c0a5..f62e57d 100644 --- a/src/tcp_server.h +++ b/src/tcp_server.h @@ -52,7 +52,7 @@ class BridgeTCPServer final: public Server { k_mutex_lock(&server_mutex, K_FOREVER); String hostname = _addr.toString(); - _listening = bridge->call(TCP_LISTEN_METHOD, listener_id, hostname, _port); + _listening = bridge->call(TCP_LISTEN_METHOD, hostname, _port).result(listener_id); k_mutex_unlock(&server_mutex); @@ -70,7 +70,7 @@ class BridgeTCPServer final: public Server { k_mutex_lock(&server_mutex, K_FOREVER); - const bool ret = bridge->call(TCP_ACCEPT_METHOD, connection_id, listener_id); + const bool ret = bridge->call(TCP_ACCEPT_METHOD, listener_id).result(connection_id); k_mutex_unlock(&server_mutex); @@ -104,7 +104,7 @@ class BridgeTCPServer final: public Server { k_mutex_lock(&server_mutex, K_FOREVER); String msg; - const bool ret = bridge->call(TCP_CLOSE_LISTENER_METHOD, msg, listener_id); + const bool ret = bridge->call(TCP_CLOSE_LISTENER_METHOD, listener_id).result(msg); if (ret) { _listening = false; From 31ddb455d642e3d47c4247e0480851dde3438283 Mon Sep 17 00:00:00 2001 From: Lucio Rossi Date: Wed, 24 Sep 2025 15:05:37 +0200 Subject: [PATCH 14/26] feat: on timeout bridge.client has appropriate error code fix: RpcResult overloaded bool using a generic return type, not setting a timeout, can potentially execute result method multiple times --- src/bridge.h | 40 +++++++++++++++++++++++++++------------- 1 file changed, 27 insertions(+), 13 deletions(-) diff --git a/src/bridge.h b/src/bridge.h index 97d84a4..84af31f 100644 --- a/src/bridge.h +++ b/src/bridge.h @@ -29,14 +29,21 @@ void updateEntryPoint(void *, void *, void *); class RpcResult { + + const int FALLBACK_TIMEOUT = 10; + public: - RpcResult(uint32_t id, RPCClient* c, struct k_mutex* m) : msg_id_wait(id), client(c), read_mutex(m) {} + RpcResult(uint32_t id, RPCClient* c, struct k_mutex* m, int timeout) : msg_id_wait(id), client(c), read_mutex(m), _timeout(timeout) {} + + template bool result(RType& result) { - template bool result(RType& result, int timeout_ms = -1) { - // Lock read mutex - if (_timeout < 0) _timeout = timeout_ms; int start = millis(); - while(true && (_timeout < 0 || (millis() - start) < _timeout)) { + while(true) { + if (_timeout > 0 && (millis() - start) > _timeout){ + client->lastError.code = GENERIC_ERR; + client->lastError.message = "Timed out"; + break; + } if (k_mutex_lock(read_mutex, K_MSEC(10)) == 0 ) { if (client->get_response(msg_id_wait, result)) { k_mutex_unlock(read_mutex); @@ -48,19 +55,20 @@ class RpcResult { k_yield(); } } - return (client->lastError.code == NO_ERR) && (_timeout < 0 || (millis() - start) < _timeout); + _executed = true; + return client->lastError.code == NO_ERR; } + operator bool() { - char c; - return result(c); - } - RpcResult& timeout(int ms) { - _timeout = ms; - return *this; + if (_executed) return client->lastError.code == NO_ERR; + MsgPack::object::nil_t nil; + return result(nil, FALLBACK_TIMEOUT); } + private: uint32_t msg_id_wait; RPCClient* client; + bool _executed = false; struct k_mutex* read_mutex; int _timeout = -1; }; @@ -81,6 +89,8 @@ class BridgeClass { bool started = false; + int _timeout = -1; + public: explicit BridgeClass(HardwareSerial& serial) { @@ -91,6 +101,10 @@ class BridgeClass { return started; } + void setTimeout(int t) { + _timeout = t; + } + // Initialize the bridge bool begin(unsigned long baud=DEFAULT_SERIAL_BAUD) { serial_ptr->begin(baud); @@ -183,7 +197,7 @@ class BridgeClass { k_yield(); } } - return RpcResult{msg_id_wait, client, &read_mutex}; + return RpcResult{msg_id_wait, client, &read_mutex, _timeout}; } From bcfb56ef99edb596d6b1c07506238243ca83cdc1 Mon Sep 17 00:00:00 2001 From: Lucio Rossi Date: Wed, 24 Sep 2025 15:18:34 +0200 Subject: [PATCH 15/26] examples: simple_bridge.ino showcases Bridge callback with async-like result retrieval --- examples/simple_bridge/simple_bridge.ino | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/examples/simple_bridge/simple_bridge.ino b/examples/simple_bridge/simple_bridge.ino index 18dee49..face5cc 100644 --- a/examples/simple_bridge/simple_bridge.ino +++ b/examples/simple_bridge/simple_bridge.ino @@ -53,5 +53,17 @@ void loop() { Serial.println(Bridge.get_error_message()); }; + // Call with deferred response check + RpcResult outcome = Bridge.call("multiply", 5.0, 7.0); + Serial.println("RPC called"); + delay(10); + if (outcome.result(res)) { + Serial.print("Result of the operation is: "); + Serial.println(res); + } else { + Serial.println(Bridge.get_error_code()); + Serial.println(Bridge.get_error_message()); + } + Bridge.notify("signal", 200); } From eab384e4fbd5871aceebdf7623e0a944cc015a90 Mon Sep 17 00:00:00 2001 From: Lucio Rossi Date: Wed, 24 Sep 2025 15:47:05 +0200 Subject: [PATCH 16/26] fix: lastError has traceback field. RpcResult.result must accept a timeout param --- src/bridge.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/bridge.h b/src/bridge.h index b827074..091a486 100644 --- a/src/bridge.h +++ b/src/bridge.h @@ -35,13 +35,14 @@ class RpcResult { public: RpcResult(uint32_t id, RPCClient* c, struct k_mutex* m, int timeout) : msg_id_wait(id), client(c), read_mutex(m), _timeout(timeout) {} - template bool result(RType& result) { + template bool result(RType& result, int timeout = -1) { + if (timeout > 0) _timeout = timeout; int start = millis(); while(true) { if (_timeout > 0 && (millis() - start) > _timeout){ client->lastError.code = GENERIC_ERR; - client->lastError.message = "Timed out"; + client->lastError.traceback = "Timed out"; break; } if (k_mutex_lock(read_mutex, K_MSEC(10)) == 0 ) { From d4cae3a7a32a4eeb5411ab6488f4af0f187c51d1 Mon Sep 17 00:00:00 2001 From: Lucio Rossi Date: Wed, 24 Sep 2025 15:56:49 +0200 Subject: [PATCH 17/26] mod: RpcResult _executed guard moved inside result method. result method must be executed exactly once --- src/bridge.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/bridge.h b/src/bridge.h index 091a486..4c66637 100644 --- a/src/bridge.h +++ b/src/bridge.h @@ -36,6 +36,8 @@ class RpcResult { RpcResult(uint32_t id, RPCClient* c, struct k_mutex* m, int timeout) : msg_id_wait(id), client(c), read_mutex(m), _timeout(timeout) {} template bool result(RType& result, int timeout = -1) { + if (_executed) return client->lastError.code == NO_ERR; + if (timeout > 0) _timeout = timeout; int start = millis(); @@ -61,7 +63,6 @@ class RpcResult { } operator bool() { - if (_executed) return client->lastError.code == NO_ERR; MsgPack::object::nil_t nil; return result(nil, FALLBACK_TIMEOUT); } From 018b569929d59a466b96f1de43697b392e37b0ec Mon Sep 17 00:00:00 2001 From: Lucio Rossi Date: Thu, 25 Sep 2025 09:44:50 +0200 Subject: [PATCH 18/26] mod: rem RpcResult.result timeout param. Setting timeout is only allowed at Bridge level feat: Bridge notifies Router with a timeout event feat: RpcResult.error field --- src/bridge.h | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/src/bridge.h b/src/bridge.h index 4c66637..fe9b35f 100644 --- a/src/bridge.h +++ b/src/bridge.h @@ -16,6 +16,7 @@ #define RESET_METHOD "$/reset" #define BIND_METHOD "$/register" +#define BRIDGE_TIMEOUT "$/bridgeTimeout" #define UPDATE_THREAD_STACK_SIZE 500 #define UPDATE_THREAD_PRIORITY 5 @@ -30,25 +31,28 @@ void updateEntryPoint(void *, void *, void *); class RpcResult { - const int FALLBACK_TIMEOUT = 10; - public: - RpcResult(uint32_t id, RPCClient* c, struct k_mutex* m, int timeout) : msg_id_wait(id), client(c), read_mutex(m), _timeout(timeout) {} + RpcError error; - template bool result(RType& result, int timeout = -1) { - if (_executed) return client->lastError.code == NO_ERR; + RpcResult(uint32_t id, RPCClient* c, struct k_mutex* rm, struct k_mutex* wm, unsigned long timeout) : msg_id_wait(id), client(c), read_mutex(rm), write_mutex(wm), _timeout(timeout) {} - if (timeout > 0) _timeout = timeout; + template bool result(RType& result) { + if (_executed) return error.code == NO_ERR; - int start = millis(); + unsigned long start = millis(); while(true) { if (_timeout > 0 && (millis() - start) > _timeout){ - client->lastError.code = GENERIC_ERR; - client->lastError.traceback = "Timed out"; + error.code = GENERIC_ERR; + error.traceback = "Timed out"; + k_mutex_lock(write_mutex, K_FOREVER); + client->notify(BRIDGE_TIMEOUT); + k_mutex_unlock(write_mutex); break; } if (k_mutex_lock(read_mutex, K_MSEC(10)) == 0 ) { if (client->get_response(msg_id_wait, result)) { + error.code = client->lastError.code; + error.traceback = client->lastError.traceback; k_mutex_unlock(read_mutex); break; } @@ -59,12 +63,12 @@ class RpcResult { } } _executed = true; - return client->lastError.code == NO_ERR; + return error.code == NO_ERR; } operator bool() { MsgPack::object::nil_t nil; - return result(nil, FALLBACK_TIMEOUT); + return result(nil); } private: @@ -72,7 +76,8 @@ class RpcResult { RPCClient* client; bool _executed = false; struct k_mutex* read_mutex; - int _timeout = -1; + struct k_mutex* write_mutex; + unsigned long _timeout = -1; }; class BridgeClass { @@ -91,7 +96,7 @@ class BridgeClass { bool started = false; - int _timeout = -1; + unsigned long _timeout = -1; public: @@ -199,7 +204,7 @@ class BridgeClass { k_yield(); } } - return RpcResult{msg_id_wait, client, &read_mutex, _timeout}; + return RpcResult{msg_id_wait, client, &read_mutex, &write_mutex, _timeout}; } From cf6b26c0e853f20e225b0fd1ba24926e419b5f14 Mon Sep 17 00:00:00 2001 From: Lucio Rossi Date: Thu, 25 Sep 2025 18:32:30 +0200 Subject: [PATCH 19/26] mod: rem timeout logic. Catch PARSING_ERR and send $/bridgeError notification --- src/bridge.h | 28 +++++++++------------------- 1 file changed, 9 insertions(+), 19 deletions(-) diff --git a/src/bridge.h b/src/bridge.h index fe9b35f..252bda6 100644 --- a/src/bridge.h +++ b/src/bridge.h @@ -16,7 +16,7 @@ #define RESET_METHOD "$/reset" #define BIND_METHOD "$/register" -#define BRIDGE_TIMEOUT "$/bridgeTimeout" +#define BRIDGE_ERROR "$/bridgeError" #define UPDATE_THREAD_STACK_SIZE 500 #define UPDATE_THREAD_PRIORITY 5 @@ -34,27 +34,24 @@ class RpcResult { public: RpcError error; - RpcResult(uint32_t id, RPCClient* c, struct k_mutex* rm, struct k_mutex* wm, unsigned long timeout) : msg_id_wait(id), client(c), read_mutex(rm), write_mutex(wm), _timeout(timeout) {} + RpcResult(uint32_t id, RPCClient* c, struct k_mutex* rm, struct k_mutex* wm) : msg_id_wait(id), client(c), read_mutex(rm), write_mutex(wm) {} template bool result(RType& result) { if (_executed) return error.code == NO_ERR; - unsigned long start = millis(); while(true) { - if (_timeout > 0 && (millis() - start) > _timeout){ - error.code = GENERIC_ERR; - error.traceback = "Timed out"; - k_mutex_lock(write_mutex, K_FOREVER); - client->notify(BRIDGE_TIMEOUT); - k_mutex_unlock(write_mutex); - break; - } if (k_mutex_lock(read_mutex, K_MSEC(10)) == 0 ) { if (client->get_response(msg_id_wait, result)) { error.code = client->lastError.code; error.traceback = client->lastError.traceback; k_mutex_unlock(read_mutex); break; + } else if (client->lastError.code == PARSING_ERR) { + error.code = client->lastError.code; + error.traceback = client->lastError.traceback; + k_mutex_lock(write_mutex, K_FOREVER); + client->notify(BRIDGE_ERROR); + k_mutex_unlock(write_mutex); } k_mutex_unlock(read_mutex); k_msleep(1); @@ -77,7 +74,6 @@ class RpcResult { bool _executed = false; struct k_mutex* read_mutex; struct k_mutex* write_mutex; - unsigned long _timeout = -1; }; class BridgeClass { @@ -96,8 +92,6 @@ class BridgeClass { bool started = false; - unsigned long _timeout = -1; - public: explicit BridgeClass(HardwareSerial& serial) { @@ -108,10 +102,6 @@ class BridgeClass { return started; } - void setTimeout(int t) { - _timeout = t; - } - // Initialize the bridge bool begin(unsigned long baud=DEFAULT_SERIAL_BAUD) { serial_ptr->begin(baud); @@ -204,7 +194,7 @@ class BridgeClass { k_yield(); } } - return RpcResult{msg_id_wait, client, &read_mutex, &write_mutex, _timeout}; + return RpcResult{msg_id_wait, client, &read_mutex, &write_mutex}; } From df1514cbb8132bad1d15d9e2f876dd19853b9882 Mon Sep 17 00:00:00 2001 From: Lucio Rossi Date: Thu, 25 Sep 2025 18:41:21 +0200 Subject: [PATCH 20/26] feat: on BRIDGE_ERROR the parsing error message is sent with notification --- src/bridge.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bridge.h b/src/bridge.h index 252bda6..049d217 100644 --- a/src/bridge.h +++ b/src/bridge.h @@ -50,7 +50,7 @@ class RpcResult { error.code = client->lastError.code; error.traceback = client->lastError.traceback; k_mutex_lock(write_mutex, K_FOREVER); - client->notify(BRIDGE_ERROR); + client->notify(BRIDGE_ERROR, error.traceback); k_mutex_unlock(write_mutex); } k_mutex_unlock(read_mutex); From 7cfc61f99ff839a24649aa026ab5bef60bcb1dab Mon Sep 17 00:00:00 2001 From: Lucio Rossi Date: Thu, 25 Sep 2025 22:13:41 +0200 Subject: [PATCH 21/26] fix: RpcResult.result must check per-call error not client->lastError --- src/bridge.h | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/bridge.h b/src/bridge.h index 049d217..9f2a47e 100644 --- a/src/bridge.h +++ b/src/bridge.h @@ -41,17 +41,15 @@ class RpcResult { while(true) { if (k_mutex_lock(read_mutex, K_MSEC(10)) == 0 ) { - if (client->get_response(msg_id_wait, result)) { - error.code = client->lastError.code; - error.traceback = client->lastError.traceback; + if (client->get_response(msg_id_wait, result, error)) { k_mutex_unlock(read_mutex); break; - } else if (client->lastError.code == PARSING_ERR) { - error.code = client->lastError.code; - error.traceback = client->lastError.traceback; + } else if (error.code != NO_ERR) { + k_mutex_unlock(read_mutex); k_mutex_lock(write_mutex, K_FOREVER); client->notify(BRIDGE_ERROR, error.traceback); k_mutex_unlock(write_mutex); + break; } k_mutex_unlock(read_mutex); k_msleep(1); From db9a2883ee8b7048f614b99905100b2e216d6855 Mon Sep 17 00:00:00 2001 From: Lucio Rossi Date: Thu, 25 Sep 2025 22:39:22 +0200 Subject: [PATCH 22/26] minor: Monitor._read optimization --- src/monitor.h | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/monitor.h b/src/monitor.h index d091b04..bac7465 100644 --- a/src/monitor.h +++ b/src/monitor.h @@ -121,25 +121,22 @@ class BridgeMonitor: public Stream { if (size == 0) return; - k_mutex_lock(&monitor_mutex, K_FOREVER); - MsgPack::arr_t message; bool ret = bridge->call(MON_READ_METHOD, size).result(message); if (ret) { + k_mutex_lock(&monitor_mutex, K_FOREVER); for (size_t i = 0; i < message.size(); ++i) { temp_buffer.store_char(static_cast(message[i])); } + k_mutex_unlock(&monitor_mutex); } // if (bridge.lastError.code > NO_ERR) { // is_connected = false; // } - - k_mutex_unlock(&monitor_mutex); } - }; extern BridgeClass Bridge; From 744fb967e2dac96bbc4091a5a3752581520bad9e Mon Sep 17 00:00:00 2001 From: Lucio Rossi Date: Fri, 26 Sep 2025 09:14:33 +0200 Subject: [PATCH 23/26] fix: RpcResult should break out of its while loop only if the packet was consumed but notify PARSING_ERR --- src/bridge.h | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/bridge.h b/src/bridge.h index 9f2a47e..68254f5 100644 --- a/src/bridge.h +++ b/src/bridge.h @@ -43,12 +43,11 @@ class RpcResult { if (k_mutex_lock(read_mutex, K_MSEC(10)) == 0 ) { if (client->get_response(msg_id_wait, result, error)) { k_mutex_unlock(read_mutex); - break; - } else if (error.code != NO_ERR) { - k_mutex_unlock(read_mutex); - k_mutex_lock(write_mutex, K_FOREVER); - client->notify(BRIDGE_ERROR, error.traceback); - k_mutex_unlock(write_mutex); + if (error.code == PARSING_ERR) { + k_mutex_lock(write_mutex, K_FOREVER); + client->notify(BRIDGE_ERROR, error.traceback); + k_mutex_unlock(write_mutex); + } break; } k_mutex_unlock(read_mutex); From 381fed79acd86fa1e4b14411126e50b097c5d8d4 Mon Sep 17 00:00:00 2001 From: Lucio Rossi Date: Fri, 26 Sep 2025 14:12:23 +0200 Subject: [PATCH 24/26] feat: overloaded RpcResult.result() waits a msgpack nil result feat: RpcResult destructor invokes .result() --- src/bridge.h | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/bridge.h b/src/bridge.h index 68254f5..03b2c59 100644 --- a/src/bridge.h +++ b/src/bridge.h @@ -60,11 +60,19 @@ class RpcResult { return error.code == NO_ERR; } - operator bool() { + bool result() { MsgPack::object::nil_t nil; return result(nil); } + ~RpcResult(){ + result(); + } + + operator bool() { + return result(); + } + private: uint32_t msg_id_wait; RPCClient* client; From 5dc55dda840775cf6889359be4a4c56022e8f86a Mon Sep 17 00:00:00 2001 From: Lucio Rossi Date: Fri, 26 Sep 2025 17:07:20 +0200 Subject: [PATCH 25/26] mod: commented out the BRIDGE_ERROR notification --- src/bridge.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/bridge.h b/src/bridge.h index 03b2c59..7a2858c 100644 --- a/src/bridge.h +++ b/src/bridge.h @@ -16,7 +16,7 @@ #define RESET_METHOD "$/reset" #define BIND_METHOD "$/register" -#define BRIDGE_ERROR "$/bridgeError" +//#define BRIDGE_ERROR "$/bridgeLog" #define UPDATE_THREAD_STACK_SIZE 500 #define UPDATE_THREAD_PRIORITY 5 @@ -43,11 +43,11 @@ class RpcResult { if (k_mutex_lock(read_mutex, K_MSEC(10)) == 0 ) { if (client->get_response(msg_id_wait, result, error)) { k_mutex_unlock(read_mutex); - if (error.code == PARSING_ERR) { - k_mutex_lock(write_mutex, K_FOREVER); - client->notify(BRIDGE_ERROR, error.traceback); - k_mutex_unlock(write_mutex); - } + // if (error.code == PARSING_ERR) { + // k_mutex_lock(write_mutex, K_FOREVER); + // client->notify(BRIDGE_ERROR, error.traceback); + // k_mutex_unlock(write_mutex); + // } break; } k_mutex_unlock(read_mutex); From a06e182265b474e62511fcaa3cc64b554bb88b89 Mon Sep 17 00:00:00 2001 From: Lucio Rossi Date: Fri, 26 Sep 2025 17:48:05 +0200 Subject: [PATCH 26/26] version: 0.2.0 --- README.md | 29 ++++++++++++++++++++++------- library.json | 2 +- library.properties | 2 +- 3 files changed, 24 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index e877494..4ea7ac6 100644 --- a/README.md +++ b/README.md @@ -24,15 +24,16 @@ String greet() { } void setup() { - Serial.begin(115200); - while (!Serial); + + Bridge.begin(); + Monitor.begin(); pinMode(LED_BUILTIN, OUTPUT); if (!Bridge.provide("set_led", set_led)) { - Serial.println("Error providing method: set_led"); + Monitor.println("Error providing method: set_led"); } else { - Serial.println("Registered method: set_led"); + Monitor.println("Registered method: set_led"); } Bridge.provide_safe("greet", greet); @@ -42,11 +43,25 @@ void setup() { void loop() { float res; if (!Bridge.call("multiply", 1.0, 2.0).result(res)) { - Serial.println("Error calling method: multiply"); - Serial.println(Bridge.get_error_code()); - Serial.println(Bridge.get_error_message()); + Monitor.println("Error calling method: multiply"); + Monitor.println(Bridge.get_error_code()); + Monitor.println(Bridge.get_error_message()); }; Bridge.notify("signal", 200); } ``` + +## Best practices ## +Avoid catching Bridge call RpcResult without invoking its .result right away +```cpp +// OK +float out; +RpcResult res = Bridge.call("multiply", 1.0, 2.0); +res.result(out); +Monitor.println("TEST"); + +// NOT OK +//RpcResult res = Bridge.call("multiply", 1.0, 2.0); +//Monitor.println("TEST"); +``` diff --git a/library.json b/library.json index 3b035a6..a74b9b2 100644 --- a/library.json +++ b/library.json @@ -11,7 +11,7 @@ "url": "https://github.com/bcmi-labs/Arduino_RouterBridge", "maintainer": true }, - "version": "0.1.4", + "version": "0.2.0", "license": "MPL2.0", "frameworks": "arduino", "platforms": "*", diff --git a/library.properties b/library.properties index d4c1ae7..d76d8a9 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=Arduino_RouterBridge -version=0.1.4 +version=0.2.0 author=BCMI-labs maintainer=BCMI-labs sentence=A RPC bridge for Arduino UNO Q boards