During this time of social distancing, it is difficult to find ways to be more active and move around. Compared to when I was in school, online classes require little moving around buildings and exchanging school materials in lockers. Walking outside the neighborhood is a great way to keep active and exercise daily, however, some days it may be too windy to walk outside or it may be raining. Additionally, during the ongoing COVID-19, it has become increasingly important to stay inside to decrease the risk of spreading or catching the virus.
This is why we were motivated to develop a fun way to keep people active and healthy indoors! MuSticks combines the ideas of music and sticks to make exercising indoors practical and fun. It is important to maintain your health and remain active during these difficult times, and we hope MuSticks can help people in this small way.
The project uses a Raspberry Pi Zero W, Adafruit Stereo Speaker Bonnet, and Surface Bone Conductive Transducer speaker. We decided to use transducers for its interesting properties of transducing the audio wave signal on hard surfaces. We decided to use plastic bottles, but you can experiment with different materials.
We decided to break the sticks in three parts.
- A speaker holder
- A bottle holder
- A board/battery holder
In order to create a full stick you will need to print two speaker holders and two bottle holders. The speaker holder fits snug in the bottle holder as shown below:
Then the board/battery is attached to the bottle holder by using screws/nuts as shown below
There are two versions of the border/battery holder; the "Battery and Board Holder.stl" file has a slighter smaller ratio than the BBH3.stl holder. This difference allows both holders to snatch in together and roll in order to close and keep the contents protected.
Putting Hardware together
In order to publish our web interface, we need to install LDAP in our RPi Zero W. You can find several web pages that guide you through the installation of LDAP in your Raspberry Pi Zero W.
We also install the mp3 utility MPG123 with the command:
$ sudo apt-get install mpg123
And then follow the instructions in the Adafruit Speaker Bonnet for Raspberry Pi easy installation here.
Thanks to Adafruit instructions we were able to find SomaFM a
Over 30 unique channels of listener-supported, commercial-free, underground/alternative radio broadcasting to the world.
It's ideal to easily test the speakers and have a variety of selections. However, it was not going to be easy to run it from a web service.
Now we were ready to create our interface.
Once the server and speakers were installed, we created a simple web interface by using "Bootstrap Studio". We are not sure if we can upload the automatic generate template, but we can show the manual changes we did to the code.
The bellow is a snapshot of the interface:
Each button image submits a code to the main page in the server to indicate the selected station. The code looks like
<button name="action" value="selEighties"><img src="assets/img/scenery/u80s-120.png" alt="DLS"></button>
<button name="action" value="selDeepSpace"><img src="assets/img/scenery/DeepSpaceOne.gif" alt="DLS"></button>
<button name="action" value="selPoptron"><img src="assets/img/scenery/poptron120.png" alt="DLS"></button>
<button name="action" value="selMissionC"><img src="assets/img/scenery/missioncontrol120.jpg" alt="DLS"></button>
<button name="action" value="selXmas"><img src="assets/img/scenery/xmasrocks120.png" alt="DLS"></button>
<button name="action" value="selStop"><img src="assets/img/scenery/image1.jpg" alt="DLS"></button>
Note that the value attribute has the values sent back to the server in a GET command name action.
The original idea was to receive the station selection and then batch a process to run in the background MPG123, but it was not possible. For starters, the program did not launch unless we used the sudo command to run the shell process.
We tried doing the bellow command, where exec is a PHP function to spawn a process:
exec("nohup mpg123 http://ice1.somafm.com/u80s-128-mp3 > /dev/null 2>&1 echo $!");
but it crashed mpg123 program as soon as it started the process.
Then we tried shell_exec with sudo privileges in the command line, like:
shell_exec('sudo mpg123 http://ice1.somafm.com/u80s-128-mp3 > /dev/null 2>&1 echo $!');
which worked, with the caveat that the process never came back to the server and therefore the client was stuck, not allowing it to make a different selection.
We finally decided to break the dependency between the web server and the MPG123 program.
We created a bash script that would run in a loop, reading the contents of a text command file and submitting at the main process level the batch jobs to run the MPG123 program. The script is explained in the next section.
The job of the web server was to get the selected station and write the corresponding command code to the command file so the bash script could read the selection and execute the process.
A concern was the concurrency of the file access, could we open it to write and at the same time read to execute the command. YES, we could!!!.
Here is part of the server PHP program that get the selection from the client and write the command code to the command file:
//Check if there is a command
//Open file to write
$myfile = fopen("runSelection.txt", "w");
//Set command to stop current streaming
//Make sure to flush buffer to file
//Wait, maybe not that necessary
//Select the next stream station
if ($_GET["action"] == "selEighties")
$myfile = fopen("runSelection.txt", "w");
Notice that we first check if the client sends a selection thru the action attribute, then we open the file for writing with the PHP function fopen, then write a command code of 4, which means stop the current MPG123 job, then we close the file to flush the buffer and allow a small delay (maybe not needed) for the bash script to read and execute this command.
Next, if a station was selected, then open the command file again (fopen), write the corresponding command, close the file (flush the buffer) and wait for the next client selection.
This process guaranteed the smooth flow of client/server execution, and did not expose any possible security by allowing the server to access the kernel process commands.
The bash script has the magic that allows the project to flourish on a web interface.
The script scans the command file runSelection.txt. This file is being writing by the PHP server service as a client requests a station.
The script runs in a loop until the exit command (2) is selected
while [ $selection -ne 2 ]
The script reads each line of the file (just one line) by the following loop command
while read line
done < runSelection.txt
Then the script reviews the selection and if it is different from the previous selection, it submits the batch file for execution, as for example
# if current selection is not the last read then execute
if [[ $selection -eq 3 ]] && [[ $lastSel -ne $selection ]]; then
# Prepare command to execute in the background
# Execute command and assign process id to variable.
nohup $cmd > /dev/null & PID=$!
# Remember last selection so it does not execute again.
Note that the script preserves the process id, so it can kill it when a stop command (4) enters the command file, as shown below:
# Kill last submitted process
nohup $cmd > /dev/null
And that's it, the bash file works happily creating and removing processes and the web service works with no interruption.
It took us some thinking to come up with the workaround but if you have a process you want to batch this might be a possible solution.
Finally, notice that we opt to use SomaFM to play music, but you can add your MP3 library of songs and have fun.