You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ESP8266+WS2812B闹钟:触发时sunrise()函数无法运行求助

问题:ESP8266闹钟触发时sunrise()函数无法生效

使用ESP8266和WS2812B制作闹钟,期望闹钟时间触发时调用sunrise()函数实现日出灯光效果。该函数直接放入loop()中时运行正常,但在闹钟触发逻辑内调用时完全没有效果。

触发代码片段

if (AlarmData.AlarmOn[Current.Day])
{
  if (!AlarmActive)//do not enter this routine if alarm already active
  {
    if (Current.Hour == AlarmData.Hour[Current.Day])
    {
      if (Current.Minute == AlarmData.Minute[Current.Day])
      {
        if (Current.Second > 0 && Current.Second < 3)
        {
          AlarmActive = true;
          sunrise();
          alarmTriggerTime = micros();
          Serial.println("Its time for your alarm!");

        }
      }
    }
  }
}

完整项目代码

#include <ESP8266WiFi.h>
#include <ArduinoOTA.h>
#include <ESP8266WebServer.h>
#include <WiFiManager.h>
#include "EEPROMAnything.h"
#include "get_time.h"
#include "OTA.h"
#include <stdio.h>
#include <stdint.h>
#include "web_portal.h"
#include "ESP8266TimerInterrupt.h"
#include "restore_factory_settings.h"
#include "FastLED.h"

#define NUM_LEDS 60
#define DATA_PIN 5

// Define the array of leds
CRGB leds[NUM_LEDS];
#define timeZone 3

#define TenSecs 10000000
#define OneMin 60000000
#define TenMins 600000000

#define TIMER_INTERVAL_MS   1000

ESP8266Timer ITimer;

int timer;
int alarmTriggerTime;
struct CurrentTime Current;
bool AlarmActive;
struct AlarmDataStruct AlarmData;
int WiFiTimer;
char factory_settings_stored [3];
bool OneSecoundPassed;

void ICACHE_RAM_ATTR TimerHandler(void)
{
  OneSecoundPassed = true;
}

void setup() {

  Serial.begin(115200);
  Serial.println("Booting");
  EEPROM.begin(512);
  EEPROM_readAnything(150, factory_settings_stored);
  if (memcmp(&factory_settings_stored, "YES", 3) != 0)
  {
    restore_factory_settings();
  }
  FastLED.addLeds<WS2812B, DATA_PIN, GRB>(leds, NUM_LEDS);
  FastLED.setBrightness(0);
  WiFi.mode(WIFI_STA);
  WiFiManager wm;

  bool response;
  response = wm.autoConnect("AutoConnectAP"); // anonymous ap
  if (!response) {
    Serial.println("Failed to connect");
    // ESP.restart();
  }
  else {
    //if you get here you have connected to the WiFi
    Serial.println("Lets Go");
  }

  start_server();
  SetupOTA();
  setup_time(timeZone);
  Current = Current_Time();

  EEPROM_readAnything(100, AlarmData);
  Serial.print("Alarm set for ");
  Serial.print(AlarmData.Hour[Current.Day]);
  Serial.print(":");
  Serial.println(AlarmData.Minute[Current.Day]);
  timer = micros();
  WiFiTimer = timer;

  // Interval in microsecs
  if (ITimer.attachInterruptInterval(TIMER_INTERVAL_MS * 1000, TimerHandler))
  {
    Serial.println("Starting  ITimer OK, millis() = " + String(millis()));
  }
  else
  {
    Serial.println("Can't set ITimer correctly. Select another freq. or interval");
  }
}

void loop() {
  if (!AlarmActive)
  {
    ArduinoOTA.handle();
    handle_client();

    if ((micros() - WiFiTimer) > TenMins) // check if wifi connection lost and if so try to reconnect
    {
      if (WiFi.status() != WL_CONNECTED)
      {
        ESP.restart(); //try to reconnect rather than resetting
      }
      WiFiTimer = micros();
    }

    if (OneSecoundPassed)
    {
      updateLocalTime();
      OneSecoundPassed = false;
    }

    //update current hour from NTP server
    if ((micros() - timer) > TenMins)
    {
      Current = Current_Time();
      timer = micros();
    }

  }
  if (AlarmActive)
  {
    if ((micros() - alarmTriggerTime) > TenMins)
    {
      AlarmActive = false; //if alarm active for 10mins and no one switches it off then do it auto
    }
  }

  if (AlarmData.AlarmOn[Current.Day])
  {
    if (!AlarmActive)//do not enter this routine if alarm already active
    {
      if (Current.Hour == AlarmData.Hour[Current.Day])
      {
        if (Current.Minute == AlarmData.Minute[Current.Day])
        {
          if (Current.Second > 0 && Current.Second < 3)
          {
            AlarmActive = true;
            sunrise();
            alarmTriggerTime = micros();
            Serial.println("Its time for your alarm!");

          }
        }
      }
    }
  }

  else
  {
    if (!AlarmActive)
    {

    }
  }
}

void updateLocalTime () {
  Current.Second++;
  if (Current.Second >= 60)
  {
    Current.Second = 0;
    Current.Minute++;
    if (Current.Minute >= 60)
    {
      Current.Minute = 0;
      Current.Hour++;
      if (Current.Hour >= 24)
      {
        Current.Hour = 0;
        Current.Day++;
        if (Current.Day >= 7)
        {
          Current.Day = 0;
        }
      }
    }
  }
  char tempTime[6];
  if (Current.Minute < 10 && Current.Second < 10)
  {
    sprintf(tempTime, "0%d:0%d", Current.Minute, Current.Second);
  }
  else if (Current.Minute < 10)
  {
    sprintf(tempTime, "0%d:%d", Current.Minute, Current.Second);
  }
  else if (Current.Second < 10)
  {
    sprintf(tempTime, "%d:0%d", Current.Minute, Current.Second);
  }
  else
  {
    sprintf(tempTime, "%d:%d", Current.Minute, Current.Second);
  }

}

void sunrise() {
  static const uint8_t sunriseLength = 30; //(min)
  static const uint8_t interval = (sunriseLength * 60) / 256;
  static const uint8_t binterval = (sunriseLength * 60) / 256;

  // current gradient palette color index
  static uint8_t heatIndex = 0; // start out at 0
  static uint8_t brIndex = 0;

  // HeatColors_p is a gradient palette built in to FastLED
  // that fades from black to red, orange, yellow, white
  // feel free to use another palette or define your own custom one
  CRGB color = ColorFromPalette(HeatColors_p, heatIndex);
  fill_solid(leds, NUM_LEDS, color); // fill the entire strip with the current color
  EVERY_N_SECONDS(binterval) {
    if (brIndex < 255) {
      FastLED.setBrightness(brIndex);
      brIndex++;
    }
  }
  EVERY_N_SECONDS(interval) {
    if (heatIndex < 255) {
      heatIndex++;
    }
  }
  FastLED.show();
}

问题原因分析
  1. EVERY_N_SECONDS宏的工作机制:FastLED的EVERY_N_SECONDS宏需要反复调用包含它的函数才能生效。它内部会记录上次执行的时间,每次函数被调用时检查是否达到指定间隔,只有这样宏内的代码才会触发。当前闹钟触发时只调用了一次sunrise(),之后没有再调用,导致亮度和颜色索引的递增代码完全无法执行。
  2. 初始状态无可见效果:sunrise()内的静态变量heatIndex和brIndex初始值都是0,第一次调用时LED会被设置为全黑,之后没有后续调用更新状态,所以看起来函数完全没生效。
  3. AlarmActive状态下的逻辑缺失:当AlarmActive设为true后,loop()跳过了大部分常规逻辑,但也没有加入持续调用sunrise()的代码,导致日出效果无法持续运行。

解决方案

1. 调整变量结构与触发逻辑

将sunrise()内的静态变量改为全局变量,方便重置和状态检查;新增一个标志位控制日出效果的运行状态:

// 新增全局变量
bool isSunriseActive = false;
uint8_t heatIndex = 0;
uint8_t brIndex = 0;
const uint8_t sunriseLength = 30; //(min)
const uint8_t interval = (sunriseLength * 60) / 256;
const uint8_t binterval = (sunriseLength * 60) / 256;

修改闹钟触发代码,设置运行标志位而非直接调用一次sunrise():

if (Current.Second > 0 && Current.Second < 3)
{
  AlarmActive = true;
  isSunriseActive = true;
  // 重置日出效果的状态变量,避免上次运行的残留影响
  heatIndex = 0;
  brIndex = 0;
  alarmTriggerTime = micros();
  Serial.println("Its time for your alarm!");
}

2. 重构sunrise()函数

移除EVERY_N_SECONDS宏,改用millis()手动计时,确保每次调用都能检查时间间隔并更新状态:

void sunrise() {
  // 检查日出效果是否已完成
  if (heatIndex >= 255 && brIndex >= 255) {
    isSunriseActive = false;
    return;
  }

  // 设置当前颜色并填充LED
  CRGB color = ColorFromPalette(HeatColors_p, heatIndex);
  fill_solid(leds, NUM_LEDS, color);

  // 手动控制亮度递增
  static unsigned long lastBrUpdate = 0;
  if (brIndex < 255 && (millis() - lastBrUpdate) >= (binterval * 1000)) {
    FastLED.setBrightness(brIndex);
    brIndex++;
    lastBrUpdate = millis();
  }

  // 手动控制颜色索引递增
  static unsigned long lastHeatUpdate = 0;
  if (heatIndex < 255 && (millis() - lastHeatUpdate) >= (interval * 1000)) {
    heatIndex++;
    lastHeatUpdate = millis();
  }

  // 更新LED显示
  FastLED.show();
}

3. 在loop()中持续调用sunrise()

修改AlarmActive状态下的逻辑,确保日出效果持续运行,并在超时后重置状态:

if (AlarmActive)
{
  if ((micros() - alarmTriggerTime) > TenMins)
  {
    AlarmActive = false;
    isSunriseActive = false;
    // 重置LED状态
    heatIndex = 0;
    brIndex = 0;
    FastLED.setBrightness(0);
    FastLED.clear();
    FastLED.show();
  } else {
    // 持续运行日出效果
    if (isSunriseActive) {
      sunrise();
    }
  }
}

内容的提问来源于stack exchange,提问作者GkNx

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 09:05:42