0

I am using a standalone ATmega328P with two piezo elements to generate some music.

I have defined some constants with the frequencies of the music notes. Then I defined a struct which contains the note for the first and the second piezo and the length of the note. Then I made more arrays of these structs to describe each songs.

The problem is that this way I run out of memory quickly. I tried to store the arrays of structs in the PROGMEM, to avoid this problem. I tried to use a small library called PROGMEM_readAnything, the memcpy_P() or the pgm_read_word() and pgm_read_byte() functions, but in all cases I get the same problem.

As I loop through the array of NOTES it skips some of the elements, while reads and plays the others correctly. It always skips the same elements, and not just random ones.

I even tried to change the microcontroller, thinking that certain parts of the chip may have been damaged by something, but uploading the same sketch I got the same results, so the microcontroller is probably intact.

Here is the code:

#include <Tone.h>
#include <avr/pgmspace.h>

// Define the notes frequency
#define G2 98
#define Gs2 104
#define Ab2 104
#define A2 110
#define As2 116

//... and so on with many other music notes ...

#define Fs7 2960
#define Gb7 2960
#define G7 3136

//Rest
#define R 0

typedef struct {
    int n1;
    int n2;
    byte units;
} NOTES;

Tone buzzer1;
Tone buzzer2;

int myTempo = 100;

// Walkyrie
const NOTES walkyrie[] PROGMEM = {

                          {Fs3, Fs4, 2},
                          {B3,  B4,  3},
                          {Fs3, Fs4, 1},
                          {B3,  B4,  2},
                          {D4,  D5,  6},
                          {B3,  B4,  6},
                          {D4,  D5,  3},
                          {B3,  B4,  1},
                          {D4,  D5,  2},
                          {Fs4, Fs5, 6},
                          {D4,  D5,  6},
                          {Fs4, Fs5, 3},
                          {D4,  D5,  1},
                          {Fs4, Fs5, 2},
                          {A4,  A5,  6},
                          {A3,  A4,  6},
                          {D4,  D5,  3},
                          {A3,  A4,  1},
                          {D4,  D5,  2},
                          {Fs4, Fs5, 6},
                          {R,    0,  4},
                          {A3,  A4,  2},
                          {D4,  D5,  3},
                          {A3,  A4,  1},
                          {D4,  D5,  2},
                          {Fs4, Fs5, 6},
                          {D4,  D5,  6},
                          {Fs4, Fs5, 3},
                          {D4,  D5,  1},
                          {Fs4, Fs5, 2},
                          {A4,  A5,  6},
                          {Fs4, Fs5, 6},
                          {A4,  A5,  3},
                          {Fs4, Fs5, 1},
                          {A4,  A5,  2},
                          {Cs5, Cs6, 6},
                          {Cs4, Cs5, 6},
                          {Fs4, Fs5, 3},
                          {Cs4, Cs5, 1},
                          {Fs4, Fs5, 2},
                          {As4, As5, 6}
                        };

void playSong()
{
    // We store the frequency of the second piezo in this variable
    int secondFreq = 0;
    Serial.println(sizeof(walkyrie)/sizeof(walkyrie[0]));
    // Walk through the array of music
    for(int i = 0; i < sizeof(walkyrie)/sizeof(walkyrie[0]); i++)
    {
        int n1;
        int n2;
        byte units;
        // Only play if it is not a rest
        if (walkyrie[i].n1 > 0)
        {
            n1 = pgm_read_word(&(walkyrie[i].n1));
            n2 = pgm_read_word(&(walkyrie[i].n2));
            units = pgm_read_byte(&(walkyrie[i].units));

            Serial.print("Row ");
            Serial.print(i);
            Serial.print(": Frequency 1: ");
            Serial.print(n1);
            Serial.print(" Frequency 2: ");
            Serial.print(n2);
            Serial.print(" Units: ");
            Serial.println(units);

            // Play the note of the first piezo
            buzzer1.play(n1, (units*myTempo));
            // If the frequency of the second piezo is 0, we play the same note
            // as the first, else the note set for the second one
            if (n2 == 0)
            {
                secondFreq = n1;
            }
            else {
                secondFreq = n2;
            }

            buzzer2.play(secondFreq, (units*myTempo));
        }

        // Then we wait for the note to end plus a little, between two notes
        delay((units*myTempo) + 10);
    }
}


void setup() {
    Serial.begin(9600);
    buzzer1.begin(11);
    buzzer2.begin(12);
}

void loop()
{
     playSong();
}

I added some lines to see in serial monitor what happens. It reads the correct length...

The output of the serial monitor is the following:

41                                        (correct length)
Row 1: Freq1: 247 Freq2: 499 Units: 3     (row 0 - the first note is already missing)
Row 2: Freq1: 185 Freq2: 370 Units: 1
Row 3: Freq1: 247 Freq2: 499 Units: 2     (row 4 missing)
Row 5: Freq1: 247 Freq2: 499 Units: 6     (row 6-7 missing)
Row 8: Freq1: 294 Freq2: 587 Units: 2
Row 9: Freq1: 370 Freq2: 740 Units: 6
Row 10: Freq1: 294 Freq2: 587 Units: 6
Row 11: Freq1: 370 Freq2: 740 Units: 3
Row 12: Freq1: 294 Freq2: 587 Units: 1
Row 13: Freq1: 370 Freq2: 740 Units: 2
Row 14: Freq1: 440 Freq2: 880 Units: 6
Row 15: Freq1: 220 Freq2: 440 Units: 6    (row 16-17 missing)
Row 18: Freq1: 294 Freq2: 587 Units: 2
Row 19: Freq1: 370 Freq2: 740 Units: 6
Row 20: Freq1: 0 Freq2: 0 Units: 4
Row 21: Freq1: 220 Freq2: 440 Units: 2
Row 22: Freq1: 294 Freq2: 587 Units: 3
Row 23: Freq1: 220 Freq2: 440 Units: 1
Row 24: Freq1: 294 Freq2: 587 Units: 2
Row 25: Freq1: 370 Freq2: 740 Units: 6
Row 26: Freq1: 294 Freq2: 587 Units: 6
Row 27: Freq1: 370 Freq2: 740 Units: 3
Row 28: Freq1: 294 Freq2: 587 Units: 1
Row 29: Freq1: 370 Freq2: 740 Units: 2
Row 30: Freq1: 440 Freq2: 880 Units: 6
Row 31: Freq1: 370 Freq2: 740 Units: 6
Row 32: Freq1: 440 Freq2: 880 Units: 3
Row 33: Freq1: 370 Freq2: 740 Units: 1
Row 34: Freq1: 440 Freq2: 880 Units: 2
Row 35: Freq1: 554 Freq2: 1109 Units: 6
Row 36: Freq1: 277 Freq2: 554 Units: 6
Row 37: Freq1: 370 Freq2: 740 Units: 3
Row 38: Freq1: 277 Freq2: 554 Units: 1
Row 39: Freq1: 370 Freq2: 740 Units: 2
Row 40: Freq1: 466 Freq2: 932 Units: 6

Why does it happen? Or is there a better, more efficient way of solving this problem?

1
  • It sounds like you may need to learn how to use a debugger to step through your code. With a good debugger, you can execute your program line by line and see where it is deviating from what you expect. This is an essential tool if you are going to do any programming. Further reading: How to debug small programs Commented Aug 11, 2016 at 20:01

1 Answer 1

3

In this line, you check the data, but you haven't done a 'pgm_read_word()' to actually get the data from the flash memory:

if(walkyrie[i].n1 > 0)

If, by accident, you get a non-zero value, then you correctly read the values from flash, but otherwise, you skip that row.

Further evidence:

Row 20: Frq1: 0 Frq2: 0 Units: 4

Here, n1 is zero, but that test should have skipped the row.

Also, the logic for a 'rest' is a little off. Right now, you don't read the units for the duration of the rest, so it's using the previous value (from a played note).

I think I'd get all three values first, and then check them.

I'd also encode the frequencies into a byte, and use a look-up table to convert the "key number" into a frequency (like MIDI key numbers). Your array of structs will be a little smaller that way. Maybe turn on the __packed__ (whatever) attribute also, to avoid the padding between the entries -- if saving flash space matters (then you could get more songs in there!)

Sounds fun! Good luck!

Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.