Jules EVRARDNicolas DAILLYfcaronAghilasse OUALI
Published © Apache-2.0

Bloom Hub : Connected Greenhouse

It allows to manage easily our greenhouse.

IntermediateShowcase (no instructions)Over 2 days77
Bloom Hub : Connected Greenhouse

Things used in this project

Hardware components

Arduino MKR WAN 1310
Arduino MKR WAN 1310
This mkrWan card allows us to connect on a lora wan network and to rely all componants on.
×1
Gravity: Analog Soil Moisture Sensor For Arduino
DFRobot Gravity: Analog Soil Moisture Sensor For Arduino
This soil moisture sensor is used to detect the humidity and to know if the plant needs water.
×1
DHT11 Temperature & Humidity Sensor (3 pins)
DHT11 Temperature & Humidity Sensor (3 pins)
This sensor is used to detect the air humidity and the temperature.
×1
Axial Fan, 5 V
Axial Fan, 5 V
The fan allows to reduce the temperature when it's too high and give some air to the plant.
×1
SparkFun Qwiic Single Relay
SparkFun Qwiic Single Relay
×2
water pump
The water pump gives some water for the plant if it's dry.
×1
led
×1
IoT Training Controller Light Sound Sensor Action
National Control Devices IoT Training Controller Light Sound Sensor Action
×1
light sensor
×1
Arduino Dipole Pentaband Waterproof Antenna
×1

Software apps and online services

Arduino Anduino IDE
Xcode
Apple Xcode
InfluxDB
The Things Stack
The Things Industries The Things Stack
VMWare Workstation
Node-RED
Node-RED

Hand tools and fabrication machines

3D Printer (generic)
3D Printer (generic)

Story

Read more

Custom parts and enclosures

Pole

It's a pole that we have printed four times to rely the four plexiglass walls and maintain the plexiglass roof.

Code

mkrwan_code

C/C++
Thanks to this code you can find which PIN we have connected the different sensors/actuators to.
#include <MKRWAN.h>
#include "DHT.h"

// Déclaration des broches pour les capteurs
#define solPin A1 // Port de connexion du capteur d'humidité du sol
#define lumPIN A2 // Port de connexion du capteur de luminosité
#define DHTPIN 2   // Broche digitale connectée au capteur de température/humidité
#define pompePin 4
#define ventiPin 3

// Déclaration du type de capteur DHT
#define DHTTYPE DHT11   // DHT 11

// Déclaration des objets pour les capteurs
DHT dht(DHTPIN, DHTTYPE);

LoRaModem modem;

String appEui = "EDF6FF626EE36A8B";
String appKey = "FAFB287E5A8C7A1B0C25DF337D197F08";

void setup() {
  Serial.begin(115200);
  while (!Serial);

  if (!modem.begin(EU868)) {
    Serial.println("Failed to start module");
    while (1) {}
  };

  int connected = modem.joinOTAA(appEui, appKey);
  if (!connected) {
    Serial.println("Something went wrong; are you indoor? Move near a window and retry");
    while (1) {}
  }
  
  // Initialize DHT sensor
  dht.begin();

  pinMode(solPin, INPUT);
  pinMode(pompePin, OUTPUT);
  pinMode(ventiPin, OUTPUT);

  // Set poll interval to 60 secs.
  modem.minPollInterval(60);
}

void loop() {
  byte data[]={0,1,2,3};

  data[0] = dht.readHumidity();   // Lecture de l'humidité de l'air
  data[1] = dht.readTemperature();  // Lecture de la temperature
  data[2] = analogRead(solPin);  // Lecture de l'humidité du sol
  data[3] = analogRead(lumPIN);  // Lecture de la luminosité

  Serial.println("Humidite (air) : " + String(data[0]) + "\nTemperature : " + String(data[1]) + "\nHumidite (sol) : " + String(data[2]) +  "\nLuminosite : " + String(data[3]));

  // Envoi des données via LoRa
  int err;
  modem.beginPacket();
  modem.write(data,sizeof(data));
  
  err = modem.endPacket(true);

  // Gestion des erreurs d'envoi
  if (err > 0) {
    Serial.println("Message sent correctly!");
  } else {
    Serial.println("Error sending message :(");
    Serial.println("(you may send a limited amount of messages per minute, depending on the signal strength");
    Serial.println("it may vary from 1 message every couple of seconds to 1 message every minute)");
  }

  delay(10000);

  if (!modem.available()) {
    Serial.println("No downlink message received at this time.");
    return;
  }
  char rcv[64];
  int i = 0;
  while (modem.available()) {
    rcv[i++] = (char)modem.read();
  }

  int actionneur = (int)rcv[0];
  int onoff = (int)rcv[1];
  
  Serial.print("Received: ");
  for (unsigned int j = 0; j < i; j++) {
    // Convertir chaque byte directement en entier
    int value = (int)rcv[j];
    Serial.print(value);
    Serial.print(" ");
  }
  Serial.println();

  if(actionneur == 1) {
    Serial.print("Ventilateur : ");
    if(onoff == 0) {
      Serial.print("OFF\n");
      digitalWrite(ventiPin, LOW);
    } else {
      Serial.print("ON\n");
      digitalWrite(ventiPin, HIGH);
    }
  }
  if(actionneur == 2) {
    Serial.print("Pompe : ");
    if(onoff == 0) {
      Serial.print("OFF\n");
      digitalWrite(pompePin, LOW);
    } else {
      Serial.print("ON\n");
      digitalWrite(pompePin, HIGH);
    }
  }
}

app_iOS

Swift
This is the codes for create the mobile app of this projet, but this app isn't finish, they are juste design of "Home Page"
No preview (download only).

node-red_code

JSON
[
    {
        "id": "57f75a3b6c5ae1f6",
        "type": "tab",
        "label": "UpLink",
        "disabled": true,
        "info": "",
        "env": []
    },
    {
        "id": "c2eed093f9eeab3c",
        "type": "tab",
        "label": "DownLink",
        "disabled": true,
        "info": "",
        "env": []
    },
    {
        "id": "4ae4cdca593b3adc",
        "type": "tab",
        "label": "Test_InfluxBD",
        "disabled": false,
        "info": "",
        "env": []
    },
    {
        "id": "15483bae4a781c02",
        "type": "tab",
        "label": "Flux 4",
        "disabled": false,
        "info": "",
        "env": []
    },
    {
        "id": "34326d421022e9e0",
        "type": "mqtt-broker",
        "name": "",
        "broker": "eu1.cloud.thethings.network",
        "port": "1883",
        "clientid": "",
        "autoConnect": true,
        "usetls": false,
        "protocolVersion": "4",
        "keepalive": "60",
        "cleansession": true,
        "autoUnsubscribe": true,
        "birthTopic": "",
        "birthQos": "0",
        "birthRetain": "false",
        "birthPayload": "",
        "birthMsg": {},
        "closeTopic": "",
        "closeQos": "0",
        "closeRetain": "false",
        "closePayload": "",
        "closeMsg": {},
        "willTopic": "",
        "willQos": "0",
        "willRetain": "false",
        "willPayload": "",
        "willMsg": {},
        "userProps": "",
        "sessionExpiry": ""
    },
    {
        "id": "0f6933e94f26f46a",
        "type": "influxdb",
        "hostname": "YOUR_IP",
        "port": "8086",
        "protocol": "http",
        "database": "database",
        "name": "Influx DB",
        "usetls": false,
        "tls": "",
        "influxdbVersion": "2.0",
        "url": "YOUR_URL",
        "timeout": "10",
        "rejectUnauthorized": true
    },
    {
        "id": "13153bb5f9c12352",
        "type": "mqtt in",
        "z": "57f75a3b6c5ae1f6",
        "name": "",
        "topic": "#",
        "qos": "2",
        "datatype": "json",
        "broker": "34326d421022e9e0",
        "nl": false,
        "rap": true,
        "rh": 0,
        "inputs": 0,
        "x": 490,
        "y": 180,
        "wires": [
            [
                "1b25dd88db264a7f",
                "e9056d4594084b97"
            ]
        ]
    },
    {
        "id": "1b25dd88db264a7f",
        "type": "debug",
        "z": "57f75a3b6c5ae1f6",
        "name": "debug message.payload",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "payload",
        "targetType": "msg",
        "statusVal": "",
        "statusType": "auto",
        "x": 870,
        "y": 120,
        "wires": []
    },
    {
        "id": "e9056d4594084b97",
        "type": "debug",
        "z": "57f75a3b6c5ae1f6",
        "name": "debug msg",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "payload",
        "targetType": "msg",
        "statusVal": "",
        "statusType": "auto",
        "x": 830,
        "y": 240,
        "wires": []
    },
    {
        "id": "75eaaea178698596",
        "type": "mqtt out",
        "z": "c2eed093f9eeab3c",
        "name": "",
        "topic": "v3/{YOUR_NAME@ttn}/devices/{YOUR_TOKEN}/down/push",
        "qos": "",
        "retain": "",
        "respTopic": "",
        "contentType": "",
        "userProps": "",
        "correl": "",
        "expiry": "",
        "broker": "34326d421022e9e0",
        "x": 1010,
        "y": 360,
        "wires": []
    },
    {
        "id": "99798bfec09f519a",
        "type": "inject",
        "z": "c2eed093f9eeab3c",
        "name": "",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": true,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "[0,42,255,0]",
        "payloadType": "bin",
        "x": 360,
        "y": 280,
        "wires": [
            [
                "a0da53aaf7d70249"
            ]
        ]
    },
    {
        "id": "a0da53aaf7d70249",
        "type": "function",
        "z": "c2eed093f9eeab3c",
        "name": "function 1",
        "func": "return {\n    \"payload\": {\n        \"downlinks\": [{\n            \"f_port\": 15,\n            \"frm_payload\": msg.payload.toString(\"base64\"),\n            \"priority\": \"NORMAL\"\n        }]\n    }\n}",
        "outputs": 1,
        "timeout": 0,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 620,
        "y": 320,
        "wires": [
            [
                "fe789f8475066678",
                "75eaaea178698596"
            ]
        ]
    },
    {
        "id": "fe789f8475066678",
        "type": "debug",
        "z": "c2eed093f9eeab3c",
        "name": "msg.payload",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "payload",
        "targetType": "msg",
        "statusVal": "",
        "statusType": "auto",
        "x": 870,
        "y": 240,
        "wires": []
    },
    {
        "id": "db58a8d72c2abd4b",
        "type": "change",
        "z": "4ae4cdca593b3adc",
        "name": "",
        "rules": [
            {
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "",
                "tot": "str"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 250,
        "y": 120,
        "wires": [
            []
        ]
    },
    {
        "id": "e1a05b872d603976",
        "type": "influxdb out",
        "z": "4ae4cdca593b3adc",
        "influxdb": "0f6933e94f26f46a",
        "name": "Injection",
        "measurement": "mesure",
        "precision": "",
        "retentionPolicy": "",
        "database": "database",
        "precisionV18FluxV20": "ms",
        "retentionPolicyV18Flux": "",
        "org": "YOUR ORGANIZATION",
        "bucket": "YOUR BUCKET",
        "x": 860,
        "y": 340,
        "wires": []
    },
    {
        "id": "7a29e69ca9209197",
        "type": "function",
        "z": "4ae4cdca593b3adc",
        "name": "function 2",
        "func": "msg.payload=[{toto:20,tata:5},{bonjour:\"pomme\"}];\nreturn msg;",
        "outputs": 1,
        "timeout": 0,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 620,
        "y": 380,
        "wires": [
            [
                "e1a05b872d603976"
            ]
        ]
    },
    {
        "id": "1b58b4efd5ba5607",
        "type": "inject",
        "z": "4ae4cdca593b3adc",
        "name": "",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "",
        "payloadType": "date",
        "x": 290,
        "y": 340,
        "wires": [
            [
                "7a29e69ca9209197"
            ]
        ]
    },
    {
        "id": "c9a0ae963aa84b53",
        "type": "mqtt in",
        "z": "15483bae4a781c02",
        "name": "",
        "topic": "#",
        "qos": "2",
        "datatype": "json",
        "broker": "34326d421022e9e0",
        "nl": false,
        "rap": true,
        "rh": 0,
        "inputs": 0,
        "x": 290,
        "y": 380,
        "wires": [
            [
                "3625e67435bc0e17"
            ]
        ]
    },
    {
        "id": "3625e67435bc0e17",
        "type": "json",
        "z": "15483bae4a781c02",
        "name": "",
        "property": "payload",
        "action": "obj",
        "pretty": false,
        "x": 510,
        "y": 380,
        "wires": [
            [
                "bb23db23c0412ce6",
                "4a97f127a5a2f88a"
            ]
        ]
    },
    {
        "id": "bb23db23c0412ce6",
        "type": "function",
        "z": "15483bae4a781c02",
        "name": "function 3",
        "func": "if (msg.payload.uplink_message && msg.payload.uplink_message.decoded_payload) {\n    var luminosite = msg.payload.uplink_message.decoded_payload.Luminosite;\n    var humiditeAir = msg.payload.uplink_message.decoded_payload.humuditeAir;\n    var humiditeSol = msg.payload.uplink_message.decoded_payload.humuditeSol;\n    var temperature = msg.payload.uplink_message.decoded_payload.temperature;\n\n    msg.payload = [{luminosite:luminosite, humiditeAir:humiditeAir, humiditeSol:humiditeSol, temperature:temperature}, {bonjour:\"pomme\"}];\n\n} else {\n    // Si les données nécessaires ne sont pas présentes, log un message d'erreur ou manipuler autrement\n    node.warn(\"decoded_payload ou uplink_message manquant dans le payload\");\n    return null; // Stopper l'exécution pour ce message\n}\n\nreturn msg;",
        "outputs": 1,
        "timeout": 0,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 720,
        "y": 380,
        "wires": [
            [
                "9475b628a48babbb"
            ]
        ]
    },
    {
        "id": "9475b628a48babbb",
        "type": "influxdb out",
        "z": "15483bae4a781c02",
        "influxdb": "0f6933e94f26f46a",
        "name": "Injection",
        "measurement": "mesure",
        "precision": "",
        "retentionPolicy": "",
        "database": "database",
        "precisionV18FluxV20": "ms",
        "retentionPolicyV18Flux": "",
        "org": "YOUR ORGANIZATION",
        "bucket": "YOUR BUCKET",
        "x": 920,
        "y": 380,
        "wires": []
    },
    {
        "id": "4a97f127a5a2f88a",
        "type": "debug",
        "z": "15483bae4a781c02",
        "name": "debug 1",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "payload",
        "targetType": "msg",
        "statusVal": "",
        "statusType": "auto",
        "x": 860,
        "y": 500,
        "wires": []
    }
]

Credits

Jules EVRARD
1 project • 0 followers
Nicolas DAILLY
41 projects • 26 followers
Associated Professor at UniLaSalle - Amiens / Head of the Computer Network Department / Teach Computer and Telecommunication Networks
fcaron
22 projects • 7 followers
Aghilasse OUALI
1 project • 0 followers

Comments