<?xml version='1.0' encoding='utf-8'?>
<feed xmlns="http://www.w3.org/2005/Atom"><id>https://jeffmcbride.net/atom.xml</id><title>Jeff McBride</title><updated>2026-10-12T07:00:00Z</updated><link href="https://jeffmcbride.net/" rel="alternate" /><link href="https://jeffmcbride.net/atom.xml" rel="self" /><author><name>Jeff McBride</name></author><entry><id>https://jeffmcbride.net/posts/droplet-capacitance-sensing/</id><title>Droplet Capacitance Sensing</title><link href="https://jeffmcbride.net/posts/droplet-capacitance-sensing/" rel="alternate" /><published>2026-10-12T07:00:00Z</published><updated>2026-10-12T07:00:00Z</updated><summary type="html">&lt;p&gt;This is a throwback to something I worked on a few years ago: the PurpleDrop digital microfluidic (DMF) device and a capacitance sensing method I designed for it that turned out to work pretty well. In the process I encountered a weird dielectric hysteresis phenomenon in kapton which I’ve never really understood, and which completely disappeared when I tried to look closer at it, leaving the observation a nagging mystery to me still today.&lt;/p&gt;
&lt;p&gt;The topic has come back to mind again lately, so I want to write about it. It was an interesting project that enabled better drop control, opened the door to feedback controlled drop splitting, and provided the measurements needed to later perform some surface lifetime experiments. I’ll give a little background about electrowetting-on-dielectric (EWOD) systems since most readers probably don’t know what that is, and then focus mainly on how the capacitance sensing worked: it used current integration to measure the charge transferred to electrodes during voltage switching.&lt;/p&gt;
&lt;p&gt;Then, I’ll talk about the as-yet unexplained surprise – a large hysteresis in the capacitance that I interpreted to be dielectric hysteresis, but which disappeared when I came back to measure it more carefully.&lt;/p&gt;
&lt;section id="dmf-background" class="level2"&gt;
&lt;h2 class="anchored" data-anchor-id="dmf-background"&gt;DMF Background&lt;/h2&gt;
&lt;div class="quarto-figure quarto-figure-center"&gt;
&lt;figure class="figure"&gt;
&lt;p&gt;&lt;a href="electrowetting_diagram.png" class="lightbox" data-gallery="quarto-lightbox-gallery-1" title="A diagram illustrating the electrowetting behavior"&gt;&lt;img src="https://jeffmcbride.net/posts/droplet-capacitance-sensing/electrowetting_diagram.png" class="img-fluid figure-img" alt="A diagram illustrating the electrowetting behavior"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;figcaption&gt;A diagram illustrating the electrowetting behavior&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/div&gt;
&lt;p&gt;Electrowetting-on-dielectric (EWOD) has been explored for decades now as a method of automated liquid handling for biological processes. Essentially, small water droplets – which likely contain other reagents or biological molecules – are trapped between two surfaces and manipulated by the selective application of a voltage on a two-dimensional array of electrodes. There’s a plethora of literature about different ways to fabricate these devices, and attempts to perform a wide array of lab processes on them. It basically works by creating electrostatic attraction between charge in the drop and the electrodes (even with low ion concentration, water molecules are sufficiently polar for this to work), making the drop seem to “wet” the normally hydrophobic surface. It’s sort of like having an electrically controllable hydrophobicity. The surface must be carefully coated with some form of fluoropolymer in order to keep it very hydrophobic by default.&lt;/p&gt;
&lt;p&gt;I am aware of a number of attempts to commercialize products based on the technology, most failing. Illumina launched a sequencing sample prep product called ‘Neoprep’ in 2015, only to drop it later. Around 2019, Oxford Nanopore released the VolTRAX, a EWOD-based device for performing sequencing prep. The VolTRAX used a thin-film-transistor (TFT) based process, similar to that used in displays developed by a Sharp spin-off (AqDrop) to create its substrate. I thought this was a brilliant idea at the time – it allowed for many more smaller electrodes by integrating transistors – solving the problem of connector pins to the consumable substrate constraining the number of controllable electrodes – by integrating transistors right into the consumable substrate, and also taking advantage of existing high-volume manufacturing processes. This project, and AqDrop, both quietly disappeared. I have heard that the AqDrop substrates suffered from dielectric breakdown, but it’s only a rumor and I cannot substantiate it.&lt;/p&gt;
&lt;p&gt;There have been a few companies trying to sell generic DMF devices, including &lt;a href="https://sci-bots.com/"&gt;Sci-bots&lt;/a&gt;. They sell the DropBot, and as far as I can tell are still selling some number of them. They still send me a newsletter from time to time, at least. I suspect most of those sales are one-offs to researchers looking to do an experiment and write a paper, rather than to do “real” lab work, but I would love to hear counter-examples to prove that wrong!&lt;/p&gt;
&lt;p&gt;The problem with using DMF systems for automating real lab work seems to be reliability – and cost to a lesser extent – and this is entirely consistent with my experience working with them. They are extremely cool to watch and program, and they have an almost magic quality to the way they operate. However it is finicky to prepare and build the consumables, and their operation depends on careful control of the experiment and is prone to failure. Getting samples in and out of the device is particularly fraught. So all in all, I’m bearish about large-scale application of the technology for general lab automation. Still, it was an interesting project to work on!&lt;/p&gt;
&lt;/section&gt;
&lt;section id="capacitance-for-volume-sensing" class="level2"&gt;
&lt;h2 class="anchored" data-anchor-id="capacitance-for-volume-sensing"&gt;Capacitance for Volume Sensing&lt;/h2&gt;
&lt;p&gt;The reason that measuring capacitance is interesting for a DMF device is that it can be used to determine drop volume. The capacitance between an electrode and the top-plate increases in proportion to the area covered by a drop between them. Knowing volume can be useful:&lt;/p&gt;
&lt;ol type="1"&gt;
&lt;li&gt;It allows for faster drop movement because the capacitance signal can be used to determine when the drop has moved, so that the controller can proceed as quickly as possible to the next step.&lt;/li&gt;
&lt;li&gt;It allows detecting problems, e.g.&amp;nbsp;when a drop gets stuck. Perhaps the situation can be resolved with an increase in voltage, or perhaps the protocol must be stopped early. The velocity of each movement can also be used to detect a fouling area of the surface even before it causes a failure.&lt;/li&gt;
&lt;li&gt;When combined with feedback control, it can allow more accurate drop splitting and dispensing, supporting partial electrode coverage and arbitrary drop volumes, rather than relying on the number of active electrodes alone to control volume when dispensing.&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
&lt;section id="ac-systems" class="level2"&gt;
&lt;h2 class="anchored" data-anchor-id="ac-systems"&gt;AC Systems&lt;/h2&gt;
&lt;p&gt;DMF electrodes are driven with an AC voltage because charge build-up can cause problems when driven with DC. On many systems, such as the &lt;a href="https://microfluidics.utoronto.ca/dropbot/"&gt;DropBot&lt;/a&gt;, a single high voltage sinusoidal source is used with an array of switches (e.g.&amp;nbsp;solid state relays) to enable individual electrodes, and the AC signal lends itself well to capacitance measurement via impedance measurement – it simply requires an RC filter on the top-plate and a voltage measurement. DropBot does this, and it seems to me based on their papers and chats with one of its designers that it works quite well.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="purpledrop-pseudo-ac-drive" class="level2"&gt;
&lt;h2 class="anchored" data-anchor-id="purpledrop-pseudo-ac-drive"&gt;PurpleDrop “Pseudo-AC” Drive&lt;/h2&gt;
&lt;p&gt;PurpleDrop has a different electrode drive architecture, and it doesn’t really allow measuring capacitance in the same way. Instead of a sinusoidal source, PurpleDrop uses high-voltage digital outputs on two 64-channel &lt;a href="https://www.microchip.com/en-us/product/HV507"&gt;HV507&lt;/a&gt; chips. These are just shift registers with push-pull outputs – but they support 300 V! This saves space and cost by replacing 128 SSRs with two chips. In order to avoid charge trapping issues, PurpleDrop switches the polarity at 500Hz, driving the top-plate and inactive electrodes high while driving the active electrodes low for 1ms, then driving the active electrodes high while grounding the others for 1ms. The top-plate is driven by one of the HV507 pins, just like the other electrodes.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="current-sense-design" class="level2"&gt;
&lt;h2 class="anchored" data-anchor-id="current-sense-design"&gt;Current Sense Design&lt;/h2&gt;
&lt;section id="overview" class="level3"&gt;
&lt;h3 class="anchored" data-anchor-id="overview"&gt;Overview&lt;/h3&gt;
&lt;p&gt;A sense resistor placed between the HV507 pin and the top-plate is used to measure the total amount of charge that is transferred to the top-plate when an electrode – or set of electrodes – is charged from 0V to V_HV, relative to the top-plate. A voltage integrator measures the total amount of charge transferred over a relevant window of time, and capacitance is the total amount of charge transferred per volt.&lt;/p&gt;
&lt;p&gt;Because the top-plate is also charged to V_HV (which can be up to 300V) during the ‘negative’ part of the cycle, the high voltage part of the circuit must be isolated or else the current sense amplifier would be seeing some very large input voltages. To avoid that, the top-plate is tied to the main board ground, and the HV supply output as well as the HV507 chips are isolated from the rest of the board. This requires some additional opto-isolators to facilitate control between the two isolated domains, but that was well worth it for the capacitance sensing!&lt;/p&gt;
&lt;div class="quarto-figure quarto-figure-center"&gt;
&lt;figure class="figure"&gt;
&lt;p&gt;&lt;a href="SenseDiagram.png" class="lightbox" data-gallery="quarto-lightbox-gallery-2" title="Block diagram of the Purpledrop system. The HV507 drivers are isolated from the main board power supplies, so that the top plate can be connected to the board ground, allowing sense measurement with no common-mode component."&gt;&lt;img src="https://jeffmcbride.net/posts/droplet-capacitance-sensing/SenseDiagram.png" class="img-fluid figure-img" alt="Block diagram of the Purpledrop system. The HV507 drivers are isolated from the main board power supplies, so that the top plate can be connected to the board ground, allowing sense measurement with no common-mode component."&gt;&lt;/a&gt;&lt;/p&gt;
&lt;figcaption&gt;Block diagram of the Purpledrop system. The HV507 drivers are isolated from the main board power supplies, so that the top plate can be connected to the board ground, allowing sense measurement with no common-mode component.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id="sequencing" class="level3"&gt;
&lt;h3 class="anchored" data-anchor-id="sequencing"&gt;Sequencing&lt;/h3&gt;
&lt;p&gt;Switching the electrode drive polarity causes a lot of transient current as various capacitances (caused by all the drops on the board, and stray capacitance) are charged/discharged. To measure only a select subset of electrodes, their charging must be isolated from the polarity switch signals or they will be swamped. It’s necessary to switch the polarity, allow sufficient time for the system to settle, and then to charge the to-be-measured electrodes in isolation.&lt;/p&gt;
&lt;p&gt;The HV507 provides two special function pins that make this sequencing faster and require fewer shift register loads (though as I’ll get to soon, we will end up doing them anyway):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The POL pin swaps the polarity of all outputs – generating the 500Hz alternating voltage requires only toggling POL.&lt;/li&gt;
&lt;li&gt;The BL (blank) pin (active-low) clears all outputs when asserted, which can be used to delay the charging of active electrodes after polarity switch&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Measurements are taken at the start of the positive half of the cycle – i.e.&amp;nbsp;when the top plate is driven low and active electrodes are driven high. The image below shows the sequence of events:&lt;/p&gt;
&lt;div class="quarto-figure quarto-figure-center"&gt;
&lt;figure class="figure"&gt;
&lt;p&gt;&lt;a href="active_measurement_sequence.png" class="lightbox" data-gallery="quarto-lightbox-gallery-3" title="Capacitance Measurement Sequence"&gt;&lt;img src="https://jeffmcbride.net/posts/droplet-capacitance-sensing/active_measurement_sequence.png" class="img-fluid figure-img" width="400" alt="Capacitance Measurement Sequence"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;figcaption&gt;Capacitance Measurement Sequence&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/div&gt;
&lt;p&gt;During the negative half of the cycle, the electrodes are driven to -V_HV relative to the top-plate. Then the polarity is switched and blanking is enabled, and a short delay allows the system to stabilize with all electrodes (including the top-plate) driven to the same voltage. In the meantime, a new configuration of “active” electrodes can be clocked into the HV507 shift registers. Then after the blanking delay, the current integrator is reset, followed by the release of the BL signal causing the active electrodes to be driven to V_HV while the top plate remains at 0 V. After a small delay to allow the electrode voltages to stabilize, the output of the current integrator – i.e.&amp;nbsp;the total transferred charge – is read by an ADC.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="measurement-groups" class="level3"&gt;
&lt;h3 class="anchored" data-anchor-id="measurement-groups"&gt;Measurement Groups&lt;/h3&gt;
&lt;p&gt;When I say “active” electrodes, I mean the electrodes which are enabled by the droplet control algorithm for the purpose of manipulating the drops. The capacitance of these can be measured every polarity switch without interfering with system operation at all since we have to drive those electrodes anyway. It’s also possible to activate other electrodes very briefly, just to measure their capacitance, and the system supports this in two ways:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;scan groups (up to 5)&lt;/li&gt;
&lt;li&gt;bulk scan&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Scan groups are measured every 500Hz cycle by activating each group just long enough to measure capacitance. The activation is not long enough to significantly affect the movement of drops. It does subtract from the on-time of the active electrodes, which slightly reduces the pressure created on drops, but for a small number of scan groups this is only a tiny effect.&lt;/p&gt;
&lt;div class="quarto-figure quarto-figure-center"&gt;
&lt;figure class="figure"&gt;
&lt;p&gt;&lt;a href="CapacitanceSampleTiming.png" class="lightbox" data-gallery="quarto-lightbox-gallery-4" title="Timing diagram of group sampling at the beginning of the positive polarity phase"&gt;&lt;img src="https://jeffmcbride.net/posts/droplet-capacitance-sensing/CapacitanceSampleTiming.png" class="img-fluid figure-img" width="450" alt="Timing diagram of group sampling at the beginning of the positive polarity phase"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;figcaption&gt;Timing diagram of group sampling at the beginning of the positive polarity phase&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/div&gt;
&lt;p&gt;A “bulk scan” is performed at a much lower rate (typically 2 Hz), and involves activating each of the 127 electrodes one by one to measure its capacitance. This does disrupt the pressure created by active electrodes and can sometimes be detected as a quick wobble in a drop as the active electrode(s) affecting it are disabled for a fraction of a second and then re-enabled, but is not really a functional issue.&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="integration-circuit-design" class="level2"&gt;
&lt;h2 class="anchored" data-anchor-id="integration-circuit-design"&gt;Integration Circuit Design&lt;/h2&gt;
&lt;div class="quarto-figure quarto-figure-center"&gt;
&lt;figure class="figure"&gt;
&lt;p&gt;&lt;a href="pulse_integration_schematic.png" class="lightbox" data-gallery="quarto-lightbox-gallery-5" title="Pulse Integrator Schematic"&gt;&lt;img src="https://jeffmcbride.net/posts/droplet-capacitance-sensing/pulse_integration_schematic.png" class="img-fluid figure-img" alt="Pulse Integrator Schematic"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;figcaption&gt;Pulse Integrator Schematic&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/div&gt;
&lt;section id="current-gain-selection" class="level3"&gt;
&lt;h3 class="anchored" data-anchor-id="current-gain-selection"&gt;Current Gain Selection&lt;/h3&gt;
&lt;p&gt;An analog mux allows software to select between two values of sense resistor, to allow for the possibly large dynamic range of measuring large groups of electrodes and single electrodes. The selection of gain can be based on the number of electrodes being activated during the measurement, as this sets a rough upper limit on capacitance.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="amplifier-stages" class="level3"&gt;
&lt;h3 class="anchored" data-anchor-id="amplifier-stages"&gt;Amplifier Stages&lt;/h3&gt;
&lt;p&gt;Three op-amps are used in the integrator. The first provides buffering and a small amount of gain. The second is a voltage integrator, counting charge transfer. The third stage amplifies the integrator output to a good range for the ADC. A FET provides a software-controlled reset of the integrator voltage, used to start the integration&lt;sup&gt;1&lt;/sup&gt;.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="negative-supply-rail" class="level3"&gt;
&lt;h3 class="anchored" data-anchor-id="negative-supply-rail"&gt;Negative Supply Rail&lt;/h3&gt;
&lt;p&gt;The first iterations of this circuit lacked the negative power rail for the op-amps, and this led to some problems. For starters, despite being “rail-to-rail” amplifiers, the output can’t truly go to the 0V rail, so there was always a small amount of “masked” input signal on the integrator input, leading to integrations like this one:&lt;/p&gt;
&lt;div class="quarto-figure quarto-figure-center"&gt;
&lt;figure class="figure"&gt;
&lt;p&gt;&lt;a href="scope_capture.png" class="lightbox" data-gallery="quarto-lightbox-gallery-6" title="Scope capture of integrator output. Left: A measurement with no drop present. Right: A measurement with a drop present."&gt;&lt;img src="https://jeffmcbride.net/posts/droplet-capacitance-sensing/scope_capture.png" class="img-fluid figure-img" alt="Scope capture of integrator output. Left: A measurement with no drop present. Right: A measurement with a drop present."&gt;&lt;/a&gt;&lt;/p&gt;
&lt;figcaption&gt;Scope capture of integrator output. Left: A measurement with no drop present. Right: A measurement with a drop present.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/div&gt;
&lt;p&gt;The steady ramp is due to the offset on the first stage output. It can somewhat be calibrated for, but not with great accuracy because the shape of the current pulse varies quite a bit based on the capacitance being charged, and the amount of signal being masked depends on the shape. A bigger problem was that input noise which was nearly zero-mean is no longer zero-mean after having the negative component clipped. Adding the negative supply rail improved accuracy substantially.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="augment-fet" class="level3"&gt;
&lt;h3 class="anchored" data-anchor-id="augment-fet"&gt;“Augment” FET&lt;/h3&gt;
&lt;p&gt;An additional software controller NFET is added between HV_GND (the low side of the isolated HV supply and the ground connection for the HV507s) and GND (the ground reference for the amplifiers and ADCs, as well as the rest of the board), and is enabled when the top-plate output is driven low. I found this to improve noise performance a lot, as the HV507 drive strength is quite weak.&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="results" class="level2"&gt;
&lt;h2 class="anchored" data-anchor-id="results"&gt;Results&lt;/h2&gt;
&lt;p&gt;After a few iterations, the sensing was working great. The main reason I added the high-speed “group” measurements was to do feedback control of drop splitting. I also set up PWM control for the electrodes, allowing electrode groups to be turned off early, to control the amount of pressure induced on the drop. By controlling the relative pressure on each side of a drop being split at 500Hz, using the capacitive volume measurement as feedback, a drop could be dispensed with much more accurate volume or a single drop could be divided more accurately. I made some videos at the time, and I think these give a good intuitive idea of how it worked:&lt;/p&gt;
&lt;section id="capacitance-measurement-demo-video" class="level3"&gt;
&lt;h3 class="anchored" data-anchor-id="capacitance-measurement-demo-video"&gt;Capacitance Measurement Demo Video&lt;/h3&gt;
&lt;p&gt;I made a video at the time showcasing the capacitance features:&lt;/p&gt;
&lt;div class="video-embed"&gt;
&lt;iframe title="PurpleDrop Capacitance Measurement Demo" width="100%" height="100%" src="https://video.jeffmcbride.net/videos/embed/b7DNpDTTeboTsF1V1iMPxD" frameborder="0" allowfullscreen="" sandbox="allow-same-origin allow-scripts allow-popups allow-forms" style="position: absolute; inset: 0"&gt;
&lt;/iframe&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id="syringe-pump-driven-dispensing" class="level3"&gt;
&lt;h3 class="anchored" data-anchor-id="syringe-pump-driven-dispensing"&gt;Syringe Pump Driven Dispensing&lt;/h3&gt;
&lt;p&gt;I also used feedback control for actively pumped drop dispensing, where the feedback was crucial for knowing how much to move the syringe pump and for splitting off accurately sized drops:&lt;/p&gt;
&lt;div class="video-embed"&gt;
&lt;iframe title="PurpleDrop Capacitance Controlled Syringe Dispense" width="100%" height="100%" src="https://video.jeffmcbride.net/videos/embed/row8xUGZWCR4xnUeV8pe24" frameborder="0" allowfullscreen="" sandbox="allow-same-origin allow-scripts allow-popups allow-forms" style="position: absolute; inset: 0"&gt;
&lt;/iframe&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="the-kapton-hysteresis-mystery" class="level2"&gt;
&lt;h2 class="anchored" data-anchor-id="the-kapton-hysteresis-mystery"&gt;The Kapton Hysteresis Mystery&lt;/h2&gt;
&lt;section id="capacitance-depends-heavily-on-voltage-history" class="level3"&gt;
&lt;h3 class="anchored" data-anchor-id="capacitance-depends-heavily-on-voltage-history"&gt;Capacitance depends heavily on voltage history&lt;/h3&gt;
&lt;p&gt;Those videos were made with a Mylar film dielectric. I initially used kapton film, and it has some favorable properties like better temperature range and strong dielectric strength – one of the things we wanted to do was thermocycling, which involves heating the drop, and I thought kapton would be better suited to this. However, when I first started testing out capacitance sensing while working with the kapton film, it proved quite frustrating! It showed what appeared to me to be a strong dielectric hysteresis. Essentially, the amount of charge transferred during electrode activation depended on the recent history of the applied voltage.&lt;/p&gt;
&lt;p&gt;I apologize in advance if the description I’m about to provide ends up confusing – it confused me a bit too at the time, and I’m writing now from memory, some messy contemporaneous notes, and whatever data I collected then well enough to understand now.&lt;/p&gt;
&lt;p&gt;The first sign that something was going on was that the active and bulk scans did not match at all. It quickly became clear as well that the value read for a particular electrode during the bulk scan depended HEAVILY on whether that electrode was currently active.&lt;/p&gt;
&lt;p&gt;Here’s an example. The plot below shows the bulk capacitance measurement over a long period of time. The electrode is toggled on/off every 30s as the drop is left to evaporate and shrink. The higher values are when the electrode is active, and the lower points are when it is not. The difference between active and inactive is almost a factor of 3!&lt;/p&gt;
&lt;div class="quarto-figure quarto-figure-center"&gt;
&lt;figure class="figure"&gt;
&lt;p&gt;&lt;a href="alternating_active_bulk_cap.png" class="lightbox" data-gallery="quarto-lightbox-gallery-7" title="Alternating Active State"&gt;&lt;img src="https://jeffmcbride.net/posts/droplet-capacitance-sensing/alternating_active_bulk_cap.png" class="img-fluid figure-img" alt="Alternating Active State"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;figcaption&gt;Alternating Active State&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/div&gt;
&lt;p&gt;I started making plots like this one for a different electrode, which is just extracting one point for each 30s period in the plot above, and also including the active measurement:&lt;/p&gt;
&lt;div class="quarto-figure quarto-figure-center"&gt;
&lt;figure class="figure"&gt;
&lt;p&gt;&lt;a href="alternate_while_evap2.png" class="lightbox" data-gallery="quarto-lightbox-gallery-8" title="Alternating while evaporating 2"&gt;&lt;img src="https://jeffmcbride.net/posts/droplet-capacitance-sensing/alternate_while_evap2.png" class="img-fluid figure-img" alt="Alternating while evaporating 2"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;figcaption&gt;Alternating while evaporating 2&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/div&gt;
&lt;p&gt;In this case, you can see that the bulk measurement while active is only slightly lower than the active measurement itself (they are measuring the same thing, so should be equal). However, for other electrodes, the difference was more stark. At first, I didn’t see the pattern because the assignment of output channel to electrode is quite randomized due to the vagaries of board layout, but then I did. The relationship between bulk measurement and active measurement for a single electrode seemed to depend strongly on the output number (i.e.&amp;nbsp;the position in the HV507 shift register). To explore this relationship, I put a few drops on different electrodes with the aim of spreading them out roughly evenly across the 128 output positions:&lt;/p&gt;
&lt;div class="quarto-figure quarto-figure-center"&gt;
&lt;figure class="figure"&gt;
&lt;p&gt;&lt;a href="distributed_droplets.png" class="lightbox" data-gallery="quarto-lightbox-gallery-9" title="Droplets on a board"&gt;&lt;img src="https://jeffmcbride.net/posts/droplet-capacitance-sensing/distributed_droplets.png" class="img-fluid figure-img" width="250" alt="Droplets on a board"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;figcaption&gt;Droplets on a board&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/div&gt;
&lt;p&gt;Then, I measured the ratio of active and bulk capacitance for each droplet covered electrode:&lt;/p&gt;
&lt;div class="quarto-figure quarto-figure-center"&gt;
&lt;figure class="figure"&gt;
&lt;p&gt;&lt;a href="bulk_and_active_vs_electrode_number.png" class="lightbox" data-gallery="quarto-lightbox-gallery-10" title="Plot of active/bulk ratio over electrode number"&gt;&lt;img src="https://jeffmcbride.net/posts/droplet-capacitance-sensing/bulk_and_active_vs_electrode_number.png" class="img-fluid figure-img" alt="Plot of active/bulk ratio over electrode number"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;figcaption&gt;Plot of active/bulk ratio over electrode number&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/div&gt;
&lt;p&gt;Clearly there is a trend. The important thing to note here: during bulk scan, the electrodes are measured from 127 to 0, so the lower the electrode number, the more time that passes from the electrode being driven negatively to the capacitance measurement – measuring all 128 electrodes takes about 1.8ms. I did some additional experiments where I varied the scan timing, and confirmed that time delay from last activation was indeed a key factor in the decreasing bulk measurement.&lt;/p&gt;
&lt;p&gt;Although much of the activation effect dissipates in just the 1.8ms it takes to do a scan, some residual effect could be seen to persist and slowly decay for several minutes.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="the-history-of-other-electrodes-matters-too" class="level3"&gt;
&lt;h3 class="anchored" data-anchor-id="the-history-of-other-electrodes-matters-too"&gt;The history of other electrodes matters too&lt;/h3&gt;
&lt;p&gt;It also became clear that activating other electrodes which were covered by the same drop as the measured electrode could affect the measurement in ways that were difficult to understand, but seemed to depend on whether the activated electrode came before or after the measured electrode in the scan. Here’s just one illustrative example sequence of activating different electrodes:&lt;/p&gt;
&lt;div class="quarto-figure quarto-figure-center"&gt;
&lt;figure class="figure"&gt;
&lt;p&gt;&lt;a href="before_after_sequence_run2_97-95-64.png" class="lightbox" data-gallery="quarto-lightbox-gallery-11" title="Three nearby electrodes"&gt;&lt;img src="https://jeffmcbride.net/posts/droplet-capacitance-sensing/before_after_sequence_run2_97-95-64.png" class="img-fluid figure-img" alt="Three nearby electrodes"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;figcaption&gt;Three nearby electrodes&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/div&gt;
&lt;p&gt;For these tests I would place a large drop on the board covering a 3x3 square of electrodes, and then pick three electrodes in this square to analyze: The center electrode, one electrode with a higher number (scanned earlier) and one with a lower number (scanned later). The three capacitances are denoted c1-c3, and a1-a3 denote whether each electrode is active (1 means it is active). In the above plot, the electrodes were c1=97, c2=95, c3=64, so they were scanned in the order c1, c2, then c3. It’s difficult to identify the pattern here, but it is clear that neighboring electrodes also matter and that the bulk capacitance measurement is no good without controlling for these effects. I won’t try to recount all of it for you here, but I did a whole series of experiments like this trying to find a clear explanatory pattern and didn’t find it.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="the-effect-is-not-present-with-mylar" class="level3"&gt;
&lt;h3 class="anchored" data-anchor-id="the-effect-is-not-present-with-mylar"&gt;The effect is not present with Mylar&lt;/h3&gt;
&lt;p&gt;All of this was quite frustrating, and then one day quite by accident I discovered that when I used mylar film for the dielectric instead of kapton these effects nearly vanished. If I went looking, I I could identify some ghost of the same effects, but the magnitude was so much smaller that I don’t think I would have ever taken note of it if I had started with the Mylar. This convinced me that it was some property of the Kapton causing it, and from there I switched to using the Mylar film.&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="follow-up-testing-no-hysteresis-found" class="level2"&gt;
&lt;h2 class="anchored" data-anchor-id="follow-up-testing-no-hysteresis-found"&gt;Follow-up testing: No hysteresis found&lt;/h2&gt;
&lt;p&gt;The mystery still gnawed at me – and I did still want to be able to support Kapton for its heat resilience – so I set out to explore it more. I thought it would be easier to understand if I had a little more flexibility in the waveforms I applied than the PurpleDrop hardware allowed, and so I decided I would set up a more isolated experiment focused on exploring the capacitance of the kapton film. I built a &lt;a href="../../high-voltage-amplifier/"&gt;high voltage amplifier&lt;/a&gt; so I could drive arbitrary waveforms, and a small fixture for applying voltages across the dielectric (with or without fluid). Then I set up an Analog Discovery to act as a python-controlled function generator and ADC. I even installed a TE cooler to control temperature of the film.&lt;/p&gt;
&lt;div class="quarto-figure quarto-figure-center"&gt;
&lt;figure class="figure"&gt;
&lt;p&gt;&lt;a href="film_test_stand.jpg" class="lightbox" data-gallery="quarto-lightbox-gallery-12" title="Dielectric Film Test Stand"&gt;&lt;img src="https://jeffmcbride.net/posts/droplet-capacitance-sensing/film_test_stand.jpg" class="img-fluid figure-img" alt="Dielectric Film Test Stand"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;figcaption&gt;Dielectric Film Test Stand&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/div&gt;
&lt;p&gt;After all this preparatory work, finally I set out to do the actual experiment. To my surprise and frustration, I found no sign at all of any hysteresis in the kapton! I tried adding teflon coatings, like it is on the PurpleDrop; still no effect. Suspecting something about my HV function generator setup, I wired in a PurpleDrop so I could drive it with an HV507 just like in the real system. Still nothing. I recall being at a loss for explanation at the time, and by this point my interest in the DMF project was waning. So I more or less dropped it there.&lt;/p&gt;
&lt;p&gt;Every now and then though I wonder about it and feel bad that I never got to the bottom of it. Based on the confusing effects of neighboring electrodes, my best theory today is that there is some form of lateral charge movement going on within the kapton film, and that to recreate the effect I need to include neighboring electrodes to drive as well in my test setup (the test fixture I built had only one electrode). This is pretty speculative, but still it feels promising to me today. Somehow though I don’t remember thinking about this theory at the time. Or maybe I did but just didn’t have time to pursue it, and now I’ve forgotten.&lt;/p&gt;
&lt;p&gt;In any case, I don’t yet know if taking the time to write this down is a preliminary step to recreating a better experiment setup to explore this more, or if it is just idle reminiscence. I’ll have to get back to you on that. In the meantime, if you have any ideas, please email me!&lt;/p&gt;


&lt;/section&gt;


&lt;div id="quarto-appendix" class="default"&gt;&lt;section id="footnotes" class="footnotes footnotes-end-of-document"&gt;&lt;h2 class="anchored quarto-appendix-heading"&gt;Footnotes&lt;/h2&gt;

&lt;ol&gt;
&lt;li id="fn1"&gt;&lt;p&gt;An astute reader may notice that the reset has a voltage divider so that it will reset to a nearly-but-not-quite zero voltage. I believe this is an accidental hold-over from a previous revision which lacked a negative voltage rail for the op-amp, where some saturation recovery time was required after driving the input all the way to the rail. I don’t think it’s necessary at all in this revision – but my memory is weak so maybe I’ve forgotten something.↩︎&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;&lt;/div&gt;</summary><author><name>Jeff McBride</name></author><category term="microfluidics" /><category term="electronics" /><category term="projects" /></entry><entry><id>https://jeffmcbride.net/posts/rust-static-init-with-none/</id><title>Rust static init with None</title><link href="https://jeffmcbride.net/posts/rust-static-init-with-none/" rel="alternate" /><published>2026-02-27T08:00:00Z</published><updated>2026-02-27T08:00:00Z</updated><summary type="html">&lt;p&gt;Today I learned: initializing a rust static with None does NOT ensure a zero initialized variable!&lt;/p&gt;
&lt;section id="static-data-intro" class="level2"&gt;
&lt;h2 class="anchored" data-anchor-id="static-data-intro"&gt;Static data intro&lt;/h2&gt;
&lt;p&gt;I encourage the use of static storage for all of your embedded application data storage. The reason is that then you know pretty well at build time what your RAM usage is and if it will fit, and you have less risk of stack overflow when the stack is used only for small local stuff.&lt;/p&gt;
&lt;p&gt;So, a common pattern is allocating large buffers of data in &lt;code&gt;static&lt;/code&gt; variables. However, rust also has some limits on const functionality which can be used for initialization, and some strict rules about uninitialized variables, so a common pattern is to store your static variables in an Option which starts at None, and gets initialized at startup:&lt;/p&gt;
&lt;div class="code-copy-outer-scaffold"&gt;&lt;div class="sourceCode" id="cb1" style="background: #f1f3f5;"&gt;&lt;pre class="sourceCode rust code-with-copy"&gt;&lt;code class="sourceCode rust"&gt;&lt;span id="cb1-1"&gt;&lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;static&lt;/span&gt; DATA&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;:&lt;/span&gt; Mutex&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;lt;&lt;/span&gt;&lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;Option&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;lt;&lt;/span&gt;MyType&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;gt;&amp;gt;&lt;/span&gt;  &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt; &lt;span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;Mutex::&lt;/span&gt;new(&lt;span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;"&gt;None&lt;/span&gt;)&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;/section&gt;
&lt;section id="bss-vs-data" class="level2"&gt;
&lt;h2 class="anchored" data-anchor-id="bss-vs-data"&gt;BSS vs DATA&lt;/h2&gt;
&lt;p&gt;A key thing to know is that your static variables can end up in two categories:&lt;/p&gt;
&lt;p&gt;BSS is a section of memory which is zero-ized during the runtime init – before your application hits main. DATA is a section of memory that get copied into RAM during the runtime init. The key differentiator is that DATA goes into your flash – it’s part of your code size – while BSS does not.&lt;/p&gt;
&lt;p&gt;Variables which are initialized with ALL zeros go into BSS. Variables which are not, go into DATA. And, there’s no splitting up data, or compression here. If a struct has a size of 1kB, and one byte in the initial value is non zero, all 1kB will be stored into your flash! This can lead to some surprising code bloat, so it’s generally a good idea to try to make sure that your large data types can be zero-initialized where possible.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="is-none-encoded-with-zeros" class="level2"&gt;
&lt;h2 class="anchored" data-anchor-id="is-none-encoded-with-zeros"&gt;Is &lt;code&gt;None&lt;/code&gt; encoded with zeros?&lt;/h2&gt;
&lt;p&gt;If you’d asked me a couple days ago, I’d have said: “yes, I think so!” Now, I know the answer is: “Usually!”&lt;/p&gt;
&lt;p&gt;But today, when I was investigating my code size, I found a surprisingly large symbol in the data section. The size made sense – the struct contained about 5kB worth of CAN message buffering – but it was being initialied with None so why was it going into the &lt;code&gt;.data&lt;/code&gt; section?!&lt;/p&gt;
&lt;p&gt;The answer appears to be that None is usually encoded with zeros, but not &lt;em&gt;always&lt;/em&gt;. For example, there is an exception when wrapping &lt;code&gt;bool&lt;/code&gt; types, which the compiler knows can only have the value of 0 or 1. Since it knows the bool can never be 2, it is free to use the value of 2 as the discriminant for None, and combine the discriminant into the value byte, so it does. This has the benefit that &lt;code&gt;size_of::&amp;lt;Option&amp;lt;bool&amp;gt;&amp;gt;()&lt;/code&gt; is 1, just like the &lt;code&gt;bool&lt;/code&gt;. Otherwise, it would have to prepend an extra discriminant in front of the bool value, making the size of the data type larger in memory.&lt;/p&gt;
&lt;p&gt;My struct, in addition to the CAN message buffers, had an array of bools, and so it seems that the compiler re-arranged the ordering of the struct in memory so that the array of bools came first, and then used a value of 2 as the discriminant for the Option enum, and so my entire 5k struct got initialized from &lt;code&gt;.data&lt;/code&gt;.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="demo-code" class="level2"&gt;
&lt;h2 class="anchored" data-anchor-id="demo-code"&gt;Demo Code&lt;/h2&gt;
&lt;p&gt;I wrote a quick example to demonstrate the behavior:&lt;/p&gt;
&lt;div class="code-copy-outer-scaffold"&gt;&lt;div class="sourceCode" id="cb2" style="background: #f1f3f5;"&gt;&lt;pre class="sourceCode rust code-with-copy"&gt;&lt;code class="sourceCode rust"&gt;&lt;span id="cb2-1"&gt;&lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;use&lt;/span&gt; &lt;span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;core::mem::&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt;size_of&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt; transmute&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;};&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb2-2"&gt;&lt;/span&gt;
&lt;span id="cb2-3"&gt;&lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;struct&lt;/span&gt; Foo &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb2-4"&gt;    buf&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;:&lt;/span&gt; [&lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;u32&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt; &lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;256&lt;/span&gt;]&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb2-5"&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb2-6"&gt;&lt;/span&gt;
&lt;span id="cb2-7"&gt;&lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;struct&lt;/span&gt; FooWithBool &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb2-8"&gt;    value&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;:&lt;/span&gt; &lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;bool&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb2-9"&gt;    buf&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;:&lt;/span&gt; [&lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;u32&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt; &lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;256&lt;/span&gt;]&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb2-10"&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb2-11"&gt;&lt;/span&gt;
&lt;span id="cb2-12"&gt;&lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;struct&lt;/span&gt; FooWithBoolArray &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb2-13"&gt;    value&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;:&lt;/span&gt; [&lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;bool&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt; &lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;4&lt;/span&gt;]&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb2-14"&gt;    buf&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;:&lt;/span&gt; [&lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;u32&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt; &lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;256&lt;/span&gt;]&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb2-15"&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb2-16"&gt;&lt;/span&gt;
&lt;span id="cb2-17"&gt;&lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;static&lt;/span&gt; FOO&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;:&lt;/span&gt; &lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;Option&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;lt;&lt;/span&gt;Foo&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;gt;&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt; &lt;span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;"&gt;None&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb2-18"&gt;&lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;static&lt;/span&gt; FOO_WITH_BOOL&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;:&lt;/span&gt; &lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;Option&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;lt;&lt;/span&gt;FooWithBool&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;gt;&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt; &lt;span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;"&gt;None&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb2-19"&gt;&lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;static&lt;/span&gt; FOO_WITH_BOOL_ARRAY&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;:&lt;/span&gt; &lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;Option&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;lt;&lt;/span&gt;FooWithBoolArray&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;gt;&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt; &lt;span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;"&gt;None&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb2-20"&gt;&lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;static&lt;/span&gt; BOOL&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;:&lt;/span&gt; &lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;Option&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;lt;&lt;/span&gt;&lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;bool&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;gt;&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt; &lt;span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;"&gt;None&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb2-21"&gt;&lt;/span&gt;
&lt;span id="cb2-22"&gt;&lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;fn&lt;/span&gt; main() &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb2-23"&gt;    &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;const&lt;/span&gt; FOO_SIZE&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;:&lt;/span&gt; &lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;usize&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt; &lt;span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;size_of::&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;lt;&lt;/span&gt;Foo&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;gt;&lt;/span&gt;()&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb2-24"&gt;    &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;const&lt;/span&gt; FWB_SIZE&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;:&lt;/span&gt; &lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;usize&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt; &lt;span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;size_of::&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;lt;&lt;/span&gt;FooWithBool&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;gt;&lt;/span&gt;()&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb2-25"&gt;    &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;const&lt;/span&gt; FWBA_SIZE&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;:&lt;/span&gt; &lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;usize&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt; &lt;span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;size_of::&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;lt;&lt;/span&gt;FooWithBoolArray&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;gt;&lt;/span&gt;()&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb2-26"&gt;    &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;const&lt;/span&gt; BOOL_SIZE&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;:&lt;/span&gt; &lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;usize&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt; &lt;span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;size_of::&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;lt;&lt;/span&gt;&lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;Option&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;lt;&lt;/span&gt;&lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;bool&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;gt;&amp;gt;&lt;/span&gt;()&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb2-27"&gt;    &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;let&lt;/span&gt; foo_data&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;:&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;amp;&lt;/span&gt;[&lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;u8&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt; FOO_SIZE] &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt; &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;unsafe&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt; transmute(&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;amp;&lt;/span&gt;FOO) &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;};&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb2-28"&gt;    &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;let&lt;/span&gt; fwb_data&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;:&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;amp;&lt;/span&gt;[&lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;u8&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt; FWB_SIZE] &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt; &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;unsafe&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt; transmute(&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;amp;&lt;/span&gt;FOO_WITH_BOOL) &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;};&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb2-29"&gt;    &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;let&lt;/span&gt; fwba_data&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;:&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;amp;&lt;/span&gt;[&lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;u8&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt; FWBA_SIZE] &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt; &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;unsafe&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt; transmute(&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;amp;&lt;/span&gt;FOO_WITH_BOOL_ARRAY) &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;};&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb2-30"&gt;    &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;let&lt;/span&gt; bool_data&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;:&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;amp;&lt;/span&gt;[&lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;u8&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt; BOOL_SIZE] &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt; &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;unsafe&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt; transmute(&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;amp;&lt;/span&gt;BOOL) &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;};&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb2-31"&gt;&lt;/span&gt;
&lt;span id="cb2-32"&gt;    &lt;span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;// Show the first 12 bytes of each&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb2-33"&gt;    &lt;span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;println!&lt;/span&gt;(&lt;span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;"&gt;"size_of Option&amp;lt;Foo&amp;gt;: {}"&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt; FOO_SIZE)&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb2-34"&gt;    &lt;span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;println!&lt;/span&gt;(&lt;span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;"&gt;"size_of Option&amp;lt;FooWithBool&amp;gt;: {}"&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt; FWB_SIZE)&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb2-35"&gt;    &lt;span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;println!&lt;/span&gt;(&lt;span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;"&gt;"size_of Option&amp;lt;FooWithBoolArray&amp;gt;: {}"&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt; FWBA_SIZE)&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb2-36"&gt;    &lt;span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;println!&lt;/span&gt;(&lt;span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;"&gt;"size_of Option&amp;lt;bool&amp;gt;: {}"&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt; BOOL_SIZE)&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb2-37"&gt;    &lt;span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;println!&lt;/span&gt;(&lt;span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;"&gt;"FOO:           {:?}"&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;amp;&lt;/span&gt;foo_data[&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;..&lt;/span&gt;&lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;12&lt;/span&gt;])&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb2-38"&gt;    &lt;span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;println!&lt;/span&gt;(&lt;span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;"&gt;"FOO_WITH_BOOL: {:?}"&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;amp;&lt;/span&gt;fwb_data[&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;..&lt;/span&gt;&lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;12&lt;/span&gt;])&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb2-39"&gt;    &lt;span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;println!&lt;/span&gt;(&lt;span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;"&gt;"FOO_WITH_BOOL_ARRAY: {:?}"&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;amp;&lt;/span&gt;fwba_data[&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;..&lt;/span&gt;&lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;12&lt;/span&gt;])&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb2-40"&gt;    &lt;span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;println!&lt;/span&gt;(&lt;span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;"&gt;"BOOL: {:?}"&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt; bool_data)&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb2-41"&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;You can also run this yourself on the &lt;a href="https://play.rust-lang.org/?version=stable&amp;amp;mode=debug&amp;amp;edition=2024&amp;amp;gist=24f8ec1501a93c7bff413825b5345b13"&gt;Rust Playground&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The output?&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;size_of Option&amp;lt;Foo&amp;gt;: 1024
size_of Option&amp;lt;FooWithBool&amp;gt;: 1028
size_of Option&amp;lt;FooWithBoolArray&amp;gt;: 1028
size_of Option&amp;lt;bool&amp;gt;: 1
FOO:           [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
FOO_WITH_BOOL: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
FOO_WITH_BOOL_ARRAY: [2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
BOOL: [2]&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see, with a plain bool, you get a non-zero encoding, as with the array of bools. A single bool in the struct does NOT result in non-zero None encoding, I think because it had to pad the bool anyway, so the 3 extra padding bytes can be used to store the discriminant.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="the-solution" class="level2"&gt;
&lt;h2 class="anchored" data-anchor-id="the-solution"&gt;The solution&lt;/h2&gt;
&lt;p&gt;The only reliable solution I can think of to ensure a static var initializes with 0 is to use &lt;a href="https://doc.rust-lang.org/std/mem/union.MaybeUninit.html"&gt;&lt;code&gt;MaybeUninit&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The &lt;a href="https://docs.rs/static_cell/latest/static_cell/index.html"&gt;&lt;code&gt;static_cell&lt;/code&gt; crate&lt;/a&gt; handles this well for you when you just need a local &lt;code&gt;&amp;amp;mut&lt;/code&gt;. It will not really help you share the static with an IRQ.&lt;/p&gt;


&lt;/section&gt;</summary><category term="embedded" /><category term="rust" /></entry><entry><id>https://jeffmcbride.net/posts/2025-book-reviews/</id><title>2025 Book Reviews</title><link href="https://jeffmcbride.net/posts/2025-book-reviews/" rel="alternate" /><published>2025-12-30T08:00:00Z</published><updated>2025-12-30T08:00:00Z</updated><summary type="html">&lt;p&gt;With 2025 coming to a close, I’m going through the annual tradition of reflecting on, and summarizing the year. One of the things I’m reviewing is my reading log, so here’s a few of the books I read this year that most standout in my memory.&lt;/p&gt;
&lt;section id="the-bobiverse-series" class="level2"&gt;
&lt;h2 class="anchored" data-anchor-id="the-bobiverse-series"&gt;The Bobiverse Series&lt;/h2&gt;
&lt;div class="quarto-figure quarto-figure-center"&gt;
&lt;figure class="figure"&gt;
&lt;p&gt;&lt;a href="we_are_legion.png" class="lightbox" data-gallery="quarto-lightbox-gallery-1" title="We are Legion cover"&gt;&lt;img src="https://jeffmcbride.net/posts/2025-book-reviews/we_are_legion.png" class="img-fluid figure-img" width="250" alt="We are Legion cover"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;figcaption&gt;We are Legion cover&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/div&gt;
&lt;p&gt;A 5-book series by Dennis E. Taylor.&lt;/p&gt;
&lt;p&gt;This is almost certainly the most fun read on this list. I bought the first book on a recommendation from my brother, and then went through all five books over the next couple months. It’s a light, somewhat corny sci-fi story based on a formerly-human “replicant” named Bob – his mind uploaded onto some specialized hardware, he’s sort of an AI with human memories – who is placed in command of a self-reproducing Von Neumann probe and sets off to explore the galaxy. Over the centuries, he ends up building a whole new post-organic civilization.&lt;/p&gt;
&lt;p&gt;Apparently, book 6 &lt;a href="http://dennisetaylor.org/status-of-things/"&gt;is coming&lt;/a&gt;, and I look forward to it.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="the-control-of-nature" class="level2"&gt;
&lt;h2 class="anchored" data-anchor-id="the-control-of-nature"&gt;The Control of Nature&lt;/h2&gt;
&lt;div class="quarto-figure quarto-figure-center"&gt;
&lt;figure class="figure"&gt;
&lt;p&gt;&lt;a href="control_of_nature.jpg" class="lightbox" data-gallery="quarto-lightbox-gallery-2" title="Control of Nature cover"&gt;&lt;img src="https://jeffmcbride.net/posts/2025-book-reviews/control_of_nature.jpg" class="img-fluid figure-img" width="250" alt="Control of Nature cover"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;figcaption&gt;Control of Nature cover&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/div&gt;
&lt;p&gt;This is a book of three journalistic essays by John McPhee, published in 1989.&lt;/p&gt;
&lt;p&gt;The essays are:&lt;/p&gt;
&lt;ol type="1"&gt;
&lt;li&gt;A story of the Army Corp of Engineers and it’s struggles to keep the Mississippi river on its course.&lt;/li&gt;
&lt;li&gt;The 1973 Heimaey eruption in Iceland, and how a ragtag team worked to aquire every pump they could to move enough sea-water to redirect the lava flows and save the harbor.&lt;/li&gt;
&lt;li&gt;Debri flows in the San Gabriel mountains around Los Angeles, and the cities struggle to contain them&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The Icelandic eruption was my favorite. The stories of walking, driving bull dozers, and keeping hoses running across the barely solidified lava fields as things spontaneously caught on fire and lava bombs fell from the sky are great. I ended up visiting Iceland shortly after reading it, and was disappointed I didn’t have time to take the ferry to the island where it took place.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="the-nvidia-way" class="level2"&gt;
&lt;h2 class="anchored" data-anchor-id="the-nvidia-way"&gt;The Nvidia Way&lt;/h2&gt;
&lt;div class="quarto-figure quarto-figure-center"&gt;
&lt;figure class="figure"&gt;
&lt;p&gt;&lt;a href="the_nvidia_way.jpg" class="lightbox" data-gallery="quarto-lightbox-gallery-3" title="The Nvidia Way cover"&gt;&lt;img src="https://jeffmcbride.net/posts/2025-book-reviews/the_nvidia_way.jpg" class="img-fluid figure-img" width="250" alt="The Nvidia Way cover"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;figcaption&gt;The Nvidia Way cover&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/div&gt;
&lt;p&gt;A profile of Jensen Huang and the rise of Nvidia by Tae Kim. I found this to be a very well written book filled with interesting details and stories. I think this kind of book can easily be very boring, but this one really held my attention, and I learned some things.&lt;/p&gt;


&lt;/section&gt;</summary><category term="books" /></entry><entry><id>https://jeffmcbride.net/posts/pdo-defaults/</id><title>Adding PDO Defaults to zencan</title><link href="https://jeffmcbride.net/posts/pdo-defaults/" rel="alternate" /><published>2025-12-28T08:00:00Z</published><updated>2025-12-28T08:00:00Z</updated><summary type="html">&lt;p&gt;Adding a simple feature to zencan brought up some thorny topics.&lt;/p&gt;
&lt;section id="the-original-problem" class="level2"&gt;
&lt;h2 class="anchored" data-anchor-id="the-original-problem"&gt;The original problem&lt;/h2&gt;
&lt;p&gt;In zencan, objects have a default value. Of course they do – they have to start with something! Often times that’s just 0, but some objects should be initialized with particular values. For “application objects” – the custom objects created in the application-specific “device config” file – the default value can be specified as part of the object definition. However, some objects are defined automatically for the application by the code generation system, including the PDO configuration objects. Until just recently, there wasn’t any way to specify the defaults for these.&lt;/p&gt;
&lt;p&gt;PDO config objects (there are two for each PDO, called the “comm”, and “mapping” objects) control how PDO messages are sent and received, and which data objects the PDO payload bytes are mapped to. Up to now, I’ve configured PDOs by writing to the comm and mapping objects via the SDO server, and then saving those values to flash. That’s great and this type of run-time configurability is a key motivator for the project, but for some applications it makes a lot of sense to boot up with pre-configured PDOs, and so zencan needed a mechanism to specify defaults.&lt;/p&gt;
&lt;p&gt;So, easy enough: I’ll add some attributes in the &lt;code&gt;[pdos]&lt;/code&gt; section of the device config to set the defaults, I figured. I already have a schema used in the “node configuration” files, which are used by zencan-client to do the run-time configuration. It looks like this:&lt;/p&gt;
&lt;div class="code-copy-outer-scaffold"&gt;&lt;div class="sourceCode" id="cb1" style="background: #f1f3f5;"&gt;&lt;pre class="sourceCode toml code-with-copy"&gt;&lt;code class="sourceCode toml"&gt;&lt;span id="cb1-1"&gt;&lt;span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;# Configure TPDO1 to send on CAN ID 0x200&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb1-2"&gt;&lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;[tpdo.1]&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb1-3"&gt;&lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;enabled&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt; &lt;span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;"&gt;true&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb1-4"&gt;&lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;cob&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt; &lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;0x200&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb1-5"&gt;&lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;transmission_type&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt; &lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;254&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb1-6"&gt;&lt;span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;# Fill the PDO data with 4 bytes from object 0x2000sub1 and 4 more from 0x2000sub2&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb1-7"&gt;&lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;mappings&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;[&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb1-8"&gt;    &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;{ &lt;/span&gt;&lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;index&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt;&lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;0x2000&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;, &lt;/span&gt;&lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;sub&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt;&lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;1&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;, &lt;/span&gt;&lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;size&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt;&lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;32&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt; },&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb1-9"&gt;    &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;{ &lt;/span&gt;&lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;index&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt;&lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;0x2000&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;, &lt;/span&gt;&lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;sub&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt;&lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;2&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;, &lt;/span&gt;&lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;size&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt;&lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;32&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt; },&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb1-10"&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;“This will be quick and easy”, I figured. It was not.&lt;/p&gt;
&lt;p&gt;Objects in zencan are defined as static variables in generated code. Then references to all the objects are put into a list of &lt;code&gt;ODEntry&lt;/code&gt; objects. It looks something like this:&lt;/p&gt;
&lt;div class="code-copy-outer-scaffold"&gt;&lt;div class="sourceCode" id="cb2" style="background: #f1f3f5;"&gt;&lt;pre class="sourceCode rust code-with-copy"&gt;&lt;code class="sourceCode rust"&gt;&lt;span id="cb2-1"&gt;&lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;pub&lt;/span&gt; &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;static&lt;/span&gt; RPDO_COMM_OBJECTS&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;:&lt;/span&gt; [PdoCommObject&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt; &lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;1usize&lt;/span&gt;] &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt; [&lt;/span&gt;
&lt;span id="cb2-2"&gt;    &lt;span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;PdoCommObject::&lt;/span&gt;new(&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;amp;&lt;/span&gt;NODE_STATE&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;.&lt;/span&gt;rpdos()[&lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;0usize&lt;/span&gt;])&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb2-3"&gt;]&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb2-4"&gt;&lt;/span&gt;
&lt;span id="cb2-5"&gt;&lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;pub&lt;/span&gt; &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;static&lt;/span&gt; OBJECT1000&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;:&lt;/span&gt; Object1000 &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt; &lt;span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;Object1000::&lt;/span&gt;&lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;default&lt;/span&gt;()&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb2-6"&gt;&lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;pub&lt;/span&gt; &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;static&lt;/span&gt; OBJECT1001&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;:&lt;/span&gt; Object1001 &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt; &lt;span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;Object1001::&lt;/span&gt;&lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;default&lt;/span&gt;()&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb2-7"&gt;&lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;pub&lt;/span&gt; &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;static&lt;/span&gt; OD_TABLE&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;:&lt;/span&gt; [ODEntry&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt; &lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;3usize&lt;/span&gt;] &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt; [&lt;/span&gt;
&lt;span id="cb2-8"&gt;    ODEntry &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb2-9"&gt;        index&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;:&lt;/span&gt; &lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;0x1000&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb2-10"&gt;        data&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;:&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;amp;&lt;/span&gt;OBJECT1000&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb2-11"&gt;    &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;},&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb2-12"&gt;    ODEntry &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb2-13"&gt;        index&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;:&lt;/span&gt; &lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;0x1001&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb2-14"&gt;        data&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;:&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;amp;&lt;/span&gt;OBJECT1001&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb2-15"&gt;    &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;},&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb2-16"&gt;    ODEntry &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb2-17"&gt;        index&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;:&lt;/span&gt; &lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;0x1400&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb2-18"&gt;        data&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;:&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;amp;&lt;/span&gt;RPDO_COMM_OBJECTS[&lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;0usize&lt;/span&gt;]&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb2-19"&gt;    &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;},&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb2-20"&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Defaults are just rust initializers. And this is where the first issue popped up.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="non-const-init" class="level2"&gt;
&lt;h2 class="anchored" data-anchor-id="non-const-init"&gt;Non-const init&lt;/h2&gt;
&lt;p&gt;The first problem you hit here is that when you write to the mapping object, you write a u32 referencing the object-to-be-mapped by index and sub-index. But what the PDO actually stores is a reference to the object, so that it doesn’t have to search through the table every time it has to load/store data. The data stored for each mapping looks like this:&lt;/p&gt;
&lt;div class="code-copy-outer-scaffold"&gt;&lt;div class="sourceCode" id="cb3" style="background: #f1f3f5;"&gt;&lt;pre class="sourceCode rust code-with-copy"&gt;&lt;code class="sourceCode rust"&gt;&lt;span id="cb3-1"&gt;&lt;span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;/// Data structure for storing a PDO object mapping&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-2"&gt;&lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;struct&lt;/span&gt; MappingEntry &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-3"&gt;    &lt;span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;/// A reference to the object which is mapped&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-4"&gt;    &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;pub&lt;/span&gt; object&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;:&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;amp;&lt;/span&gt;&lt;span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;"&gt;'static&lt;/span&gt; ODEntry&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;lt;&lt;/span&gt;&lt;span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;"&gt;'static&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-5"&gt;    &lt;span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;/// The index of the sub object mapped&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-6"&gt;    &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;pub&lt;/span&gt; sub&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;:&lt;/span&gt; &lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;u8&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-7"&gt;    &lt;span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;/// The length of the mapping in bytes&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-8"&gt;    &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;pub&lt;/span&gt; length&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;:&lt;/span&gt; &lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;u8&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-9"&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Strictly speaking, the code generation “knows” the name and ID of every object, so it could probably generate const MappingEntry objects with the correct static references. But this is going to really complicated the already too complicated code generation code, and also yet another datatype that should be private has to become public, because the generated code now has to know more about the internals of the PDO object implementation.&lt;/p&gt;
&lt;p&gt;But there’s a bigger reason this isn’t worth pursuing: Eventually zencan needs to support restoring defaults at run-time for all objects. So objects need to store a live working value as well as the default value for re-initializing.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="duplicating-memory" class="level2"&gt;
&lt;h2 class="anchored" data-anchor-id="duplicating-memory"&gt;Duplicating memory&lt;/h2&gt;
&lt;p&gt;This raises a new concern: to restore at default, there is a requirement that both the default value and the live value be stored somewhere. But, ideally, the default value will be stored exactly once, and only in flash, while the live value will also be stored exactly once, and in RAM.&lt;/p&gt;
&lt;p&gt;This led down a new rabbit hole of testing what scenarios rust will place data into the &lt;code&gt;data&lt;/code&gt; section vs &lt;code&gt;bss&lt;/code&gt;. The &lt;code&gt;data&lt;/code&gt; section is part of the program flash size and static RAM allocation – it is copied to RAM to initialize the variables. The bss section is just zero’d, so it doesn’t have corresponding bytes in flash, making the program size smaller. In order for an object to live in &lt;code&gt;bss&lt;/code&gt;, it needs to be initialized with all zeros. Incidentally, if one of the attributes of your object is a reference, it cannot be initialized with zeros.&lt;/p&gt;
&lt;p&gt;There are other concerns, like in the case of PDOs, they have to allocate RAM storage for the maximum allowed number of mappings, but in flash, it would be better if the defaults were stored as variably-sized slices, so only the number of mappings initialized in the default have to take up program space.&lt;/p&gt;
&lt;p&gt;I suspect that in the future, this will be revisted, and I think the path is going to be to enforce that the object and the object dict are private and can only be accessed via a getter function which can ensure a one-time lazy initialization process is observed. This will allow things like &lt;code&gt;MaybeUnininit&amp;lt;&amp;amp;'static Pdo&amp;gt;&lt;/code&gt; where needed, to ensure that objects can go into BSS.&lt;/p&gt;
&lt;p&gt;But as spun in a few circles on this, I realized that I was falling into a classic scope creep trap that I am very susceptible to: instead of finishing the PDO default feature I set out to implement I was refactoring all object initialization which could better be done in a follow-on PR. So I’m leaving this for later, and accepting that the first pass at PDO defaults may use more flash bytes than strictly necessary.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="defaults-as-a-function-of-runtime-state" class="level2"&gt;
&lt;h2 class="anchored" data-anchor-id="defaults-as-a-function-of-runtime-state"&gt;Defaults as a function of runtime state&lt;/h2&gt;
&lt;p&gt;It is standard in CANOpen devices for the default PDO COB IDs to be based on the configured node ID, e.g.&amp;nbsp;&lt;code&gt;$NODEID+0x180&lt;/code&gt;. This makes a lot of sense: you might have a device that puts out some PDOs by default, and you might put more than one of those devices on the network, so zencan should support this! This means that the default we wish to load isn’t even known at compile time.&lt;/p&gt;
&lt;p&gt;Also! The main unit of information is the object data – i.e.&amp;nbsp;the bytes stored in the objects. This is what remote clients can access via the SDO server, and this is what is stored in flash when settings are persisted. So lets say our node ID is 1 and the default initializer has set the COB ID to 0x181. Now, lets say we save all the objects to flash. Then we change the node ID to 2 and reset. Should the new COB ID be 0x182? That’s what I would expect! The user never changed it from the default, which is &lt;code&gt;$NODEID+0x180&lt;/code&gt;, and now &lt;code&gt;$NODEID=2&lt;/code&gt;, so &lt;code&gt;0x182&lt;/code&gt;. But what we read from flash for the COMM object is &lt;code&gt;0x181&lt;/code&gt;. Did the user write this value, or was it a default calculation? We now have to somehow distinguish these cases.&lt;/p&gt;
&lt;p&gt;We could track whether the default COMM value has been changed, and then skip storing it to flash if it has not been changed from the default. That would help. But as things are setup currently, the object can’t distinguish between a default being written and a value written from e.g.&amp;nbsp;an SDO client.&lt;/p&gt;
&lt;p&gt;Speaking of SDO clients: ideally, when they read the PDO configuration they could tell the difference between a COB ID which is set to &lt;code&gt;0x181&lt;/code&gt; and a COB ID which is set to &lt;code&gt;$NODEID+0x180&lt;/code&gt;. &lt;em&gt;Ideally&lt;/em&gt; they’d even be able to configure a COB ID to use a node ID offset at runtime. If I was designing this protocol, I’d put a flag in the PDO COMM object called &lt;code&gt;add_node_id&lt;/code&gt; to control this behavior so that the state is encoded in the object.&lt;/p&gt;
&lt;p&gt;As I have done before when I have found the CANOpen protocol confounding, I went and and looked at what the most popular C implementation does (&lt;a href="https://github.com/CANopenNode/CANopenNode/blob/master/301/CO_PDO.c#L679"&gt;relevant file&lt;/a&gt;).&lt;/p&gt;
&lt;div class="code-copy-outer-scaffold"&gt;&lt;div class="sourceCode" id="cb4" style="background: #f1f3f5;"&gt;&lt;pre class="sourceCode c code-with-copy"&gt;&lt;code class="sourceCode c"&gt;&lt;span id="cb4-1"&gt;&lt;span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;/* if default CAN-ID is written, store to OD without Node-ID */&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb4-2"&gt;&lt;span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;if&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;(&lt;/span&gt;CAN_ID &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;==&lt;/span&gt; PDO&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;-&amp;gt;&lt;/span&gt;preDefinedCanId&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;)&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb4-3"&gt;    &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;(&lt;/span&gt;&lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;void&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;)&lt;/span&gt;CO_setUint32&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;(&lt;/span&gt;bufCopy&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt; COB_ID &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;amp;&lt;/span&gt; &lt;span class="bn" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;0xFFFFFF80&lt;/span&gt;&lt;span class="bu" style="color: null;
background-color: null;
font-style: inherit;"&gt;U&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb4-4"&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-copy-outer-scaffold"&gt;&lt;div class="sourceCode" id="cb5" style="background: #f1f3f5;"&gt;&lt;pre class="sourceCode c code-with-copy"&gt;&lt;code class="sourceCode c"&gt;&lt;span id="cb5-1"&gt;&lt;span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;/* If default CAN-ID is stored in OD (without Node-ID), add Node-ID */&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb5-2"&gt;&lt;span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;if&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;((&lt;/span&gt;CAN_ID &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;!=&lt;/span&gt; &lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;0&lt;/span&gt;&lt;span class="bu" style="color: null;
background-color: null;
font-style: inherit;"&gt;U&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;)&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;(&lt;/span&gt;CAN_ID &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;==&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;(&lt;/span&gt;preDefinedCanId &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;amp;&lt;/span&gt; &lt;span class="bn" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;0xFF80&lt;/span&gt;&lt;span class="bu" style="color: null;
background-color: null;
font-style: inherit;"&gt;U&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;)))&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb5-3"&gt;    CAN_ID &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt; preDefinedCanId&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb5-4"&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;To understand the above snippets, you should know that (sticking with &lt;code&gt;$NODEID=1&lt;/code&gt; and &lt;code&gt;COB_ID=$NODEID + 0x180&lt;/code&gt; as an example) &lt;code&gt;preDefinedCanId&lt;/code&gt; is set to 0x181. If the value encoded in the object is &lt;code&gt;0x180&lt;/code&gt;, it always adds the node id. If a user writes &lt;code&gt;0x181&lt;/code&gt;, it assumes that what the user really meant was &lt;code&gt;$NODEID + 0x180&lt;/code&gt; and stores &lt;code&gt;0x180&lt;/code&gt; to the object. So if I try to write 0x181, it gets converted to 0x180. And when the stored value is 0x180, that is interpreted as the default &lt;code&gt;$NODEID+0x180&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;I don’t love this. It’s a &lt;em&gt;special rule&lt;/em&gt;, and it’s a &lt;em&gt;surprising behavior&lt;/em&gt;. It also makes it impossible to use 0x180 as a COB ID, or to set the COB ID to &lt;code&gt;0x181&lt;/code&gt; irrespective of the node ID. If I write 0x181 to the COB ID, I expect that to be the new COB ID even if I change the node ID. It’s not hard to come up with a use case where this actually becomes a problem:&lt;/p&gt;
&lt;p&gt;Imagine some device expects a particular message on 0x181 because historically it was sent by node 1 using the default. I come along and decide that I’m going to manage my network by serial number, and I don’t care about what node IDs are assigned, so I write a controller which enumerates the nodes via LSS, and assigns them arbitrary IDs, but loads configurations into each node based on serial number. I want a node with a particular serial number to send the 0x181 PDO, so I set it up that way, only to find that when assigned a random node ID it is suddenly sending the PDO &lt;em&gt;on a different ID!&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Also I think ideally it should be possible to set &lt;em&gt;default&lt;/em&gt; PDO IDs which do not have the node ID added. A realistic use case is: “this type of device always sends this message, regardless of the assigned node ID”. But this precludes any default initialization scheme which is limited to restoring object values, because the answer to the question “should I add the node ID” is not encoded anywhere in the object values. This is the same situation I’m sure the CANOpenNode author(s) were faced with when they decided to enact the special rules above.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="options" class="level2"&gt;
&lt;h2 class="anchored" data-anchor-id="options"&gt;Options&lt;/h2&gt;
&lt;p&gt;Once again I am tempted to throw out the CANOpen spec and go my own way, but I’m not quite there yet. Some options considered:&lt;/p&gt;
&lt;ol type="1"&gt;
&lt;li&gt;Use COB_ID=0 as a special value indicating to use the default. The default has to be stored outside of the object data, and serialization to persistent storage has to be prevented when the value is 0 (because serializing just reads the object, and reading the object returns the computed value).&lt;/li&gt;
&lt;li&gt;Adopt the special rules of CANOpenNode.&lt;/li&gt;
&lt;li&gt;Drop support for adding node ID to PDO IDs altogether.&lt;/li&gt;
&lt;li&gt;Add a new sub object to the PDO comm object to store a flag for “add the node ID” behavior.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I think 4 is clearly the ideal. Doing anything else feels like I am compromising for the sake of an old protocol, but also, if I add a sub object then someone, someday is going to use zencan to try to do a CANOpen compliance certification and will fail and be annoyed with me.&lt;/p&gt;
&lt;p&gt;Number 2 and 3 I think are non-starters. The NODE ID offsets are standard and useful, and the special rules of number 2 are just too weird of a corner case for my taste.&lt;/p&gt;
&lt;p&gt;So number 1 seems like the most plausible. It allows assigning any ID to the PDO via SDO, and it allows defaults to include the node ID or not. It does not allow an SDO client to distinguish between default and non-default state in general, and it does not allow setting arbitrary &lt;code&gt;$NODEID + x&lt;/code&gt; values for the COB ID, but this is not an immediate problem at least. Also, I think it is still relatively easy in the future to add an optional extension sub-object to the COMM object with the &lt;code&gt;add_node_id&lt;/code&gt; flag if the need ever really does arise.&lt;/p&gt;


&lt;/section&gt;</summary><category term="zencan" /><category term="embedded" /><category term="rust" /></entry><entry><id>https://jeffmcbride.net/posts/zencan-crates-published/</id><title>Publishing zencan crates</title><link href="https://jeffmcbride.net/posts/zencan-crates-published/" rel="alternate" /><published>2025-10-29T07:00:00Z</published><updated>2025-10-29T07:00:00Z</updated><summary type="html">&lt;p&gt;I’ve finally published v0.0.1 of zencan to crates.io.&lt;/p&gt;
&lt;p&gt;After a big push back in June to get to something that more or less worked, I published &lt;a href="../../blog/2025/06/05/introducing-zencan"&gt;zencan&lt;/a&gt; on github and then proceeded to lose momentum for a while. My attention wandered to other things, and I’ve been traveling a lot. But in the meantime, it has been out there, and I can tell that at least a few people have been trying to use it, because some of them have reached out to me about it. The project even picked up a second contributor: Ettiene &lt;a href="https://github.com/mcbridejc/zencan/pull/22"&gt;provided some examples for the esp32&lt;/a&gt;, as well as fixing some bugs. I can also see from the search traffic that people are out there searching for “rust canopen”. All of this is good motivation to put more work into the project.&lt;/p&gt;
&lt;p&gt;I put off publishing the crates initially because I was hoping to resolve some changes to a dependency I had forked. After radio silence from the maintainer&lt;sup&gt;1&lt;/sup&gt;, I ended up &lt;a href="https://github.com/mcbridejc/zencan/pull/25"&gt;working around that&lt;/a&gt; easily enough. Beyond that, it has just been a nagging sense of perfectionism – there are more features I should implement or better docs to write before it was worth publishing a crate. Somehow pushing the crate feels more symbolic than throwing some code onto a github repo. It feels like more of a statement that “this is actually good enought to use”.&lt;/p&gt;
&lt;p&gt;But I decided it will be fine, and I think the practice of releases, writing changelogs, considering breaking changes more, etc will be good for the process.&lt;/p&gt;
&lt;p&gt;Also! The timing is good now because rust v1.90 stabilized &lt;a href="https://github.com/rust-lang/cargo/pull/15636/"&gt;multi package publishing&lt;/a&gt;! One of the authors of this feature wrote a &lt;a href="https://www.tweag.io/blog/2025-07-10-cargo-package-workspace/"&gt;good blog post&lt;/a&gt; about it. Zencan is broken up into many crates, so this made the releasing process a lot easier.&lt;/p&gt;
&lt;p&gt;Now there are more features to work on. Top of the probable list are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;CAN-FD support&lt;/li&gt;
&lt;li&gt;Refactoring the device config / object dictionary management code, and adding EDS export&lt;/li&gt;
&lt;li&gt;SDO Client Updates
&lt;ul&gt;
&lt;li&gt;blocking client support&lt;/li&gt;
&lt;li&gt;no_std client support&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;SYNC support (client and node)&lt;/li&gt;
&lt;/ul&gt;




&lt;div id="quarto-appendix" class="default"&gt;&lt;section id="footnotes" class="footnotes footnotes-end-of-document"&gt;&lt;h2 class="anchored quarto-appendix-heading"&gt;Footnotes&lt;/h2&gt;

&lt;ol&gt;
&lt;li id="fn1"&gt;&lt;p&gt;Not trying to throw any shade. I have been in that position, and will likely be again. Publishing some open source code doesn’t obligate someone to support it for randos like me forever.↩︎&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;&lt;/div&gt;</summary><category term="zencan" /><category term="embedded" /><category term="rust" /></entry><entry><id>https://jeffmcbride.net/posts/building-a-rust-application-for-the-coremp135-using-slint/</id><title>Building an application for the CoreMP135 using Rust and Slint</title><link href="https://jeffmcbride.net/posts/building-a-rust-application-for-the-coremp135-using-slint/" rel="alternate" /><published>2025-10-27T07:00:00Z</published><updated>2025-10-27T07:00:00Z</updated><summary type="html">&lt;p&gt;This is a followup to &lt;a href="../../blog/2025/10/25/coremp135-linux-device-buildroot/"&gt;Creating linux images for the M5Stack CoreMP135 with Buildroot&lt;/a&gt;. The last post talked about using buildroot to make a linux image, and this post is about building an application in Rust and running it on the image. The demo application is a CAN logger. It will use Slint to create a simple UI on the touchscreen to start and stop the log file, and it will log data to an exFAT partition on the microSD card.&lt;/p&gt;
&lt;section id="getting-code-onto-the-device" class="level2"&gt;
&lt;h2 class="anchored" data-anchor-id="getting-code-onto-the-device"&gt;Getting code onto the device&lt;/h2&gt;
&lt;p&gt;If you successfully built an image using &lt;a href="https://github.com/mcbridejc/coremp135-bsp"&gt;&lt;code&gt;coremp135-bsp&lt;/code&gt;&lt;/a&gt;, and you added your key to the &lt;code&gt;authorized_keys&lt;/code&gt; file on the target, you should be able to SSH to it by connecting to ETH1 and using the static IP configured on it – &lt;code&gt;192.168.2.254&lt;/code&gt;, or whatever you customized it to.&lt;/p&gt;
&lt;p&gt;Then you can copy files to it with scp, e.g.&amp;nbsp;&lt;code&gt;scp -O my_file root@192.168.2.254:/root/&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;I like to add the following entry to &lt;code&gt;~/.ssh/config&lt;/code&gt; to simplify connecting:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;host coremp135
    HostName 192.168.2.254
    StrictHostKeyChecking no
    UpdateHostkeys yes
    User root&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;From here on out, you may see me reducing transfers to &lt;code&gt;scp -O my_file coremp135:/root/&lt;/code&gt;; this is why.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="cross-compiling" class="level2"&gt;
&lt;h2 class="anchored" data-anchor-id="cross-compiling"&gt;Cross Compiling&lt;/h2&gt;
&lt;p&gt;First of all, lets just get Hello World building.&lt;/p&gt;
&lt;p&gt;Create a new project: &lt;code&gt;cargo init&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Add a &lt;code&gt;rust-toolchain.toml&lt;/code&gt; file with the following:&lt;/p&gt;
&lt;div class="code-copy-outer-scaffold"&gt;&lt;div class="sourceCode" id="cb2" style="background: #f1f3f5;"&gt;&lt;pre class="sourceCode toml code-with-copy"&gt;&lt;code class="sourceCode toml"&gt;&lt;span id="cb2-1"&gt;&lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;[toolchain]&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb2-2"&gt;&lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;channel&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt; &lt;span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;"&gt;"1.90.0"&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb2-3"&gt;&lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;targets&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;[&lt;/span&gt; &lt;span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;"&gt;"armv7-unknown-linux-gnueabihf"&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb2-4"&gt;&lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;profile&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt; &lt;span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;"&gt;"default"&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;And, we’ll go ahead and add the &lt;code&gt;linuxfb&lt;/code&gt; dependency – we’re going to it later, and it is going to be using bindgen, and will require some header files from the buildroot system, so lets get it compiling now too.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;cargo add linuxfb&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Now you can build for the arm target with the following command, but it will fail!&lt;/p&gt;
&lt;p&gt;&lt;code&gt;cargo build --release --target=armv7-unknown-linux-gnueabihf&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;We need to use the linker from buildroot, which is setup in &lt;code&gt;.cargo/config.toml&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# .cargo/config.toml
[target.armv7-unknown-linux-gnueabihf]
linker = "arm-buildroot-linux-gnueabihf-gcc"&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Great. Now building will fail differently!&lt;/p&gt;
&lt;p&gt;We’re going to be setting up some environment variables, to get that linker in our path and also to setup some options for sysroots. The extra options aren’t necessary if we are building pure rust, but when you link against shared libraries, or compile C code (e.g.&amp;nbsp;with bindgen/clang) during the build, you likely will need them. I put these environment variables into &lt;code&gt;.envrc&lt;/code&gt;, so they can be used with tools like &lt;a href="https://direnv.net"&gt;direnv&lt;/a&gt;, but they can also be activated in your current shell by simply sourcing the file: &lt;code&gt;. ./.envrc&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# .envrc

# The directory where host binaries are stored in `/bin`, and the sysroot is stored in
# `arm-buildroot-linux-gnueabihf/sysroot`. This can be `output/host` in a buildroot working
# directory, or an SDK tarball built using `make sdk`
BUILDROOT_SDK_RELATIVE="../buildroot/output/host"
# Paths have to be absolute, because crates will build with different working directories
export BUILDROOT_SDK=$(cd "$BUILDROOT_SDK_RELATIVE"; pwd -P)
export SYSROOT=$BUILDROOT_SDK/arm-buildroot-linux-gnueabihf/sysroot
export PKG_CONFIG_SYSROOT_DIR=$SYSROOT
export PKG_CONFIG_PATH=$SYSROOT/usr/lib/pkgconfig/
# We need the linker (`arm-buildroot-linux-gnueabihf-gcc`) from buildroot in the path. But buildroot
# also builds a host rustc, and this will override the cargo installed rust toolchain unless we output
# cargo into path ahead of it
export PATH=$HOME/.cargo/bin:$BUILDROOT_SDK/bin:$PATH
# Bindgen is used by some lib crates, like linuxfb, and it needs to know where sysroot is as well
export BINDGEN_EXTRA_CLANG_ARGS_armv7_unknown_linux_gnueabihf="--sysroot $SYSROOT"&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can generate a tarball with all of the required sysroot files from buildroot using the &lt;code&gt;make sdk&lt;/code&gt; target, and I do recommend doing this and saving the tarball once your linux system is somewhat stable.&lt;/p&gt;
&lt;p&gt;Now hopefully, your program builds. You can transfer it to the target using scp, then login and run it:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;scp -O target/armv7-unknown-linux-gnueabihf/release/demo coremp135:/root  
ssh coremp135
# ./demo 
Hello, world!&lt;/code&gt;&lt;/pre&gt;
&lt;/section&gt;
&lt;section id="building-a-ui" class="level2"&gt;
&lt;h2 class="anchored" data-anchor-id="building-a-ui"&gt;Building a UI&lt;/h2&gt;
&lt;p&gt;The touchscreen can be used by drawing to the frame buffer at &lt;code&gt;/dev/fb1&lt;/code&gt;, and reading input events from &lt;code&gt;/dev/input/event0&lt;/code&gt;.&lt;/p&gt;
&lt;section id="rendering" class="level3"&gt;
&lt;h3 class="anchored" data-anchor-id="rendering"&gt;Rendering&lt;/h3&gt;
&lt;p&gt;For this, we’re going to use &lt;a href="https://slint.dev"&gt;Slint&lt;/a&gt; to build the UI. You can use tools like the &lt;a href="https://marketplace.visualstudio.com/items?itemName=Slint.slint"&gt;Slint vscode plugin&lt;/a&gt; or &lt;a href="https://slintpad.com"&gt;SlintPad&lt;/a&gt; to design the slint file, and it has good support for embedded devices (although note the licensing for commercial products). Of course, any UI library capable of drawing pixels to a frame buffer will work here too!&lt;/p&gt;
&lt;p&gt;We’ll use the Slint &lt;a href="https://docs.slint.dev/latest/docs/rust/slint/platform/software_renderer/"&gt;software renderer&lt;/a&gt; and the &lt;a href="https://crates.io/crates/linuxfb"&gt;linuxfb crate&lt;/a&gt; crate to draw to the screen in our own event loop.&lt;/p&gt;
&lt;p&gt;The minimum test application to render a slint UI looks like this:&lt;/p&gt;
&lt;p&gt;First, add the following dependencies to &lt;code&gt;Cargo.toml&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-copy-outer-scaffold"&gt;&lt;div class="sourceCode" id="cb6" style="background: #f1f3f5;"&gt;&lt;pre class="sourceCode toml code-with-copy"&gt;&lt;code class="sourceCode toml"&gt;&lt;span id="cb6-1"&gt;&lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;[dependencies]&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb6-2"&gt;&lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;linuxfb&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt; &lt;span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;"&gt;"0.3.1"&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb6-3"&gt;&lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;slint&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;{ &lt;/span&gt;&lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;version&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt; =&lt;/span&gt; &lt;span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;"&gt;"1.13.1"&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;, &lt;/span&gt;&lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;default-features&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt; =&lt;/span&gt; &lt;span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;"&gt;false&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;, &lt;/span&gt;&lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;features&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt; =&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;[&lt;/span&gt;&lt;span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;"&gt;"renderer-software"&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt; &lt;span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;"&gt;"std"&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt; &lt;span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;"&gt;"compat-1-2"&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;] }&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb6-4"&gt;&lt;/span&gt;
&lt;span id="cb6-5"&gt;&lt;/span&gt;
&lt;span id="cb6-6"&gt;&lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;[build-dependencies]&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb6-7"&gt;&lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;slint-build&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt; &lt;span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;"&gt;"1.13.1"&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Then, add a simple .slint file to define the UI in &lt;code&gt;ui/touch.slint&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { AboutSlint, Button, VerticalBox } from "std-widgets.slint";

export component Demo inherits Window {
    VerticalBox {
        alignment: start;
        Text {
            text: "Hello World!";
            font-size: 24px;
            horizontal-alignment: center;
        }
        AboutSlint {
            preferred-height: 150px;
        }
        HorizontalLayout { alignment: center; Button { text: "OK!"; } }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then add a &lt;code&gt;build.rs&lt;/code&gt; to compile your UI at build-time:&lt;/p&gt;
&lt;div class="code-copy-outer-scaffold"&gt;&lt;div class="sourceCode" id="cb8" style="background: #f1f3f5;"&gt;&lt;pre class="sourceCode rust code-with-copy"&gt;&lt;code class="sourceCode rust"&gt;&lt;span id="cb8-1"&gt;&lt;span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;// build.rs&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb8-2"&gt;&lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;fn&lt;/span&gt; main() &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb8-3"&gt;    &lt;span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;slint_build::&lt;/span&gt;compile_with_config(&lt;/span&gt;
&lt;span id="cb8-4"&gt;        &lt;span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;"&gt;"ui/touch.slint"&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb8-5"&gt;        &lt;span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;slint_build::CompilerConfiguration::&lt;/span&gt;new()&lt;/span&gt;
&lt;span id="cb8-6"&gt;            &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;.&lt;/span&gt;embed_resources(&lt;span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;slint_build::EmbedResourcesKind::&lt;/span&gt;EmbedForSoftwareRenderer)&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb8-7"&gt;    )&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;.&lt;/span&gt;unwrap()&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb8-8"&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Then, in the application:&lt;/p&gt;
&lt;div class="code-copy-outer-scaffold"&gt;&lt;div class="sourceCode" id="cb9" style="background: #f1f3f5;"&gt;&lt;pre class="sourceCode rust code-with-copy"&gt;&lt;code class="sourceCode rust"&gt;&lt;span id="cb9-1"&gt;&lt;span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;// main.rs&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb9-2"&gt;&lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;use&lt;/span&gt; &lt;span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;std::&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt;&lt;span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;rc::&lt;/span&gt;Rc&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt; &lt;span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;time::&lt;/span&gt;Instant&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;};&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb9-3"&gt;&lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;use&lt;/span&gt; &lt;span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;slint::platform::&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt;&lt;span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;software_renderer::&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt;MinimalSoftwareWindow&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt; Rgb565Pixel&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;},&lt;/span&gt; Platform&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;};&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb9-4"&gt;&lt;/span&gt;
&lt;span id="cb9-5"&gt;&lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;struct&lt;/span&gt; MyPlatform &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb9-6"&gt;    window&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;:&lt;/span&gt; Rc&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;lt;&lt;/span&gt;MinimalSoftwareWindow&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb9-7"&gt;    start_instant&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;:&lt;/span&gt; Instant&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb9-8"&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb9-9"&gt;&lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;impl&lt;/span&gt; MyPlatform &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb9-10"&gt;    &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;pub&lt;/span&gt; &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;fn&lt;/span&gt; new(window&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;:&lt;/span&gt; Rc&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;lt;&lt;/span&gt;MinimalSoftwareWindow&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;gt;&lt;/span&gt;) &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;Self&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb9-11"&gt;        &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;let&lt;/span&gt; start_instant &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt; &lt;span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;Instant::&lt;/span&gt;now()&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb9-12"&gt;        &lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;Self&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb9-13"&gt;            window&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb9-14"&gt;            start_instant&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb9-15"&gt;        &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb9-16"&gt;    &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb9-17"&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb9-18"&gt;&lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;impl&lt;/span&gt; Platform &lt;span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;for&lt;/span&gt; MyPlatform &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb9-19"&gt;    &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;fn&lt;/span&gt; create_window_adapter(&lt;/span&gt;
&lt;span id="cb9-20"&gt;        &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;amp;&lt;/span&gt;&lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;self&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb9-21"&gt;    ) &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;Result&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;lt;&lt;/span&gt;Rc&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;dyn&lt;/span&gt; &lt;span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;slint::platform::&lt;/span&gt;WindowAdapter&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;gt;,&lt;/span&gt; &lt;span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;slint::&lt;/span&gt;PlatformError&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;gt;&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb9-22"&gt;        &lt;span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;"&gt;Ok&lt;/span&gt;(&lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;self&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;.&lt;/span&gt;window&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;.&lt;/span&gt;clone())&lt;/span&gt;
&lt;span id="cb9-23"&gt;    &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb9-24"&gt;&lt;/span&gt;
&lt;span id="cb9-25"&gt;    &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;fn&lt;/span&gt; duration_since_start(&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;amp;&lt;/span&gt;&lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;self&lt;/span&gt;) &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;core::time::&lt;/span&gt;Duration &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb9-26"&gt;        &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;self&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;.&lt;/span&gt;start_instant&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;.&lt;/span&gt;elapsed()&lt;/span&gt;
&lt;span id="cb9-27"&gt;    &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb9-28"&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb9-29"&gt;&lt;/span&gt;
&lt;span id="cb9-30"&gt;&lt;span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;// Include the types created by slint in build.rs&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb9-31"&gt;&lt;span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;slint::include_modules!&lt;/span&gt;()&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb9-32"&gt;&lt;/span&gt;
&lt;span id="cb9-33"&gt;&lt;span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;/// Draw the UI to the framebuffer and exit&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb9-34"&gt;&lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;fn&lt;/span&gt; main() &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb9-35"&gt;    &lt;/span&gt;
&lt;span id="cb9-36"&gt;    &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;const&lt;/span&gt; DISPLAY_WIDTH&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;:&lt;/span&gt; &lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;u32&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt; &lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;320&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb9-37"&gt;    &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;const&lt;/span&gt; DISPLAY_HEIGHT&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;:&lt;/span&gt; &lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;u32&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt; &lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;240&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb9-38"&gt;    &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;let&lt;/span&gt; fb &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt; &lt;span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;linuxfb::Framebuffer::&lt;/span&gt;new(&lt;span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;"&gt;"/dev/fb1"&lt;/span&gt;)&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;.&lt;/span&gt;unwrap()&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb9-39"&gt;&lt;/span&gt;
&lt;span id="cb9-40"&gt;    &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;let&lt;/span&gt; window &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt; &lt;span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;MinimalSoftwareWindow::&lt;/span&gt;new(&lt;span class="bu" style="color: null;
background-color: null;
font-style: inherit;"&gt;Default&lt;/span&gt;&lt;span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;::&lt;/span&gt;&lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;default&lt;/span&gt;())&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb9-41"&gt;    &lt;span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;slint::platform::&lt;/span&gt;set_platform(&lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;Box&lt;/span&gt;&lt;span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;::&lt;/span&gt;new(&lt;span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;MyPlatform::&lt;/span&gt;new(window&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;.&lt;/span&gt;clone())))&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;.&lt;/span&gt;unwrap()&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb9-42"&gt;    window&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;.&lt;/span&gt;set_size(&lt;span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;slint::PhysicalSize::&lt;/span&gt;new(&lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;320&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt; &lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;240&lt;/span&gt;))&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb9-43"&gt;&lt;/span&gt;
&lt;span id="cb9-44"&gt;    &lt;span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;// 'Demo' is the type exported in touch.slint&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb9-45"&gt;    &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;let&lt;/span&gt; _ui &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt; &lt;span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;Demo::&lt;/span&gt;new()&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;.&lt;/span&gt;unwrap()&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb9-46"&gt;    &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;let&lt;/span&gt; &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;mut&lt;/span&gt; buffer &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt; [Rgb565Pixel(&lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;0&lt;/span&gt;)&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt; (DISPLAY_WIDTH &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;*&lt;/span&gt; DISPLAY_HEIGHT) &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;as&lt;/span&gt; &lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;usize&lt;/span&gt;]&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb9-47"&gt;    &lt;span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;slint::platform::&lt;/span&gt;update_timers_and_animations()&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb9-48"&gt;    &lt;/span&gt;
&lt;span id="cb9-49"&gt;    window&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;.&lt;/span&gt;draw_if_needed(&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;|&lt;/span&gt;renderer&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;|&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb9-50"&gt;        &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;let&lt;/span&gt; &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;mut&lt;/span&gt; data &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt; fb&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;.&lt;/span&gt;map()&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;.&lt;/span&gt;unwrap()&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb9-51"&gt;&lt;/span&gt;
&lt;span id="cb9-52"&gt;        renderer&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;.&lt;/span&gt;render(&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;amp;&lt;/span&gt;&lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;mut&lt;/span&gt; buffer&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt; DISPLAY_WIDTH &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;as&lt;/span&gt; &lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;usize&lt;/span&gt;)&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb9-53"&gt;        &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;unsafe&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb9-54"&gt;            &lt;span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;core::ptr::&lt;/span&gt;copy_nonoverlapping(&lt;/span&gt;
&lt;span id="cb9-55"&gt;                buffer&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;.&lt;/span&gt;as_ptr() &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;as&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;*&lt;/span&gt;&lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;mut&lt;/span&gt; &lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;u8&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb9-56"&gt;                data&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;.&lt;/span&gt;as_mut_ptr()&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb9-57"&gt;                data&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;.&lt;/span&gt;len()&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb9-58"&gt;            )&lt;/span&gt;
&lt;span id="cb9-59"&gt;        &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb9-60"&gt;    &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;}&lt;/span&gt;)&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb9-61"&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Run that, and if all goes well, it will draw the UI onto the screen, and quit.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="input-events" class="level3"&gt;
&lt;h3 class="anchored" data-anchor-id="input-events"&gt;Input Events&lt;/h3&gt;
&lt;p&gt;Now we need to be able to push buttons!&lt;/p&gt;
&lt;p&gt;The &lt;a href="https://crates.io/crates/evdev"&gt;evdev crate&lt;/a&gt; allows reading touch events from the touchscreen. The &lt;code&gt;MinimalSoftwareWindow&lt;/code&gt; struct provides the &lt;a href="https://docs.slint.dev/latest/docs/rust/slint/struct.Window#method.dispatch_event"&gt;&lt;code&gt;dispatch_event()&lt;/code&gt;&lt;/a&gt; method for receiving input events. So to build an interactive UI, we just need to create an event loop: a thread which reads events, passes them to the Window, and then redraws as necessary. The only thing is, the &lt;code&gt;dispatch_event()&lt;/code&gt; method wants to receive a &lt;a href="https://docs.slint.dev/latest/docs/rust/slint/platform/enum.WindowEvent"&gt;`WindowEvent’&lt;/a&gt;, so clearly we need some code to convert the evdev events into these.&lt;/p&gt;
&lt;p&gt;If you are trying to do this…you are in luck! I did it already and published it in the &lt;a href="https://crates.io/crates/slint-evdev-input"&gt;slint-evdev-input&lt;/a&gt; crate.&lt;/p&gt;
&lt;p&gt;The code for the entire event loop is getting a little long, so I’m not going to include it here. Instead, &lt;a href="https://github.com/mcbridejc/coremp135-canlogger/blob/1a6c792933f50f8edd5fe80dc8272e6fd508922b/canlogger-external/canlogger/src/ui.rs"&gt;check it out in the repo&lt;/a&gt;.&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="putting-it-all-together" class="level2"&gt;
&lt;h2 class="anchored" data-anchor-id="putting-it-all-together"&gt;Putting it all together&lt;/h2&gt;
&lt;p&gt;Now, you can build your linux filesystem with buildroot, and you can compile a rust application, linking against the SDK/filesystem created by buildroot which will run on your system. But ultimately, you probably want to combine those steps.&lt;/p&gt;
&lt;p&gt;There’s no clear one-right-way here, that I see. One way would be to take the &lt;code&gt;coremp135-bsp&lt;/code&gt; project, clone it, and start modifying it. But I wanted to set it up so that I could re-use a single BSP with different applications. The method I’ve arrived at is to create an application repository and have it pull in the &lt;code&gt;buildroot&lt;/code&gt; tree and the &lt;code&gt;coremp135-bsp&lt;/code&gt; tree as submodules, then define a second external tree which ammends the BSP tree to customize the system, and add its own applications.&lt;/p&gt;
&lt;p&gt;IMHO, Buildroot does not exactly shine at this, and this process feels a little brittle. But, it is the best I’ve come up with so far.&lt;/p&gt;
&lt;section id="compiling-outside-of-buildroot-vs-a-package" class="level3"&gt;
&lt;h3 class="anchored" data-anchor-id="compiling-outside-of-buildroot-vs-a-package"&gt;Compiling outside of buildroot vs a package&lt;/h3&gt;
&lt;p&gt;You can create your application as a buildroot package, letting the buildroot make system compile and install it. This is a very typical thing to do. The issues that I ran into were:&lt;/p&gt;
&lt;ol type="1"&gt;
&lt;li&gt;I want to also be able to &lt;code&gt;cargo build&lt;/code&gt; + &lt;code&gt;scp&lt;/code&gt; during development, so I need to set that up to link against the buildroot SDK anyway&lt;/li&gt;
&lt;li&gt;The version of the rust toolchain included with buildroot was too old for me, so I preferred to compile with my own rust toolchain, rather than the one buildroot installs for itself.&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
&lt;section id="build-and-install" class="level3"&gt;
&lt;h3 class="anchored" data-anchor-id="build-and-install"&gt;Build and Install&lt;/h3&gt;
&lt;p&gt;In the end, I have a simple &lt;a href="https://github.com/mcbridejc/coremp135-canlogger/blob/main/Makefile"&gt;Makefile&lt;/a&gt; at the top-level of the project which builds everything.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;all: buildroot

.PHONY: buildroot
buildroot:
    cd buildroot; \
    make BR2_EXTERNAL=../coremp135-bsp:../canlogger-external coremp135_defconfig; \
    make COREMP135_EXFAT_DATA=1 -j4&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The rust application is built and installed via a &lt;a href="https://github.com/mcbridejc/coremp135-canlogger/blob/main/canlogger-external/board/canlogger/post-build.sh"&gt;post-build script&lt;/a&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;set -e 

# Build rust application
(cd ${BR2_EXTERNAL_CANLOGGER_PATH}/canlogger; source ./.envrc; cargo build --release --target armv7-unknown-linux-gnueabihf)
# Copy application to target output directory
cp ${BR2_EXTERNAL_CANLOGGER_PATH}/canlogger/target/armv7-unknown-linux-gnueabihf/release/canlogger $TARGET_DIR/usr/bin/canlogger

# Copy overlay files to target output directory
cp -r ${BR2_EXTERNAL_CANLOGGER_PATH}/board/canlogger/overlay/* $TARGET_DIR

# Copy public key for login
if [ -e ~/.ssh/id_rsa.pub ]; then
  mkdir -p $TARGET_DIR/root/.ssh
  cp ~/.ssh/id_rsa.pub $TARGET_DIR/root/.ssh/authorized_keys
fi&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And this seems to work OK.&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="expandable-data-partition" class="level2"&gt;
&lt;h2 class="anchored" data-anchor-id="expandable-data-partition"&gt;Expandable Data partition&lt;/h2&gt;
&lt;p&gt;A nuisance of programming devices with SD cards is that you don’t generally know the size of the flash when you create an image – more over, you ideally don’t want create a 32GB image file just so you can program a mostly empty partition on a 32GB card.&lt;/p&gt;
&lt;p&gt;My setup has three mountable partitions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;rootfs A&lt;/li&gt;
&lt;li&gt;rootfs B&lt;/li&gt;
&lt;li&gt;data&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The two rootfs partitions contain the filesystem, and are used by RAUC for doing updates – if you are currently booting from A, then an update will be installed on B and the next reboot will boot from B.&lt;/p&gt;
&lt;p&gt;The data partition is meant to store data which should not be erased on update. I also like to create separate filesystems for writing data, because setting things up so that your application doesn’t write to your root filesystem is good for stability.&lt;/p&gt;
&lt;p&gt;During image creation, the data partition is created as the last partition on the card, with a fixed, relatively small size. But really, I’d like it to be expanded to fill whatever space is available on the card. This is done in canlogger via an init script. After flashing the card, the first bootup will see that the partition table is small, and it will expand the partition to the end of the drive. Since the drive is already mounted as the &lt;code&gt;/&lt;/code&gt; drive, a reboot is required to get linux to reload the partition table, so the script reboots after modifying partitions. Then on the next boot, it sees that the partition is large, but that the filesystem is still at its original size, so it resizes the filesystem. This is all handled in the &lt;a href="https://github.com/mcbridejc/coremp135-canlogger/blob/main/canlogger-external/board/canlogger/overlay/etc/init.d/S15resize_data"&gt;S15resize_data init script&lt;/a&gt;.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="repos" class="level2"&gt;
&lt;h2 class="anchored" data-anchor-id="repos"&gt;Repos&lt;/h2&gt;
&lt;p&gt;All the relevant code is at https://github.com/mcbridejc/coremp135-canlogger. Oh, and it’s also capable of logging two CAN busses to a CSV file on the exFAT partition. Maybe that’s useful to somebody too.&lt;/p&gt;
&lt;p&gt;If you want to build a project based on the CoreMP135 and want help, you can &lt;a href="https://www.relativejump.com"&gt;hire me&lt;/a&gt;! I’d love a good reason to design a custom board around the STM32MP1 or STM32MP2 family, so especially if you are thinking about doing that let’s chat.&lt;/p&gt;


&lt;/section&gt;</summary><category term="coremp135" /><category term="embedded" /><category term="linux" /><category term="buildroot" /><category term="rust" /></entry><entry><id>https://jeffmcbride.net/posts/coremp135-linux-device-buildroot/</id><title>Creating linux images for the M5Stack CoreMP135 with Buildroot</title><link href="https://jeffmcbride.net/posts/coremp135-linux-device-buildroot/" rel="alternate" /><published>2025-10-25T07:00:00Z</published><updated>2025-10-25T07:00:00Z</updated><summary type="html">&lt;p&gt;This post is about the M5Stack CoreMP135: why I like it, and a tutorial about how to use buildroot to create a linux image for it. A &lt;a href="../../blog/2025/10/27/building-a-rust-application-for-the-coremp135-using-slint"&gt;follow-up post&lt;/a&gt; describes how I create applications with Rust to run on it.&lt;/p&gt;
&lt;p&gt;If you want to build a project based on the CoreMP135 and want help, you can &lt;a href="https://www.relativejump.com"&gt;hire me&lt;/a&gt;! I’d love a good reason to design a custom board around the STM32MP1 or STM32MP2 family, so especially if you are thinking about doing that let’s chat.&lt;/p&gt;
&lt;section id="about-the-coremp135" class="level2"&gt;
&lt;h2 class="anchored" data-anchor-id="about-the-coremp135"&gt;About the CoreMP135&lt;/h2&gt;
&lt;p&gt;I really like &lt;a href="https://docs.m5stack.com/en/core/M5CoreMP135"&gt;this&lt;/a&gt; little $75 computer from M5Stack. It’s a nicely packaged, low power, small application processor which can run Linux and has a great set of IO with good connectors. It also has a small touch screen for a UI. It’s like a less powerful, better packaged Raspberry PI designed for industrial control applications.&lt;/p&gt;
&lt;div class="quarto-figure quarto-figure-center"&gt;
&lt;figure class="figure"&gt;
&lt;p&gt;&lt;img src="https://jeffmcbride.net/posts/coremp135-linux-device-buildroot/m5stack_features.webp" class="img-fluid figure-img"&gt;&lt;/p&gt;
&lt;figcaption&gt;M5Stack CoreMP135&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/div&gt;
&lt;p&gt;It’s based on the STM32MP135. I’m a long-time user of the STM32 line of MCUs, and this family is a great addition to it – it’s an application processor with a peripheral set that feels more like a microcontroller than e.g.&amp;nbsp;the broadcomm chips you find on a raspberry pi. It’s single core, and has just 512MB of RAM, so it’s a lightweight for computational power, but I don’t usually need that and the packaging on this is so much nicer than a raspberry PI with some kluged on extension board to get the IO you need. It also comes with a DIN rail mount as well as tabs with screw holes for mounting in your system.&lt;/p&gt;
&lt;p&gt;I first picked it up to replace a raspberry PI I had as a house monitor / control system for a camper. The raspberry PI had the usual problems:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Power hungry&lt;/li&gt;
&lt;li&gt;I had to have large USB adapters for CAN and RS485, and I don’t like those hanging off the USB-A ports in a vibration-heavy environment&lt;/li&gt;
&lt;li&gt;Flaky USB drivers that seem to crash every couple weeks, requiring a reboot.&lt;/li&gt;
&lt;li&gt;Needs a 5V regulator&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The M5Stack has:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;2 CAN controllers&lt;/li&gt;
&lt;li&gt;1 RS485&lt;/li&gt;
&lt;li&gt;1 TTL UART&lt;/li&gt;
&lt;li&gt;1 I2C&lt;/li&gt;
&lt;li&gt;2 gigabit ethernets!&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This was perfect for my application, because it lowered the power consumption, runs off the 12V battery, has a smaller physical footprint, and I could use the integrated RS485 + CAN. The touch screen was a happy bonus.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Some things I wish it had&lt;/strong&gt;: An on-board eMMC so that I can store the application there while still having the uSD card for removable storage, and the ability to connect a battery backup for the RTC to keep time when 12V power is removed.&lt;/p&gt;
&lt;p&gt;I’m happy with it in the camper, and I have already ordered a second and found a use for it on a client project. It’s now one of the things that I always want to have a spare on hand for when something comes up.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="programming-with-buildroot-linux" class="level2"&gt;
&lt;h2 class="anchored" data-anchor-id="programming-with-buildroot-linux"&gt;Programming with Buildroot Linux&lt;/h2&gt;
&lt;p&gt;The hardware is great, but I felt that the buildroot examples from M5Stack left a lot to be desired. So I built my own buildroot external tree to support the board, and to base my own applications off of for faster development. So I’m going to walk through what I did to get up and running using buildroot, and RAUC for OTA updates.&lt;/p&gt;
&lt;section id="buildroot-vs-debian" class="level3"&gt;
&lt;h3 class="anchored" data-anchor-id="buildroot-vs-debian"&gt;Buildroot vs Debian&lt;/h3&gt;
&lt;p&gt;M5Stack does provide a debian image you can load, and start using this like a normal linux machine, SSHing in and installing packages with apt. I threw this right out. I don’t want to treat this like a server, I want to treat it like a microcontroller. This means I want a build process to create a complete image, and I want that image to be as small as possible. I also want the system to be stripped down to just what I need, because that’s good for stability. I also want to support over-the-air updates, so it’s easier to ship new code to the controller.&lt;/p&gt;
&lt;p&gt;There are basically two common ways to build an embedded linux image: &lt;strong&gt;buildroot&lt;/strong&gt; and &lt;strong&gt;yocto&lt;/strong&gt;. I don’t have a strong argument for one over the other, but I have a lot more experience with buildroot, and that is what M5Stack used for their example, so that is what I used.&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="buildroot-basics" class="level2"&gt;
&lt;h2 class="anchored" data-anchor-id="buildroot-basics"&gt;Buildroot Basics&lt;/h2&gt;
&lt;p&gt;The way buildroot works is: you checkout the buildroot repository, you configure it (e.g.&amp;nbsp;you can start with &lt;code&gt;make menuconfig&lt;/code&gt;, but more likely you start with a defconfig from somewhere else, more on that later), and then you build it. It will go and download tarballs of all the components you enabled – this is things like the linux kernel and u-boot, as well as utilities for your file system like busybox and an ssh server. It’s all the packages that make up a functioning linux system. It puts all these together into an image which you can load onto your flash device through a wildly convoluted series of makefiles and bash scripts.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="get-the-buildroot-tree" class="level2"&gt;
&lt;h2 class="anchored" data-anchor-id="get-the-buildroot-tree"&gt;Get the buildroot tree&lt;/h2&gt;
&lt;p&gt;I started with the &lt;code&gt;st/2024.02.9&lt;/code&gt; branch of the &lt;a href="https://github.com/bootlin/buildroot/tree/st/2024.02.9"&gt;bootlin buildroot fork&lt;/a&gt;. This does not appear to have a lot of changes from the &lt;a href="https://github.com/buildroot/buildroot/tree/2024.02.9"&gt;mainline buildroot&lt;/a&gt;, and the changes seem to be related to trusted-firmware-m, which I don’t think is relevant to the STM32MP135, so I think I could have just used the mainline buildroot. But I haven’t it, so I’m not 100% sure.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="the-br2-external-tree" class="level2"&gt;
&lt;h2 class="anchored" data-anchor-id="the-br2-external-tree"&gt;The BR2 External Tree&lt;/h2&gt;
&lt;p&gt;A typical way to customize a buildroot system is to create a &lt;a href="https://buildroot.org/downloads/manual/manual.html#outside-br-custom"&gt;“br2 external tree”&lt;/a&gt;. This can contain your own configuration, filesystem overlay, device trees, custom packages, etc.&lt;/p&gt;
&lt;p&gt;Bootlin maintains an example BR2 external tree for the whole STM32MP1 family &lt;a href="https://github.com/bootlin/buildroot-external-st/"&gt;here&lt;/a&gt;. It’s really close, but you can’t use this as-is for the CoreMP135 because it is based on dev boards which have different hardware, different pinouts, etc. The changes require, at minimum, some modification to device trees.&lt;/p&gt;
&lt;p&gt;M5stack published their own fork of this for the CoreMP135 &lt;a href="https://github.com/m5stack/CoreMP135_buildroot-external-st/"&gt;here&lt;/a&gt;. I wish they’d cleaned it up a bit – e.g.&amp;nbsp;by removing all of the dev board defconfigs, device trees, etc, to focus on the CoreMP135. You can build a filesystem from this, but I felt it was worth the time to dig into it and identify the important changes, prune the noise, and integrate it all into my own “board support package”.&lt;/p&gt;
&lt;p&gt;I spent a fair amount of time parsing the changes and integrating into my own config, and I think it was worth it because I understand much better what’s going on, and now I have support for RAUC, examples of using the touchscreen, and a nice starting point for future projects on the CoreMP135. I spun up a new application last week for a client and it only took 1.5 days of work, because I could focus on building the new touch UI and application logic, and didn’t have to think much at all about the underlying system configuration.&lt;/p&gt;
&lt;p&gt;I’m using kernel v6.1. This is still fairly old (December 2022), and maybe one day I will take on updating to a more recent kernel. The bootlin external tree is using kernel v6.6 (October 2023), so there’s probably no reason I can’t use at least that. But there are patches that may be affected, and often there are device tree schema changes between kernel versions, so updating is not necessarily trivial. For now v6.1 is getting the job done and there are other things to do.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="touchscreen-issues" class="level2"&gt;
&lt;h2 class="anchored" data-anchor-id="touchscreen-issues"&gt;Touchscreen issues&lt;/h2&gt;
&lt;p&gt;There are a collection of patches provided in the M5Stack BR2 external examples for the ILI9342C display, and for the backlight which is controlled by an AXP2101 power management IC. M5Stack provides two board configurations:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;coremp135&lt;/code&gt; with kernel v6.1&lt;/li&gt;
&lt;li&gt;&lt;code&gt;coremp135_5_15&lt;/code&gt; with kernel v5.15&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A major change between these two kernels is the implementation of the Direct Rendering Manager (DRM) subsystem for displays. On the 5.15, the LCD driver creates a framebuffer at &lt;code&gt;/dev/fb1&lt;/code&gt; and I found drawing to the display to be as simple as writing pixel data to this framebuffer. However, on the v6.1 kernel, the LCD is implemented as a TinyDRM device. Luckily, the DRM system does support the legacy frame buffer interface as well, but I found it did not work! After a log of digging and learning, I realized that the DRM pipeline was not being enabled, and the way I ended up enabling it is by installing modetest and running that. To add the modetest executable to the system, you need the right libdrm packages installed:&lt;/p&gt;
&lt;div class="code-copy-outer-scaffold"&gt;&lt;div class="sourceCode" id="cb1" style="background: #f1f3f5;"&gt;&lt;pre class="sourceCode conf code-with-copy"&gt;&lt;code class="sourceCode toml"&gt;&lt;span id="cb1-1"&gt;&lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;BR2_PACKAGE_LIBDRM&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt;&lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;y&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb1-2"&gt;&lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;BR2_PACKAGE_LIBDRM_HAS_ATOMIC&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt;&lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;y&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb1-3"&gt;&lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;BR2_PACKAGE_LIBDRM_INSTALL_TESTS&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt;&lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;y&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Then, I just added modetest to rc.local so it is run at boot. This was a huge time sink for me, and one of the reasons I became frustrated with the M5Stack examples – the examples just do not seem to work on the &lt;code&gt;coremp135&lt;/code&gt; defconfig from the M5Stack repo. I suspect there is a more straightforward way to enable the pipeline via libdrm, but I haven’t dug into that yet.&lt;/p&gt;
&lt;p&gt;The backlight can be controlled easily via sysfs, by writing a number to &lt;code&gt;/sys/class/backlight/axp2101_m5stack_bl/brightness&lt;/code&gt;, e.g.:&lt;/p&gt;
&lt;div class="code-copy-outer-scaffold"&gt;&lt;div class="sourceCode" id="cb2" style="background: #f1f3f5;"&gt;&lt;pre class="sourceCode bash code-with-copy"&gt;&lt;code class="sourceCode bash"&gt;&lt;span id="cb2-1"&gt;&lt;span class="bu" style="color: null;
background-color: null;
font-style: inherit;"&gt;echo&lt;/span&gt; &lt;span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;"&gt;"100"&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;gt;&lt;/span&gt; /sys/class/backlight/axp2101_m5stack_bl/brightness&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;/section&gt;
&lt;section id="rauc-for-ota" class="level2"&gt;
&lt;h2 class="anchored" data-anchor-id="rauc-for-ota"&gt;RAUC for OTA&lt;/h2&gt;
&lt;p&gt;Another problem to tackle was setting up RAUC for over-the-air updates. There are a lot of different config options, and a lot of things that have to be setup just right to get everything working.&lt;/p&gt;
&lt;p&gt;The way I set it up is that the bundle always includes the entire filesystem, including linux and the application – an alternative is that you can also set up separate bundles for the filesystem and the application, so that application updates can be smaller when they don’t require any changes to the main system. There are two rootfs partitions (A and B), so that if a programming of one fails, it can fall back to booting from the previous version. There is also a third and separate data partition, which can be used to store data that does not get erased during updates.&lt;/p&gt;
&lt;p&gt;RAUC requires communication between u-boot and linux, because ultimately it is u-boot which has to decide which partition to boot from. The communication is done via a shared section of flash which stores the “u-boot env”. In the file &lt;code&gt;stm32mp13_defconfig&lt;/code&gt; from the STMicro version of the u-boot tree, you can find the value of &lt;a href="https://github.com/STMicroelectronics/u-boot/blob/v2022.10-stm32mp/configs/stm32mp13_defconfig#L5"&gt;&lt;code&gt;CONFIG_ENV_OFFSET&lt;/code&gt; defined as &lt;code&gt;0x900000&lt;/code&gt;&lt;/a&gt;, or you can once you’ve searched enough! This is there u-boot will look for its environment. There is also a second offset, &lt;code&gt;CONFIG_ENV_OFFSET_REDUND&lt;/code&gt;, for a redundant copy of the env. Why is the env stored 9MB into the SD card? I’ve certainly no idea; I guess someone decided that would allow enough space to store the FSBL images before it.&lt;/p&gt;
&lt;p&gt;So, when creating the final image file using &lt;a href="https://github.com/pengutronix/genimage"&gt;genimage&lt;/a&gt;, you must make sure to allocate the env partitions at the right flash offsets and then store the uboot env there.&lt;/p&gt;
&lt;p&gt;The final partition config in &lt;code&gt;genimage.cfg&lt;/code&gt; looks like this:&lt;/p&gt;
&lt;div class="code-copy-outer-scaffold"&gt;&lt;div class="sourceCode" id="cb3" style="background: #f1f3f5;"&gt;&lt;pre class="sourceCode conf code-with-copy"&gt;&lt;code class="sourceCode toml"&gt;&lt;span id="cb3-1"&gt;&lt;/span&gt;
&lt;span id="cb3-2"&gt;&lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;image&lt;/span&gt; &lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;sdcard.img&lt;/span&gt; &lt;span class="er" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-3"&gt;    &lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;hdimage&lt;/span&gt; &lt;span class="er" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-4"&gt;        &lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;partition-table-type&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt; &lt;span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;"&gt;"gpt"&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-5"&gt;    &lt;span class="er" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-6"&gt;&lt;/span&gt;
&lt;span id="cb3-7"&gt;    &lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;partition&lt;/span&gt; &lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;fsbl1&lt;/span&gt; &lt;span class="er" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-8"&gt;        &lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;image&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt; &lt;span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;"&gt;"%ATFBIN%"&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-9"&gt;    &lt;span class="er" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-10"&gt;&lt;/span&gt;
&lt;span id="cb3-11"&gt;    &lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;partition&lt;/span&gt; &lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;fsbl2&lt;/span&gt; &lt;span class="er" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-12"&gt;        &lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;image&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt; &lt;span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;"&gt;"%ATFBIN%"&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-13"&gt;    &lt;span class="er" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-14"&gt;&lt;/span&gt;
&lt;span id="cb3-15"&gt;    &lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;partition&lt;/span&gt; &lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;fip&lt;/span&gt; &lt;span class="er" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-16"&gt;        &lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;image&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt; &lt;span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;"&gt;"fip.bin"&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-17"&gt;    &lt;span class="er" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-18"&gt;    &lt;/span&gt;
&lt;span id="cb3-19"&gt;    &lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;partition&lt;/span&gt; &lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;u-boot-env&lt;/span&gt; &lt;span class="er" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-20"&gt;        &lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;image&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt; &lt;span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;"&gt;"uboot-env.bin"&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-21"&gt;        &lt;span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;# Matches CONFIG_ENV_OFFSET in U-Boot config&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-22"&gt;        &lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;offset&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt; &lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;0x900000&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-23"&gt;        &lt;span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;# Matches CONFIG_ENV_SIZE in U-Boot config&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-24"&gt;        &lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;size&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt; &lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;0x2000&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-25"&gt;    &lt;span class="er" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-26"&gt;&lt;/span&gt;
&lt;span id="cb3-27"&gt;    &lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;partition&lt;/span&gt; &lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;u-boot-env-red&lt;/span&gt; &lt;span class="er" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-28"&gt;        &lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;image&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt; &lt;span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;"&gt;"uboot-env.bin"&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-29"&gt;        &lt;span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;# Matches CONFIG_ENV_OFFSET_REDUND in U-Boot config&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-30"&gt;        &lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;offset&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt; &lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;0x940000&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-31"&gt;        &lt;span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;# Matches CONFIG_ENV_SIZE in U-Boot config&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-32"&gt;        &lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;size&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt; &lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;0x2000&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-33"&gt;    &lt;span class="er" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-34"&gt;&lt;/span&gt;
&lt;span id="cb3-35"&gt;    &lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;partition&lt;/span&gt; &lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;rootfs-a&lt;/span&gt; &lt;span class="er" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-36"&gt;        &lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;image&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt; &lt;span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;"&gt;"rootfs.ext4"&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-37"&gt;        &lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;bootable&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt; &lt;span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;"&gt;"yes"&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-38"&gt;        &lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;size&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt; &lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;500&lt;/span&gt;&lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;M&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-39"&gt;    &lt;span class="er" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-40"&gt;&lt;/span&gt;
&lt;span id="cb3-41"&gt;    &lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;partition&lt;/span&gt; &lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;rootfs-b&lt;/span&gt; &lt;span class="er" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-42"&gt;        &lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;image&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt; &lt;span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;"&gt;"rootfs.ext4"&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-43"&gt;        &lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;size&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt; &lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;500&lt;/span&gt;&lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;M&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-44"&gt;    &lt;span class="er" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-45"&gt;&lt;/span&gt;
&lt;span id="cb3-46"&gt;    &lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;partition&lt;/span&gt; &lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;data&lt;/span&gt; &lt;span class="er" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-47"&gt;        &lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;image&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt; &lt;span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;"&gt;"data.img"&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-48"&gt;    &lt;span class="er" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-49"&gt;&lt;span class="er" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;So, when creating the image, these partitions are loaded with the environment from the &lt;code&gt;uboot-env.bin&lt;/code&gt; file. But where does this file come from you ask?! Well, it is created by the uboot-tools package, because in &lt;code&gt;coremp135_defconfig&lt;/code&gt;, we specify the right options:&lt;/p&gt;
&lt;div class="code-copy-outer-scaffold"&gt;&lt;div class="sourceCode" id="cb4" style="background: #f1f3f5;"&gt;&lt;pre class="sourceCode conf code-with-copy"&gt;&lt;code class="sourceCode toml"&gt;&lt;span id="cb4-1"&gt;&lt;span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;# Need to customize u-boot env for OTA support&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb4-2"&gt;&lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;BR2_PACKAGE_HOST_UBOOT_TOOLS&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt;&lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;y&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb4-3"&gt;&lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;BR2_PACKAGE_HOST_UBOOT_TOOLS_ENVIMAGE&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt;&lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;y&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb4-4"&gt;&lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;BR2_PACKAGE_HOST_UBOOT_TOOLS_ENVIMAGE_SOURCE&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt;&lt;span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;"&gt;"$(BR2_EXTERNAL_COREMP135_PATH)/board/coremp135/uEnv.txt"&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb4-5"&gt;&lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;BR2_PACKAGE_HOST_UBOOT_TOOLS_ENVIMAGE_SIZE&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt;&lt;span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;"&gt;"0x2000"&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb4-6"&gt;&lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;BR2_PACKAGE_HOST_UBOOT_TOOLS_ENVIMAGE_REDUNDANT&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt;&lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;y&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;What goes in the env? Well, again as usual there are a few ways that one can go about it. What I ended up using was the countdown method, where u-boot keeps track of two counters: &lt;code&gt;BOOT_A_LEFT&lt;/code&gt; and &lt;code&gt;BOOT_B_LEFT&lt;/code&gt;. Each time u-boot boots from one of the partitions, it decrements the counter. If the counter for A reaches 0, it boots from B instead. When the linux image successfully boots, it sets the counter for the partition it booted from to 3 – this is done by running &lt;code&gt;rauc status mark-good&lt;/code&gt; in rc.local at boot.&lt;/p&gt;
&lt;p&gt;To access the env from userspace, you use the &lt;code&gt;fw_printenv&lt;/code&gt; and &lt;code&gt;fw_setenv&lt;/code&gt; commands. These are configured via &lt;code&gt;/etc/fw_env.config&lt;/code&gt;, which must also be setup to match the partitions of the env partitions, but here we can at least access them by label instead of by offset – although, do note that you must enable eudev in order to get the &lt;code&gt;/dev/disk/by-partlabel&lt;/code&gt; symlinks created.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/dev/disk/by-partlabel/u-boot-env 0 0x2000
/dev/disk/by-partlabel/u-boot-env-red 0 0x2000&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;SquashFS must also be enabled for both u-boot and the kernel. RAUC on the target has to be configured via &lt;code&gt;/etc/rauc/system.conf&lt;/code&gt;, and also the RAUC bundle itself has to be built as part of the &lt;code&gt;post-image.sh&lt;/code&gt; hook defined in the BSP external tree, using a key you created (see &lt;code&gt;generate_rauc_key.sh&lt;/code&gt;) to sign it. If this is feeling all a bit convoluted…well, I agree! That’s why I’m trying to make this BSP as re-usable as I can for myself.&lt;/p&gt;
&lt;p&gt;If you get all that working, then OTA updates can be performed by simply running a command to grab the image from an HTTP server somewhere:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;rauc install https://example.com/bundle.raucb&lt;/code&gt;&lt;/p&gt;
&lt;/section&gt;
&lt;section id="checkout-the-repo" class="level2"&gt;
&lt;h2 class="anchored" data-anchor-id="checkout-the-repo"&gt;Checkout the repo&lt;/h2&gt;
&lt;p&gt;My CoreMP135 BSP is stored on github: https://github.com/mcbridejc/coremp135-bsp&lt;/p&gt;
&lt;p&gt;The goal is for this to be a base layer, allowing an application repo to include it as a submodule, and then build on top of it to add application specific stuff. But of course, you can also just fork it and modify it.&lt;/p&gt;
&lt;p&gt;To build a filesystem, you can check out buildroot, and checkout coremp135-base, and then use buildroot make, e.g.:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git clone -b st/2024.02.9 https://github.com/bootlin/buildroot
git clone https://github.com/mcbridejc/coremp135-bsp
cd buildroot
make BR2_EXTERNAL=../coremp135-bsp coremp135_defconfig
make -j4&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To use RAUC, you need to generate keys (see &lt;code&gt;coremp135-bsp/generate_rauc_keys.sh&lt;/code&gt;) and store them somewhere. Then you can pass their location in using the environment variables &lt;code&gt;RAUC_CERT_PATH&lt;/code&gt; and &lt;code&gt;RAUC_KEY_PATH&lt;/code&gt;. You may also want to set &lt;code&gt;RAUC_COMPATIBLE&lt;/code&gt; with a unique compatibility string for your device.&lt;/p&gt;
&lt;p&gt;I also recommend copying a public SSH key to &lt;code&gt;buildroot/output/target/root/.ssh/authorized_keys&lt;/code&gt; so you can use it to log in. You can customize the ETH1 static IP address if you like by editing &lt;code&gt;coremp135-bsp/board/overlay/etc/network/interfaces&lt;/code&gt;.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="next-steps-adding-a-custom-application" class="level2"&gt;
&lt;h2 class="anchored" data-anchor-id="next-steps-adding-a-custom-application"&gt;Next steps: Adding a custom application&lt;/h2&gt;
&lt;p&gt;Next up: &lt;a href="../../blog/2025/10/27/building-a-rust-application-for-the-coremp135-using-slint"&gt;Building a Rust Application for the CoreMP135 using Slint&lt;/a&gt;&lt;/p&gt;


&lt;/section&gt;</summary><category term="coremp135" /><category term="embedded" /><category term="linux" /><category term="buildroot" /><category term="rust" /></entry><entry><id>https://jeffmcbride.net/posts/steel-wood-furniture/</id><title>Building Custom Steel and Wood Furniture</title><link href="https://jeffmcbride.net/posts/steel-wood-furniture/" rel="alternate" /><published>2025-09-14T07:00:00Z</published><updated>2025-09-14T07:00:00Z</updated><summary type="html">&lt;p&gt;I recently finished building a pair of bedside tables in a stlye I’ve used a couple times before. This post is about the project and the process I used to do it.&lt;/p&gt;
&lt;section id="why" class="level2"&gt;
&lt;h2 class="anchored" data-anchor-id="why"&gt;Why?&lt;/h2&gt;
&lt;p&gt;It’s certainly not to save money. One reason is that it’s fun to have projects. The other reason is that I can decide exactly what dimensions and features I want, rather than shopping around and settling for something else, and I think the steel frame ends up being a lower material cost and lower effort than using more wood. And If I’m being completely honest, a big reason is simply this: &lt;strong&gt;Melting metal with electricity is a rush&lt;/strong&gt;.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="caveats" class="level2"&gt;
&lt;h2 class="anchored" data-anchor-id="caveats"&gt;Caveats&lt;/h2&gt;
&lt;p&gt;I’m going to talk about what I did here. You should not infer from this that I have any idea what I’m talking about, and I’m not going to try to make a tutorial. Especially when it comes to welding, I am a complete noob. I’ve done this a few times now, and it works for me, but someone who knows what they are doing may well say I’m doing it all wrong. I learned everything I know about welding from…well, basically two youtube videos. If you read this andthink, “wow I can’t believe he did it like that”, feel free to reach out and tell me how I should have done it better!&lt;/p&gt;
&lt;/section&gt;
&lt;section id="the-bedside-tables" class="level2"&gt;
&lt;h2 class="anchored" data-anchor-id="the-bedside-tables"&gt;The bedside tables&lt;/h2&gt;
&lt;p&gt;Let’s just start with some photo of the final products, shall we? That way everyone knows what I’m talking about.&lt;/p&gt;
&lt;p&gt;I built two of these this summer. I like the double level table top, because it allows for including power outlets in a more out-of-the-way location, and for keeping books and such on the bottom, while leaving the top relatively uncluttered for storing a phone, or grabbing a glass of water in the middle of the night.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://jeffmcbride.net/posts/steel-wood-furniture/front.jpg" class="img-fluid" alt="Front View"&gt; &lt;img src="https://jeffmcbride.net/posts/steel-wood-furniture/back.jpg" class="img-fluid" alt="Back View"&gt;&lt;/p&gt;
&lt;p&gt;These are built from 3/4” square steel tubes welded together and painted black, and from 1x2 or 1x3 pine boards glued into panels. The drawers are built from single boards for the face, and 0.5” plywood for the sides, with 0.25” plywood bottoms.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="getting-the-steel" class="level2"&gt;
&lt;h2 class="anchored" data-anchor-id="getting-the-steel"&gt;Getting the steel&lt;/h2&gt;
&lt;p&gt;For these, I used &lt;a href="https://www.steelsupplylp.com/sku/108820"&gt;16ga red oxide square tubes&lt;/a&gt; from steelsupplylp.com. They will cut it to desired lengths for you for a very reasonable price, which is great because then I don’t have to cut it, and it makes the size reasonable for shipping. They say they cut to a 1/8” accuracy, but every time I’ve ordered they’ve been more accurate than that (accurate enough that they seem perfect for my purposes).&lt;/p&gt;
&lt;/section&gt;
&lt;section id="welding" class="level2"&gt;
&lt;h2 class="anchored" data-anchor-id="welding"&gt;Welding&lt;/h2&gt;
&lt;p&gt;After doing some reading, and watching some youtube videos, I decided that the cheapest way to achieve what I wanted was to go to Harbor Freight and buy a cheap flux-core arc welder (FCAW). Some other gear is also critical, e.g.:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Big gloves to insulate and protect our hands from the flying molten metal&lt;/li&gt;
&lt;li&gt;Thick, heavy clothes to cover your skin – again to protect from the flying molten metal. No really. I melted many holes in the synthetic fleece I wore the first time. Liquid metal goes all over the place. You need protection. Also probably a good idea not to do it around things that catch on fire easily.&lt;/li&gt;
&lt;li&gt;A face mask for skin and eye protection. Also because flipping your face mask up and down as you work is part of what makes welding cool and fun. You need one of these.&lt;/li&gt;
&lt;li&gt;A leather apron helps cover more of your clothes – molten metal again…&lt;/li&gt;
&lt;li&gt;I found these magnetic right angle jigs to be super useful for holding things in place while you tack them.&lt;/li&gt;
&lt;li&gt;An angle grinder. You will want to grind those horrible blobby welds you made down to something a little more smooth and presentable. “An angle grinder and paint make me the welder I ain’t”, they say.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The first time I did this, I did a test weld. It looked terrible, and it broke immediately under strain. I did 10 more, and they got much better. Am I good? No.&amp;nbsp;But steel is strong, and I’m making simple tables, so mediocre will do here. If you’re about to do this for the first time, I definitely recommend having some extra scrap tubes to practice on first. There are two adjustments on the welder: Wire feed speed and current. I found that I had to go pretty low on the current for these thin walled tubes, or else I ended up just blowing holes into the steel wall rather than welding them together.&lt;/p&gt;
&lt;p&gt;I’m not going to try to teach anyway how to weld because I suck at it and there are better resources. All I will say is that this is, in my opinion, &lt;strong&gt;totally worth trying&lt;/strong&gt;. I cannot properly explain just how satisfying it is to melt two pieces of metal together.&lt;/p&gt;
&lt;div class="quarto-figure quarto-figure-center"&gt;
&lt;figure class="figure"&gt;
&lt;p&gt;&lt;img src="https://jeffmcbride.net/posts/steel-wood-furniture/welding_gear.jpg" class="img-fluid figure-img"&gt;&lt;/p&gt;
&lt;figcaption&gt;Welding Gear&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id="wood" class="level2"&gt;
&lt;h2 class="anchored" data-anchor-id="wood"&gt;Wood&lt;/h2&gt;
&lt;p&gt;I ought some 1x3 “select pine” boards from home depot. For the second table, the 1x3’s were out of stock, so I went with 1x2 boards. These I glued up into panels for the table top, and for the five sides of the box. I built the two tables in series, rather than parallel, and so the process on the second evolved somewhat. Mainly, I finished the wood panels and installed drawer slides before gluing them into the box, and I built the box first and used it as a jig for doing the steel tube assembly.&lt;/p&gt;
&lt;p&gt;Drawers are made from some 0.5” ply, 0.25” ply (bottom) and 1x8 board (face) I had lying around as scrap.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="painting" class="level2"&gt;
&lt;h2 class="anchored" data-anchor-id="painting"&gt;Painting&lt;/h2&gt;
&lt;p&gt;After welding, and grinding the joints down flat with the angle grinder, I paint the metal with Rust-oleum flat black spray paint. Two coats, plus maybe a bit of touch up if I missed any spots.&lt;/p&gt;
&lt;p&gt;For the wood, I used Minwax Polyshades, with a satin finish. It seems to be pretty good. I’ve tried a couple others in the past, and these is easy to apply (rather than stain + poly later) and it holds up well to water and abuse. I do really struggle with getting an even coat, and avoiding drips. I suspect this is just bad technique though.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="bom-and-material-cost" class="level2"&gt;
&lt;h2 class="anchored" data-anchor-id="bom-and-material-cost"&gt;BOM and Material Cost&lt;/h2&gt;
&lt;table class="caption-top table"&gt;
&lt;thead&gt;
&lt;tr class="header"&gt;
&lt;th&gt;Descrpition&lt;/th&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;th&gt;Price (per table)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr class="odd"&gt;
&lt;td&gt;288” 0.75” 16ga tube&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.steelsupplylp.com/sku/108820"&gt;Steel Supply LP&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;$29 (shipped)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class="even"&gt;
&lt;td&gt;Select pine boards&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.homedepot.com/p/1-in-x-3-in-x-8-ft-Select-Pine-Board-922022/202535803"&gt;Home Depot&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;~$96&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class="odd"&gt;
&lt;td&gt;Drawer Slides&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.amazon.com/dp/B08L7M6VZX"&gt;Amazon&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;$13&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class="even"&gt;
&lt;td&gt;Handles&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.amazon.com/dp/B07FDRTZY8"&gt;Amazon&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;$7&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class="odd"&gt;
&lt;td&gt;Recessed Power Strip&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.amazon.com/dp/B0BN54HBTT"&gt;Amazon&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;$34&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class="even"&gt;
&lt;td&gt;Plastic feet&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.amazon.com/dp/B06XFLDXP3?th=1"&gt;Amazon&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;$5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class="odd"&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class="even"&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Total&lt;/strong&gt;:&lt;/td&gt;
&lt;td&gt;$184&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Not including a bit of 1x8 board and plywood that I used scrap for.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="other-tables" class="level2"&gt;
&lt;h2 class="anchored" data-anchor-id="other-tables"&gt;Other Tables&lt;/h2&gt;
&lt;p&gt;This is actually the third project since I bought the welder. First I built these…&lt;/p&gt;
&lt;section id="couch-end-table" class="level3"&gt;
&lt;h3 class="anchored" data-anchor-id="couch-end-table"&gt;Couch End Table&lt;/h3&gt;
&lt;p&gt;We have a convertible couch in our guest room, and I wanted a narrow table to go between it and the wall. It’s main use is as a place to sit and read, or if we have guests, a pull out bed. So, it needed a place to put a drink, a lamp, and to charge devices. I really wanted the lenght of it to match the couch.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://jeffmcbride.net/posts/steel-wood-furniture/couch_table.jpg" class="img-fluid" alt="Couch and Table"&gt; &lt;img src="https://jeffmcbride.net/posts/steel-wood-furniture/couch_table2.jpg" class="img-fluid" alt="Table alone"&gt;&lt;/p&gt;
&lt;/section&gt;
&lt;section id="end-of-hall-table" class="level3"&gt;
&lt;h3 class="anchored" data-anchor-id="end-of-hall-table"&gt;End of hall table&lt;/h3&gt;
&lt;p&gt;This one is just a bit of extra storage, and to decorate the blank end of our hallway. I found a peice of this wormwood board at a reclaimed furniture store and used that for the table top. Very simple construction with just a steel frame, and a couple of boards screwed onto that. The worst part of this one was getting stain into all those holes with a q-tip – that was a tedious job!&lt;/p&gt;
&lt;div class="quarto-figure quarto-figure-center"&gt;
&lt;figure class="figure"&gt;
&lt;p&gt;&lt;img src="https://jeffmcbride.net/posts/steel-wood-furniture/hall_table1.jpg" class="img-fluid figure-img"&gt;&lt;/p&gt;
&lt;figcaption&gt;Hall Table&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="some-more-process-photos" class="level2"&gt;
&lt;h2 class="anchored" data-anchor-id="some-more-process-photos"&gt;Some more process photos&lt;/h2&gt;
&lt;p&gt;To wrap up, here’s a random assortment of build photos…&lt;/p&gt;
&lt;p&gt;&lt;img src="https://jeffmcbride.net/posts/steel-wood-furniture/process1.jpg" class="img-fluid" alt="Process 1"&gt; &lt;img src="https://jeffmcbride.net/posts/steel-wood-furniture/process2.jpg" class="img-fluid" alt="Process 2"&gt; &lt;img src="https://jeffmcbride.net/posts/steel-wood-furniture/process3.jpg" class="img-fluid" alt="Process 3"&gt; &lt;img src="https://jeffmcbride.net/posts/steel-wood-furniture/process4.jpg" class="img-fluid" alt="Process 4"&gt; &lt;img src="https://jeffmcbride.net/posts/steel-wood-furniture/process5.jpg" class="img-fluid" alt="Process 5"&gt; &lt;img src="https://jeffmcbride.net/posts/steel-wood-furniture/process6.jpg" class="img-fluid" alt="Process 6"&gt;&lt;/p&gt;


&lt;/section&gt;</summary><category term="furniture" /><category term="projects" /></entry><entry><id>https://jeffmcbride.net/posts/zencan-device-demo/</id><title>Zencan Device Demo Video</title><link href="https://jeffmcbride.net/posts/zencan-device-demo/" rel="alternate" /><published>2025-06-18T07:00:00Z</published><updated>2025-06-18T07:00:00Z</updated><summary type="html">&lt;p&gt;Two weeks ago, I posted about a &lt;a href="../../posts/introducing-zencan/"&gt;Rust CANOpen Stack&lt;/a&gt; I was working on. It was in a bit of a disfunctional state at the time. Since then, I’ve ported two firmware projects to use it and in the process made a lot of progress on it! There’s still a ton to do to get it where I want, but today I recorded a quick live demo of the first two boards communicating using zencan! Production quality is low, but hopefully it’s still a useful way to get a feel for what it is trying to do.&lt;/p&gt;
&lt;section id="video" class="level2"&gt;
&lt;h2 class="anchored" data-anchor-id="video"&gt;Video&lt;/h2&gt;
&lt;iframe title="Zencan Demo/Walkthrough" width="560" height="315" src="https://video.jeffmcbride.net/videos/embed/4gbFyrZSE9ZaDYqivw3mpB" frameborder="0" allowfullscreen="" sandbox="allow-same-origin allow-scripts allow-popups allow-forms"&gt;
&lt;/iframe&gt;
&lt;/section&gt;
&lt;section id="notes" class="level2"&gt;
&lt;h2 class="anchored" data-anchor-id="notes"&gt;Notes&lt;/h2&gt;
&lt;p&gt;Code running on the boards:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;can-io: https://github.com/mcbridejc/can-io-firmware&lt;/li&gt;
&lt;li&gt;i4-controller: https://github.com/mcbridejc/i4-controller-firmware&lt;/li&gt;
&lt;/ul&gt;


&lt;/section&gt;</summary><category term="rust" /><category term="programming" /><category term="embedded" /><category term="zencan" /></entry><entry><id>https://jeffmcbride.net/posts/introducing-zencan/</id><title>Introducing Zencan: Easy CANOpen with Rust</title><link href="https://jeffmcbride.net/posts/introducing-zencan/" rel="alternate" /><published>2025-06-05T07:00:00Z</published><updated>2025-06-05T07:00:00Z</updated><summary type="html">&lt;p&gt;I’ve been kicking around an idea for a while now to create a CAN bus communication stack for Rust, and now it is finally taking shape. It’s still a work-in-progress, but I’m getting ready to publish a rough prototype, so I want to write about what I want it to be. I’m calling it &lt;em&gt;Zencan&lt;/em&gt;, and it’s an implementation of the &lt;a href="https://www.csselectronics.com/pages/canopen-tutorial-simple-intro"&gt;CANOpen&lt;/a&gt; protocol, with a few planned extensions.&lt;/p&gt;
&lt;p&gt;The repo for zencan is at https://github.com/mcbridejc/zencan.&lt;/p&gt;
&lt;p&gt;An example project using zencan can be found at https://github.com/mcbridejc/can-io-firmware&lt;/p&gt;
&lt;section id="background" class="level2"&gt;
&lt;h2 class="anchored" data-anchor-id="background"&gt;Background&lt;/h2&gt;
&lt;section id="whats-a-can-bus" class="level3"&gt;
&lt;h3 class="anchored" data-anchor-id="whats-a-can-bus"&gt;What’s a CAN bus?&lt;/h3&gt;
&lt;p&gt;The Controller Area Network (CAN) bus, first popularized in automotive applications, is widely used for industrial automation and motion control. If you’ve ever used an OBD-II reader to read engine error codes, you were using a CAN bus. I’ve personally used it in UAVs and on rocket engine controllers, but I feel that it does not get as much use in the open-source world as it deserves, so I want to make CAN easy with Rust in hopes that it will encourage more use.&lt;/p&gt;
&lt;p&gt;The CAN bus operates on a single differential pair, connected to all of the nodes. This means wiring multiple devices requires only two wires! It also means a single connection point can be used to plug in a computer and monitor and control all of the MCUs on the bus. It keeps wiring easy.&lt;/p&gt;
&lt;p&gt;Many MCUs come with CAN controllers, and they handle a lot of the communications overhead without any CPU involvement. For example, bus arbitration, errors and re-transmission, prioritized message queuing, and received message filtering are all often supported by hardware.&lt;/p&gt;
&lt;p&gt;CAN is fairly slow, with traditional CAN 2.0 busses running at a max bitrate of 1 Mbit/s. However, these days some MCUs are shipping with CAN-FD capable controllers, and I intend for Zencan to support CAN-FD. A CAN-FD bus allows for longer messages, and higher bitrates – usually up to 5-8 Mbit/s, although I have heard of buses as fast as 20 Mbit/s.&lt;/p&gt;
&lt;p&gt;The CAN protocol includes &lt;strong&gt;framing&lt;/strong&gt;, so everything is sent as a &lt;strong&gt;message&lt;/strong&gt;. A message has an ID, and a data payload. The ID is used to identify what’s in the message, and also implicitly, who it’s for (e.g.&amp;nbsp;here is where message filtering comes in: if a node on the bus knows which messages it is interested in, it can setup hardware filters to drop any others). I think this can be confusing sometimes, so I will restate: generally, nodes on a CAN bus do not have addresses, or IDs as a node. Instead, the &lt;strong&gt;messages&lt;/strong&gt; are tagged with IDs. If one wants to assign addresses to specific nodes, this has to be built on top of the CAN protocol!&lt;/p&gt;
&lt;/section&gt;
&lt;section id="why-i-like-can" class="level3"&gt;
&lt;h3 class="anchored" data-anchor-id="why-i-like-can"&gt;Why I like CAN&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;It is smaller and cheaper than Ethernet.&lt;/li&gt;
&lt;li&gt;On projects which have multiple MCUs, it is convenient to plug into a single bus and talk to all of them.&lt;/li&gt;
&lt;li&gt;There are standard tools for monitoring and plotting data on a CAN bus, with tools like &lt;a href="https://www.savvycan.com/"&gt;SavvyCAN&lt;/a&gt; and the &lt;a href="https://www.csselectronics.com/pages/can-dbc-file-database-intro"&gt;DBC&lt;/a&gt; file format one can easily plot data with no code.&lt;/li&gt;
&lt;li&gt;It is robust! With its low speed, smart controller hardware, and differential signalling, in my experience CAN is very tolerant of less-than-ideal wiring scenarios.&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="zencan-goals" class="level2"&gt;
&lt;h2 class="anchored" data-anchor-id="zencan-goals"&gt;Zencan Goals&lt;/h2&gt;
&lt;section id="rust" class="level3"&gt;
&lt;h3 class="anchored" data-anchor-id="rust"&gt;Rust&lt;/h3&gt;
&lt;p&gt;Zencan is built in Rust because that’s what I’m into lately, and there’s a lack of CANOpen stacks out there for Rust.&lt;/p&gt;
&lt;p&gt;I’ve been writing C/C++ for embedded systems a long time, and although there are definitely some trade-offs, I see Rust as the path forward for me. Look, if you catch me at the wrong time – like when I’ve just spent an hour appeasing the borrow checker, or am frustrated by difficulty viewing static variables in a debugger – I might offer a different opinion. But on the whole…&lt;/p&gt;
&lt;/section&gt;
&lt;section id="configurability" class="level3"&gt;
&lt;h3 class="anchored" data-anchor-id="configurability"&gt;Configurability&lt;/h3&gt;
&lt;p&gt;I want configurability! I want to be able to build a set of components, put them all on a CAN bus, and make them talk to each other, without having to change their firmware to do it. One way that a CAN bus has traditionally been designed is to keep a “master spreadsheet” of all the data that has to be transferred on the bus (motor temperature, motor current, velocity command, etc), cram these all into a set of messages, assign them each a CAN ID, and then write the appropriate software for each node to send and receive the relevant messages. This is fine, but I want to grab a generic motor controller board, and a generic IO board with an analog joystick plugged into it, and then configure the joystick to command the motor controller without modifying the software.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="observability" class="level3"&gt;
&lt;h3 class="anchored" data-anchor-id="observability"&gt;Observability&lt;/h3&gt;
&lt;p&gt;I want to standardize interactions one might want to have with MCUs on a bus so that I can solve them well once and re-use it. I want to plug into the bus and:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Report all the devices on the bus&lt;/li&gt;
&lt;li&gt;Program any device with new firmware&lt;/li&gt;
&lt;li&gt;Trivially plot values being generated by that device over time, or log for plotting later&lt;/li&gt;
&lt;li&gt;View metadata about the device such as software versions, serial numbers, etc&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;section id="ease" class="level3"&gt;
&lt;h3 class="anchored" data-anchor-id="ease"&gt;Ease&lt;/h3&gt;
&lt;p&gt;A lot of code is required to get all of that, but I shouldn’t have to think about it every time I start a new project. I should just be able to instantiate a node that works with a basic configuration file and a small amount of boilerplate.&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="architecture" class="level2"&gt;
&lt;h2 class="anchored" data-anchor-id="architecture"&gt;Architecture&lt;/h2&gt;
&lt;section id="canopen" class="level3"&gt;
&lt;h3 class="anchored" data-anchor-id="canopen"&gt;CANOpen&lt;/h3&gt;
&lt;p&gt;There is an existing protocol, built on top of CAN, which supports a LOT of what I want to do. It’s called &lt;a href="https://en.wikipedia.org/wiki/CANopen"&gt;CANOpen&lt;/a&gt;. I think that Zencan is going to actually be a Rust implementation of a CANOpen stack, with some extra features layered onto it. I have some concerns about this, like the fact that CANOpen is managed by the &lt;a href="https://www.can-cia.org/"&gt;CiA&lt;/a&gt;, which restricts access to the specification documents to members only. And even if you manage to find these documents, you may not find them as helpful or clear as you would like. But, the concept of the Object Dictionary and the Electronic Data Sheet (EDS) describing the objects in it, combined with the ability to map arbitrary object data into PDO messages, solves a lot of the configurability and observability requirements. CANOpen isn’t exactly what I would design from scratch, but it is pretty close and has the benefit of being an established standard with existing devices and software tools. And, as far as I can find, there isn’t yet a full-featured, mature CANOpen stack available for Rust. So I’m going to start here, and see if I can get everything I want out of it while maintaining CANOpen compatibility.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="specific-goals" class="level3"&gt;
&lt;h3 class="anchored" data-anchor-id="specific-goals"&gt;Specific Goals&lt;/h3&gt;
&lt;p&gt;Specific goals of the project are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Support creating nodes on embedded (&lt;code&gt;no_std&lt;/code&gt;) or linux targets&lt;/li&gt;
&lt;li&gt;No heap; all data statically allocated.&lt;/li&gt;
&lt;li&gt;Be CANOpen compatible&lt;/li&gt;
&lt;li&gt;Support device discovery&lt;/li&gt;
&lt;li&gt;Easy node configuration and object definition via a “device config” TOML file&lt;/li&gt;
&lt;li&gt;Support CAN-FD&lt;/li&gt;
&lt;li&gt;Support bulk data transfer – e.g.&amp;nbsp;a device I am targeting is an e-ink display, which requires transferring frames of pixels&lt;/li&gt;
&lt;li&gt;Standardized device software updates and version reporting&lt;/li&gt;
&lt;li&gt;A GUI and CLI interface for device management via socketcan&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;section id="configuration-files" class="level3"&gt;
&lt;h3 class="anchored" data-anchor-id="configuration-files"&gt;Configuration Files&lt;/h3&gt;
&lt;p&gt;I am still hashing this out, and it may change!&lt;/p&gt;
&lt;p&gt;There is a file format called EDS, or Electronic Data Sheet, which is used to describe the objects in a CANOpen object dictionary. I initially started out using an EDS file as an input to the code generation, but found that this was not ideal. For one thing, the format is fairly denormalized/redundant, which means that it can have inconsistencies and be difficult to edit manually. For another, I want the ability to specify Zencan specific options, and had no (good) way to work that into an EDS file. So instead, I’ve created a TOML schema for device configuration files, and EDS files will be generated as an output to be used with tools which support them.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="static-data-shared-between-objects-and-threads-without-heap" class="level3"&gt;
&lt;h3 class="anchored" data-anchor-id="static-data-shared-between-objects-and-threads-without-heap"&gt;Static Data, shared between objects and threads, without heap&lt;/h3&gt;
&lt;p&gt;The first challenge was sharing data between different contexts. Maybe it’s just code that doesn’t know each other (e.g.&amp;nbsp;the &lt;code&gt;zencan-node&lt;/code&gt; crate, and user’s application code) sharing access to the object dictionary. Maybe it is really on other threads. In a situation with &lt;code&gt;alloc&lt;/code&gt; available, one might simply use an &lt;code&gt;Arc&lt;/code&gt; to wrap the shared objects. Without Arc, we have to pass references. This quickly can lead to lifetime hell. Instead of managing these lifetimes with lots of generics, I decided to require many of the data structures to be static. In the embedded context, this is almost always the desired case anyway, and only one instance of a node is expected to exist. In other contexts, like hosting a node on linux, or in tests, this may not be the case. These can be addressed using &lt;code&gt;Box::leak&lt;/code&gt;, to make a heap allocated object static.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="dynamic-code-generation" class="level3"&gt;
&lt;h3 class="anchored" data-anchor-id="dynamic-code-generation"&gt;Dynamic Code Generation&lt;/h3&gt;
&lt;p&gt;Some code will be generated in build.rs, using the &lt;code&gt;zencan-build&lt;/code&gt; crate, based on a TOML file in the project. This is somewhat similar to the way that the &lt;a href="https://canopennode.github.io/"&gt;CANOpenNode&lt;/a&gt; C stack does it, but the EDS file, and more specifically the &lt;a href="https://github.com/CANopenNode/CANopenEditor"&gt;C# application&lt;/a&gt; used to edit the EDS file generate a C source and header file for inclusion in your application. I think that rust tooling will provide a good mechanism to auto-generate this code as part of the build process. It is still auto-generated code, and that has its downsides – especially readability – but I don’t see how to get around it. The generated code is saved to the compilation OUT_DIR, its name stored in an env var, so that it can be included via a macro somewhere in the application. This concept is modeled after &lt;a href="https://docs.slint.dev/latest/docs/rust/slint/docs/mcu/"&gt;slint&lt;/a&gt;, which does a similar thing for including generated code from a &lt;code&gt;.slint&lt;/code&gt; file which defines the GUI.&lt;/p&gt;
&lt;p&gt;In build.rs, the code is generated from the device config, with a name (e.g.&amp;nbsp;‘EXAMPLE1’):&lt;/p&gt;
&lt;div class="code-copy-outer-scaffold"&gt;&lt;div class="sourceCode" id="cb1" style="background: #f1f3f5;"&gt;&lt;pre class="sourceCode rust code-with-copy"&gt;&lt;code class="sourceCode rust"&gt;&lt;span id="cb1-1"&gt;&lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;fn&lt;/span&gt; main() &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb1-2"&gt;    &lt;span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;if&lt;/span&gt; &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;let&lt;/span&gt; &lt;span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;"&gt;Err&lt;/span&gt;(e) &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt; &lt;span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;zencan_build::&lt;/span&gt;build_node_from_device_config(&lt;span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;"&gt;"EXAMPLE1"&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt; &lt;span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;"&gt;"device_configs/example1.toml"&lt;/span&gt;) &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb1-3"&gt;        &lt;span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;eprintln!&lt;/span&gt;(&lt;span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;"&gt;"Error building node from example1.toml: {}"&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt; e)&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb1-4"&gt;        &lt;span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;std::process::&lt;/span&gt;exit(&lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;1&lt;/span&gt;)&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb1-5"&gt;    &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb1-6"&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Then in your application, the generated code can be included wherever you like as:&lt;/p&gt;
&lt;div class="code-copy-outer-scaffold"&gt;&lt;div class="sourceCode" id="cb2" style="background: #f1f3f5;"&gt;&lt;pre class="sourceCode rust code-with-copy"&gt;&lt;code class="sourceCode rust"&gt;&lt;span id="cb2-1"&gt;&lt;span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;zencan_node::include_modules!&lt;/span&gt;(EXAMPLE1)&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The name allows multiple nodes to be instantiated in a single application, although I do not yet have a use in mind for this, other than tests.&lt;/p&gt;
&lt;p&gt;Here’s an example of what the generated code looks like, which might help understand what’s being generated:&lt;/p&gt;
&lt;div class="code-copy-outer-scaffold"&gt;&lt;div class="sourceCode" id="cb3" style="background: #f1f3f5;"&gt;&lt;pre class="sourceCode rust code-with-copy"&gt;&lt;code class="sourceCode rust"&gt;&lt;span id="cb3-1"&gt;&lt;span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"&gt;#[&lt;/span&gt;allow&lt;span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"&gt;(&lt;/span&gt;dead_code&lt;span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"&gt;)]&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-2"&gt;&lt;span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"&gt;#[&lt;/span&gt;derive&lt;span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"&gt;(&lt;/span&gt;&lt;span class="bu" style="color: null;
background-color: null;
font-style: inherit;"&gt;Debug&lt;/span&gt;&lt;span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"&gt;)]&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-3"&gt;&lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;pub&lt;/span&gt; &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;struct&lt;/span&gt; Object1000 &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-4"&gt;    &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;pub&lt;/span&gt; value&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;:&lt;/span&gt; AtomicCell&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;lt;&lt;/span&gt;&lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;u32&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-5"&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-6"&gt;&lt;span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"&gt;#[&lt;/span&gt;allow&lt;span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"&gt;(&lt;/span&gt;dead_code&lt;span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"&gt;)]&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-7"&gt;&lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;impl&lt;/span&gt; Object1000 &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-8"&gt;    &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;pub&lt;/span&gt; &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;fn&lt;/span&gt; set_value(&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;amp;&lt;/span&gt;&lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;self&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt; value&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;:&lt;/span&gt; &lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;u32&lt;/span&gt;) &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-9"&gt;        &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;self&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;.&lt;/span&gt;value&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;.&lt;/span&gt;store(value)&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-10"&gt;    &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-11"&gt;    &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;pub&lt;/span&gt; &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;fn&lt;/span&gt; get_value(&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;amp;&lt;/span&gt;&lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;self&lt;/span&gt;) &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;u32&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-12"&gt;        &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;self&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;.&lt;/span&gt;value&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;.&lt;/span&gt;load()&lt;/span&gt;
&lt;span id="cb3-13"&gt;    &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-14"&gt;    &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;const&lt;/span&gt; &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;fn&lt;/span&gt; default() &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;Self&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-15"&gt;        Object1000 &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-16"&gt;            value&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;:&lt;/span&gt; &lt;span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;AtomicCell::&lt;/span&gt;new(&lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;0i64&lt;/span&gt; &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;as&lt;/span&gt; &lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;u32&lt;/span&gt;)&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-17"&gt;        &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-18"&gt;    &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-19"&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-20"&gt;&lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;impl&lt;/span&gt; ObjectRawAccess &lt;span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;for&lt;/span&gt; Object1000 &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-21"&gt;    &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;fn&lt;/span&gt; write(&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;amp;&lt;/span&gt;&lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;self&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt; sub&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;:&lt;/span&gt; &lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;u8&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt; offset&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;:&lt;/span&gt; &lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;usize&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt; data&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;:&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;amp;&lt;/span&gt;[&lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;u8&lt;/span&gt;]) &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;Result&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;lt;&lt;/span&gt;()&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt; AbortCode&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;gt;&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-22"&gt;        &lt;span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;if&lt;/span&gt; sub &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;==&lt;/span&gt; &lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;0&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-23"&gt;            &lt;span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;if&lt;/span&gt; offset &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;!=&lt;/span&gt; &lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;0&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-24"&gt;                &lt;span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;return&lt;/span&gt; &lt;span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;"&gt;Err&lt;/span&gt;(&lt;span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;zencan_node::common::sdo::AbortCode::&lt;/span&gt;UnsupportedAccess)&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-25"&gt;            &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-26"&gt;            &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;let&lt;/span&gt; value &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt; &lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;u32&lt;/span&gt;&lt;span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;::&lt;/span&gt;from_le_bytes(&lt;/span&gt;
&lt;span id="cb3-27"&gt;                data&lt;/span&gt;
&lt;span id="cb3-28"&gt;                    &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;.&lt;/span&gt;try_into()&lt;/span&gt;
&lt;span id="cb3-29"&gt;                    &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;.&lt;/span&gt;map_err(&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;|&lt;/span&gt;_&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;|&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-30"&gt;                        &lt;span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;if&lt;/span&gt; data&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;.&lt;/span&gt;len() &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;lt;&lt;/span&gt; &lt;span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;size_of::&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;lt;&lt;/span&gt;&lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;u32&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;gt;&lt;/span&gt;() &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-31"&gt;                            &lt;span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;zencan_node::common::sdo::AbortCode::&lt;/span&gt;DataTypeMismatchLengthLow&lt;/span&gt;
&lt;span id="cb3-32"&gt;                        &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;}&lt;/span&gt; &lt;span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;else&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-33"&gt;                            &lt;span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;zencan_node::common::sdo::AbortCode::&lt;/span&gt;DataTypeMismatchLengthHigh&lt;/span&gt;
&lt;span id="cb3-34"&gt;                        &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-35"&gt;                    &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;}&lt;/span&gt;)&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;?,&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-36"&gt;            )&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-37"&gt;            &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;self&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;.&lt;/span&gt;set_value(value)&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-38"&gt;            &lt;span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;"&gt;Ok&lt;/span&gt;(())&lt;/span&gt;
&lt;span id="cb3-39"&gt;        &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;}&lt;/span&gt; &lt;span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;else&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-40"&gt;            &lt;span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;"&gt;Err&lt;/span&gt;(&lt;span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;AbortCode::&lt;/span&gt;NoSuchSubIndex)&lt;/span&gt;
&lt;span id="cb3-41"&gt;        &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-42"&gt;    &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-43"&gt;    &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;fn&lt;/span&gt; read(&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;amp;&lt;/span&gt;&lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;self&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt; sub&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;:&lt;/span&gt; &lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;u8&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt; offset&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;:&lt;/span&gt; &lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;usize&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt; buf&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;:&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;amp;&lt;/span&gt;&lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;mut&lt;/span&gt; [&lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;u8&lt;/span&gt;]) &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;Result&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;lt;&lt;/span&gt;()&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt; AbortCode&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;gt;&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-44"&gt;        &lt;span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;if&lt;/span&gt; sub &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;==&lt;/span&gt; &lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;0&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-45"&gt;            &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;let&lt;/span&gt; bytes &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt; &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;self&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;.&lt;/span&gt;get_value()&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;.&lt;/span&gt;to_le_bytes()&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-46"&gt;            &lt;span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;if&lt;/span&gt; offset &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;+&lt;/span&gt; buf&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;.&lt;/span&gt;len() &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;gt;&lt;/span&gt; bytes&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;.&lt;/span&gt;len() &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-47"&gt;                &lt;span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;return&lt;/span&gt; &lt;span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;"&gt;Err&lt;/span&gt;(&lt;/span&gt;
&lt;span id="cb3-48"&gt;                    &lt;span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;zencan_node::common::sdo::AbortCode::&lt;/span&gt;DataTypeMismatchLengthHigh&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-49"&gt;                )&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-50"&gt;            &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-51"&gt;            buf&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;.&lt;/span&gt;copy_from_slice(&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;amp;&lt;/span&gt;bytes[offset&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;..&lt;/span&gt;offset &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;+&lt;/span&gt; buf&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;.&lt;/span&gt;len()])&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-52"&gt;            &lt;span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;"&gt;Ok&lt;/span&gt;(())&lt;/span&gt;
&lt;span id="cb3-53"&gt;        &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;}&lt;/span&gt; &lt;span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;else&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-54"&gt;            &lt;span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;"&gt;Err&lt;/span&gt;(&lt;span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;AbortCode::&lt;/span&gt;NoSuchSubIndex)&lt;/span&gt;
&lt;span id="cb3-55"&gt;        &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-56"&gt;    &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-57"&gt;    &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;fn&lt;/span&gt; sub_info(&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;amp;&lt;/span&gt;&lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;self&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt; sub&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;:&lt;/span&gt; &lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;u8&lt;/span&gt;) &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;Result&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;lt;&lt;/span&gt;SubInfo&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt; AbortCode&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;gt;&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-58"&gt;        &lt;span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;if&lt;/span&gt; sub &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;!=&lt;/span&gt; &lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;0&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-59"&gt;            &lt;span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;return&lt;/span&gt; &lt;span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;"&gt;Err&lt;/span&gt;(&lt;span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;AbortCode::&lt;/span&gt;NoSuchSubIndex)&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-60"&gt;        &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-61"&gt;        &lt;span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;"&gt;Ok&lt;/span&gt;(SubInfo &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-62"&gt;            access_type&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;:&lt;/span&gt; &lt;span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;zencan_node::common::objects::AccessType::&lt;/span&gt;Const&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-63"&gt;            data_type&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;:&lt;/span&gt; &lt;span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;zencan_node::common::objects::DataType::&lt;/span&gt;UInt32&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-64"&gt;            size&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;:&lt;/span&gt; &lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;4usize&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-65"&gt;            pdo_mapping&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;:&lt;/span&gt; &lt;span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;zencan_node::common::objects::PdoMapping::&lt;/span&gt;&lt;span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;"&gt;None&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-66"&gt;            persist&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;:&lt;/span&gt; &lt;span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;"&gt;false&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-67"&gt;        &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;}&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb3-68"&gt;    &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-69"&gt;    &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;fn&lt;/span&gt; object_code(&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;amp;&lt;/span&gt;&lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;self&lt;/span&gt;) &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;zencan_node::common::objects::&lt;/span&gt;ObjectCode &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-70"&gt;        &lt;span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;zencan_node::common::objects::ObjectCode::&lt;/span&gt;Var&lt;/span&gt;
&lt;span id="cb3-71"&gt;    &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-72"&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-73"&gt;&lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;pub&lt;/span&gt; &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;static&lt;/span&gt; OBJECT1000&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;:&lt;/span&gt; Object1000 &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt; &lt;span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;Object1000::&lt;/span&gt;&lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;default&lt;/span&gt;()&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-74"&gt;&lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;pub&lt;/span&gt; &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;static&lt;/span&gt; NODE_STATE&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;:&lt;/span&gt; NodeState&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;lt;&lt;/span&gt;&lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;4usize&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt; &lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;4usize&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;gt;&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt; &lt;span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;NodeState::&lt;/span&gt;new()&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-75"&gt;&lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;pub&lt;/span&gt; &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;static&lt;/span&gt; NODE_MBOX&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;:&lt;/span&gt; NodeMbox &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt; &lt;span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;NodeMbox::&lt;/span&gt;new(NODE_STATE&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;.&lt;/span&gt;rpdos())&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-76"&gt;&lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;pub&lt;/span&gt; &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;static&lt;/span&gt; OD_TABLE&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;:&lt;/span&gt; [ODEntry&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt; &lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;31usize&lt;/span&gt;] &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt; [&lt;/span&gt;
&lt;span id="cb3-77"&gt;    ODEntry &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-78"&gt;        index&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;:&lt;/span&gt; &lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;0x1000&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-79"&gt;        data&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;:&lt;/span&gt; &lt;span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;ObjectData::&lt;/span&gt;Storage(&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;amp;&lt;/span&gt;OBJECT1000)&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-80"&gt;    &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;},&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-81"&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Each object gets a struct defined, and an implementation of the &lt;code&gt;ObjectRawAccess&lt;/code&gt; trait. All objects are instantiated statically, and stored as a table in &lt;code&gt;OD_TABLE&lt;/code&gt;. This is the object dictionary. &lt;code&gt;NODE_STATE&lt;/code&gt; includes some static state information used by the instantiated Node, and &lt;code&gt;NODE_MBOX&lt;/code&gt; provides a Sync data structure for pass received messages, so that messages can be received in an IRQ handler.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="threading" class="level3"&gt;
&lt;h3 class="anchored" data-anchor-id="threading"&gt;Threading&lt;/h3&gt;
&lt;p&gt;The expectation is that a single thread will own the &lt;code&gt;Node&lt;/code&gt; object, and that most of the node behavior will happen on this thread in the form of calls to &lt;code&gt;Node::process&lt;/code&gt;. A separate object, the &lt;code&gt;NodeMbox&lt;/code&gt; allows for reception of messages on another thread. The expected use for this is to push received messages into the mailbox object in an IRQ handler. The object dictionary is Sync, using the &lt;a href="https://docs.rs/critical-section/latest/critical_section/"&gt;critical_section&lt;/a&gt; crate for protecting data access. Critical section will allow embedded applications to implement critical sections by disabling interrupts, and linux applications to do so using global locks.&lt;/p&gt;
&lt;p&gt;I tried to use &lt;a href="https://docs.rs/crossbeam/latest/crossbeam/"&gt;crossbeam&lt;/a&gt;’s &lt;a href="https://docs.rs/crossbeam/latest/crossbeam/atomic/struct.AtomicCell.html"&gt;AtomicCell&lt;/a&gt; for atomic access, but it does not currently support thumbv6 (i.e.&amp;nbsp;Cortex M0) targets at all, because these targets lack CAS support, and I intend to use this on M0 targets.&lt;/p&gt;
&lt;p&gt;The object dictionary, and all of the objects are Sync, as they may need to be accessed by various application code running in various thread contexts.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="object-storage-vs-callback" class="level3"&gt;
&lt;h3 class="anchored" data-anchor-id="object-storage-vs-callback"&gt;Object Storage vs Callback&lt;/h3&gt;
&lt;p&gt;Zencan supports two object types:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Storage&lt;/em&gt; objects have statically allocated storage for their data and support simple read/write operations&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Callback&lt;/em&gt; objects rely on callback functions to implement their access, allowing for validation and dynamic data handling during read or write&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;section id="control-tools" class="level3"&gt;
&lt;h3 class="anchored" data-anchor-id="control-tools"&gt;Control tools&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;zencan-cli&lt;/code&gt; crate comes with a REPL-style shell for interacting with devices over socketcan in real-time. A GUI version of this is planned as well.&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="summary" class="level2"&gt;
&lt;h2 class="anchored" data-anchor-id="summary"&gt;Summary&lt;/h2&gt;
&lt;p&gt;I consider Zencan now as a prototype, and still evolving. It needs more examples, documentation, and I need to implement it in more devices to flesh things out. There are still important features missing, and I expect some churn on the architecture/API. I hope over the next few months to integrate it into a few more projects, while continuing to develop it into something that someone besides myself might want to use! There are a few loose ends to tie up before I push a first release to crates.io, but I expect to be doing that soon.&lt;/p&gt;


&lt;/section&gt;</summary><category term="rust" /><category term="programming" /><category term="embedded" /><category term="zencan" /></entry><entry><id>https://jeffmcbride.net/posts/rtt-errors-with-wfi/</id><title>Errors using RTT and WFI</title><link href="https://jeffmcbride.net/posts/rtt-errors-with-wfi/" rel="alternate" /><published>2025-05-22T07:00:00Z</published><updated>2025-05-22T07:00:00Z</updated><summary type="html">&lt;p&gt;&lt;em&gt;This is for future-me, and for future internet searchers.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Using probe-rs to attach to an STM32G0 family MCU to read log messages via defmt over RTT, I encountered the following error:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; WARN probe_rs::util::rtt::client: RTT read pointer changed, re-attaching
 WARN probe_rs::rtt::channel: RTT channel name points to unrecognized memory. Bad target description?
 WARN probe_rs::rtt::channel: RTT channel name points to unrecognized memory. Bad target description?&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This occurred the same using &lt;a href="https://docs.rs/defmt-rtt/latest/defmt_rtt/"&gt;defmt_rtt&lt;/a&gt; or &lt;a href="https://docs.rs/rtt-target/latest/rtt_target/"&gt;rtt_target&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Reading through the probe-rs logs, it seemed that at some point after correctly reading the RTT memory block, it would start reading junk where it expected to read a &lt;code&gt;SEGGER RTT&lt;/code&gt; identifier.&lt;/p&gt;
&lt;p&gt;I am using &lt;a href="https://docs.rs/lilos/latest/lilos/"&gt;lilos&lt;/a&gt;, and in its executor it uses the WFI instruction to sleep. By trying different configurations, I found that the error only occurs when the lilos executor is run.&lt;/p&gt;
&lt;p&gt;On many cortex processors, some options have to be enabled to keep the clocks running on the debug core during sleep, e.g., on the STM32G0, the following bits have to be set:&lt;/p&gt;
&lt;div class="code-copy-outer-scaffold"&gt;&lt;div class="sourceCode" id="cb2" style="background: #f1f3f5;"&gt;&lt;pre class="sourceCode rust code-with-copy"&gt;&lt;code class="sourceCode rust"&gt;&lt;span id="cb2-1"&gt;&lt;span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;pac::&lt;/span&gt;DBGMCU&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;.&lt;/span&gt;cr()&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;.&lt;/span&gt;modify(&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;|&lt;/span&gt;w&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;|&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb2-2"&gt;    w&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;.&lt;/span&gt;set_dbg_standby(&lt;span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;"&gt;true&lt;/span&gt;)&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb2-3"&gt;    w&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;.&lt;/span&gt;set_dbg_stop(&lt;span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;"&gt;true&lt;/span&gt;)&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb2-4"&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;}&lt;/span&gt;)&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;What I learned today is that while this may keep the dbg core running, either the SRAM or something in the bus matrix still gets disabled unless there is at least one active master, and I guess the debug core does not count. So to keep access to SRAM via the debugger during sleep, you can enable a DMA:&lt;/p&gt;
&lt;div class="code-copy-outer-scaffold"&gt;&lt;div class="sourceCode" id="cb3" style="background: #f1f3f5;"&gt;&lt;pre class="sourceCode rust code-with-copy"&gt;&lt;code class="sourceCode rust"&gt;&lt;span id="cb3-1"&gt;&lt;span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;pac::&lt;/span&gt;RCC&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;.&lt;/span&gt;ahbenr()&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;.&lt;/span&gt;modify(&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;|&lt;/span&gt;w&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;|&lt;/span&gt; w&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;.&lt;/span&gt;set_dma1en(&lt;span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;"&gt;true&lt;/span&gt;))&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;With all three of these bits set, I can reliably read from SRAM over SWD and thus RTT is happy.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/probe-rs/probe-rs/issues/350#issuecomment-740550519"&gt;This probe-rs issue&lt;/a&gt; discusses the problem.&lt;/p&gt;</summary><category term="rust" /><category term="programming" /><category term="embedded" /></entry><entry><id>https://jeffmcbride.net/posts/rust-async-functions-as-state-machines/</id><title>Using a Rust async function as a polled state machine</title><link href="https://jeffmcbride.net/posts/rust-async-functions-as-state-machines/" rel="alternate" /><published>2025-05-16T07:00:00Z</published><updated>2025-05-16T07:00:00Z</updated><summary type="html">&lt;section id="using-async-functions-to-auto-generate-a-state-machine" class="level2"&gt;
&lt;h2 class="anchored" data-anchor-id="using-async-functions-to-auto-generate-a-state-machine"&gt;Using &lt;code&gt;async&lt;/code&gt; functions to auto-generate a state machine&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;How to poll an async function manually, and then use the rust compiler as a finite-state-machine generator&lt;/em&gt;&lt;/p&gt;
&lt;/section&gt;
&lt;section id="introduction" class="level2"&gt;
&lt;h2 class="anchored" data-anchor-id="introduction"&gt;Introduction&lt;/h2&gt;
&lt;p&gt;An interesting thing about Rust async functions is that what that &lt;code&gt;async&lt;/code&gt; keyword actually does is tell the compiler to automatically convert your function into a state machine so that it can pick up where it left off, and have it return a &lt;a href="https://doc.rust-lang.org/core/task/enum.Poll.html"&gt;core::task::Poll&lt;/a&gt; result every time it’s poll method is called. The future object get packed with any data it needs to keep track of it’s execution state.&lt;/p&gt;
&lt;p&gt;Normally, a one doesn’t call this poll method directly, but uses an Executor like &lt;a href="https://docs.rs/tokio/latest/tokio/"&gt;tokio&lt;/a&gt; or &lt;a href="https://docs.rs/lilos/latest/lilos/"&gt;lilos&lt;/a&gt; to poll it. The executor may not necessarily poll it in a busy loop, it may end up blocking the OS task waiting for file handles to be ready, or waiting for an interrupt to go off to flag the task as ready to run. But, there’s nothing stopping you from calling poll yourself!&lt;/p&gt;
&lt;p&gt;I’ve done this a few times, in a couple basic scenarios, and I think it can be useful. At least, it’s useful to know how to do it. The basic premise is that sometimes, you need to build a state machine that gets called periodically, and steps through a process. For complicated state machines, this can be keeping track of a lot of state transitions with big match variables. It’s fine, but sometimes I find that the state machine written as such is hard to reason about, compared to performing the same sequence of events in a blocking manner.&lt;/p&gt;
&lt;p&gt;The full demo code from this article can be found &lt;a href="https://github.com/mcbridejc/rust-async-state-machines-demo"&gt;on github&lt;/a&gt;&lt;/p&gt;
&lt;/section&gt;
&lt;section id="linear-sequence-vs-state-machine" class="level2"&gt;
&lt;h2 class="anchored" data-anchor-id="linear-sequence-vs-state-machine"&gt;Linear sequence vs state machine&lt;/h2&gt;
&lt;p&gt;As a simple comparison, consider a sequence of register initializations:&lt;/p&gt;
&lt;div class="code-copy-outer-scaffold"&gt;&lt;div class="sourceCode" id="cb1" style="background: #f1f3f5;"&gt;&lt;pre class="sourceCode rust code-with-copy"&gt;&lt;code class="sourceCode rust"&gt;&lt;span id="cb1-1"&gt;&lt;span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;/// A somewhat psuedo-codey demonstration of sending a series of commands&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb1-2"&gt;&lt;span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;/// to a device and waiting for an acknowledgement&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb1-3"&gt;&lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;fn&lt;/span&gt; init_device() &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb1-4"&gt;    &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;let&lt;/span&gt; device &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt; &lt;span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;ImaginaryDeviceCommander::&lt;/span&gt;new()&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb1-5"&gt;    device&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;.&lt;/span&gt;command(&lt;span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;"&gt;"write REG1 10"&lt;/span&gt;)&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb1-6"&gt;    &lt;span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;while&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;!&lt;/span&gt;device&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;.&lt;/span&gt;is_ready() &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;{}&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb1-7"&gt;    device&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;.&lt;/span&gt;command(&lt;span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;"&gt;"write REG2 20"&lt;/span&gt;)&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb1-8"&gt;    &lt;span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;while&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;!&lt;/span&gt;device&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;.&lt;/span&gt;is_ready() &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;{}&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb1-9"&gt;    device&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;.&lt;/span&gt;command(&lt;span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;"&gt;"write REG3 30"&lt;/span&gt;)&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb1-10"&gt;    &lt;span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;while&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;!&lt;/span&gt;device&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;.&lt;/span&gt;is_ready() &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;{}&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb1-11"&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;But now imagine that our process can’t block. Maybe it is part of a “super loop”: an application with a single thread that cyclically calls many modules so they can update themselves. Now the device initialization process needs to be refactors so that it can be performed iteratively over a series of function calls which always return quickly, something like this:&lt;/p&gt;
&lt;div class="code-copy-outer-scaffold"&gt;&lt;div class="sourceCode" id="cb2" style="background: #f1f3f5;"&gt;&lt;pre class="sourceCode rust code-with-copy"&gt;&lt;code class="sourceCode rust"&gt;&lt;span id="cb2-1"&gt;&lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;struct&lt;/span&gt; DeviceInitter &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb2-2"&gt;    command_sent&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;:&lt;/span&gt; &lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;bool&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb2-3"&gt;    step&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;:&lt;/span&gt; &lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;usize&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb2-4"&gt;    device&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;:&lt;/span&gt; ImaginaryDeviceCommander&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb2-5"&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb2-6"&gt;&lt;/span&gt;
&lt;span id="cb2-7"&gt;&lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;impl&lt;/span&gt; DeviceInitter &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb2-8"&gt;    &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;pub&lt;/span&gt; &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;fn&lt;/span&gt; run(&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;amp;&lt;/span&gt;&lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;mut&lt;/span&gt; &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;self&lt;/span&gt;) &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;bool&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb2-9"&gt;&lt;/span&gt;
&lt;span id="cb2-10"&gt;        &lt;span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;if&lt;/span&gt; step &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;3&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb2-11"&gt;            &lt;span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;match&lt;/span&gt; command_sent &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb2-12"&gt;                &lt;span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;"&gt;true&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb2-13"&gt;                    &lt;span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;if&lt;/span&gt; &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;self&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;.&lt;/span&gt;device&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;.&lt;/span&gt;is_ready() &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb2-14"&gt;                        &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;self&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;.&lt;/span&gt;step &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;+=&lt;/span&gt; &lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;1&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb2-15"&gt;                        &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;self&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;.&lt;/span&gt;command_sent &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt; &lt;span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;"&gt;false&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb2-16"&gt;                    &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb2-17"&gt;                &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb2-18"&gt;                &lt;span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;"&gt;false&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb2-19"&gt;                    &lt;span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;match&lt;/span&gt; &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;self&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;.&lt;/span&gt;step &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb2-20"&gt;                        &lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;0&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;self&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;.&lt;/span&gt;device&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;.&lt;/span&gt;command(&lt;span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;"&gt;"write REG1 10"&lt;/span&gt;)&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb2-21"&gt;                        &lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;1&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;self&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;.&lt;/span&gt;device&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;.&lt;/span&gt;command(&lt;span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;"&gt;"write REG2 20"&lt;/span&gt;)&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb2-22"&gt;                        &lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;2&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;self&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;.&lt;/span&gt;device&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;.&lt;/span&gt;command(&lt;span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;"&gt;"write REG3 30"&lt;/span&gt;)&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb2-23"&gt;                    &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb2-24"&gt;                    &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;self&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;.&lt;/span&gt;command_sent &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt; &lt;span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;"&gt;true&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb2-25"&gt;                &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb2-26"&gt;            &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb2-27"&gt;        &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb2-28"&gt;        &lt;span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;if&lt;/span&gt; &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;self&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;.&lt;/span&gt;step &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;==&lt;/span&gt; &lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;4&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb2-29"&gt;            &lt;span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;// Complete!&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb2-30"&gt;            &lt;span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;return&lt;/span&gt; &lt;span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;"&gt;true&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb2-31"&gt;        &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb2-32"&gt;    &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb2-33"&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The first version certainly seems nicer to read and understand!&lt;/p&gt;
&lt;/section&gt;
&lt;section id="how-to-manually-poll-an-async-function" class="level2"&gt;
&lt;h2 class="anchored" data-anchor-id="how-to-manually-poll-an-async-function"&gt;How to manually poll an async function&lt;/h2&gt;
&lt;p&gt;There are two main things to know:&lt;/p&gt;
&lt;ol type="1"&gt;
&lt;li&gt;You have to pin it&lt;/li&gt;
&lt;li&gt;You have to create a dummy context to pass to poll&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Both are easy to do!&lt;/p&gt;
&lt;p&gt;Finally, you need to make sure your future will yield at appropriate times. Normally, async functions yield back to the executor when they stop to wait on some IO operation to the OS, such as reading a network socket. But you can also use the &lt;a href="https://docs.rs/futures/latest/futures/macro.pending.html"&gt;pending!()&lt;/a&gt; macro to yield at any point.&lt;/p&gt;
&lt;p&gt;Here is a function to run a future once, and a simple state machine demoing it’s use:&lt;/p&gt;
&lt;div class="code-copy-outer-scaffold"&gt;&lt;div class="sourceCode" id="cb3" style="background: #f1f3f5;"&gt;&lt;pre class="sourceCode rust code-with-copy"&gt;&lt;code class="sourceCode rust"&gt;&lt;span id="cb3-1"&gt;&lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;use&lt;/span&gt; &lt;span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;core::pin::&lt;/span&gt;Pin&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-2"&gt;&lt;/span&gt;
&lt;span id="cb3-3"&gt;&lt;span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;/// Poll a future one time, and return its result if it completes&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-4"&gt;&lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;pub&lt;/span&gt; &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;fn&lt;/span&gt; poll_once&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;lt;&lt;/span&gt;T&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;gt;&lt;/span&gt;(&lt;/span&gt;
&lt;span id="cb3-5"&gt;    &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;mut&lt;/span&gt; f&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;:&lt;/span&gt; Pin&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;lt;&amp;amp;&lt;/span&gt;&lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;mut&lt;/span&gt; &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;dyn&lt;/span&gt; &lt;span class="bu" style="color: null;
background-color: null;
font-style: inherit;"&gt;Future&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;lt;&lt;/span&gt;Output &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt; T&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-6"&gt;) &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;Option&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;lt;&lt;/span&gt;T&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;gt;&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-7"&gt;    &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;let&lt;/span&gt; &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;mut&lt;/span&gt; cx &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt; &lt;span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;futures::task::Context::&lt;/span&gt;from_waker(&lt;/span&gt;
&lt;span id="cb3-8"&gt;        &lt;span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;futures::task::&lt;/span&gt;noop_waker_ref()&lt;/span&gt;
&lt;span id="cb3-9"&gt;    )&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-10"&gt;&lt;/span&gt;
&lt;span id="cb3-11"&gt;    &lt;span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;match&lt;/span&gt; f&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;.&lt;/span&gt;as_mut()&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;.&lt;/span&gt;poll(&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;amp;&lt;/span&gt;&lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;mut&lt;/span&gt; cx) &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-12"&gt;        &lt;span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;core::task::Poll::&lt;/span&gt;Ready(result) &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;"&gt;Some&lt;/span&gt;(result)&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-13"&gt;        &lt;span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;core::task::Poll::&lt;/span&gt;Pending &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;"&gt;None&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-14"&gt;    &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-15"&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-16"&gt;&lt;/span&gt;
&lt;span id="cb3-17"&gt;&lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;fn&lt;/span&gt; main() &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-18"&gt;    &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;let&lt;/span&gt; &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;mut&lt;/span&gt; future &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt; &lt;span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;pin!&lt;/span&gt;(&lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;async&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-19"&gt;        &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;let&lt;/span&gt; &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;mut&lt;/span&gt; i &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt; &lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;0&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-20"&gt;        &lt;span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;loop&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-21"&gt;            &lt;span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;if&lt;/span&gt; i &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;==&lt;/span&gt; &lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;2&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-22"&gt;                &lt;span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;return&lt;/span&gt; &lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;42&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-23"&gt;            &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;}&lt;/span&gt; &lt;span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;else&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-24"&gt;                i &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;+=&lt;/span&gt; &lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;1&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-25"&gt;                &lt;span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;// Yield back to the executor. This means that&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-26"&gt;                &lt;span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;// the future's `poll` function will return&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-27"&gt;                &lt;span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;// `Poll::Pending` and the subsequent call will&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-28"&gt;                &lt;span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;// pick up here&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-29"&gt;                &lt;span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;pending!&lt;/span&gt;()&lt;/span&gt;
&lt;span id="cb3-30"&gt;            &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-31"&gt;        &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-32"&gt;    &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;}&lt;/span&gt;)&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-33"&gt;&lt;/span&gt;
&lt;span id="cb3-34"&gt;    &lt;span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;// i = 0. Not done.&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-35"&gt;    &lt;span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;assert_eq!&lt;/span&gt;(poll_once(future&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;.&lt;/span&gt;as_mut())&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt; &lt;span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;"&gt;None&lt;/span&gt;)&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-36"&gt;    &lt;span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;// i = 1. Not done.&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-37"&gt;    &lt;span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;assert_eq!&lt;/span&gt;(poll_once(future&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;.&lt;/span&gt;as_mut())&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt; &lt;span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;"&gt;None&lt;/span&gt;)&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-38"&gt;    &lt;span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;// i = 2. Done!&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-39"&gt;    &lt;span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;assert_eq!&lt;/span&gt;(poll_once(future&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;.&lt;/span&gt;as_mut())&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt; &lt;span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;"&gt;Some&lt;/span&gt;(&lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;42&lt;/span&gt;))&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-40"&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;There’s no async runtime like tokio, async-io, etc, here; the only dependency is the &lt;a href="https://docs.rs/futures/latest/futures/"&gt;futures&lt;/a&gt; crate.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="use-case-chunked-serialization" class="level2"&gt;
&lt;h2 class="anchored" data-anchor-id="use-case-chunked-serialization"&gt;Use case: chunked serialization&lt;/h2&gt;
&lt;p&gt;One reason I wrote this article is that a library I am working on needed to serialize a handful of data structures in an embedded context to persist it to flash. I want to impose minimum requirements on the application, so:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;No heap usage, which means no returning a &lt;code&gt;Vec&amp;lt;u8&amp;gt;&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Do not assume the serialized output will fit in RAM. It has to be able to be written to flash in small chunks. Only the application knows what “small” means here.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This means that the serializer has to expect a series of &lt;code&gt;read(buf: &amp;amp;mut [u8])&lt;/code&gt; calls, copy the next N bytes into the buffer, and then keep track of where it was so that it can pick up where it left off on the next call to &lt;code&gt;read&lt;/code&gt;. This is kind of a pain in the ass! It means, e.g., that when serializing a u32, it may end up writing the first byte on one call to read, and the next 3 bytes on the next.&lt;/p&gt;
&lt;p&gt;I started writing a state machine to do this, and then decided to try it as an async function. As an example for this article, I simplified the serialization a bit, but it is essentially the same. We are going to serialize some objects that look like this:&lt;/p&gt;
&lt;div class="code-copy-outer-scaffold"&gt;&lt;div class="sourceCode" id="cb4" style="background: #f1f3f5;"&gt;&lt;pre class="sourceCode rust code-with-copy"&gt;&lt;code class="sourceCode rust"&gt;&lt;span id="cb4-1"&gt;&lt;span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;/// Just an example of an object to be serialized.&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb4-2"&gt;&lt;span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;/// Each object has a type, and a block of bytes to describe it.&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb4-3"&gt;&lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;struct&lt;/span&gt; Object &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb4-4"&gt;    &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;pub&lt;/span&gt; object_type&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;:&lt;/span&gt; &lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;u8&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb4-5"&gt;    &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;pub&lt;/span&gt; data&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;:&lt;/span&gt; &lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;Vec&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;lt;&lt;/span&gt;&lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;u8&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb4-6"&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;They will get serialized into the flash as &lt;code&gt;[&amp;lt;length:u16&amp;gt; &amp;lt;object_type:u8&amp;gt; &amp;lt;data:n&amp;gt;]&lt;/code&gt;, where length is the length of the data + object type byte.&lt;/p&gt;
&lt;p&gt;So first, I need to create an async function that serializes the data, &lt;strong&gt;but yields after writing out each byte&lt;/strong&gt;:&lt;/p&gt;
&lt;div class="code-copy-outer-scaffold"&gt;&lt;div class="sourceCode" id="cb5" style="background: #f1f3f5;"&gt;&lt;pre class="sourceCode rust code-with-copy"&gt;&lt;code class="sourceCode rust"&gt;&lt;span id="cb5-1"&gt;&lt;span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;/// Utility to write bytes to a provided function, returning a Poll::Pending between each byte&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb5-2"&gt;&lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;async&lt;/span&gt; &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;fn&lt;/span&gt; write_bytes(src&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;:&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;amp;&lt;/span&gt;[&lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;u8&lt;/span&gt;]&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt; &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;mut&lt;/span&gt; write_fn&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;:&lt;/span&gt; &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;impl&lt;/span&gt; &lt;span class="bu" style="color: null;
background-color: null;
font-style: inherit;"&gt;FnMut&lt;/span&gt;(&lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;u8&lt;/span&gt;)) &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb5-3"&gt;    &lt;span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;for&lt;/span&gt; i &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;in&lt;/span&gt; &lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;0&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;..&lt;/span&gt;src&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;.&lt;/span&gt;len() &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb5-4"&gt;        write_fn(src[i])&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb5-5"&gt;        &lt;span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;pending!&lt;/span&gt;()&lt;/span&gt;
&lt;span id="cb5-6"&gt;    &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb5-7"&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb5-8"&gt;&lt;/span&gt;
&lt;span id="cb5-9"&gt;&lt;span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;/// Implements a serializer for a list of objects.&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb5-10"&gt;&lt;span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;///&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb5-11"&gt;&lt;span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;/// Implementing this as an async function allows the sequence of writes to be written linearly and&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb5-12"&gt;&lt;span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;/// simply, allowing rustc to compile it into a state machine so that the serialization can be&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb5-13"&gt;&lt;span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;/// broken up into arbitrary chunks&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb5-14"&gt;&lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;async&lt;/span&gt; &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;fn&lt;/span&gt; polling_write(objects&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;:&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;amp;&lt;/span&gt;[Object]&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt; &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;mut&lt;/span&gt; write_fn&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;:&lt;/span&gt; &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;impl&lt;/span&gt; &lt;span class="bu" style="color: null;
background-color: null;
font-style: inherit;"&gt;FnMut&lt;/span&gt;(&lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;u8&lt;/span&gt;)) &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb5-15"&gt;    &lt;span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;for&lt;/span&gt; obj &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;in&lt;/span&gt; objects &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb5-16"&gt;        &lt;span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;// First serialize the size of the object, which is the length of the data + 1 byte for the&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb5-17"&gt;        &lt;span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;// object type&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb5-18"&gt;        &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;let&lt;/span&gt; len &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt; (obj&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;.&lt;/span&gt;data&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;.&lt;/span&gt;len() &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;+&lt;/span&gt; &lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;1&lt;/span&gt;) &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;as&lt;/span&gt; &lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;u16&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb5-19"&gt;        write_bytes(&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;amp;&lt;/span&gt;len&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;.&lt;/span&gt;to_le_bytes()&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;amp;&lt;/span&gt;&lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;mut&lt;/span&gt; write_fn)&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;.&lt;/span&gt;&lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;await&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb5-20"&gt;        &lt;span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;// Serialize the object type&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb5-21"&gt;        write_bytes(&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;amp;&lt;/span&gt;[obj&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;.&lt;/span&gt;object_type]&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;amp;&lt;/span&gt;&lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;mut&lt;/span&gt; write_fn)&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;.&lt;/span&gt;&lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;await&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb5-22"&gt;        &lt;span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;// Serialize the object data&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb5-23"&gt;        write_bytes(&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;amp;&lt;/span&gt;obj&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;.&lt;/span&gt;data&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;amp;&lt;/span&gt;&lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;mut&lt;/span&gt; write_fn)&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;.&lt;/span&gt;&lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;await&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb5-24"&gt;    &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb5-25"&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Great, that wasn’t so bad.&lt;/p&gt;
&lt;p&gt;Now, lets wrap up the future into a struct that can provide the required read function:&lt;/p&gt;
&lt;div class="code-copy-outer-scaffold"&gt;&lt;div class="sourceCode" id="cb6" style="background: #f1f3f5;"&gt;&lt;pre class="sourceCode rust code-with-copy"&gt;&lt;code class="sourceCode rust"&gt;&lt;span id="cb6-1"&gt;&lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;pub&lt;/span&gt; &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;struct&lt;/span&gt; AsyncSerializer&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;lt;&lt;/span&gt;&lt;span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;"&gt;'a&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt; &lt;span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;"&gt;'b&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt; F&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;:&lt;/span&gt; &lt;span class="bu" style="color: null;
background-color: null;
font-style: inherit;"&gt;Future&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;lt;&lt;/span&gt;Output &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt; ()&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb6-2"&gt;    fut&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;:&lt;/span&gt; Pin&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;lt;&amp;amp;&lt;/span&gt;&lt;span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;"&gt;'a&lt;/span&gt; &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;mut&lt;/span&gt; F&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb6-3"&gt;    reg&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;:&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;amp;&lt;/span&gt;&lt;span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;"&gt;'b&lt;/span&gt; RefCell&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;lt;&lt;/span&gt;&lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;u8&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb6-4"&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb6-5"&gt;&lt;/span&gt;
&lt;span id="cb6-6"&gt;&lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;impl&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;lt;&lt;/span&gt;&lt;span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;"&gt;'a&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt; &lt;span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;"&gt;'b&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt; F&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;gt;&lt;/span&gt; AsyncSerializer&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;lt;&lt;/span&gt;&lt;span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;"&gt;'a&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt; &lt;span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;"&gt;'b&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt; F&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb6-7"&gt;&lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;where&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb6-8"&gt;    F&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;:&lt;/span&gt; &lt;span class="bu" style="color: null;
background-color: null;
font-style: inherit;"&gt;Future&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;lt;&lt;/span&gt;Output &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt; ()&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb6-9"&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb6-10"&gt;    &lt;span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;/// Create a serializer using a future and a shared data buffer&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb6-11"&gt;    &lt;span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;///&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb6-12"&gt;    &lt;span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;/// The future should write to reg, and it *must yield after writing each byte*.&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb6-13"&gt;    &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;pub&lt;/span&gt; &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;fn&lt;/span&gt; new(fut&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;:&lt;/span&gt; Pin&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;lt;&amp;amp;&lt;/span&gt;&lt;span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;"&gt;'a&lt;/span&gt; &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;mut&lt;/span&gt; F&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;gt;,&lt;/span&gt; reg&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;:&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;amp;&lt;/span&gt;&lt;span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;"&gt;'b&lt;/span&gt; RefCell&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;lt;&lt;/span&gt;&lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;u8&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;gt;&lt;/span&gt;) &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;Self&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb6-14"&gt;        &lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;Self&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt; fut&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt; reg &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb6-15"&gt;    &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb6-16"&gt;&lt;/span&gt;
&lt;span id="cb6-17"&gt;    &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;pub&lt;/span&gt; &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;fn&lt;/span&gt; read(&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;amp;&lt;/span&gt;&lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;mut&lt;/span&gt; &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;self&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt; buf&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;:&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;amp;&lt;/span&gt;&lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;mut&lt;/span&gt; [&lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;u8&lt;/span&gt;]) &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;usize&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb6-18"&gt;        &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;let&lt;/span&gt; &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;mut&lt;/span&gt; pos &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt; &lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;0&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb6-19"&gt;        &lt;span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;loop&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb6-20"&gt;            &lt;span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;if&lt;/span&gt; pos &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;gt;=&lt;/span&gt; buf&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;.&lt;/span&gt;len() &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb6-21"&gt;                &lt;span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;return&lt;/span&gt; pos&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb6-22"&gt;            &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb6-23"&gt;&lt;/span&gt;
&lt;span id="cb6-24"&gt;            &lt;span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;if&lt;/span&gt; &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;let&lt;/span&gt; &lt;span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;"&gt;Some&lt;/span&gt;(_) &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt; poll_once(&lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;self&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;.&lt;/span&gt;fut&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;.&lt;/span&gt;as_mut()) &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb6-25"&gt;                &lt;span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;// Serialization is complete&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb6-26"&gt;                &lt;span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;return&lt;/span&gt; pos&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb6-27"&gt;            &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;}&lt;/span&gt; &lt;span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;else&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb6-28"&gt;                buf[pos] &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;*&lt;/span&gt;&lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;self&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;.&lt;/span&gt;reg&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;.&lt;/span&gt;borrow()&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb6-29"&gt;                pos &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;+=&lt;/span&gt; &lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;1&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb6-30"&gt;            &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb6-31"&gt;        &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb6-32"&gt;    &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb6-33"&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;And finally, put it together and use it:&lt;/p&gt;
&lt;div class="code-copy-outer-scaffold"&gt;&lt;div class="sourceCode" id="cb7" style="background: #f1f3f5;"&gt;&lt;pre class="sourceCode rust code-with-copy"&gt;&lt;code class="sourceCode rust"&gt;&lt;span id="cb7-1"&gt;&lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;fn&lt;/span&gt; main() &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb7-2"&gt;    &lt;span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;// `byte_buf` serves as a temporary register for communication between the future which implements&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb7-3"&gt;    &lt;span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;// serialization and the AsyncSerializer wrapper&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb7-4"&gt;    &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;let&lt;/span&gt; byte_buf &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt; &lt;span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;RefCell::&lt;/span&gt;new(&lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;0u8&lt;/span&gt;)&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb7-5"&gt;    &lt;span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;// Create the future from an async function, each time it is polled, it will write one byte to&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb7-6"&gt;    &lt;span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;// `byte_buf`&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb7-7"&gt;    &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;let&lt;/span&gt; future &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt; &lt;span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;pin!&lt;/span&gt;(polling_write(&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;amp;&lt;/span&gt;objects&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;|&lt;/span&gt;b&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;|&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;*&lt;/span&gt;byte_buf&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;.&lt;/span&gt;borrow_mut() &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt; b))&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb7-8"&gt;    &lt;span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;// instantiate the wrapper which will drive the future&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb7-9"&gt;    &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;let&lt;/span&gt; &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;mut&lt;/span&gt; async_serializer &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt; &lt;span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;AsyncSerializer::&lt;/span&gt;new(future&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;amp;&lt;/span&gt;byte_buf)&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb7-10"&gt;&lt;/span&gt;
&lt;span id="cb7-11"&gt;    &lt;span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;// A vec to store the fully written data&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb7-12"&gt;    &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;let&lt;/span&gt; &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;mut&lt;/span&gt; output &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt; &lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;Vec&lt;/span&gt;&lt;span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;::&lt;/span&gt;new()&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb7-13"&gt;    &lt;span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;// A temporary small buffer. Data will be serialized one 3-byte chunk at a time into this buffer.&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb7-14"&gt;    &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;let&lt;/span&gt; &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;mut&lt;/span&gt; buf &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt; [&lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;0&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt; &lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;3&lt;/span&gt;]&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb7-15"&gt;    &lt;span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;loop&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb7-16"&gt;        &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;let&lt;/span&gt; write_size &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt; async_serializer&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;.&lt;/span&gt;read(&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;amp;&lt;/span&gt;&lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;mut&lt;/span&gt; buf)&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb7-17"&gt;        output&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;.&lt;/span&gt;extend_from_slice(&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;amp;&lt;/span&gt;buf[&lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;0&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;..&lt;/span&gt;write_size])&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb7-18"&gt;        &lt;span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;if&lt;/span&gt; write_size &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;lt;&lt;/span&gt; buf&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;.&lt;/span&gt;len() &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb7-19"&gt;            &lt;span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;break&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb7-20"&gt;        &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb7-21"&gt;    &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb7-22"&gt;&lt;/span&gt;
&lt;span id="cb7-23"&gt;    &lt;span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;assert_eq!&lt;/span&gt;(output&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt; [&lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;5&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt;&lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;0&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt;&lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;0&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt;&lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;1&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt;&lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;2&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt;&lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;3&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt;&lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;4&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt; &lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;9&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt;&lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;0&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt;&lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;1&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt;&lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;1&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt;&lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;2&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt;&lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;3&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt;&lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;4&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt;&lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;5&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt;&lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;6&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt;&lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;7&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt;&lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;8&lt;/span&gt;])&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb7-24"&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This is made a little more complicated by the requirement that it not use the Heap. If we could use the heap, the future could be wrapped by &lt;code&gt;Box::pin&lt;/code&gt;, and that makes managing lifetimes easier. As it is, we need to pin the future on the stack, which means that the stack frame it is created on has to outlive the serialization process. To make this a easier on the user, we can create a function to create the future and the shared RefCell, wrap them up, and provide them to a user provided callback:&lt;/p&gt;
&lt;div class="code-copy-outer-scaffold"&gt;&lt;div class="sourceCode" id="cb8" style="background: #f1f3f5;"&gt;&lt;pre class="sourceCode rust code-with-copy"&gt;&lt;code class="sourceCode rust"&gt;&lt;span id="cb8-1"&gt;    &lt;span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;/// Create a serializer for objects, and pass it to the provided callback&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb8-2"&gt;    &lt;span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;///&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb8-3"&gt;    &lt;span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;/// This allows for pinning the required data on the stack for the duration of the serializer&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb8-4"&gt;    &lt;span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;/// lifetime&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb8-5"&gt;    &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;pub&lt;/span&gt; &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;fn&lt;/span&gt; serialize_objects(objects&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;:&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;amp;&lt;/span&gt;[Object]&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt; &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;mut&lt;/span&gt; cb&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;:&lt;/span&gt; &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;impl&lt;/span&gt; &lt;span class="bu" style="color: null;
background-color: null;
font-style: inherit;"&gt;FnMut&lt;/span&gt;(&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;amp;&lt;/span&gt;&lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;mut&lt;/span&gt; &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;dyn&lt;/span&gt; PersistSerializer)) &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb8-6"&gt;        &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;let&lt;/span&gt; reg &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt; &lt;span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;RefCell::&lt;/span&gt;new(&lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;0u8&lt;/span&gt;)&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb8-7"&gt;        &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;let&lt;/span&gt; fut &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt; &lt;span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;pin!&lt;/span&gt;(polling_serializer(objects&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;|&lt;/span&gt;b&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;|&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;*&lt;/span&gt;(&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;amp;&lt;/span&gt;reg)&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;.&lt;/span&gt;borrow_mut() &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt; b))&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb8-8"&gt;        &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;let&lt;/span&gt; &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;mut&lt;/span&gt; serializer &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt; &lt;span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;AsyncSerializer::&lt;/span&gt;new(fut&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;amp;&lt;/span&gt;reg)&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb8-9"&gt;        cb(&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;amp;&lt;/span&gt;&lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;mut&lt;/span&gt; serializer)&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb8-10"&gt;    &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;And then the usage becomes:&lt;/p&gt;
&lt;div class="code-copy-outer-scaffold"&gt;&lt;div class="sourceCode" id="cb9" style="background: #f1f3f5;"&gt;&lt;pre class="sourceCode rust code-with-copy"&gt;&lt;code class="sourceCode rust"&gt;&lt;span id="cb9-1"&gt;    &lt;span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;// A vec to store the fully written data&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb9-2"&gt;    &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;let&lt;/span&gt; &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;mut&lt;/span&gt; async_output &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt; &lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;Vec&lt;/span&gt;&lt;span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;::&lt;/span&gt;new()&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb9-3"&gt;&lt;/span&gt;
&lt;span id="cb9-4"&gt;    &lt;span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;async_serialize::&lt;/span&gt;serialize_objects(&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;amp;&lt;/span&gt;objects&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;|&lt;/span&gt;serializer&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;|&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb9-5"&gt;        &lt;span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;// A temporary small buffer. Data will be serialized one 3-byte chunk at a time into this buffer.&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb9-6"&gt;        &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;let&lt;/span&gt; &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;mut&lt;/span&gt; buf &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt; [&lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;0&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt; &lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;3&lt;/span&gt;]&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb9-7"&gt;        &lt;span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;loop&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb9-8"&gt;            &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;let&lt;/span&gt; write_size &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt; serializer&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;.&lt;/span&gt;read(&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;amp;&lt;/span&gt;&lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;mut&lt;/span&gt; buf)&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb9-9"&gt;            async_output&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;.&lt;/span&gt;extend_from_slice(&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;amp;&lt;/span&gt;buf[&lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;0&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;..&lt;/span&gt;write_size])&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb9-10"&gt;            &lt;span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;if&lt;/span&gt; write_size &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;lt;&lt;/span&gt; buf&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;.&lt;/span&gt;len() &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb9-11"&gt;                &lt;span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;break&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb9-12"&gt;            &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb9-13"&gt;        &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb9-14"&gt;    &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;}&lt;/span&gt;)&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;/section&gt;
&lt;section id="trade-offs" class="level2"&gt;
&lt;h2 class="anchored" data-anchor-id="trade-offs"&gt;Trade-offs&lt;/h2&gt;
&lt;p&gt;To be honest, I’m not 100% convinced this is a great idea. There are &lt;em&gt;for sure&lt;/em&gt; times when it’s better to write out a sequence of linear actions. But, there are downsides! There is added complexity in creating and understanding the async function, dealing with the pinning (which is especially painful without &lt;code&gt;alloc&lt;/code&gt;), and also, stepping through async functions in the debugger can be unpleasant.&lt;/p&gt;
&lt;p&gt;There is another more complicated example in &lt;a href="https://github.com/mcbridejc/rust-async-state-machines-demo"&gt;the repo&lt;/a&gt;, and splitting off the processing into the future adds a layer of communication: the “main” task cannot pass data into the future once it is created, so they have to communicate using a shared structure, which has to be Sync (at least if you want to avoid &lt;code&gt;unsafe&lt;/code&gt; code), even though we can actually be sure that the future will never be executed on another thread.&lt;/p&gt;


&lt;/section&gt;</summary><category term="rust" /><category term="programming" /></entry><entry><id>https://jeffmcbride.net/posts/gauss-speedway/</id><title>The Gauss Speedway</title><link href="https://jeffmcbride.net/posts/gauss-speedway/" rel="alternate" /><published>2022-11-15T08:00:00Z</published><updated>2022-11-15T08:00:00Z</updated><summary type="html">&lt;section id="summary" class="level2"&gt;
&lt;h2 class="anchored" data-anchor-id="summary"&gt;Summary&lt;/h2&gt;
&lt;p&gt;Here’s a fun toy project I finally finished this month. It’s a racetrack built with a printed circuit board, and small 3D printed magnetic cars. They just drive around the circuit when you apply (the right) current! That’s it! That’s the project!&lt;/p&gt;
&lt;div class="quarto-video ratio ratio-16x9"&gt;&lt;iframe data-external="1" src="https://player.vimeo.com/video/771275219" frameborder="0" title="" allow="autoplay; fullscreen; picture-in-picture" allowfullscreen=""&gt;&lt;/iframe&gt;&lt;/div&gt;
&lt;/section&gt;
&lt;section id="history" class="level2"&gt;
&lt;h2 class="anchored" data-anchor-id="history"&gt;History&lt;/h2&gt;
&lt;p&gt;It started from a conversation with &lt;a href="https://kevinlynagh.com"&gt;Kevin Lynagh&lt;/a&gt;, who did some experiments&lt;sup&gt;1&lt;/sup&gt; with &lt;a href="https://kevinlynagh.com/pcb-stepper/"&gt;moving magnets around a 2D grid&lt;/a&gt; with wires running both directions, which planted the idea of a racetrack version in my head. Thanks to my now rather extensive practice designing weird PCB geometries in Kicad with python, I was able to design a test board pretty quickly over the new years holiday and ordered it. It worked pretty well, I posted a &lt;a href="https://twitter.com/mcbridejc/status/1493776011627884548"&gt;short video&lt;/a&gt; and then forgot about it for a while.&lt;/p&gt;
&lt;p&gt;Then I picked it up again in the spring, imagining it as kind of a slot car game. So this led me down a deep rabbit hole of a) building a wireless slot car controller, and b) a wireless charging station for the slot car controller.&lt;/p&gt;
&lt;div class="quarto-figure quarto-figure-center"&gt;
&lt;figure class="figure"&gt;
&lt;p&gt;&lt;a href="slot_controller.png" class="lightbox" data-gallery="quarto-lightbox-gallery-1" title="Slot controller"&gt;&lt;img src="https://jeffmcbride.net/posts/gauss-speedway/slot_controller.png" class="img-fluid figure-img" width="600" alt="Slot controller"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;figcaption&gt;Slot controller&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/div&gt;
&lt;p&gt;I spent a fair bit of time learning about wireless charging, going so far as to do VNA measurements to get a better simulation model of the coil coupling. I learned some things, and should probably write an article about that at some point before I forget completely. I also built a second revision of the track board, with two lanes, because of course a slot track with one car is boring! I only did this because I failed to engage my brain properly. Had I engaged my brain properly I would have realized that the slot cars full of magnets would exert substantial force on each other when brought into close proximity in the narrow confines of a race track &lt;em&gt;before ordering the board&lt;/em&gt;. Ah well; I realized eventually. I got the remote working enough to decide that actually it wasn’t that fun to drive this with a slot remote, especially if there was only one car.&lt;/p&gt;
&lt;div class="quarto-figure quarto-figure-center"&gt;
&lt;figure class="figure"&gt;
&lt;p&gt;&lt;a href="wpt_vna.jpg" class="lightbox" data-gallery="quarto-lightbox-gallery-2" title="Measuring the coupling between two wireless power transfer coils"&gt;&lt;img src="https://jeffmcbride.net/posts/gauss-speedway/wpt_vna.jpg" class="img-fluid figure-img" width="600" alt="Measuring the coupling between two wireless power transfer coils"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;figcaption&gt;Measuring the coupling between two wireless power transfer coils&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/div&gt;
&lt;p&gt;So I dropped all that. A nice feature of projects done for fun is that their value is derived primarily from your enjoyment, so anything is reasonably in scope as long as you’re enjoying doing it. So arguably no waste was incurred here.&lt;/p&gt;
&lt;p&gt;A lot of this kind of project goes as far as it takes to satisfy myself that I know how I would do it, and then there’s just not so much motiviation to go any further. It starts to feel like busy work. But, in this case I wanted a packaged up version that &lt;em&gt;just worked&lt;/em&gt;, so I did one more board spin – what you see here – and now I’m pretty happy with it&lt;sup&gt;2&lt;/sup&gt;.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="the-design" class="level2"&gt;
&lt;h2 class="anchored" data-anchor-id="the-design"&gt;The Design&lt;/h2&gt;
&lt;section id="physics" class="level3"&gt;
&lt;h3 class="anchored" data-anchor-id="physics"&gt;Physics&lt;/h3&gt;
&lt;p&gt;The physics isn’t too complicated. To think about how this works you basically need to know one thing: The force created on a wire with a current is equal to the cross product B x I, which means that the force pushes &lt;strong&gt;orthogonally&lt;/strong&gt; to the direction of the current and the direction of the B field. So if the magnetic field is pointed down into the board, and it’s overtop of a horizontal trace aligned with the X axis of the PCB, the magnet will be pushed vertically along the Y axis (the direction depends on the direction of current and B field, but we don’t have to concern ourselves with that too much, we can make it go either direction just by flipping the magnet).&lt;/p&gt;
&lt;div style="text-align: center; padding: 20px"&gt;
&lt;video src="/img/animation.mkv" width="500px" controls=""&gt;
&lt;/video&gt;&lt;/div&gt;
&lt;p&gt;The arrangement of the wires on the two phases allows it two be driven as a bipolar stepper motor, alternating the current in each 90 degrees out of phase. The controller on the Gauss Speedway does a bit of microstepping, performing 16 current steps per each cycle.&lt;/p&gt;
&lt;p&gt;Then you have the problem of keeping the cars on the track, especially around curves where there are sideways g’s being pulled. That’s what the guard rails are for. These traces run along the outside, and push back in, so if the car deviates from the center, the force from the guard rail it moves towards increases, pushing it back.&lt;/p&gt;
&lt;div class="quarto-figure quarto-figure-center"&gt;
&lt;figure class="figure"&gt;
&lt;p&gt;&lt;a href="guard_rails.png" class="lightbox" data-gallery="quarto-lightbox-gallery-3" title="Guard rail traces keep things in the center"&gt;&lt;img src="https://jeffmcbride.net/posts/gauss-speedway/guard_rails.png" class="img-fluid figure-img" width="400" alt="Guard rail traces keep things in the center"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;figcaption&gt;Guard rail traces keep things in the center&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id="current-control" class="level3"&gt;
&lt;h3 class="anchored" data-anchor-id="current-control"&gt;Current control&lt;/h3&gt;
&lt;p&gt;The electronics are quite simple. We need to measure the capacitive touch sensors, but this is handled by the Touch Sense Controller peripheral on the microcontroller. Easy! The copper touch pads just wire to the microcontroller pins via a resistor.&lt;/p&gt;
&lt;p&gt;Then we need to be able to control three currents: two for the drive phases, and one for the guard rails, and these currents need to be reversable. In order to reliably control the current regardless of the input voltage or trace resistance (e.g.&amp;nbsp;the guard rail resistance is &amp;lt; 1ohm, while the traces are about 5ohms), each current driver gets a relatively large inductor and a capacitor across the trace. A net-tie is used in Kicad to represent the traces of the track.&lt;/p&gt;
&lt;div class="quarto-figure quarto-figure-center"&gt;
&lt;figure class="figure"&gt;
&lt;p&gt;&lt;a href="drive_schematic.png" class="lightbox" data-gallery="quarto-lightbox-gallery-4" title="Schematic of a single current driver"&gt;&lt;img src="https://jeffmcbride.net/posts/gauss-speedway/drive_schematic.png" class="img-fluid figure-img" width="600" alt="Schematic of a single current driver"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;figcaption&gt;Schematic of a single current driver&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/div&gt;
&lt;p&gt;As long as the voltage and trace current worked out so that the peak “on” current didn’t damage anything, I suspect you could get away with driving this without the inductance. But that’s not very flexible, and I just don’t like that current ripple! The inductor part number is SRR1260A-680M.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="pcb" class="level3"&gt;
&lt;h3 class="anchored" data-anchor-id="pcb"&gt;PCB&lt;/h3&gt;
&lt;p&gt;While I got away with two layers on the prototypes, I had to go to four layers for this. There just wasn’t any way to get signals from outside the track to inside on a two layer board, so the choice was either to keep all of the control electronics outside or add layers. Putting everything outside would have made it larger, and it already barely fits on my 3D printer bed so I didn’t want that. A side benefit of going four layers is that the second layer ends up being much closer to the top, which is good for those newtons/A. The only downside is it costs more.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="enclosure" class="level3"&gt;
&lt;h3 class="anchored" data-anchor-id="enclosure"&gt;Enclosure&lt;/h3&gt;
&lt;p&gt;The board is just mounted into a 3D printed frame, using M3 screws and heat-sink thread inserts. The frame just barely fits on the 220x220mm bed of my Ender 3.&lt;/p&gt;
&lt;div class="quarto-figure quarto-figure-center"&gt;
&lt;figure class="figure"&gt;
&lt;p&gt;&lt;a href="frame.png" class="lightbox" data-gallery="quarto-lightbox-gallery-5" title="3D printed frame render"&gt;&lt;img src="https://jeffmcbride.net/posts/gauss-speedway/frame.png" class="img-fluid figure-img" width="500" alt="3D printed frame render"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;figcaption&gt;3D printed frame render&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id="the-cars" class="level3"&gt;
&lt;h3 class="anchored" data-anchor-id="the-cars"&gt;The Cars&lt;/h3&gt;
&lt;p&gt;The cars I printed on an FDM printer, with a 0.2mm nozzle. I got these &lt;a href="https://www.apexmagnets.com/magnets/2mm-x-1mm-disc-neodymium-magnet"&gt;2mmx1mm magnets&lt;/a&gt; from Apex magnets, and glued them into the car with the medium CA. It’s important that they all go in with fields oriented the same way. You can arrange for this by installing one reference magnet onto something (or just use an existing car) and putting a stack of magnets onto that, then pull them off one by one. I like to mark the “up” side with a little sharpie dot, so I can keep track of the orientation visually while installing.&lt;/p&gt;
&lt;div class="quarto-figure quarto-figure-center"&gt;
&lt;figure class="figure"&gt;
&lt;p&gt;&lt;a href="cars.jpg" class="lightbox" data-gallery="quarto-lightbox-gallery-6" title="View of two cars, top and bottom"&gt;&lt;img src="https://jeffmcbride.net/posts/gauss-speedway/cars.jpg" class="img-fluid figure-img" width="500" alt="View of two cars, top and bottom"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;figcaption&gt;View of two cars, top and bottom&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="levitation" class="level2"&gt;
&lt;h2 class="anchored" data-anchor-id="levitation"&gt;Levitation&lt;/h2&gt;
&lt;p&gt;For a first order analysis, one might think of the magnetic field as simply pointing down into the board. But this is only true at the very center of the magnet. Once you move out, the B field bends upwards, which creates the tantalizing possibility of a force in the z direction. An upward force of course leads to levitation. The current phases in fact create an oscillating z force &lt;sup&gt;3&lt;/sup&gt; up and down as the car moves along. These are relatively small relative to the forward forces, and don’t cause any big problems.&lt;/p&gt;
&lt;p&gt;But the guard rails can be positioned fairly far outside the magnet, where the field bends substantially. Can the guard rails be used for levitation?&lt;/p&gt;
&lt;p&gt;First of all, if you want to get the force upwards instead of downwards (no good; just increases your friction), you have to swap the current so that the tangential force from the rails is outwards. This is unstable, but lots of unstable systems can be made stable with an appropriate feedback control! I could add a middle rail, and a metal bar across the car. Then the capacitance between the inner rail and the two outer rails could provide a very fast measurement of the lateral displacement of the car. “Cool!”, I think. But then, once again, I engaged my brain, and realized that while this works great if the only thing we were worried about was controlling the lateral translation, but of course a car is free to rotate too! It can rotate in about the Z direction, AND once it’s actually levitated it can rotate around the other two directions as well. The pitch rotation (the front/back going up and down) is fine, it will be stable. But the roll is tightly coupled to the left/right translation control, and I just don’t see anyway to decouple them.&lt;/p&gt;
&lt;div class="quarto-figure quarto-figure-center"&gt;
&lt;figure class="figure"&gt;
&lt;p&gt;&lt;a href="magnet_levitation_diagram.png" class="lightbox" data-gallery="quarto-lightbox-gallery-7" title="Idea for stabilizing two axes. With alternating guard rails, you could both sense the lateral displacement of the front and back independently, and control them separately. This could control translation and rotatation about the z axis (yaw), but roll is still unconstrained."&gt;&lt;img src="https://jeffmcbride.net/posts/gauss-speedway/magnet_levitation_diagram.png" class="img-fluid figure-img" width="400" alt="Idea for stabilizing two axes. With alternating guard rails, you could both sense the lateral displacement of the front and back independently, and control them separately. This could control translation and rotatation about the z axis (yaw), but roll is still unconstrained."&gt;&lt;/a&gt;&lt;/p&gt;
&lt;figcaption&gt;Idea for stabilizing two axes. With alternating guard rails, you could both sense the lateral displacement of the front and back independently, and control them separately. This could control translation and rotatation about the z axis (yaw), but roll is still unconstrained.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/div&gt;
&lt;p&gt;So I’ve concluded there’s just no way to get levitation out of this system, but if anyone has some ideas and wants to prove me wrong, please do!&lt;/p&gt;
&lt;/section&gt;
&lt;section id="conclusion" class="level2"&gt;
&lt;h2 class="anchored" data-anchor-id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Yeah that’s it. It’s kind of a fun demo, and now that it’s easy to build/run I can make a few more and pass them out to unlucky friends and family!&lt;/p&gt;
&lt;p&gt;Since I needed to fix a few things, I ordered a rev 2 which fixes some oversights on the first design, and also flips the guard rails and phases so the guard rails are on top. Now that I’m building it as a 4-layer board, the second layer isn’t very far away, and I hope this will still work with less noise (the phase traces create bumps!). I found it runs really nicely with a thin film of PET, and I’m hoping for a similar result with the guard rails on top.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Hardware design: https://github.com/mcbridejc/GaussSpeedway&lt;/li&gt;
&lt;li&gt;Control software: https://github.com/mcbridejc/speedway-controller&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;section id="footnotes" class="level2"&gt;
&lt;h2 class="anchored" data-anchor-id="footnotes"&gt;Footnotes&lt;/h2&gt;


&lt;/section&gt;


&lt;div id="quarto-appendix" class="default"&gt;&lt;section id="footnotes" class="footnotes footnotes-end-of-document"&gt;&lt;h2 class="anchored quarto-appendix-heading"&gt;Footnotes&lt;/h2&gt;

&lt;ol&gt;
&lt;li id="fn1"&gt;&lt;p&gt;I have one small correction to Kevin’s analysis. He notes that “the transition energies become positive” at larger magnet to coil distances, and that he was surprised to see this. This error occurs because his formula is only considering the four nearest wires. If you consider more wires this phenomenon disappears.↩︎&lt;/p&gt;&lt;/li&gt;
&lt;li id="fn2"&gt;&lt;p&gt;I have some python simulations of this, and may dig them out at some point and share…↩︎&lt;/p&gt;&lt;/li&gt;
&lt;li id="fn3"&gt;&lt;p&gt;Mostly… actually I need one more spin for a couple things and I’m thinking I should do the next one in black soldermask?↩︎&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;&lt;/div&gt;</summary><category term="electronics" /><category term="projects" /></entry><entry><id>https://jeffmcbride.net/posts/acoustic-beamforming/</id><title>Acoustic Beamforming Notes</title><link href="https://jeffmcbride.net/posts/acoustic-beamforming/" rel="alternate" /><published>2022-10-10T07:00:00Z</published><updated>2022-10-10T07:00:00Z</updated><summary type="html">&lt;section id="building-an-acoustic-phased-array" class="level2"&gt;
&lt;h2 class="anchored" data-anchor-id="building-an-acoustic-phased-array"&gt;Building an Acoustic Phased Array&lt;/h2&gt;
&lt;div class="quarto-figure quarto-figure-center"&gt;
&lt;figure class="figure"&gt;
&lt;p&gt;&lt;a href="parts_annotated.jpg" class="lightbox" data-gallery="quarto-lightbox-gallery-1" title="Accoustic Array Parts"&gt;&lt;img src="https://jeffmcbride.net/posts/acoustic-beamforming/parts_annotated.jpg" class="img-fluid figure-img" width="600" alt="Accoustic Array Parts"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;figcaption&gt;Accoustic Array Parts&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id="introduction" class="level2"&gt;
&lt;h2 class="anchored" data-anchor-id="introduction"&gt;Introduction&lt;/h2&gt;
&lt;p&gt;Around August of 2022 I started working on an acoustic phased array. I bought some microphones, wired them up to an STM32H7 dev board, and wrote some Rust code to process the audio. This demo was designed to detect the source of a noise within the horizontal plane – i.e.&amp;nbsp;it’s azimuth angle. Along the way I got some familiarity with acoustic beamforming, and it was a good exercise in Rust development, with the same codebase being used on the microcontroller, a GUI application, and a browser application. This post describes the development of the 6 microphone array, and a simulator for larger arrays. Perhaps in the future, I’ll have more to share about a much larger array combined with a camera; we’ll see!&lt;/p&gt;
&lt;/section&gt;
&lt;section id="the-video" class="level2"&gt;
&lt;h2 class="anchored" data-anchor-id="the-video"&gt;The Video&lt;/h2&gt;
&lt;p&gt;To start with, you can check out this quick demo video to get an idea of what it does:&lt;/p&gt;
&lt;div style="text-align: center"&gt;
&lt;iframe width="560" height="315" src="https://www.youtube.com/embed/xdeHO8E6DT0" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""&gt;
&lt;/iframe&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id="what-is-an-acoustic-phased-array" class="level2"&gt;
&lt;h2 class="anchored" data-anchor-id="what-is-an-acoustic-phased-array"&gt;What is an acoustic phased array?&lt;/h2&gt;
&lt;p&gt;If you have multiple omnidirectional detectors at different locations, you can combine them into a directional detector, more sensitive to a particular source direction. If you can adjust the relative delay of the signals, you can select the focal point of the array, creating an directional receiver which can be steered electronically. It applies to electromagnetic as well as acoustic signals. With a single delay configuration at any given time, this is useful for focusing your receiver on a source, attenuating background noise coming from other directions. But also, if you can capture a segment of the raw signals, and then adjust the delays digitally you can compute a a power level for one moment, at many different focal points, effectively creating an image of power arriving from the different directions.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="components-and-code" class="level2"&gt;
&lt;h2 class="anchored" data-anchor-id="components-and-code"&gt;Components and code&lt;/h2&gt;
&lt;p&gt;I started with an STM32H7 Nucleo development board, and set out to see how many audio channels I could capture with it and stream to the ethernet port. I built an array with six microphones, which conveintely attaches to the Nucleo board.&lt;/p&gt;
&lt;p&gt;Then I wrote some Rust code to convert the PDM microphone input to PCM, do an FFT to get a frequency spectra, and then do the delay-and-sum calculations in the frequency domain to focus the array to an array of focal points and compute total power for each. With just the six microphones, I found that it works well for measuring the azimuth to a source, but only marginally as a “camera” imaging a full 2D focal plane. This wasn’t a huge surprise; I assumed there was a reason that commercial acoustic cameras have many more than six microphones. To aid in development, I wrote a GUI using Rust GTK bindings to process the stream of UDP packets from the STM32H7, and display live results.&lt;/p&gt;
&lt;p&gt;Once I had a reliable azimuth measurement, I added a brushless motor to rotate an indicator, so that it could physically point at the source of a detected noise.&lt;/p&gt;
&lt;p&gt;Finally, I got the beamforming running on the microcontroller, albeit with some compromises since the CPU wasn’t able to fully process the data in real-time.&lt;/p&gt;
&lt;p&gt;Finally, I created a javascript application to simulate a noise source and a hypothetical acoustic camera, in order to experiment with different micro layouts. This was built in Javascript, because 1) I wanted others to be able to run it without needing a Rust compiler or native binaries and 2) the UI is more complex and I find it’s easier to build with React, and 3) it’s a great demo of compiling Rust code to WASM to run in a browser!&lt;/p&gt;
&lt;p&gt;At some point, I may refactor this a bit, but as of writing most of the code can be found in a &lt;a href="https://github.com/mcbridejc/acoustic-array-tools"&gt;single repository&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The main exception is a stand-alone app which does nothing but read audio and output to UDP packets: &lt;a href="https://github.com/mcbridejc/pdmstream"&gt;pdmstream&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The simulator runs in the browser, and if you want to try it out without having to compile anything, it can be found &lt;a href="../../acoustic-sim/"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="capturing-audio-from-pdm-microphones-on-stm32h753" class="level2"&gt;
&lt;h2 class="anchored" data-anchor-id="capturing-audio-from-pdm-microphones-on-stm32h753"&gt;Capturing audio from PDM microphones on STM32H753&lt;/h2&gt;
&lt;p&gt;I started with this processor because a) I had a Nucleo-144 dev board for it laying around and b) at 400MHz it’s a high-end cortex, and I wanted to get as much DSP processing into this as possible. So the first question, how many synchronized PDM inputs can we get on this thing? (TL;DR: It’s 8.)&lt;/p&gt;
&lt;section id="what-are-pdm-inputs" class="level3"&gt;
&lt;h3 class="anchored" data-anchor-id="what-are-pdm-inputs"&gt;What are PDM inputs&lt;/h3&gt;
&lt;p&gt;I’m using MEMS PDM microphones from &lt;a href="https://www.adafruit.com/product/3492"&gt;Adafruit&lt;/a&gt;. PDM stands for pulse density modulation. These use delta-sigma modulators to output 1-bit samples at a high rate (3.072MHz in my case) which can then be filtered to normal higher bit depth PCM samples. Many PDM microphones support combining two mics into one wire, where one channel is read on the falling clock edge, and the other is read on the rising edge.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="the-sai-peripherals" class="level3"&gt;
&lt;h3 class="anchored" data-anchor-id="the-sai-peripherals"&gt;The SAI Peripherals&lt;/h3&gt;
&lt;p&gt;The STM32H753 has four SAI – serial audio interface – peripherals, which support a variety of audio protocols. Two of them have a PDM mode, to read two microphones on each data channel, which looks like the best option for reading these microphones. There are two SAI peripherals, and at first reading each supports 4 data inputs. With two mics per input, that’s 16 channels of capture! However, as I dug into the reference manual, this number dropped to 8. The first thing is, the fourth input is left unconnected for both peripherals, so that drops us to 12 potential channels.&lt;/p&gt;
&lt;div class="quarto-figure quarto-figure-center"&gt;
&lt;figure class="figure"&gt;
&lt;p&gt;&lt;a href="SAI_ImplementationTable.png" class="lightbox" data-gallery="quarto-lightbox-gallery-2" title="Table 417 from Reference Manual"&gt;&lt;img src="https://jeffmcbride.net/posts/acoustic-beamforming/SAI_ImplementationTable.png" class="img-fluid figure-img" width="400" alt="Table 417 from Reference Manual"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;figcaption&gt;Table 417 from Reference Manual&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/div&gt;
&lt;p&gt;Then, when I got down to pin assignments, I found that the SAI had pretty limited options. D2 on SAI1 only has one remap: PE4. Which would be OK, except – &lt;strong&gt;incredibly!&lt;/strong&gt; – SAI4 D2 also has only one remap option, and it is &lt;strong&gt;also PE4&lt;/strong&gt;!. So you can’t use both of them, at least not to capture different microphones! That drops the total possible input channels to 10. Finally, the D3 input on SAI1/4 has two remaps, but they are the same for both peripherals, which means if you want to use both, you have to use both of the two pins. This would be OK, but one of them is used for ethernet on the Nucleo board. That means I’m down to 8 channels.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="getting-data-out" class="level3"&gt;
&lt;h3 class="anchored" data-anchor-id="getting-data-out"&gt;Getting data out&lt;/h3&gt;
&lt;p&gt;The first pass at the embedded code was just a simple capture and ship approach, so that I could experiment with the signal processing on my workstation instead of on the microcontroller. With six channels (3.072MBit/s each), we need a total of about 18.4MBit/s throughput. Sadly, the USB on this chip only supports 12Mbit with the internal PHY, and the nucleo doesnt’ provide a faster USB PHY, so the only good option is the ethernet port: at 100MBit, it can push out all six channels comfortably, and the Rust &lt;code&gt;stm32h7xx_hal&lt;/code&gt; crate supports smoltcp on this chip so it’s pretty straightforward to get going.&lt;/p&gt;
&lt;p&gt;To read the data, one of the DMA1 streams is setup to read the SAI1 peripheral to memory, a pool of buffers is allocated, and the DMA IRQ can then push the completed buffer into a queue for transmit, then grab a free buffer from the pool and set it up for the next DMA transfer. The DMA is used in dual-buffer mode so when the transfer completes it automatically moves on to the other pre-configured destination address, and you have an entire DMA transfer time for the IRQ to swap out the inactive buffer.&lt;/p&gt;
&lt;p&gt;All of this happens in the &lt;a href="https://github.com/mcbridejc/pdmstream"&gt;pdmstream&lt;/a&gt; application.&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="audio-processing" class="level2"&gt;
&lt;h2 class="anchored" data-anchor-id="audio-processing"&gt;Audio processing&lt;/h2&gt;
&lt;div class="quarto-figure quarto-figure-center"&gt;
&lt;figure class="figure"&gt;
&lt;p&gt;&lt;a href="basic_processing_flow.png" class="lightbox" data-gallery="quarto-lightbox-gallery-3" title="Diagram of the high level steps to compute a power vector from incoming PDM data"&gt;&lt;img src="https://jeffmcbride.net/posts/acoustic-beamforming/basic_processing_flow.png" class="img-fluid figure-img" width="600" alt="Diagram of the high level steps to compute a power vector from incoming PDM data"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;figcaption&gt;Diagram of the high level steps to compute a power vector from incoming PDM data&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/div&gt;
&lt;section id="converting-pdm-to-pcm" class="level3"&gt;
&lt;h3 class="anchored" data-anchor-id="converting-pdm-to-pcm"&gt;Converting PDM to PCM&lt;/h3&gt;
&lt;p&gt;The biggest downside to the PDM microphones (e.g., over an I2S version) is that they have to be filtered to get to higher bit-depth, lower frequency PCM samples, and this filtering is fairly inefficient to do on a CPU. All that’s really required is a low-pass filter, but if you want good noise performance and efficient processing there are some tricks, and it helps to do it in stages. Consider that the input frequency is 3MHz, and you probably don’t want to run a filter with a large number of taps at that sample frequency. I’m not going to get into the filtering much here, but will just point out that Tom Verbeure has &lt;a href="https://tomverbeure.github.io/2020/12/20/Design-of-a-Multi-Stage-PDM-to-PCM-Decimation-Pipeline.html"&gt;a great series of blog posts&lt;/a&gt; about it. I will simply share the setup I ended up going with:&lt;/p&gt;
&lt;div class="quarto-figure quarto-figure-center"&gt;
&lt;figure class="figure"&gt;
&lt;p&gt;&lt;a href="filtering_block_diagram.png" class="lightbox" data-gallery="quarto-lightbox-gallery-4" title="Fitlering starts with two stages of CIC decimation, then an FIR filter is run at 96KHz, and that output is further decimated by simple moving average to 24kHz"&gt;&lt;img src="https://jeffmcbride.net/posts/acoustic-beamforming/filtering_block_diagram.png" class="img-fluid figure-img" width="600" alt="Fitlering starts with two stages of CIC decimation, then an FIR filter is run at 96KHz, and that output is further decimated by simple moving average to 24kHz"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;figcaption&gt;Fitlering starts with two stages of CIC decimation, then an FIR filter is run at 96KHz, and that output is further decimated by simple moving average to 24kHz&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/div&gt;
&lt;p&gt;The CIC filters operate on 32-bit integers, because the integrator stage depends on integer overflow to work properly. After the second CIC filter, the values are converted to 32-bit floats, because my benchmarking found that doing the math in floats was ever so slightly faster than ints, and I don’t have to worry about managing fixed point math.&lt;/p&gt;
&lt;p&gt;For determing FIR filter coefficients, I can highly recommend &lt;a href="https://github.com/chipmuenk/pyfda"&gt;pyfda&lt;/a&gt;.&lt;/p&gt;
&lt;section id="conversion-performance" class="level4"&gt;
&lt;h4 class="anchored" data-anchor-id="conversion-performance"&gt;Conversion performance&lt;/h4&gt;
&lt;p&gt;As it turns out, doing the PDM conversion on the STM32H7 is work for the CPU. Just doing the PDM filtering on all six channels ends up using about 80% of the CPU time, leaving little for beamforming. It became pretty clear after doing some benchmarking that I was not going to be able to process all of the data in real time. So some kind of scheme for selectively processing chunks and handling overflow gracefully was clearly called for, and I’ll talk about that later.&lt;/p&gt;
&lt;p&gt;One thing I want to note: &lt;a href="https://arm-software.github.io/CMSIS_5/DSP/html/index.html"&gt;CMSIS-DSP&lt;/a&gt; proved to be much faster than my implementation. I wrote my own FIR filter in Rust, and benchmarked it against the &lt;code&gt;arm_fir_f32&lt;/code&gt; function provided by the CMSIS library, and it ran 4x faster.&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="beamforming" class="level3"&gt;
&lt;h3 class="anchored" data-anchor-id="beamforming"&gt;Beamforming&lt;/h3&gt;
&lt;p&gt;The beamforming is performed as a delay-and-sum in the frequency domain. It operates on a window of 1,024 samples (although I think I may be shrinking this to 512 or 256 in the future), which yields a spectrum with 1,024 frequency bins. Only a subset of the frequency bins are used, defined by a start and end frequency. For azimuth detection, I’ve found 500-2000Hz to be an effective range. Each spectrum bin is represented by a complex number which has a magnitude (i.e.&amp;nbsp;total power) and a phase. To perform a delay, it can be multiplied by another complex number with unit magnitude, and an angle determining the phase adjustment. The delay to be applied can be calculated from the distance from each focal point to each microphone.&lt;/p&gt;
&lt;p&gt;All of these phase adjustment complex numbers can be pre-calculated, yielding a NMICS x NFOCALPOINTS x NFFT matrix of complex numbers. Then computing the power for beamforming just requires one complex multiply for each frequency bin in each channel, then a summing of the phase adjusted values across channels. Sadly, storing this steering vector matrix requires a lot of memory, and all of the memory on the STM32H7 is already being used to hold audio samples, so when running on the STM32 I only pre-compute the &lt;code&gt;NMICS x NFOCALPOINTS&lt;/code&gt; distances, not the complex steering vector. This leads to a lot of trig functions, and makes things slow. A phase (scalar) to phasor (complex) look-up function could probably be accurate enough in a reasonable size, and shave off a lot of sin/cos calls, but I haven’t looked into it at this point.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="practical-implementation-issues" class="level3"&gt;
&lt;h3 class="anchored" data-anchor-id="practical-implementation-issues"&gt;Practical implementation issues&lt;/h3&gt;
&lt;p&gt;The core PDM-to-PCM, and beamforming code lives in the &lt;code&gt;dsp&lt;/code&gt; crate in the repository, so that it can be shared by the embedded code (&lt;code&gt;stm32&lt;/code&gt;), the GTK gui (&lt;code&gt;gui&lt;/code&gt;) and the React simulator (&lt;code&gt;websim&lt;/code&gt;) crates. Creating code that could be used in the memory-constrained, heapless, no-std environment of the &lt;code&gt;stm32&lt;/code&gt; project, while also be used in the others created a lot of friction for me with Rust’s strict ownership management and borrow checking. I could probably write a whole blog post about the issues that popped up, but that will require a lot more reflection than I can muster tonight and take up too much space here. But one common pattern was that I needed static versions which could have their allocation size known at runtime, and heap based versions which could have things like number of microphones adjusted at run time, so you’ll see a lot of things like in the code with an interface trait, like,&lt;/p&gt;
&lt;div class="code-copy-outer-scaffold"&gt;&lt;div class="sourceCode" id="cb1" style="background: #f1f3f5;"&gt;&lt;pre class="sourceCode rust code-with-copy"&gt;&lt;code class="sourceCode rust"&gt;&lt;span id="cb1-1"&gt;&lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;pub&lt;/span&gt; &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;trait&lt;/span&gt; BeamFormer &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb1-2"&gt;    &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;fn&lt;/span&gt; compute_power(&lt;/span&gt;
&lt;span id="cb1-3"&gt;            &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;amp;&lt;/span&gt;&lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;self&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb1-4"&gt;            spectra&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;:&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;amp;&lt;/span&gt; &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;dyn&lt;/span&gt; Spectra&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb1-5"&gt;            power_out&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;:&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;amp;&lt;/span&gt;&lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;mut&lt;/span&gt; [&lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;f32&lt;/span&gt;]&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb1-6"&gt;            start_freq&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;:&lt;/span&gt; &lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;f32&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb1-7"&gt;            end_freq&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;:&lt;/span&gt; &lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;f32&lt;/span&gt;)&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb1-8"&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;And two separate implementations, like:&lt;/p&gt;
&lt;div class="code-copy-outer-scaffold"&gt;&lt;div class="sourceCode" id="cb2" style="background: #f1f3f5;"&gt;&lt;pre class="sourceCode rust code-with-copy"&gt;&lt;code class="sourceCode rust"&gt;&lt;span id="cb2-1"&gt;&lt;span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;/// BeamFormer implementation using heap for storing steering vectors. Parameters, such as the&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb2-2"&gt;&lt;span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;/// number of mics, number of focal points, or size of NFFT can be configured at runtime&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb2-3"&gt;&lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;pub&lt;/span&gt; &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;struct&lt;/span&gt; HeapBeamFormer &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb2-4"&gt;    steering_vectors&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;:&lt;/span&gt; Array3&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;lt;&lt;/span&gt;Complex&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;lt;&lt;/span&gt;&lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;f32&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;gt;&amp;gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb2-5"&gt;    sample_freq&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;:&lt;/span&gt; &lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;f32&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb2-6"&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-copy-outer-scaffold"&gt;&lt;div class="sourceCode" id="cb3" style="background: #f1f3f5;"&gt;&lt;pre class="sourceCode rust code-with-copy"&gt;&lt;code class="sourceCode rust"&gt;&lt;span id="cb3-1"&gt;&lt;span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;/// BeamFormer implementation with fixed parameters known at compile time, and no heap usage&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-2"&gt;&lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;pub&lt;/span&gt; &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;struct&lt;/span&gt; StaticBeamFormer&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;const&lt;/span&gt; NCHAN&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;:&lt;/span&gt; &lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;usize&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt; &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;const&lt;/span&gt; NFOCAL&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;:&lt;/span&gt; &lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;usize&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt; &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;const&lt;/span&gt; NFFT&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;:&lt;/span&gt; &lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;usize&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;gt;&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-3"&gt;    steering_vectors&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;:&lt;/span&gt; [[[Complex&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;lt;&lt;/span&gt;&lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;f32&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;gt;;&lt;/span&gt; NFFT]&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt; NFOCAL]&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt; NCHAN]&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-4"&gt;    sample_freq&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;:&lt;/span&gt; &lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;f32&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb3-5"&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Or even a third option:&lt;/p&gt;
&lt;div class="code-copy-outer-scaffold"&gt;&lt;div class="sourceCode" id="cb4" style="background: #f1f3f5;"&gt;&lt;pre class="sourceCode rust code-with-copy"&gt;&lt;code class="sourceCode rust"&gt;&lt;span id="cb4-1"&gt;&lt;span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;/// A Beamformer implementation that does less pre-computation to avoid storage of large &lt;/span&gt;&lt;/span&gt;
&lt;span id="cb4-2"&gt;&lt;span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;/// steering vectors, but this means it has to do more computation, sadly. &lt;/span&gt;&lt;/span&gt;
&lt;span id="cb4-3"&gt;&lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;pub&lt;/span&gt; &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;struct&lt;/span&gt; SmallMemBeamFormer&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;const&lt;/span&gt; NCHAN&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;:&lt;/span&gt; &lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;usize&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt; &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;const&lt;/span&gt; NFOCAL&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;:&lt;/span&gt; &lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;usize&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb4-4"&gt;&lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;where&lt;/span&gt; [()&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt; NCHAN &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;-&lt;/span&gt; &lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;1&lt;/span&gt;]&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb4-5"&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb4-6"&gt;    dist&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;:&lt;/span&gt; [[&lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;f32&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt; NFOCAL]&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt; NCHAN &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;-&lt;/span&gt; &lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;1&lt;/span&gt;]&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb4-7"&gt;    sample_freq&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;:&lt;/span&gt; &lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;f32&lt;/span&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb4-8"&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="first-gui-implementation" class="level2"&gt;
&lt;h2 class="anchored" data-anchor-id="first-gui-implementation"&gt;First GUI implementation&lt;/h2&gt;
&lt;p&gt;The first processing happened on my computer, with a GUI built using the rust GTK bindings, and &lt;a href="https://github.com/plotters-rs/plotters"&gt;plotters-rs&lt;/a&gt;. This lives in the &lt;code&gt;gui&lt;/code&gt; direcotry of the project repo. It receives raw PDM data sent in broadcast UDP packets, collects them into appropriately sized frames, and passes them on to the DSP processing. It computes power for two beamforming focal point arrays: The circular array for azimuth calculation, and a rectangular grid for generating an image, so that both can be displayed live.&lt;/p&gt;
&lt;p&gt;&lt;a href="gui_screenshot.png" class="lightbox" data-gallery="quarto-lightbox-gallery-5"&gt;&lt;img src="https://jeffmcbride.net/posts/acoustic-beamforming/gui_screenshot.png" class="img-fluid" width="500"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/section&gt;
&lt;section id="azimuth-finding" class="level2"&gt;
&lt;h2 class="anchored" data-anchor-id="azimuth-finding"&gt;Azimuth Finding&lt;/h2&gt;
&lt;p&gt;One of the things I wanted to do as a first demo was to use the 6-mic array to determine azimuth angle of the source (i.e.&amp;nbsp;what direction in the horizontal plane), and swivel something to point at them.&lt;/p&gt;
&lt;div class="quarto-figure quarto-figure-center"&gt;
&lt;figure class="figure"&gt;
&lt;p&gt;&lt;a href="styrofoam_head.jpg" class="lightbox" data-gallery="quarto-lightbox-gallery-6" title="I tried mounging this styrofoam head above the array, but alas, it was too big and too acoustically absorbant"&gt;&lt;img src="https://jeffmcbride.net/posts/acoustic-beamforming/styrofoam_head.jpg" class="img-fluid figure-img" width="400" alt="I tried mounging this styrofoam head above the array, but alas, it was too big and too acoustically absorbant"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;figcaption&gt;I tried mounging this styrofoam head above the array, but alas, it was too big and too acoustically absorbant&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/div&gt;
&lt;p&gt;To find azimuth, I created an array of focal points arranged in a circle around the microphone array, with a radius of 2m, and 0.1 meters above the plane –I started with 100 points, but decreased it to 60 when I wanted to speed up processing on the STM32. So basically you focus the array at a series of points a little above the array, at a distance larger than the array size, and even if the height/elevation is off by a bit, I still expect the focal point nearest in azimuth to the source to have the highest power. The first naive approach is to take the highest power radial in each frame and call it a target. But there’s noise so we only want to respond to signals with certain power; so one can measure the RMS amplitude of each frame and set a threshold. Anything below the RMS threshold gets ignored. This is better, but what I found was that there are often echos after a sharp signal which show up at very different azimuth angles – I believe this is reverberation off of walls and other objects. This can lead to a quick solution to the correct azimuth, followed 50ms later by a bad solution.&lt;/p&gt;
&lt;p&gt;So I added some more filtering. First of all, I reduced the azimuth power vector (the array of power at all N radials) to a complex value representing the “moment” of the power. Basically, convert each radial to a complex number with magnitude equal to the power measured there, and angle equal to the azimuth angle of the focal point. Sum these and you get a single complex moment.&lt;/p&gt;
&lt;p&gt;Then for temporal filtering, the filter begins an estimate whenever the RMS power of a frame exceeds the threshold. It then accumulates the power moment for all frames until either: 25 frames are accumualted (about 1 second of audio), or the RMS falls below the threshold. The angle of the accumulated moment yields the new azimuth value. After an estimate, the RMS threshold is raised to the average RMS value of all frames used in the estimate, and decays over time. This prevents frames which are louder than the initial threshold, but quieter than the frames used for the recent estimate to be ignored, preventing the echo issues, and generally leading to a more stable azimuth estimate.&lt;/p&gt;
&lt;p&gt;If that explanation leaves you wanting, the code is &lt;a href="https://github.com/mcbridejc/acoustic-array-tools/blob/main/dsp/src/dsp/azimuth.rs"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="physical-pointing-device" class="level2"&gt;
&lt;h2 class="anchored" data-anchor-id="physical-pointing-device"&gt;Physical pointing device&lt;/h2&gt;
&lt;p&gt;I wanted a visible indicator of angle detection, attached to the microphones, so that if you said something, it could &lt;strong&gt;point at you&lt;/strong&gt;. An arrow on a screen or, even worse, a number printed on a console – while technically adequate to show it was working – just wouldn’t have the same effect. So I needed a servo. A cheap PWM servo would have been grand, except they don’t do continuous rotation, or cover the full 360 degree range. So I went with a BLDC servo. This isn’t so hard to do: you just need angle measurement, and a driver with 3 half-bridges you can PWM. While building this, I found this excellent chip:&lt;/p&gt;
&lt;div class="quarto-figure quarto-figure-center"&gt;
&lt;figure class="figure"&gt;
&lt;p&gt;&lt;a href="stspin233_diagram.png" class="lightbox" data-gallery="quarto-lightbox-gallery-7" title="The STSPIN233 3-phase driver for 1.8-10V, up to 1.3A."&gt;&lt;img src="https://jeffmcbride.net/posts/acoustic-beamforming/stspin233_diagram.png" class="img-fluid figure-img" width="300" alt="The STSPIN233 3-phase driver for 1.8-10V, up to 1.3A."&gt;&lt;/a&gt;&lt;/p&gt;
&lt;figcaption&gt;The STSPIN233 3-phase driver for 1.8-10V, up to 1.3A.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/div&gt;
&lt;p&gt;The STSPIN233 I hadn’t seen yet, but it’s a really great tiny and cheap option for relatively low-power/low-voltage 3 phase driver. Three half bridges, gate drivers, and it supports (optional) current sense on all three phases individually which is great for e.g.&amp;nbsp;FOC. I couldn’t find this on a breakout available anywhere, so I had to buy an &lt;a href="https://www.mikroe.com/stspin233-click"&gt;STSPIN233 CLICK&lt;/a&gt; board, which has more on it than I wanted, but that’s fine. Since it has the MCU, I went ahead and put the servo control on it.&lt;/p&gt;
&lt;p&gt;I got some &lt;a href="https://ams.com/en/as5600"&gt;AS5600&lt;/a&gt; magnetic encoder breakout boards off amazon to use for angle measurement.&lt;/p&gt;
&lt;p&gt;I also got one of these &lt;a href="https://www.amazon.com/dp/B08S5JSD3Q"&gt;BLDC Gimbal Motors&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Then I 3D printed some parts to mount the magnet on the rotor, hold the encoder, and mount the whole thing to the microphone ring. It looks like this:&lt;/p&gt;
&lt;p&gt;&lt;a href="motor_cross_section6.png" class="lightbox" data-gallery="quarto-lightbox-gallery-8"&gt;&lt;img src="https://jeffmcbride.net/posts/acoustic-beamforming/motor_cross_section6.png" class="img-fluid" width="500"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The servo controller code gets programmed onto the STM32F0 that comes on the CLICK board, and it lives on github here: &lt;a href="https://github.com/mcbridejc/bldc-servo"&gt;bldc-servo&lt;/a&gt;.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="fitting-it-onto-the-microcontroller" class="level2"&gt;
&lt;h2 class="anchored" data-anchor-id="fitting-it-onto-the-microcontroller"&gt;Fitting it onto the microcontroller&lt;/h2&gt;
&lt;p&gt;On the STM32, I can’t process all the frames, so they are filtered based on RMS. First of all, we allocate most of the on-chip RAM to two buffer pools:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;One has 7 100kB buffers for holding PDM frames (the PDM data is BIG! 700kB holds 300ms)&lt;/li&gt;
&lt;li&gt;One has 12 4kB buffers for holding a single channel of a PCM frame&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Then the processing is separated into two stages:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;pre-processing&lt;/em&gt; runs the PDM to PCM conversion on one channel of data, and calculates an RMS value for the frame – basically, how loud is it?&lt;/li&gt;
&lt;li&gt;&lt;em&gt;post-processing&lt;/em&gt; finishes the PDM to PCM conversion on the remaining channels and then performs the beamforming and azimuth estimation.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="quarto-figure quarto-figure-center"&gt;
&lt;figure class="figure"&gt;
&lt;p&gt;&lt;a href="buffer_flow_diagram.png" class="lightbox" data-gallery="quarto-lightbox-gallery-9" title="After capture, two processing queues hold audio data in flight."&gt;&lt;img src="https://jeffmcbride.net/posts/acoustic-beamforming/buffer_flow_diagram.png" class="img-fluid figure-img" width="600" alt="After capture, two processing queues hold audio data in flight."&gt;&lt;/a&gt;&lt;/p&gt;
&lt;figcaption&gt;After capture, two processing queues hold audio data in flight.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/div&gt;
&lt;p&gt;The first queue is a standard queue for fresh frames; it shouldn’t ever have more than one frame at a time. The second queue has special rules: When a buffer is pushed onto the queue, the number of frames in the queue with audio data are limited to a maximum value. If there are more than 4 frames in the queue with data after a push, the frame with the lowest RMS value has its audio data freed. The &lt;code&gt;RmsBuffer&lt;/code&gt; object (which requires only a tiny bit of RAM) remains in the queue as a place holder, indicating to later processing when a frame was dropped, and providing the RMS value (which can be used by the azimuth estimation filter even if the audio is dropped). This way the pointing decision is based on the loudest frames, and because the pre-processing is fast enough and runs at higher priority, the DMA never runs out of buffers to store the next frame. The end result is that if we get a burst of more “loud” frames than we can store, we will end up processing (at least) the loudest four of them. This seems to work well enough.&lt;/p&gt;
&lt;section id="async-vs-tasks" class="level3"&gt;
&lt;h3 class="anchored" data-anchor-id="async-vs-tasks"&gt;Async vs Tasks&lt;/h3&gt;
&lt;p&gt;I started out using Rust async functions, and the &lt;a href="https://github.com/embassy-rs/embassy"&gt;embassy&lt;/a&gt; async executor with separate tasks for the pre- and post-processing. This sort of “cooperative” multitasking means that the post-processing task has to yield frequently enough to ensure that the pre-processing task can finish each new frame and – if necessary – return the lowest RMS frame back to the pool. You can insert this breaks in processing by inserting some strategic &lt;code&gt;embassy_futures.yield_now().await&lt;/code&gt; statements, but I found doing this in between processing each channel wasn’t sufficient, there were not obvious places to insert yields, and no easy way to make guarantees about timing in the face of potential processing changes.&lt;/p&gt;
&lt;p&gt;Unfortunately, there’s no way for an interrupt to cause a task switch – switching can only happen on await statements. So in this case, I think the async model wasn’t quite right. Instead, I hi-jacked an unused interrupt to get easy task-free threading on the cortex, using a great trick I learned from RTIC:&lt;/p&gt;
&lt;p&gt;First, define an interrupt to execute your task. In this case, it just completes pre-processing for any available frames:&lt;/p&gt;
&lt;div class="code-copy-outer-scaffold"&gt;&lt;div class="sourceCode" id="cb5" style="background: #f1f3f5;"&gt;&lt;pre class="sourceCode rust code-with-copy"&gt;&lt;code class="sourceCode rust"&gt;&lt;span id="cb5-1"&gt;&lt;span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"&gt;#[&lt;/span&gt;interrupt&lt;span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb5-2"&gt;&lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;fn&lt;/span&gt; LPTIM5() &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb5-3"&gt;    &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;let&lt;/span&gt; audio_reader &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt; &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;unsafe&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt; AUDIO_READER&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;.&lt;/span&gt;as_mut()&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;.&lt;/span&gt;unwrap() &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;};&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb5-4"&gt;    &lt;span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;embassy_futures::&lt;/span&gt;block_on(audio_reader&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;.&lt;/span&gt;preprocess())&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb5-5"&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Enable the interrupt in the NVIC (NOT in the peripheral):&lt;/p&gt;
&lt;div class="code-copy-outer-scaffold"&gt;&lt;div class="sourceCode" id="cb6" style="background: #f1f3f5;"&gt;&lt;pre class="sourceCode rust code-with-copy"&gt;&lt;code class="sourceCode rust"&gt;&lt;span id="cb6-1"&gt;&lt;span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;// Enable an "extra" irq to serve as a higher priority task than main loop&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb6-2"&gt;&lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;unsafe&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb6-3"&gt;    cp&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;.&lt;/span&gt;NVIC&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;.&lt;/span&gt;set_priority(&lt;span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;device::Interrupt::&lt;/span&gt;LPTIM5&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;,&lt;/span&gt; &lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;200&lt;/span&gt;)&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb6-4"&gt;    &lt;span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;cortex_m::peripheral::NVIC::&lt;/span&gt;unmask(&lt;span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;device::Interrupt::&lt;/span&gt;LPTIM5)&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb6-5"&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Then whenever a new buffer comes in, the processing task can be triggered from the IRQ.&lt;/p&gt;
&lt;div class="code-copy-outer-scaffold"&gt;&lt;div class="sourceCode" id="cb7" style="background: #f1f3f5;"&gt;&lt;pre class="sourceCode rust code-with-copy"&gt;&lt;code class="sourceCode rust"&gt;&lt;span id="cb7-1"&gt;&lt;span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;// IRQ goes off on completion of a DMA transfer. We then swap out the inactive buffer&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb7-2"&gt;&lt;span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;// for a for a fresh one, and pass the completed one off to the queue for processing&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb7-3"&gt;&lt;span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"&gt;#[&lt;/span&gt;interrupt&lt;span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb7-4"&gt;&lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;fn&lt;/span&gt; DMA1_STR0() &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb7-5"&gt;    &lt;span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;// &amp;lt;snip&amp;gt;... Handle the DMA stuff, get the new buffer&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb7-6"&gt;    &lt;/span&gt;
&lt;span id="cb7-7"&gt;    &lt;span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;// Put the buffer into the queue for processing&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb7-8"&gt;    out_producer&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;.&lt;/span&gt;enqueue(pdm_buffer)&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;.&lt;/span&gt;ok()&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;.&lt;/span&gt;expect(&lt;span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;"&gt;"failed queuing pdm buffer"&lt;/span&gt;)&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb7-9"&gt;&lt;/span&gt;
&lt;span id="cb7-10"&gt;    &lt;span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;// Wake the the pre-processing task which lives at the LPTIM5 interrupt vector&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb7-11"&gt;    &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;let&lt;/span&gt; &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;mut&lt;/span&gt; nvic &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt; &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;unsafe&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;{&lt;/span&gt; &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;crate&lt;/span&gt;&lt;span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;::device::CorePeripherals::&lt;/span&gt;steal()&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;.&lt;/span&gt;NVIC &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;};&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb7-12"&gt;    nvic&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;.&lt;/span&gt;request(&lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;crate&lt;/span&gt;&lt;span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;::device::Interrupt::&lt;/span&gt;LPTIM5)&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb7-13"&gt;&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;With the priorities set right, the processing task can be preempted by other more urgent IRQs (like the DMA or UART), but it will preempt any ongoing post-processing and execute immediately after the DMA IRQ returns.&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="moving-forward" class="level2"&gt;
&lt;h2 class="anchored" data-anchor-id="moving-forward"&gt;Moving Forward&lt;/h2&gt;
&lt;section id="morebetter-processing" class="level3"&gt;
&lt;h3 class="anchored" data-anchor-id="morebetter-processing"&gt;More/better processing&lt;/h3&gt;
&lt;p&gt;A good acoustic camera will need more microphones, and it’s clear that the STM32H7 is not the platform for that. This is a pretty ideal application for an FPGA, I think, especially since the initial PDM filtering can be done with very low bit width, so it can be quite small in the FPGA. If the FPGA can take over audio capture/filtering, the STM32H7 could do the beamforming, but at a less-than-ideal frame rate, especially since the processing time will grow with number of channels. Switching to I2S microphones would save a lot of the processing, but I still don’t see any path to capturing more channels on the STM32H7.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="combine-with-a-camera" class="level3"&gt;
&lt;h3 class="anchored" data-anchor-id="combine-with-a-camera"&gt;Combine with a camera&lt;/h3&gt;
&lt;p&gt;I want to see just how cheaply I could produce an acoustic camera with display and video overlay. The commercial products out there are all rather expensive, and it would be cool to have a working open source camera.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="wake-on-keyword-a-real-head" class="level3"&gt;
&lt;h3 class="anchored" data-anchor-id="wake-on-keyword-a-real-head"&gt;Wake-on-keyword + a real head&lt;/h3&gt;
&lt;p&gt;Part of the motivation here was to create a fun desk toy that would look at you when you talk to it. To make that really work I need a) a head arrangement that doesn’t interfere with the array and b) a keyword recognition model.&lt;/p&gt;
&lt;p&gt;The head seems very solvable. Maybe a wireframe head will allow sound through well enough, or I can locate the microphones above it (perhaps my head gets a crown?). The keyword recognition, I’m not sure. It seems like training a good keyword model takes a lot of examples, and whether I can reduce that model to something that will fit in memory and run on the STM32 is TBD.&lt;/p&gt;


&lt;/section&gt;
&lt;/section&gt;</summary><category term="electronics" /><category term="projects" /><category term="DSP" /></entry><entry><id>https://jeffmcbride.net/posts/high-voltage-amplifier/</id><title>High Voltage Amplifier</title><link href="https://jeffmcbride.net/posts/high-voltage-amplifier/" rel="alternate" /><published>2022-05-17T07:00:00Z</published><updated>2022-05-17T07:00:00Z</updated><summary type="html">&lt;p&gt;An open-source design for a high-voltage amplifier based on the Apex PA88 450V opamp.&lt;/p&gt;
&lt;div class="quarto-figure quarto-figure-center"&gt;
&lt;figure class="figure"&gt;
&lt;p&gt;&lt;a href="HVAmplifier.jpg" class="lightbox" data-gallery="quarto-lightbox-gallery-1" title="Photo of high voltage in 3d printed enclosure"&gt;&lt;img src="https://jeffmcbride.net/posts/high-voltage-amplifier/HVAmplifier.jpg" class="img-fluid figure-img" alt="Photo of high voltage in 3d printed enclosure"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;figcaption&gt;Photo of high voltage in 3d printed enclosure&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/div&gt;
&lt;section id="whats-it-for" class="level2"&gt;
&lt;h2 class="anchored" data-anchor-id="whats-it-for"&gt;What’s it for?&lt;/h2&gt;
&lt;p&gt;It’s just an amplifier that supports a relatively high output voltage. In this case, a +/- 3.3V signal in can be amplified to a +/- 200V signal out. Sometimes you need to create a signal with a fairly high voltage. Driving piezo activators is a classic example. Another example is generating a sine wave to drive electrodes on a digital microfluidic device. In my case, I wanted to test the dielectric properties of different films at high voltage.&lt;/p&gt;
&lt;p&gt;There are benchtop high voltage amps available, but they are pretty expensive and pretty big.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="the-design" class="level2"&gt;
&lt;h2 class="anchored" data-anchor-id="the-design"&gt;The design&lt;/h2&gt;
&lt;p&gt;The amplifier itself is a &lt;a href="https://www.apexanalog.com/products/pa88.html"&gt;PA88&lt;/a&gt; from Apex. This was straight-forward to use, as it’s just an op-amp with a few discretes as recommended in the datasheet. I set the current limit resistor to 150ohm, limiting the output to 4.5mA or so. Gain is adjustable by a trim pot.&lt;/p&gt;
&lt;div class="quarto-figure quarto-figure-center"&gt;
&lt;figure class="figure"&gt;
&lt;p&gt;&lt;a href="PA88.jpg" class="lightbox" data-gallery="quarto-lightbox-gallery-2" title="The PA88 comes in a massive TO-3 package"&gt;&lt;img src="https://jeffmcbride.net/posts/high-voltage-amplifier/PA88.jpg" class="img-fluid figure-img" alt="The PA88 comes in a massive TO-3 package"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;figcaption&gt;The PA88 comes in a massive TO-3 package&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/div&gt;
&lt;p&gt;The remainder of the design is a switching power supply to generate a 200V rail, and a negative 200V rail, and this was a fun challenge. In the past if I needed a negative supply for an op-amp, I’ve used the &lt;a href="https://www.maximintegrated.com/en/products/power/charge-pumps/MAX1044.html"&gt;MAX1044&lt;/a&gt; switched-capacitor converter to invert the positive supply. But this is certainly not going to work for 200V. I found this &lt;a href="https://www.ti.com/lit/an/slua288/slua288.pdf"&gt;TI AppNote&lt;/a&gt; demonstrating the use of a switched capacitor inverter for a bipolar supply. On each switching cycle, power from the inductor is dumped preferentially into whichever output (positive or negative) has the lower magnitude, so by regulating the positive output you get the negative output regulation for “free”.&lt;/p&gt;
&lt;p&gt;I used the LT1171HV for the switcher. Because it can only handle 75V on the switch output, I added a couple diode boost stages to both positive and negative outputs, so at 200V output the SW pin only sees 67V.&lt;/p&gt;
&lt;p&gt;The total BOM cost ended up being about $70, but that’s only because I was able to find the PA88 on Ebay for $30, which is a great deal. The usual price is over $200.&lt;/p&gt;
&lt;div class="quarto-figure quarto-figure-center"&gt;
&lt;figure class="figure"&gt;
&lt;p&gt;&lt;a href="HVAmplifier_open.jpg" class="lightbox" data-gallery="quarto-lightbox-gallery-3" title="High voltage amplifier with top of case removed to show circuit board"&gt;&lt;img src="https://jeffmcbride.net/posts/high-voltage-amplifier/HVAmplifier_open.jpg" class="img-fluid figure-img" alt="High voltage amplifier with top of case removed to show circuit board"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;figcaption&gt;High voltage amplifier with top of case removed to show circuit board&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id="results" class="level2"&gt;
&lt;h2 class="anchored" data-anchor-id="results"&gt;Results&lt;/h2&gt;
&lt;p&gt;I’ve brought this up, and I’ve used it a bit with my Analog Discovery 2 waveform generator as the input. So far it seems to work great. The only caveat is that I’m driving fairly small capacitive loads, so I haven’t really stressed the output power. The LTSpice simulation says it should be fine to supply 4mA continuous, but I haven’t done this yet.&lt;/p&gt;
&lt;p&gt;The design is available on &lt;a href="https://github.com/mcbridejc/HV_Amplifier"&gt;github&lt;/a&gt;, and includes the KiCad design + gerbers, as well as STLs for the 3D printed enclosure.&lt;/p&gt;


&lt;/section&gt;</summary><category term="projects" /><category term="electronics" /></entry><entry><id>https://jeffmcbride.net/posts/kicad-track-layout/</id><title>KiCad Racetrack Layout with Python Plugin</title><link href="https://jeffmcbride.net/posts/kicad-track-layout/" rel="alternate" /><published>2022-03-19T07:00:00Z</published><updated>2022-03-19T07:00:00Z</updated><summary type="html">&lt;section id="kicad-magnet-racetrack-layout-with-python-plugin" class="level2"&gt;
&lt;h2 class="anchored" data-anchor-id="kicad-magnet-racetrack-layout-with-python-plugin"&gt;KiCad Magnet Racetrack Layout with Python Plugin&lt;/h2&gt;
&lt;/section&gt;
&lt;section id="summary" class="level2"&gt;
&lt;h2 class="anchored" data-anchor-id="summary"&gt;Summary&lt;/h2&gt;
&lt;p&gt;In 2022, I designed a small magnet racetrack on a PCB, and to do it I had to create some python tools for programmatically adding tracks and via to the PCB design. This post talks about those tools and how they work.&lt;/p&gt;
&lt;div class="quarto-figure quarto-figure-center"&gt;
&lt;figure class="figure"&gt;
&lt;p&gt;&lt;a href="racetrack_layout.png" class="lightbox" data-gallery="quarto-lightbox-gallery-1" title="Example of a generated racetrack design"&gt;&lt;img src="https://jeffmcbride.net/posts/kicad-track-layout/racetrack_layout.png" class="img-fluid figure-img" alt="Example of a generated racetrack design"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;figcaption&gt;Example of a generated racetrack design&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/div&gt;
&lt;p&gt;This was all tested in KiCad v6.0.4. The python API does change from time to time, and this could break in some way in a future release.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="tracks-vs-footprints" class="level2"&gt;
&lt;h2 class="anchored" data-anchor-id="tracks-vs-footprints"&gt;Tracks vs Footprints&lt;/h2&gt;
&lt;p&gt;I experimented a bit with the best way to approach this, before settling on the method I did. The first thing I tried was creating the track as a footprint, since this is what I’ve used before for designing digital microfluidic electrodes. But, there were a couple problems with this:&lt;/p&gt;
&lt;p&gt;The first is that footprints do not allow custom geometries with electrical connectivity. What this means is that when you put copper shapes – lines, polygons, anything other than pads – into the footprint, those shapes do not get assigned the net from a pad that they touch, and you can’t draw a track to connect to it in pcbnew. You can work around this by turning off the rule preventing DRC errors, but this is annoying, and you can’t then actually use the DRC checker to make sure you’ve connected everything correctly.&lt;/p&gt;
&lt;p&gt;The second is that it is difficult to change the footprint, and I found it convenient to use the script to layout tracks, but still retain the ability to manually modify them a bit in pcbnew after generation.&lt;/p&gt;
&lt;p&gt;Instead of generating footprints, I created an Action Plugin which would draw the tracks and vias right into the PCB design.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="the-basics-creating-a-plugin-to-modify-the-board" class="level2"&gt;
&lt;h2 class="anchored" data-anchor-id="the-basics-creating-a-plugin-to-modify-the-board"&gt;The Basics: creating a plugin to modify the board&lt;/h2&gt;
&lt;p&gt;To create an action plugin, you just have to define a class which inherits from &lt;code&gt;pcbnew.ActionPlugin&lt;/code&gt; in a python script, call the &lt;code&gt;register()&lt;/code&gt; method on an instance of that class, and place that script into the kicad search path.&lt;/p&gt;
&lt;p&gt;Here’s a simple example which will create a track and via at a fixed location:&lt;/p&gt;
&lt;div class="code-with-filename"&gt;
&lt;div class="code-with-filename-file"&gt;
&lt;pre&gt;&lt;strong&gt;example_plugin.py&lt;/strong&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="code-copy-outer-scaffold"&gt;&lt;div class="sourceCode" id="cb1" data-filename="example_plugin.py" style="background: #f1f3f5;"&gt;&lt;pre class="sourceCode python code-with-copy"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb1-1"&gt;&lt;span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;"&gt;import&lt;/span&gt; pcbnew&lt;/span&gt;
&lt;span id="cb1-2"&gt;&lt;/span&gt;
&lt;span id="cb1-3"&gt;&lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;def&lt;/span&gt; pcbpoint(p):&lt;/span&gt;
&lt;span id="cb1-4"&gt;    &lt;span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;return&lt;/span&gt; pcbnew.wxPointMM(&lt;span class="bu" style="color: null;
background-color: null;
font-style: inherit;"&gt;float&lt;/span&gt;(p[&lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;0&lt;/span&gt;]), &lt;span class="bu" style="color: null;
background-color: null;
font-style: inherit;"&gt;float&lt;/span&gt;(p[&lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;1&lt;/span&gt;]))&lt;/span&gt;
&lt;span id="cb1-5"&gt;&lt;/span&gt;
&lt;span id="cb1-6"&gt;&lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;class&lt;/span&gt; ExamplePlugin(pcbnew.ActionPlugin):&lt;/span&gt;
&lt;span id="cb1-7"&gt;    &lt;span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;"""Draw a track and via&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb1-8"&gt;&lt;span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;    """&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb1-9"&gt;    &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;def&lt;/span&gt; defaults( &lt;span class="va" style="color: #111111;
background-color: null;
font-style: inherit;"&gt;self&lt;/span&gt; ):&lt;/span&gt;
&lt;span id="cb1-10"&gt;        &lt;span class="va" style="color: #111111;
background-color: null;
font-style: inherit;"&gt;self&lt;/span&gt;.name &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt; &lt;span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;"&gt;"Example Plugin"&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb1-11"&gt;        &lt;span class="va" style="color: #111111;
background-color: null;
font-style: inherit;"&gt;self&lt;/span&gt;.category &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt; &lt;span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;"&gt;"Modify PCB"&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb1-12"&gt;        &lt;span class="va" style="color: #111111;
background-color: null;
font-style: inherit;"&gt;self&lt;/span&gt;.description &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt; &lt;span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;"&gt;"Draws a track and a via"&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb1-13"&gt;        &lt;span class="va" style="color: #111111;
background-color: null;
font-style: inherit;"&gt;self&lt;/span&gt;.show_toolbar_button &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt; &lt;span class="va" style="color: #111111;
background-color: null;
font-style: inherit;"&gt;True&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb1-14"&gt;&lt;/span&gt;
&lt;span id="cb1-15"&gt;    &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;def&lt;/span&gt; Run( &lt;span class="va" style="color: #111111;
background-color: null;
font-style: inherit;"&gt;self&lt;/span&gt; ):&lt;/span&gt;
&lt;span id="cb1-16"&gt;        board &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt; pcbnew.GetBoard()&lt;/span&gt;
&lt;span id="cb1-17"&gt;        group &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt; pcbnew.PCB_GROUP(&lt;span class="va" style="color: #111111;
background-color: null;
font-style: inherit;"&gt;self&lt;/span&gt;.board)&lt;/span&gt;
&lt;span id="cb1-18"&gt;        board.Add(group)&lt;/span&gt;
&lt;span id="cb1-19"&gt;&lt;/span&gt;
&lt;span id="cb1-20"&gt;        &lt;span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;# Draw a track which goes from (100, 100) to (100, 110)&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb1-21"&gt;        track &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt; pcbnew.PCB_TRACK(board)&lt;/span&gt;
&lt;span id="cb1-22"&gt;        track.SetStart(pcbpoint((&lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;100&lt;/span&gt;, &lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;100&lt;/span&gt;)))&lt;/span&gt;
&lt;span id="cb1-23"&gt;        track.SetEnd(pcbpoint((&lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;100&lt;/span&gt;, &lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;110&lt;/span&gt;)))&lt;/span&gt;
&lt;span id="cb1-24"&gt;        &lt;span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;# Size here is specified as integer nanometers, so multiply mm by 1e6&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb1-25"&gt;        track.SetWidth(&lt;span class="bu" style="color: null;
background-color: null;
font-style: inherit;"&gt;int&lt;/span&gt;(&lt;span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;0.3&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;*&lt;/span&gt; &lt;span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;1e6&lt;/span&gt;))&lt;/span&gt;
&lt;span id="cb1-26"&gt;        track.SetLayer(pcbnew.F_Cu)&lt;/span&gt;
&lt;span id="cb1-27"&gt;        board.Add(track)&lt;/span&gt;
&lt;span id="cb1-28"&gt;        group.AddItem(track)&lt;/span&gt;
&lt;span id="cb1-29"&gt;&lt;/span&gt;
&lt;span id="cb1-30"&gt;        &lt;span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;# Now draw a via at one end of the track&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb1-31"&gt;        via &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt; pcbnew.PCB_VIA(board)&lt;/span&gt;
&lt;span id="cb1-32"&gt;        via.SetPosition(pcbpoint((&lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;100&lt;/span&gt;, &lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;110&lt;/span&gt;)))&lt;/span&gt;
&lt;span id="cb1-33"&gt;        via.SetDrill(&lt;span class="bu" style="color: null;
background-color: null;
font-style: inherit;"&gt;int&lt;/span&gt;(&lt;span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;0.3&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;*&lt;/span&gt; &lt;span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;1e6&lt;/span&gt;))&lt;/span&gt;
&lt;span id="cb1-34"&gt;        via.SetWidth(&lt;span class="bu" style="color: null;
background-color: null;
font-style: inherit;"&gt;int&lt;/span&gt;(&lt;span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;0.6&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;*&lt;/span&gt; &lt;span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;1e6&lt;/span&gt;))&lt;/span&gt;
&lt;span id="cb1-35"&gt;        board.Add(via)&lt;/span&gt;
&lt;span id="cb1-36"&gt;        group.AddItem(via)&lt;/span&gt;
&lt;span id="cb1-37"&gt;&lt;/span&gt;
&lt;span id="cb1-38"&gt;ExamplePlugin().register()&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;This plugin also creates a group, and adds the new elements to the group. This makes it easy to select them all together to move, or to delete. If you make changes to the layout script, you have to delete the old elements, reload plugins, and then re-run the plugin to regenerate.&lt;/p&gt;
&lt;p&gt;If you’re wondering where to install the python file, KiCad 6 has a helpful tool menu option to open the plugin directory:&lt;/p&gt;
&lt;p&gt;&lt;a href="open_plugin_directory.png" class="lightbox" data-gallery="quarto-lightbox-gallery-2"&gt;&lt;img src="https://jeffmcbride.net/posts/kicad-track-layout/open_plugin_directory.png" class="img-fluid" width="600"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;For me, on linux, I place plugins in &lt;code&gt;/home/jeff/.local/share/kicad/6.0/scripting/plugins&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;After adding the plugin file, or making changes to it, you need to run the Tools-&amp;gt;External Plugins-&amp;gt;Refresh Plugins command in pcbnew.&lt;/p&gt;
&lt;p&gt;When the plugin is loaded, it can be run from the Tools-&amp;gt;External Plugins menu, or by clicking the new icon in the toolbar.&lt;/p&gt;
&lt;p&gt;&lt;a href="run_icon.png" class="lightbox" data-gallery="quarto-lightbox-gallery-3"&gt;&lt;img src="https://jeffmcbride.net/posts/kicad-track-layout/run_icon.png" class="img-fluid" width="600"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/section&gt;
&lt;section id="generating-the-whole-track" class="level2"&gt;
&lt;h2 class="anchored" data-anchor-id="generating-the-whole-track"&gt;Generating the whole track&lt;/h2&gt;
&lt;p&gt;The full script for laying out the prototype track can be found in this &lt;a href="https://gist.github.com/mcbridejc/86b6a5e57d9cf06f394ab0474bd5d158"&gt;gist&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;It won’t teach you much more about controlling KiCad though, as the rest of the script is basically geometry for generating tracks, and I’m not realy going to go into that much here. In fact, that was just the first take, and it was eventually superceded by &lt;a href="https://github.com/mcbridejc/curvycad"&gt;CurvyCad&lt;/a&gt; which implements the same thing in a much more generic and re-usable way.&lt;/p&gt;
&lt;p&gt;But, one problem which pops up is how to wire the generated tracks. The track has two guard rail traces, and two phases A/B, and both ends of these wires need to get wired to connector pins. The newly generated tracks don’t have any net information, but that’s OK because that means that as soon as you connect them to something in pcbnew, they will inherit the net. The main trick is, you have to go to the “Interactive Router Settings” dialog, and enable “Highlight collisions” mode, and check the “Allow DRC violations” option. This lets you route tracks from your connector pads to the generated tracks, and when you do, KiCad automatically assigns them the connected net.&lt;/p&gt;
&lt;p&gt;Here’s the schematic for the racetrack. It’s pretty simple:&lt;/p&gt;
&lt;p&gt;&lt;a href="racetrack_schematic.png" class="lightbox" data-gallery="quarto-lightbox-gallery-4"&gt;&lt;img src="https://jeffmcbride.net/posts/kicad-track-layout/racetrack_schematic.png" class="img-fluid" width="600"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The net ties serve to represent the four tracks on the board: the two rails and the two alternating phases. These tracks are not &lt;em&gt;really&lt;/em&gt; connections; they are more like two-pin components and the net ties help represent that.&lt;/p&gt;
&lt;p&gt;The script, for simplicity, generates a fully connected loop. After running it, I decided where I wanted to insert connections and I manually cut the traces to wire them to the connector pins.&lt;/p&gt;
&lt;p&gt;That’s pretty much it. For a lot of tasks, the ability to programmatically generate PCB features allows for a lot faster design, and iteration. This demo was the first test, but now that it’s in place, I can quickly adjust the design and iterate on different versions of it.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="demo-video" class="level2"&gt;
&lt;h2 class="anchored" data-anchor-id="demo-video"&gt;Demo Video&lt;/h2&gt;
&lt;p&gt;And just in case you’re curious, here’s a video of the track in action:&lt;/p&gt;
&lt;iframe width="560" height="315" src="https://www.youtube.com/embed/cv7kJ6GT7s4" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""&gt;
&lt;/iframe&gt;
&lt;/section&gt;
&lt;section id="part-2-curvycad" class="level2"&gt;
&lt;h2 class="anchored" data-anchor-id="part-2-curvycad"&gt;Part 2: CurvyCad&lt;/h2&gt;
&lt;p&gt;The first plugin was very quick and dirty, and it basically had two functions: &lt;code&gt;append_straight&lt;/code&gt; and &lt;code&gt;append_arc&lt;/code&gt;. As example, the code below lays out a simple oval track, which starts at coordinates (150,150) and ends up back at the same point. This works because it was a simple shape and it was fairly easy to come up with a sequence which terminates exactly where it began, but it’s not very generalizable.&lt;/p&gt;
&lt;div class="code-copy-outer-scaffold"&gt;&lt;div class="sourceCode" id="cb2" style="background: #f1f3f5;"&gt;&lt;pre class="sourceCode python code-with-copy"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb2-1"&gt;TURN_RADIUS &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt; &lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;30&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb2-2"&gt;pos &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt; (&lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;150&lt;/span&gt;, &lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;150&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb2-3"&gt;pos &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt; append_straight(board, pos, &lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;10&lt;/span&gt;, &lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;0&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb2-4"&gt;pos &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt; append_arc(board, pos, TURN_RADIUS, &lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;0&lt;/span&gt;, &lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;90&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb2-5"&gt;pos &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt; append_arc(board, pos, TURN_RADIUS, &lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;90&lt;/span&gt;, &lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;90&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb2-6"&gt;pos &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt; append_straight(board, pos, &lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;10&lt;/span&gt;, &lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;180&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb2-7"&gt;pos &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt; append_arc(board, pos, TURN_RADIUS, &lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;180&lt;/span&gt;, &lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;90&lt;/span&gt;)&lt;/span&gt;
&lt;span id="cb2-8"&gt;pos &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt; append_arc(board, pos, TURN_RADIUS, &lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;270&lt;/span&gt;, &lt;span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;90&lt;/span&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Instead, I wanted to be able to make more arbitrary path designs, and ideally, I’d like to sketch them in a CAD tool. So, &lt;a href="https://github.com/mcbridejc/curvycad"&gt;CurvyCad&lt;/a&gt; was born. The idea is, you define one period of the repeating pattern for the track, along a straight line, and then import a DXF file containing a series of connected lines and arcs which define the track, and the periodic pattern is repeated along this path, being bent as necessary to follow the centerline. Additionally, the pitch of the pattern – how much distance it takes to complete one cycle – is adjusted so that the last cycle ends at the end of the curve. This way, if you make a looping track, the traces at the end of the track match up to the traces at the start perfectly.&lt;/p&gt;
&lt;p&gt;The pattern can be defined from just three elements:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Parallel lines (these run along the track, parallel to the centerline)&lt;/li&gt;
&lt;li&gt;Transverse lines (these run perpendicular to the track)&lt;/li&gt;
&lt;li&gt;Vias (just points, really, which are maintained to be at the same transverse distance along the line as it curves)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is a little bit restrictive, but it limits the output to shapes which can be represented as lines and arcs. If you try to warp an arc along another arc, for example, the path you end up with will not be an arc. You could do the transformation, but it would have to be piecewise approximated with a series of lines in Kicad. More importantly, I didn’t need this so I didn’t want to do all that extra work!.&lt;/p&gt;
&lt;div class="quarto-figure quarto-figure-center"&gt;
&lt;figure class="figure"&gt;
&lt;p&gt;&lt;a href="MagTrack_rev2.png" class="lightbox" data-gallery="quarto-lightbox-gallery-5" title="The second iteration of magnet track, created with CurvyCad"&gt;&lt;img src="https://jeffmcbride.net/posts/kicad-track-layout/MagTrack_rev2.png" class="img-fluid figure-img" alt="The second iteration of magnet track, created with CurvyCad"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;figcaption&gt;The second iteration of magnet track, created with CurvyCad&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/div&gt;


&lt;/section&gt;</summary><category term="projects" /><category term="kicad" /></entry><entry><id>https://jeffmcbride.net/posts/collecting-environmental-sensor-data-with-sdr/</id><title>Collecting home environmental sensor data with SDR, prometheus, and grafana</title><link href="https://jeffmcbride.net/posts/collecting-environmental-sensor-data-with-sdr/" rel="alternate" /><published>2021-03-23T07:00:00Z</published><updated>2021-03-23T07:00:00Z</updated><summary type="html">&lt;section id="summary" class="level2"&gt;
&lt;h2 class="anchored" data-anchor-id="summary"&gt;Summary&lt;/h2&gt;
&lt;p&gt;Although I explored a design for a &lt;a href="../../epaper-temperature-humidity-sensor/"&gt;custom humidity and temperature sensor&lt;/a&gt;, here I describe the solution I ultimately ended up with and which I would recommend to anyone looking to implement such a system.&lt;/p&gt;
&lt;p&gt;This article discusses the use of cheap acurite sensors which transmit their measurements on 433MHz ISM band which can be easily picked up with a cheap RTL-SDR device, and the setup of prometheus for data capture as well as the &lt;a href="https://github.com/mcbridejc/monitor_rtl433"&gt;monitor_rtl433&lt;/a&gt; utility I created to act as a bridge between the SDR and Prometheus.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="the-sensors" class="level2"&gt;
&lt;h2 class="anchored" data-anchor-id="the-sensors"&gt;The sensors&lt;/h2&gt;
&lt;p&gt;There are a number of options for sensors on the market. I’ve used these two with success. They have worked reliably, and they each come with a unique serial number so you can add as many as you want to your system and still differentiate between them.&lt;/p&gt;
&lt;p&gt;The AcuRite 06044 is an indoor only model with display:&lt;/p&gt;
&lt;div class="quarto-figure quarto-figure-center"&gt;
&lt;figure class="figure"&gt;
&lt;p&gt;&lt;a href="acurite_06044.jpg" class="lightbox" data-gallery="quarto-lightbox-gallery-1" title="Photo of Acurite 06044 sensor"&gt;&lt;img src="https://jeffmcbride.net/posts/collecting-environmental-sensor-data-with-sdr/acurite_06044.jpg" class="img-fluid figure-img" width="200" alt="Photo of Acurite 06044 sensor"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;figcaption&gt;Photo of Acurite 06044 sensor&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/div&gt;
&lt;p&gt;The AcuRite 06002M is an indoor/outdoor model without a display:&lt;/p&gt;
&lt;div class="quarto-figure quarto-figure-center"&gt;
&lt;figure class="figure"&gt;
&lt;p&gt;&lt;a href="acurite_06002M.jpg" class="lightbox" data-gallery="quarto-lightbox-gallery-2" title="Photo of Acurite 06002M sensor"&gt;&lt;img src="https://jeffmcbride.net/posts/collecting-environmental-sensor-data-with-sdr/acurite_06002M.jpg" class="img-fluid figure-img" width="120" alt="Photo of Acurite 06002M sensor"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;figcaption&gt;Photo of Acurite 06002M sensor&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/div&gt;
&lt;p&gt;There are other devices supported by &lt;a href="https://github.com/merbanan/rtl_433"&gt;rtl_433&lt;/a&gt;. Any of those should be easy to set up too.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="other-hardware" class="level2"&gt;
&lt;h2 class="anchored" data-anchor-id="other-hardware"&gt;Other hardware&lt;/h2&gt;
&lt;p&gt;To receive the signals, I used an &lt;a href="https://www.rtl-sdr.com/"&gt;RTL-SDR&lt;/a&gt; plugged into an odroid single board computer; a raspberry PI would also work just fine here, as would any other SBC that can run linux.&lt;/p&gt;
&lt;p&gt;&lt;a href="odroid_antenna.jpg" class="lightbox" data-gallery="quarto-lightbox-gallery-3"&gt;&lt;img src="https://jeffmcbride.net/posts/collecting-environmental-sensor-data-with-sdr/odroid_antenna.jpg" class="img-fluid" width="400"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/section&gt;
&lt;section id="setting-up-linux" class="level2"&gt;
&lt;h2 class="anchored" data-anchor-id="setting-up-linux"&gt;Setting up Linux&lt;/h2&gt;
&lt;p&gt;We’re going to setup a few services now:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Prometheus&lt;/em&gt; is a time series database.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Grafana&lt;/em&gt; is a dashboard/plotting package that will pull data from prometheus and display it in your browser.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;monitor_rtl433&lt;/em&gt; is a simple daemon I created to read the sensor data from the SDR and provide the stats for prometheus&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I’ll give a quick run-down of how I installed these, but I also recommend checking out the documentation for the projects or other tutorials specific to that if you run into trouble or want to adapt your methods a bit.&lt;/p&gt;
&lt;section id="setting-up-prometheus" class="level3"&gt;
&lt;h3 class="anchored" data-anchor-id="setting-up-prometheus"&gt;Setting up prometheus&lt;/h3&gt;
&lt;p&gt;Prometheus has binaries for arm7, available for download &lt;a href="https://prometheus.io/download/"&gt;here&lt;/a&gt;. To install, just download the arm7 tarball and extract:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;wget https://github.com/prometheus/prometheus/releases/download/v2.25.2/prometheus-2.25.2.linux-armv7.tar.gz
tar -xvzf prometheus-2.25.2.linux-armv7.tar.gz
mv prometheus-2.25.2.linux-armv7 prometheus&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then create a configuration for prometheus, to tell it where to scrape input data from. Here’s my configuration as an example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;## prometheus.yml
# my global config
global:
  scrape_interval:     15s # Set the scrape interval to every 15 seconds. Default is every 1 minute.
  evaluation_interval: 15s # Evaluate rules every 15 seconds. The default is every 1 minute.
  # scrape_timeout is set to the global default (10s).

# Alertmanager configuration
alerting:
  alertmanagers:
  - static_configs:
    - targets:
      # - alertmanager:9093

# A scrape configuration containing exactly one endpoint to scrape:
# Here it's Prometheus itself.
scrape_configs:
  # The job name is added as a label `job=&amp;lt;job_name&amp;gt;` to any timeseries scraped from this config.
  - job_name: 'prometheus'

    # metrics_path defaults to '/metrics'
    # scheme defaults to 'http'.

    static_configs:
    - targets: ['localhost:9090']

  - job_name: 'monitor433'
    scrape_interval: 10s
    static_configs:
    - targets: ['localhost:5000']&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The most important bit is the ‘monitor433’ scrape config job, as this tells prometheus to scape from &lt;code&gt;http://localhost:5000/metrics&lt;/code&gt;, which is where the python `monitor433’ process we’ll be setting up shortly will be listening.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="installing-grafana" class="level3"&gt;
&lt;h3 class="anchored" data-anchor-id="installing-grafana"&gt;Installing grafana&lt;/h3&gt;
&lt;p&gt;Grafana also has a stand-alone arm binaries release, which can be found &lt;a href="https://grafana.com/grafana/download?pg=get&amp;amp;plcmt=selfmanaged-box1-cta1&amp;amp;platform=arm"&gt;here&lt;/a&gt;, and which can be installed much the same way into your home directory:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;wget https://dl.grafana.com/oss/release/grafana-7.4.5.linux-armv6.tar.gz
tar -zxvf grafana-7.4.5.linux-armv6.tar.gz
mv grafana-7.4.5.linux-armv6 grafana&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Grafana configuration is stored in &lt;code&gt;grafana/conf/grafana.ini&lt;/code&gt;. I’ll leave any setup for grafana to the reader, because, to be honest, it has been a while since I set it up and I don’t know what I had to change. It may be that the default config is fine and you can do all of the setup in the grafana GUI. Check out the setup instructions here: https://prometheus.io/docs/visualization/grafana/.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="install-rtl_433" class="level3"&gt;
&lt;h3 class="anchored" data-anchor-id="install-rtl_433"&gt;Install rtl_433&lt;/h3&gt;
&lt;p&gt;You’ll need the rtl_433 utility installed. There are instructions for building &lt;a href="https://github.com/merbanan/rtl_433/blob/master/docs/BUILDING.md"&gt;here&lt;/a&gt;. I built it from source with cmake. You may be able to install from a package depending on your distribution, e.g.&amp;nbsp;with ubuntu &lt;code&gt;sudo apt install rtl-433&lt;/code&gt;.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="identifying-your-devices" class="level3"&gt;
&lt;h3 class="anchored" data-anchor-id="identifying-your-devices"&gt;Identifying your devices&lt;/h3&gt;
&lt;p&gt;Once rtl_433 is installed, and your SDR is plugged in, you can run &lt;code&gt;rtl_433 -F json&lt;/code&gt; to get a live stream of events detected. It should look something like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# rtl_433 -F json
rtl_433 version 19.08-18-g8eecdbb branch master at 201909241811 inputs file rtl_tcp RTL-SDR
Use -h for usage help and see https://triq.org/ for documentation.
Trying conf file at "rtl_433.conf"...
Trying conf file at "/root/.config/rtl_433/rtl_433.conf"...
Trying conf file at "/usr/local/etc/rtl_433/rtl_433.conf"...
Trying conf file at "/etc/rtl_433/rtl_433.conf"...

    Consider using "-M newmodel" to transition to new model keys. This will become the default someday.
    A table of changes and discussion is at https://github.com/merbanan/rtl_433/pull/986.

Registered 108 out of 138 device decoding protocols [ 1-4 8 11-12 15-17 19-21 23 25-26 29-36 38-60 63 67-71 73-100 102-103 108-116 119 121 124-128 131-138 ]
Found Rafael Micro R820T tuner
Exact sample rate is: 250000.000414 Hz
[R82XX] PLL not locked!
Sample rate set to 250000 S/s.
Tuner gain set to Auto.
Tuned to 433.920MHz.
{"time" : "2021-03-25 03:27:05", "model" : "LaCrosse TX141TH-Bv2 sensor", "id" : 71, "channel" : 0, "battery" : "OK", "temperature_C" : 11.100, "humidity" : 75, "test" : "No"}
{"time" : "2021-03-25 03:27:06", "model" : "Acurite tower sensor", "id" : 15922, "channel" : "A", "battery_low" : 0, "temperature_C" : 20.800, "humidity" : 46}
{"time" : "2021-03-25 03:27:06", "model" : "Acurite tower sensor", "id" : 15922, "channel" : "A", "battery_low" : 0, "temperature_C" : 20.800, "humidity" : 46}
{"time" : "2021-03-25 03:27:06", "model" : "Acurite tower sensor", "id" : 15922, "channel" : "A", "battery_low" : 0, "temperature_C" : 20.800, "humidity" : 46}
{"time" : "2021-03-25 03:27:07", "model" : "Acurite 606TX Sensor", "id" : 43, "battery" : "OK", "temperature_C" : 9.300, "mic" : "CHECKSUM"}
{"time" : "2021-03-25 03:27:10", "model" : "Acurite tower sensor", "id" : 15352, "channel" : "B", "battery_low" : 0, "temperature_C" : 7.700, "humidity" : 88}
{"time" : "2021-03-25 03:27:10", "model" : "Acurite tower sensor", "id" : 15352, "channel" : "B", "battery_low" : 0, "temperature_C" : 7.700, "humidity" : 88}
{"time" : "2021-03-25 03:27:10", "model" : "Acurite tower sensor", "id" : 15352, "channel" : "B", "battery_low" : 0, "temperature_C" : 7.700, "humidity" : 88}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now your task is to identify which sensor is which, and how difficult this is will depend on how many sensors you and your neighbors have. One trick is to switch the “channel” on a sensor of interest between A and B, and watch for one of the “id” numbers to change accordingly. Or, just power on and off a sensor and watch for it to start and stop. For some sensors, there is an LED which blinks each time it transmits an update, and it’s possible to visually correlate this with the updates on the terminal. Once I figure out the ID number of a sensor, I usually write it on the back of the sensor for next time.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="setting-up-monitor_rtl433" class="level3"&gt;
&lt;h3 class="anchored" data-anchor-id="setting-up-monitor_rtl433"&gt;Setting up monitor_rtl433&lt;/h3&gt;
&lt;p&gt;You can checkout the monitor project from &lt;a href="https://github.com/mcbridejc/monitor_rtl433"&gt;https://github.com/mcbridejc/monitor_rtl433&lt;/a&gt;. This utility is a little python daemon I wrote to spawn an rtl_433 process, and run a webserver to make make collected data available. In order to define exactly what goes into the &lt;code&gt;/metrics&lt;/code&gt; route scraped by prometheus, you will need to write a little custom python.&lt;/p&gt;
&lt;p&gt;For an example, see &lt;a href="https://github.com/mcbridejc/monitor_rtl433/blob/master/examples/main.py"&gt;examples/main.py&lt;/a&gt;. Basically, you need to create a python object, derived from &lt;code&gt;MetricFilter&lt;/code&gt; for each sensor you want to collect data from. The &lt;code&gt;process&lt;/code&gt; method on this class takes a dict created from the json blob returned by rtl_433, and maps it to &lt;code&gt;Metric&lt;/code&gt; objects which are provided to prometheus. This translation is necessary because the format of the JSON data varies for different sensor types.&lt;/p&gt;
&lt;p&gt;Here’s an example of the MetricFilter class that works for both types of acurite sensor:&lt;/p&gt;
&lt;div class="code-copy-outer-scaffold"&gt;&lt;div class="sourceCode" id="cb5" style="background: #f1f3f5;"&gt;&lt;pre class="sourceCode python code-with-copy"&gt;&lt;code class="sourceCode python"&gt;&lt;span id="cb5-1"&gt;&lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;class&lt;/span&gt; AcuriteTower(MetricFilter):&lt;/span&gt;
&lt;span id="cb5-2"&gt;    &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;def&lt;/span&gt; &lt;span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;"&gt;__init__&lt;/span&gt;(&lt;span class="va" style="color: #111111;
background-color: null;
font-style: inherit;"&gt;self&lt;/span&gt;, &lt;span class="bu" style="color: null;
background-color: null;
font-style: inherit;"&gt;id&lt;/span&gt;):&lt;/span&gt;
&lt;span id="cb5-3"&gt;        &lt;span class="va" style="color: #111111;
background-color: null;
font-style: inherit;"&gt;self&lt;/span&gt;.&lt;span class="bu" style="color: null;
background-color: null;
font-style: inherit;"&gt;id&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt; &lt;span class="bu" style="color: null;
background-color: null;
font-style: inherit;"&gt;id&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb5-4"&gt;        &lt;span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;# The `_match` property will be used to determine which sensor records&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb5-5"&gt;        &lt;span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;# this filter will be applied to&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb5-6"&gt;        &lt;span class="va" style="color: #111111;
background-color: null;
font-style: inherit;"&gt;self&lt;/span&gt;._match &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt; {&lt;span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;"&gt;"model"&lt;/span&gt;: &lt;span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;"&gt;"Acurite tower sensor"&lt;/span&gt;, &lt;span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;"&gt;"id"&lt;/span&gt; : &lt;span class="va" style="color: #111111;
background-color: null;
font-style: inherit;"&gt;self&lt;/span&gt;.&lt;span class="bu" style="color: null;
background-color: null;
font-style: inherit;"&gt;id&lt;/span&gt;}&lt;/span&gt;
&lt;span id="cb5-7"&gt;        &lt;/span&gt;
&lt;span id="cb5-8"&gt;    &lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;def&lt;/span&gt; process(&lt;span class="va" style="color: #111111;
background-color: null;
font-style: inherit;"&gt;self&lt;/span&gt;, r):&lt;/span&gt;
&lt;span id="cb5-9"&gt;        &lt;span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;"""Takes a single sensor record, and converts it to 0 or more metrics&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb5-10"&gt;&lt;span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;        """&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb5-11"&gt;        sensor_id &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt; &lt;span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;"&gt;"&lt;/span&gt;&lt;span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;%s%s&lt;/span&gt;&lt;span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;"&gt;"&lt;/span&gt; &lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;%&lt;/span&gt; (&lt;span class="bu" style="color: null;
background-color: null;
font-style: inherit;"&gt;str&lt;/span&gt;(&lt;span class="va" style="color: #111111;
background-color: null;
font-style: inherit;"&gt;self&lt;/span&gt;.&lt;span class="bu" style="color: null;
background-color: null;
font-style: inherit;"&gt;id&lt;/span&gt;), r[&lt;span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;"&gt;'channel'&lt;/span&gt;]) &lt;/span&gt;
&lt;span id="cb5-12"&gt;        &lt;span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;yield&lt;/span&gt; Metric(&lt;span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;"&gt;'temperature'&lt;/span&gt;, degc2f(r[&lt;span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;"&gt;'temperature_C'&lt;/span&gt;]), labels&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt;{&lt;span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;"&gt;'sensor_id'&lt;/span&gt;: sensor_id})&lt;/span&gt;
&lt;span id="cb5-13"&gt;        &lt;span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;yield&lt;/span&gt; Metric(&lt;span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;"&gt;'humidity'&lt;/span&gt;, r[&lt;span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;"&gt;'humidity'&lt;/span&gt;], labels&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt;{&lt;span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;"&gt;'sensor_id'&lt;/span&gt;: sensor_id})&lt;/span&gt;
&lt;span id="cb5-14"&gt;        &lt;span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;yield&lt;/span&gt; Metric(&lt;span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;"&gt;'battery_warning'&lt;/span&gt;, r[&lt;span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;"&gt;'battery_low'&lt;/span&gt;], labels&lt;span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt;{&lt;span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;"&gt;'sensor_id'&lt;/span&gt;: sensor_id})&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;In the constructor, it sets the &lt;code&gt;_match&lt;/code&gt; variable to a dict. This filter will be used to process any incoming data that matches all of the key/value pairs provided here. In this case, that means any records where “model” is “Acurite tower sensor” and “id” is the provided number.&lt;/p&gt;
&lt;p&gt;Once you create your own script based on the example main.py, you can run that and point your web browser at port 5000.&lt;/p&gt;
&lt;p&gt;Here, you’ll get a landing page showing some options:&lt;/p&gt;
&lt;p&gt;&lt;a href="landing_page_screenshot.png" class="lightbox" data-gallery="quarto-lightbox-gallery-4"&gt;&lt;img src="https://jeffmcbride.net/posts/collecting-environmental-sensor-data-with-sdr/landing_page_screenshot.png" class="img-fluid" width="400"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;If you follow the sensors link, you’ll get a table with the most recent update from all of the sensors it has seen since it was started. If you live around other people, you’ll probably see a lot of sensors here. Other’s weather sensors, tire pressure sensors, alarm system sensors, etc.&lt;/p&gt;
&lt;p&gt;&lt;a href="sensors_table_screenshot.png" class="lightbox" data-gallery="quarto-lightbox-gallery-5"&gt;&lt;img src="https://jeffmcbride.net/posts/collecting-environmental-sensor-data-with-sdr/sensors_table_screenshot.png" class="img-fluid" width="400"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The most important thing to make sure you are setup correctly for prometheus, is to make sure that you get the expected metrics under the &lt;code&gt;/metrics&lt;/code&gt; link. It should look something like this:&lt;/p&gt;
&lt;p&gt;&lt;a href="metrics_screenshot.png" class="lightbox" data-gallery="quarto-lightbox-gallery-6"&gt;&lt;img src="https://jeffmcbride.net/posts/collecting-environmental-sensor-data-with-sdr/metrics_screenshot.png" class="img-fluid" width="400"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/section&gt;
&lt;section id="configure-services" class="level3"&gt;
&lt;h3 class="anchored" data-anchor-id="configure-services"&gt;Configure services&lt;/h3&gt;
&lt;p&gt;In order to make sure your three new services are automatically started at boot, you can add them as systemd services.&lt;/p&gt;
&lt;p&gt;Here are my service definition files, which are stored in &lt;code&gt;/etc/systemd/system&lt;/code&gt;.&lt;/p&gt;
&lt;div class="code-with-filename"&gt;
&lt;div class="code-with-filename-file"&gt;
&lt;pre&gt;&lt;strong&gt;/etc/systemd/system/prometheus.service&lt;/strong&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="code-copy-outer-scaffold"&gt;&lt;div class="sourceCode" id="cb6" data-filename="/etc/systemd/system/prometheus.service" style="background: #f1f3f5;"&gt;&lt;pre class="sourceCode ini code-with-copy"&gt;&lt;code class="sourceCode ini"&gt;&lt;span id="cb6-1"&gt;&lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;[Unit]&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb6-2"&gt;&lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;Description&lt;/span&gt;&lt;span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt;&lt;span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;"&gt;Prometheus time series database&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb6-3"&gt;&lt;/span&gt;
&lt;span id="cb6-4"&gt;&lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;[Service]&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb6-5"&gt;&lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;Type&lt;/span&gt;&lt;span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt;&lt;span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;"&gt;simple&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb6-6"&gt;&lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;WorkingDirectory&lt;/span&gt;&lt;span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt;&lt;span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;"&gt;/root/prometheus&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb6-7"&gt;&lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;ExecStart&lt;/span&gt;&lt;span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt;&lt;span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;"&gt;/root/prometheus/prometheus --config.file=prometheus.yml &lt;/span&gt;&lt;/span&gt;
&lt;span id="cb6-8"&gt;&lt;/span&gt;
&lt;span id="cb6-9"&gt;&lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;[Install]&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb6-10"&gt;&lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;WantedBy&lt;/span&gt;&lt;span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt;&lt;span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;"&gt;multi-user.target&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="code-with-filename"&gt;
&lt;div class="code-with-filename-file"&gt;
&lt;pre&gt;&lt;strong&gt;/etc/systemd/system/grafana.service&lt;/strong&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="code-copy-outer-scaffold"&gt;&lt;div class="sourceCode" id="cb7" data-filename="/etc/systemd/system/grafana.service" style="background: #f1f3f5;"&gt;&lt;pre class="sourceCode ini code-with-copy"&gt;&lt;code class="sourceCode ini"&gt;&lt;span id="cb7-1"&gt;&lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;[Unit]&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb7-2"&gt;&lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;Description&lt;/span&gt;&lt;span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt;&lt;span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;"&gt;Grafana server&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb7-3"&gt;&lt;/span&gt;
&lt;span id="cb7-4"&gt;&lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;[Service]&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb7-5"&gt;&lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;Type&lt;/span&gt;&lt;span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt;&lt;span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;"&gt;simple&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb7-6"&gt;&lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;WorkingDirectory&lt;/span&gt;&lt;span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt;&lt;span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;"&gt;/root/grafana&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb7-7"&gt;&lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;ExecStart&lt;/span&gt;&lt;span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt;&lt;span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;"&gt;/root/grafana/bin/grafana-server -config conf/grafana.ini&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb7-8"&gt;&lt;/span&gt;
&lt;span id="cb7-9"&gt;&lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;[Install]&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb7-10"&gt;&lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;WantedBy&lt;/span&gt;&lt;span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt;&lt;span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;"&gt;multi-user.target&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="code-with-filename"&gt;
&lt;div class="code-with-filename-file"&gt;
&lt;pre&gt;&lt;strong&gt;/etc/systemd/system/monitor_rtl433.service&lt;/strong&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="code-copy-outer-scaffold"&gt;&lt;div class="sourceCode" id="cb8" data-filename="/etc/systemd/system/monitor_rtl433.service" style="background: #f1f3f5;"&gt;&lt;pre class="sourceCode ini code-with-copy"&gt;&lt;code class="sourceCode ini"&gt;&lt;span id="cb8-1"&gt;&lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;[Unit]&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb8-2"&gt;&lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;Description&lt;/span&gt;&lt;span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt;&lt;span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;"&gt;433MHz sensor monitor&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb8-3"&gt;&lt;/span&gt;
&lt;span id="cb8-4"&gt;&lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;[Service]&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb8-5"&gt;&lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;Type&lt;/span&gt;&lt;span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt;&lt;span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;"&gt;simple&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb8-6"&gt;&lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;Environment&lt;/span&gt;&lt;span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt;&lt;span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;"&gt;MONITOR_RTL433_HOST="0.0.0.0"&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb8-7"&gt;&lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;ExecStart&lt;/span&gt;&lt;span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt;&lt;span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;"&gt;/usr/bin/python3 /root/monitor433.py&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb8-8"&gt;&lt;/span&gt;
&lt;span id="cb8-9"&gt;&lt;span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;"&gt;[Install]&lt;/span&gt;&lt;/span&gt;
&lt;span id="cb8-10"&gt;&lt;span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;"&gt;WantedBy&lt;/span&gt;&lt;span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;"&gt;=&lt;/span&gt;&lt;span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;"&gt;multi-user.target&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;To make sure these services are started at boot, you may need to run these commands:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo systemctl enable grafana.service
sudo systemctl enable prometheus.service
sudo systemctl enable monitor_rtl433.service&lt;/code&gt;&lt;/pre&gt;
&lt;/section&gt;
&lt;section id="grafana-setup" class="level3"&gt;
&lt;h3 class="anchored" data-anchor-id="grafana-setup"&gt;Grafana Setup&lt;/h3&gt;
&lt;p&gt;Now you can setup grafana dashboards with your incoming sensor info. Create plots, or gauges, or whatever you prefer. In grafana, I label data sources according to where they are, e.g.&amp;nbsp;“Garage” or “Bedroom”. Here’s a quick example of a configuration for a temperature plot:&lt;/p&gt;
&lt;p&gt;&lt;a href="grafana_screenshot.png" class="lightbox" data-gallery="quarto-lightbox-gallery-7"&gt;&lt;img src="https://jeffmcbride.net/posts/collecting-environmental-sensor-data-with-sdr/grafana_screenshot.png" class="img-fluid" width="600"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Checkout the &lt;a href="https://grafana.com/docs/"&gt;Grafana Docs&lt;/a&gt; for everything you can do.&lt;/p&gt;


&lt;/section&gt;
&lt;/section&gt;</summary><category term="home automation" /><category term="software" /><category term="projects" /></entry><entry><id>https://jeffmcbride.net/posts/epaper-temperature-humidity-sensor/</id><title>Custom Temperature and Humidity Sensor with E-ink Display</title><link href="https://jeffmcbride.net/posts/epaper-temperature-humidity-sensor/" rel="alternate" /><published>2021-03-22T07:00:00Z</published><updated>2021-03-22T07:00:00Z</updated><summary type="html">&lt;section id="e-paper-temperature-and-humidity-sensor" class="level2"&gt;
&lt;h2 class="anchored" data-anchor-id="e-paper-temperature-and-humidity-sensor"&gt;E-Paper Temperature and Humidity Sensor&lt;/h2&gt;
&lt;/section&gt;
&lt;section id="summary" class="level2"&gt;
&lt;h2 class="anchored" data-anchor-id="summary"&gt;Summary&lt;/h2&gt;
&lt;p&gt;In 2019 I wanted some sensors to spread around my new house, and I wanted a new project, so I designed this little sensor. It was a fun exercise controlling the e-paper display, and I like the vertical USB-C connector design that allowed it to go straight onto a charger plugged into a wall outlet. Designing a sensor like this so small without inducing any self-heating is a challenge if you are looking for good accuracy. In the end, I decided to base my system on off-the-shelf 433MHz sensors, with an RTL-SDR as a base station collecting data, as described &lt;a href="../../collecting-environmental-sensor-data-with-sdr/"&gt;here&lt;/a&gt;&lt;/p&gt;
&lt;/section&gt;
&lt;section id="design" class="level2"&gt;
&lt;h2 class="anchored" data-anchor-id="design"&gt;Design&lt;/h2&gt;
&lt;p&gt;The sensor uses an ESP32 module as the controller and WiFi radio, an AMS ENS210 for temperature and humidity sensing, and a &lt;a href="https://www.digikey.com/en/products/detail/pervasive-displays/E2144CS021/6821161"&gt;1.44” Pervasive Displays e-ink display&lt;/a&gt;. It has a USB-C connector sticking out of the back, with the idea that it could be plugged into a USB-C charger on any outlet for easy mounting and no need for batteries.&lt;/p&gt;
&lt;p&gt;Once I got it all working, I found that it had a significant self-heating problem. Even after enabling sleep mode on the ESP32, and only powering on to sample every minute, I still got 5F+ of rise. I did a second revision of the board, with the temperature sensor placed onto a cut-out island at the bottom, with an air intake next to it so that – hopefully – convection would constantly pull fresh air in over the sensor and I could measure the ambient temperature, not the elevated value inside the case. This helped: with the redesign I got about 2F rise above ambient. Much better but still not as good as I’d like.&lt;/p&gt;
&lt;div class="quarto-figure quarto-figure-center"&gt;
&lt;figure class="figure"&gt;
&lt;p&gt;&lt;a href="front_assembled.jpg" class="lightbox" data-gallery="quarto-lightbox-gallery-1" title="Front view of assembled sensor"&gt;&lt;img src="https://jeffmcbride.net/posts/epaper-temperature-humidity-sensor/front_assembled.jpg" class="img-fluid figure-img" alt="Front view of assembled sensor"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;figcaption&gt;Front view of assembled sensor&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/div&gt;
&lt;div class="quarto-figure quarto-figure-center"&gt;
&lt;figure class="figure"&gt;
&lt;p&gt;&lt;a href="back_assembled.jpg" class="lightbox" data-gallery="quarto-lightbox-gallery-2" title="Back view of assembled sensor}"&gt;&lt;img src="https://jeffmcbride.net/posts/epaper-temperature-humidity-sensor/back_assembled.jpg" class="img-fluid figure-img" alt="Back view of assembled sensor}"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;figcaption&gt;Back view of assembled sensor}&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/div&gt;
&lt;div class="quarto-figure quarto-figure-center"&gt;
&lt;figure class="figure"&gt;
&lt;p&gt;&lt;a href="back_pcb.jpg" class="lightbox" data-gallery="quarto-lightbox-gallery-3" title="Back of printed circuit board"&gt;&lt;img src="https://jeffmcbride.net/posts/epaper-temperature-humidity-sensor/back_pcb.jpg" class="img-fluid figure-img" alt="Back of printed circuit board"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;figcaption&gt;Back of printed circuit board&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/div&gt;
&lt;div class="quarto-figure quarto-figure-center"&gt;
&lt;figure class="figure"&gt;
&lt;p&gt;&lt;a href="front_pcb.jpg" class="lightbox" data-gallery="quarto-lightbox-gallery-4" title="Front of printed circuit board"&gt;&lt;img src="https://jeffmcbride.net/posts/epaper-temperature-humidity-sensor/front_pcb.jpg" class="img-fluid figure-img" alt="Front of printed circuit board"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;figcaption&gt;Front of printed circuit board&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id="plan-b" class="level2"&gt;
&lt;h2 class="anchored" data-anchor-id="plan-b"&gt;Plan B&lt;/h2&gt;
&lt;p&gt;I put the first one into operation. But then two things happened: first I bought some off-the-shelf 433MHz wireless sensors that are much cheaper than I could build the custom sensor for and seem to have better accuracy, and then the e-ink display suddenly stopped working. The sensor still reported fine over WiFi, but a bit of debugging seem to suggest something had gone wrong with the display. The COTS sensors have a lot to recommend them: e.g.&amp;nbsp;they are cheap, they can be read easily with a RTL-SDR, no WiFi configuration necessary. So ultimately, while it was fun to bring up the e-ink display and this was a neat project, it didn’t make sense to keep using it.&lt;/p&gt;
&lt;p&gt;Since then, I’ve been using a &lt;a href="../../collecting-environmental-sensor-data-with-sdr/"&gt;small single-board computer and RTL-SDR&lt;/a&gt; to collect data from a number of sensors into a Prometheus database, and it has been simple and reliable. When I want to add a new sensor, I don’t have to order parts or build anything.&lt;/p&gt;


&lt;/section&gt;</summary><category term="embedded" /><category term="electronics" /><category term="projects" /><category term="home automation" /></entry><entry><id>https://jeffmcbride.net/posts/tapper/</id><title>Tapper: A flexible, packaged touch-screen kit for STM32</title><link href="https://jeffmcbride.net/posts/tapper/" rel="alternate" /><published>2021-03-01T08:00:00Z</published><updated>2021-03-01T08:00:00Z</updated><summary type="html">&lt;section id="tapper" class="level2"&gt;
&lt;h2 class="anchored" data-anchor-id="tapper"&gt;Tapper&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://github.com/mcbridejc/tapper"&gt;Tapper&lt;/a&gt; is a general purple STM32 + touchscreen in a box with flexible IO for quick prototyping.&lt;/p&gt;
&lt;p&gt;&lt;a href="top_assembled_1.jpg" class="lightbox" data-gallery="quarto-lightbox-gallery-1"&gt;&lt;img src="https://jeffmcbride.net/posts/tapper/top_assembled_1.jpg" class="img-fluid" width="600"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The situation is this: You want a microcontroller to read and display some sensor, control some process, or provide some user interface – the details vary – and you just want to build one or two but you also want it to be somewhat permanent. I really hate the jumble of wires, and the unreliability of breadboarding, but don’t want to go off and design a custom board and enclosure for each thing. I want something that’s flexible, but more reliable. It needs to be in a case. It needs to have connectorized IO. It needs to be flexible about providing power, because I don’t want to end up with a second power supply to drive the external bits, I just want to plug it in.&lt;/p&gt;
&lt;p&gt;On Tapper:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;All the IO is connected by soldered jumpers between through holes.&lt;/li&gt;
&lt;li&gt;Each of the three six-pin connectors provides power from one of the power rails (3.3V, 5V, or VIN) based on a solder bridge&lt;/li&gt;
&lt;li&gt;The touch screen is constant, wired to the same pins, so it works with the same software you used the last time&lt;/li&gt;
&lt;li&gt;The nucleo-32 dev board provides an on-board ST-link for programming and debugging, as well as a USB serial port&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;With tapper, and a power supply like &lt;a href="https://www.amazon.com/3V-Adjustable-Adapter-Control-Variable/dp/B07QVXYBDC/"&gt;this&lt;/a&gt; you can get a very portable setup with whatever supply voltage you need.&lt;/p&gt;
&lt;p&gt;My first use-case was a spincoater RPM controller. But I’ve also used it to wire up an I2C sensor and display the reading while allowing a user to switch modes and a few other projects.&lt;/p&gt;
&lt;p&gt;More detailed info lives on the &lt;a href="https://github.com/mcbridejc/tapper"&gt;github page&lt;/a&gt;. There’s an &lt;a href="https://github.com/mcbridejc/spincoater-controller"&gt;example software project&lt;/a&gt;, which includes a basic UI widget API for the touch screen. At some point I may expand on this and pull it out as a stand-alone library. The software uses the &lt;a href="https://modm.io/"&gt;modm C++ peripheral library&lt;/a&gt;, as do most of my embedded C++ projects.&lt;/p&gt;
&lt;p&gt;&lt;a href="tapper_composite.jpg" class="lightbox" data-gallery="quarto-lightbox-gallery-2"&gt;&lt;img src="https://jeffmcbride.net/posts/tapper/tapper_composite.jpg" class="img-fluid" width="600"&gt;&lt;/a&gt;&lt;/p&gt;


&lt;/section&gt;</summary><category term="electronics" /><category term="projects" /><category term="embedded" /></entry><entry><id>https://jeffmcbride.net/posts/kicad-component-layout/</id><title>KiCad automated component layout via configuration file</title><link href="https://jeffmcbride.net/posts/kicad-component-layout/" rel="alternate" /><published>2021-02-28T08:00:00Z</published><updated>2021-02-28T08:00:00Z</updated><summary type="html">&lt;section id="kicad-component-layout" class="level2"&gt;
&lt;h2 class="anchored" data-anchor-id="kicad-component-layout"&gt;Kicad Component Layout&lt;/h2&gt;
&lt;/section&gt;
&lt;section id="introduction" class="level2"&gt;
&lt;h2 class="anchored" data-anchor-id="introduction"&gt;Introduction&lt;/h2&gt;
&lt;p&gt;A KiCad plugin for quick programmatic layout of components on your PCB.&lt;/p&gt;
&lt;p&gt;I’ve done a number of board designs that involve laying out a lot of footprints in precise locations that are more easily described in code than via the KiCad UI. I wrote previously about &lt;a href="../../programmatic-layout-with-kicad-and-python"&gt;programmatic component layout&lt;/a&gt; in KiCad, but since then I’ve refined the process a fair bit. The code run in Kicad is reduced to a single plugin script, which you can find here: &lt;a href="https://github.com/mcbridejc/kicad_component_layout"&gt;https://github.com/mcbridejc/kicad_component_layout&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The basic premise is that you, the designer, write a script (could be python, or whatever you prefer) to generate a layout.yaml, and in KiCad you execute the plugin to sync the instructions in the layout.yaml file to your PCB design. This means that you can iterate and update your design without having to constantly reload the kicad plugin, and also if you work on multiple projects that use auto-generated placements, you don’t need to manage separate plugins for each project (I think project associated plugins would be a great feature for Kicad, but alas it is not available at the moment).&lt;/p&gt;
&lt;/section&gt;
&lt;section id="example-layout.yaml" class="level2"&gt;
&lt;h2 class="anchored" data-anchor-id="example-layout.yaml"&gt;Example layout.yaml&lt;/h2&gt;
&lt;p&gt;The layout file can change the positions, rotation, flip status (top/bottom), and the footprint for a component based on designators. I think a quick example of a layout file should give the idea:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  origin: [x0, y0] # Offset applied to all component locations
  components:
      R1:
          location: [x, y] # mm
          rotation: [r] # degrees
          flipped: false
          footprint:
              path: path/to/library.pretty
              name: SomeFootprint
      J1:
          ...&lt;/code&gt;&lt;/pre&gt;
&lt;/section&gt;
&lt;section id="github" class="level2"&gt;
&lt;h2 class="anchored" data-anchor-id="github"&gt;Github&lt;/h2&gt;
&lt;p&gt;There are more instructions on the &lt;a href="https://github.com/mcbridejc/kicad_component_layout"&gt;github project&lt;/a&gt;. If you find it useful, or have suggestions for how to expand it, feel free to open an issue or email with suggestions. I’m pretty open to expanding it to cover more use cases if anyone else finds it useful. I’ve found that this simple plugin handles all of the programmatic use cases I’ve needed, and it avoids the need for project specific code to be installed as a KiCad plugin.&lt;/p&gt;


&lt;/section&gt;</summary><category term="electronics" /><category term="kicad" /></entry></feed>