/**
* IMPORTANT!!!
* IN THE TOP RIGHT CORNER OF THE SCREEN YOU WILL SEE THE SIMULATION SPEED,
* SO DO NOT BE SURPRISED IF THE MUSIC SOUNDS STRANGE (SLOWER) WHEN
* THE SIMULATOR IS AT 60% - 65%
**/
#include <LiquidCrystal.h>
#include <TimerOne.h>
#include "pitches.h"
LiquidCrystal lcd(7, 6, 5, 4, 3, 2);
const byte STEP1[8] = {
0b01110,
0b10001,
0b10001,
0b01110,
0b11111,
0b00100,
0b01010,
0b10001
};
const byte STEP2[8] = {
0b01110,
0b10001,
0b10001,
0b01110,
0b11111,
0b00100,
0b01010,
0b01010
};
const byte STEP3[8] = {
0b01110,
0b10001,
0b10001,
0b01110,
0b11111,
0b00100,
0b00100,
0b00100
};
// notes in the melody – constant values defining frequency for each used note
const int MELODY[] = {
NOTE_C4,
NOTE_G3,
NOTE_G3,
NOTE_A3,
NOTE_G3,
0,
NOTE_B3,
NOTE_C4
};
// note durations: 4 = quarter note, 8 = eighth note, etc.:
const int NOTE_DURATIONS[] = {
4, // NOTE_C4,
8, // NOTE_G3,
8, // NOTE_G3,
4, // NOTE_A3,
4, // NOTE_G3,
4, // 0,
4, // NOTE_B3,
4 // NOTE_C4
};
// Based on Example #4, we take the base value as 1000 milliseconds, in other words one second
// Then we need to consider the values from the noteDurations array ... 1/4 --> 250 ms, 1/8 --> 125 ms
// The pause between the notes also needs to be taken into account, so 250*1.3 = 325 ms, 125*1.3 = 162 ms
// The greatest common factor between these four numbers is GCF([125, 162, 250, 325]) = 1 ms
// So, in other words, we need to cycle the interrupt service routine every 1 ms
const int ISR_PERIOD_TIME = 1000; // 10^3 μseconds --> 1 millisecond (10^-3 seconds)
// Change this to increase / decrease the speed of the walking animation
const unsigned int WALKING_ANIMATION_DELAY = 100;
volatile unsigned int currentNote;
unsigned int playArrayInternalIndex;
unsigned int melodyArrLength;
byte currentImage;
int *playArray;
void setup(void) {
melodyArrLength = sizeof(MELODY) / sizeof(int); // Get the length of the melody array
calculatePlayArray();
lcd.createChar(0, STEP1);
lcd.createChar(1, STEP2);
lcd.createChar(2, STEP3);
Timer1.initialize(ISR_PERIOD_TIME); // init the timing interval for event triggering (1s = 10-6s)
Timer1.attachInterrupt(playMusic); // The function is called at the preset time interval
}
void playMusic() {
if (currentNote % 2 == 0) {
const int currentMelodyIndex = currentNote / 2;
tone(8, MELODY[currentMelodyIndex], playArray[currentNote]);
} else {
tone(8, 0, playArray[currentNote]);
}
if (playArrayInternalIndex < playArray[currentNote]) {
playArrayInternalIndex++;
} else {
playArrayInternalIndex = 0;
currentNote++;
if (currentNote > melodyArrLength * 2) {
currentNote = 0;
}
noTone(8);
}
}
void playAnimation() {
lcd.setCursor(0,0);
lcd.write(currentImage);
if (currentImage == 2) {
currentImage = 0;
} else {
currentImage++;
}
}
void calculatePlayArray() {
// melodyLength * 2, because you need a pause after every note
playArray = (int *) malloc(2 * melodyArrLength * sizeof(int));
for (unsigned int i = 0; i < 2 * melodyArrLength; i++) {
const unsigned int currentMelodyIndex = i / 2;
const unsigned int durationInMilliSeconds = 1000 / NOTE_DURATIONS[currentMelodyIndex];
// Every even note starting with 0 will be a note, odds will be a pause
if (i % 2 == 0) {
playArray[i] = durationInMilliSeconds;
} else {
playArray[i] = durationInMilliSeconds * 1.3;
}
}
}
void loop(void) {
playAnimation();
delay(WALKING_ANIMATION_DELAY);
}