Va bene...domani faccio qualche prova e vi aggiorno!
Lucio
Va bene...domani faccio qualche prova e vi aggiorno!
Lucio
#include <Arduino_CAN.h>
static uint32_t const CAN_ID = 0x20;
void setup()
{
Serial.begin(115200);
if (!CAN.begin(CanBitRate::BR_250k))
{
Serial.println("CAN.begin(...) failed.");
for (;;) {}
}
}
uint8_t const msg_text[] = "buongiorno";
uint8_t const msg_data = analogRead(A0);
void loop()
{
//CanMsg const msg(CanStandardId(CAN_ID), sizeof(msg_text), msg_text);
CanMsg const msg(CanStandardId(CAN_ID), sizeof(msg_data), msg_data);
if (int const rc = CAN.write(msg); rc < 0)
{
Serial.print ("CAN.write(...) failed with error code ");
Serial.println(rc);
for (;;) { }
}
delay(1000);
}
Allora sono arrivato qui...i dati tipo stringa vengono inviati correttamente (come mi aspettavo "buongiorno" viene troncato, parole più corte rimangono intere) in esadecimale...quindi poi dovrò riconvertirli con qualche funzione (probabilmente esiste già?).
Il dato numerico mi rimane ancora oscuro invece.
Sono partito da un analogRead() che quindi restituisce valori interi ma non capisco come spedirli dal momento che non posso passare alla funzione msg() degli int...
??? in che senso ? se spedisci "CIAO" otterrai dentro a msg la parte data che è già una stringa.
al massimo puoi copiare la stringa in un'altra variabile stringa.
uint8_t miaStringa[10];
...
CanMsg const msg = CAN.read();
strncpy(miaStringa,msg.data,10); // copia dati ricevuti in miaStringa
Serial.print("msg.data ="); Serial.println(miaStringa);
Serial.print("miaStringa="); Serial.println(msg.data); // dovrebbero essere uguali
...
Prima di tutto qui c'e' un errore, in quanto analogRead() restituisce un numero maggiore di 255
uint8_t è unsigned a 8 bit... quindi 0-255
Mi riferivo al riconvertire la stringa da hex a testo...ho provato facendo CanMsg printTo(msg)
come mi avevi consigliato tu ma non stampa nulla.
Ho provato anche mettendo un numero intero ma ottengo sempre questo errore Compilation error: invalid conversion from 'uint32_t {aka long unsigned int}' to 'const uint8_t* {aka const unsigned char*}' [-fpermissive]
Io ti ho detto di fare Serial.println(msg.data); la printTo() è interna alla libreria, puoi guardarla per vedere che lei semplicemente stampa tutti e 8 i byte in esadecimale, ovvero stampa di un testo il codice ascii di ogni singola lettera
Il problema dei numeri che hanno più di un byte è spedirli perchè devi spezzarli in più byte, spedirli e poi rimetterli insieme.
L'esempio CANwrite già ha dentro un esempio per spedire un numero, spedisce un contatore a 32 bit. Anche un float è 32 bit, un int su arduino R3 è 16 bit (2 byte) ma su R4 è 32 bit
uint32_t msg_cnt = 4660; // in esa 0x00001234 4 byte 0,0,12,34
...
uint8_t const msg_data[] = {0,0,0,0,0,0,0,0};
memcpy((void *)(msg_data + 4), &msg_cnt, sizeof(msg_cnt));
quel pezzo scrive i 4 byte del contatore dentro al msg dalla posizione +4 ovvero gli ultimi 4 byte
P.S. il msg_data SEMPRE un array di 8 byte deve essere, devi solo capire come scriverci dentro
in ricezione (ma non l'ho provato): secondo parametro memcpy dovrebbe essere senza & perchè un array è già un puntatore
uint32_t mioVal; // 4 byte
...
CanMsg const msg = CAN.read();
memcpy((void *)(mioval), msg.data+4, sizeof(mioVal));
Serial.print("mioVal="); Serial.println(mioVal);
@lucio_lucio: Perdona ma ... UNO R4 bella scheda con una MCU potente, CAN bus, ecc. però ... se prima di utilizzare cose di una certa complessità non si studiano le BASI di ciò che si usa (perché qui è evidente che mancano alcune BASI) ed in particolare del linguaggio e si spera di andare avanti facendo dei copia/incolla di esempi e suggerimenti, t'assicuro ... NON si arriva da nessuna parte!
Fossi in te mi fermerei un attimo, mi metterei a STUDIARE e poi riprenderei in mano il progetto con le idee molto più chiare e sicuramente in modo molto più proficuo.
Poi ... fai tu ...
Guglielmo
@nid69ita okay ora ho capito meglio soltanto che non capisco perché venga creato l'array vuoto per poi andare a riempirlo con memcpy...non basta inviare il dato e basta? In fondo mi pare di aver visto che comunque i byte mancanti vengono riempiti con degli zero...
@gpb01 sono d'accordissimo con te e quello che dici è lo stesso approccio che ho già seguito quando ho iniziato con Arduino...il mio attuale problema è che semplicemente non mi sono mai trovato a lavorare con tipi di dati come uint_8t e ciò mi manda in confusione.
Sono solo dei tipi definiti in <stdint.h> e servnoo ad uniformare e rendere univoci i tipi, difatti, su Arduino UNO un 'int' è a 16 bit, su un ESP32 un 'int' è a 32 bit e questo crea confusione.
I tipi definiti in <stdint.h> invece indicano ESATTAMENTE la lunghezza e quindi sono gli stessi su tutte le piattaforme.
Guglielmo
Ottimo, grazie mille per il chiarimento!
Ma perchè questi tipi possono contenere sia numeri che caratteri invece di essere univoci come gli altri (tipo int, float, double, ecc)?
Che intendi?
Per la MCU numeri, caratteri, ecc. sempre sequenze di bit messi in bytes sono ... è poi la rappresentazione verso l'uomo che cambia, ma non certo per la macchina ...
Esempio: valore decimale 64, sta in un byte, lo puoi scrieve come 64, lo puoi scrivere in hex come 0x41 o lo puoi scrivere come carattere 'A' ... sempre un byte che contiene 0100 0001 sarà ... poi tu te lo fai mostrare come vuoi, ma internamente non cambia nulla.
Guglielmo
Certo questo lo so...intendevo dire che, ad esempio, se dichiaro la variabile x come int posso assegnarle soltanto valori interi mentre se le assegnassi un carattere il compilatore mi darebbe errore. Invece con uint ho notato che è sufficiente dichiarare come array di char e accetta anche i caratteri, cosa che invece "normalmente" mi richiederebbe di cambiare tipo di dato da int a String.
Ne sei proprio sicuro?
Secondo me se assegni 'A' ad un 'int' non ti da nessun errore ... e nemmeno un warning ...
Guglielmo
String NON è una variabile ma una classe.
Che usi per oggetti String. Un oggetto String non è una semplice variabile che contiene un valore. Non lo vedi ma internamente ha un array di char o comunque una memoria gestita a byte (uint8), un contatore di lunghezza, etc. Poi la classe implementa comandi e operatori che nascondono come è fatta dentro.
La stringa C classica è una cosa "particolare". Un array di byte, terminata da null che chiamiamo stringa, qualcosa di "particolare" decisa da chi ha inventato il liinguaggio C .
Un array di int o float è un array/vettore senza particolarità.
P. S. uint non ha nessun significato
Una variabile int a 16 bit (2 byte) se voglio posso anche stampare a video i 2 byte che lo compongono, anche come 2 caratteri ma ha senso? No.
Allora eccomi tornato...dopo aver fatto alcune prove e capito un po' meglio tutta la questione sono riuscito a mandare delle float attraverso il CanBus. Il problema ora è che non capisco come salvare i dati in arrivo per convertirli da HEX a decimale e ri-salvarli in una float da poter usare.
Vi allego i codici del trasmettitore e ricevitore.
SENDER
#include <Arduino_CAN.h>
static uint32_t const CAN_ID = 0x01; //Indirizzo CAN
void setup() {
Serial.begin(115200); //Inizializza seriale
if (!CAN.begin(CanBitRate::BR_250k)) {
Serial.println("CAN.begin(...) failed.");
for (;;) {}
}
}
float msg_cnt = 22.30; //Contenuto messaggio
void loop() {
/* Assemble a CAN message with the format of
* 0xCA 0xFE 0x00 0x00 [4 byte message counter]
*/
uint8_t const msg_data[8] = {}; //Buffer messaggio
memcpy((void *)(msg_data), &msg_cnt, sizeof(msg_cnt)); //Copia contenuto messaggio nel buffer
CanMsg const msg(CanStandardId(CAN_ID), sizeof(msg_data), msg_data); //Spedisce messaggio
/* Transmit the CAN message, capture and display an
* error core in case of failure.
*/
if (int const rc = CAN.write(msg); rc < 0) {
Serial.print("CAN.write(...) failed with error code ");
Serial.println(rc);
for (;;) {}
}
/* Only send one message per second. */
delay(100);
}
RECEIVER
#include <Arduino_CAN.h>
/**************************************************************************************
* SETUP/LOOP
**************************************************************************************/
void setup() {
Serial.begin(115200);
while (!Serial) {}
if (!CAN.begin(CanBitRate::BR_250k)) {
Serial.println("CAN.begin(...) failed.");
for (;;) {}
}
}
void loop() {
if (CAN.available()) {
CanMsg const msg = CAN.read();
if (msg.getStandardId() == 0x00){
Serial.print("0x00 ");
Serial.println(msg);
}
if (msg.getStandardId()==0x01){
Serial.print("0x01 ");
Serial.println(msg);
}
}
}
RISOLUZIONE!
Ciao a tutti, finalmente ho risolto il problema, spero che il mio lavoro sia di aiuto per coloro che vorranno ripetere lo stesso progetto.
Innanzi tutto ogni elemento della rete CAN deve avere un ID, da dichiarare in questo modo prima del setup:
static uint32_t const CAN_ID = 0x01; //Indirizzo CAN, cambiare a piacimento
Successivamente, nel setup, bisogna inizializzare la comunicazione cosi:
if (!CAN.begin(CanBitRate::BR_250k)) {
Serial.println("CAN.begin(...) failed.");
for (;;) {}
}
In questo caso, come nell'esempio della libreria, ho aggiunto uno statement di controllo che ritenta la connessione nel caso questa non vada a buon fine.
Ed infine, per facilitare le operazioni di lettura/scrittura, ho scritto delle funzioni apposite:
void sendCanFloat(float msg_cnt) {
uint8_t const msg_data[8] = {}; //Buffer messaggio
memcpy((void*)(msg_data), &msg_cnt, sizeof(msg_cnt)); //Copia contenuto messaggio nel buffer
CanMsg const msg(CanStandardId(CAN_ID), sizeof(msg_data), msg_data); //Spedisce messaggio
if (int const rc = CAN.write(msg); rc < 0) {
Serial.print("CAN.write(...) failed with error code ");
Serial.println(rc);
}
}
void sendCanChar(char msg_cnt) {
uint8_t const msg_data[8] = {}; //Buffer messaggio
memcpy((void*)(msg_data), &msg_cnt, sizeof(msg_cnt)); //Copia contenuto messaggio nel buffer
CanMsg const msg(CanStandardId(CAN_ID), sizeof(msg_data), msg_data); //Spedisce messaggio
if (int const rc = CAN.write(msg); rc < 0) {
Serial.print("CAN.write(...) failed with error code ");
Serial.println(rc);
}
}
char readCanChar(int address) {
if (CAN.available()) {
CanMsg const msg = CAN.read();
const uint8_t* buffer = msg.data;
char res;
memcpy(&res, buffer, sizeof(buffer));
if (msg.getStandardId() == address) {
return res;
}
}
}
float readCanFloat(int address) {
if (CAN.available()) {
CanMsg const msg = CAN.read();
const uint8_t* buffer = msg.data;
float res;
memcpy(&res, buffer, sizeof(buffer));
if (msg.getStandardId() == address) {
return res;
}
}
}
Chiaramente si possono creare altre funzioni simili per decodificare altri tipi di dati. L'utilizzo di queste nel codice principale avviene così:
sendCanChar('a'); //carattere da inviare
char risposta = readCanChar(0x00); //indirizzo da cui leggere
sendCanFloat(123.45); //valore da inviare
float risposta = readCanFloat(0x00); //indirizzo da cui leggere
Di nuovo, spero che questo post sia di aiuto.
Un saluto,
Lucio
Seriamente?
ti rendi conto che hai commesso ameno tre errori, 2 gravi?
se qualcuno segue questi tuoi "esempi risolutivi" va semplicemente a fare danni al suo programma
come tu te ne stai facendo adesso
non ci credi?
static uint32_t const CAN_ID = 0x01; //Indirizzo CAN, cambiare a piacimento
sai cosa vuol dire una dichiarazione static nel preambolo e non in una funzione?
uint8_t const msg_data[8] = {};
una dichiarazione di costante "vuota"?
3) certo, perché qui si "scrive" la costante
memcpy((void*)(msg_data), &msg_cnt, sizeof(msg_cnt));
senza assicurarsi che sia stata "pulita" (si tratta di una variabile "locale", e questo per buona grazia non lo conto come errore)
4) e qui poi ci si rende conto che non si tratta di dimenticanze, ma proprio errori in pieno
char readCanChar(int address) {
if (CAN.available()) {
CanMsg const msg = CAN.read();
const uint8_t* buffer = msg.data;
char res;
memcpy(&res, buffer, sizeof(buffer));
if (msg.getStandardId() == address) {
return res;
}
}
}
di nuovo buffer è costante, ma scritto, di nuovo senza controllare ne che la sua dimensione sia corretta ne che sia stato svuotato
ma lo si assegna alla memoria indirizzata da una variabile carattere (1 byte, scritti 8, 7 sovrascrittti da chissà cosa)
inoltre non vedo ne un controllo di disponibilità CAN.available(), consigliato negli esempi ne un abbozzo di sincronismo, nulla garantisce che mentre trasmettiamo un float il ricevitore si aspetti un int
tutto questo non è ne buona ne pessima programmazione, è semplicemente programmazione pericolosa
segnalarlo come "soluzione" è pericoloso per i niubbi
io proporrei ai moderatori di togliere il flag "soluzione"
@Salvorhardin: ... è un po' di tempo che, qui sul forum, vedo gente che usa attributi come 'static' o 'const' ed anche 'volatile',così alla leggera, senza porsi minimamente il problema di COSA significhino e di QUANDO e COME vadano utilizzati
Mi sa che c'è in giro (YouTube o altrove) qualche tutorial ... ad mentula canis
Guglielmo