Moist; Moister; Moisterest (aka Three Raspberry Pico Moisture Sensors Tested)

Gardening is not my thing at all. I can do houseplants, because they’re right in front of me, wilting away as they beg for water. But anything that’s out of sight may as well not exist. And, by the end of summer, they generally don’t, and the only sign that they ever might have done is a few dry twigs.

So, of course, I do what all programmers do when faced with a simple but mundane task that would only take a few minutes a week: I spend days trying to automate it.

Ideally I’d automate the watering, but that’s too ambitious for me right now. The baby steps version is some kind of simple alert that tells me when the plants in the shed are about to expire. Because it’s what I know, I’ll be using a Raspberry Pico, and at some point I’ll send data to an API on a bigger machine, and then there’ll be a separate project to display relevant info.

In my mind I don’t need to monitor every pot out there, but ideally I’d have three or four pots monitored by a single Pico, and in the end that would be powered by a battery that’s kept topped up by a solar panel.

Without much planning I dove into moisture sensors as the first step. I had four things in my mind:

  1. Could I get them to work?
  2. Could I interpret the results?
  3. How much power do they need?
  4. How many can I attach to one Pico?

First up was the one that looked the easiest, based mostly on the fact that it’s the most expensive.

The Monk Makes Plant Monitor

A Monk Makes plant monitor

I’ve already played around this when testing different ways of measuring temperature using the Pico, because as well as soil moisture content it also does temperature and humidity. That’s three things in one, but then it also costs more than three other mid-range moisture sensors.

In fact, it’s generally a clever piece of kit, as it does all the hard work of turning analog information (such as a moisture scale) into digital output. It means it can work with microcontrollers that don’t have analog to digital convertors (ADC), although slightly redundant with the Pico which does have ADCs.

Getting it working was one of the simplest things so far, with a nice library and a very compact example that uses it. Moisture level is returned as an integer between 1 and 100 and it’s up to you to work out what that means in terms of whether a plant needs more water or not, although there’s also a light on the sensor itself that goes from green, through to orange and red, depending on what it thinks of the current situation.

It was easy to use, but I have a suspicion it uses a fair amount of power (in Pico terms), not least because there are LEDs shining all the time. It also appears that because of the sophisticated way it communicates with the Pico (through UART) that you can only have one per device.

The Waveshare Moisture Sensor

A Waveshare soil moisture sensor

I’m beginning to learn that whatever it is you might want to do with a Pico, Waveshare has something that will do it. It might not be top of the range, and the documentation might only just make sense, but it will generally a) work and b) be cheap.

Costing roughly a third of the Monk Makes sensor, they are also a lot simpler. It also appears they use an out-of-favour technique to measure moisture sensor, in that they pass a current between the two prongs and better conductivity means moister soil. This sounds fine, but the current causes corrosion of the prongs, which is not something that’s a problem for the Monk Makes sensor which is capacitive, not resistive as the Waveshare ones are. A write-up of the differences between resistive and capacitive is here.

The code was simple, though, with demo code available to download from their website. Basically, it makes use of the analog to digital convertor on the Pico, and just like the Monk Makes sensor you get an integer back which is a kind of “moistness percent”.

Without all the library imports, the important part is:

analog_value = machine.ADC(sensor_pin_number)
reading = analog_value.read_u16()
wetness = reading / 65535 * 100

It means it has to be plugged into one of the three ADC pins on the Pico (26, 27 or 28).

Summary, though, was that it was easy, seemed to work, and cheap.

Grow Moisture Sensor

Three Grow soil moisture sensors

Somewhere between the price of Monk Makes and Waveshare was the Grow sensor. (The picture above shows three together, which is how they come, for about the same price as the Monk Makes device.)

The Grow sensor is intended to be used as part of a more fully-fledged ecosystem, but the docs said it could be used standalone so, like a fool, I believed them.

To be fair, it just took a bit of searching to find a standalone Python example. Plus some soldering as I hadn’t bought the right cable…

Functionally, the Grow sensor is capacitive, like the Monk Makes, but to send data back to the Pico it uses yet another technique: Pulse Wave Modulation.

I’d used this a bit as an OUT signal to change the brightness of some LEDs, but I had no idea how I’d use it with an IN signal. More searching and a small amount of code provided the answer:

last_value = sensor_pin.value()
start = time.ticks_ms()
first = None
last = None
ticks = 0
while ticks < 10 and time.ticks_diff(time.ticks_ms(), start) <= 1000:
  value = sensor_pin.value()
  if last_value != value:
    if first == None:
      first = time.ticks_ms()
    last = time.ticks_ms()
    ticks += 1
    last_value = value
  if not first or not last:
    wetness = 0.0
  else:
    # calculate the average tick between transitions in ms
    average = time.ticks_diff(last, first) / ticks
    # scale the result to a 0...100 range where 0 is very dry
    # and 100 is standing in water
    # dry = 10ms per transition, wet = 80ms per transition
    min_ms = 20
    max_ms = 80
    average = max(min_ms, min(max_ms, average)) # clamp range
    scaled = ((average - min_ms) / (max_ms - min_ms)) * 100
    wetness = round(scaled, 2)

There sensor_pin above is a machine.Pin object.

The good thing about this is that it can use any pin, not just the three ADC ones available. Again, the figure returned is a number between 0 and 100, and it’s down to interpretation as to what’s considered too dry or too wet.

And The Moistest Winner?

The Monk Makes sensor is the most sophisticated, but the limitation of one per Pico, plus my assumptions about power consumption, rule this out. On the plus side it’s very easy to use, looks robust, and you get bonus info in terms of temperature and humidity.

At the other end, the Waveshare sensors are the cheapest, and they do work. The downsides are a) being resistive, which apparently means they could only have a lifespan of a year or two, and b) you can only have three per Pico. I might not need more than three, but I think that and the corrosion tips the balance.

The Grow sensor, then, was the surprise winner. I say “surprise” because it started as the least promising as I tried to find out how to interface with it outside of the rest of the Grow ecosystem. But when it does work, it does everything: it’s capacitive, the number you can have on one microcontroller is really only limited by pins, and it also looks and feels like a nice product.

Some Comparison Output Data

Finally, I plugged all three into the same Pico and put them all in the same plant pot, close together near the edge of the pot The numbers that came back are all in approximately the same range. This is in a large pot that was watered a few days ago so should be fairly evenly moist:

Monk Makes: 29
Waveshare: 27
Grow: 24

Not too far apart, and I think as a general rule I’ll take it that anything under 20 from any sensor means that watering is required.

Now to work out how to run them all from solar so I can put them in the shed…

Leave a Reply

Your email address will not be published. Required fields are marked *