I thought I share this although it is still a bit rough on the edges.
I am using a KY-040 rotary encoder wired to a nodeMCU to have a physical dimmer.
The idea is to be able to turn the knob and my lights will be dimming accordingly.
I wired the KY-040 like this:
CLK -> D1
DT -> D2
SW -> D3
+ -> 3.3V
GND -> GND
This is the Arduino Sketch for the NodeMCU:
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
// Update these with values suitable for your network.
const char* ssid = "SSID";
const char* password = "PASSWORD";
const char* mqtt_server = "BROKER_IP";
const char* topicEncoder = "dial/encoder";
const char* topicButton = "dial/button";
char charPos [5];
#define pinSW D3
#define pinA D1 // Connected to CLK on KY-040
#define pinB D2 // Connected to DT on KY-040
int encoderPosCount = 0;
int pinALast;
int aVal;
int Button;
int aButton;
boolean bCW;
String strTopic;
String strPayload;
WiFiClient espClient;
PubSubClient client(espClient);
void setup_wifi() {
delay(10);
// We start by connecting to a WiFi network
Serial.println();
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED)
{
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
}
void callback(char* topic, byte* payload, unsigned int length) {
payload[length] = '\0';
strTopic = String((char*)topic);
if (strTopic == "dial/SetValue")
{
Serial.println("received");
encoderPosCount = atoi((char*)payload);
Serial.println (encoderPosCount);
}
}
void reconnect() {
// Loop until we're reconnected
while (!client.connected()) {
Serial.print("Attempting MQTT connection...");
// Attempt to connect
if (client.connect("arduinoClient")) {
Serial.println("connected");
// Once connected, publish an announcement...
client.subscribe("dial/#");
} else {
Serial.print("failed, rc=");
Serial.print(client.state());
Serial.println(" try again in 5 seconds");
// Wait 5 seconds before retrying
delay(5000);
}
}
}
void setup() {
pinMode (pinA,INPUT);
pinMode (pinB,INPUT);
pinMode (pinSW, INPUT);
/* Read Pin A
Whatever state it's in will reflect the last position
*/
pinALast = digitalRead(pinA);
Serial.begin (115200);
setup_wifi();
client.setServer(mqtt_server, 1883);
client.setCallback(callback);
}
void loop() {
if (!client.connected()) {
reconnect();
}
if (!(digitalRead(pinSW))) { // check if pushbutton is pressed
client.publish(topicButton, "ON");
while (!digitalRead(pinSW)) {} // wait til switch is released
delay(10); // debounce
client.publish(topicButton, "OFF");
}
aVal = digitalRead(pinA);
if (aVal != pinALast){ // Means the knob is rotating
// if the knob is rotating, we need to determine direction
// We do that by reading pin B.
if (digitalRead(pinB) != aVal) { // Means pin A Changed first - We're Rotating Clockwise
//client.publish(topicEncoder, "UP");
encoderPosCount = encoderPosCount + 5;
if (encoderPosCount >254) { encoderPosCount=254;}
//delay(10);
} else {// Otherwise B changed first and we're moving CCW
//client.publish(topicEncoder, "DOWN");
encoderPosCount = encoderPosCount - 5;
if (encoderPosCount <0) { encoderPosCount=0; }
//delay(10);
}
Serial.println(encoderPosCount);
dtostrf(encoderPosCount,5,1,charPos) ;
client.publish(topicEncoder, charPos);
}
pinALast = aVal;
client.loop();
}
The code needs some clean-up… but is working.
So whenever the knob is turned it sends the value to HASS via MQTT.
Same goes for the button press.
The value also can be set via MQTT from HASS - the idea here is that If I change the brightness for example via the Webinterface this will be reflected on the nodeMCU.
HASS configuration is a follows:
configuration.yaml:
sensor:
- platform: mqtt
name: encoder
state_topic: "dial/encoder"
qos: 0
- platform: template
sensors:
turntable_brightness:
value_template: '{{ states.light.turntable.attributes.brightness }}'
binary_sensor:
- platform: mqtt
name: button
state_topic: "dial/button"
qos: 0
Now I need the following automations to put things together:
- alias: 'Encoder changed'
trigger:
platform: state
entity_id: sensor.encoder
action:
service: light.turn_on
entity_id: light.turntable, light.floor_lamp, light.tv_lamp
data_template:
brightness: '{{ states.sensor.encoder.state | int }}'
transition: 0.5
- alias: 'brightness changed'
trigger:
platform: template
value_template: '{{(as_timestamp(now()) - as_timestamp(states.sensor.turntable_brightness.last_changed) > 20) and (states.light.turntable.attributes.brightness | int != states.sensor.turntable_brightness | int)}}'
action:
service: mqtt.publish
data:
topic: 'dial/SetValue'
payload_template: "{{ (states.light.turntable.attributes.brightness + states.light.floor_lamp.attributes.brightness + states.light.tv_lamp.attributes.brightness)/3 | int}}"
- alias: 'Button clicked'
trigger:
platform: mqtt
topic: 'dial/button'
payload: 'ON'
action:
service: light.toggle
entity_id: light.turntable, light.floor_lamp, light.tv_lamp
And thats it.
The only problem so far is that the encoder is not reacting correctly if I turn it fast.
And there is considerable delay before applying the new brightness values. If anyone has an idea on how to streamline this it would be much appreciated.