We’re using application monitoring at work for making sure any issues with our services won’t go unnoticed. Notification email is always sent in case of emergency, but the emails often get lost in people’s inboxes and the investigation of the problem is delayed. Therefore, I wanted to create a real world counterpart of the alert emails to inform us when our services encounter issues. Since a single email may go unnoticed, but bright flashing light in the middle of the room will not.
Therefore, I needed to create a link between physical and digital world. I had an extra Raspberry Pi board lying around, which could be the missing link. In addition to Raspberry Pi, there are the three major components in this project:
Raspberry Pi 3 isn’t capable of driving 12 V light, I used the custom board I had made earlier. Details about the custom board can be found in here:
Versatile control board for Raspberry Pi 3
For my project of building a real world warning light for application monitors, I needed a way to control 12 V beacon…
Custom board supplies the power to the beacon light based on the 5 V signal it receives from Raspberry Pi.
The wiring is quite simple as the controller board has 40-pin connector for connecting the Raspberry Pi and a spring-cage terminal block for the beacon light. Here’s a picture of the boards in the official Raspberry Pi case:
Software part consists of an Android Things app running on Raspberry Pi 3. The app is just a “headless” Android app that is launched automatically on Boot and interacts with the hardware peripherals. The main steps are:
- Connect to Wifi network
- Get the current status of the application monitors from Monitis
- Turn the beacon light on/off accordingly
First I declared a home activity, which is created when Raspberry Pi boots up.
Because the app should continuously poll our application monitoring system to know if a monitor is failing, I used a Handler to schedule a runnable every 30 seconds. In onCreate method of the home activity, I declare the handler and the runnable that it should call on startup and then every 30 seconds:
I wanted automatically to connect a known WiFi network, because I didn’t want to go through the hassle of configuring the wifi settings, whenever I move the device to a new place. The network is using WPA security protocol, which can be configured as follows:
Then I enable and declare the previously configured network so it can be connected to. AddNetwork function returns a network ID, which is used to connect to it.
Even though this is run immediately after the Raspberry Pi has booted up and the board shouldn’t be connected to any network, I still disconnect it from all networks, before connecting to the new network.
Monitis has Java SDK, which offers an easy way to integrate to their API. The API requires an API key and a secret key, which can be obtained either using the API (documentation) or directly from their dashboard (Tools → API → API key).
The code loads the last result of all external monitors from all locations and checks if any of them is currently failing (status is “NOK”). This doesn’t take into account of the alerts rules defined for the monitors, but it’s enough for my purposes. For example, if the monitor is configured to alert upon two consecutive failures from two locations, single location may be failing, while the monitor should not.
For interacting with the custom board, I created a custom (user) driver to abstract the communication details from the app itself. The board has two outputs and therefore the driver requires two GPIO pins that are connected to the board:
Driver can be then instantiated by the app and called when the app receives the monitor status from Monitis API. The input signals in the custom boards are active when pulled to ground, so therefore we must set the GPIO as active low. Meaning that when its value is set to true, Android will pull it to ground therefore turning on the regulator on the board.
Monitis related logic is in a separate service class, which runs the query in a separate thread and returns the status in a callback: