Skip to content

Allow definition of custom network interfaces #921

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions examples/esp32_pio/CustomNetworkClient/.clang-format
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
BasedOnStyle: Google
2 changes: 2 additions & 0 deletions examples/esp32_pio/CustomNetworkClient/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.pio
.vscode
Empty file.
25 changes: 25 additions & 0 deletions examples/esp32_pio/CustomNetworkClient/platformio.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
; PlatformIO Project Configuration File
;
; Build options: build flags, source filter
; Upload options: custom upload port, speed and extra flags
; Library options: dependencies, extra library storages
; Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html

[env:esp32dev]
platform = espressif32@6.11.0
board = esp32dev
framework = arduino
monitor_speed = 115200
upload_speed = 921600
build_flags =
-DCORE_DEBUG_LEVEL=5
-D WEBSOCKETS_NETWORK_TYPE=10
-D TINY_GSM_MODEM_SIM7600
lib_deps =
digitaldragon/SSLClient@^1.3.2
vshymanskyy/StreamDebugger@^1.0.1
vshymanskyy/TinyGSM@^0.12.0
symlink://../../..
3 changes: 3 additions & 0 deletions examples/esp32_pio/CustomNetworkClient/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
This example project shows how to use a custom NetworkClient class to switch between two underlying network interfaces.

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.
126 changes: 126 additions & 0 deletions examples/esp32_pio/CustomNetworkClient/src/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
/*
* main.cpp
*
* Created on: 15.06.2024
*
*/

#include <Arduino.h>
#include <WebSocketsClient.h>
#include <WiFiMulti.h>

#include "network_client_impl.h"

WiFiMulti wifiMulti;
WebSocketsClient webSocket;

#define USE_SERIAL Serial

void setClock() {
configTime(0, 0, "pool.ntp.org", "time.nist.gov");

USE_SERIAL.print(F("Waiting for NTP time sync: "));
time_t nowSecs = time(nullptr);
while (nowSecs < 8 * 3600 * 2) {
delay(500);
USE_SERIAL.print(F("."));
yield();
nowSecs = time(nullptr);
}

USE_SERIAL.println();
struct tm timeinfo;
gmtime_r(&nowSecs, &timeinfo);
USE_SERIAL.print(F("Current time: "));
USE_SERIAL.print(asctime(&timeinfo));
}

void hexdump(const void *mem, uint32_t len, uint8_t cols = 16) {
const uint8_t *src = (const uint8_t *)mem;
USE_SERIAL.printf("\n[HEXDUMP] Address: 0x%08X len: 0x%X (%d)",
(ptrdiff_t)src, len, len);
for (uint32_t i = 0; i < len; i++) {
if (i % cols == 0) {
USE_SERIAL.printf("\n[0x%08X] 0x%08X: ", (ptrdiff_t)src, i);
}
USE_SERIAL.printf("%02X ", *src);
src++;
}
USE_SERIAL.printf("\n");
}

void webSocketEvent(WStype_t type, uint8_t *payload, size_t length) {
switch (type) {
case WStype_DISCONNECTED:
USE_SERIAL.printf("[WSc] Disconnected!\n");
break;
case WStype_CONNECTED:
USE_SERIAL.printf("[WSc] Connected to url: %s\n", payload);

// send message to server when Connected
webSocket.sendTXT("Connected");
break;
case WStype_TEXT:
USE_SERIAL.printf("[WSc] get text: %s\n", payload);

// send message to server
// webSocket.sendTXT("message here");
break;
case WStype_BIN:
USE_SERIAL.printf("[WSc] get binary length: %u\n", length);
hexdump(payload, length);

// send data to server
// webSocket.sendBIN(payload, length);
break;
case WStype_ERROR:
case WStype_FRAGMENT_TEXT_START:
case WStype_FRAGMENT_BIN_START:
case WStype_FRAGMENT:
case WStype_FRAGMENT_FIN:
break;
}
}

