Introduction: Multichannel Wireless Light Dimmer

About: Curious and Passionate about Electronics.

Save Energy Beautifully !!

By using a light dimmer in one’s living room, it’s possible to completely alter the mood and ambience of a room at the drop of a hat.
The Internet of Things is set to disrupt the way we live and work. Smart homes filled with connected products are loaded with possibilities to make our lives easier, more convenient, and more comfortable.

With Wi-Fi connectivity present in the dimming controller, the above benefits get reflected in this module. However, this actually carries with it a host of less obvious but equally favorable benefits such as reduced energy consumption and improved longevity - longer the bulbs are dimmed, lesser the energy they use and longer they last.

This project aims to

● Control the intensity of the dimmable lighting appliances via a trailing edge dimmer.

● Provide the user , ability to control lightsindependently and wirelessly over the Wi-Fi network and the Internet!

Step 1: Specifications

  • 2 channel dimmer module for indoor lighting applications (# channels can be easily expanded as per need)
  • Can be easily setup in Trailing Edge/Leading Edge configuration with some small variations in the software,hardware remains the same!!
  • Rating: 200 W/channel
  • No minimum load on any channel (True 3 wire dimmer!!)
  • Wireless control via Wifi (Uses the popular NodeMCU module)
  • Web Interface for controlling dimmer settings
  • Can be controlled from any device with browser support (Chrome, Firefox)
  • Supports OTA(Over the air programming) for software updates

Nice..,isn't it you too can make one for yourselves!!,I have spent a few months understanding and making this dimmer module.

You will find all the source files,step by step instructions and things I experienced while making this project compiled in this instructable.

I hope you would learn a lot and enjoy as you proceed further!!

Step 2: Understanding Light Dimmers

There are two main categories of traditional AC dimmers (also known as 'phase-cut' dimmers)

  • Leading Edge Dimmers
  • Trailing Edge Dimmers

These vary the brightness of the lighting appliance by varying the rms voltage applied to the load.

This design uses a Trailing Edge Dimmer

They provide a 'soft start', increasing the voltage to the lamp relatively slowly.

With incandescent lamps, this almost eliminates 'thermal shock' - that brief period at switch-on where the lamp draws around 10 times the normal operating current.
By including the soft-start feature lamp life is increased, but it doesn't help CFLs or LED lamps much.

Trailing edge dimmers commonly use a MOSFET, as these require almost no control current and are rugged and reliable.

Another option is to use an IGBT (insulated gate bipolar transistor), which combines the advantages of both MOSFET and bipolar transistor. These are generally more expensive than MOSFETs.

Step 3: System Block Diagram

Step 4: Components

  • ESP8266(NodeMCU) - 1
  • 4N35 - 3(Optocoupler IC)

  • 1N4148 - 4

  • HLK-PM01 - 1 (Mains to 5v dc conversion module)

  • IRF740 - 4 (N-MOSFET)
  • Fuse (2 A) ,SH22 Type Fuse Holder - 1

  • BA159 (Fast Switching Rectifiers) - 2
  • R 33K ½ W - 2

  • R 10K ⅛ W - 3

  • R 15K ½ W - 4

  • R 430 ⅛ W - 2
  • R 100 ⅛ W - 2

  • C 47uF - 4

  • C 10uF - 1

  • Zener Diodes(12 V) - 2
  • 6 pin IC Holders - 3 (For 4N35)
  • Female Header Pins (For ESP8266)
  • Incandescent Bulbs
  • Plastic Switch-box

Step 5: Schematic(The Trailing Edge Dimmer)

(Refer pdf version for better resolution)

Each subsection of the schematic has been explained in detail further.

Step 6: Zero Crossing Detection Unit

  • Diodes D3,D4,D5,D6 form a bridge rectifier.
  • U2 is an optocoupler(4N35), and its LED is powered via R6 and R7, and then from the bridge rectifier.
  • The interrupt signal is provided to NodeMCU via pin D6.
  • R8 is a pull-up resistor.
  • The zero crossing detector, gives a negative-going pulse when the mains voltage is close to zero.
  • This is used to synchronize the mcu to the mains, and is really the heart of the circuit.
  • A series string of 33K resistors is used instead of 66K resistor to divide the total of 1W power dissipation.

Step 7: 12V DC Supply

To minimize the switching losses in the mosfet IRF740, the gate control signal from the mcu is scaled to 12V.

Note: Each Channel needs it’s own dc supply.

  • A Fast switching diode BA159 is used to rectify the AC input.
  • D2 is a 12V zener diode

Step 8: MOSFET Driver

  • The AC switch is formed by two N-Mosfets in a series configuration
  • 4N35 IC provides the necessary isolation from the mains
  • The control signals from the MCU are fed to the optocouplers U1,U3

DC supply to MCU is provided by 220V AC to 5V DC 3W PCB Mounted Plastics Enclosed Isolated Switching Step-Down Power Supply Module (HLK- PM01) having compact form factor.

Step 9: Putting Everything Together

After understanding each of the sub units,functioning of the entire circuit is illustrated here.

Step 10: Programming the ESP8266 !!

Arduino IDE is used to program the NodeMCU ESP8266 module.

  • To use ESP8266 with arduino IDE follow the steps given below,
    • Start Arduino and open Preferences window.

    • Enter - "http://arduino.esp8266.com/staging/package_esp8266com_index.json" into Additional Board Manager URLs field.

    • Open Boards Manager from Tools > Board menu and install esp8266 platform

    • Select NodeMCU 1.0 (ESP 12E module) board from Tools > Board menu after installation

    • Install WebSockets library by markus sattler from Sketch > Include Library > Manage Libraries
  • Please refer following link for getting started with Arduino IDE for ESP8266
    https://github.com/esp8266/Arduino

  • Download the source code from the link provided,https://github.com/agkonale/Multichannel-Wireless-Dimmer/tree/master/Arduino

  • The Source contains following files

    • Trailing_Edge_Dimmer_v5.ino
    • dimmer.h
    • hw_timer.c
    • hw_timer.h
  • Using long delays is highly discouraged while using ESP8266 as there is a lot of code that needs to run on the chip besides the sketch when WiFi is connected which may lead to frequent WDT resets.Hence we use hw_timers to save ourselves from this trouble.

  • The hw_timer.c,hw_timer.h files provide necessary functions to use hardware timer present in ESP8266.
    • hw_timer_init(FRC1_TIMER_SOURCE_TYPE source_type, u8 req) function initializes the timer of ESP8266, if req argument is 0 the timer would start in non-auto load mode else it would start in auto load mode.
    • hw_timer_set_func(void (* user_hw_timer_cb_set)(void)) function sets the isr for handling the timer interrupt.

    • hw_timer_arm(u32 val) starts the timer with appropriate timing interval.

  • dimmer.h

Include the necessary library files.

#include <ESP8266WiFi.h>

#include <WiFiClient.h>

#include <ESP8266WebServer.h>

#include <ESP8266mDNS.h>

#include <ESP8266HTTPUpdateServer.h>

#include <Arduino.h>

#include <WebSocketsServer.h>

#include <Hash.h><br>

Next we define the dimmer module properties.

#define NUM_CHANNELS 2
#define ZERO_CROSSING_INT_PIN 12
#define DELTA 4               //(t_zero_crossing - t_interrupt)/STEP_TIME
#define STEP_TIME 78          //for 128 lvls (in uS) (65 for 50 Hz)

//128 lvl brightness control 
int Dimming_Lvl[NUM_CHANNELS] = {0,0}; //(0-127) 

int Drive_Pin[NUM_CHANNELS] = {5,4};
int State[NUM_CHANNELS] = {0,0};

//Wifi Access Point Settings
String ssid = "-----------";
String password = "-----------";

volatile boolean isHandled[NUM_CHANNELS] = {0,0};
volatile int Lvl_Counter[NUM_CHANNELS] = {0,0};

NUM_CHANNELS can be increased as per your need but you will have make necessary replications where ever needed.(This design is scalable to more than 2 channels!!)

STEP_TIME defines the interval over which the timer ISR is called repeatedly.It is calculated as follows:

STEP_TIME: 1/(2*frequency*NumLvls)* 10^6
Default :

frequency = 50 Hz NumLvls = 128

ssid : Network SSID
password : Network Password

Drive_Pin : Array consisting of Pin Numbers of NodeMCU (Please use proper pin numbers) ZERO_CROSSING_INT_PIN : Interrupt Pin

DELTA : There might be some fixed mismatch between the actual zero-crossing and the interrupt,which is removed by this constant.

-

-

Next we add a function to connect the ESP8266 module to the WiFi access point.

int connectToWiFi() 
{
  WiFi.begin(ssid.c_str(), password.c_str());
  
  int i=0;
  while (WiFi.status() != WL_CONNECTED) 
  {
    if (i == 30) 
    {
      return -1;
    }
    delay(1000);
    DEBUG_PRINT(".");
    i++;
  } 
  DEBUG_PRINTLN("");
  DEBUG_PRINTLN("Connected to ");
  DEBUG_PRINTLN(ssid);
  DEBUG_PRINTLN("IP address: ");
  DEBUG_PRINTLN(WiFi.localIP());
  
  return 0;
}

-

-

Now to start the WebSocket Connection WebSocketConnect() function is defined.

// WebSocket Connection

void WebSocketConnect() 
{
  webSocket.begin();
  webSocket.onEvent(webSocketEvent);
}

-

-

Zero_Crossing_Int() is the ISR attached to the ZERO_CROSSING_INT_PIN to detect the zero crossings.

void Zero_Crossing_Int()

{
  if(NumActiveChannels > 0)
  {
    NumHandled = 0;

    for(int i=0; i<NUM_CHANNELS, i++)
    {
	isHandled[i] = 0;       

	if(State[i] == 1)
      {
        digitalWrite(Drive_Pin[i], HIGH);
      }

    }  
    zero_cross = 1; 
  }
}

-

-

dimTimerISR() is the timer ISR which is served every STEP_TIME uSeconds.We maintain counters for each active channel and compare/update them every cycle.

void dimTimerISR()
{
  if(zero_cross == 1)                     
  {
    for(int i = 0; i < NUM_CHANNELS; i++) 
    {
      if(State[i] == 1)
      {
        if(Lvl_Counter[i] > Dimming_Lvl[i] + DELTA)       
        { 
          digitalWrite(Drive_Pin[i], LOW);     
          Lvl_Counter[i] = 0;  
          isHandled[i] = 1; 
          
          NumHandled++;
          if(NumHandled == NumActiveChannels)
          {    
            zero_cross = 0;     
          }
        } 
        else if(isHandled[i] == 0)
        {
          Lvl_Counter[i]++;                     
        }          
     }
   }
  }
}

-

-

To manage the state(ON/OFF) of the channels following function is defined.

void Update_State(int ON_OFF,int Channel_Number)
{ 
  if(State[Channel_Number] == 0 && ON_OFF == 1)
  {
    NumActiveChannels++;
  }
  else if(State[Channel_Number] == 1 && ON_OFF == 0)
  {
    NumActiveChannels--;
  }
  State[Channel_Number] = ON_OFF;
}

-

-

webSocketEvent() function develops the communication protocol between the ESP8266 and the clients.

void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length) 
{
  switch (type) {
    case WStype_DISCONNECTED:
      break;
    case WStype_CONNECTED:
      {
        IPAddress ip = webSocket.remoteIP(num);
        lastChangeTime = millis();
        isSynced = 0;
        DEBUG_PRINTLN(ip);
      }
      break;
      
    case WStype_TEXT:
      {
        String text = String((char *) &payload[0]);
        lastChangeTime = millis();
        isSynced = 0;
              
        if(text.startsWith("a"))
        {
          String aVal=(text.substring(text.indexOf("a")+1,text.length())); 
          int Lvl_0 = aVal.toInt();
          Dimming_Lvl[0] = Lvl_0;
          DEBUG_PRINTLN(Lvl_0);
        }
  
        if(text.startsWith("b"))
        {
          String bVal=(text.substring(text.indexOf("b")+1,text.length())); 
          int Lvl_1 = bVal.toInt();
          Dimming_Lvl[1] = Lvl_1;
          DEBUG_PRINTLN(Lvl_1);
        }

	if(text=="ON_0")
        {
          Update_State(1,0);
          DEBUG_PRINTLN("Channel 0 ON!!");         
        }
          
        if(text=="OFF_0")
        {
          Update_State(0,0);
          digitalWrite(Drive_Pin[0], LOW);
          DEBUG_PRINTLN("Channel 0 OFF!!");
        }
         
        if(text=="ON_1")
        {
          Update_State(1,1);
          DEBUG_PRINTLN("Channel 1 ON!!");        
        }
          
        if(text=="OFF_1")
        {
          Update_State(0,1);
          digitalWrite(Drive_Pin[1], LOW);
          DEBUG_PRINTLN("Channel 1 OFF!!");
        }    
      }
      //webSocket.sendTXT(num, payload, length);
      //webSocket.broadcastTXT(payload, length);
      break;

    case WStype_BIN:
      hexdump(payload, length);
      //webSocket.sendBIN(num, payload, length);
      break;
  }
}

