_settings_loop() never handled EVENT_TIMEOUT, so leaving the watch
face parked in settings mode (4Hz tick, blinking abbreviation) with
no button presses meant it stayed there indefinitely instead of
returning to the clock display like every other face's settings mode
does on timeout.
Factor the existing EVENT_MODE_BUTTON_UP transition logic into
_exit_settings_mode() and reuse it for EVENT_TIMEOUT.
Improvements:
- The name of the time zone is displayed for a brief movement when cycling through the selected time zones.
- The face now comes with pre-selected zones to show-case its functionality: Seattle, New York, UTC, Shanghai, and Tokyo.
_monitor_display() used %C (wide character, expects wint_t) instead
of %c (plain char/int) to print the X/Y/Z axis label. Every other
single-character display in the codebase uses %c; on the embedded
build, without wide-char support in the C library, %C is not a
valid conversion for the char actually being passed.
state->settings was malloc'd in lis2dw_monitor_face_setup() outside
the guard that only runs on first allocation of the face's context.
setup() is called again every time the watch wakes from deep sleep
(movement.c's app_setup() re-runs watch_faces[i].setup() for every
face on wake), so each wake cycle allocated a new settings array and
abandoned the previous pointer with no matching free.
Since NUM_SETTINGS is a compile-time constant, make settings a
fixed-size array embedded directly in lis2dw_monitor_state_t instead
of a separately malloc'd pointer, so it's allocated exactly once
along with the rest of the state and never leaks.
This watch face monitors daily hydration by tracking and logging water intake.
In tracking mode: Display current water intake and percentage
- Alarm button: +100ml (or configured glass size)
- Light button: -100ml (or configured glass size)
- Alarm long press: Display deviation from estimate
- Light long press: Switch to settings mode
- Alarm really long press: Switch to log mode
In settings mode: Configure glass size, daily goal, wake time, sleep time, alert interval
- Light button: Switch to next setting
- Alarm button: Advance current setting
- Alarm long press: Reset to default value
- Mode button: Switch to tracking mode
In log mode: Display log entries of water intake, date and deviation from goal
- Alarm button: Switch to next log entry
- Light button: Cycle through intake, date and deviation
- Mode button: Switch to tracking mode
Background tasks:
- Automatic reset at wake time
- Alert if intake below estimate at sleep time
- Alert at interval if intake below estimate, waking the watch if asleep
_increment_date() kept its own copy of the days-per-month table
and leap-year check, duplicating _days_in_month() in the same
file. Two sources of truth is how the July day-count typo went
unnoticed. Call _days_in_month() directly instead, leaving one
table in the file.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PSucp94GA31sshe5AjvSzQ
date_time.unit.day is a 5-bit bitfield (max 31). Incrementing 31
directly in the bitfield wrapped to 0 via truncation before the
day > days_in_month bounds check ever ran, so the overflow was
never caught. The result decoded as "day 0" of the current month,
i.e. the last day of the previous month, silently rolling the
displayed date backwards (e.g. Aug 31 -> Jul 31 -> Jun 30).
Compute the incremented day in a plain uint8_t local first, compare
it against the month length, and only then write the final value
into the bitfield.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PSucp94GA31sshe5AjvSzQ
The local days_in_month table in _increment_date() listed July as
having 30 days instead of 31, diverging from the correct table in
_days_in_month(). This made July 31 unreachable when incrementing
the day field in settings mode.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PSucp94GA31sshe5AjvSzQ
The Frq page used remainderf to extract the fractional part of the
correction value, but remainderf rounds to the nearest integer and
returns negative results when the fraction exceeds 0.5. This caused
values like 0.965 ppm to render as " 0-350" instead of " 09650".
Switch to fmodf, matching the DELtA page above.
Python is needed to run uf2conv, so the final build step will fail
without it. Most folks will have a global Python to fall back to, but
this change fixes the build for those who don't, and makes the Nix
environment more complete.
With double tap enabled, one had to tap not too fast on
the watch to increment the minutes regularly. Otherwise,
some taps would be discarded.
This made using the countdown face without looking at the
screen for acknowledgement of taps impossible.
It was also impossible to increment minutes quickly, every second tap
would be discarded.