Instructables

WARNING: Some people try to build this with an optocoupler with zerocrossing coz 'that is better' right? Some are even told in electronics shops it is better to use such an optocoupler. WRONG. This will only work with a random fire optocoupler: NOT igniting at zerocrossing is the principle of this dimmer.

Switching an AC load with an Arduino is rather simpel: either a mechanical relay or a solid state relay with an optically isolated Triac. (if you use an 8051 or PIC16F877A microcontroller, there is stuff for you too here.)

It becomes a bit more tricky if one wants to dim a mains AC lamp with an arduino: just limiting the current through e.g. a transistor is not really possible due to the large power the transistor then will need to dissipate, resulting in much heat and it is also not efficient from an energy use point of view.

One way of doing it is through phase control with a Triac: the Triac then is fully opened, but only during a part of the sinus AC wave. This is called leading edge cutting.
One could let an Arduino just open the Triac for a number of microseconds, but that has the problem that it is unpredictable during what part of the sinus wave the triac opens and therefore the dimming level is unpredictable. One needs a reference point in the sinus wave.
For that a zero crossing detector is necessary. This is a circuit that tells the Arduino (or another micro controller) when the sinus-wave goes through zero and therefore gives a defined point on that sinus wave.
Opening the Triac after a number of microseconds delay starting from the zero crossing therefore gives a predictable level of dimming.

Another way of doing this is by Pulse Skip Modulation. With PSM, one or more full cycles (sinuswaves) are transferred to the load and then one or more cycles are not. Though effective, it is not a good way to dim lights as there is a chance for flickering. Though it might be tempting, in PSM one should always allow a full sinuswave to be passed to the load, not a half sinus as in that case the load will be fed factually from DC which is not a good thing for most AC loads. The difference between leading edge cutting and PSM is mainly in the software: in both cases one will need a circuit that detects the zero crossing and that can control a triac.

A circuit that can do this is easy to build: The zero crossing is directly derived from the rectified mains AC lines – via an optocoupler of course- and gives a signal every time the wave goes through zero. Because the sine wave first goes through double phased rectification, the zero-crossing signal is given regardless whether the sinus wave goes up through zero or down through zero. This signal then can be used to trigger an interrupt in the Arduino.

It goes without saying that there needs to be a galvanic separation between the Arduino side of things and anything connected to the mains. For those who do not understand 'galvanic separation' it means 'no metal connections' thus ---> opto-couplers. BUT, if you do not understand 'galvanic separation', maybe you should not build this.

The circuit pictured here does just that. The mains 220Volt voltage is led through two 30k resistors to a bridge rectifier that gives a double phased rectified signal to a 4N25 opto-coupler. The LED in this opto-coupler thus goes low with a frequency of 100Hz and the signal on the collector is going high with a frequency of 100Hz, in line with the sinusoid wave on the mains net. The signal of the 4N25 is fed to an interrupt pin in the Arduino (or other microprocessor). The interrupt routine feeds a signal of a specific length to one of the I/O pins. The I/O pin signal goes back to our circuit and opens the LED and a MOC3021, that triggers the Opto-Thyristor briefly. The LED in series with the MOC3021 indicates if there is any current going through the MOC3021. Mind you though that in dimming operation that light will not be very visible because it is very short lasting. Should you chose to use the triac switch for continuous use, the LED will light up clearly.

Mind you that only regular incandescent lamps are truly suitable for dimming. It will work with a halogen lamp as well, but it will shorten the life span of the halogen lamp. It will not work with any cfl lamps, unless they are specifically stated to be suited for a dimmer. The same goes for LED lamps

If you are interested in an AC dimmer such as this but you do not want to try building it yourself, there is a somewhat similar dimmer available at www.inmojo.com, however, that is a 110 Volt 60Hz version (but adaptable for 220 50Hz), that has been out of stock for a while. You will also find a schedule here.

NOTE! It is possible that depending on the LED that is used, the steering signal just does not cut it and you may end up with a lamp that just flickers rather than being smoothly regulated. Replacing the LED with a wire bridge will cure that. The LED is not really necessary. increase the 220 ohm resistor to 470 then


STOP: This circuit is attached to a 110-220 Voltage. Do not build this if you are not confident about what you are doing. Unplug it before coming even close to the PCB. The cooling plate of the Triac is attached to the mains. Do not touch it while in operation. Put it in a proper enclosure/container.

