WebSockets_Generic Library.

khoih-prog

Well-known member
https://github.com/khoih-prog/WebSockets_Generic

How To Install Using Arduino Library Manager

Current Release

Why do we need this library

Many Web services require WebSockets library , which is so far written only for ESP8266/ESP32 boards. The ESP boards rely on this Markus Sattler's WebSockets library to connect to Alexa via Sinric or SinricPro skills.

This library is based on and modified from WebSockets library to provide support to many boards such as Teensy (4.1, 4.0, 3.x, LC), Arduino SAMD21, Adafruit SAMD21/SAMD51, nRF52, STM32, etc. and enable those boards to use WebSockets services, including voice-control Alexa along with Blynk. The WebSockets can be used with ESP's WiFi, WiFiNINA, W5x00 and ENC28J60 Ethernet.

Please see illustrating examples.

New in v2.2.1

1. Bump up to sync with v2.2.1 of original WebSockets library

New in v2.1.3

1. Add support to nRF52 boards, such as AdaFruit Feather nRF52832, nRF52840 Express, BlueFruit Sense, Itsy-Bitsy nRF52840 Express, Metro nRF52840 Express, NINA_B30_ublox, etc..
2. Add support to SAM51 (Itsy-Bitsy M4, Metro M4, Grand Central M4, Feather M4 Express, etc.).
3. Add support to SAMD21 (ZERO, MKR, NANO_33_IOT, M0, M0 Pro, AdaFruit CIRCUITPLAYGROUND_EXPRESS, etc.).
4. Add support to Teensy (4.1, 4.0, 3.x, LC).
5. Add support to SAM DUE.


How to use

In your code, select one of the currently supported network connection from the following list:

1. NETWORK_ESP8266_ASYNC for ESP8266 Async
2. NETWORK_W5100 for W5x00 Ethernet
3. NETWORK_ENC28J60 for ENC28J60 Ethernet
4. NETWORK_ESP32 for ESP32 WiFi
5. NETWORK_ESP32_ETH for ESP32 Ethernet
6. NETWORK_WIFININA for WiFiNINA

then add

Code:
#define WEBSOCKETS_NETWORK_TYPE

before

Code:
#include <WebSocketsClient_Generic.h>


Example Generic_WebSocketServer_W5500

Code:
#define _WEBSOCKETS_LOGLEVEL_     3
#define WEBSOCKETS_NETWORK_TYPE   NETWORK_W5100

#include <WebSocketsServer_Generic.h>

WebSocketsServer webSocket = WebSocketsServer(81);

uint8_t mac[6] =  { 0xDE, 0xAD, 0xBE, 0xEF, 0xBE, 0x0A };

// Select the IP address according to your local network
IPAddress ip(192, 168, 2, 222);

// Only for W5100
#define SDCARD_CS       4

void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length)
{
  switch (type)
  {
    case WStype_DISCONNECTED:
      //Serial.println( "[" + String(num) + "] Disconnected!");
      break;
    case WStype_CONNECTED:
      {
        Serial.println( "[" + String(num) + "] Connected!");
        //IPAddress ip = webSocket.remoteIP(num);
        //Serial.printf("[%u] Connected from %d.%d.%d.%d url: %s\n", num, ip[0], ip[1], ip[2], ip[3], payload);

        // send message to client
        webSocket.sendTXT(num, "Connected");
      }
      break;
    case WStype_TEXT:
      Serial.println( "[" + String(num) + "] get Text: " + String((char *) payload));

      // send message to client
      webSocket.sendTXT(num, "message here");

      // send data to all connected clients
       webSocket.broadcastTXT("message here");
      break;
    case WStype_BIN:
      Serial.println( "[" + String(num) + "] get binary length: " + String(length));
      
      //hexdump(payload, length);

      // send message to client
      // webSocket.sendBIN(num, payload, length);
      break;

      default:
        break;
  }
}

void setup()
{
  pinMode(SDCARD_CS, OUTPUT);
  digitalWrite(SDCARD_CS, HIGH); // Deselect the SD card
  
  // Serial.begin(921600);
  Serial.begin(115200);
  while (!Serial);

  Serial.println("\nStart Generic_WebSocketServer_W5500");

  for (uint8_t t = 4; t > 0; t--)
  {
    Serial.println("[SETUP] BOOT WAIT " + String(t));
    Serial.flush();
    delay(1000);
  }

  // start the ethernet connection and the server:
  // Use Static IP
  Ethernet.begin(mac, ip);
  //Configure IP address via DHCP
  //Ethernet.begin(mac);

  // server address, port and URL
  Serial.print("WebSockets Server IP address: ");
  Serial.println(Ethernet.localIP());
  
  webSocket.begin();
  webSocket.onEvent(webSocketEvent);
}

void loop()
{
  webSocket.loop();
}


Debug Terminal Output when running nRF52_Blynk_W5500_Alexa

You can see the Adafruit NRF52840_FEATHER Express board, with W5500 Ethernet shield, connects to Blynk using BlynkEthernet_WM Library

It also uses WebSockets_Generic library to communicate with SINRIC skills, and Alexa, to control a relay using Alexa voice control via Amazon Alexa phone APP

Code:
Start nRF52_Blynk_W5500_Alexa using W5x00_Shield on NRF52840_FEATHER
LittleFS Flag read = 0xd0d04321
Flag read = 0xd0d04321
No doubleResetDetected
Saving DOUBLERESETDETECTOR_FLAG to DRD file : 0xd0d01234
Saving DRD file OK
SetFlag write = 0xd0d01234
[881] ======= Start Default Config Data =======
[881] Hdr=NonSSL,BName=Air-Control
[881] Svr=account.duckdns.org,Tok=token1
[881] Svr1=blynk-cloud.com,Tok1=<<my real Blynk auth>>
[882] Prt=8080,SIP=
[882] LoadCfgFile 
[883] OK
[883] ======= Start Stored Config Data =======
[883] Hdr=W5X00,BName=Air-Control
[883] Svr=account.duckdns.org,Tok=****
[884] Svr1=blynk-cloud.com,Tok1=****
[884] Prt=8080,SIP=
[884] CCSum=0x262e,RCSum=0x262e
[886] LoadCredFile 
[886] ChkCrR: Buffer allocated, sz=37
[886] ChkCrR:pdata=****,len=36
[886] ChkCrR:pdata=****,len=24
[886] OK
[887] CrCCsum=ea7,CrRCsum=ea7
[887] Buffer freed
[887] Valid Stored Dynamic Data
[889] LoadCredFile 
[889] CrR:pdata=****,len=36
[889] CrR:pdata=****,len=24
[889] OK
[889] CrCCsum=ea7,CrRCsum=ea7
[890] Hdr=W5X00,BName=Air-Control
[890] Svr=account.duckdns.org,Tok=****
[890] Svr1=blynk-cloud.com,Tok1=****
[890] Prt=8080,SIP=
[890] MAC:FE-F8-E0-CB-D0-BD
_pinCS = 0
W5100 init, using SS_PIN_DEFAULT = 10, new ss_pin = 10, W5100Class::ss_pin = 10
W5100::init: W5500, SSIZE =4096
[2577] IP:192.168.2.89
[2577] bg: noConfigPortal = true
[2577] bg: noConfigPortal = true
[2577] bg:ECon.TryB
[2577] 
    ___  __          __
   / _ )/ /_ _____  / /__
  / _  / / // / _ \/  '_/
 /____/_/\_, /_//_/_/\_\
        /___/ v0.6.1 on Arduino

[2578] BlynkArduinoClient.connect: Connecting to account.duckdns.org:8080
[2687] Ready (ping: 3ms).
[2754] Connected to Blynk Server = account.duckdns.org, Token = ****
[2754] bg:EBCon

Your stored Credentials :
SINRIC API Key = ****
Device_ID1 = ****
[WSc] Service connected to sinric.com at url: /
Waiting for commands from sinric.com ...
Stop doubleResetDetecting
Saving to DRD file : 0xd0d04321
Saving DRD file OK
LittleFS Flag read = 0xd0d04321
ClearFlag write = 0xd0d04321
[WSc] get text: {"deviceId":"****","action":"setPowerState","value":"ON"}
Turn on for unknown device id: ****
[WSc] get text: {"deviceId":"****","action":"setPowerState","value":"OFF"}
Turn off for unknown device id: ****
[WSc] get text: {"action":"test","sender":"web"}
[WSc] received test command from sinric.com
[WSc] get text: {"action":"test","sender":"web"}
[WSc] received test command from sinric.com
[WSc] get text: {"deviceId":"****","action":"setPowerState","value":"ON"}
Turn on device id: ****
[WSc] get text: {"deviceId":"****","action":"setPowerState","value":"OFF"}
Turn off Device ID: ****
 
WebSockets_Generic Library release 2.4.0 to add support to Teensy 4.1 NativeEthernet

The new release to support Teensy 4.1 NativeEthernet has been published.

The Teensy 4.1 NativeEthernet examples can be found at Teensy NativeEthernet

Please test and report issue in WebSockets_Generic issues


Major Release v2.4.0

1. Add support to Teensy 4.1 using NativeEthernet.
2. Add support to STM32F/L/H/G/WB/MP1 using built-in LAN8742A.
3. Sync with v2.3.4 of original WebSockets library
4. Add Teensy, Teensy 4.1 NativeEthernet and STM32 LAN8742A examples.
5. Fix bugs in and optimize examples.
 
You can also use this WebSockets2_Generic Library, which is also supporting Teensy 4.1 NativeEthernet

Major Release v1.1.0
1. Add support to Teensy using Ethernet libraries such as Ethernet, Ethernet2, Ethernet3, EthernetLarge, EthernetENC, UIPEthernet.
2. Add support to Teensy 4.1 using NativeEthernet library.
3. Add Version String
 
For usage with Teensy 4.1 and the NativeEthernet library, which Websocket Generic library would you recommend? the 1st or 2nd version? Sorry if it's a stupid question but I wonder what are the differences (again only for T4_1 with NativeEthernet)?
 
Teensy 4.1 websocket client using websockets2_generic, NativeEthernet, Ethernet Kit

Hello
thank you for your support.

I am trying to use the example in the Generic2 library Teensy41_Client.ino For Teensy 4.1 with NativeEthernet. to connect to nodejs server on laptop via direct ethernet cable connection. I am trying to assign fixed IP both for Teensy41 and for the laptop with nodejs server.

Please help !

Thank you in advance

in the T41 program: _test62.ino
Arduino 1.8.13
Teensy Loader 1.53
we use the libraries
websockets2_generic 1.10.1
NativeEthernet
TeensyID
Teensy41 Websockets Client (using NativeEthernet)

nodejs: simple websockets server
v12.16.2

behaviour
nodejs is on laptop ipServer[] = {192, 168, 1, 40};
can ping the T41 ipClient[] = {192,168,1,41}; from laptop
direct cable connection between laptop and T41
T41 has no wiznet module and has a functional and tested TCP connection
stops at client.connect(url)
tried with and without ws:// in url same result
or at Failed to configure Ethernet using DHCP

