Puppe zum Sprechen bringen mit Arduino Uno und Adafruit MP3 Shield

In diesem Beitrag zeige ich, wie man mit dem Arduino Uno und dem „Music Maker“-MP3-Shield von Adafruit eine Puppe zum Sprechen bringen kann. Ziel des ganzen: Die Puppe soll beim Einschalten eine bestimmte MP3 abspielen. Danach werden in größer werdenden Zeitabständen andere MP3s in einer zufällig generierten Reihenfolge abgespielt. Außerdem soll es einen Taster geben, mit dem man eine andere, bestimmte MP3 abspielen lassen kann.

ronnyIn meinem Beispiel handelt es sich um eine Puppe, die den Charakter Ronny aus dem Point-and-Click-Adventure Deponie Doomsday darstellt. Infos zum Spiel gibt es hier, hier ist der Link zur Bildquelle. Beim Nähen des Kuscheltiers hat mir dieses Video sehr geholfen.

Der Vorteil bei diesem Charakter ist, dass er in einem Blumentopf sitzt. In diesem wird später die gesamte Technik verschwinden. Der Blumentopf wird mit einer Holzplatte von oben verschlossen, sodass in die Platte die Lautsprecher und Knöpfe eingebaut werden können.

Teileliste

Alle Teile habe ich bei EXP Tech bestellt. Der Versand lief sehr schnell und die Produkte hielten alle, was sie versprachen. Wer die Teile nachkaufen möchte, findet in folgender Liste auch direkt die Artikelnummern.

  • Arduino Uno R3 (EXP-R08-002)
  • Adafruit „Music Maker“ MP3 Shield for Arduino w/3W Stereo Amp (EXP-R15-427)
  • 2x Lautsprecher – 3″ Durchmesser – 4 Ohm 3 Watt (EXP-R15-500)
  • Kingston 8 GB Micro-SDHC Class10 Speicherkarte (EXP-T04-034)
  • Weatherproof Metal Pushbutton with White LED Ring – 16mm White Momentary (EXP-R15-102)
  • 16mm Illuminated Pushbutton – White Latching On/Off Switch (EXP-R15-543)
  • DC Stecker Buchse PRT-00119 (EXP-R05-428)

Insgesamt landet man dann ca. bei 80 Euro. Was dann noch fehlt, ist die Stromversorgung für den Arduino. Diese stellt man entweder über eine Batterie her, z.B. mit einem Batterie-Clip für 9V-Blöcke (EXP-R15-841, 3,25 Euro) oder über ein Netzteil zum Anschluss an eine normale Steckdose (Link zu Amazon, ca. 10 Euro).

Für die Schaltung benötigt man außerdem noch zwei Widerstände, in meinem Fall einmal 10kΩ (4-Ring-Farbcode: braun, schwarz, organge, gold) und einmal 1kΩ (braun, schwarz, rot, gold). Informationen zur Farbkodierung von Widerständen findet man hier.

Wer noch Kabel braucht, kann zu einem der folgenden Produkte greifen und hat damit auch noch genug für spätere Projekte (jeweils ca. 18 Euro):

  • Hook-Up Wire – Sortiment (Solid Core) (EXP-R05-545)
  • Hook-Up Wire – Sortiment 22AWG (Stranded) (EXP-R05-546)

Jetzt kann man sich Lötkolben und Zange zurecht legen und es kann losgehen!

Die Schaltung

Das MP3 Shield wird mit den beiliegenden Stecker-Pins auf den Arduino gesteckt. Die Pins werden dann mit dem Shield verlötet. Eine detailierte Anleitung dazu findet man hier.

Sowohl der Taster (momentary switch) als auch der Schalter (latching switch) haben eingebaute LEDs, die jedoch separat ansteuerbar sind, d.h. sie können z.B. auch dann leuchten, wenn sie nicht gedrückt werden (oder auch komplett unabhängig von ihren Zustand). Der Schalter soll als Ein- und Ausschalter fungieren, er wird dafür bei einer der beiden Adern (Plus, Minus) zwischen die Eingangsbuchse und den Arduino geschaltet. Sobald der Arduino „hochgefahren“ ist, schaltet er die LED dann dauerhaft ein. Zwischen den Arduino und die LED des Schalters muss der der 1kΩ-Vorwiderstand geschaltet werden, um die LED nicht zu zerstören.

Der Taster hingegen soll dazu dienen, eine bestimmte MP3 abzuspielen, die in der Zufallsreihenfolge ansonsten nicht vorkommt. Die LED des Tasters soll immer ausgeschaltet sein, während eine MP3 abgespielt wird. In der restlichen Zeit bleibt sie jedoch dauerhaft eingeschaltet. In diesem Taster ist bereits ein Vorwiderstand eingebaut, sodass man die LED-Anschlüsse direkt mit dem Arduino verbinden kann. Der common-Anschluss des Tasters wird mit einem 5-Volt-Pin des Arduinos verbunden. Der normally open-Anschluss wird mit einem Arduino-Pin verbunden, über den später der Zustand des Tasters ausgelesen werden kann. Damit der Zustand dieses Pins auch im nicht gedrückten Zustand des Tasters definiert ist, verbindet man ihn auch noch parallel über den 10kΩ-Widerstand mit einem der Ground-Pins des Arduinos („man zieht den Taster auf Masse“).

Danach muss man nur noch die Lautsprecher mit den entsprechenden Ausgängen des MP3-Shields verbinden und die Schaltung ist komplett. Hier eine Darstellung der Schaltung (ohne die Verbindungen vom MP3-Shield zu den Lautsprechern), anklicken zum Vergrößern:

schaltung(Quelle der schematischen Arduino-Darstellung: hier, alle restlichen Bilder stammen von EXP-Tech.de)

Wie man auf dem Bild erkennen kann, soll also die LED des Schalters über Arduino-Pin 8 und die LED des Tasters über Pin 9 geschaltet werden. Den Zustand des Tasters kann man über den Pin 3 einlesen. Wichtig ist, dass man keine Pins benutzt, die vom MP3-Shield schon belegt sind (Informationen über belegte Pins findet man in der Dokumentation des Shields).

Die Lautsprecher, Knöpfe und die Buchse für die Stromversorgung habe ich in die Holzplatte, die später oben im Blumentopf liegen soll, eingelassen. Die Widerstände und zum Teil auch die Kabel habe ich mit Sekundenkleber von unten an der Platte befestigt, damit hinterher nichts wackelt. Mein Ergebnis sah dann so aus:

20160520_111950

Vorne im Bild sieht man eine Klemme, mit der ich drei Kabel miteinander verbunden habe. Das war nötig, da der Arduino nicht genug Ground-Pins zur verfügung stellt. Alternativ hätte man die Kabel auch zusammen löten können. Eine solche Klemme gibt es für wenige Cents im Baumarkt.

Die Programmierung des Arduinos

Zuerst lädt man sich die Arduino-IDE von hier herunter und installiert diese. Danach benötigt man noch die zum MP3-Shield gehörende Library. Diese findet man hier. Unter Windows entpackt man die Dateien in den Ordner Eigene Dokumente\Arduino\Libraries\Adafruit_VS1053. Anleitungen für andere Betriebssysteme findet man hier. Wenn man die Arduino-IDE startet, findet man unter Datei->Beispiele->Adafruit VS1053 Library ein Sketch namens player_simple.

player_simple

Diesen Sketch habe ich als Grundlage für meinen eigenen Code benutzt. Ich kann nur empfehlen, diesen Sketch einmal durchzuarbeiten, um die Funktionsweise des MP3-Shields nachvollziehen zu können.

Bevor wir MP3-Dateien abspielen können, müssen wir diese auf die SD-Karte kopieren und diese in den SD-Karten-Slot auf dem MP3-Shield stecken. In meinem Fall tragen die MP3s die Dateinamen track001.mp3 bis track062.mp3. Die Anzahl der MP3-Dateien wird vom Programm nicht automatisch bestimmt, sondern steht in der Konstanten

const int numberOfTracks = 62;

Nachdem ich das Programm um die gewünschten Funktionen erweitert habe, entstand folgender Code:

// include SPI, MP3 and SD libraries
#include <SPI.h>
#include <Adafruit_VS1053.h>
#include <SD.h>

// These are the pins used for the breakout example
#define BREAKOUT_RESET  9      // VS1053 reset pin (output)
#define BREAKOUT_CS     10     // VS1053 chip select pin (output)
#define BREAKOUT_DCS    8      // VS1053 Data/command select pin (output)
// These are the pins used for the music maker shield
#define SHIELD_RESET  -1      // VS1053 reset pin (unused!)
#define SHIELD_CS     7      // VS1053 chip select pin (output)
#define SHIELD_DCS    6      // VS1053 Data/command select pin (output)

// These are common pins between breakout and shield
#define CARDCS 4     // Card chip select pin
// DREQ should be an Int pin, see http://arduino.cc/en/Reference/attachInterrupt
#define DREQ 3       // VS1053 Data request, ideally an Interrupt pin

Adafruit_VS1053_FilePlayer musicPlayer = 
  // create shield-example object!
  Adafruit_VS1053_FilePlayer(SHIELD_RESET, SHIELD_CS, SHIELD_DCS, DREQ, CARDCS);

// define LED pins and button pin
const int latchLEDpin = 8;      // latching button LED
const int buttonLEDpin = 9;     // normal button LED
const int buttonPin = 2;        // button state (INPUT)
const int numberOfTracks = 62;

unsigned long nextStartMillis = 0;
unsigned long minutesBetweenTracks = 0;
unsigned long lastMillis = 0;
unsigned long totalMillis = 0;
int playlist[numberOfTracks-2];
int lastTrack = numberOfTracks;

void generatePlaylist() {
  bool trackUsed[numberOfTracks-2];
  for(int i=0; i<numberOfTracks-2; i++) {
    trackUsed[i] = false;
  }

  for(int i=0; i<numberOfTracks-2; i++) {
    int randomNumber = random(numberOfTracks-2-i);
    int count=0;
    int j=0;

    while(count < randomNumber || trackUsed[j]) {      
      if(!trackUsed[j]) {
        count++;
      }
      j++;
    }
    trackUsed[j] = true;
    playlist[i] = j+3;
  }
}

void printPlaylist() {
  Serial.println("current playlist:");
  for(int i=0; i<numberOfTracks-3; i++) {
    if(playlist[i] < 10) {
      Serial.print(" ");
    }
    Serial.print(playlist[i]);
    Serial.print(", ");
    if((i+1)%20 == 0) {
      Serial.println();
    }
  }
  if(playlist[numberOfTracks-3] < 10) {
    Serial.print(" ");
  }
  Serial.println(playlist[numberOfTracks-3]);
}


/****************/
/* --- SETUP ---*/
/****************/
void setup() {
  Serial.begin(9600);
  Serial.println("Ronny\n-----\n");

  pinMode(latchLEDpin, OUTPUT);
  pinMode(buttonLEDpin, OUTPUT);
  pinMode(buttonPin, INPUT);

  digitalWrite(latchLEDpin, HIGH);
  digitalWrite(buttonLEDpin, HIGH);

  unsigned int seed = analogRead(A0) * analogRead(A1);
  Serial.print("initialising the random generator with seed: ");
  Serial.println(seed);
  randomSeed(seed);

  if (! musicPlayer.begin()) { // initialise the music player
     Serial.println(
            F("Couldn't find VS1053, do you have the right pins defined?"));
     while (1);
  }
  Serial.println(F("VS1053 found"));
  
  SD.begin(CARDCS);    // initialise the SD card
  
  // Set volume for left, right channels. lower numbers == louder volume!
  if(digitalRead(buttonPin) == HIGH) {
    Serial.println("starting in EXTRA LOUD MODE");
    musicPlayer.setVolume(0,0);
    while(digitalRead(buttonPin) == HIGH) {
      digitalWrite(buttonLEDpin, LOW);
      digitalWrite(latchLEDpin, LOW);
      delay(20);
      digitalWrite(buttonLEDpin, HIGH);
      digitalWrite(latchLEDpin, HIGH);
      delay(20);
    }
  } else {
    musicPlayer.setVolume(20,20);
  }
  
  Serial.println(F("playing track 1 (track001.mp3) - Rrrrrooooonny!"));
  musicPlayer.playFullFile("track001.mp3");

  lastMillis = millis();
  totalMillis = millis();
}

