albe ed aurore fin boreali, giorni e tramonti fin tropicali

Che è una citazione da "la genesi" di Francesco Guccini
mi riallaccio così a quanti (e sono tanti) che vorrebbero fare dei generatori di alba e tramonto
sia per delle serre che per dei plastici che per dei presepi
io mi limito ad un generatore "universale" che permette la generazione in PWM di rosso verde e blu a dare tutte le possibili condizioni di luce
il tutto si basa sul lavoro "preparatorio" che avevo fatto per dare una mano a uno, qui
che poi se le è cavata egregiamente da solo, bene ne sono contento
però vi lascio qui il mio "distillato di sudore" che essendo ben commentato potrebbe essere una buona base di partenza per "allargarlo" per fare "di più" oppure anche per ridurlo
ad esempio si potrebbero gestire 2 o 3 "batterie" di led RGB per far "muovere" la luce durante il giorno, oltre che indebolire e cambiare colore
il trutto si basa su un tipo dati struct che deve contenere tutte le informazioni necessarie a rappresentare lo stato del sistema

ecco un esempio:

// la struttura che contiene le ore
typedef struct
{
    byte ora;
    byte minuto;
    byte rosso;
    byte verde;
    byte blu;
    char nome[15];
} orario;

naturalmente all'ora "giusta" serve di "eseguire" questa struttura, o meglio una variabile del tipo di questa struttura:

void accendi(orario appoggio)
{
    // esegue l'accensione dei vari led come descritto in appoggio
    analogWrite(LEDR, appoggio.rosso);
    analogWrite(LEDG, appoggio.verde);
    analogWrite(LEDB, appoggio.blu);
    return;
}

come vedete gli faccio eseguire le accensioni in PWM, abbastanza banale ma sono le cose banali che vanno, l'idea, ripresa dalla programmazione in C++ sarebbe di "chiudere" tutte le operazioni dentro qui, senza andare in giro a sporcare il programma
non è programmazione ad oggetti, ma un'idea presa da li

