Tonuino - alternative Firmware

Project stats

  • Difficulty: hard 5/5
  • Cost: 0€
  • Time: ~400h


birdshouse open inside of the bird's house Guitar Amp design inside of Guitar Amp
Some fabrication examples of Tonuino - alternative Firmware.

C++ A software written for the Arduino Framework that controls an Mp3 Player (with SD card), an NFC Tag Reader, and provides user controls that together work like a jukebox that can be controlled via NFC Tag. For example, an NFC tag can be linked to a folder on the SD card containing audio files. The tag will also store information about the requested play mode for this album, e.g. “Random”. Once a linked tag is placed on this jukebox, the configured folder will play with the selected playmode. User controls allow to select the track, change volume and even to navigate a voice menu that allows deleting a tag, linking/ configuring a tag, and to lock/ unlock the input keys if required.


Idea and fantastic execution found on Thorsten Voß’s blog. It began with a feature (encoder support) that I wanted to add. Unfortunately, the original code was a single-file application with several thousand lines of code and poor readabilty, so I had a hard time getting my changes in. So I re-wrote the code from scratch for better structure, readability, maintainability, and extensibility. By doing this, in parallel I read some books about Object Oriented design in C++, Clean Code and Software Design Patterns and applied some where I tought they’d fit in. This way, I was able to both improve modularity and readability of the code, plus I taught myself how to write code in a “bigger” project.

Hurdles to overcome

This was first real OO C++ project, and, maybe, it was a little big to start with. All the new-to-me concepts, writing to an interface, first-time usage of Platformio as IDE, utilizing the googletest unit test framework, using coding patterns like factory or Dependency Injection took nearly a year of evenings before this project could be completed. And there’s always something left to refactor and to improve.

Quick facts

  • Unit test suite of 250+ test cases
  • Serial debug output configurable
  • Loosely coupled C++ OO architecture
  • Individual modules with clear tasks, scaleable and easily accessible for future feature add
  • custom Hardware abstraction layer; most of the code should be portable to other mcus without changes
  • custom Dependency Injection framework (Loader class)


  • Configurable user input (buttons or encoder)
  • Auto-Poweroff when no button pressed for a configurable time
  • Power on via “play” button press
  • Autoplay feature
  • Status Led feature
  • User Input lock/unlock feature
  • Multiple playmodes [Album, Random, SaveProgress, OneTrack] available per Nfc Tag
  • Voice menus for linking or deletion of Nfc Tags
  • Optimized for battery applications (e.g. powerbank) using sleep states
  • Low power consumption @5V: ~40mA in idle, ~75mA playing medium volume
  • Config file for init volume, lullabye timer etc.
  • Auto-recovery from stuck prompts
  • Voice prompts for most common state errors

Features not incliuded

  • No powerbank state of charge detection
  • No config settings menu (init volume, lullabye timer duration, standby duration etc.)


The Project is build for the Arduino Framework - tested on an Arduino nano board - using the PlatformIO IDE. The following sections show the design.

Project Module overview

Folder name in /lib Purpose
Arduino minimalistic close-to-Hardware implementations, not unit-testable
Arduino_HardwareAbstraction Hardware abstraction to enable portability and testing
Config System configuration parameters
Folder Playlist and playmode business logic
Loader Dependency Injection Framework
MessageHandler System messages and Debug Framework
Mp3 Mp3 control (Status, Folder, Prompts, Advertisements)
Nfc Tag control (Status, Read, Write, Delete)
PowerManager Control Status Led and Keep Alive based on system state
Tonuino Main task scheduler
UserInput Button or Encoder input handling
Utilities Timers, Led Control, Pin control
VoiceMenu Link / Delete / Config menu business logic

External Libraries

Auto-install through Platformio on initial build through Library Dependency Finder.

Class Diagrams

Class diagrams would have been too much non-automated work. Take this instead, please. Software module overview It should give an accurate understanding of how the software module are interacting and what APIs the modules are offering.

Get started!

It’s DIY time! Clone my repository to get started with. Read the document. Build it with PlatformIO. Below you can find what you need to get started with your unique speaker design.

How to build the solution



Unit tests

Unit tests are written with the gtest C++ unit test framework. The reside in the test/desktop folder. Note that googletest will require gcc with some libraries to be installed, how to can be found in one of my other projects. Once these prerequisites are complete and a simple ASSERT_TRUE(false); test fails as expected using the gtest environment, the project’s Unit Tests can be built and run using pio test -e desktop -f desktop, detailed out here, in the PlatformIO CLI (terminal).

Acceptance tests

Although these tests could be automated, it is much easier to just perform those tests by hand after you programmed your µC and put all electronic components together. Each line in the tables below is a test case on its own. The test suites (Heading name) do have a System Pre: property, that needs to be re-established prior to executing the individual test cases. If the expectation clause is met, the test is a PASS.

Switching ON

System Pre: System is OFF

Action Expectation
press Play/Pause button LED flashes slowly?
press Play/pause button Welcome prompt plays?

Switching OFF

System Pre: System is ON

Action Expectation
Playback on pause, no button input System switches off after a time?
System shutdown Farewell prompt plays?
System shutdown LED switched off?

Play Help Prompt

System Pre: System is ON

Action Expectation
Long press Play/Pause button Help prompt plays?
Help prompty playing Can be interrupted with any button press?

Behavior without Tag

System Pre: System is ON, no Tag present

Action Expectation
press Play/Pause button prompts “couldn’t find track” error?
press Next button prompts “couldn’t find track” error?
press Prev button prompts “couldn’t find track” error?
doubleclick Play/Pause button Delete Menu prompt played?

Behavior with Tag

System Pre: System is ON, linked Tag available

Precondition Action Expectation
no Tag placed place known Tag starts Playback of correct Folder?
active playback press Next button plays next track?
active playback press Next button next track in accordance with selected playMode of Folder?
active playback long press Next button increases volume?
active playback press Prev button plays previous track?
active playback press Prev button previous track in accordance with selected playMode of Folder?
active playback long press Prev button decreases volume?
active playback press Play/Pause button pauses track?
paused playback press Play/Pause button resumes track?
active playback doubleclick Play/Pause button Lock prompt played?
active playback doubleclick Play/Pause button Playback resumes after Lock prompt played?
active playback doubleclick Play/Pause button locks button input?
locked button input doubleclick Play/Pause button Unlock prompt played?
locked button input doubleclick Play/Pause button Playback resumes after Unlock prompt played?
locked button input doubleclick Play/Pause button unlocks button input?
paused playback doubleclick Play/Pause button Playback resumes after Lock prompt played?

Behavior with unlinked Tag

System Pre: System is ON

Action Expectation
place unlinked Tag plays LinkMenu Prompt?
navigate through LinkMenu plays Configuration Success Prompt?
place Tag again plays linked folder?


Material list

Minimal discrete parts approach

Amount Item Purpose
1 Arduino (e.g. Nano) Brains
1 Dfplayer Mini Mp3 Player Mouth
1 Micro SD Card Memory
1 MFRC522 Nfc Reader Psi-Sense
1 Bistable Relay 5V, e.g. HFE20 Coffee - no sleep
2 Diodes e.g. 1n4007 vene valve
1 PNP Transistor e.g. BC327 Coffee maker switch
2 Resistors 1k blood-stream conditioner
1 Resistor 220 dim blink strength
1 LED, color of choice blink
1 Powerbank/ 5V supply Food
3 push buttons, e.g. cherry MX keys pressure sensors
1 speaker, e.g.5W@4Ohms Lungs
1 USB-A plug Straw
1 housing Body

Additional coponents and tips

Jumper wires, PCB, Sockets, and expendable materials as you deem fit Same as for the “original” project; Alternatively buy one rotary encoder instead of push buttons and configure the project to use it. As the system is optimized for battery usage, take a bi-stable relay or a JFET transistor (with low gate voltage and low Source-Drain voltage drop) for keepAlive functionality mitigating additional current add through always-on relay coils. A cheap powerbank will last for many hours. Alternatively, e.g. 3xAA batteries can be used - but the Dfmini will really make some noise if not enough current can be supplied due to running resets (and may even get bricked through current surges, so beware).


Circuit Diagram