blink_without_delay für Fortgeschrittene

Hallo,

ich verstehe zwar mittlerweile die blink_without_delay-Logik, stehe nun aber vor einer komplexeren LED-Schalt-Aufgabe, bei der ich leider nicht weiterkomme.

Folgendes Problem:

Eine LED soll innerhalb der ersten 250ms einer Sekunde bis zu fünf Mal aufleuchten. Es soll also auch möglich sein die LED einmal, zweimal, dreimal und viermal über diese 250ms für jeweils 30ms pro "Aufleuchten" aufleuchten zu lassen.

Die Leuchtdauer pro Aufleuchten ist mit 30ms festgelegt und die Abstände zwischen den einzelnen Einschaltvorgängen müssen identisch sein.

Ein Beispiel (y-Achse = Zeit):

Die LED soll in diesem Fall dreimal während der ersten 250ms für jeweils 30ms aufleuchten:

0ms LED ein
30ms LED aus

125ms LED an
155 ms LED aus

250ms LED ein
280ms LED aus

Nach 1000ms soll sich das Ganze dann wiederholen..

Natürlich soll nebenher noch anderer Code abgearbeitet werden können (daher möchte ich mit millis() arbeiten) und Timer2, oder wie das heisst scheidet aus.

Bin leider scheinbar zu doof eine entspr. Logik zu entwickeln.

Vielleicht mag mich ja einer von Euch auf die richtige Fährte führen.

Ich erwarte keine Komplettlösung.

Gruß Chris

Pseudo-Code

boolean ledOn;
unsigned long lastMillis;
unsigned long newMillis = millis() - lastMillis;

if(newMillis >= 1000) {
  lastMillis = millis()
}

if(newMillis < 30) ledOn = true;
else if(newMillis < 60) ledOn = false;
// ....
const int ledPin =  13;
unsigned long previousMillis = 0;
unsigned long currentMillis = 0;
boolean ledState = 1;
int counter = 0;

void switchLED() {
	ledState = !ledState;
	digitalWrite(ledPin,ledState);	
}

void LED() {
	currentMillis = millis();
	if (currentMillis - previousMillis >= 1) {
		previousMillis = currentMillis;
		counter++;
	}
	if (counter ==   30) switchLED();
	if (counter ==  125) switchLED();
	if (counter ==  155) switchLED();
	if (counter ==  250) switchLED();
	if (counter ==  280) switchLED();
	if (counter == 1000) {
		counter = 0;
		switchLED();
	}
}

int main() {
	init();
	//setup
	pinMode(ledPin,OUTPUT);
	digitalWrite(ledPin,ledState);
	//loop
	while (1) {
		LED();
		//some other code
	}
}

Hallo,

ich danke Euch für Eure Codes.

sschultewolters Code ist zwar Pseudocode, was vollkommen in Ordnung ist, berücksichtigt jedoch nicht die von mir geforderte Angabe mit den 250ms.

PhunThoms Code kompiliert bei mir zwar, führt aber zu Fehlverhalten der LED.

Hier mal mein Code, welcher leider ebenfalls (mir unerklärliche) Fehlverhalten der LED aufweist:

/**************************************************  LED-Parameter  */

const byte LED = 13;  // Initialisierung der LED

byte LED_st = 11;  // Blinkanzahl der LED

int ms_dur = 1000;
unsigned long blink_start_time = 0;
const byte short_LED_dur = 30;         // Definiert die Leuchtdauer der LED 
unsigned long LED_short_on_time = 0;   // Definiert den Zeitpunkt, wann die LEDs zuletzt eingeschaltet wurde
const int LED_quarter_dur = 250;      // Definiert die 250ms, in denen die LED blinken soll

void setup()
{
  pinMode(LED, OUTPUT);
}

void loop()
{
  if(millis() - blink_start_time >= ms_dur)  // Jede Sekunde..
  {
    digitalWrite(LED,HIGH);
    blink_start_time = millis();
  }


  if(millis() - blink_start_time <= LED_quarter_dur)                               // So lange noch keine 250ms einer angebrochenen Sekunde vergangen sind,
  {
    if(millis() - LED_short_on_time >= (LED_quarter_dur / (LED_st - 10)))     // wenn das Intervall noch nicht überschritten wurde
    {
      if(digitalRead(LED == LOW))
      {
        digitalWrite(LED ,HIGH);
      }
      LED_short_on_time = millis();
    }
  }

  if(millis() - LED_short_on_time >= short_LED_dur)
  {
    if(digitalRead(LED == HIGH))
    {
      digitalWrite(LED ,LOW);
    }
  }
}

Die LED blinkt nicht wie von mir erwartet regelmäßig, sondern manchmal etwas kürzer, oder nur einmal anstatt zweimal.

Bitte um Hilfe.

Gruß Chris

Chris72622:
sschultewolters Code ist zwar Pseudocode, was vollkommen in Ordnung ist, berücksichtigt jedoch nicht die von mir geforderte Angabe mit den 250ms.

Der Code müsste funktionieren. Du musst natürlich die If-abfragen passend ergänzen. Es ist nur der Anfang.

Alternative:(Pseudocode)

boolean ledOn;
unsigned long lastMillis;
unsigned long newMillis = millis() - lastMillis;

if(newMillis >= 1000) {
  lastMillis = millis()
}

if( (newMillis < 30) || ( (newMillis >125)&&(newMillis < 155))||( (newMillis >250)&&(newMillis <280)))  ledOn = true;
else ledOn= false;

mein Versuch (geht davon aus, dass mit einer Pause angefangen wird und mit einem Blink aufgehört wird, demnach gleich viele Pausen wie Blinks vorhanden sind):

boolean newPeriod = true;
boolean pause = true;
unsigned long startPeriod;
unsigned long startPause;
unsigned long startBlink;

void blinkSequence (int blinks) {
		int pauseTime = (250 - (blinks * 30)) / blinks;                  	// legt die Pausenzeit bei Anzahl blinks fest
		if (newPeriod) {
				startPeriod = millis();
				startPause = millis();
				newPeriod = false;
		}
		if (millis() - startPeriod < 251) {				                      // nur in ersten 250ms wird geblinkt
				if (millis() - startPause >= pauseTime && pause) {
			 			digitalWrite(led, HIGH);
				         		pause = false;
			 			startBlink = millis();
				}
				if (millis() - startBlink >= 30 && !pause) {
			 			digitalWrite(led, LOW);
				        		pause = true;
			 			startPause = millis();
				}
		}
		else if (millis() - startPeriod >= 1000 {
				newPeriod = true;
		}
}

Habs nicht ausprobiert.

Hallo,

habe den Code “kompilierbar aufbereitet”.

const byte led = 13;  // Initialisierung der LED

boolean newPeriod = true;
boolean pause = true;
unsigned long startPeriod;
unsigned long startPause;
unsigned long startBlink;

void setup()
{
}

void loop()
{
}

void blinkSequence (int blinks)
{
  int pauseTime = (250 - (blinks * 30)) / blinks;  // legt die Pausenzeit bei Anzahl blinks fest
  if (newPeriod)
  {
    startPeriod = millis();
    startPause = millis();
    newPeriod = false;
  }
  if (millis() - startPeriod < 251)
  {  // nur in ersten 250ms wird geblinkt
    if (millis() - startPause >= pauseTime && pause)
    {
      digitalWrite(led, HIGH);
      pause = false;
      startBlink = millis();
    }
    if (millis() - startBlink >= 30 && !pause)
    {
      digitalWrite(led, LOW);
      pause = true;
      startPause = millis();
    }
  }
  else if (millis() - startPeriod >= 1000)
  {
    newPeriod = true;
  }
}

Ergebnis: Die LED leuchtet kontinuierlich.

Gruß Chris

Edit: Wie bekommt man eigentlich so große Tab-Schritte in den Code?

Hallo,

warum probierst du nicht meinen Code? Gerade in richtigen Code übersetzt, probiert und funktioniert.

int led = 13;
unsigned long lastMillis;
unsigned long newMillis = millis() - lastMillis;

void setup() {
  pinMode(led, OUTPUT);
}
void loop() {
  newMillis=millis()-lastMillis;
 if(newMillis >= 1000) {
  lastMillis = millis();
}

if( (newMillis < 30) || ( (newMillis >125)&&(newMillis < 155))||( (newMillis >250)&&(newMillis <280)))
   {digitalWrite(led, HIGH);}   
 else
   {digitalWrite(led, LOW);}

War grad dabei.

Werde es jetzt letzten Endes der Einfachheit halber so machen.

Für die Nachwelt:

In dem von Dir zuletzt geposteten Code fehlt die letzte Klammer.

Danke allen Beteiligten!

Gruß Chris

Chris72622:
Hallo,

habe den Code “kompilierbar aufbereitet”.

Ergebnis: Die LED leuchtet kontinuierlich.

Gruß Chris

Edit: Wie bekommt man eigentlich so große Tab-Schritte in den Code?

Du hast vergessen, die Funktion in der Loop aufzurufen. So müssts sein:
E: und den LED Pin als Output zu definieren.

const byte led = 13;  // Initialisierung der LED

boolean newPeriod = true;
boolean pause = true;
unsigned long startPeriod;
unsigned long startPause;
unsigned long startBlink;

void setup()
{
pinMode(led, OUTPUT);
}

void loop()
{
blinkSequence(3);
}

void blinkSequence (int blinks)
{
  int pauseTime = (250 - (blinks * 30)) / blinks;  // legt die Pausenzeit bei Anzahl blinks fest
  if (newPeriod)
  {
    startPeriod = millis();
    startPause = millis();
    newPeriod = false;
  }
  if (millis() - startPeriod < 251)
  {  // nur in ersten 250ms wird geblinkt
    if (millis() - startPause >= pauseTime && pause)
    {
      digitalWrite(led, HIGH);
      pause = false;
      startBlink = millis();
    }
    if (millis() - startBlink >= 30 && !pause)
    {
      digitalWrite(led, LOW);
      pause = true;
      startPause = millis();
    }
  }
  else if (millis() - startPeriod >= 1000)
  {
    newPeriod = true;
  }
}

Die Tabschritte sind aus dem Texteditor kopiert, wohl deshalb etwas zu gross… :slight_smile:
E2: die an die Funktion übergebene Zahl sollte übrigens für die Anzahl Blinks stehen. In diesem Fall sollte also die LED 3mal innerhalb der ersten 250ms blinken. Wenn du die Zahl durch 1,2,4 oder 5 ersetzt, sollte es entsprechend diese Anzahl blinken… probiers doch bitte auch aus, wenn der andere Code funktioniert, würde mich wundernehmen :slight_smile: und um es selbst auszuprobieren, habe ich gar keine Möglichkeit, so genaue Messungen vor zu nehmen. Abweichungen können übrigens entstehen, da beim Geteiltrechnen Kommazahlen entstehen würden, die hier einfach konsequent abgerundet werden.

Ich verweise nochmal auf diese Lib: http://forum.arduino.cc/index.php?topic=165552.0
Den Link habe ich dir schonmal bezüglich eines anderen Themas geschickt.

Falls es noch komplexer werden sollte macht es Sinn, da du deine Aufgaben dann einfach miteinander Verschachteln kannst ohne dass zuviele If Abfragen benötigt werden.

Der Code würde dann wie folgt aussehen:

#include <simpleThread.h>

#define _sT_cnt _sT_cnt_2 
simpleThread_init(_sT_cnt);
simpleThread_new_timebased_dynamic (_sT_P1  , _sT_millis, 30, _sT_start  , LED_Blink);
simpleThread_new_timebased_dynamic (_sT_P2  , _sT_millis, 1000, _sT_start  , Start_LED_Blink);

uint8_t g_blink_cnt = 0;
uint8_t g_led_status = 0;

// Thread LED Blink
  void simpleThread_setup(LED_Blink)
  {
    g_blink_cnt = 0;  
  }
  void simpleThread_stable(LED_Blink)
  {
    //Led ausschalten
    digitalWrite(13, LOW);
  } 
  boolean simpleThread_loop(LED_Blink)
  {  
    if(g_blink_cnt < 8) { //8*30 = 240 ms
      // LED Blink
      g_led_status = !g_led_status;
      digitalWrite(13, g_led_status);
      g_blink_cnt++;
    } else {
      // Thread wird sich selbst deaktivieren
      simpleThread_stopStable(LED_Blink);
    }
    return 0;  
  }

// Thread Start Led Blink
  void simpleThread_setup(Start_LED_Blink)
  {}
  boolean simpleThread_loop(Start_LED_Blink)
  {   
    simpleThread_restart(LED_Blink);  
    return 0;  
  }

// Setup
void setup() 
{    
  pinMode(13, OUTPUT);
  /* Threads initialisieren */
  simpleThread_initSetup(_sT_cnt);
}

void loop() {
  /* calls the threads */ 
  simpleThread_run(_sT_priority);
  
  // hier der andere Code, besser noch in einem eigenen Thread
}

Falls noch mehr Zeiten bei dir hinzukommen sollten, führt auf dauer kein Weg dran vorbei eine Lib zu verwenden die diese oder eine Ähnliche Funktionalität bietet.

Hallo Jomelo,

ich weiss und Du hast ganz bestimmt auch recht.

Jedoch muss ich zugeben, dass mich die Komplexität der SimpleThreads-Geschichte erschlagen hat.

Ich habe zwar noch nicht aufgegeben, kann aber das aktuelle Projekt nicht komplett umstricken.

Sollte es mittlerweile anfängerfreundlichere Beispiele/ Einstiegsmöglichkeiten geben, würde ich mich sehr über eine Nachricht (gerne auch per PM) freuen.

Ehrlich gesagt sehe ich mich zum jetzigen Zeitpunkt manchmal kurz davor, dass ich mich verstricke und es einen Riesenschlag tut. :~

Gruß Chris

Edit: Dein Code lässt die LED lediglich sekündlich ganz kurz aufblitzen. SimpleThread-Library ist installiert und es wird anstandslos kompiliert. :~

Jomelo:
Ich verweise nochmal auf diese Lib: Projektvorstellung: simpleThreads auf dem Arduino mit Hilfe von Makros - Deutsch - Arduino Forum
Den Link habe ich dir schonmal bezüglich eines anderen Themas geschickt.

Ich habe übrigens auch schon mal was zum Thema "Blinken mit verschiedenen Timings gleichzeitig" gepostet, und zwar hier:

http://forum.arduino.cc/index.php?topic=146086.msg1099068#msg1099068

Beim Projekt "Leuchtturm-Karte" ging es damals darum, einen ganzen Schwung Leuchttürme mit jeweils eigenem Leuchtfeuer-Timing blinken zu lassen. Die Timings hier sind zwar kürzer, aber prinzipiell unterscheidet es sich nicht.

BTW: Dass der millis()-Timer "unrund" läuft, ist Dir hoffenlich bekannt?
Innerhalb von 1000 Millisekunden tickt die millis() Funktion nur 977 mal um eins vor und 23 mal gleich um zwei.

Falls es also bei den 30 Millisekunden drauf ankommt, diese möglichst exakt einzuhalten, würde ich das Timing in dem Fall lieber über die micros() Funktion statt über die millis() Funktion ausstoppen.

jurs:
BTW: Dass der millis()-Timer "unrund" läuft, ist Dir hoffenlich bekannt?

Nein. :astonished:

1000 Dank. Dieses mir bisher fehlende Wissen hat mich in der Vergangenheit schätzungsweise 24h meines Lebens gekostet, da ich mich beim Fehler suchen im Kreis drehte. Schätzungsweise drei von ihnen habe ich heute verbraten. :stuck_out_tongue:

Gruß Chris

Chris72622:
Nein. :astonished:

1000 Dank. Dieses mir bisher fehlende Wissen hat mich in der Vergangenheit schätzungsweise 24h meines Lebens gekostet, da ich mich beim Fehler suchen im Kreis drehte.

Dann passt es ja, dass ich das mal gepostet habe. :wink:

Wenn Du mal sehen möchtest, wo und wie genau der millis() Zähler im Programm springt, kannst Du das mit diesem Sketch selbst austesten:

// Messung des unrunden Laufs der millis()-Funktion
// Test-Sketch by 'jurs' for German Arduino forum

void setup() {
  Serial.begin(57600);
  Serial.println();
  Serial.println("Start");
}

void loop()
{
  static unsigned long count;
  static unsigned long lastMillis;
  unsigned long thisMillis=millis();
  int diff=thisMillis-lastMillis;
  lastMillis=thisMillis;
  if (diff>1) // millis() ist um mehr als 1 gesprungen
  {
    count++;
    Serial.print(count);
    Serial.print('\t');
    Serial.print(thisMillis);
    Serial.print('\t');
    Serial.println(diff);
  }
  if (millis()>10240) while(1); // Stop nach 10 Sekunden
}

Der Sketch gibt 10 Sekunden lang immer dann eine Zeile mit drei Werten aus, wenn der Zähler um mehr als 1 springt. Die angezeigten Werte sind:

  • Anzahl der falschen Zählungen
  • Stand des millis() Zählers beim Auftreten der falschen Zählung
  • um wieviel tatsächlich weitergezählt wird (ist immer "2")

Offenbar zählt millis() intern in 1/1024 Sekunden Schritten, und weil damit nach 1000 Zählungen erst 1000/1024 Sekunden vergangen wären, mogelt die Funktion von Zeit zu Zeit zum Ausgleich eine Millisekunde extra drauf, damit es zeitlich wieder zusammenpasst und millis() wenigstens im zeitlichen Durchschnitt pro Sekunde um 1000 Millisekunden weiterzählt.

Falls Du Englisch kannst, Dann schau Dir einfach die Beisspiele in meinem Blog an. Da sind jede Menge Effekte die Blinken ohne in loop irgendwas zu tun.

Ja, dankeschön!

Hier wird man ja echt super versorgt. :slight_smile:

Gruß Chris