TCP & UDP Server on Teensy 4.1

Alex4455

Active member
Greetings, dear ones.

Please tell a beginner how to adopt this code on Teensy 4.1 ?
with Arduino NANO and W5500, it works very well, but there were not enough free pins,
I've been fighting for two days, nothing works, please help!
I looked at the QNEthernet library, but for me it is very difficult to understand.

Code:
#include <Ethernet.h>

#define UDP_Port 5007  // UDP Port
EthernetUDP udpServer;
byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};
EthernetServer tcpServer(5005); // TCP Port
boolean alreadyConnected = false; // regardless of whether the client was connected earlier or not
char packetBufferTCP[256];
byte len = 0;

void setup() {
  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
    }
  }
  while (Ethernet.linkStatus() == LinkOFF) {
    Serial.println("Ethernet cable is not connected!");
    for (;;); // There's no point in continuing, we're not doing anything else
  }
  Serial.println("Start...");
  if (Ethernet.begin(mac) == 0) {
    Serial.println("Failed to configure Ethernet using DHCP!");
    for (;;); // There's no point in continuing, we're not doing anything else
  }
  Ethernet.begin(mac, Ethernet.localIP());  // Launching Ethernet and UDP
  Serial.print("Connected, local IP address: ");
  Serial.println(Ethernet.localIP());
  udpServer.begin(UDP_Port);
  tcpServer.begin();
}

void loop() {
  uint8_t packetSize = udpServer.parsePacket(); // if there is available data, we read the package
  EthernetClient tcpClient = tcpServer.available(); // we are waiting for a new client
  // when the client sends the first byte
  if (tcpClient) {
    tcpClient.flush(); // clear the input buffer:
    if (tcpClient.available() > 0) { // we read the bytes coming from the client
      len = tcpClient.read(packetBufferTCP, 256); // reading the packet and transferring it to the buffer
    }
  }
  //...
  //...
} //===== End Loop =====
 
Last edited:
Can confirm the QNEthernet BroadcastChat works well with the T_4.1's onboard ethernet PHY using the Magjack adapter. It uses UDP broadcast not TCP ...

Set up three of them and modified code to send messages the other receive for days unattended on local network - also works for messages typed and sent.

Key change for preventing message loss when blasting groups of 30 was this " (32) " setting up larger number of packet buffers:
Code:
// UDP port.
EthernetUDP udp(32);  // (##)-packet buffer allocate for incoming messages
 
I figured it out more or less, did it this way, I don't know how correctly, but it works as I need it!
This is only TCP, I think it will be easier with UDP, in my device, reception is over TCP, transmission is over UDP,
evaluate the code, point out the errors, and another question, can I make a fixed IP address for Teensy 4.1,
i.e. that it would always be the same ?
 

Attachments

  • TCP.ino
    1.4 KB · Views: 115
@Alex4455: p#5 code worked on T_4.1 here

Using notes here : \libraries\QNEthernet\examples\AppWithListenersTemplate\AppWithListenersTemplate.ino

This is working TCP with static IP - may need work to be 'right':
Code:
#include <QNEthernet.h>
using namespace qindesign::network;
constexpr bool kStartWithDHCP = false;
IPAddress staticIP{192, 168, 0, 162};//{192, 168, 1, 101};
IPAddress subnetMask{255, 255, 255, 0};
IPAddress gateway{192, 168, 0, 1};
IPAddress dnsServer = gateway;
// The link timeout, in milliseconds. Set to zero to not wait and
// instead rely on the listener to inform us of a link.
constexpr uint32_t kLinkTimeout = 5000;  // 5 seconds

EthernetServer server(5005);       // telnet by default, the port is used
boolean alreadyConnected = false;  // regardless of whether the client was connected earlier or not

void setup() {
  Serial.begin(115200);
  while (!Serial && millis() < 10000 ) ; // wait for serial monitor
  Serial.println("\n" __FILE__ " " __DATE__ " " __TIME__);
  if ( CrashReport ) Serial.print( CrashReport );

  if ( !kStartWithDHCP ) {
    Serial.printf("Starting Ethernet with static IP...\r\n");
    Ethernet.setDNSServerIP(dnsServer);  // Set first so that the
    // listener sees it
    Ethernet.begin(staticIP, subnetMask, gateway);

    // When setting a static IP, the address is changed immediately,
    // but the link may not be up; optionally wait for the link here
    if (kLinkTimeout > 0) {
      if (!Ethernet.waitForLink(kLinkTimeout)) {
        printf("Warning: No link detected\r\n");
        // We may still see a link later, after the timeout, so
        // continue instead of returning
      }
      else {
        IPAddress ip = Ethernet.localIP();
        if (ip != INADDR_NONE) {
          IPAddress subnet = Ethernet.subnetMask();
          Serial.printf("Local IP: %u.%u.%u.%u\r\n", ip[0], ip[1], ip[2], ip[3], subnet[0], subnet[1], subnet[2], subnet[3]);
        }
      }
    }
  }
  else {
    Serial.printf("Starting Ethernet with DHCP...\r\n"); // Initialize Ethernet, in this case with DHCP
    if (!Ethernet.begin()) {
      Serial.printf("Failed to start Ethernet\r\n");
      return;
    }
  }
  Ethernet.onLinkState([](bool state) {
    Serial.printf("Link %s\r\n", state ? "ON" : "OFF");
  }); // setup callback
  Ethernet.onAddressChanged([]() {
    IPAddress ip = Ethernet.localIP();
    bool hasIP = (ip != INADDR_NONE);
    if (hasIP) {
      IPAddress subnet = Ethernet.subnetMask();
      Serial.printf("Local IP: %u.%u.%u.%u\r\n", ip[0], ip[1], ip[2], ip[3], subnet[0], subnet[1], subnet[2], subnet[3]);
    } else {
      Serial.printf("Address changed: No IP address\r\n");
    }
  }); // setup callback
  //Udp.begin(localPort);
  server.begin();  // start listening to customers
}

