Dimming an AC lamp via MQTT

Years ago I published a TRIAC dimmer that could be controlled by a simple microcontroller such as an arduino. Times have changed and right now it is of more importance to be able to control that lamp from a home automation system such as OpenHab, Homematic or NodeRed.

So, let’s have a look at the circuit first:
This is a rather classic circuit. It detects the zerocrossing on the grid and then a microcontroller ignites the TRIAC with a time delay that determines the brightness of the attached Lamp.  The full cycle is 10mS (at 50Hz)
A circuit like this is easy to make for very low cost (1-2 Euro).

However, if you shy away from soldering  such a circuit, there are ready made modules available as well:
You should not need to pay more than 3-4 USD for such a module. Anything above that is robbery

Software to control this dimmer would look something like this:


#include <Ethernet.h>
#include <PubSubClient.h>
#include  <TimerOne.h>

#define CLIENT_ID       "Dimmer"
#define PUBLISH_DELAY   3000

String ip = "";
bool startsend = HIGH;
uint8_t mac[6] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x07};

volatile int i=0;               // Variable to use as a counter volatile as it is in an interrupt
volatile boolean zero_cross=0;  // Boolean to store a "switch" to tell us if we have crossed zero
int AC_pin1 = 4;// Output to Opto Triac
int dim1 = 0;// Dimming level (0-100)  0 = on, 100 = 0ff

int inc=1;          // counting up or down, 1=up, -1=down
int freqStep = 100; // make this 83 for 60Hz gridfrequency

EthernetClient ethClient;
PubSubClient mqttClient;
long previousMillis;

void zero_cross_detect() {    
  zero_cross = true;               // set the boolean to true to tell our dimming function that a zero cross has occured
  i=0;
  digitalWrite(AC_pin1, LOW);       // turn off TRIAC (and AC)
}                                 

// Turn on the TRIAC at the appropriate time
void dim_check() {                   
  if(zero_cross == true) {              
    if(i>=dim1) {                     
      digitalWrite(AC_pin1, HIGH); // turn on light       
      i=0;  // reset time step counter                         
      zero_cross = false; //reset zero cross detection
    } 
    else {
      i++; // increment time step counter                     
    }                                
  }                                  
} 

void setup() {
  
  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);
  pinMode(4, OUTPUT);

  // setup serial communication

  Serial.begin(9600);
  while (!Serial) {};
  Serial.println(F("dimmer"));
  Serial.println();

  // setup ethernet communication using DHCP
  if (Ethernet.begin(mac) == 0) {
    Serial.println(F("Unable to configure Ethernet using DHCP"));
    for (;;);
  }

  Serial.println(F("Ethernet configured via DHCP"));
  Serial.print("IP address: ");
  Serial.println(Ethernet.localIP());
  Serial.println();

  ip = String (Ethernet.localIP()[0]);
  ip = ip + ".";
  ip = ip + String (Ethernet.localIP()[1]);
  ip = ip + ".";
  ip = ip + String (Ethernet.localIP()[2]);
  ip = ip + ".";
  ip = ip + String (Ethernet.localIP()[3]);
  //Serial.println(ip);

  // setup mqtt client
  mqttClient.setClient(ethClient);
  mqttClient.setServer( "192.168.1.103", 1883); // <= put here the address of YOUR MQTT server //Serial.println(F("MQTT client configured")); mqttClient.setCallback(callback); Serial.println(); Serial.println(F("Ready to send data")); previousMillis = millis(); mqttClient.publish("home/br/nb/ip", ip.c_str()); } void loop() { // it's time to send new data? if (millis() - previousMillis > PUBLISH_DELAY) {
  sendData();
  previousMillis = millis();

  }

  mqttClient.loop();
  Serial.print("dim1 in loop = ");
  Serial.println(dim1);
}

void sendData() {
  char msgBuffer[20];
  if (mqttClient.connect(CLIENT_ID)) {
    mqttClient.subscribe("home/br/sb");
    if (startsend) {
     
      mqttClient.publish("home/br/nb/ip", ip.c_str());
      startsend = LOW;
    }
  }
}

void callback(char* topic, byte* payload, unsigned int length) {
  char msgBuffer[20];
  
   payload[length] = '\0';            // terminate string with '0'
  String strPayload = String((char*)payload);  // convert to string
  Serial.print("strPayload =  ");
  Serial.println(strPayload); //can use this if using longer southbound topics
  Serial.print("Message arrived [");
  Serial.print(topic);
  Serial.print("] ");//MQTT_BROKER
  for (int i = 0; i < length; i++) {
    Serial.print((char)payload[i]);
  }
  Serial.println();
  Serial.println(payload[0]);

dim1=strPayload.toInt();

}

The value freqStep is usually set at 75 or 78 with this type of circuits, which allows for 128 steps of dimming at 50Hz gridfrequency. I have set the value here purposely to 100 allowing for only 100 steps, which is convenient for use in OpenHAB as  the slider goes from 0-100%.
The calculation is as follows:

50Hz
As we have two zerocrossings per sine wave, the frequency of zerocrossings is 100Hz. Therefore the period between two zerocrossings is 10mSec is 10000uS.
So if you want to have 100 levels of dimming the steps you need to take are 10000/100=100uS

60Hz
The frequency of zerocrossings is 120Hz, therefore the period between two zerocrossings is 8.3mS is 8300uS. So if you want to have 100 levels of dimming, the steps you need to take are 8300/100=83uS -> freqStep=83

 

Advertisements

8 thoughts on “Dimming an AC lamp via MQTT”

  1. Hi, Can you share the name of the ready made module? Can this be used to dim a 220v lamp?
    Thanks in advance for your answer.
    BR.

      1. Thanks for your answer.
        Looking at the code I see that you use an interruption PIN but (at least in Arduinos UNO and NANO) interrupts are disabled by SoftwareSerial, that is needed to connect using a ESP8266.
        Which arduino did you use for this? or how can you managed to stablish network connection for mqtt without SoftwareSerial?
        A solution could be to run this directly on an ESP8266. Do you know if it is possible or only runs on an Arduino board?

        Thanks in advance for your answer.

        Best regards.

      2. This was on a UNO.
        I am not using software serial at all, neither do I use an ESP8266 here, that would be foolish to use just for internet connection. I am using an ArduinoUNO with Ethernetshield here.
        As you can see in the code, there is no setup of a software serial, nor are there any AT commands being send to it.

        As the ESP8266 is a powerful microcontroller in its own right it would be a waste to use it simply as a connection to Ethernet, but even if you wanted that, there is no need for software serial, hardware serial will do just fine.

        I presume it is possible to run the code on an ESP8266, but that is not a ‘solution’ but just another project.

    1. Sergio, I don’t think that will be a big problem. But can I maybe emphasize that you can buy a ready made board for 3-4 dollars. I am all for DIY, but sometimes it is better to just buy it ready made 🙂

  2. Thanks for reply. You are right, Sometimes I buy ready made modules, they are often cheap and useful and do not justify making them yourself.
    This time, I have all the parts in stock so I decided to build it to save time.
    But I have somes problems. The first one is when I connect the 220V supply the 47K resistors turning very hot (with or whithout load). Second, at the output of bridge I measure no more than 1.3V, never detect the zero cross signal.
    I have one more question. For the ESP8266, the interrupts are not equal to Arduino. Arduino uses the Ticker library and is not supported by the ESP8266. I’m right?. Greetings and thanks again.

    1. Sergio, I fully understand, I sometimes do the same.
      The heat dissipated by the 47k resistors is not depending on the load, they merely drive the zerocross detector.
      Roughly 5mA will flow through each resistor, which amounts to abt 1 Watt.
      The value of these resistors, and thus the powerdissipation is a bit between a roc and a hard place as you could increase them, but at a certain point that will lead to the zerocross pulse becoming wider. Feel free though to experiment with a higher value.
      I am not sure how you measured the output. If just with a voltmeter it is higly unlikely that you could measure any difference as the pulses come by with 100-120 pulses/second, so what you will see on a standard multimeter will be anaverage voltage.
      You are right about the ESP8266, interrupts but I dont think that has anything to do with the ticker library. The Arduino has hardware interrupts.
      The Tickerlibrary works both on Arduino as on ESP afaik https://github.com/esp8266/Arduino/blob/master/libraries/Ticker/examples/TickerBasic/TickerBasic.ino

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.