TCP/IP Server für Scada

Hallo,
Ich möchte Daten von Sensoren(Temperatur, Luftfeuchtigkeit und CO2 Gehalt) an einer Scada Software schicken(konkret: ExoMachine Scada Expert). Dafür habe ich schon einen Webserver aufgesetzt um mich mit dem Ethernetshield vertraut zu machen. Das hat alles wunderbar funktioniert.
Jetzt nimmt das Scada Programm am liebsten TCP/IP als Datenverbindung. Es wird jede Sekunde vom Client eine Anfrage geschickt um die 3 Messwerte zu bekommen. Dort kann ich auch die IP-Adresse einfach angeben.
Meine Fragen: Wie kann ich eine TCP/IP Server aufsetzten? Ist das ähnlich zum Webserver? Brauche ich nur den Befehl Ethernet.write()?
Momentan kann der Client keine Verbindung aufbauen.

Hier mein Code(ohne Webserver):
Für den Webserver habe ich einfach die drei Zeilen von Ethernet.write(Messwert) geändert.

#include <Adafruit_Sensor.h>   //hum
#include <DHT.h>    //hum
#include <DHT_U.h>  //hum
#include <Wire.h>   //temp
#include <LiquidCrystal_I2C.h>  //lc
#include <SPI.h>    //Webserver
#include <Ethernet.h>   //Webserver

#define address 0x4F //temp

#define DHTPIN 2    // Digital pin connected to the DHT sensor 
#define DHTTYPE DHT11 //sensor type
DHT_Unified dht(DHTPIN, DHTTYPE);

int SensorPin = 6; // Der PWM-Pin des Sensors wird an Pin6 des Mikrocontrollers angeschlossen
int Messbereich = 5000; // Der voreingestellte Messbereich (0-5000ppm)
unsigned long ZeitMikrosekunden; // Variable für die Dauer des PWM-Signalpegels in Mikrosenkunden
unsigned long ZeitMillisekunden; // Variable für die Dauer des PWM-Signalpegels in Millisekunden
int PPM = 0; // Variable für den CO2-Messwert in ppm (parts per million - Anteile pro Million)
float Prozent=0; // Variable für den prozentuale Länge des PWM-Signals
int ppmmitte[5];
int ppmmittelwert = 0;
float tempmitte[5];
float tempmittelwert = 0;
float hummitte[5];
float hummittelwert = 0;
float humwert = 0;
int cmitte;

uint32_t delayMS;

int ampelRotPin = 9;
int ampelGelbPin = 8;
int ampelGruenPin = 7;

LiquidCrystal_I2C lcd(0x27, 16, 2); //16 Zeichen in 2 Zeilen und der HEX-Adresse 0x27

byte mac[] = {
  0xA8, 0x61, 0x0A, 0xAE, 0x76, 0x76
};
IPAddress ip(172, 30, 254, 5 );
IPAddress gateway(172, 30, 254, 1);
// Initialize the Ethernet server library
// with the IP address and port you want to use
// (port 80 is default for HTTP):
EthernetServer server(80);


//einmalig ausgeführte SetUp Befehle
void setup()
{
 //Starten der seriellen Übertragung
 Serial.begin(9600);
 Serial.println("Starting...");
 dht.begin();
 Wire.begin();

 lcd.init(); //Im Setup wird der LCD gestartet 
 lcd.backlight(); //Hintergrundbeleuchtung einschalten (lcd.noBacklight(); schaltet die Beleuchtung aus)
 byte Celsius[8] = {B11100,B10100,B11100,B0000,B00000,B00000,B00000,B00000};    //Grad Anzeige selber erstellt
 lcd.createChar(0, Celsius);
 lcd.setCursor(0, 0);
 lcd.print("Hum ");
 lcd.setCursor(4, 0);
 lcd.print("Temp ");
 lcd.setCursor(12, 0);
 lcd.print("CO2");

 pinMode(SensorPin, INPUT); //Der Pin für die Sensorwerte (6) wird als Eingang definiert.

 pinMode(ampelRotPin, OUTPUT);
 pinMode(ampelGelbPin, OUTPUT);
 pinMode(ampelGruenPin, OUTPUT);

 while (!Serial) {
    ; // wait for serial port to connect. Needed for native USB port only
  }
  Serial.println("Ethernet WebServer Example");

  // start the Ethernet connection and the server:
  Ethernet.setGatewayIP(gateway);
  Ethernet.begin(mac, ip);

  // Check for Ethernet hardware present
  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.");
  }

  // start the server
  server.begin();
  Serial.print("server is at ");
  Serial.println(Ethernet.localIP());
}


void loop(){
   delay(1000);   //pause
 
    sensors_event_t event;   //Auslesen der Luftfeuchtigkeit
    dht.humidity().getEvent(&event);    // Get humidity event and print its value.
    if (isnan(event.relative_humidity)) {
      Serial.println(F("Error reading humidity!"));
    } else {
       humwert = event.relative_humidity;
       humwert += 17;
 }

  ZeitMikrosekunden = pulseIn(SensorPin, HIGH, 2000000); // Der pulseIn Befehl misst die Zeit, ab der ein Signal am angegebenen Pin auf HIGH wechselt und in diesem Zustand verbleibt. Standartmäßig endet diese Messung nach maximal 1.000.000 Mikrosekunden (1000ms). Durch das Ahängen des letzten Wertes kann man diesen sogenannten "Timeout" verlängern. Da das Signal des CO2 Sensors bis zu 1004ms lang sein kann, müssen wir den Wert entsprechend hoch ansetzen.
  ZeitMillisekunden = ZeitMikrosekunden/1000; // Umwandeln der Zeiteinheit von Mikrosekunden in Millisekunden.
  float Prozent = ZeitMillisekunden / 1004.0; // Die maximale Länge des PWM-Signals ist laut Datenblatt des MH-Z19B 1004ms (Millisekunden) lang. Daher berechnen wir hier die gemessene PWM-Signaldauer durch die maximal mögliche Signaldauer und erhalten einen Prozentwert des aktiven (5V) Pegels im PWM-Signal. Dieser Prozentwert spiegelt einen PPM-Wert zwischen 0PPM und 5000PPM wieder.
  PPM = Messbereich * Prozent; // PPM-Wert berechnen aus der prozentualen Signaldauer und dem maximalen Messbereich.

  Wire.beginTransmission(address);
  Wire.write(0x00);
  Wire.requestFrom(address, 2);

  float temperature = 0;
  if (Wire.available()) {
  
    int a = Wire.read();
    int b = Wire.read();
    float temp = a;
    temp=temp+((float)b/1024.0);
    temperature = temp;  
  }
  Wire.endTransmission();

  mittelwert(PPM, humwert, temperature); //Mittelwertberechnung siehe unten



  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
          Ethernet.write(ppmmittelwert);
          Ethernet.write(hummittelwert);
          Ethernet.write(tempmittelwert);
          break;
        }
        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(5);
    // close the connection:
    client.stop();
    Serial.println("client disconnected");
  }
}

//Von 5 Werten wird der Durchschnitt berechnet und erst nach 5 Sekunden ausgegeben
//Sinn: Ausschläge von Messwerten werden abgeflacht
void mittelwert(int ppm, float hum, float temp){
  cmitte += 1;
  switch (cmitte){        //Auch mit einem if-else realisierbar
    case 1:
      ppmmitte[0] = ppm;
      tempmitte[0] = temp;
      hummitte[0] = hum;
    break;
    case 2:
      ppmmitte[1] = ppm;
      tempmitte[1] = temp;
      hummitte[1] = hum;
    break;
    case 3:
      ppmmitte[2] = ppm;
      tempmitte[2] = temp;
      hummitte[2] = hum;
    break;
    case 4:
      ppmmitte[3] = ppm;
      tempmitte[3] = temp;
      hummitte[3] = hum;
    break;
    case 5:
      ppmmittelwert = 0;
      tempmittelwert = 0;
      hummittelwert = 0;
      ppmmitte[4] = ppm;
      tempmitte[4] = temp;
      hummitte[4] = hum;
      for(int i = 0; i<5; i++){
        ppmmittelwert += ppmmitte[i];
        tempmittelwert += tempmitte[i];
        hummittelwert += hummitte[i];
      }
      ppmmittelwert /= (cmitte);
      tempmittelwert /= (cmitte);
      hummittelwert /= (cmitte);
      cmitte=0;
      display(ppmmittelwert, hummittelwert, tempmittelwert);
    break;
  }
}

void display(int ppmw, float humw, float tempw){
  Serial.print("CO2 PPM: "); // Ausgabe der Werte über den Seriellen Monitor
  Serial.println(ppmw);
  lcd.setCursor(12, 1); //Ausgabe der Werte über das LCD Display
  lcd.print(ppmw);

  Serial.print(F("Humidity: "));
  Serial.print(humw, 0);
  Serial.println(F("%"));
  lcd.setCursor(0, 1);
  lcd.print(humw, 0);
  lcd.setCursor(2, 1);
  lcd.print("%");
 
  Serial.print("Temp: ");  
  Serial.print(tempw);  
  Serial.print('\n');
  lcd.setCursor(4, 1);
  lcd.print(tempw);
  lcd.setCursor(9, 1);
  lcd.write((uint8_t)0);
  lcd.setCursor(10, 1);
  lcd.print(F("C "));

  if(ppmw<700 && ppmw>0){   //Ampelphasen 1
    digitalWrite(ampelRotPin, LOW);
    digitalWrite(ampelGelbPin, LOW);
    digitalWrite(ampelGruenPin, HIGH);
    lcd.setCursor(15, 1);
    lcd.print(" ");   //Damit die Null von den TausendernPPM nicht stehen bleibt
  } else if(ppmw>701 && ppmw<1000){ //Ampelphasen 2
    digitalWrite(ampelRotPin, LOW);
    digitalWrite(ampelGelbPin, HIGH);
    digitalWrite(ampelGruenPin, HIGH);
    lcd.setCursor(15, 1);
    lcd.print(" ");   //Damit die Null von den TausendernPPM nicht stehen bleibt
  }else if(ppmw>1001 && ppmw<1400){   //Ampelphasen 3
    digitalWrite(ampelRotPin, LOW);
    digitalWrite(ampelGelbPin, HIGH);
    digitalWrite(ampelGruenPin, LOW);
  } else if(ppmw>1401){     //Ampelphasen 4
    digitalWrite(ampelRotPin, HIGH);
    digitalWrite(ampelGelbPin, LOW);
    digitalWrite(ampelGruenPin, LOW);
  }
}

Falls jemand noch mehr Infos haben möchte: Es ist eine CO2 Ampel mit lcd Display zur Aufzeichnung der Luftqualität im Büro. Jetzt soll es auf einen Großbildschirm noch schöner dargestellt werden

Schonmal danke im vorraus. Ich bin für jede Hilfe offen

Im englischen Teil des Forum müssen die Beiträge und Diskussionen in englischer Sprache verfasst werden. Deswegen wurde diese Diskussion in den deutschen Teil des Forums verschoben.

mfg ein Moderator.

Welche Hardware benutzt Du?
DHT11 ist sehr ungenau und kann keine CO2 Messung durchführen, da braust Du anderen Sensor zB. MQ-135, MH-Z19B oder Arduino freundlich MG 811.
Als Temp, Feuchtigkeit Sensor wurde empfehlen GY-21 HTU21.

Die eigentliche Frage ist:
Was codiert deine Scada-Software im TCP-Telegramm.

TCP ist erstmal nur das Transportprotokoll, beschreibt also den Mechanismus der Datenübetragung, aber nicht den Inhalt, die Semantik und den detaillierten Aufbau des zu übertragenden Inhalt.

Hast du hierzu Doku wie die Anfrage aussieht bzw. Wie die Antwort erwartet wird?

Um es kurz zu machen, meist wird TCP/IP dazu verwendet die Nutzdaten zu Kapseln, die Nutzdaten selbst sind meist ein höheres Protokoll wie zum Beispiel Modbus oder Http.

Diese höheren Protokolle stellen quasi die Linguistische Notation dar.

Grüsse Markus

Ich benutze den MH-Z19B für die CO2 Messung. Für die Luftfeuchtigkeit benutze ich den DHT11 und für die Temperatur ein i2c Ding von Allnet (hat keine konkreten Namen). Habe viele Sensoren ausprobiert und die waren mir am genausten -> Bin zufrieden mit denen.
Natürlich benutze ich dann noch das Ethernet Shield 2.

Hallo,
Du musst jetzt erst mal wissen was der Scada Client haben will und wie die Daten aussehen, ich kenne das System nicht, aber da lassen sich vermutlich auch gefühlte tausend Parameter einstellen. Welcher Port wird verwendet. Wie sind die Daten aufgebaut, usw. Ich könnte mir vorstellen das Du da erst mal etwas probieren musst. Such mal nach dem Windows Programm "Paketsender" , Damit kannst Du u.A. einen TCP Server-Betrieb "einstellen". Damit kannst Du das dann erst mal simulieren. Oder Du kannst auch eine client "einstellen" und dann damit deinen Arduino Sketch testen. Ich finde das Programm für sowas sehr hilfreich, weil man es erst mal nur mit einer Baustelle zu tun hat. Wenn Du ganz in die Tiefe gehen musst bleibt dir immer noch der Heifisch, den Du natürlich auch parallel zu dem Paketsender betreiben kannst.
Heinz

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.