Hostlink PLC e arduino, generare FCS

Ciao a tutti,
vorrei mettere in comunicazione un mio PLC Omron e l’arduino tramite il protocollo Hostlink. (in allegato la descrizione con degli esempi di utilizzo)

Il problema è nella costruzione della stringa, soprattutto quando cerco di creare il checksum.
Vi allego il codice che ho scritto fin ora

char stringa[16];

void setup() {

  Serial.begin(9600); 
}

void loop() {
char* ab;
ab=comando();
  Serial.print(ab);                       


  delay(1000);                     
}


char* comando(){

  stringa[0]='@';
//nodo
stringa[1]='0';
stringa[2]='0';
//codice iniziale
stringa[3]='R';
stringa[4]='R';
//canale iniziale
stringa[5]=0;
stringa[6]=0;
stringa[7]=2;
stringa[8]=0;
//numero canali
stringa[9]=0;
stringa[10]=0;
stringa[11]=0;
stringa[12]=1;
//FCS
int a,b;
char risultato[16];
for(a=0;a<13;a++)
{
  if (a==0)
  risultato[a]=stringa[a] ^ stringa[a+1];
  else
  risultato[a]=risultato[a-1] ^ stringa[a+1];
  
  }
//reinserisco l'FCS nella stringa di partenza
for (a=13;a<=14;a++)
stringa[a]=risultato[a-1];

return stringa;

}

Cosa mi consigliate?

Grazie in anticipo a tutti :smiley:

hostlink.pdf (248 KB)

Ho letto veloce ma questa NON è un char:

stringa[1]='00';

Il vettore è di elementi char, perciò ogni elemento deve essere un char. '0' o 'R' è un char, 'RR' sono due char e il compilatore legge solo il primo.

Devi per forza fare una cella alla volta:

stringa[1]='0';
stringa[2]='0';
stringa[3]='R';
stringa[4]='R';

Grazie per la celere risposta.

Vorrei chiederti 2 cose

a che serve * dopo char (es. char* comando) e se secondo te il calcolo dell' FCS (checksum) è giusto

Grazie :D

Per il calcolo, non lo conosco.

Per l'asterisco, si riferisce ai puntatori di memoria.

Se dichiari

char c='X';
char vc[]={'A','B','C'};
char vs="Frase";

char * pc=vs;

Avrai in memoria, c come variabile carattere che può contenere un carattere e occupa 1 byte. vc è un vettori di 3 caratteri con i valori 'A','B','C' vs è una stringa (vettore di caratteri terminato da '\0' carattere fine stringa) e il vettore è di 6 caratteri (5+1)

pc non è una cella che può contenere un carattere ma contiene un puntatore ovvero l'indirizzo a qualcosa. Questo puntatore può puntare vettori o variabili di tipo carattere. Attraverso di lui potrai leggere/scrivere un carattere. Nell'esempio pc punta al primo elemento di vs (elemento 0). Scrivere

vs[2]='X';

oppure

*(pc+2)='X';

è uguale.

pc è particolare perchè di un puntatore puoi modificare il contenuto ovvero l'indirizzo , esempio:

ps=ps+1;

Ora ps punta alla cella 1 di vs. Attraverso ps (usando come sintassi l'asterisco) allora usi l'indirizzo e leggi/scrivi quello a cui il puntatore punta:

*ps='X';
char cx=*ps;

ciao,
grazie mille per la spiegazione, sei stato chiarissimo!

Per il calcolo dell’FCS ho trovato questo esempio scritto in c su un forum Omron. Stavo cercando di adattarlo al mio programma ma non ci sono ancora riuscito.
Puoi suggerirmi qualcosa?

/* Calcola la somma di controllo */

CalcFCS(char *stringa)
{
int l,index;

l=strlen(stringa); // l = lunghezza della stringa

FCS='@'; // inizializzo il valore
for(index=1;index < l;index++)

FCS^=*(stringa+index); // eseguo XOR per tutti i caratteri

return 0;
}

grazie mille ancora

Per il calcolo ho trovato questo, ma in VB:
http://www.plcforum.it/forums/lofiversion/index.php/t3985.html

Public Function CalcoloFcs(TestoFcs As String) as String
Dim lun As Integer, idx As Integer, somma As Integer, car As integer, ret As String
lun = Len(TestoFcs)
somma = 0
For idx = 1 To lun
    car = Asc(Mid$(TestoFcs, idx, 1))
    somma = somma Xor car
Next idx
ret = Hex$(somma)
If Len(ret) = 1 Then ret = "0" & ret
CalcoloFcs = ret
End Function

In VB: ? CalcoloFcs("@00WD00001234") → “57”
@00WD0000123457*

Complesso ritornare in C una stringa (è un vettore), meglio ritornare il valore in cifra. Poi il confronto lo farai convertendo anche il secondo valore in cifra.

unsigned char CalcFCS(char *str, int lun)
{ unsigned char sum=0;
  for(int i=0;i<lun;i++)
  { sum ^= *(str+i);
  }
  return sum;
}
...
chiamata: 
   unsigned char chk=CalcFCS(stringa,sizeof(stringa));   // numero elementi del vettore
oppure
  unsigned char chk=CalcFCS(stringa,strlen(stringa));    // solo se stringa ha terminatore '\0' !!!
oppure
  unsigned char chk=CalcFCS(stringa,13);  // numero fisso, sai tu quanti elementi sono validi, mi sembra nel tuo primo esempio, 13 elementi poi chksum

Ho capito ora, il checksum è da aggiungere ad una stringa da spedire, non per controllare in risposta. Quanti caratteri sono il checksum?

Il checksum è formato da 2 caratteri, ti allego l'esempio che ho trovato nel manuale Omron

Quindi la funzione CalcFCS() ti dà il valore decimale (un byte). Devi a questo punto prendere la parte bassa (prima 4 bit) e la parte alta (secondi 4 bit) e convertirli in una cifra ascii dove '0' è 48, '1' è 49 etc. Se i primi 4 bit sono 0100->ovvero 4 sommando 48 ottieni 52 ovvero in ascii -> '4' Secondi 4 bit sono 0110-> ovvero 6 +48->54->'6' ed hai i due caratteri da mettere nelle celle 13 e 14 (credo) e poi in 15° cella '*' e in 16° cella il valore 13

...
stringa[12]=1;
unsigned char chk=CalcFCS(stringa,13)
stringa[13]=highByte(chk)+(highByte(chk)>9?55:48);
stringa[14]=lowByte(chk) +(lowByte(chk) >9?55:48);
stringa[15]='*';
stringa[16]=13;

nid69ita: Ho capito ora, il checksum è da aggiungere ad una stringa da spedire, non per controllare in risposta. ...

Il "checksum" serve per controllare che i dati trasmessi arrivino corretti, indipendentemente da chi riceve e da chi trasmette.

Chi trasmette (chiunque sia) prende il blocco dei dati, con una data formula calcola il checksum ed invia al ricevente sia i dati che il checksum. Il ricevente riceve i dati, calcola anche lui il checksum [u]con la stessa formula[/u] e lo compara con quello che ha ricevuto. Se è uguale i dati sono arrivati correttamente, se è diverso i dati vengono scartarti perché sono arrivati corrotti.

E' quindi fondamentale il calcolo corretto del cheksum altrimenti i due NON si parlano. ;)

Guglielmo

Si, @gpb quel che non avevo capito subito è che @blasted stà per ora implementando la costruzione del msg di invio, mentre io ero già al check del msg d’arrivo

La stessa funzione la potrai usare nel controllare la risposta, confrontando 2 numeri, uno dato dalla CalcFCS() sui primi 13 caratteri e l’altra cifra la devi calcolare prendendo il carattere in cella 13 e 14 e facendo il procedimento inverso.

chk1=CalcFCS(stringa,13);
chk2=(stringa[13]-(stringa[13]>9?55:48))*16 + (stringa[14]-(stringa[14]>9?55:48) );   
// * 16 equivale a spostare a  sinistra di 4 bit     
if(chk1==chk2)   // okay

In C, in questo caso, è meglio lavorare con i numeri che con le stringhe alla Basic.

Discussione interessante. ;)

Frase alternativa di "x iscrizione" :sweat_smile:

nid69ita: Si, @gpb quel che non avevo capito subito è che @blasted stà per ora implementando la costruzione del msg di invio, mentre io ero già al check del msg d'arrivo ...

Guglielmo

Allora ragazzi grazie a tutti per le risposte!

Ho alcune domande ancora :wink:

@nid69ita
Ho provato il codice su vb e mi esce 57
Dopo inserisco la stessa stringa, però in C, e mi esce 87. Sbaglio qualcosa?

poi vorrei sapere perchè in questo esempio metti nella posizione 12 il valore 1?

...
stringa[12]=1;
unsigned char chk=CalcFCS(stringa,13)
stringa[13]=highByte(chk)+48;
stringa[14]=lowByte(chk)+48;
stringa[15]='*';
stringa[16]=13;

Comunque quello che ho scritto fino ad ora è praticamente questo:

char stringa[16]="@00WD00001234";


void setup() {

  Serial.begin(9600);
}


void loop() {
//  unsigned char chk=CalcFCS(stringa,13);
  
  
 // stringa[12]=1;
unsigned char chk=CalcFCS(stringa,13);
stringa[13]=highByte(chk)+48;
stringa[14]=lowByte(chk)+48;
stringa[15]='*';
stringa[16]=13;
  
      Serial.println(stringa);
      delay(2000);
  
}


unsigned char CalcFCS(char *str, int lun)
{ unsigned char sum=0;
  for(int i=0;i<lun;i++)
  { sum ^= *(str+i);
  }
  return sum;
}

e in uscita ottengo

@00WD000012340?_* (dove al posto di _ c’è un carattere incomprensibile :smiley: )

blasted: @nid69ita poi vorrei sapere perchè in questo esempio metti nella posizione 12 il valore 1?

Copiato dal tuo esempio iniziale dove caricavi ogni elemento a mano. :grin:

87 decimale => 57 esadecimale, la versione VB lo dà in esadecimale, quella in C in decimale (usa Serial.println(xx,HEX); per vederlo in esa)

Metti anche una bella una bella Serial.println(chk); per vedere cosa calcola la funzione. Credo comunque di aver fatto un errore, manca la conversione in esadecimale. Quelle due cifre devono essere in esa, giusto? da 0-9 no problem, 10-15=>A-F

if(highByte(chk)>9)
  stringa[13]=highByte(chk)+55;  // 10-> +55=>65=>'A'
else
  stringa[13]=highByte(chk)+48;
if(lowByte(chk)>9)
  stringa[14]=lowByte(chk)+55;
else
  stringa[14]=lowByte(chk)+48;

Oppure in maniera meno leggibile:

stringa[13]=highByte(chk)+(highByte(chk)>9?55:48);
stringa[14]=lowByte(chk) +(lowByte(chk) >9?55:48);    // utilizzo if in linea

Consiglio (ricevuto nel forum) in generale x sketch Arduino, nella setup() metti come prima istruzione una delay(1000); oppure delay(2000);

Si bisognerebbe convertire i 2 valori in esadecimale.. Come posso fare?

Mi servirebbe anche perchè ho un'altro vettore da 16 contenente o 1 o 0 e dovrei prima dividerlo in 4 parti e poi convertirlo in esadecimale. per la divisone penso che non ci siano problemi, mentre per la conversione non saprei come fare. Ci sono istrzioni che fanno questo?

Grazie ancora per l'aiuto :D

Guarda che nel mio post precedente ho aggiunto la conversione di quei due valori in esa e anche il post dove ho messo il codice per testata la risposta l'ho aggiornato. :grin:

blasted: Mi servirebbe anche perchè ho un'altro vettore da 16 contenente o 1 o 0 e dovrei prima dividerlo in 4 parti e poi convertirlo in esadecimale. per la divisone penso che non ci siano problemi, mentre per la conversione non saprei come fare. Ci sono istrzioni che fanno questo?

si, la snprintf(); è come la printf ma "stampi" dentro a una stringa. Usi il parametro %X http://www.cplusplus.com/reference/cstdio/snprintf/?kw=snprintf

char buf[10];
int valore=166;
snprintf(buf,sizeof(buf),"%X",valore);   // ottieni A6   con %x ottieni => a6

nid69ita: Guarda che nel mio post precedente ho aggiunto la conversione di quei due valori in esa e anche il post dove ho messo il codice per testata la risposta l'ho aggiornato. :grin:

Dove? non riesco a trovare il pezzo della conversione

Prima era:

stringa[13]=highByte(chk)+48;
stringa[14]=lowByte(chk) +48;    // 4+48=>52=>'4'

Ora discrimino con un if in linea se il valore supera 9 perchè 0-9=>'0'-'9' ma 10-15=>'A'-'F' Ti metto il metodo meno leggibile ma più stringato:

stringa[13]=highByte(chk)+(highByte(chk)>9?55:48);   // se valore>9 55 else 48
stringa[14]=lowByte(chk) +(lowByte(chk) >9?55:48);   // utilizzo if in linea