Skip to content
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
4 changes: 4 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co

**Bullet GCSS** (Ground Control Station System) is a web-based UAV ground control station that operates over cellular networks with no range limit. It is a PWA (no app installation required) that works cross-platform.

## Local Resources

- **INAV firmware repository** is available at `~/dev/inav` — useful for cross-referencing MSP protocol definitions, box IDs, and flight controller behavior.

## Two-Component Architecture

### 1. ESP32-Modem (Embedded Firmware — [ESP32-Modem/](ESP32-Modem/))
Expand Down
508 changes: 156 additions & 352 deletions ESP32-Modem/ESP32-Modem.cpp

Large diffs are not rendered by default.

29 changes: 8 additions & 21 deletions ESP32-Modem/msp_library.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@
#define MSP_NAV_STATUS 121
#define MSP_SENSOR_STATUS 151
#define MSP_SET_WP 209
#define MSP_SET_RAW_RC 200
#define MSP_SET_MODE_RANGE 35
#define MSP2_INAV_SET_AUX_RC 0x2230
#define MSP2_INAV_ANALOG 0x2002
#define MSP2_INAV_MISC2 0x203A
#define MSP2_INAV_SET_WP_INDEX 0x2221 // in: jump to WP N during active mission (U8 index, 0-based)
Expand All @@ -70,35 +71,21 @@
#define MSP_PERM_ID_FAILSAFE 27
#define MSP_PERM_ID_WP 28
#define MSP_PERM_ID_CRUISE 53
#define MSP_PERM_ID_MSPOVERRIDE 50

// One entry returned by MSP_MODE_RANGES (4 bytes each, up to 20 entries)
// One entry returned by MSP_MODE_RANGES (4 bytes each, up to 40 entries)
struct modeRangeEntry_t {
uint8_t permanentId; // mode identifier (see MSP_PERM_ID_* above)
uint8_t auxChannelIndex; // AUX channel (0 = AUX1); RC channel index = auxChannelIndex + 4
uint8_t startStep; // activation range start; PWM = 900 + step * 25
uint8_t endStep; // activation range end
} __attribute__((packed));

// Computed activation info for a single flight mode (populated from MSP_MODE_RANGES at startup)
struct modeRangeInfo_t {
uint8_t rcChannelIndex; // 0-based RC channel index (auxChannelIndex + 4)
uint16_t onValue; // PWM midpoint of the activation range
uint16_t startPWM; // lower bound of the activation range (900 + startStep * 25)
uint16_t endPWM; // upper bound of the activation range (900 + endStep * 25)
bool found; // true once a valid range was found for this mode
};

// All per-mode state bundled into one struct.
// range: RC channel assignment and activation PWM (from MSP_MODE_RANGES)
// available: channel is enabled in msp_override_channels (set at startup)
// active: a sustained RC override for this mode is currently commanded
// boxId: index in the MSP_ACTIVEBOXES bitmask (from MSP_BOXNAMES)
// Per-mode runtime state.
// active: a sustained RC override for this mode is currently commanded
// boxId: index in the MSP_ACTIVEBOXES bitmask (from MSP_BOXNAMES)
struct FlightMode {
modeRangeInfo_t range;
bool available;
bool active;
uint8_t boxId;
bool active;
uint8_t boxId;
};

// This enum is a copy from INAV one in "src/main/fc/rc_modes.h"
Expand Down
1 change: 0 additions & 1 deletion ESP32-Modem/uav_status.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,6 @@ struct uav_status
uint8_t navState;
uint8_t isWpMissionValid;
uint8_t downlinkStatus; // 1 = subscribed to command topic ok, 0 = not subscribed
uint8_t mspRcOverride; // 1 = MSP RC Override flight mode is active, 0 = not active
uint8_t cmdRth; // 1 = RTH channel actively overridden by firmware
uint8_t cmdAltHold; // 1 = AltHold channel actively overridden
uint8_t cmdCruise; // 1 = Cruise channel actively overridden
Expand Down
3 changes: 0 additions & 3 deletions UI/basicui.html
Original file line number Diff line number Diff line change
Expand Up @@ -844,9 +844,6 @@ <h5 class="modal-title" id="bsModalCommandsLabel">Send Command</h5>
<p id="commandsDownlinkWarning" class="text-warning small mb-2" style="display:none;">
&#9888; No command channel. Commands will not be received.
</p>
<p id="commandsMroWarning" class="text-warning small mb-2" style="display:none;">
&#9888; MSP RC Override not active. RC commands will have no effect.
</p>
<div class="rc-cmd-grid mb-3">
<div class="rc-cmd-row">
<span class="rc-cmd-label">RTH</span>
Expand Down
6 changes: 0 additions & 6 deletions UI/js/CommScripts.js
Original file line number Diff line number Diff line change
Expand Up @@ -760,7 +760,6 @@ export function resetDataObject()
mWhDraw: 0,
protocolVersion: 1, // 1 = current/legacy; set from low-priority message (pv field)
downlinkStatus: 0, // 0 = firmware not subscribed to command topic, 1 = subscribed ok
mspRcOverride: 0, // 0 = MSP RC Override flight mode not active, 1 = active (commands can be sent)
cmdRth: 0, // 1 = firmware actively overriding this channel, 0 = not overriding
cmdAltHold: 0,
cmdCruise: 0,
Expand Down Expand Up @@ -1134,11 +1133,6 @@ function parseStandardTelemetryMessage(payload)
if(raw === 0 || raw === 1)
data.downlinkStatus = raw;
break;
case "mro":
raw = parseInt(arrData[1]);
if(raw === 0 || raw === 1)
data.mspRcOverride = raw;
break;
case "cmdrth":
raw = parseInt(arrData[1]);
if(raw === 0 || raw === 1) data.cmdRth = raw;
Expand Down
2 changes: 0 additions & 2 deletions UI/js/InfoPanelScripts.js
Original file line number Diff line number Diff line change
Expand Up @@ -207,8 +207,6 @@ function getCommandChannelIcon()
{
if(!mqttConnected || data.downlinkStatus !== 1)
return "img/command_error.png";
if(data.mspRcOverride !== 1)
return "img/command_warning.png";
return "img/command_ok.png";
}

Expand Down
8 changes: 0 additions & 8 deletions UI/js/MissionPlannerScripts.js
Original file line number Diff line number Diff line change
Expand Up @@ -472,10 +472,6 @@ async function uploadMission() {
alert('Cannot upload: WP Mission mode is currently active on the aircraft.\nDeactivate it first.');
return;
}
if (data.mspRcOverride === 1) {
alert('Cannot upload: MSP RC Override mode is currently active on the aircraft.\nDeactivate it first.');
return;
}
if (data.uavIsArmed === 1) {
if (!confirm('The aircraft is currently armed.\nUploading a mission mid-flight is dangerous.\n\nAre you sure you want to continue?'))
return;
Expand Down Expand Up @@ -548,10 +544,6 @@ async function downloadMission() {
alert('Cannot download: WP Mission mode is currently active on the aircraft.\nDeactivate it first.');
return;
}
if (data.mspRcOverride === 1) {
alert('Cannot download: MSP RC Override mode is currently active on the aircraft.\nDeactivate it first.');
return;
}
if (data.uavIsArmed === 1) {
if (!confirm('The aircraft is currently armed.\nDownloading a mission mid-flight may be dangerous.\n\nAre you sure you want to continue?'))
return;
Expand Down
7 changes: 3 additions & 4 deletions UI/js/bsPageScripts.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ function openCommandsModal() {

function updateCommandsPanel() {
var downlinkOk = mqttConnected && data.downlinkStatus === 1;
var rcOk = downlinkOk && data.mspRcOverride === 1;
var rcOk = downlinkOk;

// Version warning: shown when version is known and < 9.0.0
var versionKnown = data.fcVersion !== "";
Expand All @@ -115,7 +115,6 @@ function updateCommandsPanel() {
}

document.getElementById("commandsDownlinkWarning").style.display = downlinkOk ? "none" : "block";
document.getElementById("commandsMroWarning").style.display = (downlinkOk && !rcOk) ? "block" : "none";

document.getElementById("btSendPing").disabled = !downlinkOk;
var extOk = data.extCmdsSupported >= 1;
Expand Down Expand Up @@ -964,11 +963,11 @@ function wireEventListeners() {
for (var i = 0; i < rcCommands.length; i++) {
(function(entry) {
document.getElementById(entry.onId).addEventListener("click", function() {
if (!mqttConnected || data.downlinkStatus !== 1 || data.mspRcOverride !== 1) return;
if (!mqttConnected || data.downlinkStatus !== 1) return;
publishCommand(entry.cmd, 1);
});
document.getElementById(entry.offId).addEventListener("click", function() {
if (!mqttConnected || data.downlinkStatus !== 1 || data.mspRcOverride !== 1) return;
if (!mqttConnected || data.downlinkStatus !== 1) return;
publishCommand(entry.cmd, 0);
});
})(rcCommands[i]);
Expand Down
Loading
Loading