Remote Allotment Monitoring System With Tool Use Tracker Utilising WEMOS and MQTT
by aawmartin in Circuits > Arduino
27 Views, 1 Favorites, 0 Comments
Remote Allotment Monitoring System With Tool Use Tracker Utilising WEMOS and MQTT
Hello this is our combination soil moisture monitoring system, thermostat, and tool logging Station. It is based on the WEMOS D1 mini (clone) and allow for the tracking of select plants soil moisture level, the Atmospheric temperature at the stations location and keeping track of what tools are in use in shared gardening environments. Each of these elements are modular and can be added or removed as needed. Both the Moisture sensors and tool tracking are scalable up to the capacity of the devices used and can be tracked through a MQTT server.
Supplies
This is the full list of all compoents used. Each individual section will also have a component breakdown for what was used in that section:
- Soil Moisture Sensor (1 per Periferal Sensor
- LEDs (2 per Periferal Sensor)
- WEMOS D1 Mini (2 +1 per Periferal Sensor)
- Wires (All of them)
- Either a bread board or suitable PCB board & Solder
- DHT sensor
- MFRC522 RFID reader Module
- RFID Dongle (1 for each tool you wish to track)
Set Up
Each WEMOS will require being connected to the wifi and then to the MQTT server and depending on your network it requires being registered to access the network. This is mostly for industrial and corporate networks not your home network. Speak to your IT department to be sure. Since we are also using the Arduino IDE framework to program the WEMOS we will need download this Library:
esp8266 by ESP8266 Community
And add this URL to our additional board manager URls manager found under File/ Preferences for window or Arduino IDE / Settings for Mac:
https://arduino.esp8266.com/stable/package_esp8266com_index.json
Once you Have done that your device will be able to be used to program a wide variety of WEMOS’s but more importantly the one we will be using repeated through this project.
Each of your WEMOS’s will need the following code in order to function:
#include <ESP8266WiFi.h> //Used to connect to the wifi
#include <MQTT.h> // Used to connect to an MQTT server
const char ssid[] = "NETWORK_HERE"; // Wifi network. Input your network name in-between the parenthesis.
const char pass[] = "PASSWORD_HERE"; // Wifi Password. Input your networks password in-between the parenthesis if your network requires one.
WiFiClientSecure net;// Sets up a network object to connect via
MQTTClient client; // Sets up an Object to connect to the MQTT server with
void connect() {
while (WiFi.status() != WL_CONNECTED) { //Try and connect
delay(500);
}
net.setInsecure(); // Don't care about verifying a secure connection
while (!client.connect("Portal Name", "Username", "Password")) {
//These 3 fields are used to connect to the specific MQTT server you want to run the project on and the first field should be uniquely named for each different WEMOS you are using as this is how the server recognises the device. Failure to do so will result in a failure to connect to the sever and other issues.
delay(500);
}
client.subscribe("Project/Codes"); //Subscribe to recieve messages from the specific server channel
}
Next portion nested within void setup:
WiFi.begin(ssid, pass); //log into the wifi
client.begin("host", Port , net); //Connect to the correct serve via the correct port. Change these to the required values
client.onMessage(messageReceived); // Establish communication and confirm recieving
connect(); // Run connection subroutine
Final Portion nested within Void Loop:
client.loop();
delay(10); //LAG counter
if (!client.connected()) { //If not connected try to reconnect
connect();
}
With this code the WEMOS should connect to your network and then to the server you choose and will attempt to reconnect if disconnected and listen for any messages coming in from the selected channel.
Modularity
There are 4 elements of this project wee need to apply specific code for:
- The Moisture Sensor Peripheral
- The Moisture Sensor Base
- The DHT Temperature Sensor
- RFID Scanner
The Moisture Sensor Peripheral (Now referred to as MSP) and the RFID Scanner require separated individual boards whereas the Base and Temperature sensor can be mounted together on one board. You will need an MSP for each plant you wish to monitor, and each requires its own board.
MSP
Components (Per sensor)
- Soil Moisture Sensor
- LED
- WEMOS D1 Mini
- Wires
- Either a bread board or suitable PCB board & Solder
Wiring
Most of the wiring is direct except from a ground rail that you need to set up on one line of the PCB (It can and is recommended to be the same rail as the WEMOS ground pin.). Make sure to split the rails under neath the WEMOS as to not connect the pins across and the rails should be orientated perpendicular rather than parallel to the pins.
Sensor Power -> 5V
Sensor Ground -> Ground Rail
Sensor signal -> A0
LED + -> D1
LED- -> Ground Rail
Code
Note: This code is the same for all of the periferal sensors with the exception of a few key points. These are:
- Portal Name (Must be unique for each WEMOS)
- myVal and response codes (Will be a selected number= X with X0 being the call code, X1 being the happy response and X2 being the negative response)
- Publish Channel (Usual defined as SensorX to avoid confusion)
- Sensor name .
- Resistance value to determine the desired hydration
Insert the following before void Setup but after Void connect:
void messageReceived(String &topic, String &payload) { //If you recieve a message on the call responce channel
Serial.println(payload); //Print it out
int myVal = payload.toInt(); // Convert string to interger
if (myVal == X0) { //Check if its your call message
if (analogRead(A0) <= 400) { //If yes see if plant is wet (this value can be calibrated to meet a plants desired moisture level)
digitalWrite (D1,LOW); // if yes light goes off
client.publish("Product/Codes", "X1"); // we send happy code
client.publish("Product/SensorX", "Sensor X is: HAPPY"); // and say on server this sensor is happy
}
else { //if plant is not wet
digitalWrite (D1,HIGH); // turn LED on to alert user
client.publish("Product/Codes", "X2"); // Send thirst code
client.publish("("Product/SensorX", "Sensor X is THIRSTY"); // And say on server this sensor is thirsty
}
}
}
Insert into void setup:
Serial.begin(115200); //Start Serial
pinMode(D1,OUTPUT); // Set LED pin to output
pinMode (A0, INPUT); // Set moisture sensor pin to input
Insert this in void loop to monitor the hydro sensor via serial:
Serial.println(analogRead(A0)); //Send the Moisture sensor data to serial to check its working.
delay(500); // Every 0.5 seconds
Moisture Sensor Base
Components
- WEMOS D1 mini
- Wires
- Breadboard or PCB and Solder
- LED per Sensor (Up to a maximum of 8, you can attach more sensors but won’t be able to use the LEDs as a visual means of tracking their state. We are yet to test the server-side capacity so it is currently only limited by code outputs )
Wiring
The only real wiring for this component is the LEDS from their respective pins to the ground rail. These LED might wish to be on longer wires depending on how you wish to mount them.
Code
Our demo here is currently set up for 3 sensor with 3 LEDs. For each additional sensor you will need to add another copy of the skeleton code section to the appropriate section and fill it in with the desired values. The is a limit for the number of LEDS of 8 but we are yet to find the limit server side for the number of sensors.
Inserted just below MQTTClient:
unsigned long lastMillis = 0; // Int used in a rolling timer. Allows it to count continuosly without read delay
int Count = 1; // Used to track which sensor in being called on
int myVal = 0; // Used as a call and responce for the sensors
The Response code inserted before void Setup but after void Connect:
void messageReceived(String &topic, String &payload) { //Recieve message from subscribed channel. Recieved in the form of a string.
int myVal = payload.toInt();// Convert string to interger
(The 2 lines above are fully required even if you aren’t using LEDS. If you aren’t wishing to use LEDs you can remove the rest of this section as it is only for their control as it’s the sensors themselves that control the server-side monitoring.)
if (myVal == 11) { //Sensor 1 Happy
digitalWrite(D7, LOW);
}
if (myVal == 12) { // Sensor 1 Thirsty
digitalWrite(D7, HIGH);
}
if (myVal == 21) { //Sensor 2 Happy
digitalWrite(D6, LOW);
}
if (myVal == 22) { // Sensor 2 Thirsty
digitalWrite(D6, HIGH);
}
if (myVal == 31) { //Sensor 3 Happy
digitalWrite(D5, LOW);
}
if (myVal == 32) { // Sensor 3 Thirsty
digitalWrite(D5, HIGH);
}
}
Skeleton Code for LED Control
if (myVal == X1) { //Sensor X is Happy
digitalWrite(Xpin, LOW);
}
if (myVal == X2) { // Sensor X is Thirsty
digitalWrite(Xpin, HIGH);
}
NOTE: In testing this component, we did find noticeable lag between the code being sent and the LED changing. We tried a few different fixes, but nothing worked in the way we wanted. Server-side monitoring was unaffected and worked flawlessly .
In void Setup:
pinMode(D5,OUTPUT); //LED for each sensor set up on the relevant pin. Can be expanded up to the number of availible pins
pinMode(D6,OUTPUT);
pinMode(D7,OUTPUT);
In Void Loop. The main call code. We put this after all other items in this section as we want to avoid lag, make sure that we are connected and have any other processes happen at their coded rate:
if (millis() - lastMillis > 10000) { //10 second continous timer
lastMillis = millis(); // This section checks if it hase been atleast 10 IRL seconds if yes continue to do the thing and rest the timer. If no then wait.
The Nested Call Code Example. Nested within the timers if statement as we only want this to happen when the timer goes off. The count is based on the number of sensors you have with each count checking the lasts call to see if it has been responded to if it has they call their own and increases the count. If not they mark the sensor as lost (since it hasn’t responded) and they call their own and increases the count. It is sequenced in reverse to prevent run on through the sequence. Lastly after it has checked all the possible counts for sensors it checks if it has reached the top one (Marked as 0) and if it has it resets to 1.
if (Count = 3) {
if (myVal = 20) {
client.publish"Product/Sensor3", "Yellow is: LOST");
client.publish("Product/Code ", "30");
delay(500);
}
else {
client.publish("Product/Code ", "30");
delay(500);
}
Count = 0;
delay(500);
}
if (Count = 2) {
if (myVal = 10) {
client.publish("Product/Sensor2", "Red is: LOST");
client.publish("Product/Code ", "20");
delay(500);
}
else {
client.publish("Product/Code ", "20");
delay(500);
}
Count = 3;
delay(500);
}
if (Count = 1) {
if (myVal = 30) {
client.publish("Product/Sensor1", "Blue is: LOST");
client.publish("Product/Code ", "10");
delay(500);
}
else {
client.publish("Product/Code”, "10");
delay(500);
}
Count = 2;
delay(500);
}
if (Count = 0) {
Count = 1;
delay(500);
}
}
Skeleton for call code:
A equal the count for the sensor, an integer between 1 and the number of sensors
Y0 is the previous counts call code
Y is the previous sensor Will change which channel the message is published in and the which sensor the message refers to,
X is the current sensor Will change which channel the message is published in and the which sensor the message refers to, will be the same as A
X0 is the current sensors code which you are testing.
B next count, A+1
if (Count = A) {
if (myVal = Y0) {
client.publish("Product/SensorY", "Sensor Y is: LOST");
client.publish("Product/Code", "X0");
delay(500);
}
else {
client.publish("Product/Code", "X0");
delay(500);
}
Count = B;
delay(500);
}
Note: for the last sensor (which will be the first in the sequence) B will always be 0 and you will have:
if (Count = 0) {
Count = 1;
delay(500);
}
}
To act as a reset for the count before the next timer check.
DHT Temperature Sensor
Components
- DHT sensor
- Wires
- WEMOS D1 mini (can be the same as used for the moisture sensor base)
Wiring and Setup
For this component you need to have downloaded the DHT sensor library by Adafruit. Wiring is all direct but again uses a ground rail if you are using the same WEMOS as the base and are using LEDS. If either of statements are false you can wire it in directly.
Power ->
Ground -> Ground
Signal -> 4
Code
In top of Code with other library includes:
#include "DHT.h" // Used to allow the DHT sensor to function
#define DHTPIN 4 //Assigns the DHT pin Note this is the ISO pin label not the D label
#define DHTTYPE DHT11 // Defines the sensor type
DHT dht(DHTPIN, DHTTYPE); // Setsup DHT with both the previous factors
In Void Setup:
dht.begin(); // Activate the DHT sensor
In Void Loop:
delay(5000); //Delay for time between temperature updates
float t = dht.readTemperature(); //Take temperature reading from data
String Temp; // Setup a string for the temperature
Temp = "The temperature is currenly " + String(t) + " Degrees Celcius"; //Turn data into a nice to read message in string format
client.publish("Product/Temp", Temp); // Publish it to the temperature subchannel
// Note: you can also retrieve both humidity and temperature in Fahrenheit, but we chose to exclude these values to avoid overloading the server. You can include them with:
float h = dht.readHumidity(); // For Humidity
float f = dht.readTemperature(true); // For Fahrenheit
RFID Scanner
Components
- WEMOS D1 mini
- MFRC522 RFID reader Module
- Jumper Cables (Both of our components came with pre-soldered male pins so we only used female to female wires.)
- An RFID Dongle for each tool
Set Up & Wiring
Install the library ‘ MFRC522 by GitHubCommunity’ . Wiring is all Direct and follows these connections:
RST -> D1
SS -> D2
MOSI -> D7
MISO -> D6
SCK -> D5
GND -> Ground
3.3V _> 3V3
Code
There are 2 flexible variables in this code: 1. The storage array of your RFID codes found in the matrix (Each dongle will have a unique hex code that the code checks against its database to see if a tool has been scanned and if so which. 2. The tool name variables each used to track the state of a specific tool.
Each of these increase with each tool you wish to track and can cause some complexity beyond what we have detailed here.
Primarily before void Connect:
#include <SPI.h>
#include <MFRC522.h> //SPi and MFRC522 Librarys used to make the RFID module function
#define RSTPIN D1
#define SSPIN D2
MFRC522 rc(SSPIN, RSTPIN); // Defining RFID communication pins to allow it to function
int readSuccess; // Int to check if a card has been detected
int Code; // Int to define which code has been read relevant to the respective tool
int ToolA ; // Keeps track of the first tool’s state
int ToolB; // Keeps track of the second tool’s state
int ToolC; // Keeps track of the third tool’s state
//2-dimensional array to store card codes
byte defcard[][4] = { { 0x93,0xBA,0x40,0x16 }, { 0x1D,0x89,0xC5,0x1 },{0x77,0x33,0xA,0x1} }; // Each code refers to a specific tool, These will be different with your own dongles. Dongle codes can be found by scanning them after up loading this full code. Their specific hex code will pop into serial monitor when scanned.
int N = 3; //change this to the number of tools you will track
byte readcard[4];
Added to Void Set Up to initialise the reader and display the desired codes:
SPI.begin(); //Start the reciever database
rc.PCD_Init(); //initialize the receiver
rc.PCD_DumpVersionToSerial(); // Tell us what version we're using
Serial.println(F("the authorised cards are")); //display authorised cards just to demonstrate you may comment this section out
for (int i = 0; i < N; i++) { // Check each card in order
Serial.print(i + 1);
Serial.print(" ");
for (int j = 0; j < 4; j++) {
Serial.print(defcard[i][j], HEX); //Print each cards code in order
}
Serial.println("");
}
Serial.println("");
Serial.println(F("Scan Access Card to see Details"));
}
Code to add to the bottom of Void loop. This checks the scanned dongle and if it’s a correct one marks which one before changing that tools state and sending the message to the server:
readSuccess = getid(); //If we register a card
if (readSuccess) {
int match = 0;
//See if it is one of our registered cards/tools
for (int i = 0; i < N; i++) {
if (!memcmp(readcard, defcard[i], 4)) { //if it is
Code = i; //Set the tool value to the corresponding tool
Serial.println(i); // print it
match++;
}
}
if (Code == 1){ // If its tool 1…
if (ToolA ==1){ // ...and its being taken out...
client.publish("Product/Tool1", "Tool 1 is: IN USE"); //... Say its being taken out on the server...
ToolA = 2;//... and change its status to out
delay(2000);
}
else{ // ...and its being put back...
client.publish("Product/Tool1", "Tool 1 is: AVAILABLE"); //... Say its being put back on the server...
ToolA = 1; //... and change its status to available
delay(2000);
}
}
if (Code == 2){// If its the 2nd tool...
if (ToolB==1){ // ...and its being taken out...
client.publish("Product/Tool2", "Tool 2 is: IN USE"); //... Say its being taken out on the server...
ToolB = 2; //... and change its status to out
delay(2000);
}
else{ // ...and its being put back...
client.publish("Product/Tool2", "Tool 2 is: AVAILABLE"); //... Say its being put back on the server...
ToolB = 1; //... and change its status to available
delay(2000);
}
}
if (Code == 0){// If its the 2nd fork... Note: Small quirk of the matrix is that it starts at 0 for this variable.
if (ToolC ==1){ // ...and its being taken out...
client.publish("Product/Tool3",” Tool 3 is: IN USE"); //... Say its being taken out on the server...
ToolC = 2; // ... and change its status to out
delay(2000);
}
else{ // ...and its being put back...
client.publish("Product/Tool3", "Tool 3 is: AVAILABLE"); //... Say its being put back on the server...
ToolC = 1; //... and change its status to available
delay(2000);
}
} //Delays included to prevent double scans
}
}
Skeleton Code
Matrices Starts at 0So 1 =0, 2 =1, ect for Code variable. Channels and tool variable can customised to reflect to tool in question but each must be unique.
A = Matrices value for Code variable
ToolX is the variable for the tools status
if (Code == A){
if (ToolX==1){
client.publish("Product/ToolX", "Tool X is: IN USE”);
ToolX = 2;
delay(2000);
}
else{
client.publish("Product/ToolX", "Tool X is: AVAILABLE");
ToolX = 1;
delay(2000);
}
}
Insert After Void Loop outside of any other voids. This subfunction collects the id of the scanned dongle and displays it via the Serial monitor. This is especially useful during set up and expansion of the product.
int getid() {
if (!rc.PICC_IsNewCardPresent()) {
return 0;
}
if (!rc.PICC_ReadCardSerial()) {
return 0;
}
Serial.println("THE UID OF THE SCANNED CARD IS:");
for (int i = 0; i < 4; i++) {
readcard[i] = rc.uid.uidByte[i]; //storing the UID of the tag in readcard
Serial.print("0x");
Serial.print(readcard[i], HEX);
if (i < 3) {
Serial.print(",");
}
}
Serial.println("");
Serial.println("Now Comparing with Authorised cards");
rc.PICC_HaltA();
return 1;
}
Conclusion
We hope that this brief instructible is helpful for when you try to create and customise your own allotment monitoring system. Its Versatility and modularity will hopefully appeal to your needs.
Authors:
The Nature Uniting Technology Towards Interconnected Networks Group
Calum Moore
Luke Ryder
Fergus Ried
Alan Arthur William Martin