<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
  
  <title>Eren SOYLU</title>
  <subtitle>Engineer, Thinker, Art Enthusiast</subtitle>
  <link href="https://www.erensoylu.com/feed.xml" rel="self" />
  <link href="https://www.erensoylu.com/" />
  <updated>2026-08-14T00:00:00Z</updated>
  <id>https://www.erensoylu.com/</id>
  <author>
    <name>Eren SOYLU</name>
  </author>
  <entry>
    <title>Reading a schematic when you are not an electrical engineer</title>
    <link href="https://www.erensoylu.com/blog/reading-a-schematic/" />
    <updated>2026-06-09T00:00:00Z</updated>
    <id>https://www.erensoylu.com/blog/reading-a-schematic/</id>
    <content type="html">&lt;p&gt;I write firmware. Every so often a board lands on my desk with a schematic, a
half-finished bring-up doc, and a question: why doesn&#39;t the sensor respond? You
do not need an EE degree to answer that. You need a method.&lt;/p&gt;
&lt;h2&gt;Start from the connector, not the corner&lt;/h2&gt;
&lt;p&gt;Schematics are drawn for the person who designed them, and their reading order
is rarely useful to you. Do not start top-left. Start at the thing you are
debugging and walk outwards:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Find the part in the schematic by its &lt;strong&gt;designator&lt;/strong&gt; (&lt;code&gt;U7&lt;/code&gt;, &lt;code&gt;J3&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Find its power pins. Note the rail name — &lt;code&gt;+3V3&lt;/code&gt;, &lt;code&gt;VDDA&lt;/code&gt;, &lt;code&gt;VBUS&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Find that rail&#39;s regulator. Note its enable pin and what drives it.&lt;/li&gt;
&lt;li&gt;Only then follow the signal you actually care about.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Step 3 catches an embarrassing share of &amp;quot;dead peripheral&amp;quot; bugs. A rail with an
enable pin tied to a GPIO is a rail that is off until your firmware turns it on.&lt;/p&gt;
&lt;h2&gt;Net names are the real index&lt;/h2&gt;
&lt;p&gt;Every wire belongs to a &lt;strong&gt;net&lt;/strong&gt;, and nets have names. A good schematic uses the
same net name everywhere the wire appears, including across sheets, and the
off-sheet connector arrows tell you where to look next. If you can search the
PDF, searching for &lt;code&gt;I2C1_SDA&lt;/code&gt; is faster than tracing lines with a cursor.&lt;/p&gt;
&lt;p&gt;Watch for the two ways this bites:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Buses.&lt;/strong&gt; &lt;code&gt;SPI2_MOSI&lt;/code&gt; may appear once as part of &lt;code&gt;SPI2[0..3]&lt;/code&gt;. Expand the bus
label before concluding the pin is unconnected.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Net aliases.&lt;/strong&gt; Two names joined by a zero-ohm link or a solder bridge are
the same net &lt;em&gt;if the link is fitted&lt;/em&gt;. Check the BOM for &amp;quot;DNP&amp;quot; — do not
populate. A DNP resistor is a wire that isn&#39;t there.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Pull-ups are configuration&lt;/h2&gt;
&lt;p&gt;On an I²C bus, the pull-up resistors are not decoration; they set the rise time
and therefore the maximum usable clock. Two common failures:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;No pull-ups at all&lt;/strong&gt; because the designer assumed the MCU&#39;s internal ones.
Internal pull-ups are typically tens of kΩ — far too weak for 400 kHz.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Pull-ups on two boards.&lt;/strong&gt; Stack a carrier and a sensor board that each fit
4.7 kΩ and you get 2.35 kΩ, which some parts will not drive low enough.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If the bus works at 100 kHz and fails at 400 kHz, suspect the pull-ups before
your driver.&lt;/p&gt;
&lt;h2&gt;Three measurements before you touch firmware&lt;/h2&gt;
&lt;p&gt;With a multimeter and five minutes:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Measure&lt;/th&gt;
&lt;th&gt;Expect&lt;/th&gt;
&lt;th&gt;If it&#39;s wrong&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Each rail, board powered&lt;/td&gt;
&lt;td&gt;Within a few % of nominal&lt;/td&gt;
&lt;td&gt;Regulator not enabled, or shorted&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reset pin, idle&lt;/td&gt;
&lt;td&gt;Held at the rail, not floating&lt;/td&gt;
&lt;td&gt;Missing pull-up, or held by a debugger&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SDA and SCL, bus idle&lt;/td&gt;
&lt;td&gt;Both at the rail&lt;/td&gt;
&lt;td&gt;Missing pull-ups, or a device holding the line&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;That last row is the one I forget most. A device stuck mid-transaction holds SDA
low forever, and every subsequent transfer fails in a way that looks exactly
like a driver bug. Nine clock pulses on SCL frees it — most vendor HALs have a
&lt;code&gt;bus_recover()&lt;/code&gt; for precisely this.&lt;/p&gt;
&lt;h2&gt;The habit worth building&lt;/h2&gt;
&lt;p&gt;Write down what you measured, where, and when, next to the schematic reference:&lt;/p&gt;
&lt;pre class=&quot;language-text&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;2026-06-08  rev B, s/n 004
  +3V3   3.29 V   ok
  VDDA   0.00 V   -&gt; EN on U4 tied to PA8, firmware never drives it. Fixed.
  SDA    3.28 V   ok (R21/R22 fitted, 4k7)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Six months later that block tells you which board revision you were holding and
what was already ruled out. It is the same reason I keep calibration constants
in config rather than in code: a measurement without its context is just a
number.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Graceful shutdown in Go is mostly about the order</title>
    <link href="https://www.erensoylu.com/blog/graceful-shutdown-go/" />
    <updated>2026-07-02T00:00:00Z</updated>
    <id>https://www.erensoylu.com/blog/graceful-shutdown-go/</id>
    <content type="html">&lt;p&gt;Most &amp;quot;graceful shutdown&amp;quot; examples stop at &lt;code&gt;srv.Shutdown(ctx)&lt;/code&gt; and call it done.
That drains in-flight HTTP requests, which is genuinely useful — and it is also
the part least likely to lose anything. The data loss happens behind the
handler, in whatever the request handed off to.&lt;/p&gt;
&lt;h2&gt;The shape of the problem&lt;/h2&gt;
&lt;p&gt;A typical service has layers that must stop in a specific order:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Stop accepting new work&lt;/strong&gt; — the listener, and any queue consumer.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Finish work already accepted&lt;/strong&gt; — in-flight handlers, in-flight jobs.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Flush anything buffered&lt;/strong&gt; — metrics, batched writes, log shippers.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Close the connections&lt;/strong&gt; those flushes needed.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Reverse any two of these and you get a class of bug that only shows up under
deploy pressure: a batch writer closed before the handler that was about to use
it, or a database pool closed while a job still holds a transaction.&lt;/p&gt;
&lt;h2&gt;Signal first, then unwind&lt;/h2&gt;
&lt;pre class=&quot;language-go&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-go&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	ctx&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; stop &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; signal&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;NotifyContext&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;context&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Background&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
		syscall&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;SIGINT&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; syscall&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;SIGTERM&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;defer&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;stop&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

	srv &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;http&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Server&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;Addr&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;:8080&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Handler&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;routes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
	workers &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; worker&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;New&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;pool&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

	&lt;span class=&quot;token keyword&quot;&gt;go&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; err &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; srv&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;ListenAndServe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;errors&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Is&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;err&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; http&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ErrServerClosed&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
			log&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Fatalf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;listen: %v&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; err&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
	workers&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Start&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

	&lt;span class=&quot;token operator&quot;&gt;&amp;lt;-&lt;/span&gt;ctx&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Done&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
	log&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;shutdown: draining&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

	&lt;span class=&quot;token comment&quot;&gt;// Give the whole unwind one budget, not one per step.&lt;/span&gt;
	shutdownCtx&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; cancel &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; context&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;WithTimeout&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;context&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Background&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;25&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;time&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Second&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;defer&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;cancel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

	&lt;span class=&quot;token comment&quot;&gt;// 1 + 2: stop accepting, finish in flight.&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; err &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; srv&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Shutdown&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;shutdownCtx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; err &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		log&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Printf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;http drain: %v&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; err&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; err &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; workers&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Drain&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;shutdownCtx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; err &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		log&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Printf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;worker drain: %v&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; err&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

	&lt;span class=&quot;token comment&quot;&gt;// 3: flush buffers that the above may have filled.&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; err &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; metrics&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Flush&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;shutdownCtx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; err &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		log&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Printf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;metrics flush: %v&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; err&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

	&lt;span class=&quot;token comment&quot;&gt;// 4: only now is it safe to close the pool.&lt;/span&gt;
	pool&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Close&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
	log&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;shutdown: complete&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Budget the whole unwind, not each step&lt;/h2&gt;
&lt;p&gt;The mistake I see most often is a fresh 30-second timeout at every stage. Your
orchestrator does not care about your stages — Kubernetes sends &lt;code&gt;SIGTERM&lt;/code&gt;, waits
&lt;code&gt;terminationGracePeriodSeconds&lt;/code&gt;, then sends &lt;code&gt;SIGKILL&lt;/code&gt;. If the sum of your stage
timeouts exceeds that grace period, the last stage never runs, and the last
stage is usually the flush.&lt;/p&gt;
&lt;p&gt;One &lt;code&gt;context.WithTimeout&lt;/code&gt; shared across the unwind, set comfortably below the
grace period, makes the arithmetic obvious:&lt;/p&gt;
&lt;pre class=&quot;language-yaml&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;terminationGracePeriodSeconds&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;30&lt;/span&gt;   &lt;span class=&quot;token comment&quot;&gt;# must exceed the 25s budget above&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Test it the boring way&lt;/h2&gt;
&lt;p&gt;You do not need a chaos framework. Start the service, put a slow request through
it, send &lt;code&gt;SIGTERM&lt;/code&gt;, and assert the request completed and the job landed:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;curl&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-s&lt;/span&gt; localhost:8080/slow &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;sleep&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.2&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;kill&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-TERM&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;pgrep &lt;span class=&quot;token parameter variable&quot;&gt;-f&lt;/span&gt; myservice&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;wait&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If that passes and your logs show the four stages in order, you have the
property you actually wanted. Everything else is tuning.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Your PWM driver is lying to you</title>
    <link href="https://www.erensoylu.com/blog/pwm-calibration/" />
    <updated>2026-08-14T00:00:00Z</updated>
    <id>https://www.erensoylu.com/blog/pwm-calibration/</id>
    <content type="html">&lt;p&gt;I spent an evening convinced I had a bug in my servo easing code. The motion was
smooth on the bench and jittery on the rig, and the difference tracked with
nothing I could name. The code was fine. The clock was not.&lt;/p&gt;
&lt;h2&gt;The internal oscillator is nominal, not accurate&lt;/h2&gt;
&lt;p&gt;The PCA9685 datasheet gives its internal oscillator as &lt;strong&gt;25 MHz&lt;/strong&gt;. That number
is a nominal figure with a tolerance, and individual parts land a few percent
either side of it. The prescaler you write is computed against that assumed
25 MHz:&lt;/p&gt;
&lt;pre class=&quot;language-python&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;prescale &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;round&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;25_000_000&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;4096&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; target_hz&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If the real oscillator runs at 26 MHz, every pulse you emit is about 4% shorter
than you asked for. Ask for a 1500 µs neutral pulse and the servo sees roughly
1440 µs — which it reads as &amp;quot;rotate a bit&amp;quot;. Across a chain of servos with
slightly different parts, they disagree with each other too.&lt;/p&gt;
&lt;h2&gt;Measure the part in front of you&lt;/h2&gt;
&lt;p&gt;Put a scope or a logic analyser on one channel, ask for a known frequency, and
measure what actually comes out. Then solve for the oscillator:&lt;/p&gt;
&lt;pre class=&quot;language-python&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Ask for 50 Hz, measure 52.1 Hz on the pin.&lt;/span&gt;
measured_hz &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;52.1&lt;/span&gt;
requested_hz &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;50.0&lt;/span&gt;
actual_osc &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;25_000_000&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;measured_hz &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; requested_hz&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;# ≈ 26.05 MHz&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Store &lt;code&gt;actual_osc&lt;/code&gt; per board and use it in the prescaler calculation instead of
the constant. That is the whole fix:&lt;/p&gt;
&lt;pre class=&quot;language-python&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;set_frequency&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;self&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; target_hz&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    prescale &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;round&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;osc_hz &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;4096&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; target_hz&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;
    self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_write&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;MODE1&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; SLEEP&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_write&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;PRESCALE&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;max&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;min&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;255&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; prescale&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_write&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;MODE1&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; RESTART&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Leave the knob in&lt;/h2&gt;
&lt;p&gt;The instinct is to hide the calibration behind a constant once you have measured
one board. Don&#39;t. The next board will be different, and a &lt;code&gt;osc_hz&lt;/code&gt; field in your
config costs nothing:&lt;/p&gt;
&lt;pre class=&quot;language-toml&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-toml&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token table class-name&quot;&gt;driver.pca9685&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token key property&quot;&gt;address&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0x40&lt;/span&gt;
&lt;span class=&quot;token key property&quot;&gt;osc_hz&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;26_050_000&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;# measured 2026-08-12, board rev C&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A note with the date and the board revision turns a magic number into a
measurement. Six months later, that comment is the difference between trusting
the value and re-deriving it from scratch.&lt;/p&gt;
&lt;p&gt;The general lesson is older than this chip: anything derived from a nominal
oscillator, a nominal resistor, or a nominal supply voltage is an estimate. If
your control loop cares, measure it once and write it down.&lt;/p&gt;
</content>
  </entry>
</feed>