This article will show you a simple and interesting project: how to play polyphonically "The Avengers" theme.

Watch the circuit's video on YouTube:



It is easy to generate sounds with Arduino using the tone function:

tone(pin, frequency, duration)

You can make a sequence of notes to play a melody.

But this function is limited to play one note per time, so we can't play more complex musics.

To address this issue, we'll use the Len Shustek's PlayTune library.

This library is capable of playing up to 6 tones simultaneously (3 using Arduino Uno and 6 using Arduino Mega).

Click on the link below to download the library:

https://github.com/LenShustek/arduino-playtune

Schematics

The circuit is very simple: connect the digital pins 10, 11 and 12 to 220-ohms resistors, and the other ends should be connected together to one terminal of a speaker. The other terminal of the speaker should be connected to ground.
You can also connect a LED to each of the ports, thus indicating when a signal present on that channel. This connection is optional, and does not interfere with the operation of audio playback.

Breadboard Assembly

This circuit can be easily mounted on a breadboard.
It is recommended to use a speaker because the buzzers typically used with Arduino, can not play the song with a suitable volume.

You'll have a better result if use a small speaker box, even without amplification.

I used this speaker box from a multimedia kit. It has a 4 ohm speaker and has no amplification.


Fritzing files

Click on the link below to download the project files:

https://drive.google.com/file/d/1gVy0avhG6uasFhVPmANaLVBrEw3i20Wh/view?usp=sharing

Programming

Upload the code below and remember to import the Playtune library. It can be downloaded using the link below:


// Plays polyphonic music using 3 interrupts
// Playtune library is required: https://github.com/LenShustek/arduino-playtune
// 2019 - Marcelo Maximiano - Maxblitz.com

#include <Playtune.h>

const byte PROGMEM avengers[] = {
0x90,0x3E,0x00,0x7A,0x80,0x00,0x02,0x90,0x3E,0x00,0x7A,0x80,0x00,0x02,0x90,0x3E,
0x00,0x7A,0x80,0x01,0x79,0x90,0x3E,0x00,0x7A,0x80,0x00,0x02,0x90,0x3E,0x00,0x7A,
0x80,0x00,0x02,0x90,0x3E,0x00,0x7A,0x80,0x01,0x79,0x90,0x3E,0x00,0x7A,0x80,0x00,
0x02,0x90,0x3E,0x00,0x7A,0x80,0x00,0x02,0x90,0x3E,0x00,0x7A,0x80,0x00,0x02,0x90,
0x3E,0x00,0x7A,0x80,0x00,0x02,0x90,0x3F,0x00,0x7A,0x80,0x00,0x02,0x90,0x3F,0x00,
0x7A,0x80,0x00,0x02,0x90,0x3F,0x00,0x7A,0x80,0x01,0x79,0x90,0x3F,0x00,0x7A,0x80,
0x00,0x02,0x90,0x3F,0x00,0x7A,0x80,0x00,0x02,0x90,0x40,0x00,0x7A,0x80,0x01,0x79,
0x90,0x40,0x00,0x7A,0x80,0x00,0x02,0x90,0x40,0x00,0x7A,0x80,0x00,0x02,0x90,0x40,
0x00,0x7A,0x80,0x00,0x02,0x90,0x40,0x00,0x7A,0x80,0x00,0x02,0x90,0x41,0x00,0x7A,
0x80,0x00,0x02,0x90,0x41,0x00,0x7A,0x80,0x00,0x02,0x90,0x41,0x00,0x7A,0x80,0x01,
0x79,0x90,0x41,0x00,0x7A,0x80,0x00,0x02,0x90,0x41,0x00,0x7A,0x80,0x00,0x02,0x90,
0x40,0x00,0x7A,0x80,0x01,0x79,0x90,0x40,0x00,0x7A,0x80,0x00,0x02,0x90,0x40,0x00,
0x7A,0x80,0x00,0x02,0x90,0x40,0x00,0x7A,0x80,0x00,0x02,0x90,0x40,0x00,0x7A,0x80,
0x00,0x02,0x90,0x3F,0x00,0x7A,0x80,0x00,0x02,0x90,0x3F,0x00,0x7A,0x80,0x00,0x02,
0x90,0x3F,0x00,0x7A,0x80,0x01,0x79,0x90,0x3F,0x00,0x7A,0x80,0x00,0x02,0x90,0x3F,
0x00,0x7A,0x80,0x00,0x02,0x90,0x3A,0x00,0xF7,0x80,0x00,0xFC,0x90,0x3C,0x00,0xF7,
0x80,0x00,0xFC,0x90,0x4A,0x91,0x37,0x92,0x4F,0x00,0x7A,0x80,0x81,0x00,0x02,0x90,
0x3E,0x91,0x4A,0x00,0x7A,0x80,0x81,0x00,0x02,0x90,0x3E,0x91,0x4A,0x00,0x7A,0x80,
0x81,0x01,0x79,0x90,0x3E,0x91,0x4A,0x00,0x7A,0x80,0x81,0x00,0x02,0x90,0x3E,0x91,
0x4A,0x00,0x7A,0x80,0x81,0x00,0x02,0x90,0x3E,0x91,0x4A,0x00,0x7A,0x80,0x81,0x01,
0x79,0x90,0x3E,0x91,0x4A,0x00,0x7A,0x80,0x81,0x00,0x02,0x90,0x3E,0x91,0x4A,0x00,
0x7A,0x80,0x81,0x00,0x02,0x90,0x3E,0x91,0x4A,0x00,0x7A,0x80,0x81,0x00,0x02,0x90,
0x3E,0x91,0x4A,0x00,0x7A,0x82,0x80,0x81,0x00,0x02,0x92,0x4F,0x90,0x3F,0x91,0x4B,
0x00,0x7A,0x80,0x81,0x00,0x02,0x90,0x3F,0x91,0x4B,0x00,0x7A,0x80,0x81,0x00,0x02,
0x90,0x3F,0x91,0x4B,0x00,0x7A,0x80,0x81,0x01,0x79,0x90,0x3F,0x91,0x4B,0x00,0x7A,
0x80,0x81,0x00,0x02,0x90,0x3F,0x91,0x4B,0x00,0x7A,0x80,0x81,0x00,0x02,0x90,0x40,
0x91,0x4C,0x00,0x7A,0x80,0x81,0x01,0x79,0x90,0x40,0x91,0x4C,0x00,0x7A,0x80,0x81,
0x00,0x02,0x90,0x40,0x91,0x4C,0x00,0x7A,0x80,0x81,0x00,0x02,0x90,0x40,0x91,0x4C,
0x00,0x7A,0x80,0x81,0x00,0x02,0x90,0x40,0x91,0x4C,0x00,0x7A,0x82,0x80,0x81,0x00,
0x02,0x92,0x4F,0x90,0x41,0x91,0x4D,0x00,0x7A,0x80,0x81,0x00,0x02,0x90,0x41,0x91,
0x4D,0x00,0x7A,0x80,0x81,0x00,0x02,0x90,0x41,0x91,0x4D,0x00,0x7A,0x80,0x81,0x01,
0x79,0x90,0x41,0x91,0x4D,0x00,0x7A,0x80,0x81,0x00,0x02,0x90,0x41,0x91,0x4D,0x00,
0x7A,0x80,0x81,0x00,0x02,0x90,0x40,0x91,0x4C,0x00,0x7A,0x80,0x81,0x01,0x79,0x90,
0x40,0x91,0x4C,0x00,0x7A,0x80,0x81,0x00,0x02,0x90,0x40,0x91,0x4C,0x00,0x7A,0x80,
0x81,0x00,0x02,0x90,0x40,0x91,0x4C,0x00,0x7A,0x80,0x81,0x00,0x02,0x90,0x40,0x91,
0x4C,0x00,0x7A,0x82,0x80,0x81,0x00,0x02,0x92,0x4F,0x90,0x3F,0x91,0x4B,0x00,0x7A,
0x80,0x81,0x00,0x02,0x90,0x3F,0x91,0x4B,0x00,0x7A,0x80,0x81,0x00,0x02,0x90,0x3F,
0x91,0x4B,0x00,0x7A,0x80,0x81,0x01,0x79,0x90,0x3F,0x91,0x4B,0x00,0x7A,0x80,0x81,
0x00,0x02,0x90,0x3F,0x91,0x4B,0x00,0x7A,0x80,0x81,0x00,0x02,0x90,0x3A,0x91,0x46,
0x00,0xF7,0x80,0x81,0x00,0xFC,0x90,0x3C,0x91,0x48,0x00,0xF7,0x80,0x81,0x00,0xFA,
0x82,0x00,0x02,0x90,0x37,0x91,0x3E,0x92,0x4A,0x00,0x7A,0x81,0x82,0x00,0x02,0x91,
0x3E,0x92,0x4A,0x00,0x7A,0x81,0x82,0x00,0x02,0x91,0x3E,0x92,0x4A,0x00,0x7A,0x81,
0x82,0x00,0x02,0x92,0x4F,0x00,0x7A,0x82,0x00,0x02,0x92,0x4F,0x00,0x7A,0x82,0x00,
0x02,0x92,0x4A,0x00,0x7A,0x82,0x00,0x02,0x91,0x3E,0x92,0x4A,0x00,0x7A,0x81,0x82,
0x00,0x02,0x91,0x3E,0x92,0x4A,0x00,0x7A,0x81,0x82,0x00,0x02,0x91,0x3E,0x92,0x4A,
0x00,0x7A,0x81,0x82,0x00,0x02,0x92,0x4A,0x00,0x7A,0x82,0x00,0x02,0x92,0x4A,0x00,
0x7A,0x82,0x00,0x02,0x92,0x4F,0x00,0x7A,0x82,0x00,0x02,0x91,0x3E,0x92,0x4A,0x00,
0x7A,0x81,0x82,0x00,0x02,0x91,0x3E,0x92,0x4A,0x00,0x7A,0x80,0x81,0x82,0x00,0x02,
0x91,0x3E,0x92,0x4A,0x90,0x37,0x00,0x7A,0x81,0x82,0x80,0x00,0x02,0x91,0x3E,0x92,
0x4A,0x90,0x37,0x00,0x7A,0x81,0x82,0x80,0x00,0x02,0x90,0x3E,0x91,0x3F,0x92,0x4B,
0x00,0x7A,0x81,0x82,0x00,0x02,0x91,0x3F,0x92,0x4B,0x00,0x7A,0x81,0x82,0x00,0x02,
0x91,0x3F,0x92,0x4B,0x00,0x7A,0x81,0x82,0x00,0x02,0x92,0x4F,0x00,0x7A,0x80,0x82,
0x00,0x02,0x90,0x3C,0x92,0x4F,0x00,0x7A,0x82,0x00,0x02,0x92,0x4A,0x00,0x3C,0x80,
0x00,0x3E,0x82,0x00,0x02,0x91,0x3F,0x92,0x4B,0x90,0x37,0x00,0x7A,0x81,0x82,0x80,
0x00,0x02,0x90,0x3A,0x91,0x3F,0x92,0x4B,0x00,0x3C,0x80,0x00,0x3E,0x81,0x82,0x00,
0x02,0x90,0x3A,0x91,0x40,0x92,0x4C,0x00,0x7A,0x81,0x82,0x00,0x02,0x92,0x4A,0x00,
0x7A,0x82,0x00,0x02,0x92,0x4A,0x00,0x7A,0x82,0x00,0x02,0x92,0x4F,0x00,0x7A,0x80,
0x82,0x00,0x02,0x90,0x3C,0x91,0x40,0x92,0x4C,0x00,0x7A,0x81,0x82,0x00,0x02,0x91,
0x40,0x92,0x4C,0x00,0x7A,0x81,0x82,0x00,0x02,0x91,0x40,0x92,0x4C,0x00,0x7A,0x80,
0x81,0x82,0x00,0x02,0x90,0x3E,0x91,0x40,0x92,0x4C,0x00,0x7A,0x80,0x81,0x82,0x00,
0x02,0x90,0x3E,0x91,0x41,0x92,0x4D,0x00,0x7A,0x81,0x82,0x00,0x02,0x91,0x41,0x92,
0x4D,0x00,0x7A,0x81,0x82,0x00,0x02,0x91,0x41,0x92,0x4D,0x00,0x7A,0x81,0x82,0x00,
0x02,0x92,0x4F,0x00,0x7A,0x80,0x82,0x00,0x02,0x90,0x37,0x92,0x4F,0x00,0x7A,0x82,
0x00,0x02,0x92,0x4A,0x00,0x7A,0x82,0x00,0x02,0x91,0x41,0x92,0x4D,0x00,0x7A,0x81,
0x82,0x00,0x02,0x91,0x41,0x92,0x4D,0x00,0x7A,0x81,0x82,0x00,0x02,0x91,0x40,0x92,
0x4C,0x00,0x7A,0x81,0x82,0x00,0x02,0x92,0x4A,0x00,0x7A,0x82,0x00,0x02,0x92,0x4A,
0x00,0x7A,0x82,0x00,0x02,0x92,0x4F,0x00,0x7A,0x80,0x82,0x00,0x02,0x91,0x40,0x92,
0x4C,0x90,0x37,0x00,0x7A,0x81,0x82,0x80,0x00,0x02,0x91,0x40,0x92,0x4C,0x90,0x37,
0x00,0x7A,0x81,0x82,0x80,0x00,0x02,0x91,0x40,0x92,0x4C,0x90,0x37,0x00,0x7A,0x81,
0x82,0x80,0x00,0x02,0x91,0x40,0x92,0x4C,0x90,0x37,0x00,0x7A,0x81,0x82,0x80,0x00,
0x02,0x90,0x3E,0x91,0x3F,0x92,0x4B,0x00,0x7A,0x81,0x82,0x00,0x02,0x91,0x3F,0x92,
0x4B,0x00,0x7A,0x81,0x82,0x00,0x02,0x91,0x3F,0x92,0x4B,0x00,0x7A,0x81,0x82,0x00,
0x02,0x92,0x4F,0x00,0x7A,0x80,0x82,0x00,0x02,0x90,0x3C,0x92,0x4F,0x00,0x7A,0x82,
0x00,0x02,0x92,0x4A,0x00,0x3C,0x80,0x00,0x3E,0x82,0x00,0x02,0x91,0x3F,0x92,0x4B,
0x90,0x37,0x00,0x7A,0x81,0x82,0x80,0x00,0x02,0x90,0x3A,0x91,0x3F,0x92,0x4B,0x00,
0x3C,0x80,0x00,0x3E,0x81,0x82,0x00,0x02,0x90,0x3A,0x91,0x3A,0x92,0x46,0x00,0x7A,
0x00,0x02,0x00,0x7A,0x81,0x82,0x00,0x02,0x92,0x4A,0x00,0x7A,0x82,0x00,0x02,0x92,
0x4F,0x00,0x7A,0x80,0x82,0x00,0x02,0x90,0x39,0x91,0x3C,0x92,0x48,0x00,0x7A,0x00,
0x02,0x00,0x7A,0x81,0x82,0x00,0x02,0x92,0x4A,0x00,0x7A,0x82,0x00,0x02,0x92,0x4F,
0x00,0x7A,0x80,0x82,0x00,0x02,0x90,0x37,0x91,0x3E,0x92,0x4A,0x00,0x7A,0x81,0x82,
0x00,0x02,0x91,0x3E,0x92,0x4A,0x00,0x7A,0x81,0x82,0x00,0x02,0x91,0x3E,0x92,0x4A,
0x00,0x7A,0x81,0x82,0x00,0x02,0x92,0x4F,0x00,0x7A,0x82,0x00,0x02,0x92,0x4F,0x00,
0x7A,0x82,0x00,0x02,0x92,0x4A,0x00,0x7A,0x82,0x00,0x02,0x91,0x3E,0x92,0x4A,0x00,
0x7A,0x81,0x82,0x00,0x02,0x91,0x3E,0x92,0x4A,0x00,0x7A,0x81,0x82,0x00,0x02,0x91,
0x3E,0x92,0x4A,0x00,0x7A,0x81,0x82,0x00,0x02,0x92,0x4A,0x00,0x7A,0x82,0x00,0x02,
0x92,0x4A,0x00,0x7A,0x82,0x00,0x02,0x92,0x4F,0x00,0x7A,0x82,0x00,0x02,0x91,0x3E,
0x92,0x4A,0x00,0x7A,0x81,0x82,0x00,0x02,0x91,0x3E,0x92,0x4A,0x00,0x7A,0x80,0x81,
0x82,0x00,0x02,0x91,0x3E,0x92,0x4A,0x90,0x37,0x00,0x7A,0x81,0x82,0x80,0x00,0x02,
0x91,0x3E,0x92,0x4A,0x90,0x37,0x00,0x7A,0x81,0x82,0x80,0x00,0x02,0x90,0x3E,0x91,
0x3F,0x92,0x4B,0x00,0x7A,0x81,0x82,0x00,0x02,0x91,0x3F,0x92,0x4B,0x00,0x7A,0x81,
0x82,0x00,0x02,0x91,0x3F,0x92,0x4B,0x00,0x7A,0x81,0x82,0x00,0x02,0x92,0x4F,0x00,
0x7A,0x80,0x82,0x00,0x02,0x90,0x3C,0x92,0x4F,0x00,0x7A,0x82,0x00,0x02,0x92,0x4A,
0x00,0x3C,0x80,0x00,0x3E,0x82,0x00,0x02,0x91,0x3F,0x92,0x4B,0x90,0x37,0x00,0x7A,
0x81,0x82,0x80,0x00,0x02,0x90,0x3A,0x91,0x3F,0x92,0x4B,0x00,0x3C,0x80,0x00,0x3E,
0x81,0x82,0x00,0x02,0x90,0x3A,0x91,0x40,0x92,0x4C,0x00,0x7A,0x81,0x82,0x00,0x02,
0x92,0x4A,0x00,0x7A,0x82,0x00,0x02,0x92,0x4A,0x00,0x7A,0x82,0x00,0x02,0x92,0x4F,
0x00,0x7A,0x80,0x82,0x00,0x02,0x90,0x3C,0x91,0x40,0x92,0x4C,0x00,0x7A,0x81,0x82,
0x00,0x02,0x91,0x40,0x92,0x4C,0x00,0x7A,0x81,0x82,0x00,0x02,0x91,0x40,0x92,0x4C,
0x00,0x7A,0x80,0x81,0x82,0x00,0x02,0x90,0x3E,0x91,0x40,0x92,0x4C,0x00,0x7A,0x80,
0x81,0x82,0x00,0x02,0x90,0x3E,0x91,0x41,0x92,0x4D,0x00,0x7A,0x81,0x82,0x00,0x02,
0x91,0x41,0x92,0x4D,0x00,0x7A,0x81,0x82,0x00,0x02,0x91,0x41,0x92,0x4D,0x00,0x7A,
0x81,0x82,0x00,0x02,0x92,0x4F,0x00,0x7A,0x80,0x82,0x00,0x02,0x90,0x37,0x92,0x4F,
0x00,0x7A,0x82,0x00,0x02,0x92,0x4A,0x00,0x7A,0x82,0x00,0x02,0x91,0x41,0x92,0x4D,
0x00,0x7A,0x81,0x82,0x00,0x02,0x91,0x41,0x92,0x4D,0x00,0x7A,0x81,0x82,0x00,0x02,
0x91,0x40,0x92,0x4C,0x00,0x7A,0x81,0x82,0x00,0x02,0x92,0x4A,0x00,0x7A,0x82,0x00,
0x02,0x92,0x4A,0x00,0x7A,0x82,0x00,0x02,0x92,0x4F,0x00,0x7A,0x80,0x82,0x00,0x02,
0x91,0x40,0x92,0x4C,0x90,0x37,0x00,0x7A,0x81,0x82,0x80,0x00,0x02,0x91,0x40,0x92,
0x4C,0x90,0x37,0x00,0x7A,0x81,0x82,0x80,0x00,0x02,0x91,0x40,0x92,0x4C,0x90,0x37,
0x00,0x7A,0x81,0x82,0x80,0x00,0x02,0x91,0x40,0x92,0x4C,0x90,0x37,0x00,0x7A,0x81,
0x82,0x80,0x00,0x02,0x90,0x3E,0x91,0x3F,0x92,0x4B,0x00,0x7A,0x81,0x82,0x00,0x02,
0x91,0x3F,0x92,0x4B,0x00,0x7A,0x81,0x82,0x00,0x02,0x91,0x3F,0x92,0x4B,0x00,0x7A,
0x81,0x82,0x00,0x02,0x92,0x4F,0x00,0x7A,0x80,0x82,0x00,0x02,0x90,0x3C,0x92,0x4F,
0x00,0x7A,0x82,0x00,0x02,0x92,0x4A,0x00,0x3C,0x80,0x00,0x3E,0x82,0x00,0x02,0x91,
0x3F,0x92,0x4B,0x90,0x37,0x00,0x7A,0x81,0x82,0x80,0x00,0x02,0x90,0x3A,0x91,0x3F,
0x92,0x4B,0x00,0x3C,0x80,0x00,0x3E,0x81,0x82,0x00,0x02,0x90,0x3A,0x91,0x3A,0x92,
0x46,0x00,0x7A,0x00,0x02,0x00,0x7A,0x81,0x82,0x00,0x02,0x92,0x4A,0x00,0x7A,0x82,
0x00,0x02,0x92,0x4F,0x00,0x7A,0x80,0x82,0x00,0x02,0x90,0x39,0x91,0x3C,0x92,0x48,
0x00,0x7A,0x00,0x02,0x00,0x7A,0x81,0x82,0x00,0x02,0x92,0x4A,0x00,0x7A,0x82,0x00,
0x02,0x92,0x4F,0x00,0x7A,0x80,0x82,0x00,0x02,0x90,0x4F,0x91,0x37,0x92,0x3E,0x00,
0x7A,0x82,0x00,0x02,0x92,0x3E,0x00,0x7A,0x82,0x00,0x02,0x92,0x3E,0x00,0x7A,0x82,
0x00,0x02,0x92,0x4F,0x00,0x7A,0x82,0x00,0x02,0x92,0x4F,0x00,0x7A,0x82,0x00,0x02,
0x92,0x4A,0x00,0x7A,0x82,0x00,0x02,0x92,0x3E,0x00,0x7A,0x82,0x00,0x02,0x92,0x3E,
0x00,0x7A,0x82,0x00,0x02,0x92,0x3E,0x00,0x7A,0x82,0x00,0x02,0x92,0x4A,0x00,0x7A,
0x82,0x00,0x02,0x92,0x4A,0x00,0x7A,0x82,0x00,0x02,0x92,0x4F,0x00,0x7A,0x82,0x00,
0x02,0x92,0x3E,0x00,0x7A,0x82,0x00,0x02,0x92,0x3E,0x00,0x7A,0x80,0x81,0x82,0x00,
0x02,0x92,0x3E,0x90,0x4A,0x91,0x37,0x00,0x7A,0x82,0x80,0x81,0x00,0x02,0x92,0x3E,
0x90,0x4A,0x91,0x37,0x00,0x7A,0x82,0x80,0x81,0x00,0x02,0x90,0x56,0x91,0x3E,0x92,
0x3F,0x00,0x7A,0x82,0x00,0x02,0x92,0x3F,0x00,0x7A,0x82,0x00,0x02,0x92,0x3F,0x00,
0x7A,0x82,0x00,0x02,0x92,0x4F,0x00,0x7A,0x80,0x81,0x82,0x00,0x02,0x90,0x54,0x91,
0x3C,0x92,0x4F,0x00,0x7A,0x82,0x00,0x02,0x92,0x4A,0x00,0x3C,0x80,0x81,0x00,0x3E,
0x82,0x00,0x02,0x92,0x3F,0x90,0x4B,0x91,0x37,0x00,0x7A,0x82,0x80,0x81,0x00,0x02,
0x90,0x52,0x91,0x3A,0x92,0x3F,0x00,0x3C,0x80,0x81,0x00,0x3E,0x82,0x00,0x02,0x90,
0x52,0x91,0x3A,0x92,0x40,0x00,0x7A,0x82,0x00,0x02,0x92,0x4A,0x00,0x7A,0x82,0x00,
0x02,0x92,0x4A,0x00,0x7A,0x82,0x00,0x02,0x92,0x4F,0x00,0x7A,0x80,0x81,0x82,0x00,
0x02,0x90,0x54,0x91,0x3C,0x92,0x40,0x00,0x7A,0x82,0x00,0x02,0x92,0x40,0x00,0x7A,
0x82,0x00,0x02,0x92,0x40,0x00,0x7A,0x80,0x81,0x82,0x00,0x02,0x90,0x56,0x91,0x3E,
0x92,0x40,0x00,0x7A,0x80,0x81,0x82,0x00,0x02,0x90,0x56,0x91,0x3E,0x92,0x41,0x00,
0x7A,0x82,0x00,0x02,0x92,0x41,0x00,0x7A,0x82,0x00,0x02,0x92,0x41,0x00,0x7A,0x82,
0x00,0x02,0x92,0x4F,0x00,0x7A,0x80,0x81,0x82,0x00,0x02,0x90,0x4F,0x91,0x37,0x92,
0x4F,0x00,0x7A,0x82,0x00,0x02,0x92,0x4A,0x00,0x7A,0x82,0x00,0x02,0x92,0x41,0x00,
0x7A,0x82,0x00,0x02,0x92,0x41,0x00,0x7A,0x82,0x00,0x02,0x92,0x40,0x00,0x7A,0x82,
0x00,0x02,0x92,0x4A,0x00,0x7A,0x82,0x00,0x02,0x92,0x4A,0x00,0x7A,0x82,0x00,0x02,
0x92,0x4F,0x00,0x7A,0x80,0x81,0x82,0x00,0x02,0x92,0x40,0x90,0x4C,0x91,0x37,0x00,
0x7A,0x82,0x80,0x81,0x00,0x02,0x92,0x40,0x90,0x4C,0x91,0x37,0x00,0x7A,0x82,0x80,
0x81,0x00,0x02,0x92,0x40,0x90,0x4C,0x91,0x37,0x00,0x7A,0x82,0x80,0x81,0x00,0x02,
0x92,0x40,0x90,0x4C,0x91,0x37,0x00,0x7A,0x82,0x80,0x81,0x00,0x02,0x90,0x56,0x91,
0x3E,0x92,0x3F,0x00,0x7A,0x82,0x00,0x02,0x92,0x3F,0x00,0x7A,0x82,0x00,0x02,0x92,
0x3F,0x00,0x7A,0x82,0x00,0x02,0x92,0x4F,0x00,0x7A,0x80,0x81,0x82,0x00,0x02,0x90,
0x54,0x91,0x3C,0x92,0x4F,0x00,0x7A,0x82,0x00,0x02,0x92,0x4A,0x00,0x3C,0x80,0x81,
0x00,0x3E,0x82,0x00,0x02,0x92,0x3F,0x90,0x4B,0x91,0x37,0x00,0x7A,0x82,0x80,0x81,
0x00,0x02,0x90,0x52,0x91,0x3A,0x92,0x3F,0x00,0x3C,0x80,0x81,0x00,0x3E,0x82,0x00,
0x02,0x90,0x52,0x91,0x3A,0x92,0x3A,0x00,0x7A,0x00,0x02,0x00,0x7A,0x82,0x00,0x02,
0x92,0x4A,0x00,0x7A,0x82,0x00,0x02,0x92,0x4F,0x00,0x7A,0x80,0x81,0x82,0x00,0x02,
0x90,0x51,0x91,0x39,0x92,0x3C,0x00,0x7A,0x00,0x02,0x00,0x7A,0x82,0x00,0x02,0x92,
0x4A,0x00,0x7A,0x82,0x00,0x02,0x92,0x4F,0x00,0x7A,0x80,0x81,0x82,0x00,0x02,0x90,
0x4F,0x91,0x37,0x92,0x3E,0x00,0x7A,0x82,0x00,0x02,0x92,0x3E,0x00,0x7A,0x82,0x00,
0x02,0x92,0x3E,0x00,0x7A,0x82,0x00,0x02,0x92,0x4F,0x00,0x7A,0x82,0x00,0x02,0x92,
0x4F,0x00,0x7A,0x82,0x00,0x02,0x92,0x4A,0x00,0x7A,0x82,0x00,0x02,0x92,0x3E,0x00,
0x7A,0x82,0x00,0x02,0x92,0x3E,0x00,0x7A,0x82,0x00,0x02,0x92,0x3E,0x00,0x7A,0x82,
0x00,0x02,0x92,0x4A,0x00,0x7A,0x82,0x00,0x02,0x92,0x4A,0x00,0x7A,0x82,0x00,0x02,
0x92,0x4F,0x00,0x7A,0x82,0x00,0x02,0x92,0x3E,0x00,0x7A,0x82,0x00,0x02,0x92,0x3E,
0x00,0x7A,0x80,0x81,0x82,0x00,0x02,0x92,0x3E,0x90,0x4A,0x91,0x37,0x00,0x7A,0x82,
0x80,0x81,0x00,0x02,0x92,0x3E,0x90,0x4A,0x91,0x37,0x00,0x7A,0x82,0x80,0x81,0x00,
0x02,0x90,0x56,0x91,0x3E,0x92,0x3F,0x00,0x7A,0x82,0x00,0x02,0x92,0x3F,0x00,0x7A,
0x82,0x00,0x02,0x92,0x3F,0x00,0x7A,0x82,0x00,0x02,0x92,0x4F,0x00,0x7A,0x80,0x81,
0x82,0x00,0x02,0x90,0x54,0x91,0x3C,0x92,0x4F,0x00,0x7A,0x82,0x00,0x02,0x92,0x4A,
0x00,0x3C,0x80,0x81,0x00,0x3E,0x82,0x00,0x02,0x92,0x3F,0x90,0x4B,0x91,0x37,0x00,
0x7A,0x82,0x80,0x81,0x00,0x02,0x90,0x52,0x91,0x3A,0x92,0x3F,0x00,0x3C,0x80,0x81,
0x00,0x3E,0x82,0x00,0x02,0x90,0x52,0x91,0x3A,0x92,0x40,0x00,0x7A,0x82,0x00,0x02,
0x92,0x4A,0x00,0x7A,0x82,0x00,0x02,0x92,0x4A,0x00,0x7A,0x82,0x00,0x02,0x92,0x4F,
0x00,0x7A,0x80,0x81,0x82,0x00,0x02,0x90,0x54,0x91,0x3C,0x92,0x40,0x00,0x7A,0x82,
0x00,0x02,0x92,0x40,0x00,0x7A,0x82,0x00,0x02,0x92,0x40,0x00,0x7A,0x80,0x81,0x82,
0x00,0x02,0x90,0x56,0x91,0x3E,0x92,0x40,0x00,0x7A,0x80,0x81,0x82,0x00,0x02,0x90,
0x56,0x91,0x3E,0x92,0x41,0x00,0x7A,0x82,0x00,0x02,0x92,0x41,0x00,0x7A,0x82,0x00,
0x02,0x92,0x41,0x00,0x7A,0x82,0x00,0x02,0x92,0x4F,0x00,0x7A,0x80,0x81,0x82,0x00,
0x02,0x90,0x4F,0x91,0x37,0x92,0x4F,0x00,0x7A,0x82,0x00,0x02,0x92,0x4A,0x00,0x7A,
0x82,0x00,0x02,0x92,0x41,0x00,0x7A,0x82,0x00,0x02,0x92,0x41,0x00,0x7A,0x82,0x00,
0x02,0x92,0x40,0x00,0x7A,0x82,0x00,0x02,0x92,0x4A,0x00,0x7A,0x82,0x00,0x02,0x92,
0x4A,0x00,0x7A,0x82,0x00,0x02,0x92,0x4F,0x00,0x7A,0x80,0x81,0x82,0x00,0x02,0x92,
0x40,0x90,0x4C,0x91,0x37,0x00,0x7A,0x82,0x80,0x81,0x00,0x02,0x92,0x40,0x90,0x4C,
0x91,0x37,0x00,0x7A,0x82,0x80,0x81,0x00,0x02,0x92,0x40,0x90,0x4C,0x91,0x37,0x00,
0x7A,0x82,0x80,0x81,0x00,0x02,0x92,0x40,0x90,0x4C,0x91,0x37,0x00,0x7A,0x82,0x80,
0x81,0x00,0x02,0x90,0x56,0x91,0x3E,0x92,0x3F,0x00,0x7A,0x82,0x00,0x02,0x92,0x3F,
0x00,0x7A,0x82,0x00,0x02,0x92,0x3F,0x00,0x7A,0x82,0x00,0x02,0x92,0x4F,0x00,0x7A,
0x80,0x81,0x82,0x00,0x02,0x90,0x54,0x91,0x3C,0x92,0x4F,0x00,0x7A,0x82,0x00,0x02,
0x92,0x4A,0x00,0x3C,0x80,0x81,0x00,0x3E,0x82,0x00,0x02,0x92,0x3F,0x90,0x4B,0x91,
0x37,0x00,0x7A,0x82,0x80,0x81,0x00,0x02,0x90,0x52,0x91,0x3A,0x92,0x3F,0x00,0x3C,
0x80,0x81,0x00,0x3E,0x82,0x00,0x02,0x90,0x52,0x91,0x3A,0x92,0x3A,0x00,0x7A,0x00,
0x02,0x00,0x7A,0x82,0x00,0x02,0x92,0x4A,0x00,0x7A,0x82,0x00,0x02,0x92,0x4F,0x00,
0x7A,0x80,0x81,0x82,0x00,0x02,0x90,0x51,0x91,0x39,0x92,0x3C,0x00,0x7A,0x00,0x02,
0x00,0x7A,0x82,0x00,0x02,0x92,0x4A,0x00,0x7A,0x82,0x00,0x02,0x92,0x4F,0x00,0x7A,
0x80,0x81,0x82,0x00,0x02,0x92,0x4F,0x00,0x7A,0x82,0x00,0x02,0x92,0x4A,0x00,0x7A,
0x82,0x00,0x02,0x92,0x4A,0x00,0x7A,0x82,0x00,0x02,0x92,0x4F,0x00,0x7A,0x82,0x00,
0x02,0x91,0x37,0x92,0x4F,0x00,0x7A,0x82,0x00,0x02,0x92,0x4A,0x00,0x7A,0x82,0x00,
0x02,0x92,0x4A,0x00,0x7A,0x82,0x00,0x02,0x92,0x4F,0x00,0x7A,0x81,0x82,0x00,0x02,
0x91,0x43,0x92,0x4F,0x00,0x7A,0x82,0x00,0x02,0x92,0x4A,0x00,0x7A,0x82,0x00,0x02,
0x92,0x4A,0x00,0x7A,0x82,0x00,0x02,0x92,0x4F,0x00,0x7A,0x82,0x00,0x02,0x92,0x4F,
0x00,0x7A,0x82,0x00,0x02,0x92,0x4A,0x00,0x7A,0x82,0x00,0x02,0x92,0x4A,0x00,0x7A,
0x82,0x00,0x02,0x92,0x4F,0x00,0x7A,0x81,0x82,0x00,0x02,0x90,0x37,0x92,0x4F,0x00,
0x7A,0x82,0x00,0x02,0x92,0x4A,0x00,0x7A,0x82,0x00,0x02,0x92,0x4A,0x00,0x7A,0x82,
0x00,0x02,0x92,0x4F,0x00,0x7A,0x82,0x00,0x02,0x92,0x4F,0x00,0x7A,0x82,0x00,0x02,
0x92,0x4A,0x00,0x7A,0x82,0x00,0x02,0x92,0x4A,0x00,0x7A,0x82,0x00,0x02,0x92,0x4F,
0x00,0x7A,0x82,0x00,0x02,0x91,0x3C,0x92,0x4F,0x00,0x7A,0x82,0x00,0x02,0x92,0x4A,
0x00,0x7A,0x82,0x00,0x02,0x92,0x4A,0x00,0x7A,0x82,0x00,0x02,0x92,0x4F,0x00,0x7A,
0x82,0x00,0x02,0x92,0x4F,0x00,0x7A,0x82,0x00,0x02,0x92,0x4A,0x00,0x7A,0x82,0x00,
0x02,0x92,0x4A,0x00,0x7A,0x82,0x00,0x02,0x92,0x4F,0x00,0x7A,0x80,0x81,0x82,0x00,
0x02,0x91,0x3E,0x92,0x4F,0x00,0x7A,0x82,0x00,0x02,0x92,0x4A,0x00,0x7A,0x81,0x82,
0x00,0x02,0x91,0x3A,0x92,0x4A,0x00,0x7A,0x82,0x00,0x02,0x92,0x4F,0x00,0x7A,0x81,
0x82,0x00,0x02,0x91,0x37,0x92,0x4F,0x00,0x7A,0x82,0x00,0x02,0x92,0x4A,0x00,0x7A,
0x82,0x00,0x02,0x92,0x4A,0x00,0x7A,0x82,0x00,0x02,0x92,0x4F,0x00,0x7A,0x81,0x82,
0x00,0x02,0x91,0x43,0x92,0x4F,0x00,0x7A,0x82,0x00,0x02,0x92,0x4A,0x00,0x7A,0x82,
0x00,0x02,0x92,0x4A,0x00,0x7A,0x82,0x00,0x02,0x92,0x4F,0x00,0x7A,0x82,0x00,0x02,
0x92,0x4F,0x00,0x7A,0x82,0x00,0x02,0x92,0x4A,0x00,0x7A,0x82,0x00,0x02,0x92,0x4A,
0x00,0x7A,0x82,0x00,0x02,0x92,0x4F,0x00,0x7A,0x81,0x82,0x00,0x02,0x90,0x3F,0x91,
0x3C,0x92,0x4F,0x00,0x7A,0x82,0x00,0x02,0x92,0x4A,0x00,0x7A,0x82,0x00,0x02,0x92,
0x4A,0x00,0x7A,0x82,0x00,0x02,0x92,0x4F,0x00,0x7A,0x82,0x00,0x02,0x92,0x4F,0x00,
0x7A,0x82,0x00,0x02,0x92,0x4A,0x00,0x7A,0x82,0x00,0x02,0x92,0x4A,0x00,0x7A,0x82,
0x00,0x02,0x92,0x4F,0x00,0x7A,0x80,0x81,0x82,0x00,0x02,0x90,0x3E,0x91,0x3B,0x92,
0x4F,0x00,0x7A,0x82,0x00,0x02,0x92,0x4A,0x00,0x7A,0x82,0x00,0x02,0x92,0x4A,0x00,
0x7A,0x82,0x00,0x02,0x92,0x4F,0x00,0x7A,0x82,0x00,0x02,0x92,0x4F,0x00,0x7A,0x82,
0x00,0x02,0x92,0x4A,0x00,0x7A,0x82,0x00,0x02,0x92,0x4A,0x00,0x7A,0x82,0x00,0x02,
0x92,0x4F,0x00,0x7A,0x80,0x81,0x82,0x00,0x02,0x90,0x48,0x91,0x4C,0x92,0x54,0x00,
0x7A,0x81,0x00,0x02,0x91,0x48,0x00,0x7A,0x81,0x00,0x02,0x91,0x48,0x00,0x7A,0x81,
0x00,0x02,0x91,0x4C,0x00,0x7A,0x81,0x00,0x02,0x91,0x3C,0x00,0x7A,0x00,0x02,0x00,
0x7A,0x00,0x02,0x00,0x7A,0x00,0x02,0x00,0x7A,0x81,0x00,0x02,0x91,0x48,0x00,0x7A,
0x00,0x02,0x00,0x7A,0x00,0x02,0x00,0x7A,0x00,0x02,0x00,0x7A,0x00,0x02,0x00,0x7A,
0x00,0x02,0x00,0x7A,0x00,0x02,0x00,0x7A,0x00,0x02,0x00,0x7A,0x81,0x00,0x02,0x91,
0x4C,0x00,0x7A,0x81,0x00,0x02,0x91,0x48,0x00,0x7A,0x81,0x00,0x02,0x91,0x48,0x00,
0x7A,0x81,0x00,0x02,0x91,0x4C,0x00,0x7A,0x81,0x00,0x02,0x91,0x41,0x00,0x7A,0x00,
0x02,0x00,0x7A,0x00,0x02,0x00,0x7A,0x00,0x02,0x00,0x7A,0x81,0x00,0x02,0x91,0x4D,
0x00,0x7A,0x00,0x02,0x00,0x7A,0x00,0x02,0x00,0x7A,0x00,0x02,0x00,0x7A,0x00,0x02,
0x00,0x7A,0x00,0x02,0x00,0x7A,0x81,0x00,0x02,0x91,0x48,0x00,0x7A,0x00,0x02,0x00,
0x7A,0x82,0x80,0x81,0x00,0x02,0x91,0x3E,0x92,0x56,0x90,0x4A,0x01,0xF4,0x01,0xF1,
0x00,0x02,0x03,0xE5,0x80,0x81,0x82,0x00,0x02,0x90,0x4C,0x92,0x4C,0x91,0x3B,0x01,
0xF4,0x01,0xF1,0x80,0x82,0x81,0x00,0x02,0x90,0x4B,0x92,0x4B,0x91,0x3B,0x03,0xE5,
0x80,0x82,0x81,0x00,0x02,0x92,0x43,0x91,0x43,0x90,0x40,0x00,0x7A,0x82,0x81,0x00,
0x02,0x92,0x43,0x91,0x43,0x00,0x7A,0x82,0x81,0x00,0x02,0x92,0x40,0x91,0x40,0x00,
0x7A,0x82,0x81,0x00,0x02,0x92,0x40,0x91,0x40,0x00,0x7A,0x82,0x81,0x00,0x02,0x92,
0x40,0x91,0x40,0x00,0x7A,0x82,0x81,0x00,0x02,0x92,0x40,0x91,0x40,0x00,0x7A,0x82,
0x81,0x00,0x02,0x92,0x43,0x91,0x43,0x00,0x7A,0x82,0x81,0x00,0x02,0x92,0x43,0x91,
0x43,0x00,0x7A,0x82,0x81,0x00,0x02,0x92,0x43,0x91,0x43,0x00,0x7A,0x82,0x81,0x00,
0x02,0x92,0x43,0x91,0x43,0x00,0x7A,0x82,0x81,0x00,0x02,0x92,0x40,0x91,0x40,0x00,
0x7A,0x82,0x81,0x00,0x02,0x92,0x40,0x91,0x40,0x00,0x7A,0x82,0x81,0x00,0x02,0x92,
0x40,0x91,0x40,0x00,0x7A,0x82,0x81,0x00,0x02,0x92,0x40,0x91,0x40,0x00,0x7A,0x82,
0x81,0x00,0x02,0x92,0x43,0x91,0x43,0x00,0x7A,0x82,0x81,0x00,0x02,0x92,0x43,0x91,
0x43,0x00,0x7A,0x80,0x82,0x81,0x00,0x02,0x90,0x3C,0x91,0x40,0x92,0x43,0x00,0x7A,
0x82,0x00,0x02,0x92,0x43,0x00,0x7A,0x82,0x00,0x02,0x92,0x40,0x00,0x7A,0x82,0x00,
0x02,0x92,0x40,0x00,0x7A,0x82,0x00,0x02,0x92,0x40,0x00,0x7A,0x00,0x02,0x00,0x7A,
0x00,0x02,0x00,0x7A,0x00,0x02,0x00,0x7A,0x82,0x00,0x02,0x92,0x40,0x00,0x7A,0x00,
0x02,0x00,0x7A,0x00,0x02,0x00,0x7A,0x00,0x02,0x00,0x7A,0x00,0x02,0x00,0x7A,0x00,
0x02,0x00,0x7A,0x82,0x00,0x02,0x92,0x47,0x00,0x7A,0x00,0x02,0x00,0x7A,0x80,0x81,
0x82,0x00,0x02,0x90,0x3D,0x91,0x40,0x92,0x45,0x00,0x7A,0x00,0x02,0x00,0x7A,0x00,
0x02,0x00,0x7A,0x00,0x02,0x00,0x7A,0x00,0x02,0x00,0x7A,0x00,0x02,0x00,0x7A,0x00,
0x02,0x00,0x7A,0x00,0x02,0x00,0x7A,0x00,0x02,0x00,0x7A,0x00,0x02,0x00,0x7A,0x00,
0x02,0x00,0x7A,0x00,0x02,0x00,0x7A,0x00,0x02,0x00,0x7A,0x00,0x02,0x00,0x7A,0x00,
0x02,0x00,0x7A,0x00,0x02,0x00,0x7A,0x80,0x81,0x82,0x00,0x02,0x90,0x3C,0x91,0x40,
0x92,0x43,0x00,0x7A,0x82,0x00,0x02,0x92,0x43,0x00,0x7A,0x82,0x00,0x02,0x92,0x40,
0x00,0x7A,0x82,0x00,0x02,0x92,0x40,0x00,0x7A,0x82,0x00,0x02,0x92,0x40,0x00,0x7A,
0x82,0x00,0x02,0x92,0x40,0x00,0x7A,0x82,0x00,0x02,0x92,0x43,0x00,0x7A,0x82,0x00,
0x02,0x92,0x43,0x00,0x7A,0x80,0x81,0x82,0x00,0x02,0x90,0x3E,0x91,0x42,0x92,0x43,
0x00,0x7A,0x82,0x00,0x02,0x92,0x43,0x00,0x7A,0x82,0x00,0x02,0x92,0x40,0x00,0x7A,
0x82,0x00,0x02,0x92,0x40,0x00,0x7A,0x82,0x00,0x02,0x92,0x40,0x00,0x7A,0x82,0x00,
0x02,0x92,0x40,0x00,0x7A,0x82,0x00,0x02,0x92,0x43,0x00,0x7A,0x82,0x00,0x02,0x92,
0x43,0x00,0x7A,0x80,0x81,0x82,0x00,0x02,0x90,0x40,0x91,0x43,0x92,0x4F,0x00,0x7A,
0x82,0x00,0x02,0x92,0x4F,0x00,0x7A,0x82,0x00,0x02,0x92,0x4C,0x00,0x7A,0x82,0x00,
0x02,0x92,0x4C,0x00,0x7A,0x82,0x00,0x02,0x92,0x4C,0x00,0x7A,0x82,0x00,0x02,0x92,
0x4C,0x00,0x7A,0x82,0x00,0x02,0x92,0x4F,0x00,0x7A,0x82,0x00,0x02,0x92,0x4F,0x00,
0x7A,0x82,0x00,0x02,0x92,0x4F,0x00,0x7A,0x82,0x00,0x02,0x92,0x4F,0x00,0x7A,0x82,
0x00,0x02,0x92,0x4C,0x00,0x7A,0x82,0x00,0x02,0x92,0x4C,0x00,0x7A,0x82,0x00,0x02,
0x92,0x4C,0x00,0x7A,0x82,0x00,0x02,0x92,0x4C,0x00,0x7A,0x82,0x00,0x02,0x92,0x4F,
0x00,0x7A,0x82,0x00,0x02,0x92,0x4F,0x00,0x7A,0x80,0x81,0x82,0x00,0x02,0x90,0x3C,
0x91,0x40,0x92,0x4F,0x00,0x7A,0x82,0x00,0x02,0x92,0x4F,0x00,0x7A,0x82,0x00,0x02,
0x92,0x4C,0x00,0x7A,0x82,0x00,0x02,0x92,0x4C,0x00,0x7A,0x82,0x00,0x02,0x92,0x4C,
0x00,0x7A,0x00,0x02,0x00,0x7A,0x00,0x02,0x00,0x7A,0x00,0x02,0x00,0x7A,0x82,0x00,
0x02,0x92,0x4C,0x00,0x7A,0x00,0x02,0x00,0x7A,0x00,0x02,0x00,0x7A,0x00,0x02,0x00,
0x7A,0x00,0x02,0x00,0x7A,0x00,0x02,0x00,0x7A,0x82,0x00,0x02,0x92,0x53,0x00,0x7A,
0x00,0x02,0x00,0x7A,0x80,0x81,0x82,0x00,0x02,0x90,0x3D,0x91,0x40,0x92,0x51,0x00,
0x7A,0x00,0x02,0x00,0x7A,0x00,0x02,0x00,0x7A,0x00,0x02,0x00,0x7A,0x00,0x02,0x00,
0x7A,0x00,0x02,0x00,0x7A,0x00,0x02,0x00,0x7A,0x00,0x02,0x00,0x7A,0x00,0x02,0x00,
0x7A,0x00,0x02,0x00,0x7A,0x00,0x02,0x00,0x7A,0x00,0x02,0x00,0x7A,0x00,0x02,0x00,
0x7A,0x00,0x02,0x00,0x7A,0x00,0x02,0x00,0x7A,0x00,0x02,0x00,0x7A,0x80,0x81,0x82,
0x00,0x02,0x90,0x3C,0x91,0x40,0x92,0x53,0x00,0x7A,0x82,0x00,0x02,0x92,0x53,0x00,
0x7A,0x82,0x00,0x02,0x92,0x4E,0x00,0x7A,0x82,0x00,0x02,0x92,0x4E,0x00,0x7A,0x82,
0x00,0x02,0x92,0x4E,0x00,0x7A,0x82,0x00,0x02,0x92,0x4E,0x00,0x7A,0x82,0x00,0x02,
0x92,0x53,0x00,0x7A,0x82,0x00,0x02,0x92,0x53,0x00,0x7A,0x80,0x81,0x82,0x00,0x02,
0x90,0x3B,0x91,0x3F,0x92,0x53,0x00,0x7A,0x82,0x00,0x02,0x92,0x53,0x00,0x7A,0x82,
0x00,0x02,0x92,0x4E,0x00,0x7A,0x82,0x00,0x02,0x92,0x4E,0x00,0x7A,0x82,0x00,0x02,
0x92,0x4E,0x00,0x7A,0x82,0x00,0x02,0x92,0x4E,0x00,0x7A,0x82,0x00,0x02,0x92,0x53,
0x00,0x7A,0x82,0x00,0x02,0x92,0x53,0x00,0x7A,0x80,0x81,0x82,0x00,0x02,0x90,0x3C,
0x91,0x43,0x92,0x40,0x00,0x7A,0x00,0x02,0x00,0x7A,0x00,0x02,0x00,0x7A,0x00,0x02,
0x00,0x7A,0x00,0x02,0x00,0x7A,0x00,0x02,0x00,0x7A,0x81,0x82,0x00,0x02,0x91,0x42,
0x92,0x3E,0x00,0x7A,0x81,0x82,0x00,0x02,0x91,0x43,0x92,0x40,0x00,0x7A,0x80,0x81,
0x82,0x00,0x02,0x90,0x3E,0x91,0x45,0x92,0x42,0x00,0x7A,0x00,0x02,0x00,0x7A,0x00,
0x02,0x00,0x7A,0x00,0x02,0x00,0x7A,0x00,0x02,0x00,0x7A,0x00,0x02,0x00,0x7A,0x00,
0x02,0x00,0x7A,0x00,0x02,0x00,0x7A,0x80,0x81,0x82,0x00,0x02,0x90,0x3C,0x91,0x56,
0x92,0x3E,0x00,0x7A,0x80,0x81,0x82,0x00,0x7F,0x90,0x3C,0x91,0x58,0x92,0x40,0x00,
0x7A,0x80,0x81,0x82,0x02,0x73,0x90,0x3C,0x91,0x58,0x92,0x40,0x00,0x7A,0x80,0x81,
0x82,0x00,0x7F,0x90,0x3E,0x91,0x5A,0x92,0x42,0x00,0x7A,0x80,0x81,0x82,0x02,0x73,
0x90,0x40,0x91,0x40,0x92,0x40,0x00,0x7A,0x00,0x02,0x00,0x7A,0x00,0x02,0x00,0x7A,
0x00,0x02,0x00,0x7A,0x00,0x02,0x00,0x7A,0x00,0x02,0x00,0x7A,0x00,0x02,0x00,0x7A,
0x00,0x02,0x00,0x7A,0x00,0x02,0x00,0x7A,0x00,0x02,0x00,0x7A,0x00,0x02,0x00,0x7A,
0x00,0x02,0x00,0x7A,0x00,0x02,0x00,0x7A,0x00,0x02,0x00,0x7A,0x00,0x02,0x00,0x7A,
0x00,0x02,0x00,0x7A,0x80,0x00,0x02,0x90,0x3C,0x00,0x7A,0x00,0x02,0x00,0x7A,0x00,
0x02,0x00,0x7A,0x00,0x02,0x00,0x7A,0x81,0x82,0x00,0x02,0x91,0x40,0x92,0x4C,0x00,
0x7A,0x00,0x02,0x00,0x7A,0x00,0x02,0x00,0x7A,0x00,0x02,0x00,0x7A,0x81,0x82,0x00,
0x02,0x91,0x40,0x92,0x4C,0x00,0x7A,0x00,0x02,0x00,0x7A,0x00,0x02,0x00,0x7A,0x00,
0x02,0x00,0x7A,0x00,0x02,0x00,0x7A,0x00,0x02,0x00,0x7A,0x81,0x82,0x00,0x02,0x91,
0x47,0x92,0x53,0x00,0x7A,0x00,0x02,0x00,0x7A,0x80,0x81,0x82,0x00,0x02,0x90,0x3D,
0x91,0x45,0x92,0x51,0x00,0x7A,0x00,0x02,0x00,0x7A,0x00,0x02,0x00,0x7A,0x00,0x02,
0x00,0x7A,0x00,0x02,0x00,0x7A,0x00,0x02,0x00,0x7A,0x00,0x02,0x00,0x7A,0x00,0x02,
0x00,0x7A,0x00,0x02,0x00,0x7A,0x00,0x02,0x00,0x7A,0x00,0x02,0x00,0x7A,0x00,0x02,
0x00,0x7A,0x00,0x02,0x00,0x7A,0x00,0x02,0x00,0x7A,0x00,0x02,0x00,0x7A,0x00,0x02,
0x00,0x7A,0x80,0x81,0x82,0x00,0x02,0x90,0x3C,0x91,0x43,0x92,0x4F,0x00,0x7A,0x00,
0x02,0x00,0x7A,0x00,0x02,0x00,0x7A,0x00,0x02,0x00,0x7A,0x00,0x02,0x00,0x7A,0x00,
0x02,0x00,0x7A,0x00,0x02,0x00,0x7A,0x00,0x02,0x00,0x7A,0x80,0x81,0x82,0x00,0x02,
0x90,0x3E,0x91,0x42,0x92,0x4E,0x00,0x7A,0x00,0x02,0x00,0x7A,0x00,0x02,0x00,0x7A,
0x00,0x02,0x00,0x7A,0x00,0x02,0x00,0x7A,0x00,0x02,0x00,0x7A,0x00,0x02,0x00,0x7A,
0x00,0x02,0x00,0x7A,0x80,0x81,0x82,0x00,0x02,0x90,0x40,0x91,0x40,0x92,0x4C,0x00,
0x7A,0x00,0x02,0x00,0x7A,0x00,0x02,0x00,0x7A,0x00,0x02,0x00,0x7A,0x00,0x02,0x00,
0x7A,0x00,0x02,0x00,0x7A,0x00,0x02,0x00,0x7A,0x00,0x02,0x00,0x7A,0x00,0x02,0x00,
0x7A,0x00,0x02,0x00,0x7A,0x00,0x02,0x00,0x7A,0x00,0x02,0x00,0x7A,0x00,0x02,0x00,
0x7A,0x00,0x02,0x00,0x7A,0x00,0x02,0x00,0x7A,0x00,0x02,0x00,0x7A,0x80,0x00,0x02,
0x90,0x3C,0x00,0x7A,0x00,0x02,0x00,0x7A,0x00,0x02,0x00,0x7A,0x00,0x02,0x00,0x7A,
0x81,0x82,0x00,0x02,0x91,0x40,0x92,0x4C,0x00,0x7A,0x00,0x02,0x00,0x7A,0x00,0x02,
0x00,0x7A,0x00,0x02,0x00,0x7A,0x81,0x82,0x00,0x02,0x91,0x40,0x92,0x4C,0x00,0x7A,
0x00,0x02,0x00,0x7A,0x00,0x02,0x00,0x7A,0x00,0x02,0x00,0x7A,0x00,0x02,0x00,0x7A,
0x00,0x02,0x00,0x7A,0x81,0x82,0x00,0x02,0x91,0x47,0x92,0x53,0x00,0x7A,0x00,0x02,
0x00,0x7A,0x80,0x81,0x82,0x00,0x02,0x90,0x3D,0x91,0x49,0x92,0x55,0x00,0x7A,0x00,
0x02,0x00,0x7A,0x00,0x02,0x00,0x7A,0x00,0x02,0x00,0x7A,0x00,0x02,0x00,0x7A,0x00,
0x02,0x00,0x7A,0x00,0x02,0x00,0x7A,0x00,0x02,0x00,0x7A,0x81,0x82,0x00,0x02,0x91,
0x45,0x92,0x51,0x00,0x7A,0x00,0x02,0x00,0x7A,0x00,0x02,0x00,0x7A,0x00,0x02,0x00,
0x7A,0x00,0x02,0x00,0x7A,0x00,0x02,0x00,0x7A,0x00,0x02,0x00,0x7A,0x00,0x02,0x00,
0x7A,0x80,0x81,0x82,0x00,0x02,0x90,0x3C,0x91,0x47,0x92,0x53,0x00,0x7A,0x00,0x02,
0x00,0x7A,0x00,0x02,0x00,0x7A,0x00,0x02,0x00,0x7A,0x00,0x02,0x00,0x7A,0x00,0x02,
0x00,0x7A,0x00,0x02,0x00,0x7A,0x00,0x02,0x00,0x7A,0x80,0x00,0x02,0x90,0x3B,0x00,
0x7A,0x00,0x02,0x00,0x7A,0x00,0x02,0x00,0x7A,0x00,0x02,0x00,0x7A,0x00,0x02,0x00,
0x7A,0x00,0x02,0x00,0x7A,0x00,0x02,0x00,0x7A,0x00,0x02,0x00,0x7A,0x80,0x81,0x82,
0x00,0x02,0x90,0x43,0x92,0x4F,0x91,0x58,0x00,0x7A,0x82,0x00,0x02,0x92,0x4F,0x00,
0x7A,0x82,0x00,0x02,0x92,0x4C,0x00,0x7A,0x82,0x00,0x02,0x92,0x4C,0x00,0x7A,0x82,
0x00,0x02,0x92,0x4C,0x00,0x7A,0x82,0x00,0x02,0x92,0x4C,0x00,0x7A,0x82,0x00,0x02,
0x92,0x4F,0x00,0x7A,0x82,0x00,0x02,0x92,0x4F,0x00,0x7A,0x82,0x00,0x02,0x92,0x4F,
0x00,0x7A,0x82,0x00,0x02,0x92,0x4F,0x00,0x7A,0x82,0x00,0x02,0x92,0x4C,0x00,0x7A,
0x82,0x00,0x02,0x92,0x4C,0x00,0x7A,0x82,0x00,0x02,0x92,0x4C,0x00,0x7A,0x82,0x00,
0x02,0x92,0x4C,0x00,0x7A,0x82,0x00,0x02,0x92,0x4F,0x00,0x7A,0x82,0x00,0x02,0x92,
0x4F,0x00,0x7A,0x80,0x82,0x00,0x02,0x90,0x40,0x92,0x4F,0x00,0x7A,0x82,0x00,0x02,
0x92,0x4F,0x00,0x7A,0x82,0x00,0x02,0x92,0x4C,0x00,0x7A,0x82,0x00,0x02,0x92,0x4C,
0x00,0x7A,0x82,0x81,0x00,0x02,0x92,0x4C,0x91,0x58,0x00,0x7A,0x82,0x00,0x02,0x92,
0x4C,0x00,0x7A,0x82,0x00,0x02,0x92,0x4F,0x00,0x7A,0x82,0x00,0x02,0x92,0x4F,0x00,
0x7A,0x82,0x81,0x00,0x02,0x92,0x4F,0x91,0x58,0x00,0x7A,0x82,0x00,0x02,0x92,0x4F,
0x00,0x7A,0x82,0x00,0x02,0x92,0x4C,0x00,0x7A,0x82,0x00,0x02,0x92,0x4C,0x00,0x7A,
0x82,0x00,0x02,0x92,0x4C,0x00,0x7A,0x82,0x00,0x02,0x92,0x4C,0x00,0x7A,0x82,0x81,
0x00,0x02,0x92,0x4F,0x91,0x5F,0x00,0x7A,0x82,0x00,0x02,0x92,0x4F,0x00,0x7A,0x80,
0x82,0x81,0x00,0x02,0x90,0x40,0x92,0x4F,0x91,0x5D,0x00,0x7A,0x82,0x00,0x02,0x92,
0x4F,0x00,0x7A,0x82,0x00,0x02,0x92,0x4C,0x00,0x7A,0x82,0x00,0x02,0x92,0x4C,0x00,
0x7A,0x82,0x00,0x02,0x92,0x4C,0x00,0x7A,0x82,0x00,0x02,0x92,0x4C,0x00,0x7A,0x82,
0x00,0x02,0x92,0x4F,0x00,0x7A,0x82,0x00,0x02,0x92,0x4F,0x00,0x7A,0x82,0x00,0x02,
0x92,0x4F,0x00,0x7A,0x82,0x00,0x02,0x92,0x4F,0x00,0x7A,0x82,0x00,0x02,0x92,0x4C,
0x00,0x7A,0x82,0x00,0x02,0x92,0x4C,0x00,0x7A,0x82,0x00,0x02,0x92,0x4C,0x00,0x7A,
0x82,0x00,0x02,0x92,0x4C,0x00,0x7A,0x82,0x00,0x02,0x92,0x4F,0x00,0x7A,0x82,0x00,
0x02,0x92,0x4F,0x00,0x7A,0x80,0x82,0x81,0x00,0x02,0x90,0x40,0x92,0x4F,0x91,0x5B,
0x00,0x7A,0x82,0x00,0x02,0x92,0x4F,0x00,0x7A,0x82,0x00,0x02,0x92,0x4C,0x00,0x7A,
0x82,0x00,0x02,0x92,0x4C,0x00,0x7A,0x82,0x00,0x02,0x92,0x4C,0x00,0x7A,0x82,0x00,
0x02,0x92,0x4C,0x00,0x7A,0x82,0x00,0x02,0x92,0x4F,0x00,0x7A,0x82,0x00,0x02,0x92,
0x4F,0x00,0x7A,0x80,0x82,0x81,0x00,0x02,0x90,0x42,0x92,0x4F,0x91,0x5A,0x00,0x7A,
0x82,0x00,0x02,0x92,0x4F,0x00,0x7A,0x82,0x00,0x02,0x92,0x4C,0x00,0x7A,0x82,0x00,
0x02,0x92,0x4C,0x00,0x7A,0x82,0x00,0x02,0x92,0x4C,0x00,0x7A,0x82,0x00,0x02,0x92,
0x4C,0x00,0x7A,0x82,0x00,0x02,0x92,0x4F,0x00,0x7A,0x82,0x00,0x02,0x92,0x4F,0x00,
0x7A,0x80,0x82,0x81,0x00,0x02,0x90,0x3E,0x92,0x4F,0x91,0x58,0x00,0x7A,0x82,0x80,
0x00,0x02,0x92,0x4F,0x90,0x43,0x00,0x7A,0x82,0x80,0x00,0x02,0x90,0x40,0x92,0x4C,
0x00,0x7A,0x82,0x80,0x00,0x02,0x92,0x4C,0x90,0x40,0x00,0x7A,0x82,0x80,0x00,0x02,
0x92,0x4C,0x90,0x40,0x00,0x7A,0x82,0x80,0x00,0x02,0x92,0x4C,0x90,0x40,0x00,0x7A,
0x82,0x80,0x00,0x02,0x92,0x4F,0x90,0x43,0x00,0x7A,0x82,0x80,0x00,0x02,0x92,0x4F,
0x90,0x43,0x00,0x7A,0x82,0x80,0x00,0x02,0x90,0x40,0x92,0x4F,0x00,0x7A,0x82,0x80,
0x00,0x02,0x92,0x4F,0x90,0x43,0x00,0x7A,0x82,0x80,0x00,0x02,0x90,0x42,0x92,0x4C,
0x00,0x7A,0x82,0x80,0x00,0x02,0x92,0x4C,0x90,0x40,0x00,0x7A,0x82,0x80,0x00,0x02,
0x92,0x4C,0x90,0x40,0x00,0x7A,0x82,0x80,0x00,0x02,0x92,0x4C,0x90,0x40,0x00,0x7A,
0x82,0x80,0x00,0x02,0x92,0x4F,0x90,0x43,0x00,0x7A,0x82,0x80,0x00,0x02,0x92,0x4F,
0x90,0x43,0x00,0x7A,0x82,0x81,0x80,0x00,0x02,0x90,0x3E,0x92,0x4A,0x91,0x56,0x00,
0x7A,0x82,0x81,0x80,0x00,0x7F,0x92,0x4C,0x91,0x58,0x90,0x4C,0x00,0x7A,0x82,0x81,
0x80,0x02,0x73,0x92,0x4C,0x91,0x58,0x90,0x4C,0x00,0x7A,0x82,0x81,0x80,0x00,0x7F,
0x92,0x4E,0x91,0x5A,0x90,0x4E,0x00,0x7A,0x82,0x81,0x80,0x02,0x73,0x92,0x4C,0x91,
0x58,0x90,0x4C,0x0F,0x9D,0x82,0x81,0x80,0xF0};

Playtune p;

void setup() {
  p.tune_initchan(10);
  p.tune_initchan(11);
  p.tune_initchan(12);
}

void loop() {
  p.tune_playscore(avengers); // start
  while (p.tune_playing);     // wait while playing
  p.tune_delay(3000);         // do not use Arduino delay
}




If you need to restore the ESP8266 firmware, follow the instructions below:

1) Connect the FTDI adapter, an external power source, and set the jumpers as below:


2) Upload an "empty" Arduino sketch to be sure that all the ports are not in use. Note the FTDI COM port in the IDE bottom right corner:

3) Close the Arduino IDE

4) Download the ESP flasher software:
    https://drive.google.com/file/d/0B7n8Axhg7tChaHlLSENjWE5XRGM/view


5) Unzip files in a folder (e.g. C:\ESP8266)

6) Run "esp8266_flasher.exe" program

7) Set the FTDI COM port, as shown in step 2:


8) Click the "Bin" button and select the file "ai-thinker-0.9.5.2-9600.bin":


9) Click the "Download" button:


10) Wait while the firmware is transferred to the ESP-01:



11) Close the window when download is done:


12) You can test the firmware typing "AT" in the serial monitor of Arduino IDE:


   If "OK" appears, the AT command firmware is properly installed:


13) To check the firmware version, type "AT+GMR" and click "Send" button:


14) Set jumpers to the default position:




Para demonstrar a utilização da placa standalone MBZ Pro Mega, vamos realizar uma das montagens clássicas com Arduino: um relógio digital.
Mas para incrementar um pouco, vamos fazer esse relógio FALAR as horas.




Para tornar a montagem mais fácil, não será necessário nenhum módulo especial para reproduzir o áudio, para tanto vamos utilizar a biblioteca TMRpcm, que toca arquivos PCM/WAV direto de um cartão SD, através de uma porta PWM do Arduino.


Apesar desse artigo mostrar a montagem usando uma placa MBZ Standalone, ela não é imprescindível para a conclusão da montagem. 


Cartão SD

Para ler o cartão SD pode ser utilizado um módulo padrão para Arduino. A principal vantagem desse módulo é que ele já pode trabalhar diretamente com 5V, tanto na alimentação quanto nos pinos de sinal:

Mas se você não tem um módulo SD, pode usar um simples adaptador de cartão SD com uma barra de pinos soldada nos seus terminais:


  

A alimentação do cartão SD deve ser 3.3V e os pinos de sinal devem trabalhar também com 3.3V, para isso pode-se usar um divisor de tensão com resistores (como no módulo SD) ou usar um chip CD74HC4050, que faz a conversão do nível de sinal de 5V para 3.3V.
Lembrando que a utilização desse chip torna a leitura muito mais rápida que nos módulos SD normais, que usam divisores de tensão com resistores. O único módulo SD no mercado que se compara é o da Adafruit, que também usa o chip 4050.
Como a MBZ Pro Mega tem um chip desses, vamos utilizá-lo do projeto.

