Generare un numero casuale dato un seme

Salve, vi chiedo scusa se chiedo una cosa forse facile, ma è la prima volta che scrivo un programma per arduino. La mia intenzione è visualizzare un numero casuale su un display led a 4 cifre dopo aver premuto un tasto, ora la parte che pilota il display e il tasto sono riuscito a farla grazie a vari esempi, i miei dubbi sono su come generare il numero casuale.

Partiamo dal fatto che io voglia settare un seed tramite un dip switch da 8, come dovrei passare i valori alla funzione randomSeed() per settare così random()? Per essere sicuro che il numero ogni volta sia diverso, non dovrei salvarlo in memoria e confrontarlo prima di visualizzarlo?

La funzione che genera i numeri random nell'Arduino NON è assolutamente random. Come tutte le funzioni del genere, usa un algoritmo pseudocasuale che genera una sequenza di numeri partendo da un valore iniziale e trovando poi i numeri mediante una formula matematica. Ad ogni accensione, l'algoritmo restituisce sempre la stessa sequenza.

Si usa appunto il seme per dare un valore iniziale differente affinché la funzione non ripeta gli stessi numeri. Tieni però presente che se usi lo stesso seme, la sequenza si ripete comunque.

Il tuo dip switch potresti collegarlo ad altrettanti pin dell'Arduino e leggere poi lo stato di questi pin e "costruire" un numero ad 8 bit che passeresti poi all'Arduino con randomSeed().

In alternativa, ho scritto tempo fa una libreria, pRNG, che genera numeri casuali sempre differenti ad ogni accensione, senza bisogno di fornire un seme iniziale a mano (link nella mia firma).

Purtroppo a parità di randomSeed() avrai sempre la stessa sequenza di random(), difatti ... il software di generazione è solo pseudo-casuale.

Soluzione semplice ... usare come randomSeed() il valore letto da un pin analogico lasciato scollegato. Data l'alta impedenza del pin rileverà dei valori casuali del segnale in ingresso che potrai usare come numero di partenza.

Soluzione più sofisticata ... utilizzare l'apposita libreria scritta da Leo (... il nostro mod ;)) che trovi QUI.

Guglielmo

PS : Oopps ... mi sono appunto sovrapposto con Leo :grin: :grin: :grin:

Grazie ad entrambi per il chiarimento.

Il tuo dip switch potresti collegarlo ad altrettanti pin dell'Arduino e leggere poi lo stato di questi pin e "costruire" un numero ad 8 bit che passeresti poi all'Arduino con randomSeed().

Mi potresti spiegare come fare questa "costruzione"? Devo sfruttare un array?

No, una semplice variabile di tipo "byte", appunto, che contiene 8 bit. Poi leggi lo stato di ognuno degli 8 pin: lo stato low è letto come "0", mentre lo stato high è letto come "1". A questo punto non devi fare altro che, con le istruzioni bitSet e bitClear impostare lo stato dei bit corrispondenti agli 8 pin e poi hai il tuo seme da passare a randomSeed. Mi spiego, costruisci un array di 8 elementi con ogni elemento contenente il n° del pin. L'elemento 0 dell'array sarà poi il bit 0 del byte. L'elemento 1 sarà il bit 1 ecc... Basta con un ciclo for leggere lo stato del pin n ed impostare il bit n del tuo byte.

Penso di aver capito, scusa se abuso della tua pazienza ma potresti dirmi se è corretto come ho scritto o c’è un modo migliore?

   int array; //variabile in cui salverò lo stato di ogni bit
   byte seed; // variabile che otterò e manderò a randomSeed()
   int seedArray [8]={pin0,...,pin7}; // variabile array in cui associo i pin dello switch
   int i; // variabile per scorrere l'array
   
  for (i=0, i<8, i++){
   array=digitalRead (seedArray [ i ]);
     if (array==HIGH){
         bitSet (seed, i)}
         else {
            bitClear(seed,i}
   }

Sì, o anche in modo più semplice: prima assegni 0 alla variabile byte, e poi setti ad 1 solo i bit che risultano alti.
Inoltre:

  1. usa un array di byte per i pin, risparmi memoria (il numero di un pin non sarà MAI maggiore di 255);
  2. il seed è un tipo byte anch’esso;
  3. non dichiarare l’indice “i” anche al di fuori del ciclo for, se non ti serve da altre parti.
byte seed; 
byte seedArray [8]={pin0,...,pin7};

seed = 0;
for (byte i = 0, i < 8, i++){
   if (digitalRead (seedArray[i])) {
      bitSet (seed, i);
   }
}

PS:
il codice si racchiude con gli appositi tag

Decisamente ottimizzato, grazie.

Ora devo solo visualizzarlo su Dipaly LED a 4 cifre, ho scritto questa parte di codice per definire come formare le cifre:

void showdigit (int digit)
{

switch (digit) {
   
 digitalWrite(A, LOW);
 digitalWrite(B, LOW);
 digitalWrite(C, LOW);
 digitalWrite(D, LOW);
 digitalWrite(E, LOW);
 digitalWrite(F, LOW);
 digitalWrite(G, LOW);

 case 0:

 digitalWrite(A, HIGH);
 digitalWrite(B, HIGH);
 digitalWrite(C, HIGH);
 digitalWrite(D, HIGH);
 digitalWrite(E, HIGH);
 digitalWrite(F, HIGH);
 break;

ecc.

digit è la variabile in cui salverò prima le migliaia,centinaia ecc.

La mia domanda è come posso scomporrere il numero?Non sono certo ma devo ad esempio:

// migliaia
(numero/1000)

// centinaia

(numero%1000)

// decine

(numero&100)

// unità
(numero/10)

leo72: PS: il codice si racchiude con gli appositi tag

:P

Visto che il numero lo estrai a sorte, potresti semplicemente estrarre 4 numeri da 0 a 9 e visualizzare nella corrispondente cella del display il numero estratto, no? ;)

Scusami per il tag. :P

  // funzione per visualizzare le 4 cifre su Display LED

  void showDigits (int otp){

  showDigit (otp/1000);  // mi restituisce solo la prima cifra (partendo da sinistra)
  digitalWrite(DIG1, HIGH); 
  digitalWrite(DIG2, LOW); 
  digitalWrite(DIG3, LOW);
  digitalWrite(DIG4, LOW);

  delay (1);  // ritardo di 1 ms prima di passare alla successiva cifra 

  otp = otp%1000;  // mi restituisce solo la seconda cifra (partendo da sinistra)
  digitalWrite(DIG1, LOW); 
  showDigit(otp/100); 
  digitalWrite(DIG2, HIGH); 

  delay (1); 

  otp = otp%100;  // mi restituisce solo la terza cifra (partendo da sinistra)  
  digitalWrite(DIG2, LOW);
  showDigit(otp/10);
  digitalWrite(DIG3, HIGH);

  delay (1);

  otp = otp%10;  // mi restituisce solo la quarta cifra (partendo da sinistra)
  digitalWrite(DIG3, LOW);
  showDigit(otp); 
  digitalWrite(DIG4, HIGH);

  delay (1);  // grazie ad ogni piccolo ritardo sì ha l'effetto che le cifre si accendano tutte contemporaneamente

  }

Sinceramente non mi viene in mente altro, come suggerisci tu dovrei usare un "ciclo for", ma non sarebbe troppo lungo il tempo di accensione di una cifra all'altra? Come ho scritto sopra sono sicuro che mi appaiano contemporaneamente (almeno per l'occhio umano).

Ti crei un array di 4 elementi, in ogni elemento metti il numero da 0 a 9 estratto, poi una volta che hai le 4 cifre mandi tutto al display, multiplexando la visualizzazione. Ossia devi accendere velocemente ed in sequenza una cifra alla volta, tenendola accesa per almeno per un tempo sufficiente a non far vedere lo sfarfallio.

Grazie mille per i consigli. :)

In settimana prendo la roba e lo provo con Leonardo.

Spero di non approfittare della pazienza e di non andare troppo fuori topic. Avrei la seguente curiosità: è possibile scrivere un bootloader per l'ATMEGA, che carichi lo sketch da una memoria esterna (esemp. EEPROM)? Scusate l'ignoranza, ma eventualmente in quale linguaggio si scriverebbe?

Esiste un bootloader che può caricare lo sketch da un file su SD: https://github.com/thseiler/embedded/tree/master/avr/2boots

da EEPROM non ne conosco ma potrebbe anche esserci

Come sempre molto gentile, appena avrò fatto le provo con Arduino, vedrò il booloader per completare il quadro. Naturalmente ti aggiornerò su come andrà.

Grazie per ora. :)

mounes: In settimana prendo la roba e lo provo con Leonardo.

Rileggo solo ora con attenzione... Leonardo... non so se quel bootloader lì è compatibile con la Leonardo. In genere quando si parla di "Arduino" ci si riferisce alla Uno, il chip della Leonardo è completamente diverso perché integra l'USB e tante altre cose, difatti ha un bootloader diverso da quello usato sulla Uno. Non so se qeul 2boots gira anche sulla Leonardo.

Per ora provo lo sketch, poi mi "cervellerò" con il boot. :)

Grazie cmq per l'avvertimento.

Purtroppo qualcosa non va… è tutto il pomeriggio che provo ma non so veramente cosa non vada. Potete aiutarmi?!

 // Inizializzazione

// definisco il pin associato al pulsante di START

const int buttonStart = 7;

// definisco i pin del DIP switch

const int SW1 = 14;
const int SW2 = 15;
const int SW3 = 16;
const int SW4 = 17;
const int SW5 = 18;
const int SW6 = 19;

// definisco i pin dei segmenti del Display LED

const int A = 12;  // pin che controllano i segmenti delle cifre
const int B = 8;
const int C = 4;
const int D = 2;
const int E = 1;
const int F = 11;
const int G = 5;
const int DP = 3;

const int DIG1 = 13;  // pin che controllano le singole cifre
const int DIG2 = 10;
const int DIG3 = 9;
const int DIG4 = 6;

// definisco il tipo dei pin 

void setup(){
  
  pinMode(buttonStart, INPUT_PULLUP);
  
  pinMode(SW1, INPUT);
  pinMode(SW2, INPUT);
  pinMode(SW3, INPUT);
  pinMode(SW4, INPUT);
  pinMode(SW5, INPUT);
  pinMode(SW6, INPUT);
  
  pinMode(A, OUTPUT);
  pinMode(B, OUTPUT);
  pinMode(C, OUTPUT);
  pinMode(D, OUTPUT);
  pinMode(E, OUTPUT);
  pinMode(F, OUTPUT);
  pinMode(G, OUTPUT);
  pinMode(DP, OUTPUT);

  pinMode(DIG1, OUTPUT);
  pinMode(DIG2, OUTPUT);
  pinMode(DIG3, OUTPUT);
  pinMode(DIG4, OUTPUT);

}

int buttonState = 0;  // definisco una variabile per controllare lo stato del pulsante di START

void loop(){
  
  buttonState = digitalRead(buttonStart);  // legge lo stato del pulsante di START

  if (buttonState == LOW){
    
   // operazione che legge i bit del DIP switch e crea il seed
    
   byte seed; // variabile che otterò e manderò a randomSeed() per generare la one time password
   byte seedArray [8]={SW1,SW2,SW3,SW4,SW5,SW6,0,0}; // variabile array in cui associo i pin del DIP switch
   
   seed = 0;  // setto tutti i bit a 0
   for (int i = 0; i<8; i++){  // leggo lo stato di ogni pin del Dip switch e se è HIGH setto a 1 il bit del seed di quella data posizione
    if (digitalRead(seedArray [i]) == HIGH){
         bitSet(seed, i);
         
         // TEST: visualizzo allo shermo del pc i bit del seed e quest'ultimo in forma decimale
         Serial.begin(9600);
         Serial.print(seedArray [i], BIN);
         Serial.print(seed, DEC);
         Serial.print("\t");
         
    }
   }
   
  // generazione dellala one time password

  int otp;  // variabile in cui verrà salvata la one time password

  randomSeed(seed);  // inizializzo la funzione random che genererà la one time password
  otp = random(10000);  // dato che ho un display a 4 cifre potrò visualizzare solo un numero tra 0 e 9999

  // TEST: visualizzo allo schermo del pc la one time password
  Serial.begin(9600);
  Serial.print(otp); 
 
  showDigits(otp);
  
  delay (5000);  // visualizzo la one time password per 5 secondi, prima di spegenere il Display LED
  
  digitalWrite(DIG1, LOW); 
  digitalWrite(DIG2, LOW); 
  digitalWrite(DIG3, LOW);
  digitalWrite(DIG4, LOW);
  
  delay (1);
  
  }
} 

// definisco la funzione per visualizzare un numero da 0 a 9 in base alla cifra ricevuta

void showDigit (int digit){

   digitalWrite(A, HIGH);  // spengo tutti i LED della cifra (nel caso ad anodo comune si pongono i pin HIGH)
   digitalWrite(B, HIGH);
   digitalWrite(C, HIGH);
   digitalWrite(D, HIGH);
   digitalWrite(E, HIGH);
   digitalWrite(F, HIGH);
   digitalWrite(G, HIGH);
   digitalWrite(DP, HIGH);

  switch (digit) {  
   
   case 0:  //per ogni numero accenderò i relativi LED (nel caso ad anodo comune si pongono i pin LOW)
 
   digitalWrite(A, LOW);
   digitalWrite(B, LOW);
   digitalWrite(C, LOW);
   digitalWrite(D, LOW);
   digitalWrite(E, LOW);
   digitalWrite(F, LOW);
 
   break;

   case 1:
    
   digitalWrite(B, LOW);
   digitalWrite(C, LOW);
 
   break;

   case 2:
 
   digitalWrite(A, LOW);
   digitalWrite(B, LOW);
   digitalWrite(D, LOW);
   digitalWrite(E, LOW);
   digitalWrite(G, LOW);
 
   break;

   case 3:

   digitalWrite(A, LOW);
   digitalWrite(B, LOW);
   digitalWrite(C, LOW);
   digitalWrite(D, LOW);
   digitalWrite(G, LOW);

   break;

   case 4:

   digitalWrite(B, LOW);
   digitalWrite(C, LOW);
   digitalWrite(F, LOW);
   digitalWrite(G, LOW);
 
   break;

   case 5:
 
   digitalWrite(A, LOW);
   digitalWrite(C, LOW);
   digitalWrite(D, LOW);
   digitalWrite(F, LOW);
   digitalWrite(G, LOW);
 
   break;

   case 6:
 
   digitalWrite(A, LOW);
   digitalWrite(C, LOW);
   digitalWrite(D, LOW);
   digitalWrite(E, LOW);
   digitalWrite(F, LOW);
   digitalWrite(G, LOW);
 
   break;

   case 7:
 
   digitalWrite(A, LOW);
   digitalWrite(B, LOW);
   digitalWrite(C, LOW);

   break;

   case 8:
 
   digitalWrite(A, LOW);
   digitalWrite(B, LOW);
   digitalWrite(C, LOW);
   digitalWrite(D, LOW);
   digitalWrite(E, LOW);
   digitalWrite(F, LOW);
   digitalWrite(G, LOW);
 
   break;

   case 9:
 
   digitalWrite(A, LOW);
   digitalWrite(B, LOW);
   digitalWrite(C, LOW);
   digitalWrite(D, LOW);
   digitalWrite(F, LOW);
   digitalWrite(G, LOW);
 
   break;

   default:

   break;

 } 
}

// funzione per visualizzare le 4 cifre su Display LED

void showDigits (int digits){

  showDigit (digits/1000);  // mi restituisce solo la prima cifra (partendo da sinistra)
  digitalWrite(DIG1, HIGH); // accende solo la prima cifra (nel caso ad anodo comune si pone il pin HIGH)
  digitalWrite(DIG2, LOW); 
  digitalWrite(DIG3, LOW);
  digitalWrite(DIG4, LOW);

  delay (1);  // ritardo di 1 ms prima di passare alla cifra successiva

  digits = digits%1000;  // mi restituisce solo la seconda cifra (partendo da sinistra)
  digitalWrite(DIG1, LOW); 
  showDigit(digits/100); 
  digitalWrite(DIG2, HIGH); 

  delay (1); 

  digits = digits%100;  // mi restituisce solo la terza cifra (partendo da sinistra)  
  digitalWrite(DIG2, LOW);
  showDigit(digits/10);
  digitalWrite(DIG3, HIGH);

  delay (1);

  digits = digits%10;  // mi restituisce solo la quarta cifra (partendo da sinistra)
  digitalWrite(DIG3, LOW);
  showDigit(digits); 
  digitalWrite(DIG4, HIGH);

  delay (1);  // grazie ad ogni piccolo ritardo sì ha l'effetto che le cifre si accendano tutte contemporaneamente

}

Scusate, la mia domanda era troppo generica... cmq non mi visualizzava correttamente il numero, ora lo visualizza ma per un atimo e rimane accesa solo l'ultima cifra.

Ho capito che devo modificare l'ultima funzione che ho scritto "showDigits", devo sfruttare un ciclo for o lo switch case? (è tutto i lgiorno che ci lavoro) :P

