Skip to content

Add Spektrum satellite serial protocol #2419

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Add Spektrum satellite serial protocol
  • Loading branch information
dkorkmazturk committed Feb 6, 2024
commit 7f164c33eef13336d2c0f45977f9b8bbe319e567
16 changes: 10 additions & 6 deletions src/html/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -223,24 +223,28 @@ <h2>Serial Protocol</h2>
<option value='4'>SUMD</option>
<option value='5'>DJI RS Pro</option>
<option value='6'>HoTT Telemetry</option>
<option value='7'>DSMX 11MS</option>
<option value='8'>DSMX 22MS</option>
<option value='9'>DSM2 11MS</option>
<option value='10'>DSM2 22MS</option>
</select>
<label for='serial-protocol'>Serial Protocol</label>
</div>
</div>
<div id="sbus-config" style="display: none;">
<h2>SBUS Failsafe</h2>
Set the failsafe behaviour when using the SBUS protocol:<br/>
<div id="failsafe-config" style="display: none;">
<h2>Failsafe</h2>
Set the failsafe behaviour when using the SBUS or Spektrum remote receiver protocol:<br/>
<ul>
<li>"No Pulses" stops sending SBUS data when a connection to the transmitter is lost</li>
<li>"No Pulses" stops sending data when a connection to the transmitter is lost</li>
<li>"Last Position" continues to send the last received channel data along with the FAILSAFE bit set</li>
</ul>
<br/>
<div class="mui-select">
<select id='sbus-failsafe' name='serial-failsafe'>
<select id='serial-failsafe' name='serial-failsafe'>
<option value='0'>No Pulses</option>
<option value='1'>Last Position</option>
</select>
<label for="sbus-failsafe">SBUS Failsafe</label>
<label for="serial-failsafe">Failsafe</label>
</div>
</div>

Expand Down
12 changes: 9 additions & 3 deletions src/html/scan.js
Original file line number Diff line number Diff line change
Expand Up @@ -337,8 +337,8 @@ function updateConfig(data, options) {
else if (proto === 2 || proto === 3 || proto === 5) { // SBUS (and inverted) or DJI-RS Pro
_('rcvr-uart-baud').disabled = true;
_('rcvr-uart-baud').value = '100000';
_('sbus-config').style.display = 'block';
_('sbus-failsafe').value = data['sbus-failsafe'];
_('failsafe-config').style.display = 'block';
_('serial-failsafe').value = data['serial-failsafe'];
}
else if (proto === 4) { // SUMD
_('rcvr-uart-baud').disabled = true;
Expand All @@ -350,6 +350,12 @@ function updateConfig(data, options) {
_('rcvr-uart-baud').value = '19200';
_('sbus-config').style.display = 'none';
}
else if (proto >= 7 && proto <= 10) { // Spektrum Remote Receiver
_('rcvr-uart-baud').disabled = true;
_('rcvr-uart-baud').value = '115200';
_('failsafe-config').style.display = 'block';
_('serial-failsafe').value = data['serial-failsafe'];
}
}
updatePwmSettings(data.pwm);
_('serial-protocol').value = data['serial-protocol'];
Expand Down Expand Up @@ -634,7 +640,7 @@ if (_('config')) {
return JSON.stringify({
"pwm": getPwmFormData(),
"serial-protocol": +_('serial-protocol').value,
"sbus-failsafe": +_('sbus-failsafe').value,
"serial-failsafe": +_('serial-failsafe').value,
"modelid": +_('modelid').value,
"force-tlm": +_('force-tlm').checked
});
Expand Down
6 changes: 5 additions & 1 deletion src/include/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,11 @@ enum eSerialProtocol : uint8_t
PROTOCOL_INVERTED_SBUS,
PROTOCOL_SUMD,
PROTOCOL_DJI_RS_PRO,
PROTOCOL_HOTT_TLM
PROTOCOL_HOTT_TLM,
PROTOCOL_SPEKTRUM_REMOTE_DSMX_11MS,
PROTOCOL_SPEKTRUM_REMOTE_DSMX_22MS,
PROTOCOL_SPEKTRUM_REMOTE_DSM2_11MS,
PROTOCOL_SPEKTRUM_REMOTE_DSM2_22MS,
};

enum eFailsafeMode : uint8_t
Expand Down
1 change: 1 addition & 0 deletions src/lib/CONFIG/config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1087,4 +1087,5 @@ void RxConfig::SetFailsafeMode(eFailsafeMode failsafeMode)
m_modified = true;
}
}

#endif
8 changes: 6 additions & 2 deletions src/lib/LUA/rx_devLUA.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ static char pwmModes[] = "50Hz;60Hz;100Hz;160Hz;333Hz;400Hz;10kHzDuty;On/Off;DSh
static struct luaItem_selection luaSerialProtocol = {
{"Protocol", CRSF_TEXT_SELECTION},
0, // value
"CRSF;Inverted CRSF;SBUS;Inverted SBUS;SUMD;DJI RS Pro;HoTT Telemetry",
"CRSF;Inverted CRSF;SBUS;Inverted SBUS;SUMD;DJI RS Pro;HoTT Telemetry;DSMX 11MS;DSMX 22MS;DSM2 11MS;DSM2 22MS",
STR_EMPTYSPACE
};

