Rotary Encoder non risponde come dovrebbe. Valori sballati !!

Ciao a tutti … :slight_smile:
Non sono esperto, sto cercando di creare uno script facendo un po’ di copia/incolla qua e la.

In sostanza ho un LCD dove vengono indicate temperatura e umidità (ad un intervallo regolato con “millis”) e un “Rotary Encoder” dovrebbe permettermi di scegliere un valore.

Premetto che il Rotary Encoder funziona abbastanza bene, dato che con lo sketch di esempio le letture vengono fuori più o meno bene.

Il problema nasce quando sposto tutto nel mio sketch, le letture diventano imprecise, i valori aumentano e diminuiscono come pare a loro.

Ho lasciato tutto invariato rispetto agli esempi, pin, nome variabili e tutto quanto. Credo ci sia qualche conflitto con le misurazioni, o con i MILLIS.

Dei MILLIS ho bisogno, perché altrimenti le letture di umidità e temperatura vengono sballate e delay mi bloccherebbe tutto.

Insomma quando giro il ROTARY a destra talvolta non mi varia nulla, altre va avanti di uno e indietro di due!

Sapreste darmi un aiuto così ad occhio e croce?

Grazie mille!!!

P.S. Non fate caso al codice commentato … erano prove, ma forse vi saranno utili per capire quali tentativi (errati) ho fatto!

EDIT
Ho appena scoperto che, girando il Rotary molto, molto lentamente, le letture sono buone. Devo in sostanza accompagnarlo tra uno “scatto” e l’altro. In questo modo, i valori variano in maniera corretta!

//INIZIO PREPARAZIONE DEL DISPLAY

#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define outputA 6
#define outputB 7
#define OLED_RESET 4
Adafruit_SSD1306 display(OLED_RESET);
#define DHT11PIN 2
#define NUMFLAKES 10
#define XPOS 0
#define YPOS 1
#define DELTAY 2
#define LOGO16_GLCD_HEIGHT 16 
#define LOGO16_GLCD_WIDTH  16 

// FINE PREPARAZIONE DEL DISPLAY

// INIZIO PREPARAZIONE DEL SENSORE 

#include <dht11.h>
dht11 DHT11;
#define DHT11PIN 2

float umidita = 0;
float centigradi = 0;

// FINE PREPARAZIONE DEL SENSORE

// INIZIO IMPOSTAZIONE TIMER INTERVALLO RILEVAZIONI

unsigned long previousMillis = 0; 
const long interval = 6000;

// FINE IMPOSTAZIONE TIMER INTERVALLO RILEVAZIONI

// INIZIO IMPOSTAZIONE ROTARY

 #define outputA 6
 #define outputB 7
 int counter = 0; 
 int aState;
 int aLastState; 

// FINE IMPOSTAZIONE ROTARY

void setup() {


  
  Serial.begin(9600); //avviamo la comunicazione seriale 
  
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);  // accendimo lo schermo
  display.clearDisplay();  // cancelliamo lo schermo parte 1
  display.display(); // cancelliamo lo schermo parte 2
  delay (1500);
  
  pinMode (outputA,INPUT); // definiamo la prima rotazione del pomello
  pinMode (outputB,INPUT); // definiamo la seconda rotazione del pomello
  aLastState = digitalRead(outputA);   


}

void loop() {

  unsigned long currentMillis = millis(); //impostazioni timer intervallo




// INIZIO ROTARY

aState = digitalRead(outputA); // Reads the "current" state of the outputA
   // If the previous and the current state of the outputA are different, that means a Pulse has occured
   if (aState != aLastState){     
     // If the outputB state is different to the outputA state, that means the encoder is rotating clockwise
     if (digitalRead(outputB) != aState) { 
       counter ++;
     } else {
       counter --;
     }

     Serial.print("Position: ");
     Serial.println(counter);

   } 
   aLastState = aState; // Updates the previous state of the outputA with the current state

   // FINE ROTARY

 if (currentMillis - previousMillis >= interval) {
    previousMillis = currentMillis;
  

// INIZIO RILEVAMENTO UMIDITÀ E TEMPERATURA



int chk = DHT11.read(DHT11PIN);

  Serial.print("Lettura sensore: ");
  switch (chk)
  {
    case DHTLIB_OK:
      Serial.println("OK");
      break;
    case DHTLIB_ERROR_CHECKSUM:
      Serial.println("Checksum error");
      break;
    case DHTLIB_ERROR_TIMEOUT:
      Serial.println("Time out error");
      break;
    default:
      Serial.println("Unknown error");
      break;
  }
  }
 // Serial.print("Umidita' (%): ");
 // Serial.print((float)DHT11.humidity);
  umidita = DHT11.humidity; // valore umidità assegnato a questa variabile

 // Serial.print("Temperatura (C): ");
 // Serial.println((float)DHT11.temperature, 2);
//  delay(2000);

  // FINE RILEVAMENTO UMIDITÀ E TEMPERATURA


  display.setTextSize(1);
  display.setTextColor(WHITE);
  display.setCursor(0,0);
  display.clearDisplay();
  display.print("Umidita': % ");
  display.println(umidita);
  
  display.setCursor(0,10);
  display.print("Temperatura: ");
  display.println((float)DHT11.temperature, 2);
  display.setCursor(0,20);
  display.print("Position: ");
  display.println(counter);
  display.display();
 

}

Funesto: Premetto che il Rotary Encoder funziona abbastanza bene, dato che con lo sketch di esempio le letture vengono fuori più o meno bene.

Ciao, quando scrivi che le letture "vengono fuori più o meno bene" intendi dire che non va nemmeno con l'esempio?... già questo mi fa pensare a qualche errore nella configurazione hardware, controlla bene i segnali.

Ti consiglio comunque di utilizzare la libreria apposita per la lettura degli encoder, non puoi leggere i segnali nel loop, la lettura avviene con intervalli troppo lunghi e quindi non garantisce la lettura di tutti i cambiamenti di stato dei segnali A e B, per questo ogni tanto va avanti e spesso fa "a caso"

Dino

Mi viene un dubbio quando dici che ruotando molto lentamente funziona ... non e' che le funzioni di lettura del DHT e di pilotaggio del display richiedono troppo tempo, rispetto alla differenza di tempo fra i due impulsi dell'encoder, per cui se giri troppo velocemente se li perde ?

In quel caso sarebbe meglio usare almeno un'interrupt (uno solo per un'encoder ruotato a mano e' piu che sufficente) attivato sul "rising" (o falling, dipende da come l'encoder e' cablato elettricamente) di uno dei due pin ... all'interno della ISR controlli lo stato dell'altro pin, se e' alto stai girando in un senso, se e' basso stai girando nell'altro, sempre ...

Grazie per le risposte! :-) Si, con lo sketch di prova funziona bene, e naturalmente i risultati li scrive solo sul monitor seriale.

Etemenanki: Mi viene un dubbio quando dici che ruotando molto lentamente funziona ... non e' che le funzioni di lettura del DHT e di pilotaggio del display richiedono troppo tempo, rispetto alla differenza di tempo fra i due impulsi dell'encoder, per cui se giri troppo velocemente se li perde ?

In quel caso sarebbe meglio usare almeno un'interrupt (uno solo per un'encoder ruotato a mano e' piu che sufficente) attivato sul "rising" (o falling, dipende da come l'encoder e' cablato elettricamente) di uno dei due pin ... all'interno della ISR controlli lo stato dell'altro pin, se e' alto stai girando in un senso, se e' basso stai girando nell'altro, sempre ...

Questo mi sembra plausibile ... ma ripeto dio davvero davvero sono a zero! Ad esempio, se giro uno scatto ogni 5 secondi succede questo: Il display parte dal valore zero. Ruoto e passa moooolto velocemente da 1 e torna a zero.

Se invece ruoto lentamente, accompagnando tra uno scatto e l'altro allora legge bene, anche se sono continuo e costante nel ruotare.

Purtroppo io non so davvero cosa sia e come si inserisca un interrupt!. Diciamo che per il grezzo lavoro al quale sarebbe destinato (dovrebbe ad un determinato valore di umidità far scattare un relè) non sarebbe un grosso problema ruotare lentamente .... ma vorrei anche capire come mai!

Sapete, quando uno si intestardisce! :D

Grazie!!!!

Prova a guardare se QUESTA libreria può fare al caso tuo semplificandoti la vita ... ::)

Guglielmo

I Serial.print e le scritture sul display rallentano il loop. Scrivi solo una volta ogni secondo o due, con qualcosa come:

//t è unsigned long

if(millis()-t>999) // Una volta al secondo:
  {
  t=millis();
  Serial.print ... ... ...
  display.print ... ... ... 
  }

Per leggere l’encoder, ormai faccio sempre così:

void encoder()
{
//S=3-(PIND>>3)&B00000011; Serviva per l'encoder su PD3 e PD4, ma ora è su PD0 e PD1.

//     PD 76543210
S=3-PIND&B00000011; // Gli I/O 0 e 1 sono PD0 e PD1, perciò non devo scorrere a destra. 
// Il valore binario di S rappresenta A e B. Il centrale dell'encoder è a massa, quindi faccio complemento a 3 (11)  
S^=S>>1; // Faccio XOR (^) fra S (gray) e il suo bit 1, facendolo scorrere a Dx: AB XOR A, ottenendo un binario che per ogni scatto fa 0-1-2-3-0 oppure 0-3-2-1-0.
if (S!=So && S==0) X=0;
if (X==0)  // X evita letture multiple, cambiando stato subito dopo la prima lettura dell'encoder. Viene azzerato solo quando S torna a zero.
  {
  if (So==1&&S==2)
    {E=1; X=1; Bip();}
  if (So==3&&S==2)
    {E=-1; X=1; Bip();}
  if (S==0)
    {E=0; X=0;}
  So=S;  
  }
}

void Bip() {if(biptic==1||biptic==3)tone(7,1000,15);}

loop()
{
... ... ...
encoder();
if(E!=0){valore+=E; E=0;}
if(valore>valMax) valore=valMax;
if(valore<1) valore=1;
  
lcd.setCursor(0,0);
lcd.print(valore);
}

E restituisce 1 se l’encoder è stato ruotato in senso orario o -1 se è stato ruotato in senso antiorario; se non è stato ruotato, restituisce 0.