2.11 – Marta POC Main Script

I worked on the software for a few nights and it’s really dirty but come on, it’s also free, everyone can have it and it works. So actually, my code has a lot in common with yo mamma. Sorry, couldn’t resist.

Now that you have seen all of the components up and running individually, it’s time to use them all together. There’s a proof-of-concept implementation available speaking to all the sensors, speakers and lights. It’s nothing fancy but definitely enough to get you going. The current status of the Python scripts is like “kinda works”.

I’m not going to explain all the itty-bitty details of the scripts. If you wanna dive into the software, go ahead and improve it. That shouldn’t be too hard as it is just the bare minimum to show what the box is capable of.

To get an idea: The different components all kind of work on their own. The actors (like the LED strip, the status LED, the music player) consume data while sensors (MPU, buttons, RFID reader) produce data. This may happen in different threads.

Event data is pushed into a message queue maintained by the main script. Despite some maintenance work, all events are passed to the active event handler. Currently that’s almost always the music handler which implements the ‘normal’ behavior of the box. It could also be some kind of unimplemented game handler or whatever. Anyhow, these handlers are able to interact with all of the components in the box. Special RFID tags may issue the main script to change the active handler.

To start the proof of concept main program, we need first to set a variable $MARTA pointing to the base directory of the project. Only then can we start the script. Remember that certain things currently only work as root user.

pi@raspberrypi $ sudo MARTA=/home/pi/mmm/ python /home/pi/mmm/marta/Marta.py log2stdout

The WS281X library takes some time to setup all the necessary stuff, so just give it a few seconds. You should eventually hear a startup sound and an LED light ring rainbow animation. Also, the command line should be flooded with debug messages.

Now, as soon as you place a tag on top of the machine… nothing happens. Well, at least nothing to be heard. We need to meet a few prerequisites in order to get the program to do what we want. Just stop the script using CTRL + C or kill the process manually.

The following approach is designed in such a way, that one is able to proceed without ever having to interact with a script. It is purely based on files and something like FileZilla or any other SFTP client will totally suffice to edit the music library and tag links.

The default music handler obtains the audio files it plays when a tag is placed on the RFID reader from a directory called audio. The internal structure of that folder must follow certain rules. To get an idea of that mandatory folder composition, the project contains a valid example directory structure.

pi@raspberrypi $ sudo apt-get install tree
pi@raspberrypi $ tree /home/pi/mmm/audio

/home/pi/mmm/audio
├── another example AABBCCDDEEFF
│   ├── another file names don't matter.mp3
│   └── file1.mp3
├── C01DDEADBEEF
│   ├── album
│   │   ├── music_0.mp3
│   │   ├── music_1.mp3
│   │   ├── music_2.mp3
│   │   └── music_3.mp3
│   └── another album
│       ├── my favorite music.mp3
│       ├── some song.mp3
│       └── whatever.mp3
├── example_112233445566
│   ├── another sub folder names do not matter at all
│   │   ├── track1.mp3
│   │   ├── track2.mp3
│   │   ├── track3.mp3
│   │   └── track4.mp3
│   └── subfolder
│       ├── chapter1.mp3
│       └── chapter2.mp3
├── something else: 0123456789AB
│   ├── audiobook_song_1.mp3
│   ├── audiobook_song_2.mp3
│   └── audiobook_song_3.mp3
└── system
    ├── shutdown.mp3
    └── startup.mp3

9 directories, 20 files

A sub folder named system may not be changed or removed as it contains the minimum required audio files to startup M³.

All other user supplied directories will be linked to RFID tags by their name. Everything within the folder’s name will be ignored, but the suffix, which must be an upper case hexadecimal ID of length 12.

To obtain such an ID, we observe a special file within the audio folder. It’s called unknown_tag.txt. This very file will only be existent as long as the music handler reads an unknown tag. It contains the hexadecimal ID of the current tag on the machine in the exact format we need. As soon as it is removed, the file will be unlinked.

pi@raspberrypi $ cat /home/pi/mmm/audio/unknown_tag.txt

5A0084EEF3C3

