/**
Circuit Connections
-------------------
+---------+--------------------+
| Arduino | Connected to |
+---------+--------------------+
| D0 | N/C (Arduino Rx) |
| D1 | N/C (Arduino Tx) |
| D2 | Rotary Encoder DT |
| D3 | Rotary Encoder CLK |
| D4 | Rotary Encoder SW |
| D5 | BUZZER + |
| D6 | RELAY IN |
| D7 | N/C |
| D8 | N/C |
| D9 | Servo Motor |
| D10 | N/C |
| D11 | N/C |
| D12 | N/C |
| D13 | N/C |
| A0 | N/C |
| A1 | N/C |
| A2 | N/C |
| A3 | N/C |
| A4 | OLED SDA |
| A5 | OLED SCL |
+---------+--------------------+
/**************************************************************************
Nombre del Proyecto: TIMER PROGRAMABLE + RELAY
Versión: Version FINAL
Descripción:
AGOSTO 10 2024
-Permite configurar los minutos o los segundos girando el encoder
para pasar de minutos a segundos, tan solo se debe presionar una vez
el botón.
-Permite detener el conteo y reconfigurar los valores mediante un doble
click en el boton, Sin perturbar la carga conectada al relay
Este programa es software libre; se puede redistribuir y/o modificar
bajo los términos de la Licencia Pública General GNU publicada por
la Free Software Foundation; ya sea la versión 2 de la Licencia, o
(a su elección) cualquier versión posterior.
Este programa se distribuye con la esperanza de que sea útil,
pero SIN NINGUNA GARANTÍA; incluso sin la garantía implícita de
COMERCIALIZACIÓN o IDONEIDAD PARA UN PROPÓSITO PARTICULAR. Ver el
GNU General Public License para más detalles.
Deberías haber recibido una copia de la Licencia Pública General GNU
junto a este programa; si no, escriba a la Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, EE. UU.
Se puede encontrar una copia de la Licencia Pública General GNU en
<http://www.gnu.org/licenses/>.
**************************************************************************/
#include <Encoder.h>
#include <U8g2lib.h>
// Pines del encoder
const int pinA = 2;
const int pinB = 3;
const int BUTTON_PIN = 4;
#define BUZZER_PIN 5
#define RELAY_PIN 6
const int tones[] = {261, 277, 294, 311, 330, 349, 370, 392, 415, 440, 466, 494, 550, 650, 900, 900, 900};
const int countTones = 17;
// Inicializa el encoder
Encoder myEnc(pinA, pinB);
// Variables del temporizador
bool cambio = false;
long oldPosition = -999;
bool buttonPressed = false;
bool timerRunning = false;
long timerSeconds = 0;
long timerMinutes = 0;
unsigned long startTime = 0; // Variable para almacenar el tiempo de inicio
unsigned long lastUpdateTime = 0; // Variable para almacenar la última actualización del temporizador
bool configuringMinutes = true; // Indica si se está configurando los minutos o segundos
int buttonPressCount = 0;
unsigned long lastButtonPressTime = 0;
bool isEditing = false; // Indica si se está editando el tiempo
// Inicializa la pantalla OLED
U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, U8X8_PIN_NONE);
void setup() {
pinMode(BUTTON_PIN, INPUT_PULLUP);
pinMode(RELAY_PIN, OUTPUT);
u8g2.begin();
u8g2.setContrast(255); // Contraste máximo
u8g2.setFontDirection(2);
u8g2.setFont(u8g2_font_logisoso28_tf); // Fuente más grande
}
void loop() {
if (digitalRead(BUTTON_PIN) == LOW && !buttonPressed) {
delay(50); // debounce delay
buttonPressed = true;
buttonPressCount++;
lastButtonPressTime = millis();
}
if (digitalRead(BUTTON_PIN) == HIGH) {
buttonPressed = false;
}
// Cambia entre minutos y segundos con un solo click
if (buttonPressCount == 1 && millis() - lastButtonPressTime > 250) {
configuringMinutes = !configuringMinutes;
buttonPressCount = 0;
isEditing = true;
}
// Inicia el temporizador con doble click
if (buttonPressCount == 2) {
buttonPressCount = 0;
if (!timerRunning) {
digitalWrite(RELAY_PIN, HIGH);
startTime = millis();
lastUpdateTime = millis();
timerRunning = true;
} else {
timerRunning = false;
}
isEditing = false;
}
if (timerRunning) {
updateTimer(); // Lógica para actualizar el temporizador
} else {
adjustTime(); // Lógica para ajustar el tiempo con el encoder
}
displayTime(); // Lógica para mostrar el tiempo
}
void adjustTime() {
long newPosition = myEnc.read();
long diff = newPosition - oldPosition;
if (diff != 0) {
isEditing = true;
if (diff > 0) { // Giro en sentido horario
if (configuringMinutes) {
if (timerMinutes < 59) {
timerMinutes++;
}
} else {
if (timerSeconds < 59) {
timerSeconds++;
}
}
} else { // Giro en sentido antihorario
if (configuringMinutes) {
if (timerMinutes > 0) {
timerMinutes--;
}
} else {
if (timerSeconds > 0) {
timerSeconds--;
}
}
}
oldPosition = newPosition;
}
}
void updateTimer() {
unsigned long currentTime = millis();
if (currentTime - lastUpdateTime >= 1000) { // Verificar si ha pasado un segundo
lastUpdateTime += 1000;
if (timerSeconds > 0) {
timerSeconds--;
} else {
if (timerMinutes > 0) {
timerMinutes--;
timerSeconds = 59;
} else {
timerRunning = false;
timerMinutes = 0;
timerSeconds = 0;
}
}
}
if ((timerMinutes == 0) && (timerSeconds == 0)) {
digitalWrite(RELAY_PIN, LOW);
tonos();
}
u8g2.sendBuffer(); // Envía el contenido al display
u8g2.setDrawColor(0); // Fondo negro
u8g2.drawBox(15, 11, 90, 35);
}
void tonos() {
for (int iTone = 0; iTone < countTones; iTone++) {
tone(BUZZER_PIN, tones[iTone]);
delay(100);
}
noTone(BUZZER_PIN);
}
void displayTime() {
// Alternar el estado de parpadeo
static unsigned long lastBlinkTime = 0;
static bool blinkState = true;
unsigned long currentTime = millis();
// Cambia el estado de parpadeo cada medio segundo
if (currentTime - lastBlinkTime >= 500) {
lastBlinkTime = currentTime;
blinkState = !blinkState;
}
//u8g2.clearBuffer(); // Limpia el buffer de la pantalla
if (isEditing) {
if (blinkState) {
// Mostrar el texto en blanco sobre fondo negro
u8g2.setDrawColor(1); // Texto blanco
if (timerMinutes>9){
u8g2.setCursor(108, 15);
}else{
u8g2.setCursor(90, 15);
}
if (configuringMinutes) {
// Mostrar minutos y segundos
u8g2.print(timerMinutes);
u8g2.print(":");
if (timerSeconds < 10) {
u8g2.print("0");
}
u8g2.print(timerSeconds);
} else {
// Mostrar minutos y segundos
u8g2.print(timerMinutes);
u8g2.print(":");
if (timerSeconds < 10) {
u8g2.print("0");
}
u8g2.print(timerSeconds);
}
}
} else {
// Muestra los minutos y segundos en blanco sobre fondo negro
u8g2.setDrawColor(1); // Texto blanco
if (timerMinutes>9){
u8g2.setCursor(108, 15);
}else{
u8g2.setCursor(90, 15);
}
u8g2.print(timerMinutes);
u8g2.print(":");
if (timerSeconds < 10) {
u8g2.print("0");
}
u8g2.print(timerSeconds);
}
// Actualiza la pantalla con el contenido del buffer
u8g2.sendBuffer();
// Si estamos en modo de edición y el estado de parpadeo está apagado,
// limpia solo la parte de la pantalla que corresponde a los dígitos en edición
if (isEditing && !blinkState) {
//
if (configuringMinutes) {
// Borra solo la parte correspondiente a los minutos
u8g2.setDrawColor(0); // Fondo negro
u8g2.drawBox(75, 10, 127, 127); // Ajusta las coordenadas y tamaño según el área a borrar
} else {
// Borra solo la parte correspondiente a los segundos
u8g2.setDrawColor(0); // Fondo negro
u8g2.drawBox(10, 10, 53, 60); // Ajusta las coordenadas y tamaño según el área a borrar
}
borde();
u8g2.sendBuffer(); // Actualiza la pantalla con el contenido del buffer
}
}
void borde(){
// Coordenadas y dimensiones del temporizador
int x = 15; // Posición X del temporizador
int y = 10; // Posición Y del temporizador
int width = 100; // Ancho del temporizador
int height = 40; // Alto del temporizador
int radius = 5; // Radio para las esquinas redondeadas
u8g2.setDrawColor(1); // Fondo blanco
// Dibuja un rectángulo con esquinas redondeadas alrededor del temporizador
u8g2.drawRFrame(x - 2, y - 2, width + 4, height + 4, radius);
// Envía el contenido al display
u8g2.sendBuffer();
}