Be kind to your plants! Learn about the green house environment using MKR1000 and sensors. Control soil humidity and air conditions!
Things used in this project
Hardware components |
||||||
|
× | 1 | ||||
|
× | 1 | ||||
|
× | 1 | ||||
|
× | 1 | ||||
|
× | 1 | ||||
|
× | 1 | ||||
|
× | 1 | ||||
Software apps and online services |
||||||
|
||||||
|
Story
Grow plants and vegetables more aware of their environment conditions. Simply by using a MKR1000
Requirements for Plant Growth
Plant growth depends of several ideal environment conditions such as temperature, humidity, light and other mineral nutrients or oxygen.
Providing the plants with an adequate amount of water or keep the normal temperature in the green house can be a tricky process but not if you could do it it much easier.
Hardware overview
Temperature / Humidity
TH1 – Outside temp values will be provided from a waterproof DS18B20 sensor – D1 pin connection
TH2 – Inside temp. / hum. can be found out from DHT22 – pin D0
TH3 – FS200/SHT25 – will be introduced in soil conditions. Connections will at for SCL & SDA pins 12/11
Water lever
W1 – in order to get water level will be using a ultrasound sensor like HC – SR04 Connections at A0 – Echo and Trigger at A1. Based on this value you can control a valve using R1 – 4 channels relay.
Air Temperature / Oxygen
A1 – Base on temperature/ humidity values you can command when to open windows using R2 relay. Based on climate you should tune it as you wish.
Relays R1 / R2
To set some commands like open Windows or start any pump you could send commands to the relays … R1 is connected to digital pins 7,8,9,10 and R2 5,
Connections on the MKR1000
MKR1000 – aRest
Now that we connected all things together is time to get some data and make sens using aRest library, that will provide a web interface to MKR1000 device with json format string. Use the libraries, .ino files provided bellow and change your Wifi router connection accordingly.
aRest Interface
Data and Interface
As long that we have an interface our device we could also consume this data and see it on the local network.
We’ve been using Visual Studio 2015 and .net to build up a Windows application that will help you visualize green house live conditions but also a bit of daily history or all sort of logs that you want to integrate.
Next step will be to modify the DsKey and AKey provided in the Settings.txtfrom application folder. If everything works you should see some data online already.
Follow DasData’s hackster channel for more future updates
Green house Automation
You should decide what valves or motors will create more action based on your greenhouse configuration and sizes. All this could be controlled from relays.
Schematics
DasFilisera – Schematics
Code
Filisera_Arduino
C/C++
/*
DASFilisera using aREST Library for the Arduino/Genuino MKR1000 board.
aREST - Written in 2016 by Marco Schwartz under a GPL license.
*/
// Import required libraries
#include <SPI.h>
#include <WiFi101.h>
#include <aREST.h>
#include "DHT.h"
#include <OneWire.h>
#include <Wire.h>
#include <DallasTemperature.h>
#include <SHT2x.h>
// *************************
// ** Senzori Temperatura **
// *************************
#define DHTPIN 0 // Inside humidity/temp/heat index
#define DHTTYPE DHT22 // DHT 22 (AM2302), AM2321
#define ONE_WIRE_BUS 1
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);
float umidSol = 0; // Soil humidity
float umidAer = 0; // Air humidity
float tempIn = 0; // Temperature in
float tempOut = 0; // Temperature out
float tempSol = 0; // Container volume
float umidexIn = 0; // Feels like temp
//VOLUM
#define echoPin A0 // Echo pin A0
#define trigPin A1 // Trigger pin A1
long duration ; // duration to calculate distance
int HR_dist = 0; // Calculated distance
int minimumRange=5; //Min Sonar range
int maximumRange=200; //Max Sonar range
unsigned long pulseduration=0;
int nivelCm, nivelRamasCm;
int nivelProc, nivelVol;
int empty = 100;
int full = 100;
int sqrLevel, sqrInvertLevel ;
// Status
int status = WL_IDLE_STATUS;
// Create aREST instance
aREST rest = aREST();
// WiFi parameters
char ssid[] = "WIFI_AP";
char password[] = "WIFI_PASSW";
// The port to listen for incoming TCP connections
#define LISTEN_PORT 80
// Create an instance of the server
WiFiServer server(LISTEN_PORT);
//INITIALIZARE SENZORI
DHT dht(DHTPIN, DHTTYPE);
// RELEE RELEEE
int aVentil, aLateral;
int inReleu1Ventil = 6;
int inReleu2Ventil = 7;
int inReleu3Ventil = 8;
int inReleu4Ventil = 9;
int inReleu1Lateral = 10;
int inReleu2Lateral = 11;
// Declare functions to be exposed to the API
int ledControl(String command);
void setup(void)
{
// Start Serial
Serial.begin(115200);
// Init variables and expose them to REST API
// rest.variable("temperature",&temperature);
// rest.variable("humidity",&humidity);
// Function to be exposed
rest.function("led",ledControl);
// Give name and ID to device
rest.set_id("1");
rest.set_name("mkr1000");
// Connect to WiFi
while (status != WL_CONNECTED) {
Serial.print("Attempting to connect to SSID: ");
Serial.println(ssid);
status = WiFi.begin(ssid, password);
// Wait 10 seconds for connection:
delay(10000);
}
Serial.println("WiFi connected");
// Start the server
server.begin();
Serial.println("Server started");
// Print the IP address
IPAddress ip = WiFi.localIP();
Serial.print("IP Address: ");
Serial.println(ip);
// SETUP DHT - INSIDE
DHT dht(DHTPIN, DHTTYPE);
// sensors.begin(); // Pornire senzor temperatura exterioara DS-16B20
pinMode(trigPin, OUTPUT);
pinMode(echoPin, INPUT);
// Init variables and expose them to REST API
rest.variable("Vol_container",&sqrLevel );
rest.variable("Umiditate_sol",&umidSol );
rest.variable("Umiditate_aer",&umidAer );
rest.variable("Temp_in",&tempIn );
rest.variable("Temp_out",&tempOut );
rest.variable("Temp_sol",&tempSol );
// rest.variable("Umidex_in",&umidexIn );
// sensors.requestTemperatures(); // Temperatura Out
}
void loop() {
getmyDistance();
// getSoilVals();
getInsideVals();
// getOutSideVals();
// Handle REST calls
WiFiClient client = server.available();
if (!client) {
return;
}
while(!client.available()){
delay(1);
}
rest.handle(client);
}
void getmyDistance() {
digitalWrite(trigPin,LOW);
delayMicroseconds(2);
digitalWrite(trigPin,HIGH);
delayMicroseconds(10);
digitalWrite(trigPin,LOW);
duration = pulseIn(echoPin,HIGH);
//calculate distance
HR_dist = duration/58.2;
// sqrLevel = empty - HR_dist;
if (HR_dist >= maximumRange || HR_dist <= minimumRange) {
sqrLevel = 0;
}
else {
sqrLevel = empty - HR_dist;
Serial.println(HR_dist);
}
delay(100);
}
void getDistance() {
digitalWrite(trigPin, LOW);
delayMicroseconds(10);
// now send the 10uS pulse out to activate Ping
digitalWrite(trigPin, HIGH); delayMicroseconds(10); digitalWrite(trigPin, LOW);
// finally, measure the length of the incoming pulse
pulseduration = pulseIn(echoPin, HIGH);
// divide the pulse length by half
pulseduration = pulseduration/2;
// convert to centimetres.
nivelRamasCm = int(pulseduration/29);
nivelCm = empty - nivelRamasCm;
nivelVol = map(nivelRamasCm, full, empty, 100, 0);
// Tipareste nivelul in centimetri
if (nivelCm >= -5 && nivelCm <=135) {
double a = nivelCm / 100 * nivelVol;
int sqrLevel = 240 - nivelCm;
int sqrInvertLevel = 120 + nivelRamasCm;
if (nivelCm >65 && nivelCm <=85){
}
if (nivelCm >85 && nivelCm <=100){
}
}
}
long microsecondsToCentimeters (long microseconds) {
// The speed of sound is 340 m/s or 29 microseconds per centimeter
// The ping travels forth and back, so, the distance is half the distance traveled
return microseconds / 29 / 2;
}
void getInsideVals() {
float h = dht.readHumidity();
// Read temperature as Celsius (the default)
float t = dht.readTemperature();
// Read temperature as Fahrenheit (isFahrenheit = true)
// float f = dht.readTemperature(true);
umidAer = h;
tempIn = t;
// Check if any reads failed and exit early (to try again).
if (isnan(h) || isnan(t)) {
Serial.println("Failed to read from DHT sensor!");
return;
}
// Compute heat index in Fahrenheit (the default)
//float hif = dht.computeHeatIndex(f, h);
// Compute heat index in Celsius (isFahreheit = false)
// float hic = dht.computeHeatIndex(t, h, false);
// umidexIn = hic;
}
void getOutSideVals() {
tempOut = sensors.getTempCByIndex(0) ;
Serial.println("Temperature Out: ");
Serial.print(tempOut);
}
void getSoilVals() {
umidSol = SHT2x.GetHumidity();
tempSol = SHT2x.GetTemperature();
Serial.println("Humidity Soil(%RH): ");
Serial.print(umidSol);
Serial.print(" Temperature Soil (C): ");
Serial.print(tempSol);
}
// sensors.requestTemperatures(); // Temperatura Out
// Custom function accessible by the API
int ledControl(String command) {
// Get state from command
int state = command.toInt();
digitalWrite(6,state);
return 1;
}
libraries.zip
C/C++
No preview (download only).
Filisera – vb.zip
VBScript
No preview (download only).
Filisera – VS
VBScript
Imports System.Globalization
Imports System.IO
Imports System.Net
Imports System.Threading
Imports System.Windows.Forms.DataVisualization.Charting
Imports Newtonsoft.Json
Public Class Form1
Private _deviceIP As String = "" 'DEVICE IP
Private intVolumContainer, intUmidSol, intUmidSera, intTempAfara, intTempSera, intTempSol, intUmidex As String ' VARIABILES
Private strR1, strR2 As String 'RELAYS
Public Count As CountDown 'COUNTDOWN TICK
Private _dasResult As String = "" 'RESULTAT CITIRE SENZORI DEVICE
Private _AKey As String = "" 'USER KEY DASDATA
Private _DSKey As String = "" 'DATASET KEY DASDATA
Private _myDas As New dasData.das
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Count = New CountDown(50, 0) '5 SECONDS UPDATE
AddHandler Count.Tick, AddressOf Count_Tick
AddHandler Count.TimesOut, AddressOf Times_up
Try 'READ SETTINGS FROM .txt FILE
Dim path As String = Directory.GetCurrentDirectory()
Dim FILE_NAME As String = path & "\Settings.txt"
Dim myText As String = "" '
If System.IO.File.Exists(FILE_NAME) = True Then
Dim objReader As New System.IO.StreamReader(FILE_NAME, System.Text.Encoding.Default)
Do While objReader.Peek() <> -1
myText &= objReader.ReadLine()
Loop
Dim _settingItems() As String = myText.Split(" ")
_deviceIP = _settingItems(0) 'IP-ul DEVICE
_AKey = _settingItems(1).Replace("akey:", "")
_DSKey = _settingItems(2).Replace("dskey:", "")
cbxEnabled.Text = "Device:" & _deviceIP
txtInfo.Text = "IP device: " & _deviceIP & vbNewLine & "AKey: " & _AKey & vbNewLine & "DSKey: " & _DSKey & vbNewLine
strR1 = _settingItems(3).Replace("relay1:", "")
txtInfo.Text += "Relay1: " & strR1 & vbNewLine
strR2 = _settingItems(4).Replace("relay2:", "")
txtInfo.Text += "Relay2: " & strR2 & vbNewLine
' txtInfo.Text = "Settings: " & myText
objReader.Dispose()
objReader.Close()
End If
Catch ex As Exception
lblDevice.Text = ex.Message.ToString
End Try
cmdCheckDevice()
'LOCAL SETTINGS
Thread.CurrentThread.CurrentCulture = New CultureInfo("en-US")
'Thread.CurrentThread.CurrentUICulture = New CultureInfo("ro-RO")
cmdStartFilisera()
End Sub
Private Sub Count_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs)
cmdGetSensorValues()
cmdTriggers()
If CDate(dtFrom.Text).ToString("dd.MM.yyyy") = Date.Now().ToString("dd.MM.yyyy") Then
cmdStartFilisera()
End If
End Sub
Private Sub Times_up(ByVal sender As System.Object, ByVal e As System.EventArgs)
' Count.Reset()
'Count.Pause()
End Sub
Private Sub cmdStartFilisera()
Try
dtFrom.MaxDate = DateTime.Now
_dasResult = _myDas.getDas(_DSKey, "json", 0, 100)
cmdFillData()
Catch ex As Exception
lblDevice.Text += ex.Message.ToString
End Try
End Sub
Private Sub cbxEnabled_CheckedChanged(sender As Object, e As EventArgs) Handles cbxEnabled.CheckedChanged
cmdCheckDevice()
End Sub
Private Sub cmdCheckDevice()
Try
If cbxDemo.Checked = False Then
lblDevice.Text = ("Enable device!")
lblEvenimente.Text = Date.Now.ToString("dd.MM.yyyy HH:mm:ss")
If cbxEnabled.Checked = True Then
Dim pingsender As New Net.NetworkInformation.Ping
If Not pingsender.Send(_deviceIP).Status = Net.NetworkInformation.IPStatus.Success Then
lblDevice.Text = ("Device not availble on the local network!")
cbxEnabled.Checked = False
btnTemperatura.Enabled = False
btnVolumContainer.Enabled = False
Count.Reset()
Count.Pause()
MessageBox.Show("Device at " & _deviceIP & " not found!")
Else
lblDevice.Text = ("Device connected!")
btnTemperatura.Enabled = True
btnVolumContainer.Enabled = True
cmdGetSensorValues()
Timer1.Enabled = True
cmdAddData()
Count.Start()
End If
Else
End If
Else
btnTemperatura.Enabled = True
btnVolumContainer.Enabled = True
cmdGetSensorValues()
Timer1.Enabled = True
cmdAddData()
Count.Start()
End If
Catch ex As Exception
End Try
End Sub
Private Sub btnVolumContainer_Click(sender As Object, e As EventArgs) Handles btnVolumContainer.Click
' http://192.168.0.102/digital/7/1
'CLOSE WATER FROM BUTTON
Dim _ItemsR1() As String = strR1.Split(",")
If btnVolumContainer.Text.Contains("Open") Then
cmdRelay(_ItemsR1(0), 1)
cmdRelay(_ItemsR1(1), 1)
cmdRelay(_ItemsR1(2), 0)
cmdRelay(_ItemsR1(3), 0)
btnVolumContainer.Text = "Close water"
Else
cmdRelay(_ItemsR1(0), 0)
cmdRelay(_ItemsR1(1), 0)
cmdRelay(_ItemsR1(2), 1)
cmdRelay(_ItemsR1(3), 1)
btnVolumContainer.Text = "Open water"
End If
' Dim _url As String = _deviceIP & "digital/7/1"
End Sub
Private Sub btnTemperatura_Click(sender As Object, e As EventArgs) Handles btnTemperatura.Click
'CLOSE WINDOW FROM BUTTON
Dim _ItemsR2() As String = strR2.Split(",")
If btnTemperatura.Text.Contains("Open") Then
cmdRelay(_ItemsR2(0), 1)
cmdRelay(_ItemsR2(1), 1)
btnTemperatura.Text = "Close window"
Else
cmdRelay(_ItemsR2(0), 0)
cmdRelay(_ItemsR2(1), 0)
btnTemperatura.Text = "Open window"
End If
End Sub
Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
cmdAddData()
End Sub
Private Sub btnTestCloud_Click(sender As Object, e As EventArgs) Handles btnTestCloud.Click
Try
cmdStartFilisera()
Catch ex As Exception
MessageBox.Show("Lost connection with DasData!")
End Try
End Sub
Private Function cmdRelay(ByVal _DPin As String, ByVal _cmd As Int16)
Try
Dim _urlCommand As String = _deviceIP & "/digital/" & _DPin & "/" & _cmd
Dim client As New WebClient()
client.Headers.Add("user-agent", "Arduino/4.0 (compatible; MSIE 6.0; Windows NT 5.2; .NET CLR 1.0.3705;)")
Dim data As Stream = client.OpenRead(_urlCommand)
Dim reader As New StreamReader(data)
Dim s As String = reader.ReadToEnd()
' txtInfo.Text += (s)
data.Close()
reader.Close()
Catch ex As Exception
End Try
End Function
Private Sub dtFrom_ValueChanged(sender As Object, e As EventArgs) Handles dtFrom.ValueChanged
Try 'GET VALUES BASED ON DATE SELECTION
_dasResult = _myDas.getDas(_DSKey, "jsond", CDate(dtFrom.Text).ToString("dd.MM.yyyy"), CDate(dtFrom.Text).ToString("dd.MM.yyyy"))
cmdFillData()
Catch ex As Exception
MessageBox.Show(ex.Message.ToString)
End Try
End Sub
Private Sub cmdGetSensorValues()
Try
''GET SENSOR VALUES FROM DEVICE
If cbxDemo.Checked = False Then
Dim client As New WebClient()
client.Headers.Add("user-agent", "Arduino/4.0 (compatible; MSIE 6.0; Windows NT 5.2; .NET CLR 1.0.3705;)")
Dim data As Stream = client.OpenRead("http://" & _deviceIP & "/index.html")
Dim reader As New StreamReader(data)
Dim s As String = reader.ReadToEnd()
s = s.Replace("""", "").Replace("}", "").Replace("{", "").Replace(" ", "").Replace("variables:", "")
Dim sItems() As String = s.Split(",")
intVolumContainer = cmdGetVal(sItems(0))
intUmidSol = cmdGetVal(sItems(1))
intUmidSera = cmdGetVal(sItems(2))
intTempSera = cmdGetVal(sItems(3))
intTempAfara = cmdGetVal(sItems(4))
intTempSol = cmdGetVal(sItems(5))
data.Close()
reader.Close()
Else
intVolumContainer = _rnd(60)
intUmidSol = _rnd(80)
intUmidSera = _rnd(20)
intTempSera = _rnd(45)
intTempAfara = _rnd(57)
intTempSol = _rnd(66)
End If
ggVolum.Value = intVolumContainer
rgVolum.Value = intVolumContainer
ggUmidSol.Value = intUmidSol
ggUmidSera.Value = intUmidSera
ggTempSera.Value = intTempSera
ggTempOut.Value = intTempAfara
ggTempSol.Value = intTempSol
Catch ex As Exception
'{"variables" {"Volum_container": 0, "Umiditate_sol": 0.00, "Umiditate_aer": 36.60, "Temp_in": 26.50, "Temp_out": 0.00, "Temp_sol": 0.00, "Umidex_in": 26.37}, "id": "1", "name": "FiliSera", "hardware": "arduino", "connected": true}
End Try
End Sub
Private Function cmdGetVal(ByVal _sItem As String)
Try
'SPLIT STRING AND GET VALUES BACK
Dim _s() As String = _sItem.Split(":")
Return _s(1)
Catch ex As Exception
End Try
End Function
Private Sub cmdAddData()
Try
'SEND DATA TO DASDATA CLOUD STORAGE
Dim _strFiliseraDataSend As String = intVolumContainer & "|" & intUmidSol & "|" & intUmidSera & "|" & intTempAfara & "|" & intTempSera & "|" & intTempSol '& "|"& intUmidex & "|"&
Dim _dasResult As String = _myDas.sendDas(_strFiliseraDataSend, _DSKey, _AKey)
Catch ex As Exception
cmdLogMe("ERROR..." & ex.Message.ToString(), "error")
End Try
End Sub
Private Sub cmdFillData()
Try
'FILL INTERFACE WILL ALL SORT OF DATA
Dim dataSet As DataSet = JsonConvert.DeserializeObject(Of DataSet)(_dasResult)
Dim dataTable As DataTable = dataSet.Tables(0)
'GRIDVIEW
DataGridView1.DataSource = dataSet.Tables(0)
DataGridView1.AutoGenerateColumns = True
'CHARTS
ChartTemp.DataSource = dataTable
ChartTemp.Series.Clear()
chartUmid.DataSource = dataTable
chartUmid.Series.Clear()
chartVolum.DataSource = dataTable
chartVolum.Series.Clear()
Dim i As Integer = 0
Dim j As Integer = 0
Dim k As Integer = 0
For Each table In dataSet.Tables
For Each row In table.Rows
For Each col In table.Columns
Dim colName As String = col.ColumnName.ToString
If colName <> "id" And colName <> "AIDate" Then
Try
Dim serieName As String = RTrim(colName)
'VOLUME
If colName.Contains("Nivel") Then
chartVolum.Series.Add(serieName)
chartVolum.Series(serieName).LabelFormat = "N1"
chartVolum.Series(i).ChartType = SeriesChartType.Line
chartVolum.Series(i).BorderWidth = 2
chartVolum.Series(i).Color = Color.Aqua
chartVolum.Series(i).BorderDashStyle = ChartDashStyle.Solid
chartVolum.Series(serieName).YValueMembers = RTrim(colName)
chartVolum.Series(serieName).XValueMember = "AIDate"
i += 1
End If
'HUMIDITY
If colName.Contains("Umid") Then
chartUmid.Series.Add(serieName)
chartUmid.Series(serieName).LabelFormat = "N1"
chartUmid.Series(j).ChartType = SeriesChartType.Line
chartUmid.Series(j).BorderWidth = 2
chartUmid.Series(j).BorderDashStyle = ChartDashStyle.Solid
chartUmid.Series(serieName).YValueMembers = RTrim(colName)
chartUmid.Series(serieName).XValueMember = "AIDate"
j += 1
End If
'TEMP
If colName.Contains("Temp") Then
ChartTemp.Series.Add(serieName)
ChartTemp.Series(serieName).LabelFormat = "N1"
ChartTemp.Series(k).ChartType = SeriesChartType.Line
ChartTemp.Series(k).BorderWidth = 2
ChartTemp.Series(k).BorderDashStyle = ChartDashStyle.Solid
ChartTemp.Series(serieName).YValueMembers = RTrim(colName)
ChartTemp.Series(serieName).XValueMember = "AIDate"
k += 1
End If
Catch ex As Exception
End Try
End If
Next
Exit For
Next
Next
'FILL THE CHARTS
ChartTemp.DataBind()
ChartTemp.Visible = True
chartUmid.DataBind()
chartUmid.Visible = True
chartVolum.DataBind()
chartVolum.Visible = True
Catch ex As Exception
cmdLogMe("ERROR..." & ex.Message.ToString(), "error")
End Try
End Sub
Private Sub cmdTriggers()
'SET TRIGGERS AND STORE THE LOG
Select Case intVolumContainer
Case > 80
cmdLogMe("HIGH HIGH - Container volume - stop water", "0")
Case > 70
' cmdLogMe("HIGH - Container volume - water will start", "1")
Case < 20
' cmdLogMe("LOW - Container volume low - water will stop", "1")
Case < 10
cmdLogMe("LOW LOW - Container volume high high high - start water", "0")
End Select
Select Case intTempSera
Case > 38
cmdLogMe("HIGH HIGH - Temperature in green house - open windows", "0")
Case > 30
' cmdLogMe("HIGH - Temperature in green house - prepare windows", "1")
Case < 10
' cmdLogMe("LOW - Temperature in green house - prepare windows", "1")
Case < 10
cmdLogMe("LOW LOW - Container volume high high high - start water", "0")
End Select
End Sub
Public Function _rnd(ByVal n As Integer) As Integer
'initialize random number generator
Dim r As New Random(System.DateTime.Now.Millisecond)
Return r.Next(1, n)
End Function
Private Sub cmdLogMe(message As String, _type As String)
Try
Dim _strLog As String = "adeewdf4-4aa34-14ass24-8436-aea1" 'LOG DASDATA DS KEY
lstEvenimente.Items.Add((System.DateTime.Now & " " & message) + Constants.vbNewLine)
'STORE LOGS TO DASDATA
_myDas.sendDas(message & "|" & _type, _strLog, _AKey)
Catch ex As Exception
cmdLogMe("ERROR..." & ex.Message.ToString(), "error")
End Try
End Sub
End Class
Source : DasFilisera Green House