<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[codeof.me]]></title><description><![CDATA[The Digital Artisan: Tommi Laukkanen - Engineer, Developer, Maker, and Technology Enthusiast]]></description><link>https://www.codeof.me/</link><image><url>https://www.codeof.me/favicon.png</url><title>codeof.me</title><link>https://www.codeof.me/</link></image><generator>Ghost 5.84</generator><lastBuildDate>Wed, 06 May 2026 11:30:34 GMT</lastBuildDate><atom:link href="https://www.codeof.me/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[Cyberdore 2064 - RPi0 based cyberdeck to handle netrunning tasks on the couch]]></title><description><![CDATA[Retro futuristic cyberdeck with multifunctional rotary wheel.]]></description><link>https://www.codeof.me/cyberdore-2064-your-cyberdeck-companion-for/</link><guid isPermaLink="false">65854bd40b18b200018aa447</guid><category><![CDATA[Cyberdeck]]></category><category><![CDATA[RaspberryPi]]></category><category><![CDATA[3DPrinting]]></category><dc:creator><![CDATA[Tommi Laukkanen]]></dc:creator><pubDate>Thu, 18 Jul 2024 09:26:41 GMT</pubDate><media:content url="https://www.codeof.me/content/images/2024/07/Cyberdore-2064---Cyberdeck-Feature-Image.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://www.codeof.me/content/images/2024/07/Cyberdore-2064---Cyberdeck-Feature-Image.jpg" alt="Cyberdore 2064 - RPi0 based cyberdeck to handle netrunning tasks on the couch"><p>I&apos;m a fan of cyberpunk genre so one of the interests is to craft my own cyberdecks every now and then to help me jack into the net with a proper device. </p><p>You don&apos;t end up endless social media scrolling with these device - although it does have a handy scroll wheel on the side to navigate your Mastodon feed &#x1F913;</p><figure class="kg-card kg-gallery-card kg-width-wide kg-card-hascaption"><div class="kg-gallery-container"><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://www.codeof.me/content/images/2024/07/Cyberdore-2064---Cyberdeck-part-3.jpg" width="2000" height="2000" loading="lazy" alt="Cyberdore 2064 - RPi0 based cyberdeck to handle netrunning tasks on the couch" srcset="https://www.codeof.me/content/images/size/w600/2024/07/Cyberdore-2064---Cyberdeck-part-3.jpg 600w, https://www.codeof.me/content/images/size/w1000/2024/07/Cyberdore-2064---Cyberdeck-part-3.jpg 1000w, https://www.codeof.me/content/images/size/w1600/2024/07/Cyberdore-2064---Cyberdeck-part-3.jpg 1600w, https://www.codeof.me/content/images/size/w2400/2024/07/Cyberdore-2064---Cyberdeck-part-3.jpg 2400w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://www.codeof.me/content/images/2024/07/Cyberdore-2064---Cyberdeck-running-C64-emulator.jpg" width="2000" height="2000" loading="lazy" alt="Cyberdore 2064 - RPi0 based cyberdeck to handle netrunning tasks on the couch" srcset="https://www.codeof.me/content/images/size/w600/2024/07/Cyberdore-2064---Cyberdeck-running-C64-emulator.jpg 600w, https://www.codeof.me/content/images/size/w1000/2024/07/Cyberdore-2064---Cyberdeck-running-C64-emulator.jpg 1000w, https://www.codeof.me/content/images/size/w1600/2024/07/Cyberdore-2064---Cyberdeck-running-C64-emulator.jpg 1600w, https://www.codeof.me/content/images/size/w2400/2024/07/Cyberdore-2064---Cyberdeck-running-C64-emulator.jpg 2400w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://www.codeof.me/content/images/2024/07/Cyberdore-2064---Cyberdeck-running-C64-game-Neuromancer-2.jpg" width="2000" height="2000" loading="lazy" alt="Cyberdore 2064 - RPi0 based cyberdeck to handle netrunning tasks on the couch" srcset="https://www.codeof.me/content/images/size/w600/2024/07/Cyberdore-2064---Cyberdeck-running-C64-game-Neuromancer-2.jpg 600w, https://www.codeof.me/content/images/size/w1000/2024/07/Cyberdore-2064---Cyberdeck-running-C64-game-Neuromancer-2.jpg 1000w, https://www.codeof.me/content/images/size/w1600/2024/07/Cyberdore-2064---Cyberdeck-running-C64-game-Neuromancer-2.jpg 1600w, https://www.codeof.me/content/images/2024/07/Cyberdore-2064---Cyberdeck-running-C64-game-Neuromancer-2.jpg 2000w" sizes="(min-width: 720px) 720px"></div></div><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://www.codeof.me/content/images/2024/07/Cyberdore-2064---Cyberdeck-running-Neuromancer-game.jpg" width="2000" height="2000" loading="lazy" alt="Cyberdore 2064 - RPi0 based cyberdeck to handle netrunning tasks on the couch" srcset="https://www.codeof.me/content/images/size/w600/2024/07/Cyberdore-2064---Cyberdeck-running-Neuromancer-game.jpg 600w, https://www.codeof.me/content/images/size/w1000/2024/07/Cyberdore-2064---Cyberdeck-running-Neuromancer-game.jpg 1000w, https://www.codeof.me/content/images/size/w1600/2024/07/Cyberdore-2064---Cyberdeck-running-Neuromancer-game.jpg 1600w, https://www.codeof.me/content/images/size/w2400/2024/07/Cyberdore-2064---Cyberdeck-running-Neuromancer-game.jpg 2400w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://www.codeof.me/content/images/2024/07/Cyberdore-2064---Cyberdeck-with-C64-Vice-emulator.jpg" width="2000" height="2000" loading="lazy" alt="Cyberdore 2064 - RPi0 based cyberdeck to handle netrunning tasks on the couch" srcset="https://www.codeof.me/content/images/size/w600/2024/07/Cyberdore-2064---Cyberdeck-with-C64-Vice-emulator.jpg 600w, https://www.codeof.me/content/images/size/w1000/2024/07/Cyberdore-2064---Cyberdeck-with-C64-Vice-emulator.jpg 1000w, https://www.codeof.me/content/images/size/w1600/2024/07/Cyberdore-2064---Cyberdeck-with-C64-Vice-emulator.jpg 1600w, https://www.codeof.me/content/images/size/w2400/2024/07/Cyberdore-2064---Cyberdeck-with-C64-Vice-emulator.jpg 2400w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://www.codeof.me/content/images/2024/07/Cyberdore-2064---Cyberdeck-runnint-Mastodon-client.jpg" width="2000" height="2000" loading="lazy" alt="Cyberdore 2064 - RPi0 based cyberdeck to handle netrunning tasks on the couch" srcset="https://www.codeof.me/content/images/size/w600/2024/07/Cyberdore-2064---Cyberdeck-runnint-Mastodon-client.jpg 600w, https://www.codeof.me/content/images/size/w1000/2024/07/Cyberdore-2064---Cyberdeck-runnint-Mastodon-client.jpg 1000w, https://www.codeof.me/content/images/size/w1600/2024/07/Cyberdore-2064---Cyberdeck-runnint-Mastodon-client.jpg 1600w, https://www.codeof.me/content/images/size/w2400/2024/07/Cyberdore-2064---Cyberdeck-runnint-Mastodon-client.jpg 2400w" sizes="(min-width: 720px) 720px"></div></div></div><figcaption><p><span style="white-space: pre-wrap;">Cyberdeck photos</span></p></figcaption></figure><p>Here are some background and inspiration if you want to build your own.</p><div class="kg-card kg-callout-card kg-callout-card-blue"><div class="kg-callout-emoji">&#x1F4A1;</div><div class="kg-callout-text">Checkout my earlier cyberdeck too, the original Cyberdore:<br><a href="https://www.codeof.me/cyberdore-raspberry-pi-powered-cyberdeck/">https://www.codeof.me/cyberdore-raspberry-pi-powered-cyberdeck/</a></div></div><h3 id="ingredients">Ingredients</h3><p>The deck is constructed of the following parts:</p><ul><li>3D printed case (design by me &#x1F60A;) - check it from Printables: Cyberdore 2064</li><li>Small keyboard, Rii 518BT Bluetooth keyboard - <a href="https://www.aliexpress.com/item/1005004467913924.html?ref=codeof.me">Aliexpress</a>&#xA0;</li><li>Raspberry Pi Zero</li><li>Raspberry Pi Pico to handle both scroll wheel and cassette animation on small OLED display - connected to Zero with USB cable</li><li>3.5&quot; LCD Display - I used&#xA0;<a href="https://www.waveshare.com/wiki/3.5inch_RPi_LCD_(A)?ref=codeof.me">this Waveshare version</a></li><li>SSD1306 OLED I2C 128X64 as small display</li><li>KY-040 rotary encoder for the wheel</li><li>Battery - I used this battery shield that takes single 18650 battery -&#xA0;<a href="https://www.amazon.de/-/en/AZDelivery-Battery-Expansion-Shield-18650/dp/B086W7326Q/?ref=codeof.me">Amazon.de</a></li><li>A couple of small on/off switches and one 5 pin connector that now is only a prop, but the idea is to extend it to be a proper USB compatible port.</li><li>Some parts are glued like the &quot;Cyberdore 2064&quot; panel but mostly fitted together using M2 screws</li></ul><figure class="kg-card kg-gallery-card kg-width-wide kg-card-hascaption"><div class="kg-gallery-container"><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://www.codeof.me/content/images/2024/07/Cyberdore-2064---Cyberdeck-back-plate.jpg" width="2000" height="2000" loading="lazy" alt="Cyberdore 2064 - RPi0 based cyberdeck to handle netrunning tasks on the couch" srcset="https://www.codeof.me/content/images/size/w600/2024/07/Cyberdore-2064---Cyberdeck-back-plate.jpg 600w, https://www.codeof.me/content/images/size/w1000/2024/07/Cyberdore-2064---Cyberdeck-back-plate.jpg 1000w, https://www.codeof.me/content/images/size/w1600/2024/07/Cyberdore-2064---Cyberdeck-back-plate.jpg 1600w, https://www.codeof.me/content/images/size/w2400/2024/07/Cyberdore-2064---Cyberdeck-back-plate.jpg 2400w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://www.codeof.me/content/images/2024/07/Cyberdore-2064---Cyberdeck-from-back.jpg" width="2000" height="2000" loading="lazy" alt="Cyberdore 2064 - RPi0 based cyberdeck to handle netrunning tasks on the couch" srcset="https://www.codeof.me/content/images/size/w600/2024/07/Cyberdore-2064---Cyberdeck-from-back.jpg 600w, https://www.codeof.me/content/images/size/w1000/2024/07/Cyberdore-2064---Cyberdeck-from-back.jpg 1000w, https://www.codeof.me/content/images/size/w1600/2024/07/Cyberdore-2064---Cyberdeck-from-back.jpg 1600w, https://www.codeof.me/content/images/size/w2400/2024/07/Cyberdore-2064---Cyberdeck-from-back.jpg 2400w" sizes="(min-width: 720px) 720px"></div></div><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://www.codeof.me/content/images/2024/07/Cyberdore-2064---Cyberdeck-internals.jpg" width="2000" height="2000" loading="lazy" alt="Cyberdore 2064 - RPi0 based cyberdeck to handle netrunning tasks on the couch" srcset="https://www.codeof.me/content/images/size/w600/2024/07/Cyberdore-2064---Cyberdeck-internals.jpg 600w, https://www.codeof.me/content/images/size/w1000/2024/07/Cyberdore-2064---Cyberdeck-internals.jpg 1000w, https://www.codeof.me/content/images/size/w1600/2024/07/Cyberdore-2064---Cyberdeck-internals.jpg 1600w, https://www.codeof.me/content/images/size/w2400/2024/07/Cyberdore-2064---Cyberdeck-internals.jpg 2400w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://www.codeof.me/content/images/2024/07/Cyberdore-2064---Cyberdeck-multipurpose-wheel.jpg" width="2000" height="2000" loading="lazy" alt="Cyberdore 2064 - RPi0 based cyberdeck to handle netrunning tasks on the couch" srcset="https://www.codeof.me/content/images/size/w600/2024/07/Cyberdore-2064---Cyberdeck-multipurpose-wheel.jpg 600w, https://www.codeof.me/content/images/size/w1000/2024/07/Cyberdore-2064---Cyberdeck-multipurpose-wheel.jpg 1000w, https://www.codeof.me/content/images/size/w1600/2024/07/Cyberdore-2064---Cyberdeck-multipurpose-wheel.jpg 1600w, https://www.codeof.me/content/images/size/w2400/2024/07/Cyberdore-2064---Cyberdeck-multipurpose-wheel.jpg 2400w" sizes="(min-width: 720px) 720px"></div></div></div><figcaption><p><span style="white-space: pre-wrap;">Cyberdeck from the back. Casing shows the Raspberry Pi Zero and Pico. 18650 battery shield on top. Rotary encoder on the left.CyCe</span></p></figcaption></figure><h3 id="rotary-wheel">Rotary wheel</h3><p>I&apos;m using the same mechanism for the wheel as in my <a href="https://www.codeof.me/pikku-dial-multi-mode-dial-controller/" rel="noreferrer">Pikku Dial</a> USB wheel controller. I just created a custom micro-USB to micro-USB mini cable, so I was able to hook it up to Raspberry Pi Zero. Similar cable that I created for the <a href="https://www.codeof.me/handheld-from-80s-that-we-never-had-gamedore-2064/" rel="noreferrer">Gamedore 2064</a> handheld game console.</p><h3 id="3d-printed-case">3D Printed Case</h3><p>I designed the case with Autodesk Fusion. I wanted to have a deck with a handle so that you can carry it around easily. I also added a place for the small display where to display a cassette animation to give a proper retro futuristic feel to the device.</p><p>The keyboard is fitted tightly within the case.</p><p>You can download the .stl files from my Printables page: <a href="https://www.printables.com/model/945411-cyberdore-2064-cyberdeck?ref=codeof.me">https://www.printables.com/model/945411-cyberdore-2064-cyberdeck</a></p><figure class="kg-card kg-image-card kg-width-full kg-card-hascaption"><img src="https://www.codeof.me/content/images/2024/07/Cyberdore-2064---Cyberdeck---3d-printed-parts.jpg" class="kg-image" alt="Cyberdore 2064 - RPi0 based cyberdeck to handle netrunning tasks on the couch" loading="lazy" width="1286" height="1120" srcset="https://www.codeof.me/content/images/size/w600/2024/07/Cyberdore-2064---Cyberdeck---3d-printed-parts.jpg 600w, https://www.codeof.me/content/images/size/w1000/2024/07/Cyberdore-2064---Cyberdeck---3d-printed-parts.jpg 1000w, https://www.codeof.me/content/images/2024/07/Cyberdore-2064---Cyberdeck---3d-printed-parts.jpg 1286w"><figcaption><span style="white-space: pre-wrap;">3D printed case - back cover is not shown here</span></figcaption></figure><h3 id="send-feedback">Send Feedback</h3><p>I&apos;m happy to receive feedback about the device through Mastodon. Link to my account:</p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://fosstodon.org/@tlaukkanen/111110496450254240?ref=codeof.me"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Tommi Laukkanen (@tlaukkanen@fosstodon.org)</div><div class="kg-bookmark-description">Attached: 2 images New #cyberdeck build, Cyberdore 2064, is progressing nicely. Design and printed parts 95% ready. Waiting for couple of components from mail to fully hook up the #RaspberryPi Pico to Zero for extra cybernetic controls. :raspberrypi: :commodore:</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://fosstodon.org/packs/media/icons/apple-touch-icon-180x180-a75559a0af48064c1b7c71b81f3bf7c6.png" alt="Cyberdore 2064 - RPi0 based cyberdeck to handle netrunning tasks on the couch"><span class="kg-bookmark-author">Fosstodon</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://cdn.fosstodon.org/media_attachments/files/111/110/400/709/118/107/original/1533d08f3e481a91.jpg" alt="Cyberdore 2064 - RPi0 based cyberdeck to handle netrunning tasks on the couch"></div></a></figure>]]></content:encoded></item><item><title><![CDATA[Handheld from 80s that we never had - Gamedore 2064]]></title><description><![CDATA[Created 3D printable gaming retro handheld to play C64 games on the go. The game console that we never had.]]></description><link>https://www.codeof.me/handheld-from-80s-that-we-never-had-gamedore-2064/</link><guid isPermaLink="false">658537ea0b18b200018aa36c</guid><category><![CDATA[Gadgets]]></category><category><![CDATA[RaspberryPi]]></category><category><![CDATA[Python]]></category><category><![CDATA[3DPrinting]]></category><category><![CDATA[Gaming]]></category><dc:creator><![CDATA[Tommi Laukkanen]]></dc:creator><pubDate>Sat, 23 Dec 2023 21:42:02 GMT</pubDate><media:content url="https://www.codeof.me/content/images/2023/12/Gamedore_feature_image.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://www.codeof.me/content/images/2023/12/Gamedore_feature_image.jpg" alt="Handheld from 80s that we never had - Gamedore 2064"><p>I&apos;m a huge fan of Cyberpunk genre and love to design and build weird Cyberdecks. After building the latest deck, Cyberdore 2064 (posting more info on that later), I had a sudden interest in trying to make a gaming handheld to play Commodore 64 games. Main components I used were Raspberry Pi Zero W for running Vice emulator, Raspberry Pi Pico to handle the controls (D-Pad and buttons), 3.5&quot; RPi Display, 18650 battery shield. This is a short story on how to create the device by yourself.</p><figure class="kg-card kg-embed-card kg-card-hascaption"><iframe width="200" height="113" src="https://www.youtube.com/embed/Y3oFY9sf9Oo?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen title="Gamedore 2064 - Retro handheld game console from 80s"></iframe><figcaption><p><span style="white-space: pre-wrap;">Timelapse video of design and build of Gamedore 2064 handheld device</span></p></figcaption></figure><p>Even though I started my love of computers with Commodore VIC 20, the Commodore 64 was the one that really got me hooked on computers and the reason I ended up in the software development career.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.codeof.me/content/images/2023/12/Gamedore_Commodore_Dpad.jpg" class="kg-image" alt="Handheld from 80s that we never had - Gamedore 2064" loading="lazy" width="2000" height="1126" srcset="https://www.codeof.me/content/images/size/w600/2023/12/Gamedore_Commodore_Dpad.jpg 600w, https://www.codeof.me/content/images/size/w1000/2023/12/Gamedore_Commodore_Dpad.jpg 1000w, https://www.codeof.me/content/images/size/w1600/2023/12/Gamedore_Commodore_Dpad.jpg 1600w, https://www.codeof.me/content/images/2023/12/Gamedore_Commodore_Dpad.jpg 2000w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">Gamedore is a tribute to the Commodore 64 and the D-pad logo is one detail to support this.</span></figcaption></figure><h3 id="tldr">TL;DR</h3><ul><li>You can find the 3d printable STL files from Printables - please click the like button there so I receive Prucameter points :) <a href="https://www.printables.com/model/671075-gamedore-2064?ref=codeof.me">Gamedore 2064 by Tommi L. | Download free STL model | Printables.com</a></li><li>You can find the Circuitpython source code for controller from GitHub: <a href="https://github.com/tlaukkanen/rpi-pico-dpad?ref=codeof.me">tlaukkanen/rpi-pico-dpad: Simple game controller with Raspberry Pi Pico (github.com)</a></li></ul><h2 id="bill-of-material">Bill of Material</h2><figure class="kg-card kg-image-card kg-width-full kg-card-hascaption"><img src="https://www.codeof.me/content/images/2023/12/Gamedore_parts_2000px.jpg" class="kg-image" alt="Handheld from 80s that we never had - Gamedore 2064" loading="lazy" width="2000" height="1623" srcset="https://www.codeof.me/content/images/size/w600/2023/12/Gamedore_parts_2000px.jpg 600w, https://www.codeof.me/content/images/size/w1000/2023/12/Gamedore_parts_2000px.jpg 1000w, https://www.codeof.me/content/images/size/w1600/2023/12/Gamedore_parts_2000px.jpg 1600w, https://www.codeof.me/content/images/2023/12/Gamedore_parts_2000px.jpg 2000w"><figcaption><span style="white-space: pre-wrap;">Components needed to build Gamedore</span></figcaption></figure><p>Device is using the following components:</p><ul><li>Raspberry Pi Zero W (I would suggest on using Zero 2 for better performance)</li><li>Raspberry Pi Pico for handling buttons and d-pad</li><li>3.5&quot; LCD display - Like this from Aliexpress - Quite cheap and looks horrible - and learned later that this is the bottleneck for slow framerates</li><li>18650 battery shield - <a href="https://www.aliexpress.com/item/1005005993379022.html?ref=codeof.me">Like this from Aliexpress</a> - I unsoldered the USB port from the board so that it fits the case better</li><li>D-Pad component - <a href="https://www.aliexpress.com/item/4000681560472.html?ref=codeof.me">This one from Aliexpress</a></li><li>Quiet buttons - 6pce, <a href="https://www.aliexpress.com/item/32653516999.html?ref=codeof.me">these from Aliexpress</a></li><li>Really thin wire for buttons and d-pad so those fit in the case slots</li><li>Red 3mm LED to act as activity indicator in the front panel, you should use some resistor for the LED - I think I used 220 or 1k Ohm for my device.</li></ul><h2 id="3d-printable-handheld-shell">3D Printable Handheld Shell</h2><p>I designed the shell case with Fusion 360 and made the components to be printable without support. You should be able to print this with a printer that has a print area of minimum 15 x 10 cm. I used my Prusa MINI+ to print out the parts.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.codeof.me/content/images/2023/12/Gamedore_3dprintable_components.png" class="kg-image" alt="Handheld from 80s that we never had - Gamedore 2064" loading="lazy" width="958" height="1006" srcset="https://www.codeof.me/content/images/size/w600/2023/12/Gamedore_3dprintable_components.png 600w, https://www.codeof.me/content/images/2023/12/Gamedore_3dprintable_components.png 958w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">3D printed parts</span></figcaption></figure><h2 id="controlshardware">Controls - Hardware</h2><p>The device uses six buttons and one d-pad for direction controls. The controls are created with Pico which will act as USB HID device when it is connected with USB cable to RPi Zero.</p><p>Here are schematics on how the buttons and d-pad can be connected to Pico. I might have used some of the different pins in my setup, so photos don&apos;t necessarily match the diagram. You just need to note your pins and configure those accordingly in the Python code later.</p><figure class="kg-card kg-image-card"><img src="https://www.codeof.me/content/images/2023/12/Gamedore_schematics.png" class="kg-image" alt="Handheld from 80s that we never had - Gamedore 2064" loading="lazy" width="746" height="1100" srcset="https://www.codeof.me/content/images/size/w600/2023/12/Gamedore_schematics.png 600w, https://www.codeof.me/content/images/2023/12/Gamedore_schematics.png 746w" sizes="(min-width: 720px) 720px"></figure><h2 id="controlssoftware">Controls - Software</h2><p>I created the control software using <a href="https://circuitpython.org/board/raspberry_pi_pico/?ref=codeof.me">Circuitpython</a> using the <a href="https://learn.adafruit.com/debouncer-library-python-circuitpython-buttons-sensors?ref=codeof.me">adafruit_debouncer</a>, for identifying button presses and <a href="https://learn.adafruit.com/circuitpython-essentials/circuitpython-hid-keyboard-and-mouse?ref=codeof.me">adafruit_hid</a> for setting up Pico to act has USB keyboard. Code will interpret button or d-pad clicks to keycodes. You can easily customize those to fit the emulator that you want to use. I used the following as those work quite well with Commodore VICE emulator.</p><p>Check out the latest up-to-date code from GitHub:</p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://github.com/tlaukkanen/rpi-pico-dpad?ref=codeof.me"><div class="kg-bookmark-content"><div class="kg-bookmark-title">GitHub - tlaukkanen/rpi-pico-dpad: Simple game controller with Raspberry Pi Pico</div><div class="kg-bookmark-description">Simple game controller with Raspberry Pi Pico. Contribute to tlaukkanen/rpi-pico-dpad development by creating an account on GitHub.</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://github.com/fluidicon.png" alt="Handheld from 80s that we never had - Gamedore 2064"><span class="kg-bookmark-author">GitHub</span><span class="kg-bookmark-publisher">tlaukkanen</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://opengraph.githubassets.com/bd461ea22b1b5d2b09c8734d848327f1209a284671a66bed66bfcd04c2efbbf5/tlaukkanen/rpi-pico-dpad" alt="Handheld from 80s that we never had - Gamedore 2064"></div></a></figure><pre><code class="language-python">import board
import usb_hid
import digitalio
import adafruit_debouncer
from adafruit_hid.keycode import Keycode
from adafruit_hid.keyboard import Keyboard

