
<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Ishuah</title>
    <description>Free Spirit</description>
    <link>https://ishuah.com/</link>
    <atom:link href="https://ishuah.com/feed.xml" rel="self" type="application/rss+xml"/>
    <pubDate>Thu, 11 Apr 2024 14:52:16 +0000</pubDate>
    <lastBuildDate>Thu, 11 Apr 2024 14:52:16 +0000</lastBuildDate>
    <generator>Jekyll v3.9.5</generator>
    
      <item>
        <title>The Mel in Melograno</title>
        <description>&lt;p&gt;&lt;em&gt;Mel and Mansa dining al fresco at Café Melograno&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Mel, Mansa, and I spent the whole month of February in Australia. Why Australia? Because 11 years ago, a feisty 19-year-old Kenyan landed in Perth at the peak of summer, the heat barely melting her excitement. All her hard work paid off, and she got accepted at Edith Cowan University. Over the next five years, she earned her tuition, studied, and graduated nearly top of her class. Years later, she captured my heart, and we started our life together. This feisty woman is Mel. This trip was a journey into her early years.&lt;/p&gt;

&lt;p&gt;One of the first places we visited was Café Melograno, a cornerstone in Mel’s early adulthood. She worked here four out of the five years she spent in Perth. When I first saw it, I realized it was much bigger than what I’d pictured from Mel’s stories. It felt like I was visiting the set of a movie I’d watched countless times. I saw the iconic La Marzocco coffee machine, where she excelled as a barista, and the royal blue awning, which took months to set up because of council restrictions. I also recognized the “dog parking” tree at the front, where patrons left their loyal companions for the company of humans.&lt;/p&gt;

&lt;p&gt;In the time I’ve known Mel, I’ve come to recognize that Café Melograno is hands down the best place she’s ever worked. A big part of that is the people, so I was excited to get to know characters I’d only heard of in stories. I met the stylish Wes and “turbo” Tash, Mel’s former colleagues at Café Melograno. Stylish Wes because he and his partner Simon were always the best-dressed couple at parties. The “turbo” prefix for Tash because she runs an average of four minutes a kilometer, a pace I can only dream of. We had the chance to spend time with Tash and reminisce about the good old days at Café Melograno. It was easy to see that Mel made lasting connections here.&lt;/p&gt;

&lt;p&gt;One of the principal characters from Café Melograno is Sue, or as Mel calls her, “my Australian Mother.” Sue was a part-time waitress at the cafe and a part-time teacher at an early childhood care center. Instead of planning a meeting, Mel chose to surprise her. Sue believed she was meeting Mel’s sister at King’s Park to receive a gift from Kenya. There was shock, disbelief, laughter, and a lot of hugs. We sat with her for about an hour, and I had plenty of time to appreciate why she and Mel were close. Sue was a constant source of support in Mel’s life in Perth, always offering wise advice and a listening ear whenever Mel needed it.&lt;/p&gt;

&lt;p&gt;We can’t talk about Melograno without mentioning Jeff and his partner Greg, who owned Café Melograno when Mel worked there. They sold it two months after Mel left for Kenya. It wasn’t the same without her. From Mel’s description of Jeff, I knew he had a bold sense of humor, did not like holding babies, and loved his BMW coupe. I also know that he took the staff’s side when customers complained, was always at the café, and gave Mel a chance when no one else would. We met Jeff and Greg for lunch, and I quickly understood that Mel’s respect for them was mutual. I also saw Jeff’s quick wit, dry humor in action, and Greg’s calm and composed conversation.&lt;/p&gt;

&lt;p&gt;When Mel was overwhelmed, Jeff hired another barista to help out. He was an excellent barista, worked well with the team, and the customers loved him. There was just one problem; his name was also Jeff. There was a little chaos at the cafe - they had too many Jeffs. Quick-witted Jeff got fed up with all the tomfoolery (Jefffoolery, really) and delegated Barista Jeff to brainstorm a new name for himself. They settled on Peter because Barista Jeff’s middle name sounded similar, and Quick-witted Jeff knew a Peter. From then on, he was Peter, but only the staff called him that. The customers still called him Jeff, much to Quick-witted Jeff’s chagrin. We met Barista Jeff and his fiancé Donna on our last day in Perth. Again, I saw the same pattern of sharing, listening, and being genuine.&lt;/p&gt;

&lt;p&gt;I also met some of the customers who enjoyed Mel’s Java. We bumped into  Lee and Marie at the cafe. They wanted to know if she was back and how soon they could get their first coffee. Then there’s Mark, who kept in touch with Mel through his periodic newsletter. He had just celebrated his 80th birthday. As a testament to his youth, he had an 80s gym-wear-themed party. Stylish Wes and Simon stole the show in red hotpants, crew socks, and a boombox. Mark had to keep reminding them whose birthday it was.&lt;/p&gt;

&lt;p&gt;It would be remiss of me not to mention Irene, Mel’s friend from university. Edith Cowan University is a forty-minute train ride from the city. Mel and Irene met on one of these train rides and struck an instant friendship. To understand this friendship, you must first know that Irene is a Zimbabwean who’s spent time in Kenya. Mel is a Kenyan who grew up in Zimbabwe. There’s an instant kinship formed when you speak a shared tongue in a foreign land. This kinship blossomed into a friendship that’s now ten-plus years old. Irene and her fiance Christian hosted us for dinner and brunch; their company felt like home.&lt;/p&gt;

&lt;p&gt;The people make the place; these are the people who made Perth for Mel, and I am honored to have met them.&lt;/p&gt;
</description>
        <pubDate>Fri, 08 Mar 2024 07:51:03 +0000</pubDate>
        <link>https://ishuah.com/2024/03/08/the-mel-in-melograno/</link>
        <guid isPermaLink="true">https://ishuah.com/2024/03/08/the-mel-in-melograno/</guid>
        
        <category>family</category>
        
        <category>australia</category>
        
        
      </item>
    
      <item>
        <title>Work-Life Balance</title>
        <description>&lt;p&gt;I thought I knew what work-life balance meant. I was a happy-go-lucky bachelor with very little care in the world. I could stay up late, finish a week’s worth of work and then run to the nearest beach to enjoy life. I dedicated months to purely training for half marathons and climbed Kilimanjaro twice, once on a whim.&lt;/p&gt;

&lt;p&gt;Then there was a lull when the pandemic hit. The world slowed down, and my anxiety powered up. 2020 was a very introspective year. I started therapy, and while on this journey, I realized that I wasn’t enjoying life. I was drinking more and adventuring less. Work was going great, but life suffered.&lt;/p&gt;

&lt;p&gt;2021 was a better year. I landed a new job, had several public speaking opportunities, and life was getting more satisfactory. The most memorable event, I sent a text that changed my life.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;“Do you want to grab a coffee sometime?”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I distinctly remember that we didn’t drink coffee. I drank beer, craft beer. My date, Mel, had wine by the glass. There was live music, and we danced. Later that night, Mel texted me.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;“I enjoyed the laughs. Now you need to divulge the agenda.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I trembled a bit as I wrote my reply. Maybe it was the craft beer. I’ve known Mel since 2018; we were colleagues once. We lost touch over the years, but we started talking in mid-2021. Out of the blue, I asked her out with no clear agenda. My ancestors must have been very worried about their lineage.&lt;/p&gt;

&lt;p&gt;And that was the beginning of a life-changing experience. Mel reminded me about my passions in life. A few months after we started dating, she got me guitar lessons after I mentioned how I’d love to learn how to play the guitar. I played for three months and even learned how to tune a guitar. But I’m no Fancy Fingers, so I’m more careful about what I wish for around Mel.&lt;/p&gt;

&lt;p&gt;On the last weekend of January 2022, Mel and I attended Fancy Finger’s concert at The Alchemist. We stayed till the end because we wanted to dance to our favorite song, Daddy Issues.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;“I’m about to be a father, na siko ready.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I remember this night because we found out we were pregnant the very next day. We were scared, happy, shocked, joyful, a blend of emotions. We made our decision.&lt;/p&gt;

&lt;p&gt;Seven months, twenty-two days later, we welcomed Mansa to the world. That was the most life-changing of all my life-changing moments. Life became fuller. In those seven months, I proposed, and we built a home. We didn’t do it alone. I’m grateful for the support structure we have. My mum bought a shipment of baby clothes that will last till Mansa is 5. My mother-in-law traveled eight hours to see her granddaughter as soon as she was born. My father, always with a somber expression, speaks fatherly advice. I cannot fail to mention Esther, my cousin, and our doula. She labored with Mel, taught me how to bathe my daughter, and is always a phone call away when we have questions.&lt;/p&gt;

&lt;p&gt;This essay started with a purpose, but, like every conversation, I ended up talking about my daughter.&lt;/p&gt;

&lt;p&gt;So, what is work-life balance? When you can give your best at work without missing important moments of your life.&lt;/p&gt;

&lt;p&gt;Love you, Mel &amp;amp; Mansa.&lt;/p&gt;
</description>
        <pubDate>Thu, 24 Nov 2022 19:12:38 +0000</pubDate>
        <link>https://ishuah.com/2022/11/24/work-life-balance/</link>
        <guid isPermaLink="true">https://ishuah.com/2022/11/24/work-life-balance/</guid>
        
        <category>family</category>
        
        
      </item>
    
      <item>
        <title>A Memoir on Reference Counting</title>
        <description>&lt;p&gt;&lt;em&gt;Photo by &lt;a href=&quot;https://unsplash.com/@gary_at_unsplash?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Gary Chan&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/s/photos/garbage-collection?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Unsplash&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In 1960, John McCarthy published a paper titled &lt;a href=&quot;http://www-formal.stanford.edu/jmc/recursive.pdf&quot;&gt;Recursive Functions of Symbolic Expressions and Their Computation by Machine, Part I&lt;/a&gt;. In it, he meticulously describes the original implementation of Lisp, the second oldest programming language still in use. As part of the language, McCarthy describes an algorithm that automatically reclaims memory. He calls it a “reclamation cycle”, but in a footnote added in 1995, he writes:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;We already called this process “garbage collection”, but I guess I chickened out of using it in the paper—or else the Research Laboratory of Electronics grammar ladies wouldn’t let me.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;McCarthy coined the term “garbage collection” and introduced the first garbage collection process. His implementation was a tracing algorithm that identifies reachable objects by depth-first search and frees up unreachable objects. Although a remarkable proof of concept, this approach had limitations. McCarthy points out in the paper:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Its efficiency depends upon not coming close to exhausting the available memory with accessible lists. This is because the reclamation process requires several seconds to execute, and therefore must result in the addition of at least several thousand registers to the free-storage list if the program is not to spend most of its time in reclamation.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This statement implies that garbage collection calls become costly when the program runs close to memory capacity.&lt;/p&gt;

&lt;p&gt;In December 1960, another computer scientist, George E. Collins, published a paper titled &lt;a href=&quot;https://dl.acm.org/doi/pdf/10.1145/367487.367501&quot;&gt;A Method for Overlapping and Erasure of Lists&lt;/a&gt;. In this paper, Collins addresses the drawbacks in McCarthy’s approach and introduces reference counting, a more efficient process of reclaiming memory. Speaking of McCarthy’s approach, he writes:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;McCarthy’s solution is very elegant, but unfortunately it contains two sources of inefficiency. First and most important, the time required to carry out this reclamation process is nearly independent of the number of registers reclaimed. Its efficiency thereby diminishes drastically as the memory approaches full utilization. Second, the method as used by McCarthy required that a bit (McCarthy uses the sign bit) be reserved in each word for tagging accessible registers during the survey of accessibility. If, as in our own current application of list processing, much of the atomic data consists of signed integers or floating-point numbers, this results in awkwardness and further loss of efficiency.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Collins’s solution worked by maintaining a count of references (pointers) to each object in memory. When a new pointer is created the counter is incremented. Inversely, the counter is decremented when a pointer is destroyed. When an object’s reference count gets to zero, its memory is automatically reclaimed. To optimize this solution, Collins allocates space for a reference count only when an object has more than two references.&lt;/p&gt;

&lt;p&gt;Collin’s approach, unlike McCarthy’s, is interleaved with program execution, thus better suited for programs where response time is critical. That said, this approach has a subtle drawback in that if objects have a pointer cycle, their reference count can never reach zero and therefore cannot be reclaimed. At the time, the LISP language did not allow cyclic data structures, so this issue did not affect memory reclamation.&lt;/p&gt;

&lt;p&gt;Some languages still use reference counting without any special handling of pointer cycles. Perl 5, for instance, supports weak references, allowing programmers to deliberately avoid creating reference cycles. If data structures contain reference cycles, Perl runtime will only reclaim them once the parent thread dies. This is considered a welcome tradeoff, as opposed to implementing overhead cycle detection that would slow down execution time.&lt;/p&gt;

&lt;p&gt;Python implements reference counting too. To address reference cycles, Python implements an algorithm called &lt;a href=&quot;https://rushter.com/blog/python-garbage-collector/#:~:text=no%20longer%20needed.-,Generational%20garbage%20collector,-Why%20do%20we&quot;&gt;generational cyclic garbage collection&lt;/a&gt;. Reference counting runs in real-time, while cyclic garbage collection runs periodically. One of the reasons why Python maintains the GIL is to protect the reference count from race conditions.&lt;/p&gt;

&lt;p&gt;In June 2001, David Bacon and V.T Rajan published &lt;a href=&quot;https://pages.cs.wisc.edu/~cymen/misc/interests/Bacon01Concurrent.pdf&quot;&gt;Concurrent Cycle Collection in Reference Counted Systems&lt;/a&gt;. In this paper, they address reference cycles collection in reference counted systems by building on McCarthy’s prior work. As Bacon and Rajan observed:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;…when a reference count is decremented and does not reach zero, it is considered as a candidate root of a garbage cycle, and a local search is performed. This is a depth-first search which subtracts out counts due to internal pointers.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Bacon and Rajan present two algorithms, a synchronous algorithm, and a concurrent algorithm. The concurrent algorithm is based on the synchronous algorithm, with additional safety checks to guard against race conditions. PHP is a reference-counted language that applies reference cycle collection. As of version 5.3, PHP implements the synchronous algorithm to handle circular reference memory leaks.&lt;/p&gt;

&lt;p&gt;Objective-C implements &lt;a href=&quot;https://en.wikipedia.org/wiki/Automatic_Reference_Counting#cite_note-clang-4&quot;&gt;Automatic Reference Counting&lt;/a&gt;, which differs from garbage collection in that there’s no background process that deallocates unused memory. Instead, ARC works by inserting reference counting code at compile-time, saving the developer the hard work of manually managing memory. Objective C has a rigid set of &lt;a href=&quot;https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/MemoryMgmt.html&quot;&gt;memory management rules&lt;/a&gt;. The team behind Objective C’s compiler realized that these rules were dependable enough to be automated as part of the compiler. ARC does not handle reference cycles, but as with Perl, this is considered a minor setback.&lt;/p&gt;

&lt;p&gt;Reference counting is still a relevant topic in Computer Science. In 2019, Leonardo de Moura and Sebastian Ullrich published &lt;a href=&quot;https://arxiv.org/pdf/1908.05647.pdf&quot;&gt;Counting Immutable Beans: Reference Counting Optimized for Purely Functional Programming&lt;/a&gt;. In this paper, they describe in detail a reference counting memory reclamation strategy for &lt;a href=&quot;https://github.com/leanprover/lean4&quot;&gt;Lean 4&lt;/a&gt;, an open source functional programming language. The performance numbers in the paper are impressive, Lean 4 beats most of the compilers in all the tests. However, Moura and Ulrich state in the paper:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;We remark that in Lean and λpure, it is not possible to create cyclic data structures. Thus, one of the main criticisms against reference counting does not apply.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Unlike  Lean and λpure, most functional languages have no restrictions on cyclic data structures, meaning the algorithm proposed won’t work for them. Maybe someone will come up with a cyclic reference collection algorithm for functional languages in the near future.&lt;/p&gt;

&lt;h3 id=&quot;references&quot;&gt;References:&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://www-formal.stanford.edu/jmc/recursive.pdf&quot;&gt;Recursive Functions of Symbolic Expressions and Their Computation by Machine, Part I&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://dl.acm.org/doi/pdf/10.1145/367487.367501&quot;&gt;A Method for Overlapping and Erasure of Lists&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://pages.cs.wisc.edu/~cymen/misc/interests/Bacon01Concurrent.pdf&quot;&gt;Concurrent Cycle Collection in Reference Counted Systems&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://arxiv.org/pdf/1908.05647.pdf&quot;&gt;Counting Immutable Beans: Reference Counting Optimized for Purely Functional Programming&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://realpython.com/python-gil/&quot;&gt;What Is the Python Global Interpreter Lock (GIL)?&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://rushter.com/blog/python-garbage-collector/&quot;&gt;Garbage collection in Python: things you need to know&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/MemoryMgmt.html&quot;&gt;Advanced Memory Management in Objective-C&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Mon, 30 May 2022 18:11:37 +0000</pubDate>
        <link>https://ishuah.com/2022/05/30/a-memoir-on-reference-counting/</link>
        <guid isPermaLink="true">https://ishuah.com/2022/05/30/a-memoir-on-reference-counting/</guid>
        
        <category>memory-management</category>
        
        <category>garbage-collection</category>
        
        
      </item>
    
      <item>
        <title>SIMD intrinsics: A Benchmark Study</title>
        <description>&lt;p&gt;&lt;em&gt;Photo by &lt;a href=&quot;https://unsplash.com/@enric_moreu?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Enric Moreu&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/s/photos/math-numbers?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Unsplash&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;A few weeks ago, I came across this interesting paper, &lt;a href=&quot;https://cs.paperswithcode.com/paper/parsing-gigabytes-of-json-per-second&quot;&gt;Parsing Gigabytes of JSON per Second&lt;/a&gt;. 2.5 Gigabytes of JSON per second on commodity processors, to be precise. Three pages into the paper, I discovered that I needed more background knowledge on &lt;a href=&quot;https://en.wikipedia.org/wiki/SIMD&quot;&gt;SIMD&lt;/a&gt; instructions. SIMD is like the Mona Lisa, I have an idea of what it looks like, but that representation is far from the actual painting. After reading a few articles and numerous build errors later, I’m confident enough to write on the topic.&lt;/p&gt;

&lt;figure&gt;
  &lt;img class=&quot;inverted-svg&quot; src=&quot;/images/simd.jpg&quot; width=&quot;450&quot; /&gt;
&lt;/figure&gt;

&lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/SIMD&quot;&gt;SIMD&lt;/a&gt;, short for Single Instruction Multiple Data, is a parallel processing model that applies a single operation to multiple sets of vectors. If you’re not familiar with SIMD, there are several great introductory &lt;a href=&quot;http://const.me/articles/simd/simd.pdf&quot;&gt;articles&lt;/a&gt; and &lt;a href=&quot;https://nccastaff.bournemouth.ac.uk/jmacey/Lectures/SIMD/#/&quot;&gt;slides&lt;/a&gt; on the topic.&lt;/p&gt;

&lt;h2 id=&quot;the-setup&quot;&gt;the setup&lt;/h2&gt;
&lt;p&gt;I chose to benchmark the dot product operation because it’s a cheap operation, constrained only by the input size.
The input consists of two float vectors, &lt;em&gt;a&lt;/em&gt; and &lt;em&gt;b&lt;/em&gt;, each of size &lt;em&gt;n&lt;/em&gt;.&lt;/p&gt;
&lt;figure&gt;
  &lt;img class=&quot;inverted-svg&quot; src=&quot;/images/dot-product.svg&quot; /&gt;
&lt;/figure&gt;

&lt;p&gt;Where &lt;em&gt;a&lt;/em&gt; and &lt;em&gt;b&lt;/em&gt; are vectors, &lt;em&gt;n&lt;/em&gt; is the size of the vectors, and &lt;em&gt;ai&lt;/em&gt; &amp;amp; &lt;em&gt;bi&lt;/em&gt; are vector components belonging to vectors &lt;em&gt;a&lt;/em&gt; &amp;amp; &lt;em&gt;b&lt;/em&gt;.&lt;/p&gt;
&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// Representation in pseudocode&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;number&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;dot_product&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;sum&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;sum&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sum&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The input size is computed as &lt;em&gt;n&lt;/em&gt; &lt;strong&gt;x&lt;/strong&gt; float size (bytes) &lt;strong&gt;x&lt;/strong&gt; number of vectors (2).  To avoid memory shortage, I capped the input size at 256 MB (two 128 MB vectors).&lt;/p&gt;

&lt;p&gt;I ran all my benchmarks on a Quad core Intel Core i7-8550U CPU with a 1MB L2 cache and an 8MB L3 cache. This CPU supports AVX2.&lt;/p&gt;

&lt;h2 id=&quot;implementation&quot;&gt;implementation&lt;/h2&gt;
&lt;p&gt;I started by comparing two functions, a scalar implementation, and a vectorized implementation.&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/ishuah/aef6e8a54307e7a99230f4f357b4ba1f.js&quot;&gt;&lt;/script&gt;

&lt;p&gt;The first implementation matches the pseudocode example above. A loop that runs &lt;em&gt;n&lt;/em&gt; times, adding the result of &lt;em&gt;a[i]&lt;/em&gt; &lt;strong&gt;x&lt;/strong&gt; &lt;em&gt;b[i]&lt;/em&gt; to the variable &lt;em&gt;sum&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;The vectorized implementation follows the same pattern but has several key differences.
Firstly, all the key variables are &lt;em&gt;type&lt;/em&gt; &lt;strong&gt;__m256&lt;/strong&gt;, a data type representing a 256-bit SIMD register. In simple terms, this is a vector of eight 32-bit floating-point  values.&lt;/p&gt;

&lt;p&gt;The loop count increments by eight because the function &lt;a href=&quot;https://www.intel.com/content/www/us/en/develop/documentation/cpp-compiler-developer-guide-and-reference/top/compiler-reference/intrinsics/intrinsics-for-intel-advanced-vector-extensions/intrinsics-for-load-and-store-operations-1/mm256-loadu-ps.html&quot;&gt;&lt;strong&gt;_mm256_loadu_ps&lt;/strong&gt;&lt;/a&gt; (L21, L22) loads eight floating-point values from unaligned memory into a &lt;strong&gt;__m256&lt;/strong&gt; vector. The function &lt;a href=&quot;https://www.intel.com/content/www/us/en/develop/documentation/cpp-compiler-developer-guide-and-reference/top/compiler-reference/intrinsics/intrinsics-for-intel-advanced-vector-extensions-2/intrinsics-for-fused-multiply-add-operations/mm-fmadd-ps-mm256-fmadd-ps.html&quot;&gt;&lt;strong&gt;_mm256_fmadd_ps&lt;/strong&gt;&lt;/a&gt; multiplies matching elements from the first two vectors and adds them to the value in the matching index of the third vector. To ensure correct computations, I added an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;assert&lt;/code&gt; on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;L16&lt;/code&gt; to ensure the input arrays size is a multiple of 8.&lt;/p&gt;

&lt;p&gt;The function &lt;a href=&quot;https://www.intel.com/content/www/us/en/develop/documentation/cpp-compiler-developer-guide-and-reference/top/compiler-reference/intrinsics/intrinsics-for-intel-advanced-vector-extensions/intrinsics-for-load-and-store-operations-1/mm256-storeu-ps.html&quot;&gt;&lt;strong&gt;_mm256_storeu_ps&lt;/strong&gt;&lt;/a&gt; moves eight floating-point values from a &lt;strong&gt;__m256&lt;/strong&gt; vector to an unaligned memory location.&lt;/p&gt;

&lt;h3 id=&quot;compare-results&quot;&gt;compare results&lt;/h3&gt;

&lt;p&gt;I used &lt;a href=&quot;https://github.com/google/benchmark&quot;&gt;google/benchmark&lt;/a&gt; to run my benchmarks.&lt;/p&gt;

&lt;pre&gt;Benchmark                            Time  CPU  Time Old  Time New  CPU Old  CPU New
-----------------------------------------------------------------------------------
&lt;font color=&quot;#586E75&quot;&gt;[dot_product vs. dot_product_256]/1&lt;/font&gt;&lt;font color=&quot;#93A1A1&quot;&gt;  -0.8158  -0.8158&lt;/font&gt;  155357  28624  155354  28623
&lt;font color=&quot;#586E75&quot;&gt;[dot_product vs. dot_product_256]/2&lt;/font&gt;&lt;font color=&quot;#93A1A1&quot;&gt;  -0.8117  -0.8117&lt;/font&gt;  302681  56987  302667  56987
&lt;font color=&quot;#586E75&quot;&gt;[dot_product vs. dot_product_256]/4&lt;/font&gt;&lt;font color=&quot;#93A1A1&quot;&gt;  -0.8004  -0.8004&lt;/font&gt;  595212  118786  595200  118786
&lt;font color=&quot;#586E75&quot;&gt;[dot_product vs. dot_product_256]/8&lt;/font&gt;&lt;font color=&quot;#93A1A1&quot;&gt;  -0.7486  -0.7486&lt;/font&gt;  1260476  316823  1260469  316824
&lt;font color=&quot;#586E75&quot;&gt;[dot_product vs. dot_product_256]/16&lt;/font&gt;&lt;font color=&quot;#93A1A1&quot;&gt;  -0.6466  -0.6466&lt;/font&gt;  2711183  958018  2711140  957993
&lt;font color=&quot;#586E75&quot;&gt;[dot_product vs. dot_product_256]/32&lt;/font&gt;&lt;font color=&quot;#93A1A1&quot;&gt;  -0.6019  -0.6019&lt;/font&gt;  5450561  2169969  5450430  2169929
&lt;font color=&quot;#586E75&quot;&gt;[dot_product vs. dot_product_256]/64&lt;/font&gt;&lt;font color=&quot;#93A1A1&quot;&gt;  -0.5795  -0.5795&lt;/font&gt;  11411248  4797997  11411161  4797871
&lt;font color=&quot;#586E75&quot;&gt;[dot_product vs. dot_product_256]/128&lt;/font&gt;&lt;font color=&quot;#93A1A1&quot;&gt;  -0.5781  -0.5781&lt;/font&gt;  23172851  9776048  23172549  9775924
&lt;font color=&quot;#586E75&quot;&gt;[dot_product vs. dot_product_256]/256&lt;/font&gt;&lt;font color=&quot;#93A1A1&quot;&gt;  -0.5554  -0.5554&lt;/font&gt;  44252652  19673917  44251304  19673560
&lt;/pre&gt;

&lt;p&gt;Each row represents a comparison between &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dot_product&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dot_product_256&lt;/code&gt; with different input sizes. The values in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Time&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CPU&lt;/code&gt; columns are calculated as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;(new - old) / |old|&lt;/code&gt; (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;x 100&lt;/code&gt; to get percentage). The last four columns are time measurements in nanoseconds.&lt;/p&gt;

&lt;p&gt;I expected &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dot_product_256&lt;/code&gt; to be faster, but I did not anticipate the big gap. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;81.58%&lt;/code&gt; faster with an input size of 1MB, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;55.54%&lt;/code&gt; faster with an input size of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;256MB&lt;/code&gt;.&lt;/p&gt;

&lt;figure&gt;
  &lt;img class=&quot;inverted-svg&quot; src=&quot;/images/simd-graph.svg&quot; /&gt;
&lt;/figure&gt;

&lt;h3 id=&quot;analysis-i&quot;&gt;analysis I&lt;/h3&gt;

&lt;p&gt;You can view the full analysis on &lt;a href=&quot;https://godbolt.org/z/G1aG5xf3q&quot;&gt;Compiler Explorer&lt;/a&gt;.&lt;/p&gt;

&lt;figure&gt;
  &lt;img class=&quot;inverted-svg&quot; src=&quot;/images/compiler-explorer-analysis-i.png&quot; /&gt;
&lt;/figure&gt;

&lt;p&gt;The scalar implementation has three AVX instructions corresponding to the loop statement &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sum += a[0] * b[0];&lt;/code&gt;:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.felixcloutier.com/x86/MOVSS.html&quot;&gt;vmovss&lt;/a&gt; unloads the pointer value &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;a[0]&lt;/code&gt; into a 128 bit register.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.felixcloutier.com/x86/MULSS.html&quot;&gt;vmulss&lt;/a&gt; multiplies the value &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;a[0]&lt;/code&gt; with the value at pointer reference &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;b[0]&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.felixcloutier.com/x86/ADDSS.html&quot;&gt;vaddss&lt;/a&gt; adds the result from the multiplication operation above to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sum&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Compare that with the vectorized implementation instructions:&lt;/p&gt;

&lt;figure&gt;
  &lt;img class=&quot;inverted-svg&quot; src=&quot;/images/compiler-explorer-analysis-i-avx.png&quot; /&gt;
&lt;/figure&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.felixcloutier.com/x86/MOVUPS.html&quot;&gt;vmovups&lt;/a&gt; loads eight floating-point values from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;a&lt;/code&gt; into a SIMD register.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.felixcloutier.com/x86/VFMADD132PS%3AVFMADD213PS%3AVFMADD231PS.html&quot;&gt;vfmadd231ps&lt;/a&gt; multiplies the eight floating-point values loaded from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;a&lt;/code&gt; with eight floating-point values loaded from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;b&lt;/code&gt; and adds the result to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sum&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;xmm0-xmm7&lt;/code&gt; registers used in the scalar implementations are 128 bit wide. In contrast, the vectorized implementation uses &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ymm0-ymm7&lt;/code&gt; registers, which are 256 bit wide. The letters FMA in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vfmadd231ps&lt;/code&gt; stand for &lt;a href=&quot;https://link.springer.com/content/pdf/10.1007%2F978-0-8176-4705-6_5.pdf&quot;&gt;Fused Multiply-Add&lt;/a&gt;, the technical term for the floating-point operation of multiplication and addition in one step.&lt;/p&gt;

&lt;h2 id=&quot;compiler-optimization&quot;&gt;compiler optimization&lt;/h2&gt;
&lt;p&gt;Up until this point, I’ve been using conservative optimization compiler flags &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-O3&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-march=native&lt;/code&gt;. I wanted to test another flag &lt;a href=&quot;https://kristerw.github.io/2021/10/19/fast-math/&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-ffast-math&lt;/code&gt;&lt;/a&gt;, which tells the compiler to perform more aggressive floating-point optimizations. Very similar to cutting the brakes on your car to make it go faster.&lt;/p&gt;

&lt;pre&gt;Benchmark                            Time  CPU  Time Old  Time New  CPU Old  CPU New
-----------------------------------------------------------------------------------
&lt;font color=&quot;#586E75&quot;&gt;[dot_product vs. dot_product_256]/1&lt;/font&gt;&lt;font color=&quot;#FDF6E3&quot;&gt;  -0.0500  -0.0500&lt;/font&gt;  25535  24258  25535  24257
&lt;font color=&quot;#586E75&quot;&gt;[dot_product vs. dot_product_256]/2&lt;/font&gt;&lt;font color=&quot;#FDF6E3&quot;&gt;  -0.0556  -0.0556&lt;/font&gt;  51505  48644  51504  48642
&lt;font color=&quot;#586E75&quot;&gt;[dot_product vs. dot_product_256]/4&lt;/font&gt;&lt;font color=&quot;#FDF6E3&quot;&gt;  -0.0546  -0.0546&lt;/font&gt;  105722  99951  105718  99949
&lt;font color=&quot;#586E75&quot;&gt;[dot_product vs. dot_product_256]/8&lt;/font&gt;&lt;font color=&quot;#93A1A1&quot;&gt;  -0.1465  -0.1465&lt;/font&gt;  385659  329162  385646  329135
&lt;font color=&quot;#586E75&quot;&gt;[dot_product vs. dot_product_256]/16&lt;/font&gt;&lt;font color=&quot;#FDF6E3&quot;&gt; +0.0208 +0.0208&lt;/font&gt;  949516  969264  949485  969199
&lt;font color=&quot;#586E75&quot;&gt;[dot_product vs. dot_product_256]/32&lt;/font&gt;&lt;font color=&quot;#FDF6E3&quot;&gt;  -0.0118  -0.0118&lt;/font&gt;  2301268  2274091  2301235  2274021
&lt;font color=&quot;#586E75&quot;&gt;[dot_product vs. dot_product_256]/64&lt;/font&gt;&lt;font color=&quot;#FDF6E3&quot;&gt;  +0.0037  +0.0037&lt;/font&gt;  4863097  4881084  4862852  4880919
&lt;font color=&quot;#586E75&quot;&gt;[dot_product vs. dot_product_256]/128&lt;/font&gt;&lt;font color=&quot;#FDF6E3&quot;&gt;  +0.0220  +0.0220&lt;/font&gt;  9883521  10101137  9883309  10101054
&lt;font color=&quot;#586E75&quot;&gt;[dot_product vs. dot_product_256]/256&lt;/font&gt;&lt;font color=&quot;#FDF6E3&quot;&gt;  +0.0273  +0.0273&lt;/font&gt;  19079586  19601290  19079604  19600141
&lt;/pre&gt;

&lt;p&gt;Both functions benchmark at almost equal speeds. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dot_product_256&lt;/code&gt; has the biggest lead, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;14.65%&lt;/code&gt; (input size, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;8 MB&lt;/code&gt;). &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dot_product&lt;/code&gt; is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2.73%&lt;/code&gt; faster on the largest input size, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;256 MB&lt;/code&gt;.&lt;/p&gt;

&lt;figure&gt;
  &lt;img class=&quot;inverted-svg&quot; src=&quot;/images/ffast-math-graph.svg&quot; /&gt;
&lt;/figure&gt;

&lt;h3 id=&quot;analysis-ii&quot;&gt;analysis II&lt;/h3&gt;

&lt;p&gt;You can view the full analysis on &lt;a href=&quot;https://godbolt.org/z/nv4ebcPq4&quot;&gt;Compiler Explorer&lt;/a&gt;&lt;/p&gt;

&lt;figure&gt;
  &lt;img class=&quot;inverted-svg&quot; src=&quot;/images/compiler-explorer-analysis-ii.png&quot; /&gt;
&lt;/figure&gt;

&lt;p&gt;The scalar implementation compiled looks very different. The loop statement &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sum += a[0] * b[0];&lt;/code&gt; now has 29 corresponding Assembly instructions. The compiler applied &lt;a href=&quot;https://en.wikipedia.org/wiki/Loop_unrolling&quot;&gt;loop unrolling&lt;/a&gt;, an optimization strategy that minimizes the cost of loop overhead. The loop unrolls in four iterations. By examining one iteration, you’ll notice the use of 256 bit registers and SIMD intrinsics.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-assembly&quot;&gt;# First iteration: L33 - L40
vmovups ymm4, ymmword ptr [rsi + 4*rdx]
vmovups ymm5, ymmword ptr [rsi + 4*rdx + 32]
vmovups ymm6, ymmword ptr [rsi + 4*rdx + 64]
vmovups ymm7, ymmword ptr [rsi + 4*rdx + 96]
vfmadd132ps   ymm4, ymm0, ymmword ptr [rdi + 4*rdx] # ymm4 = (ymm4 * mem) + ymm0
vfmadd132ps   ymm5, ymm1, ymmword ptr [rdi + 4*rdx + 32] # ymm5 = (ymm5 * mem) + ymm1
vfmadd132ps   ymm6, ymm2, ymmword ptr [rdi + 4*rdx + 64] # ymm6 = (ymm6 * mem) + ymm2
vfmadd132ps   ymm7, ymm3, ymmword ptr [rdi + 4*rdx + 96] # ymm7 = (ymm7 * mem) + ymm3
&lt;/code&gt;&lt;/pre&gt;

&lt;h1 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;Thanks to compiler optimization, the scalar implementation matches the vector implementation performance-wise. Both cases use SIMD registers and intrinsics, whether intentionally written or optimized later by the compiler. The optimized implementations handle larger data sets faster, &lt;a href=&quot;https://lemire.me/blog/2018/07/05/how-quickly-can-you-compute-the-dot-product-between-two-large-vectors/&quot;&gt;but they have limits too&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Hardware, specifically the CPU, is the determining factor when optimizing with SIMD. Most modern CPUs support &lt;a href=&quot;https://en.wikipedia.org/wiki/Advanced_Vector_Extensions#:~:text=CPUs%20with%20AVX2%5Bedit%5D&quot;&gt;AVX2&lt;/a&gt;, fewer support &lt;a href=&quot;https://en.wikipedia.org/wiki/AVX-512#:~:text=F%2C%20VL%2C%20BW-,CPUs%20with%20AVX-512,-%5Bedit%5D&quot;&gt;AVX2-512&lt;/a&gt; (512 bit wide registers).&lt;/p&gt;
</description>
        <pubDate>Sun, 19 Dec 2021 19:12:21 +0000</pubDate>
        <link>https://ishuah.com/2021/12/19/a-benchmark-study-simd-intrinsics/</link>
        <guid isPermaLink="true">https://ishuah.com/2021/12/19/a-benchmark-study-simd-intrinsics/</guid>
        
        <category>SIMD</category>
        
        <category>AVX2</category>
        
        <category>vector-intrinsics</category>
        
        
      </item>
    
      <item>
        <title>Build A Simple Terminal Emulator In 100 Lines of Golang</title>
        <description>&lt;p&gt;&lt;em&gt;Photo by &lt;a href=&quot;https://unsplash.com/@ngeshlew?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Lewis Ngugi&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/photos/f5pTwLHCsAg&quot;&gt;Unsplash&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In a previous &lt;a href=&quot;https://ishuah.com/2021/02/04/understanding-the-linux-tty-subsystem/&quot;&gt;article&lt;/a&gt;, I wrote a brief introduction to the current state of the TTY Subsystem. This article builds on the concepts covered in that article, adding a practical understanding of how the TTY Subsystem works. We’re building a simple &lt;a href=&quot;https://en.wikipedia.org/wiki/Terminal_emulator&quot;&gt;terminal emulator&lt;/a&gt; in Golang. This installment is the second article in my ‘terminal emulator’ series.&lt;/p&gt;

&lt;h2 id=&quot;the-user-interface&quot;&gt;the user interface&lt;/h2&gt;
&lt;p&gt;The first thing we’re going to build is the user interface. It’s nothing fancy, just a triangle with legible text in it.
I’ll use the &lt;a href=&quot;https://github.com/fyne-io/fyne&quot;&gt;Fyne UI Toolkit&lt;/a&gt;. It’s mature and reasonably &lt;a href=&quot;https://developer.fyne.io/&quot;&gt;documented&lt;/a&gt;. 
Here’s our first iteration:&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/ishuah/6b5b97131639c7ce410abb7b9caecec3.js&quot;&gt;&lt;/script&gt;

&lt;p&gt;The program above uses the Fyne UI API to render a text grid with the text “I’m on a terminal!”.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/01-ui.png&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;pseudoterminal&quot;&gt;pseudoterminal&lt;/h2&gt;
&lt;p&gt;The next step involves connecting to the TTY driver that lives in the kernel. We’ll be using the &lt;a href=&quot;https://en.wikipedia.org/wiki/Pseudoterminal&quot;&gt;pseudoterminal&lt;/a&gt; for this task. Like the TTY driver, the pseudoterminal lives in the OS kernel. It consists of a pair of pseudo-devices, a pty master, and a pty slave.&lt;/p&gt;

&lt;figure&gt;&lt;img src=&quot;/images/pseudo-terminal.jpg&quot; /&gt;&lt;figcaption&gt;The pty master and pty slave communicate with each other through the TTY driver&lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;The pty slave receives all its input from the pty master. It also sends all its output to pty master. The pty master sends keystrokes from the keyboard to the pty slave. It also prints output from the pty slave to the display.
I’ll use &lt;a href=&quot;https://github.com/creack/pty&quot;&gt;pty&lt;/a&gt;, a Go package for interfacing with Unix pseudoterminals.&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/ishuah/e54a5445cf5ec0352915a508f1955bbd.js&quot;&gt;&lt;/script&gt;

&lt;p&gt;In the code above, lines 22-30 handle starting the bash process. We now have a pty master pointer p (line 23).&lt;/p&gt;

&lt;p&gt;Line 32 writes the characters “ls” and the return carriage byte to the pty master. As explained in the diagram above, the pty master sends these characters to the pty slave. Simply put, we’ve sent a command to the bash process.&lt;/p&gt;

&lt;p&gt;The program above results in a text grid with an unordered list of items in your current directory.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/02-pseudoterminal.png&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;input-from-keyboard&quot;&gt;input from keyboard&lt;/h2&gt;
&lt;p&gt;We’re now going to read input from the keyboard and write to the pty master. The Fyne UI toolkit provides an effortless way of capturing keyboard input.&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/ishuah/6e39845670b156ffae429b986d283d13.js&quot;&gt;&lt;/script&gt;

&lt;p&gt;SetOnTypedKey captures special keypress events and passes them to our callback function, onTypedKey. Special keys include the escape key, enter key, backspace, etc. Our callback function only handles one special keypress event, the enter keypress.&lt;/p&gt;

&lt;p&gt;SetOnTypedRune works very similarly to SetOnTypedKey, except instead of special keypress events, it captures character keypress events. The attached callback function onTypedRune writes the characters to the pty master.&lt;/p&gt;

&lt;p&gt;I’ve also added a goroutine that reads from the pty master and writes to our UI text grid. There’s no buffer or cursor management. The result is beautiful chaos.&lt;/p&gt;

&lt;p&gt;When you run the program above and type a command, let’s say ‘ls -al.’ You should see the UI update with the expected output. If you like throwing caution to the wind, run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ping 8.8.8.8&lt;/code&gt;.&lt;/p&gt;

&lt;figure&gt;
    &lt;img src=&quot;/images/htop-view-germ.png&quot; /&gt;
    &lt;figcaption&gt;Bash runs as a subprocess, spawned by our program. Bash receives the command `ping 8.8.8.8` and spawns a subprocess.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;We’re not handling signals yet, so Ctrl-C will not stop the job. You’ll have to terminate the terminal emulator.&lt;/p&gt;

&lt;h2 id=&quot;print-to-screen&quot;&gt;print to screen&lt;/h2&gt;
&lt;p&gt;So far, we can type commands on our terminal emulator, receive command output, and chaotically print to our UI. Let’s make a few improvements on how we print to screen. We’ll update our display goroutine to display the pty output history instead of just printing the last line on the screen. The pseudoterminal doesn’t manage output history. We’ll have to handle it ourselves using an output buffer.&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/ishuah/fa78f31e3ec1cc3f84ffe0a25dd1cf17.js&quot;&gt;&lt;/script&gt;

&lt;p&gt;Our output buffer comes in the form of a slice of rune slices. I’ve declared a constant MaxBufferSize to limit buffer size because this is what sane people do. You’re probably wondering why I used a slice instead of an array. We’ll come to that in a bit.&lt;/p&gt;

&lt;p&gt;In our previous iteration, we read from pty master in chunks of 256 bytes. I’ve updated our reader goroutine to use a &lt;a href=&quot;https://golang.org/pkg/bufio/#Reader&quot;&gt;bufio.Reader&lt;/a&gt;. The bufio.Reader type has a method &lt;a href=&quot;https://golang.org/pkg/bufio/#Reader.ReadRune&quot;&gt;ReadRune&lt;/a&gt; that returns a single rune and its size in bytes. This method feeds our output buffer one rune at a time.&lt;/p&gt;

&lt;p&gt;Line 72 - 79 defines an if block that checks whether the read rune is a newline character. If it is, we know that we’re about to append a new line. Before we do this, we first check whether the buffer is at capacity (line 73). If it is, we pop the first line to create space for the new line. MaxBufferSize is 16. This way, we always have a buffer of length 16 holding the last 16 lines of pty master output.&lt;/p&gt;

&lt;p&gt;The display functionality is now running on its goroutine. It redraws the display every 100 milliseconds.&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;conclusion&lt;/h2&gt;
&lt;p&gt;We now have a simple terminal emulator in &lt;del&gt;100&lt;/del&gt; 106 lines of Go! Of course, there’s still a long way to go before our tiny program can be called a functional terminal emulator. The next articles in this series will cover ansi escape codes, special keys, signals, and the all-powerful cursor.&lt;/p&gt;
</description>
        <pubDate>Wed, 10 Mar 2021 06:13:57 +0000</pubDate>
        <link>https://ishuah.com/2021/03/10/build-a-terminal-emulator-in-100-lines-of-go/</link>
        <guid isPermaLink="true">https://ishuah.com/2021/03/10/build-a-terminal-emulator-in-100-lines-of-go/</guid>
        
        <category>linux</category>
        
        <category>tty</category>
        
        <category>terminal-emulator</category>
        
        
      </item>
    
      <item>
        <title>Understanding The Linux TTY Subsystem</title>
        <description>&lt;p&gt;&lt;em&gt;Photo by &lt;a href=&quot;https://unsplash.com/@_imkiran?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Sai Kiran Anagani&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/s/photos/linux?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Unsplash&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;TTY stands for TeleTYpe. If you Google the word teletype, a picture of a device that looks like a typewriter shows up. How did a typewriter become an essential part of the Linux operating system?&lt;/p&gt;

&lt;h2 id=&quot;how-it-all-started&quot;&gt;how it all started&lt;/h2&gt;
&lt;p&gt;The teletype came about through a series of innovations around message transmission on electric channels. It has a rich history going back to the 1840s. Several innovations and collaborations led to the development of the &lt;a href=&quot;https://en.wikipedia.org/wiki/Telex&quot;&gt;Telex exchange network&lt;/a&gt; in the late 1920s. Telex eventually grew to over 100,000 connections worldwide and was vital in global communication post World War II.&lt;/p&gt;

&lt;p&gt;Meanwhile, computer technology was progressing. Earlier computers could only run one program at a time but in the 1960s, multiprogramming computers appeared on the market. These computers could interact with users in realtime via a command-line interface. There was suddenly a need for input and output devices. Instead of building new I/O machines, pragmatic engineers reused existing teletypes. Teletypes were already on the market, and they fit the use case perfectly as physical terminals for mainframe computers.&lt;/p&gt;

&lt;p&gt;Users could now type commands on the teletype and receive the computer output via punched tape. Later versions of the teletype were completely electronic and utilized electronic screens. Users could move the cursor and clear the screen, features unavailable on printed paper teletypes.&lt;/p&gt;

&lt;h2 id=&quot;the-teletype-as-a-physical-terminal&quot;&gt;the teletype as a physical terminal&lt;/h2&gt;
&lt;figure&gt;&lt;img src=&quot;/images/teletype-terminal.jpg&quot; /&gt;&lt;figcaption&gt;The Teletype terminal&lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;A physical line connects the teletype to the Universal Asynchronous Receiver and Transmitter on the computer. This physical line consists of two cables, an input and output cable. When a user types on the terminal, the input cable sends the keystrokes to the UART driver, which sends the keystrokes to the line discipline layer. The line discipline does three things:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;It echoes the keystrokes to the output cable (back to the terminal/teletype), so the user can see what they’ve typed&lt;/li&gt;
  &lt;li&gt;It manages the character buffer.&lt;/li&gt;
  &lt;li&gt;It handles special editing commands (erase, backspace, clear line).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When the user presses the enter key, line discipline sends the buffered characters to the TTY driver, which passes the characters to the foreground process attached to the TTY.
The UART driver, line discipline, and the TTY driver make up the TTY device.&lt;/p&gt;

&lt;h2 id=&quot;terminal-emulators&quot;&gt;terminal emulators&lt;/h2&gt;
&lt;p&gt;As technology improved, computers shrunk in size, and teletypes became cumbersome. Software emulated teletypes replaced physical teletypes. These terminal emulators work in the same way as their physical counterparts, the only difference being that there are no physical lines and UART connections. Examples of terminal emulators include xterm and the gnome-terminal.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/terminal-emulator.jpg&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;pseudo-terminals-pty&quot;&gt;pseudo-terminals (PTY)&lt;/h2&gt;
&lt;p&gt;The whole TTY subsystem residing in the kernel made terminal interactions inflexible. The solution was to move terminal emulation to the userland, leaving line discipline and the TTY driver intact in the kernel. The PTY consists of two parts:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;The PTY master (PTMX) - attached to the terminal emulator&lt;/li&gt;
  &lt;li&gt;The PTY slave  (PTS) - provides processes with an interface to the PTY master&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/images/pseudo-terminal.jpg&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Note: the line discipline instance is not evoked when the TTY driver is sending user program output to PTY master.&lt;/p&gt;

&lt;h2 id=&quot;shell&quot;&gt;shell&lt;/h2&gt;
&lt;p&gt;So far, we’ve covered terminal emulators and pseudo-terminals. The shell is a program that resides in userland and manages user-computer interactions. Examples of shell programs include bash, zsh, and fish.&lt;/p&gt;

&lt;h2 id=&quot;putting-it-all-together&quot;&gt;putting it all together&lt;/h2&gt;
&lt;p&gt;What happens when you open a terminal emulator on Linux? I’ll give an example with my environment: I use gnome-terminal as my terminal emulator and zsh as my shell.&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;Gnome-terminal renders its UI on the video display.&lt;/li&gt;
  &lt;li&gt;It requests a PTY from the OS.&lt;/li&gt;
  &lt;li&gt;It launches a zsh subprocess.&lt;/li&gt;
  &lt;li&gt;It sets the stdin, stdout, and stderr of zsh to PTY slave.&lt;/li&gt;
  &lt;li&gt;It listens for keystroke events and sends the characters to PTY master.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src=&quot;/images/htop.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Systemd is Linux’s system and service manager. As shown above, systemd spawns a gnome-terminal subprocess, which in turn starts a zsh subprocess. The output above is from htop, which I ran from my terminal emulator. So, what happens when you run a command on a terminal emulator?&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;PTY master receives characters from gnome-terminal and passes them to the line discipline layer.&lt;/li&gt;
  &lt;li&gt;Line discipline layer buffers the characters as you type them, writing back to PTY master so that you can see what you’re keying in.&lt;/li&gt;
  &lt;li&gt;When you press enter, the line discipline layer sends the characters to the TTY driver, which then passes the characters to the PTY slave.&lt;/li&gt;
  &lt;li&gt;zsh reads the characters “htop” and forks the process to run the htop program. The forked process has the same stdin stdout and stderr as zsh.&lt;/li&gt;
  &lt;li&gt;htop prints to stdout (PTY slave).&lt;/li&gt;
  &lt;li&gt;PTY slave passes the output to PTY master, which passes it on to gnome-terminal.&lt;/li&gt;
  &lt;li&gt;Gnome-terminal reads the output and redraws the UI. The read is a loop, any change in the htop output reflects on the display in real-time.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;conclusion&lt;/h2&gt;
&lt;p&gt;This was a brief introduction to the Linux TTY subsystem. Much of how it operates right now is influenced by technical decisions made over 60 years ago. Remarkable resilience.
If you have any remarks or feedback, please reach out on &lt;a target=&quot;blank_&quot; href=&quot;https://twitter.com/ishuah_&quot;&gt;twitter!&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Technical references:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.linusakesson.net/programming/tty/&quot;&gt;The TTY demystified by Linus Åkesson&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://dev.to/napicella/linux-terminals-tty-pty-and-shell-192e&quot;&gt;Linux terminals, tty, pty and shell by Nicola Apicella&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.howtogeek.com/428174/what-is-a-tty-on-linux-and-how-to-use-the-tty-command/&quot;&gt;What is a TTY on Linux? (and How to Use the tty Command) by Dave McKay&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Thu, 04 Feb 2021 07:28:22 +0000</pubDate>
        <link>https://ishuah.com/2021/02/04/understanding-the-linux-tty-subsystem/</link>
        <guid isPermaLink="true">https://ishuah.com/2021/02/04/understanding-the-linux-tty-subsystem/</guid>
        
        <category>linux</category>
        
        <category>tty</category>
        
        
      </item>
    
      <item>
        <title>Running Kubernetes On My Raspberry Pi Subnet</title>
        <description>&lt;p&gt;This is the second article in my home lab series. The first article in the series, &lt;a href=&quot;https://ishuah.com/2021/01/12/three-pis-one-network/&quot;&gt;Three Pis, One Network&lt;/a&gt;, describes in detail how to set up a private network with 3 (or more) Raspberry Pis.&lt;/p&gt;

&lt;p&gt;I chose Kubernetes (a.k.a. k8s) because most of my home lab experiments are container based and require minimal resources. There are two Kubernetes distributions of note, &lt;a href=&quot;https://microk8s.io/&quot; target=&quot;blank_&quot;&gt;microk8s&lt;/a&gt; and &lt;a href=&quot;https://k3s.io/&quot; target=&quot;blank_&quot;&gt;k3s&lt;/a&gt;. K3s has been around for longer, with a bigger support community. Microk8s is a relatively new Canonical project with excellent documentation.&lt;/p&gt;

&lt;h2 id=&quot;k8s-cluster-design&quot;&gt;K8s cluster design&lt;/h2&gt;
&lt;figure&gt;&lt;img src=&quot;/images/k8s-cluster.png&quot; /&gt;&lt;figcaption&gt;The Kubernetes Cluster Design&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;The diagram above describes role delegation for all the cluster nodes. The Router RPi will serve as the k8s master and the remaining two RPi hosts will serve as k8s leaf nodes. Microk8s is my Kubernetes distro of choice.&lt;/p&gt;

&lt;h2 id=&quot;static-ip-addresses-for-pi-hosts&quot;&gt;static ip addresses for pi hosts&lt;/h2&gt;
&lt;p&gt;Kubernetes requires static IP addresses for each node in the cluster.&lt;/p&gt;

&lt;p&gt;I edited &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/dnsmasq.conf&lt;/code&gt; and added the following lines.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-apache&quot; data-lang=&quot;apache&quot;&gt;&lt;span class=&quot;c&quot;&gt;# /etc/dnsmasq.conf&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# assign static IP addresses to each Pi&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# these are dummy MAC addresses&lt;/span&gt;
dhcp-host=dc:a6:32:00:00:01,10.0.0.50
dhcp-host=dc:a6:32:00:00:02,10.0.0.51&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The lines above bind each RPi’s MAC address to a static IP address.&lt;/p&gt;

&lt;h2 id=&quot;enabling-cgroups&quot;&gt;enabling cgroups&lt;/h2&gt;
&lt;p&gt;Before installing MicroK8s I had to enable cgroups. I looked this up because my Linux kernel understanding is a bit rusty. Cgroups (Control Groups) are a kernel feature which allow processes to be organized into ordered groups whose resource usage can be monitored and managed. Kubernetes uses this feature to manage container resources in a pod.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://ubuntu.com/tutorials/how-to-kubernetes-cluster-on-raspberry-pi?&amp;amp;_ga=2.1581681.1904028082.1610988454-689418591.1607279417#4-installing-microk8s&quot;&gt;As per the MicroK8s tutorial&lt;/a&gt;, I edited the file &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/boot/firmware/cmdline.txt&lt;/code&gt; and added the following options:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;nv&quot;&gt;cgroup_enable&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;memory &lt;span class=&quot;nv&quot;&gt;cgroup_memory&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;1&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;After the edit, a reboot is required. I did this on all the Pis in my network.&lt;/p&gt;

&lt;h2 id=&quot;installing-microk8s&quot;&gt;installing microk8s&lt;/h2&gt;
&lt;p&gt;Installing Microk8s is very straight forward. There’s a snap package available, all I needed to do was install it on each RPi in my network.&lt;/p&gt;

&lt;p&gt;I executed the following commands on each RPi:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;c&quot;&gt;#install microk8s&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;snap &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;microk8s &lt;span class=&quot;nt&quot;&gt;--classic&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--channel&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;1.20/stable
&lt;span class=&quot;c&quot;&gt;# add the current user to the group 'microk8s'&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;usermod &lt;span class=&quot;nt&quot;&gt;-a&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-G&lt;/span&gt; microk8s &lt;span class=&quot;nv&quot;&gt;$USER&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# change ownership of the file ~/.kube to the current user&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;sudo chown&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-R&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$USER&lt;/span&gt; ~/.kube
&lt;span class=&quot;c&quot;&gt;# add an alias for the command 'microk8s kubectl'&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;tee&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-a&lt;/span&gt; ~/.bash_aliases &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt;EOF
&lt;span class=&quot;nb&quot;&gt;alias &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;kubectl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'microk8s kubectl'&lt;/span&gt;
EOF

&lt;span class=&quot;nb&quot;&gt;source&lt;/span&gt; ~/.bash_aliases&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;I read through &lt;a href=&quot;https://microk8s.io/docs/commands&quot;&gt;this command reference&lt;/a&gt; to familiarize myself with the microk8s command line interface.&lt;/p&gt;

&lt;p&gt;At the time of writing, Microk8s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;v1.20&lt;/code&gt; was the latest stable release available. Prior to its release I tried out &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;v1.19&lt;/code&gt; and encountered &lt;a href=&quot;https://github.com/ubuntu/microk8s/issues/1401&quot; target=&quot;blank_&quot;&gt;a major issue while adding multiple nodes to the cluster&lt;/a&gt;. I have not encountered this issue with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;v1.20&lt;/code&gt;.&lt;/p&gt;

&lt;h2 id=&quot;adding-nodes-to-the-cluster&quot;&gt;adding nodes to the cluster&lt;/h2&gt;
&lt;p&gt;Once microk8s is installed on all the Pis, I ran the following command on the Router RPi (designated master node):&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;microk8s.add-node&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The command above generates the following output:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;From the node you wish to &lt;span class=&quot;nb&quot;&gt;join &lt;/span&gt;to this cluster, run the following:
microk8s &lt;span class=&quot;nb&quot;&gt;join &lt;/span&gt;192.168.1.10:25000/182474c990a59770b1abe3ef9a5a40fd

If the node you are adding is not reachable through the default interface you can use one of the following:
 microk8s &lt;span class=&quot;nb&quot;&gt;join &lt;/span&gt;10.0.0.1:25000/182474c990a59770b1abe3ef9a5a40fd
 microk8s &lt;span class=&quot;nb&quot;&gt;join &lt;/span&gt;192.168.1.10:25000/182474c990a59770b1abe3ef9a5a40fd
 microk8s &lt;span class=&quot;nb&quot;&gt;join &lt;/span&gt;172.17.0.1:25000/182474c990a59770b1abe3ef9a5a40fd
 microk8s &lt;span class=&quot;nb&quot;&gt;join &lt;/span&gt;10.1.24.0:25000/182474c990a59770b1abe3ef9a5a40fd&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;I &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ssh'd&lt;/code&gt; into one of the RPi nodes and ran the command:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;c&quot;&gt;# I used the master IP 10.0.0.1 because that's the interface IP address that's reachable by the node&lt;/span&gt;
microk8s &lt;span class=&quot;nb&quot;&gt;join &lt;/span&gt;10.0.0.1:25000/182474c990a59770b1abe3ef9a5a40fd&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;I generated a new connection string and added the other RPi node to the cluster.&lt;/p&gt;

&lt;p&gt;Back on the master node I ran the following command:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;kubectl get node&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Which returns:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;NAME       STATUS   ROLES    AGE   VERSION
10.0.0.1   Ready    &amp;lt;none&amp;gt;   1d   v1.20.1-34+97978f80232b01
10.0.0.50  Ready    &amp;lt;none&amp;gt;   1d   v1.20.1-34+97978f80232b01
10.0.0.51  Ready    &amp;lt;none&amp;gt;   1d   v1.20.1-34+97978f80232b01&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The cluster is on like the break of dawn.&lt;/p&gt;

&lt;h1 id=&quot;enable-the-kubernetes-dashboard&quot;&gt;enable the kubernetes dashboard&lt;/h1&gt;
&lt;p&gt;Microk8s comes with a number of addons that you can use to enrich your Kubernetes cluster. The first addon I enabled was the Kubernetes dashboard. How else could I monitor my little pods?&lt;/p&gt;

&lt;p&gt;I executed the following commands on my master node:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;c&quot;&gt;# enable the dashboard and a few other necessary addons&lt;/span&gt;
microk8s &lt;span class=&quot;nb&quot;&gt;enable &lt;/span&gt;dns dashboard
&lt;span class=&quot;c&quot;&gt;# label the dashboard service&lt;/span&gt;
kubectl label service/kubernetes-dashboard kubernetes.io/cluster-service&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--namespace&lt;/span&gt; kube-system
&lt;span class=&quot;c&quot;&gt;# Proxy to make the dashboard accessible from my home network&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;microk8s.kubectl proxy &lt;span class=&quot;nt&quot;&gt;--accept-hosts&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;.&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--address&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;0.0.0.0 &amp;amp;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Executing the command &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubectl cluster-info&lt;/code&gt; now returns:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;Kubernetes control plane is running at https://127.0.0.1:16443
CoreDNS is running at https://127.0.0.1:16443/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy
Metrics-server is running at https://127.0.0.1:16443/api/v1/namespaces/kube-system/services/https:metrics-server:/proxy
kubernetes-dashboard is running at https://127.0.0.1:16443/api/v1/namespaces/kube-system/services/https:kubernetes-dashboard:/proxy&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The Kubernetes dashboard has a secure token based login system. I’m not too concerned about security (the Kubernetes cluster is only accessible from my home network) so I disabled the login mechanism on the dashboard.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;microk8s.kubectl &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; kube-system edit deploy kubernetes-dashboard &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; yaml
&lt;span class=&quot;c&quot;&gt;# Add '- --enable-skip-login' after '- --namespace=kube-system'&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The edit should end up like this:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;na&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;containers&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;--auto-generate-certificates&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;--namespace=kube-system&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;--enable-skip-login&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# the new line&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The dashboard was now accessible on the master node’s home network IP address:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-url&quot; data-lang=&quot;url&quot;&gt;http://192.168.1.10:8001/api/v1/namespaces/kube-system/services/https:kubernetes-dashboard:/proxy/#/overview?namespace=default&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Note: the login page still shows up but there’s a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;skip&lt;/code&gt; button which allow you to bypass the login procedure.&lt;/p&gt;

&lt;h2 id=&quot;bonus-enable-prometheus&quot;&gt;bonus: enable prometheus&lt;/h2&gt;
&lt;p&gt;Microk8s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;v1.20&lt;/code&gt; ships with Prometheus.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;microk8s &lt;span class=&quot;nb&quot;&gt;enable &lt;/span&gt;prometheus
&lt;span class=&quot;c&quot;&gt;# port forwarding to enable external access to Prometheus dashboard&lt;/span&gt;
microk8s kubectl port-forward &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; monitoring service/prometheus-k8s &lt;span class=&quot;nt&quot;&gt;--address&lt;/span&gt; 0.0.0.0 9090:9090
&lt;span class=&quot;c&quot;&gt;# port forwarding to enable external access to Grafana dashboard&lt;/span&gt;
microk8s kubectl port-forward &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; monitoring service/grafana &lt;span class=&quot;nt&quot;&gt;--address&lt;/span&gt; 0.0.0.0 3000:3000&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;I could now access the Prometheus dashboard on..&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-url&quot; data-lang=&quot;url&quot;&gt;http://192.168.1.10:9090/&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;..and the Grafana dashboard on&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-url&quot; data-lang=&quot;url&quot;&gt;http://192.168.1.10:3000/ &lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The Grafana dashboard has a default username:password  =&amp;gt; admin:admin.&lt;/p&gt;

&lt;h2 id=&quot;pains&quot;&gt;pains&lt;/h2&gt;
&lt;p&gt;Besides the node connection issue I encountered with microk8s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;v1.19&lt;/code&gt;, I did not encounter any other blockers while creating my k8s cluster.&lt;/p&gt;

&lt;p&gt;The next article in this series will be about one of my favorite experiments!&lt;/p&gt;
</description>
        <pubDate>Tue, 19 Jan 2021 08:30:29 +0000</pubDate>
        <link>https://ishuah.com/2021/01/19/running-a-kubernetes-cluster-on-my-raspberry-pi-subnet/</link>
        <guid isPermaLink="true">https://ishuah.com/2021/01/19/running-a-kubernetes-cluster-on-my-raspberry-pi-subnet/</guid>
        
        <category>raspberry-pi</category>
        
        <category>kubernetes</category>
        
        <category>microk8s</category>
        
        <category>ubuntu</category>
        
        <category>home-lab</category>
        
        
      </item>
    
      <item>
        <title>Three Pis, One Network</title>
        <description>&lt;p&gt;Most of my projects start from a point of curiosity. This one is no different. I’ve tinkered with Raspberry Pis over the years, but I’ve never tried anything on this scale. Three Raspberry Pis connected via a network switch to create a tiny home lab. The perfect environment to run destructive experiments.&lt;/p&gt;

&lt;h2 id=&quot;cluster-design&quot;&gt;cluster design&lt;/h2&gt;

&lt;figure&gt;&lt;img src=&quot;/images/rpi-network-topology.jpg&quot; /&gt;&lt;figcaption&gt;My Raspberry Pi Network Topology&lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;As illustrated above, the Raspberry Pi network is a private subnet. One Raspberry Pi serves as a router, leasing out IP addresses to any hosts connected to the private subnet switch. Additionally, the router acts as an internet gateway by forwarding internet from its Wi-Fi interface (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;wlan0&lt;/code&gt;, connected to my home Wi-Fi) to its Ethernet interface (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;eth0&lt;/code&gt;, connected to the private subnet).&lt;/p&gt;

&lt;h2 id=&quot;components-list&quot;&gt;components list&lt;/h2&gt;

&lt;p&gt;This is the list of the components that make up my RPi network.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Raspberry Pi 4B x3&lt;/li&gt;
  &lt;li&gt;Raspberry Pi 4B  heatsinks x3&lt;/li&gt;
  &lt;li&gt;32 GB MicroSD x3&lt;/li&gt;
  &lt;li&gt;30 cm Cat6 ethernet cable with RJ45 connectors x3&lt;/li&gt;
  &lt;li&gt;USB Type C charging cable x3&lt;/li&gt;
  &lt;li&gt;USB charging hub x1&lt;/li&gt;
  &lt;li&gt;8-port 10/100Mbps unmanaged switch x1&lt;/li&gt;
  &lt;li&gt;MicroHDMI to HDMI cable x1&lt;/li&gt;
  &lt;li&gt;USB Keyboard x1&lt;/li&gt;
&lt;/ol&gt;

&lt;figure&gt;&lt;img src=&quot;/images/rpi-network-components.jpg&quot; /&gt;&lt;figcaption&gt;The components&lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;I couldn’t find a local supplier with 1 ft flexible Cat6 Ethernet cables, so I had to get a 1 m Giganet Cat6 UTP Pure Copper Ethernet Cable, cut it up into 4 pieces and attach RJ45 connectors to each of them. Functionally, they serve their purpose but aesthetically, my Pi layout is restricted.&lt;/p&gt;

&lt;p&gt;I bought a generic USB charging hub which, shocker, could not power more than two Pis at a go. I’m currently powering each Pi independently using old phone charging heads. Not the best setup, but it’s the best I could do for now.&lt;/p&gt;

&lt;h2 id=&quot;setting-up-the-raspberry-pi-router&quot;&gt;setting up the raspberry pi router&lt;/h2&gt;

&lt;p&gt;The first step is installing an Operating System on the Raspberry Pi that I intend to use as my router. There are a number of options available but based on my use case Ubuntu Server 20.04 was an obvious choice. I recently found out that they have an arm64 release built specifically for the Raspberry Pi.&lt;/p&gt;

&lt;p&gt;Ubuntu has a &lt;a href=&quot;https://ubuntu.com/tutorials/how-to-install-ubuntu-on-your-raspberry-pi#1-overview&quot;&gt;well written tutorial&lt;/a&gt; on how to install the OS on a Raspberry Pi. For the sake of brevity, I will not rewrite the steps on this post, It’s easier to follow the documentation as is on the Ubuntu support page. I set up a Wi-Fi connection on the Pi and skipped step 5 because I won’t be needing a desktop environment on the RPi router.&lt;/p&gt;

&lt;h3 id=&quot;wlan0-static-ip-address&quot;&gt;wlan0 static ip address&lt;/h3&gt;
&lt;p&gt;The RPi router will serve as a jump server, connecting the private subnet to my home network. For this reason I needed to assign it a static IP address on my home network. This was my process:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Get the RPi’s MAC address on wlan0 by running:&lt;/li&gt;
&lt;/ul&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;ip addr show wlan0 | &lt;span class=&quot;nb&quot;&gt;grep link&lt;/span&gt;/ether | &lt;span class=&quot;nb&quot;&gt;awk&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'{print $2}'&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;ul&gt;
  &lt;li&gt;Create a Network-DHCP binding on my home network router’s admin portal (192.168.1.1). I bound the RPi’s MAC address to the static IP address &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;192.168.1.10&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I could now &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ssh&lt;/code&gt; into my RPi router on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ubuntu@192.168.1.10&lt;/code&gt;.&lt;/p&gt;

&lt;h3 id=&quot;eth0-static-ip-address&quot;&gt;eth0 static ip address&lt;/h3&gt;
&lt;p&gt;I’ll be running a DHCP server listening on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;eth0&lt;/code&gt;. Before setting that up I need to configure a static IP address for the RPi router’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;eth0&lt;/code&gt; on the private network.&lt;/p&gt;

&lt;p&gt;Ubuntu 17.10 and later uses &lt;a href=&quot;https://netplan.io/&quot;&gt;Netplan&lt;/a&gt; as the default network management tool. Netplan’s configuration are stored in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/netplan&lt;/code&gt; directory. Ubuntu Server 20.04 is provisioned with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cloud-init&lt;/code&gt;, I needed to disable it first before assigning a static IP address to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;eth0&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I created the file &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/cloud/cloud.cfg.d/99-disable-network-config.cfg&lt;/code&gt; and added the following:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-go&quot; data-lang=&quot;go&quot;&gt;&lt;span class=&quot;n&quot;&gt;network&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;disabled&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Then I assigned the static IP address &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;10.0.0.1/8&lt;/code&gt; to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;eth0&lt;/code&gt; by creating the file &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/netplan/01-netplan.yaml&lt;/code&gt; and adding the following:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;na&quot;&gt;network&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;ethernets&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;eth0&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;addresses&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;10.0.0.1/8&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;]&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;nameservers&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;na&quot;&gt;addresses&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;8.8.4.4&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;8.8.8.8&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;2&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h3 id=&quot;dnsmasq&quot;&gt;dnsmasq&lt;/h3&gt;

&lt;p&gt;Next, I installed dnsmasq (short for dns masquerade) to serve as my DHCP server.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;c&quot;&gt;# install dnsmasq&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;apt update &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;apt &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;dnsmasq
&lt;span class=&quot;c&quot;&gt;# backup the default dnsmasq config&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;sudo mv&lt;/span&gt; /etc/dnsmasq.conf /etc/dnsmasq.conf.bak&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;I created a new dnsmasq config &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sudo vim /etc/dnsmasq.conf&lt;/code&gt;:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-apache&quot; data-lang=&quot;apache&quot;&gt;&lt;span class=&quot;c&quot;&gt;# DHCP should lease addresses over eth0&lt;/span&gt;
interface=eth0
&lt;span class=&quot;c&quot;&gt;# Listen on the static IP address 10.0.0.1&lt;/span&gt;
&lt;span class=&quot;nc&quot;&gt;listen&lt;/span&gt;-address=10.0.0.1
&lt;span class=&quot;c&quot;&gt;# Enable dnsmasq's integrated DHCP server&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# and define 96 available address leases&lt;/span&gt;
dhcp-range=10.0.0.32,10.0.0.128,12h
&lt;span class=&quot;c&quot;&gt;# declare name servers&lt;/span&gt;
&lt;span class=&quot;nc&quot;&gt;server&lt;/span&gt;=8.8.8.8
&lt;span class=&quot;nc&quot;&gt;server&lt;/span&gt;=8.8.4.4
&lt;span class=&quot;c&quot;&gt;# Bind dnsmasq to eth0&lt;/span&gt;
bind-interfaces
&lt;span class=&quot;c&quot;&gt;# prevent packets with malformed domain names from forwarding&lt;/span&gt;
&lt;span class=&quot;nc&quot;&gt;domain&lt;/span&gt;-needed
&lt;span class=&quot;c&quot;&gt;# prevent packets from non-routed address spaces from forwarding&lt;/span&gt;
bogus-priv
&lt;span class=&quot;c&quot;&gt;# Use the hosts file on this machine&lt;/span&gt;
expand-hosts&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Running &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sudo service dnsmasq status&lt;/code&gt; returns status &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;failed&lt;/code&gt; because &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;eth0&lt;/code&gt; is not connected, yet. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dnsmasq&lt;/code&gt; will fail to assign an IP address to an interface if it’s not up. That’s okay, we’ll get to the networking part later on.&lt;/p&gt;

&lt;h2 id=&quot;forward-internet-from-wlan0-to-eth0&quot;&gt;forward internet from wlan0 to eth0&lt;/h2&gt;
&lt;p&gt;In my topology description above, the RPi hosts in my private network have access to the internet through the Router Pi. To achieve this, I’ll be setting up internet forwarding on the Router Pi.&lt;/p&gt;

&lt;p&gt;First, I enabled IP forwarding by uncommenting the following line in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/sysctl.conf&lt;/code&gt;:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-conf&quot; data-lang=&quot;conf&quot;&gt;&lt;span class=&quot;n&quot;&gt;net&lt;/span&gt;.&lt;span class=&quot;n&quot;&gt;ipv4&lt;/span&gt;.&lt;span class=&quot;n&quot;&gt;ip_forward&lt;/span&gt;=&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Then I added a masquerade rule to packets leaving the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;wlan0&lt;/code&gt; interface. This means that traffic through &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;wlan0&lt;/code&gt; can be rerouted without disruption.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;iptables &lt;span class=&quot;nt&quot;&gt;-t&lt;/span&gt; nat &lt;span class=&quot;nt&quot;&gt;-A&lt;/span&gt; POSTROUTING &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; wlan0 &lt;span class=&quot;nt&quot;&gt;-j&lt;/span&gt; MASQUERADE&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The next command forwards ESTABLISHED and RELATED packets from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;wlan0&lt;/code&gt; to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;eth0&lt;/code&gt;.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;iptables &lt;span class=&quot;nt&quot;&gt;-A&lt;/span&gt; FORWARD &lt;span class=&quot;nt&quot;&gt;-i&lt;/span&gt; wlan0 &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; eth0 &lt;span class=&quot;nt&quot;&gt;-m&lt;/span&gt; state &lt;span class=&quot;nt&quot;&gt;--state&lt;/span&gt; RELATED,ESTABLISHED &lt;span class=&quot;nt&quot;&gt;-j&lt;/span&gt; ACCEPT&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;This last command forwards all packets from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;eth0&lt;/code&gt; to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;wlan0&lt;/code&gt;.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;iptables &lt;span class=&quot;nt&quot;&gt;-A&lt;/span&gt; FORWARD &lt;span class=&quot;nt&quot;&gt;-i&lt;/span&gt; eth0 &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; wlan0 &lt;span class=&quot;nt&quot;&gt;-j&lt;/span&gt; ACCEPT&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Finally, my &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;iptables&lt;/code&gt; rules looked like this:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-go&quot; data-lang=&quot;go&quot;&gt;&lt;span class=&quot;n&quot;&gt;Chain&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;INPUT&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;policy&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ACCEPT&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;30335&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;packets&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1580&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;K&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bytes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
 &lt;span class=&quot;n&quot;&gt;pkts&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bytes&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;target&lt;/span&gt;     &lt;span class=&quot;n&quot;&gt;prot&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;opt&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;in&lt;/span&gt;     &lt;span class=&quot;n&quot;&gt;out&lt;/span&gt;     &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;               &lt;span class=&quot;n&quot;&gt;destination&lt;/span&gt;                   

&lt;span class=&quot;n&quot;&gt;Chain&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FORWARD&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;policy&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ACCEPT&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;packets&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bytes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
 &lt;span class=&quot;n&quot;&gt;pkts&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bytes&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;target&lt;/span&gt;     &lt;span class=&quot;n&quot;&gt;prot&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;opt&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;in&lt;/span&gt;     &lt;span class=&quot;n&quot;&gt;out&lt;/span&gt;     &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;               &lt;span class=&quot;n&quot;&gt;destination&lt;/span&gt;                   
    &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;     &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ACCEPT&lt;/span&gt;     &lt;span class=&quot;n&quot;&gt;all&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;--&lt;/span&gt;  &lt;span class=&quot;n&quot;&gt;wlan0&lt;/span&gt;  &lt;span class=&quot;n&quot;&gt;eth0&lt;/span&gt;    &lt;span class=&quot;m&quot;&gt;0.0.0.0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;            &lt;span class=&quot;m&quot;&gt;0.0.0.0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;            &lt;span class=&quot;n&quot;&gt;state&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RELATED&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ESTABLISHED&lt;/span&gt;
    &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;     &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ACCEPT&lt;/span&gt;     &lt;span class=&quot;n&quot;&gt;all&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;--&lt;/span&gt;  &lt;span class=&quot;n&quot;&gt;eth0&lt;/span&gt;   &lt;span class=&quot;n&quot;&gt;wlan0&lt;/span&gt;   &lt;span class=&quot;m&quot;&gt;0.0.0.0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;            &lt;span class=&quot;m&quot;&gt;0.0.0.0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;           

&lt;span class=&quot;n&quot;&gt;Chain&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OUTPUT&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;policy&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ACCEPT&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;30665&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;packets&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1624&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;K&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bytes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
 &lt;span class=&quot;n&quot;&gt;pkts&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bytes&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;target&lt;/span&gt;     &lt;span class=&quot;n&quot;&gt;prot&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;opt&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;in&lt;/span&gt;     &lt;span class=&quot;n&quot;&gt;out&lt;/span&gt;     &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;               &lt;span class=&quot;n&quot;&gt;destination&lt;/span&gt;                    &lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;I installed &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;iptables-persistent&lt;/code&gt; to save the iptables rules permanently.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;apt &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;iptables-persistent&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Note: Remember to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sudo reboot&lt;/code&gt; before proceeding to the next step.&lt;/p&gt;

&lt;h2 id=&quot;raspberry-pi-hosts&quot;&gt;raspberry pi hosts&lt;/h2&gt;
&lt;p&gt;I wrote the Ubuntu Server 20.04 image for the Pi hosts in almost the same way I did it for the Router Pi, except setting up a Wi-Fi connection. These hosts will access the internet via forwarded connections on the Router Pi.&lt;/p&gt;

&lt;h2 id=&quot;putting-it-all-together&quot;&gt;putting it all together&lt;/h2&gt;
&lt;p&gt;The final stage is connecting all the Pis to the network switch and powering up the switch. All that toil has to amount to something.&lt;/p&gt;

&lt;p&gt;Running &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sudo service dnsmasq status&lt;/code&gt; should return &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Active: active(running)&lt;/code&gt;:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;● dnsmasq.service - dnsmasq - A lightweight DHCP and caching DNS server
     Loaded: loaded &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;/lib/systemd/system/dnsmasq.service&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; enabled&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; vendor preset: enabled&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
     Active: active &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;running&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; since Tue 2021-01-12 12:12:11 UTC&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; 20min ago
    Process: 1249 &lt;span class=&quot;nv&quot;&gt;ExecStartPre&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;/usr/sbin/dnsmasq &lt;span class=&quot;nt&quot;&gt;--test&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;code&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;exited, &lt;span class=&quot;nv&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;0/SUCCESS&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
    Process: 1356 &lt;span class=&quot;nv&quot;&gt;ExecStart&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;/etc/init.d/dnsmasq systemd-exec &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;code&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;exited, &lt;span class=&quot;nv&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;0/SUCCESS&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
    Process: 1379 &lt;span class=&quot;nv&quot;&gt;ExecStartPost&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;/etc/init.d/dnsmasq systemd-start-resolvconf &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;code&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;exited, &lt;span class=&quot;nv&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;0/SUCCESS&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
   Main PID: 1377 &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;dnsmasq&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
      Tasks: 1 &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;limit: 4436&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
     Memory: 2.7M
     CGroup: /system.slice/dnsmasq.service
             └─1377 /usr/sbin/dnsmasq &lt;span class=&quot;nt&quot;&gt;-x&lt;/span&gt; /run/dnsmasq/dnsmasq.pid &lt;span class=&quot;nt&quot;&gt;-u&lt;/span&gt; dnsmasq &lt;span class=&quot;nt&quot;&gt;-7&lt;/span&gt; /etc/dnsmasq.d,.dpkg-dist,.dpkg-old,.dpkg-new &lt;span class=&quot;nt&quot;&gt;--local-service&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--trust-anchor&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;.,20326,8,2,e06d44b80b8f1d39a95c0b0d7c65d08458e880409&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Running &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cat /var/lib/misc/dnsmasq.leases&lt;/code&gt; should list two DHCP leases, assigned to the two hosts in the private network. I tested internet forwarding by &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ssh&lt;/code&gt;ing into each Pi using the IPs on their leases and checking that they have internet access.&lt;/p&gt;

&lt;p&gt;That’s it!&lt;/p&gt;

&lt;h2 id=&quot;pains&quot;&gt;pains&lt;/h2&gt;
&lt;p&gt;I had a hard time figuring out how to assign a static IP address to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;eth0&lt;/code&gt; on the Router Pi. The last time I did that was before netplan, when all network configs were on one file: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/network/interfaces&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The hardware currently sits in a disorganized state on my work desk. I ordered a Raspberry Pi &lt;a href=&quot;https://www.amazon.com/GeeekPi-Raspberry-Cluster-Stackable-4-Layers/dp/B083FDHPBH/ref=sr_1_3?dchild=1&amp;amp;keywords=raspberry%2Bpi%2B4%2Btower&amp;amp;qid=1610457918&amp;amp;sr=8-3&amp;amp;th=1&quot;&gt;cluster case&lt;/a&gt; and &lt;a href=&quot;https://www.amazon.com/Cable-Matters-160021-Snagless-Ethernet/dp/B00E5I7T9I/ref=sr_1_2_sspa?dchild=1&amp;amp;keywords=raspberry%2Bpi%2B4%2Bcat6%2Bcable&amp;amp;qid=1610458153&amp;amp;sr=8-2-spons&amp;amp;spLa=ZW5jcnlwdGVkUXVhbGlmaWVyPUExMUVHVDdPU0FWVTdZJmVuY3J5cHRlZElkPUEwNTMwMDk5MU5aODRBNzhQVDAzVSZlbmNyeXB0ZWRBZElkPUEwNDgxOTc2RU9BNjhDTldGVDE4JndpZGdldE5hbWU9c3BfYXRmJmFjdGlvbj1jbGlja1JlZGlyZWN0JmRvTm90TG9nQ2xpY2s9dHJ1ZQ&amp;amp;th=1&quot;&gt;flexible Cat6 Ethernet cables&lt;/a&gt; so that I can have a better organized home lab.&lt;/p&gt;

&lt;p&gt;The next article in this series is &lt;a href=&quot;https://ishuah.com/2021/01/19/running-a-kubernetes-cluster-on-my-raspberry-pi-subnet/&quot;&gt;Running a Kubernetes Cluster On My Raspberry Pi Subnet&lt;/a&gt;&lt;/p&gt;
</description>
        <pubDate>Tue, 12 Jan 2021 06:29:29 +0000</pubDate>
        <link>https://ishuah.com/2021/01/12/three-pis-one-network/</link>
        <guid isPermaLink="true">https://ishuah.com/2021/01/12/three-pis-one-network/</guid>
        
        <category>raspberry-pi</category>
        
        <category>networking</category>
        
        <category>ubuntu</category>
        
        <category>home-lab</category>
        
        
      </item>
    
      <item>
        <title>Hover As An Offline Fallback</title>
        <description>&lt;p&gt;A fallback is defined as a contingency option to be taken if the preferred choice is unavailable. Hover is an Android SDK that automates existing USSD sessions in the background of Android applications. We will set up a USSD channel (with a USSD backend), configure an action on the Hover dashboard and integrate the action as an offline fallback in an Android app.&lt;/p&gt;

&lt;p&gt;USSD has two modes of operation, USSD PULL, which is mobile initiated and USSD PUSH, which is network initiated. For this fallback use-case, we’ll be using the mobile initiated mode to communicate data between the app and server. The USSD protocol will serve as a transport layer and USSD messages will be the data packets communicated between the app and the server. Hover will be the “USSD client” for the Android app, capable of sending and receiving USSD messages.&lt;/p&gt;

&lt;figure class=&quot;kg-card kg-image-card kg-card-hascaption&quot;&gt;&lt;img src=&quot;/images/ussd-flow-2.jpg&quot; class=&quot;kg-image&quot; /&gt;&lt;figcaption&gt;image 1.1&lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;The maximum number of characters in a USSD message varies between carriers. Safaricom (KE) has a limit of 160 characters sent from the network from the user and a user input limit of 80 characters. Similarly, USSD session length varies between 90-180 seconds, depending on the carrier.&lt;/p&gt;

&lt;h3 id=&quot;architecture&quot;&gt;Architecture&lt;/h3&gt;
&lt;figure class=&quot;kg-card kg-image-card kg-card-hascaption&quot;&gt;&lt;img src=&quot;/images/architecture.jpg&quot; class=&quot;kg-image&quot; /&gt;&lt;figcaption&gt;image 1.2&lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;For the purpose of this demonstration, we’ll be sending a set of key/value pairs to the server. The flow involves four messages sent between the mobile app and the server:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;Mobile dials USSD code, initiating a USSD session&lt;br /&gt;
    2. Server responds with the message &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;send data&lt;/code&gt;, awaiting input&lt;br /&gt;
    3. Mobile sends url query string&lt;br /&gt;
    4. Server receives and persists the data; responds with a final success message&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We’ll use the Hover SDK to automate these four steps.&lt;/p&gt;

&lt;h3 id=&quot;the-ussd-gateway&quot;&gt;The USSD Gateway&lt;/h3&gt;

&lt;p&gt;The first step is setting up a channel on the USSD Gateway. Unlike most protocols, USSD channels are limited to the country/region that the providing telecommunication company operates in. For the purpose of this demo I’ve used a shared USSD channel, * 384 *94#, provided by &lt;a href=&quot;https://africastalking.com/&quot;&gt;Africa’s Talking&lt;/a&gt;. This short code is only available on Safaricom (KE) and Orange (KE) networks. Dialing the USSD code gives us the following message:&lt;/p&gt;

&lt;figure class=&quot;kg-card kg-image-card&quot;&gt;&lt;img src=&quot;/images/Screenshot_20200518_164016-1.jpg&quot; class=&quot;kg-image&quot; /&gt;&lt;/figure&gt;

&lt;p&gt;That’s because we haven’t set up the callback function that will read and respond to USSD requests. USSD response messages are in plain text beginning with the keyword &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CON&lt;/code&gt; if the session is ongoing. If the response message is the last for the session, begin with the keyword &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;END&lt;/code&gt;. A simple USSD callback function that responds request with the message &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Welcome to Hover&lt;/code&gt; would look like this:&lt;/p&gt;

&lt;!--kg-card-begin: markdown--&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;    &lt;span class=&quot;c1&quot;&gt;# Python 3.8.2
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ussd_callback&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;“&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Welcome&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;to&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Hover&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;”&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;!--kg-card-end: markdown--&gt;

&lt;p&gt;The complete callback function for the architecture described above (image 1.2) looks like this:&lt;/p&gt;

&lt;!--kg-card-begin: markdown--&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;    &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;urllib.parse&lt;/span&gt;
    
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;save_report&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;params&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;urllib&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parse_qs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'body'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;keep_blank_values&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'text'&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;“&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;statusCode&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;”&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;400&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;“&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;”&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;“&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bad&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;”&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    
        &lt;span class=&quot;n&quot;&gt;text&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'text'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
        
        &lt;span class=&quot;c1&quot;&gt;# Respond with the text `send input`, expect data
&lt;/span&gt;        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;text&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;''&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;“&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;statusCode&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;”&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;“&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;”&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;“&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CON&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;send&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;”&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        
        &lt;span class=&quot;c1&quot;&gt;# Parse url query string
&lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;urllib&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parse_qs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        
        &lt;span class=&quot;c1&quot;&gt;# If the data sent is malformed, respond with `bad request`
&lt;/span&gt;        &lt;span class=&quot;c1&quot;&gt;# and end the session
&lt;/span&gt;        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;bool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;“&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;statusCode&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;”&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;400&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;“&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;”&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;“&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bad&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;”&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    
        &lt;span class=&quot;c1&quot;&gt;# Process, persist, analyze data
&lt;/span&gt;        
        &lt;span class=&quot;c1&quot;&gt;# End session, `success`
&lt;/span&gt;        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;“&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;statusCode&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;”&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;“&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;”&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;“&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;success&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;”&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;!--kg-card-end: markdown--&gt;

&lt;p&gt;The example above is Python 3.8.2 code deployed as a serverless function on AWS. I won’t dig deep on setting up a callback function in this blog post because a lot of the technical input is subjective based on language choice and deployment options. Africa’s Talking USSD API sends a post request to your callback function with &lt;a href=&quot;https://build.at-labs.io/docs/ussd%2Foverview&quot;&gt;five parameters as described in their documentation&lt;/a&gt;. In the example, we read the &lt;em&gt;text&lt;/em&gt; field, which contains the user input. If the text field is empty then that means the session has just been initiated and the function responses with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CON send data&lt;/code&gt;. Otherwise, we try parse the text field (assuming it’s url encoded) and end the session.&lt;/p&gt;

&lt;p&gt;Dialing  * 384 *94# now gives you the following messages:&lt;/p&gt;

&lt;figure class=&quot;kg-card kg-image-card kg-width-full&quot;&gt;&lt;img src=&quot;/images/Screenshot_20200518-164351-1.jpg&quot; class=&quot;kg-image&quot; /&gt;&lt;/figure&gt;
&lt;h3 id=&quot;the-hover-dashboard&quot;&gt;The Hover Dashboard&lt;/h3&gt;

&lt;p&gt;Now we have a mature USSD interface that we can use to send data to our backend. The next step is integrating Hover to automate interactions with this interface. If you’re not familiar with creating actions and parsers on our dashboard, &lt;a href=&quot;https://docs.usehover.com/actions&quot;&gt;you can read through our documentation&lt;/a&gt;.&lt;/p&gt;

&lt;figure class=&quot;kg-card kg-image-card kg-card-hascaption&quot;&gt;&lt;img src=&quot;/images/offline-fallback-action.png&quot; class=&quot;kg-image&quot; /&gt;&lt;figcaption&gt;image 1.3 Offline Fallback Action&lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;The image above is an action configuration for our USSD interface. There’s one step configured, a variable that’s a direct response to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;send data&lt;/code&gt; prompt.&lt;/p&gt;

&lt;figure class=&quot;kg-card kg-image-card kg-card-hascaption&quot;&gt;&lt;img src=&quot;/images/offline-fallback-parsers.png&quot; class=&quot;kg-image&quot; /&gt;&lt;figcaption&gt;image 1.4&lt;/figcaption&gt;&lt;/figure&gt;

&lt;!--
The action has two parsers; a success parser and a failure parser. The success parser matches the message _success_, denoting a successful end of the USSD session. Similarly, the failure parser matches the message _bad request_, denoting a failed session.

### The Hover SDK

The final step is automating this USSD action from an Android app. This part requires the Hover SDK to run, instructions on how to[install it are in our documentation](https://docs.usehover.com/installation).

&lt;!--kg-card-begin: markdown--&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span class=&quot;n&quot;&gt;sendData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;setOnClickListener&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;payload&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;“”&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;report_title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;isNotEmpty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;payload&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;“&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;report_title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;”&lt;/span&gt;

            &lt;span class=&quot;n&quot;&gt;payload&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hungry&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;isChecked&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;payload&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;plus&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;“&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hungry&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;”&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;payload&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;plus&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;“&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hungry&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;”&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;intensity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;isNotEmpty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;payload&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;payload&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;plus&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;“&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;intensity&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;intensity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;”&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            
            &lt;span class=&quot;o&quot;&gt;//&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Input&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RatingBar&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;payload&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;payload&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;plus&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;“&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mood&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mood&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rating&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;”&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;


            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;payload&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;isNotEmpty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HoverParameters&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ACTION_ID&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;extra&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;“&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;payload&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;”&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;payload&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;buildIntent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

                &lt;span class=&quot;n&quot;&gt;startActivityForResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;Toast&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;makeText&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;“&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Nothing&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;to&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;!”&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Toast&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LENGTH_LONG&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;show&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;!--kg-card-end: markdown--&gt;

&lt;p&gt;The example above is in Kotlin and it demonstrated how you can run the action we created before with a valid payload. In this specific case, a valid payload means a URL encoded key/value pairs separated by a semicolon. Example:&lt;/p&gt;

&lt;!--kg-card-begin: markdown--&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-url&quot; data-lang=&quot;url&quot;&gt;activity=idle;course=818;altitude=1780.91;path=Ngong+road&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;!--kg-card-end: markdown--&gt;

&lt;p&gt;Africa’s Talking’s USSD API posts query string encoded parameters to the callback URL and the individual parameters are separated by an ampersand (&amp;amp;). This means that we can’t use the ampersand (&amp;amp;) in our query string that will sit in the &lt;em&gt;text&lt;/em&gt; parameter, that’s why I’ve intentionally used the semicolon separator in the schema above. The semicolon separator is illegal according to the &lt;a href=&quot;https://www.w3.org/TR/2014/REC-html5-20141028/forms.html#url-encoded-form-data&quot;&gt;2014 W3C recommendation&lt;/a&gt; but &lt;a href=&quot;https://github.com/python/cpython/blob/3.8/Lib/urllib/parse.py#L375&quot;&gt;Python 3 still supports it&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Running the action described above (on an Android phone with no internet connectivity) will result in the following screens:&lt;/p&gt;

&lt;figure class=&quot;kg-card kg-image-card&quot;&gt;&lt;img src=&quot;/images/offline-fallback-screens.jpg&quot; class=&quot;kg-image&quot; /&gt;&lt;/figure&gt;
&lt;h3 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h3&gt;

&lt;p&gt;This demonstration shows how you can send data from an offline phone to your server but since the flow of data is bidirectional, you can also send a payload from your server to your app.&lt;/p&gt;

&lt;p&gt;Due to the restrictions in message size and session length, you can only send lean data via USSD.  I used the query string schema because of the limited payload size but any schema/encoding can be used as long as the payload size is not exceeded. You can send multiple messages in sequence as long as the session is still valid. This means that you can implement a chunking algorithm to send data in sequence.&lt;/p&gt;

&lt;p&gt;Originally posted on the &lt;a href=&quot;https://blog.usehover.com/hover-offline-fallback/&quot;&gt;Hover blog&lt;/a&gt;.&lt;/p&gt;
</description>
        <pubDate>Tue, 26 May 2020 07:28:22 +0000</pubDate>
        <link>https://ishuah.com/2020/05/26/hover-offline-fallback/</link>
        <guid isPermaLink="true">https://ishuah.com/2020/05/26/hover-offline-fallback/</guid>
        
        <category>ussd</category>
        
        <category>offline-fallback</category>
        
        <category>android-sdk</category>
        
        
      </item>
    
      <item>
        <title>Preact-Rails Gem&amp;#58; Preact integration for Ruby on Rails.</title>
        <description>&lt;p&gt;Our vision at Hover is to empower developers with resources, services, and tools to build for local communities at global scale. As part of this vision, we’re happy to announce a new open source gem, &lt;a href=&quot;https://github.com/UseHover/preact-rails&quot;&gt;preact-rails&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;A few weeks ago we wrote about how our &lt;a href=&quot;https://medium.com/use-hover/our-developers-outgrew-our-mvp-83221f83bb77&quot;&gt;developers outgrew our dashboard&lt;/a&gt; within two months of our official launch. We took this as an opportunity to improve our dashboard UI and UX.&lt;/p&gt;

&lt;p&gt;The first step was picking a javascript UI library. We had a lot of options but the ones that stood out were Vue.js and React. Part of the engineering team was familiar with React and it proved robust enough so we went ahead and picked that. You’re probably wondering why I’m mentioning React when the title clearly states “Preact”. Keep reading, it gets better.&lt;/p&gt;

&lt;p&gt;Our dashboard is built on Ruby on Rails which is, by all accounts, very opinionated. Decoupling the front-end to an independent project would cause a lot of integration pains, the biggest being managing user sessions without compromising security. We wanted to upgrade the front-end without committing major changes to the back-end engine. The driving factor here was time, we didn’t have enough of it (do we ever?).&lt;/p&gt;

&lt;p&gt;Taking this into consideration, we went back to our javascript UI library choice, React. We chose react before we made the choice not to build a whole front-end project on it’s own. The most feasible option was to serve react components on top of Rails views. It’s minimal and it serves our purpose of not intruding on the back-end and there was a gem just for this, &lt;a href=&quot;https://github.com/reactjs/react-rails&quot;&gt;react-rails&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We started testing the gem by building a few simple components: buttons and forms. Around this time &lt;a href=&quot;https://medium.com/u/50dd4d42d4e9&quot;&gt;David Kutalek&lt;/a&gt; came across &lt;a href=&quot;https://preactjs.com/&quot;&gt;Preact&lt;/a&gt; (mentioned to him by our head of design, &lt;a href=&quot;https://medium.com/u/fe7d40e3c821&quot;&gt;Justin Scherer&lt;/a&gt;). We were intrigued. In a nutshell, Preact is a fast 3kB alternative to React. There are subtle differences that are addressed by a compatibility layer &lt;a href=&quot;https://github.com/developit/preact-compat&quot;&gt;preact-compat&lt;/a&gt;. We wanted to test it out before we jumped in head first. There was one issue: we couldn’t find a gem similar to &lt;a href=&quot;https://github.com/reactjs/react-rails&quot;&gt;react-rails&lt;/a&gt;, a Preact implementation for Ruby on Rails. That’s when &lt;a href=&quot;https://github.com/UseHover/preact-rails&quot;&gt;preact-rails&lt;/a&gt; was conceptualized. Inspired (and heavily influenced) by &lt;a href=&quot;https://github.com/reactjs/react-rails&quot;&gt;react-rails&lt;/a&gt;, we started building a Preact implementation for Ruby on Rails.&lt;/p&gt;

&lt;p&gt;This is where we get a tiny bit technical. There are two parts of this project. The first part is the &lt;a href=&quot;https://rubygems.org/gems/preact-rails&quot;&gt;ruby gem&lt;/a&gt;, aptly named preact-rails. The gem serves one purpose, to make &lt;a href=&quot;https://github.com/UseHover/preact-rails/blob/master/lib/preact/view_helper.rb#L2&quot;&gt;preact_component&lt;/a&gt; available as a view helper. This view helper takes in three variables, a component name, component props and component options and returns a div DOM node with the properties &lt;em&gt;data-preact-class&lt;/em&gt; and &lt;em&gt;data-preact-props&lt;/em&gt;. That’s a lot of confusing words, let me demonstrate.&lt;/p&gt;

&lt;p&gt;In your view, you call preact_component like so:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;  &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;sx&quot;&gt;%= preact_component(“SimpleButton”, { label: “Start” }) %&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Where &lt;em&gt;“SimpleButton”&lt;/em&gt; is the Preact component name, &lt;em&gt;label&lt;/em&gt; is a prop and &lt;em&gt;“Start”&lt;/em&gt; is a prop value. And this is what gets rendered when you load the view on a browser:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;  &lt;span class=&quot;nt&quot;&gt;&amp;lt;div&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;data-preact-class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;“SimpleButton”&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;data-preact-props=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;“{'label':'Start'}”&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;That’s the first part. The second part of the project is an npm package, &lt;a href=&quot;https://www.npmjs.com/package/preact_ujs&quot;&gt;preact_ujs&lt;/a&gt;. (UJS stands for Unobtrusive JavaScript). This package takes the div rendered above and inflates the named Preact component. This is the complex part but I’ll try make it comprehendible.&lt;/p&gt;

&lt;p&gt;Let’s start with the SimpleButtonComponent, defined as such:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-js&quot; data-lang=&quot;js&quot;&gt;  &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;h&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Component&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;“&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;preact&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;”&lt;/span&gt;
  
  &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;SimpleButton&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Component&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;render&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;button&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;label&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;/button&amp;gt;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;)
&lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The component returns a button DOM element with the property &lt;em&gt;label&lt;/em&gt; as the child node. Now, back to preact_ujs. This package has a function &lt;a href=&quot;https://github.com/UseHover/preact-rails/blob/master/preact_ujs/index.js#L19&quot;&gt;mountComponent&lt;/a&gt; where all the action happens. It starts by finding all DOM components with the attribute &lt;em&gt;data-preact-class,&lt;/em&gt; loops through each of them &lt;a href=&quot;https://github.com/UseHover/preact-rails/blob/master/preact_ujs/index.js#L25&quot;&gt;finding the component class definition&lt;/a&gt; and &lt;a href=&quot;https://github.com/UseHover/preact-rails/blob/master/preact_ujs/index.js#L27&quot;&gt;parsing the props&lt;/a&gt;. Finally, &lt;a href=&quot;https://github.com/UseHover/preact-rails/blob/master/preact_ujs/index.js#L28&quot;&gt;it renders the component&lt;/a&gt;, that’s it!&lt;/p&gt;

&lt;p&gt;That’s the inner workings of the gem, you can take a closer look on the &lt;a href=&quot;https://github.com/UseHover/preact-rails&quot;&gt;github repo&lt;/a&gt;. We have complete setup instructions on the README.&lt;/p&gt;

&lt;p&gt;We used this gem to test Preact and we loved it. We really didn’t need all the rich features offered by React so the initial proposition attracted us. Three sprints later we launched our new dashboard. So far user feedback has been positive, our developers love it!&lt;/p&gt;

&lt;p&gt;We’re still building on our dashboard, powered by our handy little gem. As we build we’ll be making improvements on the gem, starting with tests and TypeScript support. Pull requests are welcome!&lt;/p&gt;

&lt;p&gt;Originally posted on the &lt;a href=&quot;https://blog.usehover.com/preact-rails-gem--preact-integration-for-ruby-on-rails-/&quot;&gt;Hover blog&lt;/a&gt;.&lt;/p&gt;
</description>
        <pubDate>Tue, 24 Sep 2019 00:00:00 +0000</pubDate>
        <link>https://ishuah.com/2019/09/24/preact-rails-gem-preact-integration-for-ruby-on-rails/</link>
        <guid isPermaLink="true">https://ishuah.com/2019/09/24/preact-rails-gem-preact-integration-for-ruby-on-rails/</guid>
        
        <category>developer-tools</category>
        
        
      </item>
    
  </channel>
</rss>