Skip to content

Commit 7f164c3

Browse files
committed
Add Spektrum satellite serial protocol
1 parent 9ecff46 commit 7f164c3

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
@@ -223,24 +223,28 @@ <h2>Serial Protocol</h2>
223223
<option value='4'>SUMD</option>
224224
<option value='5'>DJI RS Pro</option>
225225
<option value='6'>HoTT Telemetry</option>
226+
<option value='7'>DSMX 11MS</option>
227+
<option value='8'>DSMX 22MS</option>
228+
<option value='9'>DSM2 11MS</option>
229+
<option value='10'>DSM2 22MS</option>
226230
</select>
227231
<label for='serial-protocol'>Serial Protocol</label>
228232
</div>
229233
</div>
230-
<div id="sbus-config" style="display: none;">
231-
<h2>SBUS Failsafe</h2>
232-
Set the failsafe behaviour when using the SBUS protocol:<br/>
234+
<div id="failsafe-config" style="display: none;">
235+
<h2>Failsafe</h2>
236+
Set the failsafe behaviour when using the SBUS or Spektrum remote receiver protocol:<br/>
233237
<ul>
234-
<li>"No Pulses" stops sending SBUS data when a connection to the transmitter is lost</li>
238+
<li>"No Pulses" stops sending data when a connection to the transmitter is lost</li>
235239
<li>"Last Position" continues to send the last received channel data along with the FAILSAFE bit set</li>
236240
</ul>
237241
<br/>
238242
<div class="mui-select">
239-
<select id='sbus-failsafe' name='serial-failsafe'>
243+
<select id='serial-failsafe' name='serial-failsafe'>
240244
<option value='0'>No Pulses</option>
241245
<option value='1'>Last Position</option>
242246
</select>
243-
<label for="sbus-failsafe">SBUS Failsafe</label>
247+
<label for="serial-failsafe">Failsafe</label>
244248
</div>
245249
</div>
246250

src/html/scan.js

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -337,8 +337,8 @@ function updateConfig(data, options) {
337337
else if (proto === 2 || proto === 3 || proto === 5) { // SBUS (and inverted) or DJI-RS Pro
338338
_('rcvr-uart-baud').disabled = true;
339339
_('rcvr-uart-baud').value = '100000';
340-
_('sbus-config').style.display = 'block';
341-
_('sbus-failsafe').value = data['sbus-failsafe'];
340+
_('failsafe-config').style.display = 'block';
341+
_('serial-failsafe').value = data['serial-failsafe'];
342342
}
343343
else if (proto === 4) { // SUMD
344344
_('rcvr-uart-baud').disabled = true;
@@ -350,6 +350,12 @@ function updateConfig(data, options) {
350350
_('rcvr-uart-baud').value = '19200';
351351
_('sbus-config').style.display = 'none';
352352
}
353+
else if (proto >= 7 && proto <= 10) { // Spektrum Remote Receiver
354+
_('rcvr-uart-baud').disabled = true;
355+
_('rcvr-uart-baud').value = '115200';
356+
_('failsafe-config').style.display = 'block';
357+
_('serial-failsafe').value = data['serial-failsafe'];
358+
}
353359
}
354360
updatePwmSettings(data.pwm);
355361
_('serial-protocol').value = data['serial-protocol'];
@@ -634,7 +640,7 @@ if (_('config')) {
634640
return JSON.stringify({
635641
"pwm": getPwmFormData(),
636642
"serial-protocol": +_('serial-protocol').value,
637-
"sbus-failsafe": +_('sbus-failsafe').value,
643+
"serial-failsafe": +_('serial-failsafe').value,
638644
"modelid": +_('modelid').value,
639645
"force-tlm": +_('force-tlm').checked
640646
});

src/include/common.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,11 @@ enum eSerialProtocol : uint8_t
197197
PROTOCOL_INVERTED_SBUS,
198198
PROTOCOL_SUMD,
199199
PROTOCOL_DJI_RS_PRO,
200-
PROTOCOL_HOTT_TLM
200+
PROTOCOL_HOTT_TLM,
201+
PROTOCOL_SPEKTRUM_REMOTE_DSMX_11MS,
202+
PROTOCOL_SPEKTRUM_REMOTE_DSMX_22MS,
203+
PROTOCOL_SPEKTRUM_REMOTE_DSM2_11MS,
204+
PROTOCOL_SPEKTRUM_REMOTE_DSM2_22MS,
201205
};
202206

203207
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)