Javascript‐Programming - iNavFlight/inav GitHub Wiki
INAV JavaScript Programming
The INAV Configurator includes a JavaScript transpiler that allows you to program flight controller logic conditions using JavaScript instead of raw logic condition commands. This provides a modern development experience with IntelliSense, syntax highlighting, and real-time validation.
📚 Table of Contents
Quick Start
The JavaScript transpiler converts JavaScript code into INAV logic conditions, enabling you to program flight controller behavior using familiar JavaScript syntax.
Modern Development Experience
Real-time autocomplete with type information and documentation
Clear error messages with line numbers and suggestions
Basic Example
const { flight, override } = inav;
// Increase VTX power when far from home
if (flight.homeDistance > 500) {
override.vtx.power = 4;
}
Complete example showing VTX power control based on distance
Features
Bidirectional Translation
- Transpiler: JavaScript → INAV Logic Conditions
- Decompiler: INAV Logic Conditions → JavaScript
- Round-trip capable: Code survives transpile/decompile cycles
Development Experience
- Monaco Editor: Full-featured code editor with syntax highlighting
- IntelliSense: Context-aware autocomplete with documentation
- Real-time Validation: Immediate feedback on syntax errors
- Error Messages: Clear, actionable messages with line numbers
Language Support
All INAV Operations Supported:
- Arithmetic operations (
+,-,*,/,%) - Comparison and logical operations (
>,<,===,&&,||,!) - Math functions (
Math.min(),Math.max(),Math.sin(),Math.cos(),Math.tan(),Math.abs()) - Flow control (
edge(),sticky(),delay(),timer(),whenChanged()) - Variable management (
gvar[0-7],let,var) - RC channel access and state detection (
rc[n].value,rc[n].low,rc[n].mid,rc[n].high) - Flight parameter overrides (
override.vtx.*,override.throttle, etc.) - Flight mode detection (
flight.mode.poshold,flight.mode.rth, etc.) - PID controller outputs (
pid[0-3].output) - Waypoint navigation
JavaScript Features:
constdestructuring:const { flight, override } = inav;letvariables: variables with a lifetime of that loop onlyvarvariables: allocated to global variables for long lifetime- Arrow functions:
() => condition - Object property access:
flight.altitude,rc[0].value
Programming Patterns
Continuous Conditions (if statements)
Use if statements for conditions that check and execute every cycle:
const { flight, override } = inav;
// Checks every cycle - adjusts VTX power continuously
if (flight.homeDistance > 100) {
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:
const { flight, gvar, edge } = inav;
// Executes ONCE when armTimer reaches 1000ms
edge(() => flight.armTimer > 1000, { duration: 0 }, () => {
gvar[0] = flight.yaw; // Save initial heading
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 Conditions (sticky)
Use sticky() for conditions that latch ON and stay ON until reset. Assign the result to a variable to use in conditions:
const { flight, override, gvar, sticky } = inav;
// Create a latch that turns ON when RSSI < 30, OFF when RSSI > 70
var rssiWarning = sticky({
on: () => flight.rssi < 30,
off: () => flight.rssi > 70
});
// Use the latch variable to control actions
if (rssiWarning) {
override.vtx.power = 4; // Max power while latched
}
The latch variable can be referenced multiple times:
const { flight, override, gvar, sticky } = inav;
var lowBatteryLatch = sticky({
on: () => flight.cellVoltage < 330,
off: () => flight.cellVoltage > 350
});
// Use the latch variable to control multiple actions
if (lowBatteryLatch) {
override.throttleScale = 50;
gvar[0] = 1; // Warning flag
}
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:
const { flight, gvar, delay } = inav;
// Executes only if RSSI < 30 for 2 seconds continuously
delay(() => flight.rssi < 30, { duration: 2000 }, () => {
gvar[0] = 1; // Set failsafe flag
});
Use when:
- Avoiding false triggers
- Requiring sustained conditions
- Timeouts and delays
Periodic Timer (timer)
Use timer() for actions that toggle ON and OFF periodically:
const { gvar, timer } = inav;
// Toggle gvar[0] every second: ON for 1000ms, OFF for 1000ms
timer(1000, 1000, () => {
gvar[0] = 1; // Active during ON phase
});
Use when:
- Blinking indicators
- Periodic sampling
- Timed sequences
Change Detection (whenChanged)
Use whenChanged() to detect when a value changes by a threshold:
const { flight, gvar, whenChanged } = inav;
// Trigger when altitude changes by >= 10m within 100ms
whenChanged(flight.altitude, 10, () => {
gvar[0] = gvar[0] + 1; // Count altitude changes
});
Use when:
- Detecting significant value changes
- Monitoring sensor variations
- Change-based triggers
Supported Operations
Arithmetic
+,-,*,/,%(modulus)
Comparisons
===,>,<(standard comparisons)approxEqual(a, b, tolerance)- approximate equality with tolerance
Logical
&&,||,!(standard logical operators)xor(a, b)- exclusive ORnand(a, b)- NOT ANDnor(a, b)- NOT OR
Math Functions
Math.min(a, b)- minimum of two valuesMath.max(a, b)- maximum of two valuesMath.sin(degrees)- sine (takes degrees, not radians)Math.cos(degrees)- cosine (takes degrees)Math.tan(degrees)- tangent (takes degrees)Math.abs(x)- absolute value
Scaling Functions
mapInput(value, maxValue)- scales from [0:maxValue] to [0:1000]mapOutput(value, maxValue)- scales from [0:1000] to [0:maxValue]
Example:
// Scale RC throttle (1000-2000) to normalized (0-1000)
const normalized = mapInput(rc[3].value - 1000, 1000);
// Scale normalized (0-1000) to servo angle (0-180)
const servoAngle = mapOutput(normalized, 180);
RC Channel Access
// RC channel value (1000-2000us)
if (rc[1].value > 1500) {
gvar[0] = 1;
}
// RC channel state detection
if (rc[1].low) { // < 1333us
gvar[1] = 1;
}
if (rc[1].mid) { // 1333-1666us
gvar[2] = 1;
}
if (rc[1].high) { // > 1666us
gvar[3] = 1;
}
Variables
// Global variables (persisted, 8 slots)
gvar[0] = 100;
gvar[1] = gvar[1] + 1;
// Let variables (compile-time constants)
let maxAlt = 500;
if (flight.altitude > maxAlt) {
gvar[0] = 1;
}
// Var variables (allocated to gvar slots)
var counter = 0;
counter = counter + 1;
Note that variable names are stored in Configurator. Loading from the FC to a different computer will use default variable names. It is recommended to save your scripts in Notepad or your favorite editor when upgrading to a new version of INAV.
Flight Parameter Overrides
const { override } = inav;
// VTX control
override.vtx.power = 4; // Power level (0-4)
override.vtx.band = 5; // Band (0-5)
override.vtx.channel = 7; // Channel (0-8)
// Throttle control
override.throttle = 1500; // Direct throttle (1000-2000)
override.throttleScale = 75; // Scale percentage (0-100)
// Arming safety
override.armSafety = 1; // Override arming checks
Flight Mode Detection
Check which flight modes are currently active:
const { flight, gvar, override } = inav;
// Check specific flight modes
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
}
if (flight.mode.failsafe === 1) {
gvar[7] = 1; // Emergency flag
}
Available flight modes:
flight.mode.failsafe- Failsafe modeflight.mode.manual- Manual/passthrough modeflight.mode.rth- Return to homeflight.mode.poshold- Position holdflight.mode.cruise- Cruise modeflight.mode.althold- Altitude holdflight.mode.angle- Angle/self-level modeflight.mode.horizon- Horizon modeflight.mode.air- Air modeflight.mode.acro- Acro modeflight.mode.courseHold- Course holdflight.mode.waypointMission- Waypoint mission activeflight.mode.user1throughflight.mode.user4- User-defined modes
PID Controller Outputs
INAV has 4 programming PID controllers (configured in the Programming PID tab). You can read their output values in JavaScript:
const { pid, gvar, override } = inav;
// Read PID controller outputs
if (pid[0].output > 500) {
override.throttle = 1600;
}
// Store PID output for OSD display
gvar[0] = pid[0].output;
// Combine multiple PID outputs
gvar[1] = pid[0].output + pid[1].output;
PID controllers:
pid[0].outputthroughpid[3].output- Output values from the 4 programming PID controllers
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.
Common Questions
Q: Which pattern should I use?
- Use
iffor continuous control (checking every cycle) - Use
edge()for one-time actions when condition becomes true - Use
sticky()for latching conditions with hysteresis - Use
delay()for actions that need sustained conditions - Use
timer()for periodic toggling - Use
whenChanged()for detecting value changes
Q: How do I debug my code?
Use global variables to track state:
const { flight, gvar, edge } = inav;
// Initialize debug counter
edge(() => flight.armTimer > 1000, { duration: 0 }, () => {
gvar[7] = 0; // Use gvar[7] as debug counter
});
// Increment on each event
edge(() => flight.rssi < 30, { duration: 0 }, () => {
gvar[7] = gvar[7] + 1;
});
// Check gvar[7] value in OSD or Configurator
Q: What's the difference between let and var?
letthe variable goes away after each loop (implemented via compile-time expression substitution)varvariables are allocated to gvar slots (keep their values between loops)
Q: How many global variables can I use?
INAV has 8 global variables: gvar[0] through gvar[7]. Each can store values from -1,000,000 to 1,000,000.
Q: Can I use regular JavaScript functions?
specific INAV functions are supported (edge(), sticky(), delay(), timer(), whenChanged(), and Math functions). The transpiler converts JavaScript to INAV logic conditions, which have specific supported operations.
Examples
Initialize Variables on Arm
const { flight, gvar, edge } = inav;
edge(() => flight.armTimer > 1000, { duration: 0 }, () => {
gvar[0] = 0; // Reset counter
gvar[1] = flight.yaw; // Save heading
gvar[2] = flight.altitude; // Save starting altitude
});
Count Events
const { flight, gvar, edge } = inav;
// Initialize counter on arm
edge(() => flight.armTimer > 1000, { duration: 0 }, () => {
gvar[0] = 0;
});
// Count each time RSSI drops below 30
edge(() => flight.rssi < 30, { duration: 100 }, () => {
gvar[0] = gvar[0] + 1;
});
VTX Power Based on Distance
![RC Override Example]
const { flight, override } = inav;
// Far away - maximum power
if (flight.homeDistance > 500) {
override.vtx.power = 4;
}
// Medium distance
if (flight.homeDistance > 200 && flight.homeDistance <= 500) {
override.vtx.power = 3;
}
// Close to home - reduce power
if (flight.homeDistance <= 200) {
override.vtx.power = 2;
}
Low Voltage Warning with Hysteresis
const { flight, gvar, sticky, override } = inav;
// Latch warning at 3.3V/cell, clear at 3.5V/cell
var lowVoltageWarning = sticky({
on: () => flight.cellVoltage < 330, // Warning threshold
off: () => flight.cellVoltage > 350 // Recovery threshold
});
if (lowVoltageWarning) {
override.throttleScale = 50; // Reduce throttle
gvar[0] = 1; // Warning flag
}
Debounce Noisy Signal
const { flight, override, edge } = inav;
// Only trigger if RSSI < 30 for at least 500ms
edge(() => flight.rssi < 30, { duration: 500 }, () => {
override.vtx.power = 4;
});
Stick Combination Detection
const { rc, gvar } = inav;
// Detect specific stick position combination
if (rc[1].low && rc[2].mid && rc[3].high) {
gvar[0] = 1; // Special mode activated
}
Available Objects
const {
flight, // Flight telemetry (altitude, speed, GPS, battery, etc.)
override, // Override flight parameters (VTX, throttle, arming)
rc, // RC channels (rc[1-18].value, .low, .mid, .high)
gvar, // Global variables (gvar[0-7])
pid, // Programming PID controller outputs (pid[0-3].output)
waypoint, // Waypoint navigation info
edge, // Edge detection function
sticky, // Latching condition function
delay, // Delayed execution function
timer, // Periodic timer function
whenChanged // Change detection function
} = inav;
The flight object includes a mode sub-object for checking active flight modes:
flight.mode.poshold,flight.mode.rth,flight.mode.althold, etc.
Tips
- Initialize variables on arm using
edge()withflight.armTimer > 1000 - Use gvars for state - they persist between logic condition evaluations
- edge() duration = 0 means instant trigger on condition becoming true
- edge() duration > 0 adds debounce time
- if statements are continuous - they execute every cycle
- sticky() provides hysteresis - prevents rapid ON/OFF switching
- All trig functions take degrees, not radians
- RC channels: 0-17 (18 channels total)
- Global variables: -1,000,000 to 1,000,000 range
Version History
INAV 9.0: JavaScript programming introduced
- All INAV logic condition operations supported
- RC channel state detection (LOW/MID/HIGH)
- XOR/NAND/NOR logical operations
- 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
Related Pages:
- Programming Tab - How to use the Programming tab
- Logic Conditions - Traditional logic condition programming
Last Updated: 2025-12-10