//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Day timer
// 8 channels
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// **************************************************** BASIC DEFINITIONS
#include <limits.h>
#include <EEPROM.h> // max: 4096
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
#define LED_PIN 13 // MEGA: 13 / RP2040: 25
#define RND_PIN A0 // MEGA: A0 / RP2040: 26 (A0)
#define PWRCTRL_PIN A4 // MEGA: A4 / RP2040: 27 (A1)
#define BUTTON_A_PIN A1 // any digital
#define BUTTON_B_PIN A3 // any digital
#define BUTTON_C_PIN 12 // any digital
#define BUTTON_D_PIN A2 // any digital
#define OUT_0_PIN 2 // any digital
#define OUT_1_PIN 3 // any digital
#define OUT_2_PIN 4 // any digital
#define OUT_3_PIN 5 // any digital
#define OUT_4_PIN 6 // any digital
#define OUT_5_PIN 7 // any digital
#define OUT_6_PIN 8 // any digital
#define OUT_7_PIN 9 // any digital
// **************************************************** AUX
// ----------------------------------------------------
void printLine() {
Serial.println();
}
// ----------------------------------------------------
template <typename T, typename... Types>
void printLine(T first, Types... other) {
Serial.print(first);
printLine(other...) ;
}
// **************************************************** CLASSES
// ----------------------------------------------------
class Button {
private:
byte pin; // defined button (pin)
Button* button[4]; // simultaneous button(s) (max: 4)
byte buttons; // number of simultaneous button(s)
bool lastState;
bool wasOnFlag;
bool wasOn3sFlag;
unsigned long lastDebounceTime;
const unsigned long debounceDelay = 50;
bool currentState;
unsigned long pressedTime;
bool ignoreButtonRelease;
public:
Button(byte buttonPin) : pin(buttonPin) { // define button
pinMode(pin, INPUT_PULLUP);
buttons = 0; // no objects stored
lastState = HIGH;
currentState = HIGH;
lastDebounceTime = 0;
wasOnFlag = false;
wasOn3sFlag = false;
pressedTime = 0;
ignoreButtonRelease = false;
}
Button(Button* buttonObjects[], byte length) : buttons(length) { // define simultaneous already defined button(s)
for (byte i = 0; i < buttons; i++) button[i] = buttonObjects[i];
lastState = HIGH;
currentState = HIGH;
lastDebounceTime = 0;
wasOnFlag = false;
wasOn3sFlag = false;
pressedTime = 0;
ignoreButtonRelease = false;
}
void reset() {
ignoreButtonRelease = true;
if(buttons > 0) { // reset all simultaneous button(s)
for(byte i = 0; i < buttons; i++) button[i]->reset();
}
}
void update() {
bool reading;
if(buttons == 0) { // defined button
reading = digitalRead(pin);
} else { // simultaneous button(s)
reading = LOW;
for(byte i = 0; i < buttons; i++) { // reading LOW only if all pins are LOW
if(digitalRead(button[i]->pin) == HIGH) reading = HIGH;
}
}
if(reading != lastState) {
lastDebounceTime = millis();
}
if((millis() - lastDebounceTime) > debounceDelay) {
if(currentState != reading) {
currentState = reading;
if(currentState == LOW) { // button(s) pressed
ignoreButtonRelease = false; // remove reset
pressedTime = millis();
} else { // button(s) released
if(buttons > 0) { // reset all simultaneous button(s)
for(byte i = 0; i < buttons; i++) button[i]->reset();
}
if(!ignoreButtonRelease) { // if reset, ignore button(s) released, otherwise, signal
unsigned long heldTime = millis() - pressedTime;
wasOnFlag = true;
if(heldTime >= 3000) {
wasOnFlag = false;
wasOn3sFlag = true;
}
}
}
}
}
lastState = reading;
}
bool wasOn() {
if(wasOnFlag) {
wasOnFlag = false;
return true;
}
return false;
}
bool wasOn3s() {
if(wasOn3sFlag) {
wasOn3sFlag = false;
return true;
}
return false;
}
};
// ----------------------------------------------------
class ConfigDigit {
private:
byte *digitMemory;
byte screenX; // left pixel
byte screenY; // top pixel
byte mode; // type of config
public:
ConfigDigit() {}
ConfigDigit(byte *_digitMemory, byte _screenX, byte _screenY, byte _mode) {
digitMemory = _digitMemory;
screenX = _screenX;
screenY = _screenY;
mode = _mode;
}
byte value() {
return *digitMemory;
}
void show() {
if(mode == 0) display.fillTriangle(screenX + 2, screenY + 8, screenX, screenY + 10, screenX + 4, screenY + 10, WHITE);
if(mode == 1) display.fillTriangle(screenX + 2, screenY - 2, screenX, screenY - 4, screenX + 4, screenY - 4, WHITE);
display.display();
}
void hide() {
if(mode == 0) display.fillRect(screenX, screenY + 8, 6, 8, BLACK);
if(mode == 1) display.fillRect(screenX, screenY - 8, 6, 8, BLACK);
display.display();
}
void increment(byte min, byte max) {
if(*digitMemory >= max) *digitMemory = min;
else (*digitMemory)++;
display.setTextColor(WHITE, BLACK);
display.setCursor(screenX, screenY);
display.print(*digitMemory);
display.display();
}
};
// ----------------------------------------------------
class OnOffSettings {
public:
byte channel;
byte timeHoursStart0;
byte timeHoursStart1;
byte timeMinutesStart0;
byte timeMinutesStart1;
byte timeHoursEnd0;
byte timeHoursEnd1;
byte timeMinutesEnd0;
byte timeMinutesEnd1;
};
// **************************************************** MAIN DEFINITIONS
// ----------------------------------------------------
byte outSwitchWidth;
byte outSwitchHeight;
byte outSwitchHBorder;
byte outSwitchRowStartY;
byte timeRowStartY;
byte relays[8]; //relay outputs
int powerLossCtrlThreshold = 700; // RPi 2040: <=3.3V, <=4095 / arduino NANO: <=5V, <=1023
Button bA = Button(BUTTON_A_PIN);
Button bB = Button(BUTTON_B_PIN);
Button bC = Button(BUTTON_C_PIN);
Button bD = Button(BUTTON_D_PIN);
Button* _bAC[] = {&bA, &bC};
Button* _bBD[] = {&bB, &bD};
Button* _bAB[] = {&bA, &bB};
Button* _bCD[] = {&bC, &bD};
Button bAC = Button(_bAC, 2);
Button bBD = Button(_bBD, 2);
Button bAB = Button(_bAB, 2);
Button bCD = Button(_bCD, 2);
byte status; // 0:pause - 1:run - 2:configClock - 3:configChannels
bool timeChanged;
byte timeHours[2]; // current hours
byte timeMinutes[2]; // current minutes
byte speed[2]; // current speed
byte configPosition;
ConfigDigit timeHours0;
ConfigDigit timeHours1;
ConfigDigit timeMinutes0;
ConfigDigit timeMinutes1;
ConfigDigit speed0;
ConfigDigit speed1;
ConfigDigit configClockDigit[6];
byte configTimeHours[2]; // config hours
byte configTimeMinutes[2]; // config minutes
byte configSpeed[2]; // config speed
OnOffSettings configOnOffSettings; // channel, timeHoursStart[2], timeMinutesStart[2], timeHoursEnd[2], timeMinutesEnd[2]
int onOffSettingsSize = 10;
OnOffSettings onOffSettings[10];
unsigned long previousMS;
unsigned long powerLossCtrlMS;
// **************************************************** FUNCTIONS
// ----------------------------------------------------
void setup() {
Serial.begin(115200);
randomSeed(analogRead(RND_PIN)); // live this pin not connected
/*
// RP2040 I2C0: SDA (GP0, GP4, GP8, GP12, GP16, GP20), SCL (GP1, GP5, GP9, GP13, GP17, GP21)
// not needed for default
Wire.setSDA(8); // GP8 for SDA (I2C0) ... use the default: 4
Wire.setSCL(9); // GP9 for SCL (I2C0) ... use the default: 5
Wire.begin(); // Start I2C
*/
if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3D for 128x64
Serial.println(F("SSD1306 allocation failed"));
for(;;);
}
delay(2000);
pinMode(LED_PIN, OUTPUT);
digitalWrite(LED_PIN, LOW); // Off
// set the 8 output relays as output
relays[0] = OUT_0_PIN;
relays[1] = OUT_1_PIN;
relays[2] = OUT_2_PIN;
relays[3] = OUT_3_PIN;
relays[4] = OUT_4_PIN;
relays[5] = OUT_5_PIN;
relays[6] = OUT_6_PIN;
relays[7] = OUT_7_PIN;
for(byte ch = 0; ch < 8; ch++) {
pinMode(relays[ch], OUTPUT);
digitalWrite(relays[ch], LOW); // Off
}
unsigned long powerLossCtrlMS = millis();
// EEPROM.begin(2048); // rpi 2040 - normal flash sector size is 4K
outSwitchWidth = display.width() / 8; // 8 outputs
outSwitchHBorder = outSwitchWidth / 4;
outSwitchHeight = outSwitchWidth - 2 * outSwitchHBorder; // square
outSwitchRowStartY = 0; // top of the screen
timeRowStartY = display.height() - 7; // bottom of the screen
getStoredTime();
getStoredSpeed();
getStoredOnOffSettings();
timeHours0 = ConfigDigit(&configTimeHours[0], 31, timeRowStartY, 1);
timeHours1 = ConfigDigit(&configTimeHours[1], 37, timeRowStartY, 1);
timeMinutes0 = ConfigDigit(&configTimeMinutes[0], 49, timeRowStartY, 1);
timeMinutes1 = ConfigDigit(&configTimeMinutes[1], 55, timeRowStartY, 1);
speed0 = ConfigDigit(&configSpeed[0], 109, timeRowStartY, 1);
speed1 = ConfigDigit(&configSpeed[1], 115, timeRowStartY, 1);
configClockDigit[0] = timeHours0;
configClockDigit[1] = timeHours1;
configClockDigit[2] = timeMinutes0;
configClockDigit[3] = timeMinutes1;
configClockDigit[4] = speed0;
configClockDigit[5] = speed1;
display.clearDisplay();
display.setTextSize(1);
printLine("PAUSE");
status = 0; // 0:pause - 1:run - 2:configClock - 3:configChannels
showPauseText();
timeChanged = true;
printLine("AFTER SETUP");
}
// ----------------------------------------------------
void loop() {
int power = analogRead(PWRCTRL_PIN);
if(power < powerLossCtrlThreshold) { // eventual power loss
unsigned long currentMS = millis();
if((currentMS - powerLossCtrlMS) >= 5000) { // minimum interval: 5s
storeTime();
printLine("Power loss (<", powerLossCtrlThreshold, "): ", power);
digitalWrite(LED_PIN, HIGH); // On
delay(500);
digitalWrite(LED_PIN, LOW); // Off
powerLossCtrlMS = currentMS;
}
}
if(timeChanged) {
showTime();
timeChanged = false;
}
if(status == 1) tryIncrementTime(); // run
buttons();
}
// ----------------------------------------------------
void tryIncrementTime() {
unsigned long currentMS = millis();
byte _speed = speed[0] * 10 + speed[1];
if(_speed * (currentMS - previousMS) / 60000 >= 1) { // 'minute' change
if(timeMinutes[1] < 9) timeMinutes[1]++;
else {
timeMinutes[1] = 0;
if(timeMinutes[0] < 5) timeMinutes[0]++;
else { // 'hour' change
timeMinutes[0] = 0;
if((timeHours[0] < 2 && timeHours[1] < 9) || (timeHours[0] == 2 && timeHours[1] < 3)) timeHours[1]++;
else {
timeHours[1] = 0;
if(timeHours[0] < 2) timeHours[0]++;
else timeHours[0] = 0;
}
}
}
previousMS = currentMS;
timeChanged = true;
}
}
// ----------------------------------------------------
void buttons() {
// Buttons: A(A1) B(A3) C(D12) D(A2)
// status ... 0:pause - 1:run - 2:configClock - 3:configChannels
// arrays of buttons always first
bAC.update();
bBD.update();
bAB.update();
bCD.update();
// single buttons always last
bA.update();
bB.update();
bC.update();
bD.update();
// arrays of buttons always first
bool bAC_wasOn = bAC.wasOn();
bool bAC_wasOn3s = bAC.wasOn3s();
bool bBD_wasOn = bBD.wasOn();
bool bBD_wasOn3s = bBD.wasOn3s();
bool bAB_wasOn = bAB.wasOn();
bool bAB_wasOn3s = bAB.wasOn3s();
bool bCD_wasOn = bCD.wasOn();
bool bCD_wasOn3s = bCD.wasOn3s();
// single buttons always last
bool bA_wasOn = bA.wasOn();
bool bA_wasOn3s = bA.wasOn3s();
bool bB_wasOn = bB.wasOn();
bool bB_wasOn3s = bB.wasOn3s();
bool bC_wasOn = bC.wasOn();
bool bC_wasOn3s = bC.wasOn3s();
bool bD_wasOn = bD.wasOn();
bool bD_wasOn3s = bD.wasOn3s();
if(bAC_wasOn || bAC_wasOn3s) {
bA_wasOn = false;
bA_wasOn3s = false;
bC_wasOn = false;
bC_wasOn3s = false;
}
if(bBD_wasOn || bBD_wasOn3s) {
bB_wasOn = false;
bB_wasOn3s = false;
bD_wasOn = false;
bD_wasOn3s = false;
}
if(bAB_wasOn || bAB_wasOn3s) {
bA_wasOn = false;
bA_wasOn3s = false;
bB_wasOn = false;
bB_wasOn3s = false;
}
if(bCD_wasOn || bCD_wasOn3s) {
bC_wasOn = false;
bC_wasOn3s = false;
bD_wasOn = false;
bD_wasOn3s = false;
}
/*
if(bA_wasOn) printLine("bA ON");
if(bA_wasOn3s) printLine("bA3s ON");
if(bB_wasOn) printLine("bB ON");
if(bB_wasOn3s) printLine("bB3s ON");
if(bC_wasOn) printLine("bC ON");
if(bC_wasOn3s) printLine("bC3s ON");
if(bD_wasOn) printLine("bD ON");
if(bD_wasOn3s) printLine("bD3s ON");
if(bAC_wasOn) printLine("bAC ON");
if(bAC_wasOn3s) printLine("bAC3s ON");
if(bBD_wasOn) printLine("bBD ON");
if(bBD_wasOn3s) printLine("bBD3s ON");
if(bAB_wasOn) printLine("bAB ON");
if(bAB_wasOn3s) printLine("bAB3s ON");
if(bCD_wasOn) printLine("bCD ON");
if(bCD_wasOn3s) printLine("bCD3s ON");
*/
switch(status) {
case 0: // pause
if(bBD_wasOn) { // set to run
printLine("RUN");
status = 1;
hidePauseText();
previousMS = millis(); // this function will restart counting after approximately 50 days
break;
}
if(bCD_wasOn3s) { // set to configClock
printLine("CONFIG CLOCK");
status = 2;
hidePauseText();
showConfigText(true);
configPosition = 0;
memcpy(configTimeHours, timeHours, sizeof(timeHours));
memcpy(configTimeMinutes, timeMinutes, sizeof(timeMinutes));
memcpy(configSpeed, speed, sizeof(speed));
configClockDigit[configPosition].show();
break;
}
if(bAB_wasOn3s) { // set to configChannels
printLine("CONFIG CHANNELS");
status = 3;
hidePauseText();
showConfigText(false);
// show row
// show cursor on channel of selected row
for(byte ch = 0; ch < 8; ch++) selectedOutSwitch(ch, true);
display.display();
break;
}
break;
case 1: // run
if(bAC_wasOn) { // set to pause
printLine("PAUSE");
status = 0;
showPauseText();
break;
}
break;
case 2: // configClock
if(bD_wasOn) { // move right
printLine("CLOCK: MOVE RIGHT");
configClockDigit[configPosition].hide();
configPosition++;
if(configPosition > 5) configPosition = 0;
configClockDigit[configPosition].show();
break;
}
if(bB_wasOn) { // increment
printLine("CLOCK: INCREMENT");
if(configPosition == 0) configClockDigit[0].increment(0, 2); // timeHours0
if(configPosition == 1) configClockDigit[1].increment(0, configClockDigit[0].value() == 2 ? 3 : 9); // timeHours1
if(configPosition == 2) configClockDigit[2].increment(0, 5); // timeMinutes0
if(configPosition == 3) configClockDigit[3].increment(0, 9); // timeMinutes1
if(configPosition == 4) configClockDigit[4].increment(configClockDigit[5].value() == 0 ? 1 : 0, 6); // speed0
if(configPosition == 5) configClockDigit[5].increment(configClockDigit[4].value() == 0 ? 1 : 0, 9); // speed1
break;
}
if(bAC_wasOn3s) { // cancel and set to pause
printLine("CANCEL CONFIG CLOCK");
status = 0;
hideConfigText(true);
configClockDigit[configPosition].hide();
showPauseText();
showTime();
break;
}
if(bBD_wasOn3s) { // confirm and set to pause
printLine("CONFIRM CONFIG CLOCK");
status = 0;
hideConfigText(true);
configClockDigit[configPosition].hide();
showPauseText();
memcpy(timeHours, configTimeHours, sizeof(configTimeHours));
memcpy(timeMinutes, configTimeMinutes, sizeof(configTimeMinutes));
memcpy(speed, configSpeed, sizeof(configSpeed));
storeTime();
storeSpeed();
showTime();
break;
}
break;
case 3: // configChannels
if(bA_wasOn) { // move up
printLine("CHANNELS: MOVE UP");
// hide cursor on channel of previous selected row
// increment and show another row
// show cursor on channel of selected row
break;
}
if(bC_wasOn) { // move down
printLine("CHANNELS: MOVE DOWN");
// hide cursor on channel of previous selected row
// decrement and show another row
// show cursor on channel of selected row
break;
}
if(bD_wasOn) { // move right
printLine("CHANNELS: MOVE RIGHT");
break;
}
if(bB_wasOn) { // increment
printLine("CHANNELS: INCREMENT");
break;
}
if(bAC_wasOn3s) { // cancel and set to pause
printLine("CANCEL CONFIG CHANNELS");
status = 0;
hideConfigText(false);
// hide row
// hide cursor on channel of selected row
showPauseText();
showTime();
break;
}
if(bBD_wasOn3s) { // confirm and set to pause
printLine("CONFIRM CONFIG CHANNELS");
status = 0;
hideConfigText(false);
// hide row
// hide cursor on channel of selected row
showPauseText();
// insert configOnOffSettings in selected onOffSettings[s]
storeOnOffSettings();
showTime();
break;
}
break;
}
}
// ----------------------------------------------------
void showTime() {
display.setTextColor(WHITE, BLACK);
display.setCursor(1, timeRowStartY);
display.print("Time:");
display.print(timeHours[0]);
display.print(timeHours[1]);
display.print("h");
display.print(timeMinutes[0]);
display.print(timeMinutes[1]);
display.print("m Speed:");
display.print(speed[0]);
display.print(speed[1]);
display.print("x");
bool on[8];
for(byte ch = 0; ch < 8; ch++) { // reset channels display row
byte x = ch * outSwitchWidth;
display.fillRect(x + outSwitchHBorder, outSwitchRowStartY, outSwitchWidth - 2 * outSwitchHBorder, outSwitchHeight, BLACK);
display.drawRect(x + outSwitchHBorder, outSwitchRowStartY, outSwitchWidth - 2 * outSwitchHBorder, outSwitchHeight, WHITE);
on[ch] = false;
}
int currentTime = (timeHours[0] * 10 + timeHours[1]) * 60 + timeMinutes[0] * 10 + timeMinutes[1];
for(byte i = 0; i < onOffSettingsSize; i++) {
if(onOffSettings[i].channel > 7) break;
int timeStart = (onOffSettings[i].timeHoursStart0 * 10 + onOffSettings[i].timeHoursStart1) * 60 + onOffSettings[i].timeMinutesStart0 * 10 + onOffSettings[i].timeMinutesStart1;
int timeEnd = (onOffSettings[i].timeHoursEnd0 * 10 + onOffSettings[i].timeHoursEnd1) * 60 + onOffSettings[i].timeMinutesEnd0 * 10 + onOffSettings[i].timeMinutesEnd1;
if(!on[onOffSettings[i].channel]) on[onOffSettings[i].channel] = currentTime >= timeStart && currentTime <= timeEnd;
outSwitch(onOffSettings[i].channel, on[onOffSettings[i].channel]);
}
display.display();
}
// ----------------------------------------------------
void showPauseText() {
display.fillRoundRect(44, 27, 39, 13, 2, WHITE);
display.setTextColor(BLACK, WHITE);
display.setCursor(49, 30);
display.print("PAUSE");
display.display();
}
// ----------------------------------------------------
void hidePauseText() {
display.fillRoundRect(44, 27, 39, 13, 2, BLACK);
display.display();
}
// ----------------------------------------------------
void showConfigText(bool clock) {
if(clock) {
display.fillRoundRect(23, 35, 81, 13, 2, WHITE);
display.setTextColor(BLACK, WHITE);
display.setCursor(28, 38);
display.print("CONFIG CLOCK");
} else {
display.fillRoundRect(14, 40, 99, 13, 2, WHITE);
display.setTextColor(BLACK, WHITE);
display.setCursor(19, 43);
display.print("CONFIG CHANNELS");
}
}
// ----------------------------------------------------
void hideConfigText(bool clock) {
if(clock) display.fillRoundRect(23, 35, 81, 13, 2, BLACK);
else display.fillRoundRect(14, 40, 99, 13, 2, BLACK);
}
// ----------------------------------------------------
void outSwitch(byte ch, bool on) { // 0-7 (A-H), true/false ... show on sceen and set output
byte x = ch * outSwitchWidth;
if(on) {
display.fillRect(x + outSwitchHBorder, outSwitchRowStartY, outSwitchWidth - 2 * outSwitchHBorder, outSwitchHeight, WHITE);
digitalWrite(relays[ch], HIGH); // On
} else {
display.drawRect(x + outSwitchHBorder, outSwitchRowStartY, outSwitchWidth - 2 * outSwitchHBorder, outSwitchHeight, WHITE);
digitalWrite(relays[ch], LOW); // Off
}
}
// ----------------------------------------------------
void selectedOutSwitch(byte ch, bool on) { // 0-7 (A-H), true/false
byte x = ch * outSwitchWidth;
if(on) display.fillRoundRect(x + outSwitchWidth / 2 - outSwitchHBorder / 2, outSwitchRowStartY + outSwitchHeight + outSwitchHBorder / 2, outSwitchHBorder, outSwitchHBorder, outSwitchHBorder / 2, WHITE);
else display.fillRoundRect(x + outSwitchWidth / 2 - outSwitchHBorder / 2, outSwitchRowStartY + outSwitchHeight + outSwitchHBorder / 2, outSwitchHBorder, outSwitchHBorder, outSwitchHBorder / 2, BLACK);
}
// ----------------------------------------------------
void storeTime() {
printLine("time to store: ", timeHours[0], timeHours[1], "h", timeMinutes[0], timeMinutes[1], "m");
byte offset = 0;
EEPROM.update(offset++, timeHours[0]);
EEPROM.update(offset++, timeHours[1]);
EEPROM.update(offset++, timeMinutes[0]);
EEPROM.update(offset++, timeMinutes[1]);
// EEPROM.commit(); // rpi 2040
}
// ----------------------------------------------------
void getStoredTime() {
byte offset = 0;
timeHours[0] = EEPROM.read(offset++);
timeHours[1] = EEPROM.read(offset++);
timeMinutes[0] = EEPROM.read(offset++);
timeMinutes[1] = EEPROM.read(offset++);
printLine("time read (raw): ", timeHours[0], timeHours[1], "h", timeMinutes[0], timeMinutes[1], "m");
// check validity
bool ok = true;
if(timeHours[0] > 2) ok = false;
if(timeHours[0] == 2 && timeHours[1] > 3) ok = false;
if(timeHours[1] > 9) ok = false;
if(timeMinutes[0] > 5) ok = false;
if(timeMinutes[1] > 9) ok = false;
if(!ok) { // not valid, reset
timeHours[0] = 0;
timeHours[1] = 0;
timeMinutes[0] = 0;
timeMinutes[1] = 0;
}
printLine("time read (fixed): ", timeHours[0], timeHours[1], "h", timeMinutes[0], timeMinutes[1], "m");
}
// ----------------------------------------------------
void storeSpeed() {
printLine("speed to store: ", speed[0], speed[1], "x");
byte offset = 4;
EEPROM.update(offset++, speed[0]);
EEPROM.update(offset++, speed[1]);
// EEPROM.commit(); // rpi 2040
}
// ----------------------------------------------------
void getStoredSpeed() {
byte offset = 4;
speed[0] = EEPROM.read(offset++);
speed[1] = EEPROM.read(offset++);
printLine("speed read (raw): ", speed[0], speed[1], "x");
// check validity
bool ok = true;
if(speed[0] > 6) ok = false;
if(speed[0] == 6 && speed[1] != 0) ok = false;
if(speed[0] == 0 && speed[1] < 1) ok = false;
if(speed[1] > 9) ok = false;
if(!ok) { // not valid, reset
speed[0] = 6;
speed[1] = 0;
}
printLine("speed read (fixed): ", speed[0], speed[1], "x");
}
// ----------------------------------------------------
void storeOnOffSettings() {
checkOnOffSettings();
sortOnOffSettings();
printLine("store OnOffSettings!");
byte offset = 6;
for(byte i = 0; i < onOffSettingsSize; i++) {
EEPROM.update(offset++, onOffSettings[i].channel);
EEPROM.update(offset++, onOffSettings[i].timeHoursStart0);
EEPROM.update(offset++, onOffSettings[i].timeHoursStart1);
EEPROM.update(offset++, onOffSettings[i].timeMinutesStart0);
EEPROM.update(offset++, onOffSettings[i].timeMinutesStart1);
EEPROM.update(offset++, onOffSettings[i].timeHoursEnd0);
EEPROM.update(offset++, onOffSettings[i].timeHoursEnd1);
EEPROM.update(offset++, onOffSettings[i].timeMinutesEnd0);
EEPROM.update(offset++, onOffSettings[i].timeMinutesEnd1);
}
// EEPROM.commit(); // rpi 2040
}
// ----------------------------------------------------
void getStoredOnOffSettings() {
byte offset = 6;
for(byte i = 0; i < onOffSettingsSize; i++) {
onOffSettings[i].channel = EEPROM.read(offset++);
onOffSettings[i].timeHoursStart0 = EEPROM.read(offset++);
onOffSettings[i].timeHoursStart1 = EEPROM.read(offset++);
onOffSettings[i].timeMinutesStart0 = EEPROM.read(offset++);
onOffSettings[i].timeMinutesStart1 = EEPROM.read(offset++);
onOffSettings[i].timeHoursEnd0 = EEPROM.read(offset++);
onOffSettings[i].timeHoursEnd1 = EEPROM.read(offset++);
onOffSettings[i].timeMinutesEnd0 = EEPROM.read(offset++);
onOffSettings[i].timeMinutesEnd1 = EEPROM.read(offset++);
// test - temp
onOffSettings[i].channel = random(15);
onOffSettings[i].timeHoursStart0= random(2);
onOffSettings[i].timeHoursStart1= random(10);
onOffSettings[i].timeMinutesStart0= random(6);
onOffSettings[i].timeMinutesStart1 = random(10);
onOffSettings[i].timeHoursEnd0 = random(2);
onOffSettings[i].timeHoursEnd1 = random(10);
onOffSettings[i].timeMinutesEnd0 = random(6);
onOffSettings[i].timeMinutesEnd1 = random(10);
}
printLine("OnOffSettings read (raw)!");
checkOnOffSettings();
sortOnOffSettings();
}
// ----------------------------------------------------
void sortOnOffSettings() {
printLine("Before sort ...");
for(byte i = 0; i < onOffSettingsSize; i++) printLine("\t", i, ") [", onOffSettings[i].channel, "] ", onOffSettings[i].timeHoursStart0, onOffSettings[i].timeHoursStart1, "h", onOffSettings[i].timeMinutesStart0, onOffSettings[i].timeMinutesStart1, "m ", onOffSettings[i].timeHoursEnd0, onOffSettings[i].timeHoursEnd1, "h", onOffSettings[i].timeMinutesEnd0, onOffSettings[i].timeMinutesEnd1, "m");
// sort by channel, by timeStart and by timeEnd
for(byte i = 0; i < onOffSettingsSize - 1; i++) {
for(byte j = 0; j < onOffSettingsSize - i - 1; j++) {
unsigned long sortA = onOffSettings[j].channel * 100000000 + onOffSettings[j].timeHoursStart0 * 10000000 + onOffSettings[j].timeHoursStart1 * 1000000 + onOffSettings[j].timeMinutesStart0 * 100000 + onOffSettings[j].timeMinutesStart1 * 10000 + onOffSettings[j].timeHoursEnd0 * 1000 + onOffSettings[j].timeHoursEnd1 * 100 + onOffSettings[j].timeMinutesEnd0 * 10 + onOffSettings[j].timeMinutesEnd1;
unsigned long sortB = onOffSettings[j + 1].channel * 100000000 + onOffSettings[j + 1].timeHoursStart0 * 10000000 + onOffSettings[j + 1].timeHoursStart1 * 1000000 + onOffSettings[j + 1].timeMinutesStart0 * 100000 + onOffSettings[j + 1].timeMinutesStart1 * 10000 + onOffSettings[j + 1].timeHoursEnd0 * 1000 + onOffSettings[j + 1].timeHoursEnd1 * 100 + onOffSettings[j + 1].timeMinutesEnd0 * 10 + onOffSettings[j + 1].timeMinutesEnd1;
if(sortA > sortB) {
OnOffSettings temp = onOffSettings[j];
onOffSettings[j] = onOffSettings[j + 1];
onOffSettings[j + 1] = temp;
}
}
}
printLine("After sort ...");
for(byte i = 0; i < onOffSettingsSize; i++) printLine("\t", i, ") [", onOffSettings[i].channel, "] ", onOffSettings[i].timeHoursStart0, onOffSettings[i].timeHoursStart1, "h", onOffSettings[i].timeMinutesStart0, onOffSettings[i].timeMinutesStart1, "m ", onOffSettings[i].timeHoursEnd0, onOffSettings[i].timeHoursEnd1, "h", onOffSettings[i].timeMinutesEnd0, onOffSettings[i].timeMinutesEnd1, "m");
}
// ----------------------------------------------------
void checkOnOffSettings() {
// check validity
for(byte i = 0; i < onOffSettingsSize; i++) {
bool ok = true;
if(onOffSettings[i].channel > 7) ok = false;
if(onOffSettings[i].timeHoursStart0 > 2) ok = false;
if(onOffSettings[i].timeHoursStart0 == 2 && onOffSettings[i].timeHoursStart1 > 3) ok = false;
if(onOffSettings[i].timeHoursStart1 > 9) ok = false;
if(onOffSettings[i].timeMinutesStart0 > 5) ok = false;
if(onOffSettings[i].timeMinutesStart1 > 9) ok = false;
if(onOffSettings[i].timeHoursEnd0 > 2) ok = false;
if(onOffSettings[i].timeHoursEnd0 == 2 && onOffSettings[i].timeHoursEnd1 > 3) ok = false;
if(onOffSettings[i].timeHoursEnd1 > 9) ok = false;
if(onOffSettings[i].timeMinutesEnd0 > 5) ok = false;
if(onOffSettings[i].timeMinutesEnd1 > 9) ok = false;
if(ok) {
int timeStart = (onOffSettings[i].timeHoursStart0 * 10 + onOffSettings[i].timeHoursStart1) * 60 + onOffSettings[i].timeMinutesStart0 * 10 + onOffSettings[i].timeMinutesStart1;
int timeEnd = (onOffSettings[i].timeHoursEnd0 * 10 + onOffSettings[i].timeHoursEnd1) * 60 + onOffSettings[i].timeMinutesEnd0 * 10 + onOffSettings[i].timeMinutesEnd1;
ok = timeStart <= timeEnd;
}
if(!ok) { // not valid, reset
onOffSettings[i].channel = 8; // invalid
onOffSettings[i].timeHoursStart0 = 0;
onOffSettings[i].timeHoursStart1 = 0;
onOffSettings[i].timeMinutesStart0 = 0;
onOffSettings[i].timeMinutesStart1 = 0;
onOffSettings[i].timeHoursEnd0 = 0;
onOffSettings[i].timeHoursEnd1 = 0;
onOffSettings[i].timeMinutesEnd0 = 0;
onOffSettings[i].timeMinutesEnd1 = 0;
}
}
}
// ----------------------------------------------------
void processAtPowerDown() { // when power down, save current status
storeTime();
}