/*
POC Weiche, Fahrstraße, Zieltaste
https://forum.arduino.cc/t/schattenbahnhofsteuerung/1208510/46
Simulation: https://wokwi.com/projects/386439244339395585
by noiasca
2024-01-09 Grundgerüst
2024-01-11 MCP23S17 eingebunden, Debug Meldungen, erstes Time Management
2024-02-15 fixed class WeicheMCP https://forum.arduino.cc/t/schattenbahnhofsteuerung/1208510/63
2024-03-03 fixed class WeicheMCP, 4 turnouts
Anmerkung: durch die fehlende SPI Kommunikation wird der Sketch im Wokwi Simualtor nicht funktionieren
*/
#include <Streaming.h> // Streaming by Peter Polidoro (boardmanager) - macht das Serial.print einfacher.
#include <Adafruit_MCP23X17.h>
constexpr uint8_t csPinA {5}; // chip select für ersten MCP23S17
constexpr uint8_t buttonPinA {A0}; // nur mal ein Test Button
constexpr uint8_t buttonPinB {A1}; // noch ein Test Button
constexpr uint8_t buttonPinC {A2}; // nur mal ein Test Button
constexpr uint8_t buttonPinD {A3}; // noch ein Test Button
Adafruit_MCP23X17 mcpA; // ein MCP Objekt muss angelegt werden - @todo: Umbauen auf Array
// Weichen an einem MCP23S17
class WeicheMCP {
Adafruit_MCP23X17 &mcp; // Referenz auf ein MCP Objekt - ein konkreter MCP23S17
const uint8_t pinGerade; // Ausgang für Gerade - hardcoded auf Port A
const uint8_t pinAbbiegen; // Ausgang für abbiegen - hardcoded auf Port A
const uint8_t pinRueckGerade; // Rückmeldung für Gerade - hardcoded auf Port B
const uint8_t pinRueckAbbiegen; // Rückmeldung für Abbiegen - hardcoded auf PortB
uint32_t previousMillis = 0; // Zeitmanagement
const uint16_t interval = 2000; // hard off der Magnetantriebe
bool isActive = false; // Magnetantrieb ist ein oder aus
uint8_t direction = 0; // 0 gerade, 1 abbiegen
public:
WeicheMCP(Adafruit_MCP23X17 &mcp, uint8_t pinGerade, uint8_t pinAbbiegen, uint8_t pinRueckGerade, uint8_t pinRueckAbbiegen /*, welcher MCP*/) :
mcp{mcp},
pinGerade {pinGerade},
pinAbbiegen {pinAbbiegen},
pinRueckGerade {pinRueckGerade},
pinRueckAbbiegen {pinRueckAbbiegen} {}
// Einstellungen im setup()
void begin() {
mcp.pinMode(pinGerade, OUTPUT);
mcp.pinMode(pinAbbiegen, OUTPUT);
mcp.pinMode(pinRueckGerade, INPUT); // tbc INPUT_PULLUP?
mcp.pinMode(pinRueckAbbiegen, INPUT); // tbc INPUT_PULLUP?
}
// Aktion für Geradeausfahrt
int gerade() {
if (!isActive) {
mcp.digitalWrite(pinGerade, HIGH);
direction = 0;
isActive = true;
previousMillis = millis();
Serial << "gerade " << pinGerade << '\n';
return 0; // OK
}
return -1; // befehl nicht angenommen
}
// Aktion für abbiegen
// @todo - codeduplikate von gerade/abbieben in eine Funktion auslagern
int abbiegen() {
if (!isActive) {
mcp.digitalWrite(pinAbbiegen, HIGH);
direction = 1;
isActive = true;
previousMillis = millis();
Serial << "abbiegen " << pinAbbiegen << '\n';
return 0; // ok
}
return -1; // befehl nicht angenommen
}
// Rückgabe des aktuellen Status
int getStatus() {
return direction;
};
// RUN: automatische Abschaltung der Magnetartikel
void update(uint32_t currentMillis = millis()) {
if (isActive && currentMillis - previousMillis > interval) {
mcp.digitalWrite(pinGerade, LOW);
mcp.digitalWrite(pinAbbiegen, LOW);
isActive = false;
Serial << "abschalten " << '\n';
}
}
};
WeicheMCP weiche[] {
{mcpA, 0, 1, 8, 9}, // Anlage einer Weiche
{mcpA, 2, 3, 10, 11}, // Anlage einer weiteren Weiche (am gleichen MCP)
{mcpA, 4, 5, 12, 13}, // Anlage einer Weiche
{mcpA, 6, 7, 14, 15}, // Anlage einer weiteren Weiche (am gleichen MCP)
};
// Eingänge der Buttons initialisieren
void buttonBegin() {
pinMode(buttonPinA, INPUT_PULLUP);
pinMode(buttonPinB, INPUT_PULLUP);
pinMode(buttonPinC, INPUT_PULLUP);
pinMode(buttonPinD, INPUT_PULLUP);
}
// Buttons abfragen und etwas ausführen
void buttonRead() {
if (digitalRead(buttonPinA) == LOW) weiche[0].gerade();
if (digitalRead(buttonPinB) == LOW) weiche[0].abbiegen();
if (digitalRead(buttonPinC) == LOW) weiche[1].gerade();
if (digitalRead(buttonPinD) == LOW) weiche[1].abbiegen();
}
// Befehle über die serielle Schnittstelle
void serialRead() {
int c = Serial.read();
switch (c) {
case -1 : break;
case '1': weiche[0].gerade(); break;
case '2': weiche[0].abbiegen(); break;
case '3': weiche[1].gerade(); break;
case '4': weiche[1].abbiegen(); break;
case '5': weiche[2].gerade(); break;
case '6': weiche[2].abbiegen(); break;
case '7': weiche[3].gerade(); break;
case '8': weiche[3].abbiegen(); break;
}
}
void setup() {
Serial.begin(115200);
Serial << "Weichensteuerung\n";
delay(500);
SPI.begin();
if (!mcpA.begin_SPI(csPinA)) {
Serial << "mcpA Error.\n";
}
Serial << "D120\n";
for (auto &i : weiche) {
i.begin(); // jede Weiche initialisieren
}
buttonBegin();
Serial << "ende setup\n";
}
void loop() {
buttonRead(); // taster auslesen
serialRead();
// Hintergrund-Aktivitäten auslösen
for (auto &i : weiche) i.update(); // jeder Weiche etwas Zeit für Hintergrundaktivitäten geben
}
//