Problema magari banale...ma non ci dormo la notte!

Ciao a tutti, mi chiamo Marco e sono nuovo del Forum.
Ho cominciato da poco a lavorare con Arduino e lo trovo fantastico! Tuttavia il C mi fa impazzire :)!

Vi espongo il (magari banale) problema.

Devo azionare un relè: nessun problema di collegamento, funzia tutto!

Il problema risulta essere nel comando!

Ho un ingresso di riferimento (una tensione esterna, variabile da 0 a 5V). Quando la mia tensione supera i 3V, ho bisogno che il rele intervenga (stato del pin, diciamo tredici, alto). Dopo ciò, ho bisogno che il mio relè intervenga ancora dopo che si superano i 4V (stato del pin, basso)...ma che se la tensione scende sotto i 4V, lo stato del pin rimanga basso!

Il problema è che in "salita" di tensione ho bisogno di 2 azioni, ed in "discesa" di tensione di una sola!
Come posso fare?

Grazie!

Ciao, non ho tanto capito la seconda parte del problema, ovvero supero i 3V quindi il relè si attiva, supero i 4 il relè si disattiva e resta disattivato anche se torno sotto i 4V ma fino s che tensione deve restare disattivato? Se scendo sotto i 3V poi si deve riattivare se li supera nuovamente o la tensione deve andare a zero? Posta il codice che hai scritto finora e se puoi argomenta meglio il funzionamento così si evita di scrivere codice che poi non si adatta al tuo problema.
In ogni caso ti serve una macchina a stati finiti ovvero con una variabile ti memorizzi lo stato attuale e poi in base a quello valuti se attivare o disattivare il relè, questo come spunto generale poi se metti il codice entriamo in dettaglio al tuo problema.

Ti ringrazio per la tempestiva risposta!

Si ho bisogno di:

U > 3V ----> Pin 1 (alto)
U > 4V ----> Pin 0 (basso)
U > 4,3V --->Pin 0 (basso) <- questo è per esempio
U < 4V ----> Pin 0 (basso)

e via così...

Cioè, in discesa di tensione i comandi dovrebbero dovrebbero variare. Ho pensato di mettere un secondo controllo in // al segnale A0. Quindi fare A0 e A1 (medesimo segnale, splittare le variabili e inserire controlli diversi sulle variabili).

Ecco il codice (molto rudimentale)

// the setup routine runs once when you press reset:
void setup() {
// initialize serial communication at 9600 bits per second:
Serial.begin(9600);
}

int var;
int sub;

void loop() {
// read the input on analog pin 0:
int sensorValue = analogRead(A0);
pinMode (13, OUTPUT);

// Convert the analog reading (which goes from 0 - 1023) to a voltage (0 - 5V):
double u = sensorValue * (5.0 / 1023.0);
double sub = sensorValue * (5.0 /1023.0);
// print out the value you read:

//ciclo if comando iniziale

if (u < 3.0) {

digitalWrite (13, HIGH);
}

else if (u > 4.5) {

digitalWrite (13, HIGH);
}

else {
digitalWrite (13, LOW);
}

Serial.println(u);

// ritardo in millisecondi

KKMeph:
Ciao a tutti, mi chiamo Marco e sono nuovo del Forum.

Ti invitiamo a presentarti (dicci quali conoscenze hai di elettronica e di programmazione) qui: Presentazioni
e a leggere il regolamento se non lo hai già fatto: Regolamento
Qui una serie di link utili, non inerenti al tuo problema:

Il codice devi racchiuderlo nei tag code, vedi sezione 7 del regolamento, spiega bene come fare ( pulsante </> ).
Altrimenti parte del codice può essere visualizzata male o mancare perchè interpretato come attributo del testo stesso.

Pardon, avevo già visto i link utili...mi era scappato il regolamento ed il codice!

Ecco qui, ho fatto un paio di modifiche seguendo il suggerimento datomi

// the setup routine runs once when you press reset:
void setup() {
  // initialize serial communication at 9600 bits per second:
  Serial.begin(9600);
}

int var; 
int sub;
int k = 0; 


void loop() {
  // read the input on analog pin 0:
  int sensorValue = analogRead(A0);
  int valore2 = analogRead(A1); 
  pinMode (13, OUTPUT);
  
  // Convert the analog reading (which goes from 0 - 1023) to a voltage (0 - 5V):
  double u = sensorValue * (5.0 / 1023.0);
  double sub = valore2 * (5.0 /1023.0);
  // print out the value you read:


if (k==0){

             if (u>3){
                          digitalWrite (13,HIGH);} 
             else {digitalWrite (13, LOW);}
                     

k=k+1;

}


if (k==1) {

            if (u>4.5){
                         digitalWrite (13, LOW);}
            else {digitalWrite (13, HIGH);}
                        
    
k=k+1;

}

if (k==2) {
            if (u <=4.5 && u > 3) {
                          digitalWrite (13,LOW);}

            else {digitalWrite (13, HIGH);}
             
k=0;
}




 





Serial.println(u);


// ritardo in millisecondi 

delay (1000); 

}

Quel che non si capisce è cosa deve succedere quando la tensione scende. Deve rimanere spento per sempre? Oppure c'è una soglia X una volta scesi sotto la quale, se si riprende a salire, si deve riaccendere il relé?

Scusami, cerco di spiegarmi meglio, andando per step

Ipotesi di ciclo 1

U < 3, Led OFF
U > 3, Led ON
3 < U < 4.5, LED ON
U>4.5, LED OFF

e solo dopo il superamento di questa soglia

3 < U < 4.5, LED OFF -- Comando che cozza con il comando di prima.

Dopo quando la tensione scende sotto i tre, ritorna tutto normale.

Domanda un pò stupida se vogliamo, ma @KKMeph a che ca...spita servono quelle montagne di righe vuote ??
Ci vuole un mese per leggere il codice !! Suggerisco di toglierle e piuttosto metti delle righe di commento per separare le varie "zone" del programma :slight_smile:
E nell'editor Arduino prova ad usare CTRL+T che rimette un pò a posto il codice come indentazione

// the setup routine runs once when you press reset:
void setup() {
  // initialize serial communication at 9600 bits per second:
  Serial.begin(9600);
}
//dichiarazione delle variabili
int sub;
int k = 0;
//parte di programma già predefinito in cui leggo l'ingresso, in 2 canali (al momento ne uso solo uno) E' sempre lo
//stesso segnale ma splittato su due ingressi
void loop() {
  // read the input on analog pin 0:
  int sensorValue = analogRead(A0);
  int valore2 = analogRead(A1);
  pinMode (13, OUTPUT);
  // Convert the analog reading (which goes from 0 - 1023) to a voltage (0 - 5V):
  double u = sensorValue * (5.0 / 1023.0);
  double sub = valore2 * (5.0 / 1023.0);
  //ecco la parte di programma
  if (k == 0) {
    if (u < 3) {
      digitalWrite (13, LOW);
    }
    else {
      digitalWrite (13, HIGH);
    }

    k = k + 1;
  }
  if (k == 1) {
    if (u > 4.5) {
      digitalWrite (13, LOW);
    }
    k = k + 1;
  }
  if (k == 2) {
    if (u <= 4.5 && u > 3.0) {
      digitalWrite (13, LOW);
    }
    k = 0;
  }
  Serial.println(u);
  // ritardo in millisecondi
  delay (2);

}

Allora più o meno dovrebbe diventare una cosa del genere (non posso provarlo però):

bool bSogliaMinimaRaggiunta = false;
bool bSogliaMassimaRaggiunta = false;
if(!bSogliaMinimaRaggiunta ){
  bSogliaMinimaRaggiunta = valore>3;
  if(bSogliaMinimaRaggiunta ){
    digitalWrite (13, HIGH);
    bSogliaMassimaRaggiunta  = false;
  }
}
else{
  bSogliaMassimaRaggiunta = bSogliaMassimaRaggiunta || valore > 4.5;
  if(bSogliaMassimaRaggiunta){
     digitalWrite (13, LOW);
  }
  bSogliaMinimaRaggiunta = valore>3;
}

Ho appena provato...si accende (ho anche un led che prende alimentazione dal pin 13, in modo da far prima e non basandomi solo sul clic del relè) ai 3V e poi sta sempre acceso :frowning:

prima di ricontrollare il codice, ti faccio una domanda, ma non è che questo controllo ti serve per gestire l'isteresi di una temperatura per evitare il continuo accendersi / spegnersi della caldaia o simile?

No, serve a gestire dei valori in tensione (opportunamente serviti ad Arduino mediante apposito partitore). Tali valori di tensioni sono dovuti (per dover di cronaca) ad una variazione di resistività di un materiale, dovuta (finalmente ci arrivo) ad una variazione di temperatura.

Perchè? Non riesco a darne fuori comunque :frowning:

Perchè? Non riesco a darne fuori comunque :frowning:

Io ho la febbre, quindi ...do un po i numeri.

Io mica l'ho capito, intanto noto incongruenze tra codice tuo e descrizione, cioè nel codice accendi quando nella descrizione dici di spegnere.

1 ) non usare float o double con le if > o <, e peggio ancora se ==
Puoi sempre usare i valori in virgola mobile per comodità, con tal fine puoi creare una macro ftoadc(v).
2 ) La macchina a stati la puoi fare con switch case, in fondo il led può essere acceso o spento e quando
è spento non serve ricontrollare se è spento e viceversa. In sostanza i due stati sono mutualmente esclusivi.
3 ) Non ti voglio male, ma capisci che ho la febbre, la bronchite e il naso chiuso e ciò mi rende capriccioso. 8)

Tiè una bella macchina a stati per cerebrolesi.

typedef void (*std_fnc_ptr)(void);
std_fnc_ptr mutexRun;

void setup() {
  // initialize serial communication at 9600 bits per second:
  Serial.begin(9600);
  mutexRun = ledIsOff; // prenota l'esecuzione di ledIsOff()
  
}

void ledIsOn() {
    if (analogValue > 4.5) {
        ; // spegni led
        mutexRun = ledIsOff;  // // prenota l'esecuzione di ledIsOff()
    }
}

void ledIsOff() {
    if ((analogValue > 3) && (analogValue < 4.5)) {
        ; // accendi led
        mutexRun = ledIsOn; // prenota l'esecuzione di ledIsOn()
    }
}

void loop() {
  mutexRun();  // esegue ledIsOff o ledIsOn 
}

Per evitare di usare i float come in if (analogValue > 4.5) { dovresti creare la macro con define, ma ora sono
a corto di pazienza. Il codice si trasformerebbe in

if ( analogValue > ftoadc(4.5) ) {

Ciao.

Ti ringrazio per la risposta e per i consigli dati (soprattutto sul tipo di variabile). Purtroppo del tuo codice ci ho capito veramente gran poco, essendo il mio C arrugginito da anni ormai.

Provo a rispiegare il tutto.

Abbiate presente una forma d'onda triangolare;

Nella fase di salita, ci sarà un punto che sarà a meta fra il punto zero e il punto MAX. Chiamiamolo punto A.
Nella fase di discesa, ci sarà analogamente un punto fra il punto MAX ed il punto zero (ovviamente traslato nel tempo). Chiamiamolo punto B.

Se sulle ascisse abbiamo il tempo, X(A) sarà ovviamente diverso da X(B). Se sulle ordinate abbiamo un valore di tensione, sarà Y(A)=Y(B).

La situazione che descrivo è la seguente, facendo un passo in più. Eliminiamo mezzo triangolo, teniamo solo la prima parte.

Punto zero : comando
Punto fra zero e mediano: comando 1
Punto Max: comando 2.

Ora se ipotizziamo che il mio valore in ordinata cali (da MAX a zero) si troverà prima o poi sul punto relativo al comando 1. E lo eseguirà.

Ecco, io non voglio che lo esegua. Lo deve eseguire solo in salita, in discesa non deve variare il suo stato (dallo stato raggiunto con il max).

Meglio ora?

Grazie!

Ecco, io non voglio che lo esegua. Lo deve eseguire solo in salita, in discesa non deve variare il suo stato (dallo stato raggiunto con il max).

Meglio ora?

Per stabilire se U sale o scende si devono prendere due campioni, U(t) e U(t+1), usando gli interi con segno
puoi stabilire se sta salendo o scendendo, perché:

Salendo sta:
u(t) = 0
u(t+1) = 1
1 - 0 = 1 (positivo)

Scendendo sta:
u(t) = 1
u(t+1) = 0
0 - 1 = -1 (negativo)

Serve proprio monitorare se sta salendo o scendendo?

Purtroppo del tuo codice ci ho capito veramente gran poco, essendo il mio C arrugginito da anni ormai.

Rinfresca allora i puntatori a funzione, typedef, per tutta curiosità se al mio codice sostituisci i commenti con il codice che accende il led e che legge A0, cosa esce fuori?

ops, a proposito ti serve leggere solo A0, A1 lo hai usato nel tentativo di venirne a capo, giusto?

Ciao.

Si, A1 avevo pensato di utilizzarlo per "splittare" il segnale e quindi dare un doppio controllo...

MauroTec:
1 ) non usare float o double con le if > o <, e peggio ancora se ==
Puoi sempre usare i valori in virgola mobile per comodità, con tal fine puoi creare una macro ftoadc(v).

cos'e' questa ftoadc() ??

Mi da errore al primo led in on

typedef void (*std_fnc_ptr)(void);
std_fnc_ptr mutexRun;

void setup() {
  // initialize serial communication at 9600 bits per second:
  Serial.begin(9600);
  pinMode (13, OUTPUT);


  mutexRun = ledIsOff; // prenota l'esecuzione di ledIsOff()
  void ledIsOn() {
    if (u > 4.5) {
      digitalWrite (13, LOW); // spegni led
      mutexRun = ledIsOff;  // // prenota l'esecuzione di ledIsOff()
    }
  }

  void ledIsOff() {
    if ((u > 3) && (u < 4.5)) {
      ; // accendi led
      mutexRun = ledIsOn; // prenota l'esecuzione di ledIsOn()
    }
  }

  void loop() {

    int sensorValue = analogRead(A0);

    // Convert the analog reading (which goes from 0 - 1023) to a voltage (0 - 5V):
    float u = sensorValue * (5.0 / 1023.0);
    mutexRun();  // esegue ledIsOff o ledIsOn

    Serial.println (u);


  }