WAIT: Let me just add a stronger warning here: This circuit is safe if it is built and implemented only by people who know what they are doing. If you have no clue or if you are doubting about what you do, chances are you are going to be DEAD!


Materials
Zerocrossing
4N25 €0.25 or H11AA1 or IL250, IL251, IL252, LTV814 (see text in the next step)
Resistor 10k €0.10
bridge rectifier 400 Volt €0.30
2x 30 k resistor 1/2 Watt (resistors will probably dissipate 400mW max each €0.30
1 connector €0.20
5.1 Volt zenerdiode (optional)



Lamp driver
LED (Note: you can replace the LED with a wire bridge as the LED may sometimes cause the lamp to flicker rather than to regulate smoothly)
MOC3021 If you chose another type, make sure it has NO zero-crossing detection)
Resistor 220 Ohm €0.10 (I actually used a 330 Ohm and that worked fine)
Resistor 470 Ohm-1k (I ended up using a 560 Ohm and that worked well)
TRIAC TIC206 €1.20 or BR136 €0.50
1 connector €0.20

Other
Piece of PCB 6x3cm
electric wiring

That is about €3 in parts

 
Remove these adsRemove these ads by Signing Up
1-40 of 377Next »
PrinsNathan1 month ago
Hi, diybloke! When I tried your code with this circuit today it didn't work, when I ran a simple analog write to the input pin it did, but it wasn't in sync of course so it faded. I think there might me a problem with my zero cross signal. I don't have a scope so I can't check that, should I replace my 4N27 or try and borrow a scope?
diy_bloke (author)  PrinsNathan1 month ago

sorry to hear you had a problem. as it seems your output works well, and provided you have made no mistakes in the program, it mist be the input signal.
before u buy a new optocoupler, I advise the following: measure the voltage over the input of the optocoupler. Does it get any voltage at all?
if so measure the voltage over the output, oventough it should pulsate at 100 Hz, you should be able to get a reading. If so, measure the voltage over the interrupt pin.
Are you sure all the connections are OK?
If that all fails get a new optocoupler

Hmmm I do get readings, some nearly non existent and some around 500mv.. But it does work now for some reason! If I use the code with the timer library it works but not with the other code. And it seems to work better on interupt 4 than 0.... Do you know why that might be? And lastly, is it possible to run multiple dimmers with different values at the same time? Thanks so much!
diy_bloke (author)  PrinsNathan1 month ago

I am glad you got it working. I do not know why the one code didnt and the other does, unless u are trying to have the arduino do something else in the mean time as well, then the timer is a better option. Also the interrupts should not make a difference. I take it u are using an Arduino Mega?
yes you can run different dimmers at the same time with different values. Currently busy with an RGB (=3 channel) dimmer

My apologies for the late response! I made my 'final' version! I used the photoresist technique to make a PCB, my first stab at custom PCB's and it was awesome. Now I'll add a cheap chinese Arduino pro mini, bluetooth connectivity, and a potentiometer and put it in a nice case... So I can change the value on my phone and manually! Thanks for the fritzing files btw :)

IMG_20141106_232909[1].jpgIMG_20141106_232850[1].jpgIMG-20141106-WA0006[1].jpeg
diy_bloke (author)  PrinsNathan14 days ago

looks great, thanks for sharing.
Chinese Arduino pro mini's are unbelievably cheap right now. I got them for 1.58 euro a piece.

can i use tic106 instead of tic206 ??

diy_bloke (author)  ahmedmahrous1 month ago

no the TIC106 is a thyristor, not a triac

What about BTA16
diy_bloke (author)  ahmedmahrous1 month ago

yes can use

Please tell me triac number that I can use it instead of tic206
diy_bloke (author)  ahmedmahrous1 month ago

i already did, below, i advised BT(A)136. BTA16 also good

AbuW1 month ago

hi, this board can work with raspberry pi? can show the connection for raspberry pi? thanks.

20141008011105.jpg
diy_bloke (author)  AbuW1 month ago

yes it can work with Raspberry PI
All you need is an interrupt Pin and and an output pin. You are free to chose those.
The old kernell in the raspberry didnt have interrupts I think but the new one has.
I like yr picture

AbuW diy_bloke1 month ago

thanks for the comment, can you guide which pin to use as interrupt and output pin? in raspberry pi GPIO 18 is PWM. thanks.

