Have you ever found yourself, like me, frustrated by forgetting to water your plants? Why not try creating a low-cost smart irrigation system that supports remote management and real-time monitoring of your plants' status? A LoRa node can be integrated with a soil moisture sensor to collect and transmit real-time data to a cloud platform via a LoRa gateway. Furthermore, the platform can send remote commands to control the water pump, enabling automated irrigation and precise monitoring of plant conditions. Let's get hands-on and build it together!
1Hardware configuration1.1Physical hardware connection
- '–': Representing Connection
- AB01 3.3V – Adapter board VCC
- AB01 GND – Adapter board GND
- AB01 GPIO1 – Adapter board AO
- Adapter board '+' – Soil moisture sensor '+'
- Adapter board '-' – Soil moisture sensor '-'
- AB01 5V – Relay VCC
- AB01 GND – Relay GND
- AB01 SDA – Relay IN
- Negative pole of water pump – Relay 'NO'
- AB01 GND – Relay 'COM'
- AB01 Vin – Positive pole of water pump
Note:After connecting the development board AB01 to the power supply, the red indicator light of the relay will light up, and the green light will only light up after issuing the command to turn on the water pump.
1.2Configure HT-7603 gateway on Snapemu
1.3Configure LoRa Node AB01 Dev-Board(V2) on Snapemu
2.Related software codeAfter all hardware connections are completed, download the pre written LoRaWAN firmware from Arduino.
#include "LoRaWan_APP.h"
#include "Arduino.h"
/* OTAA para*/
uint8_t devEui[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1A };
uint8_t appEui[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
uint8_t appKey[] = { 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88 };
/* ABP para*/
uint8_t nwkSKey[] = { 0x15, 0xb1, 0xd0, 0xef, 0xa4, 0x63, 0xdf, 0xbe, 0x3d, 0x11, 0x18, 0x1e, 0x1e, 0xc7, 0xda,0x85 };
uint8_t appSKey[] = { 0xd7, 0x2c, 0x78, 0x75, 0x8c, 0xdc, 0xca, 0xbf, 0x55, 0xee, 0x4a, 0x77, 0x8d, 0x16, 0xef,0x67 };
uint32_t devAddr = ( uint32_t )0x007e6ae1;
/*LoraWan channelsmask, default channels 0-7*/
uint16_t userChannelsMask[6]={ 0x00FF,0x0000,0x0000,0x0000,0x0000,0x0000 };
/*LoraWan region, select in arduino IDE tools*/
LoRaMacRegion_t loraWanRegion = ACTIVE_REGION;
/*LoraWan Class, Class A and Class C are supported*/
DeviceClass_t loraWanClass = LORAWAN_CLASS;
/*the application data transmission duty cycle. value in [ms].*/
uint32_t appTxDutyCycle = 15000;
/*OTAA or ABP*/
bool overTheAirActivation = LORAWAN_NETMODE;
/*ADR enable*/
bool loraWanAdr = LORAWAN_ADR;
/* set LORAWAN_Net_Reserve ON, the node could save the network info to flash, when node reset not need to join again */
bool keepNet = LORAWAN_NET_RESERVE;
/* Indicates if the node is sending confirmed or unconfirmed messages */
bool isTxConfirmed = LORAWAN_UPLINKMODE;
/* Application port */
uint8_t appPort = 2;
uint8_t confirmedNbTrials = 4;
const int soilMoisturePin = 1;
/* Prepares the payload of the frame */
static void prepareTxFrame( uint8_t port )
{
int sensorValue = analogRead(soilMoisturePin);
Serial.print("ADC Value: ");
Serial.println(sensorValue);
appDataSize = 0;
unsigned char*puc;
appData[appDataSize++] = 0x00; // parent ID
appData[appDataSize++] = 0x00; // parent ID
appData[appDataSize++] = 0x05; // sensor length
appData[appDataSize++] = 0x00;
puc = (unsigned char *)(&sensorValue);
appData[appDataSize++] = puc[3];
appData[appDataSize++] = puc[2];
appData[appDataSize++] = puc[1];
appData[appDataSize++] = puc[0];
Serial.print("Sending data: ");
for (int i = 0; i < appDataSize; i++) {
Serial.print(appData[i], HEX);
Serial.print(" ");
}
if (sensorValue < 300) {
Serial.println("🌱 The soil is too dry, water it quickly!");
}
else if (sensorValue >= 300 && sensorValue <= 700) {
Serial.println("🌼 The soil moisture is just right, no watering is needed");
}
else {
Serial.println("🚫 The soil is too wet, be careful of rotting the roots");
}
}
bool waterPumpState = false;
uint8_t command = 0; // To store received command
void downLinkDataHandle(McpsIndication_t *mcpsIndication) {
if (mcpsIndication->BufferSize > 0) {
// Print the received downlink data
Serial.print("+Received Buffer: ");
for (uint8_t i = 0; i < mcpsIndication->BufferSize; i++) {
Serial.printf("0x%02X ", mcpsIndication->Buffer[i]);
}
Serial.println();
// Extract the first byte as a command
uint8_t command = mcpsIndication->Buffer[0];
Serial.print("Received command: 0x");
Serial.println(command, HEX);
// Process according to commands
switch (command) {
case 0x01:
Serial.println("Command 0x01: Turn on the water pump...");
waterPumpState = true;
break;
case 0x02:
Serial.println("Command 0x02: Turn off the water pump");
waterPumpState = false;
break;
default:
Serial.println("Unknown command received.");
break;
}
} else {
Serial.println("No data received.");
}
}
void setup() {
Serial.begin(115200);
pinMode(P0_1, OUTPUT);
digitalWrite(P0_1, LOW);
}
void loop()
{
switch( deviceState )
{
case DEVICE_STATE_INIT:
{
#if(LORAWAN_DEVEUI_AUTO)
LoRaWAN.generateDeveuiByChipID();
#endif
#if(AT_SUPPORT)
getDevParam();
#endif
printDevParam();
LoRaWAN.init(loraWanClass,loraWanRegion);
deviceState = DEVICE_STATE_JOIN;
break;
}
case DEVICE_STATE_JOIN:
{
LoRaWAN.join();
break;
}
case DEVICE_STATE_SEND:
{
prepareTxFrame( appPort );
pinMode(P0_1, OUTPUT);
if(waterPumpState) {
digitalWrite(P0_1, HIGH); // Turn on the water pump
Serial.println("Water pump is ON.");
} else {
digitalWrite(P0_1, LOW); // Turn off the water pump
Serial.println("Water pump is OFF.");
}
LoRaWAN.send();
deviceState = DEVICE_STATE_CYCLE;
break;
}
case DEVICE_STATE_CYCLE:
{
// Schedule next packet transmission
txDutyCycleTime = appTxDutyCycle + randr( 0, APP_TX_DUTYCYCLE_RND );
LoRaWAN.cycle(txDutyCycleTime);
deviceState = DEVICE_STATE_SLEEP;
break;
}
case DEVICE_STATE_SLEEP:
{
LoRaWAN.sleep();
break;
}
default:
{
deviceState = DEVICE_STATE_INIT;
break;
}
}
}
Note: Suggest choosing the working mode of the device as class C
After the device is successfully connected to the network, the serial port will display soil moisture and water pump status.
Due to the ultrasonic sensor not being within the automatic decoding range of the platform, the platform is unable to automatically decode the ultrasonic sensor data. Therefore, we need to write decoding functions to display real-time data on the platform application interface. This document provides detailed operations.
After successful decoding, the Snapemu platform application interface will display as shown in the figure.
Web Version
Mobile Version
Download the Snapemu app to view data anytime, anywhere.
Additionally, shortcut keys can be used to issue commands for controlling the water pump's operation based on soil moisture levels. This document provides detailed instructions for issuing commands on SnapEmu.
After SnapEmu cloud platform issues the pump activation command, the LoRaWAN gateway receives it and forwards it to the AB01 development board via wireless communication. The AB01 decodes the downlink data, recognizes the pump activation command, and drives the SDA pin high, thereby controlling the water pump power supply through a relay, allowing it to start running until the device receives a shutdown command.
Note: SDA here is only used as a regular GPIO to control the switch of the water pump.
After the water pump is activated, as shown in the figure.
Comments
Please log in or sign up to comment.