// Control some LED strips, using settings received by wireless // 2010-06-11 http://opensource.org/licenses/mit-license.php // $Id: $ // see http://news.jeelabs.org/2010/06/15/remote-rgb-strip-control/ // // 2010-06-12: delayed init of RF12 driver, to make LED startup a bit quicker // 2010-09-08: adapted for 2x rgb with 0..100% intensity control // // example commands: // 100,100,100,100,100,100,100,100,30s // 100,100,100,0,100,100,100,0,30s // 100,40,5,0,100,40,5,100,30s // // TODO: staggered turn-on, to reduce the inrush current of time slot zero #include #include #include #define DEBUG 0 #define BAND RF12_868MHZ // wireless frequency band #define GROUP 5 // wireless net group #define NODEID 30 // node id on wireless to which this sketch responds #define NVALUES 8 // can be adjusted from 1..8 (must also adjust "masks") #define MAXVAL 100 // maximum dimmer value, corresponds to full-on static byte settings[NVALUES]; // 0 = red, 1 = green, 2 = blue, 3 = white static byte slots[MAXVAL+1]; // time slots, toggles whenever a bit is set // which setting affects which I/O pin: // bits 0..3 = AIO1 .. AIO4 // bits 4..7 = DIO1 .. DIO4 static const byte masks[NVALUES] = { 0x20, 0x02, 0x80, 0x00, 0x08, 0x10, 0x01, 0x00 }; static void setupIO() { for (byte i = 0; i < NVALUES; ++i) { PORTC &= ~ (masks[i] & 0x0F); // turn AIO pin off DDRC |= masks[i] & 0x0F; // make pin an output PORTD &= ~ (masks[i] & 0xF0); // turn DIO pin off DDRD |= masks[i] & 0xF0; // make pin an output } } static void prepareSlots() { #if DEBUG Serial.print("RGB values:"); for (byte i = 0; i < NVALUES; ++i) { Serial.print(' '); Serial.print(settings[i], DEC); } Serial.println(); #endif // use 4th value as intensity control over RGB values (all in 0..100 range) settings[0] = (settings[0] * settings[3] + MAXVAL/2) / MAXVAL; settings[1] = (settings[1] * settings[3] + MAXVAL/2) / MAXVAL; settings[2] = (settings[2] * settings[3] + MAXVAL/2) / MAXVAL; // also for the second RGB group settings[4] = (settings[4] * settings[7] + MAXVAL/2) / MAXVAL; settings[5] = (settings[5] * settings[7] + MAXVAL/2) / MAXVAL; settings[6] = (settings[6] * settings[7] + MAXVAL/2) / MAXVAL; // set a few bits, i.e. at the time when we need to toggle the output pin memset(slots, 0, sizeof slots); // note: a 0 setting toggles the same bit twice, so it won't ever turn on for (byte i = 0; i < NVALUES; ++i) { byte level = settings[i]; // get the requested PWM level if (level > MAXVAL) level = MAXVAL; byte mask = masks[i]; // map setting to corresponding I/O pin slots[0] ^= mask; // toggle on at the start of the loop slots[level] ^= mask; // toggle off at the proper time } // Special case: run LED at full brightness for level MAXVAL - this is done // by turning the LED on at the start of each scan and never toggling it. // This is done in loop() below, but we must prevent toggling in this case. slots[0] ^= slots[MAXVAL]; } static void loadSettings() { for (byte i = 0; i < NVALUES; ++i) settings[i] = EEPROM.read(i); prepareSlots(); } static void saveSettings() { for (byte i = 0; i < NVALUES; ++i) EEPROM.write(i, settings[i]); prepareSlots(); } static void showSettings() { } void setup () { #if DEBUG Serial.begin(57600); Serial.println("\n[rgbAdjust]"); #endif setupIO(); loadSettings(); rf12_initialize(NODEID, BAND, GROUP); } void loop () { if (rf12_recvDone() && rf12_hdr == (RF12_HDR_DST | NODEID) && rf12_crc == 0 && rf12_len == NVALUES) { memcpy(settings, (void*) rf12_data, rf12_len); saveSettings(); } // handle the special case: full-on never toggles the LED // FIXME: this needs to be refined if not all pins are allocated to LED PWM PORTC = (PORTC & 0xF0) | (slots[MAXVAL] & 0x0F); PORTD = (PORTC & 0x0F) | (slots[MAXVAL] & 0xF0); // here, the LED state is known: all LEDs with levels < MAXVAL are off for (byte i = 0; i < MAXVAL; ++i) { PINC = slots[i] & 0x0F; // toggle AIO pins for time slot i PIND = slots[i] & 0xF0; // toggle DIO pins for time slot i delayMicroseconds(31); } }