The Arduino Plug and Make is a maker-friendly kit that includes the latest Arduino UNO R4 WiFi along with a collection of Modulino boards. This document describes how the Plug and Make components may be assembled and then programmed to operate as a metronome with tone and light indicating the musical beat. It also leverages the UNO R4 WiFi’s matrix display to show the Beats Per Minute (BPM). An image of the assembled project is included as Figure 1. The operation is shown in Video 1. We can hear the metronome with emphasis on beat #1. We can also see the lights with blue for beat #1 and red for all others.
Download the code here: Metronome.zip (3.0 KB)
Figure 1: Image of the Arduino Plug and Make Kit components as assembled for the metronome project. The Modulino modules are electrically connected using daisy-chained Qwiic connectors.
Video 1: Operation of the metronome with 4-beat musical meter (4/4 time). Beat 1 is the high tone with blue LEDs, beats 2, 3, and 4 have a low tone with red LEDs.
Components used in the project
This project is built with components that are included with the Arduino Plug and Make Kit. The single addition is the cover from a red translucent Hammond 1591STRD enclosure. This red lens makes it easier to see and film the UNO R4 WiFi’s LED display matrix.
If you would like to learn more about the Arduino 12 x 8 LED matrix, please see this article which describes the Charlieplexing technique.
Modulino Tutorials:
You may also be interested in these DigiKey tutorials that explore the operation of the Modulino boards:
- Modulino Pixels
- Modulino Buzzer
- Bookmark for future installments
Physical connections
The Arduino Plug and Make hardware, with its Qwiic connections, greatly simplifies the assembly. Figure 1 shows one way to orient the components. The order of placement and electrical connection of the daisy chain is not critical as each Modulino board is individually addressed. You are therefore free to choose any orientation, limited only by the length of the connecting wires.
Software description
For a metronome, predictable timing is of utmost importance. The timing is especially challenging when we incorporate both light and sound. The programmer must carefully coordinate the operation of independent Modulino modules with special attention to blocking and non-blocking methods.
What is blocking code?
The common example of blocking code is the Arduino delay( ) function. Recall that delay( ) halts the operation of your loop( ) code until the delay time has passed. The microcontroller is effectively incapacitated while delay( ) is executing, as it is unable to respond to user inputs.
Tech Tip: The delay( ) function halts your loop( ) program. It does not impact processes that occur in the background using Interrupt Service Routines (ISRs). For example, the timer mechanisms associated with millis( ) continue to run in the background. This is a welcomed operation, as the primary metronome is entirely dependent on these timing mechanisms that occur as background Interrupt Service Routines (ISRs).
Tracking the Beats Per Minute
The Beats Per Minute (BPM) variable is maintained in the Modulino Knob (encoder) module. The Knob module consists of a rotary encoder and a microcontroller to monitor position and communicate on the Qwiic bus. This microcontroller tracks the integer number of “rotary clicks” entered by the user where each click equates to a single change in BPM value. This operation is facilitated using the get( ) method. The set( ) method is used to bound the BPM value.
The function to maintain the BPM is included below. In this example, the set( ) method is used to bound the BPM value between 1 and 300 BPM.
int get_BPM( ){
int BPM = encoder.get( );
if (BPM < 1) {
encoder.set(1);
BPM = 1; // Don't divide by zero
}
if (BPM > MAX_BPM) {
encoder.set(MAX_BPM);
BPM = MAX_BPM;
}
return BPM;
}
Display the BPM
The Arduino UNO R4 WiFi’s LED matrix is used to display the BPM as shown in Video 1. This code is executed when the BPM value changes, specifically when (BPM != last_BPM). Note the use of the snprintf( ) function to convert the BPM value into a human readable string suitable for display on the LED matrix.
Tech Tip: The sprintf( ) and snprintf( ) functions as located in the C Standard I/O (stdio.h) library, perform similar operation. However, sprintf( ) should be avoided as it does not check the size of the associated buffer. Without this protection, it is easy to overwrite the buffer resulting in erratic operation that is very difficult to troubleshoot.
if (BPM != last_BPM) {
i = 0; // Always start a new sequence with beat 1
delay(1000); // Allow the user time to set the new BPM (blocking metronome is off)
BPM = get_BPM(); // User should be done, get the new value
last_BPM = BPM;
matrix.beginDraw(); // Standard Arduino code to operate the UNO R4 WiFi’s LED matrix
matrix.stroke(0xFFFFFFFF);
matrix.textScrollSpeed(50);
snprintf(buffer, sizeof(buffer), " %d BPM ", BPM); // See tech tip
matrix.textFont(Font_5x7);
matrix.beginText(0, 1, 0xFFFFFF);
matrix.println(buffer);
matrix.endText(SCROLL_LEFT);
matrix.endDraw();
delay_val = 60000 / BPM; // 60 second per minute and 1000 ms per second
}
Tech Tip: The Arduino LED matrix display API is blocking. As a result, the BPM display is not available when the metronome is operating.
How time is maintained
There are many different ways to keep track of time in an Arduino microcontroller including the blocking delay( ), use of millis( ), and for advanced users, dedicated hardware timers. For this project, I elected to use the non-blocking timer code as described in this article. This code is a compromise that embeds the millis( ) function into a compact class-based routine. Note that the reference article includes a tutorial describing the conventional Arduino non-blocking techniques as well as the class-based implementation.
As you read the timing article you will notice the emphasis on Programmable Logic Controllers (PLC). In fact, the timer code functions as if it were a timer module programmed using the PLC’s Structured Text (ST) language. Here is an example usage of the timer class. In this instance TON_1 produces a pulse once every 5 seconds. The TOF_1 timer extends this pulse by 300 ms. The result is a blink variable that produces a 300 ms pulse once every 5000 ms.
// Produce a pulse that is one program scan long that occurs once every 5 seconds.
// Use that pulse to blink an LED with a 300 ms on period.
TON_1.update(!TON_1.Q, 5000);
TOF_1.update(TON_1.Q, 300);
int blink = TOF_1.Q;
Tech Tip: This timer code was developed as an experiment with PLCs. Recall that a PLC is designed for real-world control of a machine or process; at a fundamental level, the PLC code is non-blocking.
The timer code was also an experiment with AI. I asked the ChatGPT 4.0 to construct a class-based Arduino timer module that operates like a PLC’s Structured Text (ST) implementation. After several hours of refinement, the code operates as expected with ST-like (non-blocking) statements in the Arduino environment. This is a good example of improving your coding skills by adapting techniques from one environment (PLC ST) to another (Arduino C++).
Maintain the musical meter
Now that we have a heartbeat, the metronome is hard coded to maintain a 4/4 time signature as shown in this code snippet. The first beat is emphasized with a higher 880 Hz tone and flash of the blue LEDs as shown in Video 1. All other beats are announced with a 440 Hz tone and flash of the red LEDs.
The code listing has a mixture of blocking and non-blocking routines. The Buzzer is non-blocking as statements such as buzzer.tone(880, 50) will sound the 880 Hz tone for 50 ms. The LED flash requires a more complex blocking mechanism. Observe that a period of time must pass between the show( ) used to set and the second show( ) method used to clear the LEDs. Since the LEDs are very bright, a 5 ms period is sufficient.
if (TON_1.Q) { // This is a pulse (high for one loop( ) cycle) coincident with the BPM.
if (i >= 4) { // TODO: Allow user to adjust the musical meter.
i = 0;
}
if (i == 0) {
buzzer.tone(880, 50); // hard code musical note A3
flash_LED(BLUE, 50, 5);
} else {
buzzer.tone(440, 50); // hard code musical note A4
flash_LED(RED, 50, 5);
}
i++;
}
Future modifications
This project is a quick demonstration exploring how to use the Arduino Plug and Play. There is still plenty of room to improve the device. Here are some ideas for your consideration:
-
Use the Modulino Buttons module to control the meter. Currently, it is hard-coded in 4/4 time. It would be useful to include a user-selectable cut time, 3/4 time, common time, or even 5/4 time — so that you can rock the main theme from Mission Impossible.
-
Incorporate a button to play a constant tuning note.
-
Improve the responsiveness of the user interface. For example, implement a course and fine control to allow the user to quickly locate the desired BPM.
-
Add a foot pedal to slide (dynamically adjust the time) to provide more natural phrasing for a solo player.
-
Measure and then calibrate the device for precise BPM.
-
For ambitious experimenters, shift to a breadboard using advanced components and techniques such as a Direct Digital Synthesis (DDS) to produce sinusoidal tuning note(s) with the ability to change volume. You could also add an arbitrary waveform generator to change the tone of the beat from the robotic beep-beep to wooden blocks or even a cowbell – I gott’a have more cowbell!
-
Modify the 12 x 8 display so that it is non-blocking. This would allow the display to constantly scroll the BPM.
Parting thoughts
The Arduino Plug and Make kit provides a convenient platform from which students can explore a variety of sensors and actuators. The breadboard-free design allows your students to focus exclusively on the software.
The metronome as described in this article is very easy to replicate. At the same time, there are many areas for improvement. Your first challenge is to use the Modulino Buttons module to facilitate change in meter from common time to cut time, waltz, 5/4, or even a jazzy 7/4 as in featured in Pink Floyd’s Money.
In the spirit of the open-source community please copy, modify, and distribute your own metronome. Please add your comments and questions to the space below.
Best wishes,
APDahlen
Related Information
Please follow these links to related and useful information:
About this author
Aaron Dahlen, LCDR USCG (Ret.), serves as an application engineer at DigiKey. He has a unique electronics and automation foundation built over a 27-year military career as a technician and engineer which was further enhanced by 12 years of teaching (partially interwoven with military experience). With an MSEE degree from Minnesota State University, Mankato, Dahlen has taught in an ABET-accredited EE program, served as the program coordinator for an EET program, and taught component-level repair to military electronics technicians. Dahlen has returned to his Northern Minnesota home and thoroughly enjoys researching and writing educational articles about electronics and automation.
Highlighted experience
Dahlen is an active contributor to DigiKey’s Technical Forum. At the time of this writing, he has created over 180 unique posts and provided an additional 580 forum posts. Dahlen shares his insights on a wide variety of topics including microcontrollers, FPGA programming in Verilog, and a large body of work on industrial controls.