Ausgang per Web schalten mit PHP

Auch auf die Gefahr hin, das ich mich wiederhole :slight_smile:
Schrittweise vorgehen. Wenn es nicht funktioniert, dann gehe einen Schritt zurück und prüfe ab wann es nicht mehr so läuft wie es sollte.
Was mir auf alle Fälle schon mal auffällt ist:

   char command =client.read();
    byte pinnumber =client.read();
    byte pinvalue =client.read();
    byte pintime =client.read();  //öffnungszeit Ventil zb. 15min
    long timerlength = client.read();

Hier liest Du viel mehr Daten als Du mit dem PHP-Script abschickst. Wenn pintime schon der Wert für den Timer ist, wofür ist dann timerlength gut?
Abgesehen davon, das Du mit client.read() nur ein Byte liest und keinen long-Wert.
timerpin[] und timerlength[] sind globale arrays, die außerhalb von loop() definiert werden müssen, damit sie ihre Werte zwischen den einzelnen loop() Duchläufen behalten. Eine Zuweisung zu diesen Arrays muss also immer mit einem Index erfolgen.
NUMTIMER ist ein "#define" mit dem man festlegt, wieviele timer es geben soll.

Ich würde sagen, wir gehen die Umsetzung des Arduino-Codes diesmal etwas anders an. Versuch mal stuchpunktartig zu beschreiben, was der Code machen soll, wenn das Kommando "T" an den Arduino geschickt wird.
Sowas wie

  • "T" aus verbindung gelesen
    1. Byte "Pinnumber" gelesen und in pinnumber gespeichert
    1. Byte "Pinvalue gelesen
  • ...

Damit kannst Du Dir schrittweise (da ist es wieder :-)) klar machen was passieren soll und es dann entsprechend programmieren.

Moin,
ich verstehe diese Code Zeile nicht, bzw. was mir die Fehlermeldung sagen will.. grrr
Die Codezeile wird Makiert:

//alle timer auswerten
for(int t=0; t<NUMTIMER;t++) {

Also, t=0; heißt doch es soll mit 0 beginnen, oder ?
Aber wo steht das t=0 ist ? :~
Und "t < NUMTIMER;" bis dahin soll gezählt werden
( #define NUMTIMER = 3; // wieviele timer es gibt (15,30,60 min) <-- Oder falsch verstanden ?

Fehlermeldung:

Mario_1.cpp: In function 'void loop()':
Mario_1:47: error: expected primary-expression before '[' token
Mario_1:47: error: expected primary-expression before ']' token
Mario_1:47: error: expected primary-expression before ';' token
Mario_1:47: error: expected `)' before ';' token
Mario_1:47: error: name lookup of 't' changed for new ISO 'for' scoping
Mario_1:47: error: using obsolete binding at 't'
Mario_1:47: error: expected `;' before ')' token
Mario_1:125: error: expected `}' at end of input
Mario_1:125: error: expected `}' at end of input
Mario_1:125: error: expected `}' at end of input

Vor dem "setup()" habe ich folgendes festgelegt:

unsigned long previousMillis = 0;       // speichert wie viele Sekunden seit derletzten aenderung vergangen sind
#define NUMTIMER[] = 3;          // wieviele timer es gibt (15,30,60 min)
int timerpin;
int timerlength;

Und im "loop()" steht das (hoffe ich habe dich da richtig verstanden (4. byte):

    char command =client.read();
    byte pinnumber =client.read();
    byte pinvalue =client.read();
    byte timerlength = client.read(); // zeit für ventil öffnung
    byte returnvalue = 0;

Hier nochmal der ganze Code:

#include <SPI.h>
#include <Ethernet.h>

// MAC und IP Konfiguration
byte mac[] = { 0x90, 0xA2, 0xDA, 0x00, 0xEE, 0xF3 };
IPAddress ip(192,168,178,216);
IPAddress gateway(192,168,178,1);
IPAddress subnet(255, 255, 255, 0);

// warten auf port 23
EthernetServer server(23);

unsigned long previousMillis = 0;       // speichert wie viele Sekunden seit derletzten aenderung vergangen sind
#define NUMTIMER = 3;          // wieviele timer es gibt (15,30,60 min)
int timerpin;
int timerlength;


void setup() {
  //Ethernet initialisieren
  Ethernet.begin(mac, ip, gateway, subnet);
  // Server starten
  server.begin();
  // serielle konsole öffnen (debug)
  Serial.begin(9600);
}

void loop() {
  
  // auf eine eingehende Verbindung warten
  EthernetClient client = server.available();
  //wenn verbindung, dann 3 bytes lesen; 
  if (client) {

    char command =client.read();
    byte pinnumber =client.read();
    byte pinvalue =client.read();
    byte timerlength = client.read(); // zeit für ventil öffnung
    byte returnvalue = 0;
   
    
    //prüfen ob eine Sekunde vergangen ist
  if(millis()-previousMillis >= 1000) {
    //aktuelle Zeit merken
    previousMillis = millis();

    //alle timer auswerten 
    for(int t=0; t<NUMTIMER;t++) {

      //nur timer beachten, die auch verwendet werden sollen (positive PIN nummer)
      if(timerpin[t] > 0 ){

        //bei aktuellem timer eine sekunde abziehen, wenn "0", dann abgelaufen
        if( --timerlength[t] == 0) {

          //Ausgang umschalten ( HIGH -> LOW oder LOW -> HIGH)
          digitalWrite(timerpin[t],!digitalRead(timerpin[t]));  

          //timer deaktivieren ( timer mit pin "-1" sind inaktiv)
          timerpin[t] = -1;

          //debug
          Serial.print("Timer ");
          Serial.print(t);
          Serial.println(" abeglaufen.");
        }
      }  
    }
  }

    //Kommando auswerten
    switch(command) {

    case 'S':
      Serial.print("S Kommando empfangen, setze Pin ");
      Serial.print(pinnumber);
      Serial.print(" auf ");
      Serial.println(pinvalue);
      pinMode(pinnumber,OUTPUT);
      digitalWrite(pinnumber,pinvalue);
     //gesetzten wert auch zurückliefern
      returnvalue=pinvalue;
      break;

    case 'R':
      returnvalue = digitalRead(pinnumber);
      Serial.print("R Kommando empfangen, lese Pin ");
      Serial.print(pinnumber);
      Serial.print(". Wert = ");
      Serial.println(returnvalue);
      break;

   case 'T':
     Serial.print("T Kommando empfangen, setze Pin ");
      Serial.print(pinnumber);
      Serial.print(" auf ");
      Serial.println(pinvalue);
      pinMode(pinnumber,OUTPUT);
      digitalWrite(pinnumber,pinvalue);
      Serial.print(" für ");
      Serial.print(timerlength);
      Serial.print(" min");
     //gesetzten wert auch zurückliefern
      returnvalue=pinvalue;
      break;
     
    default:
      Serial.println("Fehler, unbekanntes Kommando");    
      break;

    }  
    
    // rückantwort senden (1 byte) die "+48" sorgen dafür das der Wert 0 oder 1 als Zeichen "0" oder "1" übertragen werden. 
    //das vereinfacht die verarbeitung in php.
    client.write(returnvalue+48);
    
    //übertragung erzwingen
    client.flush();
    
    //10ms delay damit daten sicher gesendet werden.
    delay(10);
    
    //wichtg! client verbindung beenden
    client.stop();
  }
}

Irgendwie habe ich wohl was falsch deklariert oderso, kann aber nichts mit den Fehlermeldungen Anfangen :blush:

Die Variable "t" deklarierst du ja direkt in der for-Schleife.

Das einzige, was ich jetzt gefunden hab ist folgende zeile, die ich mir nicht erklären kann:
digitalWrite(timerpin[t],!digitalRead(timerpin[t])); 
digitalRead liefert HIGH oder LOW (1 oder 0 intern) - sagst du damit, dass es das Gegenteil von dem gelieferten Wert sein soll? Weiß nicht, ob das so funktioniert...

Hi,
laut Mario´s Sketch-Auszug, soll das wohl funktionieren.
Er hatte das ganze wohl schon getestet.

Zur Motivation smiley-grin
Ich habe das heute Morgen mal fix umgesetzt. Es war einfach zu warm um noch schlafen zu können smiley-sweat
Es funktioniert. Sogar recht einfach. Auch die Steuerung der "Timer" über millis() ist eigentlich recht simple.

//prüfen ob eine Sekunde vergangen ist
  if(millis()-previousMillis >= 1000) {
    //aktuelle Zeit merken
    previousMillis = millis();

    //alle timer auswerten 
    for(int t=0; t<NUMTIMER;t++) {

      //nur timer beachten, die auch verwendet werden sollen (positive PIN nummer)
      if(timerpin[t] > 0 ){

        //bei aktuellem timer eine sekunde abziehen, wenn "0", dann abgelaufen
        if( --timerlength[t] == 0) {

          //Ausgang umschalten ( HIGH -> LOW oder LOW -> HIGH)
          digitalWrite(timerpin[t],!digitalRead(timerpin[t]));  

          //timer deaktivieren ( timer mit pin "-1" sind inaktiv)
          timerpin[t] = -1;

          //debug
          Serial.print("Timer ");
          Serial.print(t);
          Serial.println(" abeglaufen.");
        }
      }  
    }
  }

Damit versuche ich mich,also daraus einen ganzen SKETCH zu machen... Klapp nur irgendwie nicht, weil ich einiges (noch) nicht verstehe. :blush:

( #define NUMTIMER = 3; // wieviele timer es gibt (15,30,60 min) <-- Oder falsch verstanden ?

Leider ja, denn die 15,30 und 60 Minuten sind ja nur eine Zeit, von der Du eine auswählst, das hat ja noch nichts mit einem Timer zu tun.
Verschiedene Timer brauchst Du, wenn Du mehrere Ventile steuern willst.
Z.B. timerpin[0]=9, timerlength[0]=900 würde den ersten Timer für Pin 9 auf 900 Sekunden (15min) setzen.
timerpin[1]=10, timerlength[1]=3600 wäre dann der 2. Timer für Pin 10 und 60 Minuten.

digitalWrite(timerpin[t],!digitalRead(timerpin[t])); 
sieht seltsam aus, funktioniert aber.

unsigned long previousMillis = 0;       // speichert wie viele Sekunden seit derletzten aenderung vergangen sind
#define NUMTIMER[] = 3;          // wieviele timer es gibt (15,30,60 min)
int timerpin;
int timerlength;

Die previousMillis sind richtig, aber das define nicht. Wie bereits geschrieben, müssen timerpin und timerlength beides Arrays sehen.
Also

unsigned long previousMillis = 0;       // speichert wie viele Sekunden seit derletzten aenderung vergangen sind
#define NUMTIMER 3          // wieviele timer es gibt (15,30,60 min)
int timerpin[NUMTIMER];
int timerlength[NUMTIMER];
...
void setup() {
...
}

Hinter das "#define" kommt kein Semikolon und die Zuweisung erfolgt auch nicht mit einem "=". "define" ist eine Präprozessoranweisung für den Compiler. Der geht einfach nur stumpf durch den gesamten Code und ersetzt überall das "NUMTIMER" durch die 3, das hat noch nichst mit dem Programm zu tun, das ist reine Textersetzung. Das Trick dabei ist, das man später die Anzahl der Arrayeinträge an einer einzigen Stelle ändern kann, ohne die 3 überall im Code zu ersetzen.

Die Auswertung für die vergangene Zeit darf auch nicht innerhalb des "if(client)" Blocks stehen, denn die Zeit läuft ja auch weiter wenn kein Client connected ist.
Am besten setzt Du den Zeit-Auswertungsblock gleich an den Anfang der loop().

Was bei der Auswertung des Kommandos "T" noch fehlt, ist das Setzen des übergebenen Timer-Wertes. Der muss ja irgendwo gespeichert werden, ebenso wie die Pin Nummer, die dem Timer zugeordnet ist.

Die Lösung selbst ist eigentlich nicht schwer, Du versuchst aber alles auf einmal zu machen und dafür fehlt die zur Zeit noch das Wissen und die Übersicht. Versuch daher erstmal den Ablauf wie Du ihn verstehst zu beschreiben. Sobald das stimmt und Du verstehst was, wann wie passieren soll, dann fängst Du an das in ein C Programm zu giessen.

Hallo Mario,
Danke für die super Erklärung !!

Hinter das "#define" kommt kein Semikolon und die Zuweisung erfolgt auch nicht mit einem "=". "define" ist eine Präprozessoranweisung für den Compiler. Der geht einfach nur stumpf durch den gesamten Code und ersetzt überall das "NUMTIMER" durch die 3, das hat noch nichst mit dem Programm zu tun, das ist reine Textersetzung. Das Trick dabei ist, das man später die Anzahl der Arrayeinträge an einer einzigen Stelle ändern kann, ohne die 3 überall im Code zu ersetzen.

So versteht das auch einer wie ich XD

Dann mach ich mal...
Bis später... :wink:

Hi,
ok, ich raff es nicht. :blush:
Mein Sketch sieht jetzt so aus, das er zwar kompiliert aber nicht mehr tut.
Rufe ich die Website auf, kommt die Fehlermeldung "Kommando konnte nicht abgesetzt werden"
Hier mein Sketch:

#include <SPI.h>
#include <Ethernet.h>

// MAC und IP Konfiguration
byte mac[] = { 0x90, 0xA2, 0xDA, 0x00, 0xEE, 0xF3 };
IPAddress ip(192,168,178,216);
IPAddress gateway(192,168,178,1);
IPAddress subnet(255, 255, 255, 0);

// warten auf port 23
EthernetServer server(23);

unsigned long previousMillis = 0;       // speichert wie viele Sekunden seit derletzten aenderung vergangen sind
#define NUMTIMER 3          // wieviele timer es gibt (15,30,60 min)
int timerpin[NUMTIMER];
int timerlength[NUMTIMER];

void setup() {
  //Ethernet initialisieren
  Ethernet.begin(mac, ip, gateway, subnet);
  // Server starten
  server.begin();
  // serielle konsole öffnen (debug)
  Serial.begin(9600);
}

void loop() {
  
  //prüfen ob eine Sekunde vergangen ist
  if(millis()-previousMillis >= 1000) {
    //aktuelle Zeit merken
    previousMillis = millis();

    //alle timer auswerten 
    for(int t=0; t<NUMTIMER;t++) {

      //nur timer beachten, die auch verwendet werden sollen (positive PIN nummer)
      if(timerpin[t] > 0 ){

        //bei aktuellem timer eine sekunde abziehen, wenn "0", dann abgelaufen
        if( --timerlength[t] == 0) {

          //Ausgang umschalten ( HIGH -> LOW oder LOW -> HIGH)
          digitalWrite(timerpin[t],!digitalRead(timerpin[t]));  

          //timer deaktivieren ( timer mit pin "-1" sind inaktiv)
          timerpin[t] = -1;

          //debug
          Serial.print("Timer ");
          Serial.print(t);
          Serial.println(" abeglaufen.");
        }
      }  
    }
  }
  
  // auf eine eingehende Verbindung warten
  EthernetClient client = server.available();
  //wenn verbindung, dann 4 bytes lesen; 
  if (client) {

    char command =client.read();
    byte pinnumber =client.read();
    byte pinvalue =client.read();
    byte timerlength = client.read(); // zeit für ventil öffnung
    byte returnvalue = 0;
    byte timerpin = pinnumber;
   

    //Kommando auswerten
    switch(command) {

    case 'S':
      Serial.print("S Kommando empfangen, setze Pin ");
      Serial.print(pinnumber);
      Serial.print(" auf ");
      Serial.println(pinvalue);
      pinMode(pinnumber,OUTPUT);
      digitalWrite(pinnumber,pinvalue);
     //gesetzten wert auch zurückliefern
      returnvalue=pinvalue;
      break;

    case 'R':
      returnvalue = digitalRead(pinnumber);
      Serial.print("R Kommando empfangen, lese Pin ");
      Serial.print(pinnumber);
      Serial.print(". Wert = ");
      Serial.println(returnvalue);
      break;

   case 'T':
     Serial.print("T Kommando empfangen, setze Pin ");     
      Serial.print(timerpin);
      Serial.print(" auf ");
      Serial.println(pinvalue);
      pinMode(pinnumber,OUTPUT);
      digitalWrite(timerpin,pinvalue);
      Serial.print(" für ");
      Serial.print(timerlength);
      Serial.print(" min");
     //gesetzten wert auch zurückliefern
      returnvalue=pinvalue;
      break;
     
    default:
      Serial.println("Fehler, unbekanntes Kommando");    
      break;

    }  
    
    // rückantwort senden (1 byte) die "+48" sorgen dafür das der Wert 0 oder 1 als Zeichen "0" oder "1" übertragen werden. 
    //das vereinfacht die verarbeitung in php.
    client.write(returnvalue+48);
    
    //übertragung erzwingen
    client.flush();
    
    //10ms delay damit daten sicher gesendet werden.
    delay(10);
    
    //wichtg! client verbindung beenden
    client.stop();
  }
}

Irgendwie habe den jetzt total vermurgst... Ich bin der Meinung ich habe alles übergeben, aber anscheinend fehlt irgendwas oder ist falsch gesetzt.

Hier der PHP Teil:

<html>
<head>
</head>
<body>

<h1>Test</h1>
<?php

$arduino_ip = "192.168.178.216";
$arduino_port = "23";

function arduino_send($ip,$port,$command) {
    $res = fsockopen($ip,$port);
    if($res) {
        fwrite($res,$command);
        $ret =fread($res,1);
        echo "arduino_send() returned = $ret 
\n";
        return $ret;
    } else {
        echo "Fehler, Kommando konnte nicht abgesetzt werden";
    }
}

if(isset($_GET['led1'])) {
    $led1 = $_GET['led1'];
    arduino_send($arduino_ip,$arduino_port,"S".chr(4).chr($led1));
} else {
    $led1 = arduino_send($arduino_ip,$arduino_port,"R".chr(4).chr(0));
}

if(isset($_GET['V1'])) {
    $V1 = $_GET['V1'];
    arduino_send($arduino_ip,$arduino_port,"T".chr(4).chr(1).chr($V1));
} else {
    $V1 = arduino_send($arduino_ip,$arduino_port,"R".chr(4).chr(0));
}
?>
<form action="">
  <p>
LED 1 :
<input type="radio" name="led1" value="0" <?php if($led1==0) echo "checked" ?> >aus -
<input type="radio" name="led1" value="1" <?php if($led1==1) echo "checked" ?> >an


 </p>
  <input type="submit" name="submit" value="Senden">
</form>


<form action="">
  <p>
   <select name="V1">
    <option value="15">15 min</option>
    <option value="30">30 min</option>
    <option value="60">60 min</option>
   </select>
  </p>
   <input type="submit" name="submit" value="Senden">
</form>

</body
</HTML>
  1. Du musst, wenn Du Dein Protokoll änderst, natürlich auch den PHP-Code anpassen. Dein Arduino-Sketch erwartet das bei JEDEM Kommando nun 4 Bytes gesendet werden. Da Du ber Beim Kommando "R" und "S" nur 3 Bytes sendest, wartet Dein Sketch entsprechend bis zum timeout und macht nicht weiter. Du kannst das auf zwei Arten beheben. Du kannst immer 4 Bytes schicken, das ist aber unpraktisch, da es ja irgendwann mit zusätzlichen Kommandos auch 5 oder 6 oder mehr werden können. Dann müßtest Du immer alles neu Anpassen, was zum Einen Fehleranfällig und zum Anderen Aufwändig ist.
    Du kannst aber auch bei jedem Aufruf nur die Bytes schicken, die Du benötigst. Dein Sketch liest dann am Anfang immer nur die minimale Anzahl von Bytes von einem client und erst in switch - case Block liest Du die für das entsprechende Kommando notwendigen zusätzlichen Bytes ein. Lediglich die Variablen dafür würde ich bereits oben deklarieren, das ist übersichtlicher.

  2. Wieso glaubst Du, das Du in Deinem Sketch alles korrekt übergeben hast? timerpin und timerlength sind Arrays, Du verwendest aber nirgendwo in der loop() ein Array. Im Gegenteil, Du deklarierst die Variablen timerlength und timerpin nocheinmal innerhalb von loop() als lokale Variablen vom Typ byte. Diese "verdecken" dann aber die globalen Arrays timerlength und timerpin. Die Schleife, die im Sekundentakt die einzelnen Timer abklappert erwartet aber, das die Variablen timerlength und timerpin auf ein Array zeigen und verwendet an dieser Stelle den Speicher auch so, womit Du plötzlich statt auf jeweils ein Byte, wie es deklariert ist auf 3x2x4 Bytes zugreifst und diese veränderst. Damit bringst Du Dein Programm total aus dem Tritt, da Du mit Sicherheit Speicherzellen veränderst, die vom Programm anderweitig verwendet werden.

Um das erstmal zum Laufen zu bekommen, ändere folgendes in Deinem Sketch:

  1. Das "byte" vor timerlength und timerpin in dem Block wo die Daten gelesen werden muss weg.
  2. Innerhalb von loop() änderst Du alle Vorkommen von timerlength in timerlength[0]
  3. Innerhalb von loop() änderst Du alle Vorkommen von timerpin in timerpin[0]

Damit überschreibt dann Dein Programm innerhalb von loop() nicht mehr die globalen Variablen für die Timer und verwendet erstmal einen timer für das Zählen der Zeit.
Im übrigen zählt Dein Sketch die timer in Sekunden nicht in Minuten. Damit es Minuten werden solltest Du den übergebenen Wert noch mit 60 multiplizieren.

Leider ist Deine Lösung aber generell für eine Verwendung von mehreren Timern ungeeignet, da Du in Deinem Aufruf nicht übergibst welcher Timer verwendet werden soll. Sobald Du mehr als ein Ventil schalten willst, bekommst Du Probleme.

Hallo Mario,
sorry wenn ich verärgert habe, aber ich habe es nicht verstanden.
Ich habe es jetzt so verändert wie du es geschrieben hast und es funktioniert.

Um das erstmal zum Laufen zu bekommen, ändere folgendes in Deinem Sketch:

  1. Das "byte" vor timerlength und timerpin in dem Block wo die Daten gelesen werden muss weg.
  2. Innerhalb von loop() änderst Du alle Vorkommen von timerlength in timerlength[0]
  3. Innerhalb von loop() änderst Du alle Vorkommen von timerpin in timerpin[0]

Habe ich getan, es läuft. <Danke!>

Im übrigen zählt Dein Sketch die timer in Sekunden nicht in Minuten. Damit es Minuten werden solltest Du den übergebenen Wert noch mit 60 multiplizieren.

Auch hier muss ich wieder passen, ich versuche seit 3 std heraus zu finden wo ich das eintragen muss, aber ohne erfolg. Egal wo ich es eintrage, nix, die Zeit wird nicht multipliziert. :frowning:

Leider ist Deine Lösung aber generell für eine Verwendung von mehreren Timern ungeeignet, da Du in Deinem Aufruf nicht übergibst welcher Timer verwendet werden soll. Sobald Du mehr als ein Ventil schalten willst, bekommst Du Probleme.

Ok, aber es soll auch immer nur ein Ventik zur Zeit geöffnet sein,niemals zwei.
Auch hier stehe ich auf dem Schlauch, meinst du mit mehreren TIMERN z.b. V1, V2, V3 für Ventil1, Ventil2 usw. ? So wie im PHP angegeben?
Wenn ich es so versuche, gibt er Fehlermeldungen raus:

int NUMTIMER[] = {0,1};

0 für V1 und 1 für V2 , Dachte ich :frowning:

Ich habe jetzt mal die PHP Seite erweitert, es funktioniert, aber wie du schon sagst, im SerialMonitor, wird immer nur Timer 0 angezeigt.
Hier der Sketch:

#include <SPI.h>
#include <Ethernet.h>

// MAC und IP Konfiguration
byte mac[] = { 0x90, 0xA2, 0xDA, 0x00, 0xEE, 0xF3 };
IPAddress ip(192,168,178,216);
IPAddress gateway(192,168,178,1);
IPAddress subnet(255, 255, 255, 0);

// warten auf port 23
EthernetServer server(23);

unsigned long previousMillis = 0;       

#define NUMTIMER 2

int timerpin[NUMTIMER];
int timerlength[NUMTIMER];

void setup() {
  //Ethernet initialisieren
  Ethernet.begin(mac, ip, gateway, subnet);
  // Server starten
  server.begin();
  // serielle konsole öffnen (debug)
  Serial.begin(9600);
}

void loop() {
  
    //prüfen ob eine Sekunde vergangen ist
  if(millis()-previousMillis >= 1000) {
    //aktuelle Zeit merken
    previousMillis = millis();

    //alle timer auswerten 
    for(int t=0; t<NUMTIMER;t++) {

      //nur timer beachten, die auch verwendet werden sollen (positive PIN nummer)
      if(timerpin[t] > 0 ){

        //bei aktuellem timer eine sekunde abziehen, wenn "0", dann abgelaufen
        if( --timerlength[t] == 0) {
          //Ausgang umschalten ( HIGH -> LOW oder LOW -> HIGH)
          digitalWrite(timerpin[t],!digitalRead(timerpin[t]));  

          //timer deaktivieren ( timer mit pin "-1" sind inaktiv)
          timerpin[t] = -1;

          //debug
          Serial.print("T Kommando empfangen, Timer ");
          Serial.print(t);
          Serial.println(" abeglaufen.");
        }
      }  
    }
  }

  // auf eine eingehende Verbindung warten
  EthernetClient client = server.available();
  
  if (client) {
   
    char command =client.read();
    byte pinnumber =client.read();
    byte pinvalue =client.read();
    byte returnvalue = 0;
    
   

    //Kommando auswerten
    switch(command) {

    case 'S':
      Serial.print("S Kommando empfangen, setze Pin ");
      Serial.print(pinnumber);
      Serial.print(" auf ");
      Serial.println(pinvalue);
      pinMode(pinnumber,OUTPUT);
      digitalWrite(pinnumber,pinvalue);
     //gesetzten wert auch zurückliefern
      returnvalue=pinvalue;
      break;

    case 'R':
      returnvalue = digitalRead(pinnumber);
      Serial.print("R Kommando empfangen, lese Pin ");
      Serial.print(pinnumber);
      Serial.print(". Wert = ");
      Serial.println(returnvalue);
      break;

   case 'T':
     timerlength[0] = client.read(); // zeit für ventil öffnung
     timerpin[0] = pinnumber;
     Serial.print("T Kommando empfangen, setze Pin ");     
      Serial.print(timerpin[0]);
      Serial.print(" auf ");
      Serial.println(pinvalue);
      pinMode(timerpin[0],OUTPUT);
      digitalWrite(timerpin[0],pinvalue);
      Serial.print("T Kommando empfangen, setze Time auf ");
      Serial.print(timerlength[0]);
      Serial.print(" sec");
      Serial.println(" ");
     //gesetzten wert auch zurückliefern
      returnvalue=pinvalue;
      break;

    default:
      Serial.println("Fehler, unbekanntes Kommando");    
      break;

    }  
    
    // rückantwort senden (1 byte) die "+48" sorgen dafür das der Wert 0 oder 1 als Zeichen "0" oder "1" übertragen werden. 
    //das vereinfacht die verarbeitung in php.
    client.write(returnvalue+48);
    
    //übertragung erzwingen
    client.flush();
    
    //10ms delay damit daten sicher gesendet werden.
    delay(10);
    
    //wichtg! client verbindung beenden
    client.stop();
  }
}

und hier der PHP-Code:

<html>
<head>
</head>
<body>

<h1>Test</h1>
<?php

$arduino_ip = "192.168.178.216";
$arduino_port = "23";

function arduino_send($ip,$port,$command) {
    $res = fsockopen($ip,$port);
    if($res) {
        fwrite($res,$command);
        $ret =fread($res,1);
        echo "arduino_send() returned = $ret 
\n";
        return $ret;
    } else {
        echo "Fehler, Kommando konnte nicht abgesetzt werden";
    }
}

if(isset($_GET['led1'])) {
    $led1 = $_GET['led1'];
    arduino_send($arduino_ip,$arduino_port,"S".chr(4).chr($led1));
} else {
    $led1 = arduino_send($arduino_ip,$arduino_port,"R".chr(4).chr(0));
}

if(isset($_GET['V1'])) {
    $V1 = $_GET['V1'];
    arduino_send($arduino_ip,$arduino_port,"T".chr(4).chr(1).chr($V1));
} else {
    $V1 = arduino_send($arduino_ip,$arduino_port,"R".chr(4).chr(0));
}

if(isset($_GET['V2'])) {
    $V2 = $_GET['V2'];
    arduino_send($arduino_ip,$arduino_port,"T".chr(5).chr(1).chr($V2));
} else {
    $V2 = arduino_send($arduino_ip,$arduino_port,"R".chr(5).chr(0));
}
?>
<form action="">
  <p>
LED 1 :
<input type="radio" name="led1" value="0" <?php if($led1==0) echo "checked" ?> >aus -
<input type="radio" name="led1" value="1" <?php if($led1==1) echo "checked" ?> >an


  </p>
  <input type="submit" name="submit" value="Senden">
</form>


<form action="">
  <p>Timer 1

   <select name="V1">
    <option value="15">15 min</option>
    <option value="30">30 min</option>
    <option value="45">45 min</option>
    <option value="60">60 min</option>
   </select>
  </p>
   <input type="submit" name="submit" value="Senden">
</form>

<form action="">
 <p>Timer 2

  <select name="V2">
  <option value="15">15 min</option>
  <option value="30">30 min</option>
  <option value="45">45 min</option>
  <option value="60">60 min</option>
  </select>
 </p>
  <input type="submit" name="submit" value="senden">
</form>

</body
</HTML>

Guten Morgen,

Auch hier muss ich wieder passen, ich versuche seit 3 std heraus zu finden wo ich das eintragen muss, aber ohne erfolg. Egal wo ich es eintrage, nix, die Zeit wird nicht multipliziert.

Wo hast Du es denn eingetragen? Und was hat nicht funktioniert? Entschuldige wenn ich so direkt frage, aber hast Du einfach nur probiert, oder überlegt wo es passen könnte? Das ist nicht böse gemeint, aber ich glaube Du verfolgst bei der Programmierung immer noch einen falschen Ansatz. Erstmal irgendwelchen Code schreiben und dann schauen was der macht ist keine gute Taktik. Es hat einen Grund warum ich Dich schon zwei Mal versucht habe zu überzeugen erstmal die Schritte die in Deinem Programm passieren sollen in Worten als kleine Liste aufzuschreiben. Wenn Du da schon ein Problem mit dem Verständnis zum Ablauf hast, wird das beim Programmieren nicht besser, weil Du Dich dort noch zusätzlich auf die Syntax von C und die semantisch korrekte Umsetzung Deines Konzepts konzentrieren musst.
Am Beispiel der Umrechnung der Dauer in Minuten:

  • PHP sendet die Dauer als 4. Parameter in Minuten
  • Sketch liest im switch-case Block den zusätzlichen 4. Parameter, wenn Kommando "T" erkannt wurde in die Variable timerlength[0]
  • timerlength[0] muss aber in Sekunden angegeben werden, daher muss der gerade übermittelte Werte umgerechnet werden
  • Um auf Sekunden zu kommen, muss der Wert mit 60 multipliziert werden -> timerlength[0] = timerlength[0] *60

Ok, aber es soll auch immer nur ein Ventik zur Zeit geöffnet sein,niemals zwei.
Auch hier stehe ich auf dem Schlauch, meinst du mit mehreren TIMERN z.b. V1, V2, V3 für Ventil1, Ventil2 usw. ? So wie im PHP angegeben?
Wenn ich es so versuche, gibt er Fehlermeldungen raus:
Code:
int NUMTIMER[] = {0,1};
0 für V1 und 1 für V2 , Dachte ich smiley-sad

Wann welches Ventil geöffnet ist, ist für die Programmierung an der Stelle erstmal völlig uninteressant. Warum?

  1. Weil man immer möglichst allgemeine Lösungen baut, in diesem Fall also mit der Möglichkeit "Ventile" (also Pins) unabhängig voneinander mit unterschiedlichen Timern zu schalten. Wie das dann von einer konkreten Anwendung genutzt wird, ist eine ganz andere Sache.
  2. Ein PHP- und HTML-Script ist schnell geändert, dort steckt ja die eigentliche Anwendungslogik drin, den Arduino später neu zu flashen ist deutlich mehr Aufwand. Den Arduino interessiert ja im Prinzip auch nicht um "was" es eigentlich geht. Der bekommt nur "Kommandos" wie "schalte Pin 3 ein" , "lese Pin 4 aus", oder "Schalte Pin 9 für 15 Minuten auf HIGH, danach wieder auf LOW" und das sollte alles unabhängig voneinander funktionieren.

Du hast geschrieben, das Du das mit dem "#define NUMTIMER 3" verstanden hast, schreibst aber trotzdem wieder sowas : int NUMTIMER[] = {0,1};? Das ist das, was ich mit dem rumprobieren meine. Das führt leider in den seltensten Fällen zum Erfolg. Und selbst wenn, dann nur aus Glück und man versteht meistens am Ende trotzdem nicht warum es plötzlich funktioniert. Ergo -> kein guter Ansatz.

Du hast in Deinem Code ein
#define NUMTIMER 2 stehen. Das bedeutet, das die beiden Arrays timerlength und timerpin je 2 Einträge haben können. Du hast also schon 2 Timer zur Verfügung, die Du für V1 und V2 nutzen kannst (timerlength[0] und timerlength[1]. Was fehlt ist nur die Zuordnung. Dein Sketch muss ja irgendwo die Information herbekommen, welcher Timer für welches Pin verwendet werden soll.
Hier hast Du auch wieder zwei Möglichkeiten.

  1. Die einfache Variante wäre einfach zu sagen wenn Pin 4 übergeben wird, nutze Timer 0 und bei Pin 5 nutze Timer 1. Das ist aber schlechter Stil, denn Du verlagerst damit Teile Deiner Anwendungslogik in den allgemeinen Arduino-Sketch. Die Gefahr besteht, das Du irgendwann im PHP-Code plötzlich Pin 6 übergibst, weil sich Deine Schaltung geändert hat und dann der Code nicht mehr greift.
  2. Die allgemeinere Variante. Du übergibst mit dem Kommando "T" einfach noch ein zusätzliches Byte, das bestimmt, welcher Timer verwendet werden soll. Dieses wird dann, genauso wie timerlength per client.read() gelesen. Allerdings musst Du aufpassen, denn Du solltest ersten die Timer-Nummer übergeben, ehe Du mit timerlength[timernummer] = client.read() die Länge liest. (Du kannst Dir natürlich auch eine einfache "byte" Variable als "Zwischenspeicher" deklarieren.

Achso, ich bin auch nicht "verärgert". Leider versuchst Du immer noch den zweiten Schritt vor dem Ersten zu machen und stolperst dabei regelmäßig. Das ist, sagen wir mal eine kleine Gedulds-Herausforderung für mich :D, gehört aber einfach dazu, wenn man jemanden etwas wirklich verständlich machen will. "Echtes" Lernen bedeutet nun mal, das man Prinzipien versteht und nicht einfach nur "auswendig" lernt. Damit habe ich mir als Tutor immer zusätzliche Arbeit gemacht und auch so machens "Opfer" zur Verzweiflung getrieben. Du bist also in guter Gesellschaft ]:smiley:

int ledPin[] = {7,8,9}; // LED-Array mit Pin-werten

Ist korrekt, aich Dein

int NUMTIMER[] = {0,1};

war, zumindest für diese Zeile, erstmal snytaktischkorrekt.
Allerdings wird im Sketch selbst das NUMTIMER nicht als Variable oder Array verwendet, sonder nur als Platzhalter für die Zahl 3 oder 5 oder 2 oder welche Anzahl von Timern Du haben willst. NUMTIMER ist also NICHT der Timer selbst sonder nur der Wert für die Anzahl der Timer die es geben soll.
timerlength[] und timerpin[] sind die beiden Arrays, die den eigentlichen Timer bilden, denn diese speichern ja die Länge der Laufzeit und den Pin für einen spezifischen timer, sprich Index der Arrays.

Schön, das Dein Projekt jetzt funktioniert. Du hast Dir natürlich für den Anfang auch ein recht komplexes Projekt ausgesucht. Aus meiner Sicht etwas zu komplex für eine sinnvolle Lernkurve, denn zu viele Baustellen und Prinzipien gleichzeitig mit denen man sich auseinandersetzen muss, überfordern einen doch leicht.
Nur nicht aufgeben und dran bleiben, irgendwann lichtet sich der Nebel :slight_smile:

Hallo allerseits,
also Projekt läuft.
Nochmal vielen Dank an "mkl0815" für die tolle Hilfe und Unterstützung :smiley:

Arduino Ausgänge werden mittels PHP für eine Vorgegeben Zeit geöffnet und dann automatisch geschlossen.
Den Code liefer ich später nach, muss den noch kommentieren.
Sollte jemand den schon vorher haben wollen, kurze PN.

Guten Morgen,

kann es sein, das aus diesem Thread Beiträge verschwunden sind? Speziell zwischen meinem letzten Post und dem vorletzten fehlt irgendwie der "es klappt jetzt" Post von Cetax. Daher mutet der Thread jetzt etwas seltsam an :slight_smile:

Moin,
ja also wie soll ich sagen... :blush:
Ups.. Ich habe den falschen Button angeklickt und da war es wech..
Sorry !

Hallo allerseits,
ich wende mich nochmal mit einem Problem an Euch.
Ich versuche gerade die Rest-Zeit eines Timers auszulesen und auf einer Website mittels PHP auszugeben.
Was auch teils schon geht. Im SerialMonitor wird dann z.b. 900sec angezeigt, aber auf der Website nur die erste Zahl!
Wie kann ich die ganze Zahl ausgeben lassen ?
Hier mal der Sketch (Case 'U' ist der Teil) :

#include <SPI.h>
#include <Ethernet.h>

// MAC und IP Konfiguration
byte mac[] = { 0x90, 0xA2, 0xDA, 0x00, 0xEE, 0xF3 };
IPAddress ip(192,168,178,216);
IPAddress gateway(192,168,178,1);
IPAddress subnet(255, 255, 255, 0);

// warten auf port 23
EthernetServer server(23);

unsigned long previousMillis = 0;       

#define NUMTIMER 6                    // 6 Timer
int TI;
int timerpin[NUMTIMER];
int timerlength[NUMTIMER];

void setup() {
  //Ethernet initialisieren
  Ethernet.begin(mac, ip, gateway, subnet);
  // Server starten
  server.begin();
  // serielle konsole öffnen (debug)
  Serial.begin(9600);
}

void loop() {
  
    //prüfen ob eine Sekunde vergangen ist
  if(millis()-previousMillis >= 1000) {
    //aktuelle Zeit merken
    previousMillis = millis();

    //alle timer auswerten 
    for(int t=0; t<NUMTIMER;t++) {

      //nur timer beachten, die auch verwendet werden sollen (positive PIN nummer)
      if(timerpin[t] > 0 ){

        //bei aktuellem timer eine sekunde abziehen, wenn "0", dann abgelaufen
        if( --timerlength[t] == 0) {
          //Ausgang umschalten ( HIGH -> LOW oder LOW -> HIGH)
          digitalWrite(timerpin[t],!digitalRead(timerpin[t]));  

          //timer deaktivieren ( timer mit pin "-1" sind inaktiv)
          timerpin[t] = -1;

          //debug
          Serial.print("T Kommando empfangen, Timer ");
          Serial.print(t);
          Serial.println(" abeglaufen.");
        }
      }  
    }
  }

  // auf eine eingehende Verbindung warten
  EthernetClient client = server.available();
  
  if (client) {
   
    char command =client.read();
    byte pinnumber =client.read();
    byte pinvalue =client.read();
    byte returnvalue = 0;
    
   

    //Kommando auswerten
    switch(command) {

    case 'S':
      Serial.print("S Kommando empfangen, setze Pin ");
      Serial.print(pinnumber);
      Serial.print(" auf ");
      Serial.println(pinvalue);
      pinMode(pinnumber,OUTPUT);
      digitalWrite(pinnumber,pinvalue);
     //gesetzten wert auch zurückliefern
      returnvalue=pinvalue;
      break;

    case 'R':
      returnvalue = digitalRead(pinnumber);
      Serial.print("R Kommando empfangen, lese Pin ");
      Serial.print(pinnumber);
      Serial.print(". Wert = ");
      Serial.println(returnvalue);
      break;
      
   case 'U':
      Serial.print("U Kommando empfangen, lese Timer ");
      Serial.print(TI);
      Serial.print(" Wert = ");
      Serial.println(timerlength[TI]);
      client.print(timerlength[TI]);
      //client.print(returnvalue);
      break;

   case 'T':
     TI = client.read();
     timerlength[TI] = client.read(); // zeit für ventil öffnung
     timerpin[TI] = pinnumber;
     timerlength[TI] = timerlength[TI] *60; // die empfangenen sec*60=min
     Serial.print("T Kommando empfangen, ");
     Serial.print("Timer ");
     Serial.print(TI);
     Serial.println(" gestartet");
      Serial.print("T Kommando empfangen, setze Pin ");     
      Serial.print(timerpin[TI]);
      Serial.print(" auf ");
      Serial.println(pinvalue);
      pinMode(timerpin[TI],OUTPUT);
      digitalWrite(timerpin[TI],pinvalue);
      Serial.print("T Kommando empfangen, setze Time auf ");
      Serial.print(timerlength[TI]);
      Serial.print(" sec");
      Serial.println(" ");
     //gesetzten wert auch zurückliefern
      returnvalue=pinvalue;
      break;

    default:
      Serial.println("Fehler, unbekanntes Kommando");    
      break;

    }  
    
    // rückantwort senden (1 byte) die "+48" sorgen dafür das der Wert 0 oder 1 als Zeichen "0" oder "1" übertragen werden. 
    //das vereinfacht die verarbeitung in php.
    client.write(returnvalue+48);
    
    //übertragung erzwingen
    client.flush();
    
    //10ms delay damit daten sicher gesendet werden.
    delay(10);
    
    //wichtg! client verbindung beenden
    client.stop();
  }
}

Und die PHP-Ausgabe:

<?php

$arduino_ip = "192.168.178.216";
$arduino_port = "23";

function arduino_send($ip,$port,$command) {
    $res = fsockopen($ip,$port);
    if($res) {
        fwrite($res,$command);
        $ret =fread($res,1);
        //echo "Ventil = $ret 
\n";
        //echo "1 = Zu | 0 = Auf 
\n";
        return $ret;
    } else {
        echo "Fehler, kein kontakt.
Kommando konnte nicht abgesetzt werden";
    }
}

  $V1 = arduino_send($arduino_ip,$arduino_port,"U".chr(4).chr(1).chr($V1));
  echo $V1;
?>

Ich hoffe Ihr versteht was ich meine :blush:
Im Serial Monitor sieht es so aus wie es eigentlich auch auf der Website zu sehen sein soll:

T Kommando empfangen, Timer 0 gestartet
T Kommando empfangen, setze Pin 4 auf 0
T Kommando empfangen, setze Time auf 900 sec 
U Kommando empfangen, lese Timer 0 Wert = 900

Und auf der Website wird nur die 9 von der 900 angezeigt.

Vielen Dank...

Moin,
keiner eine Idee, warum nur die erste Zahl (bsp. 9) angezeigt wird und nicht die ganze (bsp. 900) ?
Vieleicht kann ja nochma einer drauf schauen, wäre echt super :grin:

Guten Morgen,

mal abgesehen davon, das Du in Deinem Sketch immer Timer 0 verwendest, da die Variable "TI" nie einen übergebenen Wert zugewiesen bekommt, ist die Lösung Deines Problems vermutlich recht einfach:

$ret =fread($res,10);

Damit werden im PHP Code 10 Zeichen aus der Verbindung gelesen und nicht nur 1 wie vorher.
Das mit den verschiedenen Timern solltest Du Dir aber nochmal ansehen.
Mario.

Hi, einfach eine kurze Zwischenfrage:
Um die verbindung zwischen Arduino und Pc herzustellen nutzt ihr da einen lokalen Webserver (wie z.B. Apache bei XAMPP) oder führt ihr die php-Dateien einfach von einem beliebigen Ordner im Browser (ja muss beim lokalen Webserver auch) aus?

mfg
Balli

Bei Cetax ist der Webserver glaub ich ein Synology-NAS, das auch PHP (+ Webserver) spricht. So wie die Umsetzung in diesem Projekt ist, braucht es zwingend einen Webserver. Wo der läuft ist erstmal egal, das kann auch ein lokaler XAMPP sein. Bei mir ist es ein MacMini Server :slight_smile:

Hallo,
ja ist richtig, ich habe hier eine Synologie DS111 wo der Apache drauf läuft.
@Mario, nochmal Danke, hätte man auch selber drauf kommen müssen :roll_eyes:
Ich habe am falschen Ende probiert, ich dachte es müste noch ein byte mehr übertragen werden, damit beide Zahlen angezeigt werden. :blush:
Aber so ist das mit "halbwissen".... lol

Aber jetzt gehts, Timer werden übergeben und die Restzeit für jedes Ventil (Ausgang) angezeigt.

Damit ist dieses Projekt hier erstmal zu Ende. Ich werde den Code so schnell wie möglich nach schieben, aber mit dem Kommentieren hapert es noch :relaxed:

Also vielen vielen Dank für die tolle und sehr Hilfreiche unterstützung an mkl0815 !!!
Ich kann sagen ich habe vieles gelernt und einiges werde ich wohl nie lernen... :wink:
DANKE !!!