소스 검색

Migrating some test blog posts

Steffen Cole Blake 4 년 전
부모
커밋
678b78522e

+ 1 - 1
config.toml

@@ -25,7 +25,7 @@ preserveTaxonomyNames = true
     showhugo = true
 
     # Show a CRT screen effect. Default is enabled.
-    showcrt = false
+    showcrt = true
 
     # Urls for social networks
     #facebook = "https://facebook.com"

+ 34 - 17
content/about.md

@@ -1,14 +1,14 @@
 +++
-title = "About"
+title = "About - Steffen Blake"
 description = "Who? Me?"
 date = "2021-09-17"
 aliases = ["about-us", "about-hugo", "contact"]
 author = "Steffen Blake"
 +++
 
-With over 7 years experience in the Kitchen Industry and another 6 years of software engineering experience to match, I have developed an interesting taste for code. I love delving deep into how codebases tick and learning new architectures whenever I can.
+With over 7 years experience in the Kitchen Industry and another 6 years of software engineering experience to match, I have developed an interesting taste for code (pun intended). I love delving deep into how codebases tick and learning new architectures whenever I can.
 
-Join me on a fun filled adventure while we dive into rebuilding wheels from the ground up (and learning how the work while we do it!)
+Join me on a fun filled adventure while we dive into rebuilding wheels from the ground up (and learning how they work while we do it!)
 
 ## Contact Info
 
@@ -20,20 +20,37 @@ Last Updated: 2021-09-17
 
 This list is neither all comprehensive nor necessarily perfectly up to date. I'm constantly deep diving into random new (usually useful) skills. If you have further questions regarding specific stuff, absolutely shoot me an email!
 
-* C# : ★★★★★
-* .NET Core : ★★★★★
-* ASP.NET MVC : ★★★★★
-* Razor Pages : ★★★★☆
-* Javascript (Web) : ★★★★★
-* Javascript (Node) : ★★★★☆
-* CSS / SCSS : ★★★★☆
-* SQL : ★★★☆☆
-* Unit Testing (Moq, nUnit, xUnit) : ★★★★☆
-* Code Review / Peer Management : ★★★★★
-* APIs (REST, Consuming, Designing) : ★★★★★
-* Docker : ★★★☆☆
-* Kubernetes : ★★★☆☆
-* Bash: ★★★☆☆
+```
+====== Skills (In no particular order) =======
+
+C# ---------------------------------- ★★★★★
+
+.NET Core --------------------------- ★★★★★
+
+ASP.NET MVC ------------------------- ★★★★★
+
+Razor Pages ------------------------- ★★★★☆
+
+Javascript (Web) -------------------- ★★★★★
+
+Javascript (Node) ------------------- ★★★★☆
+
+CSS / SCSS -------------------------- ★★★★☆
+
+SQL --------------------------------- ★★★☆☆
+
+Unit Testing (Moq, nUnit, xUnit) ---- ★★★★☆
+
+Code Review / Peer Management ------- ★★★★★
+
+APIs (REST, Consuming, Designing) --- ★★★★★
+
+Docker ------------------------------ ★★★☆☆
+
+Kubernetes -------------------------- ★★★☆☆
+
+Bash: ------------------------------- ★★★☆☆
+```
 
 ##  Experience
 

+ 226 - 0
content/post/Async-Event-Stream-for-IoT-Devices-in-.Net-Core.md