Ho risolto la visualizzazione del display led a 4 cifre, se può servire a qualcuno:

 void showDigits (int digits){
  
  int temp; // variabile in cui salvo temporaneamente il resto della one time password per determinare di quante cifre è composta
  
  // definisco le variabili in cui salverò le migliaia, centinaia, decine ed unità della one time password
  
  int thousand = digits/1000;
  temp = digits%1000;
  int hundred = temp/100;
  temp = temp%100;
  int ten = temp/10 ;
  temp = temp%10;
  int unit = temp;
  
  // ciclo che determina la visualizzazione delle singole cifre
  
  if (thousand == 0){
    if (hundred == 0){
      if (ten == 0){  // accende solo la cifra delle unità (nel caso ad anodo comune si pone il pin HIGH)
        digitalWrite(DIG1, LOW); 
        digitalWrite(DIG2, LOW); 
        digitalWrite(DIG3, LOW);
        digitalWrite(DIG4, HIGH);
    showDigit (unit);                   
        delay(5);
      }
      else {  // accende la cifra delle decine e delle unità 
        digitalWrite(DIG1, LOW);
    digitalWrite(DIG2, LOW);
    digitalWrite(DIG3, HIGH);
    showDigit (ten);
    delay(5);
        digitalWrite(DIG3, LOW);
    digitalWrite(DIG4, HIGH);
    showDigit (unit);
    delay(5);
    digitalWrite(DIG4, LOW);
      }
    }
    else{  // accende la cifra delle centinaia, delle decine e delle unità 
      digitalWrite(DIG1, LOW);
      digitalWrite(DIG2, HIGH);
      showDigit(hundred);
      delay(5);
      digitalWrite(DIG2, LOW);
      digitalWrite(DIG3, HIGH);
      showDigit(ten);
      delay(5);
      digitalWrite(DIG3, LOW);
      digitalWrite(DIG4, HIGH);
      showDigit(unit);
      delay(5);
      digitalWrite(DIG4, LOW);
    }
  }
  else{  // accende la cifra delle migliaia, delle centinaia, delle decine e delle unità 
    digitalWrite(DIG1, HIGH);
    showDigit(thousand);
    delay(5);
    digitalWrite(DIG1, LOW);
    digitalWrite(DIG2, HIGH);
    showDigit(hundred);
    delay(5);
    digitalWrite(DIG2, LOW);
    digitalWrite(DIG3, HIGH);
    showDigit(ten);
    delay(5);
    digitalWrite(DIG3, LOW);
    digitalWrite(DIG4, HIGH);
    showDigit(unit);
    delay(5);
    digitalWrite(DIG4, LOW);
  }
}

Rieccomi, spero qualcuno possa aiutarmi, ho quasi finito il mio progetto, ma ho un problema nella generazione del numero random ovvero se gli passo un seed di soli 0 mi genera bene una sequenza random ogni volta che premo il pulsante ma se pongo almeno un bit del seed ad 1 premendo il pulsante ottengo sempre lo stesso numero.
Aiutatemi perfavore non so prorpio come risolverlo. :~

 void loop(){
  
  buttonState = digitalRead(buttonStart);  // legge lo stato del pulsante di START

  if (buttonState == LOW){
    
   // operazione che legge i bit del DIP switch e crea il seed  
    
   int dipArray [6]={SW6,SW5,SW4,SW3,SW2,SW1}; // variabile array in cui associo i pin del DIP switch
   int seedArray [8]={0,0,0,0,0,0,0,0};  // variabile array in cui salvo i valori dei pin
   int val = 0;
   for (int j=0; j<6; j++){
     val = digitalRead(dipArray [j]);
      if (val > 0){
       seedArray [j+2] = 1;
     }
   }
       
   byte seed; // variabile che otterò e manderò a randomSeed() per generare la one time password     
   seed = 0;  // setto a 0 tutti i bit
   for (int i = 0; i<8; i++){  // leggo lo stato di ogni pin del Dip switch e se è HIGH setto a 1 il bit del seed di quella data posizione
    if (seedArray [i] == 1){
         bitSet(seed, i);
        }
   }
   
 
   
   Serial.begin(9600);
   Serial.print("\t");
   Serial.println("Bit del Seed");
  
   
   for (int k = 0; k<8; k++){
   // TEST: visualizzo allo shermo del pc i bit del seed e quest'ultimo in forma decimale
         Serial.begin(9600);
         Serial.print("\t");
         Serial.print(bitRead(seed, k));
         Serial.print("\t");
   }

  // generazione dellala one time password

  int otp;  // variabile in cui verrà salvata la one time password
  
  randomSeed(seed);  // inizializzo la funzione random che genererà la one time password
  otp = random(10000);  // dato che ho un display a 4 cifre potrò visualizzare solo un numero tra 0 e 9999

  // TEST: visualizzo allo schermo del pc la one time password
  Serial.begin(9600);
  Serial.println("");
  Serial.print("\t");
  Serial.println("Seed in Decimale");
  Serial.print("\t");
  Serial.print(seed, DEC);
  Serial.println("");
  Serial.print("\t");
  Serial.println("One Time Password");
  Serial.print("\t");
  Serial.print(otp);
  Serial.println(""); 
  
  // visualizzo la one time password per circa 3 secondi, prima di spegenere il Display LED
  
  for (int t = 0; t<100; t++){  
  showDigits(otp);
  }
  
  delay (5);
  
  }
}