the nodejs server program is (tested and working):
//https://www.npmjs.com/package/websocket
//Server Example
//Here's a short example showing a server that echos back anything sent to it,
// whether utf-8 or binary.

//#!/usr/bin/env node
var WebSocketServer = require('websocket').server;
var http = require('http');

var server = http.createServer(function(request, response) {
console.log((new Date()) + ' Received request for ' + request.url);
response.writeHead(404);
response.end();
});
server.listen(8080, function() {
console.log((new Date()) + ' Server is listening on port 8080');
});

wsServer = new WebSocketServer({
httpServer: server,
// You should not use autoAcceptConnections for production
// applications, as it defeats all standard cross-origin protection
// facilities built into the protocol and the browser. You should
// *always* verify the connection's origin and decide whether or not
// to accept it.
autoAcceptConnections: false
});

function originIsAllowed(origin) {
// put logic here to detect whether the specified origin is allowed.
return true;
}

wsServer.on('request', function(request) {
if (!originIsAllowed(request.origin)) {
// Make sure we only accept requests from an allowed origin
request.reject();
console.log((new Date()) + ' Connection from origin ' + request.origin + ' rejected.');
return;
}

var connection = request.accept('echo-protocol', request.origin);
console.log((new Date()) + ' Connection accepted.');
connection.on('message', function(message) {
if (message.type === 'utf8') {
console.log('Received Message: ' + message.utf8Data);
connection.sendUTF(message.utf8Data);
}
else if (message.type === 'binary') {
console.log('Received Binary Message of ' + message.binaryData.length + ' bytes');
connection.sendBytes(message.binaryData);
}
});
connection.on('close', function(reasonCode, description) {
console.log((new Date()) + ' Peer ' + connection.remoteAddress + ' disconnected.');
});
});
 
Hello
thank you for your support.

I am trying to use the example in the Generic2 library Teensy41_Client.ino For Teensy 4.1 with NativeEthernet. to connect to nodejs server on laptop via direct ethernet cable connection.

in the T41 program: _test62.ino
Arduino 1.8.13
Teensy Loader 1.53
we use the libraries
websockets2_generic 1.10.1
NativeEthernet
TeensyID
Teensy41 Websockets Client (using NativeEthernet)

nodejs: simple websockets server
v12.16.2

behaviour
nodejs is on laptop ipServer[] = {192, 168, 1, 40};
can ping the T41 ipClient[] = {192,168,1,41}; from laptop
direct cable connection between laptop and T41
T41 has no wiznet module and has a functional and tested TCP connection
stops at client.connect(url)
tried with and without ws:// in url same result
or at Failed to configure Ethernet using DHCP

the nodejs server program is:
//https://www.npmjs.com/package/websocket
//Server Example
//Here's a short example showing a server that echos back anything sent to it,
// whether utf-8 or binary.

//#!/usr/bin/env node
var WebSocketServer = require('websocket').server;
var http = require('http');

var server = http.createServer(function(request, response) {
console.log((new Date()) + ' Received request for ' + request.url);
response.writeHead(404);
response.end();
});
server.listen(8080, function() {
console.log((new Date()) + ' Server is listening on port 8080');
});

wsServer = new WebSocketServer({
httpServer: server,
// You should not use autoAcceptConnections for production
// applications, as it defeats all standard cross-origin protection
// facilities built into the protocol and the browser. You should
// *always* verify the connection's origin and decide whether or not
// to accept it.
autoAcceptConnections: false
});

function originIsAllowed(origin) {
// put logic here to detect whether the specified origin is allowed.
return true;
}

wsServer.on('request', function(request) {
if (!originIsAllowed(request.origin)) {
// Make sure we only accept requests from an allowed origin
request.reject();
console.log((new Date()) + ' Connection from origin ' + request.origin + ' rejected.');
return;
}

var connection = request.accept('echo-protocol', request.origin);
console.log((new Date()) + ' Connection accepted.');
connection.on('message', function(message) {
if (message.type === 'utf8') {
console.log('Received Message: ' + message.utf8Data);
connection.sendUTF(message.utf8Data);
}
else if (message.type === 'binary') {
console.log('Received Binary Message of ' + message.binaryData.length + ' bytes');
connection.sendBytes(message.binaryData);
}
});
connection.on('close', function(reasonCode, description) {
console.log((new Date()) + ' Peer ' + connection.remoteAddress + ' disconnected.');
});
});
 
Please check your network, installation, etc.

I've tested using both NativeEthernet and QNEthernet and the exactly the same server code, without any issue

1. NativeEthernet

Code:
Start Teensy41_Client on Teensy 4.1
WebSockets2_Generic v1.10.1
Ethernet connected (192.168.2.103)
Connected to server ws://192.168.2.31:8080
Got Message: Hello Server

2. QNEthernet

Code:
Starting Teensy41_Client on TEENSY 4.1 using QNEthernet
WebSockets2_Generic v1.10.1
=========== USE_QN_ETHERNET ===========
Initialize Ethernet using DHCP => Connected! IP address:192.168.2.103
Connected to server : ws://192.168.2.31:8080
Got Message: Hello Server

I also suggest you use the new and better QNEthernet

Good Luck,
 
Thank you for the suggestions!
I will try it again using the QN Ethernet as per your advice.
I will keep the direct Ethernet connection (cable) between laptop (with ndejs server) and Teensy 4.1 using the fixed IP addresses.
I will also keep the Arduino 1.8.13 Teensyduino Loader 1.53 as before, unless suggested otherwise.
I will surely let you know teh result. THX again
 
