-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathindex.xml
More file actions
2936 lines (1850 loc) · 286 KB
/
index.xml
File metadata and controls
2936 lines (1850 loc) · 286 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<title>Blake Cole</title>
<link>https://www.blakecole.com/</link>
<atom:link href="https://www.blakecole.com/index.xml" rel="self" type="application/rss+xml" />
<description>Blake Cole</description>
<generator>Hugo Blox Builder (https://hugoblox.com)</generator><language>en-us</language><lastBuildDate>Mon, 24 Oct 2022 00:00:00 +0000</lastBuildDate>
<image>
<url>https://www.blakecole.com/media/icon_hu1996601983673389572.png</url>
<title>Blake Cole</title>
<link>https://www.blakecole.com/</link>
</image>
<item>
<title>Example Talk</title>
<link>https://www.blakecole.com/event/example/</link>
<pubDate>Sat, 01 Jun 2030 13:00:00 +0000</pubDate>
<guid>https://www.blakecole.com/event/example/</guid>
<description><div class="px-4 py-3 mb-6 rounded-md bg-primary-100 dark:bg-primary-900">
<pre><code>&lt;div class=&quot;flex&quot;&gt;
&lt;span class=&quot;pr-3 pt-1 text-primary-600 dark:text-primary-300&quot;&gt;
&lt;svg height=&quot;24&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;0 0 24 24&quot;&gt;&lt;path fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;1.5&quot; d=&quot;m11.25 11.25l.041-.02a.75.75 0 0 1 1.063.852l-.708 2.836a.75.75 0 0 0 1.063.853l.041-.021M21 12a9 9 0 1 1-18 0a9 9 0 0 1 18 0m-9-3.75h.008v.008H12z&quot;/&gt;&lt;/svg&gt;
&lt;/span&gt;
&lt;span class=&quot;dark:text-neutral-300&quot;&gt;Click on the &lt;strong&gt;Slides&lt;/strong&gt; button above to view the built-in slides feature.&lt;/span&gt;
&lt;/div&gt;
</code></pre>
</div>
<p>Slides can be added in a few ways:</p>
<ul>
<li><strong>Create</strong> slides using Hugo Blox Builder&rsquo;s <a href="https://docs.hugoblox.com/reference/content-types/" target="_blank" rel="noopener"><em>Slides</em></a> feature and link using <code>slides</code> parameter in the front matter of the talk file</li>
<li><strong>Upload</strong> an existing slide deck to <code>static/</code> and link using <code>url_slides</code> parameter in the front matter of the talk file</li>
<li><strong>Embed</strong> your slides (e.g. Google Slides) or presentation video on this page using <a href="https://docs.hugoblox.com/reference/markdown/" target="_blank" rel="noopener">shortcodes</a>.</li>
</ul>
<p>Further event details, including <a href="https://docs.hugoblox.com/reference/markdown/" target="_blank" rel="noopener">page elements</a> such as image galleries, can be added to the body of this page.</p>
</description>
</item>
<item>
<title>Geometric programming for aerodynamically-actuated wingsail design optimization</title>
<link>https://www.blakecole.com/publication/jrnl-ieee-joe-2025/</link>
<pubDate>Sat, 26 Jul 2025 00:00:00 +0000</pubDate>
<guid>https://www.blakecole.com/publication/jrnl-ieee-joe-2025/</guid>
<description></description>
</item>
<item>
<title>Reverse Engineering an LED Display</title>
<link>https://www.blakecole.com/post/3-led-timer/</link>
<pubDate>Wed, 16 Apr 2025 08:18:13 -0400</pubDate>
<guid>https://www.blakecole.com/post/3-led-timer/</guid>
<description>
<details class="print:hidden xl:hidden" open>
<summary>Table of Contents</summary>
<div class="text-sm">
<nav id="TableOfContents">
<ul>
<li><a href="#backstory">Backstory</a></li>
<li><a href="#topics-not-covered">Topics Not Covered</a></li>
<li><a href="#methodology">Methodology</a></li>
<li><a href="#inspect-hardware">Inspect Hardware</a>
<ul>
<li><a href="#power-supply">Power Supply</a></li>
<li><a href="#microcontrollers">Microcontrollers</a></li>
<li><a href="#shift-registers">Shift Registers</a></li>
</ul>
</li>
<li><a href="#modify-hardware">Modify Hardware</a>
<ul>
<li><a href="#signal-injection">Signal Injection</a></li>
<li><a href="#a-small-setback">A Small Setback</a></li>
<li><a href="#adding-garnishes">Adding Garnishes</a></li>
</ul>
</li>
<li><a href="#write-software">Write Software</a>
<ul>
<li><a href="#timer-logic">Timer Logic</a></li>
<li><a href="#numeric-digits">Numeric Digits</a></li>
</ul>
</li>
<li><a href="#build-controller">Build Controller</a>
<ul>
<li><a href="#final-stretch">Final Stretch</a></li>
<li><a href="#stress-testing">Stress Testing</a></li>
</ul>
</li>
<li><a href="#reflection">Reflection</a></li>
</ul>
</nav>
</div>
</details>
<h2 id="backstory">Backstory</h2>
<p>Last September, my brother came to me with a conundrum: the controller for a high-power LED timer he had been using to run our local community surfing competitions had been stolen. To make matters worse, the original manufacturer wanted more than $1,000 to replace it &ndash; a sum which far exceeded the modest budget of the nonprofit organization for which he volunteers, the <a href="https://southbayboardriders.org/" target="_blank" rel="noopener">South Bay Boardriders Club (SBBC)</a>. With competitions scheduled to resume in just a few months, they were at a loss for what to do.</p>
<figure style="text-align: center; margin-top: 50px; margin-bottom: 50px;">
<a data-fancybox="" href="sbbc.png" style="display:inline-block; margin:0; padding:0; line-height:0;" data-caption="LED timer in action at a surf competition in Manhattan Beach, CA.">
<img src="sbbc.png" style="display:block; margin:0; padding:0;" alt="LED timer in action at a surf competition in Manhattan Beach, CA." >
</a>
<figcaption style="text-align: left;" data-pre="Figure " data-post=":" >
<p>
LED timer in action at a surf competition in Manhattan Beach, CA.
</p>
</figcaption>
</figure>
<p>I told them I couldn&rsquo;t make any promises, but I&rsquo;d have a look. I proceeded to take apart the LED display box and inspect the copper traces on the manufacturer&rsquo;s printed circuit board (PCB) using my backpacking headlamp. I identified the power source, microcontroller, and an array of shift registers. I realized that if I could carefully remove the manufacturer&rsquo;s microcontroller, and replace it with one of my own, I could inject serial data into the shift registers to control each of the 30 LED channels.</p>
<p>I didn&rsquo;t want to replicate the prior controller architecture, I wanted to make it better. So, I made it wireless and waterproof. I also added custom toggle buttons to improve the end-user&rsquo;s experience. I soldered the requisite wires into through holes on the manufacturer&rsquo;s PCB, covered the joints with conformal coating, and ran a sequence of tests to identify any bugs in my software. The controller worked flawlessly, and my total bill of materials was an order of magnitude less than the manufacturer&rsquo;s quote &ndash; about $135.</p>
<figure style="text-align: center; margin-top: 50px; margin-bottom: 50px;">
<a data-fancybox="" href="finished_product.png" style="display:inline-block; margin:0; padding:0; line-height:0;" data-caption="The finished product.">
<img src="finished_product.png" style="display:block; margin:0; padding:0;" alt="&lt;center&gt;The finished product.&lt;/center&gt;" width="450" >
</a>
<figcaption style="text-align: left;" data-pre="Figure " data-post=":" >
<p>
<center>The finished product.</center>
</p>
</figcaption>
</figure>
<h2 id="topics-not-covered">Topics Not Covered</h2>
<p>This project involved a few microcontroller flourishes that I chose not to cover:</p>
<ul>
<li>Wireless communication using the <a href="https://www.espressif.com/en/solutions/low-power-solutions/esp-now" target="_blank" rel="noopener">ESP-NOW</a> protocol</li>
<li>Reducing power consumption using <a href="https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/system/sleep_modes.html" target="_blank" rel="noopener">deep-sleep</a> mode</li>
<li>External hardware interrupts</li>
<li>Shift register control</li>
</ul>
<p>I decided not to cover these topics in detail because fantastic resources already exist online. Instead, I decided to focus mostly on the narrative journey, and provide some insight into my thought process as I worked through the problem.</p>
<p>If you&rsquo;re interested in learning more about microcontrollers and hobby electronics, I&rsquo;d highly recommend checking out the <a href="https://www.youtube.com/@Dronebotworkshop" target="_blank" rel="noopener">DroneBot Workshop</a> channel on YouTube. Bill is a fantastic educator, and one of my favorite engineering content creators.</p>
<p>If you are curious about my specific implementation, feel free to <a href="../../#contact">reach out</a>.</p>
<h2 id="methodology">Methodology</h2>
<p>One of the most important lessons I learned in graduate school was the value of breaking large, complex problems into a sequence of simpler ones with measurable outcomes. This framework permits incremental progress, and reduces the likelihood that you&rsquo;ll waste a bunch of time on a problem you can&rsquo;t actually solve. This was my first guiding principle:</p>
<div class="px-4 py-3 mb-6 rounded-md bg-primary-100 dark:bg-primary-900">
<div class="mb-2 font-bold text-primary-600 dark:text-primary-300">
Guiding Principle #1: Incremental Progress
</div>
<div class="dark:text-neutral-300">Break problems into a series of sequential hurdles with measurable outcomes.</div>
</div>
<p>When I first got my hands on the LED timer, I had no idea whether I&rsquo;d actually be able to fix it. There were so many uncertainties:</p>
<ul>
<li>What is the power and control architecture inside the box?</li>
<li>Will I be able to make sense of the manufacturer&rsquo;s PCB?</li>
<li>Could I modify the manufacturer&rsquo;s PCB without destroying it?</li>
<li>Is the power system functional?</li>
<li>Could I write a software program that mimics countdown timer logic?</li>
<li>How might I allow the end-user to control the timer in an intuitive manner?</li>
</ul>
<p>I took a deep breath, and started breaking down the problem, working backwards from my desired endpoint.</p>
<script src="https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.min.js"></script>
<script>
document.addEventListener("DOMContentLoaded", function() {
function initMermaidDiagrams(theme) {
mermaid.initialize({ theme: theme, startOnLoad: false, logLevel: 'fatal' });
mermaid.init(undefined, document.querySelectorAll('.mermaid'));
}
let currentTheme = document.documentElement.classList.contains('dark') ? 'dark' : 'default';
initMermaidDiagrams(currentTheme);
const observer = new MutationObserver(function(mutationsList) {
for (let mutation of mutationsList) {
if (mutation.attributeName === 'class') {
let newTheme = document.documentElement.classList.contains('dark') ? 'dark' : 'default';
if (newTheme !== currentTheme) {
currentTheme = newTheme;
initMermaidDiagrams(newTheme);
}
}
}
});
observer.observe(document.documentElement, { attributes: true, attributeFilter: ['class'] });
});
</script>
<div class="mermaid">
flowchart BT
A[LED Power] --> B[LED Control]
B --> C[Timer Logic]
C --> D[Interactivity]
D --> E[Wired Control]
E --> F[Wireless Control]
</div>
<p>I knew I wanted to build a wireless controller with an array of buttons that would allow an end-user to set, start, stop, and reset the LED timer; but, before fussing over wireless capabilities, I needed to make sure I could build a wired controller, and write a simple countdown timer program; before I could do that, I needed figure out a way to illuminate each of the 30 LED channels independently; before that, I needed to figure out how the LEDs were being controlled; and before that, I needed to figure out how the LEDs were being powered.</p>
<p>Soon, a mental map of the problem began to take shape:</p>
<div class="markmap" style="height: 500px;">
<pre>- Fix Display
- Inspect Hardware
- Power Supply
- Shift Registers
- Microcontroller
- PCB Traces
- Modify Hardware
- ?
- Write Software
- Timer Logic
- Button Logic
- Sleep Mode
- Wireless
- Build Controller
- Enclosure
- Microcontroller
- Battery
- Buttons
- Charging Port</pre>
</div>
<h2 id="inspect-hardware">Inspect Hardware</h2>
<p>I knew needed to make sense of the electronic architecture of the display box &ndash; if I couldn&rsquo;t figure out how the display was <em>supposed</em> to work, well then I obviously wouldn&rsquo;t be able to modify it without wrecking it. So, I opened the four access panels on the box, and started poking around.</p>
<figure style="text-align: center; margin-top: 50px; margin-bottom: 50px;">
<a data-fancybox="" href="blank_timer.jpeg" style="display:inline-block; margin:0; padding:0; line-height:0;" data-caption="My first look inside the display box; after nearly going blind, I draped a towel over the front of the box to attenuate the LED intensity.">
<img src="blank_timer.jpeg" style="display:block; margin:0; padding:0;" alt="My first look inside the display box; after nearly going blind, I draped a towel over the front of the box to attenuate the LED intensity." >
</a>
<figcaption style="text-align: left;" data-pre="Figure " data-post=":" >
<p>
My first look inside the display box; after nearly going blind, I draped a towel over the front of the box to attenuate the LED intensity.
</p>
</figcaption>
</figure>
<p>My high-level goal at this point was to figure out where I might be able to break into the usual chain of command in a minimally-invasive manner. This was another one of my guiding principles:</p>
<div class="px-4 py-3 mb-6 rounded-md bg-primary-100 dark:bg-primary-900">
<div class="mb-2 font-bold text-primary-600 dark:text-primary-300">
Guiding Principle #2: Do No (Unnecessary) Harm
</div>
<div class="dark:text-neutral-300">If you can&rsquo;t avoid making hardware modifications, be judicious.</div>
</div>
<p>The manufacturer&rsquo;s PCB was gorgeous: they included capacitors and diodes in the power circuits to ensure clean, unidirectional current flow; all of the soldered joints were covered in conformal coating; and the wire harnessess were all professional-grade. Somebody clearly put a lot of thought into the layout, and I really didn&rsquo;t want to mess with it. Instead, I was hoping I might be able to overwrite the manufacturer&rsquo;s firmware, and leave the PCB completely intact. Turns out, that wasn&rsquo;t going to be possible, but more on that <a href="https://www.blakecole.com/post/3-led-timer/#modify-hardware">later</a>.</p>
<figure style="text-align: center; margin-top: 50px; margin-bottom: 50px;">
<a data-fancybox="" href="first_look_pcb.jpeg" style="display:inline-block; margin:0; padding:0; line-height:0;" data-caption="Initial inspection of the manufacturer&rsquo;s printed circuit board (PCB), with annonymous finger pointing at the microcontroller.">
<img src="first_look_pcb.jpeg" style="display:block; margin:0; padding:0;" alt="Initial inspection of the manufacturer&#39;s printed circuit board (PCB), with annonymous finger pointing at the microcontroller." width="500" >
</a>
<figcaption style="text-align: left;" data-pre="Figure " data-post=":" >
<p>
Initial inspection of the manufacturer&rsquo;s printed circuit board (PCB), with annonymous finger pointing at the microcontroller.
</p>
</figcaption>
</figure>
<p>Before anything else, I wanted to make sure that the display box was still capable of delivering power to the LEDs.</p>
<h3 id="power-supply">Power Supply</h3>
<p>When inspecting a new piece of hardware, I generally like to start with the power system &ndash; it is the least ambiguous and easiest to identify. The external power cable is typically connected to a <em>power supply</em>, which contains a <a href="https://en.wikipedia.org/wiki/Rectifier" target="_blank" rel="noopener">rectifier</a> that converts alternating current (AC) to direct current (DC). Once I found the power supply, I identified positive voltage and ground wires, and traced them to the PCB. The output voltage is almost always specified on the power supply, but it is nevertheless good practice to verify it with a multimeter.</p>
<figure style="text-align: center; margin-top: 50px; margin-bottom: 50px;">
<a data-fancybox="" href="power_supply.png" style="display:inline-block; margin:0; padding:0; line-height:0;" data-caption="A 27V power supply.">
<img src="power_supply.png" style="display:block; margin:0; padding:0;" alt="A 27V power supply." width="400" >
</a>
<figcaption style="text-align: left;" data-pre="Figure " data-post=":" >
<p>
A 27V power supply.
</p>
</figcaption>
</figure>
<p>I plugged in the display box, and immediately heard the power supply fan roar to life. A default pattern of LEDs illuminated. So far so good. The power supply was outputting 27 VDC, which I found somewhat curious &ndash; most devices run on 5V, 12V, or 24V. Turns out, there was a very good reason for this. I noticed that each LED channel was composed of 13 individual LEDs, connected in series. All LEDs are designed to operate within a nominal voltage range, and a quick search on <a href="https://www.digikey.com/en/products/detail/cree-led/C5SMF-RJF-CT0W0BB2/4794052" target="_blank" rel="noopener">DigiKey</a> indicated that these LEDs preferred to run at 2.1V.</p>
<p>What is 27 Volts, divided by 13 LEDs? <strong>2.08 Volts per LED</strong>. Not a coincidence.</p>
<p>I also noticed a secondary pair of power wires which were routed from the main power supply to the PCB via a 5V <a href="https://en.wikipedia.org/wiki/Battery_eliminator_circuit" target="_blank" rel="noopener">battery eliminator circuit</a>, or BEC. A BEC is a small onboard voltage regulator that draws power from a device’s main power source, and steps it down to a stable, lower voltage to safely run sensitive electronics like microcontrollers. I made a mental note of the 5V input on the PCB &ndash; eventually, I would probably need to find a place to tap into this voltage source to power my own microcontroller.</p>
<h3 id="microcontrollers">Microcontrollers</h3>
<p>Have you ever wondered how modern consumer applicances work?</p>
<p>What actually happens when you push the buttons on your microwave oven, or turn the dial on your washing machine? How do these actions actually control the device? If you aren&rsquo;t an electrical engineer, you might assume that whatever is going on behind the scenes is far beyond your understanding. Well, I can assure you, it isn&rsquo;t!</p>
<p>Hiding behind almost every electronic device is a single key component: a <em><strong>microcontroller</strong></em>. A microcontroller is essentially a simple computer that continuously measures electrical signals (inputs) to sense its environment, makes decisions based on those inputs, and generates electrical signals (outputs) to control actuators or communicate with other devices. If an electronic device is a football team, the microcontroller is the quarterback: it coordinates all the peripherals (i.e., sensors, actuators, displays) using predefined communication protocols.</p>
<figure style="text-align: center; margin-top: 50px; margin-bottom: 50px;">
<a data-fancybox="" href="arduino.png" style="display:inline-block; margin:0; padding:0; line-height:0;" data-caption="An ATmega328P microcontroller (outlined in red) on an Arduino UNO printed circuit board.">
<img src="arduino.png" style="display:block; margin:0; padding:0;" alt="An [ATmega328P](https://www.digikey.com.au/en/products/detail/microchip-technology/ATMEGA328P-PU/1914589) microcontroller (outlined in red) on an [Arduino UNO](https://store-usa.arduino.cc/products/arduino-uno-rev3?queryID=f87920403a6ad2e9dba7b0fe935fd940) printed circuit board." width="400" >
</a>
<figcaption style="text-align: left;" data-pre="Figure " data-post=":" >
<p>
An <a href="https://www.digikey.com.au/en/products/detail/microchip-technology/ATMEGA328P-PU/1914589" target="_blank" rel="noopener">ATmega328P</a> microcontroller (outlined in red) on an <a href="https://store-usa.arduino.cc/products/arduino-uno-rev3?queryID=f87920403a6ad2e9dba7b0fe935fd940" target="_blank" rel="noopener">Arduino UNO</a> printed circuit board.
</p>
</figcaption>
</figure>
<p>It really is as simple as that! Once you start looking, you&rsquo;ll see these everwhere &ndash; in your kitchen, in your car, and even on your body. Indeed, smartwatches contain <em>very</em> small microcontrollers that read data from embedded sensors and specify which pixels to illuminate on the watch face. Basically any time you see a device with buttons, switches, or LED indicators, you can be reasonably sure that there is a microcontroller nearby.</p>
<p>This fact has an exciting corollary: <em><strong>if you know how to program microcontrollers, you can control almost any electronic system</strong></em>. This is honestly insane. Microcontrollers allow you to build everything from home security networks to automated drip-irrigation systems for your houseplants. You can design party costumes with embedded LEDs that react to light, sound, and heat signatures. You can build custom weather stations. You could design a remote-controlled actuator to open and close a window, or deploy a retractable sun shade. The possibilities are functionally infinite.</p>
<p>If you want to learn how to do this, there is no shortage of tutorials online (check out <a href="https://www.youtube.com/@Dronebotworkshop" target="_blank" rel="noopener">DroneBot Workshop</a>). If you like working on little personal projects, I promise it is worth your time.</p>
<p>Without an understanding of microcontrollers, there is no chance I would have been able to build a controller for this LED display. Again, this would be like trying to run a play in football without a quarterback &ndash; the center would hike the ball into the clear blue sky, and the play would be over.</p>
<figure style="text-align: center; margin-top: 50px; margin-bottom: 50px;">
<a data-fancybox="" href="first_look_mcu.jpeg" style="display:inline-block; margin:0; padding:0; line-height:0;" data-caption="Computer, enhance! A close-up image of the OEM microcontroller. The alphanumeric code on this chip helped me identify the microcontroller, and find its corresponding data sheet. The chip pinout diagram allowed me to zero in on a subset of ports that could be used for LED control.">
<img src="first_look_mcu.jpeg" style="display:block; margin:0; padding:0;" alt="Computer, enhance! A close-up image of the OEM microcontroller. The alphanumeric code on this chip helped me identify the microcontroller, and find its corresponding [data sheet](https://www.digikey.ch/htmldatasheets/production/33029/0/0/1/mc68h-s-c705c8a.html). The chip pinout diagram allowed me to zero in on a subset of ports that could be used for LED control." width="500" >
</a>
<figcaption style="text-align: left;" data-pre="Figure " data-post=":" >
<p>
Computer, enhance! A close-up image of the OEM microcontroller. The alphanumeric code on this chip helped me identify the microcontroller, and find its corresponding <a href="https://www.digikey.ch/htmldatasheets/production/33029/0/0/1/mc68h-s-c705c8a.html" target="_blank" rel="noopener">data sheet</a>. The chip pinout diagram allowed me to zero in on a subset of ports that could be used for LED control.
</p>
</figcaption>
</figure>
<p>After I had located the microcontroller on the PCB, I knew I would need to identify the pins that were being used to control the LEDs. Once I figured this out, I could either:</p>
<ul>
<li>Leave the OEM microcontroller in place, overwrite the firmware, and send commands to these pins.</li>
<li>Yank out the OEM microcontroller, replace it with one of my own, and inject my own signals (the specific nature of these signals was still TBD).</li>
</ul>
<p>There were a few ways of going about this. The easiest was probably to connect each pin to an oscilloscope and look for a digital signal, but I didn&rsquo;t have an oscilloscope. Back to the drawing board. The alphanumeric code printed on the microcontroller indicated that it was a <a href="https://www.digikey.ch/htmldatasheets/production/33029/0/0/1/mc68h-s-c705c8a.html" target="_blank" rel="noopener">MC68HSC705C8A</a>, manufactured by NXP Semiconductors. I pulled up the pinout diagram, and began a process of elimination.</p>
<figure style="text-align: center; margin-top: 50px; margin-bottom: 50px;">
<a data-fancybox="" href="mcu_pinout.png" style="display:inline-block; margin:0; padding:0; line-height:0;" data-caption="Using this pinout diagram, I was able to eliminate a number of pins from consideration; moreover, it revealed the location of the microcontroller&rsquo;s designated serial output ports &ndash; these became my prime suspects.">
<img src="mcu_pinout.png" style="display:block; margin:0; padding:0;" alt="Using this pinout diagram, I was able to eliminate a number of pins from consideration; moreover, it revealed the location of the microcontroller&#39;s designated serial output ports -- these became my prime suspects." >
</a>
<figcaption style="text-align: left;" data-pre="Figure " data-post=":" >
<p>
Using this pinout diagram, I was able to eliminate a number of pins from consideration; moreover, it revealed the location of the microcontroller&rsquo;s designated serial output ports &ndash; these became my prime suspects.
</p>
</figcaption>
</figure>
<p>Once I had narrowed my search to five or six pins, I used my multimeter to check if any of them exhibited any electrical activity &ndash; I wouldn&rsquo;t be able to resolve the shape of digital signals, but I&rsquo;d at least be able to see the voltage jumping around. After a few rounds of this, I had a fairly good sense of which GPIO pins were being used for LED control.</p>
<p>The precise means by which the LEDs were being controlled was still somewhat unclear to me, but I was making progress. I knew the command signals originated from the microcontroller, and I knew which ports were being used to issue those commands. I knew that a high-power LED display like this one couldn&rsquo;t be powered directly by a microcontroller, so I expected to see an array of <a href="https://en.wikipedia.org/wiki/Relay" target="_blank" rel="noopener">relays</a> or other electronic switches further downstream. But, confusingly, I didn&rsquo;t see anything on the PCB that looked like a relay &ndash; much less 30 of them. Something else was going on.</p>
<h3 id="shift-registers">Shift Registers</h3>
<p>Microcontrollers have a limited number of so-called general purpose input-output (GPIO) pins. These GPIO pins are the physical ports where the microcontroller interfaces with sensors, actuators, and displays. For most simple projects, the handful of GPIO ports built into the microcontroller are more than sufficient; however, some electronic devices require hundreds (e.g., automotive control), thousands, or even millions (e.g., 4K LED display) of outputs.</p>
<p>When the number of required outputs exceeds the number of GPIO pins on the microcontroller, a device called a <a href="https://en.wikipedia.org/wiki/Shift_register" target="_blank" rel="noopener">shift register</a> can come in handy. From a functional perspective, shift registers are incredibly simple: they have a sequence of memory slots that can hold a single binary bit (i.e., either a <code>0</code> or a <code>1</code>); when a new bit is introduced at the &ldquo;front end&rdquo;, all the other values shift over by one slot, and the oldest value gets bumped out. Importantly, the state of each memory slot can be read simultaneously. You can think of a shift register as a marquee billboard, wherein new tiles can only be added from the lefthand side: each time a new tile is added, all the other tiles shift to the right, and the oldest tile is knocked off the end.</p>
<figure style="text-align: center; margin-top: 50px; margin-bottom: 50px;">
<a data-fancybox="" href="spyder_marquee.jpeg" style="display:inline-block; margin:0; padding:0; line-height:0;" data-caption="A shift register can be thought of as a simple marquee billboard, wherein new tiles can only be added from the left, and each tile can only display a 0 or 1 [photo: Spyder Surf].">
<img src="spyder_marquee.jpeg" style="display:block; margin:0; padding:0;" alt="A shift register can be thought of as a simple marquee billboard, wherein new tiles can only be added from the left, and each tile can only display a ```0``` or ```1``` [photo: [Spyder Surf](https://spydersurf.com/)]." width="500" >
</a>
<figcaption style="text-align: left;" data-pre="Figure " data-post=":" >
<p>
A shift register can be thought of as a simple marquee billboard, wherein new tiles can only be added from the left, and each tile can only display a <code>0</code> or <code>1</code> [photo: <a href="https://spydersurf.com/" target="_blank" rel="noopener">Spyder Surf</a>].
</p>
</figcaption>
</figure>
<p>So what&rsquo;s the big deal?</p>
<p>A Serial-In, Parallel-Out (SIPO) shift register effectively transforms a single GPIO pin into many. How is this possible? A microcontroller can transmit individual bits (i.e., <code>0</code>s and <code>1</code>s) to a shift register hundreds of thousands of times per second; the shift register then makes the data contained in those bits available in parallel, effectively instantaneously. A <code>clock</code> signal (generally a square wave) determines when the shift register reads a new value, and a <code>latch</code> signal determines when the data will be sent to the output channels. The following animation, created by <a href="https://lastminuteengineers.com/74hc595-shift-register-arduino-tutorial/" target="_blank" rel="noopener">Last Minute Engineers</a>, is an absolutely brilliant demonstration of how shift registers work.</p>
<figure style="text-align: center; margin-top: 50px; margin-bottom: 50px;">
<a data-fancybox="" href="shift_register.gif" style="display:inline-block; margin:0; padding:0; line-height:0;" data-caption="Animation showing the inner workings of a shift register. Every time the clock signal goes from high (1) to low (0), a new bit is read into the shift register; every time the latch signal goes from high to low, the full set of stored bits is transfered to the storage register, where they can be read simultaneously on each output channel [source: Last Minute Engineers].">
<img src="shift_register.gif" style="display:block; margin:0; padding:0;" alt="Animation showing the inner workings of a shift register. Every time the ```clock``` signal goes from high (```1```) to low (```0```), a new bit is read into the ```shift register```; every time the ```latch``` signal goes from high to low, the full set of stored bits is transfered to the ```storage register```, where they can be read simultaneously on each ```output``` channel [source: [Last Minute Engineers](https://lastminuteengineers.com/74hc595-shift-register-arduino-tutorial/)]." >
</a>
<figcaption style="text-align: left;" data-pre="Figure " data-post=":" >
<p>
Animation showing the inner workings of a shift register. Every time the <code>clock</code> signal goes from high (<code>1</code>) to low (<code>0</code>), a new bit is read into the <code>shift register</code>; every time the <code>latch</code> signal goes from high to low, the full set of stored bits is transfered to the <code>storage register</code>, where they can be read simultaneously on each <code>output</code> channel [source: <a href="https://lastminuteengineers.com/74hc595-shift-register-arduino-tutorial/" target="_blank" rel="noopener">Last Minute Engineers</a>].
</p>
</figcaption>
</figure>
<p>So, if you wanted to control the <code>ON/OFF</code> state of 8 LEDs, rather than wiring each LED to its own GPIO pin, you could connect the LEDs to an 8-bit shift register. If you wanted to turn on the first three LEDs, you would send the following serial signal to the shift register: <code>1 1 1 0 0 0 0 0</code>.</p>
<figure style="text-align: center; margin-top: 50px; margin-bottom: 50px;">
<a data-fancybox="" href="sipo_shift_register.png" style="display:inline-block; margin:0; padding:0; line-height:0;" data-caption="Diagram of a 4-bit shift register [source: Wikimedia Commons].">
<img src="sipo_shift_register.png" style="display:block; margin:0; padding:0;" alt="Diagram of a 4-bit shift register [source: [Wikimedia Commons](https://en.wikipedia.org/wiki/File:4-Bit_SIPO_Shift_Register.svg)]." >
</a>
<figcaption style="text-align: left;" data-pre="Figure " data-post=":" >
<p>
Diagram of a 4-bit shift register [source: <a href="https://en.wikipedia.org/wiki/File:4-Bit_SIPO_Shift_Register.svg" target="_blank" rel="noopener">Wikimedia Commons</a>].
</p>
</figcaption>
</figure>
<p>What if you wanted to control more than 8 LEDs? Perhaps many more? One of the awesome things about shift registers is that they can be connected in series; in this configuration, the oldest bit in the leading register spills over and becomes the newest bit in the following register.</p>
<p>I was starting to suspect that this was probably how the LEDs on my display were being controlled: the microcontroller sends 32 bits of serial data to the first of four 8-bit shift registers connected in series; once the first shift register is full, it begins passing bits to the second, and so on, like a beautiful cascading waterfall. This would explain the small number of active GPIO pins on the microcontroller.</p>
<figure style="text-align: center; margin-top: 50px; margin-bottom: 50px;">
<a data-fancybox="" href="waterfall.png" style="display:inline-block; margin:0; padding:0; line-height:0;" data-caption="Shift registers connected in series allow data to cascade from one to another [Source: DALL-E 3].">
<img src="waterfall.png" style="display:block; margin:0; padding:0;" alt="Shift registers connected in series allow data to cascade from one to another [Source: DALL-E 3]." width="400" >
</a>
<figcaption style="text-align: left;" data-pre="Figure " data-post=":" >
<p>
Shift registers connected in series allow data to cascade from one to another [Source: DALL-E 3].
</p>
</figcaption>
</figure>
<p>However, I still couldn&rsquo;t explain the lack of relays: you can&rsquo;t illuminate high-power LEDs using weak digital signals like these.</p>
<p>Well, it turns out the shift registers used in this particular application are somewhat special &ndash; they are so-called <em>sinking current</em> shift registers. According to the <a href="https://www.ti.com/lit/ds/symlink/tpic6a595.pdf" target="_blank" rel="noopener">TPIC6A595 data sheet</a>, &ldquo;Outputs are low-side, open-drain DMOS transistors with output ratings of 50V and a 350mA continuous sink-current capability. When data in the output buffers is low, the DMOS-transistor outputs are off. When data is high, the DMOS-transistor outputs have sink current capability.&rdquo;</p>
<p>This effectively means that the shift registers are able to act like relays, dictating the flow of high-voltage current to each LED channel. When the shift register output is <code>low</code>, the corresponding LED channel is allowed to float at 27 VDC (0 V differential voltage), and will not illuminate; when the shift register output goes <code>high</code>, the channel is grounded, and the LEDs illuminate.</p>
<figure style="text-align: center; margin-top: 50px; margin-bottom: 50px;">
<a data-fancybox="" href="shift_reg_pinout.png" style="display:inline-block; margin:0; padding:0; line-height:0;" data-caption="More handwritten notes. At this point, I had mostly figured out which GPIO pins the microcontroller was using to drive the shift registers; however, just to make sure, I used my backpacking headlamp to follow the copper traces from the first shift register&rsquo;s inputs (SER IN, RCK, and SRCK) back to their respective sources at the microcontroller. The digital output pins D0 - D7 are each connected to a single LED channel.">
<img src="shift_reg_pinout.png" style="display:block; margin:0; padding:0;" alt="More handwritten notes. At this point, I had mostly figured out which GPIO pins the microcontroller was using to drive the shift registers; however, just to make sure, I used my backpacking headlamp to follow the copper traces from the first shift register&#39;s inputs (```SER IN```, ```RCK```, and ```SRCK```) back to their respective sources at the microcontroller. The digital output pins ```D0``` - ```D7``` are each connected to a single LED channel." >
</a>
<figcaption style="text-align: left;" data-pre="Figure " data-post=":" >
<p>
More handwritten notes. At this point, I had mostly figured out which GPIO pins the microcontroller was using to drive the shift registers; however, just to make sure, I used my backpacking headlamp to follow the copper traces from the first shift register&rsquo;s inputs (<code>SER IN</code>, <code>RCK</code>, and <code>SRCK</code>) back to their respective sources at the microcontroller. The digital output pins <code>D0</code> - <code>D7</code> are each connected to a single LED channel.
</p>
</figcaption>
</figure>
<p>Gradually, a more complete picture was coming into focus: the main power supply provides 27 VDC to illuminate the LEDs, while the BEC provides 5 VDC to power the microcontroller and shift registers. Each of the four white connectors beneath the microcontroller are responsible for delivering power to a single LED panel; each panel has either 7 or 8 channels, and each channel has 13 discrete LEDs. There is a copper trace connecting each of the four shift registers in series, allowing bits to cascade from one to the next. The microcontroller sends serial commands to the shift registers, and the shift registers dictate which LED channels are illuminated.</p>
<p>Knowing this, I began to formulate a plan to control each LED channel independently.</p>
<h2 id="modify-hardware">Modify Hardware</h2>
<p>I soon realized that I wouldn&rsquo;t be able to repurpose the existing microcontroller. For one, there was no obvious means of communicating with it &ndash; no USB port, no exposed UART serial pins. Further, I didn&rsquo;t have any experience with this specific microcontroller, so I didn&rsquo;t know which application programming interfaces (APIs) I could utilize. So, I figured, if I was going to write my own firmware anyway, I might as well remove the existing microcontroller and use one I was already familiar with.</p>
<figure style="text-align: center; margin-top: 50px; margin-bottom: 50px;">
<a data-fancybox="" href="inject_serial.jpeg" style="display:inline-block; margin:0; padding:0; line-height:0;" data-caption="Having removed the OEM microcontroller, I was able to use jumper wires to inject serial signals from my microcontroller. Eventually, I would develop a more robust wiring scheme, but this approach worked for proof of concept purposes.">
<img src="inject_serial.jpeg" style="display:block; margin:0; padding:0;" alt="Having removed the OEM microcontroller, I was able to use jumper wires to inject serial signals from my microcontroller. Eventually, I would develop a more robust wiring scheme, but this approach worked for *proof of concept* purposes." >
</a>
<figcaption style="text-align: left;" data-pre="Figure " data-post=":" >
<p>
Having removed the OEM microcontroller, I was able to use jumper wires to inject serial signals from my microcontroller. Eventually, I would develop a more robust wiring scheme, but this approach worked for <em>proof of concept</em> purposes.
</p>
</figcaption>
</figure>
<h3 id="signal-injection">Signal Injection</h3>
<p>Using a small flathead screwdriver, I gently removed the microcontroller from its 40-pin DIP IC socket adaptor. Admittedly, this was a little freaky; once the microcontroller was removed, we had a football team with no quarterback. When I plugged in the display, a completely random sequence of LED channels was illuminated. This was disheartening, but not altogether surprising. I pressed on.</p>
<p>I took a quick break from hardware hacking to figure out how to control shift registers using a microcontroller; a few Google searches and tutorials later, I felt like I was ready to give it a crack. I used jumper wires to connect my microcontroller to the appropriate pins on the socket adaptor, and deployed my code.</p>
<p>Nothing happend. This made sense. The display box was unplugged. <em>Sigh</em>.</p>
<p>With my microcontroller dutifully sending serial signals to the shift registers (I hoped), I took a deep breath, grabbed the display box power cable, and plugged it in.</p>
<p>It worked.</p>
<p>It f**king worked!</p>
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
<iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/7D-akG85p3o?autoplay=0&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"></iframe>
</div>
<p>I can&rsquo;t really express the excitement I felt in this moment. When I saw intelligible numeric digits dancing across each panel, I knew I had basically won the game &ndash; I had independent control of the LED channels. I was going to beat this thing. Everything else &ndash; writing the timer logic, enabling wireless communication, adding external interrupts &ndash; all these tasks would take time and effort, but I knew I could do them.</p>
<p>As it turned out, I wasn&rsquo;t out of the woods, not even close &ndash; many painful setbacks were yet to come. But I got used to them. I&rsquo;d throw a temper tantrum (sorry Mom and Dad), settle down, and figure out what to do next.</p>
<h3 id="a-small-setback">A Small Setback</h3>
<p>At some point two of the LED channels on one of the panels inexplicably went dark, making the afflicted digit unreadable. This was infuriating &ndash; I had come so far! Nevertheless, there was no way I could deliver the display to the South Bay Boardriders Club in this condition. This was sort of an <em>all-or-nothing</em> game. So, I started tinkering.</p>
<p>First, I used a small flathead screwdriver to gently scrape the conformal coating off the LED solder joints on the broken panel. I then powered up the display, and used my microcontroller to send the digit <code>8</code> to this panel (or, in serial speak, <code>11111111</code>) to ensure that all 7 LED channels were receiving power. The two broken channels remained dark. Using a 3V power supply, I applied voltage across each LED in the broken channels to identify the duds; this was a little sketchy, as the max rated voltage for each LED was 2.6V. I was effectively risking destroying good LEDs in order to find the bad ones, but I figured they could probably handle a momentary hit of excess voltage. LEDs are pretty sturdy. Next, I used desoldering braid to remove the solder from the anode and cathode of each broken LED, and popped them out of the board. I ordered a few replacement LEDs from DigiKey, and waited a few days. Sometimes it is good to take a little break!</p>
<div class="px-4 py-3 mb-6 rounded-md bg-primary-100 dark:bg-primary-900">
<div class="mb-2 font-bold text-primary-600 dark:text-primary-300">
Guiding Principle #3: Prioritize Sanity
</div>
<div class="dark:text-neutral-300">Take a break, avoid a headache!</div>
</div>
<p>This principle was flagrantly violated many times over the course of this project, but so it goes.</p>
<p>Anyway, when my replacement LEDs arrived, I soldered them in place, applied conformal coating, and tested the panel &ndash; good as new. Another sigh of relief.</p>
<div class="gallery">
<a data-fancybox="gallery-led-panel" href="https://www.blakecole.com/post/3-led-timer/led-panel/IMG_5712.jpeg" >
<img src="https://www.blakecole.com/post/3-led-timer/led-panel/IMG_5712_hu17002878301467713175.jpeg" alt="">
</a>
<a data-fancybox="gallery-led-panel" href="https://www.blakecole.com/post/3-led-timer/led-panel/IMG_5807.jpeg" >
<img src="https://www.blakecole.com/post/3-led-timer/led-panel/IMG_5807_hu1487689399782247760.jpeg" alt="">
</a>
</div>
<h3 id="adding-garnishes">Adding Garnishes</h3>
<p>Having overcome the first hurdle &ndash; and the biggest hurdle &ndash; I went to work on all the little flourishes. I cleaned up the wiring and found better, more permanent places on the PCB to inject my signals; I wrote code to handle the <a href="https://www.blakecole.com/post/3-led-timer/#timer-logic">countdown timer logic</a>; I designed a state machine that could be controlled using toggle buttons, and wrote code that would recognize each button press as a non-blocking external interrupt; finally, I enabled wireless communication between the microcontroller I had installed inside the LED display box and a secondary external microcontroller outfitted with a series of toggle buttons.</p>
<p>The external microcontroller &ndash; which would be housed inside a waterproof enclosure and handle user commands &ndash; would be battery powered, so I enabled <a href="https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/system/sleep_modes.html" target="_blank" rel="noopener">deep-sleep mode</a> to conserve power when it was not in use. The end-user could wake the microcontroller by pressing a button.</p>
<figure style="text-align: center; margin-top: 50px; margin-bottom: 50px;">
<a data-fancybox="" href="esp32.jpeg" style="display:inline-block; margin:0; padding:0; line-height:0;" data-caption="Somewhat inconveniently, my favorite microcontroller uses 3.3V logic, but the TPIC6A595 shift register requires 5V logic. So, I soldered a logic-level converter onto a breadboard alongside my microcontroller to clean up the wiring a bit. The green and yellow wires carry the clock and latch signals, while the blue wire carries the serial input signal. The microcontroller is powered by a USB-C cable, which taps into the 5V source provided by the BEC.">
<img src="esp32.jpeg" style="display:block; margin:0; padding:0;" alt="Somewhat inconveniently, my favorite microcontroller uses 3.3V logic, but the TPIC6A595 shift register requires 5V logic. So, I soldered a logic-level converter onto a breadboard alongside my microcontroller to clean up the wiring a bit. The green and yellow wires carry the ```clock``` and ```latch``` signals, while the blue wire carries the ```serial input``` signal. The microcontroller is powered by a USB-C cable, which taps into the 5V source provided by the BEC." width="500" >
</a>
<figcaption style="text-align: left;" data-pre="Figure " data-post=":" >
<p>
Somewhat inconveniently, my favorite microcontroller uses 3.3V logic, but the TPIC6A595 shift register requires 5V logic. So, I soldered a logic-level converter onto a breadboard alongside my microcontroller to clean up the wiring a bit. The green and yellow wires carry the <code>clock</code> and <code>latch</code> signals, while the blue wire carries the <code>serial input</code> signal. The microcontroller is powered by a USB-C cable, which taps into the 5V source provided by the BEC.
</p>
</figcaption>
</figure>
<p>The frame of the LED display box is made entirely of sheet metal; very sturdy, but also completely unamenable to radio wave transmission. I needed a way to get my wireless commands into this stubborn <a href="https://en.wikipedia.org/wiki/Faraday_cage" target="_blank" rel="noopener">Faraday Cage</a>, so I added an external antenna.</p>
<figure style="text-align: center; margin-top: 50px; margin-bottom: 50px;">
<a data-fancybox="" href="ext_antenna.jpeg" style="display:inline-block; margin:0; padding:0; line-height:0;" data-caption="The display box itself is made of galvanized steel sheet metal, and thus constitutes a Faraday Cage; accordingly, I installed an external antenna to permit wireless communication.">
<img src="ext_antenna.jpeg" style="display:block; margin:0; padding:0;" alt="The display box itself is made of galvanized steel sheet metal, and thus constitutes a [Faraday Cage](https://en.wikipedia.org/wiki/Faraday_cage); accordingly, I installed an external antenna to permit wireless communication." width="500" >
</a>
<figcaption style="text-align: left;" data-pre="Figure " data-post=":" >
<p>
The display box itself is made of galvanized steel sheet metal, and thus constitutes a <a href="https://en.wikipedia.org/wiki/Faraday_cage" target="_blank" rel="noopener">Faraday Cage</a>; accordingly, I installed an external antenna to permit wireless communication.
</p>
</figcaption>
</figure>
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
<iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/DhhVJPRAjXo?autoplay=0&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"></iframe>
</div>
<h2 id="write-software">Write Software</h2>
<p>Most of the code I used for this project was adapted from similar stuff I wrote in grad school. This is one of the really cool things about software: you can gradually build up a quiver of increasingly advanced capabilities over time, and deploy them as needed in your future projects. Consequently, each successive project gets easier: the effort required to finish your $n^{th}$ project is approximately $\mathcal{O}(e^{-n})$.</p>
<h3 id="timer-logic">Timer Logic</h3>
<p>Have you ever wondered how a digital clock works?</p>
<p>If you answered <em>yes</em> to that, I am both impressed and a little concerned.</p>
<p>I certainly hadn&rsquo;t given it much thought before embarking on this project; but, once I started working out the algorithm, I became increasingly absorbed. It was really fun to break down the basic behavior of a timer &ndash; something we have all used a million times &ndash; and describe it programmatically: <em>Okay, so we decrement the one&rsquo;s place of the seconds digit until we get to zero, but then what happens? We add nine to it, and decrement the ten&rsquo;s place of the seconds digit&hellip;</em></p>
<p>And so on and so forth.</p>
<p>I&rsquo;m sure there&rsquo;s probably a fancier way to implement this logic, but there was no premium on computational efficiency for this particular task &ndash; it only runs once per second (which is like a million years in computer time), and involves only scalar arithmetic operations. Nested <em>if-statements</em> are interpretable and get the job done:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-matlab" data-lang="matlab"><span class="line"><span class="cl"><span class="c">%% Set initial timer digit values</span>
</span></span><span class="line"><span class="cl"><span class="n">m1</span> <span class="p">=</span> <span class="mi">0</span><span class="p">;</span> <span class="c">% digit 1: minute (10)</span>
</span></span><span class="line"><span class="cl"><span class="n">m2</span> <span class="p">=</span> <span class="mi">1</span><span class="p">;</span> <span class="c">% digit 2: minute (1)</span>
</span></span><span class="line"><span class="cl"><span class="n">s1</span> <span class="p">=</span> <span class="mi">1</span><span class="p">;</span> <span class="c">% digit 3: second (10)</span>
</span></span><span class="line"><span class="cl"><span class="n">s2</span> <span class="p">=</span> <span class="mi">1</span><span class="p">;</span> <span class="c">% digit 4: second (1)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c">%% Timer Active (decrement once per second)</span>
</span></span><span class="line"><span class="cl"><span class="k">while</span> <span class="o">~</span><span class="p">(</span><span class="n">m1</span> <span class="o">==</span> <span class="mi">0</span> <span class="o">&amp;&amp;</span> <span class="n">m2</span> <span class="o">==</span> <span class="mi">0</span> <span class="o">&amp;&amp;</span> <span class="n">s1</span> <span class="o">==</span> <span class="mi">0</span> <span class="o">&amp;&amp;</span> <span class="n">s2</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="n">s2</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="n">s2</span> <span class="p">=</span> <span class="n">s2</span><span class="o">-</span><span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="k">else</span>
</span></span><span class="line"><span class="cl"> <span class="n">s2</span> <span class="p">=</span> <span class="n">s2</span><span class="o">+</span><span class="mi">9</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="n">s1</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="n">s1</span> <span class="p">=</span> <span class="n">s1</span><span class="o">-</span><span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="k">else</span>
</span></span><span class="line"><span class="cl"> <span class="n">s1</span> <span class="p">=</span> <span class="n">s1</span><span class="o">+</span><span class="mi">5</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="n">m2</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="n">m2</span> <span class="p">=</span> <span class="n">m2</span><span class="o">-</span><span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="k">else</span>
</span></span><span class="line"><span class="cl"> <span class="n">m2</span> <span class="p">=</span> <span class="n">m2</span><span class="o">+</span><span class="mi">9</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="n">m1</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="n">m1</span> <span class="p">=</span> <span class="n">m1</span><span class="o">-</span><span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="k">else</span>
</span></span><span class="line"><span class="cl"> <span class="n">m1</span> <span class="p">=</span> <span class="n">m1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="k">end</span>
</span></span><span class="line"><span class="cl"> <span class="k">end</span>
</span></span><span class="line"><span class="cl"> <span class="k">end</span>
</span></span><span class="line"><span class="cl"> <span class="k">end</span>
</span></span><span class="line"><span class="cl"> <span class="n">fprintf</span><span class="p">(</span><span class="s">&#39;%i%i:%i%i\n&#39;</span><span class="p">,</span> <span class="n">m1</span><span class="p">,</span> <span class="n">m2</span><span class="p">,</span> <span class="n">s1</span><span class="p">,</span> <span class="n">s2</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="n">pause</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span></code></pre></div>
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
<iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/usOYB9nmdMs?autoplay=0&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"></iframe>
</div>
<p>The logic for <code>stopwatch mode</code> was mostly the same, with a few sign changes.</p>
<h3 id="numeric-digits">Numeric Digits</h3>
<p>In order to display digits on each of the four panels of the LED display, I needed to figure out what sequences of bits corresponded to the numbers <code>0</code> through <code>9</code>. This wasn&rsquo;t particularly challenging once I realized that the LED channels were arranged in a counter-clockwise fashion around each digit. The following table shows how to express each digit using either binary or hexidecimal serial commands.</p>
<figure style="text-align: center; margin-top: 50px; margin-bottom: 50px;">
<a data-fancybox="" href="digit_binary_hex.png" style="display:inline-block; margin:0; padding:0; line-height:0;" data-caption="A useful cheat sheet that illustrates how to create numeric digits on a 7-segment display, using either binary or hex serial commands.">
<img src="digit_binary_hex.png" style="display:block; margin:0; padding:0;" alt="A useful cheat sheet that illustrates how to create numeric digits on a 7-segment display, using either binary or hex serial commands." >
</a>
<figcaption style="text-align: left;" data-pre="Figure " data-post=":" >
<p>
A useful cheat sheet that illustrates how to create numeric digits on a 7-segment display, using either binary or hex serial commands.
</p>
</figcaption>
</figure>
<h2 id="build-controller">Build Controller</h2>
<p>This stage of the project was notably less exciting for me. I knew I had already overcome the biggest challenges, and the ones that remained were all fairly pedestrian: wire cutting, soldering, and repurposing code I had already written for other projects. At this point, I had been working on this project off-and-on for a few weeks, and I was pretty eager to wrap things up.</p>
<p>I think most engineering projects follow an arc like this. At first you&rsquo;re really excited, eager to tackle a new and interesting problem. Gradually, you realize how much work will be involved. You wonder whether you can even solve the problem. You experience a setback or two. You spiral into disillusionment. But, eventually, you experience a breakthrough! You become momentarily reinvigorated, only to realize that there is still an absurd amount of work left to do. You have already invested far too much time to quit now, so you grind out the remainder of the project.</p>
<p>One of my favorite engineering content creators, <a href="https://www.youtube.com/@rctestflight" target="_blank" rel="noopener">Daniel Riley (@rctestflight)</a>, illustrated a similar narrative arc which he called <em>The Project Curve.</em></p>
<figure style="text-align: center; margin-top: 50px; margin-bottom: 50px;">
<a data-fancybox="" href="project_curve.png" style="display:inline-block; margin:0; padding:0; line-height:0;" data-caption="The Project Curve, featuring the Trough of Despair [source: rctestflight].">
<img src="project_curve.png" style="display:block; margin:0; padding:0;" alt="The Project Curve, featuring the Trough of Despair [source: [rctestflight](https://www.youtube.com/@rctestflight)]." >
</a>
<figcaption style="text-align: left;" data-pre="Figure " data-post=":" >
<p>
The Project Curve, featuring the Trough of Despair [source: <a href="https://www.youtube.com/@rctestflight" target="_blank" rel="noopener">rctestflight</a>].
</p>
</figcaption>
</figure>
<h3 id="final-stretch">Final Stretch</h3>
<p>In any case, I still had a fair bit of work to do. This was my basic sequence of tasks:</p>
<ol>
<li>Permanently install internal microcontroller, solder connectors.</li>
<li>Apply conformal coating to any soldered joints.</li>
<li>Implement timer logic wirelessly on external microcontroller.</li>
<li>Handle button presses using external interrupts (non-blocking).</li>
<li>Design state machine (i.e. <em>if Button_A pressed, then&hellip;</em>).</li>
<li>Test all buttons.</li>
<li>Cut holes in enclosure, install buttons and pass-throughs.</li>
</ol>
<figure style="text-align: center; margin-top: 50px; margin-bottom: 50px;">
<a data-fancybox="" href="button_chaos.jpeg" style="display:inline-block; margin:0; padding:0; line-height:0;" data-caption="Before you solder your buttons into a fancy waterproof enclosure, it is good to make sure they work! This is what the chaotic prototyping stage looks like.">
<img src="button_chaos.jpeg" style="display:block; margin:0; padding:0;" alt="Before you solder your buttons into a fancy waterproof enclosure, it is good to make sure they work! This is what the chaotic prototyping stage looks like." >
</a>
<figcaption style="text-align: left;" data-pre="Figure " data-post=":" >
<p>
Before you solder your buttons into a fancy waterproof enclosure, it is good to make sure they work! This is what the chaotic prototyping stage looks like.
</p>
</figcaption>
</figure>
<p>The external controller itself was nothing fancy &ndash; I ordered a waterproof enclosure big enough to hold a microcontroller, a LiPo battery, and the inevitable tangled mess of wires connecting the buttons to the microcontroller. I drilled nine holes in the enclosure lid for the buttons, and one in the side wall of the base for a USB-C charging port. I soldered 3.5mm bullet connectors to the button wires to facilitate connections, wired everything up, slapped the lid on, and bada bing bada boom we had a controller.</p>
<h3 id="stress-testing">Stress Testing</h3>
<p>When you are building a device that will eventually be used by someone else, you have to think very carefully about all the ways in which things could go wrong. Or, you can simply go nuts and subject your device to unadulterated chaos. I took the latter approach.</p>
<p>I slammed my fingers on the controller buttons in the most absurd sequences; I pressed and held multiple buttons at the same time, while simultaneously clicking others &ndash; <em>what would happen if the user attempted to increment and decrement a digit simultaneously while holding down the start button?</em> I needed to make sure it wasn&rsquo;t possible to accidentally push the state machine into some unforseen and irrecoverable corner. Once I was satisfied that no combination of button presses would result in catastrophe, I gave the controller a gentle pat and a knowing nod of respect.</p>
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
<iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/_EQp3Tdwu8k?autoplay=0&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"></iframe>
</div>
<p>I subjected the display box to some abuse as well:</p>
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
<iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/QYc6ZZx7gSI?autoplay=0&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"></iframe>
</div>
<p>Sometimes, it is both useful and appropriate to be a bit of a menace!</p>
<h2 id="reflection">Reflection</h2>
<p>This project took me about 40 hours over four weeks to complete &ndash; not a strictly optimal use of my time, but illustrative of the commitment required to reverse engineer a modern device if you have a sufficiently sophisticated technical background.</p>
<p>Plus, it felt good to do something nice for my brother &ndash; he always looks out for me, so I was happy to be in a position to reciprocate.</p>
<p>Six months have passed since I delivered this LED display and wireless controller to the <a href="https://southbayboardriders.org/" target="_blank" rel="noopener">South Bay Boardriders Club</a>, and apparently it is still working. I am stoked to have had the opportunity to leverage my engineering skills to help out, and learn some new tricks in the process.</p>
<p>Thanks for reading.</p>
</description>
</item>
<item>
<title>Open Water Swim Route Optimization Using Reversible Jump Markov Chain Monte Carlo (RJ-MCMC)</title>
<link>https://www.blakecole.com/post/2-mcmc-mv-swim/</link>
<pubDate>Sun, 23 Mar 2025 00:00:00 +0000</pubDate>
<guid>https://www.blakecole.com/post/2-mcmc-mv-swim/</guid>
<description>
<details class="print:hidden xl:hidden" open>
<summary>Table of Contents</summary>
<div class="text-sm">
<nav id="TableOfContents">
<ul>
<li><a href="#should-you-read-this">Should You Read This?</a></li>
<li><a href="#recapitulation">Recapitulation</a></li>
<li><a href="#need-for-speed">Need for Speed</a></li>
<li><a href="#markov-chain-monte-carlo">Markov Chain Monte Carlo</a>
<ul>
<li><a href="#history">History</a></li>
<li><a href="#algorithm">Algorithm</a></li>
<li><a href="#python-demo">Python Demo</a></li>
</ul>
</li>
<li><a href="#problem-setup">Problem Setup</a>
<ul>
<li><a href="#goal">Goal</a></li>
<li><a href="#constraints">Constraints</a></li>
<li><a href="#assumptions">Assumptions</a></li>
<li><a href="#parameters">Parameters</a></li>
<li><a href="#methodology">Methodology</a></li>
<li><a href="#useful-functions">Useful Functions</a></li>
</ul>
</li>
<li><a href="#results">Results</a>
<ul>
<li><a href="#caseys-solution">Casey&rsquo;s Solution</a></li>
<li><a href="#my-solution">My Solution</a></li>
</ul>
</li>
<li><a href="#flourishes">Flourishes</a>
<ul>
<li><a href="#gaussian-bump">Gaussian Bump</a></li>
<li><a href="#simulated-annealing">Simulated Annealing</a></li>
</ul>
</li>
</ul>
</nav>
</div>
</details>