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:

EDIT 2019 There is a library for the Arduino, the ESP8266 and the ESP32.

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

 

Advertisement

Controlling Neopixel or RGB LED with Openhab

Controlling Neopixels or RGB LEDs from an ESP8266, controlled by OpenHab is fairly simple. I will present here a working system with a Colorpicker, some predefined buttons and sliders. It will be updateable by OTA and provide some  feedback to openhab on the node parameters

Using a Colorpicker
The sitemap and items file are fairly easy, just using a Colorpicker. I will describe some additions, but the basic files are quite simple

itemsfile

Group All
Color RGBLed "NeoPixel Color" (All)
String RGBLedColor (All) {mqtt=">[mosquitto:OpenHab/RGB:command:*:default]"}

There are 2 items here. One item contains the HSB value, coming from the colorpicker, The other item, that will not be visible on the screen, will contain the calculated RGB code that will be sent out via MQTT.

sitemap file is as follows:

sitemap NeoPixel label="NeoPixel"
{
	Frame label="NeoPixel" {
		Colorpicker item=RGBLed icon="slider"
	}
}

The colorpicker sends an HSB (Hue, Saturation, Brightness) code, but that isnt anything a regular Neopixel LED or regular RGB LED can use, so I set up a rule that triggers on any change of the RGB item to change the HSB code into a regular RGB code. That RGBcode is joined in 1 string called ’ color’. The rule then assigns that ‘color’ string to a new item called “RGBLedColor”

rules file

import org.eclipse.smarthome.core.library.types.DecimalType
import org.eclipse.smarthome.core.library.types.HSBType

rule "Set HSB value of item RGBLed to RGB color value"
when
	Item RGBLed changed
then
	val hsbValue = RGBLed.state as HSBType

	val brightness = hsbValue.brightness.intValue 
	val redValue = ((((hsbValue.red.intValue * 255) / 100) *brightness) /100).toString
	val greenValue = ((((hsbValue.green.intValue * 255) / 100) *brightness) /100).toString
	val blueValue = ((((hsbValue.blue.intValue * 255) / 100) *brightness) /100).toString

	val color = redValue + "," + greenValue + "," + blueValue

	sendCommand( RGBLedColor, color)
end

//part of Neopixel conf files

On the receiving end, there is a Wemos D1 mini that controls a NeoPix strip. The ‘color’ string is split in its separate RGB values. As such it could also be used for an RGB LED or seperate Red, Green, Blue Led’s.
The code is reacting to one MQTT topic, basically switching the entire strip at one go in the desired color and brightness, but I added a structure catching some other MQTT topics that now go to empty routines but that could be used for patterns like a breathing light or a moving pattern. As illustration I added a simple red-blue pattern that responds to MQTT “OpenHab/RGB/scene” with payload “2”  I am using the broker name ‘mosquitto’, if your broker has a different name you needto modify that.

I have to say though that this is not the most efficient way of adding topics, but for beginners it is easiest to follow.
A much more efficient way is to keep the topics of one specified length with only say the last 2 or 3 characters defining the desired action and then only checking for those last characters. A good example of this is for instance in the gateway or sonoff code on https://github.com/computourist/ESP8266-MQTT-client4. For readability sake however, in this example I have chosen to use a more traditional approch and as long as you just want to control some LEDs it is easier.

The ESP8266 code does send some information back: it returns the software version, the IP number and the RSSI.
With regard to the software version and IP number… if you have several ESP8266’s and or Arduino’s in your system, it is easy to lose track which one does what and with what software.

The ESP8266 code is OTA updateable
I have used a fork of the O’Leary PubSub library that makes it easier to send variables as the MQTT payload without having to resort to setting up buffer space and using c_string. Library is here4: https://github.com/Imroy/pubsubclient4.
If you already have the O’Leary PubSub library installed, you will need to put the library files in your sketch folder and change #include <PubSubClient.h> into #include “PubSubClient.h”

Sending predefined colors
If next to the Colorpicker you also want to be able to send predefined colors via a button, then you only need to add some lines in the items and sitemap file:

add to itemsfile:

    Switch NEO_RED	"Red"	<red> {mqtt=">[mosquitto:OpenHab/RGB:command:ON:255,0,0]"}
    Switch NEO_YELLOW	"Yellow"	<yellow>	{mqtt=">[mosquitto:OpenHab/RGB:command:ON:100,96,0]"}
    Switch NEO_BLUE	"Blue"	<darkblue>{mqtt=">[mosquitto:OpenHab/RGB:command:ON:0,0,255]"}

add to the sitemap:

Switch item=NEO_RED mappings=[ON="ON"]
Switch item=NEO_YELLOW mappings=[ON="ON"]
Switch item=NEO_BLUE mappings=[ON="ON"]

As it can be a bit of a bother to find and type all the codes for the colors you may want, I added for download some 160 predefined color settings to pick and add to your sitemap and itemsfile, including some simple icons. As these items already send an R,G,B, code they do not need the rulesfile, only the colorpicker does.

How about Sliders
Perhaps you do not want to use a colorpicker but just individual sliders for Red, Green and Blue.
That is quite easy too.
add the following to your itemsfile:

Dimmer NEO_SLIDERRED "Neopixel Red [%d %%]"  <red> {mqtt=">[mosquitto:OpenHab/RGB/RED:command:*:default]"}
Dimmer NEO_SLIDERGREEN "Neopixel Green [%d %%]" <green> {mqtt=">[mosquitto:OpenHab/RGB/GREEN:command:*:default]"}
Dimmer NEO_SLIDERBLUE "Neopixel Blue [%d %%]" <blue> {mqtt=">[mosquitto:OpenHab/RGB/BLUE:command:*:default]"}

