Building a IoT farming from Swinburne workshop

Hello there. Recently, I joined a Swinburne workshop about IoT and get a farming kit from them. So, I will share my experience of assembling this kit.

Assembling the circuit

At first, I received a package of electronic component and started to connect the main board to battery. Unfortunately, the wire of battery holder is broken and the main board cannot turn on.

After measuring both wire using multimeter, I noticed that somewhere in the middle of red wire is broken. To fix this, I just cut off the wire and replace it using a jumper wire from the farming kit. After the soldering iron is hot enough, the new wire is soldered in place.

Writing boilerplate code and preparing server

Based on the workshop, I should use Arduino IDE with ESP plugin to program the main board which is a nodeMCU with esp-12e core. But, the Arduino IDE for Linux is come in appimage format and I don’t want to pollute my precious EXT4 file system with appimage. So, I use PlatformIO instead. By setting the board to esp-12e in platformIO config file and write a Hello World c++ code, everything is good to go.

After, making a working program run successfully on the nodeMCU, it’s time to prepare the server side thing. The platform I am going to run the MQTT server and node red server is a TV box installed with armbian instead of my laptop. The reason is I want to make the IoT farming robot run 24 hrs and really use it in real life. So, running a server on my laptop is not a good idea.

Luckily, the MQTT broker server I am going to use, mosquitto is available in armbian repository. Meanwhile, the Node Red can be installed from npm repository. After installing all the thing I need, I started to configure the mosquitto. Due to the IoT farm is going to use in real life, everything need to be secure and practical. So, I added auth username and password to the config.

However, due to my home weird networking, the server is actually unreachable to nodeMCU. This is because they are separated by two router in two different LAN.

To establish the connection, there are two way. First, is by using P2P VPN, zerotier to something like a virtual LAN but installing a VPN client on a tiny ESP is not practical. So, I am going to use another way, port forwarding. Basically, I port forward the MQTT port of the server, 1883 to a port of the secondary router, 6969.

Then, to ensure the port is really working, I scan the port from the primary router using nmap.

Coding for NodeMCU

Now, the server preparation is done. It’s time to write some code for NodeMCU. Unfortunately, I delay this for too long until the example code for IoT farming in WhatsApp is not available. So, I have to write everything on my own. After, a few hours of writing and search endless of Stack Overflow, I finally make a prototype code. When I upload it onto the NodeMCU, I was full of disappointment because it is not working. No matter how I change the coding logic, the code is still not working.

As a senior programmer of SMK Jalan Arang robotics club, I smell something sus in the networking. I scanned the LAN traffic using wireshark with MITM sniffing and realize there are no out going packet from NodeMCU but the ping is still working.

I discussed with another robotic club senior, Declan and he suggest me to use datatype IPAddress to define server address:

1
IPAddress server(192, 168, 0, 2); 

instead of write it in URL:

1
char* server = "192.168.0.2";

After that, the code is finally working. (In the screenshot, I test it using a MQTT server installed on my laptop)

Node Red Server

Now, coding for NodeMCU is done. It’s time to code the Node Red Server. Since Node Red use drag and drop to do coding, this step is far easier than previous steps.

Then, here’s the ui. To access the web page, P2P VPN is required to connect.

phone

PC

Connectivity indicator

To make use of the LED traffic light from the farming kit, I am going to use it as connectivity indicator.

MQTT server down

If the MQTT server is down, red and orange led will turn on.

Node Red server down

If the Node Red server is down, green and orange led will turn on.

Everything in good status

If both servers is online, green led will turn on.

Power consumption test

Because this IoT farm is going to use for a long time, understand its power consumption is important so the battery wont run out so often. To test the power consumption, I use a lab bench power supply which has ammeter and set its output voltage to around 8.5V. (The voltmeter of the power supply has zero error so 9V might burn the circuit.)

normal condition (water pump off)

running condition (water pump on but no load)

on-load condition (water pump on and pumping water)

So, here is the result:

Condition Voltage Ampere Power
idle 8.5V 0.05A 0.425W
no load 8.6V 0.35A 3.01W
on load 8.2V 0.6A 4.92W

The power consumption in idle condition is quite high if it is only power by non-rechargable battery. So, I will replace the power supply to solar panel in the future.

