As bonus sketches become available, they will be published on this page. To use them, you’ll need to first download the Arduino multi-function shield library in the Introduction.
Arduino Metronome
The Arduino metronome application allows music students to work on their timing skills by displaying and sounding a beat, where the BPM rate and time signature are configurable by the user. Button 1 starts/stops the metronome, buttons 2 and 3 configure the BPM and time signature, respectively. The configuration settings are retained when the Arduino is powered off.
#include <MultiFuncShield.h>
#include <avr/eeprom.h>
// Metronome
typedef struct Config {
char appNameAndVersion[16];
byte signature; // must be between 2 to 8
unsigned int tempo; // must be between 60 to 240
};
enum MetronomeDisplayModeValues
{
DISPLAY_TIMER,
DISPLAY_SIGNATURE,
DISPLAY_TEMPO
};
enum MetronomeStatusValues
{
METRO_OFF,
METRO_ON
};
Config currentConfig;
byte beatCounter;
byte displayMode = DISPLAY_TIMER;
byte statusMode = METRO_OFF;
const byte led[] = {13,12,11,10};
void setup() {
// put your setup code here, to run once:
loadConfig();
MFS.initialize();
MFS.write(beatCounter);
}
void loop() {
const unsigned int oldTempo = currentConfig.tempo;
const byte oldSignature = currentConfig.signature;
const byte btn = MFS.getButton();
switch (displayMode)
{
case DISPLAY_TIMER:
if (btn == BUTTON_1_PRESSED)
{
if (statusMode == METRO_OFF)
{
statusMode = METRO_ON; // Turn metronome on, if off.
}
else if (statusMode == METRO_ON)
{
statusMode = METRO_OFF; // Turn metronome off, if on.
beatCounter = 0;
MFS.write(beatCounter);
clearLeds();
}
}
else if (btn == BUTTON_2_PRESSED)
{
displayMode = DISPLAY_TEMPO;
MFS.write((int)currentConfig.tempo);
MFS.blinkDisplay(DIGIT_ALL, ON);
}
else if (btn == BUTTON_3_PRESSED)
{
displayMode = DISPLAY_SIGNATURE;
displaySignature(currentConfig.signature);
MFS.blinkDisplay(DIGIT_ALL, ON);
}
break;
case DISPLAY_SIGNATURE:
if (btn == BUTTON_1_PRESSED)
{
displayMode = DISPLAY_TIMER;
MFS.write(beatCounter);
MFS.blinkDisplay(DIGIT_ALL, OFF);
saveConfig(); // Write config to eeprom.
}
else if (btn == BUTTON_2_PRESSED)
{
currentConfig.signature--; // Change to lower time signature
if (currentConfig.signature <2)
{
currentConfig.signature = 2;
}
}
else if (btn == BUTTON_3_PRESSED)
{
currentConfig.signature++; // Change to higher time signature
if (currentConfig.signature > 8)
{
currentConfig.signature = 8;
}
}
if (oldSignature != currentConfig.signature)
{
displaySignature(currentConfig.signature);
}
break;
case DISPLAY_TEMPO:
if (btn == BUTTON_1_PRESSED)
{
displayMode = DISPLAY_TIMER;
MFS.write(beatCounter);
MFS.blinkDisplay(DIGIT_ALL, OFF);
saveConfig(); // Write config to eeprom.
}
else if (btn == BUTTON_2_PRESSED)
{
currentConfig.tempo--; // Reduce tempo by 1
}
else if (btn == BUTTON_3_PRESSED)
{
currentConfig.tempo++; // Increase tempo by 1
}
else if (btn == BUTTON_2_LONG_PRESSED)
{
currentConfig.tempo = ((currentConfig.tempo-2) / 2) * 2; // Reduce tempo to next even number
}
else if (btn == BUTTON_3_LONG_PRESSED)
{
currentConfig.tempo = ((currentConfig.tempo+2) / 2) * 2; // Increase tempo to next even number
}
if (oldTempo != currentConfig.tempo)
{
if (currentConfig.tempo < 60) { currentConfig.tempo = 60; } else if (currentConfig.tempo > 240)
{
currentConfig.tempo = 240;
}
MFS.write((int)currentConfig.tempo);
}
break;
}
switch (statusMode)
{
case METRO_OFF:
break;
case METRO_ON:
// Has millisecond countdown timer reached 0?
if (MFS.getTimer() == 0)
{
// Reset countdown timer.
MFS.setTimer(240000 / (currentConfig.tempo * (currentConfig.signature <= 4 ? 4 : 8)));
if (beatCounter < 1 || beatCounter >= currentConfig.signature)
{
// Sound beeper every time beat counter is reset.
MFS.beep(2);
beatCounter = 1;
}
else
{
// bump beat counter
beatCounter++;
}
if (displayMode == DISPLAY_TIMER)
{
MFS.write(beatCounter);
}
}
break;
}
if (beatCounter != 0)
{
clearLeds();
digitalWrite(led[(beatCounter-1) & 3], beatCounter == 1 ? 0 : millis() & 7); // use crude PWM to change brightness
}
}
// -------------------------------------------------------------
float displaySignature(byte signature)
{
float sig = (float)signature;
if (signature <= 4)
{
sig = sig + 0.4;
}
else
{
sig = sig + 0.8;
}
MFS.write(sig);
}
// -------------------------------------------------------------
void clearLeds()
{
for (int i=0; i < sizeof(led); i++)
{
digitalWrite(led[i], 1);
}
}
// -------------------------------------------------------------
const char appNameAndVersion[] = "Metronome V1.0";
void loadConfig()
{
// Attempt to load config from EEPROM
eeprom_read_block(¤tConfig, (void *)0, sizeof (Config));
if (strcmp(currentConfig.appNameAndVersion, appNameAndVersion) != 0)
{
// Config not found in eeprom, so set default values here.
strcpy(currentConfig.appNameAndVersion, appNameAndVersion);
currentConfig.signature = 4;
currentConfig.tempo = 80;
}
}
void saveConfig()
{
eeprom_write_block(¤tConfig, (void *)0, sizeof (Config));
}
Download the source code here.
Don’t forget to download and install the shield library before using!
Thank’s this a great work
but i’m still nube to programe arduino, can u telling me how to display Alpabat & number in the same time,
Like this “A100” or “100B”.
Sorry my english so bad.
tq
Hi Riyanto
You can display alphanumeric values already by doing MFS.write(“A100”); but you will need to do your own string formatting if you want to do something more complex.
Hope that helps.
Thank’s Kashif
it’s worked, but i can’t display the value of preset_pot sketch with alphabet on left seven segment,
what’s i to do now?
Tq
Try changing your loop function to look like this:
void loop() {
char buffer[6];
sprintf(buffer, "A%d", analogRead(POT_PIN));
MFS.write(buffer);
delay(100);
}
That should work for you.
if the shield is not plugged in just right it acts goofy. remember to cut the extra leads
Thank you again
What you write is very useful for me, I have a small project for my hobby, maybe one day I’ll show you what I made.
” Terima Kasih Banyak ” Indonesian people say
Hi,
Great website, video and code.
Can I use the function Tone rather than Beep?
Thanks
Hi Sergio
Thanks! You may certainly try tone(), although I haven’t tried using it myself, so I can’t say if it will interfere with how some of the library functions work.
Hi, I’m having problems with the code.
It appears “ser_open (): can not open device” \\. \ COM1 “: The system can not find the file specified.”
What could be happening? The code is also ignoring the ‘typedef’.
Hi do you have plans for a bonus Sketch for a simple Greenhouse monitor-watering project. Everything seems to be there on the board it just needs the code?
This is fantastic, thank you. I want to incorporate this into a realtime sequencer at a division of 480ppq. How would I do that?
Hello Christopher. I have another version of the code that generates a midi clock signal, but you’ll need a midi shield that uses midi cables to connect to another musical device. If that’s what you’re looking for, I’ll make the code available for download.
Hi Kashif
The shield is fantastic, also the website and the Hackatronics instructions you made in 2015.
I have an open source FPGA with ICE40 and Arduino form factor.
Do you think I could use the shield coupled with the FPGA , it must be coded in Verilog, so I can not use Arduino libraries..
https://github.com/FPGAwars/Alhambra-II-FPGA/wiki
https://github.com/Alhambra-bits/Lattuino-studio
Thanks Roberto. You should be able to find a schematic of the shield to help determine suitability with FPGA. Let us know how it works out.
Hi Kashif,
I am working with a research student and we are trying to get the metronome up and running. Do we have to input certain values to make this code work? We are kind of newbies to this board. We had uploaded the provided code and we don’t get anything on the LED display and the buttons are not functional. Please let me know if you can assist. This is the code we are currently using.
Thanks!
#include
#include
// Metronome
typedef struct Config {
char appNameAndVersion[16];
byte signature; // must be between 2 to 8
unsigned int tempo; // must be between 60 to 240
};
enum MetronomeDisplayModeValues
{
DISPLAY_TIMER,
DISPLAY_SIGNATURE,
DISPLAY_TEMPO
};
enum MetronomeStatusValues
{
METRO_OFF,
METRO_ON
};
Config currentConfig;
byte beatCounter;
byte displayMode = DISPLAY_TIMER;
byte statusMode = METRO_OFF;
const byte led[] = {13,12,11,10};
void setup() {
// put your setup code here, to run once:
loadConfig();
MFS.initialize();
MFS.write(beatCounter);
}
void loop() {
const unsigned int oldTempo = currentConfig.tempo;
const byte oldSignature = currentConfig.signature;
const byte btn = MFS.getButton();
switch (displayMode)
{
case DISPLAY_TIMER:
if (btn == BUTTON_1_PRESSED)
{
if (statusMode == METRO_OFF)
{
statusMode = METRO_ON; // Turn metronome on, if off.
}
else if (statusMode == METRO_ON)
{
statusMode = METRO_OFF; // Turn metronome off, if on.
beatCounter = 0;
MFS.write(beatCounter);
clearLeds();
}
}
else if (btn == BUTTON_2_PRESSED)
{
displayMode = DISPLAY_TEMPO;
MFS.write((int)currentConfig.tempo);
MFS.blinkDisplay(DIGIT_ALL, ON);
}
else if (btn == BUTTON_3_PRESSED)
{
displayMode = DISPLAY_SIGNATURE;
displaySignature(currentConfig.signature);
MFS.blinkDisplay(DIGIT_ALL, ON);
}
break;
case DISPLAY_SIGNATURE:
if (btn == BUTTON_1_PRESSED)
{
displayMode = DISPLAY_TIMER;
MFS.write(beatCounter);
MFS.blinkDisplay(DIGIT_ALL, OFF);
saveConfig(); // Write config to eeprom.
}
else if (btn == BUTTON_2_PRESSED)
{
currentConfig.signature –; // Change to lower time signature
if (currentConfig.signature 8)
{
currentConfig.signature = 8;
}
}
if (oldSignature != currentConfig.signature)
{
displaySignature(currentConfig.signature);
}
break;
case DISPLAY_TEMPO:
if (btn == BUTTON_1_PRESSED)
{
displayMode = DISPLAY_TIMER;
MFS.write(beatCounter);
MFS.blinkDisplay(DIGIT_ALL, OFF);
saveConfig(); // Write config to eeprom.
}
else if (btn == BUTTON_2_PRESSED)
{
currentConfig.tempo–; // Reduce tempo by 1
}
else if (btn == BUTTON_3_PRESSED)
{
currentConfig.tempo++; // Increase tempo by 1
}
else if (btn == BUTTON_2_LONG_PRESSED)
{
currentConfig.tempo = ((currentConfig.tempo-2) / 2) * 2; // Reduce tempo to next even number
}
else if (btn == BUTTON_3_LONG_PRESSED)
{
currentConfig.tempo = ((currentConfig.tempo+2) / 2) * 2; // Increase tempo to next even number
}
if (oldTempo != currentConfig.tempo)
{
if (currentConfig.tempo 240)
{
currentConfig.tempo = 240;
}
MFS.write((int)currentConfig.tempo);
}
break;
}
switch (statusMode)
{
case METRO_OFF:
break;
case METRO_ON:
// Has millisecond countdown timer reached 0?
if (MFS.getTimer() == 0)
{
// Reset countdown timer.
MFS.setTimer(240000 / (currentConfig.tempo * (currentConfig.signature <= 4 ? 4 : 8)));
if (beatCounter = currentConfig.signature)
{
// Sound beeper every time beat counter is reset.
MFS.beep(2);
beatCounter = 1;
}
else
{
// bump beat counter
beatCounter++;
}
if (displayMode == DISPLAY_TIMER)
{
MFS.write(beatCounter);
}
}
break;
}
if (beatCounter != 0)
{
clearLeds();
digitalWrite(led[(beatCounter-1) & 3], beatCounter == 1 ? 0 : millis() & 7); // use crude PWM to change brightness
}
}
// ————————————————————-
float displaySignature(byte signature)
{
float sig = (float)signature;
if (signature <= 4)
{
sig = sig + 0.4;
}
else
{
sig = sig + 0.8;
}
MFS.write(sig);
}
// ————————————————————-
void clearLeds()
{
for (int i=0; i < sizeof(led); i++)
{
digitalWrite(led[i], 1);
}
}
// ————————————————————-
const char appNameAndVersion[] = "Metronome V1.0";
void loadConfig()
{
// Attempt to load config from EEPROM
eeprom_read_block(¤tConfig, (void *)0, sizeof (Config));
if (strcmp(currentConfig.appNameAndVersion, appNameAndVersion) != 0)
{
// Config not found in eeprom, so set default values here.
strcpy(currentConfig.appNameAndVersion, appNameAndVersion);
currentConfig.signature = 4;
currentConfig.tempo = 80;
}
}
void saveConfig()
{
eeprom_write_block(¤tConfig, (void *)0, sizeof (Config));
}
Hi Richard, it’s been a while since I did any work on this.
Can you confirm if you’ve uploaded the source to an Uno R3 (it wont work on an Uno R4)? Also, are you using the most recent multifunction shield library available from this website? There is a version of this library available using the Arduino IDE by someone else, and this metronome likely wont work with it.
Can you confirm if you’ve been able to get any of the other examples working with your board?
I am looking for assistance in controlling relays with the countdown timer. I need relay 1 to trigger at the start of the timer for 10 seconds and then off again, and relay 2 to trigger when the countdown reaches 0 for 10 seconds then off again.