Browse Source

Initial -untested- support for Xiaomi Smart Desk Lamp (#884) and rotary encoders

ech1560
Xose Pérez 6 years ago
parent
commit
ea1207195c
11 changed files with 453 additions and 4 deletions
  1. +2
    -0
      code/espurna/config/arduino.h
  2. +132
    -0
      code/espurna/config/defaults.h
  3. +8
    -0
      code/espurna/config/general.h
  4. +51
    -0
      code/espurna/config/hardware.h
  5. +3
    -0
      code/espurna/config/progmem.h
  6. +7
    -0
      code/espurna/config/types.h
  7. +156
    -0
      code/espurna/encoder.ino
  8. +4
    -1
      code/espurna/espurna.ino
  9. +4
    -0
      code/espurna/light.ino
  10. +57
    -0
      code/espurna/migrate.ino
  11. +29
    -3
      code/platformio.ini

+ 2
- 0
code/espurna/config/arduino.h View File

@ -95,6 +95,7 @@
//#define LOHAS_9W
//#define YJZK_SWITCH_1CH
//#define YJZK_SWITCH_3CH
//#define XIAOMI_SMART_DESK_LAMP
//--------------------------------------------------------------------------------
// Features (values below are non-default values)
@ -109,6 +110,7 @@
//#define DEBUG_UDP_SUPPORT 1
//#define DEBUG_WEB_SUPPORT 0
//#define DOMOTICZ_SUPPORT 0
//#define ENCODER_SUPPORT 1
//#define HOMEASSISTANT_SUPPORT 0
//#define I2C_SUPPORT 1
//#define INFLUXDB_SUPPORT 1


+ 132
- 0
code/espurna/config/defaults.h View File

@ -208,6 +208,138 @@
#define BUTTON8_RELAY 0
#endif
// -----------------------------------------------------------------------------
// Encoders
// -----------------------------------------------------------------------------
#ifndef ENCODER1_PIN1
#define ENCODER1_PIN1 GPIO_NONE
#endif
#ifndef ENCODER2_PIN1
#define ENCODER2_PIN1 GPIO_NONE
#endif
#ifndef ENCODER3_PIN1
#define ENCODER3_PIN1 GPIO_NONE
#endif
#ifndef ENCODER4_PIN1
#define ENCODER4_PIN1 GPIO_NONE
#endif
#ifndef ENCODER5_PIN1
#define ENCODER5_PIN1 GPIO_NONE
#endif
#ifndef ENCODER1_PIN2
#define ENCODER1_PIN2 GPIO_NONE
#endif
#ifndef ENCODER2_PIN2
#define ENCODER2_PIN2 GPIO_NONE
#endif
#ifndef ENCODER3_PIN2
#define ENCODER3_PIN2 GPIO_NONE
#endif
#ifndef ENCODER4_PIN2
#define ENCODER4_PIN2 GPIO_NONE
#endif
#ifndef ENCODER5_PIN2
#define ENCODER5_PIN2 GPIO_NONE
#endif
#ifndef ENCODER1_BUTTON_PIN
#define ENCODER1_BUTTON_PIN GPIO_NONE
#endif
#ifndef ENCODER2_BUTTON_PIN
#define ENCODER2_BUTTON_PIN GPIO_NONE
#endif
#ifndef ENCODER3_BUTTON_PIN
#define ENCODER3_BUTTON_PIN GPIO_NONE
#endif
#ifndef ENCODER4_BUTTON_PIN
#define ENCODER4_BUTTON_PIN GPIO_NONE
#endif
#ifndef ENCODER5_BUTTON_PIN
#define ENCODER5_BUTTON_PIN GPIO_NONE
#endif
#ifndef ENCODER1_BUTTON_LOGIC
#define ENCODER1_BUTTON_LOGIC HIGH
#endif
#ifndef ENCODER2_BUTTON_LOGIC
#define ENCODER2_BUTTON_LOGIC HIGH
#endif
#ifndef ENCODER3_BUTTON_LOGIC
#define ENCODER3_BUTTON_LOGIC HIGH
#endif
#ifndef ENCODER4_BUTTON_LOGIC
#define ENCODER4_BUTTON_LOGIC HIGH
#endif
#ifndef ENCODER5_BUTTON_LOGIC
#define ENCODER5_BUTTON_LOGIC HIGH
#endif
#ifndef ENCODER1_BUTTON_MODE
#define ENCODER1_BUTTON_MODE INPUT_PULLUP
#endif
#ifndef ENCODER2_BUTTON_MODE
#define ENCODER2_BUTTON_MODE INPUT_PULLUP
#endif
#ifndef ENCODER3_BUTTON_MODE
#define ENCODER3_BUTTON_MODE INPUT_PULLUP
#endif
#ifndef ENCODER4_BUTTON_MODE
#define ENCODER4_BUTTON_MODE INPUT_PULLUP
#endif
#ifndef ENCODER5_BUTTON_MODE
#define ENCODER5_BUTTON_MODE INPUT_PULLUP
#endif
#ifndef ENCODER1_MODE
#define ENCODER1_MODE 1
#endif
#ifndef ENCODER2_MODE
#define ENCODER2_MODE 1
#endif
#ifndef ENCODER3_MODE
#define ENCODER3_MODE 1
#endif
#ifndef ENCODER4_MODE
#define ENCODER4_MODE 1
#endif
#ifndef ENCODER5_MODE
#define ENCODER5_MODE 1
#endif
#ifndef ENCODER1_CHANNEL1
#define ENCODER1_CHANNEL1 0
#endif
#ifndef ENCODER2_CHANNEL1
#define ENCODER2_CHANNEL1 0
#endif
#ifndef ENCODER3_CHANNEL1
#define ENCODER3_CHANNEL1 0
#endif
#ifndef ENCODER4_CHANNEL1
#define ENCODER4_CHANNEL1 0
#endif
#ifndef ENCODER5_CHANNEL1
#define ENCODER5_CHANNEL1 0
#endif
#ifndef ENCODER1_CHANNEL2
#define ENCODER1_CHANNEL2 1
#endif
#ifndef ENCODER2_CHANNEL2
#define ENCODER2_CHANNEL2 1
#endif
#ifndef ENCODER3_CHANNEL2
#define ENCODER3_CHANNEL2 1
#endif
#ifndef ENCODER4_CHANNEL2
#define ENCODER4_CHANNEL2 1
#endif
#ifndef ENCODER5_CHANNEL2
#define ENCODER5_CHANNEL2 1
#endif
// -----------------------------------------------------------------------------
// Relays
// -----------------------------------------------------------------------------


+ 8
- 0
code/espurna/config/general.h View File

@ -221,6 +221,14 @@
#define BUTTON_LNGLNGCLICK_DELAY 10000 // Time in ms holding the button down to get a long-long click
#endif
//------------------------------------------------------------------------------
// ENCODER
//------------------------------------------------------------------------------
#ifndef ENCODER_SUPPORT
#define ENCODER_SUPPORT 0
#endif
//------------------------------------------------------------------------------
// LED
//------------------------------------------------------------------------------


+ 51
- 0
code/espurna/config/hardware.h View File

@ -2675,6 +2675,57 @@
#define MY92XX_MAPPING 0, 1, 2, 3, 4
#define LIGHT_WHITE_FACTOR (0.1) // White LEDs are way more bright in the B1
// -----------------------------------------------------------------------------
#elif defined(XIAOMI_SMART_DESK_LAMP)
// Info
#define MANUFACTURER "XIAOMI"
#define DEVICE "SMART_DESK_LAMP"
// Buttons
#define BUTTON1_PIN 2
#define BUTTON2_PIN 14
#define BUTTON1_MODE BUTTON_PUSHBUTTON | BUTTON_DEFAULT_HIGH | BUTTON_SET_PULLUP
#define BUTTON2_MODE BUTTON_PUSHBUTTON | BUTTON_DEFAULT_HIGH | BUTTON_SET_PULLUP
// This button doubles as switch here and as encoder mode switch below
// Clicking it (for less than 500ms) will turn the light on and off
// Double and Long clicks will not work as these are used to modify the encoder action
#define BUTTON1_RELAY 1
#define BUTTON_LNGCLICK_DELAY 500
#define BUTTON1_DBLCLICK BUTTON_MODE_NONE
#define BUTTON1_LNGCLICK BUTTON_MODE_NONE
#define BUTTON1_LNGLNGCLICK BUTTON_MODE_NONE
// Hidden button will enter AP mode if dblclick and reset the device when long-long-clicked
#define BUTTON2_DBLCLICK BUTTON_MODE_AP
#define BUTTON2_LNGLNGCLICK BUTTON_MODE_RESET
// Light
#define LIGHT_PROVIDER LIGHT_PROVIDER_DIMMER
#define DUMMY_RELAY_COUNT 1
#define LIGHT_CHANNELS 2
#define LIGHT_CH1_PIN 5 // warm white
#define LIGHT_CH1_INVERSE 0
#define LIGHT_CH2_PIN 4 // cold white
#define LIGHT_CH2_INVERSE 0
// Encoder
// If mode is ENCODER_MODE_RATIO, the value ratio between both channels is changed
// when the button is not pressed, and the overall brightness when pressed
// If mode is ENCODER_MODE_CHANNEL, the first channel value is changed
// when the button is not pressed, and the second channel when pressed
// If no ENCODERX_BUTTON_PIN defined it will only change the value of the first defined channel
#define ENCODER_SUPPORT 1
#define ENCODER1_PIN1 12
#define ENCODER1_PIN2 13
#define ENCODER1_BUTTON_PIN 2 // active low by default, with software pullup
#define ENCODER1_CHANNEL1 0 // please note this value is 0-based (LIGHT_CH1 above)
#define ENCODER1_CHANNEL2 1 // please note this value is 0-based (LIGHT_CH2 above)
#define ENCODER1_MODE ENCODER_MODE_RATIO
// -----------------------------------------------------------------------------
// TEST boards (do not use!!)
// -----------------------------------------------------------------------------


+ 3
- 0
code/espurna/config/progmem.h View File

@ -55,6 +55,9 @@ PROGMEM const char espurna_modules[] =
#if DOMOTICZ_SUPPORT
"DOMOTICZ "
#endif
#if ENCODER_SUPPORT
"ENCODER "
#endif
#if HOMEASSISTANT_SUPPORT
"HOMEASSISTANT "
#endif


+ 7
- 0
code/espurna/config/types.h View File

@ -48,6 +48,13 @@
#define BUTTON_SET_PULLUP 4
#endif
//------------------------------------------------------------------------------
// ENCODER
//------------------------------------------------------------------------------
#define ENCODER_MODE_CHANNEL 0
#define ENCODER_MODE_RATIO 1
//------------------------------------------------------------------------------
// RELAY
//------------------------------------------------------------------------------


+ 156
- 0
code/espurna/encoder.ino View File

@ -0,0 +1,156 @@
/*
ENCODER MODULE
Copyright (C) 2018 by Xose Pérez <xose dot perez at gmail dot com>
*/
#if ENCODER_SUPPORT && (LIGHT_PROVIDER != LIGHT_PROVIDER_NONE)
#include <Encoder.h>
#include <vector>
typedef struct {
Encoder * encoder;
unsigned char button_pin;
unsigned char button_logic;
unsigned char button_mode;
unsigned char mode;
unsigned char channel1; // default
unsigned char channel2; // only if button defined and pressed
} encoder_t;
std::vector<encoder_t> _encoders;
void _encoderConfigure() {
// Clean previous encoders
for (unsigned char i=0; i<_encoders.size(); i++) {
free(_encoders[i].encoder);
}
_encoders.clear();
// Load encoders
#if (ENCODER1_PIN1 != GPIO_NONE) && (ENCODER1_PIN2 != GPIO_NONE)
{
_encoders.push_back({
new Encoder(ENCODER1_PIN1, ENCODER1_PIN2),
ENCODER1_BUTTON_PIN, ENCODER1_BUTTON_LOGIC, ENCODER1_BUTTON_MODE, ENCODER1_MODE,
ENCODER1_CHANNEL1, ENCODER1_CHANNEL2
});
}
#endif
#if (ENCODER2_PIN1 != GPIO_NONE) && (ENCODER2_PIN2 != GPIO_NONE)
{
_encoders.push_back({
new Encoder(ENCODER2_PIN1, ENCODER2_PIN2),
ENCODER2_BUTTON_PIN, ENCODER2_BUTTON_LOGIC, ENCODER2_BUTTON_MODE, ENCODER2_MODE,
ENCODER2_CHANNEL1, ENCODER2_CHANNEL2
});
}
#endif
#if (ENCODER3_PIN1 != GPIO_NONE) && (ENCODER3_PIN2 != GPIO_NONE)
{
_encoders.push_back({
new Encoder(ENCODER3_PIN1, ENCODER3_PIN2),
ENCODER3_BUTTON_PIN, ENCODER3_BUTTON_LOGIC, ENCODER3_BUTTON_MODE, ENCODER3_MODE,
ENCODER3_CHANNEL1, ENCODER3_CHANNEL2
});
}
#endif
#if (ENCODER4_PIN1 != GPIO_NONE) && (ENCODER4_PIN2 != GPIO_NONE)
{
_encoders.push_back({
new Encoder(ENCODER4_PIN1, ENCODER4_PIN2),
ENCODER4_BUTTON_PIN, ENCODER4_BUTTON_LOGIC, ENCODER4_BUTTON_MODE, ENCODER4_MODE,
ENCODER4_CHANNEL1, ENCODER4_CHANNEL2
});
}
#endif
#if (ENCODER5_PIN1 != GPIO_NONE) && (ENCODER5_PIN2 != GPIO_NONE)
{
_encoders.push_back({
new Encoder(ENCODER5_PIN1, ENCODER5_PIN2),
ENCODER5_BUTTON_PIN, ENCODER5_BUTTON_LOGIC, ENCODER5_BUTTON_MODE, ENCODER5_MODE,
ENCODER5_CHANNEL1, ENCODER5_CHANNEL2
});
}
#endif
// Setup encoders
for (unsigned char i=0; i<_encoders.size(); i++) {
if (GPIO_NONE != _encoders[i].button_pin) {
pinMode(_encoders[i].button_pin, _encoders[i].button_mode);
}
}
}
void _encoderLoop() {
// for each encoder
for (unsigned char i=0; i<_encoders.size(); i++) {
// get encoder
encoder_t encoder = _encoders[i];
// read encoder
long delta = encoder.encoder->read();
encoder.encoder->write(0);
if (0 == delta) continue;
DEBUG_MSG_P(PSTR("[ENCODER] Delta: %d\n"), delta);
// action
if (encoder.button_pin == GPIO_NONE) {
// if there is no button, the encoder driver the CHANNEL1
lightChannelStep(encoder.channel1, delta);
} else {
// check if button is pressed
bool pressed = (digitalRead(encoder.button_pin) != encoder.button_logic);
if (ENCODER_MODE_CHANNEL == encoder.mode) {
// the button controls what channel we are changing
lightChannelStep(pressed ? encoder.channel2 : encoder.channel1, delta);
} if (ENCODER_MODE_RATIO == encoder.mode) {
// the button controls if we are changing the channel ratio or the overall brightness
if (pressed) {
lightChannelStep(encoder.channel1, delta);
lightChannelStep(encoder.channel2, -delta);
} else {
lightBrightnessStep(delta);
}
}
}
lightUpdate(true, true);
}
}
// -----------------------------------------------------------------------------
void encoderSetup() {
// Configure encoders
_encoderConfigure();
// Main callbacks
espurnaRegisterLoop(_encoderLoop);
espurnaRegisterReload(_encoderConfigure);
DEBUG_MSG_P(PSTR("[ENCODER] Number of encoders: %u\n"), _encoders.size());
}
#endif // ENCODER_SUPPORT && (LIGHT_PROVIDER != LIGHT_PROVIDER_NONE)

+ 4
- 1
code/espurna/espurna.ino View File

@ -110,14 +110,17 @@ void setup() {
#if LIGHT_PROVIDER != LIGHT_PROVIDER_NONE
lightSetup();
#endif
relaySetup();
#if BUTTON_SUPPORT
buttonSetup();
#endif
#if ENCODER_SUPPORT && (LIGHT_PROVIDER != LIGHT_PROVIDER_NONE)
encoderSetup();
#endif
#if LED_SUPPORT
ledSetup();
#endif
#if MQTT_SUPPORT
mqttSetup();
#endif


+ 4
- 0
code/espurna/light.ino View File

@ -741,6 +741,10 @@ void lightChannel(unsigned char id, unsigned int value) {
}
}
void lightChannelStep(unsigned char id, int steps) {
lightChannel(id, lightChannel(id) + steps * LIGHT_STEP);
}
unsigned int lightBrightness() {
return _light_brightness;
}


+ 57
- 0
code/espurna/migrate.ino View File

@ -1106,6 +1106,63 @@ void migrate() {
setSetting("myDCKIGPIO", 15);
setSetting("relays", 1);
#elif defined(YJZK_SWITCH_1CH)
setSetting("board", 85);
setSetting("ledGPIO", 0, 13);
setSetting("ledLogic", 0, 0);
setSetting("ledWifi", 0);
setSetting("btnGPIO", 0, 0);
setSetting("btnRelay", 0, 0);
setSetting("relayGPIO", 0, 12);
setSetting("relayType", 0, RELAY_TYPE_NORMAL);
#elif defined(YJZK_SWITCH_3CH)
setSetting("board", 86);
setSetting("ledGPIO", 0, 13);
setSetting("ledLogic", 0, 0);
setSetting("ledWifi", 0);
setSetting("btnGPIO", 0, 0);
setSetting("btnGPIO", 1, 9);
setSetting("btnGPIO", 2, 10);
setSetting("btnRelay", 0, 0);
setSetting("btnRelay", 1, 1);
setSetting("btnRelay", 2, 2);
setSetting("relayGPIO", 0, 12);
setSetting("relayGPIO", 1, 5);
setSetting("relayGPIO", 2, 4);
setSetting("relayType", 0, RELAY_TYPE_NORMAL);
setSetting("relayType", 1, RELAY_TYPE_NORMAL);
setSetting("relayType", 2, RELAY_TYPE_NORMAL);
#elif defined(XIAOMI_SMART_DESK_LAMP)
setSetting("board", 87);
setSetting("relayProvider", RELAY_PROVIDER_LIGHT);
setSetting("lightProvider", LIGHT_PROVIDER_DIMMER);
setSetting("relays", 1);
setSetting("chGPIO", 0, 5);
setSetting("chGPIO", 1, 4);
setSetting("chLogic", 0, 0);
setSetting("chLogic", 1, 0);
setSetting("btnGPIO", 0, 2);
setSetting("btnGPIO", 1, 14);
setSetting("btnRelay", 0, 0);
setSetting("btnLngDelay", 500);
setSetting("btnDblClick", 0, BUTTON_MODE_NONE);
setSetting("btnLngClick", 0, BUTTON_MODE_NONE);
setSetting("btnLngLngClick", 0, BUTTON_MODE_NONE);
setSetting("btnDblClick", 1, BUTTON_MODE_AP);
setSetting("btnLngLngClick", 1, BUTTON_MODE_RESET);
setSetting("enc1stGPIO", 0, 12);
setSetting("enc2ndGPIO", 0, 13);
setSetting("encBtnGPIO", 0, 2);
setSetting("encMode", ENCODER_MODE_RATIO);
#else
// Allow users to define new settings without migration config


+ 29
- 3
code/platformio.ini View File

@ -1,5 +1,5 @@
[platformio]
env_default = wemos-d1mini-relayshield
env_default = nodemcu-lolin
src_dir = espurna
data_dir = espurna/data
@ -75,6 +75,7 @@ lib_deps =
https://bitbucket.org/xoseperez/debounceevent.git#2.0.1
https://github.com/xoseperez/eeprom_rotate#0.9.1
Embedis
Encoder
https://github.com/plerup/espsoftwareserial#3.4.1
https://github.com/me-no-dev/ESPAsyncTCP#55cd520
https://github.com/me-no-dev/ESPAsyncWebServer#05306e4
@ -2551,7 +2552,7 @@ extra_scripts = ${common.extra_scripts}
[env:allterco-shelly1]
platform = ${common.platform}
framework = ${common.framework}
board = ${common.board_1m}
board = ${common.board_2m}
board_build.flash_mode = ${common.flash_mode}
lib_deps = ${common.lib_deps}
lib_ignore = ${common.lib_ignore}
@ -2562,7 +2563,7 @@ extra_scripts = ${common.extra_scripts}
[env:allterco-shelly1-ota]
platform = ${common.platform}
framework = ${common.framework}
board = ${common.board_1m}
board = ${common.board_2m}
board_build.flash_mode = ${common.flash_mode}
lib_deps = ${common.lib_deps}
lib_ignore = ${common.lib_ignore}
@ -2572,3 +2573,28 @@ upload_port = ${common.upload_port}
upload_flags = ${common.upload_flags}
monitor_speed = ${common.monitor_speed}
extra_scripts = ${common.extra_scripts}
[env:xiaomi-smart-desk-lamp]
platform = ${common.platform}
framework = ${common.framework}
board = ${common.board_1m}
board_build.flash_mode = ${common.flash_mode}
lib_deps = ${common.lib_deps}
lib_ignore = ${common.lib_ignore}
build_flags = ${common.build_flags_1m0m} -DXIAOMI_SMART_DESK_LAMP
monitor_speed = ${common.monitor_speed}
extra_scripts = ${common.extra_scripts}
[env:xiaomi-smart-desk-lamp-ota]
platform = ${common.platform}
framework = ${common.framework}
board = ${common.board_1m}
board_build.flash_mode = ${common.flash_mode}
lib_deps = ${common.lib_deps}
lib_ignore = ${common.lib_ignore}
build_flags = ${common.build_flags_1m0m} -DXIAOMI_SMART_DESK_LAMP
upload_speed = ${common.upload_speed}
upload_port = ${common.upload_port}
upload_flags = ${common.upload_flags}
monitor_speed = ${common.monitor_speed}
extra_scripts = ${common.extra_scripts}

Loading…
Cancel
Save