[SOLVED] Photon goes off-line frequently

I know this topic has come up before but I most of those involved HTTP requests coming to rapidly and tying up all the Sockets. Here is my situation:

I am only using Particle functions (2), variables (6) and publish (2). I publish two events every minute. I have attached a screen capture from my console.

I am getting OK WiFi with an RSSI of -71. Running firmware version 0.6.2

What could be causing this? Again, if I missed something from the other posts - sorry, I did look.

Thanks,

Chip

That’s probably caused by your code - which we’d need to see

1 Like

@scruffr,

This program is automating the watering of the plants on my back deck.

Sure, this is early work so it is not all commented. Here is the main program:

/*
 * Project AquaMaster
 * Description: Watering program for the back deck
 * Author: Chip McClelland
 * Date: 7/18/17
 */

 STARTUP(WiFi.selectAntenna(ANT_AUTO)); // continually switches at high speed between antennas

 #include <I2CSoilMoistureSensor.h>

 I2CSoilMoistureSensor sensor;

 const int solenoidPin = D2;
 const int resetChirp = D3;
 const int blueLED = D7;

 unsigned long waterTime = 60000;
 String RSSIdescription = "";
 String RSSIstring = "";
 String capDescription = "";
 int capValue = 0;
 int wateringNow = 0;
 int waterEnabled = 0;
 int currentPeriod = 0;  // Change length of period for testing 2 times in main loop


void setup() {
  Wire.begin();
  Serial.begin(9600);
  pinMode(solenoidPin,OUTPUT);
  digitalWrite(solenoidPin, LOW);
  pinMode(blueLED,OUTPUT);
  pinMode(resetChirp,OUTPUT);

  Time.zone(-4);    // Raleigh DST (watering is for the summer)

  Particle.variable("Watering", wateringNow);
  Particle.variable("WiFiStrength", RSSIdescription);
  Particle.variable("RSSI",RSSIstring);
  Particle.variable("Moisture", capDescription);
  Particle.variable("capValue", capValue);
  Particle.variable("Enabled", waterEnabled);
  Particle.function("start-stop", startStop);
  Particle.function("Enabled", wateringEnabled);


  sensor.begin(); // reset sensor
  delay(1000); // give some time to boot up
  Serial.print("I2C Soil Moisture Sensor Address: ");
  Serial.println(sensor.getAddress(),HEX);
  Serial.print("Sensor Firmware version: ");
  Serial.println(sensor.getVersion(),HEX);
  Serial.println();

}


void loop() {
  if (Time.minute() != currentPeriod)
  {
    capValue = sensor.getCapacitance();
    Particle.publish("Soil Moisture", String(capValue));
    getMoisture(capValue);
    Serial.print(capValue); //read capacitance register
    //Serial.print(", Temperature: ");
    //Serial.println(sensor.getTemperature()/(float)10); //temperature register
    getWiFiStrength();
    currentPeriod = Time.minute();
    if (waterEnabled)
    {
      if (currentPeriod >= 5 && currentPeriod <= 8)
      {
        if ((capDescription == "Very Dry") || (capDescription == "Dry") || (capDescription == "Normal"))
        {
        Particle.publish("Watering","Watering");
        digitalWrite(solenoidPin, HIGH);
        delay(waterTime);
        digitalWrite(solenoidPin,LOW);
        Particle.publish("Watering","Done");
        }
        else Particle.publish("Watering","Not Needed");
      }
      else Particle.publish("Watering","Not Time");
    }
  }
}

void turnOnWater(unsigned long duration)
{
  digitalWrite(blueLED, HIGH);
  digitalWrite(solenoidPin, HIGH);
  wateringNow = 1;
  delay(duration);
  digitalWrite(blueLED, LOW);
  digitalWrite(solenoidPin, LOW);
  wateringNow = 0;
}

int startStop(String command)   // Will reset the local counts
{
  if (command == "start")
  {
    turnOnWater(waterTime);
    return 1;
  }
  else if (command == "stop")
  {
    digitalWrite(blueLED, LOW);
    digitalWrite(solenoidPin, LOW);
    wateringNow = 0;
    return 1;
  }
  else
  {
    Serial.print("Got here but did not work: ");
    Serial.println(command);
    return 0;
  }
}

