TRIAC (BTA41-600) and optocoupler (MOC3041) not working with load

Thread Starter


Joined Jun 23, 2021
Hey guys, it's my first post here, so I'm sorry if I'm doing it in the wrong place and I really hope you can help me!

I'm developing a dimmer with ESP8266 and I'm having trouble with the TRIAC circuit. I have a zero cross detector developed and it works fine, but when I try to send the signal to the triac, it just works without a load conected (just with the voltmeter).

Explaining better:
When I receive a zero cross signal, I wait a time (via timer interrupt) in a range 0~100 (where 0 is 0ms and 100 is 8.3ms) and set the output to the MOC. If I have just the voltmeter connected to the AC output I can see all the range variation (between 220V and 0V), but if I connect some load it goes to 0V if I try to set the TRIAC after the zero cross signal (if I set the TRIAC while I'm receiving the zero cross signal, I have 220V in the output lol).

Important information:
1. AC Voltage: 220V/60Hz
2. I'm using a transistor to control de MOC because ESP8266 have 12mA of pin current limit.

Here I have a spreadsheet with the timer delay value VS output Voltage with and without load:

Here my circuit (using MOC3041 and BTA41-600V instead):

And the firmware:
#include <user_interface.h>;//Biblioteca necessaria para acessar os Timers
#include <time.h>
#include <Ticker.h>  //Ticker Library

volatile int i=0;               // counter
volatile boolean zero_cross=0;  // cruzamento por zero status
int sinalOpto = D2;// Output to Opto Triac
int sinalZero = D1;
int dim1 = 0;// dimer level (0 = 100%; 100=0%)

void ICACHE_RAM_ATTR zero_cross_detect();
void ICACHE_RAM_ATTR dim_check();

void setup() {
  attachInterrupt(sinalZero, zero_cross_detect, RISING);    // define interrupção no pino
  timer1_attachInterrupt(dim_check); // timer 12.000 Hz (100x meio ciclo)
  timer1_enable(TIM_DIV1, TIM_EDGE, TIM_SINGLE);
  pinMode(sinalOpto, OUTPUT);
  while (!Serial) {};

void loop(){
  yield(); //delay(1);

void ICACHE_RAM_ATTR zero_cross_detect() {    //detecção de zero
  static unsigned long last_interrupt_time = 0;
  unsigned long interrupt_time = millis();
  if (interrupt_time - last_interrupt_time > 2){   //debounce
    zero_cross = true;               // seta flag de cruzamento por zero
    digitalWrite(sinalOpto, LOW);       // desativa o triac
    Serial.print("crossing ");
  last_interrupt_time = interrupt_time;

void ICACHE_RAM_ATTR dim_check() { //timer
  if(zero_cross == true) {
    if(i>=dim1) {
      Serial.print("Setting ");
      digitalWrite(sinalOpto, HIGH); // ativa o triac     
      i=0;  // reseta contagem                     
      zero_cross = false;
    else {
      i++; //                   

Thank you all so much and I'm sorry about the long post. If you need more info, just ask please!

Thread Starter


Joined Jun 23, 2021
At what point do you switch off the signal to the MOC3021?
Just when I read the next zero (line 36).

But I was reading here other posts and I think I know what is happening. I'm using a MOC3041 and it's a zero-crossing optocoupler. I'm going to change it for a MOC3021 and than I bring here the news.

Thank you!