Neuling hat ne Frage Projekt Leuchtturm-Karte

@sth77: wieso überfordert das Anfänger? Copy + Paste kann jeder :wink: 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

Hab ich gemacht, umgesteckt auf 2-7, Code geändert, 2-5 leuchten normal und 6+7 glimmen nur im Takt.

Sketch von jurs drauf und 2-7 blinken wie sie sollen
Sketch von Udo Klein geändert, drauf 6 und 7 glimmen nur

Habe es selber gefunden
erste Zeile

const uint8_t pins = 6;
geändert in
const uint8_t pins = 8;

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.

Ich hätte statt Speicherverbrauch sagen sollen "Hauptspeicherverbrauch".

Nachtrag: um die Pinzahl nicht mehr explizit setzen zu müssen hier noch eine kleine Änderung (die Flashbedarf erhöht aber dafür die Benutzung bequemer macht):

//
//  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>

uint8_t setOutput(uint8_t pin) {
    pinMode(pin, OUTPUT);
    return 0;
}

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 = setOutput(led);
    
    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() {
    MsTimer2::set(1, blink);
    MsTimer2::start();
}

void loop() {}

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.

Falsche Antwort. Der Zustand wird dorthin ausgegeben. Aber die Phase, d.h. die Zeit die er bisher in der Periode ist steht woanders.

Hinweis: was macht eigentlich "static"? Und warum funktioniert das hier korrekt?

static uint16_t phase = 0;
phase = phase < d1+d2+d3+d4+d5+d6+d7+d8-1? phase+1: 0;

Der Interrupt in Deinem Sketch wird einmal pro Millisekunde aufgerufen und "phase+1" zählt die Anzahl der Millisekunden bei jedem Aufruf hoch und weist das Ergebnis an phase zu, solange die Bedingung (Anzahl der Millisekunden kleiner als die Summe Einzel-Phasen) erfüllt ist. Sobald die Bedingung nicht mehr erfüllt ist, wird phase wieder auf null gesetzt. Das Speichern der Phase für jeden Turm einzeln funktioniert, weil die Funktion als Template-Funktion tatsächlich so oft vorhanden ist, wie es verschiedene Türme gibt, d.h. "phase" gibt es als Variable für jeden Turm einzeln und kann daher einzeln hochzählen und auf null resettet werden. Als static Variable behält die Variable ihren zugewiesenen Wert zwischen den Funktionsaufrufen.

BTW Systemauslastung in meinem Sketch:
Da alle Einzelphasen Vielfache von 100 Millisekunden sind, brauche ich in meinem Sketch natürlich nicht tatsächlich jede Millisekunde (und schon gar nicht mehrmals pro Millisekunde) auf eine Änderung des Turmstatus zu prüfen, sondern nur zehnmal pro Sekunde.

Falls Systemleistung für zusätzliche Aufgaben benötigt wird, lassen sich mit einer kleinen Änderung der loop-Funktion in meinem Sketch zehnmal pro Sekunde Zeitscheiben von 99 Millisekunden Dauer freischaufeln, in denen andere "langlaufende" Aufgaben erledigt werden können, sogar kurze Delays wären dort möglich, etwa zum Entprellen von Tasten oder um den seriellen Empfangspuffer mit empfangenen Daten zu füllen, oder um langsame Sensoren auszulesen.

Reduzierung der Systemauslastung für meinen Sketch, liefert 10 mal pro Sekunde frei nutzbare Zeitscheiben von 99 ms:

void loop() {
  static long nextLoopTime;
  if (millis()>=nextLoopTime) 
  {
    nextLoopTime=((millis()+100)/100)*100;
    TurmStatus(Norderney_timing);
    TurmStatus(Pilsum_timing);
    TurmStatus(Campen_timing);
    TurmStatus(Schillig_timing);
    TurmStatus(Voslapp_timing);
    TurmStatus(Tossens_timing);
    // "lange" bis zu 99ms laufenden Code hier rein 
  } 
  // "kurz" bis zu 1 ms laufenden Code hier rein
}

Vielleicht soll die Landkarte ja sogar noch zu einer "interaktiven Landkarte" ausgebaut werden, etwa mit einem Bedienpult mit sechs Knöpfen und einem LCD-Display. Und wenn man am Bedienpult einen Taster drückt, kann man den Tastendruck mit einem kurzen Delay entprellen und das LCD-Display zeigt Zusatzinfos wie die Höhe und das Baujahr des Leuchtturms an. Nur mal so als Idee.

