Tiny Teensy UDP Performance Experiment (2021-12-18)
Hackaday gets it and also notes the IEEE 1588 precision time protocol, which I should check out, as it does some clock sync.
I don't have an ethernet port on this laptop, so I'm setting up the other end of this experiment on the PC... code seems simple enough so far... though I do want the Serial printing...
OK there's a basic teensy sketch that I have running, now a little npm udp...
I will admit I'm a little confused about the actual ethernet routing... I have this plugged right into the computer's port, it might need to be hooked up to my local net.
I think I need some DHCP / more advanced net config knowledge to setup the teensy's IP.
Yep, I was trying to setup as static, that's borked. I suspect, as is the case with USB, we will have to do some manouvering for auto-establishing connections...
Alright I can DHCP it and ping it, so UDP now. This is running over my wireless net at home (pc -> wifi -> locallan -> teensy) so I am seeing ping times around ~ 18ms, whereas this dude sees ~ 650us RTT and 96mbps.
Alright I got one packet down; I'm just setting the IP manually, it seems to DHCP to the same place each time. Like I said, real net config later...
Bounced it back, now a little ring loop in JS.
And I get about 18.5ms RTT, same as the ping - so that at least is a good result. But this is on my local wifi w/ a few computers on the net, so pushing performance probably means some dedicated wires.
Here's the ring-and-return code,
#include <Arduino.h>
#include <NativeEthernet.h>
#include <NativeEthernetUdp.h>
// coordinates
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
IPAddress ip(192, 168, 1, 177);
unsigned int localPort = 8888;
// todo: teensys have a MAC written into the fuse, s/o paul, u r the goat
// this forum post: https://forum.pjrc.com/threads/62932-Teensy-4-1-MAC-Address
// suggests we can retrieve it with
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]);
}
// buffers
char packetBuffer[UDP_TX_PACKET_MAX_SIZE];
char replyBuffer[] = "acknowledged";
// instance
EthernetUDP UDP;
void setup() {
pinMode(13, OUTPUT);
digitalWrite(13, LOW);
// setup debug line
Serial.begin(9600);
while(!Serial); // waits until serial connection established
Serial.println("hello serial");
//try DHCSP
if(Ethernet.begin(mac) == 0){
Serial.println("DHCP failed");
} else {
UDP.begin(localPort);
Serial.print("alive on: ");
Serial.print(Ethernet.localIP());
Serial.print(":");
Serial.println(localPort);
}
}
unsigned long lastTick = 0;
void loop() {
// read packets if we can
int pckLen = UDP.parsePacket();
if(pckLen){
//Serial.print("rx'd pck w/ len ");
//Serial.println(pckLen);
// lettuce reply
UDP.beginPacket(UDP.remoteIP(), UDP.remotePort());
UDP.write(replyBuffer);
UDP.endPacket();
}
if(millis() > lastTick + 250){
lastTick = millis();
digitalWrite(13, !digitalRead(13));
}
}
import dgram from 'dgram'
import performance from 'perf_hooks'
let timestamp = () => {
return performance.performance.now()
}
// ahn dgram (node's UDP object) socket
const sckt = dgram.createSocket('udp4')
sckt.on('error', (err) => {
console.log(`sckt error:\n${err.stack}`)
sckt.close()
})
sckt.on('message', (msg, rinfo) => {
count ++
if(count > iter){
let time = timestamp() - startTime
console.log(`averate rtt ${time / iter}`)
} else {
txPck()
}
})
sckt.on('listening', () => {
const addr = sckt.address();
console.log(`sckt listening on ${addr.address}:${addr.port}`)
// send something (?)
setTimeout(() => {
console.log(`begin...`)
startTime = timestamp()
txPck()
}, 1000)
})
sckt.bind(8040);
let txPck = () => {
return new Promise((resolve, reject) => {
sckt.send("hello-udp", 8888, "130.44.148.111", (cb) => {
resolve()
})
})
}
let startTime = 0
let count = 0
let iter = 1000
So that about concludes the experiment for today: this stuff works, the teensy is rad, and I should probably look to this for future systems... ethernet hookup is maybe not that valuable if we anyways move to python systems, but it's great to know this is available.
Conclusions
- 18.5ms RTT is not that impressive, but was tested w/ wifi link
- others have achieved 650us RTT, which is impressive, so better LAN config is worth experimenting with
- ethernet hookup can anyways eliminate the use of local servers, for zero-user-install systems
- easy hookup probably means some routine which scans for MAC addresses, luckily teensies have these baked in... we want some slick codes that do this ibid the methods we use for usb port auto-connections...
- however, more interesting performance is in the 960MHz cpu clock, which might allow for really high perf. lookahead & loop closing, should chase after that first... likewise moving virtual machines / osap into python codes
Next
- we can overclock the teensy close to 960 MHz, try that out
- flops math for teensy fixed-horizon lookahead...
- match on mac addresses for port lookup routines
- is udp / ethernet worth it, or should we just focus on lookahead & i.e. faster python serial link ?
- nice in the future to hit embedded endpoints straight from the browser, no user-side install