/* KingArt Cover/Shutter/Blind/Curtain support for ESPURNA Based on xdrv_19_ps16dz.dimmer.ino, PS_16_DZ dimmer support for Tasmota Copyright (C) 2019 by Albert Weterings */ #include "curtain_kingart.h" #if KINGART_CURTAIN_SUPPORT #include "ntp.h" #include "mqtt.h" #ifndef KINGART_CURTAIN_PORT #define KINGART_CURTAIN_PORT Serial // Hardware serial port by default #endif #ifndef KINGART_CURTAIN_BUFFER_SIZE #define KINGART_CURTAIN_BUFFER_SIZE 100 // Local UART buffer size #endif #define KINGART_CURTAIN_TERMINATION '\e' // Termination character after each message #define KINGART_CURTAIN_BAUDRATE 19200 // Serial speed is fixed for the device char _KACurtainBuffer[KINGART_CURTAIN_BUFFER_SIZE]; bool _KACurtainNewData = false; // ----------------------------------------------------------------------------- // Private // ----------------------------------------------------------------------------- void _KACurtainSend(const char * tx_buffer) { KINGART_CURTAIN_PORT.print(tx_buffer); KINGART_CURTAIN_PORT.print(KINGART_CURTAIN_TERMINATION); KINGART_CURTAIN_PORT.flush(); } void _KACurtainReceiveUART() { static unsigned char ndx = 0; while (KINGART_CURTAIN_PORT.available() > 0 && !_KACurtainNewData) { char rc = KINGART_CURTAIN_PORT.read(); if (rc != KINGART_CURTAIN_TERMINATION) { _KACurtainBuffer[ndx] = rc; if (ndx < KINGART_CURTAIN_BUFFER_SIZE - 1) ndx++; } else { _KACurtainBuffer[ndx] = '\0'; _KACurtainNewData = true; ndx = 0; } } } /* Buttons on the device will move Cover/Shutter/Blind/Curtain up/open or down/close On the end of every movement the unit reports the last action and posiston over MQTT topic {hostname}/curtain RAW paylod format looks like: AT+UPDATE="switch":"on","setclose":13 AT+UPDATE="switch":"off","setclose":38 AT+UPDATE="switch":"pause","setclose":75 The device is listening to MQTT topic {hostname}/curtain/set, to which you can send: - position value, in range from 0 to 100 - "pause", to stop the movement. The device will determine the direction all by itself. # Set the Cover / Shutter / Blind / Curtain run time The factory default Open and Close run time for the switch is 50 seconds, and it must be set to an accurate run time for smooth working. Some motors do not have the resistance stop function, so when the Cover/Shutter/Blind/Curtain track open or close to the maximum length, but the motor keeps on running. This might cause damage on the motor and the switch, it also wastes a lot of energy. In order to protect the motor, this switch designed with a time setting function. After setting up the run time, the switch will automaticly stop when the track reaches its limits. The run time setting is also helpful for the accurate control when manually controlling the device via the touch panel. After installing the switch and connecting the switch for the very first time: - First, it will automatically close the Cover/Shutter/Blind/Curtain to the maximum. - Press and hold the touch interface pause button for around 4 seconds until the red background led lights up and starts blinking. Then, press the open touch button so start the opening process. - When cover is fully open, press the Open or Close button to stop the timer and save the calculated run time. To configure the device: - Press up/down for 5 seconds to bring device into AP mode. After pressing up/down again, device will restart in normal mode. */ void _KACurtainResult() { if (_KACurtainNewData) { // Need to send confiramtion to the N76E003AT20 that message is received _KACurtainSend("AT+SEND=ok"); // We don't handle "setclose" any other way, simply redirect payload value const String buffer(_KACurtainBuffer); #if MQTT_SUPPORT int setclose_idx = buffer.indexOf("setclose"); if (setclose_idx > 0) { auto position = buffer.substring(setclose_idx + strlen("setclose") + 2, buffer.length()); int leftovers = position.indexOf(','); if (leftovers > 0) { position = position.substring(0, leftovers); } mqttSend(MQTT_TOPIC_CURTAIN, position.c_str()); } #endif // MQTT_SUPPORT // Handle configuration button presses if (buffer.indexOf("enterESPTOUCH") > 0) { wifiStartAP(); } else if (buffer.indexOf("exitESPTOUCH") > 0) { deferredReset(100, CUSTOM_RESET_HARDWARE); } _KACurtainNewData = false; } } // %d = now() / time_t / NTP timestamp in seconds // %03u = millis() / uint32_t / we need last 3 digits // %s = char strings for various actions // Tell N76E003AT20 to stop moving and report current position void _KACurtainPause(const char * message) { char tx_buffer[80] = {0}; snprintf_P( tx_buffer, sizeof(tx_buffer), PSTR("AT+UPDATE=\"sequence\":\"%d%03u\",\"switch\":\"%s\""), now(), millis() % 1000, message ); _KACurtainSend(tx_buffer); } // Tell N76E003AT20 to go to position X (based on X N76E003AT20 decides to go up or down) void _KACurtainSetclose(const char * message) { char tx_buffer[80] = {0}; snprintf_P( tx_buffer, sizeof(tx_buffer), PSTR("AT+UPDATE=\"sequence\":\"%d%03u\",\"switch\":\"%s\",\"setclose\":%s"), now(), millis() % 1000, "off", message ); _KACurtainSend(tx_buffer); } #if MQTT_SUPPORT void _KACurtainActionSelect(const char * message) { if (strcmp(message, "pause") == 0) { _KACurtainPause(message); } else { _KACurtainSetclose(message); } } void _KACurtainCallback(unsigned int type, const char * topic, char * payload) { if (type == MQTT_CONNECT_EVENT) { mqttSubscribe(MQTT_TOPIC_CURTAIN); } if (type == MQTT_MESSAGE_EVENT) { // Match topic const String t = mqttMagnitude(const_cast(topic)); if (t.equals(MQTT_TOPIC_CURTAIN)) { _KACurtainActionSelect(payload); } } } #endif // MQTT_SUPPORT // ----------------------------------------------------------------------------- // SETUP & LOOP // ----------------------------------------------------------------------------- void _KACurtainLoop() { _KACurtainReceiveUART(); _KACurtainResult(); } void kingartCurtainSetup() { // Init port to receive and send messages KINGART_CURTAIN_PORT.begin(KINGART_CURTAIN_BAUDRATE); // Register MQTT callback only when supported #if MQTT_SUPPORT mqttRegister(_KACurtainCallback); #endif // MQTT_SUPPORT // Register loop to poll the UART for new messages espurnaRegisterLoop(_KACurtainLoop); } #endif // KINGART_CURTAIN_SUPPORT