Pages: [1]   Go Down
Author Topic: Protocollo su rs485 - consigli.  (Read 1260 times)
0 Members and 1 Guest are viewing this topic.
Rome
Offline Offline
God Member
*****
Karma: 1
Posts: 643
La mia prima bromografata!!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Ciao a tutti! Ho bisogno di qualche consiglio sulla logica, poi magari anche sulla programmazione se m'incarto..

La topologia è questa:
4 slave dotati di 2 celle di carico ognuna, attiny85 e un SN75176 per l'RS485, cosicchè i dati mi viaggiano su doppino per una moderata distanza.
Le celle le leggo bene, ed esco in seriale software dai pin 2 e 0 come Rx e Tx. uso il pin 1 come selettore per tx/rx dell'SN75176.
...e fin qui tutto ok...
ora devo farmi un protocollo abbastanza affidabile per comunicare ad un master (probabilmente un 328P standalone..) le letture delle celle di carico.
io avevo pensato a questo:
Il MASTER chiede la lettura allo SLAVE1, che risponde.
una volta che il master riceve dallo SLAVE1, chiede al 2 e così via..
Immagino di dover fare così per non far accavallare le comunicazioni dei vari Slave, giusto? idee migliori?
non ho trovato librerie che mi possano aiutare, se non roba complicata tipo canbus, quindi penso di dover scrivere tutto. Quindi immagino di dover spedire tutto come ascii, in modo da poter differenziare caratteri di inizio/fine e byte di somma per integrità, dai caratteri numerici che compongono il dato di lettura. Anche qui chiedo conferma/consigli.

Può bastare?

Quindi ogni SLAVE sta in ascolto, finchè non legge una chiamata, immagino composta da:
byte d'inizio, byte indirizzo Slave, byte somma dei precedenti.

poi la risposta la immagino composta da:
byte d'inizio, byte indirizzo Slave per conferma, 4 byte per il dato cella1 (lettura analogica 0-1024), 4 byte per il dato cella2,  tot byte somma.

questo se spedisco tutto come singoli caratteri ascii.
oppure mi posso permettere di spedire i dati come valori decimali convertiti in binario?

Qualsiasi consiglio è stragradito!
Davide.
Logged

Verona
Offline Offline
Jr. Member
**
Karma: 0
Posts: 79
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

mi son trovato bene con la libreria simplemodbus master/slave e avevo fatto qualche prova anche seguendo questo tutorial: http://www.gammon.com.au/forum/?id=11428. Se poi vuoi scriverti il protocollo per tua soddisfazione tanto di cappello smiley-grin
Logged

Global Moderator
Italy
Offline Offline
Brattain Member
*****
Karma: 325
Posts: 22498
Logic is my way
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Secondo me ha più senso farsi un semplice protocollino personale che usare una libreria più "professionale".
Alla fine, ciò che devi fare è molto semplice.

Ah, una cosa. Per spedire un valore compreso fra 0 e 1023 ti bastano 2 byte: un tipo dati int (2 byte appunto). Lo spedisci con lowByte(dato) e highByte(dato) e lato Master lo ricostruisci semplicemente mettendo insieme i 2 byte in una variabile int:
Code:
datoRicevuto = (byteAlto<<8) | byteBasso
« Last Edit: April 28, 2012, 07:43:04 am by leo72 » Logged


