// Plant moisture monitoring
// by Gaston Paradis July 5, 2015
// This program will monitor the soil moisture of a plant and send an email when it need waterning
// Will use DEEP SLEEP to minimized the usage of battery
#define MOISTPIN_1 A0 // Pin where the soil moisture sensor is connected
#define READING_FREQUENCY (1*10*1000) // Frequency of reading the soil moisture
#define FLAG 1 // EEPROM memory location to indicate if the minimum have been reached
#define DAY 2 // EEPROM memory location containning the day the error message have been send
struct Content{ // Create a structure to contain data for each sensor
char name[15];
char plant[15];
int pin; // Pin where the soil moisture sensor is connected
int reading; // Will store the reading of the soil moisture for sensor
int minimum; // Level below what an alarm should be send for sensor
int alarm_from; // Publishing should be done only after this hour
int alarm_to; // Publishing should be done only until this hour
};
struct Content sensor = {"Sensor #2","Cactus",MOISTPIN_1,0,25,8,23};// Loading variable values
unsigned long lastloop = 0; // Use to control the next reading
char publishString [40]; // Use to publish to the register
char publish_event[10];
void setup() {
Serial.begin(9600);
Time.zone(-4); // Set time zone to Eastern USA daylight saving time
// DST Daily Saving Time Second Sunday March until First Sunday vember at 02:00
//Time.zone(-4); // Set time zone to Eastern USA daylight saving time
// DST Daily Saving Time Second Sunday March until First Sunday vember at 02:00
sensor.reading = map(analogRead(sensor.pin), 0, 4100, 100, 0); // Initial reading
EEPROM.write(FLAG, FALSE); // The reading is above minimum set the falg FALSE INITIAL VALUE
EEPROM.write(DAY,99); // Make sure there is no date in the memory
delay(2000); // temporaly to slow down things
sprintf(publish_event,"Moisture");
sprintf(publishString,"%u:%u:%u|||%i|||%i",Time.hour(),Time.minute(),Time.second(),sensor.reading,sensor.minimum);
Spark.publish(publish_event,publishString);
delay(2000); // temporaly to slow down things for publication
if(sensor.reading >= sensor.minimum){ // Flag to indicate that minimum has been reached
Spark.publish("DEBUG setting the flag to FALSEt");
Serial.println("DEBUG setting the flag to FALSEt") ;
delay(2000);
EEPROM.write(FLAG, TRUE); // The reading is above minimum set the falg FALSE
EEPROM.write(DAY,99); // Make sure there is no date in the memory
}
//Publish need water only once day
if ((sensor.reading < sensor.minimum) // the moisture reading is below the minimum?
// I wish to publish an alarm only within a determine time (not in the midle of the night)
and ( Time.hour() >sensor.alarm_from) // the current hour is more than the minimum hour to publish alarm
and (Time.hour() <sensor.alarm_to) // the current hour is less that the maximum hour to publish an alarm
) {
Spark.publish("DEBUG below minimum");
if(EEPROM.read(FLAG) == FALSE){ // The flag not being set: first time around MSG
// Spark.publish("Need Water");
delay(2000); // temporaly to slow down things for publication
//Spark.publish("DEBUG Flag not set");
delay(2000); // temporaly to slow down things for publication
EEPROM.write(FLAG, TRUE); // Set the flag because the minimum has been reached
EEPROM.write(DAY, Time.day()); // Remember the date the msg was published
}
if(EEPROM.read(FLAG == TRUE)) { // We have been here before
if(EEPROM.read(DAY) != Time.day() ){ // Stored date is different than current day
//Spark.publish("Need Water");
delay(2000); // temporaly to slow down things for publication
//Spark.publish("DEBUG Flag set not same date");
EEPROM.write(DAY, Time.day()); // Remember the date the msg was published
}
else{
//Spark.publish("DEBUG Flag set, same date");
delay(2000); // temporaly to slow down things for publication
}
}
}
delay(30000); // wait 20 second doing nothing to allow update OTA (Over The Air)
Spark.sleep(SLEEP_MODE_DEEP,2); // Goto Deep Sleep for 2 seconds
}
void loop() {
// The main loop does nothing
}
@Gaston, lines like this Spark.publish("DEBUG below minimum"); are missing an event name! You should use a non blocking millis() counter instead of the delay(30000); and call Spark.process() in the look to allow background processing:
unsigned long waitOTA;
void setup() {
...
waitOTA = millis();
while (millis() - waitOTA < 30000)
Spark.process();
Spark.sleep(...
@Gaston, usually when a Core won’t listen it is due to user code not allowing the background process to run or causes a memory error (array out of bounds, etc.). The suggestions @ScruffR and I made were to avoid both situations. My observation regarding your Spark.publish() calls was not correct HOWEVER you do need to avoid spaces in the event text.
If you wan to OTA to your non-listening core, you will need to do a factory reset, redo the wifi credentials and then flash your code over. Alternatively, you could use Particle CLI to flash your code via USB by putting the Core in DFU mode.
If you’ve got CLI installed, put your Core into DFU mode (see docs how to) and flash via USB the code with @peekay123 improvements regarding 30sec delay.
After you got this sorted OTA should work for future updates.
If you haven’t got CLI and don’t want to install it, you can do a factory reset and then flash updated code OTA.
Should I use the suggested code AS IS.
I insert the unsigned… at the beginning of my program.
Then I cut an paste your suggestion. I am getting a compiling error < error: lvalue required as left operand of assignment> for the line while…
Here is my modified program
// Plant moisture monitoring
// by Gaston Paradis July 5, 2015
// This program will monitor the soil moisture of a plant and send an email when it need waterning
// Will use DEEP SLEEP to minimized the usage of battery
#define MOISTPIN_1 A0 // Pin where the soil moisture sensor is connected
#define READING_FREQUENCY (1*10*1000) // Frequency of reading the soil moisture
#define FLAG 1 // EEPROM memory location to indicate if the minimum have been reached
#define DAY 2 // EEPROM memory location containning the day the error message have been send
struct Content{ // Create a structure to contain data for each sensor
char name[15];
char plant[15];
int pin; // Pin where the soil moisture sensor is connected
int reading; // Will store the reading of the soil moisture for sensor
int minimum; // Level below what an alarm should be send for sensor
int alarm_from; // Publishing should be done only after this hour
int alarm_to; // Publishing should be done only until this hour
};
struct Content sensor = {"Sensor #2","Cactus",MOISTPIN_1,0,25,8,23};// Loading variable values
char publishString [40]; // Use to publish to the register
char publish_event[10];
unsigned long waitOTA; //used to delay
void setup() {
Serial.begin(9600);
Time.zone(-4); // Set time zone to Eastern USA daylight saving time
// DST Daily Saving Time Second Sunday March until First Sunday vember at 02:00
//Time.zone(-4); // Set time zone to Eastern USA daylight saving time
// DST Daily Saving Time Second Sunday March until First Sunday vember at 02:00
sensor.reading = map(analogRead(sensor.pin), 0, 4100, 100, 0); // Initial reading
if ((EEPROM.read(FLAG)!= FALSE) or (EEPROM.read(FLAG)!= TRUE )){ //If this is the first time the program run
// a random value will be populated
// need to set a value
EEPROM.write(FLAG, FALSE); // The reading is above minimum set the falg FALSE INITIAL VALUE
EEPROM.write(DAY,99); // Make sure there is no date in the memory
}
delay(2000); // temporaly to slow down things
sprintf(publish_event,"Moisture");
sprintf(publishString,"%u:%u:%u|||%i|||%i",Time.hour(),Time.minute(),Time.second(),sensor.reading,sensor.minimum);
Spark.publish(publish_event,publishString);
delay(2000); // temporaly to slow down things for publication
if(sensor.reading >= sensor.minimum){ // Flag to indicate that minimum has been reached
Spark.publish("DEBUG setting the flag to FALSE");
//Serial.println("DEBUG setting the flag to FALSE") ;
delay(2000);
EEPROM.write(FLAG, TRUE); // The reading is above minimum set the falg FALSE
EEPROM.write(DAY,99); // Make sure there is no date in the memory
}
//Publish need water only once day
if ((sensor.reading < sensor.minimum) // the moisture reading is below the minimum?
// I wish to publish an alarm only within a determine time (not in the midle of the night)
&& ( Time.hour() >sensor.alarm_from) // the current hour is more than the minimum hour to publish alarm
&& (Time.hour() <sensor.alarm_to) // the current hour is less that the maximum hour to publish an alarm
) {
Spark.publish("DEBUG below minimum");
if(EEPROM.read(FLAG) == FALSE){ // The flag not being set: first time around MSG
// Spark.publish("Need Water");
delay(2000); // temporaly to slow down things for publication
//Spark.publish("DEBUG Flag not set");
delay(2000); // temporaly to slow down things for publication
EEPROM.write(FLAG, TRUE); // Set the flag because the minimum has been reached
EEPROM.write(DAY, Time.day()); // Remember the date the msg was published
}
if(EEPROM.read(FLAG == TRUE)) { // We have been here before
if(EEPROM.read(DAY) != Time.day() ){ // Stored date is different than current day
//Spark.publish("Need Water");
delay(2000); // temporaly to slow down things for publication
//Spark.publish("DEBUG Flag set not same date");
EEPROM.write(DAY, Time.day()); // Remember the date the msg was published
}
else{
//Spark.publish("DEBUG Flag set, same date");
delay(2000); // temporaly to slow down things for publication
}
}
}
//delay(30000); // wait 20 second doing nothing to allow update OTA (Over The Air)
waitOTA = millis();
while (millis() = waitOTA < 30000)
Spark.process();
Spark.sleep(SLEEP_MODE_DEEP,2); // Goto Deep Sleep for 2 seconds
}
void loop() {
// The main loop does nothing
}
On a Core you should allow for 30sec, since the application and the system firmware need to be transfered.
But if you set up a button or a Spark.function() to enter “OTA mode” these 30sec won’t slow down your normal application sessions.
Even with the 30sec I sometimes have to flash multiple times even though I get a successful flash message. I’ve noticed this on another device that doesn’t use deep sleep, though it restarts like it was flashed. I’ve gotten into the habit of publishing a version number on startup so I can tell that the new code is actually running but it’s a bit annoying.
Since you have up to 10 sec of delays scattered over your code and your code also will consume some time plus the the time to reconnect on startup, how do you decide when to press “flash”?
As suggested above, only enter OTA mode on demand and then stay there as long as you see fit.
The Photon should take a lot less time than the Core, it usually only needs to update app firmware (unless you’ll transition to a new FW version).
Further more is there a pending issue, where application code was able to interfere with OTA firmware download, which resulted in the device still running old code after apparent successful update.
Thanks for the info. The above is not my code; my code publishes a startup message, takes a few seconds to check some sensors, publishes the result and then does peekay’s Spark.process loop for 30sec. I typically press flash from the Web interface when I see the startup message in the Web dashboard. I’m not near the device so a button won’t help and I figure a function call will have a similar timing issue so overall it’s easier just to flash a few times.
uint32_t msTimeout = 10000; // normal timeout if not OTAing
uint32_t msMillis;
void setup()
{
Spark.function("waitOTA", extendDelay);
...
msMillis = millis();
while(millis() - msMillis < msTimeout)
Spark.process();
...
}
int extendDelay(String cmd)
{
msTimeout = 180000; // or cmd.toInt() if you want to control it remotely
msMillis = millis();
}
Alternatively you could listen for the SSE event that gets published each time your Particle comes online, trigger an event to which your device subscribed that will force it into OTA mode.