Neuling hat ne Frage Projekt Leuchtturm-Karte

Wenn Du mir die Frequenzen der 6 Leuchttürme verrätst und versprichst, daß Ergebnis ins Netz zu stellen, dann baue ich das POV Programm direkt für Deinen Fall um.

Ich hab ja nur eine Handvoll mal nach Leuchtfeuern navigiert in meinem Landratten-Hobbyurlaubssegler-Leben, und im echten Leben hat man auch den Nachteil, selten mehrere gleichzeitig zu sehen.

Dass die Feuer tatsächlich über lange Zeiträume synchron sind, entspricht eigentlich nicht meinem Eindruck.

Udo's POV Ansatz ist toll, lebt aber von der Synchronisation, während Leuchttürme für mich langsam und scheinbar unregelmäßig blinken und die Muster erst mit langem Hinschauen und Mitzählen deutlich werden...

Hier vorstellen solltest du dein Projekt in jedem Fall ;)

Das wäre ja Spitze :stuck_out_tongue_closed_eyes:

Hier die Zeiten:

alles in Sekunden.

twoll065

Leuchtfeuer.pdf (4.94 KB)

twoll065: alles in Sekunden.

Im Prinzip ist das Blinkenlassen von ein paar LEDs in ihrem jeweils eigenen Blinkrhythmus keine große Aktion.

Es geht es doch eigentlich nur darum, ein in Text- und Tabellenform formuliertes Problem in geeignete Datenstrukturen und Algorithmen zu verpacken, so dass der Arduino dann die Pins High und Low schalten kann, um LEDs zum Leuchten zu bringen oder dunkel zu belassen.

Die drei Statusarten "ein", "aus" und "Pause" verstehe ich so, dass "Pause" ebenfalls "aus" bedeutet, so dass es eigentlich nur "ein" und "aus" gibt, oder?

Beim Turm Pilsum schließt sich offenbar an 3 s "aus" noch 8,5s "Pause" an, was sich zu 11,5s "aus" zusammenfassen läßt?

Schau mal drüber, ob es so korrekt ist (Timings in Millisekunden, damit man alles mit "ganzen Zahlen" rechnen kann):

//                      ein   aus   ein   aus   ein   aus   ein   aus
int Norderney_timing[]={ 200, 2800, 200 , 2800, 200 , 5800};
int Pilsum_timing[]   ={3000, 3000, 3000,11500};
int Campen_timing[]   ={ 700, 2300,  700, 2300,  700, 2300,  700, 5300};
int Schillig_timing[] ={6000, 6000};
int Voslapp_timing[]  ={6000, 3000};
int Tossens_timing[]  ={6000, 1000};

So?

Moin, genau so ist das gemeint.

Pause = aus

twoll065: genau so ist das gemeint.

Alles klar, anbei mein Codevorschlag.

// Leuchtturm LEDs schalten by Jurs im Arduino-Forum Deutsch
//
// LED: Pin-Nummer der zu schaltenden LED
// gesamt= Gesamtlänge aller Timings zusammen für einen Zyklus des Leuchtturms
// n= Anzahl der nachfolgenden ein/aus Timings
//                      LED gesamt  n   ein   aus   ein   aus   ein   aus   ein   aus
int Norderney_timing[]={ 2, 12000,  6,  200, 2800,  200, 2800, 200 , 5800};
int Pilsum_timing[]   ={ 3, 20500,  4, 3000, 3000, 3000,11500};
int Campen_timing[]   ={13, 15000,  8,  700, 2300,  700, 2300,  700, 2300,  700, 5300};
int Schillig_timing[] ={ 5, 12000,  2, 6000, 6000};
int Voslapp_timing[]  ={ 6,  9000,  2, 6000, 3000};
int Tossens_timing[]  ={ 7,  7000,  2, 6000, 1000};

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  // define OUTPUT pins
  pinMode(Norderney_timing[0], OUTPUT);
  pinMode(Pilsum_timing[0], OUTPUT);
  pinMode(Campen_timing[0], OUTPUT);
  pinMode(Schillig_timing[0], OUTPUT);
  pinMode(Voslapp_timing[0], OUTPUT);
  pinMode(Tossens_timing[0], OUTPUT);
}


void TurmStatus(int* Turmdata)
{
  // Anfangs von eingeschaltetem Blinkstatus ausgehen
  boolean blinkStatusOn=true;
  // Zeit im Blinkzyklus per Modulo-Arithmetik 
  // aus millis() und der Gesamt-Zykluszeit des Turms errechnen
  long inCycleTime=millis() % Turmdata[1];
  int onoffCount=Turmdata[2]; // Anzahl der ein/aus Schaltungen
  while (onoffCount>0 && inCycleTime-Turmdata[3+Turmdata[2]-onoffCount]>0)
  {
    // bereits abgelaufene Blinkzeit abziehen
    inCycleTime-=Turmdata[3+Turmdata[2]-onoffCount];
    onoffCount--; // Anzahl der ein/aus Schaltungen vermindert
    blinkStatusOn=!blinkStatusOn; // Blinkstatus wechselt
  }
  // wenn alles durch, dann den errechneten Blinkstatus setzen
  digitalWrite(Turmdata[0], blinkStatusOn);
}

void loop() {
  TurmStatus(Norderney_timing);
  TurmStatus(Pilsum_timing);
  TurmStatus(Campen_timing);
  TurmStatus(Schillig_timing);
  TurmStatus(Voslapp_timing);
  TurmStatus(Tossens_timing);
  // put the rest of your loop code here
}

In das Daten-Array pro Leuchtturm habe ich nun noch drei weitere Werte gepackt, und zwar - LED: die Pin-Nummer des zu schaltenden Arduino Digital-Pins - gesamt: die Gesamt-Zykluslänge (alle ein/aus-Einzelzeiten zusammengezählt) - n: die Anzahl der ein/aus Schaltungen an diesem Leuchtturm pro Zyklus

So konnte ich den Code kurz halten und trotz der einfachen Programmlogik ohne zusätzliche Libraries sollten die Schaltzeiten zwar nicht auf die Mikrosekunde genau, aber auf einem 16 MHz Arduino doch so ca. +/- zwei Millisekunden eingehalten werden.

Natürlich gibt es immer mehrere Möglichkeiten, so etwas zu programmieren.

Es funktioniert XD XD XD

Du bist ein Held

Danke

twoll065:
Es funktioniert XD XD XD

Du bist ein Held

Danke

Bitte, gern geschehen!
In lichten Momenten schreibe ich sowas hin und es funktioniert praktisch auf Anhieb.

Zwei kleine Code-Korrekturen habe ich aber trotzdem noch:

Das “Serial.begin(9600);” ist natürlich überflüssig in einem Programm, das nichts über Serial ausgibt.
Schadet aber auch nicht weiter.

Gewichtiger ist die leicht falsche Abbruchbedingung, es muss in dieser Zeile “>=” statt “=” heißen,
korrekt wäre:
while (onoffCount>0 && inCycleTime-Turmdata[3+Turmdata[2]-onoffCount]>=0)

Dann wird das Timing auch nicht " ca. +/- zwei Millisekunden " sondern “besser als +/- eine Millisekunde” eingehalten.
Das habe ich gerade mal mit ein paar Timing-Tests festgestellt.

Wenn ich mich nicht verrechnet habe, dauert es 51660 Sekunden, bis sich ein identischer Blinkzyklus wiederholt, man kann also kaum zweimal pro Tag absolut identische Blinkfolgen beobachten.

Der Quellcode ist so gestaltet, dass sich mehr blinkende Türme in einer größeren Karte leicht auch nachträglich einbauen lassen. Und die “Drehzahl” der loop()-Funktion liegt momentan mit 6 Türmen bei weit über 2000 pro Sekunde, so daß jeder Turmstatus in jeder Millisekunde mehr als zweimal aktualisiert wird, da sind also noch Reserven vorhanden.

Viel Spaß beim Basteln Deiner blinkenden Landkarte!

Eine andere Variante wäre z.B. so:

//
//  www.blinkenlight.net
//
//  Copyright 2013 Udo Klein
//
//  This program is free software: you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program. If not, see http://www.gnu.org/licenses/

#include <MsTimer2.h>

const uint8_t pins = 6;

template <uint8_t led, uint16_t d1, uint16_t d2, uint16_t d3, uint16_t d4, 
                       uint16_t d5, uint16_t d6, uint16_t d7, uint16_t d8>
void light_my_fire() {  
    static uint16_t phase = 0;
    
    phase = phase < d1+d2+d3+d4+d5+d6+d7+d8-1? phase+1: 0;

    digitalWrite(led, phase < d1                  ? HIGH:
                      phase < d1+d2               ? LOW:
                      phase < d1+d2+d3            ? HIGH:
                      phase < d1+d2+d3+d4         ? LOW:
                      phase < d1+d2+d3+d4+d5      ? HIGH:
                      phase < d1+d2+d3+d4+d5+d6   ? LOW:
                      phase < d1+d2+d3+d4+d5+d6+d7? HIGH:
                                                    LOW);                                                   
} 

void blink() {
    light_my_fire<0,  200, 2800,  200, 2800,  200, 5800,    0,    0>();
    light_my_fire<1, 3000, 3000, 3000, 3000,    0, 8500,    0,    0>();
    light_my_fire<2,  700, 2300,  700, 2300,  700, 2300,  700, 5300>();
    light_my_fire<3, 6000, 6000,    0,    0,    0,    0,    0,    0>();
    light_my_fire<4, 6000, 3000,    0,    0,    0,    0,    0,    0>();
    light_my_fire<5, 6000, 1000,    0,    0,    0,    0,    0,    0>();


}

void setup() {
    for (uint8_t pin=0; pin<pins; ++pin) {
        pinMode(pin, OUTPUT);
    }

    MsTimer2::set(1, blink);
    MsTimer2::start();
}

void loop() {}

Und wenn gewünscht ist, daß die Leuchtfeuer nicht 100% gleich in der Phase laufen, dann einfach bei der letzten Pause immer 1ms draufhauen. Also so:

//
//  www.blinkenlight.net
//
//  Copyright 2013 Udo Klein
//
//  This program is free software: you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program. If not, see http://www.gnu.org/licenses/

#include <MsTimer2.h>

const uint8_t pins = 6;

template <uint8_t led, uint16_t d1, uint16_t d2, uint16_t d3, uint16_t d4, 
                       uint16_t d5, uint16_t d6, uint16_t d7, uint16_t d8>
void light_my_fire() {  
    static uint16_t phase = 0;
    
    phase = phase < d1+d2+d3+d4+d5+d6+d7+d8-1? phase+1: 0;

    digitalWrite(led, phase < d1                  ? HIGH:
                      phase < d1+d2               ? LOW:
                      phase < d1+d2+d3            ? HIGH:
                      phase < d1+d2+d3+d4         ? LOW:
                      phase < d1+d2+d3+d4+d5      ? HIGH:
                      phase < d1+d2+d3+d4+d5+d6   ? LOW:
                      phase < d1+d2+d3+d4+d5+d6+d7? HIGH:
                                                    LOW);                                                   
} 

void blink() {
    light_my_fire<0,  200, 2800,  200, 2800,  200, 5801,    0,    0>();
    light_my_fire<1, 3000, 3000, 3000, 3000,    0, 8501,    0,    0>();
    light_my_fire<2,  700, 2300,  700, 2300,  700, 2300,  700, 5301>();
    light_my_fire<3, 6000, 6001,    0,    0,    0,    0,    0,    0>();
    light_my_fire<4, 6000, 3001,    0,    0,    0,    0,    0,    0>();
    light_my_fire<5, 6000, 1001,    0,    0,    0,    0,    0,    0>();


}

void setup() {
    for (uint8_t pin=0; pin<pins; ++pin) {
        pinMode(pin, OUTPUT);
    }

    MsTimer2::set(1, blink);
    MsTimer2::start();
}

void loop() {}

[quote author=Udo Klein link=topic=146086.msg1099399#msg1099399 date=1359736490] Eine andere Variante wäre z.B. so: [/quote]

Das habe ich mir gedacht, dass Dein Programm ganz anders wird als meins. Und vom Mikrosekunden-Timing her noch viel akkurater als meins, das die Timings nur mit millis() macht.

Sehr schöner Programm-Sketch!

jurs: Sehr schöner Programm-Sketch!

Dem möchte ich nur bedingt zustimmen, ich denke nämlich, dass das so manchen Anfänger überfordern wird. Auf jeden Fall ist er sehr effektiv programmiert und aus meiner Sicht eine sehr gute Leistung. :)

So wie ich das sehe ist Meins nicht genauer als Deins. Das Hauptproblem ist wie immer die Genauigkeit des Hardware Taktes. Das Einzige wo Deine Lösung ein bischen daneben haut ist alle 50 Tage beim millis overflow. Ansonsten sehe ich nicht wieso Deine Lösung weniger genau sein sollte. Wieso denkst Du, daß Deine Lösung nicht so genau ist?

Anmerkung: durch das Pollen von "millis" fängst Du Dir zwar Phasenjitter ein, aber den hat meine Lösung auch. Wenn ich den nicht haben wollte müsste ich Timer 0 stoppen. Aber genauer wird es dadurch auch nicht. Nur der Jitter nimmt dann ab.

Hauptunterschied ist der deutlich geringere Speicherverbrauch und die geringere Prozessorlast bei meiner Lösung. Und loop() bleibt frei ;) Aber am Ende zählt immer nur ob die Anforderungen erfüllt sind und da ist Deine Lösung gleich gut. Und sie war schneller verfügbar.

Knobelfrage: warum funktioniert mein Programm überhaupt?

D.h. wo hält es eigentlich den Phasenzustand der Leuchttürme? Betonung auf "mehr als ein Turm" ;)

@sth77: wieso überfordert das Anfänger? Copy + Paste kann jeder ;) Die Lösung von Jurs ist auch nicht leichter zu verstehen wenn man nicht programmieren kann.

sth77: Dem möchte ich nur bedingt zustimmen, ich denke nämlich, dass das so manchen Anfänger überfordern wird.

Ja, von meinem Programm-Sketch werden Anfänger auf Anhieb vermutlich mehr Codeteile verstehen als von Udos Code.

Aber andererseits ist Copy-and-Paste bei einem fertigen Programm so oder so nicht allzu schwierig.

Das mit den "Templates" gehört beispielsweise so überhaupt nicht zu meinen Programmiertechniken und selbst ich mußte eben erstmal googeln, was Templates bei C++-Code überhaupt sind und was die machen. Also bei Udos Code mußte ich auch erstmal Tante Google bemühen, um den Code überhaupt zu verstehen, was der macht.

Interrupts und Nicht-Standard-Libraries sind auch nicht das allererste Thema für Anfänger, aber wenigstens damit habe ich schon was gemacht und die MsTimer-Library auch schon mal früher gesehen als ich mir Udos DCF-Empfangsfilter angesehen habe.

