Delay funktioniert nicht

Hallo Zusammen

Ich bastell mir gerade u.a. eine Weichensteuerung zusammen. Dabei gibt es einen Mega als Master, der Befehle per LAN empfängt und über I2C an Slave-NANOs weiterleitet. Das fuktioniert soweit. Knackpunkt ist die Ansteuerung der Weichen. Die brauchen je nach Richtung einen 12V-Impuls, entweder +/- für die eine und -/+ für die andere Richtung. Realisiert ist das über zwei Relais mit zwei Dioden. Auch das funktioniert, eigentlich! Um die Weiche nicht zu beschädigen und v.a. damit sie auch zuverlässig schalten kann, soll der genannte Impuls erstmal für 1 Sekunde anliegen. Soviel zum Soll.

Nun ist es aber so, dass das Delay in der Sub WeicheLinks() oder WeicheRechts() ignoriert wird. Selbst wenn ich da ein delay(10000) einbaue, ziehen die Relais nur für einen Sekundenbruchteil an.

Kann das evtl. am Event liegen, der die Sub aufruft? Im Loop funktioniert das Delay. Im Netz habe ich nichts dazu gefunden...

Das Board ist ein NANO-Klon. Den (das?) Sketch hab ich mal komplett hier rein gepackt, vielleicht liegt der Fehler ja doch irgendwo anders.

Danke

#include <Wire.h>

int pin_weiche1_L=2;
int pin_weiche1_R=3;
int LED = 13;
int x = 0;
int speed = 0;

int device;
int order;
int value;

#define WIRE Wire

// Motortreiber PINs
#define MEn1 10
#define MEn2 5
#define MIn11 9
#define MIn12 6
#define MIn21 7
#define MIn22 8

// Slave-Adresse
#define NODE_ADDRESS 2
// Länge Übertragung
#define PAYLOAD_SIZE 4
byte nodePayload[PAYLOAD_SIZE];

void setup() {

  WIRE.begin();
  Serial.begin(9600);

  // PINs setzen
  pinMode(MEn1, OUTPUT);
  pinMode(MEn2, OUTPUT);
  pinMode(MIn11, OUTPUT);
  pinMode(MIn12, OUTPUT);
  pinMode(MIn21, OUTPUT);
  pinMode(MIn22, OUTPUT);

  // pinMode (LED, OUTPUT);
  Wire.begin(NODE_ADDRESS);
  Wire.onReceive(receiveEvent); // erstelle ein Empfangen-Ereignis
  Wire.onRequest(requestEvent); // erstelle ein Anfrage-Ereignis

  pinMode(pin_weiche1_L, OUTPUT);
  pinMode(pin_weiche1_R, OUTPUT);

  digitalWrite(pin_weiche1_L, HIGH);
  digitalWrite(pin_weiche1_R, HIGH);

  
  Serial.println("Bereit...");
}

void loop() {

}

void WeicheLinks() {

  digitalWrite(pin_weiche1_L, LOW);
  digitalWrite(pin_weiche1_R, HIGH);
  Serial.println("set");
  delay(1000);
  digitalWrite(pin_weiche1_L, HIGH);
  digitalWrite(pin_weiche1_R, HIGH);
  Serial.println("reset");

}


void WeicheRechts() {

  digitalWrite(pin_weiche1_L, HIGH);
  digitalWrite(pin_weiche1_R, LOW);
  Serial.println("set");
  delay(1000);
  digitalWrite(pin_weiche1_L, HIGH);
  digitalWrite(pin_weiche1_R, HIGH);
  Serial.println("reset");

}

void receiveEvent(int bytes) {
  device = Wire.read();              // lies die gesendeten Daten aus
  order = Wire.read();
  value = Wire.read();
  
  Serial.println(bytes);
  Serial.print("> ");
  Serial.print(device);
  Serial.print(" ");
  Serial.print(order);
  Serial.print(" ");
  Serial.println(value);

  if (device == 1){
    if (order == 1) {
      Serial.println("Weiche R");
      WeicheRechts();
    }
    else if (order == 2)  {
      Serial.println("Weiche L"); 
      WeicheLinks();
    }
  }

  if (device == 2) {
    setMotorSpeed2(order, value);
  }
}

