added plant monitor
|
@ -47,4 +47,5 @@ See the [documentation in this repository](./hardware/README.md).
|
||||||
## Hardware sensors
|
## Hardware sensors
|
||||||
|
|
||||||
* [Energy Monitor](./software/firmware/energy-monitor/README.md)
|
* [Energy Monitor](./software/firmware/energy-monitor/README.md)
|
||||||
|
* [Plant Monitor](./software/firmware/plant-monitor/README.md)
|
||||||
* [Shelly Example](./software/firmware/shelly-monitor/README.md)
|
* [Shelly Example](./software/firmware/shelly-monitor/README.md)
|
99
software/firmware/plant-monitor/README.md
Normal file
|
@ -0,0 +1,99 @@
|
||||||
|
# Plant Monitor
|
||||||
|
|
||||||
|
##
|
||||||
|
|
||||||
|
We use our existing plant monitor for this project.
|
||||||
|
|
||||||
|
|
||||||
|
## Tasmota
|
||||||
|
|
||||||
|
[Tasmota](https://tasmota.github.io/docs/) is an alternative Firmware for ESP8266. It's easy to use with it's graphical user interface.
|
||||||
|
|
||||||
|
You can flash Tasmota right from the browser using the [Tasmota Web Installer](https://tasmota.github.io/).
|
||||||
|
|
||||||
|
![Flash it](./docs/images/01-flash-1.png)
|
||||||
|
|
||||||
|
You have to connect your device through USB and select the right port. Exisisting firmware will be delete.
|
||||||
|
|
||||||
|
![Erase everything](./docs/images/02-flash-2.png)
|
||||||
|
|
||||||
|
After Tasmota has been flashed to your ESP, you can already set up your wifi.
|
||||||
|
|
||||||
|
![Set up Wifi](./docs/images/03-wifi.png)
|
||||||
|
|
||||||
|
After your device is connected to Wifi, you can switch over to the web UI of your device. Yes, your device now runs an embedded web server. There you can configure your device.
|
||||||
|
|
||||||
|
![Embedded web server](./docs/images/04-web-ui.png)
|
||||||
|
|
||||||
|
### Configure
|
||||||
|
|
||||||
|
Using this Web UI you can setup up your device: `Configure -> Configure Module`
|
||||||
|
|
||||||
|
Our Wemos D1 mini clone is a `generic device with 18 ports`. We have a DHT11 connected to D1 and an analogue measurement on A0.
|
||||||
|
|
||||||
|
![Basic configuration](./docs/images/05-configuration.png)
|
||||||
|
|
||||||
|
|
||||||
|
After configuring it this way, we can see you data in the web UI.
|
||||||
|
|
||||||
|
![Flash it](./docs/images/06-overview.png)
|
||||||
|
|
||||||
|
### MQTT
|
||||||
|
|
||||||
|
Tasmota's main protocol is MQTT. You can setup MQTT under `Configuration -> MQTT`.
|
||||||
|
|
||||||
|
![MQTT](./docs/images/07-setup-mqtt.png)
|
||||||
|
|
||||||
|
To send data more frequent (nice for debugging) you have to change the telemetry period to a lower leven (than 300 s / 5 min).
|
||||||
|
|
||||||
|
![Telemetry interval](./docs/images/08-configure-telemetry-interval.png)
|
||||||
|
|
||||||
|
Then our device will send data like this:
|
||||||
|
|
||||||
|
```
|
||||||
|
{
|
||||||
|
"Time": "2023-02-26T17:19:55",
|
||||||
|
"ANALOG": {
|
||||||
|
"A0": 6
|
||||||
|
},
|
||||||
|
"DHT11": {
|
||||||
|
"Temperature": 19,
|
||||||
|
"Humidity": 44,
|
||||||
|
"DewPoint": 6.4
|
||||||
|
},
|
||||||
|
"TempUnit": "C"
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
You can subscribe to this MQTT event in Node-RED.
|
||||||
|
|
||||||
|
![Node-RED](./docs/images/08-node-red-overview.png)
|
||||||
|
|
||||||
|
There is a small converter function, so we only store the interesting values in InfluxDB.
|
||||||
|
|
||||||
|
```
|
||||||
|
return {
|
||||||
|
payload: {
|
||||||
|
temperature: Number(msg.payload.DHT11.Temperature),
|
||||||
|
humidity: Number(msg.payload.DHT11.Humidity)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
```
|
||||||
|
We can query this data in InfluxDB.
|
||||||
|
|
||||||
|
![InfluxDB](./docs/images/09-influx.png)
|
||||||
|
|
||||||
|
This is our query.
|
||||||
|
|
||||||
|
```
|
||||||
|
from(bucket: "plant")
|
||||||
|
|> range(start: v.timeRangeStart, stop: v.timeRangeStop)
|
||||||
|
|> filter(fn: (r) => r["_measurement"] == "msg")
|
||||||
|
|> filter(fn: (r) => r["_field"] == "temperature" or r["_field"] == "humidity")
|
||||||
|
|> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)
|
||||||
|
|> yield(name: "mean")
|
||||||
|
```
|
||||||
|
|
||||||
|
We can use this query to create a dashboard in Grafana.
|
||||||
|
![InfluxDB](./docs/images/10-grafana.png)
|
||||||
|
|
BIN
software/firmware/plant-monitor/docs/images/01-flash-1.png
Normal file
After Width: | Height: | Size: 115 KiB |
BIN
software/firmware/plant-monitor/docs/images/02-flash-2.png
Normal file
After Width: | Height: | Size: 122 KiB |
BIN
software/firmware/plant-monitor/docs/images/03-wifi.png
Normal file
After Width: | Height: | Size: 129 KiB |
BIN
software/firmware/plant-monitor/docs/images/04-web-ui.png
Normal file
After Width: | Height: | Size: 96 KiB |
BIN
software/firmware/plant-monitor/docs/images/05-configuration.png
Normal file
After Width: | Height: | Size: 181 KiB |
BIN
software/firmware/plant-monitor/docs/images/06-overview.png
Normal file
After Width: | Height: | Size: 82 KiB |
BIN
software/firmware/plant-monitor/docs/images/07-setup-mqtt.png
Normal file
After Width: | Height: | Size: 127 KiB |
After Width: | Height: | Size: 113 KiB |
After Width: | Height: | Size: 112 KiB |
BIN
software/firmware/plant-monitor/docs/images/09-influx.png
Normal file
After Width: | Height: | Size: 456 KiB |
BIN
software/firmware/plant-monitor/docs/images/10-grafana.png
Normal file
After Width: | Height: | Size: 312 KiB |
|
@ -24,6 +24,9 @@
|
||||||
"workflow": {
|
"workflow": {
|
||||||
"mode": "manual"
|
"mode": "manual"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"debug": {
|
||||||
|
"filter": "filterAll"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -6,7 +6,7 @@
|
||||||
"view-store-position": false,
|
"view-store-position": false,
|
||||||
"view-show-grid": true,
|
"view-show-grid": true,
|
||||||
"view-snap-grid": true,
|
"view-snap-grid": true,
|
||||||
"view-grid-size": 20,
|
"view-grid-size": "20",
|
||||||
"view-node-status": true,
|
"view-node-status": true,
|
||||||
"view-node-show-label": true,
|
"view-node-show-label": true,
|
||||||
"view-show-tips": true,
|
"view-show-tips": true,
|
||||||
|
@ -15,6 +15,18 @@
|
||||||
"tours": {
|
"tours": {
|
||||||
"welcome": "3.0.2"
|
"welcome": "3.0.2"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"git": {
|
||||||
|
"user": {
|
||||||
|
"name": "",
|
||||||
|
"email": ""
|
||||||
|
},
|
||||||
|
"workflow": {
|
||||||
|
"mode": "manual"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"debug": {
|
||||||
|
"filter": "filterSelected"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -206,72 +206,6 @@
|
||||||
"url": "http://influxdb:8086",
|
"url": "http://influxdb:8086",
|
||||||
"rejectUnauthorized": true
|
"rejectUnauthorized": true
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"id": "3cc11d24.ff01a2",
|
|
||||||
"type": "comment",
|
|
||||||
"z": "f6f2187d.f17ca8",
|
|
||||||
"name": "WARNING: please check you have started this container with a volume that is mounted to /data\\n otherwise any flow changes are lost when you redeploy or upgrade the container\\n (e.g. upgrade to a more recent node-red docker image).\\n If you are using named volumes you can ignore this warning.\\n Double click or see info side panel to learn how to start Node-RED in Docker to save your work",
|
|
||||||
"info": "\nTo start docker with a bind mount volume (-v option), for example:\n\n```\ndocker run -it -p 1880:1880 -v /home/user/node_red_data:/data --name mynodered nodered/node-red\n```\n\nwhere `/home/user/node_red_data` is a directory on your host machine where you want to store your flows.\n\nIf you do not do this then you can experiment and redploy flows, but if you restart or upgrade the container the flows will be disconnected and lost. \n\nThey will still exist in a hidden data volume, which can be recovered using standard docker techniques, but that is much more complex than just starting with a named volume as described above.",
|
|
||||||
"x": 330,
|
|
||||||
"y": 100,
|
|
||||||
"wires": []
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "39aa2ca9.804da4",
|
|
||||||
"type": "debug",
|
|
||||||
"z": "f6f2187d.f17ca8",
|
|
||||||
"name": "",
|
|
||||||
"active": true,
|
|
||||||
"tosidebar": true,
|
|
||||||
"console": false,
|
|
||||||
"tostatus": false,
|
|
||||||
"complete": "payload",
|
|
||||||
"targetType": "msg",
|
|
||||||
"statusVal": "",
|
|
||||||
"statusType": "auto",
|
|
||||||
"x": 670,
|
|
||||||
"y": 360,
|
|
||||||
"wires": []
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "262a3923.e7b216",
|
|
||||||
"type": "influxdb in",
|
|
||||||
"z": "f6f2187d.f17ca8",
|
|
||||||
"influxdb": "eeb221fb.ab27f",
|
|
||||||
"name": "",
|
|
||||||
"query": "SELECT * from test",
|
|
||||||
"rawOutput": false,
|
|
||||||
"precision": "",
|
|
||||||
"retentionPolicy": "",
|
|
||||||
"org": "my-org",
|
|
||||||
"x": 450,
|
|
||||||
"y": 360,
|
|
||||||
"wires": [
|
|
||||||
[
|
|
||||||
"39aa2ca9.804da4"
|
|
||||||
]
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "803d82f.ff80f8",
|
|
||||||
"type": "inject",
|
|
||||||
"z": "f6f2187d.f17ca8",
|
|
||||||
"name": "",
|
|
||||||
"repeat": "",
|
|
||||||
"crontab": "",
|
|
||||||
"once": false,
|
|
||||||
"onceDelay": 0.1,
|
|
||||||
"topic": "",
|
|
||||||
"payload": "",
|
|
||||||
"payloadType": "date",
|
|
||||||
"x": 240,
|
|
||||||
"y": 360,
|
|
||||||
"wires": [
|
|
||||||
[
|
|
||||||
"262a3923.e7b216"
|
|
||||||
]
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"id": "00a750cdfb54579e",
|
"id": "00a750cdfb54579e",
|
||||||
"type": "mqtt in",
|
"type": "mqtt in",
|
||||||
|
@ -335,18 +269,95 @@
|
||||||
"id": "499e0f3651d818db",
|
"id": "499e0f3651d818db",
|
||||||
"type": "debug",
|
"type": "debug",
|
||||||
"z": "f6f2187d.f17ca8",
|
"z": "f6f2187d.f17ca8",
|
||||||
|
"name": "consumption",
|
||||||
|
"active": true,
|
||||||
|
"tosidebar": true,
|
||||||
|
"console": false,
|
||||||
|
"tostatus": false,
|
||||||
|
"complete": "payload",
|
||||||
|
"targetType": "msg",
|
||||||
|
"statusVal": "",
|
||||||
|
"statusType": "auto",
|
||||||
|
"x": 810,
|
||||||
|
"y": 420,
|
||||||
|
"wires": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b14166f65d5aec75",
|
||||||
|
"type": "mqtt in",
|
||||||
|
"z": "f6f2187d.f17ca8",
|
||||||
|
"name": "",
|
||||||
|
"topic": "tele/tasmota_A4D1F3/SENSOR",
|
||||||
|
"qos": "1",
|
||||||
|
"datatype": "auto-detect",
|
||||||
|
"broker": "7ce136dbb8c897d1",
|
||||||
|
"nl": false,
|
||||||
|
"rap": true,
|
||||||
|
"rh": 0,
|
||||||
|
"inputs": 0,
|
||||||
|
"x": 210,
|
||||||
|
"y": 720,
|
||||||
|
"wires": [
|
||||||
|
[
|
||||||
|
"e3742f5060cf6cb7",
|
||||||
|
"29cd45a4f1feb505"
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "23c62c30ebabca6e",
|
||||||
|
"type": "influxdb out",
|
||||||
|
"z": "f6f2187d.f17ca8",
|
||||||
|
"influxdb": "d61a7da6caeb26aa",
|
||||||
|
"name": "Influx plant sensor",
|
||||||
|
"measurement": "msg",
|
||||||
|
"precision": "",
|
||||||
|
"retentionPolicy": "",
|
||||||
|
"database": "database",
|
||||||
|
"precisionV18FluxV20": "ms",
|
||||||
|
"retentionPolicyV18Flux": "",
|
||||||
|
"org": "Curious Community Labs",
|
||||||
|
"bucket": "plant",
|
||||||
|
"x": 790,
|
||||||
|
"y": 640,
|
||||||
|
"wires": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "e3742f5060cf6cb7",
|
||||||
|
"type": "debug",
|
||||||
|
"z": "f6f2187d.f17ca8",
|
||||||
"name": "debug 1",
|
"name": "debug 1",
|
||||||
"active": true,
|
"active": true,
|
||||||
"tosidebar": true,
|
"tosidebar": true,
|
||||||
"console": false,
|
"console": false,
|
||||||
"tostatus": false,
|
"tostatus": false,
|
||||||
"complete": "false",
|
"complete": "payload",
|
||||||
|
"targetType": "msg",
|
||||||
"statusVal": "",
|
"statusVal": "",
|
||||||
"statusType": "auto",
|
"statusType": "auto",
|
||||||
"x": 800,
|
"x": 500,
|
||||||
"y": 420,
|
"y": 780,
|
||||||
"wires": []
|
"wires": []
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"id": "29cd45a4f1feb505",
|
||||||
|
"type": "function",
|
||||||
|
"z": "f6f2187d.f17ca8",
|
||||||
|
"name": "toNumber",
|
||||||
|
"func": "var newMsg = {\n payload: {\n temperature: Number(msg.payload.DHT.Temperature),\n humidity: Number(msg.payload.DHT11.Humidity)\n }\n};\nreturn newMsg;",
|
||||||
|
"outputs": 1,
|
||||||
|
"noerr": 0,
|
||||||
|
"initialize": "",
|
||||||
|
"finalize": "",
|
||||||
|
"libs": [],
|
||||||
|
"x": 520,
|
||||||
|
"y": 640,
|
||||||
|
"wires": [
|
||||||
|
[
|
||||||
|
"23c62c30ebabca6e"
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"id": "e0977f2582bfaaa6",
|
"id": "e0977f2582bfaaa6",
|
||||||
"type": "mqtt in",
|
"type": "mqtt in",
|
||||||
|
|
|
@ -269,18 +269,95 @@
|
||||||
"id": "499e0f3651d818db",
|
"id": "499e0f3651d818db",
|
||||||
"type": "debug",
|
"type": "debug",
|
||||||
"z": "f6f2187d.f17ca8",
|
"z": "f6f2187d.f17ca8",
|
||||||
|
"name": "consumption",
|
||||||
|
"active": true,
|
||||||
|
"tosidebar": true,
|
||||||
|
"console": false,
|
||||||
|
"tostatus": false,
|
||||||
|
"complete": "payload",
|
||||||
|
"targetType": "msg",
|
||||||
|
"statusVal": "",
|
||||||
|
"statusType": "auto",
|
||||||
|
"x": 810,
|
||||||
|
"y": 420,
|
||||||
|
"wires": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b14166f65d5aec75",
|
||||||
|
"type": "mqtt in",
|
||||||
|
"z": "f6f2187d.f17ca8",
|
||||||
|
"name": "",
|
||||||
|
"topic": "tele/tasmota_A4D1F3/SENSOR",
|
||||||
|
"qos": "1",
|
||||||
|
"datatype": "auto-detect",
|
||||||
|
"broker": "7ce136dbb8c897d1",
|
||||||
|
"nl": false,
|
||||||
|
"rap": true,
|
||||||
|
"rh": 0,
|
||||||
|
"inputs": 0,
|
||||||
|
"x": 210,
|
||||||
|
"y": 720,
|
||||||
|
"wires": [
|
||||||
|
[
|
||||||
|
"29cd45a4f1feb505"
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "23c62c30ebabca6e",
|
||||||
|
"type": "influxdb out",
|
||||||
|
"z": "f6f2187d.f17ca8",
|
||||||
|
"influxdb": "d61a7da6caeb26aa",
|
||||||
|
"name": "Influx plant sensor",
|
||||||
|
"measurement": "msg",
|
||||||
|
"precision": "",
|
||||||
|
"retentionPolicy": "",
|
||||||
|
"database": "database",
|
||||||
|
"precisionV18FluxV20": "ms",
|
||||||
|
"retentionPolicyV18Flux": "",
|
||||||
|
"org": "Curious Community Labs",
|
||||||
|
"bucket": "plant",
|
||||||
|
"x": 790,
|
||||||
|
"y": 640,
|
||||||
|
"wires": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "e3742f5060cf6cb7",
|
||||||
|
"type": "debug",
|
||||||
|
"z": "f6f2187d.f17ca8",
|
||||||
"name": "debug 1",
|
"name": "debug 1",
|
||||||
"active": true,
|
"active": true,
|
||||||
"tosidebar": true,
|
"tosidebar": true,
|
||||||
"console": false,
|
"console": false,
|
||||||
"tostatus": false,
|
"tostatus": false,
|
||||||
"complete": "false",
|
"complete": "payload",
|
||||||
|
"targetType": "msg",
|
||||||
"statusVal": "",
|
"statusVal": "",
|
||||||
"statusType": "auto",
|
"statusType": "auto",
|
||||||
"x": 800,
|
"x": 760,
|
||||||
"y": 420,
|
"y": 760,
|
||||||
"wires": []
|
"wires": []
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"id": "29cd45a4f1feb505",
|
||||||
|
"type": "function",
|
||||||
|
"z": "f6f2187d.f17ca8",
|
||||||
|
"name": "toNumber",
|
||||||
|
"func": "var newMsg = {\n payload: {\n temperature: Number(msg.payload.DHT11.Temperature),\n humidity: Number(msg.payload.DHT11.Humidity)\n }\n};\nreturn newMsg;",
|
||||||
|
"outputs": 1,
|
||||||
|
"noerr": 0,
|
||||||
|
"initialize": "",
|
||||||
|
"finalize": "",
|
||||||
|
"libs": [],
|
||||||
|
"x": 520,
|
||||||
|
"y": 640,
|
||||||
|
"wires": [
|
||||||
|
[
|
||||||
|
"23c62c30ebabca6e",
|
||||||
|
"e3742f5060cf6cb7"
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"id": "e0977f2582bfaaa6",
|
"id": "e0977f2582bfaaa6",
|
||||||
"type": "mqtt in",
|
"type": "mqtt in",
|
||||||
|
|