Comunicazione MODBUS RS 485 problemi di interpretazione con Arduino

dovrei salvare in una variabile di Arduinbo il valore ricavato da un analizzatore di rete elettrica che comunica in protocollo RS 485 Modbus RTU. Questo protocollo consiste in richieste al dispositivo tramite frame e risposte dallo stesso con altro frame dove si possono trovare i valori richiesti.

i dati utilizzati come risposta sono in formato floating point 32bit IEEE Per capire un po' questo formato ho trovato un software che crea le stringhe e le invia registrandone le risposte dallo slave. Allego le schermate dell'applicazione per comprendere:

formatting link
Facendo un'interrogazione dei registri interessati (2, sempre adiacenti) la stringa di

formatting link
I valori utili per il dato da estrapolare sono questi [43] [61] [B3] [33]

formatting link
Il formato selezionato nella immagine mi restituisce il valore corretto della lettura (in questo caso in volt)

Alla fine di questa pappardella vi chiedo: esiste qualche modo/metodo/libreria che mi converta questi 4 numeri esadecimali in un valore rispondente al valore da ottenere?

complicato... Grazie

--
Franco
Reply to
Franzthepanz
Loading thread data ...

formatting link

nell'esempio:snelheid = u.fval;tu devi fare l'opposto riempire l'array snelheide poi avere il float:u.fval=snelheid;la conversione e' fatta con l'assegnazione che fa una conversione di tipo il penultimo post assomiglia a cio' che ho fatto in altri linguaggi in pratica hai due variabili "sovrapposte" negli stessi 4 indirizzi di memoria ram se ci metti un float (32 bit) puoi vedere ogni byte come e' oppure se tu metti i 4 byte(che e' quel che vuoi fare) leggi la variabile float equivalente, molto tempo fa avevo fatto una demo (in turbopascal) che permetteva di impostare 1/0 ogni singolo bit e vedere cosa usciva. Ho usato cose simili in plm/80 bascom, ma finora non ho ancora usato arduino.

ciao delo

"Franzthepanz" ha scritto nel messaggio news:mtdvm7$3no$ snipped-for-privacy@speranza.aioe.org...

Reply to
delo

Il 17/09/15 11:04, Franzthepanz ha scritto:

Mai usato Arduino, ma se il linguaggio e' C standard, puoi utilizzare una "union" sovrapponendo una variabile float a un array di 4 byte.

Es.

union Data { float variabile_1; uchar byte[4]; } data;

Da una parte riempi i 4 byte e dall'altra leggi il valore in float.

Ciao G.

Reply to
Giuseppe³

Dimenticavo:

per accedere alle variabili:

data.byte[0]=1; data.byte[1]=2; data.byte[2]=3; data.byte[3]=4;

printf("%f",data.variabile_1);

Ovvio che questi valori assegnati non significano nulla.

Ciao G.

Reply to
Giuseppe³

Dopo aver strapazzato l'unico neurone rimasto, Giuseppe=c2=b3 il 17/09/2015 ha pensato bene di dire:

Caspita, spiegazioni "ermetiche"... purtroppo sono un apprendista e certe cose che possono sembrare (a voi) ovvie per me sono arabo. :-)

--
Franco
Reply to
Franzthepanz

Il 17/09/15 22:16, Franzthepanz ha scritto:

Non ho capito cosa non hai capito :) In pratica le union sovrappongono nella stessa area di memoria le variabili che sono specificate all'interno come membri. Nell'esempio variabile_1 usa la stessa area di memoria dell'array byte.

Tutto questo permette di far accedere il compilatore all'area di memoria prelevando o scrivendo i dati sotto varie forme. In questo esempio se accedi come data.variabile_1 carichi il dato in memoria come float, se invece accedi come data.byte[] carichi i dati come char (di solito a 8 bit, ma non e' detto, dipende dal target).

Si potrebbe fare la stessa cosa usando i puntatori, ma e' un po' piu' complesso da capire pur essendo in definitiva la stessa cosa.

Se mi spieghi cosa non ti e' chiaro, posso essere piu' esauriente.

Ciao Giuseppe

Reply to
Giuseppe³

Dopo aver strapazzato l'unico neurone rimasto, Giuseppe=c2=b3 il 18/09/2015 ha pensato bene di dire:

CUT

matematica che di programmazione.

--
Franco
Reply to
Franzthepanz

Il 19/09/15 13:32, Franzthepanz ha scritto:

Certi compilatori accettano come tipo di dato unsigned char. Per comodita' io mi sono fatto delle typedef chiamandolo uchar. E' piu' comodo scrivere uchar invece che unsigned char, e sono talmente abituato ad usarlo che mi e' stato un riflesso automatico :)

