Risolto: giochiamo con gli array

Ciao a tutti, per studio e curiosità mi sono messo a giocare un po' con gli array.
Praticamente ho scritto un programma stupido, dove nel setup scrivo all'interno di due array bidimensionali, numeri progressivi, per poi leggerli nel loop e metterli in un array di char.
La finalità è quella di avere un array char composto da un header seguito poi dai numeri che sono all'interno degli array bidimensionali.
Ho scritto questo:

int rele1[3][5];
int rele2[2][7];
byte A = 0;
byte indice = 0;
char buffer_rele[90];
boolean one_time = false;
void setup() {
  Serial.begin (115000);

  for (byte i = 0; i < 3; i++) {
    for (byte x = 0; x < 5; x++) {
      rele1[i][x] = A;
      A++;
      Serial.println(rele1[i][x]);
    }
  }
  Serial.print("A"); Serial.println(A);
  for (byte i = 0; i < 2; i++) {
    for (byte x = 0; x < 7; x++) {
      rele2[i][x] = A;
      A++;
      Serial.println(rele2[i][x]);
    }
  }

  A = 1;
  buffer_rele[0] = 'T';
  Serial.print("rele1"); Serial.println(rele1[2][4]);
  Serial.print("rele2"); Serial.println(rele2[1][6]);
  delay(2000);
}

void loop() {
  if (one_time == false) {
    for (byte i=0;i<3;i++){for(byte x=0;x<5;x++){forma(rele1[i][x]);indice=indice+2;}
                          }
                          indice=indice+1;
    Serial.print("rele2"); Serial.println(rele2[1][3]);
    for (byte a = 0; a < 2; a++) {
      for (byte b = 0; b < 7; b++) {
        forma(rele2[a][b]);
        indice = indice + 2;
      }
    }
    buffer_rele[indice + 1] = "\0";
    Serial.print("indice"); Serial.println(indice);
    indice = 0;
    Serial.print("array "); Serial.println(buffer_rele);
    one_time = true;
  }
}
void forma(int X) {       //per convertire int in char

#define N 2
  char a[N];
  Serial.print("X"); Serial.println(X);
  itoa(X, a, 10); //converto il numero int X in char
  byte l1 = strlen (a);
  memmove (a + (N - l1), a, l1);
  memset (a, '0', N - l1);    //riempio di 0 i vuoti
  for (byte i = 0; i < N; i++) {
    buffer_rele[indice + i + 1] = a[i];
  }
  Serial.println("esco");
}

La funzione forma() serve per convertire int in char e metterlo in fila nel buffer.
Beh, si blocca...e non capisco perchè...
Se nel loop faccio eseguire solo rele1 o solo rele2 funziona bene, ma se li metto insieme si impalla.
Dove sbaglio??
Mi sembra di no fare cose sbagliate.

Testa la funzione forma con numeri da 0 a 9. Se X diventa 10 il buffer

char a[N];

risulta essere troppo piccolo.

Poi non capisco perché il memove, se a è array si 1 carattere più il terminatore di c string allora N-l1 = 0.
Per di più memmove sullo stesso array, booo N=2 l1= 2.

Prova anche formattare il codice tramite l'ide con Ctrl-T (mi pare).

Poi in che senso si impalla?
Se si blocca è sintomo di pasticcio con array.

Ciao.

Io confesso di non aver capito, oltre un poco a cosa vuole realmente fare, soprattutto come lo sta facendo e perché in quel modo così complicato...

Generalmente preferisco prima avere chiaro il problema/requisito, poi si può passare alla codifica. Quindi la mia domanda è: puoi chiarire esattamente cosa vuoi fare? Ossia partendo da due array (che possono essere anche solo vettori ossia array unidimensionali, e poi il problema è lo stesso per uno, tre, cinquantatre, perché due? vabbé...) di valori interi, che sono due byte, vuoi arrivare ad un array di caratteri ossia una "stringa C", ma fatta come? Insomma, devi sempre definire bene COSA vuoi fare, e solo dopo passare a COME farlo.

Per dire, non lo hai spiegato (e se vuoi che la gente ti aiuti devi farlo...), ma guardando il codice mi sembra di capire che i singoli valori li vuoi rappresentare con 2 caratteri, con uno zero iniziale (qundi il 3 diventa "03"), e questo va bene a patto che tu sia certo che i valori vadano da 0 a 99 al massimo. Nell'esempio rientrano inq uesto intervallo, ma allora a che serve usare gli "int" che arrivano da -32768 a 32767?
Poi evita i nomi di variabili in maiuscolo come "X", per convenzione sono solo le costanti o i simboli, così come i nomi di funzioni meglio renderli più autoesplicativi, quindi non "void forma(int X)" ma meglio "void inserisciIntero(int valore)"... E per finire, se una cosa la devi fare solo una volta ti basta metterla nella setup(), evita inutili variabili flag come la "one_time".

Detto questo, ti faccio un esempio (ma è quello che chiedo), diciamo che ho un array di 10 valori interi:

byte arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

Voglio ottenere una stringa con un primo carattere marcatore, es. 'X', poi i valori espressi in due caratteri, ossia:

"X01020304050607080910"

Ok, questo lo fai con molte meno istruzioni ed in modo molto più chiaro così:

// Arradi di 10 valori interi (byte, quindi tra 0 e 255, ma devono andare tra 0 e 99).
byte arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

// Qui il buffer deve essere almeno pari al numero di elementi dell'array moltiplicato 
// per il numero di caratteri, nel nostro caso 2, più uno per il marcatore ed uno per il 
// carattere 0x0 di fine stringa, quindi 2*10+1+1=22
char buf[25] = "X";

void setup() {
  Serial.begin(115200);
  char valore[3];
  for(int i=0; i<10; ++i) {
    sprintf(valore, "%02d", arr[i]);
    strcat(buf, valore);
  }
  Serial.println("Stringa risultante:");
  Serial.println(buf);
}

void loop() {

}

PS: significa anche che magari meglio se studi un poco le funzioni C di manipolazione stringhe...:wink:

Ciao DOCDOC, prima di tutto grazie per la risposta e poi chiedo scusa se non sono stato molto chiaro.
Cerco di chiarire: il tutto è nato perchè mi sono messo a fare un po' di manipolazione con gli array, e poi ho visto che mi poteva venire utile per un progetto che volevo fare.
Praticamente ho questi due array bidimensionali i quali (in teoria) dovranno essere di tipo byte contenenti numeri cha vanno da 0 a max 70; questi numeri li devo mandare via RS485 ad un altro device con il protocollo PJON, il quale chiede per la trasmissione un buffer array di char.
Quindi l'idea era quella di creare un buffer, mettere un header identificativo della stringa e poi in sequenza inserire i numeri trasformati in char; ovvio che se un numero è infariore a 10, devo scriverlo con lo 0 davanti (es. 6 andrà scritto 06 mentre 50 rimane 50).
La funzione forma() l' ho usata in un altro progetto dove dovevo fare la stessa cosa, ma inserivo all'interno del buffer un solo numero massimo di due cifre (una variabile tipo int) convertito.
Pensavo di fare la stessa cosa con gli array ma mi sono scontrato con questo problema.
Maurotec quando passo un array solo alla funzione, tutto gira regolarmente, ma se ne passo due il micro impazzisce, sembra si resetti sempre o inizia a scrivere cose strane in seriale.
Adesso provo quello che ha scritto Docdoc e vediamo...

Docdoc, scusa , il tuo esempio però fa riferiemento ad un array monodimensionale, come modifico per un bidimensionale?

Ma perché tutto questo casino?

Casti a puntatore a byte il nome dello array e sei a posto, usi quello per la trasmissione

manolomao:
Docdoc, scusa , il tuo esempio però fa riferiemento ad un array monodimensionale, come modifico per un bidimensionale?

Beh, ma hai due indici, qual è il problema?

// Arradi di 2x5 valori interi (byte, quindi tra 0 e 255, ma devono andare tra 0 e 99).
byte arr[2][5] = { {1, 2, 3, 4, 5}, {6, 7, 8, 9, 10} };
char buf[25] = "X";

void setup() {
  Serial.begin(115200);
  char valore[3];
  for(int i=0; i<2; ++i) 
    for(int j=0; j<5;++j){
      sprintf(valore, "%02d", arr[i][j]);
      strcat(buf, valore);
    }
  }
  Serial.println("Stringa risultante:");
  Serial.println(buf);
}

void loop() {

}

Ripeto, devi studiare un pochino e "sperimentare" singolarmente i vari aspetti, soprattutto visto il "minestrone" di codice che hai messo nel primo post...

Standardoil:
Ma perché tutto questo casino?
Casti a puntatore a byte il nome dello array e sei a posto, usi quello per la trasmissione

Magari perché deve trasmettere una stringa, non lo sappiamo, e comunque la sua richiesta era "giocare" con gli array per imparare a gestirli, per cui resto su questo obiettivo.

Bah, lui deve trasmettere usando pjon

Che accetta void *, non char *, quindi non stringhe

Invece per il bidimensionale

// Arradi di 2x5 valori interi (byte, quindi tra 0 e 255, ma devono andare tra 0 e 99).
byte arr[2][5] = { {1, 2, 3, 4, 5}, {6, 7, 8, 9, 10} };
char buf[25] = "X";

void setup() {
  Serial.begin(115200);
  char valore[3];
  for(int i=0; i<2*5;i++)
    {
      sprintf(valore, "%02d", arr[i]);
      strcat(buf, valore);
    }
  }
  Serial.println("Stringa risultante:");
  Serial.println(buf);
}

void loop() {

}

In C gli array bidimensionali sono impaccati

Standardoil:
Bah, lui deve trasmettere usando pjon
Che accetta void *, non char *, quindi non stringhe

Ok, ma ripeto, se il suo scopo (almeno iniziale) è iniziare ad imparare come si gestiscono gli array, per me se sta usando pjon o altro non mi interessa. Anzi, se non sa neanche come si gestiscono array e stringhe C è il caso che inizi ad imparare, PRIMA di fare altre cose più elaborate come usare pjon. Altrimenti è come cercare di applicare le trasformazioni di Lorentz senza sapere cosa sia un sistema di riferimento...

In C gli array bidimensionali sono impaccati

Si, ok, ma o consideri il contesto (vedi il suo primo codice...) e quindi si fa un minimo di "istruzione", oppure spiegare tecnicismi come questo, seppur corretto, è superfluo ma può addirittura contribuire a confondere le idee...

Non hai torto

Ma la domanda rimane:

Perché si fa tutte queste pippe cognitive?

Programmare deve essere operazione lineare, non una montagna da scalare tutte le volte.

Via tutte queste pippe cognitive, che offuscano la vista del problema vero...

La funzione forma() l' ho usata in un altro progetto dove dovevo fare la stessa cosa, ma inserivo all'interno del buffer un solo numero massimo di due cifre (una variabile tipo int) convertito.

Ti è chiaro quanto deve essere grande il buffer da dare in pasto a itoa?
Almeno deve essere grande n caratteri + 1, quindi se N=2 ci puoi conservare solo un carattere utile, cioè n=1.
se n=2 allora N=n+1. Questo è necessario per fare spazio al terminatore di C string '\0'.

Devi dare conferma su questo punto, perché è cruciale per giocare con gli array assicurandosi di non "fare la pipì fuori dal vaso".

Ciao.

Maurotec:
Ti è chiaro quanto deve essere grande il buffer da dare in pasto a itoa?
Almeno deve essere grande n caratteri + 1, quindi se N=2 ci puoi conservare solo un carattere utile, cioè n=1.
se n=2 allora N=n+1. Questo è necessario per fare spazio al terminatore di C string '\0'.

N deve essere 2 questo il risultato in seriale passando il primo array alla funzione

T000102030405060708091011121314

Se N lo metto a 3 la seriale restituisce

T0000000000000000000001010101014

E non è quello che vorrei fare....
Passando il secondo array senza il primo il risultato è

T1516171819202125262728293031

Quindi sembra che singolarmente i due array funzionano, ma quando gli passo entrambi gli array

X184
18:35:22.114 -> ⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮

Alla funzione non so perchè gli passa 184 anzichè 15 come dovrebbe essere la prima cella dell'array passato...
E questo fa impazzire il micro....
Cosa succede alla funzione? Dove sbaglio?
Standardoil ho provato quello da te postato, questo l'output in seriale

X537542547552557562567572577582

Non mi sembra funzioni correttamente....
E ad ogni modo, ho bisogno che quando i numeri sono singoli, debbano essere preceduti da uno 0 (1=01).

Standardoil:
Ma perché tutto questo casino?

Casti a puntatore a byte il nome dello array e sei a posto, usi quello per la trasmissione

Ecco, questo non lo so fare....
Per la trasmissione usavo mandare un array char con una lettera per identificare che dati mandavo e poi i numeri che volevo mandare.
ultima cosa Standardoil, cosa intendi con

Arradi di 2x5 valori interi (byte, quindi tra 0 e 255, ma devono andare tra 0 e 99).

Grazie per l'aiuto, e grazie per farmi capire sempre qualcosa in più.

manolomao:
ultima cosa Standardoil, cosa intendi con

Arradi di 2x5 valori interi (byte, quindi tra 0 e 255, ma devono andare tra 0 e 99).

Grazie per l'aiuto, e grazie per farmi capire sempre qualcosa in più.

non saprei..
non la ho scritta io quella roba li...

per la parte di trasmissione,
certo, se hai già un protocollo scritto per la trasmissione tra le due macchine, continua così, è la strada giusta

N deve essere 2 questo il risultato in seriale passando il primo array alla funzione

Sbagliato, la dimensione del buffer anche in eccesso non può e non deve determinare anomalia.

Comunque se X rimane nel range 0÷9 con N=2 non si verifica errore, ma se X=10 il buffer deve almeno essere grande 3 elementi.

Ciao.

Quindi scusate, come risolvo il problema???
Ho provato la funzione scritta da Docdoc e corretta da Standardoil ma non funziona, la mia funzione, anche se con pippe mentali, sembra funzionare se passo un array solo, al passaggio del secondo si blocca e questo non lo capisco...
Come posso fare in altro modo?
Come posso ordinare due array bidimensionali in un buffer char ponendo degli 0 qualora vi sia una sola unità??
Aiuto.

Dopo averci dormito sopra, e analizzando con attenzione quello scritto da docdoc, devo dire che funziona.
Chiedo scusa se ho avuto un approcio poco attento

char buf[100] ;  //occupa 59 byte
byte rele1[3][5];
byte rele2[2][7];
byte A;
void setup() {


  for (byte i=0;i<3;i++){for (byte x=0;x<5;x++){rele1[i][x]=A;A++;}
                       }
  for (byte i=0;i<2;i++){for(byte x=0;x<7;x++){rele2[i][x]=A;A++;}
                       }                     
  buf[0]='T';
  Serial.begin(115200);
  char valore[3];
  
  for(int i=0; i<3; ++i){
    for(int j=0; j<5;++j)
 {
      sprintf(valore, "%02d", rele1[i][j]);
      strcat(buf, valore);
    }
  }
    for(int i=0; i<2; ++i){
    for(int j=0; j<7;++j)
    {
      sprintf(valore, "%02d", rele2[i][j]);
      strcat(buf, valore);
    }
    }
   Serial.println("Stringa risultante2:");
  Serial.println(buf);  
  Serial.print("lunghezza array ");Serial.println(sizeof(buf));
  Serial.print("lunghezza string");Serial.println(strlen(buf));
}

void loop() {

}

fa esattamente quello che chiedevo...

T0001020304050607080910111213141516171819202122232425262728

Ho provato anche quello scritto da standardoil, ma non funziona...in seriale scrive

T728733738743748753758763768773778783788793798714721728735742749756763770777784791798805

Grazie.

manolomao:
Dopo averci dormito sopra, e analizzando con attenzione quello scritto da docdoc, devo dire che funziona.

Bene! :slight_smile:

Solo un paio di note "stilistiche" ma che sono spesso utili:

...
  for (byte i=0;i<3;i++){for (byte x=0;x<5;x++){rele1[i][x]=A;A++;}
                       }
  for (byte i=0;i<2;i++){for(byte x=0;x<7;x++){rele2[i][x]=A;A++;}
                       }                     
...

Evita di mettere parentesi graffe non indentate, o indentate in modo "strano", perché non rendono ben leggibile il codice a prima vista.
Se poi devi aggiungere una sola istruzione potresti anche evitarle. Inoltre se hai quella variabile che deve "contare", puoi usare l'operatore unario di incremento "++" tutto insieme all'istruzione.
E, infine, evita di usare maiuscole per le variabili, perché, come dicevo, sono per convenzione il modo di distinguere le variabili dalle costanti o simboli!
Quindi ti consiglio una cosa del genere (nota la "A" che ora è "a" e che inizializzo a zero esplicitamente):

...
byte a = 0;
...
  for (byte i=0; i<3; i++)
    for (byte x=0; x<5; x++)
      rele1[i][x]=a++;
  for (byte i=0; i<2; i++)
    for(byte x=0; x<7; x++)
      rele2[i][x]=a++;

oppure, meno compatto ma più formalmente "corretto":

  for (byte i=0; i<3; i++) {
    for (byte x=0; x<5; x++) {
      rele1[i][x]=a++;
    }
  }
  for (byte i=0; i<2; i++) {
    for(byte x=0; x<7; x++) {
      rele2[i][x]=a++;
    }
  }

Poi occhio che con questa istruzione:

  buf[0]='T';

tu NON imposti la stringa "buf" al valore "T", ma solamente il PRIMO carattere a 'T' ma non hai modo di sapere cosa ci sia dal secondo carattere in poi. Casualmente, poiché le aree di memoria sono inizializzate con degli zeri, ti funziona perché lo zero è il terminatore di stringa, ma devi abituarti sempre ad inizializzare esplicitamente le tue variabili (soprattutto le stringhe in questo caso) anche per evitare che cambi di piattaforma possano darti risultati diversi da quanto atteso.

Per cui hai vari modi per farlo (la scelta dipende dal proprio "stile" di programmazione, e/o da come quella variabile viene usata, e/o come viene usata...) ossia potresti impostare manualmente non solo il carattere ma anche il terminatore di stringa nel secondo carattere (ma non è la soluzione che preferisco):

  buf[0]='T';
  buf[1]=0;

oppure fai:

char buf[100] = "T";

che alloca lo spazio di 100 caratteri e ne imposta il valore iniziale come stringa "T" ossia i byte 'T' e terminatore di stringa 0x0, se questo è il valore che deve avere sempre all'inizio del codice.
Oppure se nel codice vuoi poter inizializzare la stringa più volte o con valori differenti basta fare:

  strcpy(buf, "T");

che copia la stringa "T" (quindi sempre i due byte) all'indirizzo "buf".

Grazie docdoc della lezione, molto interessante; essendo io un elettronico, mi diletto a programmare, e certi trucchi e malizie mi sfuggono...
Terrò a mente i consigli che mi hai dato

rele1[i][x]=a++;

Questa proprio non la sapevo, così si accorcia il codice...
Per quanto riguarda

  strcpy(buf, "T");

ecco, quello che volevo fare è utilizzare lo stesso buffer (buf) per inizializzare e scrivere altre stringhe con header diverso.
Ma praticamente questa istruzione mi permettere di scrivere sempre nel primo spazio di buf?
Poi posso aggiungere il resto come per l'esempio dei due array?
Ti chiedo questo perchè ho visto che utilizzando buf per altre stringhe, si crea sovrapposizione, non si azzera, ma accoda, quindi o azzero la stringa con tutti 0 oppure dovrei trovare una strada alternativa.
Cosa mi consigli di fare?
Avevo pensato anche di utilizzare un altro array char, ma mi sembra una perdita di memoria inutile.
Grazie.

manolomao:

rele1[i][x]=a++;

Questa proprio non la sapevo, così si accorcia il codice...

Si, non è che sia in genere tanto necessario accorciare il codice, ma l'operatore di post-incremento "a++" è stato pensato appositamente ("usa" il valore attuale della variabile e DOPO l'uso lo incrementa) come quello di pre-incremento "++a" (che prima aggiunge 1 e poi "usa" il valore risultante, già incrementato).

Per quanto riguarda

  strcpy(buf, "T");

ecco, quello che volevo fare è utilizzare lo stesso buffer (buf) per inizializzare e scrivere altre stringhe con header diverso.
Ma praticamente questa istruzione mi permettere di scrivere sempre nel primo spazio di buf?

Esattamente, infatti in genere anche io uso un buffer "globale" (ovviamente di dimensione sufficiente per contenere qualsiasi valore previsto dal codice), che poi inizializzi con quella strcpy(), aggiungendo quello che serve. Ma prova anche a cercare la funzione C "sprintf()" che spesso è molto utile anche per evitare tante concatenazioni successive...

Ti chiedo questo perchè ho visto che utilizzando buf per altre stringhe, si crea sovrapposizione, non si azzera, ma accoda, quindi o azzero la stringa con tutti 0 oppure dovrei trovare una strada alternativa.

Non c'è bisogno, teoricamente se devi ad esempio creare una nuova stringa/buffer (sottolineo che questa cosa vale solo se parliamo di stringhe, non di valori binari..) che inizia con "X" farai semplicemente:
strcpy(buf, "X");
In alternativa, per azzerare del tutto la stringa/buffer basta anche mettere uno zero come primo carattere:
buf[0] = 0;
tutti i byte successivi verranno ignorati (sempre, ripeto a patto che si parli di stringhe C).

Avevo pensato anche di utilizzare un altro array char, ma mi sembra una perdita di memoria inutile.

Se non ti servono contemporaneamente o non devi conservarne il valore precedente, la stringa è "usa e getta" (ossia la riempi con i dati e la usi, fine) quindi puoi successivamente reinizializzarla ed usarla altrove.

Grazie docdoc, tutto chiaro! :sunglasses: