/* !!! Configuration start !!! */ /* RGB led pins */ unsigned char ledPin[] = {9, 10, 11}; /* Fading hue increment */ float hueInc = 0.001; /* !!! Configuration end !!! */ /* Values below this line will be overrided when Processing sketch starts, so it's useless to change them */ /* RGB led status */ int ledStatus[] = {0, 0, 0}; /* RGB led PWM value */ int ledPWM[] = {0, 0, 0}; /* Current mode */ unsigned char curMode = 0; /* Maximum strobe frequency (Hz) 0 - 255 */ float maxFreq = 30; /* Current strobe frequency (maxFreq/255) 0 - 255 */ float curFreq = 0; /* Current strobe duty cycle (0 - 255) */ float curDuty = 127; /* Current fading speed (0 - 255 milliseconds between one hue and the other) */ unsigned char curFadeSpeed = 255; /* Delay timings */ unsigned long sPeriod1, sPeriod2; /* Current brightness (0 - 255) */ int curBright = 255; /* Serial communication protocol */ /* 4 bit command code + 4 bit short data */ /* 8 bit long data for certains commands */ /* If command code has MSB set it means it needs 8 bit data */ /* Otherwise, it needs only code + 4 bit data */ /* |code|data|datadata| (1 char = 1 bit, | doesn't count) */ /* Command codes */ /* 0000 - Set LED status */ /* Short data bits - SRGB */ /* S: 1 led on, 0 led off */ /* R: 1 apply on Red LED, 0 doesn't apply on Red LED */ /* G: 1 apply on Green LED, 0 doesn't apply on Green LED */ /* B: 1 apply on Blue LED, 0 doesn't apply on Blue LED */ /* Command can apply on multiple LEDs */ /* No long data */ /* 0001 - Select mode */ /* Short data bits - MODE */ /* 0000: Normal mode */ /* 0001: RGB fading mode */ /* 0010: Fading strobe mode */ /* No long data */ /* 0010 - Reset hue (set curHue to 0) */ /* Short data bits - 0000 */ /* No long data */ /* 1000 - Set maximum strobe frequency */ /* Short data bits - 0000 */ /* Long data bits: frequency 0 - 255 Hz */ /* 1001 - Set strobe/fading frequency/speed */ /* Short data bits */ /* 0000: Strobe frequency */ /* 0001: Fading speed */ /* Long data bits */ /* Strobe frequency 0 - 255 relative to the maximum frequency */ /* e.g. If max freq is 17Hz and you set it to 150/255 the freq will be 10Hz (17 * 150/255) */ /* OR fading speed 0 - 255 MILLISECONDS between one hue and the other */ /* 1010 - Set strobe duty cycle */ /* Short data bits - 0000 */ /* Long data bits: duty cycle 0 - 255 (e.g. 51/255 duty cycle means that the LED will be on for 20% of the period in strobe mode) */ /* 1011 - Set LED PWM value */ /* Short data bits - 0RGB */ /* R: 1 apply on Red LED, 0 doesn't apply on Red LED */ /* G: 1 apply on Green LED, 0 doesn't apply on Green LED */ /* B: 1 apply on Blue LED, 0 doesn't apply on Blue LED */ /* Command can apply on multiple LEDs */ /* Long data bits: PWM value 0 - 255 */ /* 1100 - Set brightness */ /* Short data bits - 0000 */ /* Long data bits: brightness 0 - 255 */ /* This won't affect colors, only their brightness */ /* (colors ratios remain unchanged) */ int curBits = -1; unsigned long curMillis; unsigned char curStat = 0; float curHue = 0; float fadeStrobeHueInc; /* Recalculate delay timings */ void timeCalc() { if(curFreq > 0) { sPeriod1 = ((1000 / ((curFreq * maxFreq) / 255)) * curDuty) / 255; sPeriod2 = ((1000 / ((curFreq * maxFreq) / 255)) * (255 - curDuty)) / 255; } } /* Recalculate fading strobe hue increment */ void fadeStrobeCalc() { if(curFadeSpeed > 0) fadeStrobeHueInc = ((sPeriod1 + sPeriod2) * hueInc) / curFadeSpeed; else fadeStrobeHueInc = (sPeriod1 + sPeriod2) * hueInc; /* 1ms won't make any difference!!! */ } /* Receive a command from serial port */ /* code, shortData, longData: pointers where the various parts of the command will be written */ /* Return 0 if no new command is available, 1 if command has been received */ /* This ISN'T blocking! */ int recvCmd(unsigned char &code, unsigned char &shortData, unsigned char &longData) { unsigned char bits; /* Check for a command code or long data */ if(Serial.available() < 1) return 0; /* Read a byte from the serial port */ bits = Serial.read(); if(curBits < 0) { /* New command */ curBits = bits; if(bits & 0x80) { /* MSB set, this command expects long data */ if(Serial.available() < 1) return 0; longData = Serial.read(); } code = (bits >> 4) & 0xF; shortData = bits & 0xF; curBits = -1; } else { /* Got long data for the command */ longData = bits; bits = (unsigned char) curBits; /* Just to avoid annoying casting brackets */ code = (bits >> 4) & 0xF; shortData = bits & 0xF; curBits = -1; } return 1; } /* Receive and process commands */ void processCmd() { unsigned char code, shortData, longData; if(!recvCmd(code, shortData, longData)) return; switch(code) { case 0x0: /* 0000 - Set LED status */ if(shortData & 0x4) /* Red */ ledStatus[0] = (shortData >> 3) & 0x1; if(shortData & 0x2) /* Green */ ledStatus[1] = (shortData >> 3) & 0x1; if(shortData & 0x1) /* Blue */ ledStatus[2] = (shortData >> 3) & 0x1; break; case 0x1: /* 0001 - Select mode */ if(shortData < 3) {/* Check if the mode is ok */ curMode = shortData; curStat = 0; } break; case 0x2: /* 0010 - Reset hue */ curHue = 0; break; case 0x8: /* 1000 - Set maximum strobe frequency */ maxFreq = longData; timeCalc(); fadeStrobeCalc(); break; case 0x9: /* 1001 - Set strobe/fading frequency/speed */ if(shortData == 0) { curFreq = longData; timeCalc(); fadeStrobeCalc(); } else if(shortData == 1) { curFadeSpeed = longData; fadeStrobeCalc(); } break; case 0xA: /* 1010 - Set strobe duty cycle */ curDuty = longData; timeCalc(); fadeStrobeCalc(); break; case 0xB: /* 1011 - Set PWM value */ if(shortData & 0x4) /* Red */ ledPWM[0] = longData; if(shortData & 0x2) /* Green */ ledPWM[1] = longData; if(shortData & 0x1) /* Blue */ ledPWM[2] = longData; break; case 0xC: /* 1100 - Set brightness */ curBright = longData; break; default: break; } return; } /* Convert hue to RGB */ /* hue: Pointer to float hue. Remember that it's in modulo-1 arithmetic, so 0 == 1. It'll be fixed automatically anyway. */ /* R, G, B: pointers where red, green and blue values will be stored (0 - 255) */ /* I based it on http://en.wikipedia.org/wiki/File:HSV-RGB-comparison.svg */ void hueToRGB(float &hue, int &R, int &G, int &B) { unsigned char degree; float sixHue, frac; /* Here 0 == 1 :) */ /* Fix hue value, so you can just increment it without worring about overflow */ hue = hue - floor(hue); sixHue = 6 * hue; degree = floor(sixHue); frac = sixHue - degree; if(degree == 6) degree = 0; if(degree == 0) { R = 255; G = frac * 255; B = 0; } else if(degree == 1) { R = (1 - frac) * 255; G = 255; B = 0; } else if(degree == 2) { R = 0; G = 255; B = frac * 255; } else if(degree == 3) { R = 0; G = (1 - frac) * 255; B = 255; } else if(degree == 4) { R = frac * 255; G = 0; B = 255; } else { R = 255; G = 0; B = (1 - frac) * 255; } return; } void setup() { /* Setup ports */ pinMode(ledPin[0], OUTPUT); pinMode(ledPin[1], OUTPUT); pinMode(ledPin[2], OUTPUT); pinMode(2, OUTPUT); /* It's alive! */ digitalWrite(2, HIGH); /* Setup serial communication */ Serial.begin(57600); /* Cleanup serial port */ Serial.flush(); /* Calculate timings */ timeCalc(); fadeStrobeCalc(); /* Ready! */ Serial.write("Ready!"); } void loop() { processCmd(); int R, G, B; if(curMode == 0 || curMode == 2) { if(curStat == 0) { if(curMode == 0) { R = (ledPWM[0] * ledStatus[0] * curBright) / 255; G = (ledPWM[1] * ledStatus[1] * curBright) / 255; B = (ledPWM[2] * ledStatus[2] * curBright) / 255; } else { hueToRGB(curHue, R, G, B); R = (R * ledStatus[0] * curBright) / 255; G = (G * ledStatus[1] * curBright) / 255; B = (B * ledStatus[2] * curBright) / 255; } analogWrite(ledPin[0], R); analogWrite(ledPin[1], G); analogWrite(ledPin[2], B); if(curFreq > 0) { curMillis = millis(); curStat = 1; } else if(curFreq == 0 && curMode == 2) { curMillis = millis(); curStat = 3; } } else if(curStat == 1 && (millis() - curMillis) >= sPeriod1) { analogWrite(ledPin[0], 0); analogWrite(ledPin[1], 0); analogWrite(ledPin[2], 0); curMillis = millis(); curStat = 2; } else if(curStat == 2 && (millis() - curMillis) >= sPeriod2) { if(curMode == 2) curHue += fadeStrobeHueInc; curStat = 0; } else if(curStat == 3 && (millis() - curMillis) >= curFadeSpeed) { curHue += hueInc; curStat = 0; } } else if(curMode == 1) { if(curStat == 0) { hueToRGB(curHue, R, G, B); R = (R * ledStatus[0] * curBright) / 255; G = (G * ledStatus[1] * curBright) / 255; B = (B * ledStatus[2] * curBright) / 255; analogWrite(ledPin[0], R); analogWrite(ledPin[1], G); analogWrite(ledPin[2], B); curMillis = millis(); curStat = 1; } else if(curStat == 1 && (millis() - curMillis) >= curFadeSpeed) { curHue += hueInc; curStat = 0; } } }