mirror of
https://github.com/Snigelson/pytimex.git
synced 2026-07-28 04:05:41 +00:00
Moving protocol documentation to its own file
This commit is contained in:
+299
@@ -0,0 +1,299 @@
|
||||
This document is based on [the protocol documentation by Tommy Johnson]
|
||||
(https://web.archive.org/web/20030803072005/http://csgrad.cs.vt.edu/~tjohnson/timex/).
|
||||
I have added and corrected some information. There may be more differences
|
||||
between watch versions, but most information for the model 70 should be
|
||||
correct.
|
||||
|
||||
Sound and app packets are not yet documented.
|
||||
|
||||
|
||||
## Physical level
|
||||
|
||||
Data is sent as a series of pulses. When sent using th CRT, each byte
|
||||
appears as 1-9 horizontal lines, with 2 bytes being sent each frame. A
|
||||
CRT draws its image sequentially as it receives the video signal, with
|
||||
each line fading shortly after being drawn, so to a light sensor it
|
||||
appears as a short pulse. LCDs in general receives an entire frame and
|
||||
draws all pixels almost at the same time, which is why LCDs do not work
|
||||
for data transfer.
|
||||
|
||||
Timex solved this problem by offering a "Notebook Adapter" which
|
||||
essentialy is a serially attached unit which transcodes data into the
|
||||
appropriate pulses for the watch to receive. This gives us a very nice
|
||||
advantage: it is super simple to log data from the program.
|
||||
|
||||
Bytes are sent least significant bit first, one start bit, no stop bit. A
|
||||
one is indicated by the absence of a pulse, a zero is a pulse. This means
|
||||
the byte 0x5a would look like pnpnppnpn (where p indicates a pulse, and n
|
||||
the absence of a pulse).
|
||||
|
||||
Some measurements were done by probing the video signal. Pulses are
|
||||
approximately 32 µs long. Pulses are sent at an interval of approximately
|
||||
480 µs. Some configuration files mentioned 2048 baud, which would mean
|
||||
approximately 488 µs. So this sort of makes sense.
|
||||
|
||||
Data transfer using CRT is done at 640x480@60Hz. At this resolution and
|
||||
frequency, the horizontal refresh frequency is 31.46875 kHz, so one line
|
||||
is drawn approximately every 31.78 µs. using the above timings, we can
|
||||
quantify the above measurement and conclude that one bit is one line and
|
||||
a bit is sent every 15 lines. The watch seems to be a bit flexible on
|
||||
this though.
|
||||
|
||||
Data bytes are separated by approximately 2 ms, and packets are separated
|
||||
by approximately 240 ms. The interpacket delay is also present after
|
||||
each block of synchronization bytes.
|
||||
|
||||
|
||||
## Synchronization
|
||||
|
||||
When initiating transfer, first 200 bytes of 0x55 is sent. During this
|
||||
time the watch will beep and give the user some time to align the watch.
|
||||
The exact number here is not important.
|
||||
|
||||
After that 50 bytes of 0xAA is sent. The original protocol documentation
|
||||
says 40 bytes, but I got 50 bytes from the software and I have not
|
||||
verified if the exact number is important.
|
||||
|
||||
|
||||
## Strings
|
||||
|
||||
Strings are encoded in a special charset, which I call the Timex charset.
|
||||
|
||||
From the old protocol documentation:
|
||||
|
||||
```char set for labels is: (6 bits per char)
|
||||
0-9 : digits
|
||||
10-36: letters
|
||||
37-63: symbols:
|
||||
space !"#$%&'()*+,-./;\
|
||||
divide =
|
||||
bell (image, not sound)
|
||||
?```
|
||||
|
||||
Since only 6 bits are used per character, the 24 bits of 4 characters can
|
||||
be packed into 3 bytes.
|
||||
|
||||
First byte:
|
||||
- High 2 bits contin low bits of second character
|
||||
- Low 6 bits contain first character
|
||||
|
||||
Second byte:
|
||||
- High 4 bits contain low bits of third character
|
||||
- Low 4 bits contain high bits of second character
|
||||
|
||||
Third byte:
|
||||
- High 6 bits contain last character
|
||||
- Low 2 bits contain high bits of third character
|
||||
|
||||
Unused bits are set to ones.
|
||||
|
||||
If the above explanation, maybe some pseudo code can clear it up:
|
||||
|
||||
```
|
||||
byte0 = (ch2&0x03)<<6) | (ch1&0x3F)
|
||||
byte1 = (ch3&0x0F)<<4) | ((ch2>>2)&0x0F)
|
||||
byte2 = (ch4&0x3F)<<2) | ((ch3&0x30)>>4)
|
||||
```
|
||||
|
||||
|
||||
## Data packets
|
||||
|
||||
Data is separated into packets. Each packet has a framing consisting of
|
||||
packet length, data type and checksum as follows:
|
||||
|
||||
| Byte no. | Description |
|
||||
| -------- | -------------------------------- |
|
||||
| 1 | Packet lengh (including framing) |
|
||||
| 2 | Packet type |
|
||||
| 3->len-2 | Payload (if any) |
|
||||
| len-1 | High byte of checksum |
|
||||
| len | Low byte of checksum |
|
||||
|
||||
Checksum is CRC-16/ARC.
|
||||
|
||||
When describing packet contents below, only the payload bytes are
|
||||
considered. Therefore, "byte 1" would mean the third byte of the packet.
|
||||
|
||||
|
||||
### Packet types
|
||||
|
||||
Note that names are made up and not official Timex names.
|
||||
|
||||
List is ordered by in which order the watch expects the packets. I will
|
||||
probably add more info on order later.
|
||||
|
||||
| ID | Name | Description |
|
||||
| ---- | ------ | ----------------------------------- |
|
||||
| 0x20 | START1 | Data transfer start |
|
||||
| 0x30 | TIME | Time information |
|
||||
| 0x31 | TZNAME | Time zone name |
|
||||
| 0x32 | TIMETZ | Time information and time zone name |
|
||||
| 0x60 | START2 | Marks start of DATA1 packets |
|
||||
| 0x61 | DATA1 | Contains lots of data |
|
||||
| 0x62 | END1 | Marks end of DATA1 packets |
|
||||
| 0x50 | ALARM | Alarm data |
|
||||
| 0x70 | SALARM | Sent after silent alarms |
|
||||
| 0x21 | END2 | End of data transfer |
|
||||
|
||||
|
||||
### 0x20 - START1
|
||||
|
||||
| Byte | Description |
|
||||
| ---- | ----------- |
|
||||
| 1 | Always 0x00 |
|
||||
| 2 | Always 0x00 |
|
||||
| 3 | Version |
|
||||
|
||||
Version is 0x01 for model 70, 0x03 for model 150 and 0x04 for model 150s.
|
||||
|
||||
Example packet: 0x07 0x20 0x00 0x00 0x01 0xc0 0x7f
|
||||
|
||||
|
||||
### 0x30 - TIME
|
||||
|
||||
| Byte | Description |
|
||||
| ---- | -------------------------------- |
|
||||
| 1 | Timezone ID (1 or 2) |
|
||||
| 2 | Hour |
|
||||
| 3 | Minute |
|
||||
| 4 | Month |
|
||||
| 5 | Day of month |
|
||||
| 6 | Year (mod 100) |
|
||||
| 7 | Day of week (0=monday, 6=sunday) |
|
||||
| 8 | Seconds |
|
||||
| 9 | 12h format (1) or 24h format (2) |
|
||||
|
||||
|
||||
### 0x31 - TZNAME
|
||||
|
||||
| Byte | Description |
|
||||
| ---- | -------------------------------- |
|
||||
| 1 | Timezone ID (1 or 2) |
|
||||
| 2 | Character 1 of timezone name |
|
||||
| 3 | Character 2 of timezone name |
|
||||
| 4 | Character 3 of timezone name |
|
||||
|
||||
Insert spaces on unused characters.
|
||||
|
||||
Example: 0x02 0x0e 0x1c 0x1d - timezone 2 named EST
|
||||
|
||||
|
||||
### 0x32 - ALARM2
|
||||
|
||||
Alarm packet for model 150 and 150s. To be documented.
|
||||
|
||||
|
||||
### 0x60 - START2
|
||||
|
||||
| Byte | Description |
|
||||
| ---- | --------------------------------- |
|
||||
| 1 | Number of DATA1 packets to follow |
|
||||
|
||||
|
||||
### 0x61 - DATA1
|
||||
|
||||
| Byte | Description |
|
||||
| ---- | ---------------------------------- |
|
||||
| 1 | Sequence ID (starts at 1) |
|
||||
| 2->3 | Start index of appointments |
|
||||
| 4->5 | Start index of TODOs |
|
||||
| 6->7 | Start index of phone numbers |
|
||||
| 8->9 | Start index of anniversaries |
|
||||
| 10 | Number of appointments |
|
||||
| 11 | Number of TODOs |
|
||||
| 12 | Number of phone numbers |
|
||||
| 13 | Number of anniversaries |
|
||||
| 14 | Unknown, see below |
|
||||
| 15 | Early alarm, in 5 minute intervals |
|
||||
|
||||
Sequence ID is incremented for each DATA1 packet sent.
|
||||
|
||||
Indices are counted zero inexed from first address byte (2). This means
|
||||
the first data is always located at index 0x0e.
|
||||
|
||||
The unknown byte was documented as 0x60, but I seem to
|
||||
get 0x14 instead. Requires some investigating.
|
||||
|
||||
Byte 15 indicates how long before the specified time the alarm will sound
|
||||
(I believe).
|
||||
|
||||
Check if the following DATA1 packets contain data start indexes etc too,
|
||||
or if their payloads are just concatenated.
|
||||
|
||||
The following data in the payload are records of the following
|
||||
format:
|
||||
|
||||
These 4 record types are found in DATA1 packets:
|
||||
|
||||
Appointment record
|
||||
|
||||
| Byte | Description |
|
||||
| ------ | ---------------------------------- |
|
||||
| 1 | Record length |
|
||||
| 2 | Month |
|
||||
| 3 | Day |
|
||||
| 4 | Time (see below) |
|
||||
| 5->len | Packed string |
|
||||
|
||||
Time is encoded in 15 minute intervals since midnight, such that 08:45 is
|
||||
8*4+3=35.
|
||||
|
||||
Todo record
|
||||
|
||||
| Byte | Description |
|
||||
| ------ | ---------------------------------- |
|
||||
| 1 | Record length |
|
||||
| 2 | Priority |
|
||||
| 3->len | Packed string |
|
||||
|
||||
Phone number record
|
||||
|
||||
| Byte | Description |
|
||||
| ------ | ---------------------------------- |
|
||||
| 1 | Record length |
|
||||
| 2-6 | Phone number (BCD, 2 digits per byte, little endian) |
|
||||
| 7->len | Packed string |
|
||||
|
||||
Anniversary record
|
||||
|
||||
| Byte | Description |
|
||||
| ------ | ---------------------------------- |
|
||||
| 1 | Record length |
|
||||
| 2 | Month |
|
||||
| 3 | Day |
|
||||
| 4->len | Packed string |
|
||||
|
||||
|
||||
### 0x62 - END1
|
||||
|
||||
No payload
|
||||
|
||||
|
||||
### 0x50 - ALARM
|
||||
|
||||
| Byte | Description |
|
||||
| ----- | ---------------------------------- |
|
||||
| 1 | Alarm ID (starts at 1) |
|
||||
| 2 | Hour |
|
||||
| 3 | Minute |
|
||||
| 4 | Month (0 for every month) |
|
||||
| 5 | Day (0 for every day) |
|
||||
| 6->13 | Label, unpacked |
|
||||
| 14 | 1 if audible, otherwise 0 |
|
||||
|
||||
|
||||
### 0x70 - SALARM
|
||||
|
||||
Didn't know what to call this. Sent after silent alarms
|
||||
|
||||
| Byte | Description |
|
||||
| ---- | ---------------------------------- |
|
||||
| 1 | Always 0 |
|
||||
| 2 | ID of preceding alarm + 0x61 |
|
||||
| 3 | Always 0 |
|
||||
|
||||
|
||||
### 0x21 - END2
|
||||
|
||||
No payload
|
||||
|
||||
@@ -33,28 +33,15 @@ the first sync bytes sent). After "U" is received, all bytes are sent
|
||||
over the IR LED. To get back to the initial state, device power must be
|
||||
cycled. This is done by pulling CTS low for a few hundred milliseconds.
|
||||
|
||||
Since I do not have access to an actual adapter, I have no way of
|
||||
verifying the timings of the device. It's a safe bet, though, that they
|
||||
will be very similar to those from the CRT.
|
||||
|
||||
A one is indicated by the absence of a pulse, a zero is a pulse of about
|
||||
32 µs. Pulses are sent at an interval of approximately 480 µs, with some
|
||||
jitter. Some configuration files mentioned 2048 baud, which would mean
|
||||
approximately 488 µs. So this sort of makes sense.
|
||||
|
||||
Bytes are sent least significant bit first, one start bit, no stop bit.
|
||||
This means the byte 0x5a would look like pnpnppnpn (where p indicates
|
||||
a pulse, and n the absence of a pulse).
|
||||
|
||||
There are some requirements on both the spacing between bytes, and the
|
||||
spacing between packages. Using the Notebook Adapter and original
|
||||
software, this is done by the PC and the adapter just naively sends the
|
||||
data along. Byte spacing seems to be on the order of 2 ms and package
|
||||
spacing on the order of 240 ms.
|
||||
|
||||
When a byte is sent to the adapter, it replies with the same byte. I'm
|
||||
assuming it's done to keep things in sync.
|
||||
|
||||
Since I do not have access to an actual adapter, I have no way of
|
||||
verifying the timings of the device. It's a safe bet, though, that they
|
||||
will be very similar to those from the CRT. It is also possible the
|
||||
data could be sent somewhat faster since it does not need to be phase
|
||||
locked to the CRT refresh.
|
||||
|
||||
|
||||
## The Blaster
|
||||
|
||||
@@ -77,40 +64,3 @@ others. Connect an IR LED to pin 12 with a suitable resistor in series
|
||||
and you're golden. Experiment with distance and LED frequency to get it
|
||||
just right. Some LEDs are very focused and offers only a narrow beam, so
|
||||
if you have the option, try to find one with a wide beam.
|
||||
|
||||
|
||||
## Additions/corrections to the github document
|
||||
|
||||
Start package contains 0x00 0x00 0x01 for the original Data Link (70?),
|
||||
but for model 150 it contains 0x00 0x00 0x03, and for 150s it contains
|
||||
0x00 0x00 0x04.
|
||||
|
||||
```
|
||||
zero or more packet type TIME 0x30
|
||||
byte 1 - timezone (the watch has two timezones, 1 or 2)
|
||||
byte 2 - hour
|
||||
byte 3 - minute
|
||||
byte 4 - month
|
||||
byte 5 - day of month
|
||||
byte 6 - year (last 2 digits)
|
||||
byte 7 - day of week (0=monday, 6=sunday)
|
||||
byte 8 - Pretty certain this is seconds
|
||||
byte 9 - 12h format (1) or 24h format (2)
|
||||
|
||||
one package type 0x31 for each time zone - TZNAME
|
||||
Contains names of timezones, unpacked, timex charset
|
||||
byte 1 - Time zone number (1 or 2)
|
||||
byte 2-4 - Name of time zone (e.g. 0x0e 0x1c 0x1d for EST)
|
||||
```
|
||||
|
||||
Checksum seems to be CRC-16/ARC.
|
||||
|
||||
For appointment packets, time is indicated in quarters of an hour since
|
||||
midnight, if that makes more sense. So for instance 08:45 would be
|
||||
8*4+3=35 quarters.
|
||||
|
||||
For the 150 models, the protocol seems slightly different. For instance,
|
||||
there is a 0x32 package for sending time, combining the 0x30 and 0x31
|
||||
packets. Also, the 0x70 packet sent after inaudible alarms is omitted.
|
||||
There might be more differences I've yet to uncover. Also proocol for
|
||||
apps and sounds are yet to be documented.
|
||||
|
||||
Reference in New Issue
Block a user