Table des matières

Test avec un encoder/switch

La reconnaissance vocale n'étant pas fiable, j'ai cherché un autre moyen de piloter mon module.
Je me suis tourné vers un encodeur/switch.

Ma commande aliexpress

Je vous invite à regarder une page google pour en savoir plus sur les encodeurs.
Recherche google

En gros on récupère un signal de cette forme aux bornes de codeur.
Suivant le décalage De A ou B on connais le sens et la vitesse.

Afin de voir dans un menu le résultat de la rotation du codeur, j'ai ajouter un écran LCD 2×16 en I2C.
Et pour le fun, une led RGB !
Je me suis créer une petit maquette avec le fruit de mes réflexions sur l'émission IR et le capteur de lumière.

Schémas

Sous eagle comme d'hab.

Programmation

Comme je voulais laisser la place pour un émetteur/récepteur RF433MHz et un récepteur IF.
J'ai été obligé de laisser les pattes : 2,3,10 et 11 disponibles.
J'avais donc un problème pour le codeur et le switch afin de récupérer une action rapidement.
J'ai découvert que quasi toutes les pattes de l'arduino pouvait générer une interruption, du coup plus de soucis.
J'ai commencé avec la librairie attachinterrupt :
http://www.arduino.cc/en/Reference/AttachInterrupt
Puis utilisé EnableInterrupt qui est plus récent et mis à jour :
https://github.com/GreyGnome/EnableInterrupt

Quelques mots sur le code

Tout n'est pas de moi !
J'ai pris des exemples à droite et gauche puis assemblé le tout.
Les sources sont cités au début.
Le menu n'a que 5 choix, mais on peu en ajouter plus et même créer des sous menus.
Il n'y a pas la gestion RF et récepteur IF comprise. Pour l'instant je suis sur une maquette.

/* Logiciel des fonctions de la platine
 * exemple lcd de la lib liquidcrystal_i2c
 * encodeur sur http://bildr.org/2012/08/rotary-encoder-arduino/
 *   Encodeur utilise EnableInterrupt
 * DHT11 http://total-informatique.com/utiliser-le-dht11-avec-un-ecran-lcd/
 * RGB http://www.mbeckler.org/microcontrollers/rgb_led/
 * Menu https://skyduino.wordpress.com/2014/07/06/arduino-lcd-faire-un-menu-sous-forme-de-liste/
 * 
 * Synoptique :
 * On affiche un message de bienvenue
 * Affiche température et humidité
 * Si bouton tourne ou push
 *   Affiche menu
 *   Gestion menu
 */
 
#include <avr/pgmspace.h>  // library for keeping variables in Flash RAM
#include "Wire.h" 
#include "LiquidCrystal_I2C.h"
#include "DHT.h" 			// Librairie pour le capteur DHT
#include <IRremote.h>
#include <EnableInterrupt.h>
 
// ### Menu ###
prog_char const mainMenu1[] PROGMEM = "1.Television ";
prog_char const mainMenu2[] PROGMEM = "2.XBMC       ";
prog_char const mainMenu3[] PROGMEM = "3.Lum centre ";
prog_char const mainMenu4[] PROGMEM = "4.Lum bandeau";
prog_char const mainMenu5[] PROGMEM = "5.Luminosite";         
const char* const mainMenu[] PROGMEM = {
  mainMenu1, mainMenu2, mainMenu3, mainMenu4, mainMenu5};
 
// Data télécommande
#define cmd_code    0xffffffff        // remote code pour transmettre. 
#define	Tv_onoff 		0x20df10ef        // Codes des télécommandes
#define	Tv_volP  		0x20df40bf
#define	Tv_volM 	 	0x20dfc03f
#define	Ampli_on  	0x7e817e81
#define	Ampli_off 	0x7e81fe01
#define	Ampli3    	0x5eA1609e
#define	Ampli2    	0x5eA1c03e
#define	Ampli_volP  0x5eA158a7
#define	Ampli_volM  0x5eA1d827  		// Codes des télécommandes
 
// ### Definition DHT ###
#define DHTPIN 6 //Pin auquel est connecté le capteur DHT
#define DHTTYPE DHT11 //Si vous utiliser le DHT 11
//#define DHTTYPE DHT22 //Si vous utiliser le DHT 22 (AM2302)
//#define DHTTYPE DHT21 //Si vous utiliser le DHT 21 (AM2301)
 
// ### Definition RGB ###
#define slen 7       // 7 characters, e.g. '#ff6666'
 
/* Définition IR */
#define REMOTE_BIT          3532
#define tempsIR             100        // Temps entre 2 commandes IR
 
// ### Definition senseur lumière ###
#define LUM_SENSOR_PIN  	A0 		// lumière sensor connected to this analog pin
 
