Skip to content
  • MySensors
  • OpenHardware.io
  • Categories
  • Recent
  • Tags
  • Popular
Skins
  • Light
  • Brite
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • Default (No Skin)
  • No Skin
Collapse
Brand Logo
  1. Home
  2. Announcements
  3. 💬 Soil Moisture Sensor

💬 Soil Moisture Sensor

Scheduled Pinned Locked Moved Announcements
99 Posts 33 Posters 31.6k Views 32 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • R Offline
    R Offline
    RobKuipers
    wrote on last edited by RobKuipers
    #13

    My two cents. A soil moisture sensor that requires no extra hardware (not counting electric wire). Highly inspired by everything I've read in this thread. Comments welcome.
    Excuse the long post.

    /*
     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_RFM69
    #define MY_RFM69_FREQUENCY RF69_868MHZ
    #define MY_IS_RFM69HW
    
    // 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 4
    
    #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 2
    #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 SENSOR2_ROD1_DIGITAL 6
    #define SENSOR2_ROD1_ANALOG A2
    #define SENSOR2_ROD2_DIGITAL 7
    #define SENSOR2_ROD2_ANALOG A3
    
    #define SLEEP_IN_MS 60000		// 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)10		// 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_LEVEL);
    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 };
    
    	// Connect Digital pin 6 to Analog input A2 and a metal rod
    	// Connect Digital pin 7 to Analog input A3 and another metal rod.
    	sensors[1] = { SENSOR2_ROD1_DIGITAL, SENSOR2_ROD1_ANALOG, SENSOR2_ROD2_DIGITAL, SENSOR2_ROD2_ANALOG, -1, false };
    
    	for  (int i = 0; i<NUM_MOISTURE_SENSORS; i++)
    		sensors[i].connected = testSensorConnections(sensors[i]);
    }
    
    void setup()
    {
    
    }
    
    
    void presentation() {
    	sendSketchInfo("Moisture Sensor", "1.1", ACK);
    
    	for (int i = 0; i < NUM_MOISTURE_SENSORS; i++)
    	{
    		if (sensors[i].connected) present(CHILD_ID+i, S_MOISTURE, ACK);
    	}
    	present(CHILD_ID_TEMPERATURE, S_TEMP);
    }
    
    void loop()
    {
    	bool reportNow = (interruptedBy == digitalPinToInterrupt(REPORTNOWSWITCH_PIN));
    
    	if (reportNow)
    	{
    		// Little trick for debouncing the switch
    		attachInterrupt(digitalPinToInterrupt(REPORTNOWSWITCH_PIN), debounce, RISING);
    		wait(500);
    
    		Sprintln(F("Report now switch pressed"));
    		countLoops = 0;
    
    	}
    
    	for (int i = 0; i < NUM_MOISTURE_SENSORS; i++)
    	{
    		if (sensors[i].connected)
    		{
    			output_value = measure(sensors[i]);
    			if ((sensors[i].level != output_value) || reportNow)
    			{
    				sensors[i].level = output_value;
    				send(msgMoistureSensor.setSensor(CHILD_ID+i).set(output_value), ACK);
    			}
    		}
    	}
    
    	// Every fifteen minutes; poll temperature
    	if (countLoops%EVERY_15_MINUTES==0 || reportNow) 
    	{
    		float newTemp = readTemp();
    		if (oldTemperature != newTemp || reportNow)
    		{
    			send(msgChipTemp.set(newTemp, 1), ACK);
    			oldTemperature = newTemp;
    		}
    
    		int batLevel = getBatteryLevel();
    		if ((oldBatLevel != batLevel) || reportNow) // ...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(digitalPinToInterrupt(REPORTNOWSWITCH_PIN), FALLING, 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;
    }
    
    float readTemp() 
    {
    #if defined (xxMY_RADIO_RFM69) && !defined(MY_RFM69_NEW_DRIVER)
    	return _radio.readTemperature(-3);
    #else
    	// Read 1.1V reference against MUX3  
    	return (readMUX(_BV(REFS1) | _BV(REFS0) | _BV(MUX3)) - 125) * 0.1075f;
    #endif
    }
    
    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() {
    }
    
    
    
    
    ร 1 Reply Last reply
    1
    • E Offline
      E Offline
      Ed1500
      wrote on last edited by
      #14

      The greatest problem with these sensors is electrolysis and subsequent oxidation, due to the DC current flowing through the sensor in a humid environment.
      There are some solutions: Most of the circuits that supposedly feed the sensor with A are bogus as it is apulsed DC at best..
      One could try a capacitive sensor...... in theory very good but in practice plagued by issues.

      What I have done is to remove constant current from the sensor by feeding it from a transistor that I can switch on and off. I take a measurement every 2-6 hrs and switch the current off in between. Makes a huge difference

      1 Reply Last reply
      2
      • rollercontainerR Offline
        rollercontainerR Offline
        rollercontainer
        wrote on last edited by rollercontainer
        #15

        Next logic step is flipping polarity like here: http://gardenbot.org/howTo/soilMoisture/

        E 1 Reply Last reply
        0
        • rollercontainerR rollercontainer

          Next logic step is flipping polarity like here: http://gardenbot.org/howTo/soilMoisture/

          E Offline
          E Offline
          Ed1500
          wrote on last edited by
          #16

          @rollercontainer That is definitely a good solution too, but I think gardenbot approaches it a bit too complicated from the software side when he points out that you get two readings with different values that 'need to be smoothed'

          I'd say do this:
          Both pins LOW when you are not taking a reading. That restperiod can be hours.
          When you are ready to take a reading:
          make one pin HIGH, take a reading and discard that one
          Flip the voltage, take another reading (to balance the time) and use that one
          Both pins LOW again

          R 1 Reply Last reply
          0
          • E Ed1500

            @rollercontainer That is definitely a good solution too, but I think gardenbot approaches it a bit too complicated from the software side when he points out that you get two readings with different values that 'need to be smoothed'

            I'd say do this:
            Both pins LOW when you are not taking a reading. That restperiod can be hours.
            When you are ready to take a reading:
            make one pin HIGH, take a reading and discard that one
            Flip the voltage, take another reading (to balance the time) and use that one
            Both pins LOW again

            R Offline
            R Offline
            RobKuipers
            wrote on last edited by
            #17

            @Ed1500 This is exactly what I've done with the sketch I posted earlier in this thread.
            Use two inputs per sensor. Flip polarity for every reading and then rest in a high impedance state so as not to corrode the measuring rods (just some copper wire in my case). This has the added benefit of using the least power.
            Has been working like a charm for a couple of months now. Still planning to make a couple extra for the garden.

            E 1 Reply Last reply
            0
            • R RobKuipers

              @Ed1500 This is exactly what I've done with the sketch I posted earlier in this thread.
              Use two inputs per sensor. Flip polarity for every reading and then rest in a high impedance state so as not to corrode the measuring rods (just some copper wire in my case). This has the added benefit of using the least power.
              Has been working like a charm for a couple of months now. Still planning to make a couple extra for the garden.

              E Offline
              E Offline
              Ed1500
              wrote on last edited by
              #18

              @RobKuipers sensible, good sketch. Truthfully, with just the very short reading alone (I do say a milisecond or less per 4 hours), the sensor hardly corrodes. I have two galvanized nails that I have in the soil for 4 seasons. Yes, not silky smooth anymore but really no trace of electrolysis, even one made from a clotheshanger that still is doing well

              1 Reply Last reply
              0
              • peternilsson75P Offline
                peternilsson75P Offline
                peternilsson75
                wrote on last edited by
                #19

                Did this sketch / wiring / sensor work for anyone? I read that more than I had questions about the pin 6 and 7 but those pins aren't connected in the wiring diagram. If anyone have any ideas on what to change to get it working it would be great to know!

                1 Reply Last reply
                0
                • M Offline
                  M Offline
                  manutremo
                  wrote on last edited by
                  #20

                  The sketch does not correspond to the sensor shown in the images. It is much better to use the 2-pin sensors and drive them directly using digital outputs. Search for FC28 (better) or YL-69 in ebay or amazon. I use a voltage divider with a 10k resistor

                  I've been using the alternating polarity approach on about 20 sensors, with perfect results and no signs of corrosion after around 6 months. I run some tests to determine the effect of measuring time and finally came up with 5ms with no averaging. The batteries last for months; I'm not sure how many since I haven't yet had to replace any (status led removed from 3.3v arduino mini pro board).

                  As a reference, I tested a rain sensor (same principle) with no alternating current and as soon as a drop of water touched the tracks, small bubbles were produced with indicated that electrolysis was taking place. The effect could be seen on the tracks after just a couple of minutes.

                  I'm attaching my sketch below. It reports battery level in addition to moisture. It uses the development branch of the mysensors library in order to use the new version of the rfm69 drivers with RSSI ATC - which btw works more than perfect.

                  I'm using Domoticz which includes a predefined device for moisture. This device uses the centibar scale, so I calibrated my sensors in % moisture and then convert to cb.

                  #define MY_RADIO_RFM69
                  #define MY_RFM69_NEW_DRIVER   // ATC on RFM69 works only with the new driver (not compatible with old=default driver)
                  #define MY_IS_RFM69HW
                  #define MY_RFM69_FREQUENCY RFM69_868MHZ
                  #define MY_RFM69_ATC_TARGET_RSSI_DBM (-70)
                  #define MY_RFM69_NETWORKID  100
                  
                  #define MY_PARENT_NODE_ID 0
                  #define MY_PARENT_NODE_IS_STATIC
                  #define MY_TRANSPORT_MAX_TX_FAILURES 3
                  
                  #define MY_DEBUG 
                  
                  #include <MySensors.h>
                  #include <SPI.h>
                  #include <Vcc.h>
                  #include <Streaming.h>
                  #include <math.h>
                  
                  #define VERSION "1.1"
                  /* Measurement probe connected to pins shown below
                  I avoided using pins 2 and 3 because they are reserved for IRQ (potential future use) - 2 is also used by the RFM69 module.
                  Although this may not actually have a noticeable effect, I also avoided 5 and 6 because they support PWM and hence are a bit slower.
                  */
                  #define PIN_ALIM1 4                                   // Connect to input of resistor
                  #define PIN_ALIM2 7                                   // Connect to input of measuring probe
                  #define PIN_LECTURA A0
                  
                  #define AGUA_DIR 780.0
                  #define AGUA_INV 160.0
                  #define AIRE_DIR 0.0
                  #define AIRE_INV 1023.0
                  #define TIEMPO_LECTURA 5
                  #define SLEEP_TIME_1h 3132000 // 1 h = 1*60*60000 = 3600000 ms -13% = 3132000 ms(my arduinos show a delay of 8s/min = 13%)
                  #define SLEEP_TIME_2h 6264000 // 2 h = 2*60*60000 = 7200000 ms -13% = 6264000 ms
                  #define SLEEP_TIME_3h 9396000 // 3 h = 3*60*60000 = 10800000 ms -13% = 9396000 ms
                  
                  // Battery calibration (Li-ion)
                  const float VccMin   = 3.0;                         // Minimum expected Vcc level, in Volts.
                  const float VccMax   = 4.2;                         // Maximum expected Vcc level, in Volts.
                  const float VccCorrection = 3.82/3.74;              // Measured Vcc by multimeter divided by reported Vcc
                  
                  #define CHILD_MOIST_ID 1
                  MyMessage msgmoist(CHILD_MOIST_ID, V_LEVEL);
                  Vcc vcc(VccCorrection);
                  
                  float oldresultcb=0;
                  int oldbat=0, count=0;
                  
                  void presentation(){
                    Serial.begin(115200);
                    sendSketchInfo("Sensor de humedad", VERSION);
                    present(CHILD_MOIST_ID, S_MOISTURE, "Humedad suelo");
                    analogReference(DEFAULT);
                    pinMode(PIN_LECTURA, INPUT);
                    pinMode(PIN_ALIM1, OUTPUT);
                    pinMode(PIN_ALIM2, OUTPUT);
                  }
                  
                  void loop()
                  {
                    unsigned int value1, value2;
                    float result1, result2, resultp, resultcb;
                  
                  //Measurement of moisture
                    wait(TIEMPO_LECTURA);
                    digitalWrite(PIN_ALIM1, HIGH);
                    digitalWrite(PIN_ALIM2, LOW);
                    wait(TIEMPO_LECTURA);
                    value1=analogRead(PIN_LECTURA);
                    result1=constrain(value1/(AGUA_DIR-AIRE_DIR)*100.0, 1, 100);
                  
                    digitalWrite(PIN_ALIM1, LOW);
                    digitalWrite(PIN_ALIM2, HIGH);
                    wait(TIEMPO_LECTURA);
                    value2=analogRead(PIN_LECTURA);
                    digitalWrite(PIN_ALIM1, LOW);
                    digitalWrite(PIN_ALIM2, LOW);
                    result2=constrain(100-(value2-AGUA_INV)/(AIRE_INV-AGUA_INV)*100.0,1,100);
                  
                  /*Conversion from % moisture to cb taken from http://lieth.ucdavis.edu/Research/tens/98/SmtPub.htm
                  Another option https://www.researchgate.net/figure/260321179_fig1_Fig-1-Relation-curve-between-water-tension-cb-and-soil-moisture-percentage
                  The scale used in Domoticz is explained here http://www.irrometer.com/basics.html and can be checked in file domoticz/main/RFXNames.cpp
                    0-10 Saturated Soil. Occurs for a day or two after irrigation 
                    10-20 Soil is adequately wet (except coarse sands which are drying out at this range) 
                    20-60 Usual range to irrigate or water (most soils except heavy clay soils). 
                    60-100 Usual range to irrigate heavy clay soils 
                    100-200 Soil is becoming dangerously dry
                  */
                    resultp=(result1+result2)/2.0;
                    resultcb=constrain(square((-2.96699+351.395/resultp)),0,200);                           //Equation fit using stat software
                    count++;
                    
                  //Send the data
                    if ((oldresultcb!=resultcb) || (count==4)) send(msgmoist.set((unsigned int)resultcb));
                  
                  //Measure battery voltage here since it has been under change recently (more reliable)
                    float v = vcc.Read_Volts();  
                    int p = vcc.Read_Perc(VccMin, VccMax);
                    p=constrain(p,0,100);
                    if ((p!=oldbat) || (count==4)) sendBatteryLevel(p);
                  
                  //Save the last values and reset the counter
                    oldresultcb=resultcb;
                    oldbat=p;
                    if (count==4) count=0;
                  
                  #ifdef MY_DEBUG
                    Serial << "Value1=" << value1 << " " << result1 << endl << "Value2=" << value2 << " " << result2 << endl << "Result = " << resultp << "% (" << resultcb << "cb)" << endl;
                    Serial << "VCC = " << v << " Volts" << endl << "VCC% = " << p << " %" << endl;
                  #endif
                  
                    sleep(SLEEP_TIME_2h, true);  
                    }
                  

                  And this is how it looks in Domoticz:

                  alt text

                  I hope this helps.

                  peternilsson75P 1 Reply Last reply
                  2
                  • M manutremo

                    The sketch does not correspond to the sensor shown in the images. It is much better to use the 2-pin sensors and drive them directly using digital outputs. Search for FC28 (better) or YL-69 in ebay or amazon. I use a voltage divider with a 10k resistor

                    I've been using the alternating polarity approach on about 20 sensors, with perfect results and no signs of corrosion after around 6 months. I run some tests to determine the effect of measuring time and finally came up with 5ms with no averaging. The batteries last for months; I'm not sure how many since I haven't yet had to replace any (status led removed from 3.3v arduino mini pro board).

                    As a reference, I tested a rain sensor (same principle) with no alternating current and as soon as a drop of water touched the tracks, small bubbles were produced with indicated that electrolysis was taking place. The effect could be seen on the tracks after just a couple of minutes.

                    I'm attaching my sketch below. It reports battery level in addition to moisture. It uses the development branch of the mysensors library in order to use the new version of the rfm69 drivers with RSSI ATC - which btw works more than perfect.

                    I'm using Domoticz which includes a predefined device for moisture. This device uses the centibar scale, so I calibrated my sensors in % moisture and then convert to cb.

                    #define MY_RADIO_RFM69
                    #define MY_RFM69_NEW_DRIVER   // ATC on RFM69 works only with the new driver (not compatible with old=default driver)
                    #define MY_IS_RFM69HW
                    #define MY_RFM69_FREQUENCY RFM69_868MHZ
                    #define MY_RFM69_ATC_TARGET_RSSI_DBM (-70)
                    #define MY_RFM69_NETWORKID  100
                    
                    #define MY_PARENT_NODE_ID 0
                    #define MY_PARENT_NODE_IS_STATIC
                    #define MY_TRANSPORT_MAX_TX_FAILURES 3
                    
                    #define MY_DEBUG 
                    
                    #include <MySensors.h>
                    #include <SPI.h>
                    #include <Vcc.h>
                    #include <Streaming.h>
                    #include <math.h>
                    
                    #define VERSION "1.1"
                    /* Measurement probe connected to pins shown below
                    I avoided using pins 2 and 3 because they are reserved for IRQ (potential future use) - 2 is also used by the RFM69 module.
                    Although this may not actually have a noticeable effect, I also avoided 5 and 6 because they support PWM and hence are a bit slower.
                    */
                    #define PIN_ALIM1 4                                   // Connect to input of resistor
                    #define PIN_ALIM2 7                                   // Connect to input of measuring probe
                    #define PIN_LECTURA A0
                    
                    #define AGUA_DIR 780.0
                    #define AGUA_INV 160.0
                    #define AIRE_DIR 0.0
                    #define AIRE_INV 1023.0
                    #define TIEMPO_LECTURA 5
                    #define SLEEP_TIME_1h 3132000 // 1 h = 1*60*60000 = 3600000 ms -13% = 3132000 ms(my arduinos show a delay of 8s/min = 13%)
                    #define SLEEP_TIME_2h 6264000 // 2 h = 2*60*60000 = 7200000 ms -13% = 6264000 ms
                    #define SLEEP_TIME_3h 9396000 // 3 h = 3*60*60000 = 10800000 ms -13% = 9396000 ms
                    
                    // Battery calibration (Li-ion)
                    const float VccMin   = 3.0;                         // Minimum expected Vcc level, in Volts.
                    const float VccMax   = 4.2;                         // Maximum expected Vcc level, in Volts.
                    const float VccCorrection = 3.82/3.74;              // Measured Vcc by multimeter divided by reported Vcc
                    
                    #define CHILD_MOIST_ID 1
                    MyMessage msgmoist(CHILD_MOIST_ID, V_LEVEL);
                    Vcc vcc(VccCorrection);
                    
                    float oldresultcb=0;
                    int oldbat=0, count=0;
                    
                    void presentation(){
                      Serial.begin(115200);
                      sendSketchInfo("Sensor de humedad", VERSION);
                      present(CHILD_MOIST_ID, S_MOISTURE, "Humedad suelo");
                      analogReference(DEFAULT);
                      pinMode(PIN_LECTURA, INPUT);
                      pinMode(PIN_ALIM1, OUTPUT);
                      pinMode(PIN_ALIM2, OUTPUT);
                    }
                    
                    void loop()
                    {
                      unsigned int value1, value2;
                      float result1, result2, resultp, resultcb;
                    
                    //Measurement of moisture
                      wait(TIEMPO_LECTURA);
                      digitalWrite(PIN_ALIM1, HIGH);
                      digitalWrite(PIN_ALIM2, LOW);
                      wait(TIEMPO_LECTURA);
                      value1=analogRead(PIN_LECTURA);
                      result1=constrain(value1/(AGUA_DIR-AIRE_DIR)*100.0, 1, 100);
                    
                      digitalWrite(PIN_ALIM1, LOW);
                      digitalWrite(PIN_ALIM2, HIGH);
                      wait(TIEMPO_LECTURA);
                      value2=analogRead(PIN_LECTURA);
                      digitalWrite(PIN_ALIM1, LOW);
                      digitalWrite(PIN_ALIM2, LOW);
                      result2=constrain(100-(value2-AGUA_INV)/(AIRE_INV-AGUA_INV)*100.0,1,100);
                    
                    /*Conversion from % moisture to cb taken from http://lieth.ucdavis.edu/Research/tens/98/SmtPub.htm
                    Another option https://www.researchgate.net/figure/260321179_fig1_Fig-1-Relation-curve-between-water-tension-cb-and-soil-moisture-percentage
                    The scale used in Domoticz is explained here http://www.irrometer.com/basics.html and can be checked in file domoticz/main/RFXNames.cpp
                      0-10 Saturated Soil. Occurs for a day or two after irrigation 
                      10-20 Soil is adequately wet (except coarse sands which are drying out at this range) 
                      20-60 Usual range to irrigate or water (most soils except heavy clay soils). 
                      60-100 Usual range to irrigate heavy clay soils 
                      100-200 Soil is becoming dangerously dry
                    */
                      resultp=(result1+result2)/2.0;
                      resultcb=constrain(square((-2.96699+351.395/resultp)),0,200);                           //Equation fit using stat software
                      count++;
                      
                    //Send the data
                      if ((oldresultcb!=resultcb) || (count==4)) send(msgmoist.set((unsigned int)resultcb));
                    
                    //Measure battery voltage here since it has been under change recently (more reliable)
                      float v = vcc.Read_Volts();  
                      int p = vcc.Read_Perc(VccMin, VccMax);
                      p=constrain(p,0,100);
                      if ((p!=oldbat) || (count==4)) sendBatteryLevel(p);
                    
                    //Save the last values and reset the counter
                      oldresultcb=resultcb;
                      oldbat=p;
                      if (count==4) count=0;
                    
                    #ifdef MY_DEBUG
                      Serial << "Value1=" << value1 << " " << result1 << endl << "Value2=" << value2 << " " << result2 << endl << "Result = " << resultp << "% (" << resultcb << "cb)" << endl;
                      Serial << "VCC = " << v << " Volts" << endl << "VCC% = " << p << " %" << endl;
                    #endif
                    
                      sleep(SLEEP_TIME_2h, true);  
                      }
                    

                    And this is how it looks in Domoticz:

                    alt text

                    I hope this helps.

                    peternilsson75P Offline
                    peternilsson75P Offline
                    peternilsson75
                    wrote on last edited by
                    #21

                    @manutremo sensors ordered and I'm looking forward to try it! Just a quick question; where did you connect the resistor? Thanks,

                    Peter

                    R 1 Reply Last reply
                    0
                    • peternilsson75P peternilsson75

                      @manutremo sensors ordered and I'm looking forward to try it! Just a quick question; where did you connect the resistor? Thanks,

                      Peter

                      R Offline
                      R Offline
                      RobKuipers
                      wrote on last edited by
                      #22

                      @peternilsson75 With the sketch I posted back in May, you just need two pieces of conductive wire. No resistor or amplifier needed. :-)

                      1 Reply Last reply
                      0
                      • D Offline
                        D Offline
                        dakipro
                        wrote on last edited by
                        #23

                        Hey @RobKuipers I am trying to reuse your sketch as a water leakage sensor, but I got stopped at the Header.h file. Can you please share its content?

                        I am also considering using two pieces of wire (on a ribbon strip though, just to peal isolation at strategic places).
                        I will use it on battery and I was thinking on measuring every minute or so, and send a heartbeat every half hour/hour with battery status.
                        Do you have any other tips about converting the sketch to water leak?
                        Thanks for the sketch btw!

                        C: OpenHAB2 with node-red on linux laptop
                        GW: Arduino Nano - W5100 Ethernet, Nrf24l01+ 2,4Ghz mqtt
                        GW: Arduino Mega, RFLink 433Mhz

                        R 1 Reply Last reply
                        0
                        • D dakipro

                          Hey @RobKuipers I am trying to reuse your sketch as a water leakage sensor, but I got stopped at the Header.h file. Can you please share its content?

                          I am also considering using two pieces of wire (on a ribbon strip though, just to peal isolation at strategic places).
                          I will use it on battery and I was thinking on measuring every minute or so, and send a heartbeat every half hour/hour with battery status.
                          Do you have any other tips about converting the sketch to water leak?
                          Thanks for the sketch btw!

                          R Offline
                          R Offline
                          RobKuipers
                          wrote on last edited by
                          #24

                          @dakipro the content of header.h is

                          typedef struct {
                          int digital_input_a;
                          int analog_input_a;
                          int digital_input_b;
                          int analog_input_b;
                          int level;
                          bool connected;
                          } sensorWiring;
                          

                          Detecting water leakage I would do exactly as you suggested: if you mean eg. to detect a leaking washing machine, it could be monitored by laying the stripped wires parallel on the bottom of a container or tray and put the machine on top of it.
                          It should be easy to modify or extend the sketch to implement a binary switch to indicate leakage above a certain moisture threshold.

                          Good luck. Please let us know about your progress.
                          Rob

                          1 Reply Last reply
                          0
                          • D Offline
                            D Offline
                            dakipro
                            wrote on last edited by
                            #25

                            Thanks @RobKuipers , it compiles fine now. I will work on finetuning the code, just to confirm, you attach one wire to the both D4 and A0, and second to D5 and A1 ?

                            I will test with a variation of this ribbon wire https://ae01.alicdn.com/kf/HTB1admbHVXXXXcfXVXXq6xXFXXXb/NEW-font-b-laptop-b-font-Switch-touchpad-font-b-cable-b-font-1-0mm-pitch.jpg
                            Just to expose the wire every few cm with a dremel/polisher, and stick the wire to the floor under the appliance somehow (double tape).

                            (for start I will put it under dryer actually, because baby sometimes removes the rubber sealing and it starts leaking)

                            But I am now being concerned about the corrosion, as it is very thin wire... If it doesn't work long, I will replace it with something more robust and corrosion resistant.

                            C: OpenHAB2 with node-red on linux laptop
                            GW: Arduino Nano - W5100 Ethernet, Nrf24l01+ 2,4Ghz mqtt
                            GW: Arduino Mega, RFLink 433Mhz

                            R 1 Reply Last reply
                            0
                            • D dakipro

                              Thanks @RobKuipers , it compiles fine now. I will work on finetuning the code, just to confirm, you attach one wire to the both D4 and A0, and second to D5 and A1 ?

                              I will test with a variation of this ribbon wire https://ae01.alicdn.com/kf/HTB1admbHVXXXXcfXVXXq6xXFXXXb/NEW-font-b-laptop-b-font-Switch-touchpad-font-b-cable-b-font-1-0mm-pitch.jpg
                              Just to expose the wire every few cm with a dremel/polisher, and stick the wire to the floor under the appliance somehow (double tape).

                              (for start I will put it under dryer actually, because baby sometimes removes the rubber sealing and it starts leaking)

                              But I am now being concerned about the corrosion, as it is very thin wire... If it doesn't work long, I will replace it with something more robust and corrosion resistant.

                              R Offline
                              R Offline
                              RobKuipers
                              wrote on last edited by
                              #26

                              @dakipro Good to hear you have it up and running. You are correct about the connections.
                              Any wire will do, they just have to be close to each other. Enough to bridge the leaking water. As soon as both wires touch the same puddle you will get readings way above zero.
                              The sketch can do multiple detectors with just one Arduino; so you could could create separate alarms for both the washer and the dryer :-)

                              1 Reply Last reply
                              0
                              • alowhumA Offline
                                alowhumA Offline
                                alowhum
                                Plugin Developer
                                wrote on last edited by
                                #27

                                Has anyone had a look at the "Chirp" sensor? It's a great little open hardware project that can be bought on Aliexpress for $4 - $6.

                                alt text

                                • It monitors the waterlevel and light level.
                                • When soil moisture is low, it will chirp to let you know it needs water.
                                • It can be read out via i2c!
                                mfalkviddM 1 Reply Last reply
                                0
                                • alowhumA alowhum

                                  Has anyone had a look at the "Chirp" sensor? It's a great little open hardware project that can be bought on Aliexpress for $4 - $6.

                                  alt text

                                  • It monitors the waterlevel and light level.
                                  • When soil moisture is low, it will chirp to let you know it needs water.
                                  • It can be read out via i2c!
                                  mfalkviddM Online
                                  mfalkviddM Online
                                  mfalkvidd
                                  Mod
                                  wrote on last edited by mfalkvidd
                                  #28

                                  @alowhum yes, the chirp sensor has been discussed a few times.

                                  1 Reply Last reply
                                  0
                                  • D Offline
                                    D Offline
                                    dakipro
                                    wrote on last edited by
                                    #29

                                    Here is the water leakage sensor I was mentioning few posts above, using thin ribbon cable as a sensor.
                                    https://forum.mysensors.org/topic/7736/water-leakage-sensor-using-thin-ribbon-cable-testing-reliability
                                    Time will tell if it is usable at all before it corrodes completely.
                                    Thanks @RobKuipers for the code, works like a charm!

                                    C: OpenHAB2 with node-red on linux laptop
                                    GW: Arduino Nano - W5100 Ethernet, Nrf24l01+ 2,4Ghz mqtt
                                    GW: Arduino Mega, RFLink 433Mhz

                                    1 Reply Last reply
                                    0
                                    • L Offline
                                      L Offline
                                      lshield
                                      wrote on last edited by
                                      #30

                                      This is a really simple question but I'm new to the whole thing. How would I use the water level sensor which is shown in the shopping guide? I understand that it's analog but I'm not completely sure how I'd write the sketch. Thanks!

                                      1 Reply Last reply
                                      0
                                      • M Offline
                                        M Offline
                                        manutremo
                                        wrote on last edited by
                                        #31

                                        Just to summarize since the thread is becoming a bit confusing.

                                        The sensor shown in the example and the shopping guide is no more than a device that measures the resistance between the two pins of the fork. That is done by the boards, which includes an analog output and a digital output.

                                        Should you just need to know when moisture is over or below a certain degree, just connect the digital output to a digital pin in the Arduino. Then use the potentiometer in the board to decide the switching point. In a battery powered node, this could be connected to an interrupt pin so the node sleeps and is only waken up when the moisture falls under the predetermined level to send an alert to the controller. But if you want to know track how moisture evolves, you may connect the analog output of the board to an analog pin in the arduino, which will provide a numerical value. Then the sensor needs to be calibrated; there are several forms but one involves measuring the output when the fork is submerged in water (which would be 100% moisture) and then when it's in air (that would be 0%). You can then map this scale to a moisture scale, typically a cb scale.

                                        The negative side of using that board is that the current always flows in the same direction through the fork. The same occurs with another similar type of sensor like the sparkfun here. This will lead in some time to corrosion of the fork, even if it's one of the latest nickeled ones. Reports in the internet vary from weeks to months, but in any case the form will corrode and as a result the measurement will drift slowly.

                                        The alternating polarization strategy tries to overcome this problem. To do so, the board is removed and only the fork is used. Instead of connecting it to Vcc and GND, the two terminals are connected so that the fork is actually one of the resistors in a voltage divider. The other resistor is usually a 10k resistor. In this setup, one of the digital pins is connected to one leg of the resistor, the other resistor leg is connected to one side of the fork, and the other side of the fork is connected to the other digital pin on the Arduino. Another wire needs then to be connected between the connection between the resistor and the fork, to an analog pin of the Arduino, which will read a value that will be proportional to the resistance of the fork, therefore to the moisture level. Then, by switching the pins from INPUT to OUTPUT, and from HIGH to LOW, you can have the current flow in one direction or the opposite one, which significantly delays the corrosion. In my case, the forks still look like new after months of use. Corrosion speed will still obviously depend by time between readings, reading time, soil type and other factors. I've never experimented with this sensor but with a rain sensor I could see corrosion symptoms after some minutes of continuous readings. This sensor also needs to be calibrated in a similar way as the former one. This setup makes the sketch a bit more complex but there are multiple examples here and in the internet.

                                        There are also variations on the measurement strategy within this approach. For example, you may just take a reading in one direction, another reading in the other direction, convert them to moisture level, and average them. Other people take several readings and average them all. I realized that if the reading is repeated, the value increases with each reading until it stabilizes at a certain value, so I decided to have the sketch iterate until two consecutive readings get the same result. The measuring time also needs to be asessed; in my investigation, the shorter the time, the less battery consumption, but at some point around 5ms the readings started to be unreliable. On the other hand, the longer the measurement the more realiable, but the span of the measurements in analog pin where closer and closer which led to loss of accuracy, and of course higher battery consumption. I decided 10ms was a good balance but others' milage may vary.

                                        Finally, there are other completely types of moisture sensor that measure the soil dielectric constant instead of its resistance. They are said to be more reliable, and additionally they do not suffer from corrosion since they do not need to be conductive, hence they are covered by a layer of non-metal material (probably epoxy?). This makes them more durable but also more expensive. I have no experience with those.

                                        I hope this contributes to clarify this topic a little bit. This thread contains additional information on the same topic.

                                        1 Reply Last reply
                                        3
                                        • M Offline
                                          M Offline
                                          Maxime
                                          wrote on last edited by
                                          #32

                                          I guess I'm not the only one seeking for the correct diagram. It can be found here: http://vanderleevineyard.com/1/post/2012/08/-the-vinduino-project-3-make-a-low-cost-soil-moisture-sensor-reader.html
                                          alt text

                                          1 Reply Last reply
                                          0
                                          Reply
                                          • Reply as topic
                                          Log in to reply
                                          • Oldest to Newest
                                          • Newest to Oldest
                                          • Most Votes


                                          13

                                          Online

                                          11.7k

                                          Users

                                          11.2k

                                          Topics

                                          113.1k

                                          Posts


                                          Copyright 2025 TBD   |   Forum Guidelines   |   Privacy Policy   |   Terms of Service
                                          • Login

                                          • Don't have an account? Register

                                          • Login or register to search.
                                          • First post
                                            Last post
                                          0
                                          • MySensors
                                          • OpenHardware.io
                                          • Categories
                                          • Recent
                                          • Tags
                                          • Popular