<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Hagen Williams</title>
    <description>The latest articles on DEV Community by Hagen Williams (@hagen_williams_b9992b1b47).</description>
    <link>https://dev.to/hagen_williams_b9992b1b47</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4150834%2Fda2662c1-87ef-4469-8b67-79a660755ae3.png</url>
      <title>DEV Community: Hagen Williams</title>
      <link>https://dev.to/hagen_williams_b9992b1b47</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/hagen_williams_b9992b1b47"/>
    <language>en</language>
    <item>
      <title>Errata Lens: an ESP32 agent that knows when the datasheet is wrong</title>
      <dc:creator>Hagen Williams</dc:creator>
      <pubDate>Wed, 30 Sep 2026 19:14:14 +0000</pubDate>
      <link>https://dev.to/hagen_williams_b9992b1b47/errata-lens-an-esp32-agent-that-knows-when-the-datasheet-is-wrong-1o5d</link>
      <guid>https://dev.to/hagen_williams_b9992b1b47/errata-lens-an-esp32-agent-that-knows-when-the-datasheet-is-wrong-1o5d</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/sanity-2026-09-16"&gt;Sanity Challenge, Path One: Ship an Agent That Queries Real Content&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Errata Lens&lt;/strong&gt; answers ESP32 questions the way a careful senior engineer does: it checks whether the chip's &lt;strong&gt;errata&lt;/strong&gt; overrides the &lt;strong&gt;datasheet&lt;/strong&gt;, for the &lt;strong&gt;exact silicon revision&lt;/strong&gt; on your board.&lt;/p&gt;

&lt;p&gt;That last part is the whole problem. The ESP32 has shipped in five silicon revisions (v0.0 → v3.1). Some bugs were fixed in v1.0, some in v3.0, some were never fixed, and at least one (WDT-3.15, a live-lock watchdog issue) only &lt;em&gt;appears&lt;/em&gt; on v3.x. The datasheet reads the same for all of them. Ask a normal chatbot "can I rely on GPIO36 as an input with Wi-Fi on?" and you get the datasheet answer, which is how people lose a week to an 80 ns glitch (GPIO-3.11).&lt;/p&gt;

&lt;p&gt;I'm a computer engineering student who writes Verilog and firmware, and this is the tool I wanted on the bench.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Try asking it:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"Reading a sensor on GPIO36 with Wi-Fi on, I get random low glitches. Why?"&lt;/li&gt;
&lt;li&gt;"Chip is ESP32-D0WD (revision v1.1). Is PSRAM safe?"&lt;/li&gt;
&lt;li&gt;"Should we move our product from v1.1 to v3.1? What do we gain and what new bugs do we get?"&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Live app:&lt;/strong&gt; &lt;a href="https://esp32-errata-lens.vercel.app" rel="noopener noreferrer"&gt;https://esp32-errata-lens.vercel.app&lt;/a&gt; (pick a silicon revision, then try an example question)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9oqn2vt9i2x43p8jnv6u.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9oqn2vt9i2x43p8jnv6u.gif" alt="Errata Lens demo: GPIO36 glitch question, GROQ join, Knowledge Base conflict, v1.1 to v3.1 upgrade" width="600" height="338"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;An upgrade question, as answered by the agent (v1.1 → v3.1):&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Errata&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Fixed going to v3.1&lt;/td&gt;
&lt;td&gt;CPU-3.9, CPU-3.10, RES-3.8&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;New on v3.1&lt;/td&gt;
&lt;td&gt;WDT-3.15 (dual-core live-lock, no silicon fix)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Unchanged&lt;/td&gt;
&lt;td&gt;22, including GPIO-3.11 and all 11 TWAI (CAN) errata&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;It got those counts (25 on v1.1, 23 on v3.1) from GROQ joins, then read the Knowledge Base for the WDT-3.15 workaround: a level 4/5 timer interrupt on each core.&lt;/p&gt;

&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/Quasie1" rel="noopener noreferrer"&gt;
        Quasie1
      &lt;/a&gt; / &lt;a href="https://github.com/Quasie1/esp32-errata-lens" rel="noopener noreferrer"&gt;
        esp32-errata-lens
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      ESP32 agent that checks the errata against the datasheet for your exact silicon revision. Sanity Context (Knowledge Base + GROQ MCP) + Claude. DEV x Sanity Challenge entry.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Errata Lens — ESP32&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;An agent that answers ESP32 questions by checking the &lt;strong&gt;errata against the datasheet for your exact silicon revision&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Built for the &lt;a href="https://dev.to/challenges/sanity-2026-09-16" rel="nofollow"&gt;DEV × Sanity Challenge&lt;/a&gt;, Path One.&lt;/p&gt;
&lt;div class="snippet-clipboard-content notranslate position-relative overflow-auto"&gt;&lt;pre class="notranslate"&gt;&lt;code&gt;esp32-errata-agent/
├── studio/   Sanity Studio: chip, chipRevision, erratum schemas + seed data (32 errata, 5 revisions)
└── app/      Next.js agent: Vercel AI SDK + Claude + two Sanity Context MCP endpoints
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;How it works&lt;/h2&gt;
&lt;/div&gt;
&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;What it holds&lt;/th&gt;
&lt;th&gt;Why it matters&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Knowledge Base&lt;/strong&gt; (Context, KB mode)&lt;/td&gt;
&lt;td&gt;ESP32 datasheet PDF + errata site (+ optional TRM)&lt;/td&gt;
&lt;td&gt;The build flags every place the errata contradicts the datasheet as an &lt;strong&gt;Issue&lt;/strong&gt;; you resolve it once and it sticks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Errata database&lt;/strong&gt; (Context, GROQ mode)&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;erratum&lt;/code&gt; → &lt;code&gt;chipRevision&lt;/code&gt; references, &lt;code&gt;fixedIn&lt;/code&gt;, &lt;code&gt;introducedIn&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;"Which bugs does &lt;em&gt;my&lt;/em&gt; v1.1 have that v3.1 fixes?" is a join, not a keyword search&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;identifyRevision&lt;/code&gt; tool&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Local parser&lt;/td&gt;
&lt;td&gt;Turns esptool output / boot logs / module markings&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;…&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/Quasie1/esp32-errata-lens" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;&lt;code&gt;studio/&lt;/code&gt; holds the Sanity schema and seed data, and &lt;code&gt;app/&lt;/code&gt; holds the Next.js agent. &lt;code&gt;README.md&lt;/code&gt; covers setup from scratch.&lt;/p&gt;

&lt;h2&gt;
  
  
  How I Used Sanity
&lt;/h2&gt;

&lt;p&gt;Two Sanity Context MCP endpoints, one per retrieval mode, because the problem has two halves.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Knowledge Base mode: reconciling prose that disagrees.&lt;/strong&gt;&lt;br&gt;
I pointed a Knowledge Base ("ESP32 Silicon Truth") at two crawls: the Espressif errata site (47 pages) and the ESP-IDF peripheral driver docs (25 pages). The build distilled 118 documents into 23 entries, tagging the errata topics &lt;code&gt;[core]&lt;/code&gt; because the Knowledge Base's purpose names them.&lt;/p&gt;

&lt;p&gt;The build also caught a real contradiction I would have missed. The drafted SPI entry said the GPIO matrix caps SPI at &lt;strong&gt;40 MHz&lt;/strong&gt;. The source says that's only true in half-duplex mode without DMA. For ordinary full-duplex reads the limit is &lt;strong&gt;26.6 MHz&lt;/strong&gt;. Context raised it as a &lt;em&gt;Critical&lt;/em&gt; conflict with both claims and their citations side by side:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmz3hbfizxnprg49wjag2.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmz3hbfizxnprg49wjag2.jpg" alt="SPI conflict raised by the Knowledge Base build" width="737" height="910"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I picked the source's claim, and it became a standing instruction that survives rebuilds. I also wrote one by hand for GPIO-3.11, anchored to the errata page and the GPIO driver page: when they disagree, the errata wins for the revisions it lists.&lt;/p&gt;

&lt;p&gt;A plain vector index over these pages would hand the agent both numbers and hope it picked right. Here the conflict gets settled once, at build time, by a person.&lt;/p&gt;

&lt;p&gt;The agent calls &lt;code&gt;initial_context&lt;/code&gt; for the outline, then &lt;code&gt;knowledge_base_read&lt;/code&gt; with several entry paths in one call (or &lt;code&gt;knowledge_base_search&lt;/code&gt; when the outline doesn't make the location obvious).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. GROQ mode: revision math that a keyword search can't do.&lt;/strong&gt;&lt;br&gt;
In the dataset, each &lt;code&gt;erratum&lt;/code&gt; references the &lt;code&gt;chipRevision&lt;/code&gt; documents it affects, plus &lt;code&gt;introducedIn&lt;/code&gt; / &lt;code&gt;fixedIn&lt;/code&gt;. Each revision stores its chip-marking letter, module code and eFuse signature. That turns questions into joins:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Bugs on my v1.1 that v3.1 fixes → CPU-3.9, CPU-3.10, RES-3.8
*[_type=="erratum" &amp;amp;&amp;amp; references("rev-esp32-v1-1")
  &amp;amp;&amp;amp; !references("rev-esp32-v3-1")]{errataId, title, fixedIn}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One gotcha worth sharing: my first version filtered with &lt;code&gt;"v1.1" in affectedRevisions[]-&amp;gt;name&lt;/code&gt;. It works against the Content Lake directly, but through the Context GROQ endpoint it silently returned &lt;strong&gt;zero results&lt;/strong&gt;. The endpoint rewrites dereferences into projections (&lt;code&gt;-&amp;gt;{"name": name}&lt;/code&gt;), so the &lt;code&gt;in&lt;/code&gt; compared a string against objects. Switching to &lt;code&gt;references()&lt;/code&gt; on the stable revision document IDs fixed it, and the system prompt now tells the agent to do the same.&lt;/p&gt;

&lt;p&gt;Keyword search can find "PSRAM". It can't tell you that CPU-3.9 applies to your v1.0/v1.1 board but not v3.x, or that upgrading brings in WDT-3.15. Structure can.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. A small local tool&lt;/strong&gt; parses esptool output, ESP-IDF boot logs, and module markings (&lt;code&gt;MF&lt;/code&gt;/&lt;code&gt;ME&lt;/code&gt;/&lt;code&gt;MG&lt;/code&gt;) into a revision, so users can paste what they already have.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Answer shape.&lt;/strong&gt; Every answer has the same four parts: Answer → Docs say → Errata override (with erratum IDs + links) → Your revision. The UI shows each MCP call as an expandable badge, so you can see the GROQ query and which KB entries were read.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stack:&lt;/strong&gt; Next.js 16, Vercel AI SDK 6 (&lt;code&gt;@ai-sdk/mcp&lt;/code&gt;), Claude, Sanity Studio 6.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sanity Project Details
&lt;/h2&gt;

&lt;p&gt;Project ID: &lt;code&gt;ziyzznsh&lt;/code&gt;, dataset &lt;code&gt;production&lt;/code&gt; (public). Try it: &lt;a href="https://ziyzznsh.api.sanity.io/v2025-01-01/data/query/production?query=*%5B_type==%22erratum%22%5D%5B0...5%5D%7BerrataId,title,fixedIn%7D" rel="noopener noreferrer"&gt;https://ziyzznsh.api.sanity.io/v2025-01-01/data/query/production?query=*[_type=="erratum"][0...5]{errataId,title,fixedIn}&lt;/a&gt;&lt;br&gt;
Schema: &lt;code&gt;chip&lt;/code&gt;, &lt;code&gt;chipRevision&lt;/code&gt; (with &lt;code&gt;efuseSignature&lt;/code&gt; object), &lt;code&gt;erratum&lt;/code&gt;. 32 errata × 5 revisions, transcribed from Espressif's errata summary tables.&lt;/p&gt;

&lt;h2&gt;
  
  
  Credits
&lt;/h2&gt;

&lt;p&gt;Errata data © Espressif Systems, from the &lt;a href="https://docs.espressif.com/projects/esp-chip-errata/en/latest/esp32/" rel="noopener noreferrer"&gt;ESP32 Series SoC Errata&lt;/a&gt;. The dataset stores only IDs, titles and revision applicability; all prose comes from the Knowledge Base with citations back to Espressif.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>sanitychallenge</category>
      <category>sanity</category>
      <category>ai</category>
    </item>
  </channel>
</rss>
