Skip to content

Commit e5e57fb

Browse files
authored
RX serial refactor (user selectable CRSF/SBUS serial protocol) (ExpressLRS#2094)
* Initial SBUS poc * Allow CRSF/SBUS protocol selection * Tidy up the PWM selection page * Lua config wip * Cleanup of Lua params and LQ/RSSI as channel data * Set lost packet/frame flag for SBUS * wip Lua serial protocol configuration * Runtime protocol configuration * Refactor the pin mode setting in Lua * Cleanup deprecated config options * Refactoring RX serial for multiple protocol handling * Only allow Serial on pins 1/3 (in Lua and web UI) * Update web UI for serial protocol handling * Review changes * remove ChannelData from CRSF class * Only log in options/hardware if LOG_INIT is defined
1 parent 51390df commit e5e57fb

54 files changed

Lines changed: 1207 additions & 762 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

src/html/index.html

Lines changed: 47 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -88,13 +88,10 @@ <h2>Runtime Options</h2>
8888
<label>AirPort UART baud</label>
8989
</div>
9090
@@else:
91-
<div class="mui-textfield">
91+
<div id="baud-config" class="mui-textfield" style="display: block;">
9292
<input size='7' id='rcvr-uart-baud' name='rcvr-uart-baud' type='text'/>
9393
<label>UART baud</label>
9494
</div>
95-
<div class="mui-checkbox">
96-
<label><input id='rcvr-invert-tx' name='rcvr-invert-tx' type='checkbox'/>Invert TX pin</label>
97-
</div>
9895
<div class="mui-checkbox">
9996
<label><input id='lock-on-first-connection' name='lock-on-first-connection' type='checkbox'/>Lock on first connection</label>
10097
</div>
@@ -170,50 +167,62 @@ <h2>Button Actions</h2>
170167
@@if not isTX:
171168
<div class="mui-tabs__pane" id="pane-justified-3">
172169
<div class="mui-panel">
173-
<h2>Model Match</h2>
174-
Specify the 'Receiver' number in OpenTX/EdgeTX model setup page and turn on the 'Model Match'
175-
in the ExpressLRS Lua script for that model. 'Model Match' is between 0 and 63 inclusive.
176-
<br/><br/>
177-
<form class="mui-form--inline elrs-inline-form" action='/modelmatch' id='modelmatch' method='POST'>
170+
<div id="pwm_tab">
171+
<h2>PWM Output</h2>
172+
Set PWM output mode and failsafe positions.
173+
<ul>
174+
<li><b>Output:</b> Receiver output pin</li>
175+
<li><b>Mode:</b> Output frequency, binary On/Off, or Serial (for MCU pins 1 and 3 only) mode</li>
176+
<ul>
177+
<li>When enabling serial pins, be sure to select the <b>Serial Protocol</b>below and <b>UART baud</b> on the <b>Options</b> tab</li>
178+
</ul>
179+
<li><b>Input:</b> Input channel from the handset</li>
180+
<li><b>Invert:</b> Invert input channel position</li>
181+
<li><b>750us:</b> Use half pulse width (494-1006us) with center 750us instead of 988-2012us</li>
182+
<li><b>Failsafe:</b> Absolute position to set the servo on failsafe</li>
183+
<ul>
184+
<li>Does not use "Invert" flag</li>
185+
<li>Value will be halved if "750us" flag is set</li>
186+
<li>Will be converted to binary for "On/Off" mode (>1500us = HIGH)</li>
187+
</ul>
188+
</ul>
189+
<br/><br/>
190+
<form action="/pwm" id="pwm" method="POST">
191+
</form>
192+
</div>
193+
<form class="mui-form" action='/config' id='config' method='POST'>
194+
<div id="serial-config" style="display: block;">
195+
<h2>Serial Protocol</h2>
196+
Set the protocol used to communicate with the flight controller.
197+
<br/><br/>
198+
<div class="mui-select">
199+
<select id='serial-protocol' name='serial-protocol'>
200+
<option value='0'>CRSF</option>
201+
<option value='1'>Inverted CRSF</option>
202+
<option value='2'>SBUS</option>
203+
<option value='3'>Inverted SBUS</option>
204+
</select>
205+
<label>Serial Protocol</label>
206+
</div>
207+
</div>
208+
<h2>Model Match</h2>
209+
Specify the 'Receiver' number in OpenTX/EdgeTX model setup page and turn on the 'Model Match'
210+
in the ExpressLRS Lua script for that model. 'Model Match' is between 0 and 63 inclusive.
211+
<br/><br/>
178212
<div class="mui-checkbox">
179213
<label><input id='model-match' name='model-match' type='checkbox'/> Enable Model Match</label>
180214
</div>
181215
<div class="mui-textfield">
182216
<input id='modelid' type='text' name='modelid' value="255" required/>
183217
<label>Model ID</label>
184218
</div>
185-
<button type='submit' class="mui-btn mui-btn--small mui-btn--primary">Save</button>
186-
</form>
187-
</div>
188-
<div id="pwm_tab" class="mui-panel">
189-
<h2>PWM Output</h2>
190-
Set PWM output mode and failsafe positions.
191-
<ul>
192-
<li><b>Output:</b> Receiver output pin</li>
193-
<li><b>Mode:</b> Output frequency or binary On/Off mode</li>
194-
<li><b>Input:</b> Input channel from the handset</li>
195-
<li><b>Invert:</b> Invert input channel position</li>
196-
<li><b>750us:</b> Use half pulse width (494-1006us) with center 750us instead of 988-2012us</li>
197-
<li><b>Failsafe:</b> Absolute position to set the servo on failsafe</li>
198-
<ul>
199-
<li>Does not use "Invert" flag</li>
200-
<li>Value will be halved if "750us" flag is set</li>
201-
<li>Will be converted to binary for "On/Off" mode (>1500us = HIGH)</li>
202-
</ul>
203-
</ul>
204-
<br/><br/>
205-
<form action="/pwm" id="pwm" method="POST">
206-
</form>
207-
</div>
208-
<div class="mui-panel">
209-
<h2>Force telemetry off</h2>
210-
When running multiple receivers simultaneously from the same TX (to increase the number of PWM servo outputs), there can be at most one receiver with telemetry enabled.<br>Enable this option to ignore the "Telem Ratio" setting on the TX and never send telemetry from this receiver.
211-
<br/><br/>
212-
<form class="mui-form--inline elrs-inline-form" action='/forceTelemetry' id='forcetlm' method='POST'>
219+
<h2>Force telemetry off</h2>
220+
When running multiple receivers simultaneously from the same TX (to increase the number of PWM servo outputs), there can be at most one receiver with telemetry enabled.<br>Enable this option to ignore the "Telem Ratio" setting on the TX and never send telemetry from this receiver.
221+
<br/><br/>
213222
<div class="mui-checkbox">
214223
<label><input id='force-tlm' name='force-tlm' type='checkbox' value="1"/> Force telemetry OFF on this receiver</label>
215224
</div>
216-
<input type='submit' value='Save' class="mui-btn mui-btn--small mui-btn--primary">
225+
<button type='submit' class="mui-btn mui-btn--small mui-btn--primary">Save</button>
217226
</form>
218227
</div>
219228
<div class="mui-panel">

src/html/scan.js

Lines changed: 108 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -34,36 +34,48 @@ function getPwmFormData() {
3434
outData.push(raw);
3535
++ch;
3636
}
37-
38-
const outForm = new FormData();
39-
outForm.append('pwm', outData.join(','));
40-
return outForm;
37+
return outData;
4138
}
4239

4340
function enumSelectGenerate(id, val, arOptions) {
4441
// Generate a <select> item with every option in arOptions, and select the val element (0-based)
4542
const retVal = `<div class="mui-select"><select id="${id}">` +
4643
arOptions.map((item, idx) => {
47-
return `<option value="${idx}"${(idx == val) ? ' selected' : ''}>${item}</option>`;
44+
if (item) return `<option value="${idx}"${(idx == val) ? ' selected' : ''} ${item == 'Disabled' ? 'disabled' : ''}>${item}</option>`;
45+
return '';
4846
}).join('') + '</select></div>';
4947
return retVal;
5048
}
5149

50+
@@if not isTX:
5251
function updatePwmSettings(arPwm) {
5352
if (arPwm === undefined) {
5453
if (_('pwm_tab')) _('pwm_tab').style.display = 'none';
5554
return;
5655
}
56+
var pin1Index = undefined;
57+
var pin3Index = undefined;
5758
// arPwm is an array of raw integers [49664,50688,51200]. 10 bits of failsafe position, 4 bits of input channel, 1 bit invert, 4 bits mode, 1 bit for narrow/750us
5859
const htmlFields = ['<div class="mui-panel"><table class="pwmtbl mui-table"><tr><th class="mui--text-center">Output</th><th>Mode</th><th>Input</th><th class="mui--text-center">Invert?</th><th class="mui--text-center">750us?</th><th>Failsafe</th></tr>'];
5960
arPwm.forEach((item, index) => {
60-
const failsafe = (item & 1023) + 988; // 10 bits
61-
const ch = (item >> 10) & 15; // 4 bits
62-
const inv = (item >> 14) & 1;
63-
const mode = (item >> 15) & 15; // 4 bits
64-
const narrow = (item >> 19) & 1;
65-
const modeSelect = enumSelectGenerate(`pwm_${index}_mode`, mode,
66-
['50Hz', '60Hz', '100Hz', '160Hz', '333Hz', '400Hz', '10KHzDuty', 'On/Off']);
61+
const failsafe = (item.config & 1023) + 988; // 10 bits
62+
const ch = (item.config >> 10) & 15; // 4 bits
63+
const inv = (item.config >> 14) & 1;
64+
const mode = (item.config >> 15) & 15; // 4 bits
65+
const narrow = (item.config >> 19) & 1;
66+
const pin = item.pin;
67+
const modes = ['50Hz', '60Hz', '100Hz', '160Hz', '333Hz', '400Hz', '10KHzDuty', 'On/Off'];
68+
if (pin == 1) {
69+
modes.push('Serial TX');
70+
modes.push(undefined); // true PWM
71+
pin1Index = index;
72+
}
73+
if (pin == 3) {
74+
modes.push('Serial RX');
75+
modes.push(undefined); // true PWM
76+
pin3Index = index;
77+
}
78+
const modeSelect = enumSelectGenerate(`pwm_${index}_mode`, mode, modes);
6779
const inputSelect = enumSelectGenerate(`pwm_${index}_ch`, ch,
6880
['ch1', 'ch2', 'ch3', 'ch4',
6981
'ch5 (AUX1)', 'ch6 (AUX2)', 'ch7 (AUX3)', 'ch8 (AUX4)',
@@ -76,17 +88,58 @@ function updatePwmSettings(arPwm) {
7688
<td><div class="mui-checkbox mui--text-center"><input type="checkbox" id="pwm_${index}_nar"${(narrow) ? ' checked' : ''}></div></td>
7789
<td><div class="mui-textfield"><input id="pwm_${index}_fs" value="${failsafe}" size="6"/></div></td></tr>`);
7890
});
79-
htmlFields.push('</table></div><button type="submit" class="mui-btn mui-btn--primary">Set PWM Output</button>');
91+
htmlFields.push('</table></div>');
8092

8193
const grp = document.createElement('DIV');
8294
grp.setAttribute('class', 'group');
8395
grp.innerHTML = htmlFields.join('');
8496

85-
@@if not isTX:
8697
_('pwm').appendChild(grp);
87-
_('pwm').addEventListener('submit', callback('Set PWM Output', 'Unknown error', '/pwm', getPwmFormData));
88-
@@end
98+
99+
var setDisabled = (index, onoff) => {
100+
_(`pwm_${index}_ch`).disabled = onoff;
101+
_(`pwm_${index}_inv`).disabled = onoff;
102+
_(`pwm_${index}_nar`).disabled = onoff;
103+
_(`pwm_${index}_fs`).disabled = onoff;
104+
}
105+
// put some contraints on pin1/3 mode selects
106+
if (pin1Index !== undefined && pin3Index !== undefined) {
107+
const pin1Mode = _(`pwm_${pin1Index}_mode`);
108+
const pin3Mode = _(`pwm_${pin3Index}_mode`);
109+
pin1Mode.onchange = () => {
110+
if (pin1Mode.value == 8) { // Serial
111+
pin3Mode.value = 8;
112+
setDisabled(pin1Index, true);
113+
setDisabled(pin3Index, true);
114+
pin3Mode.disabled = true;
115+
_('serial-config').style.display = 'block';
116+
_('baud-config').style.display = 'block';
117+
}
118+
else {
119+
pin3Mode.value = 0;
120+
setDisabled(pin1Index, false);
121+
setDisabled(pin3Index, false);
122+
pin3Mode.disabled = false;
123+
_('serial-config').style.display = 'none';
124+
_('baud-config').style.display = 'none';
125+
}
126+
}
127+
pin3Mode.onchange = () => {
128+
if (pin3Mode.value == 8) { // Serial
129+
pin1Mode.value = 8;
130+
setDisabled(pin1Index, true);
131+
setDisabled(pin3Index, true);
132+
pin3Mode.disabled = true;
133+
_('serial-config').style.display = 'block';
134+
_('baud-config').style.display = 'block';
135+
}
136+
}
137+
const pin3 = pin3Mode.value;
138+
pin1Mode.onchange();
139+
if(pin1Mode.value != 8) pin3Mode.value = pin3;
140+
}
89141
}
142+
@@end
90143

91144
function init() {
92145
// setup network radio button handling
@@ -150,6 +203,10 @@ function timeoutCurrentColors() {
150203
}
151204

152205
function updateConfig(data) {
206+
if (data.product_name) _('product_name').textContent = data.product_name;
207+
if (data.reg_domain) _('reg_domain').textContent = data.reg_domain;
208+
if (data.uid) _('uid').value = data.uid.toString();
209+
if (data.uidtype) _('uid-type').textContent = data.uidtype;
153210
if (data.mode==='STA') {
154211
_('stamode').style.display = 'block';
155212
_('ssid').textContent = data.ssid;
@@ -167,14 +224,21 @@ function updateConfig(data) {
167224
storedModelId = 255;
168225
}
169226
_('modelid').value = storedModelId;
170-
171227
_('force-tlm').checked = data.hasOwnProperty('forcetlm') && data.forcetlm;
172-
@@end
173-
if (data.product_name) _('product_name').textContent = data.product_name;
174-
if (data.reg_domain) _('reg_domain').textContent = data.reg_domain;
175-
if (data.uid) _('uid').value = data.uid.toString();
176-
if (data.uidtype) _('uid-type').textContent = data.uidtype;
228+
_('serial-protocol').onchange = () => {
229+
if (_('serial-protocol').value == 0 || _('serial-protocol').value == 1) {
230+
_('rcvr-uart-baud').disabled = false;
231+
_('rcvr-uart-baud').value = '420000';
232+
}
233+
else {
234+
_('rcvr-uart-baud').disabled = true;
235+
_('rcvr-uart-baud').value = '100000';
236+
}
237+
}
177238
updatePwmSettings(data.pwm);
239+
_('serial-protocol').value = data['serial-protocol'];
240+
_('serial-protocol').onchange();
241+
@@end
178242
@@if isTX:
179243
if (data.hasOwnProperty['button-colors']) {
180244
if (_('button1-color')) _('button1-color').oninput = changeCurrentColors;
@@ -215,7 +279,6 @@ function getNetworks() {
215279
if (this.status == 204) {
216280
setTimeout(getNetworks, 2000);
217281
} else {
218-
console.log(this.responseText);
219282
const data = JSON.parse(this.responseText);
220283
if (data.length > 0) {
221284
_('loader').style.display = 'none';
@@ -411,7 +474,7 @@ function callback(title, msg, url, getdata, success) {
411474
}
412475
};
413476
xmlhttp.open('POST', url, true);
414-
if (getdata) data = getdata();
477+
if (getdata) data = getdata(xmlhttp);
415478
else data = null;
416479
xmlhttp.send(data);
417480
};
@@ -446,18 +509,18 @@ _('reset-options').addEventListener('click', callback('Reset Runtime Options', '
446509

447510
_('sethome').addEventListener('submit', setupNetwork);
448511
_('connect').addEventListener('click', callback('Connect to Home Network', 'An error occurred connecting to the Home network', '/connect', null));
449-
if (_('modelmatch') != undefined) {
450-
_('modelmatch').addEventListener('submit', callback('Set Model Match', 'An error occurred updating the model match number', '/model',
451-
() => {
452-
return new FormData(_('modelmatch'));
512+
if (_('config') != undefined) {
513+
_('config').addEventListener('submit', callback('Set Configuration', 'An error occurred updating the configuration', '/config',
514+
(xmlhttp) => {
515+
xmlhttp.setRequestHeader('Content-Type', 'application/json');
516+
return JSON.stringify({
517+
"pwm": getPwmFormData(),
518+
"protocol": +_('serial-protocol').value,
519+
"modelid": +_('modelid').value,
520+
"forcetlm": +_('force-tlm').checked
521+
});
453522
}));
454523
}
455-
if (_('forcetlm') != undefined) {
456-
_('forcetlm').addEventListener('submit', callback('Set force telemetry', 'An error occurred updating the force telemetry setting', '/forceTelemetry',
457-
() => {
458-
return new FormData(_('forcetlm'));
459-
}));
460-
}
461524

462525
function submitOptions(e) {
463526
e.stopPropagation();
@@ -474,15 +537,16 @@ function submitOptions(e) {
474537
// Serialize and send the formObject
475538
xhr.send(JSON.stringify(formObject, function(k, v) {
476539
if (v === '') return undefined;
477-
if (_(k) && _(k).type == 'color') return undefined;
478-
if (_(k) && _(k).type == 'checkbox') {
479-
return v == 'on' ? true : false;
480-
}
481-
if (_(k) && _(k).classList.contains('array')) {
482-
const arr = v.split(',').map((element) => {
483-
return Number(element);
484-
});
485-
return arr.length == 0 ? undefined : arr;
540+
if (_(k)) {
541+
if (_(k).type == 'color') return undefined;
542+
if (_(k).type == 'checkbox') return v === 'on';
543+
if (_(k).classList.contains('datatype-boolean')) return v === 'true';
544+
if (_(k).classList.contains('array')) {
545+
const arr = v.split(',').map((element) => {
546+
return Number(element);
547+
});
548+
return arr.length == 0 ? undefined : arr;
549+
}
486550
}
487551
if (typeof v === 'boolean') return v;
488552
return isNaN(v) ? v : +v;
@@ -545,6 +609,7 @@ function updateOptions(data) {
545609
if (Array.isArray(value)) _(key).value = value.toString();
546610
else _(key).value = value;
547611
}
612+
if(_(key).onchange) _(key).onchange();
548613
}
549614
}
550615
if (data['wifi-ssid']) _('homenet').textContent = data['wifi-ssid'];

0 commit comments

Comments
 (0)