#include <TimerOne.h>
#include <digitalWriteFast.h>
#include <IRremote.h>
#define TRIAC_ON 0UL
#define TRIAC_OFF 10000UL
const uint8_t XZERO_PIN = 2;
const uint8_t TRIAC_PIN = 3;
const uint8_t IR_PIN = 4;
const uint16_t MAINS_FREQUENCY = 50;
const uint16_t DIMM_STEPS_PER_SECOND = 2 * MAINS_FREQUENCY;
const uint16_t MAX_BRIGHTNESS = 253;
const uint16_t MIN_BRIGHTNESS = 1;
//const uint32_t MAX_BRIGHT_PULSE_DELAY = (-1.5034e-10 * pow(MAX_BRIGHTNESS, 5) + 9.5843e-08 * pow(MAX_BRIGHTNESS, 4) - 2.2953e-05 * pow(MAX_BRIGHTNESS, 3) + 0.0025471 * pow(MAX_BRIGHTNESS, 2) - 0.14965 * MAX_BRIGHTNESS + 9.9846) * 1000UL;
//const uint32_t MIN_BRIGHT_PULSE_DELAY = (-1.5034e-10 * pow(MIN_BRIGHTNESS, 5) + 9.5843e-08 * pow(MIN_BRIGHTNESS, 4) - 2.2953e-05 * pow(MIN_BRIGHTNESS, 3) + 0.0025471 * pow(MIN_BRIGHTNESS, 2) - 0.14965 * MIN_BRIGHTNESS + 9.9846) * 1000UL;
const uint32_t MAX_BRIGHT_PULSE_DELAY = 9900 - (9900 - 200) / 255.0 * MAX_BRIGHTNESS;
const uint32_t MIN_BRIGHT_PULSE_DELAY = 9900 - (9900 - 200) / 255.0 * MIN_BRIGHTNESS;
const float STEP_WIDTH = (MAX_BRIGHTNESS - MIN_BRIGHTNESS + 1) / (float)DIMM_STEPS_PER_SECOND;
uint32_t brightPulseDelay[100];
uint16_t fadeinRampTimeSecs;
uint16_t fadeoutRampTimeSecs;
uint16_t intermissionSecs;
volatile bool zeroCross_flag = false;
volatile bool timerRunning = false;
volatile uint32_t dimmerPulseDelay = TRIAC_OFF;
enum fade_t {
fadein,
fadeout
};
enum remote_t {
ON,
OFF,
PLAY,
STOP
} remoteMode = OFF;
// ----------------------------------------------------------------------------
static void stop_timer() {
noInterrupts();
if (timerRunning) {
Timer1.stop();
timerRunning = false;
}
interrupts();
}
// ----------------------------------------------------------------------------
static void zeroCross_ISR() {
stop_timer(); // detener timer si esta corriendo
zeroCross_flag = true;
if (dimmerPulseDelay <= MAX_BRIGHT_PULSE_DELAY) { // brillo maximo?
digitalWriteFast(TRIAC_PIN, HIGH); // mantener cebado el triac
} else { // caso contrario
digitalWriteFast(TRIAC_PIN, LOW); // desactivar triac en cruce por cero
if (dimmerPulseDelay < MIN_BRIGHT_PULSE_DELAY) { // si brillo mayor a umbral de apagado
timerRunning = true;
Timer1.initialize(dimmerPulseDelay); // setear retrazo del pulso de encendido
}
}
}
// ----------------------------------------------------------------------------
static void timer_ISR() {
digitalWriteFast(TRIAC_PIN, HIGH);
stop_timer();
}
// ----------------------------------------------------------------------------
void blink_led(bool firstTime = false, uint32_t tOn = 500UL, uint32_t tOff = 500UL) {
static uint32_t oldTime;
static uint32_t interval;
static uint32_t timeOn;
static uint32_t timeOff;
static bool ledState = true;
if (firstTime) {
oldTime = millis();
ledState = true;
timeOn = tOn;
timeOff = tOff;
interval = timeOn;
digitalWriteFast(LED_BUILTIN, ledState);
} else if (millis() - oldTime >= interval) {
ledState = !ledState;
digitalWriteFast(LED_BUILTIN, ledState ? HIGH : LOW);
interval = ledState ? timeOn : timeOff;
oldTime += ledState ? timeOff : timeOn;
}
}
// ----------------------------------------------------------------------------
void delay_with_blink(uint32_t blink_time) {
uint32_t t0 = millis();
while (millis() - t0 <= blink_time) {
blink_led();
// delay(1);
check_remote();
}
}
// ----------------------------------------------------------------------------
void long_delay_with_blink(uint16_t blink_time_secs) {
blink_led(true, 100UL, 900UL);
// uint32_t t0 = millis();
delay_with_blink(blink_time_secs * 1000UL);
digitalWriteFast(LED_BUILTIN, LOW);
}
// ----------------------------------------------------------------------------
void set_triac_state(uint32_t value) {
while (!zeroCross_flag) blink_led();
noInterrupts();
dimmerPulseDelay = value; //MIN_DIM_VALUE;
zeroCross_flag = false;
interrupts();
}
// ----------------------------------------------------------------------------
void fade(uint32_t fadeRampTime, fade_t direction) {
blink_led(true, 200UL, 300UL);
for (int i = 0; i < DIMM_STEPS_PER_SECOND; i++) {
for (int j = 0; j < fadeRampTime; j++) {
while (!zeroCross_flag) blink_led();
noInterrupts();
if (direction == fadein) {
dimmerPulseDelay = brightPulseDelay[i];
} else {
dimmerPulseDelay = brightPulseDelay[DIMM_STEPS_PER_SECOND - i];
}
zeroCross_flag = false;
interrupts();
}
}
if (direction == fadein) {
set_triac_state(TRIAC_ON);
} else {
set_triac_state(TRIAC_OFF);
}
}
// ----------------------------------------------------------------------------
bool check_remote(){
bool temp = false;
if (IrReceiver.decode()) {
Serial.println(IrReceiver.decodedIRData.command, HEX);
IrReceiver.printIRResultShort(&Serial);
temp = true;
switch (IrReceiver.decodedIRData.command) {
case 0xA2: if (remoteMode == STOP || remoteMode == ON) {
remoteMode = OFF;
} else if (remoteMode == OFF) {
remoteMode = ON;
}
Serial.println(remoteMode);
break;
case 0xA8: if (remoteMode == STOP || remoteMode == ON) {
remoteMode = PLAY;
} else if (remoteMode == PLAY) {
remoteMode = STOP;
}
break;
// case 0xE0: remoteMode = STOP; break;
// case 0x22: remoteMode = OFF; break;
}
IrReceiver.resume(); // Enable receiving of the next value
}
return temp;
}
// ----------------------------------------------------------------------------
void setup() {
Serial.begin(115200);
Serial.println(STEP_WIDTH);
Serial.println(9900 - (9900 - 200) / 255.0 * 253);
Serial.println( 9900-(9900 - 200) / 255.0 * 1);
//while(1);
float bright;
for (int i = 0; i < DIMM_STEPS_PER_SECOND; i++) {
bright = i * STEP_WIDTH + MIN_BRIGHTNESS; // * STEP_WIDTH;
// brightPulseDelay[i] = (-1.5034e-10 * pow(bright, 5) + 9.5843e-08 * pow(bright, 4) - 2.2953e-05 * pow(bright, 3) + 0.0025471 * pow(bright, 2) - 0.14965 * bright + 9.9846) * 1000UL;
brightPulseDelay[i] = 9900 - (9900 - 200) / 255.0 * bright;
Serial.print(i); Serial.print(": \t");
Serial.println(brightPulseDelay[i]);
}
//while(1);
pinModeFast(TRIAC_PIN, OUTPUT);
digitalWriteFast(TRIAC_PIN, LOW);
pinModeFast(LED_BUILTIN, OUTPUT);
digitalWriteFast(LED_BUILTIN, LOW);
IrReceiver.begin(IR_PIN);
delay(1000);
// Cargar valores por serial
fadeinRampTimeSecs = 5; // secs
fadeoutRampTimeSecs = 5; // secs
intermissionSecs = 20; // secs
intermissionSecs -= (fadeinRampTimeSecs + fadeoutRampTimeSecs);
while(remoteMode != ON) {
check_remote();
}
Timer1.initialize(30000000);
Timer1.attachInterrupt(timer_ISR);
attachInterrupt(digitalPinToInterrupt(XZERO_PIN), zeroCross_ISR, RISING);
stop_timer();
set_triac_state(TRIAC_OFF);
// Serial.println("Iniciando...");
fade(1, fadein);
long_delay_with_blink(10);
while(!check_remote());
}
// ----------------------------------------------------------------------------
void loop() {
// Serial.println("Apagando luces...");
fade(fadeoutRampTimeSecs, fadeout);
// Serial.println("Proyectando...");
long_delay_with_blink(5); // tiempo proyeccion
// Serial.println("Encendiendo luces...");
fade(fadeinRampTimeSecs, fadein);
// Serial.println("Intervalo...");
long_delay_with_blink(intermissionSecs);
}
// ----------------------------------------------------------------------------