Sync inav to Gitea
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

This commit is contained in:
2026-08-03 16:37:40 +08:00
commit dac37fd077
14095 changed files with 5603119 additions and 0 deletions
@@ -0,0 +1,169 @@
# INAV Constants Generator
## Purpose
Generates `inav_constants.js` from the INAV firmware header file (`logic_condition.h`) to ensure the transpiler/decompiler constants exactly match the actual firmware.
## Single Source of Truth Architecture
**Firmware Header** (`logic_condition.h`)
↓ (parse)
**Constants File** (`inav_constants.js`) - AUTO-GENERATED
↓ (import)
**API Definitions** (`flight.js`, etc.) - Reference constants
↓ (use)
**Transpiler & Decompiler** - Use constants
## Usage
### Generate Constants
```bash
node scripts/generate-constants.js <path-to-logic_condition.h> [output-path]
```
### Example
```bash
# From INAV configurator root
node scripts/generate-constants.js \
../inav/src/main/programming/logic_condition.h \
js/transpiler/transpiler/inav_constants.js
```
### Default Output
If output path is not specified, defaults to:
```
js/transpiler/transpiler/inav_constants.js
```
## What It Parses
The parser extracts these enums from `logic_condition.h`:
1. **logicOperation_e**`OPERATION`
- TRUE, EQUAL, GREATER_THAN, etc.
2. **logicOperandType_s**`OPERAND_TYPE`
- VALUE, RC_CHANNEL, FLIGHT, etc.
3. **logicFlightOperands_e**`FLIGHT_PARAM`
- ARM_TIMER, HOME_DISTANCE, RSSI, YAW, etc.
4. **logicFlightModeOperands_e**`FLIGHT_MODE`
- FAILSAFE, MANUAL, RTH, etc.
5. **logicWaypointOperands_e**`WAYPOINT_PARAM`
- IS_WP, WAYPOINT_INDEX, etc.
## Generated File Format
```javascript
/**
* AUTO-GENERATED from firmware header files
* DO NOT EDIT MANUALLY
*/
const OPERAND_TYPE = {
VALUE: 0,
RC_CHANNEL: 1,
FLIGHT: 2,
// ...
};
const OPERATION = {
TRUE: 0,
EQUAL: 1,
// ...
};
const FLIGHT_PARAM = {
ARM_TIMER: 0,
HOME_DISTANCE: 1,
// ...
YAW: 40, // ← Correct value from firmware!
// ...
};
// ... exports
```
## Build Integration
Add to package.json scripts:
```json
{
"scripts": {
"generate-constants": "node scripts/generate-constants.js ../inav/src/main/programming/logic_condition.h",
"prebuild": "npm run generate-constants"
}
}
```
This ensures constants are regenerated before each build.
## Next Steps: Update API Definitions
Currently, API definitions have hardcoded values:
```javascript
// inav.flight.js - WRONG (hardcoded)
yaw: {
inavOperand: { type: 2, value: 17 } // Wrong value!
}
```
Change to reference constants:
```javascript
// inav.flight.js - CORRECT (references constants)
const { OPERAND_TYPE, FLIGHT_PARAM } = require('../../transpiler/inav_constants.js');
yaw: {
inavOperand: { type: OPERAND_TYPE.FLIGHT, value: FLIGHT_PARAM.ATTITUDE_YAW }
}
```
Benefits:
- ✅ Single source of truth (firmware)
- ✅ Type-safe references
- ✅ Compile-time errors if constants missing
- ✅ Auto-update when firmware changes
## Verification
After generating, verify key values:
```bash
grep "ATTITUDE_YAW" js/transpiler/transpiler/inav_constants.js
# Should show: ATTITUDE_YAW: 40,
grep "IS_ARMED" js/transpiler/transpiler/inav_constants.js
# Should show: IS_ARMED: 17,
```
## Error Handling
The parser handles:
- ✅ Both `typedef enum { } name_e;` and `typedef enum name { }` formats
- ✅ Explicit values (`= 40`)
- ✅ Auto-incrementing values
- ✅ Hex values (`= 0x10`)
- ✅ C-style comments (`//` and `/* */`)
- ✅ Missing enums (warns but continues)
## Maintenance
**When INAV firmware updates:**
1. Get new `logic_condition.h` from firmware repo
2. Run `npm run generate-constants`
3. Review diff in `inav_constants.js`
4. Test transpiler/decompiler
5. Commit updated constants file
**DO NOT manually edit `inav_constants.js`** - all changes will be overwritten!
@@ -0,0 +1,321 @@
# 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
```
@@ -0,0 +1,126 @@
# INAV Logic Condition Operations Reference
This document provides a complete reference for all INAV logic condition operations and their JavaScript transpiler implementations.
## All INAV Operations Now Implemented! 🎉
All INAV logic condition operations supported by the firmware are now fully implemented in the JavaScript transpiler.
## Already Implemented
**Arithmetic**: ADD, SUB, MUL, DIV, MODULUS (via +, -, *, /, %)
**Comparisons**: EQUAL, GREATER_THAN, LOWER_THAN, APPROX_EQUAL (via ===, >, <, approxEqual())
**Logical**: AND, OR, NOT, XOR, NAND, NOR (via &&, ||, !, xor(), nand(), nor())
**Math**: MIN, MAX, SIN, COS, TAN, ABS (via Math.min/max/sin/cos/tan/abs)
**Scaling**: MAP_INPUT, MAP_OUTPUT (via mapInput(), mapOutput())
**Flow control**: STICKY, EDGE, DELAY, TIMER, DELTA (via on.* and helper functions)
**Variables**: GVAR_SET, GVAR_INC, GVAR_DEC (via assignments, ++, --)
**Overrides**: VTX, throttle, arming, etc. (via override.* properties)
**RC Channel States**: LOW, MID, HIGH (via rc[n].low, rc[n].mid, rc[n].high)
## Usage Examples
### Logical Operations
```javascript
// XOR - true when exactly one condition is true
if (xor(inav.flight.armed, inav.flight.mode.failsafe)) {
// One or the other, but not both
}
// NAND - false only when both are true
if (nand(inav.flight.armed, inav.gvar[0] > 100)) {
// Not both conditions true
}
// NOR - true only when both are false
if (nor(inav.flight.mode.failsafe, inav.flight.mode.rth)) {
// Neither failsafe nor RTH active
}
// Approximate equality with tolerance
if (approxEqual(inav.flight.altitude, 1000, 50)) {
// Altitude is 1000 ± 50
}
```
### Scaling/Mapping Operations
```javascript
// mapInput: Scale from [0:maxValue] to [0:1000]
// Example: RC value (1000-2000) to normalized (0-1000)
const normalizedThrottle = mapInput(inav.rc[3].value - 1000, 1000);
// mapOutput: Scale from [0:1000] to [0:maxValue]
// Example: normalized (0-1000) to servo angle (0-180)
const servoAngle = mapOutput(normalizedThrottle, 180);
// Chaining for full range mapping
// Map altitude (0-5000m) to percentage (0-100)
const altitudePercent = mapOutput(mapInput(inav.flight.altitude, 5000), 100);
```
### RC Channel State Detection
```javascript
// LOW state - RC value < 1333us
if (inav.rc[0].low) {
// Roll stick is in low position
inav.gvar[0] = 1;
}
// MID state - RC value between 1333-1666us
if (inav.rc[1].mid) {
// Pitch stick is centered
inav.gvar[1] = 1;
}
// HIGH state - RC value > 1666us
if (inav.rc[2].high) {
// Throttle stick is in high position
inav.override.vtx.power = 4;
}
// Access raw RC value
if (inav.rc[3].value > 1700) {
// Custom threshold on yaw channel
inav.gvar[2] = 1;
}
// Combined with logical operations
if (inav.rc[0].low && inav.rc[1].mid && inav.rc[2].high) {
// Specific stick combination detected
inav.override.armSafety = 1;
}
```
## Notes
- RC channels: `rc[0]` through `rc[17]` (18 channels)
- RC channel properties: `.value` (1000-2000us), `.low` (<1333us), `.mid` (1333-1666us), `.high` (>1666us)
- All trig functions (sin/cos/tan) take degrees, not radians
- MODULUS operator bug fixed: `%` now correctly generates MODULUS operation
- MAP_INPUT normalizes to [0:1000], MAP_OUTPUT scales from [0:1000]
## Recent Changes
**2025-11-25 (Session 3)**:
- ✅ Implemented RC channel LOW/MID/HIGH state detection (rc[n].low, rc[n].mid, rc[n].high)
- ✅ Fixed analyzer to validate RC channel array access patterns
- ✅ Updated codegen to generate LOW/MID/HIGH operations (operations 4, 5, 6)
- 🎉 ALL INAV logic condition operations now fully implemented and tested!
**2025-11-25 (Session 2)**:
- ✅ Implemented XOR, NAND, NOR logical operations (xor(), nand(), nor())
- ✅ Implemented APPROX_EQUAL comparison (approxEqual())
- ✅ Implemented MAP_INPUT and MAP_OUTPUT scaling (mapInput(), mapOutput())
**2025-11-25 (Session 1)**:
- ✅ Implemented Math.min(), Math.max(), Math.sin(), Math.cos(), Math.tan()
- ✅ Fixed MODULUS operator (was using wrong constant name)
- 📝 Documented unimplemented operations for future work
**Last Updated**: 2025-11-25
@@ -0,0 +1,267 @@
# Transpiler Testing Guide
This guide explains how to test changes to the INAV JavaScript transpiler to ensure all components work together correctly.
## Overview
The transpiler has 4 main components that must stay in sync:
1. **Parser** (`transpiler/parser.js`) - Parses JavaScript code into AST
2. **Analyzer** (`transpiler/analyzer.js`) - Validates syntax and checks for errors
3. **Codegen** (`transpiler/codegen.js`) - Generates INAV CLI commands from AST
4. **Decompiler** (`transpiler/decompiler.js`) - Converts CLI commands back to JavaScript
## When Adding a New Feature
When implementing a new operation, function, or syntax feature, you must update:
### 1. Analyzer (transpiler/analyzer.js)
- Add validation for new syntax patterns
- Update property access validation if needed
- Add warnings for unsupported variations
Example: For RC channel states, added validation for `rc[0-17]` with `.low/.mid/.high` properties
### 2. Codegen (transpiler/codegen.js)
- Add code generation for the new feature
- Handle the new AST node types
- Generate correct INAV operation codes
Example: For RC channel states, added handler in `generateCondition()` to detect `rc[n].low` and generate LOW operation (4)
### 3. Decompiler (transpiler/decompiler.js)
- Add reverse mapping from operation codes to JavaScript
- Ensure generated code matches expected syntax
Example: For RC channel states, map operations 4/5/6 to `.low/.mid/.high` properties
### 4. Diagnostics (optional, editor/diagnostics.js)
- Add helpful warnings for common mistakes
- Suggest correct syntax alternatives
## Round-Trip Testing
The most reliable way to test transpiler changes is round-trip testing:
JavaScript → CLI commands → JavaScript
### Basic Test Template
```javascript
import { Transpiler } from './transpiler/index.js';
import { Decompiler } from './transpiler/decompiler.js';
const testCode = `
// Your test code here
if (inav.flight.altitude > 100) {
inav.gvar[0] = 1;
}
`;
console.log('=== Original JavaScript ===\n');
console.log(testCode);
// Transpile
const transpiler = new Transpiler();
const transpileResult = transpiler.transpile(testCode);
console.log('\n=== Generated CLI Commands ===\n');
transpileResult.commands.forEach(cmd => console.log(cmd));
// Parse commands to LC format for decompiler
const logicConditions = transpileResult.commands.map((cmd) => {
const parts = cmd.split(/\s+/);
return {
index: parseInt(parts[1]),
enabled: parseInt(parts[2]),
activatorId: parseInt(parts[3]),
operation: parseInt(parts[4]),
operandAType: parseInt(parts[5]),
operandAValue: parseInt(parts[6]),
operandBType: parseInt(parts[7]),
operandBValue: parseInt(parts[8]),
flags: parseInt(parts[9])
};
});
// Decompile
const decompiler = new Decompiler();
const decompileResult = decompiler.decompile(logicConditions);
console.log('\n=== Decompiled JavaScript ===\n');
console.log(decompileResult.code);
if (decompileResult.success) {
console.log('\n✅ Round-trip successful!');
} else {
console.error('\n❌ Decompilation failed:', decompileResult.error);
}
```
### Running Tests
```bash
cd /path/to/inav-configurator/js/transpiler
# Create your test file
nano test_feature.js
# Run the test
node test_feature.js
# Clean up after testing
rm test_feature.js
```
## Test Cases to Verify
When making changes, test these scenarios:
### 1. Basic Functionality
```javascript
if (inav.flight.altitude > 100) {
inav.gvar[0] = 1;
}
```
Expected: 2 commands (condition + action)
### 2. Error Handling
```javascript
// Invalid syntax - should produce helpful error
if (inav.flight.invalidProperty > 100) {
inav.gvar[0] = 1;
}
```
Expected: Error with suggestion for correct property
### 3. Edge Cases
```javascript
// Test boundary values
if (inav.rc[0].low) { // Channel 0 (first)
inav.gvar[0] = 1;
}
if (inav.rc[17].high) { // Channel 17 (last valid)
inav.gvar[1] = 1;
}
```
Expected: Valid generation for both
### 4. Complex Combinations
```javascript
// Test multiple operations together
if (xor(inav.rc[0].low, inav.flight.armed)) {
inav.gvar[0] = Math.max(100, inav.flight.altitude);
}
```
Expected: Proper nesting of operations
## Common Issues
### Analyzer Rejects Valid Syntax
**Problem**: Analyzer validation is too strict
**Solution**: Update `checkPropertyAccess()` or relevant validation method
**Example**: RC channels needed special handling for array syntax `rc[n]`
### Codegen Produces Wrong Operation Code
**Problem**: Operation constant name incorrect or not imported
**Solution**: Check `OPERATION.*` constants match `inav_constants.js`
**Example**: MODULUS was incorrectly `OPERATION.MOD` instead of `OPERATION.MODULUS`
### Decompiler Output Doesn't Match Input
**Problem**: Decompiler reverse mapping incomplete
**Solution**: Add case for new operation in decompiler
**Example**: LOW/MID/HIGH operations needed to map to `.low/.mid/.high` properties
### Line Numbers Off in Errors
**Problem**: Auto-import adds lines before parsing
**Solution**: Track lineOffset and adjust all error/warning line numbers
**Fixed**: Session 1 of 2025-11-25
## Verification Checklist
Before committing changes:
- [ ] Analyzer validates new syntax without false positives
- [ ] Codegen generates correct operation codes
- [ ] Decompiler correctly reverses the operation
- [ ] Round-trip test passes (JS → CLI → JS)
- [ ] Error messages are helpful and accurate
- [ ] Edge cases are handled (boundary values, empty inputs)
- [ ] Documentation updated (OPERATIONS_REFERENCE.md, API definitions)
## Example: Adding a New Operation
Let's say you want to add support for a new INAV operation `FOOBAR (operation 99)`:
**1. Check if operation exists in firmware**
```javascript
// Look in transpiler/inav_constants.js
const OPERATION = {
// ...
FOOBAR: 99, // Check this exists
// ...
};
```
**2. Update Codegen**
```javascript
// In transpiler/codegen.js
case 'CallExpression': {
const funcName = condition.callee?.name;
if (funcName === 'foobar') {
// Generate FOOBAR operation
const resultIndex = this.lcIndex;
this.commands.push(
`logic ${this.lcIndex} 1 ${activatorId} ${OPERATION.FOOBAR} ...`
);
this.lcIndex++;
return resultIndex;
}
}
```
**3. Update Decompiler**
```javascript
// In transpiler/decompiler.js
case OPERATION.FOOBAR:
return 'foobar()';
```
**4. Update Analyzer (if needed)**
```javascript
// In transpiler/analyzer.js
// Add validation for foobar() usage
```
**5. Test Round-Trip**
```javascript
const testCode = `
if (foobar()) {
inav.gvar[0] = 1;
}
`;
// Run round-trip test...
```
**6. Update Documentation**
- Add to OPERATIONS_REFERENCE.md
- Add usage examples
- Update API definitions if needed
## Last Updated
2025-11-25 - Created after implementing RC channel state detection and completing all INAV operations
@@ -0,0 +1,297 @@
# timer() and whenChanged() Examples
## timer() Examples
### Example 1: Periodic VTX Power Boost
```javascript
// Boost VTX power to maximum for 1 second every 5 seconds
inav.events.timer(1000, 5000, () => {
inav.override.vtx.power = 4;
});
```
### Example 2: Flashing OSD Layout
```javascript
// Alternate between OSD layout 0 and 1 every second
inav.events.timer(1000, 1000, () => {
inav.override.osdLayout = 1;
});
```
### Example 3: Periodic Status Check
```javascript
// Record battery voltage every 10 seconds for 100ms
inav.events.timer(100, 10000, () => {
inav.gvar[0] = inav.flight.vbat;
inav.gvar[1] = inav.flight.current;
});
```
### Example 4: Warning Beep Pattern
```javascript
// Beep pattern: 200ms on, 200ms off, 200ms on, 5s off
// (Would need multiple timers or different approach for complex patterns)
inav.events.timer(200, 200, () => {
inav.override.rcChannel(8, 2000); // Beeper channel
});
```
## whenChanged() Examples
### Example 1: Altitude Change Logger
```javascript
// Log altitude whenever it changes by 50cm or more
inav.events.whenChanged(inav.flight.altitude, 50, () => {
inav.gvar[0] = inav.flight.altitude;
});
```
### Example 2: RSSI Drop Detection
```javascript
// Boost VTX power when RSSI drops by 10 or more
inav.events.whenChanged(inav.flight.rssi, 10, () => {
if (inav.flight.rssi < 50) {
inav.override.vtx.power = 4;
}
});
```
### Example 3: Speed Change Tracker
```javascript
// Track ground speed changes of 100cm/s or more
inav.events.whenChanged(inav.flight.groundSpeed, 100, () => {
inav.gvar[1] = inav.flight.groundSpeed;
});
```
### Example 4: Battery Voltage Monitor
```javascript
// Record voltage whenever it changes by 1 unit (0.1V)
inav.events.whenChanged(inav.flight.vbat, 1, () => {
inav.gvar[2] = inav.flight.vbat;
inav.gvar[3] = inav.flight.mahDrawn;
});
```
### Example 5: Climb Rate Detection
```javascript
// Detect rapid climbs (>50cm/s change in vertical speed)
inav.events.whenChanged(inav.flight.verticalSpeed, 50, () => {
if (inav.flight.verticalSpeed > 200) {
// Climbing fast - reduce throttle scale
inav.override.throttleScale = 80;
}
});
```
## Combined Examples
### Example 1: Timer + WhenChanged
```javascript
// Periodic VTX boost
inav.events.timer(1000, 5000, () => {
inav.override.vtx.power = 4;
});
// Log altitude changes
inav.events.whenChanged(inav.flight.altitude, 100, () => {
inav.gvar[0] = inav.flight.altitude;
});
```
### Example 2: Conditional Timer
```javascript
// Only run timer when armed
if (inav.flight.isArmed) {
inav.events.timer(500, 500, () => {
inav.override.osdLayout = 2;
});
}
```
### Example 3: Emergency Response System
```javascript
// Monitor RSSI drops
inav.events.whenChanged(inav.flight.rssi, 5, () => {
if (inav.flight.rssi < 30) {
// RSSI critical - max VTX power
inav.override.vtx.power = 4;
inav.gvar[7] = 1; // Set emergency flag
}
});
// Monitor altitude changes
inav.events.whenChanged(inav.flight.altitude, 200, () => {
if (inav.flight.altitude > 10000) {
// Too high - warn
inav.gvar[6] = inav.flight.altitude;
}
});
```
### Example 4: Data Logging System
```javascript
// Periodic logging every 5 seconds
inav.events.timer(100, 5000, () => {
inav.gvar[0] = inav.flight.vbat;
inav.gvar[1] = inav.flight.current;
inav.gvar[2] = inav.flight.altitude;
});
// Event-based logging on significant changes
inav.events.whenChanged(inav.flight.altitude, 500, () => {
inav.gvar[3] = inav.flight.altitude;
inav.gvar[4] = inav.flight.verticalSpeed;
});
inav.events.whenChanged(inav.flight.groundSpeed, 200, () => {
inav.gvar[5] = inav.flight.groundSpeed;
});
```
## Logic Condition Output Examples
### timer() Output
```javascript
inav.events.timer(1000, 2000, () => {
inav.gvar[0] = 1;
});
```
Generates:
```
logic 0 1 -1 49 0 1000 0 2000 0 # TIMER: 1000ms ON, 2000ms OFF
logic 1 1 0 18 0 0 0 0 1 0 # gvar[0] = 1
```
### whenChanged() Output
```javascript
inav.events.whenChanged(inav.flight.altitude, 50, () => {
inav.gvar[0] = inav.flight.altitude;
});
```
Generates:
```
logic 0 1 -1 50 2 12 0 50 0 # DELTA: altitude, threshold 50
logic 1 1 0 18 0 0 2 12 0 # gvar[0] = altitude
```
## Use Cases
### timer() Use Cases
1. **Periodic Tasks** - Execute actions at regular intervals
2. **Flashing Indicators** - Toggle states on/off
3. **Sampling** - Collect data periodically
4. **Timeouts** - Implement time-based state machines
5. **Warning Systems** - Beep or flash warnings
### whenChanged() Use Cases
1. **Event Detection** - React to significant changes
2. **Data Logging** - Record values when they change
3. **Thresholds** - Trigger actions on large changes
4. **Rate Limiting** - Avoid excessive updates (100ms window)
5. **Monitoring** - Track important parameter changes
## Best Practices
### timer()
- ✅ Use for periodic, time-based actions
- ✅ Keep durations reasonable (>100ms)
- ✅ Avoid very short ON times (may miss cycles)
- ❌ Don't use for one-time delays (use delay() instead)
- ❌ Don't nest timers (creates complex behavior)
### whenChanged()
- ✅ Use for event-driven responses
- ✅ Set thresholds appropriate to your data
- ✅ Monitor slowly-changing values
- ❌ Don't use for rapidly-changing values (may trigger constantly)
- ❌ Don't set threshold too low (noise will trigger it)
- ⚠️ Remember: 100ms detection window (built into DELTA operation)
## Common Patterns
### Pattern 1: Status Monitor
```javascript
// Log key parameters when they change significantly
inav.events.whenChanged(inav.flight.vbat, 2, () => { inav.gvar[0] = inav.flight.vbat; });
inav.events.whenChanged(inav.flight.rssi, 10, () => { inav.gvar[1] = inav.flight.rssi; });
inav.events.whenChanged(inav.flight.altitude, 100, () => { inav.gvar[2] = inav.flight.altitude; });
```
### Pattern 2: Periodic Beacon
```javascript
// Boost VTX power briefly every 10 seconds
inav.events.timer(500, 10000, () => {
inav.override.vtx.power = 4;
});
```
### Pattern 3: Adaptive Response
```javascript
// React to rapid altitude changes
inav.events.whenChanged(inav.flight.altitude, 200, () => {
if (inav.flight.altitude < 1000) {
inav.override.throttleScale = 120; // Boost
} else {
inav.override.throttleScale = 80; // Reduce
}
});
```
## Troubleshooting
### timer() Issues
**Problem:** Timer doesn't seem to trigger
- Check that ON duration > 0
- Check that OFF duration > 0
- Verify actions are valid
**Problem:** Timer seems irregular
- INAV runs at different rates depending on mode
- Timer precision is limited by LC execution rate
### whenChanged() Issues
**Problem:** Never triggers
- Check threshold isn't too high
- Verify value actually changes
- Remember: 100ms detection window
**Problem:** Triggers too often
- Increase threshold
- Value may be noisy
- Consider using timer() for periodic sampling instead
**Problem:** Doesn't track fast changes
- Built-in 100ms window
- Use timer() for faster sampling if needed
@@ -0,0 +1,156 @@
# timer() and whenChanged() Functions
## Overview
The `timer()` and `whenChanged()` functions provide advanced timing and change-detection capabilities for INAV logic programming.
## timer() Function
### Syntax
```javascript
inav.events.timer(onMs, offMs, () => {
// actions
});
```
### Description
Execute actions on a periodic timer with on/off cycling. The action executes during the "on" period and pauses during the "off" period.
### Parameters
- `onMs`: Duration in milliseconds to run the action
- `offMs`: Duration in milliseconds to wait between executions
- `action`: Arrow function containing actions to execute during on-time
### Example
```javascript
// Flash VTX power: ON for 1 second, OFF for 2 seconds, repeat
inav.events.timer(1000, 2000, () => {
inav.override.vtx.power = 4;
});
```
**Generated Logic Conditions:**
```
logic 0 1 -1 49 0 1000 0 2000 0 # TIMER: ON 1000ms, OFF 2000ms
logic 1 1 0 25 0 0 0 0 4 0 # Set VTX power = 4
```
### Validation Rules
- Requires exactly 3 arguments
- `onMs` must be numeric literal > 0
- `offMs` must be numeric literal > 0
- Third argument must be arrow function with actions
## whenChanged() Function
### Syntax
```javascript
inav.events.whenChanged(value, threshold, () => {
// actions
});
```
### Description
Execute actions when a monitored value changes by more than the specified threshold. Uses a built-in 100ms detection window.
### Parameters
- `value`: Flight parameter or global variable to monitor
- `threshold`: Minimum change required to trigger (numeric literal)
- `action`: Arrow function containing actions to execute on change
### Example
```javascript
// Log altitude whenever it changes by 50cm or more
inav.events.whenChanged(inav.flight.altitude, 50, () => {
inav.gvar[0] = inav.flight.altitude;
});
```
**Generated Logic Conditions:**
```
logic 0 1 -1 50 2 12 0 50 0 # DELTA: altitude, threshold 50
logic 1 1 0 18 0 0 2 12 0 # gvar[0] = altitude
```
### Validation Rules
- Requires exactly 3 arguments
- `value` must be a valid flight parameter or gvar
- `threshold` must be numeric literal > 0
- Third argument must be arrow function with actions
## Round-Trip Support
Both functions support perfect round-trip transpilation/decompilation:
### timer() Round-Trip
```javascript
// Original JavaScript
inav.events.timer(1000, 2000, () => { inav.gvar[0] = 1; });
// Transpiled to logic conditions
logic 0 1 -1 49 0 1000 0 2000 0
logic 1 1 0 18 0 0 0 0 1 0
// Decompiled back to JavaScript
inav.events.timer(1000, 2000, () => {
inav.gvar[0] = 1;
});
```
### whenChanged() Round-Trip
```javascript
// Original JavaScript
inav.events.whenChanged(inav.flight.altitude, 50, () => { inav.gvar[0] = inav.flight.altitude; });
// Transpiled to logic conditions
logic 0 1 -1 50 2 12 0 50 0
logic 1 1 0 18 0 0 2 12 0
// Decompiled back to JavaScript
inav.events.whenChanged(inav.flight.altitude, 50, () => {
inav.gvar[0] = inav.flight.altitude;
});
```
## API Definitions
Both functions are defined in `api/definitions/events.js`:
```javascript
timer: {
type: 'function',
desc: 'Execute action on a periodic timer (on/off cycling)',
params: {
onMs: { type: 'number', unit: 'ms', desc: 'Duration to run action' },
offMs: { type: 'number', unit: 'ms', desc: 'Duration to wait between executions' },
action: { type: 'function', desc: 'Action to execute during on-time' }
},
example: 'inav.events.timer(1000, 5000, () => { inav.override.vtx.power = 4; })'
},
whenChanged: {
type: 'function',
desc: 'Execute when value changes by more than threshold',
params: {
value: { type: 'number', desc: 'Value to monitor' },
threshold: { type: 'number', desc: 'Change threshold' },
action: { type: 'function', desc: 'Action to execute on change' }
},
example: 'inav.events.whenChanged(inav.flight.altitude, 100, () => { inav.gvar[0] = inav.flight.altitude; })'
}
```
## See Also
- **TIMER_WHENCHANGED_EXAMPLES.md** - More usage examples and patterns
- **JAVASCRIPT_PROGRAMMING_GUIDE.md** - Complete programming guide
- **events.js** - Full API definitions
@@ -0,0 +1,284 @@
# INAV API Definitions - Complete Implementation
## Overview
The INAV JavaScript API definitions are implemented in `js/transpiler/api/definitions/`. These files define the complete JavaScript API surface that maps to INAV firmware logic conditions.
## API Definition Files
### 1. ✅ **flight.js** - Flight Telemetry (READ-ONLY)
**Source**: `src/main/programming/logic_condition.c` (OPERAND_FLIGHT)
Contains ~40 flight parameters including:
- **Timing**: armTimer, flightTime
- **Distance**: homeDistance, tripDistance, homeDirection
- **Communication**: rssi
- **Battery**: vbat, cellVoltage, current, mahDrawn, mwhDrawn, batteryPercentage
- **GPS**: gpsSats, gpsValid
- **Speed/Altitude**: groundSpeed, altitude, verticalSpeed
- **Attitude**: roll, pitch, yaw, heading, throttlePos
- **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
### 2. ✅ **override.js** - Flight Control Overrides (WRITABLE)
**Source**: `src/main/programming/logic_condition.c` (OPERATION_OVERRIDE_*)
Contains override operations:
- **Throttle**: throttleScale, throttle
- **VTX** (nested): power, band, channel
- **Attitude** (nested): roll.angle, roll.rate, pitch.angle, pitch.rate, yaw.angle, yaw.rate
- **Heading**: heading override
- **RC Channels**: rcChannel[] array
- **Arming**: armSafety
- **OSD**: osdElement
### 3. ✅ **rc.js** - RC Receiver Channels (READ-ONLY)
**Source**: RC channel handling in firmware
18 RC channels (rc[0] through rc[17]), each with:
- `value`: Raw channel value (1000-2000µs)
- `low`: Channel < 1333µs
- `mid`: Channel 1333-1666µs
- `high`: Channel > 1666µs
### 4. ✅ **gvar.js** - Global Variables (READ/WRITE)
**Source**: `src/main/programming/global_variables.c`
8 global variables (gvar[0] through gvar[7]):
- Range: -1,000,000 to 1,000,000
- Used for storing/sharing data between logic conditions
### 5. ✅ **waypoint.js** - Waypoint Navigation (READ-ONLY)
**Source**: `src/main/navigation/navigation_pos_estimator.c`
Waypoint mission data:
- **Current WP**: number, action
- **Position**: latitude, longitude, altitude
- **Navigation**: distance, bearing
- **Status**: missionReached, missionValid
### 6. ✅ **pid.js** - Programming PID Controllers
**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
### 7. ✅ **helpers.js** - Math & Utility Functions
**Source**: `src/main/programming/logic_condition.c` (OPERATION_*)
Math functions:
- **Basic**: min, max, abs
- **Trig**: sin, cos, tan (degrees)
- **Mapping**: mapInput, mapOutput
- **Arithmetic**: add, sub, mul, div, mod (operators)
### 8. ✅ **events.js** - Event Handler Functions
**Source**: Logic condition framework
Event handlers:
- **on.arm**: Execute after arming with delay
- **on.always**: Execute every cycle
- **when**: Execute when condition true
- **sticky**: Execute between on/off conditions
- **edge**: Execute on rising edge
- **delay**: Execute after condition true for duration
- **timer**: Execute on periodic timer
- **whenChanged**: Execute when value changes
### 9. ✅ **index.js** - Main Export
Combines all definitions into single object.
## Implementation Status
| File | Status | Lines | Operands | Notes |
|------|--------|-------|----------|-------|
| flight.js | ✅ Ready | ~250 | FLIGHT(0-44) | All flight parameters |
| override.js | ✅ Ready | ~150 | OPS(23-46) | All override operations |
| rc.js | ✅ Ready | ~80 | RC(0-17) | 18 RC channels |
| gvar.js | ✅ Ready | ~30 | GVAR(0-7) | 8 global variables |
| waypoint.js | ✅ Ready | ~80 | WAYPOINT(0-8) | Waypoint nav data |
| pid.js | ✅ Ready | ~70 | PID(0-3) | 4 PID controllers |
| helpers.js | ✅ Ready | ~80 | OPS(14-39) | Math functions |
| events.js | ✅ Ready | ~120 | - | Event handlers |
| index.js | ✅ Ready | ~20 | - | Main export |
## Operand Type Mapping
From INAV firmware (`logic_condition.h`):
```c
typedef enum logicOperandType_s {
LOGIC_CONDITION_OPERAND_TYPE_VALUE = 0, // Literal number
LOGIC_CONDITION_OPERAND_TYPE_RC_CHANNEL = 1, // RC channel value
LOGIC_CONDITION_OPERAND_TYPE_FLIGHT = 2, // Flight parameter (flight.js)
LOGIC_CONDITION_OPERAND_TYPE_FLIGHT_MODE = 3, // Flight mode
LOGIC_CONDITION_OPERAND_TYPE_LC = 4, // Logic condition result
LOGIC_CONDITION_OPERAND_TYPE_GVAR = 5, // Global variable (gvar.js)
LOGIC_CONDITION_OPERAND_TYPE_PID = 6, // Programming PID (pid.js)
LOGIC_CONDITION_OPERAND_TYPE_WAYPOINTS = 7 // Waypoint (waypoint.js)
} logicOperandType_e;
```
## Operation Mapping
Key operations from firmware:
```c
// Conditionals
OPERATION_TRUE = 0
OPERATION_EQUAL = 1
OPERATION_GREATER_THAN = 2
OPERATION_LOWER_THAN = 3
OPERATION_LOW = 4
OPERATION_MID = 5
OPERATION_HIGH = 6
// Logical
OPERATION_AND = 7
OPERATION_OR = 8
OPERATION_NOT = 12
OPERATION_STICKY = 13
// Arithmetic
OPERATION_ADD = 14
OPERATION_SUB = 15
OPERATION_MUL = 16
OPERATION_DIV = 17
OPERATION_MOD = 18
// Global Variables
OPERATION_GVAR_SET = 19
OPERATION_INC_GVAR = 20
OPERATION_DEC_GVAR = 21
// Overrides
OPERATION_OVERRIDE_ARM_SAFETY = 23
OPERATION_OVERRIDE_ARMING_DISABLED = 24
OPERATION_OVERRIDE_THROTTLE_SCALE = 25
OPERATION_OVERRIDE_THROTTLE = 26
OPERATION_OVERRIDE_VTX_POWER = 27
OPERATION_OVERRIDE_VTX_BAND = 28
OPERATION_OVERRIDE_VTX_CHANNEL = 29
// Math
OPERATION_MIN = 30
OPERATION_MAX = 31
OPERATION_ABS = 32
OPERATION_SIN = 35
OPERATION_COS = 36
OPERATION_TAN = 37
OPERATION_MAP_INPUT = 38
OPERATION_MAP_OUTPUT = 39
```
## Flight Parameter Values
From firmware (`logic_condition.c`):
```c
LOGIC_CONDITION_OPERAND_FLIGHT_ARM_TIMER = 0
LOGIC_CONDITION_OPERAND_FLIGHT_HOME_DISTANCE = 1
LOGIC_CONDITION_OPERAND_FLIGHT_TRIP_DISTANCE = 2
LOGIC_CONDITION_OPERAND_FLIGHT_RSSI = 3
LOGIC_CONDITION_OPERAND_FLIGHT_VBAT = 4
LOGIC_CONDITION_OPERAND_FLIGHT_CELL_VOLTAGE = 5
LOGIC_CONDITION_OPERAND_FLIGHT_CURRENT = 6
LOGIC_CONDITION_OPERAND_FLIGHT_MAH_DRAWN = 7
LOGIC_CONDITION_OPERAND_FLIGHT_GPS_SATS = 9
LOGIC_CONDITION_OPERAND_FLIGHT_GROUND_SPEED = 11
LOGIC_CONDITION_OPERAND_FLIGHT_ALTITUDE = 12
// ... and more
```
## Usage After Implementation
Once these files are created:
### 1. Analyzer Auto-Updates
```javascript
// analyzer.js automatically picks up new properties
const apiDefinitions = require('./../api/definitions/index.js');
this.inavAPI = this.buildAPIStructure(apiDefinitions);
// No code changes needed!
```
### 2. Decompiler Auto-Updates
```javascript
// decompiler.js automatically maps operands
this.operandToProperty = this.buildOperandMapping(apiDefinitions);
// Decompiles FLIGHT(40) → inav.flight.compassHeading automatically
```
### 3. TypeScript Auto-Generation
```javascript
// types.js generates Monaco definitions
const dts = generateTypeDefinitions(apiDefinitions);
// IntelliSense shows all properties automatically
```
### 4. Adding New Properties
To add `flight.newSensor`:
1. Edit `flight.js`:
```javascript
newSensor: {
type: 'number',
desc: 'New sensor value',
inavOperand: { type: 2, value: 50 }
}
```
2. Done! Everything updates automatically:
- ✅ Analyzer validates `flight.newSensor`
- ✅ Decompiler recognizes operand 50
- ✅ TypeScript shows in autocomplete
- ✅ Code generator uses correct operand
## Verification Checklist
After creating these files:
- [ ] All files exist in `js/transpiler/api/definitions/`
- [ ] `index.js` exports all definitions
- [ ] Each property has `inavOperand` or `inavOperation`
- [ ] Operand values match INAV firmware
- [ ] Range values are correct
- [ ] Readonly flags are correct
- [ ] Nested objects are properly structured
- [ ] `analyzer.js` imports and uses definitions
- [ ] `decompiler.js` imports and uses definitions
- [ ] `types.js` generates TypeScript correctly
- [ ] Test transpilation works
- [ ] Test decompilation works
- [ ] Test Monaco autocomplete works
## References
- **INAV Source**: https://github.com/iNavFlight/inav
- `src/main/programming/logic_condition.c`
- `src/main/programming/logic_condition.h`
- `src/main/programming/global_variables.c`
- `src/main/programming/pid.c`
- **Documentation**: Programming Framework.md
- **Configurator**: programming.js, programming.html
## Benefits
**Single Source of Truth**: One place to edit
**Automatic Updates**: Add property → everything works
**Type Safety**: Proper TypeScript generation
**Maintainability**: Easy to keep in sync with INAV
**Documentation**: Self-documenting API
**Validation**: Comprehensive range/type checking
## Maintenance
When adding new properties or updating existing ones, see `api_maintenance_guide.md` for the complete workflow and best practices.
@@ -0,0 +1,409 @@
# API Definition Maintenance Guide
## Single Source of Truth
All INAV JavaScript API definitions are centralized in:
```
js/transpiler/api/definitions/
```
**When adding new INAV features, you only need to edit files in this directory.**
## Directory Structure
```
js/transpiler/api/definitions/
├── index.js # Exports all definitions
├── events.js # Event handlers (timer, whenChanged, etc.)
├── flight.js # Flight parameters (read-only)
├── gvar.js # Global variables (read/write)
├── helpers.js # Math & utility functions
├── override.js # Override settings (writable)
├── pid.js # Programming PID controllers
├── rc.js # RC channels (read/write)
└── waypoint.js # Waypoint navigation
```
## Definition Format
Each definition file exports objects following this structure:
```javascript
module.exports = {
propertyName: {
type: 'number' | 'boolean' | 'string' | 'object' | 'function',
desc: 'Human-readable description',
unit: 'Unit of measurement (optional)',
readonly: true | false,
range: [min, max], // Optional: valid value range
inavOperand: {
type: 2, // OPERAND_TYPE constant
value: 1 // Operand value for INAV
},
inavOperation: 27, // Optional: OPERATION constant
},
// Nested objects
nestedObject: {
type: 'object',
desc: 'Description',
properties: {
subProperty: {
type: 'number',
desc: 'Sub-property description',
// ... same structure as above
}
}
}
};
```
## What Uses These Definitions
### 1. **Semantic Analyzer** (`analyzer.js`)
- Validates property access
- Checks writable properties
- Validates value ranges
- **Auto-updates when definitions change**
### 2. **Type Definitions** (`types.js`)
- Generates TypeScript definitions for Monaco Editor
- Provides IntelliSense autocomplete
- **Auto-generates from definitions**
### 3. **Code Generator** (`codegen.js`)
- Maps JavaScript to INAV operands
- Uses `inavOperand` and `inavOperation` fields
- **Requires manual update for new operations**
### 4. **Decompiler** (`decompiler.js`)
- Reverse maps INAV to JavaScript
- Uses operand mappings from API definitions
- **Auto-updates when definitions change**
## Adding a New Property
### Example: Adding `flight.compassHeading`
**1. Edit `js/transpiler/api/definitions/flight.js`:**
```javascript
module.exports = {
// ... existing properties ...
compassHeading: {
type: 'number',
unit: '°',
desc: 'Compass heading in degrees (0-359)',
readonly: true,
range: [0, 359],
inavOperand: {
type: 2, // OPERAND_TYPE.FLIGHT
value: 40 // FLIGHT_PARAM.COMPASS_HEADING
}
}
};
```
**2. Update `inav_constants.js` (if needed):**
Only if adding a completely new INAV firmware feature:
```javascript
const FLIGHT_PARAM = {
// ... existing params ...
COMPASS_HEADING: 40
};
const FLIGHT_PARAM_NAMES = {
// ... existing names ...
[FLIGHT_PARAM.COMPASS_HEADING]: 'compassHeading'
};
```
**3. That's it!**
The following automatically update:
- ✅ Semantic analyzer validates `flight.compassHeading`
- ✅ TypeScript definitions show in autocomplete
- ✅ Range checking works automatically
- ✅ Decompiler recognizes the property
## Adding a New Writable Property
### Example: Adding `override.vtx.frequency`
**1. Edit `js/transpiler/api/definitions/override.js`:**
```javascript
module.exports = {
// ... existing properties ...
vtx: {
type: 'object',
desc: 'VTX control',
properties: {
power: { /* ... */ },
band: { /* ... */ },
channel: { /* ... */ },
// NEW PROPERTY
frequency: {
type: 'number',
unit: 'MHz',
desc: 'VTX frequency in MHz',
readonly: false, // Writable!
range: [5000, 6000],
inavOperation: 50 // New operation code
}
}
}
};
```
**2. Update `inav_constants.js`:**
```javascript
const OPERATION = {
// ... existing operations ...
OVERRIDE_VTX_FREQUENCY: 50
};
```
**3. Update `codegen.js` (manual):**
Add code generation logic:
```javascript
// In generateAction() method
if (stmt.target === 'inav.override.vtx.frequency') {
return this.pushLogicCommand(
OPERATION.OVERRIDE_VTX_FREQUENCY,
{ type: OPERAND_TYPE.VALUE, value: 0 },
this.valueOperand(stmt.value),
activatorId
);
}
```
## Adding a New Top-Level API Object
### Example: Adding `inav.sensors`
**1. Create `js/transpiler/api/definitions/sensors.js`:**
```javascript
'use strict';
module.exports = {
acc: {
type: 'boolean',
desc: 'Accelerometer sensor detected',
readonly: true,
inavOperand: {
type: 2, // FLIGHT
value: 50 // New param ID
}
},
mag: {
type: 'boolean',
desc: 'Magnetometer sensor detected',
readonly: true,
inavOperand: {
type: 2,
value: 51
}
}
// ... more sensors
};
```
**2. Update `js/transpiler/api/definitions/index.js`:**
```javascript
'use strict';
module.exports = {
flight: require('./inav.flight.js'),
override: require('./inav.override.js'),
rc: require('./rc.js'),
gvar: require('./gvar.js'),
waypoint: require('./inav.waypoint.js'),
pid: require('./pid.js'),
helpers: require('./helpers.js'),
events: require('./events.js'),
sensors: require('./sensors.js') // ADD THIS
};
```
**3. Update TypeScript types in `types.js` generation:**
The type generator should automatically pick it up, but verify:
```javascript
// In generateTypeDefinitions()
dts += generateInterfaceFromDefinition('sensors', apiDefinitions.sensors);
```
## Validation Checklist
When adding/modifying API definitions:
- [ ] Property has correct `type`
- [ ] Has descriptive `desc`
- [ ] Has `unit` if applicable
- [ ] `readonly` flag is correct
- [ ] `range` is specified for numeric values
- [ ] `inavOperand` maps to correct INAV constant
- [ ] `inavOperation` specified for writable properties
- [ ] Updated `index.js` if new file
- [ ] Updated `inav_constants.js` if new INAV feature
- [ ] Updated `codegen.js` for new writable properties
- [ ] Tested with sample code
- [ ] TypeScript definitions generate correctly
## Testing Changes
After modifying definitions:
```javascript
// 1. Test semantic analysis
const code = `
if (sensors.acc) {
// ...
}
`;
const transpiler = new Transpiler();
const result = transpiler.transpile(code);
// Should not have errors
// 2. Test type generation
const { generateTypeDefinitions } = require('./api/types.js');
const dts = generateTypeDefinitions(apiDefinitions);
// Should include new properties
// 3. Test in Monaco Editor
// Open configurator, verify autocomplete shows new properties
```
## Common Mistakes
### ❌ Wrong: Editing analyzer.js directly
```javascript
// DON'T DO THIS in analyzer.js:
this.inavAPI = {
'flight': {
properties: ['homeDistance', 'newProperty'] // Hard-coded!
}
};
```
### ✅ Right: Edit definition file
```javascript
// DO THIS in inav.flight.js:
module.exports = {
newProperty: {
type: 'number',
desc: 'New property',
// ...
}
};
```
### ❌ Wrong: Duplicating definitions
```javascript
// DON'T duplicate in multiple files
// decompiler.js - NO!
const FLIGHT_PARAMS = {
1: 'homeDistance'
};
// analyzer.js - NO!
properties: ['homeDistance']
```
### ✅ Right: Use centralized definitions
```javascript
// DO THIS - import from definitions
const apiDefinitions = require('./../api/definitions/index.js');
const flightDef = apiDefinitions.flight;
```
## File Dependencies
```
js/transpiler/api/definitions/
├── index.js
├── events.js
├── flight.js
├── gvar.js
├── helpers.js
├── override.js
├── pid.js
├── rc.js
└── waypoint.js
Used by:
├── analyzer.js (validation)
├── types.js (TypeScript generation)
├── codegen.js (code generation)
└── decompiler.js (via inav_constants.js)
```
## Migration from Hardcoded Values
If you find hardcoded API definitions elsewhere in the code:
1. **Identify the hardcoded values**
2. **Check if they exist in `api/definitions/`**
3. **If not, add them to appropriate definition file**
4. **Replace hardcoded values with imports**
5. **Test thoroughly**
6. **Remove old hardcoded definitions**
Example:
```javascript
// Before (hardcoded in analyzer.js)
this.inavAPI = {
'flight': {
properties: ['homeDistance', 'altitude']
}
};
// After (using definitions)
const apiDefinitions = require('./../api/definitions/index.js');
this.inavAPI = this.buildAPIStructure(apiDefinitions);
```
## Summary
**One Rule: Edit only `js/transpiler/api/definitions/*.js`**
Everything else updates automatically (except `codegen.js` which requires manual updates for new operations).
This ensures:
- ✅ Single source of truth
- ✅ No duplication
- ✅ Easy maintenance
- ✅ Fewer bugs
- ✅ Automatic validation
- ✅ Automatic type generation
Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 123 KiB

@@ -0,0 +1,217 @@
# INAV JavaScript Transpiler - Technical Overview
## Overview
The INAV JavaScript Transpiler is a bidirectional JavaScript ↔ INAV Logic Conditions system that allows users to write flight controller logic in JavaScript instead of raw logic condition commands.
**System Components:**
- **Transpiler**: JavaScript → INAV Logic Conditions
- **Decompiler**: INAV Logic Conditions → JavaScript
- **Semantic Analysis**: Full validation and error checking
- **Parser**: Production-grade using Acorn
- **Code Generation**: Optimized INAV CLI commands
- **Integration**: Monaco Editor with IntelliSense
## Architecture
```
┌─────────────────────────────────────────────────────────────┐
│ User Interface │
│ (Monaco Editor + Event Handlers) │
└───────────────┬───────────────────────┬─────────────────────┘
│ │
│ Transpile │ Load from FC
▼ ▼
┌───────────────────────────┐ ┌──────────────────────────────┐
│ TRANSPILER │ │ DECOMPILER │
│ (JavaScript → INAV) │ │ (INAV → JavaScript) │
└───────────────────────────┘ └──────────────────────────────┘
│ │
▼ ▼
┌──────────────────┐ ┌──────────────────┐
│ Parser (Acorn) │ │ Analyze & Group │
└────────┬─────────┘ └────────┬─────────┘
│ │
▼ ▼
┌──────────────────┐ ┌──────────────────┐
│ Semantic Analyzer│ │ Generate Code │
└────────┬─────────┘ └────────┬─────────┘
│ │
▼ │
┌──────────────────┐ │
│ Optimizer │ │
└────────┬─────────┘ │
│ │
▼ │
┌──────────────────┐ │
│ Code Generator │ │
└────────┬─────────┘ │
│ │
▼ ▼
┌─────────────────────────────────────────────────────────────┐
│ INAV Logic Conditions │
│ (Flight Controller MSP) │
└─────────────────────────────────────────────────────────────┘
```
## Key Features
### Transpiler (JavaScript → INAV)
**Robust Parsing**
- Uses Acorn for production-grade JavaScript parsing
- Handles all edge cases correctly
- Proper error messages with line/column numbers
**Comprehensive Validation**
- Variable scope checking
- Property access validation
- Range checking (gvar indices, heading values, etc.)
- Dead code detection
- Conflict detection
- Uninitialized variable detection
**Smart Code Generation**
- Optimized logic condition output
- Efficient operand usage
- Proper activator chaining
**Developer Experience**
- Monaco Editor integration
- Real-time syntax highlighting
- IntelliSense autocomplete
- Lint mode for fast feedback
- Detailed error messages with code context
### Decompiler (INAV → JavaScript)
**Intelligent Reconstruction**
- Pattern recognition for handler types
- Smart grouping of related conditions
- Preserves logical structure
**Comprehensive Coverage**
- All INAV operations supported
- Flight parameters
- Global variables
- Override operations
- Arithmetic operations
**Warning System**
- Alerts about lossy conversions
- Flags unsupported features
- Suggests manual review where needed
**Documentation**
- Inline comments in generated code
- Warning annotations
- Original logic condition references
## Usage Examples
### Example 1: Transpilation
**Input JavaScript:**
```javascript
if (inav.flight.homeDistance > 100) {
inav.override.vtx.power = 3;
}
```
**Output INAV Commands:**
```
logic 0 1 -1 2 2 1 0 100 0
logic 1 1 0 27 0 0 0 3 0
```
### Example 2: Decompilation
**Input INAV Commands:**
```
logic 0 1 -1 2 2 5 0 350 0
logic 1 1 0 25 0 0 0 50 0
```
**Output JavaScript:**
```javascript
if (inav.flight.cellVoltage < 350) {
inav.override.throttleScale = 50;
}
```
### Example 3: Full Round-Trip
**Original Code:**
```javascript
on.arm({ delay: 1 }, () => {
inav.gvar[0] = inav.flight.yaw;
});
if (inav.flight.homeDistance > 500) {
inav.override.vtx.power = 4;
inav.override.throttleScale = 75;
}
```
**Transpiled → Saved to FC → Loaded from FC:**
```javascript
// INAV Logic Conditions - Decompiled to JavaScript
// Note: Comments, variable names, and some structure may be lost
on.arm({ delay: 1 }, () => {
inav.gvar[0] = inav.flight.yaw;
});
if (inav.flight.homeDistance > 500) {
inav.override.vtx.power = 4;
inav.override.throttleScale = 75;
}
```
## Known Limitations
### Transpiler
1. **Subset of JavaScript**: Only supports INAV-specific syntax
2. **No complex expressions**: Nested function calls not supported
3. **Limited control flow**: Only if/else supported, no loops or complex functions
### Decompiler
1. **Lossy conversion**: Comments and variable names lost
2. **Structure changes**: Optimizations may alter original code
3. **Complex conditions**: May not perfectly reconstruct nested logic
4. **LC references**: References between logic conditions flagged for review
## Testing
### Running Tests
```bash
npm test parser.test.js
npm test analyzer.test.js
npm test decompiler.test.js
npm test integration.test.js
```
### Test Coverage
- Parser: Empty input, syntax errors, edge cases
- Analyzer: Validation, dead code, conflicts, ranges
- Transpiler: Full pipeline, error handling
- Decompiler: All operations, grouping, warnings
- Integration: Monaco editor, UI events, MSP communication
## Further Documentation
- **User Guide**: See `JAVASCRIPT_PROGRAMMING_GUIDE.md` for usage patterns
- **API Reference**: See `api_definitions_summary.md` for complete API
- **Maintenance**: See `api_maintenance_guide.md` for adding new features
- **Timer/WhenChanged**: See `TIMER_WHENCHANGED_EXAMPLES.md` for advanced patterns
+289
View File
@@ -0,0 +1,289 @@
# INAV JavaScript Programming Documentation
Complete documentation for the INAV JavaScript transpiler that allows programming flight controller logic conditions in JavaScript.
## 📚 Table of Contents
- [Quick Start](#quick-start)
- [User Guides](#user-guides)
- [Developer Documentation](#developer-documentation)
- [Features](#features)
---
## Quick Start
The INAV JavaScript transpiler converts JavaScript code into INAV logic conditions, enabling you to program flight controller behavior using familiar JavaScript syntax instead of raw logic condition commands.
### Features at a Glance
**✨ Modern Development Experience**
![IntelliSense Autocomplete](intellisense.png)
*Real-time autocomplete with type information and documentation*
![Helpful Warnings](warnings.png)
*Clear error messages with line numbers and suggestions*
### Basic Example
```javascript
// Increase VTX power when far from home
if (inav.flight.homeDistance > 500) {
inav.override.vtx.power = 4;
}
```
![VTX Power Control Example](example_vtx_power.png)
*Complete example showing VTX power control based on distance*
### RC Channel Override Example
![RC Override Example](example_override_rc.png)
*Using RC channels to control behavior*
---
## User Guides
### 📖 [JavaScript Programming Guide](JAVASCRIPT_PROGRAMMING_GUIDE.md)
**Start here if you're new to INAV JavaScript programming**
Quick reference covering:
- Pattern guide: `if`, `edge()`, `sticky()`, `delay()`, `timer()`, `whenChanged()`
- Common patterns (initialization, event counting, debouncing)
- Key differences between patterns
- Available objects and APIs
- Debugging techniques
### 🔧 [Operations Reference](OPERATIONS_REFERENCE.md)
**Complete reference for all supported operations**
Comprehensive guide to all INAV logic condition operations:
- ✅ Arithmetic: `+`, `-`, `*`, `/`, `%`
- ✅ Comparisons: `===`, `>`, `<`, `approxEqual()`
- ✅ Logical: `&&`, `||`, `!`, `xor()`, `nand()`, `nor()`
- ✅ Math: `Math.min()`, `Math.max()`, `Math.sin()`, `Math.cos()`, `Math.tan()`, `Math.abs()`
- ✅ Scaling: `mapInput()`, `mapOutput()`
- ✅ Flow control: `edge()`, `sticky()`, `delay()`, `timer()`, `whenChanged()`
- ✅ Variables: `gvar[0-7]`, `let`, `var`
- ✅ Overrides: `override.vtx.*`, `override.throttle`, `override.armSafety`, etc.
- ✅ RC channel states: `rc[n].low`, `rc[n].mid`, `rc[n].high`, `rc[n].value`
Includes usage examples and notes for each operation.
### ⏱️ [Timer and WhenChanged Examples](TIMER_WHENCHANGED_EXAMPLES.md)
**Practical examples for time-based and change-detection patterns**
Examples covering:
- Timer patterns (blinking, cycling, periodic actions)
- Change detection (RSSI monitoring, altitude tracking)
- Combined patterns with multiple conditions
- Real-world use cases
---
## Developer Documentation
### 🏗️ [Technical Implementation Overview](implementation_summary.md)
**Architecture and design of the transpiler system**
Covers:
- System architecture and component interaction
- Transpiler pipeline (Parser → Analyzer → Optimizer → Codegen)
- Decompiler pattern recognition
- Key features and capabilities
- Integration with Monaco Editor
### 🧪 [Testing Guide](TESTING_GUIDE.md)
**How to test changes to the transpiler**
Essential reading before making changes:
- Which components need updates (parser, analyzer, codegen, decompiler)
- Round-trip testing template (JavaScript → CLI → JavaScript)
- Common issues and solutions
- Verification checklist
- Step-by-step example of adding a new operation
**Always use round-trip testing when modifying the transpiler!**
### 🔌 [API Definitions Summary](api_definitions_summary.md)
**Structure of the INAV API definitions**
Documents the API definition system:
- Available API objects (`flight`, `override`, `rc`, `gvar`, `waypoint`, etc.)
- Property types and metadata
- INAV operand mappings
- How definitions drive IntelliSense
### 🛠️ [API Maintenance Guide](api_maintenance_guide.md)
**How to add, modify, or maintain API definitions**
Step-by-step instructions for:
- Adding new properties to existing APIs
- Creating new API objects
- Updating operand mappings
- Testing API changes
- Common pitfalls
### ⏲️ [Timer and WhenChanged Implementation](TIMER_WHENCHANGED_IMPLEMENTATION.md)
**Technical details of TIMER and DELTA operations**
Implementation guide for:
- How TIMER operation works (operations 49)
- How DELTA operation works (operation 50)
- AST representation
- Code generation strategy
- Decompiler pattern recognition
### ⚙️ [Generate Constants README](GENERATE_CONSTANTS_README.md)
**How to regenerate INAV constants from firmware**
Instructions for:
- Extracting operation codes from firmware
- Regenerating `inav_constants.js`
- Keeping constants in sync with firmware
- What to do when firmware adds new operations
---
## 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 Integration**: Full-featured code editor
- **IntelliSense**: Context-aware autocomplete with documentation
- **Real-time Validation**: Immediate feedback on syntax errors
- **Syntax Highlighting**: JavaScript syntax with INAV-specific extensions
- **Error Messages**: Clear, actionable error messages with line numbers
### Language Support
**All INAV Operations Supported:**
- Arithmetic operations
- Comparison and logical operations
- Math functions (trigonometry, min/max)
- Flow control (edge detection, sticky conditions, delays, timers)
- Variable management (global variables, let/var)
- RC channel access and state detection
- Flight parameter overrides
- Waypoint navigation
**JavaScript Features:**
- Namespaced API access: `inav.flight.*`, `inav.override.*`, `inav.events.*`
- `let`/`const` variables: compile-time constant substitution
- `var` variables: allocated to global variables
- Ternary operator: `condition ? value1 : value2`
- Arrow functions: `() => condition`
- Object property access: `inav.flight.altitude`, `inav.rc[0].value`
- Binary expressions: `+`, `-`, `*`, `/`, `%`
- Comparison operators: `>`, `<`, `===`
- Logical operators: `&&`, `||`, `!`
- Math methods: `Math.min()`, `Math.max()`, `Math.sin()`, etc.
- Flight mode detection: `inav.flight.mode.poshold`, `inav.flight.mode.rth`, etc.
- PID controller outputs: `inav.pid[0-3].output`
### Validation
- Property access validation against API definitions
- Range checking (gvar indices, heading values, etc.)
- Type checking for function arguments
- Dead code detection
- Uninitialized variable detection
- Helpful suggestions for common mistakes
### Optimization
- Common Subexpression Elimination (CSE)
- Efficient operand usage
- Optimized GVAR_INC/GVAR_DEC generation
- Minimal logic condition count
---
## File Organization
```
js/transpiler/
├── docs/ # Documentation (this directory)
│ ├── index.md # This file - documentation index
│ ├── JAVASCRIPT_PROGRAMMING_GUIDE.md # User guide
│ ├── OPERATIONS_REFERENCE.md # All operations
│ ├── TIMER_WHENCHANGED_EXAMPLES.md # Timer/change examples
│ ├── implementation_summary.md # Technical overview
│ ├── TESTING_GUIDE.md # Testing workflow
│ ├── api_definitions_summary.md # API structure
│ ├── api_maintenance_guide.md # API maintenance
│ ├── GENERATE_CONSTANTS_README.md # Constants generation
│ ├── TIMER_WHENCHANGED_IMPLEMENTATION.md # Implementation details
│ ├── intellisense.png # Screenshot: autocomplete
│ ├── warnings.png # Screenshot: errors
│ ├── example_vtx_power.png # Screenshot: VTX example
│ └── example_override_rc.png # Screenshot: RC example
├── api/ # API definitions
│ └── definitions/ # INAV API object definitions
├── editor/ # Monaco editor integration
│ ├── monaco_setup.js # Editor configuration
│ ├── intellisense.js # Autocomplete provider
│ └── diagnostics.js # Real-time validation
├── transpiler/ # Core transpiler
│ ├── index.js # Main transpiler entry point
│ ├── parser.js # JavaScript parser (Acorn wrapper)
│ ├── analyzer.js # Semantic analysis
│ ├── optimizer.js # Code optimization (CSE)
│ ├── codegen.js # INAV command generation
│ ├── decompiler.js # INAV → JavaScript
│ ├── inav_constants.js # Operation codes and types
│ ├── variable_handler.js # let/var variable management
│ ├── arrow_function_helper.js # Arrow function utilities
│ └── error_handler.js # Error collection
└── tools/ # Build tools
└── generate-constants.js # Extract constants from firmware
```
---
## Getting Help
### Common Questions
**Q: Which pattern should I use?**
- Use `if` for 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
See the [JavaScript Programming Guide](JAVASCRIPT_PROGRAMMING_GUIDE.md) for detailed examples.
**Q: How do I debug my code?**
Use global variables to track state and view them in the OSD or configurator. See the debugging section in the [JavaScript Programming Guide](JAVASCRIPT_PROGRAMMING_GUIDE.md).
**Q: What operations are supported?**
All INAV logic condition operations! See [Operations Reference](OPERATIONS_REFERENCE.md) for the complete list.
**Q: How do I contribute?**
Read the [Testing Guide](TESTING_GUIDE.md) to understand the testing workflow, then make your changes ensuring all 4 components (parser, analyzer, codegen, decompiler) are updated and round-trip tested.
---
## Version History
**2025-11-25**: Complete implementation
- All INAV logic condition operations now 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
Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB