things working more normally
A => firmware/remote_sensors_mkr1000/include/Credentials.h +89 -0
@@ 0,0 1,89 @@ 
+/****************************************************************************************************************************
+  Credentials.h
+  For SAMD21 MKR1000 boards using WiFi101 Modules/Shields
+  
+  WiFiManager_Generic_WM_Lite is a library for the Mega, Teensy, SAM DUE, SAMD and STM32 boards 
+  (https://github.com/khoih-prog/WiFiManager_Generic_Lite) to enable store Credentials in EEPROM/LittleFS for easy 
+  configuration/reconfiguration and autoconnect/autoreconnect of WiFi and other services without Hardcoding.
+  
+  Built by Khoi Hoang https://github.com/khoih-prog/WiFiManager_Generic_Lite
+  Licensed under MIT license
+ *****************************************************************************************************************************/
+
+#ifndef Credentials_h
+#define Credentials_h
+
+#include "defines.h"
+
+/// Start Default Config Data //////////////////
+
+/*
+#define SSID_MAX_LEN      32
+//From v1.0.3, WPA2 passwords can be up to 63 characters long.
+#define PASS_MAX_LEN      64
+
+typedef struct
+{
+  char wifi_ssid[SSID_MAX_LEN];
+  char wifi_pw  [PASS_MAX_LEN];
+}  WiFi_Credentials;
+
+#define NUM_WIFI_CREDENTIALS      2
+
+// Configurable items besides fixed Header, just add board_name 
+#define NUM_CONFIGURABLE_ITEMS    ( ( 2 * NUM_WIFI_CREDENTIALS ) + 1 )
+////////////////
+
+typedef struct Configuration
+{
+  char header         [16];
+  WiFi_Credentials  WiFi_Creds  [NUM_WIFI_CREDENTIALS];
+  char board_name     [24];
+  int  checkSum;
+} WIFI_GENERIC_Configuration;
+*/
+
+#define TO_LOAD_DEFAULT_CONFIG_DATA      false
+
+#if TO_LOAD_DEFAULT_CONFIG_DATA
+
+// This feature is primarily used in development to force a known set of values as Config Data
+// It will NOT force the Config Portal to activate. Use DRD or erase Config Data with Blynk.clearConfigData()
+
+// Used mostly for development and debugging. FORCES default values to be loaded each run.
+// Config Portal data input will be ignored and overridden by DEFAULT_CONFIG_DATA
+//bool LOAD_DEFAULT_CONFIG_DATA = true;
+
+// Used mostly once debugged. Assumes good data already saved in device.
+// Config Portal data input will be override DEFAULT_CONFIG_DATA
+bool LOAD_DEFAULT_CONFIG_DATA = false;
+
+
+WIFI_GENERIC_Configuration defaultConfig =
+{
+  //char header[16], dummy, not used
+  "WIFI_GENERIC",
+  // WiFi_Credentials  WiFi_Creds  [NUM_WIFI_CREDENTIALS];
+  // WiFi_Credentials.wifi_ssid and WiFi_Credentials.wifi_pw
+  "SSID1",  "password1",
+  "SSID2",  "password2",
+  //char board_name     [24];
+  "SAMD-Control",
+  // terminate the list
+  //int  checkSum, dummy, not used
+  0
+  /////////// End Default Config Data /////////////
+};
+
+#else
+
+bool LOAD_DEFAULT_CONFIG_DATA = false;
+
+WIFI_GENERIC_Configuration defaultConfig;
+
+#endif    // TO_LOAD_DEFAULT_CONFIG_DATA
+
+/////////// End Default Config Data /////////////
+
+
+#endif    //Credentials_h

          
A => firmware/remote_sensors_mkr1000/include/SHT1x.h +90 -0
@@ 0,0 1,90 @@ 
+/**
+ * SHT1x Library
+ *
+ * Copyright 2017 Vincent Pang <wingshun.pang@gmail.com>
+ * Copyright 2009 Jonathan Oxer <jon@oxer.com.au> / <www.practicalarduino.com>
+ * Based on previous work by:
+ *    Maurice Ribble: <www.glacialwanderer.com/hobbyrobotics/?p=5>
+ *    Wayne ?: <ragingreality.blogspot.com/2008/01/ardunio-and-sht15.html>
+ *
+ * Manages communication with SHT1x series (SHT10, SHT11, SHT15)
+ * temperature / humidity sensors from Sensirion (www.sensirion.com).
+ */
+#ifndef SHT1x_h
+#define SHT1x_h
+
+#if (ARDUINO >= 100)
+#include <Arduino.h>
+#else
+#include <WProgram.h>
+#endif
+
+class SHT1x
+{
+  public:
+    enum class Voltage : uint8_t
+    {
+      DC_5_0v = 0
+      , DC_4_0v
+      , DC_3_5v
+      , DC_3_3v
+      , DC_3_0v
+      , DC_2_5v
+    };
+
+    enum class TemperatureMeasurementResolution : uint8_t
+    {
+      Temperature_14bit = 0
+      , Temperature_12bit
+    };
+
+    enum class HumidityMeasurementResolution : uint8_t
+    {
+      Humidity_12bit = 0
+      , Humidity_8bit
+    };
+
+    enum class ShtCommand : uint8_t
+    {
+      MeasureTemperature        = 0b00000011
+      , MeasureRelativeHumidity = 0b00000101
+      , ReadStatusRegister      = 0b00000111
+      , WriteStatusRegister     = 0b00000110
+      , SoftReset               = 0b00011110
+    };
+
+    SHT1x(uint8_t dataPin, uint8_t clockPin, Voltage voltage = Voltage::DC_5_0v);
+
+    float readHumidity() const;
+    float readTemperatureC() const;
+    float readTemperatureF() const;
+
+  private:
+    uint16_t readRawData(ShtCommand command, uint8_t dataPin, uint8_t clockPin) const;
+    bool sendCommandSHT(ShtCommand command, uint8_t dataPin, uint8_t clockPin) const;
+    bool waitForResultSHT(uint8_t dataPin) const;
+    uint16_t getData16SHT(uint8_t dataPin, uint8_t clockPin) const;
+    void skipCrcSHT(uint8_t dataPin, uint8_t clockPin) const;
+
+    double getC1(HumidityMeasurementResolution resolution) const;
+    double getC2(HumidityMeasurementResolution resolution) const;
+    double getC3(HumidityMeasurementResolution resolution) const;
+
+    double getT1(HumidityMeasurementResolution resolution) const;
+    double getT2(HumidityMeasurementResolution resolution) const;
+
+    double getD1ForC(Voltage voltage) const;
+    double getD1ForF(Voltage voltage) const;
+
+    double getD2ForC(TemperatureMeasurementResolution resolution) const;
+    double getD2ForF(TemperatureMeasurementResolution resolution) const;
+
+    const uint8_t _dataPin;
+    const uint8_t _clockPin;
+
+    const Voltage _voltage;
+    const TemperatureMeasurementResolution _tempResolution;
+    const HumidityMeasurementResolution _humidityResolution;
+};
+
+#endif

          
A => firmware/remote_sensors_mkr1000/include/config.h +9 -0
@@ 0,0 1,9 @@ 
+#define WANT_NTP 1
+#define WANT_OTA 1
+#define WANT_MDNS 1
+
+#define FIRMWARE_REVISION 1028
+
+#define RESET_INTERVAL (1000 * 3600 * 24 * 7)
+
+

          
A => firmware/remote_sensors_mkr1000/include/defines.h +153 -0
@@ 0,0 1,153 @@ 
+/****************************************************************************************************************************
+  defines.h
+  For SAMD21 MKR1000 boards using WiFi101 Modules/Shields
+  
+  WiFiManager_Generic_WM_Lite is a library for the Mega, Teensy, SAM DUE, SAMD and STM32 boards 
+  (https://github.com/khoih-prog/WiFiManager_Generic_Lite) to enable store Credentials in EEPROM/LittleFS for easy 
+  configuration/reconfiguration and autoconnect/autoreconnect of WiFi and other services without Hardcoding.
+  
+  Built by Khoi Hoang https://github.com/khoih-prog/WiFiManager_Generic_Lite
+  Licensed under MIT license
+ *****************************************************************************************************************************/
+
+#ifndef defines_h
+#define defines_h
+
+/* Comment this out to disable prints and save space */
+#define DEBUG_WIFI_WEBSERVER_PORT     Serial
+#define WIFI_GENERIC_DEBUG_OUTPUT     Serial
+
+#define _WIFI_GENERIC_LOGLEVEL_       1
+#define _WIFIMULTI_LOGLEVEL_          1
+
+#define DRD_GENERIC_DEBUG             true
+
+#if ( defined(ARDUINO_SAMD_MKR1000) || defined(ARDUINO_SAMD_MKRWIFI1010) )
+  #if defined(WIFI_GENERIC_USE_SAMD)
+    #undef WIFI_GENERIC_USE_SAMD
+    #undef WIFI_USE_SAMD
+  #endif
+  #define WIFI_GENERIC_USE_SAMD      true
+  #define WIFI_USE_SAMD          true
+#else
+  #error This code is intended to run only on the SAMD boards using WINC1500 and WiFi101 ! Please check your Tools->Board setting.
+#endif
+
+#if defined(WIFI_GENERIC_USE_SAMD)
+
+  #if defined(ARDUINO_SAMD_MKR1000)
+    #define BOARD_TYPE      "SAMD MKR1000"
+  #elif defined(ARDUINO_SAMD_MKRWIFI1010)
+    #define BOARD_TYPE      "SAMD MKRWIFI1010"
+  #else
+    #define BOARD_TYPE      "SAMD Unknown"
+  #endif
+
+#endif
+
+// Start location in EEPROM to store config data. Default 0
+// Config data Size currently is 128 bytes)
+#define EEPROM_START              0
+#define EEPROM_SIZE               (2 * 1024)
+
+/////////////////////////////////////////////
+
+// Add customs headers from v1.1.0
+#define USING_CUSTOMS_STYLE           true
+#define USING_CUSTOMS_HEAD_ELEMENT    true
+#define USING_CORS_FEATURE            true
+
+/////////////////////////////////////////////
+
+#define USE_WIFI101            true
+
+#if defined(USE_WIFI_NINA)
+  #undef USE_WIFI_NINA
+#endif
+
+#define USE_WIFI_NINA           false
+
+#define SHIELD_TYPE             "WINC1500 using WiFi101 Library"
+#warning Using WiFi101 Library
+
+/////////////////////////////////////////////
+
+// Permit running CONFIG_TIMEOUT_RETRYTIMES_BEFORE_RESET times before reset hardware
+// to permit user another chance to config. Only if Config Data is valid.
+// If Config Data is invalid, this has no effect as Config Portal will persist
+#define RESET_IF_CONFIG_TIMEOUT                   true
+
+// Permitted range of user-defined RETRY_TIMES_RECONNECT_WIFI between 2-5 times
+#define RETRY_TIMES_RECONNECT_WIFI                3
+
+// Permitted range of user-defined CONFIG_TIMEOUT_RETRYTIMES_BEFORE_RESET between 2-100
+#define CONFIG_TIMEOUT_RETRYTIMES_BEFORE_RESET    5
+
+// Config Timeout 120s (default 60s). Applicable only if Config Data is Valid
+#define CONFIG_TIMEOUT                      120000L
+
+/////////////////////////////////////////////
+
+// Permit input only one set of WiFi SSID/PWD. The other can be "NULL or "blank"
+// Default is false (if not defined) => must input 2 sets of SSID/PWD
+#define REQUIRE_ONE_SET_SSID_PW             true    //false
+
+// Max times to try WiFi per loop() iteration. To avoid blocking issue in loop()
+// Default 1 if not defined, and minimum 1.
+//#define MAX_NUM_WIFI_RECON_TRIES_PER_LOOP     2
+
+// Default no interval between recon WiFi if lost
+// Max permitted interval will be 10mins
+// Uncomment to use. Be careful, WiFi reconnect will be delayed if using this method
+// Only use whenever urgent tasks in loop() can't be delayed. But if so, it's better you have to rewrite your code, e.g. using higher priority tasks.
+//#define WIFI_RECON_INTERVAL                   30000
+
+/////////////////////////////////////////////
+
+#define USE_DYNAMIC_PARAMETERS        true
+
+/////////////////////////////////////////////
+
+#define SCAN_WIFI_NETWORKS                  true
+
+// To be able to manually input SSID, not from a scanned SSID lists
+#define MANUAL_SSID_INPUT_ALLOWED           true
+
+// From 2-15
+#define MAX_SSID_IN_LIST                  8
+
+/////////////////////////////////////////////
+
+// Optional, to use Board Name in Menu
+#define USING_BOARD_NAME                    true
+
+/////////////////////////////////////////////
+
+// Optional, to use Board Name in Menu
+#define USING_CONFIG_MODE_LED               true
+
+#if USING_CONFIG_MODE_LED
+  #if defined(LED_BUILTIN)
+    #define CONFIG_MODE_LED     LED_BUILTIN
+  #else
+    // Using default pin 13 for CONFIG_MODE_LED. To be changed as necessary
+    #define CONFIG_MODE_LED     13
+  #endif
+
+  #define LED_ON      HIGH
+  #define LED_OFF     LOW
+#endif
+
+/////////////////////////////////////////////
+
+#include <WiFiManager_Generic_Lite.h>
+
+#define HOST_NAME   "REMOTE-WEATHER-SENSOR"
+
+#ifdef LED_BUILTIN
+  #define LED_PIN     LED_BUILTIN
+#else
+  #define LED_PIN     13
+#endif
+
+#endif      //defines_h

          
A => firmware/remote_sensors_mkr1000/include/dynamicParams.h +64 -0
@@ 0,0 1,64 @@ 
+/****************************************************************************************************************************
+  dynamicParams.h
+  For SAMD21 MKR1000 boards using WiFi101 Modules/Shields
+  
+  WiFiManager_Generic_WM_Lite is a library for the Mega, Teensy, SAM DUE, SAMD and STM32 boards 
+  (https://github.com/khoih-prog/WiFiManager_Generic_Lite) to enable store Credentials in EEPROM/LittleFS for easy 
+  configuration/reconfiguration and autoconnect/autoreconnect of WiFi and other services without Hardcoding.
+  
+  Built by Khoi Hoang https://github.com/khoih-prog/WiFiManager_Generic_Lite
+  Licensed under MIT license
+ *****************************************************************************************************************************/
+
+#ifndef dynamicParams_h
+#define dynamicParams_h
+
+#include "defines.h"
+
+// USE_DYNAMIC_PARAMETERS defined in defined.h
+
+/////////////// Start dynamic Credentials ///////////////
+
+//Defined in <WiFiManager_Generic_Lite_SAMD.h>
+/**************************************
+  #define MAX_ID_LEN                5
+  #define MAX_DISPLAY_NAME_LEN      16
+
+  typedef struct
+  {
+  char id             [MAX_ID_LEN + 1];
+  char displayName    [MAX_DISPLAY_NAME_LEN + 1];
+  char *pdata;
+  uint8_t maxlen;
+  } MenuItem;
+**************************************/
+
+#if USE_DYNAMIC_PARAMETERS
+
+#define MAX_SERIAL_LEN      10
+
+char Hub_Serial_Number [MAX_SERIAL_LEN + 1]  = "HB-54321";
+char Sensor_Serial_Number [MAX_SERIAL_LEN + 1]  = "ST-54321";
+
+#define MAX_FREQ_LEN      5
+char Report_Frequency  [MAX_FREQ_LEN + 1]   = "60";
+
+MenuItem myMenuItems [] =
+{
+  { "hsn", "Hub Serial Number", Hub_Serial_Number,  MAX_SERIAL_LEN },
+  { "ssn", "Sensor Serial Number", Sensor_Serial_Number,  MAX_SERIAL_LEN },
+  { "rfq", "Report Frequency",        Report_Frequency,   MAX_FREQ_LEN },
+};
+
+uint16_t NUM_MENU_ITEMS = sizeof(myMenuItems) / sizeof(MenuItem);  //MenuItemSize;
+
+#else
+
+MenuItem myMenuItems [] = {};
+
+uint16_t NUM_MENU_ITEMS = 0;
+
+#endif    //USE_DYNAMIC_PARAMETERS
+
+
+#endif      //dynamicParams_h

          
A => firmware/remote_sensors_mkr1000/src/main.cpp +612 -0
@@ 0,0 1,612 @@ 
+#include <Arduino.h>
+
+#include "defines.h"
+#include "Credentials.h"
+#include "dynamicParams.h"
+
+#include <WiFi101.h>
+#include <WiFiUdp.h>
+#include <WiFiManager_Generic_Lite.h>
+#include <MDNS_Generic.h>
+
+#include "config.h"
+
+#ifdef WANT_NTP
+#include <NTPClient.h>
+#endif /* WANT_NTP */
+#ifdef WANT_OTA
+#define NO_OTA_PORT
+#include <ArduinoOTA.h>
+#endif /* WANT_OTA */
+
+#include <StreamUtils.h>
+#include <ArduinoJson.h>
+
+#include <HTU2xD_SHT2x_Si70xx.h>
+#include "bme280.h"
+
+/****************************************************************************************************************************
+  MKR1000_WiFi101.ino
+  For SAMD21 MKR1000 boards using WiFi101 Modules/Shields
+
+  WiFiManager_Generic_WM_Lite is a library for the Mega, Teensy, SAM DUE, SAMD and STM32 boards
+  (https://github.com/khoih-prog/WiFiManager_Generic_Lite) to enable store Credentials in EEPROM/LittleFS for easy
+  configuration/reconfiguration and autoconnect/autoreconnect of WiFi and other services without Hardcoding.
+
+  Built by Khoi Hoang https://github.com/khoih-prog/WiFiManager_Generic_Lite
+  Licensed under MIT license
+  *****************************************************************************************************************************/
+
+
+WiFiManager_Generic_Lite* WiFiManager_Generic;
+
+HTU2xD_SHT2x_SI70xx ht2x(HTU2xD_SENSOR, HUMD_08BIT_TEMP_12BIT);
+bfs::Bme280 bme280(&Wire, bfs::Bme280::I2C_ADDR_PRIM);
+
+#ifdef WANT_OTA
+#define OTA_PASSWORD "7648"
+#endif /* WANT_OTA */
+
+#define DEBUG(X...) do{if(Serial)Serial.println(X);}while(0)
+#define DEBUGCHAR(X...) do{if(Serial)Serial.print(X);}while(0)
+
+
+/* by default, the value of PROJECTNAME will be used as the prefix of the devices's
+ * hostname and the configuration access point ssid.
+ */
+#define PROJECTNAME "ENVSENSOR"
+
+/* when storing data in the pseudo-eeprom of the ESP, you must specify how much space
+ * will be needed for the data stored. changing this value will likely cause existing
+ * data to be lost, so best over-estimate before going live.
+ */
+#define EEPROM_BYTES_NEEDED 50
+
+boolean sensorPresent;
+boolean bmeSensorPresent;
+
+#define SLOW 250
+#define MEDIUM 100
+#define FAST 50
+
+#define sleep(X) delay(X)
+#define ON 1
+#define OFF 0
+//
+//WiFiClient wifiClient;
+void weather_report();
+
+#define dataPin SDA
+#define clockPin SCL
+
+#define RAIN_PIN 7
+#define SPEED_PIN 6
+#define DIR_PIN A0
+
+#define RAIN_DEBOUNCE_MS 350
+
+#define SMARTWEATHER_PORT 50222
+
+IPAddress br_adr;
+
+WiFiUDP udp;
+
+String hubsn;
+String airsn;
+int reportfreq = 60;
+
+#if WANT_MDNS
+String hostname;
+WiFiUDP mdnsUdp;
+MDNS mdns(mdnsUdp);
+#endif /* WANT_MDNS */
+
+#ifdef WANT_NTP
+WiFiUDP ntpUdp;
+NTPClient timeClient(ntpUdp);
+#endif /* WANT_NTP */
+
+void rainIncrement();
+void reportRain(int increments);
+
+long ts;
+int reportcnt = 0;
+int lastobservation = 0;
+int lastreport = 0;
+void blink(int speed, int count) {
+    pinMode(16, OUTPUT);
+    for(; count > 0; count--) {
+        digitalWrite(16, ON);
+        sleep(speed);
+        digitalWrite(16, OFF);
+        sleep(speed);
+    }
+    digitalWrite(16, ON);
+}
+
+
+void heartBeatPrint(void)
+{
+    static int num = 1;
+
+    if (WiFi.status() == WL_CONNECTED)
+        Serial.print("H");        // H means connected to WiFi
+    else
+    {
+        if (WiFiManager_Generic->isConfigMode())
+            Serial.print("C");        // C means in Config Mode
+        else
+            Serial.print("F");        // F means not connected to WiFi
+    }
+
+    if (num == 80)
+    {
+        Serial.println();
+        num = 1;
+    }
+    else if (num++ % 10 == 0)
+    {
+        Serial.print(F(" "));
+    }
+}
+
+void check_status()
+{
+    static unsigned long checkstatus_timeout = 0;
+
+    //KH
+#define HEARTBEAT_INTERVAL    20000L
+    // Print hearbeat every HEARTBEAT_INTERVAL (20) seconds.
+    if ((millis() > checkstatus_timeout) || (checkstatus_timeout == 0))
+    {
+        heartBeatPrint();
+        checkstatus_timeout = millis() + HEARTBEAT_INTERVAL;
+    }
+}
+
+#if USING_CUSTOMS_STYLE
+const char NewCustomsStyle[] /*PROGMEM*/ = "<style>div,input{padding:5px;font-size:1em;}input{width:95%;}body{text-align: center;}\
+button{background-color:blue;color:white;line-height:2.4rem;font-size:1.2rem;width:100%;}fieldset{border-radius:0.3rem;margin:0px;}</style>";
+#endif
+
+void setup()
+{
+    // Debug console
+    Serial.begin(115200);
+    while (!Serial && millis() < 5000);
+
+    delay(200);
+
+    Wire.begin();
+
+    pinMode(RAIN_PIN, INPUT_PULLUP);
+    pinMode(SPEED_PIN, INPUT_PULLUP);
+    pinMode(DIR_PIN, INPUT);
+
+    Serial.print(F("\nStart MKR1000_WiFi101 on ")); Serial.print(BOARD_NAME);
+    Serial.print(F(" with ")); Serial.println(SHIELD_TYPE);
+
+#if (USE_WIFI_NINA || USE_WIFI101)
+    Serial.println(WIFIMULTI_GENERIC_VERSION);
+#endif
+    Serial.println(WIFI_MANAGER_GENERIC_LITE_VERSION);
+
+    WiFiManager_Generic = new WiFiManager_Generic_Lite();
+
+    // Optional to change default AP IP(192.168.4.1) and channel(10)
+    //WiFiManager_Generic->setConfigPortalIP(IPAddress(192, 168, 120, 1));
+    WiFiManager_Generic->setConfigPortalChannel(0);
+
+#if USING_CUSTOMS_STYLE
+    WiFiManager_Generic->setCustomsStyle(NewCustomsStyle);
+#endif
+
+#if USING_CUSTOMS_HEAD_ELEMENT
+    WiFiManager_Generic->setCustomsHeadElement("<style>html{filter: invert(10%);}</style>");
+#endif
+
+#if USING_CORS_FEATURE
+    WiFiManager_Generic->setCORSHeader("Your Access-Control-Allow-Origin");
+#endif
+
+    // Set customized DHCP HostName
+    WiFiManager_Generic->begin();
+    //Or use default Hostname "MKR1000-WIFI-XXXXXX"
+    //WiFiManager_Generic->begin();
+
+    int attempts = 0;
+
+    while ((sensorPresent = ht2x.begin()) != true && attempts < 10) //reset sensor, set heater off, set resolution, check power (sensor doesn't operate correctly if VDD < +2.25v)
+    {
+        Serial.println(F("HTU2xD/SHT2x not connected, fail or VDD < +2.25v")); //(F()) save string to flash & keeps dynamic memory free
+        delay(10);
+        attempts++;
+    }
+
+    if(sensorPresent)
+        Serial.println(F("HTU2xD/SHT2x OK"));
+
+    attempts = 0;
+    while ((bmeSensorPresent = bme280.Begin()) != true && attempts < 10) //reset sensor, set heater off, set resolution, check power (sensor doesn't operate correctly if VDD < +2.25v)
+    {
+        Serial.println(F("BME280 not connected or failed")); //(F()) save string to flash & keeps dynamic memory free
+        delay(10);
+        attempts++;
+    }
+    if(bmeSensorPresent) {
+        Serial.println(F("BME280 OK"));
+
+       bme280.ConfigStandbyTime(bfs::Bme280::STANDBY_TIME_500_MS);
+    }
+
+    if (WiFi.status() == WL_CONNECTED) {
+        hostname = HOST_NAME;
+
+#ifdef WANT_MDNS
+        // Initialize the mDNS library. You can now reach or ping this
+        // Arduino via the host name "hostname.local", provided that your operating
+        // system is mDNS/Bonjour-enabled (such as MacOS X).
+        // Always call this before any other method!
+        WIFI_GENERIC_Configuration config;
+        WiFiManager_Generic->getFullConfigData(&config);
+        if(&config != NULL) {
+            Serial.println("Board Name");
+            Serial.println(config.board_name);
+            hostname = config.board_name;
+        } else {
+            Serial.println("No Board Name!");
+        }
+
+        //hostname.toUpperCase();
+        hostname.toLowerCase();
+        hostname.replace(" ",  "-");
+        hostname.replace("_",  "-");
+
+        Serial.print("Registering mDNS hostname: "); Serial.println(hostname);
+        Serial.print("To access, using "); Serial.print(hostname); Serial.println(".local");
+
+        mdns.begin(WiFi.localIP(), hostname.c_str());
+
+        Serial.println(F("Registering MDNS"));
+
+#endif /* WANT_MDNS */
+
+#ifdef WANT_OTA
+        ArduinoOTA.begin(WiFi.localIP(), hostname.c_str(), (const char *) OTA_PASSWORD, InternalStorage);
+#endif /* WANT_OTA */
+
+        udp.begin(SMARTWEATHER_PORT);
+
+#ifdef WANT_NTP
+        Serial.println(F("Registering NTP client"));
+        timeClient.begin();
+#endif /* WANT_NTP */
+    }
+
+#if (LIMITED_RAM_DEVICE)
+    Serial.println("Limited RAM");
+#endif
+
+    attachInterrupt(digitalPinToInterrupt(RAIN_PIN), rainIncrement, FALLING);
+
+}
+
+int rainIncrements = 0;
+
+static unsigned long last_rain_interrupt_time = 0;
+
+void rainIncrement() {
+    unsigned long interrupt_time = millis();
+    if ((interrupt_time - last_rain_interrupt_time) > RAIN_DEBOUNCE_MS)
+    {
+        rainIncrements++;
+        last_rain_interrupt_time = interrupt_time;
+    }
+}
+
+void printWiFiStatus() {
+    // print the SSID of the network you're attached to:
+    Serial.print("SSID: ");
+    Serial.println(WiFi.SSID());
+
+    // print your WiFi 101 Shield's IP address:
+    IPAddress ip = WiFi.localIP();
+    Serial.print("IP Address: ");
+    Serial.println(ip);
+
+    // print the received signal strength:
+    long rssi = WiFi.RSSI();
+    Serial.print("signal strength (RSSI):");
+    Serial.print(rssi);
+    Serial.println(" dBm");
+#if (LIMITED_RAM_DEVICE)
+    Serial.println("Limited RAM");
+#endif
+}
+
+#if USE_DYNAMIC_PARAMETERS
+void displayCredentials()
+{
+    Serial.println(F("\nYour stored Credentials :"));
+
+    for (uint16_t i = 0; i < NUM_MENU_ITEMS; i++)
+    {
+        Serial.print(myMenuItems[i].displayName);
+        Serial.print(F(" = "));
+        Serial.println(myMenuItems[i].pdata);
+    }
+}
+
+void displayCredentialsInLoop()
+{
+    static bool displayedCredentials = false;
+
+    if (!displayedCredentials)
+    {
+        for (int i = 0; i < NUM_MENU_ITEMS; i++)
+        {
+            if (!strlen(myMenuItems[i].pdata))
+            {
+                break;
+            }
+
+            if ( i == (NUM_MENU_ITEMS - 1) )
+            {
+                displayedCredentials = true;
+                displayCredentials();
+            }
+        }
+    }
+}
+
+#endif
+
+boolean statusNotDisplayed = true;
+
+void loop()
+{
+    WiFiManager_Generic->run();
+    check_status();
+
+    if (WiFi.status() == WL_CONNECTED) {
+        if (statusNotDisplayed) {
+            br_adr = WiFi.localIP();
+            br_adr[3] = 255;
+
+            Serial.println("Smartweather destination");
+            Serial.println(br_adr);
+            Serial.println(SMARTWEATHER_PORT);
+            String svc = hostname + "._http";
+            mdns.addServiceRecord(svc.c_str(), 80, MDNSServiceTCP);
+            printWiFiStatus();
+            statusNotDisplayed = false;
+
+            for (uint16_t i = 0; i < NUM_MENU_ITEMS; i++)
+            {
+                if(myMenuItems[i].id != NULL && strcmp("hsn", myMenuItems[i].id) == 0) {
+                    Serial.print("HUB SERIAL NUMBER");
+                    hubsn = myMenuItems[i].pdata;
+                }
+                if(myMenuItems[i].id != NULL && strcmp("ssn", myMenuItems[i].id) == 0) {
+                    Serial.print("SENSOR SERIAL NUMBER");
+                    airsn = myMenuItems[i].pdata;
+                }
+                if(myMenuItems[i].id != NULL && strcmp("rfq", myMenuItems[i].id) == 0) {
+                    Serial.print("REPORT FREQUENCY");
+                    String fq = myMenuItems[i].pdata;
+                    reportfreq = fq.toInt();
+                    Serial.print(reportfreq);
+                }
+
+                Serial.print(myMenuItems[i].displayName);
+                Serial.print(F(" = "));
+                Serial.println(myMenuItems[i].pdata);
+            }
+        }
+
+#ifdef WANT_OTA
+        ArduinoOTA.poll();
+#endif /* WANT_OTA */
+
+        // This actually runs the mDNS module. YOU HAVE TO CALL THIS PERIODICALLY,
+        // OR NOTHING WILL WORK! Preferably, call it once per loop().
+        mdns.run();
+        weather_report();
+    }
+
+#if USE_DYNAMIC_PARAMETERS
+    displayCredentialsInLoop();
+#endif
+}
+
+void weather_report() {
+    int written = 0;
+    long tsm = millis();
+
+    if(tsm > RESET_INTERVAL) {
+        delay(1000);
+    }
+
+    if((tsm/1000) > (lastreport + 30)) {
+        lastreport = millis() / 1000;
+#ifdef WANT_NTP
+        timeClient.update();
+        ts = timeClient.getEpochTime();
+#endif /* WANT_NTP */
+        blink(SLOW, 3);
+
+        {
+            Serial.println("Sending hub status");
+            Serial.println(ts);
+
+            StaticJsonDocument<200> doc;
+
+            doc["serial_number"] = hubsn;
+            doc["type"] = "hub_status";
+            doc["firmware_revision"] = FIRMWARE_REVISION;
+            doc["uptime"] = reportcnt; // millis() / 1000;
+            doc["rssi"] = WiFi.RSSI();
+            doc["timestamp"] = ts;
+
+            if (udp.beginPacket(br_adr, SMARTWEATHER_PORT)) {
+                Serial.println("Packet begun.");
+                written = serializeJson(doc, udp);
+                if (udp.endPacket())
+                    Serial.println("Sent.");
+                reportcnt++;
+            } else {
+                Serial.println("Failed to send packet.");
+            }
+        }
+
+        {
+            Serial.println("Sending device status");
+
+            StaticJsonDocument<200> doc;
+
+            doc["serial_number"] = airsn;
+            doc["hub_sn"] = hubsn;
+            doc["type"] = "device_status";
+            doc["firmware_revision"] = FIRMWARE_REVISION;
+            doc["uptime"] = millis() / 1000;
+            doc["rssi"] = WiFi.RSSI();
+            doc["hub_rssi"] = WiFi.RSSI();
+            doc["voltage"] = 0.0;
+            doc["timestamp"] = ts;
+
+            if (udp.beginPacket(br_adr, SMARTWEATHER_PORT))
+                Serial.println("Packet begun.");
+            written = serializeJson(doc, udp);
+            Serial.println(written);
+            udp.println();
+            if (udp.endPacket())
+                Serial.println("Sent.");
+        }
+    }
+
+    if((tsm/1000) > (lastobservation + reportfreq)) {
+        StaticJsonDocument<400> doc;
+        blink(FAST, 2);
+        int rain = rainIncrements;
+        rainIncrements = 0;
+
+#ifdef WANT_NTP
+        ts = timeClient.getEpochTime();
+#endif /* WANT_NTP */
+        Serial.println("Sending observation");
+
+        lastobservation = (tsm/1000);
+        doc["serial_number"] = airsn;
+        doc["hub_sn"] = hubsn;
+        doc["type"] = "obs_st";
+        doc["firmware_revision"] = FIRMWARE_REVISION;
+        JsonArray obs = doc.createNestedArray("obs");
+        JsonArray obsValues = obs.createNestedArray();
+        obsValues.add(ts);
+
+        // lull
+        // avg
+        // gust
+        // direction
+        // sample interval
+        obsValues.add(0);
+        obsValues.add(0);
+        obsValues.add(0);
+        obsValues.add(0);
+        obsValues.add(60);
+
+        if(bmeSensorPresent) {
+            float t,h,p;
+
+            if(bme280.Read()) {
+
+            t = bme280.die_temp_c();
+            h = bme280.humidity_rh();
+            p = bme280.pres_pa()/100;
+
+                Serial.print("T ");
+                Serial.println(t);
+
+                Serial.print("H ");
+                Serial.println(h);
+
+                Serial.print("P ");
+                Serial.println(p);
+
+                obsValues.add(p);
+                obsValues.add(t);
+                obsValues.add(h);
+            }
+            else {
+                Serial.println("Error reading BME280 sensor.");
+                obsValues.add(0.0);
+                obsValues.add(0.0);
+                obsValues.add(0.0);
+            }
+        } else {
+            obsValues.add(0);
+            if(sensorPresent) {
+                float t,h;
+                t = ht2x.readTemperature();
+                h = ht2x.readHumidity();
+
+                Serial.print("T ");
+                Serial.println(t);
+
+                Serial.print("H ");
+                Serial.println(h);
+
+                obsValues.add(t);
+                obsValues.add(h);
+            } else {
+                     obsValues.add(0);
+                     obsValues.add(0);
+            }
+        }
+
+        if(sensorPresent) {
+            float t, h;
+            t = ht2x.readTemperature();
+            h = ht2x.readHumidity();
+
+            Serial.print("T ");
+            Serial.println(t);
+
+            Serial.print("H ");
+            Serial.println(h);
+
+//            obsValues.add(t);
+//            obsValues.add(h);
+        }
+        // ill
+        obsValues.add(0);
+        // uv
+        obsValues.add(0);
+        // rad
+        obsValues.add(0);
+        //rain prev minute
+        // employing a 0.01 inch increment gauge
+        Serial.println("rain increments");
+        Serial.println(rain);
+        obsValues.add(rain * (0.01 * 25.4));
+        // precip type
+        obsValues.add(0);
+        // lightning distance
+        obsValues.add(0);
+        // lightning count
+        obsValues.add(0);
+        // battery voltage
+        obsValues.add(0);
+
+        obsValues.add((reportfreq/60));
+
+        if(udp.beginPacket(br_adr, SMARTWEATHER_PORT))
+            Serial.println("Packet begun.");
+        written = serializeJson(doc, udp);
+        Serial.println(written);
+        udp.println();
+        if(udp.endPacket())
+            Serial.println("Sent.");
+    }
+    delay(50);
+}
  No newline at end of file