// Var pour DHT
float fltHumidity; 		//Pourcentage d'humidité mesuré
float fltTemperature; //Température mesurée en Celsius
float hum;						// ancienne val humidité
float temp;						// ancienne val température
 
// Var pour RGB
int redPin   = 10;   // Red LED,   connected to digital pin 9
int greenPin = 9;    // Green LED, connected to digital pin 10
int bluePin  = 12;   // Blue LED,  connected to digital pin 11
 
// ### Definition Encoder ###
//these pins can not be changed 2/3 are special pins
#define encoderPin1 7
#define encoderPin2 8
#define encoderSwitchPin 4 //push button switch
 
volatile int lastEncoded = 0;
volatile long encoderValue = 0;
 
//long lastencoderValue = 0;
long ancienencoderValue = 0;
 
// ==== Variables Générales ====
const int intTimePause = 5000; 	//Par défaut on actualise les valeures toutes les 5 secondes
long position  = -999; 
char serInStr[slen];  			// array to hold the incoming serial string bytes
 
boolean TV = false;
boolean sortm = false;
unsigned long TimeOut;
char buffer[16];    // make sure this is large enough for the largest string it must hold
//char prompt = "Choisissez !";
 
//IR LED must be connected to Arduino PWM pin 3.
IRsend irsend; 					// innitialise l' IR object
 
LiquidCrystal_I2C lcd(0x27,16,2);  // set the LCD address to 0x27 for a 16 chars and 2 line display
DHT dht(DHTPIN, DHTTYPE); //On initialise le capteur DHT
 
boolean bp=false;
int r, v, b;
 
void BPtest() {
  bp=true;		// Renvoie bp vrai si bouton pressé
}
 
void updateEncoder() {
  int MSB = digitalRead(encoderPin1); //MSB = most significant bit
  int LSB = digitalRead(encoderPin2); //LSB = least significant bit
 
  int encoded = (MSB << 1) |LSB; //converting the 2 pin value to single number
  int sum  = (lastEncoded << 2) | encoded; //adding it to the previous encoded value
 
  if(sum == 0b1101 || sum == 0b0100 || sum == 0b0010 || sum == 0b1011) encoderValue ++;
  if(sum == 0b1110 || sum == 0b0111 || sum == 0b0001 || sum == 0b1000) encoderValue --;
 
  lastEncoded = encoded; //store this value for next time
	// Renvoie la valeur encoderValue contenant la position.
}
 
// Attach the interrupt in setup()
void setup() {
 
  Serial.begin(115200);
  lcd.init();                      	// initialize the lcd 
 
  pinMode(encoderPin1, INPUT_PULLUP);  	// Défini les pattes de l'encodeur
  pinMode(encoderPin2, INPUT_PULLUP);
  pinMode(encoderSwitchPin, INPUT_PULLUP);
 
  enableInterrupt(encoderPin1, updateEncoder, CHANGE);
  enableInterrupt(encoderPin2, updateEncoder, CHANGE);
  enableInterrupt(encoderSwitchPin, BPtest, CHANGE);
 
  pinMode(redPin,   OUTPUT);   		// sets the pins LED RGB as output
  pinMode(greenPin, OUTPUT);   
  pinMode(bluePin,  OUTPUT);
 
  ancienencoderValue=encoderValue=0;
  bp=false;
 
  // Message de bienvenue sur le LCD.
  lcd.backlight();
  lcd.print("Salut le monde!");
  lcd.setCursor(0, 1);
  lcd.print("Tourne le bouton");
  delay(1500);
  lcd.clear();
 
}
 
