4

2017-03-23 update

I still don't know why it works so well on Yún. Maybe it has a better antenna? Anyway, I've returned Uno WiFi and got a SparkFun ESP32 Thing. Unfortunately, it has the same problem as Uno WiFi. To fix it I've switched from TCP to UDP. It made a world of difference: servos are very responsive now. UDP is OK for my use case: I can send the state of the whole system in each message and lost messages or messages arriving out of order are not a big problem.

Original question

I want to control servos over WiFi with high precision and low latency. I managed to achieve this using TCP socket communication with Arduino Yún. I want to repeat this with Arduino Uno WiFi, but I can't find example code for socket communication. I connected to the board using telnet but servo output is jerky — as if there was a delay after each servo.write call.

How can I run a TCP socket server on Arduino Uno WiFi in a way that allows for fast low latency communication?

Longer description

I experimented with controlling servos over WiFi using an Arduino Yún board that I borrowed from a friend. I connected a MG92B servo to pin 9, 5V, and GND. I start a TCP socket server on the Arduino and send angle values from a Python script dozens of times per second. It works smoothly.

Encouraged by this experiment I bought my own Arduino. I chose Arduino Uno WiFi (developer edition) because, unlike Yún, it's compatible with shields. Unfortunately I can't find example code for socket communication. I found an example that uses telnet, so I adapted it and used it with my Python script. Sadly, the servo movement is jerky — as if there was a delay after each servo.write call.

Below is a video showing both boards running two sketches: "no network movement" and "network movement".

Video

Code

I attach all code I used.

Arduino oscillation test with no remote control

I use this code to test that the servo is working correctly. It works on both Arduino Yún and Arduino Uno WiFi.

#include <Servo.h>


const int servoPin = 9;
Servo servo;


void setup() {
    servo.attach(servoPin);
}


void loop() {
    // configure movement
    int angleMin = 15;
    int angleMax = 165;
    int interval = 30;

    // oscillate between `angle_min` and `angle_max`
    int angle = angleMin;
    for (int i = 0; i < 2; i = (i + 1) % 2) {
        int sign = pow(-1, i);
        for (int j = 0; j < angleMax - angleMin; j += 1) {
            angle += sign;
            servo.write(angle);
            delay(interval);
        }
    }
}

Arduino Uno WiFi reading from telnet

This is jerky.

#include <Servo.h>
#include <UnoWiFiDevEd.h>


const int servoPin = 9;
Servo servo;


void setup() {
    Wifi.begin();
    servo.attach(servoPin);
}


void loop() {
    while (Wifi.available()) {
        servo.write(Wifi.read());
    }
}

Arduino Yún reading from socket

This is smooth.

#include <Bridge.h>
#include <Servo.h>
#include <YunClient.h>
#include <YunServer.h>


// configure
const int port = 5555;
const int servoPin = 9;

// listen on given port
Servo servo;
YunServer server(port);


void setup() {
    // set up servo
    servo.attach(servoPin);

    // start server
    Bridge.begin();
    server.noListenOnLocalhost();
    server.begin();
}


void loop() {
    YunClient client = server.accept();
    byte angle;

    if (client) {
        while (client.connected()) {
            angle = client.read();
            if (angle != 255) {
                servo.write(angle);
            }
        }

        client.stop();
    }

    Serial.println("waiting for client");
    delay(1000);
}

Python writing to socket

This is the network equivalent of the Arduino oscillation test. Works both with Yún (port 5555) and Uno (port 23).

import socket
from time import sleep


# connect
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect(('192.168.240.1', 5555))  # use 5555 for Yun and 23 for Uno

# configure movement
angle_min = 15
angle_max = 165
interval = 30 / 1e3

# oscillate between `angle_min` and `angle_max`
while True:
    angle = angle_min
    for sign in [1, -1]:
        for _ in range(angle_max - angle_min):
            angle += sign
            client.send(bytearray([angle]))
            sleep(interval)
1

I suspect these delays are caused by TCP buffering.

For some theory on this sort of thing, perhaps read about Nagle's Algorithm which covers some of the issues with small packet buffering. It seems reasonable to me that lower-power and/or lower-speed (or even "just different") boards would use more aggressive buffering.

TCP uses all manner of buffering and checks to ensure guaranteed delivery of data (or reported error), whereas UDP does not.

If UDP is working for you, perhaps you may want to include some of your own error tracking in the comms protocol, unless missing the odd packet is not an issue to your control system.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.