Skip to content

Commit 8a261ad

Browse files
authored
Allow definition of custom network interfaces (#921)
* Allow definition of custom network interfaces * Create network client interface class * Change to PIMPL approach * Add example for custom network clients Why: - Show users how to use multiple network interfaces This change addresses the need by: - Adding an example PIO project to use Wi-Fi and GSM/LTE * Add WebSockets prefix to normal and secure client Why: - Avoid name collision - Fix broken reconnect change This change addresses the need by: - Adding WebSockets prefix to all custom clients - Marking custom client as secure in clientDisconnect() - Remove broken fix for reconnecting
1 parent 1789a18 commit 8a261ad

14 files changed

+619
-2
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
BasedOnStyle: Google
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
.pio
2+
.vscode

examples/esp32_pio/CustomNetworkClient/lib/README

Whitespace-only changes.
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
; PlatformIO Project Configuration File
2+
;
3+
; Build options: build flags, source filter
4+
; Upload options: custom upload port, speed and extra flags
5+
; Library options: dependencies, extra library storages
6+
; Advanced options: extra scripting
7+
;
8+
; Please visit documentation for the other options and examples
9+
; https://docs.platformio.org/page/projectconf.html
10+
11+
[env:esp32dev]
12+
13+
board = esp32dev
14+
framework = arduino
15+
monitor_speed = 115200
16+
upload_speed = 921600
17+
build_flags =
18+
-DCORE_DEBUG_LEVEL=5
19+
-D WEBSOCKETS_NETWORK_TYPE=10
20+
-D TINY_GSM_MODEM_SIM7600
21+
lib_deps =
22+
digitaldragon/SSLClient@^1.3.2
23+
vshymanskyy/StreamDebugger@^1.0.1
24+
vshymanskyy/TinyGSM@^0.12.0
25+
symlink://../../..
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
This example project shows how to use a custom NetworkClient class to switch between two underlying network interfaces.
2+
3+
In this case it shows how a board can support Wi-Fi as well as GSM/LTE connectivity. It uses a shim to switch between the two network interfaces, thereby allowing a single binary to handle both interfaces without needing to reboot. This example can be extended to cover other network interfaces that conform to the Client() class interface.
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
/*
2+
* main.cpp
3+
*
4+
* Created on: 15.06.2024
5+
*
6+
*/
7+
8+
#include <Arduino.h>
9+
#include <WebSocketsClient.h>
10+
#include <WiFiMulti.h>
11+
12+
#include "network_client_impl.h"
13+
14+
WiFiMulti wifiMulti;
15+
WebSocketsClient webSocket;
16+
17+
#define USE_SERIAL Serial
18+
19+
void setClock() {
20+
configTime(0, 0, "pool.ntp.org", "time.nist.gov");
21+
22+
USE_SERIAL.print(F("Waiting for NTP time sync: "));
23+
time_t nowSecs = time(nullptr);
24+
while (nowSecs < 8 * 3600 * 2) {
25+
delay(500);
26+
USE_SERIAL.print(F("."));
27+
yield();
28+
nowSecs = time(nullptr);
29+
}
30+
31+
USE_SERIAL.println();
32+
struct tm timeinfo;
33+
gmtime_r(&nowSecs, &timeinfo);
34+
USE_SERIAL.print(F("Current time: "));
35+
USE_SERIAL.print(asctime(&timeinfo));
36+
}
37+
38+
void hexdump(const void *mem, uint32_t len, uint8_t cols = 16) {
39+
const uint8_t *src = (const uint8_t *)mem;
40+
USE_SERIAL.printf("\n[HEXDUMP] Address: 0x%08X len: 0x%X (%d)",
41+
(ptrdiff_t)src, len, len);
42+
for (uint32_t i = 0; i < len; i++) {
43+
if (i % cols == 0) {
44+
USE_SERIAL.printf("\n[0x%08X] 0x%08X: ", (ptrdiff_t)src, i);
45+
}
46+
USE_SERIAL.printf("%02X ", *src);
47+
src++;
48+
}
49+
USE_SERIAL.printf("\n");
50+
}
51+
52+
void webSocketEvent(WStype_t type, uint8_t *payload, size_t length) {
53+
switch (type) {
54+
case WStype_DISCONNECTED:
55+
USE_SERIAL.printf("[WSc] Disconnected!\n");
56+
break;
57+
case WStype_CONNECTED:
58+
USE_SERIAL.printf("[WSc] Connected to url: %s\n", payload);
59+
60+
// send message to server when Connected
61+
webSocket.sendTXT("Connected");
62+
break;
63+
case WStype_TEXT:
64+
USE_SERIAL.printf("[WSc] get text: %s\n", payload);
65+
66+
// send message to server
67+
// webSocket.sendTXT("message here");
68+
break;
69+
case WStype_BIN:
70+
USE_SERIAL.printf("[WSc] get binary length: %u\n", length);
71+
hexdump(payload, length);
72+
73+
// send data to server
74+
// webSocket.sendBIN(payload, length);
75+
break;
76+
case WStype_ERROR:
77+
case WStype_FRAGMENT_TEXT_START:
78+
case WStype_FRAGMENT_BIN_START:
79+
case WStype_FRAGMENT:
80+
case WStype_FRAGMENT_FIN:
81+
break;
82+
}
83+
}
84+
85+
void setup() {
86+
USE_SERIAL.begin(115200);
87+
88+
USE_SERIAL.setDebugOutput(true);
89+
90+
USE_SERIAL.println();
91+
USE_SERIAL.println();
92+
USE_SERIAL.println();
93+
94+
for (uint8_t t = 4; t > 0; t--) {
95+
USE_SERIAL.printf("[SETUP] BOOT WAIT %d...\n", t);
96+
USE_SERIAL.flush();
97+
delay(1000);
98+
}
99+
100+
wifiMulti.addAP("Berge", "BlauerHimmel!");
101+
102+
// WiFi.disconnect();
103+
while (wifiMulti.run() != WL_CONNECTED) {
104+
delay(100);
105+
}
106+
107+
// Call to enable WiFi interface to be used by the webSocketClient
108+
WebSocketsNetworkClient::Impl::enableWifi();
109+
110+
setClock();
111+
112+
// server address, port and URL. This server can be flakey.
113+
// Expected response: Request served by 0123456789abcdef
114+
webSocket.beginSSL("echo.websocket.org", 443, "/");
115+
116+
// event handler
117+
webSocket.onEvent(webSocketEvent);
118+
119+
// use HTTP Basic Authorization this is optional enable if needed
120+
// webSocket.setAuthorization("user", "Password");
121+
122+
// try ever 5000 again if connection has failed
123+
webSocket.setReconnectInterval(5000);
124+
}
125+
126+
void loop() { webSocket.loop(); }
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
#include "network_client_impl.h"
2+
3+
WebSocketsNetworkClient::WebSocketsNetworkClient()
4+
: _impl(new WebSocketsNetworkClient::Impl()) {}
5+
WebSocketsNetworkClient::WebSocketsNetworkClient(WiFiClient wifi_client)
6+
: _impl(new WebSocketsNetworkClient::Impl()) {}
7+
WebSocketsNetworkClient::~WebSocketsNetworkClient() {}
8+
9+
int WebSocketsNetworkClient::connect(IPAddress ip, uint16_t port) {
10+
if (_impl->gsm_client_) {
11+
return _impl->gsm_client_->connect(ip, port);
12+
} else if (_impl->wifi_client_) {
13+
return _impl->wifi_client_->connect(ip, port);
14+
}
15+
Serial.println(_impl->no_interface_error_);
16+
return 0;
17+
}
18+
19+
int WebSocketsNetworkClient::connect(const char *host, uint16_t port) {
20+
if (_impl->gsm_client_) {
21+
return _impl->gsm_client_->connect(host, port);
22+
} else if (_impl->wifi_client_) {
23+
return _impl->wifi_client_->connect(host, port);
24+
}
25+
Serial.println(_impl->no_interface_error_);
26+
return 0;
27+
}
28+
int WebSocketsNetworkClient::connect(const char *host, uint16_t port,
29+
int32_t timeout_ms) {
30+
if (_impl->gsm_client_) {
31+
return _impl->gsm_client_->connect(host, port, timeout_ms);
32+
} else if (_impl->wifi_client_) {
33+
return _impl->wifi_client_->connect(host, port, timeout_ms);
34+
}
35+
Serial.println(_impl->no_interface_error_);
36+
return 0;
37+
}
38+
39+
size_t WebSocketsNetworkClient::write(uint8_t data) {
40+
if (_impl->gsm_client_) {
41+
return _impl->gsm_client_->write(data);
42+
} else if (_impl->wifi_client_) {
43+
return _impl->wifi_client_->write(data);
44+
}
45+
Serial.println(_impl->no_interface_error_);
46+
return 0;
47+
}
48+
49+
size_t WebSocketsNetworkClient::write(const uint8_t *buf, size_t size) {
50+
Serial.printf("Send_: %zu\n", size);
51+
if (_impl->gsm_client_) {
52+
return _impl->gsm_client_->write(buf, size);
53+
} else if (_impl->wifi_client_) {
54+
return _impl->wifi_client_->write(buf, size);
55+
}
56+
Serial.println(_impl->no_interface_error_);
57+
return 0;
58+
}
59+
60+
size_t WebSocketsNetworkClient::write(const char *str) {
61+
const int size = strlen(str);
62+
Serial.printf("Send: %zu\n", size);
63+
if (_impl->gsm_client_) {
64+
return _impl->gsm_client_->write((const uint8_t *)str, size);
65+
} else if (_impl->wifi_client_) {
66+
return _impl->wifi_client_->write((const uint8_t *)str, size);
67+
}
68+
Serial.println(_impl->no_interface_error_);
69+
return 0;
70+
}
71+
72+
int WebSocketsNetworkClient::available() {
73+
if (_impl->gsm_client_) {
74+
return _impl->gsm_client_->available();
75+
} else if (_impl->wifi_client_) {
76+
return _impl->wifi_client_->available();
77+
}
78+
Serial.println(_impl->no_interface_error_);
79+
return 0;
80+
}
81+
82+
int WebSocketsNetworkClient::read() {
83+
if (_impl->gsm_client_) {
84+
return _impl->gsm_client_->read();
85+
} else if (_impl->wifi_client_) {
86+
return _impl->wifi_client_->read();
87+
}
88+
Serial.println(_impl->no_interface_error_);
89+
return 0;
90+
}
91+
92+
int WebSocketsNetworkClient::read(uint8_t *buf, size_t size) {
93+
if (_impl->gsm_client_) {
94+
return _impl->gsm_client_->read(buf, size);
95+
} else if (_impl->wifi_client_) {
96+
return _impl->wifi_client_->read(buf, size);
97+
}
98+
Serial.println(_impl->no_interface_error_);
99+
return 0;
100+
}
101+
102+
int WebSocketsNetworkClient::peek() {
103+
if (_impl->gsm_client_) {
104+
return _impl->gsm_client_->peek();
105+
} else if (_impl->wifi_client_) {
106+
return _impl->wifi_client_->peek();
107+
}
108+
Serial.println(_impl->no_interface_error_);
109+
return 0;
110+
}
111+
112+
void WebSocketsNetworkClient::flush() {
113+
if (_impl->gsm_client_) {
114+
return _impl->gsm_client_->flush();
115+
} else if (_impl->wifi_client_) {
116+
return _impl->wifi_client_->flush();
117+
}
118+
Serial.println(_impl->no_interface_error_);
119+
}
120+
121+
void WebSocketsNetworkClient::stop() {
122+
if (_impl->gsm_client_) {
123+
return _impl->gsm_client_->stop();
124+
} else if (_impl->wifi_client_) {
125+
return _impl->wifi_client_->stop();
126+
}
127+
Serial.println(_impl->no_interface_error_);
128+
}
129+
130+
uint8_t WebSocketsNetworkClient::connected() {
131+
if (_impl->gsm_client_) {
132+
return _impl->gsm_client_->connected();
133+
} else if (_impl->wifi_client_) {
134+
return _impl->wifi_client_->connected();
135+
}
136+
Serial.println(_impl->no_interface_error_);
137+
return 0;
138+
}
139+
140+
WebSocketsNetworkClient::operator bool() {
141+
if (_impl->gsm_client_) {
142+
return _impl->gsm_client_->operator bool();
143+
} else if (_impl->wifi_client_) {
144+
return _impl->wifi_client_->operator bool();
145+
}
146+
Serial.println(_impl->no_interface_error_);
147+
return 0;
148+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#include "network_client_impl.h"
2+
3+
std::unique_ptr<WiFiClient> WebSocketsNetworkClient::Impl::wifi_client_ = nullptr;
4+
std::unique_ptr<WiFiClientSecure> WebSocketsNetworkClient::Impl::wifi_client_secure_ =
5+
nullptr;
6+
std::unique_ptr<TinyGsmClient> WebSocketsNetworkClient::Impl::gsm_client_ = nullptr;
7+
std::unique_ptr<SSLClient> WebSocketsNetworkClient::Impl::gsm_client_secure_ = nullptr;
8+
const char *WebSocketsNetworkClient::Impl::no_interface_error_ = "No interface";
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
#pragma once
2+
3+
#include <SSLClient.h>
4+
#include <TinyGSM.h>
5+
#include <WebSocketsNetworkClientSecure.h>
6+
#include <WiFiClientSecure.h>
7+
8+
struct WebSocketsNetworkClient::Impl {
9+
static void enableWifi() {
10+
// Do nothing if already enabled
11+
if (wifi_client_ && wifi_client_secure_) {
12+
return;
13+
}
14+
wifi_client_ = std::unique_ptr<WiFiClient>(new WiFiClient());
15+
wifi_client_secure_ =
16+
std::unique_ptr<WiFiClientSecure>(new WiFiClientSecure());
17+
}
18+
static void disableWifi() {
19+
wifi_client_ = nullptr;
20+
wifi_client_secure_ = nullptr;
21+
}
22+
static void enableGsm(TinyGsm* tiny_gsm) {
23+
// Do nothing if already enabled
24+
if (gsm_client_ && gsm_client_secure_) {
25+
return;
26+
}
27+
gsm_client_ = std::unique_ptr<TinyGsmClient>(new TinyGsmClient(*tiny_gsm));
28+
gsm_client_secure_ =
29+
std::unique_ptr<SSLClient>(new SSLClient(gsm_client_.get()));
30+
}
31+
static void disableGsm() {
32+
if (gsm_client_secure_) {
33+
gsm_client_secure_->stop();
34+
}
35+
gsm_client_secure_ = nullptr;
36+
gsm_client_ = nullptr;
37+
}
38+
39+
static std::unique_ptr<WiFiClient> wifi_client_;
40+
static std::unique_ptr<WiFiClientSecure> wifi_client_secure_;
41+
static std::unique_ptr<TinyGsmClient> gsm_client_;
42+
static std::unique_ptr<SSLClient> gsm_client_secure_;
43+
44+
static const char* no_interface_error_;
45+
};

0 commit comments

Comments
 (0)