int wateringEnabled(String command)
  {
    if (command == "enabled")
    {
      waterEnabled = 1;
      return 1;
    }
    else if (command == "not")
    {
      waterEnabled = 0;
      return 1;
    }
    else
    {
      waterEnabled = 0;
      return 0;
    }
  }

int getWiFiStrength()
{
  int wifiRSSI = WiFi.RSSI();
  RSSIstring = String(wifiRSSI);
  if (wifiRSSI >= 0)
  {
    Serial.println("WiFi RSSI Error");
    RSSIdescription = "error";
    return 0;
  }
  Serial.print("WiFi RSSI:");
  Serial.println(wifiRSSI);
  int strength = map(wifiRSSI, -127, -1, 0, 5);
  Serial.print("The signal strength is: ");
  switch (strength)
  {
    case 0:
      RSSIdescription = "Poor Signal";
      break;
    case 1:
      RSSIdescription = "Low Signal";
      break;
    case 2:
      RSSIdescription = "Medium Signal";
      break;
    case 3:
      RSSIdescription = "Good Signal";
      break;
    case 4:
      RSSIdescription = "Very Good Signal";
      break;
    case 5:
      RSSIdescription = "Great Signal";
      break;
  }
  Serial.println(RSSIdescription);
}


void getMoisture(int value)
{
  if ((value >= 500) || (value <=300))
  {
    capDescription = "error";
  }
  else
  {
    int strength = map(value, 300, 500, 0, 5);
    switch (strength)
    {
      case 0:
        capDescription = "Very Dry";
        break;
      case 1:
        capDescription = "Dry";
        break;
      case 2:
        capDescription = "Normal";
        break;
      case 3:
        capDescription = "Wet";
        break;
      case 4:
        capDescription = "Very Wet";
        break;
      case 5:
        capDescription = "Waterlogged";
        break;
    }
  }
  Particle.publish("Moisture Level", capDescription);
}

There is a simple library for the capacitive soil moisture sensor which is here: https://github.com/Apollon77/I2CSoilMoistureSensor

I appreciate your and the communities help. The Electron has been a great platform and I am looking forward to working on the Photon.

Once the project is complete, I will document and share it with the community.

Thanks,

Chip

Have you got your I2C pull-up resistors in place?

It seems as if - at some point - your code doesn’t service the cloud for more than 10 sec which causes an auto-reconnect.
To keep the connection alive you can add SYSTEM_THREAD(ENABLED) at the top of your project, but this won’t cure the actual “hang” in your current code - although it might help locating the offending instruction.

ScruffR,

Yes, I have the i2c resistors in place and am getting good data from the soil moisture sensor.

This program gets i2c data from the sensor at the top of each minute. However, the off-line / on-line events all occur in the middle of the minute period when the device should simply be watching the clock. Could the Time function be causing the hang?

I will try the SYSTEM_THREAD suggestion and do appreciate the help.

Chip

I don’t think so. The time functions are reading the internal clock, so they return quickly.[quote=“chipmc, post:5, topic:34692”]
However, the off-line / on-line events all occur in the middle of the minute period
[/quote]

Not sure what you mean by “middle”, since the first one you show occurs just 2 seconds before the top of a minute. Also, the amount of time offline is not exactly 5 seconds; the 3 you show are 8, 16, and 6 seconds. It’s hard to draw conclusions from such a small subset of data. Are you seeing these offline events every minute?

I’ve seen these type of events in one of my programs too; usually, the device is offline for 1 to 7 seconds, and occur maybe 20 times a day (offline meaning not connected to the cloud, but still connected to WiFi). I haven’t found any reason for those yet (I do have SYSTEM_THREAD(ENABLED), and have no blocking code). I don’t see any correlation in my code between when these events happen, and what’s happening in the code at that moment.

Ric,

