Wemos D1 Mini (8266) + Rotary Encoder

Bonjour à tous ,

Debutant en programmation j’ai pour projet de faire un bouton “connecté” afin de contrôler mon enceinte sonos.(play , pause , next , previous et volume)

Pour la partie logicielle j’utilise une API ( http sonos api ) installé sur un raspeberry Pi qui permet d’envoyer les commandes a mon enceinte sonos .

Pour la partie hardware , j’utilise un Wemos D1 Mini et un Rotary Encoder (Ky040) cablé comme indiqué :
D1 → Ky040 CLK
D2 → Ky040 DT
D3 → Ky040 SW

ce que j’attend du programme est plutôt simple :
si je clique sur le bouton , le. Wemos construit une requete et l’envoie a l’api qui controle le sonos
en fonction du nombre de clique l’action peut être play/pause , suivant , précédent.

pour cette partie la tout fonctionne même si mon code risque de bruler les yeux des puristes … ( je suis d’ailleurs ouvert a tous conseils me permettant d’amélirorer / optimiser le code )

le problème est surtout pour la partie volume , je n’arrive pas a faire fonctionner le rotary en effet j’ai l’impression que le changement d’etat n’est pas detecté , il y a quelque chose a de particulier a faire sur le ESP8266 ?

j’ai également essayé la lib RotaryEncoder de Matthias hertel ( juste l’exemple fournit avec le la lib ) et le wemos reboot constamment .

je vous joins le code , si quelqu’un a deja rencontré le problème je suis preneur d’information

si vous avez des conseils concernant le code en général je suis également intéressé

merci

#include <ArduinoJson.h>
#include <ESP8266HTTPClient.h>
#include <OneButton.h>
#include <ESP8266WiFi.h>

// pin rotary

#define outputA 1 // clk
#define outputB 2 // dt

/////////////////////////////// User Settings /////////////////////////////////////////

const char* ssid = "XXXXXXXX";
const char* password = "XXXXXXXX";

// Variable API Sonos
String SPEAKER_SONOS = "Salon";
String URL_API_SONOS = "http://192.168.0.13:5005";
String ACTION = "";

/////////////////////////////// Hardware Configuration ////////////////////////////////

//attach a button on pin D3 to the library
OneButton button(D3, true);

// initialisation httpclient
HTTPClient req;

///////////////////////////////variables rotary///////////////////////////////////////
int counter = 0;
int aState;
int aLastState;
int swState;

////////////////////////////////variables fin ///////////////////////////////////////

void setup()
{
 // init console
 Serial.begin(115200);
 delay(10);

 // link fonction bouton
 button.attachDoubleClick(next); // link the function to be called on a doubleclick event.
 button.attachClick(playpause); // link the function to be called on a singleclick event.
 button.attachLongPressStop(previous); // link the function to be called on a longpress event.

// rotary pin

 pinMode(outputA, INPUT);
 //read clk as input
 pinMode(outputB, INPUT);
 //read dt as input

 // init wifi
 Serial.print("Connecting to ");
 Serial.println(ssid);
 
 WiFi.mode(WIFI_STA);
 WiFi.begin(ssid, password);
 
 while (WiFi.status() != WL_CONNECTED)
 {
 delay(500);
 Serial.print(".");
 }

 Serial.println("");
 Serial.println("WiFi connected");
 Serial.print("IP address: ");
 Serial.println(WiFi.localIP());
}

void loop()
{
 button.tick();

 /////test rotary

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

 // counter cannot go above or below 100
 if (counter > 100) {
 counter = 100;
 };
 if (counter < 0) {
 counter = 0;
 };
 }
 int volLevel = counter;

 Serial.println(volLevel);
 Serial.println(swState);
 Serial.println(aState);
 Serial.println(aLastState);

 aLastState = aState;
}

void requeteApi(String ACTION){
 int httpcode;
 String requete = URL_API_SONOS + "/" + SPEAKER_SONOS + "/" + ACTION;

 // affichage de la requete dans la console
 Serial.println(requete);
 // creation de la requete
 req.begin(requete);
 // recuperation du code http de retour
 httpcode = req.GET();

 //Check the returning code
 if (httpcode > 0){
 String payload = req.getString();
 Serial.println(payload);
 if (payload != "{\"status\":\"success\"}"){
 Serial.println("Erreur pour joindre l'API");
 }
 //Close connection
 req.end();
 }
 else{
 Serial.println("HTTP SONOS API ne repond pas");
 }
}

void next(){
 Serial.println("double click");
 requeteApi("next");
}

void playpause(){
 Serial.println("simple click");
 requeteApi("playpause");
}

void previous(){
 Serial.println("long click");
 requeteApi("previous");
}

Bonjour

#define outputA 1 // clk
#define outputB 2 // dt

Sans en être certain il me semble que 1 désigne GIPO1 et non D1 (GPIO5)
mettre D1 et D2, comme cela a été fait pour le bouton poussoir
ici le repérage des GPIO avec leur désignation Dx : LOLIN D1 mini — WEMOS documentation

module KY-040 bien alimenté en 3,3V ?

