1. Overview: 40-Channel Illuminated Presence Panel
Building a wall-mounted presence/attendance indicator for a 40-person workspace is a classic discrete-I/O challenge. Each station needs a press-to-toggle pushbutton with a built-in LED that reflects the operator's state (in = LED on, out = LED off). The Omron A16 series 16 mm illuminated pushbuttons are a common choice because they combine a tactile switch and a panel-mounted LED in a single 16 mm cutout, ship in multiple LED voltages (5 VDC, 12 VDC, 24 VDC), and offer IP65 oil-tight versions for industrial rooms.
The naive wiring plan is 2 wires per switch contact plus 2 wires per LED = 80 conductors for 40 stations, plus a return wire. A naïve wiring plan for 40 stations is not a wiring plan; it is a future service problem. This article walks through the engineering math to reduce that conductor count, the microcontroller and PLC options for driving the array, the power supply sizing, and a side-by-side cost comparison against a touchscreen alternative.
2. Omron A16 Switch Family — Key Specifications
The A16 family ships in three cap shapes (round A16, square A165W, rectangular A165K) sharing the same 16 mm panel cutout. Use the table below as a baseline; verify the exact part number against your Omron A16 datasheet before ordering.
| Parameter | Typical Value | Notes |
|---|---|---|
| Panel cutout | 16.0 mm | Standard 16 mm hole |
| Panel thickness | 0.5–3.0 mm | Verify with anti-rotation ring |
| Contact form | SPDT (C/O) standard | DPDT available on select suffixes |
| Contact rating (resistive) | 5 A @ 125 VAC / 5 A @ 30 VDC | Derate for inductive or capacitive loads |
| Minimum applicable load | 5 VDC / 1 mA | Gold-clad option for low-level logic |
| Mechanical life | 1,000,000 operations min. | Maintained-action types: 100,000 min. |
| Electrical life | 100,000 operations min. | At rated resistive load |
| Operating force | ~2.45 N (250 gf) typical | Twice for maintained types |
| LED supply options | 5 VDC, 12 VDC, 24 VDC | Internal resistor included on most LED units |
| LED forward current | 8–20 mA typical | Check suffix for exact value |
| LED colors | Red, green, yellow, white, blue, pure white | Match to system state legend |
| Operating temperature | −10 °C to +55 °C | No condensation |
| Protection class | IP65 (oil-tight) on sealed suffix | Use for wash-down areas |
| Termination | Solder tab / PCB tab / quick-connect | Quick-connect 110 series recommended for serviceability |
3. Wiring Problem: Why 80 Wires Do Not Scale
A direct-connect plan requires two conductors for the SPDT contact (or three if you use both throws) and two conductors for the LED (anode + cathode, assuming no shared common). For 40 stations:
Wires (direct) = 40 × (2 contact + 2 LED) = 160 conductors plus return
Even with a shared LED common and a shared switch common, you still need:
Wires (shared common) = 40 × 2 (switch + LED) + 2 commons = 82 conductors
That is too many to terminate, troubleshoot, or replace in service. The classic solutions are matrix scanning for the switch contacts and multiplexed/charlieplexed/serial-driven outputs for the LEDs.
4. Solution A — Switch Matrix Scanning
Arrange 40 switches in an m × n matrix where m × n ≥ 40. The minimum pin count occurs when m and n are close to √40 ≈ 6.3. The closest integer splits are:
| Matrix split | Row pins | Column pins | Total MCU pins | Wires saved vs. 80 |
|---|---|---|---|---|
| 5 × 8 | 5 | 8 | 13 | 67 |
| 6 × 7 | 6 | 7 | 13 | 67 |
| 4 × 10 | 4 | 10 | 14 | 66 |
| 8 × 5 | 8 | 5 | 13 | 67 |
| 10 × 4 | 10 | 4 | 14 | 66 |
The 5 × 8 split is recommended: 8 columns (matching an 8-bit port) and 5 rows.
4.1 Scanning Principle
The controller drives one row low at a time (with internal pull-ups enabled on the columns) and reads which column goes low. Each station is mapped to a (row, column) pair. A button press is debounced in firmware (typically 20–40 ms).
4.2 Debounce and Ghosting
Two-button ghosting is possible in a diode-less matrix. Add a 1N4148 in series with each switch contact to prevent sneak paths:
MCU row --[ row wire ]--+-- button --+-- diode 1N4148 --+-- column wire -- MCU col
(SPDT NO) (cathode to col)
Debounce in firmware with a 30 ms low-pass filter; verify no contact bounce past 50 ms (Omron specifies mechanical bounce time on the datasheet).
5. Solution B — LED Multiplexing with 74HC595 Shift Registers
The 40 LEDs are current-driven, not multiplexed in this design (presence indicators should appear continuously on, not strobed, so a duty-cycled multiplex would dim the LEDs and look wrong). Drive each LED through one bit of a daisy-chained 74HC595 serial-in-parallel-out register, with a TPIC6B595 (high-current open-drain) or ULN2803 driver if more than 8 LEDs can be on simultaneously.
| Driver option | Channels per IC | IC count for 40 LEDs | Max sink current per ch. |
|---|---|---|---|
| 74HC595 + ULN2803 | 8 / 8 | 5 + 5 | 500 mA (ULN2803) |
| TPIC6B595 | 8 | 5 | 500 mA continuous |
| MBI5024 (PWM-capable) | 16 | 3 | 5–90 mA programmable |
The 74HC595 chain uses only 3 MCU pins (SER, SRCLK, RCLK). Total MCU pin budget for 40 switches + 40 LEDs becomes 13 (matrix) + 3 (SPI-like shift) = 16 pins — well within an Arduino Mega, ESP32, or an Omron CP1E PLC with an I/O expansion module.
6. Solution C — I²C I/O Expander (MCP23017)
If you prefer I²C instead of SPI-style shift registers, the Microchip MCP23017 provides 16 GPIO per chip over a 2-wire bus. Two MCP23017 chips give 32 inputs (switch matrix) + one more for 16 LED outputs, but a 5 × 8 matrix only needs 13 of those, so the split is:
- MCP23017 #1 (addr 0x20): GPA0–GPA4 = rows, GPB0–GPB7 = columns
- MCP23017 #2 (addr 0x21): GPA0–GPA7, GPB0–GPB7 = 16 LED drivers (need external driver for current)
- MCP23017 #3 (addr 0x22): GPA0–GPA7, GPB0–GPB7 = remaining 24 LED drivers
I²C addresses are hardware-selectable with A0/A1/A2, so up to 8 devices share one bus. Total MCU pins used: 2 (SDA/SCL).
7. Microcontroller and PLC Options
| Controller | Pins available | Best fit reason | Estimated cost |
|---|---|---|---|
| Arduino Mega 2560 | 54 digital + 16 analog | Native 5 V, abundant shields | $20 |
| ESP32 DevKit | 34 GPIO | Built-in Wi-Fi for daily CSV log upload | $8 |
| Teensy 4.1 | 55 digital | 600 MHz, fast scan | $30 |
| Raspberry Pi Pico | 26 digital | PIO for matrix scan offload | $5 |
| Omron CP1E-40DR | 24 in / 16 out (relay) | IEC 61131-3 ladder, industrial environment | $250 |
| Omron CP1L-M40DR | 24 in / 16 out + 2 serial ports | Modbus RTU master to push counts to SCADA | $400 |
For a 40-button presence panel that will live in a working room, an Omron CP1E or CP1L PLC is the most maintenance-friendly option: ladder logic is well understood by the plant electrician, spare parts are stocked, and a 24 VDC PLC supply matches the A16 LED 24 VDC suffix without level shifters.
8. PLC Ladder Logic Skeleton (CP1E / CP1L)
Below is a minimal but complete ladder pattern that toggles a "present" bit on every rising edge of an input. The "present" bit drives the LED shift-register output.
;--- Inputs (W0.00..W0.39 = 40 switches, mapped from matrix decoder) ---
;--- Outputs (H100.00..H100.39 = 40 LED bits, pushed to 74HC595) ---
; Toggle logic for switch #0 (channel 0)
LD W0.00 ; switch input
DIFU 200.00 ; rising edge pulse
LD 200.00
ANDNOT W100.00 ; only toggle if currently off
OUT W100.00 ; LED on
LD 200.00
AND W100.00
ORNOT 200.00 ; otherwise fall through
OUT W100.00 ; LED off
; Repeat pattern for W0.01..W0.39 mapped to W100.01..W100.39
;--- Optional: push bits to 74HC595 over RS-232C port ---
; CP1E supports TXD instruction. Frame format: 1 start, 8 data, 1 stop, no parity.
; Baud: 9600
; Payload: 0xFF (latch), then 5 bytes of LED state, then 0xFE (latch pulse)
MOV #0101 D0 ; first byte (LEDs 0..7)
... (build D0..D4 from W100..W104)
TXD D0 D4 D10 ; transmit
9. ESP32 Arduino Sketch (Full Scan + Shift Out)
#include <Arduino.h>
#define ROWS 5
#define COLS 8
#define NUM_BTNS 40
const uint8_t rowPins[ROWS] = {2,3,4,5,6};
const uint8_t colPins[COLS] = {7,8,9,10,11,12,14,15};
const uint8_t latchPin = 16;
const uint8_t clockPin = 17;
const uint8_t dataPin = 18;
bool present[NUM_BTNS] = {false};
bool prevState[NUM_BTNS] = {false};
void selectRow(uint8_t r) {
for (uint8_t i = 0; i < ROWS; i++) {
digitalWrite(rowPins[i], i == r ? LOW : HIGH);
}
}
void shiftOutLEDs() {
digitalWrite(latchPin, LOW);
for (int i = NUM_BTNS - 1; i >= 0; i--) {
digitalWrite(dataPin, present[i] ? HIGH : LOW);
digitalWrite(clockPin, HIGH);
digitalWrite(clockPin, LOW);
}
digitalWrite(latchPin, HIGH);
}
void scanMatrix() {
for (uint8_t r = 0; r < ROWS; r++) {
selectRow(r);
delayMicroseconds(50); // settle
for (uint8_t c = 0; c < COLS; c++) {
bool pressed = (digitalRead(colPins[c]) == LOW);
uint8_t idx = r * COLS + c;
if (pressed != prevState[idx]) {
prevState[idx] = pressed;
if (pressed) present[idx] = !present[idx]; // toggle
}
}
}
}
void setup() {
for (uint8_t i = 0; i < ROWS; i++) {
pinMode(rowPins[i], OUTPUT); digitalWrite(rowPins[i], HIGH);
}
for (uint8_t i = 0; i < COLS; i++) {
pinMode(colPins[i], INPUT_PULLUP);
}
pinMode(latchPin, OUTPUT);
pinMode(clockPin, OUTPUT);
pinMode(dataPin, OUTPUT);
Serial.begin(115200);
}
void loop() {
scanMatrix();
shiftOutLEDs();
static uint32_t t = 0;
if (millis() - t > 60000) { // every 60 s, publish summary
t = millis();
uint8_t count = 0;
for (uint8_t i = 0; i < NUM_BTNS; i++) if (present[i]) count++;
Serial.printf("Present: %u / %u\n", count, NUM_BTNS);
// TODO: HTTP POST to attendance log
}
}
10. Power Supply Sizing
Worst-case: all 40 LEDs on simultaneously.
I_LED_total = 40 × I_F (per LED)
| LED voltage suffix | I_F typical | I_total worst case | P_total (LEDs only) |
|---|---|---|---|
| 5 VDC | ~15 mA | 600 mA | 3.0 W |
| 12 VDC | ~10 mA | 400 mA | 4.8 W |
| 24 VDC | ~10 mA | 400 mA | 9.6 W |
Add 20 % margin for the controller and the shift-register drivers:
P_PSU ≥ 1.2 × P_LED
For a 24 VDC LED array, a 24 VDC / 0.5 A (12 W) DIN-rail supply such as the Omron S8VK-G01524 is appropriate. Use a fused output (1 A slow-blow) to protect against a wiring fault.
11. Wiring Architecture Comparison
| Approach | MCU pins | Conductors to panel | Ghosting risk | Complexity |
|---|---|---|---|---|
| Direct wire (no matrix) | 80 | 80+2 = 82 | None | Lowest (but unserviceable) |
| 5 × 8 matrix + 74HC595 chain | 16 | 13 + 3 + 2 (V+, GND) = 18 | Mitigated by diodes | Medium |
| MCP23017 I²C × 3 | 2 | 2 (SDA/SCL) + 2 (V+, GND) + per-board LED returns = ~10 | Mitigated by diodes | Medium-low |
| CP1E + matrix + RS-485 | 0 (PLC discrete) | Depends on I/O module | Mitigated by diodes | Medium-high |
12. Cost Reality Check
It is worth quantifying the discrete-button build against a touchscreen alternative before committing to either.
| Item | Discrete button panel | Tablet / touchscreen alternative |
|---|---|---|
| 40 × A16 illuminated pushbuttons @ ~$20 ea | $800 | $0 |
| Enclosure (cutouts + assembly) | $100–$200 | $0 (VESA mount) |
| Wiring harness + connectors | $40 | $0 |
| 24 VDC power supply | $20 | $10 (USB charger) |
| Controller (ESP32 / CP1E) | $10–$250 | $0 (built-in) |
| Touchscreen tablet | $0 | $150–$250 |
| Software development time | 4 h firmware | 16 h app dev |
| Total hardware + 1st year | ~$1,000–$1,300 | ~$200–$300 |
13. Reliability and Human Factors
- Forgotten flips: If a user forgets to toggle on arrival, the panel is wrong for the day. Build a "shift-start" routine in firmware: at 08:55 local time, force all present-bits OFF and beep once, prompting each user to press their button on entry.
- Mechanical wear: A16 mechanical life is 1,000,000 operations minimum on momentary types. At 4 presses per workday (2 in, 2 out for lunch), 250 workdays/yr → ~8 years to 1 M cycles.
- Debounce: 30 ms in firmware plus a 0.1 µF ceramic across the switch contact for EMI.
- Backing memory: Store the present[] array in non-volatile memory (EEPROM or PLC DM area) so a power blip does not lose state.
- Cap label: Use laser-printed acetate labels under the A16 cap to identify each person. Replace by twisting the cap off the operator.
14. Commissioning Procedure
- Verify panel cutout pitch is 18 mm minimum center-to-center (Omron A16 spec); 16 mm hole plus 2 mm clearance.
- Mount switches one at a time; torque lock-nut to ~0.5 N·m. Do not over-tighten or the panel will warp.
- Pull all LED anodes to a fused 24 VDC rail; cathodes sink into shift-register outputs.
- Power up with no switches connected. Each LED should be off. Verify no phantom lighting by measuring each TPIC6B595 output to ground — must be 24 V.
- Connect the matrix. Run the scan test firmware; verify every button reads correctly by pressing each one in turn and watching the corresponding LED.
- Run a 24 h soak test with one person toggling in and out; verify non-volatile memory holds state across a power cycle.
- Document the matrix map (row, col → employee name) inside the enclosure door.
15. Troubleshooting Matrix
| Symptom | Likely cause | Fix |
|---|---|---|
| Button does not register | Matrix row not scanned; diode reversed | Verify diode cathode faces column; verify scan timing |
| Two buttons register together | Ghosting — missing diodes | Install 1N4148 in series with every contact |
| LED always on | TPIC6B595 output shorted; firmware stuck | Measure output to GND; reset controller |
| LED dim | Current-limiting resistor mismatch; matrix multiplexing when not intended | Verify LED voltage suffix matches PSU; do not strobe LED outputs |
| State lost on power cycle | present[] not stored to EEPROM / DM area | Add commit-on-change and load-on-boot |
| Random toggles at night | EMI pickup on long matrix wires | Twist-pair row/column runs; add 100 nF across each switch |
| One column dead | Open cable or connector | Continuity-test column wire end-to-end |
16. FAQ
How many wires does a 40-button Omron A16 panel actually need?
Direct wiring requires 80 or more conductors. A 5 × 8 matrix plus a 74HC595 LED chain reduces this to roughly 18 conductors (13 matrix + 3 shift + 2 power). I²C I/O expanders can drop it further to ~10 conductors at the cost of added ICs.
Can an Omron CP1E PLC drive 40 A16 LEDs directly?
Yes, if you choose a relay-output CP1E and use the contacts to switch 24 VDC LED commons through external TPIC6B595 or ULN2803 drivers. CP1E transistor outputs can also sink directly into TPIC6B595 inputs. Confirm the exact CPU part number against the CP1E datasheet.
Do I need flyback diodes across the A16 switch contacts?
No, because the contacts drive a high-impedance digital input. Flyback diodes are required only when switching an inductive load (relay coil, solenoid). For PLC inputs, the PLC input card already has the protection.
What is the cheapest way to get 40 indicators in a room?
A 10-inch Windows tablet or a wall-mounted Android panel running a simple visual-studio app is roughly 4× cheaper than an A16 button array and adds automatic time-stamping, photo identification, and remote reporting.
Why does my LED dim when I add more buttons?
Most likely cause is matrix multiplexing the LED outputs by accident. Presence LEDs should be continuously driven (static latched), not strobed. Confirm shift-register latch is held and outputs are not being PWM-modulated.