Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
66 commits
Select commit Hold shift + click to select a range
e331da4
split wwvbLogicSignal into two
emmby Dec 3, 2025
1cc2115
split wwvbLogicSignal into getWwvbBit and getWwvbSignalLevel
emmby Dec 3, 2025
eef91d0
fix indentation
emmby Dec 3, 2025
9312123
initial commit to dd DCF, JJY, MSF signals
emmby Dec 3, 2025
5a859a7
simplify signal switching logic
emmby Dec 3, 2025
aa8228e
feat: Introduce `txtempus` comparison tests and refactor time signal …
emmby Dec 3, 2025
c8ec4bf
merge
emmby Dec 3, 2025
4783085
fix MSF
emmby Dec 3, 2025
c9aae51
build all four signals in github actions
emmby Dec 3, 2025
b7c0890
WIP: Pre-encode MSF and WWVB signal frames for the entire minute to s…
emmby Dec 3, 2025
6207b4f
refactor: rename `SignalBit_T` enum to `TimeCodeSymbol` and `getBit` …
emmby Dec 3, 2025
c694f1b
feat: Refactor signal generation to pre-encode minute data and retrie…
emmby Dec 3, 2025
89e38be
refactor: move WWVB signal encoding logic into `encodeMinute` and cha…
emmby Dec 4, 2025
f610674
refactor: Store time code frames as `TimeCodeSymbol` arrays instead o…
emmby Dec 4, 2025
e3fdde3
cleanup
emmby Dec 4, 2025
1d71c88
refactor: move radio time signal headers to include directory and upd…
emmby Dec 4, 2025
81104fb
tidy
emmby Dec 4, 2025
b25e19e
potential fix for broken test on github workflow
emmby Dec 4, 2025
9874f76
possible fix for JJY test failure
emmby Dec 4, 2025
22caab6
updates to reflect additional signals
emmby Dec 4, 2025
a51ae8c
nit
emmby Dec 4, 2025
3e246b7
feat: Add persistent network time synchronization toggle with UI cont…
emmby Dec 4, 2025
ab434ec
feat: Implement manual date and time setting via new UI inputs, suppo…
emmby Dec 4, 2025
0ee31aa
refactor: Change `lastSync` variable to use `millis()` for relative t…
emmby Dec 4, 2025
911b17f
nit
emmby Dec 4, 2025
48e544f
fix: Prevent stale network sync reboot before 12 PM local time.
emmby Dec 4, 2025
2fa7dea
feat: Display "Never" for last sync time when `lastSync` is 0, otherw…
emmby Dec 4, 2025
b5b8cd2
feat: enable partial manual time updates by parsing date and time com…
emmby Dec 4, 2025
f5e2862
Merge branch 'feature-disable-ntp' into feature-multisignal
emmby Dec 4, 2025
f7f601d
feat: Introduce `getName()` virtual method to `RadioTimeSignal` and i…
emmby Dec 4, 2025
cce8927
fix weird merge issues
emmby Dec 4, 2025
f84da71
fix unterminated string
emmby Dec 4, 2025
273c610
detach led before changing frequency
emmby Dec 4, 2025
e350666
test: Add signal switching test
emmby Dec 4, 2025
b58ff09
Confirmed that JJY at 60khz successfully set my Junghans Max Bill
emmby Dec 4, 2025
b6fbb6c
fix test
emmby Dec 6, 2025
9f69a8b
Delete build_log.txt
emmby Dec 7, 2025
5ad41cf
refactor: unify time signal encoding to use a single 64-bit frame and…
emmby Dec 7, 2025
6957710
refactor: Rename `reverse8Bits` to `reverse8` and `toBCD` to `to_bcd`.
emmby Dec 7, 2025
a0838a1
Merge branch 'feature-multisignal' of https://github.com/emmby/WatchT…
emmby Dec 7, 2025
6f530f6
Add WWVB BCD decoding to visualization
emmby Mar 8, 2026
a1bc3a5
add BCD decoding for other protocols
emmby Mar 8, 2026
16ec859
display UTC
emmby Mar 8, 2026
d8a2b45
add day of year
emmby Mar 8, 2026
6ab2c2a
track signal deviations
emmby Mar 8, 2026
ea7efc1
refactor: migrate TransitionStats to microsecond precision with 1ms j…
emmby Mar 8, 2026
ab726eb
Add transition timing jitter measurement using RTC tv_usec
emmby Mar 9, 2026
0734f3b
Add ESPUI jitter histogram visualization
emmby Mar 9, 2026
97a4a8f
Add 7-day rolling history to jitter stats
emmby Mar 9, 2026
9fd3fc2
Track per-frame nonzero jitter counts
emmby Mar 9, 2026
1313bf3
Enhance jitter stats: local reset, p99.9/max, error frame log
emmby Mar 9, 2026
b527bc8
feat: increase Async TCP stack size to 16384 by modifying platformio.…
emmby Mar 9, 2026
ee33d6e
Move signal output to esp_timer for jitter-free transitions
emmby Mar 9, 2026
a33456f
remove nonzero frame logging, no longer needed
emmby Mar 9, 2026
df237a5
remove avg
emmby Mar 9, 2026
2850016
Remove all #ifdef UNIT_TEST from WatchTower.ino
emmby Mar 9, 2026
28235db
pull neopixel code into a separate file to make watchtower.ino cleaner
emmby Mar 9, 2026
89b4f7e
refactor webui into a separate file
emmby Mar 9, 2026
52cdf2c
remove transition jitter stat logging
emmby Mar 9, 2026
98674a9
show each bit as it's sent
emmby Mar 9, 2026
db8ef3b
Organize WatchTower.ino with section headers
emmby Mar 9, 2026
500f9a7
minor
emmby Mar 9, 2026
45aa13e
cleanup
emmby Mar 9, 2026
57e5dfb
fix colors
emmby Mar 9, 2026
382c3fa
timezone->TIMEZONE
emmby Mar 10, 2026
61d48ec
nit
emmby Mar 11, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,18 @@ on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
signal: [WWVB, DCF77, MSF, JJY]
include:
- signal: WWVB
flags: ""
- signal: DCF77
flags: "-D SIGNAL_DCF77"
- signal: MSF
flags: "-D SIGNAL_MSF"
- signal: JJY
flags: "-D SIGNAL_JJY"

steps:
- uses: actions/checkout@v3
Expand Down Expand Up @@ -39,7 +51,11 @@ jobs:
run: pio pkg install

- name: Run Build
env:
PLATFORMIO_BUILD_FLAGS: ${{ matrix.flags }}
run: pio run

- name: Run Native Tests
env:
PLATFORMIO_BUILD_FLAGS: ${{ matrix.flags }}
run: pio test -e native
13 changes: 12 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,21 @@
# The Watch Tower WWVB transmitter
# The Watch Tower time signal transmitter

![](docs/ezgif-2a44364473c432.gif)

There are some beautiful radio-controlled watches available these days from Citizen, Seiko, Junghans, and even Casio. These timepieces don’t need fiddling every other month, which is great if you have more than one or two and can never remember what comes after “thirty days hath September…”

In the US, these watches work by receiving a 60-bit 1-Hz signal on a 60-kHz carrier wave broadcast from Fort Collins, Colorado called [WWVB](https://en.wikipedia.org/wiki/WWVB). The broadcast is quite strong and generally covers the entire continental US, but some areas of the country can have unreliable reception. I live in the SF Bay Area in an area with high RF noise and my reception can be spotty. My watches sync often enough that it’s not an issue 363 days out of the year, but sometimes they can miss DST shifts for a day or two. The east coast is known to be even more challenging. And people who live in other countries such as Australia have generally been out of luck.

Similar systems exist in other parts of the world, such as [DCF77](https://en.wikipedia.org/wiki/DCF77) in Germany (covering much of Europe), [MSF](https://en.wikipedia.org/wiki/Time_from_NPL_(MSF)) in the UK, and [JJY](https://en.wikipedia.org/wiki/JJY) in Japan.

Wouldn’t it be great if anyone anywhere in the world could set up a home transmitter to broadcast the time so their watches were always in sync?

WatchTower supports all of these signals! By default it transmits WWVB, but it can be easily configured to transmit DCF77, MSF, or JJY.

> [!NOTE]
> DCF77, MSF, and JJY support is experimental and has not been extensively tested.
> Please file an issue if you run into any problems.

WWVB has been around awhile and there have been various other projects ([1](https://www.instructables.com/WWVB-radio-time-signal-generator-for-ATTINY45-or-A/),[2](https://github.com/anishathalye/micro-wwvb)) that have demonstrated the feasibility of making your own WWVB transmitter. But these all had very limited range. I wanted to build something that could cover my whole watch stand and be based on a more familiar toolset for the typical hobbyist, namely USB-based 32-bit microcontroller development boards, WiFi, and Arduino. My goal was to make something approachable, reliable, and attractive enough it could sit with my watch collection.

## Is this legal?
Expand All @@ -16,6 +24,9 @@ The FCC requires a license to transmit, but has an [exemption](https://www.law.c

## About WWVB

> [!NOTE]
> This section will dive into WWVB because that's the signal the WatchTower uses by default. You can check wikipedia for more details on JJY, DCF77, and MSF.

The classic WWVB transmits one bit of information per second (1Hz) and takes one minute (60 bits) to transmit a full time and date frame.

### An example
Expand Down
Loading
Loading