diff --git a/sonoff/my_user_config.h b/sonoff/my_user_config.h index e1f6e364a..8e3050a4e 100644 --- a/sonoff/my_user_config.h +++ b/sonoff/my_user_config.h @@ -356,6 +356,7 @@ #define MP3_VOLUME 10 // Set the startup volume on init, the range can be 0..30(max) #define USE_TUYA_DIMMER // Add support for Tuya Serial Dimmer #define TUYA_DIMMER_ID 0 // Default dimmer Id +//#define USE_ARMTRONIX_DIMMERS //Add support for Armtronix Dimmers // Power monitoring sensors ----------------------- #define USE_PZEM004T // Add support for PZEM004T Energy monitor (+2k code) diff --git a/sonoff/sonoff.h b/sonoff/sonoff.h index d66ccf218..fb59eac68 100644 --- a/sonoff/sonoff.h +++ b/sonoff/sonoff.h @@ -227,7 +227,7 @@ enum DomoticzSensors {DZ_TEMP, DZ_TEMP_HUM, DZ_TEMP_HUM_BARO, DZ_POWER_ENERGY, D enum Ws2812ClockIndex { WS_SECOND, WS_MINUTE, WS_HOUR, WS_MARKER }; enum Ws2812Color { WS_RED, WS_GREEN, WS_BLUE }; -enum LightTypes {LT_BASIC, LT_PWM1, LT_PWM2, LT_PWM3, LT_PWM4, LT_PWM5, LT_PWM6, LT_PWM7, LT_SERIAL, LT_NU9, LT_NU10, LT_WS2812, LT_RGBW, LT_RGBWC}; +enum LightTypes {LT_BASIC, LT_PWM1, LT_PWM2, LT_PWM3, LT_PWM4, LT_PWM5, LT_PWM6, LT_PWM7, LT_SERIAL, LT_SERIAL2, LT_NU10, LT_WS2812, LT_RGBW, LT_RGBWC}; enum LichtSubtypes {LST_NONE, LST_SINGLE, LST_COLDWARM, LST_RGB, LST_RGBW, LST_RGBWC}; enum LichtSchemes {LS_POWER, LS_WAKEUP, LS_CYCLEUP, LS_CYCLEDN, LS_RANDOM, LS_MAX}; diff --git a/sonoff/sonoff_template.h b/sonoff/sonoff_template.h index 2735ac76e..f2595c82c 100644 --- a/sonoff/sonoff_template.h +++ b/sonoff/sonoff_template.h @@ -251,6 +251,7 @@ enum SupportedModules { APLIC_WDP303075, TUYA_DIMMER, GOSUND, + ARMTRONIX_DIMMERS, MAXMODULE }; /********************************************************************************************/ @@ -472,6 +473,7 @@ const uint8_t kModuleNiceList[MAXMODULE] PROGMEM = { OBI, ESP_SWITCH, // Switch Devices TUYA_DIMMER, // Dimmer Devices + ARMTRONIX_DIMMERS, H801, // Light Devices MAGICHOME, ARILUX_LC01, @@ -1230,6 +1232,23 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_LED2_INV, // GPIO13 LED2 (red) inv GPIO_REL1, // GPIO14 Relay (0 = Off, 1 = On) 0, 0, 0 + }, + { "ARMTR Dimmer", // ARMTRONIX Dimmer, one or two channel (ESP8266 w/ separate MCU dimmer) + // https://www.tindie.com/products/Armtronix/wifi-ac-dimmer-two-triac-board/ + // https://www.tindie.com/products/Armtronix/wifi-ac-dimmer-esp8266-one-triac-board-alexaecho/ + GPIO_USER, + GPIO_TXD, // GPIO01 MCU serial control + GPIO_USER, + GPIO_RXD, // GPIO03 MCU serial control + GPIO_USER, + GPIO_USER, + 0, 0, 0, 0, 0, 0, // Flash connection + GPIO_USER, + GPIO_USER, + GPIO_USER, + GPIO_USER, + GPIO_USER, + 0 } }; diff --git a/sonoff/support.ino b/sonoff/support.ino index a27e08866..02e8ad74f 100644 --- a/sonoff/support.ino +++ b/sonoff/support.ino @@ -994,7 +994,9 @@ void GetFeatures(void) #ifdef USE_RC_SWITCH feature_drv2 |= 0x00010000; // xdrv_17_rcswitch.ino #endif - +#ifdef USE_ARMTRONIX_DIMMERS + feature_drv2 |= 0x00020000; // xdrv_18_armtronixdimmer.ino +#endif #ifdef NO_EXTRA_4K_HEAP diff --git a/sonoff/xdrv_04_light.ino b/sonoff/xdrv_04_light.ino index ae632b912..35cda4f97 100644 --- a/sonoff/xdrv_04_light.ino +++ b/sonoff/xdrv_04_light.ino @@ -396,6 +396,9 @@ void LightInit(void) else if (LT_SERIAL == light_type) { light_subtype = LST_SINGLE; } + else if (LT_SERIAL2 == light_type) { + light_subtype = LST_COLDWARM; + } else { light_pdi_pin = pin[GPIO_DI]; light_pdcki_pin = pin[GPIO_DCKI]; @@ -831,6 +834,12 @@ void LightAnimate(void) LightSerialDuty(cur_col[0]); } #endif // USE_TUYA_DIMMER +#ifdef USE_ARMTRONIX_DIMMERS + if (light_type == LT_SERIAL2) { + LightSerial2Duty(cur_col[0],cur_col[1]); + } +#endif // USE_ARMTRONIX_DIMMERS + } } } diff --git a/sonoff/xdrv_18_armtronixDimmers.ino b/sonoff/xdrv_18_armtronixDimmers.ino new file mode 100644 index 000000000..9fae00ff0 --- /dev/null +++ b/sonoff/xdrv_18_armtronixDimmers.ino @@ -0,0 +1,194 @@ +/* + xdrv_18_armtronixdimmer.ino - Armtronix dimmers support for Sonoff-Tasmota + + Copyright (C) 2018 digiblur, Joel Stein and Theo Arends + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/*This code can be used for Armtronix dimmers. The dimmers contain a Atmega328 to do the actual dimming. + Checkout the Tasmota Wiki for information on how to flash this Atmega328 with the firmware to work together with this driver. +*/ + +#ifdef USE_ARMTRONIX_DIMMERS + +#define XDRV_18 18 + +#include + +TasmotaSerial *ArmtronixSerial = nullptr; + +boolean armtronix_ignore_dim = false; // Flag to skip serial send to prevent looping when processing inbound states from the faceplate interaction +int8_t armtronix_wifi_state = -2; // Keep MCU wifi-status in sync with WifiState() +int8_t armtronix_dimState[2]; //Dimmer state values. +int8_t armtronix_knobState[2]; //Dimmer state values. + + +/*********************************************************************************************\ + * Internal Functions +\*********************************************************************************************/ + + + +void LightSerial2Duty(uint8_t duty1, uint8_t duty2) +{ + if (ArmtronixSerial && !armtronix_ignore_dim) { + duty1 = ((float)duty1)/2.575757; //max 99 + duty2 = ((float)duty2)/2.575757; //max 99 + armtronix_dimState[0] = duty1; + armtronix_dimState[1] = duty2; + ArmtronixSerial->print("Dimmer1:"); + ArmtronixSerial->print(duty1); + ArmtronixSerial->print("\nDimmer2:"); + ArmtronixSerial->println(duty2); + + snprintf_P(log_data, sizeof(log_data), PSTR( "ARM: Send Serial Packet Dim Values=%d,%d"), armtronix_dimState[0],armtronix_dimState[1]); + AddLog(LOG_LEVEL_DEBUG); + + } else { + armtronix_ignore_dim = false; + snprintf_P(log_data, sizeof(log_data), PSTR( "ARM: Send Dim Level skipped due to already set. Value=%d,%d"), armtronix_dimState[0],armtronix_dimState[1]); + AddLog(LOG_LEVEL_DEBUG); + + } +} + +void ArmtronixRequestState(){ + if(ArmtronixSerial) { + // Get current status of MCU + snprintf_P(log_data, sizeof(log_data), "TYA: Request MCU state"); + AddLog(LOG_LEVEL_DEBUG); + ArmtronixSerial->println("Status"); + + } +} + +/*********************************************************************************************\ + * API Functions +\*********************************************************************************************/ + +boolean ArmtronixModuleSelected() +{ + light_type = LT_SERIAL2; + return true; +} + +void ArmtronixInit() +{ + armtronix_dimState[0] = -1; + armtronix_dimState[1] = -1; + armtronix_knobState[0] = -1; + armtronix_knobState[1] = -1; + ArmtronixSerial = new TasmotaSerial(pin[GPIO_RXD], pin[GPIO_TXD], 2); + if (ArmtronixSerial->begin(115200)) { + if (ArmtronixSerial->hardwareSerial()) { ClaimSerial(); } + ArmtronixSerial->println("Status"); + } +} + +void ArmtronixSerialInput() +{ + String answer; + int8_t newDimState[2]; + uint8_t temp; + int commaIndex; + char scmnd[20]; + if (ArmtronixSerial->available()) { + yield(); + answer = ArmtronixSerial->readStringUntil('\n'); + if(answer.substring(0,7) == "Status:"){ + commaIndex = 6; + for(int i =0;i<2;i++){ + newDimState[i] = answer.substring(commaIndex+1,answer.indexOf(',',commaIndex+1)).toInt(); + if(newDimState[i] != armtronix_dimState[i]){ + temp = ((float)newDimState[i])*1.01010101010101; //max 255 + armtronix_dimState[i] = newDimState[i]; + armtronix_ignore_dim = true; + snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_CHANNEL "%d %d"),i+1, temp); + ExecuteCommand(scmnd,SRC_SWITCH); + snprintf_P(log_data, sizeof(log_data), PSTR("ARM: Send CMND_CHANNEL=%s"), scmnd ); + AddLog(LOG_LEVEL_DEBUG); + } + commaIndex = answer.indexOf(',',commaIndex+1); + } + armtronix_knobState[0] = answer.substring(commaIndex+1,answer.indexOf(',',commaIndex+1)).toInt(); + commaIndex = answer.indexOf(',',commaIndex+1); + armtronix_knobState[1] = answer.substring(commaIndex+1,answer.indexOf(',',commaIndex+1)).toInt(); + } + } +} + +void ArmtronixSetWifiLed(){ + uint8_t wifi_state = 0x02; + switch(WifiState()){ + case WIFI_SMARTCONFIG: + wifi_state = 0x00; + break; + case WIFI_MANAGER: + case WIFI_WPSCONFIG: + wifi_state = 0x01; + break; + case WIFI_RESTART: + wifi_state = 0x03; + break; + } + + snprintf_P(log_data, sizeof(log_data), "ARM: Set WiFi LED to state %d (%d)", wifi_state, WifiState()); + AddLog(LOG_LEVEL_DEBUG); + + char state = '0' + (wifi_state & 1 > 0); + ArmtronixSerial->print("Setled:"); + ArmtronixSerial->write(state); + ArmtronixSerial->write(','); + state = '0' + (wifi_state & 2 > 0); + ArmtronixSerial->write(state); + ArmtronixSerial->write(10); + armtronix_wifi_state = WifiState(); + + +} + + +/*********************************************************************************************\ + * Interface +\*********************************************************************************************/ +boolean Xdrv18(byte function) +{ + boolean result = false; + + if (ARMTRONIX_DIMMERS == Settings.module) { + switch (function) { + case FUNC_MODULE_INIT: + result = ArmtronixModuleSelected(); + break; + case FUNC_INIT: + ArmtronixInit(); + break; + case FUNC_LOOP: + if (ArmtronixSerial) { ArmtronixSerialInput(); } + break; + case FUNC_EVERY_SECOND: + if(ArmtronixSerial){ + if (armtronix_wifi_state!=WifiState()) { ArmtronixSetWifiLed(); } + if(uptime&1){ + ArmtronixSerial->println("Status"); + } + } + break; + } + } + return result; +} + +#endif // USE_ARMTRONIX_DIMMERS