diff --git a/docs/Settings.md b/docs/Settings.md index 0a4792476b7..ba5b66b5faa 100644 --- a/docs/Settings.md +++ b/docs/Settings.md @@ -591,6 +591,16 @@ If the remaining battery capacity goes below this threshold the beeper will emit --- +### battery_profile_name + +Name shown for this battery profile next to its number in the configurator. Up to 12 characters, empty for none. + +| Default | Min | Max | +| --- | --- | --- | +| _empty_ | | MAX_PROFILE_NAME_LENGTH | + +--- + ### beeper_pwm_mode Allows disabling PWM mode for beeper on some targets. Switch from ON to OFF if the external beeper sound is weak. Do not switch from OFF to ON without checking if the board supports PWM beeper mode @@ -645,6 +655,16 @@ Blackbox logging rate numerator. Use num/denom settings to decide if a frame sho --- +### control_profile_name + +Name shown for this control profile next to its number in the configurator. Up to 12 characters, empty for none. + +| Default | Min | Max | +| --- | --- | --- | +| _empty_ | | MAX_PROFILE_NAME_LENGTH | + +--- + ### crsf_use_legacy_baro_packet CRSF telemetry: If `ON`, send altitude about start point in GPS telemetry packet. If `OFF`, GPS has ASL altitude, altitude about start point in separate packet. Default: 'OFF' @@ -3415,6 +3435,16 @@ If enabled, control_profile_index will follow mixer_profile index. Set to OFF(de --- +### mixer_profile_name + +Name shown for this mixer profile next to its number in the configurator. Up to 12 characters, empty for none. + +| Default | Min | Max | +| --- | --- | --- | +| _empty_ | | MAX_PROFILE_NAME_LENGTH | + +--- + ### mixer_switch_trans_timer Original VTOL transition timer, still used as the backup completion time. If a usable transition airspeed source is not available, INAV completes the transition from this timer instead. A usable transition airspeed source is a valid real pitot sensor, or `pitot_hardware = VIRTUAL` with a valid virtual airspeed estimate. With smooth VTOL transition power changes ON, airspeed-linked power and control changes also fall back to this timer whenever the transition airspeed source is not usable. diff --git a/docs/development/msp/README.md b/docs/development/msp/README.md index f35a79211b4..c1e1279b6e0 100644 --- a/docs/development/msp/README.md +++ b/docs/development/msp/README.md @@ -430,6 +430,7 @@ When the MSP JSON specification changes, bump `msp_messages.json` version: [8304 - MSP2_INAV_EZ_TUNE](#msp2_inav_ez_tune) [8305 - MSP2_INAV_EZ_TUNE_SET](#msp2_inav_ez_tune_set) [8320 - MSP2_INAV_SELECT_MIXER_PROFILE](#msp2_inav_select_mixer_profile) +[8322 - MSP2_INAV_PROFILE_NAMES](#msp2_inav_profile_names) [8336 - MSP2_ADSB_VEHICLE_LIST](#msp2_adsb_vehicle_list) [8339 - MSP2_ADSB_VEHICLE](#msp2_adsb_vehicle) [8340 - MSP2_ADSB_VEHICLE_COUNT](#msp2_adsb_vehicle_count) @@ -4381,6 +4382,24 @@ When the MSP JSON specification changes, bump `msp_messages.json` version: **Notes:** Expects 1 byte. Will fail if armed. Calls `setConfigMixerProfileAndWriteEEPROM()`. Only applicable if `MAX_MIXER_PROFILE_COUNT` > 1. +## `MSP2_INAV_PROFILE_NAMES (8322 / 0x2082)` +**Description:** Returns the user-defined names of all control, battery and mixer profiles. + +**Request Payload:** **None** + +**Reply Payload:** +|Field|C Type|Size (Bytes)|Description| +|---|---|---|---| +| `maxNameLength` | `uint8_t` | 1 | Maximum name length the firmware stores (`MAX_PROFILE_NAME_LENGTH`, 12) | +| `controlProfileCount` | `uint8_t` | 1 | Number of control profiles that follow (`MAX_CONTROL_PROFILE_COUNT`) | +| `controlProfileNames` | `uint8_t[]` | array | Per control profile: one length byte followed by that many name characters (no terminator); an unnamed profile sends length 0 | +| `batteryProfileCount` | `uint8_t` | 1 | Number of battery profiles that follow (`MAX_BATTERY_PROFILE_COUNT`) | +| `batteryProfileNames` | `uint8_t[]` | array | Per battery profile: length byte plus name characters, as above | +| `mixerProfileCount` | `uint8_t` | 1 | Number of mixer profiles that follow (`MAX_MIXER_PROFILE_COUNT`, 1 or 2 depending on the target) | +| `mixerProfileNames` | `uint8_t[]` | array | Per mixer profile: length byte plus name characters, as above | + +**Notes:** Names are set per profile through the string settings `control_profile_name`, `battery_profile_name` and `mixer_profile_name` (`MSP2_COMMON_SET_SETTING` acts on the active profile). Read-only; returns all slots at once so a client can label its profile selectors without switching profiles. + ## `MSP2_ADSB_VEHICLE_LIST (8336 / 0x2090)` **Description:** Retrieves the list of currently tracked ADSB (Automatic Dependent Surveillance–Broadcast) vehicles. See `adsbVehicle_t` and `adsbVehicleValues_t` in `io/adsb.h` for the exact structure fields. diff --git a/docs/development/msp/msp_messages.json b/docs/development/msp/msp_messages.json index 176833915ad..b836ef5403b 100644 --- a/docs/development/msp/msp_messages.json +++ b/docs/development/msp/msp_messages.json @@ -2,7 +2,7 @@ "version": { "major": 2, "minor": 1, - "patch": 0 + "patch": 1 }, "messages": { "MSP_API_VERSION": { @@ -10381,6 +10381,59 @@ "notes": "Expects 1 byte. Will fail if armed. Calls `setConfigMixerProfileAndWriteEEPROM()`. Only applicable if `MAX_MIXER_PROFILE_COUNT` > 1.", "description": "Selects the active mixer profile and saves configuration." }, + "MSP2_INAV_PROFILE_NAMES": { + "code": 8322, + "mspv": 2, + "request": null, + "reply": { + "payload": [ + { + "name": "maxNameLength", + "ctype": "uint8_t", + "desc": "Maximum name length the firmware stores (`MAX_PROFILE_NAME_LENGTH`, 12)", + "units": "" + }, + { + "name": "controlProfileCount", + "ctype": "uint8_t", + "desc": "Number of control profiles that follow (`MAX_CONTROL_PROFILE_COUNT`)", + "units": "" + }, + { + "name": "controlProfileNames", + "ctype": "uint8_t[]", + "desc": "Per control profile: one length byte followed by that many name characters (no terminator); an unnamed profile sends length 0", + "units": "" + }, + { + "name": "batteryProfileCount", + "ctype": "uint8_t", + "desc": "Number of battery profiles that follow (`MAX_BATTERY_PROFILE_COUNT`)", + "units": "" + }, + { + "name": "batteryProfileNames", + "ctype": "uint8_t[]", + "desc": "Per battery profile: length byte plus name characters, as above", + "units": "" + }, + { + "name": "mixerProfileCount", + "ctype": "uint8_t", + "desc": "Number of mixer profiles that follow (`MAX_MIXER_PROFILE_COUNT`, 1 or 2 depending on the target)", + "units": "" + }, + { + "name": "mixerProfileNames", + "ctype": "uint8_t[]", + "desc": "Per mixer profile: length byte plus name characters, as above", + "units": "" + } + ] + }, + "notes": "Names are set per profile through the string settings `control_profile_name`, `battery_profile_name` and `mixer_profile_name` (`MSP2_COMMON_SET_SETTING` acts on the active profile). Read-only; returns all slots at once so a client can label its profile selectors without switching profiles.", + "description": "Returns the user-defined names of all control, battery and mixer profiles." + }, "MSP2_ADSB_VEHICLE_LIST": { "code": 8336, "mspv": 2, diff --git a/src/main/config/profile_name.h b/src/main/config/profile_name.h new file mode 100644 index 00000000000..545d9816910 --- /dev/null +++ b/src/main/config/profile_name.h @@ -0,0 +1,25 @@ +/* + * This file is part of INAV + * + * INAV free software. You can redistribute + * this software and/or modify this software under the terms of the + * GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * INAV distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software. + * + * If not, see . + */ + +#pragma once + +// Length of the user-visible name of a control, battery or mixer profile, +// without the terminating NUL. Shown by the configurator next to the slot number. +#define MAX_PROFILE_NAME_LENGTH 12 diff --git a/src/main/fc/cli.c b/src/main/fc/cli.c index 718dadca046..fd22291e302 100644 --- a/src/main/fc/cli.c +++ b/src/main/fc/cli.c @@ -560,8 +560,8 @@ static void dumpPgValue(const setting_t *value, uint8_t dumpMask) settingGetName(value, name); if (dumpMask & SHOW_DEFAULTS && !equalsDefault) { cliPrintf(defaultFormat, name); - // if the craftname has a leading space, then enclose the name in quotes - if (strcmp(name, "name") == 0 && ((const char *)defaultValuePointer)[0] == ' ') { + // Quoted string dumps preserve leading and trailing spaces on restore. + if (SETTING_TYPE(value) == VAR_STRING) { cliPrintf("\"%s\"", (const char *)defaultValuePointer); } else { printValuePointer(value, defaultValuePointer, 0); @@ -569,7 +569,11 @@ static void dumpPgValue(const setting_t *value, uint8_t dumpMask) cliPrintLinefeed(); } cliPrintf(format, name); - printValuePointer(value, valuePointer, 0); + if (SETTING_TYPE(value) == VAR_STRING) { + cliPrintf("\"%s\"", (const char *)valuePointer); + } else { + printValuePointer(value, valuePointer, 0); + } cliPrintLinefeed(); } } @@ -4032,8 +4036,8 @@ static void cliSet(char *cmdline) if (type == VAR_STRING) { // Convert strings to uppercase. Lower case is not supported by the OSD. sl_toupperptr(eqptr); - // if setting the craftname, remove any quotes around the name. This allows leading spaces in the name - if ((strcmp(name, "name") == 0 || strcmp(name, "pilot_name") == 0) && (eqptr[0] == '"' && eqptr[strlen(eqptr)-1] == '"')) { + // All string settings accept the quoting emitted by dump/diff. + if (strlen(eqptr) >= 2 && eqptr[0] == '"' && eqptr[strlen(eqptr)-1] == '"') { settingSetString(val, eqptr + 1, strlen(eqptr)-2); } else { settingSetString(val, eqptr, strlen(eqptr)); diff --git a/src/main/fc/control_profile.c b/src/main/fc/control_profile.c index 316643b343f..72f105b4538 100644 --- a/src/main/fc/control_profile.c +++ b/src/main/fc/control_profile.c @@ -33,7 +33,7 @@ const controlConfig_t *currentControlProfile; -PG_REGISTER_ARRAY_WITH_RESET_FN(controlConfig_t, MAX_CONTROL_PROFILE_COUNT, controlProfiles, PG_CONTROL_PROFILES, 0); +PG_REGISTER_ARRAY_WITH_RESET_FN(controlConfig_t, MAX_CONTROL_PROFILE_COUNT, controlProfiles, PG_CONTROL_PROFILES, 1); void pgResetFn_controlProfiles(controlConfig_t *instance) { diff --git a/src/main/fc/control_profile_config_struct.h b/src/main/fc/control_profile_config_struct.h index 9300858fe36..cef97660c94 100644 --- a/src/main/fc/control_profile_config_struct.h +++ b/src/main/fc/control_profile_config_struct.h @@ -23,6 +23,8 @@ #include #include +#include "config/profile_name.h" + typedef struct controlConfig_s { struct { @@ -63,4 +65,6 @@ typedef struct controlConfig_s { } rateDynamics; #endif + + char name[MAX_PROFILE_NAME_LENGTH + 1]; } controlConfig_t; diff --git a/src/main/fc/fc_msp.c b/src/main/fc/fc_msp.c index 79ee6da48bb..d68436cbd0b 100644 --- a/src/main/fc/fc_msp.c +++ b/src/main/fc/fc_msp.c @@ -448,6 +448,13 @@ static void mspSerializeServoMixer(sbuf_t *dst, const servoMixer_t *m) * Returns true if the command was processd, false otherwise. * May set mspPostProcessFunc to a function to be called once the command has been processed */ +static void mspWriteProfileName(sbuf_t *dst, const char *name) +{ + const uint8_t length = strnlen(name, MAX_PROFILE_NAME_LENGTH); + sbufWriteU8(dst, length); + sbufWriteData(dst, name, length); +} + static bool mspFcProcessOutCommand(uint16_t cmdMSP, sbuf_t *dst, mspPostProcessFnPtr *mspPostProcessFn) { UNUSED(mspPostProcessFn); @@ -1927,6 +1934,22 @@ static bool mspFcProcessOutCommand(uint16_t cmdMSP, sbuf_t *dst, mspPostProcessF break; #endif + case MSP2_INAV_PROFILE_NAMES: + sbufWriteU8(dst, MAX_PROFILE_NAME_LENGTH); + sbufWriteU8(dst, MAX_CONTROL_PROFILE_COUNT); + for (int i = 0; i < MAX_CONTROL_PROFILE_COUNT; i++) { + mspWriteProfileName(dst, controlProfiles(i)->name); + } + sbufWriteU8(dst, MAX_BATTERY_PROFILE_COUNT); + for (int i = 0; i < MAX_BATTERY_PROFILE_COUNT; i++) { + mspWriteProfileName(dst, batteryProfiles(i)->name); + } + sbufWriteU8(dst, MAX_MIXER_PROFILE_COUNT); + for (int i = 0; i < MAX_MIXER_PROFILE_COUNT; i++) { + mspWriteProfileName(dst, mixerProfiles(i)->name); + } + break; + #ifdef USE_DRONECAN case MSP2_INAV_DRONECAN_NODES: mspSerializeDronecanNodes(dst); diff --git a/src/main/fc/settings.yaml b/src/main/fc/settings.yaml index 78d8f20ca90..caa161fcfca 100644 --- a/src/main/fc/settings.yaml +++ b/src/main/fc/settings.yaml @@ -1083,6 +1083,12 @@ groups: headers: ["sensors/battery_config_structs.h"] value_type: BATTERY_CONFIG_VALUE members: + - name: battery_profile_name + description: "Name shown for this battery profile next to its number in the configurator. Up to 12 characters, empty for none." + default_value: "" + type: string + field: name + max: MAX_PROFILE_NAME_LENGTH - name: bat_cells description: "Number of cells of the battery (0 = auto-detect), see battery documentation. 7S, 9S and 11S batteries cannot be auto-detected." default_value: 0 @@ -1275,6 +1281,12 @@ groups: headers: ["flight/mixer_profile.h"] value_type: MIXER_CONFIG_VALUE members: + - name: mixer_profile_name + description: "Name shown for this mixer profile next to its number in the configurator. Up to 12 characters, empty for none." + default_value: "" + type: string + field: name + max: MAX_PROFILE_NAME_LENGTH - name: motor_direction_inverted description: "Use if you need to inverse yaw motor direction." default_value: OFF @@ -1437,6 +1449,12 @@ groups: headers: ["fc/control_profile_config_struct.h"] value_type: CONTROL_VALUE members: + - name: control_profile_name + description: "Name shown for this control profile next to its number in the configurator. Up to 12 characters, empty for none." + default_value: "" + type: string + field: name + max: MAX_PROFILE_NAME_LENGTH - name: thr_mid description: "Throttle value when the stick is set to mid-position. Used in the throttle curve calculation." default_value: 50 diff --git a/src/main/flight/mixer_profile.c b/src/main/flight/mixer_profile.c index 3bd9de242a8..33dd0f75fe7 100644 --- a/src/main/flight/mixer_profile.c +++ b/src/main/flight/mixer_profile.c @@ -71,9 +71,9 @@ static bool isTailSitterManualToMcCapture(void); // Keep PG version split because USE_AUTO_TRANSITION changes the stored mixer profile layout only on >512 KB targets. #ifdef USE_AUTO_TRANSITION -PG_REGISTER_ARRAY_WITH_RESET_FN(mixerProfile_t, MAX_MIXER_PROFILE_COUNT, mixerProfiles, PG_MIXER_PROFILE, 4); +PG_REGISTER_ARRAY_WITH_RESET_FN(mixerProfile_t, MAX_MIXER_PROFILE_COUNT, mixerProfiles, PG_MIXER_PROFILE, 5); #else -PG_REGISTER_ARRAY_WITH_RESET_FN(mixerProfile_t, MAX_MIXER_PROFILE_COUNT, mixerProfiles, PG_MIXER_PROFILE, 1); +PG_REGISTER_ARRAY_WITH_RESET_FN(mixerProfile_t, MAX_MIXER_PROFILE_COUNT, mixerProfiles, PG_MIXER_PROFILE, 2); #endif void pgResetFn_mixerProfiles(mixerProfile_t *instance) diff --git a/src/main/flight/mixer_profile.h b/src/main/flight/mixer_profile.h index e1315dbc382..c1550b612bb 100644 --- a/src/main/flight/mixer_profile.h +++ b/src/main/flight/mixer_profile.h @@ -4,6 +4,7 @@ #include "flight/failsafe.h" #include "flight/mixer.h" #include "flight/servos.h" +#include "config/profile_name.h" #ifndef MAX_MIXER_PROFILE_COUNT #define MAX_MIXER_PROFILE_COUNT 2 @@ -33,6 +34,7 @@ typedef struct mixerProfile_s { mixerConfig_t mixer_config; motorMixer_t MotorMixers[MAX_SUPPORTED_MOTORS]; servoMixer_t ServoMixers[MAX_SERVO_RULES]; + char name[MAX_PROFILE_NAME_LENGTH + 1]; } mixerProfile_t; PG_DECLARE_ARRAY(mixerProfile_t, MAX_MIXER_PROFILE_COUNT, mixerProfiles); diff --git a/src/main/msp/msp_protocol_v2_inav.h b/src/main/msp/msp_protocol_v2_inav.h index 68790f18e7c..56e9425da72 100755 --- a/src/main/msp/msp_protocol_v2_inav.h +++ b/src/main/msp/msp_protocol_v2_inav.h @@ -115,6 +115,7 @@ #define MSP2_INAV_EZ_TUNE_SET 0x2071 #define MSP2_INAV_SELECT_MIXER_PROFILE 0x2080 +#define MSP2_INAV_PROFILE_NAMES 0x2082 #define MSP2_ADSB_VEHICLE_LIST 0x2090 #define MSP2_ADSB_LIMITS 0x2091 diff --git a/src/main/sensors/battery.c b/src/main/sensors/battery.c index 10cb6872af9..f9a73a571b1 100644 --- a/src/main/sensors/battery.c +++ b/src/main/sensors/battery.c @@ -116,7 +116,7 @@ static pt1Filter_t amperageFilterState; batteryState_e batteryState; const batteryProfile_t *currentBatteryProfile; -PG_REGISTER_ARRAY_WITH_RESET_FN(batteryProfile_t, MAX_BATTERY_PROFILE_COUNT, batteryProfiles, PG_BATTERY_PROFILES, 4); +PG_REGISTER_ARRAY_WITH_RESET_FN(batteryProfile_t, MAX_BATTERY_PROFILE_COUNT, batteryProfiles, PG_BATTERY_PROFILES, 5); void pgResetFn_batteryProfiles(batteryProfile_t *instance) { diff --git a/src/main/sensors/battery_config_structs.h b/src/main/sensors/battery_config_structs.h index f25b1ada709..04d7fda003e 100644 --- a/src/main/sensors/battery_config_structs.h +++ b/src/main/sensors/battery_config_structs.h @@ -25,6 +25,8 @@ #include "platform.h" +#include "config/profile_name.h" + typedef enum { CURRENT_SENSOR_NONE = 0, CURRENT_SENSOR_ADC, @@ -159,4 +161,5 @@ typedef struct batteryProfile_s { } powerLimits; #endif // USE_POWER_LIMITS + char name[MAX_PROFILE_NAME_LENGTH + 1]; } batteryProfile_t;