# Button mapping
BTN_A = 3
BTN_B = 1
BTN_C = 0
BTN_D = 7
BTN_E = 5
BTN_F = 2
BTN_DOWN = 4
BTN_LEFT = 6
BTN_RIGHT = 8
BTN_UP = 9
BTN_PRESS = 10

button_pins = [
    digitalio.DigitalInOut(board.GP3),
    digitalio.DigitalInOut(board.GP4),
    digitalio.DigitalInOut(board.GP5),
    digitalio.DigitalInOut(board.GP6),
    digitalio.DigitalInOut(board.GP7),
    digitalio.DigitalInOut(board.GP8),
    digitalio.DigitalInOut(board.GP9),
    digitalio.DigitalInOut(board.GP10),
    digitalio.DigitalInOut(board.GP11),
    digitalio.DigitalInOut(board.GP12),
    digitalio.DigitalInOut(board.GP13)
]

buttons = [None] * 11
for i in range(0, 11):
    button_pins[i].direction = digitalio.Direction.INPUT
    button_pins[i].pull = digitalio.Pull.DOWN
    buttons[i] = adafruit_debouncer.Debouncer(button_pins[i])

keyboard = Keyboard(usb_hid.devices)

def get_key(button):
    if button == BTN_A:
        key = Keycode.F12
    elif button == BTN_B:
        key = Keycode.F1
    elif button == BTN_C:
        key = Keycode.ENTER
    elif button == BTN_D:
        key = Keycode.ONE
    elif button == BTN_E:
        key = Keycode.A
    elif button == BTN_F:
        key = Keycode.SPACEBAR
    elif button == BTN_DOWN:
        key = Keycode.DOWN_ARROW
    elif button == BTN_LEFT:
        key = Keycode.LEFT_ARROW
    elif button == BTN_RIGHT:
        key = Keycode.RIGHT_ARROW
    elif button == BTN_UP:
        key = Keycode.UP_ARROW
    elif button == BTN_PRESS:
        key = Keycode.X
    else:
        key = Keycode.ESCAPE
    return key

while True:
    for i in range(0, 11):
        buttons[i].update()
        if buttons[i].rose:
            key = get_key(i)
            keyboard.press(key)
        if buttons[i].fell:
            key = get_key(i)
            keyboard.release(key)
</code></pre>
<h2 id="emulators">Emulators</h2><p>There are multiple emulators to choose from for the RPi Zero. One of the easiest might be RetroPie as you can get full SD card image for that from the official Raspberry image generator. After that you just need to install the drivers for the 3.5&quot; display.</p><p>For better performance it&apos;s also good to check optimization tutorials on how to enable and use the FBCP-iLi9341 driver with the 3.5&quot; display as that will boost up the FPS significantly:</p><ul><li><a href="https://www.youtube.com/watch?v=xOPVlq4kM9c&amp;ref=codeof.me">RetroPie 3.5 inch SPI display HIGH FPS (FBCP - iLi9341) (youtube.com)</a></li><li><a href="https://github.com/juj/fbcp-ili9341?ref=codeof.me">juj/fbcp-ili9341: A blazing fast display driver for SPI-based LCD displays for Raspberry Pi A, B, 2, 3, 4 and Zero (github.com)</a></li></ul><p>After everything is setup, you are ready to go and enjoy those golden games from the 80s with the gaming device that we never had back in the day :&apos;-)</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.codeof.me/content/images/2023/12/Gamedore_boulder_dash.jpg" class="kg-image" alt="Handheld from 80s that we never had - Gamedore 2064" loading="lazy" width="2000" height="1126" srcset="https://www.codeof.me/content/images/size/w600/2023/12/Gamedore_boulder_dash.jpg 600w, https://www.codeof.me/content/images/size/w1000/2023/12/Gamedore_boulder_dash.jpg 1000w, https://www.codeof.me/content/images/size/w1600/2023/12/Gamedore_boulder_dash.jpg 1600w, https://www.codeof.me/content/images/2023/12/Gamedore_boulder_dash.jpg 2000w" sizes="(min-width: 1200px) 1200px"><figcaption><span style="white-space: pre-wrap;">Gamedore running Boulder Dash game</span></figcaption></figure><p>Add your comments on my Mastodon post:</p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://fosstodon.org/@tlaukkanen/111631962224513079?ref=codeof.me"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Tommi Laukkanen (@tlaukkanen@fosstodon.org)</div><div class="kg-bookmark-description">Attached: 1 image Released STL files and code for #RaspberryPi Pico for my Gamedore 2064 console as a Christmas gift to my #Maker friends &#x1F385; Merry Christmas to you all and I hope you get plenty of PLA in your Christmas socks to continue #3dPrinting in 2024! I also published a YouTube video of the&#x2026;</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://fosstodon.org/packs/media/icons/apple-touch-icon-1024x1024-db6849588b44f525363c37b65ef0ac66.png" alt="Handheld from 80s that we never had - Gamedore 2064"><span class="kg-bookmark-author">Fosstodon</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://cdn.fosstodon.org/media_attachments/files/111/631/941/143/656/707/original/7d117e86018fd4aa.jpg" alt="Handheld from 80s that we never had - Gamedore 2064"></div></a></figure>]]></content:encoded></item><item><title><![CDATA[Picoclock - Smart Desktop/Wall Clock Display]]></title><description><![CDATA[RPi Pico W powered customizable smart clock display with 3D printable case. Can be mounted on a wall. Desktop stand also available.]]></description><link>https://www.codeof.me/picoclock-smart-desktop-wall-clock-display/</link><guid isPermaLink="false">64f34a711a2608000156b667</guid><category><![CDATA[3DPrinting]]></category><category><![CDATA[RaspberryPi]]></category><category><![CDATA[Python]]></category><category><![CDATA[Gadgets]]></category><dc:creator><![CDATA[Tommi Laukkanen]]></dc:creator><pubDate>Sun, 03 Sep 2023 14:18:27 GMT</pubDate><media:content url="https://www.codeof.me/content/images/2023/09/Picoclock-on-desktop--Large-.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://www.codeof.me/content/images/2023/09/Picoclock-on-desktop--Large-.jpg" alt="Picoclock - Smart Desktop/Wall Clock Display"><p>As soon I installed solar panels on top of my house, I was interested in seeing more information about the panel power output as well as current electricity price (spot price) of the current hour. Even though I&apos;m not yet that keen to optimize my daily actions but still knowing these numbers may eventually guide me to use household devices when sun is shining, and electricity price is low. I also might be tempted to turn off devices when electricity price is high so that produced energy would be sold back to the grid for high price.</p><p>So, the problem was how to build a device that is always on and able to show real-time data from internet services and would look nice so it could be placed in a visible place in the house. </p><p>I chose Raspberry Pico W as the microcontroller as that has WLAN for internet connectivity, can be programmed easily with MicroPython and it&apos;s easy to hook it up with TM1637 driven 4-digit display. I chose the small display just because I happened to have few of those in my drawers - another reason is that it fits nicely in the same case that I have used for <a href="https://www.codeof.me/pikku-dial-multi-mode-dial-controller/">Pikku Dial</a> and <a href="https://www.codeof.me/pikku-raspberry-pi-pico-powered-macropad/">Pikku Macropad</a>.</p><p><strong>TL;DR:</strong> <a href="https://github.com/tlaukkanen/picoclock?ref=codeof.me">GitHub link for code</a> and <a href="https://www.printables.com/model/570828?ref=codeof.me">Printables.com link for STL files</a></p><figure class="kg-card kg-video-card kg-card-hascaption"><div class="kg-video-container"><video src="https://www.codeof.me/content/media/2023/09/PicoClock.mp4" poster="https://img.spacergif.org/v1/852x480/0a/spacer.png" width="852" height="480" loop autoplay muted playsinline preload="metadata" style="background: transparent url(&apos;https://www.codeof.me/content/images/2023/09/media-thumbnail-ember280.jpg&apos;) 50% 50% / cover no-repeat;"></video><div class="kg-video-overlay"><button class="kg-video-large-play-icon"><svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24"><path d="M23.14 10.608 2.253.164A1.559 1.559 0 0 0 0 1.557v20.887a1.558 1.558 0 0 0 2.253 1.392L23.14 13.393a1.557 1.557 0 0 0 0-2.785Z"/></svg></button></div><div class="kg-video-player-container kg-video-hide"><div class="kg-video-player"><button class="kg-video-play-icon"><svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24"><path d="M23.14 10.608 2.253.164A1.559 1.559 0 0 0 0 1.557v20.887a1.558 1.558 0 0 0 2.253 1.392L23.14 13.393a1.557 1.557 0 0 0 0-2.785Z"/></svg></button><button class="kg-video-pause-icon kg-video-hide"><svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24"><rect x="3" y="1" width="7" height="22" rx="1.5" ry="1.5"/><rect x="14" y="1" width="7" height="22" rx="1.5" ry="1.5"/></svg></button><span class="kg-video-current-time">0:00</span><div class="kg-video-time">/<span class="kg-video-duration"></span></div><input type="range" class="kg-video-seek-slider" max="100" value="0"><button class="kg-video-playback-rate">1&#xD7;</button><button class="kg-video-unmute-icon"><svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24"><path d="M15.189 2.021a9.728 9.728 0 0 0-7.924 4.85.249.249 0 0 1-.221.133H5.25a3 3 0 0 0-3 3v2a3 3 0 0 0 3 3h1.794a.249.249 0 0 1 .221.133 9.73 9.73 0 0 0 7.924 4.85h.06a1 1 0 0 0 1-1V3.02a1 1 0 0 0-1.06-.998Z"/></svg></button><button class="kg-video-mute-icon kg-video-hide"><svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24"><path d="M16.177 4.3a.248.248 0 0 0 .073-.176v-1.1a1 1 0 0 0-1.061-1 9.728 9.728 0 0 0-7.924 4.85.249.249 0 0 1-.221.133H5.25a3 3 0 0 0-3 3v2a3 3 0 0 0 3 3h.114a.251.251 0 0 0 .177-.073ZM23.707 1.706A1 1 0 0 0 22.293.292l-22 22a1 1 0 0 0 0 1.414l.009.009a1 1 0 0 0 1.405-.009l6.63-6.631A.251.251 0 0 1 8.515 17a.245.245 0 0 1 .177.075 10.081 10.081 0 0 0 6.5 2.92 1 1 0 0 0 1.061-1V9.266a.247.247 0 0 1 .073-.176Z"/></svg></button><input type="range" class="kg-video-volume-slider" max="100" value="100"></div></div></div><figcaption>Picoclock displaying time, weather, and current energy spot price. The lens is not as reflective in real life as in the video :)</figcaption></figure><h2 id="bill-of-material">Bill of Material</h2><p>Required components to build Picoclock:</p><ul><li>Raspberry Pico W</li><li>4-digit LED display with TM1637 driver (used <a href="https://www.amazon.de/dp/B07XQ2M188/?ref=codeof.me">these</a> from Amazon.de)</li><li>3D printed case</li><li>42mm lens (used something like <a href="https://www.aliexpress.com/item/1005001538796925.html?ref=codeof.me">this</a> from Aliexpress)</li></ul><h2 id="schematics">Schematics</h2><p>As Pico can be powered with USB cable then the only wires needed are for display. TM1637 only requires four wires: VCC, Ground, Data, Clock.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.codeof.me/content/images/2023/09/circuit.png" class="kg-image" alt="Picoclock - Smart Desktop/Wall Clock Display" loading="lazy" width="360" height="280"><figcaption>Picoclock circuit</figcaption></figure><h2 id="3d-printed-case">3D Printed Case</h2><p>The base layer of the case is the same as in my Pikku controllers, <a href="https://www.codeof.me/pikku-dial-multi-mode-dial-controller/">Pikku Dial</a> and <a href="https://www.codeof.me/pikku-raspberry-pi-pico-powered-macropad/">Pikku Macropad</a>. It fits the RPi Pico nicely and can different configurations on top of it. I designed a holder for 4-digit display and added a front panel with a slot for a glass lens to give it a retro futuristic look. I used <a href="https://www.aliexpress.com/item/1005001538796925.html?ref=codeof.me">42mm lens from this Aliexpress seller</a>. I used M2 screws to screw in the display component on the second layer. The display cover and top layer should snap fit to the case so glue is not necessary. I did glue up the gray U-shaped second layer component to the base for firm fit.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.codeof.me/content/images/2023/09/3d_parts_exploded_view.png" class="kg-image" alt="Picoclock - Smart Desktop/Wall Clock Display" loading="lazy" width="1459" height="1003" srcset="https://www.codeof.me/content/images/size/w600/2023/09/3d_parts_exploded_view.png 600w, https://www.codeof.me/content/images/size/w1000/2023/09/3d_parts_exploded_view.png 1000w, https://www.codeof.me/content/images/2023/09/3d_parts_exploded_view.png 1459w" sizes="(min-width: 720px) 720px"><figcaption>3D printable case components</figcaption></figure><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.codeof.me/content/images/2023/09/20230826_195316--Medium-.jpg" class="kg-image" alt="Picoclock - Smart Desktop/Wall Clock Display" loading="lazy" width="768" height="768" srcset="https://www.codeof.me/content/images/size/w600/2023/09/20230826_195316--Medium-.jpg 600w, https://www.codeof.me/content/images/2023/09/20230826_195316--Medium-.jpg 768w" sizes="(min-width: 720px) 720px"><figcaption>Pico and wiring</figcaption></figure><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.codeof.me/content/images/2023/09/20230826_200927--Medium-.jpg" class="kg-image" alt="Picoclock - Smart Desktop/Wall Clock Display" loading="lazy" width="768" height="768" srcset="https://www.codeof.me/content/images/size/w600/2023/09/20230826_200927--Medium-.jpg 600w, https://www.codeof.me/content/images/2023/09/20230826_200927--Medium-.jpg 768w" sizes="(min-width: 720px) 720px"><figcaption>Display added on top of Pico</figcaption></figure><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.codeof.me/content/images/2023/09/20230827_115945_edited.jpg" class="kg-image" alt="Picoclock - Smart Desktop/Wall Clock Display" loading="lazy" width="2000" height="2000" srcset="https://www.codeof.me/content/images/size/w600/2023/09/20230827_115945_edited.jpg 600w, https://www.codeof.me/content/images/size/w1000/2023/09/20230827_115945_edited.jpg 1000w, https://www.codeof.me/content/images/size/w1600/2023/09/20230827_115945_edited.jpg 1600w, https://www.codeof.me/content/images/size/w2400/2023/09/20230827_115945_edited.jpg 2400w" sizes="(min-width: 720px) 720px"><figcaption>Picoclock on the wall</figcaption></figure><h2 id="python-code">Python Code</h2><p>You can check the full source code from GitHub. Remember to rename <code>config_example.py</code> to <code>config.py</code> and replace the placeholder values with your wifi credentials. You can also get yourself a <a href="https://www.openweathermap.org/?ref=codeof.me">OpenWeatherMap.org</a> API key from that site.</p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://github.com/tlaukkanen/picoclock?ref=codeof.me"><div class="kg-bookmark-content"><div class="kg-bookmark-title">tlaukkanen/picoclock</div><div class="kg-bookmark-description">Small Raspberry Pico powered desktop or wall display - tlaukkanen/picoclock</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://github.com/fluidicon.png" alt="Picoclock - Smart Desktop/Wall Clock Display"><span class="kg-bookmark-author">GitHub</span><span class="kg-bookmark-publisher">tlaukkanen</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://opengraph.githubassets.com/f5ec4f7591f61437605e447182efe8d22ed42170f29f91bfdb465b3f92031d92/tlaukkanen/picoclock" alt="Picoclock - Smart Desktop/Wall Clock Display"></div></a></figure><p>HTTP client module (Micropython should have built-in client nowadays)</p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://github.com/balloob/micropython-http-client?ref=codeof.me"><div class="kg-bookmark-content"><div class="kg-bookmark-title">GitHub - balloob/micropython-http-client: MicroPython HTTP Client</div><div class="kg-bookmark-description">MicroPython HTTP Client. Contribute to balloob/micropython-http-client development by creating an account on GitHub.</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://github.com/fluidicon.png" alt="Picoclock - Smart Desktop/Wall Clock Display"><span class="kg-bookmark-author">GitHub</span><span class="kg-bookmark-publisher">balloob</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://opengraph.githubassets.com/d4b2ec4c3a39bc653bc8c9c5f188145cf3a615903991b4efad694200c1b8a1d2/balloob/micropython-http-client" alt="Picoclock - Smart Desktop/Wall Clock Display"></div></a></figure><p>For TM1637 display I used this module:</p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://github.com/mcauser/micropython-tm1637?ref=codeof.me"><div class="kg-bookmark-content"><div class="kg-bookmark-title">GitHub - mcauser/micropython-tm1637: MicroPython driver for TM1637 quad 7-segment LED modules</div><div class="kg-bookmark-description">MicroPython driver for TM1637 quad 7-segment LED modules - GitHub - mcauser/micropython-tm1637: MicroPython driver for TM1637 quad 7-segment LED modules</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://github.com/fluidicon.png" alt="Picoclock - Smart Desktop/Wall Clock Display"><span class="kg-bookmark-author">GitHub</span><span class="kg-bookmark-publisher">mcauser</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://opengraph.githubassets.com/fd64f976d191cc6bc5bcffb8254b605f3d4cde5751499e4ce6a8efae801d8f20/mcauser/micropython-tm1637" alt="Picoclock - Smart Desktop/Wall Clock Display"></div></a></figure><h2 id="make-it-your-own">Make it your own</h2><p>Customize the code as you please and share what you make it to display! Join the conversation at Mastodon:</p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://fosstodon.org/@tlaukkanen/111001674798451746?ref=codeof.me"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Tommi Laukkanen (@tlaukkanen@fosstodon.org)</div><div class="kg-bookmark-description">Attached: 1 image Released source code and STL files for my Picoclock smart clock display. Easily customizable (aka MicroPython &#x1F913;) to show relevant information to you. https://www.codeof.me/picoclock-smart-desktop-wall-clock-display/</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://fosstodon.org/packs/media/icons/apple-touch-icon-1024x1024-db6849588b44f525363c37b65ef0ac66.png" alt="Picoclock - Smart Desktop/Wall Clock Display"><span class="kg-bookmark-author">Fosstodon</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://cdn.fosstodon.org/media_attachments/files/111/001/667/654/875/847/original/0bca60fb477e3537.jpeg" alt="Picoclock - Smart Desktop/Wall Clock Display"></div></a></figure><p></p>]]></content:encoded></item><item><title><![CDATA[Eurorack Skull Attenuator for Darker Tunes]]></title><description><![CDATA[Designed and built a simple passive attenuator modular synth module for Eurorack. Uses 3D printed parts where skull is used as a slider knob.]]></description><link>https://www.codeof.me/modular-attenuator-for-darker-techno/</link><guid isPermaLink="false">6489d73917afc60001506fb7</guid><category><![CDATA[Eurorack]]></category><category><![CDATA[3DPrinting]]></category><category><![CDATA[Gadgets]]></category><dc:creator><![CDATA[Tommi Laukkanen]]></dc:creator><pubDate>Fri, 21 Jul 2023 09:07:59 GMT</pubDate><media:content url="https://www.codeof.me/content/images/2023/07/Header-image_edited.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://www.codeof.me/content/images/2023/07/Header-image_edited.jpg" alt="Eurorack Skull Attenuator for Darker Tunes"><p>Created another Eurorack modular synth module after the <a href="https://www.codeof.me/emergency-bass-drop-module/">Emergency Bass Drop Module</a>. This is a passive attenuator/fader that can attenuate the given signal. It&apos;s still easy to build as it doesn&apos;t require current from the Eurorack bus and only uses couple of components. Attenuator circuit is created using a 10k slider pot along with single 1k resistor.</p><div class="kg-card kg-callout-card kg-callout-card-grey"><div class="kg-callout-emoji">&#x1F39A;&#xFE0F;</div><div class="kg-callout-text"><em>&quot;Forgive me Father for I have Synth (again)&quot;!</em></div></div><h2 id="bill-of-material">Bill of material</h2><p>Only couple of components are needed:</p><ul><li>10k slider pot, similar as <a href="https://www.amazon.de/-/en/Logarithmic-Sliding-Potentiometer-Arduino-Electronic/dp/B07Z3DFLGD/?ref=codeof.me">these in Amazon.de</a></li><li>1k resistor</li><li>3d printed panels</li><li>2 x 3.5mm mono sockets, similar as <a href="https://www.aliexpress.com/item/4000713153284.html?ref=codeof.me">these in Aliexpress</a></li></ul><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.codeof.me/content/images/2023/07/Pieces_edited.jpg" class="kg-image" alt="Eurorack Skull Attenuator for Darker Tunes" loading="lazy" width="2000" height="1125" srcset="https://www.codeof.me/content/images/size/w600/2023/07/Pieces_edited.jpg 600w, https://www.codeof.me/content/images/size/w1000/2023/07/Pieces_edited.jpg 1000w, https://www.codeof.me/content/images/size/w1600/2023/07/Pieces_edited.jpg 1600w, https://www.codeof.me/content/images/2023/07/Pieces_edited.jpg 2000w" sizes="(min-width: 720px) 720px"><figcaption>3D printed and electronic components</figcaption></figure><h2 id="3d-printable-parts">3D Printable Parts</h2><p>Designed a simple panel and slider decoration with Fusion 360. I used the existing <a href="https://www.printables.com/model/300614-human-skull-cut?ref=codeof.me">Human Skull Cut model by TAB</a> and just resized it and added a hole for slider knob.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.codeof.me/content/images/2023/07/Autodesk-Fusion--Medium-.png" class="kg-image" alt="Eurorack Skull Attenuator for Darker Tunes" loading="lazy" width="1366" height="717" srcset="https://www.codeof.me/content/images/size/w600/2023/07/Autodesk-Fusion--Medium-.png 600w, https://www.codeof.me/content/images/size/w1000/2023/07/Autodesk-Fusion--Medium-.png 1000w, https://www.codeof.me/content/images/2023/07/Autodesk-Fusion--Medium-.png 1366w" sizes="(min-width: 720px) 720px"><figcaption>Eurorack panel and slider decorations</figcaption></figure><p>You can download the STL files from Printables:<br><a href="https://www.printables.com/model/525980-eurorack-skull-attenuatorfader-module?ref=codeof.me">Eurorack Skull Attenuator/Fader Module by Tommi L. | Download free STL model | Printables.com</a></p><h2 id="circuit">Circuit</h2><p>Circuit follows a simple passive mixer setup. In fact, it would be nice to create a skull themed passive mixer with the same design :)</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.codeof.me/content/images/2023/07/circuit.png" class="kg-image" alt="Eurorack Skull Attenuator for Darker Tunes" loading="lazy" width="440" height="170"><figcaption>Circuit</figcaption></figure><h2 id="final-module">Final Module</h2><p>Some images of the final module. Maybe some video of actual usage later :)</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.codeof.me/content/images/2023/07/20230609_205938--Medium-_edited.jpg" class="kg-image" alt="Eurorack Skull Attenuator for Darker Tunes" loading="lazy" width="768" height="768" srcset="https://www.codeof.me/content/images/size/w600/2023/07/20230609_205938--Medium-_edited.jpg 600w, https://www.codeof.me/content/images/2023/07/20230609_205938--Medium-_edited.jpg 768w" sizes="(min-width: 720px) 720px"><figcaption>Front view of the module</figcaption></figure><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.codeof.me/content/images/2023/07/20230609_205954-1---Medium-_edited.jpg" class="kg-image" alt="Eurorack Skull Attenuator for Darker Tunes" loading="lazy" width="768" height="768" srcset="https://www.codeof.me/content/images/size/w600/2023/07/20230609_205954-1---Medium-_edited.jpg 600w, https://www.codeof.me/content/images/2023/07/20230609_205954-1---Medium-_edited.jpg 768w" sizes="(min-width: 720px) 720px"><figcaption>Back view of the module, highlighting the wiring</figcaption></figure><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.codeof.me/content/images/2023/07/Skull-Attenuator-Module-in-Eurorack-case.jpg" class="kg-image" alt="Eurorack Skull Attenuator for Darker Tunes" loading="lazy" width="768" height="768" srcset="https://www.codeof.me/content/images/size/w600/2023/07/Skull-Attenuator-Module-in-Eurorack-case.jpg 600w, https://www.codeof.me/content/images/2023/07/Skull-Attenuator-Module-in-Eurorack-case.jpg 768w" sizes="(min-width: 720px) 720px"><figcaption>Module in Eurorack case along with other modules</figcaption></figure><p>Join the discussion in Mastodon. Any ideas what to build next?</p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://fosstodon.org/@tlaukkanen/110751314642173659?ref=codeof.me"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Tommi Laukkanen (@tlaukkanen@fosstodon.org)</div><div class="kg-bookmark-description">Attached: 3 images Forgive me father for I have synth again! This time designed and built a skull attenuator #ModularSynth module for #Eurorack - Great for attenuating those darker voltages &#x1F608; &#x1F3B6; https://www.codeof.me/modular-attenuator-for-darker-techno/</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://fosstodon.org/packs/media/icons/apple-touch-icon-1024x1024-db6849588b44f525363c37b65ef0ac66.png" alt="Eurorack Skull Attenuator for Darker Tunes"><span class="kg-bookmark-author">Fosstodon</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://cdn.fosstodon.org/media_attachments/files/110/751/294/772/469/205/original/9c835c08bc221374.jpg" alt="Eurorack Skull Attenuator for Darker Tunes"></div></a></figure>]]></content:encoded></item><item><title><![CDATA[Emergency Bass Drop Module]]></title><description><![CDATA[Passive switch module for Eurorack created from emergency push button. Includes links to .STL files for 3D printing the module parts.]]></description><link>https://www.codeof.me/emergency-bass-drop-module/</link><guid isPermaLink="false">6466327d17afc60001506dd3</guid><category><![CDATA[3DPrinting]]></category><category><![CDATA[Eurorack]]></category><category><![CDATA[Gadgets]]></category><dc:creator><![CDATA[Tommi Laukkanen]]></dc:creator><pubDate>Sun, 02 Jul 2023 14:15:00 GMT</pubDate><media:content url="https://www.codeof.me/content/images/2023/05/EBD_Feature_Image_edited.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://www.codeof.me/content/images/2023/05/EBD_Feature_Image_edited.jpg" alt="Emergency Bass Drop Module"><p>This is a simple passive switch modular synth module for Eurorack. Emergency push button is used as a switch component. Can be used for both ways by routing trigger signal either to A or B output or other way around by switching from trigger signal A to trigger signal B.</p><div class="kg-card kg-callout-card kg-callout-card-grey"><div class="kg-callout-emoji">&#x1F39B;&#xFE0F;</div><div class="kg-callout-text"><b><strong style="white-space: pre-wrap;">What is Eurorack?</strong></b><a href="https://en.wikipedia.org/wiki/Eurorack?ref=codeof.me">Wikipedia answer</a>: Eurorack is a modular synthesizer format originally specified in 1995 by Doepfer Musikelektronik. It has since grown in popularity, and as of 2022 has become a dominant hardware modular synthesizer format, with over 15,000 modules available from more than 1000 different manufacturers ranging from DIY kits and boutique, cottage-industry designers to well-known, established synth mass-manufacturers like Moog and Roland.</div></div><h2 id="bill-of-material">Bill of Material</h2><p>Requires only few parts:</p><ul><li>Emergency stop switch, e.g. <a href="https://www.amazon.de/dstfuy-Emergency-Pressure-Waterproof-Mushroom/dp/B0BF9P235T/?ref=codeof.me">this from Amazon.de</a> ~13&#x20AC;</li><li>3.5mm jack socket (3 pce), e.g. <a href="https://www.amazon.de/-/en/3-5mm-Jack-Socket-Pin-5pcs-Stereo/dp/B0BGQ5QRMP/?ref=codeof.me">this from Amazon.de</a> ~9&#x20AC;/5 pce. There are cheaper sets in Aliexpress etc. ...and also mono socket would be better for Eurorack but I had these spare stereo ones available so used them.</li><li>Wires</li><li>3D printed panel and button</li></ul><h2 id="circuit">Circuit</h2><p>It&apos;s just a switch &#x1F643;</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.codeof.me/content/images/2023/05/circuit.png" class="kg-image" alt="Emergency Bass Drop Module" loading="lazy" width="380" height="200"><figcaption><span style="white-space: pre-wrap;">Switch</span></figcaption></figure><h2 id="panels">Panels</h2><p>Had couple of designs for the front panel. Settled on this one that tries to visualize the audio waves rushing from the button to the audio jacks.</p><p>You can download the 3D printable STL files from Printables.com:<br><a href="https://www.printables.com/model/531630-emergency-bass-drop-module-for-eurorack?ref=codeof.me">Emergency Bass Drop Module for Eurorack by Tommi L. | Download free STL model | Printables.com</a></p><p></p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.codeof.me/content/images/2023/05/3d_printed_parts.png" class="kg-image" alt="Emergency Bass Drop Module" loading="lazy" width="912" height="912" srcset="https://www.codeof.me/content/images/size/w600/2023/05/3d_printed_parts.png 600w, https://www.codeof.me/content/images/2023/05/3d_printed_parts.png 912w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">3D printable parts. Front and back panels, button, and button collar.</span></figcaption></figure><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.codeof.me/content/images/2023/05/Front.jpg" class="kg-image" alt="Emergency Bass Drop Module" loading="lazy" width="768" height="768" srcset="https://www.codeof.me/content/images/size/w600/2023/05/Front.jpg 600w, https://www.codeof.me/content/images/2023/05/Front.jpg 768w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">Sanded and spray painted the front panel after the print.</span></figcaption></figure><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.codeof.me/content/images/2023/05/Back.jpg" class="kg-image" alt="Emergency Bass Drop Module" loading="lazy" width="768" height="768" srcset="https://www.codeof.me/content/images/size/w600/2023/05/Back.jpg 600w, https://www.codeof.me/content/images/2023/05/Back.jpg 768w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">Added a small slot for wires so that it keeps the wiring clean and tidy. Small NASA text as additional detail :)</span></figcaption></figure><p>Join the conversation at Mastodon. Any simple ideas to build next?</p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://fosstodon.org/@tlaukkanen/110389609205672770?ref=codeof.me"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Tommi Laukkanen (@tlaukkanen@fosstodon.org)</div><div class="kg-bookmark-description">Attached: 2 images Made Emergency Bass Drop #Eurorack #ModularSynth module. Panel and button designed in #Fusion360. Front panel &amp; button spray painted after #3dPrinting. Used passive emergency push button as switch. This will drop the bass in style :mastodance: &#x1F63A; &#x1F3B6;</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://fosstodon.org/packs/media/icons/apple-touch-icon-1024x1024-db6849588b44f525363c37b65ef0ac66.png" alt="Emergency Bass Drop Module"><span class="kg-bookmark-author">Fosstodon</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://cdn.fosstodon.org/media_attachments/files/110/389/570/265/865/958/original/f43af0dd144d8aeb.jpeg" alt="Emergency Bass Drop Module"></div></a></figure>]]></content:encoded></item><item><title><![CDATA[Pikku Dial - Multimode Dial Controller Powered by RPi Pico]]></title><description><![CDATA[Designed a small open-source multimode USB dial controller powered by Raspberry Pi Pico. Code is highly customizable. STL files available.]]></description><link>https://www.codeof.me/pikku-dial-multi-mode-dial-controller/</link><guid isPermaLink="false">644e1d9417afc60001506c2c</guid><category><![CDATA[3DPrinting]]></category><category><![CDATA[Gadgets]]></category><category><![CDATA[Python]]></category><category><![CDATA[RaspberryPi]]></category><dc:creator><![CDATA[Tommi Laukkanen]]></dc:creator><pubDate>Sun, 30 Apr 2023 16:24:36 GMT</pubDate><media:content url="https://www.codeof.me/content/images/2023/04/Pikku-Dial-feature-image.jpeg" medium="image"/><content:encoded><![CDATA[<img src="https://www.codeof.me/content/images/2023/04/Pikku-Dial-feature-image.jpeg" alt="Pikku Dial - Multimode Dial Controller Powered by RPi Pico"><p>Designed a small USB dial controller to fit together with its predecessor, the <a href="https://www.codeof.me/pikku-raspberry-pi-pico-powered-macropad/">Pikku Macropad</a>. The Dial also uses Raspberry Pi Pico and is even simpler under the hood as the only component in addition to Pico is KY-040 rotary encoder. The rotary encoder includes a switch action in addition to rotary function so pushing it will switch between different modes that you can easily customize in Python code.</p><p>The modes I have there currently are:</p><ol><li>Volume</li><li>Scroll (mouse-wheel up/down)</li><li>Arrow keys up/down (like moving a cursor up/down or scroll in smaller steps)</li><li>Fidget mode (does nothing, a stress toy &#x1F600;)</li></ol><p><strong>TL;DR</strong>: Check the GitHub repository for code: <a href="https://github.com/tlaukkanen/pikku-macropad?ref=codeof.me">github.com/tlaukkanen/pikku-macropad</a>. 3D printable .stl models are also available via <a href="https://www.printables.com/model/466564-pikku-dial-controller?ref=codeof.me">Printables.com</a></p><h2 id="bill-of-material">Bill of Material</h2><p>Components needed to build your own dial controller:</p><ul><li>Raspberry Pi Pico</li><li>KY-040 rotary encoder (<a href="https://www.amazon.de/Encoder-Rotation-Automotive-Electronics-Multimedia/dp/B09726Y8RB/?ref=codeof.me">I used these from Amazon.de</a>)</li><li>3D printed case</li></ul><h2 id="hardware3d-printed-case">Hardware - 3D Printed Case</h2><p>I designed the case to be simple and easy to print. It consists of four layers where the base houses the Raspberry Pico (same as in macropad), the second layer holds the rotary encoder in place on top of Pico. The third layer is the top panel enclosure and the last one is the actual dial knob that is connected to rotary encoder.</p><figure class="kg-card kg-image-card"><img src="https://www.codeof.me/content/images/2023/04/All-dial-components.png" class="kg-image" alt="Pikku Dial - Multimode Dial Controller Powered by RPi Pico" loading="lazy" width="1169" height="1050" srcset="https://www.codeof.me/content/images/size/w600/2023/04/All-dial-components.png 600w, https://www.codeof.me/content/images/size/w1000/2023/04/All-dial-components.png 1000w, https://www.codeof.me/content/images/2023/04/All-dial-components.png 1169w" sizes="(min-width: 720px) 720px"></figure><p>You can print parts with assorted colors to have different looks depending how you want it to look and to differentiate or make it use similar colors as your macropad.</p><p>I used some super glue to put the second layer in place with the rotary encoder. It shouldn&apos;t need it necessarily as there are only applied forces downwards when rotary is pressed.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.codeof.me/content/images/2023/04/20230425_201855--Medium-.jpg" class="kg-image" alt="Pikku Dial - Multimode Dial Controller Powered by RPi Pico" loading="lazy" width="768" height="768" srcset="https://www.codeof.me/content/images/size/w600/2023/04/20230425_201855--Medium-.jpg 600w, https://www.codeof.me/content/images/2023/04/20230425_201855--Medium-.jpg 768w" sizes="(min-width: 720px) 720px"><figcaption>All components printed and assembled. I did some sand &amp; spray rounds after this to achieve the look that you can see in the first picture on this page.</figcaption></figure><h3 id="have-fun-with-dial-knobs">Have Fun with Dial Knobs</h3><p>You can quite easily design your own rotary knobs and try to find the one that suits you the best. I found the dial knobs with small &quot;slot&quot; for a finger to be nice.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.codeof.me/content/images/2023/04/Dial-knobs.png" class="kg-image" alt="Pikku Dial - Multimode Dial Controller Powered by RPi Pico" loading="lazy" width="1199" height="739" srcset="https://www.codeof.me/content/images/size/w600/2023/04/Dial-knobs.png 600w, https://www.codeof.me/content/images/size/w1000/2023/04/Dial-knobs.png 1000w, https://www.codeof.me/content/images/2023/04/Dial-knobs.png 1199w" sizes="(min-width: 720px) 720px"><figcaption>Models of some of the dial knob designs that I tested</figcaption></figure><h3 id="improvement-ideas-for-the-case">Improvement Ideas for the Case</h3><p>There are couple of ideas that could improve the usability a bit: weight and smaller rotary ratio, meaning that it could have gearing so that rotary encoder would rotate faster with smaller movement. Now it&apos;s over 10&#xB0; angle for each step. Something like 2:1 - 4:1 gear ration in between the dial and encoder could feel better.</p><p>The case is quite light so rotating the dial can easily rotate the case too. Some weight could be added by adding some metal pieces to the empty slots in the case.</p><h2 id="schematics-and-soldering">Schematics and Soldering</h2><p>The wiring is simple as you only have the Pico and rotary encoder. Wire the power from Pico&apos;s VSYS to + in KY-040. GND ground to GND ground of KY-040.</p><p>Depends on your code wire the GPIO pins in the following way (or choose your own):</p><ul><li>GPIO PIN 20 to SW (Switch)</li><li>GPIO PIN 19 to DT</li><li>GPIO PIN 18 to CLK</li></ul><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.codeof.me/content/images/2023/04/circuit-1.png" class="kg-image" alt="Pikku Dial - Multimode Dial Controller Powered by RPi Pico" loading="lazy" width="380" height="280"><figcaption>Circuit diagram on how to connect KY-040 rotary encoder to Pico</figcaption></figure><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.codeof.me/content/images/2023/04/Pikku-dial-wiring.jpg" class="kg-image" alt="Pikku Dial - Multimode Dial Controller Powered by RPi Pico" loading="lazy" width="768" height="768" srcset="https://www.codeof.me/content/images/size/w600/2023/04/Pikku-dial-wiring.jpg 600w, https://www.codeof.me/content/images/2023/04/Pikku-dial-wiring.jpg 768w" sizes="(min-width: 720px) 720px"><figcaption>Soldered wires from Pico to KY-040</figcaption></figure><h2 id="code">Code</h2><p>I flashed the Raspberry Pi Pico with the latest CircuitPython firmware by following <a href="https://learn.adafruit.com/getting-started-with-raspberry-pi-pico-circuitpython/circuitpython?ref=codeof.me">this &quot;Getting started with CircuitPython&quot; tutorial from Adafruit</a>. For code I used CircuitPython libraries:</p><ul><li><a href="https://github.com/adafruit/Adafruit_CircuitPython_HID?ref=codeof.me">adafruit-circuitpython-hid</a> for keyboard and mouse communication</li></ul><p>Here&apos;s the full code for the macropad. Code can also be found from GitHub, <a href="https://github.com/tlaukkanen/pikku-macropad/?ref=codeof.me">here</a>.</p><pre><code class="language-python">import board
import digitalio
import rotaryio
import time
import usb_hid
import adafruit_hid.keyboard as keyboard
import adafruit_hid.mouse as mouse
from adafruit_hid.keyboard_layout_us import KeyboardLayoutUS
from adafruit_hid.keycode import Keycode
from adafruit_hid.consumer_control import ConsumerControl
from adafruit_hid.consumer_control_code import ConsumerControlCode

# Define pins
DT_PIN = board.GP19
CLK_PIN = board.GP18
SW_PIN = board.GP20

BTN_DOWN = 1
BTN_UP = 0

button = digitalio.DigitalInOut(SW_PIN)
button.direction = digitalio.Direction.INPUT
button.pull = digitalio.Pull.DOWN
last_button_state = BTN_UP
last_position = None

# Define initial mode
mode = &quot;VOL&quot;

# Define keyboard layout
pikku_keyboard = keyboard.Keyboard(usb_hid.devices)
layout = KeyboardLayoutUS(pikku_keyboard)
cc = ConsumerControl(usb_hid.devices)
pikku_mouse = mouse.Mouse(usb_hid.devices)

# Define rotary encoder callback function
def change_mode():
    global mode
    if mode == &quot;VOL&quot;:
        mode = &quot;SCROLL&quot;
    elif mode == &quot;SCROLL&quot;:
        mode = &quot;CURSOR&quot;
    elif mode == &quot;CURSOR&quot;:
        mode = &quot;FIDGET&quot;
    else:
        mode = &quot;VOL&quot;
    time.sleep(0.1)

# Define rotary encoder reading function
def read_encoder(encoder):
    global last_position
    position = encoder.position
    if last_position == None or position == last_position:
        last_position = position
        return 0
    delta = last_position - position
    last_position = position
    return delta

# Create rotary encoder object
encoder = rotaryio.IncrementalEncoder(DT_PIN, CLK_PIN)

# Define main loop
while True:
    # Read rotary encoder
    encoder_delta = read_encoder(encoder)
    if encoder_delta != 0:
        print(&quot;delta: &quot; + str(encoder_delta))

    # Adjust volume, scroll or cursor based on mode
    if mode == &quot;VOL&quot;:
        if encoder_delta &gt; 0:
            cc.send(ConsumerControlCode.VOLUME_INCREMENT)
        elif encoder_delta &lt; 0:
            cc.send(ConsumerControlCode.VOLUME_DECREMENT)
    elif mode == &quot;SCROLL&quot;:
        if encoder_delta &gt; 0:
            pikku_mouse.move(wheel=-1)
        elif encoder_delta &lt; 0:
            pikku_mouse.move(wheel=1)
    elif mode == &quot;CURSOR&quot;:
        if encoder_delta &gt; 0:
            pikku_keyboard.send(Keycode.DOWN_ARROW)
        elif encoder_delta &lt; 0:
            pikku_keyboard.send(Keycode.UP_ARROW)
    else:
        # Fidget mode - do nothing
        if encoder_delta &gt; 0:
            print(&quot;right&quot;)
        elif encoder_delta &lt; 0:
            print(&quot;left&quot;)

    # Check if rotary encoder switch is pressed
    if button.value is False and last_button_state == BTN_UP:
        change_mode()
        print(&quot;new mode: &quot; + mode) 
        last_button_state = BTN_DOWN
        time.sleep(0.1)
    else:
        last_button_state = BTN_UP

    # Delay to avoid debounce
    time.sleep(0.02)
</code></pre><p>Have fun on building your own! Remember to <a href="https://www.printables.com/model/453228-pikku-macropad/comments?ref=codeof.me">post your make on Printables</a> :)</p><p>Join the discussion in Mastodon:</p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://fosstodon.org/@tlaukkanen/110288719127128739?ref=codeof.me"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Tommi Laukkanen (@tlaukkanen@fosstodon.org)</div><div class="kg-bookmark-description">Attached: 2 images Released code and model files for this small multimode dial controller, &#x201C;Pikku Dial&#x201D;. It goes well together with its sibling, Pikku Macropad &#x1F970;#RaspberryPi #3dPrinting #CircuitPython https://www.codeof.me/pikku-dial-multi-mode-dial-controller/</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://fosstodon.org/packs/media/icons/apple-touch-icon-1024x1024-db6849588b44f525363c37b65ef0ac66.png" alt="Pikku Dial - Multimode Dial Controller Powered by RPi Pico"><span class="kg-bookmark-author">Fosstodon</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://cdn.fosstodon.org/media_attachments/files/110/288/710/377/443/045/small/b774ef46f3bf8cd2.png" alt="Pikku Dial - Multimode Dial Controller Powered by RPi Pico"></div></a></figure>]]></content:encoded></item><item><title><![CDATA[Pikku - Raspberry Pi Pico Powered Macropad]]></title><description><![CDATA[Small customizable six key macropad powered by Raspberry Pi Pico microcontroller.]]></description><link>https://www.codeof.me/pikku-raspberry-pi-pico-powered-macropad/</link><guid isPermaLink="false">63bdbc5817afc6000150677e</guid><category><![CDATA[3DPrinting]]></category><category><![CDATA[Gadgets]]></category><category><![CDATA[Productivity]]></category><category><![CDATA[RaspberryPi]]></category><category><![CDATA[Python]]></category><dc:creator><![CDATA[Tommi Laukkanen]]></dc:creator><pubDate>Sat, 15 Apr 2023 15:50:33 GMT</pubDate><media:content url="https://www.codeof.me/content/images/2023/04/Pikku_header--Medium-.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://www.codeof.me/content/images/2023/04/Pikku_header--Medium-.jpg" alt="Pikku - Raspberry Pi Pico Powered Macropad"><p>I designed and built a small Raspberry Pi Pico powered macropad. Code is written with Python and it is easy to customize with your own macros. You can start with current code as a basis and add your own macros there. I added functionality so that the first key changes the mode - current modes include <em>VS Code</em>, <em>MS Teams</em>, <em>Git</em> and more could be easily added.</p><p><strong>TL;DR</strong>: Check the GitHub repository for code: <a href="https://github.com/tlaukkanen/pikku-macropad?ref=codeof.me">github.com/tlaukkanen/pikku-macropad</a>. 3D printable .stl models are also available via <a href="https://www.printables.com/model/453228-pikku-macropad?ref=codeof.me">Printables.com</a></p><h2 id="bill-of-material">Bill of Material</h2><p>Components needed to build your own macropad:</p><ul><li>Raspberry Pi Pico</li><li>MX key switches and keycaps (6 pcs), I used <a href="https://thepihut.com/products/orange-dsa-keycaps-for-mx-compatible-switches-10-pack?ref=codeof.me">these</a> from Pi Hut</li><li>Display Module SSD1306 I2C 128x32 LCD OLED Screen</li><li>3D printed case</li></ul><h2 id="schematics">Schematics</h2><p>The wiring is quite simple: SSD1306 is connected via I2C pins (GP4=SDA and GP5=SCL) and buttons are connected to 3.3V voltage and other pins from switch is connected to individual GP6,7,8,10,11,12 pins.</p><figure class="kg-card kg-image-card"><img src="https://www.codeof.me/content/images/2023/04/circuit.png" class="kg-image" alt="Pikku - Raspberry Pi Pico Powered Macropad" loading="lazy" width="480" height="540"></figure><h2 id="hardware3d-printed-case-and-soldering">Hardware - 3D Printed Case and Soldering</h2><p>I designed the case to be simple and easy to print. It consists of three layers where the base houses the Raspberry Pico, the middle piece holds the key switches in place and the small top part fits the display inside.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.codeof.me/content/images/2023/04/Pikku_3D_case.png" class="kg-image" alt="Pikku - Raspberry Pi Pico Powered Macropad" loading="lazy" width="850" height="850" srcset="https://www.codeof.me/content/images/size/w600/2023/04/Pikku_3D_case.png 600w, https://www.codeof.me/content/images/2023/04/Pikku_3D_case.png 850w" sizes="(min-width: 720px) 720px"><figcaption>All three parts can be printed without support</figcaption></figure><p>You can print parts with assorted colors to have different looks.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.codeof.me/content/images/2023/04/Pikku_stack--Medium-.jpg" class="kg-image" alt="Pikku - Raspberry Pi Pico Powered Macropad" loading="lazy" width="768" height="768" srcset="https://www.codeof.me/content/images/size/w600/2023/04/Pikku_stack--Medium-.jpg 600w, https://www.codeof.me/content/images/2023/04/Pikku_stack--Medium-.jpg 768w" sizes="(min-width: 720px) 720px"><figcaption>Assorted color combinations</figcaption></figure><p>Soldering should be done when key switches are in place as the wiring wouldn&apos;t allow you to fit switches into the middle layer part afterwards.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.codeof.me/content/images/2023/04/Pikku_soldering--Medium-.jpg" class="kg-image" alt="Pikku - Raspberry Pi Pico Powered Macropad" loading="lazy" width="768" height="768" srcset="https://www.codeof.me/content/images/size/w600/2023/04/Pikku_soldering--Medium-.jpg 600w, https://www.codeof.me/content/images/2023/04/Pikku_soldering--Medium-.jpg 768w" sizes="(min-width: 720px) 720px"><figcaption>Key switch wiring needs to be done when switches are inserted into middle piece</figcaption></figure><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.codeof.me/content/images/2023/04/Pikku_open_case--Medium-.jpg" class="kg-image" alt="Pikku - Raspberry Pi Pico Powered Macropad" loading="lazy" width="1364" height="768" srcset="https://www.codeof.me/content/images/size/w600/2023/04/Pikku_open_case--Medium-.jpg 600w, https://www.codeof.me/content/images/size/w1000/2023/04/Pikku_open_case--Medium-.jpg 1000w, https://www.codeof.me/content/images/2023/04/Pikku_open_case--Medium-.jpg 1364w" sizes="(min-width: 720px) 720px"><figcaption>All components soldered and in place. ...and I spotted that brown wire came loose from the first switch. Had to resolder that in after taking a photo :)</figcaption></figure><h2 id="code">Code</h2><p>I flashed the Raspberry Pi Pico with the latest CircuitPython firmware by following <a href="https://learn.adafruit.com/getting-started-with-raspberry-pi-pico-circuitpython/circuitpython?ref=codeof.me">this &quot;Getting started with CircuitPython&quot; tutorial from Adafruit</a>. For code I used CircuitPython libraries:</p><ul><li><a href="https://github.com/adafruit/Adafruit_CircuitPython_HID?ref=codeof.me">adafruit-circuitpython-hid</a> for keyboard communication</li><li><a href="https://github.com/adafruit/Adafruit_CircuitPython_SSD1306?ref=codeof.me">adafruit-circuitpython-ssd1306</a> for controlling display</li><li>Downloaded font5x8.bin file from <a href="https://github.com/adafruit/Adafruit_CircuitPython_framebuf?ref=codeof.me">adafruit-circuitpython-framebuf</a> and uploaded to root of Pico. This is used by SSD1306 module for displaying text with <code>ssd1306_display.text(&quot;some text&quot;, x, y)</code></li></ul><p>Here&apos;s the full code for the macropad. Code can also be found from GitHub, <a href="https://github.com/tlaukkanen/pikku-macropad/?ref=codeof.me">here</a>.</p><pre><code class="language-python">import board
import busio
import time
import usb_hid
import digitalio
import adafruit_ssd1306
from adafruit_hid.keycode import Keycode
from adafruit_hid.keyboard import Keyboard
from adafruit_hid.keyboard_layout_us import KeyboardLayoutUS

# Button mapping
BTN1_PIN = board.GP6
BTN2_PIN = board.GP8
BTN3_PIN = board.GP11
BTN4_PIN = board.GP7
BTN5_PIN = board.GP10
BTN6_PIN = board.GP12

buttons = [
    digitalio.DigitalInOut(BTN1_PIN),
    digitalio.DigitalInOut(BTN2_PIN),
    digitalio.DigitalInOut(BTN3_PIN),
    digitalio.DigitalInOut(BTN4_PIN),
    digitalio.DigitalInOut(BTN5_PIN),
    digitalio.DigitalInOut(BTN6_PIN)
]
for i in range(0,6):
    buttons[i].direction = digitalio.Direction.INPUT
    buttons[i].pull = digitalio.Pull.DOWN

# OLED display setup
WIDTH = 128
HEIGHT = 32

# I2C pins
SCL = 5
SDA = 4

BTN_DOWN = 1
BTN_UP = 0

lastState = BTN_UP
modes = [
    [&apos;Teams&apos;,           # Mode name
     &apos;Search&apos;,          # Key 2 function
     &apos;Goto&apos;,            # Key 3 function
     &apos;Toggle mute&apos;,     # Key 4 function 
     &apos;Toggle camera&apos;,   # Key 5 function
     &apos;Hangup&apos;           # Key 6 function
    ],
    [&apos;VS Code&apos;,         # Mode name
     &apos;Explorer&apos;,        # Key 2 function
     &apos;Problems&apos;,        # Key 3 function
     &apos;Search&apos;,          # Key 4 function
     &apos;Debug&apos;,           # Key 5 function
     &apos;Output&apos;           # Key 6 function
    ],
    [&apos;Git&apos;,             # Mode name
     &apos;Branch..&apos;,        # Key 2 function
     &apos;Checkout main&apos;,   # Key 3 function
     &apos;Status&apos;,          # Key 4 function
     &apos;Push&apos;,            # Key 5 function
     &apos;Pull org main&apos;    # Key 6 function
    ]

]

keyboard = Keyboard(usb_hid.devices)
layout = KeyboardLayoutUS(keyboard)
MODE_TEAMS = 0
MODE_VSCODE = 1
MODE_GIT = 2
COUNT_OF_MODES = 3
currentMode = MODE_GIT

# Initialize I2C
i2c = busio.I2C(scl=board.GP5, sda=board.GP4)
display = adafruit_ssd1306.SSD1306_I2C(WIDTH, HEIGHT, i2c)

def handle_mode_press(mode, key):
    if(mode == MODE_TEAMS):
        if key == 1:
            keyboard.send(Keycode.LEFT_CONTROL, Keycode.E)
        if key == 2:
            keyboard.send(Keycode.LEFT_CONTROL, Keycode.G)
        if key == 3:
            keyboard.send(Keycode.LEFT_CONTROL, Keycode.SHIFT, Keycode.M)
        if key == 4:
            keyboard.send(Keycode.LEFT_CONTROL, Keycode.SHIFT, Keycode.O)
        if key == 5:
            keyboard.send(Keycode.LEFT_CONTROL, Keycode.SHIFT, Keycode.B)
    if(mode == MODE_VSCODE):
        if key == 1:
            keyboard.send(Keycode.LEFT_CONTROL, Keycode.SHIFT, Keycode.E)
        if key == 2:
            keyboard.send(Keycode.LEFT_CONTROL, Keycode.SHIFT, Keycode.M)
        if key == 3:
            keyboard.send(Keycode.LEFT_CONTROL, Keycode.SHIFT, Keycode.F)
        if key == 4:
            keyboard.send(Keycode.F5)
        if key == 5:
            keyboard.send(Keycode.LEFT_CONTROL, Keycode.SHIFT, Keycode.U)
    if(mode == MODE_GIT):
        if key == 1:
            layout.write(&apos;git branch -b &apos;)
        if key == 2:
            layout.write(&apos;git checkout main\n&apos;)
        if key == 3:
            layout.write(&apos;git status\n&apos;)
        if key == 4:
            layout.write(&apos;git push\n&apos;)
        if key == 5:
            layout.write(&apos;git pull origin main\n&apos;)

def draw_screen(text, x, y):
    global currentMode
    global modes
    display.fill(0)
    display.text(text, x, y, 1)
    display.text(&quot;Mode: &quot; + modes[currentMode][0], 24, 12, 1)
    display.text(&quot;Pikku v1.0&quot;, 24, 24, 1)
    display.show()

def buttonPress(last):
    global currentMode
    tempLast = last
    text = &quot;&quot;
    nextState = last

    # Check key 1 mode change
    if (tempLast == BTN_UP) and (buttons[0].value):
        nextState = BTN_DOWN
        currentMode += 1
        if(currentMode&gt;COUNT_OF_MODES-1):
            currentMode = 0
        draw_screen(&quot;&quot;, 1, 1)

    # Check keys 2-6 press
    for btn in range(1, 6):
        if (tempLast == BTN_UP) and (buttons[btn].value):
            nextState = BTN_DOWN
            text = modes[currentMode][btn]
            draw_screen(text, 1, 1)
            # Type the text as key presses
            handle_mode_press(currentMode, btn)
            time.st pull origleep(0.256)

    # Key up
    if  (tempLast == BTN_DOWN and
        not buttons[0].value and
        not buttons[1].value and
        not buttons[2].value and
        not buttons[3].value and
        not buttons[4].value and
        not buttons[5].value):
        nextState = BTN_UP
        draw_screen(&quot;&quot;, 1, 1)
    return nextState

