If you are a keen gardener, you will be well aware of the need to keep your flowers and vegetables hydrated, and of how much time this takes using a watering can. The alternative is to set up an automatic watering system. Automatic watering timers are widely available, but I wanted one that would respond to soil moisture, not just the time of day.
First, connect your Arduino MKR WAN to your TTN account by following the procedure here.
If you wish to connect to an application such as Home Assistant using MQTT, then read this.
Next, collect together all the items listed above ready for assembly.
What’s in the picture:
1. MKR WAN 1310.
2. Antenna.
3. Battery
4. Fuel gauge and battery monitor.
5. Soil moisture probe.
6. Soil moisture sensor.
7. Connector pins.
8. One input to two output connector.
9. JST-PH 2-pin jumper cable.
10. I2C Qwiic Stemma QT cable.
11. Jumper wires, 2 black and 2 red for GND and VCC, and one white for sensor wire.
12. Waterproof junction box.
The numbers in red refer to the same components that are numbered in the previous image. So, ‘8’ refers to the one input to two output connector, ‘9’ is the JST-PH 2-pin jumper cable, and 10 is the I2C Qwiic Stemma QT cable.
The connector pins (number ‘7’ in the previous image) will need to be soldered on to the soil moisture sensor (labelled ‘6’ in the previous image) before you can connect the jumper cables.
Before we can start to assemble the components, we are going to solder the pins to the soil moisture sensor. Place the shorter end of the pins into the holes in the moisture sensor. You will need a fine point on your soldering iron.
Turn on the soldering iron and place the tip of the iron onto the metal circle surrounding the pin. After a few seconds apply the solder. Don’t apply too much solder, or you will short out the pins.
Check that the soil sensor fits through the holes. If not, you will need to adjust the opening using a file or a larger drill. Once you have a good fit, you can fix the sensor in place using a waterproof glue.
I used EVO-STIK Serious Glue, which claims to be high strength and waterproof (and a non-conductor of electricity). Put some glue under the body of the moisture sensor, then press down so that the ‘legs’ are pushed all the way through the holes and the back of the sensor is firmly against the top of the junction box. Hold this in place for a few minutes while the glue ‘sets’, and then don’t move it for a couple of hours while the glue hardens.
If you connect up the components, you should see something like this.
Note that the battery is not connected in this image. Before connecting the Arduino to your computer, connect the battery; otherwise, you may see inaccurate or unexpected readings in the Arduino IDE Serial Monitor. Once the battery is connected, connect the Arduino to your computer using the micro USB cable to verify that everything is working correctly.
If you have bought a junction box with a mounting plate, then you can mount the components on that. You will need some standoff screws (see shopping list above) and some cable ties for the ariel and battery. This is what I used.
For the wire splitter connector:
2 x M3 x 10mm screws
2 x M3 nuts
For the soil moisture sensor:
1 x M3 x 10 + 6 standoff screw
1 x M3 nut
1 x M3 + 6 screw
For the battery monitor:
4 x M2 x 8mm screws
4 x M2 x 15mm standoff screws
4 x M2 washers
4 x M2x 5mm screws
For the Arduino MKR WAN 1310:
4 x M2 x 8mm screws
4 x M2 x 20mm standoff screws
4 x M2 washers
4 x M2x 5mm screws
And four 12mm cable ties.
Now it is time to see if it all works. Make sure that the battery is connected to the battery monitor and then connect the Arduino to your computer using the micro USB cable.
We need to connect your Arduino to your TTN account, if you have not already done so. Click here for instructions.
We are now going to open a sketch in your Arduino IDE, which will take soil moisture readings and upload them to your TTN account. Here is the sketch, but you can also download it from GitHub here.
// For LoRa:
#include <MKRWAN.h>
// For battery monitor:
#include "Adafruit_MAX1704X.h"
Adafruit_MAX17048 maxlipo;
// For battery charger
#include <Arduino_PMIC.h>
// For LowPower
#include "ArduinoLowPower.h"
LoRaModem modem;
// Please enter your sensitive data in the arduino_secrets.h tab
#include "arduino_secrets.h"
// Start of soil moisture code...
// initiialise some variables
// the sensor value is taken from the sensor pin (A0)
int sensorPin = A0;
int sensorValue;
int dryAnalogValue = 1023;
int wetAnalogValue = 0;
int DryValuePercent = 0;
int WetValuepercent = 100;
// ...end of soil moisture code
// initialises a variable for battery state of charge:
int cellPercent = 0;
// We need the details below to connect your device to LoRa
// Read arduino secrets
String appEui = APP_EUI;
String appKey = APP_KEY;
void setup() {
// Put your setup code here, to run once:
// Start of battery charger code...
if (!PMIC.begin()) {
Serial.println("Failed to initialize PMIC!");
while (1);
}
// Set the input current limit to 2 A and the overload input voltage to 3.88 V
if (!PMIC.setInputCurrentLimit(2.0)) {
Serial.println("Error in set input current limit");
}
if (!PMIC.setInputVoltageLimit(3.88)) {
Serial.println("Error in set input voltage limit");
}
// set the minimum voltage used to feeding the module embed on Board
if (!PMIC.setMinimumSystemVoltage(3.5)) {
Serial.println("Error in set minimum system volage");
}
// Set the desired charge voltage to 4.11 V
if (!PMIC.setChargeVoltage(4.2)) {
Serial.println("Error in set charge volage");
}
// Set the charge current to 375 mA
// the charge current should be defind as maximum at (C for hour)/2h
// to avoid battery explosion (for example for a 750mAh battery set to 0.375 A)
if (!PMIC.setChargeCurrent(0.375)) {
Serial.println("Error in set charge current");
}
Serial.println("Initialization done!");
if (!PMIC.enableCharge()) {
Serial.println("Error enabling Charge mode");
}
// End of battery charger code.
Serial.begin(9600);
while (!Serial && millis() < 10000); //This line is needed to work with batteries https://forum.arduino.cc/t/it-does-not-work-powered-by-2xaa-batteries/520097/5
// The code below connects your device to LoRa
if (!modem.begin(EU868)) { // change this to your regional band (eg. US915, AS923, ...)
Serial.println("Failed to start module");
while (1) {}
};
Serial.print("Your module version is: ");
Serial.println(modem.version());
Serial.print("Your device EUI is: ");
Serial.println(modem.deviceEUI());
int connected = modem.joinOTAA(appEui, appKey);
// Start of battery monitor code...
Serial.println(F("\nAdafruit MAX17048 simple demo"));
if (!maxlipo.begin()) {
Serial.println(F("Couldnt find Adafruit MAX17048?\nMake sure a battery is plugged in!"));
while (1) delay(10);
}
Serial.print(F("Found MAX17048"));
Serial.print(F(" with Chip ID: 0x"));
Serial.println(maxlipo.getChipID(), HEX);
// ...end of battery monitor code.
if (!connected) {
Serial.println("Something went wrong; are you indoors? Move near a window and retry");
while (1) {}
}
}
void loop() {
// put your main code here, to run repeatedly:
// Start of soil moisture code...
sensorValue = analogRead(sensorPin);
Serial.println("Analog Value : ");
Serial.println(sensorValue);
sensorValue = analogRead(sensorPin);
Serial.print("Analog moisture Value : ");
Serial.println(sensorValue);
int moistureValuePercent = map(sensorValue, dryAnalogValue,
wetAnalogValue, DryValuePercent, WetValuepercent);
Serial.print("moisture percent: ");
Serial.print(moistureValuePercent);
Serial.println("%");
if (moistureValuePercent == DryValuePercent)
{
Serial.println("plant is dry !! ");
}
else
{
Serial.println("plant is OK !! ");
}
// ...end of soil moisture code
// Start of battery monitor code...
cellPercent =(maxlipo.cellPercent());
Serial.print(F("Batt Voltage: ")); Serial.print(maxlipo.cellVoltage(), 3); Serial.println(" V");
Serial.print(F("Batt Percent: ")); Serial.print(maxlipo.cellPercent(), 1); Serial.println(" %");
Serial.println();
// ...end of battery monitor code.
byte payload[2]; // The [2] means we are sending two items in the array. Arrays start at zero.
payload[0] = moistureValuePercent; // Send soil moisture by LoRa.
payload[1] = cellPercent; // Send state of charge by LoRa.
delay(1000); // Gives it time to execute.
modem.beginPacket();
modem.write(payload,2); // The number after the word 'payload' is the number of items sent in the array above. If you add another item, increment this number.
int err = modem.endPacket(false);
if (err > 0) {
Serial.println("Data Sent");
} else {
Serial.println("Error");
}
Serial.print("soil moisture data sent is: ");
Serial.print("soil moisture % is: "); Serial.println(moistureValuePercent);
Serial.print("battery % is: "); Serial.println(cellPercent);
LowPower.deepSleep(1800000); // 30 minute intervals. This is the line we will be using when the sensor is in production mode.
//delay(300000); // This is the interval between each temperature and state of charge reading. The default is five minutes.
//delay(30000); // We can use 30 seconds while in testing mode.
}
Here is that GitHub link again: https://github.com/DoctorDulcamara/MKRWAN-soil-moisture-MAX17048-Charger-Lowpower.
For data visualisation you can now connect your TTN application to Datacake by following the instructions here: https://loraworkshop.com/configuring-your-device-on-datacake-part-1.html.
Once you have connected your TTN device with Datacake, you can set up a dashboard to display you data by following these instructions: https://loraworkshop.com/configuring-your-device-on-datacake-part-2.html. No data will be displayed until this section has been completed.
Here is a Datacake dashboard I set up earlier to show soil moisture content and the battery state of charge.
By now you should have your soil moisture sensor ready to deploy. This image shows a sensor released into the wild. To discover the soil moisture content of my cabbage patch, click here.
Now we have to act on the information received. In the next section we will build a water control valve, which can be automatically activated by our soil moisture sensor.
Next section coming soon.