/************/
/*---LOOP---*/
/************/
void loop() {
  if(millis() < totalMillis) { // overflow
    nextStartMillis = millis() + minutesBetweenTracks*60*1000;
    lastMillis = 0;
  }
  totalMillis = millis();
  int buttonState = digitalRead(buttonPin);
  if(buttonState == HIGH) {
    digitalWrite(buttonLEDpin, LOW);
    Serial.println("playing track 2 (track002.mp3)");
    musicPlayer.playFullFile("track002.mp3"); // play full song
    digitalWrite(buttonLEDpin, HIGH);
    nextStartMillis = millis() + minutesBetweenTracks*60*1000;
  }
  if(millis() >= nextStartMillis) { // play next track
    if(lastTrack >= numberOfTracks-1) {
      lastTrack = 1;
      generatePlaylist();
      printPlaylist();
    }    
    int track = lastTrack+1;
    char filename[12];
    sprintf(filename, "track%03d.mp3", playlist[track-2]);
    Serial.print("playing track ");
    Serial.print(playlist[track-2]);
    Serial.print(" (");
    Serial.print(filename);
    Serial.println(")");
    digitalWrite(buttonLEDpin, LOW);
    musicPlayer.playFullFile(filename);
    digitalWrite(buttonLEDpin, HIGH);
    lastTrack++;

    if(minutesBetweenTracks == 0) {
      minutesBetweenTracks = 1;
    } else if(minutesBetweenTracks < 24) {
      minutesBetweenTracks+=2;
    }
    Serial.print("next track in ");
    Serial.print(minutesBetweenTracks);
    Serial.println(" minutes");
    nextStartMillis = millis() + minutesBetweenTracks*60*1000;
    lastMillis = millis();
  }
  if(millis() > lastMillis + 5000) {
    lastMillis = millis();
    Serial.print("next track in ");
    Serial.print((nextStartMillis-millis())/1000);
    Serial.println(" seconds");
  }

  delay(50);
}

Diesen Sketch kann man nun auf den Arduino laden und alles sollte funktionieren. Nach der Programmierung lässt man nur noch die ganze Technik im Blumentopf verschwinden und man ist fertig.

20160522_230011

In bewegten Bildern kann man ihn sich hier ansehen:

2 Gedanken zu „Puppe zum Sprechen bringen mit Arduino Uno und Adafruit MP3 Shield

  1. Hallo Gerry,
    das ist ja eine tolle Beschreibung. Genau das was ich suche für mein Projekt von interaktiven Bildern.
    Diese Bilder reagieren auf den Betrachter: über einen Bewegungsmelder möchte ich verschiedene Texte und Geräusche von der SD Speicherkarte starten.
    Außerdem soll noch ein Bildteil mittels Servo kurz bewegt werden.
    Daneben soll natürlich die Möglichkeit bestehen das Gerät auch über Knopfdruck zu starten.
    Meine Frage ist: wie integriere ich den Bewegungsmelder und das Servo in den Schaltplan und in die Software? Ich bin so ziemlich Laie auf dem Gebiet des Arduino aber fasziniert von den Möglichkeiten. Erste Gehversuche habe ich bereits gemacht mit den üblichen Sketches und Beispielen , die ich alle zum Laufen gebracht habe. Sogar ein kleiner Roboter wetzt durch mein Wohnzimmer . Aber da war die Baubeschreibung und der Sketch schon vorgegeben.
    Es würde mich sehr freuen, wenn du mir da weiterhelfen könntest.

    1. Hallo Herbert, danke für deinen Kommentar!
      Deine Idee klingt wirklich sehr interessant! Wie du den Bewegungsmelder in den Schaltplan einbauen kannst, hängt natürlich vom Bewegungsmelder selbst ab. Da sollte dann eine Dokumentation dabei sein, wie man ihn beschalten sollte. Es wird allerdings vermutlich letzten Endes darauf hinauslaufen, dass du ihn an einen analogen Eingang des Arduinos hängst und über diesen Port dann eine Spannung auslesen wirst. In der Software wird die „Bewegungserkennung“ dann (so stelle ich es mir zumindest am einfachsten vor) in der Update-Methode ablaufen. Du vergleichst die aktuelle Eingangsspannung mit der vorherigen Eingangsspannung (die du dementsprechend in einer Variable zwischenspeicherst) und wenn eine Abweichung einer gewissen Größe festzustellen ist, dann bewegst du den Servo, spielst Musik ab, etc…
      So in etwa würde ich mir das ganze vorstellen. Du kannst gerne nochmal schreiben, ob du voran kommst! 🙂
      Liebe Grüße!

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.