-
-
Notifications
You must be signed in to change notification settings - Fork 0
Refactor shot clock state handling #3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -1,26 +1,14 @@ | ||||||
| #include <M5Unified.h> | ||||||
| #include <Preferences.h> | ||||||
|
|
||||||
| // Global variables | ||||||
| Preferences preferences; | ||||||
| const int DEFAULT_TIME = 60; // Constant default time | ||||||
| int defaultTime = DEFAULT_TIME; | ||||||
| int currentTime = defaultTime; | ||||||
| bool isRunning = false; | ||||||
| bool isTimeUp = false; | ||||||
| bool flashState = false; // For background flashing effect | ||||||
| unsigned long lastFlashTime = 0; // For controlling flash timing | ||||||
|
|
||||||
| // Screen layout constants | ||||||
| const int FONT_SIZE = 3; | ||||||
| const int TIME_X = 160; // Center X for landscape mode (320/2) | ||||||
| const int TIME_Y = 100; // Slightly above center for better layout with buttons | ||||||
| const int BUTTON_HEIGHT = 50; | ||||||
| const int BUTTON_WIDTH = 120; // Increased width since we only have 2 buttons now | ||||||
| const int BUTTON_Y = 180; // Moved up slightly for better layout | ||||||
| const int FLASH_INTERVAL = 500; // Flash interval in milliseconds | ||||||
|
|
||||||
| // Button position definitions | ||||||
|
|
||||||
| constexpr int DEFAULT_TIME = 60; | ||||||
| constexpr int FONT_SIZE = 3; | ||||||
| constexpr int TIME_X = 160; | ||||||
| constexpr int TIME_Y = 100; | ||||||
| constexpr int BUTTON_HEIGHT = 50; | ||||||
| constexpr int BUTTON_WIDTH = 120; | ||||||
| constexpr int BUTTON_Y = 180; | ||||||
| constexpr int FLASH_INTERVAL = 500; // milliseconds | ||||||
|
|
||||||
| struct Button { | ||||||
| int x; | ||||||
| int y; | ||||||
|
|
@@ -30,108 +18,149 @@ struct Button { | |||||
| uint16_t color; | ||||||
| }; | ||||||
|
|
||||||
| // Adjusted button positions for landscape mode with proper spacing | ||||||
| struct ShotClock { | ||||||
| int remaining = DEFAULT_TIME; | ||||||
| bool running = false; | ||||||
| bool timeUp = false; | ||||||
| bool flashState = false; | ||||||
| bool alarmPlayed = false; | ||||||
| unsigned long lastSecondTick = 0; | ||||||
| unsigned long lastFlashToggle = 0; | ||||||
|
|
||||||
| void reset() { | ||||||
| remaining = DEFAULT_TIME; | ||||||
| running = false; | ||||||
| timeUp = false; | ||||||
| flashState = false; | ||||||
| alarmPlayed = false; | ||||||
| const unsigned long now = millis(); | ||||||
| lastSecondTick = now; | ||||||
| lastFlashToggle = now; | ||||||
| } | ||||||
|
|
||||||
| void start() { | ||||||
| if (timeUp) { | ||||||
| return; | ||||||
| } | ||||||
| if (remaining <= 0) { | ||||||
| remaining = DEFAULT_TIME; | ||||||
| } | ||||||
| running = true; | ||||||
| lastSecondTick = millis(); | ||||||
| } | ||||||
|
|
||||||
| void stop() { | ||||||
| running = false; | ||||||
| } | ||||||
|
|
||||||
| bool update() { | ||||||
| const unsigned long now = millis(); | ||||||
| bool changed = false; | ||||||
|
|
||||||
| if (running && !timeUp) { | ||||||
| const unsigned long elapsed = now - lastSecondTick; | ||||||
| if (elapsed >= 1000) { | ||||||
| const int secondsPassed = elapsed / 1000; | ||||||
| lastSecondTick += secondsPassed * 1000; | ||||||
|
|
||||||
| if (secondsPassed > 0) { | ||||||
| remaining = (remaining > secondsPassed) ? remaining - secondsPassed : 0; | ||||||
| changed = true; | ||||||
| } | ||||||
|
|
||||||
| if (remaining == 0) { | ||||||
| setTimeUp(now); | ||||||
| changed = true; | ||||||
| } | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| if (timeUp && (now - lastFlashToggle) >= FLASH_INTERVAL) { | ||||||
| flashState = !flashState; | ||||||
| lastFlashToggle = now; | ||||||
| changed = true; | ||||||
| } | ||||||
|
|
||||||
| return changed; | ||||||
| } | ||||||
|
|
||||||
| private: | ||||||
| void setTimeUp(unsigned long now) { | ||||||
| running = false; | ||||||
| timeUp = true; | ||||||
| flashState = true; | ||||||
| lastFlashToggle = now; | ||||||
| if (!alarmPlayed) { | ||||||
| M5.Speaker.tone(1000, 1000); | ||||||
| alarmPlayed = true; | ||||||
| } | ||||||
| } | ||||||
| }; | ||||||
|
|
||||||
| Button startButton = {20, BUTTON_Y, BUTTON_WIDTH, BUTTON_HEIGHT, "START", GREEN}; | ||||||
| Button stopButton = {180, BUTTON_Y, BUTTON_WIDTH, BUTTON_HEIGHT, "STOP", RED}; | ||||||
| Button resetButton = {180, BUTTON_Y, BUTTON_WIDTH, BUTTON_HEIGHT, "RESET", BLUE}; // Same position as STOP button | ||||||
| Button resetButton = {180, BUTTON_Y, BUTTON_WIDTH, BUTTON_HEIGHT, "RESET", BLUE}; | ||||||
|
|
||||||
| ShotClock shotClock; | ||||||
|
|
||||||
| // Forward declarations | ||||||
| void updateDisplay(); | ||||||
| void drawButtons(); | ||||||
| void drawButton(const Button& btn, const char* label); | ||||||
| void handleTouch(const m5::touch_detail_t& touch); | ||||||
| bool isInButton(const m5::touch_detail_t& touch, const Button& btn); | ||||||
| void resetTimer(); | ||||||
|
|
||||||
| void setup() { | ||||||
| // Initialize M5Stack | ||||||
| auto cfg = M5.config(); | ||||||
| cfg.internal_imu = false; // Disable IMU as it's not needed | ||||||
| cfg.internal_rtc = false; // Disable RTC as it's not needed | ||||||
| cfg.internal_mic = false; // Disable microphone as it's not needed | ||||||
| cfg.internal_spk = true; // Enable speaker for buzzer | ||||||
| cfg.internal_imu = false; | ||||||
| cfg.internal_rtc = false; | ||||||
| cfg.internal_mic = false; | ||||||
| cfg.internal_spk = true; | ||||||
| M5.begin(cfg); | ||||||
|
|
||||||
| // Initialize display | ||||||
| M5.Display.setRotation(1); // Set rotation so buttons are at the bottom | ||||||
| M5.Display.setRotation(1); | ||||||
| M5.Display.setTextSize(FONT_SIZE); | ||||||
| M5.Display.setTextColor(WHITE, BLACK); | ||||||
| M5.Display.setTextDatum(MC_DATUM); | ||||||
|
|
||||||
| // Clear and initialize preferences | ||||||
| preferences.begin("shotclock", false); | ||||||
| preferences.clear(); // Clear any stored preferences | ||||||
| defaultTime = DEFAULT_TIME; | ||||||
| currentTime = defaultTime; | ||||||
|
|
||||||
| // Initialize display | ||||||
|
|
||||||
| shotClock.reset(); | ||||||
| updateDisplay(); | ||||||
| drawButtons(); | ||||||
| } | ||||||
|
|
||||||
| void loop() { | ||||||
| M5.update(); | ||||||
| auto t = M5.Touch.getDetail(); | ||||||
| if (t.isPressed()) { | ||||||
| handleTouch(t); | ||||||
|
|
||||||
| auto touch = M5.Touch.getDetail(); | ||||||
| if (touch.isPressed()) { | ||||||
| handleTouch(touch); | ||||||
| } | ||||||
|
|
||||||
| if (isRunning && !isTimeUp) { | ||||||
| currentTime--; | ||||||
| if (currentTime <= 0) { | ||||||
| currentTime = 0; | ||||||
| isTimeUp = true; | ||||||
| isRunning = false; | ||||||
| M5.Speaker.tone(1000, 1000); // Time up sound | ||||||
| lastFlashTime = millis(); // Initialize flash timing | ||||||
| flashState = true; // Start with red background | ||||||
| } | ||||||
|
|
||||||
| if (shotClock.update()) { | ||||||
| updateDisplay(); | ||||||
| } | ||||||
|
|
||||||
| // Handle background flashing when time is up | ||||||
| if (isTimeUp) { | ||||||
| unsigned long currentTime = millis(); | ||||||
| if (currentTime - lastFlashTime >= FLASH_INTERVAL) { | ||||||
| flashState = !flashState; // Toggle flash state | ||||||
| lastFlashTime = currentTime; | ||||||
| updateDisplay(); | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| delay(1000); // Screen update interval | ||||||
| } | ||||||
|
|
||||||
| void resetTimer() { | ||||||
| currentTime = defaultTime; | ||||||
| isTimeUp = false; | ||||||
| isRunning = false; | ||||||
| flashState = false; | ||||||
| updateDisplay(); | ||||||
| delay(10); | ||||||
|
||||||
| delay(10); | |
| delay(100); |
Copilot
AI
Jan 8, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The label is redundantly extracted from the same button that was just assigned to activeButton. Since activeButton.label already contains the correct label, this line can be simplified to const char* label = activeButton.label;
| const char* label = shotClock.timeUp ? resetButton.label : stopButton.label; | |
| const char* label = activeButton.label; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The drift correction logic assumes
secondsPassedis always 1 in normal operation. If the device experiences significant lag (e.g., >2 seconds),remainingcould underflow. Consider adding a bounds check:remaining = max(0, remaining - secondsPassed)or limitingsecondsPassedto prevent unexpected behavior.