Stavo passando il tempo scrivendo una nuova libreria che generi numeri casuali un po' più... casuali di quelli generati dalla libreria dell'Arduino che, se non inizializzata con un seme sempre diverso, genera la stessa sequenza di numeri ad ogni avvio. Per far ciò ho usato un vecchio algoritmo, il Multiply-with-carry inventato da George Marsaglia. Ho però un problema.
Nel costruttore della classe ho messo il richiamo alla routine per inizializzare le 2 variabili interne dell'algoritmo, routine che costruisce il loro valore iniziale assemblando le letture dei primi 4 pin analogici. Il problema è che se metto tale inizializzazione all'interno del costruttore della classe l'algoritmo restituisce sempre 0. Se invece richiamo la funzione di inizializzazione in modo esplicito all'interno della routine setup() allora tutto funziona perfettamente! Sembra che nei costruttrori di una classe la funzione analogRead NON... funzioni!
Sto usando l'Arduino IDE 1.0.1 su un computer non mio con Windows XP ( ). Nello sketch di esempio ho commentato la chiamata esplicita all'inizializzatore (il metodo initialize() è stato lasciato pubblico nell'header volutamente, per fare le prove, ma anche mettendolo privato non cambia nulla). Potete provare compilando con e senza tale chiamata per sapere se funziona oppure no? Oppure sapete dirmi se ho sbagliato qualcosa?
Ho provato sull'ide 0022, solito kubuntu; si comporta esattamente come hai descritto.
Senza l'inizializzazione nel setup genera soltanto 0.
Domani metto l'ultimo ide e provo anche con quello.
Anch'io ho provato con la 0022 sempre sotto Windows e non va lo stesso.
Non capisco da cosa dipenda, ma a questo punto non dipende né dal SO né dalla versione dell'IDE.
Comunque non ci vedo nulla di sbagliato. Quelle variabili globali identiche a quelle membro non dovrebbero comportare problemi perchè per accedere a quelle devi accederevi con lo scope superiore ::m_w.
Scusa prova a mettere il contenuto della funzione initialize... direttamente nel costruttore, solo per vedere se il problema è la chiamata ad analogRead.
Anch'io ho provato con la 0022 sempre sotto Windows e non va lo stesso.
Non capisco da cosa dipenda, ma a questo punto non dipende né dal SO né dalla versione dell'IDE.
Ho già trasferito il codice contenuto in initialize() direttamente all'interno del costruttore ma non è cambiato nulla.
Il problema sembra proprio analogRead all'interno di esso.
Se provi ad assegnare un valore arbitrario a m_w e m_z all'interno del costruttore vedrai che il generatore funziona. Se invece ci metti una lettura analogica non va.
Questo funziona ma perché in pratica istanzi la classe dal setup(). Anche richiamando initialize() manualmente funziona.
Quindi c'è qualcosa che il core di Arduino non gradisce: le analogRead nei costruttori di classe. Perché?
Io credo che questo sia normale e Arduino questa volta non abbia la colpa.
Quando crei istanza di tipo in realtà stai dichiarando una variabile di tipo definito dall'utente, quella dichiarazione non si trova nel main() per cui è come chiamare analogRead() fuori dal main() e questo non è previsto dal C/C++, in quanto il punto di entrata del programma è la funzione main() e solo li dentro puoi avviare codice operativo come le funzioni.
Comunque risolvi con ti ho indicato, oppure fai il corpo della classe vuoto e costringi l'utente ad avviare il medoto di inizializzazione.
L'intenzione era quella di semplificare, mettendo l'initialize trasparente all'utente ma anche prima di tutto il resto perché se ai pin analogici c'è collegato qualcosa, volevo che la funzione li leggesse prima magari che l'utente li iniziasse ad usare. Mettere il costruttore nel setup non mi risolve queso ultimo problema, perché l'utente potrebbe infilarmi la dichiarazione d'istanza dopo aver magari messo uno di quei pin analogici in output, per esempio.
Quoto l'ultima risposta di MauroTec, ha centrato il punto.
La via più semplice secondo me è questa:
MauroTec:
[...]fai il corpo della classe vuoto e costringi l'utente ad avviare il medoto di inizializzazione.
Volendo nel costruttore o nell'initialize, fai segliere all'utente quali pin usare. Nota che se lasci tutti i pin senza nulla di collegato, le letture che fanno sono più o meno tutte simili...poco randomico
Ciò non spiega perché l'analogRead non funziona all'interno del costruttore di una classe.
Sul fatto che i pin scollegati non diano entropia invece dissento: è proprio il fatto di essere flottanti che permette di avere un seme più casuale rispetto ad una lettura fatta su un pin con qualcosa di collegato che da sempre la stessa lettura.
EDIT:
ho fatto anche una segnalazione sul forum internazionale http://arduino.cc/forum/index.php/topic,111463.0.html
Indipendentemente dall'apparire logica o meno, la lettura analogica deve funzionare dovunque secondo me.
Siccome io sono TESTONE (non Testato :P), ho rimaneggiato la mia lib inserendo nel costruttore una lettura grezza (tramite registri) di 2 pin analogici e..... FUNZIONA!
Quindi il bug è nel core dell'Arduino che, per qualche motivo, non permette di usare la analogRead lì. Non dovrebbe essere un problema di visibilità perché la compilazione avviene senza problemi
99 su 100 anche l'analogRead ha un costruttore, o meglio un'inizializzazione, probabilmente contenuta dal main.c init()... e quale "costruttore" viene chiamato prima?
osservando il codice della analogRead, essa non setta il pin di attivazione ADC ADEN in ADCSRA, cosa che probabilmente da per scontato essere fatta dalla famosa init. ma essendo la tua variabile glogale, e quindi costruita PRIMA che il main abbia inizio, il pin rimane così comè.
test veloce per avvalorare la teoria: stampa il valore di ADCSRA nel costruttore... ovvio che anche la serial avarà lo stesso problema (anzi peggio visto che il begin avviene nel setup, quindi ancora dopo, dovrai salvarne il valore in una variabile do comodo e stamparla inseguito con tutta calma.
infine ad essere pignoli l'analogRead dovrebbe ache assicurarsi che in PRADC l'adc non sia spento (quindi il pin ADC deve essere a 0 per funzionare). è il valore di default, ma se uno va in sleep e poi si risveglia potrebbe rimanere fregato.
se te allocassi dinamicamente l'oggetto,probab non avresti questo problema..
potresti invece usare l'inizializzazione pigra:
nel costruttore inizializzi una variabile privata inizializzzat=false; quando usi l'oggetto testi la variabile: se è false, inizializzi per bene la baracca..
No, devo farlo obbligatoriamente prima che l'utente faccia qualsiasi cosa sui pin che uso per la lettura.
Mettiamo che l'utente li usi per qualcosa di suo e mi metta uno o più pin su uno stato Low o High, avendo deciso di usarli come pin digitali. A questo punto leggo o 0 o 1023, perdendo ogni entropia e ritrovandomi con la stessa sequenza di numeri generata ad ogni riavvio dell'Arduino.
hai provato con l'allocazone dinamica?il problema dovrebbe sparire..
oppure sempre usando l'inizializzazione lazy: prendi come sede sia il millis o qlk altro timer, più una versione raffinata dell'analog read:
Per ogni pin analogico
fai 2 letture consecutive e le metti in due variabili
per ogni bit che differisce tra la prima e la 2 lettura(usi xor), prendi il bit della 1^(o 2^) lettura e te lo tieni
ripeti il tutto finchè hai abbastanza bit per creare il seme e NON hai sforato un certo tempo..
ti va bene cm idea?
se vuoi,puoi fare una chiamata a vuoto nel setup..
A me va bene comunque la soluzione che ho trovato, che oltretutto dovrebbe risultare anche più veloce dell'analogRead dell'Arduino.
Il tuo suggerimento mi ha invece fatto venire in mente che potrei infilare dentro ad un timer una specie di raccoglitore di entropia
Creo un pool di entropia che poi alimento con le letture fatte ad esempio dall'utente sui vari pin oppure letture sui pin analogiche fatte ogni x, in modo da raccoglierne un po' anche dopo l'inizializzazione dell'algoritmo. Non vorrei però appesantire troppo il codice. Poi alla fine scoppia il micro dalla roba che gli metto dentro