Files
Baseball-display/src/main.cpp
T
2026-07-16 03:37:10 +02:00

173 lines
4.4 KiB
C++

#include <Arduino.h>
#include <WiFi.h>
#include <WebServer.h>
#include <FastLED.h>
#include <ArduinoJson.h>
#include <time.h>
#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(
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Baseball Display</title>
<style>
body { font-family: Arial, sans-serif; background: #111; color: #eee; padding: 24px; }
.card { max-width: 420px; margin: 0 auto; background: #222; padding: 20px; border-radius: 12px; }
label { display: block; margin-top: 10px; }
input, button { width: 100%; padding: 10px; margin-top: 6px; }
button { background: #4f46e5; color: white; border: none; border-radius: 8px; }
</style>
</head>
<body>
<div class="card">
<h1>Baseball Scoreboard</h1>
<form action="/update" method="post">
<label>Home score</label>
<input name="homeScore" value="0" />
<label>Away score</label>
<input name="awayScore" value="0" />
<label>Inning</label>
<input name="inning" value="1" />
<label>Outs</label>
<input name="outs" value="0" />
<label>Balls</label>
<input name="balls" value="0" />
<label>Strikes</label>
<input name="strikes" value="0" />
<button type="submit">Update display</button>
</form>
</div>
</body>
</html>
)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<LED_TYPE, LED_PIN, COLOR_ORDER>(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();
}
}