Loss of WiFi connection... seeking input on my code

So I have two Photons controlling two sets of LED strips under my Kitchen Counter, controllable by my Vera home automation server, pushbuttons or my DO button by IFTTT. I have two, one the Master and the other the Slave. The Master transmits via UDP status changes to new PWM values (brightness) and Slave responds accordingly. Nice smooth fades look great:

Master sends heartbeat to Slave… if power is shut off at master at the wall switch, the Slave detects the loss of the heartbeat and turns itself “off”… very straightforward.

However, my Master constantly drops WiFi signal and never recovers… annoyingly so. The only way for me to get it back on-line is to power cycle it. I have several Particle devices working at the house and in this case, it is line of sight with the WiFi router, about 12 meters away. The Slave has never disconnected in a way that it didn’t automatically recover. So I am wondering if I have some other issue in my code, and was hoping that the experts here could assist! I am open to any comments.

/*
* Particle Core Dimmer Control
* by Jim Brower - BulldogLowell@gmail.com
* Version 1.0a
* July 17, 2015
* Features the following:
* * Non-blocking dimming using my Fade.h library
* * Can serve as a Master for other Particle enabled devices using UDP
* * Configurable timers for automatic control
* * Webhook enables you to create Sunrise and Sunset offsets for Automation
*/

/* Example of working Sunrise and Sunset times webhook:

{
	"event": "fl_sun_time",
	"url": "http://api.wunderground.com/api/<GoGetYourOwnApiKey>/astronomy/q/FL/Weston.json",
	"requestType": "POST",
	"headers": null,
	"query": null,
	"responseTemplate": "{{#sun_phase}}{{sunrise.hour}}~{{sunrise.minute}}~{{sunset.hour}}~{{sunset.minute}}~{{/sun_phase}}",
	"json": null,
	"auth": null,
	"mydevices": true
}
*/

#include "Fade.h"
#include <application.h>

//#define DEBUG_ON  // Comment out to supress serial output

#ifdef DEBUG_ON
#define DEBUG_PRINT(x) Serial.print(x)
#define DEBUG_PRINTLN(x) Serial.println(x)
#define SERIAL_START(x) Serial.begin(x)
#else
#define DEBUG_PRINT(x)
#define DEBUG_PRINTLN(x)
#define SERIAL_START(x)
#endif

#define POWER_PIN D4     // Toggles LEDs on/off
#define FADE_UP_PIN D6   // increases LED fade
#define FADE_DOWN_PIN D2 // decreases LED fade
#define FADE_BUTTON_INCREMENT (255/10)  // modify denominator to step up/down in larger/smaller increments
#define PWM_PIN  A4      // output pin to your MOSFET

const int dawnDuskOffset = 45; // you can automatically have your device set norms for Day and Night states (minutes)
const int RESET_TIME = 4;
const int RECOVERY_BRIGHTNESS = 100; // when you power cycle it will recover to RECOVERY_BRIGHTNESS.
const int buttonPin[] = {POWER_PIN, FADE_UP_PIN, FADE_DOWN_PIN};  // array for the pushbuttons

struct deviceTime {
  int Hour;
  int Minute;
};

//default values for sunrise/sunset
deviceTime sunset = {
  18,0};
deviceTime sunrise = {
   6,0};

char message[40] = "No Time Values Recieved";
unsigned long webhookTimer;
int oldValue[3];
int currentLevel = 0;
int lastHour;
const int UDP_TX_PACKET_MAX_SIZE = 4;
const IPAddress slaveIP(10,0,1,255);
const unsigned int localPort = 2222;
UDP Udp;
unsigned long udpHeartbeatTimer = millis();

//extern "C" char* itoa(int a, char* buffer, unsigned char radix);

bool isDaytime;

unsigned long lastPress;

Fade ledStrip(PWM_PIN, 15); // Create the Fade object, set the PWM pin and set the time between increments in milliseconds

void setup()
{
  SERIAL_START(115200);
  WiFi.setCredentials("REDACTED", "REDACTED");
  Spark.function("setDimLevel" , incomingDimCommand);
  Spark.variable("currentLevel", &currentLevel, INT);
  Spark.variable("SunriseTime", &message, STRING);
  Spark.variable("IsDaytime", &isDaytime, INT);
  Spark.subscribe("hook-response/fl_sun_time", sunTimeHandler, MY_DEVICES);
  Udp.begin(localPort);
  int startupLevel = 255;
  if (EEPROM.read(1))
  {
    startupLevel = 51;
  }
  sendFadeTargetAsUDP(startupLevel);
  EEPROM.write(1,0);
  for (int i = 0; i < 3; i++)
  {
    pinMode(buttonPin[i], INPUT_PULLUP);
  }
  DEBUG_PRINTLN("Publishing Sunrise/Sunset WebHook...");
  Spark.publish("fl_sun_time");
  char buffer[30] = "";
  sprintf(buffer, "Sunrise: %02d:%02d, Sunset: %02d:%02d", sunrise.Hour, sunrise.Minute, sunset.Hour, sunset.Minute);
  strcpy (message, buffer);
  pinMode(PWM_PIN, OUTPUT);
  ledStrip.write(startupLevel);
  char myMessage[60] = "";
  sprintf(myMessage, "Kitchen Dimmer Reset: now set to %d%%", map(ledStrip.getSetpoint(), 0, 255, 0, 100));
  Spark.publish("pushover", String(myMessage), 60, PRIVATE); // Spark WebHook!!!
}
//
void loop()
{
  ledStrip.update();
  currentLevel = ledStrip.read();
  int currentHour = Time.hour();
  if (lastHour == RESET_TIME && lastHour != currentHour)
  {
    EEPROM.write(1,1);
    System.reset();
  }
  lastHour = currentHour;
  if (millis() - webhookTimer > 60 * 60 * 1000UL)
  {
    DEBUG_PRINTLN("Publishing Sunrise/Sunset WebHook...");
    Spark.publish("fl_sun_time");
    unsigned long timer = millis();
    while(millis() - timer < 3000UL)
    {
      Spark.process();
    }
    DEBUG_PRINT("Retrieved new times for ");
    DEBUG_PRINTLN(message);
    webhookTimer = millis();
  }

  bool daylightSavings = IsDST(Time.day(), Time.month(), Time.weekday());
  Time.zone(daylightSavings? -4 : -5);
  isDaytime = IsDAYTIME();

  if (Udp.parsePacket())
  {
    char packetBuffer[UDP_TX_PACKET_MAX_SIZE] = "";
    Udp.read(packetBuffer, UDP_TX_PACKET_MAX_SIZE);
    DEBUG_PRINT("UDP Echo recieved:");
    DEBUG_PRINTLN(packetBuffer);
  }
  if (millis() - udpHeartbeatTimer >= 500UL)
  {
    sendHeartBeatAsUDP("HB");
    udpHeartbeatTimer = millis();
  }

  for (int i = 0; i < 3; i++)
  {
    int value = digitalRead(buttonPin[i]);
    if (value != oldValue[i] && value == LOW && millis() - lastPress > 50)
    {
      lastPress = millis();
      int fadeTarget = ledStrip.read();
      switch (i)
      {
        case 0:
          fadeTarget = fadeTarget? 0 : 255;
          DEBUG_PRINT("POWER Button pressed; new value:");
          DEBUG_PRINTLN(fadeTarget);
          break;
        case 1:
          fadeTarget = min(fadeTarget + FADE_BUTTON_INCREMENT, 255);
          /*if (fadeTarget > 255 - FADE_BUTTON_INCREMENT)
          {
            fadeTarget = 255;
          }*/
          DEBUG_PRINT("UP Button pressed; new value:");
          DEBUG_PRINTLN(fadeTarget);
          break;
        case 2:
          fadeTarget = max(fadeTarget - FADE_BUTTON_INCREMENT, 0);
          /*if (fadeTarget < FADE_BUTTON_INCREMENT)
          {
            fadeTarget = 0;
          }*/
          DEBUG_PRINT("DOWN Button pressed; new value:");
          DEBUG_PRINTLN(fadeTarget);
          break;
        default:
          // Nothing to do here...
          ;
      }
      sendFadeTargetAsUDP(fadeTarget);
      ledStrip.write(fadeTarget);
    }
    oldValue[i] = value;
  }
}
//
bool IsDAYTIME()
{
  int rise_time = tmConvert_t(Time.year(), Time.month(), Time.day(), sunrise.Hour, sunrise.Minute, 0);
  int set_time = tmConvert_t(Time.year(), Time.month(), Time.day(), sunset.Hour, sunset.Minute, 0);
  int now_time = tmConvert_t(Time.year(), Time.month(), Time.day(), Time.hour(), Time.minute(), Time.second());
  return (now_time > (rise_time + (dawnDuskOffset * 60)) && now_time < set_time - (dawnDuskOffset * 60));
}
//
inline time_t tmConvert_t(int YYYY, byte MM, byte DD, byte hh, byte mm, byte ss)
{
  struct tm t;
  t.tm_year = YYYY-1900;
  t.tm_mon = MM;
  t.tm_mday = DD;
  t.tm_hour = hh;
  t.tm_min = mm;
  t.tm_sec = ss;
  t.tm_isdst = 0;
  time_t t_of_day = mktime(&t);
  return t_of_day;
}
//
void sunTimeHandler(const char * event, const char * data)
{
  String sunriseReturn = String(data);
  char sunriseBuffer[125] = "";
  sunriseReturn.toCharArray(sunriseBuffer, 125); // example: \"5~37~20~30~\"
  sunrise.Hour = atoi(strtok(sunriseBuffer, "\"~"));
  sunrise.Minute = atoi(strtok(NULL, "~"));
  sunset.Hour = atoi(strtok(NULL, "~"));
  sunset.Minute = atoi(strtok(NULL, "~"));
  char buffer[30] = "";
  sprintf(buffer, "Sunrise: %02d:%02d, Sunset: %02d:%02d", sunrise.Hour, sunrise.Minute, sunset.Hour, sunset.Minute);
  strcpy (message, buffer);
}
//
bool IsDST(int dayOfMonth, int month, int dayOfWeek)
{
  if (month < 3 || month > 11)
  {
    return false;
  }
  if (month > 3 && month < 11)
  {
    return true;
  }
  int previousSunday = dayOfMonth - (dayOfWeek - 1);
  if (month == 3)
  {
    return previousSunday >= 8;
  }
  return previousSunday <= 0;
}
//
int incomingDimCommand(String params)
{
  int value = params.toInt();
  if (value == -1) // i.e. -1 to toggle
  {
    value = (ledStrip.read()? 0 : 100);
  }
  else
  {
    value = constrain(value, 0, 100);
  }

  sendFadeTargetAsUDP(map(value, 0, 100, 0, 255));
  ledStrip.write(map(value, 0, 100, 0, 255));
  DEBUG_PRINT("I just set incoming dim level to ");
  DEBUG_PRINT(value);
  DEBUG_PRINTLN("%");
  char myMessage[60] = "";
  sprintf(myMessage, "Kitchen Dimmer now set to %d%%", value);
  Spark.publish("pushover", String(myMessage), 60, PRIVATE); // Spark WebHook!!!
  return value;
}
//

void sendFadeTargetAsUDP(int slaveTarget)
{
  DEBUG_PRINT("sending message via UDP, message = ");
  char udpString[UDP_TX_PACKET_MAX_SIZE] = "";
  itoa(slaveTarget, udpString, 10); // base 10
  DEBUG_PRINTLN(udpString);
  Udp.beginPacket(slaveIP, 2222);
  Udp.write(udpString);
  Udp.endPacket();
}
void sendHeartBeatAsUDP(const char* slaveTarget)
{
  Udp.beginPacket(slaveIP, 2222);
  Udp.write(slaveTarget);
  Udp.endPacket();
}

there really isn’t much to it… the time ‘stuff’ is there for future automation responses which I cannot get to until I solve the WiFi connection issues.

You can see I am doing a nightly reset() but that does nothing to recover…

My Fade library is non-blocking:

#ifndef Fade_h
#define Fade_h

#include "application.h"

class Fade
{
  public:
    Fade() {};
    Fade(int pin, uint32_t timeStep = 15, uint8_t min = 0, uint8_t max = 255);
    void write(int to);
    void update();
    void update(uint32_t time);
    uint8_t read();
    uint32_t readSpeed();
    uint32_t writeSpeed(uint32_t time);
    uint8_t getSetpoint();
  private:
    uint8_t _min;
    uint8_t _max;
    uint8_t _targetFade;
    uint8_t _pwmRate;
    uint32_t _time;
    uint32_t _last;
    uint8_t _pin;
};

#endif

and:

#include <application.h>

#include "Fade.h"


Fade::Fade(int pin, uint32_t timeStep, uint8_t min, uint8_t max)
{
  _pin = pin;
  _time = timeStep;
  _min = min;
  _max = max;
  pinMode(_pin, OUTPUT);
  analogWrite(_pin, _min);
  _pwmRate = _min;
}

void Fade::write(int to)
{
  _targetFade = (uint8_t) constrain(to, _min, _max);

  this->update();
}

void Fade::update()
{
  this->update(millis());
}

void Fade::update(uint32_t time)
{
  if (time - _time > _last)
  {
    _last = time;
    if (_pwmRate > _targetFade) analogWrite(_pin, --_pwmRate);
    if (_pwmRate < _targetFade) analogWrite(_pin, ++_pwmRate);
  }
}

uint8_t Fade::getSetpoint()
{
  return _targetFade;
}

uint8_t Fade::read()
{
  return _pwmRate;
}

uint32_t Fade::readSpeed()
{
  return _time;
}

uint32_t Fade::writeSpeed(uint32_t time)
{
  _time = time;
}

Try updating to the latest (0.4.4-rc.3) system firmware on the github releases page.

Yes, I should have mentioned that I am using latest firmware.

Anyone see any issues in my code?