Good to know about the Time.minute() function being solid, in some of my other projects I use a RTC clock module over i2c and that can be an issue.

I have put the SYSTEM_THREAD(ENABLED) line in and have not seen any issues in the past hour. I will keep an eye on this. I will also need to get rid of the delay()s in my code.

When I first started look at this, I thought I saw a pattern in the 5 seconds and the “middle” of the minute, but you are right the screen capture I sent does not match that and perhaps it was such a small sample that it was not valid.

I do think that these disconnects are occurring in the period between the sensing and reporting that occurs at the top of each minute. As I look back over the console log, I don’t see any minutes missing in the soil moisture so, my assumption is that it is happening when the main loop is executing the if() statement.

If it is not software, could it be a WiFi issue? My next plan is to put a better 9dBi “duck” antenna on instead of the PCB antenna I am using now.

The good news is that my plans are getting watered, I just don’t want this to stop working when I am away from home.

Thanks for all the suggestions,

Chip

Good to hear that your plants are doing well.
I do not have any contributions to the content, but still I would like to take a look at my project to plant irrigation. Works with Photon, but should also work with Electron.

If it wasn’t in the firmware (including a possible slow I2C) I wouldn’t see how SYSTEM_THREAD(ENABLED) could help the issue.
But since you said that it did at least reduce the problem, I feel even more compelled to suppose it actually is a firmware issue.

Postler,

Looks great. Will take a look - I really like the Blynk interface.

Thank you for sharing

Chip

ScruffR,

Well, right after I posted that…

So, this is the pattern,

  • Time.minute() changes to 43 and the program takes a soil moisture reading
  • The soil moisture raw reading and description are reported using Particle.publish - this is complete in the 1st second
  • At about the “middle” of the minute (29 seconds) the Photon goes offline and reconnects 8 seconds later
  • The next reporting at 44 minutes goes off without a hitch.

What is the Photon doing during that time besides running in the main loop with the if (Time.minute() != currentPeriod) switch? Does it update the Particle.variable or Particle.function values?

These intermittent problems are always the hardest to solve.

Thanks,

Chip

Particle.variable() and Particle.function() are not serviced between iterations of loop() unless they have been actively requested via the cloud. No request, no load on the controller.

You may want to add some more serial logging (including the Particle.connected() state) in your code and for testing disable the extra Particle.publishes() besides the Soil Moisture one.
Maybe you can see a pattern in the serial logs.

And one of my standard suggestions: Replace all your String stuff with C strings (char[]) :wink:

@ScruffR and all,

I have been working on the finishing touches for the irrigation project and am very happy with the performance of the system. My plants are enjoying more consistent watering as well - even when I am out of town. At your suggestion, I have reduced the number of publishes, eliminated the evil Strings and finished commenting my code. I have not, however, been able to prevent the constant on-line / off-line events which, while not interfering with the function of the device, are concerning for a project I want to be able to rely on.

Assuming, this is due to my code, I would welcome any comments or suggestions on how to make this better and reduce the number of times the device goes off-line. Thank you all in advance for your help. Full project documentation is here.

Thank you, Chip

/*
 * Project AquaMaster
 * Description: Watering program for the back deck
 * Author: Chip McClelland
 * Date: 7/18/17
 */
 // Wiring for Chirp (Board/Assign/Cable) - Red/Vcc/Orange, Black/GND/Green, Blue/SCL/Green&White, Yellow/SDA/Orange&White

STARTUP(WiFi.selectAntenna(ANT_AUTO));      // continually switches at high speed between antennas
SYSTEM_THREAD(ENABLED);

// Software Release lets me know what version the Particle is running
#define SOFTWARERELEASENUMBER "0.7"

// Included Libraries
#include <I2CSoilMoistureSensor.h>          // Apollon77's Chirp Library: https://github.com/Apollon77/I2CSoilMoistureSensor

// Function Prototypes
I2CSoilMoistureSensor sensor;               // For the Chirp sensor

