/*
Proyecto: Temporizador con selección de 3 tiempos y control de servo
Plataforma: ATtiny85 / Digispark
Resumen de funcionamiento:
--------------------------
- Un botón permite seleccionar uno de tres tiempos (L1, L2 y L1+L2) mediante pulsaciones cortas.
- Con una pulsación larga, se inicia el conteo del tiempo seleccionado.
- Al iniciar el conteo, todos los indicadores (LED1, LED2 y NeoPixel) parpadean simultáneamente durante un aviso de 2 segundos.
- Durante el conteo, los LEDs parpadean de forma alternada y el NeoPixel parpadea con el LED1 para indicar que el temporizador está activo.
- Al finalizar el tiempo, el servo se mueve a 90° durante 1 segundo y luego regresa a su posición inicial (0°.
- Luego de eso, el NeoPixel se pone en blanco y los LEDs tradicionales se apagan. El sistema vuelve al estado inicial, esperando una nueva selección.
Esquema de conexión:
--------------------
+-------------------------+
| |
| ATtiny85/Digispark |
| |
+-------------------------+
| | | | |
| | | | |
Pin0 Pin1 Pin2 Pin3 Pin4
| | | | |
[Botón] [LED1] [LED2] [Servo] [NeoPixel]
| | | | |
GND GND GND VCC GND
|
|
[Servo Motor]
|
[GND]
Conexiones:
-----------
- Pin 0 (PB0) → Botón con resistencia pull-up interna (conectado a GND).
- Pin 1 (PB1) → LED1 con resistencia limitadora (220–470Ω) a GND.
- Pin 2 (PB2) → LED2 con resistencia limitadora (220–470Ω) a GND.
- Pin 3 (PB3) → Señal de control del servo (alimentar con 5V).
- Pin 4 (PB4) → Pin de datos del NeoPixel.
- Servo y NeoPixel → Alimentar con una fuente de 5V externa si es necesario, conectando su GND al GND del ATtiny85.
Notas:
------
- El botón usa la resistencia pull-up interna del microcontrolador.
- El servo necesita una fuente estable de 5V.
- El sistema es compatible con Wokwi para emulación.
*/
#include <Adafruit_SoftServo.h>
#include <Adafruit_NeoPixel.h>
#define LED_BUTTON_PIN 0
#define SERVO_PIN 3
#define LED1_PIN 1
#define LED2_PIN 2
#define NEOPIXEL_PIN 4
#define NUMPIXELS 1
#define DEBOUNCE_DELAY 200
#define BLINK_INTERVAL 150
#define LONG_PRESS_TIME 1000
#define START_BLINK_COUNT 5
#define START_BLINK_INTERVAL 150
// Tres tiempos correspondientes a L1, L2 y L1+L2
int led_times[] = {1000, 5000, 10000};
int current_mode = 0; // 0: L1, 1: L2, 2: L1+L2
int current_time = 0;
unsigned long last_short_press_time = 0;
Adafruit_SoftServo myServo;
Adafruit_NeoPixel neoPixel(NUMPIXELS, NEOPIXEL_PIN, NEO_GRB + NEO_KHZ800);
int initial_position = 0;
int final_position = 90;
bool button_was_pressed = false;
unsigned long button_press_start = 0;
bool counting_active = false;
unsigned long counting_start_time = 0;
bool servo_moved = false;
unsigned long servo_move_time = 0;
bool start_blink_active = false;
unsigned long start_blink_start_time = 0;
// Nueva función para establecer el color del NeoPixel según el modo
void setNeoPixelModeColor(int mode) {
if (mode == 0) { // L1 - Amarillo
neoPixel.setPixelColor(0, neoPixel.Color(255, 255, 0));
} else if (mode == 1) { // L2 - Naranja
neoPixel.setPixelColor(0, neoPixel.Color(255, 128, 0));
} else if (mode == 2) { // L1+L2 - Rojo
neoPixel.setPixelColor(0, neoPixel.Color(255, 0, 0));
}
neoPixel.show();
}
// Función que actualiza los LEDs para mostrar el modo actual
void setRegularLEDsForMode(int mode) {
digitalWrite(LED1_PIN, LOW);
digitalWrite(LED2_PIN, LOW);
if (mode == 0) {
digitalWrite(LED1_PIN, HIGH);
} else if (mode == 1) {
digitalWrite(LED2_PIN, HIGH);
} else if (mode == 2) {
digitalWrite(LED1_PIN, HIGH);
digitalWrite(LED2_PIN, HIGH);
}
}
void setup() {
pinMode(LED_BUTTON_PIN, INPUT_PULLUP);
pinMode(LED1_PIN, OUTPUT);
pinMode(LED2_PIN, OUTPUT);
// Inicializar NeoPixel y apagarlo
neoPixel.begin();
neoPixel.show();
// Establecer el estado inicial de los indicadores
setRegularLEDsForMode(current_mode);
setNeoPixelModeColor(current_mode);
current_time = led_times[current_mode];
myServo.attach(SERVO_PIN);
myServo.write(initial_position);
}
void loop() {
unsigned long now = millis();
bool button_state = digitalRead(LED_BUTTON_PIN) == LOW;
// --- Detectar pulsación corta o larga ---
if (button_state) {
if (!button_was_pressed) {
button_press_start = now;
button_was_pressed = true;
}
} else {
if (button_was_pressed) {
unsigned long press_duration = now - button_press_start;
button_was_pressed = false;
if (press_duration < LONG_PRESS_TIME && (now - last_short_press_time) > DEBOUNCE_DELAY) {
if (!counting_active && !start_blink_active) {
last_short_press_time = now;
current_mode = (current_mode + 1) % 3;
// Actualizar el estado de los indicadores para el nuevo modo
setRegularLEDsForMode(current_mode);
setNeoPixelModeColor(current_mode);
current_time = led_times[current_mode];
}
} else if (press_duration >= LONG_PRESS_TIME && !counting_active && !start_blink_active) {
start_blink_active = true;
start_blink_start_time = now;
}
}
}
// --- Aviso inicio: 5 destellos simultáneos ---
if (start_blink_active) {
unsigned long elapsed = now - start_blink_start_time;
int total_blink_time = START_BLINK_COUNT * 2 * START_BLINK_INTERVAL;
if (elapsed >= total_blink_time) {
start_blink_active = false;
counting_active = true;
counting_start_time = now;
servo_moved = false;
// Apaga los LEDs y el NeoPixel al terminar el aviso
digitalWrite(LED1_PIN, LOW);
digitalWrite(LED2_PIN, LOW);
neoPixel.clear();
neoPixel.show();
} else {
int phase = (elapsed / START_BLINK_INTERVAL) % 2;
digitalWrite(LED1_PIN, phase ? HIGH : LOW);
digitalWrite(LED2_PIN, phase ? HIGH : LOW);
if (phase) {
setNeoPixelModeColor(current_mode); // Enciende con el color del modo
} else {
neoPixel.clear(); // Apaga el NeoPixel
neoPixel.show();
}
}
}
// --- Conteo con parpadeo secuencial entre LED1 y LED2 y NeoPixel ---
if (counting_active) {
unsigned long elapsed = now - counting_start_time;
bool led1_on = (elapsed / BLINK_INTERVAL) % 2 == 0;
digitalWrite(LED1_PIN, led1_on ? HIGH : LOW);
digitalWrite(LED2_PIN, led1_on ? LOW : HIGH);
// El NeoPixel parpadea con el LED1 en el color seleccionado
if (led1_on) {
setNeoPixelModeColor(current_mode); // Enciende NeoPixel
} else {
neoPixel.clear(); // Apaga NeoPixel
neoPixel.show();
}
if (!servo_moved && elapsed >= current_time) {
myServo.write(final_position);
servo_move_time = now;
servo_moved = true;
}
if (servo_moved && (now - servo_move_time >= 1000)) {
myServo.write(initial_position);
counting_active = false;
servo_moved = false;
// Al finalizar el ciclo, apaga los LEDs tradicionales y el NeoPixel se pone en blanco
digitalWrite(LED1_PIN, LOW);
digitalWrite(LED2_PIN, LOW);
neoPixel.setPixelColor(0, neoPixel.Color(255, 255, 255)); // Color blanco
neoPixel.show();
}
}
// Es necesario llamar a refresh para que el servomotor funcione con SoftServo
myServo.refresh();
}