Debounce zero crossing

I am tuning some old code for zero-crossing detection, but as I'm stripping the code down things make little sense.
this is my circuit on the breadboard:

this is the code:

#define ZC_PIN 0

unsigned volatile long t_zc = 0;
unsigned volatile long t_zc_prev = 0;
unsigned volatile long time_half_period = 0;
volatile volatile boolean zc = false;

void zeroCrossing() {
  zc = true;
  detachInterrupt(digitalPinToInterrupt(ZC_PIN));
}

void setup() {
  Serial.begin(115200)

  pinMode(ZC_PIN, INPUT_PULLUP);

  attachInterrupt(digitalPinToInterrupt(ZC_PIN), zeroCrossing, FALLING);
}

void loop() {
  long mcros = micros();

   // debounce; 5000is half of half-period micros
   if (!zc && mcros > (t_zc + 5000)) {
    attachInterrupt(digitalPinToInterrupt(ZC_PIN), zeroCrossing, FALLING);
  }

  if (zc) {
    zc = false;

    t_zc_prev = t_zc;
    t_zc = mcros;

    time_half_period = t_zc - t_zc_prev;

    Serial.print("time_half_period: ");
    Serial.println(time_half_period);
  }
}

and the output this produces:

14:32:18.711 -> time_half_period: 10005
14:32:18.711 -> time_half_period: 10293
14:32:18.711 -> time_half_period: 10024
14:32:18.743 -> time_half_period: 9717
14:32:18.743 -> time_half_period: 10306
14:32:18.743 -> time_half_period: 9678
14:32:18.743 -> time_half_period: 10012
14:32:18.775 -> time_half_period: 10294
14:32:18.775 -> time_half_period: 9737
14:32:18.775 -> time_half_period: 10000
14:32:18.807 -> time_half_period: 10023
14:32:18.807 -> time_half_period: 9967
14:32:18.807 -> time_half_period: 10013
14:32:18.839 -> time_half_period: 10295
14:32:18.839 -> time_half_period: 9714
14:32:18.839 -> time_half_period: 10288
14:32:18.871 -> time_half_period: 10013
14:32:18.871 -> time_half_period: 9721
14:32:18.871 -> time_half_period: 10024
14:32:18.903 -> time_half_period: 10256
14:32:18.903 -> time_half_period: 10019
14:32:18.903 -> time_half_period: 9712
14:32:18.903 -> time_half_period: 10035
14:32:18.936 -> time_half_period: 10269
14:32:18.936 -> time_half_period: 9744
14:32:18.936 -> time_half_period: 10255
14:32:18.967 -> time_half_period: 10024
14:32:18.967 -> time_half_period: 9987
14:32:18.967 -> time_half_period: 9745
14:32:18.999 -> time_half_period: 9989
...
14:32:18.903 -> time_half_period: 9712
14:32:18.903 -> time_half_period: 10035
14:32:18.936 -> time_half_period: 10269
14:32:18.936 -> time_half_period: 9744
14:32:18.936 -> time_half_period: 10255
14:32:18.967 -> time_half_period: 10024
14:32:18.967 -> time_half_period: 9987
14:32:18.967 -> time_half_period: 9745
14:32:18.999 -> time_half_period: 9989
14:32:18.999 -> time_half_period: 10296
14:32:18.999 -> time_half_period: 9988
14:32:19.032 -> time_half_period: 9751
14:32:19.032 -> time_half_period: 9954
14:32:19.064 -> time_half_period: 20303
14:32:19.064 -> time_half_period: 10050
14:32:19.064 -> time_half_period: 9697
14:32:19.064 -> time_half_period: 10027
14:32:19.096 -> time_half_period: 10276
14:32:19.096 -> time_half_period: 9720
14:32:19.096 -> time_half_period: 9992
14:32:19.128 -> time_half_period: 10016
14:32:19.128 -> time_half_period: 9999
14:32:19.128 -> time_half_period: 10319
14:32:19.160 -> time_half_period: 9687
14:32:19.160 -> time_half_period: 10321
14:32:19.160 -> time_half_period: 9969
14:32:19.192 -> time_half_period: 9733
14:32:19.192 -> time_half_period: 10002
14:32:19.192 -> time_half_period: 10320
14:32:19.192 -> time_half_period: 9980
14:32:19.225 -> time_half_period: 9733
14:32:19.225 -> time_half_period: 9956

do you see it? like there is almost a pattern in there: what was missing in previous half period gets counted in in the next one... it's not exact but sort of like

14:32:10.930 -> time_half_period: 10022
14:32:10.930 -> time_half_period: 9988

but some runs wider:

14:32:13.435 -> time_half_period: 10317
14:32:13.435 -> time_half_period: 9700

what can cause this? what can I do to smooth it?

board: ESP32 - WROVER-E
thanks

The CTR (Current Transfer Ratio) of the H11AA1 is not symmetrical, so the half periods will not be equal.

Is that a problem?

Hi, @marko89

Put a fullwave bridge rectifier between the transformer secondary and the opto, that way only one of the LEDs will be used but on both positive and negative voltage excursions.

Tom.. :grinning: :+1: :coffee: :australia:

tbh I never heard of such thing.. well, what I need is this to serve the purpose: to open triac at the right moment.

so you say I can not rely on H11AA1 ? From what I found this optocoupler is widely used for zc detection and I need it to match..well..reality.

No the H11AA1 is fine.
The difference is minuscule.
10000-9988 = 12usec
10000/12 = 833
180 degrees / 833 = 0.22 degrees. Such a small error will absolutely make no difference in a 50Hz phase cut circuit.

2 Likes

You don't need to hammer the input the way you are (35ma is a lot).

what does that mean?

That your two current limiting resistors of 330 ohms each could be higher value and push less current through the LED and the hole thing would still work fine.

Check the data sheet and figure out a proper good enough current, I'm sure the little teeny LEDs in the opto would appreciate it. :expressionless:

a7

Record the value of micros() in your ISR. Now you only set a flag, that zc happened, but it takes some time until that flag is read in loop() as in between iterations of loop() the ESP is doing other things, such as updating micros() and millis() and checking it's WiFi and whatnot. The time that takes may vary.

Also there's no need to detach and attach the interrupt all the time.

Try this (untested of course):

#define ZC_PIN 0

volatile boolean zc = false;
volatile unsigned long zc_time;
unsigned long previous_zc_time;

void zeroCrossing() {
  zc_time = micros();
  zc = true;
}

void setup() {
  Serial.begin(115200)
  pinMode(ZC_PIN, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(ZC_PIN), zeroCrossing, FALLING);
}

void loop() {
  if (zc) {
    zc = false;
    Serial.print("time_half_period: ");
    Serial.println(zc_time - zc_time_old);
    zc_time_old = zc_time;
  }
}

Is it holes or is it electrons? I can never remember.:thinking:

1 Like

it's debounce, i can show you what it does without removing that ISR:

19:39:44.712 -> time_half_period: 66
19:39:44.712 -> time_half_period: 75
19:39:44.712 -> time_half_period: 10300
19:39:44.712 -> time_half_period: 8223
19:39:44.712 -> time_half_period: 65
19:39:44.712 -> time_half_period: 65
19:39:44.712 -> time_half_period: 1223
19:39:44.712 -> time_half_period: 73
19:39:44.712 -> time_half_period: 72
19:39:44.744 -> time_half_period: 8263
19:39:44.744 -> time_half_period: 65
19:39:44.744 -> time_half_period: 73
19:39:44.744 -> time_half_period: 65
19:39:44.744 -> time_half_period: 1199
19:39:44.744 -> time_half_period: 73
19:39:44.744 -> time_half_period: 10247
19:39:44.744 -> time_half_period: 8297
19:39:44.744 -> time_half_period: 74
19:39:44.744 -> time_half_period: 64
19:39:44.744 -> time_half_period: 65
19:39:44.744 -> time_half_period: 1189
19:39:44.744 -> time_half_period: 65
19:39:44.777 -> time_half_period: 76
19:39:44.777 -> time_half_period: 10315
19:39:44.777 -> time_half_period: 8155
19:39:44.777 -> time_half_period: 72

it says in datasheet 1,5V drop and 60mA... so i did 22V/0.06A which is 366Ω... but I don't know maybe I do that all wrong way, please correct.

Very interesting, considering there is nothing that can possibly bounce. There's nothing mechanical involved that bounces. Do have a look at the signal you're getting though a scope. I'm curious what could possible cause such behaviour. I've never seen it before, and I have done zero crossing detection in various ways, and also done other signals through optocouplers. No "bounce" issues, ever.

Try a stronger external pull-up, something like 1k or 2k2. That should also improve the waveform on the output, especially considering you drive that opto VERY hard.

That are no doubt the "absolute maximum" ratings. Normal is 1.2V drop for an IR LED. A current of around 5 mA is enough for an optocoupler, and makes for a much happier and longer living LED. In that case you can also use a weaker pull-up, the internal pull-up of the pin is probably enough already.

bounce here means, re-triggering.
in opposite to what you saying I found people recommending stronger pull-up resistore. maybe because it is easier to ground it (ehm, very roughtly)

this is the triggerring signal:


maybe too wide spikes/noise?

I don't think the interrupt inputs on an ESP32 are Schmidt trigger like they are on some Atmel chips so there could be some glitching from the slow transition of a sine wave.

The data sheet uses an I(F) of 0.010A on all of its graphs so I would think that more appropriate than 0.060A. Also a collector resistor of 10k would insure staying well within the recommended specs as 100k is at the max end.

I would do a quick test with a Schmidt trigger logic gate on the opto output as well.

if you mean to use 10㏀, I can, but all-in-all it's all the same. more I don't understand unfort.

They're in series, either way, even though they're at opposite ends.
Inasmuch, it's approx. 24vac / (330 + 330)
(I don't understand what it is with n00bs and forcing max current through LEDs.)

1 Like

It's hard to tell from your scope traces but it looks like you don't always return to the minimum logic high level (0.7 * 3.3V ) when your opt turns off. I think if you reduce the forward current on the LED and use a stronger pull-up resistor that will change.

Fixed that for Schmitt's Mom.

The 328p has hysteresis, but fun fact not: Counterfeit AVR chips may not.

a7

When I did zero-crossing (a million years ago) I used a half-wave rectifier and I just found the positive-going zero-crossings. If you find the positive-going and you know the line frequency, you know exactly when the next one is coming. :wink:

I also used a transformer for isolation so I didn't use an opto-isolator. I don't remember what I did for over-voltage protection, which is "automatically" taken care of by the opto-isolator.

Assuming you are using a TRIAC in a dimmer application, a little bounce/re-trigger isn't a problem because a TRIAC latches-on until current drops to zero (at the next zero crossing). If you get noise and it triggers too-soon, that can be a problem.

I actually triggered somewhere between zero and 90 degrees, and then calculated the true zero-crossing. (That required some trial-and-error calibration.)