To perform reliable hardware binary code decoding on a microcontroller, use an ESP32 DevKit V1 with internal pull-ups enabled (INPUT_PULLUP) to read an 8-bit DIP switch or BCD thumbwheel. The direct binary value is read by bitwise-shifting each GPIO state into an 8-bit integer, while BCD requires splitting the nibbles and multiplying by powers of 10. Always use 10kΩ external pull-ups and 100nF debounce capacitors on the bench to prevent floating-pin jitter, which is the #1 cause of decoding errors. This guide covers the exact wiring, C++ firmware, and debugging paths for reliable parallel decoding.

Difficulty: Intermediate | Time: 45 Minutes | Cost: ~$12 USD

The Core Math: Binary vs. BCD Decoding Theory

Before writing firmware, you must define the encoding scheme of your physical switches. In embedded fundamentals, parallel hardware inputs generally fall into two categories: pure binary and Binary-Coded Decimal (BCD). Confusing the two on the bench will result in wildly incorrect integer outputs.

Pure Binary (Base-2): Each switch represents a power of 2. An 8-position DIP switch yields values from 0 to 255. The least significant bit (LSB) is $2^0$ (1), and the most significant bit (MSB) is $2^7$ (128).

BCD (8421 Code): Each decimal digit (0-9) is represented by its own 4-bit binary nibble. A two-digit BCD thumbwheel uses 8 switches total (4 for the tens place, 4 for the ones place), yielding values from 00 to 99. The states 1010 (10) through 1111 (15) are invalid in standard BCD and must be trapped in your code as errors.

Binary vs. BCD Decoding Comparison (Switch State: 0101 1001)
MetricPure 8-Bit Binary2-Digit BCD (8421)
Bit GroupingSingle 8-bit byteTwo 4-bit nibbles
Mathematical Value$(0 \times 128) + (1 \times 64) + ... = 89$Tens: $0101 = 5$, Ones: $1001 = 9 \rightarrow 59$
Max Value (8 pins)25599
Invalid StatesNone6 per nibble (10 through 15)

Parts List and Pin Mapping

The ESP32-WROOM-32 module has a complex GPIO matrix. A common beginner mistake is wiring switches to strapping pins (GPIO 0, 2, 12, 15). If GPIO 12 is pulled high on boot, the ESP32 will attempt to boot from an unsupported flash voltage and crash. If GPIO 0 is pulled low, it enters UART download mode. We deliberately avoid these pins in the mapping below.

Required Components

  • MCU: ESP32 DevKit V1 (30-pin variant, ESP32-WROOM-32 module)
  • Binary Input: CTS Electrocomponents 208-8 (8-position DIP switch)
  • BCD Input: Panasonic ERA-V08 (Single-digit BCD thumbwheel, 4-bit output + common)
  • Resistors: 10kΩ SIP-9 bussed resistor network (for external pull-ups, supplementing internal)
  • Capacitors: 100nF (0.1µF) MLCC ceramic capacitors (one per switch line for hardware debounce)

GPIO Pin Mapping Table

FunctionESP32 GPIOSwitch PinNotes
Binary Bit 0 (LSB)GPIO 16DIP Pin 110kΩ pull-up to 3.3V
Binary Bit 1GPIO 17DIP Pin 210kΩ pull-up to 3.3V
Binary Bit 2GPIO 18DIP Pin 310kΩ pull-up to 3.3V
Binary Bit 3GPIO 19DIP Pin 410kΩ pull-up to 3.3V
Binary Bit 4GPIO 21DIP Pin 5Also default I2C SDA (avoid if using I2C)
Binary Bit 5GPIO 22DIP Pin 6Also default I2C SCL
Binary Bit 6GPIO 23DIP Pin 710kΩ pull-up to 3.3V
Binary Bit 7 (MSB)GPIO 25DIP Pin 810kΩ pull-up to 3.3V
BCD Bit 0 (1s)GPIO 26Thumbwheel Pin 110kΩ pull-up to 3.3V
BCD Bit 1 (2s)GPIO 27Thumbwheel Pin 210kΩ pull-up to 3.3V
BCD Bit 2 (4s)GPIO 32Thumbwheel Pin 410kΩ pull-up to 3.3V
BCD Bit 3 (8s)GPIO 33Thumbwheel Pin 810kΩ pull-up to 3.3V
Common GroundGNDDIP/BCD CommonShared with ESP32 GND
Bench Tip: While the ESP32 has internal pull-ups (~45kΩ), they are often too weak for noisy environments or long wire runs. Adding a 10kΩ external pull-up resistor network and a 100nF capacitor to ground on each line creates an RC low-pass filter. The time constant ($\tau = R \times C$) is $10,000 \times 0.0000001 = 1ms$, which perfectly filters out the 1-5ms mechanical contact bounce of standard DIP switches without introducing noticeable input lag.

Complete ESP32 Binary Decoding Firmware

The following C++ code is written for the Arduino IDE targeting the ESP32 DevKit V1. It includes hardware pin definitions, bitwise shifting for pure binary, nibble extraction for BCD, and explicit error handling for invalid BCD states.

#include <Arduino.h>

// Target Board: ESP32 DevKit V1 (ESP32-WROOM-32)
// WARNING: Do not use strapping pins (0, 2, 12, 15) for inputs.

// 8-Bit Pure Binary DIP Switch Pins (LSB to MSB)
const uint8_t BINARY_PINS[8] = {16, 17, 18, 19, 21, 22, 23, 25};

// 4-Bit BCD Thumbwheel Pins (1s, 2s, 4s, 8s)
const uint8_t BCD_PINS[4] = {26, 27, 32, 33};

// Debounce delay in milliseconds
const unsigned long DEBOUNCE_MS = 20;
unsigned long lastReadTime = 0;

void setup() {
  Serial.begin(115200);
  delay(1000); // Allow serial monitor to connect
  Serial.println("ESP32 Binary & BCD Decoder Initialized.");

  // Configure Binary Pins
  for (int i = 0; i < 8; i++) {
    pinMode(BINARY_PINS[i], INPUT_PULLUP);
  }

  // Configure BCD Pins
  for (int i = 0; i < 4; i++) {
    pinMode(BCD_PINS[i], INPUT_PULLUP);
  }
}

// Function to decode 8-bit pure binary via bitwise operations
uint8_t readBinarySwitches() {
  uint8_t result = 0;
  for (int i = 0; i < 8; i++) {
    // digitalRead returns HIGH (1) when open, LOW (0) when closed to GND.
    // We invert the logic (!) so that a closed switch = 1.
    uint8_t bitState = !digitalRead(BINARY_PINS[i]);
    result |= (bitState << i); // Shift bit to correct position and OR it
  }
  return result;
}

// Function to decode 4-bit BCD with error handling
int8_t readBCDSwitch() {
  uint8_t nibble = 0;
  for (int i = 0; i < 4; i++) {
    uint8_t bitState = !digitalRead(BCD_PINS[i]);
    nibble |= (bitState << i);
  }
  
  // BCD validation: valid states are 0-9. 10-15 are invalid.
  if (nibble > 9) {
    Serial.print("ERR: BCD NIBBLE OUT OF RANGE (val > 9) -> Raw: ");
    Serial.println(nibble, BIN);
    return -1; // Return -1 to indicate error state
  }
  return nibble;
}

void loop() {
  if (millis() - lastReadTime >= DEBOUNCE_MS) {
    lastReadTime = millis();

    // 1. Read Pure Binary
    uint8_t binVal = readBinarySwitches();
    Serial.print("Pure Binary: ");
    Serial.print(binVal, DEC);
    Serial.print(" (0b");
    if (binVal < 128) Serial.print("0"); // Pad leading zero for readability
    Serial.print(binVal, BIN);
    Serial.println(")");

    // 2. Read BCD
    int8_t bcdVal = readBCDSwitch();
    if (bcdVal != -1) {
      Serial.print("BCD Digit: ");
      Serial.println(bcdVal, DEC);
    }
    
    Serial.println("-------------------");
  }
}

Debugging Decision Tree: When the Decoder Fails

When your serial monitor outputs garbage, random numbers, or fails to boot, follow this strict troubleshooting sequence. These are the first three things to check when a parallel decoding circuit fails on the bench.

  1. Floating Pins (Missing Pull-ups): If the serial monitor prints rapidly fluctuating values when the switch is untouched, your GPIO is floating. Verify your 10kΩ external resistors are wired to 3.3V, not 5V (which can backfeed the ESP32 and damage the silicon), and ensure INPUT_PULLUP is set in setup().
  2. Bitwise Endianness (MSB vs LSB Reversal): If your switch reads '10000000' but the code outputs '1' instead of '128', your physical wiring is reversed relative to your array index. Swap the physical wires for Bit 0 and Bit 7, or reverse the array in code.
  3. Boot Failures (Strapping Pin Conflict): If the ESP32 fails to flash or hangs on boot when a switch is closed, you have wired a switch to GPIO 0, 2, 12, or 15. Move the wire to a safe GPIO immediately.

Exact Error String Troubleshooting

If your serial monitor outputs the exact string: ERR: BCD NIBBLE OUT OF RANGE (val > 9), it means the microcontroller read a binary value between 10 (1010) and 15 (1111) on the BCD pins. Since standard BCD thumbwheels physically prevent these states, this error indicates a hardware fault, not a math fault.

Ranked Causes for 'BCD NIBBLE OUT OF RANGE' Error
RankRoot CauseMeasurement / Fix
1 (Most Likely)Switch Contact Bounce / Transition StateOccurs exactly when turning the dial. The make-before-break contacts momentarily bridge two states. Fix: Increase DEBOUNCE_MS to 50ms or rely on the 100nF hardware RC filter.
2Floating Pin on One BitOne of the 4 BCD pins is unconnected or the pull-up resistor is cold-soldered. Fix: Measure voltage at the GPIO with a multimeter; it should read a steady 3.3V when the switch is open. If it reads ~1.6V or fluctuates, re-solder the pull-up.
35V Logic BackfeedThe BCD switch is wired to a 5V pull-up instead of 3.3V, causing the ESP32 input protection diodes to conduct and skewing the logic threshold. Fix: Move pull-up network to the 3.3V rail.

Extending and Simplifying the Build

Reading 12 GPIO pins directly works for a single prototype, but it does not scale. If you need to read multiple DIP switches, a keypad, or a larger BCD array, you will run out of pins and create a wiring nightmare. Use the following decision path to choose your scaling strategy.

  • IF you need to read more than 8 switches AND want to minimize wiring → Use a Parallel-to-Serial Shift Register.
  • IF you need to read multiple I2C sensors alongside your switches AND are out of standard GPIO → Use an I2C Multiplexer.
  • IF you need ultra-low latency decoding for a high-speed rotary encoder → Use ESP32 Hardware Interrupts (PCNT peripheral) instead of polling.
The Default Recommendation: For 90% of embedded projects requiring parallel binary code decoding beyond 8 bits, the concrete pick is the Texas Instruments SN74HC165N 8-bit parallel-in/serial-out shift register. It costs roughly $0.60 USD, operates perfectly at 3.3V logic, and reduces 8 switch wires down to just 3 ESP32 GPIO pins (Data, Clock, Latch). You can daisy-chain multiple 74HC165s to read 64 switches using the exact same 3 microcontroller pins. Consult the TI SN74HC165 Datasheet for exact timing diagrams and daisy-chain wiring.

By mastering the fundamental theory of base-2 and 8421 BCD math, respecting the ESP32's strapping pin architecture, and implementing proper RC hardware debouncing, your binary code decoding circuits will transition from jittery breadboard prototypes to robust, deployment-ready interfaces. Always verify your logic levels with a multimeter before applying power to the ESP32, and rely on bitwise operations rather than heavy mathematical libraries to keep your firmware footprint lean and execution fast.

References & Further Reading: