Blink Without Delay - Umsetzung in Loop

Hallo Community

Innerhalb eines grösseren Projekts verzweifle ich gerade an einer einfachen Detailfunktion. Im Prinzip ist es die Anwendung des "Blink Without Delay" Bespiels. Ich möchte einen Ausgang zwei mal in Folge kurz ansteuern, dann soll eine längere Pause folgen.

Der Code sieht wie folgt aus (gekürzt):

// variables for ringing
int RING_PIN_STATE = LOW;
unsigned long previousMillis = 0;
unsigned long previousMillis2 = 0;
const long intervallRing = 1000; //intervall klingeln (ms)
const long intervallBreak = 10000; //intervall Pause zwischen klingeln (ms)

void loop() {

  unsigned long currentMillis = millis();
  unsigned long currentMillis2 = millis();

// Klingeln nach Zufallszeit (intervallBreak)
       else if (currentMillis - previousMillis >= intervallBreak){      
         previousMillis = currentMillis;
         //Zweimal Klingeln 
         for (int i = 0; i <= 3; i++) {
          if (currentMillis2 - previousMillis2 >= intervallRing) {
           previousMillis2 = currentMillis2;
            if (RING_PIN_STATE == LOW) {
             RING_PIN_STATE = HIGH;
            } else {
             RING_PIN_STATE = LOW;
            }
          }
         }
        digitalWrite(RING_PIN, RING_PIN_STATE);

Das Problem ist nun, dass der Ausgang im Takt von "intervallBreak" toggelt. Eigentlich soll der Ausgang viermal für die Zeit "intervallRing" toggeln und dann soll für die Zeit "intervallBreak" nichts passieren.
Das Ganze läuft auf einem Teensy 4.0, der unterstützt leider kein Debugging :frowning:

Ich hoffe jemand unabhängiges sieht den Fehler direkt.

Viele Grüsse
webghost

Ja, die for schleife ist das Problem. Zeige einmal den ganzen Code. Das muss man anders lösen.

void loop() {
  // First, read the buttons
  buttonRecord.update();
  buttonPlay.update();
  buttonTest.update();
  unsigned long currentMillis = millis();
  unsigned long currentMillis2 = millis();


  switch(mode){
    case Mode::Ready:
      // Falling edge occurs when the handset is lifted --> 611 telephone
      if (buttonRecord.fallingEdge()) {
        Serial.println("Handset lifted");
        mode = Mode::Prompting; print_mode();
      }
      else if(buttonPlay.fallingEdge()) {
        //playAllRecordings();
        playLastRecording();
      }
      // Klingeln nach Zufallszeit (intervallBreak)
       else if (currentMillis - previousMillis >= intervallBreak){      
         previousMillis = currentMillis;
         //Zweimal Klingeln 
         for (int i = 0; i <= 3; i++) {
          if (currentMillis2 - previousMillis2 >= intervallRing) {
           previousMillis2 = currentMillis2;
            if (RING_PIN_STATE == LOW) {
             RING_PIN_STATE = HIGH;
            } else {
             RING_PIN_STATE = LOW;
            }
          }
         }
        digitalWrite(RING_PIN, RING_PIN_STATE);
      } 
      break;

danach folgt der nächste Case.

Das ist Mist. Wer soll sich denn da einen reim drauf machen.

// Ich möchte einen Ausgang zwei mal in Folge kurz ansteuern, dann soll eine längere Pause folgen.
// Eigentlich soll der Ausgang viermal für die Zeit "intervallRing" toggeln und dann soll für die Zeit "intervallBreak" nichts passieren.



// variables for ringing
constexpr byte ringPin {3};

constexpr unsigned long intervallRing {1000}; //intervall klingeln (ms)
constexpr unsigned long intervallBreak {10000}; //intervall Pause zwischen klingeln (ms)
unsigned long breakTime = intervallRing;
unsigned long lastmillis;
byte zaehler = 0;

void setup()
{
  pinMode(ringPin, OUTPUT);
  digitalWrite(ringPin, LOW);
}

void loop()
{
  machSwitchFunktion();
}

void machSwitchFunktion()
{
  if (millis() - lastmillis > breakTime)
  {
    lastmillis = millis();
    switch (zaehler)
    {
      case 0 ... 3:
        digitalWrite(ringPin, !digitalRead(ringPin));
        if (zaehler == 3)
        {
          breakTime = intervallBreak;
        }
        break;
      case 4:
        breakTime = intervallRing;
        digitalWrite(ringPin, LOW);
        break;
      default:
        zaehler = 0;
        break;
    }
    zaehler++;
  }
}

(Die Pause ist Absicht!)

ohne void setup(){} wird es schwierig

Hallo sskwebghost

Herzlich Willkommen im weltbesten Arduino Forum der Welt.

Hier kommt eine angepasste Version des "Blink Without Delay" Bespiels.
Ich habe ein array eingelötet, das die unterscheidlichen Zeiten für das Interval enthält.

// constants won't change. Used here to set a pin number:
const int ledPin =  LED_BUILTIN;// the number of the LED pin

// Variables will change:
int ledState = LOW;             // ledState used to set the LED

// Generally, you should use "unsigned long" for variables that hold time
// The value will quickly become too large for an int to store
unsigned long previousMillis = 0;        // will store last time LED was updated
uint8_t indexCounter;
uint8_t ringState=HIGH;
// constants won't change:
const long interval[] {1000,1000,10000,1000}; // intervals at which to blink (milliseconds)

void setup() {
  // set the digital pin as output:
  pinMode(ledPin, OUTPUT);
}

void loop() {
  // here is where you'd put code that needs to be running all the time.

  // check to see if it's time to blink the LED; that is, if the difference
  // between the current time and last time you blinked the LED is bigger than
  // the interval at which you want to blink the LED.
  unsigned long currentMillis = millis();

  if (currentMillis - previousMillis >= interval[indexCounter]) {
    // save the last time you blinked the LED
    previousMillis = currentMillis;
    indexCounter=(indexCounter+1)%(sizeof(interval)/sizeof(interval[0]));

    // if the LED is off turn it on and vice-versa:
    if (ledState == LOW) {
      ledState = HIGH;
    } else {
      ledState = LOW;
    }

    // set the LED with the ledState of the variable:
    digitalWrite(ledPin, ledState and ringState);
  }
}

Viel Spass beim Telefonbasteln.

Das könnte auch als nur 1 Zyklus interpretiert weden, von z.B. insgesamt 4 sec.
In den ersten 500 ms an, dann aus, von 1000 bis 1500 ms wieder an, dann aus, bis die 4000 ms erreicht sind.

bool doppelblink () {
static uint32_t startTime;
const uint16_t on1 = 0;
const uint16_t off1 = 500;
const uint16_t on2 = 1000;
const uint16_t off2 = 1500;
const uint16_t total = 4000;
  if ( millis() - startTime > total ) startTime = millis();
  uint16_t phase = millis() - startTime;
  if (phase > off2) return false;
  if (phase > on2) return true;
  if (phase > off1) return false;
  return true;
}


void loop() {
   digitalWrite( LED_BUILTIN, doppelblink() );
}

setup kriegt man selber hin.

Vielen Dank für den schnellen und zahlreichen Support!

Das Beispiel von paulpaulson habe ich umgesetzt und der Blinktakt funktioniert auch. Nun möchte ich den Blinktakt jeweils nach Zeitspanne "intervalBreak" starten, daran scheitere ich gerade. Eigentlich sollte die Logik doch gleich sein, nur veschachtelt.

Hier der angepasst Code:

Grüsse
webghost

Hello sskwebghost

Probier mal die folgende Modifikation des Sketches aus.
Evt. muss die Zeit in der Variable mit dem Namen intervallBreak noch angepasst werden.

// constants won't change. Used here to set a pin number:
const int ledPin =  LED_BUILTIN;// the number of the LED pin
// Variables will change:
int ledState = LOW;             // ledState used to set the LED
// Generally, you should use "unsigned long" for variables that hold time
// The value will quickly become too large for an int to store
unsigned long previousMillis = 0;        // will store last time LED was updated
unsigned long previousMillis2 = 0;
uint8_t indexCounter;
uint8_t ringState = HIGH;
const long intervallBreak = 30000; //intervall Pause zwischen klingeln (ms)
// constants won't change:
const long interval[] {1000, 1000, 10000, 1000}; // intervals at which to blink (milliseconds)

void setup() {
  // set the digital pin as output:
  pinMode(ledPin, OUTPUT);
}

void loop() {
  // here is where you'd put code that needs to be running all the time.

  // check to see if it's time to blink the LED; that is, if the difference
  // between the current time and last time you blinked the LED is bigger than
  // the interval at which you want to blink the LED.
  unsigned long currentMillis = millis();
  unsigned long currentMillis2 = millis();

  // Start Blinksequenz nach Zeit intervallBreak
  if (currentMillis2 - previousMillis2 >= intervallBreak) {
    previousMillis2 = currentMillis2;
    ringState = ringState ? LOW : HIGH;
  }
  if (currentMillis - previousMillis >= interval[indexCounter]) {
    // save the last time you blinked the LED
    previousMillis = currentMillis;
    indexCounter = (indexCounter + 1) % (sizeof(interval) / sizeof(interval[0]));

    // if the LED is off turn it on and vice-versa:
    if (ledState == LOW) {
      ledState = HIGH;
    } else {
      ledState = LOW;
    }

    // set the LED with the ledState of the variable:
    digitalWrite(ledPin, ledState and ringState);
  }
}

Ich wünche einen geschmeidigen Tag und viel Spaß beim Programmieren in C++.

Du möchtest einen endlichen Automaten bauen.

Du möchtest deinen endlichen Automaten erweitern.

was hat Dir an #4 nicht gefallen?

Hab jetzt eine Zustandsautomaten nach dem Bespiel aus #3 gebaut.

// Ich möchte einen Ausgang zwei mal in Folge kurz ansteuern, dann soll eine längere Pause folgen.
// Eigentlich soll der Ausgang viermal für die Zeit "intervallRing" toggeln und dann soll für die Zeit "intervallBreak" nichts passieren.

#define RING_PIN 1

// variables for ringing
constexpr byte ringPin {3};

constexpr unsigned long intervallRing {1000}; //intervall klingeln (ms)
constexpr unsigned long intervallBreak {10000}; //intervall Pause zwischen klingeln (ms)
unsigned long breakTime = intervallRing;
unsigned long lastmillis;
byte zaehler = 0;

void setup()
{
  pinMode(RING_PIN, OUTPUT);
  digitalWrite(RING_PIN, LOW);
}

void loop()
{
  machSwitchFunktion();
}

void machSwitchFunktion()
{
  if (millis() - lastmillis > breakTime)
  {
    lastmillis = millis();
    switch (zaehler)
    {
      case 0 ... 3:
        digitalWrite(RING_PIN, !digitalRead(RING_PIN));
        if (zaehler == 3)
        {
          breakTime = intervallBreak;
        }
        break;
      case 4:
        breakTime = intervallRing;
        digitalWrite(RING_PIN, LOW);
        break;
      default:
        zaehler = 0;
        break;
    }
    zaehler++;
  }
}

Im ersten Durchgang funktionier es, danach ist Ausgang für 10s HIGH (anstatt LOW für intervallBreak). Die Zeit intervallBreak wird doch nur in case 4 geschrieben, in diesem case wird der Ausgang doch automatisch auf LOW gesetzt.

Wenn du ein UNO, Mega, Nano alle R3, nutzt dann ist Pin 0 und 1 Tabu, die werden durch Verbindung zu Rechner benutzt

Es war #4

Es hat seinen Grund, warum das bei mir

heisst.

Nein die wird im ersten case gesetzt, wenn der Zaehler 3 ist.
Damit ist beim nächsten Umlauf breakTime 10000 Sekunden.
Das heisst es dauert genau so lange bis case 4 erreicht wird.

Empfehlung: Wenn der zaehler == 3 die breakTime setzt, in dem selben Funktionsblock den ringPin LOW setzen, oder noch spannender:

// Ich möchte einen Ausgang zwei mal in Folge kurz ansteuern, dann soll eine längere Pause folgen.
// Eigentlich soll der Ausgang viermal für die Zeit "intervallRing" toggeln und dann soll für die Zeit "intervallBreak" nichts passieren.


// variables for ringing
constexpr byte ringPin {1};

constexpr unsigned long intervallRing {1000}; //intervall klingeln (ms)
constexpr unsigned long intervallBreak {10000}; //intervall Pause zwischen klingeln (ms)
unsigned long breakTime = intervallRing;
unsigned long lastmillis;
byte zaehler = 0;

void setup()
{
  pinMode(ringPin, OUTPUT);
  digitalWrite(ringPin, LOW);
}

void loop()
{
  machSwitchFunktion();
}

void machSwitchFunktion()
{
  if (millis() - lastmillis > breakTime)
  {
    lastmillis = millis();
    switch (zaehler)
    {
      case 0 ... 3:
        digitalWrite(ringPin, !digitalRead(ringPin));
        if (zaehler == 3)
        {
        }
        break;
      case 4:
        breakTime = intervallBreak;
        digitalWrite(ringPin, LOW);
        break;
      case 5:
        breakTime = intervallRing;
        break;
      default:
        zaehler = 0;
        break;
    }
    zaehler++;
  }
}

Ist tensee - #1

Bis jetzt habe ich noch nicht wirklich verstanden, was es tun soll.
Aber ich kann zeigen wie es bei mir in etwa aussehen würde.


#include <CooperativeTask.h>

const int RING_PIN =  LED_BUILTIN;



class Test : public Task
{
  private:
    int inner ;  // Laufvariable
    int outher ; // Laufvariable

  public:
    virtual void run() override
    {

      TaskBlock
      {
        pinMode(RING_PIN, OUTPUT);
        while(1)
        {
          for(outher = 0; outher < 2; outher++)
          {
            for(inner = 0; inner < 2; inner++)
            {
              digitalWrite(RING_PIN, 1);
              taskPause(1000); // leucht dauer
              digitalWrite(RING_PIN, 0);
              taskPause(1000); // dunkelzeit
            }
            taskPause(1000); // zwischen dual blink
          }
          taskPause(10000);
        }
      }
    }
} test;

void setup()
{
}

void loop()
{
  scheduler.run();
}

https://forum.arduino.cc/uploads/short-url/coceQ2S8lS0uUIVzj4DLo8MuMYr.zip

@sskwebghost
Ich hätte da für dich eine Library:
http://werner.rothschopf.net/microcontroller/202202_tools_led_en.htm

Du kannst statt einer LED natürlich auch einen Buzzer anschließen.
Du nimmst einfach den Effekt für die Rhythm LED und setzt deine Zeiten:

/*
  Rhythm LED
  
  by noiasca
*/

#include <Noiasca_led.h>               // download library from http://werner.rothschopf.net/microcontroller/202202_tools_led_en.htm
#include <utility/Noiasca_discrete.h>  // use the discrete pins on the Arduino/Microcontroller
RhythmPin rhythmLed {13};              // declare LED and assign pin

void setup() {
  rhythmLed.begin();                   // you have to call the .begin() method for the LED
  //                      ON   OFF   ON   OFF
  rhythmLed.setInterval(1000, 1000, 1000, 10000);
  rhythmLed.on();                      // you can switch the effect on
}

void loop() {
  rhythmLed.update();                  // you have to call update() for the LED
}

sollte das geblinke abgeschaltet werden müssen ruf einfach ein

rhythmLed.off(); 

Sorry, das ich mich erst jetzt wieder melden - war im Urlaub.

Das Problem habe ich über einen Zustandsautomaten gelöst, wie von @my_xy_projekt vorgeschlagen wurden.
Vielen Dank für euren guten Support!

Grüsse webghost

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