Dimmable LED Actuator

written by hek

Create your own dimmable LED strip! And adjust the dim level from your controller over the air.

The picture on the right shows how you wrap it up in a box of your selection

Wiring Things Up

Start by connecting the radio module.

Example

/mysensors/MySensors/examples/DimmableLEDActuator/DimmableLEDActuator.ino
Last updated by tekka007, 24 Jul 2022, "Upate CI and HW defs (#1530)"
/*
 * The MySensors Arduino library handles the wireless radio link and protocol
 * between your home built sensors/actuators and HA controller of choice.
 * The sensors forms a self healing radio network with optional repeaters. Each
 * repeater and gateway builds a routing tables in EEPROM which keeps track of the
 * network topology allowing messages to be routed to nodes.
 *
 * Created by Henrik Ekblad <[email protected]>
 * Copyright (C) 2013-2019 Sensnology AB
 * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors
 *
 * Documentation: http://www.mysensors.org
 * Support Forum: http://forum.mysensors.org
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * version 2 as published by the Free Software Foundation.
 *
 *******************************
 *
 * REVISION HISTORY
 * Version 1.0 - February 15, 2014 - Bruce Lacey
 * Version 1.1 - August 13, 2014 - Converted to 1.4 (hek)
 *
 * DESCRIPTION
 * This sketch provides a Dimmable LED Light using PWM and based Henrik Ekblad
 * <[email protected]> Vera Arduino Sensor project.
 * Developed by Bruce Lacey, inspired by Hek's MySensor's example sketches.
 *
 * The circuit uses a MOSFET for Pulse-Wave-Modulation to dim the attached LED or LED strip.
 * The MOSFET Gate pin is connected to Arduino pin 3 (LED_PIN), the MOSFET Drain pin is connected
 * to the LED negative terminal and the MOSFET Source pin is connected to ground.
 *
 * This sketch is extensible to support more than one MOSFET/PWM dimmer per circuit.
 * http://www.mysensors.org/build/dimmer
 */

// Enable debug prints to serial monitor
#define MY_DEBUG

// Enable and select radio type attached
#define MY_RADIO_RF24
//#define MY_RADIO_NRF5_ESB
//#define MY_RADIO_RFM69
//#define MY_RADIO_RFM95

#include <MySensors.h>

#define SN "DimmableLED"
#define SV "1.1"

#define LED_PIN 3      // Arduino pin attached to MOSFET Gate pin
#define FADE_DELAY 10  // Delay in ms for each percentage fade up/down (10ms = 1s full-range dim)

static int16_t currentLevel = 0;  // Current dim level...
MyMessage dimmerMsg(0, V_DIMMER);
MyMessage lightMsg(0, V_LIGHT);


/***
 * Dimmable LED initialization method
 */
void setup()
{
    // Pull the gateway's current dim level - restore light level upon node power-up
    request( 0, V_DIMMER );
}

void presentation()
{
    // Register the LED Dimmable Light with the gateway
    present( 0, S_DIMMER );

    sendSketchInfo(SN, SV);
}

/***
 *  Dimmable LED main processing loop
 */
void loop()
{
}



void receive(const MyMessage &message)
{
    if (message.getType() == V_LIGHT || message.getType() == V_DIMMER) {

        //  Retrieve the power or dim level from the incoming request message
        int requestedLevel = atoi( message.data );

        // Adjust incoming level if this is a V_LIGHT variable update [0 == off, 1 == on]
        requestedLevel *= ( message.getType() == V_LIGHT ? 100 : 1 );

        // Clip incoming level to valid range of 0 to 100
        requestedLevel = requestedLevel > 100 ? 100 : requestedLevel;
        requestedLevel = requestedLevel < 0   ? 0   : requestedLevel;

        Serial.print( "Changing level to " );
        Serial.print( requestedLevel );
        Serial.print( ", from " );
        Serial.println( currentLevel );

        fadeToLevel( requestedLevel );

        // Inform the gateway of the current DimmableLED's SwitchPower1 and LoadLevelStatus value...
        send(lightMsg.set(currentLevel > 0));

        // hek comment: Is this really nessesary?
        send( dimmerMsg.set(currentLevel) );


    }
}

/***
 *  This method provides a graceful fade up/down effect
 */
void fadeToLevel( int toLevel )
{

    int delta = ( toLevel - currentLevel ) < 0 ? -1 : 1;

    while ( currentLevel != toLevel ) {
        currentLevel += delta;
        analogWrite( LED_PIN, (int)(currentLevel / 100. * 255) );
        delay( FADE_DELAY );
    }
}

Example with Rotary Encoder

