Bislang (zu Teil 1, zu Teil 2) kam das TDMM noch nicht zu Wort, doch heute ist der große Tag gekommen: Es wird nun mit Ihnen sprechen. Spannung, Strom und Widerstand werden angesagt, wenn Sie möchten. Sie können wählen, welche Werte angezeigt und welche zusätzlich gesprochen werden.
Materialliste
Mini MP3 Player DFPlayer Master Module
2 x Schiebeschalter
Das DFPlayer DFR0299 Modul
Beginnen wir mit dem zentralen Modul, das uns die Sprachausgabe ermöglicht, dem DFPlayer.
Zu diesem Modul gibt es eine Menge an Informationen, u.a. ein ausführliches eBook von AZ. Dort werden verschiedene Schaltungsvarianten vorgestellt. Noch detaillierter ist das Hersteller-Wiki unter wiki.dfrobot.com. Auf dieser Seite sieht man, dass es zwei verschiedene DFPlayer gibt. Uns interessiert allein der DFR0299. Wer sich noch intensiver umschaut, sieht, dass es für diesen Player einige Lieferanten gibt und - vermutlich - auch unterschiedliche Firmware. Dazu später mehr.

Ursprünglich war das Modul wohl dafür gedacht, mp3-Files von einer eingesteckten SD-Karte abzuspielen. Dafür braucht man nur einige Taster anschließen und man hat einen MP3-Player. Das Modul bringt einen integrierten Verstärker mit. Lautsprecher und Tasten anschließen - fertig. Und das alles für ein paar wenige €uro.
Zusätzlich gibt es für beide Ausgangskanäle je einen DAC-Ausgang, so dass man einen Verstärker und zwei Lautsprecher anschließen kann. Die Tonqualität wird dadurch noch besser. Auch ein Kopfhörer oder Ohrhörer können verwendet werden. Diese „Stereoanlage“ ist wahrhaft kompakt.
Irgendwann kam wohl die Anforderung, den DFPlayer über eine serielle Schnittstelle zu steuern und so kam ein 9.600 Baud Interface hinzu. Über diesen Kanal werden wir den Player später mit der Bibliothek „SoftwareSerial“ steuern.
Der Player erleichtert uns seinen Einsatz auch dadurch, dass er in 5 V- und 3,3 V-Umgebungen eingesetzt werden kann, ohne dass wir einen Pegelwandler benötigen. In der Blogreihe zum „Sprechenden Farbdetektor“ hat Andreas Wolter gezeigt, wie man mit einem 1 k - Widerstand die Pegelanpassung vornimmt.
Praktische Erfahrungen
Dank der Library „Talking_Display“ von Gerald Lechner, kann der DFPlayer unkompliziert in jede Anwendung integriert werden, die sprechen soll. Dabei sind auf der Hardwareseite einige Punkte zu beachten, die nicht nur mir bei der Entwicklung des Gerätes aufgefallen sind:
• Einwandfreie Stromversorgung: Es ist wichtig, dass die Stromversorgung stabil und ausreichend leistungsfähig ist. Auf jeden Fall sollte so nahe wie möglich an den Stromversorgungspins des DFPlayer ein Tantalkondensator von 10 µF … 22 µF liegen (Polarität beachten!).
• Der Ruhestrom des DFPlayer beträgt ca. 25 mA, weil der integrierte Verstärker vom Typ „Klasse AB“ ist. Wenn das Gerät spricht, fließen in Abhängigkeit von der eingestellten Lautstärke bis zu 75 mA. Kommt die Stromversorgung von einem USB-Anschluss, der auch sonst schon viel zu tun hat, so sind Aussetzer möglich. Worte werden ausgelassen. Gute Erfahrungen habe ich mit einer Powerbank 2.600 mAh gemacht.
• Die Zugriffsgeschwindigkeit der SD-Karte, auf der die mp3-Files liegen, ist kritisch. Zuerst nutzte ich eine ältere Karte. Es fehlten dann manchmal einzelne Worte, was ich mir nicht erklären konnte. Tatsächlich konnte das Problem erst mit einer ausreichend schnellen SD-Karte behoben werden.
• Die mp3-Files können nicht beliebig angeordnet und umsortiert werden, weil der Player sie in der Reihenfolge sucht, in der sie aufgespielt wurden. Wenn sie später die SD-Karte mit den mp3-Files beladen, benennen sie die Files bitte nicht um oder sortieren sie neu. Das Fileformat ist FAT32.
• Wenn Sie selbst solche mp3-Files erzeugen möchten (z.B. in andereren Sprachen), können Sie so wie ich die Webseite voicebooking.com besuchen.
Wir erweitern die Hardware
Im praktischen Einsatz hat sich gezeigt, dass das TDMM nicht immer sprechen soll, sondern nur auf Wunsch. Also haben wir drei Schalter, mit denen wir die Sprachvorgaben einstellen:
1. Spannung ansagen - Schalter gegen Masse an D3
2. Strom ansagen - Schalter gegen Masse an D4
3. Widerstand ansagen - Schalter an A0 mit Spannungsteiler (siehe TDMM Teil 2)
Den errechneten Wert der Leistung lasse ich nicht ansagen. Wer das haben möchte, kann den Sketch mühelos entsprechend erweitern.
Es gibt außerdem eine Taste mit PullUp-Widerstand 1kΩ an D0, die man drückt, um die Ansage abzurufen. Diese Taste befindet sich am besten an der Prüfspitze, so dass man sich in aller Ruhe auf die Messung konzentrieren kann. Man drückt den Knopf und bekommt den Messwert angesagt.
Somit belegen wir mit diesem Teil der Logik insgesamt 3 + 1 Eingänge. Wir haben in Teil 2 schon gesehen, wie die Widerstandsmessung zugeschaltet wird. Wenn sie aktiviert ist, wird beim Druck auf die Sprechtaste immer auch der Widerstand angesagt.
Schauen wir uns an, wie das Breadboard jetzt ausschaut:

Sprachausgabe einrichten
Für die Sprachausgabe verwenden wir die Library „Talking_Display“ von Gerald Lechner. Ursprünglich wurde diese Library für die Ansage von Datum, Uhrzeit und Messwerten gebaut. Dazu passen die mp3-Dateien in deutscher und englischer Sprache, die auf der GitHub-Seite der Library angeboten werden. Für unser Projekt benötigen wir ein paar zusätzliche mp3-Files. Sie können das Gesamtpaket der Sprachdateien hier herunterladen.
Wie die Sprachausgabe in den Sketch integriert wird, zeigt der erweiterte Code, hier der Setup-Teil:
// TDMM - the talking digital multimeter - German speaking DMM
// Michael Klein & Bernd Schaedler 2024
//
// Thanks to Rob.Tillaart for: ADS1X15.h
// Thanks to Gerald Lechner for: Talking_Display.h
// Based on WEMOS D1 mini V3 - use board manager LOLIN(WEMOS) D1 mini Pro
//#include <ESP8266WiFi.h>
#include <ADS1X15.h>
#include <U8g2lib.h>
#include <SoftwareSerial.h>
#include <Talking_Display.h>
#define DFPLAYER_RX 12 //RX Anschluss für die Kommunikation mit dem DFPlayer
#define DFPLAYER_TX 13 //TX Anschluss für die Kommunikation mit dem DFPlayer
#define BUSY 14 //D5 Busy Leitung vom DFPlayer
SoftwareSerial ss(DFPLAYER_RX,DFPLAYER_TX); // RX, TX
Talking_Display<SoftwareSerial> td(ss,BUSY);
U8G2_SH1106_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/U8X8_PIN_NONE); u8g2_uint_t offset;
u8g2_uint_t width;
int32_t val_00 =0; int32_t val_01 =0; int32_t val_02=0; int32_t val_03=0;
float dcurrent = 0; float dcurrenty=0;
int32_t vres_00 =0; int32_t vres_01 =0; float resval=0; float resvolts_01 =0;
bool stateD0 = digitalRead(16); bool stateD3 = digitalRead(0);
bool stateD4 = digitalRead(2);
ADS1115 ADS(0x48);
void setup() {
Serial.begin(19200);
td.begin();
ss.setTimeout(2000);
td.setVolume(27);
Serial.println("Sprachausgabe bereit");
td.setEnglish(false);
td.setWordTimeout(2);
td.registerOnError(tdError);
pinMode(16,INPUT); // D0 for "speak"-Key
pinMode(2, INPUT_PULLUP); // D3 for "say-ampere" switch
pinMode(0, INPUT_PULLUP); // D4 for "say-volt" switch
pinMode(15, OUTPUT); //D8 for relay
Serial.println(__FILE__);
Serial.print("ADS1X15_LIB_VERSION: "); Serial.println(ADS1X15_LIB_VERSION);
Wire.begin(); // D1 mini V3 connect SDA > D2 (Pin 4) SCL > D1 (Pin 5)
ADS.begin();
if (!ADS.begin()) {
Serial.println("address ADS1115 or 0x48 not found");
}
if (!ADS.isConnected()) {
Serial.println("address 0x48 not found");
}
Serial.println("AD1115 is properly connected.");
u8g2.begin();
u8g2.enableUTF8Print();
startScreen(); // call for initial screen
} // end of setup
Wir betrachten hier nur den ersten Teil bis zum Ende des setup(). Wegen der Mittelwertbildung haben wir sämtliche Integervariablen, die bei der Messung eine Rolle spielen, auf int32_t umgestellt.
Es beginnt mit den #include und #define Vereinbarungen für <SoftwareSerial.h> , <Talking_Display> und die Definition der Pins RX = 12 und TX = 13, sowie BUSY =14 für den Player. Mit BUSY zeigt der Player an, dass er das letzte aufgerufene mp3 zu Ende abgespielt hat.
Es werden die Instanzen für beide Objekte gebildet. Außerdem legen wir noch die bool-Variablen für D0, D3 und D4 fest, an denen unsere Schalter und der Taster angeschlossen sind.
Es wird zuerst der Timeout für den Player festgelegt und die Lautstärke auf 27 festgelegt. Im Wertebereich 0 - 30 wählt man eine angenehme Einstellung. Danach folgt die Spracheinstellung auf Deutsch, der Timeout für ein einzelnes Wort und wir registrieren bei der Library den DFPlayer für seine Fehlermeldungen. Dazu gibt es auf der Herstellerseite eine passende Testroutine mit allen Fehlertypen.
Nachdem die pinModes der Digital-I/Os definiert sind, wird die ADS1115-Library initiiert und die Verbindung getestet, das Display angehängt und der Startscreen aufgerufen. Dort wird bereits das erste mp3-File abgerufen. Es hat die Nummer 362 und produziert den Marimba-Startsound:
void startScreen() {
u8g2.clearBuffer();
u8g2.setFont(u8g2_font_ncenB08_tr); // set fontsize
u8g2.setFontPosTop(); // set position
const char* title = "-- TDMM --";
const char* subtitle = "art-of-electronics.blog";
int xTitle = (128 - u8g2.getUTF8Width(title)) / 2; // title position + center
int yTitle = 10; // vertical position of title
int xSubtitle = (128 - u8g2.getUTF8Width(subtitle)) / 2; // subtitle + center
int ySubtitle = 40; // subtitle vertical position
u8g2.setDrawColor(1); // textcolor (1 = white)
u8g2.drawStr(xTitle, yTitle, title);
u8g2.drawStr(xSubtitle, ySubtitle, subtitle);
u8g2.sendBuffer(); // send display data
td.say(362); // Start tune
delay(3500);
}
Eine Besonderheit ist wichtig: Der Sketch verlangt, dass in der loop() ein Aufruf von td.loop() bei jedem Schleifendurchlauf erfolgt.
Wir haben in Teil 2 schon den Teil der Widerstandsmessung ausführlich besprochen. Die Spannungs- und Strommessung kennen wir noch aus Teil 1 dieser Beitragsserie. Tatsächlich ist der Sketch nicht viel umfangreicher geworden, denn die Library <Talking_Display> nimmt uns einen erheblichen Teil der Arbeit ab. Wir nutzen folgende Funktionen für die Sprachausgabe:
td.say(int) Sprich die Datei mit dieser Integer-Nummer
td.sayInt(int x) Sprich die Integerzahl x
Widerstandswerte werden ohne Nachkommastellen ausgegeben.
Somit bleibt die loop() recht übersichtlich:
void loop() {
ADS.setGain(0);
val_01=0;
for (int x=0; x<10; x++){ // measure voltage
val_00 = ADS.readADC_Differential_0_1();
val_01=val_01+val_00;
delay(2); // delay 2ms
}
val_01=val_01/10; // mean value of voltage
float volts_01 = ADS.toVoltage(val_01)*22.87; // multiplier
String voltsString = String(volts_01);
Serial.print("U: "); Serial.print(voltsString); Serial.print(" Volt");
val_02=0; dcurrent=0; dcurrenty=0; // measure current
for (int y=0; y<10; y++){
val_02 = ADS.readADC(2);
float volts_02 = ADS.toVoltage(val_02);
dcurrenty = (volts_02 - 2.396)*5.405; // multiplier for 0.185 mV/A
dcurrent=abs(dcurrent+dcurrenty*1.045); //factor 1.045: empirical correction
}
dcurrent=dcurrent/10; // mean value of current
String dcurrentx = String(dcurrent);
Serial.print("\t"); Serial.print("I: "); Serial.print(dcurrentx); Serial.print(" A");
u8g2.clearBuffer(); // show values on OLED
u8g2.setFont(u8g2_font_ncenB12_tf);
u8g2.setCursor(0, 20);
u8g2.print("U: ");u8g2.print(voltsString);u8g2.print(" V");
u8g2.setCursor(3, 35);
u8g2.print("I: ");u8g2.print(dcurrent);u8g2.print(" A");
u8g2.setCursor(2, 50);
u8g2.print("P: ");u8g2.print(abs(dcurrent*volts_01));u8g2.print(" W");
u8g2.sendBuffer();
Serial.print("\t"); Serial.print("P: "); Serial.print(dcurrent*volts_01);Serial.println(" W");
stateD0=digitalRead(16); stateD3=digitalRead(2); stateD4=digitalRead(0);
if (stateD0 == LOW && stateD3 == LOW) {
Serial.println("Volts speaking activated");// volt- & speak-key pressed?
td.sayFloat(volts_01,2);
delay(1500);
td.say(363);
}
if (stateD0 == LOW && stateD4 == LOW) { // amp- & speak-key pressed?
Serial.println("Ampere speaking activated");
delay(1000);
td.sayFloat(dcurrent,2);
delay(1500);
td.say(364);
}
stateD0 = HIGH; // set state back to HIGH
if (analogRead(0)>400){ // "resistor-switch set?"
resistor();
}
td.loop();
delay(1500);
} // end of loop
Praktische Umsetzung
Sobald Sie die Hardware komplett aufgebaut und den Sketch eingerichtet haben, empfehle ich nochmals eine eingehende Überprüfung. Unser Breadboard ist dicht bevölkert, alle Anschlüsse am D1 Mini sind genutzt. Die RX/TX-Verkabelung am DFPlayer muss stimmen, sonst rührt er sich nämlich nicht.
Beim Hochladen des Sketches sollen alle Schalter der Sprachausgabe ausgeschaltet sein.
Dann laden Sie bitte die mp3-Dateien auf eine FAT32 formatierte, leere SD-Karte. Danach kann es schon losgehen. Beim Starten hören Sie den Marimba-Sound und sehen die Anzeige „TDMM“ auf dem Display. Wenn sie so weit gekommen sind, sollte nicht mehr viel schiefgehen.
Für meinen Prototypen habe ich den „Sprech-Taster“ als Reedkontakt mit Magnetbedienung ausgebildet. Der dient nur zum Testen. Für den Tagesbetrieb nutze ich klassische Prüfleitungen. An der Plus-Prüfspitze habe ich einen kleinen Taster montiert, um die Sprachansage auszulösen.
Die beigefügten Videos zeigen das Timing der Sprachansage mit den gewählten Einstellungen. Die meisten Zuhörer fanden die Ansage klar und passend. Ein paar Ungeduldige wollten nicht so lange auf das gesprochene Ergebnis warten. Das ist Geschmackssache. Die Ansage kann vom Timing her jeder so einrichten, wie er möchte.
Manchmal kommt es vor, dass das TDMM ein Komma „verschluckt“. Auch dann bleibt die Ansage eindeutig. Er sagt dann z.B. nicht „zwanzig Komma fünf eins Volt“ sondern „zwanzig -Pause- fünf eins Volt“. Meistens funktioniert es gut. Manchmal kommt es auch bei mir vor, dass ein Aussetzer passiert. Ich will das nicht verschweigen, weil ich einige Mühe hatte, ein akzeptables Ergebnis zu erzielen. Die Videos zeigen Beispiele.
Gerald Lechner hat dankenswerter Weise eine alternative Library vorbereitet, die jedoch einen ESP32- Prozessor braucht. Die mp3s werden im Mini-Filesystem des ESP32 gespeichert. Damit braucht es keine Kommunikation mehr mit dem DFPlayer und es entstehen keine Wartezeiten.
Die Herausforderung werde ich annehmen, sobald die neue Library für den ESP32 online ist. Es wird dafür das gesamte TDMM ein wenig überarbeitet. Vielleicht treffe ich noch auf Schwachstellen im praktischen Betrieb. Schon in den letzten Monaten ist mir der Prototyp „ans Herz gewachsen“, weil er unkompliziert und ausgesprochen zuverlässig meine Anforderungen abdeckt. Das „neue Modell“ kommt dieses Jahr.
Sollten Sie die Videos an dieser Stelle nicht sehen, überprüfen Sie bitte die Cookieeinstellungen Ihres Browsers
Verbesserungen
Das TDMM hat zwei Erweiterungen erhalten, um den praktischen Betrieb zu vereinfachen. Für die Widerstandsmessung habe ich zwei Bananenstecker mit Krokodilklemmen verlötet, um eine einfache Messhilfe zu schaffen.

