Ciclo While non funziona

Salve, ho creato un programma di esempio per verificare il funzionamento di un mio progetto. Prima del cilclo while avevo inserito i cicli for con delay, poi ho cercato di modificare usando millis e while ma non funziona. Ho messo dei print per verificare i passaggi, ma non viene stampato niente, come se nel ciclo while non entrasse.


#include <RBDdimmer.h>//


#define outputPin  12 
#define al 20 // percentuale
#define tempo 300 
int mta=0;  // Indica metà tempo di alba
int flag = 0;
int i=0;
long mil = 0;

dimmerLamp dimmer(outputPin); //initialase port for dimmer for MEGA, Leonardo, UNO, Arduino M0, Arduino Zero

int outVal = 0;

void setup() {
Serial.begin(9600);
  dimmer.begin(NORMAL_MODE, ON); //dimmer initialisation: name.begin(MODE, STATE) 
  dimmer.setPower(0);
}

void loop() 
{
   mta=(((al/2)*tempo)/100)*10;
   
 while (flag = 0) {
    Serial.println(flag);
   if (millis()-mil > tempo) {
     i++;
      if (i>100) flag = 1; i=100;
     Serial.print(i);
dimmer.setPower(i); // name.setPower(0%-100%)
 mil = millis(); 
  }
 }
flag = 0; 
 // Serial.println(flag);
while (flag = 0) {
    
   if (millis()-mil > tempo) {
     i--;
    if (i<1) flag = 1; i=0;
dimmer.setPower(i); // name.setPower(0%-100%)
 Serial.println(i);
 mil = millis(); 
  }
 } 
}

È lo stesso problema di: https://forum.arduino.cc/t/if-non-funziona/1069826/6

Porca miseria, non mi riesce ad entrare in testa, il bello è che ho visto anche tanti esempi in rete. Di sicuro mi porto dietro la programmazione in Basic che facevo da giovane

A maggior ragione nel test di uguaglianza meglio abituarsi a scrivere la parte costante per prima, così il compilatore segnala l'errore.

Secondo problema, anche le righe:
if (i>100) flag = 1; i=100;
sono corrette sintatticamente ma non come logica. Solo la prima istruzione dopo if è condizionata, mentre i=100 viene eseguita sempre. Anche qui meglio abituarsi a racchiudere sempre tra graffe tutto quello che deve essere sotto condizione.

Che intendi per scrivere prima la parte costante?
Hai detto che dopo l'if viene eseguito solo il primo comando se è vera la condizione, mentre il secondo viene sempre eseguito, però non è un errore sintattico. In rete ho visto parecchi tutorial ma nessuno parla mai di sintassi. C'è un posto in cui si parla di sintassi?
Sembra una scemenza ma ricontrollando il codice dopo un errore, mi accorgo, a volte, di aver scritto un comando in Basic, tipo il ciclo For Next, If End If. Forse perché abituato al Basic ma secondo me il C++ lo hanno complicato inutilmente. Perché non continuare ad usare la sintassi e i comandi del Basic ed aggiungere solo i nuovi comandi? Tanto è comunque un linguaggio compilato. Pure gli errori che restituisce l'IDE sono criptici, raramente riesco a trovare la causa da quello che viene scritto, devo sempre ricontrollare tutto il codice. Infatti a volte, mi viene evidenziato una riga ma l'errore si trova ad un'altra riga.

Se scrivi while (0 = flag) ottieni un errore di compilazione, mentre con while (flag = 0) no.
Come ti ha già evidenziato @Claudio_FF nel while non stai facendo un confronto (operatore == ), ma un'assegnazione (opratore = )

Perché sono due linguaggi completamente diversi che non hanno nulla a che vedere l'uno con l'altro a partire dal principio di funzionamento: il BASIC nasce come linguaggio interpretato (ovvero un incubo per il debugging visto che alcuni errori li scoprivi solo in esecuzione :wink:) mentre il C/C++ è un linguaggio compilato.

Se vuoi un consiglio non richiesto, lascia perdere questo approccio linguaggio A - linguaggio B; anche a me capita ogni tanto di "confondere" alcune istruzioni tipiche del Javascript mentre scrivo del codice C++ (sono molto simili sintatticamente).
Il concetto di programmazione non si esaurisce con il linguaggio scelto per lo sviluppo che anzi secondo me è del tutto secondario e poco importante.

Ciò che è fondamentale è l'algoritmo che sta alla base del software e ad esempio in questo tuo sketch è "debole" perché dici di aver sostituito i delay() con un ciclo while(), ma dal punto di vista funzionale non cambia nulla perché l'algoritmo è ancora bloccante.
Se ad esempio tu volessi far eseguire al micro un ulteriore compito, che ne so tipo leggere lo stato di un pulsante ed agire di conseguenza, dovresti comunque attendere il completamento dei due while() esattamente come accadeva prima con l'istruzione delay().