[quote author=Udo Klein link=topic=146086.msg1099536#msg1099536 date=1359741099]
Das Einzige wo Deine Lösung ein bischen daneben haut ist alle 50 Tage beim millis overflow. [/quote]

Ja, wobei man das 50-Tage-Problem mit einer Subtraktions-Arithmetik statt Modulo-Arithmetik und einigen Zusatzvariablen auch in meinem Programm beseitigen könnte und das Programm damit wohl auch noch schneller machen könnte.

Ich habe zum Testen mal eine Interrupt-0 Behandlungsroutine für Pin-2 eingeklinkt, in der die High- und Low-Pegel vom micros()-Timing her genau ausgemessen und in der Loop per Serial ausgegeben werden. Bei meinem Programm jittert es, und bei den Timings mit Deinem Programm-Sketch sind keine Differenzen zum Soll-Timing feststellbar.

Hauptunterschied ist der deutlich geringere Speicherverbrauch und die geringere Prozessorlast bei meiner Lösung. Und loop() bleibt frei :wink:

Prozessorlast und freie loop() OK, da gebe ich Dir Recht.

Aber lasse bei meinem Programm mal die nicht benötigte Initialisierung mit “Serial.begin(9600)” im Programm-Sketch weg! Also die kompilierte Programmdatei von meinem Programm ist dann DEUTLICH kleiner als die von Deinem. Mit auskommentierter Serial-Initialisierung hat mein Programm unter Arduino 1.0.1 nur eine angezeigte Binäre Sketchgröße von 1254 Bytes.

Knobelfrage: warum funktioniert mein Programm überhaupt?

D.h. wo hält es eigentlich den Phasenzustand der Leuchttürme? Betonung auf “mehr als ein Turm” :wink:

Der Phasenzustand der Leuchttürme wird doch in Deinem wie auch in meinem Sketch nur im LED-Pin gespeichert und vor dem Setzen des Pins jedesmal zur Laufzeit neu ermittelt: In meinem Sketch in den Zeilen unmittelbar vor dem Setzen des Zustands mit digitalWrite, und bei Deinem Sketch direkt im digitalWrite-Funktionsaufruf per Verzweigungslogik.

@twoll065: Die MsTimer2-Library mußt Du vor dem Kompilieren schon noch herunterladen und im Libraries-Verzeichnis installieren, Udo verwendet nämlich nicht nur erweiterte Programmiertechniken, sondern auch eine externe Nicht-Standard Library in seinem Sketch.

Habe ich gemacht

ausgegeben wird:

avrdude: stk500_getsync(): not in sync: resp=0xf0

Was heißt das?

twoll065: ausgegeben wird:

avrdude: stk500_getsync(): not in sync: resp=0xf0

Was heißt das?

Manchmal verhaspeln sich Compiler und Uploader, einfach nochmal probieren! Funktioniert's auch nach mehrmaligen Versuchen nicht?

Alles neu gestartet aber geht nicht

twoll065:
Alles neu gestartet aber geht nicht

Merkwürdig.
Nimm in Udos Sketch mal andere Pins für die LEDs als die Hardware-Serial RX/TX Pins an 0 und 1!

Vielleicht ist Udos Programm ja so schnell, dass es schon Ausgaben an 0 und 1 macht, während der Upload noch gar nicht abgeschlossen ist.

Vorschlag: Nimm die Pins 2 bis 7 als LED-Pins statt 0 bis 5!

Statt:

 light_my_fire<0,  200, 2800,  200, 2800,  200, 5800,    0,    0>();
 light_my_fire<1, 3000, 3000, 3000, 3000,    0, 8500,    0,    0>();

Setze

 light_my_fire<6,  200, 2800,  200, 2800,  200, 5800,    0,    0>();
 light_my_fire<7, 3000, 3000, 3000, 3000,    0, 8500,    0,    0>();

Und statt:

for (uint8_t pin=0; pin<pins; ++pin) {
        pinMode(pin, OUTPUT);
    }

Setze:

for (uint8_t pin=2; pin<pins+2; ++pin) {
        pinMode(pin, OUTPUT);
    }

Hast du den korrekten Com-Port und das richtige Board ausgwählt, bzw. überhaupt einen Arduino angeschloßen?

Addi