The following article was made by the guest author Bastian Brumbi made available:
After it in first part In this part, further modules are added to the basic functions. In addition to the components from the first part, the following products are required:
Hardware
Next we take a look at the hardware. Now put the new components on the Breadboard from the previous part and cable them as follows:
Microcontroller |
module |
D 4 |
Led ring - in |
A 4 |
OLED - SDA |
A 5 |
OLED - SCL |
software
Additional libraries must be installed for the LED ring and the OLED display. As usual, these can be installed via the integrated library manager or as a .zip file in the Arduino IDE:
Adafruit SSD1306
Adafruit neopixel
The goal is to display the date number on the display and the programming of the cards. The LED ring is intended to represent a progress bar and the status by changing the color. Since we cannot read this data through a command on the DFPlayer, we have to store it in the program. For this purpose, two arrays are initialized, in the first the duration of each individual file is given in seconds, in the second the name, which is to be displayed on the display. It should be noted that the positions are moved in the array, but must match the file name (001.mp3 = [0]). For reasons of the low storage space, the shortest possible names should be chosen without umlauts.
The associated libraries are still integrated for the display and the LED ring. For the display, the Wire Library, which is required for communication, and the Gfx Library integrated for the graphic calculations.
Copy or load the source code to the Arduino ID (Download):
#include <Arduino.h> #include <Spi.h> #include <Mfrc522.h> #include "DfrobotdfPlayermini.h" #include <Software.h> #include <Adafruit_neopixel.h> #include <Wire.h> #include <Adafruit_gfx.h> #include <Adafruit_ssd1306.h> #define Rst_pin 9 #define Ss_pin 10 #define Up 7 #define OK 6 #define Dwn 5
For the IIC address of the display, we define an additional symbolic constant here.
#define Screen_address 0x3c
Then the global variables and objects are created with the corresponding constructor. We take over some of the following methods from the first part.
Adafruit_neopixel ring(12, LED, Neo_grb + Neo_khz800); Adafruit_ssd1306 display(128, 32, &Wire, -1); Software softserial(2, 3); Dfrobotdfplayermini mydfplayer; Mfrc522 mfrc522(Ss_pin, Rst_pin); Mfrc522::Mibare_key key; Mfrc522::Status code status; byte sector = 1; // position in the memory byte blockadr = 4; byte data block[] = { // buffer to describe 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; byte buffer[18]; // buffer for reading byte trailer block = 7; Bool State = true; const intimately Filecount = 4; // !!! Number of files, please adjust !!!
In addition, the arrays are initialized for the title and the duration and the variables for determining the time played.
String title[Filecount] = {"TXT1", "Txt2", "Txt3", "Txt4"}; // title to be displayed intimately duration[Filecount] = {234, 231, 234, 123}; // duration in seconds intimately stop = 0; intimately start time = 0; intimately correction = 0; intimately Currfile = 0; byte Volume = 20; void readcard() { // Read the RFID Stack -> Buffer IF ( ! mfrc522.Picc_readcarderial()) return; // card cannot be read -> demolition byte size = Sizeof(buffer); status = (Mfrc522::Status code) mfrc522.PCD_authenticate(Mfrc522::Picc_cmd_mf_auth_Key_a, trailer block, &key, &(mfrc522.uid)); IF (status != Mfrc522::Status_ok) { Serial.print(F("PCD_authenticate () Failed:")); Serial.print(mfrc522.Get status code(status)); return; } status = (Mfrc522::Status code) mfrc522.MIFARE_READ(blockadr, buffer, &size); IF (status != Mfrc522::Status_ok) { Serial.print(F("MIFARE_READ () Failed:")); Serial.print(mfrc522.Get status code name(status)); } mfrc522.Picc_halta(); mfrc522.PCD_StopCrypto1(); } void writecard() { // Writing the DataBlock -> RFID Stack IF ( ! mfrc522.Picc_readcarderial()) return; // card cannot be read -> demolition status = (Mfrc522::Status code) mfrc522.PCD_authenticate(Mfrc522::Picc_cmd_mf_auth_Key_a, trailer block, &key, &(mfrc522.uid)); IF (status != Mfrc522::Status_ok) { Serial.print(F("PCD_authenticate () Failed:")); Serial.print(mfrc522.Get status code name(status)); return; } status = (Mfrc522::Status code) mfrc522.MIFARE_WRITE(blockadr, data block, 16); IF (status != Mfrc522::Status_ok) { Serial.print(F("MIFARE_WRITE () Failed:")); Serial.print(mfrc522.Get status code name(status)); } mfrc522.Picc_halta(); mfrc522.PCD_StopCrypto1(); }
This function is later called for displaying text on the display:
void display text(String text){ display.Clear display(); display.SettextSize(2); display.SettextColor(SSD1306_White); display.setcursor(5,10); display.print(text); display.display(); }
The StartCard () function largely corresponds to the old one, only that during the programming process of the file index and with a stored card, the stored card is displayed on the display. In addition, the ring shines blue during the description process of the card and after successful describing it is switched off again.
void startcard() { readcard(); // new card IF(buffer[0] != 1) { // marker not available mydfplayer.play(1); intimately I = 1; display text((String)I); while(true) { IF(!digital read(Up)) { while(!digital read(Up)) delay(20); IF(I < Filecount) { I+=1; mydfplayer.next(); } Else { I = 1; mydfplayer.play(1); } display text((String)I); } IF(!digital read(Dwn)) { while(!digital read(Dwn)) delay(20); IF(I > 1) { I-=1; mydfplayer.preview(); } Else { I = Filecount; mydfplayer.play(Filecount); } display text((String)I); } IF(!digital read(OK)) { // confirm and describe display text("Write"); while(!digital read(OK)) delay(20); data block[0] = 1; data block[1] = I; ring.fill(ring.Color(0, 0, 255), 0, 12); ring.show(); while(!mfrc522.Picc_isnewCardpresent()) delay(20); writecard(); display text((title[I - 1])); ring.fill(ring.Color(0, 0, 0), 0, 12); ring.show(); return; } } } // registered card Else { Currfile = buffer[1]; display text((title[Currfile - 1])); mydfplayer.play(Currfile); start time = Millis()/1000; correction = 0; } }
The following method represents the progress bar on the LED ring. At the beginning, the traffic jams of the DFPlayer are queried. If nothing is played, the method is ended and if not paused, the LEDs are switched off. After that, the time played is calculated and the number of LEDs that are supposed to light map () Function determined. In the end, the ring is dimmed or, if not yet the end of the file, the previously calculated number of LEDs is displayed.
void progress() { IF(mydfplayer.readstate() != 1) { IF(State == 1) { ring.fill(ring.Color(0, 0, 0), 0, 12); ring.show(); } return; // does not play -> demolition } for(intimately I=0; I<12; I++) { ring.setpixelcolor(I, ring.Color(0, 0, 0)); } intimately played = (Millis()/1000) - start time - correction; byte LED = map(played, 0, duration[Currfile - 1], 0, 13); IF(LED > 12) { display text("End"); display.stop(); for(intimately I = 255; I>=0; I--) { for(intimately n=0; n<12; n++) { ring.setpixelcolor(n, ring.Color(0, I, 0)); } ring.show(); delay(10); } LED = 0; } Else { for(intimately I=0; I<LED; I++) { ring.setpixelcolor(I, ring.Color(0, 255, 0)); } ring.show(); } }
The next function is the replacement for the previous button queries in Loop (). If no button is pressed, the end follows by a earlyreturn. While a button is pressed, the volume is increased or reduced in 2 steps. An orange circular sector is displayed according to the newly set volume.
void volume() { intimately LED; IF(digital read(Up) && digital read(Dwn)) return; for(intimately I=0; I<12; I++) { ring.setpixelcolor(I, ring.Color(0, 0, 0)); } while(!digital read(Up)) { IF(Volume<30) Volume += 2; mydfplayer.volume(Volume); LED = map(Volume, 1, 30, 0, 12); for(intimately I=0; I<LED; I++) { ring.setpixelcolor(I, ring.Color(255, 255, 0)); } ring.show(); delay(700); } while(!digital read(Dwn)) { IF(Volume>0) Volume -= 2; mydfplayer.volume(Volume); LED = map(Volume, 1, 30, 0, 12); for(intimately I=0; I<12; I++) { ring.setpixelcolor(I, ring.Color(0, 0, 0)); } for(intimately I=0; I<LED; I++) { ring.setpixelcolor(I, ring.Color(255, 255, 0)); } ring.show(); delay(700); } for(intimately I = 255; I>=0; I--) { for(intimately n=0; n<LED; n++) { ring.setpixelcolor(n, ring.Color(I, I, 0)); } ring.show(); delay(2); } }
In the set up() the starting functions are executed, the player initialized, the RFID sensor (exactly we im first part), the LED ring and the display.
void set up() { softserial.Begin(9600); Serial.Begin(115200); while(!mydfplayer.Begin(softserial, false, false)) { Serial.print(F("DfPlayer Unluke to Begin")); delay(1000); } Spi.Begin(); mfrc522.PCD_init(); for (byte I = 0; I < 6; I++) { key.keybye[I] = 0xff; } pin mode(Up, Input_pullup); pin mode(OK, Input_pullup); pin mode(Dwn, Input_pullup); pin mode(SW, Input_pullup); mydfplayer.volume(Volume); //0-30 ring.Begin(); ring.show(); ring.setbrightness(75); ring.fill(ring.Color(0, 0, 0), 0, 12); ring.show(); IF(!display.Begin(Ssd1306_switchcapvcc, Screen_address)) { Serial.print(F("SSD1306 Allocation Failed")); } display.Clear display(); display.display(); display text("Start"); }
In the Loop () Become the methods progress() and Volume () repeated permanently. When the middle button is pressed, the current time is saved in a variable, withdrawn from time when continuing and saved as a correction factor.
void loop() { IF (mfrc522.Picc_isnewCardpresent()) { // Card recognized Serial.print("Card recognized"); startcard(); } volume(); progress(); IF(!digital read(OK)) { while(!digital read(OK)) delay(20); // Waiting until button was let go IF(State) { mydfplayer.pause(); State = 0; stop = Millis()/1000; } Else { mydfplayer.start(); State = 1; correction += ABS(stop - (Millis()/1000)); } } IF(!State) { ring.fill(ring.Color(120, 0, 0), 0, 12); ring.show(); } }
Conclusion
In this part we added the following functions to the Tonuino set:
- Display of progress
- Relief in operation in programming the cards
- Improvement of the volume setting
- Representation of the volume on the LED ring
- Display of the title on the display
In the next part, the project will be built into a 3D printed housing and a Rotary Encoder is added as an alternative control of the volume.
Have fun recovery :)
7 commenti
Andreas Wolter
@Rudolf Gerlinger: der Nano wurde aus Platzgründen gewählt. Er hat aber (wie Sie schon sagten) weniger Programmspeicher. Der MEGA sollte genauso funktionieren. Sie müssen nur darauf achten, SDA und SCL korrekt anzuschließen. Auf dem Nano sind das A4 und A5. Der MEGA hat dafür separate Pins. Schauen Sie dafür in das Pinout.
Eventuell müssen Sie auch für die restlichen Komponenten andere Pins verwenden. Dann müssen Sie das im Quellcode umschreiben.
Grüße,
Andreas Wolter
AZ-Delivery Blog
Rudolf Gerlinger
Hallo an die Spezialisten,
kann ich auch den Arduino Mega nehmen, er hat wohl mehr Speicherplatz als ein Nano
Kann ich die Programmierung vom Nano übernehmen.
Mit besten Dank
Rudolf
Konrad
Hi,
das mit den Titeln habe ich inzwischen herausgefunden.
Man muss die Titel einzeln hochkopieren. Dann klappt es.
@Andreas: Das mit den Track auf einmal habe ich ziemlich oft geprüft und hat nicht geklappt.
Danke trotzdem für die Geduld.
Konrad
Andreas Wolter
@Konrad: ist die Reihenfolge genau um 1 verschoben, oder sind die Titel komplett durcheinander?
Bastian Brumbi hatte im Text darauf hingewiesen, dass alle Tracks zusammen auf die Karte kopiert werden müssen.
Grüße,
Andreas Wolter
AZ-Delivery Blog
Konrad
Hallo nochmal, diesmal in diesem Blog.
Kurze Info: Ich habe das Gehäuse aus Blog 3 ausgedruckt und Alles zusammen hingebracht; – halt ohne Encoder (siehe meine Probleme im Kommentar Blog3) mit dem Code komplett aus Blog 2. Bis hier läuft technisch Alles einwandfrei.
Vielen Dank an den Autoren! Mein Enkel wird es danken und die Box sieht echt toll aus.
Eine komische Erfahrung musste ich aber machen:
Die Titel habe ich auf die SD-Karte wie beschreiben kopiert (001.mp3, 002.mp3, etc.). Im Code habe ich dann, – wie oben beschreiben die
String titel[FileCount] = {"1- Logical", “2- Breakfast”, “…….”};
geändert (Wie man erkennen kann: Lieder von Supertramp).
Den FileCount habe ich auf 12 gesetzt und in den Arrays geachtet, dass es auch 12 Inhalte gibt.
Problem: Die Liednummerierung passt nicht beim Abspielen. So ist z. B. Lied “2-Breakfast” (angezeigt im Display), plötzlich z.B. der Logical Song.
Warum ist das so?
Das Ganze mühsam raushören und dann im Code neu eingeben ist ziemlich mühsam.
Was mache ich da falsch?
Danke schon mal und ich hoffe ich belästige den Auto nicht so. Bin gerade Strohwitwer und kann mich also austoben.
Konrad
Bastian Brumbi
Vielen Dank für das ausführliches Feedback und die wertvollen Hinweise!
Ich habe mir die angesprochenen Punkte noch einmal genau angeschaut und die erwähnten Fehler behoben.
Dieses Projekt soll das bisherige Tonuino Projekt erweitern, sodass auch Einsteiger das Programm nachvollziehen können. Falls sie die Wiedergabefunktionen des Offiziellen Tonuino Projekts (von Thorsten Voss) benötigen, können sie einen Blick auf dieses Werfen, welches jedoch deutlich komplexer ist.
Nochmals danke für das Feedback; Ich hoffe, dass die Verbesserungen helfen werden!
Grüße,
Bastian Brumbi
Bernd-Steffen
Hallo zusammen, die vorgestellten Erweiterungen zum TonUINO von Thorsten Voss sind ganz nett für die Erleichterung der Bedienung (Nr. der Wiedergabedatei oder Lautstärke-Niveau anzeigen) allerdings vermisse ich die vielfältigen Funktionen (z.B. freie Wahl eines Ordners, Wiedergabemodus wie Hörbuch-, Hörspiel- oder Party-Modus, Admin-Funktionen) des o.a. Projektes! Der Autor hätte auch in der Beschreibung darauf hinweisen können, dass die Anzahl der wiederzugebeneden Dateien fix im Sketch einzugeben sind, ebenso die zugehörigen Texte und Dauer der Wiedergabe. Das empfinde ich als schwerwiegende Einschränkung gegenüber der Voss-Version! Außerdem sind einige Fehler im Sketch (auch in der Download-Datei aus der Cloud!) enthalten: die Konstanten LED (s. Zeile 22) und SW (s. Zeile 256) sind nicht definiert und führen zu Fehlern beim Compilieren. Im Sketch des 3. Teils sind die Definitionen dann vorhanden, (#define LED 4 und #define SW 8)
Ansonsten ein schöne Erweiterung – super wäre, wenn die genannten Funktionen des Voss-Projektes mit den hier vorgestellten Erweiterungen kombiniert würden. Auch sollte dieses Projekt als Quelle erwähnt werden!
Viele Grüße von Bernd-Steffen