analog to digital signal/Using it/10. Chatter and hysteresis
Using it · 10 of 11

Chatter and hysteresis

A slow input crossing the knob does not cross once. It wobbles, and a comparator that answers in a microsecond with no hysteresis flips DIG at every wobble. Debouncing DIG thins it out. Reading ANA and switching at two lines either side of the knob cures it, and the sketch can learn where the knob is from DIG itself.

One crossing, many flips

Chatter at the threshold
The sketch believes
ANA
0.887 V
Sketch sees
LOW
Changes
0
The input is about to drift up through a threshold at 1.00 V over 1.2 s, wobbling a little. Press run.

A light fading at dusk, or a potentiometer turned slowly, does not cross the knob cleanly. It drifts toward the line with a little wobble on it: a shadow, a lamp's flicker, noise on the wire. Near the line, each wobble takes it across and back.

The comparator answers in about a microsecond, so it follows every one. DIG flips each time, and anything reading DIG sees several changes for one slow crossing: a lamp that flickers, a counter that counts too many. The trace in the figure is a picture of the behaviour, not a measurement, but the behaviour is real. ON's datasheet adds that the LM393 tends to oscillate during a transition, through stray capacitance from its output back to its inputs, which a slow input makes last longer.

Hysteresis is two lines

The usual cure is hysteresis: switch on at one line and off at another, a little lower. Once the input is past the upper line, a wobble has to carry it all the way below the lower one to change the answer, and a wobble smaller than the gap cannot. The datasheet recommends a few millivolts of it, made by a resistor from the output back to the plus input.

This board has no such resistor. So the sketch does it.

Debounce thins it, hysteresis cures it

The first idea is to debounce DIG: believe a change only after it has held for, say, 40 ms. That removes the fastest flips. But a slow wobble can hold across the line for longer than any sensible window, and then it gets through, as the middle setting of the figure shows.

The cure is to read ANA and compare in the sketch, with two lines. The trick in this sketch is where the lines come from. DIG changes only when the input is at the knob, so every time DIG changes, ANA is the threshold, and the sketch notes it. Then it switches on above the threshold plus 50 mV and off below it minus 50 mV. The knob still sets the point; the sketch only adds the gap.

What you should see

Turn the input block slowly through the knob and back:

above the knob   DIG flipped 7 times
below the knob   DIG flipped 4 times

One line per crossing, however many times DIG itself flipped on the way.

The code

Reads ANA in millivolts. Whenever DIG changes, the input is at the knob, so it notes ANA as the threshold. It then switches on only above the threshold plus HYST_MV and off only below it minus HYST_MV, and counts how often DIG flipped meanwhile.

adc_hysteresis.ino
/*
  Analog to Digital Signal - hysteresis in the sketch    TK29 / /p/tk29

  Any analog block pushed into IN, parts facing the same way. Either
  switch position: DIG changes at the knob's voltage either way.

  Wiring, OUT to your board. Count from the square pad, parts up,
  OUT at the bottom:

    GND -> GND
    VCC -> 5V on an Uno; 3V3 on an ESP32, ESP32-S3 or Pico
           (DIG is pulled up to VCC)
    DIG -> D2 on an Uno, GPIO 25 on an ESP32, GPIO 7 on an
           ESP32-S3, GP15 on a Raspberry Pi Pico
    ANA -> A0 on an Uno, GPIO 34 on an ESP32, GPIO 4 on an
           ESP32-S3, GP26 on a Raspberry Pi Pico

  Arduino IDE
    Tools > Board                 your board, e.g. Arduino Uno
    Tools > Port                  the one that appears when you plug in
    Tools > USB CDC On Boot       Enabled   (ESP32-S3 only)
    No library needed.
*/

// DIG_PIN, then ANA_PIN.
// Uno: 2 and A0. ESP32: 25 and 34. ESP32-S3: 7 and 4. Pico: 15 and 26.
const int DIG_PIN = 2;
const int ANA_PIN = A0;

// Uno and Pico only: the ADC's full scale, in mV.
// Uno: 5000. Pico: 3300.
const float FULL_SCALE_MV = 5000.0;
// Switch this far either side of the knob: two lines 100 mV apart.
const float HYST_MV = 50;

int lastDig;
float thresholdMv = -1;         // learned from DIG, below
bool above = false;             // the steady answer
unsigned long flips = 0;        // how often DIG itself changed

float readMilliVolts() {
#if defined(ARDUINO_ARCH_ESP32)
  return analogReadMilliVolts(ANA_PIN);   // calibrated in the chip
#else
  return analogRead(ANA_PIN) * FULL_SCALE_MV / 1023.0;
#endif
}

void setup() {
  Serial.begin(115200);
  pinMode(DIG_PIN, INPUT);      // the block has its own pull-up
  lastDig = digitalRead(DIG_PIN);
}

void loop() {
  float mv = readMilliVolts();
  int dig = digitalRead(DIG_PIN);

  if (dig != lastDig) {         // DIG changes only at the knob,
    lastDig = dig;              // so ANA is the threshold now
    thresholdMv = mv;
    flips++;
  }
  if (thresholdMv < 0) return;  // not crossed yet

  bool was = above;
  if (!above && mv > thresholdMv + HYST_MV) above = true;
  if (above && mv < thresholdMv - HYST_MV) above = false;

  if (above != was) {
    Serial.print(above ? "above the knob" : "below the knob");
    Serial.print("   DIG flipped ");
    Serial.print(flips);
    Serial.println(" times");
    flips = 0;
  }
}

The knob still sets the threshold: turn it, cross it once, and the sketch has the new one. The switch position does not matter, because DIG changes at the same voltage either way. above means above the knob; to act on darker, use !above.

When it does not work

It still switches twice now and then.

The wobble is bigger than the gap. HYST_MV is 50, so the lines are 100 mV apart; a noisy input, or a lamp's own light reaching the sensor, can swing further. Raise HYST_MV to 100 and try again. The wider the gap, the further past the knob it has to go before it switches.

Nothing happens until I move the input through the knob once.

That is by design: the sketch learns the threshold the first time DIG changes, and has no line to switch at until then. If the input starts far from the knob, sweep it through once after power-up, or give thresholdMv a starting value measured with the previous article's sketch.

Could I not just add a capacitor on DIG?

It would slow the edges down and hide the fastest flips, but a slow wobble across the line gets through anyway, and a slow edge on a digital input can make things worse. Hysteresis is the cure; this board has none, so the sketch supplies it.

Does the LED flicker too?

Yes. It shows DIG directly, so it chatters along with it. The sketch's decision is steady; the LED on the board is not. Watch the lamp or the serial monitor, not the TK29's LED.

Where this goes next

Six symptoms, and the part of the board each one points at.

When the output is wrong

Edit this page — content/books/analog-to-digital-signal/chatter-and-hysteresis.mdx

Community

Questions about this product

See what other owners have asked, and read their solutions.

Ask a question ↗

Analog to Digital Signal

Loading discussions…

Discuss this article

Ask about this page. The answer stays here, on the page it belongs to, for whoever hits the same wall next.

Browse Modules and blocks on the forum