Expand Down Expand Up @@ -348,7 +348,11 @@ static void registerLuaParameters()
}
});

if (config.GetSerialProtocol() == PROTOCOL_SBUS || config.GetSerialProtocol() == PROTOCOL_INVERTED_SBUS || config.GetSerialProtocol() == PROTOCOL_DJI_RS_PRO)
if (config.GetSerialProtocol() == PROTOCOL_SBUS ||
config.GetSerialProtocol() == PROTOCOL_INVERTED_SBUS ||
config.GetSerialProtocol() == PROTOCOL_DJI_RS_PRO ||
(config.GetSerialProtocol() >= PROTOCOL_SPEKTRUM_REMOTE_DSMX_11MS &&
config.GetSerialProtocol() <= PROTOCOL_SPEKTRUM_REMOTE_DSM2_22MS))
{
registerLUAParameter(&luaFailsafeMode, [](struct luaPropertiesCommon* item, uint8_t arg){
config.SetFailsafeMode((eFailsafeMode)arg);
Expand Down
4 changes: 2 additions & 2 deletions src/lib/WIFI/devWIFI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -333,7 +333,7 @@ static void GetConfiguration(AsyncWebServerRequest *request)
json["config"]["mode"] = wifiMode == WIFI_STA ? "STA" : "AP";
#if defined(TARGET_RX)
json["config"]["serial-protocol"] = config.GetSerialProtocol();
json["config"]["sbus-failsafe"] = config.GetFailsafeMode();
json["config"]["serial-failsafe"] = config.GetFailsafeMode();
json["config"]["modelid"] = config.GetModelId();
json["config"]["force-tlm"] = config.GetForceTlmOff();
#if defined(GPIO_PIN_PWM_OUTPUTS)
Expand Down Expand Up @@ -468,7 +468,7 @@ static void UpdateConfiguration(AsyncWebServerRequest *request, JsonVariant &jso
DBGLN("Setting serial protocol %u", protocol);
config.SetSerialProtocol((eSerialProtocol)protocol);

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

Expand Down
2 changes: 1 addition & 1 deletion src/python/serve_html.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@
}
],
"serial-protocol": 3,
"sbus-failsafe": 0,
"serial-failsafe": 0,
"product_name": "Generic ESP8285 + 5xPWM 2.4Ghz RX",
"lua_name": "ELRS+PWM 2400RX",
"reg_domain": "ISM2G4",
Expand Down
111 changes: 111 additions & 0 deletions src/src/rx-serial/SerialSatellite.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
#include "SerialSatellite.h"
#include "CRSF.h"
#include "config.h"
#include "device.h"

#if defined(TARGET_RX)

static uint16_t crsfToSatellite(uint16_t crsfValue, uint8_t satelliteChannel, eSerialProtocol protocol)
{
static constexpr uint16_t SATELLITE_MIN_US = 903;
static constexpr uint16_t SATELLITE_MAX_US = 2097;

const bool isDsm2_22ms = (protocol == PROTOCOL_SPEKTRUM_REMOTE_DSM2_22MS);

// Map the channel data.
const uint16_t us = constrain(CRSF_to_US(crsfValue), SATELLITE_MIN_US, SATELLITE_MAX_US);
const float divisor = isDsm2_22ms ? 1.166f : 0.583f;

uint16_t channelValue = roundf((us - SATELLITE_MIN_US) / divisor);

// Encode the channel information.
channelValue |= satelliteChannel << (isDsm2_22ms ? 10 : 11);

#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
return __builtin_bswap16(channelValue);
#else
return channelValue;
#endif // __BYTE_ORDER__
}

uint32_t SerialSatellite::sendRCFrame(bool frameAvailable, bool frameMissed, uint32_t *channelData)
{
const eSerialProtocol protocol = config.GetSerialProtocol();
const uint32_t callbackIntervalMs =
((protocol == PROTOCOL_SPEKTRUM_REMOTE_DSMX_11MS) ||
(protocol == PROTOCOL_SPEKTRUM_REMOTE_DSM2_11MS))
? 11
: 22;

if ((failsafe && config.GetFailsafeMode() == FAILSAFE_NO_PULSES) ||
(!sendPackets && connectionState != connected) ||
frameMissed)
{
// Fade count does not overflow.
if (sendPackets && (fadeCount < UINT8_MAX))
{
++fadeCount;
}
return callbackIntervalMs;
}
sendPackets = true;

uint16_t outgoingPacket[SATELLITE_CHANNELS_PER_PACKET];

// Our bandwidth is limited, so try to send updates only for the channels
// with new values to reduce the channel update latency. Updates are
// checked in round-robin fashion to make sure that the channels do not
// starve.
for (uint8_t channelCount = 0, packetDataCount = 0;
(channelCount < SATELLITE_MAX_CHANNELS) && (packetDataCount < SATELLITE_CHANNELS_PER_PACKET);
++channelCount)
{
// If the channel data is updated, add it to the packet. Or, if the
// space remaining in the packet is equal to the number of remaining
// channels to be checked for updates, just fill the packet with the
// remaining channels no matter if they have updates or not because we
// need to send exactly 7 channel data in every packet.
if ((prevChannelData[roundRobinIndex] != channelData[roundRobinIndex]) ||
((SATELLITE_CHANNELS_PER_PACKET - packetDataCount) == (SATELLITE_MAX_CHANNELS - channelCount)))
{
prevChannelData[roundRobinIndex] = channelData[roundRobinIndex];
outgoingPacket[packetDataCount++] =
crsfToSatellite(channelData[roundRobinIndex],
roundRobinIndex,
protocol);
}

++roundRobinIndex;
if (roundRobinIndex >= SATELLITE_MAX_CHANNELS)
{
roundRobinIndex = 0;
}
}

uint8_t protocolValue{};
switch (protocol)
{
case PROTOCOL_SPEKTRUM_REMOTE_DSMX_11MS:
protocolValue = 0xB2;
break;
case PROTOCOL_SPEKTRUM_REMOTE_DSMX_22MS:
protocolValue = 0xA2;
break;
case PROTOCOL_SPEKTRUM_REMOTE_DSM2_11MS:
protocolValue = 0x12;
break;
default:
protocolValue = 0x01;
}

// Transmit the fade count.
_outputPort->write(fadeCount);
// Transmit the protocol in use.
_outputPort->write(protocolValue);
// Transmit the channel data.
_outputPort->write(reinterpret_cast<uint8_t *>(outgoingPacket), sizeof(outgoingPacket));

return callbackIntervalMs;
}

#endif // defined(TARGET_RX)
26 changes: 26 additions & 0 deletions src/src/rx-serial/SerialSatellite.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#include "SerialIO.h"

class SerialSatellite : public SerialIO
{
public:
SerialSatellite(Stream &out, Stream &in)
: SerialIO(&out, &in) {}

~SerialSatellite() override = default;

void queueLinkStatisticsPacket() override {}
void queueMSPFrameTransmission(uint8_t *data) override {}
uint32_t sendRCFrame(bool frameAvailable, bool frameMissed, uint32_t *channelData) override;

private:
static constexpr uint16_t SATELLITE_MAX_CHANNELS = 12;
static constexpr uint8_t SATELLITE_CHANNELS_PER_PACKET = 7;

void processBytes(uint8_t *bytes, uint16_t size) override{};

uint32_t prevChannelData[SATELLITE_CHANNELS_PER_PACKET] = {0};

uint8_t fadeCount{0};
uint8_t roundRobinIndex{0};
bool sendPackets{false};
};
11 changes: 11 additions & 0 deletions src/src/rx_main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include "rx-serial/SerialSUMD.h"
#include "rx-serial/SerialAirPort.h"
#include "rx-serial/SerialHoTT_TLM.h"
#include "rx-serial/SerialSatellite.h"

#include "rx-serial/devSerialIO.h"
#include "devLED.h"
Expand Down Expand Up @@ -1186,6 +1187,7 @@ static void setupSerial()
{
bool sbusSerialOutput = false;
bool sumdSerialOutput = false;
bool spektrumSatelliteSerialOutput = false;

#if defined(PLATFORM_ESP8266) || defined(PLATFORM_ESP32)
bool hottTlmSerial = false;
Expand Down Expand Up @@ -1227,6 +1229,11 @@ static void setupSerial()
serialBaud = 19200;
}
#endif
else if (config.GetSerialProtocol() >= PROTOCOL_SPEKTRUM_REMOTE_DSMX_11MS && config.GetSerialProtocol() <= PROTOCOL_SPEKTRUM_REMOTE_DSM2_22MS)
{
spektrumSatelliteSerialOutput = true;
serialBaud = 115200;
}
bool invert = config.GetSerialProtocol() == PROTOCOL_SBUS || config.GetSerialProtocol() == PROTOCOL_INVERTED_CRSF || config.GetSerialProtocol() == PROTOCOL_DJI_RS_PRO;

#ifdef PLATFORM_STM32
Expand Down Expand Up @@ -1327,6 +1334,10 @@ static void setupSerial()
serialIO = new SerialHoTT_TLM(SERIAL_PROTOCOL_TX, SERIAL_PROTOCOL_RX);
}
#endif
else if (spektrumSatelliteSerialOutput)
{
serialIO = new SerialSatellite(SERIAL_PROTOCOL_TX, SERIAL_PROTOCOL_RX);
}
else
{
serialIO = new SerialCRSF(SERIAL_PROTOCOL_TX, SERIAL_PROTOCOL_RX);
Expand Down