iTranslated by AI
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
volatileflag 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.
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
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.
AI・IoT・クラウドを中心に、実務で使える知見や検証結果を発信します。 お仕事のご相談はこちら: bloomblock.net/contact/
Discussion