mirror of
https://github.com/rstrouse/ESPSomfy-RTS.git
synced 2025-12-13 02:52:11 +01:00
Work on 80-bit protocol
* Fixes issue #11 TX Power is now persisted to NVS * Increase timing for tilt motors from 7 to 15 beats. #1 * Add time to frame log. * Allow RX and TX pins to be shared and multiplexed
This commit is contained in:
parent
ab7cdba519
commit
5f5ac9fa32
9 changed files with 66 additions and 20 deletions
|
|
@ -3,7 +3,7 @@
|
||||||
#ifndef configsettings_h
|
#ifndef configsettings_h
|
||||||
#define configsettings_h
|
#define configsettings_h
|
||||||
|
|
||||||
#define FW_VERSION "v1.4.3"
|
#define FW_VERSION "v1.4.4"
|
||||||
enum DeviceStatus {
|
enum DeviceStatus {
|
||||||
DS_OK = 0,
|
DS_OK = 0,
|
||||||
DS_ERROR = 1,
|
DS_ERROR = 1,
|
||||||
|
|
|
||||||
14
Somfy.cpp
14
Somfy.cpp
|
|
@ -25,7 +25,7 @@ uint8_t rxmode = 0; // Indicates whether the radio is in receive mode. Just to
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define SETMY_REPEATS 15
|
#define SETMY_REPEATS 15
|
||||||
#define TILT_REPEATS 7
|
#define TILT_REPEATS 15
|
||||||
|
|
||||||
int sort_asc(const void *cmp1, const void *cmp2) {
|
int sort_asc(const void *cmp1, const void *cmp2) {
|
||||||
int a = *((uint8_t *)cmp1);
|
int a = *((uint8_t *)cmp1);
|
||||||
|
|
@ -137,12 +137,14 @@ void somfy_frame_t::decodeFrame(byte* frame) {
|
||||||
case somfy_commands::Prog:
|
case somfy_commands::Prog:
|
||||||
case somfy_commands::SunFlag:
|
case somfy_commands::SunFlag:
|
||||||
case somfy_commands::Flag:
|
case somfy_commands::Flag:
|
||||||
case somfy_commands::StepUp:
|
|
||||||
case somfy_commands::UnknownC:
|
case somfy_commands::UnknownC:
|
||||||
case somfy_commands::UnknownD:
|
case somfy_commands::UnknownD:
|
||||||
case somfy_commands::UnknownE:
|
case somfy_commands::UnknownE:
|
||||||
case somfy_commands::UnknownF:
|
case somfy_commands::UnknownF:
|
||||||
|
break;
|
||||||
|
case somfy_commands::StepUp:
|
||||||
case somfy_commands::StepDown:
|
case somfy_commands::StepDown:
|
||||||
|
// These must be 80 bit commands
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
this->valid = false;
|
this->valid = false;
|
||||||
|
|
@ -1812,8 +1814,11 @@ void Transceiver::emitFrame(somfy_frame_t *frame, somfy_rx_t *rx) {
|
||||||
evt.appendMessage(buf);
|
evt.appendMessage(buf);
|
||||||
snprintf(buf, sizeof(buf), "\"bits\":%d,", rx->bit_length);
|
snprintf(buf, sizeof(buf), "\"bits\":%d,", rx->bit_length);
|
||||||
evt.appendMessage(buf);
|
evt.appendMessage(buf);
|
||||||
|
snprintf(buf, sizeof(buf), "\"valid\":%s,", frame->valid ? "true" : "false");
|
||||||
|
evt.appendMessage(buf);
|
||||||
snprintf(buf, sizeof(buf), "\"sync\":%d,\"pulses\":[", frame->hwsync);
|
snprintf(buf, sizeof(buf), "\"sync\":%d,\"pulses\":[", frame->hwsync);
|
||||||
evt.appendMessage(buf);
|
evt.appendMessage(buf);
|
||||||
|
|
||||||
if(rx) {
|
if(rx) {
|
||||||
for(uint16_t i = 0; i < rx->pulseCount; i++) {
|
for(uint16_t i = 0; i < rx->pulseCount; i++) {
|
||||||
snprintf(buf, sizeof(buf), "%s%d", i != 0 ? "," : "", rx->pulses[i]);
|
snprintf(buf, sizeof(buf), "%s%d", i != 0 ? "," : "", rx->pulses[i]);
|
||||||
|
|
@ -1969,6 +1974,7 @@ void transceiver_config_t::save() {
|
||||||
pref.putFloat("rxBandwidth", this->rxBandwidth); // float
|
pref.putFloat("rxBandwidth", this->rxBandwidth); // float
|
||||||
pref.putBool("enabled", this->enabled);
|
pref.putBool("enabled", this->enabled);
|
||||||
pref.putBool("radioInit", true);
|
pref.putBool("radioInit", true);
|
||||||
|
pref.putChar("txPower", this->txPower);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
pref.putBool("internalCCMode", this->internalCCMode);
|
pref.putBool("internalCCMode", this->internalCCMode);
|
||||||
|
|
@ -2061,6 +2067,10 @@ void transceiver_config_t::apply() {
|
||||||
if(!radioInit) return;
|
if(!radioInit) return;
|
||||||
Serial.print("Applying radio settings ");
|
Serial.print("Applying radio settings ");
|
||||||
Serial.printf("Setting Data Pins RX:%u TX:%u\n", this->RXPin, this->TXPin);
|
Serial.printf("Setting Data Pins RX:%u TX:%u\n", this->RXPin, this->TXPin);
|
||||||
|
//if(this->TXPin != this->RXPin)
|
||||||
|
// pinMode(this->TXPin, OUTPUT);
|
||||||
|
//pinMode(this->RXPin, INPUT);
|
||||||
|
// Essentially these call only preform the two functions above.
|
||||||
if(this->TXPin == this->RXPin)
|
if(this->TXPin == this->RXPin)
|
||||||
ELECHOUSE_cc1101.setGDO0(this->TXPin); // This pin may be shared.
|
ELECHOUSE_cc1101.setGDO0(this->TXPin); // This pin may be shared.
|
||||||
else
|
else
|
||||||
|
|
|
||||||
Binary file not shown.
Binary file not shown.
10
Web.cpp
10
Web.cpp
|
|
@ -905,7 +905,10 @@ void Web::begin() {
|
||||||
server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Shade not found to pair\"}"));
|
server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Shade not found to pair\"}"));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
shade->sendCommand(somfy_commands::Prog, 7);
|
if(shade->bitLength == 56)
|
||||||
|
shade->sendCommand(somfy_commands::Prog, 7);
|
||||||
|
else
|
||||||
|
shade->sendCommand(somfy_commands::Prog, 1);
|
||||||
shade->paired = true;
|
shade->paired = true;
|
||||||
shade->save();
|
shade->save();
|
||||||
DynamicJsonDocument doc(512);
|
DynamicJsonDocument doc(512);
|
||||||
|
|
@ -951,7 +954,10 @@ void Web::begin() {
|
||||||
server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Shade not found to unpair\"}"));
|
server.send(500, _encoding_json, F("{\"status\":\"ERROR\",\"desc\":\"Shade not found to unpair\"}"));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
shade->sendCommand(somfy_commands::Prog, 7);
|
if(shade->bitLength == 56)
|
||||||
|
shade->sendCommand(somfy_commands::Prog, 7);
|
||||||
|
else
|
||||||
|
shade->sendCommand(somfy_commands::Prog, 1);
|
||||||
shade->paired = false;
|
shade->paired = false;
|
||||||
shade->save();
|
shade->save();
|
||||||
DynamicJsonDocument doc(512);
|
DynamicJsonDocument doc(512);
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
1.4.3
|
1.4.4
|
||||||
|
|
@ -3,10 +3,10 @@
|
||||||
<head>
|
<head>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<link rel="stylesheet" href="main.css?v=1.4.3" type="text/css" />
|
<link rel="stylesheet" href="main.css?v=1.4.4" type="text/css" />
|
||||||
<link rel="stylesheet" href="icons.css?v=1.4.3" type="text/css" />
|
<link rel="stylesheet" href="icons.css?v=1.4.4" type="text/css" />
|
||||||
<link rel="icon" type="image/png" href="favicon.png" />
|
<link rel="icon" type="image/png" href="favicon.png" />
|
||||||
<script type="text/javascript" src="index.js?v=1.4.3"></script>
|
<script type="text/javascript" src="index.js?v=1.4.4"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="divContainer" class="container" style="user-select:none;position:relative;border-radius:27px;">
|
<div id="divContainer" class="container" style="user-select:none;position:relative;border-radius:27px;">
|
||||||
|
|
@ -426,7 +426,7 @@
|
||||||
<div id="divFrameLog" class="frame-log" style="display:none;">
|
<div id="divFrameLog" class="frame-log" style="display:none;">
|
||||||
<h1 style="text-align: center;padding:10px;"><span>Frame Logs</span><span class="button-outline" onclick="document.getElementById('divFrameLog').style.display = 'none';" style="float:right;font-size:1.25rem;display:inline-block;vertical-align:middle;width:38px;height:38px;position:relative;padding-top:4px;"><span style="vertical-align:middle;clear:both;text-align:center;display:inline-block;"><i id="icoConfig" class="icss-x" style=""></i></span></span></h1>
|
<h1 style="text-align: center;padding:10px;"><span>Frame Logs</span><span class="button-outline" onclick="document.getElementById('divFrameLog').style.display = 'none';" style="float:right;font-size:1.25rem;display:inline-block;vertical-align:middle;width:38px;height:38px;position:relative;padding-top:4px;"><span style="vertical-align:middle;clear:both;text-align:center;display:inline-block;"><i id="icoConfig" class="icss-x" style=""></i></span></span></h1>
|
||||||
<hr style="margin:0px;" />
|
<hr style="margin:0px;" />
|
||||||
<div class="frame-header"><span>Key</span><span>Address</span><span>Command</span><span>Code</span><span>RSSI</span><span>Bits</span></div>
|
<div class="frame-header"><span>Key</span><span>Address</span><span>Command</span><span>Code</span><span>RSSI</span><span>Bits</span><span style="text-align:center;width:77px;">Time</span></div>
|
||||||
<div id="divFrames" class="frame-list"></div>
|
<div id="divFrames" class="frame-list"></div>
|
||||||
<div class="button-container" style="text-align:center">
|
<div class="button-container" style="text-align:center">
|
||||||
<button type="button" class="btnCopyFrame" style="display:inline-block;width:44%;" onclick="somfy.framesToClipboard();">Copy</button>
|
<button type="button" class="btnCopyFrame" style="display:inline-block;width:44%;" onclick="somfy.framesToClipboard();">Copy</button>
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,13 @@ document.oncontextmenu = (event) => {
|
||||||
event.preventDefault(); event.stopPropagation(); return false;
|
event.preventDefault(); event.stopPropagation(); return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Date.prototype.toJSON = function () {
|
||||||
|
let tz = this.getTimezoneOffset();
|
||||||
|
let sign = tz > 0 ? '-' : '+';
|
||||||
|
let tzHrs = Math.floor(Math.abs(tz) / 60).fmt('00');
|
||||||
|
let tzMin = (Math.abs(tz) % 60).fmt('00');
|
||||||
|
return `${this.getFullYear()}-${(this.getMonth() + 1).fmt('00')}-${this.getDate().fmt('00')}T${this.getHours().fmt('00')}:${this.getMinutes().fmt('00')}:${this.getSeconds().fmt('00')}.${this.getMilliseconds().fmt('000')}${sign}${tzHrs}${tzMin}`;
|
||||||
|
};
|
||||||
Number.prototype.round = function (dec) { return Number(Math.round(this + 'e' + dec) + 'e-' + dec); };
|
Number.prototype.round = function (dec) { return Number(Math.round(this + 'e' + dec) + 'e-' + dec); };
|
||||||
Number.prototype.fmt = function (format, empty) {
|
Number.prototype.fmt = function (format, empty) {
|
||||||
if (isNaN(this)) return empty || '';
|
if (isNaN(this)) return empty || '';
|
||||||
|
|
@ -81,7 +87,7 @@ Number.prototype.fmt = function (format, empty) {
|
||||||
}
|
}
|
||||||
if (fw.length > rw.length) {
|
if (fw.length > rw.length) {
|
||||||
var pstart = fw.indexOf('0');
|
var pstart = fw.indexOf('0');
|
||||||
if (pstart > 0) {
|
if (pstart >= 0) {
|
||||||
var plen = fw.length - pstart;
|
var plen = fw.length - pstart;
|
||||||
var pos = fw.length - rw.length - 1;
|
var pos = fw.length - rw.length - 1;
|
||||||
while (rw.length < plen) {
|
while (rw.length < plen) {
|
||||||
|
|
@ -372,7 +378,7 @@ async function reopenSocket() {
|
||||||
await initSockets();
|
await initSockets();
|
||||||
}
|
}
|
||||||
class General {
|
class General {
|
||||||
appVersion = 'v1.4.3';
|
appVersion = 'v1.4.4';
|
||||||
reloadApp = false;
|
reloadApp = false;
|
||||||
async init() {
|
async init() {
|
||||||
this.setAppVersion();
|
this.setAppVersion();
|
||||||
|
|
@ -987,9 +993,14 @@ class Somfy {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (sval === val) {
|
if (sval === val) {
|
||||||
errorMessage(document.getElementById('fsSomfySettings'), `The ${name.replace('Pin', '')} pin is duplicated by the ${s.replace('Pin', '')}. All pin definitions must be unique`);
|
if ((name === 'TXPin' && s === 'RXPin') ||
|
||||||
valid = false;
|
(name === 'RXPin' && s === 'TXPin'))
|
||||||
return false;
|
continue; // The RX and TX pins can share the same value. In this instance the radio will only use GDO0.
|
||||||
|
else {
|
||||||
|
errorMessage(document.getElementById('fsSomfySettings'), `The ${name.replace('Pin', '')} pin is duplicated by the ${s.replace('Pin', '')}. All pin definitions must be unique`);
|
||||||
|
valid = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1300,11 +1311,22 @@ class Somfy {
|
||||||
let frames = document.getElementById('divFrames');
|
let frames = document.getElementById('divFrames');
|
||||||
let row = document.createElement('div');
|
let row = document.createElement('div');
|
||||||
row.classList.add('frame-row');
|
row.classList.add('frame-row');
|
||||||
let html = `<span>${frame.encKey}</span><span>${frame.address}</span><span>${frame.command}</span><span>${frame.rcode}</span><span>${frame.rssi}dBm</span><span>${frame.bits}</span><div class="frame-pulses">`;
|
row.setAttribute('data-valid', frame.valid);
|
||||||
|
// The socket is not sending the current date so we will snag the current receive date from
|
||||||
|
// the browser.
|
||||||
|
let fnFmtDate = (dt) => {
|
||||||
|
return `${(dt.getMonth() + 1).fmt('00')}/${dt.getDate().fmt('00')} ${dt.getHours().fmt('00')}:${dt.getMinutes().fmt('00')}:${dt.getSeconds().fmt('00')}.${dt.getMilliseconds().fmt('000')}`;
|
||||||
|
}
|
||||||
|
let fnFmtTime = (dt) => {
|
||||||
|
return `${dt.getHours().fmt('00')}:${dt.getMinutes().fmt('00')}:${dt.getSeconds().fmt('00')}.${dt.getMilliseconds().fmt('000')}`;
|
||||||
|
}
|
||||||
|
frame.time = new Date();
|
||||||
|
let html = `<span>${frame.encKey}</span><span>${frame.address}</span><span>${frame.command}</span><span>${frame.rcode}</span><span>${frame.rssi}dBm</span><span>${frame.bits}</span><span>${fnFmtTime(frame.time)}</span><div class="frame-pulses">`;
|
||||||
for (let i = 0; i < frame.pulses.length; i++) {
|
for (let i = 0; i < frame.pulses.length; i++) {
|
||||||
if (i !== 0) html += ',';
|
if (i !== 0) html += ',';
|
||||||
html += `${frame.pulses[i]}`;
|
html += `${frame.pulses[i]}`;
|
||||||
}
|
}
|
||||||
|
html += '</div>'
|
||||||
row.innerHTML = html;
|
row.innerHTML = html;
|
||||||
frames.prepend(row);
|
frames.prepend(row);
|
||||||
this.frames.push(frame);
|
this.frames.push(frame);
|
||||||
|
|
@ -1732,7 +1754,6 @@ class Somfy {
|
||||||
putJSON('/tiltCommand', { shadeId: shadeId, target: parseInt(command, 10) }, (err, shade) => {
|
putJSON('/tiltCommand', { shadeId: shadeId, target: parseInt(command, 10) }, (err, shade) => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
linkRemote(shadeId) {
|
linkRemote(shadeId) {
|
||||||
let div = document.createElement('div');
|
let div = document.createElement('div');
|
||||||
let html = `<div id="divLinking" class="instructions" data-type="link-remote" data-shadeid="${shadeId}">`;
|
let html = `<div id="divLinking" class="instructions" data-type="link-remote" data-shadeid="${shadeId}">`;
|
||||||
|
|
@ -1785,7 +1806,6 @@ class Somfy {
|
||||||
somfy.sendTiltCommand(shadeId, el.value);
|
somfy.sendTiltCommand(shadeId, el.value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
openSetPosition(shadeId) {
|
openSetPosition(shadeId) {
|
||||||
console.log('Opening Shade Positioner');
|
console.log('Opening Shade Positioner');
|
||||||
if (typeof shadeId === 'undefined') {
|
if (typeof shadeId === 'undefined') {
|
||||||
|
|
|
||||||
|
|
@ -704,6 +704,7 @@ div.frame-header > span {
|
||||||
}
|
}
|
||||||
div.frame-row > span {
|
div.frame-row > span {
|
||||||
text-align:right;
|
text-align:right;
|
||||||
|
text-decoration:inherit;
|
||||||
}
|
}
|
||||||
div.frame-row > span:nth-child(1),
|
div.frame-row > span:nth-child(1),
|
||||||
div.frame-header > span:nth-child(1) {
|
div.frame-header > span:nth-child(1) {
|
||||||
|
|
@ -728,9 +729,14 @@ div.frame-header > span {
|
||||||
}
|
}
|
||||||
div.frame-row > span:nth-child(6),
|
div.frame-row > span:nth-child(6),
|
||||||
div.frame-header > span:nth-child(6) {
|
div.frame-header > span:nth-child(6) {
|
||||||
width: 47px;
|
width: 27px;
|
||||||
text-align:center;
|
text-align:center;
|
||||||
}
|
}
|
||||||
|
div.frame-row > span:nth-child(7),
|
||||||
|
div.frame-header > span:nth-child(7) {
|
||||||
|
text-align: right;
|
||||||
|
white-space:nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
div.frame-list > div:nth-child(2n+1) {
|
div.frame-list > div:nth-child(2n+1) {
|
||||||
background: beige;
|
background: beige;
|
||||||
|
|
@ -740,6 +746,10 @@ div.frame-row {
|
||||||
width:100%;
|
width:100%;
|
||||||
background:white;
|
background:white;
|
||||||
}
|
}
|
||||||
|
div.frame-row[data-valid="false"] {
|
||||||
|
text-decoration:line-through;
|
||||||
|
color:red;
|
||||||
|
}
|
||||||
div.frame-pulses {
|
div.frame-pulses {
|
||||||
width:100%;
|
width:100%;
|
||||||
padding-left:7px;
|
padding-left:7px;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue