mirror of
https://github.com/SasaKaranovic/HousePlantMonitoringSystem.git
synced 2026-07-08 17:52:37 +02:00
@@ -1,3 +1,4 @@
|
||||
#include <EEPROM.h>
|
||||
#include "WiFi.h"
|
||||
#include "ESPAsyncWebServer.h"
|
||||
#include <esp_task_wdt.h>
|
||||
@@ -7,12 +8,17 @@
|
||||
#include "Wire.h"
|
||||
|
||||
#define WDT_TIMEOUT 20000
|
||||
#define EEPROM_SIZE 4
|
||||
|
||||
AsyncWebServer server(80);
|
||||
int WiFi_status = WL_IDLE_STATUS;
|
||||
volatile uint32_t nFlowSensorCount = 0;
|
||||
volatile uint32_t nFlowSensorCount_last = 0;
|
||||
|
||||
char dbgBuff[4024] = {0};
|
||||
uint32_t dbgBuffPos = 0;
|
||||
float fImpulsePerML = 0;
|
||||
|
||||
void IRAM_ATTR ISR_flowSensor() {
|
||||
nFlowSensorCount++;
|
||||
}
|
||||
@@ -37,6 +43,21 @@ void setup()
|
||||
|
||||
Wire.begin(I2C_SDA_PIN, I2C_SCL_PIN);
|
||||
|
||||
// EEPROM begin
|
||||
EEPROM.begin(EEPROM_SIZE);
|
||||
|
||||
// Debug EEPROM before read
|
||||
Serial.println("Before read: fImpulsePerML");
|
||||
Serial.println((float)(fImpulsePerML),4);
|
||||
|
||||
// Read flow value from EEPROM
|
||||
fImpulsePerML = EEPROM.readFloat(0);
|
||||
|
||||
// Debug EEPROM after read
|
||||
Serial.println("After read: fImpulsePerML");
|
||||
Serial.println((float)(fImpulsePerML),4);
|
||||
|
||||
|
||||
// Configure WDT
|
||||
Serial.println("Configuring WDT...");
|
||||
esp_task_wdt_init(WDT_TIMEOUT, true); //enable panic so ESP32 restarts
|
||||
@@ -61,7 +82,20 @@ void setup()
|
||||
setupWebServer();
|
||||
server.begin();
|
||||
|
||||
attachInterrupt(FLOW_SENSOR_PIN, ISR_flowSensor, FALLING);
|
||||
flowInterruptEnabled(true);
|
||||
}
|
||||
|
||||
|
||||
void flowInterruptEnabled(bool state)
|
||||
{
|
||||
if(state)
|
||||
{
|
||||
attachInterrupt(FLOW_SENSOR_PIN, ISR_flowSensor, FALLING);
|
||||
}
|
||||
else
|
||||
{
|
||||
detachInterrupt(FLOW_SENSOR_PIN);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -257,5 +291,48 @@ void setupWebServer(void)
|
||||
request->send(200, "text/plain", "OK");
|
||||
return;
|
||||
});
|
||||
|
||||
// Calibrate flow sensor
|
||||
server.on("/calibrateFlowSensor", HTTP_GET, [] (AsyncWebServerRequest *request) {
|
||||
Serial.println("Calibrate edges per mL");
|
||||
|
||||
if ( request->hasParam("edges") )
|
||||
{
|
||||
String xEdges;
|
||||
float fEdges = 0;
|
||||
|
||||
xEdges = request->getParam("edges")->value();
|
||||
fEdges = xEdges.toFloat();
|
||||
|
||||
// Update value
|
||||
fImpulsePerML = fEdges;
|
||||
EEPROM.writeFloat(0, fEdges);//EEPROM.put(address, param);
|
||||
EEPROM.commit();
|
||||
|
||||
Serial.print("Changing fImpulsePerML to ");
|
||||
Serial.println((float)(fImpulsePerML),4);
|
||||
|
||||
request->send(200, "text/plain", "OK");
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
request->send(404, "text/plain", "MISSING_ARGUMENT");
|
||||
return;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
void LED_Blink(uint8_t nTimes, uint16_t n_delayPeriod)
|
||||
{
|
||||
while(nTimes--)
|
||||
{
|
||||
digitalWrite(LED_PIN, HIGH);
|
||||
delay(n_delayPeriod);
|
||||
|
||||
digitalWrite(LED_PIN, LOW);
|
||||
delay(n_delayPeriod);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ uint8_t gnSolenoidsOnBusCnt = 0;
|
||||
// Plant watering definitions
|
||||
#define WATERING_VOLUME_MINIMUM 10
|
||||
#define WATERING_VOLUME_MAXIMUM 550
|
||||
#define WATERING_FLOW_EDGES_PER_ML 4
|
||||
#define WATERING_FLOW_EDGES_PER_L 5880 //98
|
||||
|
||||
// PlantSystem_tick periods
|
||||
#define PERIOD_RESCAN_BUS 24*3600*1000 // Every 24h
|
||||
@@ -66,6 +66,7 @@ uint32_t nTimeout = 0;
|
||||
|
||||
void PlantSystem_tick(void)
|
||||
{
|
||||
bool bWateringError = false;
|
||||
esp_task_wdt_reset();
|
||||
|
||||
// -- Rescan I2C bus
|
||||
@@ -124,15 +125,23 @@ void PlantSystem_tick(void)
|
||||
// -- Process watering request
|
||||
if(bWatering_requestPending || bWatering_inProgress)
|
||||
{
|
||||
// Watering logic
|
||||
// Pump creates high pressure inside watering system. Because of this
|
||||
// at the beginning of watering process we will first, open valve, then turn on pump.
|
||||
// When we are done, we will first turn off pump and then solenoid.
|
||||
//
|
||||
// In previous version, water pump was too weak so we turned on the pump first to keep the pressure on
|
||||
// and prevent water from "backing up" (i.e letting air in from plant side).
|
||||
// With peristaltic pump we can open the solenoids first and still keep air out of the system.
|
||||
//
|
||||
|
||||
Serial.println("bWatering_requestPending | bWatering_inProgress");
|
||||
// Transition to in progress
|
||||
bWatering_inProgress = true;
|
||||
bWatering_requestPending = false;
|
||||
nFlowSensorCount = 0;
|
||||
|
||||
// Note: Solenoid address and volume must have been validated
|
||||
Serial.println("Turning pump ON");
|
||||
digitalWrite(WATER_PUMP_EN_PIN, HIGH);
|
||||
delay(200);
|
||||
// - Open solenoid
|
||||
Serial.println("Energizing solenoid");
|
||||
if (PlantSystem_SetSolenoidState(nWatering_i2cAddress, true) != true)
|
||||
{
|
||||
@@ -141,33 +150,84 @@ void PlantSystem_tick(void)
|
||||
digitalWrite(WATER_PUMP_EN_PIN, LOW);
|
||||
Serial.println("Turning pump OFF");
|
||||
Serial.println("Failed to open solenoid. Aborting...");
|
||||
LED_Blink(4, 250);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// - Reset flow counter
|
||||
nFlowSensorCount = 0;
|
||||
nFlowSensorCount_last = 0;
|
||||
flowInterruptEnabled(true);
|
||||
|
||||
// - Turn ON pump
|
||||
bWateringError = true;
|
||||
Serial.println("Turning pump ON");
|
||||
digitalWrite(WATER_PUMP_EN_PIN, HIGH);
|
||||
|
||||
esp_task_wdt_reset();
|
||||
nTimeout = millis() + 10000;
|
||||
while(nFlowSensorCount < (WATERING_FLOW_EDGES_PER_ML*nWatering_volume))
|
||||
nTimeout = millis() + 10000; // 10sec time-out for watering
|
||||
|
||||
// Calculate water flow
|
||||
uint32_t nWaterFlow_millis_timestamp = millis();
|
||||
float flowMilliLitres = 0.0;
|
||||
unsigned long totalMilliLitres = 0;
|
||||
|
||||
// Wait until target mL or timeout is reached
|
||||
while(millis() < nTimeout)
|
||||
{
|
||||
if(millis() > nTimeout)
|
||||
// Calculate mL every second
|
||||
if( millis() >= nWaterFlow_millis_timestamp)
|
||||
{
|
||||
break;
|
||||
// Disable interrupts
|
||||
flowInterruptEnabled(false);
|
||||
|
||||
// Update mL
|
||||
flowMilliLitres = nFlowSensorCount/fImpulsePerML;
|
||||
totalMilliLitres += flowMilliLitres;
|
||||
|
||||
// Check if target has been reached
|
||||
if(totalMilliLitres >= nWatering_volume)
|
||||
{
|
||||
bWateringError = false;
|
||||
break;
|
||||
}
|
||||
|
||||
// Update for next iteration
|
||||
nWaterFlow_millis_timestamp = millis() + 1000;
|
||||
nFlowSensorCount = 0;
|
||||
esp_task_wdt_reset();
|
||||
flowInterruptEnabled(true);
|
||||
}
|
||||
}
|
||||
esp_task_wdt_reset();
|
||||
|
||||
Serial.println("De-Energizing solenoid");
|
||||
PlantSystem_SetSolenoidState(nWatering_i2cAddress, false);
|
||||
delay(200);
|
||||
// - Turn OFF pump
|
||||
digitalWrite(WATER_PUMP_EN_PIN, LOW);
|
||||
Serial.println("Turning pump OFF");
|
||||
delay(200);
|
||||
|
||||
// - Close solenoid
|
||||
Serial.println("De-Energizing solenoid");
|
||||
PlantSystem_SetSolenoidState(nWatering_i2cAddress, false);
|
||||
|
||||
|
||||
// Store last flow data and prepare for new request
|
||||
nFlowSensorCount_last = nFlowSensorCount;
|
||||
nFlowSensorCount_last = 0;
|
||||
nFlowSensorCount = 0;
|
||||
nWatering_volume = 0;
|
||||
|
||||
// Reset request
|
||||
bWatering_inProgress = false;
|
||||
bWatering_requestPending = false;
|
||||
if(bWateringError)
|
||||
{
|
||||
LED_Blink(4, 250);
|
||||
}
|
||||
else
|
||||
{
|
||||
LED_Blink(2, 500);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -610,10 +670,8 @@ void PlantSystem_RequestPrimeSystem(void)
|
||||
|
||||
bool PlantSystem_PrimeSystemWithWater(void)
|
||||
{
|
||||
// Turn ON Pump
|
||||
Serial.println("Turning pump ON");
|
||||
digitalWrite(WATER_PUMP_EN_PIN, HIGH);
|
||||
delay(1000);
|
||||
|
||||
bool bSolenoidOpen = false;
|
||||
|
||||
// Turn ON all solenoids
|
||||
Serial.println("Energizing ALL solenoids");
|
||||
@@ -623,6 +681,7 @@ bool PlantSystem_PrimeSystemWithWater(void)
|
||||
{
|
||||
Serial.print("Openned solenoid 0x");
|
||||
Serial.println(gSolenoidsOnBus[i].i2cAddress, HEX);
|
||||
bSolenoidOpen = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -630,11 +689,25 @@ bool PlantSystem_PrimeSystemWithWater(void)
|
||||
Serial.println(gSolenoidsOnBus[i].i2cAddress, HEX);
|
||||
}
|
||||
delay(500);
|
||||
|
||||
// Only turn on pump AFTER solenoid has oppened
|
||||
if(bSolenoidOpen)
|
||||
{
|
||||
// Turn ON Pump
|
||||
Serial.println("Turning pump ON");
|
||||
digitalWrite(WATER_PUMP_EN_PIN, HIGH);
|
||||
delay(1000);
|
||||
}
|
||||
}
|
||||
|
||||
// Let the water flow for a while
|
||||
delay(5000);
|
||||
|
||||
// Turn OFF pump
|
||||
delay(1000);
|
||||
Serial.println("Turning pump OFF");
|
||||
digitalWrite(WATER_PUMP_EN_PIN, LOW);
|
||||
|
||||
// Turn OFF all solenoids
|
||||
Serial.println("De-energizing ALL solenoids");
|
||||
for(uint8_t i=0; i<gnSolenoidsOnBusCnt; i++)
|
||||
@@ -653,11 +726,6 @@ bool PlantSystem_PrimeSystemWithWater(void)
|
||||
}
|
||||
|
||||
|
||||
// Turn OFF pump
|
||||
delay(1000);
|
||||
Serial.println("Turning pump OFF");
|
||||
digitalWrite(WATER_PUMP_EN_PIN, LOW);
|
||||
|
||||
nFlowSensorCount = 0;
|
||||
return true;
|
||||
|
||||
|
||||
@@ -0,0 +1,632 @@
|
||||
{
|
||||
"annotations": {
|
||||
"list": [
|
||||
{
|
||||
"builtIn": 1,
|
||||
"datasource": "-- Grafana --",
|
||||
"enable": true,
|
||||
"hide": true,
|
||||
"iconColor": "rgba(0, 211, 255, 1)",
|
||||
"name": "Annotations & Alerts",
|
||||
"type": "dashboard"
|
||||
}
|
||||
]
|
||||
},
|
||||
"editable": true,
|
||||
"gnetId": null,
|
||||
"graphTooltip": 0,
|
||||
"id": 9,
|
||||
"iteration": 1634868145960,
|
||||
"links": [],
|
||||
"panels": [
|
||||
{
|
||||
"aliasColors": {},
|
||||
"bars": false,
|
||||
"dashLength": 10,
|
||||
"dashes": false,
|
||||
"datasource": "MySQL - Plant System",
|
||||
"description": "",
|
||||
"fieldConfig": {
|
||||
"defaults": {},
|
||||
"overrides": []
|
||||
},
|
||||
"fill": 0,
|
||||
"fillGradient": 0,
|
||||
"gridPos": {
|
||||
"h": 11,
|
||||
"w": 24,
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"hiddenSeries": false,
|
||||
"id": 2,
|
||||
"legend": {
|
||||
"alignAsTable": true,
|
||||
"avg": true,
|
||||
"current": true,
|
||||
"max": true,
|
||||
"min": true,
|
||||
"rightSide": true,
|
||||
"show": true,
|
||||
"total": false,
|
||||
"values": true
|
||||
},
|
||||
"lines": true,
|
||||
"linewidth": 1,
|
||||
"nullPointMode": "null",
|
||||
"options": {
|
||||
"alertThreshold": true
|
||||
},
|
||||
"percentage": false,
|
||||
"pluginVersion": "7.5.4",
|
||||
"pointradius": 2,
|
||||
"points": true,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [],
|
||||
"spaceLength": 10,
|
||||
"stack": false,
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"format": "time_series",
|
||||
"group": [],
|
||||
"metricColumn": "sensorID",
|
||||
"rawQuery": true,
|
||||
"rawSql": "SELECT\r\n UNIX_TIMESTAMP(`insertTime`) AS `time`,\r\n `sensor_links`.`plantName` AS `metric`,\r\n `sensor_data`.`T` as `value` \r\nFROM `sensor_data`\r\nLEFT JOIN `sensor_links` ON\r\n`sensor_data`.`address` = `sensor_links`.`sensorAddress`\r\nWHERE $__timeFilter(`insertTime`)",
|
||||
"refId": "Temperature",
|
||||
"select": [
|
||||
[
|
||||
{
|
||||
"params": [
|
||||
"id"
|
||||
],
|
||||
"type": "column"
|
||||
}
|
||||
]
|
||||
],
|
||||
"table": "sensor_data",
|
||||
"timeColumn": "timestamp",
|
||||
"timeColumnType": "int",
|
||||
"where": [
|
||||
{
|
||||
"name": "$__unixEpochFilter",
|
||||
"params": [],
|
||||
"type": "macro"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"thresholds": [],
|
||||
"timeFrom": null,
|
||||
"timeRegions": [],
|
||||
"timeShift": null,
|
||||
"title": "Temperature",
|
||||
"tooltip": {
|
||||
"shared": true,
|
||||
"sort": 0,
|
||||
"value_type": "individual"
|
||||
},
|
||||
"transformations": [
|
||||
{
|
||||
"id": "seriesToColumns",
|
||||
"options": {
|
||||
"byField": "Time"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "organize",
|
||||
"options": {
|
||||
"excludeByName": {},
|
||||
"indexByName": {
|
||||
"Cactus Large": 2,
|
||||
"Cactus Small": 4,
|
||||
"Palm Tree": 3,
|
||||
"Pineapple": 1,
|
||||
"Time": 0
|
||||
},
|
||||
"renameByName": {}
|
||||
}
|
||||
}
|
||||
],
|
||||
"transparent": true,
|
||||
"type": "graph",
|
||||
"xaxis": {
|
||||
"buckets": null,
|
||||
"mode": "time",
|
||||
"name": null,
|
||||
"show": true,
|
||||
"values": []
|
||||
},
|
||||
"yaxes": [
|
||||
{
|
||||
"$$hashKey": "object:66",
|
||||
"decimals": 1,
|
||||
"format": "celsius",
|
||||
"label": "",
|
||||
"logBase": 1,
|
||||
"max": "55",
|
||||
"min": "20",
|
||||
"show": true
|
||||
},
|
||||
{
|
||||
"$$hashKey": "object:67",
|
||||
"format": "short",
|
||||
"label": null,
|
||||
"logBase": 1,
|
||||
"max": null,
|
||||
"min": null,
|
||||
"show": false
|
||||
}
|
||||
],
|
||||
"yaxis": {
|
||||
"align": false,
|
||||
"alignLevel": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"aliasColors": {},
|
||||
"bars": false,
|
||||
"dashLength": 10,
|
||||
"dashes": false,
|
||||
"datasource": "MySQL - Plant System",
|
||||
"description": "",
|
||||
"fieldConfig": {
|
||||
"defaults": {},
|
||||
"overrides": []
|
||||
},
|
||||
"fill": 0,
|
||||
"fillGradient": 0,
|
||||
"gridPos": {
|
||||
"h": 10,
|
||||
"w": 24,
|
||||
"x": 0,
|
||||
"y": 11
|
||||
},
|
||||
"hiddenSeries": false,
|
||||
"id": 3,
|
||||
"legend": {
|
||||
"alignAsTable": true,
|
||||
"avg": true,
|
||||
"current": true,
|
||||
"max": true,
|
||||
"min": true,
|
||||
"rightSide": true,
|
||||
"show": true,
|
||||
"total": false,
|
||||
"values": true
|
||||
},
|
||||
"lines": true,
|
||||
"linewidth": 1,
|
||||
"nullPointMode": "null",
|
||||
"options": {
|
||||
"alertThreshold": true
|
||||
},
|
||||
"percentage": false,
|
||||
"pluginVersion": "7.5.4",
|
||||
"pointradius": 2,
|
||||
"points": true,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [],
|
||||
"spaceLength": 10,
|
||||
"stack": false,
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"format": "time_series",
|
||||
"group": [],
|
||||
"metricColumn": "sensorID",
|
||||
"rawQuery": true,
|
||||
"rawSql": "SELECT\r\n UNIX_TIMESTAMP(`insertTime`) AS `time`,\r\n `sensor_links`.`plantName` AS metric,\r\n (4095-`sensor_data`.`M`) AS `value`\r\nFROM `sensor_data`\r\nLEFT JOIN `sensor_links` ON\r\n`sensor_data`.`address` = `sensor_links`.`sensorAddress`\r\nWHERE $__timeFilter(`insertTime`)",
|
||||
"refId": "Soil Moisture",
|
||||
"select": [
|
||||
[
|
||||
{
|
||||
"params": [
|
||||
"id"
|
||||
],
|
||||
"type": "column"
|
||||
}
|
||||
]
|
||||
],
|
||||
"table": "sensor_data",
|
||||
"timeColumn": "timestamp",
|
||||
"timeColumnType": "int",
|
||||
"where": [
|
||||
{
|
||||
"name": "$__unixEpochFilter",
|
||||
"params": [],
|
||||
"type": "macro"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"thresholds": [],
|
||||
"timeFrom": null,
|
||||
"timeRegions": [],
|
||||
"timeShift": null,
|
||||
"title": "Soil Moisture",
|
||||
"tooltip": {
|
||||
"shared": true,
|
||||
"sort": 0,
|
||||
"value_type": "individual"
|
||||
},
|
||||
"transformations": [
|
||||
{
|
||||
"id": "seriesToColumns",
|
||||
"options": {
|
||||
"byField": "Time"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "organize",
|
||||
"options": {
|
||||
"excludeByName": {},
|
||||
"indexByName": {
|
||||
"Cactus Large": 2,
|
||||
"Cactus Small": 4,
|
||||
"Palm Tree": 3,
|
||||
"Pineapple": 1,
|
||||
"Time": 0
|
||||
},
|
||||
"renameByName": {}
|
||||
}
|
||||
}
|
||||
],
|
||||
"transparent": true,
|
||||
"type": "graph",
|
||||
"xaxis": {
|
||||
"buckets": null,
|
||||
"mode": "time",
|
||||
"name": null,
|
||||
"show": true,
|
||||
"values": []
|
||||
},
|
||||
"yaxes": [
|
||||
{
|
||||
"$$hashKey": "object:66",
|
||||
"decimals": 0,
|
||||
"format": "none",
|
||||
"label": "",
|
||||
"logBase": 1,
|
||||
"max": null,
|
||||
"min": null,
|
||||
"show": true
|
||||
},
|
||||
{
|
||||
"$$hashKey": "object:67",
|
||||
"format": "short",
|
||||
"label": null,
|
||||
"logBase": 1,
|
||||
"max": null,
|
||||
"min": null,
|
||||
"show": false
|
||||
}
|
||||
],
|
||||
"yaxis": {
|
||||
"align": false,
|
||||
"alignLevel": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"aliasColors": {},
|
||||
"bars": false,
|
||||
"dashLength": 10,
|
||||
"dashes": false,
|
||||
"datasource": "MySQL - Plant System",
|
||||
"description": "",
|
||||
"fieldConfig": {
|
||||
"defaults": {},
|
||||
"overrides": []
|
||||
},
|
||||
"fill": 0,
|
||||
"fillGradient": 0,
|
||||
"gridPos": {
|
||||
"h": 10,
|
||||
"w": 24,
|
||||
"x": 0,
|
||||
"y": 21
|
||||
},
|
||||
"hiddenSeries": false,
|
||||
"id": 4,
|
||||
"legend": {
|
||||
"alignAsTable": true,
|
||||
"avg": true,
|
||||
"current": true,
|
||||
"max": true,
|
||||
"min": true,
|
||||
"rightSide": true,
|
||||
"show": true,
|
||||
"total": false,
|
||||
"values": true
|
||||
},
|
||||
"lines": true,
|
||||
"linewidth": 1,
|
||||
"nullPointMode": "null",
|
||||
"options": {
|
||||
"alertThreshold": true
|
||||
},
|
||||
"percentage": false,
|
||||
"pluginVersion": "7.5.4",
|
||||
"pointradius": 2,
|
||||
"points": true,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [],
|
||||
"spaceLength": 10,
|
||||
"stack": false,
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"format": "time_series",
|
||||
"group": [],
|
||||
"metricColumn": "sensorID",
|
||||
"rawQuery": true,
|
||||
"rawSql": "SELECT\r\n UNIX_TIMESTAMP(`insertTime`) AS `time`,\r\n `sensor_links`.`plantName` AS metric,\r\n (4095-`sensor_data`.`A`) AS `value`\r\nFROM `sensor_data`\r\nLEFT JOIN `sensor_links` ON\r\n`sensor_data`.`address` = `sensor_links`.`sensorAddress`\r\nWHERE $__timeFilter(`insertTime`)",
|
||||
"refId": "Soil Moisture",
|
||||
"select": [
|
||||
[
|
||||
{
|
||||
"params": [
|
||||
"id"
|
||||
],
|
||||
"type": "column"
|
||||
}
|
||||
]
|
||||
],
|
||||
"table": "sensor_data",
|
||||
"timeColumn": "timestamp",
|
||||
"timeColumnType": "int",
|
||||
"where": [
|
||||
{
|
||||
"name": "$__unixEpochFilter",
|
||||
"params": [],
|
||||
"type": "macro"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"thresholds": [],
|
||||
"timeFrom": null,
|
||||
"timeRegions": [],
|
||||
"timeShift": null,
|
||||
"title": "Ambient Light",
|
||||
"tooltip": {
|
||||
"shared": true,
|
||||
"sort": 0,
|
||||
"value_type": "individual"
|
||||
},
|
||||
"transformations": [
|
||||
{
|
||||
"id": "seriesToColumns",
|
||||
"options": {
|
||||
"byField": "Time"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "organize",
|
||||
"options": {
|
||||
"excludeByName": {},
|
||||
"indexByName": {
|
||||
"Cactus Large": 2,
|
||||
"Cactus Small": 4,
|
||||
"Palm Tree": 3,
|
||||
"Pineapple": 1,
|
||||
"Time": 0
|
||||
},
|
||||
"renameByName": {}
|
||||
}
|
||||
}
|
||||
],
|
||||
"transparent": true,
|
||||
"type": "graph",
|
||||
"xaxis": {
|
||||
"buckets": null,
|
||||
"mode": "time",
|
||||
"name": null,
|
||||
"show": true,
|
||||
"values": []
|
||||
},
|
||||
"yaxes": [
|
||||
{
|
||||
"$$hashKey": "object:66",
|
||||
"decimals": 0,
|
||||
"format": "none",
|
||||
"label": "",
|
||||
"logBase": 1,
|
||||
"max": null,
|
||||
"min": null,
|
||||
"show": true
|
||||
},
|
||||
{
|
||||
"$$hashKey": "object:67",
|
||||
"format": "short",
|
||||
"label": null,
|
||||
"logBase": 1,
|
||||
"max": null,
|
||||
"min": null,
|
||||
"show": false
|
||||
}
|
||||
],
|
||||
"yaxis": {
|
||||
"align": false,
|
||||
"alignLevel": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"aliasColors": {},
|
||||
"bars": false,
|
||||
"dashLength": 10,
|
||||
"dashes": false,
|
||||
"datasource": "MySQL - Plant System",
|
||||
"fieldConfig": {
|
||||
"defaults": {},
|
||||
"overrides": []
|
||||
},
|
||||
"fill": 0,
|
||||
"fillGradient": 0,
|
||||
"gridPos": {
|
||||
"h": 9,
|
||||
"w": 24,
|
||||
"x": 0,
|
||||
"y": 31
|
||||
},
|
||||
"hiddenSeries": false,
|
||||
"id": 6,
|
||||
"legend": {
|
||||
"alignAsTable": true,
|
||||
"avg": true,
|
||||
"current": false,
|
||||
"max": true,
|
||||
"min": true,
|
||||
"rightSide": true,
|
||||
"show": true,
|
||||
"total": true,
|
||||
"values": true
|
||||
},
|
||||
"lines": false,
|
||||
"linewidth": 2,
|
||||
"nullPointMode": "null",
|
||||
"options": {
|
||||
"alertThreshold": false
|
||||
},
|
||||
"percentage": false,
|
||||
"pluginVersion": "7.5.4",
|
||||
"pointradius": 5,
|
||||
"points": true,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [],
|
||||
"spaceLength": 10,
|
||||
"stack": false,
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"format": "time_series",
|
||||
"group": [],
|
||||
"metricColumn": "none",
|
||||
"rawQuery": true,
|
||||
"rawSql": "SELECT\r\n UNIX_TIMESTAMP(`timestamp`) AS `time`,\r\n `sensor_links`.`plantName` AS metric,\r\n `waterVolume` AS `value`\r\nFROM `watering_data`\r\nLEFT JOIN `sensor_links` ON\r\n`watering_data`.`solenoidAddress` = `sensor_links`.`solenoidAddress`\r\nWHERE $__timeFilter(`timestamp`)",
|
||||
"refId": "A",
|
||||
"select": [
|
||||
[
|
||||
{
|
||||
"params": [
|
||||
"waterVolume"
|
||||
],
|
||||
"type": "column"
|
||||
}
|
||||
]
|
||||
],
|
||||
"table": "watering_data",
|
||||
"timeColumn": "timestamp",
|
||||
"timeColumnType": "datetime",
|
||||
"where": [
|
||||
{
|
||||
"name": "$__timeFilter",
|
||||
"params": [],
|
||||
"type": "macro"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"thresholds": [],
|
||||
"timeFrom": null,
|
||||
"timeRegions": [],
|
||||
"timeShift": null,
|
||||
"title": "Watering data",
|
||||
"tooltip": {
|
||||
"shared": true,
|
||||
"sort": 0,
|
||||
"value_type": "individual"
|
||||
},
|
||||
"transparent": true,
|
||||
"type": "graph",
|
||||
"xaxis": {
|
||||
"buckets": null,
|
||||
"mode": "time",
|
||||
"name": null,
|
||||
"show": true,
|
||||
"values": []
|
||||
},
|
||||
"yaxes": [
|
||||
{
|
||||
"$$hashKey": "object:756",
|
||||
"format": "mlitre",
|
||||
"label": null,
|
||||
"logBase": 1,
|
||||
"max": null,
|
||||
"min": null,
|
||||
"show": true
|
||||
},
|
||||
{
|
||||
"$$hashKey": "object:757",
|
||||
"format": "short",
|
||||
"label": null,
|
||||
"logBase": 1,
|
||||
"max": null,
|
||||
"min": null,
|
||||
"show": false
|
||||
}
|
||||
],
|
||||
"yaxis": {
|
||||
"align": false,
|
||||
"alignLevel": null
|
||||
}
|
||||
}
|
||||
],
|
||||
"refresh": "1m",
|
||||
"schemaVersion": 27,
|
||||
"style": "dark",
|
||||
"tags": [],
|
||||
"templating": {
|
||||
"list": [
|
||||
{
|
||||
"description": null,
|
||||
"error": null,
|
||||
"hide": 2,
|
||||
"label": "Sensor_0x10",
|
||||
"name": "Sensor_0x10",
|
||||
"query": "Cactus",
|
||||
"skipUrlSync": false,
|
||||
"type": "constant"
|
||||
},
|
||||
{
|
||||
"description": null,
|
||||
"error": null,
|
||||
"hide": 2,
|
||||
"label": "Sensor_0x11",
|
||||
"name": "Sensor_0x11",
|
||||
"query": "Palm Tree",
|
||||
"skipUrlSync": false,
|
||||
"type": "constant"
|
||||
},
|
||||
{
|
||||
"description": null,
|
||||
"error": null,
|
||||
"hide": 2,
|
||||
"label": "Sensor_0x12",
|
||||
"name": "Sensor_0x12",
|
||||
"query": "Avocado Tree",
|
||||
"skipUrlSync": false,
|
||||
"type": "constant"
|
||||
},
|
||||
{
|
||||
"description": null,
|
||||
"error": null,
|
||||
"hide": 2,
|
||||
"label": "Sensor_0x13",
|
||||
"name": "Sensor_0x13",
|
||||
"query": "Rose",
|
||||
"skipUrlSync": false,
|
||||
"type": "constant"
|
||||
}
|
||||
]
|
||||
},
|
||||
"time": {
|
||||
"from": "now-24h",
|
||||
"to": "now"
|
||||
},
|
||||
"timepicker": {},
|
||||
"timezone": "browser",
|
||||
"title": "Plant Overview",
|
||||
"uid": "VCPJlvuGz",
|
||||
"version": 33
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
-- phpMyAdmin SQL Dump
|
||||
-- version 5.1.0
|
||||
-- https://www.phpmyadmin.net/
|
||||
--
|
||||
-- Host: 192.168.0.25:3306
|
||||
-- Generation Time: Oct 22, 2021 at 02:07 AM
|
||||
-- Server version: 8.0.24
|
||||
-- PHP Version: 7.4.16
|
||||
|
||||
SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
|
||||
START TRANSACTION;
|
||||
SET time_zone = "+00:00";
|
||||
|
||||
|
||||
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
|
||||
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
|
||||
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
|
||||
/*!40101 SET NAMES utf8mb4 */;
|
||||
|
||||
--
|
||||
-- Database: `plantsystem`
|
||||
--
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
-- Table structure for table `sensor_data`
|
||||
--
|
||||
|
||||
CREATE TABLE `sensor_data` (
|
||||
`id` int NOT NULL,
|
||||
`insertTime` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`address` tinyint UNSIGNED NOT NULL,
|
||||
`T` float UNSIGNED NOT NULL,
|
||||
`M` smallint UNSIGNED NOT NULL,
|
||||
`A` smallint UNSIGNED NOT NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='Sensor data';
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
-- Table structure for table `sensor_links`
|
||||
--
|
||||
|
||||
CREATE TABLE `sensor_links` (
|
||||
`id` int UNSIGNED NOT NULL,
|
||||
`plantName` varchar(25) NOT NULL COMMENT 'Plant name',
|
||||
`sensorAddress` tinyint UNSIGNED NOT NULL COMMENT 'I2C address of sensor attached to this plant',
|
||||
`solenoidAddress` tinyint UNSIGNED NOT NULL COMMENT 'I2C address of solenoid attached to this plant'
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
-- Table structure for table `watering_data`
|
||||
--
|
||||
|
||||
CREATE TABLE `watering_data` (
|
||||
`id` int UNSIGNED NOT NULL,
|
||||
`solenoidAddress` tinyint UNSIGNED NOT NULL,
|
||||
`waterVolume` smallint UNSIGNED NOT NULL,
|
||||
`timestamp` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
|
||||
|
||||
--
|
||||
-- Indexes for dumped tables
|
||||
--
|
||||
|
||||
--
|
||||
-- Indexes for table `sensor_data`
|
||||
--
|
||||
ALTER TABLE `sensor_data`
|
||||
ADD PRIMARY KEY (`id`);
|
||||
|
||||
--
|
||||
-- Indexes for table `sensor_links`
|
||||
--
|
||||
ALTER TABLE `sensor_links`
|
||||
ADD PRIMARY KEY (`id`);
|
||||
|
||||
--
|
||||
-- Indexes for table `watering_data`
|
||||
--
|
||||
ALTER TABLE `watering_data`
|
||||
ADD PRIMARY KEY (`id`);
|
||||
|
||||
--
|
||||
-- AUTO_INCREMENT for dumped tables
|
||||
--
|
||||
|
||||
--
|
||||
-- AUTO_INCREMENT for table `sensor_data`
|
||||
--
|
||||
ALTER TABLE `sensor_data`
|
||||
MODIFY `id` int NOT NULL AUTO_INCREMENT;
|
||||
|
||||
--
|
||||
-- AUTO_INCREMENT for table `sensor_links`
|
||||
--
|
||||
ALTER TABLE `sensor_links`
|
||||
MODIFY `id` int UNSIGNED NOT NULL AUTO_INCREMENT;
|
||||
|
||||
--
|
||||
-- AUTO_INCREMENT for table `watering_data`
|
||||
--
|
||||
ALTER TABLE `watering_data`
|
||||
MODIFY `id` int UNSIGNED NOT NULL AUTO_INCREMENT;
|
||||
COMMIT;
|
||||
|
||||
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
|
||||
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
|
||||
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
|
||||
@@ -0,0 +1,319 @@
|
||||
{
|
||||
"name": "Watering System - Periodic Watering",
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {},
|
||||
"name": "Start",
|
||||
"type": "n8n-nodes-base.start",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
460,
|
||||
770
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"operation": "executeQuery",
|
||||
"query": "SELECT * FROM `sensor_links` WHERE `solenoidAddress` <> '0' ORDER BY `solenoidAddress` ASC"
|
||||
},
|
||||
"name": "Select Solenoids",
|
||||
"type": "n8n-nodes-base.mySql",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
660,
|
||||
570
|
||||
],
|
||||
"credentials": {
|
||||
"mySql": "NAS - MySQL"
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"batchSize": 1,
|
||||
"options": {
|
||||
"reset": false
|
||||
}
|
||||
},
|
||||
"name": "For Each Solenoid",
|
||||
"type": "n8n-nodes-base.splitInBatches",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
850,
|
||||
570
|
||||
],
|
||||
"alwaysOutputData": true,
|
||||
"continueOnFail": true
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"url": "http://192.168.0.19/waterPlant",
|
||||
"responseFormat": "string",
|
||||
"options": {},
|
||||
"queryParametersUi": {
|
||||
"parameter": [
|
||||
{
|
||||
"name": "address",
|
||||
"value": "={{$json[\"solenoidAddress\"]}}"
|
||||
},
|
||||
{
|
||||
"name": "volume",
|
||||
"value": "={{$json[\"waterVolume\"]}}"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"name": "Water Plant",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
1260,
|
||||
570
|
||||
],
|
||||
"notesInFlow": false,
|
||||
"notes": "Send request to water the plant with 100ml of water"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"table": "watering_data",
|
||||
"columns": "solenoidAddress, waterVolume",
|
||||
"options": {}
|
||||
},
|
||||
"name": "Update `watering_data`",
|
||||
"type": "n8n-nodes-base.mySql",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
1450,
|
||||
770
|
||||
],
|
||||
"credentials": {
|
||||
"mySql": "NAS - MySQL"
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"functionCode": "const sleepTime = 25000; // in miliseconds\n\nfunction sleep(milliseconds) {\n return new Promise(\n resolve => setTimeout(resolve, milliseconds)\n );\n}\n// Sleep\nawait sleep(sleepTime );\n// Output data\nreturn items;\n"
|
||||
},
|
||||
"name": "Delay",
|
||||
"type": "n8n-nodes-base.function",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
1450,
|
||||
570
|
||||
],
|
||||
"notesInFlow": false,
|
||||
"notes": "Sleep"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"conditions": {
|
||||
"number": [],
|
||||
"boolean": [
|
||||
{
|
||||
"value1": "={{$node[\"For Each Solenoid\"].context[\"noItemsLeft\"]}}",
|
||||
"value2": true
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"name": "IF",
|
||||
"type": "n8n-nodes-base.if",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
1660,
|
||||
320
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {},
|
||||
"name": "NoOp",
|
||||
"type": "n8n-nodes-base.noOp",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
1860,
|
||||
420
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"values": {
|
||||
"number": [
|
||||
{
|
||||
"name": "waterVolume",
|
||||
"value": 50
|
||||
}
|
||||
]
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"name": "Water Volume",
|
||||
"type": "n8n-nodes-base.set",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
1060,
|
||||
570
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"chatId": "1254950103",
|
||||
"text": "=Finished watering the plants!",
|
||||
"additionalFields": {}
|
||||
},
|
||||
"name": "Telegram",
|
||||
"type": "n8n-nodes-base.telegram",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
1860,
|
||||
270
|
||||
],
|
||||
"credentials": {
|
||||
"telegramApi": "Telegram N8N"
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"triggerTimes": {
|
||||
"item": [
|
||||
{
|
||||
"hour": 5
|
||||
},
|
||||
{
|
||||
"hour": 19
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"name": "Cron",
|
||||
"type": "n8n-nodes-base.cron",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
460,
|
||||
570
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"path": "f7048627-a726-4344-8d49-e7ad19c4415a",
|
||||
"options": {}
|
||||
},
|
||||
"name": "Webhook",
|
||||
"type": "n8n-nodes-base.webhook",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
460,
|
||||
370
|
||||
],
|
||||
"webhookId": "f7048627-a726-4344-8d49-e7ad19c4415a"
|
||||
}
|
||||
],
|
||||
"connections": {
|
||||
"Select Solenoids": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "For Each Solenoid",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"For Each Solenoid": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Water Volume",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Water Plant": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Delay",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "Update `watering_data`",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Delay": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "IF",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"IF": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "NoOp",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "Telegram",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"node": "For Each Solenoid",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Water Volume": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Water Plant",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Cron": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Select Solenoids",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Webhook": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Select Solenoids",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"active": false,
|
||||
"settings": {},
|
||||
"id": "8"
|
||||
}
|
||||
@@ -0,0 +1,186 @@
|
||||
{
|
||||
"name": "Plant System - Read Sensor Data",
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {},
|
||||
"name": "Start",
|
||||
"type": "n8n-nodes-base.start",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
250,
|
||||
350
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"url": "http://192.168.0.19/sensorData",
|
||||
"allowUnauthorizedCerts": true,
|
||||
"options": {
|
||||
"followRedirect": true,
|
||||
"ignoreResponseCode": true
|
||||
}
|
||||
},
|
||||
"name": "HTTP Request",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
500,
|
||||
350
|
||||
],
|
||||
"continueOnFail": true
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"chatId": "1254950103",
|
||||
"text": "=HousePlantSystem: Reading {{$node[\"HTTP Request\"].json[\"count\"]}} sensors instead of 4.",
|
||||
"additionalFields": {}
|
||||
},
|
||||
"name": "Telegram",
|
||||
"type": "n8n-nodes-base.telegram",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
1000,
|
||||
150
|
||||
],
|
||||
"credentials": {
|
||||
"telegramApi": "Telegram N8N"
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"table": "sensor_data",
|
||||
"columns": "address, T, M, A",
|
||||
"options": {}
|
||||
},
|
||||
"name": "MySQL",
|
||||
"type": "n8n-nodes-base.mySql",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
1000,
|
||||
350
|
||||
],
|
||||
"credentials": {
|
||||
"mySql": "NAS - MySQL"
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"functionCode": "const sensorData = [];\n\nfor (let i=0; i<items[0].json.count; i++)\n{\n\tsensorData.push( {json: items[0].json.sensors[i] });\n}\n\nreturn sensorData;\n"
|
||||
},
|
||||
"name": "Function",
|
||||
"type": "n8n-nodes-base.function",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
750,
|
||||
350
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"triggerTimes": {
|
||||
"item": [
|
||||
{
|
||||
"mode": "everyX",
|
||||
"value": 15,
|
||||
"unit": "minutes"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"name": "Cron",
|
||||
"type": "n8n-nodes-base.cron",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
250,
|
||||
150
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"conditions": {
|
||||
"boolean": [],
|
||||
"number": [
|
||||
{
|
||||
"value1": "= {{$json[\"count\"]}}",
|
||||
"operation": "largerEqual",
|
||||
"value2": 4
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"name": "IF",
|
||||
"type": "n8n-nodes-base.if",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
750,
|
||||
150
|
||||
]
|
||||
}
|
||||
],
|
||||
"connections": {
|
||||
"Start": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "HTTP Request",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"HTTP Request": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Function",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "IF",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Function": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "MySQL",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Cron": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "HTTP Request",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"IF": {
|
||||
"main": [
|
||||
[],
|
||||
[
|
||||
{
|
||||
"node": "Telegram",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"active": true,
|
||||
"settings": {},
|
||||
"id": "3"
|
||||
}
|
||||
@@ -0,0 +1,298 @@
|
||||
{
|
||||
"name": "PlantSystem - Water All Plants",
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {},
|
||||
"name": "Start",
|
||||
"type": "n8n-nodes-base.start",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
700,
|
||||
550
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"operation": "executeQuery",
|
||||
"query": "SELECT * FROM `sensor_links` ORDER BY `solenoidAddress` ASC"
|
||||
},
|
||||
"name": "Select Solenoids",
|
||||
"type": "n8n-nodes-base.mySql",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
900,
|
||||
550
|
||||
],
|
||||
"credentials": {
|
||||
"mySql": "NAS - MySQL"
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"batchSize": 1,
|
||||
"options": {
|
||||
"reset": false
|
||||
}
|
||||
},
|
||||
"name": "For Each Solenoid",
|
||||
"type": "n8n-nodes-base.splitInBatches",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
1100,
|
||||
550
|
||||
],
|
||||
"alwaysOutputData": true,
|
||||
"continueOnFail": true
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"url": "http://192.168.0.19/waterPlant",
|
||||
"responseFormat": "string",
|
||||
"options": {},
|
||||
"queryParametersUi": {
|
||||
"parameter": [
|
||||
{
|
||||
"name": "address",
|
||||
"value": "={{$json[\"solenoidAddress\"]}}"
|
||||
},
|
||||
{
|
||||
"name": "volume",
|
||||
"value": "={{$json[\"waterVolume\"]}}"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"name": "Water Plant",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
1500,
|
||||
550
|
||||
],
|
||||
"notesInFlow": false,
|
||||
"notes": "Send request to water the plant with 100ml of water"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"table": "watering_data",
|
||||
"columns": "solenoidAddress, waterVolume",
|
||||
"options": {}
|
||||
},
|
||||
"name": "Update `watering_data`",
|
||||
"type": "n8n-nodes-base.mySql",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
1500,
|
||||
750
|
||||
],
|
||||
"credentials": {
|
||||
"mySql": "NAS - MySQL"
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"functionCode": "const sleepTime = 15000; // in miliseconds\n\nfunction sleep(milliseconds) {\n return new Promise(\n resolve => setTimeout(resolve, milliseconds)\n );\n}\n// Sleep\nawait sleep(sleepTime );\n// Output data\nreturn items;\n"
|
||||
},
|
||||
"name": "Delay",
|
||||
"type": "n8n-nodes-base.function",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
1700,
|
||||
550
|
||||
],
|
||||
"notesInFlow": false,
|
||||
"notes": "Sleep"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"conditions": {
|
||||
"number": [],
|
||||
"boolean": [
|
||||
{
|
||||
"value1": "={{$node[\"For Each Solenoid\"].context[\"noItemsLeft\"]}}",
|
||||
"value2": true
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"name": "IF",
|
||||
"type": "n8n-nodes-base.if",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
1900,
|
||||
300
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {},
|
||||
"name": "NoOp",
|
||||
"type": "n8n-nodes-base.noOp",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
2100,
|
||||
300
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"values": {
|
||||
"number": [
|
||||
{
|
||||
"name": "waterVolume",
|
||||
"value": 50
|
||||
}
|
||||
]
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"name": "Water Volume",
|
||||
"type": "n8n-nodes-base.set",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
1300,
|
||||
550
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"chatId": "1254950103",
|
||||
"text": "=Watering {{$json[\"plantName\"]}} with {{$json[\"waterVolume\"]}}mL of water",
|
||||
"additionalFields": {}
|
||||
},
|
||||
"name": "Telegram",
|
||||
"type": "n8n-nodes-base.telegram",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
1500,
|
||||
950
|
||||
],
|
||||
"credentials": {
|
||||
"telegramApi": "Telegram N8N"
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"path": "4d08ae89-8eba-4ee4-80c2-d8f93ff51adb",
|
||||
"options": {}
|
||||
},
|
||||
"name": "Webhook",
|
||||
"type": "n8n-nodes-base.webhook",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
700,
|
||||
390
|
||||
],
|
||||
"webhookId": "4d08ae89-8eba-4ee4-80c2-d8f93ff51adb"
|
||||
}
|
||||
],
|
||||
"connections": {
|
||||
"Start": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Select Solenoids",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Select Solenoids": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "For Each Solenoid",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"For Each Solenoid": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Water Volume",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Delay": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "IF",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Water Plant": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Delay",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"IF": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "NoOp",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"node": "For Each Solenoid",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Water Volume": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Update `watering_data`",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "Telegram",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "Water Plant",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Webhook": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Select Solenoids",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"active": true,
|
||||
"settings": {},
|
||||
"id": "4"
|
||||
}
|
||||
@@ -0,0 +1,208 @@
|
||||
{
|
||||
"name": "PlantSystem - Water Specific Plant",
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {},
|
||||
"name": "Start",
|
||||
"type": "n8n-nodes-base.start",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
800,
|
||||
750
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"url": "http://192.168.0.19/waterPlant",
|
||||
"responseFormat": "string",
|
||||
"options": {},
|
||||
"queryParametersUi": {
|
||||
"parameter": [
|
||||
{
|
||||
"name": "address",
|
||||
"value": "={{$json[\"solenoidAddress\"]}}"
|
||||
},
|
||||
{
|
||||
"name": "volume",
|
||||
"value": "={{$node[\"Webhook\"].json[\"query\"][\"volume\"]}}"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"name": "Water Plant",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
1200,
|
||||
550
|
||||
],
|
||||
"notesInFlow": false,
|
||||
"alwaysOutputData": true,
|
||||
"continueOnFail": true,
|
||||
"notes": "Send request to water the plant with 100ml of water"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"path": "756fac76-f3d7-4fce-b586-27e114afba46",
|
||||
"options": {}
|
||||
},
|
||||
"name": "Webhook",
|
||||
"type": "n8n-nodes-base.webhook",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
800,
|
||||
550
|
||||
],
|
||||
"webhookId": "756fac76-f3d7-4fce-b586-27e114afba46"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"operation": "executeQuery",
|
||||
"query": "=SELECT * from `sensor_links` where `solenoidAddress` = '{{$json[\"query\"][\"solenoidAddress\"]}}' LIMIT 1"
|
||||
},
|
||||
"name": "MySQL",
|
||||
"type": "n8n-nodes-base.mySql",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
1000,
|
||||
550
|
||||
],
|
||||
"credentials": {
|
||||
"mySql": "NAS - MySQL"
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"table": "watering_data",
|
||||
"columns": "=solenoidAddress, waterVolume",
|
||||
"options": {}
|
||||
},
|
||||
"name": "Insert Water Amount",
|
||||
"type": "n8n-nodes-base.mySql",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
1810,
|
||||
450
|
||||
],
|
||||
"credentials": {
|
||||
"mySql": "NAS - MySQL"
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"functionCode": "items[0].json.waterVolume = $node[\"Webhook\"].json[\"query\"][\"volume\"];\nitems[0].json.solenoidAddress= $node[\"MySQL\"].json[\"solenoidAddress\"];\nreturn items;\n"
|
||||
},
|
||||
"name": "Function",
|
||||
"type": "n8n-nodes-base.function",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
1610,
|
||||
450
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"conditions": {
|
||||
"string": [
|
||||
{
|
||||
"value1": "={{$json[\"data\"]}}",
|
||||
"operation": "contains",
|
||||
"value2": "\"result\": OK"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"name": "IF",
|
||||
"type": "n8n-nodes-base.if",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
1400,
|
||||
550
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"chatId": "1254950103",
|
||||
"text": "=Water specific plant failed: Plant #{{$node[\"MySQL\"].json[\"sensorAddress\"]}} - Volume:{{$node[\"Webhook\"].json[\"query\"][\"volume\"]}}ml",
|
||||
"additionalFields": {}
|
||||
},
|
||||
"name": "Telegram",
|
||||
"type": "n8n-nodes-base.telegram",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
1600,
|
||||
650
|
||||
],
|
||||
"credentials": {
|
||||
"telegramApi": "Telegram N8N"
|
||||
}
|
||||
}
|
||||
],
|
||||
"connections": {
|
||||
"Webhook": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "MySQL",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"MySQL": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Water Plant",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Function": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Insert Water Amount",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Water Plant": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "IF",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"IF": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Function",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"node": "Telegram",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"active": true,
|
||||
"settings": {},
|
||||
"id": "5"
|
||||
}
|
||||
@@ -22,6 +22,13 @@ Blog page: https://sasakaranovic.com/projects/diy-house-plants-watering-system-p
|
||||
Build watering system for house plants. In part two of this video series, we will create a central unit that takes sensor readings and also waters our plants on command.
|
||||
|
||||
|
||||
## Part 3: Dashboard
|
||||
## Part 3: Automation and Conclusion
|
||||
|
||||
[](https://youtu.be/jLdTUMHOhRE)
|
||||
|
||||
Watch: [DIY House Plants Watering System // Part 2: Automation and Conclusion](https://youtu.be/jLdTUMHOhRE)
|
||||
|
||||
Blog page: https://sasakaranovic.com/projects/diy-house-plants-watering-system-part-3-automation/
|
||||
|
||||
Build watering system for house plants. In part two of this video series, we will create a central unit that takes sensor readings and also waters our plants on command.
|
||||
|
||||
TBD
|
||||
|
||||
Reference in New Issue
Block a user