Skip to content
Closed
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
140 changes: 124 additions & 16 deletions docs/javascript_programming/JAVASCRIPT_PROGRAMMING_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,22 +61,45 @@ edge(() => flight.armTimer > 1000, { duration: 0 }, () => {
---

### Latching/Sticky Conditions
Use `sticky()` for conditions that latch ON and stay ON until reset:
Use `sticky()` for conditions that latch ON and stay ON until reset.

**Option 1: Variable assignment syntax** (recommended when you need to reference the latch state):

```javascript
const { flight, gvar, sticky, override } = inav;

// Create a latch: ON when RSSI < 30, OFF when RSSI > 70
var rssiWarning = sticky({
on: () => flight.rssi < 30,
off: () => flight.rssi > 70
});

// Use the latch to control actions
if (rssiWarning) {
override.vtx.power = 4; // Max power while latched
}
```

**Option 2: Callback syntax** (simpler when actions are self-contained):

```javascript
const { flight, gvar, sticky } = inav;
const { flight, sticky, override } = inav;

// Latches ON when RSSI < 30, stays ON until RSSI > 70
// Latch ON when RSSI < 30, OFF when RSSI > 70
sticky(
() => flight.rssi < 30, // ON condition
() => flight.rssi > 70, // OFF condition
() => flight.rssi > 70, // OFF condition
() => {
override.vtx.power = 4; // Executes while latched
}
);
```

**Parameters:**
**Parameters (variable syntax):**
- **on**: Condition that latches ON
- **off**: Condition that latches OFF

**Parameters (callback syntax):**
- **onCondition**: When to latch ON
- **offCondition**: When to latch OFF
- **action**: What to do while latched
Expand Down Expand Up @@ -172,17 +195,18 @@ if (flight.homeDistance <= 200) {

### Hysteresis/Deadband
```javascript
const { flight, gvar, sticky } = inav;
const { flight, gvar, sticky, override } = inav;

// Turn ON at low voltage, turn OFF when recovered
sticky(
() => flight.cellVoltage < 330, // Warning threshold
() => flight.cellVoltage > 350, // Recovery threshold
() => {
override.throttleScale = 50; // Reduce throttle while in warning
gvar[0] = 1; // Warning flag
}
);
var lowVoltageWarning = sticky({
on: () => flight.cellVoltage < 330, // Warning threshold
off: () => flight.cellVoltage > 350 // Recovery threshold
});

if (lowVoltageWarning) {
override.throttleScale = 50; // Reduce throttle while in warning
gvar[0] = 1; // Warning flag
}
```

---
Expand All @@ -198,21 +222,105 @@ sticky(

---

##Variables

### Let/Const Variables

Use `let` or `const` to define reusable expressions that are compiled into the logic:

```javascript
const { flight, override } = inav;

// Define reusable calculations
let distanceThreshold = 500;
let altitudeLimit = 100;
let combinedCondition = flight.homeDistance > distanceThreshold && flight.altitude > altitudeLimit;

// Use in conditions
if (combinedCondition) {
override.vtx.power = 4;
}
```

**Benefits:**
- Makes code more readable with named values
- Compiler automatically optimizes duplicate expressions
- Variables preserve their custom names through compile/decompile cycles

**Important:** `let`/`const` variables are **compile-time substituted**, not runtime variables. For runtime state, use `gvar[]`.

### Ternary Operator

Use ternary expressions for conditional values:

```javascript
const { flight, override } = inav;

// Assign based on condition
let throttleLimit = flight.cellVoltage < 330 ? 25 : 50;

if (flight.cellVoltage < 350) {
override.throttleScale = throttleLimit;
}

// Inline in expressions
override.vtx.power = flight.homeDistance > 500 ? 4 : 2;
```

**Use when:** You need conditional value assignment in a single expression.

---

## Available Objects

```javascript
const {
flight, // Flight telemetry
const {
flight, // Flight telemetry (including flight.mode.*)
override, // Override flight parameters
rc, // RC channels
gvar, // Global variables (0-7)
pid, // Programming PID outputs (pid[0-3].output)
waypoint, // Waypoint navigation
edge, // Edge detection
sticky, // Latching conditions
delay // Delayed execution
} = inav;
```

### Flight Mode Detection

Check which flight modes are currently active via `flight.mode.*`:

```javascript
const { flight, gvar, override } = inav;

if (flight.mode.poshold === 1) {
gvar[0] = 1; // Flag: in position hold
}

if (flight.mode.rth === 1) {
override.vtx.power = 4; // Max power during RTH
}
```

**Available modes:** `failsafe`, `manual`, `rth`, `poshold`, `cruise`, `althold`, `angle`, `horizon`, `air`, `acro`, `courseHold`, `waypointMission`, `user1` through `user4`

### PID Controller Outputs

Read output values from the 4 programming PID controllers (configured in Programming PID tab):

```javascript
const { pid, gvar, override } = inav;

if (pid[0].output > 500) {
override.throttle = 1600;
}

gvar[0] = pid[0].output; // Store for OSD display
```

**Available:** `pid[0].output` through `pid[3].output`

---

## Tips
Expand Down
8 changes: 4 additions & 4 deletions docs/javascript_programming/api_definitions_summary.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ Contains ~40 flight parameters including:
- **State**: isArmed, isAutoLaunch, isFailsafe
- **Profile**: mixerProfile
- **Navigation**: activeWpNumber, activeWpAction, courseToHome, gpsCourseOverGround
- **Modes** (nested): failsafe, manual, rth, poshold, althold, wp, gcs_nav, airmode, angle, horizon, cruise
- **Modes** (nested `flight.mode.*`): failsafe, manual, rth, poshold, cruise, althold, angle, horizon, air, acro, courseHold, waypointMission, user1-user4

### 2. ✅ **override.js** - Flight Control Overrides (WRITABLE)
**Source**: `src/main/programming/logic_condition.c` (OPERATION_OVERRIDE_*)
Expand Down Expand Up @@ -63,9 +63,9 @@ Waypoint mission data:
**Source**: `src/main/programming/pid.c`

4 PID controllers (pid[0] through pid[3]), each with:
- `configure()` method: setpoint, measurement, p, i, d, ff
- `output` property: Controller output
- `enabled` property: Controller state
- `output` property: Controller output value (read-only)

Note: PID controller parameters (setpoint, measurement, gains) are configured in the Programming PID tab, not in JavaScript. The JavaScript code can only read the output values.

### 7. ✅ **helpers.js** - Math & Utility Functions
**Source**: `src/main/programming/logic_condition.c` (OPERATION_*)
Expand Down
20 changes: 13 additions & 7 deletions docs/javascript_programming/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,8 @@ Instructions for:
- Variable management (global variables, let/var)
- RC channel access and state detection
- Flight parameter overrides
- Flight mode detection (`flight.mode.poshold`, `flight.mode.rth`, etc.)
- PID controller outputs (`pid[0-3].output`)
- Waypoint navigation

**JavaScript Features:**
Expand Down Expand Up @@ -275,12 +277,16 @@ Read the [Testing Guide](TESTING_GUIDE.md) to understand the testing workflow, t

## Version History

**2025-11-25**: Complete implementation
- All INAV logic condition operations now supported
**INAV 9.0**: JavaScript programming introduced
- All INAV logic condition operations supported
- RC channel state detection (LOW/MID/HIGH)
- XOR/NAND/NOR logical operations
- APPROX_EQUAL comparison
- MAP_INPUT/MAP_OUTPUT scaling
- Comprehensive documentation

**Last Updated**: 2025-11-25
- Approximate equality with tolerance
- MAP_INPUT/MAP_OUTPUT scaling functions
- Timer and change detection functions
- Flight mode detection (`flight.mode.poshold`, `flight.mode.rth`, etc.)
- PID controller output access (`pid[0-3].output`)
- Named parameter syntax for sticky with variable assignment
- IntelliSense and real-time validation

**Last Updated**: 2025-12-10
23 changes: 23 additions & 0 deletions src/main/fc/fc_msp.c
Original file line number Diff line number Diff line change
Expand Up @@ -564,6 +564,29 @@ static bool mspFcProcessOutCommand(uint16_t cmdMSP, sbuf_t *dst, mspPostProcessF
sbufWriteU32(dst, logicConditionGetValue(i));
}
break;
case MSP2_INAV_LOGIC_CONDITIONS_CONFIGURED:
{
// Returns 8-byte bitmask where bit N = 1 if logic condition N is configured (non-default)
uint64_t mask = 0;
for (int i = 0; i < MAX_LOGIC_CONDITIONS; i++) {
const logicCondition_t *lc = logicConditions(i);
// Check if any field differs from default reset values
bool isConfigured = (lc->enabled != 0) ||
(lc->activatorId != -1) ||
(lc->operation != 0) ||
(lc->operandA.type != LOGIC_CONDITION_OPERAND_TYPE_VALUE) ||
(lc->operandA.value != 0) ||
(lc->operandB.type != LOGIC_CONDITION_OPERAND_TYPE_VALUE) ||
(lc->operandB.value != 0) ||
(lc->flags != 0);
if (isConfigured) {
mask |= ((uint64_t)1 << i);
}
}
sbufWriteU32(dst, (uint32_t)(mask & 0xFFFFFFFF)); // Lower 32 bits
sbufWriteU32(dst, (uint32_t)((mask >> 32) & 0xFFFFFFFF)); // Upper 32 bits
}
break;
Comment on lines 564 to +589
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: Validate and reserve the required payload size before writing to the stream buffer to avoid out-of-bounds writes. [Learned best practice, importance: 6]

New proposed code:
 case MSP2_INAV_LOGIC_CONDITIONS_STATUS:
-    for (int i = 0; i < MAX_LOGIC_CONDITIONS; i++) {
-        sbufWriteU32(dst, logicConditionGetValue(i));
+    {
+        const unsigned needed = MAX_LOGIC_CONDITIONS * 4;
+        if (!sbufEnsureWrite(dst, needed)) { return false; }
+        for (int i = 0; i < MAX_LOGIC_CONDITIONS; i++) {
+            sbufWriteU32(dst, logicConditionGetValue(i));
+        }
     }
     break;
 case MSP2_INAV_LOGIC_CONDITIONS_CONFIGURED:
     {
-        // Returns 8-byte bitmask where bit N = 1 if logic condition N is configured (non-default)
         uint64_t mask = 0;
         for (int i = 0; i < MAX_LOGIC_CONDITIONS; i++) {
             const logicCondition_t *lc = logicConditions(i);
-            // Check if any field differs from default reset values
             bool isConfigured = (lc->enabled != 0) ||
                                 (lc->activatorId != -1) ||
                                 (lc->operation != 0) ||
                                 (lc->operandA.type != LOGIC_CONDITION_OPERAND_TYPE_VALUE) ||
                                 (lc->operandA.value != 0) ||
                                 (lc->operandB.type != LOGIC_CONDITION_OPERAND_TYPE_VALUE) ||
                                 (lc->operandB.value != 0) ||
                                 (lc->flags != 0);
             if (isConfigured) {
                 mask |= ((uint64_t)1 << i);
             }
         }
-        sbufWriteU32(dst, (uint32_t)(mask & 0xFFFFFFFF));        // Lower 32 bits
-        sbufWriteU32(dst, (uint32_t)((mask >> 32) & 0xFFFFFFFF)); // Upper 32 bits
+        if (!sbufEnsureWrite(dst, 8)) { return false; }
+        sbufWriteU32(dst, (uint32_t)(mask & 0xFFFFFFFF));
+        sbufWriteU32(dst, (uint32_t)((mask >> 32) & 0xFFFFFFFF));
     }
     break;

case MSP2_INAV_GVAR_STATUS:
for (int i = 0; i < MAX_GLOBAL_VARIABLES; i++) {
sbufWriteU32(dst, gvGet(i));
Expand Down
1 change: 1 addition & 0 deletions src/main/msp/msp_protocol_v2_inav.h
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@

#define MSP2_INAV_MISC2 0x203A
#define MSP2_INAV_LOGIC_CONDITIONS_SINGLE 0x203B
#define MSP2_INAV_LOGIC_CONDITIONS_CONFIGURED 0x203C // Returns 8-byte bitmask of non-default logic conditions

#define MSP2_INAV_ESC_RPM 0x2040
#define MSP2_INAV_ESC_TELEM 0x2041
Expand Down