From b0800a9e9ead59b55146e98d9fae31356e95fb0b Mon Sep 17 00:00:00 2001 From: Lucio Rossi Date: Wed, 6 Aug 2025 18:05:18 +0200 Subject: [PATCH 1/7] 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 2/7] 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 3/7] 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 4/7] 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 5/7] 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 58f86d6a6cdf715c7837d68162fb34ad2da11e3a Mon Sep 17 00:00:00 2001 From: Lucio Rossi Date: Wed, 17 Sep 2025 15:30:35 +0200 Subject: [PATCH 6/7] 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 7/7] 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); } }