My MySensors RGBW plug in has an init problem


  • Contest Winner

    I've created a RGB(W) led strip MySensors controller which interacts with the RGB color wheel plug in from MiOS Marketplace.

    The problem i have with it is that the the Arduino plug in does not initialize with MiOS plug in as "slave" plug in.
    It is made by vosmont and can be found here and i've added MyS support (see below)

    If someone can help me by fixing the init problem it would be really nice, but so far.

    i did make a small work around (tested with UI5 Vera 3):
    1 make sure both Arduino and the MiOS RGB controller module are installed.
    2 start MySensors inclusion
    3 reset MySensors RGB module
    4 Two devices will (should) be detected and appear. (a MYS controller and a RGB color wheel)
    5 Remember the RBV modules 'altid' (wrench-icon, Advanced-tab), this looks like "6;1"
    6 Remember the Device ID of the MYS Arduindo master plug in
    7 Now delete the RGB color wheel (trash can icon)
    8 Create a new device (APPS -> Develop Apps -> Create device)
    9 Description field: <give a usefull name> like RGB strip
    Upnp Device Filename field : D_RGBController1.xml
    No parent device !!!
    10 Press the Create device button
    11 Press the reload button, wait for the system becomes stable
    12 Change the setting of te RGB controller (wrench-icon, Settings-tab)
    13 Select: MySensors RGBW
    Arduino plugin Id: <enter ID remembered and step 6>
    RGB Node altid: <enter ID remembered and step 5>
    14 Press save button
    15 Now the color wheel can be used

    rgbcolor.png

    To be able to control MyS RGB modules the following coded is added to L_RGBController1.lua after line 1004 (before Color transition management)

    -- MySensor RGB(W)
    RGBDeviceTypes["MYS-RGBW"] = {
    	getParameters = function (lul_device)
    		return {
    			name = "MySensors RGBW",
    			settings = {
    				{ variable = "ArduinoId", name = "Arduino plugin Id", type = "string" },
    				{ variable = "RadioId",   name = "RGB Node altid",    type = "string" }
    			}
    		}
    	end,
    
    	getColorChannelNames = function (lul_device)
    		return {"red", "green", "blue", "warmWhite"}
    	end,
    
    	getAnimationProgramNames = function()
    		return {
    			"Rainbow slow",
    			"Rainbow medium",
    			"Rainbow fast",
    			"Random slow colors",
    			"Random medium colors",
    			"Random fast colors",
    			"RGB fade slow colors",
    			"RGB fade medium colors",
    			"RGB fade fast colors",
    			"Multicolor fade slow colors",
    			"Multicolor fade medium colors",
    			"Multicolor fade fast colors",
    			"Current color flash slow colors",
    			"Current color flash medium colors",
    			"Current color flash fast colors",
    		}
    	end,
    
    	init = function (lul_device)
    		debug("MYS-RGBW.init", "Init for device #" .. tostring(lul_device))
    		pluginParams.rgbArduinoId = tonumber(getVariableOrInit(lul_device, SID.RGB_CONTROLLER, "ArduinoId", ""))
    		pluginParams.rgbRadioId = getVariableOrInit(lul_device, SID.RGB_CONTROLLER, "RadioId", "")
    		return true
    	end,
    
    	setStatus = function (lul_device, newTargetValue)
    		debug("MYS.setStatus", "Set status '" .. tostring(newTargetValue) .. "' for device #" .. tostring(lul_device))
    		if (tostring(newTargetValue) == "1") then
    			debug("MYS.setStatus", "Switches on ")
    			local formerColor = luup.variable_get(SID.RGB_CONTROLLER, "Color", lul_device):gsub("#","")
    			luup.call_action("urn:upnp-arduino-cc:serviceId:arduino1", "SendCommand", {variableId = "RGBW", value = formerColor, radioId = pluginParams.rgbRadioId}, pluginParams.rgbArduinoId)
    			luup.variable_set(SID.SWITCH, "Status", "1", lul_device)
    		else
    			debug("MYS.setStatus", "Switches off")
                            luup.call_action("urn:upnp-arduino-cc:serviceId:arduino1", "SendCommand", {variableId = "LIGHT", value = "0", radioId = pluginParams.rgbRadioId}, pluginParams.rgbArduinoId)
    			luup.variable_set(SID.SWITCH, "Status", "0", lul_device)
    		end
    
    	end,
    
    	setColor = function (lul_device, color)
    		colorString = tostring(color)
    		debug("MYS.setColor", "Set RGBW color #" .. colorString .. " for device #" .. tostring(lul_device))
                    luup.call_action("urn:upnp-arduino-cc:serviceId:arduino1", "SendCommand", {variableId = "RGBW", value = colorString, radioId = pluginParams.rgbRadioId}, pluginParams.rgbArduinoId)
    	end,
    
    	startAnimationProgram = function (lul_device, programId, programName)
    		if ((programName ~= nil) and (programName ~= "")) then
    			--debug("MYS.startAnimationProgram", "Start animation program '" .. programName .. "'")
    			mode = 0
    			if string.match(programName, "Random") then
    				mode = 0x01
    			end
    			if string.match(programName, "RGB fade") then
    				mode = 0x02	
    			end
    			if string.match(programName, "Multicolor fade") then
    				mode = 0x03	
    			end
    			if string.match(programName, "Current color flash") then
    				mode = 0x04
    			end
    			if string.match(programName, "slow") then
    				mode = 0x10 + mode
    			end
    			if string.match(programName, "medium") then
    				mode = 0x20 + mode
    			end
    			if string.match(programName, "fast") then
    				mode = 0x30 + mode
    			end
    			debug("MYS.startAnimationProgram", "Start animation program '" .. programName .. "' " .. tostring(mode))
    			luup.call_action("urn:upnp-arduino-cc:serviceId:arduino1", "SendCommand", {variableId = "VAR_1", value = tostring(mode), radioId = pluginParams.rgbRadioId}, pluginParams.rgbArduinoId)
    		else
    			debug("MYS.startAnimationProgram", "Stop animation program")
    			luup.call_action("urn:upnp-arduino-cc:serviceId:arduino1", "SendCommand", {variableId = "VAR_1", value = "00", radioId = pluginParams.rgbRadioId}, pluginParams.rgbArduinoId)
    			setColorTarget(lul_device, "")
    		end
    	end
    }
    
    

    And this is the MyS RGB color strip plugin

    // RBG led strip plug in.
    // by Bart Eversdijk (c) 2015.
    #include <MySensor.h>
    #include <SPI.h>
    
    // If not defined RGB is used otherwise RGBW
    #define RGBW 1
    
    #define RED   3  // Arduino PWM pin for Red
    #define GREEN 5 // Arduino PWM pin for Green
    #define BLUE  6  // Arduino PWM pin for Blue
    #define WHITE 7  // Arduino PWM pin for White (ilumination)
    
    
    enum ANIMATIOMODES {RAINBOW=0,RANDONMIZE,FADERGB,FADEMULTICOLR,FLASHCOLOR,LAST_PROGRAM};
    
    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};
    
    #ifdef RGBW
      #define NODENAME "RGBW node"
      #define NODETYPE S_RGBW_LIGHT
      byte rgb_pins[] = {RED, GREEN, BLUE, WHITE};
    #else
      #define NODENAME "RGB node"
      #define NODETYPE S_RGB_LIGHT
      byte rgb_pins[]   = {RED, GREEN, BLUE};
    #endif
    
    byte ledOffValues[] = {0, 0, 0, 0};
    byte rgb_values[]   = {0, 0, 0, 0};
    
    #define NODEID 2  // Assigning the node id
    #define SUBID  1  // sensor number needed in the custom devices set up
    
    void incomingMessage(const MyMessage &message);
    #define NUM_OF_COLORS sizeof(rgb_pins)
    int speedtable[] = { 0, 100, 50, 2 };
    #define NUM_OF_SPEEDS sizeof(speedtable)
    
    struct
    {
       byte values[4];
       byte speedsetting;
       byte mode;
       bool status;
    } rgb = { {0,0,0,0}, 0, RAINBOW, false};
    
    bool    flashOn      = true;
    int     syscounter   = 0;
    int     lastUpdate   = 0;
    bool    newSetting   = false;
    
    MySensor gw; 
    
    void setup()
    {
        // Initialize library and add callback for incoming messages
        gw.begin(incomingMessage, AUTO /*NODEID*/);
        // Send the sketch version information to the gateway and Controller
        gw.sendSketchInfo(NODENAME, "1.1");
        // Register the sensor to gw
        gw.present(SUBID, NODETYPE);
    
        // Set the rgb(w) pins in output mode
        for (int i = 0; i < NUM_OF_COLORS; i++) {
            pinMode(rgb_pins[i], OUTPUT);
        }
        recallEeprom();
        setLedValues(rgb.values, true);
        
        Serial.println("Init done");
    }
    
    void loop()
    {
        // Alway process incoming messages whenever possible
        gw.process();
    
        if (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);
                     break;
                 
                  case FLASHCOLOR:
                     setLedValues(flashOn ? ledOffValues : rgb.values, false);
                     flashOn = !flashOn;
                     break;
                                 
                   case RANDONMIZE:
                     long number = random(0, 0xFFFFFF);
                     rgb_values[0] = number >> 16 & 0xFF ;
                     rgb_values[1] = number >> 8 & 0xFF ;
                     rgb_values[2] = number & 0xFF;
                     setLedValues(rgb_values, false);
                     break;
               }
            }
            delay(rgb.mode == RANDONMIZE || rgb.mode == FLASHCOLOR ? 50 : 1);
        }
        if (newSetting && (lastUpdate + 30000 < syscounter)) {   
            // Wait for a couple of seconds be fore actual storing the current setting in to EEPROM
            // This will save the EEPROM's life time, when playing around with colors
            Serial.println(" Store EERPOM");
            storeEeprom();
            newSetting = false;
        } 
        delay(1);
        syscounter++;
    }
    
    void animateRainbowStep()
    {    
        static float counter = 0;
        float        pi      = 3.14159; 
        counter++;
        rgb_values[0] = map(sin(counter/100         )*1000,-1000,1000,0,255);
        rgb_values[1] = map(sin(counter/100 + pi*2/3)*1000,-1000,1000,0,255);
        rgb_values[2] = map(sin(counter/100 + pi*4/3)*1000,-1000,1000,0,255);
        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);
        }
    }
    
    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.type == V_RGB || message.type == V_RGBW) {
    	// starting to process the hex code
            String hexstring = message.getString();
    
            long number;
    #ifdef RGBW
            char white[3];
            white[0] = hexstring[6];
            white[1] = hexstring[7];
            white[2] = 0;
            number = (long) strtol( &white[0], NULL, 16);
            rgb.values[3] = number & 0xFF;
    #endif        
            hexstring[6] = 0;
            number = (long) strtol( &hexstring[0], NULL, 16);
            rgb.values[0] = number >> 16 & 0xFF ;
            rgb.values[1] = number >> 8 & 0xFF ;
            rgb.values[2] = number & 0xFF;
            
            rgb.speedsetting = 0;
            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");
              setLedValues(rgb_values, true);
          } else {
             Serial.println("OFF: Switch colors off");
             setLedValues(ledOffValues, true);
          }
          rgb.speedsetting = 0;
          rgb.status = message.getBool();
          lastUpdate = syscounter;
          newSetting = true;
        }
        
        if (message.type == V_VAR1) {
           Serial.println("Set speed and program value"); 
           byte newsetting = message.getByte();
           rgb.speedsetting = (newsetting >> 4) & 0x0F;
           byte newmode = newsetting & 0x0F;
    
           if (newmode != rgb.mode) {
               for (byte i = 0; i < NUM_OF_COLORS; i++) {
                   rgb_values[i] = 0;
               }
               rgb.mode = newmode;
           }
           if (rgb.speedsetting > 0) {
             rgb.status = true;
           }
           lastUpdate = syscounter;
           newSetting = true;
          
           Serial.print("Data 0x");
           Serial.print(newsetting, HEX);
           Serial.print(" speed:");
           Serial.print(rgb.speedsetting);
           Serial.print(" mode:");
           Serial.println(rgb.mode);
        }
    }
    
    void setLedValues(byte *rgb, bool show)
    {
        for (int i = 0; i < NUM_OF_COLORS; i++) {
            analogWrite(rgb_pins[i], rgb[i]);
        }  
     
        if (show) {
          Serial.print("Red: " );
          Serial.print(rgb[0], HEX);
          Serial.print("  Green: " );
          Serial.print(rgb[1], HEX);
          Serial.print("  Blue: " );
          Serial.print(rgb[2], HEX);
     #ifdef RGBW
            Serial.print("  White is " );
            Serial.print(rgb[3], HEX);
     #endif
          Serial.println();
        }
    }
    
    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++);
        }
    }
    

    Adding a reference to the MiOS forum about this plug in:
    http://forum.micasaverde.com/index.php/topic,32613.0.html


  • Admin

    Nice work @BartE.

    If you'd like you can perhaps incorporate this in the MySensors plugin? Or ask vosmont to pull your additions?


  • Contest Winner

    Thx @hek

    I'm not sure what you mean by incorporate it in the MySensors plug in, but i did make a pull-request to vosmont.

    I hope he can help me with fixing the initialize issue, for some reason the MySensor plug in does not start when the RGBcontroller is configured as slave. I took me quite some time figuring this out but no luck.


  • Admin

    I have a vague memory of trying setting up a VariableContainer(plugin) as child-device in MySensors way back. But it didn't work either. I don't think a plugin in Vera can delegate commands to child devices (the build in devices is the exception).

    That's why I think you have to move the parts needed from vosmont's device into the MySensors plugin. The GUI/descriptor files can of course be reused.

    @akbooer might have some more insight to the Vera mysteries.



  • Happy to help. I've played a bit with @vosmont's RGB plugin when getting it to run under openLuup. It would be best if he could add a new device as part of the standard plugin - it does have some very specific code for different manufacturers. I guess I need to take a closer look at the workflow in the OP and try and understand what is needed and what is not working.


  • Contest Winner

    @akbooer that would be really great if you could help.

    What i see is that Vera calls the initialized (startup) function of the child devices though the parent device. So in this specific case the RGBController lua startup function is called with the lul_device id of the MySensors device and the startup function of the child plugin is NOT called.

    So i modified the RGB controller startup function (L_RGBController1.lua) with a check if it was called for a child or for itself as parent.

    function startup (lul_device)
    	log("startup", "Start plugin '" .. PLUGIN_NAME .. "' (v" .. PLUGIN_VERSION .. ")")
    
    	-- Update static JSON file
    	if updateStaticJSONFile(lul_device, PLUGIN_NAME .. "1") then
    		warning("startup", "'device_json' has been updated : reload LUUP engine")
    		luup.reload()
    		return false, "Reload LUUP engine"
    	end
    
    	-- Init
     	if luup.devices[lul_device].device_type == "urn:schemas-upnp-org:device:RGBController:1" then
    	     log("startup", "Found RGB master #" .. lul_device)
      	     initPluginInstance(lul_device)
    
    	     -- ... and now my watch begins (setting changes)
    	     luup.variable_watch("initPluginInstance", SID.RGB_CONTROLLER, "DeviceType", lul_device)
    	     luup.variable_watch("onDebugValueIsUpdated", SID.RGB_CONTROLLER, "Debug", lul_device)
    	else
    	     -- Look for a child RGB device, which need to start up
    	     for child_id, v in pairs(luup.devices) do
    		   -- if I am the parent device of a child RGBController start it up
    		   if v.device_num_parent == lul_device and v.device_type == "urn:schemas-upnp-org:device:RGBController:1" then
    			log("startup", "Found RGB child #" .. child_id)
      	     		initPluginInstance(child_id)
    
    	     		-- ... and now my watch begins (setting changes)
    	     		luup.variable_watch("initPluginInstance", SID.RGB_CONTROLLER, "DeviceType", child_id)
    	     		luup.variable_watch("onDebugValueIsUpdated", SID.RGB_CONTROLLER, "Debug", child_id)
    		   end
    	     end
    	end
    
    	if (luup.version_major >= 7) then
    		luup.set_failure(0, lul_device)
    	end
    
    	return true
    end
    
    

    This does NOT do the trick, i think the MySensors device is not ready (initialized) at the moment the RGB child start up is called. I even copied all action implementations (with dummy debug log) from the I_RGBController1.xml to I_Arduino1.xml so the "no implementation errors" where all gone.

    Maybe the L_Arduino.lua needs to be adapted so that the RGB-startup function is called when MySensors is ready or implement a delayed startup for child devices in L_RGBController1.lua

    I'm not sure...



  • Just to start again from the beginning, you are trying to get the RGBW Controller plugin to work with MySensors RGBW devices. What I don't know is how MySensors presents an RGBW device - I am assuming you get a node with 4 child dimmers, but perhaps not? This is what a native Vera multi-colour dimmer typically does (eg. Fibaro.)

    One problem I foresee is that the MySensors plugin is parent to all the MySensors devices, be they actual nodes or individual sensors, so you don't get the same logical grouping with one Vera RGBW device controlling its separate R G B W children (which is what, I think, @vosmont's controller requires.)


  • Contest Winner

    @akbooer: No, since version 1.5 MySensors supports RGB and RGBW devices and in L_Arduino.lua these devices are created with D_RGBController1.xml. This is also the definition file name of vosmont's RGB-controller (coincident or not?).

    So one child is presented of type RGB or RGBW showing the vosmont's RGB color-wheel, the code i did write for the RGB-controller directly control's the RGB MyS device with MyS message's.

    luup.call_action("urn:upnp-arduino-cc:serviceId:arduino1", "SendCommand", {variableId = "RGBW", value = formerColor, radioId = pluginParams.rgbRadioId}, pluginParams.rgbArduinoId)
    

    This part of the code is working.

    You are right an alternative could be, represent 3 or 4 dimmers and configure vosmont RGB module so it controls these 3 or 4 dimmers like Fibaro. But i do like the idea of having only one tile with the color wheel.



  • @BartE OK, so the device D_RGBController1.xml is actually a child of the Arduino plugin, which handles (with your changes) the action call you showed. The problem, then is that @vosmont's plugin does not recognise this device as one it can control? Or is it that you are expecting, somehow, the colour wheel to work directly with the MYSensor plugin?

    Sorry to be so dim about this, but I'd hate to be chasing up the wrong tree.


  • Contest Winner

    @akbooer the second option, i expect the color wheel to work directly with the MySensor plugin/Arduino. Like some other supported RGB devices.

    To do so i've created a new type of device in vosmont's plugin called "MYS-RGBW", this one you can select in the RGBcontroller and as work around one has to set the MySensor plugin id and the radio id of the RGB device. But when it is created as child device this information should be known to the plugin.

    So the problem is NOT controlling the device, but having the RGBController to startup correctly as child.



  • @BartE said:

    So the problem is NOT controlling the device, but having the RGBController to startup correctly as child.

    OK, here's my confusion: you can't have the Arduino plugin AND the RGBW Controller plugin both controlling the RGBW device. By definition, it is a child of the Arduino plugin and all functionality for that child device has to be provided by the parent. Are there, perhaps, some actions defined in the services or implementation file which are not, in fact, implemented by your additional code in the Arduino plugin?


Log in to reply
 

Suggested Topics

  • 10
  • 1
  • 1
  • 5
  • 1

9
Online

11.4k
Users

11.1k
Topics

112.7k
Posts