Das Speichern der Phase für jeden Turm einzeln funktioniert, weil die Funktion als Template-Funktion tatsächlich so oft vorhanden ist, wie es verschiedene Türme gibt

Yes!!!

Reduzierung der Systemauslastung für meinen Sketch, liefert 10 mal pro Sekunde frei nutzbare Zeitscheiben von 99 ms:

Der Haken ist nur: Du brauchst kooperativen Code der nie länger als 99ms unterwegs ist. Meine Lösung braucht nur, daß der Code keine Interrupts sperrt.

Hallo Leute,

mit großem Interesse habe ich die Lösungen um die Leuchtfeuerkarte gelesen. Sketches funktionieren sehr gut. Ich denke hier wird auch keiner wegen einigen Millisekunden Abweichung schauen wollen.
Das Problem mit einer Leuchtfeuerkarte habe ich nun schon fast 20 Jahre vor mir her geschoben, leider hatte ich bisher noch keinen richtigen "Drive" gehabt es umzusetzen.
Anfang des Jahres einen ARDUINO Uno besorgt und etws rumgespielt. Da kam die Frage von TWOLL065 gerade recht. Allerdings will ich das Ding etwas aufbohren - (50 - 100 Leuchtfeuer).
Deswegen habe ich etwas mit Schieberegistern gespielt. Mehrere 595 zusammengeschaltet und verschiedene Sketche probiert. Man kann zwar Counter rauf und runter zählen, aber leider habe ich noch nicht den richtigen Ansatz um den Leuchtturm Sketch mit einem Shift Out Sketch zu verbinden. D.h. die nun als LED Out deklarierten Bits zu einem, bzw. mehreren Byte(s) zusammen zu packen und dann an das Schieberegister zu senden.
Gibt es hierzu eine einfache Lösung?

Grüße MKy

MKy_KB:
leider habe ich noch nicht den richtigen Ansatz um den Leuchtturm Sketch mit einem Shift Out Sketch zu verbinden. D.h. die nun als LED Out deklarierten Bits zu einem, bzw. mehreren Byte(s) zusammen zu packen und dann an das Schieberegister zu senden.
Gibt es hierzu eine einfache Lösung?

Einfach ist ja immer relativ.

Und da ich mit Schieberegistern noch nichts praktisch gemacht habe, habe ich eher eine theoretische Vorstellung darüber. Davon und von dem von mir auf der ersten Seite von mir geposteten Sketch ausgehend, würde ich es wie folgt machen:

  1. Da die Leuchttürme bei der Lösung mit Schieberegistern nicht mehr bestimmten Arduino-Pins zugeordnet sind, wird das erste Feld der Turmdeklaration aus meinem Sketch nicht benötigt. Benötigt wird allerdings eine Variable, um sich den Zustand des Turms zu merken, also ob seine LED/Lampe gerade an oder aus ist. Dies wird benötigt, weil der Zustand aller Türme am Ende in das/die Schieberegister geschoben werden muß und das geht erst, wenn alle Turmzustände eines 100ms Intervalls zuvor ermittelt wurden und bekannt sind. Also verändere ich die Datenstruktur und recycele das erste Feld, statt des LED-Pins wird sich dort der LED-Status des Turms gemerkt (0 aus, 1 an), ich setze anfänglich mal 0 ein.
// LEDState - 0 bei LED aus, 1 bei LED an
// 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[]={ 0, 12000,  6,  200, 2800,  200, 2800, 200 , 5800};
int Pilsum_timing[]   ={ 0, 20500,  4, 3000, 3000, 3000,11500};
int Campen_timing[]   ={ 0, 15000,  8,  700, 2300,  700, 2300,  700, 2300,  700, 5300};
int Schillig_timing[] ={ 0, 12000,  2, 6000, 6000};
int Voslapp_timing[]  ={ 0,  9000,  2, 6000, 3000};
int Tossens_timing[]  ={ 0,  7000,  2, 6000, 1000};

Zweite Änderung: Den Türmen sind keine Arduino-Pins mehr zugewiesen daher diesen Codeblock löschen:

  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);

Anstelle des Codes zum Initialisieren der Arduino-Pins als OUTPUT muss stattdessen der Code zum Initialisieren der Schieberegister eingefügt werden.

Die dritte Änderung ist, dass nach dem Berechnen des Turmstatus nicht direkt die LED geschaltet werden kann, sondern dass sich stattdessen der ermittelte Wert gemerkt werden muss. Also
statt:

digitalWrite(Turmdata[0], blinkStatusOn);

setze:

// im ersten Feld der Turmdaten merken, ob die LED gerade an oder aus sein soll
if (blinkStatusOn) Turmdata[0]=1; else Turmdata[0]=0;

