Skip to content

Commit e00bd09

Browse files
committed
Add Spektrum satellite serial protocol
1 parent c91185f commit e00bd09

File tree

10 files changed

+182
-15
lines changed

10 files changed

+182
-15
lines changed

src/html/index.html

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -212,24 +212,28 @@ <h2>Serial Protocol</h2>
212212
<option value='4'>SUMD</option>
213213
<option value='5'>DJI RS Pro</option>
214214
<option value='6'>HoTT Telemetry</option>
215+
<option value='7'>DSMX 11MS</option>
216+
<option value='8'>DSMX 22MS</option>
217+
<option value='9'>DSM2 11MS</option>
218+
<option value='10'>DSM2 22MS</option>
215219
</select>
216220
<label for='serial-protocol'>Serial Protocol</label>
217221
</div>
218222
</div>
219-
<div id="sbus-config" style="display: none;">
220-
<h2>SBUS Failsafe</h2>
221-
Set the failsafe behaviour when using the SBUS protocol:<br/>
223+
<div id="failsafe-config" style="display: none;">
224+
<h2>Failsafe</h2>
225+
Set the failsafe behaviour when using the SBUS or Spektrum remote receiver protocol:<br/>
222226
<ul>
223-
<li>"No Pulses" stops sending SBUS data when a connection to the transmitter is lost</li>
227+
<li>"No Pulses" stops sending data when a connection to the transmitter is lost</li>
224228
<li>"Last Position" continues to send the last received channel data along with the FAILSAFE bit set</li>
225229
</ul>
226230
<br/>
227231
<div class="mui-select">
228-
<select id='sbus-failsafe' name='serial-failsafe'>
232+
<select id='serial-failsafe' name='serial-failsafe'>
229233
<option value='0'>No Pulses</option>
230234
<option value='1'>Last Position</option>
231235
</select>
232-
<label for="sbus-failsafe">SBUS Failsafe</label>
236+
<label for="serial-failsafe">Failsafe</label>
233237
</div>
234238
</div>
235239

src/html/scan.js

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -314,8 +314,8 @@ function updateConfig(data, options) {
314314
else if (proto === 2 || proto === 3 || proto === 5) { // SBUS (and inverted) or DJI-RS Pro
315315
_('rcvr-uart-baud').disabled = true;
316316
_('rcvr-uart-baud').value = '100000';
317-
_('sbus-config').style.display = 'block';
318-
_('sbus-failsafe').value = data['sbus-failsafe'];
317+
_('failsafe-config').style.display = 'block';
318+
_('serial-failsafe').value = data['serial-failsafe'];
319319
}
320320
else if (proto === 4) { // SUMD
321321
_('rcvr-uart-baud').disabled = true;
@@ -327,6 +327,12 @@ function updateConfig(data, options) {
327327
_('rcvr-uart-baud').value = '19200';
328328
_('sbus-config').style.display = 'none';
329329
}
330+
else if (proto >= 7 && proto <= 10) { // Spektrum Remote Receiver
331+
_('rcvr-uart-baud').disabled = true;
332+
_('rcvr-uart-baud').value = '115200';
333+
_('failsafe-config').style.display = 'block';
334+
_('serial-failsafe').value = data['serial-failsafe'];
335+
}
330336
}
331337
updatePwmSettings(data.pwm);
332338
_('serial-protocol').value = data['serial-protocol'];
@@ -611,7 +617,7 @@ if (_('config')) {
611617
return JSON.stringify({
612618
"pwm": getPwmFormData(),
613619
"serial-protocol": +_('serial-protocol').value,
614-
"sbus-failsafe": +_('sbus-failsafe').value,
620+
"serial-failsafe": +_('serial-failsafe').value,
615621
"modelid": +_('modelid').value,
616622
"force-tlm": +_('force-tlm').checked
617623
});

src/include/common.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,11 @@ enum eSerialProtocol : uint8_t
190190
PROTOCOL_INVERTED_SBUS,
191191
PROTOCOL_SUMD,
192192
PROTOCOL_DJI_RS_PRO,
193-
PROTOCOL_HOTT_TLM
193+
PROTOCOL_HOTT_TLM,
194+
PROTOCOL_SPEKTRUM_REMOTE_DSMX_11MS,
195+
PROTOCOL_SPEKTRUM_REMOTE_DSMX_22MS,
196+
PROTOCOL_SPEKTRUM_REMOTE_DSM2_11MS,
197+
PROTOCOL_SPEKTRUM_REMOTE_DSM2_22MS,
194198
};
195199

196200
enum eFailsafeMode : uint8_t

src/lib/CONFIG/config.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1087,4 +1087,5 @@ void RxConfig::SetFailsafeMode(eFailsafeMode failsafeMode)
10871087
m_modified = true;
10881088
}
10891089
}
1090+
10901091
#endif

src/lib/LUA/rx_devLUA.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ static char pwmModes[] = "50Hz;60Hz;100Hz;160Hz;333Hz;400Hz;10kHzDuty;On/Off;DSh
1818
static struct luaItem_selection luaSerialProtocol = {
1919
{"Protocol", CRSF_TEXT_SELECTION},
2020
0, // value
21-
"CRSF;Inverted CRSF;SBUS;Inverted SBUS;SUMD;DJI RS Pro;HoTT Telemetry",
21+
"CRSF;Inverted CRSF;SBUS;Inverted SBUS;SUMD;DJI RS Pro;HoTT Telemetry;DSMX 11MS;DSMX 22MS;DSM2 11MS;DSM2 22MS",
2222
STR_EMPTYSPACE
2323
};
2424

@@ -348,7 +348,11 @@ static void registerLuaParameters()
348348
}
349349
});
350350

351-
if (config.GetSerialProtocol() == PROTOCOL_SBUS || config.GetSerialProtocol() == PROTOCOL_INVERTED_SBUS || config.GetSerialProtocol() == PROTOCOL_DJI_RS_PRO)
351+
if (config.GetSerialProtocol() == PROTOCOL_SBUS ||
352+
config.GetSerialProtocol() == PROTOCOL_INVERTED_SBUS ||
353+
config.GetSerialProtocol() == PROTOCOL_DJI_RS_PRO ||
354+
(config.GetSerialProtocol() >= PROTOCOL_SPEKTRUM_REMOTE_DSMX_11MS &&
355+
config.GetSerialProtocol() <= PROTOCOL_SPEKTRUM_REMOTE_DSM2_22MS))
352356
{
353357
registerLUAParameter(&luaFailsafeMode, [](struct luaPropertiesCommon* item, uint8_t arg){
354358
config.SetFailsafeMode((eFailsafeMode)arg);

src/lib/WIFI/devWIFI.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -333,7 +333,7 @@ static void GetConfiguration(AsyncWebServerRequest *request)
333333
json["config"]["mode"] = wifiMode == WIFI_STA ? "STA" : "AP";
334334
#if defined(TARGET_RX)
335335
json["config"]["serial-protocol"] = config.GetSerialProtocol();
336-
json["config"]["sbus-failsafe"] = config.GetFailsafeMode();
336+
json["config"]["serial-failsafe"] = config.GetFailsafeMode();
337337
json["config"]["modelid"] = config.GetModelId();
338338
json["config"]["force-tlm"] = config.GetForceTlmOff();
339339
#if defined(GPIO_PIN_PWM_OUTPUTS)
@@ -468,7 +468,7 @@ static void UpdateConfiguration(AsyncWebServerRequest *request, JsonVariant &jso
468468
DBGLN("Setting serial protocol %u", protocol);
469469
config.SetSerialProtocol((eSerialProtocol)protocol);
470470

471-
uint8_t failsafe = json["sbus-failsafe"] | 0;
471+
uint8_t failsafe = json["serial-failsafe"] | 0;
472472
DBGLN("Setting SBUS failsafe mode %u", failsafe);
473473
config.SetFailsafeMode((eFailsafeMode)failsafe);
474474

src/python/serve_html.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@
7171
}
7272
],
7373
"serial-protocol": 3,
74-
"sbus-failsafe": 0,
74+
"serial-failsafe": 0,
7575
"product_name": "Generic ESP8285 + 5xPWM 2.4Ghz RX",
7676
"lua_name": "ELRS+PWM 2400RX",
7777
"reg_domain": "ISM2G4",

src/src/rx-serial/SerialSatellite.cpp

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
#include "SerialSatellite.h"
2+
#include "CRSF.h"
3+
#include "config.h"
4+
#include "device.h"
5+
6+
#if defined(TARGET_RX)
7+
8+
static uint16_t crsfToSatellite(uint16_t crsfValue, uint8_t satelliteChannel, eSerialProtocol protocol)
9+
{
10+
static constexpr uint16_t SATELLITE_MIN_US = 903;
11+
static constexpr uint16_t SATELLITE_MAX_US = 2097;
12+
13+
const bool isDsm2_22ms = (protocol == PROTOCOL_SPEKTRUM_REMOTE_DSM2_22MS);
14+
15+
// Map the channel data.
16+
const uint16_t us = constrain(CRSF_to_US(crsfValue), SATELLITE_MIN_US, SATELLITE_MAX_US);
17+
const float divisor = isDsm2_22ms ? 1.166f : 0.583f;
18+
19+
uint16_t channelValue = roundf((us - SATELLITE_MIN_US) / divisor);
20+
21+
// Encode the channel information.
22+
channelValue |= satelliteChannel << (isDsm2_22ms ? 10 : 11);
23+
24+
#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
25+
return __builtin_bswap16(channelValue);
26+
#else
27+
return channelValue;
28+
#endif // __BYTE_ORDER__
29+
}
30+
31+
uint32_t SerialSatellite::sendRCFrame(bool frameAvailable, bool frameMissed, uint32_t *channelData)
32+
{
33+
const eSerialProtocol protocol = config.GetSerialProtocol();
34+
const uint32_t callbackIntervalMs =
35+
((protocol == PROTOCOL_SPEKTRUM_REMOTE_DSMX_11MS) ||
36+
(protocol == PROTOCOL_SPEKTRUM_REMOTE_DSM2_11MS))
37+
? 11
38+
: 22;
39+
40+
if ((failsafe && config.GetFailsafeMode() == FAILSAFE_NO_PULSES) ||
41+
(!sendPackets && connectionState != connected) ||
42+
frameMissed)
43+
{
44+
// Fade count does not overflow.
45+
if (sendPackets && (fadeCount < UINT8_MAX))
46+
{
47+
++fadeCount;
48+
}
49+
return callbackIntervalMs;
50+
}
51+
sendPackets = true;
52+
53+
uint16_t outgoingPacket[SATELLITE_CHANNELS_PER_PACKET];
54+
55+
// Our bandwidth is limited, so try to send updates only for the channels
56+
// with new values to reduce the channel update latency. Updates are
57+
// checked in round-robin fashion to make sure that the channels do not
58+
// starve.
59+
for (uint8_t channelCount = 0, packetDataCount = 0;
60+
(channelCount < SATELLITE_MAX_CHANNELS) && (packetDataCount < SATELLITE_CHANNELS_PER_PACKET);
61+
++channelCount)
62+
{
63+
// If the channel data is updated, add it to the packet. Or, if the
64+
// space remaining in the packet is equal to the number of remaining
65+
// channels to be checked for updates, just fill the packet with the
66+
// remaining channels no matter if they have updates or not because we
67+
// need to send exactly 7 channel data in every packet.
68+
if ((prevChannelData[roundRobinIndex] != channelData[roundRobinIndex]) ||
69+
((SATELLITE_CHANNELS_PER_PACKET - packetDataCount) == (SATELLITE_MAX_CHANNELS - channelCount)))
70+
{
71+
prevChannelData[roundRobinIndex] = channelData[roundRobinIndex];
72+
outgoingPacket[packetDataCount++] =
73+
crsfToSatellite(channelData[roundRobinIndex],
74+
roundRobinIndex,
75+
protocol);
76+
}
77+
78+
++roundRobinIndex;
79+
if (roundRobinIndex >= SATELLITE_MAX_CHANNELS)
80+
{
81+
roundRobinIndex = 0;
82+
}
83+
}
84+
85+
uint8_t protocolValue{};
86+
switch (protocol)
87+
{
88+
case PROTOCOL_SPEKTRUM_REMOTE_DSMX_11MS:
89+
protocolValue = 0xB2;
90+
break;
91+
case PROTOCOL_SPEKTRUM_REMOTE_DSMX_22MS:
92+
protocolValue = 0xA2;
93+
break;
94+
case PROTOCOL_SPEKTRUM_REMOTE_DSM2_11MS:
95+
protocolValue = 0x12;
96+
break;
97+
default:
98+
protocolValue = 0x01;
99+
}
100+
101+
// Transmit the fade count.
102+
_outputPort->write(fadeCount);
103+
// Transmit the protocol in use.
104+
_outputPort->write(protocolValue);
105+
// Transmit the channel data.
106+
_outputPort->write(reinterpret_cast<uint8_t *>(outgoingPacket), sizeof(outgoingPacket));
107+
108+
return callbackIntervalMs;
109+
}
110+
111+
#endif // defined(TARGET_RX)

src/src/rx-serial/SerialSatellite.h

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#include "SerialIO.h"
2+
3+
class SerialSatellite : public SerialIO
4+
{
5+
public:
6+
SerialSatellite(Stream &out, Stream &in)
7+
: SerialIO(&out, &in) {}
8+
9+
~SerialSatellite() override = default;
10+
11+
void queueLinkStatisticsPacket() override {}
12+
void queueMSPFrameTransmission(uint8_t *data) override {}
13+
uint32_t sendRCFrame(bool frameAvailable, bool frameMissed, uint32_t *channelData) override;
14+
15+
private:
16+
static constexpr uint16_t SATELLITE_MAX_CHANNELS = 12;
17+
static constexpr uint8_t SATELLITE_CHANNELS_PER_PACKET = 7;
18+
19+
void processBytes(uint8_t *bytes, uint16_t size) override{};
20+
21+
uint32_t prevChannelData[SATELLITE_CHANNELS_PER_PACKET] = {0};
22+
23+
uint8_t fadeCount{0};
24+
uint8_t roundRobinIndex{0};
25+
bool sendPackets{false};
26+
};

src/src/rx_main.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include "rx-serial/SerialSUMD.h"
2424
#include "rx-serial/SerialAirPort.h"
2525
#include "rx-serial/SerialHoTT_TLM.h"
26+
#include "rx-serial/SerialSatellite.h"
2627

2728
#include "rx-serial/devSerialIO.h"
2829
#include "devLED.h"
@@ -1186,6 +1187,7 @@ static void setupSerial()
11861187
{
11871188
bool sbusSerialOutput = false;
11881189
bool sumdSerialOutput = false;
1190+
bool spektrumSatelliteSerialOutput = false;
11891191

11901192
#if defined(PLATFORM_ESP8266) || defined(PLATFORM_ESP32)
11911193
bool hottTlmSerial = false;
@@ -1227,6 +1229,11 @@ static void setupSerial()
12271229
serialBaud = 19200;
12281230
}
12291231
#endif
1232+
else if (config.GetSerialProtocol() >= PROTOCOL_SPEKTRUM_REMOTE_DSMX_11MS && config.GetSerialProtocol() <= PROTOCOL_SPEKTRUM_REMOTE_DSM2_22MS)
1233+
{
1234+
spektrumSatelliteSerialOutput = true;
1235+
serialBaud = 115200;
1236+
}
12301237
bool invert = config.GetSerialProtocol() == PROTOCOL_SBUS || config.GetSerialProtocol() == PROTOCOL_INVERTED_CRSF || config.GetSerialProtocol() == PROTOCOL_DJI_RS_PRO;
12311238

12321239
#ifdef PLATFORM_STM32
@@ -1327,6 +1334,10 @@ static void setupSerial()
13271334
serialIO = new SerialHoTT_TLM(SERIAL_PROTOCOL_TX, SERIAL_PROTOCOL_RX);
13281335
}
13291336
#endif
1337+
else if (spektrumSatelliteSerialOutput)
1338+
{
1339+
serialIO = new SerialSatellite(SERIAL_PROTOCOL_TX, SERIAL_PROTOCOL_RX);
1340+
}
13301341
else
13311342
{
13321343
serialIO = new SerialCRSF(SERIAL_PROTOCOL_TX, SERIAL_PROTOCOL_RX);

0 commit comments

Comments
 (0)