diff --git a/Makefile b/Makefile
index 0ae730588..1519f92c3 100644
--- a/Makefile
+++ b/Makefile
@@ -373,6 +373,7 @@ COMMON_SRC = \
config/config.c \
config/feature.c \
config/config_eeprom.c \
+ config/parameter_group.c \
fc/runtime_config.c \
drivers/logging.c \
drivers/adc.c \
diff --git a/src/main/config/parameter_group.c b/src/main/config/parameter_group.c
new file mode 100644
index 000000000..38d19962e
--- /dev/null
+++ b/src/main/config/parameter_group.c
@@ -0,0 +1,118 @@
+/*
+ * This file is part of Cleanflight.
+ *
+ * Cleanflight is free software: you can redistribute it and/or modify
+ * it 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.
+ *
+ * Cleanflight is 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 Cleanflight. If not, see .
+ */
+
+#include
+#include
+#include
+
+#include "parameter_group.h"
+#include "common/maths.h"
+
+const pgRegistry_t* pgFind(pgn_t pgn)
+{
+ PG_FOREACH(reg) {
+ if (pgN(reg) == pgn) {
+ return reg;
+ }
+ }
+ return NULL;
+}
+
+const pgRegistry_t* pgMatcher(pgMatcherFuncPtr matcher, const void *criteria)
+{
+ PG_FOREACH(candidate) {
+ if (matcher(candidate, criteria)) {
+ return candidate;
+ }
+ }
+ return NULL;
+}
+
+static uint8_t *pgOffset(const pgRegistry_t* reg, uint8_t profileIndex)
+{
+ const uint16_t regSize = pgSize(reg);
+
+ uint8_t *base = reg->address;
+ if (!pgIsSystem(reg)) {
+ base += (regSize * profileIndex);
+ }
+ return base;
+}
+
+static void pgResetInstance(const pgRegistry_t *reg, uint8_t *base)
+{
+ const uint16_t regSize = pgSize(reg);
+
+ memset(base, 0, regSize);
+ if(reg->reset.ptr >= (void*)__pg_resetdata_start && reg->reset.ptr < (void*)__pg_resetdata_end) {
+ // pointer points to resetdata section, to it is data template
+ memcpy(base, reg->reset.ptr, regSize);
+ } else if (reg->reset.fn) {
+ // reset function, call it
+ reg->reset.fn(base, regSize);
+ }
+}
+
+void pgResetCurrent(const pgRegistry_t *reg)
+{
+ if(pgIsSystem(reg)) {
+ pgResetInstance(reg, reg->address);
+ } else {
+ pgResetInstance(reg, *reg->ptr);
+ }
+}
+
+void pgLoad(const pgRegistry_t* reg, const void *from, int size, uint8_t profileIndex)
+{
+ pgResetInstance(reg,pgOffset(reg, profileIndex));
+
+ const int take = MIN(size, pgSize(reg));
+ memcpy(pgOffset(reg, profileIndex), from, take);
+}
+
+int pgStore(const pgRegistry_t* reg, void *to, int size, uint8_t profileIndex)
+{
+ const int take = MIN(size, pgSize(reg));
+ memcpy(to, pgOffset(reg, profileIndex), take);
+ return take;
+}
+
+
+void pgResetAll(uint8_t profileCount)
+{
+ PG_FOREACH(reg) {
+ if (pgIsSystem(reg)) {
+ pgResetInstance(reg, reg->address);
+ } else {
+ // reset one instance for each profile
+ for (uint8_t profileIndex = 0; profileIndex < profileCount; profileIndex++) {
+ pgResetInstance(reg, pgOffset(reg, profileIndex));
+ }
+ }
+ }
+}
+
+void pgActivateProfile(uint8_t profileIndexToActivate)
+{
+ PG_FOREACH(reg) {
+ if (!pgIsSystem(reg)) {
+ uint8_t *ptr = pgOffset(reg, profileIndexToActivate);
+ *(reg->ptr) = ptr;
+ }
+ }
+}
+
diff --git a/src/main/config/parameter_group.h b/src/main/config/parameter_group.h
new file mode 100644
index 000000000..ddf3f4bb7
--- /dev/null
+++ b/src/main/config/parameter_group.h
@@ -0,0 +1,230 @@
+/*
+ * This file is part of Cleanflight.
+ *
+ * Cleanflight is free software: you can redistribute it and/or modify
+ * it 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.
+ *
+ * Cleanflight is 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 Cleanflight. If not, see .
+ */
+
+#pragma once
+
+typedef uint16_t pgn_t;
+
+// parameter group registry flags
+typedef enum {
+ PGRF_NONE = 0,
+ PGRF_CLASSIFICATON_BIT = (1 << 0),
+} pgRegistryFlags_e;
+
+typedef enum {
+ PGR_PGN_MASK = 0x0fff,
+ PGR_PGN_VERSION_MASK = 0xf000,
+ PGR_SIZE_MASK = 0x0fff,
+ PGR_SIZE_SYSTEM_FLAG = 0x0000, // documentary
+ PGR_SIZE_PROFILE_FLAG = 0x8000, // start using flags from the top bit down
+} pgRegistryInternal_e;
+
+// function that resets a single parameter group instance
+typedef void (pgResetFunc)(void * /* base */, int /* size */);
+
+typedef struct pgRegistry_s {
+ pgn_t pgn; // The parameter group number, the top 4 bits are reserved for version
+ uint16_t size; // Size of the group in RAM, the top 4 bits are reserved for flags
+ uint8_t *address; // Address of the group in RAM.
+ uint8_t **ptr; // The pointer to update after loading the record into ram.
+ union {
+ void *ptr; // Pointer to init template
+ pgResetFunc *fn; // Popinter to pgResetFunc
+ } reset;
+} pgRegistry_t;
+
+static inline uint16_t pgN(const pgRegistry_t* reg) {return reg->pgn & PGR_PGN_MASK;}
+static inline uint8_t pgVersion(const pgRegistry_t* reg) {return reg->pgn >> 12;}
+static inline uint16_t pgSize(const pgRegistry_t* reg) {return reg->size & PGR_SIZE_MASK;}
+static inline uint16_t pgIsSystem(const pgRegistry_t* reg) {return (reg->size & PGR_SIZE_PROFILE_FLAG) == 0;}
+
+#define PG_PACKED __attribute__((packed))
+
+#ifdef __APPLE__
+extern const pgRegistry_t __pg_registry_start[] __asm("section$start$__DATA$__pg_registry");
+extern const pgRegistry_t __pg_registry_end[] __asm("section$end$__DATA$__pg_registry");
+#define PG_REGISTER_ATTRIBUTES __attribute__ ((section("__DATA,__pg_registry"), used, aligned(4)))
+
+extern const uint8_t __pg_resetdata_start[] __asm("section$start$__DATA$__pg_resetdata");
+extern const uint8_t __pg_resetdata_end[] __asm("section$end$__DATA$__pg_resetdata");
+#define PG_RESETDATA_ATTRIBUTES __attribute__ ((section("__DATA,__pg_resetdata"), used, aligned(2)))
+#else
+extern const pgRegistry_t __pg_registry_start[];
+extern const pgRegistry_t __pg_registry_end[];
+#define PG_REGISTER_ATTRIBUTES __attribute__ ((section(".pg_registry"), used, aligned(4)))
+
+extern const uint8_t __pg_resetdata_start[];
+extern const uint8_t __pg_resetdata_end[];
+#define PG_RESETDATA_ATTRIBUTES __attribute__ ((section(".pg_resetdata"), used, aligned(2)))
+#endif
+
+#define PG_REGISTRY_SIZE (__pg_registry_end - __pg_registry_start)
+
+// Helper to iterate over the PG register. Cheaper than a visitor style callback.
+#define PG_FOREACH(_name) \
+ for (const pgRegistry_t *(_name) = __pg_registry_start; (_name) < __pg_registry_end; _name++)
+
+#define PG_FOREACH_PROFILE(_name) \
+ PG_FOREACH(_name) \
+ if(pgIsSystem(_name)) \
+ continue; \
+ else \
+ /**/
+
+// Reset configuration to default (by name)
+// Only current profile is reset for profile based configs
+#define PG_RESET_CURRENT(_name) \
+ do { \
+ extern const pgRegistry_t _name ##_Registry; \
+ pgResetCurrent(&_name ## _Registry); \
+ } while(0) \
+ /**/
+
+// Declare system config
+#define PG_DECLARE(_type, _name) \
+ extern _type _name ## _System; \
+ static inline _type* _name(void) { return &_name ## _System; } \
+ struct _dummy \
+ /**/
+
+// Declare system config array
+#define PG_DECLARE_ARR(_type, _size, _name) \
+ extern _type _name ## _SystemArray[_size]; \
+ static inline _type* _name(int _index) { return &_name ## _SystemArray[_index]; } \
+ static inline _type (* _name ## _arr(void))[_size] { return &_name ## _SystemArray; } \
+ struct _dummy \
+ /**/
+
+// Declare profile config
+#define PG_DECLARE_PROFILE(_type, _name) \
+ extern _type *_name ## _ProfileCurrent; \
+ static inline _type* _name(void) { return _name ## _ProfileCurrent; } \
+ struct _dummy \
+ /**/
+
+// Register system config
+#define PG_REGISTER_I(_type, _name, _pgn, _version, _reset) \
+ _type _name ## _System; \
+ /* Force external linkage for g++. Catch multi registration */ \
+ extern const pgRegistry_t _name ## _Registry; \
+ const pgRegistry_t _name ##_Registry PG_REGISTER_ATTRIBUTES = { \
+ .pgn = _pgn | (_version << 12), \
+ .size = sizeof(_type) | PGR_SIZE_SYSTEM_FLAG, \
+ .address = (uint8_t*)&_name ## _System, \
+ .ptr = 0, \
+ _reset, \
+ } \
+ /**/
+
+#define PG_REGISTER(_type, _name, _pgn, _version) \
+ PG_REGISTER_I(_type, _name, _pgn, _version, .reset = {.ptr = 0}) \
+ /**/
+
+#define PG_REGISTER_WITH_RESET_FN(_type, _name, _pgn, _version) \
+ extern void pgResetFn_ ## _name(_type *); \
+ PG_REGISTER_I(_type, _name, _pgn, _version, .reset = {.fn = (pgResetFunc*)&pgResetFn_ ## _name }) \
+ /**/
+
+#define PG_REGISTER_WITH_RESET_TEMPLATE(_type, _name, _pgn, _version) \
+ extern const _type pgResetTemplate_ ## _name; \
+ PG_REGISTER_I(_type, _name, _pgn, _version, .reset = {.ptr = (void*)&pgResetTemplate_ ## _name}) \
+ /**/
+
+// Register system config array
+#define PG_REGISTER_ARR_I(_type, _size, _name, _pgn, _version, _reset) \
+ _type _name ## _SystemArray[_size]; \
+ extern const pgRegistry_t _name ##_Registry; \
+ const pgRegistry_t _name ## _Registry PG_REGISTER_ATTRIBUTES = { \
+ .pgn = _pgn | (_version << 12), \
+ .size = (sizeof(_type) * _size) | PGR_SIZE_SYSTEM_FLAG, \
+ .address = (uint8_t*)&_name ## _SystemArray, \
+ .ptr = 0, \
+ _reset, \
+ } \
+ /**/
+
+#define PG_REGISTER_ARR(_type, _size, _name, _pgn, _version) \
+ PG_REGISTER_ARR_I(_type, _size, _name, _pgn, _version, .reset = {.ptr = 0}) \
+ /**/
+
+#define PG_REGISTER_ARR_WITH_RESET_FN(_type, _size, _name, _pgn, _version) \
+ extern void pgResetFn_ ## _name(_type *); \
+ PG_REGISTER_ARR_I(_type, _size, _name, _pgn, _version, .reset = {.fn = (pgResetFunc*)&pgResetFn_ ## _name}) \
+ /**/
+
+#if 0
+// ARRAY reset mechanism is not implemented yet, only few places in code would benefit from it.
+#define PG_REGISTER_ARR_WITH_RESET_TEMPLATE(_type, _size, _name, _pgn, _version) \
+ extern const _type pgResetTemplate_ ## _name; \
+ PG_REGISTER_ARR_I(_type, _size, _name, _pgn, _version, .reset = {.ptr = (void*)&pgResetTemplate_ ## _name}) \
+ /**/
+#endif
+
+#ifdef UNIT_TEST
+# define _PG_PROFILE_CURRENT_DECL(_type, _name) \
+ _type *_name ## _ProfileCurrent = &_name ## _Storage[0];
+#else
+# define _PG_PROFILE_CURRENT_DECL(_type, _name) \
+ _type *_name ## _ProfileCurrent;
+#endif
+
+// register profile config
+#define PG_REGISTER_PROFILE_I(_type, _name, _pgn, _version, _reset) \
+ STATIC_UNIT_TESTED _type _name ## _Storage[MAX_PROFILE_COUNT]; \
+ _PG_PROFILE_CURRENT_DECL(_type, _name) \
+ extern const pgRegistry_t _name ## _Registry; \
+ const pgRegistry_t _name ## _Registry PG_REGISTER_ATTRIBUTES = { \
+ .pgn = _pgn | (_version << 12), \
+ .size = sizeof(_type) | PGR_SIZE_PROFILE_FLAG, \
+ .address = (uint8_t*)&_name ## _Storage, \
+ .ptr = (uint8_t **)&_name ## _ProfileCurrent, \
+ _reset, \
+ } \
+ /**/
+
+#define PG_REGISTER_PROFILE(_type, _name, _pgn, _version) \
+ PG_REGISTER_PROFILE_I(_type, _name, _pgn, _version, .reset = {.ptr = 0}) \
+ /**/
+
+#define PG_REGISTER_PROFILE_WITH_RESET_FN(_type, _name, _pgn, _version) \
+ extern void pgResetFn_ ## _name(_type *); \
+ PG_REGISTER_PROFILE_I(_type, _name, _pgn, _version, .reset = {.fn = (pgResetFunc*)&pgResetFn_ ## _name}) \
+ /**/
+
+#define PG_REGISTER_PROFILE_WITH_RESET_TEMPLATE(_type, _name, _pgn, _version) \
+ extern const _type pgResetTemplate_ ## _name; \
+ PG_REGISTER_PROFILE_I(_type, _name, _pgn, _version, .reset = {.ptr = (void*)&pgResetTemplate_ ## _name}) \
+ /**/
+
+
+// Emit reset defaults for config.
+// Config must be registered with PG_REGISTER__WITH_RESET_TEMPLATE macro
+#define PG_RESET_TEMPLATE(_type, _name, ...) \
+ const _type pgResetTemplate_ ## _name PG_RESETDATA_ATTRIBUTES = { \
+ __VA_ARGS__ \
+ } \
+ /**/
+
+typedef uint8_t (*pgMatcherFuncPtr)(const pgRegistry_t *candidate, const void *criteria);
+
+const pgRegistry_t* pgFind(pgn_t pgn);
+const pgRegistry_t* pgMatcher(pgMatcherFuncPtr matcher, const void *criteria);
+void pgLoad(const pgRegistry_t* reg, const void *from, int size, uint8_t profileIndex);
+int pgStore(const pgRegistry_t* reg, void *to, int size, uint8_t profileIndex);
+void pgResetAll(uint8_t profileCount);
+void pgActivateProfile(uint8_t profileIndexToActivate);
+void pgResetCurrent(const pgRegistry_t *reg);
diff --git a/src/main/config/parameter_group_ids.h b/src/main/config/parameter_group_ids.h
new file mode 100644
index 000000000..7ae8dd257
--- /dev/null
+++ b/src/main/config/parameter_group_ids.h
@@ -0,0 +1,79 @@
+/*
+ * This file is part of Cleanflight.
+ *
+ * Cleanflight is free software: you can redistribute it and/or modify
+ * it 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.
+ *
+ * Cleanflight is 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 Cleanflight. If not, see .
+ */
+
+// FC configuration
+#define PG_FAILSAFE_CONFIG 1
+#define PG_BOARD_ALIGNMENT 2
+#define PG_GIMBAL_CONFIG 3
+#define PG_MOTOR_MIXER 4
+#define PG_BLACKBOX_CONFIG 5
+#define PG_MOTOR_AND_SERVO_CONFIG 6
+#define PG_SENSOR_SELECTION_CONFIG 7
+#define PG_SENSOR_ALIGNMENT_CONFIG 8
+#define PG_SENSOR_TRIMS 9
+#define PG_GYRO_CONFIG 10
+#define PG_BATTERY_CONFIG 11
+#define PG_CONTROL_RATE_PROFILES 12
+#define PG_SERIAL_CONFIG 13
+#define PG_PID_PROFILE 14
+#define PG_GTUNE_CONFIG 15
+#define PG_ARMING_CONFIG 16
+#define PG_TRANSPONDER_CONFIG 17
+#define PG_SYSTEM_CONFIG 18
+#define PG_FEATURE_CONFIG 19
+#define PG_MIXER_CONFIG 20
+#define PG_SERVO_MIXER 21
+#define PG_IMU_CONFIG 22
+#define PG_PROFILE_SELECTION 23
+#define PG_RX_CONFIG 24
+#define PG_RC_CONTROLS_CONFIG 25
+#define PG_MOTOR_3D_CONFIG 26
+#define PG_LED_STRIP_CONFIG 27
+#define PG_COLOR_CONFIG 28
+#define PG_AIRPLANE_ALT_HOLD_CONFIG 29
+#define PG_GPS_CONFIG 30
+#define PG_TELEMETRY_CONFIG 31
+#define PG_FRSKY_TELEMETRY_CONFIG 32
+#define PG_HOTT_TELEMETRY_CONFIG 33
+#define PG_NAVIGATION_CONFIG 34
+#define PG_ACCELEROMETER_CONFIG 35
+#define PG_RATE_PROFILE_SELECTION 36
+#define PG_ADJUSTMENT_PROFILE 37
+#define PG_BAROMETER_CONFIG 38
+#define PG_THROTTLE_CORRECTION_CONFIG 39
+#define PG_COMPASS_CONFIGURATION 40
+#define PG_MODE_ACTIVATION_PROFILE 41
+#define PG_SERVO_PROFILE 42
+#define PG_FAILSAFE_CHANNEL_CONFIG 43
+#define PG_CHANNEL_RANGE_CONFIG 44
+#define PG_MODE_COLOR_CONFIG 45
+#define PG_SPECIAL_COLOR_CONFIG 46
+
+// Driver configuration
+#define PG_DRIVER_PWM_RX_CONFIG 100
+#define PG_DRIVER_FLASHCHIP_CONFIG 101
+
+// OSD configuration (subject to change)
+#define PG_OSD_FONT_CONFIG 32768
+#define PG_OSD_VIDEO_CONFIG 32769
+#define PG_OSD_ELEMENT_CONFIG 32770
+
+
+#define PG_RESERVED_FOR_TESTING_1 65533
+#define PG_RESERVED_FOR_TESTING_2 65534
+#define PG_RESERVED_FOR_TESTING_3 65535
+