Snigelson / pytimex

Python library and utilities for generating and transferring data to Timex Data Link smartwatches
12 stars 1 forks source link

Datalink 150 and 150s support #2

Open ahhuhtal opened 2 years ago

ahhuhtal commented 2 years ago

Hi!

I couldn't figure out a better way of contacting you. I was about to start a very similar project to yours, but happened to stumble across this just before.

I've been reverse-engineering the upload protocol recently (mostly regarding the 150, which I have myself). I would be very interested in collaborating with you on this project, but I think we'd need to organize somehow. That is, instead of me just writing something and submitting pull requests out of the blue.

My interest in the watch has been the programmability and the reverse engineering related to that. I've been trying to understand the contents of the upload protocol, instead of just replicating what the official software generates. For example, I suspect the weird 0x70 packets sent after each disabled alarm are a work-around for some kind of firmware bug. I believe so, because on the 150, the 0x70 packets are direct RAM write commands, allowing writing arbitrary data to memory.

Regarding the 150, I'm working toward a full ROM dump and disassembly. My plan is to have a Wristapp dump the ROM using the buzzer.

It seems there has been a lot of interest regarding the watches lately :-)

Snigelson commented 2 years ago

Hi! Always nice when someone else shows interest in your projects :)

While writing the software I've been very much pushing the limits of the watch too. Learned the hard way that sending too many alarms makes the watch hang, figured out that you can add some letters in phone numbers, etc. The library doesn't do a very good job at replicating the data from the original software, and that was never the plan.

I agree the 0x70 packet is to fix some kind of bug. This page, linked in PROTOCOL.md, lists some packets I have neither added to my documentation or implemented, among others the 0x70 packet.

And, in case you haven't seen it already, here is a wristapp to dump any part of watch memory. It starts by default at $4000 which is the system ROM. But I guess you're trying to use the buzzer as a kind of modem? That's a very clever way of implementing two-way communication!

As this project grows, I'd love to see someone step up and take the role as maintainer. I always have too many other projects running at the same time, but I like the feeling of starting something and seeing it take off on it's own. In the meantime, I'll try and see if I can figure out how to add you as a collaborator.

ahhuhtal commented 2 years ago

Hi :-)

Toebes.com is great! Though not everything there is correct (such as packet 0x90, subtype 1). It's still by far the best source of information.

John Toebes maintained his site on geocities already back in the late 90s when I first got my watch. When geocities notified that they'd shut down their service, I backed up Toebes' stuff :-). Luckily he moved the stuff to his own server, though I'm still hanging on to that backup.

I've been using Toebes' assembler for building Wristapps. I used his hexdump utility for reverse-engineering of some undocumented ROM routines, which I needed for this demo I made. I looked at the relevant ROM parts with hexdump and typed out the contents to a disassembler.

Regarding the watch hanging: hold down all five buttons for a few seconds and it'll reboot. This has saved me several times from needing to remove the battery :-)

But coming back to your project. The first thing I thought we should discuss is the nomenclature for 150 packets. You've documented 0x90 as "START3" for instance. However, the 150 has three types of 0x90 packets (subtypes 1, 2 and 3), which are very different from each other. I'd opt for separating them and calling them e.g. START3_EEPROM, START3_WRISTAPP and START3_SOUND instead of trying to keep them somehow related to each other.

Also, the 150 handles EEPROM data in a very different way to the 70. I believe the 70 stores the EEPROM "file system" directly in the beginning of the EEPROM. Thus the pointers to appointments etc. are just the index of where they start in the stream. On the 150 the "file system" is stored in SRAM and only the actual entry data is on the EEPROM. The file system points to where each type of entry starts in the EEPROM, with the beginning of the EEPROM being denoted by the address 0x0236. I've no idea where this address comes from, but could be determined from the ROM dump. Internal EEPROM handling routines use 0x0000 as the beginning of the EEPROM.