If you want to attach a pushable rotary encoder and control the dim level locally at the node you can use the following example.

The wiring of the encoder is described in the sketch.

This example uses the external Bounce2 and Encoder libraries found here. Please install it and restart the Arduino IDE before trying to compile.

/**
 * The MySensors Arduino library handles the wireless radio link and protocol
 * between your home built sensors/actuators and HA controller of choice.
 * The sensors forms a self healing radio network with optional repeaters. Each
 * repeater and gateway builds a routing tables in EEPROM which keeps track of the
 * network topology allowing messages to be routed to nodes.
 *
 * Created by Henrik Ekblad <[email protected]>
 * Copyright (C) 2013-2015 Sensnology AB
 * Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors
 *
 * Documentation: http://www.mysensors.org
 * Support Forum: http://forum.mysensors.org
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * version 2 as published by the Free Software Foundation.
 *
 *******************************
 *
 * REVISION HISTORY
 * Version 1.0 - Developed by Bruce Lacey and GizMoCuz (Domoticz)
 * Version 1.1 - Modified by hek to incorporate a rotary encode to adjust 
 *                                light level locally at node
 * 
 * DESCRIPTION
 * This sketch provides an example how to implement a dimmable led light node with a rotary 
 * encoder connected for adjusting light level. 
 * The encoder has a click button which turns on/off the light (and remembers last dim-level) 
 * The sketch fades the light (non-blocking) to the desired level. 
 *
 * Default MOSFET pin is 3
 * 
 *  Arduino      Encoder module
 *  ---------------------------
 *  5V           5V (+)  
 *  GND          GND (-)
 *  4            CLK (or putput 1)
 *  5            DT  (or output 1) 
 *  6            SW (Switch/Click)  
 */

 
// Enable debug prints
#define MY_DEBUG

// Enable and select radio type attached
#define MY_RADIO_RF24
//#define MY_RADIO_RFM69

#include <MySensors.h>  
#include <Bounce2.h>
#include <Encoder.h>

#define LED_PIN 3           // Arduino pin attached to MOSFET Gate pin
#define KNOB_ENC_PIN_1 4    // Rotary encoder input pin 1
#define KNOB_ENC_PIN_2 5    // Rotary encoder input pin 2
#define KNOB_BUTTON_PIN 6   // Rotary encoder button pin 
#define FADE_DELAY 10       // Delay in ms for each percentage fade up/down (10ms = 1s full-range dim)
#define SEND_THROTTLE_DELAY 400 // Number of milliseconds before sending after user stops turning knob
#define SN "DimmableLED /w button"
#define SV "1.2"

#define CHILD_ID_LIGHT 1

#define EEPROM_DIM_LEVEL_LAST 1
#define EEPROM_DIM_LEVEL_SAVE 2

#define LIGHT_OFF 0
#define LIGHT_ON 1

int dimValue;
int fadeTo;
int fadeDelta;
byte oldButtonVal;
bool changedByKnob=false;
bool sendDimValue=false;
unsigned long lastFadeStep;
unsigned long sendDimTimeout;
char convBuffer[10];

MyMessage dimmerMsg(CHILD_ID_LIGHT, V_DIMMER);
Encoder knob(KNOB_ENC_PIN_1, KNOB_ENC_PIN_2);  
Bounce debouncer = Bounce(); 

void setup()  
{ 
  // Set knob button pin as input (with debounce)
  pinMode(KNOB_BUTTON_PIN, INPUT);
  digitalWrite(KNOB_BUTTON_PIN, HIGH);
  debouncer.attach(KNOB_BUTTON_PIN);
  debouncer.interval(5);
  oldButtonVal = debouncer.read();

  // Set analog led pin to off
  analogWrite( LED_PIN, 0);

  // Retreive our last dim levels from the eprom
  fadeTo = dimValue = 0;
  byte oldLevel = loadLevelState(EEPROM_DIM_LEVEL_LAST);
  Serial.print("Sending in last known light level to controller: ");
  Serial.println(oldLevel);  
  send(dimmerMsg.set(oldLevel), true);   

  Serial.println("Ready to receive messages...");  
}

void presentation() {
  // Send the Sketch Version Information to the Gateway
  present(CHILD_ID_LIGHT, S_DIMMER);
  sendSketchInfo(SN, SV);
}

void loop()      
{
  // Check if someone turned the rotary encode
  checkRotaryEncoder();
  
  // Check if someone has pressed the knob button
  checkButtonClick();
  
  // Fade light to new dim value
  fadeStep();
}

