Archivi tag: numeri casuali

randomSeed() su Arduino come usarla

In riferimento al precedente post: “Come ottenere più valori da una funzione in Arduino: variabili globali, riferimenti e struct – lezione 1/2” un utente mi ha scritto in privato per chiedermi chiarimenti in merito all’uso di randomSeed(), nello specifico di randomSeed(analogRead(A0)). La spiegazione che ho fornito è diventata alla fine qualcosa che posso riutilizzare con i miei studenti quindi ne ho realizzato un post sul sito in modo che possa essere letto da tutti.

Quando in uno sketch usate random(), state usando un generatore pseudo-casuale: i numeri “sembrano” casuali, ma in realtà seguono una sequenza deterministica. Se riavviate la scheda e non fate nulla, spesso la sequenza riparte uguale. Per evitare questo comportamento (molto evidente nei giochi, nelle simulazioni e nelle attività di laboratorio), Arduino mette a disposizione randomSeed(), che inizializza il punto di partenza della sequenza.

Sintetizzo cosa fa nel dettaglio randomSeed()

  • random() genera numeri pseudo-casuali.
  • randomSeed(seed) imposta il seme (seed), cioè il valore da cui parte la sequenza.

questo vuol dire che:

  • Seed uguale > sequenza uguale.
  • Seed diverso > sequenza diversa.

L’effetto di randomSeed(analogRead(A0)) utilizzato nella lezione precedente è duplice:

  1. legge un valore analogico da A0 con analogRead(A0)
  2. usa quel valore come seme per randomSeed(...)

L’idea (consigliata anche nella documentazione Arduino) è: se A0 è scollegato, la lettura è influenzata dal “rumore” elettrico ambientale e tende a variare; quindi anche il seed cambia e, a ogni reset, random() produce una sequenza diversa.

Però attenzione, un pin “floating” non garantisce casualità perfetta; spesso fornisce poca entropia, ma per scopi didattici (giochi, simulazioni, esercizi) è in genere sufficiente. Ovviamente non è una tecnica adatta a sicurezza/criptografia.

Precisazione: differenze tra UNO R3 e UNO R4

  • Arduino UNO R3: analogRead() è a 10 bit: valori 0–1023.
  • Arduino UNO R4: per compatibilità, analogRead() parte di default a 10 bit (0–1023), ma l’UNO R4 supporta risoluzioni più alte (fino a 14 bit) tramite analogReadResolution().

Questa informazioni può essere utile anche per il seed: più risoluzione vuol dire più livelli possibili nella lettura analogica, quindi (potenzialmente) seed più variabile.

Di seguito alcune proposte di attività da svolgere a scuola o per esercitarsi.
Tutti gli esempi stampano su Serial Monitor. Usate 9600 baud e, quando richiesto, lasciate A0 scollegato.

Attività 01: serie che si ripete – (senza randomSeed)

Obiettivo: vedere che, senza inizializzazione, la sequenza tende a ripetersi dopo un reset
Consegna: fate partire lo sketch, annotate i primi 10 numeri, premete reset e confrontate.

/*
  Prof. Maffucci Michele
  14.02.26
  Attività 01: serie che si ripete - (senza randomSeed)
*/

void setup() {
  Serial.begin(9600);
}

void loop() {
  Serial.println("Primi 10 numeri pseudo-casuali (senza seed):");
  for (int i = 0; i < 10; i++) {
    Serial.println(random(0, 100));
  }
  Serial.println("--- Premi reset della scheda e confronta ---\n");
  delay(3000);
}

Attività 02: Seed fisso = sequenza ripetibile (utile nei test)

Obiettivo: capire che un seed costante produce sempre la stessa sequenza (utile per il debug).
Consegna: verificare che a ogni reset i numeri restano identici.

/*
  Prof. Maffucci Michele
  14.02.26
  Attività 02: capire che un seed costante produce sempre la stessa sequenza (utile per il debug)
*/

void setup() {
  Serial.begin(9600);
  randomSeed(12345); // seed fisso
}

void loop() {
  Serial.println("Primi 10 numeri con seed fisso (12345):");
  for (int i = 0; i < 10; i++) {
    Serial.println(random(0, 100));
  }
  Serial.println("--- Premi reset: deve ripetersi uguale ---\n");
  delay(3000);
}

Attività 03: Seed “rumoroso” con analogRead(A0) (A0 scollegato)

Obiettivo: ottenere sequenze diverse a ogni reset usando randomSeed(analogRead(A0)).
Consegna:

  1. eseguite con A0 scollegato > osservate seed e sequenza
  2. collega A0 a GND o 5V > osservate come seed e sequenza diventano più “stabili” (meno variabili)
/*
  Prof. Maffucci Michele
  14.02.26
  Attività 03: ottenere sequenze diverse a ogni reset usando randomSeed(analogRead(A0))
*/

void setup() {
  Serial.begin(9600);

  // A0 deve essere scollegato per sfruttare la variabilità del rumore
  int seme = analogRead(A0);
  randomSeed(seme);

  Serial.print("Seed letto da A0: ");
  Serial.println(seme);
  Serial.println();
}

void loop() {
  Serial.println("Primi 10 numeri con seed da A0:");
  for (int i = 0; i < 10; i++) {
    Serial.println(random(0, 100));
  }
  Serial.println("--- Premi reset: dovrebbero cambiare ---\n");
  delay(3000);
}

Attività 04: utilizzo di UNO R4 per avere seed più “ricco” aumentando la risoluzione (12/14 bit)

Obiettivo: su UNO R4 aumentare la risoluzione ADC e “mescolare” più letture per un seed più variabile.
Consegna:

  • fate 5 reset su UNO R3 e 5 reset su UNO R4, annotate i seed e valutate quanto cambiano
  • su UNO R4 provate anche analogReadResolution(12) e confronta con 14

Lo sketch che segue viene compilato anche su UNO R3: la parte analogReadResolution() viene attivata solo su UNO R4.

/*
  Prof. Maffucci Michele
  14.02.26
  Attività 04: utilizzo di UNO R4 per avere seed più "ricco" aumentando la risoluzione (12/14 bit)
*/

unsigned long creaSeedDaRumoreA0() {
  unsigned long seed = 0;

  // Su UNO R4 puoi aumentare la risoluzione (10 bit default, fino a 14 bit)
#if defined(ARDUINO_UNOR4_MINIMA) || defined(ARDUINO_UNOR4_WIFI)
  analogReadResolution(14);
#endif

  // Mescolo 32 letture per raccogliere più variabilità
  for (int i = 0; i < 32; i++) {
    seed = (seed << 1) ^ (unsigned long)analogRead(A0);
    delay(2);
  }
  return seed;
}

void setup() {
  Serial.begin(9600);

  Serial.println("Seed avanzato da A0 (A0 scollegato):");
  unsigned long seed = creaSeedDaRumoreA0();
  Serial.print("Seed calcolato: ");
  Serial.println(seed);

  randomSeed(seed);
  Serial.println();
}

void loop() {
  Serial.println("Primi 10 numeri con seed avanzato:");
  for (int i = 0; i < 10; i++) {
    Serial.println(random(0, 100));
  }
  Serial.println("--- Reset e confronta ---\n");
  delay(3000);
}

Ora vi propongo un piccolo progetto, il classico dado, che sicuramente riuscirete a svolgere velocemente.

Esercizio: “dado elettronico”

Obiettivo: usare random() in un contesto concreto e verificare che con randomSeed i lanci non siano sempre uguali dopo reset.

Vi lascio qualche giorno per provare a sviluppare una soluzione e poi ve ne propongo una io. 🙂

Terminiamo questa lezione riassumendo quanto visto:

  • random() = pseudo-casuale (sequenza deterministica);
  • randomSeed() sposta il punto di partenza della sequenza;
  • randomSeed(analogRead(A0)) funziona solo se A0 varia davvero (meglio scollegato o con una sorgente “rumorosa”);
  • UNO R4 può aumentare la risoluzione ADC con analogReadResolution() (default 10 bit per compatibilità).

Buon Coding a tutti 🙂

Appunti di programmazione su Arduino: numeri casuali

randomSeed(seed)

Imposta un valore di partenza per generare un numero casuale.

randomSeed(value); // assegna a value un valore casuale

Poiché Arduino non è in grado di creare un vero numero casuale la funzione randomSeed consente di inserire una variabile, una costante o un’altra funzione casuale per generare numeri “casuali” ancora più casuali 🙂
randomSeed viene utilizzata come base di partenza per generare un numero casuale, può essere utilizzata in diversi modi associandola ad altre funzioni, come ad esempio utilizzare il valore restituito da millis() funzione che restituisce il numero di millisecondi da quando la scheda Arduino è in funzione, o ancora con analogRead() per leggere il rumore elettrico attraverso un pin analogico, in questo modo trattandosi di rumore, che varia in modo caotico, la analogRead() restituirà un valore “abbastanza” casuale.

random(max)
random(min, max)

La funzione random permette di restituire numeri pseudo-casuali in un intervallo specificato tra un valore minimo e massimo.

value = random(100, 200); // assegna a 'value' un valore casuale
                          // compreso tra 100 e 200

Nota: utilizzare questa funzione dopo aver utilizzato la funzione randomSeed().

L’esempio che segue crea un numero casuale compreso tra 0 e 255 e fornisce un segnale PWM su un pin PWM uguale al valore casuale:

int randNumber; // variabile usata per memorizzare il valore casuale
int led = 10;   // un led con in serie una resistenza da 220 Ohm
                // inserito sul pin 10

void setup(){}  // non e' necessaria nessuna configurazione
void loop()
{
   randomSeed(millis());         // imposta millis() come base per
                                 // generare un numero
                                 // casuale da 0 a 255
   randNumber = random(255)      // numero casuale da 0 a 255
   analogWrite(led, randNumber); // uscita segnale PWM
   delay(500);                   // pausa di mezzo secondo
}

Come potete notare viene utilizzata la funzione randomSeed(millis()) per inizializzare il generatore di numeri casuali, agendo in questo modo siamo abbastanza sicuri di generare numri sempre diversi.

Per le lezioni precedenti consultare la sezione Appunti di programmazione che trovate nella pagina Arduino di questo sito.