Frage zur Programmstruktur meiner Weihnachtssteuerung

Hi,

mein Ziel ist es eine Weihnachtssteuerung zu realisieren, d.h. es gibt in meinem Garten verschiedene Orte die unabhängig voneinander ein- und ausschaltbar sein sollen über ein Webinterface. (Siehe Bild)
Nun habe ich eine Frage zu meinem Arduino-Skript. (Siehe unten)

Wenn ich das Programm auf den Arduino hochgeladen habe, funktioniert es soweit.
D.h. wenn ich auf den Button „Dach Ein“ drücke auf der Website, dann wird die if-Funktion „Dach Ein“ ausgeführt.
Wenn ich jedoch die if-Funktion mit der Switch/case-Anweisung ausführen will,
funktioniert das soweit auch, nur ich kann über die if-Funktion „Dach Aus“ nicht mehr die Funktion beenden…

Die Übergabewerte der „Dach Aus“ if-Funktion stimmen das weiß ich. Über den Seriellen Monitor habe ich nun rausbekommen, dass meine Wertübergabe an die Variablen „kAnzahl“, „ort“ und „modus“ nicht mehr übergeben werden, nachdem die if-Funktion mit der Switch/case-Anweisung ausgeführt wurde… kann mir da vielleicht einer weiterhelfen woran das liegen kann?

Ich komme irgendwie nicht weiter… :frowning:

