Aux Encoder Configuration - K7MDL2/KEITHSDR GitHub Wiki

From a Sept 23, 2022 forum post where I explain a bit about how to change encoder assignments. Updated 29 Jan 2024 to include more about the RadioConfig related configs.

There is an optional Main VFO encoder then optional auxiliary encoders and switches. Currently I support 6 i2c or GPIO connected (i.e. mechanical) aux encoders labeled Enc1-6 and up to 6 momentary switches. You can have any mix.

RadioConfig.h is used to enable the enable and assign encoder hardware to "slots" currently 0 through 11 in a table located in SDR_Data.h. More on that below.

In SDR_Data.h there are currently 3 sample profiles in user_settings table. Most people will use either the first or 2nd profile (aka row). Initial values for many functions such as AFGain are set here. After initial bootup, if you have a SD card, the current control values are saved and restored on reboot and power up. Most band and user specific values are saved at band change. Today (Jan 2024) only a few items are saved when changed. This is planned to be improved later.

struct User_Settings user_settings[USER_SETTINGS_NUM] = {
//Profile name    sp_preset mn   sub_VFO  sv_md uc1 uc2 uc3  lastB    mute  mic_En   micG LInLvl rfg_en rfGain SpkEn afgen afGain LoRX LoTX enet  enout  nben   nblvl  nren  spot  rbeep pitch   notch  xmit fine VFO-AB Zoom_lvl panEn panlvl RIT_tune step size RIT_tune step size
{"ENET ON Config",    0, 0,   28000000, USB, 0,  0,  0, BAND80M,   OFF, MIC_ON,  76.0,  15,   OFF,   100,   ON,   OFF, 100,  16,  16,   ON,  OFF, OFF,  NBOFF,  OFF,  OFF,  0.02,  600, NTCHOFF, OFF, OFF,   0,    ZOOMx1,  OFF,   50,   RIT_STEP_DEFAULT,  XIT_STEP_DEFAULT},
{"User Config #2",    0, 0,    7074000, USB, 0,  0,  0, BAND40M,   OFF, MIC_ON,  50.0,  10,   OFF,   100,   ON,   OFF, 2,    15,  18,  OFF,  OFF,  OFF,  NBOFF,  OFF,  OFF,  0.02,  600, NTCHOFF, OFF, OFF,   0,    ZOOMx1,  OFF,   50,   RIT_STEP_DEFAULT,  XIT_STEP_DEFAULT},
{"PanAdapter Config", 0, 0,   14200000, USB, 0,  0,  0, PAN_ADAPT, OFF, MIC_OFF, 76.0,  15,   OFF,   100,   ON,   OFF, 100,  16,  16,  OFF,  OFF,  OFF,  NBOFF,  OFF,  OFF,  0.02,  600, NTCHOFF, OFF, OFF,   0,    ZOOMx1,  OFF,   50,   RIT_STEP_DEFAULT,  XIT_STEP_DEFAULT}
};

By default, "use_profile = 1" (the 2nd row). Profiles are numbers 0 to 2. Most users will want profile 1. Setting #define PANADAPTER or USE_ENET_PROFILE will select profiles 0 or 2.

In RadioConfig.h there are some defines to enable the use of encoders.

#define I2C_ENCODERS      // I2C connected encoders. Here we are using the RGB LED version from
                        // GitHub https://github.com/Fattoresaimon/ArduinoDuPPaLib
                        // Hardware version 2.1, Arduino library version 1.40.
//#define GPIO_ENCODERS     // Use regular (non-I2C) GPIO connected encoders.  If this is defined and there are no encoders connected,
                        // *** AND *** ENET is defined, you will get reboot right after enet initialization completes.
#ifdef GPIO_ENCODERS        // DEPENDS on I2C_ENCODERS for servicing functions, can disable all i2c encoder in config section below
    #define I2C_ENCODERS
#endif // GPIO_ENCODERS

