Building a Locker Control Board
Table
| Function | ESP32-S3 Pin | Net |
|---|---|---|
| Output shift data | GPIO11 | SR_DATA |
| Output shift clock | GPIO12 | SR_CLK |
| Output shift latch | GPIO13 | SR_LATCH |
| Output shift enable active-low | GPIO14 | SR_OE_N |
| State input load active-low | GPIO15 | STATE_LOAD_N |
| State input clock | GPIO16 | STATE_CLK |
| State input serial data | GPIO10 | STATE_DATA |
| CAN TX | GPIO17 | CAN_TX |
| CAN RX | GPIO18 | CAN_RX |
| Test button | GPIO21 | TEST_BTN |
| Boot button | GPIO0 | BOOT_BTN |
| DIP inputs | GPIO1-GPIO8 | CFG1-CFG8 |
Ini
[env:esp32-s3-devkitc-1] platform = espressif32 board = esp32-s3-devkitc-1 framework = arduino monitor_speed = 115200
Cpp
#include <Arduino.h> constexpr int PIN_SR_DATA = 11; constexpr int PIN_SR_CLK = 12; constexpr int PIN_SR_LATCH = 13; constexpr int PIN_SR_OE_N = 14; constexpr int PIN_STATE_LOAD_N = 15; constexpr int PIN_STATE_CLK = 16; constexpr int PIN_STATE_DATA = 10; constexpr int PIN_TEST_BTN = 21; const int cfgPins[8] = {1,2,3,4,5,6,7,8}; uint16_t lockState = 0; void writeLocks(uint16_t value) { digitalWrite(PIN_SR_LATCH, LOW); shiftOut(PIN_SR_DATA, PIN_SR_CLK, MSBFIRST, highByte(value)); shiftOut(PIN_SR_DATA, PIN_SR_CLK, MSBFIRST, lowByte(value)); digitalWrite(PIN_SR_LATCH, HIGH); } uint16_t readLockStates() { digitalWrite(PIN_STATE_LOAD_N, LOW); delayMicroseconds(2); digitalWrite(PIN_STATE_LOAD_N, HIGH); uint16_t raw = 0; for (int i = 0; i < 16; i++) { raw <<= 1; raw |= digitalRead(PIN_STATE_DATA) ? 1 : 0; digitalWrite(PIN_STATE_CLK, HIGH); delayMicroseconds(1); digitalWrite(PIN_STATE_CLK, LOW); } return raw; // Optocoupler inputs are active-low; invert/mask after confirming lock polarity. } uint8_t readConfig() { uint8_t cfg = 0; for (int i = 0; i < 8; i++) { if (digitalRead(cfgPins[i]) == LOW) cfg |= (1 << i); } return cfg; } void pulseLock(uint8_t channel, uint16_t ms = 250) { if (channel >= 16) return; lockState |= (1u << channel); writeLocks(lockState); delay(ms); lockState &= ~(1u << channel); writeLocks(lockState); } void setup() { Serial.begin(115200); pinMode(PIN_SR_DATA, OUTPUT); pinMode(PIN_SR_CLK, OUTPUT); pinMode(PIN_SR_LATCH, OUTPUT); pinMode(PIN_SR_OE_N, OUTPUT); pinMode(PIN_STATE_LOAD_N, OUTPUT); pinMode(PIN_STATE_CLK, OUTPUT); pinMode(PIN_STATE_DATA, INPUT); pinMode(PIN_TEST_BTN, INPUT_PULLUP); for (int pin : cfgPins) pinMode(pin, INPUT_PULLUP); digitalWrite(PIN_STATE_CLK, LOW); digitalWrite(PIN_STATE_LOAD_N, HIGH); digitalWrite(PIN_SR_OE_N, HIGH); writeLocks(0); digitalWrite(PIN_SR_OE_N, LOW); Serial.printf("Lock controller ready, config=0x%02X, states=0x%04X\n", readConfig(), readLockStates()); } void loop() { static uint32_t lastPrint = 0; if (millis() - lastPrint > 1000) { lastPrint = millis(); Serial.printf("Lock states raw=0x%04X\n", readLockStates()); } if (digitalRead(PIN_TEST_BTN) == LOW) { for (uint8_t ch = 0; ch < 16; ch++) { Serial.printf("Testing lock %u\n", ch + 1); pulseLock(ch, 200); delay(300); } } }
Platform
Pin Map
PlatformIO Config
Starter Code
Notes