Clique aqui para ver o data sheet do CD74HC4050

Faça o download dos arquivos de voz WAV e descompacte o arquivo ZIP na raiz do cartão SD. Use um cartão SD de no máximo 2GB.

Clique aqui para fazer o download do arquivo com os áudios WAV

Se você quiser criar seus próprios arquivos WAV, eles devem estar configurados com os seguintes parâmetros: WAV, mono, 16Khz, 8 bit.
Você pode gerar novos arquivos de áudio a partir de TTS (Texto to Speech) no site Voice RSS.


Amplificador

Os arquivos WAV são lidos e as informações são traduzidas em pulsos PWM na porta digital 9 do Arduino. Se você ligar um fone de ouvido já pode ouvir o arquivo sendo tocado.
Como a potência na saída não é suficiente para um alto-falante, esse áudio passa por um amplificador feito com o C.I. TDA7052.
O chip TDA7052 é um amplificador mono de 1W, bem simples de usar e fácil de ser encontrado.

Clique aqui para ver o data sheet do TDA7052

Esse amplificador poderia ser feito com outros chips como, por exemplo, o LM386, ou mesmo com um módulo de amplificador como o PAM8403.

O transistor BC548 está sendo utilizado como um interruptor para ligar a alimentação no TDA7052 apenas quando for necessário.



O Circuito

A figura abaixo mostra a montagem do circuito usando um Arduino Nano em uma breadboard:

Abaixo está o diagrama esquemático do circuito:



Usando a MBZ Pro Mega

A MBZ Standalone permite fazer a montagem do Relógio Falante em uma única placa. Ela tem uma área para montar um Arduino (que já tem header para o RTC), uma área no centro equivalente à uma mini-breadboard (onde você pode soldar os componentes do circuito) e uma área para colocar um ESP8266 para acesso WIFI. Neste projeto não será utilizado WIFI, portanto não vamos usar esse módulo, mas vamos aproveitar o chip CD74HC4050 que faz a conversão dos sinais digitais de 5V para 3.3V:


A foto acima mostra o circuito do amplificador TDA7052 soldado na placa e dois headers, um para o OLED e outro para o cartão SD.



Agora basta encaixar o RTC, o cartão SD, o display OLED, ligar uma fonte de alimentação, fazer o upload do sketch e o relógio está pronto para funcionar.




Segue abaixo o código fonte (sketch) que deve ser colocado no Arduino. Faça as modificações que quiser, para adaptá-lo às suas necessidades: 

#include <RtcDS3231.h>
#include <TMRpcm.h>
#include <Wire.h>
#include <SD.h>
#include <U8glib.h>

#define SD_ChipSelect_Pin 10
#define Amp_Enable_Pin 8
#define Tmc_Pin 9
#define btn_Pin A0

U8GLIB_SH1106_128X64 u8g(4, 5, 6, 7);  //software SPI: SCK=4, MOSI=5, CS=7, A0=7

RtcDS3231 Rtc;
RtcDateTime now;

bool dispTime = true;

TMRpcm tmrpcm;

void setup ()
{
  Serial.begin(9600);

  // inicializa o SD
  if (!SD.begin(SD_ChipSelect_Pin)) {
    Serial.println("Erro SD");
    return;
  }

  // inicializa o TmrPCM
  tmrpcm.speakerPin = Tmc_Pin;
  tmrpcm.quality(1);
  tmrpcm.setVolume(5);

  // inicializa o pino que liga o amplificador
  pinMode(Amp_Enable_Pin, OUTPUT);
  digitalWrite(Amp_Enable_Pin, LOW);

  // inicializa o RTC
  Rtc.Begin();
  RtcDateTime compiled = RtcDateTime(__DATE__, __TIME__);
  if (!Rtc.IsDateTimeValid())
  {
    Rtc.SetDateTime(compiled);
  }
  if (!Rtc.GetIsRunning()) {
    Serial.println("Inicializando o RTC");
    Rtc.SetIsRunning(true);
  }
  RtcDateTime now = Rtc.GetDateTime();
  if (now < compiled)
  {
    Rtc.SetDateTime(compiled);
  }

  // gira o display OLED
  u8g.setRot180();

  // inicializa o push-button
  pinMode(btn_Pin, INPUT);
  digitalWrite(btn_Pin, LOW);
  pinMode(13, OUTPUT);
}

void displayTime(void) {
  int hour = now.Hour();
  int minute = now.Minute();
  int day = now.Day();
  int month = now.Month();
  int year = now.Year();
  char st[10];

  sprintf(st, "%02d:%02d", hour, minute);

  u8g.setFont(u8g_font_fub35n);
  u8g.drawStr( 0, 36, st);

  sprintf(st, "%02d/%02d/%04d", day, month, year);
  u8g.setFont(u8g_font_9x15);
  u8g.drawStr( 20, 60, st);
}

void loop ()
{
  now = Rtc.GetDateTime();

  if (dispTime) {
    Serial.println(now.Second());
    // exibe a hora no oled
    u8g.firstPage();
    do {
      displayTime();
    } while ( u8g.nextPage() );
  }

  if (now.Second() == 0) {
    dispTime = true;
  } else {
    dispTime = false;
  }

  if (Rtc.IsDateTimeValid()) {
    now = Rtc.GetDateTime();
  }

  if (digitalRead(btn_Pin) == HIGH) {
    talkTime(now.Hour(), now.Minute());
    delay(500);
  }
}

void talkTime(int hour, int minute) {
  char fileHour[12];
  char fileMinute[12];

  if (hour > 12) {
    hour = hour - 12;
  }

  sprintf(fileHour, "%02dH.wav", hour);
  Serial.println(fileHour);

  turnAmpOn();
  tmrpcm.play(fileHour);
  waitPlay();

  if (minute > 0) {
    sprintf(fileMinute, "%02dM.wav", minute);
    Serial.println(fileMinute);
    tmrpcm.play(fileMinute);
    waitPlay();
  }

  turnAmpOff();
}

void waitPlay() {
  bool b = false;

  while (tmrpcm.isPlaying()) {
    delay(10);
  }
}

void turnAmpOn() {
  digitalWrite(Amp_Enable_Pin, HIGH);
}

void turnAmpOff() {
  digitalWrite(Amp_Enable_Pin, LOW);
}


Após a compilação será exibida uma mensagem de pouca memória disponível, mas o programa funcionará normalmente. Essa mensagem é devido ao uso da biblioteca U8GLIB que consome muita memória para as fontes que serão exibidas no display OLED. Também a biblioteca SD consome muita memória.

from Makezine

In 2015, my son had to make a board game for a middle school science fair, and the theme was Ancient Egypt. I just figured it out was an opportunity to introduce him to electronics. 
My initial suggestion was to replace the conventional dice with an electronic dice, but my kid and his classmates are very creative, so when their ideas began to emerge, I didn’t want to limit their ideas. Therefore, the things became a little more complex.



They did the project concept, game and enclosure design and I helped them with my electronics expertise.



The brain of this project is an Arduino Nano, that controls some other modules and circuits (display, MP3 player, buttons, and LEDs).

Just like any schoolwork, they had to do a lot of research about ancient Egyptian history, because they needed to formulate a lot of questions for the quiz that was part of the game. They also had a lot of work to drawing the map of the board, especially considering they used MS-Word to do it. It was printed on vinyl adhesive.​


For the enclosure design, they were inspired by pinball machines. They wanted to make it with a large area for the map and an inclined display, which also hides the pharaoh’s sarcophagus, and when a player reaches the goal, some LEDs are lit, revealing the "secret treasure".




The enclosure was made of plastic (polyethylene) and acrylic sheets, due to its ease of cutting and folding.




The circuit design was created with Fritzing software. I do not like to use jumpers, so usually I make double-sided PCBs. But, as the time was short, I chose to simplify making single-side PCB, using toner transfer method.



This project has 24 LEDs, so I had to use a multiplexer IC. The MAX7219 solved the LEDs problem, but caused a loud noise to come from the speakers. To fix this issue the MAX7219 was replaced by its “brother” MAX7221, which has protection against electro magnetic interference (EMI).


 

The questions of the quiz are shown in a 20×4 LCD display as well as played through the speakers with the kids’ voices, which we recorded and distorted to sound scary like a mummy.

The first programming challenge was to display portuguese accented characters. Despite the fact that the LCD display allowed 8 customizable characters, it wasn’t enough. So to work around this problem I had to dynamically load the custom characters as required.

Another programming challenge I had to overcome was the Arduino 2KB RAM limitation, since I needed lot of memory to hold the quiz strings, which can neither be solved by progmem tricky. I had to store these strings into an external 32 KB EEPROM (Microchip 24LC256). I wrote some Arduino sketches that was executed once, to store these strings into EEPROM, and after it was retrieved by the main program, using their memory addresses.

After all, I think that Arduino Nano was pushed to its limits. More than that, only using an Arduino Mega.

From conception to finish, the project took 30 days.

All information about this project (source-code, PCI design, pictures, etc) are available in GitHub:
https://github.com/marcelomaximiano/BoardGame

Playing game video: https://www.youtube.com/watch?v=Zrz66H8dYvI
Testing session video: https://www.youtube.com/watch?v=dJfBCsZCKEY

Source Code:

#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include "Bounce2.h"

// ***** LCD
#define BACKLIGHT_PIN 5
LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);  // Set the LCD I2C address
#include "LedControlMS.h"
#define DataIn_Pin 4
#define Clock_Pin  7
#define Load_Pin   8

// ***** Leds
LedControl lc = LedControl(DataIn_Pin, Clock_Pin, Load_Pin, 1);
#define ledButtonA    17
#define ledButtonB    18
#define ledButtonC    19
#define ledWhite1     20
#define ledWhite2     21
#define ledSnakeRed   22
#define ledSnakeGreen 23

// ***** Buttons
#define buttonRot  A0
#define buttonA    A1
#define buttonB    A2
#define buttonC    A3
#define snakeWire  9
#define snakeStart 10
#define snakeEnd   11
Bounce debouncerRot = Bounce();
Bounce debouncerA   = Bounce();
Bounce debouncerB   = Bounce();
Bounce debouncerC   = Bounce();
Bounce debouncerSW  = Bounce();
Bounce debouncerS1  = Bounce();
Bounce debouncerS2  = Bounce();

// buttons events
#define evtBtnRot    0
#define evtBtnA      1
#define evtBtnB      2
#define evtBtnC      3
#define evtSW        4
#define evtS1Press   5
#define evtS1Release 6
#define evtS2Press   7
#define evtS2Release 8

// ***** Rotary Encoder
int Rot_Pin1 = 12;
int Rot_Pin2 = 2;
int encoderValue = 1;
int lastEncoded = LOW;

// ***** Sound Volume
int soundVolume = 20;  // max 31

// ***** Sound Track
#define soundTrackStart 52
#define soundTrackEnd   58
int soundTrack = soundTrackStart;


// ***** Acentos para mensagens gerais
char strAccents[11] = "          ";

void setup()
{
  Serial.begin(9600);
  Wire.begin();

  uint32_t seed = millis();
  randomSeed(seed);

  lc.shutdown(0, false);
  /* Set the brightness to a medium values */
  lc.setIntensity(0, 12);
  /* and clear the display */
  lc.clearDisplay(0);

  // initialize Rotary Encoder
  pinMode (Rot_Pin1, INPUT);
  pinMode (Rot_Pin2, INPUT);

  // inicializa os botoes
  initButton(buttonRot);
  initButton(buttonA);
  initButton(buttonB);
  initButton(buttonC);
  initButton(snakeWire);
  initButton(snakeStart);
  initButton(snakeEnd);

  // toca o som de introduçao
  getSoundVolume();
  soundPlay(48);
  
  // Switch on the backlight
  pinMode (BACKLIGHT_PIN, OUTPUT);
  digitalWrite(BACKLIGHT_PIN, HIGH);
  lcd.begin(20, 4);              // initialize the lcd
  lcd.clear();
  lcd.setCursor(0, 1);
  displayRomStr(10020);     // O Segredo
  loadChars(strAccents);
  lcd.setCursor(0, 2);
  displayRomStr(10040);     // da Piramide
  delay(3000);
}

// Timers
unsigned long timer1 = 0;
unsigned long timer1_interval = 1000;
unsigned long timer2 = 0;
unsigned long timer2_interval = 300;
unsigned long timer3 = 0;
unsigned long timer3_interval = 20000;
unsigned long timer4 = 0;
unsigned long timer4_interval = 30000;

// etapas do jogo
#define HowManyPlayers 1
#define Begin          2
#define Roulette       3
#define GameOver       4

bool gameOver = false;

int NumberPlayers = 2;
int buttonPressed = -1;

char* players[]   = { "vermelho", "azul", "marrom", "amarelo", "branco" };
int rouletteVal[] = { 5, 4, 0, 3, 2, 1, 3, 6, 1, 3, 2, 4, -1, 0, 6, 1 };
int playersPos[]  = { 0, 0, 0, 0, 0 };
int playersPenalty[]  = { 0, 0, 0, 0, 0 };

int currentPlayer = 0;
int roulettePos = 1;

// niveis de dificuldade das perguntas
#define questionLevel01 1
#define questionLevel02 1
#define questionLevel03 1

void loop()
{
  unsigned long currentMillis = millis();

  // toca o som Numero de Jogadores
  soundPlay(49);
  
  // numero de jogadores
  ligaLed(ledButtonA, true);
  ligaLed(ledButtonB, true);
  ligaLed(ledButtonC, true);

  digitalWrite ( BACKLIGHT_PIN, HIGH );
  loadChars(strAccents);
  lcd.clear();
  lcd.setCursor(0, 0);
  displayRomStr(10100);     // Numero de jogadores
  lcd.setCursor(0, 1);
  char str[10];
  sprintf(str, "%1d", NumberPlayers);
  lcd.write(str);
  lcd.setCursor(9, 1);
  lcd.write("A) +");
  lcd.setCursor(9, 2);
  lcd.write("B) -");
  lcd.setCursor(9, 3);
  lcd.write("C) confirma");
  buttonPressed = -1;

  while (true)
  {
    buttonPressed = getButton();

    if (buttonPressed == evtBtnA) {
      NumberPlayers++;
      if (NumberPlayers > 5) {
        NumberPlayers = 5;
      }
    }
    if (buttonPressed == evtBtnB) {
      NumberPlayers--;
      if (NumberPlayers < 2) {
        NumberPlayers = 2;
      }
    }

    if (buttonPressed != -1) {
      lcd.setCursor(0, 1);
      char str[10];
      sprintf(str, "%1d", NumberPlayers);
      lcd.write(str);
    }

    if (buttonPressed == evtBtnC) {
      uint32_t seed = millis();
      randomSeed(seed);
      lcd.clear();
      ligaLed(ledButtonA, false);
      ligaLed(ledButtonB, false);
      ligaLed(ledButtonC, false);
      displayPlayers();
      waitConfirm(3000);
      displayPressToBegin();
      game();
      break;
    }
    buttonPressed = -1;
  }
}

void displayPressToBegin() {
  digitalWrite(BACKLIGHT_PIN, HIGH);

  ligaLed(ledButtonA, false);
  ligaLed(ledButtonB, false);
  ligaLed(ledButtonC, false);

  // toca o som Pressione C para iniciar
  soundPlay(50);

  lcd.clear();
  lcd.setCursor(0, 0);
  displayRomStr(10060);     // Pressione C para
  lcd.setCursor(0, 1);
  displayRomStr(10080);     // iniciar a partida

  waitConfirm(0);
}

void game() {
  int rotV = lastEncoded;

  // reinicializa a posição dos jogadores
  for (int i = 0; i <= 4; i++) {
    playersPos[i] = 0;
    playersPenalty[i]  = 0;
  }
  currentPlayer = 0;
  gameOver = false;

  // toca a musica de abertura
  soundTrack = soundTrackStart;
  soundPlay(soundTrack);

  while (true)
  {
    turnOffAllLeds();
    ligaLed(ledButtonA, false);
    ligaLed(ledButtonB, false);
    ligaLed(ledButtonC, true);

    digitalWrite(BACKLIGHT_PIN, HIGH);
    lcd.clear();
    displayPlayer();
    lcd.setCursor(0, 1);
    lcd.write("Gire a roleta!");
    ligaLed(roulettePos, true);

    while (true)
    {
      // **********  Rotary Encoder
      readRotaryEncoder();
      if (rotV != lastEncoded) {
        ringSeq(encoderValue);
      }

      buttonPressed = getButton();

      if ((buttonPressed == evtBtnRot) || (buttonPressed == evtBtnC)) {
        loadChars(strAccents);
        lcd.clear();
        ligaLed(ledButtonA, false);
        ligaLed(ledButtonB, false);
        ligaLed(ledButtonC, false);
        spinRoulette();

        lcd.clear();
        lcd.setCursor(0, 0);
        displayPlayer();

        if (roulettePos < 1) roulettePos = 1;
        if (roulettePos > 16) roulettePos = 16;
        
        int rv = rouletteVal[roulettePos - 1];

        if (rv == -1) {
          playersPos[currentPlayer] = 0;
          lcd.setCursor(0, 1);
          displayRomStr(10180);     // Volte ao inicio!
        }
        else if (rv == 0) {
          lcd.setCursor(0, 1);
          displayRomStr(10200);     // Fique na mesma casa!
        }
        else {
          playersPos[currentPlayer] = playersPos[currentPlayer] + rv;
          if (playersPos[currentPlayer] > 54) {
            playersPos[currentPlayer] = 55;
          }

          lcd.setCursor(0, 1);
          displayRomStr(10220);     // Avance X casa
          char str[10];
          sprintf(str, "%1d", rv);
          lcd.setCursor(7, 1);
          lcd.write(str);
          if (rv > 1) {
            lcd.setCursor(13, 1);
            lcd.write("s");
          }

          waitConfirm(2000);

          switch (playersPos[currentPlayer]) {
            case 5: case 16: case 22: case 31: case 42: case 47: case 52:
              displayGetCard();
              break;
            case 8: case 14:
              snake(50);
              break;
            case 27: case 37:
              snake(40);
              break;
            case 44: case 50:
              snake(30);
              break;
            case 20:
              delay(2000);
              playersPos[currentPlayer] = 12;
              lcd.setCursor(0, 1);
              displayRomStr(10240);     // Volte para a casa 12
              break;
            case 25:
              delay(2000);
              playersPos[currentPlayer] = 33;
              lcd.setCursor(0, 1);
              displayRomStr(10260);     // Avance p/ a casa 33
              break;
          }

          if (playersPos[currentPlayer] == 55) {
            delay(2000);
            // toca a musica final
            soundPlay(64);
            lcd.clear();
            lcd.setCursor(0, 0);
            displayRomStr(10280);     // Parabens!!!
            lcd.setCursor(0, 1);
            displayPlayer();
            lcd.setCursor(0, 2);
            displayRomStr(10300);     // Venceu o jogo !!!
            ligaLed(ledWhite1, true);
            ligaLed(ledWhite2, true);
            gameOver = true;
            delay(5000);
            displayGameOver();
            break;
          }
        }
        waitConfirm(3000);
        displayPos();
        waitConfirm(3000);

        currentPlayer++;
        if (currentPlayer > NumberPlayers - 1) {
          currentPlayer = 0;
        }
        break;
      }
    }
    if (gameOver) {
      break;
    }
  }
  buttonPressed = -1;
}

void snake(int time) {
  loadChars(strAccents);

  // toca o som de introduçao
  soundPlay(60);
  
  turnOffAllLeds();
  ligaLed(ledSnakeGreen, true);

  lcd.clear();
  lcd.setCursor(0, 1);
  displayRomStr(10320);     // Desafio da Cobra
  delay(2000);

  lcd.clear();
  lcd.setCursor(0, 0);
  displayRomStr(10660);     // O objetivo é levar
  lcd.setCursor(0, 1);
  displayRomStr(10680);     // a argola até a base
  lcd.setCursor(0, 2);
  displayRomStr(10700);     // vermelha, sem encostar
  lcd.setCursor(0, 3);
  displayRomStr(10720);     // na cobra metálica.
  waitConfirm(5000);

  lcd.clear();
  displayPlayer();
  lcd.write(",");
  lcd.setCursor(0, 1);
  displayRomStr(10340);     // voce tem XX segundos
  lcd.setCursor(9, 1);
  lcd.print(time, DEC);
  lcd.setCursor(0, 2);
  displayRomStr(10360);     // para atravessar o
  lcd.setCursor(0, 3);
  displayRomStr(10380);     // Vale das Cobras !!!
  waitConfirm(5000);

  lcd.clear();
  lcd.setCursor(0, 0);
  displayRomStr(10400);     // Para começar
  lcd.setCursor(0, 1);
  displayRomStr(10420);     // encoste a argola na
  lcd.setCursor(0, 2);
  displayRomStr(10440);     // parte metalica da
  lcd.setCursor(0, 3);
  displayRomStr(10460);     // base preta.
  buttonPressed = -1;

  while (true) {
    buttonPressed = getButton();
    if (buttonPressed == evtS1Press) {
      break;
    }
  }

  lcd.clear();
  lcd.setCursor(0, 0);
  displayRomStr(10740);     // Pode começar !!!
  buttonPressed = -1;

  while (true) {
    buttonPressed = getButton();
    if (buttonPressed == evtS1Release) {
      break;
    }
  }

  // toca a trilha da cobra
  soundPlay(61);

  lcd.clear();
  lcd.setCursor(0, 0);
  displayRomStr(10760);     // Não encoste
  lcd.setCursor(0, 1);
  displayRomStr(10780);     // na cobra!

  unsigned long currentMillis = millis();
  timer2 = currentMillis;
  timer2_interval = 1000;
  buttonPressed = -1;

  while (true) {
    currentMillis = millis();
    buttonPressed = getButton();

    if (buttonPressed == evtS2Press) {
      // toca a trilha da cobra exito
      soundPlay(62);
      playersPos[currentPlayer] = playersPos[currentPlayer] + 1;
      lcd.clear();
      lcd.setCursor(0, 0);
      displayRomStr(10280);     // Parabéns !!!
      lcd.setCursor(0, 1);
      displayRomStr(10800);     // Você conseguiu !!!
      lcd.setCursor(0, 3);
      displayRomStr(10600);     // Avance uma casa
      lcd.write("Avance 1 casa.");
      break;
    }

    if ((buttonPressed == evtSW) || (time < 0)) {
      // toca o som da mordida da cobra
      soundPlay(63);
      playersPos[currentPlayer] = playersPos[currentPlayer] - 1;
      ligaLed(ledSnakeRed, true);
      ligaLed(ledSnakeGreen, false);
      lcd.clear();
      lcd.setCursor(0, 0);
      displayRomStr(10820);     // A cobra mordeu você!
      lcd.setCursor(0, 2);
      displayRomStr(10640);     // Retorne uma casa
      break;
    }

    if (currentMillis - timer2 > timer2_interval) {
      lcd.setCursor(0, 3);
      lcd.write("Tempo : ");
      lcd.print(time, DEC);
      lcd.write(" ");
      timer2 = currentMillis;
      time--;
    }
  }

  waitConfirm(3000);
  turnOffAllLeds();

  // toca a proxima musica
  soundTrack++;
  if (soundTrack > soundTrackEnd) {
    soundTrack = soundTrackStart;
  }
  soundPlay(soundTrack);
}

void ligaLed(int i, bool flag) {
  int dig = 0;
  int ld = 0;

  if ((i >= 1) && (i <= 8)) {
    dig = 0;
    if (i == 6) {
      i = 7;
    }
    else if (i == 7) {
      i = 6;
    }
    if (i == 8) {
      ld = 0;
    }
    else {
      ld = i;
    }
  }
  else if ((i >= 9) && (i <= 16)) {
    dig = 1;
    i = i - 8;
    if (i == 8) {
      ld = 0;
    }
    else {
      ld = i;
    }
  }
  else if ((i >= 17) && (i <= 24)) {
    dig = 3;
    i = i - 16;
    if (i == 8) {
      ld = 0;
    }
    else {
      ld = i;
    }
  }

  lc.setLed(0, dig, ld, flag);
}

void clearRing() {
  for (int r = 4; r <= 7; r++) {
    lc.setRow(0, r, B00000000);
  }
}

void ringSeq(int n) {
  clearRing();
  for (int c = 1; c <= n; c++) {
    ring(c);
  }
}

void ring(int n) {
  int dig = 0;
  int ld = 0;

  switch (n) {
    case 0:
      dig = 7;
      ld = 3;
      break;
    case 1 ... 4:
      dig = 4;
      ld = n - 1;
      break;
    case 5 ... 8:
      dig = 5;
      ld = n - 5;
      break;
    case 9 ... 12:
      dig = 6;
      ld = n - 9;
      break;
    case 13 ... 15:
      dig = 7;
      ld = n - 13;
      break;
    default:
      break;
  }
  lc.setLed(0, dig, ld, true);
}

void readRotaryEncoder() {
  int n = LOW;
  n = digitalRead(Rot_Pin1);
  if ((lastEncoded == LOW) && (n == HIGH)) {
    if (digitalRead(Rot_Pin2) == LOW) {
      encoderValue--;
    }
    else {
      encoderValue++;
    }
    if (encoderValue > 15) encoderValue = 15;
    if (encoderValue < 1) encoderValue = 1;
  }
  lastEncoded = n;
}

void initButton(int pinButton) {
  pinMode(pinButton, INPUT);
  digitalWrite(pinButton, HIGH);
  pinMode(pinButton, INPUT_PULLUP);

  switch (pinButton) {
    case buttonRot:
      debouncerRot.attach(pinButton);
      debouncerRot.interval(5); // interval in ms
      break;
    case buttonA:
      debouncerA.attach(pinButton);
      debouncerA.interval(5); // interval in ms
      break;
    case buttonB:
      debouncerB.attach(pinButton);
      debouncerB.interval(5); // interval in ms
      break;
    case buttonC:
      debouncerC.attach(pinButton);
      debouncerC.interval(5); // interval in ms
      break;
    case snakeWire:
      debouncerSW.attach(pinButton);
      debouncerSW.interval(5); // interval in ms
      break;
    case snakeStart:
      debouncerS1.attach(pinButton);
      debouncerS1.interval(5); // interval in ms
      break;
    case snakeEnd:
      debouncerS2.attach(pinButton);
      debouncerS2.interval(5); // interval in ms
      break;
    default:
      break;
  }
}

// verifica qual botão foi acionado
int getButton() {
  // evento do botao Rotary Encoder
  debouncerRot.update();
  static int lastBtnRotVal = -1;
  int BtnRotVal = debouncerRot.read();
  if (BtnRotVal != lastBtnRotVal) {
    lastBtnRotVal = BtnRotVal;
    if ( BtnRotVal == LOW ) {
      // evento Release
    }
    else {
      // evento Press
      return evtBtnRot;
    }
  }
  // evento do botao A
  debouncerA.update();
  static int lastBtnAVal = -1;
  int BtnAVal = debouncerA.read();
  if (BtnAVal != lastBtnAVal) {
    lastBtnAVal = BtnAVal;
    if ( BtnAVal == LOW ) {
      // evento Release
    }
    else {
      // evento Press
      return evtBtnA;
    }
  }

  // evento do botao B
  debouncerB.update();
  static int lastBtnBVal = -1;
  int BtnBVal = debouncerB.read();
  if (BtnBVal != lastBtnBVal) {
    lastBtnBVal = BtnBVal;
    if ( BtnBVal == LOW ) {
      // evento Release
    }
    else {
      // evento Press
      return evtBtnB;
    }
  }

  // evento do botao C
  debouncerC.update();
  static int lastBtnCVal = -1;
  int BtnCVal = debouncerC.read();
  if (BtnCVal != lastBtnCVal) {
    lastBtnCVal = BtnCVal;
    if ( BtnCVal == LOW ) {
      // evento Release
    }
    else {
      // evento Press
      return evtBtnC;
    }
  }

  // evento encostar a argola no arame
  debouncerSW.update();
  static int lastBtnSWVal = -1;
  int BtnSWVal = debouncerSW.read();
  if (BtnSWVal != lastBtnSWVal) {
    lastBtnSWVal = BtnSWVal;
    if ( BtnSWVal == HIGH ) {
      // evento Release
    }
    else {
      // evento Press
      return evtSW;
    }
  }

  // evento encostar a argola no ponto de inicio
  debouncerS1.update();
  static int lastBtnS1Val = -1;
  int BtnS1Val = debouncerS1.read();
  if (BtnS1Val != lastBtnS1Val) {
    lastBtnS1Val = BtnS1Val;
    if ( BtnS1Val == HIGH ) {
      // evento Release
      return evtS1Release;
    }
    else {
      // evento Press
      return evtS1Press;
    }
  }

  // evento encostar a argola no ponto final
  debouncerS2.update();
  static int lastBtnS2Val = -1;
  int BtnS2Val = debouncerS2.read();
  if (BtnS2Val != lastBtnS2Val) {
    lastBtnS2Val = BtnS2Val;
    if ( BtnS2Val == HIGH ) {
      // evento Release
      return evtS2Release;
    }
    else {
      // evento Press
      return evtS2Press;
    }
  }

  getSoundVolume();

  return -1;
}

// mostra os jogados ativos no jogo e a sequencia deles
void displayPlayers() {
  digitalWrite(BACKLIGHT_PIN, HIGH);
  lcd.clear();
  lcd.setCursor(0, 0);
  displayRomStr(10120);     // Ordem dos jogadores
  lcd.setCursor(0, 1);
  lcd.write("1-");
  lcd.write(players[0]);
  lcd.setCursor(11, 1);
  lcd.write("2-");
  lcd.write(players[1]);
  if (NumberPlayers > 2) {
    lcd.setCursor(0, 2);
    lcd.write("3-");
    lcd.write(players[2]);
  }
  if (NumberPlayers > 3) {
    lcd.setCursor(11, 2);
    lcd.write("4-");
    lcd.write(players[3]);
  }
  if (NumberPlayers > 4) {
    lcd.setCursor(0, 3);
    lcd.write("5-");
    lcd.write(players[4]);
  }
}

// apaga todos os leds
void turnOffAllLeds() {
  lc.shutdown(0, false);
  lc.clearDisplay(0);
}

// faz os leds da roleta acenderem sequencialmente
// com velocidade indicada pelo valor do rotary encoder.
void spinRoulette() {
  int randNumber = random(20);

  digitalWrite(BACKLIGHT_PIN, LOW);

  int turns = randNumber + (encoderValue * 2);
  int i = 0;
  int speed = 180 - (encoderValue * 10);
  if (speed < 10) speed = 10;

  while (i <= turns) {
    ligaLed(roulettePos, true);
    delay(speed);
    speed = speed + 1;
    ligaLed(roulettePos, false);
    roulettePos++;
    if (roulettePos > 16) {
      roulettePos = 1;
    }
    i++;
  }

  int rv = rouletteVal[roulettePos - 1];

  // evita que o jogador atual sofra a penalidade
  // de voltar ao início mais de uma vez
  if (rv == -1) {
    if (playersPenalty[currentPlayer] > 0) {
      roulettePos++;
      rv = rouletteVal[roulettePos - 1];
    }
    playersPenalty[currentPlayer]++;
  }

  ligaLed(roulettePos, true);
  digitalWrite(BACKLIGHT_PIN, HIGH);
}

// mostra o placar geral, com a posição
// no tabuleiro de cada jogador
void displayPos() {
  digitalWrite(BACKLIGHT_PIN, HIGH);
  lcd.clear();
  lcd.setCursor(0, 0);
  displayRomStr(10140);     // Placar geral:

  lcd.setCursor(0, 1);
  lcd.write(players[0]);
  char str[10];
  sprintf(str, " %2d", playersPos[0]);
  lcd.setCursor(10, 1);
  lcd.write(str);

  if (NumberPlayers > 1) {
    lcd.setCursor(0, 2);
    lcd.write(players[1]);
    char str[10];
    sprintf(str, " %2d", playersPos[1]);
    lcd.setCursor(10, 2);
    lcd.write(str);
  }
  if (NumberPlayers > 2) {
    lcd.setCursor(0, 3);
    lcd.write(players[2]);
    char str[10];
    sprintf(str, " %2d", playersPos[2]);
    lcd.setCursor(10, 3);
    lcd.write(str);
  }

  if (NumberPlayers > 3) {
    waitConfirm(3000);
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.write(players[3]);
    char str[10];
    sprintf(str, " %2d", playersPos[3]);
    lcd.setCursor(10, 0);
    lcd.write(str);
  }

  if (NumberPlayers > 4) {
    lcd.setCursor(0, 1);
    lcd.write(players[4]);
    char str[10];
    sprintf(str, " %2d", playersPos[4]);
    lcd.setCursor(10, 1);
    lcd.write(str);
  }

  buttonPressed = -1;
  waitConfirm(3000);
}

// mostra a mensagem de fim de jogo
void displayGameOver() {
  digitalWrite(BACKLIGHT_PIN, HIGH);
  lcd.clear();
  lcd.setCursor(0, 1);
  displayRomStr(10160);     // game over
  waitConfirm(0);
}


// exibe uma questao aleatoria, lendo da memoria EEPROM ------------------
#define eepromChip 0x50    //romAddress of 24LC256 eeprom chip
#define recCount 47
#define recordLength 160
#define fieldLength   20
// questions in EEPROM
#define questLevel01_begin  0
#define questLevel01_end   13
#define questLevel02_begin 14
#define questLevel02_end   32
#define questLevel03_begin 33
#define questLevel03_end   46

// exibe a mensagem para o jogador retirar uma carta
// e indicar, através dos botões, a cor dela.
void displayGetCard() {
  loadChars(strAccents);
  digitalWrite(BACKLIGHT_PIN, HIGH);
  lcd.clear();
  displayPlayer();
  lcd.setCursor(0, 1);
  displayRomStr(10480);     // deve pegar uma carta
  lcd.setCursor(0, 2);
  displayRomStr(10500);     // e pressionar o botao
  lcd.setCursor(0, 3);
  displayRomStr(10520);     // da mesma cor.

  // espera a resposta
  ligaLed(ledButtonA, true);
  ligaLed(ledButtonB, true);
  ligaLed(ledButtonC, true);

  buttonPressed = -1;
  int level = 0;

  while (true)
  {
    buttonPressed = getButton();

    if (buttonPressed == 1) {  // botao vermelho = pergunta dificil
      level = 3;
      buttonPressed = -1;
      break;
    }
    if (buttonPressed == 2) {  // botao azul = pergunta media
      level = 2;
      buttonPressed = -1;
      break;
    }
    if (buttonPressed == 3) {  // botao verde = pergunta facil
      level = 1;
      buttonPressed = -1;
      break;
    }
    buttonPressed = -1;
  }

  lcd.clear();
  lcd.write("O ");
  displayPlayer();
  lcd.setCursor(0, 1);
  displayRomStr(10540);     // deve responder a
  lcd.setCursor(0, 2);
  displayRomStr(10560);     // pergunta a seguir:

  displayQuestion(level);
}

void displayPlayer() {
  lcd.write("Jogador ");
  lcd.write(players[currentPlayer]);
}

// exibe uma pergunta aleatória, de acordo com o nível
// de dificuldade indicado na variável level.
// Lê a pergunta da memória EEPROM e exibe no display
// lcd utilizando caracteres acentuados, que são
// carregados dinamicamente de acordo com os acentos
// que serão necessários para a pergunta a ser exibida.
void displayQuestion(int level) {
  static int questionNumber = -1;
  int rnd = -1;
  int tries = 0;
  buttonPressed = -1;


  digitalWrite(BACKLIGHT_PIN, HIGH);

  // Sorteia uma questão dentro do range
  // de dificuldade indicada no parâmetro level.
  // Se coincindir com o número sorteado na
  // rodada anterior, tenta sortear novamente.
  do
  {
    switch (level) {
      case 1:
        rnd = random(questLevel01_begin, questLevel01_end);
        break;
      case 2:
        rnd = random(questLevel02_begin, questLevel02_end);
        break;
      case 3:
        rnd = random(questLevel03_begin, questLevel03_end);
        break;
    }
    if (rnd != questionNumber) {
      break;
    }
    tries++;
  } while (tries < 3);

  questionNumber = rnd;

  unsigned char rdata[21] = "ABCDEFGHIJ1234567890";
  char xdata[21] = "ABCDEFGHIJ1234567890";
  unsigned int romAddr = questionNumber * recordLength;;
  char mp3[5] = "????";
  int mp3Ind = 0;
  char aDelay[3] = "00";
  int  questDelay = 0;
  char accents[11] = "          ";
  char correct = 'X';

  // le o header do registro no rom -------
  readEEPROM(eepromChip, romAddr, rdata, fieldLength);
  strncpy(xdata, (char*)rdata, fieldLength);

  // le o nome do arquivo mp3 da questao
  mp3[0] = xdata[0];
  mp3[1] = xdata[1];
  mp3[2] = xdata[2];
  mp3[3] = xdata[3];
  mp3Ind = atoi(mp3);

  // le o tempo de espera entre a questao e as alternativas
  aDelay[0] = xdata[5];
  aDelay[1] = xdata[6];
  questDelay = atoi(aDelay) * 1000;

  // le os caracteres especiais da pergunta
  for (int i = 0; i < 10; i++) {
    accents[i] = xdata[i + 8];
  }
  loadChars(accents);

  // le da rom as quatro linhas da questao
  lcd.clear();
  romAddr = romAddr + fieldLength;
  for (int i = 0; i < 4; i++) {
    readEEPROM(eepromChip, romAddr, rdata, fieldLength);
    strncpy(xdata, (char*)rdata, fieldLength);
    if (xdata[0] != ' ') {
      lcd.setCursor(0, i);
      lcdDisplay(xdata, accents);
    }
    romAddr = romAddr + fieldLength;
  }

  soundStop();
  delay(1000);
  soundPlay(mp3Ind);
  waitConfirm(questDelay);

  // le as tres alternativas
  lcd.clear();

  for (int i = 0; i < 3; i++) {
    readEEPROM(eepromChip, romAddr, rdata, fieldLength);
    strncpy(xdata, (char*)rdata, fieldLength);
    // procura por * no final da string, para saber se é a alternativa correta
    if (xdata[19] == '*') {
      correct = char(65 + i);
    }
    xdata[18] = 0;   // trunca a string em 18 caracteres
    lcd.setCursor(0, i);
    lcd.write(char(65 + i));
    lcd.write(")");
    lcdDisplay(xdata, accents);
    romAddr = romAddr + fieldLength;
  }

  // espera a resposta
  ligaLed(ledButtonA, true);
  ligaLed(ledButtonB, true);
  ligaLed(ledButtonC, true);

  buttonPressed = -1;
  bool ra = false;

  while (true)
  {
    buttonPressed = getButton();

    if (buttonPressed == 1) {
      if (correct == 'A') ra = true;
      buttonPressed = -1;
      break;
    }
    if (buttonPressed == 2) {
      if (correct == 'B') ra = true;
      buttonPressed = -1;
      break;
    }
    if (buttonPressed == 3) {
      if (correct == 'C') ra = true;
      buttonPressed = -1;
      break;
    }
    buttonPressed = -1;
  }

  lcd.clear();

  if (ra == true) {
    lcd.setCursor(0, 0);
    displayRomStr(10580);     // Resposta correta !!!
    lcd.setCursor(0, 1);
    displayRomStr(10600);     // Avance 1 casa
    playersPos[currentPlayer] = playersPos[currentPlayer] + 1;
    if (playersPos[currentPlayer] > 54) {
      playersPos[currentPlayer] = 55;
    }
  } else {
    lcd.setCursor(0, 0);
    displayRomStr(10620);     // Resposta errada !!!
    lcd.setCursor(0, 1);
    displayRomStr(10640);     // Retorne 1 casa
    playersPos[currentPlayer] = playersPos[currentPlayer] - 1;
  }

  waitConfirm(2000);

  // toca a proxima musica
  soundTrack++;
  if (soundTrack > soundTrackEnd) {
    soundTrack = soundTrackStart;
  }
  soundPlay(soundTrack);  
}

// espera pelo pressionamento do botão C, ou pelo tempo estipulado na variável timeWait,
// se timeWait for igual a zero, espera indefinidamente
void waitConfirm(int timeWait) {
  unsigned long currentMillis = millis();

  ligaLed(ledButtonA, false);
  ligaLed(ledButtonB, false);
  ligaLed(ledButtonC, true);

  timer1 = currentMillis;
  buttonPressed = -1;

  while (true)
  {
    currentMillis = millis();
    if (timeWait > 0) {
      if (currentMillis - timer1 > timeWait) {
        timer1 = currentMillis;
        buttonPressed = -1;
        break;
      }
    }

    buttonPressed = getButton();

    if (buttonPressed == 3) {
      buttonPressed = -1;
      break;
    }
    buttonPressed = -1;
  }
}

// exibe texto acentuado no display
void lcdDisplay(char str[], char accents[]) {
  int p = 0;
  int i = 0;
  char s[21];

  int len1 = 0;
  while (str[len1] != 0) {
    len1++;
  };

  int len2 = 0;
  while ((accents[len2] != 0) && (accents[len2] != 32)) {
    len2++;
  };

  for (i = 0; i < len1; i++) {
    p = -1;
    int cp = 0;
    while (cp < len2) {
      if (str[i] == accents[cp]) {
        p = cp;
        break;
      }
      cp++;
    }
    if (p >= 0) {
      s[i] = char(p + 1);
    } else {
      s[i] = str[i];
    }
  }
  s[i] = 0;

  lcd.write(s);
}

// Caracteres acentuados
const uint8_t charBitmap[][12] = {
  { 0b01110, 0b00000, 0b01110, 0b00001, 0b01111, 0b10001, 0b01111, 0b00000 }, // ã
  { 0b00010, 0b00100, 0b01110, 0b00001, 0b01111, 0b10001, 0b01111, 0b00000 }, // á
  { 0b00100, 0b01010, 0b01110, 0b00001, 0b01111, 0b10001, 0b01111, 0b00000 }, // â
  { 0b00010, 0b00100, 0b01110, 0b10001, 0b11111, 0b10001, 0b10001, 0b00000 }, // Á
  { 0b00010, 0b00100, 0b01110, 0b10001, 0b11111, 0b10000, 0b01110, 0b00000 }, // é
  { 0b00100, 0b01010, 0b01110, 0b10001, 0b11111, 0b10000, 0b01110, 0b00000 }, // ê
  { 0b00010, 0b00100, 0b00000, 0b01100, 0b00100, 0b00100, 0b01110, 0b00000 }, // í
  { 0b00010, 0b00100, 0b01110, 0b00100, 0b00100, 0b00100, 0b01110, 0b00000 }, // Í
  { 0b00010, 0b00100, 0b01110, 0b10001, 0b10001, 0b10001, 0b01110, 0b00000 }, // ó
  { 0b01110, 0b00000, 0b01110, 0b10001, 0b10001, 0b10001, 0b01110, 0b00000 }, // õ
  { 0b01110, 0b10001, 0b01110, 0b10001, 0b10001, 0b10001, 0b01110, 0b00000 }, // ô
  { 0b00010, 0b00100, 0b10001, 0b10001, 0b10001, 0b10011, 0b01101, 0b00000 }, // ú
  { 0b00000, 0b00000, 0b01110, 0b10000, 0b10000, 0b10001, 0b01110, 0b11000 }  // ç
};

// carrega os caracteres especiais para a ROM do LCD
void loadChars(char str[]) {
  int len = 0;
  do {
    len++;
  } while ((str[len] != 0) && (str[len] != 32));

  for (int i = 0; i < len; i++) {
    char c = str[i];
    if (c == '@') lcd.createChar (i + 1, (uint8_t *)charBitmap[0]);
    if (c == '#') lcd.createChar (i + 1, (uint8_t *)charBitmap[1]);
    if (c == '$') lcd.createChar (i + 1, (uint8_t *)charBitmap[2]);
    if (c == '&') lcd.createChar (i + 1, (uint8_t *)charBitmap[3]);
    if (c == '*') lcd.createChar (i + 1, (uint8_t *)charBitmap[4]);
    if (c == '/') lcd.createChar (i + 1, (uint8_t *)charBitmap[5]);
    if (c == '<') lcd.createChar (i + 1, (uint8_t *)charBitmap[6]);
    if (c == '>') lcd.createChar (i + 1, (uint8_t *)charBitmap[7]);
    if (c == '_') lcd.createChar (i + 1, (uint8_t *)charBitmap[8]);
    if (c == ']') lcd.createChar (i + 1, (uint8_t *)charBitmap[9]);
    if (c == '[') lcd.createChar (i + 1, (uint8_t *)charBitmap[10]);
    if (c == '{') lcd.createChar (i + 1, (uint8_t *)charBitmap[11]);
    if (c == '}') lcd.createChar (i + 1, (uint8_t *)charBitmap[12]);
  }
}

// lê a memória EEPROM usando comunicação I2c
void readEEPROM(int deviceromAddress, unsigned int eeromAddress, unsigned char* data, unsigned int num_chars)
{
  unsigned char i = 0;
  Wire.beginTransmission(deviceromAddress);
  Wire.write((int)(eeromAddress >> 8));   // MSB
  Wire.write((int)(eeromAddress & 0xFF)); // LSB
  Wire.endTransmission();

  Wire.requestFrom(deviceromAddress, num_chars);

  while (Wire.available()) data[i++] = Wire.read();
}

void soundPlay(int soundIndex) {
  getSoundVolume();
  Serial.print("soundVolume=");
  Serial.println(soundVolume);
  if (soundVolume > 0) {
     soundSetVolume(soundVolume);
     Serial.write(0x7E);
     Serial.write(0x04);
     Serial.write(0xA0); // A0 for SD card
     Serial.write((byte)0x00);
     Serial.write(soundIndex); // track number
     Serial.write(0x7E);
     Serial.print("soundIndex=");
     Serial.println(soundIndex);
  }
}

void soundPause() {
  Serial.write(0x7E);
  Serial.write(0x02);
  Serial.write(0xA3);
  Serial.write(0x7E);
}

void soundStop() {
  Serial.write(0x7E);
  Serial.write(0x02);
  Serial.write(0xA4);
  Serial.write(0x7E);
}

void soundSetVolume(int soundVolume) {
  // 00-mute
  // 31-max volume
  Serial.write(0x7E);
  Serial.write(0x03);
  Serial.write(0xA7);
  Serial.write(soundVolume); //  volume max 31 (1F)
  Serial.write(0x7E);
}

#define volPin A6    // select the input pin for the potentiometer

void getSoundVolume() {
  static unsigned long lastTime = millis();
  if (millis() - lastTime > 1000) {
    lastTime = millis();
    int sensorValue = analogRead(volPin);
    int v = map(sensorValue, 530, 1023, 0, 30);
    if (v < 0) {
      v = 0;
    }
    if (v > 30) {
      v = 30;
    }
    if (v != soundVolume) {
      soundVolume = v;
      soundSetVolume(soundVolume);
    }
  }
}

void displayRomStr(unsigned int eepromAddress) {
  unsigned char rdata[21] = "ABCDEFGHIJ1234567890";
  char xdata[21] = "ABCDEFGHIJ1234567890";
  unsigned int romAddr = 10000;

  // se a lista de acentos estiver vazia
  // carrega da EEPROM
  if (strAccents[0] == 32) {
    readEEPROM(eepromChip, romAddr, rdata, 20);
    strncpy(strAccents, (char*)rdata, 10);
    strAccents[11] = 0;
  }

  readEEPROM(eepromChip, eepromAddress, rdata, 20);
  strncpy(xdata, (char*)rdata, 20);

  lcdDisplay(xdata, strAccents);
}