Theb-1 / ESP8266-wifi-light-dimmer

MIT License
66 stars 33 forks source link

NMI Timer #2

Closed brunoudi closed 7 years ago

brunoudi commented 7 years ago

I implemented a dimmer based on the NMI timer, but I got a point:

In 'stand by', the NMI timer handle the interrupt repeatedly by intervals precisally of 1.677.722 uS (or ~1,67 s). This automatically interrupt can be disabled or changed the time interval?

When I implemented the NMI timer I got a delay on the turn ON and OFF, and studying the code I saw it happens because the change of state is handled by the "dimTimerISR" and not by the "zcDetectISR". To solve that I changed my code to this:

void dimTimerISR() {

  if (curBrightness < 255 && curBrightness > 0) { { // to prevent unexpected pulses every 1,67s
    digitalWrite(outPin, HIGH);
    delayMicroseconds(20);
    digitalWrite(outPin, LOW);
  }

}

void zcDetectISR() {

  if (zcState == 0) {
    zcState = 1;

    if (curBrightness < 255 && curBrightness > 0) {

      int dimDelay = 30 * (255 - curBrightness) + 400;
      hw_timer_arm(dimDelay);

    }

    if (fade == 1) {
      if (curBrightness > tarBrightness || (state == 0 && curBrightness > 0)) {
        curBrightness -= 1;
      }
      else if (curBrightness < tarBrightness && state == 1 && curBrightness < 255) {
        curBrightness += 1;
      }
    }
    else {
      if (state == 1) {
        curBrightness = tarBrightness;
      }
      else {
        curBrightness = 0;
      }
    }

    if (curBrightness == 0) {
      state = 0;
      digitalWrite(outPin, 0);
    }
    else if (curBrightness == 255) {
      state = 1;
      digitalWrite(outPin, 1);
    }

    zcState = 0;
  }
}

I actually dont know if it is OK use the 'delayMicroseconds(20);' inside the NMI handler. And I can't think a better algorithm to get the dimmer working without the delay to power ON and OFF. Any ideas or comments?

Theb-1 commented 7 years ago

What do you mean by "stand-by"? Are you trying to use this in low power mode?

This is where you set up the timer. Passing in a 0 in the second parameter is supposed to tell it to not auto-rearm (you can verify in the hw_timer source since it does not pass in 'FRC1_AUTO_LOAD'). hw_timer_init(NMI_SOURCE, 0); hw_timer_set_func(dimTimerISR);

I'm not showing any odd issues with the timer on my ESP12.

You could have issues with any delays in the interrupt. I've seen issues where the ESP crashes or the wifi stack crashes (no wifi until reboot). 20us is pretty small so there may not be any issues. You'll have to test and see.

brunoudi commented 7 years ago

I'm not trying to use it in low power mode, when I said the NMI timer in 'stand by' mode I was referring that is not armed (wich happens in the ZC interrupt). For test this you can comment out the attach Interrupt for the ZC, and put a Serial.print in the NMI interrupt handler, you will see the Serial.print happens every 1.677.722 uS precisally.

My point is, considering the ZC handler is what arm the NMI timer, this not happens when brightness is 255 or the lamp is OFF (curBrightness = 0). Considering the NMI is handled 'automatically' every ~1,6 seconds, in those conditions the lamp will have a delay to Power On or Off. For example, 'if curBrightness = 255' the Power On/Off will delay ~1,6 seconds (worst case), and 'if (curBrightness < 255 && curBrightness > 0)' the turnOff happens instantally.

I found a better solution, than the suggestion in my first message:

void dimTimerISR() {
  if (fade == 1) {
    if (curBrightness > tarBrightness || (state == 0 && curBrightness > 0)) {
      --curBrightness;
    }
    else if (curBrightness < tarBrightness && state == 1 && curBrightness < 255) {
      ++curBrightness;
    }
  }
  else {
    if (state == 1) {
      curBrightness = tarBrightness;
    }
    else {
      curBrightness = 0;
    }
  }

  if (curBrightness == 0) {
    state = 0;
    digitalWrite(outPin, 0);
  }
  else if (curBrightness == 255) {
    state = 1;
    digitalWrite(outPin, 1);
  }
  else {
    digitalWrite(outPin, 1);
    delayMicroseconds(20); //included
    digitalWrite(outPin, LOW); //included
  }

  zcState = 0;
}

void zcDetectISR() {
  if (zcState == 0) {
    zcState = 1;

    if (curBrightness < 255 && curBrightness > 0) {
      //digitalWrite(outPin, 0);
      uint32_t dimDelay = 25 * (255 - curBrightness) + 700;
      hw_timer_arm(dimDelay);
    }
    else {
      hw_timer_arm(3000);
    }
  }
}

1) When curBrightness = 0 or 255, I arm the NMI timer with 3000. This value is because the ZC detection can happens due some noise and 3ms will disconsider it.

2) When dimmering, I believe that is because my components, the ZC is detected really near the real ZC (Vin < 5 V). So the 'turn off' of the outPin not happens in the same semicicle. The lamp was always in full bright. To solve that, in the NMI timer handler I change the outPin 'turn on' to just a pulse of 20us.

Theb-1 commented 7 years ago

I was able to verify this behavior. I've added your updates to my code and haven't seen any side effects so they should be in the next push I do.

Thanks.