The Sound (and Sight) of Music! Teaching Electronics with the STM32 Nucleo
By Michael Parks, P.E., Mouser Electronics
Licensed under CC BY-SA 4.0
Download Full Project
BOM
Software
When you are ready to integrate the Nucleo into the project, we will need to first program the Nucleo to do what we desire. In this example, we will sample the output of each channel's audio using the onboard analog-to-digital converter. Depending on the intensity of each channel's audio, and in turn the voltage level of the op amp’s output, we will drive 1, 2, or 3 LEDs per channel. So if the audio is very low we will drive 1 LED, and if it is very loud we will drive 3 LEDs.
The program we are running on the Nucleo is pretty straightforward:
Sample the analog value of both the left and right channel and digitize.
Assign the digitized values to a variable so we can use it throughout the current loop iteration.
Look at the digitized value of the channel and determine if its volume is low, medium, or high.
You can tweak the values of the variables that trigger the low, medium, and high LEDs. You can read more in the software section, look for the discussion on lowCutoff, mediumCutoff, and highCutoff. For now, the default logic is as follows:
If the channel is sampled to be below 10, then it is nearly perfectly quiet, turn off all the LEDs
If the channel is sampled to be between 10 and 1000, then turn on just one LED since the volume is low.
If the channel is sampled to be between 1000 and 2000, then turn on two LEDs since the volume is medium.
If the channel is sampled to be greater than 2000, then turn on all three LEDs since the volume is high.
The program code for the Nucleo is listed below and can also be found in the Resources section as main.cpp or as a text file named LightSound_NucleoCode.txt:
#include "mbed.h"
AnalogIn leftChannel(A0); // left channel
AnalogIn rightChannel(A1); // right channel
DigitalOut leftLED_LOW(D2);
DigitalOut leftLED_MED(D3);
DigitalOut leftLED_HIGH(D4);
DigitalOut rightLED_LOW(D5);
DigitalOut rightLED_MED(D6);
DigitalOut rightLED_HIGH(D7);
void setLeftLED_OFF();
void setLeftLED_LOW();
void setLeftLED_MED();
void setLeftLED_HIGH();
void setRightLED_OFF();
void setRightLED_LOW();
void setRightLED_MED();
void setRightLED_HIGH();
int main() {
float leftChannelSignal;
float rightChannelSignal;
float lowCutoff = 10;
float mediumCutoff = 1000;
float highCutoff = 2000;
printf("\nTurn Sound Into Light\n");
while(1) {
leftChannelSignal = leftChannel.read(); // Converts and read the analog input value (value from 0.0 to 1.0)
rightChannelSignal = rightChannel.read(); // Converts and read the analog input value (value from 0.0 to 1.0)
leftChannelSignal = leftChannelSignal * 3300; // Change the value to be in the 0 to 3300 range
rightChannelSignal = rightChannelSignal * 3300; // Change the value to be in the 0 to 3300 range
printf("L Channel = %.0f mV\n", leftChannelSignal);
printf("R Channel = %.0f mV\n", rightChannelSignal);
//LEFT CHANNEL
if (leftChannelSignal <= lowCutoff) {
setLeftLED_OFF();
}
else if (leftChannelSignal > lowCutoff && leftChannelSignal <= mediumCutoff) {
setLeftLED_LOW();
}
else if (leftChannelSignal > mediumCutoff && leftChannelSignal <= highCutoff) {
setLeftLED_MED();
}
else {
setLeftLED_HIGH();
}
//RIGHT CHANNEL
if (rightChannelSignal <= lowCutoff) {
setRightLED_OFF();
}
else if (rightChannelSignal > lowCutoff && rightChannelSignal <= mediumCutoff) {
setRightLED_LOW();
}
else if (rightChannelSignal > mediumCutoff && rightChannelSignal <= highCutoff) {
setRightLED_MED();
}
else {
setRightLED_HIGH();
}
//wait(0.2); // 200 ms
}
}
//LEFT CHANNEL
void setLeftLED_OFF(){
leftLED_LOW = 0;
leftLED_MED = 0;
leftLED_HIGH = 0;
}
void setLeftLED_LOW(){
leftLED_LOW = 1;
leftLED_MED = 0;
leftLED_HIGH = 0;
}
void setLeftLED_MED(){
leftLED_LOW = 1;
leftLED_MED = 1;
leftLED_HIGH = 0;
}
void setLeftLED_HIGH(){
leftLED_LOW = 1;
leftLED_MED = 1;
leftLED_HIGH = 1;
}
//RIGHT CHANNEL
void setRightLED_OFF(){
rightLED_LOW = 0;
rightLED_MED = 0;
rightLED_HIGH = 0;
}
void setRightLED_LOW(){
rightLED_LOW = 1;
rightLED_MED = 0;
rightLED_HIGH = 0;
}
void setRightLED_MED(){
rightLED_LOW = 1;
rightLED_MED = 1;
rightLED_HIGH = 0;
}
void setRightLED_HIGH(){
rightLED_LOW = 1;
rightLED_MED = 1;
rightLED_HIGH = 1;
}
Good coding practice would dictate that since the left and right channels are performing similar tasks that we could incorporate both into a single functions. However, we kept them separate to help with understanding what’s going on with each channel and so you can create different effects for each channel independently.
Now that we understand what the code is doing, let’s have quick discussion of what is needed to actually get the code onto the Nucleo. To make things as simple as possible to start, you can simply copy the code we’ve provided above and paste into the tool.
Figure 9: The free mbed.org programming tool for the Nucleo. Shown is the Works Space Manager window. (mbed_workspaceManager.png)
First you will need to download the USB driver for the Nucelo-F401RE . Click on Software and then Development Tool Software to find the “part number” for the download package: STSW-LINK009 . Or, you can find it by starting from here: https://developer.mbed.org/platforms/ST-Nucleo-F401RE. This site is also good for more information on the Nucleo, including firmware upgrades. You should check for the latest firmware upgrade every time you get a new board as good practice. Full instructions are offered on how to make these downloads if you are using the Nucleo for the first time, or with a new PC.
You can head over to mbed.org and create a free account. The nice thing about the Nucleo is that you can use the programming environment in the browser so it’s always up to date. Once you are logged in you should see the “Compiler” link in the top right of the website, click on that. This will open up your workspace where all your projects will reside. From here follow these steps:
In the top left click on “New” and select “New Program”.
In the dialog box make sure the following are set:
Platform: “NUCLEO-F401RE”
Template: “Empty program”
Program Name: Whatever you wish. I chose “LightsAndSounds”
This will create your default files needed, including “main.cpp”. Double click that file to open the editor.
Paste in the code we’ve provided.
Click “Save All”
Click “Compile”. This will create the necessary .bin file that you will drop onto your STM32 Nucleo. You will be asked where you want to save the file to on your computer. Usually I choose the Desktop.
Installing code on the STM32 Nucleo is as simple as dragging the file from wherever you saved it onto the STM32 Nucleo, just as you would click-and-drag any file onto a USB thumb drive.
That’s it. After the .bin file is transferred the Nucleo will automatically reboot and start running your application.
We would love to hear what you think about this project; please tell us in the comments section below.