diff --git a/code/espurna/config/arduino.h b/code/espurna/config/arduino.h index e7201c0f..6f9a13c0 100644 --- a/code/espurna/config/arduino.h +++ b/code/espurna/config/arduino.h @@ -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 diff --git a/code/espurna/config/defaults.h b/code/espurna/config/defaults.h index 0f50c769..d97d7711 100644 --- a/code/espurna/config/defaults.h +++ b/code/espurna/config/defaults.h @@ -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 // ----------------------------------------------------------------------------- diff --git a/code/espurna/config/general.h b/code/espurna/config/general.h index 9e090cb5..67457ab7 100644 --- a/code/espurna/config/general.h +++ b/code/espurna/config/general.h @@ -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 //------------------------------------------------------------------------------ diff --git a/code/espurna/config/hardware.h b/code/espurna/config/hardware.h index 6ddb12cd..3cef05eb 100644 --- a/code/espurna/config/hardware.h +++ b/code/espurna/config/hardware.h @@ -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!!) // ----------------------------------------------------------------------------- diff --git a/code/espurna/config/progmem.h b/code/espurna/config/progmem.h index 0298091b..f370070c 100644 --- a/code/espurna/config/progmem.h +++ b/code/espurna/config/progmem.h @@ -55,6 +55,9 @@ PROGMEM const char espurna_modules[] = #if DOMOTICZ_SUPPORT "DOMOTICZ " #endif + #if ENCODER_SUPPORT + "ENCODER " + #endif #if HOMEASSISTANT_SUPPORT "HOMEASSISTANT " #endif diff --git a/code/espurna/config/types.h b/code/espurna/config/types.h index 2bf6d4ea..880b6dd2 100644 --- a/code/espurna/config/types.h +++ b/code/espurna/config/types.h @@ -48,6 +48,13 @@ #define BUTTON_SET_PULLUP 4 #endif +//------------------------------------------------------------------------------ +// ENCODER +//------------------------------------------------------------------------------ + +#define ENCODER_MODE_CHANNEL 0 +#define ENCODER_MODE_RATIO 1 + //------------------------------------------------------------------------------ // RELAY //------------------------------------------------------------------------------ diff --git a/code/espurna/encoder.ino b/code/espurna/encoder.ino new file mode 100644 index 00000000..1934efcb --- /dev/null +++ b/code/espurna/encoder.ino @@ -0,0 +1,156 @@ +/* + +ENCODER MODULE + +Copyright (C) 2018 by Xose PĂ©rez + +*/ + +#if ENCODER_SUPPORT && (LIGHT_PROVIDER != LIGHT_PROVIDER_NONE) + +#include +#include + +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 _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) diff --git a/code/espurna/espurna.ino b/code/espurna/espurna.ino index a4fcf432..d085b285 100644 --- a/code/espurna/espurna.ino +++ b/code/espurna/espurna.ino @@ -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 diff --git a/code/espurna/light.ino b/code/espurna/light.ino index d2456dca..3e48af12 100644 --- a/code/espurna/light.ino +++ b/code/espurna/light.ino @@ -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; } diff --git a/code/espurna/migrate.ino b/code/espurna/migrate.ino index 14f80b05..fdec5006 100644 --- a/code/espurna/migrate.ino +++ b/code/espurna/migrate.ino @@ -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 diff --git a/code/platformio.ini b/code/platformio.ini index 33d854e8..896cc9ac 100644 --- a/code/platformio.ini +++ b/code/platformio.ini @@ -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}