draw_screen(&quot;&quot;, 1, 1)
while True:
    lastState = buttonPress(lastState)</code></pre><p>Have fun building your own! Remember to <a href="https://www.printables.com/model/453228-pikku-macropad/comments?ref=codeof.me">post your make on Printables</a> :)</p><p>Join the discussion with Mastodon:</p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://fosstodon.org/@tlaukkanen/110203640566929595?ref=codeof.me"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Tommi Laukkanen (@tlaukkanen@fosstodon.org)</div><div class="kg-bookmark-description">Attached: 1 image Designed and built a small macropad, Pikku. Powered by #RaspberryPi Pico and #CircuitPython. I made the code and STL available for #3dPrinting https://www.codeof.me/pikku-raspberry-pi-pico-powered-macropad/</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://fosstodon.org/packs/media/icons/apple-touch-icon-1024x1024-db6849588b44f525363c37b65ef0ac66.png" alt="Pikku - Raspberry Pi Pico Powered Macropad"><span class="kg-bookmark-author">Fosstodon</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://cdn.fosstodon.org/media_attachments/files/110/203/635/344/333/873/original/9f8130fc1313ae01.jpg" alt="Pikku - Raspberry Pi Pico Powered Macropad"></div></a></figure>]]></content:encoded></item><item><title><![CDATA[Cyberdore - Raspberry Pi powered Cyberdeck]]></title><description><![CDATA[<p>As a fan of cyberpunk and sci-fi I&apos;ve always wanted to have a cyberdeck of my own. Finally designed and built one - I call it Cyberdore.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.codeof.me/content/images/2023/01/cassette.gif" class="kg-image" alt loading="lazy" width="400" height="300"><figcaption>I broke the small 128x128 display when installing it to the device. In a way it gives a nice cyberpunk feel</figcaption></figure>]]></description><link>https://www.codeof.me/cyberdore-raspberry-pi-powered-cyberdeck/</link><guid isPermaLink="false">63c3ef4f17afc600015067ec</guid><category><![CDATA[RaspberryPi]]></category><category><![CDATA[Gadgets]]></category><category><![CDATA[Cyberdeck]]></category><category><![CDATA[3DPrinting]]></category><dc:creator><![CDATA[Tommi Laukkanen]]></dc:creator><pubDate>Sun, 15 Jan 2023 20:55:07 GMT</pubDate><media:content url="https://www.codeof.me/content/images/2023/01/20211227_122320.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://www.codeof.me/content/images/2023/01/20211227_122320.jpg" alt="Cyberdore - Raspberry Pi powered Cyberdeck"><p>As a fan of cyberpunk and sci-fi I&apos;ve always wanted to have a cyberdeck of my own. Finally designed and built one - I call it Cyberdore.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.codeof.me/content/images/2023/01/cassette.gif" class="kg-image" alt="Cyberdore - Raspberry Pi powered Cyberdeck" loading="lazy" width="400" height="300"><figcaption>I broke the small 128x128 display when installing it to the device. In a way it gives a nice cyberpunk feel to it when it&apos;s missing some of the lines :)</figcaption></figure><h2 id="ingredients">Ingredients</h2><p>I wanted my cyberdeck to be portable but still run full Linux. Also wanted to have some sci-fi feel to it but keep it a bit retro. Here are components that I used:</p><ul><li>Raspberry Pi (I used 3 Model B+ first but ended up using Zero W for lower energy consumption)</li><li>Battery - I used this battery shield that takes single 18650 battery <a href="https://www.amazon.de/-/en/AZDelivery-Battery-Expansion-Shield-18650/dp/B086W7326Q/?ref=codeof.me">Amazon.de</a></li><li>3.5&quot; LCD Display - I used <a href="https://www.waveshare.com/wiki/3.5inch_RPi_LCD_(A)?ref=codeof.me">this Waveshare version</a></li><li>Rii X1 mini wireless keyboard - <a href="https://www.amazon.de/gp/product/B00VWVXQB6/?ref=codeof.me">this in Amazon.de</a></li><li>3D printed case - see next chapter</li></ul><p>That was the mandatory part to get functional minicomputer. I did want to have some sci-fi cyberpunk props there too, so I designed a small screen on the side to display retro cassette mimicking animation. For this I used:</p><ul><li>Raspberry Pi Pico</li><li>Small 1.5&quot; OLED display with 128x128 resolution, I used <a href="https://www.waveshare.com/1.5inch-oled-module.htm?ref=codeof.me">this from Waveshare</a></li><li>40mm flatback magnifying glass, something like <a href="https://www.aliexpress.com/item/32931774604.html?ref=codeof.me">this in Aliexpress</a></li><li>Red LED for power indicator</li></ul><p>I also added a wi-fi antenna as a prop. I did mean to enhance the wi-fi range with it first but eventually didn&apos;t add external wi-fi card for this purpose.</p><p>Total cost for the components depends highly where you are. Many might have Raspberry Pi already but depending on the model (and your country) getting it from the store can cost anything between 10&#x20AC; to 90&#x20AC; due to component shortage. I&apos;d guess the total average cost for materials is around 100-150&#x20AC;.</p><h2 id="cyberdeck-case">Cyberdeck case</h2><p>I started the design based on keyboard dimensions and I was able to embed the keyboard quite well in the front with minimal noticeable gaps. I added main 3.5&quot; and circular lens glass displays on top of the keyboard. I also wanted to have a retro feel to the case and gave it the grille and power led on top as a tribute to Commodore computers that got me started in computing in 80s (Vic 20 &amp; C64 FTW!)</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.codeof.me/content/images/2023/01/Model-1.png" class="kg-image" alt="Cyberdore - Raspberry Pi powered Cyberdeck" loading="lazy" width="1402" height="782" srcset="https://www.codeof.me/content/images/size/w600/2023/01/Model-1.png 600w, https://www.codeof.me/content/images/size/w1000/2023/01/Model-1.png 1000w, https://www.codeof.me/content/images/2023/01/Model-1.png 1402w" sizes="(min-width: 720px) 720px"><figcaption>Final cyberdeck, the Cyberdore, case</figcaption></figure><p>The case is made of layers which are held together by four M2 screws that go through all the layers. The back panel has a snap fit design so it can be removed without screws when you need to have access to Raspberry Pi.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.codeof.me/content/images/2023/01/Model-2.png" class="kg-image" alt="Cyberdore - Raspberry Pi powered Cyberdeck" loading="lazy" width="1366" height="802" srcset="https://www.codeof.me/content/images/size/w600/2023/01/Model-2.png 600w, https://www.codeof.me/content/images/size/w1000/2023/01/Model-2.png 1000w, https://www.codeof.me/content/images/2023/01/Model-2.png 1366w" sizes="(min-width: 720px) 720px"><figcaption>The layers</figcaption></figure><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.codeof.me/content/images/2023/01/Cyberdeck_Cybernepa_case_02_middle.png" class="kg-image" alt="Cyberdore - Raspberry Pi powered Cyberdeck" loading="lazy" width="800" height="600" srcset="https://www.codeof.me/content/images/size/w600/2023/01/Cyberdeck_Cybernepa_case_02_middle.png 600w, https://www.codeof.me/content/images/2023/01/Cyberdeck_Cybernepa_case_02_middle.png 800w" sizes="(min-width: 720px) 720px"><figcaption>Rendering of the case. There&apos;s plenty of room to hold extra accessories or larger batteries.</figcaption></figure><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.codeof.me/content/images/2023/01/20211227_142005.jpg" class="kg-image" alt="Cyberdore - Raspberry Pi powered Cyberdeck" loading="lazy" width="2000" height="2000" srcset="https://www.codeof.me/content/images/size/w600/2023/01/20211227_142005.jpg 600w, https://www.codeof.me/content/images/size/w1000/2023/01/20211227_142005.jpg 1000w, https://www.codeof.me/content/images/size/w1600/2023/01/20211227_142005.jpg 1600w, https://www.codeof.me/content/images/size/w2400/2023/01/20211227_142005.jpg 2400w" sizes="(min-width: 720px) 720px"><figcaption>Here are initial internals with RPi 3 Model B+ - switched later to RPi Zero W</figcaption></figure><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.codeof.me/content/images/2023/01/20211227_141855.jpg" class="kg-image" alt="Cyberdore - Raspberry Pi powered Cyberdeck" loading="lazy" width="2000" height="2000" srcset="https://www.codeof.me/content/images/size/w600/2023/01/20211227_141855.jpg 600w, https://www.codeof.me/content/images/size/w1000/2023/01/20211227_141855.jpg 1000w, https://www.codeof.me/content/images/size/w1600/2023/01/20211227_141855.jpg 1600w, https://www.codeof.me/content/images/size/w2400/2023/01/20211227_141855.jpg 2400w" sizes="(min-width: 720px) 720px"><figcaption>Snap fit back panel</figcaption></figure><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.codeof.me/content/images/2023/01/20211227_180514.jpg" class="kg-image" alt="Cyberdore - Raspberry Pi powered Cyberdeck" loading="lazy" width="2000" height="2000" srcset="https://www.codeof.me/content/images/size/w600/2023/01/20211227_180514.jpg 600w, https://www.codeof.me/content/images/size/w1000/2023/01/20211227_180514.jpg 1000w, https://www.codeof.me/content/images/size/w1600/2023/01/20211227_180514.jpg 1600w, https://www.codeof.me/content/images/size/w2400/2023/01/20211227_180514.jpg 2400w" sizes="(min-width: 720px) 720px"><figcaption>First boot - it works :D</figcaption></figure><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.codeof.me/content/images/2023/08/20211227_121915_edited--Medium-.jpg" class="kg-image" alt="Cyberdore - Raspberry Pi powered Cyberdeck" loading="lazy" width="768" height="768" srcset="https://www.codeof.me/content/images/size/w600/2023/08/20211227_121915_edited--Medium-.jpg 600w, https://www.codeof.me/content/images/2023/08/20211227_121915_edited--Medium-.jpg 768w" sizes="(min-width: 720px) 720px"><figcaption>Portable enough to use around the house</figcaption></figure><h2 id="printables">Printables</h2><p>Checkout my Printables page for .stl files if you&apos;d like to build or mod your own version: <a href="https://www.printables.com/model/369948-cyberdore-rpi-powered-cyberdeck?ref=codeof.me">Cyberdore - RPi powered Cyberdeck by Tommi L. | Download free STL model | Printables.com</a></p>]]></content:encoded></item><item><title><![CDATA[Picomoro - Pomodoro Timer]]></title><description><![CDATA[Built a device with RPi Pico that provides the timer functionality for Pomodoro technique. Here are instructions on how to build your own.]]></description><link>https://www.codeof.me/picomoro-pomodoro-timer/</link><guid isPermaLink="false">63af0b8f78123d0001c01318</guid><category><![CDATA[RaspberryPi]]></category><category><![CDATA[Python]]></category><category><![CDATA[Productivity]]></category><category><![CDATA[3DPrinting]]></category><category><![CDATA[Gadgets]]></category><dc:creator><![CDATA[Tommi Laukkanen]]></dc:creator><pubDate>Fri, 13 Jan 2023 19:13:33 GMT</pubDate><media:content url="https://www.codeof.me/content/images/2023/01/Picomoro-in-hand--Large-_edited.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://www.codeof.me/content/images/2023/01/Picomoro-in-hand--Large-_edited.jpg" alt="Picomoro - Pomodoro Timer"><p>Starting the year 2023 with productivity in mind. I&apos;ve always been fan of different techniques to gain better focus for tasks at hand. Pomodoro technique is interesting and I&apos;ve always liked physical productivity gear over software apps.</p><p>Therefore, I took as a task to design and build Picomoro - a small pomodoro timer powered by Raspberry Pi Pico. Designed as cubelike case with Fusion 360 and printed with Prusa Mini. Here are the steps if you&apos;d also like to build one or use it as a basis for something more exciting, like smart clock etc.</p><p><strong>TL;DR</strong> Checkout source code at GitHub, <a href="https://www.printables.com/model/361636-picomoro?ref=codeof.me">3D printable case at Printables.com</a></p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.codeof.me/content/images/2023/01/Picomoro-on-desk--Large-_edited.jpg" class="kg-image" alt="Picomoro - Pomodoro Timer" loading="lazy" width="1918" height="1080" srcset="https://www.codeof.me/content/images/size/w600/2023/01/Picomoro-on-desk--Large-_edited.jpg 600w, https://www.codeof.me/content/images/size/w1000/2023/01/Picomoro-on-desk--Large-_edited.jpg 1000w, https://www.codeof.me/content/images/size/w1600/2023/01/Picomoro-on-desk--Large-_edited.jpg 1600w, https://www.codeof.me/content/images/2023/01/Picomoro-on-desk--Large-_edited.jpg 1918w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">Picomoro on desk</span></figcaption></figure><h2 id="pomodoro-technique">Pomodoro Technique</h2><p>Wikipedia describes Pomodoro technique like this:</p><blockquote>The original technique has six steps:<br><br>1. Decide on the task to be done.<br>2. Set the pomodoro timer (typically for 25 minutes).<br>3. Work on the task.<br>4. End work when the timer rings and take a short break (typically 5&#x2013;10 minutes).<br>5. If you have finished fewer than three pomodoros, go back to Step 2 and repeat until you go through all three pomodoros.<br>6. After three pomodoros are done, take the fourth pomodoro and then take a long break (typically 20 to 30 minutes). Once the long break is finished, return to step 2.<br><br>For the purposes of the technique, a pomodoro is an interval of work time.</blockquote><h2 id="functional-requirements">Functional Requirements</h2><p>I started the design with my additional technical requirements. Timer should have:</p><ul><li>Display for displaying minutes and seconds</li><li>Button or dial to change the initial time and start the timer</li><li>Buzzer to notify that time is up</li><li>Timer can be powered by USB cable</li></ul><h2 id="bill-of-material">Bill of Material</h2><p>From the requirements I ended up with the following components. Mostly built with components that I had laying around:</p><ul><li>Raspberry Pi Pico - small and powerful microcontroller that can be programmed with many languages, including Micropython</li><li>SMA420564 - Four Digit Seven Segment LED Display 0.56&quot;</li><li>Mini on/off rocker switch (<a href="https://www.amazon.de/gp/product/B07L6HMQ4S?ref=codeof.me">this</a> in Amazon.de or <a href="https://www.aliexpress.com/item/32976435586.html?ref=codeof.me">this</a> could be the same in Aliexpress)</li><li>4 x 220 Ohm resistors</li><li>KY-006 Piezoelectric buzzer</li><li>KY-040 Rotary Encoder</li><li>3 AA batteries</li><li>on/off switch</li></ul><h2 id="schematic">Schematic</h2><p>Here&apos;s a schematic on how I connected the components together. Instead of 5V battery I used three normal AA batteries in series (~ 4.5V). As stated <a href="https://projects.raspberrypi.org/en/projects/introduction-to-the-pico/12?ref=codeof.me">here</a>, Pico can be safely powered with voltage between 1.8V and 5.5V. I added 220 Ohm resistors for the display to limit the current for digit displays. It seems to work without those but didn&apos;t like to test if it breaks something eventually when running for a long time.</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.codeof.me/content/images/2023/01/circuit.svg" class="kg-image" alt="Picomoro - Pomodoro Timer" loading="lazy" width="620" height="440"><figcaption><span style="white-space: pre-wrap;">Schematic for connecting display, buzzer and rotary encoder to Raspberry Pi Pico</span></figcaption></figure><h2 id="case">Case</h2><p>It was fun to design this. I wanted it to have retro feel and look like a bedside clock. Case design should still have enough space to fit everything inside without special arrangements with the circuitry.</p><p>So, I produced this cubelike shape but with a twist so that the bottom is cut in a way that display rotates a bit upwards for easier reading.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.codeof.me/content/images/2023/01/Case-design.png" class="kg-image" alt="Picomoro - Pomodoro Timer" loading="lazy" width="1785" height="1077" srcset="https://www.codeof.me/content/images/size/w600/2023/01/Case-design.png 600w, https://www.codeof.me/content/images/size/w1000/2023/01/Case-design.png 1000w, https://www.codeof.me/content/images/size/w1600/2023/01/Case-design.png 1600w, https://www.codeof.me/content/images/2023/01/Case-design.png 1785w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">3D case design using Fusion 360</span></figcaption></figure><p><em>Retrospect</em>: a better placement for rotary dial could have been on the top as one of the functionalities is to press the dial and it now needs to be held from the other side if you press the dial - otherwise it would slide on the desk as it&apos;s so light.</p><h3 id="3d-printing-the-case">3D printing the case</h3><p>Printed the case parts from two different PLAs. The display cover with matte black PLA and dial, case and back cover with beige PLA that reminds me of good old Commodore 64 and Amiga case colors.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.codeof.me/content/images/2023/01/3d-prints.png" class="kg-image" alt="Picomoro - Pomodoro Timer" loading="lazy" width="1600" height="564" srcset="https://www.codeof.me/content/images/size/w600/2023/01/3d-prints.png 600w, https://www.codeof.me/content/images/size/w1000/2023/01/3d-prints.png 1000w, https://www.codeof.me/content/images/2023/01/3d-prints.png 1600w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">3D printed with two different PLAs. I always try to design pieces so that you wouldn&apos;t need support structure.</span></figcaption></figure><p>Final tests before closing the enclosure.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.codeof.me/content/images/2023/01/Picomoro-in-progress--Large-_edited.jpg" class="kg-image" alt="Picomoro - Pomodoro Timer" loading="lazy" width="1918" height="1080" srcset="https://www.codeof.me/content/images/size/w600/2023/01/Picomoro-in-progress--Large-_edited.jpg 600w, https://www.codeof.me/content/images/size/w1000/2023/01/Picomoro-in-progress--Large-_edited.jpg 1000w, https://www.codeof.me/content/images/size/w1600/2023/01/Picomoro-in-progress--Large-_edited.jpg 1600w, https://www.codeof.me/content/images/2023/01/Picomoro-in-progress--Large-_edited.jpg 1918w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">Testing the circuitry before fitting all inside the case</span></figcaption></figure><h2 id="code">Code</h2><p>I wrote the actual logic for Raspberry Pi Pico with Micropython. Basic logic is that timer counts down and displays the remaining time. The rotary encoder can control the initial time which defaults to Pomodoro&apos;s 25 minutes but can be adjusted by turning the knob either clockwise (increase minutes) or counterclockwise (decrease minutes). When the knob is pressed the timer starts.</p><p>Buzzer is used for notifying user that knob controls are acknowledged. The buzzer will also beep when the timer runs to zero.</p><p>Full code is only a bit under 250 lines of code:</p><pre><code class="language-python">import time
