Close

The two button mode-set UI

ken-yapKen Yap wrote 03/14/2025 at 22:01 • 8 min read • Like

The two button mode-set user interface is a design to allow changing many settings with only two buttons. It is used in many devices, even my ancient Casio F91-W watch from 45 years ago used it. Here is a simple example used in a 4 digit clock:

Indication Button 1 Button 2 Notes
Normal display Time displayed Go to set hour Leaving normal display may require holding down the button for a while so that it's hard to activate accidentally
Set hour Hour displayed Go to set minute Advance hour
Set minute Minute displayed Go to set brightness Advance minute
Set brightness All 8s displayed Go to normal display Change brightness In some implementations pressing both button 1 and 2 together does this

As you can see, button 1 loops through the various modes, and button 2 is an increment button, which also loops on wrap around. Sometimes a third button is provided to decrement the counter, but I feel this detracts from simplicity. With autorepeat it doesn't take long to wrap around anyway.

Notes

By contrast, the clock chips from National Semiconductor used a multi-button scheme. On the MM5316, I count: Fast set, Slow set, Seconds display, Alarm display, Sleep display, Alarm off, and Snooze. Fast set and slow set require estimating how close one is to the target to change buttons instead of setting the relevant counter directly. A bedside clock has room for more buttons, but I feel that there are too many buttons.

Here is a more elaborate example where the clock stores the date also. Let's put aside how or when the date is displayed. It could be alternately with the time as some old clock chips did, or a separate display:

Indication Button 1 Button 2 Notes
Normal display Time displayed Go to set hour
Set hour Hour displayed Go to set minute Advance hour
Set minute Minute displayed Go to date Advance minute
Date Month and day displayed Go to set month
Set month Month displayed Go to set day Advance month
Set day Day displayed Go to set brightness Advance day
Set brightness All 8s displayed Go to normal display Change brightness

Here is another elaborate example for an alarm clock radio with sleep timer which is turned on by an alarm match, or by manually setting to non-zero. Notice that button 2 has a function in normal display mode:

Indication Button 1 Button 2 Notes
Normal display Time displayed Go to set hour Turns off alarm by zeroing sleep timer
Set hour Hour displayed Go to set minute Advance hour
Set minute Minute displayed Go to alarm time Advance minute
Alarm time and status Alarm time Go to set alarm hour Toggle alarm Colon indicates if alarm active
Set alarm hour Alarm hour displayed with steady colon Go to set alarm minute Advance alarm hour
Set alarm minute Alarm minute displayed with steady colon Go to set sleep minute Advance alarm minute
Set sleep minute Sleep minute displayed without colon Go to normal display Decrease sleep minute If at zero, will wrap around to 59 and turn on alarm (sleep output)

The alarm is actually the radio, whose power is switched by the sleep output, doing double duty as an alarm and a sleep radio. Notice that we arrange it so that the display also indicates which mode it is in, to avoid having more indicators. This is not so important when the display technology can provide indicators, like LCDs, and LED raster displays.

The implementation is quite straightforward. It consists of a switch statement for both buttons, and a switch statement for button 2:

static void switchaction()
{
        switch (swstate & B_BOTH) {
        case B_0:
                mode++;
                if (mode >= END_MODE)
                        mode = NORMAL_MODE;
                break;
        case B_1:       // also cancels alarm in normal mode
                switch (mode) {
                case NORMAL_MODE:
                        if (sleep_minutes > 0)
                                sleep_zero();
                        return;
                case TIME_HOURS:
                        mains_time.hours++;
                        if (mains_time.hours >= 24)
                                mains_time.hours = 0;
                        break;
                case TIME_MINS:
                        mains_time.seconds = 0;
                        mains_time.minutes++;
                        if (mains_time.minutes >= 60)
                                mains_time.minutes = 0;
                        break;
                case ALARM_SET:
                        alarm_time.on = !alarm_time.on;
                        break;
                case ALARM_HOURS:
                        alarm_time.hours++;
                        if (alarm_time.hours >= 24)
                                alarm_time.hours = 0;
                        break;
                case ALARM_MINS:
                        alarm_time.minutes++;
                        if (alarm_time.minutes >= 60)
                                alarm_time.minutes = 0;
                        break;
                case SLEEP_MINS:
                        if (sleep_minutes == 0)
                                sleep_on();
                        else
                                sleep_sub1();
                        break;
                }
                display_update();
                alarm_check();
                break;
        }
        if (mode == NORMAL_MODE)
                button_timeout = 0;
        else
                button_timeout = BUTTON_TIMEOUT;
}

The mode-set user interface is not suitable for all devices. It's best for those where setting is rarely done. Its greatest advantages are that it only requires two buttons and the model is easy to understand. It's not so good when multiple settings have to be adjusted around the same time. For example you wouldn't want it for a DSO panel.

Like

Discussions

danjovic wrote 6 days ago point

Nowadays all my clocks run a state machine that relies on a  combination of uniquely generated events of pulse and long press on it's buttons. The last one used a single button yet allowing me to setup things like colors and hourly beep.

My main loop basically updates at every 10ms doing the following tasks

- scan the button (s)

- run the state machine

- retrieve time from RTC

- update the display

https://hackaday.io/project/202436-cis-4

The button scan count  the time the button was pressed. when its equal a long press it will return a long press. It will only happen once.

When the button is not pressed the scan function check the time the button was pressed. It should be more than a debounce period and less than a long press. If both conditions are met it returns a pulse (short press)

That allow me to simplify my IHMs a lot at a expense of 1 byte per switch.

  Are you sure? yes | no