void receive(const MyMessage &message)
{
  if (message.type == V_LIGHT) {
    // Incoming on/off command sent from controller ("1" or "0")
    int lightState = message.getString()[0] == '1';
    int newLevel = 0;
    if (lightState==LIGHT_ON) {
      // Pick up last saved dimmer level from the eeprom
      newLevel = loadLevelState(EEPROM_DIM_LEVEL_SAVE);
    } 
    // Send dimmer level back to controller with ack enabled
    send(dimmerMsg.set(newLevel), true);
    // We do not change any levels here until ack comes back from gateway 
    return;
  } else if (message.type == V_DIMMER) {
    // Incoming dim-level command sent from controller (or ack message)
    fadeTo = atoi(message.getString(convBuffer));
    // Save received dim value to eeprom (unless turned off). Will be
    // retreived when a on command comes in
    if (fadeTo != 0)
      saveLevelState(EEPROM_DIM_LEVEL_SAVE, fadeTo);
  }
  saveLevelState(EEPROM_DIM_LEVEL_LAST, fadeTo);
  
  Serial.print("New light level received: ");
  Serial.println(fadeTo);

  if (!changedByKnob) 
    knob.write(fadeTo); 
    
  // Cancel send if user turns knob while message comes in
  changedByKnob = false;
  sendDimValue = false;

  // Stard fading to new light level
  startFade();
}



void checkRotaryEncoder() {
  long encoderValue = knob.read();

  if (encoderValue > 100) {
    encoderValue = 100;
    knob.write(100);
  } else if (encoderValue < 0) {
    encoderValue = 0;
    knob.write(0);
  }

  if (encoderValue != fadeTo) { 
    fadeTo = encoderValue;
    changedByKnob = true;
    startFade();
  }
}

void checkButtonClick() {
  debouncer.update();
  byte buttonVal = debouncer.read();
  byte newLevel = 0;
  if (buttonVal != oldButtonVal && buttonVal == LOW) {
    if (dimValue==0) {
      // Turn on light. Set the level to last saved dim value
      int saved = loadLevelState(EEPROM_DIM_LEVEL_SAVE);
      newLevel = saved > 0 ? saved : 100;
    }
    send(dimmerMsg.set(newLevel),true);
  }
  oldButtonVal = buttonVal;
}

void startFade() {
  fadeDelta = ( fadeTo - dimValue ) < 0 ? -1 : 1;
  lastFadeStep = millis();
}

// This method provides a graceful none-blocking fade up/down effect
void fadeStep() {
  unsigned long currentTime  = millis();
  if ( dimValue != fadeTo && currentTime > lastFadeStep + FADE_DELAY) {
    dimValue += fadeDelta;
    analogWrite( LED_PIN, (int)(dimValue / 100. * 255) );
    lastFadeStep = currentTime;
    
    Serial.print("Fading level: ");
    Serial.println(dimValue);

    if (fadeTo == dimValue && changedByKnob) {
      sendDimValue = true;
      sendDimTimeout = currentTime;
    }
  } 
  // Wait a few millisecs before sending in new value (if user still turns the knob)
  if (sendDimValue && currentTime > sendDimTimeout + SEND_THROTTLE_DELAY)  {
     // We're done fading.. send in new dim-value to controller.
     // Send in new dim value with ack (will be picked up in incomingMessage) 
    send(dimmerMsg.set(dimValue), true); // Send new dimmer value and request ack back
    sendDimValue = false;
  }
}

// Make sure only to store/fetch values in the range 0-100 from eeprom
int loadLevelState(byte pos) {
  return min(max(loadState(pos),0),100);
}
void saveLevelState(byte pos, byte data) {
  saveState(pos,min(max(data,0),100));
}

Datasheets

NameSize# Downloads
irlz44n.pdf107.47 kB4126

Shopping Guide

10pcs DIP IRLZ44N Logic Level Transistor MOSFET
Use these to enable dimming of 12/24V leds and led lists
undefined   Buy
undefined   Buy
10pcs Rotary Knobs
16mm top and 6mm shaft insert diameter.
1 available - $9.99 ($1.00 / Count)   Buy
undefined   Buy
12V 5M single color LED strip
Select between 3528/5050/5630 LEDs.
Unavailable   Buy
undefined   Buy
24V 5m Waterproof 5050 RGBW SMD LED Strip
60leds/m. RGB+White led strip.
Unavailable   Buy
undefined   Buy
5pcs Rotary Encoder
KY-040. Can register endless turns in each direction. Build in push-button.
Unavailable   Buy
undefined   Buy
Waterproof Transformer for LED Strip
Different models. AC/DC Power Adapter
Unavailable   Buy
undefined   Buy

Gateways

Sensors & Actuators

Related Content

Comments