Skip to content

Commit 1dee3f0

Browse files
authored
Use modelId for configuration and model matching (ExpressLRS#765)
* Make the switch mode configurable from LUA Removes the switch and telemetry defines * Add modelId parsing into CRSF Use radio modelId as an index for different configs i.e. the rate, tlm ratio, max power and switch mode are per model * Implement model match on the TX, with updates to LUA script * Add model match to RX (specified by a user define) * RX webpage update to allow setting model match id * Set model match from command line Introduce a telemetry command so the model match can be updated via passthrough * Add MSP command to set model match This enables us to have a LUA command that sends a MSP message to a connected receiver to set its model match id.
1 parent 3f68161 commit 1dee3f0

26 files changed

Lines changed: 417 additions & 163 deletions

.github/workflows/build.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,9 +98,9 @@ jobs:
9898
;;
9999
esac
100100
# All the features
101-
PLATFORMIO_BUILD_FLAGS="$REG -DHYBRID_SWITCHES_8 -DENABLE_TELEMETRY -DUSE_DIVERSITY" pio run -e ${{ matrix.target }}
101+
PLATFORMIO_BUILD_FLAGS="$REG -DUSE_DIVERSITY" pio run -e ${{ matrix.target }}
102102
# Minimal/default features
103-
PLATFORMIO_BUILD_FLAGS="$REG !-DHYBRID_SWITCHES_8 !-DENABLE_TELEMETRY !-DUSE_DIVERSITY" pio run -e ${{ matrix.target }}
103+
PLATFORMIO_BUILD_FLAGS="$REG !-DUSE_DIVERSITY" pio run -e ${{ matrix.target }}
104104
mv .pio/build ~/artifacts/AU_915
105105
106106
- name: Store Artifacts

src/html/rx_index.html

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,24 @@ <h3 id="status"></h3>
5555
</fieldset>
5656
</div>
5757

58+
<div align="left">
59+
<fieldset>
60+
If you set the 'Model Match' below to a number other than zero, you can specify the 'Receiver' number in OpenTX/EdgeTX
61+
model setup page and turn on the 'Model Match' in the ELRS LUA script for that model. 'Model Match' is between 0 and 63 inclusive.
62+
<br><br>
63+
<legend>
64+
<h2>Model Match:</h2>
65+
</legend>
66+
<form action='/modelmatch' id='modelmatch' method='POST'>
67+
<div class="group">
68+
<input id='modelid' type='text' name='modelid'>
69+
<input type='submit' value='Set Model Match'>
70+
</div>
71+
</form>
72+
<br><br>
73+
</fieldset>
74+
</div>
75+
5876
<div align="left" id="apmode" style="display:none;">
5977
<fieldset>
6078
Here you can join a network and it will be saved as your &quot;home&quot; network.

src/html/scan.js

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,29 @@
11
document.addEventListener("DOMContentLoaded", get_mode, false);
22
var scanTimer = undefined;
33

4+
function _(el) {
5+
return document.getElementById(el);
6+
}
7+
48
function get_mode() {
59
var json_url = 'mode.json';
610
xmlhttp = new XMLHttpRequest();
711
xmlhttp.onreadystatechange = function () {
812
if (this.readyState == 4 && this.status == 200) {
913
var data = JSON.parse(this.responseText);
1014
if (data.mode==="STA") {
11-
document.getElementById('stamode').style.display = 'block';
12-
document.getElementById('ssid').textContent = data.ssid;
15+
_('stamode').style.display = 'block';
16+
_('ssid').textContent = data.ssid;
1317
} else {
14-
document.getElementById('apmode').style.display = 'block';
18+
_('apmode').style.display = 'block';
1519
if (data.ssid) {
16-
document.getElementById('homenet').textContent = data.ssid;
20+
_('homenet').textContent = data.ssid;
1721
} else {
18-
document.getElementById('connect').style.display = 'none';
22+
_('connect').style.display = 'none';
1923
}
2024
scanTimer = setInterval(get_networks, 2000);
2125
}
26+
_('modelid').value = data.modelid;
2227
}
2328
};
2429
xmlhttp.open("POST", json_url, true);
@@ -32,8 +37,8 @@ function get_networks() {
3237
xmlhttp.onreadystatechange = function () {
3338
if (this.readyState == 4 && this.status == 200) {
3439
var data = JSON.parse(this.responseText);
35-
document.getElementById('loader').style.display = 'none';
36-
autocomplete(document.getElementById('network'), data);
40+
_('loader').style.display = 'none';
41+
autocomplete(_('network'), data);
3742
clearInterval(scanTimer);
3843
}
3944
};
@@ -109,7 +114,7 @@ function autocomplete(inp, arr) {
109114

110115
/*execute a function presses a key on the keyboard:*/
111116
inp.addEventListener("keydown", function (e) {
112-
var x = document.getElementById(this.id + "autocomplete-list");
117+
var x = _(this.id + "autocomplete-list");
113118
if (x) x = x.getElementsByTagName("div");
114119
if (e.keyCode == 40) {
115120
/*If the arrow DOWN key is pressed,
@@ -166,10 +171,6 @@ function autocomplete(inp, arr) {
166171

167172
//=========================================================
168173

169-
function _(el) {
170-
return document.getElementById(el);
171-
}
172-
173174
function uploadFile() {
174175
var file = _("firmware_file").files[0];
175176
var formdata = new FormData();
@@ -269,7 +270,10 @@ _('sethome').addEventListener('submit', callback("Set Home Network", "An error o
269270
}));
270271
_('connect').addEventListener('click', callback("Connect to Home Network", "An error occurred connecting to the Home network", "/connect", null));
271272
_('access').addEventListener('click', callback("Access Point", "An error occurred starting the Access Point", "/access", null));
272-
_('forget').addEventListener('click', callback("Forget Home Network", "An error occurred forgetting the home network", "/forget", null));
273+
_('forget').addEventListener('click', callback("Forget Home Network", "An error occurred forgetting the home network", "/forget", null));
274+
if (_('modelmatch') != undefined) {
275+
_('modelmatch').addEventListener('submit', callback("Set Model Match", "An error occurred updating the model match number", "/model", null));
276+
}
273277

274278
//=========================================================
275279

src/lib/CRSF/CRSF.cpp

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ void (*CRSF::disconnected)() = &nullCallback; // called when CRSF stream is lost
3636
void (*CRSF::connected)() = &nullCallback; // called when CRSF stream is regained
3737

3838
void (*CRSF::RecvParameterUpdate)() = &nullCallback; // called when recv parameter update req, ie from LUA
39+
void (*CRSF::RecvModelUpdate)() = &nullCallback; // called when model id cahnges, ie command from Radio
3940

4041
/// UART Handling ///
4142
uint32_t CRSF::GoodPktsCountResult = 0;
@@ -52,8 +53,11 @@ volatile inBuffer_U CRSF::inBuffer;
5253
uint8_t CRSF::currentSwitches[N_SWITCHES] = {0};
5354
uint8_t CRSF::sentSwitches[N_SWITCHES] = {0};
5455

56+
uint8_t CRSF::nextSwitchFirstIndex = 0;
5557
uint8_t CRSF::nextSwitchIndex = 0; // for round-robin sequential switches
5658

59+
uint8_t CRSF::modelId = 0;
60+
5761
volatile uint8_t CRSF::ParameterUpdateData[3] = {0};
5862
volatile bool CRSF::elrsLUAmode = false;
5963

@@ -184,11 +188,7 @@ void CRSF::flush_port_input(void)
184188
*/
185189
uint8_t ICACHE_RAM_ATTR CRSF::getNextSwitchIndex()
186190
{
187-
int firstSwitch = 0; // sequential switches includes switch 0
188-
189-
#if defined HYBRID_SWITCHES_8
190-
firstSwitch = 1; // skip 0 since it is sent on every packet
191-
#endif
191+
int firstSwitch = nextSwitchFirstIndex;
192192

193193
// look for a changed switch
194194
int i;
@@ -206,18 +206,19 @@ uint8_t ICACHE_RAM_ATTR CRSF::getNextSwitchIndex()
206206
// keep track of which switch to send next if there are no changed switches
207207
// during the next call.
208208
nextSwitchIndex = (i + 1) % 8;
209-
210-
#ifdef HYBRID_SWITCHES_8
211-
// for hydrid switches 0 is sent on every packet, skip it in round-robin
212209
if (nextSwitchIndex == 0)
213210
{
214-
nextSwitchIndex = 1;
211+
nextSwitchIndex = nextSwitchFirstIndex;
215212
}
216-
#endif
217213

218214
return i;
219215
}
220216

217+
void ICACHE_RAM_ATTR CRSF::setNextSwitchFirstIndex(int firstSwitchIndex)
218+
{
219+
nextSwitchFirstIndex = firstSwitchIndex;
220+
}
221+
221222
/**
222223
* Record the value of a switch that was sent to the rx
223224
*/
@@ -226,6 +227,11 @@ void ICACHE_RAM_ATTR CRSF::setSentSwitch(uint8_t index, uint8_t value)
226227
sentSwitches[index] = value;
227228
}
228229

230+
uint8_t ICACHE_RAM_ATTR CRSF::getModelID()
231+
{
232+
return modelId;
233+
}
234+
229235
#if CRSF_TX_MODULE
230236
void ICACHE_RAM_ATTR CRSF::sendLinkStatisticsToTX()
231237
{
@@ -711,11 +717,17 @@ bool ICACHE_RAM_ATTR CRSF::ProcessPacket()
711717
} else {
712718
elrsLUAmode = false;
713719
}
720+
if (packetType == CRSF_FRAMETYPE_COMMAND &&
721+
SerialInBuffer[5] == SUBCOMMAND_CRSF &&
722+
SerialInBuffer[6] == COMMAND_MODEL_SELECT_ID) {
723+
modelId = SerialInBuffer[7];
724+
RecvModelUpdate();
725+
return true;
726+
}
714727
ParameterUpdateData[0] = packetType;
715728
ParameterUpdateData[1] = SerialInBuffer[5];
716729
ParameterUpdateData[2] = SerialInBuffer[6];
717730
RecvParameterUpdate();
718-
719731
return true;
720732
}
721733
Serial.println("Got Other Packet");

src/lib/CRSF/CRSF.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,12 +45,17 @@ class CRSF
4545

4646
static uint8_t currentSwitches[N_SWITCHES];
4747
static uint8_t sentSwitches[N_SWITCHES];
48+
// index of the first switch to send in round-robin
49+
static uint8_t nextSwitchFirstIndex;
4850
// which switch should be sent in the next rc packet
4951
static uint8_t nextSwitchIndex;
52+
// The model ID as received from the Transmitter
53+
static uint8_t modelId;
5054

5155
static void (*disconnected)();
5256
static void (*connected)();
5357

58+
static void (*RecvModelUpdate)();
5459
static void (*RecvParameterUpdate)();
5560

5661
static volatile uint8_t ParameterUpdateData[3];
@@ -82,9 +87,12 @@ class CRSF
8287
static void ICACHE_RAM_ATTR sendSetVTXchannel(uint8_t band, uint8_t channel);
8388

8489
uint8_t ICACHE_RAM_ATTR getNextSwitchIndex();
90+
void ICACHE_RAM_ATTR setNextSwitchFirstIndex(int firstSwitchIndex);
8591
void ICACHE_RAM_ATTR setSentSwitch(uint8_t index, uint8_t value);
8692

87-
///// Variables for OpenTX Syncing //////////////////////////
93+
uint8_t ICACHE_RAM_ATTR getModelID();
94+
95+
///// Variables for OpenTX Syncing //////////////////////////
8896
#define OpenTXsyncPacketInterval 200 // in ms
8997
static void ICACHE_RAM_ATTR setSyncParams(uint32_t PacketInterval);
9098
static void ICACHE_RAM_ATTR JustSentRFpacket();

src/lib/CrsfProtocol/crsf_protocol.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,14 @@ typedef enum
120120
CRSF_FRAMETYPE_MSP_WRITE = 0x7C, // write with 8 byte chunked binary (OpenTX outbound telemetry buffer limit)
121121
} crsf_frame_type_e;
122122

123+
typedef enum {
124+
SUBCOMMAND_CRSF = 0x10
125+
} crsf_command_e;
126+
127+
typedef enum {
128+
COMMAND_MODEL_SELECT_ID = 0x05
129+
} crsf_subcommand_e;
130+
123131
enum {
124132
CRSF_FRAME_TX_MSP_FRAME_SIZE = 58,
125133
CRSF_FRAME_RX_MSP_FRAME_SIZE = 8,

src/lib/MSP/msptypes.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
#define MSP_ELRS_FUNC 0x4578 // ['E','x']
44

5+
#define MSP_SET_RX_CONFIG 45
56
#define MSP_VTX_CONFIG 88 //out message Get vtx settings - betaflight
67
#define MSP_SET_VTX_CONFIG 89 //in message Set vtx settings - betaflight
78

@@ -10,6 +11,7 @@
1011
#define MSP_ELRS_TX_PWR 0x07
1112
#define MSP_ELRS_TLM_RATE 0x08
1213
#define MSP_ELRS_BIND 0x09
14+
#define MSP_ELRS_MODEL_ID 0x0A
1315

1416
// CRSF encapsulated msp defines
1517
#define ENCAPSULATED_MSP_PAYLOAD_SIZE 4

src/lib/OTA/OTA.cpp

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010

1111
#if TARGET_TX or defined UNIT_TEST
1212

13-
#if defined HYBRID_SWITCHES_8 or defined UNIT_TEST
1413
/**
1514
* Hybrid switches packet encoding for sending over the air
1615
*
@@ -25,11 +24,7 @@
2524
* Inputs: crsf.ChannelDataIn, crsf.currentSwitches
2625
* Outputs: Radio.TXdataBuffer, side-effects the sentSwitch value
2726
*/
28-
#ifdef ENABLE_TELEMETRY
2927
void ICACHE_RAM_ATTR GenerateChannelDataHybridSwitch8(volatile uint8_t* Buffer, CRSF *crsf, bool TelemetryStatus)
30-
#else
31-
void ICACHE_RAM_ATTR GenerateChannelDataHybridSwitch8(volatile uint8_t* Buffer, CRSF *crsf)
32-
#endif
3328
{
3429
Buffer[0] = RC_DATA_PACKET & 0b11;
3530
Buffer[1] = ((crsf->ChannelDataIn[0]) >> 3);
@@ -52,9 +47,7 @@ void ICACHE_RAM_ATTR GenerateChannelDataHybridSwitch8(volatile uint8_t* Buffer,
5247
uint8_t value = crsf->currentSwitches[nextSwitchIndex];
5348

5449
Buffer[6] =
55-
#ifdef ENABLE_TELEMETRY
5650
TelemetryStatus << 7 |
57-
#endif
5851
// switch 0 is one bit sent on every packet - intended for low latency arm/disarm
5952
crsf->currentSwitches[0] << 6 |
6053
// tell the receiver which switch index this is
@@ -65,10 +58,8 @@ void ICACHE_RAM_ATTR GenerateChannelDataHybridSwitch8(volatile uint8_t* Buffer,
6558
// update the sent value
6659
crsf->setSentSwitch(nextSwitchIndex, value);
6760
}
68-
#endif // HYBRID_SWITCHES_8
6961

70-
#if !defined HYBRID_SWITCHES_8 or defined UNIT_TEST
71-
void ICACHE_RAM_ATTR GenerateChannelData10bit(volatile uint8_t* Buffer, CRSF *crsf)
62+
void ICACHE_RAM_ATTR GenerateChannelData10bit(volatile uint8_t* Buffer, CRSF *crsf, bool TelemetryStatus)
7263
{
7364
Buffer[0] = RC_DATA_PACKET & 0b11;
7465
Buffer[1] = ((crsf->ChannelDataIn[0]) >> 3);
@@ -88,7 +79,6 @@ void ICACHE_RAM_ATTR GenerateChannelData10bit(volatile uint8_t* Buffer, CRSF *cr
8879
Buffer[6] |= CRSF_to_BIT(crsf->ChannelDataIn[10]) << 1;
8980
Buffer[6] |= CRSF_to_BIT(crsf->ChannelDataIn[11]) << 0;
9081
}
91-
#endif // !HYBRID_SWITCHES_8
9282

9383
#endif
9484

src/lib/OTA/OTA.h

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -14,23 +14,8 @@
1414
#define SYNC_PACKET 0b10
1515

1616
#if TARGET_TX or defined UNIT_TEST
17-
#if defined HYBRID_SWITCHES_8 or defined UNIT_TEST
18-
#ifdef ENABLE_TELEMETRY
1917
void ICACHE_RAM_ATTR GenerateChannelDataHybridSwitch8(volatile uint8_t* Buffer, CRSF *crsf, bool TelemetryStatus);
20-
#else
21-
void ICACHE_RAM_ATTR GenerateChannelDataHybridSwitch8(volatile uint8_t* Buffer, CRSF *crsf);
22-
#endif
23-
#endif
24-
25-
#if !defined HYBRID_SWITCHES_8 or defined UNIT_TEST
26-
void ICACHE_RAM_ATTR GenerateChannelData10bit(volatile uint8_t* Buffer, CRSF *crsf);
27-
#endif
28-
29-
#if defined HYBRID_SWITCHES_8
30-
#define GenerateChannelData GenerateChannelDataHybridSwitch8
31-
#else
32-
#define GenerateChannelData GenerateChannelData10bit
33-
#endif
18+
void ICACHE_RAM_ATTR GenerateChannelData10bit(volatile uint8_t* Buffer, CRSF *crsf, bool TelemetryStatus);
3419
#endif
3520

3621
#if TARGET_RX or defined UNIT_TEST

src/lib/STM32F3_WS2812B/dummy.c

Whitespace-only changes.

0 commit comments

Comments
 (0)