Runscope API Monitoring    Learn More →

Building a Steam Powered IoT API with Thingsboard

By Joshua Curry on .

This is the fifth and final post in our Featured Guest Series! Joshua Curry shares his experience building an IoT project with a steam engine, Thingsboard, Raspberry Pis, and Runscope.

If you're interested in being a part of our next series, fill out this short form and we'll get in touch with you for our next run.

The project began late at night, as all good steam projects should. I had been thinking about some of the projects I’ve built with microcomputers like the Raspberry Pi and wanted to vault them out of the bland novelty of blinking and beeping boxes.

I have a model steam engine I inherited from a tinkering uncle and thought it would be a gas to try and make an IoT device out of it. I also have experience in API production and consumption, so I wanted it to be a relevant POC for devs trying to integrate the physical world with the virtual.

Bridging 300 years of technology seemed like an appropriate way of taking some of the hot air out of the IoT hype. It was also a chance to really explore modern API testing tools such as Runscope.

A photo of a Dampfmaschine D10 steam engine

The goal of the project was to take a machine without a natural digital context and enable remote monitoring and control through a standardized API. One of the challenges I wanted to take on was how to differentiate a hardware failure versus an API failure and then pass that response on for debugging by a dev down the line.

Despite planning to spend a few hours testing sensors, I found myself immersed in the world of IoT API provisioning for the next week.

Setting Up The Project

I started with two Raspberry Pis and set them up with fresh OS installs and sensor libraries. From a practical standpoint, being able to monitor and manipulate multiple devices is crucial.

After trying some of the heavy hitters like AWS IoT and IBM Watson IoT, I settled on a much smaller open source IoT project called Thingsboard. The enterprise players have a raft of features that are thick with dependencies on other services they offer. By choosing the smaller package, I was able to provision a simple droplet on DigitalOcean.

A screenshot of the website main page

The base IoT capability of the devices is to send MQTT messages to an external broker. Sending MQTT back and forth is fairly trivial, but getting them into a state that’s readable by an API requires a platform. Thingsboard digests MQTT and offers distinct REST API endpoints for each device, as well as an administrative API that offers historical data queries.

Running on the Raspberry Pi, the following python snippet sets up a basic connection to the remote Thingsboard install I had started on my Digital Ocean droplet:

client = mqtt.Client()
client.connect(THINGSBOARD_HOST, 1883, 60)


sensor_data['temperature'] = round(int(temp))    # Boiler temperature
sensor_data['humidity'] = round(int(humidity))    # Steam detect
sensor_data['snd'] = snd                # RPM threshold
client.publish('v1/devices/me/telemetry', json.dumps(sensor_data), 1)

After installing Thingsboard and doing some basic configuration, the API was exposed. I created a free trial account on Runscope and was able to get some basic scheduled tests going within a few minutes. That’s when the API problems began, and I hadn’t even lit a fire yet.

API Testing and Debugging

The most common error was an authentication failure. So, I kept the tests running on my Runscope dashboard as I dug into the docs, trying different out different API configurations. It turns out that Thingsboard needed an extra setup string before the access token.

One Runscope feature I discovered was to have multiple API tests running simultaneously so that I could experiment up and down the URI hierarchy at the same time. For example, I had one test that just requested the name of the device and another that actually requested the latest telemetry post. The first indicated a pass/fail of the device API and the second included parameters that depended on the time period of the data. That was useful for testing parameter combinations for related but distinct resources.

As debugging progressed, the steady march of red error bars along my Runscope dashboard visualizer gave way to flashes of green. I was able to compare test results over time, with verbose request and response payloads from each time period. Success became steady, so I felt confident the API was behaving well enough for trials.

A screenshot of the Runscope dashboard, showing a list view of API test runs with green and red checkmarks

Running the Steam Engine

It was time to pull the Dampfmaschine D10 (by Wilesco) off the high shelf it had been parked on for months. The box smelled vaguely of kerosene and gear oil. I was glad to find that I still had some of the little fuel cubes they give you to light the boiler.

Another photo of the Dampfmaschine D10 steam engine, showing the two Raspberry PIs connected to it

I filled the boiler using the tiny funnel and pushed aside my Raspberry duo for space. Steam engines aren’t very complicated machines, so there wasn’t much to assemble. I lit one of the small fire cubes and slid in the drawer underneath the boiler. Then, I waited. It takes a while to build up enough pressure.

I took the temperature module from one Raspberry Pi and placed it near the boiler and then connected a microphone module near the primary cylinder. The idea was to measure boiler temperature for device state and then use the microphone to measure the cylinder action threshold, i.e. whether it's running or not.

Soon, a weak chirp built to a solid whistle as the release valve signaled peak pressure. The wheel began to turn in a lurching loop and was quickly spinning at full RPM.

My terminal window began to ripple with streams of MQTT messages from both Pis as the sensors spit out data. The Thingsboard telemetry window dutifully relayed the data, and I switched over to my Runscope tests to see the results.

A screenshot of the dashboard, showing the Latest Telemetry tab with values for the humidity, sound, and temperature sensors

There were plenty of errors. Now disappointed, but curious, I noticed the headers I set up were different for some of the tests. Luckily, it was a noob mistake; I had used X- Authentication instead of X-Authorization as a header. After fixing it and restarting the schedule, I saw a succession of green check marks on my tests.

The API responses were meaty enough to begin creating Runscope assertions for various states. One for API up or down, another for boiler sensor data/no data/fail, another for the cylinder sound monitor timestamp, and the last one for telemetry update time.

That last one was the goal of the project. If the Raspberry Pis are on and functioning, they will send telemetry (MQTT) even if there is no new sensor data. If they have crashed or power is off, the telemetry will stop. I can set a query for when the last successful telemetry was sent and if it's not recent, trigger an alert with a failure message and a time when it failed.

Thus, Runscope can be set to email me if the API goes down, the steam engine has stopped, or Raspberry’s are not responding. All different cases and priorities.

Wrapping Up

Runscope was a really useful tool to have in this project. IoT has both software and hardware challenges. It took care of the software monitoring while I focussed on the hardware, to make sure it didn’t explode in my face.

IoT is about much more than light bulbs and fitness trackers. The manufacturing industry is in the process of updating and integrating vast amounts of legacy (and proprietary) monitoring applications. Yielding modern, standards compliant APIs for machine monitoring and control is the task of armies of engineers right now.

This model steam engine is pretty low tech, but the project illustrates a viable approach to modernizing legacy machinery. It was also fun to do and made use of a diverse set of modern tech tools that aren’t always seen as related.

Categories: featured guest series, IoT, testing

Everything is going to be 200 OK®