[GH-ISSUE #801] Example - my approach to including wifimanager into the code #672

Open
opened 2026-02-28 01:26:30 +03:00 by kerem · 8 comments
Owner

Originally created by @TinajaLabs on GitHub (Jan 8, 2019).
Original GitHub issue: https://github.com/tzapu/WiFiManager/issues/801

Showing this here for feedback. I love wifimanager but I find it difficult to intersperse the various pieces of wifiManager code into my own code.

This is an example that I use for a tip-cup rain gauge. The wifiManager part allows a user to set the local wifi station as well as the mqtt server (node-red) and part of the mqtt topic for location info.

The main parts of wifimanager have been broken into the following methods which make it easier for me to set up for other sensors. I'm including features like On Demand button, reset with long press down, SPIFFS.
I would like to know if I'm covering all the bases properly or if there's a better way to do this. Not an expert with the Arduino C-like code:

  • saveConfigCallback
  • runWifiManager
  • loadConfigFile
  • saveConfigFile

The thought here is that perhaps there could be some examples that show these pieces as methods rather than large chunks of code in the setup or loop methods.

Also, as I mentioned, I also collect some location info in the wifiManager page which is to be used to create the appropriate mqtt topics when using the sensors in different locations. Typical topics might look like this:

  • farm/barn/fenceline/WemosD1Mini/rain/state/value
  • farm/barn/fenceline/WemosD1Mini/rssi/state/value

The first part (farm/barn/fenceline) is collected by a wifiManager parameter and then constructed into the final topic string in a separate method, setRainTopics. I would like to know if this is a reasonable approach or maybe a better way.

Thanks for any tips,
Chris.

/*
   TinajaLabs.com
  ---------------------------------------------------------------
   wfmt-rainguage

   Attributes:
   - WifiManager
   - FS
   - MQTT

   - RSSI: dbm
   - davis rain guage: tip bucket rain guage
     outputs triggers indicating the bucket has tipped out 0.01 inches of rain

   Arduino IDE settings:

    Board: Feather HUZZAH ESP8266
    Flash Size: 1M (64k SPIFFS)
    lwIPVariant: v2 Lower Memory
    VTables: Flash
    CPU Frequency: 80MHz
    Upload Speed: 230400
    Erase Flash: Only Sketch
    Port: dev/ttyUSB0, dev/ttyUSB1

    Board: LOLIN (WEMOS) D1 mini Lite
    Flash Size: 1M (64k SPIFFS)
    lwIPVariant: v2 Lower Memory
    VTables: Flash
    CPU Frequency: 80MHz
    Upload Speed: 230400
    Erase Flash: Only Sketch
    Port: dev/ttyUSB0, dev/ttyUSB1

  2018.12.02 - updated new soldered rain sensor board hooked up to a Davis 6465 Rain guage
  https://www.davisinstruments.com/product_documents/weather/spec_sheets/6463_6465_SS.pdf
  
  ---------------------------------------------------------------
*/

#include <FS.h>           //this needs to be first, or it all crashes and burns...
#include <ESP8266WiFi.h>  //https://github.com/esp8266/Arduino
#include <DNSServer.h>
#include <WiFiManager.h>  //https://github.com/tzapu/WiFiManager
#include <ESP8266WebServer.h> //Local WebServer used to serve the configuration portal
#include <ArduinoJson.h>  //https://github.com/bblanchon/ArduinoJson
#include <Adafruit_Sensor.h>  // Adafruit Unified Sensor Driver - https://github.com/adafruit/Adafruit_Sensor

// -------------------------- pubsub for mqtt
#include <PubSubClient.h>
#ifdef ESP32
#include <SPIFFS.h>
#endif



// ========================== for this system device


// -------------------------- for wemos d1 mini - WeMos.cc
//static const uint8_t D0   = 16;
//static const uint8_t D1   = 5;
//static const uint8_t D2   = 4;
//static const uint8_t D3   = 0;
//static const uint8_t D4   = 2;
//static const uint8_t D5   = 14;
//static const uint8_t D6   = 12;
//static const uint8_t D7   = 13;
//static const uint8_t D8   = 15;
//static const uint8_t D9   = 3;
//static const uint8_t D10  = 1;

#define TRIGGER_PIN   0                 // for wifimanager, D3, GPIO0, on wemos d1 mini
#define INTERRUPT_PIN 4                 // for rain gauge, D2, GPIO04, on wemos d1 mini
#define MQTT_LED      2                 // D4, GPIO2, on wemos d1 mini
char topic_device[20] = "WemosD1Mini/"; // defines specific device



// ========================== specific sensor configs

// -------------------------- RSSI sensor value
long rssiPreviousMillis = 0;
long rssiInterval = 5000; // every 5 seconds
char chipID[10];
char rssi_base[60] = "rssi/state/value";
char rssi_topic[100];



// -------------------------- for rain guage
const unsigned int DEBOUNCE_TIME = 50;
const float BUCKET_AMT = 0.01;

volatile unsigned long rainTips = 0;
volatile unsigned long rainInches = 0;
volatile unsigned long last_interrupt_time = 0;

long lastPubSensorRain = 0;
unsigned long delayTimeSensorRain = 5000;

char rain_base[60] = "rain/state/value";
char rain_topic[100];



// -------------------------- for mqtt
WiFiClient espClient;
PubSubClient mqttClient(espClient);

long lastMqttReconnectAttempt = 0;
char mqtt_server[40] = "192.168.0.254"; // for variables we'll edit in the wifimanager


// -------------------------- for wifiManager
bool shouldSaveConfig = false;

// to save settings, Spiffs, FS
const char* CONFIG_FILE = "/config.json";


// -------------------------- generic topic strings

// topic_loc is for the location prefix for all of the topics - it is configurable
// the middle is injected with the chipid
// the rest are suffixes for the various pub/sub entities

char topic_loc[30] = "farm/house/livingroom/";  // configurable in wifiManager

char publ_base[60] = "publish/state/value";
char subs_base[60] = "subscribe/state/value";
char subs_topic[100];
char publ_topic[100];


// Function Prototypes
bool loadConfigFile();
bool saveConfigFile();


// ==========================================================
// SETUP ====================================================
void setup() {

  Serial.begin(57600);
  Serial.println("\n\n=== Starting Tinaja Device ===");
  Serial.print("=== "); Serial.println(topic_loc);
  Serial.print("=== "); Serial.println(topic_device);

  // set the pins to be used for buttons, LEDs, etc.
  pinMode(INTERRUPT_PIN, INPUT_PULLUP);
  pinMode(TRIGGER_PIN, INPUT);  // set the pin to trigger wifiManager
  pinMode(MQTT_LED, OUTPUT);    // Initialize the MQTT_LED pin as an output
  digitalWrite(MQTT_LED, LOW);  // turn on LED during setup

  runWifiManager("auto");

  // ----------------------------- set mqtt topics
  setChipId();

  setRainTopics();

  // ----------------------------- initialize sensors

  // set up rain guage Interrupt
  attachInterrupt(digitalPinToInterrupt(INTERRUPT_PIN), rainInterrupt, FALLING);
  // attachInterrupt(INTERRUPT_PIN, rainInterrupt, FALLING);

  // ----------------------------- for mqtt client
  mqttClient.setServer(mqtt_server, 1883);
  mqttClient.setCallback(mqttCallback);


  Serial.println("=== Finished setup ===");
  Serial.println("");
  digitalWrite(MQTT_LED, HIGH);  // turn off LED after setup

}



// ==========================================================
// LOOP =====================================================
void loop() {

  if (!mqttClient.connected()) {
    Serial.println("mqttClient not connected...");
    digitalWrite(MQTT_LED, LOW);  // turn on LED to indicate disconnected
    long now = millis();
    if (now - lastMqttReconnectAttempt > 10000) {
      lastMqttReconnectAttempt = now;
      // Attempt to reconnect
      if (mqttReconnect()) {
        lastMqttReconnectAttempt = 0;
      }
    }
    digitalWrite(MQTT_LED, HIGH);  // turn off LED
  } else {
    // Client connected
    mqttClient.loop();
  }


  // if config reset pin has been pushed down
  if ( digitalRead(TRIGGER_PIN) == LOW ) {
    delay(50); // poor mans debounce, not recommended for production code
    if ( digitalRead(TRIGGER_PIN) == LOW ) {
      delay(3000);
      // if config reset pin has been pushed down for 3 seconds, reset/restart
      if ( digitalRead(TRIGGER_PIN) == LOW ) {
        Serial.println("Trigger pin low for 3+ seconds. Resetting device...");
        runWifiManager("reset");     // < <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< RESET CAN HAPPEN
      } else {
        Serial.println("Starting config portal...");
        runWifiManager("auto");
      }
    }
  }


  // ----------------------------------------------------------
  // put the main code below, to run repeatedly:


  // publish msgs about the sensor stats
  rssiCheck();
  publishRainfall();

  delay(50); // longer than 50 if mqttClient is often not connected...
  digitalWrite(MQTT_LED, HIGH);  // turn off LED

}





// ==========================================================
void rainInterrupt() {
  // runs in the Interrupt as defined in setup

  if ((millis() - last_interrupt_time) > DEBOUNCE_TIME ) { // debounce of sensor signal
    rainTips++;
    last_interrupt_time = millis();
  }

}


void publishRainfall() {

  Serial.print(" rain tips: "); Serial.println(rainTips);

  long now = millis();
  if (now - lastPubSensorRain > delayTimeSensorRain) {
    digitalWrite(MQTT_LED, LOW);  // turn on LED during setup
    
    lastPubSensorRain = now;
    float thisTotal = rainTips * BUCKET_AMT;
    Serial.print("mqtt pub - Rainfall inches: "); Serial.println(thisTotal);
    mqttClient.publish(rain_topic, String(thisTotal).c_str());
    rainTips = 0;

    digitalWrite(MQTT_LED, HIGH);  // turn on LED during setup
  }
}



// ==========================================================
void rssiCheck() {
  if (millis() - rssiPreviousMillis > rssiInterval) {
    digitalWrite(MQTT_LED, LOW);  // turn on LED during setup

    int32_t rssiValue = WiFi.RSSI();
    char rssiMsg[50];
    snprintf (rssiMsg, 75, "%ld", WiFi.RSSI());
    Serial.print("mqtt pub - signal strength (RSSI): "); Serial.println(rssiValue);
    mqttClient.publish(rssi_topic, rssiMsg);
    rssiPreviousMillis = millis();

    digitalWrite(MQTT_LED, HIGH);  // turn on LED during setup
  }
}



// ==========================================================
void saveConfigCallback () {
  //callback notifying us of the need to save config
  Serial.println(" --- Should save config");
  shouldSaveConfig = true;
}


// ==========================================================
void runWifiManager(String mode) {

  //Local intialization. Once its business is done, there is no need to keep it around
  WiFiManager wifiManager;

  wifiManager.setSaveConfigCallback(saveConfigCallback);

  if (mode == "reset") {
    Serial.println("Resetting/restarting the current configuration...");
    wifiManager.resetSettings(); // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< RESET CAN HAPPEN
    ESP.restart();
  }

  // load the settings from the file system (FS), from setupSpiffs
  loadConfigFile();

  Serial.println("starting wifiManager");

  // set up some additional parameters
  WiFiManagerParameter custom_mqtt_server("mqtt_server", "MQTT server addr", mqtt_server, 40);
  WiFiManagerParameter custom_topic_loc("topic_loc", "Topic Location", topic_loc, 30);
  wifiManager.addParameter(&custom_mqtt_server);
  wifiManager.addParameter(&custom_topic_loc);


  wifiManager.setConfigPortalTimeout(60); // sets timeout until configuration portal gets turned off
  wifiManager.setConnectTimeout(5); // how long to try to connect for before continuing


  // TODO: add password to wifimanager login
  if (mode == "auto") {
    Serial.println("    Attempting automatic connection");
    if (!wifiManager.autoConnect("TinajaAutoAP")) {
      Serial.println("failed to connect and hit timeout");
      delay(3000);
      //reset and try again, or maybe put it to deep sleep
      ESP.reset();
      delay(5000);
    }
  } else {
    Serial.println("Attempting on demand connection");
    if (!wifiManager.startConfigPortal("TinajaOnDemandAP")) {
      Serial.println("failed to connect and hit timeout");
      delay(3000);
      //reset and try again, or maybe put it to deep sleep
      ESP.reset();
      delay(5000);
    }
  }

  strcpy(mqtt_server, custom_mqtt_server.getValue());
  strcpy(topic_loc, custom_topic_loc.getValue());

  // ----------------------------- save the custom parameters to FS
  if (shouldSaveConfig) {
    saveConfigFile();
    shouldSaveConfig = false;
  }

  //if you get here you have connected to the WiFi
  Serial.print("    OK. Device is running with IP address: ");
  Serial.println(WiFi.localIP());
  // Serial.println(WiFi.SSID());
  // Serial.println(WiFi.psk());
}


// ==========================================================
void mqttCallback(char* topic, byte* payload, unsigned int length) {
  Serial.print("Message arrived [");
  Serial.print(topic);
  Serial.print("] ");
  for (int i = 0; i < length; i++) {
    Serial.print((char)payload[i]);
  }
  Serial.println();

  // Switch on the LED if an 1 was received as first character
  if ((char)payload[0] == '1') {
    digitalWrite(MQTT_LED, LOW);   // Turn the LED on (Note that LOW is the voltage level
    // but actually the LED is on; this is because
    // it is acive low on the ESP-01)
  } else {
    digitalWrite(MQTT_LED, HIGH);  // Turn the LED off by making the voltage HIGH
  }
}


// ==========================================================
bool mqttReconnect() {

  // Loop until we're reconnected
  Serial.print("Attempting connection to MQTT server: " ); Serial.print(mqtt_server); Serial.print("... ");

  // Create a random client ID
  String clientId = "tinajaMQTTClient-";
  clientId += String(random(0xffff), HEX);

  // Attempt to connect
  if (mqttClient.connect(clientId.c_str())) {
    Serial.println(" Connected.");

    // Once connected, publish an announcement...
    mqttClient.publish(publ_topic, chipID);
    Serial.print("  published to topic: "); Serial.print(publ_topic); Serial.print(".");
    Serial.print(" Payload is chip Id: "); Serial.println(chipID);

    // ... and resubscribe
    mqttClient.subscribe(subs_topic);
    Serial.print(" subscribed to topic: "); Serial.println(subs_topic);
  } else {
    Serial.println(" Not connected.");
  }

  Serial.println();
  Serial.println("=========================================================");
  Serial.println();

  return mqttClient.connected();
}


// ==========================================================
bool loadConfigFile() {

  //clean FS, for testing
  // SPIFFS.format(); // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

  //read configuration from FS json
  Serial.println("");
  Serial.print("mounting FS...");

  if (SPIFFS.begin()) {
    Serial.println(" mounted.");
    if (SPIFFS.exists(CONFIG_FILE)) {
      //file exists, reading and loading
      Serial.print("reading config file... ");
      File configFile = SPIFFS.open(CONFIG_FILE, "r");
      if (configFile) {
        Serial.print(" opened and retrieved data: ");
        Serial.println("");
        size_t size = configFile.size();

        // Allocate a buffer to store contents of the file.
        std::unique_ptr<char[]> buf(new char[size]);

        configFile.readBytes(buf.get(), size);
        DynamicJsonBuffer jsonBuffer;
        JsonObject& json = jsonBuffer.parseObject(buf.get());
        json.printTo(Serial);

        if (json.success()) {

          // set up the extra parameters
          if (json.containsKey("mqtt_server")) {
            strcpy(mqtt_server, json["mqtt_server"]);
          }
          if (json.containsKey("topic_loc")) {
            strcpy(topic_loc, json["topic_loc"]);
          }

          Serial.println("");
          Serial.println("successfully loaded json config");
          Serial.println("");

        } else {
          Serial.println(" >>> failed to load json config <<<");
        }
      }
    }
  } else {
    Serial.println(" >>> failed to mount FS <<<");
  }
  //end read
}


// ==========================================================
bool saveConfigFile() {

  // write configs to local file store

  Serial.println("Saving config...");
  DynamicJsonBuffer jsonBuffer;
  JsonObject& json = jsonBuffer.createObject();

  json["mqtt_server"] = mqtt_server;
  json["topic_loc"] = topic_loc;

  // Open file for writing
  File f = SPIFFS.open(CONFIG_FILE, "w");
  if (!f) {
    Serial.println("Failed to open config file for saving");
    return false;
  }

  json.prettyPrintTo(Serial);
  // Write data to file and close it
  json.printTo(f);
  f.close();

  Serial.println("\nConfig file was successfully saved");
  return true;
}


// ========================== set up the topics for this device

void setRainTopics() {

  strcat(rssi_topic, topic_loc); strcat(rssi_topic, topic_device); strcat(rssi_topic, chipID); strcat(rssi_topic, "/"); strcat(rssi_topic, "device/"); strcat(rssi_topic, rssi_base);
  strcat(publ_topic, topic_loc); strcat(publ_topic, topic_device); strcat(publ_topic, chipID); strcat(publ_topic, "/"); strcat(publ_topic, "device/"); strcat(publ_topic, publ_base);
  strcat(subs_topic, topic_loc); strcat(subs_topic, topic_device); strcat(subs_topic, chipID); strcat(subs_topic, "/"); strcat(subs_topic, "device/"); strcat(subs_topic, subs_base);

  strcat(rain_topic, topic_loc); strcat(rain_topic, topic_device); strcat(rain_topic, chipID); strcat(rain_topic, "/"); strcat(rain_topic, "rainguage/"); strcat(rain_topic, rain_base);

  Serial.println("concatenated topics: ");
  Serial.println(rssi_topic); // Serial.println(" = " + strlen(rssi_topic));
  Serial.println(publ_topic); // Serial.println(" = " + strlen(publ_topic));
  Serial.println(subs_topic); // Serial.println(" = " + strlen(subs_topic));
  Serial.println(rain_topic); // Serial.println(" = " + strlen(rain_topic));

  Serial.println("");
}


// ========================== set up the topics for this device
void setChipId() {
  snprintf(chipID, 10, "%ld", ESP.getChipId());
  Serial.println(""); Serial.print("Device chip id: "); Serial.println(chipID);
  // return chipID;
}

Originally created by @TinajaLabs on GitHub (Jan 8, 2019). Original GitHub issue: https://github.com/tzapu/WiFiManager/issues/801 Showing this here for feedback. I love wifimanager but I find it difficult to intersperse the various pieces of wifiManager code into my own code. This is an example that I use for a tip-cup rain gauge. The wifiManager part allows a user to set the local wifi station as well as the mqtt server (node-red) and part of the mqtt topic for location info. The main parts of wifimanager have been broken into the following methods which make it easier for me to set up for other sensors. I'm including features like `On Demand button`, `reset with long press down`, `SPIFFS`. `I would like to know if I'm covering all the bases properly or if there's a better way to do this`. Not an expert with the Arduino C-like code: * saveConfigCallback * runWifiManager * loadConfigFile * saveConfigFile `The thought here is that perhaps there could be some examples that show these pieces as methods rather than large chunks of code in the setup or loop methods.` Also, as I mentioned, I also collect some location info in the wifiManager page which is to be used to create the appropriate mqtt topics when using the sensors in different locations. Typical topics might look like this: * farm/barn/fenceline/WemosD1Mini/rain/state/value * farm/barn/fenceline/WemosD1Mini/rssi/state/value The first part (farm/barn/fenceline) is collected by a wifiManager parameter and then constructed into the final topic string in a separate method, setRainTopics. `I would like to know if this is a reasonable approach or maybe a better way.` Thanks for any tips, Chris. ```C /* TinajaLabs.com --------------------------------------------------------------- wfmt-rainguage Attributes: - WifiManager - FS - MQTT - RSSI: dbm - davis rain guage: tip bucket rain guage outputs triggers indicating the bucket has tipped out 0.01 inches of rain Arduino IDE settings: Board: Feather HUZZAH ESP8266 Flash Size: 1M (64k SPIFFS) lwIPVariant: v2 Lower Memory VTables: Flash CPU Frequency: 80MHz Upload Speed: 230400 Erase Flash: Only Sketch Port: dev/ttyUSB0, dev/ttyUSB1 Board: LOLIN (WEMOS) D1 mini Lite Flash Size: 1M (64k SPIFFS) lwIPVariant: v2 Lower Memory VTables: Flash CPU Frequency: 80MHz Upload Speed: 230400 Erase Flash: Only Sketch Port: dev/ttyUSB0, dev/ttyUSB1 2018.12.02 - updated new soldered rain sensor board hooked up to a Davis 6465 Rain guage https://www.davisinstruments.com/product_documents/weather/spec_sheets/6463_6465_SS.pdf --------------------------------------------------------------- */ #include <FS.h> //this needs to be first, or it all crashes and burns... #include <ESP8266WiFi.h> //https://github.com/esp8266/Arduino #include <DNSServer.h> #include <WiFiManager.h> //https://github.com/tzapu/WiFiManager #include <ESP8266WebServer.h> //Local WebServer used to serve the configuration portal #include <ArduinoJson.h> //https://github.com/bblanchon/ArduinoJson #include <Adafruit_Sensor.h> // Adafruit Unified Sensor Driver - https://github.com/adafruit/Adafruit_Sensor // -------------------------- pubsub for mqtt #include <PubSubClient.h> #ifdef ESP32 #include <SPIFFS.h> #endif // ========================== for this system device // -------------------------- for wemos d1 mini - WeMos.cc //static const uint8_t D0 = 16; //static const uint8_t D1 = 5; //static const uint8_t D2 = 4; //static const uint8_t D3 = 0; //static const uint8_t D4 = 2; //static const uint8_t D5 = 14; //static const uint8_t D6 = 12; //static const uint8_t D7 = 13; //static const uint8_t D8 = 15; //static const uint8_t D9 = 3; //static const uint8_t D10 = 1; #define TRIGGER_PIN 0 // for wifimanager, D3, GPIO0, on wemos d1 mini #define INTERRUPT_PIN 4 // for rain gauge, D2, GPIO04, on wemos d1 mini #define MQTT_LED 2 // D4, GPIO2, on wemos d1 mini char topic_device[20] = "WemosD1Mini/"; // defines specific device // ========================== specific sensor configs // -------------------------- RSSI sensor value long rssiPreviousMillis = 0; long rssiInterval = 5000; // every 5 seconds char chipID[10]; char rssi_base[60] = "rssi/state/value"; char rssi_topic[100]; // -------------------------- for rain guage const unsigned int DEBOUNCE_TIME = 50; const float BUCKET_AMT = 0.01; volatile unsigned long rainTips = 0; volatile unsigned long rainInches = 0; volatile unsigned long last_interrupt_time = 0; long lastPubSensorRain = 0; unsigned long delayTimeSensorRain = 5000; char rain_base[60] = "rain/state/value"; char rain_topic[100]; // -------------------------- for mqtt WiFiClient espClient; PubSubClient mqttClient(espClient); long lastMqttReconnectAttempt = 0; char mqtt_server[40] = "192.168.0.254"; // for variables we'll edit in the wifimanager // -------------------------- for wifiManager bool shouldSaveConfig = false; // to save settings, Spiffs, FS const char* CONFIG_FILE = "/config.json"; // -------------------------- generic topic strings // topic_loc is for the location prefix for all of the topics - it is configurable // the middle is injected with the chipid // the rest are suffixes for the various pub/sub entities char topic_loc[30] = "farm/house/livingroom/"; // configurable in wifiManager char publ_base[60] = "publish/state/value"; char subs_base[60] = "subscribe/state/value"; char subs_topic[100]; char publ_topic[100]; // Function Prototypes bool loadConfigFile(); bool saveConfigFile(); // ========================================================== // SETUP ==================================================== void setup() { Serial.begin(57600); Serial.println("\n\n=== Starting Tinaja Device ==="); Serial.print("=== "); Serial.println(topic_loc); Serial.print("=== "); Serial.println(topic_device); // set the pins to be used for buttons, LEDs, etc. pinMode(INTERRUPT_PIN, INPUT_PULLUP); pinMode(TRIGGER_PIN, INPUT); // set the pin to trigger wifiManager pinMode(MQTT_LED, OUTPUT); // Initialize the MQTT_LED pin as an output digitalWrite(MQTT_LED, LOW); // turn on LED during setup runWifiManager("auto"); // ----------------------------- set mqtt topics setChipId(); setRainTopics(); // ----------------------------- initialize sensors // set up rain guage Interrupt attachInterrupt(digitalPinToInterrupt(INTERRUPT_PIN), rainInterrupt, FALLING); // attachInterrupt(INTERRUPT_PIN, rainInterrupt, FALLING); // ----------------------------- for mqtt client mqttClient.setServer(mqtt_server, 1883); mqttClient.setCallback(mqttCallback); Serial.println("=== Finished setup ==="); Serial.println(""); digitalWrite(MQTT_LED, HIGH); // turn off LED after setup } // ========================================================== // LOOP ===================================================== void loop() { if (!mqttClient.connected()) { Serial.println("mqttClient not connected..."); digitalWrite(MQTT_LED, LOW); // turn on LED to indicate disconnected long now = millis(); if (now - lastMqttReconnectAttempt > 10000) { lastMqttReconnectAttempt = now; // Attempt to reconnect if (mqttReconnect()) { lastMqttReconnectAttempt = 0; } } digitalWrite(MQTT_LED, HIGH); // turn off LED } else { // Client connected mqttClient.loop(); } // if config reset pin has been pushed down if ( digitalRead(TRIGGER_PIN) == LOW ) { delay(50); // poor mans debounce, not recommended for production code if ( digitalRead(TRIGGER_PIN) == LOW ) { delay(3000); // if config reset pin has been pushed down for 3 seconds, reset/restart if ( digitalRead(TRIGGER_PIN) == LOW ) { Serial.println("Trigger pin low for 3+ seconds. Resetting device..."); runWifiManager("reset"); // < <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< RESET CAN HAPPEN } else { Serial.println("Starting config portal..."); runWifiManager("auto"); } } } // ---------------------------------------------------------- // put the main code below, to run repeatedly: // publish msgs about the sensor stats rssiCheck(); publishRainfall(); delay(50); // longer than 50 if mqttClient is often not connected... digitalWrite(MQTT_LED, HIGH); // turn off LED } // ========================================================== void rainInterrupt() { // runs in the Interrupt as defined in setup if ((millis() - last_interrupt_time) > DEBOUNCE_TIME ) { // debounce of sensor signal rainTips++; last_interrupt_time = millis(); } } void publishRainfall() { Serial.print(" rain tips: "); Serial.println(rainTips); long now = millis(); if (now - lastPubSensorRain > delayTimeSensorRain) { digitalWrite(MQTT_LED, LOW); // turn on LED during setup lastPubSensorRain = now; float thisTotal = rainTips * BUCKET_AMT; Serial.print("mqtt pub - Rainfall inches: "); Serial.println(thisTotal); mqttClient.publish(rain_topic, String(thisTotal).c_str()); rainTips = 0; digitalWrite(MQTT_LED, HIGH); // turn on LED during setup } } // ========================================================== void rssiCheck() { if (millis() - rssiPreviousMillis > rssiInterval) { digitalWrite(MQTT_LED, LOW); // turn on LED during setup int32_t rssiValue = WiFi.RSSI(); char rssiMsg[50]; snprintf (rssiMsg, 75, "%ld", WiFi.RSSI()); Serial.print("mqtt pub - signal strength (RSSI): "); Serial.println(rssiValue); mqttClient.publish(rssi_topic, rssiMsg); rssiPreviousMillis = millis(); digitalWrite(MQTT_LED, HIGH); // turn on LED during setup } } // ========================================================== void saveConfigCallback () { //callback notifying us of the need to save config Serial.println(" --- Should save config"); shouldSaveConfig = true; } // ========================================================== void runWifiManager(String mode) { //Local intialization. Once its business is done, there is no need to keep it around WiFiManager wifiManager; wifiManager.setSaveConfigCallback(saveConfigCallback); if (mode == "reset") { Serial.println("Resetting/restarting the current configuration..."); wifiManager.resetSettings(); // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< RESET CAN HAPPEN ESP.restart(); } // load the settings from the file system (FS), from setupSpiffs loadConfigFile(); Serial.println("starting wifiManager"); // set up some additional parameters WiFiManagerParameter custom_mqtt_server("mqtt_server", "MQTT server addr", mqtt_server, 40); WiFiManagerParameter custom_topic_loc("topic_loc", "Topic Location", topic_loc, 30); wifiManager.addParameter(&custom_mqtt_server); wifiManager.addParameter(&custom_topic_loc); wifiManager.setConfigPortalTimeout(60); // sets timeout until configuration portal gets turned off wifiManager.setConnectTimeout(5); // how long to try to connect for before continuing // TODO: add password to wifimanager login if (mode == "auto") { Serial.println(" Attempting automatic connection"); if (!wifiManager.autoConnect("TinajaAutoAP")) { Serial.println("failed to connect and hit timeout"); delay(3000); //reset and try again, or maybe put it to deep sleep ESP.reset(); delay(5000); } } else { Serial.println("Attempting on demand connection"); if (!wifiManager.startConfigPortal("TinajaOnDemandAP")) { Serial.println("failed to connect and hit timeout"); delay(3000); //reset and try again, or maybe put it to deep sleep ESP.reset(); delay(5000); } } strcpy(mqtt_server, custom_mqtt_server.getValue()); strcpy(topic_loc, custom_topic_loc.getValue()); // ----------------------------- save the custom parameters to FS if (shouldSaveConfig) { saveConfigFile(); shouldSaveConfig = false; } //if you get here you have connected to the WiFi Serial.print(" OK. Device is running with IP address: "); Serial.println(WiFi.localIP()); // Serial.println(WiFi.SSID()); // Serial.println(WiFi.psk()); } // ========================================================== void mqttCallback(char* topic, byte* payload, unsigned int length) { Serial.print("Message arrived ["); Serial.print(topic); Serial.print("] "); for (int i = 0; i < length; i++) { Serial.print((char)payload[i]); } Serial.println(); // Switch on the LED if an 1 was received as first character if ((char)payload[0] == '1') { digitalWrite(MQTT_LED, LOW); // Turn the LED on (Note that LOW is the voltage level // but actually the LED is on; this is because // it is acive low on the ESP-01) } else { digitalWrite(MQTT_LED, HIGH); // Turn the LED off by making the voltage HIGH } } // ========================================================== bool mqttReconnect() { // Loop until we're reconnected Serial.print("Attempting connection to MQTT server: " ); Serial.print(mqtt_server); Serial.print("... "); // Create a random client ID String clientId = "tinajaMQTTClient-"; clientId += String(random(0xffff), HEX); // Attempt to connect if (mqttClient.connect(clientId.c_str())) { Serial.println(" Connected."); // Once connected, publish an announcement... mqttClient.publish(publ_topic, chipID); Serial.print(" published to topic: "); Serial.print(publ_topic); Serial.print("."); Serial.print(" Payload is chip Id: "); Serial.println(chipID); // ... and resubscribe mqttClient.subscribe(subs_topic); Serial.print(" subscribed to topic: "); Serial.println(subs_topic); } else { Serial.println(" Not connected."); } Serial.println(); Serial.println("========================================================="); Serial.println(); return mqttClient.connected(); } // ========================================================== bool loadConfigFile() { //clean FS, for testing // SPIFFS.format(); // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< //read configuration from FS json Serial.println(""); Serial.print("mounting FS..."); if (SPIFFS.begin()) { Serial.println(" mounted."); if (SPIFFS.exists(CONFIG_FILE)) { //file exists, reading and loading Serial.print("reading config file... "); File configFile = SPIFFS.open(CONFIG_FILE, "r"); if (configFile) { Serial.print(" opened and retrieved data: "); Serial.println(""); size_t size = configFile.size(); // Allocate a buffer to store contents of the file. std::unique_ptr<char[]> buf(new char[size]); configFile.readBytes(buf.get(), size); DynamicJsonBuffer jsonBuffer; JsonObject& json = jsonBuffer.parseObject(buf.get()); json.printTo(Serial); if (json.success()) { // set up the extra parameters if (json.containsKey("mqtt_server")) { strcpy(mqtt_server, json["mqtt_server"]); } if (json.containsKey("topic_loc")) { strcpy(topic_loc, json["topic_loc"]); } Serial.println(""); Serial.println("successfully loaded json config"); Serial.println(""); } else { Serial.println(" >>> failed to load json config <<<"); } } } } else { Serial.println(" >>> failed to mount FS <<<"); } //end read } // ========================================================== bool saveConfigFile() { // write configs to local file store Serial.println("Saving config..."); DynamicJsonBuffer jsonBuffer; JsonObject& json = jsonBuffer.createObject(); json["mqtt_server"] = mqtt_server; json["topic_loc"] = topic_loc; // Open file for writing File f = SPIFFS.open(CONFIG_FILE, "w"); if (!f) { Serial.println("Failed to open config file for saving"); return false; } json.prettyPrintTo(Serial); // Write data to file and close it json.printTo(f); f.close(); Serial.println("\nConfig file was successfully saved"); return true; } // ========================== set up the topics for this device void setRainTopics() { strcat(rssi_topic, topic_loc); strcat(rssi_topic, topic_device); strcat(rssi_topic, chipID); strcat(rssi_topic, "/"); strcat(rssi_topic, "device/"); strcat(rssi_topic, rssi_base); strcat(publ_topic, topic_loc); strcat(publ_topic, topic_device); strcat(publ_topic, chipID); strcat(publ_topic, "/"); strcat(publ_topic, "device/"); strcat(publ_topic, publ_base); strcat(subs_topic, topic_loc); strcat(subs_topic, topic_device); strcat(subs_topic, chipID); strcat(subs_topic, "/"); strcat(subs_topic, "device/"); strcat(subs_topic, subs_base); strcat(rain_topic, topic_loc); strcat(rain_topic, topic_device); strcat(rain_topic, chipID); strcat(rain_topic, "/"); strcat(rain_topic, "rainguage/"); strcat(rain_topic, rain_base); Serial.println("concatenated topics: "); Serial.println(rssi_topic); // Serial.println(" = " + strlen(rssi_topic)); Serial.println(publ_topic); // Serial.println(" = " + strlen(publ_topic)); Serial.println(subs_topic); // Serial.println(" = " + strlen(subs_topic)); Serial.println(rain_topic); // Serial.println(" = " + strlen(rain_topic)); Serial.println(""); } // ========================== set up the topics for this device void setChipId() { snprintf(chipID, 10, "%ld", ESP.getChipId()); Serial.println(""); Serial.print("Device chip id: "); Serial.println(chipID); // return chipID; } ```
Author
Owner

@tablatronix commented on GitHub (Jan 8, 2019):

I use the development branch DEV example as my template for sample code, I have also added some better examples of use cases.

code is a bit to read, and not syntax highlighted

<!-- gh-comment-id:452340406 --> @tablatronix commented on GitHub (Jan 8, 2019): I use the development branch DEV example as my template for sample code, I have also added some better examples of use cases. code is a bit to read, and not syntax highlighted
Author
Owner

@TinajaLabs commented on GitHub (Jan 8, 2019):

OK. Syntax highlighting added but I understand... it is a bit to read. ;)

