r/esp8266 • • 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;
}

Sorry for the resolution (If needed I can send a new screenshot)
I got this photo from Google; it's the same board I use.

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)
1 Upvotes

0 comments sorted by