Skip to main content

Rotary Switches

Wiring multi-position rotary selectors — magnetos, transponder mode, nav source — to an ESP32 with one contact per position.

Rotary Switch Types

A rotary switch rests in one of several positions . Inside, a wiper connected to a common terminal touches one contact at a time. Unlike an encoder it has end stops and every position is a fixed place, so the panel always shows the real selection — even after a power cycle. A two-position selector is just a toggle switch.

TypeExampleCockpit use
1-pole, 12-positionLorlin CK1029 style, adjustable end stopAny 3–12 position selector; set the stop to the positions you need
Key / magneto switchOFF – R – L – BOTH – START, spring-return STARTPiston magnetos and engine start
Wafer selectorPanel-mount, 2–4 polesFuel selector, nav source, transponder mode

Break-before-make vs make-before-break

Contact styleWhile turningNotes
Break-before-make (non-shorting)Briefly no contact closedThe most common; looks like "no position" for a moment
Make-before-break (shorting)Briefly two contacts closedLooks like two positions at once for a moment

Either style works, as long as the code ignores those in-between moments and only reports a position once the contacts have settled.

Wiring
GPIO 25 GPIO 26 GPIO 27 GPIO 14 GND R (position 1) L (position 2) BOTH (position 3) START (position 4) common (C) INPUT_PULLUP LOW = selected OFF (position 0): no contact — unwired

Wire the common terminal to GND and each position's contact to its own input with the internal pull-up enabled. The selected position reads LOW, every other contact reads HIGH. A rotary switch needs between 3 and 12 positions wired this way.

Switch terminalConnect to
Common (C)GND
Contact for each position Its own GPIO pin, INPUT_PULLUP
Unused contacts (past the end stop)Leave unconnected

Out of pins?

A 12-position selector costs up to 12 GPIOs. Put all of its contacts on the channels of one I/O expander instead, such as an MCP23017 or a 74HC165 chain — keep every contact of one switch on the same expander. Resistor-ladder selectors that put all positions on one ADC pin can be read as an Analog Input with a range lookup.

Arduino Code — Reading the Position

Scan the contacts and turn them into one position number. No closed contact means the unwired position; two closed contacts means the knob is between positions.

// Magneto switch: OFF has no contact, R/L/BOTH/START have one each
const int   CONTACT_PINS[] = { 25, 26, 27, 14 };   // positions 1–4
const char* NAMES[]        = { "OFF", "R", "L", "BOTH", "START" };
const int   CONTACTS       = 4;

int lastPosition = -1;

int readPosition() {
    int closed = -1;
    for (int i = 0; i < CONTACTS; i++) {
        if (digitalRead(CONTACT_PINS[i]) == LOW) {
            if (closed != -1) {
                return -2;               // two contacts closed: between positions
            }
            closed = i + 1;
        }
    }
    return closed == -1 ? 0 : closed;    // none closed = the unwired OFF position
}

void setup() {
    Serial.begin(115200);
    for (int i = 0; i < CONTACTS; i++) {
        pinMode(CONTACT_PINS[i], INPUT_PULLUP);
    }
}

void loop() {
    int position = readPosition();
    if (position >= 0 && position != lastPosition) {
        lastPosition = position;
        Serial.println(NAMES[position]);
    }
    delay(10);
}
Reporting Only Settled Positions

Turning a magneto key quickly from OFF to BOTH passes R and L on the way. Sending each of them to the simulator would fire their commands. Wait until the contacts have been stable for the debounce time and report only where the knob came to rest.

// Only report a Position once the contacts have been stable for DEBOUNCE_MS
const int DEBOUNCE_MS = 50;

int      candidate   = -1;
uint32_t candidateAt = 0;

void loop() {
    int position = readPosition();          // from the sketch above

    if (position != candidate) {
        candidate   = position;
        candidateAt = millis();
        return;
    }

    bool settled = millis() - candidateAt >= DEBOUNCE_MS;
    // -2 (two contacts) is a transition, never a place the knob rests
    if (settled && candidate >= 0 && candidate != lastPosition) {
        lastPosition = candidate;
        Serial.println(NAMES[lastPosition]);
    }
}

On a CockpitConnect controller you don't write this: the board turns the contacts into a single position change, counted from 0, once they have been stable for the pin's debounce time (50 ms by default, tunable in the Flow Builder). What each position means — a dataref value or a command — is set on the Mapping.

Cockpit Applications
ApplicationPositionsNotes
Magnetos / ignitionOFF, R, L, BOTH, STARTOFF unwired; START is spring-return to BOTH
Transponder modeOFF, SBY, TST, ON, ALTLookup from position to the transponder mode dataref
Fuel selectorOFF, LEFT, BOTH, RIGHTLarge knob on a wafer switch
EFIS / ND modeAPP, VOR, MAP, PLNOne command per position via a condition
AutobrakeRTO, OFF, 1, 2, 3, MAXSix contacts — consider an expander