#include <IRremote.hpp>
/* =======================
Pin configuration
======================= */
const byte irReceivePin = 2;
const byte rPin = 9;
const byte gPin = 6;
const byte bPin = 5;
/* =======================
Fade configuration
======================= */
const int fadeSteps = 100;
const int fadeDelay = 10; // ms per step
float currentH = 0, currentS = 0, currentL = 0;
float targetH = 0, targetS = 0, targetL = 50;
float hStep, sStep, lStep;
unsigned long lastFadeTime = 0;
bool fading = false;
int fadeStep = 0;
/* =======================
RGB struct
======================= */
struct RGB {
uint8_t r;
uint8_t g;
uint8_t b;
};
/* =======================
Shortest hue delta
======================= */
float shortestHueDelta(float from, float to) {
return fmod(to - from + 540.0f, 360.0f) - 180.0f;
}
/* =======================
Start / restart fade
======================= */
void startFade() {
fadeStep = 0;
hStep = shortestHueDelta(currentH, targetH) / fadeSteps;
sStep = (targetS - currentS) / fadeSteps;
lStep = (targetL - currentL) / fadeSteps;
fading = true;
}
/* =======================
Hue helper for HSL
======================= */
float hue2rgb(float p, float q, float t) {
if (t < 0) t += 1;
if (t > 1) t -= 1;
if (t < 1.0f / 6.0f) return p + (q - p) * 6.0f * t;
if (t < 1.0f / 2.0f) return q;
if (t < 2.0f / 3.0f) return p + (q - p) * (2.0f / 3.0f - t) * 6.0f;
return p;
}
/* =======================
TRUE HSL → RGB
======================= */
RGB hslToRgb(float h, float s, float l) {
h = fmod(h, 360.0f) / 360.0f;
s /= 100.0f;
l /= 100.0f;
float r, g, b;
if (s == 0) {
r = g = b = l; // achromatic
} else {
float q = (l < 0.5f) ? (l * (1.0f + s)) : (l + s - l * s);
float p = 2.0f * l - q;
r = hue2rgb(p, q, h + 1.0f / 3.0f);
g = hue2rgb(p, q, h);
b = hue2rgb(p, q, h - 1.0f / 3.0f);
}
RGB color;
color.r = (uint8_t)round(r * 255.0f);
color.g = (uint8_t)round(g * 255.0f);
color.b = (uint8_t)round(b * 255.0f);
return color;
}
/* =======================
Fade update (non-blocking)
======================= */
void fadeToTarget() {
if (!fading) return;
if (millis() - lastFadeTime < fadeDelay) return;
lastFadeTime = millis();
if (fadeStep >= fadeSteps) {
currentH = fmod(targetH + 360.0f, 360.0f);
currentS = targetS;
currentL = targetL;
fading = false;
} else {
currentH = fmod(currentH + hStep + 360.0f, 360.0f);
currentS += sStep;
currentL += lStep;
fadeStep++;
}
RGB color = hslToRgb(currentH, currentS, currentL);
analogWrite(rPin, color.r);
analogWrite(gPin, color.g);
analogWrite(bPin, color.b);
}
/* =======================
IR command handling
======================= */
void handleCommand() {
if (!IrReceiver.decode()) return;
uint32_t cmd = IrReceiver.decodedIRData.command;
//Serial.println(cmd, HEX) ;
switch (cmd) {
case 0xA2: // POWER → white
targetH = 0;
targetS = 0;
targetL = 50;
break;
case 0x30: // RED
targetH = 0;
targetS = 100;
break;
case 0x18: // GREEN
targetH = 120;
targetS = 100;
break;
case 0x7A: // BLUE
targetH = 240;
targetS = 100;
break;
case 0x10: // CYAN
targetH = 180;
targetS = 100;
break;
case 0x38: // MAGNETA
targetH = 300;
targetS = 100;
break;
case 0x5A: // YELLOW
targetH = 60;
targetS = 100;
case 0xE0: // BRIGHTNESS DOWN
targetL = max(0.0f, targetL - 5.0f);
break;
case 0x90: // BRIGHTNESS UP
targetL = min(100.0f, targetL + 5.0f);
break;
default:
IrReceiver.resume();
return;
}
startFade();
IrReceiver.resume();
}
/* =======================
Setup
======================= */
void setup() {
pinMode(rPin, OUTPUT);
pinMode(gPin, OUTPUT);
pinMode(bPin, OUTPUT);
Serial.begin(115200);
IrReceiver.begin(irReceivePin);
Serial.println(F("IR HSL Fade Ready"));
}
/* =======================
Main loop
======================= */
void loop() {
handleCommand();
fadeToTarget();
}