<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>HandShakeTec</title><link>https://handshaketec.com/</link><description>Recent content on HandShakeTec</description><generator>Hugo</generator><language>en</language><copyright>&amp;copy; 2026 Matt Oppenheim. All rights reserved.</copyright><lastBuildDate>Fri, 09 Oct 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://handshaketec.com/index.xml" rel="self" type="application/rss+xml"/><item><title>Firmware changes</title><link>https://handshaketec.com/firmware_changes/</link><pubDate>Fri, 09 Oct 2026 00:00:00 +0000</pubDate><guid>https://handshaketec.com/firmware_changes/</guid><description>&lt;!--
 One line per fix, newest first, labelled by device. Each line starts with the
 FIRMWARE_VERSION date of the build that contains the fix (a letter suffix marks a
 second fix on the same day).
 scripts/remote_deployment/02_validate_and_prepare.sh fails if either firmware's
 FIRMWARE_VERSION has no line here. Add the line in the same change that bumps
 the version (see design.md, 2026-10-09).
-->
&lt;p>Each fix is listed with the version date that the device shows for about two seconds
when it starts. Devices flashed before 2026-10-09 show no version. Reflash them to
start from the first versioned build.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>2026-10-09b&lt;/strong> — T-Watch S3: the green bar now follows the trigger level. When you change it from the T-Embed, a full bar means the movement would have triggered a shake.&lt;/li>
&lt;li>&lt;strong>2026-10-09b&lt;/strong> — T-Watch S3: the green bar keeps the highest level reached and falls back over about a second, so a short punch is visible. In roll mode it shows the roll instead of the punch strength.&lt;/li>
&lt;li>&lt;strong>2026-10-09b&lt;/strong> — T-Watch S3: &amp;ldquo;&amp;ndash; NO SAVE &amp;ndash;&amp;rdquo; stays on the screen after a shake instead of disappearing. &amp;ldquo;- TASK FAIL -&amp;rdquo; is shown if the watch could not start one of its tasks.&lt;/li>
&lt;li>&lt;strong>2026-10-09&lt;/strong> — T-Watch S3 and T-Embed: the firmware version is shown at start-up.&lt;/li>
&lt;li>&lt;strong>2026-10-08&lt;/strong> — T-Embed: flashes without holding the BOOT button again. The USB keyboard now starts on the first shake instead of at power-up.&lt;/li>
&lt;li>&lt;strong>2026-10-06&lt;/strong> — T-Watch S3: the watch confirms that each shake reached the T-Embed, and shows NO LINK when it did not.&lt;/li>
&lt;li>&lt;strong>2026-10-06&lt;/strong> — T-Watch S3: the radio settle delay is 10 ms instead of 100 ms, so a shake reaches the keyboard about 90 ms sooner.&lt;/li>
&lt;li>&lt;strong>2026-09-08&lt;/strong> — T-Watch S3: fixed a boot loop caused by the audio driver. The watch has no audio.&lt;/li>
&lt;/ul></description></item><item><title>Using HandShake</title><link>https://handshaketec.com/how_to_use_handshake/</link><pubDate>Fri, 09 Oct 2026 00:00:00 +0000</pubDate><guid>https://handshaketec.com/how_to_use_handshake/</guid><description>&lt;p>This page shows how to use the Lilygo T-Watch S3 and the Lilygo T-Embed to work as the &lt;a href="https://mattoppenheim.com/projects/handshake/">handshake project&lt;/a>.
This page assumes that you have already programmed both devices with the HandShake firmware.&lt;/p>
&lt;h1 id="summary">Summary&lt;/h1>
&lt;p>Turn on the watch using a long press on the button.&lt;/p>
&lt;p>Shake the watch gently and check that this is detected as a switch by the receiver unit.&lt;/p>
&lt;p>Adjust the level of shake that will trigger a switch action using the jog dial on the receiver, followed by a long press on the button in the middle of the jog dial. This sends the new shake detection threshold to the watch.&lt;/p>
&lt;h1 id="handshake-components">HandShake components&lt;/h1>
&lt;p>The HandShake system comprises two parts, a smart watch (Lilygo T-Watch S3) and a receiver unit that sends a keystroke to a communication device (Lilygo T-Embed) through a USB cable. The two devices are shown in the photo below.&lt;/p>
&lt;figure class="image center">
&lt;img src="https://handshaketec.com/images/t_watch_s3_off_t_embed_on.png" loading="lazy" width="400" height="121">
&lt;figcaption>
&lt;strong>Lilygo T-Watch S3 and Lilygo T-Embed&lt;/strong>&lt;br>&lt;/figcaption>
&lt;/figure>

&lt;p>When the watch starts, it shows its firmware version, a date such as 2026-10-09, for about two seconds. The T-Embed does the same. The &lt;a href="https://handshaketec.com/firmware_changes/">Firmware changes&lt;/a> page lists what changed in each version.&lt;/p>
&lt;p>When the watch has booted up, which takes a few seconds, you will see the display below:&lt;/p>
&lt;figure class="image center">
&lt;img src="https://handshaketec.com/images/t_watch_s3_standby.png" loading="lazy">
&lt;figcaption>
&lt;strong>Watch default display&lt;/strong>&lt;br>&lt;/figcaption>
&lt;/figure>

&lt;p>The screen has two sliders:&lt;/p>
&lt;ul>
&lt;li>The &lt;strong>top slider&lt;/strong> (yellow) shows the trigger level, the amount of shake needed to operate your communication software. Its value is also shown on the watch screen as &amp;lsquo;shake trigger: xxx&amp;rsquo; where xxx is a number that varies from 525 to 1000 (these limits may change). The lower the number, the softer the motion required to trigger a shake detection. This slider only changes when you send a new trigger level from the receiver.&lt;/li>
&lt;li>The &lt;strong>bottom slider&lt;/strong> (green) shows how close the latest movement came to triggering. It updates in real time.&lt;/li>
&lt;/ul>
&lt;p>The bottom slider runs from the watch at rest (empty) to the trigger level (full). Once it reaches the right-hand end, the shake detection is triggered. If you change the trigger level from the receiver, the green slider rescales to match, so a full bar always means a trigger.&lt;/p>
&lt;p>The green slider keeps the strongest movement of the last moment and then falls back over about a second, so even a short sharp punch is visible. If the watch is set to detect a roll rather than a punch, the green slider shows the strength of the roll.&lt;/p>
&lt;p>When an intended shake is detected this display is shown on the watch:&lt;/p>
&lt;figure class="image center">
&lt;img src="https://handshaketec.com/images/t_watch_s3_shake_detection.png" loading="lazy">
&lt;figcaption>
&lt;strong>Shake detected display&lt;/strong>&lt;br>&lt;/figcaption>
&lt;/figure>

&lt;p>The line below the battery reading is the watch&amp;rsquo;s status line. It shows &lt;strong>*** SHAKE ***&lt;/strong> or &lt;strong>*** NO LINK ***&lt;/strong> briefly after a gesture, and is otherwise empty. If the watch has a lasting problem, the message stays there and comes back after each gesture:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>&amp;ndash; NO SAVE &amp;ndash;&lt;/strong>: the watch cannot store its settings. It runs with the built-in trigger level, and a new level sent from the receiver is lost when the watch restarts. Reflash the firmware.&lt;/li>
&lt;li>&lt;strong>- TASK FAIL -&lt;/strong>: the watch could not start one of its internal tasks. Restart the watch. If it comes back, reflash the firmware.&lt;/li>
&lt;/ul>
&lt;p>Watches with firmware older than 2026-10-09b lose these messages after the first gesture.&lt;/p>
&lt;p>To charge the watch, attach a USB cable to the micro USB socket in the side of the watch and connect to a USB socket or charger. For whatever reason, once the watch battery is at 0% it stays at 0% for an age while charging then starts ramping up to 100% fairly quickly. I don&amp;rsquo;t know how to improve this yet.&lt;/p>
&lt;figure class="image center">
&lt;img src="https://handshaketec.com/images/t_watch_s3_charging.png" loading="lazy">
&lt;figcaption>
&lt;strong>Charging the watch&lt;/strong>&lt;br>&lt;/figcaption>
&lt;/figure>

&lt;h1 id="setting-up-the-receiver-unit">Setting up the receiver unit&lt;/h1>
&lt;p>The T-Embed receiver unit is powered from the USB-C socket on its side.&lt;/p>
&lt;p>The receiver has a jog dial around the button.
This dial is used to adjust the trigger threshold.
Use the dial to set a new trigger threshold then send this new value to the watch by giving a long press (about 2s) to the button in the middle of the dial.
Turning the jog wheel clockwise will increase the amount of shake needed to send a trigger to your communication device, turning the jog wheel anti-clockwise decreases the amount of shake needed to send a trigger to your communication device.
The updated trigger will not take effect in the watch until you give a long press on the button though.
Check the watch to see that the new shake value is shown on the watch display.&lt;/p>
&lt;p>The button on the receiver can be used to adjust the sensitivity of the gesture detection algorithm running on the T-Watch:&lt;/p>
&lt;p>A short press: The receiver asks the watch for the largest shake it measured over the last 5 seconds and shows that value on the receiver display.
Ask the person wearing the watch to make the gesture you want to detect, then give a short press to see how strongly that gesture registered.
Set the trigger threshold a little below the value you see, so the gesture is detected reliably without every small movement triggering it.&lt;/p>
&lt;p>A long press: The receiver sends the trigger threshold shown on the receiver display to the watch.&lt;/p>
&lt;p>This allows the sensitivity of the gesture that is detected to be adjusted without having to touch the T-Watch.&lt;/p></description></item><item><title>Publications</title><link>https://handshaketec.com/publications/</link><pubDate>Mon, 05 Oct 2026 00:00:00 +0000</pubDate><guid>https://handshaketec.com/publications/</guid><description>&lt;p>Articles about the HandShake project and the hardware behind it:&lt;/p>
&lt;ul>
&lt;li>
&lt;p>&lt;strong>HandShake - Using Arm Movement as an AAC Switch&lt;/strong>&lt;br>
Oppenheim, M. Frankowski, T.: Communication Matters. 40, 2, p. 16-19&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Empowering Disabled Persons with Hand Motions for Environmental Controls: Real-Time Sensor Display Using Python and a Smart Watch&lt;/strong>&lt;br>
Oppenheim, M., 1/11/2025, In: Circuit Cellar. 1, 424, p. 26-34&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Power Profiler for Accelerated Embedded Hardware Development: Streamlining the Development Process with Efficient Power Analysis&lt;/strong>&lt;br>
Oppenheim, M., 24/06/2025, In: Circuit Cellar - The Magazine For Computer Applications. 1, 420, p. 26-33&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Devices Aid Speech for People with Disabilities&lt;/strong>&lt;br>
Oppenheim, M., 21/06/2021, In: Circuit Cellar - The Magazine For Computer Applications. 1, 372, p. 30-37&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Using the BBC micro:bit as AAC – Three Solutions&lt;/strong>&lt;br>
Oppenheim, M., 30/11/2020, In: Communication Matters. 34, 3, p. 13-15&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>HandShake – Using Hand Motion Recognition to Enable Communication&lt;/strong>&lt;br>
Oppenheim, M. &amp;amp; McIntyre, F., 30/04/2020, In: Communication Matters. 34, 1, p. 19-21&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Full Stack Python: Micropython and Python for rapid embedded hardware and GUI development&lt;/strong>&lt;br>
Oppenheim, M., 28/02/2017, In: Circuit Cellar - The Magazine For Computer Applications. 1, 319, p. 17-25&lt;/p>
&lt;/li>
&lt;/ul></description></item><item><title>Device Flasher help</title><link>https://handshaketec.com/flash_device_help/</link><pubDate>Mon, 14 Sep 2026 00:00:00 +0000</pubDate><guid>https://handshaketec.com/flash_device_help/</guid><description>&lt;p>The &lt;a href="https://handshaketec.com/flash_device/">&lt;strong>Device Flasher&lt;/strong> page&lt;/a> is where you load
the firmware file you built on the configuration page onto your device. This works for
both the T-Watch S3 and the T-Embed, using the same page.&lt;/p>
&lt;p>This is called &amp;lsquo;flashing&amp;rsquo; the device, as the memory that the firmware is written to is
called flash memory.&lt;/p>
&lt;p>It all happens in your browser — there&amp;rsquo;s nothing to install. You&amp;rsquo;ll need &lt;strong>Google
Chrome or Microsoft Edge&lt;/strong>; other browsers such as Firefox and Safari can&amp;rsquo;t talk to the
device over USB and won&amp;rsquo;t work.&lt;/p>
&lt;p>You don&amp;rsquo;t need any programming knowledge — just work through the page from top to
bottom.&lt;/p>
&lt;h1 id="before-you-start">Before you start&lt;/h1>
&lt;p>You&amp;rsquo;ll need:&lt;/p>
&lt;ul>
&lt;li>The &lt;code>.bin&lt;/code> firmware file you downloaded from the &lt;a href="https://handshaketec.com/watch_configuration/">T-Watch-S3 configuration
page&lt;/a> or the &lt;a href="https://handshaketec.com/embed_configuration/">T-Embed configuration
page&lt;/a>.&lt;/li>
&lt;li>A USB cable that carries data, not just power. Some cables only charge — if your
device isn&amp;rsquo;t found in Step 2 below, try a different cable.
&lt;ul>
&lt;li>The &lt;strong>T-Watch S3&lt;/strong> uses a &lt;strong>USB micro&lt;/strong> cable.&lt;/li>
&lt;li>The &lt;strong>T-Embed&lt;/strong> uses a &lt;strong>USB-C&lt;/strong> cable.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;h1 id="step-1--select-the-firmware-file">Step 1 — Select the firmware file&lt;/h1>
&lt;p>Under &lt;strong>1. Select Firmware File&lt;/strong>, click the file box and choose the &lt;code>.bin&lt;/code> file you
downloaded from the configuration page.&lt;/p>
&lt;p>The page checks the file and uploads it. You don&amp;rsquo;t have to set a start address or any
other technical option — the file you download already contains everything the device
needs, and the page works out where it goes.&lt;/p>
&lt;p>If you see an error message here:&lt;/p>
&lt;ul>
&lt;li>&lt;em>&amp;ldquo;File must be .bin&amp;rdquo;&lt;/em> — you&amp;rsquo;ve picked the wrong file, or renamed the firmware so it no
longer ends in &lt;code>.bin&lt;/code>.&lt;/li>
&lt;li>&lt;em>&amp;ldquo;File too small&amp;rdquo;&lt;/em> — the file isn&amp;rsquo;t a firmware image. A real one is at least 0.3 MB.
This usually means a download didn&amp;rsquo;t finish, so download it again from the
configuration page.&lt;/li>
&lt;/ul>
&lt;h2 id="about-the-audio-file-status-section">About the Audio File Status section&lt;/h2>
&lt;p>Section &lt;strong>2. Audio File Status&lt;/strong> is there for information only. It lists what it found
for &lt;strong>both&lt;/strong> devices, whichever firmware you happen to have selected, so seeing a line
about the other device is normal and isn&amp;rsquo;t a mistake.&lt;/p>
&lt;p>You don&amp;rsquo;t need to do anything in this section. If you chose to have sound on the
T-Embed, it is already built into the &lt;code>.bin&lt;/code> file — there&amp;rsquo;s no separate audio file for
you to select or flash.&lt;/p>
&lt;h1 id="step-2--connect-your-device">Step 2 — Connect your device&lt;/h1>
&lt;p>Plug your device into your computer with a data-capable USB cable — micro-USB for the
T-Watch S3, USB-C for the T-Embed.&lt;/p>
&lt;p>Once the firmware file has been checked, a &lt;strong>Connect&lt;/strong> button will appear under &lt;strong>3.
Connect &amp;amp; Flash&lt;/strong>. Click it. Your browser will show a list of devices — choose the one
for your watch or T-Embed and click &lt;strong>Connect&lt;/strong> in that list.&lt;/p>
&lt;p>The names in this list come from your computer, not from HandShake, so they&amp;rsquo;re often
unhelpful — something like &amp;ldquo;USB Serial Device&amp;rdquo;, &amp;ldquo;USB JTAG/serial debug unit&amp;rdquo;, or a
string of letters and numbers rather than &amp;ldquo;T-Watch S3&amp;rdquo;. If it&amp;rsquo;s not obvious which entry
is your device, close the list, unplug the device, click &lt;strong>Connect&lt;/strong> again and note down
what&amp;rsquo;s in the list without it, then plug the device back in and click &lt;strong>Connect&lt;/strong> once
more. The extra entry that appears only when the device is plugged in is the one you
want.&lt;/p>
&lt;p>If your device doesn&amp;rsquo;t appear in the list, make sure it&amp;rsquo;s powered on and properly
connected, then try disconnecting and reconnecting the cable.&lt;/p>
&lt;h1 id="step-3--install-the-firmware">Step 3 — Install the firmware&lt;/h1>
&lt;p>Once the device is connected, a small window opens with a few choices. Click
&lt;strong>Install HandShake code&lt;/strong>.&lt;/p>
&lt;p>You&amp;rsquo;ll be asked to confirm. There&amp;rsquo;s also an &lt;strong>Erase device&lt;/strong> tick box — you can leave
this alone. The firmware you downloaded is a complete image, so erasing first isn&amp;rsquo;t
needed.&lt;/p>
&lt;p>You&amp;rsquo;ll then see it work through &lt;strong>Preparing installation&lt;/strong>, &lt;strong>Writing&lt;/strong>, and finally
&lt;strong>Installation complete&lt;/strong>. Writing takes a short while — leave the cable connected and
don&amp;rsquo;t close the browser tab until it says it has finished.&lt;/p>
&lt;p>The device restarts on its own once installation is complete, so there&amp;rsquo;s nothing further
for you to do.&lt;/p>
&lt;h2 id="checking-which-firmware-version-is-on-the-device">Checking which firmware version is on the device&lt;/h2>
&lt;p>When the device restarts, its screen shows the word &lt;strong>firmware&lt;/strong> and a date, such as
&lt;strong>2026-10-09&lt;/strong>, for about two seconds before the normal display appears. This date is the
firmware version. The &lt;a href="https://handshaketec.com/firmware_changes/">Firmware changes&lt;/a> page lists what changed
in each version, so you can see whether your device has a particular fix.&lt;/p>
&lt;p>The date is the version of the program, not the day you built or flashed it. A device
flashed today can show an earlier date if nothing has changed since then.&lt;/p>
&lt;p>Firmware flashed before 9 October 2026 doesn&amp;rsquo;t show a version. Build and flash new
firmware to get it.&lt;/p>
&lt;h1 id="if-something-goes-wrong">If something goes wrong&lt;/h1>
&lt;p>&lt;strong>Don&amp;rsquo;t panic.&lt;/strong> If flashing fails partway through, or you flash the wrong file by
mistake, just reconnect the device and repeat the steps above with the correct file.
There&amp;rsquo;s no way to damage the device by flashing it again.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Device not detected?&lt;/strong> Check it&amp;rsquo;s connected and powered on. Try a different USB
cable or a different USB port on your computer.&lt;/li>
&lt;li>&lt;strong>Flash failed?&lt;/strong> Disconnect and reconnect the device, then try again.&lt;/li>
&lt;li>&lt;strong>Still stuck?&lt;/strong> Open the browser&amp;rsquo;s developer console (press F12) to see if there&amp;rsquo;s an
error message, and get in touch if you need help — it&amp;rsquo;s useful for me to know if
you&amp;rsquo;ve hit a problem.&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>T-Embed repeatedly not detected&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>Unplug the T-Embed from the USB cable.&lt;/li>
&lt;li>Push in the button in the middle of the jog dial.&lt;/li>
&lt;li>Keep this button pressed while you plug the USB cable back in.&lt;/li>
&lt;li>Release the button. The display will probably stay black.&lt;/li>
&lt;li>Now the T-Embed should be detected and flash.&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>T-Watch repeatedly not detected&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>Have a look at the section &amp;lsquo;Resetting the watch if it becomes unresponsive&amp;rsquo; in this page: &lt;a href="https://handshaketec.com/watch_configuration_help/#resetting-the-watch-if-it-becomes-unresponsive">watch_configuration_help&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>T-Embed configuration help</title><link>https://handshaketec.com/embed_configuration_help/</link><pubDate>Mon, 14 Sep 2026 00:00:00 +0000</pubDate><guid>https://handshaketec.com/embed_configuration_help/</guid><description>&lt;p>The &lt;a href="https://handshaketec.com/embed_configuration/">&lt;strong>T-Embed Configuration&lt;/strong> page&lt;/a> lets you choose what your T-Embed receiver does, then builds a
custom firmware file (the program that runs on the device) for you to download and
put onto the T-Embed. The T-Embed is the small receiver unit that sits plugged into
your computer or other device and passes on the signal sent by the watch — see the
&lt;a href="https://handshaketec.com/getting_started/">Getting Started&lt;/a> and
&lt;a href="https://handshaketec.com/how_to_use_handshake/">Using HandShake&lt;/a> pages if you&amp;rsquo;re not
sure which device is which.&lt;/p>
&lt;p>The options that you can set include:&lt;/p>
&lt;ul>
&lt;li>Whether the T-Embed plays a sound each time it gets a shake signal from the watch.&lt;/li>
&lt;li>Whether the T-Embed sends a keystroke to whatever it&amp;rsquo;s plugged into each time it gets a shake signal from the watch.&lt;/li>
&lt;li>Which watch the T-Embed should listen to.&lt;/li>
&lt;/ul>
&lt;p>You don&amp;rsquo;t need any programming knowledge — just work through the page from top to
bottom.&lt;/p>
&lt;p>How it works:
Each time you change an option on this page, that choice is written to a configuration
file for your session. When you create the firmware at the bottom of the page, this
configuration file is read and used to build the firmware. So, you can go back and
change your selections as many times as you want before you generate the firmware.&lt;/p>
&lt;p>If the option you&amp;rsquo;re setting has a &amp;lsquo;submit&amp;rsquo; button, the value you enter isn&amp;rsquo;t written to
the configuration file until you click that button. The two on/off switches (Audio and
Keyboard) save themselves the moment you click them — there&amp;rsquo;s no separate submit button
for those.&lt;/p>
&lt;h1 id="using-the-t-embed-configuration-website">Using the T-Embed configuration website&lt;/h1>
&lt;p>Using this website you will configure and download a file for your T-Embed which will be
called e.g.&lt;/p>
&lt;p>zeuss-tembed-firmware.bin&lt;/p>
&lt;p>You are free to rename the file after you download it.&lt;/p>
&lt;p>The Greek god&amp;rsquo;s name at the start of the filename will probably be different from the
example and will change each time that you restart the configuration page. See below for
details of why this is done.&lt;/p>
&lt;h2 id="step-1--check-your-session-name">Step 1 — Check your session name&lt;/h2>
&lt;p>At the top of the page you&amp;rsquo;ll see your &lt;strong>session name&lt;/strong>.
This is a name of a Greek god.&lt;/p>
&lt;p>Each time you re-open the configuration page, you will be assigned a new session name.
This is done so that the firmware you build doesn&amp;rsquo;t get mixed up with anyone else&amp;rsquo;s.
The names are taken randomly from a pool of Greek god names.
When you next use the site you will probably get a different session name.&lt;/p>
&lt;p>The firmware filename produced by the website contains the session name, e.g.
zeuss-tembed-firmware.bin, so that you get the firmware for your device and not
somebody else&amp;rsquo;s, even if several people are using the site at the same time.&lt;/p>
&lt;p>You&amp;rsquo;re free to rename the firmware after you&amp;rsquo;ve downloaded it to your own device.
Renaming the file won&amp;rsquo;t change how it works, so long as you keep &amp;lsquo;.bin&amp;rsquo; at the end of
the filename. The &amp;lsquo;.bin&amp;rsquo; ending is required so that the flashing tool recognises the
file as a firmware file.&lt;/p>
&lt;p>If the session name is &amp;lsquo;undefined&amp;rsquo; then there is a problem with the website! Try again
in 10 minutes, or let me know and I&amp;rsquo;ll get it fixed.&lt;/p>
&lt;h2 id="step-2--turn-audio-on-shake-on-or-off">Step 2 — Turn &amp;ldquo;Audio on shake&amp;rdquo; on or off&lt;/h2>
&lt;p>Under &lt;strong>Audio Control&lt;/strong>, the toggle switch controls whether the T-Embed plays a short
sound — a click, like a stick being tapped — whenever it receives a shake signal from
the paired watch. This gives you, or whoever is nearby, an audible confirmation that a
shake has been picked up, separate from anything that happens on the connected device.&lt;/p>
&lt;p>With this switched on, you also get the same sound when you double-click the T-Embed&amp;rsquo;s
button to test the switch, which is useful when you&amp;rsquo;re setting things up.&lt;/p>
&lt;p>Click the switch to turn it on (it turns green) or off. There&amp;rsquo;s no submit button to
click — the setting is saved as soon as you click the switch.&lt;/p>
&lt;h2 id="step-3--turn-keyboard-on-shake-on-or-off">Step 3 — Turn &amp;ldquo;Keyboard on shake&amp;rdquo; on or off&lt;/h2>
&lt;p>Under &lt;strong>Keyboard Control&lt;/strong>, the toggle switch controls whether the T-Embed acts as a
USB keyboard plugged into whatever it&amp;rsquo;s connected to (a computer, tablet, or other
device that accepts a USB keyboard). When this is switched on, each time the T-Embed
receives a shake signal from the paired watch it types a single letter — the letter
&lt;strong>a&lt;/strong> — into that device.&lt;/p>
&lt;p>This matters when you set up your communication software: whatever you want the shake to
do, set it to be triggered by the &lt;strong>a&lt;/strong> key.&lt;/p>
&lt;p>Click the switch to turn it on or off. As with Audio Control, this saves immediately —
there&amp;rsquo;s no separate submit button.&lt;/p>
&lt;p>You can turn both Audio Control and Keyboard Control on together, or use just one of
them, depending on what you need.&lt;/p>
&lt;h2 id="step-4--enter-the-t-watchs-mac-address">Step 4 — Enter the T-Watch&amp;rsquo;s MAC address&lt;/h2>
&lt;p>Under &lt;strong>Enter MAC Address for the Paired T-Watch&lt;/strong>, type in the MAC address of the
T-Watch S3 that this T-Embed should listen to. This is what pairs the two devices
together, so that your T-Embed only reacts to your own watch and ignores anybody
else&amp;rsquo;s.&lt;/p>
&lt;p>You can type the address either with colons (e.g. &lt;code>AA:BB:CC:DD:EE:FF&lt;/code>) or without them
(e.g. &lt;code>AABBCCDDEEFF&lt;/code>) — either format is accepted.&lt;/p>
&lt;p>If you don&amp;rsquo;t already know your watch&amp;rsquo;s MAC address, see the
&lt;a href="https://handshaketec.com/mac_identification/">Finding the MAC address of your module&lt;/a>
page for step-by-step instructions on reading it off the watch using a free website
called ESPConnect. Make sure you read the address &lt;strong>from the watch&lt;/strong>, not the T-Embed —
it&amp;rsquo;s easy to mix the two devices up when you have both plugged in.&lt;/p>
&lt;p>Once you&amp;rsquo;ve typed in the address, click &lt;strong>Submit MAC&lt;/strong>. A message will appear
confirming it&amp;rsquo;s been saved. If you see a warning about an invalid address instead,
double-check you&amp;rsquo;ve copied it correctly — the tips on the MAC identification page
about characters that look alike (0/O, 1/I/L, 8/B) are worth a look if you&amp;rsquo;re stuck.&lt;/p>
&lt;h2 id="step-5--generate-the-firmware">Step 5 — Generate the firmware&lt;/h2>
&lt;p>Once you&amp;rsquo;ve set the options above the way you want them, click &lt;strong>Generate Firmware&lt;/strong>.
The site reads your choices and builds the firmware file. This usually takes a minute
or two, and you&amp;rsquo;ll see progress messages while it works, so please be patient.&lt;/p>
&lt;p>The build carries on if you leave the page. Come back to this page and you&amp;rsquo;ll see its
progress, or its result if it has finished.&lt;/p>
&lt;h2 id="step-6--download-the-firmware">Step 6 — Download the firmware&lt;/h2>
&lt;p>While the build runs, the download button shows &lt;em>Building&amp;hellip;&lt;/em>. Once the build finishes,
a &lt;strong>download&lt;/strong> option appears. Click it to save the firmware file to your computer.&lt;/p>
&lt;p>Once it&amp;rsquo;s downloaded, the firmware is yours forever! You own it. No time limits, no
subscriptions. All yours.&lt;/p>
&lt;p>To put this firmware onto your T-Embed, see the
&lt;a href="https://handshaketec.com/flash_device_help/">Device Flasher help page&lt;/a> for step-by-step
instructions.&lt;/p></description></item><item><title>Finding the MAC address of your module</title><link>https://handshaketec.com/mac_identification/</link><pubDate>Thu, 10 Sep 2026 00:00:00 +0000</pubDate><guid>https://handshaketec.com/mac_identification/</guid><description>&lt;h1 id="what-is-a-mac">What is a MAC?&lt;/h1>
&lt;p>Every wireless device has a MAC address. It is a unique code set at the factory. You can think of it as the device&amp;rsquo;s serial number for wireless communication.&lt;/p>
&lt;p>A MAC address is 12 characters long. It is usually written in pairs separated by colons, for example:&lt;/p>
&lt;p>&lt;code>34:85:18:A1:2B:3C&lt;/code>&lt;/p>
&lt;p>It only ever contains the numbers 0 to 9 and the letters A to F.&lt;/p>
&lt;h1 id="why-does-handshake-need-it">Why does HandShake need it?&lt;/h1>
&lt;p>The watch and the receiver each need to know the other&amp;rsquo;s MAC address:&lt;/p>
&lt;ul>
&lt;li>The &lt;strong>watch&lt;/strong> needs the receiver&amp;rsquo;s MAC address so it knows where to send its signals.&lt;/li>
&lt;li>The &lt;strong>receiver&lt;/strong> needs the watch&amp;rsquo;s MAC address so it knows which watch to listen to. It will then ignore every other watch.&lt;/li>
&lt;/ul>
&lt;p>This matters when several people use HandShake in the same room. Each watch talks only to its own receiver and each receiver responds only to its own watch.&lt;/p>
&lt;h1 id="how-to-find-the-mac-addresses">How to find the MAC addresses&lt;/h1>
&lt;p>You need two MAC addresses: one for the watch and one for the receiver. You can read both using a free website called ESPConnect. Nothing needs to be installed.&lt;/p>
&lt;p>&lt;strong>You will need:&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>A computer running Google Chrome or Microsoft Edge. Other browsers such as Firefox and Safari will not work.&lt;/li>
&lt;li>A USB-C cable that carries data. Some cables only charge. If the device doesn&amp;rsquo;t appear in step 4, try a different cable.&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>Steps:&lt;/strong>&lt;/p>
&lt;p>Follow these steps for the watch, then repeat them for the receiver.&lt;/p>
&lt;ol>
&lt;li>Connect the device to the computer with the USB cable.&lt;/li>
&lt;li>Open Chrome or Edge and go to &lt;a href="https://thelastoutpostworkshop.github.io/ESPConnect/">https://thelastoutpostworkshop.github.io/ESPConnect/&lt;/a>&lt;/li>
&lt;li>Click &lt;strong>Connect&lt;/strong>.&lt;/li>
&lt;li>A box will pop up asking you to choose a device. Select the device. It is usually listed as &lt;em>USB JTAG/serial debug unit&lt;/em>. Then click &lt;strong>Connect&lt;/strong> in the box.&lt;/li>
&lt;li>Wait a few seconds for the page to finish connecting.&lt;/li>
&lt;li>Open the &lt;strong>Device Info&lt;/strong> tab. The MAC address is shown in the summary.&lt;/li>
&lt;li>Write the MAC address down and label it clearly as &lt;strong>watch&lt;/strong> or &lt;strong>receiver&lt;/strong>.&lt;/li>
&lt;li>Click &lt;strong>Disconnect&lt;/strong> and unplug the device.&lt;/li>
&lt;/ol>
&lt;p>If the device doesn&amp;rsquo;t start normally after unplugging, turn it off and on again.&lt;/p>
&lt;h1 id="tips-for-entering-the-mac-addresses">Tips for entering the MAC addresses&lt;/h1>
&lt;ul>
&lt;li>Don&amp;rsquo;t swap them. The watch is given the &lt;strong>receiver&amp;rsquo;s&lt;/strong> address. The receiver is given the &lt;strong>watch&amp;rsquo;s&lt;/strong> address.&lt;/li>
&lt;li>Copy each address carefully, one pair at a time.&lt;/li>
&lt;li>Watch out for characters that look alike. A MAC address never contains the letter O, so it is always the number zero. The same goes for I and L, which are always the number 1.&lt;/li>
&lt;li>Check that you haven&amp;rsquo;t mixed up 8 and B.&lt;/li>
&lt;li>Capital or lower-case letters make no difference.&lt;/li>
&lt;/ul>
&lt;p>If the watch and receiver don&amp;rsquo;t connect, a mistyped or swapped MAC address is the most likely cause. Check both addresses character by character before trying anything else.&lt;/p></description></item><item><title>Getting Started</title><link>https://handshaketec.com/getting_started/</link><pubDate>Fri, 21 Aug 2026 00:00:00 +0000</pubDate><guid>https://handshaketec.com/getting_started/</guid><description>&lt;p>This page shows how to get started with setting up the HandShake project.&lt;/p>
&lt;h1 id="what-youll-need">What you&amp;rsquo;ll need&lt;/h1>
&lt;p>&lt;a href="https://lilygo.cc/products/t-watch-s3">Lilygo T-Watch S3&lt;/a>, (~$45)&lt;/p>
&lt;p>&lt;a href="https://lilygo.cc/products/t-embed">Lilygo T-Embed&lt;/a> (~$30)&lt;/p>
&lt;p>One USB micro cable to connect with the T-Watch for programming and charging.&lt;/p>
&lt;p>One USB C cable to connect with the T-Embed for programming, to connect to the device you want to send a keystroke to and for powering the T-Embed through.&lt;/p>
&lt;h1 id="getting-started-with-the-t-watch-s3-and-the-t-embed">Getting started with the T-Watch S3 and the T-Embed&lt;/h1>
&lt;p>The first time that you use the T-Watch S3, you&amp;rsquo;ll need to turn on the battery. Instructions and photos showing how to do this are &lt;a href="https://handshaketec.com/t_watch_s3_first_time_use">on this page&lt;/a>.
I got caught out by not switching on the battery!&lt;/p>
&lt;p>The T-Embed is powered through the USB cable so you don&amp;rsquo;t need to worry about turning on a battery for this.&lt;/p>
&lt;p>Once you&amp;rsquo;ve checked that the two devices are in good working order, you need to program each device with the HandShake code.&lt;/p>
&lt;p>For each device you work through the relevant configuration page, generate the code for the device, then download and save the code to your own laptop or PC. Now you own the code. It&amp;rsquo;s yours.&lt;/p>
&lt;p>Once you have the code saved, you can load it on to the watch or receiver. This is called &amp;lsquo;flashing&amp;rsquo; the device as the memory that the code is written to is called flash memory.&lt;/p>
&lt;p>Each configuration page has a comprehensive help page.&lt;/p>
&lt;p>These are the pages that allow you to generate and download the code:&lt;/p>
&lt;p>&lt;a href="https://handshaketec.com/watch_configuration/">T-Watch-S3 configuration page&lt;/a>&lt;/p>
&lt;p>&lt;a href="https://handshaketec.com/embed_configuration/">T-Embed configuration page&lt;/a>&lt;/p>
&lt;p>These are the help pages for the configuration pages:&lt;/p>
&lt;p>&lt;a href="https://handshaketec.com/watch_configuration_help/">T-Watch-S3 configuration help page&lt;/a>&lt;/p>
&lt;p>&lt;a href="https://handshaketec.com/embed_configuration_help/">T-Embed configuration help page&lt;/a>&lt;/p>
&lt;p>Load the firmware onto the device using the &lt;a href="https://handshaketec.com/flash_device/">device flasher page&lt;/a>&lt;/p></description></item><item><title>HandShake T-Embed configuration</title><link>https://handshaketec.com/embed_configuration/</link><pubDate>Fri, 21 Aug 2026 00:00:00 +0000</pubDate><guid>https://handshaketec.com/embed_configuration/</guid><description>&lt;!-- HTMX loaded locally to avoid CDN dependency and improve performance -->
&lt;head>
&lt;script src="https://handshaketec.com/static/js/htmx.min.js">&lt;/script>

&lt;div id="backend-status-banner" role="alert" aria-live="assertive" hidden
 style="border:1px solid #b45309;border-left:4px solid #b45309;background:#fffbeb;
 color:#7c2d12;padding:0.85rem 1rem;border-radius:4px;margin:1rem 0;
 font-family:system-ui,-apple-system,'Segoe UI',sans-serif;line-height:1.5;">
 &lt;strong style="display:block;margin-bottom:0.25rem;">Cannot reach the server&lt;/strong>
 &lt;span>
 This page cannot contact the configuration service, so the controls below will
 not work. Check your connection and reload the page.
 &lt;/span>
&lt;/div>

&lt;script>
(function () {
 if (typeof htmx === 'undefined') {
 console.error('backend_status: htmx is not loaded yet — include this shortcode after the htmx script tag.');
 return;
 }

 
 
 
 
 
 htmx.config.responseHandling = [
 { code: '204', swap: false },
 { code: '[23]..', swap: true },
 { code: '5..', swap: true, error: true },
 { code: '[45]..', swap: false, error: true }
 ];

 
 document.body.addEventListener('htmx:sendError', function () {
 var banner = document.getElementById('backend-status-banner');
 if (banner) banner.hidden = false;
 });
})();
&lt;/script>


&lt;script>
window.csrfToken = null;
window.csrfTokenError = null;



document.addEventListener('htmx:configRequest', (event) => {
 if (window.csrfToken) {
 event.detail.headers['X-CSRFToken'] = window.csrfToken;
 }
});














window.sessionReady = fetch('/whoami', { signal: AbortSignal.timeout(10000) })
 .then(r => r.json().then(data => {
 if (!r.ok) throw Object.assign(new Error(data.error || `HTTP ${r.status}`), { data });
 return data;
 }));

window.sessionReady
 .then(() => fetch('/csrf_token', { signal: AbortSignal.timeout(10000) }))
 .then(r => {
 if (!r.ok) throw new Error(`HTTP ${r.status}`);
 return r.json();
 })
 .then(data => {
 window.csrfToken = data.csrf_token;
 
 document.body.dispatchEvent(new CustomEvent('session-ready'));
 })
 .catch(err => {
 console.error('Failed to initialize page:', err);
 window.csrfTokenError = err.message;
 const statusEl = document.getElementById('csrf-error');
 if (statusEl) {
 statusEl.textContent = 'Security error: Could not initialize. Please refresh the page.';
 statusEl.style.color = '#dc3545';
 statusEl.style.padding = '10px';
 statusEl.style.backgroundColor = '#ffe6e6';
 statusEl.style.marginBottom = '15px';
 statusEl.style.borderRadius = '4px';
 }
 });
&lt;/script>

&lt;/head>
&lt;h1 id="introduction">Introduction&lt;/h1>
&lt;p>HandShake configuration tool for the T-Embed device.&lt;/p>
&lt;p>&lt;strong>Under test - something will need fixing!&lt;/strong>&lt;/p>
&lt;div style="background-color: #fff3cd; border: 1px solid #ffc107; border-radius: 4px; padding: 15px; margin-bottom: 20px; color: #856404; border-left: 4px solid #ff9800;">
 &lt;strong>⚠️ Disclaimer:&lt;/strong> ** THIS SITE IS INCOMPLETE - FOR USE FOR TESTING BY THE DEVELOPERS ONLY **
HandShake is free, open-source software provided as-is, with no warranty of any kind. It configures unmodified, manufacturer-certified hardware (Lilygo T-Watch S3 / T-Embed) that you purchase separately. Before relying on any device for communication access, test its switch response thoroughly with a professional (Occupational Therapist, Speech and Language Therapist, Technologist, experienced Carer). Report issues to &lt;a href="matt@mattoppenheim.com">matt@mattoppenheim.com&lt;/a>
&lt;/div>

&lt;div id="csrf-error">&lt;/div>
&lt;!-- shortcodes are in layouts/shortcodes -->

&lt;link rel="stylesheet" href="https://handshaketec.com/css/watch-configuration.css?v=3">

&lt;div class="session-box" id="session-box">
 &lt;h2>Your session name&lt;/h2>
 &lt;div class="session-name" id="session-name">…&lt;/div>
&lt;/div>

&lt;script type="module">
 import { SessionNameComponent } from '/js/components/session-name.js';
 new SessionNameComponent('session-box');
&lt;/script>

&lt;script>
(function () {
 function init() {
 const form = document.getElementById("mac_form_tembed");
 if (!form) return;

 const errorDiv = document.getElementById("mac-twatch-submit-error");
 const responseDiv = document.getElementById("mac_response_tembed");

 form.addEventListener('htmx:responseError', function () {
 if (errorDiv) errorDiv.style.display = 'block';
 });

 form.addEventListener('htmx:beforeRequest', function () {
 if (errorDiv) errorDiv.style.display = 'none';
 if (responseDiv) responseDiv.innerHTML = '';
 });
 }

 
 
 if (document.readyState === 'loading') {
 document.addEventListener('DOMContentLoaded', init);
 } else {
 init();
 }
})();
&lt;/script>

&lt;h1 id="audio-control-tembed">Audio Control&lt;/h1>

&lt;div class="audio-control-section">
 &lt;label for="audio-toggle-tembed" class="audio-label">Audio on shake:&lt;/label>
 &lt;div class="toggle-wrapper">
 &lt;div hx-get="/get_audio_input_tembed" hx-trigger="load, session-ready from:body" hx-swap="innerHTML" hx-on::afterSwap="htmx.process(this)">
 &lt;input type="checkbox" id="audio-toggle-tembed" name="audio_enabled" class="toggle-checkbox" hx-post="/submit_audio_tembed" hx-trigger="change" hx-target="#audio-response-tembed" hx-swap="innerHTML">
 &lt;/div>
 &lt;label for="audio-toggle-tembed" class="toggle-label">&lt;/label>
 &lt;/div>
 &lt;div id="audio-load-error" class="toggle-error" style="display:none;">
 ⚠️ Failed to load audio setting. Please refresh the page.
 &lt;/div>
 
 &lt;div id="audio-response-tembed" class="audio-response"
 hx-get="/get_audio_status_tembed" hx-trigger="load, session-ready from:body" hx-swap="innerHTML">&lt;/div>