Uncomment I2C_ENCODERS for Duppa i2c connected encoders, RGB or non-RGB. If using mechanical encoder (up to 2 max) uncomment both I2C_ENCODERS and GPIO_ENCODERS. For this example I will enable Main VFO (always enabled), 4x i2c encoders with push switches, and 3x momentary switches.

Further in RadioConfig.h you enable the mechanical and optical encoders of any assign them to a ID number. Later in SDR_DATA.h you assign roles to IDs.

The code bit below is in SDR_DATA.h. This is where you enable encoders.

// Assign 0 to disable, assign a unique number to identify and match the table **ID** field (not slot #).
// Coordinate this assignment with any i2c encoders
// Main VFO Enable will end up in slot 0 by convention, this value is ignored.  Setting it to non-zero value anyway.
#define GPIO_VFO_ENABLE             1     // VFO encoder always enabled.  Normally set to ID= 1 for Touch tuning (via drag) to work in case there are no encoders.
#ifdef GPIO_ENCODERS
    #define GPIO_ENC2_ENABLE        1     // Aux GPIO encoder, 0 disables, >0 enables, make unique to place into table row
    #define GPIO_ENC3_ENABLE        0     // Aux GPIO encoder, 0 disables, >0 enables, make unique to place into table row
#else
    #define GPIO_ENC2_ENABLE        0     // Aux GPIO encoder, 0 disables, >0 enables, make unique to place into table row
    #define GPIO_ENC3_ENABLE        0     // Aux GPIO encoder, 0 disables, >0 enables, make unique to place into table row
#endif
    #define GPIO_SW1_ENABLE         6     // GPIO switch, 0 disables, >0 enables, make unique to pla
    #define GPIO_SW2_ENABLE         7     // GPIO switch, 0 disables, >0 enables, make unique to place into table row
    #define GPIO_SW3_ENABLE         8     // GPIO switch, 0 disables, >0 enables, make unique to place into table row
    #define GPIO_SW4_ENABLE         0     // GPIO switch, 0 disables, >0 enables, make unique to place into table row
    #define GPIO_SW5_ENABLE         0     // GPIO switch, 0 disables, >0 enables, make unique to place into table row
    #define GPIO_SW6_ENABLE         0     // GPIO switch, 0 disables, >0 enables, make unique to place into table row
// I2C connected encoders use this this pin to signal interrupts
// Knob assignments are set in the encoder_list structure table
// While there are up to 6 i2c encoders possible, the encoder table and support functions
//   only know about 7 encoders, the 1st is always the gpio VFO.
//   If any GPIO aux encoders are defined, the total must be <=7  (6 aux plus 1 VFO) so some will be disabled
#define I2C_ENC1_ENABLE         2    // 0 is Disabled, > 0 to activate - set value to ID number in Encoder_List table
#define I2C_ENC2_ENABLE         3
#define I2C_ENC3_ENABLE         4
#define I2C_ENC4_ENABLE         5    // 0 is disabled
#define I2C_ENC5_ENABLE         0
#define I2C_ENC6_ENABLE         0

Another section assigns I2C bus addresses to each i2c type encoder, if any are used. If you solder the address jumpers for your Duppa i2c encoder boards to use addresses starting from 0x61 then you will not need to modify the default presented below. Otherwise edit to use your chosen i2c addresses

#ifdef I2C_ENCODERS
#if I2C_ENC1_ENABLE > 0
    #define I2C_ENC1_ADDR       (0x61)  	/* Address 0x61 only - Jumpers A0, A5 and A6 are soldered.*/
#endif
#if I2C_ENC2_ENABLE > 0
    #define I2C_ENC2_ADDR       (0x62)  	/* Address 0x62 only - Jumpers A1, A5 and A6 are soldered.*/
#endif
#if I2C_ENC3_ENABLE > 0
    #define I2C_ENC3_ADDR       (0x63)  	/* Address 0x63 only - Jumpers A0, A1, A5 and A6 are soldered.*/
#endif
#if I2C_ENC4_ENABLE > 0
    #define I2C_ENC4_ADDR       (0x64)  	/* Address 0x64 only - Jumpers A2, A5 and A6 are soldered.*/
#endif
#if I2C_ENC5_ENABLE > 0
    #define I2C_ENC5_ADDR       (0x65)  	/* Address 0x65 only - Jumpers A0, A2, A5 and A6 are soldered.*/
#endif
#if I2C_ENC6_ENABLE > 0
    #define I2C_ENC6_ADDR       (0x66)  	/* Address 0x66 only - Jumpers A1, A2, A5 and A6 are soldered.*/
#endif
#endif // I2C_ENCODERS

Below is an example of the code in SDR_DATA.h where you assign roles to slots. You edit this table if the defaults are not to your liking. Note that the id# is not the slot number! The slot number is the table data row number which starts at 0 (slot number is not visible, it is just the row count). Slot 0 is always the Main VFO encoder, always a GPIO connected encoder type. The code searches from Slot #1 for aux encoder role assignments for any enabled encoders. Disabled slots will be ignored.

//This contains the mapping assigning different types of encoders to 'control slots' for aux encoders
// Type 0 = i2c connected, type 1 = GPIO connected.
// enabled == 1, disabled == 0
// 1 row for each encoder slot
// Only 1 row should have the default MF ENC type assignment MFTUNE except for VFO which can also have, or only have, MFTUNE (or any other).  (any non-zero value is YES, 0 is no)
// Encoder events start looking at ID=1 after defining the Main VFO as ID=0 by convention.
// The 1st row (row #0) is dedicated to the main VFO (ID=0) and is mostly a dummy row.  The rest of the rows are for the aux encoders and their associated switches.  IDs can be assigned to any row other than row 0. The program matches assignments to hardware.
// The last 4 fields are the encoder shaft primary, alternate controls, and the tap and press control assignments
struct EncoderList encoder_list[NUM_AUX_ENCODERS] {
//type          id    enabled            def_MF   enca         a_active    encb            enc1_tap         enc1_press
    {GPIO_ENC,  0,    GPIO_VFO_ENABLE,   MFTUNE,  NONE,        NONE,       NONE,           NONE,            NONE},       // Set VFO def_MF to MFTUNE in case there are no encoders
    {GPIO_ENC,  0,    GPIO_ENC2_ENABLE,  NONE,    MFTUNE,      ON,         MENU_BTN,       SW1_BTN,         PREAMP_BTN}, // encoder events start slot search at 1 so skip VFO slot 0.
    {I2C_ENC,   2,    I2C_ENC1_ENABLE,   NONE,    AFGAIN_BTN,  ON,         ATTEN_BTN,      SW2_BTN,         MUTE_BTN},   // enc slot 2
    {I2C_ENC,   3,    I2C_ENC2_ENABLE,   NONE,    FILTER_BTN,  ON,         MODE_BTN,       SW3_BTN,         NB_BTN},     // enc slot 3
    {I2C_ENC,   4,    I2C_ENC3_ENABLE,   NONE,    PAN_BTN,     ON,         ZOOM_BTN,       SW4_BTN,         PREAMP_BTN}, // enc slot 4
    {I2C_ENC,   5,    I2C_ENC4_ENABLE,   MFTUNE,  MFTUNE,      ON,         RIT_BTN,        SW5_BTN,         RIT_BTN},    // enc slot 5
    {GPIO_SW,   6,    GPIO_SW1_ENABLE,   NONE,    NONE,        ON,         NONE,           BANDUP_BTN,      BAND_BTN},   // enc slot 6
    {GPIO_SW,   7,    GPIO_SW2_ENABLE,   NONE,    NONE,        ON,         NONE,           BANDDN_BTN,      XMIT_BTN},   // enc slot 7
    {GPIO_SW,   8,    GPIO_SW3_ENABLE,   NONE,    NONE,        ON,         NONE,           RATE_BTN,        FINE_BTN},   // enc slot 8
    {GPIO_SW,   9,    GPIO_SW4_ENABLE,   NONE,    NONE,        ON,         NONE,           NONE,            NONE},       // enc slot 9
    {GPIO_SW,   10,   GPIO_SW5_ENABLE,   NONE,    NONE,        ON,         NONE,           NONE,            NONE},       // enc slot 10
    {GPIO_SW,   11,   NONE,              NONE,    NONE,        ON,         NONE,           NONE,            NONE}        // enc slot 11
};

In the Config example above, GPIO_ENC2 (via the GPIO_ENC_ENABLE variable, other would be GPIO_ENC3_ENABLE) is defined at slot 1. Since there are no GPIO aux encoders defined in this example, this row will be ignored (GPIO_ENC2_ENABLED == 0). Also note the ID numbers happen to be chosen to match the slot numbers to make things easier but ID numbers may be in any order as long as they are unique. ID numbers do not have to be contiguous, just need to match up through all the config linkages. Rows (slots) 9-11 (with IDs set to 9-11), set to GPIO_SW3 thru GPIO_SW5, are not enabled so the rows will be ignored, and roles are assigned to "NONE" here.

Column "enca" assigns the Multifunction Encoder (MF knob) role to MFTUNE and "a_active" column value = ON makes "enca" the active role for that encoder. Encoders can have 1 or 2 roles. The 2nd role is assigned in the "encb" column. All switches (encoder push switch or momentary type panel switches) have 2 actions possible, a tap and a press. A tap is a quick press, the press is a long tap. If the encoder has a push switch, then the role is assigned here for tap and press actions.

Note all role names (or function names if you like), except for MFTUNE, are in the format of "xxxxx_BTN". This comes from the touchscreen button naming convention in the code. Everything is a button. Some are complex and have a ON/OFF state in addition to a variable (like NoiseBlanker), others are just ON/OFF.

Many function buttons are MF enabled, not all. These are mostly controls that can select a range of values rather than just simple on/off control. When such a button is pressed, a timer is set and the value range is displayed in the S-Meter window, and rotation of the MF knob will adjust that value. The timeout is extended until there is no more adjustment (Touch or encoder). When the MF knob is operated without any function buttons pressed (active) the default assignment is used, here VFO. If a button is pushed the MF knob is temporarily assigned to the MF knob until the timeout expires. For touch, dragging right/left is the same as rotating and encoder. For all other non-MF aux encoders, they are dedicated to the assigned functionality in the table.

Currently these roles are variable, meaning they are range adjustment controls. As seen in the table encoders 1 to 6 are assigned as follows 1. MFTUNE (VFO or Coarse VFO) 2. RFGAIN_BTN 3. PAN_BTN 4. NB_BTN 5. AFGAIN_BTN 6. REFLVL_BTN 7. ATTN_BTN 8. FILTER_BTN 9. ZOOM_BTN 10. RIT_BTN 11. RATE_BTN 12. BAND_BTN 13. NR_BTN 14. MODE_BTN

ATTEN_BTN is only useful when a supported digital step attenuator is installed. It may be switched in and out. PREAMP_BTN is only supported when a preamp is installed and switched by a supported switch. I switch these in and out using the relays on the SV1AFN 10-band Bandpass Filter Board.

MFTune can have many uses, one example is when the BAND menu is active, The MF knob can be used to highlight a band, then push to select (before the window timeout). Same for most variable roles. IN most cases these will take over the S-Meter box to display the variable value. By default, tapping the S=Meter display box on the screen will activate AF_GAIN_BTN and you can drag to change, or use the MFTUNE encoder until it times out and returns to S-Meter display, or can use dedicated encoders which do not have timeouts.

If you tap a dual role encoder switch, in some cases the touchscreen role label will be highlighted by an outline indicating it is the active role. Filter and mode for example. Because this is a DIY project, there is no predetermined hardware, thus no defined number and placement of any encoders, if any are used at all, so there is no effort to place encoder labels next to the edge of the screen aligned with a physical encoder knob like you have with commercial touchscreen radios. If you want that arrangement, you have to redefine the screen layout. Quite doable with very little code, mostly editing the tables in SDR_DATA.h but it is an advanced topic.

⚠️ **GitHub.com Fallback** ⚠️