Rome
Offline Offline
God Member
*****
Karma: 1
Posts: 643
La mia prima bromografata!!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Grazie intanto per le risposte.
Ieri ho fatto le 6 a tirar fuori il mio protocollino, alla fine un pò per imparare, un pò per avere una cosa dedicata e leggera, preferisco farne uno ad hoc.
purtroppo alle 6 in punto mi sono incartato con la funzione itoa()... ma funziona sull'IDE 1.0?
la volevo usare per trasformare in caratteri il numero 0000-1023. Però mi sa che faccio con i due byte, come suggerisce Leo.
Cmq, itoa() non va. io ho scritto questo:
Code:
int cella1 = 30;
char cella[5];
itoa(cella1,celle,10);
ma in compilazione mi restituisce:
error: expected constructor, destructor, or type conversion before ‘(’ token
Non capisco..
Comunque, se funzionasse, mi dovrebbe riempire celle[] con il valore di cella1 in ascii, giusto?
« Last Edit: April 28, 2012, 06:25:15 am by dab77 » Logged

Rome
Offline Offline
God Member
*****
Karma: 1
Posts: 643
La mia prima bromografata!!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

ho anche un'altra domandina:
volendo mandare un byte 'somma' per conferma integrità dati, se sommo 6 byte, in questo caso devo mandare 2 byte per esprimere la somma?
come si fa normalmente?
Logged

Rome
Offline Offline
God Member
*****
Karma: 1
Posts: 643
La mia prima bromografata!!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Ah, una cosa. Per spedire un valore compreso fra 0 e 1023 ti bastano 2 byte: un tipo dati int (2 byte appunto). Lo spedisci con lowByte(dato) e highByte(dato) e lato Master lo ricostruisci semplicemente mettendo insieme i 2 byte in una variabile int: datoRicevuto = (byteAlto<<smiley-cool || byteBasso

Leo, il comando giusto è:
Code:
datoRicevuto = byteAlto << 8 | byteBasso
vero? cioè | al posto di ||.
Logged

Global Moderator
Italy
Offline Offline
Brattain Member
*****
Karma: 325
Posts: 22498
Logic is my way
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Ah, una cosa. Per spedire un valore compreso fra 0 e 1023 ti bastano 2 byte: un tipo dati int (2 byte appunto). Lo spedisci con lowByte(dato) e highByte(dato) e lato Master lo ricostruisci semplicemente mettendo insieme i 2 byte in una variabile int: datoRicevuto = (byteAlto<<smiley-cool || byteBasso

Leo, il comando giusto è:
Code:
datoRicevuto = byteAlto << 8 | byteBasso
vero? cioè | al posto di ||.
Sì, scusa. Ho messo un | di troppo. Ho corretto.
Logged


Global Moderator
Italy
Offline Offline
Brattain Member
*****
Karma: 325
Posts: 22498
Logic is my way
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Rispondo a caso.

itoa:
io uso sprintf, visto che dicono che itoa non è C++ standard per cui è a discrezione del compilatore come implementare quella funzione e se implementarla.

itoa2:
perché devi andare ad occupare 6 byte di Sram e tutto l'aggravio di codice che porta l'uso di sprintf con sé quando puoi spedire 2 byte e leggere 2 byte per ricostruire il valore?

somma di controllo (checksum):
la quantità di bit che usi per la somma di controllo la definisci. Ovviamente più bit usi più è accurata e precisa nonché esente da errori la somma stessa. 8 bit possono essere già sufficienti, ma se vuoi più sicurezza adoperane pure 16.
Logged


Rome
Offline Offline
God Member
*****
Karma: 1
Posts: 643
La mia prima bromografata!!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

chiedevo di itoa solo per cultura, infatti sto facendo come suggerisci tu. Ovviamente mi funziona..

Sul checksum perdonami ma non ho capito una cosa.
se io faccio:
char buf[6];
e poi sommo buf[0]+buf[1]+ecc...
che tipo di numero mi esce fuori? e quanto grande?
io immagino di stare facendo: 256+256+256... (con 256 valore max..) quindi diciamo 256 * 6 = 1536, quindi per descriverlo tutto mi servono almeno 11bit, quindi 2 byte. Sto dicendo giusto?
se in caso invio solo un byte, avrò comunque un controllo, ma solo se la somma non supera i 256, altrimenti il controllo è sempre vero.
Logged

Global Moderator
Italy
Offline Offline
Brattain Member
*****
Karma: 325
Posts: 22498
Logic is my way
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Non è che devi fare la somma aritmetica, ma un "checksum".
http://it.wikipedia.org/wiki/Somma_di_controllo

Prendi un byte e ci "sommi" dentro i valori dei vari byte. Per "sommare" puoi usare diversi algoritmi, come i più semplici CRC o altri
http://it.wikipedia.org/wiki/Cyclic_redundancy_check

Se vuoi cose un po' più raffinate guarda qui:
http://www.das-labor.org/wiki/AVR-Crypto-Lib/en

Altrimenti un semplicissimo bit di parità:
http://it.wikipedia.org/wiki/Bit_di_parit%C3%A0

Oppure fai uno xor fra i vari byte. Insomma, di alternative ce ne sono molte.
Logged


Rome
Offline Offline
God Member
*****
Karma: 1
Posts: 643
La mia prima bromografata!!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

ok, dovrei aver capito dove sbagliavo..
Penso che sto facendo un pò troppe conversioni, però funziona.
posto i codici, se qualcuno ha voglia di dargli un'occhiata e correggermi..

Master:
Code:
#include "SoftwareSerial.h"

int EN_PIN = 4;
int LED_PIN = 13;
#define rxPin 2
#define txPin 3
int ind = 0;
char buf[8];
int dati[4][2];

SoftwareSerial rs485(rxPin, txPin);

void setup() {
  pinMode(EN_PIN, OUTPUT);
  Serial.begin(9600);
  rs485.begin(9600);
  pinMode (LED_PIN, OUTPUT);
}

void loop() {
  // inviare req(address '1' to '4', richiesta '5' to '8')
  req('1','5');
  delay(1);
  req('2','5');
  delay(1);
  req('3','5');
  delay(1);
  req('4','5');
  delay(1); 
}

void req(char addr, char reqType) {
  rs485.write(2);
  rs485.write(addr);
  rs485.write(reqType);
  rs485.write(addr+reqType);
  rs485.write(3);
 
  delay(50);  //Aspetta che risponde..
 
  if (rs485.available()>0) {
    if (rs485.read()==2) {
      for (int i=0; i<8; i++) {
        buf[i] = rs485.read();
      }
      ind = buf[0];
      if (ind == addr) {
        int sommaBytes = 0;
        for (int k=0;k<5;k++) {
          sommaBytes = sommaBytes + byte(buf[k]);
        }
        byte checksum = (buf[6] << 8 | buf[5]);           //////////// prima era: int checksum = (buf[6] << 8 | buf[5]);
                                              //////////// ma se buf[6] inizia per 1 checksum diventa negativo, e la comparazione con sommaBytes ritorna False.

        if ((buf[7]==3)&&(sommaBytes==checksum)) {
          Serial.println("Checksum and ETX ok!");
          ind = ind-48;
          Serial.print("Indirizzo : ");
          Serial.println(ind);
          Serial.println("SUM ok!");
          Serial.println("ETX ok!");
          Serial.print("Motore : ");
          Serial.println(ind);
          dati[ind][0] = (byte(buf[2]) << 8 | byte(buf[1]));
          dati[ind][1] = (byte(buf[4]) << 8 | byte(buf[3]));
          Serial.print("  Cella1 : ");
          Serial.println(dati[ind][0]);
          Serial.print("  Cella2 : ");
          Serial.println(dati[ind][1]);
        }
      }
    }
  }
}
..e uno degli Slave:
Code:
int EN_PIN = 4;
int LED_PIN = 13;
int cella1 = 1012;
int cella2 = 1003;
char addr = '1';
int reqType;
char buf[4];

void setup() {
  pinMode(EN_PIN, OUTPUT);
  Serial.begin(9600);
  Serial1.begin(9600);
  pinMode (LED_PIN, OUTPUT);
}

void loop() {
  while (Serial1.available()==0) {
    digitalWrite(LED_PIN, HIGH);
  }
  digitalWrite(LED_PIN, LOW);
  delay(10);
  if (Serial1.read()==2) {
    for (int i=0; i<4; i++) {
      buf[i] = Serial1.read();
    }
    Serial.print("Indirizzo : ");
    int ind = buf[0];
    Serial.println(char(ind));
    if (ind == addr) {
      Serial.print("Richiesta : ");
      reqType = buf[1];
      Serial.println(char(reqType));
      if ((buf[3]==3)&&(buf[0]+buf[1]==buf[2])) {
        Serial.println("SUM ok!");
        Serial.println("ETX ok!");
        if (reqType == '5') {
          invia();
        }
      }
    }
  }
}

void invia() {
  Serial1.write(2);
  Serial1.write(addr);
  Serial1.write(lowByte(cella1));
  Serial1.write(highByte(cella1));
  Serial.println("Valori celle:");
  Serial.println(lowByte(cella1));
  Serial.println(highByte(cella1));
  Serial1.write(lowByte(cella2));
  Serial1.write(highByte(cella2));
  Serial.println(lowByte(cella2));
  Serial.println(highByte(cella2));
  int checksum = (addr+lowByte(cella1)+highByte(cella1)+lowByte(cella2)+highByte(cella2));
  Serial.print("Checksum : ");
  Serial.println(checksum);
  Serial1.write(lowByte(checksum));
  Serial.println(lowByte(checksum));
  Serial1.write(highByte(checksum));
  Serial.println(highByte(checksum));
  Serial1.write(3);
}

Ovviamente ci sono ancora un sacco di Serial.print() per debuggare..

EDIT: lo scrivo solo se a qualcuno viene in mente di prendere spunto da questo codice, che c'è un piccolo errore. l'ho corretto e ho segnato il punto.
in più i delay sono da regolare a seconda della velocità di trasmissione. Quindi il codice così com'è non è affidabile al 100%.

Davide.
« Last Edit: May 21, 2012, 04:12:21 am by dab77 » Logged

Pages: [1]   Go Up
Jump to: