udpsend Not Working With Multiple Ethernet Devices

Amina Kirby's icon

Hi all,

I am sending UDP from Max to two microcontrollers simultaneously over a network. I had been accomplishing this over WiFi using two NodeMCU ES-12 ESP8266 boards as the microcontrollers. This worked fine, albeit sometimes it was a little laggy.

I recently switched out the ESP8266's for two Arduino Nano's, each attached to an Ethernet shield. My plan was to ditch the WiFi and instead send UDP with everything connected through my router. However, when I send UDP to both devices simultaneously, it seems like only one receives it's message and the other chokes.

Is there anything I'm missing? I don't understand why sending to both devices worked fine over WiFi but it doesn't seem to be happy over Ethernet.

Both Arduino Nano's are each set to their own static IP address and port. I'm using Max 8, a PC with Windows 7, and a Netgear R6230 as the router.

Any suggestions or input is much appreciated!

Thanks,
Amina

Amina Kirby's icon

Whoop- please disregard! I resolved the issue- I had to change the MAC address for each Nano. They were both on the same address before.

:)

Ramon's icon

I know you've solved your problem, but I was wondering if you could send me more information about your original setup. I've been trying to figure out how to control some servos over wifi, and I have a couple ESP8266 boards.

I thought the Ableton Connection kit might work, and I've uploaded StandardFirmataWifi to the board, but it's not registering in the selection menu.

I'd be happy to get things working in a Max standalone project.

Amina Kirby's icon

Hi Ramon- I'd be happy to help troubleshoot. Could you share the code you've uploaded to the ESP8266? What model of ESP8266 board are you using specifically?

I haven't checked out the Ableton Connection kit yet, but if you're just wanting to send control messages to some servos over WiFi, a fairly simple patch in standalone Max should be able to accomplish that paired with the right code on the ESP8266.

Ramon's icon

Thanks!
The Connection Kit works great on an Uno with StandardFirmata, so I though I'd try out the built-in StandardFirmataWiFi in the Arduino IDE. It connects to Wifi just fine, but I'm not entirely sure what I'm dealing with in that sketch - mostly, how to even connect to it from MacOS. It's an ESP-12E board.
Most of the searching I've done results in a bunch of technical jargon, and I'm definitely more of a tinkerer.

For my purposes, the timing is not super important, so long as it is in the ball park. I'm planning on mounting a couple of pan-tilt camera heads that can be automated by Ableton Live. I can definitely get it done with all wired connections, but I'd rather go wireless.
One other option I have is to have one ESP8266 hooked up to the computer and another hooked up to the servos. I'm not sure if that makes thing easier or not.

A bit more background on my attempts, I got a HC-06 and an UNO to control the servos from Pictoblox, but the Connection Kit doesn't work in this case either. I can connect to the UNO via Bluetooth, but the servos jump from 0 to 180 with nothing in between.

Amina Kirby's icon

Hi Ramon-

I made a Max patch and some code for the ESP-12E that is at least getting integer values into the ESP-12E and then printing them to the serial port- though I don't have any servos handy to fully test it out. The example I included below demonstrates controlling two servos with one ESP-12E through Max.

One issue with communicating to the ESP-12E from the Connection Kit in Ableton is that I don't believe the ESP-12E will be able to read OSC messages- or at least I haven't had any luck with it thus far. The udpsend and udpreceive objects in Max also package data as OSC, so I've instead been using the sadam.udpSender object from the Sadam library to send raw UDP to the ESP-12E.

Give it a try and see if it works?

#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <WiFiUdp.h>
#include <Servo.h>

#ifndef STASSID
#define STASSID "NETWORK_NAME_HERE"                      //Enter your network name here.
#define STAPSK  "NETWORK_PASSWORD_HERE"                  //Enter your network password here.
#endif
WiFiUDP Udp;
IPAddress ip(10, 0, 0, 20);                              //Set a static IP address for the ESP-12E.
IPAddress gateway(10, 0, 0, 1);                          //The IP address of  your router/modem.
IPAddress subnet(255, 255, 255, 0);
unsigned int localPort = 6000;                           //Port ESP-12E will be receiving messages on.
char packetBuffer[UDP_TX_PACKET_MAX_SIZE + 1];           //buffer to hold incoming packet
int UDP_DATA_AVAILABLE;                                  //Used to write serial data only if it is available.
int LED_ON;                                              //Enabling/disabling built-in LED.

int TIME;                                                //Used for blinking the ESP-12E LED any time a UDP message is received.
int PASTTIME;

Servo servo[1];                                          //Declares 2 servo objects.

int Servo_Label[] = {'a', 'b'};                          //One label for each servo. These will be the symbols prepended to the value of each servo in Max.
int Servo_Value[] = {0, 0};                              //Integer to store the value of each servo.

void UDP_COLLECT(){                                      //Waits for incoming UDP messages. Upon receiving, reads UDP packet into a buffer.
  int packetSize = Udp.parsePacket();
  if(packetSize){
    int n = Udp.read(packetBuffer, UDP_TX_PACKET_MAX_SIZE);
    packetBuffer[n] = 0;
    UDP_DATA_AVAILABLE = 1;

    digitalWrite(LED_BUILTIN, LOW);
      LED_ON = 1;
      PASTTIME = TIME;
    };

  if(TIME - PASTTIME >= 5 && LED_ON == 1){
    digitalWrite(LED_BUILTIN, HIGH);
    LED_ON = 0;
  };
}

void SERVO_READ(int label, int value, int servo_num){    //Parses out the header byte (either 'a' or 'b'), reads the value byte into an int and then writes the int value to the serial port and corresponding servo.
  if(packetBuffer[0] == label){
      int value = packetBuffer[1];
      Serial.print(value);
      servo[servo_num].write(value);
      packetBuffer[0] = 0;
      packetBuffer[1] = 0;
    }
}

void setup() {
  Serial.begin(115200);

  WiFi.mode(WIFI_STA);
  WiFi.config(ip, gateway, subnet);
  WiFi.begin(STASSID, STAPSK);
  while (WiFi.status() != WL_CONNECTED){delay(5);}       //Initializes WiFi connection.
  Udp.begin(localPort);                                  //Starts listening to UDP on specified port.

  servo[0].attach(2);                                    //The attached pin numbers may have to change depending on what PWM pins you prefer to use on the ESP-12E
  servo[1].attach(5);

  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, HIGH);                       //The built-in LED on the ESP-12E is reversed- HIGH turns the LED off, LOW turns it on.
}

void loop() {
  TIME = millis();

  UDP_COLLECT();                                         //Checks to see if any UDP messages have been received.
  if(UDP_DATA_AVAILABLE){                                //If UDP data is available, reads it and then writes to the corresponding servo.
    for(int x = 0; x < sizeof(Servo_Label); x ++){
      SERVO_READ(Servo_Label[x], Servo_Value[x], x);
    };
    UDP_DATA_AVAILABLE = 0;
  }
}
ESP-12E Servo Control.maxpat
text/plain 3.17 KB

Ramon's icon

Hi Amina, thanks so much. I've definitely made some progress. I've managed to get one servo working with your sketch. However, there are still some issues. I know you've already gone above and beyond, but if you know anything off hand, please let me know.
1) The 'prepend 97' slider works on pin 4. It seems like "servo[0].attach(2);" in the sketch is indicating pin 2. The 'prepend 98' slider also affects pin 4, but not in any predictable way. I would have thought that pin 5 is controlled by the 'prepend 98' slider.
2) Changing the 'prepend 97' slider does move the servo, but only slightly in the direction of the new value. This is pretty easy to fix in the patch with a line object, but I'll still try to figure out how to fix it in the sketch.
3) Probably the smallest problem, but I tried setting a static IP in the sketch. However, the board connects to my wifi on a totally different IP. Easy enough to snoop out when I need it, but I'm not sure why the static IP doesn't work in the code.

I'll stare at the sketch when I get a chance. Perhaps there are some fixes I can divine over time. I'm also going to try to get 6 or more servos working on one board. Presumably, I can just replicate some of the code and make some tweaks here and there.

thanks again, for the code and your patience.

Amina Kirby's icon

Hi Ramon-

Give the revised code I attached below a shot and see if that works instead- it should now be able to control two servos on D5 and D6 of the ESP-12E.

A couple things that I changed:

- I forgot that the pinout for the ESP8266 is a little counter-intuitive in that the actual GPIO numbers are different than the numbers printed on the board itself. Because of this, D5 and D6 are actually the equivalent of GPIO14 and GPIO12. So in the code when pinMode() is set for each servo's control pin, it is 14 and 12 instead of 5 and 6. I attached the ESP-12E pinout for reference.
- I don't believe the Servo.h library will easily work with the ESP8266 because most Arduino boards will only output 8-bit PWM (0 - 255) whereas the ESP8266 outputs 10-bit PWM (0 - 1023). So I ditched the Servo.h library and instead just used analogWrite() commands to set the control pin for each servo.

Also regarding the static IP- you may need to change the static IP so that it is in the same address range as the router. For example, if your router's IP is 192.168.1.1, you may need to change the static IP of the ESP8266 to something like 192.168.1.20. I think it theoretically should still work in the 10.0.0.1 range, but I've had this issue before and seemed to be able to remedy it by changing the ESP8266 IP to the same range.

Hope that helps! :)

Cheers,
Amina

#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <WiFiUdp.h>

#ifndef STASSID
#define STASSID "[YOUR NETWORK HERE]"                    //Enter your network here.
#define STAPSK  "[YOUR NETWORK PASSWORD HERE]"           //Enter your network password here.
#endif
WiFiUDP Udp;
IPAddress ip(10, 0, 0, 20);                              //Set a static IP address for the ESP-12E.
IPAddress gateway(10, 0, 0, 1);                          //The IP address of  your router/modem.
IPAddress subnet(255, 255, 255, 0);
unsigned int localPort = 6000;                           //Port ESP-12E will be receiving messages on.
char packetBuffer[UDP_TX_PACKET_MAX_SIZE + 1];           //buffer to hold incoming packet
int UDP_DATA_AVAILABLE;                                  //Used to write serial data only if it is available.
int LED_ON;                                              //Enabling/disabling built-in LED.

int TIME;                                                //Used for blinking the ESP-12E LED any time a UDP message is received.
int PASTTIME;

int Servo_Label[] = {'a', 'b'};                          //One label for each servo. These will be the symbols prepended to the value of each servo in Max.
int Servo_Value[] = {0, 0};                              //Integer to store the value of each servo.
int Servo_Pin[] = {14, 12};                              //ESP-12E GPIO # for controlling each servo.

void UDP_COLLECT(){                                      //Waits for incoming UDP messages. Upon receiving, reads UDP packet into a buffer.
  int packetSize = Udp.parsePacket();
  if(packetSize){
    int n = Udp.read(packetBuffer, UDP_TX_PACKET_MAX_SIZE);
    packetBuffer[n] = 0;
    UDP_DATA_AVAILABLE = 1;

    digitalWrite(LED_BUILTIN, LOW);
      LED_ON = 1;
      PASTTIME = TIME;
    };

  if(TIME - PASTTIME >= 5 && LED_ON == 1){
    digitalWrite(LED_BUILTIN, HIGH);
    LED_ON = 0;
  };
}

