initial commit

This commit is contained in:
2025-09-06 04:47:33 -04:00
commit c2c59d8b0f
3 changed files with 354 additions and 0 deletions

348
src/led-controller-ota.ino Executable file
View File

@@ -0,0 +1,348 @@
#include <WiFi.h>
#include <ESPmDNS.h>
#include <WiFiUdp.h>
#include <ArduinoOTA.h>
#include <FastLED.h>
#include <secrets.h> // Requires a secrets.h file with WIFI_SSID, WIFI_PASSWORD, DEVICE_SECRET
#include <vector>
#include <memory> // For std::unique_ptr
#include <map> // 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<CRGB*, int> 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<template<uint8_t DATA_PIN, EOrder RGB_ORDER> 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<LED_TYPE, DATA_PIN, RGB_ORDER>(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<uint8_t DATA_PIN, EOrder RGB_ORDER = GRB>
using WS2811Strip = LEDStrip<WS2811, DATA_PIN, RGB_ORDER>;
template<uint8_t DATA_PIN, EOrder RGB_ORDER = GRB>
using WS2812BStrip = LEDStrip<WS2812B, DATA_PIN, RGB_ORDER>;
template<uint8_t DATA_PIN, EOrder RGB_ORDER = GRB>
using WS2813Strip = LEDStrip<WS2813, DATA_PIN, RGB_ORDER>;
template<uint8_t DATA_PIN, EOrder RGB_ORDER = GRB>
using APA104Strip = LEDStrip<APA104, DATA_PIN, RGB_ORDER>;
// Class for managing patterns (Single Responsibility, depends on IPattern abstraction)
class PatternManager {
private:
std::vector<std::unique_ptr<IPattern>> patterns_;
size_t currentPatternIndex_ = 0;
uint32_t lastSwitchTime_ = 0;
public:
void addPattern(std::unique_ptr<IPattern> pattern) {
patterns_.push_back(std::move(pattern));
}
template<typename LED_STRIP_TYPE>
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<WS2811, LED_DATA_PIN_A, RGB> ledStripA(LED_COUNT_A);
LEDStrip<WS2812B, LED_DATA_PIN_B, GRB> ledStripB(LED_COUNT_B);
LEDStrip<WS2812B, LED_DATA_PIN_C, GRB> 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<JugglePattern>());
patternManager.addPattern(std::make_unique<TracerDotPattern>());
patternManager.addPattern(std::make_unique<SinelonPattern>());
patternManager.addPattern(std::make_unique<BpmPattern>());
patternManager.addPattern(std::make_unique<ConfettiPattern>());
patternManager.addPattern(std::make_unique<Glow>());
patternManager.addPattern(std::make_unique<RainbowPattern>());
}
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();
}
}

3
src/secrets.h Executable file
View File

@@ -0,0 +1,3 @@
#define WIFI_SSID "technotic"
#define WIFI_PASSWORD "timewarp2022"
#define DEVICE_SECRET "al3110e"