void requestEvent()
{
  nodePayload[0] = NODE_ADDRESS;
  nodePayload[1] = analogRead(A0) / 4;
  Wire.write(nodePayload, PAYLOAD_SIZE);
}

void setMotorSpeed2(int order, int value)
{
  int speed = (value - 65) * 10;

  if (order == 0) {
    digitalWrite(MIn21, HIGH);
    digitalWrite(MIn22, HIGH);
    int speed = 1;
  }
  else if (order == 1) {
    digitalWrite(MIn21, LOW);
    digitalWrite(MIn22, HIGH);
  }
  else if (order == 2) {
    digitalWrite(MIn21, HIGH);
    digitalWrite(MIn22, LOW);
  }

  if (speed==0) {
    digitalWrite(MIn21, LOW);
    digitalWrite(MIn22, LOW);
  }
  analogWrite(MEn2, speed);
  
  Serial.println("Motor 2"); 
  Serial.print(order); 
  Serial.print("->"); 
  Serial.println(speed); 


}

Schnellschuß zum testen:

#include <Wire.h>

int pin_weiche1_L = 2;
int pin_weiche1_R = 3;
int LED = 13;
int x = 0;
int speed = 0;

int device;
int order;
int value;

#define WIRE Wire

// Motortreiber PINs
#define MEn1 10
#define MEn2 5
#define MIn11 9
#define MIn12 6
#define MIn21 7
#define MIn22 8

// Slave-Adresse
#define NODE_ADDRESS 2
// Länge Übertragung
#define PAYLOAD_SIZE 4
byte nodePayload[PAYLOAD_SIZE];

void setup()
{
  WIRE.begin();
  Serial.begin(9600);
  // PINs setzen
  pinMode(MEn1, OUTPUT);
  pinMode(MEn2, OUTPUT);
  pinMode(MIn11, OUTPUT);
  pinMode(MIn12, OUTPUT);
  pinMode(MIn21, OUTPUT);
  pinMode(MIn22, OUTPUT);
  // pinMode (LED, OUTPUT);
  Wire.begin(NODE_ADDRESS);
  Wire.onReceive(receiveEvent); // erstelle ein Empfangen-Ereignis
  Wire.onRequest(requestEvent); // erstelle ein Anfrage-Ereignis
  pinMode(pin_weiche1_L, OUTPUT);
  pinMode(pin_weiche1_R, OUTPUT);
  digitalWrite(pin_weiche1_L, HIGH);
  digitalWrite(pin_weiche1_R, HIGH);
  Serial.println("Bereit...");
}

void loop()
{
}

void WeicheLinks()
{
  digitalWrite(pin_weiche1_L, LOW);
  digitalWrite(pin_weiche1_R, HIGH);
  Serial.println("set");
  delay(1000);
  digitalWrite(pin_weiche1_L, HIGH);
  digitalWrite(pin_weiche1_R, HIGH);
  Serial.println("reset");
}

void WeicheRechts()
{
  digitalWrite(pin_weiche1_L, HIGH);
  digitalWrite(pin_weiche1_R, LOW);
  Serial.println("set");
  delay(1000);
  digitalWrite(pin_weiche1_L, HIGH);
  digitalWrite(pin_weiche1_R, HIGH);
  Serial.println("reset");
}

void receiveEvent(int bytes)
{
  int myDevice = Wire.read();              // lies die gesendeten Daten aus
  int myOrder = Wire.read();
  int myValue = Wire.read();
  Serial.println(bytes);
  Serial.print("> ");
  Serial.print(myDevice);
  Serial.print(" ");
  Serial.print(myOrder);
  Serial.print(" ");
  Serial.println(myValue);

  if (device == 0)
  {
    device = myDevice;
    order = myOrder;
    value = myValue;
  }
  else
  {
    Serial.println(F(" Nicht ausgewertet!"));
  }

  if (device == 1)
  {
    if (order == 1)
    {
      Serial.println("Weiche R");
      WeicheRechts();
    }
    else if (order == 2)
    {
      Serial.println("Weiche L");
      WeicheLinks();
    }

    device = 0;
  }

  if (device == 2)
  {
    setMotorSpeed2(order, value);
    device = 0;
  }
}