It's better to update to the versions I'm using now

1. Teensyduino v1.56
2. Arduino IDE v1.8.19

Also try if your WS server is working by using the Nodejs WS CLient from websocket
 
I will do all suggested updates (I just saw the reply THX).
I will also re-test the server.
What I did in the meantime: I finally understood that I need an external DHCP server
Installed router and configured DHCP as required.
I used the Teensy41_Client from the QNEthernet examples
I received this message from Teensy41 after connection:
[WS] WebsocketsClient::connect: CloseReason_ProtocolError
Couldn't connect to server : ws://192.168.2.2:8080

On server side: Error: Specified protocol was not requested by the client.

I will do the suggested updates and try again. Thank you
 
Hello again !

1. Teensyduino v1.56
2. Arduino IDE v1.8.19
3. Tested server again: ok
4. Teensy41 and laptop are connected to the external DHCP server(router) on the Ethernet ports
5. I used "Teensy41_Client.ino For Teensy 4.1 with QNEthernet." from QNEthernet examples
6. I used the same server already posted.

NOTES:
on compiling I receive a long list of warnings as follows:
WARNING: library TeensyID claims to run on Teensy architecture(s) and may be incompatible with your current board which runs on avr architecture(s).

In file included from D:\ArduinoP1819\arduino-1.8.19\portable\sketchbook\libraries\WebSockets2_Generic\src/WebSockets2_Generic.h:39:0,
from D:\ArduinoP1819\arduino-1.8.19\portable\sketchbook\Teensy41_Client\Teensy41_Client.ino:54:
D:\ArduinoP1819\arduino-1.8.19\portable\sketchbook\libraries\WebSockets2_Generic\src/Tiny_Websockets_Generic/message.hpp:51:6: warning: #warning WEBSOCKETS_USE_ETHERNET and USE_QN_ETHERNET in message.hpp [-Wcpp]
#warning WEBSOCKETS_USE_ETHERNET and USE_QN_ETHERNET in message.hpp
^
In file included from D:\ArduinoP1819\arduino-1.8.19\portable\sketchbook\libraries\WebSockets2_Generic\src/Tiny_Websockets_Generic/message.hpp:52:0,
from D:\ArduinoP1819\arduino-1.8.19\portable\sketchbook\libraries\WebSockets2_Generic\src/WebSockets2_Generic.h:39,
from D:\ArduinoP1819\arduino-1.8.19\portable\sketchbook\Teensy41_Client\Teensy41_Client.ino:54:
D:\ArduinoP1819\arduino-1.8.19\portable\sketchbook\libraries\WebSockets2_Generic\src/Tiny_Websockets_Generic/internals/ws_common_QNEthernet.hpp:48:8: warning: #warning Using QNEthernet for Teensy 4.1 in ws_common_QNEthernet.hpp [-Wcpp]
#warning Using QNEthernet for Teensy 4.1 in ws_common_QNEthernet.hpp
^
In file included from D:\ArduinoP1819\arduino-1.8.19\portable\sketchbook\libraries\WebSockets2_Generic\src/WebSockets2_Generic.h:40:0,
from D:\ArduinoP1819\arduino-1.8.19\portable\sketchbook\Teensy41_Client\Teensy41_Client.ino:54:
D:\ArduinoP1819\arduino-1.8.19\portable\sketchbook\libraries\WebSockets2_Generic\src/Tiny_Websockets_Generic/client.hpp:57:6: warning: #warning WEBSOCKETS_USE_ETHERNET and USE_QN_ETHERNET in client.hpp [-Wcpp]
#warning WEBSOCKETS_USE_ETHERNET and USE_QN_ETHERNET in client.hpp
^
In file included from D:\ArduinoP1819\arduino-1.8.19\portable\sketchbook\libraries\WebSockets2_Generic\src/WebSockets2_Generic.h:41:0,
from D:\ArduinoP1819\arduino-1.8.19\portable\sketchbook\Teensy41_Client\Teensy41_Client.ino:54:
D:\ArduinoP1819\arduino-1.8.19\portable\sketchbook\libraries\WebSockets2_Generic\src/Tiny_Websockets_Generic/server.hpp:52:6: warning: #warning WEBSOCKETS_USE_ETHERNET and USE_QN_ETHERNET in server.hpp [-Wcpp]
#warning WEBSOCKETS_USE_ETHERNET and USE_QN_ETHERNET in server.hpp
^
Teensy41_Client:80: warning: #warning Using QNEthernet lib for Teensy 4.1. Must also use Teensy Packages Patch or error
#warning Using QNEthernet lib for Teensy 4.1. Must also use Teensy Packages Patch or error
^
Memory Usage on Teensy 4.1:
FLASH: code:150748, data:24472, headers:8672 free for files:7942572
RAM1: variables:88896, code:148024, padding:15816 free for local variables:271552
RAM2: variables:12384 free for malloc/new:511904

On running the Teensy41 program (with server started) I receive the following:
Starting Teensy41_Client on TEENSY 4.1 using QNEthernet
WebSockets2_Generic v1.10.1
=========== USE_QN_ETHERNET ===========
Initialize Ethernet using DHCP => Connected! IP address:192.168.2.3
[WS] WebsocketsClient::connect: CloseReason_ProtocolError
Couldn't connect to server : ws://192.168.2.2:8080

