Skip to content
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

Add MAVLink support to Backpack #141

Merged
merged 24 commits into from
Aug 3, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
2fc9771
Add MAVLink to Backpack
MUSTARDTIGERFPV Jul 18, 2024
c27a5af
Require multiple heartbeats to go to MAVLink mode
MUSTARDTIGERFPV Jul 18, 2024
9a0ba91
Better way to pull UID
MUSTARDTIGERFPV Jul 18, 2024
12f7077
Fix build, add more HTTP info
MUSTARDTIGERFPV Jul 19, 2024
a5638d6
Actually fix build (he says)
MUSTARDTIGERFPV Jul 19, 2024
f5b0982
Fix Debug builds
MUSTARDTIGERFPV Jul 19, 2024
c6d1f1f
Maybe actually fix build
MUSTARDTIGERFPV Jul 19, 2024
fc28ec2
Move ifdefs to the right section
MUSTARDTIGERFPV Jul 19, 2024
dbfff20
Move closing paren (surely this will build)
MUSTARDTIGERFPV Jul 19, 2024
c39a340
Make name consistent
MUSTARDTIGERFPV Jul 19, 2024
70325fd
Refactor WiFi MAVLink packet handling and counters
MUSTARDTIGERFPV Jul 24, 2024
ccd5c55
Fixes packet drops & adds some counters - still a few changes to make
MUSTARDTIGERFPV Jul 25, 2024
c0c727b
Clean up stats, add bidirectional framing
MUSTARDTIGERFPV Jul 25, 2024
d243c74
Remove uplink framing, clean up stats
MUSTARDTIGERFPV Jul 25, 2024
2478f43
Merge remote-tracking branch 'origin' into backpack-mavlink
MUSTARDTIGERFPV Jul 28, 2024
ed9456f
Fix overflow behavior
MUSTARDTIGERFPV Jul 29, 2024
215fc7e
Let Backpack be taken out of WiFi mode by a MAVLink message
MUSTARDTIGERFPV Jul 29, 2024
996da62
Actually, let's use a COMMAND_INT type
MUSTARDTIGERFPV Jul 29, 2024
5297dee
Fix syntax
MUSTARDTIGERFPV Jul 29, 2024
b309fe5
Update magic number name
MUSTARDTIGERFPV Jul 29, 2024
3468cf5
Move shouldForward
MUSTARDTIGERFPV Jul 29, 2024
16b6eb8
Fix no-MAVLink build
MUSTARDTIGERFPV Jul 30, 2024
7aafb0c
Enable MAVLink support for ESP32-C3 TX backpacks
MUSTARDTIGERFPV Jul 30, 2024
721cfd2
Backpack now follows TX immediately when set to MAVLink mode
MUSTARDTIGERFPV Jul 30, 2024
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
5 changes: 3 additions & 2 deletions lib/BUTTON/devButton.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include <Arduino.h>
#include "common.h"
#include "device.h"
#include "config.h"

#if defined(PIN_BUTTON)
#include "logging.h"
Expand All @@ -9,7 +10,7 @@
static Button<PIN_BUTTON, false> button;

extern unsigned long rebootTime;
void RebootIntoWifi();
void RebootIntoWifi(wifi_service_t service);

static void shortPress()
{
Expand All @@ -19,7 +20,7 @@ static void shortPress()
}
else
{
RebootIntoWifi();
RebootIntoWifi(WIFI_SERVICE_UPDATE);
}
}

Expand Down
97 changes: 90 additions & 7 deletions lib/WIFI/devWIFI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,16 @@
#include "WebContent.h"

#include "config.h"

#if defined(MAVLINK_ENABLED)
#include "common/mavlink.h"
#endif
#if defined(TARGET_VRX_BACKPACK)
extern VrxBackpackConfig config;
extern bool sendRTCChangesToVrx;
#elif defined(TARGET_TX_BACKPACK)
extern TxBackpackConfig config;
extern wifi_service_t wifiService;
#elif defined(TARGET_TIMER_BACKPACK)
extern TimerBackpackConfig config;
#else
Expand All @@ -50,7 +55,8 @@ static const char *myHostname = "elrs_vrx";
static const char *wifi_ap_ssid = "ExpressLRS VRx Backpack";
#elif defined(TARGET_TX_BACKPACK)
static const char *myHostname = "elrs_txbp";
static const char *wifi_ap_ssid = "ExpressLRS TX Backpack";
static char wifi_ap_ssid[33]; // will be set depending on wifiService

#elif defined(TARGET_TIMER_BACKPACK)
static const char *myHostname = "elrs_timer";
static const char *wifi_ap_ssid = "ExpressLRS Timer Backpack";
Expand All @@ -74,6 +80,7 @@ static volatile unsigned long changeTime = 0;
static const byte DNS_PORT = 53;
static IPAddress apIP(10, 0, 0, 1);
static IPAddress netMsk(255, 255, 255, 0);
static IPAddress apBroadcast(10, 0, 0, 255);
static DNSServer dnsServer;

static AsyncWebServer server(80);
Expand All @@ -92,6 +99,16 @@ static AsyncEventSource logging("/logging");
static char logBuffer[256];
static int logPos = 0;

#if defined(MAVLINK_ENABLED)
// This is the port that we will listen for mavlink packets on
static constexpr uint16_t MAVLINK_PORT_LISTEN = 14555;
// This is the port that we will send mavlink packets to
static constexpr uint16_t MAVLINK_PORT_SEND = 14550;
WiFiUDP mavlinkUDP;
uint32_t mavlink_from_uart_counter = 0;
uint32_t mavlink_to_uart_counter = 0;
#endif

/** Is this an IP? */
static boolean isIp(String str)
{
Expand Down Expand Up @@ -447,6 +464,29 @@ static void WebUploadRTCUpdateHandler(AsyncWebServerRequest *request) {
#endif
}

static void WebMAVLinkHandler(AsyncWebServerRequest *request)
MUSTARDTIGERFPV marked this conversation as resolved.
Show resolved Hide resolved
{
#if defined(PLATFORM_ESP32)
DynamicJsonDocument json(32768);
#else
DynamicJsonDocument json(2048);
#endif
MUSTARDTIGERFPV marked this conversation as resolved.
Show resolved Hide resolved

json["counters"]["from_uart"] = mavlink_from_uart_counter;
json["counters"]["to_uart"] = mavlink_to_uart_counter;

#if defined(STM32_TX_BACKPACK)
json["stm32"] = "yes";
#endif
#if defined(AAT_BACKPACK)
WebAatAppendConfig(json);
#endif
MUSTARDTIGERFPV marked this conversation as resolved.
Show resolved Hide resolved

AsyncResponseStream *response = request->beginResponseStream("application/json");
serializeJson(json, *response);
request->send(response);
}

static void wifiOff()
{
wifiStarted = false;
Expand Down Expand Up @@ -586,6 +626,7 @@ static void startServices()
#if defined(AAT_BACKPACK)
WebAatInit(server);
#endif
server.on("/mavlink", HTTP_GET, WebMAVLinkHandler);
MUSTARDTIGERFPV marked this conversation as resolved.
Show resolved Hide resolved

server.on("/log.js", WebUpdateSendContent);
server.on("/log.html", WebUpdateSendContent);
Expand All @@ -600,6 +641,8 @@ static void startServices()

startMDNS();

mavlinkUDP.begin(MAVLINK_PORT_LISTEN);
MUSTARDTIGERFPV marked this conversation as resolved.
Show resolved Hide resolved

servicesStarted = true;
DBGLN("HTTPUpdateServer ready! Open http://%s.local in your browser", myHostname);
}
Expand Down Expand Up @@ -643,6 +686,18 @@ static void HandleWebUpdate()
#endif
changeTime = now;
WiFi.softAPConfig(apIP, apIP, netMsk);
#if defined(TARGET_TX_BACKPACK)
if (wifiService == WIFI_SERVICE_UPDATE) {
strcpy(wifi_ap_ssid, "ExpressLRS TX Backpack");
} else if (wifiService == WIFI_SERVICE_MAVLINK_TX) {
// Generate a unique SSID using config.address as hex
sprintf(wifi_ap_ssid, "ExpressLRS TX %02X%02X%02X",
firmwareOptions.uid[3],
firmwareOptions.uid[4],
firmwareOptions.uid[5]
);
}
#endif
WiFi.softAP(wifi_ap_ssid, wifi_ap_password);
WiFi.scanNetworks(true);
startServices();
Expand All @@ -667,12 +722,40 @@ static void HandleWebUpdate()
if (servicesStarted)
{
while (Serial.available()) {
int val = Serial.read();
logBuffer[logPos++] = val;
logBuffer[logPos] = 0;
if (val == '\n' || logPos == sizeof(logBuffer)-1) {
logging.send(logBuffer);
logPos = 0;
if (wifiService == WIFI_SERVICE_UPDATE) {
int val = Serial.read();
logBuffer[logPos++] = val;
logBuffer[logPos] = 0;
if (val == '\n' || logPos == sizeof(logBuffer)-1) {
logging.send(logBuffer);
logPos = 0;
}
} else if (wifiService == WIFI_SERVICE_MAVLINK_TX) {
mavlink_message_t msg;
mavlink_status_t status;
char val = Serial.read();
if (mavlink_parse_char(MAVLINK_COMM_0, val, &msg, &status)) {
mavlink_from_uart_counter++;
uint8_t buf[MAVLINK_MAX_PACKET_LEN];
uint16_t len = mavlink_msg_to_send_buffer(buf, &msg);

// Determine the broadcast address
IPAddress broadcast = WiFi.getMode() == WIFI_STA ? WiFi.broadcastIP() : apBroadcast;
mavlinkUDP.beginPacket(broadcast, MAVLINK_PORT_SEND);
mavlinkUDP.write(buf, len);
mavlinkUDP.endPacket();
}
}
MUSTARDTIGERFPV marked this conversation as resolved.
Show resolved Hide resolved
}

// Check if we have MAVLink UDP data to send over UART
if (wifiService == WIFI_SERVICE_MAVLINK_TX) {
int packetSize = mavlinkUDP.parsePacket();
if (packetSize) {
uint8_t buf[MAVLINK_MAX_PACKET_LEN];
mavlinkUDP.read(buf, packetSize);
Serial.write(buf, packetSize);
mavlink_to_uart_counter++;
MUSTARDTIGERFPV marked this conversation as resolved.
Show resolved Hide resolved
}
}

Expand Down
7 changes: 7 additions & 0 deletions lib/config/config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ TxBackpackConfig::SetDefaults()
m_config.ssid[0] = 0;
m_config.password[0] = 0;
memset(m_config.address, 0, 6);
m_config.wifiService = WIFI_SERVICE_UPDATE;
m_modified = true;
Commit();
}
Expand Down Expand Up @@ -83,6 +84,12 @@ TxBackpackConfig::SetGroupAddress(const uint8_t address[6])
m_modified = true;
}

void
TxBackpackConfig::SetWiFiService(wifi_service_t service)
{
m_config.wifiService = service;
m_modified = true;
}
#endif

/////////////////////////////////////////////////////
Expand Down
20 changes: 15 additions & 5 deletions lib/config/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,21 @@
#define VRX_BACKPACK_CONFIG_VERSION 3
#define TIMER_BACKPACK_CONFIG_VERSION 3


typedef enum {
WIFI_SERVICE_UPDATE,
WIFI_SERVICE_MAVLINK_TX,
} wifi_service_t;

#if defined(TARGET_TX_BACKPACK)

typedef struct {
uint32_t version;
bool startWiFi;
char ssid[33];
char password[65];
uint8_t address[6];
uint32_t version;
bool startWiFi;
char ssid[33];
char password[65];
uint8_t address[6];
wifi_service_t wifiService;
} tx_backpack_config_t;

class TxBackpackConfig
Expand All @@ -32,6 +40,7 @@ class TxBackpackConfig
char *GetSSID() { return m_config.ssid; }
char *GetPassword() { return m_config.password; }
uint8_t *GetGroupAddress() { return m_config.address; }
wifi_service_t GetWiFiService() { return m_config.wifiService; }

// Setters
void SetStorageProvider(ELRS_EEPROM *eeprom);
Expand All @@ -40,6 +49,7 @@ class TxBackpackConfig
void SetSSID(const char *ssid);
void SetPassword(const char *ssid);
void SetGroupAddress(const uint8_t address[6]);
void SetWiFiService(wifi_service_t service);

private:
tx_backpack_config_t m_config;
Expand Down
61 changes: 60 additions & 1 deletion src/Tx_main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,20 @@
#include "devButton.h"
#include "devLED.h"

#if defined(MAVLINK_ENABLED)
#include "common/mavlink.h"

#endif

/////////// GLOBALS ///////////

uint8_t bindingAddress[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};

const uint8_t version[] = {LATEST_VERSION};

connectionState_e connectionState = starting;
// Assume we are in wifi update mode until we know otherwise
wifi_service_t wifiService = WIFI_SERVICE_UPDATE;
unsigned long rebootTime = 0;

bool cacheFull = false;
Expand Down Expand Up @@ -65,10 +72,14 @@ void sendMSPViaEspnow(mspPacket_t *packet);
esp_now_peer_info_t peerInfo;
#endif

void RebootIntoWifi()
void RebootIntoWifi(wifi_service_t service = WIFI_SERVICE_UPDATE)
{
DBGLN("Rebooting into wifi update mode...");
config.SetStartWiFiOnBoot(true);
#if defined(TARGET_TX_BACKPACK)
// TODO it might be better to add wifi service to each type of backpack
config.SetWiFiService(service);
#endif
config.Commit();
rebootTime = millis();
}
Expand Down Expand Up @@ -236,6 +247,41 @@ void SendCachedMSP()
}
}

// This function is only used when we're not yet in WiFi MAVLink mode - its main purpose is to detect
// heartbeat packets and switch to WiFi MAVLink mode if we receive 3 heartbeats in 5 seconds.
void ProcessMAVLinkFromTX(mavlink_message_t *mavlink_rx_message, mavlink_status_t *mavlink_status)
{
static unsigned long heartbeatTimestamps[3] = {0, 0, 0};
switch (mavlink_rx_message->msgid)
{
case MAVLINK_MSG_ID_HEARTBEAT:
{
mavlink_heartbeat_t heartbeat;
mavlink_msg_heartbeat_decode(mavlink_rx_message, &heartbeat);
if (mavlink_rx_message->compid == MAV_COMP_ID_AUTOPILOT1)
{
// If we hit this line we shouldn't be in WiFi update mode but let's be certain.
if (connectionState != wifiUpdate)
{
unsigned long now = millis();
// Push the timestamp we received the heartbeat back into the array
for (int i = 0; i < 2; i++)
{
heartbeatTimestamps[i] = heartbeatTimestamps[i + 1];
}
heartbeatTimestamps[2] = now;
// If we have received 3 heartbeats in the last 5 seconds, switch to WiFi update mode
if (now - heartbeatTimestamps[0] < 5000)
{
RebootIntoWifi(WIFI_SERVICE_MAVLINK_TX);
}
}
}
break;
}
}
}

void SetSoftMACAddress()
{
if (!firmwareOptions.hasUID)
Expand Down Expand Up @@ -310,6 +356,12 @@ void setup()
if (config.GetStartWiFiOnBoot())
{
config.SetStartWiFiOnBoot(false);
if (config.GetWiFiService() != WIFI_SERVICE_UPDATE)
{
// Store the intended service mode before writing default back to persistent flash
wifiService = config.GetWiFiService();
config.SetWiFiService(WIFI_SERVICE_UPDATE);
}
config.Commit();
connectionState = wifiUpdate;
devicesTriggerEvent();
Expand Down Expand Up @@ -377,6 +429,13 @@ void loop()
ProcessMSPPacketFromTX(msp.getReceivedPacket());
msp.markPacketReceived();
}
// Process MAVLink messages to switch to wifi update mode
mavlink_message_t mavlink_rx_message;
mavlink_status_t mavlink_status;
if (mavlink_parse_char(MAVLINK_COMM_0, c, &mavlink_rx_message, &mavlink_status))
{
ProcessMAVLinkFromTX(&mavlink_rx_message, &mavlink_status);
}
}

if (cacheFull && sendCached)
Expand Down
4 changes: 4 additions & 0 deletions targets/common.ini
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ lib_deps =
[common_env_data]
build_src_filter = +<*> -<.git/> -<svn/> -<example/> -<examples/> -<test/> -<tests/> -<*.py> -<*test*.*>
build_flags = -Wall -Iinclude
# keep in sync with the version in the main firmware
mavlink_lib_dep = https://github.com/mavlink/c_library_v2.git#e54a8d2e8cf7985e689ad1c8c8f37dc0800ea87b

# ------------------------- COMMON ESP8285 DEFINITIONS -----------------
[env_common_esp8285]
Expand Down Expand Up @@ -65,6 +67,8 @@ lib_ignore = STM32UPDATE
build_flags =
${common_env_data.build_flags}
-D TARGET_TX_BACKPACK
lib_deps =
${env.lib_deps}
build_src_filter = ${common_env_data.build_src_filter} -<Vrx_main.cpp> -<rapidfire.*> -<rx5808.*> -<steadyview.*> -<fusion.*> -<hdzero.*> -<skyzone_msp.*> -<orqa.*> -<Timer_main.cpp>

# ------------------------- COMMON RAPIDFIRE-BACKPACK DEFINITIONS -----------------
Expand Down
4 changes: 4 additions & 0 deletions targets/txbp_esp.ini
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,13 @@

[env:ESP_TX_Backpack_via_UART]
extends = env_common_esp8285, tx_backpack_common
lib_deps =
${tx_backpack_common.lib_deps}
${common_env_data.mavlink_lib_dep}
build_flags =
${env_common_esp8285.build_flags}
${tx_backpack_common.build_flags}
-D MAVLINK_ENABLED=1
-D PIN_BUTTON=0
-D PIN_LED=16
upload_resetmethod = nodemcu
Expand Down
Loading