@@ -0,0 +1,226 @@
++++
+author = "Steffen Blake"
+title = "Async Event Stream for IoT Devices in .Net Core"
+date = "2021-07-12"
+description = "Building async subscription based event architecture for IoT devices in dotnet!"
+tags = [
+    "#csharp",
+    "#dotnet",
+    "#IoT",
+]
+categories = [
+    "Coding",
+    "Guides"
+]
+series = ["IoT"]
+aliases = ["async-events-IoT"]
++++
+
+Building applications for IoT Devices has it’s challenges, and as I checked out several options presented by various libraries on various platforms, the consistent challenge I found was handling a variety of events across numerous board pins I wanted to listen on.
+
+# The Challenge
+Every library I checked out involved a fair bit of boilerplate work I had to sort out in order to utilize multithreading on something like a Raspberry Pi’s multicore processor. What I really wanted was a way to easily describe subscriptions to pin events, spin up a listener, and then handle an incoming stream of events on the fly without blocking any threads.
+
+# Async Streams
+As a C# developer, back in September 2019 Microsoft announced the new `IAsyncEnumerable` interface which allowed you to yield return indefinitely on an async method, effectively allowing you to design a “subscription” to an async pipeline using the familiar foreach loop, with an async twist.
+
+Documentation on this change is best checked out here: https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-8#asynchronous-streams
+
+Async streams were powerful and the moment I learned about them, I was hungry for the opportunity to leverage this incredibly powerful new interface for my own use cases.
+
+Now, a year and a half later, I realized that this was exactly “the time to use that item.”
+
+# Official dotnet IoT Support
+Earlier Microsoft also officially supported the Nuget package `IoT.Device.Bindings` which can be checked out on their repo here:
+https://github.com/dotnet/iot
+
+This package enables one to compile dotnet core applications targeting an arm architecture, hooking into the GPIO pins and other logical controllers of a wide variety of supported boards. Including, to my excitement, Raspberry Pis and Arduinos!
+
+Now I found the same challenge was still present, but luckily on the GpioController the handy method `WaitForEventAsync` which returns a `ValueTask<WaitForEventResult>`
+
+This was enough for me to develop a powerful wrapper that extends this functionality in a broader way, utilizing dotnet’s powerful Task system for inherent async and multithreaded applications.
+
+The goal was simple, what I wanted was to define a series of `Subscription`s inside of a `SubscriptionCollection` of sorts, then compile it into an immutable SubscriptionService which could be run, returning an `IAsyncEnumerable` to be listened in on as a stream of constant async data.
+
+# Announcing Iot.Device.Subscriptions
+I created this package and pushed it up to nuget, so I am proud to announce that my first Nuget packages, `Iot.Device.Subscriptions`, is now officially up and ready to be used!
+Nuget: https://www.nuget.org/packages/Iot.Device.Subscriptions/
+Github: https://github.com/SteffenBlake/Iot.Device.Subscriptions
+
+The Github `Readme.md` provides a handy guide showing how to use the project, but I’ll go a bit more into detail here in terms of design choices and how I built the example project, `StopwatchExample`, which can be looked at here if you wish to follow along:
+https://github.com/SteffenBlake/Iot.Device.Subscriptions/tree/main/Source/Iot.Device.Subscriptions.StopwatchExample
+
+# Architecting the Example Project
+To start, there were a few boilerplate basics I wanted to ensure were implemented. I love utilizing dotnet’s `IConfiguration` libraries. Being able to configure the applications behavior at runtime using any combination of cmdline args, `AppSettings.json`, Environment Vars, and Secrets is exceptionally powerful in my opinion.
+
+This gets particularly highlighted when building an IoT app, as it can be a tad bit on the slow side to deploy the latest compiled version of your app to your device over SSH/rsync/etc each time, so you really want to avoid having to re-deploy the app every single time you make a slight tweak to a configuration value.
+
+So to start I brought in all of the Configuration nuget packages, created `AppSettings.json` in the project (and set it to Always Output in Visual Studio)
+
+Then made a simple static method to compile the `IConfiguration` into the strongly typed `Configuration` class I had built in `Configuration.cs`
+
+```csharp {linenos=table}
+private static Configuration BuildConfiguration(string[] args)
+{
+    return new ConfigurationBuilder()
+        .SetBasePath(Directory.GetParent(AppContext.BaseDirectory).FullName)
+        .AddEnvironmentVariables()
+        .AddJsonFile("AppSettings.json", true)
+        .AddUserSecrets<Program>(true)
+        .AddCommandLine(args)
+        .Build()
+        .Get<Configuration>();
+}
+```
+
+Now that the Configuration was bootstrapped up, the next step was to build a simple wrapper class for housing all the `IDisposable` controllers/drivers/etc you need to bootstrap up to interact with the IoT architecture.
+
+I designed the `Configuration` to be flexible for a wide variety of devices, since this is an example project meant to be run by anyone with the hardware on hand, so I had to add in a fair bit of extra code you typically wouldn’t be utilizing when writing a program for a specific set of hardware yourself.
+
+The reason I had to wrap all the `IDisposable`s in their own class, is due to their disposable nature. I wanted a static factory method that assembles all these IoT pieces together, however when passing Disposables up a layer, you need the layer above to be in charge of the Disposing pattern.
+
+You currently cannot declare a Disposable Tuple, nor declare a using pattern on it’s contents in any way. Manually Disposing at the end of a method is also considered poor form, so in this case the right call is to instead use the `Decorator Pattern` and wrap all your Disposables up in a “Container” class which also implements `IDisposable`, such that when it gets disposed, it triggers off the disposal of all its contents.
+
+This can be checked out over on `StopwatchController.cs`
+
+Now that we have all our bootstrapping done, all that remains is actually setting up the logic for the State Machine, which when utilizing my newly minted Subscriptions package, is quite easy!
+
+I decided I wanted to have three buttons on this program, as I feel three and more event listeners is when the gap between the traditional `WaitForEventAsync` method starts to get difficult to monitor.
+
+So I decided on doing a “Stopwatch” program, which simply just feeds out a running time to the LCD panel, with a Reset Button, a Pause Button, and a Stop button to end the program.
+
+I went with Board Pins `29`, `33`, and `40` respectively, as on the Raspberry Pi all three of these GPIO pins sit nicely right beside Ground pins, making it simple to wire up.
+
+Reset Button was wired to pins `29+30`, Pause Button to pins `33+34`, and Stop button to pins 39+40
+
+Finally I hooked up my LCD screens backpack to the standard I2C pins as usual, Pins `3/4/5/6`. See the following picture for reference, the green circles being the GPIO pins.
+
+![Raspberry Pi GPIO Pins](/images/RPI-GPIO.png)
+
+Now that was done, I could setup the Subscription service, which is pretty simple. Because I wanted Clock Events to work on the board (for incrementing the stopwatch in real time), I had to also enable Clock Events and set the `ClockRate` off the Configuration.
+
+```csharp {linenos=table}
+private static ISubscriptionService BuildSubscriptions(Configuration config)
+{
+    var collection = new SubscriptionCollection
+    {
+        ClockEnabled = true,
+        ClockRate = config.ClockRate
+    };
+    if (config.ResetBtnEnabled)
+    {
+        collection.Subscribe(config.ResetBtnPin, config.BtnPinMode, PinEventTypes.Rising);
+    }
+    if (config.PauseBtnEnabled)
+    {
+        collection.Subscribe(config.PauseBtnPin, config.BtnPinMode, PinEventTypes.Rising);
+    }
+    if (config.StopBtnEnabled)
+    {
+        collection.Subscribe(config.StopBtnPin, config.BtnPinMode, PinEventTypes.Rising);
+    }
+
+    return collection.Build();
+}
+```
+
+Furthermore, because I wanted to support individuals who may not have three buttons on hand to use, I added a boolean config toggle for enabling/disabling each of the button listeners. Once again a fair bit of this logic could be trimmed out when designing a program for more concrete specific hardware!
+
+Now that all that is done, we can actually consume the methods and spin up the listener. We also need to set our three state machine values, which is as simple as just pre-emptively declaring them outside of the `async foreach` loop
+
+```csharp {linenos=table}
+var clock = 0L;
+var buttonDelay = config.BtnSensitivityTicks;
+var paused = false;
+await foreach (var subEvent in subscriptionService.Run(stopwatch.Board, CancellationToken.None))
+{
+   ...
+}
+```
+
+And thats it! Everything that happens inside of the foreach loop is now during any of our fired events. the `subEvent` object we get informs us about the current event that has occurred, and we can process that information and update the state machine.
+
+In this case, I went with as follows:
+
+```csharp {linenos=table}
+buttonDelay += subEvent.Delta;
+
+if (!paused)
+{
+    clock += subEvent.Delta;
+}
+
+if (subEvent.PinNumber == config.ResetBtnPin && buttonDelay > config.BtnSensitivityTicks)
+{
+    clock = 0L;
+    stopwatch.Lcd.Clear();
+    buttonDelay = 0L;
+}
+
+if (subEvent.PinNumber == config.PauseBtnPin && buttonDelay > config.BtnSensitivityTicks)
+{
+    paused = !paused;
+    stopwatch.Lcd.Clear();
+    buttonDelay = 0L;
+}
+
+if (subEvent.PinNumber == config.StopBtnPin && buttonDelay > config.BtnSensitivityTicks)
+{
+    break;
+}
+
+stopwatch.Lcd.SetCursorPosition(0, 0);
+stopwatch.Lcd.Write($"{TimeSpan.FromTicks(clock):g}");
+```
+
+There are several parts here, and lets dig into each one in detail, starting from the top.
+
+```csharp {linenos=table}
+buttonDelay += subEvent.Delta;
+```
+
+Upon testing with my (extremely cheap) buttons I purchased, I found the program was running so extremely fast that when I clicked a button, it fired off several trigger events from one click. Whew! So to handle that I added a simple `buttonDelay` value which simply represented a “cooldown” between accepted button press events.
+
+### A note about the Delta value
+So what is this thing? The `Delta` value represents a precise number of ticks that have transpired since the prior clock event triggered. Handily, for non-click events (GPIO triggers), this value will always be zero, so when handling tracking time you can simply just add this value onto any running totals.
+
+#### “But I specified my clock rate in the config! Can’t I use that?”
+
+The `Clockrate` in the config specifies the minimum time for Clock events to fire, but does not necessarily ensure its maximum time. Once the Clock event fires, there can be a miniscule amount of time after to process it on the queue and push it up to the main listener.
+
+However, the `Delta` value is constant and is calculated based on the prior event moment of fire. This ensures not a single tick is missed and time is accurately tracked.
+
+For example, if you were to do:
+
+```csharp {linenos=table}
+Console.WriteLine($"{subEvent.Delta}");
+await Task.Delay(1000);
+Console.WriteLine($"{subEvent.Delta}");
+```
+
+You would get the same number out twice in the Console, each individual subEvent’s `Delta` value is fixed and immutable once pushed up.
+
+# State Machine Logic
+Moving on, because Task architecture is inherently thread safe, we can perform the following code quite easily and, despite leveraging multithreading, dont have to think too hard at all about locks or thread safe variable management. Updating the State Machine is a breeze!
+
+```csharp {linenos=table}
+if (subEvent.PinNumber == config.PauseBtnPin && buttonDelay > config.BtnSensitivityTicks)
+{
+    paused = !paused;
+    stopwatch.Lcd.Clear();
+    buttonDelay = 0L;
+}
+```
+
+As you can see, we simply just need to check if the current fired event’s `PinNumber` matches any given button, and then perform logic as such.
+
+Finally, once all buttons are checked, we finish off by updating the LCD screen to display this new information:
+
+```csharp {linenos=table}
+stopwatch.Lcd.SetCursorPosition(0, 0);
+stopwatch.Lcd.Write($"{TimeSpan.FromTicks(clock):g}");
+```
+
+And thats it! A smidge of cleanup code outside the loop to clear the screen and log the program’s end, and we have a fully functioning, Async Streamed, Subscription based program!
+
+Thanks for joining me on this little adventure, and feel free to try it out for yourself!

+ 0 - 46
content/post/emoji-support.md

@@ -1,46 +0,0 @@
-+++
-author = "Hugo Authors"
-title = "Emoji Support"
-date = "2019-03-05"
-description = "Guide to emoji usage in Hugo"
-tags = [
-    "emoji",
-]
-+++
-
-Emoji can be enabled in a Hugo project in a number of ways. 
-<!--more-->
-The [`emojify`](https://gohugo.io/functions/emojify/) function can be called directly in templates or [Inline Shortcodes](https://gohugo.io/templates/shortcode-templates/#inline-shortcodes). 
-
-To enable emoji globally, set `enableEmoji` to `true` in your site's [configuration](https://gohugo.io/getting-started/configuration/) and then you can type emoji shorthand codes directly in content files; e.g.
-
-<p><span class="nowrap"><span class="emojify">🙈</span> <code>:see_no_evil:</code></span>  <span class="nowrap"><span class="emojify">🙉</span> <code>:hear_no_evil:</code></span>  <span class="nowrap"><span class="emojify">🙊</span> <code>:speak_no_evil:</code></span></p>
-<br>
-
-The [Emoji cheat sheet](http://www.emoji-cheat-sheet.com/) is a useful reference for emoji shorthand codes.
-
-***
-
-**N.B.** The above steps enable Unicode Standard emoji characters and sequences in Hugo, however the rendering of these glyphs depends on the browser and the platform. To style the emoji you can either use a third party emoji font or a font stack; e.g.
-
-{{< highlight html >}}
-.emoji {
-  font-family: Apple Color Emoji, Segoe UI Emoji, NotoColorEmoji, Segoe UI Symbol, Android Emoji, EmojiSymbols;
-}
-{{< /highlight >}}
-
-{{< css.inline >}}
-<style>
-.emojify {
-	font-family: Apple Color Emoji, Segoe UI Emoji, NotoColorEmoji, Segoe UI Symbol, Android Emoji, EmojiSymbols;
-	font-size: 2rem;
-	vertical-align: middle;
-}
-@media screen and (max-width:650px) {
-  .nowrap {
-    display: block;
-    margin: 25px 0;
-  }
-}
-</style>
-{{< /css.inline >}}

+ 0 - 148
content/post/markdown-syntax.md

@@ -1,148 +0,0 @@
-+++
-author = "Hugo Authors"
-title = "Markdown Syntax Guide"
-date = "2019-03-11"
-description = "Sample article showcasing basic Markdown syntax and formatting for HTML elements."
-tags = [
-    "markdown",
-    "css",
-    "html",
-]
-categories = [
-    "themes",
-    "syntax",
-]
-series = ["Themes Guide"]
-aliases = ["migrate-from-jekyl"]
-+++
-
-This article offers a sample of basic Markdown syntax that can be used in Hugo content files, also it shows whether basic HTML elements are decorated with CSS in a Hugo theme.
-<!--more-->
-
-## Headings
-
-The following HTML `<h1>`—`<h6>` elements represent six levels of section headings. `<h1>` is the highest section level while `<h6>` is the lowest.
-
-# H1
-## H2
-### H3
-#### H4
-##### H5
-###### H6
-
-## Paragraph
-
-Xerum, quo qui aut unt expliquam qui dolut labo. Aque venitatiusda cum, voluptionse latur sitiae dolessi aut parist aut dollo enim qui voluptate ma dolestendit peritin re plis aut quas inctum laceat est volestemque commosa as cus endigna tectur, offic to cor sequas etum rerum idem sintibus eiur? Quianimin porecus evelectur, cum que nis nust voloribus ratem aut omnimi, sitatur? Quiatem. Nam, omnis sum am facea corem alique molestrunt et eos evelece arcillit ut aut eos eos nus, sin conecerem erum fuga. Ri oditatquam, ad quibus unda veliamenimin cusam et facea ipsamus es exerum sitate dolores editium rerore eost, temped molorro ratiae volorro te reribus dolorer sperchicium faceata tiustia prat.
-
-Itatur? Quiatae cullecum rem ent aut odis in re eossequodi nonsequ idebis ne sapicia is sinveli squiatum, core et que aut hariosam ex eat.
-
-## Blockquotes
-
-The blockquote element represents content that is quoted from another source, optionally with a citation which must be within a `footer` or `cite` element, and optionally with in-line changes such as annotations and abbreviations.
-
-#### Blockquote without attribution
-
-> Tiam, ad mint andaepu dandae nostion secatur sequo quae.
-> **Note** that you can use *Markdown syntax* within a blockquote.
-
-#### Blockquote with attribution
-
-> Don't communicate by sharing memory, share memory by communicating.<br>
-> — <cite>Rob Pike[^1]</cite>
-
-[^1]: The above quote is excerpted from Rob Pike's [talk](https://www.youtube.com/watch?v=PAAkCSZUG1c) during Gopherfest, November 18, 2015.
-
-## Tables
-
-Tables aren't part of the core Markdown spec, but Hugo supports supports them out-of-the-box.
-
-   Name | Age
---------|------
-    Bob | 27
-  Alice | 23
-
-#### Inline Markdown within tables
-
-| Italics   | Bold     | Code   |
-| --------  | -------- | ------ |
-| *italics* | **bold** | `code` |
-
-## Code Blocks
-
-#### Code block with backticks
-
-```html
-<!doctype html>
-<html lang="en">
-<head>
-  <meta charset="utf-8">
-  <title>Example HTML5 Document</title>
-</head>
-<body>
-  <p>Test</p>
-</body>
-</html>
-```
-
-#### Code block indented with four spaces
-
-    <!doctype html>
-    <html lang="en">
-    <head>
-      <meta charset="utf-8">
-      <title>Example HTML5 Document</title>
-    </head>
-    <body>
-      <p>Test</p>
-    </body>
-    </html>
-
-#### Code block with Hugo's internal highlight shortcode
-{{< highlight html >}}
-<!doctype html>
-<html lang="en">
-<head>
-  <meta charset="utf-8">
-  <title>Example HTML5 Document</title>
-</head>
-<body>
-  <p>Test</p>
-</body>
-</html>
-{{< /highlight >}}
-
-## List Types
-
-#### Ordered List
-
-1. First item
-2. Second item
-3. Third item
-
-#### Unordered List
-
-* List item
-* Another item
-* And another item
-
-#### Nested list
-
-* Fruit
-  * Apple
-  * Orange
-  * Banana
-* Dairy
-  * Milk
-  * Cheese
-
-## Other Elements — abbr, sub, sup, kbd, mark
-
-<abbr title="Graphics Interchange Format">GIF</abbr> is a bitmap image format.
-
-H<sub>2</sub>O
-
-X<sup>n</sup> + Y<sup>n</sup> = Z<sup>n</sup>
-
-Press <kbd><kbd>CTRL</kbd>+<kbd>ALT</kbd>+<kbd>Delete</kbd></kbd> to end the session.
-
-Most <mark>salamanders</mark> are nocturnal, and hunt for insects, worms, and other small creatures.

+ 0 - 49
content/post/math-typesetting.md

@@ -1,49 +0,0 @@
----
-author: Hugo Authors
-title: Math Typesetting
-date: 2019-03-08
-description: A brief guide to setup KaTeX
-math: true
----
-
-Mathematical notation in a Hugo project can be enabled by using third party JavaScript libraries.
-<!--more-->
-
-In this example we will be using [KaTeX](https://katex.org/)
-
-- Create a partial under `/layouts/partials/math.html`
-- Within this partial reference the [Auto-render Extension](https://katex.org/docs/autorender.html) or host these scripts locally.
-- Include the partial in your templates like so:  
-
-```bash
-{{ if or .Params.math .Site.Params.math }}
-{{ partial "math.html" . }}
-{{ end }}
-```
-
-- To enable KaTex globally set the parameter `math` to `true` in a project's configuration
-- To enable KaTex on a per page basis include the parameter `math: true` in content files
-
-**Note:** Use the online reference of [Supported TeX Functions](https://katex.org/docs/supported.html)
-
-{{< math.inline >}}
-{{ if or .Page.Params.math .Site.Params.math }}
-<!-- KaTeX -->
-<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/katex.min.css" integrity="sha384-zB1R0rpPzHqg7Kpt0Aljp8JPLqbXI3bhnPWROx27a9N0Ll6ZP/+DiW/UqRcLbRjq" crossorigin="anonymous">
-<script defer src="https://cdn.jsdelivr.net/npm/[email protected]/dist/katex.min.js" integrity="sha384-y23I5Q6l+B6vatafAwxRu/0oK/79VlbSz7Q9aiSZUvyWYIYsd+qj+o24G5ZU2zJz" crossorigin="anonymous"></script>
-<script defer src="https://cdn.jsdelivr.net/npm/[email protected]/dist/contrib/auto-render.min.js" integrity="sha384-kWPLUVMOks5AQFrykwIup5lo0m3iMkkHrD0uJ4H5cjeGihAutqP0yW0J6dpFiVkI" crossorigin="anonymous" onload="renderMathInElement(document.body);"></script>
-{{ end }}
-{{</ math.inline >}}
-
-### Examples
-
-{{< math.inline >}}
-<p>
-Inline math: \(\varphi = \dfrac{1+\sqrt5}{2}= 1.6180339887…\)
-</p>
-{{</ math.inline >}}
-
-Block math:
-$$
- \varphi = 1+\frac{1} {1+\frac{1} {1+\frac{1} {1+\cdots} } } 
-$$

+ 0 - 46
content/post/math-typesetting.mmark

@@ -1,46 +0,0 @@
----
-author: Hugo Authors
-title: Math Typesetting
-date: 2019-03-08
-description: A brief guide to setup KaTeX
-markup: mmark
-math: true
----
-
-Mathematical notation in a Hugo project can be enabled by using third party JavaScript libraries.
-<!--more-->
-
-In this example we will be using [KaTeX](https://katex.org/)
-
-- Create a partial under `/layouts/partials/math.html`
-- Within this partial reference the [Auto-render Extension](https://katex.org/docs/autorender.html) or host these scripts locally.
-- Include the partial in your templates like so:  
-
-```
-{{ if or .Params.math .Site.Params.math }}
-{{ partial "math.html" . }}
-{{ end }}
-```  
-- To enable KaTex globally set the parameter `math` to `true` in a project's configuration
-- To enable KaTex on a per page basis include the parameter `math: true` in content files.
-
-**Note:** Use the online reference of [Supported TeX Functions](https://katex.org/docs/supported.html)
-{{< math.inline >}}
-{{ if or .Page.Params.math .Site.Params.math }}
-<!-- KaTeX -->
-<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/katex.min.css" integrity="sha384-dbVIfZGuN1Yq7/1Ocstc1lUEm+AT+/rCkibIcC/OmWo5f0EA48Vf8CytHzGrSwbQ" crossorigin="anonymous">
-<script defer src="https://cdn.jsdelivr.net/npm/[email protected]/dist/katex.min.js" integrity="sha384-2BKqo+exmr9su6dir+qCw08N2ZKRucY4PrGQPPWU1A7FtlCGjmEGFqXCv5nyM5Ij" crossorigin="anonymous"></script>
-<script defer src="https://cdn.jsdelivr.net/npm/[email protected]/dist/contrib/auto-render.min.js" integrity="sha384-kWPLUVMOks5AQFrykwIup5lo0m3iMkkHrD0uJ4H5cjeGihAutqP0yW0J6dpFiVkI" crossorigin="anonymous" onload="renderMathInElement(document.body);"></script>
-{{ end }}
-{{</ math.inline >}}
-
-### Examples
-
-Inline math: $$ \varphi = \dfrac{1+\sqrt5}{2}= 1.6180339887… $$
-
-Block math:
-
-$$
- \varphi = 1+\frac{1} {1+\frac{1} {1+\frac{1} {1+\cdots} } } 
-$$
-

+ 0 - 45
content/post/placeholder-text.md

@@ -1,45 +0,0 @@
-+++
-author = "Hugo Authors"
-title = "Placeholder Text"
-date = "2019-03-09"
-description = "Lorem Ipsum Dolor Si Amet"
-tags = [
-    "markdown",
-    "text",
-]
-+++
-
-Lorem est tota propiore conpellat pectoribus de pectora summo. <!--more-->Redit teque digerit hominumque toris verebor lumina non cervice subde tollit usus habet Arctonque, furores quas nec ferunt. Quoque montibus nunc caluere tempus inhospita parcite confusaque translucet patri vestro qui optatis lumine cognoscere flos nubis! Fronde ipsamque patulos Dryopen deorum.
-
-1. Exierant elisi ambit vivere dedere
-2. Duce pollice
-3. Eris modo
-4. Spargitque ferrea quos palude
-
-Rursus nulli murmur; hastile inridet ut ab gravi sententia! Nomine potitus silentia flumen, sustinet placuit petis in dilapsa erat sunt. Atria tractus malis.
-
-1. Comas hunc haec pietate fetum procerum dixit
-2. Post torum vates letum Tiresia
-3. Flumen querellas
-4. Arcanaque montibus omnes
-5. Quidem et
-
-# Vagus elidunt
-
-<svg class="canon" xmlns="http://www.w3.org/2000/svg" overflow="visible" viewBox="0 0 496 373" height="373" width="496"><g fill="none"><path stroke="#000" stroke-width=".75" d="M.599 372.348L495.263 1.206M.312.633l494.95 370.853M.312 372.633L247.643.92M248.502.92l246.76 370.566M330.828 123.869V1.134M330.396 1.134L165.104 124.515"></path><path stroke="#ED1C24" stroke-width=".75" d="M275.73 41.616h166.224v249.05H275.73zM54.478 41.616h166.225v249.052H54.478z"></path><path stroke="#000" stroke-width=".75" d="M.479.375h495v372h-495zM247.979.875v372"></path><ellipse cx="498.729" cy="177.625" rx=".75" ry="1.25"></ellipse><ellipse cx="247.229" cy="377.375" rx=".75" ry="1.25"></ellipse></g></svg>
-
-[The Van de Graaf Canon](https://en.wikipedia.org/wiki/Canons_of_page_construction#Van_de_Graaf_canon)
-
-## Mane refeci capiebant unda mulcebat
-
-Victa caducifer, malo vulnere contra dicere aurato, ludit regale, voca! Retorsit colit est profanae esse virescere furit nec; iaculi matertera et visa est, viribus. Divesque creatis, tecta novat collumque vulnus est, parvas. **Faces illo pepulere** tempus adest. Tendit flamma, ab opes virum sustinet, sidus sequendo urbis.
-
-Iubar proles corpore raptos vero auctor imperium; sed et huic: manus caeli Lelegas tu lux. Verbis obstitit intus oblectamina fixis linguisque ausus sperare Echionides cornuaque tenent clausit possit. Omnia putatur. Praeteritae refert ausus; ferebant e primus lora nutat, vici quae mea ipse. Et iter nil spectatae vulnus haerentia iuste et exercebat, sui et.
-
-Eurytus Hector, materna ipsumque ut Politen, nec, nate, ignari, vernum cohaesit sequitur. Vel **mitis temploque** vocatus, inque alis, *oculos nomen* non silvis corpore coniunx ne displicet illa. Crescunt non unus, vidit visa quantum inmiti flumina mortis facto sic: undique a alios vincula sunt iactata abdita! Suspenderat ego fuit tendit: luna, ante urbem Propoetides **parte**.
-
-{{< css.inline >}}
-<style>
-.canon { background: white; width: 100%; height: auto; }
-</style>
-{{< /css.inline >}}

+ 0 - 34
content/post/rich-content.md

@@ -1,34 +0,0 @@
-+++
-author = "Hugo Authors"
-title = "Rich Content"
-date = "2019-03-10"
-description = "A brief description of Hugo Shortcodes"
-tags = [
-    "shortcodes",
-    "privacy",
-]
-+++
-
-Hugo ships with several [Built-in Shortcodes](https://gohugo.io/content-management/shortcodes/#use-hugos-built-in-shortcodes) for rich content, along with a [Privacy Config](https://gohugo.io/about/hugo-and-gdpr/) and a set of Simple Shortcodes that enable static and no-JS versions of various social media embeds.
-<!--more-->
----
-
-## YouTube Privacy Enhanced Shortcode
-
-{{< youtube ZJthWmvUzzc >}}
-
-<br>
-
----
-
-## Twitter Simple Shortcode
-
-{{< twitter_simple 1085870671291310081 >}}
-
-<br>
-
----
-
-## Vimeo Simple Shortcode
-
-{{< vimeo_simple 48912912 >}}

+ 76 - 0
content/post/why-you-dont-need-a-bootcamp.md

@@ -0,0 +1,76 @@
++++
+author = "Steffen Blake"
+title = "Why you don't need a Bootcamp"
+date = "2021-09-18"
+description = "I keep seeing people jump to recommending code bootcamps..."
+tags = [
+    "#ButThatsJustMyOpinionMan",
+    "#Coding"
+]
+categories = [
+    "Opinions",
+    "Coding"
+]
+series = ["Thoughts"]
+aliases = ["async-events-IoT"]
++++
+
+### A Boy and His IDE
+
+It’s the middle of 2006, I am a teen boy who just got my third paycheque from my summer job and lord knows my bank account is begging me to finally put my money to use. There are a plethora of options for a young man without any bills to pay, but I already knew what I wanted.
+
+I bought my first computer that weekend. In line at the shop I excitedly await the package, pumped to get home and finally strike out my own digital space on the machine. You see, up until this point I had been grounded in the world of the woeful shared ‘Family Computer’, an entity most of us 90s kids will fondly remember, most likely nestled in the corner of ones den or living room. Generally a simple machine, designed for browsing the internet and playing solitaire.
+
+But your own laptop, that’s a very different realm. You can do whatever you want, configure it however you want. By god you could even set your own background! I was ecstatic and sure enough spent the entire night putting everything together just how I liked it.
+
+And one of those steps was installing a program that today modern programmers are very familiar with. Visual Studio, at the time quite large in size for an IDE and almost monolithic compared to your usual programs. Downloading something on the scale of Gigabytes took a bit of time in the early 2000s, and up until that point I had not been able to install the software on the family computer due to its (significant) lack of capabilities to handle VS. Not to mention hard drive (or lack thereof) size.
+
+But my shiney new laptop was more than capable, and by the end of that night I had a fresh install running and ready to go. The world was my oyster, I had no idea what I was doing, but I had no intention of letting that stop my ambition. Visual Basic, compared to Visual C++ and C# sounded like a good starting point. I had no idea what the difference was between the three that moment, but I mean, come on… it literally had ‘Basic’ in its name.
+
+So I opened templates up and quickly set to work learning the UI, learning shortcuts, and by the end of the weekend I had my first windows form up running, which I proudly showed to my parents. They were quite impressed, and I kept it to myself how easy it actually was making a button do things. `They didn’t need to know that part…`
+
+But slowly the novelty faded, and I found anything actually worth doing was challenging. Who would have thought programming was hard?
+
+### It Takes a Village
+Now at this time the other large portion of my time was also spent on a very different, but equally consuming, past time. I won’t specify the name, but it was a moderately popular MMORPG of its time. It’s certainly not the first one that will come to your mind most likely, but it is probably the second or third.
+
+As I played the game, now on my own machine (with the graphic settings turned up much higher now compared to the Family Computer I should note!), I slowly began to hear word of something very relevant to my interests. You see, as most MMOs come and go, one thing will stay constant. They often are packed chock full of monotonous, repetitive tasks. Tasks that could theoretically be easily automated… and are. Botting, hacking, cheating… What a wonderful world.
+
+I was intrigued.
+
+So I found the community for my game. And I found them to be quite the agreeable bunch. They were more than welcoming with outstretched arms to invite me into their little world. I spent the rest of that summer learning, no, `devouring` knowledge. By the end of that summer I was completely integrated into their (3rd party) public API, I was learning every inch, and eventually I proudly posted my own contribution.
+
+It started out as something simple, a means for me to put my new skills to use. But it had my name on it and I never felt prouder.
+
+And that’s when the comments started to roll in.
+
+### Purpose
+At first they were pretty simple. Thanks from individuals who had used it, questions regarding documentation (which as a teenager I was admittedly poor at, of course). But then the real key to my progress appeared.
+
+It was nothing big, just a small single post, a few sentences at most. An individual had used my software and was requesting a minor addition, to make it a little bit more useful. I considered the task, it would involve having to delve into areas I hadn’t touched yet, but had heard of. That night I put out version 1.0.1 of the program, with the minor update. This was of course after many hours of Googling, posting on forums and IRC asking for help, and a fair bit of cursing. But I got it.
+
+And that user? Very happy with the update. But of course, once you show your flexibility as a developer, users are more than willing to continue.
+
+More requests started rolling in. Slowly over the next few months my small little program that started as a flex of style suddenly was a pet project. The school year had started again and it was fall, but sitting in class I would sit and doodle ideas, UI layouts, and algorithm possibilities in my notebook.
+
+Math became an intense subject for me, anytime the curriculum turned to a subject relevant to a problem I had been wrestling with in one of my projects (plural, did I mention that? Users had started asking me to make custom solutions for them and I was more than happy to!) I found my hand shooting up. The teachers were curious, some of my puzzling questions were very very specific. Bots in MMOs have to have some very particular trigonometry puzzles to solve you see.
+
+### Moving on
+The cycle kept going for two more years. Visual Basic made way for C# and full .Net stack. I went to University and enrolled in Computer Sciences. And promptly realised a large portion of my courses were teaching me things I was already very familiar with years ago.
+
+And none of the actually important topics for programming were being covered. So I dropped that after three years, switched degrees, took up an Education part time with the hopes of becoming a Math, Physics, and Comp Sci teacher at the High School level.
+
+And the entire time, I was still integrating with and learning from Communities. I created a GitHub, started building a portfolio. I built a server, installed Arch Linux, learned how to use Jira, Bitbucket, and Bamboo. I got very very comfy with command line tools. Ran a Plex server connected to a Samba Share that I piped torrents off Deluge into. And I quietly applied to every single programming position I could find in my city (all while working a full time job as a line cook to pay the bills)
+
+Then, 10 years after starting my journey, I got a phone call. A company I had applied to and went through their full hiring process with wanted an interview. They liked me. I got the job, and have been working there to date. Every day I pick up more and more skills, and I love every minute of it.
+
+### Takeaway
+In the end, every step along the way, my process had one critical thing that a coding boot camp or hackathon will never truly give you.
+
+Purpose to Intent. You can spend hundreds of hours banging your head against the wall for Math Puzzle #74 on `<Website with a bunch of puzzles>` but you’ll find within a few days you burn out.
+
+You can work out your full stack and read all the books, but you’ll find very little sticks and its exhausting to keep going every day that goes by.
+
+But you find yourself a community, you get yourself on some forums browsing feature requests for your baby, you get yourself some pull requests to review to your pet project, and you get yourself some issue reports to deal with on your branch…
+
+Well you won’t ever feel like you have to force yourself to program ever again my friend, I assure you of it.

+ 24 - 1
static/css/custom.css

@@ -1,12 +1,35 @@
-body * {
+html, body.crt {
+  height: auto;
+  min-height: 100vh;
+}
+
+.crt::after {
+  animation: vm-flicker 1s infinite;
+  animation-delay: 1s;
+}
+
+body *, .lead, .display-3 {
   line-height: 20px;
   font-size: 22px;
 }
 
+pre code {
+  line-height: 18px;
+  font-size: 20px;
+}
+
 .bootstra-enable-cursor *, body.bootstra-enable-cursor {
   cursor:text!important;
 }
 
 .bootstra-enable-cursor .nav-link *, .bootstra-enable-cursor a {
   cursor:pointer!important;
+}
+
+pre > code {
+  color: #bbb;
+  width: 100%;
+}
+pre {
+  background-color: #272822;
 }

BIN
static/images/RPI-GPIO.png