-

-

timer_init() function:

void ICACHE_FLASH_ATTR timer_init(void)
{
  //FRC1_SOURCE/NMI_SOURCE
  hw_timer_init(FRC1_SOURCE, 1);
  hw_timer_set_func(dimTimerISR);
  hw_timer_arm(STEP_TIME);
}
  • Trailing_Edge_Dimmer_v5.ino file
#include "hw_timer.h"

#include "dimmer.h"

void setup() 
{
  Serial.begin(115200); 
  pinMode(ZERO_CROSSING_INT_PIN,INPUT);
  
  for(int i=0; i<NUM_CHANNELS, i++)
{
  pinMode(Drive_Pin[i],OUTPUT);<br>  digitalWrite(Drive_Pin[i],LOW);
}
  connectToWiFi();  
  WebSocketConnect();
  MDNSConnect();
  HTTPUpdateConnect();

  noInterrupts();  
  timer_init();
  attachInterrupt(ZERO_CROSSING_INT_PIN,Zero_Crossing_Int,RISING);   
  interrupts();
}

void loop() 
{
  if(millis() - lastConnectivityCheckTime > 1000)
  {
    if(WiFi.status() != WL_CONNECTED) 
    {
      connectToWiFi();
      WebSocketConnect();
      MDNSConnect();
    }  
    lastConnectivityCheckTime = millis();
  }

  else 
  {
    webSocket.loop();
    //yield();
    
    //OTA
    if (millis() - lastTimeHost > 10) 
    {
      httpServer.handleClient();
      lastTimeHost = millis();
    }

    //Update Connected Clients
    currentChangeTime = millis();
    if(currentChangeTime - lastChangeTime> 300 && isSynced == 0) 
    {
      String websocketStatusMessage = "A" + String(Dimming_Lvl[0]) + ",B" + String(Dimming_Lvl[1]) + ",X" + String(State[0]) + ",Y" + String(State[1]);
      webSocket.broadcastTXT(websocketStatusMessage); // Tell all connected clients current state of the channels
      isSynced = 1;
    }
  }
}

Note:

OTA update can be done by accessing IP/update in the browser and uploading the compiled binary file. (Works fine for NumLvls = 64)

Eliminating Flickering and wdt crashes (as there is a lot of code that needs to run on the ESP8266 besides the sketch when WiFi is connected) is a major issue while dealing with the ESP8266.


Managing delays of the independent channels to ensure correct trigger positions for multiple loads could be tried by sorting the delays as well(Please let me know if you are successful with this method as i haven't got reasonable results with it)

Automatic frequency detection can be added to reduce flickering due changes in power line characteristics.

Step 11: Designing the Web App

      • The Web App source is a simple HTML file and uses WebSockets for handling clients.
      • Currently one needs to enter the IP address of the access point to get started (which is saved in the device automatically for future use)
      • The Network SSID and Password are hard coded into the system(You would need to modify the code to dynamically change the network)
      • The Web App Controls channel states (brightness, ON/OFF) via sliders, toggle switches.
      • Can be deployed on any device that has access to browsers like chrome/Firefox and Wi-Fi connectivity. (Note: the sliders are sluggish on android devices,may need some UI improvement)
      • Displays Real Time Channel states.
      • Eliminates the hassles of single remote as any number of users can directly control the settings via smartphones/laptops/tablets.

      Source Code

      The following link gives a good glimpse of using WebSockets with ESP8266

      http://www.whatimade.today/esp8266-on-websockets-mdns-ota-and-leds/

      Step 12: PCB Design, the Fun Part!!

      • Follow these tutorials for setup and basic understanding of EAGLE CAD software:

      -Installation and Setup

      -Using eagle schematic

      -Using eagle board layout

      • The PCB was designed in EAGLE CAD(7.2.0)
      • The EAGLE Files have been provided in this step.


      Note:

      Please place the components(especially diodes,4N35 and other polarity sensitive components) in proper orientation while soldering.You may want to lay the silkscreen (if possible) for the same.

      Step 13: Caution!!

      Now that the PCB is ready it is extremely important to take the following precautions before testing the module for the first time (Don't be overwhelmed have some patience!!!)

      • Adding 2 - 200W bulbs in series with the test circuit (between the mains power supply and the input terminal of the module) as shown in the image.
      • Covering the PCB with plastic encasing.
      • Please use mounting screws for the PCB.
      • Cover your eyes with glasses to prevent any accidental damage.

      Now you are ready to test the design safely,always remember SAFETY FIRST,especially for newbies..

      (Henceforth, you may remove the series bulbs and test the pcb directly from mains supply)

      If things go fine you have successfully made a dimmer module, Congratulations!!!

      Else check your soldering work again with a DMM.

      Step 14: Final Product!!

      The finished Dimmer module can be used to control the ambience of your room conveniently.

      I have added some videos to demonstrate/explain working of the prototype and an image showing it's installation in my home.

      Now it's time to make yours!!!

      If you happen to do some creative work do let me know.

      Step 15: Simulating Your Design(In Case You Want to Improve the Design and Verify It)

      • LTspice XVII

      LTspice is a high performance SPICE simulator, schematic capture and waveform viewer, and above all it's free!!

      Download Link

      LTspice Tutorials

      (For adding third party models - IRF740,Incandescent Lamp etc.)

      LTspice Project Simulation Files (some of the model files,along with the simulation file used have been provided in this step)

      Acknowledgements:

      • Ryan (For many helpful discussions)

      If you like my Instructable please do vote for me in the Lighting Contest !!