Ziel ist eine drahtlose, mit einem ARDUINO Mikrokontroller realisierte Modellbahnsteuerung. Als Modellbahnsteuerungsprotokoll kommt DCC in einer vereinfachten Version zur Anwendung.
Heutige Modellbahnen werden digital gesteuert. Dazu musste ein Protokoll definiert werden. Dieses Protokoll trägt den Namen «DCC Digital Command Control» Heutzutags gibt es auch Alternativen zu DCC. Die DCC-Spezifikation findet man hier:
Das DCC-Gleissignal hat einerseits den Zweck, die Fahrzeuge mit Energie zu versorgen und andererseits, sie mit individuellen Fahrzeugkommandos zu steuern. Es ist also möglich, auf einem Gleis mehrere Züge gleichzeitig zu steuern. Das folgende Bild zeigt den Signalverlauf eines Steuerbefehls. Logisch 0 und Logisch 1 wird nicht durch eine Spannungsdifferenz, sondern durch eine verschieden lange Pulsdauer unterschieden.

Arduino ist eine quelloffene, aus Soft- und Hardware bestehende Physical-Computing-Plattform.
Die Hardware besteht grundsätzlich aus einem E/A-Board mit einem Mikrocontroller und analogen und digitalen Ein- und Ausgängen.
Die Programmierung erfolgt in einer C- bzw. C++-ähnlichen Programmiersprache,
wobei technische Details wie Header-Dateien vor den Anwendern weitgehend verborgen werden
und umfangreiche Bibliotheken und Beispiele die Programmierung vereinfachen.
Beispiel für ein Programm (Sketch), das eine an das Arduino-Board angeschlossene LED blinken lässt:
int ledPin = 13; //LED an Pin 13
void setup() //Wird beim Start des Programms einmalig aufgerufen
{
pinMode(ledPin, OUTPUT); //LED-Pin als Ausgang festlegen
}
void loop() //Wird ständig wiederholt, bis das Arduino-Board ausgeschaltet wird
{
digitalWrite(ledPin, HIGH); //LED anschalten
delay(1000); //1000 Millisekunden warten
digitalWrite(ledPin, LOW); //LED ausschalten
delay(1000); //1000 Millisekunden warten
}
Wie rechnet man auf «Bit-Ebene»?
Shift-Right: x >> n (Bitfolge x um n-Stellen nach rechts verschieben. Rechts fallen die Bits heraus.) Shift-Left: x << n (Bitfolge x um n-Stellen nach links verschieben. Neue 0-Bits werden rechts eingefügt.) AND: x & y (0&0=0 / 0&1=0 / 1&0=0 / 1&1=1 Hinweis: Nicht verwechseln mit logischem AND &&) OR x | y (0|0=0 / 0|1=1 / 1|0=1 / 1|1=1 Hinweis: Nicht verwechseln mit logischem OR ||) XOR x ^ y (0^0=0 / 0^1=1 / 1^0=1 / 1^1=0) NOT ~x (~0=1 / ~1=0) (Die zur Verfügung stehende Bitbreite ist zu beachten! Stichwort: Data-Overflow) Binäre Division durch 2 (Bsp.: 12 div 2 = 6 Rest 0) 12DEZ = 1100BIN 1100BIN >> 1BIN = 110BIN (Shift-Right) 110BIN = 6DEZ (Ist bei Shift-Right rechts eine 0 herausgefallen: Rest 0) Binäre Division durch 2 (Bsp.: 15 div 2 = 7 Rest 1) 15DEZ = 1111BIN 1111BIN >> 1BIN = 111BIN (Shift-Right) 111BIN = 7DEZ (Ist bei Shift-Right rechts eine 1 herausgefallen: Rest 1) Multiplikation mit Faktor 2 (Bsp.: 13 * 2 = 26) 13DEZ = 1101BIN 1101BIN << 1BIN = 11010BIN (Shift-Left) 11010BIN = 26DEZ Addition von 2 Bits inkl. Übertrag (Volladdierer) (A=1.Bit, B=2.Bit, S=Summe, C1=Übertrag In, C2=Übertrag Out) S = (A^B) ^ C1 C2= (A&B) | (C1&(A^B)) Zahlenbeispiel 1+0+1=10 oder 2DEZ A = 1 (1. Bit) B = 0 (2. Bit) C1= 1 (Bit aus der vorangegangenen Binärstelle) S = 0 (Summe) C2= 1 (Übertrag in die nächste Binärstelle) Bit auslesen (Bsp.: Zweites Bit von links auslesen) a=0110 b=0011 c=0100 a & c = 0100 b & c = 0000 Bit setzen (Bsp.: Zweites Bit von links setzen) a=0010 b=0110 c=0100 a | c = 0110 (Bit wurde gesetzt) b | c = 0110 (Bit war schon gesetzt) Bit löschen (Bsp.: Zweites Bit von links löschen) a=0010 b=0110 c=0100 (Hinweis: ~c = 1011) a & ~c = 0010 (Bit war schon gelöscht) b & ~c = 0010 (Bit wurde gelöscht)

Die Arduino-Codelistings findet man ab hier: ARDUINO SOURCECODE DCC-PROJEKT (GEMEINSAMER HEADER)
Hier wird auf DCC verzichtet. Eine RC-Fernsteuerung übermittelt seine Befehle zu Fahrtrichtung und Geschwindigkeit
via Funkstrecke an einen RC-Empfänger in der Lok. Dieser steuert einen MAXON-Baustein mit H-Bridge, der wiederum den Motor mit Spannung versorgt. Somit braucht es
den Umweg über DCC nicht.
Als Loktyp habe ich eine LGB-Märklin-Zahnraddampflok des Typs HG-4/4 (Art.-Nr.26270/71) gewählt. Da diese Lokomotive im abgelieferten Zustand nicht
richtig überzeugen konnte, entschied ich mich, den ganzen elektrischen Teil auszubauen und mit neuen Komponenten zu versorgen.

Folgende Beweggründe gaben den Ausschlag für den Umbau:


1. Schritt - Lösen von verschiedenen Klebeverbindungen:
Unterhalb der Rauchkammertür (E), an den beiden Laternen auf der Lokrückseite (F),(G) und die Lokaufstiege links und rechts
der Rauchkammertür (A),(B),(C),(D).

2. Schritt - Lösen von Schrauben am Triebwerk:
Am Triebwerk müssen zwei Schrauben (A) und (B) entfernt werden. Um an der linken Seite an die Schraube (B) zu gelangen,
muss zuerst das Kupferrohr (C) vorsichtig gelöst und entfernt werden. (Das Foto rechts zeigt das bereits demontierte Kupferrohr)

3. Schritt - Fahrwerk vom Aufbau trennen:
Zuerst müssen die drei Schrauben (A), (B) und (C) gelöst werden. Die violett markierten Schrauben aufdrehen, falls man die Achsabdeckungen entfernen möchte.
Die rot markierten Gewindelöcher dienen ausschliesslich der Fixierung der Lok auf dem mitgelieferten Verpackungs-Gleisprofil.

4. Schritt - Lokaufbau entfernen:
Der Lokaufbau nun sorgfältig abheben. Fast alle elektrischen Verbindungen laufen über die Federpinleiste (Rote Pfeile in der Bildmitte).
Nicht aber die Kabel für die rückseitigen Laternen (Roter Pfeil rechts). Darum Vorsicht beim Abnehmen des Lokaufbaus.

5. Schritt - Rauchgenerator entfernen:
Möchte man den Rauch- und Dampfgenerator erreichen, muss man zuerst das Blech an der Kesselunterseite entfernen.
Dazu müssen die sechs mit grünen Pfeilen markierten Schrauben gelöst werden. Der Rauchgenerator ist mit (D) markiert.
Er beinhaltet ein komplexes Belüftungssystem mit vier Lüftern. Auf der Höhe des Wasserkastens kann man den Lautsprecher erkennen.
Bevor man das Rundlaufblech abnehmen kann, muss man vorher vorsichtig das Zahnstangensignal vom Wassertank lösen.

6. Schritt - Alles muss raus:
Fahrgestell mit Antriebsmotor (M) und Hilfsmotor (Z) für den Antrieb des sonst funktionslosen Zahnstangentriebwerks.
Beim Tender erkennt man den Lokdecoder mit darunterliegenden Stützkondensatoren.

Den Bühlermotor wurde durch einen MAXON RE30 (268213) ersetzt. Als PWM-Motorensteuerung dient der MAXON-Servokontroller ESCON Module 50/5 (438725).
Die Ansteuerung und Funkstrecke erfolgt mit Arduinos von Adafruit Feather M0 Radio with RFM69 Packet Radio ISM 433MHz. (3177).
Das Zahnstangentriebwerk wird durch die Zahnstange angetrieben.

Elektronikschema:

Die Arduino-Codelistings findet man ab hier: ARDUINO SOURCECODE NO-DCC-PROJEKT (LOKSEITIG)
VERWENDETER MIKROKONTROLLER:
Adafruit Feather M0 Radio with RFM69 Packet Radio, ATSAMD21G18 ARM Cortex M0 mit Funkmodul
RH_RF69 Radio-Control 433MHz ISM-Band Simple-messageing No-addressing No-reliability AES-encryption.
available() → Tested ob ein Funk-Paket vorhanden ist
waitAvailable() → Tested bis ein Funk-Paket vorhanden ist
waitPacketSent() → Wartet bis Funk-Paket verschickt ist
waitPacketSent(uint16_t timeout) → Wartet bis Timout oder bis Funk-Paket verschickt ist
waitAvailableTimeout(uint16_t timeout) → Tested bis Timout oder bis ein Funk-Paket vorhanden ist
ZUSÄTZLICHE ANGABEN ZUR BASISSTATION:
Der Funkhandregler schickt vom Benutzer eingegebene Lok-Befehle an die DCC-Basisstation.
Diese Basisstation empfaengt vom Funkhandregeler ein DCC-Kommando und erstellt das entsprechende Gleissignal (DCC-Paket).
Akku LiPo 4200mAh
Schmelzsicherung am Eingang der Versorgungsspannung
82mOhm Messwiderstand für Fahrstrommessung
10k zu 100k Spannungsteiler zur Akkuspannungsüberwachung
Zweifarbige Kontroll-LED Rot/Grün
ZUSÄTZLICHE ANGABEN ZUM FUNKHANDREGLER:
Vier Eingabetaster
Drei rote Kontroll-LEDs
SCHALTBARE FUNKTIONEN DER MAERKLIN/LGB HG4/4 ART.-NR. 26270:
00: Beleuchtung
01: Geräusch: Pfeife lang
02: Geräusch: Bremsenquietschen aus
03: Rauchgenerator
04: Zylinderdampf
05: Geräusch: Kohle schaufeln
06: Geräusch: Betriebsgeräusch per Zufall
07: Führerstandsbeleuchtung
08: Sound an/aus
09: ABV, aus
10: Zahnradtriebwerk in der Fahrt
11: Zahnradtriebwerk in Stand
12: Rangierlicht doppel A
13: Feuerschein - Feuerbüchse
14: Geräusch: Pfeife kurz
15: Geräusch: Schaffnerpfiff
16: Geräusch: Saugluftbremse mit Dampf
17: Geräusch: Bahnhofsansage
18: Geräusch: Kohle fassen
19: Geräusch: Wasser fassen
20: Geräusch: Glocke (schweizer Bahnübergang)
21: Geräusch: Sanden
22: Geräusch: Injektor
23: Geräusch: Dampf ablassen
24: Geräusch: Betriebsgeräusch (mit Zahnstange)
25: Reduzierung der Zylinderschläge
26: Geräusch: Führerstandstüre auf/zu
27: Geräusch: Wasserstandsanzeige spülen
28: Geräusch: Schmierpumpe
29: Geräusch: Rauchkammertüre öffnen / schliessen
30: Geräusch: Sicherheitsventil
DCC-BEFEHLSDECODIERUNG:
(GEMÄSS RAILCOMMUNITY, RCN-210 BIS RCN-213, AUSGABE DEZ. 2019)
Adresse Adresse Funktion Beschreibung
--------- --------- --------- ------------
0000-0000 = Broadcastadresse
1111-1111 = Leerlauf/Idle
0AAA-AAAA = Fahrzeugdecoder Kurze Adresse 0-127
10AA-AAAA 1AAA-DAAR = Einfache Zubehördecoder 11b-Adresse inkl. Funktion D=Aktivieren R=WeicheSchalten
10AA-AAAA 0AAA-0AA1 DDDD-DDDD = Erweiterter Zubehördecoder (Wird hier nicht verwendet!)
11AA-AAAA AAAA-AAAA = Fahrzeugdecoder Lange Adresse 0-10239 (Wird hier nicht verwendet!)
Fahrzeugdecoder Befehlscodierung Auszug (dargestellt mit kurzer Lokadresse)
---------------------------------------------------------------------------
Adresse Fkt/Adr Funktion Beschreibung
--------- --------- --------- ------------
0AAA-AAAA 0000-0000 = Decoder-Reset
0AAA-AAAA 0000-0001 = Decoder-Hardreset
0AAA-AAAA 0001-XXXX = Mehrfachtraktion
0AAA-AAAA 0011-1100 RGGG-GGGG - DDDD-DDDD {DDDD-DDDD {DDDD-DDDD {DDDD-DDDD} = Geschwindigkeit,Richtung,Funktion
0AAA-AAAA 0001-1111 RGGG-GGGG = 128 Geschwindigkeitsstufen-Befehl
0AAA-AAAA 01RG-GGGG = Basis Geschwindigkeits- und Richtungsbefehl
0AAA-AAAA 100X-XXXX = Funktionssteuerung F0-F4
0AAA-AAAA 1010-XXXX = Funktionssteuerung F9-F12
0AAA-AAAA 1011-XXXX = Funktionssteuerung F5-F8
0AAA-AAAA 1101-FFFF DDDD-DDDD = Funktionssteuerung F13-F68
0AAA-AAAA 111X-XXXX = Konfigurationsvariablen-Zugriffsbefehl
DCC-Befehlsdecodierung (Von dieser Basisstation unterstuetzte Befehle sind ausschliesslich 2 Byte lang!)
Adresse Funktion Beschr. Adressmaske Adresstest Funktionsmaske Funktionstest
--------- --------- ------- -------------- -------------- -------------- --------------
0AAA-AAAA 01RG-GGGG = Speed: 1000-0000=0x80 0000-0000=0x00 1100-0000=0xC0 0100-0000=0x40
0AAA-AAAA 100X-XXXX = F0-F4: 1000-0000=0x80 0000-0000=0x00 1110-0000=0xE0 1000-0000=0x80
0AAA-AAAA 1010-XXXX = F9-F12: 1000-0000=0x80 0000-0000=0x00 1111-0000=0xF0 0100-0000=0xA0
0AAA-AAAA 1011-XXXX = F5-F8: 1000-0000=0x80 0000-0000=0x00 1111-0000=0xF0 0100-0000=0xB0
10AA-AAAA 1AAA-DAAR = Zubehör: 1100-0000=0xC0 1000-0000=0x80 1000-0000=0x80 1000-0000=0x80
DCC-Adresse 0x03: Zahnraddampflok HG4/4 (0000-0011) Lokadresse
DCC-Adresse 0x78: Strom aus (120) (0111-1000) Funktion 0x80
DCC-Adresse 0x80: Weiche (Zubehör) (1000-0000) Funktion 0xF8 und 0xF9 (z.Zt.ungenutzt!)
DCC-Adresse 0x63: Lichsignal (99) (0110-0011) Funktion 0x80=Rot 0x90=Gruen (z.Zt.ungenutzt!)
Fahrtrichtung und Geschwindigkeit: 01RG-GGGG G-GGGG : 2xStopp, 2xNothalt, 28 Fahrstufen. R=1 → Vorwärts
Funktion 0-4 : 100L-0V0S : L=LED/F0/Licht&Grundsound, V=VAP/Dampf, S=SIG/F1/Lokpfeife
Funktion 5-8 : 1011-WCB0 : B=Betriebsgeräusch/F6, C=CAB/F7/Kabinenlicht, W=WAV/F8/Sound
Funktion 9-12 : 1010-00C0 : C=COG/F10/Zahnradimitation
Nr. 01RG-GGGG HEX Fahrstufe
--- ------ --------- ---- ---------
4 3210 01R0-4321
00: 1-1111 → 0101-1111 0x5F S28 Rueckwärts
01: 1-1110 → 0100-1111 0x4F S27 ...
02: 1-1101 → 0101-1110 0x5E S26
03: 1-1100 → 0100-1110 0x4E S25
04: 1-1011 → 0101-1101 0x5D S24
05: 1-1010 → 0100-1101 0x4D S23
06: 1-1001 → 0101-1100 0x5C S22
07: 1-1000 → 0100-1100 0x4C S21
08: 1-0111 → 0101-1011 0x5B S20
09: 1-0110 → 0100-1011 0x4B S19
10: 1-0101 → 0101-1010 0x5A S18
11: 1-0100 → 0100-1010 0x4A S17
12: 1-0011 → 0101-1001 0x59 S16
13: 1-0010 → 0100-1001 0x49 S15
14: 1-0001 → 0101-1000 0x58 S14
15: 1-0000 → 0100-1000 0x48 S13
16: 0-1111 → 0101-0111 0x57 S12
17: 0-1110 → 0100-0111 0x47 S11
18: 0-1101 → 0101-0110 0x56 S10
19: 0-1100 → 0100-0110 0x46 S9
20: 0-1011 → 0101-0101 0x55 S8
21: 0-1010 → 0100-0101 0x45 S7
22: 0-1001 → 0101-0100 0x54 S6
23: 0-1000 → 0100-0100 0x44 S5
24: 0-0111 → 0101-0011 0x53 S4
25: 0-0110 → 0100-0011 0x43 S3
26: 0-0101 → 0101-0010 0x52 S2 ...
27: 0-0100 → 0100-0010 0x42 S1 Rueckwärts
0-0011 → 0101-0001 0x11 Nothalt (Nicht implementiert)
0-0010 → 0100-0001 0x01 Nothalt (Nicht implementiert)
0-0001 → 0101-0000 0x10 0 Halt (Nicht implementiert
28: 0-0000 → 0100-0000 0x40 0 Halt Fahrstufe 28 (-0)
29: 0-0000 → 0110-0000 0x60 0 Halt Fahrstufe 29 (+0)
0-0001 → 0111-0000 0x10 0 Halt (Nicht implementiert)
0-0010 → 0110-0001 0x01 Nothalt (Nicht implementiert)
0-0011 → 0111-0001 0x11 Nothalt (Nicht implementiert)
30: 0-0100 → 0110-0010 0x62 S1 Vorwärts
31: 0-0101 → 0111-0010 0x72 S2 ...
32: 0-0110 → 0110-0011 0x63 S3
33: 0-0111 → 0111-0011 0x73 S4
34: 0-1000 → 0110-0100 0x64 S5
35: 0-1001 → 0111-0100 0x74 S6
36: 0-1010 → 0110-0101 0x65 S7
37: 0-1011 → 0111-0101 0x75 S8
38: 0-1100 → 0110-0110 0x66 S9
39: 0-1101 → 0111-0110 0x76 S10
40: 0-1110 → 0110-0111 0x67 S11
41: 0-1111 → 0111-0111 0x77 S12
42: 1-0000 → 0110-1000 0x68 S13
43: 1-0001 → 0111-1000 0x78 S14
44: 1-0010 → 0110-1001 0x69 S15
45: 1-0011 → 0111-1001 0x79 S16
46: 1-0100 → 0110-1010 0x6A S17
47: 1-0101 → 0111-1010 0x7A S18
48: 1-0110 → 0110-1011 0x6B S19
49: 1-0111 → 0111-1011 0x7B S20
50: 1-1000 → 0110-1100 0x6C S21
51: 1-1001 → 0111-1100 0x7C S22
52: 1-1010 → 0110-1101 0x6D S23
53: 1-1011 → 0111-1101 0x7D S24
54: 1-1100 → 0110-1110 0x6E S25
55: 1-1101 → 0111-1110 0x7E S26
56: 1-1110 → 0110-1111 0x6F S27 ...
57: 1-1111 → 0111-1111 0x7F S28 Vorwärts
17b Synchronbit 11111111111111111 Gefolgt von... 1b Delimiter 0 ...gefolgt von... 8b DCC-Adresse #3 0000 0011 ...gefolgt von... 1b Delimiter 0 ...gefolgt von... 8b DCC-Funktion FFFF FFFF ...gefolgt von... 1b Delimiter 0 ...gefolgt von... 8b Parity PPPP PPPP ...gefolgt von... 1b Endbit 1 ...danach Wiederholung des kompletten DCC-Pakets Total: 45b; 0..44; Paketdauer somit ca.: 11msec
WHT und Pinbelegung der L6203 H-Bridge -------------------------------------- EN IN1 IN2 SOURCE1 SOURCE2 SINK1 SINK2 -- --- --- ------- ------- ----- ----- H L L ON ON H L H ON ON H H L ON ON H H H ON ON l X X OFF OFF OFF OFF Pin01: OUT2 Pin02: Vs (+18V) Pin03: OUT1 Pin04: BOOT1 (15nF → OUT1) Pin05: IN1 Pin06: GND Pin07: IN2 Pin08: BOOT2 (15nF → OUT2) Pin09: Vref (220nF → GND) Pin10: SENSE Pin11: EN
//======================================================================
// Titel: Basisstation DCC
// Autor: Juerg Arnold, CH-Meilen
// Datum: 14. Mai 2022
// Zweck: Die Basisstation empfängt vom Funkhandregler
// die Fahr- und Umschaltkommandos und setzt sie in
// ein DCC-Gleissignal um.
//======================================================================
#include <SPI.h> //Fuer RFM69
#include <RH_RF69.h> //Fuer RFM69
#define LED_RED 5 // Rote Power-LED Pin Nr.5
// Rote LED on: Geraet in Betrieb aber mit tiefer Batteriespannung
// Rote LED off: Falls gruene LED ebenfalls off -> Geraet ausgeschaltet
// Rote LED blinkt: RF69 oder INA260 hat einen Fehler
#define LED_GREEN 6 // Gruene Power-LED Pin Nr.6
// Gruene LED on: Geraet in Betrieb, Batteriespannung ok.
// Gruene LED off: Falls rote LED ebenfalls off -> Geraet ausgeschaltet
#define VBATPIN A2 // Spannungsteiler 10k zu 100k gegen positive Akkuspannung Pin Nr. A1
#define CURRPIN A1 // 82mOhm Messwiderstand fuer Strommessung
#define L6203_IN1 12 //L6203 H-Bridge IN1 //L6203 H-Bridge IN1 Pin Nr.12
#define L6203_IN2 11 //L6203 H-Bridge IN2 //L6203 H-Bridge IN2 Pin Nr.11
#define L6203_EN 10 //L6203 H-Bridge EN //L6203 H-Bridge ENABLE Pin Nr.10
//Definitionen Radio Setup
#define RF69_FREQ 434.0
#define RFM69_CS 8
#define RFM69_INT 3
#define RFM69_RST 4
#define RadioTIMER 0xFFFFFF //Time Setup
RH_RF69 rf69(RFM69_CS, RFM69_INT); // Singleton instance of the radio driver
enum remoteSTATUS{RadioOK, RadioInitNOK, RadioFreqNOK};
//Funkmodul -> Empfangsbytes vom Funkhandregeler: Addresse/AddresseRedundat/Befehl/BefehlRedundant
uint8_t DCC_RadioPacket[4] = {0x00,0x00,0x00,0x00};
uint8_t DCC_RadioPacketLEN = 4;
uint8_t validDCCMessage = 0x00; //Empfangenes und gueltiges DCC-Funktionsbyte
uint8_t validDCCAdress = 0x00; //Empfangenes und gueltiges DCC-Adressbyte
bool NOTSTOP = true; //NOTSTOP-Flag
bool LokSoundOn = false; //LokSound-Flag on/off
bool LokPfiffOn = false; //LokPfiff-Flag on/off
uint8_t LokPfiffDCCAdress = 0x00; //Lokpfiff nach Zeitablauf zuruecksetzen
unsigned long LokPfiffCounter = 0; //Lokpfiff Zeitablauf
uint8_t averageCURR = 0x00; //Ueberschrittene Werte bei Strommessung
uint8_t cheksumCURR = 0x00; //Anzahl ueberschrittene Werte im Zeitfenster
//Globale in der TCC1-Handler ISR verwendete volatile Variablen
#define DCC_HI 0x000ADF //58uSec. DCC-Signal
enum stHBrideEnumType{UP, holdUP, downHI, downHOLD, downLOW};
volatile stHBrideEnumType stHBrideNEXT=UP;
volatile uint8_t signalHI=true; //Flag ob Log0 oder Log1 / Start mit Log-1
volatile uint8_t bitCount=0;
volatile uint8_t charMSG=0x00; //DCC-Speed oder Funktionsbefehl
volatile uint8_t charADR=0x00; //DCC-Adresse
//Waehrend dem Senden des DCC-Pakets zwischen Bit 27 (Separator-Bit) und Bit 45 (Endbit)
//darf sich die vom Funkhandregler erhaltene DCC-Funktion und DCC-Adresse nicht aendern.
void setup() //SETUP-------------------------------------------------------------------
{ //Serial.begin(9600);
//Serial.println("DEBUGGING...");
//Digitale Inputs/Outputs initialisieren---------------------------------------------
pinMode(L6203_IN1, OUTPUT);
pinMode(L6203_IN2, OUTPUT);
pinMode(L6203_EN, OUTPUT);
pinMode(LED_RED, OUTPUT);
pinMode(LED_GREEN, OUTPUT);
digitalWrite(L6203_IN1, LOW); //H-Bruecke OFF
digitalWrite(L6203_IN2, LOW); //H-Bruecke OFF
digitalWrite(L6203_EN, LOW); //H-Bruecke OFF
digitalWrite(LED_RED, LOW); //LED_RED OFF
digitalWrite(LED_GREEN, LOW); //LED_GREEN OFF
//Abschalten, wenn Akkuspannung zu tief:
//LiPo6s: Min. Akkuspannung pro Zelle: 3.3V -> 6*3.3V=19.8V
//LiPO6s: Abzueglich Schutzdiode: 19.8V-0.55V=19.25V
//LiPo6s: Spannungsteiler 1:10 -> 19.25/11=1.75
//Zuzueglich Reserve: 1.968V -> 22.2V -> 3.7V/Zelle
//10Bit-ADC -> 1024: D=1024/3.3*Umess
//1.968V = 610
if(analogRead(VBATPIN) < 610)
{ digitalWrite(L6203_EN, LOW); //H-Bruecke ausschalten
rf69.sleep(); //Funkmodul ausschalten
digitalWrite(LED_GREEN, LOW); //Gruene LED ausschalten
digitalWrite(LED_RED, LOW); //Rote LED ausschalten
while(true) //Akku muss geladen werden / RG-Blinklicht / Endlosschlaufe
{ digitalWrite(LED_RED, HIGH); //Rote LED einschalten
delay(500);
digitalWrite(LED_RED, LOW); //Rote LED ausschalten
digitalWrite(LED_GREEN, HIGH); //Gruene LED einschalten
delay(500);
digitalWrite(LED_GREEN, LOW); //Gruene LED ausschalten
}
}
//Initialisierung des Funkbausteins RFM69--------------------------------------------
pinMode(RFM69_RST, OUTPUT);
digitalWrite(RFM69_RST, LOW);
//Feather RFM69 TX Test!
digitalWrite(RFM69_RST, HIGH); // Manueller Reset
delay(10);
digitalWrite(RFM69_RST, LOW); // Manueller Reset
delay(10);
if (!rf69.init()) //RFM69 Funkbausteininitialisierung fehlgeschlagen -> RadioInitNOK
while(true)
{ digitalWrite(LED_RED, HIGH); //Rotes Blinken mit 400mSec
delay(400);
digitalWrite(LED_RED, LOW);
delay(400);
}
//---
//RFM69 Funkbaustein erfolgreich initialisiert. Es werden folgende Werte eingestellt:
//434.0MHz
//Modulation GFSK_Rb250Fd250
//+13dbM (fuer Low Power Module)
if (!rf69.setFrequency(RF69_FREQ)) //setFrequency 434MHz fehlgeschlagen -> RadioFreqNOK
while(true)
{ digitalWrite(LED_RED, HIGH); //Rotes Blinken mit 400mSec
delay(400);
digitalWrite(LED_RED, LOW);
delay(400);
}
//---
//Hinweis: Bei Verwendung eines High-Power RF69 wie z.B. RFM69HW, muss eine Sendeleistung
//mit dem Flag ishighpowermodule=true gesetzt werden.
//Sendeleistungsbereich: 14..20
//Das zweite Argument muss fuer das Modul 69HCW True sein.
rf69.setTxPower(20, true);
//---
//Encryption-Schluessel (Muss auf Sender und Empfaenger uebereinstimmen!
uint8_t key[]={0x10,0x51,0xB9,0xC0,0x10,0x51,0xB9,0xC0,0x15,0x00,0x74,0x60,0x15,0x00,0x74,0x60};
rf69.setEncryptionKey(key);
//rf69.sleep(); //So koennte man das Funkmodul abschalten, um Batteriestrom zu sparen
//Der Funkbaustein ist nun Sendebereit---
//Initialisierung des Timers fuer die TCC1-Handler ISR-----------------------------------------------------------------------
//***TCCx Timer-Setup for Feather M0 SAM D21
//***Divide the 48MHz system clock by 1 = 48MHz. Set division on Generic Clock Generator (GCLK) 4
GCLK->GENDIV.reg = GCLK_GENDIV_DIV(1) | GCLK_GENDIV_ID(4);
while (GCLK->STATUS.bit.SYNCBUSY);
//***Set the duty cycle to 50/50 HIGH/LOW. Enable GCLK 4. Set the clock source to 48MHz. Set clock source on GCLK 4
GCLK->GENCTRL.reg = GCLK_GENCTRL_IDC | GCLK_GENCTRL_GENEN | GCLK_GENCTRL_SRC_DFLL48M | GCLK_GENCTRL_ID(4);
while (GCLK->STATUS.bit.SYNCBUSY);
//***Enable the generic clock on GCLK4. Feed the GCLK4 to TCC0 and TCC1
GCLK->CLKCTRL.reg = GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK4 | GCLK_CLKCTRL_ID_TCC0_TCC1;
while (GCLK->STATUS.bit.SYNCBUSY);
//***Set prescaler to 1, 48Mhz. Reload timer on next prescaler clock, use if prescaler DIV is more than 1
//***TCC1->CTRLA.reg = TC_CTRLA_PRESCSYNC_PRESC | TCC_CTRLA_PRESCALER_DIV1;
//***Set the Nested Vector Interrupt Controller (NVIC) priority for TCCx to 0 (highest)
NVIC_SetPriority(TCC1_IRQn, 0);
//***Connect TCCx to Nested Vector Interrupt Controller (NVIC)
NVIC_EnableIRQ(TCC1_IRQn);
//***Enable TCCx MC0-interrupt
TCC1->INTENSET.reg = TCC_INTENSET_MC0;
//***Setup normal frequency operation on TCCx
TCC1->WAVE.reg |= TCC_WAVE_WAVEGEN_NFRQ;
while (TCC1->SYNCBUSY.bit.WAVE);
//***The timer frequency is calculated with the PER register:
//***Timer-Frequency = Generic-Clock-Frequency / (Timer-Prescaler * (Value-PER-Register + 1))
//***By default the counter counts up to the PER register value then overflows as it returns to 0.
//***In normal frequency (NFRQ) mode the timer's output toggles each time the PER value is reached.
//***Set the frequency of the timer to 0x000ADF by 48MHz and Prescale=1: TF=48MHz/(1*(0x000ADF+1))=17241.39Hz -> 58us
TCC1->PER.reg = DCC_HI; //DCC_HI=0x000ADF -> 58uSec. DCC-Signal
while(TCC1->SYNCBUSY.bit.PER);
//***Set counter compare: The CC[0] Register can be set at values between 0 and PER, and can trigger
//***interrupts at any point along the Timer-Cycle, whenever the timer's COUNT register matches the CC[0] value.
TCC1->CC[0].reg = DCC_HI; //DCC_HI=0x000ADF -> 58uSec. DCC-Signal
while(TCC1->SYNCBUSY.bit.CC0);
//***Enable timer TCCx
TCC1->CTRLA.bit.ENABLE = 1;
while (TCC1->SYNCBUSY.bit.ENABLE);
delay(100); //100ms warten
digitalWrite(LED_GREEN, LOW); //Gruene LED ausschalten
digitalWrite(LED_RED, HIGH); //Rote LED einschalten
digitalWrite(L6203_EN, LOW); //H-Bruecke ausschalten
NOTSTOP=true; //NOTSTOP aktiv -> Startsituation wird durch Funkhandregler aufgehoben
}
void loop() //-------------------------------------------------------------------------------------------------------------
{
//A: Messen der Akkuspannung -> Sofortige Abschaltung wenn Akkuspannung zu tief (Sonst Akku-Beschaedigung!)
//LiPo6s: Min. Akkuspannung pro Zelle: 3.3V -> 6*3.3V=19.8V
//LiPO6s: Abzueglich Schutzdiode: 19.8V-0.55V=19.25V
//LiPo6s: Spannungsteiler 1:10 -> 19.25/11=1.75
//10Bit-ADC -> 1024: D=1024/3.3*Umess
//1.75V = 543
if(analogRead(VBATPIN) < 543)
{ digitalWrite(L6203_EN, LOW); //H-Bruecke ausschalten
rf69.sleep(); //Funkmodul ausschalten
digitalWrite(LED_GREEN, LOW); //Gruene LED ausschalten
digitalWrite(LED_RED, LOW); //Rote LED ausschalten
while(true) //Akku muss geladen werden / RG-Blinklicht / Endlosschlaufe
{ digitalWrite(LED_RED, HIGH); //Rote LED einschalten
delay(500);
digitalWrite(LED_RED, LOW); //Rote LED ausschalten
digitalWrite(LED_GREEN, HIGH); //Gruene LED einschalten
delay(500);
digitalWrite(LED_GREEN, LOW); //Gruene LED ausschalten
}
} //end-if Akkuspannung pruefen
//B: Messen des Fahrstroms -> Durchschnittswert ueber 8 Messungen
//Es werden jeweils 8 Messungen beruecksichtigt und wenn 6 davon ueber dem Limit, dann NOTSTOP
//10Bit-ADC -> 1024: D=1024/3.3*Umess
//Rmess=0.082Ohm -> Limit von 7.0Ampere -> 0.574Volt -> 178
//Es gilt ein Zeitfenster von 8 Messungen als FIFO-Byte (averageCURR)
//Jedes gesetzte Bit entspricht einer Messung groesser dem Limit
//cheksumCURR addiert im Sinne einer Quersumme die gesetzten Bits (Ebenfalls FIFO)
//cheksumCURR muss bei jedem aus averageCURR links herausfallendes gesetzte Bit um 1 reduziert werden
//Zahlenbeispiel ueber mehrere Messloops:
//t00, I>5A: averageCURR=0000-0001; cheksumCURR=0000-0001
//t01, I>5A: averageCURR=0000-0011; cheksumCURR=0000-0011
//t02, I<5A: averageCURR=0000-0110; cheksumCURR=0000-0011
//t03, I>5A: averageCURR=0000-1101; cheksumCURR=0000-0111
//t04, I<5A: averageCURR=0001-1010; cheksumCURR=0000-0111
//t05, I>5A: averageCURR=0011-0100; cheksumCURR=0000-0111
//t06, I>5A: averageCURR=0110-1001; cheksumCURR=0000-1111
//t07, I<5A: averageCURR=1101-0010; cheksumCURR=0000-1111
//t08, I<5A: averageCURR=1010-0100; cheksumCURR=0000-0111 -> Quersumme um 1 rediziert
//t09, I<5A: averageCURR=0100-1000; cheksumCURR=0000-0011 -> Quersumme um 1 rediziert
//t10, I<5A: averageCURR=1001-0000; cheksumCURR=0000-0011
if(analogRead(CURRPIN) > 178)
{ if(averageCURR & 0x80) //Wenn vorderstes Bit links gesetzt ist, dann...
cheksumCURR = cheksumCURR >> 1; //Quersumme-1 weil linkes Bit nun aus dem Zeitfenster faellt
averageCURR = averageCURR | 0x01; //Mehr als 5A gemessen. Erstes Bit rechts mit OR auf 1 setzen
averageCURR = averageCURR << 1; //Alle Bits um eine Stelle nach links veschieben
cheksumCURR = cheksumCURR | 0x01; //Mehr als 5A gemessen. Erstes Bit rechts mit OR auf 1 setzen
cheksumCURR = cheksumCURR << 1; //Alle Bits um eine Stelle nach links veschieben
}
else
averageCURR = averageCURR << 1; //Alle Bits um eine Stelle nach links veschieben
//Es rueckt automatisch eine 0 nach)
//C: Reagieren auf Kurzschluss (Fahrstrom zu hoch), sonst Funkbefehl einlesen und verarbeiten
if(cheksumCURR >= 0x1F) //Fahrstrom zu hoch (Fuenfmal mehr als 5A gemessen)
{ digitalWrite(L6203_EN, LOW); //H-Bruecke sofort ausschalten
digitalWrite(LED_GREEN, LOW); //Gruene LED aus
digitalWrite(LED_RED, HIGH); //Rote LED ein
NOTSTOP=true; //NOTSTOP aktivieren
averageCURR = 0x00; //Alle Bits auf 0 setzen, Strommessung Reset
cheksumCURR = 0x00; //Alle Bits auf 0 setzen, Strommessung Reset
}
else //Fahrstrom korrekt
{
//Funk-Datenpaket falls vorhanden einlesen
if (rf69.waitAvailableTimeout(600))
if (rf69.recv(DCC_RadioPacket, &DCC_RadioPacketLEN))
if( (DCC_RadioPacket[2]==DCC_RadioPacket[0]) &&
(DCC_RadioPacket[3]==DCC_RadioPacket[1]) ) //Keine Uebermittlungsfehler?
{ if(NOTSTOP) //NOTSTOP ist aktiv -> Pruefen auf NOTSTOP-Deaktivierung
{ if( (DCC_RadioPacket[0]==0x78) && (DCC_RadioPacket[1]==0x01) ) //Notstopp-Deaktivierung erhalten
{ NOTSTOP=false; //Status aendern
digitalWrite(L6203_EN, HIGH); //H-Bruecke einschalten
digitalWrite(LED_RED, LOW); //Rote LED ausschalten
digitalWrite(LED_GREEN, HIGH); //Gruene LED einschalten
averageCURR = 0x00; //Strommessung Reset
cheksumCURR = 0x00; //Strommessung Reset
LokInit();
}
} //end-if Pruefen auf NOTSTOP-Deaktivierung
else //Zurzeit kein NOTSTOP aktiv
{ if( (DCC_RadioPacket[0]==0x78) && (DCC_RadioPacket[1]==0x80) ) //NOTSTOP-Befehl erhalten
{ NOTSTOP=true; //Status aendern
digitalWrite(L6203_EN, LOW); //H-Bruecke sofort ausschalten
digitalWrite(LED_GREEN, LOW); //Gruene LED aus
digitalWrite(LED_RED, HIGH); //Rote LED ein
}
else //Normaler Befehl erhalten
{ switch(DCC_RadioPacket[1])
{
case 0x91 : //LokPfiff-Befehl erhalten
LokPfiffDCCAdress=DCC_RadioPacket[0]; //Lok-Pfiff spaeter korrekt ausschalten
LokPfiffCounter=millis(); //LokPfiff-Dauer starten
LokPfiffOn=true; //Lokpfiff-Flag setzen
validDCCAdress=DCC_RadioPacket[0]; //DCC-Adresse umkopieren
validDCCMessage=DCC_RadioPacket[1]; //DCC-Funktion umkopieren
break;
case 0xBA : //LokSound-Befehl erhalten
if(LokSoundOn)
{ LokSoundOn=false;
validDCCAdress=DCC_RadioPacket[0]; //DCC-Adresse umkopieren
validDCCMessage=0xB2; //DCC-Funktion LokSound abschalten
}
else
{ LokSoundOn=true;
validDCCAdress=DCC_RadioPacket[0]; //DCC-Adresse umkopieren
validDCCMessage=DCC_RadioPacket[1]; //DCC-Funktion umkopieren
}
break;
default: validDCCAdress=DCC_RadioPacket[0]; //DCC-Adresse umkopieren
validDCCMessage=DCC_RadioPacket[1]; //DCC-Funktion umkopieren
break;
} //end-switch
} //end-if-else kein NOTSTOP-Paket erhalten
} //end-if-else kein NOTSTOP aktiv
} //end-if Gueltiges Paket erhalten
if(LokPfiffOn)
if((millis() - LokPfiffCounter) > 1000) //Lokpfiffdauer abgelaufen
{ LokPfiffOn=false;
validDCCAdress=LokPfiffDCCAdress; //DCC-Adresse der Lok mit LokPfiff-Ausloesung
validDCCMessage=0x90; //DCC-Funktion LokPfiff abschalten
}
} //end-if-else Fahrstrom korrekt
delay(300);
DCC_RadioPacket[0]=0x00; //Zwischenzeitlich eingetroffene Funkbefehle ignorieren
DCC_RadioPacket[1]=0x00;
}
void LokInit(void) //Lok vor der Fahrt initialisieren----------------------------------------------------
//HEX-BIN -> A=1010 B=1011 C=1100 D=1101 E=1110 F=1111
//F5-F8: 1011-WCB0 W=WAV/F8/Sound, C=CAB/F7/Kabinenlicht, B=Betriebsgeraeusch/F6 (F5=KohleSchaufeln)
//F0-F4: 100L-0V0S L=LED/F0/Licht&Grundsound, V=VAP/Dampf, S=SIG/F1/Lokpfeife (F4=Zylinderdampf, F2=Bremsquitschen)
{ validDCCAdress=0x03; //LokAdresse 3
validDCCMessage=0x00; //Decoder Reset
delay(4000);
validDCCMessage=0x90; //Licht&Grundsound ein
delay(1000);
validDCCMessage=0xBA; //Betriebsgeraeusch ein
delay(1000);
validDCCMessage=0x60; //Fahrstufe Halt
delay(1000);
validDCCMessage=0x90; //Licht&Grundsound ein wiederholen
delay(300);
DCC_RadioPacket[0]=0x00;
DCC_RadioPacket[1]=0x00;
}
void TCC1_Handler() //TCC1-Handler ISR--------------------------------------------------------------
{ //***Per Funk erhalten: 1 Byte (Speed/F0-F4/F5-F8/F9-F12)
//***Das DCC-Paket setzt sich zusammen aus:
//***17b Synchronbit 11111111111111111
//*** 1b Delimiter 0
//*** 8b DCC-Adresse #3 0000 0011
//*** 1b Delimiter 0
//*** 8b DCC-Funktion FFFF FFFF
//*** 1b Delimiter 0
//*** 8b Parity PPPP PPPP
//*** 1b Endbit 1
//***Total: 45b; 0..44; Paketdauer ca.: 11msec)
if (TCC1->INTFLAG.bit.MC0) // Test if an MC0 interrupt has occured
{ TCC1->INTFLAG.bit.MC0=1; // Clear the interrupt flag
switch(stHBrideNEXT)
{ case UP: digitalWrite(L6203_IN1, HIGH); //DCC-HI & DCCLO (H-Bruecke ON Source1 -> Sink2)
digitalWrite(L6203_IN2, LOW); //DCC-HI & DCCLO (H-Bruecke ON Source1 -> Sink2)
if(signalHI) //DCC-HI
stHBrideNEXT=downHI;
else //DCC-LO
stHBrideNEXT=holdUP;
break;
case holdUP: stHBrideNEXT=downLOW; //DCC-HI
break;
case downLOW: digitalWrite(L6203_IN1, LOW); //DCC-LO (H-Bruecke ON Source2 -> Sink1)
digitalWrite(L6203_IN2, HIGH); //DCC-LO (H-Bruecke ON Source2 -> Sink1)
stHBrideNEXT=downHOLD;
break;
case downHI: digitalWrite(L6203_IN1, LOW); //DCC-HI (H-Bruecke ON Source2 -> Sink1)
digitalWrite(L6203_IN2, HIGH); //DCC-HI (H-Bruecke ON Source2 -> Sink1)
case downHOLD: bitCount++; //DCC-HI & DCCLO
if(bitCount>45)
bitCount=0;
switch(bitCount)
{ //***Synchronbits:
case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
case 8: case 9: case 10: case 11: case 12: case 13: case 14: case 15:
case 16: case 17: signalHI=true; break;
//Separator-Bit (immer Log-0)
case 18: case 27: case 36: signalHI=false; break;
//***Adressbyte
case 19: signalHI=charADR & 0x80; break; //DCC-Adresse
case 20: signalHI=charADR & 0x40; break; //DCC-Adresse
case 21: signalHI=charADR & 0x20; break; //DCC-Adresse
case 22: signalHI=charADR & 0x10; break; //DCC-Adresse
case 23: signalHI=charADR & 0x08; break; //DCC-Adresse
case 24: signalHI=charADR & 0x04; break; //DCC-Adresse
case 25: signalHI=charADR & 0x02; break; //DCC-Adresse
case 26: signalHI=charADR & 0x01; break; //DCC-Adresse
//***DCC-Message
//***Speed -> 01RG-GGGG : Richtung/Geschwindigkeit
//***F0-F4 -> 100L-000S : L=LED/F0/Stirnlicht, S=SIG/F1
//***F5-F8 -> 1011-WC10 : C:CAB/F7/Kabinenlicht, W:WAV/F8/Sound
//***F9-F12 -> 1010-00C0 : C=COG/F10/Zahnradimitation
case 28: signalHI=charMSG & 0x80; break; //DCC-Message
case 29: signalHI=charMSG & 0x40; break; //DCC-Message
case 30: signalHI=charMSG & 0x20; break; //DCC-Message
case 31: signalHI=charMSG & 0x10; break; //DCC-Message
case 32: signalHI=charMSG & 0x08; break; //DCC-Message
case 33: signalHI=charMSG & 0x04; break; //DCC-Message
case 34: signalHI=charMSG & 0x02; break; //DCC-Message
case 35: signalHI=charMSG & 0x01; break; //DCC-Message
//***Paritaetsberechnung: XOR-Parity von Adressbyte DCC-Messagebyte
case 37: signalHI=(charMSG & 0x80)^(charADR & 0x80); break; //XOR-Parity berechnen
case 38: signalHI=(charMSG & 0x40)^(charADR & 0x40); break; //XOR-Parity berechnen
case 39: signalHI=(charMSG & 0x20)^(charADR & 0x20); break; //XOR-Parity berechnen
case 40: signalHI=(charMSG & 0x10)^(charADR & 0x10); break; //XOR-Parity berechnen
case 41: signalHI=(charMSG & 0x08)^(charADR & 0x08); break; //XOR-Parity berechnen
case 42: signalHI=(charMSG & 0x04)^(charADR & 0x04); break; //XOR-Parity berechnen
case 43: signalHI=(charMSG & 0x02)^(charADR & 0x02); break; //XOR-Parity berechnen
case 44: signalHI=(charMSG & 0x01)^(charADR & 0x01); break; //XOR-Parity berechnen
case 45: signalHI=true; //Endbit und neue gueltige DCC-Adresse/Funktion lesen
charMSG=validDCCMessage;
charADR=validDCCAdress;
break;
} //End-of-Switch
stHBrideNEXT = UP;
}
}
}
//======================================================================
// Titel: Funkhandregler
// Autor: Juerg Arnold, CH-Meilen
// Datum: 14. Mai 2022
// Zweck: Der Funkhandregler nimmt die Befehle vom Benutzer
// entgegen und schickt sie an die Basisstation.
//======================================================================
#include <SPI.h> //Fuer RFM69
#include <RH_RF69.h> //Fuer RFM69
//Definitionen Radio Setup
#define RF69_FREQ 434.0
#define RFM69_CS 8
#define RFM69_INT 3
#define RFM69_RST 4
RH_RF69 rf69(RFM69_CS, RFM69_INT); // Singleton-Instanz des Funkmodultreibers
#define LOK_ADRESS 0x03 //Fixe DCC-Lokadresse Nr. 3
#define SpeedPT A1 //0: Potentiometer Speed
#define BTN_STOP 6 //1: Button1 (NotStop)
#define BTN_FAHRT 10 //2: Button2 (Fahrtrichtung)
#define BTN_AUX1 11 //4: Button4 (Zurzeit belegt mit Lokpfiff)
#define BTN_AUX2 5 //3: Button3 (Zurzeit belegt mit Loksound on/off)
#define LED_BuiltIn 13 //Eingebaute rote LED
#define LED_Vorwaerts A4 //Rote LED Fahrt Vorwaerts (Rechts neben Poti)
#define LED_Rueckwaerts A2 //Rote LED Fahrt Rueckwaerts (Mitte)
#define LED_AUX A3 //Rote LED Links
#define DT_Out 400 //Debouncing-TimeOut zum Tasten entprellen
volatile unsigned long B_STOP_DT=0; //Button STOP Debouncing-Timer
volatile unsigned long B_FAHRT_DT=0; //Button STOP Debouncing-Timer
volatile unsigned long B_AUX1_DT=0; //Button AUX1 DebouncingTimer
volatile unsigned long B_AUX2_DT=0; //Button AUX2 DebouncingTimer
volatile bool NOTSTOP = false; //NOTSTOP-Taste gerdueckt
volatile uint8_t B_STATUS = 0; //Button-Nr. des zuletzt gedrueckten Buttons
//Geschwindigkeitswert in HEX gemaess Fahrstufentabelle
const uint8_t SpeedTable[2][29] =
{ {0x40,0x42,0x52,0x43,0x53,0x44,0x54,0x45,0x55,0x46,0x56,0x47,0x57,0x48,0x58, //Rueckwaerts
0x49,0x59,0x4A,0x5A,0x4B,0x5B,0x4C,0x5C,0x4D,0x5D,0x4E,0x5E,0x4F,0x5F},
{0x60,0x62,0x72,0x63,0x73,0x64,0x74,0x65,0x75,0x66,0x76,0x67,0x77,0x68,0x78, //Vorwaerts
0x69,0x79,0x6A,0x7A,0x6B,0x7B,0x6C,0x7C,0x6D,0x7D,0x6E,0x7E,0x6F,0x7F} };
uint8_t DCC_RadioPacket[4] = {0x00,0x00,0x00,0x00}; //Funkmodul -> Sendebytes zur Basisstation
uint8_t DCC_RadioPacketLEN = 4;
uint16_t ADC10BitValue = 0; //0..1023 vom AD/Wandler (Potentiometer, Akkuspannung)
uint16_t SPEEDlevel = 0; //Fahrtsufe 0..28
bool Vorwaerts = true; //Fahrtrichtung Vorwaerts oder Rueckwaerts
uint8_t counter = 0; //Counter-Variable
//***SETUP****************************************************************************************
void setup()
{ //Serial.begin(9600);
//Serial.println("ONLY FOR DEBUGGING");
// Digitale Inputs/Outputs initialisieren
pinMode(BTN_STOP, INPUT_PULLUP);
pinMode(BTN_FAHRT, INPUT_PULLUP);
pinMode(BTN_AUX2, INPUT_PULLUP);
pinMode(BTN_AUX1, INPUT_PULLUP);
pinMode(LED_Vorwaerts, OUTPUT);
pinMode(LED_Rueckwaerts, OUTPUT);
pinMode(LED_AUX, OUTPUT);
attachInterrupt(digitalPinToInterrupt(BTN_STOP), BTN_STOP_ISR, CHANGE);
attachInterrupt(digitalPinToInterrupt(BTN_FAHRT), BTN_FAHRT_ISR, CHANGE);
attachInterrupt(digitalPinToInterrupt(BTN_AUX2), BTN_AUX2_ISR, CHANGE);
attachInterrupt(digitalPinToInterrupt(BTN_AUX1), BTN_AUX1_ISR, CHANGE);
//Akkuspannung messen und auf Entscheid warten, ob geladen oder gefahren werden soll
ADC10BitValue = analogRead(A7); //Aktueller AD-Wert der Batteriemessung
counter = 0;
B_STATUS=0;
while(B_STATUS==0)
while( (counter<10) && (B_STATUS==0) )
{ digitalWrite(LED_AUX, HIGH);
if(ADC10BitValue>540) digitalWrite(LED_Rueckwaerts, HIGH); //>3.48Volt
else digitalWrite(LED_Rueckwaerts, LOW);
if(ADC10BitValue>606) digitalWrite(LED_Vorwaerts, HIGH); //>3.91Volt
else digitalWrite(LED_Vorwaerts, LOW);
delay(1000);
digitalWrite(LED_Vorwaerts, LOW);
digitalWrite(LED_Rueckwaerts, LOW);
digitalWrite(LED_AUX, LOW);
delay(66);
digitalWrite(LED_Vorwaerts, HIGH);
delay(66);
digitalWrite(LED_Vorwaerts, LOW);
delay(66);
digitalWrite(LED_Vorwaerts, HIGH);
delay(66);
digitalWrite(LED_Vorwaerts, LOW);
delay(66);
digitalWrite(LED_Vorwaerts, HIGH);
counter++;
}
//Initialisierung des Funkbausteins RFM69. Funk abschalten mit: rf69.sleep();
pinMode(RFM69_RST, OUTPUT);
digitalWrite(RFM69_RST, LOW);
//Feather RFM69 TX Test!
digitalWrite(RFM69_RST, HIGH); delay(10); //Manueller Reset
digitalWrite(RFM69_RST, LOW); delay(10); //Manueller Reset
if (!rf69.init()) //RFM69 Funkbausteininitialisierung fehlgeschlagen
while(true) RadioErrorLED();
//RFM69-Funkbaustein-Init OK! Konfiguration: 434.0MHz/Modulation-GFSK_Rb250Fd250/+13dbM LowPowerModule
if (!rf69.setFrequency(RF69_FREQ)) //setFrequency 434MHz fehlgeschlagen
while(true) RadioErrorLED();
//Bei High-Power RF69 (RFM69HW) -> Sendeleistung mit ishighpowermodule=true setzen!
//Sendeleistungsbereich: 14..20 Zweites Argument muss fuer Modul 69HCW True sein.
rf69.setTxPower(20, true);
//Encryption-Schluessel -> Uebereinstimmung Sender/Empfaenger!
uint8_t key[]={0x10,0x51,0xB9,0xC0,0x10,0x51,0xB9,0xC0,0x15,0x00,0x74,0x60,0x15,0x00,0x74,0x60};
rf69.setEncryptionKey(key); //Funkmodul ist nun sendebereit!
//NOTSTOP-Deaktivierungsbefehl an Basisstation
digitalWrite(LED_Rueckwaerts, LOW);
digitalWrite(LED_AUX, LOW);
DCC_RadioPacket[0]=0x78; //DCCAdressByte NOTSTOP
DCC_RadioPacket[1]=0x01; //DCCFunctionByte NOTSTOP Deaktivieren
DCC_RadioPacket[2]=DCC_RadioPacket[0]; //Fehlererkennung Basisstation
DCC_RadioPacket[3]=DCC_RadioPacket[1]; //Fehlererkennung Basisstation
for(counter=0; counter<10; counter++) //Den Befehl 10x schicken
{ digitalWrite(LED_Vorwaerts, HIGH);
rf69.send(DCC_RadioPacket, DCC_RadioPacketLEN);
rf69.waitPacketSent();
delay(100);
digitalWrite(LED_Vorwaerts, LOW);
delay(100);
}
digitalWrite(LED_BuiltIn, HIGH);
digitalWrite(LED_Vorwaerts, HIGH);
digitalWrite(LED_Rueckwaerts, LOW);
digitalWrite(LED_AUX, LOW);
Vorwaerts = true; //Fahrtrichtung Vorwaerts
NOTSTOP=false;
B_STATUS=0;
}
void loop()
{ switch(B_STATUS)
{
case 0: //Geschwindigkeitsbefehl aufbereiten
ADC10BitValue=analogRead(SpeedPT);
if(ADC10BitValue<9) SPEEDlevel=0;
else SPEEDlevel=(ADC10BitValue-9)/35+1;
DCC_RadioPacket[0]=LOK_ADRESS; //DCCAdressByte Lokadresse 3
if(Vorwaerts) DCC_RadioPacket[1]=SpeedTable[1][SPEEDlevel]; //Vorwaerts=SpeedTable[1]
else DCC_RadioPacket[1]=SpeedTable[0][SPEEDlevel]; //Rueckwaerts=SpeedTable[0]
break;
case 1: //AUX1 (Lokpfiff) aufbereiten
DCC_RadioPacket[0]=LOK_ADRESS; //DCCAdressByte Lokadresse 3
DCC_RadioPacket[1]=0x91; //DCCFunctionByte Lokpfiff ausloesen
//DCC_RadioPacket[1]=0x90; DCCFunctionByte Lokpfiff beenden (Erledigt die Basisstation)
B_STATUS=0;
break;
case 2: //Fahrtrichtungswechsel Vorwaerts/Rueckwaerts aufbereiten
if(NOTSTOP) //Betaetigen der Taste Fahrtrichtung hebt NOTSTOP auf
{ if(counter<10) //Der folgende Befehl im loop 10x schicken
{ DCC_RadioPacket[0]=0x78; //DCCAdressByte NOTSTOP
DCC_RadioPacket[1]=0x01; //DCCFunctionByte NOTSTOP deaktivieren
digitalWrite(LED_Rueckwaerts, LOW); //LED aus
digitalWrite(LED_AUX, LOW); //LED aus
if(counter %2) digitalWrite(LED_Vorwaerts, LOW);
else digitalWrite(LED_Vorwaerts, HIGH);
counter++;
}
else //NOTSTOP-Aufloesung wurde 10x verschickt
{ NOTSTOP=false;
B_STATUS=0;
Vorwaerts=true;
digitalWrite(LED_Vorwaerts, HIGH);
}
}
else //Normalbetrieb (kein NOTSTOP)
{ if(Vorwaerts)
{ Vorwaerts=false;
digitalWrite(LED_Vorwaerts, LOW);
digitalWrite(LED_Rueckwaerts, HIGH);
}
else
{ Vorwaerts=true;
digitalWrite(LED_Vorwaerts, HIGH);
digitalWrite(LED_Rueckwaerts, LOW);
}
B_STATUS=0;
}
break;
case 3: //NOTSTOP
counter=0; //Wird bei der Ausloesung des NOTSTOPs benoetigt (siehe case 2)
digitalWrite(LED_Vorwaerts, HIGH); //LED ein
digitalWrite(LED_Rueckwaerts, HIGH); //LED ein
digitalWrite(LED_AUX, HIGH); //LED ein
DCC_RadioPacket[0]=0x78; //DCCAdressByte NOTSTOP
DCC_RadioPacket[1]=0x80; //DCCFunctionByte NOTSTOP aktivieren
break;
case 4: //AUX2 (Loksound) aufbereiten
DCC_RadioPacket[0]=LOK_ADRESS; //DCCAdressByte Lokadresse 3
DCC_RadioPacket[1]=0xBA; //DCCFunctionByte Betriebsgeraeusch ein
//DCC_RadioPacket[1]=0xB2; DCCFunctionByte Betriebsgeraeusch aus (Erledigt die Basisstation)
B_STATUS=0;
break;
}
//Paket versenden
DCC_RadioPacket[2]=DCC_RadioPacket[0]; //Fehlererkennung Basisstation
DCC_RadioPacket[3]=DCC_RadioPacket[1]; //Fehlererkennung Basisstation
rf69.send(DCC_RadioPacket, DCC_RadioPacketLEN); //Kommando schicken
rf69.waitPacketSent();
delay(225);
}
void RadioErrorLED(void)
{ digitalWrite(LED_Vorwaerts, HIGH);
delay(200);
digitalWrite(LED_Vorwaerts, LOW);
digitalWrite(LED_Rueckwaerts, HIGH);
delay(200);
digitalWrite(LED_Rueckwaerts, LOW);
digitalWrite(LED_AUX, HIGH);
delay(200);
digitalWrite(LED_AUX, LOW);
digitalWrite(LED_Rueckwaerts, HIGH);
delay(200);
digitalWrite(LED_Rueckwaerts, LOW);
}
void BTN_AUX2_ISR() //Lokpfiff -> Weisser Button links
{ if((millis() - B_AUX2_DT) > DT_Out)
{ B_AUX2_DT = millis();
if(NOTSTOP) B_STATUS = 3;
else B_STATUS = 1;
}
}
void BTN_FAHRT_ISR() //Fahrtrichtung -> Gelber Button
{ if((millis() - B_FAHRT_DT) > DT_Out)
{ B_FAHRT_DT = millis();
B_STATUS = 2;
}
}
void BTN_STOP_ISR() //NOTSTOP -> Roter Button
{ if((millis() - B_STOP_DT) > DT_Out)
{ B_STOP_DT = millis();
NOTSTOP = true;
B_STATUS = 3;
}
}
void BTN_AUX1_ISR() //Loksound -> Weisser Button rechts
{ if((millis() - B_AUX1_DT) > DT_Out)
{ B_AUX1_DT = millis();
if(NOTSTOP) B_STATUS = 3;
else B_STATUS = 4;
}
}
//======================================================================
// Titel: HG4/4 Lok
// Autor: Juerg Arnold, CH-Meilen
// Datum: 29. Mai 2023
// Der Funkhandregler nimmt die Fahrbefehle für Lok1 und Lok2 entgegen.
// Die jeweilige Lok erhält per Funk den Fahrbefehl und regelt Geschwindigkeit/Fahrtrichtung.
// Stirnlampenwechsel gemäss CH-Vorbild und Fahrtrichtung. Bei Lokstillstand Kabinenlicht ein.
// Microcontroller: ADAFRUIT Feather M0 Radio with RFM69 Packet Radio 433MHz Art.-Nr. 3177
// Motor: MAXON RE30/60Watt 18Volt Art.-Nr. 268213/310006
// Servokontroller: MAXON ESCON Modul 50/5 Art.-Nr. 438725
// 1. Sendebyte: 0SSSSSSS Lok-A Richtung1 SSSSSSS=Speed
// 1SSSSSSS Lok-A Richtung2 Speed: 0..127 (7 Bit)
// 2. Sendebyte: 0SSSSSSS Lok-B Richtung1 SSSSSSS=Speed
// 1SSSSSSS Lok-B Richtung2 Speed: 0..127 (7 Bit)
#include <SPI.h> //Fuer RFM69
#include <RH_RF69.h> //Fuer RFM69 3177
#define RF69_FREQ 434.0 //Radio Setup in MHz
#define RFM69_CS 8 //Radio Setup
#define RFM69_INT 3 //Radio Setup
#define RFM69_RST 4 //Radio Setup
#define LED_RED 13 //Rote eingebaute LED
#define LED_CAB A2 //Führerstandslicht
#define LED_VO A3 //Stirnlampen Oben und Links
#define LED_VL 5 //Stirnlampen Oben und Links
#define LED_VR 6 //Stirnlampen Rechts
#define LED_HOL A5 //Rücklicht Oben und Links
#define LED_HR A4 //Rücklicht Rechts
#define A_IN1 A0 //MAXON Servokontroller AnalogIN1 Voltage-Sollwert
#define D_IN2 11 //MAXON Servokontroller IN2 Freigabe Hi=Aktiv
#define D_IN3 12 //MAXON Servokontroller IN3 Drehrichtung LO=CW / HI=CCW
#define ZeroSpeed 5 //Geschwindigkeitsvorgabe vom Funkhandregler ist auf Null
RH_RF69 rf69(RFM69_CS, RFM69_INT); //Singleton-Instanz des Funkmodultreibers
uint8_t RFMPKG[] = {0x00, 0x00}; //Funkmodul Empfangsbytes
uint8_t RFMLEN = 2; //Funkmodul Empfangsbytelaenge
uint16_t Speed = 0x0000; //Geschwindigkeitskommando für den 10Bit DAC des uControllers
uint8_t PKG = 0x00; //Erhaltenes Befehlspaket
void setup()
{ //Serial.begin(9600); //Serial.println("ONLY FOR DEBUGGING");
analogReadResolution(8); //AD-Wandler liefert Werte zwischen 0..255
pinMode(LED_RED, OUTPUT); //Rote eingebaute LED aktivieren
pinMode(LED_CAB, OUTPUT); //Führerstandslicht aktivieren
pinMode(LED_VO, OUTPUT); //Stirnlampen Oben aktivieren
pinMode(LED_VL, OUTPUT); //Stirnlampen Links aktivieren
pinMode(LED_VR, OUTPUT); //Stirnlampen Rechts aktivieren
pinMode(LED_HOL, OUTPUT); //Rücklicht Oben und Links aktivieren
pinMode(LED_HR, OUTPUT); //Rücklicht Rechts aktivieren
pinMode(D_IN2, OUTPUT); //D_IN2 Freigabe
pinMode(D_IN3, OUTPUT); //D_IN3 Drehrichtung
analogWrite(A_IN1, 0x000); //Speed-Sollwert auf 0 setzen
digitalWrite(D_IN2, LOW); //D_IN2 Freigabe OFF
digitalWrite(D_IN3, LOW); //D_IN3 Drehrichtung
digitalWrite(LED_RED,HIGH); //Rote eingebaute LED einschalten
digitalWrite(LED_VO, LOW); //Stirnlampe oben
digitalWrite(LED_VL, LOW); //Stirnlampe unten links
digitalWrite(LED_VR, LOW); //Stirnlampe unten rechts
digitalWrite(LED_HOL, LOW); //Ruecklampe oben und unten links
digitalWrite(LED_HR, LOW); //Ruecklampe unten rechts
digitalWrite(LED_CAB, LOW); //Fuehrerstandslicht
//Initialisierung des Funkbausteins RFM69. Funk abschalten mit: rf69.sleep();
pinMode(RFM69_RST, OUTPUT);
digitalWrite(RFM69_RST, LOW);
//Feather RFM69 TX Test!
digitalWrite(RFM69_RST, HIGH); delay(10); //Manueller Reset
digitalWrite(RFM69_RST, LOW); delay(10); //Manueller Reset
if (!rf69.init()) //RFM69 Funkbausteininitialisierung fehlgeschlagen
{ digitalWrite(LED_VO,HIGH); while(true) delay(33);} //Obere Stirnlampe an.
//RFM69-Funkbaustein-Init OK! Konfiguration: 434.0MHz/Modulation-GFSK_Rb250Fd250/+13dbM LowPowerModule
if (!rf69.setFrequency(RF69_FREQ)) //setFrequency 434MHz fehlgeschlagen
{ digitalWrite(LED_VO,HIGH); while(true) delay(33);} //Obere Stirnlampe an.
//Bei High-Power RF69 (RFM69HW) -> Sendeleistung mit ishighpowermodule=true setzen!
//Sendeleistungsbereich: 14..20 Zweites Argument muss fuer Modul 69HCW True sein.
rf69.setTxPower(20, true);
//Encryption-Schluessel -> Uebereinstimmung Sender/Empfaenger!
uint8_t key[]={0x10,0x51,0xB9,0xC0,0x10,0x51,0xB9,0xC0,0x15,0x00,0x74,0x60,0x15,0x00,0x74,0x60};
rf69.setEncryptionKey(key); //Funkmodul ist nun sendebereit!
//Alles OK und bereit zur Abfahrt
digitalWrite(LED_RED, HIGH); //Rote LED des Mikrokontrollers
digitalWrite(LED_VR, HIGH); //Stirnlampe vorne/rechts ->Immer ON
digitalWrite(LED_HR, HIGH); //Stirnlampe hinten/rechts->Immer ON
digitalWrite(LED_VO, HIGH); //Stirnlampe vorne/oben
digitalWrite(LED_VL, HIGH); //Stirnlampe vorne/links
digitalWrite(LED_HOL, LOW); //Stirnlampe hinten/oben/links
digitalWrite(D_IN2, HIGH); //D_IN2 Freigabe ON
}
void loop()
{ if (rf69.waitAvailableTimeout(200)) //Alle ca. 166mSec sollte ein Funkbefehl eintreffen
if (rf69.recv(RFMPKG, &RFMLEN))
{ PKG=RFMPKG[0]; //Lok-A: 8Bit-Paket umkopieren (Erstes Sendebyte)
//Für Lok-B zweites Sendebyte → PKG=RFMPKG[1]
Speed=0x0000; //VariablenReset
Speed=PKG & 0x7F; //7Bit-Speedbefehl auslesen
Speed=Speed << 3; //Aus 7Bit mach 10Bit weil der Mikrokontroller-Ausgang A0 ist ein 10Bit-DAC
if(Speed < ZeroSpeed) digitalWrite(LED_CAB, HIGH); //Fuehrerstandslicht ON;
else digitalWrite(LED_CAB, LOW); //Fuehrerstandslicht OFF
if(PKG & 0x80) //Fahrtrichtung auslesen
{ digitalWrite(LED_VO, LOW); //Stirnlampe vorne/oben
digitalWrite(LED_VL, LOW); //Stirnlampe vorne/links
digitalWrite(LED_HOL, HIGH); //Stirnlampe hinten/oben/links
digitalWrite(D_IN3, LOW); //Servokontroller Drehrichtung LO=CW
analogWrite(A_IN1, Speed); //Analoger Speed-Sollwert im Servokontroller setzen
}
else
{ digitalWrite(LED_VO, HIGH); //Stirnlampe vorne/oben
digitalWrite(LED_VL, HIGH); //Stirnlampe vorne/links
digitalWrite(LED_HOL, LOW); //Stirnlampe hinten/oben/links
digitalWrite(D_IN3, HIGH); //Servokontroller Drehrichtung HI=CCW
analogWrite(A_IN1, Speed); //Analoger Speed-Sollwert im Servokontroller setzen
}
}
}
//======================================================================
// Titel: HG4/4-Funk
// Autor: Juerg Arnold, CH-Meilen
// Datum: 29. Mai 2023
// Der Funkhandregler nimmt die Fahrbefehle für Lok2 und Lok3 entgegen.
// Die jeweilige Lok erhält per Funk den Fahrbefehl und regelt Geschwindigkeit/Fahrtrichtung.
// Stirnlampenwechsel gemäss CH-Vorbild und Fahrtrichtung. Bei Lokstillstand Kabinenlicht ein.
// Microcontroller: ADAFRUIT Feather M0 Radio with RFM69 Packet Radio 433MHz Art.-Nr. 3177
// Motor: MAXON RE30/60Watt 18Volt Art.-Nr. 268213/310006
// Servokontroller: MAXON ESCON Modul 50/5 Art.-Nr. 438725
// 1. Sendebyte: 0SSSSSSS Lok-A Richtung1 SSSSSSS=Speed
// 1SSSSSSS Lok-A Richtung2 Speed: 0..127 (7 Bit)
// 2. Sendebyte: 0SSSSSSS Lok-B Richtung1 SSSSSSS=Speed
// 1SSSSSSS Lok-B Richtung2 Speed: 0..127 (7 Bit)
#include <SPI.h> //RFM69 3177 Radio
#include <RH_RF69.h> //RFM69 3177 Radio
#define RF69_FREQ 434.0 //Radio Setup in MHz
#define RFM69_CS 8 //Radio Setup
#define RFM69_INT 3 //Radio Setup
#define RFM69_RST 4 //Radio Setup
#define LEDred 13 //Eingebaute rote LED
#define SPEEDApin A1 //10kOhm Potentiometer
#define DIRApin 6 //Schalter auf Potentiometer1
#define SPEEDBpin A3 //10kOhm Potentiometer
#define DIRBpin 5 //Schalter auf Potentiometer1
#define BATpin A7 //Spannungsteiler für BAT-Prüfung (3.6V..4.3V)
#define BATlow 35 //Formel:ADC=Vbat*64/6.6; Vaccu<3.6 Volt; ADC:6b
#define SendWait 100 //Wartezeit nach Packetversand
#define TimeoutButton 400 //Debouncing-TimeOut in Millisekunden zum Tasten entprellen
#define PotiLeftStop 3 //ADC-Wert der als Potentiometer auf Null gewertet wird
#define PotiRightStop 60 //ADC-Wert der als Potentiometer auf Null gewertet wird
#define Winking 50 //Rote LED ON bei Poti nicht am linken Anschlag bei Richtungswechsel
RH_RF69 rf69(RFM69_CS, RFM69_INT); // Singleton-Instanz des Funkmodultreibers
uint8_t RFMPKG[2] = {0x00, 0x00}; //Funkmodul -> 1. Sendebyte zur HG 4/4 Nr2, 2. Sendebyte zu HG4/4 Nr3
uint8_t RFMLEN = 2; //Funkmodul -> Sendebytelänge
uint8_t SpeedA = 0; //PotiA Speed
uint8_t SpeedB = 0; //PotiB Speed
uint8_t DirA = 0x00; //ButtonA Fahrtrichtung LocoA 0x00->DirA1, 0x40->DirA2
uint8_t DirB = 0x80; //ButtonB Fahrtrichtung LocoB 0x80->DirB1, 0xC0->DirB2
uint8_t Vbat = 0; //Batteriespannung aktuelle Messung
volatile unsigned long DebounceA=0; //ButtonA Debouncing-Timer
volatile unsigned long DebounceB=0; //ButtonB Debouncing-Timer
volatile bool DIRAflag=false; //Flag das anzeigt, dass Fahrtrichtungswechsel gedrückt wurde
volatile bool DIRBflag=false; //Flag das anzeigt, dass Fahrtrichtungswechsel gedrückt wurde
//***SETUP****************************************************************************************
void setup()
{ //Serial.begin(9600); //Serial.println("ONLY FOR DEBUGGING");
analogReadResolution(7); //6-Bit-ADC-Einstellung für 0..127
pinMode(LEDred, OUTPUT); //Rote eingebaute LED aktivieren
pinMode(DIRApin, INPUT_PULLUP); // Digitaler Input-A initialisieren
pinMode(DIRBpin, INPUT_PULLUP); // Digitaler Input-B initialisieren
attachInterrupt(digitalPinToInterrupt(DIRApin), DIRApinISR, FALLING);
attachInterrupt(digitalPinToInterrupt(DIRBpin), DIRBpinISR, FALLING);
//Beide Speedbuttons voll offen bedeutet kein Fahrbetrieb dafür Batterie laden
if( (analogRead(SPEEDApin) > PotiRightStop) && (analogRead(SPEEDBpin) > PotiRightStop) )
while(true) //Hier geht es nicht mehr weiter
{ digitalWrite(LEDred,HIGH); delay(100); digitalWrite(LEDred,LOW); delay(900); }
//Initialisierung des Funkbausteins RFM69. Funk abschalten mit: rf69.sleep();
pinMode(RFM69_RST, OUTPUT);
digitalWrite(RFM69_RST, LOW);
//Feather RFM69 TX Test!
digitalWrite(RFM69_RST, HIGH); delay(10); //Manueller Reset
digitalWrite(RFM69_RST, LOW); delay(10); //Manueller Reset
if (!rf69.init()) //RFM69 Funkbausteininitialisierung fehlgeschlagen
while(true)
{ digitalWrite(LEDred,HIGH); delay(100); digitalWrite(LEDred,LOW); delay(100); }
//RFM69-Funkbaustein-Init OK! Konfiguration: 434.0MHz/Modulation-GFSK_Rb250Fd250/+13dbM LowPowerModule
if (!rf69.setFrequency(RF69_FREQ)) //setFrequency 434MHz fehlgeschlagen
while(true)
{ digitalWrite(LEDred,HIGH); delay(100); digitalWrite(LEDred,LOW); delay(100); }
//Bei High-Power RF69 (RFM69HW) -> Sendeleistung mit ishighpowermodule=true setzen!
//Sendeleistungsbereich: 14..20 Zweites Argument muss fuer Modul 69HCW True sein.
rf69.setTxPower(20, true);
//Encryption-Schluessel -> Uebereinstimmung Sender/Empfaenger!
uint8_t key[]={0x10,0x51,0xB9,0xC0,0x10,0x51,0xB9,0xC0,0x15,0x00,0x74,0x60,0x15,0x00,0x74,0x60};
rf69.setEncryptionKey(key); //Funkmodul ist nun sendebereit!
//Beide Speedbuttons auf Null und damit Kavalierstart vermeiden
while( (analogRead(SPEEDApin) > PotiLeftStop) || (analogRead(SPEEDBpin)> PotiLeftStop) )
{ digitalWrite(LEDred,HIGH); delay(Winking); digitalWrite(LEDred,LOW); delay(Winking);
digitalWrite(LEDred,HIGH); delay(Winking); digitalWrite(LEDred,LOW); delay(4*SendWait);
}
//Alles OK und bereit zur Abfahrt
digitalWrite(LEDred, HIGH); //Eingebaute rote LED einschalten
}
void loop()
{
Vbat = analogRead(BATpin); //Akkuspannung prüfen
if(Vbat < BATlow) //Akkuspannung kleiner 3.6 Volt
{ while(true) //Hier geht es nicht mehr weiter
{ digitalWrite(LEDred,HIGH); delay(100); digitalWrite(LEDred,LOW); delay(900); }
}
SpeedA = analogRead(SPEEDApin); //Geschwindigkeits-DrehknopfA lesen
SpeedB = analogRead(SPEEDBpin); //Geschwindigkeits-DrehknopfB lesen
if(DIRAflag)
if(SpeedA > PotiLeftStop)
{ SpeedA=0;
digitalWrite(LEDred,HIGH); delay(Winking); digitalWrite(LEDred,LOW); delay(Winking);
digitalWrite(LEDred,HIGH); delay(Winking); digitalWrite(LEDred,LOW); delay(2*SendWait);
}
else
{ if(DirA == 0x00) DirA=0x80; //0x80->LocoA/Dir2
else DirA=0x00; //0x00->LocoA/Dir1
digitalWrite(LEDred, HIGH);
DIRAflag=false;
}
if(DIRBflag)
if(SpeedB > PotiLeftStop)
{ SpeedB=0;
digitalWrite(LEDred,HIGH); delay(Winking); digitalWrite(LEDred,LOW); delay(Winking);
digitalWrite(LEDred,HIGH); delay(Winking); digitalWrite(LEDred,LOW); delay(2*SendWait);
}
else
{ if(DirB == 0x00) DirB=0x80; //0x80->LocoB/Dir2
else DirB=0x00; //0x80->LocoB/Dir2
digitalWrite(LEDred, HIGH);
DIRBflag=false;
}
//LocoA-Kommando zusammenstellen und schicken
RFMPKG[0] = DirA; //LocoA und Fahrtrichtung
RFMPKG[0] = RFMPKG[0] | SpeedA; //7-Bit-SpeedwertA setzen
//LocoB-Kommando zusammenstellen und schicken
RFMPKG[1] = DirB; //LocoB und Fahrtrichtung
RFMPKG[1] = RFMPKG[1] | SpeedB; //7-Bit-SpeedwertB setzen
rf69.send(RFMPKG, RFMLEN); //Kommando schicken
rf69.waitPacketSent();
delay(SendWait);
}
void DIRApinISR() //ISR-RoutineA
{ if((millis() - DebounceA) > TimeoutButton ) //Debouncing-TimeOut zum Tasten entprellen
{ DebounceA = millis(); DIRAflag=true;
}
}
void DIRBpinISR() //ISR-RoutineB
{ if((millis() - DebounceB) > TimeoutButton ) //Debouncing-TimeOut zum Tasten entprellen
{ DebounceB = millis(); DIRBflag=true;
}
}