#include #include #include #include #include #include #define LED_PIN 13 #define LED_TYPE WS2812B #define COLOR_ORDER GRB #define NUM_LEDS 120 #define BRIGHTNESS 64 CRGB leds[NUM_LEDS]; WebServer server(80); struct ScoreState { int homeScore = 0; int awayScore = 0; int inning = 1; int outs = 0; int balls = 0; int strikes = 0; int hour = 12; int minute = 0; unsigned long gameStartMs = 0; }; ScoreState state; void setPixel(int index, CRGB color) { if (index >= 0 && index < NUM_LEDS) { leds[index] = color; } } void renderDigit(int digit, int startIndex, CRGB color) { static const bool segments[10][7] = { {1, 1, 1, 1, 1, 1, 0}, {0, 1, 1, 0, 0, 0, 0}, {1, 1, 0, 1, 1, 0, 1}, {1, 1, 1, 1, 0, 0, 1}, {0, 1, 1, 0, 0, 1, 1}, {1, 0, 1, 1, 0, 1, 1}, {1, 0, 1, 1, 1, 1, 1}, {1, 1, 1, 0, 0, 0, 0}, {1, 1, 1, 1, 1, 1, 1}, {1, 1, 1, 1, 0, 1, 1}, }; for (int i = 0; i < 7; ++i) { if (segments[digit][i]) { setPixel(startIndex + i, color); } else { setPixel(startIndex + i, CRGB::Black); } } } void renderScoreboard() { fill_solid(leds, NUM_LEDS, CRGB::Black); int home = state.homeScore; int away = state.awayScore; int inning = state.inning; int outs = state.outs; int balls = state.balls; int strikes = state.strikes; renderDigit(home / 10, 0, CRGB::Green); renderDigit(home % 10, 7, CRGB::Green); renderDigit(away / 10, 14, CRGB::Red); renderDigit(away % 10, 21, CRGB::Red); renderDigit(inning / 10, 28, CRGB::Blue); renderDigit(inning % 10, 35, CRGB::Blue); renderDigit(outs, 42, CRGB::Orange); renderDigit(balls, 49, CRGB::Yellow); renderDigit(strikes, 56, CRGB::Magenta); FastLED.show(); } void handleRoot() { String html = R"html( Baseball Display

Baseball Scoreboard

)html"; server.send(200, "text/html", html); } void handleUpdate() { if (server.hasArg("homeScore")) state.homeScore = server.arg("homeScore").toInt(); if (server.hasArg("awayScore")) state.awayScore = server.arg("awayScore").toInt(); if (server.hasArg("inning")) state.inning = server.arg("inning").toInt(); if (server.hasArg("outs")) state.outs = server.arg("outs").toInt(); if (server.hasArg("balls")) state.balls = server.arg("balls").toInt(); if (server.hasArg("strikes")) state.strikes = server.arg("strikes").toInt(); renderScoreboard(); server.sendHeader("Location", "/"); server.send(303); } void setup() { Serial.begin(115200); delay(1000); FastLED.addLeds(leds, NUM_LEDS); FastLED.setBrightness(BRIGHTNESS); FastLED.clear(); FastLED.show(); WiFi.mode(WIFI_AP); WiFi.softAP("BaseballDisplay", "scoreboard"); IPAddress ip = WiFi.softAPIP(); Serial.print("AP IP: "); Serial.println(ip); server.on("/", HTTP_GET, handleRoot); server.on("/update", HTTP_POST, handleUpdate); server.begin(); state.gameStartMs = millis(); renderScoreboard(); } void loop() { server.handleClient(); static unsigned long lastTimeUpdate = 0; if (millis() - lastTimeUpdate > 1000) { lastTimeUpdate = millis(); state.minute = (state.minute + 1) % 60; if (state.minute == 0) { state.hour = (state.hour + 1) % 24; } renderScoreboard(); } }