<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Filip</title>
    <description>The latest articles on DEV Community by Filip (@minkovsky).</description>
    <link>https://dev.to/minkovsky</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F15674%2Fc55d2e88-23c0-4309-8862-5302e012d643.jpg</url>
      <title>DEV Community: Filip</title>
      <link>https://dev.to/minkovsky</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/minkovsky"/>
    <language>en</language>
    <item>
      <title>Universally Stupid: Battling USB on a microcontroller with Rust</title>
      <dc:creator>Filip</dc:creator>
      <pubDate>Tue, 07 Jan 2020 08:38:14 +0000</pubDate>
      <link>https://dev.to/minkovsky/universally-stupid-battling-usb-on-a-microcontroller-with-rust-2leg</link>
      <guid>https://dev.to/minkovsky/universally-stupid-battling-usb-on-a-microcontroller-with-rust-2leg</guid>
      <description>&lt;p&gt;&lt;em&gt;Of ancient runes, &lt;br&gt;
a cursed mess - &lt;br&gt;
STM32 &lt;br&gt;
OTG FS&lt;/em&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  WTF is USB
&lt;/h1&gt;

&lt;p&gt;It's everywhere. It replaced many different connectors of the past - sometimes by just being able to emulate them, as is the case with serial ports. Some devices use it just for the power. But it's still an obtuse piece of design-by-committee which is difficult to implement right even if you've been doing this sort of thing for a long time indeed. Just ask the Raspberry Pi people!&lt;/p&gt;

&lt;p&gt;I'm going to skip over a lot of the USB spec itself, because it's huge. Indeed, even a website calling itself &lt;a href="http://www.usbmadesimple.co.uk/ums_1.htm" rel="noopener noreferrer"&gt;&lt;em&gt;USB Made Simple&lt;/em&gt;&lt;/a&gt; comes up to seven parts, not including extra material. I would try to skim through the USB 2.0 standard but apparently it's hosted by someone who doesn't understand how to set up TLS in nginx:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F1f4lxztehf2yk20wv1uq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F1f4lxztehf2yk20wv1uq.png" alt="UnSecured Blergh"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;No, I'm going to focus only on the bits that I found I needed in order to get my device to work. So starting from the electricals, a compliant-ish cable will have the following wires inside of it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Red for VBUS (nominal 5V), as expected&lt;/li&gt;
&lt;li&gt;Black for GND, also as expected&lt;/li&gt;
&lt;li&gt;Green for D+&lt;/li&gt;
&lt;li&gt;White for D-&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Everything is wrapped in a thin metallised film which acts as a shield against RF interference, which is a good job that I slit everything open and started messing about with the wires. Here's one of my breakout boards I've made. I added a Schottky diode to prevent stupid things from happening, like the computer trying push current to itself.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fg0jc31rta6vusqpj796y.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fg0jc31rta6vusqpj796y.jpg" alt="The breakout board"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For a brief mention of the theory - USB is a single-master, packet-switched, tiered star network where the host controls a number of peripherals that each have a hardware address (ish) and a network address (ish). The hardware address is composed of the Vendor ID and Product ID (VID and PID). If you want your device to be fully legit, you'll probably need to buy yourself a VID which is kinda expensive - either $5000 per year if you want to be a member of the USB Implementors Forum or $6000 one off purchase (&lt;a href="https://www.usb.org/getting-vendor-id" rel="noopener noreferrer"&gt;source&lt;/a&gt;), and there's still lots of hoops you have to jump through. Or for fewer hoops, most of them based around your product needing to be open source, you can get yourself a PID from &lt;a href="http://pid.codes/" rel="noopener noreferrer"&gt;pid.codes&lt;/a&gt; for free. And of course just for testing, you can use any VID/PID you'd like but remember this is not compliant with anything.&lt;/p&gt;

&lt;p&gt;There's also USB OTG ("On The Go") which allows connections between two embedded devices, but I don't think I ever saw it used.&lt;/p&gt;

&lt;h1&gt;
  
  
  Now there's two of them!
&lt;/h1&gt;

&lt;p&gt;There are actually two different USB implementations in the STM32 range: a USB Device core available on lower tier MCUs like F0-F3, and USB OTG cores available on higher tier MCUs like F4 and up. To make matters worse, there are two flavours of the USB OTG core. There's the Full Speed version which allows for up to 12Mbit/s data rates with a transceiver (or PHY) built into the MCU silicon, and a High Speed version which allows for up to 480 Mbit/s rates but requires an external transceiver, connected via a parallel interface to the MCU. Or, the High Speed core can also use the built-in Full Speed transceiver to achieve 12Mbit/s. Since both cores support OTG they automatically also support USB 2.0. Slightly confused? Yeah, me too.&lt;/p&gt;

&lt;p&gt;It should also be noted that &lt;em&gt;very similar&lt;/em&gt; USB OTG cores can be found in RISC-V MCUs made by the company GigaDevice which tries to compete with ST by just straight up cloning their chips. The reason is that they were originally designed by a company called Synopsys.&lt;/p&gt;

&lt;h1&gt;
  
  
  A rusty old bus
&lt;/h1&gt;

&lt;p&gt;Is there Rust support for these cores? Kinda. There is a project aiming to create a library for writing USB peripherals with the Synopsys USB OTG cores, which would support both the STM32F4 and up, as well as the weird GigaDevice parts. It can be found here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/stm32-rs/synopsys-usb-otg" rel="noopener noreferrer"&gt;https://github.com/stm32-rs/synopsys-usb-otg&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To use the USB OTG FS peripheral (plenty fast enough for a keyboard and serial port), you need to route D- to PA11 and D+ to PA12. You should also connect the USB 5V to the E5V pin on the Nucleo board, and switch the jumper for where the board will take its 5V from from U5V to E5V (Note that the Nucleo F446ZE board has USB OTG FS broken out already into a connector). There are some requirements as to how fast a particular clock needs to be. Also, you need to run a forked version of the HAL crate because it has some modifications that are required to support the USB functionality. This is an edge so bleeding, all the local vampires are trying to get in on the action.&lt;/p&gt;

&lt;p&gt;As luck would have it, there is some &lt;a href="https://github.com/Disasm/usb-otg-workspace/blob/master/example-f446ze-board/src/main.rs" rel="noopener noreferrer"&gt;example code&lt;/a&gt; for the F446 to borrow from. However, the luck ends here.&lt;/p&gt;

&lt;p&gt;From here onwards, it's all pain.&lt;/p&gt;

&lt;p&gt;It's surprisingly difficult to get any low level information about USB connections from Windows, even if you go through the trouble of installing USBPcap - a useful piece of software for capturing connections between &lt;em&gt;functional&lt;/em&gt; devices. Sometimes you won't even get Windows to grace you with an error message - the device you are connecting will just get power and that's about it. Furthermore, it seems like most other example libraries only support the OTG FS peripheral. I tried that with Mbed, a C++ based environment from Arm, and it does actually work on the Nucleo F446RE board, provided that you connect the pins right - I had a dummy USB keyboard enumerating in no time. The Rust library, however, gave me nothing.&lt;/p&gt;

&lt;h1&gt;
  
  
  The one where you get stuck
&lt;/h1&gt;

&lt;p&gt;I got stuck. The code compiled but nothing actually happened. And at the time of writing this I didn't have any test gear handy so I couldn't stick a logic analyser on the USB lines. I did open a bug on the Synopsys driver crate, and then started properly looking at the code inside, as well as other crates that together create some sort of support for the F446.&lt;/p&gt;

&lt;p&gt;I also started looking at C/C++ implementations of the low-level drivers in an attempt to fix the bug, and from there I noticed that there are actually some inconsistencies between the C header files and support libraries that ST provides, and the System View Description XML files that it also has to provide as per ARM licence terms. Namely, the SVD files can be, shall we say, utter garbage, with mislabelled or even missing registers. For instance one of the registers for the "IN endpoint 0" - a control endpoint reserved by the USB spec - that should be called &lt;code&gt;FS_DIEPTXF0&lt;/code&gt; is actually called &lt;code&gt;FS_GNPTXFSIZ&lt;/code&gt; in the SVD, and therefore, in all the crates generated from the SVD.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fw77zlibs84y8u4fol0ks.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fw77zlibs84y8u4fol0ks.png" alt="Discrepancy between register names, pictured with gratuitous poo emoji"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Oh and two other endpoint registers are missing, which means that on this microcontroller that has 6 endpoints (1 control + 5 for application use), only 4 are actually usable from Rust. I have filed a PR with the appropriate base crate to add those endpoints back, however even after applying those changes locally didn't fix my issues. So I need to think about several different strategies to deal with this. First, however, I'd really need to sit down and verify that the generated register maps match what is in the Reference Manual, in case there are more subtle errors within the SVD files.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Try to fix the synopsys-usb-otg crate.&lt;/li&gt;
&lt;li&gt;Write my own Rust USB drivers using a model much closer to what the Reference Manual offers, hoping that this would somehow work better than the existing attempt.&lt;/li&gt;
&lt;li&gt;Steal the low-level drivers from somewhere else - either the official C/C++ HAL provided by ST or something like the ChibiOS HAL, which do support USB on the F446, and then write a crate that wraps it and exposes it to Rust world.&lt;/li&gt;
&lt;li&gt;Abandon the idea of using Rust for everything, use an RTOS, write application code in Rust.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All these options seem kinda daunting. The last one would be perhaps the most &lt;em&gt;productive&lt;/em&gt; - after all, I would get a proper HAL, an actual RTOS, and community support from other users of said RTOS. However, it means using a large chunk of code that doesn't have the safety guarantees of Rust. I can minimise that chunk by only using an external USB HAL, which is probably the second most productive, and I may even be able to fit it into the usb-device model allowing me to write my actual keyboard implementation in Rust. I definitely don't want to be writing my own USB drivers given that I don't fully know what I'm doing yet, and I think the preferred option is fixing &lt;code&gt;synopsys-usb-otg&lt;/code&gt;, which would also hopefully help other people too. I will however need to buy a logic analyser capable of monitoring and decoding USB Full Speed communications and spend some time learning the usb-device model preferred in the world of embedded Rust.&lt;/p&gt;

&lt;h1&gt;
  
  
  Learnings
&lt;/h1&gt;

&lt;p&gt;It's a shame that the F446 implementation of USB is getting in the way, because from what I've seen the device side of the protocol is fairly reasonable. And not having proper test gear or much in the way of debugging capabilities doesn't really help either - sure, I can use on-chip debugging to set breakpoints in my code but I still have no visibility into what the USB core itself is doing. I can see that the &lt;code&gt;enable()&lt;/code&gt; routine runs but then nothing really happens, and the OS doesn't even see the device.&lt;/p&gt;

&lt;p&gt;It also seems that Rust isn't quite as ready for embedded use as I had thought - sure, &lt;em&gt;most&lt;/em&gt; of the stuff is there for a few lines of microcontrollers, but it's all third party support, and from what I've seen no manufacturer of MCUs has looked at supporting Rust natively. The lack of support for USB is also very annoying considering that quite a lot of what Rust has to offer would work very well on USB devices. And it seems that nobody has really invested in making a credible USB &lt;em&gt;Host&lt;/em&gt; implementation for embedded Rust either.&lt;/p&gt;

&lt;p&gt;On the other hand, maybe it would be enough to use Rust for application code and use a C-based library here and there?&lt;/p&gt;

&lt;p&gt;What do you think?&lt;/p&gt;

</description>
      <category>electronics</category>
      <category>rust</category>
      <category>embedded</category>
      <category>usb</category>
    </item>
    <item>
      <title>What's up, doc? Rust doctests and you</title>
      <dc:creator>Filip</dc:creator>
      <pubDate>Mon, 09 Dec 2019 14:16:40 +0000</pubDate>
      <link>https://dev.to/minkovsky/what-s-up-doc-rust-doctests-and-you-254e</link>
      <guid>https://dev.to/minkovsky/what-s-up-doc-rust-doctests-and-you-254e</guid>
      <description>&lt;p&gt;Rust has an interesting feature in its documentation system: if you're writing a library, it will actually compile and run the examples you give in your rustdoc comments as tests. This took me by surprise because I typically don't expect the documentation examples to immediately work, but I see why this is being done - and it's all to do with the fairly obvious: people will copy and paste examples from docs into their code.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--1mzflc4G--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/3prxmcldagot6glszb22.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1mzflc4G--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/3prxmcldagot6glszb22.png" alt="Advanced Copying And Pasting From The Docs"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So, doctests try to ensure that the examples you give in your doc comments will actually work. Cool, how do we make use of that?&lt;/p&gt;

&lt;h2&gt;
  
  
  Denial: Not a river in Egypt
&lt;/h2&gt;

&lt;p&gt;You can ignore doctests. Sometimes you may just want to let an example be an example and not be considered a test.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;h2&gt;
  
  
  Acceptance: Let it go
&lt;/h2&gt;

&lt;p&gt;You can also just let doctest run your code examples. What you need to keep in mind is that you will need to &lt;code&gt;use&lt;/code&gt; all your types inside the example, like so:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;h2&gt;
  
  
  Enlightenment: Writing examples as tests
&lt;/h2&gt;

&lt;p&gt;Tests in Rust are fairly simple - any function that either returns &lt;code&gt;()&lt;/code&gt; or panics can be a test, and your doctests can be used as regular tests, with asserts and everything:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;h2&gt;
  
  
  Galaxy brain: &lt;em&gt;hiding&lt;/em&gt; the tests in your docs
&lt;/h2&gt;

&lt;p&gt;But what if you don't actually want to put all the test code and boilerplate in the documentation example itself? Well, rust has you covered too:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;And note how this will look in the generated HTML docs:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--1yydzLXR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/tgoav5874txvljdqn8o7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1yydzLXR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/tgoav5874txvljdqn8o7.png" alt="The last example doesn't show the boilerplate code"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;(Also see the little warning label on the first example? That's because we marked the first example as an ignored test, and the docs will make a note of that - so that's an incentive to keep your doctests passing.)&lt;/p&gt;

&lt;h2&gt;
  
  
  Even more
&lt;/h2&gt;

&lt;p&gt;Those are just the basics of doctests in Rust, and if you write libraries in this language, you should definitely check out the &lt;a href="https://doc.rust-lang.org/rustdoc/documentation-tests.html"&gt;documentation tests&lt;/a&gt; manual for more - and I bet those examples are also tested 🤯&lt;/p&gt;

</description>
      <category>rust</category>
      <category>testing</category>
    </item>
    <item>
      <title>So you want to write an embedded driver in Rust</title>
      <dc:creator>Filip</dc:creator>
      <pubDate>Sat, 07 Dec 2019 21:19:10 +0000</pubDate>
      <link>https://dev.to/minkovsky/so-you-want-to-write-an-embedded-driver-in-rust-2iko</link>
      <guid>https://dev.to/minkovsky/so-you-want-to-write-an-embedded-driver-in-rust-2iko</guid>
      <description>&lt;p&gt;&lt;em&gt;This is the fourth post along my journey to creating a biometric password manager. For an overview of the project, &lt;a href="https://dev.to/minkovsky/designing-a-biometric-password-manager-26kn"&gt;read this post&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;For my sins, I decided to pick a project where I try to learn way too many different things at the same time, and choose components with little resources and lots of work that needs to be put into them in order to get them working. And so I ended up in the middle of writing a driver for an obscure part in a language I don't know very well yet, so at least I'm gonna get a blog post out of this whole process.&lt;/p&gt;

&lt;h1&gt;
  
  
  Poking it with a stick
&lt;/h1&gt;

&lt;p&gt;When you see a new library or piece of tech that you want to use but don't know how, you tend to want to look at the documentation. Except in the case where the documentation's quite limited, partly due to being badly put together, and partly because what little there is, is badly translated. It's like you buy a car but then you realise that the whole dashboard is missing, and what you have is a photo of a dashbord from a similar type of car and a brief guide of what all the wires do, except some of the labels are confusing or wrong. So you decide to make your own dashboard because hey, you bought the car already.&lt;/p&gt;

&lt;p&gt;The question is, when you start working on your dashboard, what bit do you hook up first? Ideally you want something that gives you some indication that the car is working, but doesn't actually cause it to do anything. Like the indicator lights. And so the very first request I implemented was for the status of my R502.&lt;/p&gt;

&lt;p&gt;When you're dealing with something that you don't fully understand, you tend to go a bit conservative. I wanted to make my test request something that would not alter the state of the device, something analogous to a GET request in a REST API. Many embedded components offer this sort of endpoint one way or another - it's a useful tool for validating that the device has initialised properly and is ready for operation.&lt;/p&gt;

&lt;h1&gt;
  
  
  Make it work first, make it pretty later, don't skip steps
&lt;/h1&gt;

&lt;p&gt;Prepare to rewrite your code. A lot. You want to create something simple first, but the moment it outlives its usefulness, you have to replace it. Other people will look at your code, do you want them to yell at you on Twitter?&lt;/p&gt;

&lt;p&gt;Remember how I said to do the simplest, non-destructive thing first? You should also do it in a fairly simple way. For instance, having a massive match statement that contains a handful of initial commands is okay for the start - but pretty soon you'll start wanting to split up the logic somehow into separate "classes" or modules. The final design of a driver like this should ideally separate the concerns - you'd want to put all the UART interface stuff in one layer, and packet serialisation/deserialisation in another.&lt;/p&gt;

&lt;p&gt;One more thing: Write examples. Examples are the most important piece of documentation you can ever produce. They will also help you validate your API design since you'll be forced to use the API you create. In fact, I realised that I needed to change how my API is designed after writing an example that simply authenticates with the R502 - code consuming the library ended up looking clunky and not too much to my liking. Another thing you can do in the example is cheat - while the driver I'm writing is going to target embedded devices through the embedded-hal interface, there's nothing that says you have to do development on an embedded device to begin with - just write some bridge code between your favourite serial port support crate for an x64 architecture and embedded-hal, and you'll be able to compile and run your examples directly on your machine.&lt;/p&gt;

&lt;h1&gt;
  
  
  Hell is other people's code
&lt;/h1&gt;

&lt;p&gt;Remember the dubiously modified Arduino driver that I mentioned in the last post on the R502? Turns out that actually it can tell you quite a lot about the device if you actually pay attention to it, and also really understand the datasheet. What took me the longest was trying to figure out how to put the individual commands together to actually enroll and verify fingerprints. I remember one day I was trying to figure out what commands to implement after the initial handshake protocol. So I looked more thoroughly at the examples given in the shady Dropbox link that you have to email the manufacturer for. And at some point, it just clicked - but that will only happen if you really take the mental effort to understand the datasheet and the examples, and that's not easy considering that the datasheet is written in bad English and the examples are written in bad C++.&lt;/p&gt;

&lt;h1&gt;
  
  
  A brief guide to the R502
&lt;/h1&gt;

&lt;p&gt;From what I can understand now, the theory of operation of the R502 fingerprint sensor module is thus:&lt;/p&gt;

&lt;p&gt;The front of the device is a type of camera. It uses capacitance to create an image - likely an offshoot from capacitive touch button technology. When you place a finger on the device and tell it to capture an image, it will measure how long it takes to charge each capacitor in (presumably) a matrix of 192x192 pixels, creating an image of your fingerprint. That image is stored in the R502's &lt;em&gt;image buffer&lt;/em&gt;. There exist commands to download the image from the R502 to a host, or even upload another from the host - more on that in the speculations below. The R502 can then be instructed to process the image and put the result into one of two &lt;em&gt;character buffers&lt;/em&gt; - and I suspect that &lt;em&gt;character&lt;/em&gt; here doesn't refer to text but rather the characteristics of your finger.&lt;/p&gt;

&lt;p&gt;Two character buffers? Why?&lt;/p&gt;

&lt;p&gt;Well, to enroll a fingerprint, you have to capture two images of it. Because the device has little in the way of RAM - although it does rock a full fake STM32 micro - it's easier to have two character buffers rather than two image buffers. It's also easier to run the matching algorithm on already processed fingerprints. And if you want to match a specific fingerprint, you can actually load a character &lt;em&gt;file&lt;/em&gt; from R502's internal flash into one of the buffers and capture a new image into another. Then the matching is a question of "do the two character buffers look similar?" There is also a function in the device to match a newly captured fingerprint against the whole &lt;em&gt;library&lt;/em&gt; - all enrolled fingerprints on the device. The R502 supports up to 200.&lt;/p&gt;

&lt;p&gt;So, what are the common workflows on this device then?&lt;/p&gt;

&lt;h2&gt;
  
  
  Enrolling a fingerprint
&lt;/h2&gt;

&lt;p&gt;This workflow registers a new fingerprint in the R502.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Capture a fingerprint image&lt;/li&gt;
&lt;li&gt;Process it into character buffer 1&lt;/li&gt;
&lt;li&gt;Capture another image of the same finger&lt;/li&gt;
&lt;li&gt;Process it into character buffer 2&lt;/li&gt;
&lt;li&gt;Generate a &lt;em&gt;template&lt;/em&gt; from both character buffers&lt;/li&gt;
&lt;li&gt;Store the template into the &lt;em&gt;library&lt;/em&gt; at the given index&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Matching a specific fingerprint
&lt;/h2&gt;

&lt;p&gt;This workflow matches a new fingerprint against a specific,&lt;br&gt;
previously enrolled one.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Capture a fingerprint image&lt;/li&gt;
&lt;li&gt;Process it into character buffer 1&lt;/li&gt;
&lt;li&gt;Load the requested finger from flash into character buffer 2&lt;/li&gt;
&lt;li&gt;Perform a match&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Searching for a fingerprint
&lt;/h2&gt;

&lt;p&gt;This workflow matches a new fingerprint against the whole list of&lt;br&gt;
previously enrolled fingers, and tells which one it is if there is&lt;br&gt;
a match.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Capture a fingerprint image&lt;/li&gt;
&lt;li&gt;Process it into character buffer 1&lt;/li&gt;
&lt;li&gt;Perform a search&lt;/li&gt;
&lt;/ol&gt;

&lt;h1&gt;
  
  
  The order of doing things
&lt;/h1&gt;

&lt;p&gt;The principal rule of getting things to just work is to do the simplest thing first. However, after implementing a few commands, they are all roughly the same level of simple. Sure, some of them return their data in multiple packets, but we're not doing those yet. The second thing to consider is, which piece of work unblocks the biggest proportion of further development - and in this case, capturing a fingerprint image features in all three core workflows.&lt;/p&gt;

&lt;p&gt;I am using github's projects functionaliy here to keep track of what I'm doing in an approximation of a kanban process. Each workflow has a project attached and issues are linked together - at the very least, the final workflow ticket will have a list of dependencies. It should help me keep track of what I need to do next - with the downside of making the project fully project-managed. I mean how am I supposed to just do the things in an order?&lt;/p&gt;

&lt;h1&gt;
  
  
  What is the R502 for?
&lt;/h1&gt;

&lt;p&gt;So here's the speculation section. The R502 is not an inherently secure device - I would love it to be able to do signed exchanges or even just basic encryption of packets being sent back and forth.&lt;/p&gt;

&lt;p&gt;However, that's not quite what the device is designed for - apparently, you can upload fingerprint images to it from a host machine, which in theory means you can network the things and enroll someone on one and have them be enrolled on all of your readers. One of the use cases listed on the manufacturer's website is attendance tracking - for schools or perhaps shift work. Certainly the TSA has been using fingerprint-based shift clocks (which were &lt;a href="https://youtu.be/hbqVNlwfjxo?t=1043"&gt;hilariously vulnerable&lt;/a&gt;), and I hear that this method of clocking in and out is popular in various places in Asia.&lt;/p&gt;

&lt;p&gt;Some of the other accessories sold by HZ Grow include a control box for a solenoid. You plug your fingerprint reader and lock into it, and it'll lock and unlock stuff for you based on whether the fingerprint is correct. In fact there are gun safes that implement similar mechanisms, again &lt;a href="https://www.youtube.com/watch?v=rg9k12aTR5o"&gt;hilariously vulnerable&lt;/a&gt; to attacks on anything other than the electronics.&lt;/p&gt;

&lt;h1&gt;
  
  
  Call the forklift person
&lt;/h1&gt;

&lt;p&gt;Right so I have this driver now. It doesn't do much but you are able to perform one of the main workflows outlined in a previous section, so I decided that it's a good time as any to release the bloody thing to the world and see what comes back. Hopefully nothing nasty. I picked the MIT licence for it because that's the default in Cargo and I don't hold any trademarks I wish to protect or patents I need to grant so there's no point of using the Apache 2.0 licence. Then I made sure I was happy with the documentation and finally did &lt;code&gt;cargo publish&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It's there now. On the internet. Here: &lt;a href="https://crates.io/crates/hzgrow-r502"&gt;https://crates.io/crates/hzgrow-r502&lt;/a&gt; (oh and the docs are here: &lt;a href="https://docs.rs/hzgrow-r502/0.1.1/"&gt;https://docs.rs/hzgrow-r502/0.1.1/&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;I should probably go add it to some list or something, in case someone wants to buy one of those fingerprint modules and start using it for something. There's probably fun to be had. Somehow.&lt;/p&gt;

&lt;p&gt;Next time I hope to have more of the functionality done and have a demo running on an actual microcontroller. That should be an amount of fun as well.&lt;/p&gt;

</description>
      <category>rust</category>
      <category>embedded</category>
      <category>electronics</category>
    </item>
    <item>
      <title>Strange parts: fingerprint reader testing</title>
      <dc:creator>Filip</dc:creator>
      <pubDate>Tue, 12 Nov 2019 11:08:48 +0000</pubDate>
      <link>https://dev.to/minkovsky/strange-parts-fingerprint-reader-testing-jlm</link>
      <guid>https://dev.to/minkovsky/strange-parts-fingerprint-reader-testing-jlm</guid>
      <description>&lt;p&gt;&lt;em&gt;This is the third post along my journey to creating a biometric password manager. For an overview of the project, &lt;a href="https://dev.to/minkovsky/designing-a-biometric-password-manager-26kn"&gt;read this post&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Questionable findings
&lt;/h1&gt;

&lt;p&gt;Finding the actual fingerprint reader for the job was not easy. Which is crazy considering how popular small fingerprint readers are - chances are, you have one in your pocket right now. The problem is that those parts are made specifically for mobile phone manufacturers, and don't typically trickle down to the average maker. I think at the very beginning of this project, before I even decided to do it, I spent about a day's worth of mashing different keywords together and browsing through parts. Some "highlights":&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;DigiKey, Mouser, Farnell - the usual suspects - didn't have any modules small enough for my application. Even LCSC, which specialises in Asian brands, doesn't have anything (useful) in stock.&lt;/li&gt;
&lt;li&gt;You can find quite a lot of materials about a &lt;a href="https://www.adafruit.com/product/751" rel="noopener noreferrer"&gt;particular Adafruit part&lt;/a&gt;, which unfortunately is flipping huge - the intended use is in fingerprint-enabled safes or locks.&lt;/li&gt;
&lt;li&gt;There's quite a lot on Aliexpress and Alibaba, but finding what I want in this particular haystack is never easy, especially with people putting up a clone of a clone, or reselling somebody else's part with $1 markup.&lt;/li&gt;
&lt;li&gt;You'll also find tiny little USB dongles that integrate the exact sort of fingerprint reader you might want, as if to taunt you - "you want that sensor, don't you? too bad, you can't have it"&lt;/li&gt;
&lt;li&gt;Alibaba is slightly more forthcoming but only if you tweak your keywords just right. &lt;code&gt;fingerprint reader&lt;/code&gt; seems to be more suitable for complete devices, &lt;code&gt;fingerprint sensor&lt;/code&gt; or &lt;code&gt;module&lt;/code&gt; would usually throw up more parts.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For now I settled on a module made by a company called Grow (or Hangzhou Grow), where you can buy the sensors direct on Alibaba. I will probably revisit my part selection before any serious production. However, I now have something semi-reasonable to start prototyping with: the &lt;a href="https://www.alibaba.com/product-detail/GROW-R502-Round-Smart-Electronics-Capacitive_62014711309.html" rel="noopener noreferrer"&gt;Grow R502&lt;/a&gt; &lt;em&gt;(not an affiliate link)&lt;/em&gt;. That part itself appears to be a clone of &lt;a href="https://www.miaxis.net/biometric-oem-module/miaxis-circle-integrated-fingerprint-module-sm-66h2.html" rel="noopener noreferrer"&gt;this Miaxis part&lt;/a&gt; - however that module is not readily available.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fsc02.alicdn.com%2Fkf%2FHTB1rSgfbifrK1RjSspbq6A4pFXap%2F229343960%2FHTB1rSgfbifrK1RjSspbq6A4pFXap.jpg_.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fsc02.alicdn.com%2Fkf%2FHTB1rSgfbifrK1RjSspbq6A4pFXap%2F229343960%2FHTB1rSgfbifrK1RjSspbq6A4pFXap.jpg_.webp" alt="Vendor close-up of the R502 module"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For a more detailed - and by that I mean longer and less coherent - version of this sort of process, I recommend &lt;a href="https://www.youtube.com/watch?v=nTpE1Nw3Yy4" rel="noopener noreferrer"&gt;that one EEVBlog video&lt;/a&gt;, which is just half an hour of Dave browsing through similarly decreasing in reputation corners of the internet, looking for a perfect display for his project.&lt;/p&gt;

&lt;h1&gt;
  
  
  "Data" sheets
&lt;/h1&gt;

&lt;p&gt;To get the full datasheet or manual for the sensor, or the example code, SDKs, and other essential equipment, you have to go to the manufacturer's website... and they're not there, because you need to contact them. I think this might be because of some copy protection tactics they're trying to do? In any case, what they will send you eventually is a pair of Dropbox links, one for the manuals for all their products, and one for the code examples and test software.&lt;/p&gt;

&lt;p&gt;The datasheet/manual is in delightful Chinglish that you typically only find on cheap tat that Big Clive tears down on YouTube for our collective amusement. You really have to work hard to figure out what they mean. And sometimes they'll have downright incorrect information. For instance, the datasheet would say that the connector on the back of the device is a "MX1.0-6P", presumably meaning Molex. However it's not Molex, but rather a 1mm pitch micro JST type connector. Did I mention the cables are really hard to find? Yes, you could probably make your own, but really, would you like to spend an afternoon crimping wires for a 1 millimetre pitch connectors? Nah.&lt;/p&gt;

&lt;p&gt;I would really love it if the manufacturer offered an option of a flat flex cable. That would make integrating this module in a small, USB dongle sized enclosure much easier. Perhaps that's what I'll look for when I revisit my part selections.&lt;/p&gt;

&lt;h1&gt;
  
  
  Iron work
&lt;/h1&gt;

&lt;p&gt;Because of the small pitch of the original connector, I decided to make a breakout board from some perfboard. It's just connecting some 2.54mm (0.1") headers to the loose ends of the wires coming out of the cable supplied with the sensor. You can see a video of how I did it below:&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/ShlkdMDnvTs"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Next, I just had to hook it up to a serial to USB converter that could also supply 3.3V power to the module. From there, I should be able to run the quasi-supplied software and get the module up and running. I chose to use the ESP-Prog. Normally this device is used to program the ESP8266 and ESP32 microcontrollers, however it can also function as a general purpose USB to serial converter with 3.3V supply and logic. Of the 6 pins on the R502 you only need to connect 4 in order to test the sensor - RXD, TXD, VCC, and GND. The next thing is to run the software.&lt;/p&gt;

&lt;h1&gt;
  
  
  Software gore
&lt;/h1&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/Mfc7MkEog_Y"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Once I connected everything together, I double checked which serial port I would be using and opened the TestDemo app you get by emailing the company. It lets you interface with the sensor, enrol fingerprints, query them, and notionally also view the image captured by the sensor, but...&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F07p0ucfg1khgwciyebpw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F07p0ucfg1khgwciyebpw.png" alt="Lovely glitch art"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This glitch art is lovely, isn't it? It also kinda looks like some fake unicode escaped that one twitter account. Still, the core function of the sensor did work - I was able to enrol a finger, match it again, and have it not find a match if a different finger is used. The Grow rep tells me this sort of image is normal, though, so maybe it's not meant to be like a photograph but some sort of internal representation used by the R502.&lt;/p&gt;

&lt;h1&gt;
  
  
  Road rage
&lt;/h1&gt;

&lt;p&gt;So now the "only" thing left to do is write some software for the F446RE board that works with this sensor.&lt;/p&gt;

&lt;p&gt;For which there are no Rust drivers.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;low voice&lt;/em&gt; we're gonna have to write a driver aren't we&lt;/p&gt;

&lt;p&gt;What &lt;em&gt;is&lt;/em&gt; there are some Arduino examples and a library which we can shamelessly pilfer from. It's okay because they were clearly shamelessly pilfered from Adafruit. &lt;a href="https://www.dropbox.com/sh/pznvlzx8qx5nfr3/AABpzhSyjqH0qWNYgMvxqAA9a?dl=0&amp;amp;file_subpath=%2FFingerprint-Sensor-arduino%2FFingerprint-Sensor-arduino%2Flibrary.properties&amp;amp;preview=Fingerprint-Sensor-arduino.zip" rel="noopener noreferrer"&gt;No, really.&lt;/a&gt; As well as source code for what might be the TestDemo application. That folder they send you is really full of stuff - there's apparently a .NET SDK, something for Android, two demo apps, and a copy of the aforementioned Adafruit fingerprint library, no doubt with some customisations of dubious quality.&lt;/p&gt;

&lt;p&gt;For a first stab at the driver, I want a really simple API. The fingerprint sensor itself is largely a request-response device, with a separate GPIO line for generating an interrupt when a finger is placed on the reader so it can also act as a button. Something like this would be neat:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;serial1&lt;/span&gt;&lt;span class="nf"&gt;.split&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;r502&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;R502&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rx&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;r502&lt;/span&gt;&lt;span class="nf"&gt;.send_command&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Command&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;ReadSysPara&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0xFFFFFFFF&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
                   &lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The design goal here is to make the driver be consistent with the datasheet, such as it is, which means using the same command and field names, unless they're absolutely ridiculous. I also want to use the &lt;code&gt;embedded-hal&lt;/code&gt; crate so that the whole thing can be used on any microcontroller that has Rust support when I'm done with it, and then it can be pilfered back by the manufacturer and stuck in another randomly named RAR file inside that Dropbox link. &lt;em&gt;Your (sic) welcome, HZ Grow!&lt;/em&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Up next
&lt;/h1&gt;

&lt;p&gt;Writing drivers is of course going to take a while with the sort of hectic schedule I seem to have found myself in the middle of right now, so I will write another post when I make the first release. Stay tuned for that! And for now - did you ever find yourself having to write drivers for a poorly documented part? Or designed an embedded driver API? I'd like to hear from you in the comments!&lt;/p&gt;

</description>
      <category>electronics</category>
      <category>rust</category>
    </item>
    <item>
      <title>Rusted brains: Running Rust firmware on a Cortex-M microcontroller</title>
      <dc:creator>Filip</dc:creator>
      <pubDate>Sun, 20 Oct 2019 08:19:15 +0000</pubDate>
      <link>https://dev.to/minkovsky/rusted-brains-running-rust-firmware-on-a-cortex-m-microcontroller-3had</link>
      <guid>https://dev.to/minkovsky/rusted-brains-running-rust-firmware-on-a-cortex-m-microcontroller-3had</guid>
      <description>&lt;p&gt;&lt;em&gt;This is the second post along my journey to creating a biometric password manager. For an overview of the project, &lt;a href="https://dev.to/minkovsky/designing-a-biometric-password-manager-26kn"&gt;read this post&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Because I want to program my final product in Rust - to get that memory safety and slightly more ergonomic bare-metal programming - I first need to learn how to use Rust on a microcontroller. To help me, I will be writing some relatively simple firmware programs for the Nucleo-F446RE board that I got for the whole project.&lt;/p&gt;

&lt;h1&gt;
  
  
  Prepare for boarding
&lt;/h1&gt;

&lt;p&gt;Before you embark on some crazy big project with two big unknowns, you should ask one question - will the two big unknowns work together? For Rust and ARM-based microcontrollers, the answer is largely yes. According to the Rust &lt;a href="https://forge.rust-lang.org/release/platform-support.html" rel="noopener noreferrer"&gt;platform support&lt;/a&gt; page, the &lt;code&gt;thumbv7em-none-eabi&lt;/code&gt; architecture is supported, albeit without the guarantee of Rust tests passing for each build. For now, that's good enough. The next question is, what is the best toolchain? Because ARM is such a broad platform, even if you focus on Cortex-M microcontrollers, there are loads of different options for compiling, flashing, and debugging your code. However most of them are expensive and designed for C/C++. The toolchain I'm using for this project is more of a pick-and-mix than a specialized IDE:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Visual Studio Code for editing, with the &lt;a href="https://marketplace.visualstudio.com/items?itemName=rust-lang.rust" rel="noopener noreferrer"&gt;Rust extension&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;GCC + GDB from ARM - mostly for the GDB. Make sure you get the &lt;a href="https://developer.arm.com/tools-and-software/open-source-software/developer-tools/gnu-toolchain/gnu-rm" rel="noopener noreferrer"&gt;Cortex-R/M version&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;thumbv7em-none-eabi&lt;/code&gt; target, installed using &lt;code&gt;rustup target add thumbv7em-none-eabi&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;OpenOCD for connecting GDB to the ST-Link debug hardware present on the Nucleo boards. You can get binaries from the &lt;a href="https://github.com/gnu-mcu-eclipse/openocd" rel="noopener noreferrer"&gt;GNU MCU Eclipse OpenOCD fork&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;The &lt;a href="https://marketplace.visualstudio.com/items?itemName=webfreak.debug" rel="noopener noreferrer"&gt;native debugger&lt;/a&gt; extension for debugging in VS Code&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When combined, these items provide a rather streamlined coding and debugging experience.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fo82luwxej8yxsdf5k74s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fo82luwxej8yxsdf5k74s.png" alt="Debugger paused in a random place"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Finding libraries
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=webfreak.debug" rel="noopener noreferrer"&gt;crates.io&lt;/a&gt; is the search engine for Rust packages. For hardware support, the libraries are mercifully named closely after what they do - &lt;code&gt;stm32f4xx-hal&lt;/code&gt; is a general-purpose abstraction layer crate for STM32F4xx microcontrollers, and &lt;code&gt;ssd1306&lt;/code&gt; is a driver for the SSD1306 display chip present in all these cheap 128x64 and 128x32 OLED displays you can get from the usual suspects. We will be using both of these later on.&lt;/p&gt;

&lt;h1&gt;
  
  
  Reading datasheets
&lt;/h1&gt;

&lt;p&gt;Because this is an embedded project, and we are not using Arduino or Mbed, we need to know at least a little about how the specific chip works. These can be found on the &lt;a href="https://www.st.com/en/microcontrollers-microprocessors/stm32f446re.html#resource" rel="noopener noreferrer"&gt;STM32F446RE page on ST.com&lt;/a&gt;. There is a number of documents on this page, but the most important are those two:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.st.com/resource/en/reference_manual/dm00135183.pdf" rel="noopener noreferrer"&gt;The STM32F446xx reference manual&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.st.com/resource/en/datasheet/stm32f446re.pdf" rel="noopener noreferrer"&gt;The STM32F446RE datasheet&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Combined, these two will give you a detailed picture of the peripherals and clock configuration options available on the MCU. On the other hand, they are vast, complex, and utterly dull - take the HAL option if you can. The important part is that these two should be treated as reference documents - don't read them cover to cover, grep through them to find what you need. Unless you have no idea how the chip works to begin with, in which case - ask yourself, what peripherals could you string together to achieve what you need?&lt;/p&gt;

&lt;h1&gt;
  
  
  What we will be making
&lt;/h1&gt;

&lt;p&gt;The project discussed in this post will be a simple stopwatch. The requirements are as follows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The stopwatch starts at 00:00.000&lt;/li&gt;
&lt;li&gt;Pressing the user button on the board starts the stopwatch&lt;/li&gt;
&lt;li&gt;Pressing the button again stops the stopwatch&lt;/li&gt;
&lt;li&gt;No lap or reset - to reset the stopwatch, an MCU reset will be required.&lt;/li&gt;
&lt;li&gt;Stopwatch resolution should be 1ms&lt;/li&gt;
&lt;li&gt;The screen should be updated at least every 100ms&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To meet them, the following peripherals will be used:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A hardware timer firing an interrupt every 1ms&lt;/li&gt;
&lt;li&gt;SysTick for providing slightly less accurate display update timing&lt;/li&gt;
&lt;li&gt;I2C for interfacing with the display&lt;/li&gt;
&lt;li&gt;A GPIO input to handle the button&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And because this is an embedded system, we should produce a state transition diagram to help us model the thing. For something as simple as this stopwatch, the value of creating one is minimal, but for more complex systems these become very useful for trying to figure out what states the system can reach, and to get an overview of how it behaves overall.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F64z79721bjkwqv019uex.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F64z79721bjkwqv019uex.png" alt="State diagram"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Iteration 1: A non-interactive stopwatch
&lt;/h1&gt;

&lt;p&gt;This is a stopwatch that is even dumber than the final product of this post - it will simply count how much time has passed since the last reset and display it on the screen. It doesn't mean there aren't quite a few things going on already!&lt;/p&gt;

&lt;p&gt;For one, we have to set up the internal clocks. I chose to run this example at a respectable core frequency of 48MHz (which is probably overkill but it's easy to set up like that).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Code is for example only and may not run.&lt;/span&gt;
&lt;span class="nd"&gt;#![no_std]&lt;/span&gt;
&lt;span class="nd"&gt;#![no_main]&lt;/span&gt;
&lt;span class="k"&gt;extern&lt;/span&gt; &lt;span class="k"&gt;crate&lt;/span&gt; &lt;span class="n"&gt;panic_semihosting&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;extern&lt;/span&gt; &lt;span class="k"&gt;crate&lt;/span&gt; &lt;span class="n"&gt;stm32f4xx_hal&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;hal&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="k"&gt;crate&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;hal&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;
    &lt;span class="nn"&gt;prelude&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nn"&gt;rcc&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;Rcc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Clocks&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="n"&gt;stm32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;cortex_m_rt&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nd"&gt;#[entry]&lt;/span&gt;
&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dp&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cp&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;stm32&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;Peripherals&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;take&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nn"&gt;cortex_m&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;Peripherals&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;take&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;rcc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dp&lt;/span&gt;&lt;span class="py"&gt;.RCC&lt;/span&gt;&lt;span class="nf"&gt;.constrain&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;clocks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;setup_clocks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rcc&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;loop&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;setup_clocks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rcc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Rcc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Clocks&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;rcc&lt;/span&gt;
        &lt;span class="py"&gt;.cfgr&lt;/span&gt;
        &lt;span class="nf"&gt;.hclk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;48&lt;/span&gt;&lt;span class="nf"&gt;.mhz&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
        &lt;span class="nf"&gt;.sysclk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;48&lt;/span&gt;&lt;span class="nf"&gt;.mhz&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
        &lt;span class="nf"&gt;.pclk1&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="nf"&gt;.mhz&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
        &lt;span class="nf"&gt;.pclk2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="nf"&gt;.mhz&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
        &lt;span class="nf"&gt;.freeze&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A few things of note already:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Notice that &lt;code&gt;#[entry]&lt;/code&gt; annotation on &lt;code&gt;main&lt;/code&gt;? It means that it will be the user program's direct entry point, called right after the RAM is initialised.&lt;/li&gt;
&lt;li&gt;The function &lt;em&gt;never returns&lt;/em&gt; - because there is nothing to return to. No operating system to clean up your program after it exits, no processes, nothing. When you're done, just enter an infinite loop.&lt;/li&gt;
&lt;li&gt;Following from (2) - there's no standard library and no allocator. Vector's won't work. Formatting strings requires external crates. It's a weird world.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Alright, that set up our first physical peripheral - the I2C display. To do that, I first have to initialise I2C. And to do that, I have to find two pins that can be used as I2C SCL and SDA lines. To do that, you need to open the datasheet of the specific MCU you have and do some text searches for "SCL" and "SDA" until you know which pins map to which I2C (there are several), and which alternate function the pins need to be in in order to connect to the I2C peripheral. I will be using pins 8 and 9 on port B because that's what strikes my fancy (and those pins are conveniently broken out and labelled as SCL and SDA on the Nucleo board). And they need to be in alternate function 4 because that's what the designers at STMicroelectronics thought will be suitable.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F7d7qqah60qamrxfm6h8s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F7d7qqah60qamrxfm6h8s.png" alt="An extract from the datasheet, showing that pins PB8 and PB9 connect to I2C1 in alternate mode 4."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Rust code, then, to take those GPIO pins and connect them to I2C1 is as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;gpiob&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dp&lt;/span&gt;&lt;span class="py"&gt;.GPIOB&lt;/span&gt;&lt;span class="nf"&gt;.split&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;i2c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;I2c&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;i2c1&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;dp&lt;/span&gt;&lt;span class="py"&gt;.I2C1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;gpiob&lt;/span&gt;&lt;span class="py"&gt;.pb8&lt;/span&gt;&lt;span class="nf"&gt;.into_alternate_af4&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="n"&gt;gpiob&lt;/span&gt;&lt;span class="py"&gt;.pb9&lt;/span&gt;&lt;span class="nf"&gt;.into_alternate_af4&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="nf"&gt;.khz&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="n"&gt;clocks&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, if you're slightly confused about which alternate function you need, Rust will helpfully tell you when you're wrong, and possibly even what you need to do to fix it. It's nice like that.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fohrzhqx7gkxgizmljy0l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fohrzhqx7gkxgizmljy0l.png" alt="Compiler error message also suggesting a different alternate function"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now that I have I2C up and running, it's time to set up the display itself. It's easy.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;disp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;GraphicsMode&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;SSD1306Builder&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.connect_i2c&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i2c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.into&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;disp&lt;/span&gt;&lt;span class="nf"&gt;.init&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;disp&lt;/span&gt;&lt;span class="nf"&gt;.flush&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The ssd1306 library contains some basic text and drawing functions, so you don't have to worry about writing individual pixels if you don't want to. I also have a simple, &lt;a href="https://github.com/FLamparski/stm32f446-oled-test" rel="noopener noreferrer"&gt;display-only example up on github&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;And then I had to set up the timer and spend quite a lot of time trying to figure out how exactly I was going to do it, and then even more time debugging why my interrupt service routines are being called back to back. So:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;You have to clear the interrupt flag on the peripheral&lt;/li&gt;
&lt;li&gt;At the time of writing, there was no way to do that with the HAL&lt;/li&gt;
&lt;li&gt;The actual code is very simple, but because it wasn't part of the HAL I had to first try it as &lt;code&gt;unsafe&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/stm32-rs/stm32f4xx-hal/pull/112" rel="noopener noreferrer"&gt;So I went ahead and opened a PR&lt;/a&gt;, which got accepted, merged, and released during writing. Thanks!&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Furthermore, the way to share data between the ISRs and the main thread is suitably arcane. However, it looks something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;core&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;cell&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;Cell&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;RefCell&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;core&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;ops&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;DerefMut&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="k"&gt;crate&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;hal&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;interrupt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;timer&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;Timer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Event&lt;/span&gt;&lt;span class="p"&gt;}};&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;cortex_m&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;interrupt&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;Mutex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;free&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;


&lt;span class="c1"&gt;// Outside of main():&lt;/span&gt;
&lt;span class="c1"&gt;// Set up two global, mutable, concurrency-safe containers. One is&lt;/span&gt;
&lt;span class="c1"&gt;// simple and holds our milliseconds value. The other holds the&lt;/span&gt;
&lt;span class="c1"&gt;// TIM2 reference which will be available after the setup has finished.&lt;/span&gt;
&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;ELAPSED_MS&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Mutex&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Cell&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;u32&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Mutex&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Cell&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0u32&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;TIMER_TIM2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Mutex&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;RefCell&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Timer&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nn"&gt;stm32&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;TIM2&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Mutex&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;RefCell&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;None&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;


&lt;span class="c1"&gt;// Inside main()&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;timer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Timer&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;tim2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dp&lt;/span&gt;&lt;span class="py"&gt;.TIM2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="nf"&gt;.khz&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;clocks&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;timer&lt;/span&gt;&lt;span class="nf"&gt;.listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Event&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;TimeOut&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// Free enters a critical section and this is necessary to access&lt;/span&gt;
&lt;span class="c1"&gt;// references held by a Mutex&amp;lt;T&amp;gt;.&lt;/span&gt;
&lt;span class="nf"&gt;free&lt;/span&gt;&lt;span class="p"&gt;(|&lt;/span&gt;&lt;span class="n"&gt;cs&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;TIMER_TIM2&lt;/span&gt;&lt;span class="nf"&gt;.borrow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.borrow_mut&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;timer&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="nn"&gt;stm32&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;NVIC&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;unpend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;stm32f4xx_hal&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;interrupt&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;TIM2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// Unmasking interrupts is inherently unsafe, but this one's fine&lt;/span&gt;
&lt;span class="k"&gt;unsafe&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nn"&gt;stm32&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;NVIC&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;unmask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;stm32f4xx_hal&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;interrupt&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;TIM2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;


&lt;span class="c1"&gt;// The ISR&lt;/span&gt;
&lt;span class="nd"&gt;#[interrupt]&lt;/span&gt;
&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;TIM2&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;free&lt;/span&gt;&lt;span class="p"&gt;(|&lt;/span&gt;&lt;span class="n"&gt;cs&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;ref&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;tim2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TIMER_TIM2&lt;/span&gt;&lt;span class="nf"&gt;.borrow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.borrow_mut&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.deref_mut&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// Clear the interrupt flag so that the ISR does not get retriggered&lt;/span&gt;
            &lt;span class="c1"&gt;// immediately.&lt;/span&gt;
            &lt;span class="n"&gt;tim2&lt;/span&gt;&lt;span class="nf"&gt;.clear_interrupt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Event&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;TimeOut&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;cell&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ELAPSED_MS&lt;/span&gt;&lt;span class="nf"&gt;.borrow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cs&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;val&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cell&lt;/span&gt;&lt;span class="nf"&gt;.get&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;cell&lt;/span&gt;&lt;span class="nf"&gt;.replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;val&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="c1"&gt;// Back inside main(), this time accessing the elapsed value&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;elapsed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;free&lt;/span&gt;&lt;span class="p"&gt;(|&lt;/span&gt;&lt;span class="n"&gt;cs&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="n"&gt;ELAPSED_MS&lt;/span&gt;&lt;span class="nf"&gt;.borrow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.get&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is quite a lot of work too, but that's what you get with concurrency and interrupts. First, we make sure that both the main thread and the ISR can access the elapsed milliseconds value safely - adding critical sections where necessary in order to prevent situations where the ISR is writing the value while the main thread is about to read it. A similar protection has to be applied to the &lt;code&gt;TIMER_TIM2&lt;/code&gt; variable which holds the timer state, since the ISR will need to access it to clear the interrupt. Then, the interrupt has to be enabled, and in two places - one is TIM2 itself, and the other is the NVIC - Nested Vector Interrupt Controller - which triggers ISRs. The ISR then needs to clear the condition that caused the interrupt somehow - otherwise the interrupt will get retriggered immediately. In this case, the &lt;code&gt;clear_interrupt&lt;/code&gt; method is called on TIM2.&lt;/p&gt;

&lt;p&gt;However at this point, we have our very basic stopwatch! You can see a snapshot of the code &lt;a href="https://github.com/FLamparski/rusted-brains-stopwatch/tree/fadf6e4d278fba78f4d258402450e7f9a5a8cb9c" rel="noopener noreferrer"&gt;on github&lt;/a&gt;. And a demo in this crappy gif!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fwptuveypii4x53c1671i.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fwptuveypii4x53c1671i.gif" alt="Gif showing the first stage in action"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Iteration 2: User input
&lt;/h1&gt;

&lt;p&gt;Alright, so with that first hurdle out of the way - what do we need to make the user button work? Well, we need three things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;We need to make the button pin an input. I use a pull-up resistor so that when the button is &lt;em&gt;not&lt;/em&gt; pressed, the line is high, and when it's pressed, it goes low.&lt;/li&gt;
&lt;li&gt;We need a way to tell that the button is being pressed. I used, for my sins, another interrupt.&lt;/li&gt;
&lt;li&gt;We need something to keep track of the state we're in - recall the transition diagram from above.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So, for #1:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;gpioc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dp&lt;/span&gt;&lt;span class="py"&gt;.GPIOC&lt;/span&gt;&lt;span class="nf"&gt;.split&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;board_btn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;gpioc&lt;/span&gt;&lt;span class="py"&gt;.pc13&lt;/span&gt;&lt;span class="nf"&gt;.into_pull_up_input&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;board_btn&lt;/span&gt;&lt;span class="nf"&gt;.make_interrupt_source&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;dp&lt;/span&gt;&lt;span class="py"&gt;.SYSCFG&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;board_btn&lt;/span&gt;&lt;span class="nf"&gt;.enable_interrupt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;dp&lt;/span&gt;&lt;span class="py"&gt;.EXTI&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;board_btn&lt;/span&gt;&lt;span class="nf"&gt;.trigger_on_edge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;dp&lt;/span&gt;&lt;span class="py"&gt;.EXTI&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;Edge&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;FALLING&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Very nice API. The HAL really shines here. I did however need to make the &lt;code&gt;Some(dp)&lt;/code&gt; a &lt;code&gt;Some(mut dp)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For #2, things get a little more sketchy. I needed to put both the EXTI and the button into global RefCells. Check out that &lt;code&gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&lt;/code&gt;!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;EXTI&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Mutex&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;RefCell&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nn"&gt;stm32&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;EXTI&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Mutex&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;RefCell&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;None&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;BUTTON&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Mutex&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;RefCell&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;PC13&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Input&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;PullUp&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Mutex&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;RefCell&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;None&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What is EXTI, anyway? It's a thing (peripheral?) on STM32 microcontrollers which handles EXternally Triggered Interrupts. Or at least I think that's how the initialism works. Honestly the configuration options for the EXTI confuse me to no end and I dread to think what I'd need to do if I have several buttons I want to put interrupts on - as they may go into the same interrupt. Not in this case - I only have one button, so I can just cheat and treat the whole interrupt as coming from that one button press.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;#[interrupt]&lt;/span&gt;
&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;EXTI15_10&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;free&lt;/span&gt;&lt;span class="p"&gt;(|&lt;/span&gt;&lt;span class="n"&gt;cs&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;btn_ref&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;BUTTON&lt;/span&gt;&lt;span class="nf"&gt;.borrow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.borrow_mut&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;exti_ref&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;EXTI&lt;/span&gt;&lt;span class="nf"&gt;.borrow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.borrow_mut&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;ref&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;btn&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;ref&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;exti&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;btn_ref&lt;/span&gt;&lt;span class="nf"&gt;.deref_mut&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;exti_ref&lt;/span&gt;&lt;span class="nf"&gt;.deref_mut&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;btn&lt;/span&gt;&lt;span class="nf"&gt;.clear_interrupt_pending_bit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;exti&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I still need to clear the interrupt, and then turn that button press into something useful. Which is what #3 is about. I created an enum for the state, and then added it to the global state. I needed to add the &lt;code&gt;Clone&lt;/code&gt; and &lt;code&gt;Copy&lt;/code&gt; traits in order to get it to work with a &lt;code&gt;Cell&lt;/code&gt;, but luckily those can just be derived automatically.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;#[derive(Clone,&lt;/span&gt; &lt;span class="nd"&gt;Copy)]&lt;/span&gt;
&lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="n"&gt;StopwatchState&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Ready&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Running&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Stopped&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;STATE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Mutex&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Cell&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;StopwatchState&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Mutex&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Cell&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;StopwatchState&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Ready&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, inside the button interrupt handler, I check the state, transition to the next one, and fire appropriate actions. In general this shouldn't be done in an ISR, however because my main thread purposefully sleeps 100ms every cycle, I needed a place where stopping the stopwatch would actually give the correct time. So into the ISR it goes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;STATE&lt;/span&gt;&lt;span class="nf"&gt;.borrow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.get&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="c1"&gt;// Run the state machine in an ISR - probably not something you want to do in most&lt;/span&gt;
&lt;span class="c1"&gt;// cases but this one only starts and stops TIM2 interrupts&lt;/span&gt;
&lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nn"&gt;StopwatchState&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Ready&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;stopwatch_start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cs&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;STATE&lt;/span&gt;&lt;span class="nf"&gt;.borrow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;StopwatchState&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Running&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nn"&gt;StopwatchState&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Running&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;stopwatch_stop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cs&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;STATE&lt;/span&gt;&lt;span class="nf"&gt;.borrow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;StopwatchState&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Stopped&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nn"&gt;StopwatchState&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Stopped&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And what are &lt;code&gt;stopwatch_start&lt;/code&gt; and &lt;code&gt;stopwatch_stop&lt;/code&gt;? You &lt;em&gt;can&lt;/em&gt; actually activate and deactivate TIM2 at will, however I felt like it would take too much effort for this example. Instead I do the next best thing and turn the interrupts on and off:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;stopwatch_start&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;'cs&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nv"&gt;'cs&lt;/span&gt; &lt;span class="n"&gt;CriticalSection&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;ELAPSED_MS&lt;/span&gt;&lt;span class="nf"&gt;.borrow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;unsafe&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nn"&gt;stm32&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;NVIC&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;unmask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;hal&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;interrupt&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;TIM2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;stopwatch_stop&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;'cs&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_cs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nv"&gt;'cs&lt;/span&gt; &lt;span class="n"&gt;CriticalSection&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nn"&gt;stm32&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;NVIC&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;mask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;hal&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;interrupt&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;TIM2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By the way, those &lt;code&gt;&amp;lt;'cs&amp;gt;&lt;/code&gt; things - they are lifetimes, and together with the &lt;code&gt;CriticalSection&lt;/code&gt; argument they ensure that this method can only be called from a context where interrupts are disabled.&lt;/p&gt;

&lt;p&gt;Finally, there are some changes to the display routine, but those are trivial compared to all the above. And hey - here's a slightly better gif showing the stopwatch in action. This one's hosted at imgur because dev.to broke while uploading :(.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FFSIYLWY.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FFSIYLWY.gif" alt="Stopwatch in action"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And you can also see the &lt;a href="https://github.com/FLamparski/rusted-brains-stopwatch/tree/e0c68a578909a2b18c373406ab0fc922933427c0" rel="noopener noreferrer"&gt;result on github&lt;/a&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  A moment to reflect
&lt;/h1&gt;

&lt;p&gt;Take a deep breath. Count to five. You will need it if you decide to implement something like this for your own microcontroller. One of the things that stuck with me from this whole experience is that Arduino and Mbed are about as high-level as you can get in the embedded world without bringing a whole garbage-collected runtime. The HAL I was using is somewhere in between that and writing bits to cryptically named registers (serious note to embedded systems designers - "rcc.apb2enr" is not a good name for anything). It's also definitely incomplete, as Rust is not an officially supported target language by ST. I'm already not looking forward to making USB work. On the other hand, the people involved in the Rust embedded community are amazing. The main hangout is the &lt;code&gt;#rust-embedded&lt;/code&gt; channel on Freenode. And you can make PRs to the HAL libraries directly which is not something that can be said about the STMCube-generated code.&lt;/p&gt;

&lt;p&gt;Oh yeah, I'll be getting to that - but this post is already too long.&lt;/p&gt;

&lt;h1&gt;
  
  
  Practical tips on how to not be stuck for too long
&lt;/h1&gt;

&lt;p&gt;Even if you're working with embedded devices in a language officially supported by their vendor, chances are someone has already had your problem and they may even have published something of a solution somewhere. The hardest part is finding those examples and this is where you might need to turn to unconventional sources. Google might not help you - but if you're trying to figure out how to use &lt;code&gt;input.make_interrupt_source()&lt;/code&gt; then maybe throwing the method name into github code search will bring something up. And in this case, it did.&lt;/p&gt;

&lt;p&gt;It's that or ask on the IRC because I doubt StackOverflow will be particularly forthcoming. What, actual physical things? Variable names with numbers in them? No Tony the Pony for you, my friend!&lt;/p&gt;

&lt;h1&gt;
  
  
  That's very cool but what's it got to do with biometrics?
&lt;/h1&gt;

&lt;p&gt;Nothing at all, except that I am planning to use the same platform for that actual product too. And it does mean I now know what to expect from the HAL (that I'll need to do some more PRs) and the underlying STM32 device (that I'll definitely want to make those PRs).&lt;/p&gt;

&lt;p&gt;Next one though is when I look at the fingerprint sensor. Stay tuned.&lt;/p&gt;

</description>
      <category>hardware</category>
      <category>rust</category>
    </item>
    <item>
      <title>My personal dev.to wishlist - what's yours?</title>
      <dc:creator>Filip</dc:creator>
      <pubDate>Tue, 17 Sep 2019 13:44:13 +0000</pubDate>
      <link>https://dev.to/minkovsky/my-personal-dev-to-wishlist-what-s-yours-3ae8</link>
      <guid>https://dev.to/minkovsky/my-personal-dev-to-wishlist-what-s-yours-3ae8</guid>
      <description>&lt;p&gt;dev.to is pretty cool - I like the relatively stripped-down editing interface and very quick reading experience thanks to the team's amazing work with service workers and other fun stuff.&lt;/p&gt;

&lt;p&gt;However no piece of software, or indeed no piece of design is ever perfect. I found a few places where dev.to could be made better - and this is the curated list.&lt;/p&gt;

&lt;h2&gt;
  
  
  The post editor
&lt;/h2&gt;

&lt;p&gt;I like how simple the post editor is, however in a few places where the "no simpler than it needs to be" principle doesn't quite hold up. For instance, saving drafts still triggers a full page reload that also takes you to the post draft - so if you want to save your post midway through writing or editing it, you will have to click 'Edit' again and find where you were. This is actually at odds with the Preview button which renders the post preview in place, but doesn't actually save your draft.&lt;/p&gt;

&lt;p&gt;Wish: &lt;strong&gt;Save Draft should not reload the editor&lt;/strong&gt; and definitely should not take me out of the editor.&lt;/p&gt;

&lt;p&gt;I also like the simple image uploader, however in longer posts that can be a little cumbersome as I have to scroll up to find the image upload button.&lt;/p&gt;

&lt;p&gt;Wish: &lt;strong&gt;The image upload button should be floated next to the editor&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;While I'm in the image upload screen - I sometimes have to use dodgy connections and would like to know the progress of my upload. This is generally possible with XMLHttpRequest, and would help me judge whether the upload is just slow or if something's broken.&lt;/p&gt;

&lt;p&gt;Wish: &lt;strong&gt;Have a progress bar there!&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The difficulty system
&lt;/h2&gt;

&lt;p&gt;I like the idea of the difficulty system - perhaps you want to see fewer beginner articles and more in-depth pieces on the pros and cons of particular patterns - but as it stands it seems a bit limited and not very well documented. It's not intuitive when setting the difficulty level for a post - you have to go into the Manage Post menu - and definitely not intuitive when setting the difficulty level for your feed - you have to go into Settings &amp;gt; Misc to set your desired level. I don't think many people change it.&lt;/p&gt;

&lt;p&gt;Wish: &lt;strong&gt;Make the difficulty setting more prominent for reading&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Ideally I would like to see the difficulty slider right beside my feed, and possibly allow me to set specific difficulties for each tag - I might be quite experienced in Java or Vue, but not so much in Rust or hardware development.&lt;/p&gt;

&lt;p&gt;Wish: &lt;strong&gt;Indicate what you can do in the Manage Post page,&lt;/strong&gt; or add some of the widgets from it to the bottom of the post.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tags
&lt;/h2&gt;

&lt;p&gt;Wish: &lt;strong&gt;Suggested tags for draft posts&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This one's self-explanatory - suggest some tags for your post as you write it. Cool machine learning project for whoever ends up implementing it!&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;I hope these suggestions end up reaching the dev.to team - after all, we'll all benefit from a better platform. I also don't think they are must haves, but would definitely be super nice to have.&lt;/p&gt;

&lt;p&gt;What about you? What features would you really like to see on dev.to?&lt;/p&gt;

</description>
      <category>meta</category>
      <category>discuss</category>
    </item>
    <item>
      <title>Designing a biometric password manager</title>
      <dc:creator>Filip</dc:creator>
      <pubDate>Sun, 08 Sep 2019 13:05:33 +0000</pubDate>
      <link>https://dev.to/minkovsky/designing-a-biometric-password-manager-26kn</link>
      <guid>https://dev.to/minkovsky/designing-a-biometric-password-manager-26kn</guid>
      <description>&lt;p&gt;Password prompts. Password prompts everywhere. Between logging into my work VPN every day and sudo access on my personal machine, I have to enter multiple passwords multiple times every day. I'm a lousy typist, which means I'll often mistype the passwords in question (especially on those godawful butterfly keyboards) and frustrate myself to no end.&lt;/p&gt;

&lt;p&gt;Compare that with my phone, where in most cases I can use my fingerprint to log in and even authorise transactions. Entering passwords or PINs is a back-up option rather than the default. Convenient and secure authentication, based on something you &lt;em&gt;are&lt;/em&gt; rather than something you &lt;em&gt;know&lt;/em&gt;. Wouldn't it be brilliant if I could do this on every device I use?&lt;/p&gt;

&lt;p&gt;Yes, yes it would be. And here's how I want to do it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The goal
&lt;/h2&gt;

&lt;p&gt;I am taking a more design-led approach in this section, where I outline what task I want to achieve and then flesh out how I want to achieve it. And what I want can be summarised in a sentence:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I want to be able to enter my passwords on any computer with a touch of my finger, but still be secure.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I decided that this can be broken up into three distinct sub-goals:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Security:&lt;/strong&gt; The password should only be unlocked for the right person. Someone who got hold of the device should not be able to retrieve the passwords without significant effort.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Compatibility:&lt;/strong&gt; The device should be able to enter passwords on any reasonable computer without special software or drivers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Simplicity:&lt;/strong&gt; Ideally I'd only store a handful of passwords on the device, I want to be able to quickly scroll through the list and confirm my fingerprint to enter the password.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I will discuss the implementation details in more depth later, but for now I would like to draw your attention to the inherent conflict between the security goal and the usability goals (2 and 3). This is the inherent challenge in designing security systems, as increasing a thing's usability often also increases its attack surface. I think the biometrics approach is better than secure entry keypads or miniature card readers such as the Mooltipass for this tradeoff - a secure keypad still requires you to enter a code which can be guessed, and the Mooltipass cards can be lost. A fingerprint-based system provides a reasonable level of security and convenience - a third party can't get to your passwords without cracking open the device and spending some time with a debug probe, and if you lose your finger you probably have bigger immediate problems than keeping your data secure.&lt;/p&gt;

&lt;h2&gt;
  
  
  Motivation
&lt;/h2&gt;

&lt;p&gt;The key reason for doing this project is that I'm tired of mistyping my password which we also have to change every three months. My ideal device would be something that sent that particular password down USB with a simple button press, however that's not super secure! As mentioned above I think fingerprint authentication is ultimately more secure &lt;em&gt;in the average case&lt;/em&gt; than a typed-in password, because once you're verifying who you are rather than what you know, you can make the "password" as long and secure as you can because no human will ever see it. It also makes sense to go with a keychain approach for storing a handful of passwords.&lt;/p&gt;

&lt;p&gt;The other big reason for me starting this is that I want to learn something new. To that end I decided that I want to go back to ARM microcontrollers which I know and hate from university, but on slightly different terms this time - using Rust instead of C/C++, and using a microcontroller much more popular in the maker circles. I settled on the STM32 family, and more concretely on the STM32F446RE part simply because there's a development board for it that costs £13 and it's a powerful little chip.&lt;/p&gt;

&lt;p&gt;I chose Rust because I think embedded projects are better suited to learning this systems programming language. For most projects I do on desktop, I usually prefer using something more high-level and portable, such as Python or JavaScript. However I will try out desktop Rust when writing the configuration program.&lt;/p&gt;

&lt;h2&gt;
  
  
  The device
&lt;/h2&gt;

&lt;p&gt;Given the overall goals of this project, I decided on a USB dongle form factor. With USB, I can register the device as a keyboard and send the password keystroke by keystroke to the host. USB keyboards work on any system that has USB - so no extra drivers are necessary. The configuration interface can be a virtual serial port, which also should not require extra drivers. Finally, USB can be used for firmware updates.&lt;/p&gt;

&lt;p&gt;I decided to use a scroll wheel control to select the password to enter. The scroll wheel should have nice detents and should be able to rotate freely. I want the interface to scale well to 10s of passwords stored, and so buttons are right out. Notably, the Mooltipass also uses this approach.&lt;/p&gt;

&lt;p&gt;An OLED display will be used to show the user which password they have selected, as well as other information such as the status of the device if in configuration or DFU modes. I am also considering adding a Neopixel-type RGB LED for extra indication - perhaps I'll want to let the user associate each password with a colour.&lt;/p&gt;

&lt;p&gt;Further on the user interaction - because the fingerprint reader I have in mind for this can also act as a button, I decided that placing one's finger on the reader will confirm the selection and acquire the fingerprint and try to enter the password. A fingerprint confirmation will also be necessary for entering configuration mode and updating firmware. For extra security, I may add a feature which erases the password list if no successful authentication is made a certain number of times, with exponential backoff to prevent the user accidentally erasing their device.&lt;/p&gt;

&lt;p&gt;As for the encryption - I will need to come up with a reasonable scheme that would require an attacker to have physical possession of the device and at least some lab equipment to recover the password list. At the moment, I am thinking of combining the main microcontroller's unique ID and the user's fingerprint template as a symmetric encryption key.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fgctqz81j461l9hxxbr63.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fgctqz81j461l9hxxbr63.png" alt="Combining the user's fingerprint and the microcontroller's unique ID to create an encryption key"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I would have liked the fingerprint module itself to be able to generate a cryptographic value based on the user's fingerprint and then only release it if the authorised finger was detected - however this functionality is not available on the module I have chosen.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementation
&lt;/h2&gt;

&lt;p&gt;While I mostly chose Rust because I wanted to learn the language, I also know that it is a much safer language than C/C++ when it comes to memory access. Unless code is explicitly marked as unsafe, Rust will attempt to validate memory access at compile time, catching entire classes of bugs such as use-after-free or buffer overflows.&lt;/p&gt;

&lt;p&gt;I chose this particular STM32 part for several reasons. One, the cheap development board - the chip itself is about $6 so I would not be surprised if ST sold the things at cost price. Two, it's a pretty powerful part. And three, the STM32 family is popular with makers and hobbyist electronics people. As a consequence, there is lots of documentation, both from the vendor and the community. There are also Rust libraries for accessing the chip's peripherals in a relatively nice way.&lt;/p&gt;

&lt;p&gt;The fingerprint sensor was the hardest thing to find. While there are some Arduino-friendly devices sold by Adafruit (with clones from the usual suspects), they are rather big. Good for safes or doors, not good for portable devices. It did take some research time to find a part which was small and had a reasonably convenient interface. Predictably, it comes from a relatively unknown Chinese supplier, with many clones on the market. It is the &lt;a href="http://en.hzgrow.com/product/113.html" rel="noopener noreferrer"&gt;Grow R502&lt;/a&gt;, a module roughly the size of a 10p coin, with a UART interface. It can be ordered through AliExpress or Alibaba and comes relatively quickly. However, documentation in English is a bit lacking, and I could not find demo software to go with the module. I have emailed the vendor.&lt;/p&gt;

&lt;p&gt;The Grow R502 does not do everything I'd like it to do. For one, I'd love a single piece of functionality to be added to it: when the fingerprint is first enrolled, a token is generated, which is then only returned on successful matches against that fingerprint. This would simplify my design and make the whole system more secure, as I could use that as the encryption key. Bit of trivia: the fingerprint module has a GD32 MCU, an STM32 clone from GigaDevice.&lt;/p&gt;

&lt;p&gt;Given the features I want to include on the device and the constraints imposed by the parts I want to use, I decided to lay out the system like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F6xml3o3seq1mngqcuhii.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F6xml3o3seq1mngqcuhii.png" alt="Block diagram of the device"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The plan
&lt;/h2&gt;

&lt;p&gt;I will blog my way through this project, and possibly even make some demonstration videos along the way. I want to share notes from the development process with you, and I want to get some feedback on my plans. For now I am planning 11 blog posts.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Rust on the STM32F446RE Nucleo board - where I skip the blinky and go straight for getting something on the OLED display. I will also try to cover initialisation code generation in STM Cube IDE and whether it's a good idea to do first before translating into Rust.&lt;/li&gt;
&lt;li&gt;R502 exploration - where I try to find some existing software to test the module with and start writing a test STM32 program in Rust.&lt;/li&gt;
&lt;li&gt;USB keyboard - where I try to create a simple USB Rubber Ducky clone from SPI flash&lt;/li&gt;
&lt;li&gt;Basic prototype - where I build a device which sends one password stored in plain text when the fingerprint is correct&lt;/li&gt;
&lt;li&gt;Configuring passwords - where I create a composite USB device which has both a USB keyboard and a virtual serial port, and then use that to configure the password list&lt;/li&gt;
&lt;li&gt;UI prototype - where I connect the encoder and use it to navigate up and down the list of passwords, and talk a bit more about the overall UX of the thing&lt;/li&gt;
&lt;li&gt;Encryption - where I start integrating encryption routines into the firmware&lt;/li&gt;
&lt;li&gt;Full breadboard prototype - where I put everything together on a breadboard and talk through what's going on&lt;/li&gt;
&lt;li&gt;Electronic engineering - where I create a full schematic and design a PCB for the USB dongle&lt;/li&gt;
&lt;li&gt;The one where I put together the device on the PCB and hope that it works&lt;/li&gt;
&lt;li&gt;Reflections - where I spend some time talking about what I learned through this project and where I want to take it; with some usage commentary if the thing even works after I put it together&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;There's a lot on here and I hope to be able to do it all in reasonable time. For now I'd like to invite you all to follow this series and send in some comments on this project. Until next time!&lt;/p&gt;

</description>
      <category>design</category>
      <category>hardware</category>
      <category>security</category>
    </item>
    <item>
      <title>An Air Quality Update</title>
      <dc:creator>Filip</dc:creator>
      <pubDate>Fri, 21 Jun 2019 21:09:18 +0000</pubDate>
      <link>https://dev.to/minkovsky/an-air-quality-update-47j1</link>
      <guid>https://dev.to/minkovsky/an-air-quality-update-47j1</guid>
      <description>&lt;p&gt;A few weeks back I showed you &lt;a href="https://dev.to/minkovsky/working-on-my-iot-air-quality-monitoring-setup-40a5"&gt;my air quality monitoring device&lt;/a&gt;. I've since tweaked it, ran into problems and fixed them, and added some functionality. I also gathered some interesting data using the sensor itself - and it might be a little surprising.&lt;/p&gt;

&lt;p&gt;Finally, I posted the source code for the program running on the ESP32 as a &lt;a href="https://gist.github.com/FLamparski/93af1ac4f49c2fde550a36f14a0d9446" rel="noopener noreferrer"&gt;GitHub Gist&lt;/a&gt; - there's more on it below.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding some stability, of the mechanical kind
&lt;/h2&gt;

&lt;p&gt;To prevent the components from rattling around too much inside the enclosure, I designed a backing board in Fusion 360 and used the laser cutter at the South London Makerspace. This is just a piece of plywood with some holes, allowing me to secure the SDS-011 sensor and the ESP32 breakout board with some nylon screws. I went through a couple of design iterations of these backing boards, trying to fit everything inside the enclosure I had. The final one (highlighted in the screenshot below) packs the components in about 2/3rd of the enclosure, allowing the battery holder to stand on its side as was the case when everything was wibble-wobbling around in the box.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fsauohsmzudnuoyh9agni.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fsauohsmzudnuoyh9agni.png" alt="Fusion 360 screenshot"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Needless to say, the wibble-wobbling was reduced and the pieces are now much easier to work with if I ever need to take them out of the box.&lt;/p&gt;

&lt;h2&gt;
  
  
  Batteries included
&lt;/h2&gt;

&lt;p&gt;Ah yes, the battery compartment - which I previously used as just a power supply - is now occupied by actual batteries. I sourced those from Poundland power banks, at about £2 per ~2000mAh cells. They are pretty easy to get hold of, although I'm pretty sure one of these came pre-shorted - it started getting a bit smoky whenever I put any load on it. PSA: &lt;strong&gt;If your batteries get smoky, don't use them!&lt;/strong&gt; Lithium batteries are usually quite safe, but if you short them out, they can catch fire and/or explode, so it's best to not mess about with them.&lt;/p&gt;

&lt;p&gt;I also wanted to be able to measure the level of charge in the batteries. The problem I needed to overcome was that, at full charge, a Li-ion battery (or a couple of them in parallel) has a potential of up to 4.3V - which is a full volt higher than the 3.3V level that the ESP32 operates at. I needed to get the battery voltage coming into an analogue pin of the ESP32 to top out at or below 3.3v in order to measure it without damaging the microcontroller. I can drop the voltage by using a voltage divider. I built an example of the circuit &lt;a href="http://tinyurl.com/yxp8ku24" rel="noopener noreferrer"&gt;here&lt;/a&gt; in a simulator where you can play around with the battery voltage and see how the divider circuit:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Ffl0thmsepdqddn7gvsf6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Ffl0thmsepdqddn7gvsf6.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the code, I define one of the pins as an analogue pin, and configure the ADC using some ESP32/Arduino functions. I can then read from this pin using the normal &lt;code&gt;analogRead&lt;/code&gt; function. However, when measuring an analogue value, noise and error creeps in pretty fast - so I decided to measure the battery level several times and average the results:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// setup:&lt;/span&gt;
  &lt;span class="n"&gt;adcAttachPin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BATTERY_PIN&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;analogSetPinAttenuation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BATTERY_PIN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ADC_11db&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// measure:&lt;/span&gt;
&lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="nf"&gt;get_battery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;n_samples&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;sum_reading&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;n_samples&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;batteryAdcCount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;analogRead&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BATTERY_PIN&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// Battery voltage is given by the ADC as x/4096 * 3.3 for the voltage divider&lt;/span&gt;
    &lt;span class="c1"&gt;// and * 2 for the full voltage.&lt;/span&gt;
    &lt;span class="n"&gt;sum_reading&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;batteryAdcCount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mf"&gt;4096.0&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;3.3&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;sum_reading&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;n_samples&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There is a certain dialectic when it comes to measuring battery levels. The ADC is not magic, and it requires some current to flow while the measurement is taking place. However, you don't want that current to be too high, otherwise you'd be draining the battery rather quickly. This is where the choice of resistor value in your voltage divider is important - too low and you'll burn through the battery, too high and you might not be able to measure it accurately. I picked 36kOhm for mine at pretty much random, but it's high enough that the total current flowing through the divider is about 60uA. Compared to the SDS-011 and ESP32 running at full tilt (peaking at 270mA), it's not very much. If I was crazy about power efficiency, I would experiment more with higher resistor values, and possibly add a transistor to the circuit, so that I can turn off the current if I'm not currently measuring voltage.&lt;/p&gt;

&lt;p&gt;In the end, the battery measurement works pretty well, and the battery itself seems to last at least two full days:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F11lrqcaz5yuh8aknr4mh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F11lrqcaz5yuh8aknr4mh.png" alt="Voltage graph showing the battery discharging"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  On plugging in stuff wrong
&lt;/h2&gt;

&lt;p&gt;I semi-broke an ESP32 board during the development of this by accidentally plugging it with one pin shifted down in the socket, causing 5V to be applied to its ground pin. Somehow it survived that, but not unscathed - that particular board will no longer wake up from any form of CPU sleep without an external reset, but does otherwise work. Oops. Luckily, I had a spare that worked.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to sleep right
&lt;/h2&gt;

&lt;p&gt;Turns out I was using the sleep modes of the ESP32 wrong. Initially I thought of ESP's light sleep as a special case of the Arduino &lt;code&gt;delay()&lt;/code&gt; function, except where the processor core powers down for the duration of the delay. It's kind of like that, but with one caveat: this also suspends handling of the WiFi connection, causing some instability.&lt;/p&gt;

&lt;p&gt;Furthermore, the ESP also has a deep sleep mode which saves more power, but it actually resets the microcontroller when waking up. I decided that I should use this mode, though - I don't need WiFi between measurements, so I might as well turn everything off. It also simplified the code slightly - I could put everything in &lt;code&gt;setup()&lt;/code&gt; and rely on the wakeup resets.&lt;/p&gt;

&lt;p&gt;This also made the whole system more stable, and allowed me to more easily add retry features - in the version of the gist current at the time of writing (&lt;a href="https://gist.github.com/FLamparski/93af1ac4f49c2fde550a36f14a0d9446/43b1ba31f619c24c3319f1d5bf3f93049d5d6633" rel="noopener noreferrer"&gt;permalink&lt;/a&gt;), if I can't connect to WiFi within a few seconds, I turn everything off and sleep for another 15 seconds. Now, this is still a bit buggy - there can be long stretches when the ESP can't connect at all - so I will need to look at the timeout and other causes of connectivity issues. However, the device will &lt;em&gt;eventually&lt;/em&gt; reconnect without me having to go and reset it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fcfvsj91nirc2j9hh152x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fcfvsj91nirc2j9hh152x.png" alt="Nope, didn't need that data today..."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Some actual observations
&lt;/h2&gt;

&lt;p&gt;PM2.5 of the carbon species usually comes from burning stuff, right? Well, how about toast? Or indeed, just cooking lunch?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fip1t8u9ve5wugi0z0rmm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fip1t8u9ve5wugi0z0rmm.png" alt="Lunchtime spike"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Most nights I see a significant increase in presence of PM2.5 species over the night, even though traffic outside my apartment is very light - could this be due to some scattered light affecting the SDS-011 sensor? Or temperatures going down causing dust kicked up high into the atmosphere during the day to settle down?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fdyf08orhdvr3xc3mwpzw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fdyf08orhdvr3xc3mwpzw.png" alt="High measurements overnight"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  For those playing along
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://gist.github.com/FLamparski/93af1ac4f49c2fde550a36f14a0d9446" rel="noopener noreferrer"&gt;gist&lt;/a&gt; contains the Arduino sketch running on the ESP32 inside the device. If you want to run it on your own network, you will need to change the WiFi credentials near the top of the file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="cp"&gt;#define WIFI_SSID "CHANGE ME"
#define WIFI_PASS "CHANGE ME"
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  As always, there's more
&lt;/h2&gt;

&lt;p&gt;The most obvious next step will be to figure out why the ESP is not connecting to WiFi - maybe it's the internal antenna not being very good, or the timeout being too short.&lt;/p&gt;

&lt;p&gt;Further next steps would be figuring out how to control my measurements for things like cooking - if I am going to use this device to nag my local authority, I should be reasonably sure that the data I get out of it is down to actual traffic related pollution, and not me burning toast. Finally, I should write up that IoT server setup, shouldn't I...&lt;/p&gt;

&lt;p&gt;See you then - and as always, let me know what you think about this project. I'm still liking it a lot.&lt;/p&gt;

</description>
      <category>arduino</category>
      <category>hardware</category>
      <category>iot</category>
      <category>environment</category>
    </item>
    <item>
      <title>Working on my IoT air quality monitoring setup</title>
      <dc:creator>Filip</dc:creator>
      <pubDate>Wed, 05 Jun 2019 12:27:13 +0000</pubDate>
      <link>https://dev.to/minkovsky/working-on-my-iot-air-quality-monitoring-setup-40a5</link>
      <guid>https://dev.to/minkovsky/working-on-my-iot-air-quality-monitoring-setup-40a5</guid>
      <description>&lt;p&gt;Air quality - much has been said about it in the recent years. Research is coming out saying that breathing polluted air &lt;a href="https://www.who.int/airpollution/ambient/health-impacts/en/" rel="noopener noreferrer"&gt;contributes to risk of asthma attacks, allergies, and other health issues&lt;/a&gt;. One of the most common pollutants is particulate matter - very fine carbon dust emitted from diesel and some petrol engines. If you live near a high traffic road, you are likely breathing some in right now - and it might be giving you black lung in slow motion.&lt;/p&gt;

&lt;p&gt;On an unrelated note, here's a view from my window:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqf07ri2ro1sp1r8nsl5h.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqf07ri2ro1sp1r8nsl5h.jpg" alt="Look at all those lorries"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I would like to know how much of this dust - more technically, particulate matter, is in the air outside my window throughout the day. The end goal is to use this information to set up alerts if and when pollution levels exceed government targets, and use this to keep my local authority accountable.&lt;/p&gt;

&lt;p&gt;So, how do I start?&lt;/p&gt;

&lt;h1&gt;
  
  
  The SDS-011 Sensor
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft8smi5uh4w3bubrrmugt.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft8smi5uh4w3bubrrmugt.jpg" alt="Product shot of the SDS-011, showing the included USB adapter"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The heart of the operation is the nova SDS-011 sensor. It works by blowing air past a laser beam and looking for scattered reflections. It can then isolate scattering caused by specific types of particulate matter - PM2.5 and PM10 - and measure how much of it is in the air, giving its reading in micrograms per cubic metre.&lt;/p&gt;

&lt;p&gt;The sensors are pretty cheap - less than £20 on &lt;a href="https://www.aliexpress.com/wholesale?catId=0&amp;amp;initiative_id=SB_20190605020754&amp;amp;SearchText=sds011" rel="noopener noreferrer"&gt;AliExpress&lt;/a&gt;, with roughly similar prices on Banggood and eBay if shipped from China. Some also throw in a USB adapter for the sensor's serial (UART) interface. Neat! What's not neat is the up to 3 week shipping time, but that's just par for the course when ordering from China.&lt;/p&gt;

&lt;p&gt;By default, the sensor will output its measurements continuously every second. This can be useful if you want to test that it is working with the USB adapter. In fact, I wrote some Python programs that rely on this mode to display the sensor's readings. The simplest one is here in its entirety:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;If you also want to plot this data immediately, here's one with matplotlib: &lt;a href="https://gist.github.com/FLamparski/c9d09153183f77c69f8c8ca7ca22e7ab#file-plot-py" rel="noopener noreferrer"&gt;https://gist.github.com/FLamparski/c9d09153183f77c69f8c8ca7ca22e7ab#file-plot-py&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Great, now I have some data coming in! But I don't want the sensor to be connected to my computer, I'd like to connect it to an ESP32 microcontroller (I have some of these around which I might as well use).&lt;/p&gt;

&lt;h1&gt;
  
  
  The ESP32
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F53wxo2pmpflnv83j6jxm.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F53wxo2pmpflnv83j6jxm.jpg" alt="Close-up shot of an ESP32 development board"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The ESP32 is a crazy powerful Wi-Fi microcontroller - with the most common variant boasting a dual-core 240MHz CPU, 2 or 4 MiB of flash memory, and 520 KiB of RAM. It's also largely Arduino compatible - and the board library is developed by Espressif as a first-party product.&lt;/p&gt;

&lt;p&gt;It's also really cheap for all that power: you can get it for under £7 with Amazon Prime delivery (or look at the &lt;a href="https://smile.amazon.co.uk/s?k=esp32" rel="noopener noreferrer"&gt;amazon search&lt;/a&gt; for variants with OLED displays, camera attachments, LiPo chargers, etc on board).&lt;/p&gt;

&lt;h1&gt;
  
  
  The one where I almost burn myself with a soldering iron
&lt;/h1&gt;

&lt;p&gt;To connect the SDS-011 to the ESP32, I am using a perfboard - a PCB with a grid of holes in it, allowing you to quickly assemble your circuit without having to design and make a dedicated PCB. I also needed a 5-pin JST connector to take in the cable supplied with the USB adapter, and I also decided to make a socket for my ESP32 from two pieces of female dupont pin headers. This makes the ESP board stand off from the perfboard, giving me some space to work with underneath it, and also allows me to easily recover the ESP if I ever tear this project down.&lt;/p&gt;

&lt;p&gt;I then soldered some jumper wires on the back side of the board for power and to connect the TX pin of the SDS-011 to the RX2 pin of the ESP, and RX of the SDS-011 to the TX2 pin of the ESP. I made an image in gimp to help me route the wires correctly:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff2inykvtwrnr4muy69v8.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff2inykvtwrnr4muy69v8.jpg" alt="Perfboard routing guide"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;TX2 and RX2 on the ESP board are available as Serial2 in Arduino sketches. The observant among you may have noticed an extra JST connector exposing the I2C bus - this is for later.&lt;/p&gt;

&lt;h1&gt;
  
  
  Lol embedded programming
&lt;/h1&gt;

&lt;p&gt;This is not strictly speaking a tutorial, so I'll gloss over how to actually set up ESP32 boards in Arduino - this is documented in the &lt;a href="https://github.com/espressif/arduino-esp32/blob/master/docs/arduino-ide/boards_manager.md" rel="noopener noreferrer"&gt;arduino-esp32 project itself&lt;/a&gt;. The one thing you should note is that this only works with the &lt;strong&gt;desktop&lt;/strong&gt; Arduino IDE, and only if you install the IDE through the MSI installer and &lt;strong&gt;not from Windows Store&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A very simple sketch, which simply formats the incoming data as text and outputs it to the debug serial connection, is also quite short:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;So after verifying that this works, I wanted to hunt down some more complete documentation of the SDS-011. The reason for that is that you are not supposed to run the sensor continuously for a long period of time, as it will allow dust of all kinds to accumulate inside, causing it to lose accuracy over time. The manufacturer states that the expected useful lifetime is 8000 hours of operation - so if you don't operate it all the time, you'll get more chooch for your cash. The other reason is that I'd like to run the thing off of lithium batteries at some point, and the SDS-011 uses about a Watt of power when it's running, what with its laser and fan.&lt;/p&gt;

&lt;p&gt;There is a good Arduino library for the SDS family of sensors, which supports the complete protocol they use and presents a nice API for it: &lt;a href="https://github.com/lewapek/sds-dust-sensors-arduino-library" rel="noopener noreferrer"&gt;https://github.com/lewapek/sds-dust-sensors-arduino-library&lt;/a&gt;. The protocol documentation is &lt;em&gt;an absolute pig&lt;/em&gt; to find though, and I ended up emailing the manufacturer for it. The v1.5 spec is here: &lt;a href="https://drive.google.com/open?id=0B11dfFBNOar5NHNTUVlvcFJPWUctMWZzYWRZU0RiMWxheG04" rel="noopener noreferrer"&gt;https://drive.google.com/open?id=0B11dfFBNOar5NHNTUVlvcFJPWUctMWZzYWRZU0RiMWxheG04&lt;/a&gt;. If you're implementing your own interface to the sensor, make sure to get the checksum right, otherwise it won't process your commands, and it won't tell you why either. I wrote this before I became aware of that library:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;When it's mostly wired up, it looks like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F949dnu5tuothwl0evp4a.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F949dnu5tuothwl0evp4a.jpg" alt="Photo of the assembled setup in an enclosure box"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I'm powering it through a charge port I hot glued onto the battery compartment.&lt;/p&gt;

&lt;p&gt;At this point, I can start sending all this data to a server. I decided to use an old laptop that I had in my cupboard.&lt;/p&gt;

&lt;p&gt;There is a big gotcha when using the SDS library with the ESP32: It will fail to compile due to &lt;code&gt;SoftwareSerial&lt;/code&gt; not being available in the ESP32 Arduino board package. To remedy that, I downloaded the master branch of the library and put it in my Arduino libraries folder, and then deleted all the code that relied on &lt;code&gt;SoftwareSerial&lt;/code&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Can't spell idiot without IoT
&lt;/h1&gt;

&lt;p&gt;The server is running &lt;a href="https://www.influxdata.com/" rel="noopener noreferrer"&gt;InfluxDB&lt;/a&gt;, &lt;a href="https://nodered.org/" rel="noopener noreferrer"&gt;Node-RED&lt;/a&gt;, and &lt;a href="https://grafana.com/" rel="noopener noreferrer"&gt;Grafana&lt;/a&gt; inside Docker. It's also running &lt;a href="https://mosquitto.org/" rel="noopener noreferrer"&gt;Mosquitto&lt;/a&gt; as the MQTT broker, and &lt;a href="https://github.com/portainer/portainer" rel="noopener noreferrer"&gt;Portainer&lt;/a&gt; to give me a nice GUI to the Docker host. And of course, I'm also monitoring the server itself with &lt;a href="https://www.influxdata.com/time-series-platform/telegraf/" rel="noopener noreferrer"&gt;Telegraf&lt;/a&gt; running directly on the host. This setup is largely based on &lt;a href="https://www.youtube.com/channel/UCu7_D0o48KbfhpEohoP7YSQ" rel="noopener noreferrer"&gt;Andreas Speiss&lt;/a&gt;'s Raspberry Pi configuration from his videos, however you can mix and match different components - for instance, a friend of mine is using Prometheus instead to monitor her &lt;a href="https://slides.com/daisyt/observability-kitchen" rel="noopener noreferrer"&gt;sourdough cultures&lt;/a&gt; on a RPi.&lt;/p&gt;

&lt;p&gt;This took a while to set up, not least because I was following several different sources at the same time, many of them written for the RPi and not an x86 machine. However it is mostly working now - and when I'm done tweaking the setup, I will release it on github as a docker-compose application.&lt;/p&gt;

&lt;p&gt;But finally, I was able to connect to the MQTT broker from my ESP32 and start sending data...&lt;/p&gt;

&lt;p&gt;Haha, no. The PubSubClient library I use to connect to the server has a default timeout of 15 seconds, which is way too low for my use case - given that I take about 15 seconds to wake up the sensor and then gather measurement data to average out, and spend another 15 with the CPU sleeping to reduce power use. So I just went into the library and changed the keepalive interval to something more reasonable, like 3 minutes. Afterwards, I started getting data in much more reliably, however the MQTT connection is still kinda patchy and I want to fix that before I move on to adding more sensors to the thing.&lt;/p&gt;

&lt;p&gt;In the end though, I get a good amount of data through, which I can see on my Grafana dashboard:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxelsf4h3z46ij5fqovn0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxelsf4h3z46ij5fqovn0.png" alt="Dashboard showing that a lot of emissions happen at night"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For completeness, here's the Node-RED flow:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqd93tjt36o2dwxwbr0ou.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqd93tjt36o2dwxwbr0ou.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And a photo of the setup sucking in air from outside my window to analyse:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa09jwwzcu342p6ts4481.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa09jwwzcu342p6ts4481.jpg"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  More stuff?
&lt;/h1&gt;

&lt;p&gt;For further development, I will definitely need to fix the MQTT connection. I don't know what's going on, but sometimes the ESP just plain can't connect to the server, or data gets eaten somewhere in the process. It might be a slightly too aggressive application of sleep modes, or maybe I need to tweak my WiFi settings. This is the challenge of debugging distributed systems - you just plain don't know where the problem actually is, and more often than not, it's actually many problems in many places.&lt;/p&gt;

&lt;p&gt;I also want to actually power the thing off of batteries. I have some 18650 lithium cells I bought at very reasonable prices as Poundland powerbanks (£2 per ~2000mAh cell at a brick and mortar store in the UK? And you get some charge circuitry with it? That's super cheap for hobbyists!), and a holder with appropriate charge circuitry.&lt;/p&gt;

&lt;p&gt;Once I assemble all of that, the box can be made mostly safe against rain water ingress with an O-ring seal along the top and some shrouding of the air inlet and outlet ports. Then I can put it outside and actually close my window.&lt;/p&gt;

&lt;p&gt;Finally, I can also add some I2C sensors for general weather stuff - such as the ever popular BME280. I also thought about adding a display and some sort of touch sensitive pad stuck to the underside of the enclosure cover (which is transparent) to read data directly from the thing, but I think the Grafana interface is nicer.&lt;/p&gt;

&lt;p&gt;And perhaps one day I'll design a cost reduced solution for all of this with a dedicated circuit board and put it up online for people to buy. If I can get the whole thing down to less than £30 I'd be quite happy. Yes, I will add better security if I ever sell this design, don't worry!&lt;/p&gt;

&lt;h1&gt;
  
  
  Closing thoughts for now
&lt;/h1&gt;

&lt;p&gt;I like this project. I want to know how much crap I'm breathing in and I now can. I like how this is a nice practical way to consolidate knowledge I learned at uni in two or three separate modules. And I like how quickly I can prototype all of this. I think you should try something like this too - dev hardware is super cheap these days and software is free and relatively straightforward to put together. Electronics knowledge is also easy to obtain on the internet, and proper tools are reasonably priced. Go do the thing!&lt;/p&gt;

</description>
      <category>environment</category>
      <category>arduino</category>
      <category>monitoring</category>
      <category>hardware</category>
    </item>
    <item>
      <title>Dril Or No Dril? Building a text classifier in TensorFlow</title>
      <dc:creator>Filip</dc:creator>
      <pubDate>Tue, 29 Jan 2019 18:41:50 +0000</pubDate>
      <link>https://dev.to/minkovsky/dril-or-no-dril-building-a-text-classifier-in-tensorflow-208k</link>
      <guid>https://dev.to/minkovsky/dril-or-no-dril-building-a-text-classifier-in-tensorflow-208k</guid>
      <description>&lt;p&gt;There is a ton of different tensorflow posts on the web already, and many of them are actually good. This is not that. This is me, writing a crappy little classifier for what's essentially an elaborate shitpost.&lt;/p&gt;

&lt;p&gt;In this post, I will walk you through how I built &lt;a href="https://dril-or-no-dril.glitch.me/"&gt;DRIL OR NO DRIL&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8yQP_5qO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/ijy45dvi3szdtqy9ie2p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8yQP_5qO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/ijy45dvi3szdtqy9ie2p.png" alt='An example of the application in action. A tweet of "no" yields 93.8% dril.'&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;p&gt;If you don't know who or what dril is, &lt;a href="https://twitter.com/dril"&gt;have a look&lt;/a&gt;. If you already do, great. If you're just coming back - isn't that one of the weirdest twitter accounts you've seen in a while? Anyway - the style is quite distinctive, so I thought I might have a stab at creating a classifier that tries to pick up on what makes dril &lt;em&gt;dril&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;To do that, I first need tweets. Lots of tweets, both from dril and other accounts. I ended up using myself, a few of my friends who agreed to be included in the model, and the Prime Minister (at the time of writing this paragraph, lol) Theresa May.&lt;/p&gt;

&lt;p&gt;I also need a model. I ended up using a modified version of the IMDB review classifier from &lt;a href="https://www.tensorflow.org/tutorials/keras/basic_text_classification"&gt;tensorfow docs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Finally, to put it online without having to pay for anything, I needed a way to ship my model to the browser with glitch. This also has a bonus of keeping all the text you enter in that box on your machine. I achieved that through tensorflow.js, a browser-based subset of tensorflow which runs on WebGL producing loads of warnings because, really, WebGL wasn't designed to be used this way.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting some tweets
&lt;/h2&gt;

&lt;p&gt;The first thing to do is get some tweets. This requires a Twitter API account so you can authenticate. It's that or doing some screen-scraping hacks but for convenience I stuck with the API - perhaps to my detriment. I wrote a Python script to download all these tweets which was pretty easy thanks to &lt;a href="https://www.tweepy.org/"&gt;tweepy&lt;/a&gt;. It downloads tweets and saves them into a sqlite3 database. It even supports resuming from the earliest downloaded tweet (important in case it crashes). Tweepy's cursor API is also really neat - you can iterate over tweets and it'll handle pagination for you:&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;cursor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tweepy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Cursor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_timeline&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;max_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;include_rts&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;tweet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;status_to_tuple&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;save_tweet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tweet&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;It's also great that you can tell tweepy to automatically wait in case of a rate-limit response - though I don't think I ran into that issue yet:&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_twitter_api&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;auth&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tweepy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OAuthHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;secrets&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TW_API_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;secrets&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TW_API_SECRET&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;auth&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set_access_token&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;secrets&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TW_TOKEN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;secrets&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TW_SECRET&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;tweepy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;API&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;wait_on_rate_limit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;wait_on_rate_limit_notify&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;I then ran the script on some Twitter timelines. I used dril, obviously, as well as some examples of non-dril content. Then I looked at the amount of tweets I downloaded and saw a discrepancy:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--1-GYF-0Q--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/iab6wss3n0h9ozifu4f8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1-GYF-0Q--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/iab6wss3n0h9ozifu4f8.png" alt="dril's tweet count: 8690 tweets"&gt;&lt;/a&gt; &lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--LcHEFI-k--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/j1562o3x0uc03flg9oe1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--LcHEFI-k--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/j1562o3x0uc03flg9oe1.png" alt="my dril tweet count in the database: 2930"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Turns out that as per &lt;a href="https://developer.twitter.com/en/docs/tweets/timelines/api-reference/get-statuses-user_timeline"&gt;Twitter API docs&lt;/a&gt;, the endpoint used only returns up to 3200 most recent tweets. So I guess if you really needed that archival content, you'd have to implement those screen-scraping hacks after all. I chose to not bother.&lt;/p&gt;

&lt;h2&gt;
  
  
  Installing TensorFlow
&lt;/h2&gt;

&lt;p&gt;Because this is the real hard problem in computer science (citation needed), I'm now going to spend 5 paragraphs talking about how to install TensorFlow.&lt;/p&gt;

&lt;p&gt;j/k, get anaconda and go here: &lt;a href="https://www.anaconda.com/blog/developer-blog/tensorflow-in-anaconda/"&gt;https://www.anaconda.com/blog/developer-blog/tensorflow-in-anaconda/&lt;/a&gt; - works even on exotic platforms such as Windows.&lt;/p&gt;

&lt;h2&gt;
  
  
  The classifier
&lt;/h2&gt;

&lt;p&gt;To create the classifier you will first need to load the data into a format that tensorflow accepts, and there is only one such format - numpy arrays. This is also the first step you will need to make a decision as to how you want to represent the text you put in because you can't simply throw strings at a neural network.&lt;/p&gt;

&lt;p&gt;There are a number of ways you could represent a piece of text in a compact way, for instance by using the &lt;a href="https://en.wikipedia.org/wiki/Bag-of-words_model"&gt;bag-of-words approach&lt;/a&gt; which only preserves word frequencies, or by encoding each word as a number as is the case of the TF/Keras IMDB example dataset. You can also try to do fancy things like discarding the most popular words like "a", "the", and "hyperloop is a good idea". In my example I'm not doing any of that and instead I take the raw bytes of each character and shove them into a 240-element numpy array, padding out the remaining space with zeros. The idea is that any other preprocessing could remove nuance about the style of these tweets. Also I'm lazy.&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;to_padded_bytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tweet&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;bts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nb"&gt;ord&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;tweet&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pad&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;240&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;bts&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shape&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt; &lt;span class="n"&gt;mode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'constant'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This still meant that I needed to have an embedding layer in my network that extracted features from the byte values, but it was a little bit different than in the example.&lt;/p&gt;

&lt;p&gt;Finally, the labels are represented as 2-dimensional vectors. A dril tweet is labelled as &lt;code&gt;[1, 0]&lt;/code&gt;, whereas a non-dril tweet is &lt;code&gt;[0, 1]&lt;/code&gt;. This is so that at the end of the process I can get the confidence value from the network - it will usually reply with a vector like &lt;code&gt;[0.98, 0.02]&lt;/code&gt; which means "I am 90% confident that this is a dril tweet and only 2% confident that it's not". Or the inverse. Or somewhere in between.&lt;/p&gt;

&lt;p&gt;The model itself is as follows:&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;keras&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sequential&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="n"&gt;keras&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;layers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Embedding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;input_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;240&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;keras&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;layers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Conv1D&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;140&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;padding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'valid'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;activation&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'relu'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;strides&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;keras&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;layers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GlobalAveragePooling1D&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="n"&gt;keras&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;layers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Dense&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;512&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;activation&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'relu'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;keras&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;layers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Dense&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;activation&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'softmax'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;I threw in the convolutional stage because I'm hoping it's able to pick up on the stylistic differences between types of tweeter, but so far it's mostly learned that shorter tweets are more likely to be dril, and that he doesn't use emoji very often. Nonetheless, at ~89% validation accuracy, I decided that it's good enough for a joke.&lt;/p&gt;

&lt;p&gt;If I were doing this properly I might look at existing text classification architectures and try to actually learn something from them. Then maybe I'd achieve that 99% accuracy.&lt;/p&gt;

&lt;h2&gt;
  
  
  Onwards to JavaScript
&lt;/h2&gt;

&lt;p&gt;tensorflow.js is a little limited. From the &lt;a href="https://js.tensorflow.org/tutorials/import-keras.html"&gt;docs&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;TensorFlow.js Layers currently only supports Keras models using standard Keras constructs. Models using unsupported ops or layers—e.g. custom layers, Lambda layers, custom losses, or custom metrics—cannot be automatically imported, because they depend on Python code that cannot be reliably translated into JavaScript.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's fine though, as my model &lt;em&gt;is&lt;/em&gt; only using standard constructs. The first step is to save it to a h5 file, then you can run the tensorflowjs converter on it. To get the converter, you can run &lt;code&gt;pip install tensorflowjs&lt;/code&gt; in your conda environment. Note, though, that some of the installed packages might get downgraded as the dependencies are a little out of sync - this shouldn't be too worrying as they are all within requirements of each other. The converter will generate a directory with two or more files: a &lt;code&gt;model.json&lt;/code&gt; file which describes the structure of the model, and some &lt;code&gt;groupK-shardNofM&lt;/code&gt; files which contain the learned attributes of your model (the weights, in the ML lingo). You can then serve these files from a web server and load them on the client side like so:&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// This assumes that model.json is in the same directory as the current document&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kr"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;tf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;loadModel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'model.json'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;If you don't know what the &lt;code&gt;await&lt;/code&gt; does, read this: &lt;a href="https://ponyfoo.com/articles/understanding-javascript-async-await"&gt;https://ponyfoo.com/articles/understanding-javascript-async-await&lt;/a&gt;. If you do and are positive you can't use it, it's still a promise so you can work with that instead. If you need to support Internet Explorer, ask your doctor if tensorflow.js is right for you. In my case I decided that being compatible with popular browsers is for losers and just use async/await as they are.&lt;/p&gt;

&lt;p&gt;The glitch project itself is also very simple - the main issue is getting the text from a &lt;code&gt;&amp;lt;textarea&amp;gt;&lt;/code&gt; into the same format as I used in training, namely a 1x240 tensor. The code is pretty similar to the python version:&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;tweetToTensor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tweet&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;array&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Uint8Array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;240&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;tweet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;array&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;tweet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;charCodeAt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;tf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tensor1d&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;array&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// later...&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;batch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;tweetToTensor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;reshape&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;240&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;prediction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;predict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;batch&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;prediction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;reshape&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;It's nice that I don't have to explicitly pad out my arrays here because allocating a &lt;code&gt;Uint8Array&lt;/code&gt; automatically gives me a zero'd-out array so I only need to copy in the relevant byte values.&lt;/p&gt;

&lt;p&gt;There is one issue with hosting everything on glitch though - since the &lt;code&gt;group-shard-piece-whatever&lt;/code&gt; files are binary, glitch uploads them to a cdn and gives you a long link to the file in its bucket. This is fine for images, but tensorflow.js expects that it'll be able to get the weights files from the same base URL as the model.json file (eg. if the model file is at &lt;code&gt;https://example.com/models/model.json&lt;/code&gt;, it'll look for files like &lt;code&gt;https://example.com/models/group1-shard1of1&lt;/code&gt; etc). However, since the library uses &lt;code&gt;fetch()&lt;/code&gt;, it also follows redirects, and it's easy to set up your server script to catch requests for the weights files and point it to the right place.&lt;/p&gt;

&lt;p&gt;Well, easy if you've got one or two files; if you need more than that, again ask your doctor if tensorflow.js and glitch are right for you.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where's the code?
&lt;/h2&gt;

&lt;p&gt;The classifier etc: &lt;a href="https://github.com/FLamparski/dril-or-no-dril/blob/master/Dril%20Or%20No%20Dril.ipynb"&gt;https://github.com/FLamparski/dril-or-no-dril/blob/master/Dril%20Or%20No%20Dril.ipynb&lt;/a&gt; - the same repo also contains the tweet download scripts, but you'll need to provide your own &lt;code&gt;secrets.py&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;The glitch site: &lt;a href="https://glitch.com/edit/#!/dril-or-no-dril"&gt;https://glitch.com/edit/#!/dril-or-no-dril&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Learnings
&lt;/h2&gt;

&lt;p&gt;From what's essentially an elaborate joke, I learned a thing or two about conducting machine learning experiments on data that wasn't delivered to you in a neatly wrapped package with instructions. It's a harsh world out there and most data will be messy and in the wrong format. About 2/3 (or more, haven't checked) of the code I wrote deals with acquiring the data and preparing it for the model. If I was to do this again &lt;em&gt;properly&lt;/em&gt;, I might also look into ways of getting past that Twitter API tweet limit, and gathered up much more non-dril material. As it stands, the classifier is biased towards saying the input is dril-like. I would definitely look at different text processing models, both in terms of how the neural network is actually designed, and how to encode the data going into it. The vector-of-bytes idea is not terribly efficient and would not scale well to longer documents. I might even be tempted to try and serve this model from an actual server instead of dumping it into the user's browser in hopes that it'll work (those weights files can get awfully large sometimes...). I hear that Google has an offering for production machine learning apps. Or something.&lt;/p&gt;

&lt;h2&gt;
  
  
  Discussion
&lt;/h2&gt;

&lt;p&gt;I'd like to hear from you if you have an idea of what you'd do for an application like that, especially if what you'd do is different and actually works. I'd also like to know what's the silliest machine learning thing you've made, and whether you went through the trouble of putting it online.&lt;/p&gt;

&lt;p&gt;Right, until next time!&lt;/p&gt;

</description>
      <category>tensorflow</category>
      <category>machinelearning</category>
      <category>python</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Graphology: Writing a graph of nodes UI in HTML5 (Part 1)</title>
      <dc:creator>Filip</dc:creator>
      <pubDate>Sat, 22 Apr 2017 10:01:25 +0000</pubDate>
      <link>https://dev.to/minkovsky/graphology-writing-a-graph-of-nodes-ui-in-html5-part-1</link>
      <guid>https://dev.to/minkovsky/graphology-writing-a-graph-of-nodes-ui-in-html5-part-1</guid>
      <description>&lt;p&gt;I like Blender's compositor nodes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--QBsgATOn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/http://i.imgur.com/LBSEWh4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--QBsgATOn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/http://i.imgur.com/LBSEWh4.png" alt="Blender compositor screen from some Reddit post"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It doesn't mean I know how to use them well, but I like the UI and I think it works very well in that context.&lt;/p&gt;

&lt;p&gt;I also kinda like the JACK Patchbay, because it shows you exactly where everything is connected - and it uses a similar interface.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--iUcEjjH1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/http://kxstudio.linuxaudio.org/screenshots/carla-patchbay.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--iUcEjjH1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/http://kxstudio.linuxaudio.org/screenshots/carla-patchbay.png" alt="A Patchbay running inside of Carla, a music production app"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We will not talk about the ALSA Modular Synthesiser here.&lt;/p&gt;

&lt;p&gt;I like these interfaces because they show you how everything is connected in a complex graph, where you can zoom in on a particular part and change some settings of a single node, or zoom out and get a good feel of how the data (be it pixels, vector data, sound, whatever) flows through the graph.&lt;/p&gt;

&lt;p&gt;So I want to write a general-purpose graph interface for working with graphs of complex nodes. And I want to use a data-driven approach so that, if you want, node UIs will be generated automatically.&lt;/p&gt;

&lt;h2&gt;
  
  
  So far
&lt;/h2&gt;

&lt;p&gt;A scattershot experimental approach. Here is the sort of UI I'm after. It's pretty horrible at responding to screen sizes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--OcsbbUul--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/sk7nfzihx4dju0igsa1c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--OcsbbUul--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/sk7nfzihx4dju0igsa1c.png" alt="A screenshot of the nodes UI example showing a two-node graph"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://codepen.io/FLamparski/pen/LyErym"&gt;Nodes UI example&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here's a simple demo of one of the interactions I'm after: &lt;a href="https://codepen.io/FLamparski/pen/JNoZEM"&gt;dragging around Bezier curve connectors&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Note that this may very well be a horrible and hackish approach. It uses &lt;em&gt;SVG&lt;/em&gt; to render the connector, and changes the path live as you drag its free end with the mouse.&lt;/p&gt;

&lt;h2&gt;
  
  
  Existing software
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;d3&lt;/strong&gt; - Seems like it might help in some respects but I couldn't find a demo that captures what I want. Also I want this UI to be compatible with React (more or less) which d3 isn't.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;jsPlumb&lt;/strong&gt; - &lt;a href="https://jsplumbtoolkit.com/demos/toolkit/groups/index.html"&gt;This demo&lt;/a&gt; captures an &lt;em&gt;essence&lt;/em&gt; of what I want to do. It's ugly as a naked mole rat, and the library itself is priced in $k's per year. I want my UI library to be open source (with reasonable commercial licences should this concept catch on).&lt;/p&gt;

&lt;h2&gt;
  
  
  Basic data model
&lt;/h2&gt;

&lt;p&gt;I am assuming the following things for the following example:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Nodes are representations of functions&lt;/li&gt;
&lt;li&gt;A node can have many inputs and outputs
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Output&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;nodes-ui&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;Colour&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../image/Colour&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ColourMixer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ColourMixer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Colour Mixer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;inputs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;colour1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Colour 1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Colour&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;colour2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Colour 2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Colour&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;mixType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Mix function&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;outputs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Output&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Colour&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That would be an individual node's definition. Perhaps a special case might be necessary for nodes that provide external input ("sources") or describe external outputs ("sinks") in the graph. The UI will take in those node definitions do some magic to construct graphical representations of those nodes, letting you add and remove them and link them together into graphs. Finally, it will give you back a graph of objects like this (where the &lt;code&gt;#'id'&lt;/code&gt; notation should be thought of as "a reference to a Node object with this ID):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ColourMixer-35&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ColourMixer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;mixType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;multiply&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="nx"&gt;inputs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;colour1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;from&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Image-24&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;colourData&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="nx"&gt;colour2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;from&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Colour-5&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;colour&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="nx"&gt;outputs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;to&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Display-1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;colourData&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(And I guess at the top level, you'd have to have an object like &lt;code&gt;{sources: [...], sinks: [...]}&lt;/code&gt;)&lt;/p&gt;

&lt;p&gt;From there, you'd use this graph representation to construct your internal computation graph or whatever it is you want, and then run some computations on that structure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What do you think of this idea?&lt;/strong&gt; Am I reinventing the wheel? Is there a library that can already do this? Or do you have a suggestion for improvements? Let me know in the comments!&lt;/p&gt;

</description>
      <category>html</category>
      <category>ui</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Hi, I'm Filip</title>
      <dc:creator>Filip</dc:creator>
      <pubDate>Sat, 22 Apr 2017 08:49:10 +0000</pubDate>
      <link>https://dev.to/minkovsky/hi-im-filip</link>
      <guid>https://dev.to/minkovsky/hi-im-filip</guid>
      <description>&lt;p&gt;I played with computers since the young age of 8 years old, when Scratch didn't even have lists yet, or even proper string handling. Since then I started playing with C#, Python, HTML and others in a very unstructured fashion. For a middle school IT project, I built a blog with Django, and a simple browser using the Gecko engine.&lt;/p&gt;

&lt;p&gt;You can find me on Mastodon as &lt;a href="https://mastodon.technology/@Minkovsky" rel="noopener noreferrer"&gt;@Minkovsky@mastodon.technology&lt;/a&gt; or on Twitter as &lt;a href="https://twitter.com/Minkovsky" rel="noopener noreferrer"&gt;@Minkovsky&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I live in London and study Computer Science.&lt;/p&gt;

&lt;p&gt;I work in full stack web development. I mostly use the web languages, PHP, Java (for coursework), and many others. I would love to learn more about machine learning and data science.&lt;/p&gt;

&lt;p&gt;Nice to meet you.&lt;/p&gt;

</description>
      <category>introduction</category>
    </item>
  </channel>
</rss>
