
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Michael Kafarowski</title>
    <link>https://michael.kafarowski.com/photography/</link>
    <description>Posts from Michael Kafarowski</description>
    <generator>Hugo</generator>
    
      <atom:link href="https://michael.kafarowski.com/photography/feed.xml" rel="self" type="application/rss+xml" />
    
    
    
      <item>
        <title>First Steps Into The Mesh</title>
        <link>https://michael.kafarowski.com/blog/mesh-radio-introduction/</link>
        <pubDate>Fri, 15 May 2026 00:00:00 +0000</pubDate>
        <guid>https://michael.kafarowski.com/blog/mesh-radio-introduction/</guid>
        <description><![CDATA[<h1>First Steps Into The Mesh</h1><p>Imagine an encrypted text communication network that is owned by you and your community, one that requires no subscription fees, works with little or no connection to the power grid, while serving as a gateway into meeting interesting new people and building skills in electronics. This is mesh radio!</p>
<p>With mesh radio, you can send and receive text messages across a city - not by relying on a cell company or service providers, but instead by bouncing messages between other low cost, license-free radios operated by members of your community. Your message takes multiple hops between other users&rsquo; devices, and the contents remain encrypted all the way to the recipient.</p>
<figure><img src="/blog/mesh-radio-introduction/mesh.webp"
			alt="Two friends on opposite sides of a map. Blue nodes dot the area, and green and grey arrows indicate directions of radio transmission."><figcaption>
			<p>Two friends from across town can send messages by bouncing the packets between other radios in range of each other.</p>
		</figcaption>
</figure>

<p>Meshtastic and Meshcore are two common mesh radio systems you can get into today, with minimal upfront cost and experience. Read on to learn more!</p>
<p><strong>Table of Contents</strong>
<nav id="TableOfContents">
  <ul>
    <li><a href="#why-mesh-radio">Why Mesh Radio?</a></li>
    <li><a href="#starter-hardware-for-meshtastic-and-meshcore">Starter Hardware for Meshtastic and Meshcore</a></li>
    <li><a href="#meshtastic-vs-meshcore">Meshtastic vs Meshcore</a></li>
    <li><a href="#build-and-own-your-hardware">Build and Own Your Hardware</a>
      <ul>
        <li><a href="#a-note-on-antennas">A Note on Antennas</a></li>
      </ul>
    </li>
    <li><a href="#setting-up-your-node">Setting Up Your Node</a>
      <ul>
        <li><a href="#basic-weather-resistant-design">Basic &ldquo;Weather Resistant&rdquo; Design</a></li>
      </ul>
    </li>
    <li><a href="#projects">Projects</a>
      <ul>
        <li><a href="#3d-printed-handheld-and-base-station-nodes">3D Printed Handheld and Base Station Nodes</a></li>
        <li><a href="#weatherproof-diy-solar-mesh-node">Weatherproof DIY Solar Mesh Node</a></li>
        <li><a href="#camping-or-wilderness-mesh-networks">Camping or Wilderness Mesh Networks</a></li>
        <li><a href="#mesh-transceiver-as-a-drone-payload">Mesh Transceiver as a Drone Payload</a></li>
        <li><a href="#wardriving-and-mesh-mapping">&ldquo;Wardriving&rdquo; and Mesh Mapping</a></li>
        <li><a href="#participating-in-the-community-and-expanding-the-network">Participating in the Community, and Expanding the Network</a></li>
      </ul>
    </li>
    <li><a href="#conclusion">Conclusion</a></li>
  </ul>
</nav></p>
<h2 id="why-mesh-radio">Why Mesh Radio?</h2>
<p>Mesh radios can be used as a primary communication method between groups while hiking, sailing or operating in an area with no cell coverage. During natural disasters or other emergencies where cell services or the power grid are down, mesh radio can serve as a backup communication method, especially since the infrastructure to run it is decentralized and can even be run on solar energy.</p>
<p>Meshtastic and/or Meshcore may already have a large existing community near you! Once you&rsquo;ve decided to buy some low-cost hardware, you can easily swap between firmware to test each system out.</p>
<h2 id="starter-hardware-for-meshtastic-and-meshcore">Starter Hardware for Meshtastic and Meshcore</h2>
<p>Mesh communications take place with a radio system called LoRa (LongRange). Cell phones don&rsquo;t have this type of radio circuitry, so you need special hardware to send messages from your phone. Here are some products that are ready-to-go. (I&rsquo;m not affiliated with any of the manufacturers linked in this article).</p>
<ul>
<li>The <a href="https://www.seeedstudio.com/SenseCAP-Card-Tracker-T1000-E-for-Meshtastic-p-5913.html" target="_blank" rel="noopener noreferrer">SenseCap Card Tracker</a>
 is a low profile, weather-resistant, battery-powered node that you can clip to your backpack and connect to with an app on your phone.</li>
<li>The <a href="https://lilygo.cc/en-us/products/t-deck" target="_blank" rel="noopener noreferrer">LILYGO T-Deck</a>
 is like an off-grid Blackberry. Instead of connecting your phone to a board, you can compose and read messages directly on the device.</li>
</ul>
<p>If you want to jump right in, pick up one of these and check out the <a href="https://meshtastic.org/docs/getting-started/" target="_blank" rel="noopener noreferrer">getting started guides for Meshtastic</a>
 or the <a href="https://github.com/meshcore-dev/MeshCore/blob/main/docs/faq.md" target="_blank" rel="noopener noreferrer">Meshcore FAQ</a>
.</p>
<figure><img src="/blog/mesh-radio-introduction/sensecap_backpack.webp"
			alt="A photo of an orange backpack with a credit card shaped circuit board clipped to it"><figcaption>
			<p>The SenseCAP Card can be clipped to a backpack and connects to your phone with Bluetooth. Image: amazon.ca</p>
		</figcaption>
</figure>

<p>While the products above are pretty plug-and-play and might work out of the box for some, there&rsquo;s a huge ecosystem of DIY options for customizing, making and personalizing your own systems. If you&rsquo;re interested in technical details about radio, and building your own hardware appeals to you, the rest of this article is for you!</p>
<h2 id="meshtastic-vs-meshcore">Meshtastic vs Meshcore</h2>
<p>Meshtastic and Meshcore are two implementations of the mesh radio concept. Fundamentally, they both bounce messages between multiple devices in order to reach a recipient who may be out of direct radio range.</p>
<ul>
<li>With Meshtastic, every device can re-transmit any message it hears in order to extend its reach. Because messages flood the mesh, the geographical structure of the nodes within can change - if one node moves out of range, another may take its place to deliver the message via an alternate path.</li>
<li>With Meshcore, devices are explicitly assigned to one of two roles. Repeaters are geographically static, and will repeat any transmission they hear. They can&rsquo;t send messages on their own. Companions are mobile, and used to send and receive messages. They can&rsquo;t re-transmit messages.</li>
</ul>
<p>Meshtastic is more flexible, especially when the mesh moves dynamically, such as a group on a hike. On the other hand, with many devices flooding the network by re-transmitting everything they hear, network congestion can become an issue for larger groups.</p>
<p>Meshcore trades some geographical flexibility for more efficient message routing. When repeaters are static, reliable message paths can be determined, reducing the need for flood messages. In practice, this allows for meshes that can span larger geographic areas.</p>
<p>I have tried both, and I found Meshcore to have a stronger, more focused community and better infrastructure in my area.</p>
<h2 id="build-and-own-your-hardware">Build and Own Your Hardware</h2>
<p>All the off-the-shelf products listed earlier share common components - a LoRa radio and a microcontroller that allows you to connect with your phone and send messages over the LoRa radio. The same is true for boards in a development form factor.</p>
<p>The most common LoRa radio is the SX1262. It operates at 22dBm (0.16W), but some hardware boards integrate radio amplifiers to bring this up to 28dBm (0.63W) or even 30dBm (1W - the maximum power allowed in Canada).</p>
<p>Boards I currently run:</p>
<ul>
<li><a href="https://heltec.org/project/wifi-lora-32-v3/" target="_blank" rel="noopener noreferrer">Heltec V3</a>
: low cost ESP32-based, 22dBm. Not the most power efficient, but good for a hardwired HQ node that lives at home or your office and communicates directly with a nearby repeater. If I were starting again I&rsquo;d probably go straight to the Heltec V4.3 instead of the V3.</li>
<li><a href="https://heltec.org/project/mesh-node-t114/" target="_blank" rel="noopener noreferrer">Heltec T114</a>
: based on the nrf52, this radio is much more efficient than ESP32 radios, making it great as a portable companion. It also has a much sharper screen than the Heltec V3 but still only runs at 22dBm.</li>
</ul>
<figure><img src="/blog/mesh-radio-introduction/board.webp"
			alt="A Heltec V3 board  with a the meshtastic logo, and a toonie for scale"><figcaption>
			<p>Heltec V3 running Meshtastic. You can flash Meshcore to this board as well.</p>
		</figcaption>
</figure>

<p>I&rsquo;ve also ordered the higher power boards below.</p>
<ul>
<li><a href="https://heltec.org/project/wifi-lora-32-v4/" target="_blank" rel="noopener noreferrer">Heltec V4.3</a>
: Nearly equivalent to the V3 but boosted to 28dBm (0.6W) for basically the same price. Make sure you get the V4.3 version - the V4.2 board <a href="https://github.com/meshcore-dev/MeshCore/issues/2145" target="_blank" rel="noopener noreferrer">had hardware issues related to receiver sensitivity</a>
. Buying a V4.3 direct from Heltec guarantees you don&rsquo;t get old stock.</li>
<li><a href="https://heltec.org/project/t096/" target="_blank" rel="noopener noreferrer">Heltec T096</a>
: A new release, with 28dBm (0.6W) and additional integrated GPS. I&rsquo;m looking forward to getting the T096 - it could be perfect for a small yet capable solar node or handheld.</li>
<li><a href="https://meshtastic.org/docs/hardware/devices/rak-wireless/wismesh/1w-booster/" target="_blank" rel="noopener noreferrer">RAK 1W</a>
: At 30dBm (1W) it&rsquo;s one of the most powerful boards available. I&rsquo;m planning to build a solar-powered, minimal maintenance rooftop node that will expand Meshcore into my area.</li>
</ul>
<p>With development boards, you can decide how to build your own power and antenna systems.</p>
<h3 id="a-note-on-antennas">A Note on Antennas</h3>
<p>Powering on a board without an antenna attached will burn the LoRa transceiver! This is because the radio energy has nowhere to go, and instead is reflected back into the device. The device does not like this.</p>
<p>If your hardware is from a kit, it probably came with a poor quality antenna. For good performance, you should consider buying an UF.L to SMA adapter cable, and a half-wave SMA whip antenna. If you&rsquo;re planning to build an outdoor node, you can buy UF.L to SMA adapters with O-rings. Consider also N-Type connectors and antennas, which are designed to be more durable against the elements.</p>
<figure><img src="/blog/mesh-radio-introduction/antennas.webp"
			alt="A collection of antennas"><figcaption>
			<p>From top left to right: a UF.L to SMA adapter, an SMA &lsquo;stubby&rsquo; antenna, and two poor-quality kit antennas. Bottom: half-wave antenna for 915MHz.</p>
		</figcaption>
</figure>

<p>Keep any coax cable as short as possible. At these low power levels even &ldquo;low-loss&rdquo; coax longer than a decimetre can have a noticeable negative impact on power output.</p>
<p>Ensure your antenna is tuned to the specific <a href="https://www.thethingsnetwork.org/docs/lorawan/frequencies-by-country/" target="_blank" rel="noopener noreferrer">LoRa frequency range in your region</a>
. You can test how well your antenna will transmit at a given frequency by using a <a href="https://nanovna.com/" target="_blank" rel="noopener noreferrer">Vector Network Analyzer</a>
 (VNA) and checking for the Standing Wave Ratio (SWR) - the ideal antenna is near 1 but most antennas land within 1.5 and still work fine. Not all antennas are created equal! I bought 3 &ldquo;identical&rdquo; antennas online, but one of them had much worse SWR than the other two.</p>
<figure><img src="/blog/mesh-radio-introduction/swr.webp"
			alt="Two side-by-side images of a VNA measuring SWR of a stubby, and half-wave antenna."><figcaption>
			<p>The stubby is not an effective radiator, coming in with SWR ~2, compared to the half-wave antenna that comes in at nearly 1.1.</p>
		</figcaption>
</figure>

<h2 id="setting-up-your-node">Setting Up Your Node</h2>
<p>The power level allowed for unlicensed broadcast at LoRa frequencies is 1W or less, depending on your region. This is quite low, relative to what an amateur radio operator may be used to. Therefore, it&rsquo;s less possible to achieve good contact through brute-force power output.</p>
<p>The best predictor of success for reception is line-of-sight. This means you&rsquo;ll get best results by setting up your antenna as high up and with as large of a view as you can. The website SCADACore has <a href="https://www.scadacore.com/tools/rf-path/rf-line-of-sight/" target="_blank" rel="noopener noreferrer">a cool utility for visualizing topographical line-of-sight between two points</a>
.</p>
<p>Avoid mounting antennas horizontally. Imagine a doughnut, with the antenna sticking up through the hole. The further the edge of the doughnut the stronger the emitted radio signal. With the antenna pointing up (doughnut in eating orientation), you get good 360 degree coverage. With it horizontally (doughnut in rolling orientation), most of your radio energy is directed into the ground and sky. Wikipedia has a good <a href="https://en.wikipedia.org/wiki/Monopole_antenna#Radiation_pattern" target="_blank" rel="noopener noreferrer">image that demonstrates the radio energy distribution of a monopole antenna</a>
, though I don&rsquo;t know why you&rsquo;d need it considering visual power of my doughnut analogy. Also, radios can hear each other better when their antennas are in the same orientation (due to polarization).</p>
<p>Also avoid placing the antenna next to other metal, especially if it&rsquo;s grounded. Other metal will absorb or distort the radio energy pattern.</p>
<h3 id="basic-weather-resistant-design">Basic &ldquo;Weather Resistant&rdquo; Design</h3>
<p>You&rsquo;ll find many designs online for outdoor nodes. Some are 3D printed, others use off-the-shelf weatherproof enclosures. What do you do if you want to get started right away and don&rsquo;t have anything like that?</p>
<figure><img src="/blog/mesh-radio-introduction/sketchy_outdoor_node.webp"
			alt="A plastic box encloses a circuit board, attached to a wooden post. A USB cable exits the box and an antenna protrudes out the top. The entire thing is wrapped in clear plastic."><figcaption>
			<p>If it looks silly, but it works&hellip; well, it may still be silly. But it does work.</p>
		</figcaption>
</figure>

<p>Well, the little box the device comes in kinda works? I drilled some holes to zip tie it to a horizontal pole mount, drilled more for the SMA connector and USB cable, and then slipped it into a small plastic sandwich bag. An elastic band or zip tie closing up the bag completes the build. The USB cable runs through my balcony window into a power supply in my living room.</p>
<p>It&rsquo;s pretty sketchy, and probably won&rsquo;t hold out the weather for long, but I&rsquo;ve had two of these nodes on my balcony for a few weeks now, and they have survived a few rainy days.</p>
<figure><img src="/blog/mesh-radio-introduction/node_installation.webp"
			alt="A diagram showing the installation of the node at an angle, with a dip in the cable."><figcaption>
			<p>Create natural points where water can pool and fall off, rather than having it run into the node or your window.</p>
		</figcaption>
</figure>

<p>It works best if you mount it antenna-down, and make sure that the transceiver is slightly angled upwards from the mounting point so that water doesn&rsquo;t run down the stick into the bag. Finally, putting a dip in the cable just before it enters your window will prevent water from running along the cable and dripping inside your house.</p>
<h2 id="projects">Projects</h2>
<p>Like any good hobby, once you get into it there are many possible paths and projects you can explore. Here&rsquo;s a few that are on my list.</p>
<h3 id="3d-printed-handheld-and-base-station-nodes">3D Printed Handheld and Base Station Nodes</h3>
<p>I printed a body for a Heltec T114 to be my mobile Meshcore companion device.</p>
<p>I&rsquo;ve delegated a Heltec V3 to be my Meshcore home base station. It works alongside the repeater out on the balcony to receive public messages reliably, regardless of if I have reception on my handheld. It&rsquo;s hard-powered, but also has a small rechargeable battery that gives about a day of use if unplugged.</p>
<figure><img src="/blog/mesh-radio-introduction/3d_printed_cases.webp"
			alt="3D printed cases for the electronics boards.">
</figure>

<p>The designs in this photo are the <a href="https://www.printables.com/model/982046-h2t-case-for-heltec-t114-with-gps-running-meshtast" target="_blank" rel="noopener noreferrer">H2T</a>
 and the <a href="https://www.printables.com/model/797822-bender-battery-case-for-the-heltec-v3-heltec-t114" target="_blank" rel="noopener noreferrer">Bender</a>
, available on Printables.</p>
<h3 id="weatherproof-diy-solar-mesh-node">Weatherproof DIY Solar Mesh Node</h3>
<p>Sandwich bags are not known for their long-term resistance to the elements. I also still need to have my window cracked open to run power to the nodes, and I don&rsquo;t know how much longer my wife Joyce will tolerate the tripping hazards while she&rsquo;s gardening.</p>
<p>A small solar panel (1-5W) can be sufficient to run a repeater node indefinitely, in combination with rechargeable batteries. Some boards, such as the Heltec T114 or T096, have solar power input and battery charge controllers built directly onto the board.</p>
<p>A proper weatherproof setup and a solar panel allow installation in more remote areas, and can help you and your community maintain a communication channel even in the event that severe weather affects the power grid in your area.</p>
<p>Use caution when running lithium-ion or lithium-polymer batteries, as they can fail spectacularly if overcharged, overheated, or short-circuited.</p>
<h3 id="camping-or-wilderness-mesh-networks">Camping or Wilderness Mesh Networks</h3>
<p>When I go camping, I&rsquo;m usually at the edge of, or outside of cellphone reception. This makes it difficult to communicate with friends, and also tends to drain my phone battery as it struggles to try to connect to the cell network.</p>
<p>Next time I go camping, I&rsquo;ll hang a battery-powered repeater node from a tree, and give companion nodes to my friends. We should be able to keep our phones on airplane mode to preserve battery, yet remain in contact over long distances via the mesh.</p>
<figure><img src="/blog/mesh-radio-introduction/camping.webp"
			alt="A diagram showing three people geographically separated. Blue arrows connect them to a central transmitter, while a red arrow with an X connects the two further people."><figcaption>
			<p>A central mesh node can allow people to communicate (blue) even if they are out of direct radio range (red). Yes, those are emojis. What, did you think I was going to use AI?</p>
		</figcaption>
</figure>

<p>This seems like a good use-case for Meshtastic&rsquo;s <a href="https://meshtastic.org/docs/configuration/module/store-and-forward-module/" target="_blank" rel="noopener noreferrer">store-and-forward mode</a>
, and Meshcore&rsquo;s <a href="https://github.com/meshcore-dev/MeshCore/blob/main/docs/faq.md#125-room-server" target="_blank" rel="noopener noreferrer">room servers</a>
, where a limited number of messages can be asynchronously retrieved by a client node that may have been out of range at the time they were sent.</p>
<p>If a base station or repeater device isn&rsquo;t available, Meshtastic&rsquo;s ad-hoc routing is naturally suited for a mesh where participants move. Meshcore users can enable client repeat mode which does the same thing. Note that client repeat mode uses a secondary frequency so you&rsquo;ll be disconnected from normal mesh repeaters while it&rsquo;s active.</p>
<h3 id="mesh-transceiver-as-a-drone-payload">Mesh Transceiver as a Drone Payload</h3>
<p>Few radio towers can beat the line-of-sight achieved by zip-tying a transceiver to a drone - that is, if you only need it for the 20 minutes that the drone battery lasts. Zip-tying nearly anything to a micro drone powerful enough to lift the transceiver (e.g. a Mavic Mini) might push the takeoff weight over 250g, so in Canada you&rsquo;d need <a href="https://tc.canada.ca/en/aviation/drone-safety/learn-rules-you-fly-your-drone/drone-operation-categories-pilot-certificates" target="_blank" rel="noopener noreferrer">a small basic drone operations license</a>
. If you land the drone on a structure to enable longer mesh operations, ensure you have permission from the property owner.</p>
<p>Avoid mounting the transceiver on the bottom of the drone if it has downward facing collision sensors, as the drone may interpret this as an obstacle and prevent you from descending. Avoid covering any sensors when zip-tying for the same reason.</p>
<h3 id="wardriving-and-mesh-mapping">&ldquo;Wardriving&rdquo; and Mesh Mapping</h3>
<p>The Meshcore community has developed <a href="https://meshmapper.net/" target="_blank" rel="noopener noreferrer">MeshMapper</a>
, which aims to document the reach of the Mesh network. Volunteers bring their nodes with them in their car, and the MeshMapper app automatically searches for repeaters and attempts mesh communications along their journey. It&rsquo;s a fun way to contribute to the mesh, even if you can&rsquo;t run a repeater yourself. If you do run a repeater, valuable data showing the real-world range of your repeater will become available.</p>
<figure><img src="/blog/mesh-radio-introduction/yyz_meshmapper.webp"
			alt="A map with a bunch of little square overlaid. Green areas cover the GTA core, while patchy green, orange and red areas cover smaller cities in the suburbs.."><figcaption>
			<p>Green areas indicate strong reliable coverage. Most of the Toronto downtown core and much of central Mississauga is covered.</p>
		</figcaption>
</figure>

<p>It&rsquo;s really cool to see how connected my city of Toronto is!</p>
<h3 id="participating-in-the-community-and-expanding-the-network">Participating in the Community, and Expanding the Network</h3>
<p>In the past, radio technologies and amateur radio groups have assisted in emergency situations where modern local communication infrastructure is overwhelmed or unavailable. As a more mundane example, in 2022, the major Canadian telecom company Rogers experienced a <a href="https://en.wikipedia.org/wiki/2022_Rogers_Communications_outage" target="_blank" rel="noopener noreferrer">service outage</a>
 that left 25% of Canada without internet connectivity for nearly a day.</p>
<p>Some envision blanketing entire towns, cities, states, or even entire countries with a mesh communication network as a backup to primary services. Others just enjoy building nodes, testing new hardware, or just chatting with others on the public channel.</p>
<p>Share this article with a friend and get them into the Mesh! Search for groups in your area, or start one of your own. You may also have some luck if you talk to your local ham radio club.</p>
<p>Finally, both <a href="https://github.com/meshtastic/firmware" target="_blank" rel="noopener noreferrer">Meshtastic firmware</a>
 and <a href="https://github.com/meshcore-dev" target="_blank" rel="noopener noreferrer">Meshcore firmware</a>
 are open-source, meaning you can build upon and improve the code for everyone.</p>
<h2 id="conclusion">Conclusion</h2>
<p>I hope this article has piqued your interest in this cool technology! There&rsquo;s something that&rsquo;s so intriguing to me about this little radio that can send and receive direct transmissions 10&rsquo;s of kilometres away, using less energy than the LED in your TV remote. I also love that using this technology unveils a hidden world - the apartment you walk by every day might have a Meshcore repeater on the roof. With so many connections to other STEM hobbies like 3D printing, electronics, and more, there&rsquo;s so much to uncover.</p>
<p>If you&rsquo;ve gotten into the mesh, or are working on any projects, <a href="/contact">reach out to me</a>
! I&rsquo;d love to hear about it.</p>
]]></description>
      </item>
    
      <item>
        <title>BIC Pencils as Pivots for 3D Printed Parts</title>
        <link>https://michael.kafarowski.com/blog/bic-pivots-3d-printing/</link>
        <pubDate>Sun, 11 Jan 2026 00:00:00 +0000</pubDate>
        <guid>https://michael.kafarowski.com/blog/bic-pivots-3d-printing/</guid>
        <description><![CDATA[<h1>BIC Pencils as Pivots for 3D Printed Parts</h1><p>I&rsquo;m working on a 3D printed project that requires rotational motion. With purely 3D printed parts, options for creating smooth rotation are limited. Layer lines and print orientation limit the strength and smoothness of a pivot. Ball bearings can be integrated into a printed part but are large and expensive. Simple materials like toothpicks may not rotate easily or can be weak.</p>
<p>A simple and cost-effective solution I discovered is to use the parts from a <a href="https://us.bic.com/en_us/bic-color-cues-mechanical-pencil-set-60-count-pack.html" target="_blank" rel="noopener noreferrer">BIC mechanical pencil</a>
. You can buy these for cents, but you probably have dozens just lying around!</p>
<figure><img src="/blog/bic-pivots-3d-printing/animation.gif"
			alt="An animation showing a 3d printed gimbal mechanism made with the BIC pencil pivots"><figcaption>
			<p>This is an example of a printed part using BIC pivots, enabling very smooth motion.</p>
		</figcaption>
</figure>

<p>Each pencil can make two pivots - one made of the inner and outer body tubes, and another out of the components that make up the nose mechanism. These pieces are made of glossy plastic and slide very smoothly on each other. You can easily press-fit or glue the parts into a 3d printed model.</p>
<p>Dismantle the pencil by pulling off the front cap. Carefully snip off the flange keeping the black collar in place (the flange pieces like to fly off at high speed so protect your eyes). The little spring isn&rsquo;t used here, but they can come in handy for other projects.</p>
<figure><img src="/blog/bic-pivots-3d-printing/parts.webp"
			alt="A dismantled BIC pen showing the outer plastic tube and a modified inner plastic tube. The inner plastic tube has the spring and black collar detached from the end. A pair of flush cutters rests nearby.">
</figure>

<p>You can use the black collar and the remaining piece of the tip of the pencil as a small pivot. Despite its size, it&rsquo;s surprisingly strong. The black collar has been press fit into the round piece, and the tip was cut off the pencil and press fit into the long piece.</p>
<figure><img src="/blog/bic-pivots-3d-printing/small_pivot.webp"
			alt="The tip of the pencil and the black collar is used to create a pivot between two 3d printed pieces.">
</figure>

<p>You can also use the inner and outer body pieces as a larger pivot for both rotational and linear motion. Cutting can be done with a saw, or with strong scissors or flush cutters. If using a pinching tool, you may need to reform the cut outer cylinder before the inner will rotate smoothly.</p>
<figure><img src="/blog/bic-pivots-3d-printing/large_pivot.webp"
			alt="The outer tube and the inner tube is used to create a pivot.">
</figure>

<p>When press-fitting into a part, you may wish to heat the print slightly first to allow it to conform easier, otherwise you risk the insertion force causing cracks or separation of the layers. Here I use a hot air station set to 100°C. The glass transition (softening) temperature of PLA is around 60°C, so the air hitting the part only needs to be warm, just bordering on hot.</p>
<figure><img src="/blog/bic-pivots-3d-printing/heat.webp"
			alt="A heat gun is shown warming the side of a 3d printed part where the BIC pencil pivot is to be inserted.">
</figure>

<p>I created the nested gimbal ring you saw above as a demonstration model, which you can <a href="https://www.printables.com/model/1551393-bic-pencil-pivot-gimbal" target="_blank" rel="noopener noreferrer">download for free on Printables</a>
. A small M3 screw with a weight on the end is attached to the center ring, and will always remain parallel to the ground as the outer ring is rotated around it. The pivots allow for smooth operation even with an extremely light balancing weight thanks to their low friction.</p>
<figure><img src="/blog/bic-pivots-3d-printing/model.webp"
			alt="A 3d printed gimbal held in my hand.">
</figure>

<p>I also designed this marble labyrinth game which uses the small and large BIC pivots! It is also <a href="https://www.printables.com/model/1553050-marble-balance-maze" target="_blank" rel="noopener noreferrer">available for download on Printables</a>
. I&rsquo;ve submitted it to the Balancing Games contest, so give it a like, download and make if you enjoyed it!</p>
<figure><img src="/blog/bic-pivots-3d-printing/maze2.gif"
			alt="An animation of a 3d printed marble labyrinth game.">
</figure>

<p>Hope you find this trick useful! I haven&rsquo;t seen this published anywhere else online. Try it out in on your own 3D printed projects and <a href="/contact/">let me know</a>
 what you make!</p>
]]></description>
      </item>
    
      <item>
        <title>Repair of a Marine Ammeter</title>
        <link>https://michael.kafarowski.com/blog/meter-repair/</link>
        <pubDate>Sun, 28 Dec 2025 00:00:00 +0000</pubDate>
        <guid>https://michael.kafarowski.com/blog/meter-repair/</guid>
        <description><![CDATA[<h1>Repair of a Marine Ammeter</h1><p>I found a marine-rated digital ammeter at a thrift store, but when I got home I was disappointed to find it wasn&rsquo;t working. In this article I&rsquo;ll show how I found the problem and repaired it.</p>
<p><strong>Table of Contents</strong>
<nav id="TableOfContents">
  <ul>
    <li><a href="#safety-first">Safety First</a></li>
    <li><a href="#issue-investigation">Issue Investigation</a>
      <ul>
        <li><a href="#circuit-analysis">Circuit Analysis</a></li>
        <li><a href="#taking-measurements">Taking Measurements</a></li>
      </ul>
    </li>
    <li><a href="#repairing-the-unit">Repairing the Unit</a></li>
    <li><a href="#lifetimes-of-electrolytic-capacitors">Lifetimes of Electrolytic Capacitors</a></li>
    <li><a href="#conclusion">Conclusion</a></li>
  </ul>
</nav></p>
<h2 id="safety-first">Safety First</h2>
<p><strong>This article is presented for informational purposes only</strong>. Dismantling and handling electronic devices can be dangerous. Never dismantle or handle a mains-powered circuit while it is connected to power. Components of circuits, even ones with low input voltage (such as disposable camera flashes) can generate hazardous voltages during operation. Capacitors can store large amounts of energy at hazardous voltage even after input power to a circuit is removed, and discharging them manually can generate burns or sparks.</p>
<h2 id="issue-investigation">Issue Investigation</h2>
<p>The meter is a DM-730E4A by a company called Chitai Electronic Corp, made in Taiwan. It converts a 0-50mV signal obtained from a shunt resistor into a current measurement from 0-250A. I powered on the device at its rated voltage of 24V to find it consumed about 30mA, but the LED display didn&rsquo;t light up.</p>
<figure><img src="/blog/meter-repair/circuit_off.webp"
			alt="A circuit board with a large LED 7 segment display sits on a desk mat, clamped in a small vice. The LEDs are unlit."><figcaption>
			<p>This DM-730E4A panel-mount ammeter seems to consume some power when plugged in, but the LEDs don&rsquo;t light up.</p>
		</figcaption>
</figure>

<h3 id="circuit-analysis">Circuit Analysis</h3>
<p>The first step of the repair is to analyse the circuit to get a rough idea of how it works. No schematics or any other details about this product are available online. Some reverse engineering is needed to find out how the circuit works in order to begin diagnosing it.</p>
<p>I like to web search the part numbers of all the visible integrated circuits on the board and look for datasheets. Datasheets tell us important info about the parts such as their purpose, what different pins do, and important ratings such as operating voltages and currents, signal thresholds, etc.  Good datasheets also contain detailed feature descriptions and example application circuits. Here are the datasheets of the two most prominent chips on the board:</p>
<ul>
<li><a href="https://www.power.com/sites/default/files/documents/tinyswitch-iii_family_datasheet.pdf" target="_blank" rel="noopener noreferrer">TNY280</a>
 Off-line flyback converter controller</li>
<li><a href="https://datasheet4u.com/pdf-down/A/M/E/AME-7106.pdf" target="_blank" rel="noopener noreferrer">AME7107R</a>
 All-in-one ADC + LED Driver</li>
</ul>
<p>I examined some of the traces and connections around the power converter and could then draw a simplified circuit diagram, shown below.</p>
<figure><img src="/blog/meter-repair/circuit_simple.webp"
			alt="An electronic with the following elements left to right - VBUS feeds a primary input of the transformer, the primary output travels through a MOSFET to GNDPWR. The secondary has a centre tap. The top secondary connection travels through a forward diode to the anode of the smoothing capacitor of 470uF, then a label &#43;VDC. The bottom connection travels through a backwards diode to the cathode of another 470uF capacitor, then goes to a label -VDC. The other end of each capacitor is connected to a label 0V Rev, which is in turn connected to the centre tap of the transformer secondary. A dashed line is drawn vertically through the transform, labelled Galvanic Isolation Barrier. An opto-isolator is shown below the circuit, demonstrating that a signal from the secondary voltage sense, not pictured, signals the gate of the earlier mentioned MOSFET through a block called Switch Logic."><figcaption>
			<p>A simplified circuit diagram of the power stage of the converter. The +VDC, -VDC and 0V Ref connection continue to linear voltage regulators, then the rest of the circuit.</p>
		</figcaption>
</figure>

<p>The TNY280 controls an isolated flyback converter, which changes an input voltage on one side of a transformer into a isolated, variable voltage on the other. The secondary side of the transformer has two half-bridge rectifiers each about a centre tap. The centre tap acts as the 0V reference on the secondary side, and the two half-bridge rectifiers produce a positive and negative DC voltage relative to the 0V reference. The positive and negative voltages are then passed to linear regulators, before continuing on to power the rest of the circuit.</p>
<p>The second major component of the circuit, AME7107R is an all-in-one chip that integrates an analog-to-digital converter and a seven-segment LED driver. According to the datasheet, it operates with a split-supply of 6V, 0V and -6V. The voltage created by the flyback converter should be at least this amount.</p>
<h3 id="taking-measurements">Taking Measurements</h3>
<p>I first ensured that the input voltage actually made it to the first converter stage by using my multimeter to verify the 24V input voltage was present at different points along the board leading up to the transformer.</p>
<p>I then measured the secondary side voltages relative to the 0V centre tap point. The positive voltage was only about 1V, while the negative voltage measured -9V. The low positive voltage was probably preventing the rest of the circuit from working properly.</p>
<p>I soldered a wire to the 0V point on the secondary side, and hooked my oscilloscope ground to it. I scoped the positive and negative voltages just past the rectifying diode at the anode of the 470uF smoothing capacitors. If the circuit was operating correctly, I would expect to see a fairly constant DC voltage, with a repeating pattern on top of it containing regular blips of voltage followed by longer periods where the voltage drops slightly.</p>
<p>To understand why this pattern would exist, imagine two buckets - one small and one large. The large one has a hole drilled in the bottom, so it constantly leaks a bit. The circuit begins by filling a small bucket with water from a tap. Then, it dumps the small bucketful of water into the large one. It repeats this process until the large bucket is full. As the water drains out of the hole in the large bucket, the circuit will occasionally top off the large water bucket by dumping another small bucket of water in. The water level represents the voltage, and the flow out of the hole represents the power consumption of the rest of the circuit.</p>
<p>What I observed on the meter was instead that the positive rail displayed a short spike followed by a fast decay to a low voltage.</p>
<p>To me, this indicated that the energy from the transformer was very quickly filling the capacitor but only a small amount of energy was actually stored. It was like the large bucket had become tiny, and all the bucketfuls dumped into it mostly spilled out.</p>
<figure><img src="/blog/meter-repair/scope_before.webp"
			alt="A trace on an oscilloscope showing a sharp peak of voltage that quickly decays to zero volts after about 1us."><figcaption>
			<p>This pattern repeated every few hundred microseconds. Such large peaks should not be there, and the average DC value should be higher. The charge isn&rsquo;t being effectively stored between cycles.</p>
		</figcaption>
</figure>

<p>It seemed that this 13 year old electrolytic capacitor had lost much of its capacitance. I made a note of the position and polarity of all the electrolytic capacitors on the board, then desoldered them.</p>
<p>With the capacitors desoldered, I tested them with an LCR meter. The 470uF rated capacitor probed earlier measured only 8-10uF and &gt;100 ohms of equivalent series resistance (ESR). Yes, that&rsquo;s a 50x reduction in capacitance, and about 1000x times the ESR of a new equivalent part.</p>
<figure><img src="/blog/meter-repair/lcr.webp"
			alt="A capacitor labelled 470uF is held next to the display of an LCR meter which reads 9.683uF and 109.9 ohms when measured in series mode at 1kHz."><figcaption>
			<p>The 470uF capacitor I desoldered only measured single digit microfarads and huge ESR - a new equivalent part later measured 427uF and 0.07 ohms with the same meter settings.</p>
		</figcaption>
</figure>

<p>This is not a surprising result, and in fact it was the first thing I suspected when I looked at this old circuit. The quality of the electrolyte in the capacitor directly affects the capacitance and ESR, and the electrolyte quality degrades over time and especially when exposed to heat. When the capacitance drops out of spec, critical functions of the circuit stop working properly.</p>
<h2 id="repairing-the-unit">Repairing the Unit</h2>
<p>Near exact drop-in replacements were available on Digikey. I searched for new electrolytic capacitors with the following criteria:</p>
<ul>
<li>Match original capacitance ratings</li>
<li>Match or exceed the maximum voltage rating</li>
<li>Ensure fitment by filtering by the same diameter (or smaller) while maintaining a max height that would fit in the enclosure</li>
<li>AEC-Q200 automotive qualification</li>
</ul>
<p>Parts certified for automotive use are a bit more expensive but are often rated for longer lifetimes at higher temperatures.</p>
<figure><img src="/blog/meter-repair/digikey.webp"
			alt="The interface of the website Digikey, with parametric search options highlighted."><figcaption>
			<p>Most component retailers will have extensive filters to help you narrow down your search for components.</p>
		</figcaption>
</figure>

<p>Once the parts arrived I soldered the new capacitors to the board. When installing electrolytic capacitors one must be careful to install them with the correct polarity, otherwise they will explode when the circuit is powered on!</p>
<p>After the capacitors were replaced, the meter worked right away! I measured the positive and negative rails again and found that the waveform now resembled the pattern I had expected, short pulses where the converter &ldquo;tops-off&rdquo; the voltage, followed by longer periods where the voltage drops a bit as energy is consumed by the load.</p>
<figure><img src="/blog/meter-repair/scope_after.webp"
			alt="A trace on an oscilloscope showing a peak of voltage that then decays to a constant voltage around 5V"><figcaption>
			<p>This particular trace was actually captured with a 220uF capacitor in place of the old 470uF, which was enough to restore proper operation. Once all capacitors were finally replaced with proper values, this peak almost completely disappeared.</p>
		</figcaption>
</figure>

<p>I used a simple voltage divider and a potentiometer to generate a 0-50mV signal to calibrate the meter. As I turned the potentiometer, I observed the meter changing from 0-250, its full range. Mission success!</p>
<figure><img src="/blog/meter-repair/success.webp"
			alt="A black box with a 3 digit LED display displaying 180A, connected to circuitry in the background.">
</figure>

<h2 id="lifetimes-of-electrolytic-capacitors">Lifetimes of Electrolytic Capacitors</h2>
<p>The root cause of the failure in the meter was an end-of-life wear-out failure of the electrolytic capacitors on the board.</p>
<p>Electrolytic capacitors work by layering a metal-oxide anode layer with a liquid/solid electrolyte cathode layer. The oxide on the anode layer serves as the dielectric (energy storage media). The oxide dielectric is very thin, and is also &ldquo;roughened up&rdquo; to increase its surface area. An electrolyte is used for the cathode because they can fill in the nooks and crannies of the oxide&rsquo;s rough surface. The combination of a very thin dielectric and a very large surface area gives electrolytic capacitors outstandingly high capacitance for their size.</p>
<p>A major trade-off is that over time and through exposure to high temperature the electrolyte cathode dries out, degrading the performance. The original capacitors on the meter were rated for 105 degrees Celsius, and usually this rating is tied to a lifetime - typically 2000 hours. That&rsquo;s less than three months of continuous use at the rated temperature, but for every 10 degrees of temperature reduction, lifetime roughly doubles.</p>
<p>If this meter came out of a boat somewhere near the equator, an ambient temperature of 30-40 degrees could be expected indoors while the boat sits out in the sun. Operating inside an enclosed box and inside an enclosed power panel, combined with self-heating through voltage ripple could result in operating temperatures exceeding 50-60 degrees. No more than 8-12 years of life can be expected in these conditions if the panel meters are in use 24/7.</p>
<p>I don&rsquo;t know exactly how long this meter was powered up, but the date code indicated the unit was from 2013. Also, if it&rsquo;s any indication (no pun intended), the LEDs had run for long enough to discolor the tinted cover plate around certain digit segments. Neat!</p>
<figure><img src="/blog/meter-repair/burnin.webp"
			alt="A dark red tinted piece of glass is held above a white piece of paper, and certain areas corresponding to the segments of the LED digits can be seen burned in."><figcaption>
			<p>Degredation of the pigments in the front filter was observed to take on the pattern of a 7-segment display, so the LEDs of the meters must have been operating for a long time.</p>
		</figcaption>
</figure>

<p>You can learn more about capacitors and their lifetime characteristics <a href="https://www.researchgate.net/publication/234063145_Electrolytic_Capacitor_Lifetime_Estimation" target="_blank" rel="noopener noreferrer">in this paper by Dr. Arne Albertson</a>
.</p>
<h2 id="conclusion">Conclusion</h2>
<p>Old devices that appear broken can sometimes be restored by replacing electrolytic capacitors that have degraded over time and use. These capacitors are in nearly all types of devices and have limited lifetimes especially when exposed to high operating temperature. When performing such repairs it can be fun to reverse-engineer an unknown board, and it&rsquo;s a great opportunity to learn about new chips and circuits by researching datasheets and inspecting the circuit board.</p>
]]></description>
      </item>
    
      <item>
        <title>100% Human Written</title>
        <link>https://michael.kafarowski.com/blog/human-written/</link>
        <pubDate>Wed, 03 Dec 2025 00:00:00 +0000</pubDate>
        <guid>https://michael.kafarowski.com/blog/human-written/</guid>
        <description><![CDATA[<h1>100% Human Written</h1><p><strong>100% of my English text is human-written and is comprised of my own opinions, thoughts and experiences, expressed in my own voice.</strong></p>
<p>This includes any published and unpublished writing, as well as correspondence such as comments or emails. This policy applies to both my personal and professional life.</p>
<p>I may edit images or photographs from their raw form using industry-standard tools, which may have integrated AI features (e.g. generative fill) for spot editing. Any images that are created or substantially altered by generative AI will be watermarked or disclosed as such.</p>
<p>I use AI tools to search for and help me learn new information in both my personal and professional life. I believe that the ultimate responsibility for any actions taken in response to AI rests entirely with the human using the tool.</p>
<p>I strive to produce <a href="/blog/">well-researched, high quality content</a>
 that is worth your time, respects your attention, and earns your trust. Rest assured that any mistakes that remain are human errors, not AI hallucinations.</p>
<figure><img src="/blog/human-written/thumbnail.webp"
			alt="A yellow sticky note with the words &#39;100% human written&#39; handwritten in black ink. A brush pen rests on the sticky note. The note and pen rest on a dark wooden desk.">
</figure>

<p><em>Je n’ai fait celle-ci plus longue que parce que je n’ai pas eu le loisir de la faire plus courte.</em> - Blaise Pascal (1657)</p>
]]></description>
      </item>
    
      <item>
        <title>Kafarowski Family Turkey Recipe</title>
        <link>https://michael.kafarowski.com/blog/turkey/</link>
        <pubDate>Mon, 13 Oct 2025 00:00:00 +0000</pubDate>
        <guid>https://michael.kafarowski.com/blog/turkey/</guid>
        <description><![CDATA[<h1>Kafarowski Family Turkey Recipe</h1><p>Turkey dinners are a family tradition for Thanksgiving, Easter and Christmas. We typically make the turkey for a 1pm lunch for 4-8 people. Ingredient preparation starts around 7am, the turkey is prepped and into the oven by 9am, and usually comes out to rest around noon.</p>
<p>This is the recipe I&rsquo;ve captured and formalized from collected recipe clippings, handwritten notes, and knowledge handed down. It&rsquo;s a living document that I&rsquo;ll update and add to as my process improves with each season.</p>
<p>Being able to follow a clear written process takes a lot of the stress out of the day! I&rsquo;m less likely to forget or skip steps, especially while I&rsquo;m waiting for my coffee to kick in, chatting with family, and/or after having a few glasses of wine!</p>
<figure><img src="/blog/turkey/turkey.jpg"
			alt="A roasted turkey cooling on an oven rack."><figcaption>
			<p>Here&rsquo;s the final product! If you have a hardware smell-o-vision installed on your device, you can activate it now for the full experience.</p>
		</figcaption>
</figure>

<p><strong>Table of Contents</strong>
<nav id="TableOfContents">
  <ul>
    <li><a href="#equipment-and-ingredients">Equipment and Ingredients</a></li>
    <li><a href="#preparation">Preparation</a></li>
    <li><a href="#cooking-the-turkey">Cooking the Turkey</a></li>
    <li><a href="#making-the-gravy">Making the Gravy</a></li>
    <li><a href="#notes">Notes</a>
      <ul>
        <li><a href="#frozen-turkey">My turkey is still frozen! What do I do?</a></li>
        <li><a href="#oven-explosion">The tempered glass pane of my oven exploded!</a></li>
      </ul>
    </li>
  </ul>
</nav></p>
<h2 id="equipment-and-ingredients">Equipment and Ingredients</h2>
<p>Gathering and arranging everything ahead of time is called <em>mise en place</em>, and makes the process smoother and stress-free!</p>
<table>
	<thead>
			<tr>
					<th>Equipment</th>
					<th>Purpose</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td>Roasting Pan</td>
					<td>Catches drippings and holds the turkey rack</td>
			</tr>
			<tr>
					<td>Turkey Rack</td>
					<td>Holds the turkey and allows air to circulate</td>
			</tr>
			<tr>
					<td>Set of small bowls</td>
					<td>Holds ingredients so you don’t have to touch containers while your hands are covered in turkey juice</td>
			</tr>
			<tr>
					<td>Disposable Gloves</td>
					<td>Makes handling the raw turkey less annoying</td>
			</tr>
			<tr>
					<td>Safety glasses</td>
					<td>Guards the eyes from raw turkey juice splashes (ask me how I know)</td>
			</tr>
			<tr>
					<td>Paper towels</td>
					<td>For patting down the turkey before cooking</td>
			</tr>
			<tr>
					<td>Basting Brush</td>
					<td>For applying olive oil to the turkey</td>
			</tr>
			<tr>
					<td>Aluminum Foil</td>
					<td>For plugging the cavity and covering the resting turkey</td>
			</tr>
			<tr>
					<td>Cooking Twine</td>
					<td>Tying up the legs and wings of the turkey. Pre-cut 3 pieces each 2 feet long.</td>
			</tr>
			<tr>
					<td>Leave-in Thermometer</td>
					<td>Measuring the internal temperature accurately without needing to open the oven</td>
			</tr>
			<tr>
					<td>Heat Pads</td>
					<td>To place the hot turkey pan on after cooking</td>
			</tr>
			<tr>
					<td>Towels</td>
					<td>Covering the turkey during its resting period</td>
			</tr>
	</tbody>
</table>
<table>
	<thead>
			<tr>
					<th>Ingredient</th>
					<th>Quantity</th>
					<th>Preparation</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td>Turkey (fresh)</td>
					<td>10-15lb <br> (4-7kg)</td>
					<td>Remove from fridge 1-2 hours before cooking to allow turkey to warm for more even cooking.</td>
			</tr>
			<tr>
					<td>Olive Oil</td>
					<td>?</td>
					<td>Pour into a small bowl for easy access later</td>
			</tr>
			<tr>
					<td>Salt</td>
					<td>~30g</td>
					<td>Measure out to a separate bowl for easy access later. Medium coarseness.</td>
			</tr>
			<tr>
					<td>Pepper</td>
					<td>~2g</td>
					<td>Add to salt bowl and mix</td>
			</tr>
			<tr>
					<td>Lemon</td>
					<td>½</td>
					<td>Cut into quarters. Lemon squeeze bottles work too</td>
			</tr>
			<tr>
					<td>Yellow Onion</td>
					<td>½ - 1</td>
					<td>Cut into quarters</td>
			</tr>
			<tr>
					<td>Carrots</td>
					<td>3</td>
					<td>Wash, peel, cut into 5cm long pieces</td>
			</tr>
			<tr>
					<td>Celery</td>
					<td>2-3</td>
					<td>Wash, cut into 5cm long pieces</td>
			</tr>
			<tr>
					<td>Fresh Parsley</td>
					<td>Sprigs</td>
					<td>Wash and pat dry</td>
			</tr>
			<tr>
					<td>Fresh Rosemary</td>
					<td>Sprigs</td>
					<td>Wash and pat dry</td>
			</tr>
			<tr>
					<td>Turkey Gravy Package</td>
					<td>2</td>
					<td>Reduced salt version recommended</td>
			</tr>
			<tr>
					<td>Flour</td>
					<td>As needed</td>
					<td></td>
			</tr>
			<tr>
					<td>Water</td>
					<td>&gt;400ml</td>
					<td></td>
			</tr>
	</tbody>
</table>
<figure><img src="/blog/turkey/ingredients.jpg"
			alt="A selection of the ingredients in the table above laid out on a counter.">
</figure>

<h2 id="preparation">Preparation</h2>
<ol>
<li>Close the door of any room in your house you don&rsquo;t want to smell like turkey for two days.</li>
<li>Remove the turkey from the fridge at least an hour before planning to roast (<a href="#frozen-turkey">what if my turkey is still frozen?</a>
)</li>
<li>Set oven pre-heat to 425°F (220°C) with bottom heat only, and remove the oven rack.</li>
<li>Set up your workspace with the roasting pan, turkey rack, bowls of salt/oil, paper towels, basting brush and cooking twine.</li>
<li>Don your safety glasses and gloves</li>
<li>Carefully open turkey bag and drain, then discard the turkey bag. Tip: cut a flap and drain into a bowl while the turkey is still in the bag to make cleanup easier</li>
<li>Remove any giblet bags and neck if present, set aside for soup, stuffing, or congee.</li>
<li>Place turkey on the roasting rack in a deep roasting pan with the legs up</li>
<li>Pat the inside and outside of the turkey dry with paper towels.</li>
<li>Squeeze the lemon into the cavity and lather it in.</li>
<li>Sprinkle a handful of salt into the turkey and rub it in.</li>
<li>Add most of the onion, celery, carrots and a few sprigs of rosemary and parsley into the cavity. Cap off with tin foil.</li>
<li>Baste the turkey with oil then rub a generous amount of salt/pepper over the turkey skin. Don’t forget between the wings and legs.</li>
<li>Cross the leg ends over each other, and tie together tightly with twine.</li>
<li>Press some sprigs of herbs in between the leg creases.</li>
<li>Turn the turkey over so the legs pointing down and the wings are at the top.</li>
<li>Add the rest of the onion celery, carrots and sprigs of herbs into the neck cavity.</li>
<li>Fold the wings back on top of themselves, and tie the “elbows” tight to the breast with twine. Cut excess string, otherwise it burns.</li>
<li>Press some sprigs of rosemary between the wing creases.</li>
</ol>
<p>You&rsquo;ll notice at this point that the turkey is &ldquo;upside down&rdquo;! We&rsquo;ve found that this helps the breast stay juicier because the juices will flow downwards.</p>
<h2 id="cooking-the-turkey">Cooking the Turkey</h2>
<ol>
<li>Insert an in-oven thermometer into the thickest part of the breast meat but avoid touching bone.</li>
<li>Place the prepped turkey tray into the bottom rack of the oven.</li>
<li>Cook at 425°F (220°C) for 30 minutes.</li>
<li>After 30 minutes, reduce oven setpoint temperature to 325°F (160°C) but <em>don’t open the door</em>.</li>
<li>While the turkey is cooking, use this time to sharpen the knives to prepare for carving.</li>
<li>Continue to cook until thermometer reads 160°F (70°C), after around 3-4 hours depending on the size and how frozen the turkey is before going in.</li>
<li>Remove from the oven. Drain turkey drippings into a saucepan for gravy (see below), then cover the turkey with a layer of foil and a layer of towels, then allow to rest at least 20-30 minutes. The internal temperature will continue to rise another few degrees during this time. Other parts of the turkey may be 10°F hotter than the breast.</li>
<li>Use the resting time to prepare the gravy, then carve and serve hot!</li>
</ol>
<h2 id="making-the-gravy">Making the Gravy</h2>
<ol>
<li>Scrape all the turkey drippings into a big saucepan and heat on high until the oil starts to bubble (this happens quickly).</li>
<li>Add about 400mL of water and allow the mix to come to a boil again.</li>
<li>Reduce temperature to medium, and slowly add the first package of gravy powder while stirring.</li>
<li>When the mixture comes to a boil again, sprinkle in the second package of gravy powder while stirring vigorously.</li>
<li>Alternate between slowly adding water and flour, while stirring consistently to avoid lumps. After adding water, allow the gravy to return to a boil. If the heat is too low the flour wont cook, and it wil taste floury.</li>
<li>Continue until desired consistency, taste, and volume is achieved. Our gravy typically doubles or triples in volume from the original drippings, water and gravy mix.</li>
</ol>
<figure><img src="/blog/turkey/plate.jpg"
			alt="A plate of thanksgiving food, with a turkey leg, Yorkshire Puddings, broccoli, carrots and potatoes."><figcaption>
			<p>Ready to be topped with gravy!</p>
		</figcaption>
</figure>

<h2 id="notes">Notes</h2>
<h3 id="frozen-turkey">My turkey is still frozen! What do I do?</h3>
<p>Here&rsquo;s what we did one year. Hopefully you have a sink that is large enough to fit an entire turkey.</p>
<ol>
<li>Thoroughly clean out a large sink with soap and rinse. Fill it with water.</li>
<li>Unwrap the turkey, place it on the roasting rack, and then lower it into the sink.</li>
<li>Change the water in the sink every 15-30 minutes until thawed enough</li>
<li>Try to remove giblets as soon as possible. If they are still frozen in the turkey, pour hot water in the cavity to thaw it just to remove them, but don’t allow the warm water to sit for longer than a few minutes.</li>
</ol>
<p>It’s best to continue with prepping the turkey and putting it in the oven as soon as possible after doing this. Remember to clean your sink again afterwards!</p>
<h3 id="oven-explosion">The tempered glass pane of my oven exploded!</h3>
<p>Yup, that happened to us too the first time we made turkey after moving into our new house. I suppose we were either unlucky, or the previous owners hadn&rsquo;t used the oven much&hellip;</p>
<figure><img src="/blog/turkey/glass.jpg"
			alt="The front of an oven, with a pile of tempered glass shards on the floor beneath it."><figcaption>
			<p>Better it shatter out, than in&hellip;</p>
		</figcaption>
</figure>

]]></description>
      </item>
    
      <item>
        <title>Notes on ZorinOS</title>
        <link>https://michael.kafarowski.com/blog/zorin-os-notes/</link>
        <pubDate>Mon, 01 Sep 2025 00:00:00 +0000</pubDate>
        <guid>https://michael.kafarowski.com/blog/zorin-os-notes/</guid>
        <description><![CDATA[<h1>Notes on ZorinOS</h1><p>My personal values and policies towards my online privacy, data sovereignty, and tolerance to advertising have evolved, especially since the beginning of 2025. After nearly 25 years as a Windows user, I decided to switch to an Ubuntu-based distribution called ZorinOS. I&rsquo;ll explain why it improved my life, and how I&rsquo;ve modified it to run better.</p>
<p><strong>Table of Contents</strong>
<nav id="TableOfContents">
  <ul>
    <li><a href="#a-weight-off-my-shoulders">A Weight Off My Shoulders</a></li>
    <li><a href="#quality-of-life-tweaks-for-zorin">Quality of Life Tweaks For Zorin</a>
      <ul>
        <li><a href="#improve-ui-scale">Improve UI Scale</a></li>
        <li><a href="#reduce-trackpad-scroll-speed">Reduce Trackpad Scroll Speed</a></li>
        <li><a href="#fix-garbled-bluetooth-sound">Fix Garbled Bluetooth Sound</a></li>
        <li><a href="#add-gnome-extensionmanager">Add GNOME ExtensionManager</a></li>
      </ul>
    </li>
    <li><a href="#gaming-tweaks-for-zorin">Gaming Tweaks for Zorin</a>
      <ul>
        <li><a href="#using-an-egpu-with-linux-and-a-framework-laptop">Using an eGPU with Linux and a Framework Laptop</a></li>
        <li><a href="#installing-bazzite-alongside-zorin-for-gaming">Installing Bazzite Alongside Zorin for Gaming</a></li>
      </ul>
    </li>
    <li><a href="#conclusion">Conclusion</a></li>
  </ul>
</nav></p>
<figure><img src="/blog/zorin-os-notes/screenshot.webp"
			alt="Obligatory neofetch screenshot"><figcaption>
			<p>Obligatory neofetch screenshot</p>
		</figcaption>
</figure>

<h2 id="a-weight-off-my-shoulders">A Weight Off My Shoulders</h2>
<p>Moving to Linux had a pleasant and unexpected non-technical side-effect. I almost immediately felt a weight I was previously unaware of lift off my shoulders. I think the major reasons for this are that:</p>
<ul>
<li>My system asks me for permission to do things before doing them</li>
<li>I know where all my files are, and where they aren&rsquo;t (e.g. on a cloud I didn&rsquo;t control)</li>
<li>I haven&rsquo;t been bothered by bloatware, an unsolicited AI feature, or an advertisement in the OS.</li>
</ul>
<p><em>This PC</em> feels like <em>My Computer</em> again.</p>
<h2 id="quality-of-life-tweaks-for-zorin">Quality of Life Tweaks For Zorin</h2>
<p>Below is a living document containing the list of tweaks I&rsquo;ve made to my Zorin installation to make it more pleasant to use. <em>Last Updated: September 2025</em>.</p>
<h3 id="improve-ui-scale">Improve UI Scale</h3>
<p>UI elements are either too big at 200% or too small at 100%, but using fractional scaling in GNOME results in blurriness. To make the interface more readable, I instead set resolution scaling to 100% and used <code>gnome-tweaks</code> to change text sizes. Specifically, I increased my interface text to 12pt and set the &ldquo;scaling factor&rdquo; to 1.25. This keeps elements like the task bar and title menus small and out of the way, while making the rest of the interface text large enough to read comfortably.</p>
<p>The scaling factor in tweaks sets the magnitude of the Large Text accessibility option and then enables it. Sometimes the Large Text option interferes with legacy apps, resulting in text that is too big and cut off. I set the Accessibility menu to always stay visible in the taskbar so it can be easily toggled on and off.</p>
<h3 id="reduce-trackpad-scroll-speed">Reduce Trackpad Scroll Speed</h3>
<p>My Framework laptop&rsquo;s two-finger trackpad scrolling was much too sensitive. I installed <a href="https://gitlab.com/warningnonpotablewater/libinput-config" target="_blank" rel="noopener noreferrer">libinput-config</a>
 (which first requires <code>meson</code>, <code>libc-dev</code>, and <code>libinput-dev</code> to build). I followed the readme and created a config file in which I set the <code>scroll-factor</code> to 0.25.</p>
<p>Libinput will eventually add a Lua plugin system that will make libinput-config unnecessary (slated for version 1.29).</p>
<h3 id="fix-garbled-bluetooth-sound">Fix Garbled Bluetooth Sound</h3>
<p>I had issues with bluetooth audio getting corrupted every few seconds, making videos unwatchable. I fixed this by installing the <code>blueman</code> bluetooth manager from the package store and connected to my headphone with that app instead of the system bluetooth interface.</p>
<h3 id="add-gnome-extensionmanager">Add GNOME ExtensionManager</h3>
<p>There are plenty of extensions that you can use with GNOME to modify and add functionality to your desktop environment. The GNOME Extensions website relies on a Firefox plug-in to install the extensions, but Zorin comes with Brave. Installing <code>Extension Manager</code> from the Software app allows you to browse and install extensions without Firefox.</p>
<p>Zorin even recommends it for advanced users on their website: <a href="https://help.zorin.com/docs/system-software/install-third-party-gnome-shell-extensions/" target="_blank" rel="noopener noreferrer">Zorin Docs - Install Third Party GNOME Shell Extensions</a>
.</p>
<p>Here are the extensions I&rsquo;m running:</p>
<ul>
<li>Burn My Windows (Glitch Effect, 500ms) for a neat, subtle flare when opening and closing windows.</li>
<li>Arc Menu and Dash to Panel - Set up to nearly exactly match Zorin&rsquo;s default UI, but I specifically wanted the Framework logo as my &ldquo;start&rdquo; button. Here you can downloaded a <a href="/assets/img/posts/2025-09-01-zorin-os-notes/framework_padded.png">padded</a>
 and <a href="/assets/img/posts/2025-09-01-zorin-os-notes/framework_cropped.png">cropped</a>
 version of that icon.</li>
</ul>
<h2 id="gaming-tweaks-for-zorin">Gaming Tweaks for Zorin</h2>
<h3 id="using-an-egpu-with-linux-and-a-framework-laptop">Using an eGPU with Linux and a Framework Laptop</h3>
<p>Unfortunately, eGPU usability is not seamless when running Zorin or other Linux distros on my system (Framework 13&quot;, 11th Gen Intel, Nvidia RTX3080 in Razer Core X).</p>
<p>If I boot the system with the eGPU attached, the BIOS is unable to find my M.2 drive; this did not occur with a Windows installation. When powering on, I must wait for the green light on the side of the laptop to go out before plugging in the eGPU. The drive is then recognized and the eGPU is usable in the OS. Unfortunately, hot-plugging does not work.</p>
<p>Running a second monitor via the GPU&rsquo;s HDMI port resulted in stutters and poor performance on the monitor and the built-in display. I have to run the HDMI from a Framework HDMI expansion card. Theoretically this will degrade performance, but I have not experienced any notable difference in performance compared to Windows, at least when playing No Mans Sky.</p>
<p>Apps must be explicitly informed to offload graphics tasks to the eGPU. I wrote a bash script with the following contents that allows for easy invocation of the environment variables needed.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;"><code class="language-sh" data-lang="sh"><span style="display:flex;"><span><span style="color:#75715e">#!/bin/bash
</span></span></span><span style="display:flex;"><span>exec env __NV_PRIME_RENDER_OFFLOAD<span style="color:#f92672">=</span><span style="color:#ae81ff">1</span> __GLX_VENDOR_LIBRARY_NAME<span style="color:#f92672">=</span>nvidia “$@”
</span></span></code></pre></div><p>Save this to <code>~/.local/bin/prime-run</code>, then make it executable with <code>chmod +x</code>, and add <code>~/.local/bin</code> to the path by adding the following to <code>.bashrc</code>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;"><code class="language-sh" data-lang="sh"><span style="display:flex;"><span>export PATH<span style="color:#f92672">=</span>”$HOME/.local/bin:$PATH”
</span></span></code></pre></div><p>Now just call <code>prime-run</code> instead of having to type the long variable names.
This can be used in Steam launch options with <code>prime-run %command%</code>.</p>
<h3 id="installing-bazzite-alongside-zorin-for-gaming">Installing Bazzite Alongside Zorin for Gaming</h3>
<p>Actually, I had wiped my drive and installed Bazzite first, after I had further issues with gaming on Zorin. Bazzite did solve many gaming-related issues. I thought it would be my new daily driver distro, but unfortunately, the immutable system and necessity to use exclusively flatpaks turned out to be highly limiting for some of the other work I do. Bazzite also takes a long time to boot, almost 2 minutes, which was irritating.</p>
<p>After installing Bazzite on the whole drive, I resized the main partition and created a partition for Zorin. When I reinstalled Zorin I had to manually specify the new partion as the destination (the installer could not detect the Bazzite installation automatically), then formatted it and set the mount point in the new empty partition.</p>
<p>After installation, I ran <code>ujust regenerate-grub</code>, and the Zorin installation was added to the GRUB boot menu. Now, I boot into Bazzite for gaming, and Zorin for everything else.</p>
<h2 id="conclusion">Conclusion</h2>
<p>Transitioning to Linux required me to learn new things and work around technical challenges. With these hurdles now crossed, I feel a tangible improvement to my overall quality of life in general, and have a new appreciation for how the systems I use daily can have the ability to influence my state of mind.</p>
]]></description>
      </item>
    
      <item>
        <title>Totality - An Eclipse Reflection</title>
        <link>https://michael.kafarowski.com/blog/eclipse/</link>
        <pubDate>Mon, 08 Apr 2024 00:00:00 +0000</pubDate>
        <guid>https://michael.kafarowski.com/blog/eclipse/</guid>
        <description><![CDATA[<h1>Totality - An Eclipse Reflection</h1><p>On April 8th, 2024, the shadow of a solar eclipse made its slow dance across North America.</p>
<p>The path of totality would pass tantalizingly close to my home city of Toronto, but from my home we would &ldquo;only&rdquo; see 99.56% occlusion. 99.56% is so close to 100%. If you were wealthier than 99.5% of Canadians you&rsquo;d have nearly <a href="https://distribution-a617274656661637473.pbo-dpb.ca/20de98fc3f4d93c5213f8d71fbe7cd89ae69cb1899e9cbf2d3ca4d57f18ab25a" target="_blank" rel="noopener noreferrer">$10 million</a>
. But for a solar eclipse, that missing 0.44% <a href="https://xkcd.com/2914/" target="_blank" rel="noopener noreferrer">separates</a>
 a dark afternoon from a once-in-a-lifetime experience. Full, 100% occlusion is required to experience totality - the glowing halo you see always see featured in photos of solar eclipses.</p>
<h2 id="site-selection">Site Selection</h2>
<p>In the weeks leading up to eclipse day, there were dozens of articles about the <a href="https://www.cbc.ca/news/canada/hamilton/solar-eclipse-niagara-falls-1.7159987" target="_blank" rel="noopener noreferrer">chaos and pandemonium</a>
 that would be caused by &ldquo;up to a million people&rdquo; flooding into Kingston or Niagara Falls on eclipse day. Niagara Falls even preemptively declared a state of emergency in anticipation.</p>
<p>My partner Joyce and I watched <a href="https://web.archive.org/web/20240408041830/https://www.pivotalweather.com/eclipse2024/" target="_blank" rel="noopener noreferrer">the forecasts</a>
 carefully. The forecast models turned sour late in the week before the eclipse. First, Niagara started looking cloudy. Just as I was mentally preparing myself to make the 3 hour trek to Kingston, the forecast turned cloudy there too! However, a small sliver of Ontario remained cloud-free - Essex County along Lake Erie.</p>
<figure><img src="/blog/eclipse/weather.jpg"
			alt="A weather map showing cloud cover on the day of the eclipse. Heavy cloud cover is present over the entire path through Ontario, except for Essex County, near Windsor."><figcaption>
			<p>The sliver of clear skies over Essex County looked to have the best conditions in all of Ontario (darker blue indicates heavier cloud cover).</p>
		</figcaption>
</figure>

<p>We selected a few potential areas in advance. Some isolated country roads with a large shoulder and clear views of the sky were our backup plans, but we wanted somewhere nicer we could spend the day. Point Peele was an obvious choice, but we worried that despite an early start, long lines of cars from more local cities like Detroit and Windsor would beat us there.</p>
<p>An equally attractive location was <a href="https://maps.app.goo.gl/VpGPmxupJy92b5yVA" target="_blank" rel="noopener noreferrer">Hillman Marsh</a>
, only about 20 minutes from Point Peele. Its a location well known to local birdwatchers and has a large pond, surrounded by a small woods and marshland.</p>
<p>With access to the same weather information I had, I assumed that the huge crowd of other eclipse hunters originally destined for Niagara and Kingston had turned their sights to Essex County as well. We packed water, snacks and gas and planned for  at least 16 hours of round-trip traffic (1 million is a lot of people, after all).  To mitigate some of the stress of driving there, I leveraged a tool I&rsquo;m lucky to have as a morning person - the ability to wake up and function well at crazy early hours. This was entirely to the chagrin of night owl Joyce, and the opportunity to sleep in the car for three hours didn&rsquo;t seem to make up for the fact that I dragged her out of bed at 4AM.</p>
<h2 id="the-marsh">The Marsh</h2>
<p>Arriving at 7:30, we were the first ones there. It turns out that neither Point Peele nor Hillman Marsh would reach parking lot capacity until 9-10AM, and traffic along the route we took didn&rsquo;t begin building until that time either. So in retrospect, 4AM was probably a bit early. Still, our early start offered us good time to bask in the crisp, fresh morning air and explore the beautiful walking trails around the marsh.</p>
<figure><img src="/blog/eclipse/marsh.jpg"
			alt="A photo of Hillman Marsh, showing a waterway surrounded by trees."><figcaption>
			<p>A local bird-watching hotspot, Hillman Marsh is home to a wide variety of animal and plant life.</p>
		</figcaption>
</figure>

<p>Joyce had prepared us picnic lunches of sandwiches and fruit, which we ate at our selected spot by the pond. We had plenty of space to ourselves - although more people had arrived throughout the morning, the &ldquo;park to parking lot&rdquo; size ratio was so huge that we all could enjoy the space without crowding. It was a very relaxing and peaceful experience. Sitting by the water, I thought &ldquo;we should remember this place and come back here next time&rdquo;. Hmm. That&rsquo;s not quite right, is it? If only eclipses were so frequent.</p>
<h2 id="the-anticipation-grows">The Anticipation Grows</h2>
<p>In preparation for the event I set up my DSLR camera, which I had packed entirely as an afterthought. It wasn&rsquo;t until the previous night that I had researched <a href="https://www.mreclipse.com/SEphoto/SEphoto.html" target="_blank" rel="noopener noreferrer">how to photograph an eclipse</a>
. I didn&rsquo;t have a tripod, so I had to fashion a bipod-like structure out of things I had lying around. After taping the eclipse glasses to the lens for some safe test-shots of the sun, I fine-tuned the &ldquo;infinity&rdquo; focus of my lens as recommended online; a critical adjustment, in fact. I taped my focus and locked my settings so I could spend as little time possible working the camera during totality.</p>
<p>We got our eclipse glasses from Joyce&rsquo;s work. Only a few days before however, we received a <em>caveat emptor</em> email stating that the original vendor had issued a &ldquo;stop sell order&rdquo;, as they were not an authorized reseller&hellip; but that they were &ldquo;probably fine&rdquo;. Amid the <a href="https://aas.org/press/american-astronomical-society-warns-counterfeit-fake-eclipse-glasses" target="_blank" rel="noopener noreferrer">talk of counterfeits</a>
, we had built some pinhole projectors to observe the sun with instead.</p>
<figure><img src="/blog/eclipse/projector.jpg"
			alt="The author, staring into a large box, originally for a turkey roasting pan."><figcaption>
			<p>If it looks stupid but it works, it ain&rsquo;t stupid!</p>
		</figcaption>
</figure>

<p>Mk. 1 - made from a tissue box - wasn&rsquo;t long enough to comfortably focus on the projection screen. Mk. 2 - from the box previously occupied by our Easter turkey roasting pan - worked much better. We looked completely ridiculous but it worked great!</p>
<h2 id="the-experience-begins">The Experience Begins</h2>
<p>The eclipse began at around 2 PM. We turned our pinhole projectors skyward and took quick glances through the sketchy eclipse glasses, watching as the moon took its first bite from the sun. As the occlusion progressed, the temperature dropped quite dramatically. Long-sleeve shirt weather turned into sweater weather, which turned into jacket weather. All the while, the world fell darker and darker, but in an unusual way. On a normal overcast day, the clouds diffuse the light from the sun, which softens or eliminates shadows. This was not the case today, our shadows remained just as sharp but were set against significant darkness. As the sun appeared dimmer and dimmer, our ocular instincts were often subverted - it became difficult not to take accidental unprotected glances at the sun!</p>
<p>About 10 minutes prior to the beginning of totality, silence set in. The background noise of traffic on a nearby highway had ceased. The birds, plentiful and vocal as they were mere minutes prior had stopped their singing. As darkness continued to fall the horizon embraced us with a warm orange glow from all directions.</p>
<p>In the last few moments before totality, I became hyper-aware of my surroundings, owing to the the absolute surrealism of the present experience. It reminded me of what it&rsquo;s like to transition into a lucid dream - you realize that although your eyes were open before, suddenly and strikingly you now can <em>truly see</em>.</p>
<p>We watched through the projection box as the last sliver of sun was consumed by the moon, and then we looked skyward.</p>
<figure><img src="/blog/eclipse/Eclipse.png"
			alt="The April 2024 Solar Eclipse in Totality">
</figure>

<h2 id="totality"><em>Totality.</em></h2>
<p>A hole punched straight through the sky. The darkest void surrounded by a supremely brilliant shimmering halo. Bright beads of light danced around its perimeter. The wispy, high-altitude clouds were no match for the luminance, and they faded away into the night-light sky. Venus glowed faintly nearby. It was surreal - like nothing I had ever seen in my life.</p>
<p>It&rsquo;s difficult to describe how I felt in that moment, and perhaps no words ever fully can. I thought about the rarity of these events, and how awesome it is that the lunar and solar geometry enables such stunning total eclipses. I reflected on the privilege I had to live so close and have the means to travel to see it.</p>
<p>I thought about how I almost didn&rsquo;t travel to see it. My tendency to overthink and plan for the worst means my brain has no difficulty developing compelling reasons (large crowds, getting stranded in traffic, etc) against doing things I actually want to do. Joyce helped me realize it would be worth the risks I had conjured up. Without her encouragement, I would have just watched the sky darken from our balcony.</p>
<p>With this in mind, I reflected on my earlier thoughts about how there would never be a &ldquo;next time&rdquo; for us to return here for another eclipse. 125 seconds was all we had. There are so many incredible things to see in the world. We should count ourselves lucky for all of those that we may choose to experience for longer, experience again, or defer for another time. Eventually, we will all run out of &ldquo;next times&rdquo;, and it will be sooner than we&rsquo;d like.</p>
<p>I cleared a few tears from my eyes and hugged Joyce as we continued to watch. Much too soon, the sun broke free from the edge of the void. Time was up, time to look away.</p>
<p>The chorus of birdsong began again, as if they had awoken from sleep (perhaps they really had). The golden horizon faded away as the sky brightened, first on one side, followed by the other. We stood for a few more moments, quietly processing and burning in the experience to our memories - a memory that will surely last a lifetime.</p>
]]></description>
      </item>
    
      <item>
        <title>Fix The TWSBI ECO Cap Falling Off The Tail</title>
        <link>https://michael.kafarowski.com/blog/fix-twsbi-cap-falling-off/</link>
        <pubDate>Sun, 02 Jul 2023 00:00:00 +0000</pubDate>
        <guid>https://michael.kafarowski.com/blog/fix-twsbi-cap-falling-off/</guid>
        <description><![CDATA[<h1>Fix The TWSBI ECO Cap Falling Off The Tail</h1><h2 id="introduction">Introduction</h2>
<p>After cleaning and reassembling my TWSBI ECO for the first time, I found that the cap wouldn&rsquo;t stay on the tail while writing. It was very loose and would fall off easily. If you find yourself struggling with the same problem you&rsquo;ll learn how to fix it in this article.</p>
<p>If you&rsquo;d like to watch me perform this fix, check out the video I made on this topic. Otherwise read on for the tutorial.</p>
<div style="text-align:center">
    <iframe src="https://www.youtube.com/embed/55DkbtDlL3M" allowfullscreen="" width="70%;" height="300px" frameborder="0"></iframe>
</div>
<h2 id="the-problem">The Problem</h2>
<p>It turns out that proper assembly of the pen isn&rsquo;t intuitive. Like me, you might be tempted to insert the Plunger into the body first, followed by the rest of the parts. Everything would go fine until you try to prop the pen cap on the tail - it won&rsquo;t stay on. You&rsquo;ll find that the Plunger will fully retract before the Tailcap can snug up, leading to a gap between the Tailcap and the Body.</p>
<figure><img src="/blog/fix-twsbi-cap-falling-off/tailcap_gap.jpg"
			alt="A photo of the tail end of a TWSBI ECO. The Plunger is fully retracted and the tailcap is tight, but there&#39;s a 1-2mm gap between the tailcap and the body."><figcaption>
			<p>There&rsquo;s a gap between the tailcap and the body, and the tailcap can&rsquo;t be tightened anymore to close it</p>
		</figcaption>
</figure>

<p>This gap prevents the cap from making contact with the o-ring that holds it when you place it on the tail. Without the snug seal of the o-ring, the cap sits loosely and will fall off during writing.</p>
<h2 id="the-solution">The Solution</h2>
<p>To fix this, you&rsquo;ll need to reassemble your pen in the proper order. I recommend draining and rinsing your pen of ink, otherwise things will get pretty messy.</p>
<figure><img src="/blog/fix-twsbi-cap-falling-off/parts.jpg"
			alt="All parts of the TWSBI ECO labelled. The cap is the round item with the clip. The body is the part of the pen you hold on to. The wrench is a long red rectangle with two fingers on the end that have a slot between them. The Tailcap is the item that you turn when drawing ink in. The Plunger is the piece with the round head and long screw that travels up the body when drawing ink in. The Threaded Collar is the piece that has a thread on each side separated by a ledge. The tail tube is the long cylindrical piece with no threads."><figcaption>
			<p>I just made up most of these names (except &lsquo;wrench&rsquo; - I needed a friend&rsquo;s advice on that one)</p>
		</figcaption>
</figure>

<p>Insert the red TWSBI wrench into the gap between the Tailcap and the Body. If the gap isn&rsquo;t big enough, turn the Tailcap counterclockwise to extend the Plunger. Turn the wrench clockwise until the Threaded Collar and Plunger assembly can be removed.</p>
<p>The trick to properly reassembling the pen is to put together the Plunger/Collar/Tailcap mechanism first. Begin by inserting the Tail Tube into the Threaded Collar. Next, place the Tailcap over top of the Tail Tube and screw it in about halfway. It doesn&rsquo;t have to be exact - we&rsquo;ll probably need to come back and make adjustments shortly.</p>
<p>Insert the Plunger into the other end of the Threaded Collar. Begin retracting the Plunger by rotating the Tailcap. If you&rsquo;re lucky, the Plunger will be fully retracted at the same time that the Tailcap becomes tight against the Collar. This would give you the perfect fit after reassembling. If either the Plunger or the Tailcap still had a gap when the other became tight, you&rsquo;ll need to make an adjustment.</p>
<figure><img src="/blog/fix-twsbi-cap-falling-off/bad_gap.jpg"
			alt="A photo showing the Plunger/Collar/Tailcap assembly. The Tailcap is fully tight and has no gap, but there is a 2mm gap between the collar and the Plunger."><figcaption>
			<p>Here&rsquo;s an example where the Tailcap is fully snug but the Plunger still has some travel left. If you don&rsquo;t have a demonstrator, no one can judge you if leave the gap&hellip; but you&rsquo;ll always be haunted by it.</p>
		</figcaption>
</figure>

<p>Take note of where the gap was, and then extend the Plunger and carefully remove it. If the Plunger had a gap while the Tailcap was tight, you&rsquo;ll need to loosen the Tailcap a few turns. If the Tailcap had a gap while the Plunger was tight, you&rsquo;ll need to tighten the Tailcap a few turns. Once done, carefully re-insert the Plunger and test again.</p>
<p>Once you are happy with the fit, extend the Plunger just enough to give you enough room to fit the red wrench between the Collar and the Tailcap. You can apply a few drops of grease (supplied with your pen) to the Plunger seal if you&rsquo;d like. Insert the Plunger assembly into the Body. Hold the Body and turn the wrench counter-clockwise until it is snug (be careful not to over-tighten).</p>
<p>If all went well, you should be able to fully retract the Plunger, and the cap should stay on the tail firmly.</p>
<figure><img src="/blog/fix-twsbi-cap-falling-off/mission_complete.jpg"
			alt="A photo showing the TWSBI ECO with the cap placed on the tail."><figcaption>
			<p>Mission complete!</p>
		</figcaption>
</figure>

]]></description>
      </item>
    
      <item>
        <title>Version Control for KiCAD PCB Projects</title>
        <link>https://michael.kafarowski.com/blog/version-control-for-pcb-design/</link>
        <pubDate>Fri, 16 Dec 2022 00:00:00 +0000</pubDate>
        <guid>https://michael.kafarowski.com/blog/version-control-for-pcb-design/</guid>
        <description><![CDATA[<h1>Version Control for KiCAD PCB Projects</h1><p>In this tutorial, we&rsquo;ll explore a method for organizing multiple revisions, creating backups, and allowing teams to collaborate effectively on PCB and schematic designs.</p>
<p><strong>Table of Contents</strong>
<nav id="TableOfContents">
  <ul>
    <li><a href="#what-is-version-control">What is Version Control?</a></li>
    <li><a href="#guide-to-setting-up-version-control-for-your-pcb-designs">Guide to Setting Up Version Control for your PCB Designs</a>
      <ul>
        <li><a href="#get-subversion">Get Subversion</a></li>
        <li><a href="#create-and-setup-a-subversion-repository">Create and Setup A Subversion Repository</a></li>
        <li><a href="#revision-tracking">Revision Tracking</a></li>
        <li><a href="#file-locks-and-collaboration">File Locks and Collaboration</a></li>
        <li><a href="#tags-and-production-revisions">Tags and Production Revisions</a></li>
        <li><a href="#branches-for-manufacturing-variants">Branches for Manufacturing Variants</a></li>
      </ul>
    </li>
    <li><a href="#conclusion">Conclusion</a></li>
    <li><a href="#appendix-svn-cli-commands">Appendix: SVN CLI Commands</a></li>
  </ul>
</nav></p>
<h2 id="what-is-version-control">What is Version Control?</h2>
<p>Version control is like a file time machine. As you edit files, you can create checkpoints - known as <em>commits</em> - along the way. Later on, you can travel back in time and return to a previous commit. You can also review who changed a file and what they did.</p>
<p>Why should you use a version control system for your PCB designs? Isn&rsquo;t it enough to archive old PCB revisions in a folder or create new files for each revision?</p>
<p>A version control system provides <strong>structure and organization</strong> to manage perhaps hundreds of revisions. Without the need to create multiple files and manually maintain backups, your project folder remains clean and organized. Production files are conveniently available but are unambiguously separated from development files.</p>
<p>A version control system allows <strong>effective collaboration</strong> by providing mechanisms to prevent data loss when multiple people try editing the same files. Further, any team member can check-out, review and edit the project if they have shared access to a repository.</p>
<p>Using a version control system is <strong>safer</strong> because you only modify your own local copy of the project. The entire history of the project is safely stored elsewhere. If you make a big mistake or corrupt a file, you can easily restore to the last commit. With this safety net, you can afford to take risks to solve circuit or routing problems without worrying that your previous work will be lost.</p>
<p>There are a few different options for version control systems, but I&rsquo;ve found Subversion (SVN) to be useful and practical in a production environment, particularly because of its ability to enforce locks on files - we&rsquo;ll learn more about these shortly.</p>
<h2 id="guide-to-setting-up-version-control-for-your-pcb-designs">Guide to Setting Up Version Control for your PCB Designs</h2>
<h3 id="get-subversion">Get Subversion</h3>
<p>We&rsquo;ll take a look at an example design process for a simple circuit and PCB using KiCAD, a powerful suite for schematic and PCB design. I&rsquo;m assuming you have some background in KiCAD and have it already installed. To create and manage our SVN repositories we&rsquo;ll use TortoiseSVN, a common GUI-based client for Windows that integrates nicely with the Explorer right-click menu. If you are using Mac or Linux, I&rsquo;ve listed some SVN clients that appear similar (but I haven&rsquo;t tested).</p>
<ul>
<li>Download TortoiseSVN for Windows: <a href="https://tortoisesvn.net/downloads.html" target="_blank" rel="noopener noreferrer">https://tortoisesvn.net/downloads.html</a>
</li>
<li>Download SmartSVN for Mac: <a href="https://www.smartsvn.com/" target="_blank" rel="noopener noreferrer">https://www.smartsvn.com/</a>
</li>
<li>Download RabbitVCS for Linux: <a href="http://wiki.rabbitvcs.org/wiki/download" target="_blank" rel="noopener noreferrer">http://wiki.rabbitvcs.org/wiki/download</a>
</li>
</ul>
<p>You can follow along with the command line - see the <a href="#appendix-svn-cli-commands">Appendix</a>
 at the end of this article. You&rsquo;ll need to first install SVN from Homebrew (Mac), your package manager (Linux), or by enabling the command line tools during TortoiseSVN installation (Windows).</p>
<h3 id="create-and-setup-a-subversion-repository">Create and Setup A Subversion Repository</h3>
<p>We&rsquo;ll begin by creating a new SVN Repository. A repository is the database that stores information about file data, revision history, and various &ldquo;rules&rdquo; for the project. Make a new empty folder and name it <code>BlinkyProject</code>, then right-click the folder and click TortoiseSVN -&gt; Create Repository Here. When prompted, click the &ldquo;Create Folder Structure&rdquo; button, then click OK to close all windows.</p>
<p>This repository folder only contains the raw database and not the actual files of your project, so don&rsquo;t try and modify the files. Using the repository database, TortoiseSVN can create a local &ldquo;working copy&rdquo; folder. The working copy folder contains the actual project files. When project files are changed and committed, the working copy folder syncs information about the changes to the repository database.</p>
<p>Creating the working copy is known as &ldquo;Checking Out&rdquo; the repository. Right click on the repository folder, then click SVN Checkout. Change the working copy path to somewhere on your Desktop or Documents folder, if you wish. You can leave all the other settings the same and then click OK.</p>
<p>If you navigate into the working copy folder, you&rsquo;ll see three folders - trunk, tags, and branches. Here&rsquo;s briefly what we will use each folder for (don&rsquo;t worry, each will be explained in the sections below).</p>
<ul>
<li><strong>trunk:</strong> this contains all the living, in-development project files. Whenever you make fixes or edits to a schematic or board file, you&rsquo;ll change the files in this directory.</li>
<li><strong>tags:</strong> this folder will contain &ldquo;snapshots&rdquo; of your project taken when you generate Gerber files for production.</li>
<li><strong>branches:</strong> miscellaneous boards based on tagged snapshots that may be customized, e.g for panelling.</li>
</ul>
<p>There&rsquo;s nothing special about these folders or their names, they are just SVN convention. You could rename them to something like &ldquo;development&rdquo;, &ldquo;production&rdquo;, and &ldquo;variants&rdquo; if you wanted.</p>
<h3 id="revision-tracking">Revision Tracking</h3>
<p>Let&rsquo;s explore SVN version tracking. Create a new KiCAD project inside the trunk folder, unchecking the box that says &ldquo;Create a new Folder for this Project&rdquo; so that project files are placed directly in the trunk. Open the .kicad_sch file and draw a simple circuit, such as a battery, resistor, and LED.</p>
<figure><img src="/blog/version-control-for-pcb-design/single_led.jpg"
			alt="A screenshot of KiCAD showing a circuit with a battery, resistor and an LED."><figcaption>
			<p>Our humble LED circuit</p>
		</figcaption>
</figure>

<p>Our masterpiece is complete! Let&rsquo;s commit our changes. Save and close the schematic, then right-click into the trunk folder, then click SVN Commit. Normally you would enter a brief message about your changes into the commit description section. For this commit write &ldquo;Initial Commit&rdquo;. Check the boxes next to the .kicad_pro, .kicad_pcb, and the .kicad_sch files in the area below to ensure they are included. Click OK, and the data will be sent to the repository database.</p>
<figure><img src="/blog/version-control-for-pcb-design/initial_commit.jpg"
			alt="A screenshot of the SVN commit window."><figcaption>
			<p>Making our first commit to the SVN Repository</p>
		</figcaption>
</figure>

<p>We didn&rsquo;t check off the backup folder or the .kicad_prl file. This means that any changes to the files in the backup folder or the .kicad_prl file will not be captured during commits, nor will they appear when anyone else checks out the SVN repository. This is okay - we don&rsquo;t need to store their changes in the repository because the .kicad_prl will regenerate, and the backups are only for short term use if KiCAD crashes and we forgot to save. Our commits will take care of backing up major changes.</p>
<p>Let&rsquo;s say our requirements changed and we actually need two LEDs. Update the circuit now to add a second resistor and LED and when finished, save and close the file. Commit again, as described above.</p>
<figure><img src="/blog/version-control-for-pcb-design/two_leds.jpg"
			alt="A screenshot of KiCAD showing a circuit with a battery, two resistors and LEDs."><figcaption>
			<p>Gasp, a second LED?!</p>
		</figcaption>
</figure>

<p>At any time, we can review the entire history of our project by right-clicking the folder of interest, then clicking TortoiseSVN -&gt; Show Log. By right-clicking a revision entry and clicking Browse Repository, you can open the files as they appeared at that point in history. Try opening the repository of revision 2 and viewing the .kicad_sch file. You should only see one LED present. If you would like to revert a file to how it appeared in a previous revision you can click the file, then right-click the revision and click Revert to Revision. If you open the schematic file from the file explorer, you&rsquo;ll see the file is back to one LED.</p>
<figure><img src="/blog/version-control-for-pcb-design/version_history.jpg"
			alt="A screenshot of TortoiseSVN&#39;s version history screen."><figcaption>
			<p>A list of all the changes made to the trunk directory.</p>
		</figcaption>
</figure>

<p>Commits cost nothing, so it&rsquo;s better to commit more often than less, creating checkpoints as you work. If you mess up and don&rsquo;t know how to get back to your original state or you ran out of undos, you can always restore your last known-good commit.</p>
<p>When committing, remember to write descriptive change messages because - as we&rsquo;ll soon see - trying to figure out what changes were made based on file differences alone is a major hassle.</p>
<h3 id="file-locks-and-collaboration">File Locks and Collaboration</h3>
<p>When multiple software developers work on the same file simultaneously, they can merge their changes together later. If the changes were in different sections of the file, merging is easy - both changes are incorporated. When multiple authors change the same lines however, a merge conflict occurs but can be resolved by rewriting the section of code in a way that maintains each change as required.</p>
<p>As PCB developers, we lack the critical tool that software developers take for granted here - the ability to read file differences to comprehend changes. To illustrate, right-click the .kicad_sch file and click TortoiseSVN -&gt; Diff with Previous Version. Notice how a simple change - adding a second LED and resistor - created a file difference that is almost incomprehensible.</p>
<figure><img src="/blog/version-control-for-pcb-design/file_diffs.jpg"
			alt="A screenshot of TortoiseSVN&#39;s diff screen showing a lot of changed lines between revisions."><figcaption>
			<p>I was worried such a trivial change wouldn&rsquo;t cause a diff convoluted enough to demonstrate my point, but I had no reason to fear. Unless I stared at it too long.</p>
		</figcaption>
</figure>

<p>If you are working on a project alone and on a single computer, things are simple - you know that only you are editing any particular file at a time, so no merges are required. However, if you are collaborating with others and multiple people try editing the file, the result is effectively &lsquo;all or nothing&rsquo;. Because merging multiple changes in the traditional way is impractical, you could be forced to keep one version, and discard the other. I hope you&rsquo;re good at rock-paper-scissors.</p>
<p>Luckily SVN has a tool to prevent this situation - enforceable file locks. When you enforce a lock on a file, it becomes read-only by default. You must &ldquo;take&rdquo; the lock before you can edit the file. If another collaborator has already taken the lock, the file will remain read-only. Otherwise the file becomes exclusively yours to edit. When you commit, the lock is (optionally) released, and others can work on it again.</p>
<p>I recommend enforcing locks on the .kicad_sch and .kicad_pcb files. Taking a lock on one won&rsquo;t automatically take a lock on the other, which is fine because in most cases the PCB and schematic can be safely edited simultaneously by different people. To enforce a lock, select the .kicad_sch and the .kicad_pcb file, then right-click and click TortoiseSVN -&gt; Properties. Click the arrow next to New, then click Needs-Lock, and check Locking Required, finally click OK to close all windows. Right-click and click SVN Commit to push the property change to the repository.</p>
<figure><img src="/blog/version-control-for-pcb-design/add_needs_lock.jpg"
			alt="A screenshot of TortoiseSVN&#39;s properties screen with the Needs Lock option highlighted."><figcaption>
			<p>Enforcing locks on the .kicad_pcb and .kicad_sch files will prevent other collaborators from opening the file at the same time, reducing the number of arguments about who has to do all their work over again.</p>
		</figcaption>
</figure>

<p>Notice now that if you open the .kicad_sch file, KiCAD now informs you that it is read-only. Close the file, then right-click it and select the new option SVN Get Lock. Enter a message - this will appear to anyone else that tries to take a lock on the same file - then click OK.</p>
<figure><img src="/blog/version-control-for-pcb-design/read_only.jpg"
			alt="A screenshot of the &#39;Schematic is Read-Only&#39; banner in KiCAD."><figcaption>
			<p>It&rsquo;s helpful to notice this before starting to make changes to the file, but if you don&rsquo;t KiCAD will still let you save without forcing you to re-open the file if you take the lock later.</p>
		</figcaption>
</figure>

<p>Well, well! The requirements changed again. You managed to take the lock before Bob (you want to impress the senior designer), so the file is yours to edit. Add a third LED, then finish the design by annotating and assigning footprints.</p>
<p>We&rsquo;ll create the PCB now. Take a lock on the .kicad_pcb file, then open it and arrange the footprints, then connect them with traces and draw a closed shape around them on the EdgeCuts layer. Once you are happy, save and close the files, and commit them.</p>
<figure><img src="/blog/version-control-for-pcb-design/blinky_circuit.jpg"
			alt="A screenshot of the completed PCB design in KiCAD"><figcaption>
			<p>One of my best :')</p>
		</figcaption>
</figure>

<p>In the commit message window, you&rsquo;ll notice an checkbox near the bottom left that says &ldquo;Keep Locks&rdquo;. If unchecked, the lock will be released after committing, but check the box if you plan to make more commits.</p>
<p>In rare cases, such as when Bob takes a lock but then goes on vacation (again) you can &ldquo;steal&rdquo; the lock. The lock transfers from Bob to you, but now you&rsquo;ll have to figure out what happens when he returns and realizes he can&rsquo;t commit his changes.</p>
<h3 id="tags-and-production-revisions">Tags and Production Revisions</h3>
<p>One of the worst feelings is when your board arrives in the mail and after trying to debug it you realize the schematic or PCB files on your computer don&rsquo;t quite match up. Perhaps you accidentally submitted an old Gerber file, or you made changes to the design files since submitting it for manufacturing. Even if you can access the Gerber files sent to the fab, they are of limited use because they lack any schematic or part information.</p>
<p>With version control, we can commit before generating Gerbers, then always be able to return to that revision for debugging purposes in the future. However, it can be a pain to switch back and fourth between revisions, so we can use an SVN tag to archive a copy of the production run directly in the repository and always have it available for review. Having an official &ldquo;snapshotted&rdquo; copy for each board version produced is useful during board bring-up and debugging, RMA repairs, and may be helpful for design traceability and compliance with regulations.</p>
<p>Our Blinky board is ready to be sent for fabrication. Right click on the trunk folder and click TortoiseSVN -&gt; Branch/Tag. Change the &lsquo;To path&rsquo; option to &ldquo;/tags/V1&rdquo;, and enter a message such as &ldquo;snapshot production revision V1&rdquo;. The tag has now been created, but you have to right-click the tags folder and click SVN Update for the new files to appear.</p>
<p>We will now generate Gerber files directly from the tag instead of the trunk. Having a Gerber folder alongside the development files in the trunk is dangerous - they don&rsquo;t automatically update with the design and you can easily forget to regenerate them, leaving them out-of-date when you go to production. Open the .kicad_pcb file and click File -&gt; Plot. Enter a path for the Gerbers, then click Plot. Click Generate Drill Files, then click Generate Drill File in the new window. Once you&rsquo;ve reviewed the Gerber files with GerbView, run SVN Commit once more, making sure to check the new Gerber folder and the new Gerber files to include them. Write a commit message, such as &lsquo;Create production files for revision V1&rsquo;, then click Commit.</p>
<figure><img src="/blog/version-control-for-pcb-design/commit_to_tag.jpg"
			alt="A TortoiseSVN warning message informing me that I&#39;m trying to commit to a tag folder"><figcaption>
			<p>I&rsquo;m a sign, not a cop.</p>
		</figcaption>
</figure>

<p>At this point TortoiseSVN will give you a warning about committing to a tag. By SVN convention, tags are read-only. We break this rule once to create production files, so click Commit now. If you see this warning at any other time take pause, as you may be trying to edit the tag folder instead of the trunk by mistake.</p>
<p>Now you are ready to submit the Gerber files to your PCB fabrication house of choice! At any point in the future, you can easily browse the files in this tag, which contains a guaranteed 1-to-1 link between the Gerber files and the schematic and pcb design files.</p>
<h3 id="branches-for-manufacturing-variants">Branches for Manufacturing Variants</h3>
<p>What if you need to panelize your boards before sending them for production? Panelizing boards often requires fancy PCB or Gerber editing, so it doesn&rsquo;t really fit well with either the production revisions in the tags folder or the normal development files in the trunk folder. I recommend you use the branches folder to turn the production revision into a panelized board. Inside the branches folder create a new folder. Inside, create an empty .kicad_pcb and open it in standalone mode. Now you can append the board file from a tag and turn it into a panelized design.</p>
<h2 id="conclusion">Conclusion</h2>
<p>In this article we&rsquo;ve explored how to use the Subversion version control software with a KiCAD PCB project. After setting up the repository, we made some changes to the PCB and commit and saw how SVN tracks file history, and how you could review files as they appeared in the past. We also saw how SVN&rsquo;s enforced file locks can help prevent conflicts or data loss if two people try to work on the same file at once. Finally, we took a snapshot of our project and created a tag to represent a production-ready revision, from which we derived the Gerber files.</p>
<p>Thanks for reading, and I hope you found this tutorial useful! Please feel free to reach out to me on the social links below if you have any comments or questions.</p>
<h2 id="appendix-svn-cli-commands">Appendix: SVN CLI Commands</h2>
<table>
	<thead>
			<tr>
					<th>Operation</th>
					<th>Command</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td>Create a Repository</td>
					<td><code>svnadmin create BlinkyProject</code></td>
			</tr>
			<tr>
					<td>Checkout Repository</td>
					<td><code>svn checkout file:///path/to/Project ./path/to/WorkingCopy</code></td>
			</tr>
			<tr>
					<td>Manually Create Folder Structure</td>
					<td><code>cd BlinkyProjectWorkingCopy</code>; <br> <code>mkdir tags branches trunk</code></td>
			</tr>
			<tr>
					<td>Stage Folders For Commit</td>
					<td><code>svn add . --force</code></td>
			</tr>
			<tr>
					<td>Commit Folder Structure</td>
					<td><code>svn commit -m &quot;Add folder structure&quot;</code></td>
			</tr>
			<tr>
					<td>Add KiCAD Project Files</td>
					<td><code>cd trunk</code> <br> <code>svn add *.kicad_pro *.kicad_sch *.kicad_pcb</code></td>
			</tr>
			<tr>
					<td>Commit Single LED Circuit</td>
					<td><code>svn commit -m &quot;initial commit&quot;</code></td>
			</tr>
			<tr>
					<td>Commit Two LED Circuit</td>
					<td><code>svn commit -m &quot;add second LED&quot;</code></td>
			</tr>
			<tr>
					<td>Review SVN History</td>
					<td><code>svn update</code> <br> <code>svn log</code></td>
			</tr>
			<tr>
					<td>Return File to Earlier Revision</td>
					<td><code>svn merge -r (current rev):(past rev) Blinky.kicad_sch</code> <br> Commit: <code>svn commit -m &quot;reverted to older revision&quot;</code> <br> OR <br> Return to latest: <code>svn revert Blinky.kicad_sch</code></td>
			</tr>
			<tr>
					<td>Add Needs-Lock Property</td>
					<td><code>svn propset svn:needs-lock '*' Blinky.kicad_pcb Blinky.kicad_sch</code></td>
			</tr>
			<tr>
					<td>Commit Properties</td>
					<td><code>svn commit -m &quot;Add needs lock property&quot;</code></td>
			</tr>
			<tr>
					<td>Take Lock</td>
					<td><code>svn lock Blinky.kicad_sch</code></td>
			</tr>
			<tr>
					<td>Commit third LED</td>
					<td><code>svn commit -m &quot;Add another LED&quot;</code></td>
			</tr>
			<tr>
					<td>Create V1 Tag</td>
					<td><code>svn copy . ../tags/V1</code> <br> <code>svn commit -m &quot;Create V1 tag&quot;</code></td>
			</tr>
			<tr>
					<td>Add Gerbers and Commit</td>
					<td><code>svn add Gerbers</code> <br> <code>svn commit -m &quot;Create production files for V1&quot;</code></td>
			</tr>
	</tbody>
</table>
]]></description>
      </item>
    
      <item>
        <title>Unit Testing Your Embedded Code Directly In STM32CubeIDE</title>
        <link>https://michael.kafarowski.com/blog/unit-testing-with-stm32cubeide/</link>
        <pubDate>Sun, 31 Jul 2022 00:00:00 +0000</pubDate>
        <guid>https://michael.kafarowski.com/blog/unit-testing-with-stm32cubeide/</guid>
        <description><![CDATA[<h1>Unit Testing Your Embedded Code Directly In STM32CubeIDE</h1><h2 id="introduction">Introduction</h2>
<p>In this tutorial we&rsquo;ll learn about how to make native unit testing easy for your embedded STM32 projects. We&rsquo;ll start with a default installation of STM32CubeIDE - ST&rsquo;s Cube-flavored distribution of Eclipse, and by the end of this article, you can begin writing unit tests or performing Test Driven Development on your STM32 project code directly in STM32CubeIDE.</p>
<p><strong>Table of Contents</strong>
<nav id="TableOfContents">
  <ul>
    <li><a href="#introduction">Introduction</a></li>
    <li><a href="#benefits-of-native-unit-testing-for-embedded-systems">Benefits of Native Unit Testing For Embedded Systems</a></li>
    <li><a href="#guide-to-setting-up-native-unit-testing-with-stm32cubeide">Guide To Setting Up Native Unit Testing With STM32CubeIDE</a>
      <ul>
        <li><a href="#setting-up-the-local-compiler">Setting Up The Local Compiler</a></li>
        <li><a href="#creating-the-donor-project">Creating the Donor Project</a></li>
        <li><a href="#integrating-the-test-configuration-from-the-donor-project">Integrating the Test Configuration From the Donor Project</a></li>
        <li><a href="#setting-up-the-unit-test-configuration">Setting Up The Unit Test Configuration</a></li>
        <li><a href="#building-and-running-tests">Building And Running Tests</a></li>
        <li><a href="#cleaning-up-and-next-steps">Cleaning Up and Next Steps</a></li>
      </ul>
    </li>
    <li><a href="#fun-extras">Fun Extras!</a>
      <ul>
        <li><a href="#use-the-cc-unit-testing-plugin">Use the C/C++ Unit Testing Plugin</a></li>
        <li><a href="#regular-testing-with-a-key-combination">Regular Testing With A Key Combination</a></li>
      </ul>
    </li>
    <li><a href="#conclusion">Conclusion</a></li>
  </ul>
</nav></p>
<h2 id="benefits-of-native-unit-testing-for-embedded-systems">Benefits of Native Unit Testing For Embedded Systems</h2>
<p>Why bother with unit testing on the development machine? Aren&rsquo;t printf&rsquo;s, debuggers, and on-target self-tests good enough?</p>
<p>The first advantage of native unit testing is <strong>speed</strong>. Cross-compilation is slower than native compilation, and flashing times increase as your codebase gets larger. Furthermore, in order to test a specific function you might need to manually bring the system into a particular state, set breakpoints, and examine outputs in the debugger. On an embedded system I worked on this build-flash-debug cycle could take between three and five minutes each time! Imagine realize you made a typo or a copy-paste mistake on the first line of the function and now you need to wait all over again.</p>
<p>Testing on target with the debugger is often not <strong>repeatable</strong>. It can be impractical to place the system in certain states, especially when dealing with edge-cases. With unit tests and loosely coupled code, you can craft isolated environments where you can initialize the system, inject specific inputs and review how the code reacts. You can inject rare or erroneous inputs that the real system may only seldom encounter. Every subsequent run will inject the same inputs and is not affected by any other tests.</p>
<p>Developing a system that can be tested on a native machine also <strong>improves your code</strong> by encouraging you to write in a loosely coupled and modular manner. In most cases, your unit testing won&rsquo;t involve a full emulation of an RTOS or your microcontroller&rsquo;s registers. This is a good thing - if the majority of your application code doesn&rsquo;t rely on the specifics of those layers, you can more easily port the system to a new RTOS or microcontroller in the future.</p>
<p>The final benefit - it&rsquo;s <strong>fun</strong>. It&rsquo;s extremely satisfying to design code that snaps together like Lego, builds at twice the speed as the target compiler, and then runs hundreds of test cases in milliseconds. Having test cases reduces the anxiety of adding new features or refactoring, and the increased development speed reduces the anxiety of your looming deadlines.</p>
<h2 id="guide-to-setting-up-native-unit-testing-with-stm32cubeide">Guide To Setting Up Native Unit Testing With STM32CubeIDE</h2>
<p>This guide assumes you are working on Windows, and have STM32CubeIDE installed. If not, you can download it from ST&rsquo;s website at <a href="https://www.st.com/en/development-tools/stm32cubeide.html" target="_blank" rel="noopener noreferrer">https://www.st.com/en/development-tools/stm32cubeide.html</a>
.</p>
<h3 id="setting-up-the-local-compiler">Setting Up The Local Compiler</h3>
<p>The easiest way to add a local unit testing configuration to an STM32 project is to create a native C/C++ project and use it as a template to create a build configuration in the STM32 project. We will use MSYS2&rsquo;s package manager to download and install the MinGW64 C/C++ toolchain and GoogleTest libraries. If you already have a native C/C++ development environment installed, you can skip this section and download the GoogleTest library manually from your native toolchain&rsquo;s repository.</p>
<p>Download and install MSYS2 from <a href="https://www.msys2.org/" target="_blank" rel="noopener noreferrer">https://www.msys2.org/</a>
. When installed run the MSYS2 shell (if it doesn&rsquo;t open after installation, you can find it by searching &ldquo;MSYS2 MSYS&rdquo; in the Start Menu). Run the command <code>pacman -Syu</code> , which will start the first of two stages required to bring the package manager up to date. Allow the package manager to install the packages. Between stages, the terminal window will prompt you to close and manually reopen the terminal by clicking on the MSYS2 MSYS start menu or desktop icon. Complete the setup by running <code>pacman -Syu</code> a second time and accepting the package download when prompted.</p>
<p>Once MSYS2 is fully updated, you can begin installing the required development packages. Run the following command to install basic unix-like commands, the MinGW compiler and the GoogleTest libraries.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;"><code class="language-sh" data-lang="sh"><span style="display:flex;"><span>pacman -S base-devel mingw-w64-x86_64-toolchain mingw-w64-x86_64-gtest
</span></span></code></pre></div><p>Other packages and libraries you want can be installed later and will be made automatically available to the compiler.</p>
<h3 id="creating-the-donor-project">Creating the Donor Project</h3>
<p>Open STM32CubeIDE into a new or existing workspace.</p>
<p>From File→New→C/C++ Project, create a new C++ Managed Build project. On the next page, name it TestTemplate, select the Hello World C++ project type and use the MinGW GCC toolchain. The hello world project will let us make sure that our compiler is working;</p>
<figure><img src="/blog/unit-testing-with-stm32cubeide/ProjectCreateB.png">
</figure>

<p>Once created, verify that the project builds properly by right clicking the project in the Project Explorer and clicking Build Project. The output should appear as below.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;"><code class="language-sh" data-lang="sh"><span style="display:flex;"><span>21:07:39 **** Rebuild of configuration Debug <span style="color:#66d9ef">for</span> project TestTemplate ****
</span></span><span style="display:flex;"><span>Info: Internal Builder is used <span style="color:#66d9ef">for</span> build
</span></span><span style="display:flex;"><span>g++ -O0 -g3 -Wall -c -fmessage-length<span style="color:#f92672">=</span><span style="color:#ae81ff">0</span> -o <span style="color:#e6db74">&#34;src\\TestTemplate.o&#34;</span> <span style="color:#e6db74">&#34;..\\src\\TestTemplate.cpp&#34;</span> 
</span></span><span style="display:flex;"><span>g++ -o TestTemplate.exe <span style="color:#e6db74">&#34;src\\TestTemplate.o&#34;</span> 
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>21:07:41 Build Finished. <span style="color:#ae81ff">0</span> errors, <span style="color:#ae81ff">0</span> warnings. <span style="color:#f92672">(</span>took 1s.647ms<span style="color:#f92672">)</span>
</span></span></code></pre></div><p>Finally, click the dropdown next to the run icon in the menu bar and click the local C++ executable option.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;"><code class="language-sh" data-lang="sh"><span style="display:flex;"><span>!!!Hello World!!!
</span></span></code></pre></div><p>We are now ready to use this project as a template to build local C++ unit tests and files.</p>
<h3 id="integrating-the-test-configuration-from-the-donor-project">Integrating the Test Configuration From the Donor Project</h3>
<p>Create or import an STM32 Project you would like to write unit tests for. In the filesystem root (in Explorer, not STM32CubeIDE), create two folders - one called Common and another called Testing. The Common folder is for unit testable (i.e. modular, loose-coupled, and cross platform) code and the Testing folder for the unit tests, doubles and mock objects. In STM32CubeIDE refresh the project so that the new folders appear - if they still don&rsquo;t appear after a refresh, you may have to drag them from explorer into the project hierarchy and link them manually (this is the case with most TouchGFX application template projects).</p>
<p>Double check that the STM32 project and the TestTemplate project <strong>are both in the same workspace</strong>. Right click the STM32 project in the Project Explorer and then click Build Configurations → Manage&hellip; → New.</p>
<p>Create a new configuration with the name UnitTest, and in the &ldquo;Copy settings from&rdquo; section, select &ldquo;Import from projects&rdquo; then select the <strong>debug</strong> configuration of the TestTemplate project (this way  the unit tests will have maximum debugging and no optimizations by default).</p>
<figure><img src="/blog/unit-testing-with-stm32cubeide/NewConfiguration.png">
</figure>

<p>Click OK. Back in the Manage Configurations Window, select the new UnitTest configuration and click Set Active, then click OK.</p>
<h3 id="setting-up-the-unit-test-configuration">Setting Up The Unit Test Configuration</h3>
<p>Right click the STM32 project and click Properties, then in the sidebar select C/C++ General → Paths and Symbols. Ensure that the UnitTests configuration is selected at the top of the dialog box. In the Includes tab, click Add, and then add include paths for <code>Common</code> (apply to all languages and all configurations) and <code>Testing</code> (apply to all languages only).</p>
<figure><img src="/blog/unit-testing-with-stm32cubeide/IncludeTab.png">
</figure>

<p>In the Libraries tab, add entries for <code>gtest</code> and <code>gmock</code>.</p>
<figure><img src="/blog/unit-testing-with-stm32cubeide/LibraryTab2.png">
</figure>

<p>In the Source Location tab, remove the (STM32ProjectName)/src filter, and add a filter for the <code>Common</code> and <code>Testing</code> folders.</p>
<figure><img src="/blog/unit-testing-with-stm32cubeide/SourceFilter.png">
</figure>

<p>Click OK to close the windows.</p>
<h3 id="building-and-running-tests">Building And Running Tests</h3>
<p>In the Common folder, we&rsquo;ll create a header-only class that could represent loosely coupled code that is also used in our STM32 project.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1</span><span><span style="color:#75715e">/* Common/TrivialClass.hpp */</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">2</span><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">TrivialClass</span> {
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3</span><span><span style="color:#66d9ef">public</span><span style="color:#f92672">:</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">4</span><span>	<span style="color:#66d9ef">bool</span> returnsTrue() { <span style="color:#66d9ef">return</span> true; }
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">5</span><span>	<span style="color:#66d9ef">bool</span> <span style="color:#a6e22e">returnsFalse</span>() { <span style="color:#66d9ef">return</span> false; }
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">6</span><span>};
</span></span></code></pre></div><p>In the Testings/ folder create a file called test_main.cpp, and copy the code below. The main method sets up the GoogleTest framework, and runs a trivial passing and failing test.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1</span><span><span style="color:#75715e">/* Testing/test_main.cpp */</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2</span><span><span style="color:#75715e">#include</span> <span style="color:#75715e">&#34;gtest/gtest.h&#34;</span><span style="color:#75715e">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3</span><span><span style="color:#75715e">#include</span> <span style="color:#75715e">&#34;TrivialClass.hpp&#34;</span><span style="color:#75715e">
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4</span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5</span><span><span style="color:#66d9ef">int</span> <span style="color:#a6e22e">main</span>(<span style="color:#66d9ef">int</span> ac, <span style="color:#66d9ef">char</span><span style="color:#f92672">*</span> av[])
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6</span><span>{
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7</span><span>  testing<span style="color:#f92672">::</span>InitGoogleTest(<span style="color:#f92672">&amp;</span>ac, av);
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8</span><span>  <span style="color:#66d9ef">return</span> RUN_ALL_TESTS();
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9</span><span>}
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10</span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">11</span><span>TEST(BasicTest, PassingTest)
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">12</span><span>{
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">13</span><span>	TrivialClass t;
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">14</span><span>	EXPECT_TRUE(t.returnsTrue());
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">15</span><span>}
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">16</span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">17</span><span>TEST(BasicTest, FailingTest)
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">18</span><span>{
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">19</span><span>	TrivialClass t;
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">20</span><span>	EXPECT_TRUE(t.returnsFalse());
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">21</span><span>}
</span></span></code></pre></div><p>Right click the STM32 project and click Build Project. Assuming the build passes, right click the project again → Run As → Run Configurations. Highlight C/C++ Application and then tap the New Configuration button. Leave the settings as default and click Run.</p>
<figure><img src="/blog/unit-testing-with-stm32cubeide/RunConfiguration.png">
</figure>

<p>If all goes well, you should see the following in the console:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:2;-o-tab-size:2;tab-size:2;-webkit-text-size-adjust:none;"><code class="language-sh" data-lang="sh"><span style="display:flex;"><span><span style="color:#f92672">[==========]</span> Running <span style="color:#ae81ff">2</span> tests from <span style="color:#ae81ff">1</span> test suite.
</span></span><span style="display:flex;"><span><span style="color:#f92672">[</span>----------<span style="color:#f92672">]</span> Global test environment set-up.
</span></span><span style="display:flex;"><span><span style="color:#f92672">[</span>----------<span style="color:#f92672">]</span> <span style="color:#ae81ff">2</span> tests from BasicTest
</span></span><span style="display:flex;"><span><span style="color:#f92672">[</span> RUN      <span style="color:#f92672">]</span> BasicTest.PassingTest
</span></span><span style="display:flex;"><span><span style="color:#f92672">[</span>       OK <span style="color:#f92672">]</span> BasicTest.PassingTest <span style="color:#f92672">(</span><span style="color:#ae81ff">0</span> ms<span style="color:#f92672">)</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">[</span> RUN      <span style="color:#f92672">]</span> BasicTest.FailingTest
</span></span><span style="display:flex;"><span>../Testing/tests/test_main.cpp:19: Failure
</span></span><span style="display:flex;"><span>Value of: t.returnsFalse<span style="color:#f92672">()</span>
</span></span><span style="display:flex;"><span>  Actual: false
</span></span><span style="display:flex;"><span>Expected: true
</span></span><span style="display:flex;"><span><span style="color:#f92672">[</span>  FAILED  <span style="color:#f92672">]</span> BasicTest.FailingTest <span style="color:#f92672">(</span><span style="color:#ae81ff">0</span> ms<span style="color:#f92672">)</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">[</span>----------<span style="color:#f92672">]</span> <span style="color:#ae81ff">2</span> tests from BasicTest <span style="color:#f92672">(</span><span style="color:#ae81ff">0</span> ms total<span style="color:#f92672">)</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">[</span>----------<span style="color:#f92672">]</span> Global test environment tear-down
</span></span><span style="display:flex;"><span><span style="color:#f92672">[==========]</span> <span style="color:#ae81ff">2</span> tests from <span style="color:#ae81ff">1</span> test suite ran. <span style="color:#f92672">(</span><span style="color:#ae81ff">0</span> ms total<span style="color:#f92672">)</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">[</span>  PASSED  <span style="color:#f92672">]</span> <span style="color:#ae81ff">1</span> test.
</span></span><span style="display:flex;"><span><span style="color:#f92672">[</span>  FAILED  <span style="color:#f92672">]</span> <span style="color:#ae81ff">1</span> test, listed below:
</span></span><span style="display:flex;"><span><span style="color:#f92672">[</span>  FAILED  <span style="color:#f92672">]</span> BasicTest.FailingTest
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#ae81ff">1</span> FAILED TEST
</span></span></code></pre></div><p>Because we deliberately wrote a passing test and a failing test, this is the output we expect.</p>
<p>If you&rsquo;d like, you can try running the program using the debugger by clicking the dropdown beside the debugger icon in the menu bar and selecting the UnitTest executable.</p>
<p>Congratulations, you&rsquo;re ready to begin unit testing or test driving your code right in STM32CubeIDE!</p>
<h3 id="cleaning-up-and-next-steps">Cleaning Up and Next Steps</h3>
<p>The last thing we need to do is ensure that we include our Common folder and exclude our testing directory when we are building for the STM32 target. Switch back to the Debug configuration, then open the project properties and navigate to C/C++ General → Paths and Sources. Select the Debug configuration. In the Source Filter tab, verify that the <code>Testing</code> directory isn&rsquo;t on the list, but that <code>Common</code>, <code>Drivers</code>, <code>Middleware</code>, and any other target-specific folders are. In the Includes tab make sure that the <code>Common</code> folder is appended to the end of the existing list, but that <code>Testing</code> is not. Finally, the <code>gtest</code> and <code>gmock</code> entries should not be in the Libraries tab. Repeat these instructions for the Release configuration.</p>
<p>You can remove or close the TestTemplate project at any time if you wish.</p>
<p>As you begin writing loosely coupled and unit-tested code, place the source and header files in the Common folder where it is accessible to, and compiles nicely for both the unit tests and the target system.</p>
<p>I might make a future article that covers how I write loosely coupled, modular code that works great with unit testing. For now, I recommend the following resources:</p>
<ul>
<li><a href="https://pragprog.com/titles/jgade/test-driven-development-for-embedded-c/" target="_blank" rel="noopener noreferrer">Test Driven Development for Embedded Systems by James Grenning</a>
</li>
<li><a href="https://embeddedartistry.com/blog/2018/08/06/musings-on-tight-coupling-between-firmware-and-hardware/" target="_blank" rel="noopener noreferrer">Embedded Artistry: Musings on Tight Coupling Between Firmware and Hardware</a>
</li>
<li><a href="https://google.github.io/googletest/primer.html" target="_blank" rel="noopener noreferrer">GoogleTest Primer</a>
</li>
</ul>
<h2 id="fun-extras">Fun Extras!</h2>
<h3 id="use-the-cc-unit-testing-plugin">Use the C/C++ Unit Testing Plugin</h3>
<p>The Eclipse CDT repository has a C/C++ Unit Testing Support plugin that ties into test runners like GoogleTest, and displays the output a little bit nicer than a console message. That can be installed by clicking Help → Install New Software, selecting the CDT repository in the &ldquo;Work with&rdquo; dropdown, and installing C/C++ Unit Testing Support from the CDT Optional Features section.</p>
<figure><img src="/blog/unit-testing-with-stm32cubeide/PluginSearch.png">
</figure>

<p>Once installed, open the project&rsquo;s Run Configuration dialog. Select C/C++ Unit in the sidebar, then click the New Configuration button. In the C/C++ Application section, click the Search Project button and select the executable created by the Unit Test build. Finally, in the C/C++ Testing tab, select the Google Tests Runner from the dropdown. Click the Run button, and the C/C++ Unit tab will appear and display the test results.</p>
<figure><img src="/blog/unit-testing-with-stm32cubeide/PluginOutput.png">
</figure>

<h3 id="regular-testing-with-a-key-combination">Regular Testing With A Key Combination</h3>
<p>A key benefit of writing code with Test Driven Development is that there is a tight feedback loop between writing code and testing it. Running tests regularly can help you spot bugs before they become masked. The best way to ensure that you remember to run tests regularly is to make it really easy to do. I like to assign a keyboard shortcut (such as Ctrl-R) to the Run command. When I hit the key combination my file changes are saved, the program builds, and the unit tests are run automatically.</p>
<p>To set this up, from the menu click Window → Preferences. Select the Keys submenu from the General section and search for &ldquo;Run&rdquo;. Select it and add your binding by clicking the Binding control and typing your desired key combination.</p>
<figure><img src="/blog/unit-testing-with-stm32cubeide/KeyCombination.png">
</figure>

<p>Next, in the General/Workspace/Build tab, check the box that says &ldquo;Save automatically before manual build&rdquo;.</p>
<figure><img src="/blog/unit-testing-with-stm32cubeide/AutoSave.png">
</figure>

<p>Finally, in the &ldquo;Run/Debug&rdquo;/Launching tab, in the Launch Operation section (near the bottom) check the radio button that says &ldquo;Always launch the previously launched application&rdquo;.</p>
<figure><img src="/blog/unit-testing-with-stm32cubeide/RunLast.png">
</figure>

<p>Now when you press the key combination, your project will save, build and run the unit tests immediately. If the key combination started the run configuration for the console output and you wanted it to run the C/C++ Unit plugin configuration instead, manually run the C/C++ Unit plugin configuration from Run menu. STM32CubeIDE will remember that and run that configuration after the key combination in the future.</p>
<h2 id="conclusion">Conclusion</h2>
<p>Unit testing on your native development machine is a great way to improve your embedded system development. It promotes modular code design, it is faster than target compilation, flashing and debugging, and it allows you to perform repeatable tests. In this article we explored how to set up the STM32CubeIDE to easily run unit tests against loosely-coupled STM32 project code.</p>
<p>Thank you for reading. I hope you found this article useful! I&rsquo;d love to hear your thoughts or questions about this article - you can reach me on social using the links in the footer below.</p>
<p>Happy testing!</p>
]]></description>
      </item>
    
      <item>
        <title>Intro to TouchGFX Embedded Graphics</title>
        <link>https://michael.kafarowski.com/blog/touchgfx-part-0/</link>
        <pubDate>Thu, 30 Jul 2020 00:00:00 +0000</pubDate>
        <guid>https://michael.kafarowski.com/blog/touchgfx-part-0/</guid>
        <description><![CDATA[<h1>Intro to TouchGFX Embedded Graphics</h1><h2 id="introduction">Introduction</h2>
<p>In this tutorial, I&rsquo;d like to introduce you to ST&rsquo;s TouchGFX GUI framework, and get you started on building GUI applications with your own hardware. This is the beginning of a series of posts on TouchGFX and embedded system development. Each post will build on the previous - we&rsquo;ll start from a simple demo using the TouchGFX designer, continuing with a more complex implementation using the built-in Model-View-Presenter pattern, and finally we&rsquo;ll tie it all together with a practical project that interfaces with actual hardware.</p>
<h2 id="what-is-touchgfx">What is TouchGFX?</h2>
<p>In our smartphone and web app era, people are accustom to using devices with rich and intuitive graphical user interfaces. In many cases, a touchscreen is not only preferred, but expected. TouchGFX is a GUI framework that enables you to develop such interfaces for your products. It is built exclusively for STMicroelectronics family of processors. It features an easy to use drag-and-drop GUI builder tool called the TouchGFX designer that outputs a basic code structure upon which you can build your own application. The code is written in C++, so basic knowledge of the language is a prerequisite (although none will be required for this first tutorial!).
Diving In</p>
<p>To follow along with this first tutorial, you won&rsquo;t any need any hardware yet. The TouchGFX Designer can run a simulation of your code on your local machine, allowing for quick GUI development. However, we&rsquo;ll set up our system for use with an STM32F469i Discovery Board, because we&rsquo;ll be targeting that processor in future tutorials. You&rsquo;ll need:</p>
<ul>
<li>About 30 minutes</li>
<li>A Windows machine (although the framework is not platform dependent, the Designer is Windows-only)</li>
<li>An account with STMicroelectronics to download the software (<a href="https://www.st.com/content/st_com/en/user-registration.html" target="_blank" rel="noopener noreferrer">sign up here</a>
)</li>
<li>TouchGFX Designer (<a href="https://www.st.com/en/development-tools/touchgfxdesigner.html" target="_blank" rel="noopener noreferrer">Download Link</a>
)</li>
</ul>
<p>Optionally, if you want to build with hardware, you also need:</p>
<ul>
<li>STM32CubeProgrammer (<a href="https://www.st.com/en/development-tools/stm32cubeprog.html" target="_blank" rel="noopener noreferrer">Download Link</a>
)</li>
<li>STM32F469I Discovery Board</li>
</ul>
<h2 id="installation">Installation</h2>
<p>Unzip the X-Cube-TouchGFX file and run the TouchGFX-x.xx.msi file in Utilities/PC_Software/TouchGFXDesigner to install the designer.</p>
<p>If you have a discovery board, STM32CubeProgrammer allows you to flash the application to the hardware. Install it with the default install settings. Of note, it must be installed with the default installation path for the TouchGFX Designer to be able to use it. It may request a driver install - this is normal.</p>
<p>When installation is finished, open the TouchGFX Designer.</p>
<h2 id="hello-world">Hello World!</h2>
<p>Welcome to the TouchGFX Designer! Create a new project targeting the STM32F469I Discovery Board by mousing over the Simulator box, clicking Change. Scroll nearly all the way to the right and click STM32F469I, then click Select. We&rsquo;ll use a Blank UI template. Name your project and click Create.</p>
<figure><img src="/blog/touchgfx-part-0/Select469i.PNG"
			alt="Screenshot of target selection window with the 469i board highlighted."><figcaption>
			<p>Target selection when first creating a project. We&rsquo;ll use the STM32F469I in future tutorials, so select it now.</p>
		</figcaption>
</figure>

<p>The main TouchGFX window consists of 4 major parts - the left-hand Sidebar, the main Canvas View, the right-hand side Options Box, and the top Menu Bar. In the Sidebar you change tabs to select between displaying a palette of widgets, a list of screens and objects in your application, and any custom containers you&rsquo;ve created (more on that in a future post). The main Canvas section shows the view of your application as if you were looking at it on the target screen. The Options box on the left will populate with options when you select an object.</p>
<figure><img src="/blog/touchgfx-part-0/Overview.PNG"
			alt="Screenshot of the main TouchGFX designer user interface"><figcaption>
			<p>The main TouchGFX Designer user interface.</p>
		</figcaption>
</figure>

<p>TouchGFX starts us out with a default screen. An application can have a number of independent screens. We&rsquo;ll start by adding a background to our default screen. Scroll down the widget window in the left Sidebar until you find a Box widget. Drag the box into the Canvas and grab the handles to scale it to the full size of the screen. Notice how the designer snaps your object to points of interest - namely the edges and middle of the screen. You&rsquo;ll notice that because the box is selected, the Options Sidebar has lit up with a few options, such as the widget name (currently box1), its coordinates and size, and the color and opacity. Name your box something meaningful, like &ldquo;background&rdquo;. Let&rsquo;s change the background color by clicking on the dropdown under Color and selecting your preferred color.</p>
<figure><img src="/blog/touchgfx-part-0/canvasWithBackground.JPG"
			alt="A dark grey box widget added to to the canvas to create a background"><figcaption>
			<p>If you&rsquo;re curious that&rsquo;s #FF30343A (a.k.a. &lsquo;Gunship Grey&rsquo;, at least according to me)</p>
		</figcaption>
</figure>

<p>With our background in place, let&rsquo;s add some text. Return to the widget menu of the left sidebar (the tab with the square and the +) and find the Text Area widget. Drag it into the middle of the view. If you select it, you&rsquo;ll find options in the Option Sidebar that let you change the displayed text, the color, the alignment, etc. Change the color to something visible and the text to Hello World! or your test string of choice!</p>
<p>The text is looking a little bit small, so let&rsquo;s change the style. Unfortunately, we can&rsquo;t freely select different fonts and sizes for each textbox like we do in word processors*. This constraint stems from the way that graphics such as font glyphs are stored on resource limited systems. On your personal computer, the shapes and sizes of text are calculated on the fly using equations contained in font files. On most embedded systems the processing power required for this is too high, so the hard work has to be done upfront before the program ever runs. During the compile stage, each glyph that will be displayed on your screen is converted to a static, discrete image and stored with your program. Because of this requirement we have to specify in advance which glyphs we want and how large we want them, so that the compiler knows it needs to include them. These pre-specified sets of fonts are called Typographies. Although this may seem inconvenient, it has the upside of making it easier to maintain a consistent style between elements. For example, if you had a menu bar, all the textboxes in the menu bar could share the same typography. Down the line if you wanted to modify the fonts, all you would have to do is modify the menu font typography and all the elements assigned to it would reflect your changes.</p>
<p>If we wanted a different font for each textbox we would need to make a typography for each one individually.</p>
<figure><img src="/blog/touchgfx-part-0/canvasTextJP.JPG"
			alt="Japanese characters in a textbox"><figcaption>
			<p>TouchGFX supports Unicode, so you can write text in most languages. The trick is to set the typography to a ttf font that supports the characters, such as Meiryo for Japanese. You may have to manually install the font to the TouchGFX/assets/fonts folder in the project source.</p>
		</figcaption>
</figure>

<p>You&rsquo;ll notice that by default the TouchGFX designer generates three typographies - default, large and small. We can customize the font and size of each typography by clicking on Texts in the top menu bar, then clicking on the Typographies tab. If you wish, you can add your own.</p>
<figure><img src="/blog/touchgfx-part-0/Typographies.PNG"
			alt="A screenshot of the Typographies tab"><figcaption>
			<p>Changing the typography font and size. Navigate to this page by clicking the Texts button in the Menu bar and then clicking the Typographies tab.</p>
		</figcaption>
</figure>

<p>Take this time to change the font and size of the typographies to whatever you would like. I changed the Large typography to Roboto Condensed, at 45 pixel size. The fonts that appear depend on what is available on your computer. Other settings, such as wildcards and fallbacks can be left as they are for now. We&rsquo;ll cover them in a future tutorial, but try and guess what they do based on the explanation of typographies above. (Here&rsquo;s a hint: by default, the TouchGFX designer will only generate glyphs for letters it knows for sure will be required).</p>
<p>When you finish, return to the main Canvas view and select your custom typography for your new text item. You may have to reposition it a little bit if the new typography changes the overall location.</p>
<p>Lets see it in action! Click the Run Simulator button, or if you have the dev board click Run Target. You should see your text front and center on the screen!</p>
<figure><img src="/blog/touchgfx-part-0/canvasWithText.PNG"
			alt="The completed project"><figcaption>
			<p>Hello TouchGFX! -The World, probably</p>
		</figcaption>
</figure>

<h2 id="diving-a-little-deeper">Diving a Little Deeper</h2>
<p>Congratulations! You&rsquo;ve learned how to do 80% of what you&rsquo;ll normally be doing in the TouchGFX Designer (arranging widgets and setting text)! Let&rsquo;s dive a little deeper.</p>
<p>We&rsquo;ll continue by adding a button to our screen. Find the button section, then drag a Button (or Button with Label, or Button with Icon) into the view. You&rsquo;ll see that the button has a few more options than the box, namely the images that it uses in its pressed and unpressed state, as well as the text (if you chose a label button) or an icon (if you chose an icon button).</p>
<p>Let&rsquo;s make our button do something when its clicked, like fade in our hello world text. You can achieve some basic interactive functionality using the TouchGFX Designer Interactions tab. Access the Interactions menu it by clicking the second tab on the right-hand Options box. Click Add Interaction. For the first interaction we&rsquo;ll tell the application to hide our text when we start the program. Set the trigger to be Screen is Entered. In the action pane, we&rsquo;ll choose &ldquo;Fade Widget&rdquo;. We&rsquo;ll select our hello world text object as the target, setting its opacity to 0 alpha over a duration of 0ms (instantly).</p>
<p>Add a second interaction, and choose the trigger to be Button is Clicked. Your button should be automatically selected (if you&rsquo;ve added multiple buttons you can choose which one to bind the interaction to in the &ldquo;Choose Clicked Source&rdquo; dropdown. Set the action to Fade Widget again, targeting the text object, but this time set the alpha to 255, over a duration of 1000ms. The Easing setting will determine the rate that the alpha will change over the course of the fade. A great site to visualize these settings is easings.net.</p>
<figure><img src="/blog/touchgfx-part-0/interactionsmenu.JPG"
			alt="Screenshot of the Interactions menu of the TouchGFX designer"><figcaption>
			<p>Fade Out, Fade In. Fade Out, Fade In. Fade Out&hellip;</p>
		</figcaption>
</figure>

<h2 id="the-tip-of-the-iceberg">The Tip of the Iceberg</h2>
<p>You&rsquo;ve successfully set up the TouchGFX designer and created a basic GUI application! There is so much more we can do - style our UI using the existing widgets, creating our own UI elements with custom containers, adding interactive functionality between multiple screens, interfacing with a backend system or hardware - the list goes on. In the next post we&rsquo;ll dive deeper into our application and examine the code that will bring our system to life. In the meantime, try playing around with the different widgets and interactions available and see what you can create!</p>

  <figure>
    <video width="80%" onclick="this.play()">
        <source src="/blog/touchgfx-part-0/2020-07-30%2020-14-19.mp4" type="video/mp4">
    </video>
    <figcaption>Example of an animation made possible with Interactions</figcaption>
</figure>


<h2 id="conclusion-and-footnotes">Conclusion and Footnotes</h2>
<p>Thanks for reading! I hope this post was helpful in getting your feet wet with TouchGFX. If you have any thoughts you&rsquo;d like to share about your experience, reach out at the social links below.</p>
<p>Here are some notes that may be of interest/use to you at this stage.</p>
<ul>
<li>
<p>If animations aren&rsquo;t smooth on the target, it may be because optimizations are turned off! We&rsquo;ll touch more on this in the next tutorial.</p>
</li>
<li>
<p>I&rsquo;ve encountered a bug where the simulator will work fine, but the target will refuse to display the screen on startup. Strangely, adding an invisible button tends to fix this issue, and I&rsquo;m not sure why.</p>
</li>
<li>
<p>If you have a font you would like to use that doesn&rsquo;t show up in the list on the typographies page, place the TTF file in the TouchGFX/assets/fonts/ folder in the root of the project.</p>
</li>
<li>
<p>The TouchGFX designer outputs project files compatible with a number of different IDEs. If you want to get a head start on further development, download STM32CubeIDE and explore the code.</p>
</li>
</ul>
]]></description>
      </item>
    
      <item>
        <title>Advancement of Standards</title>
        <link>https://michael.kafarowski.com/blog/advancement-of-standards/</link>
        <pubDate>Fri, 28 Oct 2016 00:00:00 +0000</pubDate>
        <guid>https://michael.kafarowski.com/blog/advancement-of-standards/</guid>
        <description><![CDATA[<h1>Advancement of Standards</h1><p>Lately it&rsquo;s been rare to see a product release that really wows people, but the <a href="https://www.youtube.com/watch?v=EA8vDBY6bCs" target="_blank" rel="noopener noreferrer">Apple event</a>
 yesterday came pretty close. Two points that I found pretty interesting were the new Touch Bar and the distinct lack of ports other than USB-C (and a 3.5mm audio jack). In this article I discuss my thoughts on the significance of these two changes.</p>
<h2 id="that-new-feature">That New Feature</h2>
<p>The Touch Bar breaks the mold of each new model being “Apple’s Fastest/Thinnest/Lightest MacBook ever” which in recent times has been mostly viewed as lackluster as far as innovation goes. It’s a long touchscreen bar that replaces the function keys with interactive software buttons, which change depending on which application you are using. It’s a new and cool addition, and there are no other keyboards out there that feature an auxiliary touch screen.</p>
<figure><img src="/blog/advancement-of-standards/touchbar.jpg"
			alt="An Apple Macbook with a Touchbar."><figcaption>
			<p>An Apple Macbook with a Touchbar.</p>
		</figcaption>
</figure>

<p>Except… there are. Both the Razer Deathstalker and the Mad Catz Strike 7 keyboards have them and they were first released in 2012.</p>
<figure><img src="/blog/advancement-of-standards/strikekeyboard.jpg"
			alt="The Mad Catz Strike 7: by far the strangest looking keyboard I&#39;ve ever seen. Note the touchscreen on the left."><figcaption>
			<p>Image of the Mad Catz Strike 7 keyboard.</p>
		</figcaption>
</figure>

<p>But the Touch Bar actually is still special.</p>
<p>Apple has the relatively unique position where their operating system (and all the apps that run on it) is limited to hardware which they have full design control over. What separates the Touch Bar from the Deathstalker or Strike 7 touchscreens is that unlike a relatively small keyboard manufacturer, Apple can effectively say to developers &ldquo;Here is this new thing, here is the API to use it and its functionality will be available to hundreds of thousands of people simultaneously”.</p>
<p>As far as time invested versus return for developers, that sounds a whole lot better than “Here is a proprietary API to allow customers who have bought this single line of keyboards from a single manufacturer in a niche market to use this touchscreen” (repeated for each different keyboard brand with a touchscreen).</p>
<p>One API and instant widespread adoption of this new feature is a great selling point for app developers to begin using it right away.</p>
<h2 id="frustration-today-convenience-tomorrow">Frustration Today, Convenience Tomorrow.</h2>
<p>Apple doesn’t stop exercising their design control with the Touch Bar. A large source of controversy this year was the removal of the 3.5mm audio jack from the iPhone in favor of a single USB-C port. While (perhaps ironically) Apple has elected to keep the audio jack on the MacBook Pro, all other ports from previous models have disappeared in favor of 2-4 USB-C ports.</p>
<p>Lately the expression “dongle hell” is often used in referring to the compatibility issues that changing to USB-C will cause. Any device that uses the previous USB generation, SD cards, monitor plugs, Ethernet cables, or other types of connectors can no longer interface directly with the laptop. USB-C has implements many improvements over standard USB, including faster data transfer speeds and higher current ratings, however the new connector forces people to buy adapters to continue using their old equipment.</p>
<p>This has all happened before though – back in the 90’s the go-to standard was the RS-232 port, as well as various other serial, parallel, PS/2, and game connectors.</p>
<figure><img src="/blog/advancement-of-standards/ports.jpg"
			alt="A photo of the IO panel from an old PC, showing many obsolete connectors."><figcaption>
			<p>This image shows PS/2 (far left, mouse and keyboard), Parallel (pink), Serial (two cyan), and Joystick (yellow) ports. All have been replaced by USB on modern PCs.</p>
		</figcaption>
</figure>

<p>In the middle of 1996 the Universal Serial Bus (USB) standard was introduced. It bore many improvements over the many cables it was to replace, but it also required an entirely new connector and used an entirely different protocol, so it was sparsely implemented at first. Can you guess which company was noted for embracing it?</p>
<p>In a move criticized by many, Apple opted to exclude the then-ubiquitous floppy disk drive from their new 1998 iMac G3, included two USB ports and told anyone that wanted to continue using floppies to buy a external USB floppy drive. From then on, USB became more and more prevalent in PCs due to the growing market for devices that used it. Almost two decades later, USB is everywhere and floppies are known merely as ‘the save button’. It may be reasonably assumed that a similar criticism and subsequent stubborn yet inevitable adoption may follow Apple’s decision to push USB-C on the 2017 MacBook Models.</p>
<figure><img src="/blog/advancement-of-standards/imacg3.jpg"
			alt="A blue and white trayloading iMac G3."><figcaption>
			<p>The iMac G3 - the computer that helped kill the floppy disk and usher in an era of USB.</p>
		</figcaption>
</figure>

<h2 id="wrap-up">Wrap-Up</h2>
<p>An occasional refresher of standards is arguably a good thing – designs plagued by feature creep can be redesigned and streamlined, multiple implementations of the standard can be combined into an overall superior implementation, and nagging issues of the previous generation can be efficiently fixed. This of course comes at the cost of discomfort among users during the transition phase.</p>
<p>Overall, the new MacBook Pro seems to be a step in the right direction for Apple. Motivation for developers to implement functionality for a new peripheral exists because of the large number of people it will reach due to Apple’s design control of their hardware. The radical and sudden adoption of USB-C has the potential to disrupt the market and increase the demand for USB-C devices, which will allow consumers to reap the benefits of it in the future.</p>
<p>Where would we be without change, after all?</p>
<p>&ndash;</p>
<p>The following articles were referenced in the writing of this post to get a sense for the original public reception of USB adoption and the removal of the floppy drive from the iMac:</p>
<p>Charles Pillar. &ldquo;The &lsquo;i&rsquo; in IMac Doesn&rsquo;t Stand for Inferior . . . or Floppy Drive.&rdquo; Los Angeles Times, 11 May 1998. Web. 28 Oct. 2016.</p>
<p>Vogt, James D. &ldquo;The IMac and the Floppy Drive: A Conspiracy Theory.&rdquo; (Opinion Piece) OSNews. 3 Aug. 1998. Web. 28 Oct. 2016.</p>
<p>Garfinkel, Simson L. &ldquo;USB Deserves More Support.&rdquo; Boston Globe Online, 20 May 1999. Web. 28 Oct. 2016.</p>
<figure><img src="/blog/advancement-of-standards/myimac.jpg"
			alt="My old iMac. Rest in peace (for now)."><figcaption>
			<p>This article is dedicated to the 2000 iMac G3 I got from a garage sale for $10 in 2009. I loved that computer until the screen or power supply died in 2012. It&rsquo;s still in my basement, waiting for me to expand my electronics knowledge to the point of being able to repair it.</p>
		</figcaption>
</figure>

]]></description>
      </item>
    
  </channel>
</rss>