commit c2c59d8b0f7632cb43381d0b24fd66c3f294510e Author: Alexander Zinn Date: Sat Sep 6 04:47:33 2025 -0400 initial commit diff --git a/README.md b/README.md new file mode 100644 index 0000000..3eccf53 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# LED CONTROLLER OTA + +This is the repository for the led controller that is in my office diff --git a/src/led-controller-ota.ino b/src/led-controller-ota.ino new file mode 100755 index 0000000..078cc3d --- /dev/null +++ b/src/led-controller-ota.ino @@ -0,0 +1,348 @@ +#include +#include +#include +#include +#include +#include // Requires a secrets.h file with WIFI_SSID, WIFI_PASSWORD, DEVICE_SECRET + +#include +#include // For std::unique_ptr +#include // For std::map + +#define STARTUP_DELAY 2000 +#define SERIAL_BAUD_RATE 115200 +#define LED_DATA_PIN_A 2 +#define LED_DATA_PIN_B 4 +#define LED_DATA_PIN_C 16 +#define LED_COUNT_A 32 +#define LED_COUNT_B 300 +#define LED_COUNT_C 150 +#define LED_BRIGHTNESS 125 +#define LED_PATTERN_DURATION 90 // seconds +#define FRAMES_PER_SECOND 80 + +const char* wifiSsid = WIFI_SSID; +const char* wifiPassword = WIFI_PASSWORD; +const char* hostname = "side-desk"; +const char* deviceSecret = DEVICE_SECRET; + +uint8_t gHue = 0; +uint32_t lastHueUpdate = 0; + +class IPattern { +public: + virtual ~IPattern() = default; + virtual void apply(CRGB* leds, uint16_t ledCount) = 0; + virtual void update() {} +}; + +class RainbowPattern : public IPattern { +public: + void apply(CRGB* leds, uint16_t ledCount) override { + fill_rainbow(leds, ledCount, gHue, 52); + } +}; + +class ConfettiPattern : public IPattern { +public: + void apply(CRGB* leds, uint16_t ledCount) override { + fadeToBlackBy(leds, ledCount, 20); + int pos = random16(ledCount); + leds[pos] += CHSV(gHue + random8(64), 200, 255); + } +}; + +class SinelonPattern : public IPattern { +public: + void apply(CRGB* leds, uint16_t ledCount) override { + fadeToBlackBy(leds, ledCount, 20); + int pos = beatsin16(13, 0, ledCount - 1); + leds[pos] += CHSV(gHue, 255, 192); + } +}; + +class BpmPattern : public IPattern { +public: + void apply(CRGB* leds, uint16_t ledCount) override { + uint8_t BeatsPerMinute = 136; + CRGBPalette16 palette = PartyColors_p; + uint8_t beat = beatsin8(BeatsPerMinute, 64, 255); + for (int i = 0; i < ledCount; i++) { + leds[i] = ColorFromPalette(palette, gHue + (i * 2), beat - gHue + (i * 10)); + } + } +}; + +class JugglePattern : public IPattern { +public: + void apply(CRGB* leds, uint16_t ledCount) override { + fadeToBlackBy(leds, ledCount, 20); + uint8_t dothue = 0; + for (int i = 0; i < 8; i++) { + leds[beatsin16(i + 7, 0, ledCount - 1)] |= CHSV(dothue, 200, 255); + dothue += 32; + } + } +}; + +class TracerDotPattern : public IPattern { +private: + std::map stripPositions; +public: + void apply(CRGB* leds, uint16_t ledCount) override { + fadeToBlackBy(leds, ledCount, 30); + + int& currentPos = stripPositions[leds]; + if (currentPos >= ledCount) currentPos = 0; + + leds[currentPos] = CRGB::Blue; + currentPos++; + } +}; + +class Glow : public IPattern { +private: + uint32_t lastFadedAt; + static uint8_t hue; + +public: + void apply(CRGB* leds, uint16_t ledCount) override { + uint32_t now = millis(); + hue = now % 255; + + if (!lastFadedAt) { + + fadeToBlackBy(leds, ledCount, 100); + + lastFadedAt = now; + + return; + } + + if (now - lastFadedAt > 2000) { + for (int i = 0; i < ledCount; i++) { + // fade everything out + leds->fadeToBlackBy(40); + + // let's set an led value + leds[i] = CHSV(hue++, 255, 255); + + FastLED.delay(33); + } + } + } +}; + +uint8_t Glow::hue = 0; + +// Generic LED Strip class that can work with any FastLED-supported LED type +template class LED_TYPE, uint8_t DATA_PIN, EOrder RGB_ORDER = GRB> +class LEDStrip { +private: + CRGB* leds_; + uint16_t ledCount_; + bool initialized_ = false; + +public: + LEDStrip(uint16_t ledCount) + : ledCount_(ledCount) { + leds_ = new CRGB[ledCount_]; + } + + ~LEDStrip() { + delete[] leds_; + } + + void initialize() { + if (!initialized_) { + FastLED.addLeds(leds_, ledCount_).setCorrection(TypicalLEDStrip); + FastLED.setBrightness(LED_BRIGHTNESS); + clear(); + show(); + initialized_ = true; + } + } + + void clear() { + fill_solid(leds_, ledCount_, CRGB::Black); + } + + void show() { + if (initialized_) { + FastLED.show(); + } + } + + CRGB* getLeds() { + return leds_; + } + uint16_t getLedCount() { + return ledCount_; + } + bool isInitialized() { + return initialized_; + } +}; + +// Convenience typedefs for common LED types +template +using WS2811Strip = LEDStrip; + +template +using WS2812BStrip = LEDStrip; + +template +using WS2813Strip = LEDStrip; + +template +using APA104Strip = LEDStrip; + +// Class for managing patterns (Single Responsibility, depends on IPattern abstraction) +class PatternManager { +private: + std::vector> patterns_; + size_t currentPatternIndex_ = 0; + uint32_t lastSwitchTime_ = 0; +public: + void addPattern(std::unique_ptr pattern) { + patterns_.push_back(std::move(pattern)); + } + + template + void applyCurrent(LED_STRIP_TYPE& strip) { + if (!patterns_.empty()) { + patterns_[currentPatternIndex_]->apply(strip.getLeds(), strip.getLedCount()); + } + } + + void update() { + if (!patterns_.empty()) { + patterns_[currentPatternIndex_]->update(); + } + } + + void nextPattern() { + if (!patterns_.empty()) { + currentPatternIndex_ = (currentPatternIndex_ + 1) % patterns_.size(); + } + } + + bool shouldSwitchPattern() { + uint32_t now = millis(); + if (now - lastSwitchTime_ >= LED_PATTERN_DURATION * 1000) { + lastSwitchTime_ = now; + return true; + } + return false; + } +}; + +// Class for OTA and WiFi management (Single Responsibility; renamed to avoid library conflict) +class OTAManager { +public: + void initialize() { + connectWiFi(); + setupOTA(); + } + + void handle() { + ArduinoOTA.handle(); + } + +private: + void connectWiFi() { + WiFi.mode(WIFI_STA); + WiFi.begin(wifiSsid, wifiPassword); + while (WiFi.waitForConnectResult() != WL_CONNECTED) { + Serial.println("WiFi Connection failed. Rebooting..."); + delay(5000); + ESP.restart(); + } + Serial.println("WiFi Connected"); + Serial.print("IP address: "); + Serial.println(WiFi.localIP()); + } + + void setupOTA() { + ArduinoOTA.setHostname(hostname); + ArduinoOTA.setPassword(deviceSecret); + ArduinoOTA.onStart([]() { + String type = (ArduinoOTA.getCommand() == U_FLASH) ? "sketch" : "filesystem"; + Serial.println("Starting update [" + type + "]"); + }); + ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) { + Serial.printf("Progress: %u%%\r", (progress / (total / 100))); + }); + ArduinoOTA.onEnd([]() { + Serial.println("\nEnd"); + }); + ArduinoOTA.onError([](ota_error_t error) { + Serial.printf("Error [%u]: ", error); + switch (error) { + case OTA_AUTH_ERROR: Serial.println("Auth Failed"); break; + case OTA_BEGIN_ERROR: Serial.println("Begin failed"); break; + case OTA_CONNECT_ERROR: Serial.println("Connect failed"); break; + case OTA_RECEIVE_ERROR: Serial.println("Receive failed"); break; + case OTA_END_ERROR: Serial.println("End failed"); break; + default: Serial.println("Unknown failure"); + } + }); + ArduinoOTA.begin(); + Serial.println("OTA Ready"); + } +}; + +// Instances +LEDStrip ledStripA(LED_COUNT_A); +LEDStrip ledStripB(LED_COUNT_B); +LEDStrip ledStripC(LED_COUNT_C); +PatternManager patternManager; +OTAManager otaManager; + +void setup() { + delay(STARTUP_DELAY); + Serial.begin(SERIAL_BAUD_RATE); + delay(STARTUP_DELAY); + + // Serial.println("Initializing ota..."); + otaManager.initialize(); + + Serial.println("Initializing led strip..."); + ledStripA.initialize(); + ledStripB.initialize(); + ledStripC.initialize(); + + // Add patterns (extensible) + patternManager.addPattern(std::make_unique()); + patternManager.addPattern(std::make_unique()); + patternManager.addPattern(std::make_unique()); + patternManager.addPattern(std::make_unique()); + patternManager.addPattern(std::make_unique()); + patternManager.addPattern(std::make_unique()); + patternManager.addPattern(std::make_unique()); +} + +void loop() { + otaManager.handle(); + + // Apply current pattern + patternManager.applyCurrent(ledStripA); + patternManager.applyCurrent(ledStripB); + patternManager.applyCurrent(ledStripC); + ledStripA.show(); + ledStripB.show(); + ledStripC.show(); + FastLED.delay(1000 / FRAMES_PER_SECOND); + + // Periodic updates + if (millis() - lastHueUpdate >= 1000) { + gHue++; + lastHueUpdate = millis(); + } + + patternManager.update(); + + if (patternManager.shouldSwitchPattern()) { + patternManager.nextPattern(); + } +} diff --git a/src/secrets.h b/src/secrets.h new file mode 100755 index 0000000..24dd5c9 --- /dev/null +++ b/src/secrets.h @@ -0,0 +1,3 @@ +#define WIFI_SSID "technotic" +#define WIFI_PASSWORD "timewarp2022" +#define DEVICE_SECRET "al3110e" \ No newline at end of file