ESP8266 Weather, Guage IRQ Debounce – Step 1.7

Welcome back once again, this should conclude the Step 1 series as we will have covered all the weather station algorithm prototyping topics. Today's discussion is on how to count the switch contact closures for both the rain and wind speed gauges. The Sparkfun Photon Weather shield connected to the optional weather gauges is setup to create pulses to represent rainfall and wind speed. The rainfall gauge fills up a small reservoir which falls over and lifts a second reservoir into place to begin filling up and the cycle continues. This seesaw action causes a switch to momentarily close and create a low going pulse until the switch opens and returns the signal high again. Each one of these pulses represents .011 inches of rainfall so all the firmware must do is accurately count the number of pulses. The wind speed gauge (anemometer) opens a switch contact twice per revolution as it rotates due to wind speed. Each pulse counted per second represents 1.492 MPH of speed, meaning you restart the count each second or divide the total count by the number of elapsed seconds and multiply by 1.492 to calculate the actual speed.

To count these pulses you can simply tie them to micro-controller IRQ inputs and have the interrupt handler code simply increment variables every time each interrupt happens. My micro-controller interrupts are configured to trigger on high to low transitions. This sounds pretty simple in theory but in practice the switches do not make clean pulses. What we get in reality is that when the mechanical switches make and break connection we get bouncing of the contacts which causes more than one edge that fires the interrupt. Lets take a look at some oscilloscope images of the rain gauge interrupt signal.

Rain IRQ Pulse Ringing

Rain IRQ Pulse Ringing

The rain gauge signal is normally high, and then falls low when the switch contacts close momentarily while the reservoir is toggling. As you can see in the image above where I have zoomed in on the falling edge of the interrupt signal, it has ringing which causes false ghost interrupts. So that is a look at the falling edge, but the following image shows the signal zoomed out to 200ms per division.

Rain Gauge Interrupt Pulse

Rain Gauge Interrupt Pulse

As you can see in this photo it looks like a nice low going pulse which lasts around 1,300ms or 1.3 seconds, I've seen worst case scenarios where the pulse lasts about 1900ms. One issue I'm seeing is that even though I don't see any ringing on the positive going edge of the pulse, I am still being interrupted on the positive going edge. I'll need to deal with both of these ringing cases in the interrupt handler and I'll talk about that later, but first lets take a look at the wind gauge signal. The wind gauge has the same type of ringing on the low going edge as the rain gauge, here is a zoomed in image of the wind gauge signal.

WindIRQzoomed

The wind speed signal has an even worse ringing than the rainfall gauge, but that is OK, because both signal's falling edge ringing is easy to mask out. I'll talk about what throws a wrench into the works and causes some grief, but first lets look at the wind speed signal zoomed out.

Wind IRQ Pulse zoomed out

Wind IRQ Pulse zoomed out

For this image, I set the scope's one shot trigger and gave the anemometer cups a spin. As you can see the wind speed signal is normally low and creates a positive going pulse as the anemometer spins. The anemometer creates 2 of these pulses per revolution. Notice that the width of the pulse gets wider as the spinning slows, and also that the time between pulses also increases. The ringing in the previous image was on this signals high to low transitions. Once again I don't see any ringing on the positive going edge of the pulse, however I'm seeing 4 interrupts per revolution, so it's definitely happening.

The strategy to debounce is to block counting interrupts that occur too close to the previous one. What you want to do is find a minimum time between interrupts that stops messy signals from falsely interrupting, but not big enough to block the actual interrupt. For the rain gauge it's pretty easy, in the worst case scenario the pulse is 1900ms wide so to block the noise on the positive going edge I need to set the time between interrupts to 2000ms or 2 seconds. This is OK because if we allow one interrupt every 2 seconds we can still measure rainfall at a rate of 19.8 inches per hour. I calculated 60 seconds * 60 minutes = 3600 seconds in an hour. Now we measure every 2 seconds so there are 1800 2 second periods in an hour and if each count equals .011 inches of rain, then 1800 * .011 = 19.8 inches of rain per hour. No problem here I don't know that anyplace on earth has had that much rain in an hour. Now lets take a look at the interrupt handler code:

void rainIrqHandler()
{
    // Determine amount of time since last IRQ
    AvatarTime curTime = { 0 };
    AT_getTime(&curTime);
    rainDeltaMills = AT_getElapsedMs(&lastRainIrqTime);
    // Remember this IRQ time to compare to next IRQ
    lastRainIrqTime = curTime;
    
    // Debounce rain switch, it creates a low pulse
    // as it begins to fall. The falling edge rings.
    // This pulse can be over 1900 ms long and then
    // its noisey on the way up, so make the
    // debounce time 2 seconds
    if(rainDeltaMills >= 2000)
    {
        rainCount++;
    }
}

As you can see the strategy is simple, calculate the elapsed time since the last IRQ and then save the current time and then if the delta time is larger that the debounce time increment rain Count.

The wind gauge is much harder to debounce when you have ringing/noise on both edges of the pulse. For example if you want to measure 140MPH winds you will get (140 / 1.492) 93.83 pulses per second or one every 10.65 milliseconds. However if you have a wind blowing at 1.5 MPH you will get 1 pulse per second and the time between positive and negative going edges can be around 500ms. If I make the interrupt block less than 600ms then I can only read 1.5MPH speed, all wind speeds faster will be blocked by the debounce logic. Here is the wind interrupt handler, basically the same as the rain except the debounce time.

void windSpeedIrqHandler()
{
    // Determine amount of time since last IRQ
    AvatarTime curTime = { 0 };
    AT_getTime(&curTime);
    windDeltaMills = AT_getElapsedMs(&lastWindIrqTime);
    // Remember this IRQ time to compare to next IRQ
    lastWindIrqTime = curTime;

    // Debounce wind switch, ringing on the way down and
    // noisey on the way back up
    if(windDeltaMills > 10)
    {
        windCount++;
    }
}

So as you can see I decided to use 10ms to debounce the wind speed interrupt handler. So right now I reliably get 4 interrupts per rotation so I am going to divide my count by 2 when using it in wind speed calculations. My theory is that I might need a stronger pull-up resistor than the weak pull-up provided by the PIC micro controller, or perhaps it just needs a conditioning capacitor. If I find the fix I will post an update to this article.

Alright, that's it for today next I'll be back with an update on the state of the hardware, so until next time Happy Making!