The main point I was making was to come up with blocks of method code to call from setup() or loop(), with button interrupt, so as to minimize the detailed code into those methods.

I have existing hard wired sensor code into which I want to add wifiManager. Methods like runWifiManager, loadConfigFile, saveConfigFile make it easier for me to re-wire existing code.

Thanks,
Chris.

<!-- gh-comment-id:452439878 --> @TinajaLabs commented on GitHub (Jan 8, 2019): OK. Syntax highlighting added but I understand... it is a bit to read. ;) The main point I was making was to come up with blocks of method code to call from setup() or loop(), with button interrupt, so as to minimize the detailed code into those methods. I have existing hard wired sensor code into which I want to add wifiManager. Methods like runWifiManager, loadConfigFile, saveConfigFile make it easier for me to re-wire existing code. Thanks, Chris.
Author
Owner

@tablatronix commented on GitHub (Jan 8, 2019):

It really all depends on flow, I would personally only start cp on demand or if creds empty

<!-- gh-comment-id:452450858 --> @tablatronix commented on GitHub (Jan 8, 2019): It really all depends on flow, I would personally only start cp on demand or if creds empty
Author
Owner

@Jeppedy commented on GitHub (Sep 17, 2020):

I love that you worked to clean this up. I may do something similar in my code.
I had a comment/suggestion/question...
If "ParamSave callback" is not called, should you pull back the values from the portal?
If the callback wasn't invoked, doesn't that mean the user did not intend the values to be stored?
I'm still working on some mods to this library, but that's one of the changes I'm proving out within my application.

Your sketch code I'm referring to is:

//JAH: Should these two lines be moved within the IF?
strcpy(mqtt_server, custom_mqtt_server.getValue());
strcpy(topic_loc, custom_topic_loc.getValue());

// ----------------------------- save the custom parameters to FS
if (shouldSaveConfig) {
saveConfigFile();
shouldSaveConfig = false;
}

<!-- gh-comment-id:694373852 --> @Jeppedy commented on GitHub (Sep 17, 2020): I love that you worked to clean this up. I may do something similar in my code. I had a comment/suggestion/question... If "ParamSave callback" is not called, should you pull back the values from the portal? If the callback wasn't invoked, doesn't that mean the user did not intend the values to be stored? I'm still working on some mods to this library, but that's one of the changes I'm proving out within my application. Your sketch code I'm referring to is: //JAH: Should these two lines be moved within the IF? strcpy(mqtt_server, custom_mqtt_server.getValue()); strcpy(topic_loc, custom_topic_loc.getValue()); // ----------------------------- save the custom parameters to FS if (shouldSaveConfig) { saveConfigFile(); shouldSaveConfig = false; }
Author
Owner

@tablatronix commented on GitHub (Sep 18, 2020):

Avoid doing work inside a IF or callback, only set a flag etc.

That is why the shouldSaveConfig flag is used