par ailleurs nommer OUTPUT une entrée est surprenant pour le lecteur....c'est pour tromper l'ennemi ?

#define outputA 1 // clk
#define outputB 2 // dt

je ne sais pas quel Arduino vous avez mais souvent la pin 1 est utilisée pour le port série ou pas forcément D1

(le nom output est bizarre pour un truc que vous mettez en input, pourquoi ne pas les appeler clkPin et dtPin par exemple)

EDIT= @al1fch, on pense pareil :slight_smile:

j'ai regardé

LED_BUILTIN = blue LED
0 = D3
1 = TX
2 = blue LED
3 = RX
4 = D2
5 = D1
12 = D6
13 = D7
14 = D5
15 = D8
16 = D0

le double repérage des entrées/sorties est au début déconcertant mais puisque @mrz544 est utilisateur de Raspberry PI il a normalement déjà rencontré cette 'subtilité' !

Hello ,

merci d’avoir pris le temps de regarder mon post

@al1fc : vous avez entièrement raison , généralement je mets D1 D2 … pour etre certain mais la c’est passé a la trappe

je suis effectivement utilisateur de raspberry , le programme que j’essaye de porter sur l’arduino je l’ai deja réalisé en python . je voulais découvrir l’arduino et je me suis dit que refaire le même programme dans un autre langage en apportant une evolution ( le rotary encoder au lieu de 3 boutons ) serait l’occasion.

pour en revenir au code j’ai tout décâblé/ recâblé et refait un sketch ne contenant que la gestion du rotary pour le volume , désormais il fonctionne :slight_smile:

#define PinA D1 // clk
#define PinB D2 // dt

int counter = 0;
int aState;
int aLastState;
int swState;


void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  pinMode(PinA, INPUT);
  //read clk as input
  pinMode(PinB, INPUT);
  //read dt as input

}

void loop() {
  // put your main code here, to run repeatedly:

  aState = digitalRead(PinA);


  if (aState != aLastState)  {
    if (digitalRead(PinB) != aState)  {

      counter ++;
    } else {
      counter --;
    }

    // counter cannot go above or below 100
    if (counter > 100)  {
      counter = 100;
    };
    if (counter < 0)  {
      counter = 0;
    };
  }


  Serial.println(counter);
 // Serial.println("aState" + aState);
  //Serial.println("aLastState" + aLastState);


  aLastState = aState;

}

j’ai en resultat la variable “counter” qui s’affiche en boucle et qui change lorsque je tourne le bouton , c’est un bon debut

neanmoins je suis obligé de laisser ce bout de code dans la boucle pour que l’action sur le bouton soit détecté , ce qui signifie dans le cas de mon programme qui va envoyer une requête avec le volume en boucle également.

en python il y a une fonction qui permet de détecter un evenement sur un bouton

GPIO.add_event_detect(12, GPIO.FALLING, callback=next_button, bouncetime=300)

je suis en train chercher sur internet quelque chose similaire qui fonctionnerait sur le wemos , si vous avez une idée ?

merci

Bonsoir

content de voir que ça fonctionne !

évènement lors d'un appui sur un bouton ?
Oui : interruption externe sur GPIO
divers tutoriels dont celui-ci : ESP8266 Interrupts and Timers using Arduino IDE (NodeMCU) | Random Nerd Tutorials

c’est justement celui que j’ai trouvé :slight_smile:

pour ceux qui rencontre le même problème si joint un code qui fonctionne ( en tout cas pour l’interruption )

#define pinA D1 // clk
#define pinB D2 // dt

int counter = 0;
int aState;
int aLastState;
int swState;


void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  pinMode(pinA, INPUT);
  //read clk as input
  pinMode(pinB, INPUT);
  //read dt as input

}

void loop() {
  // put your main code here, to run repeatedly:
  attachInterrupt(digitalPinToInterrupt(pinA), volume, CHANGE);

}

ICACHE_RAM_ATTR void volume()
{
  aState = digitalRead(pinA);


  if (aState != aLastState)  {
    if (digitalRead(pinB) != aState)  {

      counter ++;
    } else {
      counter --;
    }

    // counter cannot go above or below 100
    if (counter > 100)  {
      counter = 100;
    };
    if (counter < 0)  {
      counter = 0;
    };
  }


  Serial.println(counter);
 // Serial.println("aState" + aState);
  //Serial.println("aLastState" + aLastState);


  aLastState = aState;
  
}

il reste du travail car les chiffres s’affiche plusieurs fois lors de la rotation d’un seul cran , surement une histoire de timer
je continue de chercher

merci pour l’aide en tout cas

Bonjour ,

j’ai bien avancé sur mon problème de rotary , j’arrive a incrémenter/décrémenter la valeur lorsque je tourne le bouton et à appeler une fonction avec en paramètre ma valeur

néanmoins j’ai un problème de requête http , en effet lorsque je tourne le bouton j’ai l’impression que j’envoie trop de requete en peu de temps et l’api n’a pas le temps de repondre et retourne une erreur

#include <Arduino.h>
#include <ESP8266HTTPClient.h>
#include <OneButton.h>
#include <ESP8266WiFi.h>


#define pinA D1 // clk
#define pinB D2 // dt