void loop() {
  EthernetClient client = server.available();  // we are waiting for a new client:
  if (client) {
    int iCnt = client.available();
    Serial.printf( "\n>>>>>\tclient.available chars = %d\n", iCnt );
    while (client.available() > 0) {  // reads bytes coming from the client:
      char thisChar = client.read();
      server.write(thisChar);  // pass the bytes back to the client:
      Serial.write(thisChar);
    }
    Serial.printf( "\n\tclient.available chars = %d\t<<<<<\n", iCnt );
  }
}

<EDIT> Code Edited noted below
 
Last edited:
defragster, I am very grateful to you for your clarifications and help! you are the right person!

if I may ask one more question, how do I find out the number of accepted characters?
for example, the word "Hello" has 5 characters, how to display the number 5 ?
 
defragster, I am very grateful to you for your clarifications and help! you are the right person!

if I may ask one more question, how do I find out the number of accepted characters?
for example, the word "Hello" has 5 characters, how to display the number 5 ?

This part in loop() looks to pass characters in a way that could be counted where it reads and writes then one at a time. and assume 'client.available()' may return the actual count remaining?:
Code:
void loop() {
  EthernetClient client = server.available();  // we are waiting for a new client:
  if (client) {
    if (client.available() > 0) {  // reads bytes coming from the client:
      char thisChar = client.read();
      server.write(thisChar);  // pass the bytes back to the client:
      Serial.write(thisChar);
    }

Small note: No need to call `Ethernet.begin()` twice for DHCP.

Opps - I see that 2nd .begin() now - just cut and pasted from the noted example until it worked - and then had to log off.


Now the question is if that code can run in parallel with the UDP Chat code?
 
p#6 code edited.

> Removed extra .begin copied from other example
> The loop() code processes one packet at a time - now prints with while()
> Shows number of bytes in packet before and after
> Added print of IP used in static case
> Max bytes per packet :: >>>>> client.available chars = 1460
> using packetsender.com/ for PC client, sending file limited to 10,922 chars.

Code:
C:\Users\TimLabs\Downloads\TCP\TCP.ino Jul 18 2023 14:35:44
Starting Ethernet with static IP...
[B]Local IP: 192.168.0.162[/B]

[B]>>>>>	client.available chars = 16[/B]
testing 1 2 3 4!
[B]	client.available chars = 16	<<<<<[/B]

>>>>>	client.available chars = 16
testing 1 2 3 4!
	client.available chars = 16	<<<<<

>>>>>	client.available chars = 16
testing 1 2 3 4!
	client.available chars = 16	<<<<<

>>>>>	client.available chars = 16
testing 1 2 3 4!
	client.available chars = 16	<<<<<

[B]>>>>>	client.available chars = 439[/B]
#include <QNEthernet.h>
using namespace qindesign::network;
constexpr bool kStartWithDHCP = false;
IPAddress staticIP{192, 168, 0, 162};//{192, 168, 1, 101};
IPAddress subnetMask{255, 255, 255, 0};
IPAddress gateway{192, 168, 0, 1};
IPAddress dnsServer = gateway;
// The link timeout, in milliseconds. Set to zero to not wait and
// instead rely on the listener to inform us of a link.
constexpr uint32_t kLinkTimeout = 5000;  // 5 seconds
[B]	client.available chars = 439	<<<<<[/B]
 
Failed to make a transfer over UDP, please help!
I need to transmit three comma-separated variables over UDP, adc1, adc2, adc3
Transmission frequency 25 ms
In arduino I do this:
Code:
udpServer.beginPacket(udpServer.remoteIP(), udpServer.remotePort());
udpServer.print(String(adc1) + "," + adc2 + "," + adc3);
udpServer.endPacket();
How to do the same in Teensy 4.1?
I tried with this command, it didn't work out..
Code:
udp.send(Ethernet.broadcastIP(), PortUDP, ???, ???);
 
Smashed two sketches together: github.com/Defragster/TeensySketches/tree/main/TCP_UDPstaticIP

The above p#6 TCP code and UDP Broadcast Chat example tested before.

It will create a static IP of 192.168.0[ .150 + last MAC byte %15 ] : done because running chat takes 2 or more T_4.1's running the same code and this makes three here come up as [ .156 and .160 and .162 ]

The three UDP Chat to each other, and 'Packet Sender' delivers messages to each one by IP.

by SMASHED it is meant as 'ingloriously' UDP sketch pasted to the end of the TCP sketch and: TCP:: setup() calls renamed UDPsetup() and then TCP:: loop() calls renamed UDPloop();
> wasn't sure what it would take for TCP && UDP to coexist, so did the minimal to test.
> minor edits for making a unique IP when all three here run the same code.
> the UDP sketch HACKED and does some periodic UDP message sends: for (int ii = 0; ii < 29; ii++) { UDPdbg.printf( ...
> That .printf uses a STREAM class hacked from @KurtE to perform: udp.send(Ethernet.broadcastIP(), kPort, reinterpret_cast<const uint8_t *>(szOut), cnt);

Which could be handy for use as desired: UDPdbg.printf( "%d,%d,%d\n", adc1, adc2, adc3 );
 
Last edited:
defragster, thank you for your answer and help! But unfortunately it is very difficult for me to understand ((
could you just show me how to send this command ?
Code:
udp.send(Ethernet.broadcastIP(), PortUDP, ???, ???);
 
defragster, thank you for your answer and help! But unfortunately it is very difficult for me to understand ((

Not sure it is understood here either - I just added the TCP code to the included Chat example in QNEthernet that was modified for testing fun.

The fun stuff adds some code for sure {STREAM for print and the looping and counting of messages} - it might paste together from the original Example to its original clean/simple state like this one did.

Perhaps start trying that and if you get stuck and post the code I can assist.
 
i solved the problem! This example helped:
Code:
#include "QNEthernet.h"
#include <TimerOne.h>

namespace qn = qindesign::network;

qn::EthernetUDP udp(100);

IPAddress ip{192, 168, 1, 254};
IPAddress mask{255, 255, 255, 0};
IPAddress gateway{192, 168, 1, 1};
IPAddress broadcast_ip;

uint8_t buf[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};

int port = 2020;

void Send() {
  Serial.println("SENDING");
  udp.send(broadcast_ip, port, buf, sizeof(buf));
}

void setup() {
  Serial.begin(115200);
  while (!Serial) {}
  if (!qn::Ethernet.begin(ip, mask, gateway)) {
    Serial.println("ERROR: unable to start ethernet interface");
    while (1) {}
  }
  broadcast_ip = qn::Ethernet.broadcastIP();
  Serial.println("START");
  Timer1.initialize(10000);
  Timer1.attachInterrupt(Send);
}

void loop() {}
 
Last edited:
Great. That will send UDP - read that TCP was also needed?
basic commands are transmitted via TCP, such as turn on, turn off, switch relays and other operations,
only control data is transmitted over UDP, the data transmission period is 25 ms, so if several
packets will be lost, it's not scary ..
 
The QNEthernet library doesn’t support being called from an interrupt. There are a few ways to solve this. One is to set a flag in the interrupt and then in the main loop, check that flag and perform the required action. The `Send` function in the code is being called from within the Timer1 interrupt. Another way is to use, say, `elapsedMillis` to check for the timeout (or `millis()`), again, via the main loop.

Note that when I say "main loop" here, it doesn’t have to actually be in `loop()`, just in something that isn’t called via an interrupt function (or something called concurrently, like using multiple threads).

Example: Flag from interrupt

In the interrupt function:
Code:
// Should really be atomic; good enough for this demo
volatile bool timerFlag = false;

void Send() {
  timerFlag = true;
}

Via the main loop:
Code:
void loop() {
  if (timerFlag)
    timerFlag = false;
    Serial.println("SENDING");
    udp.send(broadcast_ip, port, buf, sizeof(buf));
  }
}

Example: Watching a timer

Via the main loop (and with no Timer1):
Code:
elapsedMillis timer;

void loop() {
  if (timer >= 10000) {  // <-- 10 seconds
    Serial.println("SENDING");
    udp.send(broadcast_ip, port, buf, sizeof(buf));
    timer = 0;
  }
}

A note on timing

I wouldn't rely on exact timing of packets, so sending exactly every, say half a second, isn't important. Networks have jitter and they're unreliable.
 
Example: Watching a timer
Via the main loop (and with no Timer1):
Code:
elapsedMillis timer;

void loop() {
  if (timer >= 10000) {  // <-- 10 seconds
    Serial.println("SENDING");
    udp.send(broadcast_ip, port, buf, sizeof(buf));
    timer = 0;
  }
}
That's exactly what I did and it works...
but it also works and Millis.

Now a new question, how to correctly add three variables separated by a comma ?
 
Last edited:
Back
Top