I did not know about waitUntil(Particle.connected);
, I also did not know that Particle.connect()
already does WiFi.connect()
, does it also do WiFi.on()
? Going to check it out, because I’d like to improve the code if that would be better.
Regardless of that though, I have no problems with publishing. All my data is transferring from the Photon to the Particle cloud. My problem is that the device is not waking up. It was in AUTOMATIC
mode, so it should connect to the internet first, but that didn’t happen. The device was just in sleep mode when I arrived to check it this morning. It was well over time as well.
Below is the full code, I’ve blanked some data like WiFi credentials for obvious reasons. Again, thanks for taking the time to look at this.
// ##### ####### ####### ####### ### # # ##### #####
// # # # # # # ## # # # # #
// # # # # # # # # # #
// ##### ##### # # # # # # # #### #####
// # # # # # # # # # # #
// # # # # # # # ## # # # #
// ##### ####### # # ### # # ##### #####
//BEFORE DEPLOYMENT CHANGE THE FOLLOWING VALUES TO THAT OF THE LOCATION!!!
//Location's WiFi information
const char* SSID_Location = "XXXXXXXX"; //WiFi network name
const char* PASS_Location = "XXXXXXXX"; //Password of the network.
//The location in our database, don't forget: create location in database.
const int location = 4; //this is a predefined location found in the database.
//At what time the device should go to sleep.
const float sleepHour = 18;
const float sleepMinute = 00;
//At what time the device should wake up.
const float wakeupHour = 7;
const float wakeupMinute = 30;
//MAKE SURE ALL THESE VARIABLES ARE SET PROPERLY BEFORE DEPLOYING THE DEVICE AT THE LOCATION.
// # # ### ####### ###
// # # # # # #
// # # # # # #
// # # # # ##### #
// # # # # # #
// # # # # # #
// ## ## ### # ###
//WIFI CREDENTIALS for development
/*DO NOT CHANGE*/ const char* SSID = "XXXXXXXX";
/*DO NOT CHANGE*/ const char* PASS = "XXXXXXXX";
//Only change these values if they change at the location
// # ### ###### ###### # ###### ### ####### #####
// # # # # # # # # # # # # # #
// # # # # # # # # # # # # #
// # # ###### ###### # # ###### # ##### #####
// # # # # # # ####### # # # # #
// # # # # # # # # # # # # # #
// ####### ### ###### # # # # # # ### ####### #####
#include <RunningMedian.h> //Averages the sensorValues to prevent false positives.
//Library by Rob Tillaart, https://github.com/RobTillaart
// # # # ###### ### # ###### # ####### #####
// # # # # # # # # # # # # # # #
// # # # # # # # # # # # # # #
// # # # # ###### # # # ###### # ##### #####
// # # ####### # # # ####### # # # # #
// # # # # # # # # # # # # # # #
// # # # # # ### # # ###### ####### ####### #####
//TIME RELATED MEASUREMENTS / TIMED ACTIVITIES
int tHour; //stores the hours
int hourOffset = 2; //Offset from server time to our time, also used to emulate night times.
int tMinute; //stores the minutes
int mMinute = 60; //used as a modulo to dictate time intervals (modulo is not necessary if you push only once per hour).
int tDay; //Used to know when it's weekend. 5, 6, 7 = Friday, Saturday, Sunday
int tSecond; //stores the seconds
int cooldown = 750; //Cooldown periods ensuring certain functions are done properly.
//Moment for debugging, make sure this falls within the active / non sleep periods.
int debugStartHour = 9; //Starting hour of the debug interval
int debugStartMinute = 0; //Starting minute of the debug interval
int debugEndHour = 9; //Ending hour of the debug interval
int debugEndMinute = 30; //Ending minute (exclusive) of the debug interval (if full hour, debugEndMinute should be 60, not 0)
bool debugState = false; //Used for other functions to detect if the device is in debug mode.
unsigned long printTimer; //Used to make the printing happen less often (once every interval)
int prInterval = 1000; //Print interval
//Based on what time the device should go to sleep and wake up, the sleep time in seconds is calculated.
//DO NOT CHANGE THE FOLLOWING FORMULAS, THIS AUTOMATICALLY CALCULATES THE SLEEPTIME!
unsigned long sleepTime = 0;//Explicitely 0 to ensure that even if the calculation fails for some reason, the device boots uip immediately.
bool sleepState = false; //Used to indicate if the device should go to sleep.
//PUBLISHSTRING used to Publish the location and value to the Particle Cloud.
char publishString[40];
//TRANSMISSION CONFIRMATIONS
int wifiState = 0; //Used to know if WiFi is on, working, connected and if a dataPublish is possible.
unsigned long stateTimer; //Used to make sure the wifiState has enough 'delay' to process all commands.
//DISTANCE SENSOR
int sensorPin = (A0); //Pin that reads out the values of the distence sensor
int sensorValue; //Raw sensor values
bool sensorState = false; //Used to know if sensor is low, high or recently low, prevents multiple data additions to value. Range is 0, 1, 2.
uint16_t value = 0; //Amount of people that walked by.
int statePin = D5; //The pin which turns the sensor on or off (connected to low-end transistor)
int nulValue = 4098; //nulValue is the lowest value of the distance sensor (in rest), all calculations are done based on this lowest value. This value starts high, to ensure the first readings are lower.
const int oST = 200; //Original sensorThreshhold
int sensorThreshold = oST; //Threshold of the sensor (above this value and somebody walked by).
const int sensorOffset = 100; //When threshold is met, apply filter to ensure no flickering readings.
int sensorMedian; //The actual average that's getting output from the value sensorValues RunningMedian.
int sampleSize = 100; //The size of the samples to be used in the RunningMedian
RunningMedian sensorValues = RunningMedian(sampleSize); //Creates an array to average the data which then gets stored in value.
unsigned long sensorTimer; //Used to make sure there are not constant readings or value additions from the sensor.
int sensorTimeOffset = 500; //The amount of time that has to pass between high and low reading of the sensor.
unsigned long correctionTimer; //Timer used to correct the nulValue if the sensor is moved or doesn't detect values within a certain range anymore.
int correctionOffset = 10000; //If the sensor isn't in the range of the nulValue for this amount of time, then the nulValue is increased.
int nulValueRange = 10; //Sensor value has to be in this range the majority of time.
bool timerSet = false; //Used to know if a timer has been set for correction
//FIRST RUN & EEPROM DATA
bool firstRun = true; //Used to activate several parts of the Photon before continuing.
int eepromAddress[] = {0, 2}; //Address of the stored values in EEPROM (uint16_t = 2 bytes)
int eepromUnwritten = 65535; //Value of eeprom for uint16_t when unwritten.
uint16_t eepromValue1; //Value 1, compared to value 2.
uint16_t eepromValue2; //Value 2, compared to value 1.
//Two values are used to ensure that there's *always* a backup value.
//If one fails, the other one can still be used (due to i.e. a power cut)
//ACTIVITY LED
int ledVal = 255; //Strength of the LED.
unsigned long bleepTimer; //Timer for when to bleep.
int bleepInterval = 1000; //At what time to bleep the LED.
int bleepDuration = 100; //How long the LED will bleep.
//MISCELLANEOUS
SYSTEM_MODE(SEMI_AUTOMATIC);
// ##### ####### ####### # # ######
// # # # # # # # #
// # # # # # # #
// ##### ##### # # # ######
// # # # # # #
// # # # # # # #
// ##### ####### # ##### #
void setup(){
//Setting up the credentials for WiFi
//Multiple credentials for the deploy location and development location
WiFi.setCredentials(SSID, PASS);
WiFi.setCredentials(SSID_Location, PASS_Location);
//Initiates the statePin as an output pin, to turn the sensor on or off.
pinMode(statePin, OUTPUT);
//Experimentation for deep sleep.
WiFi.on();
delay(cooldown);
Particle.connect();
//Initiates a serial communication.
Serial.begin(9600);
}
// # ####### ####### ######
// # # # # # # #
// # # # # # # #
// # # # # # ######
// # # # # # #
// # # # # # #
// ####### ####### ####### #
void loop(){
//This is executed when Photon turns on (this includes after a deep sleep)
while(firstRun == true){
photonWakeUp();
}
//Functions used to gather data.
detectTime(); //Detects time
readSensor(); //Reads the distance sensor to detect passersby.
//Functions that are based on time (hours/minutes, etc).
timedActivities(); //Ensures time related activities are being executed, including dataPublish.
//Based on the interal timer (millis), make a led blink to show activity.
//Green is normal activity, blue is with WiFi on.
ledBleep();
//Function to debug based on the gathered data.
serialPrint(); //Used to communicate data to a serial monitor, i.e. the computer.
}
// ####### ### # # ####### #####_
// # # ## ## # # #
// # # # # # # # # #
// # # # # # ##### # #
// # # # # # # #
// # # # # # # #
// # ### # # ####### #####`
// # ##### ####### ### # # ### ####### ### ####### #####
// # # # # # # # # # # # # # #
// # # # # # # # # # # # #
// # # # # # # # # # # ##### #####
// ####### # # # # # # # # # #
// # # # # # # # # # # # # # #
// # # ##### # ### # ### # ### ####### #####
//All activities related to timing and specific moments of when they need to be executed.
//Including the intervalled dataPublish.
void timedActivities(){
//=========================================================================//
//DEBUG WINDOW
//The Photon is turned on to make it possible to debug, upload new code and so on.
if(tHour >= debugStartHour && tHour <= debugEndHour && tMinute >= debugStartMinute && tMinute < debugEndMinute){
if(WiFi.ready()){
if(Particle.connected()){
debugState = true;
}
}
else{
WiFi.on();
WiFi.connect();
}
}
else{
debugState = false;
}
//=========================================================================//
//=========================================================================//
//SLEEPMODE
//Checks if the device needs to go in sleep mode.
//Before going to sleep, it first publishes the final data.
if(tHour == sleepHour && tMinute >= sleepMinute){
sleepState = true;
}
//After the hour of sleep.
if(tHour > sleepHour){
sleepState = true;
}
//Before the hour of wakeup.
if(tHour < wakeupHour){
sleepState = true;
}
//In the hour of wakeup, at the minute or before the minute - offset of wakeup.
if(tHour == wakeupHour && tMinute < (wakeupMinute)){
sleepState = true;
}
//If the day is Saturday (7) or Sunday (1).
if(tDay == 1 || tDay == 7){
sleepState = true;
}
//When in sleepState, publish the data, then go into sleepmode.
//Makes sure the device isn't in debugState.
if(sleepState == true && debugState == false){
if(value != 0){
tMinute = mMinute;
}
else{
photonSleep();
}
}
//=========================================================================//
//=========================================================================//
//DATAPUBLISH
//At a specified interval a data transfer will be initiated.
if(tMinute % mMinute == 0){
switch (wifiState){
case 0:
if(debugState == false){
WiFi.on();
WiFi.connect();
}
wifiState++;
break;
case 1:
if(WiFi.ready()){
wifiState++;
stateTimer = millis();
}
break;
case 2:
if(millis() > stateTimer + cooldown){
Particle.process();
wifiState++;
stateTimer = millis();
}
break;
case 3:
if(millis() > stateTimer + cooldown){
if(Particle.connected()){
wifiState++;
stateTimer = millis();
}
}
break;
case 4:
if(millis() > stateTimer + cooldown){
dataPublish();
wifiState++;
}
break;
case 5:
if(debugState == false){
// WiFi.off();
}
break;
}
}
else {
wifiState = 0;
if(debugState == false){
// WiFi.off();
}
}
//=========================================================================//
}
// ##### ####### # # ####### ######
// # # # # # # # #
// # # # # # # # #
// # # # ####### ##### ######
// # # # # # # # #
// # # # # # # # #
// ##### # # # ####### # #
// ####### # # # # ##### ####### ### ####### # # #####
// # # # ## # # # # # # # ## # # #
// # # # # # # # # # # # # # # #
// ##### # # # # # # # # # # # # # #####
// # # # # # # # # # # # # # # #
// # # # # ## # # # # # # # ## # #
// # ##### # # ##### # ### ####### # # #####
//Publishes the location and the value to the Particle Cloud.
//The Particle Cloud uses a webhook to store this information in the database.
void dataPublish(){
//Converts the location and value into a string with identifiers (var1, var2)
sprintf(publishString, "{\"var1\": %u, \"var2\": %u}", location, value);
//The string is published under the identifier "trap"
//The Trapteller webhook is triggered by this identifier.
Particle.publish("trap", publishString);
//To make sure the publish is done properly, a cooldown is added.
//When not using if/for/switch functions, make sure this cooldown is 1 second.
delay(cooldown);
value = 0; //Resets the value after it has been sent.
eepromPut(); //Resets the values of the eeprom.
}
//=========================================================================//
void debug(){
sprintf(publishString, "{Dag: %u / Tijd: %u:%u:%u / Sleeptime: %u / Value: %u}", tDay, tHour, tMinute, tSecond, sleepTime, value);
Particle.publish("debug", publishString);
delay(cooldown);
}
//=========================================================================//
//Time is synced with the Particle cloud and the values are stored
void detectTime(){
//Time correction because Particle Cloud is in another timezone than the Photon.
tHour = Time.hour() + hourOffset;
if(tHour > 24){
tHour - 24;
}
tMinute = Time.minute();
tSecond = Time.second();
tDay = Time.weekday();
}
//=========================================================================//
//Getting previously stored values from the EEPROM.
//Used to check if there's still data from previous session.
//If this is the case, this data is placed in value so it gets published.
void eepromGet(){
//Reads EEPROM from specific address.
EEPROM.get(eepromAddress[0], eepromValue1);
EEPROM.get(eepromAddress[1], eepromValue2);
//Checks if the data received is from an empty EEPROM.
//If this is the case, changes these values to 0.
if(eepromValue1 == eepromUnwritten){
eepromValue1 = 0;
}
if(eepromValue2 == eepromUnwritten){
eepromValue2 = 0;
}
//Checks if both values are the same. If this is not the case, takes the highest value.
//This is done because the device could have crashed while writing was still going on.
if(eepromValue1 != eepromValue2){
if(eepromValue1 > eepromValue2){
eepromValue2 = eepromValue1;
}
if(eepromValue2 > eepromValue1){
eepromValue1 = eepromValue2;
}
}
value = eepromValue1;
}
//=========================================================================//
//Puts the current data of value into the correct EEPROM address.
void eepromPut(){
for(int i = 0; i < 2; i++){
EEPROM.put(eepromAddress[i], value);
}
}
//=========================================================================//
//ledBleep is used to indicate some activity on the device.
//Photon's original LED breathing/signals are turned off to save power.
//When deployed, this code CAN be disabled.
//LED color explanation: RED = debug / BLUE = WiFi ready (outside debug) / GREEN = active, no wifi or debug
void ledBleep(){
if(millis() >= bleepTimer + bleepDuration && millis() < bleepTimer + bleepInterval){
RGB.color(0, 0, 0);
}
if(millis() >= bleepTimer + bleepInterval){
if(debugState == true){
RGB.color(ledVal, 0, 0);
}
else if(WiFi.ready()){
RGB.color(0, 0, ledVal);
}
else{
RGB.color(0, ledVal, 0);
}
bleepTimer = millis();
}
}
//=========================================================================//
//Used during the night, ensures the Photon goes to sleep to preserve power.
//Automatically detects if its weekend or not.
void photonSleep(){
digitalWrite(statePin, LOW); //Turn off the distance sensor.
sleepCalculator(); //Calculate the total sleep time.
if(WiFi.ready()){
if(Particle.connected()){
debug();
}
}
else{
WiFi.on();
WiFi.connect();
photonSleep();
}
firstRun == true; //Resetting firstRun for the wakeup, not sure if this gets reset on its own.
System.sleep(SLEEP_MODE_DEEP, sleepTime); //Going in to deep sleep.
System.reset(); //Not sure if this is needed, if this function continues after the deep sleep I might as well call it to be sure.
}
//=========================================================================//
//Initiated after the Photon wakes up.
//Initializes all the functions of the Photon for it to work.
void photonWakeUp(){
printTimer = millis(); //giving a value to the printTimer
digitalWrite(statePin, HIGH); //Turning the distance sensor on.
WiFi.on(); //Turning WiFi on...
WiFi.connect(); //Making it connect...
delay(cooldown);
//Checking if it's connected
if(Particle.connected()){
delay(cooldown);
detectTime(); //detect time
delay(cooldown);
eepromGet(); //get data from the EEPROM
delay(cooldown);
// RGBcontrol(); //get control of the RGB, as it uses quite some power.
debug();
bleepTimer = millis(); //starting the timer for the LED bleeps.
firstRun = false; //and to exit the firstRun.
}
else{
photonWakeUp();
}
}
//=========================================================================//
//Reads the distance sensor to detect someone passing by.
//Applying a filter to ensure that jittering values don't go above or under the threshold rapidly.
void readSensor(){
//Reading the sensor and adding it to the sensorMedian.
sensorValue = analogRead(sensorPin);
sensorValues.add(sensorValue);
sensorMedian = int(sensorValues.getMedian());
//Detects if the current sensorMedian is lower than the lowest value (this is the point where offSet should start)
//Removes the nulValue from the sensorMedian, ensuring all the readings have a baseline of nulValue.
if(sensorMedian < nulValue){
nulValue = sensorMedian;
}
sensorMedian -= nulValue;
if(sensorMedian > nulValueRange){
if(timerSet == false){
correctionTimer = millis();
timerSet = true;
}
if(timerSet == true){
if(millis() > correctionTimer + correctionOffset){
nulValue++;
}
}
}
else{
timerSet = false;
}
//Detects if the average of the sensorValue is over the threshold while the sensorState was false.
//If this is the case, the threshold is lowered (to ensure a better reading and no flickering)
//And value is increased by one (since one person passed by), at this point the sensorState is true, because someone is passing by.
if(sensorMedian > sensorThreshold && sensorState == false){
sensorThreshold -= sensorOffset;
value++;
eepromPut();
sensorState = true;
sensorTimer = millis();
}
//Prevents too many data inputs (i.e. by swaying someone's hands in front of it)
if(sensorMedian > sensorThreshold && sensorState == true && (millis() < (sensorTimer + sensorTimeOffset))){
sensorTimer = millis();
}
//Since the threshold has been lowered, the sensorValue first has to drop
//Then, if this happens and a short timer has passed, the sensorState is put to false indicating someone has passed.
if(sensorMedian < sensorThreshold && sensorState == true && (millis() > (sensorTimer + sensorTimeOffset))){
sensorThreshold = oST;
sensorState = false;
}
}
//=========================================================================//
//Get control of the Photon RGB LED, turning it off to preserve power.
void RGBcontrol(){
RGB.control(true);
RGB.color(0,0,0);
}
//=========================================================================//
//Prints desired data to the console for debugging.
void serialPrint(){
//Wait for interval to pass.
if(millis() > printTimer + prInterval){
//Print time
if(tHour < 10){
Serial.print("0");
}
Serial.print(tHour);
Serial.print(":");
if(tMinute < 10){
Serial.print("0");
}
Serial.print(tMinute);
Serial.print(":");
if(tSecond < 10){
Serial.print("0");
}
Serial.print(tSecond);
//Print other desired values
Serial.print("value");
Serial.print("\t");
Serial.print(value);
//Line Break
Serial.println();
//Resetting the printTimer.
printTimer = millis();
}
}
//=========================================================================//
//Calculates the total time to sleep until the next wakeup.
void sleepCalculator(){
unsigned int calcEvening;
unsigned int calcNight;
unsigned int calcWeekend;
//Detect if its currently after sleepHour/Minute but before midnight or before wakeupHour/Minute and after midnight.
//Based on that, calculate the calcEvening and calcNight values.
if(tHour == sleepHour && tMinute >= sleepMinute){
calcEvening = (((24 - tHour) * 60) - tMinute) * 60;
calcNight = (wakeupHour * 60 + wakeupMinute) * 60;
}
else if(tHour > sleepHour){
calcEvening = (((24 - tHour) * 60) - tMinute) * 60;
calcNight = (wakeupHour * 60 + wakeupMinute) * 60;
}
else if(tHour < wakeupHour){
calcEvening = 0;
calcNight = ((wakeupHour - tHour) * 60 - tMinute + wakeupMinute) * 60;
}
else if(tHour == wakeupHour && tMinute < wakeupMinute){
calcEvening = 0;
calcNight = (wakeupMinute - tMinute) * 60;
}
//Based on the day, add extra time for weekend sleep.
if(tDay == 6){
if(tHour == sleepHour && tMinute >= sleepMinute){
calcWeekend = 172800;
}
if(tHour > sleepHour){
calcWeekend = 172800;
}
if(tHour < wakeupHour){
calcWeekend = 0;
}
if(tHour == wakeupHour && tMinute >= sleepMinute){
calcWeekend = 0;
}
}
else if(tDay == 7){
calcWeekend = 86400;
}
else{
calcWeekend = 0;
}
sleepTime = (long) (calcEvening + calcNight + calcWeekend);
if(sleepTime > 259200){
sleepCalculator();
}
}