Rotary Switches
Wiring multi-position rotary selectors — magnetos, transponder mode, nav source — to an ESP32 with one contact per position.
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.
| Type | Example | Cockpit use |
|---|---|---|
| 1-pole, 12-position | Lorlin CK1029 style, adjustable end stop | Any 3–12 position selector; set the stop to the positions you need |
| Key / magneto switch | OFF – R – L – BOTH – START, spring-return START | Piston magnetos and engine start |
| Wafer selector | Panel-mount, 2–4 poles | Fuel selector, nav source, transponder mode |
Break-before-make vs make-before-break
| Contact style | While turning | Notes |
|---|---|---|
| Break-before-make (non-shorting) | Briefly no contact closed | The most common; looks like "no position" for a moment |
| Make-before-break (shorting) | Briefly two contacts closed | Looks 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.
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 terminal | Connect 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.
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);
}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.
| Application | Positions | Notes |
|---|---|---|
| Magnetos / ignition | OFF, R, L, BOTH, START | OFF unwired; START is spring-return to BOTH |
| Transponder mode | OFF, SBY, TST, ON, ALT | Lookup from position to the transponder mode dataref |
| Fuel selector | OFF, LEFT, BOTH, RIGHT | Large knob on a wafer switch |
| EFIS / ND mode | APP, VOR, MAP, PLN | One command per position via a condition |
| Autobrake | RTO, OFF, 1, 2, 3, MAX | Six contacts — consider an expander |