mirror of
https://github.com/rstrouse/ESPSomfy-RTS.git
synced 2026-09-20 16:42:11 +02:00
Merge d3fc68e2f7 into eb75868adb
This commit is contained in:
commit
01c0f32ddc
15 changed files with 4563 additions and 2903 deletions
20
.github/workflows/ci.yaml
vendored
20
.github/workflows/ci.yaml
vendored
|
|
@ -22,17 +22,17 @@ jobs:
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Check out code
|
- name: Check out code
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Checkout mklittlefs
|
- name: Checkout mklittlefs
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
repository: earlephilhower/mklittlefs
|
repository: earlephilhower/mklittlefs
|
||||||
path: mklittlefs
|
path: mklittlefs
|
||||||
ref: ${{ env.MKLITTLEFS_VERSION }}
|
ref: ${{ env.MKLITTLEFS_VERSION }}
|
||||||
|
|
||||||
- name: Checkout LittleFS
|
- name: Checkout LittleFS
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
repository: littlefs-project/littlefs
|
repository: littlefs-project/littlefs
|
||||||
path: mklittlefs/littlefs
|
path: mklittlefs/littlefs
|
||||||
|
|
@ -44,10 +44,10 @@ jobs:
|
||||||
|
|
||||||
- name: Create LittleFS
|
- name: Create LittleFS
|
||||||
run: |
|
run: |
|
||||||
./mklittlefs/mklittlefs --create data --size 1441792 SomfyController.littlefs.bin
|
./mklittlefs/mklittlefs --create data --size 786432 SomfyController.littlefs.bin
|
||||||
|
|
||||||
- name: Upload binaries
|
- name: Upload binaries
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: LittleFS
|
name: LittleFS
|
||||||
path: SomfyController.littlefs.bin
|
path: SomfyController.littlefs.bin
|
||||||
|
|
@ -85,17 +85,17 @@ jobs:
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Check out code
|
- name: Check out code
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
path: SomfyController
|
path: SomfyController
|
||||||
|
|
||||||
- name: Get LittleFS
|
- name: Get LittleFS
|
||||||
uses: actions/download-artifact@v3
|
uses: actions/download-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: LittleFS
|
name: LittleFS
|
||||||
|
|
||||||
- name: Install Python ${{ env.PYTHON_VERSION }}
|
- name: Install Python ${{ env.PYTHON_VERSION }}
|
||||||
uses: actions/setup-python@v4
|
uses: actions/setup-python@v5
|
||||||
with:
|
with:
|
||||||
python-version: ${{ env.PYTHON_VERSION }}
|
python-version: ${{ env.PYTHON_VERSION }}
|
||||||
|
|
||||||
|
|
@ -137,10 +137,10 @@ jobs:
|
||||||
${{ matrix.addr_bootloader }} build/SomfyController.ino.bootloader.bin \
|
${{ matrix.addr_bootloader }} build/SomfyController.ino.bootloader.bin \
|
||||||
0x8000 build/SomfyController.ino.partitions.bin \
|
0x8000 build/SomfyController.ino.partitions.bin \
|
||||||
0x10000 build/SomfyController.ino.bin \
|
0x10000 build/SomfyController.ino.bin \
|
||||||
0x290000 SomfyController.littlefs.bin
|
0x330000 SomfyController.littlefs.bin
|
||||||
|
|
||||||
- name: Upload ${{ matrix.name }}
|
- name: Upload ${{ matrix.name }}
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: ${{ matrix.name }}
|
name: ${{ matrix.name }}
|
||||||
path: |
|
path: |
|
||||||
|
|
|
||||||
4
.github/workflows/release.yaml
vendored
4
.github/workflows/release.yaml
vendored
|
|
@ -52,7 +52,7 @@ jobs:
|
||||||
|
|
||||||
- name: Create LittleFS
|
- name: Create LittleFS
|
||||||
run: |
|
run: |
|
||||||
./mklittlefs/mklittlefs --create data --size 1441792 SomfyController.littlefs.bin
|
./mklittlefs/mklittlefs --create data --size 786432 SomfyController.littlefs.bin
|
||||||
|
|
||||||
- name: Upload binaries
|
- name: Upload binaries
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
|
|
@ -172,7 +172,7 @@ jobs:
|
||||||
${{ matrix.addr_bootloader }} build${{ matrix.name }}/SomfyController.ino.bootloader.bin \
|
${{ matrix.addr_bootloader }} build${{ matrix.name }}/SomfyController.ino.bootloader.bin \
|
||||||
0x8000 build${{ matrix.name }}/SomfyController.ino.partitions.bin \
|
0x8000 build${{ matrix.name }}/SomfyController.ino.partitions.bin \
|
||||||
0x10000 build${{ matrix.name }}/SomfyController.ino.bin \
|
0x10000 build${{ matrix.name }}/SomfyController.ino.bin \
|
||||||
0x290000 SomfyController.littlefs.bin
|
0x330000 SomfyController.littlefs.bin
|
||||||
|
|
||||||
- name: Upload Firmware ${{ matrix.name }}
|
- name: Upload Firmware ${{ matrix.name }}
|
||||||
uses: shogo82148/actions-upload-release-asset@v1.7.5
|
uses: shogo82148/actions-upload-release-asset@v1.7.5
|
||||||
|
|
|
||||||
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
|
||||||
856
data/index.html
856
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;
|
||||||
|
|
|
||||||
7
partitions.csv
Normal file
7
partitions.csv
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
# Name, Type, SubType, Offset, Size, Flags
|
||||||
|
nvs, data, nvs, 0x9000, 0x5000,
|
||||||
|
otadata, data, ota, 0xe000, 0x2000,
|
||||||
|
app0, app, ota_0, 0x10000, 0x190000,
|
||||||
|
app1, app, ota_1, 0x1A0000,0x190000,
|
||||||
|
spiffs, data, spiffs, 0x330000,0xC0000,
|
||||||
|
coredump, data, coredump,0x3F0000,0x10000,
|
||||||
|
60
platformio.ini
Normal file
60
platformio.ini
Normal file
|
|
@ -0,0 +1,60 @@
|
||||||
|
; PlatformIO configuration for ESPSomfy-RTS
|
||||||
|
; Compatible with Arduino IDE (source files remain in project root)
|
||||||
|
|
||||||
|
[platformio]
|
||||||
|
src_dir = .
|
||||||
|
default_envs = esp32
|
||||||
|
|
||||||
|
[env]
|
||||||
|
framework = arduino
|
||||||
|
platform = espressif32@6.9.0
|
||||||
|
monitor_speed = 115200
|
||||||
|
upload_speed = 921600
|
||||||
|
board_build.filesystem = littlefs
|
||||||
|
board_build.flash_mode = qio
|
||||||
|
board_build.partitions = partitions.csv
|
||||||
|
lib_deps =
|
||||||
|
bblanchon/ArduinoJson@6.21.5
|
||||||
|
knolleary/PubSubClient@2.8.0
|
||||||
|
LSatan/SmartRC-CC1101-Driver-Lib@2.5.7
|
||||||
|
https://github.com/Links2004/arduinoWebSockets.git#2.4.0
|
||||||
|
build_src_filter =
|
||||||
|
+<*.ino>
|
||||||
|
+<*.cpp>
|
||||||
|
+<*.h>
|
||||||
|
-<.github/>
|
||||||
|
-<icons/>
|
||||||
|
|
||||||
|
[env:esp32]
|
||||||
|
board = esp32dev
|
||||||
|
|
||||||
|
[env:esp32c3]
|
||||||
|
board = esp32-c3-devkitc-02
|
||||||
|
board_build.mcu = esp32c3
|
||||||
|
build_flags =
|
||||||
|
-DARDUINO_USB_MODE=1
|
||||||
|
-DARDUINO_USB_CDC_ON_BOOT=1
|
||||||
|
|
||||||
|
[env:esp32s2]
|
||||||
|
board = esp32-s2-saola-1
|
||||||
|
board_build.mcu = esp32s2
|
||||||
|
build_flags =
|
||||||
|
-DARDUINO_USB_CDC_ON_BOOT=1
|
||||||
|
|
||||||
|
; ESP32-C6 requires Arduino ESP32 core 3.x (pioarduino platform fork).
|
||||||
|
; Core 3.x has breaking API changes (ETH, tcpip_adapter removed, etc.)
|
||||||
|
; that require source code modifications before this will compile.
|
||||||
|
; [env:esp32c6]
|
||||||
|
; platform = https://github.com/pioarduino/platform-espressif32/releases/download/53.03.11/platform-espressif32.zip
|
||||||
|
; board = esp32-c6-devkitc-1
|
||||||
|
; board_build.mcu = esp32c6
|
||||||
|
; build_flags =
|
||||||
|
; -DARDUINO_USB_MODE=1
|
||||||
|
; -DARDUINO_USB_CDC_ON_BOOT=1
|
||||||
|
|
||||||
|
[env:esp32s3]
|
||||||
|
board = esp32-s3-devkitc-1
|
||||||
|
board_build.mcu = esp32s3
|
||||||
|
build_flags =
|
||||||
|
-DARDUINO_USB_MODE=1
|
||||||
|
-DARDUINO_USB_CDC_ON_BOOT=1
|
||||||
Loading…
Add table
Add a link
Reference in a new issue