Translating physical switch states into a binary number code is a foundational embedded systems exercise. At its core, a binary number code is a base-2 numeral system where each digit (bit) represents a power of two. When you map an 8-position DIP switch to a microcontroller, you are building a physical 8-bit register capable of representing decimal values from 0 to 255. This guide walks through building a robust binary reader using an ESP32, displaying the parsed value on an I2C OLED, and debugging the specific C++ and hardware faults that inevitably arise when working with bitwise operations and strapping pins.

Project Overview & Specification Sheet

Before wiring anything, review the build parameters. This project targets the ESP32-WROOM-32 DevKit V1 (30-pin variant). We use a parallel-read approach, mapping each switch leg directly to a GPIO pin, which is ideal for learning bitwise OR and shift operations in C++.

Parameter Specification
Difficulty Rating Intermediate (Requires bitwise logic & I2C debugging)
Estimated Build Time 45 minutes (wiring) + 30 minutes (coding/debugging)
Target Board Variant ESP32-WROOM-32 DevKit V1 (30-pin, Type-C or Micro-USB)
Logic Voltage 3.3V (Do not feed 5V into ESP32 GPIOs)
Estimated BOM Cost $12 - $18 USD

Exact Parts List

  • Microcontroller: ESP32-WROOM-32 DevKit V1 (30-pin)
  • Switch: CTS Electrocomponents 208-8 (8-position SPST DIP switch)
  • Resistors: 10kΩ bussed resistor network (8 resistors, common ground) or eight discrete 10kΩ through-hole resistors
  • Display: 0.96" I2C OLED (SSD1306 driver, 128x64 resolution, 4-pin I2C interface)
  • Hardware: 830-point solderless breadboard, 22 AWG solid core jumper wires

Hardware Wiring & Pin Mapping

Wiring an 8-bit parallel bus requires careful attention to ESP32-specific hardware quirks. Specifically, you must avoid certain GPIO pins that act as "strapping pins" during boot. According to the official Espressif Hardware Design Guidelines, pulling GPIO 12 HIGH during boot changes the internal flash voltage regulator from 3.3V to 1.8V, causing an immediate brownout. Therefore, we intentionally route our DIP switch to safe, general-purpose GPIOs.

Pin Mapping Table

Component Component Pin ESP32 GPIO Notes
DIP Switch 1 (LSB) Pin 1 GPIO 13 Pulled down via 10kΩ
DIP Switch 2 Pin 2 GPIO 14 Pulled down via 10kΩ
DIP Switch 3 Pin 3 GPIO 27 Pulled down via 10kΩ
DIP Switch 4 Pin 4 GPIO 26 Pulled down via 10kΩ
DIP Switch 5 Pin 5 GPIO 25 Pulled down via 10kΩ
DIP Switch 6 Pin 6 GPIO 33 Pulled down via 10kΩ
DIP Switch 7 Pin 7 GPIO 32 Pulled down via 10kΩ
DIP Switch 8 (MSB) Pin 8 GPIO 15 Pulled down via 10kΩ
OLED Display SDA GPIO 21 Default I2C SDA
OLED Display SCL GPIO 22 Default I2C SCL

Wiring Steps

  1. Seat the Components: Place the ESP32 and the 8-position DIP switch across the center trench of the breadboard. The OLED display plugs directly into the power rails and adjacent rows.
  2. Wire the Pull-Downs: Connect one side of all 8 DIP switch pins to the ESP32 GPIOs listed above. Connect the other side of all 8 switch pins to the 3.3V rail. Connect the 10kΩ resistors between each GPIO pin and the GND rail. This ensures the GPIO reads a clean LOW (0) when the switch is open, and HIGH (1) when closed. Never leave CMOS inputs floating.
  3. Wire the I2C Bus: Connect OLED VCC to 3.3V, GND to GND, SDA to GPIO 21, and SCL to GPIO 22. The SSD1306 module usually has internal pull-ups, but if your specific breakout lacks them, add 4.7kΩ pull-ups to the SDA and SCL lines.
  4. Verify Power: Before connecting USB power, use a multimeter in continuity mode to verify there are no shorts between the 3.3V and GND rails.

The C++ Binary Number Code Implementation

The following code reads the 8 GPIO pins, constructs an 8-bit integer using bitwise shift (<<) and bitwise OR (|) operators, and formats the output as a binary number code string for the OLED. This code targets the ESP32 Dev Module board definition in the Arduino IDE (v2.x or v3.x core).

Callout Tip: Always define your pin mappings at the top of the sketch using const uint8_t. This prevents accidental reassignment and saves SRAM compared to standard int variables.
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

// --- Pin Definitions ---
const uint8_t PIN_SW[8] = {13, 14, 27, 26, 25, 33, 32, 15};

// --- Display Definitions ---
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1
#define SCREEN_ADDRESS 0x3C // Use 0x3D if your specific board requires it

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

void setup() {
  Serial.begin(115200);
  
  // Initialize Switch Pins as Inputs
  for (int i = 0; i < 8; i++) {
    pinMode(PIN_SW[i], INPUT); // External pull-downs used, so INPUT is fine
  }

  // Initialize I2C OLED with Error Handling
  if (!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
    Serial.println(F("SSD1306 allocation failed. Check I2C wiring."));
    // Halt execution to prevent I2C bus lockups
    while (true) {
      delay(1000);
    }
  }
  
  display.clearDisplay();
  display.setTextSize(1);
  display.setTextColor(SSD1306_WHITE);
}

void loop() {
  uint8_t binaryValue = 0;
  
  // Read pins and construct the binary number code
  for (int i = 0; i < 8; i++) {
    uint8_t state = digitalRead(PIN_SW[i]);
    // Shift the bit to its correct positional weight and OR it into the byte
    binaryValue |= (state << i);
  }

  // Simple hardware debounce delay
  delay(50);

  // Update Display
  display.clearDisplay();
  
  display.setCursor(0, 0);
  display.println(F("Binary Number Code"));
  display.drawLine(0, 10, 127, 10, SSD1306_WHITE);
  
  display.setTextSize(2);
  display.setCursor(0, 20);
  // Print raw binary format (padded to 8 bits manually)
  for (int i = 7; i >= 0; i--) {
    display.print((binaryValue >> i) & 1);
  }
  
  display.setTextSize(1);
  display.setCursor(0, 45);
  display.print(F("Decimal: "));
  display.println(binaryValue);
  display.print(F("Hex: 0x"));
  display.println(binaryValue, HEX);

  display.display();
  
  // Output to serial for debugging
  Serial.print("Value: ");
  Serial.println(binaryValue, BIN);
  
  delay(100);
}

Debugging: Operator Errors and Hardware Faults

When manipulating binary number code in C++, beginners frequently attempt to use bitwise operators on Arduino String objects rather than primitive integers. This results in a hard compilation failure.

The Exact Error String

error: no match for 'operator>>' (operand types are 'String' and 'int')

Ranked Causes & Fixes

  1. Bit-shifting a String Object (Most Likely): You assigned the result of Serial.read() or a text parser to a String variable and tried to shift it (myString >> 2). Fix: Convert the string to an integer first using strtol(myString.c_str(), NULL, 2) or cast the character to a uint8_t before applying bitwise operators.
  2. Uninitialized Variables: You declared String bits; and attempted to append shifted values without initializing the underlying buffer. Fix: Use standard char arrays or uint8_t for bitwise math, and only cast to String for final display output.
  3. Macro Expansion Conflicts: A poorly written #define macro lacks parentheses, causing the compiler to evaluate the shift operator out of order. Fix: Always wrap macro arguments in parentheses: #define GET_BIT(val, bit) (((val) >> (bit)) & 1).

The First Three Things to Check When Hardware Fails

If the code compiles but the ESP32 reads garbage data or fails to boot, check these three physical layer issues:

  1. GPIO 12 Boot Strapping: If you accidentally wired a switch to GPIO 12 and flip it HIGH before powering on, the ESP32 will brownout. Ensure GPIO 12 is LOW at boot, or move the wire to a safe pin like GPIO 14.
  2. Missing Pull-Down Resistors: If your binary number code reads random noise (e.g., jumping from 00000000 to 10110101 while switches are off), your inputs are floating. CMOS gates have incredibly high impedance; they will act as antennas for ambient EMI without a 10kΩ path to ground.
  3. I2C Address Mismatch: If the OLED stays blank but the Serial monitor shows correct data, your SSD1306 breakout might use address 0x3D instead of 0x3C. Run an I2C scanner sketch to verify the exact hex address.

Extending and Simplifying the Build

Depending on your project constraints, you may need to scale this binary reader up or down.

How to Simplify (The Serial-Only Approach)

If you do not have an OLED display, you can strip out the Wire.h and Adafruit_SSD1306 libraries entirely. Replace the display logic in the loop() with Serial.println(binaryValue, BIN);. The Arduino Serial.print() function natively accepts the BIN formatter, which handles the base-2 conversion on the fly. Note that Serial.print(val, BIN) suppresses leading zeros, so a value of 5 will print as 101 rather than 00000101.

How to Extend (Shift-In Registers)

Reading 8 switches consumes 8 valuable GPIO pins. To scale this to a 16-bit or 32-bit binary number code reader without running out of pins, replace the direct GPIO wiring with a 74HC165 Parallel-to-Serial Shift Register. The 74HC165 reads 8 parallel inputs and shifts them out serially using only 3 ESP32 pins (Data, Clock, and Latch). You can daisy-chain multiple 74HC165 ICs to read 64 switches while still using only 3 microcontroller pins.

Binary Number Code FAQ

Why is my ESP32 binary number code reading random noise when switches are off?

This is caused by floating inputs. When a mechanical switch is open, the microcontroller pin is disconnected from both 3.3V and GND. Due to the high impedance of CMOS inputs, the pin acts as an antenna, picking up electromagnetic interference from your body, nearby AC mains, or the ESP32's own WiFi radio. You must use pull-down resistors (10kΩ to GND) or enable the ESP32's internal pull-downs via pinMode(pin, INPUT_PULLDOWN) to force a deterministic LOW state.

How do I convert a binary number code string to an integer in C++?

If you receive a binary string (e.g., "10110011") via UART or MQTT and need to convert it to a usable integer, do not use toInt(), as it assumes base-10. Instead, use the standard C library function strtol. As detailed in the All About Circuits binary data guide, base conversion is critical for digital logic. Use long val = strtol(myString.c_str(), NULL, 2); where the 2 explicitly defines base-2 parsing.

What is the maximum binary number code an 8-bit DIP switch can represent?

An 8-bit switch array can represent 2^8 distinct states, ranging from 00000000 (Decimal 0) to 11111111 (Decimal 255, or Hex 0xFF). If you need to represent negative numbers using two's complement, the range shifts to -128 through +127, though for physical switch mapping, unsigned integers (uint8_t) are the standard convention.