@ScruffR Thank you for that. I reworked the code quite a bit and did a lot of testing, but there seem to be some glitches with
Particle.disconnect(); delay(3000); Particle.connect();
What I saw was that it didn’t handshake upon reconnection, but when it was reset again (in the form of System.reset();
or System.sleep();
, it would usually handshake on the next reconnecting. However, this was very glitchy because putting this code before or after
Particle.process(); delay(15000); Particle.process();
would change if it would handshake or not.
Here is my code I used for testing:
/*----------------------------------------------------------------------------------------------------------------------
* INCLUDES
*/
#include "Particle.h"
#include "cellular_hal.h"
#include <math.h>
#include <HC_SR04.h>
/*----------------------------------------------------------------------------------------------------------------------
* CONSTANTS
*/
#define EVENT_NAME "M"
#define MEASUREMENT_COUNT 21
#define BUFFER_SIZE 256
#define CELL_APN "hologram"
#define CELL_USERNAME ""
#define CELL_PASSWORD ""
#define EEPROM_TIME_ADDRESS 0
#define ELECTRON_PRODUCT_ID ----
#define ELECTRON_PRODUCT_VERSION 1
#define DELAY_CELL 180000 // Milliseconds
#define DELAY_CLOUD 180000 // Milliseconds
#define DELAY_SETTLE 10000 // Milliseconds
#define DELAY_24_HOUR 86400 // Seconds
#define DELAY_CONNECT_TIME 500 // Milliseconds
#define DELAY_PIN 100 // Milliseconds
#define DELAY_MEASUREMENT 75 // Milliseconds
#define FOUR_HOUR_WAIT 14400 // Seconds
#define SEC_IN_HOUR 3600 // Seconds
#define DELAY_UPDATE_TIME 15000 // Milliseconds
#define PIN_ECHO D5 // Connect HC-SR04 Range finder as follows:
#define PIN_RELAY D3 // GND - GND, 5V - VCC, D4 - Trig, D5 - VoltageDivider.
#define PIN_TRIG D4 // VoltageDivider (470 OHM RESISTORS FROM D5 TO GND AND TO ECHO)
#define RANGE_MAX 400
#define RANGE_MIN 0.5
#define DEBUG 1
#define BAUD_RATE 9600
/*----------------------------------------------------------------------------------------------------------------------
* MACROS
*/
#if DEBUG
# define SERIAL_DEBUG_BEGIN(x) Serial.begin(x)
# define DPRINTF(...) Serial.printf(__VA_ARGS__)
# define DPRINTLN(x) Serial.println(x)
#else // do nothing
# define SERIAL_DEBUG_BEGIN(x)
# define DPRINTF(...)
# define DPRINTLN(x)
#endif
/*----------------------------------------------------------------------------------------------------------------------
* CONFIGURATION
*/
PRODUCT_ID(ELECTRON_PRODUCT_ID); // Product ID
PRODUCT_VERSION(ELECTRON_PRODUCT_VERSION);
STARTUP(cellular_credentials_set(CELL_APN, CELL_USERNAME, CELL_PASSWORD, NULL));
STARTUP(System.enableFeature(FEATURE_RESET_INFO)); // To enable reading why the system was reset.
SYSTEM_MODE(SEMI_AUTOMATIC); // Cloud connecting at SEMI_AUTOMATIC can provide power savings.
HC_SR04 rangefinder(PIN_TRIG, PIN_ECHO, RANGE_MIN, RANGE_MAX); // initializes HC_SR04 object
/*----------------------------------------------------------------------------------------------------------------------
* GLOBALS (fix to local variables)
*/
int m_min = -1;
int m_q1 = -1;
int m_med = -1;
int m_q3 = -1;
int m_max = -1;
float m_soc = 0.0;
long hours; // holds hours until wake
long minutes; // holds minutes until wake
long seconds; // hold seconds until wake
long wakeTimer; // combination of hours, minutes, seconds
int WeeklyUpdateAddr = 10; // Holds the address of the Weekly Update bool in EEProm
bool WeeklyUpdate; // check if should update firmware or not
bool publish;
char publishString[BUFFER_SIZE];
/*======================================================================================================================
* PARTICLE MAIN CYCLE ENTRY POINTS
*/
void setup()
{
DPRINTLN("+++ setup ====================================");
initAll();
takeMeasurements(); // take measurements before stringing together data to publish
slowClock();
snprintf(publishString, sizeof(publishString), "{\"m_min\":%d,\"m_q1\":%d,\"m_med\":%d,\"m_q3\":%d,\"m_max\":%d,\"m_soc\":%f}", m_min, m_q1, m_med, m_q3, m_max, m_soc);
Cellular.on(); // explicitly trigger turning cell on - precautionary step
Cellular.connect(); // explicitly trigger cell connect - precautionary step
DPRINTLN("Cell is on and connecting");
delay(DELAY_CONNECT_TIME); // wait half a second
Particle.connect(); // needs to be called for semi-automatic mode to work
delay(DELAY_CONNECT_TIME);
Particle.process(); // explicitly trigger the background task
DPRINTLN("Particle is connecting");
if(waitFor(Cellular.ready, DELAY_CELL)) // wait up to 180 seconds for cell to connect
{
DPRINTLN("Cell has connected successfully");
if(waitFor(Particle.connected, DELAY_CLOUD)) // wait up to 180 seconds for particle to connect
{
DPRINTLN("Particle has connected successfully");
//setWakeTimer(20); // call wake time setter
wakeTimer = 10; //sleep for two minutes
//delay(5); // this allows handshake, anything more will not work - reasons unknown
for(int i = 0; i < 5; i++) // Try to connect up to 5 times
{
publish = Particle.publish(EVENT_NAME, publishString, PRIVATE);
delay(DELAY_CONNECT_TIME);
Particle.process();
if(publish)
{
DPRINTLN("Data has been published to cloud");
i = 5;
}
}
if(!publish)
{
Particle.process();
delay(DELAY_UPDATE_TIME); // 15 second wait - give time for device to identify new firmware
Particle.process();
DPRINTLN("Data has not been published - system reset");
smartReboot();
}
smartReboot(); // Check to see if firmware should be updates
Particle.process();
delay(DELAY_UPDATE_TIME); // 15 second wait - give time for device to identify new firmware
Particle.process();
DPRINTLN("Sleep set to wakeup at 8PM, Going to sleep");
System.sleep(SLEEP_MODE_SOFTPOWEROFF, wakeTimer);
}
else // cloud did not connect, sleep for four hours and try again
{
DPRINTLN("Cell did not connect within 180 seconds - Going to sleep for 4 hours");
System.sleep(SLEEP_MODE_SOFTPOWEROFF, FOUR_HOUR_WAIT);
}
}
else // cell did not connect, sleep for four hours and try again
{
DPRINTLN("Cell did not connect within 180 seconds - Going to sleep for 4 hours");
System.sleep(SLEEP_MODE_SOFTPOWEROFF, FOUR_HOUR_WAIT);
}
DPRINTLN("--- setup ====================================");
}
void loop()
{
// do nothing
}
/*======================================================================================================================
* HELPER FUNCTIONS
*/ ///=====================================================================================================================
/// <summary>This function called to initialize everything that is needed</summary> ///=====================================================================================================================
inline void initAll()
{
//delay(DELAY_SETTLE); // wait 10 seconds for initialization
DPRINTLN("+++ Initialize +++");
SERIAL_DEBUG_BEGIN(BAUD_RATE);
System.enableUpdates(); // enable system updates (might not work)
pinMode(PIN_RELAY, OUTPUT); // sets relay pin
FuelGauge fuel; // fuel gauge class
m_soc = fuel.getVCell(); // battery voltage
DPRINTLN("--- Initialize ---");
}
///=====================================================================================================================
/// <summary>
// Helper function used to calculate time (in seconds) it takes to wake the device given param targetHour. ///=====================================================================================================================
void setWakeTimer(int targetHour)
{
DPRINTLN("+++ Setting Wake Timer +++");
Particle.syncTime(); // Synchronize the time with the Particle Cloud.
waitUntil(Particle.syncTimeDone); // wait until Particle sync is complete
Time.zone(-3.5); // sets time zone to MST (end of daylight savings)
long localnow = Time.local();
long hournow = Time.hour(Time.local());
long minutenow = Time.minute(Time.local());
long secondnow = Time.second(Time.local());
// Wake Time Algorithm
hours = (targetHour + 24 - (Time.hour(Time.local()))) % 24;
minutes = (59 - Time.minute(Time.local()));
seconds = (59 - Time.second(Time.local()));
wakeTimer = ((hours-1) * 3600) + (minutes * 60) + seconds;
DPRINTLN(" \n--- Wake Timer set ---");
} ///========================================================================================= ============================
/// <summary>Helper function used in takeMeasurements to turn on the solid state relay</summary>
///=====================================================================================================================
void turnOnRelay()
{
digitalWrite(PIN_RELAY,HIGH);
delay(DELAY_PIN);
DPRINTLN("The Relay turned on");
} ///=====================================================================================================================
/// <summary>Helper function used in takeMeasurements to turn off the solid state relay</summary>
///=====================================================================================================================
void turnOffRelay()
{
digitalWrite(PIN_RELAY,LOW);
delay(DELAY_PIN);
DPRINTLN("The Relay turned off");
}
///=====================================================================================================================
/// <summary>
/// This function slows down the clock from 120MHz to 30MHz in order to be more power efficent. This function was ///=====================================================================================================================
void slowClock()
{
RCC->CFGR &= ~0xfcf0;
RCC->CFGR |= 0x0090;
SystemCoreClockUpdate();
SysTick_Configuration();
FLASH->ACR &= ~FLASH_ACR_PRFTEN;
DPRINTLN("Clock was slowed down to 30 MHz");
}
///=====================================================================================================================
/// <summary>
/// Helper function for takeMeasurements that sorts an array of length MEASUREMENT_COUNT using the insertion sort
///=====================================================================================================================
void sortArray(int myReadings[])
{
int i, j, x;
for(uint16_t i = 1; i < MEASUREMENT_COUNT; i++)
{
x = myReadings[i];
j = i - 1;
while(j >= 0 && (myReadings[j] > x))
{
myReadings[j + 1] = myReadings[j];
j = j - 1;
}
myReadings[j + 1] = x;
}
}
///=====================================================================================================================
/// <summary>
/// This function takes measurements from an ultrasonic sensor and stores the results in 5
/// global variables so that the results are ready to publish
/// </summary> ///=====================================================================================================================
void takeMeasurements()
{
DPRINTLN("+++ takeMeasurements +++");
turnOnRelay(); // Turns on relay that allows power to flow to sensor
int measurements[MEASUREMENT_COUNT];
for(int i = 0; i < MEASUREMENT_COUNT; i++) // takes MEASUREMENT_COUNT measurements
{
measurements[i] = rangefinder.getDistanceCM();
delay(DELAY_MEASUREMENT);
}
DPRINTLN("Finished Taking Measurements.");
turnOffRelay(); // Stops power from leaking to sensor
sortArray(measurements); // Sorts the measurements in ascending order
// Populates global variables
m_min = measurements[0];
m_q1 = measurements[MEASUREMENT_COUNT / 4 - 1];
m_med = measurements[MEASUREMENT_COUNT / 2 - 1];
m_q3 = measurements[MEASUREMENT_COUNT * 3 / 4 - 1];
m_max = measurements[MEASUREMENT_COUNT - 1];
DPRINTLN("--- takeMeasurements ---");
}
///=====================================================================================================================
/// <summary>
/// This function is used to reset the device. We use this because a system reset does not reset the cellular modem.
/// </summary>
///=====================================================================================================================
void smartReboot()
{
DPRINTLN("+++ Resetting Modem and Sim Card +++");
Particle.disconnect();
Cellular.command(30000, "AT+CFUN=16\r\n");
Cellular.off();
delay(1000);
Cellular.on();
Cellular.connect();
delay(DELAY_CONNECT_TIME); // wait half a second
Particle.connect();
delay(DELAY_CONNECT_TIME);
Particle.process(); // explicitly trigger the background task
//System.reset();
}
Basically, what I was seeing in testing, is that in the setup function it only forces the handshake if I call the function smartReboot()
before that Particle.process(); delay(15000); Particle.process();
The problem with that however, is that it will never do an OTA update because it doesn’t handshake at that point and check for an update and try to update (that is my suspicion at least).