Datenlogger zur Maschinendatenerfassung

Mahlzeit :slight_smile:

ich stelle mich erst einmal vor: Ich bin gelernter Elektroniker und Instandhalter für Werkzeugmaschinen. Ich bin meistens in der S7-Welt unterwegs und habe vorher wenig mit uC oder Arduinos zutun gehabt.

Für ein Projekt soll ich eine Test-Datenerfassung für die Laufzeiten von Maschinen erstellen.

Ich verwende einen ATMega mit dem Ethernet-Shield WIZ5100.

Nun zum Problem:

Im Grunde funktioniert mein Sketch ganz gut, auf die Eingänge des Arduinos sollen Ausgänge der Maschine gehen, die die jeweiligen Zustände signalisieren. Danach zählt der Arduino Sekundenweise die jeweilige Variable hoch und stellt sie via Ethernet bereit.

Dumm ist nur, das wenn mehr als ein Eingang HIGH ist sich der Arduino aufgrund meines Sketches verzählt, da ich mit delay-Befehlen arbeite.

Gibt es hier eine elegantere Lösung?

MDE_WebServer_ATMega25052019.ino (12.7 KB)

millis() siehe Nachtwächtererklährung hier im Forum

Bitte zeige Deinen Sketch in Code Tags </>, damit er direkt lesbar ist. Es darf auch gerne eine kleinere Version sein, die den Fehler zeigt (z.B. ohne Ethernet aber Debug-Meldungen über Serial), aber compilieren sollte sie schon.

Hi

millis() wurde ja schon genannt - Du merkst Dir einfach, WANN der letzte Pegelwechsel bei dem Pin war.
Ist Das >=1000ms her, zählst Du 1 hoch und schiebst die gemerkte Zeit um 1000ms nach Hinten.

Das machst Du mit allen Pins - also jeweils!
Du brauchst also pro Pin den letzten Status und die letzte Zeit.
Wenn Sekundengenau reicht, sollte Das ungefähr so gehen:

pins[]={2,3,4,5}
elemente=sizeof(pins)/sizeof(pins[0]);  //die Anzahl der angegebenen Pins ermitteln
uint32_t lastchange[elemente];            //letzte Änderung des Pin
boolean laststatus[elemente];               //letzter Pegel
uint32_t ticks[elemente];                      //hochgezählte volle 'Sekunden'
const uint16_t wartezeit=1000;            //Wartezeit zum Hochzählen in ms für einen 'tick'

void setup(){
   for (byte x=0;x<elemente;x++){
      lastchange[x]=millis();
      laststatus[x]=digitalRead(pins[x]);
   }
}

void loop(){
   for (byte x=0;x<elemente;x++){
      if (digitalRead(pins[x])!=laststatus[x]){
         laststatus[x]=!laststatus[x];   //Status anpassen
         lastchange[x]=millis();           //und 'Uhrzeit' mithalten
      }
      if (laststatus[x]){                       //nur, wenn der Pin HIGH ist)
         if (millis()-lastchange[x]>=wartezeit){
            ticks[x]++;                          //und die Wartezeit vergangen ist 
            lastchange[x]+=wartezeit;
         }
      }
   }
   //Hier die Daten 'alle Jubeljahr' verschicken
}

MfG

Dumm ist nur, das wenn mehr als ein Eingang HIGH ist sich der Arduino aufgrund meines Sketches verzählt, da ich mit delay-Befehlen arbeite.

Gibt es hier eine elegantere Lösung?

Ja, es geht viel eleganter!
Viel viel...

Aber hier eine quick'n dirty Lösung:

  1. Alle vorhandenen delay(1000) ersatzlos streichen
  2. Nur ein delay(1000) ganz an den Anfang von loop().

Hallo,

bei den durchnummerierten Listen sticht eine Struktur ins Auge die man mittels angelegten Array durchlaufen lässt.

Bsp. für deine Eingänge einlesen.

const byte SIZE = 16;

struct Daten {
  byte pin;
  byte s;
  byte m;
  unsigned long h;
};

Daten mde[SIZE];


void setup()  {
  Serial.begin(9600);

  // Datenstruktur füllen und Eingänge konfigurieren
  byte pinStart = 22;
  for (byte i=0; i<SIZE; i++)
  {
    mde[i].pin = pinStart;        // Pinkonfig 22-37
    pinMode(mde[i].pin, INPUT);
    pinStart++;
  }
}

void loop() {

  read_Inputs();

}


void read_Inputs ()
{
  for (byte i=0; i<SIZE; i++)
  {
    if (digitalRead(mde[i].pin)) {
      mde[i].s++;

      if (mde[i].s > 59) {
        mde[i].s = 0;
        mde[i].m++;
      }
      
      if (mde[i].m > 59) {
        mde[i].m = 0;
        mde[i].h++;
      }
    }
  }
}

Das gleiche machst du mit deiner Ethernet Geschichte und rufst die Funktionen mittels millis aller x ms auf. Fertig.

Theseus erklärt millis()
http://forum.arduino.cc/index.php?topic=400102.msg2752141#msg2752141

GuntherB - BlinkwithoutDelay - Die Nachtwächtererklärung
http://forum.arduino.cc/index.php?topic=423688.0

bei den durchnummerierten Listen sticht eine Struktur ins Auge

So ist es!

(ich hasse Codeduplikate und ALLE durchnummerierten Dinger)

Da ich gerade kein Ethernet zum testen aktiv habe, kann man ein p auf der SeriellenKonsole eingeben um sich die ganzen MDE Dinge im Monitor anzeigen zu lassen

Alle Duplikate entfernt.
Alle überflüssigen Delay entfernt.
Alle Durchnummeriereungen entfernt
Ansonsten, recht unverändert

//#include <SD.h>
#include <SPI.h>
#include <Ethernet.h>
#include <Streaming.h> // die Lib findest du schon ... ;-)



class Logport : public Printable
{
  private: 
   const byte pin;
   unsigned long laufzeit;

  public:
   Logport(const byte pin):pin(pin),laufzeit(0){}

   void begin()
   {
     pinMode(pin,OUTPUT);
   }

   void log()
   {
     laufzeit += digitalRead(pin);
   }

   size_t printTo(Print &p) const
   {
      size_t len = 0;
      len += p.print(" Laufzeit ist ");
      len += p.print(laufzeit / (60 * 60));
      len += p.print("h ");
      len += p.print(laufzeit / 60 % 60);
      len += p.print("m ");
      len += p.print(laufzeit % 60);
      len += p.print("s");
      return len;
  }
};


Logport logports[] {21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,};

byte mac[] = {
  0xDE, 0xAD, 0x0E, 0xEF, 0xFE, 0xFF
};
IPAddress ip(192, 168, 0, 177);
EthernetServer server(80);




void setup() 
{
 
  Ethernet.init(10);

  for(Logport &port:logports) port.begin();



  Serial.begin(9600);
  while (!Serial) {
    ; 
  }
  Serial.println("MDE Webserver");
  Ethernet.begin(mac, ip);


  if (Ethernet.hardwareStatus() == EthernetNoHardware) {
    Serial.println("Ethernet shield was not found.  Sorry, can't run without hardware. :(");
    while (true) {
      delay(1); // do nothing, no point running without Ethernet hardware
    }
  }
  if (Ethernet.linkStatus() == LinkOFF) {
    Serial.println("Ethernet cable is not connected.");
  }

  
  server.begin();

  Serial.print("server is at ");
  Serial.println(Ethernet.localIP());
}

void serialEvent()
{
  int in = Serial.read();
  int portnum = 1;
  if(in == 'p')
  {
    for(Logport &port:logports) Serial << "MDE" << portnum++ << port << endl;
    Serial << endl;
  }
}


void loop() 
{
  static unsigned long timestamp = 0;
  if(millis() - timestamp >= 1000)
  {
    timestamp += 1000;
    for(Logport &port:logports) port.log();
  }
  

  
  


      
  EthernetClient client = server.available();
  if (client) {
    Serial.println("new client");
    // an http request ends with a blank line
    boolean currentLineIsBlank = true;
    while (client.connected()) {
      if (client.available()) {
        char c = client.read();
        Serial.write(c);
        // if you've gotten to the end of the line (received a newline
        // character) and the line is blank, the http request has ended,
        // so you can send a reply
        if (c == '\n' && currentLineIsBlank) {
          // send a standard http response header
          client.println("HTTP/1.1 200 OK");
          client.println("Content-Type: text/html");
          client.println("Connection: close");  // the connection will be closed after completion of the response
          client.println("Refresh: 5");  // refresh the page automatically every 5 sec
          client.println();
          client.println("<!DOCTYPE HTML>");
          client.println("<html>");

          int portnum = 1;
          for(Logport &port:logports) client << "MDE" << portnum++ << port << "
 
" << endl;
     
          client << "</html>" << endl;

        }
        if (c == '\n') {
          // you're starting a new line
          currentLineIsBlank = true;
        } else if (c != '\r') {
          // you've gotten a character on the current line
          currentLineIsBlank = false;
        }
      }
    }
    // give the web browser time to receive the data
    delay(1);
    // close the connection:
    client.stop();
    Serial.println("client disconnected");
  }
}

Wenn du in der S7 Welt zuhause bis (sowie meiner einer) dann solltest du (wenn die original Anlage auch ne S7 hat). Mal hier auf die Seite gehen >>Klick<< . Danmit kanst du diekt an deine S7 mit PN gehen und die Daten direkt abgreifen. Solltest du eine S7-1200 V4 und größer haben dann muss PUT/GET von extern aktiviert werden im Reiter Security bei den CPU Eigenschaften. Das gleiche gibt es auch bei den 1500er ich kann aber nicht sagen ab welcher Firmware PUT/GET von Haus aus deaktiviert ist.
Ich habe es erfolgreich mit ner S7-1211C und nem Arduino MEGA mit EthernetKarte gemacht und mit einem ESP8266 via WLAN

Gruß
DerDani

Sofern die CPUs nicht nur Profibus und MPI Anschlüsse haben, wer weiß wie alt die Anlagen sind. :wink:
Ich benutze aber auch Settimino um Daten von ein paar CPUs aus einem großen Verbundnetz zu ziehen. Das funktioniert wunderbar und bringt auch niemand in Schwierigkeiten solange man nur lesend auf die Daten zugreift.

Gruß

Danke für die vielen Antworten :slight_smile:

Ich probiere die vielen Lösungen nacheinander mal aus und schau mir das settimo mal an.

Geb dann Bescheid für welche dieser Lösungen ich mich entschieden habe :slight_smile:

Wenn ich die Lösung von Combie ausprobiere kommt diese Fehlermeldung:

Arduino: 1.8.9 (Windows Store 1.8.21.0) (Windows 10), Board: "Arduino/Genuino Mega or Mega 2560, ATmega2560 (Mega 2560)"

WARNUNG: Kategorie 'Language' in der Bibliothek ArduinoStreaming ist ungültig und wird auf 'Uncategorized' festgelegt
sketch_may26a:39:72: error: field 'logports' has incomplete type 'Logport [0]'

Logport logports[] {21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,};

^

C:\Users\lfroe\Documents\Arduino\sketch_may26a\sketch_may26a.ino:6:7: note: definition of 'class Logport' is not complete until the closing brace

class Logport : public Printable

^

sketch_may26a:44:14: error: expected identifier before numeric constant

IPAddress ip(192, 168, 0, 177);

^

sketch_may26a:44:14: error: expected ',' or '...' before numeric constant

sketch_may26a:45:23: error: expected identifier before numeric constant

EthernetServer server(80);

^

sketch_may26a:45:23: error: expected ',' or '...' before numeric constant

sketch_may26a:154:1: error: expected '}' at end of input

}

^

sketch_may26a:43:1: error: too many initializers for 'byte [0] {aka unsigned char [0]}'

};

^

C:\Users\lfroe\Documents\Arduino\sketch_may26a\sketch_may26a.ino: In member function 'void Logport::setup()':

sketch_may26a:64:25: error: invalid use of non-static member function

Ethernet.begin(mac, ip);

^

sketch_may26a:78:3: error: '((Logport*)this)->Logport::server' does not have class type

server.begin();

^

C:\Users\lfroe\Documents\Arduino\sketch_may26a\sketch_may26a.ino: In member function 'void Logport::loop()':

sketch_may26a:111:27: error: '((Logport*)this)->Logport::server' does not have class type

EthernetClient client = server.available();

^

C:\Users\lfroe\Documents\Arduino\sketch_may26a\sketch_may26a.ino: At global scope:

sketch_may26a:154:1: error: expected unqualified-id at end of input

}

^

exit status 1
field 'logports' has incomplete type 'Logport [0]'

Dieser Bericht wäre detaillierter, wenn die Option
"Ausführliche Ausgabe während der Kompilierung"
in Datei -> Voreinstellungen aktiviert wäre.

Fehlt da nicht die gescheifte Klammer?

Hallo,

eine Klammer am Ende der Klasse Logport ... vor der Abschließenden };
Danach bekomme ich nur noch 2 Warnungen der Ethernet Lib.

Doc_Arduino:
Hallo,

eine Klammer am Ende der Klasse Logport ... vor der Abschließenden };
Danach bekomme ich nur noch 2 Warnungen der Ethernet Lib.

  1. Ist korrigiert (ka, wie der Fehler entstanden ist)
  2. Die Lib ist noch nicht für C++17 vorbereitet

Hallo,

ist mir auch schon einmal passiert, einmal zu viel copy & paste und schon :slight_smile:
Bei mir macht die Seite hier auch unnötig und selbstständig zusätzliche Leerzeilen rein. Nicht im Code, im Text.

Wegen den Warnungen. Liegt das daran das jede neuere Compilerversion den Code strenger prüfen kann?
Also den Syntax besser lesen kann?

switch() wurde verändert.
Im Zuge dessen wurden die Meldungen verschärft.

Abend :slight_smile:

Nochmals ein Dankeschön für die Mühe die ihr euch hier alle macht, hab noch große Schwierigkeiten mich in der Programmiersprache zurecht zu finden, deshalb dauert es immer ein bissel ehe ich antworte;). Ich teste gerade eine Mischung aus der Lösung von Doc_arduino und postmaster-ino. Muss ich nun den ethernet-Teil in den void loop() schreiben in etwa so:

client.print(mde*.s);*

  • client.print("h ");*
    _ client.print(mde*.m);_
    _
    client.print("m ");_
    _ client.print(mde.h);
    client.print("s ");*_

* client.println("*
");
* client.println("*
");
oder muss ich hierfür einen "extra"-void machen, z.B. void ethernet.
Eine Frage noch an combie, dein sketch ist mittlerweile hochladbar, jedoch bekomme ich nichts angezeigt, wenn ich im Browser die ip eintippe, es lädt lediglich endlos. Wenn ich den Programmteil richtig intepretiere sollten da doch die einzelnen Logports stehen oder?
Und zu meiner letzten Frage, die geht an Scherheinz und/oder volvodani:
Ist es möglich zwei ethernet-shields auf einem arduino zu betreiben? Wir haben leider kein sehr stabiles WLAN im Haus und wenn die Schweißerei anfängt Plasma zu brennen ist es mitn Empfang eh vorbei :wink:

Eine Frage noch an combie, dein sketch ist mittlerweile hochladbar, jedoch bekomme ich nichts angezeigt, wenn ich im Browser die ip eintippe, es lädt lediglich endlos.

Ich bin mir recht sicher, dass ich den Ethernet Teil komplett unverändert gelassen habe, nur die Ausgabe der Werte verändert.
Wenn da also jetzt was falsch ist, würde ich sagen, war das vorher auch schon falsch.
Naja... kann es halt nicht testen....

Mahlzeit,

ich wollte den Post mal wiederbeleben und erstmal anmerken, dass die Lösung mit den millis() super geklappt hat, läuft jetzt schon seit 3 Monaten stabil in der Maschine, greife auf in der PLC programmierte Ausgänge über Optokoppler ab und schicke dann die Ergebnisse über eine Webserver ins Intranet.

Will jetzt gerne den nächsten Schritt wagen und mich über Settimino an die Sache heranwagen.
Hierzu erstmal eine grundsätzliche Frage:
ist es möglich eine Arduino YUN Rev.2 mit einem zusätzlichen Ethernet-Shield auszustatten, sodass ich über den YUN die Verbindung zum Intranet sicherstelle und über das Ethernet-Shield die Verbindung ins Maschinennetz realisiere?

Hat hier jemand schonmal Erfahrungen gemacht?

Eine Frage an Scherheinz bzw. volvodani. Ihr habt ja bereits angemerkt, dass ihr mit Settimino erfolgreich arbeitet. Loggt ihr in eueren Projekten Daten aus der PLC auf Karte oder übertragt ihr diese auch einen Webserver?

Ich nutze Settmino "nur" um mir meine eigenen Remote IOs anzusteuern. Also der ESP8266 holt sich die Daten aus einem bestimmten DB und setzt entsprechend die Ausgänge. Ein paar Daten wie Temperatur kommen auch wieder zurück zu 1200er. Webserver habe ich bis jetzt noch nicht gemacht in Verbindung mit Settmino.
Gruß
DerDani