import micropython
import uasyncio as asyncio
from machine import Pin, PWM

class Rotary:
  
  ROT_CW = 1
  ROT_CCW = 2
  SW_PRESS = 4
  SW_RELEASE = 8
  
  def __init__(self,dt,clk,sw):
    self.dt_pin = Pin(dt, Pin.IN, Pin.PULL_DOWN)
    self.clk_pin = Pin(clk, Pin.IN, Pin.PULL_DOWN)
    self.sw_pin = Pin(sw, Pin.IN, Pin.PULL_DOWN)
    self.last_status = (self.dt_pin.value() &lt;&lt; 1) | self.clk_pin.value()
    self.dt_pin.irq(handler=self.rotary_change, trigger=Pin.IRQ_FALLING | Pin.IRQ_RISING )
    self.clk_pin.irq(handler=self.rotary_change, trigger=Pin.IRQ_FALLING | Pin.IRQ_RISING )
    self.sw_pin.irq(handler=self.switch_detect, trigger=Pin.IRQ_FALLING | Pin.IRQ_RISING )
    self.handlers = []
    self.last_button_status = self.sw_pin.value()
      
  def rotary_change(self, pin):
    new_status = (self.dt_pin.value() &lt;&lt; 1) | self.clk_pin.value()
    if new_status == self.last_status:
      return
    transition = (self.last_status &lt;&lt; 2) | new_status
    if transition == 0b1110:
      micropython.schedule(self.call_handlers, Rotary.ROT_CW)
    elif transition == 0b1101:
      micropython.schedule(self.call_handlers, Rotary.ROT_CCW)
    self.last_status = new_status
      
  def switch_detect(self,pin):
    if self.last_button_status == self.sw_pin.value():
      return
    self.last_button_status = self.sw_pin.value()
    if self.sw_pin.value():
      micropython.schedule(self.call_handlers, Rotary.SW_RELEASE)
    else:
      micropython.schedule(self.call_handlers, Rotary.SW_PRESS)
          
  def add_handler(self, handler):
    self.handlers.append(handler)
  
  def call_handlers(self, type):
    for handler in self.handlers:
        handler(type)

buzzer = PWM(Pin(22))
async def beep():
  buzzer.freq(600)
  buzzer.duty_u16(200)
  await asyncio.sleep(0.2)
  buzzer.deinit()

async def upBeep():
  buzzer.freq(800)
  buzzer.duty_u16(100)
  await asyncio.sleep(0.1)
  buzzer.deinit()

async def downBeep():
  buzzer.freq(500)
  buzzer.duty_u16(100)
  await asyncio.sleep(0.1)
  buzzer.deinit()

async def buzz():
  buzzer.freq(400)
  buzzer.duty_u16(200)
  await asyncio.sleep(0.1)
  buzzer.freq(800)
  buzzer.duty_u16(400)
  await asyncio.sleep(0.2)
  buzzer.freq(600)
  buzzer.duty_u16(200)
  await asyncio.sleep(0.1)
  buzzer.deinit()

DT_PIN = 27
CLK_PIN = 28
SW_PIN = 26

rotary = Rotary(DT_PIN, CLK_PIN, SW_PIN)
rotaryValue = 0
# Default countdown time 25 minutes in seconds
countdown = 25 * 60
isActive = False

def rotary_changed(change):
  global rotaryValue
  global countdown
  global isActive
  if change == Rotary.ROT_CW:
    asyncio.run(upBeep())
    countdown += 60
    # round countdown to nearest minute
    countdown = countdown - (countdown % 60)
    isActive = False
    rotaryValue += 1
    print(&quot;Rotary CW&quot;, rotaryValue)
  elif change == Rotary.ROT_CCW:
    asyncio.run(downBeep())
    if(countdown&gt;60):
      countdown -= 60
    # round countdown to nearest minute
    countdown = countdown - (countdown % 60)
    isActive = False

    rotaryValue -= 1
    print(&quot;Rotary CCW&quot;, rotaryValue)
  elif change == Rotary.SW_PRESS:
    if(countdown&lt;=0):
      countdown = 25 * 60
    else:    
      isActive = True
    asyncio.run(beep())
  elif change == Rotary.SW_RELEASE:
    print(&quot;Switch released&quot;)

rotary.add_handler(rotary_changed)

# SMA420564 pinout mapping to GPIO pins
pin = [
  # Top row
  Pin(4, Pin.OUT), # GP4 through 220 Ohm resistor
  Pin(5, Pin.OUT), # GP5
  Pin(6, Pin.OUT), # GP6
  Pin(7, Pin.OUT), # GP7 through 220 Ohm resistor
  Pin(8, Pin.OUT), # GP8 through 220 Ohm resistor
  Pin(9, Pin.OUT), # GP9
  
  # Bottom row
  Pin(16, Pin.OUT), # GP16
  Pin(17, Pin.OUT), # GP17
  Pin(18, Pin.OUT), # GP18
  Pin(19, Pin.OUT), # GP19
  Pin(20, Pin.OUT), # GP20
  Pin(21, Pin.OUT)  # GP21 through 220 Ohm resistor
]

buzz()
time.sleep(1)
beep()

def getGND(place):
  p = 0
  if place == 1:
    p = 0
  elif place == 2:
    p = 3
  elif place == 3:
    p = 4
  elif place == 4:
    p = 11
  else:
    return None
  return p

pin1digits = [0, 2, 3, 5, 6, 7, 8, 9]
pin2digits = [0, 4, 5, 6, 8, 9]
pin5digits = [0, 1, 2, 3, 4, 7, 8, 9]
pin6digits = [0, 2, 6, 8]
pin7digits = [0, 2, 3, 5, 6, 8]
pin9digits = [0, 1, 3, 4, 5, 6, 7, 8, 9]
pin10digits = [2, 3, 4, 5, 6, 8, 9]

# Draw two lines when timer is idle
def displayIdle():
  pin[3].off()
  pin[4].off()
  pin[10].on()

def display(digit,place):
  gnd = getGND(place)
  pin[gnd].off()
  if digit in pin1digits:
    pin[1].on()
  if digit in pin2digits:
    pin[2].on()
  if digit in pin5digits:
    pin[5].on()
  if digit in pin6digits:
    pin[6].on()
  if digit in pin7digits:
    pin[7].on()
  if digit in pin9digits:
    pin[9].on()
  if digit in pin10digits:
    pin[10].on()
  time.sleep(0.002)
  pin[1].off()
  pin[2].off()
  pin[5].off()
  pin[6].off()
  pin[7].off()
  pin[9].off()
  pin[10].off()  
  pin[gnd].on()

def cleanup():
  for i in [1,2,5,6,7,9,10]:
    pin[i].off()

def cleanupDigitPins():
    pin[0].on()
    pin[3].on()
    pin[4].on()
    pin[11].on()

cleanupDigitPins()

while True:
  minutes = countdown // 60
  seconds = countdown % 60

  # format minutes and seconds to 4 digits with leading zeros, 
  # for example 8 minutes and 5 seconds will be 0805
  displayString = &quot;{:02d}{:02d}&quot;.format(minutes, seconds)

  # display the time with digits
  # flicker the display 125 times per second to make it more visible
  if(countdown&gt;0):
    for flicker in range(1,125):
        for displayIndex in range(1, 5):
          digit = int(displayString[displayIndex - 1])
          display(digit, displayIndex)
    if(isActive):
      countdown -= 1
  else:
    displayIdle()

  if(countdown == 0):
    buzz()
    time.sleep(1)
    countdown = -1
</code></pre><p>I used <a href="https://thonny.org/?ref=codeof.me">Thonny IDE</a> to upload the code to Pico. Remember to name your file to main.py as then it will be automatically started even when Pico is not connected to computer or just powered with battery.</p><h2 id="finished">Finished</h2><p>Now it sits on my desk and can be activated easily for Pomodoro tasks :)</p><figure class="kg-card kg-video-card kg-width-wide kg-card-hascaption" data-kg-thumbnail="https://www.codeof.me/content/images/2023/01/media-thumbnail-ember216.jpg" data-kg-custom-thumbnail>
            <div class="kg-video-container">
                <video src="https://www.codeof.me/content/media/2023/01/Picomoro-demo-1.mp4" poster="https://img.spacergif.org/v1/512x288/0a/spacer.png" width="512" height="288" playsinline preload="metadata" style="background: transparent url(&apos;https://www.codeof.me/content/images/2023/01/media-thumbnail-ember216.jpg&apos;) 50% 50% / cover no-repeat;"></video>
                <div class="kg-video-overlay">
                    <button class="kg-video-large-play-icon" aria-label="Play video">
                        <svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24">
                            <path d="M23.14 10.608 2.253.164A1.559 1.559 0 0 0 0 1.557v20.887a1.558 1.558 0 0 0 2.253 1.392L23.14 13.393a1.557 1.557 0 0 0 0-2.785Z"/>
                        </svg>
                    </button>
                </div>
                <div class="kg-video-player-container">
                    <div class="kg-video-player">
                        <button class="kg-video-play-icon" aria-label="Play video">
                            <svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24">
                                <path d="M23.14 10.608 2.253.164A1.559 1.559 0 0 0 0 1.557v20.887a1.558 1.558 0 0 0 2.253 1.392L23.14 13.393a1.557 1.557 0 0 0 0-2.785Z"/>
                            </svg>
                        </button>
                        <button class="kg-video-pause-icon kg-video-hide" aria-label="Pause video">
                            <svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24">
                                <rect x="3" y="1" width="7" height="22" rx="1.5" ry="1.5"/>
                                <rect x="14" y="1" width="7" height="22" rx="1.5" ry="1.5"/>
                            </svg>
                        </button>
                        <span class="kg-video-current-time">0:00</span>
                        <div class="kg-video-time">
                            /<span class="kg-video-duration">0:14</span>
                        </div>
                        <input type="range" class="kg-video-seek-slider" max="100" value="0">
                        <button class="kg-video-playback-rate" aria-label="Adjust playback speed">1&#xD7;</button>
                        <button class="kg-video-unmute-icon" aria-label="Unmute">
                            <svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24">
                                <path d="M15.189 2.021a9.728 9.728 0 0 0-7.924 4.85.249.249 0 0 1-.221.133H5.25a3 3 0 0 0-3 3v2a3 3 0 0 0 3 3h1.794a.249.249 0 0 1 .221.133 9.73 9.73 0 0 0 7.924 4.85h.06a1 1 0 0 0 1-1V3.02a1 1 0 0 0-1.06-.998Z"/>
                            </svg>
                        </button>
                        <button class="kg-video-mute-icon kg-video-hide" aria-label="Mute">
                            <svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24">
                                <path d="M16.177 4.3a.248.248 0 0 0 .073-.176v-1.1a1 1 0 0 0-1.061-1 9.728 9.728 0 0 0-7.924 4.85.249.249 0 0 1-.221.133H5.25a3 3 0 0 0-3 3v2a3 3 0 0 0 3 3h.114a.251.251 0 0 0 .177-.073ZM23.707 1.706A1 1 0 0 0 22.293.292l-22 22a1 1 0 0 0 0 1.414l.009.009a1 1 0 0 0 1.405-.009l6.63-6.631A.251.251 0 0 1 8.515 17a.245.245 0 0 1 .177.075 10.081 10.081 0 0 0 6.5 2.92 1 1 0 0 0 1.061-1V9.266a.247.247 0 0 1 .073-.176Z"/>
                            </svg>
                        </button>
                        <input type="range" class="kg-video-volume-slider" max="100" value="100">
                    </div>
                </div>
            </div>
            <figcaption><p><span style="white-space: pre-wrap;">Picomoro in action</span></p></figcaption>
        </figure>]]></content:encoded></item><item><title><![CDATA[Developing Azure DevOps Pull Request Comments Task Extension]]></title><description><![CDATA[Created pipeline task extension that can add comments on Azure DevOps pull requests. Extension available through Marketplace. Open source.]]></description><link>https://www.codeof.me/azure-devops-pull-request-comments-task-extension/</link><guid isPermaLink="false">639e23ff84ea310001aa55bb</guid><category><![CDATA[Azure]]></category><category><![CDATA[DevOps]]></category><category><![CDATA[TypeScript]]></category><dc:creator><![CDATA[Tommi Laukkanen]]></dc:creator><pubDate>Sat, 07 Jan 2023 14:19:53 GMT</pubDate><media:content url="https://www.codeof.me/content/images/2022/12/code.png" medium="image"/><content:encoded><![CDATA[<img src="https://www.codeof.me/content/images/2022/12/code.png" alt="Developing Azure DevOps Pull Request Comments Task Extension"><p>TL;DR: Coded a small Azure DevOps Task extension that can be used to publish comments on Pull Requests. See and install extension from <a href="https://marketplace.visualstudio.com/items?itemName=TommiLaukkanen.pr-comment-extension&amp;ref=codeof.me">Marketplace</a> or browse the open-source code from GitHub <a href="https://github.com/tlaukkanen/azure-devops-pr-comment-extension?ref=codeof.me">azure-devops-pr-comment-extension</a>.</p><p>Task itself is easy to use in your pipeline yaml script:</p><pre><code class="language-yaml">- task: PullRequestComment@1
  inputs:
    comment: |
      This is **sample** _text_ &#x1F389;
      [This is link](https://microsoft.com)
      Build ID is $(Build.BuildId)
      | Table |
      |---|
      | Cell |</code></pre><p>The example shows that you can use multiline Markdown formatting as well as all variables that you may have available in the pipeline. When task executes, it will show up as a comment like this in your pull request comments:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.codeof.me/content/images/2022/12/image-3.png" class="kg-image" alt="Developing Azure DevOps Pull Request Comments Task Extension" loading="lazy" width="542" height="319"><figcaption>Comment in Azure DevOps pull request created by <strong>PR Comment Task</strong> extension</figcaption></figure><h2 id="how-to-create-azure-devops-extensions">How to create Azure DevOps extensions?</h2><p>It&apos;s easy to create new extensions as Microsoft provides npx tooling to create baseline for your extension. Extension consists of main three parts:</p><ol><li>Extension manifest file (<a href="https://learn.microsoft.com/en-us/azure/devops/extend/develop/manifest?view=azure-devops&amp;ref=codeof.me">vss-extension.json</a>)</li><li>Discovery assets (screenshots, markdown), things you see in Marketplace</li><li>Static files, like in this case build task Typescript code</li></ol><p>Following tutorials show this quite well so I don&apos;t duplicate them here:</p><ul><li><a href="https://learn.microsoft.com/en-us/azure/devops/extend/overview?view=azure-devops&amp;ref=codeof.me">Extensions overview - Azure DevOps | Microsoft Learn</a></li><li><a href="https://learn.microsoft.com/en-us/azure/devops/extend/develop/add-build-task?view=azure-devops&amp;ref=codeof.me">Add a build or release task in an extension - Azure DevOps | Microsoft Learn</a></li><li><a href="https://learn.microsoft.com/en-us/azure/devops/extend/develop/integrate-build-task?view=azure-devops&amp;ref=codeof.me">Custom build/release task reference - Azure DevOps | Microsoft Learn</a></li></ul><h2 id="how-to-add-comment-on-pr-in-your-extension-with-typescript">How to add comment on PR in your extension with TypeScript?</h2><p>So how to utilize and interact with Azure DevOps objects? Microsoft provides a couple of handy modules that make it easy to interact with the Azure DevOps API and especially Git API for Pull Request commenting. So, install these to your task script:</p><ul><li>npm install <a href="https://www.npmjs.com/package/azure-pipelines-task-lib?ref=codeof.me">azure-pipelines-task-lib</a></li><li>npm install <a href="https://www.npmjs.com/package/azure-devops-node-api?ref=codeof.me">azure-devops-node-api</a></li></ul><p>After this the comment thread can be created with the following task code:</p><pre><code class="language-typescript">import tl = require(&apos;azure-pipelines-task-lib/task&apos;)
import azdev = require(&apos;azure-devops-node-api&apos;)
import { CommentThreadStatus, CommentType } from &apos;azure-devops-node-api/interfaces/GitInterfaces&apos;

async function run() {
  try{
    const comment: string | undefined = tl.getInput(&apos;comment&apos;, true)
    if(comment == &apos;&apos; || comment == undefined) {
      console.log(`Empty comment given - skipping PR comment`)
      return
    }
    const pullRequestId = parseInt(tl.getVariable(&apos;System.PullRequest.PullRequestId&apos;) ?? &apos;-1&apos;)
    if(pullRequestId &lt; 0 ) {
      console.log(`No pull request id - skipping PR comment`)
      return
    }
    
    // This is especially nice for task extensions as you
    // don&apos;t need to pass access tokens as parameters but you can get
    // the access token to az api with just the following:
    const accessToken = tl.getEndpointAuthorizationParameter(&apos;SystemVssConnection&apos;, &apos;AccessToken&apos;, false) ?? &apos;&apos;
    const authHandler = azdev.getPersonalAccessTokenHandler(accessToken) ?? &apos;&apos;
    
    // All the system/build variables can be accessed in similar
    // way as you access those from pipelines
    const collectionUri = tl.getVariable(&apos;System.CollectionUri&apos;) ?? &apos;&apos;
    const repositoryId = tl.getVariable(&apos;Build.Repository.ID&apos;) ?? &apos;&apos;
    const connection = new azdev.WebApi(collectionUri, authHandler)
    const gitApi = await connection.getGitApi()
    
    // Thread and comment schema wasn&apos;t explained well anywhere
    // but this minimalistic version was constructed from various
    // Stackoverflow posts
    const thread : any = {
      comments: [{
        commentType: CommentType.Text,
        content: comment,
      }],
      lastUpdatedDate: new Date(),
      publishedDate: new Date(),
      status: CommentThreadStatus.Closed,
    }
    const t = await gitApi.createThread(thread, repositoryId, pullRequestId)
    
    // Console logging shows up nicely on the actual build log
    console.log(`Comment added on pull request: ${comment}`)
  }
  catch (err:any) {
    tl.setResult(tl.TaskResult.Failed, err.message)
  }
}

run()</code></pre>]]></content:encoded></item><item><title><![CDATA[Futuristic AI Camera]]></title><description><![CDATA[<p>Designed and built a futuristic looking camera case for Raspberry Pi Zero. The original idea was to have a camera to monitor my 3D builds and create timelapse shots of various things.</p><figure class="kg-card kg-image-card kg-width-full"><img src="https://www.codeof.me/content/images/2022/12/20211130_095423-edit-20221218130350_edited_pastel.jpg" class="kg-image" alt loading="lazy" width="2000" height="1125" srcset="https://www.codeof.me/content/images/size/w600/2022/12/20211130_095423-edit-20221218130350_edited_pastel.jpg 600w, https://www.codeof.me/content/images/size/w1000/2022/12/20211130_095423-edit-20221218130350_edited_pastel.jpg 1000w, https://www.codeof.me/content/images/size/w1600/2022/12/20211130_095423-edit-20221218130350_edited_pastel.jpg 1600w, https://www.codeof.me/content/images/size/w2400/2022/12/20211130_095423-edit-20221218130350_edited_pastel.jpg 2400w"></figure><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.codeof.me/content/images/2022/12/20221218_124940_edited_basic_pastel.jpg" class="kg-image" alt loading="lazy" width="2000" height="1000" srcset="https://www.codeof.me/content/images/size/w600/2022/12/20221218_124940_edited_basic_pastel.jpg 600w, https://www.codeof.me/content/images/size/w1000/2022/12/20221218_124940_edited_basic_pastel.jpg 1000w, https://www.codeof.me/content/images/size/w1600/2022/12/20221218_124940_edited_basic_pastel.jpg 1600w, https://www.codeof.me/content/images/2022/12/20221218_124940_edited_basic_pastel.jpg 2273w" sizes="(min-width: 720px) 720px"><figcaption>The final build of Futuristic AI Camera</figcaption></figure><h2 id="bill-of-material">Bill of Material</h2><p>The bill of material for the components is</p>]]></description><link>https://www.codeof.me/futuristic-ai-camera/</link><guid isPermaLink="false">639eecef84ea310001aa55e4</guid><dc:creator><![CDATA[Tommi Laukkanen]]></dc:creator><pubDate>Sun, 18 Dec 2022 12:11:37 GMT</pubDate><media:content url="https://www.codeof.me/content/images/2022/12/Camera_2021-Nov-06_09-19-37AM-000_CustomizedView16200451835_edited_pastel.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://www.codeof.me/content/images/2022/12/Camera_2021-Nov-06_09-19-37AM-000_CustomizedView16200451835_edited_pastel.jpg" alt="Futuristic AI Camera"><p>Designed and built a futuristic looking camera case for Raspberry Pi Zero. The original idea was to have a camera to monitor my 3D builds and create timelapse shots of various things.</p><figure class="kg-card kg-image-card kg-width-full"><img src="https://www.codeof.me/content/images/2022/12/20211130_095423-edit-20221218130350_edited_pastel.jpg" class="kg-image" alt="Futuristic AI Camera" loading="lazy" width="2000" height="1125" srcset="https://www.codeof.me/content/images/size/w600/2022/12/20211130_095423-edit-20221218130350_edited_pastel.jpg 600w, https://www.codeof.me/content/images/size/w1000/2022/12/20211130_095423-edit-20221218130350_edited_pastel.jpg 1000w, https://www.codeof.me/content/images/size/w1600/2022/12/20211130_095423-edit-20221218130350_edited_pastel.jpg 1600w, https://www.codeof.me/content/images/size/w2400/2022/12/20211130_095423-edit-20221218130350_edited_pastel.jpg 2400w"></figure><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.codeof.me/content/images/2022/12/20221218_124940_edited_basic_pastel.jpg" class="kg-image" alt="Futuristic AI Camera" loading="lazy" width="2000" height="1000" srcset="https://www.codeof.me/content/images/size/w600/2022/12/20221218_124940_edited_basic_pastel.jpg 600w, https://www.codeof.me/content/images/size/w1000/2022/12/20221218_124940_edited_basic_pastel.jpg 1000w, https://www.codeof.me/content/images/size/w1600/2022/12/20221218_124940_edited_basic_pastel.jpg 1600w, https://www.codeof.me/content/images/2022/12/20221218_124940_edited_basic_pastel.jpg 2273w" sizes="(min-width: 720px) 720px"><figcaption>The final build of Futuristic AI Camera</figcaption></figure><h2 id="bill-of-material">Bill of Material</h2><p>The bill of material for the components is the following:</p><ul><li>3D printed case</li><li>Raspberry Pi Zero</li><li><a href="https://www.waveshare.com/li-ion-battery-hat.htm?ref=codeof.me">Waveshare Li-ion Battery hat</a></li><li><a href="https://www.raspberrypi.com/products/camera-module-v2/?ref=codeof.me">Camera Module</a> with cable compatible with RPi Zero</li><li>Two push buttons, I used <a href="https://www.amazon.de/-/en/RUNCCI-YUN-Momentary-Tactile-12x12x7-3mm-Surface/dp/B07WPBQXJ9/ref=sr_1_27?crid=FKHUHW8I2TQH&amp;keywords=klingelknopf&amp;qid=1671363469&amp;sprefix=push+button%2Caps%2C122&amp;sr=8-27&amp;ref=codeof.me">these</a> from Amazon.de</li><li><a href="https://thepihut.com/products/1-5-oled-display-module?ref=codeof.me">1.5&quot; OLED</a> display or similar</li><li>Round 8x1 mm magnets for back cover, I used <a href="https://www.amazon.de/-/en/Magnets-Strong-Permanent-Whiteboard-Magnetic/dp/B0B6VXQJGP/ref=sr_1_6?crid=FKHUHW8I2TQH&amp;keywords=magnete+rund+8x1+mm&amp;qid=1671364450&amp;sprefix=push+button%2Caps%2C122&amp;sr=8-6&amp;ref=codeof.me">these</a> from Amazon.de</li></ul><h2 id="3d-printed-case">3D Printed Case</h2><p>3D case modeling was done with Fusion 360 and the final design has six different components.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.codeof.me/content/images/2022/12/2022-12-18-13_16_24-Autodesk-Fusion-360--Personal---Not-for-Commercial-Use-_edited.jpg" class="kg-image" alt="Futuristic AI Camera" loading="lazy" width="1727" height="865" srcset="https://www.codeof.me/content/images/size/w600/2022/12/2022-12-18-13_16_24-Autodesk-Fusion-360--Personal---Not-for-Commercial-Use-_edited.jpg 600w, https://www.codeof.me/content/images/size/w1000/2022/12/2022-12-18-13_16_24-Autodesk-Fusion-360--Personal---Not-for-Commercial-Use-_edited.jpg 1000w, https://www.codeof.me/content/images/size/w1600/2022/12/2022-12-18-13_16_24-Autodesk-Fusion-360--Personal---Not-for-Commercial-Use-_edited.jpg 1600w, https://www.codeof.me/content/images/2022/12/2022-12-18-13_16_24-Autodesk-Fusion-360--Personal---Not-for-Commercial-Use-_edited.jpg 1727w" sizes="(min-width: 720px) 720px"><figcaption>3D design for camera case components</figcaption></figure><p>I published the model&apos;s .STL files Creative Commons license and those can be downloaded from Printables.com, <a href="https://www.printables.com/model/342925-futuristic-smart-camera?ref=codeof.me">here</a>.</p><h2 id="the-code">The Code</h2><p>There are many good tutorials on how to use Python for taking pictures when pressing button. The possibilities on what you do with your picture files after that are endless - apply AI magic to them or just post them directly to your Telegram etc. Couple of tutorials to get you started:</p><ul><li><a href="https://github.com/raspberrypilearning/python-picamera-setup/blob/master/worksheet.md?ref=codeof.me">python-picamera-setup/worksheet.md at master &#xB7; raspberrypilearning/python-picamera-setup (github.com)</a></li><li><a href="https://www.techcoil.com/blog/building-a-raspberry-pi-3-prototype-camera-that-takes-a-picture-at-the-press-of-a-button/?ref=codeof.me">Building a Raspberry Pi 3 prototype camera that takes a picture at the press of a button (techcoil.com)</a></li></ul>]]></content:encoded></item><item><title><![CDATA[How to export data from Azure SQL to local dev database]]></title><description><![CDATA[<p>You might have had a situation where you&apos;d like to replicate a specific functionality locally that you have in your application running in Azure with same Azure SQL Database state. This tutorial explains how you can use <a href="https://azure.microsoft.com/en-us/products/data-studio/?ref=codeof.me">Azure Data Studio</a> and <a href="https://learn.microsoft.com/en-us/sql/azure-data-studio/extensions/sql-server-dacpac-extension?view=sql-server-ver16&amp;ref=codeof.me">SQL Server Dacpac extension</a> to export and</p>]]></description><link>https://www.codeof.me/how-to-export-data-from-azure-sql-to-local-dev-database/</link><guid isPermaLink="false">639dbe4b84ea310001aa5550</guid><category><![CDATA[Azure]]></category><dc:creator><![CDATA[Tommi Laukkanen]]></dc:creator><pubDate>Sat, 17 Dec 2022 13:24:06 GMT</pubDate><content:encoded><![CDATA[<p>You might have had a situation where you&apos;d like to replicate a specific functionality locally that you have in your application running in Azure with same Azure SQL Database state. This tutorial explains how you can use <a href="https://azure.microsoft.com/en-us/products/data-studio/?ref=codeof.me">Azure Data Studio</a> and <a href="https://learn.microsoft.com/en-us/sql/azure-data-studio/extensions/sql-server-dacpac-extension?view=sql-server-ver16&amp;ref=codeof.me">SQL Server Dacpac extension</a> to export and import your data and schema from Azure SQL to local dev machine.</p><h2 id="grant-access-in-azure-sql-server-firewall">Grant access in Azure SQL Server Firewall</h2><p>There are many options for exporting data but one of the easiest is to use free Azure Data Studio to both export and import data. First, you&apos;ll need to enable connection from your local machine to Azure SQL Server. You can do this from Azure Portal:</p><ul><li>Navigate to your Azure SQL Database</li><li>Navigate to Firewall rules</li></ul><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.codeof.me/content/images/2022/12/image.png" class="kg-image" alt loading="lazy" width="500" height="193"><figcaption><span style="white-space: pre-wrap;">Update: Portal keeps updating so firewall might also be accessed via </span><b><strong style="white-space: pre-wrap;">Network</strong></b><span style="white-space: pre-wrap;"> panel</span></figcaption></figure><ul><li>Add your local machine&apos;s public IP address as new rule</li></ul><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.codeof.me/content/images/2022/12/image-1.png" class="kg-image" alt loading="lazy" width="500" height="189"><figcaption><span style="white-space: pre-wrap;">You can check your IP address using service like https://whatismyip.com</span></figcaption></figure><p><strong>Note:</strong> For good security - remember to remove this rule when you don&apos;t need the access anymore</p><p>Now you should be able to connect to your Azure SQL database with Azure Data Studio. You can check your connection string from the Azure SQL Server overview page.</p><p>Disclaimer note: Your Azure setup might have other firewall rules that could prevent from you from connecting directly to your SQL Server so this might not be suitable for all setups - especially more hardened production environments.</p><h2 id="exporting-and-importing-data-and-schema">Exporting and importing data and schema</h2><p>We can&apos;t use normal backup/restore feature as that&apos;s not supported by Azure SQL Server (as of 2022) but we can do the following:</p><p>Install <a href="https://learn.microsoft.com/en-us/sql/azure-data-studio/extensions/sql-server-dacpac-extension?view=sql-server-ver16&amp;ref=codeof.me">SQL Server Dacpac extension</a> to Azure Data Studio - this can be done easiest from Data Studio&apos;s &quot;Extensions&quot; view. After extension is installed:</p><ul><li><strong>Export:</strong> click &quot;<em>Data-tier Application wizard</em>&quot; for your Azure SQL DB and select the &quot;<em>Export Bacpac</em>&quot; option</li><li><strong>Import:</strong> When export finished then import to your local database with &quot;<em>Import Bacpac</em>&quot; option</li></ul><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.codeof.me/content/images/2022/12/image-2.png" class="kg-image" alt loading="lazy" width="901" height="389" srcset="https://www.codeof.me/content/images/size/w600/2022/12/image-2.png 600w, https://www.codeof.me/content/images/2022/12/image-2.png 901w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">Export and import options in Dacpac extension&apos;s Data-tier Application Wizard</span></figcaption></figure><p>Done - now we have your data in your local database and we are ready to debug our app with the same data.</p>]]></content:encoded></item></channel></rss>