void setup() {
USE_SERIAL.begin(115200);

USE_SERIAL.setDebugOutput(true);

USE_SERIAL.println();
USE_SERIAL.println();
USE_SERIAL.println();

for (uint8_t t = 4; t > 0; t--) {
USE_SERIAL.printf("[SETUP] BOOT WAIT %d...\n", t);
USE_SERIAL.flush();
delay(1000);
}

wifiMulti.addAP("Berge", "BlauerHimmel!");

// WiFi.disconnect();
while (wifiMulti.run() != WL_CONNECTED) {
delay(100);
}

// Call to enable WiFi interface to be used by the webSocketClient
WebSocketsNetworkClient::Impl::enableWifi();

setClock();

// server address, port and URL. This server can be flakey.
// Expected response: Request served by 0123456789abcdef
webSocket.beginSSL("echo.websocket.org", 443, "/");

// event handler
webSocket.onEvent(webSocketEvent);

// use HTTP Basic Authorization this is optional enable if needed
// webSocket.setAuthorization("user", "Password");

// try ever 5000 again if connection has failed
webSocket.setReconnectInterval(5000);
}

void loop() { webSocket.loop(); }
148 changes: 148 additions & 0 deletions examples/esp32_pio/CustomNetworkClient/src/network_client.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
#include "network_client_impl.h"

WebSocketsNetworkClient::WebSocketsNetworkClient()
: _impl(new WebSocketsNetworkClient::Impl()) {}
WebSocketsNetworkClient::WebSocketsNetworkClient(WiFiClient wifi_client)
: _impl(new WebSocketsNetworkClient::Impl()) {}
WebSocketsNetworkClient::~WebSocketsNetworkClient() {}

int WebSocketsNetworkClient::connect(IPAddress ip, uint16_t port) {
if (_impl->gsm_client_) {
return _impl->gsm_client_->connect(ip, port);
} else if (_impl->wifi_client_) {
return _impl->wifi_client_->connect(ip, port);
}
Serial.println(_impl->no_interface_error_);
return 0;
}

int WebSocketsNetworkClient::connect(const char *host, uint16_t port) {
if (_impl->gsm_client_) {
return _impl->gsm_client_->connect(host, port);
} else if (_impl->wifi_client_) {
return _impl->wifi_client_->connect(host, port);
}
Serial.println(_impl->no_interface_error_);
return 0;
}
int WebSocketsNetworkClient::connect(const char *host, uint16_t port,
int32_t timeout_ms) {
if (_impl->gsm_client_) {
return _impl->gsm_client_->connect(host, port, timeout_ms);
} else if (_impl->wifi_client_) {
return _impl->wifi_client_->connect(host, port, timeout_ms);
}
Serial.println(_impl->no_interface_error_);
return 0;
}

size_t WebSocketsNetworkClient::write(uint8_t data) {
if (_impl->gsm_client_) {
return _impl->gsm_client_->write(data);
} else if (_impl->wifi_client_) {
return _impl->wifi_client_->write(data);
}
Serial.println(_impl->no_interface_error_);
return 0;
}

size_t WebSocketsNetworkClient::write(const uint8_t *buf, size_t size) {
Serial.printf("Send_: %zu\n", size);
if (_impl->gsm_client_) {
return _impl->gsm_client_->write(buf, size);
} else if (_impl->wifi_client_) {
return _impl->wifi_client_->write(buf, size);
}
Serial.println(_impl->no_interface_error_);
return 0;
}

size_t WebSocketsNetworkClient::write(const char *str) {
const int size = strlen(str);
Serial.printf("Send: %zu\n", size);
if (_impl->gsm_client_) {
return _impl->gsm_client_->write((const uint8_t *)str, size);
} else if (_impl->wifi_client_) {
return _impl->wifi_client_->write((const uint8_t *)str, size);
}
Serial.println(_impl->no_interface_error_);
return 0;
}

int WebSocketsNetworkClient::available() {
if (_impl->gsm_client_) {
return _impl->gsm_client_->available();
} else if (_impl->wifi_client_) {
return _impl->wifi_client_->available();
}
Serial.println(_impl->no_interface_error_);
return 0;
}

int WebSocketsNetworkClient::read() {
if (_impl->gsm_client_) {
return _impl->gsm_client_->read();
} else if (_impl->wifi_client_) {
return _impl->wifi_client_->read();
}
Serial.println(_impl->no_interface_error_);
return 0;
}

int WebSocketsNetworkClient::read(uint8_t *buf, size_t size) {
if (_impl->gsm_client_) {
return _impl->gsm_client_->read(buf, size);
} else if (_impl->wifi_client_) {
return _impl->wifi_client_->read(buf, size);
}
Serial.println(_impl->no_interface_error_);
return 0;
}

int WebSocketsNetworkClient::peek() {
if (_impl->gsm_client_) {
return _impl->gsm_client_->peek();
} else if (_impl->wifi_client_) {
return _impl->wifi_client_->peek();
}
Serial.println(_impl->no_interface_error_);
return 0;
}

void WebSocketsNetworkClient::flush() {
if (_impl->gsm_client_) {
return _impl->gsm_client_->flush();
} else if (_impl->wifi_client_) {
return _impl->wifi_client_->flush();
}
Serial.println(_impl->no_interface_error_);
}

void WebSocketsNetworkClient::stop() {
if (_impl->gsm_client_) {
return _impl->gsm_client_->stop();
} else if (_impl->wifi_client_) {
return _impl->wifi_client_->stop();
}
Serial.println(_impl->no_interface_error_);
}

uint8_t WebSocketsNetworkClient::connected() {
if (_impl->gsm_client_) {
return _impl->gsm_client_->connected();
} else if (_impl->wifi_client_) {
return _impl->wifi_client_->connected();
}
Serial.println(_impl->no_interface_error_);
return 0;
}

WebSocketsNetworkClient::operator bool() {
if (_impl->gsm_client_) {
return _impl->gsm_client_->operator bool();
} else if (_impl->wifi_client_) {
return _impl->wifi_client_->operator bool();
}
Serial.println(_impl->no_interface_error_);
return 0;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#include "network_client_impl.h"

std::unique_ptr<WiFiClient> WebSocketsNetworkClient::Impl::wifi_client_ = nullptr;
std::unique_ptr<WiFiClientSecure> WebSocketsNetworkClient::Impl::wifi_client_secure_ =
nullptr;
std::unique_ptr<TinyGsmClient> WebSocketsNetworkClient::Impl::gsm_client_ = nullptr;
std::unique_ptr<SSLClient> WebSocketsNetworkClient::Impl::gsm_client_secure_ = nullptr;
const char *WebSocketsNetworkClient::Impl::no_interface_error_ = "No interface";
45 changes: 45 additions & 0 deletions examples/esp32_pio/CustomNetworkClient/src/network_client_impl.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#pragma once

#include <SSLClient.h>
#include <TinyGSM.h>
#include <WebSocketsNetworkClientSecure.h>
#include <WiFiClientSecure.h>

struct WebSocketsNetworkClient::Impl {
static void enableWifi() {
// Do nothing if already enabled
if (wifi_client_ && wifi_client_secure_) {
return;
}
wifi_client_ = std::unique_ptr<WiFiClient>(new WiFiClient());
wifi_client_secure_ =
std::unique_ptr<WiFiClientSecure>(new WiFiClientSecure());
}
static void disableWifi() {
wifi_client_ = nullptr;
wifi_client_secure_ = nullptr;
}
static void enableGsm(TinyGsm* tiny_gsm) {
// Do nothing if already enabled
if (gsm_client_ && gsm_client_secure_) {
return;
}
gsm_client_ = std::unique_ptr<TinyGsmClient>(new TinyGsmClient(*tiny_gsm));
gsm_client_secure_ =
std::unique_ptr<SSLClient>(new SSLClient(gsm_client_.get()));
}
static void disableGsm() {
if (gsm_client_secure_) {
gsm_client_secure_->stop();
}
gsm_client_secure_ = nullptr;
gsm_client_ = nullptr;
}

static std::unique_ptr<WiFiClient> wifi_client_;
static std::unique_ptr<WiFiClientSecure> wifi_client_secure_;
static std::unique_ptr<TinyGsmClient> gsm_client_;
static std::unique_ptr<SSLClient> gsm_client_secure_;

static const char* no_interface_error_;
};
Loading