diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 78896ddf5d3..bf911321f14 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -256,11 +256,15 @@ jobs: for f in ./build_SITL/*_SITL.exe; do mv $f $(echo $f | sed -e 's/_[0-9]\+\.[0-9]\+\.[0-9]\+//') done + - name: Copy cygwin1.dll + run: cp /bin/cygwin1.dll ./build_SITL/ - name: Upload artifacts uses: actions/upload-artifact@v4 with: name: ${{ env.BUILD_NAME }}_SITL-WIN - path: ./build_SITL/*.exe + path: | + ./build_SITL/*.exe + ./build_SITL/cygwin1.dll test: #needs: [build] diff --git a/docs/Battery.md b/docs/Battery.md index e519c325410..a66e6ae5361 100644 --- a/docs/Battery.md +++ b/docs/Battery.md @@ -169,6 +169,155 @@ current_meter_scale = (reported_draw_mAh / charging_data_mAh) * old_current_mete = 435 ``` +## Power and Current Limiting + +INAV includes an advanced power and current limiting system to protect your battery and ESCs from excessive discharge rates. This feature automatically reduces throttle output when current or power draw exceeds configured limits. + +### Why Use Power Limiting? + +Power and current limiting helps: +- **Protect batteries** from exceeding their C-rating and getting damaged +- **Prevent voltage sag** and brown-outs during high-throttle maneuvers +- **Extend battery lifespan** by avoiding excessive discharge rates +- **Improve safety** by preventing ESC or battery overheating +- **Comply with regulations** that may limit power output + +### How It Works + +The power limiter uses a PI (Proportional-Integral) controller to smoothly reduce throttle when current or power exceeds limits. It supports two operating modes: + +1. **Continuous Limit**: The sustained current/power that can be drawn indefinitely +2. **Burst Limit**: A higher current/power allowed for a short duration before falling back to the continuous limit + +This burst mode allows brief high-power maneuvers (like punch-outs or quick climbs) while protecting the battery during sustained high-throttle flight. + +### Configuration + +Power limiting requires a current sensor (`CURRENT_METER` feature). Power-based limiting additionally requires voltage measurement (`VBAT` feature). + +#### Basic Settings (per battery profile) + +| Setting | Description | Unit | Range | +|---------|-------------|------|-------| +| `limit_cont_current` | Continuous current limit | dA (deci-amps) | 0-2000 (0-200A) | +| `limit_burst_current` | Burst current limit | dA | 0-2000 (0-200A) | +| `limit_burst_current_time` | Duration burst is allowed | ds (deci-seconds) | 0-600 (0-60s) | +| `limit_burst_current_falldown_time` | Ramp-down duration from burst to continuous | ds | 0-600 (0-60s) | +| `limit_cont_power` | Continuous power limit | dW (deci-watts) | 0-20000 (0-2000W) | +| `limit_burst_power` | Burst power limit | dW | 0-20000 (0-2000W) | +| `limit_burst_power_time` | Duration burst power is allowed | ds | 0-600 (0-60s) | +| `limit_burst_power_falldown_time` | Ramp-down duration for power | ds | 0-600 (0-60s) | + +**Note**: Set any limit to `0` to disable that specific limiter. + +#### Advanced Tuning Settings + +| Setting | Description | Default | Range | +|---------|-------------|---------|-------| +| `limit_pi_p` | Proportional gain for PI controller | 100 | 10-500 | +| `limit_pi_i` | Integral gain for PI controller | 15 | 10-200 | +| `limit_attn_filter_cutoff` | Low-pass filter cutoff frequency | 50 Hz | 10-200 | + +### Example Configurations + +#### Example 1: Simple Current Limiting (50A continuous) + +Protect a 1500mAh 4S 50C battery (75A max burst, 50A continuous safe): + +``` +battery_profile 1 + +set limit_cont_current = 500 # 50A continuous +set limit_burst_current = 750 # 75A burst +set limit_burst_current_time = 100 # 10 seconds +set limit_burst_current_falldown_time = 20 # 2 second ramp-down +``` + +#### Example 2: Power Limiting for Racing (500W limit) + +Limit total system power for racing class restrictions: + +``` +battery_profile 1 + +set limit_cont_power = 4500 # 450W continuous +set limit_burst_power = 5000 # 500W burst +set limit_burst_power_time = 50 # 5 seconds +set limit_burst_power_falldown_time = 10 # 1 second ramp-down +``` + +#### Example 3: Combined Current and Power Limiting + +Protect both battery (current) and ESCs (power): + +``` +battery_profile 1 + +# Current limits (battery protection) +set limit_cont_current = 600 # 60A continuous +set limit_burst_current = 800 # 80A burst +set limit_burst_current_time = 100 # 10 seconds + +# Power limits (ESC protection) +set limit_cont_power = 8000 # 800W continuous +set limit_burst_power = 10000 # 1000W burst +set limit_burst_power_time = 100 # 10 seconds +``` + +### Understanding Burst Mode + +When you exceed the continuous limit, the system uses "burst reserve" (like a capacitor): +- **Burst reserve** starts full and depletes when current/power exceeds the continuous limit +- When reserve is empty, the limit drops to the continuous value +- The `falldown_time` setting creates a smooth ramp-down instead of an abrupt drop +- Reserve recharges when current/power drops below the continuous limit + +**Example timeline** (60A continuous, 80A burst, 10s burst time, 2s falldown): +``` +Time Limit Reason +---- ----- ------ +0s 80A Full burst reserve +5s 80A Still have reserve (using 5s of 10s) +10s 80A Reserve depleted +10-12s 80→60A Ramping down over 2 seconds +12s+ 60A Continuous limit active +``` + +### OSD Elements + +Three OSD elements display power limiting status: + +- **`OSD_PLIMIT_REMAINING_BURST_TIME`**: Shows remaining burst time in seconds +- **`OSD_PLIMIT_ACTIVE_CURRENT_LIMIT`**: Shows current limit being enforced (blinks when limiting) +- **`OSD_PLIMIT_ACTIVE_POWER_LIMIT`**: Shows power limit being enforced (blinks when limiting) + +Enable these in the OSD tab to monitor limiting during flight. + +### Calibration Tips + +1. **Find your battery's limits**: Check manufacturer specifications for continuous and burst C-ratings + - Continuous limit = `battery_capacity_mAh × continuous_C_rating / 100` (in dA) + - Burst limit = `battery_capacity_mAh × burst_C_rating / 100` (in dA) + +2. **Test incrementally**: Start with conservative limits and increase gradually + +3. **Monitor in flight**: Use OSD elements to see when limiting activates + +4. **Calibrate current sensor**: Accurate current readings are critical - see "Current Monitoring" section above + +5. **Tune PI controller**: If limiting feels abrupt or causes oscillation, adjust `limit_pi_p` and `limit_pi_i`: + - Increase P for faster response (may cause oscillation) + - Increase I for better steady-state accuracy + - Decrease if throttle oscillates during limiting + +### Notes + +- Power limiting is part of the battery profile system - each profile can have different limits +- Both current and power limiting can be active simultaneously - the most restrictive applies +- Limiting is applied smoothly via PI controller to avoid abrupt throttle cuts +- The system uses instantaneous current/power readings for responsive limiting +- Set limits to `0` to disable a specific limiter while keeping others active + ## Battery capacity monitoring For the capacity monitoring to work you need a current sensor (`CURRENT_METER` feature). For monitoring energy in milliWatt hour you also need voltage measurement (`VBAT` feature). For best results the current and voltage readings have to be calibrated. diff --git a/docs/Blackbox.md b/docs/Blackbox.md index 3d2fb68290d..b4528d8dd23 100644 --- a/docs/Blackbox.md +++ b/docs/Blackbox.md @@ -120,6 +120,7 @@ These chips are also supported: * Winbond W25Q64 - 64 Mbit / 8 MByte * Micron N25Q0128 - 128 Mbit / 16 MByte * Winbond W25Q128 - 128 Mbit / 16 MByte +* Puya PY25Q128HA - 128 Mbit / 16 MByte * Winbond W25N01 - 1 Gbit / 128 MByte * Winbond W25N02 - 2 Gbit / 256 MByte diff --git a/docs/OSD.md b/docs/OSD.md index e64aaef9d9b..44084266f8f 100644 --- a/docs/OSD.md +++ b/docs/OSD.md @@ -200,6 +200,7 @@ Here are the OSD Elements provided by INAV. | 165 | OSD_V_DIST_TO_FENCE | 8.0.0 | | | 166 | OSD_NAV_FW_ALT_CONTROL_RESPONSE | 8.0.0 | | | 167 | OSD_NAV_MIN_GROUND_SPEED | 9.0.0 | | +| 168 | OSD_THROTTLE_GAUGE | 9.0.0 | | # Pilot Logos diff --git a/docs/development/Development.md b/docs/development/Development.md index 03dbc5b0952..de8fbdecbfe 100755 --- a/docs/development/Development.md +++ b/docs/development/Development.md @@ -107,11 +107,13 @@ The main flow for a contributing is as follows: 7. `git add ` 8. `git commit` 9. `git push origin my-new-code` -10. Create pull request using github UI to merge your changes from your new branch into `inav/master` +10. Create pull request using github UI to merge your changes from your new branch into the appropriate target branch (see "Branching and release workflow" below) 11. Repeat from step 4 for new other changes. The primary thing to remember is that separate pull requests should be created for separate branches. Never create a pull request from your `master` branch. +**Important:** Most contributions should target a maintenance branch, not `master`. See the branching section below for guidance on choosing the correct target branch. + Later, you can get the changes from the INAV repo into your `master` branch by adding INAV as a git remote and merging from it as follows: 1. `git remote add upstream https://github.com/iNavFlight/inav.git` @@ -125,10 +127,105 @@ You can also perform the git commands using the git client inside Eclipse. Refe ## Branching and release workflow -Normally, all development occurs on the `master` branch. Every release will have it's own branch named `release_x.y.z`. +INAV uses maintenance branches for active development and releases. The `master` branch receives merges from maintenance branches. + +### Branch Types + +#### Maintenance Branches (Current and Next Major Version) + +**Current version branch** (e.g., `maintenance-9.x`): +- Used for backward-compatible changes +- Bug fixes, new features, and improvements that don't break compatibility +- Changes here will be included in the next release of the current major version (e.g., 9.1, 9.2) +- Does not create compatibility issues between firmware and configurator within the same major version + +**Next major version branch** (e.g., `maintenance-10.x`): +- Used for changes that introduce compatibility requirements +- Breaking changes that would cause issues between different major versions +- New features that require coordinated firmware and configurator updates +- Changes here will be included in the next major version release (e.g., 10.0) + +#### Master Branch + +The `master` branch receives periodic merges from maintenance branches. + +### Choosing the Right Target Branch + +When creating a pull request, target the appropriate branch: + +**Target the current version branch** (e.g., `maintenance-9.x`) if your change: +- Fixes a bug +- Adds a new feature that is backward-compatible +- Updates documentation +- Adds or updates hardware targets +- Makes improvements that work with existing releases + +**Target the next major version branch** (e.g., `maintenance-10.x`) if your change: +- Breaks compatibility with the current major version +- Requires coordinated firmware and configurator updates +- Changes MSP protocol in an incompatible way +- Modifies data structures in a breaking way + +### Release Workflow + +1. Development occurs on the current version maintenance branch (e.g., `maintenance-9.x`) +2. When ready for release, a release candidate is tagged from the maintenance branch +3. Bug fixes during the RC period continue on the maintenance branch +4. After final release, the maintenance branch is periodically merged into `master` +5. The cycle continues with the maintenance branch receiving new changes for the next release + +### Propagating Changes Between Maintenance Branches + +Changes committed to the current version branch should be merged forward to the next major version branch to prevent regressions. -During release candidate cycle we will follow the process outlined below: +**Maintainer workflow:** +- Changes merged to `maintenance-9.x` should be regularly merged into `maintenance-10.x` +- This ensures fixes and features aren't lost when the next major version is released +- Prevents users from experiencing bugs in v10.0 that were already fixed in v9.x -1. Create a release branch `release_x.y.z` -2. All bug fixes found in the release candidates will be merged into `release_x.y.z` branch and not into the `master`. -3. After final release is made, the branch `release_x.y.z` is locked, and merged into `master` bringing all of the bug fixes into the development branch. Merge conflicts that may arise at this stage are resolved manually. +**Example:** +```bash +# Merge changes from current to next major version +git checkout maintenance-10.x +git merge maintenance-9.x +git push upstream maintenance-10.x +``` + +### Example Timeline + +Current state (example): +- `maintenance-9.x` - Active development for INAV 9.1, 9.2, etc. +- `maintenance-10.x` - Breaking changes for future INAV 10.0 +- `master` - Receives periodic merges from maintenance branches + +After INAV 10.0 is released: +- `maintenance-10.x` - Active development for INAV 10.1, 10.2, etc. +- `maintenance-11.x` - Breaking changes for future INAV 11.0 +- `master` - Continues receiving merges + +### Working with Maintenance Branches + +To branch from the current maintenance branch instead of master: + +```bash +# Fetch latest changes +git fetch upstream + +# Create your feature branch from the maintenance branch +git checkout -b my-new-feature upstream/maintenance-9.x + +# Make changes, commit, and push +git push origin my-new-feature + +# Create PR targeting maintenance-9.x (not master) +``` + +When updating your fork: + +```bash +# Get the latest maintenance branch changes +git fetch upstream + +# Push directly from upstream to your fork (no local checkout needed) +git push origin upstream/maintenance-9.x:maintenance-9.x +``` diff --git a/docs/javascript_programming/GENERATE_CONSTANTS_README.md b/docs/javascript_programming/GENERATE_CONSTANTS_README.md index c012a24f120..dfea3be7979 100644 --- a/docs/javascript_programming/GENERATE_CONSTANTS_README.md +++ b/docs/javascript_programming/GENERATE_CONSTANTS_README.md @@ -87,6 +87,7 @@ const FLIGHT_PARAM = { }; // ... exports + ``` ## Build Integration @@ -109,21 +110,23 @@ This ensures constants are regenerated before each build. Currently, API definitions have hardcoded values: ```javascript -// flight.js - WRONG (hardcoded) +// inav.flight.js - WRONG (hardcoded) yaw: { inavOperand: { type: 2, value: 17 } // Wrong value! } + ``` Change to reference constants: ```javascript -// flight.js - CORRECT (references constants) +// 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: diff --git a/docs/javascript_programming/JAVASCRIPT_PROGRAMMING_GUIDE.md b/docs/javascript_programming/JAVASCRIPT_PROGRAMMING_GUIDE.md index 6609aa873b6..fedda8bb7ac 100644 --- a/docs/javascript_programming/JAVASCRIPT_PROGRAMMING_GUIDE.md +++ b/docs/javascript_programming/JAVASCRIPT_PROGRAMMING_GUIDE.md @@ -22,12 +22,12 @@ conditions behind the scenes. Use `if` statements for conditions that should check and execute **every cycle**: ```javascript -const { flight, override } = inav; // Checks every cycle - adjusts VTX power continuously -if (flight.homeDistance > 100) { - override.vtx.power = 3; +if (inav.flight.homeDistance > 100) { + inav.override.vtx.power = 3; } + ``` **Use when:** You want the action to happen continuously while the condition is true. @@ -38,13 +38,13 @@ if (flight.homeDistance > 100) { Use `edge()` for actions that should execute **only once** when a condition becomes true: ```javascript -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 +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:** @@ -64,16 +64,16 @@ edge(() => flight.armTimer > 1000, { duration: 0 }, () => { Use `sticky()` for conditions that latch ON and stay ON until reset: ```javascript -const { flight, gvar, sticky } = inav; // Latches ON when RSSI < 30, stays ON until RSSI > 70 -sticky( - () => flight.rssi < 30, // ON condition - () => flight.rssi > 70, // OFF condition +inav.events.sticky( + () => inav.flight.rssi < 30, // ON condition + () => inav.flight.rssi > 70, // OFF condition () => { - override.vtx.power = 4; // Executes while latched + inav.override.vtx.power = 4; // Executes while latched } ); + ``` **Parameters:** @@ -92,12 +92,12 @@ sticky( Use `delay()` to execute after a condition has been true for a duration: ```javascript -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 +inav.events.delay(() => inav.flight.rssi < 30, { duration: 2000 }, () => { + inav.gvar[0] = 1; // Set failsafe flag }); + ``` **Parameters:** @@ -116,73 +116,73 @@ delay(() => flight.rssi < 30, { duration: 2000 }, () => { ### Initialize on Arm ```javascript -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 +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 -const { flight, gvar, edge } = inav; // Initialize -edge(() => flight.armTimer > 1000, { duration: 0 }, () => { - gvar[0] = 0; +inav.events.edge(() => inav.flight.armTimer > 1000, { duration: 0 }, () => { + inav.gvar[0] = 0; }); // Count each time RSSI drops below 30 (counts transitions, not duration) -edge(() => flight.rssi < 30, { duration: 100 }, () => { - gvar[0] = gvar[0] + 1; +inav.events.edge(() => inav.flight.rssi < 30, { duration: 100 }, () => { + inav.gvar[0] = inav.gvar[0] + 1; }); + ``` ### Debounce Noisy Signals ```javascript -const { flight, override, edge } = inav; // Only trigger if RSSI < 30 for at least 500ms -edge(() => flight.rssi < 30, { duration: 500 }, () => { - override.vtx.power = 4; +inav.events.edge(() => inav.flight.rssi < 30, { duration: 500 }, () => { + inav.override.vtx.power = 4; }); + ``` ### Multi-Stage Logic ```javascript -const { flight, override } = inav; // Stage 1: Far away -if (flight.homeDistance > 500) { - override.vtx.power = 4; +if (inav.flight.homeDistance > 500) { + inav.override.vtx.power = 4; } // Stage 2: Medium distance -if (flight.homeDistance > 200 && flight.homeDistance <= 500) { - override.vtx.power = 3; +if (inav.flight.homeDistance > 200 && inav.flight.homeDistance <= 500) { + inav.override.vtx.power = 3; } // Stage 3: Close to home -if (flight.homeDistance <= 200) { - override.vtx.power = 2; +if (inav.flight.homeDistance <= 200) { + inav.override.vtx.power = 2; } + ``` ### Hysteresis/Deadband ```javascript -const { flight, gvar, sticky } = inav; // Turn ON at low voltage, turn OFF when recovered -sticky( - () => flight.cellVoltage < 330, // Warning threshold - () => flight.cellVoltage > 350, // Recovery threshold +inav.events.sticky( + () => inav.flight.cellVoltage < 330, // Warning threshold + () => inav.flight.cellVoltage > 350, // Recovery threshold () => { - override.throttleScale = 50; // Reduce throttle while in warning - gvar[0] = 1; // Warning flag + inav.override.throttleScale = 50; // Reduce throttle while in warning + inav.gvar[0] = 1; // Warning flag } ); + ``` --- @@ -198,27 +198,101 @@ sticky( --- +## 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 -const { - flight, // Flight telemetry - override, // Override flight parameters - rc, // RC channels - gvar, // Global variables (0-7) - waypoint, // Waypoint navigation - edge, // Edge detection - sticky, // Latching conditions - delay // Delayed execution -} = inav; +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 `edge()` with `flight.armTimer > 1000` -2. **Use gvars for state** - they persist between logic condition evaluations +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 @@ -231,17 +305,17 @@ const { Use global variables to track state: ```javascript -const { flight, gvar, edge } = inav; // Debug counter -edge(() => flight.armTimer > 1000, { duration: 0 }, () => { - gvar[7] = 0; // Use gvar[7] as 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 -edge(() => flight.rssi < 30, { duration: 0 }, () => { - gvar[7] = gvar[7] + 1; +inav.events.edge(() => inav.flight.rssi < 30, { duration: 0 }, () => { + inav.gvar[7] = inav.gvar[7] + 1; }); -// Check gvar[7] value in OSD or Configurator to see event count +// Check inav.gvar[7] value in OSD or Configurator to see event count + ``` diff --git a/docs/javascript_programming/OPERATIONS_REFERENCE.md b/docs/javascript_programming/OPERATIONS_REFERENCE.md index 3506abd2c56..eb967ae31bb 100644 --- a/docs/javascript_programming/OPERATIONS_REFERENCE.md +++ b/docs/javascript_programming/OPERATIONS_REFERENCE.md @@ -24,24 +24,25 @@ All INAV logic condition operations supported by the firmware are now fully impl ```javascript // XOR - true when exactly one condition is true -if (xor(flight.armed, flight.mode.failsafe)) { +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(flight.armed, gvar[0] > 100)) { +if (nand(inav.flight.armed, inav.gvar[0] > 100)) { // Not both conditions true } // NOR - true only when both are false -if (nor(flight.mode.failsafe, flight.mode.rth)) { +if (nor(inav.flight.mode.failsafe, inav.flight.mode.rth)) { // Neither failsafe nor RTH active } // Approximate equality with tolerance -if (approxEqual(flight.altitude, 1000, 50)) { +if (approxEqual(inav.flight.altitude, 1000, 50)) { // Altitude is 1000 ± 50 } + ``` ### Scaling/Mapping Operations @@ -49,7 +50,7 @@ if (approxEqual(flight.altitude, 1000, 50)) { ```javascript // mapInput: Scale from [0:maxValue] to [0:1000] // Example: RC value (1000-2000) to normalized (0-1000) -const normalizedThrottle = mapInput(rc[3].value - 1000, 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) @@ -57,41 +58,43 @@ const servoAngle = mapOutput(normalizedThrottle, 180); // Chaining for full range mapping // Map altitude (0-5000m) to percentage (0-100) -const altitudePercent = mapOutput(mapInput(flight.altitude, 5000), 100); +const altitudePercent = mapOutput(mapInput(inav.flight.altitude, 5000), 100); + ``` ### RC Channel State Detection ```javascript // LOW state - RC value < 1333us -if (rc[0].low) { +if (inav.rc[0].low) { // Roll stick is in low position - gvar[0] = 1; + inav.gvar[0] = 1; } // MID state - RC value between 1333-1666us -if (rc[1].mid) { +if (inav.rc[1].mid) { // Pitch stick is centered - gvar[1] = 1; + inav.gvar[1] = 1; } // HIGH state - RC value > 1666us -if (rc[2].high) { +if (inav.rc[2].high) { // Throttle stick is in high position - override.vtx.power = 4; + inav.override.vtx.power = 4; } // Access raw RC value -if (rc[3].value > 1700) { +if (inav.rc[3].value > 1700) { // Custom threshold on yaw channel - gvar[2] = 1; + inav.gvar[2] = 1; } // Combined with logical operations -if (rc[0].low && rc[1].mid && rc[2].high) { +if (inav.rc[0].low && inav.rc[1].mid && inav.rc[2].high) { // Specific stick combination detected - override.armSafety = 1; + inav.override.armSafety = 1; } + ``` ## Notes diff --git a/docs/javascript_programming/TESTING_GUIDE.md b/docs/javascript_programming/TESTING_GUIDE.md index 2e6910e8854..c44f4ad9202 100644 --- a/docs/javascript_programming/TESTING_GUIDE.md +++ b/docs/javascript_programming/TESTING_GUIDE.md @@ -51,11 +51,10 @@ import { Transpiler } from './transpiler/index.js'; import { Decompiler } from './transpiler/decompiler.js'; const testCode = ` -const { flight, gvar } = inav; // Your test code here -if (flight.altitude > 100) { - gvar[0] = 1; +if (inav.flight.altitude > 100) { + inav.gvar[0] = 1; } `; @@ -97,6 +96,7 @@ if (decompileResult.success) { } else { console.error('\n❌ Decompilation failed:', decompileResult.error); } + ``` ### Running Tests @@ -120,42 +120,45 @@ When making changes, test these scenarios: ### 1. Basic Functionality ```javascript -const { flight, gvar } = inav; -if (flight.altitude > 100) { - gvar[0] = 1; +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 (flight.invalidProperty > 100) { - gvar[0] = 1; +if (inav.flight.invalidProperty > 100) { + inav.gvar[0] = 1; } + ``` Expected: Error with suggestion for correct property ### 3. Edge Cases ```javascript // Test boundary values -if (rc[0].low) { // Channel 0 (first) - gvar[0] = 1; +if (inav.rc[0].low) { // Channel 0 (first) + inav.gvar[0] = 1; } -if (rc[17].high) { // Channel 17 (last valid) - gvar[1] = 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(rc[0].low, flight.armed)) { - gvar[0] = Math.max(100, flight.altitude); +if (xor(inav.rc[0].low, inav.flight.armed)) { + inav.gvar[0] = Math.max(100, inav.flight.altitude); } + ``` Expected: Proper nesting of operations @@ -205,6 +208,7 @@ const OPERATION = { FOOBAR: 99, // Check this exists // ... }; + ``` **2. Update Codegen** @@ -223,6 +227,7 @@ case 'CallExpression': { return resultIndex; } } + ``` **3. Update Decompiler** @@ -230,24 +235,26 @@ case 'CallExpression': { // 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 = ` -const { flight } = inav; if (foobar()) { - gvar[0] = 1; + inav.gvar[0] = 1; } `; // Run round-trip test... + ``` **6. Update Documentation** diff --git a/docs/javascript_programming/TIMER_WHENCHANGED_EXAMPLES.md b/docs/javascript_programming/TIMER_WHENCHANGED_EXAMPLES.md index 14a8635e81a..35b89d30f8c 100644 --- a/docs/javascript_programming/TIMER_WHENCHANGED_EXAMPLES.md +++ b/docs/javascript_programming/TIMER_WHENCHANGED_EXAMPLES.md @@ -4,184 +4,185 @@ ### Example 1: Periodic VTX Power Boost ```javascript -const { override, timer } = inav; // Boost VTX power to maximum for 1 second every 5 seconds -timer(1000, 5000, () => { - override.vtx.power = 4; +inav.events.timer(1000, 5000, () => { + inav.override.vtx.power = 4; }); + ``` ### Example 2: Flashing OSD Layout ```javascript -const { override, timer } = inav; // Alternate between OSD layout 0 and 1 every second -timer(1000, 1000, () => { - override.osdLayout = 1; +inav.events.timer(1000, 1000, () => { + inav.override.osdLayout = 1; }); + ``` ### Example 3: Periodic Status Check ```javascript -const { flight, gvar, timer } = inav; // Record battery voltage every 10 seconds for 100ms -timer(100, 10000, () => { - gvar[0] = flight.vbat; - gvar[1] = flight.current; +inav.events.timer(100, 10000, () => { + inav.gvar[0] = inav.flight.vbat; + inav.gvar[1] = inav.flight.current; }); + ``` ### Example 4: Warning Beep Pattern ```javascript -const { override, timer } = inav; // Beep pattern: 200ms on, 200ms off, 200ms on, 5s off // (Would need multiple timers or different approach for complex patterns) -timer(200, 200, () => { - override.rcChannel(8, 2000); // Beeper channel +inav.events.timer(200, 200, () => { + inav.override.rcChannel(8, 2000); // Beeper channel }); + ``` ## whenChanged() Examples ### Example 1: Altitude Change Logger ```javascript -const { flight, gvar, whenChanged } = inav; // Log altitude whenever it changes by 50cm or more -whenChanged(flight.altitude, 50, () => { - gvar[0] = flight.altitude; +inav.events.whenChanged(inav.flight.altitude, 50, () => { + inav.gvar[0] = inav.flight.altitude; }); + ``` ### Example 2: RSSI Drop Detection ```javascript -const { flight, override, whenChanged } = inav; // Boost VTX power when RSSI drops by 10 or more -whenChanged(flight.rssi, 10, () => { - if (flight.rssi < 50) { - override.vtx.power = 4; +inav.events.whenChanged(inav.flight.rssi, 10, () => { + if (inav.flight.rssi < 50) { + inav.override.vtx.power = 4; } }); + ``` ### Example 3: Speed Change Tracker ```javascript -const { flight, gvar, whenChanged } = inav; // Track ground speed changes of 100cm/s or more -whenChanged(flight.groundSpeed, 100, () => { - gvar[1] = flight.groundSpeed; +inav.events.whenChanged(inav.flight.groundSpeed, 100, () => { + inav.gvar[1] = inav.flight.groundSpeed; }); + ``` ### Example 4: Battery Voltage Monitor ```javascript -const { flight, gvar, whenChanged } = inav; // Record voltage whenever it changes by 1 unit (0.1V) -whenChanged(flight.vbat, 1, () => { - gvar[2] = flight.vbat; - gvar[3] = flight.mahDrawn; +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 -const { flight, override, whenChanged } = inav; // Detect rapid climbs (>50cm/s change in vertical speed) -whenChanged(flight.verticalSpeed, 50, () => { - if (flight.verticalSpeed > 200) { +inav.events.whenChanged(inav.flight.verticalSpeed, 50, () => { + if (inav.flight.verticalSpeed > 200) { // Climbing fast - reduce throttle scale - override.throttleScale = 80; + inav.override.throttleScale = 80; } }); + ``` ## Combined Examples ### Example 1: Timer + WhenChanged ```javascript -const { flight, override, gvar, timer, whenChanged } = inav; // Periodic VTX boost -timer(1000, 5000, () => { - override.vtx.power = 4; +inav.events.timer(1000, 5000, () => { + inav.override.vtx.power = 4; }); // Log altitude changes -whenChanged(flight.altitude, 100, () => { - gvar[0] = flight.altitude; +inav.events.whenChanged(inav.flight.altitude, 100, () => { + inav.gvar[0] = inav.flight.altitude; }); + ``` ### Example 2: Conditional Timer ```javascript -const { flight, override, timer } = inav; // Only run timer when armed -if (flight.isArmed) { - timer(500, 500, () => { - override.osdLayout = 2; +if (inav.flight.isArmed) { + inav.events.timer(500, 500, () => { + inav.override.osdLayout = 2; }); } + ``` ### Example 3: Emergency Response System ```javascript -const { flight, override, gvar, whenChanged } = inav; // Monitor RSSI drops -whenChanged(flight.rssi, 5, () => { - if (flight.rssi < 30) { +inav.events.whenChanged(inav.flight.rssi, 5, () => { + if (inav.flight.rssi < 30) { // RSSI critical - max VTX power - override.vtx.power = 4; - gvar[7] = 1; // Set emergency flag + inav.override.vtx.power = 4; + inav.gvar[7] = 1; // Set emergency flag } }); // Monitor altitude changes -whenChanged(flight.altitude, 200, () => { - if (flight.altitude > 10000) { +inav.events.whenChanged(inav.flight.altitude, 200, () => { + if (inav.flight.altitude > 10000) { // Too high - warn - gvar[6] = flight.altitude; + inav.gvar[6] = inav.flight.altitude; } }); + ``` ### Example 4: Data Logging System ```javascript -const { flight, gvar, timer, whenChanged } = inav; // Periodic logging every 5 seconds -timer(100, 5000, () => { - gvar[0] = flight.vbat; - gvar[1] = flight.current; - gvar[2] = flight.altitude; +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 -whenChanged(flight.altitude, 500, () => { - gvar[3] = flight.altitude; - gvar[4] = flight.verticalSpeed; +inav.events.whenChanged(inav.flight.altitude, 500, () => { + inav.gvar[3] = inav.flight.altitude; + inav.gvar[4] = inav.flight.verticalSpeed; }); -whenChanged(flight.groundSpeed, 200, () => { - gvar[5] = flight.groundSpeed; +inav.events.whenChanged(inav.flight.groundSpeed, 200, () => { + inav.gvar[5] = inav.flight.groundSpeed; }); + ``` ## Logic Condition Output Examples ### timer() Output ```javascript -timer(1000, 2000, () => { - gvar[0] = 1; +inav.events.timer(1000, 2000, () => { + inav.gvar[0] = 1; }); + ``` Generates: ``` @@ -191,9 +192,10 @@ logic 1 1 0 18 0 0 0 0 1 0 # gvar[0] = 1 ### whenChanged() Output ```javascript -whenChanged(flight.altitude, 50, () => { - gvar[0] = flight.altitude; +inav.events.whenChanged(inav.flight.altitude, 50, () => { + inav.gvar[0] = inav.flight.altitude; }); + ``` Generates: ``` @@ -239,29 +241,32 @@ logic 1 1 0 18 0 0 2 12 0 # gvar[0] = altitude ### Pattern 1: Status Monitor ```javascript // Log key parameters when they change significantly -whenChanged(flight.vbat, 2, () => { gvar[0] = flight.vbat; }); -whenChanged(flight.rssi, 10, () => { gvar[1] = flight.rssi; }); -whenChanged(flight.altitude, 100, () => { gvar[2] = flight.altitude; }); +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 -timer(500, 10000, () => { - override.vtx.power = 4; +inav.events.timer(500, 10000, () => { + inav.override.vtx.power = 4; }); + ``` ### Pattern 3: Adaptive Response ```javascript // React to rapid altitude changes -whenChanged(flight.altitude, 200, () => { - if (flight.altitude < 1000) { - override.throttleScale = 120; // Boost +inav.events.whenChanged(inav.flight.altitude, 200, () => { + if (inav.flight.altitude < 1000) { + inav.override.throttleScale = 120; // Boost } else { - override.throttleScale = 80; // Reduce + inav.override.throttleScale = 80; // Reduce } }); + ``` ## Troubleshooting diff --git a/docs/javascript_programming/TIMER_WHENCHANGED_IMPLEMENTATION.md b/docs/javascript_programming/TIMER_WHENCHANGED_IMPLEMENTATION.md index e26e96683c8..8befa685ff2 100644 --- a/docs/javascript_programming/TIMER_WHENCHANGED_IMPLEMENTATION.md +++ b/docs/javascript_programming/TIMER_WHENCHANGED_IMPLEMENTATION.md @@ -8,9 +8,10 @@ The `timer()` and `whenChanged()` functions provide advanced timing and change-d ### Syntax ```javascript -timer(onMs, offMs, () => { +inav.events.timer(onMs, offMs, () => { // actions }); + ``` ### Description @@ -23,12 +24,12 @@ Execute actions on a periodic timer with on/off cycling. The action executes dur ### Example ```javascript -const { override, timer } = inav; // Flash VTX power: ON for 1 second, OFF for 2 seconds, repeat -timer(1000, 2000, () => { - override.vtx.power = 4; +inav.events.timer(1000, 2000, () => { + inav.override.vtx.power = 4; }); + ``` **Generated Logic Conditions:** @@ -47,9 +48,10 @@ logic 1 1 0 25 0 0 0 0 4 0 # Set VTX power = 4 ### Syntax ```javascript -whenChanged(value, threshold, () => { +inav.events.whenChanged(value, threshold, () => { // actions }); + ``` ### Description @@ -62,12 +64,12 @@ Execute actions when a monitored value changes by more than the specified thresh ### Example ```javascript -const { flight, gvar, whenChanged } = inav; // Log altitude whenever it changes by 50cm or more -whenChanged(flight.altitude, 50, () => { - gvar[0] = flight.altitude; +inav.events.whenChanged(inav.flight.altitude, 50, () => { + inav.gvar[0] = inav.flight.altitude; }); + ``` **Generated Logic Conditions:** @@ -89,31 +91,33 @@ Both functions support perfect round-trip transpilation/decompilation: ### timer() Round-Trip ```javascript // Original JavaScript -timer(1000, 2000, () => { gvar[0] = 1; }); +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 -timer(1000, 2000, () => { - gvar[0] = 1; +inav.events.timer(1000, 2000, () => { + inav.gvar[0] = 1; }); + ``` ### whenChanged() Round-Trip ```javascript // Original JavaScript -whenChanged(flight.altitude, 50, () => { gvar[0] = flight.altitude; }); +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 -whenChanged(flight.altitude, 50, () => { - gvar[0] = flight.altitude; +inav.events.whenChanged(inav.flight.altitude, 50, () => { + inav.gvar[0] = inav.flight.altitude; }); + ``` ## API Definitions @@ -129,7 +133,7 @@ timer: { offMs: { type: 'number', unit: 'ms', desc: 'Duration to wait between executions' }, action: { type: 'function', desc: 'Action to execute during on-time' } }, - example: 'timer(1000, 5000, () => { override.vtx.power = 4; })' + example: 'inav.events.timer(1000, 5000, () => { inav.override.vtx.power = 4; })' }, whenChanged: { @@ -140,8 +144,9 @@ whenChanged: { threshold: { type: 'number', desc: 'Change threshold' }, action: { type: 'function', desc: 'Action to execute on change' } }, - example: 'whenChanged(flight.altitude, 100, () => { gvar[0] = flight.altitude; })' + example: 'inav.events.whenChanged(inav.flight.altitude, 100, () => { inav.gvar[0] = inav.flight.altitude; })' } + ``` ## See Also diff --git a/docs/javascript_programming/api_definitions_summary.md b/docs/javascript_programming/api_definitions_summary.md index 710af423808..a7e74bd3bcb 100644 --- a/docs/javascript_programming/api_definitions_summary.md +++ b/docs/javascript_programming/api_definitions_summary.md @@ -204,13 +204,15 @@ Once these files are created: 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) → flight.compassHeading automatically +// Decompiles FLIGHT(40) → inav.flight.compassHeading automatically + ``` ### 3. TypeScript Auto-Generation @@ -218,6 +220,7 @@ this.operandToProperty = this.buildOperandMapping(apiDefinitions); // types.js generates Monaco definitions const dts = generateTypeDefinitions(apiDefinitions); // IntelliSense shows all properties automatically + ``` ### 4. Adding New Properties @@ -230,6 +233,7 @@ newSensor: { desc: 'New sensor value', inavOperand: { type: 2, value: 50 } } + ``` 2. Done! Everything updates automatically: diff --git a/docs/javascript_programming/api_maintenance_guide.md b/docs/javascript_programming/api_maintenance_guide.md index 8e9c4c9f773..85804c1e8d3 100644 --- a/docs/javascript_programming/api_maintenance_guide.md +++ b/docs/javascript_programming/api_maintenance_guide.md @@ -56,6 +56,7 @@ module.exports = { } } }; + ``` ## What Uses These Definitions @@ -103,6 +104,7 @@ module.exports = { } } }; + ``` **2. Update `inav_constants.js` (if needed):** @@ -119,6 +121,7 @@ const FLIGHT_PARAM_NAMES = { // ... existing names ... [FLIGHT_PARAM.COMPASS_HEADING]: 'compassHeading' }; + ``` **3. That's it!** @@ -159,6 +162,7 @@ module.exports = { } } }; + ``` **2. Update `inav_constants.js`:** @@ -168,6 +172,7 @@ const OPERATION = { // ... existing operations ... OVERRIDE_VTX_FREQUENCY: 50 }; + ``` **3. Update `codegen.js` (manual):** @@ -176,7 +181,7 @@ Add code generation logic: ```javascript // In generateAction() method -if (stmt.target === 'override.vtx.frequency') { +if (stmt.target === 'inav.override.vtx.frequency') { return this.pushLogicCommand( OPERATION.OVERRIDE_VTX_FREQUENCY, { type: OPERAND_TYPE.VALUE, value: 0 }, @@ -184,6 +189,7 @@ if (stmt.target === 'override.vtx.frequency') { activatorId ); } + ``` ## Adding a New Top-Level API Object @@ -218,6 +224,7 @@ module.exports = { // ... more sensors }; + ``` **2. Update `js/transpiler/api/definitions/index.js`:** @@ -226,16 +233,17 @@ module.exports = { 'use strict'; module.exports = { - flight: require('./flight.js'), - override: require('./override.js'), + flight: require('./inav.flight.js'), + override: require('./inav.override.js'), rc: require('./rc.js'), gvar: require('./gvar.js'), - waypoint: require('./waypoint.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:** @@ -245,6 +253,7 @@ The type generator should automatically pick it up, but verify: ```javascript // In generateTypeDefinitions() dts += generateInterfaceFromDefinition('sensors', apiDefinitions.sensors); + ``` ## Validation Checklist @@ -271,7 +280,6 @@ After modifying definitions: ```javascript // 1. Test semantic analysis const code = ` -const { sensors } = inav; if (sensors.acc) { // ... } @@ -288,6 +296,7 @@ const dts = generateTypeDefinitions(apiDefinitions); // 3. Test in Monaco Editor // Open configurator, verify autocomplete shows new properties + ``` ## Common Mistakes @@ -300,11 +309,12 @@ this.inavAPI = { properties: ['homeDistance', 'newProperty'] // Hard-coded! } }; + ``` ### ✅ Right: Edit definition file ```javascript -// DO THIS in flight.js: +// DO THIS in inav.flight.js: module.exports = { newProperty: { type: 'number', @@ -312,6 +322,7 @@ module.exports = { // ... } }; + ``` ### ❌ Wrong: Duplicating definitions @@ -324,6 +335,7 @@ const FLIGHT_PARAMS = { // analyzer.js - NO! properties: ['homeDistance'] + ``` ### ✅ Right: Use centralized definitions @@ -331,6 +343,7 @@ properties: ['homeDistance'] // DO THIS - import from definitions const apiDefinitions = require('./../api/definitions/index.js'); const flightDef = apiDefinitions.flight; + ``` ## File Dependencies @@ -378,6 +391,7 @@ this.inavAPI = { // After (using definitions) const apiDefinitions = require('./../api/definitions/index.js'); this.inavAPI = this.buildAPIStructure(apiDefinitions); + ``` ## Summary diff --git a/docs/javascript_programming/implementation_summary.md b/docs/javascript_programming/implementation_summary.md index ea23161d4fd..2f6e532ce8b 100644 --- a/docs/javascript_programming/implementation_summary.md +++ b/docs/javascript_programming/implementation_summary.md @@ -113,11 +113,11 @@ The INAV JavaScript Transpiler is a bidirectional JavaScript ↔ INAV Logic Cond **Input JavaScript:** ```javascript -const { flight, override } = inav; -if (flight.homeDistance > 100) { - override.vtx.power = 3; +if (inav.flight.homeDistance > 100) { + inav.override.vtx.power = 3; } + ``` **Output INAV Commands:** @@ -136,11 +136,11 @@ logic 1 1 0 25 0 0 0 50 0 **Output JavaScript:** ```javascript -const { flight, override } = inav; -if (flight.cellVoltage < 350) { - override.throttleScale = 50; +if (inav.flight.cellVoltage < 350) { + inav.override.throttleScale = 50; } + ``` ### Example 3: Full Round-Trip @@ -148,13 +148,14 @@ if (flight.cellVoltage < 350) { **Original Code:** ```javascript on.arm({ delay: 1 }, () => { - gvar[0] = flight.yaw; + inav.gvar[0] = inav.flight.yaw; }); -if (flight.homeDistance > 500) { - override.vtx.power = 4; - override.throttleScale = 75; +if (inav.flight.homeDistance > 500) { + inav.override.vtx.power = 4; + inav.override.throttleScale = 75; } + ``` **Transpiled → Saved to FC → Loaded from FC:** @@ -162,16 +163,16 @@ if (flight.homeDistance > 500) { // INAV Logic Conditions - Decompiled to JavaScript // Note: Comments, variable names, and some structure may be lost -const { flight, override, rc, gvar, on } = inav; on.arm({ delay: 1 }, () => { - gvar[0] = flight.yaw; + inav.gvar[0] = inav.flight.yaw; }); -if (flight.homeDistance > 500) { - override.vtx.power = 4; - override.throttleScale = 75; +if (inav.flight.homeDistance > 500) { + inav.override.vtx.power = 4; + inav.override.throttleScale = 75; } + ``` ## Known Limitations diff --git a/docs/javascript_programming/index.md b/docs/javascript_programming/index.md index 272d344eb7a..ce8ad26dd27 100644 --- a/docs/javascript_programming/index.md +++ b/docs/javascript_programming/index.md @@ -28,12 +28,12 @@ The INAV JavaScript transpiler converts JavaScript code into INAV logic conditio ### Basic Example ```javascript -const { flight, override } = inav; // Increase VTX power when far from home -if (flight.homeDistance > 500) { - override.vtx.power = 4; +if (inav.flight.homeDistance > 500) { + inav.override.vtx.power = 4; } + ``` ![VTX Power Control Example](example_vtx_power.png) @@ -178,15 +178,18 @@ Instructions for: - Waypoint navigation **JavaScript Features:** -- `const` destructuring: `const { flight, override } = inav;` -- `let` variables: compile-time constant substitution +- 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: `flight.altitude`, `rc[0].value` +- 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 diff --git a/src/main/drivers/flash_m25p16.c b/src/main/drivers/flash_m25p16.c index 9842a80995d..2db61a3a166 100644 --- a/src/main/drivers/flash_m25p16.c +++ b/src/main/drivers/flash_m25p16.c @@ -99,6 +99,9 @@ struct { // Winbond W25Q128_DTR // Datasheet: https://www.winbond.com/resource-files/w25q128jv%20dtr%20revb%2011042016.pdf {0xEF7018, 256, 256}, + // Puya PY25Q128HA + // Datasheet: https://www.puyasemi.com/cpzx3/info_271_itemid_87.html + {0x856018, 256, 256}, // Winbond W25Q256 // Datasheet: https://www.winbond.com/resource-files/w25q256jv%20spi%20revb%2009202016.pdf {0xEF4019, 512, 256}, diff --git a/src/main/drivers/sdcard/sdcard_impl.h b/src/main/drivers/sdcard/sdcard_impl.h index 9fcb9a46902..3eba6095904 100644 --- a/src/main/drivers/sdcard/sdcard_impl.h +++ b/src/main/drivers/sdcard/sdcard_impl.h @@ -32,6 +32,7 @@ #define SDCARD_TIMEOUT_INIT_MILLIS 200 #define SDCARD_MAX_CONSECUTIVE_FAILURES 8 +#define SDCARD_MAX_OPERATION_RETRIES 3 typedef enum { // In these states we run at the initialization 400kHz clockspeed: @@ -62,6 +63,7 @@ typedef struct sdcard_t { uint32_t operationStartTime; uint8_t failureCount; + uint8_t operationRetries; // Retry count for current operation before reset uint8_t version; bool highCapacity; diff --git a/src/main/drivers/sdcard/sdcard_sdio.c b/src/main/drivers/sdcard/sdcard_sdio.c index e0fcb3f05d6..d7cd8ff3c54 100644 --- a/src/main/drivers/sdcard/sdcard_sdio.c +++ b/src/main/drivers/sdcard/sdcard_sdio.c @@ -460,18 +460,29 @@ static sdcardOperationStatus_e sdcardSdio_writeBlock(uint32_t blockIndex, uint8_ sdcard.state = SDCARD_STATE_SENDING_WRITE; if (SD_WriteBlocks_DMA(blockIndex, (uint32_t*) buffer, 512, block_count) != SD_OK) { - /* Our write was rejected! This could be due to a bad address but we hope not to attempt that, so assume - * the card is broken and needs reset. + /* Our write was rejected! Try a few times before giving up. + * This handles transient DMA/bus issues without full card reset. */ + if (sdcard.operationRetries < SDCARD_MAX_OPERATION_RETRIES) { + sdcard.operationRetries++; + // Brief delay before retry + delay(1); + return SDCARD_OPERATION_BUSY; + } + + // Max retries exceeded, reset card + sdcard.operationRetries = 0; sdcardSdio_reset(); // Announce write failure: if (sdcard.pendingOperation.callback) { sdcard.pendingOperation.callback(SDCARD_BLOCK_OPERATION_WRITE, sdcard.pendingOperation.blockIndex, NULL, sdcard.pendingOperation.callbackData); } - return SDCARD_OPERATION_FAILURE; + return SDCARD_OPERATION_FAILURE; } + // Success - reset retry counter + sdcard.operationRetries = 0; return SDCARD_OPERATION_IN_PROGRESS; } @@ -545,8 +556,22 @@ static bool sdcardSdio_readBlock(uint32_t blockIndex, uint8_t *buffer, sdcard_op sdcard.state = SDCARD_STATE_READING; sdcard.operationStartTime = millis(); + // Success - reset retry counter + sdcard.operationRetries = 0; return true; } else { + /* Read was rejected! Try a few times before giving up. + * This handles transient DMA/bus issues without full card reset. + */ + if (sdcard.operationRetries < SDCARD_MAX_OPERATION_RETRIES) { + sdcard.operationRetries++; + // Brief delay before retry + delay(1); + return false; + } + + // Max retries exceeded, reset card + sdcard.operationRetries = 0; sdcardSdio_reset(); if (sdcard.pendingOperation.callback) { sdcard.pendingOperation.callback( @@ -601,6 +626,7 @@ void sdcardSdio_init(void) sdcard.operationStartTime = millis(); sdcard.state = SDCARD_STATE_RESET; sdcard.failureCount = 0; + sdcard.operationRetries = 0; } /** diff --git a/src/main/drivers/usb_msc_h7xx.c b/src/main/drivers/usb_msc_h7xx.c index 9bd6bdc8b37..396a5951ba8 100644 --- a/src/main/drivers/usb_msc_h7xx.c +++ b/src/main/drivers/usb_msc_h7xx.c @@ -84,7 +84,9 @@ uint8_t mscStart(void) IOInit(IOGetByTag(IO_TAG(PA11)), OWNER_USB, 0, 0); IOInit(IOGetByTag(IO_TAG(PA12)), OWNER_USB, 0, 0); - USBD_Init(&USBD_Device, &VCP_Desc, 0); + // Use MSC descriptor for standalone MSC mode (not composite) + // This fixes Windows enumeration issue with new USB library v2.11.3 + USBD_Init(&USBD_Device, &MSC_Desc, 0); /** Regsiter class */ USBD_RegisterClass(&USBD_Device, USBD_MSC_CLASS); diff --git a/src/main/fc/config.h b/src/main/fc/config.h index 17c8ded8c1e..e3bde5f3eb7 100644 --- a/src/main/fc/config.h +++ b/src/main/fc/config.h @@ -63,7 +63,7 @@ typedef enum { FEATURE_PWM_OUTPUT_ENABLE = 1 << 28, FEATURE_OSD = 1 << 29, FEATURE_FW_LAUNCH = 1 << 30, - FEATURE_FW_AUTOTRIM = 1 << 31, + FEATURE_FW_AUTOTRIM = 1U << 31, } features_e; typedef struct systemConfig_s { diff --git a/src/main/fc/fc_msp.c b/src/main/fc/fc_msp.c index 936c708030d..3e81b028de2 100644 --- a/src/main/fc/fc_msp.c +++ b/src/main/fc/fc_msp.c @@ -564,6 +564,29 @@ static bool mspFcProcessOutCommand(uint16_t cmdMSP, sbuf_t *dst, mspPostProcessF sbufWriteU32(dst, logicConditionGetValue(i)); } break; + case MSP2_INAV_LOGIC_CONDITIONS_CONFIGURED: + { + // Returns 8-byte bitmask where bit N = 1 if logic condition N is configured (non-default) + uint64_t mask = 0; + for (int i = 0; i < MIN(MAX_LOGIC_CONDITIONS, 64); i++) { + const logicCondition_t *lc = logicConditions(i); + // Check if any field differs from default reset values + bool isConfigured = (lc->enabled != 0) || + (lc->activatorId != -1) || + (lc->operation != 0) || + (lc->operandA.type != LOGIC_CONDITION_OPERAND_TYPE_VALUE) || + (lc->operandA.value != 0) || + (lc->operandB.type != LOGIC_CONDITION_OPERAND_TYPE_VALUE) || + (lc->operandB.value != 0) || + (lc->flags != 0); + if (isConfigured) { + mask |= ((uint64_t)1 << i); + } + } + sbufWriteU32(dst, (uint32_t)(mask & 0xFFFFFFFF)); // Lower 32 bits + sbufWriteU32(dst, (uint32_t)((mask >> 32) & 0xFFFFFFFF)); // Upper 32 bits + } + break; case MSP2_INAV_GVAR_STATUS: for (int i = 0; i < MAX_GLOBAL_VARIABLES; i++) { sbufWriteU32(dst, gvGet(i)); diff --git a/src/main/flight/power_limits.c b/src/main/flight/power_limits.c index 1fe13240fe7..1770fd71a7f 100644 --- a/src/main/flight/power_limits.c +++ b/src/main/flight/power_limits.c @@ -73,7 +73,10 @@ static bool wasLimitingPower = false; #endif void powerLimiterInit(void) { - if (currentBatteryProfile->powerLimits.burstCurrent < currentBatteryProfile->powerLimits.continuousCurrent) { + // Only enforce burst >= continuous if burst is enabled (non-zero) + // A value of 0 means "disabled/unlimited", not "zero amps allowed" + if (currentBatteryProfile->powerLimits.burstCurrent > 0 && + currentBatteryProfile->powerLimits.burstCurrent < currentBatteryProfile->powerLimits.continuousCurrent) { currentBatteryProfileMutable->powerLimits.burstCurrent = currentBatteryProfile->powerLimits.continuousCurrent; } @@ -87,7 +90,9 @@ void powerLimiterInit(void) { pt1FilterInitRC(¤tThrLimitingBaseFilter, LIMITING_THR_FILTER_TCONST, 0); #ifdef USE_ADC - if (currentBatteryProfile->powerLimits.burstPower < currentBatteryProfile->powerLimits.continuousPower) { + // Only enforce burst >= continuous if burst is enabled (non-zero) + if (currentBatteryProfile->powerLimits.burstPower > 0 && + currentBatteryProfile->powerLimits.burstPower < currentBatteryProfile->powerLimits.continuousPower) { currentBatteryProfileMutable->powerLimits.burstPower = currentBatteryProfile->powerLimits.continuousPower; } diff --git a/src/main/io/dashboard.c b/src/main/io/dashboard.c index 26fde5f2378..18be89d85d1 100644 --- a/src/main/io/dashboard.c +++ b/src/main/io/dashboard.c @@ -172,8 +172,8 @@ static const char* const gpsFixTypeText[] = { "3D" }; -static const char* tickerCharacters = "|/-\\"; // use 2/4/8 characters so that the divide is optimal. -#define TICKER_CHARACTER_COUNT (sizeof(tickerCharacters) / sizeof(char)) +static const char tickerCharacters[] = "|/-\\"; // use 2/4/8 characters so that the divide is optimal. +#define TICKER_CHARACTER_COUNT (sizeof(tickerCharacters) - 1) static timeUs_t nextPageAt; static bool forcePageChange; diff --git a/src/main/msc/usbd_storage_sd_spi.c b/src/main/msc/usbd_storage_sd_spi.c index 4573d156bd8..26a38e00b6a 100644 --- a/src/main/msc/usbd_storage_sd_spi.c +++ b/src/main/msc/usbd_storage_sd_spi.c @@ -29,6 +29,7 @@ /* Includes ------------------------------------------------------------------*/ #include #include +#include #include "platform.h" #include "common/utils.h" @@ -39,6 +40,7 @@ #include "drivers/light_led.h" #include "drivers/io.h" #include "drivers/bus_spi.h" +#include "drivers/time.h" /* Private typedef -----------------------------------------------------------*/ /* Private define ------------------------------------------------------------*/ @@ -56,6 +58,12 @@ #define STORAGE_BLK_NBR 0x10000 #define STORAGE_BLK_SIZ 0x200 +// H7 SDIO DMA requires 32-byte aligned buffers +// USB MSC bot_data buffer is only 16-byte aligned, so we need an intermediate buffer +#if defined(STM32H7) && defined(USE_SDCARD_SDIO) +__attribute__((aligned(32))) static uint8_t alignedBuffer[512]; +#endif + static int8_t STORAGE_Init (uint8_t lun); #ifdef USE_HAL_DRIVER @@ -143,10 +151,21 @@ static int8_t STORAGE_Init (uint8_t lun) { UNUSED(lun); LED0_OFF; - sdcard_init(); - while (sdcard_poll() == 0); + + // Only initialize if not already initialized (e.g., by blackbox) + if (!sdcard_isInitialized()) { + sdcard_init(); + } + + // Poll with timeout to avoid infinite loop + uint32_t timeout = 0; + while (sdcard_poll() == 0 && timeout < 1000) { + timeout++; + delay(1); + } + LED0_ON; - return 0; + return (timeout < 1000) ? 0 : -1; } /******************************************************************************* @@ -213,9 +232,47 @@ static int8_t STORAGE_Read (uint8_t lun, UNUSED(lun); LED1_ON; for (int i = 0; i < blk_len; i++) { - while (sdcard_readBlock(blk_addr + i, buf + (512 * i), NULL, 0) == 0) + uint32_t timeout; +#if defined(STM32H7) && defined(USE_SDCARD_SDIO) + // H7 SDIO DMA requires 32-byte aligned buffers + // USB MSC buffer may not be aligned, so use intermediate buffer + timeout = 0; + while (sdcard_readBlock(blk_addr + i, alignedBuffer, NULL, 0) == 0) { sdcard_poll(); - while (sdcard_poll() == 0); + if (++timeout > 5000) { + LED1_OFF; + return -1; // Timeout waiting for read to start + } + delay(1); + } + timeout = 0; + while (sdcard_poll() == 0) { + if (++timeout > 5000) { + LED1_OFF; + return -1; // Timeout waiting for read to complete + } + delay(1); + } + memcpy(buf + (512 * i), alignedBuffer, 512); +#else + timeout = 0; + while (sdcard_readBlock(blk_addr + i, buf + (512 * i), NULL, 0) == 0) { + sdcard_poll(); + if (++timeout > 5000) { + LED1_OFF; + return -1; // Timeout waiting for read to start + } + delay(1); + } + timeout = 0; + while (sdcard_poll() == 0) { + if (++timeout > 5000) { + LED1_OFF; + return -1; // Timeout waiting for read to complete + } + delay(1); + } +#endif } LED1_OFF; return 0; @@ -235,10 +292,47 @@ static int8_t STORAGE_Write (uint8_t lun, UNUSED(lun); LED1_ON; for (int i = 0; i < blk_len; i++) { + uint32_t timeout; +#if defined(STM32H7) && defined(USE_SDCARD_SDIO) + // H7 SDIO DMA requires 32-byte aligned buffers + // USB MSC buffer may not be aligned, so use intermediate buffer + memcpy(alignedBuffer, buf + (i * 512), 512); + timeout = 0; + while (sdcard_writeBlock(blk_addr + i, alignedBuffer, NULL, 0) != SDCARD_OPERATION_IN_PROGRESS) { + sdcard_poll(); + if (++timeout > 5000) { + LED1_OFF; + return -1; // Timeout waiting for write to start + } + delay(1); + } + timeout = 0; + while (sdcard_poll() == 0) { + if (++timeout > 5000) { + LED1_OFF; + return -1; // Timeout waiting for write to complete + } + delay(1); + } +#else + timeout = 0; while (sdcard_writeBlock(blk_addr + i, buf + (i * 512), NULL, 0) != SDCARD_OPERATION_IN_PROGRESS) { sdcard_poll(); + if (++timeout > 5000) { + LED1_OFF; + return -1; // Timeout waiting for write to start + } + delay(1); } - while (sdcard_poll() == 0); + timeout = 0; + while (sdcard_poll() == 0) { + if (++timeout > 5000) { + LED1_OFF; + return -1; // Timeout waiting for write to complete + } + delay(1); + } +#endif } LED1_OFF; return 0; diff --git a/src/main/msp/msp_protocol_v2_inav.h b/src/main/msp/msp_protocol_v2_inav.h index 696d426cd78..0b893916895 100755 --- a/src/main/msp/msp_protocol_v2_inav.h +++ b/src/main/msp/msp_protocol_v2_inav.h @@ -89,6 +89,7 @@ #define MSP2_INAV_MISC2 0x203A #define MSP2_INAV_LOGIC_CONDITIONS_SINGLE 0x203B +#define MSP2_INAV_LOGIC_CONDITIONS_CONFIGURED 0x203C // Returns 8-byte bitmask of non-default logic conditions #define MSP2_INAV_ESC_RPM 0x2040 #define MSP2_INAV_ESC_TELEM 0x2041 diff --git a/src/main/rx/crsf.c b/src/main/rx/crsf.c index 6ac184b9c66..267f8176c69 100755 --- a/src/main/rx/crsf.c +++ b/src/main/rx/crsf.c @@ -160,6 +160,11 @@ STATIC_UNIT_TESTED void crsfDataReceive(uint16_t c, void *rxCallbackData) // full frame length includes the length of the address and framelength fields const int fullFrameLength = crsfFramePosition < 3 ? 5 : crsfFrame.frame.frameLength + CRSF_FRAME_LENGTH_ADDRESS + CRSF_FRAME_LENGTH_FRAMELENGTH; + if (fullFrameLength > CRSF_FRAME_SIZE_MAX) { + crsfFramePosition = 0; + return; + } + if (crsfFramePosition < fullFrameLength) { crsfFrame.bytes[crsfFramePosition++] = (uint8_t)c; crsfFrameDone = crsfFramePosition < fullFrameLength ? false : true; diff --git a/src/main/sensors/temperature.c b/src/main/sensors/temperature.c index 3d5c05f179c..fe5e80c792f 100644 --- a/src/main/sensors/temperature.c +++ b/src/main/sensors/temperature.c @@ -98,7 +98,7 @@ static void newSensorCheckAndEnter(uint8_t type, uint64_t addr) void temperatureInit(void) { - memset(sensorStatus, 0, sizeof(sensorStatus) * sizeof(*sensorStatus)); + memset(sensorStatus, 0, sizeof(sensorStatus)); sensorsSet(SENSOR_TEMP); diff --git a/src/main/target/BLUEBERRYF435WING/config.c b/src/main/target/BLUEBERRYF435WING/config.c index 050bfa4e750..2c6de82ad87 100644 --- a/src/main/target/BLUEBERRYF435WING/config.c +++ b/src/main/target/BLUEBERRYF435WING/config.c @@ -30,6 +30,7 @@ #include "fc/fc_msp_box.h" #include "io/serial.h" #include "io/piniobox.h" +#include "sensors/gyro.h" void targetConfiguration(void) { @@ -38,4 +39,11 @@ void targetConfiguration(void) serialConfigMutable()->portConfigs[findSerialPortIndexByIdentifier(SERIAL_PORT_USART1)].functionMask = FUNCTION_MSP; serialConfigMutable()->portConfigs[findSerialPortIndexByIdentifier(SERIAL_PORT_USART1)].msp_baudrateIndex = BAUD_115200; //pinioBoxConfigMutable()->permanentId[0] = BOX_PERMANENT_ID_USER1; + +#ifdef USE_DYNAMIC_FILTERS + // Disable dynamic notch filter by default (performance optimization for wing) + // This board is performance-constrained and wing aircraft typically don't need + // dynamic notch filtering (designed for multirotor motor noise) + gyroConfigMutable()->dynamicGyroNotchEnabled = 0; +#endif } diff --git a/src/main/target/OMNIBUSF4/CMakeLists.txt b/src/main/target/OMNIBUSF4/CMakeLists.txt index a6ccb483bf5..0e2bf2002c0 100644 --- a/src/main/target/OMNIBUSF4/CMakeLists.txt +++ b/src/main/target/OMNIBUSF4/CMakeLists.txt @@ -9,3 +9,4 @@ target_stm32f405xg(OMNIBUSF4V3_S6_SS) # OMNIBUSF4V3 is a (almost identical) variant of OMNIBUSF4PRO target, # except for an inverter on UART6. target_stm32f405xg(OMNIBUSF4V3) +target_stm32f405xg(OMNIBUSF4V3_ICM SKIP_RELEASES) diff --git a/src/main/target/OMNIBUSF4/target.h b/src/main/target/OMNIBUSF4/target.h index 2c5a27afbb6..fcd89fb0e46 100644 --- a/src/main/target/OMNIBUSF4/target.h +++ b/src/main/target/OMNIBUSF4/target.h @@ -30,6 +30,8 @@ #define TARGET_BOARD_IDENTIFIER "OBSD" #elif defined(OMNIBUSF4V3) #define TARGET_BOARD_IDENTIFIER "OB43" +#elif defined(OMNIBUSF4V3_ICM) +#define TARGET_BOARD_IDENTIFIER "OB4I" #elif defined(DYSF4PRO) #define TARGET_BOARD_IDENTIFIER "DYS4" #elif defined(DYSF4PROV2) @@ -67,7 +69,14 @@ #define MPU6000_CS_PIN PA4 #define MPU6000_SPI_BUS BUS_SPI1 -#if defined(OMNIBUSF4PRO) || defined(OMNIBUSF4V3) +#if defined(OMNIBUSF4V3_ICM) + #define USE_IMU_ICM42605 + #define IMU_ICM42605_ALIGN CW180_DEG + #define ICM42605_CS_PIN PA4 + #define ICM42605_SPI_BUS BUS_SPI1 +#endif + +#if defined(OMNIBUSF4PRO) || defined(OMNIBUSF4V3) || defined(OMNIBUSF4V3_ICM) #define USE_IMU_MPU6000 #define IMU_MPU6000_ALIGN CW270_DEG #else @@ -75,8 +84,8 @@ #define IMU_MPU6000_ALIGN CW180_DEG #endif -// Support for OMNIBUS F4 PRO CORNER - it has ICM20608 instead of MPU6000 -#if defined(OMNIBUSF4PRO) || defined(OMNIBUSF4V3) +// Support for OMNIBUS F4 PRO CORNER - it has MPU6500/ICM20608 instead of MPU6000 +#if defined(OMNIBUSF4PRO) || defined(OMNIBUSF4V3) || defined(OMNIBUSF4V3_ICM) #define MPU6500_CS_PIN MPU6000_CS_PIN #define MPU6500_SPI_BUS MPU6000_SPI_BUS #define USE_IMU_MPU6500 @@ -97,7 +106,7 @@ #define USE_BARO -#if defined(OMNIBUSF4PRO) || defined(OMNIBUSF4V3) +#if defined(OMNIBUSF4PRO) || defined(OMNIBUSF4V3) || defined(OMNIBUSF4V3_ICM) #define USE_BARO_BMP280 #define BMP280_SPI_BUS BUS_SPI3 #define BMP280_CS_PIN PB3 // v1 @@ -121,7 +130,7 @@ #define VBUS_SENSING_PIN PC5 #define VBUS_SENSING_ENABLED -#if defined(OMNIBUSF4PRO) || defined(OMNIBUSF4V3) +#if defined(OMNIBUSF4PRO) || defined(OMNIBUSF4V3) || defined(OMNIBUSF4V3_ICM) #define USE_UART_INVERTER #endif @@ -140,12 +149,12 @@ #define USE_UART6 #define UART6_RX_PIN PC7 #define UART6_TX_PIN PC6 -#if defined(OMNIBUSF4V3) +#if defined(OMNIBUSF4V3) || defined(OMNIBUSF4V3_ICM) #define INVERTER_PIN_UART6_RX PC8 #define INVERTER_PIN_UART6_TX PC9 #endif -#if defined(OMNIBUSF4V3) && !(defined(OMNIBUSF4V3_S6_SS) || defined(OMNIBUSF4V3_S5S6_SS) || defined(OMNIBUSF4V3_S5_S6_2SS)) +#if (defined(OMNIBUSF4V3) || defined(OMNIBUSF4V3_ICM)) && !(defined(OMNIBUSF4V3_S6_SS) || defined(OMNIBUSF4V3_S5S6_SS) || defined(OMNIBUSF4V3_S5_S6_2SS)) #define USE_SOFTSERIAL1 #define SOFTSERIAL_1_RX_PIN PC6 // shared with UART6 TX #define SOFTSERIAL_1_TX_PIN PC6 // shared with UART6 TX @@ -193,7 +202,7 @@ #define USE_SPI_DEVICE_1 -#if defined(OMNIBUSF4PRO) || defined(OMNIBUSF4V3) +#if defined(OMNIBUSF4PRO) || defined(OMNIBUSF4V3) || defined(OMNIBUSF4V3_ICM) #define USE_SPI_DEVICE_2 #define SPI2_NSS_PIN PB12 #define SPI2_SCK_PIN PB13 @@ -202,7 +211,7 @@ #endif #define USE_SPI_DEVICE_3 -#if defined(OMNIBUSF4PRO) || defined(OMNIBUSF4V3) +#if defined(OMNIBUSF4PRO) || defined(OMNIBUSF4V3) || defined(OMNIBUSF4V3_ICM) #define SPI3_NSS_PIN PA15 #else #define SPI3_NSS_PIN PB3 @@ -215,7 +224,7 @@ #define MAX7456_SPI_BUS BUS_SPI3 #define MAX7456_CS_PIN PA15 -#if defined(OMNIBUSF4PRO) || defined(OMNIBUSF4V3) +#if defined(OMNIBUSF4PRO) || defined(OMNIBUSF4V3) || defined(OMNIBUSF4V3_ICM) #define ENABLE_BLACKBOX_LOGGING_ON_SDCARD_BY_DEFAULT #define USE_SDCARD #define USE_SDCARD_SPI @@ -250,7 +259,7 @@ #define SENSORS_SET (SENSOR_ACC|SENSOR_MAG|SENSOR_BARO) #define USE_LED_STRIP -#if (defined(OMNIBUSF4PRO) || defined(OMNIBUSF4V3)) && !defined(OMNIBUSF4PRO_LEDSTRIPM5) +#if (defined(OMNIBUSF4PRO) || defined(OMNIBUSF4V3) || defined(OMNIBUSF4V3_ICM)) && !defined(OMNIBUSF4PRO_LEDSTRIPM5) #define WS2811_PIN PB6 #else #define WS2811_PIN PA1 diff --git a/src/main/vcp_hal/usbd_desc.c b/src/main/vcp_hal/usbd_desc.c index 48806761364..62834e83382 100644 --- a/src/main/vcp_hal/usbd_desc.c +++ b/src/main/vcp_hal/usbd_desc.c @@ -91,6 +91,24 @@ USBD_DescriptorsTypeDef VCP_Desc = { USBD_VCP_InterfaceStrDescriptor, }; +#ifdef USE_USB_MSC +/* MSC-specific descriptor functions */ +static uint8_t *USBD_MSC_DeviceDescriptorCallback(USBD_SpeedTypeDef speed, uint16_t *length); +static uint8_t *USBD_MSC_ProductStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length); +static uint8_t *USBD_MSC_ConfigStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length); +static uint8_t *USBD_MSC_InterfaceStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length); + +USBD_DescriptorsTypeDef MSC_Desc = { + USBD_MSC_DeviceDescriptorCallback, + USBD_VCP_LangIDStrDescriptor, // Reuse VCP LangID + USBD_VCP_ManufacturerStrDescriptor, // Reuse VCP Manufacturer + USBD_MSC_ProductStrDescriptor, // MSC-specific + USBD_VCP_SerialStrDescriptor, // Reuse VCP Serial + USBD_MSC_ConfigStrDescriptor, // MSC-specific + USBD_MSC_InterfaceStrDescriptor, // MSC-specific +}; +#endif + /* USB Standard Device Descriptor */ #if defined ( __ICCARM__ ) /*!< IAR Compiler */ #pragma data_alignment=4 @@ -338,4 +356,77 @@ static void IntToUnicode (uint32_t value , uint8_t *pbuf , uint8_t len) pbuf[ 2* idx + 1] = 0; } } + +#ifdef USE_USB_MSC +/** + * @brief Returns the MSC device descriptor. + * @param speed: Current device speed + * @param length: Pointer to data length variable + * @retval Pointer to descriptor buffer + */ +static uint8_t *USBD_MSC_DeviceDescriptorCallback(USBD_SpeedTypeDef speed, uint16_t *length) +{ + (void)speed; + *length = sizeof(USBD_MSC_DeviceDesc); + return (uint8_t*)USBD_MSC_DeviceDesc; +} + +/** + * @brief Returns the MSC product string descriptor. + * @param speed: Current device speed + * @param length: Pointer to data length variable + * @retval Pointer to descriptor buffer + */ +static uint8_t *USBD_MSC_ProductStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length) +{ + if (speed == USBD_SPEED_HIGH) + { + USBD_GetString((uint8_t *)"STM32 Mass Storage in HS Mode", USBD_StrDesc, length); + } + else + { + USBD_GetString((uint8_t *)"STM32 Mass Storage in FS Mode", USBD_StrDesc, length); + } + return USBD_StrDesc; +} + +/** + * @brief Returns the MSC configuration string descriptor. + * @param speed: Current device speed + * @param length: Pointer to data length variable + * @retval Pointer to descriptor buffer + */ +static uint8_t *USBD_MSC_ConfigStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length) +{ + if (speed == USBD_SPEED_HIGH) + { + USBD_GetString((uint8_t *)"MSC Config", USBD_StrDesc, length); + } + else + { + USBD_GetString((uint8_t *)"MSC Config", USBD_StrDesc, length); + } + return USBD_StrDesc; +} + +/** + * @brief Returns the MSC interface string descriptor. + * @param speed: Current device speed + * @param length: Pointer to data length variable + * @retval Pointer to descriptor buffer + */ +static uint8_t *USBD_MSC_InterfaceStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length) +{ + if (speed == USBD_SPEED_HIGH) + { + USBD_GetString((uint8_t *)"MSC Interface", USBD_StrDesc, length); + } + else + { + USBD_GetString((uint8_t *)"MSC Interface", USBD_StrDesc, length); + } + return USBD_StrDesc; +} +#endif /* USE_USB_MSC */ + /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/src/main/vcp_hal/usbd_desc.h b/src/main/vcp_hal/usbd_desc.h index 3baad094f40..57d50c4da30 100644 --- a/src/main/vcp_hal/usbd_desc.h +++ b/src/main/vcp_hal/usbd_desc.h @@ -60,6 +60,10 @@ /* Exported functions ------------------------------------------------------- */ extern USBD_DescriptorsTypeDef VCP_Desc; +#ifdef USE_USB_MSC +extern USBD_DescriptorsTypeDef MSC_Desc; +#endif + #endif /* __USBD_DESC_H */ /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/