/* Demo: ein einfacher Dcc-Multifunktionsdecoder
* ---------------------------------------------
* Die Funktionen können Servos, Leds und blinkende Leds steuern
* Die Leds müssen so angeschlossen sein, dass sie bei HIGH am Ausgang leuchten
* Definiert werden die Funktionen aufsteigend ab Funktion 0.
* Wieviel Funktionen genutzt werden, bestimmt die Länge der Arrays funktionsTyp bzw. funktionsPins
* In diesem Sketch ist es noch nicht möglich, eine beliebige Auswahl von Funktionsnummern zu
* verwenden. Es sollte aber nicht sehr schwierig sein, das nachzurüsten ;-))
*
* Um den Sketch noch übersichlich zu halten, werden keine CV-Programmierungen verwendet.
* Alle Festwerte werden am Anfang mit #define oder const-Anweisungen festgelegt.
*
* Der Decoder arbeitet '2-stufig'.
* 1) Erkennen des Funktionsstatus in den empfangenen Telegrammen. Der Zustand der Funktion (EIN oder AUS)
* wird im Telegramm erkannt, und in einem Array 'funktionsWert' hinterlegt. Eine weitere Bearbeitung
* findet in der Telegrammauswertung nicht statt.
* 2) Im Loop werden die Funktionen bearbeitet. Hier wird dann nur noch auf das Array zugegriffen um
* zu erkennen ob die jeweilige Funktion ein- oder ausgeschaltet ist. Dies erlaubt eine flexible
* Bearbeitung der Funktionsausgänge, unabhängig davon wann tatsächlich ein entsprechendes Telegramm
* empfangen wird (z.B. das Blinken an den Ausgängen)
*/
#include <MobaTools.h>
//----------------------------------------------------------------------------------------
#include <NmraDcc.h>
// Leider ist die neueste Version der NmraDcc Lib nicht mehr kompatibel zu Vorgängerversionen. Ich habe
// bisher noch keinen Weg gefunden, dies automatisch anhand der NmraDcc.h zu erkennen.
// Deshalb muss bei Verwendung der älteren Versionen hier ein Flag gesetzt werden:
//#define OLD_NMRADCC
#ifndef NMRA_DCC_ENABLE_14_SPEED_STEP_MODE
#define FN_0 255
#endif
// die NmraDcc - Library gibt es unter https://github.com/mrrwa/NmraDcc/archive/master.zip
//----------------------------------------------------------------------------------------
// ################ anzupassende Konstante: #####################################################
const byte dccInputP = 2; // Inputport für DCC-Signal
const word myAddr = 1830; // Adresse auf die der Decoder reagiert
#define FKTMAX 28 // höchste Funktionsnummer, die im Telegramm noch ausgewertet wird. Es werden
// im Telegramm mehr Funktionen ausgewertet, als aktiv verwendet werden. 'Auswerten'
// heisst hier lediglich, dass der Zustand der Funktion (EIN oder AUS) im Telegramm
// erkannt und im Array 'funktionsWert' hinterlegt wird. D.h. der Sketch kennt den Zustand
// aller Funktionen von 0...28, unabhängig davon, ob für diese Funktion auch ein Ausgang und
// eine entsprechende Bearbeitung definiert ist
// Das erleichtert eine spätere Erweiterung auf die Verwendung beliebig ausgewählter
// Funktionsnummern
// ------------ Definition der aktiv verwendeten Funktionen -----------------------------------------
// Genutzt werden die Funktionsnummern ab 0. Index in die folgenden Tabellen ist die Funktionsnummer.
// Der Tabelleninhalt gibt die Parameter für die jeweilige Funktion an.
// Der Funktionstyp wird im Array funktionsTyp festgelegt.
// Werte kleiner 16 sind ein Servoausgang ( Der Wert darf maximal SERVOANZAHL-1 sein )
#define FLED 16 // Der Ausgang wird entsprechend des Funktionswertes statisch ein- bzw. ausgeschaltet
#define FBLINK 17 // Bei aktiver Funktion wird der Ausgang in einem vorgegeben Takt ein- und ausgeschaltet
const byte funktionsTyp[] = {FLED, 0, 1, 2, 3,FLED,FBLINK,FBLINK};
const byte funktionsPins[] = { 4, 5, 6, 7, 8, 9, 10, 11}; // output-pin der Funktionen
const byte funktionsAnzahl = sizeof( funktionsTyp );
#define BLINKZAHL 2 // Zahl der Funktionen mit Blinken, muss zum obigen Typ-Array passen
const int blinkEin[BLINKZAHL] = { 500, 1000 };
const int blinkAus[BLINKZAHL] = { 500, 300 };
#define SERVOANZAHL 4 // 4 Servofunktionen - muss zum obigen Typ-Array passen
const int ausPuls[SERVOANZAHL] = {1200,1200,1200,1200}; // Pulslänge Funktion aus
const int einPuls[SERVOANZAHL] = {1800,1800,1800,1800}; // Pulslänge Funktion ein
const int servoSpeed[SERVOANZAHL] = { 8, 1, 10, 20}; // Geschwindigkeit des Servo
const byte autoOff[SERVOANZAHL] = { 1, 1, 0, 0}; // automatisch Abschalten nach erreichen der Endstellung
//###########################################################################################################
// Definition der Variablen und Objekte
boolean funktionsWert[FKTMAX+1]; // aktueller Wert der Funktion vom DCC-Tel. ( ein- oder ausgeschaltet )
Servo8 fServo[SERVOANZAHL];
NmraDcc Dcc;
EggTimer blinkerT[BLINKZAHL];
byte blinkState[BLINKZAHL]; // momentaner Status de FBLINK Ausgangs (LOW oder HIGH)
///////////////////////////////////////////////////////////////
void setup() {
Dcc.pin(digitalPinToInterrupt(dccInputP), dccInputP, 1); // Dcc-Signal mit Pullup
Dcc.init( MAN_ID_DIY, 01, 0, 0 ); // Multi-Funktionsdecoder, keine Adressauswertung
// Funktionsausgänge initiieren
for ( byte i=0; i<funktionsAnzahl; i++ ) {
if ( funktionsTyp[i] < SERVOANZAHL ) {
// ServoAusgang:
byte servoNr = funktionsTyp[i];
fServo[servoNr].attach( funktionsPins[i], autoOff[servoNr] );
fServo[servoNr].setSpeed( servoSpeed[servoNr] );
} else {
// nur Digital Ausgang
pinMode( funktionsPins[i],OUTPUT );
digitalWrite( funktionsPins[i],LOW );
}
}
}
////////////////////////////////////////////////////////////////
void loop() {
Dcc.process(); // Hier werden die empfangenen Telegramme analysiert
// Bearbeiten der definierten Funktionen
byte blinkIx = 0; // Index für die Timer der Blinkfunktionen
for ( byte fIx = 0; fIx < funktionsAnzahl; fIx++ ) {
// Schleife über alle definierten Funktionen
switch ( funktionsTyp[fIx] ) {
// je nach Funktionstyp müssen unterschiedliche Bearbeitung aufgerufen werden
case FLED: //---------------------------------------------------------------------------
// Ausgang entsprechend des Funktionswertes ein/ausschalten
if ( funktionsWert[fIx] ) digitalWrite( funktionsPins[fIx], HIGH );
else digitalWrite( funktionsPins[fIx], LOW );
break;
case FBLINK: //-------------------------------------------------------------------------
// Ausgang blinkt bei aktiver Funktion
if ( funktionsWert[fIx] ) {
// Funktion ist aktiv, Led blinkt
// Solange der jeweilige Timer noch läuft, passiert hier nichts, und die Led
// verharrt im aktuellen Status (AUS oder EIN)
if ( ! blinkerT[blinkIx].running() ) {
// Timer läuft nicht, Ausgang schalten und Zeitwert setzen.
if ( blinkState[blinkIx] == HIGH ) {
//aktueller Status ist EIN, also ausschalten
digitalWrite( funktionsPins[fIx], LOW ); // Ausgang abschalten
blinkerT[blinkIx].setTime( blinkAus[blinkIx] ); // Zeit für AUS setzen
blinkState[blinkIx] = LOW; // Status auf AUS
} else {
//aktueller Status ist AUS, also einschalten
digitalWrite( funktionsPins[fIx], HIGH ); // Ausgang einschalten
blinkerT[blinkIx].setTime( blinkEin[blinkIx] ); // Zeit für EIN setzen
blinkState[blinkIx] = HIGH; // Status auf EIN
}
}
} else {
// Funktion ist inaktiv, Ausgang abschalten
digitalWrite( funktionsPins[fIx], LOW );
}
blinkIx++; // Index für nächste Funktion mit Blinken. So erhält jeder Ausgang mit
// Blinkfunktion seinen eigenen Timer für die Blinkzeiten
break;
default: //---------------------------------------------------------------------------
// Servoansteuerung, vorsichtshalber auf Zahl der Servos prüfen
// Einträge die weder den Funktionstypen oben, noch der Zahl der Servos entsprechen
// werden so ignoriert.
if ( funktionsTyp[fIx] < SERVOANZAHL ) {
byte servoNr = funktionsTyp[fIx];
if ( funktionsWert[fIx] ) fServo[servoNr].write( einPuls[servoNr] );
else fServo[servoNr].write( ausPuls[servoNr] );
}
} // Ende switch Funktionstyp -----------------------------------------------------------
} // Ende der Schleife über die definierten Funktionen
} // Ende Loop ##############################################################################
//////////////////////////////////////////////////////////////
// Unterprogramme, die von der DCC Library aufgerufen werden:
// Die folgende Funktion wird von Dcc.process() aufgerufen, wenn ein Funktionstelegramm empfangen wurde
// Funktionstelegramm ======================================================================
#ifdef OLD_NMRADCC
void notifyDccFunc( uint16_t Addr, FN_GROUP FuncNum, uint8_t FuncState){
#else
void notifyDccFunc( uint16_t Addr, DCC_ADDR_TYPE AddrType, FN_GROUP FuncNum, uint8_t FuncState){
#endif
if ( (Addr&0x3fff) == myAddr ) {
// Funktionstelegramm mit eigener Adresse erhalten
// In einem Telegramm wird immer nur ein Teil der Funktionswerte übertragen. (Bitweise codiert
// in der übergebenen Variable 'FuncState)
// Die folgenden Tabellen enthalten - abhängig von der Funktionsgruppe (FuncNum)
// jeweils die kleinste und höchste in FuncState übergebene Funktionsnummer
static byte FGrStart[] = {0,1,5, 9,13,21}; // Offset Funktionsgruppe -> kleinste Funktionsnummer
static byte FGrEnde[] = {0,4,8,12,20,28}; // Offset Funktionsgruppe -> letzte Funktionsnummer
uint8_t fMsk, i, fIxStart, fIxMax;
if ( FuncNum == FN_0_4 || FuncNum == FN_0 ) {
// Sonderfall Funktion0 ausfiltern
funktionsWert[0] = FuncState&FN_BIT_00;
}
if ( FuncNum != FN_0 ) {
// in der Gruppe FN_0 wird NUR die Funktion 0 übertragen, d.h. wir sind mit
// der obigen Auswertung fertig. In allen anderen Gruppen werden
// auch weitere Funktionen, codiert ab Bit0 übertragen, die in der folgenden
// for-Schleife ausgewertet werden.
fIxStart = FGrStart[FuncNum];
fIxMax = FGrEnde[FuncNum];
fMsk= 0b00000001; // Maske zum herausfiltern der Funktionsbits in FuncState
for ( i=fIxStart; i<= fIxMax && i<=FKTMAX; i++ ) {
// Funktionsstatus in das Array funktionsWert eintragen
funktionsWert[i] = FuncState & fMsk;
fMsk = fMsk<<1; // Maske für nächste Bitposition
}
}
}
}