TCP Verbindung mit Arduino YUN

Hallo
ich habe ein Arduino UNO mit dem ich über TCP Port 2500 eine Verbindung zu einer SPS aufgebaut habe . Das läuft auch soweit.
Jetzt habe ich versucht mit dem Yun ebenfals eine Verbindung aufzubauen über Port 2500. Die Funktioniert einfach nicht.

#include <Bridge.h>
#include <YunServer.h>
#include <YunClient.h>
#define PORT 2500

YunServer server(PORT);

void setup() {
  // Bridge startup
  pinMode(13, OUTPUT);
  digitalWrite(13, LOW);
  Bridge.begin();
  digitalWrite(13, HIGH);
  Serial.begin(9600);

  
  server.listenOnLocalhost();
  server.begin();
  Serial.print("Start");
}

void loop() {
  
  YunClient client = server.accept();
  
  
  if (client.connected()) {
        
      // Process request
          Serial.print("Verbunden");
          delay(2000);
      }
     else {
        Serial.print("0");
    
  }

  delay(300); // Poll every 50ms
}

Dies ist das Programm vom Arduino UNO womit es geht.

#include <SPI.h>
#include <Ethernet.h>
#include <Wire.h> 
#include <LiquidCrystal_I2C.h>
// LCD INT
LiquidCrystal_I2C lcd(0x3F,16,2);

// Ehernet Einstellung
  byte mac[] = { 
  0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
  IPAddress ip(192,168,178, 50);
  IPAddress gateway(192,168,178, 1);
  IPAddress subnet(255, 255, 255, 0);
  EthernetServer server(2500);

// Variable
  boolean alreadyConnected = false; 
  char thisChar[33];
  char thisChar1[16];
  char thisChar2[16];
  int i;

void setup() {
  
  lcd.init();
  lcd.backlight();
  lcd.setCursor(0,0);
  lcd.print("Starten");
  lcd.setCursor(0,1);
  lcd.print("System");
  delay( 3000);
  lcd.clear();
  
  
  // initialize the ethernet device
  Ethernet.begin(mac, ip, gateway, subnet);
  // start listening for clients
  server.begin();
 // Open serial communications and wait for port to open:
  Serial.begin(9600);
   while (!Serial) {
    ; // wait for serial port to connect. Needed for Leonardo only
  }


  Serial.print("Chat server address:");
  Serial.println(Ethernet.localIP());
  lcd.setCursor(0,0);
  lcd.print("warten auf ");
  lcd.setCursor(0,1);
  lcd.print("Verbindung"); 
}
//*********************************Hautprogram*************************************************
void loop() {
  // wait for a new client:
  EthernetClient client = server.available();
  
  

  // when the client sends the first byte, say hello:
  if (client) {
    if (!alreadyConnected) {
      // clead out the input buffer:
      client.flush();    
      Serial.println("We have a new client");
      //client.println(Verbindung);
      //server.write(Verbindung); 
      alreadyConnected = true;
      lcd.clear();
      lcd.setCursor(0,0);
      lcd.print("  Verbunden  ");
      delay( 5000);
      lcd.clear();
            
      
    } 
    //  Daten über Ehernet empfangen und in String schreiben 
    if (client.available() > 0) {
       for (i = 0; i < 32; i++) {
       // read the bytes incoming from the client:
       thisChar[i] = client.read();
      }
     // Backlight aus  
       if (strstr (thisChar, "noBacklight") ){
          lcd.noBacklight();
          Serial.println("noBacklight");
          delay(500);}
          else{
          lcd.backlight();
      }
      
      // echo the bytes back to the client:
      server.write(thisChar);
      // echo the bytes to the server as well:
      Serial.write(thisChar);
      // Zeichen für LCD Zeile 1 aus SendeString filtern
      for (i =0;i <17;  i++){
        thisChar1[i]=thisChar[i];
            
      }
           
     // Zeichen für LCD Zeile 2 aus SendeString filtern 
      for (i =17;i <33;  i++){
        thisChar2[i-16]=thisChar[i];
      }
    // Zeichenkette Zeile1 und Zeile 2 auf Display schreiben  
      lcd.setCursor(0,0);
      lcd.print(thisChar1);
      lcd.setCursor(0,1);
      lcd.print(thisChar2);
      
      delay( 1);
    }
    
  }
   
    
  
}

Versuch es mal ohne diese Zeile:

  server.listenOnLocalhost();

Nach meinem Verständnis wird damit der Server auf die Localhost-Adresse beschränkt (127.0.0.1), somit musst Du auf dem Yun (dem Linux-Teil davon) eingeloggt sein und den Zugriff von dort aus machen. Ohne diese Beschränkung solltest Du auch von aussen zugreifen können.

Hallo
das Verbindungsaufbau zum Yun hat Funktioniert. Allerdings empfange ich nur beim Verbindungsaufbau 1xDaten.
Das ech erhalte ich nicht zurück zur SPS

Ich Posten mal mein Programm. Mit einem Arduino Uno ethernet shield funktioniert das Programm obere Programm
Ich habe ein Paar sachen abgeändert müssen

#include <SPI.h>
#include <Wire.h> 
#include <LiquidCrystal_I2C.h>
#include <Bridge.h>
#include <YunServer.h>
#include <YunClient.h>
#define PORT 2500

// LCD INT
LiquidCrystal_I2C lcd(0x3F,16,2);
 YunServer server(PORT);


// Variable
  boolean alreadyConnected = false; 
  char thisChar[33];
  char thisChar1[16];
  char thisChar2[16];
  int i;

void setup() {
  
  lcd.init();
  lcd.backlight();
  lcd.setCursor(0,0);
  lcd.print("Starten");
  lcd.setCursor(0,1);
  lcd.print("System");
  delay( 3000);
  lcd.clear();
  
 
  // initialize the ethernet device
  Bridge.begin();
  // start listening for clients
  server.begin();
 // Open serial communications and wait for port to open:
  Serial.begin(9600);
   while (!Serial) {
    ; // wait for serial port to connect. Needed for Leonardo only
  }


  Serial.print("Chat server address:");
  
  lcd.setCursor(0,0);
  lcd.print("warten auf ");
  lcd.setCursor(0,1);
  lcd.print("Verbindung"); 
}
//*********************************Hautprogram*************************************************
void loop() {
  // wait for a new client:
  YunClient client = server.accept();
  
  

  // when the client sends the first byte, say hello:
  if (client) {
    if (!alreadyConnected) {
      // clead out the input buffer:
      client.flush();    
      Serial.println("We have a new client");
      //client.println(Verbindung);
      //server.write(Verbindung); 
      alreadyConnected = true;
      lcd.clear();
      lcd.setCursor(0,0);
      lcd.print("  Verbunden  ");
      delay( 5000);
      lcd.clear();
            
      
    } 
    //  Daten über Ehernet empfangen und in String schreiben 
    if (client.available() > 0) {
       for (i = 0; i < 32; i++) {
       // read the bytes incoming from the client:
       thisChar[i] = client.read();
      }
     // Backlight aus  
       if (strstr (thisChar, "noBacklight") ){
          lcd.noBacklight();
          Serial.println("noBacklight");
          delay(500);}
          else{
          lcd.backlight();
      }
      
      // echo the bytes back to the client:
      server.print(thisChar);
      // echo the bytes to the server as well:
      Serial.write(thisChar);
      // Zeichen für LCD Zeile 1 aus SendeString filtern
      for (i =0;i <17;  i++){
        thisChar1[i]=thisChar[i];
            
      }
           
     // Zeichen für LCD Zeile 2 aus SendeString filtern 
      for (i =17;i <33;  i++){
        thisChar2[i-16]=thisChar[i];
      }
    // Zeichenkette Zeile1 und Zeile 2 auf Display schreiben  
      lcd.setCursor(0,0);
      lcd.print(thisChar1);
      lcd.setCursor(0,1);
      lcd.print(thisChar2);
      
      delay( 1);
    }
    
  }
   
    
  
}

was kann das sein das es mit einem Uno geht und mit dem Yun nicht ?

Folgende Befehle gingen nicht
EthernetClient client = server.available();
server.write(thisChar);

was kann das sein das es mit einem Uno geht und mit dem Yun nicht ?

Ich glaube, Du hast nicht ganz verstanden, was ein Yun ist. Du hast Dein Programm mit einem UNO mit Ethernet-Shield erstellt und wolltest es mit möglichst wenig Aufwand auf den Yun portieren, was auch verständlich ist. Du musst Dir aber klar werden, was der Yun ist: ein kleiner Linux-Rechner mit WLAN-Anbindung, der mit einem Arduino Leonardo auf einer Platine gekoppelt ist.

Dein Programm für den UNO funktioniert auch nur, weil Du per Zufall gewissen Implementationsdetails (wahrscheinlich ohne es zu wissen) ausnutzt, eigentlich müsste es in dieser Form nicht funktionieren. Bei der Yun-Implementation tut es das dann auch nicht mehr, worüber Du dann erstaunt bist, obwohl es eigentlich umgekehrt sein müsste :-).

