Display broken CircleCI builds with a Particle Mesh IoT Network

Particle Mesh IoT Network
Particle Mesh IoT Network

This project piggybacks on my last article Creating an IoT Scrolling Marquee with Slack and Particle.

Instead of a scrolling marquee displaying unique messages, I wanted to only receive notifications when a continuous integration platform failed a build. Things that would be helpful in this notification would be, what branch failed and who was the user.

Want to see the end product? Skip to the conclusion

CircleCI is a great continuous integration platform that I use a lot for personal projects, however I sure this approach (or similar) will work with most.

The CircleCI config.yml allows you to call a command on “Build Failed”.

This is great, because we can cURL a POST request to an API passing it our CIRCLE_BRANCH and CIRCLE_USERNAME.
Easy!

Once additional spin on my approach with this project is that I wanted to use Particle Mesh Networking. Essentially, a Particle mesh network consists of a Gateway device with network access, and one/may Endpoint devices. A much better explanation can be found here on the Particle blog: How to build a wireless mesh network.

My example is going to use the Argon wifi Gateway device, and 2 x Xenon Endpoint devices.

Gateway and Endpoint devices can communicate with each other by publishing or subscribing to events. Only devices on this network will have access to these events.

Knowing this, we can define a function on our Gateway device (Argon) which when called can then emit a custom event on our network. Our endpoints, which will be subscribing to the event, can then perform a specific action.

My plan is to have 1 Xenon endpoint operate a scrolling marquee, while the other Xenon endpoint displays a tally of the amount of broken builds. What this showcases is that in a workplace environment, we could have multiple devices on our network displaying notifications of broken builds and perhaps more detailed statistics.

All the source code can be found here.

CircleCI

First we need a build that will fail.
Create a circleci repo and define your .circleci/config.yml file. Make sure you then select your git repo in the circleci app.

version: 2
jobs:
  build:
    working_directory: ~/repo
    docker:
      - image: circleci/node:8
    steps:
      - checkout
      - run: npm install
      # npm script 'foo' does not exist in our basic package.json, so should fail
      - run: npm foo
      - run:
          name: Build Success
          when: on_success
          command: |
            if [ "$CIRCLE_NODE_INDEX" == 0 ]; then
              echo "Success"
            fi
      - run:
          name: Build Failed
          when: on_fail
          # update reference to path_to_your_api/display
          command: |
            if [ "$CIRCLE_NODE_INDEX" == 0 ]; then
              curl -d '{ "branch": "'"$CIRCLE_BRANCH"'", "user": "'"$CIRCLE_USERNAME"'"}' -H "Content-Type: application/json" -X POST path_to_your_api/display
            fi

Create a very basic package.json file for your project.

{
  "name": "my-failing-circleci-app",
  "private": true,
  "version": "1.0.0",
  "description": "a basic manifest",
  "main": "index.js",
  "scripts": {},
  "devDependencies": {}
}

Pushing this to github with cause circleci to build your project. First it will install npm dependencies, and then npm foo which shouldn’t existing in your package.json file, resulting in a failed build.

Source > .circleci/config.yml

API

I have taken the API I created in Creating an IoT Scrolling Marquee with Slack and Particle and just removed unecessary endpoints.

On /api/display the API will log into my device and call it’s display_broken_build function, passing it the branch and user values.

app.post('/api/display', async (req, res, next) => {
  const { branch, user } = req.body  

  if (!auth) {
    await Particle.login({username, password})
      .then(data => {
        auth = data.body.access_token
      })
      .catch(({ status, message }) => 
        res.status(status).send({ error: message })
      )
  }

  const message = `Broken Build! ${branch} | ${user}`
  await Particle.callFunction({
    deviceId,
    name: 'display_broken_build',
    argument: message,
    auth
  })
  .then((data) => res.send(message))
  .catch((err) => {
    console.error(err)
    res.send(`callFunction error: ${JSON.stringify(err)}`)
  })

  next()
})

All secrets in my .env have been updated to connect to my Argon Gateway device.

PDEVICE=particle_device_id
PUSER=particle_user
PPASS=particle_password

Once again the API is then hosted on now, and my now.json manifest exposes my route and secrets to the API.

> now secret add mesh-pub-user foo@bar.com

Once the API has deployed, update the reference to the endpoint in your .circleci/config.yml file.

Source > index.js

Argon Gateway

I’m using the Argon as my gateway device.

Using Particles’ Workbench (or Web IDE), I flashed the argon-publish.ino on my device.

You’ll notice on setup I define the function display_broken_build that accepts a command (string), and on the event that function is called via the API, I call a displayMessage function, which will then emit matrix_display_message out onto the network.

int displayMessage(String command) {
    Mesh.publish("matrix_display_message", command);
    digitalWrite(D7, HIGH);
    delay(1000);
    digitalWrite(D7, LOW);
}

void setup() {
    pinMode(D7, OUTPUT);
    Particle.function("display_broken_build", displayMessage);
}

void loop() {}

You’ll notice on setup I define the function display_broken_build that accepts a command (string), and on the event that function is called via the API, I call a displayMessage function, which will then emit matrix_display_message out onto the network.

Source > argon-publish.ino

XENON - Scrolling Marquee

The code for this device is very similar to the original scrolling marquee example.
The difference being that on setup is subscribes to the matrix_display_message event. Once received, call displayMessage, resulting in writing to the LCD.

Mesh.subscribe("matrix_display_message", displayMessage);

Source > xenon-subscribe-scrolling-marquee.ino

XENON - Counter

The code for this device is pretty simple.

I initialise a count of 0 and everytime matrix_display_message is published it calls logToDisplay, incrementing the count by one and sending it to the display.

#include "adafruit-led-backpack.h"

Adafruit_7segment matrix = Adafruit_7segment();
int count = 0;

void logToDisplay(const char *event, const char *data) {
  digitalWrite(D7, HIGH);
  delay(1000);
  digitalWrite(D7, LOW);
  count++;
  matrix.print(count);
  matrix.writeDisplay();
}

void setup() {
  Mesh.subscribe("matrix_display_message", logToDisplay);
  matrix.begin(0x70);
  matrix.print(count);
  matrix.writeDisplay();
}

void loop() {}

Source > xenon-subscribe-counter.ino.

Conclusion

Once the API was deployed, devices flashed, and failing CircleCI repo published, I had my own sweet IoT network of displays notifying me when a build had failed.

Hackster Instructions - Broken CircleCI builds and Particle Mesh

I sourced all hardware and components for this example, from Core Electronics