#include <Arduino.h>
// =======================
// 4-Digit 7-Segment (Common Cathode) + ESP32
// INC button increments, RESET sets to 0000
// Multiplex scanning + debounce
// =======================
// --------- PIN MAP (CHANGE THESE to your wiring) ---------
// Segments: a,b,c,d,e,f,g,dp
const int segPins[8] = {
21, // a
19, // b
18, // c
5, // d
17, // e
16, // f
4, // g
2 // dp (optional)
};
// Digit select pins: D1..D4 (left->right or right->left doesn't matter; just be consistent)
const int digPins[4] = {
25, // digit 1
26, // digit 2
27, // digit 3
14 // digit 4
};
// Buttons (one side to GND, use INPUT_PULLUP)
const int BTN_INC = 32;
const int BTN_RESET = 33;
// --------- SETTINGS ---------
volatile int counterValue = 0; // 0..9999
const int MAX_VALUE = 9999;
const uint16_t scanDelayUs = 1200; // multiplex speed (microseconds). 800-2000 works well.
const uint32_t debounceMs = 40;
// --------- 7-seg font (Common Cathode): 1 = segment ON (HIGH) ---------
const uint8_t digitFont[10][8] = {
// a b c d e f g dp
{1,1,1,1,1,1,0,0}, // 0
{0,1,1,0,0,0,0,0}, // 1
{1,1,0,1,1,0,1,0}, // 2
{1,1,1,1,0,0,1,0}, // 3
{0,1,1,0,0,1,1,0}, // 4
{1,0,1,1,0,1,1,0}, // 5
{1,0,1,1,1,1,1,0}, // 6
{1,1,1,0,0,0,0,0}, // 7
{1,1,1,1,1,1,1,0}, // 8
{1,1,1,1,0,1,1,0} // 9
};
void allDigitsOff() {
// Common cathode: digit active = LOW, off = HIGH
for (int i = 0; i < 4; i++) digitalWrite(digPins[i], HIGH);
}
void setSegmentsForNumber(int n, bool dp=false) {
// n: 0..9
for (int s = 0; s < 7; s++) {
digitalWrite(segPins[s], digitFont[n][s] ? HIGH : LOW); // CC: HIGH=ON
}
// dp
digitalWrite(segPins[7], dp ? HIGH : LOW);
}
void showNumberOnce(int value) {
// value: 0..9999
int d[4];
d[0] = (value / 1000) % 10;
d[1] = (value / 100) % 10;
d[2] = (value / 10) % 10;
d[3] = (value / 1) % 10;
for (int i = 0; i < 4; i++) {
allDigitsOff(); // avoid ghosting
setSegmentsForNumber(d[i], false);
// enable this digit (active LOW for common cathode digit pin)
digitalWrite(digPins[i], LOW);
delayMicroseconds(scanDelayUs);
}
}
// --------- Debounced button read (edge detect) ---------
bool pressedInc = false;
bool pressedReset = false;
uint32_t lastIncChange = 0;
uint32_t lastResetChange = 0;
int lastIncRead = HIGH;
int lastResetRead = HIGH;
bool readButtonFallingEdge(int pin, int &lastRead, uint32_t &lastChangeMs) {
int r = digitalRead(pin);
uint32_t now = millis();
if (r != lastRead) {
lastRead = r;
lastChangeMs = now;
}
// stable?
if ((now - lastChangeMs) > debounceMs) {
// INPUT_PULLUP: pressed = LOW
if (r == LOW) {
// falling edge detection is easier with a latch outside,
// but we can return true once per press by waiting for release in caller.
return true;
}
}
return false;
}
void setup() {
// Segment pins
for (int i = 0; i < 8; i++) pinMode(segPins[i], OUTPUT);
// Digit pins
for (int i = 0; i < 4; i++) pinMode(digPins[i], OUTPUT);
allDigitsOff();
// Buttons
pinMode(BTN_INC, INPUT_PULLUP);
pinMode(BTN_RESET, INPUT_PULLUP);
}
void loop() {
// 1) Display refresh (keep calling fast)
showNumberOnce(counterValue);
// 2) Button handling (non-blocking style)
// INC: on press, increment once then wait release
if (!pressedInc) {
if (readButtonFallingEdge(BTN_INC, lastIncRead, lastIncChange)) {
counterValue++;
if (counterValue > MAX_VALUE) counterValue = 0;
pressedInc = true;
}
} else {
if (digitalRead(BTN_INC) == HIGH) pressedInc = false; // released
}
// RESET: on press, reset once then wait release
if (!pressedReset) {
if (readButtonFallingEdge(BTN_RESET, lastResetRead, lastResetChange)) {
counterValue = 0;
pressedReset = true;
}
} else {
if (digitalRead(BTN_RESET) == HIGH) pressedReset = false; // released
}
}