TonUINO Set erweitern - Teil 2 - AZ-Delivery

Der folgende Beitrag wurde uns von dem Gastautoren Bastian Brumbi zur Verfügung gestellt:

Nachdem es im ersten Teil um die Grundfunktionen gegangen ist, werden in diesem Teil weitere Module hinzugefügt. Es werden zusätzlich zu den Bauteilen aus dem ersten Teil folgende Produkte benötigt:

Hardware

Als Nächstes werfen wir einen Blick auf die Hardware. Stecken Sie nun die neuen Bauteile auf das Breadboard aus dem vorherigem Teil und verkabeln diese wie folgt:

Mikrocontroller

Modul

D 4

LED Ring - IN

A 4

OLED - SDA

A 5

OLED - SCL

Software

Für den LED Ring und das OLED Display müssen noch zusätzliche Bibliotheken installiert werden. Diese können wie gewohnt über den integrierten Library-Manager oder als .zip Datei in der Arduino IDE installiert werden:

Adafruit SSD1306
Adafruit NeoPixel

Das Ziel ist es auf dem Display den aktuellen Titel und bei der Programmierung der Karten die Dateinummer anzuzeigen. Der LED-Ring soll einen Fortschrittsbalken und den Status durch Farbänderung darstellen. Da wir diese Daten nicht durch einen Befehl am DFPlayer auslesen können, müssen wir sie im Programm hinterlegen. Hierfür werden zwei Arrays initialisiert, in das erste wird die Dauer jeder einzelnen Datei in Sekunden angegeben, im zweiten der Name, welcher auf dem Display angezeigt werden soll. Zu beachten ist, dass die Positionen im Array verschoben sind, aber mit der Dateibenennung übereinstimmen müssen (001.mp3 = [0]). Aus Gründen des geringen Speicherplatzes sollten möglichst kurze Namen ohne Umlaute gewählt werden.

Für das Display und den LED-Ring werden noch die zugehörigen Libraries eingebunden. Für das Display wird zusätzlich noch die Wire library, welche für die Kommunikation benötigt wird, und die GFX Library für die grafischen Berechnungen eingebunden.

Kopieren oder laden Sie den Quellcode in die Arduino IDE (Download):

#include <Arduino.h>
#include <SPI.h>
#include <MFRC522.h>
#include "DFRobotDFPlayerMini.h"
#include <SoftwareSerial.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

Für die IIC Adresse des Displays definieren wir hier noch eine zusätzliche symbolische Konstante.

#define SCREEN_ADDRESS 0x3C

Danach werden die globalen Variablen und Objekte mit dem entsprechenden Konstruktor erstellt. Einige der folgenden Methoden übernehmen wir aus dem ersten Teil.

Adafruit_NeoPixel ring(12, LED, NEO_GRB + NEO_KHZ800);
Adafruit_SSD1306 display(128, 32, &Wire, -1);
SoftwareSerial softSerial(2, 3);
DFRobotDFPlayerMini myDFPlayer;

MFRC522 mfrc522(SS_PIN, RST_PIN);
MFRC522::MIFARE_Key key;
MFRC522::StatusCode status;

byte sector         = 1; //Position im Speicher
byte blockAddr      = 4; 
byte dataBlock[]    = { //Buffer zum beschreiben
    0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00
};
byte buffer[18]; //Buffer zum lesen
byte trailerBlock = 7;
bool State = true;
const int FileCount = 4; //!!!Anzahl an Dateien, bitte anpassen!!!

Zusätzlich werden noch die Arrays für den Titel und die Dauer initialisiert und auch die Variablen für die Ermittlung der abgespielten Zeit.

String titel[FileCount] = {"txt1", "txt2", "txt3", "txt4"}; //anzuzeigender Titel
int duration[FileCount] = {234, 231, 234, 123}; //Dauer in Sekunden

int stopTime = 0;
int startTime = 0;
int correctionTime = 0;
int currFile = 0;

byte Volume = 20;


void readCard() { //Lesen des RfID Stacks -> Buffer
  if ( ! mfrc522.PICC_ReadCardSerial())
    return; //Karte kann nicht gelesen werden -> Abbruch

  byte size = sizeof(buffer);
  status = (MFRC522::StatusCode) mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, trailerBlock, &key, &(mfrc522.uid));
  if (status != MFRC522::STATUS_OK) {
    Serial.print(F("PCD_Authenticate() failed: "));
    Serial.println(mfrc522.GetStatusCodeName(status));
    return;
  }
  status = (MFRC522::StatusCode) mfrc522.MIFARE_Read(blockAddr, buffer, &size);
  if (status != MFRC522::STATUS_OK) {
      Serial.print(F("MIFARE_Read() failed: "));
      Serial.println(mfrc522.GetStatusCodeName(status));
  }
  mfrc522.PICC_HaltA();
  mfrc522.PCD_StopCrypto1();
}

void writeCard() { //schreiben des dataBlock -> RfID Stack
  if ( ! mfrc522.PICC_ReadCardSerial())
    return; //Karte kann nicht gelesen werden -> Abbruch

  status = (MFRC522::StatusCode) mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, trailerBlock, &key, &(mfrc522.uid));
  if (status != MFRC522::STATUS_OK) {
    Serial.print(F("PCD_Authenticate() failed: "));
    Serial.println(mfrc522.GetStatusCodeName(status));
    return;
  }
  status = (MFRC522::StatusCode) mfrc522.MIFARE_Write(blockAddr, dataBlock, 16);
  if (status != MFRC522::STATUS_OK) {
      Serial.print(F("MIFARE_Write() failed: "));
      Serial.println(mfrc522.GetStatusCodeName(status));
  }
  mfrc522.PICC_HaltA();
  mfrc522.PCD_StopCrypto1();
}

Diese Funktion wird später für das anzeigen von Text auf dem Display aufgerufen:

void displayText(String text){
  display.clearDisplay();
  display.setTextSize(2);
  display.setTextColor(SSD1306_WHITE);
  display.setCursor(5,10);
  display.println(text);
  display.display();
}

Die Funktion startCard() entspricht weitgehend der alten, nur dass während des Programmiervorgangs der Dateiindex und bei einer gespeicherten Karte der hinterlegte Name auf dem Display angezeigt werden. Außerdem leuchtet während des Beschreibevorgangs der Karte der Ring blau und nach erfolgreichem Beschreiben wird dieser wieder ausgeschaltet.

void startCard() {
  readCard();
  //neue Karte
  if(buffer[0] != 1) { //Marker nicht vorhanden
    myDFPlayer.play(1);
    int i = 1;
    displayText((String)i);
    while(true) {
      if(!digitalRead(UP)) {
        while(!digitalRead(UP)) delay(20);
        if(i < FileCount) {
          i+=1;
          myDFPlayer.next();
        }
        else {
          i = 1;
          myDFPlayer.play(1);
        }
        displayText((String)i);
      }
      if(!digitalRead(DWN)) {
        while(!digitalRead(DWN)) delay(20);
        if(i > 1) {
          i-=1;
          myDFPlayer.previous();
        }
        else {
          i = FileCount;
          myDFPlayer.play(FileCount);
        }
        displayText((String)i);
      }
      if(!digitalRead(OK)) { //bestätigen und beschreiben
        displayText("Schreiben");
        while(!digitalRead(OK)) delay(20);
        dataBlock[0] = 1;
        dataBlock[1] = i;
        ring.fill(ring.Color(0, 0, 255), 0, 12);
        ring.show();
        while(!mfrc522.PICC_IsNewCardPresent()) delay(20);
        writeCard();
        displayText((titel[i - 1]));
        ring.fill(ring.Color(0, 0, 0), 0, 12);
        ring.show();
        return;
      }
    }
  }
  //registrierte Karte
  else {
    currFile = buffer[1];
    displayText((titel[currFile - 1]));
    myDFPlayer.play(currFile);
    startTime = millis()/1000;
    correctionTime = 0;
  }
  
}

Die folgende Methode stellt den Fortschrittsbalken auf dem LED-Ring dar. Zu Beginn wird der Staus des DFPlayers abgefragt. Falls nichts abgespielt wird, wird die Methode beendet und wenn nicht pausiert wurde, werden die LEDs ausgeschaltet. Danach wird die gespielte Zeit berechnet und daraus die Anzahl an LEDs, die leuchten sollen, über die map() Funktion bestimmt. Am Ende wird der Ring gedimmt oder, falls noch nicht das Ende der Datei erreicht worden ist, wird die zuvor berechnete Anzahl an LEDs angezeigt.

void progress() {
  if(myDFPlayer.readState() != 1) {
    if(State == 1) {
      ring.fill(ring.Color(0, 0, 0), 0, 12);
      ring.show();
    }
    return; //Spielt nicht ab -> Abbruch
  }
  for(int i=0; i<12; i++) {
    ring.setPixelColor(i, ring.Color(0, 0, 0));
  }
  int played = (millis()/1000) - startTime - correctionTime;
  byte Led = map(played, 0, duration[currFile - 1], 0, 13);
  
  if(Led > 12) {
    displayText("Ende");
    display.stopscroll();
    for(int i = 255; i>=0; i--) {
      for(int n=0; n<12; n++) {
        ring.setPixelColor(n, ring.Color(0, i, 0));
      }
      ring.show();
      delay(10);
    }
    Led = 0;
  }
  else {
    for(int i=0; i<Led; i++) {
      ring.setPixelColor(i, ring.Color(0, 255, 0));
    }
    ring.show();
  }
}

Die nächste Funktion ist der Ersatz für die früheren Tasterabfragen im loop(). Wenn kein Taster gedrückt wird, folgt das Beenden durch ein earlyreturn. Während ein Taster gedrückt wird, wird die Lautstärke in 2er Schritten erhöht oder verringert. Entsprechend der neu eingestellten Lautstärke wird ein orangener Kreissektor angezeigt.

void volume() {
  int Led;
  if(digitalRead(UP) && digitalRead(DWN)) return;
  for(int i=0; i<12; i++) {
    ring.setPixelColor(i, ring.Color(0, 0, 0));
  }
  while(!digitalRead(UP)) {
    if(Volume<30) Volume += 2;
    myDFPlayer.volume(Volume);
    Led = map(Volume, 1, 30, 0, 12);
    for(int i=0; i<Led; i++) {
      ring.setPixelColor(i, ring.Color(255, 255, 0));
    }
    ring.show();
    delay(700);
  }
  while(!digitalRead(DWN)) {
    if(Volume>0) Volume -= 2;
    myDFPlayer.volume(Volume);
    Led = map(Volume, 1, 30, 0, 12);
    for(int i=0; i<12; i++) {
      ring.setPixelColor(i, ring.Color(0, 0, 0));
    }
    for(int i=0; i<Led; i++) {
      ring.setPixelColor(i, ring.Color(255, 255, 0));
    }
    ring.show();
    delay(700);
  }
  for(int i = 255; i>=0; i--) {
    for(int n=0; n<Led; n++) {
      ring.setPixelColor(n, ring.Color(i, i, 0));
    }
    ring.show();
    delay(2);
  }
}

Im setup() werden die Startfunktionen ausgeführt, der Player initialisiert, der RFID Sensor (genau wir im ersten Teil), der LED-Ring und das Display.

void setup() {
    softSerial.begin(9600);
    Serial.begin(115200);

  while(!myDFPlayer.begin(softSerial, false, false)) {
    Serial.println(F("DFPlayer unable to begin"));
    delay(1000);
  }
  
  SPI.begin();
  mfrc522.PCD_Init();

  for (byte i = 0; i < 6; i++) {
      key.keyByte[i] = 0xFF;
  }
  pinMode(UP, INPUT_PULLUP);
  pinMode(OK, INPUT_PULLUP);
  pinMode(DWN, INPUT_PULLUP);
  pinMode(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.println(F("SSD1306 allocation failed"));
  }
  
  display.clearDisplay();
  display.display();

  displayText("Start");
}

Im loop() werden die Methoden progress() und volume() dauerhaft wiederholt. Wenn der mittlere Taster gedrückt wird, wird die aktuelle Zeit in einer Variable gespeichert, beim Fortsetzen von der Zeit abgezogen und als Korrekturfaktor gespeichert.

void loop() {
  if (mfrc522.PICC_IsNewCardPresent()) { //Karte erkannt
    Serial.println("Karte erkannt");
    startCard();
    
  }
  volume();
  progress();
  if(!digitalRead(OK)) {
    while(!digitalRead(OK)) delay(20); //Warten bis Taster losgelassen wurde
    if(State) {
      myDFPlayer.pause();
      State = 0;
      stopTime = millis()/1000; 
    }
    else {
      myDFPlayer.start();
      State = 1;
      correctionTime += abs(stopTime - (millis()/1000));
    }
  }
  if(!State) {
    ring.fill(ring.Color(120, 0, 0), 0, 12);
    ring.show();
  }
}

Fazit

In diesem Teil haben wir folgende Funktionen zum TonUINO-Set ergänzt:

  • Anzeige des Fortschritts
  • Erleichterung in der Bedienung bei der Programmierung der Karten
  • Verbesserung der Lautstärkeeinstellung
  • Darstellung der Lautstärke auf dem LED-Ring
  • Anzeige des Titels auf dem Display

Im nächsten Teil wird das Projekt zum Abschluss in ein 3D gedrucktes Gehäuse gebaut und ein Rotary Encoder als alternative Steuerung der Lautstärke hinzugefügt.

Viel Spaß beim Nachbauen :)

DisplaysFür arduinoProjekte für anfänger

Laat een reactie achter

Alle opmerkingen worden voor publicatie gecontroleerd door een moderator

Aanbevolen blogberichten

  1. ESP32 jetzt über den Boardverwalter installieren - AZ-Delivery
  2. Internet-Radio mit dem ESP32 - UPDATE - AZ-Delivery
  3. Arduino IDE - Programmieren für Einsteiger - Teil 1 - AZ-Delivery
  4. ESP32 - das Multitalent - AZ-Delivery