Sprinklers typically use timers and relays to start and stop the flow of built in sprinklers but they don’t factor in how wet the soil is and they don’t factor in the current weather. How many times have you walked passed a house with sprinklers on when it’s actively raining? One time is too many.
Concept:An AVR-IoT Cellular Mini will directly monitor the soil while a raspberry pi controls the flow of water to the lawn. The AVR-IoT Cellular mini will monitor the humidity levels as well as using soil probes to measure the wetness of the soil. The Raspberry Pi will receive that information along with the weather for that day to determine when to turn on the sprinklers. If the soil is wet or it’s supposed to downpour today, no need to turn on the sprinklers. If the soil is dry and it isn’t going to downpour, turn on the sprinklers until the soil is wet again. This will allow water to be conserved while still being able to water the grass. The AVR-IoT Cellular Mini allows the monitoring to happen anywhere on the lawn and not just within range of the WiFi.
Phase 1 - Getting Data From the DeviceI like to use Adafruit IO whenever I can because it's a user friendly interface and it's easier to show that items are reactive. I want to be able to send data over the cellular network to Adafruit IO and then I will make a device to receive said data from Adafruit IO. This will allow for me to troubleshoot easier and also see what the status of both devices is remotely.
To start, I needed to setup the AVR-IoT Cellular Mini device. I followed the instructions that came with the board - https://iot.microchip.com/docs/arduino/introduction/devenv
One quick note about this is that I was unsuccessful in using the new Arduino IDE and needed to use Arduino 1.8.19. I don't know if this is specifically a Windows 11 Pro thing or if it's the same on other OS's.
These were the settings that I used (this is the suggested settings used in the link above).
From there I needed to setup Adafruit IO to interface to. This was done by following this tutuorial - https://learn.adafruit.com/welcome-to-adafruit-io/arduino-and-adafruit-io
This will get you up and running and then I modified the mqtt_custom_broker example from the AVR-IoT Cellular examples section to connect to Adafruit IO instead of AWS. This code can also be found in the attachment section for convenience but the main differences between the example and this are that you need to pass the username and key for Adafruit IO to receive or transmit the request.
// Attempt to connect to Adafruit IO
if (MqttClient.begin(MQTT_THING_NAME,
MQTT_BROKER,
MQTT_PORT,
MQTT_USE_TLS,
MQTT_KEEPALIVE,
MQTT_USE_ECC,
AIO_USERNAME, // Username for Adafruit IO
AIO_KEY)) { // AIO Key for authentication
In this project, we're really only concerned about the system sending information but you could have the device receive information as well, if that is desired, with this code.
Phase 2 - Bench Top BuildNow that we can transmit data from the device to Adafruit IO, the next step is to hook up all of the sensors that we want to use and configure the system to read in all of this information and transmit it to Adafruit IO in an efficient manner.
Because we are going to monitor soil and atmospheric conditions we are going to need:
- Soil Moisture Sensor
- Humidity Sensor
- Temperature Sensor
Because the AVR-IoT Cellular Mini already has documentation on how to read using the qwiic connector, I'm going to use physical components which require reading through the documentation.
This will include:
- The DHT22 for temperature and humidity readings (I bought mine off of Sparkfun but it has since been replaced by a newer model).
- Soil Moisture Sensor
The DHT22 requires 3-5VDC, a wire connected to the TX port of the microcontroller and ground. I used the 3V3 pin to power both the DHT22 as well as the Soil Moisture Sensor. I used the GND pin to ground both the DHT22 as well as the Soil Moisture Sensor.
I used A0 as the analog input for the Soil Moisture Sensor.
I used TX to connect to the DHT22.
Finding the pin to use as the TX port required some digging through the documentation. First I looked for the basic pinout document. You can see that TX corresponds to PF4 and A0 is PD6. This doesn't tell me what pin I should call out in the program though so I had to do more digging.
The manual thankfully provides the MCU diagram so that we can cross reference the TX pin, PF4, and find out what pin to call out in the code. PF4 is the TX pin and it is associated with 38. I can use the 38 as the pin assignment in the code. I was allowed to use A0 as the pin assignment for the soil moisture sensor but was unable to use PF4 or USART2 as a valid address.
Once this was found, I could declare the sensors in my code:
#define DHTPIN 38
#define DHTTYPE DHT22
DHT dht(DHTPIN, DHTTYPE)
#define PROBE A0
You can find the code for this step in the attachments. Here is a video of this test:
Phase 2A - Getting data to Display Nicely on Adafruit IOSo now that we can get a stream of data, we should add elements so that we can more easily monitor what is going on.
First create separate feeds for the Battery, Humidity, Temperature and Moisture levels.
Once that is completed for all the feeds, the Dashboard can be configured with these four new elements.
Once this is complete, you can use the newly modified code in the attachments and see the results on Adafruit IO.
Phase 2B - Conserving PowerNow that we can transmit all of the data to Adafruit IO that we intended to, the next step is to have the device conserve power between messages.
We're going to use the low_power library. It should be added to the top of the script by adding:
#include <low_power.h>
This was likely already done because that was how we got the voltage reading to populate the battery.
The command is pretty simple to enter low power mode. It's just:
LowPower.powerDown(1000);
This will send the device into a powered down state for the given time in seconds. It's based off of internal clock so even if it says 1000 seconds, it's likely to be less than that. Because I'm not too concerned about the exact time it comes back online, about 15 minutes is fine for testing.
One thing to remember is that if you should end the MqttClient as well as the Lte connection before going into power down mode and then start that connection when the powered down state is over. This is done by:
MqttClient.end();
Lte.end();
You can find the code for this in the attachments.
Phase 3 - Sprinkler Controller ScopeNow that we have our main board operating correctly, it's time to move onto the controller board. This will be a Raspberry Pi Pico W and it will control a valve. This board will do the following:
- Get current time - we only want to water in the morning
- Get the current weather - we only want to water when it's not likely to rain
- Get the temperature - we don't want to turn on the sprinklers if it's below freezing.
- Get the humidity and moisture - we don't want to water if we have high moisture readings and high humidity levels.
This creates a set of requirements for the device:
1) Check if it is morning.
2) If it is morning, check the weather forecast.
3) If it's not supposed to rain, check the temperature.
4) If it's >5C, check the humidity and moisture levels.
5) If the humidity and moisture levels are low, turn on the sprinkler.
Phase 3A - IFTTT to Adafruit IOGetting the time should be simple enough but we need to get the current weather forecast. To do this as cheaply as possible, we're going to use IFTTT to get weather from WeatherUnderground and send that to Adafruit IO.
First we need to configure Adafruit IO to have a tag to tie into.
Once Adafruit IO has the appropriate tag, we can update IFTTT to send today's forecast to Adafruit IO.
There isn't a lot documentation on what the available text will display but we're going to say it'll rain if it has any of the following:
- rain
- thunderstorms
- showers
So now that all of the relevant data now is shown on the Adafruit IO's site, we can move to the next phase where we have that information sent to the Raspberry PI Pico W.
There are a lot of resources on getting started with the Raspberry Pi Pico W so I'm just going to drop a link to the raspberry pi website - https://projects.raspberrypi.org/en/projects/getting-started-with-the-pico
Once the device is set up, it's time to connect it to WiFi and connect it to Adafruit IO. Use the code labeled Phase 3B. Here is a brief breakdown of the code:
1) Setup WiFi Credentials.
import network
from umqtt.simple import MQTTClient
import time
# Your Wi-Fi credentials
WIFI_SSID = 'Your-SSID'
WIFI_PASSWORD = 'Your-WIFI-PASSWORD'
2) Setup Adafruit IO credentials. This is the same information you used previously. We are going to monitor the humidity feed first.
# Adafruit IO credentials
AIO_USERNAME = 'Your-Adafruit-IO-Username'
AIO_KEY = 'Your-Adafruit-IO-Key'
AIO_FEED = f"{AIO_USERNAME}/feeds/humidity" # Adjust 'humidity' as needed
3) Connect to the internet
# Connect to Wi-Fi
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
wlan.connect(WIFI_SSID, WIFI_PASSWORD)
4) Grab the feed from Adafruit IO and print out when it's a success
# Wait for connection
while not wlan.isconnected():
pass
print('Connected to Wi-Fi')
# MQTT Callback Function
def sub_cb(topic, msg):
print((topic, msg))
# Connect to Adafruit IO
client = MQTTClient(AIO_USERNAME, 'io.adafruit.com', user=AIO_USERNAME, password=AIO_KEY, ssl=True)
client.set_callback(sub_cb)
client.connect()
client.subscribe(AIO_FEED)
print('Subscribed to Adafruit IO feed')
# Wait for and process messages
while True:
try:
client.wait_msg()
except KeyboardInterrupt:
print('Disconnected from Adafruit IO')
client.disconnect()
break
Phase 3C - Putting it all togetherNow that we have connected our device to Adafruit IO and can grab the feeds, we can now perform the final logic.
We have the following feeds:
We aren't going to use battery for the purposes of opening or closing the sprinkler but we will use WeatherToday, humidity, moisture and temperature as well as the current time.
Open the sprinkler valve if:
- WeatherToday is NOT rain, showers or thunderstorms is not in the forecast
- Humidity level is dry (less than 80%)
- Moisture level is dry (less than 500 on our analog reading)
- Temperature is above 0 degrees Celcius
- The time is between 5AM and 7AM
We don't want to water our lawn if it's going to be raining, we don't want to water if it is humid, the lawn is already moist, it's going to freeze or if it's not the morning.
We additionally want a requirement for the program to be stopped if we need. We're going to do this by using a digital input and having a jumper between VDD and this pin. This will allow for the program to be stopped as needed.
When the humidity is < 50%, moisture < 500, temperature > 5 degrees and it's between 5 and 7 AM, turn on the sprinkler. The relay is normally closed so I set it low to turn it on and set it to high when I want to turn it off. This code can be found as Phase 3C Part 1.
if humidity and temperature and moisture:
print(f"Humidity: {humidity}, Temperature: {temperature}, Moisture: {moisture}")
try:
if (float(humidity) < 50 and float(moisture) < 500 and
float(temperature) > 5 and 5 <= current_hour < 7):
print("Activating sprinkler.")
sprinkler_pin.value(0)
else:
print("Deactivating sprinkler.")
sprinkler_pin.value(1)
except ValueError:
print("Error: Could not convert feed values to float."
We
also need to factor in the weather conditions so we'll change the code to incorporate that. You can find that code as Phase 3C Final Code in the attachments.
while stop_pin.value() == 0: # Continue as long as the stop pin is low
weather_today = fetch_last_value(WEATHER_TODAY_FEED_KEY).lower()
# Check if weather conditions include rain, showers, or thunderstorm
if any(condition in weather_today for condition in ["rain", "showers", "thunderstorm"]):
print("Bad weather today:", weather_today)
sprinkler_pin.value(1) # Assuming active low, 1 deactivates the sprinkler
time.sleep(30) # Wait before checking again
continue
We check to see if the words "rain", "showers" or "thunderstorm" is in the weather condition and update as needed. This allows us to factor in those conditions and if any are present, we don't want to turn on the sprinkler so we set it to high.
I added sliders onto Adafruit IO in order to simulate values and disabled the time factor so that I could do bench top testing.
Phase 4 - Going FurtherUsing the AVR-IoT Cellular Mini, we have successfully sent data over a cellular network to Adafruit IO. We created a system using the Raspberry Pi Pico W to control a sprinkler and read data from Adafruit IO to determine when to turn on the sprinkler.
We have done all bench top testing thus far but the next phase is to create a PCB to mount the device, build an enclosure and put in the field but it'll definitely look like this:
Comments