// Constants for Pins
const int solenoidPin = D2;                 // Pin that controls the MOSFET that turn on the water
const int blueLED = D7;                     // Used for debugging, can see when water is ON
const int donePin = D6;                     // Pin the Electron uses to "pet" the watchdog
const int wakeUpPin = A7;                   // This is the Particle Electron WKP pin

// Watering Variables
unsigned long oneMinuteMillis = 60000;      // For Testing the system and smaller adjustments
int shortWaterMinutes = 1;                  // Short watering cycle
int longWaterMinutes = 5;                   // Long watering cycle - must be shorter than watchdog interval!
int wateringMinutes = 0;                    // How long will we water based on time or Moisture
int startWaterHour = 5;                     // When can we start watering
int stopWaterHour = 8;                      // When do we stop for the day
int wateringNow = 0;                        // Status - watering?
int waterEnabled = 1;                       // Allows you to disable watering from the app or Ubidots
float expectedRainfallToday = 0;            // From Weather Underground Simple Forecast qpf_allday

// Measurement Variables
char Signal[17];                            // Used to communicate Wireless RSSI and Description
char Rainfall[5];                           // Report Rainfall preduction
int capValue = 0;                           // This is where we store the soil moisture sensor raw data
int soilTemp = 0;                           // Soil Temp is measured 3" deep
char capDescription[13];                    // Characterize using a map function
char Moisture[15];                          // Combines description and capValue

// Time Period Variables
int currentPeriod = 0;                      // Change length of period for testing 2 places in main loop
int lastWateredPeriod = 0;                  // So we only wanter once an hour
int lastWateredDay = 0;                     // Need to reset the last watered period each day
int currentDay = 0;                         // Updated so we can tell which day we last watered

// Control Variables
const char* releaseNumber = SOFTWARERELEASENUMBER;  // Displays the release on the menu
volatile bool doneEnabled = true;           // This enables petting the watchdog
int lastWateredPeriodAddr = 0;              // Where I store the last watered period in EEPROM
int lastWateredDayAddr = 0;
char wateringContext[25];                   // Why did we water or not sent to Ubidots for context
float rainThreshold = 0.4;                  // Expected rainfall in inches which would cause us not to water

void setup() {
  pinMode(donePin,OUTPUT);                  // Allows us to pet the watchdog
  digitalWrite(donePin, HIGH);              // Pet now while we are getting set up
  digitalWrite(donePin, LOW);
  pinMode(solenoidPin,OUTPUT);              // Pin to turn on the water
  digitalWrite(solenoidPin, LOW);           // Make sure it is off
  pinMode(blueLED,OUTPUT);                  // Pin to see whether water should be on
  pinMode(wakeUpPin,INPUT_PULLDOWN);        // The signal from the watchdog is active HIGH
  attachInterrupt(wakeUpPin, watchdogISR, RISING);   // The watchdog timer will signal us and we have to respond

  Particle.variable("Watering", wateringNow);       // These variables are used to monitor the device will reduce them over time
  Particle.variable("WiFiStrength", Signal);
  Particle.variable("Moisture", Moisture);
  Particle.variable("Enabled", waterEnabled);
  Particle.variable("Release",releaseNumber);
  Particle.variable("LastWater",lastWateredPeriod);
  Particle.variable("RainFcst", Rainfall);
  Particle.function("start-stop", startStop);       // Here are thre functions for easy control
  Particle.function("Enabled", wateringEnabled);    // I can disable watering simply here
  Particle.function("Measure", takeMeasurements);   // If we want to see Temp / Moisture values updated
  Particle.subscribe("hook-response/Ubidots_hook", UbidotsHandler, MY_DEVICES);       // Subscribe to the integration response event
  Particle.subscribe("hook-response/weatherU_hook", weatherHandler,MY_DEVICES);       // Subscribe to weather response

  Time.zone(-4);                            // Raleigh DST (watering is for the summer)

  Wire.begin();                             // Begin to initialize the libraries and devices
  Serial.begin(9600);
  sensor.begin();                           // reset the Chrip sensor
  NonBlockingDelay(2000);                   // The sensor needs 2 seconds after reset to stabilize

  EEPROM.get(lastWateredPeriodAddr,lastWateredPeriod);    // Load the last watered period from EEPROM
  EEPROM.get(lastWateredDayAddr,lastWateredDay);          // Load the last watered day from EEPROM
}


