Unblocking loop when DHCP fails

  • Hi all!

    I have a problem, for which i couldn't find a solution. I'm using a Mega board, with W5510 Ethernet Module which is configured to use DHCP and MQTT.
    Board is pure light relay controller with BME280 sensors function.
    Now everything works just great when i have Ethernet connected.
    But i wanted my gateway to work more or less independent from my network. So when i disconnect Ethernet with DHCP option enabled, i got this:

    0 MCO:BGN:INIT GW,CP=R-NGA---,FQ=16,REL=255,VER=2.3.2
    60682 !GWT:TPC:DHCP FAIL
    60684 MCO:BGN:STP
    Hello MySensors-Relay1!
    120810 !GWT:TPC:DHCP FAIL
    Periodic send of Light state...
    End of periodic send of Light state...
    180931 !GWT:TPC:DHCP FAIL
    Periodic send of Light state...
    End of periodic send of Light state...
    241052 !GWT:TPC:DHCP FAIL
    Periodic send of Light state...
    End of periodic send of Light state..

    Which is great, however it looks like my script is doing something in background, and main loop() in executing very rarely, so it doesnt react on button input.
    Please have look at my code:

     * The MySensors Arduino library handles the wireless radio link and protocol
     * between your home built sensors/actuators and HA controller of choice.
     * The sensors forms a self healing radio network with optional repeaters. Each
     * repeater and gateway builds a routing tables in EEPROM which keeps track of the
     * network topology allowing messages to be routed to nodes.
     * Created by Henrik Ekblad <henrik.ekblad@mysensors.org>
     * Copyright (C) 2013-2015 Sensnology AB
     * Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors
     * Documentation: http://www.mysensors.org
     * Support Forum: http://forum.mysensors.org
     * This program is free software; you can redistribute it and/or
     * modify it under the terms of the GNU General Public License
     * version 2 as published by the Free Software Foundation.
     * Version 1.0 - Henrik Ekblad
     * Version 1.1 - AnonymousZebra
     * This is an example of using the Bosch BME280 module, which can measure temperature, humidity and airpressure, 
     * and do so really accurately, while using very little power. A 3.3v and a 5v version is available, make sure to check which one you have.
     * It communicates over the I2C protocol.
     * This script uses the BME280 library by Embedded Adventures. Download it, and place it in your Arduino library folder. 
     * https://github.com/embeddedadventures/BME280
     * Connect your sensor's powerlines, and connect your sensor to the SDA and SCL pins of your board. 
     * On Arduino Nano SDA is pin A4, and SCL is pin A5.
     * On the Ardunio Mega and Due the SDA in pin 20, and the SCL is pin 21.
     * On the Arduino Leonardo and Pro Micro 2 the SDA in pin 2, and the SCL is pin 3.
     * This script has been written in such a way that it can at the same time function as a repeater-node. 
     * It can also easily be used on battery power. Booth features can be turned on in the code below.
     * Finally, you can decide if you want the forecast feature to be turned on. This is a cool feature, 
     * but there is a catch: it also means that you are locked into taking a measurement exactly once a 
     * minute, to build up prediction data for the algorithm.
     * The reason so many variables have BME280 at the beginning, is so that it is easier to combine multiple sensors on one Arduino.
     * The BME280 datasheet: https://cdn.sparkfun.com/assets/learn_tutorials/4/1/9/BST-BME280_DS001-10.pdf
    // if you uncomment this, you can get test and debug updates about everything the sensor is doing by using the serial monitor tool.
    #define MY_DEBUG
    //#define MY_DEBUG_OTA
    //#define MY_GATEWAY_SERIAL
    #define MY_GATEWAY_W5100
    #define MY_MAC_ADDRESS 0x00, 0x08, 0xDC, 0xAA, 0xBB, 0x01
    #define MY_HOSTNAME "MySensors-Relay1"
    #define SKETCH_VERSION "1.3"
    //#define USE_STATIC_IP // Uncomment, if you don't want to use DHCP
    #ifdef USE_STATIC_IP
    #define MY_IP_ADDRESS 192,168,8,35          // Static IP Address
    #define MY_IP_SUBNET_ADDRESS 255,255,255,0  // IP Netmask
    #define MY_IP_GATEWAY_ADDRESS 192,168,8,1   // Internet (not MQTT) gateway address
    #define MY_PORT 5003
    // Set this node's subscribe and publish topic prefix
    #define MY_MQTT_PUBLISH_TOPIC_PREFIX "mysensors1-out"
    #define MY_MQTT_SUBSCRIBE_TOPIC_PREFIX "mysensors1-in"
    // Set MQTT client id
    #define MY_MQTT_CLIENT_ID "mysensors-relay1"
    // MQTT broker ip address or url. Define one or the other.
    //#define MY_CONTROLLER_URL_ADDRESS "m20.cloudmqtt.com"
    #define MY_CONTROLLER_IP_ADDRESS 192, 168, 8, 31
    //// The MQTT broker port to to open
    #define MY_PORT 1883
    #define MY_MQTT_USER "mqtt"
    #define MY_MQTT_PASSWORD "666mqtt"
    #define DEBOUNCE_INTERVAL 25                      // Debounce interval in ms
    #define COMPARE_TEMP 0                            // Send temperature only if it changed? 1 = Yes 0 = No. Can save battery.
    #define COMPARE_HUM 0                             // Send humidity only if changed? 1 = Yes 0 = No. Can save battery.
    #define COMPARE_BARO 0                            // Send barometric pressure only if changed? 1 = Yes 0 = No. Can save battery.
    #define COMPARE_DEW_POINT 0                       // Send dew point only if changed? 1 = Yes 0 = No. Can save battery.
    #define TEMPERATURE_THRESHOLD 0.1                 // Threshold when new temperature should be send to controller. Doesn't matter, if COMPARE_TEMP is set to 0
    #define HUMIDITY_THRESHOLD 0.1                    // Threshold when new humidity should be send to controller. Doesn't matter, if COMPARE_HUM is set to 0
    #define BAROMETRIC_THRESHOLD 0.1                  // Threshold when new pressure should be send to controller. Doesn't matter, if COMPARE_BARO is set to 0
    #define DEW_POINT_THRESHOLD 0.1                   // Threshold when new dew point should be send to controller. Doesn't matter, if COMPARE_DEW_POINT is set to 0
    #define PERIODIC_LIGHT_SEND 1                     // Send light states periodically? 1 = Yes, 0 = No, only when switch change.
    #define PERIODIC_LIGHT_INTERVAL   10000           // Interval between Light sends, button press is always send immediately
    #define BME_MEASUREMENTS_INTERVAL 60000           // Interval between BME280 reads, in ms
    #define BME_ALTITUDE 220                          // Change this value to your location's altitude (in m). Use your smartphone GPS to get an accurate value, or use an online map.
    #define MY_W5100_SPI_EN 10
    #include <SPI.h>                                  // A communication backbone, the Serial Peripheral Interface.
    #include <Ethernet.h>                             // Ethernet library
    #include <MySensors.h>                            // The MySensors library. Hurray!
    #include <Wire.h>                                 // Enables the Wire communication protocol.
    #include <BME280_MOD-1022.h>                      // Bosch BME280 Embedded Adventures MOD-1022 weather multi-sensor Arduino code, written originally by Embedded Adventures. https://github.com/embeddedadventures/BME280
    #include <Bounce2.h>
    // Ids of locally attached sensors. If you want to add some sensor besides to relays, add its name before SENSOR_LIGHT_START
    typedef enum {
    } sensorIds;
    typedef struct {
      uint8_t buttonPin;    // GPIO Pin ID for input button
      uint8_t relayPin;     // GPIO Pin ID for output relay
      bool inputLowActive;  // True, if buttonPin should react on GND, false if on 5V
      bool outputLowActive; // True, if ON state for relay will be GND, false if 5V
      uint8_t currentState;
      Bounce* debouncer;    // Leave it, don't care, filled at initialization
      MyMessage* message;   // Leave it, don't care, filled at initialization
    } tLightSwitch;
    static tLightSwitch m_lights[] = {
        .buttonPin = A0,
        .relayPin = 43,
    const float ALTITUDE = BME_ALTITUDE;                        
    unsigned long BME280measurementInterval = BME_MEASUREMENTS_INTERVAL;
    const float tempThreshold = TEMPERATURE_THRESHOLD;                     // How big a temperature difference has to minimally  be before an update is sent. Makes the sensor less precise, but also less jittery, and can save battery.
    const float humThreshold = HUMIDITY_THRESHOLD;                         // How big a humidity difference has to minimally be before an update is sent. Makes the sensor less precise, but also less jittery, and can save battery.
    const float presThreshold = BAROMETRIC_THRESHOLD;                        // How big a barometric difference has to minimally be before an update is sent. Makes the sensor less precise, but also less jittery, and can save battery.
    const float dewPointThreshold = DEW_POINT_THRESHOLD;                   // How big a dew point difference has to minimally be before an update is sent. Makes the sensor less precise, but also less jittery, and can save battery.
    float lastTemperature = -1;     // Stores the previous measurement, so it can be compared with a new measurement.
    float lastHumidity = -1;      // Stores the previous measurement, so it can be compared with a new measurement.
    float lastPressure = -1;      // Stores the previous measurement, so it can be compared with a new measurement.
    float lastDewPoint = -1;
    unsigned long BME280measurementSleepTime = 0;   // variable to store the calculated Sleep time if the node is battery powered.
    #define CONVERSION_FACTOR (1.0/10.0)    // used by forecast algorithm to convert from Pa to kPa, by dividing hPa by 10.
    const uint8_t m_lights_size = sizeof(m_lights)/sizeof(m_lights[0]);
    char uartBuffer[100];
    unsigned long m_lights_send_interval = PERIODIC_LIGHT_INTERVAL;
    #define RELAY_ON 0x80
    #define RELAY_OFF 0x02
    // Messages for sensors not related to light
    MyMessage temperatureMsg(SENSOR_TEMPERATURE_ID, V_TEMP);
    MyMessage humidityMsg(SENSOR_HUMIDITY_ID, V_HUM);
    MyMessage pressureMsg(SENSOR_BARO_ID, V_PRESSURE);
    MyMessage dewPointMsg(SENSOR_DEWPOINT_ID, V_TEMP);
    void printUart(const char *format, ...) {
      va_list args;
      va_start(args, format);
      vsprintf(uartBuffer, format, args);
    void switchRelay(uint8_t switchNo) {
      tLightSwitch* light = &m_lights[switchNo];
      uint8_t relayValue = light->currentState == RELAY_ON ? 1 : 0;
      if(light->outputLowActive) {
        relayValue = light->currentState == RELAY_ON ? 0 : 1;
      printUart("Switch %u Pin %u %s, setting to %d\n", switchNo, light->relayPin, light->outputLowActive ? "LowActive" : "HighActive", light->currentState == RELAY_ON ? 1 : 0);
      digitalWrite(light->relayPin, relayValue);
    void configurePins() {
      for(size_t i = 0; i < m_lights_size; ++i ) {
        tLightSwitch* light = &m_lights[i];
        pinMode(light->relayPin, OUTPUT);
        pinMode(light->buttonPin, light->inputLowActive ? INPUT_PULLUP : INPUT);
        light->currentState = RELAY_OFF;
        light->message = new MyMessage(SENSOR_LIGHT_START + i, V_LIGHT);
        light->debouncer = new Bounce();
    void setup() {
      Wire.begin(); // Wire.begin(sda, scl) // starts the wire communication protocol, used to chat with the BME280 sensor.
      Serial.begin(115200); // for serial debugging over USB.
      printUart("Hello %s!", MY_HOSTNAME);
    void presentation()  {
      // Send the sketch version information to the gateway and Controller
      sendSketchInfo(MY_HOSTNAME, SKETCH_VERSION);
      // Tell the MySensors gateway what kind of sensors this node has, and what their ID's on the node are, as defined in the code above.
      present(SENSOR_BARO_ID, S_BARO);
      present(SENSOR_HUMIDITY_ID, S_HUM);
      present(SENSOR_DEWPOINT_ID, S_TEMP);
      for(size_t i = 0; i < m_lights_size; ++i ) {
        present( SENSOR_LIGHT_START + i, S_LIGHT);
    // Compute the dew point temperature, given temperature and humidity (temp in Celcius)
    // Td = (f/100)^(1/8) * (112 + 0.9*T) + 0.1*T - 112
    static float computeDewPointTemp(float temperature, float humidity_percentage)
        return pow( humidity_percentage / 100.0, 0.125 ) * ( 112.0 + 0.9*temperature ) + 0.1 * temperature - 112.0;
    void loop() {
      // You should not change these variables:
      static unsigned long previousBME280Millis = 0;  // Used to remember the time that the BME280 sensor was asked for a measurement.
      static unsigned long previousLightMillis = 0;  // Used to remember the time that the BME280 sensor was asked for a measurement.
      unsigned long currentMillis = millis();         // The time since the sensor started, counted in milliseconds. This script tries to avoid using the Sleep function, so that it could at the same time be a MySensors repeater.
      static boolean BME280shouldAsk = true;          // This is true when the time is right for a new measurement to be made.
      static boolean BME280justAsked = false;         // This indicates whether we have just asked the sensor module for a measurement, so the receiving part of the code (part 2) should be primed. This two-part construction helps to bridge the time where the BME280 module is busy, without blocking the entire node from doing anything else (like being a repeater, or working with other connected sensor modules).
      // PART 1. If enough time has passed, a new measurement should be taken:
      if (BME280shouldAsk == true && currentMillis - previousBME280Millis >= BME280measurementInterval) {
        previousBME280Millis = currentMillis; // store the current time as the previous measurement start time.
        BME280shouldAsk = false;
        printUart("BME280 - Requesting new data from sensor module.");
        BME280.readCompensationParams();    // Need to read the NVM compensation parameters.
        // Normal mode for regular automatic samples
        BME280.writeStandbyTime(tsb_0p5ms);         // tsb = 0.5ms
        BME280.writeFilterCoefficient(fc_16);       // IIR Filter coefficient 16
        BME280.writeOversamplingPressure(os16x);    // pressure x16
        BME280.writeOversamplingTemperature(os8x);  // temperature x8
        BME280.writeOversamplingHumidity(os8x);     // humidity x8
        // As we exit part 1, in theory BME280.isMeasuring() should now be true.
        BME280justAsked = true; 
      // Part 2. This will trigger if the sensor has just been asked for a measurement, and is also just done figuring out those measurements.
      if(BME280justAsked == true && BME280.isMeasuring() == false) { // 
        BME280justAsked = false; // makes sure we don't do this part again in the next pass through the main loop.
        printUart("BME280 - Sensor module has some new values ready:");
        Serial.println("BME280 - Sensor module has some new values ready:");
        // Read out the data - must do this before calling the getxxxxx routines
        float temperature = BME280.getTemperatureMostAccurate();                    // Must get the temperature first.
        float humidity = BME280.getHumidityMostAccurate();                          // Get the humidity.
        float pressure_local = BME280.getPressureMostAccurate();                    // Get pressure at current location
        float pressure = pressure_local/pow((1.0 - ( ALTITUDE / 44330.0 )), 5.255); // Adjust to sea level pressure using user altitude
        float dewPoint = computeDewPointTemp(temperature, humidity);
        // Useful for debugging
        Serial.print("BME280 - Temperature = ");
        Serial.println(" °C");
        Serial.print("BME280 - Humidity = ");
        Serial.println(" %");
        Serial.print("BME280 - Pressure = ");
        Serial.println(" hPa");
        Serial.print("BME280 - Dewpoint = ");
        Serial.println(" °C");
        // Now, let's send the measurements to the gateway.
        // Send temperature
        if (COMPARE_TEMP == 1 && abs(temperature - lastTemperature) < tempThreshold) { // is the temperature difference bigger than the threshold?
          Serial.print(temperature - lastTemperature);
          Serial.print("- BME280 - Temperature difference too small, so not sending the new measurement to the gateway.\n");
        } else {
          Serial.print("BME280 - Sending the new temperature to the gateway.\n");
          send(temperatureMsg.set(temperature, 1));
          lastTemperature = temperature; // Save new temperatures to be able to compare in the next round.
        // Send humidity
        if (COMPARE_HUM == 1 && abs(humidity - lastHumidity) < humThreshold) { // is the humidity difference bigger than the threshold?
          Serial.print(humidity - lastHumidity);
          Serial.println("- BME280 - Humidity difference too small, so not sending the new measurement to the gateway.");
        } else {
          Serial.println("BME280 - Sending the new humidity to the gateway.");
          send(humidityMsg.set(humidity, 1));
          lastHumidity = humidity; // Save new humidity to be able to compare in the next round.
        // Send pressure
        if (COMPARE_BARO == 1 && abs(pressure - lastPressure) < presThreshold) { // is the pressure difference bigger than the threshold?
          Serial.print(pressure - lastPressure);
          Serial.println("- BME280 - Pressure difference too small, so not sending the new measurement to the gateway.");
        } else {
          Serial.println("BME280 - Sending the new pressure to the gateway.");
          send(pressureMsg.set(pressure, 1));
          lastPressure = pressure; // Save new pressure to be able to compare in the next round.
        // Send Dew-Point
        if (COMPARE_DEW_POINT == 1 && abs(dewPoint - lastDewPoint) < dewPointThreshold) { // is the pressure difference bigger than the threshold?
          Serial.print(dewPoint - lastDewPoint);
          Serial.println("- BME280 - DewPoint difference too small, so not sending the new measurement to the gateway.");
        } else {
          Serial.println("BME280 - Sending the new dew point to the gateway.");
          send(dewPointMsg.set(dewPoint, 1));
          lastDewPoint = dewPoint; // Save new pressure to be able to compare in the next round.
        Serial.println("BME280 - Measurement complete. Going to wait until next measurement.");
        BME280shouldAsk = true; // Ready for the new round.
      for( size_t i = 0; i < m_lights_size; ++i) {
        tLightSwitch* light = &m_lights[i];
        if(light->debouncer->update()) {
          if(light->debouncer->read() == HIGH) {
            printUart("Button %d pressed...", i);
            //light->currentState = !light->currentState;
            light->currentState = light->currentState == RELAY_ON ? RELAY_OFF : RELAY_ON;
            send(light->message->set(light->currentState == RELAY_ON ? 0xFF : 0x00));
      if( PERIODIC_LIGHT_SEND && currentMillis - previousLightMillis >= m_lights_send_interval ) {
        printUart("Periodic send of Light state...");
        for( size_t i = 0; i < m_lights_size; ++i) {
          previousLightMillis = currentMillis;
          tLightSwitch* light = &m_lights[i];
          send(light->message->set(light->currentState == RELAY_ON ? 0xFF : 0x00));
        printUart("End of periodic send of Light state...");
    } // end of main loop.
    void receive(const MyMessage &message) {
      // We only expect one type of message from controller. But we better check anyway.
      if (message.type == V_LIGHT) {
        printUart("Received update of %d sensor, new value %u", message.sensor, message.getBool());
        if(message.sensor >= SENSOR_LIGHT_START) {
          uint8_t lightSwitchNo = message.sensor - SENSOR_LIGHT_START;
          printUart("Updating light switch %u, new state: %u", lightSwitchNo, message.getBool());
          tLightSwitch* light = &m_lights[lightSwitchNo];
          light->currentState = message.getBool() ? RELAY_ON : RELAY_OFF;

    My question is, it is a normal situation?
    If not, could you please help me finding a problem in my script? 😞

    Thanks in advance!

  • @NeoX A quick look at your code and I would advise the following....

    Your void receive() section has too much going on there. Simply, set a variable in void receive() and move all the other stuff to void loop(). Especially do not print within void receive(), I had issues with that in the past.

    Can't help to try it out! 😉

  • Tried, but it doesn't help.
    My question is do anyone could confirm, that ethernet MQTT gateway could work without connection?

  • I believe this delay is caused by requesting the IP address from your DHCP server. This request has a timeout and the framework waits for this timeout to be completed before continuing with the rest of the code.
    Maybe you could try a static IP address instead of DHCP?

  • @electrik I've checked, indeed waiting for DHCP is one of problems.
    Tried to use Static IP (however, it's not really solution for my installation), but then framework is waiting for connection to MQTT server.
    I think it should be improved to use IRQ/Timers, i need to see through framework code

  • These timeouts depend on the Arduino framework, not on the mysensors framework I guess.

    @NeoX said in Unblocking loop when DHCP fails:

    but then framework is waiting for connection to MQTT server.

    Does your MQTT server have a IP at that moment?

  • Hi @NeoX ,
    did you found any solution for this problem?
    I also have arduino Mega2560 with W5100 ETH shield on it. Arduino board is MQTT ethernet gateway, but I also have many local sensors atached to GPIO pins (buttons, temperature sensor, etc.) and relay board to control lights.

    I'm stuck with the same problem: when there is no ethernet connectivity (ETH cable plugged out, ETH router or switch is rebooting) Arduino framework is waiting somewhere (probably in ETH driver) to get connectivity up again. This causes almost no time given to main loop and my code controlling GPIO peripherals is not executed until ETH connectivity goes back.
    I don't like it. I would like to use Mega2560 board as local controller for home automation, but it also must work in case when connectivity is gone. I though that mySensors libabry can be used on Arduino board together with other code doing local stuff.
    What do you think, should I abandon Arduino framework I write code for my controller using for example some small RTOS?

  • Unfortunately, i don't find any solution at the moment.
    i've readed mySensors source carefully, and it seems that if it's not connected to MQTT server (or doesn't have IP from DHCP), it will drop in internal loop which will continously try to connect every x seconds.
    mySensors code need to be rewritten i guess, at least part of ethernet client to not block when connection fails.

  • You can try to change the define


    Which will decrease the time the MQTT client waits when reconnecting

  • @electrik said in Unblocking loop when DHCP fails:


    Thanks for your suggestion. Unfortunately it was not enough, but I manipulated in ethernet library and right now I have enough CPU time in my main loop (reading 16 buttons state and control 15 GPIO outputs).
    Here is what I changed so far. I'm not sure about it long time stability, but it works on my desk:

    diff --git a/.pio/libdeps/megaatmega2560/Ethernet/src/Ethernet.h b/.pio/libdeps/megaatmega2560/Ethernet/src/Ethernet.h
    index 376e6c5..9d5e6b4 100644
    --- a/.pio/libdeps/megaatmega2560/Ethernet/src/Ethernet.h
    +++ b/.pio/libdeps/megaatmega2560/Ethernet/src/Ethernet.h
    @@ -213,8 +213,8 @@ public:
     class EthernetClient : public Client {
    -	EthernetClient() : sockindex(MAX_SOCK_NUM), _timeout(1000) { }
    -	EthernetClient(uint8_t s) : sockindex(s), _timeout(1000) { }
    +	EthernetClient() : sockindex(MAX_SOCK_NUM), _timeout(5) { }
    +	EthernetClient(uint8_t s) : sockindex(s), _timeout(5) { }
     	uint8_t status();
     	virtual int connect(IPAddress ip, uint16_t port);
    diff --git a/.pio/libdeps/megaatmega2560/MySensors/core/MyGatewayTransportMQTTClient.cpp b/.pio/libdeps/megaatmega2560/MySensors/core/MyGatewayTransportMQTTClient.cpp
    index 12cfd67..754ae03 100644
    --- a/.pio/libdeps/megaatmega2560/MySensors/core/MyGatewayTransportMQTTClient.cpp
    +++ b/.pio/libdeps/megaatmega2560/MySensors/core/MyGatewayTransportMQTTClient.cpp
    @@ -155,7 +155,7 @@ bool reconnectMQTT(void)
     		return true;
    -	delay(1000);
    +	delay(1);
     	return false;
    @@ -190,7 +190,7 @@ bool gatewayTransportConnect(void)
     	              Ethernet.localIP()[1], Ethernet.localIP()[2], Ethernet.localIP()[3]);
     	// give the Ethernet interface a second to initialize
    -	delay(1000);
    +	delay(10);
     	return true;

Log in to reply

Suggested Topics

  • 9
  • 4
  • 2
  • 6
  • 3
  • 6
  • 2