diff --git a/README.md b/README.md index 96aabf8..db4d3d0 100644 --- a/README.md +++ b/README.md @@ -113,7 +113,7 @@ The second command should identify an RP2040 MicroPython board. Run these commands from the repository root. Supporting files and font assets are copied first; `main.py` is installed last as the automatic entry point. ```sh -mpremote connect auto fs cp configuration.py font_data.py font_renderer.py hardware.py launch.py lcd_1inch28.py live_display.py params.json qmi8658.py settings.py splash.py timing.py touch_drive.py font_data*.bin startup_splash.rgb565 : +mpremote connect auto fs cp configuration.py font_data.py font_renderer.py hardware.py launch.py lcd_1inch28.py live_display.py params.json qmi8658.py ready_screen.py settings.py splash.py timing.py touch_drive.py font_data*.bin startup_splash.rgb565 : mpremote connect auto fs cp main.py : mpremote connect auto reset ``` diff --git a/User Guide.md b/User Guide.md index 7ed232d..443268a 100644 --- a/User Guide.md +++ b/User Guide.md @@ -4,7 +4,7 @@ The following describes general operation of both the ``Track Session`` and ``Rest in Pits Session`` timer. * Upon start up a boot splash will be shown for 2 seconds. -* After which the ``Primary Screen`` will be shown, detailing the current track session duration in minutes, and the statement ``Ready``. This indicates the timer is ready to start. To start the ``Track Session`` or race, ``Swipe Down``. +* After which the ``Primary Screen`` will show ``Ready`` together with the saved track duration, rest duration, and effective Launch Mode state. ``Launch unavailable`` means the saved non-zero sensitivity could not be used because the IMU is unavailable; normal swipe-down timing still works. To start the ``Track Session`` or race, ``Swipe Down``. * After swiping down, ``Go`` will display briefly. If ``Launch Mode`` has been activated, ``Lights`` will be displayed while the timer measures a stationary baseline and waits for sufficient acceleration. * While waiting in ``Launch Mode``, double-tap to cancel and return to the ``Primary Screen``. The wait also cancels automatically after 30 seconds. * Upon starting, the ``Track Session`` timer count down will be displayed, and immediately commence. diff --git a/main.py b/main.py index fe19efe..95893f6 100644 --- a/main.py +++ b/main.py @@ -20,6 +20,7 @@ track_live_frame, ) from qmi8658 import QMI8658 +from ready_screen import ready_screen_lines from settings import load_configuration, persist_setting from timing import SessionTracker from touch_drive import Touch_CST816T @@ -29,10 +30,6 @@ USER_FILE = "user.json" PIT_SESSION_MSG = ["Cool down!", "Rest in pits"] -TRACK_SESSION_MSG = ["Ready", "Swipe DOWN to start"] -CLINE1 = [TRACK_SESSION_MSG[0], None, 90, 5, "white"] -CLINE2 = [TRACK_SESSION_MSG[1], None, 185, 1, "black"] -CLINE3 = ["message", None, 35, 3, "black"] PLINE1 = [PIT_SESSION_MSG[0], None, 88, 3, "white"] PLINE2 = [PIT_SESSION_MSG[1], None, 145, 2, "red"] @@ -102,8 +99,16 @@ def main(): rest_session = SessionTracker(duration_mins=rest_length, stype="rest", debug=True) while not launch: - CLINE3[0] = str(track_session.duration_mins) + "mins" - touch.ControlScreen(lcd, text_array=[CLINE1, CLINE2, CLINE3], back_colour="green") + touch.ControlScreen( + lcd, + text_array=ready_screen_lines( + track_minutes=track_session.duration_mins, + rest_minutes=rest_session.duration_mins, + sensitivity=configured_sensitivity, + imu_available=qmi8658 is not None, + ), + back_colour="green", + ) gesture = touch.GetGesture(lcd) if gesture == "left": diff --git a/ready_screen.py b/ready_screen.py new file mode 100644 index 0000000..65fff23 --- /dev/null +++ b/ready_screen.py @@ -0,0 +1,33 @@ +"""Ready-screen settings summary independent of display hardware.""" + + +def _format_sensitivity(sensitivity): + numeric = float(sensitivity) + if numeric == int(numeric): + return str(int(numeric)) + return str(numeric) + + +def launch_status(sensitivity, imu_available): + """Return the effective Launch Mode state shown before a session.""" + if float(sensitivity) <= 0: + return "Launch OFF" + if not imu_available: + return "Launch unavailable" + return "Launch {}g".format(_format_sensitivity(sensitivity)) + + +def ready_screen_lines( + track_minutes, + rest_minutes, + sensitivity, + imu_available, +): + """Build the complete Ready screen text layout.""" + return [ + ["Ready", None, 38, 4, "white"], + ["Track {}m".format(track_minutes), None, 100, 2, "black"], + ["Rest {}m".format(rest_minutes), None, 130, 2, "black"], + [launch_status(sensitivity, imu_available), None, 160, 2, "black"], + ["Swipe DOWN to start", None, 205, 1, "black"], + ] diff --git a/tests/test_ready_screen.py b/tests/test_ready_screen.py new file mode 100644 index 0000000..e756b88 --- /dev/null +++ b/tests/test_ready_screen.py @@ -0,0 +1,42 @@ +import math +import unittest + +from font_renderer import measure_text, pixel_height +from ready_screen import launch_status, ready_screen_lines + + +class ReadyScreenTests(unittest.TestCase): + def test_summary_contains_all_saved_settings_and_start_action(self): + lines = ready_screen_lines(20, 15, 0.5, True) + + self.assertEqual( + [ + "Ready", + "Track 20m", + "Rest 15m", + "Launch 0.5g", + "Swipe DOWN to start", + ], + [line[0] for line in lines], + ) + + def test_launch_status_reflects_effective_mode(self): + self.assertEqual("Launch OFF", launch_status(0, False)) + self.assertEqual("Launch 1g", launch_status(1, True)) + self.assertEqual("Launch unavailable", launch_status(1.25, False)) + + def test_every_line_fits_inside_the_round_display(self): + display_radius = 120 + widest_values = ready_screen_lines(60, 60, 1.75, False) + + for text, _, y_position, size, _ in widest_values: + text_center_y = y_position + (pixel_height(size) / 2) + distance_from_center = text_center_y - display_radius + visible_width = 2 * math.sqrt( + (display_radius ** 2) - (distance_from_center ** 2) + ) + self.assertLessEqual(measure_text(text, size), visible_width) + + +if __name__ == "__main__": + unittest.main()