Arduino Multi-function Shield Bonus Sketches

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(&currentConfig, (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(&currentConfig, (void *)0, sizeof (Config));
}


Download the source code here.

Don’t forget to download and install the shield library before using!

14 thoughts on “Arduino Multi-function Shield Bonus Sketches

  1. Riyanto

    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

    Reply
    1. Kashif Baig Post author

      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.

      Reply
  2. Riyanto

    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

    Reply
    1. Kashif Baig Post author

      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.

      Reply
    2. matthew D Murphy

      if the shield is not plugged in just right it acts goofy. remember to cut the extra leads

      Reply
  3. Riyanto

    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

    Reply
    1. Kashif Baig Post author

      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.

      Reply
  4. Herus Munhoz

    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’.

    Reply
    1. N. A. Gribble

      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?

      Reply
  5. CHRISTOPHER M

    This is fantastic, thank you. I want to incorporate this into a realtime sequencer at a division of 480ppq. How would I do that?

    Reply
    1. Kashif Baig Post author

      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.

      Reply
    1. Cohesive Computing

      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.

      Reply

Leave a Reply

Your email address will not be published. Required fields are marked *