void loop() {
  if (Time.hour() != currentPeriod)                       // Spring into action each hour on the hour
  {
    Particle.publish("weatherU_hook");                    // Get the weather forcast
    NonBlockingDelay(5000);                               // Give the Weather Underground time to respond
    takeMeasurements("1");                                // Take measurements
    currentPeriod = Time.hour();                          // Set the new current period
    currentDay = Time.day();                              // Sets the current Day
    if (waterEnabled)
    {
      if (currentPeriod >= startWaterHour && currentPeriod <= stopWaterHour)
      {
        if ((strcmp(capDescription,"Very Dry") == 0) || (strcmp(capDescription,"Dry") == 0) || (strcmp(capDescription,"Normal") == 0))
        {
          if (currentPeriod != lastWateredPeriod || currentDay != lastWateredDay)
          {
            if (expectedRainfallToday <= rainThreshold)
            {
              strcpy(wateringContext,"Watering");
              if (currentPeriod == startWaterHour) wateringMinutes = longWaterMinutes;  // So, the first watering is long
              else wateringMinutes = shortWaterMinutes;                                 // Subsequent are short - fine tuning
            }
            else strcpy(wateringContext,"Heavy Rain Expected");
          }
          else strcpy(wateringContext,"Already Watered");
        }
        else strcpy(wateringContext,"Not Needed");
      }
      else strcpy(wateringContext,"Not Time");
    }
    else strcpy(wateringContext,"Not Enabled");
    Particle.publish("Watering", wateringContext);        // Update console on what we are doing
    sendToUbidots();                                      // Let Ubidots know what we are doing
  }
  if (wateringMinutes)                                    // This IF statement waits for permission to water
  {
    unsigned long waterTime = wateringMinutes * oneMinuteMillis;    // Set the watering duration
    wateringMinutes = 0;                                  // Reset wateringMinutes for next time
    turnOnWater(waterTime);                               // Starts the watering function
  }
}

void turnOnWater(unsigned long duration)                  // Where we water the plants - critical function completes
{
  // We are going to use the watchdog timer to ensure this function completes successfully
  // Need a watchdog interval that is slighly longer than the longest watering cycle
  // We will pet the dog then disable petting until the end of the function
  // That way, if the Particle freezes while the water is on, it will be reset by the watchdog
  // Upon reset, the water will be turned off averting disaster
  digitalWrite(donePin, HIGH);                            // We will pet the dog now so we have the full interval to water
  digitalWrite(donePin, LOW);                             // We set the delay resistor to 50k or 7 mins so that is the longest watering duration
  // Uncomment this next line only after you are sure your watchdog timer interval is greater than watering period
  doneEnabled = false;                                    // Will suspend watchdog petting until water is turned off
  // If anything in this section hangs, the watchdog will reset the Photon
  digitalWrite(blueLED, HIGH);                            // Light on for watering
  digitalWrite(solenoidPin, HIGH);                        // Turn on the water
  wateringNow = 1;                                        // This is a Particle.variable so you can see from the app
  NonBlockingDelay(duration);                             // Delay for the watering period
  digitalWrite(blueLED, LOW);                             // Turn everything off
  digitalWrite(solenoidPin, LOW);
  wateringNow = 0;
  // End mission - critical session
  doneEnabled = true;                                     // Successful response - can pet the dog again
  digitalWrite(donePin, HIGH);                            // If an interrupt came in while petting disabled, we missed it so...
  digitalWrite(donePin, LOW);                             // will pet the fdog just to be safe
  lastWateredDay = currentDay;
  lastWateredPeriod = currentPeriod;
  EEPROM.put(lastWateredPeriodAddr,currentPeriod);        // Sets the last watered period to the current one
  EEPROM.put(lastWateredDayAddr,currentDay);              // Stored in EEPROM since this issue only comes in case of a reset
  Particle.publish("Watering","Done");
}

void sendToUbidots()                                      // Houly update to Ubidots for serial data logging and analysis
{
  digitalWrite(donePin, HIGH);                            // We will pet the dog now so we have the full interval to water
  digitalWrite(donePin, LOW);                             // We set the delay resistor to 50k or 7 mins so that is the longest watering duration
  // Uncomment this next line only after you are sure your watchdog timer interval is greater than the Ubidots response period (about 5 secs)
  doneEnabled = false;                                    // Turns off watchdog petting - only a successful response will re-enable
  char data[256];                                         // Store the date in this character array - not global
  snprintf(data, sizeof(data), "{\"Moisture\":%i, \"Watering\":%i, \"key1\":\"%s\", \"SoilTemp\":%i}",capValue, wateringMinutes, wateringContext, soilTemp);
  Particle.publish("Ubidots_hook", data , PRIVATE);
  NonBlockingDelay(1000);                                 // Makes sure we are spacing out our Particle.publish requests
}

int startStop(String command)                             // So we can manually turn on the water for testing and setup
{
  if (command == "1")
  {
    wateringMinutes = shortWaterMinutes;                  // Manual waterings are short
    strcpy(wateringContext,"User Initiated");             // Add the right context for publishing
    Particle.publish("Watering", wateringContext);        // Update console on what we are doing
    sendToUbidots();                                      // Let Ubidots know what we are doing
    return 1;
  }
  else if (command == "0")                                // This allows us to turn off the water at any time
  {
    digitalWrite(blueLED, LOW);                           // Turn off light
    digitalWrite(solenoidPin, LOW);                       // Turn off water
    wateringNow = 0;                                      // Update Particle.variable
    Particle.publish("Watering","Done");                  // publish
    return 1;
  }
  else
  {
    Serial.print("Got here but did not work: ");          // Just in case - never get here
    Serial.println(command);
    return 0;
  }
}

int wateringEnabled(String command)                       // If I sense something is amiss, I can easily disable watering
  {
    if (command == "1")                                   // Default - enabled
    {
      waterEnabled = 1;
      return 1;
    }
    else if (command == "0")                              // Ensures no watering will occur
    {
      waterEnabled = 0;
      return 1;
    }
    else                                                  // Never get here but if we do, let's be safe and disable
    {
      waterEnabled = 0;
      return 0;
    }
  }

int takeMeasurements(String command)                      // Allows me to monitor variables between normal hourly updates
  {
    if (command == "1")                                   // Take a set of measurements
    {
      getMoisture();                                      // Test soil Moisture
      getWiFiStrength();                                  // Get the WiFi Signal strength
      soilTemp = int(sensor.getTemperature()/(float)10);  // Get the Soil temperature
      return 1;
    }
    else                                                  // In case something other than "1" is entered
    {
      return 0;
    }
  }

int getWiFiStrength()                                     // Measure and describe wireless signal strength
{
  int wifiRSSI = WiFi.RSSI();                             // Get the signed integer for RSSI
  char RSSIString[4];                                     // Need to create a char array so we can combine later
  snprintf(RSSIString,sizeof(RSSIString),"%d",wifiRSSI);  // Put string value into array
  char RSSIdescription[12];                               // Want to put the words with it as well
  if (wifiRSSI >= 0)                                      // Greater than zero is not possible
  {
    strcpy(Signal,"Error");
    return 0;
  }
  int strength = map(wifiRSSI, -127, -1, 0, 5);           // Map the RSSI values to words - valid RSSI falls in this range
  switch (strength)
  {
    case 0:
      strcpy(RSSIdescription,"Poor");
      break;
    case 1:
      strcpy(RSSIdescription, "Low");
      break;
    case 2:
      strcpy(RSSIdescription,"Medium");
      break;
    case 3:
      strcpy(RSSIdescription,"Good");
      break;
    case 4:
      strcpy(RSSIdescription,"Very Good");
      break;
    case 5:
      strcpy(RSSIdescription,"Great");
      break;
  }
  strcpy(Signal,RSSIdescription);                         // Combine signal description and value
  strcat(Signal,": ");
  strcat(Signal,RSSIString);                              // Value from above
  return 1;
}


void getMoisture()                                        // Here we get the soil moisture and characterize it to see if watering is needed
{
  capValue = sensor.getCapacitance();                     // capValue is typically between 300 and 700
  char capValueString[4];                                 // Create a char arrray and load with the capValue for later string formation
  snprintf(capValueString,sizeof(capValueString),"%d",capValue);
  int strength = map(capValue, 300, 500, 0, 5);           // Map - these values to cases that will use words that are easier to understand
  switch (strength)                                       // Main loop watering logic is based on capDescription not the capValue
  {
    case 0:
      strcpy(capDescription,"Very Dry");
      break;
    case 1:
      strcpy(capDescription,"Dry");
      break;
    case 2:
      strcpy(capDescription,"Normal");
      break;
    case 3:
      strcpy(capDescription,"Wet");
      break;
    case 4:
      strcpy(capDescription,"Very Wet");
      break;
    case 5:
      strcpy(capDescription,"Waterlogged");
      break;
    default:
      strcpy(capDescription,"Out of Range");              // I tweak the range to match normal values - need to see if not in range
      break;
  }
  strcpy(Moisture,capDescription);
  strcat(Moisture,": ");
  strcat(Moisture,capValueString);                        // Assemble the string that will be published
  Particle.publish("Moisture Level", Moisture);
}

void NonBlockingDelay(int millisDelay)                    // Used for a non-blocking delay - will allow for interrrupts and Particle calls
{
  unsigned long commandTime = millis();
  while (millis() <= millisDelay + commandTime)
  {
    Particle.process();                                   // This ensures that we can still service Particle processes
  }
  return;
}

void weatherHandler(const char *event, const char *data)  // Extracts the expected rainfall for today from webhook response
{
  // Uses forecast JSON for Raleigh-Durham Airport
  // Response template gets current date and qpf_allday
  // Only look at the current day
  // JSON payload - http://api.wunderground.com/api/(my key)/forecast/q/nc/raleigh-durham.json
  // Response Template: "{{#forecast}}{{#simpleforecast}}{{#forecastday}}{{date.day}}~{{qpf_allday.in}}~{{/forecastday}}{{/simpleforecast}}{{/forecast}}"
  if (!data) {                                            // First check to see if there is any data
    Particle.publish("Weather", "No Data");
    return;
  }
  char strBuffer[30] = "";                                // Create character array to hold response
  strcpy(strBuffer,data);                                 // Copy into the array
  int forecastDay = atoi(strtok(strBuffer, "\"~"));       // Use the delimiter to find today's date and expected Rainfall
  expectedRainfallToday = atof(strtok(NULL, "~"));
  snprintf(Rainfall,sizeof(Rainfall),"%4.2f",expectedRainfallToday);
}

void UbidotsHandler(const char *event, const char *data)  // Looks at the response from Ubidots - Will reset Photon if no successful response
{
  // Response Template: "{{watering.0.status_code}}"
  if (!data) {                                            // First check to see if there is any data
    Particle.publish("UbidotsResp", "No Data");
    return;
  }
  int responseCode = atoi(data);                          // Response is only a single number thanks to Template
  if ((responseCode == 200) || (responseCode == 201))
  {
    Particle.publish("UbidotsHook","Success");
    doneEnabled = true;                                   // Successful response - can pet the dog again
    digitalWrite(donePin, HIGH);                          // If an interrupt came in while petting disabled, we missed it so...
    digitalWrite(donePin, LOW);                           // will pet the dog just to be safe
  }
  else Particle.publish("UbidotsHook", data);             // Publish the response code
}

void watchdogISR()                                        // Will pet the dog ... if petting is allowed
{
  if (doneEnabled)                                        // the doneEnabled is used for Ubidots and Watering to prevent lockups
  {
    digitalWrite(donePin, HIGH);                          // This is all you need to do to pet the dog low to high transition
    digitalWrite(donePin, LOW);
  }
}
2 Likes

I don’t see anything in there that should cause those disconnects. I have tried to track this down in one of my projects, and after logging the connection status in my cat door controller program along with another Photon running only the logger, I came to the conclusion that the dropouts were due to the router, not anything in my code (the dropouts came at the same time in both devices). In my case the router was an Airport Express acting as a WiFi range extender. Even though the one Photon had a very strong signal (RSSI values around -45), I saw these correlated dropouts. I’m doing more testing by trying to “see” (with a photo resistor) the state of the LED on the Airport’s face to confirm this result. My guess is that your router is either temporarily losing the internet, or your device is losing connection because of poor signal; what kind of RSSI values are you getting?

One comment on your NonBlockingDelay() function. It is actually blocking, in the sense that nothing else (other than Particle.process) can run during the while loop. This is the same behavior that the delay() function has, so there’s no need to create your own function. If you really want it to be non-blocking, you should use a regular millis timer.

The getWiFiStrength() function is more verbose than it needs to be, though there’s nothing wrong with it in functional terms. Rather than using a switch block, strcpy(), and strcat(), that code could be simplified using an array of the strength words, and sprintf() to build the string. Something like this,

char* levels[6] = {"Poor", "Low", "Medium", "Good", "Very Good", "Great"};

int getWiFiStrength()               
{
    int wifiRSSI = WiFi.RSSI();
    if (wifiRSSI > 0) {
        sprintf(Signal, "Error");
    }else {
        int strength = map(wifiRSSI, -127, -1, 0, 5);
        sprintf(Signal, "%s: %d", levels[strength], wifiRSSI);
    }
    return 1;
}

The getMoisture() function could be streamlined in a similar manner.

2 Likes

I’d also set an explicit antenna type rather than enabling the sometimes “flaky” ANT_AUTO.

Also check your router (and other devices) for IP collisions.

1 Like

Ric,

Wow! Thank you. I really like what you did with my getWiFiStrength() function! I use this approach in several projects so your suggestion will pay many dividends.

On the NonBliockingDelay() function, I see your point. My biggest concern in this program is that something happens while the solenoid is open and the Photon locks up. So, limiting what can happen during this time is what I was going for. Having said that, I use it in other places as well so I will take another look. Thank you for pointing this out as I was not aware that interrupts are serviced with delay() until you pointed this out.

I just installing three Google WiFi routers in a mesh and am otherwise very happy with the setup. However, perhaps there is something going on with my setup and I will look into this.

Thanks again for your help and advice on how to improve my code.

Chip

ScruffR,

OK, I will change that line to STARTUP(WiFi.selectAntenna(ANT_EXTERNAL)); and see if it makes a difference.

Thanks,

Chip

@ScruffR,

That fixed it. Changed ANT_AUTO to ANT_EXTERNAL and there has not been a single disconnect in two days.

Thank you!

Chip

1 Like

I am not using an external antenna, but I am experiencing disconnects.
I narrowed it down to WiFi channel switching and just reported it on GitHub:

I am of course interested in increasing the WiFi stability. Is it recommended to explicitly set the antenna with a P1 using the on board antenna?

2 Likes

I’m seeing the same thing. I have a about -63db photon-reported -71db AP-reported connection strength which isn’t fantastic but it should be enough. I also have a Ubiquiti Router and Ubiquiti Pro AP so generally my wifi and routing is really good. However about once every 3-5 minutes I see a disconnect/reconnect (5 seconds after the disconnect). What’s interesting is that the Ubiquiti Controller thinks the wifi connection has been maintained through nearly all of the disconnects.

On the firmware side all I have is a loop watching the D0 high/low so I’m confident it’s not my code.

I bought an 9db antenna to check and see if that would fix it but no luck. I also set the antenna to ANT_EXTERNAL. AUTO was even more flakey than just the internal antenna. Still no luck. Should I just try a 2db antenna?

Seems odd to be disconnecting so much. Is there a setting to maybe turn up its patience on the connected/disconnected timeout?