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,251 @@
# Backup, Restore & Settings Migration — Architecture
> **Note:** This document describes the internals of the INAV Configurator's backup/restore and settings migration system. It is intended for Configurator developers, not end users. For user-facing documentation, see the [INAV docs](https://github.com/iNavFlight/inav/blob/master/docs/Backup%20and%20Restore.md).
## Architecture Overview
```
User → Firmware Flasher Tab → STM32.connect(onCliReady) → CLI mode
BackupRestore.captureCliDiffAll()
Save to file, prune old backups
STM32 flash (DFU or serial)
onFlashComplete() callback
Version check → Migration check → UI overlay
User confirms → Poll for FC reconnect
BackupRestore.performRestore() or performRestoreWithMigration()
saveAndReboot() or abortRestore()
```
## Files
| File | Purpose |
|------|---------|
| `js/backup_restore.js` | Core backup/restore module — CLI protocol, file I/O, auto-backup |
| `js/migration/migration_handler.js` | Version migration engine — profile chaining, line transformation |
| `js/migration/7_to_8.json` | Migration profile: INAV 7.x → 8.0 |
| `js/migration/8_to_9.json` | Migration profile: INAV 8.0 → 9.0 |
| `tabs/firmware_flasher.js` | Flash integration — auto-backup trigger, restore UI, version gating |
| `tabs/firmware_flasher.html` | Overlays and buttons for backup/restore/migration UI |
| `src/css/tabs/firmware_flasher.css` | Overlay styles |
| `js/protocols/stm32.js` | STM32 flash protocol — `onCliReady` callback, DFU timeout fix |
| `js/main/main.js` | Electron main process — IPC handlers for file operations |
| `js/main/preload.js` | IPC bridge — exposes backup API to renderer |
| `locale/en/messages.json` | All i18n translation keys |
## Adding a New Migration Profile
When a new major INAV version is released (e.g. 9.x → 10.x), create a migration profile:
### Step 1: Create the JSON profile
Create `js/migration/9_to_10.json`:
```json
{
"fromVersion": "9",
"toVersion": "10",
"description": "INAV 9.x → 10.0 migration profile",
"commandRenames": {
"old_command_name": "new_command_name"
},
"settingRenames": {
"old_setting_name": "new_setting_name"
},
"valueReplacements": {
"setting_name": {
"OLD_VALUE": "NEW_VALUE"
}
},
"removed": [
"deleted_setting_1",
"deleted_setting_2"
],
"settingPatternMappings": [
{
"pattern": "^regex_matching_setting_names$",
"valueMap": { "old_numeric_id": "new_numeric_id" },
"description": "Human-readable description of remapping"
}
],
"warnings": [
"Human-readable warning about settings whose semantics changed and need manual review."
]
}
```
### Step 2: Register the profile
In `js/migration/migration_handler.js`, add the import and append to the array:
```javascript
import profile_9_to_10 from './9_to_10.json';
const MIGRATION_PROFILES = [
profile_7_to_8,
profile_8_to_9,
profile_9_to_10, // ← add here
];
```
The migration engine automatically chains profiles. A 7.x → 10.x upgrade will apply all three profiles in sequence (7→8, 8→9, 9→10).
### How to determine what goes into a migration profile
Compare CLI settings between the old and new firmware version:
1. **Removed settings**: Run `diff all` on old and new firmware with default settings. Settings present in old but not in new → add to `removed`
2. **Renamed settings**: Check INAV release notes and source code for renamed settings → add to `settingRenames`
3. **Renamed commands**: Check for CLI command name changes (e.g. `profile``control_profile`) → add to `commandRenames`
4. **Value replacements**: Check for enum value name changes → add to `valueReplacements`
5. **Pattern mappings**: Check for bulk ID renumbering (OSD elements, etc.) → add to `settingPatternMappings`
6. **Warnings**: Check for settings where the meaning/units changed but name stayed the same → add to `warnings`
Key INAV source files to check:
- `src/main/fc/settings.yaml` — all CLI settings definitions
- `src/main/fc/cli.c` — CLI command implementations
- Release notes on GitHub
## Migration Profile Schema Reference
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `fromVersion` | `string` | Yes | Source major version number (e.g. `"9"`) |
| `toVersion` | `string` | Yes | Target major version number (e.g. `"10"`) |
| `description` | `string` | Yes | Human-readable description |
| `commandRenames` | `object` | Yes | Maps old CLI command names to new names. Applied to any token in the command line. E.g. `"profile" → "control_profile"` transforms `profile 2` to `control_profile 2` |
| `settingRenames` | `object` | Yes | Maps old `set` setting names to new names. Only applies to `set <name> = <value>` lines |
| `valueReplacements` | `object` | Yes | Maps setting names to value replacement objects `{ "oldval": "newval" }`. Only modifies the value portion after `=` |
| `removed` | `string[]` | Yes | List of setting names to remove entirely. Lines with `set <name> = ...` matching these are dropped |
| `settingPatternMappings` | `array` | Yes | Array of pattern-based value remappings for settings matching a regex. Each entry has `pattern` (regex), `valueMap` (object), `description` (string) |
| `warnings` | `string[]` | Yes | Warning messages about semantic changes requiring manual review. Displayed in migration preview overlay |
## Existing Migration Profile Details
### 7_to_8.json (INAV 7.x → 8.0)
| Category | Changes |
|----------|---------|
| **Command renames** | `profile``control_profile` |
| **Value replacements** | `gps_provider`: `UBLOX7``UBLOX` |
| **Removed settings** (18) | `control_deadband`, `cpu_underclock`, `disarm_kill_switch`, `dji_workarounds`, `fw_iterm_limit_stick_position`, `gyro_anti_aliasing_lpf_type`, `gyro_hardware_lpf`, `gyro_main_lpf_type`, `gyro_use_dyn_lpf`, `inav_use_gps_no_baro`, `inav_use_gps_velned`, `ledstrip_visual_beeper`, `max_throttle`, `nav_auto_climb_rate`, `nav_manual_climb_rate`, `osd_stats_min_voltage_unit`, `pidsum_limit`, `pidsum_limit_yaw` |
| **Pattern mappings** | `osd_custom_element_N_type`: IDs remapped 4→9, 5→16, 6→7, 7→10 |
| **Warnings** | `nav_fw_wp_tracking_accuracy` semantics changed: was arbitrary tracking response, now distance in meters |
### 8_to_9.json (INAV 8.0 → 9.0)
| Category | Changes |
|----------|---------|
| **Command renames** | `controlrate_profile``use_control_profile` |
| **Setting renames** | `mixer_pid_profile_linking``mixer_control_profile_linking`, `osd_pan_servo_pwm2centideg``osd_pan_servo_range_decadegrees` |
| **Value replacements** | None |
| **Removed settings** | None |
| **Pattern mappings** | None |
| **Warnings** | Position estimator defaults changed (`w_z_baro_v`, `inav_w_z_gps_p`, `inav_w_z_gps_v`). `ahrs_acc_ignore_rate` default changed 20→15 |
## Migration Engine Internals
### Profile chaining
`buildMigrationChain(fromVersion, toVersion)` selects all profiles where `profileFrom >= fromMajor` and `profileTo <= toMajor`, sorted by `fromVersion`. A 7.x → 9.x migration applies both 7→8 and 8→9 profiles in sequence.
### Line processing
Each non-comment, non-empty line passes through every profile in the chain. For each profile, transformations are applied in this order:
1. Command renames (any token in the line)
2. Removed settings (line dropped if `set <name>` matches)
3. Setting renames (`set <name>` replacement)
4. Value replacements (value after `=` replaced)
5. Setting pattern mappings (regex-matched settings with value remapping)
### Missing profile detection
`hasMissingProfiles()` returns `true` when the number of profiles in the chain is fewer than the number of major version steps. The UI shows a warning but still allows restore — some settings may fail.
## Edge Cases Handled
1. **Stale FC version after flash**: Real FC version is queried via `MSP_FC_VERSION` after connect, not the cached value
2. **DFU mode (no MSP)**: `FC.CONFIG` null checks prevent crashes when connected in DFU mode
3. **DFU timeout**: UI unlock and progress label update on timeout (no permanent lock)
4. **Local firmware files**: `localFirmwareLoaded` flag prevents stale dropdown version from triggering wrong migration
5. **Backup pruning with mixed versions**: Sort by timestamp portion, not full filename
6. **Multi-step migration**: 7.x → 9.x automatically chains 7→8 + 8→9 profiles
7. **Missing migration profiles**: Warning shown but restore allowed — graceful degradation
8. **Version detection from backup**: Parsed from backup header (`# Version: X.Y.Z`), not from FC state
## i18n Keys
All backup/restore/migration translation keys in `locale/en/messages.json`:
### Backup status
| Key | Text |
|-----|------|
| `backupRestoreStatusEnteringCli` | Entering CLI |
| `backupRestoreStatusReadingConfig` | Reading configuration via CLI |
| `backupRestoreStatusSavingFile` | Saving backup file... |
| `backupRestoreStatusExitingCli` | Exiting CLI mode |
| `backupRestoreBackupSaved` | Backup saved $1 |
| `backupRestoreAutoBackupSaved` | Auto-backup saved to $1 |
| `backupRestoreBackupComplete` | Backup complete |
| `backupRestoreBackupCancelled` | Backup cancelled |
| `backupRestoreBackupFailed` | Backup failed |
### Restore status
| Key | Text |
|-----|------|
| `backupRestoreStatusConnecting` | Connecting to flight controller |
| `backupRestoreStatusRestoringConfig` | Restoring configuration |
| `backupRestoreStatusRestoringProgress` | Restoring... $1 / $2 |
| `backupRestoreStatusSaving` | Saving configuration |
| `backupRestoreRestoreComplete` | Configuration restored. Flight controller is rebooting. |
| `backupRestoreRestoreCancelled` | Restore cancelled. |
| `backupRestoreRestoreFailed` | Restore failed. |
### Auto-restore UI
| Key | Text |
|-----|------|
| `backupRestoreAutoRestoreConfirm` | Restore confirmation prompt |
| `backupRestoreAutoRestoreWaiting` | Waiting for FC to reboot after flash |
| `backupRestoreAutoRestoreYes` | Yes, restore settings |
| `backupRestoreAutoRestoreNo` | No, keep current settings |
| `backupRestoreAutoRestoreWaitingPort` | Waiting for port $1 to reconnect |
| `backupRestoreDowngradeNoAutoRestore` | Major downgrade warning |
| `backupRestoreFlashCompleteBackupSaved` | Backup saved (local firmware, no restore offer) |
| `backupRestoreMigrationApplied` | Migration applied: $1 → $2 ($3 changes) |
| `backupRestoreMigrationWarningsHeader` | Migration Warnings: |
### Migration preview
| Key | Text |
|-----|------|
| `migrationPreviewTitle` | Settings Migration Required |
| `migrationPreviewSubtitle` | Conversion explanation |
| `migrationPreviewRemovedHeader` | Removed Settings: |
| `migrationPreviewRenamedSettingsHeader` | Renamed Settings: |
| `migrationPreviewRenamedCommandsHeader` | Renamed Commands: |
| `migrationPreviewValueReplacementsHeader` | Value Replacements: |
| `migrationPreviewSettingRemappingsHeader` | Setting Remappings: |
| `migrationPreviewContinue` | Continue with migration |
| `migrationPreviewCancel` | Cancel restore |
| `migrationMissingProfileWarning` | Missing profile warning |
### Error messages
| Key | Text |
|-----|------|
| `backupRestoreErrorTitle` | Restore Errors Detected |
| `backupRestoreErrorText` | Error explanation |
| `backupRestoreErrorAbort` | Abort |
| `backupRestoreErrorSave` | Save anyway |
+20
View File
@@ -0,0 +1,20 @@
# Bitmaps
The bitmapDecompress function in INAV is currently designed to work with RLE compressed monochrome bitmaps.
## Image format
![Armed RLE Bitmap](assets/armed128x32.bmp)
Images supported by this function are required to be 128 pixels wide, with the exception being an image only 8 pixels in height. The image height should be divisible by 8, but not be larger than the display height of 64. The bitmap image should be monochrome, 1-bit.
Example Photoshop new image settings:
![Photoshop](assets/photoshop.jpg)
## Compression and HEX conversion
Your new bitmap file will need to be converted to run-length encoding compressed hexidecimal. The [Gabotronics Image to HEX Converter v1.03](http://www.gabotronics.com/download/resources/bmp-converter.zip) works very nicely for this.
The proper settings are shown in the screenshot below:
![Photoshop](assets/gabotronics.jpg)
+580
View File
@@ -0,0 +1,580 @@
# Blackbox logging internals
The Blackbox is designed to record the raw internal state of the flight controller at near-maximum rate. By logging the raw inputs and outputs of key flight systems, the Blackbox log aims to allow the offline bench-top simulation, debugging, and testing of flight control algorithms using data collected from real flights.
A typical logging regime might capture 30 different state variables (for an average of 28 bytes per frame) at a sample rate of 900Hz. That's about 25,000 bytes per second, which is 250,000 baud with a typical 8-N-1 serial encoding.
## References
Please refer to the source code to clarify anything this document leaves unclear:
* INAV's Blackbox logger: [blackbox.c](https://github.com/iNavFlight/inav/blob/master/src/main/blackbox/blackbox.c),
[blackbox_io.c](https://github.com/iNavFlight/inav/blob/master/src/main/blackbox/blackbox_io.c),
[blackbox_fielddefs.h](https://github.com/iNavFlight/inav/blob/master/src/main/blackbox/blackbox_fielddefs.h)
* [C implementation of the Blackbox log decoder](https://github.com/iNavFlight/blackbox-tools)
* [JavaScript implementation of the Blackbox log decoder](https://github.com/iNavFlight/blackbox-log-viewer)
## Logging cycle
Blackbox is designed for flight controllers that are based around the concept of a "main loop". During each main loop iteration, the flight controller will read some state from sensors, run some flight control algorithms, and produce some outputs. For each of these loop iterations, a Blackbox "logging iteration" will be executed. This will read data that was stored during the execution of the main loop and log this data to an attached logging device. The data will include
algorithm inputs such as sensor and RC data, intermediate results from flight control algorithms, and algorithm outputs such as motor commands.
## Log frame types
Each event which is recorded to the log is packaged as a "log frame". Blackbox only uses a handful of different types of log frames. Each frame type is identified by a single capital letter.
### Main frames: I, P
The most basic kind of logged frames are the "main frames". These record the primary state of the flight controller (RC input, gyroscopes, flight control algorithm intermediates, motor outputs), and are logged during every logging iteration.
Each main frame must contain at least two fields, "loopIteration" which records the index of the current main loop iteration (starting at zero for the first logged iteration), and "time" which records the timestamp of the beginning of the main loop in microseconds (this needn't start at zero, on INAV it represents the system uptime).
There are two kinds of main frames, "I" and "P". "I", or "intra" frames are like video keyframes. They can be decoded without reference to any previous frame, so they allow log decoding to be resynchronized in the event of log damage. "P" or "inter" frames use an encoding which references previously logged frames in order to reduce the required datarate. When one interframe fails to decode, all following interframes will be undecodable up until the next intraframe.
### GPS frames: G, H
Because the GPS is updated so infrequently, GPS data is logged in its own dedicated frames. These are recorded whenever the GPS data changes (not necessarily alongside every main frame). Like the main frames, the GPS frames have their own intra/inter encoding system.
The "H" or "home" frame records the lat/lon of a reference point. The "G" or "GPS" frame records the current state of the GPS system (current position, altitude etc.) based on the reference point. The reference point can be updated (infrequently) during the flight, and is logged whenever it changes.
To allow "G" frames to continue be decoded in the event that an "H" update is dropped from the log, the "H" frame is logged periodically even if it has not changed (say, every 10 seconds). This caps the duration of unreadble "G" frames that will result from a single missed "H" change.
### Slow frames: S
Some flight controller state is updated very infrequently (on the order of once or twice a minute). Logging the fact that this data had not been updated during every single logging iteration would be a waste of bandwidth, so these frames are only logged when the "slow" state actually changes.
All Slow frames are logged as intraframes. An interframe encoding scheme can't be used for Slow frames, because one damaged frame causes all subsequent interframes to be undecodable. Because Slow frames are written so infrequently, one missing Slow frame could invalidate minutes worth of Slow state.
On INAV, Slow frames are currently used to log data like the user-chosen flight mode and the current failsafe state.
### Event frames: E
Some flight controller data is updated so infrequently or exists so transiently that we do not log it as a flight controller "state". Instead, we log it as a state *transition* . This data is logged in "E" or "event" frames. Each event frame payload begins with a single byte "event type" field. The format of the rest of the payload is not encoded in the
flight log, so its interpretation is left up to an agreement of the writer and the decoder.
For example, one event that INAV logs is that the user has adjusted a system setting (such as a PID setting) using INAV's inflight adjustments feature. The event payload notes which setting was adjusted and the new value for the setting.
Because these setting updates are so rare, it would be wasteful to treat the settings as "state" and log the fact that the setting had not been changed during every logging iteration. It would be infeasible to periodically log the system settings using an intra/interframe scheme, because the intraframes would be so large. Instead we only log the transitions as events, accept the small probability that any one of those events will be damaged/absent in the log, and leave it up to log readers to decide the extent to which they are willing to assume that the state of the setting between successfully-decoded transition events was truly unchanged.
## Log field format
For every field in a given frame type, there is an associated name, predictor, and encoding.
When a field is written, the chosen predictor is computed for the field, then this predictor value is subtracted from the raw field value. Finally, the encoder is used to transform the value into bytes to be written to the logging device.
### Field predictors
The job of the predictor is to bring the value to be encoded as close to zero as possible. The predictor may be based on the values seen for the field in a previous frame, or some other value such as a fixed value or a value recorded in the log headers. For example, the battery voltage values in "I" intraframes in INAV use a reference voltage that is logged as part of the headers as a predictor. This assumes that battery voltages will be broadly similar to the initial pack voltage of the flight (e.g. 4S battery voltages are likely to lie within a small range for the whole flight). In "P" interframes, the battery voltage will instead use the previously-logged voltage as a predictor, because the correlation between successive voltage readings is high.
These predictors are presently available:
#### Predict zero (0)
This predictor is the null-predictor which doesn't modify the field value at all. It is a common choice for fields which are already close to zero, or where no better history is available (e.g. in intraframes which may not rely on the previous value of fields).
#### Predict last value (1)
This is the most common predictor in interframes. The last-logged value of the field will be used as the predictor, and subtracted from the raw field value. For fields which don't change very often, this will make their encoded value be normally zero. Most fields have some time-correlation, so this predictor should reduce the magnitude of all but the noisiest fields.
#### Predict straight line (2)
This predictor assumes that the slope between the current measurement and the previous one will be similar to the slope between the previous measurement and the one before that. This is common for fields which increase at a steady rate, such as the "time" field. The predictor is `history_age_2 - 2 * history_age_1`.
#### Predict average 2 (3)
This predictor is the average of the two previously logged values of the field (i.e. `(history_age_1 + history_age_2) / 2` ). It is used when there is significant random noise involved in the field, which means that the average of the recent history is a better predictor of the next value than the previous value on its own would be (for example, in gyroscope or motor measurements).
#### Predict minthrottle (4)
This predictor subtracts the value of "minthrottle" which is included in the log header. In INAV, motors always lie in the range of `[minthrottle ... maxthrottle]` when the craft is armed, so this predictor is used for the first motor value in intraframes.
#### Predict motor[0] (5)
This predictor is set to the value of `motor[0]` which was decoded earlier within the current frame. It is used in intraframes for every motor after the first one, because the motor commands typically lie in a tight grouping.
#### Predict increment (6)
This predictor assumes that the field will be incremented by 1 unit for every main loop iteration. This is used to predict the `loopIteration` field, which increases by 1 for every loop iteration.
#### Predict home-coord (7)
This predictor is set to the corresponding latitude or longitude field from the GPS home coordinate (which was logged in a preceding "H" frame). If no preceding "H" frame exists, the value is marked as invalid.
#### Predict 1500 (8)
This predictor is set to a fixed value of 1500. It is preferred for logging servo values in intraframes, since these typically lie close to the midpoint of 1500us.
#### Predict vbatref (9)
This predictor is set to the "vbatref" field written in the log header. It is used when logging intraframe battery voltages in INAV, since these are expected to be broadly similar to the first battery voltage seen during
arming.
#### Predict last main-frame time (10)
This predictor is set to the last logged `time` field from the main frame. This is used when predicting timestamps of non-main frames (e.g. that might be logging the timing of an event that happened during the main loop cycle, like a GPS
reading).
### Field encoders
The field encoder's job is to use fewer bits to represent values which are closer to zero than for values that are further from zero. Blackbox supports a range of different encoders, which should be chosen on a per-field basis in order to minimize the encoded data size. The choice of best encoder is based on the probability distribution of the values which are to be encoded. For example, if a field is almost always zero, then an encoding should be chosen for it which can encode that case into a very small number of bits, such as one. Conversely, if a field is normally 8-16 bits large, it would be wasteful to use an encoder which provided a special short encoded representation for zero values, because this will increase the encoded length of larger values.
These encoders are presently available:
#### Unsigned variable byte (1)
This is the most straightforward encoding. This encoding uses the lower 7 bits of an encoded byte to store the lower 7 bits of the field's value. The high bit of that encoded byte is set to one if more than 7 bits are required to store the value. If the value did exceed 7 bits, the lower 7 bits of the value (which were written to the log) are removed from the value (by right shift), and the encoding process begins again with the new value.
This can be represented by the following algorithm:
```c
while (value > 127) {
writeByte((uint8_t) (value | 0x80)); // Set the high bit to mean "more bytes follow"
value >>= 7;
}
writeByte(value);
```
Here are some example values encoded using variable-byte encoding:
| Input value | Output encoding |
| ----------- | --------------- |
| 1 | 0x01 |
| 42 | 0x2A |
| 127 | 0x7F |
| 128 | 0x80 0x01 |
| 129 | 0x81 0x01 |
| 23456 | 0xA0 0xB7 0x01 |
#### Signed variable byte (0)
This encoding applies a pre-processing step to fold negative values into positive ones, then the resulting unsigned number is encoded using unsigned variable byte encoding. The folding is accomplished by "ZigZag" encoding, which is represented by:
```c
unsigned32 = (signed32 << 1) ^ (signed32 >> 31)
```
ZigZag encoding is preferred against simply casting the signed integer to unsigned, because casting would cause small negative quantities to appear to be very large unsigned integers, causing the encoded length to be similarly large. ZigZag encoding ensures that values near zero are still near zero after encoding.
Here are some example integers encoded using ZigZag encoding:
| Input value | ZigZag encoding |
| ----------- | --------------- |
| 0 | 0 |
| -1 | 1 |
| 1 | 2 |
| -2 | 3 |
| 2147483647 | 4294967294 |
| -2147483648 | 4294967295 |
#### Neg 14-bit (3)
The value is negated, treated as an unsigned 14 bit integer, then encoded using unsigned variable byte encoding. This bizarre encoding is used in INAV for battery pack voltages. This is because battery voltages are measured using a 14-bit ADC, with a predictor which is set to the battery voltage during arming, which is expected to be higher than any voltage experienced during flight. After the predictor is subtracted, the battery voltage will almost certainly be below zero.
This results in small encoded values when the voltage is closely below the initial one, at the expense of very large encoded values if the voltage rises higher than the initial one.
#### Elias delta unsigned 32-bit (4)
Because this encoding produces a bitstream, this is the only encoding for which the encoded value might not be a whole number of bytes. If the bitstream isn't aligned on a byte boundary by the time the next non-Elias Delta field arrives, or the end of the frame is reached, the final byte is padded with zeros byte-align the stream. This encoding requires more CPU time than the other encodings because of the bit juggling involved in writing the bitstream.
When this encoder is chosen to encode all of the values in INAV interframes, it saves about 10% bandwidth compared to using a mixture of the other encodings, but uses too much CPU time to be practical.
[The basic encoding algorithm is defined on Wikipedia](https://en.wikipedia.org/wiki/Elias_delta_coding). Given these utility functions:
```c
/* Write `bitCount` bits from the least-significant end of the `bits` integer to the bitstream. The most-significant bit
* will be written first
*/
void writeBits(uint32_t bits, unsigned int bitCount);
/* Returns the number of bits needed to hold the top-most 1-bit of the integer 'i'. 'i' must not be zero. */
unsigned int numBitsToStoreInteger(uint32_t i);
```
This is our reference implementation of Elias Delta:
```c
// Value must be more than zero
void writeU32EliasDeltaInternal(uint32_t value)
{
unsigned int valueLen, lengthOfValueLen;
valueLen = numBitsToStoreInteger(value);
lengthOfValueLen = numBitsToStoreInteger(valueLen);
// Use unary to encode the number of bits we'll need to write the length of the value
writeBits(0, lengthOfValueLen - 1);
// Now write the length of the value
writeBits(valueLen, lengthOfValueLen);
// Having now encoded the position of the top bit of value, write its remaining bits
writeBits(value, valueLen - 1);
}
```
To this we add a wrapper which allows encoding both the value zero and MAXINT:
```c
void writeU32EliasDelta(uint32_t value)
{
/* We can't encode value==0, so we need to add 1 to the value before encoding
*
* That would make it impossible to encode MAXINT, so use 0xFFFFFFFF as an escape
* code with an additional bit to choose between MAXINT-1 or MAXINT.
*/
if (value >= 0xFFFFFFFE) {
// Write the escape code
writeU32EliasDeltaInternal(0xFFFFFFFF);
// Add a one bit after the escape code if we wanted "MAXINT", or a zero if we wanted "MAXINT - 1"
writeBits(value - 0xFFFFFFFE, 1);
} else {
writeU32EliasDeltaInternal(value + 1);
}
}
```
Here are some reference encoded bit patterns produced by writeU32EliasDelta:
| Input value | Encoded bit string |
| ----------- | ------------------ |
| 0 | 1 |
| 1 | 0100 |
| 2 | 0101 |
| 3 | 01100 |
| 4 | 01101 |
| 5 | 01110 |
| 6 | 01111 |
| 7 | 00100000 |
| 8 | 00100001 |
| 9 | 00100010 |
| 10 | 00100011 |
| 11 | 00100100 |
| 12 | 00100101 |
| 13 | 00100110 |
| 14 | 00100111 |
| 15 | 001010000 |
| 225 | 00010001100010 |
| 4294967292 | 000001000001111111111111111111111111111101 |
| 4294967293 | 000001000001111111111111111111111111111110 |
| 4294967294 | 0000010000011111111111111111111111111111110 |
| 4294967295 | 0000010000011111111111111111111111111111111 |
Note that the very common value of zero encodes to a single bit, medium-sized values like 225 encode to 14 bits (an overhead of 4 bits over writing a plain 8 bit value), and typical 32-bit values like 4294967293 encode into 42 bits, an overhead of 10 bits.
#### Elias delta signed 32-bit (5)
The value is first converted to unsigned using ZigZag encoding, then unsigned Elias-delta encoding is applied.
#### TAG8_8SVB (6)
First, an 8-bit (one byte) header is written. This header has its bits set to zero when the corresponding field (from a maximum of 8 fields) is set to zero, otherwise the bit is set to one. The least-signficant bit in the header corresponds to the first field to be written. This header is followed by the values of only the fields which are non-zero, written using signed variable byte encoding.
This encoding is preferred for groups of fields in interframes which are infrequently updated by the flight controller. This will mean that their predictions are usually perfect, and so the value to be encoded for each field will normally be zero. This is common for values like RC inputs and barometer readings, which are updated in only a fraction of main loop iterations.
For example, given these field values to encode:
```
0, 0, 4, 0, 8
```
This would be encoded:
```
0b00010100, 0x04, 0x08
```
#### TAG2_3S32 (7)
A 2-bit header is written, followed by 3 signed field values of up to 32 bits each. The header value is based on the maximum size in bits of the three values to be encoded as follows:
| Header value | Maximum field value size | Field range |
| ------------ | ------------------------ | -------------------------- |
| 0 | 2 bits | [-2...1] |
| 1 | 4 bits | [-8...7] |
| 2 | 6 bits | [-32...31] |
| 3 | Up to 32 bits | [-2147483648...2147483647] |
If any of the three values requires more than 6 bits to encode, a second, 6-bit header value is written in the lower bits of the initial header byte. This second header has 2 bits for each of the encoded values which represents how many bytes are required to encode that value. The least-significant bits of the header represent the first field which is
encoded. The values for each possibility are as follows:
| Header value | Field size | Field range |
| ------------ | ---------- | -------------------------- |
| 0 | 1 byte | [-127...128] |
| 1 | 2 bytes | [-32768...32767] |
| 2 | 3 bytes | [-8388608...8388607] |
| 3 | 4 bytes | [-2147483648...2147483647] |
This header is followed by the actual field values in order, written least-significant byte first, using the byte lengths specified in the header.
So bringing it all together, these encoded bit patterns are possible, where "0" and "1" mean bits fixed to be those values, "A", "B", and "C" represent the first, second and third fields, and "s" represents the bits of the secondary header in the case that any field is larger than 6 bits:
```
00AA BBCC,
0100 AAAA BBBB CCCC
10AA AAAA 00BB BBBB 00CC CCCC
11ss ssss (followed by fields of byte lengths specified in the "s" header)
```
This encoding is useful for fields like 3-axis gyroscopes, which are frequently small and typically have similar magnitudes.
#### TAG8_4S16 (8)
An 8-bit header is written, followed by 4 signed field values of up to 16 bits each. The 8-bit header value has 2 bits for each of the encoded fields (the least-significant bits represent the first field) which represent the number of bits required to encode that field as follows:
| Header value | Field value size | Field range |
| ------------ | ---------------- | ---------------- |
| 0 | 0 bits | [0...0] |
| 1 | 4 bits | [-8...7] |
| 2 | 8 bits | [-128...127] |
| 3 | 16 bits | [-32768...32767] |
This header is followed by the actual field values in order, written as if the output stream was a bit-stream, with the most-significant bit of the first field ending up in the most-significant bits of the first written byte. If the number of nibbles written is odd, the final byte has its least-significant nibble set to zero.
For example, given these field values:
```
13, 0, 4, 2
```
Choosing from the allowable field value sizes, they may be encoded using this many bits each:
```
8, 0, 4, 4
```
The corresponding header values for these lengths would be:
```
2, 0, 1, 1
```
So the header and fields would be encoded together as:
```
0b01010010, 0x0D, 0x42
```
#### NULL (9)
This encoding does not write any bytes to the file. It is used when the predictor will always perfectly predict the value of the field, so the remainder is always zero. In practice this is only used for the "loopIteration" field in interframes, which is always perfectly predictable based on the logged frame's position in the sequence of frames and the "P interval" setting from the header.
## Log file structure
A logging session begins with a log start marker, then a header section which describes the format of the log, then the log payload data, and finally an optional "log end" event ("E" frame).
A single log file can be comprised of one or more logging sessions. Each session may be preceded and followed by any amount of non-Blackbox data. This data is ignored by the Blackbox log decoding tools. This allows for the logging device to be alternately used by the Blackbox and some other system (such as MSP) without requiring the ability to begin a separate log file for each separate activity.
### Log start marker
The log start marker is `H Product:Blackbox flight data recorder by Nicholas Sherlock\n`. This marker is used to discover the beginning of the flight log if the log begins partway through a file. Because it is such a long string, it is not expected to occur by accident in any sequence of random bytes from other log device users.
### Log header
The header is comprised of a sequence of lines of plain ASCII text. Each header line has the format `H fieldname:value` and ends with a `'\n'`. The overall header does not have a terminator to separate it from the log payload (the header implicitly ends when a line does not begin with an 'H' character).
The header can contain some of these fields:
#### Data version (required)
When the interpretation of the Blackbox header changes due to Blackbox specification updates, the log version is incremented to allow backwards-compatibility in the decoder:
```
H Data version:2
```
#### Logging interval
Not every main loop iteration needs to result in a Blackbox logging iteration. When a loop iteration is not logged, Blackbox is not called, no state is read from the flight controller, and nothing is written to the log. Two header lines are included to note which main loop iterations will be logged:
##### I interval
This header notes which of the main loop iterations will record an "I" intraframe to the log. If main loop iterations with indexes divisible by 32 will be logged as "I" frames, the header will be:
```
H I interval: 32
```
The first main loop iteration seen by Blackbox will be numbered with index 0, so the first main loop iteration will always be logged as an intraframe.
##### P interval
Not every "P" interframe needs to be logged. Blackbox will log a portion of iterations in order to bring the total portion of logged main frames to a user-chosen fraction. This fraction is called the logging rate. The smallest possible logging rate is `(1/I interval)` which corresponds to logging only "I" frames at the "I" interval and discarding all other loop iterations. The maximum logging rate is `1/1`, where every main loop iteration that is not an "I" frame is logged as a "P" frame. The header records the logging rate fraction in `numerator/denominator` format like so:
```
H P interval:1/2
```
The logging fraction given by `num/denom` should be simplified (i.e. rather than 2/6, a logging rate of 1/3 should be used).
Given a logging rate of `num/denom` and an I-frame interval of `I_INTERVAL`, the frame type to log for an iteration of index `iteration` is given by:
```c
if (iteration % I_INTERVAL == 0)
return 'I';
if ((iteration % I_INTERVAL + num - 1) % denom < num)
return 'P';
return '.'; // i.e. don't log this iteration
```
For an I-interval of 32, these are the resulting logging patterns at some different P logging rates.
| Logging rate | Main frame pattern | Actual portion logged |
| ------------ | ----------------------------------------------------------------- | --------------------- |
| 1 / 32 | I...............................I...............................I | 0.03 |
| 1 / 6 | I.....P.....P.....P.....P.....P.I.....P.....P.....P.....P.....P.I | 0.19 |
| 1 / 3 | I..P..P..P..P..P..P..P..P..P..P.I..P..P..P..P..P..P..P..P..P..P.I | 0.34 |
| 1 / 2 | I.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.I.P.P.P.P.P.P.P.P.P.P.P.P.P.P.P.I | 0.50 |
| 2 / 3 | I.PP.PP.PP.PP.PP.PP.PP.PP.PP.PP.I.PP.PP.PP.PP.PP.PP.PP.PP.PP.PP.I | 0.66 |
| 5 / 6 | I.PPPPP.PPPPP.PPPPP.PPPPP.PPPPP.I.PPPPP.PPPPP.PPPPP.PPPPP.PPPPP.I | 0.81 |
| 1 / 1 | IPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPIPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPI | 1.00 |
#### Firmware type (optional)
Because Blackbox records the internal flight controller state, the interpretation of the logged data will depend on knowing which flight controller recorded it. To accommodate this, the name of the flight controller should be recorded:
```
H Firmware type:INAV
```
More details should be included to help narrow down the precise flight-controller version (but these are not required):
```
H Firmware revision:c49bd40
H Firmware date:Aug 28 2015 16:49:11
```
#### Field X name (required)
This header is a comma-separated list of the names for the fields in the 'X' frame type:
```
H Field I name:loopIteration,time,axisP[0],axisP[1]...
```
The decoder assumes that the fields in the 'P' frame type will all have the same names as those in the 'I' frame, so a "Field P name" header does not need to be supplied.
#### Field X signed (optional)
This is a comma-separated list of integers which are set to '1' when their corresponding field's value should be interpreted as signed after decoding, or '0' otherwise:
```
H Field I signed:0,0,1,1...
```
#### Field X predictor (required)
This is a comma-separated list of integers which specify the predictors for each field in the specified frame type:
```
H Field I predictor:0,0,0,0...
```
#### Field X encoding (required)
This is a comma-separated list of integers which specify the encoding used for each field in the specified frame type:
```
H Field X encoding:1,1,0,0...
```
#### vbatref
This header provides the reference voltage that will be used by predictor #9.
#### minthrottle
This header provides the minimum value sent by INAV to the ESCs when armed, it is used by predictor #4.
#### Additional headers
The decoder ignores headers that it does not understand, so you can freely add any headers that you require in order to properly interpret the meaning of the logged values.
For example, to create a graphical displays of RC sticks and motor percentages, the Blackbox rendering tool requires the additional headers "rcRate" and "maxthrottle". In order to convert raw gyroscope, accelerometer and voltage readings into real-world units, the Blackbox decoder requires the calibration constants "gyro.scale", "acc_1G" and "vbatscale". These headers might look like:
```
H rcRate:100
H maxthrottle:1980
H gyro.scale:0x3d79c190
H acc_1G:4096
H vbatscale:110
```
### Log payload
The log payload is a concatenated sequence of logged frames. Each frame type which is present in the log payload must have been previously described in the log header (with Frame X name, etc. headers). Each frame begins with a single capital letter to specify the type of frame (I, P, etc), which is immediately followed by the frame's field data. There is no frame length field, checksum, or trailer.
The field data is encoded by taking an array of raw field data, computing the predictor for each field, subtracting this predictor from the field, then applying the field encoders to each field in sequence to serialize them to the log.
For example, imagine that we are encoding three fields in an intraframe, are using zero-predictors for each field (#0), and are encoding the values using the unsigned variable byte encoding (#1). For these field values:
```
1, 2, 3
```
We would encode a frame:
```
'I', 0x01, 0x02, 0x03
```
Imagine that we are encoding an array of motor commands in an interframe. We will use the previous motor commands as a predictor, and encode the resulting values using signed variable byte encoding. The motor command values seen in the previous logged iteration were:
```
1430, 1500, 1470, 1490
```
And the motor commands to be logged in this iteration are:
```
1635, 1501, 1469, 1532
```
After subtracting the predictors for each field, we will be left with:
```
205, 1, -1, 42
```
We will apply ZigZag encoding to each field, which will give us:
```
410, 2, 1, 84
```
We will use unsigned variable byte encoding to write the resulting values to the log, which will give us:
```
'P', 0x9A, 0x03, 0x02, 0x01, 0x54
```
### Log end marker
The log end marker is an optional Event ("E") frame of type 0xFF whose payload is the string `End of log\0` or more recently, `End of log (disarm reason:X)\0` (X is a number, see below). The payload ensures that random data does not look like an end-of-log marker by chance. This event signals the tidy ending of the log. All following bytes until the next log-begin marker (or end of file) should be ignored by the log decoder.
```
'E', 0xFF, "End of log (disarm reason:4)\0", 0x00
```
#### Disarm Reasons
<ol start="0">
<li> None</li>
<li> Timeout</li>
<li> Sticks</li>
<li> Switch_3D</li>
<li> Switch</li>
<li> Failsafe</li>
<li> Navigation</li>
</ol>
## Log validation
Any damage experienced to the log during recording is overwhelmingly due to sub-sequences of bytes being dropped by the logging device due to overflowing buffers. Accordingly, Blackbox logs do not bother to include any checksums (bytes are not expected to be damaged by the logging device without changing the length of the message). Because of the tight bandwidth requirements of logging, neither a frame length field nor frame trailer is recorded that would allow for the
detection of missing bytes.
Instead, the decoder uses a heuristic in order to detect damaged frames. The decoder reads an entire frame from the log (using the decoder for each field which is the counterpart of the encoder specified in the header), then it checks to see if the byte immediately following the frame, which should be the beginning of a next frame, is a recognized frame-type byte (e.g. 'I', 'P', 'E', etc). If that following byte represents a valid frame type, it is assumed that the decoded frame was the correct length (so was unlikely to have had random ranges of bytes removed from it, which would have likely altered the frame length). Otherwise, the frame is rejected, and a valid frame-type byte is looked for immediately after the frame-start byte of the frame that was rejected. A rejected frame causes all subsequent interframes to be rejected as well, up until the next intraframe.
A frame is also rejected if the "loopIteration" or "time" fields have made unreasonable leaps forward, or moved at all backwards. This suffices to detect almost all log corruption.
+20
View File
@@ -0,0 +1,20 @@
# Building Manual.
The manual PDF file is generated by concatenating relevant markdown files and by transforming the result using Gimli to obtain the final PDF file. This steps are handled automatically by the ```build_docs.sh``` script located in the root of the repository next to the Makefile.
## Requirements & Installation
The PDF manual generation uses the Gimli for the conversion. It can be installed via ruby gems. On Debian based systems the installation steps are:
```bash
sudo apt-get install ruby1.9.1 ruby1.9.1-dev rubygems zlib1g-dev wkhtmltopdf libxml2-dev libxslt-dev
sudo gem1.9.1 install gimli
```
## Configuration
All markdown files need to be registered in the ```build_manual.sh``` file individually by modifying the ```doc_files``` variable / array:
```bash
doc_files=( 'Configuration.md'
'Board - SPRACINGF3.md'
'...'
'...'
)
```
+47
View File
@@ -0,0 +1,47 @@
## Building SITL
### Compiler requirements
* Modern GCC. Must be a *real* GCC, unless you're using MacOS; faking it with clang will not work. gcc13 or later is recommended.
* Unix sockets networking. Cygwin is required on Windows (vice `winsock`).
* Pthreads
### Linux and FreeBSD:
Almost like normal, ruby, cmake and make are also required.
With cmake, the option "-DSITL=ON" must be specified.
```
mkdir build_SITL
cd build_SITL
cmake -DSITL=ON ..
make
```
### Windows:
Compile under cygwin, using the Linux instructions. Note that depending on the Cygwin packaging _du jour_ of `ruby` it _may_ also be necessary to:
* Install the `rubygems` package.
* run `gem install getoptlong`
Copy cygwin1.dll into the directory, or include cygwin's /bin/ directory in the environment variable PATH.
If the build fails (segfault, possibly out of memory), adding `-DCMAKE_BUILD_TYPE=MinRelSize` to the `cmake` command may help.
### Build manager
`ninja` may also be used (parallel builds without `-j $(nproc)`):
```
cmake -GNinja -DSITL=ON ..
ninja
```
### Supported environments
* Linux on x86_64, ia-32, Aarch64 (e.g. Rpi), RISCV64 (e.g. VisionFive2)
* Windows on x86_64
* FreeBSD x86_64 (at least).
* MacOS on x86_64 and Aarch64
+57
View File
@@ -0,0 +1,57 @@
# Building with Docker
> **On Windows building with this method is not advised and should be used only if Windows Linux Subsystem can not be used. In all other cases all Windows users should be using Linux Subsystem (WSL) instead**
Building with [Docker](https://www.docker.com/) is remarkably easy: an isolated container will hold all the needed compilation tools so that they won't interfere with your system and you won't need to install and manage them by yourself. You'll only need to have Docker itself [installed](https://docs.docker.com/install/).
The first time that you'll run a build it will take a little more time than following executions since it will be building its base image first. Once this initial process is completed, the firmware will be always built immediately.
If you want to start from scratch - _even if that's rarely needed_ - delete the `inav-build` image on your system (`docker image rm inav-build`).
## Linux
In the repo's root, run:
```
./build.sh <TARGET>
```
Where `<TARGET>` must be replaced with the name of the target that you want to build. For example:
```
./build.sh MATEKF405SE
```
Run the script with no arguments to get more details on its usage:
```
./build.sh
```
## Windows 10
Docker on Windows requires full paths for mounting volumes in `docker run` commands. For example: `c:\Users\pspyc\Documents\Projects\inav` becomes `//c/Users/pspyc/Documents/Projects/inav` .
If you are getting error "standard_init_linux.go:219: exec user process caused: no such file or directory", make sure `\cmake\docker.sh` has lf (not crlf) line endings.
You'll have to manually execute the same steps that the build script does:
1. `docker build --build-arg USER_ID=1000 --build-arg GROUP_ID=1000 -t inav-build .`
+ This step is only needed the first time.
+ If GDB should be installed in the image, add argument '--build-arg GDB=yes'
2. `docker run --rm -it -v <PATH_TO_REPO>:/src inav-build <TARGET>`
+ Where `<PATH_TO_REPO>` must be replaced with the absolute path of where you cloned this repo (see above), and `<TARGET>` with the name of the target that you want to build.
3. If you need to update `Settings.md`, run:
`docker run --entrypoint /src/cmake/docker_docs.sh --rm -it -v <PATH_TO_REPO>:/src inav-build`
4. Building SITL:
`docker run --rm --entrypoint /src/cmake/docker_build_sitl.sh -it -v <PATH_TO_REPO>:/src inav-build`
5. Running SITL:
`docker run -p 5760:5760 -p 5761:5761 -p 5762:5762 -p 5763:5763 -p 5764:5764 -p 5765:5765 -p 5766:5766 -p 5767:5767 --entrypoint /src/cmake/docker_run_sitl.sh --rm -it -v <PATH_TO_REPO>:/src inav-build`.
+ SITL command line parameters can be adjusted in `cmake/docker_run_sitl.sh`.
Refer to the [Linux](#Linux) instructions or the [build script](/build.sh) for more details.
+12
View File
@@ -0,0 +1,12 @@
# Building in FreeBSD
In order to build the INAV firmware in FreeBSD, it is recommended to install [Linux Binary Emulation](https://www.freebsd.org/doc/handbook/linuxemu.html). This will enable you to use the project recommended ARM cross-compiler. The cross-compiler available in Ports tends to be too old to build INAV firmware.
* Install Linux binary emulation
* Install the following packages (`pkg` provides suitable versions)
- `git`
- `cmake`
- `make`
- `ruby`
* Follow the [Building in Linux](Building%20in%20Linux.md) guide.
@@ -0,0 +1,49 @@
# Building in GitHub Codespaces
> **A codespace is a cloud-hosted development environment.**
> <br/>This guide provides a simple method to modify and build a version of INAV in the cloud.
## Setup
1. Navigate to the version of INAV you want to modify.
2. Create a new codespace for that version. `Code> Codespaces> Create codespace on 8.x.x.`
<img width="1567" height="769" alt="image" src="https://github.com/user-attachments/assets/4427ea1e-19ff-4c2c-8d99-3870c13f767b" />
> A new codespace will launch in your browser.
## Modify
3. Modify the code as required.
You can freeley modify your copy of the INAV code from within the cloud environment.
## Build
Use the terminal inside the codespace environment to run the following commands:
### Prepare
#### 4. Prepare the build:
```bash
cmake -B build -S .
```
<img width="1566" height="989" alt="image" src="https://github.com/user-attachments/assets/5747e49b-256b-4afb-9a16-3c592ba6773c" />
> Note: You may need to use ctrl+C to exit the process and return to the shell once it completes.
### Target
#### 5. (Optional) If you don't know your target name, list the availble targests with:
```bash
cmake --build build --target help
```
<img width="1520" height="377" alt="image" src="https://github.com/user-attachments/assets/c3fc5099-ed92-4006-94ca-d7b777d31f2f" />
### Build
#### 6. Build the binary (replace the target name with your specific one):
``` bash
cmake --build build --target NEUTRONRCF435MINI
```
<img width="1566" height="373" alt="image" src="https://github.com/user-attachments/assets/bb56ea92-0b2a-423c-b6dd-edec39f6e358" />
## Download
7. Once the build process has complete, download the .hex binary from the build folder on the right.
Example: `inav_8.0.1_NEUTRONRCF435MINI.hex` (Right-click > Download.
<img width="1566" height="978" alt="image" src="https://github.com/user-attachments/assets/fd3bdeb4-459f-433b-ab70-74a49b26712f" />
> Note: Codespaces are automatically deleted after a period of inactivity, or you can manually deleted them at https://github.com/codespaces
+19
View File
@@ -0,0 +1,19 @@
# Building in Gitpod
Gitpod offers an online build environment for building INAV targets.
## Setting up the environment and building targets
1. Go to https://gitpod.io/new
1. Paste `https://github.com/iNavFlight/inav/tree/[version]` into the field called "Select a repository".
1. Ensure that you substitute [version] (e.g. 7.1.0) with the version number of INAV that you want to build.
1. Cick on the link that shows in the drop down and Gitpod will atomatically selects the adequate Editor and Browser.
1. Leave the other fields as default and click "Continue". Your build environment will be created.
1. At the bottom of the page, you will see a command line. Type `make [TARGET]` and wait for the target to be built.
1. Once the build has finished, navigate to the build folder using `cd build`.
1. Once in the folder, run `objcopy -O ihex -R .eeprom [TARGET].elf [TARGET].hex` to convert the `.elf` file to a `.hex` file.
1. Your new target `.hex` binary will be located in a folder called `bin`, which can be found at the top left of the page.
NOTE: You can use this method to build your forks as well. Just paste in the link to your fork and follow the rest of the steps.
You are done!
+210
View File
@@ -0,0 +1,210 @@
# Generic Linux development tools
## Overview
This article endeavours to provide a generic guide for compiling INAV on Linux for INAV 2.6 and later.
INAV requires a reasonably modern `gcc-arm-none-eabi` cross-compiler. Different Linux distros will provide different versions of the cross-compiler. This will range from obsolete versions (e.g. Debian, Ubuntu LTS) to the latest stable release (Arch Linux).
In order to provide a uniform and reasonably modern cross compiler, INAV provides for the installation of a "known good / working" cross compiler, as well as a mechanism to override this if your distro provides a more modern option (e.g Arch Linux). In general, from a security perspective, Linux distros discourage the installation of software from sources other than the official distribution repositories and 'approved' sources (Ubuntu PPA, Arch AUR). The INAV approach of providing a recommended compiler is however both sound and justified:
* The cross-compiler is installed from a reputable source (ARM, the company that makes the CPUs used in our flight controllers)
* Disto cross-compilers are often older than the recommended INAV compiler
* The installed cross-compiler is only used to build INAV and it not obviously / generally available outside of the INAV build environment.
There are a however some specific cases for using the distro cross-compiler in preference to that installed by INAV:
* You are using a distro that installs a more modern compiler (Arch)
* You are using a host platform for which ARM does not provide a compiler (e.g. Linux ia32).
## Prerequisites
In addition to a cross-compiler, it is necessary to install some other tools:
* `git` : clone and manage the INAV code repository
* `cmake` : generate the build environment
* `make` : run the firmware compilation
* `ruby` : build some generated source files from JSON definitions
* `gcc` : native compiler used to generate settings and run tests
Note that INAV requires `cmake` version 3.13 or later; any distro that provides `cmake` 3.13 will also provide adequate versions of the other tools.
Note also that Ubuntu 18.04 LTS does NOT provide a modern enough `cmake`; it is recommended that you upgrade to Ubuntu 20.04 LTS which does.
Note that you may prefer to use `ninja` rather than `make` as the build manager. This is described [below](#building-with-ninja).
### Ubuntu / Debian
```
# make sure the system is updated first
sudo apt update && sudo apt upgrade
sudo apt install git make ruby cmake gcc
```
### Fedora
```
# make sure the system is updated first
sudo dnf -y update
sudo dnf install git make ruby cmake gcc
```
### Arch
```
# make sure the system is updated first
sudo pacman -Syu
sudo pacman -S git make ruby cmake gcc
```
Once these prerequisites are installed, we can clone the repository to provide a local instance of the INAV source code.
## Cloning the repository
```
git clone https://github.com/iNavFlight/inav.git
```
Note: If you have a Github account with registered ssh key you can replace the `git clone` command with `git clone git@github.com:iNavFlight/inav.git` instead of the https link.
The `git clone` creates an `inav` directory; we can enter this directory, configure the build environment and build firmware.
## Build tooling
For 2.6 and later, INAV uses `cmake` as its primary build tool. `cmake` simplifies various platform and hardware dependencies required to cross compile multiple targets. `cmake` still uses GNU `make` to invoke the actual compiler. It is necessary to configure the build environment with `cmake` before we can build any firmware.
## Using `cmake`
The canonical method of using `cmake` is to create a `build` directory and run the `cmake` and `make` commands from within the `build` directory. So, assuming we've cloned the firmware repository into an `inav` directory, we can issue the following commands to set up the build environment.
```
cd inav
# first time only, create the build directory
mkdir build
cd build
cmake ..
# note the "..", this is required as it tells cmake where to find its ruleset
```
`cmake` will check for the presence of an INAV-embedded cross-compiler; if this cross-compiler is not found it will attempt to download the vendor (ARM) GCC cross-compiler.
Note. If you want to use your own cross-compiler, either because you're running a distro (e.g. Arch Linux) that ships a more recent cross-compiler, or you're on a platform for which ARM doesn't provide a cross-compiler (e.g. 32bit Linux), the you should run the `cmake` command as:
```
cmake -DCOMPILER_VERSION_CHECK=OFF ..
```
`cmake` will generate a number of files in your `build` directory, including a cache of generated build settings `CMakeCache.txt` and a `Makefile`.
## Building the firmware
Once `cmake` has generated the `build/Makefile`, this `Makfile` (with `make`) is used to build the firmware, again from the `build` directory. It is not necessary to re-run `cmake` unless the INAV cmake configuration is changed (i.e. a new release) or you wish to swap between the ARM SDK compiler and a distro or other external compiler.
The generated `Makefile` uses different a target selection mechanism from the older (pre 2.6) top level `Makefile`; you can generate a list of targets with `make help` (or, as the list is extremely long), pipe this into a pager, e.g. `make help | less`.
Typically, to build a single target, just pass the target name to `make`; note that unlike earlier releases, `make` without a target specified will build **all** targets.
```
# Build the MATEKF405 firmware (includes debug symbols by default)
make MATEKF405
```
One can also build multiple targets from a single `make` command:
```
# parallel build using all but 1 CPU core
make -j $(($(nproc)-1)) MATEKF405 MATEKF722
```
### Building All Targets or Release Builds
**⚠️ Important for CI or building many targets:** By default, INAV builds with `-DCMAKE_BUILD_TYPE=RelWithDebInfo`, which includes maximum debug symbols. When building all ~100 targets, this uses **~109 GB of disk space**. The debug symbols are automatically stripped from the final `.hex` files, so they provide no benefit for production builds.
**For production builds, CI, or when building many targets, use Release mode:**
```bash
cd inav
mkdir build-release
cd build-release
cmake -DCMAKE_BUILD_TYPE=Release ..
# Build specific targets
make MATEKF405
# Or build all official release targets
make release
```
**Disk usage comparison:**
- `RelWithDebInfo` (default): ~109 GB for all targets
- `Release`: ~4-6 GB for all targets (96% reduction)
The final `.hex` and `.bin` files are identical in both cases.
The resultant hex file are in the `build` directory.
You can then use the INAV Configurator to flash the local `build/inav_x.y.z_TARGET.hex` file, or use `stm32flash` or `dfu-util` directly from the command line.
[msp-tool](https://github.com/fiam/msp-tool) and [flash.sh](https://github.com/stronnag/mwptools/blob/master/docs/MiscTools.asciidoc#flashsh) provide / describe 3rd party helper tools for command line flashing.
### Cleaning
You can clean out the built files, either for all targets or selectively; a selective clean target is simply defined by prefixing the target name with `clean_`:
```
# clean out every thing
make clean
# clean out single target
make clean_MATEKF405
# or multiple targets
make clean_MATEKF405 clean_MATEKF722
```
### `cmake` cache maintenance
`cmake` caches the build environment, so you don't need to rerun `cmake` each time you build a target. Two `make` options are provided to maintain the `cmake` cache.
* `make edit_cache`
* `make rebuild_cache`
It is unlikely that the typical user will need to employ these options, other than perhaps to change between the embedded ARM and distro compilers.
## Building with ninja
`cmake` is not a build system, rather it generates build files for a build manager. The examples above use `make` as the build manager; this has been the legacy way of building INAV. It is also possible to use other build systems; one popular cross-platform tool is [ninja](https://ninja-build.org/) which is both lightweight and executes parallel builds by default.
* Install `ninja` from the distro tool (apt, dnf, pacman as appropriate)
* Configure `cmake` to use `ninja` as the build system
```
cd build
# add other cmake options as required.
cmake -GNinja ..
```
* Then use `ninja` in place of `make` to perform the build
```
ninja MATEKF405 MATEKF722
```
## Updating and rebuilding
In order to update your local firmware build:
* Navigate to the local INAV repository
* Use the following steps to pull the latest changes and rebuild your local version of INAV firmware from the `build` directory:
```
$ cd inav
$ git pull
$ cd build
$ ninja <TARGET>
$ ## or make <TARGET>
```
## Advanced Usage
For more advanced development information and `git` usage, please refer to the [development guide](https://github.com/iNavFlight/inav/blob/master/docs/development/Development.md).
## Unsupported platforms
If you're using a host platform for which Arm does not supply a cross-compiler (Arm32, IA32), and the distro either does not package a suitable compiler or it's too old, then you can usually find a suitable compiler in the [xpack devtools collection](https://github.com/xpack-dev-tools/arm-none-eabi-gcc-xpack).
You will need to configure `cmake` to use the external compiler.
+130
View File
@@ -0,0 +1,130 @@
# Building in Mac OS X
Building in Mac OS X can be accomplished in just a few steps:
* Install general development tools (clang, make, git)
* Install cmake
* Checkout INAV sourcecode through git
* Build the code
## Install general development tools (clang, make, git)
Open up a terminal and run `make`. If it is installed already, you should see a message like this, which means that you
already have the required development tools installed:
```
make: *** No targets specified and no makefile found. Stop.
```
If it isn't installed yet, you might get a popup like this. If so, click the "install" button to install the commandline
developer tools:
![Prompt to install developer tools](assets/mac-prompt-tools-install.png)
If you just get an error like this instead of a helpful popup prompt:
```
-bash: make: command not found
```
Try running `xcode-select --install` instead to trigger the popup.
If that doesn't work, you'll need to install the XCode development environment [from the App Store][]. After
installation, open up XCode and enter its preferences menu. Go to the "downloads" tab and install the
"command line tools" package.
[from the App Store]: https://itunes.apple.com/us/app/xcode/id497799835
## Install cmake
The easiest way to install cmake's command line executable is via
[Homebrew](https://brew.sh) (a package manager for macOS). Go to their site
and follow their installation instructions.
Once Homebrew is installed, type `brew install cmake` in a terminal to install
cmake.
Alternatively, cmake binaries for macOS are available from
[cmake.org](https://cmake.org/download/). If you prefer installing it this way,
you'd have to manually add cmake's command line binary to your `$PATH`. Assuming
`CMake.app` has been copied to `/Applications`, adding the following line to
`~/.zshrc` would make the cmake command available.
```sh
export PATH=$PATH:/Applications/CMake.app/Contents/bin
```
## Ruby
Ruby is installed by default on macOS.
## Checkout INAV sourcecode through git
Enter your development directory and clone the [INAV repository][] using the "HTTPS clone URL" which is shown on
the right side of the INAV GitHub page, like so:
```
git clone https://github.com/iNavFlight/inav
```
This will download the entire INAV repository for you into a new folder called "inav".
[INAV repository]: https://github.com/iNavFlight/inav.git
## Build the code
Assuming you've just cloned the source code, you can switch your current
directory to INAV's source try by typing:
```sh
cd inav
```
Inside the INAV directory, create a new directory to store the built files. This
helps keeping everything nice and tidy, separating source code from artifacts. By
convention this directory is usually called `build`, but any name would work. Enter
the following command to create it and switch your working directory to it:
```sh
mkdir -p build && cd build
```
Now we need to configure the build by using the following command:
```sh
cmake ..
```
This will automatically download the required compiler for INAV, so it
might take a few minutes. Once it's finished without errors, you can
build the target that you want by typing `make target-name`. e.g.:
```sh
make -j8 MATEKF722 # Will build MATEKF722 target
```
A list of all the available targets can be displayed with:
```sh
make targets
```
Once the build completes, the correspondent `.hex` file will be found
in current directory (e.g. `build`) and it will be named as
`inav_x.y.z_TARGET.hex`. `x.y.z` corresponds to the INAV version number
while `TARGET` will be the target name you've just built. e.g.
`inav_2.6.0_MATEKF722.hex`. This is the file that can be flashed using
INAV Configurator.
## Updating to the latest source
If you want to erase your local changes and update to the latest version of the INAV source, enter your
INAV directory and run these commands to first erase your local changes, fetch and merge the latest
changes from the repository, then rebuild the firmware:
```sh
git reset --hard
git pull
make target-name # e.g. make MATEKF722
```
+91
View File
@@ -0,0 +1,91 @@
# Building with Vagrant
> **On Windows building with this method is not advised and should be used only if Windows Linux Subsystem can not be used. In all other cases all Windows users should be using Linux Subsystem (WSL) instead**
Setting up build environment with Vagrant is remarkably simple, but you still need to have some basic knowlage of your OS.
## Installing Vagrant
Vagrant needs some kind of virtualization software to run, i.e. VirtualBox.
You can get VirtualBox from here:
```
https://www.virtualbox.org/wiki/Downloads
```
Download and install Vagrant for you OS from here:
```
https://www.vagrantup.com/downloads.html
```
## Cloning INAV repository
Using git (The preferred way!)
```
git clone https://github.com/iNavFlight/inav.git
```
Or download the .zip file from
```
https://github.com/iNavFlight/inav
```
and extract it to folder of your choosing.
## Running the virtual machine
Open up a terminal or command line interface (In windows search for CMD.exe and run it as administrator!)
Navigate in to the directory of your cloned/unzipped INAV repository. (Where the "Vagrantfile" is located.) and start the virtual machine.
```
vagrant up
```
Starting the virtual machine might take some time depending on your computer speed.
When you start the virtual machine for the first time, it has to download the base virtual machine files and do some installation steps,
so it takes longer than the following times you start it.
When the start up has finished succesfully and you are back to your command prompt. Login in to the virtual machine.
```
vagrant ssh
```
## Building firmware
In the virtual machine, go to the INAV directory
```
cd inav
```
If you downloadet the repository as a zip file, you may have to type:
```
git init
```
To stop the file system boundary warnings.
Build your desired target
i.e.
```
make TARGET=AIRBOTF4
```
## Updating and rebuilding the firmware
```
git reset --hard
git pull
make clean TARGET=AIRBOTF4
make TARGET=AIRBOTF4
```
## Additional virtual machine commands
Exit from the virtual machine interface with:
```
exit
```
Shutdown the virtual machine with:
```
vagrant halt
```
Remove the virtual machine files from your computer with:
```
vagrant destroy
```
@@ -0,0 +1,199 @@
# Building in Windows 10/11 with Linux subsystem (WSL) [Recommended]
Linux subsystem for Windows (WSL) 10/11 is probably the simplest way of building INAV under Windows.
## Setting up the environment
Enable WSL:
run `windows features`
enable `windows subsytem for linux`
reboot
Install Ubuntu:
1. Go to Microsoft store https://www.microsoft.com/en-gb/store/b/home
1. Search and install most recent Ubuntu LTS version
1. When download completed, select `Launch Ubuntu`
1. When prompted enter a user name and password which you will need to remember
1. When complete, the linux command prompt will be displayed
NOTE: from this point all commands are entered into the Ubunto shell command window
Update the repo packages:
- `sudo apt update`
Install Git, Make, gcc and Ruby
- `sudo apt-get install git make cmake ruby`
Install python and python-yaml to allow updates to settings.md
- `sudo apt-get install python3`
### CMAKE and Ubuntu 18_04
To run `cmake` in the latest version you will need to update from Ubuntu `18_04` to `20_04`. The fastest way to do it is to uninstall current version and install `20_04` for Microsoft Store [https://www.microsoft.com/store/productId/9N6SVWS3RX71](https://www.microsoft.com/store/productId/9N6SVWS3RX71)
## Downloading the INAV repository (example):
Mount MS windows C drive and clone INAV
1. `cd /mnt/c`
2. `git clone https://github.com/iNavFlight/inav.git`
3. `git checkout 6.1.1` (to switch to a specific release tag, for this example INAV version 6.1.1)
4. `git checkout -b my-branch` (to create own branch)
You are ready!
You now have a folder called inav in the root of C drive that you can edit in windows
### If you get a cloning error
On some installations, you may see the following error:
```
Cloning into 'inav'...
error: chmod on /mnt/c/inav/.git/config.lock failed: Operation not permitted
fatal: could not set 'core.filemode' to 'false'
```
You can fix this with by remounting the drive using the following commands
1. `sudo umount /mnt/c`
2. `sudo mount -t drvfs C: /mnt/c -o metadata`
## Building with Make (example):
For detailed build instructions see [Building in Linux](Building%20in%20Linux.md)
Launch Ubuntu:
Click Windows Start button then scroll and launch "Ubuntu"
Building from Ubuntu command line
`cd /mnt/c/inav`
Do it onece to prepare build environment
```
mkdir build
cd build
cmake ..
```
Then to build
```
cd build
make MATEKF722
```
## Building with Ninja (example):
[Ninja](https://ninja-build.org/) is a popular cross-platform tool. It is both lightweight and executes parallel builds by default. It is advantageous to use this over the old _make_ method. There are detailed instructions for building with Ninja in [Building in Linux](Building%20in%20Linux.md#building-with-ninja).
Launch Ubuntu:
Click Windows Start button. Then scroll and launch **Ubuntu**.
> [!TIP]
> Before using Ninja, you will need to install it. From the Ubuntu command prompt type `sudo apt-get install ninja-build -y` and press enter.
Building from the command line:
First, change to the INAV directory with
```cd /mnt/c/inav```
Before building, you will need to prepare the build environment. You only need to do this once, unless you reinstall WSL or cmake.
```
mkdir build
cd build
cmake -GNinja ..
```
From then on, you can build your target by calling the following from inside the build directory.
```
ninja MATEKF722
```
If you want to build multiple targets. You can use:
```
ninja MATEKF722 MATEKF405SE SPEEDYBEEF405
```
## Updating the documents
```
cd /mnt/c/inav
python3 src/utils/update_cli_docs.py
```
## Flashing:
Launch windows configurator GUI and from within the firmware flasher select `Load firmware[Local]`
Hex files can be found in the folder `c:\inav\build`
## Troubleshooting
### Syntax error: "(" unexpected
```
dzikuvx@BerlinAtHome:/mnt/c/Users/pspyc/Documents/Projects/inav/build$ make MATEKF722SE
Generating MATEKF722SE/settings_generated.h, MATEKF722SE/settings_generated.c
/bin/sh: 1: Syntax error: "(" unexpected
make[3]: *** [src/main/target/MATEKF722SE/CMakeFiles/MATEKF722SE.elf.dir/build.make:63: src/main/target/MATEKF722SE/MATEKF722SE/settings_generated.h] Error 2
make[2]: *** [CMakeFiles/Makefile2:33607: src/main/target/MATEKF722SE/CMakeFiles/MATEKF722SE.elf.dir/all] Error 2
make[1]: *** [CMakeFiles/Makefile2:33290: src/main/target/MATEKF722SE/CMakeFiles/MATEKF722SE.dir/rule] Error 2
make: *** [Makefile:13703: MATEKF722SE] Error 2
```
This error can be triggered by a Windows PATHs included in the Linux Subsystem. The solution is:
#### For WSL V1 - Flags set as 7 by default
1. Open Windows RegEdit tool
1. Find `HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Lxss\{GUID}\Flags`
1. Change `Flags` from `7` to `5`
1. Restart WSL and Windows preferably
1. `cd build`
1. `cmake ..`
1. `make {TARGET}` should be working again
#### For WSL V2 - Flags set as 0x0000000f (15) by default
1. Open Windows RegEdit tool
1. Find `HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Lxss\{GUID}\Flags`
1. Change `Flags` from `f` to `d`, it is stored as Base Hexadecimal
1. Restart WSL and Windows preferably
1. `cd build`
1. `cmake ..`
1. `make {TARGET}` should be working again
#### Or, for either version
1. In the Linux Subsystem, `cd /etc/`
2. Create a new file with `sudo nano wsl.conf`
3. Enter the following in to the new file:
```
[Interop]
appendWindowsPath=false
```
4. Save the file by holding `Ctrl` and pressing `o`
5. Press `Enter` to confirm the wsl.conf filename.
6. Hit `Ctrl`+`x` to exit nano
7. Restart WSL and Windows preferably
8. `cd build`
9. `cmake ..`
9. `make {TARGET}` should be working again
### Building targets is very slow
I was pretty shocked when my new i7 -10750 laptop took 25 minutes to build a single target. My old i3-4030 could do the same job in about 2.5 minutes. If you're also suffering from slow builds. Open an elevated PowerShell window and type
```
wsl -l -v
```
If you see your Linux distribution is using WSL 2, this is the problem. WSL 2 is quicker than WSL 1 for a lot of things. However, if your files are on a windows mounted drive in Linux, it is extremely slow. There are two options:
1. Put your files on the Linux file system
2. Change to WSL 1
#### Using the Linux file system (recommended)
To use the Linux file system, make sure the distro is running. Open File Explorer and navigate to `\\wsl$`. In that path you will find your distros listed. At this point, map a network drive to your distro. Inside the distro, you can find your home directory at `/home/~username~/`. Create your GitHub folders here.
If after this you have problems with writing to the directories from within VSCode. Open the application for your distro and type
```
sudo chown -R ~username~ GitHub
```
`~Username~` is your root distro user that you created and `GitHub` should be the root folder for your GitHub repositories.
#### To switch back to WSL 1
To do this, in the elevated PowerShell window, you can see the name of your distro. Mine is **Ubuntu-20.04**, so I'll use that in this example. Simply type
```
wsl --set-version Ubuntu-20.04 1
```
and your distro will be converted to WSL 1. Once finished, reboot your system. Next time you compile a build, it will be faster.
@@ -0,0 +1,109 @@
# Building in Windows with MSYS2
> **Building with this method is not advised and should be used only if Windows Linux Subsystem can not be used. In all other cases all Windows users should be using Linux Subsystem (WSL) instead**
- This environment does not require installing WSL, which may not be available or would get in the way of other virtualization and/or anti-cheat software
- It is also much faster to install and get set up because of its small size(~3.65 GB total after building hex file as of 6.0.0)
## Setting up the environment
### Download and install MSYS2
1. For 6.0.0, the last version that works is [20220603](https://repo.msys2.org/distrib/x86_64/msys2-x86_64-20220603.exe)
- [20220503](https://repo.msys2.org/distrib/x86_64/msys2-x86_64-20220503.exe) is also known to work
- MSYS2 releases can be viewed at https://repo.msys2.org/distrib/x86_64/
- Scroll all the way down for an executable, scroll halfway down for a self-extracting archive
1. Open an MSYS2 terminal by running C:\msys64\msys2_shell.cmd
1. In the newly opened shell, set up your work path
- To paste commands, use "Shift+Insert" or Right-click and select "Paste"
```
mkdir /c/Workspace
```
## Downloading and installing dependencies
### Installing other dependencies:
```
pacman -S git ruby make cmake gcc mingw-w64-x86_64-libwinpthread-git unzip wget
```
- Note: If some fails to download, use the following command to install the rest without reinstalling everything:
```
pacman -S git ruby make cmake gcc mingw-w64-x86_64-libwinpthread-git unzip wget --needed
```
### Download the INAV repository
#### Go to the working directory
```
cd /c/Workspace
```
#### Download INAV source code
- For master:
```
git clone https://github.com/iNavFlight/inav
```
- For [a branch](https://github.com/iNavFlight/inav/branches) or [a tag](https://github.com/iNavFlight/inav/tags):
```
# "release_6.0.0" here can be the name of a branch or a tag
git clone --branch release_6.0.0 https://github.com/iNavFlight/inav
```
- If you are internet speed or space restrained, you can also use `--depth 1`, which won't download the whole history, and `--single-branch`, which won't download other branches:
```
git clone --depth 1 --single-branch --branch release_6.0.0 https://github.com/iNavFlight/inav
```
This results in ~302 MB instead of ~468 MB download/install size(as of 6.0.0)
### Installing xPack
1. Create xPack directory:
```
mkdir /c/Workspace/xpack
cd /c/Workspace/xpack
```
2. Find out which version of xPack you need for your INAV version:
```
# Currently, this is 10.2.1 for 6.0.0 and 10.3.1 for master
cat /c/Workspace/inav/cmake/arm-none-eabi-checks.cmake | grep "set(arm_none_eabi_gcc_version" | cut -d\" -f2
```
3. Find the version you need from the [releases page](https://github.com/xpack-dev-tools/arm-none-eabi-gcc-xpack/releases/), then either:
- Download the "...-win32-x64.zip" and copy the folder inside, or
- Right-click, choose "Copy link address" and paste it into the following commands:
```
cd /c/Workspace/xpack
# paste the link after "wget"
wget https://github.com/xpack-dev-tools/arm-none-eabi-gcc-xpack/releases/download/v10.2.1-1.1/xpack-arm-none-eabi-gcc-10.2.1-1.1-win32-x64.zip
# paste the file name after "unzip"
unzip xpack-arm-none-eabi-gcc-10.2.1-1.1-win32-x64.zip
# you can delete the zip file after as it is no longer needed
rm xpack-arm-none-eabi-gcc-10.2.1-1.1-win32-x64.zip
```
3. This is important. Put the toolkit first before your path so that it is picked up ahead of any other versions that may be present on your system:
```
export PATH=/c/Workspace/xpack/xpack-arm-none-eabi-gcc-10.2.1-1.1/bin:$PATH
```
## Building the INAV firmware
1. Create the build directory:
```
mkdir /c/Workspace/inav/build
```
2. Go into the build directory:
```
cd /c/Workspace/inav/build
```
3. Run cmake
- This may take a while. If you only want to test one target, remove the rest of the folders from C:\Workspace\inav\src\main\target\
```
cmake ..
```
4. Compile the firmware for your flight controller.
```
make MATEKH743
```
- The list of available targets in INAV can be found here: https://github.com/inavflight/inav/tree/master/src/main/target
- The generated hex file will be in the /c/Workspace/inav/build folder
## Troubleshooting
### *** multiple target patterns. Stop. | Error 2
#### Delete everything in the build directory that contains previous runs
You can either use file explorer and delete everything inside C:\Workspace\inav\build
or run:
```
cd /c/Workspace/inav/build && rm -rf *
```
### -- could not find arm-none-eabi-gcc
#### Redo export PATH, make sure xpack version number is correct:
```
export PATH=/c/Workspace/xpack/xpack-arm-none-eabi-gcc-10.2.1-1.1/bin:$PATH
```
### make: the '-j' option requires a positive integer argument
#### You are using too new version of MSYS2, uninstall and reinstall version [20220603](https://repo.msys2.org/distrib/x86_64/msys2-x86_64-20220603.exe) or [20220503](https://repo.msys2.org/distrib/x86_64/msys2-x86_64-20220503.exe)
+73
View File
@@ -0,0 +1,73 @@
# Cmake Usage
## Introduction
This guide documents INAV usage of the `cmake` build tool.
## Target Defintion
A target requires `CMakeLists.txt` file. This file contains one of more lines of the form:
```
target_hardware_definition(name optional_parameters)
```
For example:
```
target_stm32f405xg(QUARKVISION HSE_MHZ 16)
```
* The hardware is `stm32f405xg`, a F405 with a 1MiB flash
* The name is `QUARKVISION` (a board that never reached production)
* The optional parameter is `HSE_MHZ 16` defining the non-default high-speed external (HSE) oscillator clock.
## Hardware names
As of INAV 4.1, the following target hardware platforms are recognised:
* stm32f405xg
* stm32f411xe
* stm32f427xg
* stm32f722xe
* stm32f745xg
* stm32f765xg
* stm32f765xi
* stm32h743xi
The device characteristics for these names may be found at [stm32-base.org](https://stm32-base.org/cheatsheets/linker-memory-regions/).
## Optional Parameters
The following optional parameters are recognised:
| Paramater | Usage |
| --------- | ----- |
| `SKIP_RELEASES` | The target is disabled for releases and CI. It still may be possible to build the target directly. |
| `COMPILE_DEFINITIONS "VAR[=value]"` | Sets a preprocessor define. |
| `HSE_MZ value` | The target uses a high-speed external crystal (HSE) oscillator clock with a frequency different from the 8MHz default. The `value` is the desired clock, for example `HSE_MHZ 24` |
Multiple optional parameters may be specified, for example `HSE_MHZ 16 SKIP_RELEASES`.
## Target variations
A number of targets support multiple variants, either successive versions of the same hardware, or varations that enable different resources (soft serial, leds etc.) This is defined by adding additional `target_` lines to `CMakeLists.txt`. For example, the OMNIBUSF4 and its multiple clones / variations, `src/main/target/OMNIBUSF4/CMakeLists.txt`:
```
target_stm32f405xg(DYSF4PRO)
target_stm32f405xg(DYSF4PROV2)
target_stm32f405xg(OMNIBUSF4)
# the OMNIBUSF4SD has an SDCARD instead of flash, a BMP280 baro and therefore a slightly different ppm/pwm and SPI mapping
target_stm32f405xg(OMNIBUSF4PRO)
target_stm32f405xg(OMNIBUSF4PRO_LEDSTRIPM5)
target_stm32f405xg(OMNIBUSF4V3_S5_S6_2SS)
target_stm32f405xg(OMNIBUSF4V3_S5S6_SS)
target_stm32f405xg(OMNIBUSF4V3_S6_SS)
# OMNIBUSF4V3 is a (almost identical) variant of OMNIBUSF4PRO target,
# except for an inverter on UART6.
target_stm32f405xg(OMNIBUSF4V3)
```
## Adding (or removing) a source file
In the cmake system, project source files are listed in `src/main/CMakeLists.txt`. New source files must be added to this list to be considered by the build system.
+75
View File
@@ -0,0 +1,75 @@
# You can help bring INAV forward!
INAV is a big project and fully community-driven. This is why we need you, to make INAV better with every release!
There are always many new features and fixes ready to go into INAV, contributed by a lot of developers. To ensure that
all quality standards are met, all these great additions need to be checked, tested and potentially fixed before we
can add them to the code. This is why we always need your help, to keep the standards high and our aircraft safe!
If you are a bit tech-savvy and curious to try new things in the hobby you are invited to join us and help with the development.
Also, if you are good at writing in English, you are welcome to help with the documentation.
## How are changes made in INAV? (Testing and reviewing pull requests)
Most changes done to INAV are done by whats called a “pull request”, or “PR”. Each month, there are about 30 pull
requests - 30 new features or fixes added to INAV.
Each is waiting for someone
to test them, or update the related documentation, or just look over the code. If you are curious about what's coming,
then have a look at the [INAV Pull Requests](https://github.com/iNavFlight/inav/pulls)
And the ones for [Configurator](https://github.com/iNavFlight/inav-configurator/pulls)
Some of the ones ready to be tested are shown here:
[Testing Required](https://github.com/iNavFlight/inav/pulls?q=is%3Aopen+is%3Apr+label%3A"Testing+Required")
Some are just documentation updates and wed like someone to review to see if you think it reads clearly and is accurate, such as these:
[Documentation
PRs](https://github.com/iNavFlight/inav/pulls?q=is%3Aopen+is%3Apr+label%3A"Review+needed"+label%3ADocumentation)
## Documentation Updates
Another important part of software development is the documentation. This is something that INAV has struggled with in the past, and we want to get better at it. Check the new features, read the description from the developer, and make sure proper documentation for any new feature is provided. Not enough information in the PR? Feel free to make the developers aware of it. You see potential in better guides or descriptions? You are welcome to add or change things, to make them more accessible for newcomers. It is often better for someone BESIDES the developer to write or at least review the documentation for a feature because we want it to be clear and understandable to someone who doesn't already fully understand it.
The better our documentation is, the higher the chance for people to test and try awesome new features. Also everyone has to spend less time asking and answering questions - asking for help or explaining things to people who struggle to understand. This helps reduce frustration all around.
You can edit the wiki pages directly on Github. Look for the "Edit" button at the top right of any INAV wiki page.
## Github “Issues”
Nothing is perfect. Sometimes bugs might still happen or sometimes a misunderstanding causes problems. This is what pops up in the Github "Issues". Every ticket that is opened there has to be checked by someone familiar with the topic. We have to validate if it could be a user error or an actual problem. Maybe it's a wish for a change or a feature that has to be discussed.
Any help with these tickets and their validation is appreciated. Keeping this part of the project organized and clean is essential, in order to not miss important things.
If you know what the user can do to fix the problem, you can provide the necessary help and we can close the issue. People tend to expect authoritative, accurate answers on Github, so it is often helpful to point the user to the right place in the docs, or check the documentation yourself if you're unsure, then help them.
If you suspect a potential bug, ask for any additional information that might be relevant and ideally, try to validate and replicate the issue. This makes the life of the developers much easier and allows us to provide a fix faster.
## It's all about PEOPLE (socials etc)
INAV is used on quadcopters, on planes, rovers, and boats, but really - INAV is for PEOPLE.
It's all about helping people enjoy the hobby. If you're a people person, your activity in the
Facebook groups, the Telegram group, and on the Discord are really appreciated. Some of the
developers writing the math-heavy code aren't social butterflies, so they aren't active on Facebook.
If you see someone posting about a problem and you can direct them how to post in Issue on the Github,
that's really helpful. Relaying information between Telegram, Facebook, Discord, and Github is appreciated.
If you have a web site or a Youtube channel, or are interested in building one, the resources are
really helpful to the people using INAV.
## Coders
We also need people who are familiar with either HTML and Javascript to help with new features and fixes in
Configurator, or C programmers for INAV features and fixes. Especially reviewing the changes that have already been
submitted. If you are an experienced programmer who is willing to spend some time and contribute to the project, even
better! Less experienced programmers can also contribute, with small fixes, but especially by *reviewing* code that is
submitted. If you can read code you may spot typos, or things that just don't seem to make sense. This is also a great
way to learn.
Keep in mind that we are not a corporation. No one will tell you what to do. You have an idea for a great new feature? Do it! You see something that is broken and you think you can fix? Feel free to fix it! You are unsure if your idea will be accepted? The core-developer team is always there to ask and discuss things and prevent unnecessary work.
## How to get started
You are welcome to join the [INAV Discord](https://discord.gg/peg2hhbYwN) if you would like to chat with other
contributors in real time. You can also just go directly to the pull requests, Issues, and Discussions on the INAV
[Github](https://github.com/iNavFlight/inav/) and start participating in the discussions.
Also, see the [Configurator Github](https://github.com/iNavFlight/inav-configurator/)
Or, feel free to update the [wiki](https://github.com/iNavFlight/inav/wiki)
To make your own pull requests to update the code or the documentation in the docs/ folder, the [Development.md](https://github.com/iNavFlight/inav/blob/master/docs/development/Development.md#using-git-and-github) page will walk you
through how to do that.
Let's all come together and make the best INAV possible!
@@ -0,0 +1,74 @@
If your flight controller does not have an official INAV target, it is possible to use src/utils/bf2inav.py script to generate a good starting point for an unofficial INAV target.
This script can read the config.h from [Betaflight's target configuration repository](https://github.com/betaflight/config) and it currently supports STM32F405, STM32F722, STM32F745, STM32H743 and AT32F435 targets.
Locate your Flight Controller target config.h in BetaFlight's repo, eg.: [config.h](https://github.com/betaflight/config/blob/master/configs/BETAFPVF405/config.h), for BETAFPVF435 target.
It is also advisable to record the output of the timer command from BetaFlight, as it will provide useful information on `timer` usage that can be used to adjust the generated target later.
```
# timer
timer B08 AF3
# pin B08: TIM10 CH1 (AF3)
timer C08 AF3
# pin C08: TIM8 CH3 (AF3)
timer B00 AF2
# pin B00: TIM3 CH3 (AF2)
timer B01 AF2
# pin B01: TIM3 CH4 (AF2)
timer A03 AF1
# pin A03: TIM2 CH4 (AF1)
timer A02 AF1
# pin A02: TIM2 CH3 (AF1)
timer B06 AF2
# pin B06: TIM4 CH1 (AF2)
timer A08 AF1
# pin A08: TIM1 CH1 (AF1)
timer A09 AF1
# pin A09: TIM1 CH2 (AF1)
timer A10 AF1
# pin A10: TIM1 CH3 (AF1)
```
In the above example, `pin B08: TIM10 CH1 (AF3)` means that pind to CH1. This information can be used to fix the generated timer assigned to match BetaFlight's allocation by editing the `target.c` file generated by the `bf2inav.py` script.
Using the BETAFPVF405 target mentioned above, to create the target now we need to:
1. Download INAV source code and be able to build
2. Download the config.h from BetaFlight repository
3. Create a target folder that will be used as the output folder for the `bf2inav.py` script, eg: `inav/src/main/targets/BETAFPVF405`
4. Navigate to the script folder in `inav/src/utils/`
5. `python3 ./bf2inav.py -i config.h -o ../main/target/BETAFPVF405/`
6. Edit generated `target.c` and chose the correct timer definitions to match Betaflight's timer definitions.
```
timerHardware_t timerHardware[] = {
DEF_TIM(TIM3, CH3, PB0, TIM_USE_OUTPUT_AUTO, 0, 0),
//DEF_TIM(TIM8, CH2N, PB0, TIM_USE_OUTPUT_AUTO, 0, 0),
//DEF_TIM(TIM1, CH2N, PB0, TIM_USE_OUTPUT_AUTO, 0, 0),
DEF_TIM(TIM3, CH4, PB1, TIM_USE_OUTPUT_AUTO, 0, 0),
//DEF_TIM(TIM8, CH3N, PB1, TIM_USE_OUTPUT_AUTO, 0, 0),
//DEF_TIM(TIM1, CH3N, PB1, TIM_USE_OUTPUT_AUTO, 0, 0),
//DEF_TIM(TIM5, CH4, PA3, TIM_USE_OUTPUT_AUTO, 0, 0),
//DEF_TIM(TIM9, CH2, PA3, TIM_USE_OUTPUT_AUTO, 0, 0),
DEF_TIM(TIM2, CH4, PA3, TIM_USE_OUTPUT_AUTO, 0, 0),
//DEF_TIM(TIM5, CH3, PA2, TIM_USE_OUTPUT_AUTO, 0, 0),
//DEF_TIM(TIM9, CH1, PA2, TIM_USE_OUTPUT_AUTO, 0, 0),
DEF_TIM(TIM2, CH3, PA2, TIM_USE_OUTPUT_AUTO, 0, 0),
DEF_TIM(TIM8, CH3, PC8, TIM_USE_OUTPUT_AUTO, 0, 0),
//DEF_TIM(TIM3, CH3, PC8, TIM_USE_OUTPUT_AUTO, 0, 0),
DEF_TIM(TIM1, CH1, PA8, TIM_USE_OUTPUT_AUTO, 0, 0),
//DEF_TIM(TIM3, CH1, PB4, TIM_USE_BEEPER, 0, 0),
DEF_TIM(TIM4, CH1, PB6, TIM_USE_LED, 0, 0),
};
```
In this particular example, PA3, PA2 were changed to match Betaflight's mapping, and the timer PB4 was disabled, due to a timer conflict. Normal channels are prefered over N channels (CH1, over CH1N) or C channels in AT32 architectures.
7. Now update yout build scripts by running `cmake` and build the target you just created. The target name can be checked in the generated `CMakeLists.txt`, but should match the Betaflight target name.
For information on how to build INAV, check the documents in the [docs/development](https://github.com/iNavFlight/inav/tree/master/docs/development) folder.
+237
View File
@@ -0,0 +1,237 @@
# Development
This document is primarily for developers only.
## General principals
1. Name everything well.
2. Strike a balance between simplicity and not-repeating code.
3. Methods that return a boolean should be named as a question, and should not change state. e.g. 'isOkToArm()'
4. Methods that start with the word 'find' can return a null, methods that start with 'get' should not.
5. Methods should have verb or verb-phrase names, like `deletePage` or `save`. Variables should not, they generally should be nouns. Tell the system to 'do' something 'with' something. e.g. deleteAllPages(pageList).
6. Keep methods short - it makes it easier to test.
7. Don't be afraid of moving code to a new file - it helps to reduce test dependencies.
8. Avoid noise-words in variable names, like 'data' or 'info'. Think about what you're naming and name it well. Don't be afraid to rename anything.
9. Avoid comments that describe what the code is doing, the code should describe itself. Comments are useful however for big-picture purposes and to document content of variables.
10. If you need to document a variable do it at the declaration, don't copy the comment to the `extern` usage since it will lead to comment rot.
11. Seek advice from other developers - know you can always learn more.
12. Be professional - attempts at humor or slating existing code in the codebase itself is not helpful when you have to change/fix it.
13. Know that there's always more than one way to do something and that code is never final - but it does have to work.
Before making any code contributions, take a note of the https://github.com/multiwii/baseflight/wiki/CodingStyle
It is also advised to read about clean code, here are some useful links:
* http://cleancoders.com/
* http://en.wikipedia.org/wiki/SOLID_%28object-oriented_design%29
* http://en.wikipedia.org/wiki/Code_smell
* http://en.wikipedia.org/wiki/Code_refactoring
* http://www.amazon.co.uk/Working-Effectively-Legacy-Robert-Martin/dp/0131177052
## Unit testing
Ideally, there should be tests for any new code. However, since this is a legacy codebase which was not designed to be tested this might be a bit difficult.
If you want to make changes and want to make sure it's tested then focus on the minimal set of changes required to add a test.
Tests currently live in the `test` folder and they use the google test framework.
The tests are compiled and run natively on your development machine and not on the target platform.
This allows you to develop tests and code and actually execute it to make sure it works without needing a development board or simulator.
This project could really do with some functional tests which test the behaviour of the application.
All pull requests to add/improve the testability of the code or testing methods are highly sought!
Note: Tests are written in C++ and linked with with firmware's C code.
### Running the tests.
The tests and test build system is very simple and based off the googletest example files, it may be improved in due course. From the root folder of the project simply do:
Test are configured from the top level directory. It is recommended to use a separate test directory, here named `testing`.
```
mkdir testing
cd testing
# define NULL toolchain ...
cmake -DTOOLCHAIN= ..
# Run the tests
make check
```
This will build a set of executable files in the `src/test/unit` folder (below `testing`), one for each `*_unittest.cc` file.
After they have been executed by the make invocation, you can still run them on the command line to execute the tests and to see the test report, for example:
```
src/test/unit/time_unittest
Running main() from /home/jrh/Projects/fc/inav/testing/src/test/googletest-src/googletest/src/gtest_main.cc
[==========] Running 2 tests from 1 test suite.
[----------] Global test environment set-up.
[----------] 2 tests from TimeUnittest
[ RUN ] TimeUnittest.TestMillis
[ OK ] TimeUnittest.TestMillis (0 ms)
[ RUN ] TimeUnittest.TestMicros
[ OK ] TimeUnittest.TestMicros (0 ms)
[----------] 2 tests from TimeUnittest (0 ms total)
[----------] Global test environment tear-down
[==========] 2 tests from 1 test suite ran. (0 ms total)
[ PASSED ] 2 tests.
```
You can also step-debug the tests in `gdb` (or IDE debugger).
The tests are currently always compiled with debugging information enabled, there may be additional warnings, if you see any warnings please attempt to fix them and submit pull requests with the fixes.
Tests are verified and working with (native) GCC 11.20.
## Using git and github
Ensure you understand the github workflow: https://guides.github.com/introduction/flow/index.html
Please keep pull requests focused on one thing only, since this makes it easier to merge and test in a timely manner.
If you need help with pull requests there are guides on github here:
https://help.github.com/articles/creating-a-pull-request/
The main flow for a contributing is as follows:
1. Login to github, go to the INAV repository and press `fork`.
2. Then using the command line/terminal on your computer: `git clone <url to YOUR fork>`
3. `cd inav`
4. `git checkout maintenance-10.x`
5. `git checkout -b my-new-code`
6. Make changes
7. `git add <files that have changed>`
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 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 version 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`
2. `git checkout maintenance-10.x`
3. `git fetch upstream`
4. `git merge upstream/maintenance-10.x`
5. `git push origin` is an optional step that will update your fork on github
You can also perform the git commands using the git client inside Eclipse. Refer to the Eclipse git manual.
## Branching and release workflow
INAV uses maintenance branches for active development and releases. The `master` branch tracks the current version by receiving merges from the current version maintenance branch.
### 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)
### 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`, which is then merged into the next version branch
5. The cycle continues with the maintenance branch receiving new changes for the next release
**Merge flow:** `maintenance-9.x``master``maintenance-10.x`
### Propagating Changes Between Branches
Changes committed to the current version branch flow through master to the next major version branch.
**Maintainer workflow:**
- Changes in `maintenance-9.x` are merged into `master`
- Changes in `master` are then 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
**Merge flow:**
```bash
# Step 1: Merge current version to master
git checkout master
git merge maintenance-9.x
git push upstream master
# Step 2: Merge master to next version
git checkout maintenance-10.x
git merge master
git push upstream maintenance-10.x
```
**Why use master as intermediate step:** This keeps master synchronized with the current version, so if a contributor accidentally branches from master, they get current version code without breaking changes from maintenance-10.x.
### Example Timeline
**Current state (example - during 9.x series):**
- `maintenance-9.x` - Active development for INAV 9.1, 9.2, etc.
- `master` - Mirror of maintenance-9.x (receives merges via merge flow)
- `maintenance-10.x` - Breaking changes for future INAV 10.0
**After INAV 10.0 is released:**
- `maintenance-10.x` - Becomes active development for INAV 10.1, 10.2, etc.
- `master` - Now mirrors maintenance-10.x (via merge flow)
- `maintenance-11.x` - Breaking changes for future INAV 11.0
### 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
```
@@ -0,0 +1,121 @@
# Hardware Debugging In Eclipse
Build a binary with debugging information using command line or via Eclipse make target.
Example Eclipse make target
![](https://raw.github.com/wiki/hydra/cleanflight/images/eclipse-gdb-debugging/make 1 - OLIMEXINO GDB.PNG)
# GDB and OpenOCD
start openocd
Create a new debug configuration in eclipse :
![connect to openocd](http://i.imgur.com/somJLnq.png)
![use workspace default](http://i.imgur.com/LTtioaF.png)
you can control openocd with a telnet connection:
telnet localhost 4444
stop the board, flash the firmware, restart:
reset halt
wait_halt
sleep 100
poll
flash probe 0
flash write_image erase /home/user/git/inav/obj/inav_SPRACINGF3.hex 0x08000000
sleep 200
soft_reset_halt
wait_halt
poll
reset halt
A this point you can launch the debug in Eclispe.
![](http://i.imgur.com/u7wDgxv.png)
# GDB and J Link
Here are some screenshots showing Hydra's configuration of Eclipse (Kepler)
If you use cygwin to build the binaries then be sure to have configured your common `Source Lookup Path`, `Path Mappings` first, like this:
![](https://raw.github.com/wiki/cleanflight/cleanflight/images/eclipse-gdb-debugging/config 7.PNG)
Create a new `GDB Hardware Debugging` launch configuration from the `Run` menu
It's important to have build the executable compiled with GDB debugging information first.
Select the appropriate .elf file (not hex file).
DISABLE auto-build
![](https://raw.github.com/wiki/cleanflight/cleanflight/images/eclipse-gdb-debugging/config 1.PNG)
Choose the appropriate gdb executable - ideally from the same toolchain that you use to build the executable.
![](https://raw.github.com/wiki/cleanflight/cleanflight/images/eclipse-gdb-debugging/config 2.PNG)
Configure Startup as follows
Initialization commands
```
target remote localhost:2331
monitor interface SWD
monitor speed 2000
monitor flash device = STM32F103RB
monitor flash download = 1
monitor flash breakpoints = 1
monitor endian little
monitor reset
```
![](https://raw.github.com/wiki/cleanflight/cleanflight/images/eclipse-gdb-debugging/config 3.PNG)
![](https://raw.github.com/wiki/cleanflight/cleanflight/images/eclipse-gdb-debugging/config 4.PNG)
It may be useful to specify run commands too:
```
monitor reg r13 = (0x00000000)
monitor reg pc = (0x00000004)
continue
```
![](https://raw.github.com/wiki/cleanflight/cleanflight/images/eclipse-gdb-debugging/config 13.PNG)
If you use cygwin an additional entry should be shown on the Source tab (not present in this screenshot)
![](https://raw.github.com/wiki/cleanflight/cleanflight/images/eclipse-gdb-debugging/config 5.PNG)
Nothing to change from the defaults on the Common tab
![](https://raw.github.com/wiki/cleanflight/cleanflight/images/eclipse-gdb-debugging/config 6.PNG)
Start up the J-Link server in USB mode
![](https://raw.github.com/wiki/cleanflight/cleanflight/images/eclipse-gdb-debugging/config 9.PNG)
If it connects to your target device it should look like this
![](https://raw.github.com/wiki/cleanflight/cleanflight/images/eclipse-gdb-debugging/config 10.PNG)
From Eclipse launch the application using the Run/Debug Configurations..., Eclipse should upload the compiled file to the target device which looks like this
![](https://raw.github.com/wiki/cleanflight/cleanflight/images/eclipse-gdb-debugging/config 11.PNG)
When it's running the J-Link server should look like this.
![](https://raw.github.com/wiki/cleanflight/cleanflight/images/eclipse-gdb-debugging/config 12.PNG)
Then finally you can use Eclipse debug features to inspect variables, memory, stacktrace, set breakpoints, step over code, etc.
![](https://raw.github.com/wiki/cleanflight/cleanflight/images/eclipse-gdb-debugging/debugging.PNG)
If Eclipse can't find your breakpoints and they are ignored then check your path mappings (if using cygwin) or use the other debugging launcher as follows. Note the 'Select other...' at the bottom of the configuration window.
![](https://raw.github.com/wiki/cleanflight/cleanflight/images/eclipse-gdb-debugging/config 8 - If breakpoints do not work.PNG)
@@ -0,0 +1,271 @@
# Hardware Debugging in Visual Studio Code and WSL
Real debugging makes life easier, here is a configuration for VSCode with WSL for Windows 10/11.
## Hardware:
For the basics, please read [Hardware Debugging.md](Hardware%20Debugging.md) up to the section "Compilation options", everything after that is outdated.
In contrast to the instructions above, I had to connect the Target VCC pin of my original ST-Link/V2 to the 3.3V of the FC, otherwise I got an error message that the voltage of the target was too low. The FC is not supplied with voltage via this pin, only the voltage of the target is measured to ensure signal compatibility. (See ST-Link/V2 manual, table 4).
Check with "STM32 ST-LINK Utility" if the connection to the FC is working.
## Software:
Read [IDE - Visual Studio Code with Windows 10.md](IDE%20-%20Visual%20Studio%20Code%20with%20Windows%2010.md) and set up VSCode like this.
I recommend to use WSL2 and to work in the Linux file system (e.g. `~/git/inav`), access to the files in the Linux FS you get
in Windows 10 via `\\wsl$` in the address line of the explorer, in Windows 11 via the new entry "Linux" in the explorer.
"Cortex Debug" is also required as an additional extension for VSCode.
## OpenOCD
Now we come to the sticking point:
Unfortunately, WSL cannot pass the ST link (or only via tools such as usbipd-win, see the instructions here: [Windows 11 - VS Code - WSL2 - Hardware Debugging.md](Windows%2011%20-%20VS%20Code%20-%20WSL2%20-%20Hardware%20Debugging.md)), so OpenOCD must run on the Windows side, which is no problem since gdb and OpenOCD communicate with each other via TCP.
Tip:
Only for OpenOCD you don't need to install xpm and node.js, just download the archive at https://github.com/xpack-dev-tools/openocd-xpack/releases and unpack it into a folder of your choice.
Now OpenOCD needs board definition files for the FC, the files generated by the makefile do not work for me.
Copy the following files to `scripts\board` in the OpenOCD folder:
f4fc.cfg:
```
# Boardconfig for ST-Link/V2 with F4-FC
source [find interface/stlink.cfg]
transport select hla_swd
source [find target/stm32f4x.cfg]
reset_config none separate
```
f7fc.cfg
```
# Boardconfig for ST-Link/V2 with F7-FC
source [find interface/stlink.cfg]
transport select hla_swd
source [find target/stm32f7x.cfg]
reset_config none separate
```
h7fc.cfg
```
# Boardconfig for ST-Link/V2 with H7-FC
source [find interface/stlink.cfg]
transport select hla_swd
source [find target/stm32h7x_dual_bank.cfg]
reset_config none separate
```
In order for GDB and OpenOCD to be able to communicate with each other, the host (Windows) IP address is required, the easiest and most convenient way is to have this done automatically by the linux bash to do this automatically, add the following line to the `~/.bashrc` file:
`export WSL_HOST_IP=$(cat /etc/resolv.conf | sed -rn 's|nameserver (.*)|\1|p')`
Actually, the trick is quite simple:
In launch.json set `servertype` to `external` and `gdbTarget` to `windows-host-ip:3333`, the .cfg file must of course be passed when starting openOCD in windows and the connection must be bound to 0.0.0.0.
## VSCode configuration files
For convenience, I have created a set of VSCode configuration files, that do all the magic.
And before anyone asks: Yes, WSL can launch applications on the host.
launch.json
```
{
"configurations": [
{
"name": "Debug",
"type": "cortex-debug",
"request": "launch",
"servertype": "external",
"cwd": "${workspaceRoot}/debug",
//"runToEntryPoint": "main",
"executable": "${workspaceRoot}/debug/bin/${config:inav.debug.target}.elf",
"gdbTarget": "${config:openocd.host}:3333",
"svdFile": "${workspaceRoot}/dev/svd/${config:inav.debug.svdFile}",
"preLaunchTask": "Debug",
"showDevDebugOutput": "parsed"
},
],
}
```
tasks.json
```
{
"version": "2.0.0",
"tasks": [
{
"label": "Launch OpenOCD",
"command": "${config:openocd.path}",
"args": [
"-f",
"\"board/${config:inav.debug.board}\"",
"-c",
"\"bindto 0.0.0.0\""
],
"type": "shell",
"isBackground": false,
"group": "none",
"presentation": {
"reveal": "always",
"panel": "new"
},
"problemMatcher": []
},
{
"label": "CMAKE Release",
"type": "shell",
"command": "mkdir -p release && cd release && cmake -DCMAKE_BUILD_TYPE=Release ..",
"group": "build",
"problemMatcher": [],
"options": {
"cwd": "${workspaceFolder}"
}
},
{
"label": "CMAKE Debug",
"type": "shell",
"command": "mkdir -p debug && cd debug && cmake -DCMAKE_BUILD_TYPE=Debug ..",
"group": "build",
"problemMatcher": [],
"options": {
"cwd": "${workspaceFolder}"
}
},
{
"label": "CMAKE Build",
"type": "shell",
"command": "mkdir -p build && cd build && cmake ..",
"group": "build",
"problemMatcher": [],
"options": {
"cwd": "${workspaceFolder}"
}
},
{
"label": "Compile autogenerated docs",
"type": "shell",
"command": "python3 src/utils/update_cli_docs.py",
"problemMatcher": [],
"options": {
"cwd": "${workspaceFolder}"
}
},
{
"label": "Debug",
"type": "shell",
"command": "make ${config:inav.debug.target}",
"group": "build",
"problemMatcher": [],
"options": {
"cwd": "${workspaceFolder}/debug"
}
},
{
"label": "Release",
"type": "shell",
"command": "make ${config:inav.release.target}",
"group": "build",
"problemMatcher": [],
"options": {
"cwd": "${workspaceFolder}/release"
}
},
{
"label": "Build",
"type": "shell",
"command": "make ${config:inav.release.Target}",
"group": "build",
"problemMatcher": [],
"options": {
"cwd": "${workspaceFolder}/build"
}
},
{
"label": "Clean Debug",
"type": "shell",
"command": "make clean",
"group": "build",
"problemMatcher": [],
"options": {
"cwd": "${workspaceFolder}/debug"
}
},
{
"label": "Clean Build",
"type": "shell",
"command": "make clean",
"group": "build",
"problemMatcher": [],
"options": {
"cwd": "${workspaceFolder}/build"
}
},
{
"label": "Clean Release",
"type": "shell",
"command": "make clean",
"group": "build",
"problemMatcher": [],
"options": {
"cwd": "${workspaceFolder}/release"
}
},
]
}
```
settings.json
```
{
"cortex-debug.armToolchainPath": "${workspaceRoot}/tools/gcc-arm-none-eabi-10-2020-q4-major/bin",
"openocd.host": "${env:WSL_HOST_IP}",
"openocd.path": "/mnt/path/to/openocd/bin/openocd.exe",
"inav.debug.svdFile": "STM32F405.svd",
"inav.debug.target": "OMNIBUSF4SD",
"inav.release.target": "MATEKF722SE",
"inav.debug.board": "f4fc.cfg"
}
```
Only the settings.json needs to be modified:
| Option | Explanation |
|--------|-------------|
| cortex-debug.armToolchainPath | path to ARM toolchain, e.g. `${workspaceRoot}/tools/gcc-arm-none-eabi-10-2020-q4-major/bin` |
| openocd.host | IP address of the Windows host, if the environment variable was set as above, do not change anything here, otherwise manually enter the IP of the Windows host. |
| openocd.path | Path to OpenOCD in Windows, format `/mnt/[windows-drive-letter]/path/to/openocd.exe`, for example `/mnt/c/openocd-0.11.0-1/bin/openocd.exe`. |
| inav.debug.svdFile | SVD file, accroding to the processor on your board, see `/dev/svd`, for example `STM32F405.svd`. |
| inav.debug.target | Debug target, for example `OMNIBUSF4SD` |
| inav.release.target | Release target, for example `MATEKF722SE` |
| inav.debug.board | cfg file for OpenOCD, matching the processor of the board, e.g. `f4fc.cfg`, see above |
### Tasks
The following tasks are available
| Task | Description |
|------|-------------|
| Launch OpenOCD | Must be started manually once per session, starts OpenOCD with the correct options. |
| CMAKE Release | Creates a new folder "release", and configures the buildsystem for a real release build in it. |
| CMAKE Debug | Creates a new folder "debug", and configures the buildsystem for a real debug build in it. |
| CMAKE Build | Creates a new folder "build" and configures the buildsystem for the standard build. |
| Compile autogenerated docs | Rebuilds the CLI documentation |
| Debug | Compiles a new debug build for the target specified in inav.debug.target |
| Release | Compiles a new release build for the target specified in inav.release.target. |
| Build | Compiles a new standard build for the target specified in inav.release.target. |
| Clean Debug | Cleans up the debug folder |
| Clean Release | Cleans up the release folder |
| Clean Build | Cleans up the build folder |
With `F5`, a new debug session can be started normally (recompiling if necessary), with `CTRL+SHIFT+B` a build task can be started.
## Problem solving
If OpenOCD reports an error `Couldn't bind to ...`, it may be that Hyper-V is blocking these ports, see this thread on stackoverflow for solutions:
https://stackoverflow.com/questions/48478869/cannot-bind-to-some-ports-due-to-permission-denied
+160
View File
@@ -0,0 +1,160 @@
# Hardware Debugging
Hardware debugging allows debugging the firmware with GDB, including most of its
features that you can find while debugging software for a computer like setting
breakpoins or printing variables or stepping through the code.
Additionally, firmware can also be flashed directly either from the IDE or from GDB,
significanly reducing the time required for the compile/flash/test cycle.
## Required Hardware
Although more complex and expensive solutions exists, an STLink V2 clone will let you
use all the features of hardware debugging. They can be purchased on any of the typical
Chinese sites.
[ST Link V2 Clone](https://inavflight.com/shop/s/bg/1177014)
[Original ST Link V2](https://inavflight.com/shop/s/bg/1099119)
Additionally, most nucleo boards from ST come with a brekable part that contains an
STLink V2.1 or V3. These can also be used to debug an FC, but can be more difficult to
source.
To connect it a flight controller, you need to locate the SWDIO and SWCLK pins from the
MCU. These correspond to PA13 (SWDIO) and PA14 (SWCLK). Be aware that not all manufacturers
break out these pins, but a lot of them put them in small pads available somewhere.
Connect SWDIO, SWCLK and GND from the FC to pins with the FC
TODO: Add pictures of several FCs with SWDIO and SWCLK highlighted.
## Required software
Besides an ARM toolchain, [OpenOCD](http://openocd.org) is required. Note that at the
time of this writing, OpenOCD hasn't had a release in almost 3 years, so you might
need to look for unofficial releases or compile from source.
[stlink](https://github.com/texane/stlink), while not strictly required, can be handy
for quickly testing the SWD connection or flashing or erasing. To avoid ambiguities
between the hardware and the software, the former will be referred as `ST Link` while
we'll use `stlink` for the latter.
Please, follow the installation instructions for your operating system.
### Windows
Install the Windows Subsystem for Linux, then follow the Linux instructions.
### macOS
Install [Homebrew](https://brew.sh) (a package manager) first.
To install OpenOCD type `brew install open-ocd --HEAD` in a terminal. Note the `--HEAD`
command line switch.
For stlink, use `brew install stlink`.
### Linux
Install [Homebrew for Linux](https://docs.brew.sh/Homebrew-on-Linux), since versions
provided by your distro's package manager might be out of date. Homebrew can cohexist
with your existing package manager without any problems.
Then, follow the same instructions for installing OpenOCD and stlink for macOS.
## Hardware setup
Connect SWDIO and SWCLK from the FC to pins with the same label on the ST Link. You must
also connect one of the GND from FC to any of the GND pins to the ST Link. Note the
following caveats:
- There are several ST Link clone types with different pinouts. Pay attention to the pin
labels.
- In some ST Link clones, some GND pins are actually floating and not connected to
- anything. Use a multimeter to check the GND pins and use any of the valid ones.
- Even if you're powering everything from the same computer, make sure to directly connect
the grounds from the FC to the ST Link. Some FC/stlink combinations have a 0.1-0.2V
difference between their grounds and if you don't connect them, stlink won't work.
The FC can be powered by any power source that it supports (battery, USB, etc...), just
make sure to not connect power from the ST Link (the pins labelled as 3.3V and 5V) to the
FC if something else is powering it.
Once you're wired everything, test the connections with a DMM before applying power. Then
power both the FC and the stlink (the order doesn't matter) and run `st-info --probe`
You should see something like:
```
Found 1 stlink programmers
serial: 0d0d09002a12354d314b4e00
openocd: "\x0d\x0d\x09\x00\x2a\x12\x35\x4d\x31\x4b\x4e\x00"
flash: 524288 (pagesize: 16384)
sram: 131072
chipid: 0x0431
descr: F4 device (low power) - stm32f411re
```
## Compilation options
INAV is compiled with debug symbols by default, since they're only stored in the locally
generated `.elf` file and they never use flash space in the target. However, some
optimizations like inlining and LTO might rearrange some sections of the code enough
to interfere with debugging. All compile time optimizations can be disabled by
using `DEBUG=GDB` when calling `make`.
You may find that if you compile all the files without optimizations the program might
too big to fit on the target device. In that case, one of the possible solutions is
compiling all files with optimization (`make clean`, `make ...`) first, then re-save
or `touch` the files you want to be able to step though and then run `make DEBUG=GDB`.
This will then re-compile the files you're interested in debugging with debugging symbols and you will get a smaller binary file which should then fit on the device.
## Debugging
To run a debug session, you will need two terminal windows. One will run OpenOCD, while
the other one will run gdb.
Although not strictly required, it is recommended to set the target you're working on
in `make/local.mk` (create it if it doesn't exist), by adding a line like e.g.
`TARGET ?= SOME_VALID_TARGET`. This way you won't need to specify the target name in
all commands.
From one of the terminals, type `make openocd-run`. This will start OpenOCD and connect
to the MCU. Leave OpenOCD running in this terminal.
From another terminal, type `make gdb-openocd`. This will compile the `.elf` binary for
the current target and start `gdb`. From there you will usually want to execute the gdb
`load` command first, which will flash the binary to the target. Once it finishes, start
running it by executing the `continue` command.
For conveniency, you can invoke `make gdb-openocd` with the environment variable `$LOAD`
set to a non-empty string (e.g. `LOAD=1 make gdb-openocd`), which will run the `load`
command and flash the target as soon as gdb connects to it.
From there on, you can use any gdb commands like `b` for setting breakpoints, `p` for
printing, etc... Check a gdb tutorial for more details if you're not already familiar
with it.
### Rebuilding and reflashing
To rebuild, flash and rerun the binary after doing any modifications, recompile it
with `make`, then press `control+c` to interrupt gdb. Halt the target by entering the
gdb command `monitor reset halt` and then type `load` to flash it. gdb will notice the
binary has changed and re-read the debug symbols. Then you can restart the firmware with
`continue`. This way, you can very quickly flash, upload and test since neither OpenOCD
nor gdb need to be restarted.
### ST Link versions
By default, the Makefiles will assume an ST Link v2, which is the version found in the
popular and cheap clones. However, other versions are also supported. Just set the
`STLINK` environment variable (either via command line or either via `local.mk`) to
`1` or `2` or `2.1`, according to your hardware.
### Semihosting
Semihosting is an ARM feature that allows printing messages via the SWD connection.
The logging framework inside INAV can output its messages via semihosting. To enable
it, make sure you've deleted all generated files (e.g. `make clean`) and set the
environment variable `$SEMIHOSTING` to a non-empty string, either via command line
or via `local.mk`. Once you start the target, log messages will appear on the openocd
terminal. Note that even with semihosting enabled, logging has be explicitely enabled
via settings.
@@ -0,0 +1,17 @@
# Hardware In The Loop (HITL) plugin for X-Plane 11/12
**Hardware-in-the-loop (HITL) simulation**, is a technique that is used in the development and testing of complex real-time embedded systems.
**X-Plane** is a flight simulation engine series developed and published by Laminar Research https://www.x-plane.com/
**INAV-X-Plane-HITL** is plugin for **X-Plane** for testing and developing flight controllers with **INAV flight controller firmware**
https://github.com/RomanLut/INAV-X-Plane-HITL
or for latest HITL features (INAV >= 9.0)
**INAV-X-Plane-XITL**
https://github.com/Scavanger/INAV-X-Plane-XITL
HITL technique can be used to test features during development. Please check page above for installation instructions.
@@ -0,0 +1,46 @@
# Introduction
While many of the instructions here are somewhat generic and will likely work for other projects, the goal of these instructions is to assist a non-developer INAV user to acquire firmware that includes a pull request so he can flash it on his supported fc.
Building the pull request manually or using custom/unofficial targets is not the focus of this document.
# Why should you test a pull request?
- You want to volunteer time and resources helping improving INAV for everyone by catching issues before they are introduced in the next release of INAV!
- You reported or are affected by a bug that has been addressed by this pull request and want to help test the fix
- You are interested in testing a new feature implemented by this pull request
# Why should you not test a pull request?
- Pull requests are beta code and may have bugs; bugs may cause you to crash and damage your model
- Upgrading from the stable version of INAV may require changes to your config that are not yet fully documented
# Before you proceed
- Read the comments on the pull request you want to test. It may provide useful context and information on known issues, required configuration changes or what branch of the inav-configurator is required.
- Make sure the pull request has passed all checks, otherwise you may not have pre-compiled firmware images.
- Make a diff all backup of your existing INAV configuration.
- Take notes of what INAV target you are using.
- You will need a recent version of INAV Configurator from master, or even a specific branch. If you don't need a specific branch, [inav-configurator-next](https://seyrsnys-inav-cfg-next.surge.sh/) usually has recent unofficial pre-built versions of INAV Configurator. If your pull requests refers to an inav-configruator pull request, you are likely to need a specific branch of the configurator. In that case you can try to build it from source by following the build [``Instructions``](https://github.com/iNavFlight/inav-configurator#building-and-running-inav-configurator-locally-for-development) or follow instructions on how to do any needed configuration changes using the CLI.
# Finding the pull request
This is easy, but you will need to be logged in to your GitHub account.
Navigate to the INAV github project and click on [``Pull Requests``](https://github.com/iNavFlight/inav/pulls).
You can just scroll through the list to find a pull request you are interested in, or use the filter bar by typing the name of the pull request, or the number, if you know it.
![Search results](assets/pr_testing/pr_search_result.png)
Once you find the one you are looking for, go ahead an open it!
Click on the ``Checks`` tab
Click on the down arrow next to the number of artifacts
![Artifact list](assets/pr_testing/artifacts_download.png)
You should see a list of files. The one without SITL in the name, the biggest one, will be a zip file with all official target .hex files. Click on it to download it to your computer.
Extract all files and select the firmware for your target using the configurator by clicking on ``Load Firmware [Local]`` button. Don't forget to use the ``Full chip erase`` option, as there are no guarantees the firmware will be compatible with your existing settings.
# I have flashed the new firmware, what should I do next?
- You should configure your model, either manually from scratch, or by loading your diff file. Keep in mind that loading a diff file may not always work, as there may have been some other changes in INAV that require attention. But even if you start from scratch, there are usually many sections that are safe to copy over from your diff.
- Try to reproduce the bug reported or play around with the new feature.
- Once you are done testing, don't forget to report your results on the pull request. Both positive results and issues are valid and welcome feedback.
@@ -0,0 +1,122 @@
# IDE - Visual Studio Code with Windows 10
![Visual Studio Code](assets/vscode01.png)
[Visual Studio Code](https://code.visualstudio.com/) is probably the best free option for all Windows 10 users. It provides almost seamless integration with WSL running Ubuntu, syntax highlighting, building, and hardware debugging.
## Setup
1. Setup build environment using [generic WSL guide](Building%20in%20Windows%2010%20with%20Linux%20Subsystem.md)
1. Download and install [Visual Studio Code](https://code.visualstudio.com/)
1. From the VS Code Extensions download [Remote - WSL](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-wsl) plugin
1. Open INAV folder
1. Use `Ctrl + Shift + P` to run option `Remote-WSL: Reopen Folder in WSL`
1. Allow firewall and other permissions if requested
1. Install plugins in WSL workspace:
1. [C/C++ from Microsoft](https://marketplace.visualstudio.com/items?itemName=ms-vscode.cpptools) for C/C++ support
1. [Bookmarks](https://marketplace.visualstudio.com/items?itemName=alefragnani.Bookmarks) for simpler navigation
1. Configure the environment using the following snippets as a base
### C propertiues
Edit file `./.vscode/c_cpp_properties.json` to setup enabled `defines`
```
{
"configurations": [
{
"name": "Win32",
"includePath": [
"${workspaceRoot}",
"${workspaceRoot}/src/main/**"
],
"browse": {
"limitSymbolsToIncludedHeaders": false,
"path": [
"${workspaceRoot}/**"
]
},
"intelliSenseMode": "msvc-x64",
"cStandard": "c11",
"cppStandard": "c++17",
"defines": [,
"USE_OSD",
"USE_GYRO_NOTCH_1",
"USE_GYRO_NOTCH_2",
"USE_DTERM_NOTCH",
"USE_ACC_NOTCH",
"USE_GYRO_BIQUAD_RC_FIR2",
"USE_D_BOOST",
"USE_SERIALSHOT",
"USE_ANTIGRAVITY",
"USE_ASYNC_GYRO_PROCESSING",
"USE_RPM_FILTER",
"USE_GLOBAL_FUNCTIONS",
"USE_DYNAMIC_FILTERS",
"USE_DSHOT",
"FLASH_SIZE 480",
"USE_I2C_IO_EXPANDER",
"USE_PCF8574",
"USE_ESC_SENSOR"
]
}
],
"version": 4
}
```
### Tasks
Edit `./.vscode/tasks.json` to enable Building with `Ctrl + Shift + B` keyboard shortcut and from Command Console.
```
{
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "2.0.0",
"tasks": [
{
"label": "Install/Update CMAKE",
"type": "shell",
"command": "mkdir -p build && cd build && cmake ..",
"group": "build",
"problemMatcher": [],
"options": {
"cwd": "${workspaceFolder}"
}
},
{
"label": "Compile autogenerated docs",
"type": "shell",
"command": "python3 src/utils/update_cli_docs.py",
"problemMatcher": [],
"options": {
"cwd": "${workspaceFolder}"
}
},
// Example of building a single target
{
"label": "Build Matek F722-WPX",
"type": "shell",
"command": "make MATEKF722WPX",
"group": "build",
"problemMatcher": [],
"options": {
"cwd": "${workspaceFolder}/build"
}
},
// Example of building multiple targets
{
"label": "Build Matek F405-STD & WING",
"type": "shell",
"command": "make MATEKF405 MATEKF405SE",
"group": "build",
"problemMatcher": [],
"options": {
"cwd": "${workspaceFolder}/build"
}
}
]
}
```
+11
View File
@@ -0,0 +1,11 @@
# Travis
INAV provides Travis build and config files in the repository root.
## Pushing builds to a remote server
```.travis.sh``` script can upload build artifacts to a remote server. This feature is controlled by the
```PUBLISH_URL``` environment variable. If set, the build script will use the cURL binary and simulate
a file upload post to the configured server.
Pleas check the ```notifications``` section in the ```.travis.yml``` file and adjust the irc notifications if you plan on using Travis on your INAV fork
@@ -0,0 +1,262 @@
# Environment prerequistes
- Windows 10/11
- WSL2 (Ubuntu 20.04), [how to install WSL2](https://docs.microsoft.com/en-us/windows/wsl/install)
- **ATTENTION:** Works ONLY on WSL2 Kernel version 5.10.60.1 or later, check it with command `uname -a` and if it is lower - update WSL2
- Using Windows Update: Start > Settings > Windows Update > Advanced Options > Receive updates for other Microsoft products SHOULD be Turned ON
- Start > Settings > Windows Update > Check for updates
- Using Elevated Command Prompt: wsl --update
- NOTE: If your WSL is V1 -> Switch it to V2 or install new WSL (Ubuntu 20.04) instance (switching are done with command `wsl --set-default-version 2`)
- ST-Link V2 Debugger
- INAV Project are Cloned from GitHub to WSL2 storage space, look at:
- [IDE - Visual Studio Code with Windows 10](https://github.com/iNavFlight/inav/blob/master/docs/development/IDE%20-%20Visual%20Studio%20Code%20with%20Windows%2010.md)
- [Building in Windows 10 or 11 with Linux Subsystem](https://github.com/iNavFlight/inav/blob/master/docs/development/Building%20in%20Windows%2010%20or%2011%20with%20Linux%20Subsystem.md)
- [Hardware Debugging](https://github.com/iNavFlight/inav/blob/master/docs/development/Hardware%20Debugging.md)
# Installation
## WSL2 Setup
### Install prerequistes
- `sudo apt install linux-tools-5.4.0-77-generic hwdata`
- `sudo update-alternatives --install /usr/local/bin/usbip usbip /usr/lib/linux-tools/5.4.0-77-generic/usbip 20`
- `sudo apt install libncurses5`
### Adjust rules to allow connecting with non-root
The following script creates access rules for ST-LINK V2, V2.1 and V3 and for some USB-to-serial converters, download it to new file under you home folder, save, change attributes and run
- `nano createrules.sh`
- Copy and Paste script below
- Ctrl-X
- Y
- Enter
- `sudo chmod +x createrules.sh`
- `./createrules.sh`
```
#!/bin/bash
sudo tee /etc/udev/rules.d/70-st-link.rules > /dev/null <<'EOF'
# ST-LINK V2
SUBSYSTEMS=="usb", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="3748", MODE="600", TAG+="uaccess", SYMLINK+="stlinkv2_%n"
# ST-LINK V2.1
SUBSYSTEMS=="usb", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="374b", MODE="600", TAG+="uaccess", SYMLINK+="stlinkv2-1_%n"
SUBSYSTEMS=="usb", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="3752", MODE="600", TAG+="uaccess", SYMLINK+="stlinkv2-1_%n"
# ST-LINK V3
SUBSYSTEMS=="usb", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="374d", MODE="600", TAG+="uaccess", SYMLINK+="stlinkv3loader_%n"
SUBSYSTEMS=="usb", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="374e", MODE="600", TAG+="uaccess", SYMLINK+="stlinkv3_%n"
SUBSYSTEMS=="usb", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="374f", MODE="600", TAG+="uaccess", SYMLINK+="stlinkv3_%n"
SUBSYSTEMS=="usb", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="3753", MODE="600", TAG+="uaccess", SYMLINK+="stlinkv3_%n"
EOF
sudo tee /etc/udev/rules.d/70-usb-to-serial.rules > /dev/null <<'EOF'
# CP2101 - CP 2104
SUBSYSTEMS=="usb", ATTRS{idVendor}=="10c4", ATTRS{idProduct}=="ea60", MODE="600", TAG+="uaccess", SYMLINK+="usb2ser_%n"
# ATEN UC-232A
SUBSYSTEMS=="usb", ATTRS{idVendor}=="0557", ATTRS{idProduct}=="2008", MODE="600", TAG+="uaccess", SYMLINK+="usb2ser_aten_%n"
EOF
sudo udevadm control --reload-rules
```
#### In case you want reload services manually later
- `sudo service udev restart`
- `udevadm control --reload`
## Windows Setup
- Download last usbipd from [here](https://github.com/dorssel/usbipd-win/releases)
- Install it
## VS Code Setup
- INAV project is on WSL drive
- Folder opened using WSL Remote extension
- Cortex Debug extension installed
- CMake will install all dependencies after first build automatically
- Use configuration files provided below, place them to `.vscode` subfolder (create it if required)
# Debugging
## Preparing for debugging (before each new debug session)
### Open WSL2 prompt
- Go to project folder
- `lsusb`
- `sudo /lib/systemd/systemd-udevd --daemon`
- `sudo udevadm control --reload-rules && udevadm trigger`
- REPLUG USB
### Open Elevated Command Prompt (Run as Administrator)
- `usbipd wsl list` - shows you plugged and accessible USB devices
- `usbipd wsl attach --busid ID_OF_DEVICE_FROM_FIRST_COMMAND` - will attach USB device to WSL
- Just for info: `usbipd detach --busid ID_OF_DEVICE_FROM_FIRST_COMMAND` - will deattach USB device from WSL
### Back to WSL2 prompt
- `lsusb` - should show you just attached USB device
- `st-info --probe` - should "see" ST-Link and MCU
#### Leave Command Prompt and WSL Prompt minimized (for later usage)
#### **NOTE:** Due to some USB reconnect issues, sometimes, is need to execute `usbipd wsl list` and `usbipd wsl attach...` commands again, to reconnect ST-Link to WSL
## Debugging
- Connect SWD from ST-Link to FC board (at least GND, SWDIO and SWCLK should be connected, but connecting Vref to +3.3V pad and RESET accordingly will improve debugging stability a lot!)
- Power FC (can be powered from USB)
- Select (Debug) CMake configuration
- Build required target from VSCode
- Use VS Code Run -> Start Debugging (F5) menu (make sure debugging target is "Cortex Debug")
**NOTE:** sometimes "autobuild" script is not performed well, it is recommended to repeat last two steps every time you change code and need "reflash-debug"
**NOTE:** after long and/or intensive debugging OpenOCD can crash, in this case just reopen VSCode, replug ST-Link USB, reattach USB to WSL and start debug session again
# Troubleshooting
- OpenOCD shows Permission denied during "Flashing":
- Go to WSL, INAV Project folder and run `cd src/utils` and `sudo chmod +x *`
- If running from WSL itself and get errors:
- Go to WSL and run
- `sudo apt install gdb-multiarch`
- `sudo ln -s /usr/bin/gdb-multiarch /usr/bin/arm-none-eabi-gdb`
# References
- https://github.com/dorssel/usbipd-win/wiki/WSL-support
- https://devblogs.microsoft.com/commandline/connecting-usb-devices-to-wsl/#:~:text=Select%20the%20bus%20ID%20of,to%20run%20a%20sudo%20command.&text=From%20within%20WSL%2C%20run%20lsusb,it%20using%20normal%20Linux%20tools.
- https://calinradoni.github.io/pages/200616-non-root-access-usb.html
# VS Code Example configurations
`launch.json`
```
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
// ******* INAV ********
// Define the following values in settings.json
// - BUILD_DIR: Relative path to the build directory
// - TARGET: Target name that you want to launch
"version": "0.2.0",
"configurations": [
{
"name": "Cortex Debug",
"cwd": "${workspaceRoot}",
"executable": "${config:BUILD_DIR}/bin/${config:TARGET}.elf",
"request": "launch",
"type": "cortex-debug",
"servertype": "openocd",
"device": "${config:TARGET}",
"configFiles": [
"${config:BUILD_DIR}/openocd/${config:TARGET}.cfg"
],
"preLaunchTask": "openocd-debug-prepare",
"svdFile": "${config:BUILD_DIR}/svd/${config:TARGET}.svd",
}
]
}
```
`settings.json`
```
{
"C_Cpp.default.configurationProvider": "ms-vscode.cmake-tools",
"files.associations": {
"general_settings.h": "c",
"parameter_group.h": "c"
},
"BUILD_DIR": "build",
"TARGET": "MAMBAF405"
}
```
`tasks.json`
```
{
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "2.0.0",
"options": {
"env": {
"TARGET": "${config:TARGET}",
}
},
"tasks": [
{
"label": "target",
"type": "shell",
"command": "make", "args": ["-C", "${config:BUILD_DIR}", "${config:TARGET}"],
"problemMatcher": "$gcc",
"group": {
"kind": "build",
"isDefault": true
},
"presentation": {
"echo": true,
"reveal": "always",
"focus": false,
}
},
{
"label": "flash",
"type": "shell",
"command": "make", "args": ["-C", "${config:BUILD_DIR}", "openocd_flash_${config:TARGET}"],
"dependsOn": "elf"
},
{
"label": "svd",
"type": "shell",
"command": "make", "args": ["-C", "${config:BUILD_DIR}", "svd_${config:TARGET}"],
"problemMatcher": []
},
{
"label": "openocd-cfg",
"type": "shell",
"command": "make", "args": ["-C", "${config:BUILD_DIR}", "openocd_cfg_${config:TARGET}"],
"problemMatcher": []
},
{
"label": "openocd-debug-prepare",
"type": "shell",
// "dependsOn": ["svd", "openocd-cfg", "flash"],
"dependsOn": ["svd", "openocd-cfg"],
"problemMatcher": []
}
]
}
```
`cpp_properties.json`
```
{
"configurations": [
{
"name": "Win32",
"includePath": [
"${workspaceRoot}",
"${workspaceRoot}/src/main/**"
],
"browse": {
"limitSymbolsToIncludedHeaders": false,
"path": [
"${workspaceRoot}/**"
]
},
"intelliSenseMode": "msvc-x64",
"cStandard": "c11",
"cppStandard": "c++17",
"defines": [
"USE_OSD",
"USE_GYRO_NOTCH_1",
"USE_GYRO_NOTCH_2",
"USE_DTERM_NOTCH",
"USE_ACC_NOTCH",
"USE_GYRO_BIQUAD_RC_FIR2",
"USE_D_BOOST",
"USE_SERIALSHOT",
"USE_ANTIGRAVITY",
"USE_ASYNC_GYRO_PROCESSING",
"USE_RPM_FILTER",
"USE_GLOBAL_FUNCTIONS",
"USE_DYNAMIC_FILTERS",
"USE_DSHOT",
"FLASH_SIZE 480",
"USE_I2C_IO_EXPANDER",
"USE_PCF8574",
"USE_ESC_SENSOR"
],
"configurationProvider": "ms-vscode.cmake-tools"
}
],
"version": 4
}
```
@@ -0,0 +1,108 @@
# Building in windows light [Deprecated]
> **Building with this method is deprecated and not advised. All Windows users should be using
Linux Subsystem (WSL) instead**
no cygwin and no path changes
## Install Git for windows
download https://github.com/git-for-windows/git/releases/download/v2.10.1.windows.1/Git-2.10.1-32-bit.exe
Recommended install location is C:\Git (no spaces or special characters in path)
Follow images as not all are at default settings.
![Git Installation](assets/001.gitwin.png)
![Git Installation](assets/002.gitwin.png)
![Git Installation](assets/003.gitwin.png)
![Git Installation](assets/004.gitwin.png)
![Git Installation](assets/005.gitwin.png)
![Git Installation](assets/006.gitwin.png)
![Git Installation](assets/007.gitwin.png)
![Git Installation](assets/008.gitwin.png)
![Git Installation](assets/009.gitwin.png)
![Git Installation](assets/010.gitwin.png)
## Install toolset scripts
download https://www.dropbox.com/s/hhlr16h657y4l5u/devtools.zip?dl=0
extract it into C:\ it creates devtools folder
## Install latest arm toolchain
download https://gcc.gnu.org/mirrors.html
extract it into C:\devtools\gcc-arm-none-eabi-... (folder already there)
## Install Ruby
Install the latest Ruby version using [Ruby Installer](https://rubyinstaller.org).
## Test
Run C:\devtools\shF4.cmd
If everything went according the manual you should be in mingw console window. (if not we need to update this manual)
Try command "arm-none-eabi-gcc --version" and output should be like in screenshot. (tab complete works here)
![Test toolchain](assets/001.test.png)
Note1: Advanced users can edit shF4.cmd for paths if they don't want to use defaults. You might want to change TOOLS_DIR and PATH_DIRS variables.
Note2: You can copy shF4.cmd anywhere you want and run it from there. It will open console window in that folder.
Note3: Included example batch-scripts (make_REVO.bat) that you can use to build target just by double clicking it.
## Checkout and compile INAV
Head over to the INAV Github page and grab the URL of the GIT Repository: "https://github.com/iNavFlight/inav"
Run shF4.cmd and use the git commandline to checkout the repository:
```bash
git clone https://github.com/iNavFlight/inav
```
![GIT Checkout](assets/011.git_checkout.png)
![GIT Checkout](assets/002.test.png)
To compile your INAV binaries, enter the INAV directory and build the project using the make command. You can append TARGET=[HARDWARE] if you want to build anything other than the default SPRACINGF3 target:
```bash
cd inav
make TARGET=SPRACINGF3
```
![GIT Checkout](assets/003.test.png)
within few moments you should have your binary ready:
```bash
(...)
arm-none-eabi-size ./obj/main/inav_SPRACINGF3.elf
text data bss dec hex filename
127468 916 16932 145316 237a4 ./obj/main/inav_SPRACINGF3.elf
arm-none-eabi-objcopy -O ihex --set-start 0x8000000 obj/main/inav_SPRACINGF3.elf obj/inav_1.2.1_SPRACINGF3.hex
```
You can use the INAV-Configurator to flash the ```obj/inav_1.2.1_SPRACINGF3.hex``` file.
## Updating and rebuilding
Navigate to the local inavflight repository and use the following steps to pull the latest changes and rebuild your version of inavflight:
```bash
cd inav
git reset --hard
git pull
make clean TARGET=SPRACINGF3
make
```
Binary file not shown.

After

Width:  |  Height:  |  Size: 121 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 146 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 576 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 220 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 564 KiB

File diff suppressed because it is too large Load Diff
+29
View File
@@ -0,0 +1,29 @@
# INAV MSP Messages reference
**This page is auto-generated from the [master INAV MSP definitions file](https://github.com/iNavFlight/inav/blob/master/docs/development/msp/msp_messages.json)**
For details on the structure of MSP, see [The wiki page](https://github.com/iNavFlight/inav/wiki/MSP-V2)
For list of enums, see [Enum documentation page](https://github.com/iNavFlight/inav/wiki/Enums-reference)
**JSON file rev: <file_rev>**
**Warning: Verification needed, exercise caution until completely verified for accuracy and cleared, especially for integer signs. Source-based generation/validation is forthcoming. Refer to source for absolute certainty**
**If you find an error, it must be corrected in the JSON spec, not this markdown.**
**Guide:**
* **MSP Versions:**
* **MSPv1:** The original protocol. Uses command IDs from 0 to 254.
* **MSPv2:** An extended version. Uses command IDs from 0x1000 onwards.
* **Request Payload:** The request payload sent to the destination (usually the flight controller). May be empty or hold data for setting or requesting data from the FC.
* **Reply Payload:** The reply sent from the FC to the sender. May be empty or hold data.
* **Notes:** Pay attention to message notes and description.
<format>
---
+132
View File
@@ -0,0 +1,132 @@
# Format:
## JSON format example:
```
"MSP_API_VERSION": {
"code": 1,
"mspv": 1,
"request": null,
"reply": {
"payload": [
{
"name": "mspProtocolVersion",
"ctype": "uint8_t",
"units": "",
"desc": "MSP Protocol version (`MSP_PROTOCOL_VERSION`, typically 0)."
},
{
"name": "apiVersionMajor",
"ctype": "uint8_t",
"units": "",
"desc": "INAV API Major version (`API_VERSION_MAJOR`)."
},
{
"name": "apiVersionMinor",
"ctype": "uint8_t",
"units": "",
"desc": "INAV API Minor version (`API_VERSION_MINOR`)."
}
],
},
"notes": "Used by configurators to check compatibility.",
"description": "Provides the MSP protocol version and the INAV API version."
},
```
## Message fields:
**name**: MSP message name\
**code**: Integer message code\
**description**: String with description of message\
**request**: null or dict of data sent\
**reply**: null or dict of data received\
**variable_len**: Optional boolean, if true, message does not have a predefined fixed length and needs appropriate handling\
**variants**: Optional special case, message has different cases of reply/request. Key/description is not a strict expression or code; just a readable condition\
**not_implemented**: Optional special case, message is not implemented (never or deprecated)\
**replaced_by**: Optional array of MSP message names that replace this command. Present when a command is deprecated and scheduled for removal. Empty array if no replacement is needed\
**notes**: String with details of message
## Data dict fields:
**payload**: Array of payload fields\
**repeating**: Optional Special Case, integer or string of how many times the *entire* payload is repeated
## Payload fields:
### Fields:
**name**: field name from code\
**ctype**: Base C type of the value. Arrays list their element type here as well\
**desc**: Optional string with description and details of field\
**units**: Optional defined units\
**enum**: Optional string of enum struct if value is an enum
**array**: Optional boolean to denote field is array of more values\
**array_size**: If array, integer count of elements. Use `0` when the length is indeterminate/variable\
**array_size_define**: Optional string naming the source `#define` that provides the size (informational only)\
**repeating**: Optional Special case, contains array of more payload fields that are added Times * Key\
**payload**: If repeating, contains more payload fields\
**polymorph**: Optional boolean special case, field does not have a defined C type and could be anything
**Simple value**
```
{
"name": "mspProtocolVersion",
"ctype": "uint8_t",
"units": "",
"desc": "MSP Protocol version (`MSP_PROTOCOL_VERSION`, typically 0)."
},
```
**Fixed length array**
```
{
"name": "fcVariantIdentifier",
"ctype": "char",
"desc": "4-character identifier string (e.g., \"INAV\"). Defined by `flightControllerIdentifier",
"array": true,
"array_size": 4,
"units": ""
}
```
**Array sized via define**
```
{
"name": "buildDate",
"ctype": "char",
"desc": "Build date string (e.g., \"Dec 31 2023\").",
"array": true,
"array_size": 11,
"array_size_define": "BUILD_DATE_LENGTH",
"units": ""
}
```
**Undefined length array**
```
{
"name": "firmwareChunk",
"ctype": "uint8_t",
"desc": "Chunk of firmware data",
"array": true,
"array_size": 0,
}
```
**As of yet unknown length array**
```
{
"name": "elementText",
"ctype": "char",
"desc": "Static text bytes, not NUL-terminated and not yet sized.",
"array": true,
"array_size": 0
}
```
**Nested array with struct**
```
{
"repeating": "maxVehicles",
"payload": [
{
"name": "adsbVehicle",
"ctype": "adsbVehicle_t",
"desc": "Array of `adsbVehicle_t` Repeated `maxVehicles` times",
"repeating": "maxVehicles",
"array": true,
"array_size": 0,
"units": ""
}
]
}
```
+27
View File
@@ -0,0 +1,27 @@
INAV_MAIN_PATH="../../../src/main"
echo "###########"
echo get_all_inav_enums_h.py
python get_all_inav_enums_h.py --inav-root "$INAV_MAIN_PATH"
echo "###########"
echo "msp_messages.json checksum"
actual="$(md5sum msp_messages.json | awk '{print $1}')"
expected="$(awk '{print $1}' msp_messages.checksum)"
echo "Hash:" $actual
if [[ "$actual" != "$expected" ]]; then
n="$(cat rev)"
printf '%d' "$((n + 1))" > rev
echo "File changed, incrementing revision"
echo $actual > msp_messages.checksum
fi
echo "###########"
echo gen_msp_md.py
python gen_msp_md.py
echo "###########"
echo gen_enum_md.py
python gen_enum_md.py
rm all_enums.h
read -n 1 -s -r -p "Press any key to continue"
+297
View File
@@ -0,0 +1,297 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
enumdoc.py Generate Markdown documentation from C enums (no expression eval).
Rules:
- One Value column only.
* If explicit assignment is a plain int literal (dec/hex/bin/oct) -> show that number.
* If explicit assignment is anything else -> show the raw expression text.
* If no assignment -> auto-increment.
- If auto-increment occurs inside an active preprocessor condition, wrap the number
in parentheses to indicate conditional numbering: e.g., 3, 4, #ifdef, (5), (6).
- Tracks nested #if/#ifdef/#ifndef/#elif/#else/#endif and shows Condition text.
- Handles multiline enumerators (split at the first top-level comma).
"""
import sys
import re
from pathlib import Path
from typing import List, Optional
import json
# ---------- Helpers ----------
BLOCK_COMMENT_RE = re.compile(r'/\*.*?\*/', re.DOTALL)
def strip_comments(s: str) -> str:
s = BLOCK_COMMENT_RE.sub('', s)
s = re.sub(r'//.*', '', s)
return s
def find_top_level_comma(s: str) -> int:
depth = 0
for i, ch in enumerate(s):
if ch == '(':
depth += 1
elif ch == ')':
depth = max(0, depth - 1)
elif ch == ',' and depth == 0:
return i
return -1
def is_plain_int_literal(expr: str) -> Optional[int]:
"""
Return int value if expr is a plain integer literal (dec/hex/bin/oct),
otherwise None. Whitespace ok; no unary ops/casts/suffixes.
"""
t = expr.strip()
if not t:
return None
if re.fullmatch(r'0[xX][0-9A-Fa-f]+', t) or \
re.fullmatch(r'0[bB][01]+', t) or \
re.fullmatch(r'0[0-7]*', t) or \
re.fullmatch(r'[1-9][0-9]*', t) or \
t == '0':
try:
return int(t, 0)
except Exception:
return None
return None
# ---------- Parsing regexes ----------
RE_ENUM_START = re.compile(r'^\s*typedef\s+enum(?:\s+[A-Za-z_]\w*)?\s*\{')
RE_ENUM_END = re.compile(r'^\s*\}\s*([A-Za-z_]\w*)\s*;')
RE_LINE_COMMENT = re.compile(r'^\s*//\s*(.+?)\s*$')
RE_IFDEF = re.compile(r'^\s*#\s*ifdef\s+(\w+)')
RE_IFNDEF = re.compile(r'^\s*#\s*ifndef\s+(\w+)')
RE_IF = re.compile(r'^\s*#\s*if\s+(.+)$')
RE_ELIF = re.compile(r'^\s*#\s*elif\s+(.+)$')
RE_ELSE = re.compile(r'^\s*#\s*else\s*$')
RE_ENDIF = re.compile(r'^\s*#\s*endif\b')
def normalize_condition_text(text: str) -> str:
t = text.strip()
t = re.sub(r'\bdefined\s*\(\s*(\w+)\s*\)', r'\1', t)
t = re.sub(r'\s+', ' ', t)
return t
class ConditionStack:
def __init__(self):
self.stack: List[str] = []
def push_ifdef(self, sym: str): self.stack.append(sym)
def push_ifndef(self, sym: str): self.stack.append(f'!{sym}')
def push_if(self, expr: str): self.stack.append(normalize_condition_text(expr))
def elif_(self, expr: str):
if self.stack: self.stack.pop()
self.stack.append(normalize_condition_text(expr))
def else_(self):
if not self.stack: return
top = self.stack.pop()
if top.startswith('!'): self.stack.append(top[1:])
elif top and all(ch.isalnum() or ch == '_' for ch in top): self.stack.append(f'!{top}')
else: self.stack.append(f'NOT({top})')
def endif(self):
if self.stack: self.stack.pop()
def current(self) -> str:
return " AND ".join(self.stack) if self.stack else ""
def has_active(self) -> bool:
return bool(self.stack)
# ---------- Model ----------
class EnumItem:
def __init__(self, name: str, value_display: str, cond: str):
self.name = name
self.value_display = value_display # number, (number), or raw expr string
self.cond = cond
class EnumDef:
def __init__(self, name: str, source_note: str):
self.name = name
self.source_note = source_note
self.items: List[EnumItem] = []
# ---------- Core parsing ----------
def parse_files(paths: List[Path]) -> List[EnumDef]:
enums: List[EnumDef] = []
outer_cond = ConditionStack()
for path in paths:
lines = path.read_text(encoding="utf-8", errors="ignore").splitlines()
i = 0
recent_comment: Optional[str] = None
while i < len(lines):
line = lines[i]
# Track outer preproc
if m := RE_IFDEF.match(line): outer_cond.push_ifdef(m.group(1)); i += 1; continue
if m := RE_IFNDEF.match(line): outer_cond.push_ifndef(m.group(1)); i += 1; continue
if m := RE_IF.match(line): outer_cond.push_if(m.group(1)); i += 1; continue
if m := RE_ELIF.match(line): outer_cond.elif_(m.group(1)); i += 1; continue
if RE_ELSE.match(line): outer_cond.else_(); i += 1; continue
if RE_ENDIF.match(line): outer_cond.endif(); i += 1; continue
# Source comment directly above typedef
mcom = RE_LINE_COMMENT.match(line)
if mcom:
recent_comment = mcom.group(1)
if RE_ENUM_START.match(line):
source_note = recent_comment or str(path)
recent_comment = None
body_lines: List[str] = []
i += 1
local_i = i
while local_i < len(lines):
ln = lines[local_i]
if RE_ENUM_END.match(ln):
enum_name = RE_ENUM_END.match(ln).group(1)
enum = EnumDef(enum_name, source_note)
# second pass: parse enumerators
inner = ConditionStack()
current_numeric: Optional[int] = -1 # known numeric head; None means unknown
idx = 0
while idx < len(body_lines):
bl = body_lines[idx]
# inner preproc
if m := RE_IFDEF.match(bl): inner.push_ifdef(m.group(1)); idx += 1; continue
if m := RE_IFNDEF.match(bl): inner.push_ifndef(m.group(1)); idx += 1; continue
if m := RE_IF.match(bl): inner.push_if(m.group(1)); idx += 1; continue
if m := RE_ELIF.match(bl): inner.elif_(m.group(1)); idx += 1; continue
if RE_ELSE.match(bl): inner.else_(); idx += 1; continue
if RE_ENDIF.match(bl): inner.endif(); idx += 1; continue
# accumulate one item across lines
buf = [bl]
while True:
combined = strip_comments(" ".join(buf)).strip()
if not combined:
break
comma_pos = find_top_level_comma(combined)
if comma_pos != -1:
item_text = combined[:comma_pos].strip()
break
if idx + 1 >= len(body_lines):
item_text = combined
break
nxt = body_lines[idx + 1]
if RE_ENUM_END.match(nxt):
item_text = combined
break
idx += 1
buf.append(body_lines[idx])
if not combined:
idx += 1
continue
# NAME or NAME = expr
mitem = re.match(r'^\s*([A-Za-z_]\w*)\s*(?:=\s*(.*))?$', item_text)
if not mitem:
idx += 1
continue
name = mitem.group(1)
expr = (mitem.group(2) or "").strip()
# active condition text
cond_parts = [p for p in (outer_cond.current(), inner.current()) if p]
cond_text = " AND ".join(cond_parts)
# determine display value
if expr:
lit = is_plain_int_literal(expr)
if lit is not None:
# explicit numeric literal
value_display = str(lit)
current_numeric = lit
else:
# show raw expression; numeric chain becomes unknown
value_display = expr
current_numeric = None
else:
# auto-increment if we know a numeric head; else unknown
if current_numeric is None:
value_display = ""
else:
current_numeric += 1
if inner.has_active():
value_display = f"({current_numeric})"
else:
value_display = str(current_numeric)
enum.items.append(EnumItem(name=name, value_display=value_display, cond=cond_text))
idx += 1
enums.append(enum)
i = local_i + 1
break
else:
body_lines.append(lines[local_i])
local_i += 1
else:
i = local_i
continue
else:
i += 1
return enums
# ---------- Markdown rendering ----------
def render_markdown(enums: List[EnumDef]) -> str:
jsonfile = {}
out = []
out.append("# Enumerations\n")
out.append("**Auto-generated reference for MSP, refer to source for development, not this file, due to variations with #ifdefs which needs verification.**\n")
out.append("## Table of contents\n")
for e in sorted(enums, key=lambda x: x.name.lower()):
out.append(f"- [{e.name}](#enum-{e.name.lower()})")
out.append("")
for e in sorted(enums, key=lambda x: x.name.lower()):
jsonfile[e.name] = {}
out.append("---")
out.append(f"## <a id=\"enum-{e.name.lower()}\"></a>`{e.name}`\n")
if e.source_note:
out.append(f"> Source: {e.source_note}\n")
jsonfile[e.name]['_source'] = e.source_note
out.append("| Enumerator | Value | Condition |")
out.append("|---|---:|---|")
for it in e.items:
name_md = f"`{it.name}`"
val = it.value_display
cond = it.cond
out.append(f"| {name_md} | {val} | {cond} |")
jsonfile[e.name][name_md.strip('`')] = [val, cond] if len(cond)>0 else val
# normalize source to a stable inav/src/... path
if '_source' in jsonfile[e.name]:
jsonfile[e.name]['_source'] = jsonfile[e.name]['_source'].replace('../../../src', 'inav/src')
out.append("")
# While we're at it, chuck this all into a JSON file
Path("inav_enums.json").write_text(json.dumps(jsonfile,indent=4), encoding="utf-8")
return "\n".join(out)
# ---------- Main ----------
def main() -> int:
path = Path("all_enums.h")
if not path.exists():
print(f"Error: {path} not found", file=sys.stderr)
return 1
enums = parse_files([path])
md = render_markdown(enums)
Path("inav_enums_ref.md").write_text(md, encoding="utf-8")
return 0
if __name__ == "__main__":
raise SystemExit(main())
+441
View File
@@ -0,0 +1,441 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Generate Markdown documentation from an MSP message definitions JSON.
Strict + Index:
- STRICT: If a code exists in one (MSPCodes vs JSON) but not the other, crash with details.
- Index items link to headings via GitHub-style auto-anchors.
- Tight layout; identical Request/Reply tables; skip complex=true with a stub.
- Default input: msp_messages.json ; default output: MSP_Doc.md
"""
import sys
import json
import re
import unicodedata
from pathlib import Path
from typing import Any, Dict, List, Optional, Tuple, Type
import enum
def build_msp_codes_enum(defs: Dict[str, Any]) -> Type[enum.IntEnum]:
members: Dict[str, int] = {}
for name, body in defs.items():
try:
code = int(body.get("code", -1))
except (TypeError, ValueError):
continue
members[name] = code
return enum.IntEnum("MSPCodes", members)
# ---- C type size helpers ----------------------------------------------------
BASE_SIZES = {
"uint8_t": 1, "int8_t": 1, "char": 1,
"uint16_t": 2, "int16_t": 2,
"uint32_t": 4, "int32_t": 4,
"uint64_t": 8, "int64_t": 8,
"float": 4, "double": 8,
}
array_brackets_re = re.compile(r"^(?P<base>[A-Za-z_0-9]+)\[(?P<size>.*)\]$")
def parse_ctype(ctype: str) -> Tuple[str, Optional[str]]:
m = array_brackets_re.match(ctype.strip())
if not m:
return ctype.strip(), None
return m.group("base").strip(), m.group("size").strip()
def format_ctype(field: Dict[str, Any]) -> str:
raw = (field.get("ctype") or "").strip()
if not raw:
return "-"
base, bracket = parse_ctype(raw)
has_array_meta = bool(field.get("array", False))
is_array = has_array_meta or (bracket is not None)
if not is_array:
return raw
size_define = (field.get("array_size_define") or "").strip()
array_size = field.get("array_size")
size_expr = ""
if size_define:
size_expr = size_define
else:
if isinstance(array_size, int):
if array_size > 0:
size_expr = str(array_size)
elif isinstance(array_size, str):
cleaned = array_size.strip()
if cleaned and cleaned != "0":
size_expr = cleaned
if not size_expr and bracket is not None:
size_expr = bracket.strip()
if size_expr == "0":
size_expr = ""
base_part = base or raw
return f"{base_part}[{size_expr}]"
def describe_array_bytes(array_size_meta: Any, base_bytes: Optional[int], base_name: str) -> str:
"""
Returns a printable byte-count (or symbolic string) for an array entry.
"""
if isinstance(array_size_meta, int):
if array_size_meta <= 0:
return "array"
if base_bytes is None:
return str(array_size_meta)
return str(array_size_meta * base_bytes)
if isinstance(array_size_meta, str):
expr = array_size_meta.strip()
if not expr:
return "array"
if base_bytes is None or base_name == "char":
return expr
return f"{expr} * {base_bytes}"
return "array"
def sizeof_entry(field: Dict[str, Any]) -> str:
ctype = field.get("ctype", "").strip()
base, bracket = parse_ctype(ctype)
is_array = bool(field.get("array", False))
array_size_meta = field.get("array_size", None)
array_size_define = (field.get("array_size_define") or "").strip()
if is_array or bracket is not None:
base_for_size = base if (base and is_array) else (base or ctype)
base_bytes = BASE_SIZES.get(base_for_size, None)
if is_array:
size_str = describe_array_bytes(array_size_meta, base_bytes, base_for_size)
if array_size_define:
if size_str in {"array", "-"}:
size_str = array_size_define
else:
size_str = f"{size_str} ({array_size_define})"
return size_str
if bracket is not None:
if bracket == "":
return "array"
if bracket.isdigit():
n = int(bracket)
return str(n * base_bytes) if base_bytes is not None else str(n)
if base_bytes is None or base == "char":
return bracket
return f"{bracket} * {base_bytes}"
return "array"
base_bytes = BASE_SIZES.get(base, None)
return str(base_bytes) if base_bytes is not None else "-"
# ---- Markdown rendering -----------------------------------------------------
#inav_wiki_url = "https://github.com/xznhj8129/msp_documentation/blob/master/docs/inav_enums_ref.md"
inav_wiki_url = "https://github.com/iNavFlight/inav/wiki/Enums-reference"
def units_cell(field: Dict[str, Any]) -> str:
if "enum" in field:
if field["enum"]=="?_e":
return "[ENUM_NAME](LINK_TO_ENUM)"
else:
return f"[{field['enum']}]({inav_wiki_url}#enum-{field['enum'].lower()})"
u = (field.get("units") or "").strip()
return u if u else "-"
def has_fields(section: Any) -> bool:
if not isinstance(section, dict):
return False
payload = section.get("payload")
return isinstance(payload, list) and len(payload) > 0
def get_fields(section: Any) -> List[Dict[str, Any]]:
if not isinstance(section, dict):
return []
payload = section.get("payload")
return payload if isinstance(payload, list) else []
def flatten_fields_with_repeats(fields: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
"""
Flattens one level of partially repeating payload blocks:
Items with {"repeating": "SOME_SYMBOL", "payload": [...]} are expanded so each child
field gets a symbolic multiplier in the size column.
"""
out: List[Dict[str, Any]] = []
for f in fields:
if isinstance(f, dict) and "repeating" in f and isinstance(f.get("payload"), list):
repeat_sym = str(f["repeating"])
for child in f["payload"]:
if isinstance(child, dict):
c = dict(child)
# Mark repeat multiplier for the size column
c["_repeat_multiplier"] = repeat_sym
out.append(c)
else:
out.append(f)
return out
def table_with_units(fields: List[Dict[str, Any]], label: str) -> str:
flat_fields = flatten_fields_with_repeats(fields)
has_repeats = any(isinstance(f, dict) and f.get("_repeat_multiplier") for f in flat_fields)
has_units = any(
isinstance(f, dict) and (
((f.get("units") or "").strip()) or ("enum" in f)
)
for f in flat_fields
)
# Build dynamic header
cols = ["Field", "C Type"]
if has_repeats:
cols.append("Repeats")
cols.append("Size (Bytes)")
if has_units:
cols.append("Units")
cols.append("Description")
header = " \n**{label}:**\n".format(label=label)
header += "|" + "|".join(cols) + "|\n"
header += "|" + "|".join(["---"] * len(cols)) + "|\n"
# Rows
rows: List[str] = []
for f in flat_fields:
name = f.get("name", "")
size = sizeof_entry(f)
if size == "0":
size = "-"
row_cells = [f"`{name}`", f"`{format_ctype(f)}`"]
if has_repeats:
repeats = f.get("_repeat_multiplier") or "-"
row_cells.append(repeats)
row_cells.append(size)
if has_units:
units = units_cell(f)
row_cells.append(units)
desc = (f.get("desc") or "").strip()
row_cells.append(desc)
rows.append("| " + " | ".join(row_cells) + " |")
return header + "\n".join(rows) + "\n"
def render_variant(parent_name: str, variant_name: str, variant_def: Dict[str, Any]) -> str:
"""
Renders a single variant block (subsection header, description, request/reply tables).
"""
out: List[str] = []
vdesc = (variant_def.get("description") or "").strip()
# GitHub auto-anchors will work off this header text
out.append(f"#### Variant: `{variant_name}`\n\n")
if vdesc:
out.append(f"**Description:** {vdesc} \n")
req = variant_def.get("request", None)
rep = variant_def.get("reply", None)
if has_fields(req):
out.append(table_with_units(get_fields(req), "Request Payload"))
else:
out.append("\n**Request Payload:** **None** \n")
if has_fields(rep):
out.append(table_with_units(get_fields(rep), "Reply Payload"))
else:
out.append("\n**Reply Payload:** **None** \n")
out.append("\n")
return "".join(out)
def render_message(name: str, msg: Dict[str, Any]) -> Tuple[str, str]:
"""
Returns (section_markdown, heading_text_for_anchor)
"""
code = msg.get("code", 0)
hex_str = msg.get("hex", hex(code))
description = (msg.get("description") or "").strip()
notes = (msg.get("notes") or "").strip()
complex_flag = bool(msg.get("complex", False))
heading = f'## <a id="{name.lower()}"></a>`{name} ({code} / {hex_str})`'
out = [heading + "\n"]
if description:
out.append(f"**Description:** {description} \n")
#if complex_flag:
# out.append("**Special case, skipped for now**\n\n")
# return "".join(out), heading
# NEW: variant-aware rendering
variants = msg.get("variants")
if isinstance(variants, dict) and variants:
# For variant messages, render a compact per-variant table set
for vname, vdef in variants.items():
out.append(render_variant(name, vname, vdef))
else:
# Fallback: single request/reply like before
req = msg.get("request", None)
rep = msg.get("reply", None)
if has_fields(req):
out.append(table_with_units(get_fields(req), "Request Payload"))
else:
out.append("\n**Request Payload:** **None** \n")
if has_fields(rep):
out.append(table_with_units(get_fields(rep), "Reply Payload"))
else:
out.append("\n**Reply Payload:** **None** \n")
if notes:
out.append(f"\n**Notes:** {notes}\n")
out.append("\n")
return "".join(out), heading
# ---- Index + strict consistency --------------------------------------------
def build_maps(defs: Dict[str, Any], codes_cls: Type[enum.IntEnum]) -> Tuple[Dict[int, str], Dict[int, str]]:
"""
Returns:
json_by_code: {code -> message_name_from_json}
mw_by_code: {code -> enum_name_from codes_cls}
Only for codes in the enforced ranges (v1 and v2).
"""
v1_range = range(0, 255)
v2_range = range(4096, 20001)
# JSON: build by code (restrict to ranges)
json_by_code: Dict[int, str] = {}
for name, body in defs.items():
code = int(body.get("code", -1))
if code in v1_range or code in v2_range:
json_by_code[code] = name
# MSPCodes: probe the same ranges
mw_by_code: Dict[int, str] = {}
def try_get(code: int) -> Optional[str]:
try:
e = codes_cls(code)
return e.name
except Exception:
return None
for code in list(v1_range) + list(v2_range):
ename = try_get(code)
if ename is not None:
mw_by_code[code] = ename
return json_by_code, mw_by_code
def enforce_strict_match(json_by_code: Dict[int, str], mw_by_code: Dict[int, str]) -> None:
json_codes = set(json_by_code.keys())
mw_codes = set(mw_by_code.keys())
only_in_json = sorted(json_codes - mw_codes)
only_in_mw = sorted(mw_codes - json_codes)
if only_in_json or only_in_mw:
lines = ["MSP code mismatch detected:"]
if only_in_json:
lines.append(" Present in JSON but missing in MSPCodes:")
for c in only_in_json:
lines.append(f" {c}\t{json_by_code[c]}")
if only_in_mw:
lines.append(" Present in MSPCodes but missing in JSON:")
for c in only_in_mw:
lines.append(f" {c}\t{mw_by_code[c]}")
raise SystemExit("\n".join(lines))
def build_index(json_by_code: Dict[int, str]) -> str:
"""
Build a compact index linking to each heading.
"""
v1 = []
v2 = []
for code, name in sorted(json_by_code.items()):
hex_str = hex(code)
item = f"[{code} - {name}](#{name.lower()}) "
if 0 <= code <= 255:
v1.append(item)
elif 4096 <= code <= 20000:
v2.append(item)
parts = ["## Index", "### MSPv1"]
parts.extend(v1)
parts.append("\n### MSPv2")
parts.extend(v2)
parts.append("") # trailing newline
return "\n".join(parts)
# ---- Orchestration ----------------------------------------------------------
def generate_markdown(defs: Dict[str, Any]) -> str:
# Strict maps & check
codes_enum = build_msp_codes_enum(defs)
json_by_code, mw_by_code = build_maps(defs, codes_enum)
enforce_strict_match(json_by_code, mw_by_code)
# Build sections, remembering headings for slugging (already handled in index)
items = sorted(((int(body.get("code", 0)), name, body) for name, body in defs.items()),
key=lambda t: t[0])
sections = []
for _, name, body in items:
sec, _heading = render_message(name, body)
sections.append(sec)
with open("docs_v2_header.md", "r", encoding="utf-8") as f:
header = f.read()
with open("format.md", "r", encoding="utf-8") as f:
fmt = f.read()
with open("msp_messages.checksum", "r", encoding="utf-8") as f:
chksum = f.read().split(' ')[0]
with open("rev", "r", encoding="utf-8") as f:
rev = f.read()
header = header.replace('<format>',fmt)
header = header.replace('<file_rev>',rev)
header = header.replace('<file_hash>',chksum)
index_md = build_index(json_by_code)
return header + "\n" + index_md + "\n" + "".join(sections)
def main():
in_path = Path(sys.argv[1]) if len(sys.argv) >= 2 else Path("msp_messages.json")
out_path = Path(sys.argv[2]) if len(sys.argv) >= 3 else Path("README.md")
with in_path.open("r", encoding="utf-8") as f:
defs = json.load(f)
md = generate_markdown(defs)
out_path.write_text(md, encoding="utf-8")
print(f"Wrote {out_path}")
if __name__ == "__main__":
main()
@@ -0,0 +1,121 @@
#!/usr/bin/env python3
import argparse
import datetime
import re
from pathlib import Path
SUBDIRS = [
'common',
'blackbox',
'navigation',
'sensors',
'programming',
'rx',
'telemetry',
'io',
'flight',
'fc',
'drivers',
]
def strip_comments(text: str) -> str:
text = re.sub(r'/\*.*?\*/', '', text, flags=re.DOTALL) # block comments
text = re.sub(r'//.*', '', text) # line comments
return text
def extract_enums(fn: str, text: str):
src = strip_comments(text)
out = []
# typedef enum { ... } Alias;
i = 0
while True:
m = re.search(r'\btypedef\s+enum\b', src[i:])
if not m: break
start = i + m.start()
lb = src.find('{', i + m.end())
if lb == -1: break
depth = 0
k = lb
while k < len(src):
if src[k] == '{': depth += 1
elif src[k] == '}':
depth -= 1
if depth == 0:
semi = src.find(';', k)
if semi == -1: break
tail = src[k+1:semi]
alias = re.findall(r'\b([A-Za-z_]\w*)\b', tail)
if alias:
block = src[start:semi+1].strip()
out += [f'// {fn}\n', block + '\n\n']
i = semi + 1
break
k += 1
else:
break
# enum Tag { ... }; → also emit typedef enum Tag Tag;
i = 0
while True:
m = re.search(r'\benum\s+([A-Za-z_]\w*)\s*{', src[i:])
if not m: break
start = i + m.start()
tag = m.group(1)
lb = src.find('{', i + m.end() - 1)
if lb == -1: break
depth = 0
k = lb
while k < len(src):
if src[k] == '{': depth += 1
elif src[k] == '}':
depth -= 1
if depth == 0:
semi = src.find(';', k)
if semi == -1: break
block = src[start:semi+1].strip()
out += [
f'// {fn}\n',
block + '\n',
f'typedef enum {tag} {tag};\n\n'
]
i = semi + 1
break
k += 1
else:
break
return out
all_enums = []
def parse_args():
parser = argparse.ArgumentParser(description='Collect all enums from INAV sources.')
parser.add_argument(
'--inav-root',
default='../inav/src/main',
help="Path to the INAV 'src/main' directory (default: %(default)s)",
)
return parser.parse_args()
args = parse_args()
base_dir = Path(args.inav_root).expanduser()
for sd in SUBDIRS:
root = base_dir / sd
if not root.is_dir():
continue
for fn in root.rglob('*'):
print(fn)
if fn.suffix in ('.c', '.h'):
txt = fn.read_text(errors='ignore')
ret = extract_enums(fn, txt)
if ret: print(fn)
all_enums.extend(ret)
with open('all_enums.h', 'w') as out:
out.write(f"// Consolidated enums — generated on {datetime.datetime.now()}\n\n")
out.writelines(all_enums)
print(f"Found {len(all_enums)} enums. Wrote all_enums.h.")
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1 @@
c9458e9a712b7a4f3bc9333aa7bc3dcb
File diff suppressed because it is too large Load Diff
+1
View File
@@ -0,0 +1 @@
5
+257
View File
@@ -0,0 +1,257 @@
# Parameter Groups System
## Overview
Parameter Groups (PG) is INAV's system for managing persistent configuration settings. Each configuration struct is registered with a unique ID and version number, stored in EEPROM/flash, and automatically validated on boot.
## Quick Reference
**When modifying a parameter group struct:**
- ✅ **Increment version** - Safest option, always works
- ⚠️ **Don't increment** - Only safe for comments or non-structural changes
**Key files:**
- `src/main/config/parameter_group.h` - Macros and registry structure
- `src/main/config/parameter_group.c` - Runtime load/store functions
- `src/main/config/parameter_group_ids.h` - PGN definitions
- `src/main/fc/settings.yaml` - Maps CLI settings to PG fields
## How It Works
### Registration
Parameter groups use linker sections to build a compile-time registry:
```c
// In yourfeature.h
typedef struct yourFeatureConfig_s {
uint8_t enabled;
uint16_t rate;
} yourFeatureConfig_t;
PG_DECLARE(yourFeatureConfig_t, yourFeatureConfig);
// In yourfeature.c
PG_REGISTER_WITH_RESET_TEMPLATE(
yourFeatureConfig_t,
yourFeatureConfig,
PG_YOUR_FEATURE_CONFIG, // Unique ID from parameter_group_ids.h
0 // Version number
);
PG_RESET_TEMPLATE(yourFeatureConfig_t, yourFeatureConfig,
.enabled = 1,
.rate = 100
);
```
### Version Checking
On boot, `pgLoad()` validates versions before loading settings:
```c
void pgLoad(const pgRegistry_t* reg, int profileIndex,
const void *from, int size, int version)
{
pgReset(reg, profileIndex); // Always reset to defaults first
if (version == pgVersion(reg)) {
// Only copy if versions match
const int take = MIN(size, pgSize(reg));
memcpy(pgOffset(reg, profileIndex), from, take);
}
// If version mismatch: defaults remain, no corruption
}
```
**Key behavior:** Version mismatch causes settings to remain at defaults. This prevents corruption when struct layout changes.
## Version Management
### When to Increment Version
Version numbers are stored in the top 4 bits of the PGN field (valid range: 0-15).
**The safe rule:** When modifying a parameter group struct, increment the version.
**Changes requiring version increment:**
- Removing a field
- Changing field type or size
- Reordering fields
- Adding packing attributes
**Example - Removing a field:**
```c
// Before: version 4
typedef struct blackboxConfig_s {
uint16_t rate_num;
uint16_t rate_denom;
uint8_t device;
uint8_t invertedCardDetection; // Removing this
uint32_t includeFlags;
int8_t arm_control;
} blackboxConfig_t;
PG_REGISTER_WITH_RESET_TEMPLATE(blackboxConfig_t, blackboxConfig, PG_BLACKBOX_CONFIG, 4);
// After: version 5 (MUST increment!)
typedef struct blackboxConfig_s {
uint16_t rate_num;
uint16_t rate_denom;
uint8_t device;
// invertedCardDetection removed - field offsets changed!
uint32_t includeFlags; // Now at different offset
int8_t arm_control; // Now at different offset
} blackboxConfig_t;
PG_REGISTER_WITH_RESET_TEMPLATE(blackboxConfig_t, blackboxConfig, PG_BLACKBOX_CONFIG, 5);
```
**Why:** Without version increment, `pgLoad()` sees matching versions (4==4) and copies old data, but field offsets have changed. Result: `includeFlags` reads wrong bytes from EEPROM, causing corruption.
With version increment (4→5), `pgLoad()` detects mismatch and keeps defaults instead.
### Changes NOT Requiring Version Increment
- Comments only
- Default values in `PG_RESET_TEMPLATE` (doesn't affect EEPROM layout)
- settings.yaml entries (CLI mapping, not struct layout)
### Technical Note on Appending
The `pgLoad()` function uses `MIN(size, pgSize(reg))` when copying data. This means:
- If new firmware has larger struct, it copies available bytes and keeps defaults for new fields
- If new firmware has smaller struct, it copies only what fits
This behavior theoretically allows appending fields at the end without version increment. However, **the recommended practice is to always increment** unless you have specific reason not to and understand the implications.
## Complete Example
### yourfeature.h
```c
#pragma once
#include <stdint.h>
#include "config/parameter_group.h"
typedef struct yourFeatureConfig_s {
uint8_t enabled;
uint16_t updateRate;
uint32_t flags;
} yourFeatureConfig_t;
PG_DECLARE(yourFeatureConfig_t, yourFeatureConfig);
```
### yourfeature.c
```c
#include "yourfeature.h"
#include "config/parameter_group_ids.h"
PG_REGISTER_WITH_RESET_TEMPLATE(
yourFeatureConfig_t,
yourFeatureConfig,
PG_YOUR_FEATURE_CONFIG,
0
);
PG_RESET_TEMPLATE(yourFeatureConfig_t, yourFeatureConfig,
.enabled = 1,
.updateRate = 100,
.flags = 0
);
void yourFeatureInit(void)
{
if (!yourFeatureConfig()->enabled) {
return;
}
// Use configuration...
}
```
### parameter_group_ids.h
```c
typedef enum {
// ... existing entries ...
PG_YOUR_FEATURE_CONFIG = 150, // Choose unused ID
// ...
} pgn_e;
```
### settings.yaml
```yaml
- name: your_feature_enable
description: "Enable yourFeature"
field: yourFeatureConfig.enabled
type: uint8_t
min: 0
max: 1
default_value: 1
- name: your_feature_rate
description: "Update rate in Hz"
field: yourFeatureConfig.updateRate
type: uint16_t
min: 1
max: 1000
default_value: 100
```
## Accessing Configuration
### Read-only (preferred)
```c
if (yourFeatureConfig()->enabled) {
processAtRate(yourFeatureConfig()->updateRate);
}
```
### Mutable (use sparingly)
```c
yourFeatureConfigMutable()->updateRate = newRate;
```
## Registration Macros
| Macro | Use |
|-------|-----|
| `PG_DECLARE(type, name)` | Declare system PG in header |
| `PG_DECLARE_PROFILE(type, name)` | Declare profile PG in header |
| `PG_REGISTER_WITH_RESET_TEMPLATE(...)` | Register with template defaults |
| `PG_REGISTER_WITH_RESET_FN(...)` | Register with custom reset function |
| `PG_RESET_TEMPLATE(type, name, ...)` | Define default values |
## Common Patterns
### Boolean Fields
```c
uint8_t enabled; // Use uint8_t, not bool (EEPROM needs fixed sizes)
```
### Enums
```c
typedef enum { MODE_A = 0, MODE_B = 1 } mode_e;
uint8_t mode; // Store enum as uint8_t
```
### Bitfields
```c
#define FLAG_A (1 << 0)
#define FLAG_B (1 << 1)
uint32_t flags;
```
## Testing Version Changes
When incrementing a version:
1. Flash old firmware, configure non-default settings, save
2. Flash new firmware with incremented version
3. Verify settings reset to defaults (not corrupted)
4. Check for errors in CLI
## See Also
- `src/main/config/parameter_group.h` - Full macro definitions
- `src/main/config/parameter_group.c` - Implementation
- Betaflight PG documentation: https://betaflight.com/docs/development/ParameterGroups
+599
View File
@@ -0,0 +1,599 @@
# Creating INAV Releases
This document describes the process for creating INAV firmware and configurator releases.
> **Note:** This document is designed to be used with coding assistants (such as Claude Code) that can execute the commands and automate parts of the release process. Update this document with lessons learned after each release. Sensei has written more detailed guides for his process in the third-party repo https://github.com/sensei-hacker/inav-claude/tree/master/claude/release-manager
## CRITICAL PRINCIPLE: Verify Builds BEFORE Creating Tags
**Never tag a commit that hasn't been fully tested successfully.**
Order of operations:
1. Merge all firmware PRs to the release branch
2. **Ensure release branch is in nightly-build.yml** (add via PR if not)
3. **Push to release branch to trigger nightly build** (merge the workflow PR, or push trivial commit)
4. Wait for nightly build to complete, verify ALL jobs passed
5. **Download firmware artifacts from inav-nightly** (includes SITL binaries needed for configurator)
6. Update SITL binaries in configurator repo, wait for CI, merge
7. Download configurator artifacts after SITL update merged
8. Verify all artifacts (automated checks)
9. **Manual testing on Linux and Windows** (required before tagging)
10. **Only then** create tags pointing to the verified commits
If CI fails or any verification fails, fix the issue first. Do not tag broken commits.
**Why this matters:** If you tag first and then discover the build is broken, you have a tag pointing to a broken commit. By verifying artifacts first, you only tag commits that are proven to work.
## CRITICAL: CI Runs on PR Creation, Not Merge
**GitHub Actions CI runs when a PR is created/updated, not when it's merged.**
This means:
- Each PR's CI artifacts only include changes from that PR's branch
- After merging multiple PRs, no single CI run contains all the merged changes
- **The nightly-build workflow must include the release branch to get complete artifacts**
### How Nightly Builds Work
The `nightly-build.yml` workflow triggers on push to specific branches and uploads complete artifacts (hex files + SITL) to the `inav-nightly` repository.
**Ensure the release branch is in the workflow triggers:**
Check `.github/workflows/nightly-build.yml`:
```yaml
on:
push:
branches:
- master
- maintenance-8.x.x
- maintenance-9.x # Add new maintenance branches here!
```
If the maintenance branch is not listed, create a PR to add it.
### Getting Complete Firmware Artifacts
After all PRs are merged to the release branch:
1. **Verify the branch is in nightly-build.yml triggers** (or add it)
2. **Push any commit to the release branch** to trigger the nightly build
- This can be a trivial change (whitespace, comment) if needed
3. **Wait for the nightly build to complete**
4. **Download from inav-nightly releases:**
```bash
gh release list --repo iNavFlight/inav-nightly --limit 5
gh release download <tag> --repo iNavFlight/inav-nightly
```
Only artifacts from the nightly build contain all merged changes.
## Overview
INAV releases include both firmware (for flight controllers) and the configurator application (for configuration). Both repositories must be tagged with matching version numbers.
**Repositories:**
- Firmware: https://github.com/iNavFlight/inav
- Configurator: https://github.com/iNavFlight/inav-configurator
## Version Numbering
INAV uses semantic versioning: `MAJOR.MINOR.PATCH`
- **MAJOR:** Breaking changes, major new features
- **MINOR:** New features, significant improvements
- **PATCH:** Bug fixes, minor improvements
Version numbers are set in:
- Firmware: in `CMakeLists.txt` via `project(INAV VERSION X.Y.Z)`
Verify/update:
- View: `grep -E 'project\\(INAV VERSION' CMakeLists.txt`
- Update: edit `CMakeLists.txt` to set the desired version
- Configurator: in `package.json` field `"version"`
Verify/update:
- View: `jq -r .version package.json` (or `node -p "require('./package.json').version"`)
- Update: `npm version <X.Y.Z> --no-git-tag-version`
## Version String Format (RC Releases)
**CRITICAL:** Establish the canonical version string before starting any release work.
RC version strings must use **lowercase `rc`** joined to the version with a **hyphen**:
| Correct | Wrong |
|---------|-------|
| `9.1.0-rc1` | `9.1.0-RC1` |
| `9.1.0-rc2` | `9.1.0_RC2` |
| `9.0.0-rc3` | `9.0.0-rc_3` |
The Configurator firmware flasher uses a case-sensitive regex to parse firmware filenames. Uppercase `RC` or underscore separators cause the target board name to be misread, making the firmware invisible in the flasher even after a successful release upload.
---
## Pre-Release Checklist
### Code Readiness
- [ ] All planned PRs merged
- [ ] CI passing on target branch
- [ ] No critical open issues blocking release
- [ ] Version numbers updated in both repositories
- [ ] SITL binaries updated in configurator
- [ ] **PG struct validation passed** (see [PG Validation](#pg-parameter-group-validation))
### Documentation
- [ ] Release notes drafted
- [ ] Breaking changes documented
- [ ] New features documented
- [ ] **Configurator migration profile created** for major version bumps (see [Backup Restore Architecture](Backup%20Restore%20Architecture.md#adding-a-new-migration-profile))
## Release Workflow
**IMPORTANT:** Verify builds BEFORE creating tags. See "CRITICAL PRINCIPLE" section above.
```
1. Verify firmware release readiness
├── All PRs merged to firmware repo
├── Version numbers updated
├── CI passing on firmware target commit
└── PG struct validation passed
2. Download firmware artifacts FIRST
├── Download firmware hex files from CI
├── Download SITL binaries from same CI run
├── Build Linux x64 SITL locally if needed (for glibc ≤2.35 compatibility)
└── This provides SITL binaries needed for configurator
3. Update SITL in configurator
├── Create PR with SITL binaries from step 2
├── Wait for configurator CI to pass
└── Merge SITL update PR
4. Download and verify configurator artifacts
├── Download from CI run after SITL PR merged
├── Verify macOS DMGs (no cross-platform contamination)
├── Verify Windows SITL (cygwin1.dll present)
├── Verify Linux SITL (glibc <= 2.35 for Ubuntu 22.04 compatibility)
└── Automated SITL verification (glibc check, binary runs)
5. Manual testing (REQUIRED before creating tags)
├── Test configurator + SITL on Linux
├── Test configurator + SITL on Windows
├── Test configurator + SITL on macOS (if available)
└── Verify basic functionality works on each platform
6. Generate changelog
├── List PRs since last tag
├── Categorize changes
└── Format release notes
7. Create tags and draft releases (ONLY after manual testing passed)
├── Create tag + draft release for firmware (targeting verified commit)
├── Create tag + draft release for configurator (targeting verified commit)
├── Upload verified artifacts
└── Add release notes
8. Review and publish
├── Final review of draft releases
├── Maintainer approval
└── Publish releases
```
## Updating SITL Binaries
SITL binaries must be updated in the configurator repository before release. They are stored in:
```
inav-configurator/resources/public/sitl/
├── linux/
│ ├── inav_SITL
│ └── arm64/inav_SITL
├── macos/
│ └── inav_SITL
└── windows/
├── inav_SITL.exe
└── cygwin1.dll
```
### Download from Nightly
```bash
# Find matching nightly release
gh release list --repo iNavFlight/inav-nightly --limit 5
# Download SITL resources
curl -L -o /tmp/sitl-resources.zip \
"https://github.com/iNavFlight/inav-nightly/releases/download/<tag>/sitl-resources.zip"
unzip /tmp/sitl-resources.zip -d /tmp/sitl-extract
# Copy to configurator
cd inav-configurator
cp /tmp/sitl-extract/resources/sitl/linux/inav_SITL resources/public/sitl/linux/
cp /tmp/sitl-extract/resources/sitl/linux/arm64/inav_SITL resources/public/sitl/linux/arm64/
cp /tmp/sitl-extract/resources/sitl/macos/inav_SITL resources/public/sitl/macos/
cp /tmp/sitl-extract/resources/sitl/windows/inav_SITL.exe resources/public/sitl/windows/
# Commit
git add resources/public/sitl/
git commit -m "Update SITL binaries for <version>"
```
### Building SITL Locally (Recommended for Linux x64)
**IMPORTANT:** The CI-built Linux x64 SITL binary may require a newer glibc version than Ubuntu 22.04 LTS provides. To ensure compatibility with all supported Ubuntu LTS releases, build the Linux x64 SITL binary locally on Ubuntu 22.04 (glibc 2.35).
```bash
cd inav
mkdir -p build_sitl
cd build_sitl
cmake -DSITL=ON ..
make -j$(nproc)
```
The binary will be at: `build_sitl/bin/SITL.elf`
Verify the glibc requirement:
```bash
objdump -T build_sitl/bin/SITL.elf | grep GLIBC | sed 's/.*GLIBC_//;s/ .*//' | sort -V | tail -1
# Should output 2.35 or lower
```
**When to build locally vs use CI artifacts:**
- **Build locally:** Linux x64 (to ensure glibc ≤ 2.35 compatibility)
- **Use CI artifacts:** Windows (includes cygwin1.dll), macOS, Linux arm64
## Verifying SITL in Packaged Builds
After downloading configurator artifacts, verify SITL files are correctly included.
### Windows SITL Verification
**CRITICAL:** Windows SITL requires `cygwin1.dll` to run. Without it, users get "cygwin1.dll not found" errors.
```bash
# Check Windows zip contains both required files
# Note: Packaged builds use resources/sitl/ (not resources/public/sitl/)
unzip -l INAV-Configurator_win_x64_9.0.0.zip | grep -E "(cygwin1.dll|inav_SITL.exe)"
# Expected output (both files must be present):
# 2953269 12-19-2024 01:41 resources/sitl/windows/cygwin1.dll
# 1517041 12-21-2024 17:25 resources/sitl/windows/inav_SITL.exe
```
If `cygwin1.dll` is missing: **DO NOT release** - Windows SITL will be broken.
### Linux SITL glibc Verification
**CRITICAL:** Linux SITL binaries must be compiled with glibc old enough to support all non-EOL Ubuntu LTS releases.
| Period | Oldest Supported Ubuntu LTS | Required glibc |
|--------|----------------------------|----------------|
| 2025-2027 | Ubuntu 22.04.3 LTS | <= 2.35 |
```bash
# Check glibc version requirement (should output 2.35 or lower)
objdump -T inav_SITL | grep GLIBC | sed 's/.*GLIBC_//;s/ .*//' | sort -V | tail -1
```
If glibc > 2.35, the binary will fail on Ubuntu 22.04 with:
```
/lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_2.38' not found
```
### Path Differences
| Context | SITL Path |
|---------|-----------|
| Source repo | `resources/public/sitl/` |
| Packaged builds | `resources/sitl/` |
The `extraResource` config in `forge.config.js` copies `resources/public/sitl` to `resources/sitl` in packaged builds.
## Tagging and Publishing
**IMPORTANT:** Tags should only be created AFTER testing artifacts and confirming the release is ready to publish.
### Check Latest Tags
```bash
# Firmware
cd inav
git fetch --tags
git tag --sort=-v:refname | head -10
# Configurator
cd inav-configurator
git fetch --tags
git tag --sort=-v:refname | head -10
```
### Create and Push Tags (Final Step Before Publishing)
Only create tags after artifacts are tested and draft release is reviewed:
```bash
# Firmware
cd inav
git pull
git tag -a <version> -m "INAV <version>"
git push origin <version>
# Configurator
cd inav-configurator
git pull
git tag -a <version> -m "INAV Configurator <version>"
git push origin <version>
```
## Changelog Generation
### List PRs Since Last Tag
```bash
cd inav
LAST_TAG=$(git describe --tags --abbrev=0)
gh pr list --state merged --search "merged:>=$(git log -1 --format=%ai $LAST_TAG | cut -d' ' -f1)" --limit 100
```
### Verify Each PR Is on the Correct Branch
**Before including a PR in release notes**, confirm it is actually merged into the release branch, not a future branch. `gh pr list` shows PRs by merge date regardless of target branch — a PR merged to `maintenance-10.x` will appear even though it's not in the current release.
```bash
# Confirm a PR's merge commit exists on the release branch
git log upstream/maintenance-9.x --oneline | grep <short-sha>
# Or check all recent merge commits on the branch
git log upstream/maintenance-9.x --oneline --merges | head -30
```
If a PR is not in that output, exclude it from the release notes.
### Using git log
```bash
LAST_TAG=$(git describe --tags --abbrev=0)
git log $LAST_TAG..HEAD --oneline --merges
```
### Changelog Format
```markdown
## INAV <version> Release Notes
### Firmware Changes
#### New Features
- PR #1234: Description (@contributor)
#### Bug Fixes
- PR #1236: Description (@contributor)
#### Improvements
- PR #1237: Description (@contributor)
### Configurator Changes
#### New Features
- PR #100: Description (@contributor)
### Full Changelog
**Firmware:** https://github.com/iNavFlight/inav/compare/<prev-tag>...<new-tag>
**Configurator:** https://github.com/iNavFlight/inav-configurator/compare/<prev-tag>...<new-tag>
```
## PG (Parameter Group) Validation
**Run before creating tags to prevent EEPROM corruption bugs:**
```bash
cd inav
./cmake/validate-pg-for-release.sh
```
THis builds one target and checks that the parameter group structs haven't been changed without updating their version numbers.
**✅ Pass:** Proceed with release
**❌ Fail:** Create hotfix PR to increment PG version in affected struct's `PG_REGISTER` macro, then re-run
## Downloading Release Artifacts
### Firmware Hex Files
Firmware is available from the nightly build system:
```bash
# List recent nightlies
gh release list --repo iNavFlight/inav-nightly --limit 5
# Download hex files
gh release download <nightly-tag> --repo iNavFlight/inav-nightly --pattern "*.hex"
```
#### Building Firmware Locally (if needed)
**⚠️ Important:** Always use Release mode when building firmware for releases to save disk space:
```bash
cd inav
mkdir build-release
cd build-release
cmake -DCMAKE_BUILD_TYPE=Release ..
# Build all official release targets
make release
# Or build specific targets
make MATEKF405 MATEKF722
```
**Disk usage:** Release mode uses ~4-6 GB vs ~109 GB for default RelWithDebInfo mode (96% reduction). The debug symbols are stripped from final `.hex` files anyway, so Release mode produces identical output.
#### Renaming Firmware Files
Remove CI suffix and add RC number for RC releases:
```bash
RC_NUM="rc2" # Empty for final releases
# Check if any .hex files exist to avoid errors with the glob
if compgen -G "*.hex" > /dev/null; then
for f in *.hex; do
target=$(echo "$f" | sed -E 's/inav_[0-9]+\.[0-9]+\.[0-9]+_(.*)_ci-.*/\1/')
version=$(echo "$f" | sed -E 's/inav_([0-9]+\.[0-9]+\.[0-9]+)_.*/\1/')
if [ -n "$RC_NUM" ]; then
mv "$f" "inav_${version}-${RC_NUM}_${target}.hex"
else
mv "$f" "inav_${version}_${target}.hex"
fi
done
else
echo "No .hex files found to rename."
fi
```
### Configurator Builds
Download from GitHub Actions CI:
```bash
# List recent workflow runs
gh run list --repo iNavFlight/inav-configurator --limit 10
# Download artifacts (creates one subdirectory per platform artifact)
gh run download <run-id> --repo iNavFlight/inav-configurator
# CRITICAL: Organize by platform — NEVER flatten all files into one directory.
# Flattening can put Windows .exe files inside macOS DMGs (caused a 9.0.0 release incident).
mkdir -p linux/ macos/ windows/
mv INAV-Configurator_linux_*/* linux/
mv INAV-Configurator_macOS*/* macos/
mv INAV-Configurator_win_*/* windows/
rmdir INAV-Configurator_*
```
## Creating GitHub Releases
### Create Draft Release
For RC releases, add `--prerelease` so GitHub marks them as pre-release and they don't appear as the latest stable release. Use `--target <commit-sha>` to tag a specific commit (safer than tagging the current HEAD, and works even when the local repo is locked).
```bash
# Firmware (RC release)
gh release create 9.1.0-rc1 \
--repo iNavFlight/inav \
--target <commit-sha> \
--title "INAV 9.1.0-rc1 release candidate for testing" \
--notes-file release-notes.md \
--prerelease \
--draft
gh release upload 9.1.0-rc1 firmware-dir/*.hex --repo iNavFlight/inav
# Configurator (RC release)
gh release create 9.1.0-rc1 \
--repo iNavFlight/inav-configurator \
--target <commit-sha> \
--title "INAV Configurator 9.1.0-rc1 release candidate for testing" \
--notes-file release-notes.md \
--prerelease \
--draft
gh release upload 9.1.0-rc1 linux/* macos/* windows/* --repo iNavFlight/inav-configurator
# Final releases: same commands, omit --prerelease
```
### Managing Release Assets
#### Rename Assets via API
```bash
# Get release and asset IDs
gh api repos/iNavFlight/inav/releases --jq '.[] | select(.draft == true) | {id: .id, name: .name}'
gh api repos/iNavFlight/inav/releases/RELEASE_ID/assets --paginate --jq '.[] | "\(.id) \(.name)"'
# Rename an asset
gh api -X PATCH "repos/iNavFlight/inav/releases/assets/ASSET_ID" -f name="new-filename.hex"
```
#### Delete Outdated Assets from Draft Release
If a draft release has outdated assets that need to be replaced (e.g., from a previous upload attempt), delete them before uploading new ones:
```bash
gh api -X DELETE "repos/iNavFlight/inav/releases/assets/ASSET_ID"
```
### Publish Release
**Publish firmware first, then verify the Configurator can see it before publishing the Configurator release.**
```bash
# Step 1: Publish firmware release
gh release edit <version> --repo iNavFlight/inav --draft=false
```
**Step 2: Verify firmware appears in Configurator Firmware Flasher (human step)**
Open INAV Configurator → Firmware Flasher tab → enable "Show unstable releases". The new firmware version must appear in the release list. This confirms the GitHub release is properly formatted and the filename regex parsed correctly.
Also select a target whose name contains spaces (e.g., `MAMBAH743 2022B GYRO2`) and confirm it displays with spaces, not underscores — this validates that multi-word target names parsed correctly.
If the firmware does not appear: check that filenames follow `inav_<version>-rc<n>_<TARGET>.hex` exactly (lowercase `rc`, hyphen separator). See [Asset Naming Conventions](#asset-naming-conventions).
```bash
# Step 3: Publish configurator release (only after firmware verified in flasher)
gh release edit <version> --repo iNavFlight/inav-configurator --draft=false
```
## Asset Naming Conventions
**Firmware (RC releases):** `inav_<version>-rc<n>_<TARGET>.hex`
**Firmware (final):** `inav_<version>_<TARGET>.hex`
**Configurator (RC releases):** `INAV-Configurator_<platform>_<version>-rc<n>.<ext>`
**Configurator (final):** `INAV-Configurator_<platform>_<version>.<ext>`
## Maintenance Branches
When releasing a new major version, create maintenance branches:
- **maintenance-X.x** - For bugfixes to version X
- **maintenance-(X+1).x** - For breaking changes targeting the next major version
### Creating Maintenance Branches
```bash
COMMIT_SHA="<full-40-char-sha>"
# inav
gh api repos/iNavFlight/inav/git/refs -f ref="refs/heads/maintenance-9.x" -f sha="$COMMIT_SHA"
# inav-configurator
gh api repos/iNavFlight/inav-configurator/git/refs -f ref="refs/heads/maintenance-9.x" -f sha="$COMMIT_SHA"
```
### Branch Usage
- **Changes maintaining backward compatibility** → PR to maintenance-X.x (e.g., maintenance-9.x)
- **Breaking changes** (MSP protocol, settings structure) → PR to maintenance-(X+1).x (e.g., maintenance-10.x)
- When breaking changes affect CLI settings (renames, removals, value changes), a **Configurator migration profile** must be created. See [Backup Restore Architecture](Backup%20Restore%20Architecture.md#adding-a-new-migration-profile)
- **Master** → NOT a PR target (receives merges only)
Lower version branches are periodically merged into higher version branches (e.g., maintenance-9.x → master → maintenance-10.x).
## Hotfix Releases
For critical bugs discovered after release:
1. Create hotfix branch from release tag
2. Cherry-pick or create fix
3. Tag as `X.Y.Z+1` (patch increment)
4. Build and release following normal process
5. Document as hotfix in release notes
## Post-Release Tasks
- [ ] Announce release (Discord, forums, etc.)
- [ ] Update any pinned issues
- [ ] Monitor for critical bug reports
- [ ] Prepare hotfix if needed
- [ ] Update this document with any lessons learned
+152
View File
@@ -0,0 +1,152 @@
# Serial printf style debugging
## Overview
INAV offers a function to use serial `printf` style debugging.
This provides a simple and intuitive debugging facility.
This facility is only available after the serial sub-system has been initialised, but logs generated prior to serial
initialization can be obtained via the `bootlog` functionality.
In order to use this feature, the source file must include `common/log.h`.
## CLI settings
It is necessary to set a serial port for serial logging using the function mask `FUNCTION_LOG`, 32768. For convenience this may be shared with MSP (mask 1), but no other function.
For example, on a VCP port.
```
serial 20 32769 115200 115200 0 115200
```
If the port is shared, it will be reused with extant baud rate settings; if the port is not shared it is opened at 921600 baud.
There are two run time settings that control the verbosity, the most verbose settings being:
```
log_level = DEBUG
Allowed values: ERROR, WARNING, INFO, VERBOSE, DEBUG
log_topics = 0
Allowed range: 0 - 4294967295
```
The use of level and topics is described in the following sections.
## LOG LEVELS
Log levels are defined in `src/main/common/log.h`, at the time of writing these include (in ascending order):
* LOG_LEVEL_ERROR
* LOG_LEVEL_WARNING
* LOG_LEVEL_INFO
* LOG_LEVEL_VERBOSE
* LOG_LEVEL_DEBUG
These are used at both compile time and run time.
At compile time, a maximum level may be defined. As of INAV 2.3, for F3 targets the maximum level is ERROR, for F4/F7 the maximum level is DEBUG.
At run time, the level defines the level that will be displayed, so for a F4 or F7 target that has compile time suport for all log levels, if the CLI sets
```
log_level = INFO
```
then only `ERROR`, `WARNING` and `INFO` levels will be output.
## Log Topic
Log topics are defined in `src/main/common/log.h`, at the time of writing:
* LOG_TOPIC_SYSTEM
* LOG_TOPIC_GYRO
* LOG_TOPIC_BARO
* LOG_TOPIC_PITOT
* LOG_TOPIC_PWM
* LOG_TOPIC_TIMER
* LOG_TOPIC_IMU
* LOG_TOPIC_TEMPERATURE
* LOG_TOPIC_POS_ESTIMATOR
* LOG_TOPIC_VTX
* LOG_TOPIC_OSD
Topics are stored as masks (SYSTEM=1 ... OSD=1024) and may be used to unconditionally display log messages.
If the CLI `log_topics` is non-zero, then all topics matching the mask will be displayed regardless of `log_level`. Setting `log_topics` to 4294967295 (all bits set) will display all log messages regardless of run time level (but still constrained by compile time settings), so F3 will still only display ERROR level messages.
## Code usage
A set of macros `LOG_ERROR()` (log error) through `LOG_DEBUG()` (log debug) may be used, subject to compile time log level constraints. These provide `printf` style logging for a given topic.
Note that the `topic` is specified without the `LOG_TOPIC_` prefix:
```
// LOG_DEBUG(topic, fmt, ...)
LOG_DEBUG(SYSTEM, "This is %s topic debug message, value %d", "system", 42);
```
It is also possible to dump a hex representation of arbitrary data, using functions named variously `LOG_BUFFER_` (`ERROR`) and `LOG_BUF_` (anything else, alas) e.g.:
```
// LOG_BUFFER_ERROR(topic, buf, size)
// LOG_BUF_DEBUG(topic, buf, size)
struct {...} tstruct;
...
LOG_BUF_DEBUG(TEMPERATURE, &tstruct, sizeof(tstruct));
```
## Output Support
Log messages are transmitted through the `FUNCTION_LOG` serial port as MSP messages (`MSP_DEBUGMSG`). It is possible to use any serial terminal to display these messages, however it is advisable to use an application that understands `MSP_DEBUGMSG` in order to maintain readability (in a raw serial terminal the MSP message envelope may result in the display of strange characters). `MSP_DEBUGMSG` aware applications include:
* [dbg-tool](https://codeberg.org/stronnag/dbg-tool)
* [INAV Configurator](https://github.com/iNavFlight/inav-configurator)
* [mwp](https://github.com/stronnag/mwptools)
In addtion:
* [msp-tool](https://github.com/fiam/msp-tool) is obsolete and has limited OS support.
For example, with the final lines of `src/main/fc/fc_init.c` set to:
```
LOG_ERROR(SYSTEM, "Init is complete");
systemState |= SYSTEM_STATE_READY;
```
and the following CLI settings:
```
serial 20 32769 115200 115200 0 115200
set log_level = DEBUG
set log_topics = 4294967295
```
The output will be formatted as follows:
```
# dbg-tool
[dbg-tool] 12:46:49.909079 DBG: [ 3.967] Init is complete
# mwp (stderr log file)
2020-02-02T19:09:02+0000 DEBUG:[ 3.968] Init is complete
# msp-tool
[DEBUG] [ 3.967] Init is complete
```
For the Configurator, debug messages are shown in the developer console log.
Note: The numeric value in square brackets is the FC uptime in seconds.
To see printf-style log messages generated prior serial initialization, reserve about 2KB RAM to buffer the
log) by defining USE_BOOTLOG:
#define USE_BOOTLOG 2048
Then `make clean` and `make`.
Then in the CLI you can run `bootlog` to see the buffered log.
Note bootlog also requires that a serial port be defined for serial debugging.
@@ -0,0 +1,70 @@
# XML Mission File Definition
## Overview
Historically, mission planners interoperating with INAV (and multiwii) missions have used the XML mission file format defined by EOSBandi for MultiWii 2.3 (2013).
The format is defined the XSD schema here.
* Lower case tags are preferred by INAV. Older tools may prefer uppercase (the original MW usage).
* For INAV 4.0 and later, the `missionitem/flags` attribute is required for "fly-by home" waypoints and multi-mission files.
* For INAV 4.0 and later, multi-mission files; mission segments are delimited by a flag value of `165` (the MSP protocol 'last WP' value).
* For multi-mission files, the waypoints may be numbered either sequentially across the whole file, or "reset-numbering" within each mission segment. The latter may (or not) be considered to be more "human readable", particularly where `JUMP` is used.
* The `mwp` tag was introduced by the eponymous mission planner. Other mission planners may consider that reusing some of the tags (`cx`, `cy` - centre location, `zoom` TMS zoom level, `home-x`, `home-y` - home location) is useful.
* `meta` may be used as a synonym for `mwp`.
* The `version` tag may be intepreted by mission planners as they see fit. For example, the (obsolete) Android 'ez-gui' application requires '2.3-pre8'. For multi-mission files it is recommended to use another `version`.
* The `mwp` / `meta` element may be interleaved with `missionitem` in a multi-mission file to provide mission segment specific home, centre locations and zoom.
* The `fwapproach` element defines INAV 7.1.0 and later Autoland parameters for the mission.
## Validation
You can check that your files validate using the open source `xmlint` tool.
```
xmllint --schema docs/development/wp_mission_schema/mw-mission.xsd test.mission --noout
```
## Examples
### Multi-mission file with sequential numbering
```
<?xml version="1.0" encoding="UTF-8"?>
<mission>
<version value="2.3-pre8"></version>
<mwp zoom="14" cx="-3.2632398333333335" cy="54.570950466666666" home-x="0" home-y="0" save-date="2021-11-12T14:07:03Z" generator="impload"></mwp>
<missionitem no="1" action="WAYPOINT" lat="54.5722109" lon="-3.2869291" alt="50" parameter1="0" parameter2="0" parameter3="0"></missionitem>
<missionitem no="2" action="WAYPOINT" lat="54.5708178" lon="-3.2642698" alt="50" parameter1="0" parameter2="0" parameter3="0"></missionitem>
<missionitem no="3" action="WAYPOINT" lat="54.5698227" lon="-3.2385206" alt="50" parameter1="0" parameter2="0" parameter3="0" flag="165"></missionitem>
<missionitem no="4" action="WAYPOINT" lat="54.5599696" lon="-3.2958555" alt="50" parameter1="0" parameter2="0" parameter3="0"></missionitem>
<missionitem no="5" action="WAYPOINT" lat="54.5537978" lon="-3.2958555" alt="50" parameter1="0" parameter2="0" parameter3="0"></missionitem>
<missionitem no="6" action="WAYPOINT" lat="54.5547933" lon="-3.2864141" alt="50" parameter1="0" parameter2="0" parameter3="0"></missionitem>
<missionitem no="7" action="WAYPOINT" lat="54.5597705" lon="-3.2695913" alt="50" parameter1="0" parameter2="0" parameter3="0"></missionitem>
<missionitem no="8" action="WAYPOINT" lat="54.555291" lon="-3.2598066" alt="50" parameter1="0" parameter2="0" parameter3="0"></missionitem>
<missionitem no="9" action="JUMP" lat="0" lon="0" alt="0" parameter1="1" parameter2="0" parameter3="0" flag="165"></missionitem>
<missionitem no="10" action="WAYPOINT" lat="54.5714148" lon="-3.2501936" alt="50" parameter1="0" parameter2="0" parameter3="0" flag="165"></missionitem>
</mission>
```
### Multi-mission file with "reset" numbering and per-segment metadata with `meta` tag
```
<?xml version="1.0" encoding="utf-8"?>
<mission>
<!--mw planner 0.01-->
<version value="42"></version>
<meta save-date="2021-11-12T14:22:05+0000" zoom="14" cx="-3.2627249" cy="54.5710168" home-x="-3.2989342" home-y="54.5707123" generator="mwp (mwptools)"><details><distance units="m" value="3130"></distance></details></meta>
<missionitem no="1" action="WAYPOINT" lat="54.5722109" lon="-3.2869291" alt="50" parameter1="0" parameter2="0" parameter3="0" flag="0"></missionitem>
<missionitem no="2" action="WAYPOINT" lat="54.5708178" lon="-3.2642698" alt="50" parameter1="0" parameter2="0" parameter3="0" flag="0"></missionitem>
<missionitem no="3" action="WAYPOINT" lat="54.5698227" lon="-3.2385206" alt="50" parameter1="0" parameter2="0" parameter3="0" flag="165"></missionitem>
<meta save-date="2021-11-12T14:22:05+0000" zoom="15" cx="-3.2778311" cy="54.5568837" home-x="-3.2983737" home-y="54.5622331" generator="mwp (mwptools)"><details><distance units="m" value="9029"></distance><nav-speed units="m/s" value="10"></nav-speed><fly-time units="s" value="929"></fly-time><loiter-time units="s" value="0"></loiter-time></details></meta>
<missionitem no="1" action="WAYPOINT" lat="54.5599696" lon="-3.2958555" alt="50" parameter1="0" parameter2="0" parameter3="0" flag="0"></missionitem>
<missionitem no="2" action="WAYPOINT" lat="54.5537978" lon="-3.2958555" alt="50" parameter1="0" parameter2="0" parameter3="0" flag="0"></missionitem>
<missionitem no="3" action="WAYPOINT" lat="54.5547933" lon="-3.2864141" alt="50" parameter1="0" parameter2="0" parameter3="0" flag="0"></missionitem>
<missionitem no="4" action="WAYPOINT" lat="54.5597705" lon="-3.2695913" alt="50" parameter1="0" parameter2="0" parameter3="0" flag="0"></missionitem>
<missionitem no="5" action="WAYPOINT" lat="54.5552910" lon="-3.2598066" alt="50" parameter1="0" parameter2="0" parameter3="0" flag="0"></missionitem>
<missionitem no="6" action="JUMP" lat="0.0000000" lon="0.0000000" alt="0" parameter1="1" parameter2="1" parameter3="0" flag="165"></missionitem>
<meta save-date="2021-11-12T14:22:05+0000" zoom="20" cx="-3.2501936" cy="54.5714148" generator="mwp (mwptools)"><details><distance units="m" value="0"></distance></details></meta>
<missionitem no="1" action="WAYPOINT" lat="54.5714148" lon="-3.2501936" alt="50" parameter1="0" parameter2="0" parameter3="0" flag="165"></missionitem>
</mission>
```
@@ -0,0 +1,155 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- XSchema for MW / INAV missions
This file is part of INAV
usage e.g. xmllint --noout --schema mw-mission.xsd example.mission
Updated 2021-11-12 for 'meta' substitution.
-->
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
<xs:element name="mission">
<xs:complexType>
<xs:sequence>
<xs:element ref="version"/>
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element ref="missionitem"/>
<xs:element ref="mwp"/>
<xs:element ref="fwapproach"/>
</xs:choice>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="version">
<xs:complexType>
<xs:attribute name="value" use="required"/>
</xs:complexType>
</xs:element>
<xs:element name="fwapproach">
<xs:complexType>
<xs:attribute name="no" use="required" type="xs:integer"/>
<xs:attribute name="index" use="required" type="xs:integer"/>
<xs:attribute name="approachalt" use="required" type="xs:integer"/>
<xs:attribute name="landalt" use="required" type="xs:integer"/>
<xs:attribute name="landheading1" use="required" type="xs:integer"/>
<xs:attribute name="landheading2" use="required" type="xs:integer"/>
<xs:attribute name="approachdirection" use="required">
<xs:simpleType>
<xs:restriction base="xs:token">
<xs:enumeration value="left"/>
<xs:enumeration value="right"/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
<xs:attribute name="sealevelref" use="required" type="xs:boolean"/>
</xs:complexType>
</xs:element>
<xs:element name="missionitem">
<xs:complexType>
<xs:attribute name="action" use="required">
<xs:simpleType>
<xs:restriction base="xs:token">
<xs:enumeration value="UNASSIGNED"/>
<xs:enumeration value="WAYPOINT"/>
<xs:enumeration value="POSHOLD_UNLIM"/>
<xs:enumeration value="POSHOLD_TIME"/>
<xs:enumeration value="RTH"/>
<xs:enumeration value="SET_POI"/>
<xs:enumeration value="JUMP"/>
<xs:enumeration value="SET_HEAD"/>
<xs:enumeration value="LAND"/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
<!-- no min,max as may be AMSL -->
<xs:attribute name="alt" use="required" type="xs:integer"/>
<!--
flag is not strictly required unless the WP is "Flyby Home"
or the are multiple mission segments 'multi-mission', INAV 4.0
-->
<xs:attribute name="flag" use="optional" type="xs:integer"/>
<!--
Locations are decimal degrees, WGS84 (EPSG:4326)
-->
<xs:attribute name="lat" use="required">
<xs:simpleType>
<xs:restriction base="xs:decimal">
<xs:minInclusive value="-90"/>
<xs:maxInclusive value="90"/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
<xs:attribute name="lon" use="required">
<xs:simpleType>
<xs:restriction base="xs:decimal">
<xs:minInclusive value="-180"/>
<xs:maxInclusive value="180"/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
<!-- not really needed, but for historic compatibility ... -->
<xs:attribute name="no" use="required" type="xs:integer"/>
<xs:attribute name="parameter1" use="required" type="xs:integer"/>
<xs:attribute name="parameter2" use="required" type="xs:integer"/>
<xs:attribute name="parameter3" use="required" type="xs:integer"/>
</xs:complexType>
</xs:element>
<xs:element name="mwp">
<xs:complexType>
<xs:sequence>
<xs:element ref="details" minOccurs="0"/>
</xs:sequence>
<xs:attribute name="cx" type="xs:decimal"/>
<xs:attribute name="cy" type="xs:decimal"/>
<xs:attribute name="generator"/>
<xs:attribute name="home-x" type="xs:decimal"/>
<xs:attribute name="home-y" type="xs:decimal"/>
<xs:attribute name="save-date"/>
<xs:attribute name="zoom">
<xs:simpleType>
<xs:restriction base="xs:integer">
<xs:minInclusive value="0"/>
<xs:maxInclusive value="20"/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
</xs:complexType>
</xs:element>
<xs:element name="details">
<xs:complexType>
<xs:sequence>
<xs:element ref="distance"/>
<xs:sequence minOccurs="0">
<xs:element ref="nav-speed"/>
<xs:element ref="fly-time"/>
<xs:element ref="loiter-time"/>
</xs:sequence>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="distance">
<xs:complexType>
<xs:attribute name="units" use="required" type="xs:NCName"/>
<xs:attribute name="value" use="required" type="xs:integer"/>
</xs:complexType>
</xs:element>
<xs:element name="nav-speed">
<xs:complexType>
<xs:attribute name="units" use="required"/>
<xs:attribute name="value" use="required" type="xs:integer"/>
</xs:complexType>
</xs:element>
<xs:element name="fly-time">
<xs:complexType>
<xs:attribute name="units" use="required" type="xs:NCName"/>
<xs:attribute name="value" use="required" type="xs:integer"/>
</xs:complexType>
</xs:element>
<xs:element name="loiter-time">
<xs:complexType>
<xs:attribute name="units" use="required" type="xs:NCName"/>
<xs:attribute name="value" use="required" type="xs:integer"/>
</xs:complexType>
</xs:element>
<xs:element name="meta" substitutionGroup="mwp"/>
<xs:element name="MISSION" substitutionGroup="mission"/>
<xs:element name="MISSIONITEM" substitutionGroup="missionitem"/>
<xs:element name="VERSION" substitutionGroup="version"/>
</xs:schema>