#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Arduino.h>
#include <EEPROM.h>
#include <AsyncTimer.h>
#include <inttypes.h>
//#include <SmartButton.h>
#define EBUTTON_SUPPORT_TRANSITION_DISABLED
#define EBUTTON_SUPPORT_EACH_CLICK_DISABLED
#define EBUTTON_SUPPORT_DONE_CLICKING_DISABLED
//#define EBUTTON_SUPPORT_SINGLE_AND_DOUBLE_CLICKS_DISABLED
#define EBUTTON_SUPPORT_LONG_PRESS_START_DISABLED
#define EBUTTON_SUPPORT_LONG_PRESS_DURING_DISABLED
//#define EBUTTON_SUPPORT_LONG_PRESS_END_DISABLED
#include <EButton.h>
#define DEFAULT_PUMP_ON_DURATION 5000
#define DEFAULT_BUZZER_DELAY 4000
#define DEFUALT_PUMP_OFF_DURATION 10000
#define MIN_DURATION 500
#define MAX_DURATION 300000
#define PARAMETER_INCREMENT 500
#define DEFAULT_REPETITIONS_NUMBER 10
#define MIN_REPETITIONS_NUMBER 1
#define MAX_REPETITIONS_NUMBER 100
#define STARTUP_BUZZER_FREQUENCY 2000
#define BUZZER_FREQUENCY 4000
#define EEPROM_INITIAL_ADDRESS 0
#define VALIDITY_CHECK_VALUE 0xABCD
#define PARAMETERS_NUMBER 4
#define PUMP_ON_DURATION_INDEX 0
#define BUZZER_DELAY_INDEX 1
#define PUMP_OFF_DURATION_INDEX 2
#define REPETITIONS_NUMBER_INDEX 3
const char STATIC_STRING_0[] PROGMEM = "Pump On Duration";
const char STATIC_STRING_1[] PROGMEM = "Buzzer Delay";
const char STATIC_STRING_2[] PROGMEM = "Pump Off Duration";
const char STATIC_STRING_3[] PROGMEM = "Repetitions Number";
PGM_P const STATIC_STRINGS[] PROGMEM =
{
STATIC_STRING_0,
STATIC_STRING_1,
STATIC_STRING_2,
STATIC_STRING_3
};
#define FIELD_WIDTH 5
//typedef String (*ParameterConverter)(uint32_t);
typedef const char* (*ParameterConverter)(uint32_t);
class Configuration;
template<class T>
class Parameter {
friend class Configuration;
uint8_t id_;
T value_;
T minValue_;
T maxValue_;
T increment_;
bool printAsFloat_;
bool initialized_;
public:
Parameter() : id_(0xFF), value_(0), minValue_(0), maxValue_(0), increment_(0), initialized_(false), printAsFloat_(false) {}
Parameter(uint8_t id, const T& value, const T& minValue, const T& maxValue, const T& increment, bool printAsFloat) :
id_(id),
value_(value),
minValue_(minValue),
maxValue_(maxValue),
increment_(increment),
initialized_(true),
printAsFloat_(printAsFloat) {}
Parameter(uint8_t id, const T& value, bool printAsFloat) :
id_(id),
value_(value),
minValue_(MIN_DURATION),
maxValue_(MAX_DURATION),
increment_(PARAMETER_INCREMENT),
initialized_(true),
printAsFloat_(printAsFloat) {}
Parameter(const Parameter&) = default;
Parameter& operator=(const Parameter&) = default;
virtual ~Parameter() = default;
bool operator==(const Parameter& right) const {
return id_ == right.id_ &&
value_ == right.value_ &&
minValue_ == right.minValue_ &&
maxValue_ == right.maxValue_ &&
increment_ == right.increment_ &&
printAsFloat_ == right.printAsFloat_;
}
bool operator!=(const Parameter& right) const {
return !operator==(right);
}
uint8_t getId() const {
return id_;
}
void setId(uint8_t id) {
id_ = id;
}
// String getName() const {
// // Get first string in the instructions array
// char buffer[32];
// strcpy_P(buffer, (PGM_P)pgm_read_word(&STATIC_STRINGS[id_]));
// return String(buffer);
// }
const char* getName() const {
// Get first string in the instructions array
static char buffer[20];
strcpy_P(buffer, (PGM_P)pgm_read_word(&STATIC_STRINGS[id_]));
return buffer;
}
T getValue() const {
return value_;
}
void setValue(const T& value) {
value_ = value;
}
T& operator++() {
if (value_ + increment_ <= maxValue_) {
value_+=increment_;
}
return value_;
}
// T operator++(int) {
// if (value_ + increment_ <= maxValue_) {
// value_+=increment_;
// }
// return value_;
// }
T& operator--() {
if (value_ - increment_ >= minValue_) {
value_-=increment_;
}
return value_;
}
// T operator--(int) {
// if (value_ - increment_ >= minValue_) {
// value_-=increment_;
// }
// return value_;
// }
bool isValid() const {
return initialized_ && value_ >= minValue_ && value_ <= maxValue_;
}
static double convertToDouble(uint32_t value) {
double tmp = value;
tmp /= 1000;
return tmp;
}
//static String convert(uint32_t x) {
static const char* convert(uint32_t x) {
static char paramBuffer[16];
memset(paramBuffer, 0, sizeof(paramBuffer));
dtostrf(convertToDouble(x), FIELD_WIDTH, 1, paramBuffer);
// Serial.print(F("paramBuffer: "));
// Serial.println(paramBuffer);
// Serial.println(String(paramBuffer).c_str());
//return String(paramBuffer);
return paramBuffer;
};
//static String convertRaw(uint32_t x) {
static const char* convertRaw(uint32_t x) {
static char paramBuffer[16];
char format[16];
sprintf(format, "%c%d%c", '%', FIELD_WIDTH, 'd');
sprintf(paramBuffer, format, x);
//return String(paramBuffer);
return paramBuffer;
};
//template<typename Functor>
//String toString(ParameterConverter converter, bool printName = false) const {
const char* toString(ParameterConverter converter, bool printName = false) const {
if (printName) {
char buffer[64];
//sprintf(buffer, "%s: %s", getName().c_str(), converter(value_).c_str());
//sprintf(buffer, "%s: %s", getName(), converter(value_).c_str());
sprintf(buffer, "%s: %s", getName(), converter(value_));
//Serial.println(buffer);
//return String(buffer);
return buffer;
} else {
//Serial.println(converter(value_).c_str());
return converter(value_);
}
}
bool isInitialized() const {
return initialized_;
}
void reset() {
initialized_ = false;
}
void setMinValue(T minValue) {
minValue_ = minValue;
}
void setMaxValue(T maxValue) {
maxValue_ = maxValue;
}
void setIncrement(T increment) {
increment_ = increment;
}
void setInitialized(bool init) {
initialized_ = init;
}
bool isPrintAsFloat() const {
return printAsFloat_;
}
void setPrintAsFloat(bool printAsFloat) {
printAsFloat_ = printAsFloat;
}
};
struct ConfigurationData_t {
uint16_t validityCheck_;
uint32_t pumpOnDuration_;
uint32_t buzzerDelay_;
uint32_t pumpOffDuration_;
uint16_t repetitionsNumber_;
ConfigurationData_t() :
validityCheck_(0xABCD),
pumpOnDuration_(DEFAULT_PUMP_ON_DURATION),
buzzerDelay_(DEFAULT_BUZZER_DELAY),
pumpOffDuration_(DEFUALT_PUMP_OFF_DURATION),
repetitionsNumber_(DEFAULT_REPETITIONS_NUMBER) {}
};
class ConfigurationManager;
class Configuration {
friend class ConfigurationManager;
uint16_t validityCheck_;
Parameter<uint32_t> parameters_[PARAMETERS_NUMBER];
public:
Configuration();
Configuration(uint16_t validityCheck, const Parameter<uint32_t>* parameters);
Configuration(const ConfigurationData_t& confData);
Configuration(const Configuration& right);
Configuration& operator=(const Configuration& right);
virtual ~Configuration() = default;
bool operator==(const Configuration& right) const;
bool operator!=(const Configuration& right) const;
uint16_t getValidityCheck() const;
void setValidityCheck(uint16_t validityCheck);
Parameter<uint32_t> getParameter(uint8_t index) const;
void setParameter(uint8_t index, const Parameter<uint32_t>& param);
void incrementParameter(uint8_t index);
void decrementParameter(uint8_t index);
bool isValid() const;
ConfigurationData_t getConfigurationData() const;
};
class ConfigurationManager {
static ConfigurationManager* instance_;
ConfigurationManager() = default;
Configuration configuration_;
public:
static ConfigurationManager* getInstance();
bool loadConfig();
ConfigurationManager(const ConfigurationManager&) = delete;
ConfigurationManager& operator=(const ConfigurationManager&) = delete;
virtual ~ConfigurationManager() = default;
bool isValid() const;
static double convert(uint32_t value);
// static String toString(const Parameter<uint32_t>& parameter, bool printName = false);
// String getParameterAsString(uint8_t paramIndex, bool printName = false);
// String getRawParameterAsString(uint8_t paramIndex, bool printName = false);
// String formatParameter(uint8_t paramIndex, bool printName = false);
static const char* toString(const Parameter<uint32_t>& parameter, bool printName = false);
const char* getParameterAsString(uint8_t paramIndex, bool printName = false);
const char* getRawParameterAsString(uint8_t paramIndex, bool printName = false);
const char* formatParameter(uint8_t paramIndex, bool printName = false);
Configuration getConfiguration() const;
//String getParameterName(uint8_t paramIndex) const;
const char* getParameterName(uint8_t paramIndex) const;
void printConfigurationData();
};
static const uint32_t PARAMETERS_DEFAULT_VALUES[] = {DEFAULT_PUMP_ON_DURATION, DEFAULT_BUZZER_DELAY, DEFUALT_PUMP_OFF_DURATION, DEFAULT_REPETITIONS_NUMBER};
Configuration::Configuration() :
validityCheck_(VALIDITY_CHECK_VALUE),
parameters_({
Parameter<uint32_t>(0, PARAMETERS_DEFAULT_VALUES[0], true),
Parameter<uint32_t>(1, PARAMETERS_DEFAULT_VALUES[1], true),
Parameter<uint32_t>(2, PARAMETERS_DEFAULT_VALUES[2], true),
Parameter<uint32_t>(3, PARAMETERS_DEFAULT_VALUES[3], MIN_REPETITIONS_NUMBER, MAX_REPETITIONS_NUMBER, 1, false)
}
) {
}
Configuration::Configuration(uint16_t validityCheck, const Parameter<uint32_t>* parameters) {
validityCheck_ = validityCheck;
for (int i=0; i<PARAMETERS_NUMBER; i++) {
parameters_[i] = parameters[i];
}
}
Configuration::Configuration(const ConfigurationData_t& confData) {
Parameter<uint32_t> tmpParameters[PARAMETERS_NUMBER] = {
Parameter<uint32_t>(0, confData.pumpOnDuration_, true),
Parameter<uint32_t>(1, confData.buzzerDelay_, true),
Parameter<uint32_t>(2, confData.pumpOffDuration_, true),
Parameter<uint32_t>(3, confData.repetitionsNumber_, MIN_REPETITIONS_NUMBER, MAX_REPETITIONS_NUMBER, 1, false)
};
validityCheck_ = confData.validityCheck_;
for (int i=0; i<PARAMETERS_NUMBER; i++) {
parameters_[i] = tmpParameters[i];
}
}
Configuration::Configuration(const Configuration& right) {
validityCheck_ = right.validityCheck_;
for (int i=0; i<PARAMETERS_NUMBER; i++) {
parameters_[i] = right.parameters_[i];
}
}
Configuration& Configuration::operator=(const Configuration& right) {
if (this != &right) {
validityCheck_ = right.validityCheck_;
for (int i=0; i<PARAMETERS_NUMBER; i++) {
parameters_[i] = right.parameters_[i];
}
}
return *this;
}
bool Configuration::operator==(const Configuration& right) const {
bool result = validityCheck_ == right.validityCheck_;
if (result) {
for (int i=0; i<PARAMETERS_NUMBER; i++) {
result = parameters_[i] == right.parameters_[i];
if (!result) {
break;
}
}
}
return result;
}
bool Configuration::operator!=(const Configuration& right) const {
return !operator==(right);
}
Parameter<uint32_t> Configuration::getParameter(uint8_t index) const {
if (index < PARAMETERS_NUMBER) {
return parameters_[index];
}
return Parameter<uint32_t>();
}
uint16_t Configuration::getValidityCheck() const {
return validityCheck_;
}
void Configuration::setValidityCheck(uint16_t validityCheck) {
validityCheck_ = validityCheck;
}
void Configuration::setParameter(uint8_t index, const Parameter<uint32_t>& param) {
if (index < PARAMETERS_NUMBER) {
parameters_[index] = param;
}
}
bool Configuration::isValid() const {
bool result = validityCheck_ == VALIDITY_CHECK_VALUE;
//Serial.print(F("validityCheck: "));
//Serial.println(validityCheck_);
if (result) {
for (int i=0; i<PARAMETERS_NUMBER; i++) {
result = parameters_[i].isValid();
if (!result) {
break;
}
}
}
return result;
}
void Configuration::incrementParameter(uint8_t index) {
if (index < PARAMETERS_NUMBER) {
++parameters_[index];
}
}
void Configuration::decrementParameter(uint8_t index) {
if (index < PARAMETERS_NUMBER) {
--parameters_[index];
}
}
ConfigurationData_t Configuration::getConfigurationData() const {
ConfigurationData_t result;
result.validityCheck_ = validityCheck_;
result.pumpOnDuration_ = parameters_[PUMP_ON_DURATION_INDEX].value_;
result.buzzerDelay_ = parameters_[BUZZER_DELAY_INDEX].value_;
result.pumpOffDuration_ = parameters_[PUMP_OFF_DURATION_INDEX].value_;
result.repetitionsNumber_ = parameters_[REPETITIONS_NUMBER_INDEX].value_;
return result;
}
ConfigurationManager* ConfigurationManager::instance_ = nullptr;
ConfigurationManager* ConfigurationManager::getInstance() {
if (instance_ == nullptr) {
instance_ = new ConfigurationManager();
}
return instance_;
}
bool ConfigurationManager::loadConfig() {
bool result = false;
ConfigurationData_t configurationData;
//Variable to store custom object read from EEPROM.
EEPROM.get(EEPROM_INITIAL_ADDRESS, configurationData);
Configuration tmpConfiguration(configurationData);
if (tmpConfiguration.isValid()) {
Serial.println(F("Configuration validity check result: ok"));
} else {
Serial.println(F("Configuration validity check result: not ok"));
Serial.println(F("Writing default values..."));
ConfigurationData_t defaultConfig;
Serial.println(F("Default values correctly written in EEPROM"));
configuration_ = Configuration(defaultConfig);
}
return result;
}
bool ConfigurationManager::isValid() const {
return configuration_.isValid();
}
// double ConfigurationManager::convert(uint32_t value) {
// double tmp = value;
// tmp /= 1000;
// return tmp;
// }
//String ConfigurationManager::toString(const Parameter<uint32_t>& parameter, bool printName) {
const char* ConfigurationManager::toString(const Parameter<uint32_t>& parameter, bool printName) {
return parameter.toString(&Parameter<uint32_t>::convert, printName);
}
//String ConfigurationManager::getParameterAsString(uint8_t paramIndex, bool printName) {
const char* ConfigurationManager::getParameterAsString(uint8_t paramIndex, bool printName) {
static const char* result = "";
if (paramIndex < PARAMETERS_NUMBER) {
result = toString(configuration_.parameters_[paramIndex], printName);
//Serial.println(result.c_str());
}
return result;
}
//String ConfigurationManager::getRawParameterAsString(uint8_t paramIndex, bool printName) {
const char* ConfigurationManager::getRawParameterAsString(uint8_t paramIndex, bool printName) {
static const char* result = "";
if (paramIndex < PARAMETERS_NUMBER) {
// auto lambda = [] (uint32_t x) {
// char paramBuffer[16];
// char format[16];
// sprintf(format, "%c%d%c", '%', FIELD_WIDTH, 'd');
// sprintf(paramBuffer, format, x);
// return String(paramBuffer);
// };
//result = configuration_.parameters_[paramIndex].toString(lambda, printName);
result = configuration_.parameters_[paramIndex].toString(&Parameter<uint32_t>::convertRaw, printName);
}
return result;
}
//String ConfigurationManager::formatParameter(uint8_t paramIndex, bool printName) {
const char* ConfigurationManager::formatParameter(uint8_t paramIndex, bool printName) {
static const char* result = "";
if (paramIndex < PARAMETERS_NUMBER) {
if (configuration_.parameters_[paramIndex].isPrintAsFloat()) {
result = getParameterAsString(paramIndex, printName);
} else {
result = getRawParameterAsString(paramIndex, printName);
}
}
return result;
}
void ConfigurationManager::printConfigurationData() {
for (int i=0; i<PARAMETERS_NUMBER; i++) {
//Serial.println(formatParameter(i, true).c_str());
Serial.println(formatParameter(i, true));
}
}
Configuration ConfigurationManager::getConfiguration() const {
return configuration_;
}
//String ConfigurationManager::getParameterName(uint8_t paramIndex) const {
const char* ConfigurationManager::getParameterName(uint8_t paramIndex) const {
//String result = "";
static char* result = "";
if (paramIndex < PARAMETERS_NUMBER) {
result = configuration_.parameters_[paramIndex].getName();
}
return result;
}
// #include <SmartButton.h>
// using namespace smartbutton;
#define CONFIG_TIMEOUT 5000
enum class ButtonObject : uint8_t {
CONFIG,
INCREMENT,
DECREMENT,
START_STOP
};
enum class ButtonEvent : uint8_t {
BUTTON_CLICK,
BUTTON_LONG_PRESS_END
};
class State;
class Context {
State* currentState_ = nullptr;
Adafruit_SSD1306* display_ = nullptr;
public:
Context(Adafruit_SSD1306* display) : display_(display) {}
Context(const Context&) = delete;
Context& operator=(const Context&) = delete;
virtual ~Context() = default;
void setState(State* state);
void handle();
void refreshDisplay();
//void handleEvent(ButtonObject button, SmartButton::Event event);
void handleEvent(ButtonObject button, ButtonEvent event);
};
class State {
protected:
Context* context_ = nullptr;
static AsyncTimer stateTimer_;
//static State* instance_;
State() = default;
public:
State(const State&) = delete;
State& operator=(const State&) = delete;
virtual ~State() = default;
// virtual void handleButtonPressed(ButtonObject button) {};
// virtual void handleButtonReleased(ButtonObject button) {};
virtual void handleButtonClick(ButtonObject button) {};
virtual void handleButtonHold(ButtonObject button) {};
// virtual void handleButtonLongHold(ButtonObject button) {};
// virtual void handleButtonHoldRepeat(ButtonObject button) {};
// virtual void handleButtonLongHoldRepeat(ButtonObject button) {};
virtual void onExit();
virtual void onEnter();
virtual void display(Adafruit_SSD1306* display) {};
void setContext(Context* context);
void handle();
//static void drawCentreString(Adafruit_SSD1306* display, const String &buf, int x, int y);
static void drawCentreString(Adafruit_SSD1306* display, const char* buf, int x, int y);
};
AsyncTimer State::stateTimer_ = AsyncTimer(2);
class InitState : public State {
friend class State;
InitState() = default;
InitState(const InitState&) = delete;
InitState& operator=(const InitState&) = delete;
static InitState* instance_;
public:
virtual ~InitState() = default;
static InitState* getInstance();
virtual void handleButtonClick(ButtonObject button) override;
virtual void handleButtonHold(ButtonObject button) override;
virtual void display(Adafruit_SSD1306* display) override;
};
class ConfigViewState : public State {
friend class State;
ConfigViewState() = default;
ConfigViewState(const ConfigViewState&) = delete;
ConfigViewState& operator=(const ConfigViewState&) = delete;
unsigned short timerId_ = 0;
static ConfigViewState* instance_;
public:
virtual ~ConfigViewState() = default;
static ConfigViewState* getInstance();
virtual void handleButtonClick(ButtonObject button) override;
virtual void handleButtonHold(ButtonObject button) override;
virtual void onEnter() override;
virtual void display(Adafruit_SSD1306* display) override;
};
class RunningState : public State {
friend class State;
RunningState() = default;
RunningState(const RunningState&) = delete;
RunningState& operator=(const RunningState&) = delete;
unsigned short buzzerTimerId_ = 0;
unsigned short stopTimerId_ = 0;
static RunningState* instance_;
public:
virtual ~RunningState() = default;
static RunningState* getInstance();
virtual void handleButtonClick(ButtonObject button) override;
};
class ConfigEditState : public State {
friend class State;
//bool holdFilter_ = true;
uint8_t parameterIndex_ = -1;
bool show_ = true;
Configuration currentConfiguration_;
ConfigEditState() = default;
ConfigEditState(const ConfigEditState&) = delete;
ConfigEditState& operator=(const ConfigEditState&) = delete;
unsigned short timerId_ = 0;
unsigned short refreshTimerId_ = 0;
static ConfigEditState* instance_;
public:
virtual ~ConfigEditState() = default;
static ConfigEditState* getInstance();
virtual void handleButtonClick(ButtonObject button) override;
virtual void handleButtonHold(ButtonObject button) override;
//virtual void handleButtonReleased(ButtonObject button) override;
virtual void onEnter() override;
virtual void display(Adafruit_SSD1306* display) override;
//virtual void onExit() override;
};
#define REFRESH_DISPLAY_TIMEOUT 400
void Context::setState(State* state) {
Serial.println(F("Context::setState"));
if (currentState_ != nullptr) {
currentState_->onExit();
//delete currentState_;
}
state->setContext(this);
currentState_ = state;
state->onEnter();
}
void Context::handle() {
currentState_->handle();
}
// void Context::handleEvent(ButtonObject button, SmartButton::Event event) {
// switch(event) {
// case SmartButton::Event::RELEASED:
// currentState_->handleButtonReleased(button);
// break;
// case SmartButton::Event::PRESSED:
// currentState_->handleButtonPressed(button);
// break;
// case SmartButton::Event::CLICK:
// currentState_->handleButtonClick(button);
// break;
// case SmartButton::Event::HOLD:
// currentState_->handleButtonHold(button);
// break;
// case SmartButton::Event::HOLD_REPEAT:
// currentState_->handleButtonHoldRepeat(button);
// break;
// case SmartButton::Event::LONG_HOLD:
// currentState_->handleButtonLongHold(button);
// break;
// case SmartButton::Event::LONG_HOLD_REPEAT:
// currentState_->handleButtonLongHoldRepeat(button);
// break;
// }
// }
void Context::handleEvent(ButtonObject button, ButtonEvent event) {
switch(event) {
case ButtonEvent::BUTTON_CLICK:
currentState_->handleButtonClick(button);
break;
case ButtonEvent::BUTTON_LONG_PRESS_END:
currentState_->handleButtonHold(button);
break;
}
}
void Context::refreshDisplay() {
Serial.println(F("Context::refreshDisplay"));
if (currentState_ != nullptr) {
currentState_->display(display_);
}
}
void State::setContext(Context* context) {
this->context_ = context;
}
void State::onEnter() {
Serial.println(F("State::onEnter"));
this->context_->refreshDisplay();
}
void State::onExit() {
Serial.println(F("State::onExit"));
stateTimer_.cancelAll();
}
void State::handle() {
stateTimer_.handle();
}
//void State::drawCentreString(Adafruit_SSD1306* display, const String &buf, int x, int y)
void State::drawCentreString(Adafruit_SSD1306* display, const char* buf, int x, int y)
{
int16_t x1, y1;
uint16_t w, h;
display->getTextBounds(buf, x, y, &x1, &y1, &w, &h); //calc width of new string
display->setCursor(x - w / 2, y);
display->print(buf);
}
InitState* InitState::instance_ = nullptr;
InitState* InitState::getInstance() {
//return State::getInstance<InitState>();
if (instance_ == nullptr) {
instance_ = new InitState();
}
return instance_;
}
void InitState::handleButtonClick(ButtonObject button) {
switch(button) {
case ButtonObject::CONFIG:
context_->setState(ConfigViewState::getInstance());
break;
case ButtonObject::START_STOP:
context_->setState(RunningState::getInstance());
break;
default:
break;
}
}
void InitState::handleButtonHold(ButtonObject button) {
if (button == ButtonObject::CONFIG) {
context_->setState(ConfigEditState::getInstance());
}
}
void InitState::display(Adafruit_SSD1306* display) {
Serial.println(F("InitState::display"));
display->clearDisplay();
display->setTextSize(1); // Normal 1:1 pixel scale
display->setTextColor(SSD1306_WHITE); // Draw white text
display->setCursor(0,display->height()-8);
display->println(F("Config Start"));
display->display();
}
ConfigViewState* ConfigViewState::instance_ = nullptr;
ConfigViewState* ConfigViewState::getInstance() {
//return State::getInstance<ConfigViewState>();
if (instance_ == nullptr) {
instance_ = new ConfigViewState();
}
return instance_;
}
void ConfigViewState::handleButtonClick(ButtonObject button) {
if (button == ButtonObject::CONFIG) {
context_->setState(InitState::getInstance());
}
}
void ConfigViewState::handleButtonHold(ButtonObject button) {
if (button == ButtonObject::CONFIG) {
context_->setState(ConfigEditState::getInstance());
}
}
void ConfigViewState::onEnter() {
Serial.println(F("ConfigViewState::onEnter"));
State::onEnter();
timerId_ = stateTimer_.setTimeout([=]() { context_->setState(InitState::getInstance()); }, CONFIG_TIMEOUT);
}
void ConfigViewState::display(Adafruit_SSD1306* display) {
Serial.println(F("ConfigViewState::display"));
display->clearDisplay();
display->setTextSize(1); // Normal 1:1 pixel scale
display->setTextColor(SSD1306_WHITE); // Draw white text
display->setCursor(0,0); // Start at top-left corner
//display.println(F(" RD BD SD RN"));
display->println(F(" ON BD OFF RN"));
//Serial.println(ConfigurationManager::getInstance()->getParameterAsString(PUMP_ON_DURATION_INDEX).c_str());
// display->print(ConfigurationManager::getInstance()->getParameterAsString(PUMP_ON_DURATION_INDEX).c_str());
// display->print(ConfigurationManager::getInstance()->getParameterAsString(BUZZER_DELAY_INDEX).c_str());
// display->print(ConfigurationManager::getInstance()->getParameterAsString(PUMP_OFF_DURATION_INDEX).c_str());
// display->print(ConfigurationManager::getInstance()->getRawParameterAsString(REPETITIONS_NUMBER_INDEX).c_str());
display->print(ConfigurationManager::getInstance()->getParameterAsString(PUMP_ON_DURATION_INDEX));
display->print(ConfigurationManager::getInstance()->getParameterAsString(BUZZER_DELAY_INDEX));
display->print(ConfigurationManager::getInstance()->getParameterAsString(PUMP_OFF_DURATION_INDEX));
display->print(ConfigurationManager::getInstance()->getRawParameterAsString(REPETITIONS_NUMBER_INDEX));
display->display();
}
RunningState* RunningState::instance_ = nullptr;
RunningState* RunningState::getInstance() {
//return State::getInstance<RunningState>();
if (instance_ == nullptr) {
instance_ = new RunningState();
}
return instance_;
}
void RunningState::handleButtonClick(ButtonObject button) {
if (button == ButtonObject::START_STOP) {
context_->setState(InitState::getInstance());
}
}
ConfigEditState* ConfigEditState::instance_ = nullptr;
ConfigEditState* ConfigEditState::getInstance() {
//return State::getInstance<ConfigEditState>();
if (instance_ == nullptr) {
instance_ = new ConfigEditState();
}
return instance_;
}
void ConfigEditState::onEnter() {
State::onEnter();
//this->holdFilter_ = true;
parameterIndex_ = 0;
currentConfiguration_ = ConfigurationManager::getInstance()->getConfiguration();
timerId_ = stateTimer_.setTimeout([=]() { context_->setState(InitState::getInstance()); }, CONFIG_TIMEOUT);
refreshTimerId_ = stateTimer_.setInterval([=]() { context_->refreshDisplay(); }, REFRESH_DISPLAY_TIMEOUT);
}
void ConfigEditState::display(Adafruit_SSD1306* display) {
display->clearDisplay();
display->setTextSize(1); // Normal 1:1 pixel scale
display->setTextColor(SSD1306_WHITE); // Draw white text
display->setCursor(0,0); // Start at top-left corner
drawCentreString(display, ConfigurationManager::getInstance()->getParameterName(parameterIndex_), display->width()/2, 0);
//display->setCursor(8,8);
display->setTextSize(3);
//Serial.println(parameterIndex_);
if (show_) {
drawCentreString(display, ConfigurationManager::getInstance()->formatParameter(parameterIndex_), display->width()/2, 12);
//display->println(ConfigurationManager::getInstance()->formatParameter(parameterIndex_));
} else {
drawCentreString(display, " ", display->width()/2, 12);
//display->println(" ");
}
display->display();
show_ = !show_;
}
void ConfigEditState::handleButtonHold(ButtonObject button) {
if (button == ButtonObject::CONFIG) {
//if (!this->holdFilter_) {
// save
Serial.println(F("Checking differences..."));
Configuration savedConfiguration = ConfigurationManager::getInstance()->getConfiguration();
if (currentConfiguration_ != savedConfiguration) {
Serial.println(F("Saving data to EEPROM..."));
EEPROM.put(EEPROM_INITIAL_ADDRESS, currentConfiguration_.getConfigurationData());
Serial.println(F("Data saved to EEPROM..."));
} else {
Serial.println(F("No change occurred"));
}
context_->setState(InitState::getInstance());
//}
}
}
// void ConfigEditState::handleButtonReleased(ButtonObject button) {
// if (button == ButtonObject::CONFIG) {
// this->holdFilter_ = false;
// stateTimer_.reset(refreshTimerId_);
// context_->refreshDisplay();
// }
// }
void ConfigEditState::handleButtonClick(ButtonObject button) {
stateTimer_.reset(timerId_);
switch(button) {
case ButtonObject::CONFIG:
// next param
parameterIndex_++;
parameterIndex_ = parameterIndex_ % PARAMETERS_NUMBER;
stateTimer_.reset(refreshTimerId_);
context_->refreshDisplay();
break;
case ButtonObject::INCREMENT:
// param++
currentConfiguration_.incrementParameter(parameterIndex_);
stateTimer_.reset(refreshTimerId_);
context_->refreshDisplay();
break;
case ButtonObject::DECREMENT:
// param--
currentConfiguration_.decrementParameter(parameterIndex_);
stateTimer_.reset(refreshTimerId_);
context_->refreshDisplay();
break;
default:
break;
}
}
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 32 // OLED display height, in pixels
// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
// The pins for I2C are defined by the Wire-library.
// On an arduino UNO: A4(SDA), A5(SCL)
// On an arduino MEGA 2560: 20(SDA), 21(SCL)
// On an arduino LEONARDO: 2(SDA), 3(SCL), ...
#define OLED_RESET 4 // Reset pin # (or -1 if sharing Arduino reset pin)
#define SCREEN_ADDRESS 0x3C ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
Context context(&display);
#define BUTTON_PIN 8
// using namespace smartbutton;
// class ButtonInterface: public SmartButtonInterface {
// public:
// virtual void event(SmartButton *button, SmartButton::Event event, int clickCounter)
// {
// context.handleEvent(ButtonObject::CONFIG, event);
// }
// /*void toggle() {
// digitalWrite(LED_PIN, !digitalRead(LED_PIN));
// }*/
// virtual bool isPressed(SmartButton *button)
// {
// return (digitalRead(BUTTON_PIN) == LOW) ? true : false;
// }
// };
// ButtonInterface buttonInterface;
// SmartButton button(&buttonInterface);
EButton* button;
void singleClick(EButton &btn) {
//Serial.println(F("We have a click!"));
context.handleEvent(ButtonObject::CONFIG, ButtonEvent::BUTTON_CLICK);
}
// void doubleClick(EButton &btn) {
// Serial.println("We have a double click!");
// }
void longPressedEnd(EButton &btn) {
//Serial.println(F("We have a long pressed end!"));
context.handleEvent(ButtonObject::CONFIG, ButtonEvent::BUTTON_LONG_PRESS_END);
}
#ifdef __arm__
// should use uinstd.h to define sbrk but Due causes a conflict
extern "C" char* sbrk(int incr);
#else // __ARM__
extern char *__brkval;
#endif // __arm__
int freeMemory() {
char top;
#ifdef __arm__
return &top - reinterpret_cast<char*>(sbrk(0));
#elif defined(CORE_TEENSY) || (ARDUINO > 103 && ARDUINO != 151)
return &top - __brkval;
#else // __arm__
return __brkval ? &top - __brkval : &top - __malloc_heap_start;
#endif // __arm__
}
void setup() {
// put your setup code here, to run once:
Serial.begin(19200);
while (!Serial) {
; // wait for serial port to connect. Needed for native USB port only
}
Serial.println(F("Serial ok!"));
Serial.println(freeMemory());
// SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
if(!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
Serial.println(F("SSD1306 allocation failed"));
for(;;); // Don't proceed, loop forever
}
Serial.println(freeMemory());
Serial.println(F("Checking configuration..."));
ConfigurationManager::getInstance()->loadConfig();
ConfigurationManager::getInstance()->printConfigurationData();
// Show initial display buffer contents on the screen --
// the library initializes this with an Adafruit splash screen.
display.display();
delay(2000); // Pause for 2 seconds
// Clear the buffer
display.clearDisplay();
//pinMode(BUTTON_PIN, INPUT_PULLUP); // Digital input with pull-up resistors (normal high)
//button.begin(); // Initialize and register smart button
button = new EButton(BUTTON_PIN);
button->attachSingleClick(singleClick);
//button.attachDoubleClick(doubleClick);
button->attachLongPressEnd(longPressedEnd);
context.setState(InitState::getInstance());
Serial.println(freeMemory());
}
void loop() {
// put your main code here, to run repeatedly:
context.handle();
//SmartButton::service(); // Asynchronous service routine, should be called periodically
button->tick();
}
nano:12
nano:11
nano:10
nano:9
nano:8
nano:7
nano:6
nano:5
nano:4
nano:3
nano:2
nano:GND.2
nano:RESET.2
nano:0
nano:1
nano:13
nano:3.3V
nano:AREF
nano:A0
nano:A1
nano:A2
nano:A3
nano:A4
nano:A5
nano:A6
nano:A7
nano:5V
nano:RESET
nano:GND.1
nano:VIN
nano:12.2
nano:5V.2
nano:13.2
nano:11.2
nano:RESET.3
nano:GND.3
Loading
ssd1306
ssd1306
btn1:1.l
btn1:2.l
btn1:1.r
btn1:2.r