&lt;/div>

&lt;style>
 .audio-control-section {
 padding: 1em;
 border: 1px solid #ddd;
 border-radius: 6px;
 background: #fafafa;
 margin-bottom: 1.5em;
 }

 .audio-label {
 display: block;
 margin-bottom: 0.5em;
 font-weight: bold;
 color: #333;
 }

 .toggle-wrapper {
 display: flex;
 align-items: center;
 gap: 1em;
 }

 .toggle-checkbox {
 appearance: none;
 -webkit-appearance: none;
 width: 50px;
 height: 28px;
 background: #ccc;
 border-radius: 14px;
 cursor: pointer;
 position: relative;
 outline: none;
 transition: background 0.3s ease;
 }

 .toggle-checkbox:checked {
 background: #4caf50;
 }

 .toggle-checkbox::before {
 content: '';
 position: absolute;
 width: 24px;
 height: 24px;
 border-radius: 50%;
 background: white;
 top: 2px;
 left: 2px;
 transition: left 0.3s ease;
 box-shadow: 0 2px 4px rgba(0,0,0,0.2);
 }

 .toggle-checkbox:checked::before {
 left: 24px;
 }

 .toggle-label {
 display: inline-block;
 cursor: pointer;
 }

 .audio-response {
 margin-top: 0.5em;
 font-size: 0.9em;
 }

 .audio-response.success {
 color: #4caf50;
 padding: 0.5em;
 background: #e8f5e9;
 border-radius: 4px;
 }

 .audio-response.error {
 color: #f44336;
 padding: 0.5em;
 background: #ffebee;
 border-radius: 4px;
 }

 .toggle-error {
 margin-top: 0.5em;
 padding: 0.75em;
 background-color: #fff3cd;
 color: #856404;
 border: 1px solid #ffc107;
 border-radius: 4px;
 font-size: 0.9em;
 }

 @media (prefers-color-scheme: dark) {
 .toggle-error {
 background-color: #664d03;
 color: #fff;
 border-color: #997404;
 }
 }
&lt;/style>


&lt;h1 id="keyboard-control-tembed">Keyboard Control&lt;/h1>

&lt;div class="keyboard-control-section">
 &lt;label for="keyboard-toggle-tembed" class="keyboard-label">USB keyboard on shake:&lt;/label>
 &lt;div class="toggle-wrapper">
 &lt;div hx-get="/get_keyboard_input_tembed" hx-trigger="load, session-ready from:body" hx-swap="innerHTML" hx-on::afterSwap="htmx.process(this)">
 &lt;input type="checkbox" id="keyboard-toggle-tembed" name="keyboard_enabled" class="toggle-checkbox" hx-post="/submit_keyboard_tembed" hx-trigger="change" hx-target="#keyboard-response-tembed" hx-swap="innerHTML">
 &lt;/div>
 &lt;label for="keyboard-toggle-tembed" class="toggle-label">&lt;/label>
 &lt;/div>
 &lt;div id="keyboard-load-error" class="toggle-error" style="display:none;">
 ⚠️ Failed to load keyboard setting. Please refresh the page.
 &lt;/div>
 
 &lt;div id="keyboard-response-tembed" class="keyboard-response"
 hx-get="/get_keyboard_status_tembed" hx-trigger="load, session-ready from:body" hx-swap="innerHTML">&lt;/div>
&lt;/div>

&lt;style>
 .keyboard-control-section {
 padding: 1em;
 border: 1px solid #ddd;
 border-radius: 6px;
 background: #fafafa;
 margin-bottom: 1.5em;
 }

 .keyboard-label {
 display: block;
 margin-bottom: 0.5em;
 font-weight: bold;
 color: #333;
 }

 .toggle-wrapper {
 display: flex;
 align-items: center;
 gap: 1em;
 }

 .toggle-checkbox {
 appearance: none;
 -webkit-appearance: none;
 width: 50px;
 height: 28px;
 background: #ccc;
 border-radius: 14px;
 cursor: pointer;
 position: relative;
 outline: none;
 transition: background 0.3s ease;
 }

 .toggle-checkbox:checked {
 background: #2196f3;
 }

 .toggle-checkbox::before {
 content: '';
 position: absolute;
 width: 24px;
 height: 24px;
 border-radius: 50%;
 background: white;
 top: 2px;
 left: 2px;
 transition: left 0.3s ease;
 box-shadow: 0 2px 4px rgba(0,0,0,0.2);
 }

 .toggle-checkbox:checked::before {
 left: 24px;
 }

 .toggle-label {
 display: inline-block;
 cursor: pointer;
 }

 .keyboard-response {
 margin-top: 0.5em;
 font-size: 0.9em;
 }

 .keyboard-response.success {
 color: #2196f3;
 padding: 0.5em;
 background: #e3f2fd;
 border-radius: 4px;
 }

 .keyboard-response.error {
 color: #f44336;
 padding: 0.5em;
 background: #ffebee;
 border-radius: 4px;
 }

 .toggle-error {
 margin-top: 0.5em;
 padding: 0.75em;
 background-color: #fff3cd;
 color: #856404;
 border: 1px solid #ffc107;
 border-radius: 4px;
 font-size: 0.9em;
 }

 @media (prefers-color-scheme: dark) {
 .toggle-error {
 background-color: #664d03;
 color: #fff;
 border-color: #997404;
 }
 }
&lt;/style>


&lt;h1 id="esp-now-mac-address-configuration-tembed">Enter MAC Address for the Paired T-Watch&lt;/h1>

&lt;div id="esp-now-mac-tembed-component">
 &lt;form hx-target="#mac_submit_response_tembed" hx-post="/submit_mac_tembed" hx-swap="innerHTML" hx-on::after-request="htmx.ajax('GET', '/get_mac_status_tembed', {target: '#mac_response_tembed', swap: 'innerHTML'})" id="mac_form_tembed">
 &lt;label for="mac_address_tembed">T-Watch MAC Address (e.g. AA:BB:CC:DD:EE:FF or AABBCCDDEEFF):&lt;/label>&lt;br>
 &lt;div hx-get="/get_mac_input_tembed" hx-trigger="load, session-ready from:body" hx-swap="innerHTML" hx-on::response-error="document.getElementById('mac-twatch-load-error').style.display='block'">
 &lt;input type="text" name="mac_address" id="mac_address_tembed" class="mac-address-input" required
 maxlength="23" placeholder="AA:BB:CC:DD:EE:FF or AABBCCDDEEFF" style="margin-top: 10px;">
 &lt;/div>
 &lt;div id="mac-twatch-load-error" class="control-error" style="display:none; margin-top: 10px;">
 ⚠️ Failed to load MAC address field. Please refresh the page.
 &lt;/div>
 &lt;div id="mac-twatch-submit-error" class="control-error" style="display:none; margin-top: 10px;">
 ⚠️ Invalid MAC address. Please enter a valid address (e.g., AA:BB:CC:DD:EE:FF).
 &lt;/div>
 &lt;br>
 &lt;input type="submit" value="Submit MAC" class="button-primary">
 &lt;/form>
 &lt;div id="mac_submit_response_tembed" style="display: none;">&lt;/div>
 &lt;div id="mac_response_tembed" hx-get="/get_mac_status_tembed" hx-trigger="load, session-ready from:body" hx-swap="innerHTML">&lt;/div>

&lt;style>
 .control-error {
 padding: 0.75em;
 background-color: #fff3cd;
 color: #856404;
 border: 1px solid #ffc107;
 border-radius: 4px;
 font-size: 0.9em;
 }

 #mac_response_tembed {
 margin-top: 10px;
 font-size: 0.9em;
 color: #333;
 }

 @media (prefers-color-scheme: dark) {
 .control-error {
 background-color: #664d03;
 color: #fff;
 border-color: #997404;
 }
 #mac_response_tembed {
 color: #e0e0e0;
 }
 }
&lt;/style>
&lt;/div>



&lt;h1>Generate T-Embed Firmware&lt;/h1>

&lt;div id="generate-container-tembed">
 &lt;button
 id="generate-btn-tembed"
 hx-post="/generate_firmware_tembed"
 hx-trigger="click"
 hx-swap="none"
 class="generate-button"
 hx-on::after-request="window.onGenerateRequestFinishedTembed?.(event)"
 >
 Generate Firmware
 &lt;/button>

 &lt;p id="other-build-note-tembed" class="build-note" hidden>&lt;/p>

 &lt;div id="throbber-tembed" style="display:none; margin-top:10px;">
 &lt;div class="spinner">&lt;/div>
 &lt;p>Building T-Embed firmware, please wait...&lt;/p>
 &lt;/div>

 &lt;div id="output-box-tembed" style="display:none; margin-top:10px;">
 &lt;div class="output-container">
 &lt;h3>Build Output:&lt;/h3>
 &lt;pre id="output-text-tembed" class="output-text">&lt;/pre>
 &lt;div id="build-result-tembed">&lt;/div>
 &lt;/div>
 &lt;/div>
&lt;/div>


&lt;script type="module">
 import { GenerateFirmwareComponent } from '/js/components/generate-firmware.js?v=3';

 
 console.log('Checking elements:');
 console.log('generate-container-tembed:', !!document.getElementById('generate-container-tembed'));
 console.log('output-box-tembed:', !!document.getElementById('output-box-tembed'));
 console.log('throbber-tembed:', !!document.getElementById('throbber-tembed'));
 console.log('output-text-tembed:', !!document.getElementById('output-text-tembed'));

 new GenerateFirmwareComponent('generate-container-tembed', 'tembed');
&lt;/script>

&lt;h1>Download T-Embed Firmware&lt;/h1>

&lt;div id="download-elf-container-tembed">
 
 &lt;div
 id="elf-check-container-tembed"
 hx-get="/check_elf_tembed"
 hx-trigger="load, refreshDownload from:body"
 hx-swap="innerHTML"
 hx-on::response-error="document.getElementById('download-elf-tembed-load-error').style.display='block'"
 >
 &lt;p>Checking for firmware file...&lt;/p>
 &lt;/div>
 &lt;div id="download-elf-tembed-load-error" class="control-error" style="display:none; margin-top: 10px;">
 ⚠️ Failed to check firmware file. Please refresh the page.
 &lt;/div>
&lt;/div>

&lt;style>
 .download-button {
 padding: 10px 20px;
 font-size: 16px;
 background-color: #28a745;
 color: white;
 border: none;
 border-radius: 5px;
 cursor: pointer;
 transition: background-color 0.3s;
 text-decoration: none;
 display: inline-block;
 }

 .download-button:hover {
 background-color: #218838;
 }

 .download-button:active {
 background-color: #1e7e34;
 }

 .file-info {
 margin-top: 10px;
 padding: 10px;
 background-color: #f8f9fa;
 border: 1px solid #dee2e6;
 border-radius: 5px;
 font-size: 14px;
 color: #495057;
 }

 .no-file-message {
 color: #6c757d;
 font-style: italic;
 }

 .control-error {
 padding: 0.75em;
 background-color: #fff3cd;
 color: #856404;
 border: 1px solid #ffc107;
 border-radius: 4px;
 font-size: 0.9em;
 }

 @media (prefers-color-scheme: dark) {
 .control-error {
 background-color: #664d03;
 color: #fff;
 border-color: #997404;
 }
 }
&lt;/style>
</description></item><item><title>HandShake T-Watch S3 configuration</title><link>https://handshaketec.com/watch_configuration/</link><pubDate>Fri, 21 Aug 2026 00:00:00 +0000</pubDate><guid>https://handshaketec.com/watch_configuration/</guid><description>&lt;!-- HTMX loaded locally to avoid CDN dependency and improve performance -->
&lt;head>
&lt;script src="https://handshaketec.com/static/js/htmx.min.js">&lt;/script>

&lt;div id="backend-status-banner" role="alert" aria-live="assertive" hidden
 style="border:1px solid #b45309;border-left:4px solid #b45309;background:#fffbeb;
 color:#7c2d12;padding:0.85rem 1rem;border-radius:4px;margin:1rem 0;
 font-family:system-ui,-apple-system,'Segoe UI',sans-serif;line-height:1.5;">
 &lt;strong style="display:block;margin-bottom:0.25rem;">Cannot reach the server&lt;/strong>
 &lt;span>
 This page cannot contact the configuration service, so the controls below will
 not work. Check your connection and reload the page.
 &lt;/span>
&lt;/div>

&lt;script>
(function () {
 if (typeof htmx === 'undefined') {
 console.error('backend_status: htmx is not loaded yet — include this shortcode after the htmx script tag.');
 return;
 }

 
 
 
 
 
 htmx.config.responseHandling = [
 { code: '204', swap: false },
 { code: '[23]..', swap: true },
 { code: '5..', swap: true, error: true },
 { code: '[45]..', swap: false, error: true }
 ];

 
 document.body.addEventListener('htmx:sendError', function () {
 var banner = document.getElementById('backend-status-banner');
 if (banner) banner.hidden = false;
 });
})();
&lt;/script>


&lt;script>
window.csrfToken = null;
window.csrfTokenError = null;



document.addEventListener('htmx:configRequest', (event) => {
 if (window.csrfToken) {
 event.detail.headers['X-CSRFToken'] = window.csrfToken;
 }
});














window.sessionReady = fetch('/whoami', { signal: AbortSignal.timeout(10000) })
 .then(r => r.json().then(data => {
 if (!r.ok) throw Object.assign(new Error(data.error || `HTTP ${r.status}`), { data });
 return data;
 }));

window.sessionReady
 .then(() => fetch('/csrf_token', { signal: AbortSignal.timeout(10000) }))
 .then(r => {
 if (!r.ok) throw new Error(`HTTP ${r.status}`);
 return r.json();
 })
 .then(data => {
 window.csrfToken = data.csrf_token;
 
 document.body.dispatchEvent(new CustomEvent('session-ready'));
 })
 .catch(err => {
 console.error('Failed to initialize page:', err);
 window.csrfTokenError = err.message;
 const statusEl = document.getElementById('csrf-error');
 if (statusEl) {
 statusEl.textContent = 'Security error: Could not initialize. Please refresh the page.';
 statusEl.style.color = '#dc3545';
 statusEl.style.padding = '10px';
 statusEl.style.backgroundColor = '#ffe6e6';
 statusEl.style.marginBottom = '15px';
 statusEl.style.borderRadius = '4px';
 }
 });
&lt;/script>

&lt;/head>
&lt;h1 id="introduction">Introduction&lt;/h1>
&lt;p>HandShake configuration tool for the T-Watch S3.&lt;/p>
&lt;p>&lt;strong>Under test - something will need fixing!&lt;/strong>&lt;/p>
&lt;div style="background-color: #fff3cd; border: 1px solid #ffc107; border-radius: 4px; padding: 15px; margin-bottom: 20px; color: #856404; border-left: 4px solid #ff9800;">
 &lt;strong>⚠️ Disclaimer:&lt;/strong> ** THIS SITE IS INCOMPLETE - FOR USE FOR TESTING BY THE DEVELOPERS ONLY **
HandShake is free, open-source software provided as-is, with no warranty of any kind. It configures unmodified, manufacturer-certified hardware (Lilygo T-Watch S3 / T-Embed) that you purchase separately. Before relying on any device for communication access, test its switch response thoroughly with a professional (Occupational Therapist, Speech and Language Therapist, Technologist, experienced Carer). Report issues to &lt;a href="matt@mattoppenheim.com">matt@mattoppenheim.com&lt;/a>
&lt;/div>

&lt;div id="csrf-error">&lt;/div>
&lt;!-- shortcodes are in layouts/shortcodes -->

&lt;link rel="stylesheet" href="https://handshaketec.com/css/watch-configuration.css?v=3">

&lt;div class="session-box" id="session-box">
 &lt;h2>Your session name&lt;/h2>
 &lt;div class="session-name" id="session-name">…&lt;/div>
&lt;/div>

&lt;script type="module">
 import { SessionNameComponent } from '/js/components/session-name.js';
 new SessionNameComponent('session-box');
&lt;/script>

&lt;script>
(function () {
 function init() {
 const form = document.getElementById("mac_form");
 if (!form) return;

 const errorDiv = document.getElementById("mac-embed-submit-error");
 const responseDiv = document.getElementById("mac_response");

 form.addEventListener('htmx:responseError', function () {
 if (errorDiv) errorDiv.style.display = 'block';
 });

 form.addEventListener('htmx:beforeRequest', function () {
 if (errorDiv) errorDiv.style.display = 'none';
 if (responseDiv) responseDiv.innerHTML = '';
 });
 }

 
 
 if (document.readyState === 'loading') {
 document.addEventListener('DOMContentLoaded', init);
 } else {
 init();
 }
})();
&lt;/script>

&lt;h1 id="gesture-selection">Gesture Selection&lt;/h1>

&lt;form id="gesture_to_detect">
 
 &lt;div hx-get="/get_gesture_selection_input"
 hx-trigger="load, session-ready from:body"
 hx-swap="innerHTML"
 hx-on::afterSwap="htmx.process(this)"
 hx-on::response-error="document.getElementById('gesture-load-error').style.display='block'">
 &lt;/div>
 &lt;div id="gesture-load-error" class="control-error" style="display:none; margin-top: 10px;">
 ⚠️ Failed to load gesture control. Please refresh the page.
 &lt;/div>
&lt;/form>


&lt;div id="gesture_response"
 hx-get="/get_gesture_status" hx-trigger="load, session-ready from:body" hx-swap="innerHTML">&lt;/div>

&lt;style>
 .control-error {
 padding: 0.75em;
 background-color: #fff3cd;
 color: #856404;
 border: 1px solid #ffc107;
 border-radius: 4px;
 font-size: 0.9em;
 }

 @media (prefers-color-scheme: dark) {
 .control-error {
 background-color: #664d03;
 color: #fff;
 border-color: #997404;
 }
 }
&lt;/style>



&lt;h1 id="gesture-sensitivity-control">Gesture Sensitivity Control&lt;/h1>

&lt;form id="sensitivity_form">
 &lt;label for="gesture_sensitivity">Trigger threshold (580-900):&lt;/label>&lt;br>
 &lt;div style="display: flex; align-items: center; gap: 1em; margin-top: 10px;">
 &lt;span style="font-size: 0.9em; color: #666; min-width: 50px;">softer shake&lt;/span>
 &lt;div style="min-width: 200px;">
 &lt;div hx-get="/get_gesture_sensitivity_input" hx-trigger="load, session-ready from:body" hx-swap="innerHTML" hx-on::afterSwap="htmx.process(this)" hx-on::response-error="document.getElementById('sensitivity-load-error').style.display='block'" style="display: flex; flex: 1; min-width: 200px;">
 &lt;input type="range" id="gesture_sensitivity" name="gesture_sensitivity" style="flex: 1; min-width: 200px;">
 &lt;/div>
 &lt;div style="position: relative; height: 1.5em; margin-top: 0.25em; font-size: 0.75em; color: #999;">
 &lt;span style="position: absolute; left: 0%; transform: translateX(-50%);">580&lt;/span>
 &lt;span style="position: absolute; left: 37.5%; transform: translateX(-50%);">700&lt;/span>
 &lt;span style="position: absolute; left: 68.75%; transform: translateX(-50%);">800&lt;/span>
 &lt;span style="position: absolute; left: 100%; transform: translateX(-50%);">900&lt;/span>
 &lt;/div>
 &lt;/div>
 &lt;span style="font-size: 0.9em; color: #666; min-width: 50px;">harder shake&lt;/span>
 &lt;/div>
 &lt;div id="sensitivity-load-error" class="control-error" style="display:none; margin-top: 10px;">
 ⚠️ Failed to load sensitivity control. Please refresh the page.
 &lt;/div>
 &lt;br>&lt;br>
&lt;/form>

&lt;div id="sensitivity_response"
 hx-get="/get_gesture_sensitivity_status" hx-trigger="load, session-ready from:body" hx-swap="innerHTML">&lt;/div>

&lt;style>
 .control-error {
 padding: 0.75em;
 background-color: #fff3cd;
 color: #856404;
 border: 1px solid #ffc107;
 border-radius: 4px;
 font-size: 0.9em;
 }

 @media (prefers-color-scheme: dark) {
 .control-error {
 background-color: #664d03;
 color: #fff;
 border-color: #997404;
 }
 }
&lt;/style>



&lt;h1 id="min-shake-interval-control">Minimum Shake Interval Control&lt;/h1>

&lt;form id="shake_interval_form">
 &lt;label for="min_shake_interval">Minimum Intershake Interval (ms) (0-1000):&lt;/label>&lt;br>
 &lt;div style="display: flex; align-items: center; gap: 1em; margin-top: 10px;">
 &lt;span style="font-size: 0.9em; color: #666; min-width: 50px;">&lt;/span>
 &lt;div style="min-width: 200px;">
 &lt;div hx-get="/get_twatch_config_input" hx-trigger="load, session-ready from:body" hx-swap="innerHTML" hx-on::afterSwap="htmx.process(this)" hx-on::response-error="document.getElementById('shake-load-error').style.display='block'" style="display: flex; flex: 1; min-width: 200px;">
 &lt;input type="range" id="min_shake_interval" name="min_shake_interval" style="flex: 1; min-width: 200px;">
 &lt;/div>
 &lt;div style="position: relative; height: 1.5em; margin-top: 0.25em; font-size: 0.75em; color: #999;">
 &lt;span style="position: absolute; left: 0%; transform: translateX(-50%);">0&lt;/span>
 &lt;span style="position: absolute; left: 25%; transform: translateX(-50%);">250&lt;/span>
 &lt;span style="position: absolute; left: 50%; transform: translateX(-50%);">500&lt;/span>
 &lt;span style="position: absolute; left: 75%; transform: translateX(-50%);">750&lt;/span>
 &lt;span style="position: absolute; left: 100%; transform: translateX(-50%);">1000&lt;/span>
 &lt;/div>
 &lt;/div>
 &lt;span style="font-size: 0.9em; color: #666; min-width: 50px;">&lt;/span>
 &lt;/div>
 &lt;div id="shake-load-error" class="control-error" style="display:none; margin-top: 10px;">
 ⚠️ Failed to load shake interval control. Please refresh the page.
 &lt;/div>
 &lt;br>&lt;br>
&lt;/form>

&lt;div id="shake_interval_response"
 hx-get="/get_shake_interval_status" hx-trigger="load, session-ready from:body" hx-swap="innerHTML">&lt;/div>

&lt;style>
 .control-error {
 padding: 0.75em;
 background-color: #fff3cd;
 color: #856404;
 border: 1px solid #ffc107;
 border-radius: 4px;
 font-size: 0.9em;
 }

 @media (prefers-color-scheme: dark) {
 .control-error {
 background-color: #664d03;
 color: #fff;
 border-color: #997404;
 }
 }
&lt;/style>



&lt;h1 id="esp-now-mac-address-configuration">Enter MAC Address for the T-Embed&lt;/h1>

&lt;div id="esp-now-mac-component">
 &lt;form hx-target="#mac_submit_response" hx-post="/submit_mac" hx-swap="innerHTML" hx-on::after-request="htmx.ajax('GET', '/get_mac_status', {target: '#mac_response', swap: 'innerHTML'})" id="mac_form">
 &lt;label for="mac_address">T-Embed MAC Address (e.g. AA:BB:CC:DD:EE:FF or AABBCCDDEEFF):&lt;/label>&lt;br>
 &lt;div hx-get="/get_mac_input" hx-trigger="load, session-ready from:body" hx-swap="innerHTML">
 &lt;input type="text" name="mac_address" id="mac_address" class="mac-address-input" required
 maxlength="23" placeholder="AA:BB:CC:DD:EE:FF or AABBCCDDEEFF" style="margin-top: 10px;">
 &lt;/div>
 &lt;div id="mac-embed-load-error" class="control-error" style="display:none; margin-top: 10px;">
 ⚠️ Failed to load MAC address field. Please refresh the page.
 &lt;/div>
 &lt;div id="mac-embed-submit-error" class="control-error" style="display:none; margin-top: 10px;">
 ⚠️ Invalid MAC address. Please enter a valid address (e.g., AA:BB:CC:DD:EE:FF).
 &lt;/div>
 &lt;br>
 &lt;input type="submit" value="Submit MAC" class="button-primary">
 &lt;/form>
 &lt;div id="mac_submit_response" style="display: none;">&lt;/div>
 &lt;div id="mac_response" hx-get="/get_mac_status" hx-trigger="load, session-ready from:body" hx-swap="innerHTML">&lt;/div>

&lt;style>
 .control-error {
 padding: 0.75em;
 background-color: #fff3cd;
 color: #856404;
 border: 1px solid #ffc107;
 border-radius: 4px;
 font-size: 0.9em;
 }

 #mac_response {
 margin-top: 10px;
 font-size: 0.9em;
 color: #333;
 }

 @media (prefers-color-scheme: dark) {
 .control-error {
 background-color: #664d03;
 color: #fff;
 border-color: #997404;
 }
 #mac_response {
 color: #e0e0e0;
 }
 }
&lt;/style>
&lt;/div>



&lt;h1>Generate Firmware&lt;/h1>

&lt;div id="generate-container">
 &lt;button
 id="generate-btn"
 hx-post="/generate_firmware"
 hx-trigger="click"
 hx-swap="none"
 class="generate-button"
 hx-on::after-request="window.onGenerateRequestFinished?.(event)"
 >
 Generate Firmware
 &lt;/button>

 &lt;p id="other-build-note" class="build-note" hidden>&lt;/p>

 &lt;div id="throbber" style="display:none; margin-top:10px;">
 &lt;div class="spinner">&lt;/div>
 &lt;p>Building firmware, please wait...&lt;/p>
 &lt;/div>

 &lt;div id="output-box" style="display:none; margin-top:10px;">
 &lt;div class="output-container">
 &lt;h3>Build Output:&lt;/h3>
 &lt;pre id="output-text" class="output-text">&lt;/pre>
 &lt;div id="build-result">&lt;/div>
 &lt;/div>
 &lt;/div>
&lt;/div>


&lt;script type="module">
 import { GenerateFirmwareComponent } from '/js/components/generate-firmware.js?v=3';
 new GenerateFirmwareComponent('generate-container');
&lt;/script>

&lt;h1>Download Firmware&lt;/h1>

&lt;div id="download-elf-container">
 
 &lt;div
 id="elf-check-container"
 hx-get="/check_elf"
 hx-trigger="load, refreshDownload from:body"
 hx-swap="innerHTML"
 hx-on::response-error="document.getElementById('download-elf-load-error').style.display='block'"
 >
 &lt;p>Checking for firmware file...&lt;/p>
 &lt;/div>
 &lt;div id="download-elf-load-error" class="control-error" style="display:none; margin-top: 10px;">
 ⚠️ Failed to check firmware file. Please refresh the page.
 &lt;/div>
&lt;/div>

&lt;style>
 .download-button {
 padding: 10px 20px;
 font-size: 16px;
 background-color: #28a745;
 color: white;
 border: none;
 border-radius: 5px;
 cursor: pointer;
 transition: background-color 0.3s;
 text-decoration: none;
 display: inline-block;
 }
 
 .download-button:hover {
 background-color: #218838;
 }
 
 .download-button:active {
 background-color: #1e7e34;
 }
 
 .file-info {
 margin-top: 10px;
 padding: 10px;
 background-color: #f8f9fa;
 border: 1px solid #dee2e6;
 border-radius: 5px;
 font-size: 14px;
 color: #495057;
 }
 
 .no-file-message {
 color: #6c757d;
 font-style: italic;
 }

 .control-error {
 padding: 0.75em;
 background-color: #fff3cd;
 color: #856404;
 border: 1px solid #ffc107;
 border-radius: 4px;
 font-size: 0.9em;
 }

 @media (prefers-color-scheme: dark) {
 .control-error {
 background-color: #664d03;
 color: #fff;
 border-color: #997404;
 }
 }
&lt;/style>
</description></item><item><title>Conformance &amp; Certifications</title><link>https://handshaketec.com/compliance/</link><pubDate>Tue, 04 Aug 2026 00:00:00 +0000</pubDate><guid>https://handshaketec.com/compliance/</guid><description>&lt;h1 id="lilygo-t-watch-s3">Lilygo T-Watch S3&lt;/h1>
&lt;ul>
&lt;li>&lt;a href="https://handshaketec.com/documents/compliance/t_watch_s3_ce_emc_certificates/T-WATCH%20S3_2401U38108E%20ROHS%20Report.pdf">RoHS Report&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://handshaketec.com/documents/compliance/t_watch_s3_ce_emc_certificates/T-WATCH%20S3_C2401U21148E-EM%20EMC%20AOC.pdf">EMC AOC CE)&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://handshaketec.com/documents/compliance/t_watch_s3_ce_emc_certificates/T-WATCH%20S3_C2401U21148E-RF%2003-C2%20RED%20210115---CE.pdf">RED Certificate (CE)&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://handshaketec.com/documents/compliance/t_watch_s3_ce_emc_certificates/T-WATCH%20S3_Shenzhen%20Xin%20Yuan%202ASYE-T-WATCH-S3%20FCC%20Grant.pdf">FCC Grant&lt;/a>&lt;/li>
&lt;/ul>
&lt;h1 id="lilygo-t-embed">Lilygo T-Embed&lt;/h1>
&lt;ul>
&lt;li>&lt;a href="https://handshaketec.com/documents/compliance/t_embed_ce_emc_certificates/CE_33377632-f4f3-44b9-b0ec-a1ef26f14121.pdf">CE Certification&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://handshaketec.com/documents/compliance/t_embed_ce_emc_certificates/FCC_e2ebae2b-61f8-400b-96d6-56b4c81ee28f.pdf">FCC Certification&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>About the project</title><link>https://handshaketec.com/about/</link><pubDate>Thu, 30 Jul 2026 00:00:00 +0000</pubDate><guid>https://handshaketec.com/about/</guid><description>&lt;h1 id="the-handshake-project">The HandShake Project&lt;/h1>
&lt;p>HandShake helps non-speaking people who cannot use standard button or joystick controls but can still make an intentional hand or arm movement.
The system also works when worn on the knee and leg.
The smart-watch (the T-Watch S3) detects an intentional shake using its accelerometer and sends a wireless trigger to a receiver (the T-Embed), which activates assistive technology switches - for example to drive communication software like Grid, or to operate environmental controls. HandShake is an AAC (assisted augmented communication) switch.
The sensitivity, haptic feedback and audio confirmation are all adjustable to suit each user.&lt;/p>
&lt;p>The project has gone through several hardware iterations, from home-made devices then micro:bits through to the current Lilygo-based T-Watch/T-Embed system.
It&amp;rsquo;s been under long-term testing and use at a specialist college under the supervision of a Technologist and a Speech Therapist.&lt;/p>
&lt;p>Read the full project write-up &lt;a href="https://mattoppenheim.com/projects/2018/02/handshake/">here&lt;/a>.&lt;/p>
&lt;h1 id="about-me">About me&lt;/h1>
&lt;p>I re-purpose off the shelf hardware to create enabling technology. All of the project ideas come from the staff who work at specialist schools with the target end-users.&lt;/p>
&lt;p>I used to spend about half of the year working at sea in marine geophysical survey or offshore construction but now mostly loiter at InfoLab21, Lancaster University.&lt;/p>
&lt;p>My GitHub page is &lt;a href="https://github.com/mattoppenheim">here&lt;/a>.&lt;/p>
&lt;p>I&amp;rsquo;ll be releasing the code for this project as open-source under the AGPL licence on Gitlab once it&amp;rsquo;s tested.&lt;/p></description></item><item><title>Device Flasher</title><link>https://handshaketec.com/flash_device/</link><pubDate>Mon, 06 Jul 2026 00:00:00 +0000</pubDate><guid>https://handshaketec.com/flash_device/</guid><description>&lt;h1 id="flash-firmware-to-your-device">Flash Firmware to Your Device&lt;/h1>
&lt;p>This page allows you to flash custom firmware directly to your device using the Web Serial API.&lt;/p>
&lt;div style="background-color: #fff3cd; border: 1px solid #ffc107; border-radius: 4px; padding: 15px; margin-bottom: 20px; color: #856404; border-left: 4px solid #ff9800;">
 &lt;strong>⚠️ Disclaimer:&lt;/strong> ** THIS SITE IS INCOMPLETE - FOR USE FOR TESTING BY THE DEVELOPERS ONLY **
HandShake is free, open-source software provided as-is, with no warranty of any kind. It configures unmodified, manufacturer-certified hardware (Lilygo T-Watch S3 / T-Embed) that you purchase separately. Before relying on any device for communication access, test its switch response thoroughly with a professional (Occupational Therapist, Speech and Language Therapist, Technologist, experienced Carer). Report issues to &lt;a href="matt@mattoppenheim.com">matt@mattoppenheim.com&lt;/a>
&lt;/div>


&lt;style>
 body { font-family: system-ui, sans-serif; }
 .flasher-container {
 max-width: 600px;
 margin: 2rem auto;
 padding: 1rem;
 border: 1px solid #ccc;
 border-radius: 8px;
 background-color: #f9f9f9;
 }
 @media (prefers-color-scheme: dark) {
 .flasher-container { background-color: #222; border-color: #555; }
 }
 .flasher-section {
 margin: 1.5rem 0;
 padding: 1rem;
 border-left: 4px solid #2196F3;
 }
 .flasher-section h3 { margin-top: 0; color: #2196F3; }
 .file-input-wrapper { margin: 1rem 0; }
 .file-input-wrapper input[type="file"] { padding: 0.5rem; cursor: pointer; }
 .file-input-wrapper input[type="file"]:disabled { opacity: 0.5; cursor: not-allowed; }
 .status-message {
 padding: 0.75rem;
 margin: 1rem 0;
 border-radius: 4px;
 display: none;
 }
 .status-message.show { display: block; }
 .status-message.success {
 background-color: #e8f5e9;
 color: #1b5e20;
 border-left: 4px solid #4caf50;
 }
 .status-message.error {
 background-color: #ffebee;
 color: #b71c1c;
 border-left: 4px solid #f44336;
 }
 .status-message.info {
 background-color: #e3f2fd;
 color: #0d47a1;
 border-left: 4px solid #2196F3;
 }
 @media (prefers-color-scheme: dark) {
 .status-message.success { background-color: #1b5e20; color: #c8e6c9; }
 .status-message.error { background-color: #b71c1c; color: #ffcdd2; }
 .status-message.info { background-color: #0d47a1; color: #b3e5fc; }
 }
 esp-web-install-button { width: 100%; }
 esp-web-install-button.hidden { visibility: hidden; }
 esp-web-install-button[install-unsupported] { visibility: inherit; }
&lt;/style>

&lt;div class="flasher-container">
 &lt;div class="flasher-section">
 &lt;h3>1. Select Firmware File&lt;/h3>
 &lt;p>Choose the .bin firmware file you downloaded from Configuration.&lt;/p>
 &lt;div class="file-input-wrapper">
 &lt;input type="file" id="firmware-file" accept=".bin" />
 &lt;/div>
 &lt;div id="file-status" class="status-message">&lt;/div>
 &lt;/div>

 &lt;div class="flasher-section">
 &lt;h3>2. Audio File Status&lt;/h3>
 &lt;p>Audio file detection and upload status:&lt;/p>
 &lt;div id="audio-status" class="status-message">&lt;/div>
 &lt;/div>

 &lt;div class="flasher-section">
 &lt;h3>3. Connect &amp; Flash&lt;/h3>
 &lt;p>Connect your device via USB and click the button below to flash firmware and audio.&lt;/p>
 &lt;esp-web-install-button class="hidden">&lt;/esp-web-install-button>
 &lt;div id="flash-status" class="status-message">&lt;/div>
 &lt;/div>

 &lt;div class="flasher-section" style="border-left-color: #666;">
 &lt;h3>Troubleshooting&lt;/h3>
 &lt;ul>
 &lt;li>&lt;strong>Device not detected?&lt;/strong> Make sure your device is connected via USB and powered on.&lt;/li>
 &lt;li>&lt;strong>Flash failed?&lt;/strong> Try disconnecting and reconnecting your device.&lt;/li>
 &lt;li>&lt;strong>Issues?&lt;/strong> Check the browser console (F12) for error messages.&lt;/li>
 &lt;/ul>
 &lt;/div>
&lt;/div>
&lt;!-- ESP Web Tools, vendored at /js/vendor/esp-web-tools-10.4.0-hs1/ (v10.4.0,
 Apache-2.0). install-button.js is only a loader and imports ~24 sibling
 chunks by relative path, so the whole directory must be served intact.
 The -hs1 suffix is a cache-busting revision: Caddy serves *.js as
 immutable for a year, and these filenames come from upstream's build, so
 any local edit needs a NEW directory name to reach browsers. Bump it and
 update this src. Read that directory's README before changing it. -->
&lt;script type="module" src="https://handshaketec.com/js/vendor/esp-web-tools-10.4.0-hs1/install-button.js">&lt;/script>
&lt;script type="module">
import { validateFirmware } from './modules/file-validator.js';
import { uploadFirmware } from './modules/firmware-uploader.js';

let isUploading = false;
let csrfToken = null;

const fileInput = document.getElementById('firmware-file');
const button = document.querySelector('esp-web-install-button');

if (!fileInput) {
 console.error('firmware-file input element not found');
} else {
 // Disable file input until CSRF token is loaded
 fileInput.disabled = true;
}

if (!button) {
 console.error('esp-web-install-button not found');
}

// Fetch CSRF token first, before enabling interactions
fetch('/csrf_token', { signal: AbortSignal.timeout(10000) })
 .then(r => {
 if (!r.ok) throw new Error(`HTTP ${r.status}`);
 return r.json();
 })
 .then(data => {
 csrfToken = data.csrf_token;
 console.log('CSRF token loaded');
 // Enable file input and attach event listeners only after token is available
 attachEventListeners();
 })
 .catch(err => {
 console.error('Failed to load CSRF token:', err);
 // This page uses fetch() rather than HTMX, so the backend_status shortcode
 // does not cover it. Distinguish a backend outage from a genuine token
 // problem: Caddy answers 502/503/504 when granian is unreachable, and a
 // TypeError means the request never completed at all. Reporting either as a
 // "security error" sends the user looking in entirely the wrong place.
 const statusEl = document.getElementById('file-status');
 const backendDown = /HTTP 50[234]/.test(err.message) || err.name === 'TypeError';
 statusEl.textContent = backendDown
 ? '⚠️ The firmware service is unavailable, so flashing is disabled. This usually clears within a minute — please reload the page.'
 : '❌ Security error: Could not load CSRF token. Please refresh the page.';
 statusEl.className = 'status-message show error';
 });

// Attach event listeners only after CSRF token is loaded
function attachEventListeners() {
 if (fileInput) {
 fileInput.disabled = false;
 fileInput.addEventListener('change', handleFileChange);
 }

 if (button) {
 button.addEventListener('error', (e) => {
 console.error('Flash error:', e.detail);
 document.getElementById('flash-status').textContent = '❌ Flash failed: ' + e.detail;
 document.getElementById('flash-status').className = 'status-message show error';
 });

 button.addEventListener('done', (e) => {
 console.log('Flash complete');
 document.getElementById('flash-status').textContent = '✅ Firmware flashed successfully! Your device is restarting.';
 document.getElementById('flash-status').className = 'status-message show success';
 });
 }
}

async function handleFileChange(e) {
 const file = e.target.files[0];
 if (!file) return;

 if (isUploading) {
 document.getElementById('file-status').textContent = '❌ Upload in progress, please wait';
 document.getElementById('file-status').className = 'status-message show error';
 return;
 }

 console.log('File selected:', file.name, file.size);

 const validation = validateFirmware(file);
 if (!validation.valid) {
 document.getElementById('file-status').textContent = '❌ ' + validation.error;
 document.getElementById('file-status').className = 'status-message show error';
 return;
 }

 console.log('File validation passed');

 document.getElementById('file-status').textContent = '✅ Valid firmware file - uploading...';
 document.getElementById('file-status').className = 'status-message show success';

 isUploading = true;
 fileInput.disabled = true;

 try {
 const uploadData = await uploadFirmware(file, csrfToken);

 console.log('Manifest URL:', uploadData.manifest_url);

 // Display audio file status for both devices
 const audioInfo = uploadData.audio_flash_info;
 const audioStatusDiv = document.getElementById('audio-status');
 if (audioInfo) {
 let audioMsg = '';
 let audioClass = 'status-message show';

 // Show per-device status
 if (audioInfo.device_status) {
 audioMsg += '&lt;strong>Audio Files by Device:&lt;/strong>&lt;br>';
 for (const [device, status] of Object.entries(audioInfo.device_status)) {
 audioMsg += `• &lt;strong>${device}:&lt;/strong> ${status}&lt;br>`;
 }
 }

 // Show overall status if audio file will be flashed
 if (audioInfo.found &amp;&amp; audioInfo.status === 'success') {
 audioMsg += `&lt;br>✅ Audio file will be flashed: &lt;strong>${audioInfo.filename}&lt;/strong> (${(audioInfo.size / 1024).toFixed(1)} KB)`;
 audioClass += ' success';
 if (audioInfo.note) {
 audioMsg += `&lt;br>ℹ️ ${audioInfo.note}`;
 }
 } else if (audioInfo.status === 'failed') {
 audioMsg += `&lt;br>❌ Audio file found but upload failed: ${audioInfo.filename}`;
 audioClass += ' error';
 } else if (audioInfo.status &amp;&amp; audioInfo.status.includes('error')) {
 audioMsg += `&lt;br>⚠️ ${audioInfo.status}`;
 audioClass += ' error';
 } else if (audioInfo.status) {
 audioMsg += `&lt;br>ℹ️ ${audioInfo.status}`;
 if (!audioInfo.device_status) {
 audioClass += ' info';
 }
 }

 if (audioMsg) {
 audioStatusDiv.innerHTML = audioMsg;
 if (!audioInfo.device_status) {
 audioStatusDiv.className = audioClass;
 } else {
 audioStatusDiv.className = 'status-message show info';
 }
 }
 }

 if (button) {
 button.manifest = uploadData.manifest_url;
 button.classList.remove('hidden');
 document.getElementById('file-status').textContent = '✅ Valid firmware file - ready to flash';
 console.log('Button shown with manifest URL:', uploadData.manifest_url);
 } else {
 throw new Error('esp-web-install-button not found');
 }
 } catch (err) {
 console.error('Error:', err);
 document.getElementById('file-status').textContent = '❌ An error occurred during upload. Please try again.';
 document.getElementById('file-status').className = 'status-message show error';
 } finally {
 isUploading = false;
 fileInput.disabled = false;
 fileInput.value = '';
 }
}
&lt;/script>

</description></item><item><title>Contact</title><link>https://handshaketec.com/contact/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://handshaketec.com/contact/</guid><description>&lt;p>Email me at &lt;a href="mailto:matt@mattoppenheim.com">matt@mattoppenheim.com&lt;/a>&lt;/p></description></item><item><title>Getting started with the Lilygo T-Watch S3</title><link>https://handshaketec.com/t_watch_s3_first_time_use/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://handshaketec.com/t_watch_s3_first_time_use/</guid><description>&lt;h1 id="using-the-lilygo-t-watch-s3-for-the-first-time">Using the Lilygo T-Watch S3 for the first time&lt;/h1>
&lt;h2 id="turning-on-the-battery">Turning on the battery&lt;/h2>
&lt;p>Before you can turn on a new T-Watch S3 for the first time, you need to take the back off the watch, pull out the battery and &lt;strong>turn on the power switch&lt;/strong> to connect the battery to the watch.
This caught me out!
I thought I had a bad watch and all I had to do was turn it on inside!
Without the battery turned on, the watch still works when connected to a USB port using the mini-USB socket (see below). This is as the watch will draw power through the USB connection even with the battery disconnected.&lt;/p>
&lt;p>The on/off slide switch is under the battery at the base of the circuit board.
As with everything, it is straight forwards when you know how, but the switch is small and hidden, so I&amp;rsquo;ve put a few photos together to help.&lt;/p>
&lt;p>The photo below shows the corner of the back plate that has a little recess allowing you to remove it easily.&lt;/p>
&lt;figure class="image center">
&lt;img src="https://handshaketec.com/images/t_watch_s3_removing_back.png" loading="lazy">
&lt;figcaption>
&lt;strong>The corner recess used to remove the back cover&lt;/strong>&lt;br>&lt;/figcaption>
&lt;/figure>

&lt;p>Inside the T-Watch S3 you can see the battery:&lt;/p>
&lt;figure class="image center">
&lt;img src="https://handshaketec.com/images/t_watch_s3_back_removed.png" loading="lazy">
&lt;figcaption>
&lt;strong>Back plate removed from the T-Watch S3, showing the battery&lt;/strong>&lt;br>&lt;/figcaption>
&lt;/figure>

&lt;p>The battery needs to be pulled out.
It stays attached to the circuit board by two wires.
The battery is not removable, which is a bonus as it can&amp;rsquo;t be dropped then swallowed by your dog or a toddler or an unsupervised academic.&lt;/p>
&lt;p>The orange square in the photo below shows where the battery on/off switch is.
The watch ships with this in the off position.
You need to move the switch to the on position then replace the battery and the back plate.&lt;/p>
&lt;figure class="image center">
&lt;img src="https://handshaketec.com/images/t_watch_s3_battery_switch_off.png" loading="lazy">
&lt;figcaption>
&lt;strong>Battery switch in the off position&lt;/strong>&lt;br>&lt;/figcaption>
&lt;/figure>

&lt;figure class="image center">
&lt;img src="https://handshaketec.com/images/t_watch_s3_battery_switch_on.png" loading="lazy">
&lt;figcaption>
&lt;strong>Battery switch in the on position&lt;/strong>&lt;br>&lt;/figcaption>
&lt;/figure>

&lt;h2 id="usb-socket-and-power-switch-postion">USB Socket and power switch postion&lt;/h2>
&lt;p>The image below shows the side of the watch.
This photo shows the silver mini-USB socket cover and the on/off button.&lt;/p>
&lt;figure class="image center">
&lt;img src="https://handshaketec.com/images/t_watch_usb_cover_on.png" loading="lazy">
&lt;figcaption>
&lt;strong>Side of T-Watch S3, showing the USB socket cover and on/off button&lt;/strong>&lt;br>&lt;/figcaption>
&lt;/figure>

&lt;p>The silver cover on the side of the watch is on top of a mini-USB socket.&lt;/p>
&lt;p>The T-Watch S3 uses a mini-USB socket, not the latest USB-C sockets.
This socket has two uses:&lt;/p>
&lt;ul>
&lt;li>Charging using a powered USB socket or a 5V USB charger.&lt;/li>
&lt;li>Loading new code onto the watch.&lt;/li>
&lt;/ul>
&lt;p>The cover over the mini USB socket is a little tricky to get off the first time. There is a little recess on the bottom of the cover.
Pull it open with a fingernail.
The cover will not drop off the watch when it is opened as it is attached to the watch by a hinge.&lt;/p>
&lt;h2 id="turning-on-the-watch">Turning on the watch&lt;/h2>
&lt;p>To turn the watch on or off: press the button for about 3 seconds.
If you hold down the button too long, the watch turns off again as soon as it turns on.
Which is a little confusing!&lt;/p>
&lt;p>This button on the watch rotates but the rotation has no functionality.&lt;/p>
&lt;h2 id="connecting-the-usb-cable-to-charge-and-program-the-t-watch-s3">Connecting the USB cable to charge and program the T-Watch S3&lt;/h2>
&lt;p>The watch ships with a short mini-USB cable which works for both charging and programming the watch.
Use the cable to connect your T-Watch S3 to your laptop or PC.
I&amp;rsquo;ve not managed to program the watch from an Android phone, I&amp;rsquo;ve always had to use a laptop or PC.
I&amp;rsquo;ve tested programming the T-Watch S3 with Windows and Linux and had reports that it works well with the Mac, but I don&amp;rsquo;t have a Mac to verify this.&lt;/p>
&lt;figure class="image center">
&lt;img src="https://handshaketec.com/images/t_watch_s3_cable_connected.png" loading="lazy">
&lt;figcaption>
&lt;strong>USB cable connected to the watch&lt;/strong>&lt;br>&lt;/figcaption>
&lt;/figure>
</description></item><item><title>Privacy notice</title><link>https://handshaketec.com/privacy/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://handshaketec.com/privacy/</guid><description>&lt;p>This page explains what information handshaketec.com keeps about you, why it
keeps it and what you can do about it.&lt;/p>
&lt;h2 id="who-is-responsible">Who is responsible&lt;/h2>
&lt;p>This site is run by Matt Oppenheim, who is solely responsible for it and for
the data described here — not Lancaster University. For anything about
privacy, contact &lt;a href="mailto:matt@mattoppenheim.com">matt@mattoppenheim.com&lt;/a>,
the same address given on the &lt;a href="https://handshaketec.com/contact/">contact page&lt;/a>.&lt;/p>
&lt;h2 id="what-this-site-does-not-do">What this site does not do&lt;/h2>
&lt;p>There is no advertising, no analytics and no tracking on this site. We do not
sell or share your information, and we do not build profiles of visitors.
Apart from YouTube videos (see below), every page loads only from
handshaketec.com and comments.handshaketec.com.&lt;/p>
&lt;h2 id="comments">Comments&lt;/h2>
&lt;p>The comment box on every page is run by &lt;a href="https://remark42.com">Remark42&lt;/a>, an
open-source comment system that runs on our own server.&lt;/p>
&lt;p>When you comment, we keep:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>The name you use.&lt;/strong> If you comment anonymously, this is the name you type in. If you sign in with GitHub, it is your GitHub display name. We also keep a copy of your GitHub profile picture and an identifier for your GitHub account.&lt;/li>
&lt;li>&lt;strong>Your comment and when you posted it.&lt;/strong>&lt;/li>
&lt;li>&lt;strong>A scrambled form of your internet (IP) address.&lt;/strong> We never store the address itself, only a one-way code made from it, which cannot be turned back into your address. It lets us block someone who is abusing the comments without keeping their address.&lt;/li>
&lt;/ul>
&lt;p>We do not ask for your email address.&lt;/p>
&lt;p>&lt;strong>Comments are public.&lt;/strong> Anyone who visits the page can read them. Please don&amp;rsquo;t post anything that identifies a HandShake user or someone who took part in our research, unless it is you and you are happy for everyone to read it.&lt;/p>
&lt;p>&lt;strong>Signing in with GitHub.&lt;/strong> If you choose to sign in with GitHub, GitHub knows
that you signed in to this site. What GitHub does with that is covered by
&lt;a href="https://docs.github.com/en/site-policy/privacy-policies/github-general-privacy-statement">GitHub&amp;rsquo;s privacy statement&lt;/a>.&lt;/p>
&lt;p>&lt;strong>Email notifications.&lt;/strong> When someone posts a comment, the site emails a copy
of it, with the commenter&amp;rsquo;s name, to the site owner so it can be read and
moderated. These emails are sent and stored through Google&amp;rsquo;s Gmail service.&lt;/p>
&lt;p>&lt;strong>Cookies.&lt;/strong> Reading comments sets no cookies. When you sign in to comment, comments.handshaketec.com sets two: one keeps you signed in and the other protects your account from forged requests. They last about eight days, or until you sign out, which removes them straight away.&lt;/p>
&lt;p>&lt;strong>How long we keep it.&lt;/strong> Comments stay until they are deleted. The comment system makes a backup copy once a day and keeps the last 10, so a deleted comment is gone from the backups after about ten days.&lt;/p>
&lt;p>&lt;strong>Deleting your comments.&lt;/strong> You can delete a comment for a few minutes after posting it. After that, &lt;a href="https://handshaketec.com/contact/">contact us&lt;/a> and we will remove your comments and the information above.&lt;/p>
&lt;h2 id="the-configuration-and-flashing-pages">The configuration and flashing pages&lt;/h2>
&lt;p>The T-Watch S3 and T-Embed configuration pages build firmware to your
settings. To keep your settings separate from other visitors&amp;rsquo;, the site sets
one cookie, called &lt;code>session&lt;/code>, when you open one of these pages. It holds a
random name for your session and a security code that stops other sites
changing your settings, and nothing else. It is deleted when you close
your browser.&lt;/p>
&lt;p>While your session is active, the server keeps:&lt;/p>
&lt;ul>
&lt;li>the settings you choose, such as gestures, sensitivity and keys&lt;/li>
&lt;li>the MAC addresses you enter, which identify your watch and receiver to each other&lt;/li>
&lt;li>the firmware built from those settings&lt;/li>
&lt;/ul>
&lt;p>A session ends after 2 hours without use. Its settings and firmware are then
deleted from the server within a few minutes.&lt;/p>
&lt;p>If you upload a firmware file on the &lt;a href="https://handshaketec.com/flash_device/">Device Flasher&lt;/a> page,
it is deleted from the server after one hour. Flashing itself happens between
your browser and your device: nothing read from the device is sent to us.&lt;/p>
&lt;h2 id="server-logs">Server logs&lt;/h2>
&lt;p>Like most websites, the server records each request it receives: your IP
address, the date and time, the page or file asked for and the type of browser
you use. We use these records only to keep the site working and secure, for
example to find faults or block attacks. There is no fixed period for keeping
them: older records are overwritten as the logs fill up, and the logs are
cleared whenever the site is updated. How long that takes depends on how busy
the site is and how often it changes, so we can only estimate it, at a few
weeks at most.&lt;/p>
&lt;h2 id="videos">Videos&lt;/h2>
&lt;p>Pages with a video use YouTube&amp;rsquo;s privacy-enhanced player, which does not set
cookies until you press play. Playing a video is then subject to
&lt;a href="https://policies.google.com/privacy">Google&amp;rsquo;s privacy policy&lt;/a>, not ours.&lt;/p>
&lt;h2 id="why-we-are-allowed-to-use-this-information">Why we are allowed to use this information&lt;/h2>
&lt;p>UK data protection law requires a lawful basis for using personal data. Ours
is &lt;strong>legitimate interests&lt;/strong>: running a public discussion about HandShake,
building the firmware you ask for and keeping the site secure. We keep only
what those need and for no longer than the times given above.&lt;/p>
&lt;p>The &lt;code>session&lt;/code> cookie and the comment sign-in cookies are strictly necessary for
the features you choose to use, so the site does not ask for consent before
setting them.&lt;/p>
&lt;h2 id="where-the-data-is-kept">Where the data is kept&lt;/h2>
&lt;p>The server is hosted in the United Kingdom by IONOS SE. Comment notification
emails are held by Google, which may store them outside the UK under its own
safeguards for international transfers.&lt;/p>
&lt;h2 id="your-rights">Your rights&lt;/h2>
&lt;p>Under UK data protection law you have the right to:&lt;/p>
&lt;ul>
&lt;li>ask what personal data is held about you and get a copy of it&lt;/li>
&lt;li>have inaccurate data corrected&lt;/li>
&lt;li>ask for your data to be deleted&lt;/li>
&lt;li>object to how your data is used, or ask for its use to be restricted&lt;/li>
&lt;/ul>
&lt;p>To use any of these rights, &lt;a href="https://handshaketec.com/contact/">contact us&lt;/a>. We will reply within one
month. If you&amp;rsquo;re not satisfied with the response, you can complain to the UK&amp;rsquo;s
data protection regulator, the
&lt;a href="https://ico.org.uk/make-a-complaint/">Information Commissioner&amp;rsquo;s Office&lt;/a>.&lt;/p>
&lt;h2 id="changes-to-this-notice">Changes to this notice&lt;/h2>
&lt;p>If what the site collects changes, this page will be updated first. The date
under the title shows when the page was last rebuilt.&lt;/p></description></item><item><title>Watch Configuration Help</title><link>https://handshaketec.com/watch_configuration_help/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://handshaketec.com/watch_configuration_help/</guid><description>&lt;p>The &lt;a href="https://handshaketec.com/watch_configuration/">&lt;strong>Watch Configuration&lt;/strong> page&lt;/a> lets you choose what your T-Watch S3 does, then builds a
custom firmware file (the program that runs on the watch) for you to download and
put onto the device.
The options that you can set include:&lt;/p>
&lt;ul>
&lt;li>What gesture is recognised by the watch.&lt;/li>
&lt;li>How sensitive the watch is to movement.&lt;/li>
&lt;li>Which receiver module the watch communicates with.&lt;/li>
&lt;/ul>
&lt;p>You don&amp;rsquo;t need any programming knowledge — just work through
the page from top to bottom.&lt;/p>
&lt;p>How it works:
Each time you select an option or enter a value, this is written to a configuration file.
When you create the firmware at the bottom of the page, this configuration file is read and used to create the firmware with.
So, you can go back and change your selections as many times as you want.
The configuration file is only read when you click on the &amp;lsquo;Generate firmware&amp;rsquo; button at the bottom of the page.&lt;/p>
&lt;p>If the option you&amp;rsquo;re setting has a &amp;lsquo;submit value&amp;rsquo; button, then the value you select is not written to the configuration file until you click the submit button.&lt;/p>
&lt;h1 id="using-the-t-watch-s3-configuration-website">Using the T-Watch-S3 configuration website&lt;/h1>
&lt;p>Using this website you will configure and download a file for your watch which will be called e.g.&lt;/p>
&lt;p>zeuss-twatch-s3-firmware.bin&lt;/p>
&lt;p>You are free to rename the file after you download it.&lt;/p>
&lt;p>The Greek god&amp;rsquo;s name at the start of the filename will probably be different from the example and will change each time that you restart the configuration page. See below for details of why this is done.&lt;/p>
&lt;h2 id="step-1--check-your-session-name">Step 1 — Check your session name&lt;/h2>
&lt;p>At the top of the page you&amp;rsquo;ll see your &lt;strong>session name&lt;/strong>.
This is a name of a Greek god.&lt;/p>
&lt;p>Each time you re-open the configuration page, you will be assigned a new session name.
This is done so that the firmware you build doesn&amp;rsquo;t get mixed up with anyone else&amp;rsquo;s.
The names are taken randomly from a pool of Greek god names.
When you next use the site you will probably get a different session name.
This is done so that:&lt;/p>
&lt;ul>
&lt;li>If several people are using the site at the same time, everybody gets a different session name.&lt;/li>
&lt;/ul>
&lt;p>The firmware filename produced by the website contains the session name.&lt;/p>
&lt;p>e.g. zeuss-twatch-s3-firmware.bin&lt;/p>
&lt;p>This way, you get the software for your device and not for anybody elses device, even if several people are using the site at the same time.
You&amp;rsquo;ll get the same names popping up if you use the site enough as there&amp;rsquo;re only so many Greek gods!
The main reason for doing this is to enable several different users to access the site at the same time without interfering with each other.
I could&amp;rsquo;ve used something other than Greek god names but it seemed as good a method as any.&lt;/p>
&lt;p>I did add the functionality for you to assign your own session name but realised that this could be named with the name of whoever uses the system, which is a leak of personal information, so removed it.&lt;/p>
&lt;p>You&amp;rsquo;re free to rename the firmware that you create after you&amp;rsquo;ve downloaded it to your own device.
Renaming the file won&amp;rsquo;t change how it works, so long as you keep &amp;lsquo;.bin&amp;rsquo; at the end of the filename.
The &amp;lsquo;.bin&amp;rsquo; ending is required so that the software that then takes your firmware and loads it onto the watch recognises the file as a firmware file.&lt;/p>
&lt;p>If the session name is &amp;lsquo;undefined&amp;rsquo; then there is a problem with the website!
Try again in 10 minutes as this could mean that I&amp;rsquo;m uploading and testing a new version of the website and have cocked something up.
I do test the website offline before loading up a new version but sometime things work offline but not on the Real World deployment site you&amp;rsquo;re using.
Otherwise, let me know and I&amp;rsquo;ll get it fixed.&lt;/p>
&lt;h2 id="step-2--choose-a-gesture">Step 2 — Choose a gesture&lt;/h2>
&lt;p>Under &lt;strong>Gesture Selection&lt;/strong>, pick the movement the watch should respond to — a &lt;strong>punch&lt;/strong>
or a &lt;strong>roll&lt;/strong> of the wrist. If you&amp;rsquo;re just starting out trying HandShake, choose the punch gesture. This one is the most generic gesture detection algorithm.
Choose one, then click &lt;strong>submit_gesture&lt;/strong>.
The two gesture algorithms are described below.
I intend to make some videos showing the two gestures and to put some links here.&lt;/p>
&lt;h3 id="punch-gesture">Punch gesture&lt;/h3>
&lt;p>This is a simple algorithm that looks for a shake in any direction that exceeds the sensitivity value.
The sensitivity for triggering the algorithm is set below.&lt;/p>
&lt;p>A short video showing the type of motion that the punch gesture algorithm is designed to detect can be seen at:&lt;/p>
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
 &lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube-nocookie.com/embed/6gIWDpBXn14?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video">&lt;/iframe>
 &lt;/div>

&lt;h3 id="roll-gesture">Roll gesture&lt;/h3>
&lt;p>This gesture was written for a participant who had a slow wrist rolling gesture.
The roll algorithm differentiates the acceleration data from a single accelerometer axis, triggering gesture detection when the differentiated value exceeds an adjustable threshold.&lt;/p>
&lt;p>A short video showing the type of motion that the roll gesture algorithm is designed to detect can be seen at:&lt;/p>
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
 &lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube-nocookie.com/embed/OOAX4EiMQqg?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video">&lt;/iframe>
 &lt;/div>

&lt;h2 id="step-3--set-the-gesture-sensitivity">Step 3 — Set the gesture sensitivity&lt;/h2>
&lt;p>The &lt;strong>Gesture Sensitivity&lt;/strong> slider controls how strong a movement has to be before the
watch registers it.
The value entered here is just a starting point as we can adjust the sensitivity later using the receiver:&lt;/p>
&lt;p>The sensitivity can be adjusted using the jog wheel on the receiver unit and then sent to the watch using a long press on the button in the middle of this wheel.&lt;/p>
&lt;p>The lower the value that is selected, the lower the amount of shake that is required to create a trigger. This means that the watch is more sensitive to faint shakes.
This is as the number represents the value of acceleration that has to be exceeded for a trigger to be sent.
If you don&amp;rsquo;t know what value to try, just start with the default and click &lt;strong>submit_sensitivity&lt;/strong>.&lt;/p>
&lt;h2 id="step-4--set-the-minimum-shake-interval-optional">Step 4 — Set the minimum shake interval (optional)&lt;/h2>
&lt;p>The &lt;strong>Minimum Intershake Interval&lt;/strong> slider controls the minimum time (in milliseconds)
that must pass between separate shake detections. This prevents the watch from registering
the same shake as multiple separate events. Start with the default of 300 ms and click
&lt;strong>submit_shake_interval&lt;/strong>. If you find the watch is registering unintended multiple
detections, increase this value. If you find it&amp;rsquo;s too slow to respond to rapid successive
movements, decrease it.&lt;/p>
&lt;h2 id="step-5--enter-the-t-embeds-mac-address">Step 5 — Enter the T-Embed&amp;rsquo;s MAC address&lt;/h2>
&lt;p>Under &lt;strong>Enter MAC Address for the T-Embed&lt;/strong>, type in the MAC address of the T-Embed
receiver that this watch should send its signals to. This is what pairs the two devices
together, so that your watch talks to your own receiver and not anybody else&amp;rsquo;s.&lt;/p>
&lt;p>You can type the address either with colons (e.g. &lt;code>AA:BB:CC:DD:EE:FF&lt;/code>) or without them
(e.g. &lt;code>AABBCCDDEEFF&lt;/code>) — either format is accepted. Then click &lt;strong>Submit MAC&lt;/strong>.&lt;/p>
&lt;p>If you don&amp;rsquo;t already know your receiver&amp;rsquo;s MAC address, see the
&lt;a href="https://handshaketec.com/mac_identification/">Finding the MAC address of your module&lt;/a>
page for step-by-step instructions on reading it off the device. Make sure you read the
address &lt;strong>from the T-Embed&lt;/strong>, not the watch — the watch is given the receiver&amp;rsquo;s address,
and the receiver is given the watch&amp;rsquo;s address, so it&amp;rsquo;s easy to get them the wrong way
round.&lt;/p>
&lt;p>If the address is wrong, the watch tells you: it shows &lt;strong>*** NO LINK ***&lt;/strong> after each
gesture. See &lt;a href="https://handshaketec.com/watch_configuration_help/#if-the-watch-shows-no-link">If the watch shows NO LINK&lt;/a> below.&lt;/p>
&lt;h2 id="step-6--generate-the-firmware">Step 6 — Generate the firmware&lt;/h2>
&lt;p>Each time you change one of the controls above, make sure you&amp;rsquo;ve clicked its &lt;strong>submit&lt;/strong>
button — this saves your choice in the background.&lt;/p>
&lt;p>When you&amp;rsquo;re happy with all your selections, click &lt;strong>Generate Firmware&lt;/strong>. The site reads
your choices and builds the firmware file. This usually takes a minute or two, and you&amp;rsquo;ll
see progress messages while it works, so please be patient.&lt;/p>
&lt;p>The build carries on if you leave the page. Come back to this page and you&amp;rsquo;ll see its
progress, or its result if it has finished.&lt;/p>
&lt;h2 id="step-7--download-the-firmware">Step 7 — Download the firmware&lt;/h2>
&lt;p>While the build runs, the download button shows &lt;em>Building&amp;hellip;&lt;/em>. Once the build finishes, a
&lt;strong>download&lt;/strong> option appears. Click it to save the firmware file to your computer.&lt;/p>
&lt;p>Once it&amp;rsquo;s downloaded, the firmware is yours forever! You own it. No time limits, no
subscriptions. All yours.&lt;/p>
&lt;h2 id="putting-the-firmware-onto-your-watch">Putting the firmware onto your watch&lt;/h2>
&lt;p>To put this firmware onto your T-Watch S3, see the
&lt;a href="https://handshaketec.com/flash_device_help/">Device Flasher help page&lt;/a> for step-by-step
instructions.&lt;/p>
&lt;p>I turned off the touch interface on the watch to prevent accidental touches changing things.&lt;/p>
&lt;h2 id="what-the-watch-does-when-it-detects-a-gesture">What the watch does when it detects a gesture&lt;/h2>
&lt;p>When the watch detects your gesture, it sends a signal to the T-Embed straight away,
then gives a short buzz and shows &lt;strong>*** SHAKE ***&lt;/strong> on its screen. The T-Embed
then sends its keypress to your communication device. From the watch detecting the
gesture to the keypress arriving takes about 30 milliseconds — too quick to notice.&lt;/p>
&lt;p>The watch also checks that the T-Embed received each signal. If it did, nothing more
happens. If it did not, the watch warns you, as described below.&lt;/p>
&lt;h2 id="if-the-watch-shows-no-link">If the watch shows NO LINK&lt;/h2>
&lt;p>If the T-Embed did not receive the signal, the buzz is cut short by &lt;strong>three quick
clicks&lt;/strong>, and the watch shows &lt;strong>*** NO LINK ***&lt;/strong> for two seconds. No keypress
was sent for that gesture.&lt;/p>
&lt;p>Check these, in this order:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Is the T-Embed switched on and plugged in?&lt;/strong> The watch can only reach a T-Embed that
is powered.&lt;/li>
&lt;li>&lt;strong>Is the watch close enough?&lt;/strong> The watch uses a low-power radio to save its battery.
In testing it has worked at over 10 metres. If you see NO LINK at a distance, try
moving the watch closer to the T-Embed.&lt;/li>
&lt;li>&lt;strong>Is the T-Embed&amp;rsquo;s MAC address right?&lt;/strong> If the watch shows NO LINK every time, even
with the T-Embed on and close by, the address entered in
&lt;a href="https://handshaketec.com/watch_configuration_help/#step-5--enter-the-t-embeds-mac-address">Step 5&lt;/a> is probably wrong. Check it against
the T-Embed, then generate and flash the firmware again.&lt;/li>
&lt;/ul>
&lt;p>Watches with firmware built before 6 October 2026 do not have this check, so they give
no warning. Generate and flash new firmware to get it.&lt;/p>
&lt;h2 id="watch-keeps-restarting-every-few-seconds">Watch keeps restarting every few seconds&lt;/h2>
&lt;p>If the watch lights up, shows the display for a few seconds, restarts, and then keeps
repeating that same cycle, it is almost always the firmware crashing rather than a flat
battery. A battery that is genuinely too low tends to cut out at irregular moments, or
not turn the watch on at all, rather than restarting in a steady rhythm.&lt;/p>
&lt;p>Charge the watch for 20 minutes or so to rule power out. If it still restarts on the
same few-second cycle once it has charge in it, reflash the firmware using the steps
above.&lt;/p>
&lt;p>If a fresh flash of the current firmware does not fix it, please get in touch and let me
know. A repeating cycle like that is a fault in the firmware rather than anything you
have done wrong, and it helps me to know you have hit it.&lt;/p>
&lt;h2 id="resetting-the-watch-if-it-becomes-unresponsive">Resetting the watch if it becomes unresponsive&lt;/h2>
&lt;p>It is possible to write code that stops the watch from programming!
In this case we need to turn the watch off, then turn it on while pressing a small reset button inside of the watch.
I had to do this a few times when developing the firmware for the watch.
Press the little reset button inside of the case, press the power button on the side of the watch for &lt;strong>one second only&lt;/strong> release the power button, release the little button. You will feel the reset button &amp;lsquo;click&amp;rsquo; in when you press it.
Once the watch has restarted with the button held down, it will program again.&lt;/p>
&lt;p>To access this button, you need to take the back off the watch and remove the battery in the same way that you did when first using the watch to turn on the switch that connects the battery to the watch.
Note the position of the reset button shown by the orange box. I use the end of a pencil to push this down. It&amp;rsquo;s small and fiddly!&lt;/p>
&lt;figure class="image center">
&lt;img src="https://handshaketec.com/images/t_watch_s3_reset_button.png" loading="lazy">
&lt;figcaption>
&lt;strong>The reset button is inside the orange box&lt;/strong>&lt;br>&lt;/figcaption>
&lt;/figure>
</description></item></channel></rss>