Over a million developers have joined DZone.

Visualizing Smog Sensor Data With Vert.x, Prometheus, and Grafana

DZone's Guide to

Visualizing Smog Sensor Data With Vert.x, Prometheus, and Grafana

Learn about feed data gathered from smog sensors in Germany to a local server so that it can be used to visualize long-term trends.

· Big Data Zone ·
Free Resource

Download Microservices for Java Developers: A hands-on introduction to frameworks and containers. Brought to you in partnership with Red Hat.

Air pollution is a major problem in many cities around the globe. Some people in Stuttgart, Germany have developed cheap smog sensors that people can install on their balconies and other convenient places and then report data to a central site. I have written about that on OpenSource.com. The data is sent to a central server and from there, it is visualized on a map. At the time of writing the previously mentioned article, there was no way of seeing how the value had changed over time. Meanwhile, there was a visualization of the last 24 hours available on the map.

The sensor is also able to send data to an additional target; for example, a local database. Thus, I decided to feed the data to a local server for long-term storage to visualize trends.

As I was playing with Prometheus at this time, I thought that feeding the data into it may be a good use case for further exploration. Now that Prometheus wants to scrape the data and the sensor is only pushing the data, it became clear that I need an intermediate gateway that solves this mismatch and which also translates the data format the sensor sends into the format Prometheus expects.

To implement the gateway, I choose Vert.x, a modern-day reactive framework that runs on the Java Virtual Machine (JVM). I was first considering other options, but it turned out that Vert.x’s implicit handling of connections and threading made my life easy.

The gateway receives HTTP POST requests from the sensor with measurement data in InfluxDB format. It exposes the data to GET requests on /metrics in the Prometheus text format.

Finally, the data stored in Prometheus is visualized via Grafana.

I will now show some pieces of the code; the full code is available on GitHub.

Gateway Code

The code consists of two main parts for the two endpoints described above. I have implemented both in the same Verticle.

public class MainVerticle extends AbstractVerticle {
  // Label on dust data
  private static final String TYP_FS = "{typ=\"fs\"}";
  public void start(Future fut) {
    // [... see below ...]
            // Set the port we listen on to 10080
            config().getInteger("http.port", 10080),
            result -> {
              if (result.succeeded()) {

This first part creates a new verticle, the inside of start() sets the HTTP port it listens on to 10080, and then it tells Vert.x that the configuration is ready. Vert.x then starts its accept loop and dispatches incoming requests.

The next step is now to define handlers for the endpoints to do the actual work. In the handlers, we need to be careful not to block or implement slow operations; Vert.x may decide to abort the handler in this case.

Let’s look at the handler for scraping of data first:

      .handler(routingContext -> {
  HttpServerResponse response = routingContext.response();
  // If we have no data yet, return a 204 'no content'
  if (values.isEmpty()) {
  // Construct the response
  StringBuilder builder = [...]
  // send it back
      .putHeader("content-type","text/plain; version=0.0.4")

Now let’s look at the POST handler. This is a tiny bit more complicated as we need to explicitly tell Vert.x to that we want to obtain the body of the request. On top, we also want to get the sensor ID, which is encoded in a Http-Header.

// We need the body, so we need to enable getting it.
    // Bind "/" to our fs-input
          .handler(routingContext -> {
      // Get the sensor id from the header
      String tmp = routingContext.request().getHeader("X-Sensor");
      // Get the measurement data
      JsonObject body_json = routingContext.getBodyAsJson();
      JsonArray value_array = body_json.getJsonArray("sensordatavalues");
      HttpServerResponse response = routingContext.response();

Building and Running the Code

You can check out the full code from GitHub and then build it via:

$ mvn install

This creates a self-contained Java archive in the target directory, which contains our Verticle along with all the Vert.x code that is needed to run it. Thus, running is as simple as:

$ java -jar target/fsgw.jar

Configuring the Other Pieces

For the setup, I run the Gateway, Prometheus, and Grafana on the same host, with an IP of

For the Sensor

Go to the sensor configuration page, select Send to own API, and enter the IP and port of the Gateway like this:

Sensor configuration

After this is done, save the configuration and reboot the sensor.


Prometheus needs an external configuration file prometheus.yml that gets an additional scrape config for our sensor:

  - job_name: 'feinstaub'
    scrape_interval: 30s

      - targets: ['']

As the target, you enter the IP address and port from the Vert.x gateway. After this is done, you can start Prometheus and pass the path to the configuration to it:

$ prometheus -config.file /usr/local/etc/prometheus.yml


Start Grafana and point your browser at port 3000; the default login is admin/admin.
To set up Grafana, one needs to define a datasource of type Prometheus that points to the Prometheus server at http://localhost:9090/. Once this is done, we can set up a new dashboard and add graphs to it.

In above screenshot, you see the query editor. As the code in the gateway is set up to put labels on the exported items according to the type (the sensor can also report temperature and humidity), it is possible to query for the values with a label query of {typ="fs"}.


This article shows an end-to-end example of an IoT device sending data in a certain format. A gateway is implemented with the help of the Vert.x framework, which does all the magic of connection handling, etc. Finally, the data is stored in a Prometheus time series database and graphed via Grafana.

Download Building Reactive Microservices in Java: Asynchronous and Event-Based Application Design. Brought to you in partnership with Red Hat

big data ,data visualization ,sensor data ,vert.x ,prometheus ,grafana ,tutorial

Published at DZone with permission of

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}