Ich habe auch versucht die Wertübergabe in einer extra Funktion auszulagern ("wertAbfrage()“ ganz unten im Arduino-Skript) und diese dann als Interrupt aufzurufen alle paar Millisekunden,
jedoch gibt es da Probleme bei der Instanziierung der Objekte „client“ und „finder“ glaube ich.
In der objektorientierten Programmierung fehlen mir für dieses Problem einfach die Grundlagen und Kenntnisse.

Ich hoffe einer von euch kennt sich da besser aus und kann mir helfen.

Ablaufsteuerung_Winter_2015.ino (10.1 KB)

Seh kein Bild

Oh... Habe es jetzt nochmal als Anhang hochgeladen.

Schade, das du den Sketch nicht direkt gepostet (in Code-Tags) dann könnte ich den auch auf dem Tablet lesen. So habe ich leider keine Idee.

#if ARDUINO > 18
#include <SPI.h> // Für Arduino Version größer als 0018
#endif
#include <Ethernet.h>
#include <TextFinder.h>   // Bibliothek um Webinterface-Übergabewerte zu finden u. filtern
#include <SD.h>
#include "TimerOne.h"     // Interrupt-Bibliothek


//---------------------------------------------------------------------------------------//
//+++++++++++++++++ Variablen Deklarationen für "Verzögerung ohne Delay"+++++++++++++++++// 
//---------------------------------------------------------------------------------------//

long int previousMillis = 0;
int interval = 1000;
int counter = 0;  // Erhöht ihren Wert, wenn nächste Phase erreicht wird z.B. in Modus: L --> R und wird nach letzter Phase wieder auf 0 gesetzt um Modus wieder von vorn beginnen zu lassen

//---------------------------------------------------------------------------------------//
//++++++++++++++ Variablen Deklarationen für "Webinterface-Übergabewerte"++++++++++++++++// 
//---------------------------------------------------------------------------------------//

char kAnzahl;   // Kettenanzahl
int ort;        // Örtlichkeit wo der entsprechende Modus ausgeführt werden soll
int modus;      

//---------------------------------------------------------------------------------------//
//++++++++++++++++++++ Array Deklarationen für "Ort/Relais-Ausgänge"+++++++++++++++++++++// 
//---------------------------------------------------------------------------------------//

int baeume[] =      {18, 19, 20, 21, 22};
int dach[] =        {23, 24, 25, 26, 27};
int tannenbaum[] =  {28, 29, 30, 31, 32, 33};
int tujabaum[] =    {34, 35, 36, 37, 38, 39};
int tujahecke[] =   {40, 41, 42, 43, 44, 45};
int baum[] =        {46, 47, 48};
int hecke[] =       {49, 50, 51, 52, 53};

//---------------------------------------------------------------------------------------//
//+++++++++++++++++++++++++ Variablen Deklarationen für "Interrupt"++++++++++++++++++++++// 
//---------------------------------------------------------------------------------------//

byte alle_x_sekunden = 3;   //Zeit in Sekunden in der der Interrupt ausgeführt werden soll

//---------------------------------------------------------------------------------------//
//++++++++++++++++++++++ Variablen Deklarationen für "Ethernet-Shield"+++++++++++++++++++// 
//---------------------------------------------------------------------------------------//

byte mac[] = { 0x90, 0xA2, 0xDA, 0x0E, 0x09, 0x63 }; // MAC-Adresse des Ethernet-Shield
byte ip[]  = { 192, 168, 178, 222 };   // IP zum aufrufen des Webservers
byte sdPin = 4; // Pin der SD-Karte
-


EthernetServer server(80); // Server port
File webFile; 


void setup()
{
  Timer1.initialize(alle_x_sekunden*1000000);
  Timer1.attachInterrupt(check);
  
  Serial.begin(9600);


// Initialiesierung der Ausgänge //

// +++++++++++++DACH++++++++++++ //

  for (int i = 0; i < 5; i++)
  {
    pinMode(dach[i], OUTPUT);
    Serial.println("Dachvariable: " + String(i) + " von 5 initialisiert");
  }

  Serial.println();

//---------------------------------------------------------------------------------------//
//++++++++++++++++++++++++++++++++ Ethernet-Verbindung START+++++++++++++++++++++++++++++// 
//---------------------------------------------------------------------------------------//  

  Ethernet.begin(mac, ip); // Client starten
  server.begin();          // Server starten

  Serial.println("ARDUINO - STEUERUNG");
  Serial.println("Initialisiere SD-Karte...");
  if (!SD.begin(sdPin))
  {
    Serial.println(" - Initialisierung der SD-Karte fehlgeschlagen!");
    return;
  }
  Serial.println(" - SD-Karte erfolgreich initialisiert.");
  if (!SD.exists("index.htm"))
  {
    Serial.println(" - Datei (index.htm) wurde nicht gefunden!");
    return;
  }
  Serial.println(" - Datei (index.htm) wurde gefunden.");
  Serial.println();
  Serial.println("Verbraucher schalten");



  setzeAlles0();    // Setze alle Ausgänge auf Status "0"  
}   // Klammer: void setup()
void loop()
{
  EthernetClient client = server.available(); // Auf Anfrage warten
  
  if (client)
  {
    TextFinder finder(client);
    if (finder.find("GET"))
    {
      while (finder.findUntil("gruber", "\n\r"))
      {
        kAnzahl = client.read();
        ort = finder.getValue();
        modus = finder.getValue();
      }
    }       

  // ++++++++++++++++++++ DACH: MODUS AUS ++++++++++++++++++++++ //     
  if( kAnzahl == 'K' & ort == 1 & modus == 0){
    Serial.println("Modus: Dach Aus");
    Serial.println("Ort: " + String(ort));
    Serial.println("Kettenanzahl: " + String(kAnzahl));
    Serial.println("Modus: " + String(modus));
    Serial.println();
    
    for(int i=0; i <5; i++){
      digitalWrite(dach[i], HIGH);
    }
  }

// ++++++++++++++++++++ MODUS EIN ++++++++++++++++++++++ //
  if ( kAnzahl == 'K' & ort == 1 & modus == 1){
    Serial.println("Modus: Dach EIN");
    Serial.println("Ort: " + String(ort));
    Serial.println("Kettenanzahl: " + String(kAnzahl));
    Serial.println("Modus: " + String(modus));
    Serial.println();
    
    for(int i=0; i <5; i++){
      digitalWrite(dach[i], LOW);
    }
  }

// +++++++++++++++++++++ MODUS 2 (L --> R) +++++++++++++++++++ //
  while(/*kAnzahl=='K' && */ort == 1 && modus == 2){ 
    Serial.println("Modus: Dach L --> R Blinken");
    Serial.println("Ort: " + String(ort));
    Serial.println("Kettenanzahl: " + String(kAnzahl));
    Serial.println("Modus: " + String(modus));
    Serial.println();  

    if (millis() - previousMillis > interval){    
      previousMillis = millis();   // aktuelle Zeit abspeichern
      Serial.println ("hinter millis");
      Serial.println ("Counter: " + String(counter));
      setzeAlles0();

      switch(counter){
        case 0:     
          Serial.println ("Phase: " + String(counter));
          digitalWrite(dach[0], !HIGH);
          digitalWrite(dach[1], !LOW);
          digitalWrite(dach[2], !LOW);
          digitalWrite(dach[3], !LOW);
          digitalWrite(dach[4], !LOW);         
          counter++;   
          break;
        
        case 1:
          Serial.println ("Phase: " + String(counter));
          digitalWrite(dach[0], !LOW);
          digitalWrite(dach[1], !HIGH);
          digitalWrite(dach[2], !LOW);
          digitalWrite(dach[3], !LOW);
          digitalWrite(dach[4], !LOW);
          counter++;
          break;

        case 2:        
          Serial.println ("Phase: " + String(counter));
          digitalWrite(dach[0], !LOW);
          digitalWrite(dach[1], !LOW);
          digitalWrite(dach[2], !HIGH);
          digitalWrite(dach[3], !LOW);
          digitalWrite(dach[4], !LOW);
          counter++;
          break;

        case 3:
          Serial.println ("Phase: " + String(counter));
          digitalWrite(dach[0], !LOW);
          digitalWrite(dach[1], !LOW);
          digitalWrite(dach[2], !LOW);
          digitalWrite(dach[3], !HIGH);
          digitalWrite(dach[4], !LOW);
          counter++;
          break;

        case 4:
          Serial.println ("Phase: " + String(counter));
          digitalWrite(dach[0], !LOW);
          digitalWrite(dach[1], !LOW);
          digitalWrite(dach[2], !LOW);
          digitalWrite(dach[3], !LOW);
          digitalWrite(dach[4], !HIGH);
          counter = 0;
          break;
      } // Klammer: switch(counter)         
      
    } // Klammer: if (millis() - previousMillis > interval

  }// Klammer: while(/*kAnzahl=='K' && */ort == 1 && modus == 2)
  
  boolean current_line_is_blank = true;       // eine HTTP-Anfrage endet mit einer Leerzeile und einer neuen Zeile

  while (client.connected())
  {
    if (client.available())                   // Wenn Daten vom Server empfangen werden
    {
      char c = client.read();                 // empfangene Zeichen einlesen
      if (c == '\n' && current_line_is_blank) // wenn neue Zeile und Leerzeile empfangen
      { 
// Standard HTTP Header senden
        client.println("HTTP/1.1 200 OK");
        client.println("Content-Type: text/html");
        client.println("Connection: close");
        client.println();

// Website von SD-Karte laden
        webFile = SD.open("index.htm");  // Website laden
        if (webFile)
        {
          while (webFile.available())
          {
            client.write(webFile.read()); // Website an Client schicken
          }
        webFile.close();
        }
        break;
      }
      if (c == '\n')
      {
        current_line_is_blank = true;
      }
      else if (c != '\r')
      {
        current_line_is_blank = false;
      }
    } //Klammer: if (client.available()) 
  } // Klammer: while (client.connected())
  delay(1);
  client.stop();
  } // Klammer: if (client)
} // Klammer: loop-Schleife



//---------------------------------------------------------------------------------------//
//+++++++++++++++++++++++++++++++++++++ Eigene Funktionen++++++++++++++++++++++++++++++++// 
//---------------------------------------------------------------------------------------//


// +++++++++++++++++ Zeigt die Webinterface-Übergabewerte im Seriellen Monitor an ++++++++++++++++++++++ //
void check(){
 Serial.println("Vor loop Schleife-------------");
 Serial.println("Kettenanzahl: " + String(kAnzahl));
        Serial.println("Ort: " + String(ort));
        Serial.println("MOdus: " + String(modus));
        Serial.println("");
}

// +++++++++++++++++ Fragt die Webinterface-Übergabewerte ab ++++++++++++++++++++++ //
/* Funkioniert in der Ausführung als Interrupt nicht.
 * Problem: "client" und "finder" nicht deklariert
 * Lösung: Evtl. 2. Objekt jeweils instanziieren und mit Hilfe dessen Übergabewerte in die Variablen schreiben.
 * ------------------------> Funktioniert das? <-------------------------
 */
void wertAbfrage(){
  EthernetClient client = server.available(); // Auf Anfrage warten
  TextFinder finder(client);
  if (finder.find("GET"))
    {
      while (finder.findUntil("gruber", "\n\r"))
      {
        char kAnzahl = client.read();
        int ort = finder.getValue();
        int modus = finder.getValue();
      }
    }
}

// +++++++++++++++++ Setzt alle Ort/Relais-Ausgänge auf "0" ++++++++++++++++++++++ //
void setzeAlles0()
{
  for (int i = 18; i < 54; i++)
  {
    digitalWrite(i, HIGH);
    delay(1);
  }
}

Keiner ne Idee? :confused:

Decke:
Keiner ne Idee? :confused:

Bist Du sicher, dass Du keine while-Endlosschleifen programmiert hast, in denen sich Dein Programm festfrisst?

Beispielsweise kann ich in dieser while-Schleife:

while(/*kAnzahl=='K' && */ort == 1 && modus == 2){ 
...
}

nicht erkennen, dass sich 'ort' oder 'modus' innerhalb der while-Schleife ändern.
D.h. wenn die Bedingung (ort == 1 && modus == 2) beim Eintritt in die whilfe-Schleife erfüllt sind, kann ich keinen Punkt erkennen, an dem sich etwas daran ändert, dass diese Bedingung erfüllt ist.

Das kann aber auch an der Unübersichtlichkeit des Programms liegen. Wenn ich ein Programm schreibe, dann achte ich eigentlich darauf, dass jede einzelne Funktion komplett und ohne zu scrollen in einem normal großen Editor-Fenster dargestellt werden kann, also z.B. im Normalfall keine Funktion länger als 25 Zeilen wird.

Zur strukturierten Programmierung gehört bei mir auch, dass ich nichts verwende, was für das Programm nicht gebraucht wird, also z.B. ohne Not keine dynamischen "String"-Objekte in einem C/C++-Programm verwenden, sondern stattdessen nur "nullterminierte Strings", die zu den leistungsfähigen Stringfunktionen der AVR LIBC Standardlibrary kompatibel sind.

Nicht zuletzt würde ich einen eher defensiven Programmierstil pflegen, also z.B. keine "Timer-Interrupts" in einem Programm einbauen, das keine Timer-Interrupts benötigt, und dann in den Interrupt-Handler noch "Serial.print()" Funktionsaufrufe einbauen, womit jeder Arduino Sketch wenigstens potentiell zum Absturz gebracht werden kann.

Übrigens: Am kommenden Wochenende ist der erste Advent 2015. Üblicherweise hat man seine Weihnachtsbeleuchtung ab dem ersten Advent einsatzbereit, und Du hast ein paar Tage vorher noch nicht mal einen funktionsfähigen Prototypen. Bist Du Dir sicher, dass das nicht die Weihnachtsbeleuchtung 2016 werden soll?

Danke für deine Antwort.

Ich bin froh, wenn mir gesagt wird worauf ich noch so achten kann in meinen Sketchs bzw. ich auf "no goes" hingewiesen werde, Dankeschön! :blush:

Das mit der while-Schleife hatte ich auch mal so probiert wie du es meintest jurs (in die while-Scheife die Wertabfragen)

while(/*kAnzahl=='K' && */ort == 1 && modus == 2){ 

TextFinder finder(client);
    if (finder.find("GET"))
    {
      while (finder.findUntil("gruber", "\n\r"))
      {
        kAnzahl = client.read();
        ort = finder.getValue();
        modus = finder.getValue();
      }
    }       
...
}

jedoch ist es mir dann nicht mehr möglich Werte zu empfangen...
Das kann an der "TextFinder"-Bibliothek liegen, jedoch ist die doch meines Wissens notwendig um Zeichen von einer Webseite zu empfangen.

Ist es überhaupt möglich mit der Standard AVR String-Bibliothek die gleiche Funktion zu erreichen? Nämlich die Webwerte zu empfangen und in Variablen zu speichern?

Wenn ja hätte einer vielleicht ein kleines Beispiel für mich?

Grüße

Decke:
Ist es überhaupt möglich mit der Standard AVR String-Bibliothek die gleiche Funktion zu erreichen? Nämlich die Webwerte zu empfangen und in Variablen zu speichern?

Natürlich. Alles andere basiert doch auch nur darauf. Egal ob Arduino String Klasse oder TextFinder.

Eine Option ist alles zeilenweise abzuspeichern und dann erst zu suchen oder zu vergleichen. Da kann man dann z.B. mit strstr() nach Teil-Strings suchen. Dann gibt es eine Reihe von Konvertierungsfunktionen wie atoi() oder atol() für Integer oder atof() für Floats. Oder strtoul() wenn man Hex-Zahlen hat.

Man kann aber auch wie bei find() "on the fly" vergleichen ohne den Text auch zu speichern. Wie das geht sieht man hier ab Zeile 858:

Eigentlich extrem einfach. Und abgesehen von der PROGMEM Sache wird TextFinder auch nichts anderes machen. Also einen Index mitzählen wie viele Zeichen übereinstimmen und abbrechen wenn man beim Terminator des Vergleichsstrings angelangt ist

Decke:
Ist es überhaupt möglich mit der Standard AVR String-Bibliothek die gleiche Funktion zu erreichen? Nämlich die Webwerte zu empfangen und in Variablen zu speichern?

Ja, natürlich.
Mit weniger Code, schnellerer Verarbeitung, ohne Drittanbieter-Libraries und ohne String-Objekte.

Im Endeffekt brauchst Du ja nur die "GET" Zeile aus dem HTTP-Request als (nullterminierten) String.
Und den String kannst Du dann auf Variablennamen und Werte parsen.

Decke:
Wenn ja hätte einer vielleicht ein kleines Beispiel für mich?

Für Deinen Anwendungsfall mit Ein-/Aus-/Blinkschaltungen habe ich jetzt nichts direkt da, aber nur um mal ein wenig strukturierte Programmierung zu zeigen habe ich hier mal was aus meiner Festplatte rausgezogen (hoffe, der Schnipsel funktioniert):

#include <SPI.h>
#include <Ethernet.h>
char linebuf[81]; // buffer for line length 80 and terminating '\0' character

// the next two lines must contain unique MAC and IP address in your LAN network
byte mac[] = { 0x00, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
byte ip[] = { 192, 168, 2, 250 };

EthernetServer server(80);

char* readGetRequest(EthernetClient &client)
{
  int charcount=0;
  memset(linebuf,0,sizeof(linebuf));
  // http GET request is first line of request and 
  // ends with a '\n' newline character
  while (client.connected()) 
  {
    if (client.available()) 
    {
      char c = client.read();
      if (charcount<sizeof(linebuf)-1)
      {
        linebuf[charcount]=c;
        charcount++;
      }
      if (c == '\n') break; // end of header found, break while-loop
    }
  }
  return linebuf;
} // void readClientRequest(EthernetClient &client)

void sendEchoResponse(EthernetClient &client, char* text)
{
  client.println(F("HTTP/1.1 200 OK"));
  client.println(F("Content-Type: text/plain"));
  client.println();
  client.println(text);
}

void setup(){
  Serial.begin(115200);
  Ethernet.begin(mac, ip);
  Serial.print("Server is at: ");Serial.println(Ethernet.localIP());
}

void loop()
{
  EthernetClient client = server.available();
  if (client) 
  {
    char* request=readGetRequest(client);
    Serial.print("Got HTTP Request: ");  
    Serial.println(request);
    sendEchoResponse(client, request);
    client.flush();
    client.stop();
  }
}

Dieser Code macht jetzt nur folgendes:
Der GET-Request wird in einen allgemeinen Zeilenpuffer "linebuf" eingelesen, und danach als Echo-Antwort in einer einzeiligen Textdatei an den Browser zurück gesendet.

Natürlich könnte man diese Zeile jetzt parsen (typische Standardfunktion dafür wäre "strtok()" und keinesfalls "finder.find()"), Werte auslesen und ggf. in numerische Variablen umwandeln, und dann weiter verarbeiten.

Das ist jetzt natürlich nur mal ein ganz kleines Beispiel (ohne Parsen von Variablen), aber ein "String" Objekt kommt darin schon mal nicht vor, sondern die GET-Anfrage landet in einem nullterminierten String (char Array) und könnte mit strtok() nun weiter zerlegt und dann verarbeitet werden.

Edit/Nachtrag:
Deine Webseite scheint auch überhaupt nicht interaktiv zu sein und ein Feedback zu senden, was aktuell gerade geschaltet ist, sondern es wird eine immer gleichbleibende statische HTML-Seite gesendet, auf der man nur sehen kann, was man schalten kann:

webFile = SD.open("index.htm");  // Website laden
        if (webFile)
        {
          while (webFile.available())
          {
            client.write(webFile.read()); // Website an Client schicken
          }
        webFile.close();

Poste doch mal den Inhalt der "index.htm" Datei, dann kann ich ggf. mal schauen, ob ich Dir dafür einen einfachen Parser schreiben kann, der Dir gepostete Variablen verändert, so dass Du anhand der Variablen dann Deine Lichter steuern kannst.

Übrigens habe ich gesehen, dass da teilweise "blinken" gesteuert werden soll. In dem Fall will ich doch wohl hoffen, dass Du da SSR Halbleiterrelais zum Schalten verwendest und keine mechanischen Relais? Ansonsten schau mal in das Relais-Datenblatt! Viele einfache mechanische Relais sind gerade mal auf 100000 Schaltvorgänge unter Nennlast ausgelegt, und wenn Du ein mechanisches Relais beispielsweise ein dutzendmal pro Minute mit Nennlast schaltest, dann kannst Du Dir mal ausrechnen, nach wie vielen Minuten Betriebsdauer Du das nominelle Lebensende des mechanischen Relais erreicht haben wirst!

Ok danke, das hört sich gut an mit dem "char Array". :slight_smile:

Ich hätte da nur eine Frage zu dem Beispielskript und zwar zu folgender Zeile:

linebuf[charcount]=c;

Wieso wir dort immer jeder Index des linebuf-Arrays der Wert "c" zugewiesen? Oder sehe ich da was falsch?

@jurs:

Die index.htm Datei habe ich angehängt und ja.... sie ist statisch, ich weiß das ist nicht das gelbe vom Ei aber das erschien mir für den Anfang am einfachsten d.h. ich bekomme vom Client einen Wert und werte diesen aus versende aber keine Rückmeldung.

Deinen folgenden Satz habe ich nicht ganz verstanden:

... ob ich Dir dafür einen einfachen Parser schreiben kann, der Dir gepostete Variablen verändert, so dass Du anhand der Variablen dann Deine Lichter steuern kannst.

@ all:

Danke an die biseherigen Antworten von euch, geht echt schnell hier :slight_smile:

Nur der aktuelle Index. Der Index wird dann in der nächsten Zeile inkrementiert und das nächste Zeichen wird eins weiter geschrieben

Decke:
Ich hätte da nur eine Frage zu dem Beispielskript und zwar zu folgender Zeile:

linebuf[charcount]=c;

Wieso wir dort immer jeder Index des linebuf-Arrays der Wert "c" zugewiesen? Oder sehe ich da was falsch?

Da siehst Du irgendwas komplett falsch. Die Variable mit dem Namen c ist mit dem Typ "char" deklariert. Und zum Auslesen wird per Funktionsaufruf client.read() immer ein Zeichen in diese Variable eingelesen.

Solange der String kürzer ist als seine definierte maximale Länge, wird dieser char dann an der Stelle an den String drangehängt, der durch den Stand der Zählvariablen charcount vorgegeben wird. Nachdem der Buchstabe drangehängt wurde, wird die Zählvariable um eins erhöht (charcount++).

Hier wird immer ein Zeichen ausgelesen, an die Variable namens c zugewiesen, und dann an den String drangehängt:

char c = client.read();
if (charcount<sizeof(linebuf)-1)
{
  linebuf[charcount]=c;
  charcount++;
}

Decke:
@jurs:

Die index.htm Datei habe ich angehängt

Davon kann ich nichts sehen.

Am besten fügst Du den HTML-Code hier einfach zwischen Code-Tags ein, so wie Du es mit C-Code auch machen würdest.

Hier, da ist wohl irgendwas schief gelaufen...

<!DOCTYPE html>
<html>
<head>
<style type="text/css">

a:link {color:white; text-decoration:none}
a:visited {color:white; text-decoration:none}


 html {
 background: #424242;
 font-family: Arial;
 font-size:30px;
 }

 .kopf {
 text-align: center;
 color: white;
 font-size:40px;
 }
 
 .Ort {
 background: #2E2E2E;
 color: white;
 width: auto;
 heigth: auto;
 position: relative;
 float: none; 
 border-radius: 5px;
 padding-bottom: 500px;
 }
 
 .ButtonEin {
 width: 380px;
 height: auto;
 color: #58ACFA;
 border-radius: 5px;
 background-color: #04B404;
 margin: 10px;
 float: left;
 margin-right: 10px;
 }
 
 .ButtonEin a{
 margin-left: 5px;
 } 
 
 
 .ButtonAus {
 width: 380px;
 height: auto;
 color: #58ACFA;
 border-radius: 5px;
 background-color: #DF0101;
 float: right;
 margin: 10px;
 margin-left: 10px;
 }
 
 .ButtonAus a{
 margin-left: 5px;
 }
 
 .ButtonModus2 {
 width: 380px;
 height: auto;
 color: #58ACFA;
 border-radius: 5px;
 background-color: #2ECCFA;
 float: left;
 margin:10px;
 margin-left: 10px;
 
 }
 
 .ButtonModus2 a{
 margin-left: 5px;
 }
 
 .ButtonModus3 {
 width: 380px;
 height: auto;
 color: #58ACFA;
 border-radius: 5px;
 background-color: #2ECCFA;
 float: right;
 margin:10px;
 margin-left: 10px;

 
 }
 
 .ButtonModus3 a{
 margin-left: 5px;
 }
 
 .ButtonOrt1BlinkenAn a{
 margin-left: 5px;
 }
 
 .ButtonOrt1BlinkenAus {
 width: 300px;
 height: auto;
 color: #58ACFA;
 border-radius: 5px;
 background-color: #2ECCFA;
 float: right-side;
 margin: 10px;
 }
 
 .ButtonOrt1BlinkenAus a{
 margin-left: 5px;
 }
</style>

  <title>Lichtersteuerung</title>

</head>
<body>
 <div class="kopf">
 Lichtersteuerung
 </div>
 

 <div class="Ort">
 <h2>Dach</h2>
 <div class="ButtonEin">
 <a href="/?gruberK:1:1" target="ifr">Dach Ein</a>
 </div>
 
 <div class="ButtonAus"> 
 <a href="/?gruberK:1:0" target="ifr">Dach Aus</a>
 </div>
 
 <div class="ButtonModus2"> 
 <a href="/?gruberK:1:2" target="ifr">L --> R Blinken</a>
 </div>
 
 <div class="ButtonModus3"> 
 <a href="/?gruberK:1:3" target="ifr">R --> L Blinken</a>
 </div>
 
 <div class="ButtonModus2"> 
 <a href="/?gruberK:1:4" target="ifr">L --> R Blieben An</a>
 </div>
 
 <div class="ButtonModus3"> 
 <a href="/?gruberK:1:5" target="ifr">R --> L Bleiben An</a>
 </div>
 
 <div class="ButtonModus2"> 
 <a href="/?gruberK:1:6" target="ifr">Aussen in Mitte Blinken</a>
 </div>
 
 <div class="ButtonModus3"> 
 <a href="/?gruberK:1:7" target="ifr">Mitte --> Aussen Blinken</a>
 </div>
 
 
 <div class="ButtonModus2"> 
 <a href="/?gruberK:1:8" target="ifr">Aussen in Mitte Bleiben An</a>
 </div>
 
 <div class="ButtonModus3"> 
 <a href="/?gruberK:1:9" target="ifr">Mitte --> Aussen Bleiben An</a>
 </div>
 
 
 <div class="ButtonModus2"> 
 <a href="/?gruberK:1:10" target="ifr">Ungerade Blinken</a>
 </div>
 
 <div class="ButtonModus3"> 
 <a href="/?gruberK:1:11" target="ifr">Gerade Blinken</a>
 </div>
 
 
 <div class="ButtonModus2"> 
 <a href="/?gruberK:1:12" target="ifr">Ungerade Bleiben An</a>
 </div>
 
 <div class="ButtonModus3"> 
 <a href="/?gruberK:1:13" target="ifr">Gerade Bleiben An</a>
 </div>
 
 
 <div class="ButtonModus2"> 
 <a href="/?gruberK:1:14" target="ifr">Ungerade Ein</a>
 </div>
 
 <div class="ButtonModus3"> 
 <a href="/?gruberK:1:15" target="ifr">Gerade Ein</a>
 </div>
 
 <div class="ButtonModus2"> 
 <a href="/?gruberK:1:16" target="ifr">Zufall</a>
 </div>
 
 
 
 </div>
 
 
<!-- ++++++++++++++++++++++ Tannenbaum ++++++++++++++++++++++ --> 
 
 
 <div class="Ort">
 <h2>Tannenbaum</h2>
 <div class="ButtonEin">
 <a href="/?gruberG:4:1" target="ifr">Tannenbaum Ein</a>
 </div>
 
 <div class="ButtonAus"> 
 <a href="/?gruberK:4:0" target="ifr">Tannenbaum Aus</a>
 </div>
 
 <div class="ButtonModus2"> 
 <a href="/?gruberK:4:2" target="ifr">L --> R Blinken</a>
 </div>
 
 <div class="ButtonModus3"> 
 <a href="/?gruberK:4:3" target="ifr">R --> L Blinken</a>
 </div>
 
 <div class="ButtonModus2"> 
 <a href="/?gruberK:4:4" target="ifr">L --> R Blieben An</a>
 </div>
 
 <div class="ButtonModus3"> 
 <a href="/?gruberK:4:5" target="ifr">R --> L Bleiben An</a>
 </div>
 
 <div class="ButtonModus2"> 
 <a href="/?gruberK:4:6" target="ifr">Aussen in Mitte Blinken</a>
 </div>
 
 <div class="ButtonModus3"> 
 <a href="/?gruberK:4:7" target="ifr">Mitte --> Aussen Blinken</a>
 </div>
 
 
 <div class="ButtonModus2"> 
 <a href="/?gruberK:4:8" target="ifr">Aussen in Mitte Bleiben An</a>
 </div>
 
 <div class="ButtonModus3"> 
 <a href="/?gruberK:4:9" target="ifr">Mitte --> Aussen Bleiben An</a>
 </div>
 
 
 <div class="ButtonModus2"> 
 <a href="/?gruberK:4:10" target="ifr">Ungerade Blinken</a>
 </div>
 
 <div class="ButtonModus3"> 
 <a href="/?gruberK:4:11" target="ifr">Gerade Blinken</a>
 </div>
 
 
 <div class="ButtonModus2"> 
 <a href="/?gruberK:4:12" target="ifr">Ungerade Bleiben An</a>
 </div>
 
 <div class="ButtonModus3"> 
 <a href="/?gruberK:4:13" target="ifr">Gerade Bleiben An</a>
 </div>
 
 
 <div class="ButtonModus2"> 
 <a href="/?gruberK:4:14" target="ifr">Ungerade Ein</a>
 </div>
 
 <div class="ButtonModus3"> 
 <a href="/?gruberK:4:15" target="ifr">Gerade Ein</a>
 </div>
 
 <div class="ButtonModus2"> 
 <a href="/?gruberK:4:16" target="ifr">Zufall</a>
 </div>
 
 
 
 </div>
 
 
 
 <!-- ++++++++++++++++++++++ Tujabaum ++++++++++++++++++++++ --> 
 
 
 <div class="Ort">
 <h2>Tujabaum</h2>
 <div class="ButtonEin">
 <a href="/?gruberG:2:1" target="ifr">Tujabaum Ein</a>
 </div>
 
 <div class="ButtonAus"> 
 <a href="/?gruberK:2:0" target="ifr">Tujabaum Aus</a>
 </div>
 
 <div class="ButtonModus2"> 
 <a href="/?gruberK:2:2" target="ifr">L --> R Blinken</a>
 </div>
 
 <div class="ButtonModus3"> 
 <a href="/?gruberK:2:3" target="ifr">R --> L Blinken</a>
 </div>
 
 <div class="ButtonModus2"> 
 <a href="/?gruberK:2:4" target="ifr">L --> R Blieben An</a>
 </div>
 
 <div class="ButtonModus3"> 
 <a href="/?gruberK:2:5" target="ifr">R --> L Bleiben An</a>
 </div>
 
 <div class="ButtonModus2"> 
 <a href="/?gruberK:2:6" target="ifr">Aussen in Mitte Blinken</a>
 </div>
 
 <div class="ButtonModus3"> 
 <a href="/?gruberK:2:7" target="ifr">Mitte --> Aussen Blinken</a>
 </div>
 
 
 <div class="ButtonModus2"> 
 <a href="/?gruberK:2:8" target="ifr">Aussen in Mitte Bleiben An</a>
 </div>
 
 <div class="ButtonModus3"> 
 <a href="/?gruberK:2:9" target="ifr">Mitte --> Aussen Bleiben An</a>
 </div>
 
 
 <div class="ButtonModus2"> 
 <a href="/?gruberK:2:10" target="ifr">Ungerade Blinken</a>
 </div>
 
 <div class="ButtonModus3"> 
 <a href="/?gruberK:2:11" target="ifr">Gerade Blinken</a>
 </div>
 
 
 <div class="ButtonModus2"> 
 <a href="/?gruberK:2:12" target="ifr">Ungerade Bleiben An</a>
 </div>
 
 <div class="ButtonModus3"> 
 <a href="/?gruberK:2:13" target="ifr">Gerade Bleiben An</a>
 </div>
 
 
 <div class="ButtonModus2"> 
 <a href="/?gruberK:2:14" target="ifr">Ungerade Ein</a>
 </div>
 
 <div class="ButtonModus3"> 
 <a href="/?gruberK:2:15" target="ifr">Gerade Ein</a>
 </div>
 
 <div class="ButtonModus2"> 
 <a href="/?gruberK:2:16" target="ifr">Zufall</a>
 </div>
 
 
 
 </div>
 
 
 
 <!--<div class="Ort1">
 
 <h2>Ort1</h2>
 <div class="BildOrt1">
 
 </div>
 <div class="ButtonOrt1AlleAn">
 <a href="/?pinD3=1" target="ifr">Ort 1 Alles Ein</a>
 </div>
 
 <div class="ButtonOrt1AlleAus">
 <a href="/?pinD3=0" target="ifr">Ort 1 Alles Aus</a>
 
 </div>
 
 <div class="ButtonOrt1BlinkenAn">
 <a href="/?Ort1MBlink=1" target="ifr">Ort 1 Blinken Ein</a>
 </div>
 
 <div class="ButtonOrt1BlinkenAus">
 <a href="/?Ort1MBlink=0" target="ifr">Ort 1 Blinken Aus</a>
 </div>
 </div>-->
  <iframe name="ifr"<!-- style="display:none;" width="0" height="0"-->></iframe>
</body>
</html>

Decke:
Hier, da ist wohl irgendwas schief gelaufen...

Ich sehe mit meinen Fähigkeiten leider keine Möglichkeit, mit kurzfristigen und kleinen Verbesserungen am Arduino- und/oder HTML-Code das alles noch wie beabsichtigt zum Laufen zu bekommen.

Wenn es so ist wie Du eingangs geschrieben hast:
Wenn ich das Programm auf den Arduino hochgeladen habe, funktioniert es soweit.
dann lade es auf den Arduino hoch und lass es soweit funktionieren!