Conversione ascii/decimale/byte

Ciao,
ho cercato di riassumere nel titolo la mia esigenza ma adesso cerco di spiegarla bene:

Ricevo sulla seriale da un dispositivo che legge dei tag RFID una serie di caratteri codificati in ascii come questo:
230303336353931333239DA3
che si capisce meglio così:
2 30 30 33 36 35 39 31 33 32 39 D A 3

Tralasciando i caratteri di inizio, fine CR e LF (2,3 D, A) il dato è:
30 30 33 36 35 39 31 33 32 39
che convertito secondo la tabella è:
0 0 3 6 5 9 1 3 2 9
ora se lo converto in HEX ottengo:
22E56E1 diviso in byte:

02 2E 56 E1
Dulcis in fundo devo invertire i byte perchè il codice del tag è:

E1 56 2E 02, che è quello che mi aspetto

Vorrei capire se c'è un metodo più veloce e più bello per ottenere il risultato finale o se devo fare uno alla volta i passaggi descritti sopra.

Grazie

Sembri indicare che il 3 fa parte del trailer con CR e LF. Non fa invece parte del header?

0x03 0x02 ....(dati)... CR LF

Il Arduino è little endian, quindi se converti il numero che hai ricevuto in un unsigned long, verrà memorizzato in memoria con il byte meno significativo prima.

Vedere un esempio

ConversioneAsciiDecimaleByte - Wokwi ESP32, STM32, Arduino Simulator

bool ottieniCodice(byte* payload) {
  constexpr byte lunghezzaPayload = 10;
  constexpr byte intestazione1 = 'A'; // 0x03; (Usare 'A' per testare nel simulatore)
  constexpr byte intestazione2 = 'B'; // 0x02; (Usare 'B' per testare nel simulatore)

  static byte posizione = 0;
  static char txtCodice[lunghezzaPayload + 1];
  static enum : byte {OTTIENI3, OTTIENI2, OTTIENI_CODICE, OTTIENI_CR, OTTIENI_LF} passo = OTTIENI3;

  if (Serial.available() == 0) return false;

  int r = Serial.read();
  bool codicePronto = false;

  switch (passo) {
    case OTTIENI3:
      if (r == intestazione1) passo = OTTIENI2;
      break;

    case OTTIENI2:
      if (r == intestazione2)
        passo = OTTIENI_CODICE;
      else
        passo = OTTIENI3;
      break;

    case OTTIENI_CODICE:
      if (r >= '0' && r <= '9') {
        txtCodice[posizione++] = r;
        txtCodice[posizione] = '\0';
        if (posizione >= lunghezzaPayload) {
          posizione = 0;
          passo = OTTIENI_CR;
        }
      } else {
        posizione = 0;
        passo = OTTIENI3;
      }
      break;

    case OTTIENI_CR:
      if (r == '\r')
        passo = OTTIENI_LF;
      else
        passo = OTTIENI3;
      break;

    case OTTIENI_LF:
      if (r == '\n') {
        Serial.print("Ricevuto: "); Serial.println(txtCodice);
        uint32_t converti = strtoul(txtCodice, nullptr, 10);
        memcpy(payload, &converti, sizeof converti);
        codicePronto = true;
      }
      passo = OTTIENI3;
      break;
  }
  return codicePronto;
}

void setup() {
  Serial.begin(115200);
  Serial.println("Inserisci il codice");
}

void loop() {
  byte codice[4];
  if (ottieniCodice(codice)) {
    Serial.print("Codice esadecimale: ");
    for (byte i = 0; i < sizeof codice; i++) {
      if (codice[i] < 0x10) Serial.write('0');
      Serial.print(codice[i], HEX);
      Serial.write(' ');
    }
    Serial.println("\nInserisci il codice");
  }
}

Ciao,
ti confermo che la sintassi è come l'ho descritta, il micro la espone proprio così ad ogni lettura.
Grazie molte per il codice che hai postato, con le opportune modifiche si adatta perfettamente a quello che mi serve.
E grazie anche per la dritta sul insigned long, non lo sapevo.
In questo modo dovrei riuscire a fare tutto...ora ci lavoro!

fantastico - divertiti!

Ciao,
trovo una notevole difficoltà a trasformare i byte 0 0 3 6 5 9 1 3 2 9
nel numero decimale 36591329.
Ho provato con una formula matematica, ma evidentemente l'errore è concettuale, perchè il singolo byte non posso utilizzarlo come se fosse un numero.

Somma ad ogni cifra la distanza tra 0 (numero) e '0' (carattere)

Per sapere quanto vale prendi la tabella ascii

