iTranslated by AI

The content below is an AI-generated translation. This is an experimental feature, and may contain errors. View original article
💧

Detecting Water Leaks with Wio BG770A and Grove Water Sensor Using Interrupts (ISR)

に公開

In the previous article, we implemented a polling approach to read the analog value of a Grove Water Sensor every second.

While polling is simple to implement, it requires reading the sensor value every time, which raises concerns about power consumption for battery-powered devices.

In particular, when detecting events that rarely occur, such as water leakage, most sensor readings end up being meaningless.

To solve this, I implemented a method to detect changes in the sensor using interrupts with the Arduino ISR (Interrupt Service Routine).

What you will learn in this article

  • How the Grove Water Sensor's digital output works
  • Implementation patterns for interrupt detection using attachInterrupt
  • Why we use the volatile flag within an ISR (Interrupt Service Routine)

Equipment and Environment

Equipment Details
IoT Device Wio BG770A (Seeed Studio)
Sensor Grove - Water Sensor

Code Used

The entire code is available on GitHub.

https://github.com/oskar-iot/iotbuilds-samples/tree/main/wio-bg770a-water-sensor-interrupt

How Digital Output Works

The SIG pin of the Grove Water Sensor can be read as either an analog value (0–1023) or a digital value (HIGH/LOW).

The behavior when read digitally is simple.

State SIG Voltage digitalRead()
Dry High HIGH (1)
Wet Low LOW (0)

In the analog version, "lower values meant it was wetter," but in the digital version, you simply interpret LOW = Wet.

Pins: A4 and D28 are the same Grove connector

I used A4 for the analog version, but this time I will use D28.

constexpr uint8_t kDigitalSensorPin = D28; // Same Grove - Analog (P1)

A4 and D28 are the same physical pin on the same Grove Analog (P1) connector.
Last time, I executed analogRead(A4) on this pin to get the wetness level using a 0–1023 (analog) value.

This time, I will use digitalRead(D28) to obtain a HIGH/LOW digital value instead.

Wio BG770A connected to the Grove Water Sensor
Wio BG770A connected to the Grove Water Sensor

Implementing Interrupts

Polling has a structure where the "loop continues to run even if there is no change."
Interrupts "process only when there is a change," allowing the CPU to remain idle while there are no changes.

There are three key points for the implementation.

1. The ISR should only set a flag

An ISR (Interrupt Service Routine) is a function called when an interrupt occurs.
You must not call heavy processes like Serial.print() inside an ISR.
Instead, just set a volatile bool flag and leave the actual processing to the main loop.

volatile bool gDigitalStateChanged = false;

void onDigitalStateChanged() {
  gDigitalStateChanged = true;
}

volatile is a keyword that tells the compiler "this variable may be modified at any time." Because the ISR and the main loop run asynchronously, the compiler might optimize away changes if volatile is not used.

2. Use CHANGE to receive both edges

attachInterrupt(digitalPinToInterrupt(kDigitalSensorPin), onDigitalStateChanged, CHANGE);

Specifying CHANGE triggers an interrupt for both transitions: LOW to HIGH and HIGH to LOW.
This allows you to detect not only the moment water touches the sensor but also the moment it dries.

Looking at the definitions, you can also use FALLING and RISING as shown below.
FALLING triggers an interrupt only on HIGH→LOW, and RISING triggers only on LOW→HIGH.

//      LOW 0
//      HIGH 1
#define CHANGE 2
#define FALLING 3
#define RISING 4

3. Pause interrupts when reading the flag

if (gDigitalStateChanged) {
  noInterrupts();
  gDigitalStateChanged = false;
  interrupts();

  printSensorState("digital-change", digitalRead(kDigitalSensorPin));
}

Use noInterrupts() to stop interrupts while resetting the flag.
If you don't stop them, another interrupt might occur while resetting the flag, causing that event to be missed.

Experimental Results

The log below shows the state when it is dry (HIGH) at startup, followed by water being dropped onto the sensor, and then wiped off.

You can see the changes from HIGH (startup) → LOW (wet) → HIGH (after wiping).

Wio BG770A Water Sensor Interrupt Sample
Grove P1 digital input: D28(28)
Interrupt mode: detect digital HIGH/LOW changes
[startup] digital=HIGH(1)
[digital-change] digital=LOW(0)
[digital-change] digital=HIGH(1)

Unlike the polling version, the log is output only when the state changes.
While there are no changes, the loop simply runs, and nothing is output to the serial console.

Summary

By receiving the Grove Water Sensor's digital output with CHANGE, we were able to process both water detection and drying as events.

The pattern of setting a flag inside an ISR and processing it in the main loop is the standard approach for Arduino interrupt implementation.
Following these two points—using volatile and noInterrupts()—makes the implementation simple and effective.

I will use a PPK2 in the next article to measure how much power consumption differs between the polling and interrupt versions.

BloomBlockテックブログ

Discussion