mirror of
https://github.com/rstrouse/ESPSomfy-RTS.git
synced 2025-12-12 18:42:10 +01:00
Add support for RTW protocols #38
This commit is contained in:
parent
ac6d6ee34d
commit
65e4caf3b2
10 changed files with 106 additions and 34 deletions
|
|
@ -6,9 +6,9 @@
|
|||
|
||||
extern Preferences pref;
|
||||
|
||||
#define SHADE_HDR_VER 6
|
||||
#define SHADE_HDR_VER 7
|
||||
#define SHADE_HDR_SIZE 16
|
||||
#define SHADE_REC_SIZE 228
|
||||
#define SHADE_REC_SIZE 232
|
||||
|
||||
bool ConfigFile::begin(const char* filename, bool readOnly) {
|
||||
this->file = LittleFS.open(filename, readOnly ? "r" : "w");
|
||||
|
|
@ -285,7 +285,9 @@ bool ShadeConfigFile::loadFile(SomfyShadeController *s, const char *filename) {
|
|||
shade->tiltType = this->readBool(false) ? tilt_types::none : tilt_types::tiltmotor;
|
||||
else
|
||||
shade->tiltType = static_cast<tilt_types>(this->readUInt8(0));
|
||||
|
||||
if(this->header.version > 6) {
|
||||
shade->proto = static_cast<radio_proto>(this->readUInt8(0));
|
||||
}
|
||||
if(this->header.version > 1) {
|
||||
shade->bitLength = this->readUInt8(56);
|
||||
}
|
||||
|
|
@ -345,6 +347,7 @@ bool ShadeConfigFile::writeShadeRecord(SomfyShade *shade) {
|
|||
this->writeUInt32(shade->getRemoteAddress());
|
||||
this->writeString(shade->name, sizeof(shade->name));
|
||||
this->writeUInt8(static_cast<uint8_t>(shade->tiltType));
|
||||
this->writeUInt8(static_cast<uint8_t>(shade->proto));
|
||||
this->writeUInt8(shade->bitLength);
|
||||
this->writeUInt32(shade->upTime);
|
||||
this->writeUInt32(shade->downTime);
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
#ifndef configsettings_h
|
||||
#define configsettings_h
|
||||
|
||||
#define FW_VERSION "v1.5.4"
|
||||
#define FW_VERSION "v1.6.0beta"
|
||||
enum DeviceStatus {
|
||||
DS_OK = 0,
|
||||
DS_ERROR = 1,
|
||||
|
|
|
|||
80
Somfy.cpp
80
Somfy.cpp
|
|
@ -120,6 +120,23 @@ void somfy_frame_t::decodeFrame(byte* frame) {
|
|||
// Pull in the data for an 80-bit step command.
|
||||
if(this->cmd == somfy_commands::StepDown)
|
||||
this->cmd = (somfy_commands)((decoded[1] >> 4) | ((decoded[8] & 0x08) << 4));
|
||||
if(this->cmd == somfy_commands::RTWProto) {
|
||||
this->proto = radio_proto::RTW;
|
||||
switch(this->encKey) {
|
||||
case 140:
|
||||
this->cmd = somfy_commands::Prog;
|
||||
break;
|
||||
case 134:
|
||||
this->cmd = somfy_commands::Up;
|
||||
break;
|
||||
case 133:
|
||||
this->cmd = somfy_commands::My;
|
||||
break;
|
||||
case 136:
|
||||
this->cmd = somfy_commands::Down;
|
||||
break;
|
||||
}
|
||||
}
|
||||
this->rollingCode = decoded[3] + (decoded[2] << 8);
|
||||
this->remoteAddress = (decoded[6] + (decoded[5] << 8) + (decoded[4] << 16));
|
||||
this->valid = this->checksum == checksum && this->remoteAddress > 0 && this->remoteAddress < 16777215 && this->rollingCode > 0;
|
||||
|
|
@ -141,7 +158,7 @@ void somfy_frame_t::decodeFrame(byte* frame) {
|
|||
case somfy_commands::UnknownC:
|
||||
case somfy_commands::UnknownD:
|
||||
case somfy_commands::UnknownE:
|
||||
case somfy_commands::UnknownF:
|
||||
case somfy_commands::RTWProto:
|
||||
this->valid = false;
|
||||
break;
|
||||
case somfy_commands::StepUp:
|
||||
|
|
@ -223,22 +240,42 @@ void somfy_frame_t::encodeFrame(byte *frame) {
|
|||
frame[7] = 132;
|
||||
frame[8] = 0;
|
||||
frame[9] = 29;
|
||||
switch(this->cmd) {
|
||||
case somfy_commands::StepUp:
|
||||
frame[7] = 132;
|
||||
frame[8] = 56;
|
||||
frame[9] = 22;
|
||||
break;
|
||||
case somfy_commands::StepDown:
|
||||
frame[7] = 132;
|
||||
frame[8] = 48;
|
||||
frame[9] = 30;
|
||||
break;
|
||||
case somfy_commands::Prog:
|
||||
frame[7] = 196;
|
||||
frame[8] = 0;
|
||||
frame[9] = 25;
|
||||
break;
|
||||
// Ok so if this is an RTW things are a bit different.
|
||||
if(this->proto == radio_proto::RTW) {
|
||||
frame[1] = 0xF0;
|
||||
switch(this->cmd) {
|
||||
case somfy_commands::Prog:
|
||||
frame[0] = 140;
|
||||
break;
|
||||
case somfy_commands::Up:
|
||||
frame[0] = 134;
|
||||
break;
|
||||
case somfy_commands::Down:
|
||||
frame[0] = 136;
|
||||
break;
|
||||
case somfy_commands::My:
|
||||
frame[0] = 133;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
switch(this->cmd) {
|
||||
case somfy_commands::StepUp:
|
||||
frame[7] = 132;
|
||||
frame[8] = 56;
|
||||
frame[9] = 22;
|
||||
break;
|
||||
case somfy_commands::StepDown:
|
||||
frame[7] = 132;
|
||||
frame[8] = 48;
|
||||
frame[9] = 30;
|
||||
break;
|
||||
case somfy_commands::Prog:
|
||||
frame[7] = 196;
|
||||
frame[8] = 0;
|
||||
frame[9] = 25;
|
||||
break;
|
||||
}
|
||||
}
|
||||
byte checksum = 0;
|
||||
|
||||
|
|
@ -1348,6 +1385,7 @@ bool SomfyShade::fromJSON(JsonObject &obj) {
|
|||
if(obj.containsKey("stepSize")) this->stepSize = obj["stepSize"];
|
||||
if(obj.containsKey("hasTilt")) this->tiltType = static_cast<bool>(obj["hasTilt"]) ? tilt_types::none : tilt_types::tiltmotor;
|
||||
if(obj.containsKey("bitLength")) this->bitLength = obj["bitLength"];
|
||||
if(obj.containsKey("proto")) this->proto = static_cast<radio_proto>(obj["proto"].as<uint8_t>());
|
||||
if(obj.containsKey("shadeType")) {
|
||||
if(obj["shadeType"].is<const char *>()) {
|
||||
if(strncmp(obj["shadeType"].as<const char *>(), "roller", 7) == 0)
|
||||
|
|
@ -1417,6 +1455,7 @@ bool SomfyShade::toJSON(JsonObject &obj) {
|
|||
obj["tiltTime"] = this->tiltTime;
|
||||
obj["shadeType"] = static_cast<uint8_t>(this->shadeType);
|
||||
obj["bitLength"] = this->bitLength;
|
||||
obj["proto"] = static_cast<uint8_t>(this->proto);
|
||||
SomfyRemote::toJSON(obj);
|
||||
JsonArray arr = obj.createNestedArray("linkedRemotes");
|
||||
for(uint8_t i = 0; i < SOMFY_MAX_LINKED_REMOTES; i++) {
|
||||
|
|
@ -1583,6 +1622,7 @@ void SomfyRemote::sendCommand(somfy_commands cmd, uint8_t repeat) {
|
|||
frame.bitLength = this->bitLength;
|
||||
// Match the encKey to the rolling code. These keys range from 160 to 175.
|
||||
frame.encKey = 0xA0 | static_cast<uint8_t>(frame.rollingCode & 0x000F);
|
||||
frame.proto = this->proto;
|
||||
if(frame.bitLength == 0) frame.bitLength = bit_length;
|
||||
this->lastRollingCode = frame.rollingCode;
|
||||
somfy.sendFrame(frame, repeat);
|
||||
|
|
@ -2115,7 +2155,7 @@ void transceiver_config_t::fromJSON(JsonObject& obj) {
|
|||
if(obj.containsKey("deviation")) this->deviation = obj["deviation"]; // float
|
||||
if(obj.containsKey("enabled")) this->enabled = obj["enabled"];
|
||||
if(obj.containsKey("txPower")) this->txPower = obj["txPower"];
|
||||
|
||||
if(obj.containsKey("proto")) this->proto = static_cast<radio_proto>(obj["proto"].as<uint8_t>());
|
||||
/*
|
||||
if (obj.containsKey("internalCCMode")) this->internalCCMode = obj["internalCCMode"];
|
||||
if (obj.containsKey("modulationMode")) this->modulationMode = obj["modulationMode"];
|
||||
|
|
@ -2155,6 +2195,7 @@ void transceiver_config_t::toJSON(JsonObject& obj) {
|
|||
obj["frequency"] = this->frequency; // float
|
||||
obj["deviation"] = this->deviation; // float
|
||||
obj["txPower"] = this->txPower;
|
||||
obj["proto"] = static_cast<uint8_t>(this->proto);
|
||||
/*
|
||||
obj["internalCCMode"] = this->internalCCMode;
|
||||
obj["modulationMode"] = this->modulationMode;
|
||||
|
|
@ -2201,6 +2242,7 @@ void transceiver_config_t::save() {
|
|||
pref.putBool("enabled", this->enabled);
|
||||
pref.putBool("radioInit", true);
|
||||
pref.putChar("txPower", this->txPower);
|
||||
pref.putChar("proto", static_cast<uint8_t>(this->proto));
|
||||
|
||||
/*
|
||||
pref.putBool("internalCCMode", this->internalCCMode);
|
||||
|
|
@ -2253,7 +2295,7 @@ void transceiver_config_t::load() {
|
|||
this->enabled = pref.getBool("enabled", this->enabled);
|
||||
this->txPower = pref.getChar("txPower", this->txPower);
|
||||
this->rxBandwidth = pref.getFloat("rxBandwidth", this->rxBandwidth);
|
||||
|
||||
this->proto = static_cast<radio_proto>(pref.getChar("proto", static_cast<uint8_t>(this->proto)));
|
||||
this->removeNVSKey("internalCCMode");
|
||||
this->removeNVSKey("modulationMode");
|
||||
this->removeNVSKey("channel");
|
||||
|
|
|
|||
10
Somfy.h
10
Somfy.h
|
|
@ -9,7 +9,10 @@ typedef struct appver_t {
|
|||
uint8_t minor;
|
||||
uint8_t build;
|
||||
};
|
||||
|
||||
enum class radio_proto : byte {
|
||||
RTS = 0x00,
|
||||
RTW = 0x01
|
||||
};
|
||||
enum class somfy_commands : byte {
|
||||
Unknown0 = 0x0,
|
||||
My = 0x1,
|
||||
|
|
@ -26,7 +29,7 @@ enum class somfy_commands : byte {
|
|||
UnknownC = 0xC,
|
||||
UnknownD = 0xD,
|
||||
UnknownE = 0xE, // This command byte has been witnessed in the wild but cannot tell if it is from Somfy. No rolling code is sent with this and it is 56-bits.
|
||||
UnknownF = 0xF,
|
||||
RTWProto = 0xF, // RTW Protocol
|
||||
// Command extensions for 80 bit frames
|
||||
StepUp = 0x8B
|
||||
};
|
||||
|
|
@ -93,6 +96,7 @@ typedef struct somfy_tx_queue_t {
|
|||
typedef struct somfy_frame_t {
|
||||
bool valid = false;
|
||||
bool processed = false;
|
||||
radio_proto proto = radio_proto::RTS;
|
||||
int rssi = 0;
|
||||
byte lqi = 0x0;
|
||||
somfy_commands cmd;
|
||||
|
|
@ -121,6 +125,7 @@ class SomfyRemote {
|
|||
char m_remotePrefId[11] = "";
|
||||
uint32_t m_remoteAddress = 0;
|
||||
public:
|
||||
radio_proto proto = radio_proto::RTS;
|
||||
uint8_t bitLength = 0;
|
||||
char *getRemotePrefId() {return m_remotePrefId;}
|
||||
virtual bool toJSON(JsonObject &obj);
|
||||
|
|
@ -202,6 +207,7 @@ typedef struct transceiver_config_t {
|
|||
bool printBuffer = false;
|
||||
bool enabled = false;
|
||||
uint8_t type = 56; // 56 or 80 bit protocol.
|
||||
radio_proto proto = radio_proto::RTS;
|
||||
uint8_t SCKPin = 18;
|
||||
uint8_t TXPin = 12;
|
||||
uint8_t RXPin = 13;
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
4
Web.cpp
4
Web.cpp
|
|
@ -469,12 +469,14 @@ void Web::begin() {
|
|||
});
|
||||
server.on("/getNextShade", []() {
|
||||
webServer.sendCORSHeaders();
|
||||
StaticJsonDocument<128> doc;
|
||||
StaticJsonDocument<256> doc;
|
||||
uint8_t shadeId = somfy.getNextShadeId();
|
||||
JsonObject obj = doc.to<JsonObject>();
|
||||
obj["shadeId"] = shadeId;
|
||||
obj["remoteAddress"] = somfy.getNextRemoteAddress(shadeId);
|
||||
obj["bitLength"] = somfy.transceiver.config.type;
|
||||
obj["stepSize"] = 100;
|
||||
obj["proto"] = static_cast<uint8_t>(somfy.transceiver.config.proto);
|
||||
serializeJson(doc, g_content);
|
||||
server.send(200, _encoding_json, g_content);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
1.5.4
|
||||
1.6.0
|
||||
|
|
@ -3,10 +3,10 @@
|
|||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta charset="UTF-8">
|
||||
<link rel="stylesheet" href="main.css?v=1.5.4" type="text/css" />
|
||||
<link rel="stylesheet" href="icons.css?v=1.5.4" type="text/css" />
|
||||
<link rel="stylesheet" href="main.css?v=1.6.0b" type="text/css" />
|
||||
<link rel="stylesheet" href="icons.css?v=1.6.0b" type="text/css" />
|
||||
<link rel="icon" type="image/png" href="favicon.png" />
|
||||
<script type="text/javascript" src="index.js?v=1.5.4"></script>
|
||||
<script type="text/javascript" src="index.js?v=1.6.0b"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="divContainer" class="container" style="user-select:none;position:relative;border-radius:27px;">
|
||||
|
|
@ -219,8 +219,15 @@
|
|||
<div>
|
||||
<div class="field-group" style="width:127px;display:inline-block;margin-top:-20px;float:left;">
|
||||
<div class="field-group">
|
||||
<div class="field-group">
|
||||
<select id="selShadeBitLength" name="bitLength" style="width:100%;" onchange="somfy.onShadeBitLengthChanged(this);">
|
||||
<div class="field-group" style="margin-top:-10px;">
|
||||
<select id="selShadeProto" name="proto" style="width:50%;">
|
||||
<option value="0">RTS</option>
|
||||
<option value="1">RTW</option>
|
||||
</select>
|
||||
<label for="selShadeBitLength">Protocol</label>
|
||||
</div>
|
||||
<div class="field-group" style="margin-top:-10px;">
|
||||
<select id="selShadeBitLength" name="bitLength" style="width:50%;" onchange="somfy.onShadeBitLengthChanged(this);">
|
||||
<option value="56">56-BIT</option>
|
||||
<option value="80">80-BIT</option>
|
||||
</select>
|
||||
|
|
@ -318,7 +325,14 @@
|
|||
<div id="somfyTransceiver" style="display:none;position:relative;">
|
||||
<form>
|
||||
<div id="divRadioSettings" name="divRadioSettings" class="field-group1" style="display:block;position:relative">
|
||||
<div class="field-group" style="">
|
||||
<div class="field-group" style="display:inline-block;margin-right:7px;">
|
||||
<label for="selRadioProto">Protocol</label>
|
||||
<select id="selRadioProto" name="radioType" style="width:77px;">
|
||||
<option value="0">RTS</option>
|
||||
<option value="1">RTW</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="field-group" style="display:inline-block;">
|
||||
<label for="selRadioType">Radio</label>
|
||||
<select id="selRadioType" name="radioType" style="width:77px;">
|
||||
<option value="none" selected>None</option>
|
||||
|
|
|
|||
|
|
@ -378,7 +378,7 @@ async function reopenSocket() {
|
|||
await initSockets();
|
||||
}
|
||||
class General {
|
||||
appVersion = 'v1.5.4';
|
||||
appVersion = 'v1.6.0beta';
|
||||
reloadApp = false;
|
||||
async init() {
|
||||
this.setAppVersion();
|
||||
|
|
@ -924,6 +924,7 @@ class Somfy {
|
|||
document.getElementById('selTransTXPin').value = somfy.transceiver.config.TXPin.toString();
|
||||
document.getElementById('selTransRXPin').value = somfy.transceiver.config.RXPin.toString();
|
||||
document.getElementById('selRadioType').value = somfy.transceiver.config.type;
|
||||
document.getElementById('selRadioProto').value = somfy.transceiver.config.proto;
|
||||
document.getElementById('spanMaxShades').innerText = somfy.maxShades;
|
||||
document.getElementById('spanRxBandwidth').innerText = (Math.round(somfy.transceiver.config.rxBandwidth * 100) / 100).fmt('#,##0.00');
|
||||
document.getElementById('slidRxBandwidth').value = Math.round(somfy.transceiver.config.rxBandwidth * 100);
|
||||
|
|
@ -961,6 +962,7 @@ class Somfy {
|
|||
let obj = {
|
||||
enabled: document.getElementsByName('enableRadio')[0].checked,
|
||||
type: parseInt(document.getElementById('selRadioType').value, 10),
|
||||
proto: parseInt(document.getElementById('selRadioProto').value, 10),
|
||||
SCKPin: getIntValue('selTransSCKPin'),
|
||||
CSNPin: getIntValue('selTransCSNPin'),
|
||||
MOSIPin: getIntValue('selTransMOSIPin'),
|
||||
|
|
@ -1446,6 +1448,7 @@ class Somfy {
|
|||
document.getElementById('divLinkedRemoteList').innerHTML = '';
|
||||
document.getElementById('btnSetRollingCode').style.display = 'none';
|
||||
document.getElementById('selShadeBitLength').value = shade.bitLength || 56;
|
||||
document.getElementById('selShadeProto').value = shade.proto || 0;
|
||||
document.getElementById('slidStepSize').value = shade.stepSize || 100;
|
||||
document.getElementById('spanStepSize').innerHTML = shade.stepSize.fmt('#,##0');
|
||||
}
|
||||
|
|
@ -1472,6 +1475,7 @@ class Somfy {
|
|||
document.getElementById('btnLinkRemote').style.display = '';
|
||||
document.getElementById('selShadeType').value = shade.shadeType;
|
||||
document.getElementById('selShadeBitLength').value = shade.bitLength;
|
||||
document.getElementById('selShadeProto').value = shade.proto;
|
||||
document.getElementsByName('shadeAddress')[0].value = shade.remoteAddress;
|
||||
document.getElementsByName('shadeName')[0].value = shade.name;
|
||||
document.getElementsByName('shadeUpTime')[0].value = shade.upTime;
|
||||
|
|
@ -1532,6 +1536,7 @@ class Somfy {
|
|||
shadeType: parseInt(document.getElementById('selShadeType').value, 10),
|
||||
tiltTime: parseInt(document.getElementById('fldTiltTime').value, 10),
|
||||
bitLength: parseInt(document.getElementById('selShadeBitLength').value, 10) || 56,
|
||||
proto: parseInt(document.getElementById('selShadeProto').value, 10) || 0,
|
||||
stepSize: parseInt(document.getElementById('slidStepSize').value, 10) || 100
|
||||
};
|
||||
if (obj.shadeType === 1) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue