Inconsistency using UDP with two ESP8266's

I have a project where I want to create a Master and 2 Slave devices whereby the Slaves collect data and send to the Master over WiFI for display on a Web Page, the User can then interact with the web page to send commands back to each of the Master and Slaves for control of some connected equipment. I have had the web page and the user control of the equipment working in its barest form (some LED’s connected to each of the Master and one Slave). I am using ESP8266mod boards and UDP as a communication protocol and the system has to work standalone and in remote locations, for example a boating lake or model flying field. To that end my Master board also acts as an Access Point and a Web Server.

The problem I am facing is with what appears to be some inconsistency and illogical behaviour of the hardware (the ESP8266’s) with respect to the UDP comms. I have reduced my code down to a bear minimum to test and prove the comms but still things don’t seem logical when so much of the code is common between both the Master and Slave modules. I have posted both the Master and Slave module code below.

I originally bought 3 ESP8266 modules and saw some inconsistencies so then bought a further 2 from another supplier in an attempt to rule out a hardware problem. The two additional boards are slightly different in respect of PCB printing and soldering so are at the very least from a different batch if not from a different manufacturer. Also the vendor logo on the WiFi module is different between the three off and the two off.

My code has been simplified down to the following…

The Slave sends a message (1111) to the Master approx. every 10 seconds, when the Master receives the message it then sends a reply back to the slave to acknowledge receipt. Each of these steps are displayed in the respective Serial Monitor windows.

The problem I have is that Boards 1, 2, 4 & 5 all work successfully as Masters (which proves that they can all send and receive UDP messages OK) but Board 3 is the only board that will work as the Slave. If I set up any of boards 1, 2, 4 or 5 as the Slave then they will connect to the WiFi and appear to send the 1111 message but nothing is received at the Master. I cannot test if board 3 will work as a Master as I have no other working Slave to try it with.

What is really confusing me is that it doesn’t appear to be possible that it is either a software or hardware issue.

  • With respect to software, I have copied the same Slave code into all boards and it is only Board 3 that works, so I don’t see that it can be a software issue.
  • With respect to hardware, the fact that boards 1, 2, 4 & 5 can all send and receive successfully as Masters would suggest that it is also not the fault of the hardware.