void SERVO_READ(int label, int value, int servo_num){    //Parses out the header byte (either 'a' or 'b'), reads the value byte into an int and then writes the int value to the serial port and corresponding servo.
  if(packetBuffer[0] == label){
      int value = map(packetBuffer[1], 0, 179, 0, 1023);
      Serial.print(value);
      analogWrite(Servo_Pin[servo_num], value);
      packetBuffer[0] = 0;
      packetBuffer[1] = 0;
    }
}

void setup() {
  Serial.begin(115200);

  WiFi.mode(WIFI_STA);
  WiFi.config(ip, gateway, subnet);
  WiFi.begin(STASSID, STAPSK);
  while (WiFi.status() != WL_CONNECTED){delay(5);}       //Initializes WiFi connection.
  Udp.begin(localPort);                                  //Starts listening to UDP on specified port.

  analogWriteRange(1023);

  for(byte g = 0; g < sizeof(Servo_Pin); g ++){
    pinMode(Servo_Pin[g], OUTPUT);
    analogWrite(Servo_Pin[g], 0);
  }

  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, HIGH);                       //The built-in LED on the ESP-12E is reversed- HIGH turns the LED off, LOW turns it on.
}

void loop() {
  TIME = millis();

  UDP_COLLECT();                                         //Checks to see if any UDP messages have been received.
  if(UDP_DATA_AVAILABLE){                                //If UDP data is available, reads it and then writes to the corresponding servo.
    for(byte x = 0; x < sizeof(Servo_Label); x ++){
      SERVO_READ(Servo_Label[x], Servo_Value[x], x);
    };
    UDP_DATA_AVAILABLE = 0;
  }
}
Ramon's icon

I just discovered that odd GPIO numbering scheme myself. Confusing, but workable.
I'm unfortunately pretty much at the same place as before. Your first sketch is still the closest I've gotten to things working. The analogWrite sketch just produces erratic movement in the servos and lots of heat.

Thanks again for all of your help. I'll keep studying things to see if I can get more servos working over time. It's definitely put me a few steps in the right direction. I noticed that there is an ESP8266 specific servo library, so I might spend a little time with that one.

One quick question I have about the first sketch.

Servo servo[1]; //Declares 2 servo
Is the '1' in this declaration stating that there are two servos, servo[0] and servo[1]?
In other words, would this declaration setup six servos with there own IDs?

Servo servo[5];

Once again, thanks a bunch!

Amina Kirby's icon

Hi Ramon-

My initial thought was that yes- Servo servo[5] would declare an array of 6 servos, but I think that may not be the correct syntax as I was unable to get servo[1] working when I initially shared that code with the Servo servo[1] declaration. It didn't give me an error, though, so it is still a mystery to me. I know that there is a way to declare an array of objects, but I still need to research how to do it exactly...

Also after further testing I realize that I misspoke about a few things and made some errors in the most recent code I sent you- I was indeed able to get the Servo.h library working on an ESP8266! I thought that it wouldn't have been compatible, but it seems to be generating the correct frequency and duty cycle for controlling a servo now. I was also able to get two servos working on D5 and D6 (GPIO 14 and GPIO12).

I included the code below that *actually* should work now. Maybe. Hopefully. Fingers crossed ;P hah. Also I hope that my prior code didn't make your servos too hot or damage them! If you test this and get it working with 2 servos, let me know and I can then explain how to add another 4 servos- it would just be a few additional lines of code. :)

Cheers,
Amina

#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <WiFiUdp.h>
#include <Servo.h>

#ifndef STASSID
#define STASSID "NETWORK"                                //Enter your network here.
#define STAPSK  "PASSWORD"                               //Enter your network password here.
#endif
WiFiUDP Udp;
IPAddress ip(10, 0, 0, 20);                              //Set a static IP address for the ESP-12E.
IPAddress gateway(10, 0, 0, 1);                          //The IP address of  your router/modem.
IPAddress subnet(255, 255, 255, 0);
unsigned int localPort = 6000;                           //Port ESP-12E will be receiving messages on.
char packetBuffer[UDP_TX_PACKET_MAX_SIZE + 1];           //buffer to hold incoming packet
int UDP_DATA_AVAILABLE;                                  //Used to write serial data only if it is available.
int LED_ON;                                              //Enabling/disabling built-in LED.

Servo servo1;
Servo servo2;

int TIME;                                                //Used for blinking the ESP-12E LED any time a UDP message is received.
int PASTTIME;

int Servo_Label[] = {'a', 'b'};                          //One label for each servo. These will be the symbols prepended to the value of each servo in Max.
int Servo_Value[] = {0, 0};                              //Integer to store the value of each servo.
int Servo_Pin[] = {14, 12};                              //ESP-12E GPIO # for controlling each servo.

void UDP_COLLECT(){                                      //Waits for incoming UDP messages. Upon receiving, reads UDP packet into a buffer.
  int packetSize = Udp.parsePacket();
  if(packetSize){
    int n = Udp.read(packetBuffer, UDP_TX_PACKET_MAX_SIZE);
    packetBuffer[n] = 0;
    UDP_DATA_AVAILABLE = 1;

    digitalWrite(LED_BUILTIN, LOW);
      LED_ON = 1;
      PASTTIME = TIME;
    };

  if(TIME - PASTTIME >= 5 && LED_ON == 1){
    digitalWrite(LED_BUILTIN, HIGH);
    LED_ON = 0;
  };
}

void SERVO_READ(int label, int value){                   //Parses out the header byte (either 'a' or 'b'), reads the value byte into an int and then writes the int value to the serial port and corresponding servo.
  if(packetBuffer[0] == label){
      int value = packetBuffer[1];
      Serial.print(value);
      if(label == 'a'){servo1.write(value);}
      else if(label == 'b'){servo2.write(value);}
      packetBuffer[0] = 0;
      packetBuffer[1] = 0;
    }
}

void setup() {
  Serial.begin(115200);

  WiFi.mode(WIFI_STA);
  WiFi.config(ip, gateway, subnet);
  WiFi.begin(STASSID, STAPSK);
  while (WiFi.status() != WL_CONNECTED){delay(5);}       //Initializes WiFi connection.
  Udp.begin(localPort);                                  //Starts listening to UDP on specified port.

  servo1.attach(14);
  servo2.attach(12);
  servo1.write(0);
  servo2.write(0);

  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, HIGH);                       //The built-in LED on the ESP-12E is reversed- HIGH turns the LED off, LOW turns it on.
}

void loop() {
  TIME = millis();

  UDP_COLLECT();                                         //Checks to see if any UDP messages have been received.
  if(UDP_DATA_AVAILABLE){                                //If UDP data is available, reads it and then writes to the corresponding servo.
    for(byte x = 0; x < sizeof(Servo_Label); x ++){
      SERVO_READ(Servo_Label[x], Servo_Value[x]);
    };
    UDP_DATA_AVAILABLE = 0;
  }
}
Amina Kirby's icon

Yes, those were the changes I was going to say that would need to be made for more servos. :) Hooray!

Also one small thing regarding this part:

packetBuffer[0] = 0;
packetBuffer[1] = 0;
packetBuffer[2] = 0;
packetBuffer[3] = 0;

You should only need packetBuffer[0] and packetBuffer[1]- I set those to 0 to clear the label and value byte for each message sent from Max. Using the Max patch with the fader value and prepended ASCII value, you should only ever receive two bytes of data in the buffer at one time, which will get stored in packetBuffer[0] (the ASCII value / servo label) and packetBuffer[1] (the respective servo's value). Setting packetBuffer[2] and packetBuffer[3] to 0 isn't hurting anything of course, but theoretically there won't ever be a 3rd and 4th byte in any UDP packet sent over from Max, so setting packetBuffer[2] and packetBuffer[3] to 0 basically isn't doing anything.

Happy servo-ing!