mirror of
https://github.com/rstrouse/ESPSomfy-RTS.git
synced 2026-07-15 07:12:14 +02:00
Merge c7592162ec into eb75868adb
This commit is contained in:
commit
95ad4967aa
11 changed files with 4484 additions and 2891 deletions
13
.gitignore
vendored
13
.gitignore
vendored
|
|
@ -8,3 +8,16 @@ SomfyController.ino.XIAO_ESP32S3.bin
|
||||||
SomfyController.ino.esp32c3.bin
|
SomfyController.ino.esp32c3.bin
|
||||||
SomfyController.ino.esp32s2.bin
|
SomfyController.ino.esp32s2.bin
|
||||||
.vscode/settings.json
|
.vscode/settings.json
|
||||||
|
.pio/
|
||||||
|
.generated_files/
|
||||||
|
build/
|
||||||
|
dist/
|
||||||
|
nbproject/
|
||||||
|
Makefile*
|
||||||
|
Package-*
|
||||||
|
private/
|
||||||
|
_build/
|
||||||
|
!cmake/**/user.cmake
|
||||||
|
cmake/**/.generated/*
|
||||||
|
cmake/**/CMake*
|
||||||
|
out/
|
||||||
|
|
@ -7,9 +7,9 @@
|
||||||
|
|
||||||
extern Preferences pref;
|
extern Preferences pref;
|
||||||
|
|
||||||
#define SHADE_HDR_VER 24
|
#define SHADE_HDR_VER 25
|
||||||
#define SHADE_HDR_SIZE 76
|
#define SHADE_HDR_SIZE 76
|
||||||
#define SHADE_REC_SIZE 276
|
#define SHADE_REC_SIZE 320
|
||||||
#define GROUP_REC_SIZE 200
|
#define GROUP_REC_SIZE 200
|
||||||
#define TRANS_REC_SIZE 74
|
#define TRANS_REC_SIZE 74
|
||||||
#define ROOM_REC_SIZE 29
|
#define ROOM_REC_SIZE 29
|
||||||
|
|
@ -861,6 +861,12 @@ bool ShadeConfigFile::readShadeRecord(SomfyShade *shade) {
|
||||||
if(shade->proto == radio_proto::GP_Remote)
|
if(shade->proto == radio_proto::GP_Remote)
|
||||||
pinMode(shade->gpioMy, OUTPUT);
|
pinMode(shade->gpioMy, OUTPUT);
|
||||||
if(this->header.version >= 19) shade->roomId = this->readUInt8(0);
|
if(this->header.version >= 19) shade->roomId = this->readUInt8(0);
|
||||||
|
if(this->header.version >= 25) {
|
||||||
|
shade->upMidTime = this->readUInt32(0);
|
||||||
|
shade->downMidTime = this->readUInt32(0);
|
||||||
|
shade->descendTime = this->readUInt32(0);
|
||||||
|
shade->upSlatTime = this->readUInt32(0);
|
||||||
|
}
|
||||||
if(this->file.position() != startPos + this->header.shadeRecordSize) {
|
if(this->file.position() != startPos + this->header.shadeRecordSize) {
|
||||||
Serial.println("Reading to end of shade record");
|
Serial.println("Reading to end of shade record");
|
||||||
this->seekChar(CFG_REC_END);
|
this->seekChar(CFG_REC_END);
|
||||||
|
|
@ -997,7 +1003,11 @@ bool ShadeConfigFile::writeShadeRecord(SomfyShade *shade) {
|
||||||
this->writeUInt8(shade->gpioDown);
|
this->writeUInt8(shade->gpioDown);
|
||||||
this->writeUInt8(shade->gpioMy);
|
this->writeUInt8(shade->gpioMy);
|
||||||
this->writeUInt8(shade->gpioFlags);
|
this->writeUInt8(shade->gpioFlags);
|
||||||
this->writeUInt8(shade->roomId, CFG_REC_END);
|
this->writeUInt8(shade->roomId);
|
||||||
|
this->writeUInt32(shade->upMidTime);
|
||||||
|
this->writeUInt32(shade->downMidTime);
|
||||||
|
this->writeUInt32(shade->descendTime);
|
||||||
|
this->writeUInt32(shade->upSlatTime, CFG_REC_END);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
bool ShadeConfigFile::writeSettingsRecord() {
|
bool ShadeConfigFile::writeSettingsRecord() {
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
#ifndef configsettings_h
|
#ifndef configsettings_h
|
||||||
#define configsettings_h
|
#define configsettings_h
|
||||||
#include "WResp.h"
|
#include "WResp.h"
|
||||||
#define FW_VERSION "v2.4.7"
|
#define FW_VERSION "v3.0.0"
|
||||||
enum class conn_types_t : byte {
|
enum class conn_types_t : byte {
|
||||||
unset = 0x00,
|
unset = 0x00,
|
||||||
wifi = 0x01,
|
wifi = 0x01,
|
||||||
|
|
|
||||||
43
README.md
43
README.md
|
|
@ -69,6 +69,49 @@ While the interface that comes with the ESPSomfy RTS is a huge improvement, the
|
||||||
|
|
||||||
You can find the documentation for the interfaces in the [Integrations](https://github.com/rstrouse/ESPSomfy-RTS/wiki/Integrations) wiki. Plenty of stuff there for you folks that make red nodes and stuff.
|
You can find the documentation for the interfaces in the [Integrations](https://github.com/rstrouse/ESPSomfy-RTS/wiki/Integrations) wiki. Plenty of stuff there for you folks that make red nodes and stuff.
|
||||||
|
|
||||||
|
## Accurate Position Estimation for Roller Shades
|
||||||
|
|
||||||
|
### The problem
|
||||||
|
|
||||||
|
ESPSomfy RTS estimates shade position by interpolating elapsed travel time against the configured Up/Down time. This works well for simple motors, but roller shades wind their material around a drum. As the shade travels, the drum radius grows (when opening) or shrinks (when closing), so the linear speed of the fabric changes even when the motor turns at a constant angular speed. The result is that the reported percentage drifts noticeably from the real position — the shade is at 50% in Home Assistant but clearly not in the middle of the window.
|
||||||
|
|
||||||
|
### Non-linear calibration fields
|
||||||
|
|
||||||
|
Two optional fields can be set per shade to correct for this:
|
||||||
|
|
||||||
|
| Field | Description |
|
||||||
|
|---|---|
|
||||||
|
| **Up mid-time** | Elapsed time (ms) from fully closed until the shade reaches the 50% open position |
|
||||||
|
| **Down mid-time** | Elapsed time (ms) from fully open until the shade reaches the 50% closed position |
|
||||||
|
|
||||||
|
When either field is non-zero, ESPSomfy RTS fits a logarithmic model (α ratio solved via Newton's method) to the measured mid-point. All position estimates then use this model instead of linear interpolation. Set both fields to 0 to revert to the original linear behaviour.
|
||||||
|
|
||||||
|
**How to measure:** start a full travel, watch the shade, and record the time at which it crosses the halfway point of the window opening. A stopwatch is sufficient.
|
||||||
|
|
||||||
|
### Two-phase model for roller shutters with blade tilt
|
||||||
|
|
||||||
|
Roller shutters that tilt their blades go through two distinct motion phases:
|
||||||
|
|
||||||
|
- **Closing:** the shade descends (blades unrolling) then the blades tilt to fully close — a short linear phase at the end
|
||||||
|
- **Opening:** the blades open first (linear phase), then the shade ascends
|
||||||
|
|
||||||
|
Two additional fields model these phases:
|
||||||
|
|
||||||
|
| Field | Description |
|
||||||
|
|---|---|
|
||||||
|
| **Descend time** | Elapsed time (ms) from fully open until the shade touches the floor (blades horizontal, before tilt) |
|
||||||
|
| **Up slat time** | Elapsed time (ms) from fully closed until the blades are fully expanded (before the shade starts to rise) |
|
||||||
|
|
||||||
|
The non-linear model is applied only to the pure travel portion (descent or ascent), excluding the linear blade-tilt phases at each end. As with the mid-time fields, set to 0 to disable.
|
||||||
|
|
||||||
|
**How to measure:** start a full travel from the opposite end-stop and watch for the physical transition — bottom of shade touching the sill (for descend time) or blades visibly fully open (for up slat time). Both can be measured with a stopwatch from movement start.
|
||||||
|
|
||||||
|
### The special 1% position
|
||||||
|
|
||||||
|
The boundary between the two phases — blades fully expanded so that light passes between them, shade resting on the floor — is represented internally as **1%**. Positions from 100% down to 1% correspond to the blade-tilt range; positions from 1% down to 0% correspond to the shade travel range. A consequence of this is that there is no intermediate state between blades fully expanded (letting some light through) and blades fully closed (in contact with each other, no light): the shade is either in the tilt zone (≥ 1%) or in the travel zone (< 1%). In practice this is not a real-world limitation, since stopping the blades at a partially-expanded position is rarely useful, but it is worth being aware of.
|
||||||
|
|
||||||
|
All four fields are persisted to NVS, included in the backup file, and are restored on firmware update.
|
||||||
|
|
||||||
## Sources for this Project
|
## Sources for this Project
|
||||||
I spent some time reading about a myriad of topics but in the end the primary source for this project comes from https://pushstack.wordpress.com/somfy-rts-protocol/. The work done on pushstack regarding the protocol timing made this feasible without burning a bunch of time measuring pulses.
|
I spent some time reading about a myriad of topics but in the end the primary source for this project comes from https://pushstack.wordpress.com/somfy-rts-protocol/. The work done on pushstack regarding the protocol timing made this feasible without burning a bunch of time measuring pulses.
|
||||||
|
|
||||||
|
|
|
||||||
7
Somfy.h
7
Somfy.h
|
|
@ -314,6 +314,13 @@ class SomfyShade : public SomfyRemote {
|
||||||
uint32_t upTime = 10000;
|
uint32_t upTime = 10000;
|
||||||
uint32_t downTime = 10000;
|
uint32_t downTime = 10000;
|
||||||
uint32_t tiltTime = 7000;
|
uint32_t tiltTime = 7000;
|
||||||
|
uint32_t upMidTime = 0; // Timestamp (ms from fully closed) at 50% when opening; 0 = linear
|
||||||
|
uint32_t downMidTime = 0; // Timestamp (ms from fully open) at 50% when closing; 0 = linear
|
||||||
|
uint32_t descendTime = 0; // Timestamp (ms from fully open) when slats touch floor, blades expanded; 0 = disabled
|
||||||
|
uint32_t upSlatTime = 0; // Timestamp (ms from fully closed) when blades are expanded during opening; 0 = disabled
|
||||||
|
float alphaUp = 0.0f; // Derived from (upMidTime-upSlatTime)/(upTime-upSlatTime); 0 = use linear
|
||||||
|
float alphaDown = 0.0f; // Derived from downMidTime/descendTime; 0 = use linear
|
||||||
|
void computeAlphas();
|
||||||
uint16_t stepSize = 100;
|
uint16_t stepSize = 100;
|
||||||
bool save();
|
bool save();
|
||||||
bool isIdle();
|
bool isIdle();
|
||||||
|
|
|
||||||
Binary file not shown.
BIN
SomfyController.ino.esp32s3.littlefs.bin
Normal file
BIN
SomfyController.ino.esp32s3.littlefs.bin
Normal file
Binary file not shown.
|
|
@ -1 +1 @@
|
||||||
2.4.7
|
3.0.0
|
||||||
864
data/index.html
864
data/index.html
File diff suppressed because it is too large
Load diff
|
|
@ -1270,7 +1270,7 @@ var security = new Security();
|
||||||
|
|
||||||
class General {
|
class General {
|
||||||
initialized = false;
|
initialized = false;
|
||||||
appVersion = 'v2.4.7';
|
appVersion = 'v3.0.0';
|
||||||
reloadApp = false;
|
reloadApp = false;
|
||||||
init() {
|
init() {
|
||||||
if (this.initialized) return;
|
if (this.initialized) return;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue