Plant_io Basic Setup Guide | Automatically Water a Seedling Based on Soil Moisture

I just shared a new guide: “Plant_io Basic Setup Guide | Automatically Water a Seedling Based on Soil Moisture”



This guide will help you set up a Plant_io controller to automatically irrigate a plant of your choice :smiley:
Along the way, we’ll

  1. Assemble a hardware platform, connect electronics to measure soil moisture and deliver water to the soil.
  2. Plumb a pump to deliver water, and locate a soil moisture sensor into a growing medium.
  3. Log data over a long period to observe how the controller is working and how effective our growing strategy is.

    Read more
4 Likes

Is it possible to hookup several soil moisture sensors and have them in a garden bed, and the pump irrigating different parts of the the bed as needed through say a relay? I’m an older adult with a Pi 4 8gb, wanting to do something with it when the Pi 5 comes out. I’m new to programming, but this plant_io project has sparked my interest.

2 Likes

Hi Steve,

Sure is! The Pi SBCs might not be the best candidate for this vs the Pico since there are dedicated ADCs and they are quite a bit more expensive.

Pimoroni have their Grow boards which might be worth a look in.

There’s a couple other additions I think would be worth adding so will be making some custom PCBs in the future, if you have any suggestions, send them through!

2 Likes

Shall do. I’m thinking redundancies that could be actioned through IoT, like refilling the water reservoir, using a camera and viewing progress of the plants’ growth while away, or to have a visual record to go with the data from seed to adult plant.

2 Likes

Hi Steve,

Almost exactly what I am thinking, with cameras not limited to SBC’s and now usable with MCU’s like the Pico there are so many possibilities. Once I’m finished uni assignments there ought to be some updates on this project :smiley:

1 Like

The parameters provided to the normalise_x function appear not to be compatible with V2 of the soil moisture sensor.

All values fall out of range.

By debugging / trial and error I was able to determine an approx range of the new sensor. 11_200 to 21_500.

I think it’s working, for example: being out in the air it’s close to 0%, and it’s 100% when completely submerged in Water. However, slightly damp soil (what I’m targetting) gives a value of 90% which is perhaps higher than I’d expect :person_shrugging:

Am I missing anything? can you advise?

Hi @Marc67778

These soil sensors are super cool technologies but the values they produce can look pretty wild.
I found that changing the size of the pot, or the type or soil, or the number of roots, changed what kind of values I could expect. I noticed that adding more soil reduced the conductivity of the system and helped scale my values.

Consider also an alternative to the normalize funciton like writing a function that takes a values between “11_200 and 21_500” and remaps them to a function. For example:

Scaled by the max of an unsigned 16bit int

# put me inside the Plant_io class
def natty_loggy_that_bad_boy(self, k):
    #remaps to the nat log
        #args : unsigned 16 bit int
        #returns : a float between 0.0 and 1.0 
    if not isinstance(k, int) or not (k >= 0 and k < 2**16):
        raise ValueError('That is not a u16 champion')
    return math.log(k) / math.log(2**16 - 1)
def measure_soil(self):
    #...
    soil_adc_reading = self.soil.read_u16()
    scaled_reading = natty_loggy_that_bad_boy(soil_adc_reading)
    #...

Perhaps you prefer taking an average of the last 1000 samples?

def measure_soil(self):
   self.readings = []
   for _ in range(5):
       self.readings.append(self.soil.read_u16())
       if len(self.readings) > 999:
           self.readings.remove(0)
       sleep(0.05)
   scaled_readings = map(self.readings, natty_loggy_that_bad_boy)
   soil_adc_reading = sum(scaled_readings) / len(scaled_readings)
   #...

Maybe you want to control the range of your scaling function

# put me inside the Plant_io class
def natty_loggy_that_bad_boy(self, k, a, b):
    #remaps to the nat log
        #args : unsigned 16 bit int
        #      : min expected value (int)
        #      : max expected value (int)
        #returns : a float between 0.0 and 1.0 
    if not isinstance(k, int) or not (k >= a and k < b):
        raise ValueError('How very dare you!')
    return math.log(k - a) / math.log(b - 1)

Do you want to see finer details in the last 20% off your soil conductivity?

# put me inside the Plant_io class
def raise_the_roof(self, k):
    #remaps to the nat log
        #args : unsigned 16 bit int
        #returns : a float between 0.0 and 1.0 
    if not isinstance(k, int) or not (k >= 0 and k < 2**16):
        raise ValueError('That is not a u16 champion')
    return math.pow(k, 3) / math.pow(2**16 - 1, 3)
1 Like

Thanks @Pixmusix, that’s awesome! I’ll have a play around and hopefully chowing down on Alfalfa in no time at all.

2 Likes

Thanks for offering some creative remixes @Pixmusix :smiley: Stoked you actually decided to peek behind the curtain and hack up our plant_io.py file
@Marc67778 I think we could still detune the scaling to make it a bit more flexible. After all the “moisture %” is totally arbitrary anyway and the user sets their own threshold depending on their conditions.
I’ll make a commit to the library now based off your results - I think I’ll leave the upper bound where it is and decrease the lower bound. This should make the envelope most useful to most people out-of-the-box.

2 Likes