Category Archives: Allgemein

MonsterMQ with Grafana

Because MonsterMQ can store topic values directly in PostgreSQL/Timescale, you can instantly create dashboards with Grafana! 📊

Here’s a simple example:

👉 Check out the live dashboard

It’s super easy to get started. Use the public available MonsterMQ at test.monstermq.com at port 1883. Just publish a JSON string to any topic under “Test”, like “Test/Sensor1” with a payload like this: {“value”: 1}, and you’ll see the value reflected in the Grafana dashboard in real time.

I’m currently publishing temperature sensor values to the public broker from my home automation using automation-gateway.com. Just that you see some values in the dashboard.

So, if you need a broker to store your IoT data directly into TimescaleDB without the need for any additional components, consider using MonsterMQ. It’s free and available at MonsterMQ.com.

Here is the exmaple docker-compose.yml file of the public availble test.monstermq.com broker:

services:
  timescale:
    image: timescale/timescaledb:latest-pg16
    restart: unless-stopped
    ports:
      - "5432:5432"
    volumes:
      - /data/timescale:/var/lib/postgresql/data
    environment:
      POSTGRES_USER: system
      POSTGRES_PASSWORD: xxx
  monstermq:
    image: rocworks/monstermq:latest
    restart: unless-stopped
    ports:
      - 1883:1883
    volumes:
      - ./config.yaml:/app/config.yaml
    command: ["+cluster", "-log INFO"]
  pgadmin:
    image: dpage/pgadmin4
    restart: unless-stopped
    environment:
      PGADMIN_DEFAULT_EMAIL: andreas.vogler@rocworks.at
      PGADMIN_DEFAULT_PASSWORD: xxx
    volumes:
      - /data/pgadmin:/var/lib/pgadmin/storage
    ports:
      - "8080:80"
  grafana:
    image: grafana/grafana
    restart: unless-stopped
    ports:
    - 80:3000

Here is the MonsterMQ config.yml file:

Port: 1883
SSL: false
WS: true
TCP: true

SessionStoreType: POSTGRES
RetainedStoreType: POSTGRES

SparkplugMetricExpansion:
  Enabled: true

ArchiveGroups:
  - Name: "All"
    Enabled: true
    TopicFilter: [ "#" ]
    RetainedOnly: false
    LastValType: POSTGRES
    ArchiveType: NONE
  - Name: "Test"
    Enabled: true
    TopicFilter: [ "Test/#" ]
    RetainedOnly: false
    LastValType: NONE
    ArchiveType: POSTGRES

Postgres:
  Url: jdbc:postgresql://timescale:5432/monster
  User: system
  Pass: xxx

Give it a try and let me know what you think!

#iot #mqtt #monstermq #timescale #postgresql #grafana

QuestDB: My time series data’s new best friend? 📈

My first tests with QuestDB on 10 years of home automation data (1.4 billion rows) are promising.

👉 Fast ingestion of parquet files (~1 hour on an old Intel NUC i5)

I have stored my data in parquet files, one per month, and imported it with a simple Python script. The import on my really old Intel NUC i5 took only about one hour. I’ve never been able to do this so quickly with any other database.

🤔 Btw.: I think storing data in #parquet files, or any other open table format, like Apache Iceberg, is one of the best choices to keep data. Because it’s independent of a database engine.

👉 Familiar SQL syntax. I experienced that QuestDB has a powerful SQL engine. I converted some Postgres SQL statements to QuestDB without big issues or changes. And I love SQL 💚

👉 Great query response times – see image. Not a representative query, but still impressive speed.

👉 By using ZFS with compression the used disk space can be reduced to a good value.

Do you want to log your OPCUA data to QuestDB? I have added this option to the automation-gateway.com seven days ago.

Node.JS for WinCC OA? And what about Java? GraalVM? Polyglot?

🥳 Last weekend I found some time to try out an upcoming feature in WinCC Open Architecture 3.20. With the Node.js integration you can write your business logic in JavaScript with native connectivity to WinCC OA. You can take full advantage of the Node.js ecosystem.

🧐 But I am a Java enthusiast and I love the JVM ecosystem. Have you ever heard about GraalVM? It is an advanced JDK written in Java. And it has a Node.js Runtime, which gives you the power of Node.js plus the power of polyglot programming, you can mix JavaScript with Java.

👍 And it turned out that the GraalVM Node.js Runtime also works with WinCC OA! It took me some time to figure out how the polyglot interoperability works, but now I have a first draft of a Java-Library which makes it easy to use Java and OA in the Node.js environment.

🤩 I can now use Java to develop great solutions with WinCC OA.

WinCC OA & Node-Red Integration

It is very easy to get data from WinCC Open Architecture to NodeRed.

Add a new user to WinCC OA – System Management / Permission / User Administation.

We will use “node” as username.

Add Config Entry

[wssServer]
httpsPort = 8449
resourceName = "/websocket"

Start Control Manager “wss.ctl -user <username>:” Note the trailing “:” !!

wss.ctl -user node:

Node-Red: Install Palette “node-red-contrib-winccoa”

You can now add a Node. In that example we will use the dpQuery node and use “SELECT ‘_online.._value’ FROM ‘Meter_Input_WattAct.'” as query. So we just query the online value of one tag.

You have to configure the Server by clicking on the pencil button. This points to the before started Websocket Control Manager and you have to set the username and password we have added in one of the previous steps.

Embed Grafana in WinCC Unified

In this scenario we will host Grafana over the IIS from WinCC Unified. So that it comes from the same origin and that we do not come over a CORS (Cross-Origin Request Blocked) problem.

What is needed to allow Grafana to be embedded in another application is to set allow_embedding = true in the Grafana configuration file.

To host Grafana over the IIS the following settings must be made:

Add a URL Rewrite to your IIS configuration file. Change “desktop-khlb071” to your computer where Grafana is running on. Restart the Webpage with the IIS Manager.

The IIS configuration file can be found here: (C:\Program Files\Siemens\Automation\WinCCUnified\SimaticUA\web.config)

                <rule name="grafana" enabled="true" stopProcessing="false">
                    <match url="grafana(/)?(.*)" ignoreCase="true" />
                    <action type="Rewrite" url="http://desktop-khlb071:3000/{R:0}" appendQueryString="true" logRewrittenUrl="false" />
                </rule>      

Change the following configuration of Grafana (defaults.ini). Change the domain to your computer name where Grafana is running on. It must be the same name what you use in the IIS configuration file!

# The public facing domain name used to access grafana from a browser
domain = desktop-khlb071

# Redirect to correct domain if host header does not match domain
# Prevents DNS rebinding attacks
enforce_domain = false

# The full public facing url
root_url = %(protocol)s://%(domain)s:%(http_port)s/grafana

# Serve Grafana from subpath specified in `root_url` setting. By default it is set to `false` for compatibility reasons.
serve_from_sub_path = true

# set to true if you want to allow browsers to render Grafana in a <frame>, <iframe>, <embed> or <object>. default is false.
allow_embedding = true

Automation Gateway Video Tutorial

In this tutorial, I will guide you through the essential steps to set up the Automation Gateway, harness the power of YAML extensions in Visual Studio Code for configuration, and connect various devices, including OPC UA, MQTT, and PLC4X devices. I will show how to integrate the values from the devices to the Gateway’s OPC UA server and how to use the MQTT interface to get the values from the devices via a MQTT client. Additionally values from the connected devices will be logged to a Influx database.

  • Setup 0:00 – 5:30
  • YAML-Extension 2:31 – 4:15
  • OPC UA Driver: 5:31 – 10:25
  • MQTT Interface: 10:25 – 13:40
  • MQTT Driver: 13:40 – 16:42
  • PLC4X Driver: 16:42 -19:53
  • Database Logger: 19:54 – 24:56
Setup 0:00 – 5:30
YAML-Extension 2:31 – 4:15
OPC UA Driver: 5:31 – 10:25
MQTT Interface: 10:25 – 13:40
MQTT Driver: 13:40 – 16:42
PLC4X Driver: 16:42 -19:53
Database Logger: 19:54 – 24:56

Bring MQTT Payload to OPC UA?

 I wanted to get my Home-Automation values to SCADA, it’s a “self-made” JSON message format. I tried it with Ignition and the MQTT Module. Btw.: it’s great that they have the Makers Edition for non-commercial use at home 👍. But I don’t know why, it only got one topic and one value from my MQTT Broker, and it did not receive any updates. Don’t know what went wrong…

Anyhow, I decided to add a custom JSON format to the Automation-Gateway.com. It’s simple, just define the JSON-Path to the value and optionally to a timestamp in milliseconds since epoch or to an ISO 8601 format.

Now I can use the Automation-Gateway’s OPC UA server in any SCADA system to visualize my MQTT values…

Here is the config.yaml configuration file for the Automation-Gateway.

Servers:
  OpcUa:
    - Port: 4841
      Enabled: true
      LogLevel: INFO
      Topics:
        - Topic: mqtt/home/path/Original/#
Drivers:
  Mqtt:
    - Id: "home"
      LogLevel: INFO
      Host: 192.168.1.3
      Port: 1883
      Format: Json
      CustomJson:
          Value: "Value"
          TimestampMs: "TimeMS"

MQTT for Unity

“MQTT for Unity” is a Unity Package designed to seamlessly integrate MQTT (Message Queuing Telemetry Transport) functionality into Unity projects, offering a user-friendly solution for enabling real-time communication and data exchange within Unity applications.

Tested on Windows, OSX, WebGL, UWP + HoloLens2, and Android. iOS not tested, but should work as well.

You can find it at the Unity Asset Store here

Key Features:

  1. Streamlined Integration: “MQTT for Unity” provides a straightforward and hassle-free integration process, enabling developers to quickly set up MQTT communication in their Unity projects.
  2. Real-Time Communication: Harness the power of MQTT to establish real-time communication channels within your Unity application, perfect for multiplayer games, IoT applications, and more.
  3. Customizable Configuration: Easily configure MQTT parameters, such as broker settings, topic subscriptions, and message handling, to tailor the communication to your specific project needs.
  4. Cross-Platform Compatibility: “MQTT for Unity” is designed to work seamlessly across various Unity-supported platforms, including Windows, OSX, WebGL, UWP + HoloLens2, and Android. iOS not tested, but should work as well.