Mechanism behind it

Connection diagram

As a summary, here is the connection diagram between each device.

  1. Wifi connection between primary router and NodeMCU.
  2. connection between primary router and secondary router sparated into two LAN.
  3. connection between secondary router and Armbian server with Internet access.
  4. Armbian server connected to p2p vpn through both router.
  5. Outer device can route through p2p vpn and able to access secondary router LAN via armbian server ip forwarding.

MQTT

Meanwhile, the MQTT part is quite simple, only three event involved.

  • waterLvl - Indicate moisture in soil, the higher the value, the less moist the soil.
  • motor - Control pump. If value is 0, then turn on the pump, vice versa.
  • pong - Used to indicate whether Node Red server is working. (actually can use motor event to indicate)

Flowchart

I only do the flowchart for the NodeMCU side because Node Red is already use graphical programming to program.

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
#include "ESP8266WiFi.h"
#include "PubSubClient.h"

#include "Arduino.h"
#include "core_esp8266_features.h"
#include "pins_arduino.h"

#ifndef LED_BUILTIN
#define LED_BUILTIN 13
#endif

#define R_LED 4
#define G_LED 16
#define Y_LED 5

#define MOTOR 14

IPAddress server(192, 168, 0, 2);

unsigned long int lastPing = 0;

WiFiClient espClient;
PubSubClient client(espClient);
unsigned long lastMsg = 0;
#define MSG_BUFFER_SIZE (50)
char msg[MSG_BUFFER_SIZE];
int value = 0;

void callback(char *topic, byte *payload, unsigned int length) {
Serial.print("Message arrived [");
Serial.print(topic);
Serial.print("] ");
for (int i = 0; i < length; i++) {
Serial.print((char)payload[i]);
}
Serial.println();

if (strcmp(topic, "motor") == 0) {
if ((char)payload[0] == '0') {
digitalWrite(MOTOR,
LOW);
} else {
digitalWrite(MOTOR,
HIGH);
}
} else if (strcmp(topic, "pong") == 0) {
lastPing = millis();
}
}

void reconnect() {
while (!client.connected()) {
Serial.print("Attempting MQTT connection...");
if (client.connect("farm_esp", "******", "******")) {
digitalWrite(R_LED, LOW);
digitalWrite(G_LED, LOW);
digitalWrite(Y_LED, LOW);
Serial.println("connected");
client.subscribe("motor");
client.subscribe("pong");
} else {
digitalWrite(R_LED, HIGH);
digitalWrite(G_LED, LOW);
digitalWrite(Y_LED, HIGH);
Serial.print("failed, rc=");
Serial.print(client.state());
Serial.println(" try again in 5 seconds");
delay(5000);
}
}
}

void setup() {
Serial.begin(115200);
pinMode(LED_BUILTIN, OUTPUT);
pinMode(R_LED, OUTPUT);
pinMode(G_LED, OUTPUT);
pinMode(Y_LED, OUTPUT);
pinMode(MOTOR, OUTPUT);

digitalWrite(R_LED, HIGH);
digitalWrite(G_LED, HIGH);
digitalWrite(Y_LED, HIGH);

digitalWrite(MOTOR, HIGH);

Serial.println("WiFi connecting");
WiFi.begin("****", "****");
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print("*");
}

Serial.println("");
Serial.println("WiFi connection Successful");
Serial.print("The IP Address of ESP8266 Module is: ");
Serial.print(WiFi.localIP());

digitalWrite(R_LED, LOW);
digitalWrite(G_LED, LOW);
digitalWrite(Y_LED, LOW);
client.setServer(server, 6969);
client.setCallback(callback);
}

void loop() {
if (!client.connected()) {
reconnect();
delay(500);
}
client.loop();

unsigned long now = millis();
if (now - lastMsg > 2000) {
lastMsg = now;
++value;
snprintf(msg, 10, "%i", analogRead(A0));
Serial.print("Publish message: ");
Serial.println(msg);
client.publish("waterLvl", msg);
}

if (millis() - lastPing > 10000) {
digitalWrite(Y_LED, HIGH);
digitalWrite(G_LED, LOW);
} else {
digitalWrite(G_LED, HIGH);
digitalWrite(Y_LED, LOW);
}
}