Grandissimo!,
ammetto che non ho dimestichezza con la tabella ascii, ma con il tuo suggerimento ho risolto: praticamente prima di elevare alla potenza tolgo al valore del byte la "distanza" 48.

Ok...un passo alla volta..
Ora ho il mio numero intero 36591329 che, se lo visualizzo in Hex mi da correttamente 22E56E1(che ho messo nella variabile" pluto")
Ho provato a inserire una variabile del tipo

unsigned long pippo

pippo = (unsigned long)pluto

e poi ho provato visualizzarla con un

Serial.println(pippo,HEX);

il risultato è ancora 22E56E1

Non ti seguo...

Il mio obiettivo è ottenere una variabile con i 4 byte invertiti, quindi:

E1 56 2E 02

Sinceramente io non ci ho capito nulla. Non so se quei numeri sono in una C string che stampi con serial.print() oppure:

uint32_t vin = 0x022E56E1;
Serial.println(vin, HEX);

Stamperà: 22E56E1.
Se invece si tratta di una c string occrrere diversamente da quanto vedrai:
Scrivi nel tuo sketch di test questa funzione:

void toBigE(uint8_t *ptr) {
    byte x = ptr[0]; // x = 2
    ptr[0] = ptr[3];
    ptr[3] = x;
    x = ptr[1]; // x 2e
    ptr[1] = ptr[2];
    ptr[2] = x;

}

void setup() {
    Serial.begin(9600);
    uint32_t vin = 0x22E56E1;
    toBigE( (uint8_t*)&vin );
    Serial.println(vin, HEX);
}

La funzione lavora solo con tipo grandi 4 byte.
Il Serial.println(vin, HEX); stampera:
E1562E02 che il vaore di vin dopo che la toBigE() ha operato.

Ciao.

Ciao,
la funzione che mi hai suggerito funziona correttamente e infatti mi genera il codice del tag corretto.

A questo punto sto tentando di convertire quel dato in un array di 4 byte del tipo:

dato[4]={E1,56,2E,02)

ma no riesco a capire come fare.

Ci sono diversi modi per farlo, ma in realtà non c'è una vera conversione da tipo uint32_t e tipo array. Il modo consigliato è quello di fare ricorso alle union. Io non le ho usate le union e al loro posto ho trasformato/convertito un indirizzo di memoria in un puntatore a tipo uint8_t.

La conversione è banale:

uint8_t *pointer = (uint8_t*)&nomeVariabile;

Da adesso pointer può essere usato con gli indici per accedere al prossimo byte.

Certo che non è facile da spiegare come funziona, perché appena lo intuisci ti si apre una nuova dimensione.

Posso solo consigliarti questo articolo che ho scritto nel mio blog:

Ciao.

Crediamo che sia:

uint8_t * pointer = (uint8_t *) & nomeVariabile;

Esatto, mi ero accorto della mancanza dell'asterisco ma non ho avuto il tempo di modificarlo e poi l'ho dimenticato. Oggi mi sono ricordato e ho editato il post.

In breve avevo scritto così:

// errore manca l'asterisco.
uint8_t *pointer = (uint8_t)nomeVariabile;

Che avrebbe dato errore di compilazione.

PS: Grazie per la segnalazione.

Ciao.

Io ti segnalavo la mancanza dello ampersand (&)
Non dell'asterisco

In italiano: e commerciale :slight_smile:

Funziona!!
Innanzitutto grazie a tutti e soprattutto a Maurotec per aver centrato al volo le mie esigenze.
poi...
Ho cercato di capire il funzionamento dei puntatori prima di implementare il loro utilizzo nel codice, e quindi:
ho capito cosa sono e come si utilizzano con * e &
.....non mi è chiaro però quando e perchè usarli...dovrò approfondire bene.

Grazie ancora!!!

Posso sintetizzare il perché, mentre per il quando è bene non sintetizzare. In C (senza ++) non c'è altra scelta che usare i puntatori.
Supponi di avere un oggetto (un simbolo) mioOggetto che contiene tante informazioni e diciamo che è grande 64-byte. Passare come argomento questo oggetto ad una funzione per valore a come conseguenza la copia locale dell'oggetto, all'interno della funzione non operi su mioOggetto ma su una copia locale. La copia consuma risorse e tempo CPU. Mentre se alla funzione passi come argomento un puntatore a mioOggetto viene fatta copia locale del puntatore che è grande 16-bit. Tramite il puntatore da dentro la funzione operi su mioOggetto.

Poi è l'unico modo per accedere alla ram in modo arbitrario.

Ciao.