diff --git a/ConfigFile.cpp b/ConfigFile.cpp index 7af00b3..7dbfb60 100644 --- a/ConfigFile.cpp +++ b/ConfigFile.cpp @@ -6,9 +6,9 @@ extern Preferences pref; -#define SHADE_HDR_VER 2 +#define SHADE_HDR_VER 5 #define SHADE_HDR_SIZE 16 -#define SHADE_REC_SIZE 180 +#define SHADE_REC_SIZE 222 bool ConfigFile::begin(const char* filename, bool readOnly) { this->file = LittleFS.open(filename, readOnly ? "r" : "w"); @@ -281,7 +281,11 @@ bool ShadeConfigFile::loadFile(SomfyShadeController *s, const char *filename) { shade->shadeType = static_cast(this->readUInt8(0)); shade->setRemoteAddress(this->readUInt32(0)); this->readString(shade->name, sizeof(shade->name)); - shade->hasTilt = this->readBool(false); + if(this->header.version < 3) + shade->tiltType = this->readBool(false) ? tilt_types::none : tilt_types::tiltmotor; + else + shade->tiltType = static_cast(this->readUInt8(0)); + if(this->header.version > 1) { shade->bitLength = this->readUInt8(56); } @@ -292,16 +296,32 @@ bool ShadeConfigFile::loadFile(SomfyShadeController *s, const char *filename) { SomfyLinkedRemote *rem = &shade->linkedRemotes[j]; rem->setRemoteAddress(this->readUInt32(0)); if(rem->getRemoteAddress() != 0) rem->lastRollingCode = pref.getUShort(rem->getRemotePrefId(), 0); + if(this->header.version < 5 && j == 4) break; // Prior to version 5 we only supported 5 linked remotes. } shade->lastRollingCode = this->readUInt16(0); if(shade->getRemoteAddress() != 0) shade->lastRollingCode = max(pref.getUShort(shade->getRemotePrefId(), shade->lastRollingCode), shade->lastRollingCode); - shade->myPos = this->readUInt8(255); + if(this->header.version < 4) + shade->myPos = static_cast(this->readUInt8(255)); + else { + shade->myPos = this->readFloat(-1); + shade->myTiltPos = this->readFloat(-1); + } + if(shade->myPos > 100 || shade->myPos < 0) shade->myPos = -1; + if(shade->myTiltPos > 100 || shade->myTiltPos < 0) shade->myTiltPos = -1; shade->currentPos = this->readFloat(0); shade->currentTiltPos = this->readFloat(0); - shade->tiltPosition = (uint8_t)floor(shade->currentTiltPos * 100); - shade->position = (uint8_t)floor(shade->currentPos * 100); - shade->target = shade->position; - shade->tiltTarget = shade->tiltPosition; + if(shade->tiltType == tilt_types::none || shade->shadeType != shade_types::blind) { + shade->myTiltPos = -1; + shade->currentTiltPos = 0; + shade->tiltType = tilt_types::none; + } + + if(this->header.version < 3) { + shade->currentPos = shade->currentPos * 100; + shade->currentTiltPos = shade->currentTiltPos * 100; + } + shade->target = floor(shade->currentPos); + shade->tiltTarget = floor(shade->currentTiltPos); } pref.end(); if(opened) { @@ -311,12 +331,17 @@ bool ShadeConfigFile::loadFile(SomfyShadeController *s, const char *filename) { return true; } bool ShadeConfigFile::writeShadeRecord(SomfyShade *shade) { + if(shade->tiltType == tilt_types::none || shade->shadeType != shade_types::blind) { + shade->myTiltPos = -1; + shade->currentTiltPos = 0; + shade->tiltType = tilt_types::none; + } this->writeUInt8(shade->getShadeId()); this->writeBool(shade->paired); this->writeUInt8(static_cast(shade->shadeType)); this->writeUInt32(shade->getRemoteAddress()); this->writeString(shade->name, sizeof(shade->name)); - this->writeBool(shade->hasTilt); + this->writeUInt8(static_cast(shade->tiltType)); this->writeUInt8(shade->bitLength); this->writeUInt32(shade->upTime); this->writeUInt32(shade->downTime); @@ -326,7 +351,8 @@ bool ShadeConfigFile::writeShadeRecord(SomfyShade *shade) { this->writeUInt32(rem->getRemoteAddress()); } this->writeUInt16(shade->lastRollingCode); - this->writeUInt8(shade->myPos); + this->writeFloat(shade->myPos, 5); + this->writeFloat(shade->myTiltPos, 5); this->writeFloat(shade->currentPos, 5); this->writeFloat(shade->currentTiltPos, 5, CFG_REC_END); return true; diff --git a/ConfigSettings.h b/ConfigSettings.h index a3063de..bbb1f51 100644 --- a/ConfigSettings.h +++ b/ConfigSettings.h @@ -3,7 +3,7 @@ #ifndef configsettings_h #define configsettings_h -#define FW_VERSION "v1.4.7" +#define FW_VERSION "v1.5.0" enum DeviceStatus { DS_OK = 0, DS_ERROR = 1, diff --git a/Network.cpp b/Network.cpp index 2308909..f533e71 100644 --- a/Network.cpp +++ b/Network.cpp @@ -368,6 +368,7 @@ bool Network::openSoftAP() { Serial.println(); Serial.println("Turning the HotSpot On"); WiFi.disconnect(true); + WiFi.hostname("ESPSomfy RTS"); WiFi.mode(WIFI_AP_STA); delay(100); WiFi.softAP("ESPSomfy RTS", ""); @@ -375,7 +376,7 @@ bool Network::openSoftAP() { Serial.println(); Serial.print("SoftAP IP: "); Serial.println(WiFi.softAPIP()); - pinMode(D0, INPUT_PULLUP); + //pinMode(D0, INPUT_PULLUP); long startTime = millis(); int c = 0; diff --git a/Somfy.cpp b/Somfy.cpp index 44bc042..a8dda8a 100644 --- a/Somfy.cpp +++ b/Somfy.cpp @@ -514,16 +514,29 @@ bool SomfyShade::unlinkRemote(uint32_t address) { } return false; } +bool SomfyShade::isAtTarget() { return this->currentPos == this->target && this->currentTiltPos == this->tiltTarget; } void SomfyShade::checkMovement() { + // We are checking movement for essentially 3 types of motors. + // If this is an integrated tilt we need to first tilt in the direction we are moving then move. We know + // what needs to be done by the tilt type. Set a tilt first flag to indicate whether we should be tilting or + // moving. If this is only a tilt action then the regular tilt action should operate fine. int8_t currDir = this->direction; - uint8_t currPos = this->position; int8_t currTiltDir = this->tiltDirection; - uint8_t currTiltPos = this->tiltPosition; + this->direction = this->currentPos == this->target ? 0 : this->currentPos > this->target ? -1 : 1; + bool tilt_first = this->tiltType == tilt_types::integrated && ((this->direction == -1 && this->currentTiltPos != 0.0f) || (this->direction == 1 && this->currentTiltPos != 100.0f)); + this->tiltDirection = this->currentTiltPos == this->tiltTarget ? 0 : this->currentTiltPos > this->tiltTarget ? -1 : 1; + if(tilt_first) { + this->tiltDirection = this->direction; + this->direction = 0; + } + else if(this->direction != 0) this->tiltDirection = 0; + uint8_t currPos = floor(this->currentPos); + uint8_t currTiltPos = floor(this->currentTiltPos); - if(this->direction > 0) { + if(!tilt_first && this->direction > 0) { if(this->downTime == 0) { this->direction = 0; - this->currentPos = 1; + this->currentPos = 100.0; } else { // The shade is moving down so we need to calculate its position through the down position. @@ -531,7 +544,7 @@ void SomfyShade::checkMovement() { // The starting posion is a float value from 0-1 that indicates how much the shade is open. So // if we take the starting position * the total down time then this will tell us how many ms it // has moved in the down position. - int32_t msFrom0 = (int32_t)floor(this->startPos * this->downTime); + int32_t msFrom0 = (int32_t)floor((this->startPos/100) * this->downTime); // So if the start position is .1 it is 10% closed so we have a 1000ms (1sec) of time to account for // before we add any more time. @@ -540,7 +553,7 @@ void SomfyShade::checkMovement() { // don't have any rounding errors make sure that it is not greater than the max down time. msFrom0 = min((int32_t)this->downTime, msFrom0); if(msFrom0 >= this->downTime) { - this->currentPos = 1.0; + this->currentPos = 100.0f; this->direction = 0; } else { @@ -548,30 +561,33 @@ void SomfyShade::checkMovement() { // a ratio of how much time has travelled over the total time to go 100%. // We should now have the number of ms it will take to reach the shade fully close. - this->currentPos = min(max((float)0.0, (float)msFrom0 / (float)this->downTime), (float)1.0); + this->currentPos = (min(max((float)0.0, (float)msFrom0 / (float)this->downTime), (float)1.0)) * 100; // If the current position is >= 1 then we are at the bottom of the shade. - if(this->currentPos >= 1) { + if(this->currentPos >= 100) { this->direction = 0; - this->currentPos = 1.0; + this->currentPos = 100.0; } } - this->position = floor(this->currentPos * 100); - if(this->seekingPos && this->position >= this->target) { - Serial.print("Stopping Shade:"); - Serial.print(this->name); - Serial.print(" at "); - Serial.print(this->position); - Serial.print("% target "); - Serial.print(this->target); - Serial.println("%"); - if(!this->seekingFixedPos) this->sendCommand(somfy_commands::My); - else this->direction = 0; - this->seekingPos = false; - this->seekingFixedPos = false; + } + if(this->currentPos >= this->target) { + this->currentPos = this->target; + // If we need to stop the shade do this before we indicate that we are + // not moving otherwise the my function will kick in. + if(this->settingPos) { + if(!isAtTarget()) { + // We now need to move the tilt to the position we requested. + this->moveToTiltTarget(this->tiltTarget); + } + else + SomfyRemote::sendCommand(somfy_commands::My); } + this->direction = 0; + this->tiltStart = millis(); + this->startTiltPos = this->currentTiltPos; + if(this->isAtTarget()) this->commitShadePosition(); } } - else if(this->direction < 0) { + else if(!tilt_first && this->direction < 0) { if(this->upTime == 0) { this->direction = 0; this->currentPos = 0; @@ -581,7 +597,7 @@ void SomfyShade::checkMovement() { // often move slower in the up position so since we are using a relative position the up time // can be calculated. // 10000ms from 100 to 0; - int32_t msFrom100 = (int32_t)this->upTime - (int32_t)floor(this->startPos * this->upTime); + int32_t msFrom100 = (int32_t)this->upTime - (int32_t)floor((this->startPos/100) * this->upTime); msFrom100 += (millis() - this->moveStart); msFrom100 = min((int32_t)this->upTime, msFrom100); if(msFrom100 >= this->upTime) { @@ -589,115 +605,131 @@ void SomfyShade::checkMovement() { this->direction = 0; } // We should now have the number of ms it will take to reach the shade fully open. - this->currentPos = (float)1.0 - min(max((float)0.0, (float)msFrom100 / (float)this->upTime), (float)1.0); + this->currentPos = ((float)1.0 - min(max((float)0.0, (float)msFrom100 / (float)this->upTime), (float)1.0)) * 100; // If we are at the top of the shade then set the movement to 0. if(this->currentPos <= 0.0) { this->direction = 0; this->currentPos = 0; } } - this->position = floor(this->currentPos * 100); - if(this->seekingPos && this->position <= this->target) { - Serial.print("Stopping Shade:"); - Serial.print(this->name); - Serial.print(" at "); - Serial.print(this->position); - Serial.print("% target "); - Serial.print(this->target); - Serial.println("%"); - if(!this->seekingFixedPos) this->sendCommand(somfy_commands::My); - else this->direction = 0; - this->seekingFixedPos = false; - this->seekingPos = false; + if(this->currentPos <= this->target) { + this->currentPos = this->target; + // If we need to stop the shade do this before we indicate that we are + // not moving otherwise the my function will kick in. + if(this->settingPos) { + if(!isAtTarget()) { + // We now need to move the tilt to the position we requested. + this->moveToTiltTarget(this->tiltTarget); + } + else + SomfyRemote::sendCommand(somfy_commands::My); + } + this->direction = 0; + this->tiltStart = millis(); + this->startTiltPos = this->currentTiltPos; + if(this->isAtTarget()) this->commitShadePosition(); } } if(this->tiltDirection > 0) { - int32_t msFrom0 = (int32_t)floor(this->startTiltPos * this->tiltTime); + if(tilt_first) this->moveStart = millis(); + int32_t msFrom0 = (int32_t)floor((this->startTiltPos/100) * this->tiltTime); msFrom0 += (millis() - this->tiltStart); msFrom0 = min((int32_t)this->tiltTime, msFrom0); if(msFrom0 >= this->tiltTime) { - this->currentTiltPos = 1.0; + this->currentTiltPos = 100.0f; this->tiltDirection = 0; } else { - this->currentTiltPos = min(max((float)0.0, (float)msFrom0 / (float)this->tiltTime), (float)1.0); - if(this->currentTiltPos >= 1) { + this->currentTiltPos = (min(max((float)0.0, (float)msFrom0 / (float)this->tiltTime), (float)1.0)) * 100; + if(this->currentTiltPos >= 100) { this->tiltDirection = 0; - this->currentTiltPos = 1.0; + this->currentTiltPos = 100.0f; } } - this->tiltPosition = floor(this->currentTiltPos * 100); - if(this->seekingTiltPos && this->tiltPosition >= this->tiltTarget) { - Serial.print("Stopping Shade Tilt:"); - Serial.print(this->name); - Serial.print(" at "); - Serial.print(this->tiltPosition); - Serial.print("% target "); - Serial.print(this->tiltTarget); - Serial.println("%"); - this->sendCommand(somfy_commands::My); + if(tilt_first) { + if(this->currentTiltPos >= 100.0f) { + this->currentTiltPos = 100.0f; + this->moveStart = millis(); + this->startPos = this->currentPos; + this->tiltDirection = 0; + } + } + else if(this->currentTiltPos >= this->tiltTarget) { + this->currentTiltPos = this->tiltTarget; + // If we need to stop the shade do this before we indicate that we are + // not moving otherwise the my function will kick in. + if(this->settingTiltPos) SomfyRemote::sendCommand(somfy_commands::My); this->tiltDirection = 0; - this->seekingTiltPos = false; + this->settingTiltPos = false; + if(this->isAtTarget()) this->commitShadePosition(); } } else if(this->tiltDirection < 0) { + if(tilt_first) this->moveStart = millis(); if(this->tiltTime == 0) { this->tiltDirection = 0; this->currentTiltPos = 0; } else { - int32_t msFrom100 = (int32_t)this->tiltTime - (int32_t)floor(this->startTiltPos * this->tiltTime); + int32_t msFrom100 = (int32_t)this->tiltTime - (int32_t)floor((this->startTiltPos/100) * this->tiltTime); msFrom100 += (millis() - this->tiltStart); msFrom100 = min((int32_t)this->tiltTime, msFrom100); if(msFrom100 >= this->tiltTime) { - this->currentTiltPos = 0.0; + this->currentTiltPos = 0.0f; this->tiltDirection = 0; } - this->currentTiltPos = (float)1.0 - min(max((float)0.0, (float)msFrom100 / (float)this->tiltTime), (float)1.0); + this->currentTiltPos = ((float)1.0 - min(max((float)0.0, (float)msFrom100 / (float)this->tiltTime), (float)1.0)) * 100; // If we are at the top of the shade then set the movement to 0. - if(this->currentTiltPos <= 0.0) { + if(this->currentTiltPos <= 0.0f) { this->tiltDirection = 0; - this->currentTiltPos = 0; + this->currentTiltPos = 0.0f; } } - this->tiltPosition = floor(this->currentTiltPos * 100); - if(this->seekingTiltPos && this->tiltPosition <= this->tiltTarget) { - Serial.print("Stopping Shade Tilt:"); - Serial.print(this->name); - Serial.print(" at "); - Serial.print(this->tiltPosition); - Serial.print("% target "); - Serial.print(this->tiltTarget); - Serial.println("%"); - this->sendCommand(somfy_commands::My); + if(tilt_first) { + if(this->currentTiltPos <= 0.0f) { + this->currentTiltPos = 0.0f; + this->moveStart = millis(); + this->startPos = this->currentPos; + this->tiltDirection = 0; + Serial.println("Processed tilt first UP"); + } + } + else if(this->currentTiltPos <= this->tiltTarget) { + this->currentTiltPos = this->tiltTarget; + // If we need to stop the shade do this before we indicate that we are + // not moving otherwise the my function will kick in. + if(this->settingTiltPos) SomfyRemote::sendCommand(somfy_commands::My); this->tiltDirection = 0; - this->seekingTiltPos = false; + this->settingTiltPos = false; + Serial.println("Stopping at tilt position"); + if(this->isAtTarget()) this->commitShadePosition(); } } - if(currDir != this->direction && this->direction == 0) { - this->commitShadePosition(); - if(this->settingMyPos) { - delay(200); - // Set this position before sending the command. If you don't the processFrame function - // will send the shade back to its original My position. - if(this->myPos == this->position) this->myPos = 255; - else this->myPos = this->position; - SomfyRemote::sendCommand(somfy_commands::My, SETMY_REPEATS); - this->settingMyPos = false; - this->seekingFixedPos = false; - this->commitMyPosition(); + if(this->settingMyPos && this->isAtTarget()) { + delay(200); + // Set this position before sending the command. If you don't the processFrame function + // will send the shade back to its original My position. + if(this->tiltType != tilt_types::none) { + if(this->myTiltPos == this->currentTiltPos && this->myPos == this->currentPos) this->myPos = this->myTiltPos = -1; + else { + this->myPos = this->currentPos; + this->myTiltPos = this->currentTiltPos; + } } - } - if(currTiltDir != this->tiltDirection && this->tiltDirection == 0) { - this->commitTiltPosition(); - } - if(currDir != this->direction || currPos != this->position || currTiltDir != this->tiltDirection || currTiltPos != this->tiltPosition) { - // We need to emit on the socket that our state has changed. - this->position = floor(this->currentPos * 100.0); - this->tiltPosition = floor(this->currentTiltPos * 100.0); + else { + this->myTiltPos = -1; + if(this->myPos == this->currentPos) this->myPos = -1; + else this->myPos = this->currentPos; + } + SomfyRemote::sendCommand(somfy_commands::My, SETMY_REPEATS); + this->settingMyPos = false; + this->commitMyPosition(); + this->emitState(); + } + else if(currDir != this->direction || currPos != floor(this->currentPos) || currTiltDir != this->tiltDirection || currTiltPos != floor(this->currentTiltPos)) { + // We need to emit on the socket that our state has changed. this->emitState(); } - } void SomfyShade::load() { char shadeKey[15]; @@ -732,14 +764,12 @@ void SomfyShade::load() { } this->setRemoteAddress(pref.getUInt("remoteAddress", 0)); this->currentPos = pref.getFloat("currentPos", 0); - this->position = (uint8_t)floor(this->currentPos * 100); - this->target = this->position; - this->myPos = pref.getUShort("myPos", this->myPos); - this->hasTilt = pref.getBool("hasTilt", false); + this->target = floor(this->currentPos); + this->myPos = static_cast(pref.getUShort("myPos", this->myPos)); + this->tiltType = pref.getBool("hasTilt", false) ? tilt_types::none : tilt_types::tiltmotor; this->shadeType = static_cast(pref.getChar("shadeType", static_cast(this->shadeType))); this->currentTiltPos = pref.getFloat("currentTiltPos", 0); - this->tiltPosition = (uint8_t)floor(this->currentTiltPos * 100); - this->tiltTarget = this->tiltPosition; + this->tiltTarget = floor(this->currentTiltPos); pref.getBytes("linkedAddr", linkedAddresses, sizeof(linkedAddresses)); pref.end(); Serial.print("shadeId:"); @@ -749,7 +779,7 @@ void SomfyShade::load() { Serial.print(" address:"); Serial.print(this->getRemoteAddress()); Serial.print(" position:"); - Serial.print(this->position); + Serial.print(this->currentPos); Serial.print(" myPos:"); Serial.println(this->myPos); pref.begin("ShadeCodes"); @@ -771,57 +801,61 @@ void SomfyShade::publish() { snprintf(topic, sizeof(topic), "shades/%u/remoteAddress", this->shadeId); mqtt.publish(topic, this->getRemoteAddress()); snprintf(topic, sizeof(topic), "shades/%u/position", this->shadeId); - mqtt.publish(topic, this->position); + mqtt.publish(topic, static_cast(floor(this->currentPos))); snprintf(topic, sizeof(topic), "shades/%u/direction", this->shadeId); mqtt.publish(topic, this->direction); snprintf(topic, sizeof(topic), "shades/%u/target", this->shadeId); - mqtt.publish(topic, this->target); + mqtt.publish(topic, static_cast(floor(this->target))); snprintf(topic, sizeof(topic), "shades/%u/lastRollingCode", this->shadeId); mqtt.publish(topic, this->lastRollingCode); snprintf(topic, sizeof(topic), "shades/%u/mypos", this->shadeId); - mqtt.publish(topic, this->hasTilt ? "true" : "false"); + mqtt.publish(topic, static_cast(floor(this->myPos))); snprintf(topic, sizeof(topic), "shades/%u/shadeType", this->shadeId); mqtt.publish(topic, static_cast(this->shadeType)); - if(this->hasTilt) { + snprintf(topic, sizeof(topic), "shades/%u/tiltType", this->shadeId); + mqtt.publish(topic, static_cast(this->tiltType)); + if(this->tiltType != tilt_types::none) { snprintf(topic, sizeof(topic), "shades/%u/tiltDirection", this->shadeId); mqtt.publish(topic, this->tiltDirection); snprintf(topic, sizeof(topic), "shades/%u/tiltPosition", this->shadeId); - mqtt.publish(topic, this->tiltPosition); + mqtt.publish(topic, static_cast(floor(this->currentTiltPos))); snprintf(topic, sizeof(topic), "shades/%u/tiltTarget", this->shadeId); - mqtt.publish(topic, this->tiltTarget); + mqtt.publish(topic, static_cast(floor(this->tiltTarget))); } } } void SomfyShade::emitState(const char *evt) { this->emitState(255, evt); } void SomfyShade::emitState(uint8_t num, const char *evt) { char buf[320]; - if(this->hasTilt) - snprintf(buf, sizeof(buf), "{\"shadeId\":%d,\"type\":%u,\"remoteAddress\":%d,\"name\":\"%s\",\"direction\":%d,\"position\":%d,\"target\":%d,\"mypos\":%d,\"hasTilt\":%s,\"tiltDirection\":%d,\"tiltTarget\":%d,\"tiltPosition\":%d}", - this->shadeId, static_cast(this->shadeType), this->getRemoteAddress(), this->name, this->direction, this->position, this->target, this->myPos, this->hasTilt ? "true" : "false", this->tiltDirection, this->tiltTarget, this->tiltPosition); + if(this->tiltType != tilt_types::none) + snprintf(buf, sizeof(buf), "{\"shadeId\":%d,\"type\":%u,\"remoteAddress\":%d,\"name\":\"%s\",\"direction\":%d,\"position\":%d,\"target\":%d,\"mypos\":%d,\"myTiltPos\":%d,\"tiltType\":%u,\"tiltDirection\":%d,\"tiltTarget\":%d,\"tiltPosition\":%d}", + this->shadeId, static_cast(this->shadeType), this->getRemoteAddress(), this->name, this->direction, static_cast(floor(this->currentPos)), static_cast(floor(this->target)), static_cast(floor(this->myPos)), static_cast(this->myTiltPos), static_cast(this->tiltType), this->tiltDirection, static_cast(floor(this->tiltTarget)), static_cast(floor(this->currentTiltPos))); else - snprintf(buf, sizeof(buf), "{\"shadeId\":%d,\"type\":%u,\"remoteAddress\":%d,\"name\":\"%s\",\"direction\":%d,\"position\":%d,\"target\":%d,\"mypos\":%d,\"hasTilt\":%s}", - this->shadeId, static_cast(this->shadeType), this->getRemoteAddress(), this->name, this->direction, this->position, this->target, this->myPos, this->hasTilt ? "true" : "false"); + snprintf(buf, sizeof(buf), "{\"shadeId\":%d,\"type\":%u,\"remoteAddress\":%d,\"name\":\"%s\",\"direction\":%d,\"position\":%d,\"target\":%d,\"mypos\":%d,\"tiltType\":%u}", + this->shadeId, static_cast(this->shadeType), this->getRemoteAddress(), this->name, this->direction, static_cast(floor(this->currentPos)), static_cast(floor(this->target)), static_cast(floor(this->myPos)), static_cast(this->tiltType)); if(num >= 255) sockEmit.sendToClients(evt, buf); else sockEmit.sendToClient(num, evt, buf); if(mqtt.connected()) { char topic[32]; snprintf(topic, sizeof(topic), "shades/%u/position", this->shadeId); - mqtt.publish(topic, this->position); + mqtt.publish(topic, static_cast(floor(this->currentPos))); snprintf(topic, sizeof(topic), "shades/%u/direction", this->shadeId); mqtt.publish(topic, this->direction); snprintf(topic, sizeof(topic), "shades/%u/target", this->shadeId); - mqtt.publish(topic, this->target); + mqtt.publish(topic, static_cast(floor(this->target))); snprintf(topic, sizeof(topic), "shades/%u/lastRollingCode", this->shadeId); mqtt.publish(topic, this->lastRollingCode); snprintf(topic, sizeof(topic), "shades/%u/mypos", this->shadeId); - mqtt.publish(topic, this->myPos); - snprintf(topic, sizeof(topic), "shades/%u/hasTilt", this->hasTilt); - mqtt.publish(topic, this->hasTilt ? "true" : "false"); - if(this->hasTilt) { + mqtt.publish(topic, static_cast(floor(this->myPos))); + snprintf(topic, sizeof(topic), "shades/%u/tiltType", this->shadeId); + mqtt.publish(topic, static_cast(this->tiltType)); + if(this->tiltType != tilt_types::none) { + snprintf(topic, sizeof(topic), "shades/%u/myTiltPos", this->shadeId); + mqtt.publish(topic, static_cast(floor(this->myTiltPos))); snprintf(topic, sizeof(topic), "shades/%u/tiltPosition", this->shadeId); - mqtt.publish(topic, this->tiltPosition); + mqtt.publish(topic, static_cast(floor(this->currentTiltPos))); snprintf(topic, sizeof(topic), "shades/%u/tiltTarget", this->shadeId); - mqtt.publish(topic, this->tiltTarget); + mqtt.publish(topic, static_cast(floor(this->tiltTarget))); } } } @@ -837,30 +871,25 @@ void SomfyShade::processWaitingFrame() { case somfy_commands::StepUp: this->lastFrame.processed = true; // Simply move the shade up by 1%. - if(this->position > 0) { - this->seekingFixedPos = true; - this->seekingPos = true; - this->target = this->position - 1; + if(this->currentPos > 0) { + this->target = floor(this->currentPos) - 1; this->setMovement(-1); } break; case somfy_commands::StepDown: this->lastFrame.processed = true; // Simply move the shade down by 1%. - if(this->position < 100) { - this->seekingFixedPos = true; - this->seekingPos = true; - this->target = this->position + 1; + if(this->currentPos < 100) { + this->target = floor(this->currentPos) + 1; this->setMovement(1); } break; case somfy_commands::Down: case somfy_commands::Up: - if(this->hasTilt) { // Theoretically this should get here unless it does have a tilt. + if(this->tiltType == tilt_types::tiltmotor) { // Theoretically this should get here unless it does have a tilt motor. if(this->lastFrame.repeats >= TILT_REPEATS) { int8_t dir = this->lastFrame.cmd == somfy_commands::Up ? -1 : 1; - this->seekingTiltPos = false; - this->tiltTarget = dir > 0 ? 100 : 0; + this->tiltTarget = dir > 0 ? 100.0f : 0.0f; this->setTiltMovement(dir); this->lastFrame.processed = true; Serial.print(this->name); @@ -872,7 +901,6 @@ void SomfyShade::processWaitingFrame() { } else { int8_t dir = this->lastFrame.cmd == somfy_commands::Up ? -1 : 1; - this->seekingPos = false; this->target = dir > 0 ? 100 : 0; this->setMovement(dir); this->lastFrame.processed = true; @@ -882,24 +910,29 @@ void SomfyShade::processWaitingFrame() { break; case somfy_commands::My: if(this->lastFrame.repeats >= SETMY_REPEATS && this->isIdle()) { - if(this->myPos == this->position) // We are clearing it. - this->myPos = 255; - else - this->myPos = this->position; + if(floor(this->myPos) == floor(this->currentPos)) { + // We are clearing it. + this->myPos = -1; + this->myTiltPos = -1; + } + else { + this->myPos = this->currentPos; + this->myTiltPos = this->currentTiltPos; + } this->commitMyPosition(); this->lastFrame.processed = true; this->emitState(); } - else if(this->myPos >= 0 && this->myPos <= 100) { - int8_t dir = 0; - if(myPos < this->position) dir = -1; - else if(myPos > this->position) dir = 1; - if(dir != 0) this->seekingFixedPos = true; - this->seekingPos = true; - this->target = this->myPos; - this->setMovement(dir); + else if(this->isIdle()) { + if(this->myPos >= 0.0f && this->myPos <= 100.0f) this->target = this->myPos; + if(this->myTiltPos >= 0.0f && this->myTiltPos <= 100.0f) this->tiltTarget = this->myTiltPos; + this->setMovement(0); this->lastFrame.processed = true; } + else { + this->target = this->currentPos; + this->tiltTarget = this->currentTiltPos; + } if(this->lastFrame.repeats > SETMY_REPEATS + 2) this->lastFrame.processed = true; if(this->lastFrame.processed) { Serial.print(this->name); @@ -912,6 +945,9 @@ void SomfyShade::processWaitingFrame() { } } void SomfyShade::processFrame(somfy_frame_t &frame, bool internal) { + // The reason why we are processing all frames here is so + // any linked remotes that may happen to be on the same ESPSomfy RTS + // device can trigger the appropriate actions. if(this->shadeId == 255) return; bool hasRemote = this->getRemoteAddress() == frame.remoteAddress; if(!hasRemote) { @@ -926,82 +962,64 @@ void SomfyShade::processFrame(somfy_frame_t &frame, bool internal) { if(!hasRemote) return; this->lastFrame.copy(frame); int8_t dir = 0; - // If the frame came from the radio it cannot be seeking a position. This means that the target will be set. - if(!internal) this->seekingTiltPos = this->seekingPos = false; - + int8_t tiltDir = 0; + this->moveStart = this->tiltStart = millis(); + this->startPos = this->currentPos; + this->startTiltPos = this->currentTiltPos; + // If the command is coming from a remote then we are aborting all these positioning operations. + if(!internal) this->settingMyPos = this->settingPos = this->settingTiltPos = false; + // At this point we are not processing the combo buttons // will need to see what the shade does when you press both. switch(frame.cmd) { case somfy_commands::Up: - if(this->hasTilt) { - // Wait another half seccond just in case we are potentially processing a tilt. + if(this->tiltType == tilt_types::tiltmotor) { + // Wait another half second just in case we are potentially processing a tilt. if(!internal) this->lastFrame.await = millis() + 500; - else if(this->lastFrame.repeats >= TILT_REPEATS) { - // This is an internal tilt command. - this->setTiltMovement(-1); - this->lastFrame.processed = true; - return; - } - else { - dir = -1; - if(!internal) this->target = 0; - this->lastFrame.processed = true; - } + else this->lastFrame.processed = true; } else { - dir = -1; - if(!internal) this->target = 0; + // If from a remote we will simply be going up. + if(!internal) this->target = this->tiltTarget = 0.0f; this->lastFrame.processed = true; } break; case somfy_commands::Down: - if(this->hasTilt) { + if(this->tiltType == tilt_types::tiltmotor) { // Wait another half seccond just in case we are potentially processing a tilt. if(!internal) this->lastFrame.await = millis() + 500; - else if(this->lastFrame.repeats >= TILT_REPEATS) { - // This is an internal tilt command. - this->setTiltMovement(1); - this->lastFrame.processed = true; - return; - } - else { - dir = 1; - if(!internal) this->target = 100; - this->lastFrame.processed = true; - } + else this->lastFrame.processed = true; } else { - dir = 1; - if(!internal) this->target = 100; this->lastFrame.processed = true; + if(!internal) { + this->target = 100.0f; + if(this->tiltType != tilt_types::none) this->tiltTarget = 100.0f; + } } break; case somfy_commands::My: - dir = 0; if(this->isIdle()) { if(!internal) { // This frame is coming from a remote. We are potentially setting // the my position. this->lastFrame.await = millis() + 500; } - else if(myPos >= 0 && this->myPos <= 100) { + else { this->lastFrame.processed = true; - // In this instance we will be moving to the my position. This - // will be up or down or not. - if(this->myPos < this->position) dir = -1; - else if(this->myPos > this->position) dir = 1; - if(dir != 0) { - Serial.println("Start moving to My Position"); - this->seekingFixedPos = true; + if(!internal) { + if(this->myTiltPos >= 0.0f && this->myTiltPos >= 100.0f) this->tiltTarget = this->myTiltPos; + if(this->myPos >= 0.0f && this->myPos <= 100.0f) this->target = this->myPos; } - this->seekingPos = true; - this->target = this->myPos; } } - else - // This will occur if the shade needs to be stopped or there - // is no my position set. + else { this->lastFrame.processed = true; + if(!internal) { + this->target = this->currentPos; + this->tiltTarget = this->currentTiltPos; + } + } break; case somfy_commands::StepUp: if(!internal) { @@ -1010,10 +1028,8 @@ void SomfyShade::processFrame(somfy_frame_t &frame, bool internal) { else { this->lastFrame.processed = true; // Simply move the shade up by 1%. - if(this->position > 0) { - this->seekingFixedPos = true; - this->seekingPos = true; - this->target = this->position - 1; + if(this->currentPos > 0) { + this->target = this->currentPos - 0.5f; dir = -1; } } @@ -1025,10 +1041,8 @@ void SomfyShade::processFrame(somfy_frame_t &frame, bool internal) { else { this->lastFrame.processed = true; // Simply move the shade down by 1%. - if(this->position < 100) { - this->seekingFixedPos = true; - this->seekingPos = true; - this->target = this->position + 1; + if(this->currentPos < 100) { + this->target = this->currentPos + 0.5f; dir = 1; } } @@ -1037,7 +1051,7 @@ void SomfyShade::processFrame(somfy_frame_t &frame, bool internal) { dir = 0; break; } - if(dir == 0 && this->hasTilt && this->tiltDirection != 0) this->setTiltMovement(0); + //if(dir == 0 && this->tiltType == tilt_types::tiltmotor && this->tiltDirection != 0) this->setTiltMovement(0); this->setMovement(dir); } void SomfyShade::setTiltMovement(int8_t dir) { @@ -1051,154 +1065,187 @@ void SomfyShade::setTiltMovement(int8_t dir) { this->commitTiltPosition(); } } - else if(this->direction != dir) { + else if(this->tiltDirection != dir) { this->tiltStart = millis(); this->startTiltPos = this->currentTiltPos; this->tiltDirection = dir; } if(this->tiltDirection != currDir) { - this->tiltPosition = floor(this->currentTiltPos * 100.0); this->emitState(); } } void SomfyShade::setMovement(int8_t dir) { int8_t currDir = this->direction; + int8_t currTiltDir = this->tiltDirection; if(dir == 0) { - // The shade is stopped. - this->startPos = this->currentPos; - this->moveStart = 0; - this->direction = dir; - if(currDir != dir) { - this->commitShadePosition(); - } + if(currDir != dir || currTiltDir != dir) this->commitShadePosition(); } - else if(this->direction != dir) { - this->moveStart = millis(); + else { + this->tiltStart = this->moveStart = millis(); this->startPos = this->currentPos; - this->direction = dir; + this->startTiltPos = this->currentTiltPos; } - if(this->direction != currDir) { - this->position = floor(this->currentPos * 100.0); + if(this->direction != currDir || currTiltDir != this->tiltDirection) { this->emitState(); } } -void SomfyShade::setMyPosition(uint8_t target) { +void SomfyShade::setMyPosition(int8_t pos, int8_t tilt) { if(this->direction != 0) return; // Don't do this if it is moving. - Serial.println("setMyPosition called"); - if(target != this->position) { - this->settingMyPos = true; - Serial.println("Moving to set My Position"); - if(target == this->myPos) - // Setting the My Position to the same position will toggle it off. So lets send a my - // command instead of an up/down. This will ensure we get the thing cleared. - this->moveToMyPosition(); - else - this->moveToTarget(target); + if(this->tiltType != tilt_types::none) { + if(tilt < 0) tilt = 0; + if(pos != floor(this->currentPos) || tilt != floor(currentTiltPos)) { + this->settingMyPos = true; + if(pos == floor(this->myPos) && tilt == floor(this->myTiltPos)) + this->moveToMyPosition(); + else + this->moveToTarget(pos, tilt); + } + else if(pos == floor(this->myPos) && tilt == floor(this->myTiltPos)) { + SomfyRemote::sendCommand(somfy_commands::My, SETMY_REPEATS); + this->myPos = this->myTiltPos = -1; + this->commitMyPosition(); + this->emitState(); + } + else { + this->myPos = this->currentPos; + this->myTiltPos = this->currentTiltPos; + } + this->commitMyPosition(); + this->emitState(); } else { - this->sendCommand(somfy_commands::My, SETMY_REPEATS); - if(target == this->myPos) - this->myPos = 255; - else - this->myPos = target; - this->commitMyPosition(); - this->emitState(); + if(pos != floor(this->currentPos)) { + this->settingMyPos = true; + if(pos == floor(this->myPos)) + this->moveToMyPosition(); + else + this->moveToTarget(pos); + } + else if(pos == floor(this->myPos)) { + SomfyRemote::sendCommand(somfy_commands::My, SETMY_REPEATS); + this->myPos = this->myTiltPos = -1; + this->commitMyPosition(); + this->emitState(); + } + else { + this->myPos = currentPos; + this->myTiltPos = -1; + this->commitMyPosition(); + this->emitState(); + } } } void SomfyShade::moveToMyPosition() { - if(this->direction != 0 || this->myPos > 100 || this->myPos < 0) return; - if(this->position == this->myPos) return; // Nothing to see here since we are already here. + if(!this->isIdle()) return; + if(this->currentPos == this->myPos) return; // Nothing to see here since we are already here. Serial.print("Seeking my Position:"); Serial.print(this->myPos); - Serial.println("%"); - this->seekingFixedPos = true; - this->target = this->myPos; + Serial.print("% "); + this->target = floor(this->myPos); Serial.print("Moving to "); Serial.print(this->target); Serial.print("% from "); - Serial.print(this->position); + Serial.print(this->currentPos); Serial.print("% using "); - Serial.println(translateSomfyCommand(somfy_commands::My)); - this->seekingPos = true; + Serial.print(translateSomfyCommand(somfy_commands::My)); + Serial.println(this->direction); + if(this->myPos >= 0.0f && this->myPos <= 100.0f) this->target = this->myPos; + if(this->myTiltPos >= 0.0f && this->myTiltPos <= 100.0f) this->tiltTarget = this->myTiltPos; SomfyRemote::sendCommand(somfy_commands::My); } void SomfyShade::sendCommand(somfy_commands cmd, uint8_t repeat) { + // This sendCommand function will always be called externally. sendCommand at the remote level + // is expected to be called internally when the motor needs commanded. if(this->bitLength == 0) this->bitLength = somfy.transceiver.config.type; if(cmd == somfy_commands::Up) { - this->target = 0; - this->seekingPos = false; + SomfyRemote::sendCommand(cmd, repeat); + this->target = 0.0f; + if(this->tiltType == tilt_types::integrated) this->tiltTarget = 0.0f; } else if(cmd == somfy_commands::Down) { - this->target = 100; - this->seekingPos = false; + SomfyRemote::sendCommand(cmd, repeat); + this->target = 100.0f; + if(this->tiltType == tilt_types::integrated) this->tiltTarget = 100.0f; } else if(cmd == somfy_commands::My) { - if(this->isIdle() && this->myPos >= 0 && this->myPos <= 100) { + if(this->isIdle()) { this->moveToMyPosition(); return; } else { - this->target = this->position; - this->seekingPos = false; + SomfyRemote::sendCommand(cmd, repeat); + this->target = this->currentPos; + this->tiltTarget = this->currentTiltPos; } } - SomfyRemote::sendCommand(cmd, repeat); } void SomfyShade::sendTiltCommand(somfy_commands cmd) { if(cmd == somfy_commands::Up) { - this->tiltTarget = 0; - this->seekingTiltPos = false; - SomfyRemote::sendCommand(cmd, TILT_REPEATS); + SomfyRemote::sendCommand(cmd, this->tiltType == tilt_types::tiltmotor ? TILT_REPEATS : 1); + this->tiltTarget = 0.0f; } else if(cmd == somfy_commands::Down) { - this->tiltTarget = 100; - this->seekingTiltPos = false; - SomfyRemote::sendCommand(cmd, TILT_REPEATS); + SomfyRemote::sendCommand(cmd, this->tiltType == tilt_types::tiltmotor ? TILT_REPEATS : 1); + this->tiltTarget = 100.0f; } else if(cmd == somfy_commands::My) { - this->tiltTarget = this->tiltPosition; - this->seekingTiltPos = false; - SomfyRemote::sendCommand(cmd); + SomfyRemote::sendCommand(cmd, this->tiltType == tilt_types::tiltmotor ? TILT_REPEATS : 1); + this->tiltTarget = this->currentTiltPos; } } -void SomfyShade::moveToTiltTarget(uint8_t target) { +void SomfyShade::moveToTiltTarget(float target) { int8_t newDir = 0; somfy_commands cmd = somfy_commands::My; - if(target < this->tiltPosition) + if(target < this->currentTiltPos) cmd = somfy_commands::Up; - else if(target > this->tiltPosition) + else if(target > this->currentTiltPos) cmd = somfy_commands::Down; - Serial.print("Moving Tilt to "); - Serial.print(target); - Serial.print("% from "); - Serial.print(this->tiltPosition); - Serial.print("% using "); - Serial.println(translateSomfyCommand(cmd)); - this->tiltTarget = target; - if(target > 0 && target < 100) this->seekingTiltPos = true; - else this->seekingTiltPos = false; - if(cmd != somfy_commands::My) - SomfyRemote::sendCommand(cmd, TILT_REPEATS); - else - SomfyRemote::sendCommand(cmd); + if(target > 0.0f && target < 100.0f) { + if(cmd != somfy_commands::My) { + Serial.print("Moving Tilt to "); + Serial.print(target); + Serial.print("% from "); + Serial.print(this->currentTiltPos); + Serial.print("% using "); + Serial.println(translateSomfyCommand(cmd)); + SomfyRemote::sendCommand(cmd, this->tiltType == tilt_types::tiltmotor ? TILT_REPEATS : 1); + } + else + SomfyRemote::sendCommand(cmd); + this->tiltTarget = target; + } + this->settingTiltPos = true; } -void SomfyShade::moveToTarget(uint8_t target) { +void SomfyShade::moveToTarget(float pos, float tilt) { int8_t newDir = 0; somfy_commands cmd = somfy_commands::My; - if(target < this->position) + if(pos < this->currentPos) cmd = somfy_commands::Up; - else if(target > this->position) + else if(pos > this->currentPos) + cmd = somfy_commands::Down; + else if(tilt >= 0 && tilt < this->currentTiltPos) + cmd = somfy_commands::Up; + else if(tilt >= 0 && tilt > this->currentTiltPos) cmd = somfy_commands::Down; Serial.print("Moving to "); - Serial.print(target); + Serial.print(pos); Serial.print("% from "); - Serial.print(this->position); + Serial.print(this->currentPos); + if(tilt >= 0) { + Serial.print(" tilt "); + Serial.print(tilt); + Serial.print("% from "); + Serial.print(this->currentTiltPos); + } Serial.print("% using "); Serial.println(translateSomfyCommand(cmd)); - this->target = target; - if(target > 0 && target != 100) this->seekingPos = true; - else this->seekingPos = false; - SomfyRemote::sendCommand(cmd); + SomfyRemote::sendCommand(cmd); + this->target = pos; + this->settingPos = true; + if(tilt >= 0) { + this->tiltTarget = tilt; + this->settingTiltPos = true; + } } bool SomfyShade::save() { if(somfy.useNVS()) { @@ -1209,7 +1256,7 @@ bool SomfyShade::save() { pref.putChar("shadeType", static_cast(this->shadeType)); pref.putUInt("remoteAddress", this->getRemoteAddress()); pref.putString("name", this->name); - pref.putBool("hasTilt", this->hasTilt); + pref.putBool("hasTilt", this->tiltType != tilt_types::none); pref.putBool("paired", this->paired); pref.putUInt("upTime", this->upTime); pref.putUInt("downTime", this->downTime); @@ -1237,7 +1284,7 @@ bool SomfyShade::fromJSON(JsonObject &obj) { if(obj.containsKey("downTime")) this->downTime = obj["downTime"]; if(obj.containsKey("remoteAddress")) this->setRemoteAddress(obj["remoteAddress"]); if(obj.containsKey("tiltTime")) this->tiltTime = obj["tiltTime"]; - if(obj.containsKey("hasTilt")) this->hasTilt = obj["hasTilt"]; + if(obj.containsKey("hasTilt")) this->tiltType = static_cast(obj["hasTilt"]) ? tilt_types::none : tilt_types::tiltmotor; if(obj.containsKey("bitLength")) this->bitLength = obj["bitLength"]; if(obj.containsKey("shadeType")) { if(obj["shadeType"].is()) { @@ -1252,6 +1299,21 @@ bool SomfyShade::fromJSON(JsonObject &obj) { this->shadeType = static_cast(obj["shadeType"].as()); } } + if(obj.containsKey("tiltType")) { + if(obj["tiltType"].is()) { + if(strncmp(obj["tiltType"].as(), "none", 4) == 0) + this->tiltType = tilt_types::none; + else if(strncmp(obj["tiltType"].as(), "tiltmotor", 9) == 0) + this->tiltType = tilt_types::tiltmotor; + else if(strncmp(obj["tiltType"].as(), "integ", 5) == 0) + this->tiltType = tilt_types::integrated; + else if(strncmp(obj["tiltType"].as(), "tiltonly", 8) == 0) + this->tiltType = tilt_types::tiltonly; + } + else { + this->tiltType = static_cast(obj["tiltType"].as()); + } + } if(obj.containsKey("linkedAddresses")) { uint32_t linkedAddresses[SOMFY_MAX_LINKED_REMOTES]; memset(linkedAddresses, 0x00, sizeof(linkedAddresses)); @@ -1277,17 +1339,18 @@ bool SomfyShade::toJSON(JsonObject &obj) { obj["upTime"] = this->upTime; obj["downTime"] = this->downTime; obj["paired"] = this->paired; - obj["remotePrefId"] = this->getRemotePrefId(); + //obj["remotePrefId"] = this->getRemotePrefId(); obj["lastRollingCode"] = this->lastRollingCode; - obj["position"] = this->position; - obj["tiltPosition"] = this->tiltPosition; + obj["position"] = static_cast(floor(this->currentPos)); + obj["tiltPosition"] = static_cast(floor(this->currentTiltPos)); obj["tiltDirection"] = this->tiltDirection; obj["tiltTime"] = this->tiltTime; - obj["tiltTarget"] = this->tiltTarget; + obj["tiltTarget"] = static_cast(floor(this->tiltTarget)); obj["target"] = this->target; - obj["myPos"] = this->myPos; + obj["myPos"] = static_cast(floor(this->myPos)); + obj["myTiltPos"] = static_cast(floor(this->myTiltPos)); obj["direction"] = this->direction; - obj["hasTilt"] = this->hasTilt; + obj["tiltType"] = static_cast(this->tiltType); obj["tiltTime"] = this->tiltTime; obj["shadeType"] = static_cast(this->shadeType); obj["bitLength"] = this->bitLength; @@ -1303,7 +1366,7 @@ bool SomfyShade::toJSON(JsonObject &obj) { return true; } bool SomfyRemote::toJSON(JsonObject &obj) { - obj["remotePrefId"] = this->getRemotePrefId(); + //obj["remotePrefId"] = this->getRemotePrefId(); obj["remoteAddress"] = this->getRemoteAddress(); obj["lastRollingCode"] = this->lastRollingCode; return true; @@ -1598,8 +1661,8 @@ static const uint32_t tempo_wakeup_silence_min = 89565 * TOLERANCE_MIN; static const uint32_t tempo_wakeup_silence_max = 89565 * TOLERANCE_MAX; static const uint32_t tempo_synchro_hw_min = SYMBOL * 4 * TOLERANCE_MIN; static const uint32_t tempo_synchro_hw_max = SYMBOL * 4 * TOLERANCE_MAX; -static const uint32_t tempo_synchro_sw_min = 4550 * TOLERANCE_MIN; -static const uint32_t tempo_synchro_sw_max = 4550 * TOLERANCE_MAX; +static const uint32_t tempo_synchro_sw_min = 4850 * TOLERANCE_MIN; +static const uint32_t tempo_synchro_sw_max = 4850 * TOLERANCE_MAX; static const uint32_t tempo_half_symbol_min = SYMBOL * TOLERANCE_MIN; static const uint32_t tempo_half_symbol_max = SYMBOL * TOLERANCE_MAX; static const uint32_t tempo_symbol_min = SYMBOL * 2 * TOLERANCE_MIN; diff --git a/Somfy.h b/Somfy.h index ea1aa66..122384b 100644 --- a/Somfy.h +++ b/Somfy.h @@ -2,7 +2,7 @@ #define SOMFY_H #define SOMFY_MAX_SHADES 32 -#define SOMFY_MAX_LINKED_REMOTES 5 +#define SOMFY_MAX_LINKED_REMOTES 7 typedef struct appver_t { uint8_t major; @@ -35,7 +35,12 @@ enum class shade_types : byte { blind = 0x01, drapery = 0x02 }; - +enum class tilt_types : byte { + none = 0x00, + tiltmotor = 0x01, + integrated = 0x02, + tiltonly = 0x03, +}; String translateSomfyCommand(const somfy_commands cmd); somfy_commands translateSomfyCommand(const String& string); @@ -97,7 +102,7 @@ class SomfyRemote { // confirmed. The address is actually 24bits // and the rolling code is 16 bits. protected: - char m_remotePrefId[10] = ""; + char m_remotePrefId[11] = ""; uint32_t m_remoteAddress = 0; public: uint8_t bitLength = 0; @@ -119,28 +124,26 @@ class SomfyShade : public SomfyRemote { uint8_t shadeId = 255; uint64_t moveStart = 0; uint64_t tiltStart = 0; - float startPos = 0.0; - float startTiltPos = 0.00; - bool seekingPos = false; - bool seekingTiltPos = false; - bool seekingFixedPos = false; + float startPos = 0.0f; + float startTiltPos = 0.0f; bool settingMyPos = false; + bool settingPos = false; + bool settingTiltPos = false; uint32_t awaitMy = 0; public: shade_types shadeType = shade_types::roller; - bool hasTilt = false; + tilt_types tiltType = tilt_types::none; void load(); somfy_frame_t lastFrame; - float currentPos = 0.0; - float currentTiltPos = 0.0; + float currentPos = 0.0f; + float currentTiltPos = 0.0f; //uint16_t movement = 0; int8_t direction = 0; // 0 = stopped, 1=down, -1=up. int8_t tiltDirection = 0; // 0=stopped, 1=clockwise, -1=counter clockwise - uint8_t tiltPosition = 0; - uint8_t position = 0; - uint8_t target = 0; - uint8_t tiltTarget = 0; - uint8_t myPos = 255; + float target = 0.0; + float tiltTarget = 0.0; + float myPos = -1.0; + float myTiltPos = -1.0; SomfyLinkedRemote linkedRemotes[SOMFY_MAX_LINKED_REMOTES]; bool paired = false; bool fromJSON(JsonObject &obj); @@ -157,16 +160,17 @@ class SomfyShade : public SomfyRemote { void processFrame(somfy_frame_t &frame, bool internal = false); void setTiltMovement(int8_t dir); void setMovement(int8_t dir); - void setTarget(uint8_t target); - void moveToTarget(uint8_t target); - void moveToTiltTarget(uint8_t target); + void setTarget(float target); + bool isAtTarget(); + void moveToTarget(float pos, float tilt = -1.0f); + void moveToTiltTarget(float target); void sendTiltCommand(somfy_commands cmd); void sendCommand(somfy_commands cmd, uint8_t repeat = 1); bool linkRemote(uint32_t remoteAddress, uint16_t rollingCode = 0); bool unlinkRemote(uint32_t remoteAddress); void emitState(const char *evt = "shadeState"); void emitState(uint8_t num, const char *evt = "shadeState"); - void setMyPosition(uint8_t target); + void setMyPosition(int8_t pos, int8_t tilt = -1); void moveToMyPosition(); void processWaitingFrame(); void publish(); diff --git a/SomfyController.ino.esp32.bin b/SomfyController.ino.esp32.bin index 4a6362b..e60d6d1 100644 Binary files a/SomfyController.ino.esp32.bin and b/SomfyController.ino.esp32.bin differ diff --git a/SomfyController.littlefs.bin b/SomfyController.littlefs.bin index b0416c6..e6997cd 100644 Binary files a/SomfyController.littlefs.bin and b/SomfyController.littlefs.bin differ diff --git a/Utils.h b/Utils.h index 3ac30b9..a386875 100644 --- a/Utils.h +++ b/Utils.h @@ -1,15 +1,6 @@ #ifndef utils_h #define utils_h -#ifndef LED_BUILTIN - #define LED_BUILTIN 2 -#endif -#ifndef D3 - #define D3 3 -#endif -#ifndef D0 - #define D0 0 -#endif #define DEBUG_SOMFY Serial diff --git a/Web.cpp b/Web.cpp index 59a7f96..433e9ec 100644 --- a/Web.cpp +++ b/Web.cpp @@ -541,7 +541,7 @@ void Web::begin() { int shadeId = atoi(server.arg("shadeId").c_str()); SomfyShade* shade = somfy.getShadeById(shadeId); if (shade) { - DynamicJsonDocument doc(512); + DynamicJsonDocument doc(2048); JsonObject obj = doc.to(); shade->toJSON(obj); serializeJson(doc, g_content); @@ -579,7 +579,7 @@ void Web::begin() { if (shade) { shade->fromJSON(obj); shade->save(); - DynamicJsonDocument sdoc(512); + DynamicJsonDocument sdoc(2048); JsonObject sobj = sdoc.to(); shade->toJSON(sobj); serializeJson(sdoc, g_content); @@ -771,12 +771,13 @@ void Web::begin() { webServer.sendCORSHeaders(); HTTPMethod method = server.method(); uint8_t shadeId = 255; - uint8_t target = 255; - + int8_t pos = -1; + int8_t tilt = -1; if (method == HTTP_GET || method == HTTP_PUT || method == HTTP_POST) { if (server.hasArg("shadeId")) { shadeId = atoi(server.arg("shadeId").c_str()); - if(server.hasArg("target")) target = atoi(server.arg("target").c_str()); + if(server.hasArg("pos")) pos = atoi(server.arg("pos").c_str()); + if(server.hasArg("tilt")) tilt = atoi(server.arg("tilt").c_str()); } else if (server.hasArg("plain")) { DynamicJsonDocument doc(256); @@ -799,9 +800,8 @@ void Web::begin() { JsonObject obj = doc.as(); if (obj.containsKey("shadeId")) shadeId = obj["shadeId"]; else server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"No shade id was supplied.\"}")); - if(obj.containsKey("target")) { - target = obj["target"].as(); - } + if(obj.containsKey("pos")) pos = obj["pos"].as(); + if(obj.containsKey("tilt")) tilt = obj["tilt"].as(); } } else server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"No shade object supplied.\"}")); @@ -809,9 +809,9 @@ void Web::begin() { SomfyShade* shade = somfy.getShadeById(shadeId); if (shade) { // Send the command to the shade. - if(target == 255) target = shade->myPos; - if(target >= 0 && target <= 100) - shade->setMyPosition(target); + if(tilt < 0) tilt = shade->myPos; + if(pos >= 0 && pos <= 100) + shade->setMyPosition(pos, tilt); DynamicJsonDocument sdoc(512); JsonObject sobj = sdoc.to(); shade->toJSON(sobj); diff --git a/data/appversion b/data/appversion index b000a6a..3e1ad72 100644 --- a/data/appversion +++ b/data/appversion @@ -1 +1 @@ -1.4.7 \ No newline at end of file +1.5.0 \ No newline at end of file diff --git a/data/index.html b/data/index.html index afcb726..f89f6bd 100644 --- a/data/index.html +++ b/data/index.html @@ -3,10 +3,10 @@ - - + + - +
@@ -263,8 +263,12 @@