Vorrei mettere in piedi un sistema che mi consenta di monitorare il valore della tensione di rete. L'idea è quella di un trasformatore 230 V => 6 V seguito da un ponte, condensatore di filtro con carico un semplice partitore calcolato in modo di avere un range sufficiente. Non mi serve altissima precisione, vorrei solo capire se ci sono sbalzi di qualche tipo. Giusto per contestualizzare, ogni tanto mi si sconnettono gli hard disk esterni e volevo intanto escludere cadute di tensione troppo brevi per essere avvertite ma sufficienti per creare questo problema. Inoltre non mi dispiace controllare cosa succede.
Per questo motivo penso sia più importante la velocità che la precisione (che peraltro un Arduino da solo non mi darebbe) e direi che un'uscita a intervalli di 200-100 ms dovrebbe permettermi di vedere eventuali fluttuazioni rapide.
Ho deciso di usare una coda per memorizzare i valori e farne la media. Ci sono quindi A) due tempi caratteristici: uno è quello per il polling dei pin e l'altro è quello per la scrittura dei valori in uscita (BTW ciascuna delle due funzioni è chiaramente riscontrabile in "loop"). B) La dimensione dell'array che modella la coda.
Ho scritto lo sketch che accludo e prima di costruire fisicamente il circuito ho pensato di introdurre una sezione di simulazione per fare una prima valutazione. La funzione genera una sinusoide utilizzando un valore del tempo a inizio loop in una variabile globale. Nel momento in cui c'è una differenza assegnata con il valore di picco (positivo o negativo che sia), viene memorizzato l'istante corrispondente. Per valori non compresi nell'intervallo che delimita il picco, utilizzo un decadimento lineare a partire dal valore massimo positivo. Per il calcolo basta semplicemente una diminuzione proporzionale alla differenza di tempo tra quello attuale e il precedente corrispondente al picco.
Come si può vedere dal codice, almeno nelle mie intenzioni questo dovrebbe emulare la sinusoide raddizzata. Pensavo di dimensionare il condensatore di filtro con un tempo caratteristico di 2-3 volte il periodo della tensione di rete, per avere un compromesso tra precisione (quella permessa dal tutto) e la necessità di individuare fluttuazioni rapide.
Purtroppo lo sketch non funziona. Il primo controllo è stato forzare il valore ritornato dalla funzione per i valori estremi, e funziona (se non altro questo mi dice che le istruzioni di scaling variabili dovrebbero essere a posto). Ma a parte questo, nessuna delle combinazioni dei parametri con valori ragionevoli sembra dare numeri sensati. Posso solo intuire che i tre parametri di cui in A e B non solo interagiscono tra loro ma anche con il clock interno. Ovviamente nulla mi assicura sulla corretta "schedulazione" delle varie sezioni ma ho pensato che il modello UNO che sto ora usando sia sufficiente. Probabilmente c'è come minimo qualcosa di assimilabile al problema dell'aliasing ma a questo punto mi sono perso. Ancora più probabilmente c'è qualche errore di concetto nell'implementazione.
In caso di risposta vorrei chiedere di mantenersi su tecniche non dico elementari ma quasi. Come si può facilmente notare a programmazione sto quasi a zero. Inoltre non ho mai usato gli interrupt, per esempio, né timers interni né altro, e non sono una cima in questo tipo di realizzazioni. Se l'errore è solo nel codice e si può far funzionare allora bene; ma nel caso improbabile che il codice vada bene e il problema è che per dirla in modo elementare "controllo e controllore sono la stessa cosa" e che quindi realizzando il circuito fisico risolverei, meglio ancora. Però ovviamente ora non ho modo di saperlo.
Dato che non voglio passare per uno che fa perdere tempo al prossimo, se il problema è più difficile di quelle che sono le mie capacità, anche sapere questo andrà bene e lascerò stare questo specifico progetto; ovviamente è inutile spendere il proprio tempo sapendo che non sarà stato possibile recepire la soluzione proposta (chiaramente se invece il problema è di interesse generale, suppongo il topic prenderà comunque la sua strada indipendentemente dal fatto che possa essere utile a me).
Credo che la scrittura del codice sia abbastanza auto esplicativa, o almeno spero.
Grazie per qualsiasi eventuale risposta.
- Codice: Seleziona tutto
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
#define PARX_DEMO_MODE 1
// ----------------------------------------------------- user parameters
#define PARX_OUTFMT_CODE 1 /*
* Serial port out format code
*
* 0 : row of integers
* 1 : format for "Serial port plotter by Borislav
*
*/
#define PARX_NUM_OF_CHANNELS 4
unsigned int GV_input_pins [PARX_NUM_OF_CHANNELS] = { A0 , A1 , A2 , A3 } ;
#define PARX_TIMEQUANTUM 0 // (ms) wait if nothing done in 'loop'
#define PARX_READATA_TIMESCAN 5 // (ms) pool pin values interval
#define PARX_OUTPUT_TIMEINTERVAL 200 // (ms) output time interval
#define PARX_SERIAL_SPEED 9600
#define PARX_QUEUES_SIZE 68 // queue size
#define PARX_VREAD_MIN (2) // output is 2 V @ 0 analog val
#define PARX_VREAD_MAX 380 // output is 380 V @ 1023 analog val
#define PARX_VREADOFFSET (0) // V added to output
// ----------------------------------------------------- internal parameters
#define PARX_CONV_RESOLUTION_BITS 10 // converter resolution
#define PARX_READSCALE2EXP 6 // scale factor for internal calculations
// -----------------------------------------------------
#if( PARX_DEMO_MODE != 0 )
#include <math.h>
#define PARDEBX_POWER_FREQ 50 // (Hz) power frequency
#define PARDEBX_DECAY_CONST 120 // (ms) time decay
const double GV_DEB_mfact = ((2.0*3.141592654*PARDEBX_POWER_FREQ)/1000.0) ;
const double GV_DEB_decayf = 0.5 / (double) PARDEBX_DECAY_CONST ;
#endif
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// % %
// % %
// % %
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
char GV_first_loop = 0 ; // reserved
char GV_queue_is_valid = 0 ;
unsigned long GV_time_last_pinread = 0 ;
unsigned long GV_time_last_output = 0 ;
unsigned long GV_time_at_loop_start ;
unsigned int GV_readings [PARX_NUM_OF_CHANNELS][PARX_QUEUES_SIZE] ;
int GV_queue_index_pos = -1 ;
unsigned int GV_fast_ic ;
unsigned int GV_fast_jc ;
unsigned long GV_accum ;
unsigned long GV_scalefac_Adiv ;
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// % %
// % %
// % %
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
#if( PARX_DEMO_MODE != 0 )
// 1023 for arduino
double GV_DEB_ypts_full = ( (double)( ( 2 << ( PARX_CONV_RESOLUTION_BITS - 1 ) ) - 1 ) / 1.0 ) ;
// time at sin max/min
unsigned long GV_DEB_start_decay [PARX_NUM_OF_CHANNELS] ;
// emulate analog read with rectified tension
unsigned int ret_analog_read (const unsigned int a_piin)
{
unsigned int res ;
double sinres ;
double sval ;
// calculate min/max of Vin, each channel has it own phase shift
sinres = sin ( ( 0.15 * a_piin ) + ( GV_DEB_mfact * GV_time_at_loop_start ) ) ;
if( ( sinres < -0.95 ) || ( sinres > 0.95 ) )
{
GV_DEB_start_decay [a_piin] = GV_time_at_loop_start ;
sval = GV_DEB_ypts_full ;
}
else
{
sval = ( GV_DEB_ypts_full * ( 1.0 - GV_DEB_decayf * (double)( GV_time_at_loop_start - GV_DEB_start_decay [a_piin] ) ) ) ;
}
if( sval <= 0 )
res = 667 ;
else if( sval > GV_DEB_ypts_full ) // ISTR DEB
res = 666 ;
else
res = (unsigned int) sval ;
// FORZATO!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
res = 1023 ;
return (res) ;
}
#endif
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// % %
// % %
// % %
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void setup ()
{
#if( PARX_DEMO_MODE != 0 )
for( unsigned int icc = 0 ; icc < PARX_NUM_OF_CHANNELS ; icc++ )
GV_DEB_start_decay [icc] = 0 ;
#endif
// scale factor
GV_scalefac_Adiv = (unsigned long) ( ( (double)( PARX_QUEUES_SIZE ) *
(double)( 2 << (PARX_READSCALE2EXP-1) ) *
(double)( ( 2 << (PARX_CONV_RESOLUTION_BITS-1) ) - 1 ) ) /
( (double)( PARX_VREAD_MAX - PARX_VREAD_MIN ) ) ) ;
// what else?
Serial.begin (PARX_SERIAL_SPEED) ;
while( !Serial )
{
;
}
delay (10) ;
}
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// % %
// % %
// % %
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void loop ()
{
char flag_taskrun = 0 ;
long calcd_value ;
unsigned int outval ;
GV_time_at_loop_start = millis () ;
// read pin values and store in the queue
if( ( GV_time_at_loop_start - GV_time_last_pinread ) >= PARX_READATA_TIMESCAN )
{
GV_queue_index_pos++ ;
if( GV_queue_index_pos >= PARX_QUEUES_SIZE )
{
GV_queue_index_pos = 0 ;
if( !GV_queue_is_valid )
GV_queue_is_valid = 1 ;
}
for( GV_fast_ic = 0 ; GV_fast_ic < PARX_NUM_OF_CHANNELS ; GV_fast_ic++ )
{
#if( PARX_DEMO_MODE != 0 )
GV_readings [GV_fast_ic][GV_queue_index_pos] = ret_analog_read (GV_fast_ic) ;
#else
GV_readings [GV_fast_ic][GV_queue_index_pos] = analogRead (GV_input_pins[GV_fast_ic]) ;
#endif
}
GV_time_last_pinread = GV_time_at_loop_start ;
flag_taskrun = 1 ;
}
// write output values
if( ( ( GV_time_at_loop_start - GV_time_last_output ) >= PARX_OUTPUT_TIMEINTERVAL )
&& ( GV_queue_is_valid ) )
{
#if( PARX_OUTFMT_CODE == 1 )
Serial.print ("$") ;
#endif
for( GV_fast_ic = 0 ; GV_fast_ic < PARX_NUM_OF_CHANNELS ; GV_fast_ic++ )
{
GV_accum = 0 ;
for( GV_fast_jc = 0 ; GV_fast_jc < PARX_QUEUES_SIZE ; GV_fast_jc++ )
{
GV_accum += ( ( 2 << (PARX_READSCALE2EXP-1) ) * (unsigned long)( GV_readings [GV_fast_ic][GV_fast_jc] ) ) ;
}
calcd_value = ( (long)GV_accum / GV_scalefac_Adiv + PARX_VREAD_MIN ) + PARX_VREADOFFSET ;
if( ( calcd_value >= 0 )
&& ( calcd_value <= ( ( 2 << ( PARX_CONV_RESOLUTION_BITS - 1 ) ) - 1 ) ) )
outval = (unsigned int) calcd_value ;
else
outval = 0 ;
Serial.print (outval) ;
if( GV_fast_ic < ( PARX_NUM_OF_CHANNELS - 1 ) )
Serial.print (" ") ;
}
#if( PARX_OUTFMT_CODE == 1 )
/*
Serial.print (" :: ") ;
Serial.print (GV_DEB_ypts_full) ;
*/
Serial.println (";") ;
#else
Serial.println () ;
#endif
GV_time_last_output = GV_time_at_loop_start ;
flag_taskrun = 1 ;
}
if( flag_taskrun == 0 )
{
#if( PARX_TIMEQUANTUM > 0 )
delay (PARX_TIMEQUANTUM) ;
#endif
}
GV_first_loop = 1 ;
}