0

I've got an ESP32-POE running a web server as well as publishing MQTT to a broker.

I want to publish MQTT every 60s but sticking that delay into void loop(void){} means server.handleClient(); is also beholden to the same delay.

The web server is just displaying sensor data, so a 1 minute update time isn't a dealbreaker but it's a bit annoying that sometimes my browser times out.

How can I decouple these delays? I've had all sorts of issues trying to use ESPAsyncWebServer, supposedly because there are compatibility issues between AsyncWebServer and both ETH.h and ESP32 Arduino core 3.x. I've also seen millis() recommended instead of delay() in some places.

char title[] = "<location> sensor";

#include <WiFi.h>
#include <WiFiClient.h>
#include <WebServer.h>
#include <DHT.h>
#include "AsyncUDP.h"
#include <PubSubClient.h>

// ETH.h precursors
#ifndef ETH_PHY_TYPE
#define ETH_PHY_TYPE  ETH_PHY_LAN8720 // Type of the Ethernet PHY (LAN8720 or TLK110)
#define ETH_PHY_ADDR  0 // I²C-address of Ethernet PHY (0 or 1 for LAN8720, 31 for TLK110)
#define ETH_PHY_MDC   23 // Pin# of the I²C clock signal for the Ethernet PHY
#define ETH_PHY_MDIO  18 // Pin# of the I²C IO signal for the Ethernet PHY
#define ETH_PHY_POWER -1
#define ETH_CLK_MODE  ETH_CLOCK_GPIO0_IN // Pin# of the enable signal for the external crystal oscillator (-1 to disable for internal APLL source)
#endif
#include <ETH.h>
static bool eth_connected = false;

//Set up DHT11
DHT dht(4, DHT11); 

//Start Webserver
WebServer server(80);

//HTML
void handleRoot() {
  char msg[1500];

  snprintf(msg, 1500,
           "<html>\
  <head>\
    <meta http-equiv='refresh' content='60'/>\
    <meta name='viewport' content='width=device-width, initial-scale=1'>\
  </head>\
  <body>\
      <h3> %s </h3>\
      <p>\
        <span>Temperature</span>\
        <span>%.2f</span>\
        <sup>&deg;C</sup>\
      </p>\
      <p>\
        <span>Humidity</span>\
        <span>%.2f</span>\
        <sup>&percnt;</sup>\
      </p>\
      <p>\
        <span>Dew Point</span>\
        <span>%.2f</span>\
        <sup>&deg;C</sup>\
      </p>\
  </body>\
</html>",
          title, readDHTTemperature(), readDHTHumidity(), readDewPoint()
          );
  server.send(200, "text/html", msg);
}

// Set up ethernet event handler (optional)
void onEvent(arduino_event_id_t event) {
  switch (event) {
    case ARDUINO_EVENT_ETH_START:
      // Serial.println("ETH Started");
      // The hostname must be set after the interface is started, but needs
      // to be set before DHCP, so set it from the event handler thread.
      ETH.setHostname("esp32-ethernet");
      break;
    case ARDUINO_EVENT_ETH_CONNECTED: Serial.println("ETH Connected"); break;
    case ARDUINO_EVENT_ETH_GOT_IP:
      // Serial.println("ETH Got IP");
      // Serial.println(ETH);
      eth_connected = true;
      break;
    case ARDUINO_EVENT_ETH_LOST_IP:
      // Serial.println("ETH Lost IP");
      eth_connected = false;
      break;
    case ARDUINO_EVENT_ETH_DISCONNECTED:
      // Serial.println("ETH Disconnected");
      eth_connected = false;
      break;
    case ARDUINO_EVENT_ETH_STOP:
      // Serial.println("ETH Stopped");
      eth_connected = false;
      break;
    default: break;
  }
}

// MQTT info
const char *mqtt_broker = "broker.emqx.io";
char topic_temp[64];
char topic_hum[64];
const char *mqtt_username = "emqx";
const char *mqtt_password = "public";
const int mqtt_port = 1883;

WiFiClient ethClient;
PubSubClient client(ethClient);

void setup() {
  //initialise DHT sensor
  dht.begin(); 

  // Serial.begin(115200); //Initialise serial for debugging
  
  //Initialise Ethernet
  Network.onEvent(onEvent);  
  ETH.begin();

  // //Initialise WiFi for debugging
  // const char *ssid = "";
  // const char *password = "";
  // WiFi.mode(WIFI_STA);
  // WiFi.begin(ssid, password);

  //Create MQTT topic strings
  String MacAsString = ETH.macAddress();
  MacAsString.replace(":", ""); //remove colons from default output
  snprintf(topic_temp, sizeof(topic_temp), "sensor/%s/1/temperature", MacAsString.c_str());
  snprintf(topic_hum, sizeof(topic_hum), "sensor/%s/1/humidity", MacAsString.c_str());

  //Initialise web server
  server.on("/", handleRoot);   
  server.begin();

  //Connect to the MQTT broker
  client.setServer(mqtt_broker, mqtt_port);
  reconnectMQTT();
}

char tpayload[10];  //define buffers for client.publish
char hpayload[10];

void loop(void){
  //Handle HTTP requests
  server.handleClient();
  
  //publish
  dtostrf(readDHTTemperature(), 6, 2, tpayload); //convert function outputs to string for client.publish
  dtostrf(readDHTHumidity(), 6, 2, hpayload);

  client.publish(topic_temp, tpayload);
  client.publish(topic_hum, hpayload);

  delay(60000);
}

void reconnectMQTT() {
  while (!client.connected()) {
   // Serial.print("Connecting to MQTT...");
    if (client.connect("ESP32_ETH_Client")) {
      //Serial.println("connected");
    } else {
      //Serial.print("failed, rc=");
      //Serial.print(client.state());
      //Serial.println(" trying again in 2 seconds");
      delay(2000);
    }
  }
}

2 Answers 2

1

You'd get the best response time to HTTP requests by spinning up a new thread to handle them (incidentally, ESPAsyncWebServer does exactly that behind the scenes).

Arduino loop() is a very, very simple concept, but you can get a 1-second response by running it more often and choosing when to take some action. E.g.:

void loop(void){
  static uint32_t loopCounter = 0;
  //Handle HTTP requests every 1 loop
  server.handleClient();
  
  if (loopCounter % 60 == 0) {
    //publish every 60 loops
    dtostrf(readDHTTemperature(), 6, 2, tpayload); //convert function outputs to string for client.publish
    dtostrf(readDHTHumidity(), 6, 2, hpayload);

    client.publish(topic_temp, tpayload);
    client.publish(topic_hum, hpayload);
  }
  delay(1000);
  loopCounter++;
}

Timing goes a bit out of sync, because the call to server.handleClient(); adds a small delay to each 1-second loop which accumulates over time.

Sign up to request clarification or add additional context in comments.

Comments

1

So publish every 60 seconds, not every loop.

unsigned long publish_time = 0;

void loop(void){
  //Handle HTTP requests
  server.handleClient();
  
  unsigned long now = millis();
  if (publish_time < now) {
    publish_time = now + 60 * 1000;

    dtostrf(readDHTTemperature(), 6, 2, tpayload); //convert function outputs to string for client.publish
    dtostrf(readDHTHumidity(), 6, 2, hpayload);

    client.publish(topic_temp, tpayload);
    client.publish(topic_hum, hpayload);
  }
}

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.