<!-- gh-comment-id:694919065 --> @tablatronix commented on GitHub (Sep 18, 2020): Avoid doing work inside a IF or callback, only set a flag etc. That is why the shouldSaveConfig flag is used
Author
Owner

@Jeppedy commented on GitHub (Sep 18, 2020):

I'm unclear. I'm not familiar with this approach of doing work you don't need to do to keep logic out of an IF block.
If the config is not to be "saved", do you want to pull back the values from the portal? (That may be the real question here)
If no, then this is logic that is only needed when a certain condition is true, use the IF block.
It takes up cycles, and potentially causes undesirable results later... Wouldn't it?

<!-- gh-comment-id:694932461 --> @Jeppedy commented on GitHub (Sep 18, 2020): I'm unclear. I'm not familiar with this approach of doing work you don't need to do to keep logic out of an IF block. If the config is not to be "saved", do you want to pull back the values from the portal? (That may be the real question here) If no, then this is logic that is only needed when a certain condition is true, use the IF block. It takes up cycles, and potentially causes undesirable results later... Wouldn't it?
Author
Owner

@ramanraja commented on GitHub (Sep 19, 2020):

I have made similar modification in my code as @Jeppedy has done, and so
far it seems to work.
if the flag shouldSaveConfig is not true, I just return from the function.
Please let me know if there is any logical or practical flaw in my
approach. That will help correct myself.
(I may not fully understand memory management and the overheads of strcpy
etc.)
Thanks,
Rajaraman
India

On Fri, Sep 18, 2020 at 8:54 PM Jeppedy notifications@github.com wrote:

I'm unclear. I'm not familiar with this approach of doing work you don't
need to do to keep logic out of an IF block.
If the config is not to be "saved", do you want to pull back the values
from the portal? (That may be the real question here)
If no, then this is logic that is only needed when a certain condition is
true, use the IF block.
It takes up cycles, and potentially causes undesirable results later...
Wouldn't it?


You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
https://github.com/tzapu/WiFiManager/issues/801#issuecomment-694932461,
or unsubscribe
https://github.com/notifications/unsubscribe-auth/ABXAAH7MSZIFAORK34DIEADSGN3URANCNFSM4GOSQLSQ
.

<!-- gh-comment-id:695179632 --> @ramanraja commented on GitHub (Sep 19, 2020): I have made similar modification in my code as @Jeppedy has done, and so far it seems to work. if the flag shouldSaveConfig is not true, I just return from the function. Please let me know if there is any logical or practical flaw in my approach. That will help correct myself. (I may not fully understand memory management and the overheads of strcpy etc.) Thanks, Rajaraman India On Fri, Sep 18, 2020 at 8:54 PM Jeppedy <notifications@github.com> wrote: > I'm unclear. I'm not familiar with this approach of doing work you don't > need to do to keep logic out of an IF block. > If the config is not to be "saved", do you want to pull back the values > from the portal? (That may be the real question here) > If no, then this is logic that is only needed when a certain condition is > true, use the IF block. > It takes up cycles, and potentially causes undesirable results later... > Wouldn't it? > > — > You are receiving this because you are subscribed to this thread. > Reply to this email directly, view it on GitHub > <https://github.com/tzapu/WiFiManager/issues/801#issuecomment-694932461>, > or unsubscribe > <https://github.com/notifications/unsubscribe-auth/ABXAAH7MSZIFAORK34DIEADSGN3URANCNFSM4GOSQLSQ> > . >
Author
Owner

@tablatronix commented on GitHub (Sep 19, 2020):

sorry I misunderstood, I thought you mean "interrupt funciton" not if statement

<!-- gh-comment-id:695218061 --> @tablatronix commented on GitHub (Sep 19, 2020): sorry I misunderstood, I thought you mean "interrupt funciton" not if statement
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/WiFiManager#672
No description provided.