Hacking the Timex m851

Tavis Ormandy

$Id: a07cf90837a3c4373b82d6724b97593810766af7 $

This post is part of a series on my quest to find the perfect watch.

Intro

Timex m851

Take a look at this watch, it’s just some boring watch for runners, right?

Nope, I think this might be the best ultra-low power consumer digital watch ever produced!

Let me explain…

Specifications

This is the Timex m851. It uses an 8-bit Seiko SC188 CPU, has 48KB of ROM, 2KB of RAM and a 42x11 dot matrix main display.

The cpu is designed for ultra-low power operation - a single battery can last 3 years!

This is a big selling point for me, I don’t think they’re making consumer watches like this any more.

Timex USB Timex USB

Those pins on the side? That’s USB. Naturally, you can synchronize things like appointments, and so on (there’s a neat linux library for that).

Here’s where it get’s interesting… this thing has an SDK, and it is surprisingly good!

You can just $ cc helloworld.c, and upload it to your watch.

I didn’t really think I would be enamored with this watch at first – the Windows XP-era wizard Timex provided for development was… painful. However, it turned out it was just driving a UNIX-like toolchain behind the scenes.

After pulling that out, hacking this started to become pretty fun!

Makefile Hello World

WatchApps

Just want to see some code? No problem, I’ve uploaded a hello world and a Makefile to build it to github:

https://github.com/taviso/timex/

If you want to browse the full manual, there are some links below.

You can do some fun things with this, people wrote all kinds of games, utilities, and tools.

If you want a TL;DR of development, I’ll try!

Design

States and Modes

You need to split your application into states.

A state is just a way to swap code in and out as needed. When you switch states, the active code is discarded and the new code is swapped in.

You could write an app that uses up to ~30kb of code and/or data, but there is only ~2kb of RAM. This is why you need to use states, there is no paging and you couldn’t fit that in!

There is room for some common code that all your states share, and of course there is space for variables, along with a database API for persistent data.

The first few states are reserved for handling common events. The rest you can use for anything you want - you just ask the kernel to switch you whenever you need it!

if (foo > bar) {
    coreRequestStateChange(FOOBAR_STATE);
}

A mode is just a foreground application.

Your app is (probably) providing a mode, but you can also add background tasks, periodic tasks, and so on.

Requesting a mode change is sort of like exit(), the next app will gain control.

coreRequestModeChangeNext();
Events

Each state needs an event handler. A super simple one would be like this:

void default_state_manager(void)
{
    switch (CORECurrentEvent) {
        // This is called when this state becomes active.
        case COREEVENT_STATEENTRY:
            coreEnableSwitchReleaseEvents();
            coreAllowKeys(COREALLOWALLSWITCHES);
            break;
        // This is called when the user pulls the crown out.
        // When they push it back in, you get a CROWN_HOME event.
        // It can be rotated in either home or set positions.
        case COREEVENT_CROWN_SET:
            coreRequestStateChange(CORESETBANNERSTATE);
            break;
        // You can get button events too, like start/split:
        case COREEVENT_STARTSPLITDEPRESS:
            show_message();
            break;
        // The mode button probably means the user wants to exit...
        case COREEVENT_MODEDEPRESS:
            coreRequestModeChangeNext();
            break;
    }

    return;
}

In general, you can ignore any event you don’t care about.

Services

The kernel takes care of handling the hardware, dispatching events to you and provides various services.

Things like timers, generating tones (beeps), scrolling the display, access to database records, etc.

uint8_t banner[] = {
    LCDBANNER_COL10,
    DM5_H, DM5_E, DM5_L, DM5_L, DM5_O, DM5_EXCLAMATION,
    LCD_END_BANNER
};

lcdClearDisplay();

// Draw "hello!" using kernel services.
lcdDispBannerMsg(&banner);

The watch has a crown - and you can ask the kernel to configure it in a few different ways. For example, you might want pulse mode so you can monitor how quickly the user is turning it.

Crown Demo

It has a backlight, you can control that from software too.

Debugging

There’s no memory protection or anything, you can just clobber the kernel if you want… so what happens if you crash?

Well, one thing the kernel does is notify a watchdog every 2 seconds that it’s still alive. If the watchdog doesn’t see that notification, it will just reset the watch… so try not to do that! 😀

If you’re doing some heavy computation you could accidentally trigger the watchdog. Just remember to let the watchdog know you’re still alive:

 // Let the watchdog know we're not dead.
 hwResetWatchdog();
Simulator

Luckily there’s a really good simulator available, so you don’t have to sit through tedious resets everytime you make a typo. It’s actually an open source 3rd party tool, Virtual Datalink (source).

Sadly it’s for Windows only. I would port it to Linux, but it’s written in Delphi… is it feasible to port it to free pascal? Let me know!

It has conditional breakpoints, a dissasembler, save states, resource and power analysis. I’ve found it pretty capable.

Virtual Datalink

Conclusion

The main app I’m working on right now is integrating my watch with remind.

Hacking this little watch has been a super fun project!

Availability

Now that I’ve told you how much I love this thing - the bad news.

They’re long out of production, and getting hard to come by.

I bought a set of two on eBay, they just needed new batteries and were as good as new.

If this is the sort of thing that appeals to you, setup an alert and then help me hack on it 😀

Tips
  • Found a watch but missing the cable? It uses the standard USB type-a pinout with an odd connector, you can probably just use an old USB cable and alligator clips.
  • Timex used the same connector on a much less desirable device called the T5G751 (probably other model variants too). These are easy to find at reasonable prices (often < $10 at the time of writing), so you can simply toss the device and keep the cable (example).
  • There was a m851 variant called the “dress edition”, it has identical specs but a different body. It is sometimes listed with a different model number (example).

References