Wenn man die Sprachansage auslöst, werden alle ausgewählten Werte angesagt, zuletzt der Widerstandswert (ohne Nachkommastellen).
Der blaue Messwiderstand im Foto wurde mit einem 4-Leiter Präzisions- DVM vermessen. Ergebnis waren 3,32 Ω. Wie man auf dem Display erkennen kann, zeigt das TDMM 3,31 Ω. Mehrere Versuche mit anderen, niederohmigen Werten ergaben ähnlich genaue Werte.
Eine zweite Verbesserung betrifft die Prüfspitzen für die Spannungsmessung. Man erkennt am TDMM die schwarze Buchse dort, wo vorher der Einschalter war. Diese Buchse ist 4-polig und nimmt einen Klinkenstecker auf. Es ist dort der Sprechtaster angeschlossen (an der roten Prüfspitze). Die Prüfspitzen wurden mit den Buchsen für die Spannungsmessung verbunden.

Ausblick
Im nächsten Beitrag geht es um die Messung von Wechselspannungen, was sich als keine triviale Aufgabe herausstellen wird. Außerdem geht es um eine möglichst perfekte Stromversorgung für das TDMM, stabil, störungsarm und ausreichend leistungsfähig - wie immer erstellt mit Standardkomponenten.
Bis dahin,
Euer
Michael Klein