So you're a Noob? Post your questions here until you graduate! Don't be shy.
User avatar
By mrlightsman
#75952 I'm sure there is a way to do this. I just haven't figured it out. I am developing a sketch that will have three main functions:
1. Use OTA to update the sketch as needed.
2. Act as station which will transmit/receive UDP packets from other ESP on the WLAN.
3. Act as a webserver, that when logged into it's website will show the UDP responses from the other ESP.

I have developed a series of smart switches based on the ESP-01. They communicate with each other using UDP packets. However, I would like to now develop a sketch to monitor their status to make sure they are online and functioning properly.

In short, what I would like to do is to log into a "controller" ESP which serves up a webpage which automatically updates with the status of each "switch" ESP. I think I want to do this, also using UDP, by having the controller send a packet to each switch and then serve response to the webpage. (I am open to learning about smarter ways to do this.)

Ultimately, using OTA, I will adjust the code to add additional target IP addresses as I add more switches to my house. (Again, unless there is a smarter way to do this.)

In the sketch, I would define the total number of switches, then the IP address for each switch. Then using a for statement, go through a series of send/receive packets to collect the status and serve it to the webpage.

(In reality, what I am trying to do is probably not that much different than the Tardis example I've seen, I just don't understand it well enough to make the adaptation.)

In order to do this, I was thinking about declaring my variables like this:

Code: Select all//Define UDP server variables
unsigned int udpPort = 4500;  // used by udp.begin()command (it is udpPort converted for use)
char pktBuf[25]; //buffer for UDP packets

//variables for processing Switches
int count = 3; // number of IP addresses to ping

char IP1[16] = "192.168.0.2";
char IP2[16] = "192.168.0.13";
char IP3[16] = "192.168.0.45";



where I could adjust count up or down, then add additional IP# variables.

Then in the loop() is was going to try to do something like this:

Code: Select all     for (int x = 0; x<count; x++) {
    IPAddress targetIP;
    targetIP.fromString(IP(x));
      Udp.beginPacket(targetIP, udpPort);
      Udp.write("survey");
      Udp.endPacket();
      delay(10);
  //Receive incoming Udp packet and parse information
  int pktSize = Udp.parsePacket();
  if (pktSize) {
//    Serial.print(Udp.remoteIP());
//    Serial.print(":");
//    Serial.println(Udp.remotePort());
    Udp.read(pktBuf, pktSize);
   }
   String packetbuffer (pktBuf);
       packetbuffer(x) = (packetbuffer);
    for (int i = 0; i<pktSize; i++){
      pktBuf[i] = (char) 0;
      }
    Udp.flush();
   delay(500);
    }


In this case, the variable IP would be appended with x such that each time the for statement ran, it would change the variable name to... IP1, IP2, IP3, etc.

Obviously, this isn't correct and does not work. I'm looking for suggestion about how to do this... or something which will function in this way.

Then I would serve up the results to the webpage like this:

Code: Select all// Serve up the webpage with responses
  client.println("HTTP/1.1 200 OK");
  client.println("Content-Type: text/html; charset=UTF-8");
  client.println("");
  client.println("<!DOCTYPE HTML>");
  client.println("<html>");
  client.println("<head>");
  client.println("<title>Lights Manager</title>");
  client.println("</head>");
  client.println("<body>");
  client.println("<a href=\"/\">Refresh Status</a>");
  client.println("</br></br>");
  for (int x = 0; x<count; x++) {
    client.println(packetbuffer(x) "</br/");
  }
  client.println("</br>");
 
  client.println("</br>");
  client.println("</body>");
  client.println("</html>");
  }


Again, here packetbuffer(x) would change each time the for statement ran, ultimately serving up all of the udp packetbuffer responses. (Although, assuming this method is even possible, I'm not sure how to declare multiple instances of packetbuffer(x) variable.

Here is my complete code. It is a total disaster of cut and paste examples I've mashed together to try to demonstrate what I am hoping to accomplish. It contains elements of:
1. an OTA example
2. a webserver example
3. a wifi.udp example

It is a total mess!

I know this is a BIG ask, so I appreciate any help I can be given. Thanks.

Code: Select all#include <ESP8266WiFi.h>
#include <ESP8266mDNS.h>
#include <WiFiUdp.h>
#include <ESP8266WebServer.h>
#include <ArduinoOTA.h>

const char* ssid = "..........";
const char* password = "..........";


WiFiServer WebServer(80);
WiFiClient client;
WiFiUDP Udp;

//Define UDP server variables
unsigned int udpPort = 4500;  // used by udp.begin()command (it is udpPort converted for use)
char pktBuf[25]; //buffer for UDP packets


//variables for processing Switches
int count = 3; // number of IP addresses to ping

char IP1[16] = "192.168.0.2";
char IP2[16] = "192.168.0.13";
char IP3[16] = "192.168.0.45";

void setup() {
  Serial.begin(115200);
  Serial.println("Booting");
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  while (WiFi.waitForConnectResult() != WL_CONNECTED) {
    Serial.println("Connection Failed! Rebooting...");
    delay(5000);
    ESP.restart();
  }

  // Port defaults to 8266
   ArduinoOTA.setPort(8266);

  // Hostname defaults to esp8266-[ChipID]
   ArduinoOTA.setHostname("Light Manager");

  // No authentication by default
  // ArduinoOTA.setPassword("admin");

  // Password can be set with it's md5 value as well
  // MD5(admin) = 21232f297a57a5a743894a0e4a801fc3
  // ArduinoOTA.setPasswordHash("21232f297a57a5a743894a0e4a801fc3");

  ArduinoOTA.onStart([]() {
    String type;
    if (ArduinoOTA.getCommand() == U_FLASH)
      type = "sketch";
    else // U_SPIFFS
      type = "filesystem";

    // NOTE: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end()
    Serial.println("Start updating " + type);
  });
  ArduinoOTA.onEnd([]() {
    Serial.println("\nEnd");
  });
  ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
    Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
  });
  ArduinoOTA.onError([](ota_error_t error) {
    Serial.printf("Error[%u]: ", error);
    if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed");
    else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed");
    else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed");
    else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed");
    else if (error == OTA_END_ERROR) Serial.println("End Failed");
  });
  ArduinoOTA.begin();
  Serial.println("Ready");
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());

WebServer.begin();
Udp.begin(udpPort);
}

void loop() {
  ArduinoOTA.handle();

 client = WebServer.available();
  if (!client) {//restart loop
    return;
  }

  // Wait until the user sends some data
  Serial.println("New User");
  while (!client.available()) {
    delay(1);
  }

  // Read the first line of the request
  String request = client.readStringUntil('\r\n');
  Serial.println(request);
  client.flush();

  // Process the request:
  if (request.indexOf("/survey") != -1) {
    for (int x = 0; x<count; x++) {
    IPAddress targetIP;
    targetIP.fromString(IP(x));
      Udp.beginPacket(targetIP, udpPort);
      Udp.write("survey");
      Udp.endPacket();
      delay(10);
  //Receive incoming Udp packet and parse information
  int pktSize = Udp.parsePacket();
  if (pktSize) {
//    Serial.print(Udp.remoteIP());
//    Serial.print(":");
//    Serial.println(Udp.remotePort());
    Udp.read(pktBuf, pktSize);
   }
   String packetbuffer (pktBuf);
       packetbuffer(x) = (packetbuffer);
    for (int i = 0; i<pktSize; i++){
      pktBuf[i] = (char) 0;
      }
    Udp.flush();
   delay(500);
    }

  // Serve up the webpage with responses
  client.println("HTTP/1.1 200 OK");
  client.println("Content-Type: text/html; charset=UTF-8");
  client.println("");
  client.println("<!DOCTYPE HTML>");
  client.println("<html>");
  client.println("<head>");
  client.println("<title>Lights Manager</title>");
  client.println("</head>");
  client.println("<body>");
  client.println("<a href=\"/\">Refresh Status</a>");
  client.println("</br></br>");
  for (int x = 0; x<count; x++) {
    client.println(packetbuffer(x) "</br/");
  }
  client.println("</br>");
 
  client.println("</br>");
  client.println("</body>");
  client.println("</html>");
  }
}
User avatar
By rudy
#75954 I'm not opposed to the general way you want this to work. I had considered doing something along those lines. But another option that might be simpler is to use MQTT and to have the switches push the data. You can have the server subscribe to each, or all, of the messages. I think it would be easier to manage. MQTT for ESP8266 has been working reliably for quite a while. There are a lot of examples out there.

If I have time tonight I will try and take a look at what you have coded so far.
User avatar
By mrlightsman
#75976 Rudy
Thank you for the reply. I hadn't considered MQTT, but if my understanding of it is correct, it relies on an outside server to do the work? I am trying to avoid dependencies on outside resources that I cannot control.

