Make sure docs are updated / settings_md (push) Canceled after 0s
Build firmware / test (push) Canceled after 0s
Build firmware / build-SITL-Windows (push) Canceled after 0s
Build firmware / build-SITL-Mac (push) Canceled after 0s
Build firmware / build-SITL-Linux (push) Canceled after 0s
Build firmware / build-SITL-Linux-arm64 (push) Canceled after 0s
Build firmware / upload-artifacts (push) Canceled after 0s
Build firmware / build-single-target (push) Canceled after 0s
Build firmware / build (9) (push) Canceled after 0s
Build firmware / build (8) (push) Canceled after 0s
Build firmware / build (7) (push) Canceled after 0s
Build firmware / build (6) (push) Canceled after 0s
Build firmware / build (5) (push) Canceled after 0s
Build firmware / build (4) (push) Canceled after 0s
Build firmware / build (3) (push) Canceled after 0s
Build firmware / build (2) (push) Canceled after 0s
Build firmware / build (14) (push) Canceled after 0s
Build firmware / build (13) (push) Canceled after 0s
Build firmware / build (12) (push) Canceled after 0s
Build firmware / build (11) (push) Canceled after 0s
Build firmware / build (10) (push) Canceled after 0s
Build firmware / build (1) (push) Canceled after 0s
Build firmware / build (0) (push) Canceled after 0s
Build firmware / detect (push) Canceled after 0s
Build pre-release / build (push) Canceled after 0s
Build pre-release / Release (push) Canceled after 0s
322 lines
8.4 KiB
Markdown
322 lines
8.4 KiB
Markdown
# INAV JavaScript Programming - Quick Reference
|
|
|
|
## Relationship to Logic Conditions
|
|
|
|
This JavaScript programming interface is built on top of INAV's traditional
|
|
[Logic Conditions](../../Programming%20Framework.md) system. The JavaScript code you
|
|
write is transpiled (converted) into logic conditions that run on the flight controller.
|
|
|
|
If you're familiar with the traditional logic conditions interface, you can think of
|
|
JavaScript programming as a more user-friendly syntax that generates the same logic
|
|
conditions behind the scenes.
|
|
|
|
**See also:**
|
|
- [Programming Framework documentation](../../Programming%20Framework.md) - Details about the underlying logic conditions system
|
|
- [Operations Reference](OPERATIONS_REFERENCE.md) - Complete reference for all supported operations
|
|
|
|
---
|
|
|
|
## Pattern Guide
|
|
|
|
### Continuous Conditions (if statements)
|
|
Use `if` statements for conditions that should check and execute **every cycle**:
|
|
|
|
```javascript
|
|
|
|
// Checks every cycle - adjusts VTX power continuously
|
|
if (inav.flight.homeDistance > 100) {
|
|
inav.override.vtx.power = 3;
|
|
}
|
|
|
|
```
|
|
|
|
**Use when:** You want the action to happen continuously while the condition is true.
|
|
|
|
---
|
|
|
|
### One-Time Execution (edge)
|
|
Use `edge()` for actions that should execute **only once** when a condition becomes true:
|
|
|
|
```javascript
|
|
|
|
// Executes ONCE when armTimer reaches 1000ms
|
|
inav.events.edge(() => inav.flight.armTimer > 1000, { duration: 0 }, () => {
|
|
inav.gvar[0] = inav.flight.yaw; // Save initial heading
|
|
inav.gvar[1] = 0; // Initialize counter
|
|
});
|
|
|
|
```
|
|
|
|
**Parameters:**
|
|
- **condition**: Function returning boolean
|
|
- **duration**: Minimum duration in ms (0 = instant, >0 = debounce)
|
|
- **action**: Function to execute once
|
|
|
|
**Use when:**
|
|
- Initializing on arm
|
|
- Detecting events (first time RSSI drops)
|
|
- Counting discrete occurrences
|
|
- Debouncing noisy signals
|
|
|
|
---
|
|
|
|
### Latching/Sticky Conditions
|
|
Use `sticky()` for conditions that latch ON and stay ON until reset:
|
|
|
|
```javascript
|
|
|
|
// Latches ON when RSSI < 30, stays ON until RSSI > 70
|
|
inav.events.sticky(
|
|
() => inav.flight.rssi < 30, // ON condition
|
|
() => inav.flight.rssi > 70, // OFF condition
|
|
() => {
|
|
inav.override.vtx.power = 4; // Executes while latched
|
|
}
|
|
);
|
|
|
|
```
|
|
|
|
**Parameters:**
|
|
- **onCondition**: When to latch ON
|
|
- **offCondition**: When to latch OFF
|
|
- **action**: What to do while latched
|
|
|
|
**Use when:**
|
|
- Warning states that need manual reset
|
|
- Hysteresis/deadband behavior
|
|
- Failsafe conditions
|
|
|
|
---
|
|
|
|
### Delayed Execution (delay)
|
|
Use `delay()` to execute after a condition has been true for a duration:
|
|
|
|
```javascript
|
|
|
|
// Executes only if RSSI < 30 for 2 seconds continuously
|
|
inav.events.delay(() => inav.flight.rssi < 30, { duration: 2000 }, () => {
|
|
inav.gvar[0] = 1; // Set failsafe flag
|
|
});
|
|
|
|
```
|
|
|
|
**Parameters:**
|
|
- **condition**: Condition that must remain true
|
|
- **duration**: How long condition must be true (ms)
|
|
- **action**: Action to execute after delay
|
|
|
|
**Use when:**
|
|
- Avoiding false triggers
|
|
- Requiring sustained conditions
|
|
- Timeouts and delays
|
|
|
|
---
|
|
|
|
## Common Patterns
|
|
|
|
### Initialize on Arm
|
|
```javascript
|
|
|
|
inav.events.edge(() => inav.flight.armTimer > 1000, { duration: 0 }, () => {
|
|
inav.gvar[0] = 0; // Reset counter
|
|
inav.gvar[1] = inav.flight.yaw; // Save heading
|
|
inav.gvar[2] = inav.flight.altitude; // Save starting altitude
|
|
});
|
|
|
|
```
|
|
|
|
### Count Events
|
|
```javascript
|
|
|
|
// Initialize
|
|
inav.events.edge(() => inav.flight.armTimer > 1000, { duration: 0 }, () => {
|
|
inav.gvar[0] = 0;
|
|
});
|
|
|
|
// Count each time RSSI drops below 30 (counts transitions, not duration)
|
|
inav.events.edge(() => inav.flight.rssi < 30, { duration: 100 }, () => {
|
|
inav.gvar[0] = inav.gvar[0] + 1;
|
|
});
|
|
|
|
```
|
|
|
|
### Debounce Noisy Signals
|
|
```javascript
|
|
|
|
// Only trigger if RSSI < 30 for at least 500ms
|
|
inav.events.edge(() => inav.flight.rssi < 30, { duration: 500 }, () => {
|
|
inav.override.vtx.power = 4;
|
|
});
|
|
|
|
```
|
|
|
|
### Multi-Stage Logic
|
|
```javascript
|
|
|
|
// Stage 1: Far away
|
|
if (inav.flight.homeDistance > 500) {
|
|
inav.override.vtx.power = 4;
|
|
}
|
|
|
|
// Stage 2: Medium distance
|
|
if (inav.flight.homeDistance > 200 && inav.flight.homeDistance <= 500) {
|
|
inav.override.vtx.power = 3;
|
|
}
|
|
|
|
// Stage 3: Close to home
|
|
if (inav.flight.homeDistance <= 200) {
|
|
inav.override.vtx.power = 2;
|
|
}
|
|
|
|
```
|
|
|
|
### Hysteresis/Deadband
|
|
```javascript
|
|
|
|
// Turn ON at low voltage, turn OFF when recovered
|
|
inav.events.sticky(
|
|
() => inav.flight.cellVoltage < 330, // Warning threshold
|
|
() => inav.flight.cellVoltage > 350, // Recovery threshold
|
|
() => {
|
|
inav.override.throttleScale = 50; // Reduce throttle while in warning
|
|
inav.gvar[0] = 1; // Warning flag
|
|
}
|
|
);
|
|
|
|
```
|
|
|
|
---
|
|
|
|
## Key Differences
|
|
|
|
| Pattern | Executes | Reset | Use Case |
|
|
|---------|----------|-------|----------|
|
|
| `if` | Every cycle while true | N/A | Continuous control |
|
|
| `edge()` | Once per transition | When condition becomes false | Events, initialization |
|
|
| `sticky()` | Continuous while latched | When OFF condition met | Warnings, hysteresis |
|
|
| `delay()` | Once after duration | When condition becomes false | Timeouts, debouncing |
|
|
|
|
---
|
|
|
|
## Variables
|
|
|
|
### Let/Const Variables
|
|
|
|
Use `let` or `const` to define reusable expressions that are compiled into the logic:
|
|
|
|
```javascript
|
|
// Define reusable calculations
|
|
let distanceThreshold = 500;
|
|
let altitudeLimit = 100;
|
|
let combinedCondition = inav.flight.homeDistance > distanceThreshold && inav.flight.altitude > altitudeLimit;
|
|
|
|
// Use in conditions
|
|
if (combinedCondition) {
|
|
inav.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 `inav.gvar[]`.
|
|
|
|
### Ternary Operator
|
|
|
|
Use ternary expressions for conditional values:
|
|
|
|
```javascript
|
|
// Assign based on condition
|
|
let throttleLimit = inav.flight.cellVoltage < 330 ? 25 : 50;
|
|
|
|
if (inav.flight.cellVoltage < 350) {
|
|
inav.override.throttleScale = throttleLimit;
|
|
}
|
|
|
|
// Inline in expressions
|
|
inav.override.vtx.power = inav.flight.homeDistance > 500 ? 4 : 2;
|
|
```
|
|
|
|
**Use when:** You need conditional value assignment in a single expression.
|
|
|
|
---
|
|
|
|
## Available Objects
|
|
|
|
The `inav` namespace provides access to all flight controller data and control functions:
|
|
|
|
- `inav.flight` - Flight telemetry (including `flight.mode.*`)
|
|
- `inav.override` - Override flight parameters
|
|
- `inav.rc` - RC channels
|
|
- `inav.gvar` - Global variables (0-7)
|
|
- `inav.pid` - Programming PID outputs (`pid[0-3].output`)
|
|
- `inav.waypoint` - Waypoint navigation
|
|
- `inav.events.edge` - Edge detection
|
|
- `inav.events.sticky` - Latching conditions
|
|
- `inav.events.delay` - Delayed execution
|
|
|
|
### Flight Mode Detection
|
|
|
|
Check which flight modes are currently active via `inav.flight.mode.*`:
|
|
|
|
```javascript
|
|
if (inav.flight.mode.poshold === 1) {
|
|
inav.gvar[0] = 1; // Flag: in position hold
|
|
}
|
|
|
|
if (inav.flight.mode.rth === 1) {
|
|
inav.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
|
|
if (inav.pid[0].output > 500) {
|
|
inav.override.throttle = 1600;
|
|
}
|
|
|
|
inav.gvar[0] = inav.pid[0].output; // Store for OSD display
|
|
```
|
|
|
|
**Available:** `inav.pid[0].output` through `inav.pid[3].output`
|
|
|
|
---
|
|
|
|
## Tips
|
|
|
|
1. **Initialize variables on arm** using `inav.events.edge()` with `inav.flight.armTimer > 1000`
|
|
2. **Use inav.gvar for state** - they persist between logic condition evaluations
|
|
3. **edge() duration = 0** means instant trigger on condition becoming true
|
|
4. **edge() duration > 0** adds debounce time
|
|
5. **if statements are continuous** - they execute every cycle
|
|
6. **sticky() provides hysteresis** - prevents rapid ON/OFF switching
|
|
7. **Use Math functions** - `Math.abs()`, `Math.min()`, `Math.max()` are available
|
|
|
|
---
|
|
|
|
## Debugging
|
|
|
|
Use global variables to track state:
|
|
```javascript
|
|
|
|
// Debug counter
|
|
inav.events.edge(() => inav.flight.armTimer > 1000, { duration: 0 }, () => {
|
|
inav.gvar[7] = 0; // Use inav.gvar[7] as debug counter
|
|
});
|
|
|
|
// Increment on each event
|
|
inav.events.edge(() => inav.flight.rssi < 30, { duration: 0 }, () => {
|
|
inav.gvar[7] = inav.gvar[7] + 1;
|
|
});
|
|
|
|
// Check inav.gvar[7] value in OSD or Configurator to see event count
|
|
|
|
```
|