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 shield library in the Introduction.

Metronome

The 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 <TimerOne.h>
#include <Wire.h>
#include <MultiFuncShield.h>
#include <avr/eeprom.h>

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();
  Timer1.initialize();
  MFS.initialize(&Timer1);
  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!

Share on LinkedInShare on Google+Tweet about this on TwitterShare on Facebook

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

Leave a Reply

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