(For example, my main light switch sketch is built on the Sinric platform. I hope to replace that as soon as I find an Alexa sketch that works reliably. It seems Amazon changes it's protocol every so often, breaking existing sketches functional ability.)

As for this project, I did a bit of coding last night. I've simplified things for the moment to debug the different stages of development. For the moment, I have dropped OTA and webserver functions. I am focusing on making sure the UDP and multiple variables piece work correctly, using the serial monitor to debug. I haven't tested the sketch yet, but the code is much cleaner to follow.

Once I get this piece working, I will go back to tackling the webserver, then finally add in the OTA bit.

Thanks for offering to help. Here is an updated version of the simplified code.

Code: Select all#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>
#include <WiFiUdp.h>
#include <avdweb_Switch.h>

// Set ESP into WiFi client and turn on Udp
ESP8266WiFiMulti wifiMulti;
WiFiUDP Udp;

//variables for processing Switches
int count = 3; // number of IP addresses to ping
char* result[3]; // number of pktBuf... sames as count
char* IPTarget[] = {"192.168.0.2", "192.168.0.13", "192.168.0.45"};//IP address of switches

//Set up UDP variables
unsigned int udpPort = 4500;  // used in UDP port command below
char pktBuf[25]; //buffer for UDP packets

// Set pin names and variables
const byte GPIO_2 = 2; //Pushbutton on 2
const byte GPIO_0 = 0; //Pushbutton on 0

//set up button debounce
Switch GPIO_2State = Switch(GPIO_2); //device 2 button to ground
Switch GPIO_0State = Switch(GPIO_0); //device 3 button to ground


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

  WiFi.mode(WIFI_STA);
  wifiMulti.addAP("SSID 1", "Password 1");
  wifiMulti.addAP("SSID 2", "Password 2");

  Serial.println("Connecting to Wifi...");
  if (wifiMulti.run() == WL_CONNECTED) {
    Serial.print("WiFi Connected");
    Serial.print("Manager IP Address is: ");
    Serial.println(WiFi.localIP()); 
  }

  // Setup pins
  pinMode(GPIO_2, INPUT_PULLUP);
  pinMode(GPIO_0, INPUT_PULLUP);


  //Start Udp server
  Udp.begin(udpPort);
  //Serial.println("UDP Server IP");
  //Serial.println(WiFi.localIP());
  //Serial.println("UDP port");
  //Serial.println(udpPort);


}

void loop() {
  if (wifiMulti.run() != WL_CONNECTED) {
    Serial.println("WiFi not connected!!");
    delay(1000);
  }
 
  //What to do with button presses
  GPIO_0State.poll();
  GPIO_2State.poll();

  if (GPIO_2State.singleClick()){
   Serial.print("Begin Survey");
      for (int x = 0; x<count; x++) {
        IPAddress targetIP;
        targetIP.fromString(IPTarget[x]);
        Udp.beginPacket(targetIP, udpPort);
        Serial.print("#");
        Serial.println(x);
        Serial.println(" - Target IP Address: ");
        Serial.println(targetIP);
        Udp.write("survey");
        Udp.endPacket();
  //Receive survey response and parse information
        int pktSize = Udp.parsePacket();
        if (pktSize) {
          Serial.println(", Return IP Address: ");
          Serial.println(Udp.remoteIP());
          Udp.read(pktBuf, pktSize);
       }
        Serial.println(",  Message Received: ");
        Serial.println(pktBuf);
        Serial.print("");
        result[x] = (pktBuf);
  //Clear the buffer     
        for (int i = 0; i<pktSize; i++){
          pktBuf[i] = (char) 0;
        }
        Udp.flush();
       delay(500);
        }
  }

  if (GPIO_0State.singleClick()){
    Serial.print("");
    Serial.print("---------------------------------------------------------------");
    Serial.print("");
    Serial.print("Results:");
    for (int x = 0; x<count; x++) {
      Serial.print("#");
      Serial.println(x);
      Serial.println(" - ");
      Serial.println(result[x]);
      result[x] = (char) 0; // clear the results as we process them so we don't get a false positive next time.
    }
    Serial.print("End of Process");
    Serial.print("================================================================");
  }



}


What I hope is happening here is that if I click button GPIO_2 it will cause the controller to send a series of UPD packets to the IP addresses listed in character array IPTarget. Then is will receive a response packet from each device and store them in the character array result.

If I click button GPIO_0 it will serial.print the character array result.

If it works correctly, the debug messages from the send/receive (GPIO_2) and display functions (GPIO_0) will match. If this is the case, then I know the UPD and buffering pieces are working and I can move forward with transferring these results to a webserver page.

Thanks for any input.
User avatar
By rudy
#75977 I'll look at your current code when I get home tonight.

Just to add some more information about MQTT. You do not need an outside server/broker. I have a Raspberry Pi running at home that does that function (and more). It makes for a low power low cost solution. I also don't want to rely on outside sources, or a continuous Internet connection.

I have also looked at a great little project that runs MQTT on an ESP8266. https://github.com/martin-ger/uMQTTBroker It is limited in the connections it can hold and the number of topics but it does work. Since your original post I loaded it on one of my boards and did a few tests on it. I wish he would port this over to the ESP32. He has another ESP8266 implemented MQTT broker with more capabilities but it isn't an Arduino project. https://github.com/martin-ger/esp_mqtt