text: WAIT!! before you decide to build this, it is good to know that a similar dimmer is available at Aliexpress at cost that is hard to beat (currently 2.70 euro)
  WARNING: 'zero2 major grid frequencies
  in the world: 50Hz in Europe and most of Asia and Africa and 60 Hz in the America's (and parts of the Caribean). There are 2 major voltages in the world: 110-120V and 220-240V but they are not important for the mathematics hereFor 'polling' of the Zero Crossing pin
  2-using an interrupt to tell the program that there was a zero crossing
  The main difference between the two is that in 'polling' 'tapping 'wait' command
  2-by using a timer interrupt
  Again, both these methods have their pro's and con's. The 'wait' command ('delay' 'dimming' 78 'NOTE' 'dimming'
  In the main loop of the program the actual value of that variable is set
  /* AC Voltage dimmer with Zero cross detection Author: Charith Fernanado Adapted by DIY_bloke) check: <a href="">interrupts<, = (75*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) Some Triacs need a longer period digitalWrite(AC_LOAD, LOW); // No longer trigger the TRIAC (the next zero crossing will swith it off) TRIAC } void loop() { for (int i=5; i <= 128; i++){ dimming=i; delay(10); } }
  About the software: theoretically in the loop you could let variable 'i' start from '0'. However, since the timing in the interrupt is a bit of an approximation using '0' (fully on) could screw up the timing a bit. the same goes for 128 (Full off) though that seems to be less critical. Wether '5' or perhaps '1' 'waiting' in the zero cross interrupt function.
  It would be more efficient to set a timer interrupt to fire at the right moment so in the mean time the arduino can do something else. Such a program can be found in step 6.It triggers on the falling edge of the zero-crossing signal, so the timing is a bit different.
  I have not tested it myself yet, but I see reasons why it should not work: as far as i can see it doesnt receive the number typed in the serial port but it receives the ascii value of each digit that is typed, so a '0' will be seen as 48() { }
  Just a note: The above software is not mine. I think it is better to keep the check of the serial port out of the interrupt. Also the 500uS delay before the TRIAC is switched OFF is maybe a bit long.
  Step 6: Arduino Controlled Light Dimmer: the Software III
  The code below uses the timer function rather than a delay and has been confirmed to work on the Leonardo as well
  Step 7: MQTT Your Dimmer
  It is possible to use MQTT to control your dimmer, provided you have a WLAN connection. In this example I am using an Arduino UNO with Ethernetshield.
  The topic is "home/br/sb" with a payload from 0-100 to set the dimming level
  #include <Ethernet.h> // Ethernet.h #include <PubSubClient.h> // PubSubClient.h #include <TimerOne.h> /(); }
  Step 8: pinMode(AC_pin, OUTPUT); // Set the Triac); //); }
  Step 9: More Interrupt Driven Software
  Step 10: Arduino Controlled Lightdimmer: Result & Expansion
  Just a quick cellphone recorded video of it's workings
  3 channels
  This circuit can also be used for an RGB mixer, albeit you need two additional TRIAC circuits. You could use this circuit+PCB in duplo for that, but make sure you use a random cycle opto-coupler suah as the MOC3021. Do not use a zerocrossing Opto-coupler such as the MOC3041.
  I now made a 3 channel Ibble
  The zero-crossing circuit is ofcourse only needed once.
  Perhaps this is still something for tradition (call it Old fashioned): x-mas tree lights that work directly on 220 (or 110) Volts. Hang 3 different color lamp strings in the tree and regulate them with this circuit expanded with two TRIAC circuits
  Frequency
  Depending on the grid frequency (either 50 or 60) different step values need to be set manually. However, it is possible to let the software determine the frequency in a setup routine, e.g. by meauring the time between two zero crossings or count the number of zerocriossings within a set time.
  Step 11: 8051 Controlled Lightdimmer: Software
  Obviously, one can use the 8051 microcontroller series as well to control the dimmer.
  As I dont have an 8051 development system anymore, I cannot test any code, but should you want to develop this for an 8051, the following example may be of help:
  // //Controlling AC with a 8051 //Untested Code //Compiles with MicroC Pro for 8051 int dimming; int x; int i; void ex0_isr(void) iv IVT_ADDR_EX0 ilevel 0 ics ICS_AUTO { /*===================================================*/ int dimtime=(75*dimming); //delay_us(dimtime); P0=0xFF; // sets entire PORT 0 high, can also use e.g. P0_1 =1 ; delay_us(10); //propagationdelay P0=0x00; } void delay(int maal){ for (x=1; x< maal; x++) { delay_us(75); // 65 for 60Hz } } /*===================================================*/ void main() { /*------------------------ Configure INT0 (external interrupt 0) to generate an interrupt on the falling-edge of /INT0 (P3.2). Enable the EX0 interrupt and then enable the global interrupt flag. ------------------------------------------------*/ IT0_bit =1; // Configure interrupt 0 for falling edge on /INT0 (P3.2) EX0_bit = 1; // Enable EX0 Interrupt EA_bit = 1; // Enable Global Interrupt Flag P0 = 0x00; //all pin of PORT0 declared as output while (1) { for(i=5;i<128;i++){ dimming=i; delay(10);//arbitrary delay between steps } } }
  I cannot garantee this code to work as I have no way of testing it. Should anybody try it and have results I would be glad to hear it.
  An application note describing a fan control according to the same principle, can be found here.
  Step 12: PIC Controlled Lightdimmer: the Software
  If you want to use this circuit with a PIC microcontroller, the software in this link may help you get further:
  A good article on zero crossing detection with a PIC can be found here:-...
  The writer Syed Tahmid Mahbub gives a basic program that detects the zero crossing and then triggers the LED with a delay of 20ms .
  Although I never worked with PIC's before and am no crack on C programming. I decided to see if i could build on his program and make it possible to vary the light intensity, rather than just give it one value (the 20ms delay).
  I soon found out that the delay_ms and delay_us functions in C, are a bit tricky, namely that they don’t accept a variable. The delay time needs to be known at the time of compilation as it is hard coded. I saw some complicated work-arounds, but I thought a simpler solution would be to make a function that gives a 75 uS delay (make that 65 for 60Hz) and call that with a parameter determining how often that delay is looped.
  The maximum number of times the delay is looped is 128. That is because I have randomly chosen that the light should be dimmed in 128 steps (with 0 being full on and 128 being off).
  A warning though: I have no PIC programmer and I am not planning (yet) to go into pics, happy as I am with the Atmega and Attiny series. Therefore I cannot test the program. I can only say that it compiles without problems, and if you want to use the circuit on a PIC, it will help you get started. You can also find full projects, including a program, here and here, including an IR remoteand here
  //---------------------------------------------------------------------------------------------------------
  //Programmer: DIY_Bloke
  //Strongly based on a 0-X program from Syed Tahmid Mahbub
  //Compiler: mikroC PRO for PIC v4.60
  //Target PIC: PIC16F877A
  //Program for phase angle control
  //zero crossing signal on pin 33 RB0/INT
  //gating signal to MOC3021 via 220-470R from pin 19 RD0/PSP0
  //X-tal 4 MHz
  //---------------------------------------------------------------------------------------------------------
  unsigned char FlagReg;
  int x;
  int maal;
  int dimming=20;// '20' is just an example. Dimming should contain a
  // value between 0 and 128 and can be taken from e.g. a
  // variable resistor or LDR or a value coming out of a program
  sbit ZC at FlagReg.B0;
  void interrupt(){
  if (INTCON.INTF){ //INTF flag raised, so external interrupt occured
  ZC = 1;
  INTCON.INTF = 0;
  }
  }
  void delay(int maal){
  for (x=1; x< maal; x++) {
  delay_us(75); // 65 for 60Hz
  }
  }
  void main() {
  PORTB = 0;
  TRISB = 0x01; //RB0 input for interrupt
  PORTA = 0;
  ADCON1 = 7; //Disable ADC
  TRISA = 0xFF; //Make all PORTA inputs
  PORTD =
  delay(dimming); // '20' is an example
  PORTD.B0 = 1; //Send a pulse
  delay_us(250);
  PORTD.B0 = 0;
  ZC = 0;
  }
  }
  }
  Step 13: Problems
  If for whatever reason the circuit you built is not working, other than it starting to smoke.
  Before you do any work on the circuit UNPLUG IT FROM THE MAINS!!
  There are mainly 3 things that can happen:
  1-The lamp is flickering
  This is probably the most common problem you can encounter and there maybe several reasons for it e.g.
  -a 'dirty' powersupply.
  If your powersupply gives off a lot of extra spikes, these can be present on the 0X signal pin and mess up the clean Zero crossing signals. Try another powersupply.
  -'timing' problems
  using an optocoupler gives a precise zero-crossing signal, but it is not extremely narrow. The pulse width of this circuit (at 50Hz) is typically around 600us . The solution is to avoid regulating all the way down or all the way up. Also increasing or sometimes decreasing the step-value (the number 75 for 50Hz and 65 for 60Hz) may cure that.
  2-The lamp is constantly on
  This might be a software or a hardware problem and the easiest way to sort that out is to make sure the microcontroller is not connected to the circuit. If the lamp is still on there are grossly 4 possibilities:
  -You somehow fucked up the circuit, check if it is indeed OK and that everything is connected to where it shoudl be connected.
  -The MOC3021 is somehow receiving a positive input, make sure there are no stray drops of solder shorting things that shouldn’t be shorted. Short the input and ground wire and see if the lamp stays off.
  -The MOC3021 is short circuited at the high voltage end. Remove the MOC3021 from its socket and see what happens: if yr lamp stays off there is likely something wrong with yr MOC3021. If your lamp stays on, you probably have a faulty TRIAC
  -You have a faulty TRIAC. As described above. Yet, check the gate resistor if it really has the correct value, just to make sure
  3-The lamp is constantly off
  As this may also be a software or hardware problem, first see what hapens with the arduino disconnected.
  Connect the input to a plus 5Volt supply and measure the voltage on the primary side of the optocoupler (YOUR CIRCUIT SHOULD NOT BE CONNECTED TO THE MAINS). If that is a couple of volts, connect your circuit to the mains and see what happens. If the lamp switches on there is a problem with the input signal. If it stays off, you may have a faulty optocoupler, a faulty TRIAC or your circuit somehow is not connected to the mains. Another possibility is that the voltage drop over the LED is preventing the optocoupler to open, especially when you are using say 3.3 V as a driving voltage. Make sure you have an LED with a low voltage drop or replace it by a wire bridge.
  A piece of code that can help you test the Triac circuit is to add the following into the setup
  void setup()
  {
  pinMode(AC_LOAD, OUTPUT); // Set the AC Load as output
  for (int i=0; i < 10; i++) {
  digitalWrite(AC_LOAD, HIGH); // triac firing
  delay(1000);
  digitalWrite(AC_LOAD, LOW); // triac Off
  delay(1000);
  }
  }
  This will first fire the TRIAC a few times so you can see it is working
  Most common fault till now
  From all the people that contacted me about problems of the circuit not working, the most common fault was: faulty wiring: a chip put upside down, a solder joint not good, a wire not connected right.
  Step 14: The Gate Resistor: a Bit of Theory
  When cruyising the internet for Triac switches, you may have come across a large diversion in the value of the gate resistor value. My choice usually is to take the lowest value that will still protect the gate and the optocoupler.
  Reader Mali Lagunas did some research in the theory behind the criteria to choose the value of the gate resistor. Which i will copy below:
  The resistor when placed in this circuit,′s smallerac will be kept switched.
  Step 15: Dimming: a Bit of Theory
  Just as a background to this instructable: There are various types of dimmers. What is presented here is a phase-controlled (aka 'phase-cut') leading edge (aka "forward phase")TRIAC dimmer.
  Leading Edge Dimmers
  In this type the dimmer actually cuts parts off the beginning of the sinewave. This is the most widely used type of dimmer as it is ver suitable for TRIACs. After all, A Triac is easy to switch on and it will switch off by itself once there is a zero crossing because the current drops below the gate hold current
  Trailing Edge Dimmers.
  Step 16: Zero Crossing: a Bit of Theory
  Though I described the zerocrossing already, I will spend some more words on it.
  With the 'bridge and opto-coupler' circuit that I used in this project, the principle is very clear: A mains AC voltage passes through 2 resistors of 33k and is rectified by the diode bridge.
  This rectification results in a pulsating DC voltage that keeps the optocoupler open, keeping the zerocrossing signal LOW till the voltage drops to 'zero' at what point the optocoupler will not be in conduction anymore and the zerocrossing signal is pulled high, untill the voltage rises again enough to send the optocoupler into conduction again, resulting in the zerocrossing pin going LOW.
  The 'quality' of that zerocrossing pulse is of course depending on a number of factors but mainly on the speed of the optocoupler, the value of the collector resistor, but not in the least on the value of the two resistors in the mains line.
  If that value is too low, your sin wave, resulting in a wide zerocrossing signal, that starts way before the actual zerocrossing until way after the zerocrossing.
  Ergo: The resistor value should be as low as possible. In practice however I found the 2x 33k to be a good value, leading to a pulse starting abt 200uS before the actual zerocrossing. That is very acceptable. The current through the 4N25 is approximately 3.33 mA. Surely we could take that up a notch, but it isn't necessary. With these values the idle use of this circuit is an estimated 0.7 Watts
  The same actually goes for the circuit with the H11AA1. The advantage of the H11AA1 is that one doesn't need a diode bridge as it has two antiparallel diodes. The same goes for the IL250 series or the LTV814
  One can reach the same effect with two regular optocouplers such as the 4N25, as the figure shows or with a dual optocoupler.
  I also provided a circuit in which the width of the zerocrossing pulse can be regulated.
  As said before, the width of the Zerocrossing signal determines how far before the actual zerocrossing the interrupt is generated. Ideally, if you know how wide your zerocrossing signal is, you could take that into account in the software. Suppose your signal is 600uS wide and suppose that is a evenly divided timing. Then you know that your interrupt is generated some 300uS before the actual Zerocrossing. You could wait that period after each interrupt to get a more precise handling of the zerocrossing
  29 People Made This Project!
  SubhamC13 made it!
  rameshadkri made it!
  silas15 made it!
  Aboulfad made it!
  MàđhúkêshT made it!
  SudheerH made it!
  yeseulsong made it!
  tkeleş made it!
  TomaszK8 made it!
  joshsim2566 made it!
  new_tech made it!
  GasparC2 made it!
  malipivo made it!
  soreau made it!
  synchroma made it!
  chadcm380 made it!
  chadcm380 made it!
  chadcm380 made it!
  PatoOta made it!
  instasquid made it!
  GregoryR6 made it!
  Abdulaziz El-Feky made it!
  garrettx made it!
  nrocy made it!
  EnoS1 made it!
  didin.ok made it!
  Fr3dy made it!
  guttih made it!
  iamdebjyoti made it!
  See 20 More
  1,062 Discussions
  Question 7 days ago
  Excuse me..
  Hello sir. I have some problem for my final poject for my college. My project is rectifier 3 phase controlled full wave. I am using 3 zero crossing detector and 3 arduino nano for each zero crossing. Have you some code for this project sir? Please help me.. thank you sir :)
  Question 12 days ago
  My questions are based on this project
  I have built your AC triac dimming circuit with a potentiometer, and 110VAC 60Hz mains voltage. The first time I ran the program, the lamp dimmed properly. However, once I turned off power and turned in on again, the lamp did not turn on. I changed out the optocoupler it fixed the problem, but it happened again. I placed my oscilloscope across D8 (connected to the EL817 optocoupler) and GND and measured a sine wave. I understand this should be a square wave, which is what I measured when the lamp actually dimmed.
  Can you please help me understand why I am getting a sine wave across D8 and ground? Why does the circuit work once and then stops working when I go to use it again? How can I prevent these issues from happening in the future?
  Thank you in advance
  Reply 11 days ago
  Makelt, sorry to hear you have problems with the electronoob project. It is a bit hard for me to say why it wouldnt work as it is not my project, but I will see what I can do.
  If your build works well and then stops working there are a variety of possibilities. In your case, t seems like you already traces a problem once, namely a faulty Optocoupler.
  As your problems returned, it seems most likely that again it is a faulty optocoupler. Now these optocouplers do not break spontaneously. Either you had a bad batch, which is unlikely, or your circuit is killing them. The latter is usually caused by a current that is too big.
  So, I start wondering if you are using the correct resistors: 2x 47k and 1x 1k?
  If those are correct.... are you sure you connected the potentiometer correctly? and lastly, could there be shorts in your PCB/veroboard/stripboard?
  Reply 10 days ago
  Hi diy_bloke,
  Thank you so much for your response. I have 2x 47k 1/4W resistors on the AC side of the bridge rectifier. I also have a 1k 1/4W resistor and a 0.1uF capacitor connected from the emitter of the optocoupler to ground. I am sure the potentiometer is connected correctly. Also, I just verified there are no shorts in the breadboard.
  I switched out the type of optocoupler I was using from an EL817 to a 4N25, but this did not help either. There is a 330ohm resistor connected in series with the internal LED of the optocoupler. This is to ensure the forwarding current doesn't exceed 60mA.
  Reply 10 days ago
  Hi diy_bloke,
  Thank you for your response. I am using 2x 47k 1/4W resistors going to the input of the bridge rectifier. I am also using one 1k 1/4W resistor and a 0.01uF capacitor from the emitter (pin 4 of 4N25) to ground. The potentiometer is connected correctly. Also, I haven't found any shorts in the breadboard.
  Question 4 weeks ago
  If I want to control an induction motor of 2000 Watt, is it enough just to replace the Triac with a higher power and heat sink ? or other changes are needed ? my project is to run the motor at 4 ascending speeds. please any advice ?
  Reply 11 days ago
  Apologies for my late reply. Have been busy.
  In principle you can attach a heavier load, provided the TRIAC can take the power requirements.
  having said that, TRIACS are not really great with Inductive loads such as your motor. It will work, but as said TRIACS are not really the optimal candidates for it.
  I would add a snubber network though
  Question 6 weeks ago
  Hi, I was thinking in buying the one from aliexpress but the triac is rated for 16A so for a load of 3000W would be too tight. Do you think that the Triac can be changed for one that supports 40A ie. the BTA41 and the board still be saved?
  Thanks for the tutorial, it is great!!!
  7 weeks ago
  see my notes on board making here. Scroll down to the bit on board making...
  might break this out with a more detailed instruction.
  7 weeks ago
  If you ground the arduino the 1k resistor will ignite for some reason... and sound functions get compromised as the beep gets distorted. Already switched power supplies and nothing, if I plug the arduino to my laptop it stops with the beep distortion or if i ground the arduino. I'm more concerned with the resistor because it is the secound time that I had to replace it. The Dimming works but again if you ground the circuit it stops. I NEED SOME HELP!!!!
  2 months ago
  Hi there,
  I am thinking of using this project not for AC lamp dimmer but AC heat element control.
  Can I still use this circuit and everything without changing anything?
  Thank you.
  Reply 2 months ago
  yes, it will work with any pure ohms imerdance, like an incandescent bulb or a heating element
  Question 4 months ago on Step 6
  Dear diy_bloke,
  I am having trouble using the code under step 6, using a timer interrupt. I am using a regular Arduino Uno, the Robodyn Triac board linked in the introduction and a a normal incandescent lamp.
  The code listed under step 4 – i.e. using delay rather than a Timer.interrupt – works perfectly well, dimming the bulb from full power to off. This leads me to conclude that the Robodyn board, bulb and Arduino are functioning. As I intend to include several more tasks into my sketch, the code under step 4 is too demanding.
  However, using the code under step 6 results in a bulb that is burning fully.
  Fidgetting around with the code brings several issues to light:
  When I remove the code in the loop and change the value of the dim variable: some dimming occurs. However not as expected. Changes in the light level can only be seen between a setting of approx. 90 to 128. Any value below 90 leads to a bulb that seems fully on. So, why is it only responsive to approx. 1/4th of the total range? Remind you: I am using a normal incandescent lamp, no LED or anything fancy..
  Adding a Serial readout of variable dim to loop of the original code shows that the loop is being executed and the dim variable varies from 0-128 every 18 milliseconds. However, seemingly, this does not lead to the dim variable with is used within the ISR that is called by Timer1 being updated more than once. Why would this be?
  Help would be greatly appreciated!
  Reply 3 months ago
  Something odd in instructables, as I am quite sure I replied your question some time ago, but now I cant find my reply. Maybe I forgot to click something, apologies. Can I suggest you change int dimming =128 into "volatile int dimming =128"
  Answer 4 months ago
  Thijs, that is definitely a bit of a bummer. It has been a long time since I used this code and don't recall any problems with it.
  18mS is a bit of an odd value, as the entire cycle should be dealt with in 10 mSec.
  you say it caries from 0-128 every 18mS. Could you expand on that a bit more? do you mean that even if you don't touch anything, it flips from 0 to 128 every 18mS? or do you mean something else?
  5 months ago
  Hello bloke
  Thank you for you great project
  i made it with arduino . now i wont to use esp8266
  while compiling the code i faced this error...
  and i download meny arduino ide versions . so can you help me
  Reply 3 months ago
  apologies for the late reply, sometimes somehow I don't see questions pop up.
  the avr.io.h file is for avr architecture. If I now look at the old code I even wonder why I put it in. I suggest you just leave it out
  Question 5 months ago
  Hello Bloke. Very nice tutorial. Can I use this dimmer for halogen bulb of surgical microscope which is 12V 100W bulb.
  Reply 3 months ago
  in principle yes, but dimming usually not that good for the life span of halogen bulb.
  Reply 4 months ago
  is that a 12Volt DC?
  if you are thinking of dimming the primary side, that usually has a transformer, it is not impossible, but not optimal. I would rather thinkof dimming the secondary side, either with PWM, if DC, or a triac if AC