Prendendo come target un sistema ad 8 bit, devi vedere la memoria come un insieme continuo di locazioni di memoria che vengono definite come byte.

Sperando di non farti casino ulteriore, provo a sintetizzare.

Ogni dato viene memorizzato in questi byte secondo delle modalita' ben specifiche.

1 char (8 bit) occupa 1 byte e i valori che puo assumere sono da 0 a 255 1 int (16 bit) occupa 2 byte contigui e puo' assumere valori da 0 a 65535.

Gia' a questo punto bisogna aprire 2 parentesi: a) Per definire valori con segno si definisce il bit piu' significativo come segno per cui i valori che possono essere espressi sono per il char da -128 a +127, mentre diventano -32768 a +32767 per l'int. b) A seconda del micro utilizzato, l'int puo' essere memorizzato in modalita litte endian o big endian a seconda che il primo byte indichi i bit da 0 a 7 (LSB) o quelli da 8 a 15 (MSB)

A questo punto dovrebbe essere evidente che l'int puoi vederlo come due char consecutivi per cui istruendo opportunamente il compilatore C puoi far coincidere l'indirizzo di memoria dell'int con quello del primo char chiamando ad esempio la variabile int come iVal e i char come cVal0 e cVal1.

Esempio:

----------------- memaddress 0 | iVal | cVal0 | | |-------| memaddress 1 | | cVal1 | -----------------

Quando il compilatore legge o scrive iVal, imposta automaticamente memaddress 0 e memaddress 1 con il valore a 16 bit di iVal. Quando il compilatore legge o scrive cVal0 o 1, imposta memaddress 0 o memaddress 1 rispettivamente. A questo punto immagina di scrivere cVal0 con 1 e cVal1 con 2. Se vai a leggere iVal, il compilatore legge memaddress 0 e memaddress 1 per cui il valore letto sara' 1 sui bit da 15 a 8 e 2 sui bit da 7 a 0. In binario puoi vederlo come 0000000100000010 quindi 2^8 + 2^1 = 258.

Se sei arrivato fino a qui sano :) , possiamo estendere il concetto ai valori long e float. long prende 4 byte contigui mentre float (standard IEEE) prende a sua volta 4 byte.

La domanda sorge spontanea che differenza c'e' tra long e float? Risposta il modo in cui viene interpretata la sequenza di bit. Per il long la storia e' uguale a quella dell'int mentre per il float e' un po' piu' incasinata e se sei interessato puoi cominciare a vedere qui: "

formatting link
"

Alla fine di tutto questo panegirico, quello che interessa a te e' prendere due int da 16 bit, metterli in 4 byte contigui e rileggere il valore interpretando il dato come float. L'interpretazione float la fa il compilatore, per cui puoi fregartene. L'unica cosa e' sovrapporre due int con un float. Scrivi due int e rileggi il tutto come float. Per fare questo la maniera piu' elegante e' usare le union, quella piu' usata dai "duri e puri" sono i puntatori.

In caso di dubbi, chiedi. Ciao Giuseppe

Reply to
Giuseppe³

"Franzthepanz" ha scritto nel messaggio news:mtjh53$ur8$ snipped-for-privacy@speranza.aioe.org...

ora ti incasino un po le idee anche io :-)

e' una questione di come i dati vengono interpretati o usati.

prendi 4 locazioni di memoria consecutive e ci scrivi dentro 4 valori: [0] = 0x63 [1] = 0x69 [2] = 0x61 [3] = 0x6F

li puoi considerare come 4 valori da 8 bit indipendenti tra loro oppure come testo ascii sono la parola "ciao" oppure come valore 32bit BigEndian sono il valore 0x6369616F = 1667850607 oppure come valore 32bit LittleEndian sono il valore 0x6F616963 =

1868654947 oppure come floating point sono il valore 6.976153E+28

come vedi sono sempre gli stessi 4 byte, ma da come li unisci cambia il significato o valore del risultato

la union che ti hanno proposto e' un tipo si dato che in C ti permette di usare uno spazio di memoria unico per tipi di dato diversi, e nel tuo caso ti aiuta a fare la conversione voluta.

se il tuo valore floating point e' memorizzato in un array di char nel giusto ordine (esponente seguito da mantissa)

- unsigned char value[4]; un'altra strada e' la semplice istruzione:

- *(float*)value; leggerebbe il valore dell'array come floating point, esattamente come la union

Reply to
alfio

Da quello che ho capito io lui vuole usarlo nel programma, mentre il tuo si stema funziona solo in fase di compilazione.

Sempre se ho capito giusto.

mandi

Reply to
zio bapu

Il 19/09/15 18:05, zio bapu ha scritto:

