AC Dimmer with OpenHab



  • Here my code.

    /*
    AC Light Control
    Uses up and down buttons to set levels
    makes use of a timer interrupt to set the level of dimming
    */
    #include <SPI.h>
    #include <MySensor.h>  
    #include <TimerOne.h>
    #include <Bounce2.h>
    #define SN "AC Dimmer control"
    #define SV "1.0"
    #define NODE_ID 30  //change to a number to assign a specific ID
    #define FADE_DELAY 100  // Delay in ms for each percentage fade up/down (10ms = 1s full-range dim)
    #define FADE_PERCENTAGE 5 //The percentage the fade level will be changed when a button is pressed
    volatile int i=0;               // Variable to use as a counter of dimming steps. It is volatile since it is passed between interrupts
    volatile boolean zero_cross=0;  // Flag to indicate we have crossed zero
    int AC_pin = 3;                 // Output to Opto Triac
    int UP_BUTTON_PIN = 5;                 // Arduino Digital I/O pin number for the fade up button 
    int DOWN_BUTTON_PIN = 6;                 // Arduino Digital I/O pin number for the fade down button 
    int POWER_BUTTON_PIN = 7;                 // Arduino Digital I/O pin number for the power button 
    
    int freqStep = 75;              // This is the delay-per-brightness step in microseconds. It allows for 128 steps
                                    // If using 60 Hz grid frequency set this to 65
    MySensor gw;
    //Tuy chinh lai
    static int currentLevel = 128;  // Current dim level...
    uint8_t fadeLevel = 128; //used to store the fade level when using the buttons
    uint8_t upPreviousValue;
    uint8_t downPreviousValue;
    uint8_t powerPreviousValue;
    
    Bounce upDebouncer = Bounce();
    Bounce downDebouncer = Bounce();
    Bounce powerDebouncer = Bounce();
    MyMessage dimmerMsg(AC_pin, V_DIMMER);
    MyMessage lightMsg(AC_pin, V_LIGHT);
    
    unsigned long previousMillis = 0; // last time update 
    unsigned long upPreviousMillis = 0;
    unsigned long downPreviousMillis = 0; 
    unsigned long buttonFadeDelay = 200; 
    
    // =Het tuy chinh lai
    
    
    void setup() {  // Begin setup
      Serial.begin(115200);
      /// - Setup Mysensors
      Serial.println( SN ); 
      gw.begin( incomingMessage,  NODE_ID, true);
      // Register the LED Dimmable Light with the gateway
      gw.present( 3, S_DIMMER );
      
      gw.sendSketchInfo(SN, SV);
      // Pull the gateway's current dim level - restore light level upon sendor node power-up
      gw.request( 3, V_DIMMER );
      // - Het setup mysensors
      //Setup the button
      /*pinMode(buton1, INPUT);  // set buton1 pin as input
      pinMode(buton2, INPUT);  // set buton1 pin as input
      pinMode(buton3, INPUT);  // set buton1 pin as input*/
         // Setup the button
      pinMode(UP_BUTTON_PIN,INPUT);
      pinMode(DOWN_BUTTON_PIN,INPUT);
      pinMode(POWER_BUTTON_PIN,INPUT);
      
      // Activate internal pull-up
      digitalWrite(UP_BUTTON_PIN,LOW);
      digitalWrite(DOWN_BUTTON_PIN,LOW);
      digitalWrite(POWER_BUTTON_PIN,LOW);
      
      // After setting up the button, setup debouncer
      upDebouncer.attach(UP_BUTTON_PIN);
      downDebouncer.attach(DOWN_BUTTON_PIN);
      powerDebouncer.attach(POWER_BUTTON_PIN);
      upDebouncer.interval(5);
      downDebouncer.interval(5);
      powerDebouncer.interval(5);
      
      //Setup AC PIN
      pinMode(AC_pin, OUTPUT);                          // Set the Triac pin as output
      attachInterrupt(0, zero_cross_detect, RISING);    // Attach an Interupt to Pin 2 (interupt 0) for Zero Cross Detection
      Timer1.initialize(freqStep);                      // Initialize TimerOne library for the freq we need
      Timer1.attachInterrupt(dim_check, freqStep);      // Go to dim_check procedure every 75 uS (50Hz)  or 65 uS (60Hz)
      // Use the TimerOne Library to attach an interrupt
      
    }
    
    void zero_cross_detect() {    
      zero_cross = true;               // set flag for dim_check function that a zero cross has occured
      i=0;                             // stepcounter to 0.... as we start a new cycle
      digitalWrite(AC_pin, LOW);
    }                                 
    
    // Turn on the TRIAC at the appropriate time
    // We arrive here every 75 (65) uS
    // First check if a flag has been set
    // Then check if the counter 'i' has reached the dimming level
    // if so.... switch on the TRIAC and reset the counter
    void dim_check() {                   
      if(zero_cross == true) {              
        if(i>=fadeLevel) {                     
          digitalWrite(AC_pin, HIGH);  // turn on light       
          i=0;  // reset time step counter                         
          zero_cross=false;    // reset zero cross detection flag
        } 
        else {
          i++;  // increment time step counter                     
        }                                
      }    
    }                                      
    
    void loop() {  
        gw.process();
        /*
    There are 3 buttons attached to the mirror.  One is an on/off button and the other two will fade up/fade down.  
    The sensor will remember the last fade state and fade the lights to that level next time they are turned on.
    
    If fade up or fade down button is pressed it should store that value into a variable and when the ON button is pressed
    it will fade to that previous value.
     */ 
     //up button
      upDebouncer.update();
        // Get the update value
        uint8_t upValue = upDebouncer.read();
        
        unsigned long upCurrentMillis = millis();
        
        if(upCurrentMillis - upPreviousMillis > buttonFadeDelay){
          if ((upValue == HIGH) && (fadeLevel<128)) { //Because of the internal pullup resistors LOW = button is presssed
               fadeLevel = fadeLevel < 8 ? 0 : fadeLevel;
               if (fadeLevel > 8)
              { fadeLevel -= (FADE_PERCENTAGE * 1.28);
            } else {
              fadeLevel = 8;
            }
               fadeToLevel( fadeLevel );
               Serial.println(fadeLevel);
          }
          upPreviousMillis = upCurrentMillis;
        }
      
     // down button
     downDebouncer.update();
        // Get the update value
        uint8_t downValue = downDebouncer.read();
        
        unsigned long downCurrentMillis = millis();
        
        if(downCurrentMillis - downPreviousMillis > buttonFadeDelay){
          if ((downValue == HIGH) && (fadeLevel>0)) {                
             fadeLevel += (FADE_PERCENTAGE * 1.28);
             fadeLevel = fadeLevel > 128 ? 121 : fadeLevel;
             fadeToLevel( fadeLevel );
             Serial.println(fadeLevel);                 
          }
          downPreviousMillis = downCurrentMillis;
        }
     // Power button
     powerDebouncer.update();
        // Get the update value
        uint8_t powerValue = powerDebouncer.read();
        
        if(powerValue != powerPreviousValue){
          if (powerValue == HIGH) {
          Serial.print("Power Button Pressed. fadeLevel is ");
          Serial.println(fadeLevel);
         
            if (currentLevel < 128) {
              
             fadeToLevel( 128 );
             fadeLevel = 128;
            }
            else{
              if (fadeLevel == 128) {
                fadeToLevel(64);
                fadeLevel = 64;
              }
              else{
                fadeToLevel(fadeLevel);
              }
            }
          }
          powerPreviousValue = powerValue;
        }
     
    }
    void incomingMessage(const MyMessage &message) {
      if (message.type == V_LIGHT || message.type == 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.type == V_LIGHT ? 100 : 1 );
        
        // Clip incoming level to valid range of 0 to 100
        requestedLevel = requestedLevel > 100 ? 100 : requestedLevel;
        requestedLevel = requestedLevel < 0   ? 0   : requestedLevel;
        
       
        float percent_level;
        percent_level = 128 - (requestedLevel * 1.28);
        fadeToLevel( percent_level );
         Serial.print( "Changing level to " );
        Serial.print( requestedLevel );
        Serial.print( ", from " ); 
        Serial.println( currentLevel );
        // Inform the gateway of the current DimmableLED's SwitchPower1 and LoadLevelStatus value...
        gw.send(lightMsg.set(currentLevel > 0 ? 1 : 0));
    
        // hek comment: Is this really nessesary?
        gw.send( dimmerMsg.set(currentLevel) );
    
       
        }
    }
    
    /***
     *  This method provides a graceful fade up/down effect
     */
    void fadeToLevel( int toLevel ) {
      Serial.print("toLevel Value: ");
     Serial.println(toLevel);
    
      
      int delta = ( currentLevel - toLevel ) < 0 ? 1 : -1;
      Serial.print("delta Value: ");
     Serial.println(delta);
      while ( currentLevel != toLevel ) {
        currentLevel += delta;
       // analogWrite( MIRROR_LED_PIN, (int)(currentLevel / 100. * 255) );
        delay( FADE_DELAY );
        
         fadeLevel = toLevel;
      }
    
       // Inform the gateway of the current DimmableLED's SwitchPower1 and LoadLevelStatus value...
    // gw.send(lightMsg.set(currentLevel > 0 ? 1 : 0));  //used to send status of light (on/off) to Vera
      gw.send( dimmerMsg.set((int)currentLevel/100*128) );
      
    }
    
    

    OpenHab item

    Dimmer	Dimmed_Light	"Dimmer Light [%d %%]"	<slider>	(all,node1)		{ mqtt=">[mymqtt:/MyMQTT/30/3/V_DIMMER:state:*:default]" }
    

    Openhab sitemap

    Slider item=Dimmed_Light
    

    Openhab Rules

    /**
     * This is a demo rule which simulates a real dimmer by reacting to increase/decrease commands 
     * and posting an updated state on the bus 
     */
    rule "Dimmed Light"
    	when
    		Item Dimmed_Light received command
    	then
    		var Number percent = 100
    		if(Dimmed_Light.state instanceof DecimalType) percent = Dimmed_Light.state as DecimalType 
    			
    		if(receivedCommand==INCREASE) percent = percent + 5
    		if(receivedCommand==DECREASE) percent = percent - 5
    
    		if(percent<0)   percent = 0
    		if(percent>100) percent = 100
    		
    		postUpdate(Dimmed_Light, percent);
    end
    

    I have a problem:
    When i use fade up or fade down , openhab don't receive a value. But i use the fade up and fade down from openhab to AC module , the light dimmer okie.
    How can i fix it ?
    Thanks



  • @quocanhcgd Can you share the A/C dimmer design? I'm trying to do the same. Found many examples but building a circuit with a triac and zerocrossing.
    Thanks,
    Jeff



  • @quocanhcgd Can you share the A/C dimmer design?


  • Hardware Contributor

    Working on a solution for 220V/50 hz based on http://www.instructables.com/id/Arduino-controlled-light-dimmer-The-circuit/

    Dimmer.fzz

    I didn't have a symbol for TIC206 and H11AA1, so be careful and read the schematics.

    When all parts have arrived and I had time to test it, I will report here.

    https://oshpark.com/shared_projects/3skWWYHF



  • @FotoFieber I got a circuit working, But I don't know how to write the code to dim, and work remotely. I used BT137 Triacs instead of TIC206 because of availability. The main issue I had with a 110V/60Hz was the resistor pulling the zerocross Pin high, It would not work but when I removed the resistor all functional?! The schematic I used it was from Inmojo here it is http://bit.ly/1C1xCOS
    It works well the only adjustments needed is how low and how high you dim the circuit. Now the code part is what I need. I will give the above code a spin, It looks like the dimmer mirror code from @petewill.



  • were you (someone) able to debug the 'fading' funciton in the code? i think its a problem in the while loop in the arduino/mysensors code the while loop is done, but only when the while loop is comletely through , the 'dimvalue' gets updated/changed

    EDIT:
    this works!

    /*
    AC Light Control
    Uses up and down buttons to set levels
    makes use of a timer interrupt to set the level of dimming
    */
    #include <SPI.h>
    #include <MySensor.h>  
    #include <TimerOne.h>
    
    #define SN "AC Dimmer control"
    #define SV "1.0"
    #define NODE_ID 30  //change to a number to assign a specific ID
    #define FADE_DELAY 50  // Delay in ms for each percentage fade up/down (10ms = 1s full-range dim)
    #define FADE_PERCENTAGE 5 //The percentage the fade level will be changed when a button is pressed
    volatile int i=0;               // Variable to use as a counter of dimming steps. It is volatile since it is passed between interrupts
    volatile boolean zero_cross=0;  // Flag to indicate we have crossed zero
    int AC_pin = 6;                 // Output to Opto Triac
    
    int freqStep = 75;              // This is the delay-per-brightness step in microseconds. It allows for 128 steps
                                    // If using 60 Hz grid frequency set this to 65
    MySensor gw;
    //Tuy chinh lai
    static int currentLevel = 128;  // Current dim level...
    uint8_t fadeLevel = 128; //used to store the fade level when using the buttons
    uint8_t upPreviousValue;
    uint8_t downPreviousValue;
    uint8_t powerPreviousValue;
    
    
    MyMessage dimmerMsg(AC_pin, V_DIMMER);
    MyMessage lightMsg(AC_pin, V_LIGHT);
    
    
    
    // =Het tuy chinh lai
    
    
    void setup() {  // Begin setup
      Serial.begin(115200);
      /// - Setup Mysensors
      Serial.println( SN ); 
      gw.begin( incomingMessage,  NODE_ID, true);
      // Register the LED Dimmable Light with the gateway
      gw.present( 6, S_DIMMER );
      
      gw.sendSketchInfo(SN, SV);
      // Pull the gateway's current dim level - restore light level upon sendor node power-up
      gw.request( 6, V_DIMMER );
    
    
      
      //Setup AC PIN
      pinMode(AC_pin, OUTPUT);                          // Set the Triac pin as output
      attachInterrupt(1, zero_cross_detect, RISING);    // Attach an Interupt to Pin 2 (interupt 0) for Zero Cross Detection
      Timer1.initialize(freqStep);                      // Initialize TimerOne library for the freq we need
      Timer1.attachInterrupt(dim_check, freqStep);      // Go to dim_check procedure every 75 uS (50Hz)  or 65 uS (60Hz)
      // Use the TimerOne Library to attach an interrupt
      
    }
    
    void zero_cross_detect() {    
      zero_cross = true;               // set flag for dim_check function that a zero cross has occured
      i=0;                             // stepcounter to 0.... as we start a new cycle
      digitalWrite(AC_pin, LOW);
    }                                 
    
    // Turn on the TRIAC at the appropriate time
    // We arrive here every 75 (65) uS
    // First check if a flag has been set
    // Then check if the counter 'i' has reached the dimming level
    // if so.... switch on the TRIAC and reset the counter
    
    void dim_check() {                   
      if(zero_cross == true) {              
        if(i>=fadeLevel) {                     
          digitalWrite(AC_pin, HIGH);  // turn on light       
          i=0;  // reset time step counter                         
          zero_cross=false;    // reset zero cross detection flag
        } 
        else {
          i++;  // increment time step counter                     
        }                                
      }    
    }
    
                                
    
    void loop() {  
        gw.process();
    }
    
    void incomingMessage(const MyMessage &message) {
      if (message.type == V_LIGHT || message.type == 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.type == V_LIGHT ? 100 : 1 );
        
        // Clip incoming level to valid range of 0 to 100
        requestedLevel = requestedLevel > 100 ? 100 : requestedLevel;
        requestedLevel = requestedLevel < 0   ? 0   : requestedLevel;
        
       
        float percent_level;
        percent_level = 128 - (requestedLevel * 1.28);
        fadeToLevel( percent_level );
         Serial.print( "Changing level to " );
        Serial.print( requestedLevel );
        Serial.print( ", from " ); 
        Serial.println( currentLevel );
        // Inform the gateway of the current DimmableLED's SwitchPower1 and LoadLevelStatus value...
       //  gw.send(lightMsg.set(currentLevel > 0 ? 1 : 0));
    
        // hek comment: Is this really nessesary?
       // gw.send( dimmerMsg.set(currentLevel) );
    
      
        }
    }
    
    /***
     *  This method provides a graceful fade up/down effect
     */
    void fadeToLevel( int toLevel ) {
      Serial.print("toLevel Value: ");
     Serial.println(toLevel);
    
      
      int delta = ( currentLevel - toLevel ) < 0 ? 1 : -1;
      Serial.print("delta Value: ");
     Serial.println(delta);
      while ( currentLevel != toLevel ) {
        currentLevel += delta;
    
     fadeLevel= ((int)currentLevel);
        delay( FADE_DELAY );
        //fadeLevel = toLevel;
    
       
    
      }
    
    
      
    }
    


  • Can we control AC ceiling fan using openhab dimmer.
    I get circuit schematic for AC fan dimmer.96_1257497893.gif



  • can we control it?



  • @thomasdc Does this method of using the timer interrupts + pin interrupts worked well with you ? You didn't need to disable interrupts sometimes between subroutines calls ?

    I am just asking because a year and a half ago I tried to do this using a PIC and it worked well when I was using delay function to delay the firing of the Triac after zero crossing is detected, but after I switched to a non blockage technique and used a time overflow, I found that sometimes the lamp just flickers for a moment while the fading is working. It happened randomly and I couldn't know the reason.


 

336
Online

8.2k
Users

9.0k
Topics

95.9k
Posts