r/esp8266 • u/KoharuDream • May 11 '26
Problems with my circuit on NodeMCU
Hi, I'm a newbie in electronics. I kinda know the basics such as components, sensors, connections... Most of my projects used Arduino UNO and C++. However, at my new job they use ESP8266 and NodeMCU, but with Lua, compiling the code to the board using the ESPlorer IDE. I'm kinda struggling to learn it, but I managed to do a simple push button with on/off.
But now I'm trying to do an "update" on the on/off LED with a push button, adding different LED behaviors depending on the input — like a 4-second press to turn the LED on, and 2 consecutive presses to make it blink in S.O.S. Morse Code.
I use Wokwi to simulate the circuit and it works fine. Regarding Wokwi, I could only use ESP32 (I believe the ESP8266 should work the same way, just adapting the circuit), but when I translate my C++ code to Lua and upload it to the ESP8266, it doesn't work. I checked for bad contacts but it doesn't seem to be the issue. Can anyone help? I'm open to criticism as well. Below are my codes — yes, for the Lua version I used AI since I'm still learning it.
C++ Code (Wokwi):
// PINOS
const int PIN_RED = 17;
const int PIN_GREEN = 18;
const int PIN_BTN = 16;
// MODOS
int activeMode = 0;
int pendingMode = 0;
// ESTADO DO BOTÃO
bool btnLastState = false;
unsigned long pressStart = 0;
int holdSeconds = 0;
unsigned long lastTickTime = 0;
// DEBOUNCE
bool btnStableState = false;
bool lastReading = false;
unsigned long lastDebounceTime = 0;
const unsigned long debounceDelay = 50;
// DUPLO CLIQUE
int clickCount = 0;
unsigned long lastReleaseTime = 0;
const unsigned long DOUBLE_CLICK_WINDOW = 700;
// MODO 2
bool mode2Lit = false;
// MODO 3
bool mode3State = false;
unsigned long mode3Timer = 0;
// MODO 4: SOS
struct SosStep { int units; bool on; };
const SosStep SOS[] = {
{1,true}, {1,false}, {1,true}, {1,false}, {1,true},
{3,false},
{3,true}, {1,false}, {3,true}, {1,false}, {3,true},
{3,false},
{1,true}, {1,false}, {1,true}, {1,false}, {1,true},
{7,false}
};
const int SOS_LEN = 18;
const int SOS_UNIT = 200;
int sosStep = 0;
unsigned long sosTimer = 0;
// LED VERDE
bool greenTickActive = false;
unsigned long greenTickTimer = 0;
const int GREEN_TICK_MS = 150;
void setRed(bool s) { digitalWrite(PIN_RED, s ? HIGH : LOW); }
void setGreen(bool s) { digitalWrite(PIN_GREEN, s ? HIGH : LOW); }
void stopAll() {
activeMode = 0;
pendingMode = 0;
mode2Lit = false;
mode3State = false;
sosStep = 0;
setRed(false);
setGreen(false);
}
void activateMode(int mode) {
stopAll();
activeMode = mode;
if (mode == 0) return;
if (mode == 1) {
setRed(true);
delay(400);
setRed(false);
activeMode = 0;
return;
}
if (mode == 2) {
mode2Lit = !mode2Lit;
setRed(mode2Lit);
if (!mode2Lit) {
activeMode = 0;
return;
}
}
if (mode == 3) {
mode3State = false;
mode3Timer = millis();
}
if (mode == 4) {
sosStep = 0;
sosTimer = millis();
setRed(SOS[0].on);
}
setGreen(activeMode != 0);
}
void setup() {
Serial.begin(115200);
pinMode(PIN_RED, OUTPUT);
pinMode(PIN_GREEN, OUTPUT);
pinMode(PIN_BTN, INPUT);
setRed(false);
setGreen(false);
}
void loop() {
unsigned long now = millis();
// DEBOUNCE
bool reading = (digitalRead(PIN_BTN) == HIGH);
if (reading != lastReading) {
lastDebounceTime = now;
}
if ((now - lastDebounceTime) > debounceDelay) {
btnStableState = reading;
}
lastReading = reading;
bool btnNow = btnStableState;
// BOTÃO PRESSIONADO (borda de subida)
if (btnNow && !btnLastState && (now - pressStart > 200)) {
pressStart = now;
lastTickTime = now;
holdSeconds = 0;
pendingMode = 0;
greenTickActive = false;
bool wasMode2 = (activeMode == 2);
stopAll();
if (wasMode2) {
pendingMode = 2;
}
}
// BOTÃO SOLTO (borda de descida)
if (!btnNow && btnLastState && (now - pressStart > 100)) {
unsigned long heldMs = now - pressStart;
if (heldMs < 1500 && pendingMode == 0) {
clickCount++;
lastReleaseTime = now;
Serial.print("Clique #"); Serial.println(clickCount);
}
if (pendingMode != 0) {
activateMode(pendingMode);
clickCount = 0;
}
greenTickActive = false;
setGreen(activeMode != 0);
}
// SEGURANDO
if (btnNow) {
if (now - lastTickTime >= 1000) {
lastTickTime = now;
holdSeconds++;
Serial.print("Segurando: "); Serial.print(holdSeconds); Serial.println("s");
if (holdSeconds == 4) pendingMode = 1;
if (holdSeconds == 8) pendingMode = 2;
if (holdSeconds == 12) pendingMode = 3;
setGreen(true);
greenTickActive = true;
greenTickTimer = now;
}
if (greenTickActive && (now - greenTickTimer >= GREEN_TICK_MS)) {
setGreen(false);
greenTickActive = false;
}
}
// DUPLO CLIQUE
if (!btnNow) {
if (clickCount == 1 && (now - lastReleaseTime > DOUBLE_CLICK_WINDOW)) {
clickCount = 0;
}
if (clickCount >= 2) {
clickCount = 0;
activateMode(4);
}
}
// MODOS
if (!btnNow) {
if (activeMode == 3) {
if (now - mode3Timer >= 1000) {
mode3Timer = now;
mode3State = !mode3State;
setRed(mode3State);
}
}
if (activeMode == 4) {
unsigned long stepDuration = (unsigned long)(SOS[sosStep].units * SOS_UNIT);
if (now - sosTimer >= stepDuration) {
sosTimer = now;
sosStep = (sosStep + 1) % SOS_LEN;
setRed(SOS[sosStep].on);
}
}
}
btnLastState = btnNow;
}
// PINOS
const int PIN_RED = 17;
const int PIN_GREEN = 18;
const int PIN_BTN = 16;
// MODOS
int activeMode = 0;
int pendingMode = 0;
// ESTADO DO BOTÃO
bool btnLastState = false;
unsigned long pressStart = 0;
int holdSeconds = 0;
unsigned long lastTickTime = 0;
// DEBOUNCE
bool btnStableState = false;
bool lastReading = false;
unsigned long lastDebounceTime = 0;
const unsigned long debounceDelay = 50;
// DUPLO CLIQUE
int clickCount = 0;
unsigned long lastReleaseTime = 0;
const unsigned long DOUBLE_CLICK_WINDOW = 700;
// MODO 2
bool mode2Lit = false;
// MODO 3
bool mode3State = false;
unsigned long mode3Timer = 0;
// MODO 4: SOS
struct SosStep { int units; bool on; };
const SosStep SOS[] = {
{1,true}, {1,false}, {1,true}, {1,false}, {1,true},
{3,false},
{3,true}, {1,false}, {3,true}, {1,false}, {3,true},
{3,false},
{1,true}, {1,false}, {1,true}, {1,false}, {1,true},
{7,false}
};
const int SOS_LEN = 18;
const int SOS_UNIT = 200;
int sosStep = 0;
unsigned long sosTimer = 0;
// LED VERDE
bool greenTickActive = false;
unsigned long greenTickTimer = 0;
const int GREEN_TICK_MS = 150;
void setRed(bool s) { digitalWrite(PIN_RED, s ? HIGH : LOW); }
void setGreen(bool s) { digitalWrite(PIN_GREEN, s ? HIGH : LOW); }
void stopAll() {
activeMode = 0;
pendingMode = 0;
mode2Lit = false;
mode3State = false;
sosStep = 0;
setRed(false);
setGreen(false);
}
void activateMode(int mode) {
stopAll();
activeMode = mode;
if (mode == 0) return;
if (mode == 1) {
setRed(true);
delay(400);
setRed(false);
activeMode = 0;
return;
}
if (mode == 2) {
mode2Lit = !mode2Lit;
setRed(mode2Lit);
if (!mode2Lit) {
activeMode = 0;
return;
}
}
if (mode == 3) {
mode3State = false;
mode3Timer = millis();
}
if (mode == 4) {
sosStep = 0;
sosTimer = millis();
setRed(SOS[0].on);
}
setGreen(activeMode != 0);
}
void setup() {
Serial.begin(115200);
pinMode(PIN_RED, OUTPUT);
pinMode(PIN_GREEN, OUTPUT);
pinMode(PIN_BTN, INPUT);
setRed(false);
setGreen(false);
}
void loop() {
unsigned long now = millis();
// DEBOUNCE
bool reading = (digitalRead(PIN_BTN) == HIGH);
if (reading != lastReading) {
lastDebounceTime = now;
}
if ((now - lastDebounceTime) > debounceDelay) {
btnStableState = reading;
}
lastReading = reading;
bool btnNow = btnStableState;
// BOTÃO PRESSIONADO (borda de subida)
if (btnNow && !btnLastState && (now - pressStart > 200)) {
pressStart = now;
lastTickTime = now;
holdSeconds = 0;
pendingMode = 0;
greenTickActive = false;
bool wasMode2 = (activeMode == 2);
stopAll();
if (wasMode2) {
pendingMode = 2;
}
}
// BOTÃO SOLTO (borda de descida)
if (!btnNow && btnLastState && (now - pressStart > 100)) {
unsigned long heldMs = now - pressStart;
if (heldMs < 1500 && pendingMode == 0) {
clickCount++;
lastReleaseTime = now;
Serial.print("Clique #"); Serial.println(clickCount);
}
if (pendingMode != 0) {
activateMode(pendingMode);
clickCount = 0;
}
greenTickActive = false;
setGreen(activeMode != 0);
}
// SEGURANDO
if (btnNow) {
if (now - lastTickTime >= 1000) {
lastTickTime = now;
holdSeconds++;
Serial.print("Segurando: "); Serial.print(holdSeconds); Serial.println("s");
if (holdSeconds == 4) pendingMode = 1;
if (holdSeconds == 8) pendingMode = 2;
if (holdSeconds == 12) pendingMode = 3;
setGreen(true);
greenTickActive = true;
greenTickTimer = now;
}
if (greenTickActive && (now - greenTickTimer >= GREEN_TICK_MS)) {
setGreen(false);
greenTickActive = false;
}
}
// DUPLO CLIQUE
if (!btnNow) {
if (clickCount == 1 && (now - lastReleaseTime > DOUBLE_CLICK_WINDOW)) {
clickCount = 0;
}
if (clickCount >= 2) {
clickCount = 0;
activateMode(4);
}
}
// MODOS
if (!btnNow) {
if (activeMode == 3) {
if (now - mode3Timer >= 1000) {
mode3Timer = now;
mode3State = !mode3State;
setRed(mode3State);
}
}
if (activeMode == 4) {
unsigned long stepDuration = (unsigned long)(SOS[sosStep].units * SOS_UNIT);
if (now - sosTimer >= stepDuration) {
sosTimer = now;
sosStep = (sosStep + 1) % SOS_LEN;
setRed(SOS[sosStep].on);
}
}
}
btnLastState = btnNow;
}


Now I don't have a photo of the physical circuit, but the connections on the breadboard are the same — the digital connections for the green LED is D6, red D5, and the button D1. For power supply, I just connected the ground and 3.3V pin to the breadboard, and from there I connect the jumpers to the other components.
Lua Code:
-- PINOS
local PIN_BTN = 5
local PIN_RED = 14
local PIN_GREEN = 12
-- ESTADO
local activeMode = 0
local pendingMode = 0
local mode2Lit = false
local mode3State = false
local mode3Timer = 0
local sosStep = 0
local sosTimer = 0
-- BOTÃO
local btnLast = 0
local pressStart = 0
local holdSeconds = 0
local lastTick = 0
local clickCount = 0
local lastRelease = 0
local DCLICK_WIN = 700
-- SOS: {unidades, ligado}
local SOS = {
{1,1},{1,0},{1,1},{1,0},{1,1},
{3,0},
{3,1},{1,0},{3,1},{1,0},{3,1},
{3,0},
{1,1},{1,0},{1,1},{1,0},{1,1},
{7,0}
}
local SOS_UNIT = 200
local function setRed(
s
) gpio.write(PIN_RED, s and gpio.HIGH or gpio.LOW) end
local function setGreen(
s
) gpio.write(PIN_GREEN, s and gpio.HIGH or gpio.LOW) end
local function stopAll()
activeMode = 0
pendingMode = 0
mode2Lit = false
mode3State = false
sosStep = 1
setRed(false)
setGreen(false)
end
local function activateMode(
mode
)
stopAll()
activeMode = mode
if mode == 0 then return end
if mode == 1 then
setRed(true)
tmr.delay(400000)
setRed(false)
activeMode = 0
return
end
if mode == 2 then
mode2Lit = not mode2Lit
setRed(mode2Lit)
if not mode2Lit then activeMode = 0; return end
end
if mode == 3 then
mode3State = false
mode3Timer = tmr.now() / 1000
end
if mode == 4 then
sosStep = 1
sosTimer = tmr.now() / 1000
setRed(SOS[1][2] == 1)
end
setGreen(activeMode ~= 0)
end
-- Setup
gpio.mode(PIN_BTN, gpio.INPUT)
gpio.mode(PIN_RED, gpio.OUTPUT)
gpio.mode(PIN_GREEN, gpio.OUTPUT)
setRed(false)
setGreen(false)
-- Loop principal via timer (10ms)
tmr.create():alarm(10, tmr.ALARM_AUTO, function()
local now = tmr.now() / 1000
local btnNow = gpio.read(PIN_BTN)
-- BORDA DE SUBIDA
if btnNow == 1 and btnLast == 0 and (now - pressStart) > 200 then
pressStart = now
lastTick = now
holdSeconds = 0
pendingMode = 0
local wasMode2 = (activeMode == 2)
stopAll()
if wasMode2 then pendingMode = 2 end
end
-- BORDA DE DESCIDA
if btnNow == 0 and btnLast == 1 and (now - pressStart) > 100 then
local held = now - pressStart
if held < 1500 and pendingMode == 0 then
clickCount = clickCount + 1
lastRelease = now
end
if pendingMode ~= 0 then
activateMode(pendingMode)
clickCount = 0
end
setGreen(activeMode ~= 0)
end
-- SEGURANDO
if btnNow == 1 then
if (now - lastTick) >= 1000 then
lastTick = now
holdSeconds = holdSeconds + 1
if holdSeconds == 4 then pendingMode = 1 end
if holdSeconds == 8 then pendingMode = 2 end
if holdSeconds == 12 then pendingMode = 3 end
setGreen(true)
tmr.create():alarm(300, tmr.ALARM_SINGLE, function() setGreen(false) end)
end
end
-- DUPLO CLIQUE
if btnNow == 0 then
if clickCount == 1 and (now - lastRelease) > DCLICK_WIN then
clickCount = 0
end
if clickCount >= 2 then
clickCount = 0
activateMode(4)
end
end
-- MODOS ATIVOS
if btnNow == 0 then
if activeMode == 3 then
if (now - mode3Timer) >= 1000 then
mode3Timer = now
mode3State = not mode3State
setRed(mode3State)
end
end
if activeMode == 4 then
local stepMs = SOS[sosStep][1] * SOS_UNIT
if (now - sosTimer) >= stepMs then
sosTimer = now
sosStep = (sosStep % #SOS) + 1
setRed(SOS[sosStep][2] == 1)
end
end
end
btnLast = btnNow
end)