https://lock.cmpxchg8b.com/timex.html
Hacking the Timex m851
Tavis Ormandy
$Id: a07cf90837a3c4373b82d6724b97593810766af7 $
* Intro
* WatchApps
* Conclusion
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 DemoCrown 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 DatalinkVirtual 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
References
* The most important doc is the Design Guide, it's very well
written.
* Then of course, you need the CPU manual. It's also excellent!
* The toolchain also has a thorough manual.
* Finally, the complete API reference lists the services provided
by the Kernel.
HOME * ABOUT * CONTACT