TonUINO Set erweitern - Teil 3 - AZ-Delivery

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

Nachdem wir im zweiten Teil die Bedienung aus visueller Perspektive verbessert haben, werden wir nun für die einfachere Bedienung einen Rotary-Encoder hinzufügen, um die Lautstärke einzustellen.

Hardware

Folgende Produkte werden aus den vorherigen Teilen benötigt:

Zusätzlich werden noch benötigt:

An Werkzeug brauchen Sie einen Lötkolben mit Zubehör und einen 3D-Drucker.

Damit der Rotaryencoder zuverlässig funktioniert, müssen der CLK und DIR Pin an einem Interrupt-Pin des Mikrocontrollers angeschlossen sein. Unser Nano Mikrocontroller verfügt über zwei Interrupt-fähige Pins, D2 und D3.

Als Erstes löten wir zurechtgeschnittene Buchsenleisten für den Mikrocontroller und das MP3-Modul auf das PCB, gefolgt von Stiftleisten für die externen Bauteile.

Die gelben Leitungen werden unter dem PCB verlötet und die orangen auf der Oberseite.
Kontrollieren Sie, bevor Sie die Schaltung in Betrieb nehmen, dass keine Kurzschlüsse durch das Löten auf dem Lochraster entstanden sind.

Als Nächstes schließen wir die anderen Bauteile mit Dupontkabeln an. Damit die Verkabelung ordentlicher wird, ist es empfehlenswert, die Kabel in der richtigen Länge selbst zu krimpen. Wenn Sie über kein entsprechendes Werkzeug verfügen, können Sie auch die vorgekrimpten F2F Kabel benutzen.

Für die Übersichtlichkeit sind alle Spannungs- und Masseverbindungen oben nicht direkt eingezeichnet, diese müssen auf dem PCB oben rechts mit den drei Stiftleisten verbunden werden. Das RFID Modul benötigt eine Betriebsspannung von 3.3V, hierfür befindet sich unten links eine Stiftleiste.

Für das Gehäuse müssen die beiden Dateien (Gehäuse 1, Gehäuse 2) mit einem 3D-Drucker ausgedruckt werden. Das Teil mit dem großem ovalen Loch muss in Weiß oder einer anderen Lichtdurchlässigen Farbe gedruckt werden, da der LED Ring dahinter befestigt wird.

Wenn die beiden Teile gedruckt wurden, werden als Nächstes mit einem heißen Lötkolben die Gewindeeinsätze in die größeren 6 Löcher gepresst. Zum Ende werden alle Komponenten mit entsprechenden (M2 oder M3) Schrauben befestigt.

Software

Folgende Libraries müssen installiert sein:

MFRC522
DFRobot DFPlayerMini
Adafruit SSD1306
Adafruit NeoPixel

Zusätzlich zu den Libraries aus den vorherigen Teilen benötigen wir nun noch die Encoder library.

Die Libraries können wie gewohnt über den integrierten Library-Manager oder als .zip Datei in der Arduino IDE installiert werden.

Kopieren oder laden Sie nun den folgenden Code in die Arduino IDE, wählen Sie das richtige Board und Port aus und laden Sie das Programm durch Drücken des Upload Buttons auf das Board (Download).

Als erstes werden die verwendeten Bibliotheken eingebunden.

#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>
#include <Encoder.h>

Als Nächstes werden symbolische Konstanten, die Objekte der Libraries erstellt und globale Variablen erstellt. Einige der nachfolgenden Methoden werden aus den vorangegangenen Blogbeiträgen übernommen.

#define RX 15
#define TX 14
#define LED 4
#define DWN 5
#define OK 6
#define UP 7
#define RST_PIN 9
#define SS_PIN 10
#define CLK 2
#define DT 3
#define SW 8

#define SCREEN_ADDRESS 0x3C

Encoder encoder(DT,CLK);
Adafruit_NeoPixel ring(12, LED, NEO_GRB + NEO_KHZ800);
Adafruit_SSD1306 display(128, 32, &Wire, -1);
SoftwareSerial softSerial(RX, TX);
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!!!

Die Arrays titel und duration müssen wie im vorherigen Teil mit den aktuellen Daten gefüllt werden.

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

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

byte Volume = 20;
long Position;


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();
}

Folgende Methode wurde noch um das Scrollen des Textes nach rechts erweitert:

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

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;
  }
  
}

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();
  }
}

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);
  }
}

Die Funktion volumeRot() liest die Position des Rotary Encoders aus und verändert die Lautstärke entsprechend der Drehrichtung. Die Lautstärke wird dabei wie gewohnt auf dem LED-Ring abgebildet.

void volumeRot() {
  if (encoder.readAndReset() == 0 || digitalRead(SW)) return;
  int Led = 0;
  while(true) {
    long Position = encoder.readAndReset();
    if(Position > 0 && Volume <= 28) Volume += 1;
    else if(Position < 0 && Volume >= 2) Volume -= 1;
    myDFPlayer.volume(Volume);
    delay(250);
    Serial.println(Volume);
    Led = map(Volume, 1, 30, 0, 12);
    ring.fill(ring.Color(0,0,0),0,12);
    for(int i=0; i<Led; i++) {
      ring.setPixelColor(i, ring.Color(255, 255, 0));
    }
    ring.show();
    if(digitalRead(SW)) break;
  }

  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);
  }
}

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");
  display.stopscroll();
  Serial.println("Start");
}

Der Aufruf für den Rotary Encoder wird in der loop() am Ende ergänzt und damit zyklisch aufgerufen.

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();
  }
  volumeRot();
}

Der Drehencoder erfüllt die gleiche Funktion wie die äußeren Taster. Wenn Sie den Encoder nicht verwenden wollen, können Sie den Funktionsaufruf im loop() löschen.

Am Ende werden die beiden 3D-gedruckten Teile noch verschraubt. Unser Projekt ist damit komplett.

Bedienung

Legen Sie eine NFC Karte auf die Oberseite der Box, wenn die Karte bereits beschrieben ist, wird die hinterlegte Datei direkt abgespielt. Wenn eine formatierte Karte aufgelegt wird, können Sie über die äußeren Taster die Dateinummer auswählen. Wenn Sie die richtige Datei gefunden haben, können Sie über Drücken des mittleren Tasters die Nummer auf die Karte schreiben. Entfernen Sie die Karte und legen Sie diese wieder auf. Wenn der Ring nicht mehr blau leuchtet, ist die Karte beschrieben und kann ab sofort verwendet werden. Um die Lautstärke einzustellen, können Sie entweder die äußeren Taster drücken, oder den Rotary Encoder gedrückt halten und dabei drehen. Beim Drücken des mittleren Tasters wird die Datei pausiert.

Viel Spaß beim Nachbauen :)

Alle benötigten Dateien noch einmal im Überblick:

Sketch: TonUINO

3D-Druck: Gehäuse 1, Gehäuse 2

DisplaysFür arduinoProjekte für anfänger

Kommentar hinterlassen

Alle Kommentare werden von einem Moderator vor der Veröffentlichung überprüft

Empfohlene Blogbeiträge

  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