Hi All,
Finally getting some time at this today, and just want to do a quick sanity check that I am approaching this appropriately. So I have just created a blank project.

- config - will setup and control eeprom variables.
- particle_fn - will setup, define and action any particle functions. There is a function in here that calls and uses methods from the config singleton. Not sure if this is standard/recommended.
config.h
#ifndef __CONFIG_H
#define __CONFIG_H
#include "Particle.h"
/**
* This class is a singleton; you do not create one as a global, on the stack, or with new.
*
* From global application setup you must call:
* config::instance().setup();
*
* From global application loop you must call:
* config::instance().loop();
*/
class config {
public: // Public just for added variables & methods
// EEPROM VARIABLES
typedef struct{
uint32_t magic;
char email[40];
} eepromConfig;
eepromConfig config_data;
void printConfigVariables(); // Log info config varibles to console
void storeConfigVariables(); // Store variables to config struct
protected: // protected just for added variables & methods
int m_addr = 100;
const uint32_t DATA_MAGIC = 0xa2c7206a;
char init_email[40] = "";
void loadConfigVariables();
public: // Singleton Boilerplate
/**
* @brief Gets the singleton instance of this class, allocating it if necessary
*
* Use config::instance() to instantiate the singleton.
*/
static config &instance();
/**
* @brief Perform setup operations; call this from global application setup()
*
* You typically use config::instance().setup();
*/
void setup();
/**
* @brief Perform application loop operations; call this from global application loop()
*
* You typically use config::instance().loop();
*/
void loop();
protected: // Singleton Boilerplate
/**
* @brief The constructor is protected because the class is a singleton
*
* Use config::instance() to instantiate the singleton.
*/
config();
/**
* @brief The destructor is protected because the class is a singleton and cannot be deleted
*/
virtual ~config();
/**
* This class is a singleton and cannot be copied
*/
config(const config&) = delete;
/**
* This class is a singleton and cannot be copied
*/
config& operator=(const config&) = delete;
/**
* @brief Singleton instance of this class
*
* The object pointer to this class is stored here. It's NULL at system boot.
*/
static config *_instance;
};
#endif /* __CONFIG_H */
config.cpp
#include "config.h"
config *config::_instance;
// [static]
config &config::instance() {
if (!_instance) {
_instance = new config();
}
return *_instance;
}
config::config() {
}
config::~config() {
}
void config::setup() {
delay(3000);
loadConfigVariables();
Serial.println("Config Setup COMPLETE");
}
void config::loop() {
// Put your code to run during the application thread loop here
}
void config::printConfigVariables(){
Log.info("addr=%d, email=%s", m_addr, config_data.email);
Log.info("sizeof(data)=%d", sizeof(config_data));
}
void config::storeConfigVariables(){
EEPROM.put(m_addr, config_data);
}
void config::loadConfigVariables(){
Log.info("CHECKING EEPROM");
printConfigVariables();
EEPROM.get(m_addr, config_data);
if (config_data.magic != DATA_MAGIC){
Log.info("MAGIC BYTES DIFFERENT - LOADING NEW DEFAULTS");
config_data.magic = DATA_MAGIC;
strcpy(config_data.email, init_email);
printConfigVariables();
storeConfigVariables();
}
else{
Log.info("MAGIC BYTES SAME - LOADED EEPROM VALUES");
Log.info("AFTER EEPROM READ");
printConfigVariables();
}
}
particle_fn.h
#ifndef __PARTICLE_FN_H
#define __PARTICLE_FN_H
#include "Particle.h"
#include "config.h"
/**
* This class is a singleton; you do not create one as a global, on the stack, or with new.
*
* From global application setup you must call:
* particle_fn::instance().setup();
*
* From global application loop you must call:
* particle_fn::instance().loop();
*/
class particle_fn {
protected: // For code added thats not signleton boilerplate
void particleFunctionSetups();
int setEmail(String command);
public:
/**
* @brief Gets the singleton instance of this class, allocating it if necessary
*
* Use particle_fn::instance() to instantiate the singleton.
*/
static particle_fn &instance();
/**
* @brief Perform setup operations; call this from global application setup()
*
* You typically use particle_fn::instance().setup();
*/
void setup();
/**
* @brief Perform application loop operations; call this from global application loop()
*
* You typically use particle_fn::instance().loop();
*/
void loop();
protected:
/**
* @brief The constructor is protected because the class is a singleton
*
* Use particle_fn::instance() to instantiate the singleton.
*/
particle_fn();
/**
* @brief The destructor is protected because the class is a singleton and cannot be deleted
*/
virtual ~particle_fn();
/**
* This class is a singleton and cannot be copied
*/
particle_fn(const particle_fn&) = delete;
/**
* This class is a singleton and cannot be copied
*/
particle_fn& operator=(const particle_fn&) = delete;
/**
* @brief Singleton instance of this class
*
* The object pointer to this class is stored here. It's NULL at system boot.
*/
static particle_fn *_instance;
};
#endif /* __PARTICLE_FN_H */
particle_fn.cpp
#include "particle_fn.h"
particle_fn *particle_fn::_instance;
// [static]
particle_fn &particle_fn::instance() {
if (!_instance) {
_instance = new particle_fn();
}
return *_instance;
}
particle_fn::particle_fn() {
}
particle_fn::~particle_fn() {
}
void particle_fn::setup() {
particle_fn::particleFunctionSetups();
Serial.println("Particle Function Setup COMPLETE");
}
void particle_fn::loop() {
// Put your code to run during the application thread loop here
}
void particle_fn::particleFunctionSetups(){
Particle.function("setEmail_s", &particle_fn::setEmail, this);
}
int particle_fn::setEmail(String command){
strcpy(config::instance().config_data.email, command.c_str());
config::instance().storeConfigVariables(); // This just writes the struct to eeprom
config::instance().printConfigVariables();
return 1;
}
main.ino (labelled as something else)
/*
* Project particle_device_class_based_refactor
* Description:
* Author: Ivan W
* Date:
*/
#include "config.h"
#include "particle_fn.h"
SerialLogHandler logHandler(LOG_LEVEL_INFO);
// setup() runs once, when the device is first turned on.
void setup() {
// Put initialization like pinMode and begin functions here.
System.enableFeature(FEATURE_RESET_INFO);
Serial.begin(19200);
config::instance().setup();
particle_fn::instance().setup();
}
// loop() runs over and over again, as quickly as it can execute.
void loop() {
// The core of your code will likely live here.
config::instance().loop();
particle_fn::instance().loop();
}
So my questions:
- Am I going about this the right way?
- I am putting some methods into protected (if not required outside of singleton), and some methods into public (if required by other singletons or main.ino)
- When should I be considering using a mutex or worker thread? What type of singleton functionality would call for a mutex or worker thread?
- If I don't have any code in the singleton loop, ie just using singleton methods when required. Do I need to call the singleton's loop method in my main loop method? eg:
void loop() {
// The core of your code will likely live here.
config::instance().loop();
particle_fn::instance().loop();
}
- I was getting an error when trying to setup the particle functions, but fixed based on one of chip's comments from another topic, is the following the correct way to implement particle functions (from particle_fn.h)?
void particleFunctionSetups(){
Particle.function("setEmail_s", &particle_fn::setEmail, this);
}
Any advice or criticisms very welcome, I just want to make sure I am not doing anything crazy before ploughing through the remaining 2000+ lines of refactor. Thanks in advance.