/**************************************************************************************************
* DCC MP3 Decoder
* Anzahl MP3/Files, DCC-Adressen: 40
*
* SD-Karte files \mp3\0001.mp3 => Hintergrund
* 0002.mp3 - 0040.mp4 => Zugansagen
*
* DCC Verarbeitung by https://pgahtow.de/w/DCC_Dekoder
*************************************************************************************************/
//include the library code:
#include <TimerOne.h>
#include <EEPROM.h>
#include <DFMiniMp3.h>
#include "SoftwareSerial.h"
class Mp3Notify;
//-----------------------------------------------------------------------------------
// IO-Ports
//-----------------------------------------------------------------------------------
const int dccPin = 2; //nur auf Pin 2 geht die attachInterrupt Funktion!
#define Led_Adr 11
#define Led_Test 12
#define Led_Rt_Ge 6
#define Led_Ge_Rt 7
#define Taste_Laut 8
#define Taste_Leise 9
#define Taste_Adresse A0
//-----------------------------------------------------------------------------------
//Variablen für DCC Erkennung
//-----------------------------------------------------------------------------------
int countone = 0; //Zähle die high so das man eine Präambel erkennen kann (min. 10 high)
boolean getdata = false; //Es werden DCC Daten gelesen und im Array data abgespeichert
boolean dataReady = false; //Es wurden DCC Daten vollständig eingelesen
byte data[10]; //eingelesene Byte-Werte nach einer Präambel
byte datalength = 0; //Position in data wo gerade der nächste Wert hinkommt
byte countbit = 0; //Zähle Bits, nach 8 Bit wurde ein Byte gelesen und setzte dann die Variable auf 0
byte dxor = 0; //xor Prüfsumme
int dccAdr = 0; //Adresse
byte dccZustand; //0:aus, 1: aktiv
byte dccType = 0xFF; //Art der DCC Daten (Long, Short, Accessory)
//---------------------------------------------------------------------------------------------------------------
//Timer (über Millis)
//---------------------------------------------------------------------------------------------------------------
unsigned long timer;
uint16_t timerIntervall = 100; // 1/10Sekunde
int TimeOut[4]; //Timeouttabelle [Dauer in 1/10 Sekunden]
//---------------------------------------------------------------------------------------------------------------
//DCC-Adressen
//---------------------------------------------------------------------------------------------------------------
int DCC_Portadressen[41]; // Adressenspeicher
byte BetriebsMode; //0: Betrieb; 1:Learning DCC-Adressen; 2: Laut-Leise-Modus
//---------------------------------------------------------------------------------------------------------------
//MP3
//---------------------------------------------------------------------------------------------------------------
SoftwareSerial secondarySerial(4, 5); // RX, TX
typedef DFMiniMp3<SoftwareSerial, Mp3Notify> DfMp3;
DfMp3 dfmp3(secondarySerial);
uint16_t Anzahl_files;
int Mp3_Master_Volume;
int Mp3_File_Volume;
bool Sound0001_on; //Ist der Umgebungssound eingeschaltet
//Variablen zu Betriebsmode 2
byte akt_file;
bool akt_file_abspielen;
//---------------------------------------------------------------------------------------------------------------
//Timer (über Millis)
//---------------------------------------------------------------------------------------------------------------
byte LED_DB[6][3];
byte Tasten_DB[5][3];
/*************************************************************************************
* Setup
*************************************************************************************/
void setup(){
;
Serial.begin(9600);
;
//Pinmodes
;
pinMode(Led_Test, OUTPUT);
pinMode(Led_Adr, OUTPUT);
pinMode(Led_Rt_Ge, OUTPUT);
pinMode(Led_Ge_Rt, OUTPUT);
;
pinMode(Taste_Laut, INPUT);
pinMode(Taste_Leise, INPUT);
pinMode(Taste_Adresse, INPUT);
;
//Interrupt
attachInterrupt(0, dccdata, RISING); //ISR für den Dateneingang
;
//Timer1 zum bestimmen der Impulslängen des DCC Signals
Timer1.initialize(70); // set a timer of length 70 microseconds
Timer1.attachInterrupt(dcctime); // attach the service routine here
Timer1.stop(); // Timer anhalten
;
//Laden der DCC-Adressen
loadDccfromEeprom();
;
//Fertig
digitalWrite(Led_Test,HIGH);digitalWrite(Led_Adr,HIGH);
delay(1000);
digitalWrite(Led_Test,LOW);digitalWrite(Led_Adr,LOW);
;
//Init fertig
Serial.println("**************************************************");
Serial.println(" DCC-MP3-Decoder gestartet");
printDccAdr();
Serial.println("**************************************************");
dfmp3.begin();
delay(500);
dfmp3.reset();
delay(500);
Mp3_Master_Volume = EEPROM.read(3);
if(Mp3_Master_Volume >30){Mp3_Master_Volume=0;}
;
Anzahl_files = dfmp3.getTotalTrackCount(DfMp3_PlaySource_Sd);
Serial.println("Anzahl Mp3-Files: "+ String(Anzahl_files));
Serial.println("**************************************************");
}
/*************************************************************************************
* Loop
*************************************************************************************/
void loop(){
//Betriebsmode: WORK-----------------------------------
if(BetriebsMode == 0){
//Taster Adresse Lernen
if(Tasten_DB[1][2]==1){
BetriebsMode = 1;
TimeOut[1] = 100; //Timeout 10Sekunden
LED_Setzen(4, 3); //Blink Slow Gelb
}
//Taster Lautstärke lernen
if(Tasten_DB[2][2]==1 && Tasten_DB[3][2]==1){
digitalWrite(Led_Adr,HIGH);
BetriebsMode = 2;
akt_file = 1;
akt_file_abspielen = true;
}
//Masterlautstärke: Laut
if(Tasten_DB[2][2]==1){
Mp3_Master_Volume++;
if(Mp3_Master_Volume >30){
Mp3_Master_Volume=30;
LED_Setzen(1, 6);
}else{
LED_Setzen(1, 5);
}
EEPROM.write(3,Mp3_Master_Volume);
Mp3_File_Volume = EEPROM.read(3+akt_file);
dfmp3.setVolume(Mp3_File_Volume+Mp3_Master_Volume);
}
//Masterlautstärke: Leise
if(Tasten_DB[3][2]==1){
LED_Setzen(2, 5);
Mp3_Master_Volume--;
if(Mp3_Master_Volume < 0){
Mp3_Master_Volume=0;
LED_Setzen(2, 6);
}else{
LED_Setzen(2, 5);
}
EEPROM.write(3,Mp3_Master_Volume);
Mp3_File_Volume = EEPROM.read(3+akt_file);
dfmp3.setVolume(Mp3_File_Volume+Mp3_Master_Volume);
}
}
;
//Betriebsmode: LAUTSTÄRKE LERNEN --------------------------------------
if(BetriebsMode == 2){Lautstaerke_lernen();}
Tasten_DB[1][2]=0; Tasten_DB[2][2]=0; Tasten_DB[3][2]=0;
;
//DCC-OUT PIN ------------------------------------------------------------------------------------------
if(dataReady == true){
dccauswertung(); //eingelesene Daten auswerten
if(BetriebsMode == 0){SignaleSchalten();} //SignaleSchalten();
if(BetriebsMode == 1){AdressenLernen();} //Lernen der DCC-Adressen;
;
dataReady = false;
}
//--------------------------------------------------------------------
//Tastenabfrage
Tasterauswertung();
;
//Timersteuerung
long ueberlauf = millis()- timer;
if (millis()- timer > timerIntervall || ueberlauf < 0){Timer_01sek(); timer = millis();}
//Serial.println(dfmp3.getVolume());
}
/****************************************************************
* MP3 Routinen
/***************************************************************/
class Mp3Notify
{
public:
static void PrintlnSourceAction(DfMp3_PlaySources source, const char* action)
{
if (source & DfMp3_PlaySources_Sd){
Serial.print("SD Card, ");
}
;
if (source & DfMp3_PlaySources_Usb) {
Serial.print("USB Disk, ");
}
;
if (source & DfMp3_PlaySources_Flash){
Serial.print("Flash, ");
}
Serial.println(action);
}
;
static void OnError([[maybe_unused]] DfMp3& mp3, uint16_t errorCode){
// see DfMp3_Error for code meaning
Serial.println();
Serial.print("Com Error ");
Serial.println(errorCode);
}
static void OnPlayFinished([[maybe_unused]] DfMp3& mp3, [[maybe_unused]] DfMp3_PlaySources source, uint16_t track){
Serial.println("Play finished");
//Sound-loop nur für Track 0
if(BetriebsMode == 0 && Sound0001_on==true){
dfmp3.playMp3FolderTrack(1); // sd:/mp3/0001.mp3
Mp3_File_Volume = EEPROM.read(3+1);
dfmp3.setVolume(Mp3_File_Volume+Mp3_Master_Volume);
Serial.println("Loop Lautstärke: " + String(Mp3_File_Volume) +":"+String(Mp3_Master_Volume));
}
//loop im Testmode
if(BetriebsMode == 2){
dfmp3.playMp3FolderTrack(akt_file); // sd:/mp3/0001.mp3, sd:/mp3/0002.mp3, sd:/mp3/0003.mp3
}
}
static void OnPlaySourceOnline([[maybe_unused]] DfMp3& mp3, DfMp3_PlaySources source){
PrintlnSourceAction(source, "online");
}
;
static void OnPlaySourceInserted([[maybe_unused]] DfMp3& mp3, DfMp3_PlaySources source){
PrintlnSourceAction(source, "inserted");
}
static void OnPlaySourceRemoved([[maybe_unused]] DfMp3& mp3, DfMp3_PlaySources source){
PrintlnSourceAction(source, "removed");
}
};
;
void Lautstaerke_lernen(){
/*
* Speichern der Lautsärke für jedes Mp3-File
*/
;
//File abspielen
if(akt_file_abspielen==true){
dfmp3.playMp3FolderTrack(akt_file);
akt_file_abspielen=false;
;
Mp3_File_Volume = EEPROM.read(3+akt_file);
if(Mp3_File_Volume >30){Mp3_File_Volume=30;}
dfmp3.setVolume(Mp3_File_Volume+10); //Volume immer um 10 Höher, damit über die MAsterlautstärke Spielraaum ist
;
Tasten_DB[1][2]=0; Tasten_DB[2][2]=0; Tasten_DB[3][2]=0;
}
;
//Filestärke: Laut
if(Tasten_DB[2][2]==1){
Mp3_File_Volume++;
if(Mp3_File_Volume > 20){ //Volume immer um 10 Höher, daher begrenzung auf 20
Mp3_File_Volume= 20;
LED_Setzen(1, 6);
}else{
LED_Setzen(1, 5);
}
dfmp3.setVolume(Mp3_File_Volume+10);
EEPROM.write(3+akt_file,Mp3_File_Volume);
}
//Filestärke: Leise
if(Tasten_DB[3][2]==1){
LED_Setzen(2, 5);
Mp3_File_Volume--;
if(Mp3_File_Volume < 0){
Mp3_File_Volume=0;
LED_Setzen(2, 6);
}else{
LED_Setzen(2, 5);
}
dfmp3.setVolume(Mp3_File_Volume+10);
EEPROM.write(3+akt_file,Mp3_File_Volume);
}
;
//Beenden
if(Tasten_DB[2][2]==1 && Tasten_DB[3][2]==1){
BetriebsMode = 0;
dfmp3.stop();
LED_Setzen(4, 1);
delay(1000);
Tasten_DB[1][2]=0; Tasten_DB[2][2]=0; Tasten_DB[3][2]=0;
}
//Nächtses File
if(Tasten_DB[1][2]==1){
akt_file_abspielen=true;
akt_file++;
if(akt_file > Anzahl_files){akt_file=1;}
}
}
/****************************************************************
* DCC verarbeitung
/***************************************************************/
void dccdata() {
Timer1.start(); //Timer starten
}
void dcctime(){
Timer1.stop(); //Timer anhalten
int State = digitalRead(dccPin);
if (getdata == true) {
countbit += 1; //Abzählen der Bytes, also jeweils 8 Bit
}
;
if (State == LOW){ //1-Bit gelesen
countone += 1; //Zählen der 1-Bit für Präambel erforderlich
if (getdata == true && countbit <= 8){ //eingelesenen Bitwert im Array Speichern
bitWrite(data[datalength], 8-countbit, 1); //Speichert das ein 1-Bit gelesen wurde
}
if (countbit > 8) { //End-Bit gelesen.
countbit = 0;
getdata = false; //Stop des Einlesen der Daten
//XOR Prüfen:
if (data[datalength] != dxor) //Prüfen von XOR und letztes Byte
return; //verwerfen!
while (datalength < 4) { //Löschen des leeren Bereichs am Ende
datalength++;
data[datalength] = 0;
}
dataReady = true; //Fertig, Daten Auswerten!
}
} //Ende 1-Bit
else{ //0-Bit gelesen
if (getdata == true && countbit <= 8) { //eingelesenen Bitwert im Array Speichern
bitWrite(data[datalength], 8-countbit, 0); //Speichert das ein 0-Bit gelesen wurde
}
if (countone > 10){ //Präambel erkannt ab hier Daten lesen. (Es wurden mind. 10 HIGH erkannt)
getdata = true; //Einlesen der Bitwerte in Array aktivieren.
datalength = 0; //Position im Array an der die Bitwerte gespeichert werden.
countbit = 0; //beginne Bits zu zählen. Auswertung von 1 Byte Blöcken ermöglichen nach 8 Bit.
dxor = 0; //XOR zurücksetzten
}
if (countbit > 8){ //Null-Bit gelesen. (Immer nach 1 Byte)
countbit = 0;
dxor = dxor ^ data[datalength]; //XOR bestimmen!
datalength += 1; //Bitweise im Array weitergehen.
}
countone = 0; //Es wurde ein 0-Bit gelesen, lösche Anzahl gelesener 1-Bit
} //Ende 0-Bit
}
void dccauswertung() {
//für Schaladressen: 1-2044
if ((data[0] >> 6 == B10) && (data[1] >> 7 == 1)) {
//1. Adressteil
byte dccAdr1 = data[0] & B00111111;
;
//2. Adressteil
byte dccAdr2 = (data[1] & B00000110) >> 1;
;
//3. Adressteil
int dccAdr3 = (data[1] & B01110000) >> 4;
dccAdr3 = (7-dccAdr3)*256
;
//Summe
dccAdr = (dccAdr1-1)*4 + dccAdr2+1 + dccAdr3; //Adresse in 4er Blöcken
;
//DCC: Ein oder Ausschalten
dccZustand = 0;
if(data[1] & B1 == 1){dccZustand = 1;}
}
}
/****************************************************************
* DCC Auswertung
/***************************************************************/
void AdressenLernen(){
/*
* lernen der DCC Adresse
* 1. ankommende Adresse + 3 weitere nach DCC_Portadressen[1 bis 4]
* Adresse mus kleiner als 2039 sein
*/
digitalWrite(Led_Adr,LOW);
BetriebsMode=0;
;
if(dccAdr < 2039){
Serial.println("Lerne DCC-Adresse: " + String(dccAdr));
for(byte z=0;z<=3;z++){DCC_Portadressen[z+1] = dccAdr + z;}
saveDccToEeprom();
}else{
Serial.println("Fehler: DCC-Adresse: " + String(dccAdr) + " muss kleiner als 2039 sein");
}
}
void SignaleSchalten(){
/*
* Releais und Ports ein / Aus-Schalten
*/
Serial.println("--------------------------------");
Serial.println("DCC-Adresse: " + String(dccAdr) +" dccZustand: " + String(dccZustand));
;
for(byte z=0;z<=40;z++){
if(dccAdr == DCC_Portadressen[z]){
PortSchalten(z, dccZustand);
Serial.println("Schalte: Nr: " +String(z)+ ", DCC-Adresse: " + String(dccAdr) +" in " + String(dccZustand));
}
}
}
void PortSchalten(byte Nr, byte Zustand){
/*
* Physikalisches Schalten der Ports
*/
;
if(Nr == 0){digitalWrite(Led_Test,Zustand);} //TestLED
;
Serial.println("SongIdx: " + String(Nr) + " Zustand: " + String(Zustand));
//MP3decoder -------------------------------------------------------------------------------
if(Nr>0 && Zustand == 1){
dfmp3.playMp3FolderTrack(Nr);
Mp3_File_Volume = EEPROM.read(3+Nr);
dfmp3.setVolume(Mp3_File_Volume+Mp3_Master_Volume);
Serial.println("Lautstärke: " + String(Mp3_File_Volume) +":"+String(Mp3_Master_Volume));
if(Nr == 1){Sound0001_on=true;}
}
if(Nr==1 && Zustand==0){
dfmp3.stop();
Sound0001_on=false;
}
}
/*****************************************************************************************
* EEProm Routinen
*****************************************************************************************/
/*
* Adr (1-2) : DCC-Startadresse
* Adr (3) : Masterlautstärke (Werte 0-30)
* Adr (4-44): Filelautstärke (Werte 0-24)
*/
void saveDccToEeprom(){
/*
* Schreiben der erlernten DCC Adressen
*/
byte HSB = DCC_Portadressen[1]/255;
byte LSB = DCC_Portadressen[1]-(HSB*255);
;
EEPROM.write(1,HSB);
EEPROM.write(2,LSB);
}
void loadDccfromEeprom(){
/*
* Lesen der DCC-Daten aus dem EEProm
*/
byte HSB = EEPROM.read(1);
byte LSB = EEPROM.read(2);
;
DCC_Portadressen[0] = 2044; //Testadresse
for(byte z=1;z<=40;z++){
DCC_Portadressen[z] = HSB*255+LSB+z-1;
if(HSB==255 && LSB==255){DCC_Portadressen[z]=0;}
}
}
/*****************************************************************************************
* Timer Routinen
*****************************************************************************************/
void Timer_01sek(){
/* Timeoutbearbeitung
* Ausführung bei 1, Unberücksichtigt bei 0
*
* TimeOut[1]: Learning Mode
* TimeOut[2]: LED ausschalten
* TimeOut[3]:
*/
//LED-Steuerung
LED_Steuerung();
//Tastenroutinen
for(byte z=1; z<=3; z++){
if(Tasten_DB[z][1] >0){Tasten_DB[z][1]--;}
if(Tasten_DB[z][1]==0){Tasten_DB[z][2]=0;}
}
//MP3-Steuerung
dfmp3.loop();
;
//TimeoutVerarbeitung
for(byte z=1;z<=3;z++){
if(TimeOut[z] >= 2){TimeOut[z]--;}
if(TimeOut[z] == 1){Timer_ToDo(z); TimeOut[z]=0;}
}
//Serial.println(TimeOut[2]);
}
void Timer_ToDo(byte nr){
if(nr==1){LED_Setzen(4, 1); BetriebsMode=0;}
if(nr==2){LED_Setzen(4, 1);}
if(nr==3){;}
}
/*****************************************************************************************
* LED-Steuerung
*****************************************************************************************/
/*
* Modus 0: ignore
* Modus 1: aus
* Modus 2: an
* Modus 3: blink Slow
* Modus 4: blink fast
* Modus 5: Once
* Modus 6: Twice
*
* NR 1: Rt/Ge => Gelb
* NR 2: Rt/Ge => Rot
* NR 3: Gn: Testadresse
*/
byte Blink_slow=10;
byte Blink_fast=1;
byte Blink_once=5;
byte Blink_once_lang=20;
void LED_Setzen(byte Nr, byte Modus){
LED_DB[Nr][0] = Modus;
}
void LED_Steuerung(){
/*
* LED_DB(Nr,0) = Modus
* LED_DB(Nr,1) = Timer
*/
for(byte Nr=1;Nr<=4;Nr++){
byte Modus = LED_DB[Nr][0];
byte LED_Timer = LED_DB[Nr][1];
;
//--AUS-------------------------------------------
if(Modus==1){
LED_Schalten(Nr, 0);
LED_DB[Nr][0] = 0;
}
//--EIN-------------------------------------------
if(Modus==2){
LED_Schalten(Nr, 1);
LED_DB[Nr][0] = 0;
}
//--BLINK SLOW-------------------------------------
if(Modus==3){
if(LED_Timer==0){
LED_Timer=Blink_slow*2;
LED_Schalten(Nr, 1);
}
if(LED_Timer==Blink_slow){
LED_Schalten(Nr, 0);
}
LED_Timer--;
LED_DB[Nr][1] = LED_Timer;
}
//--BLINK fast-------------------------------------
if(Modus==4){
if(LED_Timer==0){
LED_Timer=Blink_fast*2;
LED_Schalten(Nr, 1);
}
if(LED_Timer==Blink_fast){
LED_Schalten(Nr, 0);
}
LED_Timer--;
LED_DB[Nr][1] = LED_Timer;
}
//--BLINK Once-------------------------------------
if(Modus==5){
if(LED_Timer==0){
LED_Timer=Blink_once;
LED_Schalten(Nr, 1);
}
if(LED_Timer==1){
LED_Schalten(Nr, 0);
LED_DB[Nr][0] = 0;
}
LED_Timer--;
LED_DB[Nr][1] = LED_Timer;
}
//--BLINK Once lang --------------------------------
if(Modus==6){
if(LED_Timer==0){
LED_Timer=Blink_once_lang;
LED_Schalten(Nr, 1);
}
if(LED_Timer==1){
LED_Schalten(Nr, 0);
LED_DB[Nr][0] = 0;
}
LED_Timer--;
LED_DB[Nr][1] = LED_Timer;
}
}
}
void LED_Schalten(byte Nr, bool status){
if(Nr==1 && status==0){digitalWrite(Led_Rt_Ge,LOW); digitalWrite(Led_Ge_Rt,LOW);}
if(Nr==1 && status==1){digitalWrite(Led_Rt_Ge,LOW); digitalWrite(Led_Ge_Rt,HIGH);}
if(Nr==2 && status==0){digitalWrite(Led_Ge_Rt,LOW); digitalWrite(Led_Rt_Ge,LOW);}
if(Nr==2 && status==1){digitalWrite(Led_Ge_Rt,LOW); digitalWrite(Led_Rt_Ge,HIGH);}
if(Nr==3 && status==0){digitalWrite(Led_Test,LOW);}
if(Nr==3 && status==1){digitalWrite(Led_Test,HIGH);}
if(Nr==4 && status==0){digitalWrite(Led_Adr,LOW);}
if(Nr==4 && status==1){digitalWrite(Led_Adr,HIGH);}
}
/*****************************************************************************************
* Tastenabfrage
*****************************************************************************************/
/*
* Taste 1: Adressetaster
* Taste 2: Lauttaster
* Taste 3: Leisetaster
*
* Tasten_DB[4][3]; [1] Sperrzeit [2] nicht gedrückt(0)/gedrückt(1)
*
*/
void Tasterauswertung(){
byte tastentimeout= 10; // enstprich 50 x 10 ms
byte T_adr = analogRead(Taste_Adresse);
byte T_laut = digitalRead(Taste_Laut);
byte T_leise = digitalRead(Taste_Leise);
;
if(T_adr > 100 && Tasten_DB[1][1] == 0){Tasten_DB[1][1]=tastentimeout;Tasten_DB[1][2]=1;}
if(T_laut == 1 && Tasten_DB[2][1] == 0){Tasten_DB[2][1]=tastentimeout;Tasten_DB[2][2]=1;}
if(T_leise == 1 && Tasten_DB[3][1] == 0){Tasten_DB[3][1]=tastentimeout;Tasten_DB[3][2]=1;}
}
/*************************************************************
* PrintRoutinen
************************************************************/
void printDccAdr(){
Serial.print(" My DCC-Adr: ");
for(byte z=1;z<=4 ;z++){
Serial.print(DCC_Portadressen[z]);
Serial.print(":");
}
Serial.print(" ... :" + String(DCC_Portadressen[40])) ;
Serial.println("");
}