// Affiche une couleur de led
void led_rgb(int teinte) {
  HSVtoRGB(&r, &v, &b, teinte, 255, 255);
  analogWrite(redPin, r );
  analogWrite(greenPin, v );
  analogWrite(bluePin, b );
} //Fin du void
 
 
void loop() {
 
  //Affiche la temp/humidité si pas menu
  tmphum();				// Affiche temp et humidité
 
	//Test, gestion menu
  if (ancienencoderValue != encoderValue) {		// Si le bouton tourne
  Serial.println("tourne");
    affmenu3();
  } 
 
  // Affiche une led rouge prete
  led_rgb(0);
 
} //FIN DE LOOP
 
 
// *** Affiche le choix du menu suivant la position de l'encoder
//     Change aussi la couleur de la led ***
void affmenu3() {
  Serial.print("Entree dans le choix"); //Serial.println(bp);
  /* Variable pour le menu */
  int nbItems = 5;				// Défini le nb d'irem dans le menu
  byte selectedMenuItem = 1;   // Choix sélectionné
  byte shouldExitMenu = false; // Devient true quand l'utilisateur veut quitter le menu
  unsigned long TempMax;
  boolean boucle = false;      // Test de boucle pour aff
  long color;					// Stock la couleur
  int ValC[ ]= {40, 80, 120, 160, 200};
 
  // Affiche une premiere valeur
  lcd.clear();
  lcd.print("Choisissez !");
  strcpy_P(buffer, (char*)pgm_read_word(&(mainMenu[selectedMenuItem-1])));
  lcd.setCursor(0, 1);
  lcd.print(buffer);
  TempMax=millis();
 
  while ((!bp) && (millis() < TempMax+20000)) {	// tant que bp off et tempsMAx <
    /* change la led rgd */
    led_rgb(ValC[selectedMenuItem+1]);
 
    /* Gére le sens du codeur */
    long encoder = encoderValue - ancienencoderValue;
    Serial.println(selectedMenuItem);
    //Serial.print("Encoder :"); Serial.println(encoder); 
    if (encoder != 0) {
 
      if (encoder > 0) {		// on inc
        /* S'il existe un choix précédent */
        if(selectedMenuItem > 1) { 
          selectedMenuItem--;
        } 
      } else { 				// on dec
        /* S'il existe un choix suivant */
        if(selectedMenuItem < nbItems) {
 
          /* Passe au choix suivant */
          selectedMenuItem++;
        } else {
          selectedMenuItem=nbItems;
        }
      }
 
    // Affiche le nouveau menu
      lcd.setCursor(0, 1);
      lcd.print("                ");	// Aff ligne vide
      strcpy_P(buffer, (char*)pgm_read_word(&(mainMenu[selectedMenuItem-1])));
      lcd.setCursor(0, 1);
      lcd.print(buffer);
 
    Serial.print("Menu :");Serial.println(selectedMenuItem);  
    }
 
    ancienencoderValue=encoderValue;
 
  }
  /* Bouton appuyé */
  //Serial.print("bp appuyé :"); Serial.println(bp);
  if(bp) {
		displayChoice(selectedMenuItem);
  }
 
  bp=false; 
  led_rgb(0);
 
}	// Fin du void
 
 
void tmphum() {    // Lit le capteur et affiche les résultats.
 
		fltHumidity = dht.readHumidity(); 								//On lit le pourcentage d'humidité
		fltTemperature = dht.readTemperature(); 					//On lit la température en degrés Celsuis
		if (isnan(fltTemperature) || isnan(fltHumidity))	//Si les valeures retournées ne sont pas des nombres :
		{
			lcd.setCursor(0, 1); 														//Positionnement du curseur
			lcd.print(DHTTYPE); 														//On affiche le type de capteur
			lcd.setCursor(5, 1);
			lcd.print(" illisible"); 												//On affiche l'erreur
		}
		else
		{
			if ((hum != fltHumidity) || (temp != fltTemperature)) lcd.clear();
		//Serial.print("Lecture temp/hum"); Serial.println(fltTemperature);
 
			//mise en forme et affichage des informations sur l'écran LCD
			//lcd.setdelay(intTimePause); //On actualise les informations toutes les x millisecondes.Cursor(0, 0); //Positionnement du curseur
			if (hum != fltHumidity) { 
				lcd.setCursor(0, 0);
				lcd.print("                ");	// Aff ligne videlcd.print(
			}
                        lcd.setCursor(0, 0);
			lcd.print("Degres : ");
			lcd.setCursor(9, 0);
			lcd.print(fltTemperature); //Affichage de la température
			lcd.setCursor(13, 0);
			lcd.print((char)223); //Affiche le caractère ° (degrés)
			lcd.setCursor(14, 0);
			lcd.print("C"); //En degrés Celsuis
			if (temp != fltTemperature) { 
				lcd.setCursor(0, 1);
				lcd.print("                ");	// Aff ligne videlcd.print(
			}
			lcd.setCursor(0, 1);
			lcd.print("Humidite : ");
			lcd.setCursor(11, 1);
			lcd.print(fltHumidity); //Affichage de l'humidité
			lcd.setCursor(15, 1);
			lcd.print("%");
		}
 
		//delay(intTimePause); //On actualise les informations toutes les x millisecondes.
 
	temp = fltTemperature;
	hum = fltHumidity;
}
 
 
/** Affiche le choix de l'utilisateur */
void displayChoice(byte selectedMenuItem) {
 
  bp=false;
  /* Affiche le choix de l'utilisateur */
  lcd.clear();
  lcd.print(F("Z'avez choisi :"));
  lcd.setCursor(0, 1);
  strcpy_P(buffer, (char*)pgm_read_word(&(mainMenu[selectedMenuItem])));
  lcd.print(buffer);
  delay(500);
 
  /* Selection de l'ordre */
  switch(selectedMenuItem) {
  case 0 :     //television
    lcd.clear();
    lcd.print(F("Television"));
    if (!TV) {                 // Tv pas allumé
    Serial.write("ordre tv on");Serial.println(selectedMenuItem);
		irsend.sendNEC(Ampli3, REMOTE_BIT);
//		irsend.sendNEC(cmd_code, REMOTE_BIT);
//		delay(tempsIR);
		irsend.sendNEC(0x20df10ef, 3532);
//		irsend.sendNEC(cmd_code, REMOTE_BIT);
//		delay(tempsIR);
        TV=true;
    } 
    else {
    Serial.write("ordre tv off");
		irsend.sendNEC(Ampli_off, REMOTE_BIT);
		irsend.sendNEC(cmd_code, REMOTE_BIT);
		delay(tempsIR);
		irsend.sendNEC(Tv_onoff, REMOTE_BIT);
		irsend.sendNEC(cmd_code, REMOTE_BIT);
		delay(tempsIR);
          TV=false;
	}
    break;
 
  case 1 :     //XBMC
    lcd.clear();
    lcd.print(F("XBMC"));
    if (!TV) {                 // xbmc pas allumé
     Serial.write("ordre xbmc on");
		irsend.sendNEC(Ampli2, REMOTE_BIT);
		irsend.sendNEC(cmd_code, REMOTE_BIT);
		delay(tempsIR);
		irsend.sendNEC(Tv_onoff, REMOTE_BIT);
		irsend.sendNEC(cmd_code, REMOTE_BIT);
		delay(tempsIR);
        TV=true;
    } 
    else {
      Serial.write("ordre xbmc off");
		irsend.sendNEC(Ampli_off, REMOTE_BIT);
		irsend.sendNEC(cmd_code, REMOTE_BIT);
		delay(tempsIR);
		irsend.sendNEC(Tv_onoff, REMOTE_BIT);
		irsend.sendNEC(cmd_code, REMOTE_BIT);
		delay(tempsIR);
        TV=false;
    }
    break;
  case 2 :     //Lumière table
    lcd.clear();
    lcd.print(F("Lum Table"));
    break;
  case 3 :     //Lumière bandeau
    lcd.clear();
    lcd.print(F("Lum bandeau"));
    break;
  case 4 :     //Luminosité
      lumin();
    break;
    delay(2000);
  }
  bp=false;
  Serial.println("Fin d'ordre");
 
} //Fin displayChoice  
 
 
void lumin()  { //Affiche la luminosité meusurée
 long tempsMax;
 lcd.clear();
 lcd.print(F("Luminosite")); 
 //Serial.print("bp > :"); Serial.println(bp);
 //delay(50);
 while (!bp) { 
   tempsMax = millis();   
//Serial.print(digitalRead(encoderSwitchPin)); Serial.print(encoderValue); Serial.println(ancienencoderValue);  
   lcd.setCursor(0, 1);
   lcd.print("                ");			// Aff ligne videlcd.print( 
   lcd.setCursor(0, 1);       				//Positionnement du curseur
   lcd.print(analogRead(LUM_SENSOR_PIN));
   //delay(200);
   while (millis() < tempsMax+500) {
   }
 }
 bp=false;
}    //Fin lumin
 
// *** Convertie une lumiére TSV (teinte, saturation, valeur) [HSVen anglais] en RGB (hackable N°6)
void HSVtoRGB(int  *r, int *g, int *b, int h, int s, int v) {
  int c;
  long l, m, n;
 
  // saturation zéro, pas de couleur
  if (s == 0) {
    *r = *g = *b = v;
    return;
  }
 
  // chroma
  c = ((h % 60) * 255) / 60;
  h /= 60;
 
  // intermédiaire
  l = (v * (256 - s)) / 256;
  m = (v * (256 - (s * c) / 256)) / 256;
  n = (v * (256 - (s * (256 - c)) / 256)) / 256;
 
  // choix dominante
  switch (h) {
    case 0:
      *r = v; *g = n; *b = l;
      break;
    case 1:
      *r = m; *g = v; *b = l;
      break;
    case 2:
      *r = l; *g = v; *b = n;
      break;
    case 3:
      *r = l; *g = m; *b = v;
      break;
    case 4:
      *r = n; *g = l; *b = v;
      break;
    default:
      *r = v; *g = l; *b = m;
      break;
  }
}  // Fin HSVtoRGB

Quelques mots sur la finalisation du projet

J'ai pensé mettre ce module dans un cylindre avec 1 bague tournante et l'afficheur en haut derrière un plexi fume.
Les diodes IF seraient à la base tournées dans tout les sens.
On peu prévoir un alim par induction sur un socle.
Le codeur demandera un peu de mécanique pour que la bague l’entraîne mais avec le possibilité d'appuyer pour déclencher le switch.

BILAN

Un encodeur c'est pas mal à condition d'utiliser les interruptions.
Sinon on loupe des crans, mais malgré les int., on ne peu pas tourner trop vite.
Sous mon code j'ai des pas de 4 à chaque cran, construction du codeur ? Erreur de prog ?
Je ne sais pas, mais il faut y penser dans le code.