Water Leak Detector Two Wires

Hi @oraclerouter -

Glad to hear you are getting some good reads, I am assuming you are pulling the pin low as you are reading Ground. If it was my project, I would for sure include some sort of time criteria as well, or take 100 samples per second and then use the average to eliminate false positives. Even better, use both. I would not rely one single readings, sorry of I was not clear enough :wink:

In my current sensor we are reading as many readings as possible each second, calculate average and then post the data.

Hope this helps.

HI @oraclerouter -

Also have a look at this project

Quick observation is that he seems to be pulling the pin HIGH rather than low. Tis might make more sense if you are reading low values even with only one pin submerged. If you pull it HIGH you should be able to easily discount any single value <2000 for example.

I am writing exams ons Friday, but will do my best to set up similar test on the weekend and report my finings.

Hope this helps.

I used it for 6 months and I did not have any false positives. I then used the hardware for another project and decommissioned it. I think thanks to this discussion I will put it back in service!

1 Like

No extra hardware required (with Proton at least)
With 100k resister between A1 and ground. Use 2 wires from +3.3 and A1
analogRead(A1) = around 37, touching both wires gives around 140, clean tap water = 1800

pinMode(A1, INPUT);
water = analogRead(A1);
if (water > 500) // make this value lower for more sensitive or higher for less sensitive.
{ // water, water, everywhere!}

I use a plastic bottle cap sitting on a paper towel to monitor my basement floor.


Hi @oraclerouter

I did something similar last year, but using an Arduino (Tiny85) to read the digital state, which would then power on the Argon to send an alarm.

Once the Tiny85 powers on the Argon via the EN Pin, you can have the argon do what ever you want, after running your code, you can then set a digital pin on the Argon High, which the Tiny85 reads to power the Argon off by setting the EN Pin low.

Tiny85 Code

#include <avr/sleep.h>
#include <avr/interrupt.h>

const int PARTICLE_EN_OFF = 0;             // Particle D6 connected to Tiny85 PB0
const int PARTICLE_EN_ON = 1;              // Tiny85 PB1 connected to Particle EN
const int ALARM_INPUT = 2;                 // Tiny85 Digital Input to power on Particle and Publish Alarm
const int ALARM_OUTPUT = 3;                // Tiny85 Digital Output Alarm Status - High or Low

void setup() {

    ADCSRA &= ~_BV(ADEN);                   // Disable ADC
    pinMode(ALARM_INPUT, INPUT);  
    digitalWrite(ALARM_INPUT, LOW);         // When first powered on set Alarm Input Low
    digitalWrite(ALARM_OUTPUT,LOW);         // When first powered on set Alarm Output(Status)Low
    digitalWrite(PARTICLE_EN_ON, LOW);      // Pulls Particle EN Low to Power Off on startup 
    pinMode(PARTICLE_EN_OFF, INPUT);        // Input to read when to power off Particle

void sleep() {

    GIMSK |= _BV(PCIE);                     // Enable Pin Change Interrupts
    PCMSK |= _BV(PCINT2);                   // Use PB2 as interrupt pin - Powers on Particle when Alarm Input goes High
    PCMSK |= _BV(PCINT0);                   // Use PB0 as interrupt pin - Powers off Particle when Particle D6 goes High
    //ADCSRA &= ~_BV(ADEN);                 // ADC off
    set_sleep_mode(SLEEP_MODE_PWR_DOWN);    // replaces above statement

    sleep_enable();                         // Sets the Sleep Enable bit in the MCUCR Register (SE BIT)
    sei();                                  // Enable interrupts
    sleep_cpu();                            // sleep

    cli();                                  // Disable interrupts
    PCMSK &= ~_BV(PCINT2);                  // Turn off PB2 as interrupt pin
    PCMSK &= ~_BV(PCINT0);                  // Turn off PB0 as interrupt pin
    sleep_disable();                        // Clear SE bit
    //ADCSRA |= _BV(ADEN);                  // ADC on

    sei();                                  // Enable interrupts
    } // sleep

ISR(PCINT0_vect) { // This is called when the interrupt occurs

    int ALARM_STATUS = digitalRead(ALARM_INPUT);
    int READ_PARTICLE = digitalRead(PARTICLE_EN_OFF);          

    if (ALARM_STATUS == HIGH) {                          
    digitalWrite(PARTICLE_EN_ON, HIGH);
    digitalWrite(ALARM_OUTPUT, HIGH);
    else digitalWrite(ALARM_OUTPUT, LOW);

    digitalWrite(PARTICLE_EN_ON, LOW);                   

void loop() {

Hi @ron7136

analogRead(A1) = around 37, touching both wires gives around 140, clean tap water = 1800

I might be misunderstanding something. How are you getting 140 when touching both wires? Shouldn’t that be > 4000 and the clean tap water >37 but <4000?

I’d assume “touching” to mean skin contact/touching with fingers (“dry skin” resistance is higher than in water) not having the wires touch eachother (that would be difficult with the contacts shown in the photo) :wink:


that’s amazing!

1 Like

that’s a smart one :wink:

ScruffR is correct, I meant touching both wires with my fingers. Sorry, that was not very clear on my part.

I’m not sure if I’m doing something wrong or I have a bad chip.
I tried @ron7136 solution with two wires and the Argon on a breadboard. Wires are not touching and the values fluctuate low/high.

Here is the code:

int analogPin = A1;
int val = 0;          

void setup()
    pinMode(analogPin, INPUT);
    Particle.variable("Volt", val);

void loop()
    val = analogRead(analogPin);    // read the input pin
    Particle.publish ("val", String(val));

Here are the results

What am I doing wrong?

What is your complete setup?
With pinMode(A1, INPUT) and no external pull resistor the pin is floating and expected to give fluctuating readings.

ron7136 is using an external 100k pull-down resistor.

Thanks … just before I saw your reply I realized what I have been doing wrong.

With this code I’m getting good reads but is this acceptable in terms of the hardware.
PinMode(analogPin, INPUT_PULLDOWN);

It should be OK, the internal pull-downs are in the region of ~40k.

1 Like

@ScruffR As aways I really appreciate the help!


@oraclerouter, @ScruffR, from the STM32 datasheets:


You will need an external pull-down of 100K as @ron7136 did.

1 Like

My experience with the Proton says this is true when configuring the pin as “INPUT” but the “INPUT_PULLDOWN” does work but in my testing the 100k made it more sensitive.

@peekay123 that would be for the Photon but does the same apply for Argon since it it using a different chip?

@oraclerouter, I missed the Argon part for some reason. I’ll check on the nRF52840 and report back.

Looking into the nRF52840 GPIO/Analog inputs, I discovered a couple of things:

  • The (digital) GPIO pull-up or pull-down resistance is “stronger” at 13K (typical) as opposed to the “weaker” 40K for the Photon.
  • Though there is no mention I could find of any pull-up or pull-down resistor for the Analog input mode, there are “pull-up” and “pull-down” LADDER resistors which are controlled via configuration registers:

    These resistors have a typical value of 160K. However, I am not sure if these are activated in the DeviceOS as there is no mechanism in the commands to specify a pull direction. It seems to me that with a value of 160K, these are there to specifically allow the user to prevent the input from floating.

The GPIO and SAAC (analog input) configuration register are different and I doubt declaring a pin as digital input with pull-down will work when the pin is reconfigured for analog input mode.