diff --git a/sonoff/settings.h b/sonoff/settings.h index f185004c4..fcffe681f 100644 --- a/sonoff/settings.h +++ b/sonoff/settings.h @@ -68,7 +68,7 @@ typedef union { // Restricted by MISRA-C Rule 18.4 bu uint32_t time_append_timezone : 1; // bit 2 (v6.2.1.2) uint32_t gui_hostname_ip : 1; // bit 3 (v6.2.1.20) uint32_t tuya_apply_o20 : 1; // bit 4 (v6.3.0.4) - uint32_t spare05 : 1; + uint32_t armtronix_apply_o20 : 1; // bit 5 (v????) uint32_t spare06 : 1; uint32_t spare07 : 1; uint32_t spare08 : 1; diff --git a/sonoff/sonoff.h b/sonoff/sonoff.h index d13460e64..ed23f80f2 100644 --- a/sonoff/sonoff.h +++ b/sonoff/sonoff.h @@ -221,13 +221,13 @@ enum ButtonStates { PRESSED, NOT_PRESSED }; enum Shortcuts { SC_CLEAR, SC_DEFAULT, SC_USER }; -enum SettingsParmaIndex {P_HOLD_TIME, P_MAX_POWER_RETRY, P_TUYA_DIMMER_ID, P_MDNS_DELAYED_START, P_MAX_PARAM8}; // Max is PARAM8_SIZE (18) +enum SettingsParmaIndex {P_HOLD_TIME, P_MAX_POWER_RETRY, P_TUYA_DIMMER_ID, P_ARMTRONIX_DIMMER_ID, P_MDNS_DELAYED_START, P_MAX_PARAM8}; // Max is PARAM8_SIZE (18) enum DomoticzSensors {DZ_TEMP, DZ_TEMP_HUM, DZ_TEMP_HUM_BARO, DZ_POWER_ENERGY, DZ_ILLUMINANCE, DZ_COUNT, DZ_VOLTAGE, DZ_CURRENT, DZ_AIRQUALITY, DZ_MAX_SENSORS}; 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_NU9, 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 642914a53..f94c716de 100644 --- a/sonoff/sonoff_template.h +++ b/sonoff/sonoff_template.h @@ -133,6 +133,8 @@ enum UserSelectablePins { GPIO_RFRECV, // RF receiver GPIO_TUYA_TX, // Tuya Serial interface GPIO_TUYA_RX, // Tuya Serial interface + GPIO_ARMTRONIX_TX, // ARMTRONIX Serial interface + GPIO_ARMTRONIX_RX, // ARMTRONIX Serial interface GPIO_SENSOR_END }; // Programmer selectable GPIO functionality offset by user selectable GPIOs @@ -190,8 +192,8 @@ const char kSensorNames[] PROGMEM = D_SENSOR_HX711_SCK "|" D_SENSOR_HX711_DAT "|" D_SENSOR_TX20_TX "|" D_SENSOR_RFSEND "|" D_SENSOR_RFRECV "|" - D_SENSOR_TUYA_TX "|" D_SENSOR_TUYA_RX; - + D_SENSOR_TUYA_TX "|" D_SENSOR_TUYA_RX "|" + D_SENSOR_ARMTRONIX_TX "|" D_SENSOR_ARMTRONIX_RX; /********************************************************************************************/ // Supported hardware modules @@ -250,6 +252,7 @@ enum SupportedModules { TECKIN, APLIC_WDP303075, TUYA_DIMMER, + ARMTRONIX_DIMMERS, GOSUND, MAXMODULE }; @@ -426,6 +429,10 @@ const uint8_t kGpioNiceList[] PROGMEM = { GPIO_TUYA_TX, // Tuya Serial interface GPIO_TUYA_RX // Tuya Serial interface #endif +#ifdef USE_ARMTRONIX_DIMMERS + GPIO_ARMTRONIX_TX, // Tuya Serial interface + GPIO_ARMTRONIX_RX // Tuya Serial interface +#endif }; const uint8_t kModuleNiceList[MAXMODULE] PROGMEM = { @@ -472,6 +479,7 @@ const uint8_t kModuleNiceList[MAXMODULE] PROGMEM = { OBI, ESP_SWITCH, // Switch Devices TUYA_DIMMER, // Dimmer Devices + ARMTRONIX_DIMMERS, H801, // Light Devices MAGICHOME, ARILUX_LC01, @@ -1217,6 +1225,22 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_USER, GPIO_USER, 0 + }, + { "ARMTR Dimmr", // ARMTRONIX Dimmer (ESP8266 w/ separate MCU dimmer) + // https://www.amazon.com/gp/product/B07CTNSZZ8/ref=oh_aui_detailpage_o00_s00?ie=UTF8&psc=1 + GPIO_USER, // Virtual Button (controlled by MCU) + GPIO_USER, // GPIO01 MCU serial control + GPIO_USER, + GPIO_USER, // GPIO03 MCU serial control + GPIO_USER, + GPIO_USER, + 0, 0, 0, 0, 0, 0, // Flash connection + GPIO_USER, + GPIO_USER, + GPIO_USER, // GPIO14 Green Led + GPIO_USER, + GPIO_USER, + 0 }, { "Gosund SP1_v23", // https://www.amazon.de/gp/product/B0777BWS1P 0, diff --git a/sonoff/support.ino b/sonoff/support.ino index bbc81b1a3..68c091f29 100644 --- a/sonoff/support.ino +++ b/sonoff/support.ino @@ -985,7 +985,9 @@ void GetFeatures() #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 9bd88e2cb..b627e9758 100644 --- a/sonoff/xdrv_04_light.ino +++ b/sonoff/xdrv_04_light.ino @@ -396,6 +396,9 @@ void LightInit() 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() 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_armtronixDualDimmer.ino b/sonoff/xdrv_18_armtronixDualDimmer.ino new file mode 100644 index 000000000..8b4c2ea83 --- /dev/null +++ b/sonoff/xdrv_18_armtronixDualDimmer.ino @@ -0,0 +1,256 @@ +/* + xdrv_16_armtronixdimmer.ino - Armtronix dimmer 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 . +*/ + +#define USE_ARMTRONIX_DIMMERS + +#ifdef USE_ARMTRONIX_DIMMERS + +#define XDRV_18 18 + +#ifndef ARMTRONIX_DIMMER_ID +#define ARMTRONIX_DIMMER_ID 0 +#endif + +#define ARMTRONIX_POWER_ID 1 + +#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 +\*********************************************************************************************/ + + + +boolean ArmtronixSetPower() +{ + boolean status = false; + + uint8_t rpower = XdrvMailbox.index; + int16_t source = XdrvMailbox.payload; + + if (source != SRC_SWITCH && ArmtronixSerial) { // ignore to prevent loop from pushing state from faceplate interaction + + snprintf_P(log_data, sizeof(log_data), PSTR("ARM: SetDevicePower.rpower=%d"), rpower); + AddLog(LOG_LEVEL_DEBUG); + //ArmtronixSendBool(ARMTRONIX_POWER_ID, rpower); + + status = true; + } + return status; +} + + +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 (id=%d)"), armtronix_dimState[0],armtronix_dimState[1], Settings.param[P_ARMTRONIX_DIMMER_ID]); + 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"); + + } +} + +void ArmtronixResetWifi() +{ + if (!Settings.flag.button_restrict) { + char scmnd[20]; + snprintf_P(scmnd, sizeof(scmnd), D_CMND_WIFICONFIG " %d", 2); + ExecuteCommand(scmnd, SRC_BUTTON); + } +} + +/*********************************************************************************************\ + * API Functions +\*********************************************************************************************/ + +boolean ArmtronixModuleSelected() +{ + if (!(pin[GPIO_ARMTRONIX_RX] < 99) || !(pin[GPIO_ARMTRONIX_TX] < 99)) { // fallback to hardware-serial if not explicitly selected + pin[GPIO_ARMTRONIX_TX] = 1; + pin[GPIO_ARMTRONIX_RX] = 3; + Settings.my_gp.io[1] = GPIO_ARMTRONIX_TX; + Settings.my_gp.io[3] = GPIO_ARMTRONIX_RX; + restart_flag = 2; + } + light_type = LT_SERIAL2; + return true; +} + +void ArmtronixInit() +{ + armtronix_dimState[0] = -1; + armtronix_dimState[1] = -1; + armtronix_knobState[0] = -1; + armtronix_knobState[1] = -1; + if (!Settings.param[P_ARMTRONIX_DIMMER_ID]) { + Settings.param[P_ARMTRONIX_DIMMER_ID] = ARMTRONIX_DIMMER_ID; + } + ArmtronixSerial = new TasmotaSerial(pin[GPIO_ARMTRONIX_RX], pin[GPIO_ARMTRONIX_TX], 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(); + } + } +} + +boolean ArmtronixButtonPressed() +{ + if (!XdrvMailbox.index && ((PRESSED == XdrvMailbox.payload) && (NOT_PRESSED == lastbutton[XdrvMailbox.index]))) { + snprintf_P(log_data, sizeof(log_data), PSTR("ARM: Reset GPIO triggered")); + AddLog(LOG_LEVEL_DEBUG); + ArmtronixResetWifi(); + return true; // Reset GPIO served here + } + return false; // Don't serve other buttons +} + +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 +\*********************************************************************************************/ +bool flip; + +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_SET_DEVICE_POWER: + result = ArmtronixSetPower(); + break; + case FUNC_BUTTON_PRESSED: + result = ArmtronixButtonPressed(); + break; + case FUNC_EVERY_SECOND: + if(ArmtronixSerial){ + flip = !flip; + if (armtronix_wifi_state!=WifiState()) { ArmtronixSetWifiLed(); } + if(flip){ + ArmtronixSerial->println("Status"); + } + } + break; + } + } + return result; +} + +#endif // USE_ARMTRONIX_DIMMERS