/////////////////////////////// User Settings /////////////////////////////////////////

const char* ssid = "XXXXX";
const char* password = "XXXXX";

// Variable API Sonos
String SPEAKER_SONOS = "Salon";
String URL_API_SONOS = "http://192.168.0.13:5005";
String URL_BASE = URL_API_SONOS + "/" + SPEAKER_SONOS;
String URL_PLAYPAUSE = URL_BASE + "/playpause";
String URL_PLAYMUSIC = URL_BASE + "/spotify/now";
String URL_NEXT = URL_BASE + "/next";
String URL_PREVIOUS = URL_BASE + "/previous";
String URL_VOLUME_UP = URL_BASE + "/volume/+";
String URL_VOLUME_DOWN = URL_BASE + "/volume/-";

/////////////////////////////// Hardware Configuration ////////////////////////////////

//attach a button on pin D3 to the library
OneButton button(D3, true);

// variable knob
int compteur = 0 ;
bool etatA ;
bool dernierEtatA ;
long unsigned tempsA ;

// initialisation httpclient
HTTPClient req;

/////////////////////////////// Fonction ////////////////////////////////

void requeteApi(String requete)
{
 int httpcode;
 // affichage de la requete dans la console
 Serial.println(requete);
 // creation de la requete
 req.begin(requete);
 // recuperation du code http de retour
 httpcode = req.GET();
delay(100);
 //Check the returning code
 if (httpcode > 0)
 {
 String payload = req.getString();
 Serial.println(payload);
 if (payload != "{\"status\":\"success\"}")
 {
 Serial.println("Erreur pour joindre l'API");
 }
 //Close connection
 req.end();
 }
 else
 {
 Serial.println("HTTP SONOS API ne repond pas");
 }
 
}

void SetvolumeUp(int vol)
{
 String testvolumeup = URL_VOLUME_UP + vol * 5;
 Serial.println(testvolumeup);
 requeteApi(testvolumeup);
}

void SetvolumeDown(int vol)
{
 String testvolumedown = URL_VOLUME_UP + vol * 5;
 requeteApi(testvolumedown);
}

ICACHE_RAM_ATTR void checkknob()
{
 // on mesure A
 etatA = digitalRead(pinA);

 // controle du temps pour eviter des erreurs
 if ( abs(millis() - tempsA) > 50 )
 {
 // Si B different de l'ancien état de A alors
 if (digitalRead(pinB) != dernierEtatA) {
 compteur--;
 SetvolumeDown(compteur);
 }
 else {
 compteur++;
 SetvolumeUp(compteur);
 }
 // memorisation du temps pour A
 tempsA = millis();
 }
 dernierEtatA = etatA ;
}

void next() {
 requeteApi(URL_NEXT);
}

void playpause() {
 requeteApi(URL_PLAYPAUSE);
}

void previous() {
 requeteApi(URL_PREVIOUS);
}

//////////////////////// MAIN //////////////////////////////////////////////////////

void setup() 
{

 //init console
 Serial.begin(115200);
 delay(10);

 // init wifi
 Serial.print("Connecting to ");
 Serial.println(ssid);

 WiFi.mode(WIFI_STA);
 WiFi.begin(ssid, password);

 while (WiFi.status() != WL_CONNECTED)
 {
 delay(500);
 Serial.print(".");
 }

 Serial.println("");
 Serial.println("WiFi connected");
 Serial.print("IP address: ");
 Serial.println(WiFi.localIP());

 // link fonction bouton
 button.attachDoubleClick(next); 
 button.attachClick(playpause); 
 button.attachLongPressStop(previous); 

 // knob 
 pinMode(pinA, INPUT);
 pinMode(pinB, INPUT);
}

void loop() {
 
 //button interrupt
 button.tick();
 //Knob interrupt
 attachInterrupt(digitalPinToInterrupt(pinA), checkknob, CHANGE);

}
WiFi connected
IP address: 192.168.0.21
http://192.168.0.13:5005/Salon/playpause
{"status":"success"}
http://192.168.0.13:5005/Salon/playpause
{"status":"success"}
http://192.168.0.13:5005/Salon/volume/+5
HTTP SONOS API ne repond pas
http://192.168.0.13:5005/Salon/volume/+5
http://192.168.0.13:5005/Salon/volume/+10
HTTP SONOS API ne repond pas
http://192.168.0.13:5005/Salon/volume/+10
http://192.168.0.13:5005/Salon/volume/+15
HTTP SONOS API ne repond pas

y a t-il quelque chose au niveau de la requete que je fais mal ? ou une meilleure facon de faire ?

je suis entre temps passé sur plateformIO que je trouve plus pratique pour developper

d’ailleurs j’ai eu un “warning” lors de la compilation concernant ma requete que je ne comprend pas bien ( je vais faire des recherches )

src/main.cpp: In function 'void requeteApi(String)':
src/main.cpp:48:20: warning: 'bool HTTPClient::begin(String)' is deprecated (declared at /Users/tom/.platformio/packages/framework-arduinoespressif8266/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.h:174) [-Wdeprecated-declarations]
   req.begin(requete);

Merci