Invio Comandi tra Client e Server

Lesto capisco cosa intendi, però questo problema che mi è stato assegnato è di fare una rete in cui ci sono N Client ( N Arduino Uno ) e N server (altri N Arduino Uno ) ognuno ha un ip in pratica gli N client hanno connesse delle pulsantiere touch e vanno a comandare in maniera confusionaria (IP) gli altri server che hanno invece collegati dei Relay quindi i comandi glieli devo passare in questo modo altrimenti risulta complicato . Dovrebbe in soldoni premi un bottone sul lato client accendi una luce o lo scaldabagno di uno dei tanti server.

si ma per fare i test devi andare per gradi, prima fai funzionare il server con telnet, poi lo fai funzionare con il client.
Altrimenti impazzisci a cercare nel server un errore del client.

Lesto ho fatto come dici tu sono andato su Telnet all'indirizzo IP specificato x il server se faccio 1 e 0 se li digito il led si accende e si spegne se digito "ON " oppure "OFF" con il codice di ON e OFF del server non va guarda il codice sarebbe questo

#include <SPI.h>                               //  Importa Libreria SPI
#include <Ethernet.h>                          //  Importa Libreria 
//  Ethernet

#define LED 7                                  //  Porta digitale 7 LED

char comando[3] ;

byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };        //  MAC Ethernet Shield del Server

byte ip[] = { 10, 10, 11, 17 };                             //  IP Arduino Server

EthernetServer server = EthernetServer(23);    //  Telnet porta 23
EthernetClient client = 0;                     //  Inizializza il Client

void setup()
{
  pinMode( LED, OUTPUT );                      //  Imposta LED OUTPUT
  Ethernet.begin(mac, ip);                     //  Inizializza Ethernet
  server.begin();
}
void loop()
{ 
  while ( client.connected() && server.available() >= 3 ) {                 //  Client-To-Serve                                                                                               
    char c = client.read();                                            //  Legge i Byte su Client
    comando[0] = c;
    c = client.read();                                            //  Legge i Byte su Client
    comando[1] = c;
    c = client.read();                                            //  Legge i Byte su Client
    comando[2] = c;
  }
  if ( comando[0] == 'O' && comando[1] == 'N' && comando[2] == ' ' ) 
    digitalWrite( LED, HIGH );
  if ( comando[0] == 'O' && comando[1] == 'F' && comando[2] == 'F' ) 
    digitalWrite( LED, LOW );
}

Sicuramente è xchè client.read legge un carattere alla volta :slight_smile: però come faccio a fargli leggere i 3 caratteri??? Grazie sempre con telnet dal lato server

il fatto di leggere 3 caratteri lo stai già facendo, e si fa esattamente così, si legge un carattere alla volta e si riscostruisce.

se faccio 1 e 0 se li digito il led si accende e si spegne se digito "ON " oppure "OFF" con il codice di ON e OFF del server non va

se non hai ricaricato il codice sul server con le modifiche che abbiamo fatto fin'ora per legger 3 lettere ci credo che non funziona. Se avessi caricato il codice 1 e 0 non dovrebbero più funzionare.

Lesto come dicevo prima

ho caricato questo sketch su un arduino uno

#include <SPI.h>                               //  Importa Libreria SPI
#include <Ethernet.h>                          //  Importa Libreria 
//  Ethernet

#define LED 7                                  //  Porta digitale 7 LED

char comando[3] ;

byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };        //  MAC Ethernet Shield del Server

byte ip[] = { 10, 10, 11, 17 };                             //  IP Arduino Server

EthernetServer server = EthernetServer(23);    //  Telnet porta 23
EthernetClient client = 0;                     //  Inizializza il Client

void setup()
{
  pinMode( LED, OUTPUT );                      //  Imposta LED OUTPUT
  Ethernet.begin(mac, ip);                     //  Inizializza Ethernet
  server.begin();
}
void loop()
{ 
  while ( client.connected() && server.available() >= 3 ) {                 //  Client-To-Serve                                                                                               
    char c = client.read();                                            //  Legge i Byte su Client
    comando[0] = c;
    c = client.read();                                            //  Legge i Byte su Client
    comando[1] = c;
    c = client.read();                                            //  Legge i Byte su Client
    comando[2] = c;
  }
  if ( comando[0] == 'O' && comando[1] == 'N' && comando[2] == ' ' ) 
    digitalWrite( LED, HIGH );
  if ( comando[0] == 'O' && comando[1] == 'F' && comando[2] == 'F' ) 
    digitalWrite( LED, LOW );
}

poi sono andato su putty ho aperto la pagina telnet dell ip 10.10.11.17 ok ? e se faccio 1 e 0 ok il led si accende e si spegne se scrivo ON oppure OFF non succede nulla :slight_smile: dove sbaglio?

sbagli che NON hai caricato il codice sull'arduino, altrimenti 1 e 0 NON funzionerebbero.
Poi magari on funzionerebbero nemmeno "ON" e "OFF" (ma dubito), ma sicuramente 0 e 1 non devono funizonare..

cioè mi spieghi se hai veramente caricato quel codice come mai si accende quando scrivi 0 e 1? arduino non se lo inventa certo dal nulla :slight_smile:

l'unica spiegazione è che sia rimasto sopra il vecchio codice.

Suggerimento: quando carichi il codice togli l'ethernet shield, se il problema persiste fai il reset a mano

se carico questo codice su arduino

#include <SPI.h>                                              //  IMPORTA LIBRERIA SPI
#include <Ethernet.h>                                         //  IMPORTA LIBRERIA ETHERNET 

#define LED 7                                                 //  PORTA DIGITALE 7 LED

//  PARAMETRI ETHERNET

byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };          //  MAC ETHERNET SHIELD DEL SERVER 
byte ip[] = { 10, 10, 11, 17 };                               //  IP ARDUINO SERVER
byte gateway[] = { 10, 10, 11, 1 };
byte subnet[] = { 255, 255, 255, 0 };                             

//  ALTRE VARIABILI

#define dimCmd 8      //  LUNGHEZZA DEL PIù LUNGO COMANDO
int cmd[dimCmd];      //  ARRAY DI COMANDO
int bitRicevuti = 0;  //  NUMERO DI BIT RICEVUTI

boolean connectState = false;                  //  USATA PER VERIFICARE CONNESSIONE CLIENT TO SERVER

unsigned long tempoDaUltimaAzione;             //  TEMPO IN ms DALL'ULTIMA ATTIVITà
unsigned long tempoMassimoPermesso = 300000;   //  5 MINUTI

EthernetServer server = EthernetServer(23);    //  Telnet porta 23
EthernetClient client = 0;                     //  Inizializza il Client

void setup()
{
  pinMode( LED, OUTPUT );                      //  Imposta LED OUTPUT
  Ethernet.begin(mac, ip);                     //  Inizializza Ethernet
  server.begin();
  delay(150);
}
void loop()
{ 
  if (server.available() && client.connected()) {  //  BYTE SUL SERVER & CONNECTED

    char c = client.read();                        //  LEGGE I BYTE SU CLIENT

    if ( c == '1' )                                //  SE BYTE = 1 LED ON
      digitalWrite( LED, HIGH );
    else
      if ( c == '0' )                              //  SE BYE = 0 LED OFF
        digitalWrite( LED, LOW );
  }
}

vado su tera term e digito 1 poi 0 il led si accende e si spegne

se carico l'altro quello del messaggio precedente con ON E OFF e provo da tera term a digitare ON O OFF IL LED non si accende...vorrei capire perchè

a me sembra stano che entrambi i codici funzionino, visto che non inizializzi mai la variabile client... ti manca un pezzo!
Fai più attenzione al codice che posti e alla descrizione degli errori...

il pezzo che manca:

client = server.available();

il pezzo errato

while  (server.available() >3 && client.connected()) {

diventa

while (client.available() < 3 && client.connected()) {

infatti server.available() ritorna l'istanza ad un EthernetClient connesso, una volta che abbiamo il client vogliamo sapere il numero di caratteri che ci ha spedito che s ottiene con client.available()

quindi diventa:

void loop()
{ 
  client = server.available();
  if (client){  //se si è connesso qualcuno
    while ( client.connected() && client.available() < 3 ) {                 //  Client-To-Serve                                                                                               
       //aspetta senza far nulla che il client si disconnetta o invii almeno 3 lettere
    }

    if (client.available() < 3) { //il client si è disconnesso, ma non ha inviato 3 lettere
      client.close(); //chiudiamo comunque la comunicazione in modo pulito, e rilasciamo eventuali risorse della classe
      return; //non vogliamo fare altro, ternima questo loop() immediatamente (e quindi passiamo al prossimo loop da capo)
    }
    char c = client.read();                                            //  Legge i Byte su Client
    comando[0] = c;
    c = client.read();                                            //  Legge i Byte su Client
    comando[1] = c;
    c = client.read();                                            //  Legge i Byte su Client
    comando[2] = c;
    if ( comando[0] == 'O' && comando[1] == 'N' && comando[2] == ' ' ) 
      digitalWrite( LED, HIGH );
    if ( comando[0] == 'O' && comando[1] == 'F' && comando[2] == 'F' ) 
      digitalWrite( LED, LOW );
 }
}

Grande Lesto mo funziona alla grande cerco pure di capire come hai fatto :wink: ...ma tipo io un client posso connetterlo ad N server facendo semplicemente client.connect(server,23); dove ovviamente server è l'IP del server a cui voglio connetterlo giusto ? :smiley: penso che sto ragionamento non faccia una piega ma per inviare un numero ? basta fare client.print(010101, BIN); x dirgli che è binario ? ...grazie mille ancora !

dipende. Un'oggetto client può essere connesso ad un solo server per volta (se non come fai a dirgli da chi leggere/scrivere?). Ma nulla vieta di avere uno più (magari un array) di oggetti client, ognuno connesso al suo server, e magari anche più di uno connesso allo stesso server.

Mi pare che però l'ethernet shield al massimo gestica 5 connessioni simultanee... Quindi esitono dei limiti, che sono aggirabili in certe condizioni

credo siano 4 le connessioni contemporanee... una cosa sullo sketch di prima a me client.close() non me lo prendeva io c'ho messo client.stop() diceva che close non era specificato nella libreria ethernet o cmq che non era un comando da applicare a client perchè? cmq con client.stop al posto di client.close va alla grande

perchè mi son sbagliato io, close non esite, ma vedo che hai risolto il bug da solo :grin:

finchè è così semplice ci arrivo anche io :roll_eyes: Grazie ancora...ma per scambiare questi comandi tra client e server mi basta studiare i comandi della libreria Ethernet sia lato server che lato client giusto x intenderci qui Ethernet - Arduino Reference giusto?

l'importante è che non ti fai problemi a chiedere cose che non capisci.

Per esempio del mio codice è tutto chiaro?

ricordati del reference: Arduino - Home
e del reference delle librerie ufficiali: Libraries - Arduino Reference
tra cui la Ethernet: Ethernet - Arduino Reference

ti salveranno ore di lavoro

scusa Lesto sto cercando di capire adesso in maniera intuitiva sono andato a provarlo e ho notato che in pratica da telnet per potergli far cambiare o stato al led prima di passargli ON o OFF devo andare a capo poi ho visto che tipo ha un comportamento anomalo sulla chiusura del telnet...forse devo capirlo meglio ...anche se le cose che mi hai scritto vicino alle istruzioni un pò mi aiutano ... semmai Grazie di ogni chiarimento...intanto continuo a capire la logica dello sketch ho scritto una stupidaggine cmq devo capire bene come legge i comandi il protocollo telnet ho visto che non è necessario andare a capo però certe volte li prende certe volte no poi delle volte pare che si blocca e se non scrivo i comandi alternati non riprende ovvero se faccio OFF OFF OFF OFF e non mi funziona magari l'OFF devo prima ridigitare ON ..e poi OFF lo prende mi devo vedere il protocollo telnet è evidente :blush: ho notato un'altra stranezza nel senso se magari lascio aperta la connessione telnet con tera term stranamente funziona anche il collegamento client-to-server tra arduino invece non dovrebbe funzionare come mai???

ah, semplice, errore mio.
Quando legge un comando, ricomincia il loop() e l'oggetto client attuale viene perso sovrascritto dalla Server.available(), però dato che il client non viene chiuso correttamente, la ethernet shield lo tiene collegato.

Quindi:

  1. essendo l'oggetto client perso dopo la lettura, il client rimane connesso ma da codice non è più raggiungibile
  2. telent invia il carattere appena lo digiti, però in realtà finisce in un buffer e alle volte ci vuole un pò prima che il buffer sia inviato: ifatti il protocollo TCP usa circa 128 byte di intestazione per inviare un messaggio, la cui dimensione massima compreso l'header (MTU) è di circa 1400byte, a seconda della rete.. capisci che sprecare 128byte di header per iviare una lettera è (preziosa) banda sprecata, quidi il SO alle volte aspetta qualche istante per vedere se scrivi altro, se sì alora manda un mesaggio unico. l'attesa è nell'ordine di meno di un secondo comunque.
  3. l'invio non serve, anzi. noi leggiamo di 3 caratteri in 3 caratteri, quindi un unvio sfalserebbe il valore of OFF in (invio = X) XOF, il successivo comando letto sarebbe FOF e così via... ricordati che telnet non può cancellare, quindi se premi invio + una lettera sbagliata + cancelli hai in realtà scritto 3 lettere, e quindi lo sfaso si annulla e puoi di nuovo funzionare.

per risolvere ci sono varie strade:

  1. disconnetti il client dopo aver letto il suo messaggio. Brutto, perchè la connessione TCP impiega un sacco di banda e risorse (vedi 3 way handshacke) quindi meglio tenere il client collegato fichè si può. Però così è facile fare più client
  2. non chiamare client = server.available(); se un client è già connesso
  3. sistemare il codice per funzionare con un array di client e accettare quindi fino a 3 client alla volta (sistema 1 + 2)

si quel discorso dell'andare a capo ...era il fatto che mi sbagliavo a scrivere e a quel punto l'unica soluzione dato che cancellare non serve a nulla è andare a capo se non sbaglio...quindi eventualmente x fare una connessione telnet alla volta come dovrei agire??? dovrei cambiare lo sketch del server? non ho ben capito..ho visto che infatti il problema è in client = server.available(); e if (client) ...però come lo risolvo per farlo riconnettere solo con 1 connessione un ultima cosa...io ho scaricato Simulator for Arduino x fare le simulazioni sai consigliarmi un software ?? x fare queste cose vorrei testare un collegamento con + arduino caricarci gli sketch che scrivo e simularli...magari sarebbe il massimo se il simulatore fosse gratuito ...grazie 1000

devi agire che non fai server.available fichè c'è un client connesso... non è difficile, provaci :slight_smile:

non ho mai usato simulatori, come puoi vedere con un poco di fantasia si può fare tutto a mano

Ora dovrei fare un'altra cosa ma ho un piccolo problema...dovrei far si che il mio arduino uno che lavora da client si comporti da server con la porta telnet e venga configurato magari questo arduino si connette con diversi IP ogni pulsante connesso ad arduino client va a comandare dei server con IP differenti sulla rete...dovrei vedere su tera term o putty la configurazione degli ip delle subnet e delle maschere...per maschere si intenderebbe "10101000" dove gli 1 indica su quali relay di quel server il mio client va ad agire riesco a visualizzare tutto ok...solo che non vedo la mask in configurazione binaria posto di seguito lo sketch

#include <SPI.h>                                              //  IMPORTA LIBRERIA SPI
#include <Ethernet.h>                                         //  IMPORTA LIBRERIA ETHERNET

#define BUTTONON  7                                           //  PIN7 INPUT = BUTTON  
#define BUTTONOFF 6                                           //  PIN7 INPUT = BUTTON  
#define dimBuff 36 //length of longest command string plus two spaces for CR + LF
char textBuff[dimBuff]; //someplace to put received text
int ts1st  = HIGH;                                            //  ts1st LEGGE E MEMORIZZA IL LIVELLO DI BUTTON  
int stato1 = HIGH;                                            //  stato1 MANTIENE LO STATO DI BUTTON
int ts2st  = HIGH;                                            //  ts2st LEGGE E MEMORIZZA IL LIVELLO DI BUTTON  
int stato2 = HIGH;                                            //  stato2 MANTIENE LO STATO DI BUTTON
int i = 0;                                                    //  i CONTA LE VOLTE DI BUTTON ON PUSH
int j = 0;                                                    //  j CONTA LE VOLTE DI BUTTON OFF PUSH
boolean conf = false;
unsigned long timeOfLastActivity;
int charsReceived = 0;

byte serverA[] = { 10, 10, 11, 17 };
byte serverB[] = { 10, 10, 11, 4 };
byte serverC[] = { 10, 10, 11, 5 };
byte serverD[] = { 10, 10, 11, 7 };
int mask = 4; //se gli passo 4 mi si vede 100 invece io vorrei vedere 00000100 se gli passo 00000100 mi si vede 10000000 p.s. l'ho passato anche come byte mask = 00000100; forse devo mettere [] ?
int Aserver1 = 10;
int Aserver2 = 10;
int Aserver3 = 11;
int Aserver4 = 17;
int subnet1 = 255;
int subnet2 = 255;
int subnet3 = 255;
int subnet4 = 0;
byte mac[] = { 
  0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0x47 };           //  MAC ETHERNET SHIELD
byte ip[] = { 
  10, 10, 11, 45 };                               //  IP CLIENT 
byte server1[] = { 
  10, 10, 11, 17 };                               //  IP SERVER1
byte server2[] = { 
  10, 10, 11, 18 };                               //  IP SERVER2
byte gateway[] = { 
  10, 10, 11, 1 };                                //  INDIRIZZO ROUTER GATEWAY
byte subnet[] = { 
  255, 255, 255, 0 };                             //  SUBNET

EthernetClient client;
EthernetServer server = EthernetServer(23);    //  Telnet porta 23

void setup() {  

  Ethernet.begin(mac, ip, gateway, subnet);                   //  INIZIALIZZA ETHERNET
  Serial.begin(9600);                                         //  INIZIALIZZA LA SERIALE
  pinMode(BUTTONON, INPUT);                                   //  IMPOSTA IL BUTTONON COME INPUT
  pinMode(BUTTONOFF, INPUT);                                  //  IMPOSTA IL BUTTONOFF COME INPUT

}  

void loop() {  
  if (server.available() && !conf ){
    conf = true;
    client = server.available();
    client.println("\nConfigurazione Arduino Client");
    client.println("\nPremere SHOW CNF per ottenere le CONFIGURAZIONI IP & MASK ");
    printPrompt();
  }
  
  if (client.connected() && client.available()) getReceivedText();
  
  ts1st = digitalRead(BUTTONON);                              //  LEGGE E MEMORIZZA IL VALORE DI BUTTONON
  ts2st = digitalRead(BUTTONOFF);                             //  LEGGE E MEMORIZZA IL VALORE DI BUTTONOFF

    if ( ts1st == HIGH && stato1 == HIGH );
  else{
    if ( ts1st == LOW && stato1 == HIGH ){                    //  PULSANTE PUSH FASE ON
      stato1 = LOW;                                           //  IMPOSTA IL NUOVO STATO = LOW
      delay(150);                                             //  RITARDO 150ms
      i++;                                                    //  INCREMENTA CONTEGGIO PUSH
      Serial.print(" IL BOTTONE ON E' STATO PREMUTO ");       //  STAMPA IL NUMERO DI PUSH EFFETTUATI
      Serial.print(i);
      Serial.print(" VOLTE");
      Serial.println();
      if (client.connect(server1, 23)){
        client.print("ON ");      
        client.stop();
      }
      else{
        Serial.println("Connessione ON fallita");             //  ALTRIMENTI
        //  STAMPA SU COM3 "Connessione ON fallita"
        client.stop();                                        //  CHIUDI CONNESSIONE
      }
    }
    if ( ts1st == HIGH && stato1 == LOW )                     //  POSIZIONE DI RIPOSO FASE OFF
      stato1 = HIGH;                                          //  AGGIORNA IL NUOVO STATO DI BUTTONON = HIGH
  }

  if ( ts2st == HIGH && stato2 == HIGH );
  else{
    if ( ts2st == LOW && stato2 == HIGH ){                    //  PULSANTE PUSH FASE ON
      stato2 = LOW;                                           //  IMPOSTA IL NUOVO STATO = LOW
      delay(150);                                             //  RITARDO 150ms
      j++;                                                    //  INCREMENTA CONTEGGIO PUSH
      Serial.print(" IL BOTTONE OFF E' STATO PREMUTO ");      //  STAMPA IL NUMERO DI PUSH EFFETTUATI
      Serial.print(j);
      Serial.print(" VOLTE");
      Serial.println();
      if (client.connect(server1, 23)){
        client.print("OFF");      
        client.stop();
      }
      else{
        Serial.println("Connessione OFF fallita");            //  ALTRIMENTI
        //  STAMPA SU COM3 "Connessione OFF fallita"
        client.stop();                                        //  CHIUDI CONNESSIONE
      }
    }
    if ( ts2st == HIGH && stato2 == LOW )                     //  POSIZIONE DI RIPOSO FASE OFF
      stato2 = HIGH;                                          //  AGGIORNA IL NUOVO STATO DI BUTTONOFF = HIGH     
  }
}
void printPrompt()
{
  timeOfLastActivity = millis();
  client.flush();
  charsReceived = 0; //count of characters received
  client.print("\n>");
}

void getReceivedText()
{
char c;
int charsWaiting;
 
// copy waiting characters into textBuff
//until textBuff full, CR received, or no more characters
charsWaiting = client.available();
do {
c = client.read();
textBuff[charsReceived] = c;
charsReceived++;
charsWaiting--;
}
while(charsReceived <= dimBuff && c != 0x0d && charsWaiting > 0);
 
//if CR found go look at received text and execute command
if(c == 0x0d) {
parseReceivedText();
// after completing command, print a new prompt
printPrompt();
}
 
// if textBuff full without reaching a CR, print an error message
if(charsReceived >= dimBuff) {
client.println();
printErrorMessage();
printPrompt();
}
// if textBuff not full and no CR, do nothing else;
// go back to loop until more characters are received
 
}
void parseReceivedText()
{
// look at first character and decide what to do
switch (textBuff[0]) {
case 'S' : showConfiguration(); break;
case 0x0d :	break; //ignore a carriage return
default: printErrorMessage();	break;
}
}

void printErrorMessage()
{
client.println("\nComando sconosciuto !");
}
void showConfiguration()
{
  
if (textBuff[1] == 'H' && textBuff[2] == 'O' && textBuff[3] == 'W' && textBuff[4] == ' ' && textBuff[5] == 'C' && textBuff[6] == 'N' && textBuff[7] == 'F' ){
  client.println();
  client.print("\nServer A:");
  client.print(" IP : ");
  client.print(Aserver1);
  client.print(".");
  client.print(Aserver2);
  client.print(".");
  client.print(Aserver3);
  client.print(".");
  client.println(Aserver4);
  client.print("          SUBNET : ");
  client.print(subnet1);
  client.print(".");
  client.print(subnet2);
  client.print(".");
  client.print(subnet3);
  client.print(".");
  client.println(subnet4);
  client.print("          MASK : ");
  client.println(mask,BIN);
}
  else
printErrorMessage();
}

si vede sia ip che subnet alla grande ma la mask si vede male.
Grazie

4 in binario è 100, se tu gli passi 00000100 in realtà stai passando 100 che è decimale, e in bianario vale appunto 1100100

per cambiare il numero di zeri da stamapre davanti devi agire sul codice della print()... oppure "smonti" mask bit per bit (vedi bitshift, operatore >>)e ogni bot fai una pritnt di '0' o '1'