Teensy 4.1 high rate websocket communication

Status
Not open for further replies.

Vincent Dw

New member
Hi everyone,

I am building a showlaser for my graduation assignment. I use the teensy 4.1 as the controller in the laser and my pc to send commands to the teensy 4.1 by a websockets connection. I succeeded in setting up the communication, but there is one thing wrong. When i send like 5000 messages in a for loop the Teensy crashes and the connection is lost. If i send 50 messages to the teensy the teensy does not crash and the connection remains.

My goal is to send around 30000 messages per second, with a stable connection. The message is a string and looks like this "x:-4000", "r:255". Are websockets suited for this use case or are their better ways to accomplish this? I would really like to use the ethernet connection since i had problems with serial being too laggy for the laser in the previous showlaser version that i build.


Below is the code i used for the teensy.
The teensy code is the example sketch found in Websockets2_Generic -> Generic -> Teensy41_NativeEthernet -> MultipleClients_Teensy41_Server.

Code:
/****************************************************************************************************************************
  MultiClients_Teensy41_Server.ino
  For Teensy 4.1 with NativeEthernet.

  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 Server and Http Server (using NativeEthernet).
  Combining the Teensy41-Server example with the NativeEthernet WebServer
  example (https://github.com/vjmuzik/NativeEthernet/blob/master/examples/WebServer/WebServer.ino).

  This sketch:
  1. Connects to a ethernet network
  2. Starts a websocket server on port 80
  3. Waits for connections
  4. As soon as a client wants to establish a connection, it checks whether a
     free slot is available and accepts it accordingly
  5. If the client is accepted it sends a welcome message and echoes any
     messages from the client
  6. Goes back to step 3

  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)
    NativeEthernet (https://github.com/vjmuzik/NativeEthernet)

  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(__IMXRT1062__) && defined(ARDUINO_TEENSY41))
  #error This is designed only for Teensy 4.1. Please check your Tools-> Boards
#endif

#define WEBSOCKETS_USE_ETHERNET     true
#define USE_NATIVE_ETHERNET         true

#include <WebSockets2_Generic.h>

using namespace websockets2_generic;

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

// Enter websockets server port.
const uint16_t port = 81;

// Define how many clients we accpet simultaneously.
const byte maxClients = 4;

WebsocketsClient clients[maxClients];
WebsocketsServer server;

void teensyMAC(uint8_t *mac) {
    for(uint8_t by=0; by<2; by++) mac[by]=(HW_OCOTP_MAC1 >> ((1-by)*8)) & 0xFF;
    for(uint8_t by=0; by<4; by++) mac[by+2]=(HW_OCOTP_MAC0 >> ((3-by)*8)) & 0xFF;
    Serial.printf("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
}

void setup()
{
  // Set the MAC address.
  teensyMAC(mac);

  // Start Serial and wait until it is ready.
  Serial.begin(115200);
  while (!Serial);

  Serial.println("\nStart MultiClients_Teensy41_Server on Teensy 4.1");
  Serial.println(WEBSOCKETS2_GENERIC_VERSION);

  // Connect to ethernet.
  if (Ethernet.begin(mac))
  {
    Serial.println("Ethernet connected");
  }
  else
  {
    Serial.println("Ethernet failed");
  }

  // Start websockets server.
  server.listen(port);

  if (server.available())
  {
    Serial.print("Server available at ws://");
    Serial.print(Ethernet.localIP());
    // Also log any non default port.
    if (port != 80)
      Serial.printf(":%d", port);

    Serial.println();
  }
  else
  {
    Serial.println("Server not available!");
  }
}

void handleMessage(WebsocketsClient &client, WebsocketsMessage message)
{
  auto data = message.data();
  Serial.println("Data: " + data);
}

void handleEvent(WebsocketsClient &client, WebsocketsEvent event, String data)
{
  if (event == WebsocketsEvent::ConnectionClosed)
  {
    Serial.println("Connection closed");
  }
}

int8_t getFreeClientIndex()
{
  // If a client in our list is not available, it's connection is closed and we
  // can use it for a new client.
  for (byte i = 0; i < maxClients; i++)
  {
    if (!clients[i].available())
      return i;
  }

  return -1;
}

void listenForClients()
{
  if (server.poll())
  {
    int8_t freeIndex = getFreeClientIndex();

    if (freeIndex >= 0)
    {
      WebsocketsClient newClient = server.accept();
      newClient.onMessage(handleMessage);
      newClient.onEvent(handleEvent);
      clients[freeIndex] = newClient;
    }
  }
}

void pollClients()
{
  for (byte i = 0; i < maxClients; i++)
  {
    clients[i].poll();
  }
}

void loop()
{
  listenForClients();
  pollClients();
}



This is the c# code i use on the computer.

Code:
for (int i = 0; i < 50; i++)
            {
                await _laserConnection.SendMessage($"x:{new Random(Guid.NewGuid().GetHashCode()).Next(-4000, 4000)}");
            }

The sendMessage method

Code:
public async Task SendMessage(string message)
        {
            var rcvBytes = Encoding.ASCII.GetBytes(message);
            var rcvBuffer = new ArraySegment<byte>(rcvBytes);
            bool messageSend = false;
            int connectionAttempts = 0;

            while (!messageSend)
            {
                connectionAttempts++;
                if (connectionAttempts > 1)
                {
                    Console.WriteLine(connectionAttempts);
                }

                try
                {
                    await _clientWebSocket.SendAsync(rcvBuffer, WebSocketMessageType.Binary, true, CancellationToken.None);
                    messageSend = true;
                }
                catch (Exception)
                {
                    await Connect();
                }
            }
        }
 
I’m curious why websockets would be better than a regular socket here. Seems like a little more overhead. According to http://eng.kifi.com/websockets-vs-regular-sockets/, websockets are for browser-to-server communication when you can’t do regular sockets.

Of course, you may prefer the websockets API. I just think regular TCP might be faster.

I’m also curious if the same code would fail using the QNEthernet library. There’s NativeEthernet and QNEthernet as the two options (that I know of) for Arduino-style libraries. (Setup is a little different, however.) That will allow you to rule out either your code or the library.

Regarding serial speed, what baud rate were you using? Did you try with other protocols too, eg. SPI?
 
Last edited:
Thank you

Thank you for suggesting sockets! I wrote an implementation and it is so much faster! I can send around 200000 messages per second :). This is way more than i need and it is very stable, no more crashing of the Teensy. You made me very happy thank you!

Teensy code
Code:
/*
  Chat Server

  A simple server that distributes any incoming messages to all
  connected clients.  To use, telnet to your device's IP address and type.
  You can see the client's input in the serial monitor as well.
  Using an Arduino Wiznet Ethernet shield.

  Circuit:
   Ethernet shield attached to pins 10, 11, 12, 13

  created 18 Dec 2009
  by David A. Mellis
  modified 9 Apr 2012
  by Tom Igoe

*/

#include <SPI.h>
#include <NativeEthernet.h>

// Enter a MAC address and IP address for your controller below.
// The IP address will be dependent on your local network.
// gateway and subnet are optional:
byte mac[6];

IPAddress ip(192, 168, 1, 177);

EthernetServer server(80);
boolean alreadyConnected = false; // whether or not the client was connected previously

void teensyMAC(uint8_t *mac) {
  for (uint8_t by = 0; by < 2; by++) mac[by] = (HW_OCOTP_MAC1 >> ((1 - by) * 8)) & 0xFF;
  for (uint8_t by = 0; by < 4; by++) mac[by + 2] = (HW_OCOTP_MAC0 >> ((3 - by) * 8)) & 0xFF;
  Serial.printf("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
}

void setup() {
  // Set the MAC address.
  teensyMAC(mac);
  Ethernet.begin(mac, ip);

  // Open serial communications and wait for port to open:
  Serial.begin(115200);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for native USB port only
  }

  // Check for Ethernet hardware present
  if (Ethernet.hardwareStatus() == EthernetNoHardware) {
    Serial.println("Ethernet shield was not found.  Sorry, can't run without hardware. :(");
    while (true) {
      delay(1); // do nothing, no point running without Ethernet hardware
    }
  }
  if (Ethernet.linkStatus() == LinkOFF) {
    Serial.println("Ethernet cable is not connected.");
  }

  // start listening for clients
  server.begin();

  Serial.print("Chat server address:");
  Serial.println(Ethernet.localIP());
}

void loop() {
  // wait for a new client:
  EthernetClient client = server.available();

  // when the client sends the first byte, say hello:
  if (client) {
    while (client.connected()) {

      while (client.available() > 0) {
        char c = client.read();
        Serial.write(c);
      }
    }

    client.stop();
    Serial.println("Client disconnected");
  }
}

C# code
Code:
public class LaserConnection
    {
        private Socket _socket;

        public LaserConnection()
        {
            var t = Task.Run(async () => await Connect());
            t.Wait();
        }

        private async Task Connect()
        {
            IPAddress address = IPAddress.Parse("192.168.1.177");
            IPEndPoint remoteEP = new IPEndPoint(address, 80);

            _socket = new Socket(address.AddressFamily,
                SocketType.Stream, ProtocolType.Tcp);

            await _socket.ConnectAsync(remoteEP);
        }
        
        public void Disconnect()
        {
            _socket.Shutdown(SocketShutdown.Both);
            _socket.Close();
        }

        public void SendMessage(string message)
        {
            try
            {
                byte[] msg = Encoding.ASCII.GetBytes(message);
                _socket.Send(msg);
            }
            catch (Exception)
            {
                Task.Run(async () => await Connect()).Wait();
            }
        }
 
Status
Not open for further replies.
Back
Top