some times I receive (only a partial last line: see below...)
Starting Teensy41_Client on TEENSY 4.1 using QNEthernet
WebSockets2_Generic v1.10.1
=========== USE_QN_ETHERNET ===========
Initialize Ethernet using DHCP => Connected! IP address:192.168.2.3
C

from server I receive the following message:
D:\ArduinoP1819\arduino-1.8.19\portable\sketchbook\Teensy41_Client>node serverT41.js
Mon Mar 07 2022 20:37:34 GMT-0500 (GMT-05:00) Server is listening on port 8080
D:\ArduinoP1819\arduino-1.8.19\portable\sketchbook\Teensy41_Client\node_modules\websocket\lib\WebSocketRequest.js:297
throw new Error('Specified protocol was not requested by the client.');
^

Error: Specified protocol was not requested by the client.
at WebSocketRequest.accept (D:\ArduinoP1819\arduino-1.8.19\portable\sketchbook\Teensy41_Client\node_modules\websocket\lib\WebSocketRequest.js:297:19)
at WebSocketServer.<anonymous> (D:\ArduinoP1819\arduino-1.8.19\portable\sketchbook\Teensy41_Client\serverT41.js:46:30)
at WebSocketServer.emit (events.js:310:20)
at WebSocketServer.handleUpgrade (D:\ArduinoP1819\arduino-1.8.19\portable\sketchbook\Teensy41_Client\node_modules\websocket\lib\WebSocketServer.js:224:14)
at Server.emit (events.js:310:20)
at onParserExecuteCommon (_http_server.js:642:14)
at onParserExecute (_http_server.js:583:3)

after ipconfig I receive the address of my laptop :
Ethernet adapter Ethernet:

Connection-specific DNS Suffix . :
IPv4 Address. . . . . . . . . . . : 192.168.2.2
Subnet Mask . . . . . . . . . . . : 255.255.255.0
Default Gateway . . . . . . . . . : 192.168.2.1


after pinging the Teensy41 I receive:
D:\ArduinoP1819\arduino-1.8.19\portable\sketchbook\Teensy41_Client>ping 192.168.2.3

Pinging 192.168.2.3 with 32 bytes of data:
Reply from 192.168.2.3: bytes=32 time<1ms TTL=255
Reply from 192.168.2.3: bytes=32 time=1ms TTL=255
Reply from 192.168.2.3: bytes=32 time<1ms TTL=255
Reply from 192.168.2.3: bytes=32 time<1ms TTL=255

Ping statistics for 192.168.2.3:
Packets: Sent = 4, Received = 4, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
Minimum = 0ms, Maximum = 1ms, Average = 0ms

In the sketch I only removed the //#include <SPI.h>
With or without it behavior is the same.

Please help me understand and fix it. If I need to read more doc on this new library please suggest the link.

Thank you in advance, I appreciate your help!

PS: should I try with your version of node.js and npm ?
I have node v12.16.2 and npm 6.14.4
 
Hello again!
I do not have a wiznet-type connection. I am using the Ethernet kit to connect directly to the Teensy41's 6-pin-connector.
This is certainly working with my previous configuration TCP server on Teensy41 (used it for 1 year) with Native_Ethernet.
I also tried to turn off Windows Defender - without success: it keeps turning ON even after my setting it to OFF.

Below is the code of the sketch,
Code:
/****************************************************************************************************************************
  this file comes from: Arduino 1.8.19/File/Examples/WebSockets_Generic/Generic/Teensy41_QNEthernet/Teensy41_Client.ino
  Arduino 1.8.19 is in portable configuration
  Teensyduino version installed 1.56
  
  Teensy41_Client.ino
  For Teensy 4.1 with QNEthernet.

  Based on and modified from Gil Maimon's ArduinoWebsockets library https://github.com/gilmaimon/ArduinoWebsockets
  to support STM32F/L/H/G/WB/MP1, nRF52, SAMD21/SAMD51, SAM DUE, Teensy boards besides ESP8266 and ESP32

  The library provides simple and easy interface for websockets (Client and Server).
  
  Built by Khoi Hoang https://github.com/khoih-prog/Websockets2_Generic
  Licensed under MIT license
 *****************************************************************************************************************************/
/*
  Teensy41 Websockets Client (using QNEthernet)

  This sketch:
  1. Connects to a ethernet network
  2. Connects to a websockets server at port 80
  3. Sends the websockets server a message ("Hello Server")
  4. Prints all incoming messages while the connection is open

  Note:
  Make sure you share your computer's internet connection with the Teensy
  via ethernet.

  Libraries:
  To use this sketch install
    TeensyID library (https://github.com/sstaub/TeensyID)
    QNEthernet (https://github.com/ssilverman/QNEthernet)

  Hardware:
  For this sketch you need a Teensy 4.1 board and the Teensy 4.1 Ethernet Kit
  (https://www.pjrc.com/store/ethernet_kit.html).

  Written by https://github.com/arnoson
*/

#if ( defined(CORE_TEENSY) && defined(__IMXRT1062__) && defined(ARDUINO_TEENSY41) ) 
  // For Teensy 4.1
  #define BOARD_TYPE      "TEENSY 4.1"
  // Use true for NativeEthernet Library, false if using other Ethernet libraries
  #define USE_NATIVE_ETHERNET     false
  #define USE_QN_ETHERNET         true
#else
  #error Only Teensy 4.1 supported
#endif

#ifndef BOARD_NAME
  #define BOARD_NAME    BOARD_TYPE
#endif

#define WEBSOCKETS_USE_ETHERNET     true

#include <WebSockets2_Generic.h>

#include <TeensyID.h>     // https://github.com/sstaub/TeensyID
// #include <SPI.h>

using namespace websockets2_generic;

WebsocketsClient client;

// Set the static IP address to use if the DHCP fails to assign
IPAddress myIP(192, 168, 2, 222);

#if USE_NATIVE_ETHERNET

  #include "NativeEthernet.h"
  #warning Using NativeEthernet lib for Teensy 4.1. Must also use Teensy Packages Patch or error
  #define SHIELD_TYPE           "using NativeEthernet"

  // We will set the MAC address at the beginning of `setup()` using TeensyID's
  // `teensyMac` helper.
  byte mac[6];

#else

  #include "QNEthernet.h"       // https://github.com/ssilverman/QNEthernet
  using namespace qindesign::network;
  #warning Using QNEthernet lib for Teensy 4.1. Must also use Teensy Packages Patch or error
  #define SHIELD_TYPE           "using QNEthernet"  

  // Static IP not working yet. Don't use now until fixed.
  #define USING_DHCP            true
  
  #if !USING_DHCP
    
    IPAddress myNetmask(255, 255, 255, 0);
    IPAddress myGW(192, 168, 2, 1);
    //IPAddress mydnsServer(192, 168, 2, 1);
    IPAddress mydnsServer(8, 8, 8, 8);
  #endif

#endif

// Enter websockets url.
// Note: wss:// currently not working.
// ws://echo.websocket.org is no longer working. Please create your own WS Local Server to test
//const char* url  = "ws://echo.websocket.org";
const char* url  = "ws://192.168.2.2:8080";

void setup() 
{
#if USE_NATIVE_ETHERNET  
  // Set the MAC address.
  teensyMAC(mac);
#endif

  // Start Serial and wait until it is ready.
  Serial.begin(115200);
  while (!Serial);
 
  Serial.print("\nStarting Teensy41_Client on "); Serial.print(BOARD_NAME); 
  Serial.print(" "); Serial.println(SHIELD_TYPE);
  Serial.println(WEBSOCKETS2_GENERIC_VERSION);

#if USE_NATIVE_ETHERNET
  Serial.println(F("======== USE_NATIVE_ETHERNET ========"));
#elif USE_QN_ETHERNET
  Serial.println(F("=========== USE_QN_ETHERNET ==========="));
#endif

#if USE_NATIVE_ETHERNET

  // start the ethernet connection and the server:
  
  // Use Static IP
  //Ethernet.begin(mac, myIP);
  // Use DHCP dynamic IP
  Ethernet.begin(mac);

  Serial.println(F("========================="));
  Serial.print("Connected! IP address:"); Serial.println(Ethernet.localIP());

#else

  #if USING_DHCP
    // Start the Ethernet connection, using DHCP
    Serial.print("Initialize Ethernet using DHCP => ");
    Ethernet.begin();
  #else   
    // Start the Ethernet connection, using static IP
    Serial.print("Initialize Ethernet using static IP => ");
    Ethernet.begin(myIP, myNetmask, myGW);
    Ethernet.setDNSServerIP(mydnsServer);
  #endif

  if (!Ethernet.waitForLocalIP(5000))
  {
    Serial.println(F("Failed to configure Ethernet"));

    if (!Ethernet.linkStatus())
    {
      Serial.println(F("Ethernet cable is not connected."));
    }

    // Stay here forever
    while (true)
    {
      delay(1);
    }
  }
  else
  {
    Serial.print(F("Connected! IP address:")); Serial.println(Ethernet.localIP());
  }

#endif

  // Connect to websocket server.
  if (client.connect(url)) 
  {
    Serial.print("Connected to server : "); Serial.println(url);
    // Send welcome message.
    client.send("Hello Server");
  } 
  else 
  {
    Serial.print("Couldn't connect to server : "); Serial.println(url);
  }

  // Run callback when messages are received.
  client.onMessage([&](WebsocketsMessage message) 
  {
    Serial.print("Got Message: ");
    Serial.println(message.data());
  });
}

void loop() 
{
  // Check for incoming messages.
  if (client.available()) 
  {
    client.poll();
  }
}

and same response from Teensy41:

Starting Teensy41_Client on TEENSY 4.1 using QNEthernet
WebSockets2_Generic v1.10.1
=========== USE_QN_ETHERNET ===========
Initialize Ethernet using DHCP => Connected! IP address:192.168.2.3
[WS] WebsocketsClient::connect: CloseReason_ProtocolError
Couldn't connect to server : ws://192.168.2.2:8080

and same response from server:
D:\ArduinoP1819\arduino-1.8.19\portable\sketchbook\Teensy41_Client>node serverT41.js
Tue Mar 08 2022 07:15:00 GMT-0500 (GMT-05:00) Server is listening on port 8080
D:\ArduinoP1819\arduino-1.8.19\portable\sketchbook\Teensy41_Client\node_modules\websocket\lib\WebSocketRequest.js:297
throw new Error('Specified protocol was not requested by the client.');
^

Error: Specified protocol was not requested by the client.
at WebSocketRequest.accept (D:\ArduinoP1819\arduino-1.8.19\portable\sketchbook\Teensy41_Client\node_modules\websocket\lib\WebSocketRequest.js:297:19)
at WebSocketServer.<anonymous> (D:\ArduinoP1819\arduino-1.8.19\portable\sketchbook\Teensy41_Client\serverT41.js:46:30)
at WebSocketServer.emit (events.js:310:20)
at WebSocketServer.handleUpgrade (D:\ArduinoP1819\arduino-1.8.19\portable\sketchbook\Teensy41_Client\node_modules\websocket\lib\WebSocketServer.js:224:14)
at Server.emit (events.js:310:20)
at onParserExecuteCommon (_http_server.js:642:14)
at onParserExecute (_http_server.js:583:3)

D:\ArduinoP1819\arduino-1.8.19\portable\sketchbook\Teensy41_Client>

THX for your time!
 
Hello again!
It's working! With DHCP.
What I did: I verified the node.js version and as I did not know what version you used, I replaced the server script with another one
compatible with my version of node.js.

I want to thank you again for your support and I am interested when the library will support direct IP addressing, so we can
remove the DHCP that is now mandatory. (as I understood from the comments).

Cheers and all the best !
 
I am interested when the library will support direct IP addressing, so we can remove the DHCP that is now mandatory. (as I understood from the comments).

Just change your code to use static IP. Follow the example WebClient

Code:
#include <QNEthernet.h>

using namespace qindesign::network;

// if you don't want to use DNS (and reduce your sketch size)
// use the numeric IP instead of the name for the server:
char server[] = "arduino.cc";  // also change the Host line in httpRequest()
//IPAddress server(100,24,172,113);

#define USING_DHCP    false   //true

#if !USING_DHCP
  // Set the static IP address to use if the DHCP fails to assign
  IPAddress myIP(192, 168, 2, 222);
  IPAddress myNetmask(255, 255, 255, 0);
  IPAddress myGW(192, 168, 2, 1);
  //IPAddress mydnsServer(192, 168, 2, 1);
  IPAddress mydnsServer(8, 8, 8, 8);
#endif

// Initialize the Ethernet client library
// with the IP address and port of the server
// that you want to connect to (port 80 is default for HTTP):
EthernetClient client;

// Variables to measure the speed
unsigned long beginMicros, endMicros;
unsigned long byteCount = 0;
bool printWebData = true;  // set to false for better speed measurement

void setup()
{
  // Open serial communications and wait for port to open:
  Serial.begin(115200);
  while (!Serial);

  Serial.println("\nStarting WebClient using QNEthernet Library");

#if USING_DHCP
  // Start the Ethernet connection, using DHCP
  Serial.print("Initialize Ethernet using DHCP => ");
  Ethernet.begin();
#else   
  // Start the Ethernet connection, using static IP
  Serial.print("Initialize Ethernet using static IP => ");
  Ethernet.begin(myIP, myNetmask, myGW);
  Ethernet.setDNSServerIP(mydnsServer);
#endif

  if (!Ethernet.waitForLocalIP(5000))
  {
    Serial.println("Failed to configure Ethernet");

    if (!Ethernet.linkStatus())
    {
      Serial.println("Ethernet cable is not connected.");
    }

    // Stay here forever
    while (true)
    {
      delay(1);
    }
  }
  else
  {
    Serial.print("IP Address = ");
    Serial.println(Ethernet.localIP());
  }

  // give the Ethernet shield a second to initialize:
  delay(1000);
  Serial.print("Connecting to ");
  Serial.print(server);
  Serial.println("...");

  // if you get a connection, report back via serial:
  if (client.connect(server, 80))
  {
    Serial.print("Connected to ");
    Serial.println(client.remoteIP());

    // Make a HTTP request:
    client.println("GET /asciilogo.txt HTTP/1.1");
    client.println("Host: arduino.cc");
    client.println("Connection: close");
    client.println();
  }
  else
  {
    // if you didn't get a connection to the server:
    Serial.println("Connection failed");
  }

  beginMicros = micros();
}

void loop()
{
  // if there are incoming bytes available
  // from the server, read them and print them:
  int len = client.available();

  if (len > 0)
  {
    byte buffer[80];

    if (len > 80)
      len = 80;

    client.read(buffer, len);

    if (printWebData)
    {
      Serial.write(buffer, len); // show in the serial monitor (slows some boards)
    }

    byteCount = byteCount + len;
  }

  // if the server's disconnected, stop the client:
  if (!client.connected())
  {
    endMicros = micros();
    Serial.println();
    Serial.println("Disconnecting.");
    client.stop();
    Serial.print("Received ");
    Serial.print(byteCount);
    Serial.print(" bytes in ");

    float seconds = (float)(endMicros - beginMicros) / 1000000.0;

    Serial.print(seconds, 4);

    float rate = (float)byteCount / seconds / 1000.0;
    Serial.print(", rate = ");
    Serial.print(rate);
    Serial.print(" kbytes/second");
    Serial.println();

    // Stay here forever
    while (true)
    {
      delay(1);
    }
  }
}

PS: still have the warnings during compilation. Should I be worried about them?

Those are informational warnings to help you know what you're doing (right or wrong). They are intentional put there because the library is supporting too many platforms and boards, so that users and I can know if our settings are correct . Ignore them if everything is OK.

I'm glad that you're doing OK now by figuring the issue yourself, with minimal support.
 
Thank you for your reply.
I am trying to use the websocket client on Teensy4.1 (no wiznet, only ethernet cable kit) but this time without using the additional external DHCP (thanks again - your advice helped to find a working solution with DHCP)
My connection is purely local no internet access.

I have tried and tried but without success to use a direct Ethernet cable connection between the nodejs server resident on my laptop and the Teensy4.1.
My Teensy 4.1 has no wiznet shield, only a standard ethernet connection kit.
I finally noticed that the example was for an Arduino with ethernet shield.

If you have an example with websocket client on Teensy4.1 (no wiznet) communicating via a static IP address to another static IP using a simple ethernet cable
that would be helpful.