With “MQTT for Unity,” developers can unlock the potential of MQTT communication in their Unity applications without the complexities of manual integration, making it an essential tool for creating interactive and connected experiences in Unity.

Online documentation can be found here.

SCADA Real Time Data and Apache SPARK

The integration of SCADA with Spark and WinCC Open Architecture offers a powerful and versatile solution that combines real-time data processing, advanced analytics, scalability, and flexibility. This combination empowers you to optimize industrial processes, make data-driven decisions, and stay ahead in a rapidly evolving technological landscape.

By utilizing my 5-year-old project that implemented a native Java manager for WinCC Open Architecture, I have enabled the integration of SCADA with Spark for the current WinCC OA Version 3.19.

Very simple example is to analyze tags and the corresponding amount of values in your SCADA system can provide valuable insights into the distribution and characteristics of the data.

res = spark.sql('SELECT tag, count(*) count FROM events GROUP BY tag ORDER by count(*) DESC')
data = res.toPandas()
plt.figure( figsize = ( 10, 6 ) )
sns.barplot( x="count", y="tag", data=data)
plt.show()

Another simple example is to calculate the moving average of 10 preceding and following values for a given data point in a time series, you can use a sliding window approach:

data = spark.sql("""
SELECT ROUND(value,2) as value, 
  AVG(value) OVER (PARTITION BY tag ORDER BY ts 
  ROWS BETWEEN 10 PRECEDING AND 10 FOLLOWING) avg 
  FROM events 
  WHERE tag = 'System1:ExampleDP_Trend2.'
  ORDER BY ts DESC
  LIMIT 100
  """).toPandas()
data = data.reset_index().rename(columns={"index": "nr"})
sns.lineplot(data=data, x='nr', y='value', label='Value')
sns.lineplot(data=data, x='nr', y='avg', label='Average')
plt.show()

By leveraging the distributed file system, you can take advantage of Spark’s parallel processing capabilities. The distributed file system ensures that the data frame is partitioned and distributed across the nodes of the Spark cluster, enabling simultaneous processing of data in parallel. This distributed approach enhances performance and scalability, allowing for efficient handling of large volumes of real-time SCADA data.

I have achieved real-time data streaming from WinCC OA to a Spark cluster with a Websocket-Server based on the Java manager. This streaming process involves continuously transferring SCADA real-time data from the WinCC OA system to the Spark cluster for further processing and analysis.

url='wss://192.168.1.190:8443/winccoa?username=root&password='
ws = create_connection(url, sslopt={"cert_reqs": ssl.CERT_NONE})

def read():
    while True:
        on_message(ws.recv())
Thread(target=read).start()

cmd={'DpQueryConnect': {'Id': 1, 'Query':"SELECT '_online.._value' FROM 'ExampleDP_*.'", 'Answer': False}}
ws.send(json.dumps(cmd))

Once the data is received by the Spark cluster, I store it as a data frame on the distributed file system (DFS). A data frame is a distributed collection of data organized into named columns, similar to a table in a relational database. Storing the data frame on the distributed file system ensures data persistence and allows for efficient processing and retrieval.

schema = StructType([
    StructField("ts", TimestampType(), nullable=False),
    StructField("tag", StringType(), nullable=False),
    StructField("value", FloatType(), nullable=False)
])
df = spark.createDataFrame(spark.sparkContext.emptyRDD(), schema)
bulk = []
last = datetime.datetime.now()
def on_message(message):
    global bulk, last, start
    data = json.loads(message) 
            
    if "DpQueryConnectResult" in data:
        values = data["DpQueryConnectResult"]["Values"]
        for tag, value in values:
            #print(tag, value)
            data = {"ts": datetime.datetime.now(), "tag": tag, "value": value}
            bulk.append(data)
            
    now =datetime.datetime.now()
    time = datetime.datetime.now() - last
    if time.total_seconds() > 10 or len(bulk) >= 1000:
        last = now

        # Create a new DataFrame with the received data
        new_df = spark.createDataFrame(bulk, schema)
        
        new_df.write \
            .format("csv") \
            .option("header", "true") \
            .mode("append") \
            .save("events.csv")
        
        bulk = []

Once the SCADA data is stored as a distributed data frame on the Spark cluster’s distributed file system, you can leverage Spark’s parallel processing capabilities to efficiently process the data in parallel.

df = spark.read \
    .format("csv") \
    .option("header", "true") \
    .option("timezone", "UTC") \
    .schema(schema) \
    .load("events.csv")
df.createOrReplaceTempView("events")

By combining SCADA (Supervisory Control and Data Acquisition) with Spark’s powerful data processing capabilities, I have created a solution that can handle large volumes of real-time data efficiently. This enables faster and more accurate decision-making based on the insights derived from the processed data.