diy_bloke (author)  AbuW1 month ago

I know I answered this question but the answer seems to have not logged.
u are sort of free to chose but pin22 is often used for interrupts. for the output pin u do not need pwm, just a pin that can switch on and off

AbuW diy_bloke1 month ago

so, that means, gpio22 is the dimmer signal in right? and i will use gpio23 as zc signal out? which coding to run the dimmer program? care to explain it. thanks.

images.jpg
diy_bloke (author)  AbuW1 month ago

made a start with a program for raspberry, but dont have time right now to finish and test it, but this may help you along:

#!/usr/bin/env python

import time
import RPi.GPIO as GPIO


# handle the ZC event
def ZC_EventHandler (pin):
print "handling ZC event"
# set dimming in your main routine
time.sleep(dimming)

# turn the TRIAC
GPIO.output(25,True)



# main function
def main():

# tell the GPIO module that we want to use
# the chip's pin numbering scheme
GPIO.setmode(GPIO.BCM)

# setup pin 23 as an input
# and set up pins 24 and 25 as outputs
GPIO.setup(23,GPIO.IN)
GPIO.setup(24,GPIO.OUT)


# tell the GPIO library to look out for an
# event on pin 23 and deal with it by calling
# the buttonEventHandler function
GPIO.add_event_detect(23,GPIO.RISING)
GPIO.add_event_callback(23,ZC_EventHandler,100)

#

# set your timing variable here


GPIO.cleanup()



if __name__=="__main__":
main()

diy_bloke (author)  AbuW1 month ago

let me just add that the raspberry might be tricky to use for this as i understand the timing isnt always that easy to control.

diy_bloke (author)  AbuW1 month ago

I am not sure what you mean with Zero crossing out, but I presume you mean the signal to drive the Triac.. I think i may choose pin 18 for that but 23 is also ok.
Unfortunately i do not have time to write a program for every microcontroller that people want to port this to but you may find some help here:

http://www.adafruit.com/blog/2013/03/22/how-to-use...

the only thing that you would need to do in yr code is to wait for the zerocrossing signal, wait a desired time and fire the triac.
good luck :-)

please i cant find TIC206 in the stores ,, what can i use instead of it ??

diy_bloke (author)  ahmedmahrous1 month ago

oddly my replies seem to not register unless I post twice. I answered this but it has disappeared.
Anyway, if u cant find a TIC 206 basically every TRIAC that is suitable for your local voltage is OK. try a BT136

didin.ok made it!1 month ago

Thanks diy_bloke. This work with the last code !

IMG_4816_800x533.JPG
diy_bloke (author)  didin.ok1 month ago

great. your setup looks good. thanks for sharing

Fr3dy made it!1 month ago

I really needed this circuit for a project.. fortunately you posted it here.. it works flawlessly with the last code.. thank you very much!!

photo 1.JPGphoto 2.JPG
diy_bloke (author)  Fr3dy1 month ago

I am happy you liked it Fr3dy. Always enjoy seeing pics showing what others made of it :-)

ksun42 months ago

hey guys! i really need help. the circuit and program code doesnt work with a 220volts AC DIMMABLE LED BULB. i really dont know what to do about it. it works perfectly with ordinary bulbs but i need to let it work with LED bulb for my project. please help me. my defend is in a week. please! please please!

diy_bloke (author)  ksun42 months ago

Hmmm seems my earlier reply didn’t store. It is however largely the same as my mail to you

ksun42 months ago

hey guys! i really need help. the circuit and program code doesnt work with a 220volts AC DIMMABLE LED BULB. i really dont know what to do about it. it works perfectly with ordinary bulbs but i need to let it work with LED bulb for my project. please help me. my defend is in a week. please! please please!

diy_bloke (author)  ksun42 months ago

you also may find this article interesting:

http://www.digikey.com/en/articles/techzone/2011/j...

All in all I am not sure what your procet needs to adhere to, but if DC is an option I would go for that

diy_bloke (author)  ksun42 months ago

See my earlier reply and my mail to you. Just let me add that pulse skip modulation works well in say motors and though it works well in incandescent lamps, it is not the best method for incandescent lamps coz of a possibility of flickering.
How that will be on LED's I am not sure.. they may flicker too at low output.

yet, if u are bound to using LED's, I would always consider using DC steering as you then can do away with the entire zerocross detection and use pwm instead

Knakworst3 months ago

Hi everybody,

I am building an Arduino terrarium climate controller and I've some problems with the TRIAC dimmer.

Maybe I have overlooked the solution in the comments but I have not found a solution yet. I built the analog board and tested it with several different software solutions, all went well until I integrated it into my own software.
In the software several temperature sensors are checked in the terrarium, during those checks the lamp flickers slightly. When I change the firing calculated value for 50Hz from 75 to 60 it looks ok but only for one dimming value if I want to dim more or want to have more light the problem occurs again.

Does this sound familiar to someone? I hope somebody can help.

thanks Rob

diy_bloke (author)  Knakworst3 months ago
there are a number of possibilities.
which sensors are you using? can you post the code you are using?

Thanks for your response.

Since this is an addition to an already running climate control unit I created a test setup with a onewire DS18B20 temperature sensor. Below you see the code I created for the test setup. I basically took your code and modified it a bit. When I comment out the call to the temperature sensor all goes well, but with the call the light starts flickering.

#include <OneWire.h>

#include <DallasTemperature.h>

float tempMeasured = 0;

int AC_LOAD = 3; // Output to Opto Triac pin

int dimming = 115; // Dimming level (0-128) 0 = ON, 128 = OFF

// Data wire of the OneWire is plugged into pin 10 on the Arduino

#define ONE_WIRE_BUS 10

// Setup a oneWire instance to communicate with any OneWire devices

// (not just Maxim/Dallas temperature ICs)

OneWire oneWire(ONE_WIRE_BUS);

// Pass our oneWire reference to Dallas Temperature.

DallasTemperature sensors(&oneWire);

void setup()

{

Serial.begin(9600);

pinMode(AC_LOAD, OUTPUT);// Set AC Load pin as output

attachInterrupt(0, zero_crosss_int, RISING); // Choose the zero cross interrupt # from the table above

}

//the interrupt function must take no parameters and return nothing

void zero_crosss_int() //function to be fired at the zero crossing to dim the light

{

// Firing angle calculation : 1 full 50Hz wave =1/50=20ms

// Every zerocrossing thus: (50Hz)-> 10ms (1/2 Cycle)

// For 60Hz => 8.33ms (10.000/120)

// 10ms=10000us

// (10000us - 10us) / 128 = 75 (Approx) For 60Hz =>65

int dimtime = (60*dimming); // For 60Hz =>65

delayMicroseconds(dimtime); // Wait till firing the TRIAC

digitalWrite(AC_LOAD, HIGH); // Fire the TRIAC

delayMicroseconds(10); // triac On propogation delay (for 60Hz use 8.33)

digitalWrite(AC_LOAD, LOW); // No longer trigger the TRIAC (the next zero crossing will swith it off) TRIAC

}

void loop() {

tempMeasured = temperatureSensor1();

if (tempMeasured > 26){

dimming = 75;

delay(20);

}

}

void getSensorData(){

// call sensors.requestTemperatures() to issue a global temperature

// request to all devices on the bus

sensors.requestTemperatures();

delay(2000);

}

float temperatureSensor1(){

// Basking sensor (blue wire)

getSensorData();

float tempS1 = sensors.getTempCByIndex(0);

delay(2000);

return (tempS1);

}

diy_bloke (author)  Knakworst3 months ago

Thanks for yr posting.

Let me begin by saying that the code I gave only serves as an example and it is not the most optimal code as it spends most of its time waiting. Your problem is akin to what commenter МихаилК describes below.

As I understand the onewire and Ds18b20 library can cause some problems with interrupts... at least, that is what various people have reported.
Other than that there is a timing problem because of what I mentioned at the beginning.
The time that you have to let the processor do everything you want is a half cycle=10mSecs. (at 50 Hz)
if you have your dimming level set at 115, that means that you are in a waiting loop for 8.9mS and have 1.1 mS left to do something.
Add to that that a reading of the DS18b20 can take up to 50mSec or more and you see the problem.
Sure, interrupts are made for that, to interrupt the processor for the Interrupt service routine, but as said,,, the OneWire library seems to screw up interrupts (see here). In the OneWire.cpp file you will find the following description for a modification that was made:
"Disable interrupts during timing critical sections
(this solves many random communication errors)"
Further studying of the cpp file shows that regularly the interrupts are disabled for over 50 uSec and sometimes even for more than 500uSec
I would start with using the other code that I describe in my ibble, that uses a timer interrupt to keep track of the dim time, rather than a waiting loop. That might already take care of yr problem to some extent. but probably not completely

Another thing to consider is to change the flow of your code: now you read your temperature sensor continuously. I understand that as it is the easiest, but in a terrarium the temperature probably will not become critical within a second, or a minute, or 5 minutes so you could decide to not read your sensor continuously but say every 5 minutes, at least you would not have the problem continuously.

You could also opt for another sensor(if that is practical). One that doesn’t take much time to read, possibly even an ntc if that is accurate enough for yr purpose.

You could take the interrupt disable out of the .cpp file

You could take a more drastic solution and let the DS18B20 be read by say an Attiny 13/45/85, that then sends that to yr arduino, either as a treshhold, or as complete temp info.

If you have further questions, please do not hesitate to ask

ok, thanks. I have already tried the other code and with same result, so no Onewire, everything OK, but with the Onewire the flickering occurs again. Replacing the Onewire is not an option, at least not anymore. Disable the interrupt sounds interesting in combination with altering the temperature sample rate. I am going to try that and see what happens.

Thanks for the info.

Rob

diy_bloke (author)  Knakworst3 months ago

Rob
If you consider to maybe read yr sensors say every 10 secs, remember that you can use the built-in ISRs to extend timer
functionality. For example, if you wanted to read a sensor every 10
seconds, there’s no timer setup that can go this long without
overflowing. However, you can use the ISR to increment a counter
variable in your program once per second, then read the sensor when the
variable hits 10. The ISR would look something like this:

ISR(TIMER1_COMPA_vect)

{

seconds++;

if(seconds == 10)

{

seconds = 0;

readSensor();

}

}

For a variable to be modified within an ISR, it must be declared as volatile. In this case, you need to declare volatile byte seconds; or similar at the start of the program.

For full description, check here:

http://arduinodiy.wordpress.com/2012/02/28/timer-interrupts/

diy_bloke (author)  diy_bloke3 months ago

If your system happens to have an RTC, say the DS1307, it would be worth considering to use the sqw to trigger an interrupt every second and use that in an interrupt routine to count to say 5 or 10 secs.
You would need to set the RTC SQW to 1 sec, which you do with:

void sqw1() // set to 1Hz
{
Wire.beginTransmission(DS1307_I2C_ADDRESS);
Wire.write(0x07); // move pointer to SQW address
Wire.write(0x10); // sends 0x10 (hex) 00010000 (binary)
Wire.endTransmission();
}

both options are a possibility. I have to look at the pros and cons and figure out which option works best with the current code. But definitly good suggestions, thanks.

Rob

mcwareg Knakworst3 months ago

H, I solved this taking out the delay from the ISR, you could do:

volatile byte ZCD=0;

void zero_crosss_int()

{

ZCD=1;

}

void loop() {

dim_func();

tempMeasured = temperatureSensor1();

if (tempMeasured > 26){

dimming = 75;

delay(20);

}

}

dim_func()

{

// Firing angle calculation : 1 full 50Hz wave =1/50=20ms

// Every zerocrossing thus: (50Hz)-> 10ms (1/2 Cycle)

// For 60Hz => 8.33ms (10.000/120)

// 10ms=10000us

// (10000us - 10us) / 128 = 75 (Approx) For 60Hz =>65

int dimtime = (60*dimming); // For 60Hz =>65

delayMicroseconds(dimtime); // Wait till firing the TRIAC

digitalWrite(AC_LOAD, HIGH); // Fire the TRIAC

delayMicroseconds(10); // triac On propogation delay (for 60Hz use 8.33)

digitalWrite(AC_LOAD, LOW); // No longer trigger the TRIAC (the next zero crossing will swith it off) TRIAC

}

Remarks:

- This would free the ISR system.

- This would only work if the main_loop < 8.3 ms

If I were you:

after detect the zero_crosss_int() I would only start a timer (for example timer 1) for the dimtime, and in the ISR(of the timer), turn on, delay 10 us and off exit (fast, no wait).

My problem was to wait INSIDE the ISR.

Actually works like a charm, I can dim and I can use comm libs, (serial, Wire, ISP,etc) at any speed without any problems, also external interrupts.

Hope it helps.

Regards

1-40 of 377Next »