P.S.
Non c'entra nulla con lo sketch in se, ma se vuoi ottenere un dimming efficace dal punto di vista visivo dovresti tenere conto della curva di sensibilità dell'occhio umano alla luce e fare un dimming logaritmico.

Grazie per le delucidazioni. Concordo sul fatto che il linguaggio usato per risolvere un problema è secondario, come hai detto tu è più importante la logica usata per risolvere il problema. Però è anche vero che se non si conoscono bene tutte le istruzioni del linguaggio usato, poi la logica non è facilmente implementabile. Hai ragione anche sul fatto di aver tolto un codice bloccante e averlo sostituito con uno diverso ma lo stesso bloccante. Sto facendo degli esperimenti per capire come funzionano i comandi. Più che altro volevo ottenere una durata più precisa dei. Per esempio, il codice scritto è relativo all'alba, prima sale, prendendo il posto della notte e poi scende mentre sale la luce del giorno (non ancora inserita). I tempi di salita e discesa variano in base al tempo totale, regolabile tramite Pulsanti e LCD, e in base alle percentuali che ho dato per ogni fase, 20% per alba, tramonto e notte e 40% per il giorno. So benissimo che a Natale dura di più la notte che il giorno, ma nel presepe deve essere il contrario, per dare più tempo allo spettatore di vedere bene le scene. Infatti, facendo durare di più la notte, è capitato che qualche visitatore accendesse la luce del telefonino per vedere.
Sempre dalle prove ho visto che lil mio occhio riesce a vedere la variazione di luce fino all'80% della potenza totale in una scala da 0 a 100, sempre con regolazione lineare. Penso che non conviene complicare le cose per introdurre una regolazione logaritmica, perché il visitatore non noterebbe la differenza.
Riguardo al codice non bloccante sto pensando a come farlo perché in effetti ci sarebbero i Pulsanti per la regolazione del tempo da verificare sempre.
Attualmente ho pensato di creare un ciclo fò da 0 a 100 con delay, per ogni fase, in ogni ciclo metto solo le uscite che devono variare in quella fase del giorno, il delay viene calcolato con la formula che vedi nel programma, è abbastanza precisa. Mettendo il controllo dei Pulsanti ad inizio loop, è logico che il controllo verrà fatto alla fine del quarto ciclo, e per una frazione di secondo, quindi difficile da prendere. Quindi o si fa un codice non bloccante o al posto dei Pulsanti metto un potenziometro, se lo Ruoto durante una delle fasi, il suo valore verrà letto quando il loop inizierà dall'inizio, così tutti i tempi che seguiranno saranno impostati da questo valore.
Vorrei inoltre, visualizzare il tempo impostato suun LCD e sempre su un LCD vorrei impostare il nome della fase e quanti secondi mancano alla sua fine.
Nel presepe del rione, abbiamo già una centralina ma è davvero complicata da usare, ha tutti potenziometri e nessun LCD che indichi qualcosa.
Per uno che ha iniziato ad usare Arduino nel periodo della pandemia e basta, ammetto che è un progetto importante.

E' proprio per questo motivo che serve una regolazione logaritmica dell'intensità luminosa, ovvero per fare in modo che l'occhio percepisca una variazione lineare (perché si compensa la curva di sensibilità media dell'occhio umano).
Implementarla è una cosa semplice: si possono usare le formule descritte nel CIE1931 oppure una "lookup table" (io preferisco quest'ultimo metodo).
Un semplice esempio con fade-in fade-out (ovviamente non bloccante) e tabella di correzione.
Sul simulatore l'effetto non si percepisce, dovresti provare con un led reale (più resistenza che ho omesso ovviamente).

Più in generale, per quanto riguarda il codice non bloccante, devi completamente eliminare il concetto di ciclo while o for che sia: la funzione loop() viene già eseguita ciclicamente e non ha senso mettere un ciclo nel ciclo.

Bello il concetto di eliminare ciclo for e while, più difficile metterlo in pratica, almeno per me che inizio ora. Però mi concentrerò su questo sistema, mi piacciono le sfide. Stasera controllo sul pc il tuo programma e lo adatterò alla mia scheda di uscita.
Giusto per curiosità, come mai sei così ferrato su Arduino?

Non mi definirei propriamente "ferrato"... diciamo che sono il classico smanettone che si diverte nel provare le cose :sweat_smile:

Per lavoro mi occupo di manutenzione e sviluppo di impianti di automazione industriale e questo immagino che in qualche modo abbia contribuito alla "forma mentis", ma il mondo dei dispositivi embedded per me è solo un hobby.

Io ci ho messo due anni per trovare una soluzione a tutti i problemi generici. Ne ho sperimentate tante, la più semplice è quella sotto il naso che chissà perché da principianti non la si vede ed è quella del super loop. Riassumendola i punti sono:

  1. il for e while ecc sono cicli innestati nel super loop, se la durata di questi è considerata bloccante non li si deve usare ma al loro posto si usa il ciclo principale (il super loop).

  2. Il controllo deve essere ceduto al super loop il più spesso possibile. Es la funzione chiamata nel super loop a causa di un evento mi porta ad eseguire un compito. Posso eseguirlo subito se ci impiega poco tempo oppure posso schedularlo e distribuire il compito in più cicli di super loop. ES: devo inviare 100 byte o li invio in un for bloccante oppure invio il primo byte e cedo il controllo al super loop. Al prossimo ciclo invio il secondo byte ecc.

  3. Il tempo è discreto. Cosa accade tra un ciclo e l'altro è sconosciuto. Un conto è però non conoscere cosa accade per un secondo un altro per 1us. In ogni caso il tempo scorre sempre a scatti.

  4. Il tempo del super loop dovrebbe essere inferiore a 5ms. Poiché si riesce facilmente a stare sotto o nei dintorni di 1ms per ciclo questo è l'obbiettivo. Molte librerie Button falliscono se il ciclo supera i 5ms ma non dipende dalla specifica libreria ma dal debounce software necessario con i pulsanti elettromeccanici.

  5. Dimentico sicuro qualcosa.

PS: il delay ti porta fuori strada.

Ciao.

1 Like

Forse no, ma è meno complicato di quanto sembra. Il mio occhiometro trova "gradevole" e lineare un fading facendo il quadrato del valore per le lampade a incandescenza, e il cubo del valore per i LED.

Ad esempio se una lampada a incandescenza viene pilotata con un valore da 0 (spenta) a 100 (massima luminosità), per vederlo lineare con l'occhio basta fare il quadrato e dividere per 100.

Per un LED pilotato in PWM da 0 a 255 basta fare il cubo e dividere per 65025.

L'unico svantaggio è che se ci sono pochi punti, e si fa un fading molto lento, si potrebbero notare gli scalini alle basse luminosità.

Riguardo al codice non bloccante sto pensando a come farlo perché in effetti ci sarebbero i Pulsanti per la regolazione del tempo da verificare sempre.

Basta strutturare il programma sotto forma di tanti "predicati" nella forma:

SE sono in questa situazione
   SE succede questo
      faccio questo

dove "questa situazione" può essere qualsiasi combinazione di variabili o flag che indicano a che punto dell'elaborazione siamo arrivati, e "succede questo" è una condizione/evento che ci aspettiamo in quella situazione.

Non è bloccante perché "faccio questo" viene eseguito se e solo se sono vere le condizioni precedenti.

Come faccio a capire quali "situazioni" sono significative, e quindi di quante situazioni, cioè stati del sistema, devo tenere conto? Quasi semplice, ogni volta che ti trovi a dire "adesso aspetto questo o quest'altro", quello è uno stato del sistema.

Con questa logica, ogni ritardo/attesa è semplicemente la permanenza in un certo stato/situazione per N cicli di loop (il superloop di Maurotec). Quando c'è l'evento giusto si fa quello che serve (se serve), si disabilita lo stato corrente che stava aspettando l'evento (si rende falso il vecchio "questa situazione"), e si abilita il nuovo stato (si rende vero il nuovo "questa situazione").

Tutto questo non riguarda strettamente ne Arduino ne il linguaggio C, anche in BASIC si può realizzare questa logica, sui sistemi embedded è particolarmente importante perché appunto ci si trova a dover gestire in tempo reale più cose "contemporaneamente".

Quante belle spiegazioni mi avete dato, in pratica mi avete messo il pepe in xxxx, mi avete stuzzicato non poco, grazie a tutti.

--- cortesemente, moderate sempre il linguaggio. Grazie. -gpb01

Ho letto il tuo sketch ed è quasi tutto arabo. Per capirlo dovresti specificare il funzionamento di ogni riga. Il tuo programma usa l'uscita pwm n.3 per il led e può andar bene perchè uso varie uscite pwm a cui sono collegati luci led. In più uso anche uscite digitali utilizzate dalla libreria RBDdimmer.h. Se col led i valori vanno da 0 a 255, con i pin della libreria i valori vanno da 0 a 100%.
Dal tuo sketch ho capito che del linguaggio non so un tubo.

Ti aggiungo qualche commento per facilitare la lettura.

La libreria RBDdimmer se non ricordo male è per gestire delle uscite a TRIAC giusto?
Il funzionamento non cambia poi molto perché tu stai dimmerando con un valore che varia da 0 a 100, mentre usando la canonica uscita PWM Arduino l'uscita varia da 0 a 255.

Ovviamente va calcolata una nuova tabella di lookup per gestire il dimming... anzi facciamo che partiamo da uno sketch più semplice senza dimming logaritmico giusto per impratichirsi con un approccio non bloccante....

Ho modificato lo sketch rimuovendo anche il tempo impostabile con il potenziometro che quindi verrà eseguito sempre a tempo costante.
Spero sia un po' più chiaro (e didattico), nel caso chiedi ancora.

Ho modificato il vecchio sketch con while in uno non bloccante, almeno spero. Il tuo lo vedrò a pancia piena. A dopo.
Si quella libreria serve per i triac.

#include <RBDdimmer.h>//


#define outputPin  12 
#define al 20 // percentuale totale della prima fase, Alba
#define tempo 300 // Indica quanti secondi dura l'intero ciclo di 4 fasi (quanto dura il giorno)
int mta=0;  // Indica metà tempo di alba
int flag = 0;
int i=0;
long mil = 0;
int fase = 0; // indica in quale fase ci troviamo: 0= fase ascendente alba, 1= fase discendente alba
dimmerLamp dimmer(outputPin); //initialase port for dimmer for MEGA, Leonardo, UNO, Arduino M0, Arduino Zero

int outVal = 0;

void setup() {
  dimmer.begin(NORMAL_MODE, ON); //dimmer initialisation: name.begin(MODE, STATE) 
  dimmer.setPower(0);
}

void loop() 
{
  mta=(((al/2)*tempo)/100)*10; // formula per calcolare quanto tempo (in millisecondi) dura una fase in base al tempo totale del giorno e alla percentuale della fase
  
  // -------------- Fase ascendende dell'alba (10%) ------------------

  if (millis()-mil > tempo && fase==0 ) {
     i++;
      if (i>100) {
        fase = 1; 
        i=100;
      }
   dimmer.setPower(i); // name.setPower(0%-100%)
   mil = millis(); 
  }
 
  // -------------- Fase discendente dell'alba (10%) ------------------

 if (millis()-mil > tempo && fase == 1) {
    i--;
    if (i<1) {
      fase = 0; 
      i=0;
    }
  dimmer.setPower(i); // name.setPower(0%-100%)
  mil = millis(); 
  }
}

A parte considerazioni puramente stilistiche, direi tutto giusto.
A dire il vero la variabile 'mil' dovrebbe essere di tipo senza segno (unsigned long), ma fino a 596 ore di funzionamento continuo non da problemi (poi il suo valore entrerebbe nel range negativo e non so bene come si comporterebbe la comparazione con millis).

che intendi per considerazioni stilistiche?
come si interpreta questa riga? fadeIn = (stepNum < 255);

A parte le righe per i pulsanti, il codice per il fader sembra uguale a quello scritto da me.

Questo è lo schema creato su un foglio per capire come si devono accendere e spegnere le luci durante le varie fasi del giorno. In orizzontale ci sono le 4 fasi del giorno suddivise in percentuali. Le percentuali possono essere variate in base a come sarà il risultato finale reale nel presepe, per ora ho immaginato queste percentuali: Alba = 10%, Giorno = 50%, Tramonto = 10%, Notte = 30%.
L'alba avrà una lampada gialla, il giorno una lampada bianca, tramonto lampada rossa e notte lampada blu.

Per alba, giorno, tramonto e notte userò lampade a filamento, per tutto il resto led. Le stelle vorrei farle tremolanti. Ho messo 3 uscite stelle per rendere più naturale il luccichio, se avessi messo un'uscita, tutte le stelle luccicherebbero allo stesso modo.

Per le case ho usato due uscite perchè voglio farle accendere e spegnere in modo graduale e in momenti differenti, per essere più reale.

I lampioni si accendono e spengono lentamente tutti insieme.

Potresti tradurre in codice il tuo occhiometro? Devo pilotare sia lampade a incandescenza che led.

E' sorto un problema non da poco, se allo sketch di prima aggiungo

dimmerLamp dimmer(alba);      // Dice quale pin  è abilitato all'uscita per l'alba
 dimmerLamp dimmer1(giorno);   // Dice quale pin  è abilitato all'uscita per il giorno
 dimmerLamp dimmer2(tramonto); // Dice quale pin  è abilitato all'uscita per il tramonto
 dimmerLamp dimmer3(notte);    // Dice quale pin  è abilitato all'uscita per la notte

solo come definizione prima del setup e faccio funzionare sempre un'uscita come nello sketch, la luce impazzisce, se tolgo dimmer1, dimmer2 e dimmer3, la lampadina torna a funzionare correttamente.