schnurloses Gleisbildstellwerk

Antworten
tomy99
Beiträge: 2
Registriert: 20.06.2026, 13:02

schnurloses Gleisbildstellwerk

Beitrag von tomy99 » 23.06.2026, 13:17

Hallo,
an genau sowas probiere ich mich auch gerade, mit Hilfe von KI, aber leider bekomme ich keine Verbindung zur Z21 (schwarz)hin.
Wäre es möglich den Code aus dem Video zu bekommen? Perfekt wäre für Adruino IDE.

Ich hatte allerdings für jede LED einen separaten PIN genutzt so brauch man keine NPN / PNP. Aber vielleicht spart man bei deiner Methode ja PINS ein und brauch keine Erweiterung aller MCP23017.

Ich stelle mal meinen Code ein, vielleicht findet ja jemand den Fehler.
Der Webserver ist erstmal ein „nice to have“, und nicht wirklich nötig.

Fehler stecken sicher irgendwo im "Z21 Weichenbefehl senden" (void sendWeiche). Aber wir (KI und ich) haben es an einen ganzen Nachmittag nicht hinbekommen eine Verbindung aufzubauen.

Folgende Funktion sind im Code:

-4 Weichen (Adressen 120–123)
-Entprellung (Taster)
-LED-Update
-Z21-Rückmeldung
-Start-Synchronisation
-Weichenbefehl senden

Loop:

-Z21 Rückmeldungen empfangen
-Taster entprellen
-Weichen schalten
-LEDs aktualisieren

Quelltext:

Code: Alles auswählen

#include <wifi_config.h>		//WLAN Login
#include <WiFi.h>
#include <WiFiUdp.h>
#include <WebServer.h>
#include <EEPROM.h>

IPAddress z21(192,168,0,111);

WiFiUDP udp;
WebServer server(80);

#define EEPROM_SIZE 64

// -----------------------------
// Weichen-Struct
// -----------------------------
struct Weiche {
  uint16_t adresse;      // DCC-Adresse
  uint8_t tasterPin;     // Taster-Pin
  uint8_t ledGerade;     // LED für "gerade"
  uint8_t ledAbzweig;    // LED für "abzweig"

  bool state;            // Weichenstellung (false = gerade)
  bool lastState;        // letzter Rohwert
  bool stableState;      // entprellter Zustand
  unsigned long lastChangeTime;
};

// -----------------------------
// 4 Weichen
// Adresse, Taster, Led1, LED2
// -----------------------------
Weiche weichen[] = {
  { 120, 15, 18, 19, false, HIGH, HIGH, 0 }, 
  { 121, 4, 21, 22, false, HIGH, HIGH, 0 },
  { 122, 13, 23, 25, false, HIGH, HIGH, 0 },
  { 123, 14, 26, 27, false, HIGH, HIGH, 0 }
};

const int anzahlWeichen = sizeof(weichen) / sizeof(weichen[0]);
const unsigned long debounceTime = 50;

// -----------------------------
// LED-Update
// -----------------------------
void updateLEDs(Weiche &w) {
  if (w.state == false) {        // gerade
    digitalWrite(w.ledGerade, HIGH);
    digitalWrite(w.ledAbzweig, LOW);
  } else {                       // abzweig
    digitalWrite(w.ledGerade, LOW);
    digitalWrite(w.ledAbzweig, HIGH);
  }
}

// -----------------------------
// EEPROM: Weichen speichern
// -----------------------------
void saveWeichenToEEPROM() {
  Serial.println("Speichere Weichen in EEPROM...");
  for (int i = 0; i < anzahlWeichen; i++) {
    EEPROM.write(i, weichen[i].state ? 1 : 0);

    Serial.print("  Weiche ");
    Serial.print(weichen[i].adresse);
    Serial.print(" -> ");
    Serial.println(weichen[i].state ? "Abzweig" : "Gerade");
  }
  EEPROM.commit();
}

// -----------------------------
// EEPROM: Weichen laden
// -----------------------------
void loadWeichenFromEEPROM() {
  Serial.println("Lade Weichen aus EEPROM...");
  for (int i = 0; i < anzahlWeichen; i++) {
    uint8_t v = EEPROM.read(i);
    weichen[i].state = (v != 0);
    updateLEDs(weichen[i]);

    Serial.print("  Weiche ");
    Serial.print(weichen[i].adresse);
    Serial.print(" -> ");
    Serial.println(weichen[i].state ? "Abzweig" : "Gerade");
  }
}

// -----------------------------
// Z21 Weichenbefehl senden
// -----------------------------
void sendWeiche(uint16_t addr, bool gerade) {
  uint8_t buf[6];
  buf[0] = 0x04;
  buf[1] = 0x00;
  buf[2] = 0x40;
  buf[3] = 0x00;
  buf[4] = (addr >> 8) & 0xFF;
  buf[5] = (addr & 0xFF) << 1;
  buf[5] |= 0x02;
  if (!gerade) buf[5] |= 0x01;

  udp.beginPacket(z21, 21105);
  udp.write(buf, 6);
  udp.endPacket();

  Serial.print("Z21 SEND: Weiche ");
  Serial.print(addr);
  Serial.print(" -> ");
  Serial.println(gerade ? "Gerade" : "Abzweig");
}

// -----------------------------
// Z21 Rückmeldungen empfangen
// -----------------------------
void empfangeZ21() {
  int packetSize = udp.parsePacket();
  if (packetSize <= 0) return;

  uint8_t buf[20];
  udp.read(buf, packetSize);

  if (buf[2] == 0x40 && buf[3] == 0x00 && packetSize >= 6) {

    uint16_t addr = (buf[4] << 8) | (buf[5] >> 1);
    bool gerade = !(buf[5] & 0x01);

    Serial.print("Z21 RECV: Weiche ");
    Serial.print(addr);
    Serial.print(" -> ");
    Serial.println(gerade ? "Gerade" : "Abzweig");

    for (int i = 0; i < anzahlWeichen; i++) {
      if (weichen[i].adresse == addr) {
        weichen[i].state = !gerade;
        updateLEDs(weichen[i]);
        saveWeichenToEEPROM();
      }
    }
  }
}

// -----------------------------
// Z21 Status beim Start anfordern
// -----------------------------
void requestZ21WeichenStatus() {
  uint8_t buf[5] = {0x05, 0x00, 0x40, 0x00, 0xF0};

  udp.beginPacket(z21, 21105);
  udp.write(buf, 5);
  udp.endPacket();

  Serial.println("Z21: Statusanforderung gesendet.");
}

// -----------------------------
// Webinterface HTML
// -----------------------------
String buildPage() {
  String html = "<html><head><meta name='viewport' content='width=device-width, initial-scale=1'>";
  html += "<style>body{font-family:sans-serif;}button{width:120px;height:40px;font-size:18px;margin:10px;}</style></head><body>";
  html += "<h2>Weichensteuerung</h2>";

  for (int i = 0; i < anzahlWeichen; i++) {
    html += "Weiche ";
    html += weichen[i].adresse;
    html += ": ";

    html += "<a href='/toggle?i=" + String(i) + "'>";
    html += "<button>";
    html += (weichen[i].state ? "Abzweig" : "Gerade");
    html += "</button></a><br>";
  }

  html += "</body></html>";
  return html;
}

void handleRoot() {
  server.send(200, "text/html", buildPage());
}

void handleToggle() {
  int i = server.arg("i").toInt();

  Serial.print("WEB: Weiche ");
  Serial.print(weichen[i].adresse);
  Serial.println(" getoggelt.");

  weichen[i].state = !weichen[i].state;
  sendWeiche(weichen[i].adresse, !weichen[i].state);
  updateLEDs(weichen[i]);
  saveWeichenToEEPROM();

  server.sendHeader("Location", "/");
  server.send(303);

// -----------------------------
// Setup
// -----------------------------
void setup() {
  Serial.begin(115200);
  Serial.println();
  Serial.println("Starte ESP32 Weichensteuerung...");
  Serial.print("Verbinde mit WLAN: ");
  Serial.println(ssid);

  WiFi.begin(ssid, pass);

  int counter = 0;
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
    counter++;
    if (counter % 10 == 0) {
      Serial.print("  (warte ");
      Serial.print(counter / 2);
      Serial.println(" Sekunden)");
    }
  }

  Serial.println();
  Serial.println("WLAN verbunden!");
  Serial.print("IP-Adresse: ");
  Serial.println(WiFi.localIP());
  Serial.println("--------------------------------------");

  udp.begin(21105);

  EEPROM.begin(EEPROM_SIZE);

  for (int i = 0; i < anzahlWeichen; i++) {
    pinMode(weichen[i].tasterPin, INPUT_PULLUP);
    pinMode(weichen[i].ledGerade, OUTPUT);
    pinMode(weichen[i].ledAbzweig, OUTPUT);
  }

  loadWeichenFromEEPROM();

  delay(300);
  requestZ21WeichenStatus();

  server.on("/", handleRoot);
  server.on("/toggle", handleToggle);
  server.begin();
}

// -----------------------------
// Loop
// -----------------------------
void loop() {

  server.handleClient();
  empfangeZ21();  // Z21 Rückmeldungen verarbeiten

  for (int i = 0; i < anzahlWeichen; i++) {

    bool reading = digitalRead(weichen[i].tasterPin);

    if (reading != weichen[i].lastState) {
      weichen[i].lastChangeTime = millis();
      weichen[i].lastState = reading;
    }

    if ((millis() - weichen[i].lastChangeTime) > debounceTime) {

      if (reading != weichen[i].stableState) {
        weichen[i].stableState = reading;

        if (reading == LOW) {
          weichen[i].state = !weichen[i].state;

          Serial.print("TASTER: Weiche ");
          Serial.print(weichen[i].adresse);
          Serial.print(" -> ");
          Serial.println(weichen[i].state ? "Abzweig" : "Gerade");

          sendWeiche(weichen[i].adresse, !weichen[i].state);
          updateLEDs(weichen[i]);
          saveWeichenToEEPROM();
        }
      }
    }
  }
}
Gruß Tom

Norbert
Beiträge: 307
Registriert: 31.12.2018, 08:07
Hat sich bedankt: 1 Mal
Danksagung erhalten: 74 Mal

Re: schnurloses Gleisbildstellwerk -- Die elektronischen Grundlagen

Beitrag von Norbert » 23.06.2026, 16:44

Mit Svens Generator erstellt funktioniert das bis heute einwandfrei.
Gruß N.
Zuletzt geändert von Norbert am 23.06.2026, 17:30, insgesamt 1-mal geändert.

little.yoda
Site Admin
Beiträge: 989
Registriert: 14.09.2018, 19:05
Hat sich bedankt: 35 Mal
Danksagung erhalten: 175 Mal

Re: schnurloses Gleisbildstellwerk -- Die elektronischen Grundlagen

Beitrag von little.yoda » 23.06.2026, 16:58

Hi

Die Funktion ist Teil von meinem Framework.
https://github.com/littleyoda/littleyoda-DCC-Decoder/
https://littleyoda.github.io/littleyoda ... coder-DOC/

ArduinoIDE unterstütze ich nicht mehr. PlatformIO benötigst du oder aber du flasht es über den Browser.
https://littleyoda.github.io/littleyoda ... DOC/Flash/

Ein Config-File kannst du über https://spurg.open4me.de/configgenerator generieren.
Hier kannst du direkt die Funktion Gleisbildstellpult aktivieren.

[Ich weiß, viele Informationen auf einmal]
[Ich habe deinen Beitrag in einen neuen Thread verschoben]

Gruß

tomy99
Beiträge: 2
Registriert: 20.06.2026, 13:02

Re: schnurloses Gleisbildstellwerk

Beitrag von tomy99 » 23.06.2026, 19:04

Danke für die Info's

Also den Config Generator hab ich schon ausprobiert. Aber dafür muss ich ja vorher noch was installieren, den DCC-Decoder wie du schreibst, werde mich mal mit der verlinkten Webseite beschäftigen. Aber Platform IO habe ich auch installiert.

Zu deinem Schaltplan, PIN 1 wäre der deklarierte LED PIN in deinem Config Generator und
PIN 2 der Taster-Ausgang. wenn ich das richtig verstehe. Und dann Haben wir noch 2x Ground, +3,3v Spannung. Sollte ich hinbekommen :)

Habe vor gelbe LED's zu verwenden. mit 2mA Diodenstrom, Betriebsspannung 1.8 bis 2.1V sagt das Datenblatt.
sollte ja mit denWiderstanden 3x 220 Ohm passen.

Funktioniert auch ein BC337 statt dem BC327?, den hätte ich da.

Tomy

little.yoda
Site Admin
Beiträge: 989
Registriert: 14.09.2018, 19:05
Hat sich bedankt: 35 Mal
Danksagung erhalten: 175 Mal

Re: schnurloses Gleisbildstellwerk

Beitrag von little.yoda » 23.06.2026, 20:03

In mein Schaltplan benötigst du einen NPN und einen PNP Transistor:
2N2222A (NPN) und BC327-40 (PNP)

Dein BC337 wäre ein NPN-Transistor und somit eine Alternative zum 2N2222A.

Antworten