|
@ -0,0 +1,198 @@ |
|
|
|
|
|
/*
|
|
|
|
|
|
|
|
|
|
|
|
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<char*>(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
|