Hopefully someone can help shed a little light on this for me please? (I apologise in advance if I've made some rookie errors but I just seem to be going around in circles, many thanks.)

My "Min_AP_Master.ino" file follows...

//This is the minimum required code to prove the functionality of a 3-way UDP communication system
//between a MASTER and two SLAVE ESP8266MOD boards (Wemos D1 Mini's).
//
//This is the code for the ACCESS POINT and MASTER board.
//
//The long term goal here is that the Slaves will collect sensor data and send it to the Master board,
//the Master board will confirm receipt otherwise the data is sent again (the send again part is not
//yet implimented).
//
//The code has been stripped to a minimum and the Slaves will send an Integer value of 1111 (from Slave 1)
//and 2222 (from Slave 2) as UDP messages to the MASTER approximately every 10 seconds.
//
//
//
// Import required libraries
#include <ESP8266WiFi.h>
#include <WiFiUdp.h>

const char* ssid     = "ESP8266-Access-Point";
const char* password = "123456789";

// Set this boards Static IP address and gateway
IPAddress local_IP(192, 168, 4, 3);
IPAddress gateway(192, 168, 4, 1);
IPAddress subnet(255, 255, 255, 0);
//IPAddress primaryDNS(8, 8, 8, 8);     // optional
//IPAddress secondaryDNS(8, 8, 4, 4);   // optional

const char* APIP = "192.168.4.1";

// Slave board 1 IP address and port
const char* slave1IP = "192.168.4.11";
const unsigned int slave1Port = 8081;

// Slave board 2 IP address and port
const char* slave2IP = "192.168.4.12";
const unsigned int slave2Port = 8082;

WiFiUDP udp;
unsigned int localPort = 4212;           // The port to listen on
char incomingPacket[255];                // Buffer to hold incoming packets

unsigned long CurrentTime = millis();
unsigned long PreviousTime = millis();

String replyForSlave1 = "Slave 1 message received by AP";
String replyForSlave2 = "Slave 2 message received by AP";

String receivedMessage = "";

void setup() {
  Serial.begin(115200);
  delay(5000);                         //This delay is just to give time to set the baud rate.

  // Configures static IP address
  if (!WiFi.config(local_IP, gateway, subnet)) {
    Serial.println("STA Failed to configure");
  }

  Serial.println("Local IP and baud rate set.");

  Serial.print("Local IP address: ");
  Serial.println(WiFi.localIP());

  Serial.print("Local Gateway   : ");
  Serial.println(WiFi.gatewayIP());

  Serial.print("Local Subnet    : ");
  Serial.println(WiFi.subnetMask());

  Serial.println();

  Serial.println("Setting AP (Access Point)…");
  // Remove the password parameter, if you want the AP (Access Point) to be open
  WiFi.softAP(ssid, password);

  IPAddress IP = WiFi.softAPIP();
  Serial.print("AP IP address: ");
  Serial.println(IP);

  // Print ESP8266 Local IP Address
  Serial.print("ESP8266 Local IP address: ");
  Serial.println(WiFi.localIP());




  // Start UDP
  udp.begin(localPort);
  Serial.printf("Listening on UDP port %d\n", localPort);
  Serial.println();
  Serial.println();
}

void loop() {

  //Put the retrieval of the UDP messages from boards 2 & 3 here...
  // Listen for incoming UDP packets
  //  if (udp.available()) {
  int packetSize = udp.parsePacket();
  if (packetSize) {
    int len = udp.read(incomingPacket, 255);
    if (len > 0) {
      incomingPacket[len] = 0;
    }
    Serial.printf("UDP packet received: %s\n", incomingPacket);
    String rIP = udp.remoteIP().toString();
    Serial.print("     From IP..... ");
    Serial.println(rIP);
    int rP = udp.remotePort();
    Serial.print("     From Port... ");
    Serial.println(rP);
    Serial.println();
    //      receivedMessage = int(incomingPacket);
    receivedMessage = incomingPacket;
  }
  //  }

  if (receivedMessage == "1111") {
    //  Reply to Slave 1
    udp.beginPacket(slave1IP, slave1Port);
    //    udp.beginPacket(APIP, slave1Port);
    udp.print(replyForSlave1);
    udp.endPacket();
    Serial.print("Just sent a reply to Slave 1 at IP ");
    Serial.print(APIP);
    Serial.print(" and Port ");
    Serial.println(slave1Port);
    Serial.println();
    receivedMessage = "";
  }

  if (receivedMessage == "2222") {
    //  Reply to Slave 2
    udp.beginPacket(slave2IP, slave2Port);
    udp.print(replyForSlave2);
    udp.endPacket();
    Serial.print("Just sent a reply to Slave 2 at IP ");
    Serial.print(slave2IP);
    Serial.print(" and Port ");
    Serial.println(slave2Port);
    Serial.println();
    receivedMessage = "";
  }

}

And my "Min_Slave_1.ino" file follows here...

//This is the minimum required code to prove the functionality of a 3-way UDP communication system
//between a MASTER and two SLAVE ESP8266MOD boards (Wemos D1 Mini's).
//
//This is the code for SLAVE number 1.
//
//The long term goal here is that the Slaves will collect sensor data and send it to the Master board,
//the Master board will confirm receipt otherwise the data is sent again (the send again part is not
//yet implimented).
//
//This code has been stripped to a minimum and will send an Integer value of 1111 as a UDP message to
//the MASTER approximately every 10 seconds (10,037 milliseconds).
//
//
//
// Import required libraries
#include <ESP8266WiFi.h>
#include <WiFiUdp.h>

// Your network credentials
const char* ssid = "ESP8266-Access-Point";
const char* password = "123456789";

// Set this boards Static IP address and gateway
IPAddress local_IP(192, 168, 4, 11);
IPAddress gateway(192, 168, 4, 1);
IPAddress subnet(255, 255, 255, 0);
//IPAddress primaryDNS(8, 8, 8, 8);     // optional
//IPAddress secondaryDNS(8, 8, 4, 4);   // optional

// Master board IP address and port
const char* masterIP = "192.168.4.1";   //seems to only work with AP IP address not Masters local IP address
const unsigned int masterPort = 4212;

// UDP settings
WiFiUDP udp;
unsigned int localPort = 8081;          // The port to listen on
char incomingPacket[255];               // Buffer to hold incoming packets

unsigned long currentTime = millis();
unsigned long udpSentTime = millis();
unsigned long elapsedTime = 0;

int udpSendPeriod = 10037;
int intToSend = 1111;


void setup() {
  Serial.begin(115200);
  delay(5000);                         //This delay is just to give time to set the baud rate.

  // Configures static IP address
  if (!WiFi.config(local_IP, gateway, subnet)) {
    Serial.println("STA Failed to configure");
  }

  Serial.println("Local IP and baud rate set.");

  // Connect to Wi-Fi
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println("Connecting to WiFi...");
  }

  Serial.println();
  Serial.println("Now connected to WiFi");

  Serial.print("Local IP Address: ");
  Serial.println(WiFi.localIP());

  Serial.print("Gateway   : ");
  Serial.println(WiFi.gatewayIP());

  Serial.print("Subnet    : ");
  Serial.println(WiFi.subnetMask());

  // Start UDP
  udp.begin(localPort);
  Serial.printf("Listening on UDP port %d\n", localPort);
  Serial.println();
  Serial.println();
}


void loop() {
  // Listen for incoming UDP packets
  //  if (udp.available()) {
  int packetSize = udp.parsePacket();
  if (packetSize) {
    int len = udp.read(incomingPacket, 255);
    if (len > 0) {
      incomingPacket[len] = 0;
    }
    Serial.printf("UDP packet received: %s\n", incomingPacket);
    String rIP = udp.remoteIP().toString();
    Serial.print("     From IP..... ");
    Serial.println(rIP);
    int rP = udp.remotePort();
    Serial.print("     From Port... ");
    Serial.println(rP);
    Serial.println();
  }

  currentTime = millis();
  elapsedTime = currentTime - udpSentTime;
  if (elapsedTime >= udpSendPeriod) {
    udpSend();
  }
}


void udpSend() {
  //
  Serial.print("Sending message: ");
  Serial.println(intToSend);
  udp.beginPacket(masterIP, masterPort);
  udp.print(intToSend);
  udp.endPacket();
  udpSentTime = millis();
  Serial.print("Just sent a message to IP ");
  Serial.print(masterIP);
  Serial.print(" and Port ");
  Serial.println(masterPort);
  Serial.println();
}

have you set different IPs for the slaves?

Furthermore in your Min_AP_Master.ino you have

```
// Set this boards Static IP address and gateway
IPAddress local_IP(192, 168, 4, 3);
IPAddress gateway(192, 168, 4, 1);
IPAddress subnet(255, 255, 255, 0);
```

... why is there another gateway on 192.164.4.1 when 192.168.4.3 is the AP ?!?

can you make a schematic so we can see each ESP with the used IPs?

Furthermore I suspect your wifi.config might be not correct. Try to find the correct documentation for the ESP8266 so you get the order of parameters correct.

May be this link helps:

1 Like

Thank you Noiasca, yes I intend to set different IP's for all the Slaves but for now I am only testing with one Master and one Slave at a time.

I'm sorry, I don't understand your comment about the gateway and the AP but I will follow it up and do some more reading to try to clarify it. Thank you for the pointer.

Re your request for a schematic, maybe the attached document that I use to track the IP's and numbers used will help?

Also, thank you for the link to the AP Class documentation.

Thank you for providing a schematic. Makes understanding your infrastructure much more easier.

What I want to point out is:
When your slave should use gateway 192.168.4.1 but the Master has IP 192.168.4.3 and obvious there is no other unit which has 192.168.4.1 in this network, how should the slave1 find the master on 192.168.4.1?

may first try would be to get rid of this non existing gateway (and reuse the 192.168.4.3 as gateway also)

softAPConfig (local_ip, local_ip, subnet);
1 Like

Hi Noiasca, thank you so much, your help has been invaluable! I have changed both the Master IP and the Gateway IP to be the same (I used ...4.1) and now all of my boards work as either a Master or as a Slave.

(I am still slightly confused as to why one board worked previously and the others didn't when the code was obviously wrong. I would have expected that all boards would not work. This seems very strange indeed).

I have now been able to move the project on some more as a result, so thank you again, but I now see one more anomaly. I have both Slave 1 and Slave 2 now running and Slave 2 works fine on it's own but when I power up Slave 1 it's messages do not get through to the Master. It seems that whichever Slave connects to the Master first takes over and the other Slave (although it connects to the Wi-Fi) does not get heard by the Master.

I see that the default number of connections for a Soft AP (with an ESP 8266) is 4 so this should not be the problem (besides the Terminal Monitor response is reporting that both Slaves are connected to WiFi OK). Do you think there is an obvious reason for this problem? If not I'm happy to go away and do some more reading / research.

Thanks again for your help.

my best bet is that you have a conflict in your IPs and may be Slave 1 / Slave 2 are using the same own IP.

On long term when you goal is to maintain one COMMON slave code whitouth any changes between the slaves, I recommend, that you store the configuration data for each device within the device. Use the library preferences for that purpose.
When you have stored your IP (or other identification data) in preferences you can still update all slaves with the same code - the code should read out the identification from preferences.

Thank you again noiasca, I have confirmed that there is no conflict in the IP's and in-fact both Slaves connect consistently to the Master (see screenshot 1 below, highlights in Yellow and Green).

It seems there is some inconsistency with the UDP part itself. Both Slaves work fine on their own but when both powered up it often happens that one works fine while the other often doesn't receive it's message back from the Master. I have shown this in screenshot 1 in Pink (Slave 1 working fine) and Blue (Slave 2 not receiving the confirmation message back from the Master even though the IP and port is correct).

I almost think it is a timing issue. I have an example in Screenshot 2 where everything is working perfectly, the results seem to be linked to the order and timing with which the individual boards are powered up. More often than not one of the boards does not work but every now and again you can strike lucky as shown here!!

Thank you for the information on COMMON code and the storing of configuration data within the devices themselves. I wasn't aware that that was possible. I will certainly use that in future, thank you again.

is there a reason why you set up slave1 and slave2 with different listening ports?
When yes. How does the master now to which slave port a messaged should be replied?

btw, when you set up a bidirectional communication without broadcast/multicast ... why do you use UDP at all and not TCP?

Hi, only for my convenience to keep them separate (and I wasn't sure if it was ok to use the same port in both devices). The Master knows which to reply to based on the following two bits of code...

// Slave board 1 IP address and port
const char* slave1IP = "192.168.4.11";
const unsigned int slave1Port = 8081;

// Slave board 2 IP address and port
const char* slave2IP = "192.168.4.12";
const unsigned int slave2Port = 8082;

and

  if (receivedMessage == "1111") {
    //  Reply to Slave 1
    udp.beginPacket(slave1IP, slave1Port);
    //    udp.beginPacket(APIP, slave1Port);
    udp.print(replyForSlave1);
    udp.endPacket();
    Serial.print("Just sent a reply to Slave 1 at IP ");
    Serial.print(slave1IP);
    Serial.print(" and Port ");
    Serial.println(slave1Port);
    Serial.println();
    receivedMessage = "";
  }

  if (receivedMessage == "2222") {
    //  Reply to Slave 2
    udp.beginPacket(slave2IP, slave2Port);
    udp.print(replyForSlave2);
    udp.endPacket();
    Serial.print("Just sent a reply to Slave 2 at IP ");
    Serial.print(slave2IP);
    Serial.print(" and Port ");
    Serial.println(slave2Port);
    Serial.println();
    receivedMessage = "";
  }

It all depends on what message the Master received that it then knows where it came from i.e. "1111" or "2222".

I used UDP originally as I got the impression from what I had read that it was a very simple protocol and that I could then just create my own check, reply and re-send routines where necessary. Maybe TCP would have been better? Do you think it would be any more reliable?

Thanks.

Here are my thoughts regarding UDP, TCP, HTTP.

Data Transmission on Microcontrollers (UDP TCP HTTP Overview for ESP8266 ESP32 Arduino UNO/NANO/MEGA)

UDP (from Wikipedia)

It has no handshaking dialogues and thus exposes the user's program to any unreliability of the underlying network; there is no guarantee of delivery, ordering, or duplicate protection.

If you don't need Broadcasts or Multicasts but bidirectional data communication, I would chose TCP (or even HTTP).

1 Like

Thanks again for you help on this. I've read your document and then looked at a number of your example files and can see why TCP would be better. I think that TCP is a little beyond my understanding at the moment, I don't fully understand the examples, but I will work through them in detail and try to apply TCP to my project. Your web site is a fantastic resource, I'm sure I will look back at it many times. Thank you.