Photon Breathing Green and code locks up

I’ve created my first project which is a project for opening and closing a door that lets my chickens in and out. It also checks their water level every hour and fills as needed. The door opens in the AM and closes in the PM. 4 minutes after the door is activated it publishes the status of the door and the water to an IFTTT applet. I created this because I travel quite a bit and I don’t like to bother friends/relatives to daily check on the hens. I have been having an issue where it will randomly just breathe green. When this happens the door and water activities don’t happen and the LCD will not update indicating to me that the application code is locked up. It has taken anywhere from 2 days to 7 days for this to happen. Based on the current time shown on the LCD when it locks up the only function that should be running is loop. The LCD has never indicated a lock up time when any other functions are activated. My code is based strictly on a 24 hour period. There are no multi-day activities at all. When I first started troubleshooting this I activated an iFTTT app that notifies me when the photon goes offline and online. That was happening over 60 times per day. So, I rewrote the code to strip it down to the bare essentials. I also changed to another home network that seemed to have a stronger signal and I added STARTUP(WiFi.setListenTimeout(60)); to hopefully kick it out of listening mode if it got locked into it. These made it so it was going offline/online less than 10 times per day. It ran for almost 7 days this time but this morning it happened again and the LCD read 0535. Nothing should have been happening at this point. Everything I’ve read on the forum indicates this is a problem of application code being locked in a loop. If that is the case I can’t see it in my code and why will it run multiple days just fine and then fail when all code is on a 24 hour time frame? I’m gong to try and post the code below. I haven’t been able to find guidance on how to post the code in a scrolling window like most folks do. So, sorry for the inconvenience and thanks for whatever suggestions you might have to stop this problem.

STARTUP(WiFi.setListenTimeout(60));//added this to the code to try and prevent photon from locking into listening mode
// This #include statement was automatically added by the Particle IDE.
#include <LiquidCrystal_I2C_Spark.h>

LiquidCrystal_I2C*lcd;
int waterSensorPin = 7;//sets pin A3 as input for the water level sensor
int waterSensor;//creates variable for storing value of waterSensorPin
int solenoidActivatePin = A2;//sets pinA4 as output to relay that controls water solenoid
int topSensorPin = 2;  // Sets the top door sensor as pin A2
int topSensor;//Variable for storing topSensorPin value
int bottomSensorPin = 3;  //Sets bottom door sensor as pin A3
int bottomSensor;//Variable for storing topSensorPin value
int motorOpenPin = 4;  //Sets pin A4 to drive door open motor
int motorClosePin = 5; //Sets pin A5 to drive door close motor
int lowPowerOn = 6;//Allows the photon to turn on the 3.3 volt power supply when needed to check the door sensors
int doorStatus;//creates a variable for storing value of current door status, Open, Closed, In Transit
int openHour;//Creates variable for storing open time hour
int openMinute;//Creates variable for storing open time minute
int closeHour;//Creates variable for storing close time hour
int closeMinute;//Creates variable for storing close time minute
int publishDoorStatus;//Creates variable to ensure that the doorStatus is only published once per event
//int testTime = 100;//Used for troubleshooting to slow serial output to make it readable

void setup() {
WiFi.selectAntenna(ANT_EXTERNAL);
WiFi.setCredentials("**************", "***************");
Serial.begin(9600);
delay(100);
pinMode(lowPowerOn, OUTPUT);//Makes lowPowerOn a digital output pin
digitalWrite(lowPowerOn, HIGH);//Initializes lowPowerOn to HIGH to turn off 3.3Vol power supply to save battery
pinMode(topSensorPin, INPUT);// Makes the topSensor pin a digital Input
pinMode(bottomSensorPin, INPUT);//Makes the bottomSensor pin a digital input
pinMode(motorOpenPin, OUTPUT);//Makes the motorOpen drive pin a digital output
digitalWrite(motorOpenPin, HIGH);//Sets the motorOpen output to high for the relay
pinMode(motorClosePin, OUTPUT);//Makes the motorClose output pin a digital output
digitalWrite(motorClosePin, HIGH);//Sets the motorOpen output pin to high for the relay
pinMode(waterSensorPin, INPUT);//declares water sensor pin as an input
pinMode(solenoidActivatePin, OUTPUT);//declares solenoid activate pin as an output
digitalWrite(solenoidActivatePin, HIGH);//initializes solenoidActivatePin as HIGH so the relay will not activate
openHour = 06;//Sets a time to open door
openMinute = 30;
closeHour = 19;//Sets a time to close the door
closeMinute = 00;
lcd = new LiquidCrystal_I2C(0x3F, 16, 2); //address of the I2C module for the LCD, sets it for 2 rows and 16 columns
lcd->init(); //Initializes the LCD
lcd->backlight(); //turns on LCD backlight
lcd->clear(); //clears the LCD screen
Time.zone(-5);//Sets the Central Time Zone


}

void loop() 
{
Particle.connect();//added to increase the frequency that the photon tries to connect to the cloud to prevent lock up
delay(100);
displaySet();//calls function to update the LCD
waterMonitor();//calls function to check water level
if((openHour == Time.hour()) && ((Time.minute() >= openMinute) && (Time.minute() <= openMinute +1)))
    {//when current time matches open time triggers functions to open door adds minute because it takes more than 1 minute to open door
    checkSensor();//triggers function to check door top and bottom sensor and determine door status
    openMotor();//uses sensor data and calls function to activate door motor to open
    }
if((closeHour == Time.hour()) && ((Time.minute() >= closeMinute) && (Time.minute() <= closeMinute +1)))
    {//when current time matches close time triggers functions to close door adds minute because it takes more than 1 minute to close door
    checkSensor();//triggers function to check door top and bottom sensor and determine door status
    closeMotor();//uses sensor data and calls function to activate door motor to close
    }
if((Time.minute() == openMinute +2)  || (Time.minute() == closeMinute +2))
    {//turns off 3.3v power supply to sensors to save LEDs and battery
    digitalWrite(lowPowerOn, HIGH);
    }
if(((openHour == Time.hour()) && (Time.minute() == (openMinute +4))) || ((closeHour == Time.hour()) && (Time.minute() == closeMinute +4)))
    {//triggers function to publish door and water status 4 minutes after door is activated to open or close
    statusPublish();
    }
}
void displaySet()//updates LCD with every loop
{
lcd->clear();
lcd->setCursor(1, 0);
lcd->print("TIME OPEN CLOSE");
lcd->setCursor(1, 1);
if(Time.hour() < 10)
    {
    lcd->print("0");
    }
lcd->print(Time.hour());
lcd->setCursor(3, 1);
if(Time.minute() < 10)
    {
    lcd->print("0");
    }
lcd->print(Time.minute());
lcd->setCursor(6, 1);
if(openHour <10)
    {
    lcd->print("0");
    }
lcd->print(openHour);
lcd->setCursor(8, 1);
if(openMinute < 10)
    {
    lcd->print("0");
    }
lcd->print(openMinute);
lcd->setCursor(11, 1);
if(closeHour < 10)
    {
    lcd->print("0");  
    }
lcd->print(closeHour);
lcd->setCursor(13, 1);
if(closeMinute < 10)
    {
    lcd->print("0");
    }
lcd->print(closeMinute);
return;
}

void waterMonitor()//checks water sensor and activates solenoid to fill if neededd
{
waterSensor = digitalRead(waterSensorPin);
if ((Time.minute() == 10) && (waterSensor == HIGH))//added time component to prevent flooding if there is a sensor malfunction
    {
    digitalWrite(solenoidActivatePin, LOW);
    lcd->init();//use this at every point there is a major draw on the battery to prevent LCD from displaying gibberish
    }
else//shut off water if full or a minute has passed
    {
    digitalWrite(solenoidActivatePin, HIGH);
    lcd->init();//use this at every point there is a major draw on the battery to prevent LCD from displaying gibberish
    }
}


void checkSensor()//checks top and bottom sensor and assigns a status value
{
digitalWrite(lowPowerOn, LOW);//Turns on 3.3Volt power supply which powers sensor and LED's through relay
delay(500); 
topSensor = digitalRead(topSensorPin);//Assigns the value of topSensoPin to variable topSensor
delay(100);
bottomSensor = digitalRead(bottomSensorPin);//Assigns the value of bottomSensorPin to variable bottomSensor
delay(100);
//Serial.println("Comparing the status of the door sensors to determine door status value");//Used in Troubleshooting
//delay(testTime);
if ((topSensor == LOW) && (bottomSensor == LOW))
    {
    doorStatus = 100;//Gives doorStatus a value of 100 which means the door is all the way open
    }
if ((topSensor == HIGH) && (bottomSensor ==HIGH))
    {
    doorStatus = 200;//Gives doorStatus a value of 200 which means the door is all the way closed
    }
if (topSensor != bottomSensor)
    {
    doorStatus = 300;//Gives doorStatus a value of 300 which means the door is in transit
    }
return;
}

void openMotor()//activates motor to open if conditions met
{
if ((doorStatus == 200) || (doorStatus == 300))//turns motor on if door is closed or in transit
    {
    digitalWrite(motorOpenPin, LOW);
    delay(1000);
    }
else//shuts motor off when conditions no longer met
    {
    digitalWrite(motorOpenPin, HIGH);
    }
lcd->init();//initializes LCD to prevent gibberish on the lcd due to power fuctuations caused by motor
return;
}   

void closeMotor()//activates motor to close if conditions met
{
if((doorStatus == 100) || (doorStatus == 300))//activates motor if door is open or in transit
    {
    digitalWrite(motorClosePin, LOW);
    delay(1000);
    }
else//shuts off motor when conditions no longer met
    {
    digitalWrite(motorClosePin, HIGH);
    }
lcd->init();//initializes LCD to prevent gibberish on screen due to motor power fluctuations
return;
}   

void statusPublish()//publishes door status and water status when called to IFTTT
{
if (doorStatus != publishDoorStatus)//if there has been a change in door status publishes to IFTTT
    {
    if(doorStatus == 100)//publishes that the door has opened all the way
        {
        Particle.publish("statusOfDoor", "Open");
        }
    if(doorStatus == 200)//publishes that the door has closed all the way
        {
        Particle.publish("statusOfDoor", "Closed");
        }
    if(doorStatus == 300)//publishes that door is not in it's expected state
        {
        Particle.publish("statusOfDoor", "Fault");
        }
    delay(1000);
    if(waterSensor == HIGH)//publishes that water is low
        {
        Particle.publish("statusOfWater", "NotFull");
        }
    if(waterSensor == LOW)//publishes that water level is where it is expected
        {
        Particle.publish("statusOfWater", "Full");
        }
    }
publishDoorStatus = doorStatus;//sets publishDoorStatus so that everything is only published once per call    
return;
}

Some notes

  • since you are running default SYSTEM_MODE(AUTOMATIC), you won’t need to call Particle.connect()
  • due to above your WiFi.setCredentials() would never be executed if they weren’t already stored and hence the reason for it being there at all seems obscure
  • if you needed to call Particle.connect() you shouldn’t be calling it repeatedly and unconditionally in loop()
  • I’d not use Time.hour() and Time.minute() to do your time range checks, but rather go with seconds based Time.local() % 86400 (thats the number of the second for the given day) which makes these checks much more reliable and easy to code
  • since Timer is second-based but your conditions only check for minutes you may need to guard blocks that could be called repeatedly in one minute against being called repeatedly for the duration of that given minute
2 Likes

-I added the Paticle.connect() based on some other posts for breathing green that suggested adding it. It didn’t change anything so I’ll eliminate it.
-I added the wifi credentials because since the photon is inside a weatherproofed box it is not easily accessible and I’ve been chasing this problem from multiple angles for several weeks and it easier to switch networks to rule out a problem when I need to. If I ever get this problem fixed I will eliminate it.
-If I get the locking up problem fixed I will look at the Time changes unless you think they could be the cause of the breathing green locking up problem.

