https://fkfd.me/fkfd2024-01-06T23:49:39Zfkfdhttps://fkfd.me/favicon.icohttps://fkfd.me/random/shenzhen_io_review/
Shenzhen I/O Game Review — fkfd.me
2024-01-06T23:49:39Z
<h1 id="shenzhen-io-game-review">Shenzhen I/O Game Review</h1>
<p>I don't usually game.<sup>[citation needed]</sup></p>
<p>Sometime in 2021, in my first days of university, I felt bored and bought
this game. Also out of vanity to show how different I am from my league of
legends roommates. (it didn't work because i got stuck at assignment eight
and gave up, until december 2023 when i decided to give it another spin
now that i've made a few PCBs and written some assembly in real life)</p>
<h2 id="what-is-this-game">What is this game</h2>
<p><a href="https://www.zachtronics.com/shenzhen-io/">Shenzhen I/O</a> is a "nerd puzzle
game" (my words) by Zachtronics.</p>
<p>You move to Shenzhen and work as an employee at 龙腾 (Longteng), where you
combine chips into circuits that meet client specifications. In each level
(assignment), you are given a canvas like this one:</p>
<p><img alt="Blank circuit board next to a waveform viewer and part
selector" src="https://fkfd.me/random/img/shenzhen_io_review/canvas.png"/></p>
<p>Then you complete these tasks, in the general order:</p>
<ul>
<li>Drag parts from the sidebar</li>
<li>Connect parts with each other and I/O pins on the sides</li>
<li>Write assembly for the programmable chips</li>
<li>Simulate and debug until output matches expected waveform</li>
</ul>
<p>The assembly language in question is invented specifically for the game.
The chips are fictional, too. It doesn't actually require prior embedded
systems knowledge. A few differences from real-life embedded systems:</p>
<ul>
<li>No need to explicitly power the chips</li>
<li>No need to worry about clock cycles</li>
<li>Everything seems to use decimal, not binary</li>
<li>Hardly any boolean operations</li>
<li>Voltage levels range from 0 to 100</li>
<li>ADC/DAC are perfectly accurate</li>
<li>Integers range from -999 to 999</li>
<li>MCUs have two registers max (<code>acc</code> and <code>dat</code>), and no SRAM</li>
<li>XBus, the asynchronous protocol, seems too complicated for one clock
cycle</li>
<li>Multidriver problems are handled simply by <code>input = max({outputs})</code>, and
can be abused</li>
</ul>
<p>And mostly importantly, the debugger is something I wish I had in real
life. You can literally step through the instructions! Imagine if you were
able to freeze time, poke an ATmega328P and know which loop it's stuck in.</p>
<p>That said, I'm not the best at this game. Hell, I'm not even <em>good</em>, as
I have created abominations like:</p>
<details>
<summary>Spoiler</summary>
<p><img alt="Circuit with five MCUs and wires that almost fill the entire canvas" src="https://fkfd.me/random/img/shenzhen_io_review/traffic_signal.png"/></p>
</details>
<p>After you complete each assignment, the game shows you where you are among
all the players. The data I got was:</p>
<p><img alt="Three histograms, showing my data float much worse than
average" src="https://fkfd.me/random/img/shenzhen_io_review/traffic_signal_histograms.png"/></p>
<p>The current best for three metrics on the leaderboard are:</p>
<ul>
<li>Lowest cost: 6 yuan (mine costs 20)</li>
<li>Power usage: 123 (mine uses 1.7K)</li>
<li>Lines of code: 9 (mine has 45)</li>
</ul>
<p>As you can see, my solution is far from good in whichever way, but
nevertheless it works. This is because there are so many ways to solve
a problem, and to optimize a solution.</p>
<p>That said, am I going to optimize my solution? No.</p>
<h2 id="why-is-it-fun">Why is it fun</h2>
<p>Like all puzzle games, it is fun because it's intellectually challenging.
It's not the kind of Genshin Impact minigame.</p>
<p>Another reason is the plot. For most of the game's target audience, living
in Shenzhen would be a foreign experience, and it's a lot of fun reading
coworkers (both Chinese and from abroad) react to projects and life. The
game's UI involves a mail client, where you receive emails from your
supervisors and coworkers, and honestly that's half the fun.</p>
<p>(lets ignore the fact that most chinese companies dont discuss everything
over email)</p>
<details>
<summary>Spoiler alert!</summary>
<p>My favorite emails are:</p>
<ul>
<li>When you designed a vape as merchandise for musician Cool Dad, who, once
the vapes were manufactured, got arrested for possession of
amphetamines. Also, Carl absolutely <em>roasted</em> Cool Dad's music.</li>
<li>When you designed a laser/illuminator apparatus for law enforcement
weapons and Carl responded with brutal sarcasm out of moral concerns.
I kinda wonder if the correct move is <em>not</em> to design it, but it's too
late.</li>
<li>When Joe proposed a haunted doll implemented with unmarked chips of
unknown origin, and was then forbidden from venturing further in the
field of "ghost electronics"</li>
<li>When a Japanese company claims to have developed a "traditional yarrow
stalk <em>I Ching</em> divination" with "MEMS technology"</li>
</ul>
</details>
<h2 id="why-is-it-hard">Why is it hard</h2>
<h3 id="you-have-to-read-the-datasheet">You have to read the datasheet</h3>
<p>The game comes with no manual; instead it ships a PDF containing all the
datasheets and supplementary materials. Fortunately they are much easier
to understand than those in real life. And they are (mostly) typeset very
well. Much more pleasant to the eye than many actual industrial
datasheets. In the one case it isn't, it's definitely intentional for
a "sketchy" vibe.</p>
<h3 id="chips-are-intentionally-underpowered">Chips are intentionally underpowered</h3>
<p>We know modern chips are extremely powerful. So powerful it would render
the game pointless. So the chips and assembly language in this game are
intentionally limited. The smaller chip only holds 9 instructions, the
larger one 14.</p>
<p>I sometimes find it hard to implement any meaningful logic with such an
underpowered chip. Once I found a solution that was 15 lines long, but
I had to split them in two chips, and then I had to write extra code so
they could communicate, which is another 10+ lines of overhead.</p>
<p>Hardware-wise, the chip has 6 pins max, and you can't just use <em>any</em> pin,
because some pins are "simple", and some are "XBus".</p>
<h3 id="conditional-instructions-are-cursed">Conditional instructions are cursed</h3>
<p>There's no actual "conditional branch". Give the chip a condition (e.g.
<code>teq acc 1</code> tests if <code>acc == 1</code>) and it sets a global state (+/-). Then
you can prepend a +/- to an instruction so that it only runs in the state.
The global state makes it really hard to emulate nested <code>if</code> statements.
Pretty sure there were actual ISAs that used this mechanism, but I always
get confused on this logic.</p>
<h3 id="labels-count-as-lines">Labels count as lines?</h3>
<p>If you wanted to jump to an instruction, you have to label the line, like</p>
<pre><code>loop:
add 1
jmp loop
</code></pre>
<p>It should make sense to any programmer that labels are not real. They are
just a fancy way to refer to the line number it precedes, and thus should
not count as a line. But in the above code it does, because the text area
only has so many rows. This leaves me with one less line for actual
instructions. It took me way too long to realize that this is legal:</p>
<pre><code>loop: add 1
jmp loop
</code></pre>
<p>The datasheet never mentioned this! I was looking at the
<a href="https://www.reddit.com/r/shenzhenIO/wiki/index/">leaderboard</a> when
I found out.</p>
<h3 id="canvas-size-is-limited">Canvas size is limited</h3>
<p>The wire traces take a significant amount of space and there is only one
side, so no vias. They will sometimes block pins. The only way to cross
two wires is to use a bridge, which only comes in one size and direction.
I have more than once made a circuit that looks like an absolute jumbled
mess. Like what even is this</p>
<details>
<summary>Spoiler</summary>
<p><img alt="Circuit with four bridges" src="https://fkfd.me/random/img/shenzhen_io_review/sandwich_machine.png"/></p>
</details>
<p>There was one level where I just <em>can't</em> get the wires to go where they
should. I tried every direction possible to route the wires, but there's
either not enough horizontal space or enough vertical space. The most
disgusting thing is this kind of notch that you can't route wires through.
These notches made two hours of my life miserable. I almost gave up.</p>
<p><img alt="Tiny notch in circuit board with wires around it, but not through it" src="https://fkfd.me/random/img/shenzhen_io_review/shoes_notch.png"/></p>
<p>Then I downloaded a solution from the leaderboard, only to find that, like
in real life, you can route traces <em>below</em> the chips. <em>Nobody mentioned
that!</em> There was nothing in the manual that explicitly said this was ok,
nor was there any hint in the game. The hack is only accessible if you
hold Tab, which enters "show wires" mode, and the chips become translucent
so you can route traces underneath.</p>
<details>
<summary>Spoiler</summary>
<p>Normal view:</p>
<p><img alt="Circuit board with chips and wires" src="https://fkfd.me/random/img/shenzhen_io_review/shoes.png"/></p>
<p>When holding Tab:</p>
<p><img alt="Same circuit but wires under chips are visible" src="https://fkfd.me/random/img/shenzhen_io_review/shoes_wires.png"/></p>
</details>
<p>I doubt this assignment is possible without this kind of hack. Anyway,
I deleted the leaderboard solution and made my own version, and I'm never
looking back.</p>
<h2 id="hardest-assignments">Hardest assignments</h2>
<p>These are a list of assignments that I felt stuck on for more than an
hour.</p>
<details>
<summary>Spoiler</summary>
<ul>
<li>Virtual reality buzzer (the level that led me to quitting in 2021)</li>
<li>Precision food scale</li>
<li>Traffic signal</li>
<li>Color changing shoes (titled "Would you believe it?" in game)</li>
</ul>
</details>
<p>Again, I'm not good at this. If I had to find an excuse, it would be that
I try my best to come up with the most elegant solution possible, however
many lines it takes. I'd use a divide & conquer approach that's as
symmetric as possible, which is not always optimal, and sometimes uses up
too much of space and makes a mess of wires.</p>
<h2 id="a-nitpick-on-the-awkwardness-of-languages">A nitpick on the awkwardness of languages</h2>
<p>The game is created by a bunch of Americans, for, I suppose, a primarily
English-speaking audience. Despite this, the game and the datasheets are
available in both English and simplified Chinese. The problems are:</p>
<ul>
<li>The Chinese does not sound Chinese</li>
<li>The English does not sound Chinese</li>
</ul>
<p>If you tried to play the Chinese version as a Chinese person, as I have,
you will find the Chinese dialog awkward. The translator did their best,
but it sounds un-Chinese.</p>
<p>They are grammatically correct, but I find this sort of conversation out
of place in a Chinese workplace. However, I would prefer if real life
workplace conversations sounded like this. Light-hearted without much
客套话 (formalities). Let's hope society evolves that way.</p>
<p>On the other hand, if you play in English, you'll find that your Chinese
coworkers have extraordinarily high English competency, atypical of
current day Shenzhen (but the game is set in the future, so one can hope).</p>
<h2 id="minigame-shenzhen-solitaire">Minigame: Shenzhen Solitaire</h2>
<p>Early in the game a coworker introduces a solitaire-style game with
Mahjong-themed cards. It's a way to relax when I'm stressed out solving
a puzzle. I like it so much that I <a href="https://git.sr.ht/~fkfd/shenzhen_solitaire">rewrote it in
Rust</a>.</p>
<h2 id="conclusion">Conclusion</h2>
<p>At the time of writing, I have not finished the game (yet). So far it's
really enjoyable. I did stress myself out a handful times halfway; that
was because I was not treating it as a game. Once I accepted the fact that
I'm just a casual player and being unable to solve the puzzles does not
result in any loss of self worth, I was again ready to have fun.</p>
https://fkfd.me/umich/f23_wrapup/
Fall 2023 wrapup — fkfd.me
2023-12-19T01:12:29Z
<h1 id="fall-2023-wrapup">Fall 2023 wrapup</h1>
<p>2023-12-18</p>
<p>My first semester at UMich is over, so here's my review.</p>
<h2 id="course-review">Course review</h2>
<h3 id="eecs-370">EECS 370</h3>
<p>Rating: <strong>4.5 / 5</strong></p>
<p>Theme: computer organization and (micro)architecture</p>
<h4 id="instructor-mark-brehob">Instructor (Mark Brehob)</h4>
<p>Pros:</p>
<ul>
<li>Tons of experience, both industrial and academic</li>
<li>Knows everything</li>
<li>Is furry-inclusive</li>
</ul>
<p>Cons:</p>
<ul>
<li>Talks too fast</li>
</ul>
<h4 id="things-i-learned">Things I learned</h4>
<ul>
<li>How assembly is written</li>
<li>How assembly is executed</li>
<li>Assembler & linker</li>
<li>Processors<ul>
<li>Single-cycle</li>
<li>Multi-cycle</li>
<li>Pipeline</li>
</ul>
</li>
<li>Cache</li>
<li>Virtual memory</li>
</ul>
<p>I had a headstart with the single-cycle processor thanks to
<a href="../../projects/nand2tetris_1/">nand2tetris</a> but the pipeline really blew
up my head. It explains why modern computers are so fast while not running
into timing errors all the time.</p>
<p>Cache and virtual memory, however, are just book-keeping, and make up for
the most boring exam questions. Virtual memory (in the scope of 370)
involves a lot of handwaving. Like, I know what page tables look like. But
who's managing it? Is the TLB part of the ISA? If I wanted to write
a kernel how much work is on my side? So many questions that hopefully
will be resolved in 482 (operating systems).</p>
<h4 id="projects">Projects</h4>
<p>All projects are written in C.</p>
<ul>
<li>p1<ul>
<li>p1a: assembler (LC2K → binary)</li>
<li>p1s: simulator (simulates binary)</li>
<li>p1m: multiplication (write a program that calculates 6203×1429 in
assembly)</li>
</ul>
</li>
<li>p2<ul>
<li>p2a: assembler (LC2K → object file)</li>
<li>p2l: linker (object files → binary)</li>
</ul>
</li>
<li>p3: pipeline simulator</li>
<li>p4: cache simulator</li>
</ul>
<p>Some argue p2l is the hardest part, but that isn't the case for me. I find
p3 the most difficult in that (a) there's a lot of data shuffled around in
the pipeline and (b) it's the only project I went to an office hour for.</p>
<h4 id="favorite-moments">Favorite moments</h4>
<ul>
<li>When Mark came in wearing a pointy hat he said was for "ease of
identification in office hours"</li>
<li>When four people showed up in their fursuits on Halloween and Mark was
like "hey cool can I take a photo afterwards"</li>
</ul>
<h3 id="eecs-281">EECS 281</h3>
<p>Rating: <strong>3.5 / 5</strong></p>
<p>Theme: data structures and algorithms</p>
<h4 id="instructor-marcus-darden">Instructor (Marcus Darden)</h4>
<p>Pros:</p>
<ul>
<li>Pretty good at explaining things</li>
<li>Has a "lecture" playlist that he plays before lecture</li>
<li>You <em>have</em> to see how long his hair is</li>
</ul>
<p>Cons:</p>
<ul>
<li>Sometimes slow</li>
</ul>
<h4 id="things-i-learned_1">Things I learned</h4>
<ul>
<li>How to use C++ STL efficiently</li>
<li>Big O notation</li>
<li>Common data structures and how they manage memory<ul>
<li>Vectors & deques</li>
<li>Binary heaps</li>
<li>Hash tables</li>
<li>Graphs</li>
</ul>
</li>
<li>Algorithms<ul>
<li>Sorts</li>
<li>Backtracking & branch-and-bound</li>
<li>Dynamic programming</li>
</ul>
</li>
</ul>
<p>VG151 (intro to computer programming) made me hate C++, because what the
fuck is polymorphism anyway. EECS 280 made me not hate it, and 281 made me
kind of like it (for the job it is designed for). In the four projects
I hardly ever inherited a class, or managed memory with <code>new</code> and
<code>delete</code>.</p>
<p>My favorite topics are hash tables and dynamic programming. Imagine this:
you've been forced to do your work on those pathetic
flip-out-of-the-armrest desks, and suddenly someone gives you a huge
table. This is what it feels to be given O(n²) space when all you've got
so far is O(n), or worse, O(1).</p>
<p>Fun fact: Before I took this course, I did not believe hash tables worked
like this. Like, seriously, why are you leaving more than half the buckets
empty??</p>
<h4 id="projects_1">Projects</h4>
<ul>
<li>p1: puzzle solver with DFS (stack) and BFS (queue)</li>
<li>p2: priority queue<ul>
<li>p2a: shooting zombies (with <code>std::priority_queue</code>)</li>
<li>p2b: implementing your own priority queue</li>
</ul>
</li>
<li>p3: bank simulator (<code>std::unordered_map</code>)</li>
<li>p4: graphs<ul>
<li>part a: maximum spanning tree with Prim's algorithm</li>
<li>part b: fast TSP (traveling salesman problem) at O(n²)</li>
<li>part c: optimal TSP at O(n!)</li>
</ul>
</li>
</ul>
<p>Note: p4b does not require a certain algorithm and is the only open-ended
"optimization", graded on how close your result is to the instructors'.
The instructors don't have an optimal answer either, because that would be
O(n!) and take forever.</p>
<p>My favorite project is p3, and it is the only project that I got 100/100.
Reason: it's the only project that is something you'd use in real life
applications. Managing a database sort of stuff.</p>
<p>Project 4 is easier for me than p1 and p2 actually. There's a lot of
nitty-bitty in the latter two. Like output formats and ambiguous
specification. My solution leaves room for improvement, but the 3% or 4%
deduction barely justifies the extra work, so I didn't even bother. If the
projects were better I'd give the course a 4 / 5.</p>
<h4 id="favorite-moment">Favorite moment</h4>
<ul>
<li>When I wrote a Tampermonkey script that plays Bad Apple!! on the
autograder, shared a video on Piazza, and got 28 "good note"s</li>
</ul>
<p><img alt='Piazza note titled "why my autograder playing bad apple" with a video of
Bad Apple!! playing on a 20×20 table, in cells colored red and
green' src="https://fkfd.me/umich/img/f23_wrapup/badapple.png"/></p>
<h3 id="german-103">GERMAN 103</h3>
<p>Rating: <strong>5 / 5</strong></p>
<p>Theme: elementary German</p>
<p>GERMAN 103 is 101 and 102 condensed in one semester, and so it is
<em>f a s t</em>. That didn't stop me from slaying it though.</p>
<h4 id="instructor-laura-okkema">Instructor (Laura Okkema)</h4>
<p>Absolute legend. If you disregard her competence at teaching German (which
she has a lot of), she:</p>
<ul>
<li>is super patient and responsible</li>
<li>uses postcards, her paintings, or her son's toys to divide class in
teams</li>
<li>had a dog and a cat when semester began</li>
<li>saw a dog abandoned on the street and went "oh I'll take her"</li>
<li>now has two dogs and a cat</li>
<li>enjoys melodic death metal</li>
<li>gave us precious feedback regarding profanity usage in German (see <a href="#rollenspiel-roleplay">§
Rollenspiel</a></li>
</ul>
<p>The only negative things I have to say about this course:</p>
<ul>
<li>We had to write an essay (~10 sentences long) and memorize it for every
test, which is in my opinion not a productive way to design a test</li>
<li>Feedback on Aufsätze is often as slow as a week (it's useful though)</li>
<li>The textbook is hella expensive and MindTap has a pretty bad user
experience</li>
</ul>
<h4 id="assignments">Assignments</h4>
<ul>
<li>MindTap (online homework thing which I forgot more than once)</li>
<li>Arbeitsblätter (worksheets for each chapter)</li>
<li>3 Aufsätze (essays)</li>
<li>Wiederholungsblätter (test reviews)</li>
<li>Summary of the book »Oh, wie schön ist Panama« by Janosch</li>
</ul>
<p>The Aufsätze is one of the only things where I have total creative
freedom, so I abused this privilege. In the first two I wrote about:</p>
<ul>
<li>Das Beamermännchen (little man named Greg who lives in a projector)</li>
<li>Die Hexe (a witch named Melissa who cast a storm-repelling spell on
a flight)</li>
</ul>
<p>My instructor liked the first one so much she printed it out and hung it
on the bulletin board :)</p>
<h4 id="rollenspiel-roleplay">Rollenspiel (roleplay)</h4>
<p>This section is primarily for my own archival purposes. It is not intended
for a general audience, but it's not top secret either.</p>
<p>tl;dr: My team performed a roleplay and won $200</p>
<details>
<summary>Warning: MASSIVE infodump</summary>
<p>The third Aufsatz is a group project, intended to be played out on stage.
I knew immediately what I wanted.</p>
<p>In the book »Oh wie schön«, on their way to Panama, the bear and the tiger
meet a fox, who "wanted to celebrate his birthday with a goose". (Janosch
18) Innocent as this sounds, an illustration shows the goose lying in the
fox's lap, next to a pot and silverware. The popular belief is that the
goose is the fox's dinner. (Myllynen 26)</p>
<p>I refuse to believe it, so in my headcanon the fox is <em>not</em> going to eat
the goose. Instead he and the goose are a married couple, having an
argument. They verbally abuse each other in front of the tiger and bear,
triggering a dispute between the latter two (who, in my headcanon, are
a gay couple).</p>
<p>I pitched my idea to my groupmates, and we went with it. The problem was
I had no way to end it, but Ava suggested we could give life to the
Tigerente (tiger-duck, which the tiger carries with him all the time like
a baby but doesn't talk) and let him settle the argument.</p>
<p>Now, with a beginning and an end, all we need to do is find a trivial
dispute that could tear not one but two relationships apart. There is zero
description of the fox and goose's life, but there was a lot about the
tiger and bear. The bear goes fishing, and the tiger picks mushrooms. At
the end of the day the bear for both of them. The trivial dispute is then:</p>
<ul>
<li>Tiger is sick of eating fish and mushrooms every day</li>
<li>Bear is the one who cooks</li>
</ul>
<p>Extrapolate this a bit, and we came up with</p>
<ul>
<li>Goose is sick of eating lettuce and potatoes every day</li>
<li>Fox is the one who cooks</li>
</ul>
<p>I believe this is very — almost too realistic. I'm sure food related
breakups happen routinely. What makes this even better is the fact that it
was the fox's birthday, which makes eating the same kind of food more
ridiculous.</p>
<p>However, we put a fundamental difference between the two duos as well:</p>
<ul>
<li>Bear and tiger are traveling to Panama</li>
<li>Fox and goose have stayed home for 20 years, despite fox's promise on
their wedding to travel around the world</li>
</ul>
<p>Which ends in the latter pair joining bear and tiger on their way to
Panama.</p>
<p>Now that we have the plot, we churned out a script. We made sure to
sprinkle in two types of humor:</p>
<ul>
<li>Situational, such as the goose saying "I have no hands, I have wings"
when asked why she never cooks";</li>
<li>Profanity.</li>
</ul>
<p>Yes, vulgarity is how you want the audience to engage. The most famous
German profanity — or perhaps word in general — is Scheiße (shit), but we
didn't end up using it. (This was, however, the first word Laura heard as
she entered the room we were rehearsing in, the moment I knocked over
a soap dispenser.)</p>
<p>The profanity we used were "Wichser" (wanker) and "Hundesohn" (son of
a bitch, or dog literally, which is anatomically not off base because
foxes are somewhat dog-like).</p>
<p>One day on the lecture I asked Laura if it was OK to use these two words.
You should see her face as I threw this question. She went through a phase
of "seriously?" and then affirmed that yes, she's OK with it.</p>
<p>Later, as she wrote feedback for our first draft, she commented on our use
of the two words again. She thinks we should reconsider these words, not
because they're naughty, but because she has better alternatives.</p>
<p>She argues that Wichser is mostly used among teenage boys, and Hundesohn
is kind of outdated, and used rarely in Germany. In addition, she left us
a paper that discusses animal insults. Like a whole 13 page paper. I have
a theory that, she downloaded this paper long ago and waited for this day.</p>
<p>She recommends the insult "dumme Gans" (dumb goose), but unfortunately the
insult was actually directed toward the fox, and to call an animal a "dumb
$animal" is like calling a human "dumb human", and weaken the insult.</p>
<p>We ended up keeping Wichser and Hundesohn, because incidentally bear and
tiger are somewhat teenagers, and the fox is "old" according to the book.
Also, these are pretty famous insults too. This seems to convince Laura.</p>
<p>We also learned that the best team (or two) would be nominated into
a competition with the other German classes, and the winning team would
receive $200 in reward. That said, I believe we had a pretty good chance.</p>
<p>Once we had the final script, we went on to do our first rehearsal. Thomas
caught covid and had to join us over Zoom.</p>
<p>At the same time, I was working on the props. My inner child/engineer
hybrid made this masterpiece:</p>
<p><img alt="Tigerente made out of cardboard" src="https://fkfd.me/umich/img/f23_wrapup/tigerente.jpg"/></p>
<p>We had one more session of rehearsal before our class performance. We
couldn't afford full costumes, so we just improvised as best as we could:</p>
<ul>
<li>Ava (goose) brought a feather scarf or something</li>
<li>Thomas (bear) wore a brown jacket</li>
<li>I (tiger) wore a yellow T-shirt</li>
</ul>
<p>Ryan also brought a butter knife. The guidelines forbid weapons, so it was
plastic. We did our final rehearsal outside of the library.</p>
<p>On performance day, I brought my Tigerente. Realizing I don't have
a string to pull him with, I used my USB microphone with a clip on one
end.</p>
<p>The performance went smoothly. The other three groups also did
a spectacular job. Their roleplays were:</p>
<ul>
<li>Rotkäppchen-artig (the little red riding hood with a twist)</li>
<li>Geistjäger (ghostbusters)</li>
<li>Fortnite (featuring lines such as „Ach! Ich bin gestorben!“ and a sheet
of paper that just said "GUN")</li>
</ul>
<p>Personally I enjoyed Rotkäppchen-artig the most but somehow Geistjäger was
nominated. Oh yeah our play was nominated too.</p>
<p>The competition was set two days later. That day I learned that the
Geistjäger cast couldn't make it, so Rotkäppchen would be competing with
us after all. The other two competitors were from 102, bringing »der
Froschprinz« (the frog prince, which featured a talking piece of bread)
and »der Urlaub« (the vacation, set in Switzerland, that somehow ended in
a guy suffering diarrhea).</p>
<p>Right after our performance, I handed my Tigerente to Hartmut (<em>the</em>
German department professor, who starred all the grammar videos) for his
autograph. He agreed.</p>
<p>After all four groups have performed, we and the professors were asked to vote.
We won, and Rotkäppchen came second. Tied third are the other two groups.</p>
<p>Absolutely wonderful.</p>
</details>
<h4 id="favorite-moments_1">Favorite moments</h4>
<ul>
<li>Rollenspiel of course</li>
<li>When Laura gave us a Kahoot quiz on a Zimtsterne recipe and I was the
only one who got the "convert 150 ℃ to ℉" question right (thanks to
KRunner) and I got a copy of her recipe</li>
</ul>
<h3 id="music-210">MUSIC 210</h3>
<p>Rating: <strong>4/5</strong></p>
<p>Theme: Rap songwriting</p>
<h4 id="instructor-deidre-smith-aka-d-s-sense">Instructor (Deidre Smith, aka D. S. SENSE)</h4>
<p>She's a nice lady who unfortunately missed a lot of lectures due to illness.
Nevertheless, she was nothing but encouraging to every one of us.</p>
<p>Creativity does not constrain me; language is the barrier. I can sometimes fail
to understand her accent, and I'm not a fluent English speaker when it comes to
rap. I took this course partly to train my spoken English, but it didn't seem
to work that well. (skill issue)</p>
<p>Every in-person class we would be given 25 minutes to work on a short piece of
hip hop. I abused this opportunity to:</p>
<ul>
<li>rant about NFTs</li>
<li>write a piece that goes between 7/8, 6/8, and 9/8 time</li>
<li>ask people in the room to decide on my grocery list</li>
</ul>
<p>Anyway, this course gave birth to my first original song, <a href="../../music/uncure-your-depression/">This Song Will
Uncure Your Depression</a>.</p>
<h3 id="chem-130">CHEM 130</h3>
<p>Rating: <strong>4.5/5</strong> (relative to my expectations and wishes, which are low)</p>
<p>I took CHEM 210 two years ago and sucked so much it wasn't even transferable.
I had to repeat it to fill in that hole on my transcript. But here, I almost
know everything, and routinely get full points on the tests.</p>
<p>There are two hypotheses why I sucked:</p>
<ul>
<li>CHEM 210 is inherently harder than 130</li>
<li>skill issue</li>
</ul>
<p>I believe both factors are at play. On one hand 210 defintely covered something
extra (entropy and electrochemistry), but on the other at that time I was
a fresh college student who didn't even know how college worked. This course
was chemistry "principles". It's nothing like high school chemistry that's just
"remember this experiment" and "remember that phenomenon". Instead it's built
upon physical and statistical principles, which I did <em>not</em> know at that time.
Now that I've taken a course on electromagnetics and one on statistics, I can
actually understand what's going on with these "dipoles" (they're just two
opposite charges on a stick) and "electron clouds" (they're just probability
density functions). This is why I wonder why this course is so often taken in
the very first semester of college, when it's not a prerequisite for any other
ECE course. In fact, that makes the experience a little like high school
— learning the "principles" without learning the principles of the principles.</p>
<h2 id="general-comments-concerns">General comments & concerns</h2>
<ul>
<li>I am aware that, as far as infrastructure goes, universities are among the
best places in the US. But even so, there's not enough buses.</li>
<li>Food too expensive. But not terrible.</li>
<li>Drinks too expensive.</li>
<li>The nature is well preserved and air quality is good.</li>
<li>It snows sometimes which is fun if I wasn't waiting for a bus outside.</li>
<li>I have to learn to slow down. When I'm walking in a dining hall, my instincts
urge me to hurry up, which is not a good strategy in a place packed with
people carrying plates.</li>
<li>People here are more welcoming and diverse than expected. I have witnessed
zero cases of xenophobia or racism so far.</li>
<li>I wanted to get an FCC ham radio license but never got around to. Next
semester I guess?</li>
</ul>
<h2 id="significant-material-gains">Significant material gains</h2>
<ul>
<li>I bought a blåhaj</li>
<li>I bought a bass</li>
<li>I bought a laptop</li>
<li>I got a "Michigan Pride" T-shirt at the drag show</li>
<li>I got a "Know two ways out" shirt at a fire safety awareness event</li>
<li>I got an "Arts & Resistance" T-shirt at the Duderstadt gallery</li>
<li>I bought a second-hand CD of <em>The Black Parade</em></li>
<li>I bought two books "just because" and three more for reasons</li>
</ul>
<h2 id="works-cited">Works Cited</h2>
<ul>
<li>Janosch. <em>Oh, wie schön ist Panama</em>. Beltz & Gelberg, 1978.</li>
<li>Myllynen, Milka. "Was schätzen Leser an einem Kinderbuchklassiker?
Aufgezeigt anhand von Kundenrezensionen auf Amazon.de zum Kinderbuch
„Oh, wie schön ist Panama“." Tampere University, April 2019. <a href="https://trepo.tuni.fi/bitstream/handle/10024/105532/MyllynenMilka2019.pdf">PDF
version on
tuni.fi</a>.
Accessed 2023-11-07.</li>
</ul>
https://fkfd.me/random/2bugs1day/
2 Bugs 1 Day — fkfd.me
2023-11-29T16:16:42Z
<h1 id="2-bugs-1-day">2 Bugs 1 Day</h1>
<p>2023-11-23</p>
<p>If I had a nickel every time I encountered a cursed bug today, I'd have
two. It's not a lot but weird how it happened twice.</p>
<h2 id="bug-1">Bug 1</h2>
<p>My roommate asked me about a bug in his C code. He passed a value to
a function. The value is not supposed to be zero, but in the output, it
apparently is. Why?</p>
<p>We could not find any lines of code that could have changed this value. It
was initialized with a command line argument (i.e. <code>atoi(argv[2])</code>), then
remains read-only.</p>
<p>After ruling out off-by-one errors and floating point rounding,
I hypothesize it has something to do with the data structure. The value is
within a struct as follows:</p>
<pre><code class="language-c">struct foo_t {
bar_t bar[256];
int value;
} foo;
</code></pre>
<p><code>foo</code> is a global instance of <code>struct foo_t</code>. To initialize it, we set
<code>value</code> to a command line argument and initialize <code>bar</code> with a for loop,
which looks like:</p>
<pre><code class="language-c">for (int i = 0; i < arg1; i++) {
for (int j = 0; j < arg2; j++) {
foo.bar[i * arg2 * arg3 + j] = 0;
}
}
</code></pre>
<p>I realized that <code>arg3</code> was unnecessary. By multiplying it, we're spacing
out the indices too much. If <code>arg1</code> gets big enough, we'll get a buffer
overflow.</p>
<p>It just so happens that all arguments are powers of 2, and <code>bar</code> is an
array of 256. Which means… You'll overwrite <code>value</code>!</p>
<p>We removed <code>arg3</code> and sure enough, it worked.</p>
<p>Conclusion: Lack of bound checking in C.</p>
<h2 id="bug-2">Bug 2</h2>
<p>Right after we hunted down this bug, my roommate said there's another bug
in VSCode that has bothered him for a while. It seems like whenever he
types the word "store" in a Markdown code block, the syntax highlighting
(regardless of language) breaks. He looked through the VSCode repo and
didn't find anything particular.</p>
<p>We found that the keyword doesn't have to be "store", it just needs to be
"re" + whitespace.</p>
<p><img alt='Some C code in a Markdown code block, containing a string literal "re ".
Highlighter refuses to match curly braces surrounding it, and completely
stops working below.' src="https://fkfd.me/random/img/2bugs1day/re.png"/></p>
<p>What does <code>re</code> stand for? The Python package?</p>
<p>This is so cursed that I decided to make the ultimate sacrifice — to
install VSCode on my own machine.</p>
<p>However, I failed to reproduce. My roommate correctly suggests it's
a problem with a plugin. He disabled all the Markdown-related plugins on
his machine. The bug is still there.</p>
<p>He then disabled all plugins (except those bundled). The bug is gone.</p>
<p>VSCode has a "plugin bisect" tool that does a binary search to find the
problematic plugin in O(log(# plugins)) time. I did that and the problem
is…</p>
<p>OCaml Platform.</p>
<p>Do you realize how shocked we were? Like, we would rather believe it was
ghosts or something. OCaml is literally the single most innocent plugin.</p>
<p>We looked at OCaml Platform's code and found something interesting.</p>
<pre><code class="language-json">{
"repository": {
"reason-code-block": {
"begin": "(re|reason|reasonml)(\\s+[^`~]*)?$",
"end": "(^|\\G)(?=\\s*[`~]{3,}\\s*$)",
...
},
...
}
</code></pre>
<p>I do not know how this works, but boy I sure know regex checks out. We
tried <code>reason</code>, and yes, it broke as well.</p>
<p>This code was added in a 2020 commit, and remains unchanged since. It's
such a minor bug that I believe 90% its users didn't even notice.
I encouraged my roommate to file a bug report — or even a pull request.</p>
<p>My hypothesis is OCaml programmers simply don't program in anything else.</p>
<p>Conclusion: OCaml plugin ships a regex with false positives.</p>
<h2 id="takeaway">Takeaway</h2>
<ol>
<li>There is a reason for every bug.</li>
<li>There is not an obvious reason for every bug.</li>
<li>C will just silently overflow your buffer without you knowing. Can't
wait till Linux is 100% Rust</li>
</ol>
https://fkfd.me/shitpost/cringiest_song/
Cringiest song I've ever heard — fkfd.me
2023-07-30T15:43:05Z
<h1 id="cringiest-song-ive-ever-heard">Cringiest song I've ever heard</h1>
<p>2023-07-30</p>
<p>Many songs are cringe, so much as to become a meme. This blogpost is about
none of that. It is about a song that is cringe to <em>me</em>, even though
I only ever heard it once.</p>
<p>Cue ten-year-old me. My elementary school took all the students to
a theater. It was probably Teacher's Day, hence the afternoon off. There
was neither movie or play, just an assortment of performances to varying
degrees of entertainment. If I remember correctly, there was a magician
who claims to be related to Liu Qian. I had no idea what she was doing.</p>
<p>At some point the host (That Guy) decided to sing an ode to our teachers.
It was a rendition of a very mainstream pop song. I did not know, but
I was about to hear <em>the</em> cringiest song. The verse is some generic pickup
lines and the chorus goes (translated):</p>
<blockquote>
<p>My beloved teacher, I love you<br/>
Let me walk into your world, and be together with you<br/>
My beloved teacher, I love you<br/>
I will do anything for you forever, in this life and the next</p>
</blockquote>
<p>At this point we discovered that this was exactly the original song, but
with the word "teacher" instead of "girl". Not only that, we were all
asked to sing along to the "I love you". I might have cooperated (oops).
What's worse, the teachers were present, which adds to the cringe factor.</p>
<p>By changing one word, That Guy managed to transform an otherwise innocent
romantic song into pure horror. Let me clarify. I do <em>not</em> want to walk
into my teacher's world. I do <em>not</em> want to be "together" with my
teachers. They're real nice, yeah, but that doesn't mean I will "do
anything" or reincarnate myself for them, no thanks.</p>
<p>The whole theater was filled to the brim with cringe. I wonder how they
approved that song. They had one program to celebrate Teacher's Day, and
they celebrated in the worst way possible.</p>
<p>The difference between this song and memes is that memes are funny. It's
just cringe. And it is no longer possible to recreate it. I can tell this
story to you as a joke, and you'd be like, "haha, the song sure is
terrible", but you can't experience the damage it did to a pool of kids.</p>
https://fkfd.me/projects/byseekel/
Byseekel — fkfd.me
2023-07-22T09:10:30Z
<h1 id="byseekel">Byseekel</h1>
<p>2023-07-17</p>
<h2 id="foreword">Foreword</h2>
<p>This blogpost will be a disappointment. A recollection of wasted efforts,
a mausoleum of failed designs, a journal of regrettable choices, and
a maker's acceptance of defeat.</p>
<p>Oh, and it won't be proofread.</p>
<p>Last year, I began building a draft of this blogpost, but never finished
nor published. It was timestamped 2022-05-03. I wish I could write this
one in the same cheerful tone, but I can't.</p>
<p>The project started in December 2021. It was finished in July 2023.
A rational question to ask is:</p>
<p><strong>What the fuck happened??</strong></p>
<h2 id="bikeblinkers-a-recap">Bikeblinkers: a recap</h2>
<p>In September 2021 I broke my wrist in a cycling accident. It gave me an
irrational obsession for cycling safety. So I designed this.</p>
<p><img alt="A pair of PCB-form blinkers in a plastic bottle, mounted at the rear of
a bike. One of them is blinking." src="https://fkfd.me/projects/img/byseekel/bikeblinkers.jpg"/></p>
<p>▲ This is the only photo I have.</p>
<p>It's basically just a 555 timer in astable mode. Two design errors were
made:</p>
<ul>
<li>The switch is on the path to the LEDs, not the 555, so the chip is
always consuming power</li>
<li>The transistors are in common-collector mode, so they aren't bright
enough</li>
</ul>
<p><strong>I was not satisfied.</strong></p>
<h2 id="byseekel-rev-0-1-and-2">Byseekel rev 0, 1, and 2</h2>
<p>December 2021, I went back home with all the Bikeblinker stuff. Finally
I had time to make something better.</p>
<p>I wanted a whole bunch more features, including</p>
<ul>
<li>speedometer and odometer, using reed switch and wheel-mounted magnet</li>
<li>hazard light</li>
<li>braking indicator</li>
<li>remote-activated buzzer</li>
<li>an LCD display</li>
</ul>
<p>At that point, every one of these seemed possible. I smugly named the
project "Byseekel" because that's how I hear Freddie sing "Bicycle race".</p>
<p>Rev 0 is on perfboard. I don't even remember how painful it was to solder.
Nevertheless, it worked. Photo on 2022-01-01.</p>
<p><img alt="Two perfboards with messy wiring" src="https://fkfd.me/projects/img/byseekel/rev0_back.jpg"/></p>
<p>▲ I don't know why I needed three voltage boosters.</p>
<p>🎖️ Achievement made: spaghetti wires</p>
<p><img alt="Same two perfboards, front side up" src="https://fkfd.me/projects/img/byseekel/rev0.jpg"/></p>
<p>I used two microcontrollers (MCUs), because I didn't know how to reliably
switch in and out of sleep mode. The smaller one is an ATmega ATtiny13A</p>
<p>The LCD was intended to go on the handlebar. It was driven with a 4-bit
parallel interface. I wrote the driver myself with the datasheet, so I was
very proud. (It was very glitchy, though)</p>
<p>🎖️ Achievement made: First LCD driver</p>
<p>Starting from Rev 1, I gave in and just made PCBs. This one's taken
2023-01-12.</p>
<p><img alt="PCB with a freshly soldered chip" src="https://fkfd.me/projects/img/byseekel/rev1.jpg"/></p>
<p>▲ I was proud of being able to solder SMT, but it wasn't a good idea.</p>
<p>🎖️ Achievement made: First functioning SMT chip</p>
<p>This board is basically just a single chip and many connectors and
jumpers. These include</p>
<ul>
<li>J1: barrel jack, but instead of 9 or 12, it's 5 volts</li>
<li>J2: SPI, to flash firmware into the chip</li>
<li>J3: an Ethernet port because I need exactly 8 wires (genius moment)</li>
<li>J4: screw connectors</li>
<li>J5: controls array of 4 relays to turn blinkers on and off</li>
<li>JP1: a jumper to disable the relays before programming, because they
share wires, and if I don't pull it out the relay goes "click clack"
nonstop</li>
<li>J6: I thought it'd be cool if I had a few "hackable" pins for future use</li>
<li>U2: radio receiver unit</li>
</ul>
<p>But I fucked up. It's impossible to install U2 and J3 at the same time,
because U2 is wider than printed on the PCB. I had completely forgotten.</p>
<p>Fixed in Rev 2. The only photo, 2023-01-18.</p>
<p><img alt="Soldered and wired PCB" src="https://fkfd.me/projects/img/byseekel/rev2.jpg"/></p>
<p>Also, in Rev 1 I forgot I needed a receptacle for the Ethernet cable. So
I put that in Rev 2.</p>
<p><img alt="Rendering of a PCB with a breakout board" src="https://fkfd.me/projects/img/byseekel/rev2_jdb.png"/></p>
<p>▲ I didn't use mousebites so JDB did not charge me extra. I used a handsaw
to break them apart. Would not do again.</p>
<p>The main problem with Rev 0 to 2 is the relay. Though they make
a satisfying noise, they are way too power hungry for a battery powered
device.</p>
<p><strong>I was not satisfied.</strong></p>
<h2 id="rev-3">Rev 3</h2>
<p>There are too many flaws with Rev 2. I decided to revamp the design
completely.</p>
<p>Problem: The relay is too power hungry.<br/>
Solution: Use transistors.</p>
<p>Problem: I can no longer afford more SMT MCUs.<br/>
Solution: Use DIP.</p>
<p>Problem: I want a marquee-style blinker.<br/>
Solution: 5 outputs instead of 2.</p>
<p>Problem: 4-bit parallel interface is unstable and wastes wires.<br/>
Solution: I2C.</p>
<p>Let's begin by introducing designs I didn't throw away.</p>
<h3 id="controller-board">Controller board</h3>
<p>I intentionally made it credit-card sized. This was in March 2022.</p>
<p><img alt="Front and back of PCB" src="https://fkfd.me/projects/img/byseekel/rev3.jpg"/></p>
<h3 id="stalks">Stalks</h3>
<p><img alt="A pair of stalks. One has exposed leads" src="https://fkfd.me/projects/img/byseekel/stalks.jpg"/></p>
<p>Blinker stalks for electric motorbikes. They're very cheap. You just pull
the lever to the left or right, or both to get hazard lights. Press down
to reset.</p>
<h3 id="led-strips">LED strips</h3>
<p>Planned red for brakes also, but now only yellow for blinkers. Scraped off
some epoxy and soldered wires to the common anode and five cathodes. The
voltage booster converts 5V to 12V.</p>
<p><img alt="The end of an unsoldered LED strip" src="https://fkfd.me/projects/img/byseekel/led_strip.jpg"/></p>
<p>▲ Then I sealed it in transparent heat shrink tube for waterproof. </p>
<h3 id="radio-module">Radio module</h3>
<p>433MHz OOK radio receiver, controlled with a garage door remote. Can store
and respond to four keys. Currently assigned three to:</p>
<ul>
<li>Lock (beep-beep, goes to sleep mode)</li>
<li>FindMyBike™ (long beep)</li>
<li>FindMyBike™ Silent Mode (flash blinkers)</li>
</ul>
<h3 id="buzzer">Buzzer</h3>
<p>It's just a piezo with two LEDs. It beeps when you give it 12V.</p>
<p><img alt="Anatomical view of buzzer" src="https://fkfd.me/projects/img/byseekel/buzzer.jpg"/></p>
<h3 id="ethernet-cable">Ethernet cable</h3>
<p>In my original design I needed a bundle of eight wires that runs the
entire length of my bike. An ethernet cable is a bundle of eight wires.
Genius idea.</p>
<p>Now that I only need three, it's still a genius idea because it's cheap
and so much easier to organize than three speaker wires.</p>
<p>Now, we'll move on to designs that failed.</p>
<h2 id="failed-designs">Failed designs</h2>
<p>Don't ask a Shanghainese what happened in 2022.</p>
<p>Anyway, I stayed at home from March to June, without access to my bicycle
or stuff like oscilloscopes. So many ideas worked on my workbench, but
couldn't be tested.</p>
<p><img alt="Line sketch of two boards. Stemming from controller board: power bank,
blinkers, braking lights, buzzer, reed. In between: ethernet cable.
Control panel board: ctrl, stalk, LCD, brake" src="https://fkfd.me/projects/img/byseekel/feature_creep.png"/></p>
<p>In the end, they grew so complicated I had to throw them away.</p>
<p><img alt="braking lights, reed, ctrl, LCD, brake and the control panel are crossed
out" src="https://fkfd.me/projects/img/byseekel/feature_creep_removal.png"/></p>
<h3 id="speedometer-odometer">Speedometer & odometer</h3>
<p>My uni has a required 80km of jogging per semester, tracked by GPS to
ensure you're neither too fast or too slow. We cheat it by cycling. Based
on my knowledge, I can stick a magnet to the spoke of the wheel, and mount
a reed switch somewhere so each time the magnet passes, we know the wheel
made one turn.</p>
<p>Problem is, it was winter break, and my bike wasn't home. So to test the
mechanism, I just held the magnet in my hand and went "whoosh whoosh
whoosh".</p>
<p>Did it work? On paper, yes. In practice? Probably not.</p>
<p>Then why did I attempt anyway? Perhaps it's because I hate jogging so
much. My superinflated maker ego was like, "Company xxx made this so
I must also be able to." Wrong. There are limits as to what one person can
do at home, even if it "sounds possible".</p>
<p>Right now, 80km is no longer a requirement for me, so I just dropped the
idea, along with the LCD and control panel.</p>
<h3 id="lcd">LCD</h3>
<p>It's a pity I cut it out. It almost worked.</p>
<p>The LCD would serve as a HUD for:</p>
<ul>
<li>speed in km/h and min/km</li>
<li>trip and total mileage (stored in EEPROM)</li>
<li>status of blinkers, brake, and hazard light</li>
</ul>
<p>I bought an I2C-to-4-bit adaptor board, and rewrote my driver (based on
<a href="https://github.com/Sovichea/avr-i2c-library">Sovichea Tep's TWI driver</a>).</p>
<p>🎖️ Achievement made: First I2C LCD driver</p>
<p>The LCD was less glitchy than in parallel mode. In parallel, when the GPIO
switched, the voltage overshot before it damped down. Thanks to Kliment,
I learned this was called ringing. But the dedicated circuitry for I2C
damps harder, so there's less of this problem.</p>
<p>Less, not none though.</p>
<h3 id="control-panel">Control panel</h3>
<p>One of the ideas that seem good on paper.</p>
<p><img alt="CAD rendering. A stack of boards with parts sandwiched
in." src="https://fkfd.me/projects/img/byseekel/panel_freecad.png"/></p>
<p>▲ Made with FreeCAD.</p>
<p>Sometimes, you just feel too confident. You think you're a god. You think
you can do anything, including mechanical engineering.</p>
<p>The intention is to laser cut eight pieces of acrylic, and bolt them
together. All seemed perfect.</p>
<p><img alt="Laser cut contours" src="https://fkfd.me/projects/img/byseekel/panel_laser.png"/></p>
<p>In November 2022 I tried to cut them. But the laser cutter didn't like the
dxf exported from FreeCAD via Inkscape. Each edge was not one line but
two, so the laser head went over each edge twice. RIP acrylic.</p>
<p><img alt="Acrylic with severely burnt edges" src="https://fkfd.me/projects/img/byseekel/panel_burnt.jpg"/></p>
<h3 id="ctrl-button">Ctrl button</h3>
<p>The ctrl button was supposed to switch the HUD when pressed and enter
sleep mode when held.</p>
<h3 id="brake">Brake</h3>
<p>The brake detection is the single least realistic design I've ever come up
with. I can't believe how I let it pass. I won't waste time trying to
explain it again. Here's a figure I drew last year. Go figure.</p>
<p><img alt="" src="https://fkfd.me/projects/img/byseekel/brakes_idea.png"/></p>
<h2 id="dormant-state-and-revival">Dormant state and revival</h2>
<p>I did practically nothing for the project between October 2022 to March
2023, because DON'T ASK A SHANGHAINESE WHAT HAPPENED IN 2022. Anyway, in
April 2023 I found myself motivated once again, partly because there's
a new hackerspace.</p>
<p>I got this box in 2022. Now I'm finally doing things to it. Horrible,
horrible things.</p>
<p><img alt="Left: a badly drilled hole on the box. Right: buzzer inserted to said
hole." src="https://fkfd.me/projects/img/byseekel/buzzer_drill.jpg"/></p>
<p>▲ I didn't have a suitable grinder, so I just drilled around until the
small holes formed a big hole.</p>
<p><img alt="Left: the box put vertically in the basket; blinkers taped to basket
rim. Right: Stalk taped to handlebar." src="https://fkfd.me/projects/img/byseekel/basket_mount.jpg"/></p>
<p>▲ This made the bicycle difficult to steer, so later I moved the box to
the back.</p>
<h2 id="byseekel-now">Byseekel, now</h2>
<p>What does Byseekel look like, now that I killed every feature I wasn't
ready for?</p>
<p>We've gone full circle.</p>
<p>Bikeblinkers was blinkers you controlled with a stalk. Right now, Byseekel
is essentially the same thing.</p>
<p>Except:</p>
<ul>
<li>better stalk</li>
<li>better wiring</li>
<li>powered by MCU</li>
<li>marquee</li>
<li>FindMyBike™</li>
<li>IP-somewhat waterproof</li>
</ul>
<p>Note that, although there's an ATmega328P, after all the features I killed
it's essentially the same as your typical Arduino code.</p>
<p>When I have photos and videos, I will post them here.</p>
<h3 id="2023-07-17-update">2023-07-17 update</h3>
<p><img alt="Left: interior of a box, containing a PCB on top of a lithium battery.
Right: same box mounted to a bike" src="https://fkfd.me/projects/img/byseekel/assembly.jpg"/></p>
<p>▲ It's mounted with tape, and when it rained, I just cover the box with
a plastic bag. Am I an engineer now?</p>
<p>So far I've easily rode it around for more than 50 km. The tape seems to
be holding it together pretty well, even on bumpy roads. The box has been
exposed to hours of direct sunlight in 38C weather and rain that made the
campus quite literally a water park and the lithium battery hasn't
exploded or caught fire. This is all I ever wanted.</p>
<h3 id="2023-07-22-update">2023-07-22 update</h3>
<video controls=""><source src="../img/byseekel/wtf.mp4"/></video>
<p>MPEG-4 Video (4.6 MiB)</p>
<h2 id="conclusion">Conclusion</h2>
<p>Whose fault is it that Byseekel wasn't finished until now?</p>
<ul>
<li>It is my fault that I overcomplicated the project</li>
<li>It is not my fault that I was locked home for half a year</li>
</ul>
<p>I am now speaking to future self.</p>
<p>Do you want to add a "useful feature" to a project? You gotta get the
project working without it first. A new batch of PCBs cost 30 RMB. That's
cheaper than hours of time you'd waste otherwise. Don't sit on your
computer, working your ass to make your CAD model "perfect". You can't
know what's perfect if you never test it.</p>
<p>If I had another chance, I'd kill the brake and reed ideas. The LCD taught
me to write I2C drivers, and to write AVR C with interrupts; I'd give it
a pass.</p>
<p>Always remember, do one thing and do it well.</p>
<p>The KiCad and AVR C files for Byseekel and Bikeblinkers are on
<a href="https://codeberg.org/fkfd/byseekel">Codeberg</a>.</p>