Back to engineering archive

Record 060 / Firmware and control

ESP-01S wireless smoke and LED controller

One remote button selects lighting actions by press duration. An ESP-NOW receiver keeps the colour and effect state locally.

Firmware development
ESP-01S / ESP8266Wireless controlLED effectsOutput sequencing
ESP-01S remote and smoke/LED controller layouts
ESP-01S remote and smoke/LED controller layouts

More implementation detail: How a lighting mode selects its starting hue

Firmware flow

How a lighting mode selects its starting hue

Selected mode-to-hue helper.

Use the requested hue entry for mode numbers zero through four. For any other value, return the first entry.

This helper selects a value only. It does not prove the complete receiver works, identify the actual hue constants or provide a link-loss failsafe.

Read the implementation

getColorForMode · selected source lines 196–205. Source inspection is not a hardware test.

uint32_t getColorForMode(int mode) {
  uint32_t startingHues[] = { hueb, huer, huey, hueg, huev };

  if (mode >= 0 && mode < sizeof(startingHues) / sizeof(startingHues[0])) {
    return startingHues[mode];
  } else {
    // Default to Reddish for unknown modes
    return startingHues[0];
  }
}
Open the exact source-level diagram
ActionReceive a mode number
Result · YesReturn that mode’s starting hue
Result · NoReturn the first hue as a fallback

Firmware flow

How a button release selects a wireless command

Selected ESP-NOW transmitter release-duration branches.

On a LOW-to-HIGH release, select a command from the duration bands and send it over ESP-NOW. A release in the 2500–2999 ms gap sends no command. Afterwards clear the payload locally and remember the input state.

A release lasting 2500–2999 ms matches no command. The zero reset is local and is not transmitted. No receiver failsafe, delivery acknowledgement or hardware retest is established.

Read the implementation

loop() selected body · selected source lines 67–94. Source inspection is not a hardware test.

  if ((newState == HIGH) && (oldState == LOW) && (millis() - lowSignalStartTime < 500)) {
    myData.a = 1;
    // Send message via ESP-NOW
    esp_now_send(broadcastAddress, (uint8_t *) &myData, sizeof(myData));
  }else if ((newState == HIGH) && (oldState == LOW) && (millis() - lowSignalStartTime >= 500) && (millis() - lowSignalStartTime < 1000)) {
    myData.a = 2;
    // Send message via ESP-NOW
    esp_now_send(broadcastAddress, (uint8_t *) &myData, sizeof(myData));
  }else if ((newState == HIGH) && (oldState == LOW) && (millis() - lowSignalStartTime >= 1000) && (millis() - lowSignalStartTime < 1500)) {
    myData.a = 3;
    // Send message via ESP-NOW
    esp_now_send(broadcastAddress, (uint8_t *) &myData, sizeof(myData));
  }else if ((newState == HIGH) && (oldState == LOW) && (millis() - lowSignalStartTime >= 1500) && (millis() - lowSignalStartTime < 2000)) {
    myData.a = 4;
    // Send message via ESP-NOW
    esp_now_send(broadcastAddress, (uint8_t *) &myData, sizeof(myData));
  }else if ((newState == HIGH) && (oldState == LOW) && (millis() - lowSignalStartTime >= 2000) && (millis() - lowSignalStartTime < 2500)) {
    myData.a = 5;
    // Send message via ESP-NOW
    esp_now_send(broadcastAddress, (uint8_t *) &myData, sizeof(myData));
  }else if ((newState == HIGH) && (oldState == LOW) && (millis() - lowSignalStartTime >= 3000)) {
    myData.a = 6;
    // Send message via ESP-NOW
    esp_now_send(broadcastAddress, (uint8_t *) &myData, sizeof(myData));
  }
  myData.a=0;
  oldState = newState;
}
Open the exact source-level diagram
CheckHas the button just been released?The input changes from LOW to HIGH.
Action · YesCompare the press duration
CheckDoes it match a command band?The 2500–2999 ms gap does not match.
Action · YesSend the selected command
Result · NoClear the local payload; remember input state

More implementation detail: How the receiver selects a lighting action

Firmware flow

How the receiver selects a lighting action

Selected ESP-01S / ESP8266 receiver command dispatch.

Choose the first matching local or remote command. It may initialise the LEDs and change colour, call the strobe routine, change the effect flags or lower the output pin. Clear the remote selector after the branch.

The receiver snapshot is incomplete for standalone compilation. Local release conditions can take precedence over remote selectors because this is a first-match chain. No link-loss failsafe or tested output behaviour is established.

Read the implementation

loop() selected body · selected source lines 95–131. Source inspection is not a hardware test.

  if ((newState == HIGH) && (oldState == LOW) && (millis() - lowSignalStartTime < 500) || sel == 1) {  // FIXME: color change delay
    if (!ledInitialized) {
        for(int i=0; i<=NUM_LEDS; i++){
  strip.setPixelColor(i, strip.gamma32(strip.ColorHSV(huewa, 255, 255)));  // Set initial color
      strip.begin();
      strip.show();}
      digitalWrite(1, HIGH);
      ledInitialized = true;  // Set the flag to true
    }
    modeChange();  // Color Transition

  } else if ((newState == HIGH) && (oldState == LOW) && (millis() - lowSignalStartTime >= 650) || sel == 2) {  // FIXME: strobe effect trigger delay

    beameffect();  // Trigger Strobe Effect

  } else if (sel == 3) {

    flash = true;           // layermode
    glittereffect = false;  // glitter mode

  } else if (sel == 4) {

    glittereffect = true;
    flash = false;

  } else if (sel == 5) {

    glittereffect = false;  //all off
    flash = false;

  } else if (sel == 6) {
    digitalWrite(1, LOW);
  }

  // Set the last-read button state to the old state.
  sel = 0;
  oldState = newState;
Open the exact source-level diagram
Action · First branchColour command: initialise once, then change mode
Action · Otherwise second branchStrobe command: call the effect routine
Action · Otherwise selector 3, 4 or 5Effect command: change the effect flagsSelect flash, select glitter, or clear both flags.
Action · Otherwise selector 6Output command: lower the control pin
Result · No matchClear the selector; remember input state

Technical notes

A short packet instead of streaming effects

The sender measures a button hold and sends one command when the button returns HIGH. The receiver keeps the effect state locally, so a packet selects an action rather than carrying every LED update.

Duration bands and their boundary

The sender assigns commands 1–5 to successive ranges below 2500 ms, then command 6 at 3000 ms or more. The inspected version has no send branch for 2500–2999 ms. After the branch chain it clears the local payload, without transmitting a reset packet.

Receiver command dispatch

The first-match chain combines local button conditions with remote selectors. It can initialize the strip and raise GPIO1, advance the hue mode, call the strobe routine, switch flash/glitter flags, or lower GPIO1. Command 5 clears two effect flags; it is not an all-output shutdown. Command 6 lowers GPIO1 without clearing the LED initialization flag.

Scope of the source evidence

The transmitter includes ESP8266WiFi.h and both files use the ESP8266-era ESP-NOW API, correcting the old ESP32 label. The receiver snapshot omits declarations needed to compile it alone. No link-loss timeout or fail-safe is established by these files, and no hardware retest is claimed.

Engineering archiveArchive record

Project enquiries

Discuss your project.

Tell us what you’re developing and where you need engineering support.

Discuss your project