Skip to content

Commit

Permalink
Initial stable
Browse files Browse the repository at this point in the history
  • Loading branch information
signag committed Aug 1, 2024
1 parent 1984e69 commit 0914458
Show file tree
Hide file tree
Showing 7 changed files with 234 additions and 173 deletions.
180 changes: 98 additions & 82 deletions README.md

Large diffs are not rendered by default.

38 changes: 38 additions & 0 deletions doc/GrafanaSetup.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Setup of monitorEMS Dashboards for Grafana

[![Up](img/goup.gif)](../README.md)

The dashboards included in this package have been developed with [Grafana Open Source](https://grafana.com/oss/grafana/) (Grafana OSS 11.1).

If you are already using Grafana, you can add these dashboards to your existing instance.

## Docker Installation of Grafana

If you are new to Grafana, it is recommended to install the Docker image of Grafana OSS:
<https://grafana.com/grafana/download?pg=oss-graf&platform=docker&edition=oss>

Details of the setup are described in [Run Grafana Docker Image](https://grafana.com/docs/grafana/latest/setup-grafana/installation/docker/).

Short version:

1. From a command prompt of the machine where Docker is running:<br>```docker run -d --name=grafana -p 3000:3000 grafana/grafana-oss```
2. From a browser on any client in the same network:<br>```http://<hostname>:3000```<br>where ```<hostname>```is the host name or IP address of the Docker machine
3. On the Login screen, enter "admin" for both, username and password
4. On request enter a new password

## Configuring the Data Source

1. Under *Connections*, choose *Add new connection* and select *InfluxDB*
2. On the configuration screen, set<br>**Query language**: Flux<br>**URL**: URL for the Influx DB<br>**Auth**: deselect all options<br>**InfluxDB Details/Organization**: Organization specified during Influx setup.<br>**InfluxDB Details/Token**: The token created in Influx.
3. Push *Save & test*

## Importing the Dashboards

You can download JSON exports of the monitorEMS dashboards from the [grafana](./../grafana/) folder of this repository.

To import these into an existing Grafana instance, proceed as follows:

1. Open the *Dashboards* overview
2. Push the *New* button and select *Import*
4. Navigate to one of the downloaded JSON files and push *Import*

47 changes: 47 additions & 0 deletions doc/Vizualization.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Visualization Examples

[![Up](img/goup.gif)](../README.md)

This package includes some examples for visualization based on data confígured in the [monitorEMS_tpl.json](./../config/monitorEMS_tpl.json).
[Grafana Setup](GrafanaSetup.md) describes how to set up the dashboards in Grafana.

## Dashboard EMS Power

![EMS Power](./img/Dashboard_EMS_Power.jpg)

The upper part of this dashboard is essentially tha same as the standard OpenEMS [Enrgy Monitor](https://github.com/OpenEMS/openems?tab=readme-ov-file#openems-ui-screenshots) dashboard, showing power from production, consumption and storage over time together with the State of Charge of the battery.

The lower part shows enrgy over time:
Consumed energy and energy fed into the grid are stacked.
The blue line shows net production, considering buffering (production + decharge - charge).
The red line shows energy obtained from the grid.

## Dashboard Battery Overview

![Battery Overview](./img/Dashboard_EMS_BatteryOverview.jpg)

Data for this dashboard are not queried directly from the system.
Instead, the time of evaluation is the last measurement within the selected period.

The upper left part shows two tables with static and dynamic battery characteristics.
The upper right part shows State of Charge, Voltage and Current. Negative current indicates discharging.

The table in the lower part represents the battery modules with their serial number, their temperature (average of three sensors) and their total voltage (sum of cell voltages).
The sequence represents stacking of the modules with module 0 being the uppermost.

## Dashboard Battery Time Series

![Battery Time Series](./img/Dashboard_EMS_BatteryTimeSeries.jpg)

This dashboard shows some characteristics of the storage system over time.

The selector drop-downs allow selecting battery, tower, module and cell. Instead of an individual cell, you may also choose "All".

The upper part of the dashboard refers to the entire battery system (more precisely: the selected tower).
It shows voltage and current as well as State of Charge and State of Health.
Shaded areas indicate phases of charging, decharging as well as idle status,
The yellow area shows the spread between minimum and maximum cell voltage within the tower.

The bottom part refers to the selected module, for which the temperatures from the three sensors are drawn.
If Cell=All is selected, the yellow area shows the spread between minimum and maximum cell voltage for the selected module.
If a specific cell is selected, the voltage for this cell is drawn.
Binary file added doc/img/goup.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
36 changes: 12 additions & 24 deletions grafana/EMS Battery_Overview.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,7 @@
},
{
"datasource": {
"type": "influxdb",
"uid": "Wivf-yM4z"
"type": "influxdb"
},
"fieldConfig": {
"defaults": {
Expand Down Expand Up @@ -106,8 +105,7 @@
"targets": [
{
"datasource": {
"type": "influxdb",
"uid": "Wivf-yM4z"
"type": "influxdb"
},
"query": "import \"date\"\r\nstop = v.timeRangeStop\r\nstart = date.sub(from: stop, d: 1d)\r\nfrom(bucket: \"EMS\")\r\n |> range(start: start, stop: stop)\r\n |> filter(fn: (r) => r[\"_measurement\"] == \"ems_battery\" or (r[\"_measurement\"] == \"ems_battery_towers\" and r[\"Tower\"] == \"0\"))\r\n |> sort(columns: [\"_time\"])\r\n |> tail(n: 1)\r\n |> drop(columns: [\"_start\", \"_stop\", \"battery\", \"_measurement\", \"_time\", \"Tower\"])\r\n |> map(fn: (r) => ({r with val: string(v: r[\"_value\"])}))\r\n |> drop(columns: [\"_value\"])\r\n |> group()\r\n |> filter(fn: (r) => \r\n r[\"_field\"] == \"BmsSerialNumber\" \r\n or r[\"_field\"] == \"BmsSoftwareVersion\"\r\n or r[\"_field\"] == \"Capacity\"\r\n or r[\"_field\"] == \"NumberOfModulesPerTower\"\r\n or r[\"_field\"] == \"NumberOfTowers\"\r\n or r[\"_field\"] == \"RackNumberOfCellsInSeriesPerModule\"\r\n )\r\n",
"refId": "A"
Expand All @@ -118,8 +116,7 @@
},
{
"datasource": {
"type": "influxdb",
"uid": "Wivf-yM4z"
"type": "influxdb"
},
"fieldConfig": {
"defaults": {
Expand Down Expand Up @@ -173,8 +170,7 @@
"targets": [
{
"datasource": {
"type": "influxdb",
"uid": "Wivf-yM4z"
"type": "influxdb"
},
"query": "import \"date\"\r\nstop = v.timeRangeStop\r\nstart = date.sub(from: stop, d: 1d)\r\nfrom(bucket: \"EMS\")\r\n |> range(start: start, stop: stop)\r\n |> filter(fn: (r) => r[\"_measurement\"] == \"ems_battery\" or (r[\"_measurement\"] == \"ems_battery_towers\" and r[\"Tower\"] == \"0\"))\r\n |> sort(columns: [\"_time\"])\r\n |> tail(n: 1)\r\n |> drop(columns: [\"_start\", \"_stop\", \"battery\", \"_measurement\", \"_time\", \"Tower\"])\r\n |> map(fn: (r) => ({r with val: string(v: r[\"_value\"])}))\r\n |> drop(columns: [\"_value\"])\r\n |> group()\r\n |> filter(fn: (r) => \r\n r[\"_field\"] == \"AvarageCellVoltage\" \r\n or r[\"_field\"] == \"MaxCellVoltage\"\r\n or r[\"_field\"] == \"MinCellVoltage\"\r\n or r[\"_field\"] == \"NoOfCycles\"\r\n or r[\"_field\"] == \"RemainingCapacity\"\r\n or r[\"_field\"] == \"UsableCapacity\"\r\n or r[\"_field\"] == \"Soh\"\r\n )",
"refId": "A"
Expand All @@ -185,8 +181,7 @@
},
{
"datasource": {
"type": "influxdb",
"uid": "Wivf-yM4z"
"type": "influxdb"
},
"fieldConfig": {
"defaults": {
Expand Down Expand Up @@ -243,8 +238,7 @@
"targets": [
{
"datasource": {
"type": "influxdb",
"uid": "Wivf-yM4z"
"type": "influxdb"
},
"query": "import \"date\"\r\nstop = v.timeRangeStop\r\nstart = date.sub(from: stop, d: 1d)\r\nfrom(bucket: \"EMS\")\r\n |> range(start: start, stop: stop)\r\n |> filter(fn: (r) => r[\"_measurement\"] == \"ems_battery_towers\" and r[\"_field\"] == \"Soc\")\r\n |> sort(columns: [\"_time\"])\r\n |> tail(n: 1)\r\n |> drop(columns: [\"_start\", \"_stop\", \"battery\", \"_measurement\", \"_time\", \"Tower\"])\r\n",
"refId": "A"
Expand All @@ -255,8 +249,7 @@
},
{
"datasource": {
"type": "influxdb",
"uid": "Wivf-yM4z"
"type": "influxdb"
},
"fieldConfig": {
"defaults": {
Expand Down Expand Up @@ -305,8 +298,7 @@
"targets": [
{
"datasource": {
"type": "influxdb",
"uid": "Wivf-yM4z"
"type": "influxdb"
},
"query": "import \"date\"\r\nstop = v.timeRangeStop\r\nstart = date.sub(from: stop, d: 1d)\r\nfrom(bucket: \"EMS\")\r\n |> range(start: start, stop: stop)\r\n |> filter(fn: (r) => r[\"_measurement\"] == \"ems_battery_towers\" and r[\"_field\"] == \"Voltage\")\r\n |> sort(columns: [\"_time\"])\r\n |> tail(n: 1)\r\n |> drop(columns: [\"_start\", \"_stop\", \"battery\", \"_measurement\", \"_time\", \"Tower\"])\r\n",
"refId": "A"
Expand All @@ -317,8 +309,7 @@
},
{
"datasource": {
"type": "influxdb",
"uid": "Wivf-yM4z"
"type": "influxdb"
},
"fieldConfig": {
"defaults": {
Expand Down Expand Up @@ -371,8 +362,7 @@
"targets": [
{
"datasource": {
"type": "influxdb",
"uid": "Wivf-yM4z"
"type": "influxdb"
},
"query": "import \"date\"\r\nstop = v.timeRangeStop\r\nstart = date.sub(from: stop, d: 1d)\r\nfrom(bucket: \"EMS\")\r\n |> range(start: start, stop: stop)\r\n |> filter(fn: (r) => r[\"_measurement\"] == \"ems_battery_towers\" and r[\"_field\"] == \"Current\")\r\n |> sort(columns: [\"_time\"])\r\n |> tail(n: 1)\r\n |> drop(columns: [\"_start\", \"_stop\", \"battery\", \"_measurement\", \"_time\", \"Tower\"])\r\n",
"refId": "A"
Expand All @@ -396,8 +386,7 @@
},
{
"datasource": {
"type": "influxdb",
"uid": "Wivf-yM4z"
"type": "influxdb"
},
"description": "",
"fieldConfig": {
Expand Down Expand Up @@ -554,8 +543,7 @@
"targets": [
{
"datasource": {
"type": "influxdb",
"uid": "Wivf-yM4z"
"type": "influxdb"
},
"query": "import \"date\"\r\nimport \"math\"\r\nimport \"join\"\r\nstop = v.timeRangeStop\r\nstart = date.sub(from: stop, d: 1d)\r\ncv =\r\n from(bucket: \"EMS\")\r\n |> range(start: start, stop: stop)\r\n |> filter(fn: (r) => r[\"_measurement\"] == \"ems_battery_cells\" and r[\"_field\"] == \"Voltage\")\r\n |> sort(columns: [\"_time\"])\r\n |> tail(n: 1)\r\n |> drop(columns: [\"_start\", \"_stop\", \"battery\", \"_measurement\", \"_time\", \"Tower\"])\r\n |> sort(columns: [\"_Module\"])\r\n |> group(columns: [\"Module\"])\r\n |> sum()\r\n |> map(fn: (r) => ({r with Voltage: float(v: r._value)/1000.0}))\r\n |> drop(columns: [\"_value\"])\r\n |> group()\r\n\r\nmt =\r\n from(bucket: \"EMS\")\r\n |> range(start: start, stop: stop)\r\n |> filter(fn: (r) => r[\"_measurement\"] == \"ems_battery_modules\")\r\n |> sort(columns: [\"_time\"])\r\n |> tail(n: 1)\r\n |> drop(columns: [\"_start\", \"_stop\", \"battery\", \"_measurement\", \"_time\", \"Tower\"])\r\n |> sort(columns: [\"_Module\"])\r\n |> group(columns: [\"Module\"])\r\n |> mean()\r\n |> map(fn: (r) => ({r with Temperature: math.round(x: r._value * 10.0) / 100.0}))\r\n |> drop(columns: [\"_value\"])\r\n |> group()\r\n\r\nms =\r\n from(bucket: \"EMS\")\r\n |> range(start: start, stop: stop)\r\n |> filter(fn: (r) => r[\"_measurement\"] == \"ems_battery_modules\")\r\n |> sort(columns: [\"_time\"])\r\n |> tail(n: 1)\r\n |> drop(columns: [\"_start\", \"_stop\", \"battery\", \"_measurement\", \"_time\", \"Tower\", \"_field\", \"_value\"])\r\n |> sort(columns: [\"_Module\"])\r\n |> tail(n: 1)\r\n |> group()\r\n\r\nmst =\r\n join.inner(left: ms, \r\n right: mt, \r\n on: (l, r) => l.Module == r.Module,\r\n as: (l, r) => ({l with Temperature: r.Temperature}) \r\n )\r\n\r\nmstv =\r\n join.inner(left: mst, \r\n right: cv, \r\n on: (l, r) => l.Module == r.Module,\r\n as: (l, r) => ({l with Voltage: r.Voltage}) \r\n )\r\nmstv",
"refId": "A"
Expand Down
Loading

0 comments on commit 0914458

Please sign in to comment.