void requestEvent()
{
  nodePayload[0] = NODE_ADDRESS;
  nodePayload[1] = analogRead(A0) / 4;
  Wire.write(nodePayload, PAYLOAD_SIZE);
}

void setMotorSpeed2(int order, int value)
{
  int speed = (value - 65) * 10;

  if (order == 0)
  {
    digitalWrite(MIn21, HIGH);
    digitalWrite(MIn22, HIGH);
    int speed = 1;
  }
  else if (order == 1)
  {
    digitalWrite(MIn21, LOW);
    digitalWrite(MIn22, HIGH);
  }
  else if (order == 2)
  {
    digitalWrite(MIn21, HIGH);
    digitalWrite(MIn22, LOW);
  }

  if (speed == 0)
  {
    digitalWrite(MIn21, LOW);
    digitalWrite(MIn22, LOW);
  }

  analogWrite(MEn2, speed);
  Serial.println("Motor 2");
  Serial.print(order);
  Serial.print("->");
  Serial.println(speed);
}

Beide CallBack laufen im Interrupt Kontext.
delay() funktioniert nicht in Interrupts, weil es selber Interrupts benötigt.

Werd ich irgendwann auch noch lernen.

@miketheb dann lagert man die Geschichte eben aus:

#include <Wire.h>

int pin_weiche1_L = 2;
int pin_weiche1_R = 3;
int LED = 13;
int x = 0;
int speed = 0;

int device;
int order;
int value;

#define WIRE Wire

// Motortreiber PINs
#define MEn1 10
#define MEn2 5
#define MIn11 9
#define MIn12 6
#define MIn21 7
#define MIn22 8

// Slave-Adresse
#define NODE_ADDRESS 2
// Länge Übertragung
#define PAYLOAD_SIZE 4
byte nodePayload[PAYLOAD_SIZE];

void setup()
{
  WIRE.begin();
  Serial.begin(9600);
  // PINs setzen
  pinMode(MEn1, OUTPUT);
  pinMode(MEn2, OUTPUT);
  pinMode(MIn11, OUTPUT);
  pinMode(MIn12, OUTPUT);
  pinMode(MIn21, OUTPUT);
  pinMode(MIn22, OUTPUT);
  // pinMode (LED, OUTPUT);
  Wire.begin(NODE_ADDRESS);
  Wire.onReceive(receiveEvent); // erstelle ein Empfangen-Ereignis
  Wire.onRequest(requestEvent); // erstelle ein Anfrage-Ereignis
  pinMode(pin_weiche1_L, OUTPUT);
  pinMode(pin_weiche1_R, OUTPUT);
  digitalWrite(pin_weiche1_L, HIGH);
  digitalWrite(pin_weiche1_R, HIGH);
  Serial.println("Bereit...");
}

void loop()
{
  checkDevices();
}

void WeicheLinks()
{
  digitalWrite(pin_weiche1_L, LOW);
  digitalWrite(pin_weiche1_R, HIGH);
  Serial.println("set");
  delay(1000);
  digitalWrite(pin_weiche1_L, HIGH);
  digitalWrite(pin_weiche1_R, HIGH);
  Serial.println("reset");
}

void WeicheRechts()
{
  digitalWrite(pin_weiche1_L, HIGH);
  digitalWrite(pin_weiche1_R, LOW);
  Serial.println("set");
  delay(1000);
  digitalWrite(pin_weiche1_L, HIGH);
  digitalWrite(pin_weiche1_R, HIGH);
  Serial.println("reset");
}

void receiveEvent(int bytes)
{
  int myDevice = Wire.read();              // lies die gesendeten Daten aus
  int myOrder = Wire.read();
  int myValue = Wire.read();
  Serial.println(bytes);
  Serial.print("> ");
  Serial.print(myDevice);
  Serial.print(" ");
  Serial.print(myOrder);
  Serial.print(" ");
  Serial.println(myValue);

  if (device == 0)
  {
    device = myDevice;
    order = myOrder;
    value = myValue;
  }
  else
  {
    Serial.println(F(" Nicht ausgewertet!"));
  }
}

void checkDevices()
{
  if (device == 1)
  {
    if (order == 1)
    {
      Serial.println("Weiche R");
      WeicheRechts();
    }
    else if (order == 2)
    {
      Serial.println("Weiche L");
      WeicheLinks();
    }

    device = 0;
  }

  if (device == 2)
  {
    setMotorSpeed2(order, value);
    device = 0;
  }
}

void requestEvent()
{
  nodePayload[0] = NODE_ADDRESS;
  nodePayload[1] = analogRead(A0) / 4;
  Wire.write(nodePayload, PAYLOAD_SIZE);
}

void setMotorSpeed2(int order, int value)
{
  int speed = (value - 65) * 10;

  if (order == 0)
  {
    digitalWrite(MIn21, HIGH);
    digitalWrite(MIn22, HIGH);
    int speed = 1;
  }
  else if (order == 1)
  {
    digitalWrite(MIn21, LOW);
    digitalWrite(MIn22, HIGH);
  }
  else if (order == 2)
  {
    digitalWrite(MIn21, HIGH);
    digitalWrite(MIn22, LOW);
  }

  if (speed == 0)
  {
    digitalWrite(MIn21, LOW);
    digitalWrite(MIn22, LOW);
  }

  analogWrite(MEn2, speed);
  Serial.println("Motor 2");
  Serial.print(order);
  Serial.print("->");
  Serial.println(speed);
}

Und dann löst man das delay() auf und baut eine Statemachine.

Danke, das bestätigt meine Vermutung.

Merce, das werde ich mal probieren. Ggf. wäre es dann auch mit millis() lösbar.

Auch millis() benötigt Interrupts.

Merke:
Keine Verzögerungen in Interrupts einbauen!
Niemals.
Denn:
Das macht mehr Probleme, als es behebt.

Du solltest dein Konzept überarbeiten, da es im Kern schon defekt ist.

1 Like

Funktioniert perfekt. :+1:

1 Like

Sagen wir mal so: es funktioniert :smile:

Perfekt wäre es, wenn das delay in der Funktion aufgelöst wird.

Wenn ich das richtig sehe, werden da noch weitere Weichen angesteuert.
Es empfiehlt sich, das ganze so kleinteilig zu bauen, dass sich die gesamten Funktionen wiederverwenden lassen.

Zum Schluss bleibt dann nur der Block für die Deklaration und Initialisierung übrig.

Aber schön, dass es gefällt. :star_struck:

Solltest Du Zeit und Lust haben:
Hier eine Variante ohne delay(), die mit mehreren Dutzend Weichen und Antrieben klar kommt.
Ich hab aus Deinem Usrprungscode beide Antriebe entnommen und die Weiche.
Zusätzlich hab ich noch 2 Weichen in "habAchtStellung" solltest Du noch weitere da anschliessen wollen...

Ob es funktioniert weiss ich nicht.
Ich hoffe im Code alles erklärt zu haben.
Meine Zugfahrt ist jetzt passend zu Ende :slight_smile:

// Forensketch
// https://forum.arduino.cc/t/delay-funktioniert-nicht/1392881

/*
   Annahmen: receiveEvent() erhält die Anzahl Bytes vollständig
   Byte 1: DEVICE-ID
   Bestimmt ob Weiche (ID:1)
   Oder Motor (ID:2 oder ID:3)

   Bei ID == 1 bestimmt Byte 2 (ORDER) die Richtung der Weiche

   Bei ID == 2 oder ID == 3 bestimmt
     Byte 2 (ORDER) die Richtung des Motors
     Byte 3 (VALUE) die Geschwindigkeit,
       wobei alles was unter 65 und über 90 als 0 und
       ab 65 bis 90 um 65 gekürzt und mit 10 mutipliziert wird

  Ist die/eine Weiche gesetzt, wird der Antrieb nach 1 Sekunde abgeschaltet
  Die Umschaltung kann auch vor Beendigung der Einschaltzeit erfolgen (ReTrigger)

*/

#include <Wire.h>

constexpr uint16_t _oneSecond {1000};

enum class WState : byte {WAIT, LEFT, RIGHT};  // Warten auf neuen Zustand , Links oder rechts schalten

struct WEICHE              // Struktur; Was zusammengehört
{
  uint8_t lPin;            // Pin Weichezunge Ausfahrt links
  uint8_t rPin;            // Ausfahrt rechts
  uint32_t switchTime;     // Einschaltzeit
  WState newState;         // Merker für neuen Status
  WState wState;           // aktueller Status
};

WEICHE w[] =
{
  {2, 3, 0, WState::WAIT, WState::WAIT,},        // Erste Konfiguration
  /*{ 4, 11, 0, State::WAIT,WState::WAIT,},*/    // :-)
  /*{A0, A1, 0, State::WAIT,WState::WAIT,},*/    // :-)

};

constexpr uint8_t wNums {sizeof(w) / sizeof(w[0])};  // Anzahl Weichen errechnen

struct MOTORDATA              // Struktur für Antrieb; Was zusammengehört
{
  uint8_t inPinA;             // Motortreiberoins
  uint8_t inPinB;
  uint8_t enaPin;
  uint8_t order;              // Hier versteckt sich die Fahrtrichtung
  uint8_t value;              // und hier die Geschwindigkeit
};

MOTORDATA mData[] =
{
  {9, 6, 10, 0, 0,},  // inPinA, inPinB, enaPin, order, value
  {7, 8, 5, 0, 0,},
};
constexpr uint8_t mNums {sizeof(mData) / sizeof(mData[0])}; // Anzahl Motoren errechnen

// Slave-Adresse
#define NODE_ADDRESS 2
// Länge Übertragung
#define PAYLOAD_SIZE 4
byte nodePayload[PAYLOAD_SIZE];

void setup()
{
  Serial.begin(9600);
  Serial.println(F("Start..."));
  Wire.begin();
  Wire.begin(NODE_ADDRESS);
  Wire.onReceive(receiveEvent); // erstelle ein Empfangen-Ereignis
  Wire.onRequest(requestEvent); // erstelle ein Anfrage-Ereignis
  // pinMode (LED, OUTPUT);
  pinMode(LED_BUILTIN, OUTPUT);  // Für den Heartbeat auf dem Board

  // PINs setzen
  for (byte b = 0; b < wNums; b++)  // Weichenpins
  {
    pinMode(w[b].lPin, OUTPUT);
    pinMode(w[b].rPin, OUTPUT);
    wSetState(b, WState::WAIT);
  };

  for (byte b = 0; b < mNums; b++)  // MotorPins
  {
    pinMode(mData[b].inPinA, OUTPUT);
    pinMode(mData[b].inPinB, OUTPUT);
    pinMode(mData[b].enaPin, OUTPUT);
    digitalWrite(mData[b].inPinA, LOW);
    digitalWrite(mData[b].inPinA, LOW);
    digitalWrite(mData[b].inPinA, LOW);
  }

  Serial.println(F("Bereit..."));
}

void loop()
{
  checkWState();   // Setzt die Weichenstellung
  setMotorState(); // Setzt die Motordaten
  heartBeat(500);  // Blinklicht
}

void receiveEvent(int bytes) //
{
  uint8_t myDevice = 0;
  uint8_t myOrder = 0;
  uint8_t myValue = 0;

  if (bytes != 0)
  { myDevice = Wire.read(); }     // lies die gesendeten Daten aus

  if (bytes > 1)
  {myOrder = Wire.read(); }

  if (bytes > 2)
  {myValue = Wire.read(); }

  if (bytes > 3)
  {
    for (byte b = 3; b < bytes; b++)        // liest restliche Bytes
    { Wire.read(); }
  }

  /*
    Serial.println(bytes);
    Serial.print("> ");
    Serial.print(myDevice);
    Serial.print(' ');
    Serial.print(myOrder);
    Serial.print(' ');
    Serial.println(myValue);
  */
  switch (myDevice)
  {
    case 1:                     // Weichensteuerung
      switch (myOrder)
      {
        case 1: w[0].newState = WState::LEFT; break;

        case 2: w[0].newState = WState::RIGHT; break;
// Weitere Weichen kommen dann hier....
        case 3: // w[1].newState = WState::LEFT;
          break;

        case 4: // w[1].newState = WState::RIGHT
          break;
      };

      break;

    case 2 ... 3:   // MotorSteuerung (erster Motor ist 0, aber Device 2! Zweiter Motor ist 1 aber Device 3)
      mData[myDevice - 2].order = myOrder;
      mData[myDevice - 2].value = myValue;
      break;
  }
}

void requestEvent()
{
  nodePayload[0] = NODE_ADDRESS;
  nodePayload[1] = analogRead(A0) / 4;
  Wire.write(nodePayload, PAYLOAD_SIZE);
}

void checkWState()
{
  for (byte b = 0; b < wNums; b++)                  // zählt durch alle Weichen
  {
    if (w[b].wState != WState::WAIT)                // Weiche ist nicht im Zustand warten?
    {
      if (millis() - w[b].switchTime > _oneSecond)  // Umstellzeit abgelaufen
      { wSetState(b, WState::WAIT); }               // Zustand warten setzen
    }

    if (w[b].wState != w[b].newState)               // Wenn alter Zustand != neuer Zustand
    { wSetState(b, w[b].newState); }                // neuen Zustand setzen
  }                                                 // zähle nächste Weiche.....
}

void wSetState(const uint8_t weichenNum, const WState newState)  // setzt die Weichen auf neue Stellung
{
  switch (newState)
  {
    case WState::WAIT:                                           // Dürfte klar sein
      digitalWrite(w[weichenNum].lPin, HIGH);
      digitalWrite(w[weichenNum].rPin, HIGH);
      w[weichenNum].wState = WState::WAIT;
      w[weichenNum].newState = WState::WAIT;
      break;

    case WState::LEFT:
      digitalWrite(w[weichenNum].lPin, LOW);
      digitalWrite(w[weichenNum].rPin, HIGH);
      w[weichenNum].switchTime = millis();                       // Umstellzeit merken, für Ausschaltautomatik
      w[weichenNum].wState = WState::LEFT;
      w[weichenNum].newState = WState::WAIT;
      break;

    case WState::RIGHT:
      digitalWrite(w[weichenNum].lPin, LOW);
      digitalWrite(w[weichenNum].rPin, HIGH);
      w[weichenNum].switchTime = millis();                       // Wie left
      w[weichenNum].wState = WState::RIGHT;
      w[weichenNum].newState = WState::WAIT;
      break;
  }
}

void setMotorState()
{
  for (byte b = 0; b < mNums; b++)
  {
    // Speed wird nur berechnet von 65 bis 90 - alles andere ist 0 wegen Überlauf!
    uint8_t speed = (mData[b].value > 65 && mData[b].value < 91) ? (mData[b].value - 65) * 10 : 0;

    if (speed == 0)                 // Wenn Speed == 0 dann Antrieb auf LOW setzen
    { mData[b].order = 3; }

    switch (mData[b].order)         // Antrieb schalten
    {
      case 0:                       // Magic Numbers ggfls. noch aufösen
        digitalWrite(mData[b].inPinA, HIGH);
        digitalWrite(mData[b].inPinB, HIGH);
        break;

      case 1:
        digitalWrite(mData[b].inPinA, LOW);
        digitalWrite(mData[b].inPinB, HIGH);
        break;

      case 2:
        digitalWrite(mData[b].inPinA, HIGH);
        digitalWrite(mData[b].inPinB, LOW);
        break;

      case 3:
        digitalWrite(mData[b].inPinA, LOW);
        digitalWrite(mData[b].inPinB, LOW);
        break;
    }

    analogWrite(mData[b].enaPin, speed);  // errechnete Geschwinigkeit setzen
    Serial.println("Motor");
    Serial.print(mData[b].order);
    Serial.print("->");
    Serial.println(speed);
  }
}

void heartBeat(const uint32_t delayTime)         // macht nur ein blinken, damit man sieht, dass der noch läuft :-)
{
  static uint32_t lastMillis = 0;

  if (millis() - lastMillis > delayTime)
  {
    lastMillis = millis();
    digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
  }
}

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.