On the 150, you can actually place the data arbitrarily in EEPROM as long as the file system pointers in SRAM are correct. In my demo, for example, I placed the scroller text in the beginning of the EEPROM, and just programmed the pointers to appointments etc. after the scroller text.

synthead commented 1 year ago

I've been using Toebes' assembler for building Wristapps. I used his hexdump utility for reverse-engineering of some undocumented ROM routines, which I needed for this demo I made. I looked at the relevant ROM parts with hexdump and typed out the contents to a disassembler.

@ahhuhtal, this demo is great!! I love how you hogged the metadata areas of the EEPROM for more data :smile: Very clever!

I wanted to share: I got the watch150.bts bitstream binary to upload using my timex_datalink_client library! Here's the quick and dirty code to make it work:

#!/usr/bin/env ruby

require "timex_datalink_client"

bitstream_file = File.open(ARGV[0], "rb")

packets = TimexDatalinkClient::Protocol3::Sync.new.packets

until bitstream_file.eof?
  length = bitstream_file.read(1).ord
  packets << [length] + bitstream_file.read(length - 1).bytes
end

notebook_adapter = TimexDatalinkClient::NotebookAdapter.new(
  serial_device: ARGV[1] || "/dev/ttyACM0",
  byte_sleep: 0.007,
  packet_sleep: 0.06,
  verbose: true
)

notebook_adapter.write(packets)

You can use this script via ./upload-bitstream watch150.bts /dev/ttyACM0! The Datalink 150s version, watc150s.bts also works with my 150s, but the graphics on the screen are jumbled up compared to the 150 version :upside_down_face:

If you want to check out my stuff, I reverse-engineered all the Datalink devices as a Ruby lib:

And I reverse-engineered the Notebook Adapter, which works with all Timex software:

In case you have a CRT, I reverse-engineered the CRT graphics in SDL2, too:

ahhuhtal commented 1 year ago

I wanted to share: I got the watch150.bts bitstream binary to upload using my timex_datalink_client library! Here's the quick and dirty code to make it work:

You can use this script via ./upload-bitstream watch150.bts /dev/ttyACM0! The Datalink 150s version, watc150s.bts also works with my 150s, but the graphics on the screen are jumbled up compared to the 150 version upside_down_face

@synthead, that's awesome! As far as I know, you're the only other person to have run the demo on a 150, and likely the only one to have run it on a 150s.

I guess the 150s indexes the segments in a different way than the 150, thus the jumbled up graphics. The demo addresses the segments with low-level calls. I don't have a 150s myself, and provided the 150s bitstream just because Toebes' assembler provides both of the binaries. It might be possible to make a 150s specific version of the demo.

If you want to check out my stuff, I reverse-engineered all the Datalink devices as a Ruby lib:

And I reverse-engineered the Notebook Adapter, which works with all Timex software:

In case you have a CRT, I reverse-engineered the CRT graphics in SDL2, too:

This is great stuff! You've gone through a huge effort here, and with a lot of documentation.

It's amazing how many people recently have had interest in these devices. I've also been meaning to get back to playing with the Datalink again. For instance, I still have no idea what the CPU clock frequency is :-).

I did manage to dump the entire ROM of my 150 through the beeper. I performed quite a bit of disassembly and reverse-engineering on the ROM, and found what appears to be production test code. One thing the test code does is it computes a checksum over the ROM contents. The checksum matches my dump, so I'm quite confident the dump is bit-for-bit correct. I'm not sure of the legality of publicly sharing the ROM, or my disassembly of it, so I haven't put it up anywhere.

synthead commented 1 year ago

Oh wow, that's neat, @ahhuhtal! Do you have the WristApp binary for the beeping ROM dump program? I just started researching 6805 programming and was going to do just that!

Thanks for the kind words! It's been a large effort to get everything reverse-engineered 😄 I was pretty excited to see the demo run on my 150! That is a lot of animation and sound packed in such a small amount of memory on such a limited platform!