Thursday, November 16, 2017

Bare Conductive Arduino MP3 Player

I found the Bare Conductive Touch Board Starter Kit while searching for activities to do with my almost nine year old son over the school holidays. It contains a custom Arduino board with a built in microSD card reader and audio output along with twelve electrode that can be used for touch or proximity sensor input. There is also a jar and tube of black conductive paint, a brush for painting them on alligator clip wires and adhesives, stencils and cardboard cutouts for designing the sensors. A 128 MB microSD card, USB card reader and portable speaker round out the collection.

The idea is that you either paint on the sensors or clip the wires to metallic objects so that when touched it triggers the playing an MP3 track.

It turned out to be a bit too complicated for a 9 year old, but my son likes to fall asleep and wake up to music and I had the idea that I would use the kit to make an interesting MP3 player on Alex's bedhead.

The first thing was to program the Arduino board, something I was now familiar with after the ticket gate project. The code examples provided were only for the triggering of single tracks, whereas I wanted proper player navigation. This included the ability to:

  • Stop, play, pause and skip tracks
  • Shuffle tracks
  • Change the volume
  • Play collections
By default the library used by the code only plays files named in the format TRACKxxx.mp3, where each x is a number 0 to 9. I did see some code for alternative names, but couldn't get it to work so I stuck with that format.

When the shuffle mode is selected random tracks are picked. Each played track is added to a list which is scanned to ensure that it is not repeated. Skipping a track forwards or backwards only moves to the next number and doesn't use random selection.

For playing collections I made a subdirectory, in my case called sleep and placed the relevant tracks in there. I also swapped the 128 MB microSD card for an 2 GB card I'd got with an old phone. This is the largest size supported by the FAT16 library. 

The code, listed at the bottom of the post, is memory intensive and occasionally locks, but it mostly seems to work.


The need to manually copy and rename tracks made the player impractical for use by my son, who also likes current pop. Instead a I got him a portable bluetooth speaker and an old mobile phone with Google Play Music installed.

Still, after all that effort I decided to get my player working anyway and painted up a pre-primed MDF board with the stencil, affixing the Arduino board and speaker at the bottom. Each silhouette corresponds to a control. It's not intuitive, but it is artistic and I might add to the design later. I like the look of the bare white, gold and black Arduino as well, a nice reminder of what is usually hidden inside the designs.


/*******************************************************************************
Andrew's Touch MP3 player
------------------------------
E0 - Stop
E1 - Play
E2 - Pause
E3 -
E4 - Skip backwards
E5 - Skip forwards
E6 -
E7 - Change Mode (Random/Continuous)
E8 - Sleep folder
E9 - Volume Down
E10 - Volume Up
E11 - Mute
Based on code by Bare Conductive code written by Stefan Dzisiewski-Smith and Peter Krige.
*******************************************************************************/
// touch includes
#include <MPR121.h>
#include <Wire.h>
#define MPR121_ADDR 0x5C
#define MPR121_INT 4
// mp3 includes
#include <SPI.h>
#include <SdFat.h>
#include <FreeStack.h>
#include <SFEMP3Shield.h>
// mp3 settings
#define TRACKCOUNT 130
#define MINVOLUME 65278
#define MAXVOLUME 15
#define OFF 0
#define ON 1
#define CONTPLAY 1
#define RANDPLAY 2
// mp3 variables
SFEMP3Shield MP3player;
byte result;
int lastPlayed = 0;
int currentTrack = 0;
int playedTracks[TRACKCOUNT];
long volume = 50;
int playMode = OFF;
int inct = 0;
int muted = OFF;
int sleep = OFF;
// mp3 behaviour defines
#define REPLAY_MODE TRUE
// touch behaviour definitions
#define firstPin 0
#define lastPin 11
// sd card instantiation
SdFat sd;
SdFile file;
void setup(){
Serial.begin(57600);
pinMode(LED_BUILTIN, OUTPUT);
//while (!Serial) ; {} //uncomment when using the serial monitor
Serial.println("Bare Conductive Touch MP3 player");
if(!sd.begin(SD_SEL, SPI_HALF_SPEED)) sd.initErrorHalt();
if(!MPR121.begin(MPR121_ADDR)) Serial.println("error setting up MPR121");
MPR121.setInterruptPin(MPR121_INT);
MPR121.setTouchThreshold(40);
MPR121.setReleaseThreshold(20);
result = MP3player.begin();
MP3player.setVolume(volume,volume);
if(result != 0) {
Serial.print("Error code: ");
Serial.print(result);
Serial.println(" when trying to start MP3 player");
}
}
void loop(){
if (playMode == OFF) {
readTouchInputs();
} else {
playMusic();
}
}
void playMusic() {
int inct = 0;
if (playMode != OFF ) {
if (playMode == RANDPLAY) {
randomSeed(analogRead(0));
if (currentTrack == 0) {
currentTrack = random(TRACKCOUNT);
}
MP3player.playTrack(currentTrack);
}
do {
switch(playMode) {
case RANDPLAY:
if (!MP3player.isPlaying()) {
MP3player.stopTrack();
playedTracks[inct] = currentTrack;
do
currentTrack = random(TRACKCOUNT);
while (checkPlayed(inct, currentTrack));
inct++;
MP3player.playTrack(currentTrack);
Serial.print("Playing track: ");
Serial.println(currentTrack);
}
break;
case CONTPLAY:
if (!MP3player.isPlaying()) {
inct++;
MP3player.stopTrack();
currentTrack = inct;
MP3player.playTrack(inct);
Serial.print("Playing track: ");
Serial.println(currentTrack);
}
default:
break;
}
readTouchInputs();
} while (inct < TRACKCOUNT);
playMode = OFF;
}
}
boolean checkPlayed(int i, int track) {
boolean played = false;
for (int j = 0; j < i + 1; j++) {
if (playedTracks[j] == track) {
played = true;
break;
}
}
return played;
}
void readTouchInputs(){
if(MPR121.touchStatusChanged()){
MPR121.updateTouchData();
// only make an action if we have one or fewer pins touched
// ignore multiple touches
if(MPR121.getNumTouches()<=1){
for (int i=0; i < 12; i++){ // Check which electrodes were pressed
if(MPR121.isNewTouch(i)){
//pin i was just touched
Serial.print("pin ");
Serial.print(i);
Serial.println(" was just touched");
digitalWrite(LED_BUILTIN, HIGH);
switch(i) {
case 0:
// Stop
playMode = OFF;
currentTrack = 0;
MP3player.stopTrack();
Serial.println("Stopped playing");
break;
case 1:
// Play
if (playMode == OFF) {
playMode = CONTPLAY;
MP3player.playTrack(currentTrack);
Serial.print("Playing track: ");
Serial.println(currentTrack);
} else {
MP3player.resumeMusic();
Serial.print("Resume playing: ");
Serial.println(currentTrack);
}
break;
case 2:
// Pause
MP3player.pauseMusic();
Serial.println("Paused music ");
break;
case 4:
// Skip backwards
MP3player.stopTrack();
currentTrack = currentTrack - 1;
if (playMode == RANDPLAY && inct > 0) {
currentTrack = playedTracks[inct - 1];
}
if (currentTrack < 0) {
currentTrack = 0;
}
MP3player.playTrack(currentTrack);
Serial.print("Skipped to track: ");
Serial.println(currentTrack);
break;
case 5:
// Skip forwards
MP3player.stopTrack();
currentTrack = currentTrack + 1;
if (currentTrack == TRACKCOUNT) {
currentTrack = TRACKCOUNT - 1;
}
MP3player.playTrack(currentTrack);
Serial.print("Skipped to track: ");
Serial.println(currentTrack);
break;
case 7:
// Change play mode
MP3player.stopTrack();
if (playMode == CONTPLAY) {
playMode = RANDPLAY;
randomSeed(analogRead(0));
currentTrack = random(TRACKCOUNT);
} else {
playMode = CONTPLAY;
currentTrack = 0;
}
Serial.print("Changed mode to: ");
Serial.println(playMode);
break;
case 8:
// Sleep directory
if (sleep == OFF) {
if(!sd.chdir("sleep")){ // select our directory
Serial.println("error changing to sleep directory"); // error message if reqd.
} else {
MP3player.stopTrack();
sleep = ON;
currentTrack = 0;
inct = 0;
MP3player.playTrack(currentTrack);
Serial.print("Current track: ");
Serial.println( currentTrack );
Serial.println("Sleep directory selected");
}
} else {
if(!sd.chdir()){ // select root
Serial.println("error changing to root directory"); // error message if reqd.
} else {
MP3player.stopTrack();
sleep = OFF;
currentTrack = 0;
inct = 0;
MP3player.playTrack(currentTrack);
Serial.print("Current track: ");
Serial.println( currentTrack );
Serial.println("Root directory selected");
}
}
break;
case 9:
// Volume down
if ( volume < MINVOLUME - 1) {
volume = volume + 1;
} else {
volume = MINVOLUME;
}
Serial.print("Volume: ");
Serial.println(volume);
MP3player.setVolume(volume,volume);
break;
case 10:
// Volume up
if ( volume > MAXVOLUME + 1) {
volume = volume - 1;
} else {
volume = MAXVOLUME;
}
Serial.print("Volume: ");
Serial.println(volume);
MP3player.setVolume(volume,volume);
break;
case 11:
// Mute
if (muted == OFF) {
MP3player.setVolume(MINVOLUME,MINVOLUME);
Serial.println("Muted volume");
muted = ON;
} else {
muted = OFF;
MP3player.setVolume(volume, volume);
Serial.println("Unmuted volume");
}
break;
default:
break;
}
} else {
if(MPR121.isNewRelease(i)){
Serial.print("pin ");
Serial.print(i);
Serial.println(" is no longer being touched");
digitalWrite(LED_BUILTIN, LOW);
}
}
}
}
}
}
view raw gistfile1.txt hosted with ❤ by GitHub

Wednesday, November 15, 2017

Raspberry Pi 5 inch and 3.5 inch touch screens

After making the Arduino ticket gate I've been inspired to play with some of the other "maker" offerings available. I recently purchased a Raspberry Pi 3 and Raspberry Pi Zero W to experiment with. That actually takes the Raspberry Pi in the house to three as I previously bought my son a Kano Computer, which also uses a Pi board.

The Raspberry Pi can be hooked up to a standard HDMI monitor, but I wanted to try something a bit smaller so I obtained an Adafruit 3.5" PiTFT Plus and a 5.0" HDMI touchscreen. The former came with a badly soldered header and I've yet to get it working. Its instructions are also woefully out of date and unsuitable for the current Raspbian Stretch version of the Linux OS. 

For both screens what you need is the LCD-Show code. However, if, like me, you installed Raspbian via NOOBS then you are likely to get a kernel panic after installing. Don't panic!

I found these instructions to be helpful, but in my case I needed to slightly modify the boot partition statement.

  1. You need to edit /boot/cmdline.txt on the SD card, either before your Pi reboots or using a second Linux machine (fortunately I had the other Raspberry Pi Zero and a MicroSD card reader to do this on).
  2. Replace root=/dev/mmcblk0p2 with root=/dev/mmcblk0p7
If editing on a second computer put the MicroSD back in the Pi's slot and reboot.

Everything should now work and you've now got a small Linux device. Not as small as my old Zaurus handheld PC's but a lot more hackable.

Size comparison between the Raspberry Pi with 5" screen and a standard mouse

Popular Posts