No, non e' come hai capito. Prova a leggere la risposta di Alfio, forse ha sintetizzato meglio il concetto.

Ciao Giuseppe

Reply to
Giuseppe³

Ok, lavevo immaginato che c'era il trucco. Cmq non sono arrivato ancora al capitolo typdef lol

CUT E menomale che hai sintetizzato... ;-) :-Z A parte gli scherzi, ti ringrazio della spiegazione ma io purtroppo sono ancora indietro con questi argomenti e purtroppo ho capito molto poco .

Non approfitto oltre della tua pazienza, sei stato fin troppo disponibile.

--
Franco
Reply to
Franzthepanz

Dopo aver strapazzato l'unico neurone rimasto, alfio il 19/09/2015 ha pensato bene di dire:

questione matematica che di programmazione.

Ottimo !

Questo l'avevo gia intuito nelle mie letture e tu l'hai spiegato in maniera "cristallina".

--
Franco
Reply to
Franzthepanz

Il 19/09/15 20:51, Franzthepanz ha scritto:

Forse un esempio vale piu' di mille parole :)

Partendo dai dati che ricevi dal tuo strumento come riportato qui "

formatting link
"

I dati che riportano la tensione, come da te indicato sono:

0x43 0x61 0xB3 0x33

quindi scriviamo:

int main () { union Data { unsigned char ValoriInByte[4]; float ValoreFloat; } data;

/* Codice di gestione del protocollo che ricava i 4 byte */

data.ValoriInByte[0]=0x43; data.ValoriInByte[1]=0x61; data.ValoriInByte[2]=0xB3; data.ValoriInByte[3]=0x33;

/* Visualizzazione valore in float */ printf("%f",data.ValoreFloat);

}

Lavorando invece con i puntatori:

int main () { unsigned char ValoriInByte[4];

/* Codice di gestione del protocollo che ricava i 4 byte */

ValoriInByte[0]=0x43; ValoriInByte[1]=0x61; ValoriInByte[2]=0xB3; ValoriInByte[3]=0x33;

/* Visualizzazione valore in float */ printf("%f",*(float*)ValoriInByte); }

Tutto qui :) Ciao Giuseppe

Reply to
Giuseppe³

"Franzthepanz" ha scritto nel messaggio news:mtkb3f$8ns$ snipped-for-privacy@speranza.aioe.org...

in linguaggio C il nome di un array equivale al suo indirizzo in memoria, se hai studiato almeno un po' le basi, gli indirizzi possono anche essere memorizzati in variabili che si definiscono "puntatori"

unsigned char * p;

quel "p" e' un puntatore, ossia puo' contenere un indirizzo di memoria.

p = value; e' una assegnazione corretta, proprio perche' value (nome dell'array di char) rappresenta l'indirizzo in cui l'array e' memorizzato.

avendo l'indirizzo di memoria in una variabile la si puo' usare per leggere il contenuto memorizzato a quell'indirizzo. per farlo si usa il simbolo * davanti al nome del puntatore

*p ritorna un singolo "unsigned char", il primo elemento dell'array. *p e p[0] sono 2 modi per leggere il primo elemento dell'array.

unsigned char primo; primo = *p; ed equivale a primo = p[0]; ed equivale anche a primo = value[0]; e primo = *value;

tutto questo per dire che value e' si un array di unsigned char, ma il simbolo value e' anche un puntatore a unsigned char.

ma come per tutte le variabili, anche con i puntatori si puo' usare l'operazione di "cast", ossia forzare una conversione esplicita in modo che punti ad un tipo di dato diverso. la scrittura: (float*)value dice di interpretare value, non piu' come puntatore a "unsigned char" ma come puntatore a "float", quindi con

*(float*)value non verra' piu' letto un solo byte, ma 4 (un float occupa 4 byte)

alla fine, per rispondere alla tua domanda, questo e' il codice per convertire il tuo array di 4 byte in float: float f; f = *(float*)value;

i puntatori sono tipi di dati a volte pericolosi, ma imparare ad usarli e' basilare per poter padroneggiare il linguaggio C. se ti era piu' chiaro l'esempio con union, usa quello, tanto le 2 strade portano allo stesso risultato.

Reply to
alfio

Dopo aver strapazzato l'unico neurone rimasto, alfio il 19/09/2015 ha pensato bene di dire:

CUT

Grazie mille a te ed anche a Giuseppee per la pazienza e la competenza. Le ultime vostre risposte sono state messe nella mia "libreria di testi da non dimenticare". Alle prossime

--
Franco
Reply to
Franzthepanz

ElectronDepot website is not affiliated with any of the manufacturers or service providers discussed here. All logos and trade names are the property of their respective owners.