Adding sound to the Aludel Elixir based Reference Designs

Sound can be a great addition to your product: alert users to problems or give feedback from actions like a button press. Sometimes, it’s also just great for demos; triggering a device to play the mario theme over cellular brings a certain joy for me, despite the number of times I’ve done it.

Today I want to add sound to the Aludel Elixir, our internal development platform.

We’re offering a limited number of Aludel Elixir boards for sale to our community members. You can sign up on this form to be notified when they’re available. The Elixir is our main board for developing reference designs and is immensely extensible via the MikroElektronika Click Board ecosystem.

Making the code more portable

The source of the PWM buzzer driver code started in the thingy91_golioth repo. I’ve written about this on the blog before, including how we created a framework for writing “songs”. Just recently I added another tune (“Ode To Joy”) without much hassle, a testament to building some structure into your code so you don’t have to replicate work each time you need to make a change.

Previously the thread that plays the notes was directly in app_rpc.c (and .h) because we were triggering all of the songs from a Remote Procedure Call. Mike had already broken out the beeper code into a separate file when he revised the thingy91_golioth repo to support the Thingy91X (more on how those changes work below). Now the code lives inside app_buzzer.c (and .h). That required that we add the new .c file to the CMakeLists.txt and have any relevant functions or variables in the header file that need to be accessed outside of those functions. We call the play_beep_once() function from main.c when a button is pressed, for instance.

I moved the beeper code into the RD template, including pulling over the broken out files that contain the thread that controls the PWM. I also needed to move any calls I wanted into main.c, including calling a buzzer init function. Then I modified the logic in app_rpc.c that handles incoming commands so that we can call the correct function (like play_mario_once()). Lastly, I needed to make sure that the devicetree was set up properly. In the case of the Aludel Elixir, the PWM was set up in the board files (aludel_elixir_common.dtsi) but the alias for the PWM pin that was in the board files did not match what I was calling when I initialize the function. A quick change to the aludel_elixir_ns.overlay fixed the alias for my build and everything was plumbed together properly.

Only compile for certain boards

One new thing to me was encapsulating code based on which board we’re building for. KConfigs are not new, nor is the fact that we can selectively compile code based on that KConfig, including a module or library being included (foreshadowing!).

You might consider this for your Zephyr repo if you’re targeting different revisions of hardware, or even entirely different boards. The latter is less common, but we have written about setting up board definitions for hardware revisions and it’s likely that a sensor or a hardware element might live on one board vs another. For instance, the Thingy91 has a buzzer, while the Thingy91X does not! (Mario, we hardly knew ye)

Future work: Making it a module or library

If you’re really going to reuse code, you should standardize it for your various projects. There are two levels of possibility here.

First, you can just turn it into a module and pull in the code as a Zephyr module. Primarily this helps with revision control, keeping the module’s version tracked in west.yml and placing the code in a known location in your project like modules/lib/<your_module>. Keen eyes will note this is also the directory where the Golioth Firmware SDK resides.

Taking it further, we can write a device driver with a custom API. This is a more involved approach, but one that newcomers to Zephyr often ask about. It involves writing code to crawl the devicetree and initialize your module. It will instantiate more than one device in the event there are multiple matching nodes in the devicetree entry. You present a unified set of calls that are accessible to different functions; in the case of the buzzer, we’d only really need to create a play_song(<songchoice>) interface.

Both methods clean up your code because you’re not copying and pasting into your design, you’re calling into external functions and APIs. They also both implement KConfig symbols to help selective code compilation in other parts of your code.

We’re always updating our Reference Designs

At Golioth, we are continually refining our hardware, firmware, and Cloud solutions to enable our clients’ IoT projects. This includes rolling new functionality into our Golioth Firmware SDK and our Reference Design Template. If you’d like to see everything in one place, check out our recent Reference Designs or consider picking up an Aludel Elixir and experience of end-to-end IoT connectivity on your bench.

Chris Gammell
Chris Gammell
Chris is the Head of Developer Relations and Hardware at Golioth. Focusing on hardware and developer relations at that software company means that he is trying to be in the shoes of a hardware or firmware developer using Golioth every day. He does that by building hardware and reference designs that Golioth customers can use to bootstrap their own designs.

Post Comments

No comments yet! Start the discussion at forum.golioth.io

More from this author

Related posts

spot_img

Latest posts

Guide to Securely Store Credentials on an nRF91 Modem

The Nordic nRF91 modems include secure storage for TLS credentials. This may be used to authenticate with Golioth. The assets are stored separately from the firmware, and once written, they cannot be read back from the device. This guide shows the process of storing and using credentials.

How to Add Golioth to an Existing Zephyr (or NCS) Project

Golioth removes the pain of connecting constrained devices to the cloud. This post shows you how to add Golioth to your existing Zephyr project and get your first device connected.

Manufacturing A Mini-fleet: Provisioning And Updating 10 Thingy91’s

Golioth demonstrates one approach to provisioning IoT devices during manufacturing. Learn some of the challenges you will face with flashing test firmware, generating and passing x.509 certificates to the devices, and using OTA to load final production firmware.

Want to stay up to date with the latest news?

Subscribe to our newsletter and get updates every 2 weeks. Follow the latest blogs and industry trends.