Neuling hat ne Frage Projekt Leuchtturm-Karte

Hallo,
ich habe vor eine Landkarte zu bauen in der 6 Leuchttürme durch LED´s angezeigt werden sollen. Diese sollen gleichzeitig, aber alle in ihren originalen Frequenzen leuchten.
Nun meine Frage. Ist es möglich, das mit einem Arduino zu bewerkstelligen?

twoll065

Klar ist das möglich mit der millis() Funktion.
Wenn du die Frequenz der einzelnen Türme kennst kein Problem.
Gruß
Bernward

Danke, dann werde ich mich warscheinlich erst einmal in die Programmierung einarbeiten müssen. Ich denke das das kein Anfängerprojekt meht ist.

:roll_eyes:

Lernen, lernen, lernen ist angesagt. Was ja nicht so schlimm ist.
Hat schon jemand so ein ähnliches Projekt probiert?

Nette Idee....

Prinzipiell ja. Du brauchst für jeden Leuchtturm einen Schaltausgang. Da hast Du genug von.

Der Rest ist Software. Das Lichttelegramm des Leuchtfeuers ist ja nichts weiter als das das Licht zu bestimmten Taktzeiten an oder aus ist.

Du musst Dir also eine Tabelle erstellen, in denen alle Hell- und Dunkelzeiten auf einer Zeitschiene abgebildet werden. Dort suchst Du Dir das kleinste gemeinsame Zeitfenster, was auch als Takt dienen kann.

Das könnte dann so aussehen:

LT1: 11100111001110011100....
LT2: 11001100110011001100....

usw.

Der Rest ist dann eigentlich simpel. Die Steuersequenz packst Du pro Leuchtturm in ein Array.

Im Loop läufst Du im Zeittakt. Pro Zeittakt nimmst Du den nächsten Wert aus dem Array und schaltest je nach Wert die entsprechende LED an oder aus. Das machst Du bei jedem Takt für alle Arrays bzw. LED.

Wenn Du am Arrayende angekommen bist gehts wieder von vorn los.

Edit: Anstelle ner Endlosschleife kannst Du natürlich auch nen Timer verwenden und dann beim Auslösen des Timer-Events die Ausgabe machen.

Ein kleiner Tipp zum lernen
wenn du im folgenden Sketch in der setup blinkdauer =1000; deklarierst, dann blinkt die LED im Sekundentakt.
Und so kannst du das parallel für alle 6 machen.

if (millis() - blinken < blinkdauer) digitalWrite(LED, HIGH);
   else digitalWrite(LED, LOW);
  if (millis() - blinken > blinkdauer * 2) blinken = millis();

Gruß
Bernward

Wenn dir vieleDinges Ansatz gefällt, will ich nichts sagen.

Sollte es dir aber (zu) kompliziert vorkommen, gibt es auch andere Ansätze (ohne kleinstes gemeinsames Zeitfenster und Takt -- eher wie Bernward's Vorschlag)

Unter der Voraussetzung, dass jeder Leuchtturm nur ein einfaches Muster (z.B. 1sec an, 9 sec aus) hat, musst du nur diese zwei Zahlen haben und dir merken, wann zum letzten Mal geschaltet wurde.

Wenn du keine delay() Funktion in loop verwendest, ist loop() in einer Millisekunde (oder so *) fertig und startet dann gleich wieder.
*)Ob so ein Durchlauf 100µs oder 10ms dauert, ist egal, aber das ist eine normale Größenordnung

In fast jedem Durchlauf wird dein Programm feststellen, dass nichts zu ändern ist; ganz selten wird festgestellt, dass eine LED einen anderen Zustand kriegt, diese letzte Schaltzeit wird gespeichert, und fertig.

Dass man so -beliebig- viele LEDs unabhängig voneinander schalten kann, ist hoffentlich klar :wink:
( na ja, mehr als Udo's 20 LEDs erfordern mehr Aufwand )

Danke schon mal,
so ähnlich wie der Fast Counter?

Ich würde für sowas die Blinksequenz einfach "malen", also so: POV Reloaded | Blinkenlight. Einfach die Periodenlänge von 2ms auf 10 oder 100ms hochdrehen und gut.

Also etwa so:

MsTimer2::set(100, blink);

Danach kannst Du bis zu 20 Leuchttürme kontrollieren :wink:

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 :wink:

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() {}

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. :slight_smile: