Initial commit
This commit is contained in:
+172
@@ -0,0 +1,172 @@
|
||||
#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();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user