and this to your sitemap

Slider item=NEO_SLIDERRED 
Slider item=NEO_SLIDERGREEN
Slider item=NEO_SLIDERBLUE

Normally that would be enough already, but our ESP8266 program is expecting one input rather than seperate RGB inputs. That is because the colorpicker we used before sends one string of info.
We could add topics to the ESp8266 program to respond to individual RG and B values (and that is what the MQTT topics OpenHab/RGB/RED, OpenHab/RGB/GREEN, and OpenHab/RGB/Blue would be for), but it might be easier to combine those and send as one. For that we need a new rule.

add this to your rules file:

rule "Send  RGB items from slider"
when
Item NEO_SLIDERRED changed or
Item NEO_SLIDERGREEN changed or
Item NEO_SLIDERBLUE changed
then
val redValue=  Math::round(2.55* (NEO_SLIDERRED.state as DecimalType).intValue)
val greenValue=  Math::round(2.55* (NEO_SLIDERGREEN.state as DecimalType).intValue) //
val blueValue=  Math::round(2.55* (NEO_SLIDERBLUE.state as Number).intValue)
val color = redValue + "," + greenValue + "," + blueValue
	sendCommand( RGBLedColor, color)
end

While we are at it
If you want the Filename, IP number and RSSI to show up, add the following to your items file:

Number W_RSSI  "RSSI [%d dbm]" <signal>  {mqtt="<[mosquitto:home/nb/weer/RSSI:state:default]"}
String W_IP "IP [%s]" <network>   {mqtt="<[mosquitto:home/nb/weer/IP:state:default]"}
String W_version "Software versie [%s]" <version>  {mqtt="<[mosquitto:home/nb/weer/version:state:default]"}
String W_MAC "MAC [%s]" <mac> (Wweather) {mqtt="<[mosquitto:home/nb/weer/mac:state:default]"}
Number W_IP_Uptime "Uptime [%d min]" <clock> (Wweather) {mqtt="<[mosquitto:home/nb/weer/uptime:state:default]"}

and the following lines to your sitemap:

   Text item=W_RSSI
   Text item=W_IP
   Text item=W_version
   Text item=W_MAC
   Text item=W_IP_Uptime

download11
https://drive.google.com/open?id=0B9IqgvBSA4aUVzgzRE9MUUY3U2M11

Although I tried on a Wemos D1 mini, I presume this will also work perfectly on the ESP8266-01. Perhaps a great way to use those if you still have on your scrapheap as this is typically a minimal pin project.

The ESP8266 program
The Esp8266 program is rather self explanatory and going into it too deep might fall outside the openhab scope a bit, but in brief, you will find several ino files and a userconfig.h file. These all should be in the same directory and it is sufficient to open the NeopixelOpenhab_vx.ino file in your IDE. The other files will then open up too.

You need to make a few modifications in the userdata.h file.

The essential changes are your password, your SSID and the address of your MQTT server. The other options are indeed ‘optional’, can leave them as is.
the Filename is sent back to openhab as an MQTT message, so in the future you will still know what file it was you put in your ESP8266. obviously you would need to fill out the correct filename.

A handy module to control neopixels from am ESP8266-01 is described here.

IFTTT Webhooks on Arduino

IFTTT is a real handy service that allows you to make applets doing a variety of things, like email you the weather update every day.
Also, most home automation services like ‘Openhab’, ‘NodeRed’ or ‘HomeAssistent’ have a link with IFTTT so you can use your own data to trigger events or have have IFTTT take action in your home automation application, like switch on lights.

But, what if you dont have a homeautomation system and you just want your individual lonely arduino with ethernetshield to trigger something in IFTTT?

Fortunately IFTTT has a service for that called ‘Webhooks’ (it used to be called ‘Maker’). I have used that service in the past out of curiosity, but recently I needed it again and sadly had to scratch my head on ‘how did I do that again?’
So after getting myself re-aquainted with it, it might be good to share that with people who are interested in using it.

Therefore, let’s make a program that uses IFTTT to Tweet data every time the garagedoor is opened. Yes I know, you can tweet directly from an Arduinoprogram as well, but that’s not the point.

First ofcourse you need to register for IFTTT if you haven’t yet done so already.
Then you create a new applet and for the “IF” service, you choose “webhook”. If you never have used webmaker before, it will ask you to connect that service and provide you with an API key. Store that one, you will need it.
In Step 2 it will ask you for an “event name”.
this is a free to choose name that we will use later to trigger the event. Let’s make that ‘garage_door’.

 

 

 

In step 3 and 4 we chose the Twitter action. If you never used twitter before with IFTTT, you will now be asked for authorization from Twitter.

In Step 5 we format the message that we want to Tweet. The picture shows how I have done that.
Save the applet

We now have finished the IFTTT part, but before we go on to the Arduino part, we will test it:
Go to your browser and type:

https://maker.ifttt.com/trigger/garage_deur/with/key/d56e34gf756/?value1=8&value2=5&value3=good%20morning

in which “d56e34gf756” should be replaced by YOUR apikey.

Within moments you should see your tweet appear in your timeline.

So all we need to do now is to have an Arduino make a POST or GET webrequest akin to the one above.
However, in this case I will not add the values as arguments but as a JSON, but it gives the same result

You will find the code belonging to this action on github.
Now obviously this code needs a trigger as well in the shape of a doorcontact (NC) on digital Pin 4, pulling that down to ground