-Do you see anything in the code that could be causing the problem? It never happens in the functions called from the loop. Always happens when running in the loop. I just can’t see anything that would cause it especially since it never happens at even close to the same period since reset.

-Did you add the scroll bars for me? If so, thanks and how do I do that?

Thanks for your input

Just spent a couple of hours looking through the reference and I’m going to try this:
-Added SYSTEM_THREAD(ENABLED); above the global declarations
-Added waitUntil(Particle.connected); in the setup prior to any Particle.publish
-Added to the top of the loop:
if(!Particle.connected())
{
delay(30000);
if(!Particle.connected())
{
System.reset();
}
}
Clunky but as I understand it, if the photon loses the cloud connection it will delay for 30 seconds, check again, and if still not connected will perform a software reset of the photon. I’m not collecting any data and nothing is based on time of last reset so this shouldn’t cause any issues.

This doesn’t tell me why I am having the problem but it should ensure that it starts working if it locks up. A manual reset always fixes it when it happens.

Did you incorporate the comments ScruffR mentioned ?

As he said, since you don’t declare a SYSTEM_MODE, the default is AUTOMATIC.
You wont need any Particle.connect, etc. Let the Photon handle that by itself.
And you don’t want a 30 second delay in your latest post, that wont work like you hope.

In your application, I don’t see a need in performing any actions every trip through loop()
Try something like this:

SYSTEM_THREAD(ENABLED);
unsigned long lastCheck = 0;

void setup() {
// your stuff here
}

void loop() {
  if ((millis() -  lastCheck) >= 1000) {  // in (ms), Runs each 1000 ms = 1 second
  // your stuff here

   lastCheck = now;
  }
} // End Loop

// your other functions...

Don’t use the WiFi.setListenTimeout(60), Particle.connect, etc, etc, and give it a test.

I took out the Particle.connect. I left in the WiFi credentials because it’s just physically easier to change the network if I need to.

I thought the statement ,

if(!Particle.connected())
{
delay(30000);
}

Would only run if my photon wasn’t connected to the cloud. If that’s true then there won’t be a delay ordinarily when it is connected.

I’ll take out the Listen timeout statement. I only put it in because this seems to be a common problem and some had suggested it would work in the forum but it was in place for the last failure.

Thanks for the input

That's correct, but I believe that delay also prevents the Photon from doing anything (connecting, etc).

I'd try to keep the code as simple as possible in the beginning. Let the Photon manage the Cloud Connection.
Don't use any code that would prevent your Photon from performing it's duties (operating the Door, refilling water, etc), just because it isn't connected to the Cloud at that particular moment.

You can always add code inside statusPublish() to verify that the cloud received the publish, in the future.

But most importantly, pay close attention to ScruffR's comments.

1 Like

Ok

Ok. I made the recommended changes and it locked up within 24 hours. I had read one post on another thread where it was stated that turning the Wifi off and on when this happens would correct the issue. So, left the SYSTEM_THREAD(ENABLED) in and using the trick Rftop described earlier in this thread using the millis() and unsigned long to create a counter I created a function to check if it was in the state where the WiFi was connected but the cloud was not for over 5 minutes. If this happened I had it shut the WiFi off then another counter turned it back on after a minute. I also added a counter function that Serial printed how many times the WiFi had been turned off and left it hooked to my computer so I could check it every so often. It went almost 10 days with out shutting WiFi off and all was well. I came out yesterday and it was breathing green but the Serial output was still working and it indicated that the WiFi had been turned off 2 times. I had to go out for the day so I left it running. Came back that night and it was breathing blue. The serial print indicated the WiFi had been turned off over 42 times in the span of a couple of hours but had not been turned off since and was currently up. Left it on over night and still no more WiFi turn offs. Thanks for the assist guys. I’m calling this one fixed.

4 Likes