Skip to content
  • MySensors
  • OpenHardware.io
  • Categories
  • Recent
  • Tags
  • Popular
Skins
  • Light
  • Brite
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • Default (No Skin)
  • No Skin
Collapse
Brand Logo
  1. Home
  2. My Project
  3. Controlling a 1st gen. LivingColors lamp

Controlling a 1st gen. LivingColors lamp

Scheduled Pinned Locked Moved My Project
6 Posts 4 Posters 6.0k Views 4 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • BartEB Offline
    BartEB Offline
    BartE
    Contest Winner
    wrote on last edited by BartE
    #1

    Having a first generation LivingColors lamp sitting around for a couple a years, mostly doing nothing because the RC batteries are worn out with 6 weeks. I was browsing the internet when a ran into a old project post on knutsel.org describing a LivingColors RC based on Arduino UNO shield, when i decided to give the LC a second life :-)

    I ordered a build and tested CC2500 shield and mounted a NRF24L01+ Radio module on the board itself. It has plenty of mount space left.

    Since the 2500 uses the SPI interface as well i had to use alternative pins for CS and CE. This is how i connected the radio
    0_1454973348897_LivingColorRemote.png

    The build device
    0_1454973369008_LivingColorCC2500MyS_small.jpg

    For controlling the lamp i'd used vosmont RGB contoller plugin as described in this post

    This is the Arduino sketch (please make sure you install the CC2500 library from knutsel.org

    /**
     * REVISION HISTORY
     * Version 1.0 - Februari 2016 - Bart Eversdijk
     *
     * DESCRIPTION
     * This sketch provides a LivingColor 1st generation  RGB controller 
     * 
     * Based on the CC2500 sheild from http://www.knutsel.org/tag/arduino-shield/
     *
     * Optimized for usage with vosmont RGB colorwheel MicsaVerde Vera plug in 
     * https://github.com/vosmont/Vera-Plugin-RGBController and http://forum.micasaverde.com/index.php?topic=32613.0
     *
     */
    #include <CC2500.h>
    #include <ColourConversion.h>
    #include <LivingColors.h>
    #include <MyTransportNRF24.h>
    #include <MyHwATMega328.h>
    #include <SPI.h>
    #include <MySensor.h>
    
    
    #define lcMOSI   11    // SPI master data out pin
    #define lcMISO   12    // SPI master data in pin
    #define lcSCK    13    // SPI clock pin
    #define lcCS     10    // SPI slave select pin
    
    #define MY_RF24_CE_PIN  6
    #define MY_RF24_CS_PIN  7
    
    // NRFRF24L01 radio driver (set low transmit power by default) 
    MyTransportNRF24 radio(MY_RF24_CE_PIN, MY_RF24_CS_PIN, RF24_PA_LEVEL_GW);  
    // Select AtMega328 hardware profile
    MyHwATMega328 hw;
    // Construct MySensors library
    MySensor gw(radio, hw);
    
    LivingColors livcol(lcCS, lcSCK, lcMOSI, lcMISO);
    
    #define lampId  0
    unsigned char lamp1[9] = { 0x07, 0x1C, 0x19, 0x10, 0x39, 0xF9, 0x3C, 0xB9, 0x11 }; // Lamp Woonkamer
    
    enum ANIMATIOMODES {RAINBOW=0,RANDONMIZE,FADERGB,FADEMULTICOLR,FLASHCOLOR,LAST_PROGRAM};
    char *ANIMATIOMODES_TXT[] = { "RAINBOW","RANDONMIZE","FADERGB","FADEMULTICOLR","FLASHCOLOR" };
    byte FADE_RGB_COLOR_MODES[]   = {0b0010,0b0011,0b0100,0b0101,0b1000,0b1001, 0xFF};
    byte FADE_MULTI_COLOR_MODES[] = {0b0010,0b0011,0b0110,0b0111,0b0100,0b0101,0b1100,0b1101,0b1000,0b1001,0b1010,0b1011,0xFF};
                              // RRGBU
    byte COLOR_WHEEL_MODES[] = {0b0101, 0b0010, 0b1001, 0b0100, 0b0011, 0b1000, 0xFF};
    
    byte ledOffValues[] = {0, 0, 0};
    byte rgb_values[]   = {0, 0, 0};
    
    byte speedtable[] = { 0, 400, 100, 20 };
    //int speedtable[] = { 0, 50, 25, 2 };
    #define NUM_OF_SPEEDS sizeof(speedtable)
    #define CHAR2BYTE(x)   (byte)(x > '9' ? (x > 'F' ? (x + 10 - 'a') : (x + 10 - 'A')) : x - '0')
    
    struct
    {
       byte values[3];
       byte speedsetting;
       byte mode;
       bool status;
       byte dimlevel;
    } rgb = { {0,0,0}, 0, RAINBOW, false, 0};
    
    bool              flashOn      = true;
    unsigned long     syscounter   = 0;
    unsigned long     lastUpdate   = 0;
    bool              newSetting   = false;
    
    
    void setup() {
        // Initialize library and add callback for incoming messages
        gw.begin(incomingMessage, AUTO, true);
        // Send the sketch version information to the gateway and Controller
        gw.sendSketchInfo("LivingColor", "1.0");
        
        // Do not present for Vera see forum post: http://forum.mysensors.org/topic/2160/my-mysensors-rgbw-plug-in-has-an-init-problem
        //gw.present(0, S_RGB_LIGHT);
        
        livcol.init();
        livcol.clearLamps();
        livcol.addLamp(lamp1);
    
        recallEeprom();
        if (rgb.status) {
           livcol.turnLampOnRGB(lampId, rgb_values[0], rgb_values[1], rgb_values[2]); 
        }
        delay(10); 
        
        Serial.println("Init done");
    }
    
    void loop()
    {
        // Alway process incoming messages whenever possible
        gw.process();
    
        if (rgb.status && speedtable[rgb.speedsetting] > 0) {
            if ((syscounter % speedtable[rgb.speedsetting]) == 0) {
               switch (rgb.mode)
               {
                   case RAINBOW:
                     animateRainbowStep();
                     break;
    
                   case FADERGB:
                     animateFadeColorStep(FADE_RGB_COLOR_MODES);
                     break;
    
                   case FADEMULTICOLR:
    //                 animateFadeColorStep(FADE_MULTI_COLOR_MODES);
                     animateColorWheelStep(COLOR_WHEEL_MODES);
                     break;
                 
                  case FLASHCOLOR:
                     setLedValues(flashOn ? ledOffValues : rgb.values, false);
                     flashOn = !flashOn;
                     break;
                                 
                   case RANDONMIZE:
                     rgb_values[0] = (byte)random(0, rgb.dimlevel);
                     rgb_values[1] = (byte)random(0, rgb.dimlevel);
                     rgb_values[2] = (byte)random(0, rgb.dimlevel);
                     setLedValues(rgb_values, false);
                     break;
               }
            }
            delay(rgb.mode == RANDONMIZE || rgb.mode == FLASHCOLOR ? 50 : 1);
        }
        
        if (newSetting && (lastUpdate + 20000 < syscounter)) {   
            // Wait for a couple of seconds be fore actual storing the current setting into EEPROM
            // This will save the EEPROM's life time, when playing around with colors
            Serial.println(" Store EERPOM");
            storeEeprom();
            newSetting = false;
        } 
        
        gw.wait(3);
        syscounter++;
    }
    
    void animateRainbowStep()
    {    
        static float counter = 0;
        float        pi      = 3.14159; 
        counter++;
        rgb_values[0] = map(sin(counter/100         )*1000,-1000,1000,0,0xFF);
        rgb_values[1] = map(sin(counter/100 + pi*2/3)*1000,-1000,1000,0,0xFF);
        rgb_values[2] = map(sin(counter/100 + pi*4/3)*1000,-1000,1000,0,0xFF);
        setLedValues(rgb_values, false);
    }
    
    void animateFadeColorStep(byte *modes)
    {    
        static int modecnt = 0;
        if (updateRGBValues(modes[modecnt] >> 1, (modes[modecnt] & 0x1) == 0x1)) { 
            modecnt = (modes[modecnt+1] == 0xFF ? 0 : modecnt + 1);
        }
    }
    
    void animateColorWheelStep(byte *modes)
    {    
        static int modecnt = 0;
        for (byte i = 0; i < 3; i++) {
            if ((modes[modecnt] >> (i + 4) & 0x1) == 0x1) {
               rgb_values[i] = 0xFF;
            }
        }
        if (updateRGBValues(modes[modecnt] >> 1, (modes[modecnt] & 0x1) == 0x1)) { 
            modecnt = (modes[modecnt+1] == 0xFF ? 0 : modecnt + 1);
        }
    }
    
    
    bool updateRGBValues(byte mode, bool down)
    {
        bool endReached = false;
        for (byte i = 0; i < 3; i++) {
            if (((mode >> i) & 0x1) == 0x1) {
               rgb_values[i] += (down ? -1 : 1);
               endReached    |= (down && (rgb_values[i] == 0x00)) || (!down && (rgb_values[i] == 0xFF));
            }
        }
        setLedValues(rgb_values, false);
        return endReached;
    }
    
    
    void incomingMessage(const MyMessage &message) {
        if (message.isAck()) {
            Serial.println("GW acknowledge");
        }
        
        if (message.type == V_RGB || message.type == V_RGBW) {
    	// starting to process the hex code
            const char * hexstring  = message.getString();
            rgb.values[0]    = (CHAR2BYTE(hexstring[0]) << 4) + CHAR2BYTE(hexstring[1]);
            rgb.values[1]    = (CHAR2BYTE(hexstring[2]) << 4) + CHAR2BYTE(hexstring[3]);
            rgb.values[2]    = (CHAR2BYTE(hexstring[4]) << 4) + CHAR2BYTE(hexstring[5]);
            rgb.dimlevel     = (CHAR2BYTE(hexstring[6]) << 4) + CHAR2BYTE(hexstring[7]);
            
            rgb.speedsetting = 0;  // new color is set stop animation (if is was running)
            if (!rgb.status) {
                // Switch on if the status was off
                rgb.status  = 1;  
                livcol.turnLampOnRGB(lampId, rgb.values[0], rgb.values[1], rgb.values[2]); 
            }
            setLedValues(rgb.values, true);
            lastUpdate       = syscounter;
            newSetting       = true;
        }
        
        if (message.type == V_STATUS) {
           if (message.getBool()) {
               Serial.println("ON: Switch to last known color values/or start the last known animation");
               livcol.turnLampOnRGB(lampId, rgb.values[0], rgb.values[1], rgb.values[2]); 
           } else {
               Serial.println("OFF: Switch colors off");
               livcol.turnLampOff(lampId);
           }
           rgb.status = message.getBool();
           lastUpdate = syscounter;
           newSetting = true;
        }
        
        if (message.type == V_VAR1) {
           Serial.println("Set speed and program value"); 
           const char * newsetting = message.getString();
           rgb.speedsetting = CHAR2BYTE(newsetting[0]);
           byte newmode     = CHAR2BYTE(newsetting[1]);
    
           if (newmode != rgb.mode) {
               for (byte i = 0; i < 3; i++) {
                   rgb_values[i] = 0;
               }
               rgb.mode = newmode;
           }
           if (rgb.speedsetting > 0) {
               rgb.status = true;
           }
           lastUpdate = syscounter;
           newSetting = true;
          
           Serial.print("Speed:");
           Serial.print(rgb.speedsetting);
           Serial.print(" mode: ");
           Serial.print(rgb.mode);
           Serial.print(" ");
           Serial.println(ANIMATIOMODES_TXT[rgb.mode]);
        }
    }
    
    
    void setLedValues(byte *setrgb, bool show)
    {
        static byte oldrgb[] = {0, 0, 0};
        
        byte newrgb[] = {0, 0, 0};
        newrgb[0] = map(setrgb[0],0,0xFF,0,show?0xFF:rgb.dimlevel);
        newrgb[1] = map(setrgb[1],0,0xFF,0,show?0xFF:rgb.dimlevel);
        newrgb[2] = map(setrgb[2],0,0xFF,0,show?0xFF:rgb.dimlevel);
        
        bool update = (newrgb[0] != oldrgb[0]) || (newrgb[1] != oldrgb[1]) || (newrgb[2] != oldrgb[2]);
        oldrgb[0] = newrgb[0];
        oldrgb[1] = newrgb[1];
        oldrgb[2] = newrgb[2];
        
        if (update) {
           livcol.setLampColourRGB(lampId, newrgb[0], newrgb[1], newrgb[2]); 
        }
        if (show) {
           Serial.print("Red: " );
           Serial.print(newrgb[0], HEX);
           Serial.print("  Green: " );
           Serial.print(newrgb[1], HEX);
           Serial.print("  Blue: " );
           Serial.print(newrgb[2], HEX);
           Serial.print("  dim level: " );
           Serial.println(rgb.dimlevel, HEX);
        }
    }
    
    void storeEeprom()
    {
        byte address = 0;
        byte *p = (byte *)&(rgb);
        for (byte i = 0; i < sizeof(rgb); i++) {
           gw.saveState(address++, p[i]);
        }
    }
    
    void recallEeprom()
    {
        byte address = 0;
        byte *p = (byte *)&(rgb);
        for (byte i = 0; i < sizeof(rgb); i++) {
           p[i] = gw.loadState(address++);
        }
    }
    

    And this is some action footage of the end result:
    https://youtu.be/7mywMRM3UT4

    1 Reply Last reply
    2
    • hekH Offline
      hekH Offline
      hek
      Admin
      wrote on last edited by
      #2

      Darn, mine is Gen2.

      1 Reply Last reply
      0
      • M Offline
        M Offline
        mathieu
        wrote on last edited by
        #3

        Very intresting : How to give a second life to my old LivingColor !
        One question : Is the shield mandatory ? Can I wire the CC2500 and the NRF24 directly on the arduino ?
        And finaly, does a chip like this one (https://fr.aliexpress.com/item/Free-Shipping-MD7105-SY-A7105-2-4G-Wireless-Transceiver-Module-3-3V-Better-Than-CC2500-NRF24L01/32714119016.html?isOrigTitle=true) will work with CC2500.h library or should I take one like this one (https://fr.aliexpress.com/item/CC2500-IC-Sans-Fil-RF-metteur-R-cepteur-2-4G-Module-ISM-SPI-D-mo-Code/32674039629.html?isOrigTitle=true) ?
        Thanks

        BartEB 1 Reply Last reply
        0
        • M mathieu

          Very intresting : How to give a second life to my old LivingColor !
          One question : Is the shield mandatory ? Can I wire the CC2500 and the NRF24 directly on the arduino ?
          And finaly, does a chip like this one (https://fr.aliexpress.com/item/Free-Shipping-MD7105-SY-A7105-2-4G-Wireless-Transceiver-Module-3-3V-Better-Than-CC2500-NRF24L01/32714119016.html?isOrigTitle=true) will work with CC2500.h library or should I take one like this one (https://fr.aliexpress.com/item/CC2500-IC-Sans-Fil-RF-metteur-R-cepteur-2-4G-Module-ISM-SPI-D-mo-Code/32674039629.html?isOrigTitle=true) ?
          Thanks

          BartEB Offline
          BartEB Offline
          BartE
          Contest Winner
          wrote on last edited by
          #4

          @mathieu no the shield is not mandatory you can just wire it up yourselves.
          But be careful the CC2500 does requires voltage shifting it will brake with TTL level.
          Here is the schematic

          I'm not familiair with this MD7105 replacement.

          One general tip for all user or the Livingcolor controller. It took my 2 broken Arduino Uno's to find out why they stopped working after 2 weeks. The onboard 3v3 power convertor is to weak ans will break. So i added my own 3v3 power regulator.

          1 Reply Last reply
          0
          • M Offline
            M Offline
            mathieu
            wrote on last edited by
            #5

            Thanks for your precisions.
            I was thinking that one of the simplest way to do it can be to simply and directly plug the CC2500 on a 3.3v arduino and power it with an external regulator, but I'm not sure de TTL level will be ok for the NRF24 !
            It should also be ok using a 5V arduino and a 4 canal IIC I2C Logic Level Converter Bi-Directionnel Module 5 V à 3.3 V (https://fr.aliexpress.com/item/Free-shipping-1pcs-4-channel-IIC-I2C-Logic-Level-Converter-Bi-Directional-Module-5V-to-3/32670479389.html?isOrigTitle=true).

            1 Reply Last reply
            0
            • rvendrameR Offline
              rvendrameR Offline
              rvendrame
              Hero Member
              wrote on last edited by
              #6

              @BartE

              I purchased this module:
              https://www.aliexpress.com/item/Wireless-Module-CC2500-2-4G-Low-power-Consistency-Stability-Small-Size/32702148262.html?spm=a2g0s.9042311.0.0.L1Dfwj

              This is the pinout...
              alt text

              ... and I'm in doubt how to wire GDO2, GDO0, RFSC above.

              RFCL - SCK (pin 13)?
              GDO2 - leave open?
              GDO0 - CE (pin 9)?
              RFCS - CS(pin 10)?

              Would you mind to clarify? Thanks!

              Home Assistant / Vera Plus UI7
              ESP8266 GW + mySensors 2.3.2
              Alexa / Google Home

              1 Reply Last reply
              0
              Reply
              • Reply as topic
              Log in to reply
              • Oldest to Newest
              • Newest to Oldest
              • Most Votes


              11

              Online

              11.7k

              Users

              11.2k

              Topics

              113.1k

              Posts


              Copyright 2025 TBD   |   Forum Guidelines   |   Privacy Policy   |   Terms of Service
              • Login

              • Don't have an account? Register

              • Login or register to search.
              • First post
                Last post
              0
              • MySensors
              • OpenHardware.io
              • Categories
              • Recent
              • Tags
              • Popular