NOTE: I currently have this posted on StackOverflow as well.
I have a Particle.IO application I wrote (that worked), and I am currently refactoring it to use C++ classes and objects.
The target device is a Particle Boron BRN404X with DeviceOS and firmware version 6.2.1.
The application has four software timers that in the original version worked (note that one class has two timers in it that's not obvious from the examples below), but now that I am using class member callbacks, the timers do not seem to be running after the respective timer start() functions are called.
I have searched and have tried a couple of ways to initialize the timers. Currently each timer is being initialized in the constructor of each object, and a public function is called from setup() to start them. Callback functions are currently marked as private in the class header files.
The following is the main file and where my objects are instantiated and the timers are started.
#include "Particle.h"
#include "spark_wiring.h"
#include "../lib/local_includes/digitalIO.h"
#include "../lib/local_includes/DataTransmit.h"
#include "../lib/local_includes/CellularCheck.h"
#include "../lib/local_includes/Power.h"
#include "../lib/local_includes/Battery.h"
#include "../lib/local_includes/Shutdown.h"
#include "../lib/local_includes/Weekly.h"
// Let Device OS manage the connection to the Particle Cloud
SYSTEM_MODE(AUTOMATIC);
// Run the application and system concurrently in separate threads
SYSTEM_THREAD(ENABLED);
// Objects
PMIC pmic;
BoronDigitalOutputs digitalOutputs;
DataTransmit dataTransmission;
CellularCheck cellularCheck(digitalOutputs);
Power powerMonitor(digitalOutputs, &cellularCheck, &dataTransmission);
Battery batteryMonitor(digitalOutputs, &powerMonitor, &dataTransmission);
Shutdown shutdown(&dataTransmission, &pmic);
Weekly weeklyAction(&dataTransmission);
/*
* @brief Initialization routine called automatically by Particle Boron SoM when system is booted up.
* @param void
* @return void
*/
void setup()
{
pmic.enableBATFET();
digitalOutputs = initDigitalOutputs();
if (!Cellular.ready() && !Particle.connected())
{
cellularCheck.setSystemConnected(false);
digitalOutputWrite(digitalOutputs, CELLSEARCH, HIGH);
digitalOutputWrite(digitalOutputs, ONCELL, LOW);
}
waitUntil(Cellular.ready);
waitUntil(Particle.connected);
if (Cellular.ready() && Particle.connected())
{
cellularCheck.setSystemConnected(true);
digitalOutputWrite(digitalOutputs, CELLSEARCH, LOW);
digitalOutputWrite(digitalOutputs, ONCELL, HIGH);
}
//EST
Time.zone(-5);
Particle.syncTime();
int powerSource = System.powerSource();
if (powerSource == POWER_SOURCE_BATTERY)
{
powerMonitor.setIsOnBattery(true);
powerMonitor.setOnAc(false);
digitalOutputWrite(digitalOutputs, ONBATPWR, HIGH);
digitalOutputWrite(digitalOutputs, ONACPWR, LOW);
}
else
{
powerMonitor.setIsOnBattery(false);
powerMonitor.setOnAc(true);
digitalOutputWrite(digitalOutputs, ONBATPWR, LOW);
digitalOutputWrite(digitalOutputs, ONACPWR, HIGH);
}
if(powerMonitor.getIsOnBattery())
{
dataTransmission.setStringOne("Application booting...");
dataTransmission.setStringTwo("NO AC POWER");
dataTransmission.sendData();
}
else
{
dataTransmission.setStringOne("Application booting...");
dataTransmission.setStringTwo("AC POWER GOOD");
dataTransmission.sendData();
}
//start timers
cellularCheck.startTimer();
powerMonitor.startTimer();
batteryMonitor.startTimer();
}
/*
* @brief Main routine continuous loop.
* @param void
* @return void
*/
void loop()
{
//service data transmission for timer interrupts.
if (dataTransmission.getPublishFlag())
{
dataTransmission.sendData();
dataTransmission.setPublishFlag(false);
}
//send weekly test messages.
if (weeklyAction.checkTime())
{
weeklyAction.performWeeklyAction();
}
shutdown.lowBatteryLife();
}
Here is one class header file and matching cpp file to show the timer initialization and callback functions. All other classes with timers and callback functions follow the same pattern.
Power.h
#ifndef POWER_H
#define POWER_H
#include "Particle.h"
#include "../lib/local_includes/digitalIO.h"
#include "../lib/local_includes/DataTransmit.h"
#include "../lib/local_includes/CellularCheck.h"
using namespace std;
class Power
{
public:
//constructor
Power(BoronDigitalIOStruct * BoronDigitalOutputs, CellularCheck * c, DataTransmit * d);
//default constructor
Power();
//destructor
~Power();
//public member functions
void startTimer(void);
//accessors
bool getIsOnAc() const;
bool getIsOnBattery() const;
//mutators
void setOnAc(bool isOnAC);
void setIsOnBattery(bool isOnBattery);
private:
BoronDigitalOutputs digitalOutputs;
CellularCheck cellularCheck;
DataTransmit transmitData;
Timer *powerCheckTimer;
//timer callback
void checkACLineVoltage(void);
bool onAC;
bool onBattery;
};
#endif
Power.cpp
#include "Particle.h"
#include "../lib/local_includes/Power.h"
#include "../lib/local_includes/digitalIO.h"
#include "../lib/local_includes/CellularCheck.h"
#include "../lib/local_includes/DataTransmit.h"
using namespace std;
//constructors & destructor
Power::Power(BoronDigitalIOStruct * BoronDigitalOutputs, CellularCheck * c, DataTransmit * d)
{
digitalOutputs = BoronDigitalOutputs;
cellularCheck = *c;
transmitData = *d;
onAC = false;
onBattery = false;
powerCheckTimer = new Timer(1000, &Power::checkACLineVoltage, *this);
}
Power::Power()
{
//intentionally left empty.
}
Power::~Power()
{
//intentionally left empty.
}
void Power::startTimer(void)
{
powerCheckTimer->start();
}
/*
* @brief Monitor AC line voltage if connected to cellular and Particle Cloud.
* @param void
* @return void
*/
void Power::checkACLineVoltage(void)
{
if(cellularCheck.getIsSystemConnected())
{
int powerSource = System.powerSource();
if (powerSource == POWER_SOURCE_BATTERY)
{
if(!onBattery && onAC)
{
onBattery = true;
onAC = false;
digitalOutputWrite(digitalOutputs, ONBATPWR, HIGH);
digitalOutputWrite(digitalOutputs, ONACPWR, LOW);
transmitData.setStringOne("Power Monitor");
transmitData.setStringTwo("AC POWER LOST");
transmitData.setPublishFlag(true);
}
}
else if (onBattery && !onAC)
{
onBattery = false;
onAC = true;
digitalOutputWrite(digitalOutputs, ONBATPWR, LOW);
digitalOutputWrite(digitalOutputs, ONACPWR, HIGH);
transmitData.setStringOne("Power Monitor");
transmitData.setStringTwo("AC POWER IS ON");
transmitData.setPublishFlag(true);
}
}
}
bool Power::getIsOnAc() const
{
return onAC;
}
bool Power::getIsOnBattery() const
{
return onBattery;
}
void Power::setOnAc(bool isOnAC)
{
onAC = isOnAC;
}
void Power::setIsOnBattery(bool isOnBattery)
{
onBattery = isOnBattery;
}
My setup() function appears to be working as the device LED’s light up based on the logic in this function. I also receive my push notifications based on this logic. The only logic that seems to work in the loop() is the Weekly functions. Everything else is based on timer callbacks that do not work.
What am I missing or doing wrong?