Today on the hookup I’m going to show you an Arduino compatible MP3 player board that you can use to add sound effects to your projects, but more importantly, I’m going to show you how to decipher other people’s Arduino code to use in your own projects.
As part of my Halloween decorations I had some pretty simple goals, I wanted the randomly generated lightning effect on my house LEDs to also play a thunder sound effect, and I wanted a crazy witch to cackle every time someone rang the doorbell. To do this, I bought this little Arduino compatible serial MP3 player from amazon. For 8 bucks you get an MP3 decoder that can read a microSD card, adjust volume and play specific tracks, but you get basically zero documentation on how to use it.
Normally when you get one of these boards it has an accompanying Arduino library to import and work with, but this one doesn’t. In fact, the only documentation can be found in the questions section of the amazon listing where ther is a link to some example code you can use to make it work.
Now, at this point in my electronics journey I feel pretty confident reading through someone else’s code to figure out what in the world is going on, but I remember a time not so long ago that the code may as well been written in hieroglyphics. This video aims to help you decipher the important parts of other people’s code, so you can make it your own.
I’m going to use the code for this MP3 player board because I think it’s more useful if I use code that I didn’t write as an example, this code will also be ideal because it doesn’t use any additional libraries so all of the functions are included in the main sketch. If you’d like to follow along, I included the link for this code in the video description below.
Before we even begin we need to separate code from not code. Most sketches will include sections called comments that are made by using double front slash, or front slash asterisk, these sections do not contain code and are just there to help you understand what each part of the sketch does and they never get uploaded to your microcontroller. The Arduino IDE turns them gray so it’s a bit easier to distinguish them from the coding regions. On to the actual code!
I remember when I first started reading and writing code my biggest problem was figuring out what commands a microcontroller could understand and what it couldn’t. The quick answer is that there are very few commands that an Arduino board can -natively- understand. These commands are called “built in functions” and there’s a list of them on the Arduino website. But why are there so many commands used in other people’s code that aren’t listed as built in functions? For instance you can see that in the code for this MP3 player it says sendCommand(CMD_PLAY, 0), which seems like a nice human readable command, but the microcontroller will have no idea what that code means unless we tell it.
Since this code doesn’t use any special libraries we should be able to figure out how the microcontroller knows what these extra commands mean. The first part says sendCommand, and if we look in the code we can see that there is a part that says void sendCommand(), this part of the code is called a function and is basically a series of actions that performs a specific task.
This function sends commands to the MP3 player that it can understand over a serial connection. You can see that it expects two inputs, the first one is a command, and the second one is data of some kind. Now our function makes a bit more sense, sendCommand(CMD_PLAY, 0) means it will use this function to send the command CMD_PLAY to the mp3 player, and it doesn’t need any additional data for that, so the data is equal to zero.
But wait, CMD_PLAY isn’t a built in function either, so what does that mean?
Another place where we teach the Arduino code new words is usually located at the top of the code. This area has a whole bunch of entries that say #define, and each define entry essentially works like the “find and replace” function in Microsoft word. Once you press the compile button it will replace every instance of the value CMD_PLAY with the value 0X0D, which is a hexadecimal number that the MP3 player will understand as a command, using #define statements allows us to make our code more human readable and also allows us to change every instance of a command quickly since editing this one line will change what each value is replaced with.
The final way we teach our Arduino new words is with variables. The difference between a define and a variable is bit obvious, but also a bit more complex. At the most basic level a define gets defined as a single value and never changes, where as a variable is, well, variable meaning you can assign it different values at different points in the code.
The more complex difference is that when you hit the compile button define values get replaced before they get to the microcontroller, but variables are assigned a location in memory on the chip.
Armed with this knowledge, we can try to figure out what this code actually does. Generally speaking there are two places to look.
The void setup() section contains parts of the code that need to run one time in order to make the hardware work. This is where you normally connect to wifi, initialize sensors, and set up MQTT or HTTP connections. Generally speaking, stuff that’s in the void setup of someone else’s code will need to be copied into the void setup of your code to make it work.
The second, and more important place to look to figure out what a program does is the void loop. The void loop runs hundreds, or even thousands of times a second.
You can see in this void loop it checks to see if there is any information has been sent via the serial connection and then sends that information to the MP3 player using the sendMP3Command function. So to figure out what that function does we will scroll through the code to find void sendMP3Command, and immediately we can see if I type question mark or the letter h, it will output this list of commands for me.
At this point, I know enough about the program to start testing it out. I’ll hook up my little MP3 player board to my ESP8266 node MCU using the pins that are listed in the define section of the sketch. The pins were initially listed as pins 5 and 6, but I chose to modify them based on the chart I made for my earlier video about selecting the correct pins on the NodeMCU. I decided that pin 4 and 5 would make better choices because they won’t interfere with the boot process.
I also need to grab a microSD card and format the names of my mp3 files using naming convention specified in the example code. Which is folders with two digit numbers and these names for the mp3 files.
Next I’ll plug my NodeMCU into my computer with a USB cable and select the correct COM port for that USB port on my computer. These are the standard settings that I use to upload programs to a NodeMCU. Once that’s all done just hit upload and the file will automatically save, compile, and upload to the ESP8266.
After it’s uploaded I should be able to go into the Serial monitor and press h to get a list of commands for my mp3 player. Pressing 1 should play my first sound effect, 2 should play the second, and 3 should play the third. Perfect. But this doesn’t really do me any good yet. I’m not going to be triggering these audio files via the serial monitor, I want them to be triggered by MQTT events.
At this point, I want to take an old sketch that I’ve already written that has WiFi and MQTT, and combine it with this mp3 player sketch to enable my mp3 player to work via MQTT. As I mentioned before, I know I’ll need to combine the void setup of my sketch with the void setup of the MP3 sketch, I’ll also need to make sure I include the functions, variables, and definitions for any commands I want to use. Specifically I’m going to be using the command CMD_PLAY_WITH_VOLUME, which allows me to specify both a file and a volume to play which will allow me to vary the volume randomly to create more realistic thunder.
To keep my code more readable I’ll group together the functions from the MP3 player and put a comment on that section so I know what those functions do. Since I borrowed this MQTT code from another project, I also need to change the ClientID and the check in topic so I don’t create conflicts on my MQTT server.
Finally I’ll add in some MQTT responses to play each track via MQTT then hit compile to see if I get any errors. If I do get an error, it’s not the end of the world. Sometimes I forget to add a semicolon or curly bracket, but usually I just forgot to copy over a needed function or variable. In this case I purposely left out the s_byte_to_hex function to show what happens when you forget something. No problem, go to the original code, copy it over and try compiling again. If you can’t find that function in the original code it’s very likely that you are missing a library, check the includes statements at the top of the code to make sure you aren’t missing any.
My program successfully compiled, so I can send it over to my NodeMCU using the same settings as before. Now when I send the message 1 to the topic Audio it plays the thunder1 file, 2 sends thunder2 and 3 sends the witch cackle. I plan on adding more later to play them at different volumes, but this will do for now.
A simple node-red automation triggers the cackle when my MQTT doorbell rings, and a small edit to my lightning LED code sends either a 1 or a 2 to trigger one of the thunder sound effects. Mission accomplished… now I just need to find some bigger speakers to use for trick or treat night.
Thanks to all my patrons over at patreon for your continued support of my channel, if you look forward to these videos each week and you’re interested in supporting my channel, check out the links in the description. If you aren’t subscribed yet, and you enjoyed this video, please consider subscribing, and as always, thanks for watching the hookup.