Die vierte und letzte Änderung ist dann, dass wo in der loop von mir der Platzhalter

// "lange" bis zu 99ms laufenden Code hier rein

eingefügt wurde (aus dem nachgebesserten loop-Code von Seite 3 dieses Threads), dann der Code hinkommt, der die nun bekannten Turmzustände (Norderney_timing[0], Pilsum_timing[0], Campen_timing[0], etc.) an das/die Schieberegister rausschiebt und aktiviert.

Da sehe ich eigentlich kein größeres softwaretechnisches Änderungsproblem bei der von mir geposteten Programmlogik.

Hallo jurs,

danke für die schnelle Antwort. Gedacht war z.B. folgenden Sketch aus dem ShiftOut Tutorial zu nehmen.
Diesen habe ich mal auf 6 Reihen erweitert und verschiedene 0b, 0x Werte oder Variablen eingetragen. Teilweise negiert weil ich die LED's an den SH Register von 5V zum SH Ausgang gelegt habe.

Diese Bytes müßten dann wohl halt mit den Blinkergebnissen gefüllt werden. Nur halt die Frage wie? Mit Byte read oder einem Array?

//**************************************************************//
//  Name    : shiftOutCode, Dual Binary Counters                 //
//  Author  : Carlyn Maw, Tom Igoe                               //
//  Date    : 25 Oct, 2006                                       //
//  Version : 1.0                                                //
//  Notes   : Code for using a 74HC595 Shift Register            //
//          : to count from 0 to 255                             //
//**************************************************************//



//Pin connected to ST_CP of 74HC595 Pin 12
int latchPin = 8;
//Pin connected to SH_CP of 74HC595 Pin 11
int clockPin = 12;
////Pin connected to DS of 74HC595 Pin 14
int dataPin = 11;



void setup() {
  //Start Serial for debuging purposes	
  Serial.begin(9600);
  //set pins to output because they are addressed in the main loop
  pinMode(latchPin, OUTPUT);

}

void loop() { 
  
  //count up routine
  for (int j = 0; j < 256; j++) {
    //ground latchPin and hold low for as long as you are transmitting
    digitalWrite(latchPin, 0);

    shiftOut(dataPin, clockPin, LSBFIRST, 0x55);           //6. Reihe links nach rechts
    
    shiftOut(dataPin, clockPin, LSBFIRST, ~0b00110011);    //5. Reihe links nach rechts
    
    shiftOut(dataPin, clockPin, LSBFIRST, ~0b00001111);    //4. Reihe links nach rechts
    
    shiftOut(dataPin, clockPin, LSBFIRST, ~0xFF);          //3. Reihe links nach rechts
    
    shiftOut(dataPin, clockPin, ~j);                       //2. Reihe 
    
    shiftOut(dataPin, clockPin, LSBFIRST, ~0b00110111);    //1. Reihe links nach rechts
    //return the latch pin high to signal chip that it 
    //no longer needs to listen for information
    digitalWrite(latchPin, 1);
    delay(100);
  }
}


void shiftOut(int myDataPin, int myClockPin, byte myDataOut) {
  // This shifts 8 bits out MSB first, 
  //on the rising edge of the clock,
  //clock idles low

//internal function setup
  int i=0;
  int pinState;
  pinMode(myClockPin, OUTPUT);
  pinMode(myDataPin, OUTPUT);

//clear everything out just in case to
//prepare shift register for bit shifting
  digitalWrite(myDataPin, 0);
  digitalWrite(myClockPin, 0);

  //for each bit in the byte myDataOut?
  //NOTICE THAT WE ARE COUNTING DOWN in our for loop
  //This means that %00000001 or "1" will go through such
  //that it will be pin Q0 that lights. 
  for (i=7; i>=0; i--)  {
    digitalWrite(myClockPin, 0);

    //if the value passed to myDataOut and a bitmask result 
    // true then... so if we are at i=6 and our value is
    // %11010100 it would the code compares it to %01000000 
    // and proceeds to set pinState to 1.
    if ( myDataOut & (1<<i) ) {
      pinState= 1;
    }
    else {	
      pinState= 0;
    }

    //Sets the pin to HIGH or LOW depending on pinState
    digitalWrite(myDataPin, pinState);
    //register shifts bits on upstroke of clock pin  
    digitalWrite(myClockPin, 1);
    //zero the data pin after shift to prevent bleed through
    digitalWrite(myDataPin, 0);
  }

  //stop shifting
  digitalWrite(myClockPin, 0);
}