Dimmer LED e potenziometro.

Salve a tutti,
premetto che sono nuovo di arduino e non ho buone conoscenze di programmazione, quindi siate comprensivi :slight_smile: .
Volevo far in modo che per valori letti dal potenziometro compresi tra 0 e 255 questi venissero usati direttamente per dimmerare il led mentre per valori da 256 a 1023 vi fosse un dimmer decrescente. Cioè in soldoni volevo che partendo da 0 con il potenziometro e girando il led aumentasse di intensità fino a 255 raggiungendo il massimo e poi, continuando a girare, diminuisse gradualmente fino a spegnersi per valore di potenziometro di 1023. Questo è il codice che ho scritto:

 int value;
int green = 5; /*led da dimmerare*/
int test = 10; /*led di controllo*/
void setup() {
	Serial.begin(9600);
	pinMode(green, OUTPUT);
}

/* di seguito la funzione per ingressi da 255 a 1023 restituisce un valore compreso tra 255 e 0 (decresente)*/
int funzione (int a) {
	int y;
	y = 255 * (a - 1023) / (255 - 1023);
	return y;
}

void loop() {
	value = analogRead(A0);
	Serial.println(value);
	if (value >= 0 && value <= 255) {
		analogWrite(green, value); /*dimmero il led*/
		analogWrite(test, 0); /*tengo spensto il controllo*/
	}
	else
	{
		analogWrite(test, 255); /*accendo il controllo*/
		int nuova = funzione(value);/*calcola il nuovo ingresso per il dimmer dalla funzione sopra*/
		analogWrite(green, nuova);
		Serial.println(nuova);
	}
	delay(10);
}

Il led di test è solo per conferma visiva di superare 255 con il potenziometro. Il problema è che la variabile “nuova” si comporta in modo strano, assumendo anche valori negativi a volte, quindi il dimmer da 255 a 1023 non funziona. Sicuramente ho sbagliato qualcosa nel codice. Vedete l’errore?
Grazie in anticipo a chiunque risponderà.

Beh, è sbagliata la funzione...
Se vuoi che il risultato vari da 255 a 0 con un ingresso da 256 a 1023 puoi fare più semplicemente y=map(a,256,1023,255,0)
Ciao.

Ciao, grazie per la risposta.
Si certo conosco il comando map. Ma appunto mi stavo chiedendo perché non funzionasse con la mia funzione?
Mi potresti spiegare meglio dove sta l'errore nella funzione o come la scriveresti in maniera corretta?
È la retta che passa per (255,255) e (1023,0) in teoria, quindi dovrebbe restituire valori tra 255 e 0.
Perché se la faccio girare su un progetto di C++ con la console si comporta come dovrebbe.
Grazie :slight_smile:

Ciao,
ho sbagliato pensando che volessi più una soluzione che una spiegazione... scusa.
Il "problema" è che siamo su una povera mcu a 8 bit senza fpu. anche sul mio mac la tua funzione funziona :slight_smile: perfettamente.
Se però la scrivi così:

y = 255.0 * (a - 1023) / (255 - 1023)

oppure così:y = 255 * (a - 1023.0) / (255 - 1023)restituisce il valore corretto.
In pratica si tratta di forzare il compilatore. Quando ci son di mezzo divisioni io lo faccio d'ufficio.
Per una spiegazione più dettagliata dobbiamo aspettare qualcuno più esperto di me.

Ciao,
figurati, non avevo specificato bene! Cioè se provassi a “forzare” il compilatore (non so bene cosa significhi) aggiungendo quella parte decimale potrebbe girare bene anche su arduino? Appena arrivo a casa ci provo. Grazie mille.
Si non capisco bene questa cosa, cioè non sono, che so, equazioni differenziali, è una semplice divisione, nella mia testa di profano sembra una cosa fin troppo semplice da fare anche per una piccola scheda.
Si sarei interessato ad approfondire questo discorso, capire bene perché mi restituisce valori negativi a volte, cosa dal punto di vista matematico impossibile usando quella funzione.
Può essere il fatto che effettivamente la funzione è un numero decimale mentre io la associo ad int a creare problemi?
PS. ho notato che se la funzione la scrivo così:

int funzione (int a) {
	int y;
	y = 255 * ((a - 1023) / (255 - 1023));
	return y;
}

mi restituisce sempre 0! com’è possibile? cioè quelle parentesi in più matematicamente sono corrette ma dal punto di vista del codice creano problemi?

Aggiornamento: funziona! con il tuo metodo :slight_smile: tuttavia ancora non capisco perché. Leggendo un po' di teoria i valori negativi potrebbero saltare fuori da overflow. Così ho provato a fare

int funzione (int a) {
	float y;
	int b;
	y = 255*(a - 1023) / (255 - 1023);
	b = (int)y;
	return b;
}

visto che gli appunti che sto leggendo consigliano di usare float quando ci sono divisioni e poi convertire in int. La funzione però mi continuava a restituire valori altalenanti e anche negativi....sono perplesso :sweat_smile:

Ciao. è sicuramente un problema di overflow sulle variabili a 16 bit.
ho scritto questo sketch di prova:

void setup() {
  Serial.begin(115200);
  for (int i = 255; i < 1024; i++) {
    int dummy = (funzione(i));
  }
}
int funzione (int a) {
  int y;
  Serial.print("a=");
  Serial.print(a);
  Serial.print(char(9));
  Serial.print("a-1023=");
  Serial.print(a - 1023);
  Serial.print(char(9));
  Serial.print("255*(a-1023)=");
  Serial.print(255 * (a - 1023));
  Serial.print(char(9));
  Serial.print("255.0*(a-1023)=");
  Serial.print(255.0 * (a - 1023));
  Serial.print(char(9));
  Serial.print("255*(a-1023)/(255-1023)=");
  Serial.print(255*(a-1023)/(255-1023));
  Serial.print(char(9));
  Serial.print("255.0*(a-1023)/(255-1023)=");
  Serial.println(255.0*(a-1023)/(255-1023));
  y = 255 * (a - 1023) / (255 - 1023);
  return y;
}
void loop() {
}

Non ti allego tutto il risultato, ma queste righe sono secondo me chiare:

a=379	a-1023=-644	255*(a-1023)=32388	255.0*(a-1023)=-164220.00	255*(a-1023)/(255-1023)=-42	255.0*(a-1023)/(255-1023)=213.83
a=380	a-1023=-643	255*(a-1023)=32643	255.0*(a-1023)=-163965.00	255*(a-1023)/(255-1023)=-42	255.0*(a-1023)/(255-1023)=213.50
a=381	a-1023=-642	255*(a-1023)=-32638	255.0*(a-1023)=-163710.00	255*(a-1023)/(255-1023)=42	255.0*(a-1023)/(255-1023)=213.16
a=382	a-1023=-641	255*(a-1023)=-32383	255.0*(a-1023)=-163455.00	255*(a-1023)/(255-1023)=42	255.0*(a-1023)/(255-1023)=212.83

in un caso il compilatore ha usato 16 bit ed è andato in overflow, nell’altro l’abbiamo “forzato” ad usare una precisione maggiore (lo vedi dal fatto che i numeri hanno due decimali, anche se entrambi a zero).

Ciao!
grazie mille, molto utile :slight_smile:
sìsì vedo, nella pratica è tutto molto chiaro. Per la parte teorica devo studiare un po’ di più. Non capisco ancora perché aggiungendo quella parte decimale riesca tutto, mentre usando un float (come ho provato a fare) vada ancora in overflow. In ogni caso da ora in poi userò questo metodo quando avrò a che fare con frazioni.
Noto anche che inizia ad andare in overflow già al passaggio [255.0*(a-1023)]. Quindi si può dire che più che il risultato finale (che non comporterebbe overflow dato che sarebbe compreso tra 0 e 255) contano più i passaggi intermedi? Cioè se in un passaggio qualsiasi di un calcolo vado in overflow allora sarà tutto falsato da questa cosa anche se il risultato finale non sarebbe in overflow (teoricamente)? Scusa magari è una domanda con risposta ovvia visto quanto detto finora.

Si, ovviamente il risultato finale dipende da tutti i passaggi intermedi!
In effetti in questo caso di overflow ce ne son ben più di uno, ed oltretutto tieni presente che le variabili "signed" soffrono di due tipi di OF. Se prendi un int e cominci ad "esagerare" prima ti diventarà negativo, e poi si riazzererà e ricomincerà da capo.
Il fatto che a te non abbia funzionato la soluzione di forzare float il risultato è anche ovvio: a quel punto il danno era fatto, ed un errore in int o float è sempre un errore :slight_smile:

La faccenda complicata è che se ti limiti ad usare le tue variabili allora sai di cosa si tratta: dichiari due int, li dividi e metti il risultato in un float? perfetto. ma se in una formula scrivi "255*(a-1023)" quale sarà il risultato? bho. dipende dal compilatore, ed in questo caso credo che si regoli cercando tra i tre termini il più "preciso". In questo caso un byte e due int, quindi il risultato sarà ancora int. Se viceserva noi scriviamo "255.0*(a-1023)" lui sceglierà tra un float e due int.

Tutto questo prendilo con le pinze. Non credo di aver detto castronerie troppo grosse, ma qualche imprecisione sicuramente ci sarà

P.S. poi non è detto che un OF sia necessiariamente male... se usi delle variabili unsigned è come fare un modulo, e talvolta può essere anche comodo, sebbene un po' criptico. Dal canto mio, soprattutto su Arduino, preferisco fare le cose più semplici possibile.

Ciao!

Nicks:
Volevo far in modo che per valori letti dal potenziometro compresi tra 0 e 255 questi venissero usati direttamente per dimmerare il led mentre per valori da 256 a 1023 vi fosse un dimmer decrescente.

Non capisco perché complicarsi la vita con funzioni, conversioni, variabili signed, casting… Basta fare così:

int value;
int green = 5; /*led da dimmerare*/
int test = 10; /*led di controllo*/
void setup() {
	Serial.begin(9600);
	pinMode(green, OUTPUT);
	pinMode(test, OUTPUT);
}

void loop() {
	value = analogRead(A0);
	Serial.println(value);
	if (value <= 255) {
		analogWrite(green, value); /*dimmero il led*/
		analogWrite(test, 0); /*tengo spensto il controllo*/
	}
	else
	{
		analogWrite(green, 1023-value);
		analogWrite(test, 255); /*accendo il controllo*/
	}
	delay(10);
}

O sbaglio?

docdoc:
Non capisco perché complicarsi la vita con funzioni, conversioni, variabili signed, casting... Basta fare così:

Guarda, anche io avrei fatto proprio così. Se il problema l'avessi avuto io...

docdoc:
O sbaglio?

Perdonami, ma penso di sì, almeno in parte.
Forse non hai seguito tutta la conversazione, ma Nicks aveva chiesto non una soluzione, ma una spiegazione del perché una funzione apparentemente corretta desse un risultato sbagliato. Io ho cercato di dargliela, secondo lo spirito del forum.
Poi un po' di ripasso ha fatto bene anche a me :slight_smile:

Ciao!

O sbaglio?

secondo me sbagli. almeno in parte

Io intendo la richiesta

Nicks:
Volevo far in modo che per valori letti dal potenziometro compresi tra 0 e 255 questi venissero usati direttamente per dimmerare il led mentre per valori da 256 a 1023 vi fosse un dimmer decrescente. Cioè in soldoni volevo che partendo da 0 con il potenziometro e girando il led aumentasse di intensità fino a 255 raggiungendo il massimo e poi, continuando a girare, diminuisse gradualmente fino a spegnersi per valore di potenziometro di 1023.

void loop() {
	value = analogRead(A0);
	Serial.println(value);
	if (value <= 255) {
		analogWrite(green, value); /*dimmero il led*/
		analogWrite(test, 0); /*tengo spensto il controllo*/
	}
	else
	{
		analogWrite(green, y=map(a,256,1023,255,0));
		analogWrite(test, 255); /*accendo il controllo*/
	}
	delay(10);
}

Il map() non funziona solo all interno dei valori di partenza indicati ma se la variabile é fuori da quel range comunque viene calcolato il risultato col rapporto indicato. Da questo possono derivare numeri negativi o maggiori al valore di destinazione.

Ciao Uwe

pippettiello:
Forse non hai seguito tutta la conversazione, ma Nicks aveva chiesto non una soluzione, ma una spiegazione del perché una funzione apparentemente corretta desse un risultato sbagliato. Io ho cercato di dargliela, secondo lo spirito del forum.

Hehe, si, lo so, e hai fatto benissimo sono comunque informazioni utili per capire meglio i meccanismi "nascosti" del casting che causano spesso (come in questo caso) apparenti incongruenze o errori.

Ma in pratica se c'è una soluzione immediata e molto più semplice è un pò come chiedere "come mai non riesco a cambiare una lampadina se uso i piedi col calzino?"... :smiley: O ti togli il calzino, ma è comunque difficile, o usi le mani! :wink:

docdoc:
... "come mai non riesco a cambiare una lampadina se uso i piedi col calzino?"...

... non capisco ... anche col calzino, il pollice opponibile dovrebbe funzionare lo stesso ... o no ?

(scusa, non ho resistito :D)