Go Down

Topic: Generare un numero casuale dato un seme (Read 1 time) previous topic - next topic

mounes

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?


leo72

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).

gpb01

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  :smiley-mr-green: :smiley-mr-green: :smiley-mr-green:
Search is Your friend ... or I am Your enemy !

mounes

Grazie ad entrambi per il chiarimento.

Quote
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?


leo72

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.

mounes

#5
Jul 10, 2014, 03:18 pm Last Edit: Jul 10, 2014, 05:38 pm by leo72 Reason: 1
Penso di aver capito, scusa se abuso della tua pazienza ma potresti dirmi se è corretto come ho scritto o c'è un modo migliore?
Code: [Select]

   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}
  }

leo72

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.


Code: [Select]
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

mounes

#7
Jul 10, 2014, 06:12 pm Last Edit: Jul 10, 2014, 06:19 pm by leo72 Reason: 1
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:

Code: [Select]


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:

Code: [Select]
// 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?  ;)

mounes

Scusami per il tag. :P

Code: [Select]
  // 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).

leo72

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.

mounes

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?

leo72

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

mounes

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.  :)

leo72


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.

Go Up