In jedem Durchlauf von loop() erzeugst Du eine neue Instanz der jeweiligen Client-Klasse. Beim Ethernet-Server wird diese intern für Dich verwaltet und jeweils die gleiche Kopie zurückgeliefert (zumindest das gleiche Socket-Objekt). Beim Yun wird gewartet bis eine neue Verbindung von aussen eintrifft und erst dann wird Dir ein neues Objekt zurückgeliefert.

Vielleicht würde es helfen, wenn Du uns etwas genauer erklärst, was Du eigentlich erreichen willst, welche Funktionalität Du genau erwartest, damit wir Dir bei der Implementierung helfen können.

Hallo danke erst mal für deine Antwort.
wie müsste deiner Meinung nach das Uno Programm ausshen ? Im Uno Programm erhalte ich über TCP Verbindung eine Zeichenkette (Text) von der SPS den ich auf einem LCD Display wiedergebe. Weiterhin gebe ich das echo an die SPS zurück. Ich habe das Programm nur zum Test für das Yun verwendet um die TCP verbindung zu Prüfen.

Ich erkläre mal kurz was ich mit dem Yun vorhabe.

ich möchte eine TCP Verbindung mit einer SPS zu dem Arduino aufbauen. Die SPS ist er Server und baut die Verbindung auf. Das Arduino ist der Client.

Ich möchte Eingänge, Ausgänge und Analogwerte vom Arduino zur SPS Senden. Weiterhin möchte ich Ausgänge auf dem Arduino mit der SPS steuern. So wie mit Bridge über Webserver.
Hierzu hätte ich das Bridge Beispiel auf meine Bedürfnisse abgeändert. Mein Problem ist ein Verbindungsaufbau zur SPS und das das Arduino Yun die gesendeten Daten erhält.

Momentan funktioniert der Verbindungsaufbau aber er werden nur beim Aufbau 1x Daten gesendet.

Was müsste angepasst werden damit dies funktioniert ?

Hallo
weiß keiner eine Lösung hierzu ?

ich möchte eine TCP Verbindung mit einer SPS zu dem Arduino aufbauen. Die SPS ist er Server und baut die Verbindung auf. Das Arduino ist der Client.

Das widerspricht sich. Wenn die SPS der Server sein soll, dann muss der Arduino die Verbindung zur SPS aufbauen. Wenn die SPS eine Verbindung zum Arduino aufbaut, ist gemäss Definition der Arduino der Server.

Ich möchte Eingänge, Ausgänge und Analogwerte vom Arduino zur SPS Senden. Weiterhin möchte ich Ausgänge auf dem Arduino mit der SPS steuern. So wie mit Bridge über Webserver.

Das ist ja grundsätzlich möglich. Erlaubt es Deine SPS denn, dafür jedes Mal eine neue TCP-Verbindung aufzubauen? Das wäre nämlich langfristig die stabilste Lösung, wenn auch nicht umwerfend ressourcenschonend im Netzwerk.

Hierzu hätte ich das Bridge Beispiel auf meine Bedürfnisse abgeändert. Mein Problem ist ein Verbindungsaufbau zur SPS und das das Arduino Yun die gesendeten Daten erhält.

Nein, er kriegt sie nicht einmal, sondern einmal pro Verbindung. Wenn Du das nicht willst, darfst Du nicht bei jedem Durchlauf von loop() eine neue Verbindung abwarten bzw. ein neues Client-Objekt kreieren.

Was müsste angepasst werden damit dies funktioniert ?

Die einfachste Möglichkeit ist, dass Du die Zeile

  YunClient client = server.accept();

von loop() nach setup() verschiebst und die Deklaration global machst. Allerdings ist das nicht äusserst ausfalltolerant, denn ein kurzer Netwerk-Unterbruch würde einen manuellen Eingriff (Reset des Arduinos) erzwingen.

Da ich keine Ahnung habe, wie der Code auf SPS-Seite aussieht, bzw. welche Randbedingungen Du dort einhalten musst, kann ich Dir keine allgemeingültigen Ratschläge geben.

Wie gesagt: die stabilste Lösung erhälst Du, wenn Du für jeden Austausch von Daten von der SPS eine neue Verbindung auslöst. Dann musst Du auch nur wenig am Code (für den Yun) verändern. Allerdings wird Dir dann Dein UNO-Code nicht viel Freude bereiten, denn dort schliesst Du die Verbindungen nie und somit könnten sich Verbindungen ansammeln, das Ethernet-Shield kann aber nur 4 davon gleichzeitig bearbeiten.

Habs hin bekommen

#include <SPI.h>
#include <Wire.h> 
#include <Bridge.h>
#include <YunServer.h>
#include <YunClient.h>
#include <Console.h>
#include <String.h>
#define PORT 2500

 YunServer server(PORT);

String command;
 void setup() {
   
    // Bridge startup
  pinMode(13, OUTPUT);
  digitalWrite(13, LOW);
  Bridge.begin();
  digitalWrite(13, HIGH);
  // start the serial for debugging
   Serial.begin(9600);
   Bridge.begin();
   Console.begin();
   server.begin();
//   while (!Console);
   Console.println("setup");
   delay(2000);
   
 }
   
 //******************************************************************Hautprogramm********************************************************
 void loop()
 {
  // listen for incoming clients
  YunClient client = server.accept();
  Console.print("run");
  if (client) {
    Console.println("client");
    
    while (client.connected()) {
       // Console.println("Verbunden");
       // Serial.print ("Verbunden");
        
      if (client.available()) {
        //char c = client.read();
        
        process(client);
        String command = client.readString(); 
        //Console.println(command);    
        client.write((uint8_t*)&command[0], command.length());
        
        
      }
    }
    // give the Client time to receive the data
    delay(10);
    // close the connection:
    client.stop();
  }
 }
 
 
 //**************************************************************Funktionen**********************************************************
 
 void process(YunClient client) {
  // read the command
  String command = client.readStringUntil('/');
  Console.println(command);
  Serial.print(command);
  // is "digital" command?
  if (command == "digital") {
  //  if (strstr (command, "digital") ){
    digitalCommand(client);
    Console.println(command);
    Serial.print(command);
  }

  // is "analog" command?
  if (command == "analog") {
    analogCommand(client);
  }

  // is "mode" command?
  if (command == "mode") {
    modeCommand(client);
  }
}

void digitalCommand(YunClient client) {
  int pin, value;

  // Read pin number
  pin = client.parseInt();
  Console.println(pin);
  // If the next character is a '/' it means we have an URL
  // with a value like: "/digital/13/1"
  if (client.read() == '/') {
    value = client.parseInt();
    digitalWrite(pin, value);
  }
  else {
    value = digitalRead(pin);
  }

  // Send feedback to client
  //client.print(F("Pin D"));
  //client.print(pin);
  //client.print(F(" set to "));
  //client.println(value);

  // Update datastore key with the current pin value
  String key = "D";
  key += pin;
  Bridge.put(key, String(value));
}

void analogCommand(YunClient client) {
  int pin, value;

  // Read pin number
  pin = client.parseInt();

  // If the next character is a '/' it means we have an URL
  // with a value like: "/analog/5/120"
  if (client.read() == '/') {
    // Read value and execute command
    value = client.parseInt();
    analogWrite(pin, value);

    // Send feedback to client
    client.print(F("Pin D"));
    client.print(pin);
    client.print(F(" set to analog "));
    client.println(value);

    // Update datastore key with the current pin value
    String key = "D";
    key += pin;
    Bridge.put(key, String(value));
  }
  else {
    // Read analog pin
    value = analogRead(pin);

    // Send feedback to client
    client.print(F("Pin A"));
    client.print(pin);
    client.print(F(" reads analog "));
    client.println(value);

    // Update datastore key with the current pin value
    String key = "A";
    key += pin;
    Bridge.put(key, String(value));
  }
}

void modeCommand(YunClient client) {
  int pin;

  // Read pin number
  pin = client.parseInt();

  // If the next character is not a '/' we have a malformed URL
  if (client.read() != '/') {
    client.println(F("error"));
    return;
  }

  String mode = client.readStringUntil('\r');

  if (mode == "input") {
    pinMode(pin, INPUT);
    // Send feedback to client
    client.print(F("Pin D"));
    client.print(pin);
    client.print(F(" configured as INPUT!"));
    return;
  }

  if (mode == "output") {
    pinMode(pin, OUTPUT);
    // Send feedback to client
    client.print(F("Pin D"));
    client.print(pin);
    client.print(F(" configured as OUTPUT!"));
    return;
  }

  client.print(F("error: invalid mode "));
  client.print(mode);
}

Das einzige Problem ist nur noch das der gelesene String z,b digital noch die Anzahl der Stringlänge enthält. Das kommt von der SPS so.
somit funktionier

