/*
File : Universal Timer R0.ino
Author : leveletech.com / wa 081214584114
Date : 2024 04 27
Description : Reverse engineering popular universal timer
*/
//--------------------------------- Include Libraries-----------------------------------------------
#include <TM1637Display.h>
//--------------------------------- Include Libraries-----------------------------------------------
//--------------------------------- Hardware Setting------------------------------------------------
//--------------------------------- Hardware Setting------------------------------------------------
//--------------------------------- Define and Constant---------------------------------------------
// Assign push button pins
const int buttonPins[5] = {4, 16, 17, 5, 18}; // Array of pins for buttons
const int numButtons = 5; // Number of buttons
bool lastButtonState[numButtons] = {false, false, false, false, false};
// Assign relay pins
const int relayPins[1] = {25}; // Array of pins for relays
const int numRelays = 1; // Number of relays
const int numTrigger = 5;
const int numTimer = 9;
const int numCounter = 2;
const int numSPWM = 2;
const int CLK = 26;
const int DIO = 27;
const uint8_t mSec[] = {SEG_G, };
const uint8_t Sec[] = {SEG_D, };
const uint8_t Min[] = {SEG_D | SEG_G | SEG_A, };
const uint8_t P11[] = {//P1.1
SEG_A | SEG_B | SEG_G | SEG_F | SEG_E, // P
SEG_F | SEG_E | SEG_C, // 1.
SEG_F | SEG_E
};
const uint8_t P12[] = {//P1.2
SEG_A | SEG_B | SEG_G | SEG_F | SEG_E, // P
SEG_F | SEG_E | SEG_C, // 1.
SEG_A | SEG_B | SEG_G | SEG_E | SEG_D// 2
};
const uint8_t P13[] = {//P1.3
SEG_A | SEG_B | SEG_G | SEG_F | SEG_E, // P
SEG_F | SEG_E | SEG_C, // 1.
SEG_A | SEG_B | SEG_G | SEG_C | SEG_D// 3
};
const uint8_t P2[] = {//P-2
SEG_A | SEG_B | SEG_G | SEG_F | SEG_E, // P
SEG_G, // -
SEG_A | SEG_B | SEG_G | SEG_E | SEG_D// 2
};
const uint8_t P31[] = {//P3.1
SEG_A | SEG_B | SEG_G | SEG_C | SEG_D, // P
SEG_F | SEG_E | SEG_C, // 3.
SEG_F | SEG_E
};
const uint8_t P32[] = {//P3.2
SEG_A | SEG_B | SEG_G | SEG_C | SEG_D, // P
SEG_F | SEG_E | SEG_C, // 3.
SEG_A | SEG_B | SEG_G | SEG_E | SEG_D// 2
};
const uint8_t P4[] = {//P-4
SEG_A | SEG_B | SEG_G | SEG_F | SEG_E, // P
SEG_G, // -
SEG_F | SEG_G | SEG_B | SEG_C// 2
};
const uint8_t OP[] = {//OP
SEG_A | SEG_B | SEG_C | SEG_D | SEG_E | SEG_F, // O
SEG_A | SEG_B | SEG_G | SEG_E | SEG_F // P
};
const uint8_t CL[] = {//CL
SEG_A | SEG_F | SEG_E | SEG_D, // C
SEG_F | SEG_E | SEG_D // L
};
const uint8_t LOP[] = {//LOP
SEG_F | SEG_E | SEG_D, // L
SEG_A | SEG_B | SEG_C | SEG_D | SEG_E | SEG_F, // O
SEG_A | SEG_B | SEG_G | SEG_E | SEG_F // P
};
const uint8_t OFF[] = {//OFF
SEG_A | SEG_B | SEG_C | SEG_D | SEG_E | SEG_F, // O
SEG_A | SEG_F | SEG_G | SEG_E, // F
SEG_A | SEG_F | SEG_G | SEG_E // F
};
const uint8_t ON[] = {//OFF
SEG_A | SEG_B | SEG_C | SEG_D | SEG_E | SEG_F, // O
SEG_A | SEG_B | SEG_C | SEG_E | SEG_F // N
};
const uint8_t nolimit[] = { //---
SEG_G, // -
SEG_G, // -
SEG_G // -
};
//--------------------------------- Define and Constant---------------------------------------------
//--------------------------------- User Define Data Type-------------------------------------------
struct timer_data {
bool IN;
bool Q;
bool TT;
bool BP1; //button pressed
bool BP2; //button pressed
unsigned long PT; //preset time
unsigned long ET; //elapsed time
unsigned long TN;//time now
unsigned long TCD; // additional timer countdown
};
struct counter_data {
bool IN;
bool R; //Reset
bool Q;
bool BP; //button pressed
int PV; //preset value
int CV; //current value
};
struct spwm_data {
bool IN;
bool Q;
bool BP; //button pressed
bool Qoff;
unsigned long PT_on; //preset time for ON state
unsigned long PT_off; //preset time for OFF state
unsigned long ET; //elapsed time
unsigned long TN;//time now
unsigned long TCD; // additional timer countdown
};
//--------------------------------- User Define Data Type-------------------------------------------
//----------------------------------Global Variable-------------------------------------------------
bool S[6] = {false, false, false, false, false, false};
bool S_P[6] = {false, false, false, false, false, false};
bool R[2] = {false, false};
bool trigger[numTrigger] = {false, false, false, false, false};
int ActiveMode = 0;
/*
0 : Normal Operation
1 : Setting Mode
*/
int SelectionMode = 0;
/*
0 : P1.1
1 : P1.2
2 : P1.3
3 : P2
4 : P3.1
5 : P3.2
6 : P4
*/
unsigned long lastDebounceTime[numButtons]; // Array to store the last debounce time for each button
const unsigned long debounceDelay = 2; // Debounce delay in milliseconds
TM1637Display display(CLK, DIO);
timer_data T[numTimer]; //TON
counter_data CTU_data[numCounter];
spwm_data spwm_dat[numSPWM];
//----------------------------------Global Variable-------------------------------------------------
//----------------------------------User Defined Function Stage 1-----------------------------------
void DI_Update() {
for (int i = 0; i < numButtons; i++) {
// Read the current state of the button
int currentState = digitalRead(buttonPins[i]);
// Check if the button state has changed and debounce it
if (currentState != lastButtonState[i]) {
// Reset the debounce timer
lastDebounceTime[i] = millis();
}
// Check if enough time has passed to consider it a valid state change
if ((millis() - lastDebounceTime[i]) > debounceDelay) {
// If enough time has passed, update the state
S[i + 1] = currentState;
}
// Store the current state for the next iteration
lastButtonState[i] = currentState;
}
}
void DO_Update() {
for (int i = 1; i < numRelays + 1; i++) {
digitalWrite(relayPins[i - 1], R[i]);
}
}
void TON(timer_data & Var) { //Timer On delay function
if (Var.IN)
{
if (!Var.BP1)
{
Var.TN = millis();
Var.BP1 = true;
Var.TT = true;
}
if (!Var.Q)
{
Var.ET = (millis() - Var.TN);
Var.TCD = Var.PT - Var.ET;
if (Var.ET >= Var.PT) {
Var.Q = true;
Var.TT = false;
Var.TCD = 0;
}
}
} else
{
Var.Q = false;
Var.BP1 = false;
Var.ET = 0;
Var.TT = false;
Var.TCD = 0;
}
}
void CTU(counter_data & Var) { //Counter Up
if (Var.IN) {
if (!Var.BP && !Var.Q)
{
Var.CV++;
Var.BP = true;
if (Var.CV >= Var.PV) Var.Q = true;
}
} else {
Var.BP = false;
}
if (Var.R) {
Var.CV = 0;
Var.Q = false;
}
}
void spwm(spwm_data & Var) { // Software PWM or Timer ON OFF
if (Var.IN)
{
if (!Var.BP)
{
Var.TN = millis();
Var.BP = true;
Var.Q = true; // Initially turn ON the output
}
Var.ET = (millis() - Var.TN);
if (Var.Q) {
Var.TCD = Var.PT_off - Var.ET;
} else {
Var.TCD = Var.PT_on - Var.ET;
}
if (Var.Q && Var.ET >= Var.PT_on) {
Var.Q = false; // If ON time is over, turn OFF the output
Var.TN = millis(); // Reset the timer for the next state
Var.Qoff = true;
}
else if (!Var.Q && Var.ET >= Var.PT_off) {
Var.Q = true; // If OFF time is over, turn ON the output
Var.TN = millis(); // Reset the timer for the next state
Var.Qoff = false;
}
}
else
{
Var.Q = false; // If input is inactive, turn OFF the output
Var.BP = false; // Reset button pressed flag
Var.ET = 0; // Reset elapsed time
Var.Qoff = false;
Var.TCD = 0;
}
}
//----------------------------------User Defined Function Stage 1-----------------------------------
//----------------------------------User Defined Function Stage 2-----------------------------------
//each mode
void Mode0_Logic() { //P1.1
if (S[1]) trigger[0] = true;
T[0].IN = trigger[0]; TON(T[0]); R[1] = T[0].TT;
if (T[0].Q) trigger[0] = false;
}
void Mode1_Logic() { //P1.2
if (S[1]) trigger[0] = true;
if (!S[1] && T[1].TT) trigger[1] = true;
if (S[1] && trigger[1]) {
T[1].TN = millis(); //reset timer
trigger[1] = false;
}
T[1].IN = trigger[0]; TON(T[1]); R[1] = T[1].TT;
if (T[1].Q) trigger[0] = false;
}
void Mode2_Logic() { //P1.3
if (S[1] && !trigger[3]) trigger[0] = true;
if (!S[1] && T[2].TT) trigger[1] = true;
if (S[1] && trigger[1]) {
trigger[0] = false;
trigger[1] = false;
trigger[3] = true;
}
if (!S[1]) trigger[3] = false;
T[2].IN = trigger[0]; TON(T[2]); R[1] = T[2].TT;
if (T[2].Q) {
trigger[0] = false;
trigger[3] = false;
}
}
void Mode3_Logic() { //P2
if (S[1]) trigger[0] = true;
T[3].IN = trigger[0]; TON(T[3]);
T[4].IN = T[3].Q; TON(T[4]); R[1] = T[4].TT;
if (T[4].Q) trigger[0] = false;
}
void Mode4_Logic() { //P3.1
if (S[1] && !trigger[1]) {
trigger[0] = true;
trigger[1] = true;
}
spwm_dat[0].IN = trigger[0]; spwm(spwm_dat[0]); R[1] = spwm_dat[0].Q;
if (CTU_data[0].PV != 0) {
CTU_data[0].IN = spwm_dat[0].Qoff; CTU(CTU_data[0]);
if (CTU_data[0].Q) {
trigger[0] = false;
CTU_data[0].R = true; CTU(CTU_data[0]);
CTU_data[0].R = false;
}
}
if (!S[1]) trigger[1] = false;
}
void Mode5_Logic() { //P3.2
if (!trigger[1]) {
trigger[0] = true;
trigger[1] = true;
}
spwm_dat[1].IN = trigger[0]; spwm(spwm_dat[1]); R[1] = spwm_dat[1].Q;
if (CTU_data[1].PV != 0) {
CTU_data[1].IN = spwm_dat[1].Qoff; CTU(CTU_data[1]);
if (CTU_data[01].Q) {
trigger[0] = false;
CTU_data[1].R = true; CTU(CTU_data[10]);
CTU_data[1].R = false;
}
}
}
void Mode6_Logic() { //P4
if (S[1]) trigger[0] = true;
T[5].IN = trigger[0] && !S[1]; TON(T[5]); R[1] = T[5].TT || S[1];
if (T[5].Q) trigger[0] = false;
}
//main process
void MainProcess() {
switch (ActiveMode) {
case 0: //Normal Operation
switch (SelectionMode) {
case 0: //P1.1
Mode0_Logic();
UpdateLCD(T[0].ET);
break;
case 1: //P1.2
Mode1_Logic();
break;
case 2: //P1.3
Mode2_Logic();
break;
case 3: //P2
Mode3_Logic();
break;
case 4: //P3.1
Mode4_Logic();
break;
case 5: //P3.2
Mode5_Logic();
break;
case 6: //P4
Mode6_Logic();
break;
}
break;
case 1: //Setting Mode
break;
}
}
//display handler
void UpdateLCD(unsigned long time_ms) {
// Convert milliseconds to minutes, seconds, and 100 milliseconds
unsigned long minutes = time_ms / (1000 * 60);
unsigned long seconds = (time_ms / 1000) % 60;
unsigned long hundred_ms = (time_ms / 100) % 10;
Serial.print("Time remaining: ");
Serial.print(minutes);
Serial.print(" minute(s), ");
Serial.print(seconds);
Serial.print(" second(s), ");
Serial.print(hundred_ms);
Serial.println(" 100ms(s)");
//setSegments(segments[],length,position)
//showNumberDec(number,leading_zeros,length,position)
display.showNumberDec(seconds,false,2,1);
display.showNumberDec(hundred_ms,false,1,3);
//display.setSegments(OP, 3, 1);
}
//reset
void reset() {
for (int i = 0; i < numTrigger; i++) {
trigger[i] = false;
}
for (int i = 0; i < numTimer; i++) {
T[i].IN = false; TON(T[i]);
}
for (int i = 0; i < numCounter; i++) {
CTU_data[i].R = true; CTU(CTU_data[i]);
CTU_data[i].R = false;
}
for (int i = 0; i < numSPWM; i++) {
spwm_dat[i].IN = false; spwm(spwm_dat[i]);
}
}
//----------------------------------User Defined Function Stage 2-----------------------------------
//----------------------------------User Defined Function Stage 3-----------------------------------
//----------------------------------User Defined Function Stage 3-----------------------------------
//----------------------------------Testing Function------------------------------------------------
void printio() {
for (int i = 1; i < 6; i++) {
Serial.print(S[i]);
} Serial.println("");
}
void printioMode0() {
Serial.print(S[1]);
Serial.print(" ");
Serial.print(trigger[0]);
Serial.print(" ");
Serial.print(T[0].IN);
Serial.print(" ");
Serial.print(T[0].PT);
Serial.print(" ");
Serial.print(T[0].ET);
Serial.print(" ");
Serial.print(T[0].TCD);
Serial.print(" ");
Serial.print(T[0].Q);
Serial.println(" ");
}
void printio2Mode4() {
Serial.print(S[1]);
Serial.print(" ");
Serial.print(trigger[0]);
Serial.print(" ");
Serial.print(spwm_dat[0].IN);
Serial.print(" ");
Serial.print(spwm_dat[0].Q);
Serial.print(" ");
Serial.print(spwm_dat[0].Qoff);
Serial.print(" ");
Serial.print(CTU_data[0].CV);
Serial.print(" ");
Serial.print(CTU_data[0].Q);
Serial.println(" ");
}
void printio2Mode5() {
Serial.print(S[1]);
Serial.print(" ");
Serial.print(trigger[0]);
Serial.print(" ");
Serial.print(spwm_dat[1].IN);
Serial.print(" ");
Serial.print(spwm_dat[1].Q);
Serial.print(" ");
Serial.print(spwm_dat[1].Qoff);
Serial.print(" ");
Serial.print(CTU_data[1].PV);
Serial.print(" ");
Serial.print(CTU_data[1].CV);
Serial.print(" ");
Serial.print(CTU_data[1].Q);
Serial.println(" ");
}
//----------------------------------Testing Function------------------------------------------------
//--------------------------------- Setup----------------------------------------------
void setup() {
Serial.begin(9600);
//Pin Initialization
// Initialize push button pins as inputs
for (int i = 0; i < numButtons; i++) {
pinMode(buttonPins[i], INPUT); // Using internal pull-up resistors
}
// Initialize relay pins as outputs
for (int i = 0; i < numRelays; i++) {
pinMode(relayPins[i], OUTPUT);
digitalWrite(relayPins[i], LOW); // Turn off all relays initially
}
//Start Services
display.setBrightness(7); // set the brightness to 7 (0:dimmest, 7:brightest)
//Timer Initialization (dalam milisecond)
T[0].PT = 222222; //P1.1 TON
T[1].PT = 5000; //P1.2 TON
T[2].PT = 5000; //P1.3 TON
T[3].PT = 5000; //P2 1st OFF delay TON
T[4].PT = 5000; //P2 2nd ON duration TON
T[5].PT = 5000; //P4 TON
T[6].PT = 5000; //
T[7].PT = 5000; //
T[8].PT = 5000; //timer switch display
spwm_dat[0].PT_on = 1; // P3.1 ON duration
spwm_dat[0].PT_off = 1; // P3.1 OFF duration
spwm_dat[1].PT_on = 1; // P3.2 ON duration
spwm_dat[1].PT_off = 1; // P3.2 OFF duration
CTU_data[0].PV = 4; //P3.1 Loop 0-> infiniti
CTU_data[1].PV = 3; //P3.2 Loop 0-> infiniti
//testing
SelectionMode = 0;
}
//--------------------------------- Setup----------------------------------------------
//--------------------------------- Loop-----------------------------------------------
void loop() {
//Update Input
DI_Update();
//Process Update
MainProcess();
//Update Output
DO_Update();
//testing
//printioMode0();
}
//--------------------------------- Loop-----------------------------------------------
//--------------------------------- Note-----------------------------------------------
/*
*/
//--------------------------------- Note-----------------------------------------------