gabriel stacey-chartrand

Week 14: interface and application programming

The assignment for this week is to write an application that interfaces a user with a device. I built a browser-based control interface for the alarm clock board from week 06.

It's a single HTML file. It connects to the alarm clock board over MQTT using the same broker and topics from week 11.

Interface demo

Connecting to the board

The interface connects to the same HiveMQ public broker as the alarm clock board, using mqtt.js loaded from a CDN, without the need for a server.

<script src="https://cdnjs.cloudflare.com/ajax/libs/mqtt/5.10.1/mqtt.min.js"></script>
const BROKER = 'wss://broker.hivemq.com:8884/mqtt';

client = mqtt.connect(BROKER, {
  clientId: 'gsc-' + Math.random().toString(16).slice(2, 8),
  ...
});

The client ID is randomly generated on each page load. That way multiple browser tabs can be open at once without kicking each other off the broker.

The interface publishes and subscribes to the same three topics as the board:

Connection state is shown as a coloured dot in the header. Green when connected, red if something goes wrong.

connection status dot in the interface header

The interface

The interface has three sections: a live clock, a wake alarm panel, and a pomodoro timer.

the full interface

Clock

I wanted the clock display in the interface to look like the physical LED matrix: amber digits on a black background. The time comes from the browser's own clock, not from the board.

the clock panel, styled to look like the MAX7219 display

Wake alarm

The alarm section has a time picker and an on/off toggle. Setting a new time publishes to s_c/alarm/set. The toggle publishes "ON" or "OFF" to s_c/alarm/status.

The toggle also listens for incoming status messages from the board, so if the alarm is turned on or off using the physical button, the interface updates to reflect that.

the alarm section

Pomodoro timer

The pomodoro section has adjustable work and break durations, a start/pause/reset button, and a 32-segment progress bar (matching the amount of columns of LEDs in the MAX7219 display).

I made the progress bar 32 segments specifically because the physical display is 32 LED columns wide. The bar fills and drains the same way on both: a right-to-left shrinking bar during work, the full display pulsing during breaks.

the pomodoro section with a work session running

When the work timer runs out, the interface publishes BREAK_START:Xmin and switches to the break phase automatically. The board does the same thing on its end so both stay in sync.

Message log

There's a message log at the bottom that shows every MQTT message published or received, colour-coded by topic. It was useful during development for seeing exactly what was happening in real time, and I kept it in the final version.

the message log, showing published and received messages colour-coded by topic

Two-way sync

Both the interface and the board can trigger state changes. If I press the physical button to pause the pomodoro, the board publishes PAUSED to s_c/alarm/pomodoro, the interface receives it and updates. If I click pause in the interface, the same message goes out and the board reacts.

To avoid acting on messages it sent itself, the interface checks the current state before applying incoming messages. If the interface is already in work phase and receives WORK_START, it ignores it.

Problems

The trickiest issue was a phase transition bug. When a work timer ran out, the interface was sending a PAUSED message right before BREAK_START. The board was receiving PAUSED a fraction of a second after entering the break phase and immediately pausing it. The fix ended up being in the firmware: a 1.5 second guard that ignores PAUSED messages arriving right after a phase starts. I also cleaned up the interface's phase transition logic to avoid sending unnecessary messages.

A note on AI assistance

Like the firmware for networking and communications week, this interface was coded by Claude. I designed what it should be and what it should do. Claude wrote the implementation.

Here's roughly the brief I was giving, built up across several rounds:

ALARM CLOCK INTERFACE, DESIGN BRIEF

Stack
  Single HTML file. No framework, no build step.
  mqtt.js from CDN.
  Same broker and topics as the board (week 11).

Styling
  Match my Fab Academy documentation site exactly:
    background: #f9f2e5 (cream/paper)
    body font: Inconsolata, monospace
    headings: Montserrat italic bold, red (#c8102e)
    accents: blue (#1e22aa)
    borders: 2px solid black
  
  Clock panel: amber/yellow digits on black.
  Should look similar to the physical LED matrix.

Layout
  Three sections, in order:
    1. Live clock
    2. Wake alarm
    3. Pomodoro timer
  Message log at the bottom.
  Connection status dot in the header.

Wake alarm section
  Time selection for alarm and an on/off toggle.
  Wording on the toggle: "on" / "off" (not arm/disarm, not enable/disable).
  Setting a time publishes to s_c/alarm/set.
  Toggle publishes "ON" or "OFF" to s_c/alarm/status.
  If the board button changes alarm state, the interface should update too.

Pomodoro section
  Adjustable work and break durations. Defaults: 25 min work, 5 min break.
  One button that acts as start / pause / resume / reset depending on state.
  Progress bar: 32 segments, to match the 32 LED columns of the physical display.
    Work phase: segments drain left to right, amber colour.
    Break phase: all segments on, pulsing green.
    Paused: dimmed.

Client ID
  Randomly generated on each page load.
  So multiple browser tabs don't kick each other off the broker.

Two-way sync
  Any state change triggered by the board's physical button must update
  the interface. Any state change from the interface must update the board.
  Neither is the single source of truth.

The main iteration was around state sync. Getting the interface to correctly reflect button presses on the board, ignore its own echoes, and not send redundant state-change messages during phase transitions took a few rounds of testing with both the board and the message log running at the same time.

Source code

Download interface (.html)
Group page for week 14
Country roads...