if (command == "digital") {

noch nicht

ich muss noch nach einer Lösung finden das ein Schlüsselwort z.b digital im String command gesucht wird.
strstr?

Du musst zwischen String Objekten und char array[] -- zur Verwirrung auch gern mal c-string genannt -- , unterscheiden.
strstr arbeitet mit char array bzw char*.

String Objekte sind erheblich dicker (was beim Yun auf der 32U4 Seite auch weh tut) und haben Methoden wie .indexOf oder .startsWith und so.

Wenn Du Deine Anbindung an die SPS stabil hinkriegen willst, solltest Du auf die String-Klasse verzichten (dynamische Speicherverwaltung ist auf einem Mikrokontroller mit 2.5kB RAM und ohne MMU alles andere als einfach und führt schnell zu fragmentiertem Speicher, womit sich dann Probleme häufen werden) und C-Strings (also char-Arrays) verwenden.

Dein

if (command == "digital") {

wäre dann

if (strncmp(command, "digital", 7) == 0) {

Ansonsten hat die String-Klasse eine equals()-Methode, die für Vergleiche geeignet ist.

Hallo
ich werde wohl von der SPS einen Char senden und keinen String. Somit wollte ich auch auf dem Arduino mit Char arbeiten.
Wie kann ich diese Funktion

command = client.readStringUntil('/');

in char schreiben ?
ich möchte alle Zeichen bis / in einen S Char einlesen und dann vergleichen ober der Text z.b "digital" enspricht.

Wie wär's mit:

#define BUFFER_SIZE 30
uint8_t i = 0;
char buffer[BUFFER_SIZE];

while (client.available() && i < BUFFER_SIZE) {
  buffer[i] = client.read();
  if (buffer[i] == '/') break;
  i++;
}
if (i < BUFFER_SIZE) buffer[i] = 0; // end of string
if (strncmp(buffer, "digital", 7) == 0) {
  // do anything
}

Wenn Du allerdings nur einzelne Buchstaben sendest, brauchst Du das gar nicht.

Hallo
wie würdest du die Komunikation zzwischen SPS und Arduino aufbauen ?

Wollte auf dem Arduino ca. 20 Variablen zur Sps Senden. In diese Variablen kann ich dann den Status eines Eingängs oder Messwerte eines Temperaturfühlers, oder IR Befehle zur SPS sinden. Soll ich alle 20 Variablen mit einmal Senden ? Oder solle ich von der SPS seite jede Variable einseln anfragen z.b

Alle
read.vale

oder

read.vale.1
read.vale.2

Hallo
habe mich jetzt dafür eintschieden Char von der SPS zu senden. Jetzt habe ich folgendes Proglemm.

ich möchte die Char Anfrage 'G','E','T','V','A','L','V','E',' ','0','1',' in einem Char Array schreiben damit ich einen Vergleich machen kann

if (command = GETVALVE 01)

while (client.connected()) {
           
      while (client.available()){
                 
         c[i] = client.read();
         Console.println(c);

Wenn du C-Strings hast und das in einem char Array stehen hast:

Um Strings zu vergleichen gibt es dann strcmp:
http://www.nongnu.org/avr-libc/user-manual/group__avr__string.html#ga46f3cbd2de457c0fb340a1f379fc33ba
http://www.cplusplus.com/reference/cstring/strcmp/

Das gibt 0 zurück wenn die zwei Strings gleich sind

Hallo
ein weiters Problem ist ich weiß nicht wann mein Packt vertig gesendet worden ist. mit lauft der buffer des C Array über.

Woher weiß ich das ein Packt gesendet wurde

void loop()
 {
  // listen for incoming clients
  YunClient client = server.accept();
 
  if (client) {
    
    
    while (client.connected()) {
            
      while (client.available()){
                 
         c = client.read();
         buf[i] =  c;
        
         i++;      
        } 
        client.write('1');
        buf[0] = '\0';       
          
        }  
     
    // give the Client time to receive the data
    delay(1);
    // close the connection:
    client.stop();
  
  
 }

Kannst du ein abschließendes Zeichen senden? Da bietet sich z.B. ein Line Feed an. ASCII Code 10 oder 0x0A

Das hier passt glaube ich auch nicht:

buf[0] = '\0';

Du musst am Ende NULL in den letzten Index schreiben. Wenn der String nicht korrekt terminiert ist funktioniert das auch nicht.

Hallo
habe einen Linefeed (LF) und einen Carriage Return (CR) eingebaut

'G','E','T','V','A','L','V','E',' ','X','X','$L', '$R';

Wie müsste mein Programm aussehen ?

Einfach if(c == 10). if(c == '\n') sollte glaube ich auch gehen und ist deutlicher

Dabei solltest du darauf achten, dass du danach auch das CR aus dem Stream entfernst. Also einfach noch ein read() das nirgends abgespeichert wird.

Und wie gesagt den String korrekt mit NULL terminieren!

Wenn dann das LF empfangen wurde kannst du z.B. eine Statusvariable setzen um das Ende zu signalisieren und die Auswertung gleich dort machen. Kommt darauf an wie genau du das strukturieren willst. Idealerweise ist die Auslese Routine von der Verarbeitung getrennt.