ora si tratta di sapere se (e quando) agire sui led
a intervalli regolari (e qui si chiede aiuto alla millis e/o a un rtc
si chiama una funzione che data ora e minuto ci dice che "oggetto" dobbiamo eseguire
si puo' anche chiamarla di continuo, ad ogni giro di loop, tanto se la luminosità non cambia non ha effetto
ma servirà per dopo, vedrete

orario attuale(byte ora, byte minuto)
{
    // restituisce la struttura orario che descrive lo stato attuale
    // versione a scatti
    for (byte i = 0; i < PASSI; i++)
    {
        int momento = ora * 60 + minuto;

        if ((programma[i].ora * 60 + programma[i].minuto) > momento)
        {
            return programma[i - 1];
        }
    }
}

come vedete dandogli in pasto ora e minuto (anche simulati in un plastico) ci dice quale stato deve venire acceso
con una banale

accendi(attuale(ora, minuto));

la attuale() restituisce dritto tra le braccia della accendi() un oggetto adeguato
e in questa maniera evito di dover passare uno, due, o tre valori numerici, passo un oggetto struttura che per costruzione (lo abbiamo fatto così all'inizio) ha tutto quello che serve
se si dovesse aggiungere una seconda terna di led si allarga la struttura e si aggiungono tre righe alla esegui
ad esempio il membro char * nome è già li proprio per eventuali stampe

la vera menata sarebbe costruire l'array di struttura
per fortuna una vecchia caratteristica del preprocessore, poco documentata e credo risalente ancora al 'B' ci permette di farlo con facilità

#define STATI \
    X( 0,  0,   0,   0,   0, mezzanotte) \
    X( 2,  0,   5,   5,  10, stelle) \
    X( 5, 35,  10,  12,  15, lucore) \
    X( 6,  0, 100, 102,  55, alba) \
    X( 8,  0, 255, 255, 255, giorno pieno)\
    X(18,  0, 255, 255, 255, fine giorno)\
    X(19,  0, 100, 102,  55, tramonto) \
    X(19, 30,  50,  55,   2, notte) \
    X(24, 00,   0,   0,   0, termine)
// ricordarsi che i nomi sono lunghi al massimo 15


// l'inizializzazione dell'array di struttura
#define X(ora, minuto, rosso, verde, blu, nome) {ora, minuto, rosso, verde, blu, #nome},
orario programma[] = { STATI
#undef X
                     };

//il numero di passi
#define PASSI  sizeof (programma ) /sizeof (orario)

la prima define (STATI) non è terminata, notate la barra retroversa alla fine della riga
e notate pure la X(.....e qui si scrive ora minuto etc etc separati da virgola)
la barra retroversa NON termina la riga
mentre la X verrà usata in una successiva define a se stante
l'unica riga terminata è l'ultima
di maniera che il tutto appare coma un'unica lunghissima riga di define
la seconda define X(....
serve per espandere dentro una inizializzazione di array le varie X(...) che abbiamo scritto prima
difatti vedete che definosce la X( come quasi fosse una macro, ne espande anche gli argomenti
dopo la sua undef chiude la definizione dello array con };
la terza define è banale, per sapere il numero di elementi senza doverli contare

allego qui il file ino ma ci sono ancora due cose da dire

albatramonto.ino (4.4 KB)

Ho cambiato post per problemi di lunghezza
rimane da dire un paio di cose

per un presepio e/o un plastico basta simulare lo scorrere delle ore con un delay (o usando millis() )

void loop(void)
{
    // per non usare un orologio, e per provare in fretta simulo lo scorrere delle ore a velocità accelerata
    for (byte ora = 0; ora < 24; ora++)
    {
        for (byte minuto = 0; minuto < 59; minuto++)
        {
            Serial.print("Ora attuale: ");

            if (ora < 10)
            {
                Serial.print('0');
            }

            Serial.print(ora);
            Serial.print(':');

            if (minuto < 10)
            {
                Serial.print('0');
            }

            Serial.print(minuto);
            accendi(attualesoft(ora, minuto));
            delay(100);
        }
    }
}

che faccio anche stampare l'ora, per debug
però noterete che ho usato una funzione della quale non avevo parlato
la attualesoft() al posto della atttuale
la attualesoft() restituisce non un oggetto letto dall'array di oggetti pre-inizializzato con la macro X
ma l'interpolazione lineare tra i due oggetti immediatamente precedente ed immediatamente successivo all'ora in corso
eccola:

orario attualesoft(byte ora, byte minuto)
{
    // restituisce la struttura orario che descrive lo stato attuale
    // versione a rampa
    orario appoggio;
    byte i = 0;

    for (i = 0; i < PASSI; i++)
    {
        int momento = ora * 60 + minuto;

        if ((programma[i].ora * 60 + programma[i].minuto) > momento)
        {
            appoggio = programma[i - 1];
            break;
        }
    }

    // ora devo calcolare le luminosità
    // tempo passato dall'ultimo cambio
    int passato = (ora - appoggio.ora) * 60 + minuto - appoggio.minuto;
    // tempo che deve passare in totale prima del prossimo cambio
    int totale = (programma[i].ora - appoggio.ora) * 60 + programma[i].minuto - appoggio.minuto;
    // frazione
    float frazione = (float)passato / totale;
    appoggio.rosso = programma[i - 1].rosso + 0.5 + frazione * (programma[i].rosso - programma[i - 1].rosso);
    appoggio.verde = programma[i - 1].verde + 0.5 + frazione * (programma[i].verde - programma[i - 1].verde);
    appoggio.blu = programma[i - 1].blu + 0.5 + frazione * (programma[i].blu - programma[i - 1].blu);
    return appoggio;
}

modificando opportunamente sia l'oggetto struct, che la dichiarazione dell'array di oggetti, che la funzione di esecuzione si ottiene un generatore di albe e tramonti "universale"

1 Like

Eeeehhhhhbeh......
C è chi ci mette 3 giorni e crea uno sformato e chi in 1 giorno crea un pasticcino.....complimenti.

Grazie
non per i complimenti, che fanno sempre piacere

ma per aver letto e cercato, anche al di fuori del tuo trehad specifico

meriti un karma, per l'attività e la curiosità

poi se ne hai bisogno, libero di ispirarti, copiare, criticare e chiedere

Standardoil:
....ma l'interpolazione lineare tra i due oggetti immediatamente precedente ed immediatamente successivo all'ora in corso

Adesso la ho capita...
geniale
noi invece lo ricalcoliamo a passi costanti durante l'accensione in pwm

togli l'interpolazione lineare, metti interpolazione trigonometrica, sfasi i tre colori di un angolo opportuno e te la cavi con solo 2 punti per l'intera giornata... buio e piena luce

non basta, il rosso è sfasato in anticipo la mattina e in ritardo la sera
servirebbe di variare la linearità dei led (dei valori mappati sui led) e magari di lavorare in un differente spazio colore, perché è sfasato l'arancione, più che il rosso, specialmente la mattina
comunque la cosa di cambiare l'interpolazione è buona