I am always stuck here (see below. I used the recommended code). I think it should at least connect to server on port 3000
Starting WebClient using QNEthernet Library
Initialize Ethernet using static IP => IP Address = 192.168.2.222
Connecting to 192.168.2.221...
Connection failed

The server I use already, connects to a local html client and communicates websockets after connection

Code:
#!/usr/bin/env node

/*
https://github.com/Vuka951/tutorial-code/blob/master/js-express-websockets/done/index.js
query: I receive blob instead of message
issue and solution: https://stackoverflow.com/questions/53884481/websocket-sending-blob-object-instead-of-string

*/

const open = require('open');
const express = require('express')
const app = express()
const server = require('http').createServer(app);
//ws is the protocol
const WebSocket = require('ws');
//wss is the server
const wss = new WebSocket.Server({ server:server });

wss.on('connection', function connection(ws) {
  console.log('A new client Connected!');
  ws.send('server says: Welcome New Client!');

  ws.on('message', function incoming(data) {
    console.log('received: %s', data);
    //ws.send("I got ur message. It is: " + message);

    wss.clients.forEach(function each(client) {
      if (client !== ws && client.readyState === WebSocket.OPEN) {
      	//for each client different from the current one owning the current ws 
        //message = "converted message";
        //var message = "";
        //message = data;
        client.send(data.toString());
      }
    }); // forEach
    
  }); // end ws.on
}); // end wss.on

app.get('/', (req, res) => res.send('from server:>>>  Hello World! '))

server.listen(3000, () => console.log(`Server is Listening on port :3000`))
open('file:///D:/test7/newChart6.html', {app: {name:'firefox'}});
Thank you again for your advice!
I would like just to know if it is possible or not yet. I can still use the previous solution with DHCP but static IP would have been a more elegant approach.
 
I loaded the `Teensy41_Client` example and changed:
1. The IP, subnet mask, gateway, and DNS to something suitable,
2. Changed USING_DHCP to false,
3. Commented out the <TeensyID.h> and <SPI.h> includes so the example would compile for my system. (I don't have TeensyID installed.) Note: TeensyID.h isn't needed for QNEthernet unless you want to get the MAC address, and SPI.h isn't used for either Ethernet library.

I then uploaded the example and see this output:
Code:
Starting Teensy41_Client on TEENSY 4.1 using QNEthernet
WebSockets2_Generic v1.10.0
=========== USE_QN_ETHERNET ===========
Initialize Ethernet using static IP => Connected! IP address:192.168.4.150
Couldn't connect to server : ws://192.168.2.30:8080

I can also ping the board. It looks like this works for me.

Update: I just updated the library to v1.10.1 and it still works.
 
Thanks @shawn,

Somehow it's working to connect to other HTTP site using staticIP with WebClient.

It's possible a bug in the WebSockets2_Generic library or somewhere that staticIP doesn't work to connect to WS Server. Using DHCP is OK.

I'm currently busy and can't have time to debug this issue yet. Your help is greatly appreciated as you've investigated and had some PRs for this library.

Update: DHCP OK

Code:
Starting Teensy41_Client on TEENSY 4.1 using QNEthernet
WebSockets2_Generic v1.10.1
=========== USE_QN_ETHERNET ===========
Initialize Ethernet using DHCP => Connected! IP address:192.168.2.107
Connected to server : ws://192.168.2.30:8080
Got Message: Hello Server
 
Last edited:
@khoih-prog What do you see when you change to a static IP? Also, do you have a suggestion for the quickest way to create a WebSockets server (preferably on a Mac)? (I haven't looked much yet...)
 
I'm using the WS Server from websocket

To install, from root or sudo (for Ubuntu, but I guess Mac has similar way). You'd need to install Nodejs and npm first from

1. Nodejs.org download/
2. npm

then install websocket (server / client)

Code:
# npm install websocket

The WS Server code I'm using to test

Code:
#!/usr/bin/env node
var WebSocketServer = require('websocket').server;
var http = require('http');

var server = http.createServer(function(request, response) {
    console.log((new Date()) + ' Received request for ' + request.url);
    response.writeHead(404);
    response.end();
});
server.listen(8080, function() {
    console.log((new Date()) + ' Server is listening on port 8080');
});

wsServer = new WebSocketServer({
    httpServer: server,
    // You should not use autoAcceptConnections for production
    // applications, as it defeats all standard cross-origin protection
    // facilities built into the protocol and the browser.  You should
    // *always* verify the connection's origin and decide whether or not
    // to accept it.
    autoAcceptConnections: false
});

function originIsAllowed(origin) {
  // put logic here to detect whether the specified origin is allowed.
  return true;
}

wsServer.on('request', function(request) {
    
    if (!originIsAllowed(request.origin)) {
      // Make sure we only accept requests from an allowed origin
      request.reject();
      console.log((new Date()) + ' Connection from origin ' + request.origin + ' rejected.');
      return;
    }
    
    //var connection = request.accept('echo-protocol', request.origin);
    var connection = request.accept(null, request.origin);
    console.log((new Date()) + ' Connection accepted.');
    connection.on('message', function(message) {
        if (message.type === 'utf8') {
            console.log('Received Message: ' + message.utf8Data);
            connection.sendUTF(message.utf8Data);
        }
        else if (message.type === 'binary') {
            console.log('Received Binary Message of ' + message.binaryData.length + ' bytes');
            connection.sendBytes(message.binaryData);
        }
    });
    connection.on('close', function(reasonCode, description) {
        console.log((new Date()) + ' Peer ' + connection.remoteAddress + ' disconnected.');
    });
});
 
Back
Top