Water leakage sensor using thin ribbon cable (testing reliability)



  • Here is a quick'n'dirty sensor I made using 2m long thin ribbon cable from ebay/aliexpress ($2-3) as a leakage sensor to monitor thumble dryer. Sometimes baby would take out the rubber on the doors, or I would not connect the condenser properly after cleaning and it would leak quite a lot of water on the floor.

    Almost invisible under the appliance

    0_1506373053923_Water leakage sensor 1 - IMG_0713_1024.jpg

    0_1506373074866_Water leakage sensor 2 - IMG_0715_1024.jpg

    0_1506373131763_Water leakage sensor 3 - IMG_0380_1024.jpg

    Used the Easy/Newbie pcb as the base, i really get bored of soldering the radio. Also used the little magnets as battery contacts, then the batteries do fit into the small box.
    For the sensor/contact itself I "polished" out the insulation on the ribbon using dremel polishing bit every feww centimeters (it is very easy to damage the copper line, so if you plan to reproduce this one, test with few bits). It looks ugly as hell but it is under the dryer so... functionality beats aesthetics here by far.
    I connected every second line with one contact, and every second with second contact, which gave me a sensor that would detect smallest drop of water (probably not needed but... playing around).

    0_1506373748875_Water leakage sensor 5 - IMG_0369_1024.jpg

    0_1506373421203_Water leakage sensor 4 - IMG_0375_1024.jpg

    This is the code I am using, quickly modified the code that @RobKuipers shared (thanks again!) here https://forum.mysensors.org/topic/4827/soil-moisture-sensor/13

    I just removed some not needed code and played around with sensitivity. It is checking for value every two minutes and sensitivity value around 50 works fine for me (if case before sending the moisture around line 149).
    I also flashed 1mhz bootloader with BOD lowered to 1.8v, so it runs a bit longer on batteries without power stepup module

    Here is the code that runs it

    /*
     Name:		MYS_MoistureSensor.ino
     Created:	5/25/2017 1:04:35 PM
     Author:	Rob
    
     Soil moisture measuring by using stainless steel rods (or any other conductor).
     Probably the simplest project ever, since only a MySensors-node and some wire is needed to set things up.
     
     The sketch alternates current during measuring to prevent corrosion of the rods due to electrolyses.
     Odd readings may occur when starting (eg. increasing soil moisture for no apparent reason), please just allow the electrodes to settle down in the soil.
     No extra hardware needed. 
     
     I use an Arduino Mini 3V3, powered on two AA cells. It is suggested you set the fuses for a lower Brown Out Detection (BOD). But anything goes.
    
     Just tie D4 and A0 together to one rod, and D5 and A1 to another rod. This is sensor one.
     For the second sensor tie D6 and A2 together to one rod, and D7 and A3 to another rod.
     Connect a pushbutton between GND and D3 if you need a button that makes the node report immediately (can be omitted)
    
     Measurement are taken every minute and send to the gateway if different from the previous reading.
     In case of no changes, the node reports itself every four hours.
     The output is between 0 (dry) and 100 (wet).
    
     Can also be used as a depth moisture sensor with three sensor zones; in that case use one (common) long rod and three smaller sensors along
     the height of the rod and configure the sketch accordingly.
    	 sensors[0] = { 4, A0, 5, A1, -1, false };
    	 sensors[1] = { 4, A0, 6, A2, -1, false };
    	 sensors[2] = { 4, A0, 7, A3, -1, false };
    
    */
    
    #include "Header.h"
    
    // Enable debug Serial.prints to serial monitor
    #define MY_DEBUG 
    
    #if defined MY_DEBUG
    #define Sprintln(a) (Serial.println(a))
    #define Sprint(a) (Serial.print(a))
    #else 
    #define Sprintln(a)
    #define Sprint(a)
    #endif
    
    // Enable and select radio type attached
    #define MY_RADIO_NRF24
    
    // Use PA_LOW for RF24+PA (Power Amplifier)
    //#define MY_RF24_PA_LEVEL RF24_PA_LOW
    //#define MY_RF24_PA_LEVEL RF24_PA_MAX
    
    #define MY_NODE_ID 42
    
    #include <MySensors.h>
    
    #define ACK 0        // = false
    #define CHILD_ID 1
    #define REPORTNOWSWITCH_PIN 3    // Arduino Digital I/O pin for button/reed switch (must be an interrupt pin!)
    
    #define NUM_MOISTURE_SENSORS 1
    #define CHILD_ID_TEMPERATURE (CHILD_ID+NUM_MOISTURE_SENSORS+1)
    
    #define SENSOR1_ROD1_DIGITAL 4
    #define SENSOR1_ROD1_ANALOG A0
    #define SENSOR1_ROD2_DIGITAL 5
    #define SENSOR1_ROD2_ANALOG A1
    
    
    #define SLEEP_IN_MS 120000		// every minute a new measurement
    #define EVERY_15_MINUTES (3600000/4/SLEEP_IN_MS)
    #define EVERY_4_HOURS (3600000*4/SLEEP_IN_MS) 
    #define NUM_READS (int)5		// Number of sensor reads for filtering
    
    int countLoops;
    int8_t interruptedBy = -1;
    int oldBatLevel;
    float oldTemperature;
    
    int output_value;
    
    /// Included in Header.h:
    //typedef struct {
    //	int digital_input_a;
    //	int analog_input_a;
    //	int digital_input_b;
    //	int analog_input_b;
    //	int level;
    //	bool connected;
    //} sensorWiring;
    
    sensorWiring sensors[NUM_MOISTURE_SENSORS];
    
    MyMessage msgMoistureSensor(CHILD_ID, V_TRIPPED);
    //MyMessage msgChipTemp(CHILD_ID_TEMPERATURE, V_TEMP);
    
    
    void before()
    {
    	// All buttons as input-pullup as per ATMEGA recommendation to use less power (and more safety) 
    	// (http://electronics.stackexchange.com/questions/43460/how-should-unused-i-o-pins-be-configured-on-atmega328p-for-lowest-power-consumpt)
    	for (int i = 1; i <= 8; i++)
    	{
    		pinMode(i, INPUT_PULLUP);
    	}
    
    	// Now explicity set pins as needed
    
    	// Setup report-now switch, activate internal pull-up
    	//pinMode(REPORTNOWSWITCH_PIN, INPUT_PULLUP);
    
    	// Initialize sensor variables
    
    	// Connect Digital pin 4 to Analog input A0 and a metal rod
    	// Connect Digital pin 5 to Analog input A1 and another metal rod.
    	sensors[0] = { SENSOR1_ROD1_DIGITAL, SENSOR1_ROD1_ANALOG, SENSOR1_ROD2_DIGITAL, SENSOR1_ROD2_ANALOG, -1, false };
    
    
    	for  (int i = 0; i<NUM_MOISTURE_SENSORS; i++)
    		sensors[i].connected = testSensorConnections(sensors[i]);
    }
    
    void setup()
    {
    
    }
    
    
    void presentation() {
    	sendSketchInfo("Water Leak Sensor no42", "1.1", ACK);
    
    	for (int i = 0; i < NUM_MOISTURE_SENSORS; i++)
    	{
    		if (sensors[i].connected) present(CHILD_ID+i, S_WATER_LEAK , ACK);
    	}
    	//present(CHILD_ID_TEMPERATURE, S_TEMP);
    }
    
    void loop()
    {
    	
    
    	for (int i = 0; i < NUM_MOISTURE_SENSORS; i++)
    	{
    		if (sensors[i].connected)
    		{
    			output_value = measure(sensors[i]);
    			
    			Sprint(F("output_value is "));
    			Sprintln(output_value);
    			if ((sensors[i].level != output_value) && output_value > 50)
    			{
    				sensors[i].level = output_value;
    				send(msgMoistureSensor.setSensor(CHILD_ID+i).set(1), ACK);
    			}
    		}
    	}
    
    	// Every fifteen minutes; poll temperature
    	if (countLoops%EVERY_15_MINUTES==0) 
    	{
    
    		int batLevel = getBatteryLevel();
    		if ((oldBatLevel != batLevel)) // ...but only when changed, or when button is pressed; 
    		{
    			sendBatteryLevel(batLevel, ACK);
    			oldBatLevel = batLevel;
    		}
    	}
    
    	// So you know I'm alive
    	if (countLoops == EVERY_4_HOURS)
    	{
    		sendHeartbeat(ACK);
    		countLoops = 0;
    	}
    
    	countLoops++;
    
    	interruptedBy = sleep(SLEEP_IN_MS);
    }
    
    // Connect Digital pin 'digital_input_a' to Analog input 'analog_input_a' and a metal rod,
    // do the same for b
    long measure(sensorWiring sensor)
    {
    	long total = 0;
    	int reading_a = 0;
    	int reading_b = 0;
    
    	for (int i = 0; i<NUM_READS; i++) {
    		// Left to right
    		reading_a = measureOneDirection(sensor.digital_input_a, sensor.digital_input_b, sensor.analog_input_a);
    		// Right to left
    		reading_b = measureOneDirection(sensor.digital_input_b, sensor.digital_input_a, sensor.analog_input_b);
    
    		total += reading_a + reading_b;
    	}
    	return map(total / (2 * NUM_READS), 1023, 0, 0, 100);
    }
    
    long measureOneDirection(int digital_input_1, int digital_input_2, int analog_input_1)
    {
    	pinMode(digital_input_2, OUTPUT);
    	digitalWrite(digital_input_2, LOW);
    	pinMode(digital_input_1, INPUT_PULLUP);
    	delayMicroseconds(100);
    	long reading = analogRead(analog_input_1);
    	//delayMicroseconds(25);
    	pinMode(digital_input_1, INPUT);     // High impedance                 
    	pinMode(digital_input_2, INPUT);     // High impedance                 
    	delay(1);
    
    	Sprint(F("measureOneDirection - Reading "));
    	Sprintln(reading);
    
    	return reading;
    }
    
    // test the connections of both rods of a sensor
    boolean testSensorConnections(sensorWiring moistureSensor)
    {
    	return (testSensorConnection(moistureSensor.digital_input_a, moistureSensor.analog_input_a) && testSensorConnection(moistureSensor.digital_input_b, moistureSensor.analog_input_b));
    }
    
    //  test if digital pin is connected to correct analog pin
    boolean testSensorConnection(int digital_input, int analog_input)
    {
    	pinMode(digital_input, OUTPUT);
    	digitalWrite(digital_input, HIGH);                        
    	delayMicroseconds(100);
    	long reading_1 = analogRead(analog_input);   
    	digitalWrite(digital_input, LOW);                      
    	delayMicroseconds(100);
    	long reading_2 = analogRead(analog_input);   
    	pinMode(digital_input, INPUT);     // High impedance                 
    	delay(1);
    
    	Sprint(F("testSensorConnection - Reading1 "));
    	Sprintln(reading_1);
    	Sprint(F("testSensorConnection - Reading2 "));
    	Sprintln(reading_2);
    
    	bool correct = ((reading_1 == 1023) && (reading_2 == 0));
    	return correct;
    }
    
    
    long readMUX(uint8_t aControl) 
    {
    	long result;
    
    	ADMUX = aControl;
    	delay(20); // Wait for Vref to settle
    	noInterrupts();
    	// start the conversion
    	ADCSRA |= _BV(ADSC) | _BV(ADIE);
    	set_sleep_mode(SLEEP_MODE_ADC);    // sleep during sample
    	interrupts();
    	sleep_mode();
    	// reading should be done, but better make sure
    	// maybe the timer interrupt fired 
    	while (bit_is_set(ADCSRA, ADSC));
    	// Reading register "ADCW" takes care of how to read ADCL and ADCH.
    	result = ADCW;
    
    	return result;
    
    }
    
    
    // Battery measure
    int getBatteryLevel()
    {
    	int results = (readVcc() - 2000) / 10;
    
    	if (results > 100)
    		results = 100;
    	if (results < 0)
    		results = 0;
    	return results;
    } // end of getBandgap
    
    // when ADC completed, take an interrupt 
    EMPTY_INTERRUPT(ADC_vect);
    
    long readVcc() {
    	long result;
    	// Read 1.1V reference against AVcc
    	result = readMUX(_BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1));
    
    	result = 1126400L / result; // Back-calculate AVcc in mV (1024 steps times 1100 mV (1.1V) = 1126400L)
    
    	return result;
    }
    
    
    // Utter nonsense, but needed for attaching an interrupt to...
    void debounce() {
    }
    
    

    Now the only question is: How long will copper wire detect the water before corroding? I understand that the code has polarity change to avoid electrolytic effect, but still moisture from the air will certainly corrode exposed wires. If it holds a year that would be ok, everything above that would be great 🙂
    If it doesn't work long enough, then I will just buy some more robust sensor and replace the ribbon strip with it.



  • Just wanted to report that node is still running as expected. Now after six months battery is reporting 51%, which is as expected (1 year on set of AAs checking every 2 minutes).
    And it has saved us from buying new flor several times (last time today) by detecting water under the dryer (which will most likely be replaced as it is either broken or stupid as hell, doesn't always detect if filter is full and just spills water on the flor).
    So ribbon cable is working fine as water leakage sensor, and batteries hold good enough 🙂


  • Hero Member

    Does tarnishing of the copper over time affect its conductivity?



  • @NeverDie to be honest I haven't measured anything scientifically as the copper tape is glued to the floor, and it is a bit difficult to test where it is positioned right now, but as mentioned, it is still reacting to the water on it, but I am just having some value over which I send 1 to the controller, so... even if it affects some reading, it is well in the tolerance.
    Only thing I noticed is that there is a bit of tape sticking in front of the appliance, and when we vacuum and mop, i think that we are gradually removing pieces of the tape it self. Which is not that critical for me as that part that was sticking out was basically a measurement error (me being clumsy). Even if I have to replace the tape after a year or so, it is still fine for me as alternative would be sensor with much smaller footprint, thus potentially even missing detecting water if it leaks behind/near the sensor. I've seen a lot of z-wave and also xiaomi sensors that have three pins as sensors, and that is fine but they require quite a lot of water to be triggered. I would rather get notification with first drops of water.

    I have just finished building another one using same sketch and logic, but this time using the usual "Rain Detection Sensor" sensor as this one actually needs to cover only a small area just bellow the valve.
    And I plan to build a few more, just for convenience and "feeling rich with sensors" if you guys know what I mean 🙂


  • Hero Member

    If it ever became a problem, you could use the same tinning chemicals used to tin the copper on PCB's.

    Also, maybe aluminum tape would work just as well? Then, you'd have no corrosion.

    What is the "usual 'Rain detection sensor'" that you're referring to?



  • @NeverDie good tip about tinning, and alu tape. I haven't seen them thought, only copper tape (perhaps I wasn't even looking). I will order some just to have it around if I get extra inspiration.

    Rain sensor is what it says on the store page https://www.mysensors.org/store/water
    1_1522787948453_Water leak - water heater-1_1024.jpg

    0_1522787948453_Water leak - water heater-2_1024.jpg



  • Then I have an "alarm" flow in node-red (via openhab) which blinks all lights in the house, sends notification to mobile phones with high priority etc. Which brings me a lot of joy when all works fine (and annoys my wife, as usual)...


Log in to reply
 

Suggested Topics

  • 8
  • 3
  • 44
  • 1
  • 1
  • 7

15
Online

11.4k
Users

11.1k
Topics

112.7k
Posts