17 August 2021 / Last updated: 17 Aug 2021

How to make your own balenaBlocks: simple, drop-in edge app functionality

In a previous blog post, we introduced balena Blocks, intelligent, drop-in chunks of app functionality built to handle common tasks. Then we showed off some examples of blocks we have already made (like audio, sensor, and pulse). This post is going to explore the anatomy of a block in more depth, and hopefully inspire more people to make and share their own blocks.
balenaBlocks are drop-in bits of containerized functions for your next edge and IoT project
If you’ve been wondering, “how do I make my own balenaBlock?” and “how do I show it off to all of my friends and encourage others to use it?”, this is the article for you.

A reminder about balenaBlocks

A balenaBlock (from this point we’ll just use block) is a container image, which developers can add to their multicontainer application to provide functionality they need. The aim is to reduce the friction of application development by providing fragments of functionality, much like a code package provides to software developers. Similar to how developers don't want to have to write math and string manipulation functions themselves, IoT developers don't want to have to create containers for every function their application may need to work.
Why spend hours getting a browser to run well in a container, when the browser block already does it?

A quick, closer look inside of a block

On the inside, blocks have a common high-level architecture to them:
Here's a look at a balenaBlock's composition
They deploy one or more executables, run them on top of a balena base image, and wrap around them to make them easier to configure and manage. The wrapper can use the functions of the balena environment to discover and automatically configure the executable wherever possible, and to intelligently offer an opinion on how the executable is likely to be used.
You can find all of the blocks we've built so far over on the balenaHub.

What should become a block?

Just like with app stores and software package managers, more people contributing blocks improves the usefulness of the whole ecosystem. We at balena don't want to make all the blocks, and we don't want to be the gatekeepers of what is and is not a block, either. We want you, the community, to make pieces of functionality you have in your projects into something that others can benefit from.
We've found that a good rule of thumb for what is a good candidate for a block is "Would it do anything useful if it were deployed to a device on it's own?"
For instance, if you installed a database onto Intel NUC, it wouldn't be much use on it's own. So that sounds like a good candidate for a block, since other services could make use of it. Whereas a container categorising images and displaying the category on a screen: that sounds like an app.

How to make a block

OK, so let's assume I've done a good job of convincing you to get involved, and you're ready to make a block... how do we go about that? Let's lay out a 5 step plan:

Step 1: Identify a pattern

There's very little point in creating a block nobody needs, so start off by looking for patterns of repeated functionality in your and others projects. For example, it was pretty clear to my colleague (and possibly nicest man in the world) Hardware Hacker Alan that lots of projects needed a feed from an attached camera. He had made some AI projects, he'd seen the kerberos (surveillance camera project) blog post and heard others discussing projects like a cat cam, a hydroponic web stream and a baby monitor. All of those things needed a camera feed.
Alan had identified a pattern where a block could prove useful.

Step 2: Find an implementation

The next step is to solve the need, in this example get a camera feed, by finding (or making if you're a hotshot developer!) something that provides the function. Alan took a look around and found that balena already had a project balenaCam which was getting a camera feed and streaming it over WebRTC. The app was, at its heart, using gstreamer to capture a video feed, in this case from any attached camera.

Step 3: Find the boundaries and interfaces

Now we have an implementation, we need to figure out the boundaries of the block. You don't want your block to be too specific, or it limits the scope of other use cases that can make use of its functionality.
Alan didn't want to take the whole of balenaCam as his block, since that would only prove useful for anyone building something on top of WebRTC. Instead, he wanted to just take gstreamer's output, and make it possible to send that somewhere else as an RTSP (Real-Time Streaming Protocol) feed.
Once you know the boundary, you can then consider how another service (or block) can interface with your block. It may be that your block takes input from another service, like the connector block takes data from an MQTT broker, or the Audio block which takes a pulse audio source.
Alan decided his block would send an RTSP feed to somewhere else - so his interface is actually a push to another service.

Step 4 (optional): Wrap it in balena magic

The thing that can make the difference between just an executable in a container, and a balena block, is the way you can wrap it in some code or a bash script, to make use of some balenaCloud features. The browser block is a good example to use here. Getting Chromium to run inside a container, although useful, is only some of what the block provides. It also wraps the browser in some code, so that it can be configured easily. The code does things for itself, like detect the resolution of the attached display, and size the output window accordingly. It reads in any environment variables, so that a user can set things like whether the cursor shows or is hidden, and what URL to show on startup.
Another thing your code can do is implement an API so that other services can interact with it at runtime. The browser block does this to allow the URL to be changed and things like GPU acceleration and kiosk mode enabled or disabled. The Fin block provides an API so that other services can perform actions on the balenaFin coprocessor, like put the device to sleep.
Alan's capture block doesn't need an API, but he has decided that users should be able to configure it with environment variables, so that they can decide where the RTSP feed should be sent, and which port to use.

Step 5: Dress it up for balenaHub

The way applications and blocks appear in balenaHub is driven by the code in your GitHub repository. So there are a few things to add, which drive this behavior, namely a logo image and a contract. The latter sounds like a big deal, but actually it's just a text file with a certain (YAML) format.
The easiest thing to do is just to take a copy of the contents of the audio block's contract and paste it into a file named balena.yml at the root of your code repository. Now change the various bits as they relate to your block, such as the name, a description, and then types of device you have tested the block on.
NOTE: The logo is a PNG file, and the only constraints are that it should be 512x512 pixels square, and pointed to by the contract file above.
Here's an example:
name: capture-block
description: >-
  Sends an RTSP video feed from a connect camera to a configurable endpoint
version: 0.0.1
type: sw.application
assets:
  repository:
    type: blob.asset
    data:
      url: 'https://github.com/balenablocks/capture
  logo:
    type: blob.asset
    data:
      url: 'https://raw.githubusercontent.com/balenablocks/capture/master/logo.png'
data:
  defaultDeviceType: raspberrypi4-64
  supportedDeviceTypes:
    - raspberry-pi
    - raspberry-pi2
    - raspberrypi3
    - raspberrypi3-64
    - raspberrypi4-64
Check out our documentation on balena.yml to ensure you’re on the right track.

How to contribute a block

Now that you’ve made it this far, you know what balenaBlocks are, and which ones are available to use today. Maybe you’re thinking, “Wow, I have some ideas for blocks that would be useful for your projects!”
I challenge you to contribute to balenaHub.

Contributing at-a-glance

You'll want to start a balenaCloud account (first ten devices are free and fully-featured), and create a fleet that uses your block.
Contributors then need to go to balenaHub and fill out a quick form to submit your block to be reviewed and added to balenaHub. At some point, we’ll make this even easier by offering a self-submission method for balenaBlocks.
Get even more information about fleets and blocks on balenaHub.

Until next time

balenaBlocks are pre-built container images which application developers can add to their multi-container apps to provide useful functionality. Their aim is to reduce the friction of creating IoT applications which enables rapid prototyping and development.
Why don't you see what you can cook up using the blocks we have today? If you have any ideas for blocks you'd like to see developed, or would like to contribute towards, head over to our forums and start a discussion. We'd love to know what people are making, and what would reduce their friction.
by Phil WilsonProduct Builder