Go on, create a directory of any name and append your ID. When you’re done, you need to put some content into it. There are two ways of doing that.

A top level tag directory may either contain mp3 files without any other directory structure (have a look at the example folder something else: 0123456789AB). If you want to further structure your audio collection, it may consist of another level of album folders (see example_112233445566). In the latter case, no audio files are allowed within the tag directory itself. The sub directories may now be named as you like, no naming conventions are enforced. Just push your mp3 files into these folders.

Currently, only files supported by mpg123 can be used.

If you want to be sure that your file system structure meets these easy to remember requirements, use the supplied script which maps tags to directory lists.

pi@raspberrypi $ python /home/pi/mmm/marta/TagToDir.py /home/pi/mmm/audio

20:43:09.303 |   TagToDir |    /home/pi/mmm/audio/system exists
20:43:09.314 |   TagToDir |    C01DDEADBEEF=['/home/pi/mmm/audio/C01DDEADBEEF/album', '/home/pi/mmm/audio/C01DDEADBEEF/another album']
20:43:09.321 |   TagToDir |    0123456789AB=['/home/pi/mmm/audio/something else: 0123456789AB']
20:43:09.327 |   TagToDir |    AABBCCDDEEFF=['/home/pi/mmm/audio/another example AABBCCDDEEFF']
20:43:09.334 |   TagToDir |    112233445566=['/home/pi/mmm/audio/example_112233445566/another sub folder names do not matter at all', '/home/pi/mmm/audio/example_112233445566/subfolder']
20:43:09.338 |   TagToDir |    Everything seems fine.

Now you are good to go. As soon as the device recognizes one of the linked tags, the files within the directories will be played back. Try it out by running the Marta main script again.

I will not explain the software anymore. Instead, I encourage you to head over to the project’s software repository and start fiddling around.

A few features of the prototype:

  • Startup and shutdown animation/sound
  • Manual and timeout soft shutdown
  • RFID tag detection
  • Visible notification for tag detection/removal
  • Unknown tag information
  • Playback of songs
  • Volume control
  • Playback speed control
  • LED brightness control
  • Next/Previous track
  • Next/Previous album
  • LED animation of current song index within album
  • LED rainbow demo

The script itself doesn’t shutdown M³ and that’s intentional. I didn’t wanna deal with unexpected errors within python too much, so the whole process exits. I wrapped the call to python into a startup shell script. It will execute the Marta main script over and over again until the callee exits successfully (i.e. exit value is 0). In that very case, the shell script will shutdown the whole device gracefully. This way, unexpected behavior (i.e. uncaught errors) in the python world don’t lead to a system shutdown but to some kind of ‘soft restart’ of the M³ software.

The shell script ‘knows’ its location within the project file tree, so we don’t need to set the $MARTA environment variable. Just execute the script.

pi@raspberrypi $ sudo /home/pi/mmm/scripts/marta_startup.sh

       Welcome to
  __  __ __  __ __  __ 
 |  \/  |  \/  |  \/  |
 | |\/| | |\/| | |\/| |
 | |  | | |  | | |  | |
 |_|  |_|_|  |_|_|  |_|
                       
  Marta Musik Maschine

Main directory: /home/pi/mmm
Starting Marta main script.

Everything should work just like before.

There’s one very important thing left to do. We want this very shell script to be executed as soon as the box boots. We therefore utilize a runcom (run commands) file and add a call to our script at the end of it:

pi@raspberrypi $ sudo nano /etc/rc.local

#!/bin/sh -e
# [...]
# By default this script does nothing.

# Print the IP address
_IP=$(hostname -I) || true
if [ "$_IP" ]; then
  printf "My IP address is %s\n" "$_IP"
fi

/home/pi/mmm/scripts/marta_startup.sh &

exit 0

The startup script will be run as soon as the device boots, python will be executed. As soon as the user wishes to turn of the machine, the button press will be recognized, the python program will exit with value 0 which initiates a shutdown command.

That’s it, you’re done with the electronics and software part. Build an enclosure yourself or use the one I’m going to show you.