▲ 26 r/XTEINK

My CrossInk fork, the Almanac, got a massive upgrade with the touch screen on the X4 Pro. Still ironing out a few edge cases but open source soon!

I was surprised how easy it was to get my fork (Claude AI coded but all data is publicly sourced) ported from the x4 to the Pro. Very easy. My Almanac includes a searchable/randomized dictionary, thesaurus, simple english wikipedia, world facts, a spinnable globe, sky chart, a planetarium with spinnable celestial bodies, graphing calculator, and chess and go problems. Love this device!

u/CommunityFan89 — 5 days ago
▲ 187 r/arduino

My open source arduino sketch turned my Watchy smartwatch into a pocket WatchyCelestial: photographic Moon with real libration, star chart, and WiFi time sync. Please do not use my previous project, the WatchyAlmanac. Details in body text.

Built the code with Claude AI assistance; ideas, design, hardware testing by me.

I had built up a reference-tool watch, the WatchyAlmanac, over the past while: dictionary, board game puzzles, the works. Because it was so addicting, I tore a shoulder muscle from too much repetitive motion. Figured a watch I have to keep poking at was part of the problem, so I stripped it back to just the sky; the WatchyCelestial.

Three faces now. The clock shows time, date, moon phase in words, and sunrise/sunset. The Moon face is a photographic globe with actual libration worked out from the date, so it shows the exact face the Moon is turning toward me tonight, terminator and all, dithered to 1-bit. The sky face is an all-sky chart of 904 stars down to mag 4.5 from the Yale Bright Star Catalogue with constellation lines you can toggle.

The astronomy is Meeus and NOAA math, verified on my PC against astropy and the libration against JPL DE421 before any of it went on the watch. It's an ESP32 with no floating point unit, so the globe renders in fixed point and the star chart uses precomputed data in flash. Each face redraws once a minute off the RTC alarm and goes right back to deep sleep.

The one thing that actually annoyed me is that the Watchy has no backup battery for its clock, so a dead LiPo means resetting the time by hand. So I added WiFi NTP sync on a button hold, and it also fires automatically on boot if the RTC comes back flagging that it lost power. Credentials live in a text file on the flash. That path is the only time the radio ever powers up.

Happy to answer anything. Uploaded to my github soon. :)

Edit: It's live! Sparkadium/WatchyCelestial

u/CommunityFan89 — 27 days ago

Trying to figure out if my vape is hackable enough to run snake/pong? I searched the chip but Google turns up nothing for GLAC7BA or 5671 3LE.

It's a 510 vape battery by Tribal, apparently a ripoff of the Yocan Kodo Pro. It's got a small screen with three buttons. I've tried plugging it into a raspberry pi pressing all the button combos when plugging it in, but nothing seems to enumerate. Is this a dead end? I'll post more pics in the comments. Thank you.

u/CommunityFan89 — 28 days ago
▲ 37 r/XTEINK

The Xteink Almanac: Open source fork of CrossInk; full dictionary, simple English wikipedia text, world factbook that links to an interactive globe, moon, and sky chart, as well as calculator and thousands of chess and go puzzles, all offline

Idea, design, and some simple code bugfixing/tweaks were mine, data was gathered from open source resources (no data generated by AI, that's a firm rule for this project), and code was Claude AI's. It'll be posted to my github soon!

I wanted a device that was an optimized, educational ADHD time killer when I'm stuck somewhere I don't want to be.

For the process of how I put it together, it's just a matter of asking the AI what I wanted out of the firmware. It started as a project to put a dictionary on my smartwatch, and spiraled out of control. Any questions, I'm happy to do my best to answer! And if you have any suggestions, please feel free.

Edit: it's live!

Release Globe, Moon & Calculator · Sparkadium/crosspoint-reader-almanac

u/CommunityFan89 — 1 month ago
▲ 76 r/esp32

I put a spinning informational globe on my ESP32 e-reader

Honesty note: I built this with Claude AI. The ideas and testing on real hardware was me, a lot of the code was the AI's, and everything got checked against real-world references before it went on the device.

I've been tinkering with custom firmware (CrossInk, a fork of CrossPoint) for the Xteink X4 and wanted something fun to show off what e-ink can do, so I put together an informational globe app. You get a view of the Earth from space, and instead of moving a cursor around, the whole planet turns underneath a fixed crosshair as you press the buttons. It feels a bit like spinning a desk globe with your thumb.

What it does:

  • Shows day and night on the planet in real time. The dark half of the world is shaded, with a lighter band of twilight between day and night, and it's all based on where the sun actually is right now. If you look up your own town, it matches what's outside your window.
  • Names whatever country is under the crosshair, and one press opens that country's entry from the CIA World Factbook. Population, geography, history, all readable right on the device with adjustable text size.
  • Has a country search. Pick any country from a list and the globe flies you straight to its capital city.
  • Country borders can be switched on or off, and the day/night shading can too, so you can have a clean map look or the full view of the planet.
  • Three zoom levels, and a home button that flies back to wherever you live.

All the map data comes from Natural Earth, which is a free public source used by real cartographers, and the country info comes from the CIA World Factbook. Nothing is hand-drawn or made up, and the little scripts that build the data files can be rerun by anyone.

Happy to share more screenshots or answer questions about running custom stuff on these little e-readers.

Open source repo going on my github soon!

u/CommunityFan89 — 1 month ago
▲ 398 r/chess

I put chess problems on my e-reader

There's over 9000 Lichess problems on here. I designed the sprites myself. The device is an xteink x4.

u/CommunityFan89 — 1 month ago
▲ 1.1k r/arduino

I crammed an arduino sketch with a 69k-word dictionary, the CIA World Factbook, 11,814 go problems, and a real star chart into a Watchy (ESP32, 4MB, e-ink)

This started as "let me put a dictionary on my watch" and got out of hand.

What's on it now:

- full offline dictionary, 69,457 words, uncapped definitions (yes, "cut" has 20 pages of meanings)

- gazetteer built from the CIA World Factbook: 261 countries, capital/population/languages/history blurb, searchable from the same letter grid as the dictionary

- tsumego trainer with 11,814 go problems from Lichess and Cho Chikun sets, progress saved in NVS so it survives deep sleep

- star chart for my location: 904 stars plus constellation lines, moon phase, sunrise/sunset. on a 200x200 e-ink panel one pixel is about 1.2 degrees, and the math checks out way finer than that

- 1-bit photo viewer

clock face that deep sleeps between minutes and wakes on the RTC alarm interrupt like stock Watchy does, so it actually works as a watch

Full disclosure: Claude AI wrote the code for this, I just wrangled the idea of it together.

Everything lives in a 640KB app partition and a 3.25MB SPIFFS on the stock 4MB flash. The dictionary and gazetteer share one file format: sorted entries in compressed 32KB blocks with a small index at the front, so lookup is a binary search on the index and one decompress. Feels instant even with the CPU clocked down.

Things that fought back:

mkspiffs refused to build images past ~2.1MB on a 3MB partition, no error that made sense. Espressif's spiffsgen.py packed the same data fine. If your SPIFFS mysteriously "fills up" way before the partition size, try the python tool.

One dictionary block came out slightly bigger than the decompress buffer on the watch. The block splitter had always rounded up to the next line break, which was harmless with short definitions and quietly blew past the buffer once definitions got long. Never crashed before because it never got the chance. Splitter now rounds down, and the build script refuses to ship a block that doesn't fit.

"'DictEntry' does not name a type" on code that had compiled for months. Turns out the Arduino IDE auto-generates prototypes for your functions and drops them in at the first function definition in the sketch. I'd added a small helper function above my includes, so the generated prototypes landed before my types existed. Rule I follow now: no function definitions above your includes in a .ino.

The watch would deep sleep and instantly boot into the wrong mode. I'm using light sleep in the idle loop with a short timer wake to save power, and that timer stays armed going into deep sleep, so it "slept" for a quarter second and woke confused. Clearing all wake sources before deep sleep fixed it.

The PCF8563 alarm enable bits are inverted, 0 means enabled. Fun.

Power stuff that made a real difference: display.hibernate() before deep sleep (skip this and the e-ink controller drains your battery in days), running at 80MHz with bursts to 240 for decompression and the star math, light sleep between button polls, and shorter idle timeouts in modes where you're not reading. Buttons still feel instant.

One rule I held the whole way: no hand-typed data anywhere. Star positions come from the Yale Bright Star Catalogue, constellation lines from d3-celestial, countries from the factbook.json project, words from Wordset, all pulled in by build scripts I can rerun. And before the astronomy code ever touched the watch, the same C got compiled on a desktop and its output compared against astropy across a pile of random dates worst star error is around a tenth of a pixel, sunrise within seconds. I wanted to be able to hold my wrist up at the night sky and trust it.

Code and build scripts going up on my github (sparkadium) soon. Happy to answer questions about the file format, the partition table, or the tsumego setup.

Edit: It's alive! Sparkadium/WatchyAlmanac: I turned a Watchy into an almanac

u/CommunityFan89 — 1 month ago
▲ 15 r/esp32

I crammed a 69k-word dictionary, the CIA World Factbook, 11,814 go problems, and a real star chart into a Watchy (ESP32, 4MB, e-ink)

This started as "let me put a dictionary on my watch" and got out of hand.

What's on it now:

  • full offline dictionary, 69,457 words, uncapped definitions (yes, "cut" has 20 pages of meanings)
  • gazetteer built from the CIA World Factbook: 261 countries, capital/population/languages/history blurb, searchable from the same letter grid as the dictionary
  • tsumego trainer with 11,814 go problems from Lichess and Cho Chikun sets, progress saved in NVS so it survives deep sleep
  • star chart for my location: 904 stars plus constellation lines, moon phase, sunrise/sunset. on a 200x200 e-ink panel one pixel is about 1.2 degrees, and the math checks out way finer than that
  • 1-bit photo viewer
  • clock face that deep sleeps between minutes and wakes on the RTC alarm interrupt like stock Watchy does, so it actually works as a watch

Full disclosure: Claude AI wrote the code for this, I just wrangled the idea of it together.

Everything lives in a 640KB app partition and a 3.25MB SPIFFS on the stock 4MB flash. The dictionary and gazetteer share one file format: sorted entries in compressed 32KB blocks with a small index at the front, so lookup is a binary search on the index and one decompress. Feels instant even with the CPU clocked down.

Things that fought back:

mkspiffs refused to build images past ~2.1MB on a 3MB partition, no error that made sense. Espressif's spiffsgen.py packed the same data fine. If your SPIFFS mysteriously "fills up" way before the partition size, try the python tool.

One dictionary block came out slightly bigger than the decompress buffer on the watch. The block splitter had always rounded up to the next line break, which was harmless with short definitions and quietly blew past the buffer once definitions got long. Never crashed before because it never got the chance. Splitter now rounds down, and the build script refuses to ship a block that doesn't fit.

"'DictEntry' does not name a type" on code that had compiled for months. Turns out the Arduino IDE auto-generates prototypes for your functions and drops them in at the first function definition in the sketch. I'd added a small helper function above my includes, so the generated prototypes landed before my types existed. Rule I follow now: no function definitions above your includes in a .ino.

The watch would deep sleep and instantly boot into the wrong mode. I'm using light sleep in the idle loop with a short timer wake to save power, and that timer stays armed going into deep sleep, so it "slept" for a quarter second and woke confused. Clearing all wake sources before deep sleep fixed it.

The PCF8563 alarm enable bits are inverted, 0 means enabled. Fun.

Power stuff that made a real difference: display.hibernate() before deep sleep (skip this and the e-ink controller drains your battery in days), running at 80MHz with bursts to 240 for decompression and the star math, light sleep between button polls, and shorter idle timeouts in modes where you're not reading. Buttons still feel instant.

One rule I held the whole way: no hand-typed data anywhere. Star positions come from the Yale Bright Star Catalogue, constellation lines from d3-celestial, countries from the factbook.json project, words from Wordset, all pulled in by build scripts I can rerun. And before the astronomy code ever touched the watch, the same C got compiled on a desktop and its output compared against astropy across a pile of random dates worst star error is around a tenth of a pixel, sunrise within seconds. I wanted to be able to hold my wrist up at the night sky and trust it.

Code and build scripts going up on my github (sparkadium) soon. Happy to answer questions about the file format, the partition table, or the tsumego setup.

u/CommunityFan89 — 1 month ago
▲ 98 r/esp32

Go puzzle trainer on the Watchy e-ink watch. 11,814 problems in 402KB, no engine needed

I've been doing a bunch of Watchy V2 projects lately (dictionary, e-book reader, fractal watchface) and this one turned out to be a surprisingly good fit for the hardware, so I figured I'd share.

Full disclosure up front: Claude wrote the code on this one. My role was the idea, the design decisions, flashing and testing on real hardware, and the debugging back and forth when things didn't work. I know AI-assisted projects are a mixed bag around here, so take that for whatever it's worth to you. The end result runs on my wrist and I learned mostly how it works, which is what I care about.

It's a tsumego trainer. Tsumego are Go life and death puzzles, and they're basically perfect for a 200x200 1-bit e-ink screen. A full 19x19 Go board would be unreadable at 10px per intersection, but tsumego are local corner fights, usually 8x7 or so, which gives you 20-25px per point. Black stones are filled circles, white stones are outlined, and it looks great on e-ink.

The trick that makes it work with zero game AI: you don't need an engine. Puzzle collections come with the answers baked in, so verification is just "does your move match the answer key". There's a Python script that converts an open source problem library into a compact binary format, about 34 bytes per problem. The whole thing (Cho Chikun's three Life and Death encyclopedias plus a few tesuji collections, 11,814 problems) packs down to 402KB in SPIFFS.

The device still needs real Go rules for captures (flood fill for liberties), and this is where the AI workflow got interesting: it wrote a Python mirror of the on-device logic and simulated solving all 11,814 problems before I ever flashed anything. That caught a real data quirk. Some published solutions contain immediate ko recaptures, which is technically illegal but it's how problem authors write "and it becomes ko" without showing the ko threats. So solution moves bypass the ko check and only freehand moves get rule-checked. A few problems even have "play nothing" as the correct answer, so long-pressing a button passes.

Four buttons handle everything. Two move the cursor (it skips occupied points, which helps a lot), one places a stone, one undoes. Long press opens a menu with a problem scrubber that accelerates the longer you hold, 1 then 10 then 100 then 1000 per tick, so jumping around 11k problems takes a few seconds. Progress is a bitmap in NVS, deep sleep between sessions with state in RTC RAM, and partial refresh keeps stone placement feeling instant.

One gotcha that cost me an evening, and this part was all me and my old muscle memory: my usual mkspiffs command used -s 0x2F0000 for the 1MB APP / 3MB SPIFFS partition scheme, but newer ESP32 cores carve a 64KB coredump partition out of the end of it. SPIFFS silently refuses to mount if the image size doesn't match, and the correct size now is 0x2E0000. If your SPIFFS mysteriously won't mount after a core update, check that first.

Happy to share the code and the packer script if anyone wants them.

u/CommunityFan89 — 2 months ago
▲ 430 r/esp32

I put a 37,000-word dictionary on my ESP32 eink smartwatch

I built a fully offline dictionary for the Watchy V2 e-ink smartwatch. It holds over 37,000 searchable English words with definitions, runs entirely on-device with no phone or internet needed, and fits on the ESP32's 4MB flash.

What it does

  • On-device word search: using a 4-button alphabetical grid. You navigate a 6×5 letter grid (down/right with hold-to-repeat), build up your query, and it shows live prefix matches as you type. Pick a match and you get the full definition with word-wrapped text on the 200×200 e-ink display.
  • Paginated definitions: long definitions flow across multiple pages instead of getting cut off. Bottom-right/left buttons flip through pages before moving to the next/previous word.
  • Word of the Day: shows a random word on boot or when you press a button. Skips synonym redirects so you always get a real definition.
  • Synonym cross-references: less common words redirect to more common synonyms (like "ephemeral → passing"), so the definition still shows up. This compression trick lets me fit way more words in limited flash.
  • Clock on the search screen: reads the PCF8563 RTC. Time is settable via a button combo (top-left + bottom-right held together).
  • Persists your last viewed word: across reboots via SPIFFS.

The dictionary data pipeline

This was honestly the hardest part. I wrote a Python script that:

  1. Downloads the entire Wordset dictionary from GitHub (108K entries, modern community-curated definitions, CC-BY-SA licensed)
  2. Cross-references with Google's 20,000 most frequent English words to prioritize common words
  3. Adds a curated vocabulary supplement of ~130 "educated but not web-frequent" words (cynic, melancholy, ephemeral, serendipity, etc.) that frequency lists miss
  4. Builds synonym redirect entries, if a word's synonym is already in the dictionary with a full definition, the less common word becomes a tiny redirect (~15 bytes instead of ~100). This roughly doubled the searchable word count for the same file size.
  5. Abbreviates speech parts: (noun) → (n.), (adjective) → (adj.), etc.
  6. Outputs a sorted tab-separated file that the ESP32 binary-searches at runtime

The final dictionary: 15,784 full definitions + 10,387 synonym redirects + 11,285 frequency-ranked entries = 37,553 searchable words in 2.6MB.

Technical challenges and solutions

Partition wrangling. The default "Huge APP" partition gives the sketch 3MB and SPIFFS only ~900KB. My sketch compiles to ~467KB, so I flipped it, 1MB for the app, 3MB for SPIFFS, using a custom partitions.csv. Had to use Arduino IDE v2 for flashing the sketch (it reads custom partitions) and command-line mkspiffs + esptool for the SPIFFS upload since the Arduino v1 plugin doesn't handle custom partitions well.

Binary search on SPIFFS. Looking up a word in a 2.6MB sorted text file on SPIFFS using binary search. The initial implementation scanned forward to find line boundaries, which sometimes skipped entries entirely (searching "gho" wouldn't find "ghost"). Fixed by scanning backward with a 256-byte buffered read, one SPIFFS read instead of a hundred individual seek+read calls. Same buffered approach for navigating to the previous word.

E-ink partial refresh for the letter grid. Full screen refreshes on e-ink are slow. The letter picker grid uses setPartialWindow() to only refresh the grid region when navigating letters, making it feel much snappier.

GPIO 26 debounce. One of the four buttons (bottom-left, GPIO 26) is on an ADC2 pin which is electrically noisier than the others. It gets extra confirmation reads with delays between them to filter false triggers.

Display pin confusion. The Watchy V2 uses GPIO 10 for DISPLAY_DC, not 17 as some documentation suggests. This caused a boot crash loop that showed zero output on serial, took a while to figure out.

Hardware

  • Watchy V2 (ESP32 PICO-D4, GDEH0154D67 200×200 1-bit e-ink, PCF8563 RTC)
  • 4 buttons: top-right, top-left, bottom-right, bottom-left
  • No external components needed

Source

Code and dictionary generation scripts on GitHub: https://github.com/Sparkadium/Watchy-Dictionary

Includes the Arduino sketch, the DictEntry.h header, the Python prep script, and the custom partition table. The prep script downloads everything it needs from GitHub automatically.

Edit: correction: 28,608 words, not 37K — I was testing larger dictionaries that didn't fit the SPIFFS ceiling. Sorry!

Edit 2: Optimizations coming soon; with some compression, over 69,000 words/definitions were able to be put in the dictionary this uses. As well, initiating deep sleep after 15 seconds of inactivity makes sense to conserve battery life. The search function was also improved. I'll post these changes to the Github today or tomorrow.

u/CommunityFan89 — 2 months ago

I colored my drawing of Oliver Tree. I miss him.

I'm a hobbyist artist. I showed my mom the black and white sketch and she thought he was Elton John, so I added color.

u/CommunityFan89 — 2 months ago
▲ 1 r/deaf

How can I find and make friends with Deaf people?

I'm a hearing neurodivergent 36 year old LGBTQ man. Every Deaf person I've met in my life has been kind to me, and every interaction has been wholesome. I would love to be friends with someone Deaf. Unfortunately, I can miss social cues and don't follow up on opportunities to connect with others, Deaf or not. Should I just look up Deaf events in my area to go to and hope for the best? I know a little ASL, but not enough to hold a conversation, and I mix up my signs when nervous a lot.

reddit.com
u/CommunityFan89 — 3 months ago
▲ 786 r/asl

I made an ASL alphabet poster and donated it to the Canadian Association of the Deaf

Background and context: I'm a hearing Canadian artist and have wanted to learn ASL for a long time. I found that drawing the alphabet was good practice for hands. I got feedback from online groups for ASL as to how to best accurately portray hand signs. It took me 5 days to finish the poster, made by cutting hands out of paper, cutting out the shadows, and drawing the creases on those. That was last year I made this.

A few weeks ago I reached out to CAD-ASC to ask if they'd be interested in the poster I made. I sent a couple of photos of it, and they were happy to accept. I was asked if they could use it in their Pride display and hang it somewhere in their office space. I'm so glad, all the people I met with there liked the poster.

u/CommunityFan89 — 3 months ago