<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet href="./css/pretty-atom-feed.xsl" type="text/xsl"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
  <title>Forrest O. Interactive</title>
  <subtitle>Forrest O.&#39;s web blog wiki garden home</subtitle>
  <link href="https://forresto.com/feed.xml" rel="self" />
  <link href="https://forresto.com/" />
  <updated>2025-06-10T00:00:00Z</updated>
  <id>https://forresto.com/</id>
  <author>
    <name>Forrest O.</name>
  </author>
  <entry>
    <title>Tactile Labyrinth</title>
    <link href="https://forresto.com/fab-academy/tactile-labyrinth.html" />
    <updated>2025-06-10T00:00:00Z</updated>
    <id>https://forresto.com/fab-academy/tactile-labyrinth.html</id>
    <content type="html">&lt;h1 id=&quot;presentation&quot; tabindex=&quot;-1&quot;&gt;Presentation&lt;/h1&gt;
&lt;link rel=&quot;stylesheet&quot; href=&quot;https://cdn.jsdelivr.net/gh/paulirish/lite-youtube-embed@0.3.3/src/lite-yt-embed.min.css&quot;&gt;
&lt;script defer=&quot;defer&quot; src=&quot;https://cdn.jsdelivr.net/gh/paulirish/lite-youtube-embed@0.3.3/src/lite-yt-embed.min.js&quot;&gt;&lt;/script&gt;
&lt;div id=&quot;Z1EnZgib5fY&quot; class=&quot;eleventy-plugin-youtube-embed&quot;&gt;&lt;lite-youtube videoid=&quot;Z1EnZgib5fY&quot; style=&quot;background-image: url(&#39;https://i.ytimg.com/vi/Z1EnZgib5fY/hqdefault.jpg&#39;);&quot; params=&quot;rel=0&quot;&gt;&lt;div class=&quot;lty-playbtn&quot;&gt;&lt;/div&gt;&lt;/lite-youtube&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://fabacademy.org/2025/labs/aalto/students/forrest-oliphant/presentation.mp4&quot;&gt;presentation.mp4&lt;/a&gt; – on the fabacademy.org archive&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=Z1EnZgib5fY&quot;&gt;presentation video mirror&lt;/a&gt; – on youtube&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&quot;inspiration&quot; tabindex=&quot;-1&quot;&gt;Inspiration&lt;/h1&gt;
&lt;p&gt;One definition of &lt;em&gt;labyrinth&lt;/em&gt; is a winding path with no branches, so if you keep walking in one direction, you cover the whole path and end up back where you started.&lt;/p&gt;
&lt;p&gt;I like this aesthetic, and think it would be nice as an object designed for touching.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Noomorph&lt;/em&gt; is a made-up word, consisting of &lt;em&gt;noo&lt;/em&gt; (thought) and &lt;em&gt;morph&lt;/em&gt; (shape). So &lt;em&gt;noomorphology&lt;/em&gt; could be a design process that gives shape to thought.&lt;/p&gt;
&lt;h1 id=&quot;evaluation-questions&quot; tabindex=&quot;-1&quot;&gt;Evaluation questions&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;Document a final project masterpiece that integrates the range of units covered, answering:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;what-does-it-do%3F&quot; tabindex=&quot;-1&quot;&gt;What does it do?&lt;/h2&gt;
&lt;p&gt;The tactile labyrinth is a 3D path around a ball that can be traced and followed with the hands. Lights along the path are programmed with animations that react to the motion of the ball.&lt;/p&gt;
&lt;h2 id=&quot;who%E2%80%99s-done-what-beforehand%3F&quot; tabindex=&quot;-1&quot;&gt;Who’s done what beforehand?&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Michael McGinnis: &lt;a href=&quot;http://superplexus.com/history/perplexus-1/&quot;&gt;Perplexus&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Nervous System: &lt;a href=&quot;https://n-e-r-v-o-u-s.com/blog/?p=8222&quot;&gt;coral cup&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Carl Bugeja: &lt;a href=&quot;https://www.youtube.com/watch?v=Jjg6-xT6rJg&quot;&gt;Building a Mini Vegas-Sphere&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Ancient: &lt;a href=&quot;https://www.youtube.com/watch?v=zz59e1wWyVc&quot;&gt;The Fibrovisor&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://forresto.com/media-cache/labyrinth-mood-board.jpg-NXNN21T84K-1280.avif 1280w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://forresto.com/media-cache/labyrinth-mood-board.jpg-NXNN21T84K-1280.webp 1280w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://forresto.com/media-cache/labyrinth-mood-board.jpg-NXNN21T84K-1280.jpeg&quot; alt=&quot;Labyrinth mood board&quot; width=&quot;1280&quot; height=&quot;1120&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.tldraw.com/f/z-a5JhkqRlBK2VdWFM7kk?d=v-846.-1264.7325.5131.IqdinIZI6i_zIJB1pRpjk&quot;&gt;Mood board&lt;/a&gt; – interactive version with video embeds and links.&lt;/p&gt;
&lt;h2 id=&quot;what-did-you-design%3F&quot; tabindex=&quot;-1&quot;&gt;What did you design?&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Parametric design app that simulates a tiny planet and rope loop with self-collisions and gravity.&lt;/li&gt;
&lt;li&gt;Once the path is solved, sockets for the LED wires are added, and holes are subtracted, using Boolean operations. The sockets are oriented towards the center of the ball.&lt;/li&gt;
&lt;li&gt;The app exports positive and negative space STLs that are combined in Blender or Prusa Slicer before 3D printing.&lt;/li&gt;
&lt;li&gt;The parts snap together securely, mostly without glue.&lt;/li&gt;
&lt;li&gt;The LEDs are inserted into each socket. The wire loops through the inside of the ball.&lt;/li&gt;
&lt;li&gt;A platform sits between the halves of the ball. It was designed to keep the accelerometer at the center of the ball, and the battery close to the center of gravity.&lt;/li&gt;
&lt;li&gt;Arduino code interfaces Adafruit accelerometer and FastLED libraries.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;what-sources-did-you-use%3F&quot; tabindex=&quot;-1&quot;&gt;What sources did you use?&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/@3DSinghVFX&quot;&gt;3D Singh VFX&lt;/a&gt; Blender Geometry Nodes tutorials&lt;/li&gt;
&lt;li&gt;Claude Sonnet 3.5, 3.7, 4 via &lt;a href=&quot;https://forresto.com/tags/readme.html&quot;&gt;readme-based development&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Adafruit accelerometer library&lt;/li&gt;
&lt;li&gt;FastLED library, especially the &lt;a href=&quot;https://github.com/FastLED/FastLED/wiki/Pixel-reference#setting-hsv-colors-&quot;&gt;&amp;quot;rainbow&amp;quot; balanced color functions&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;what-materials-and-components-were-used%3F&quot; tabindex=&quot;-1&quot;&gt;What materials and components were used?&lt;/h2&gt;
&lt;p&gt;Where did they come from?&lt;/p&gt;
&lt;p&gt;How much did they cost?&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;maker and part&lt;/th&gt;
&lt;th&gt;part number&lt;/th&gt;
&lt;th&gt;source&lt;/th&gt;
&lt;th&gt;cost&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://wiki.seeedstudio.com/XIAO-RP2040/&quot;&gt;Seeed Studio XIAO RP2040&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;102010428&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://www.digikey.com/en/products/detail/seeed-technology-co-ltd/102010428/14672129&quot;&gt;Digikey&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;$4.68&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://www.adafruit.com/product/4438&quot;&gt;Adafruit LSM6DSOX 6 DoF Accelerometer and Gyroscope - STEMMA QT / Qwiic&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;4438&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://www.digikey.com/en/products/detail/adafruit-industries-llc/4438/11497501&quot;&gt;Digikey&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;$11.95&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Texas Instruments 3 V to 5 V Level Shifter&lt;/td&gt;
&lt;td&gt;TXB0104DR&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://www.digikey.com/en/products/detail/texas-instruments/TXB0104DR/1629101&quot;&gt;Digikey&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;$0.92&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Horizontal SMD 2.54mm pitch pin socket connector (01x04) ($0.67 * 3)&lt;/td&gt;
&lt;td&gt;BG300-04-A-L-A&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://www.digikey.com/en/products/detail/gct/BG300-04-A-L-A/9859595&quot;&gt;Digikey&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;$2.01&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Horizontal SMD 2.54mm pitch pin socket connector (01x03)&lt;/td&gt;
&lt;td&gt;BG300-03-A-L-A&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://www.digikey.com/en/products/detail/gct/BG300-03-A-L-A/9859594&quot;&gt;Digikey&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;$0.51&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7 Position vertical header ($1.08 * 2)&lt;/td&gt;
&lt;td&gt;NPTC071KFXC-RC&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://www.digikey.com/en/products/detail/sullins-connector-solutions/NPTC081KFXC-RC/776058&quot;&gt;Digikey&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;$2.16&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Omron push button switch&lt;/td&gt;
&lt;td&gt;B3SN-3012&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://www.digikey.com/en/products/detail/omron-electronics-inc-emc-div/B3SN-3012/1811776&quot;&gt;Digikey&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;$0.71&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;YAGEO 4.99 kOhms resistor 1206 ($0.10 * 2)&lt;/td&gt;
&lt;td&gt;RC1206FR-074K99L&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://www.digikey.com/en/products/detail/yageo/RC1206FR-074K99L/728896&quot;&gt;Digikey&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;$0.20&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TDK Corporation 0.1 µF Ceramic Capacitor 1206 ($0.23 * 2)&lt;/td&gt;
&lt;td&gt;C3216X7R2A104K160AA&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://www.digikey.com/en/products/detail/tdk-corporation/C3216X7R2A104K160AA/513967&quot;&gt;Digikey&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;$0.46&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bantam Proto Board Copper Clad FR1, Single Sided ($67.53/25)&lt;/td&gt;
&lt;td&gt;MT1004&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://www.digikey.fi/en/products/detail/bantam-tools/MT1004/8567212&quot;&gt;Digikey&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;$2.70&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://www.btf-lighting.com/products/ws2812b-led-string-bt-music-control-for-party-christmas-lights-decoration-room-birthday-decor-led-light-outdoor-waterproof-ip67-dc5v?variant=45666708127970&quot;&gt;BTF-Lighting WS2812B 10m / 100 LED String&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;btf-5v-100l-t&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://www.amazon.com/BTF-LIGHTING-Individual-Addressable-Christmas-Controller/dp/B01DC0ITX4/&quot;&gt;Amazon&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;$16.99&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://filalab.shop/product/pctg-filaments/?attribute_pa_weight=1-kg&amp;amp;attribute_pa_diameter=1-75-mm&amp;amp;attribute_pa_color=transparent-no-ral&amp;amp;variation_id=9742&amp;amp;add-to-cart=9741&quot;&gt;Filalab transparent PCTG filament&lt;/a&gt; ($28.50/kg, 537g)&lt;/td&gt;
&lt;td&gt;4779051441044&lt;/td&gt;
&lt;td&gt;Filalab&lt;/td&gt;
&lt;td&gt;$15.30&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://filalab.shop/product/pla-filaments/?attribute_pa_weight=1-kg&amp;amp;attribute_pa_diameter=1-75-mm&amp;amp;attribute_pa_color=black&quot;&gt;Filalab Galaxy Blue PLA filament&lt;/a&gt; ($22.80/kg, 351g)&lt;/td&gt;
&lt;td&gt;4779051440993&lt;/td&gt;
&lt;td&gt;Filalab&lt;/td&gt;
&lt;td&gt;$8.00&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://www.clasohlson.com/fi/Powerbank-Plug-In-Varavirtalahde-USB-C,-5000-mAh/p/39-3976-1&quot;&gt;Clas Ohlson 5000mah battery bank&lt;/a&gt; (generic versions of the product exist)&lt;/td&gt;
&lt;td&gt;39-3976-1&lt;/td&gt;
&lt;td&gt;Clas Ohlson&lt;/td&gt;
&lt;td&gt;$28.46&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Baltic birch plywood, 3mm (128mm square, from offcut)&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Fablab&lt;/td&gt;
&lt;td&gt;$2.00&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;Total:&lt;/strong&gt; $97.05 USD (€85.06 EUR)&lt;/p&gt;
&lt;h2 id=&quot;what-parts-and-systems-were-made%3F&quot; tabindex=&quot;-1&quot;&gt;What parts and systems were made?&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;3D printed parts for the inner case (opaque) and LED path (transparent)&lt;/li&gt;
&lt;li&gt;Laser cut mounting platform for circuit boards&lt;/li&gt;
&lt;li&gt;Custom circuit board&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;what-processes-were-used%3F&quot; tabindex=&quot;-1&quot;&gt;What processes were used?&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;3D printing&lt;/li&gt;
&lt;li&gt;Laser cutting&lt;/li&gt;
&lt;li&gt;CNC milling for the circuit board&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;what-questions-were-answered%3F&quot; tabindex=&quot;-1&quot;&gt;What questions were answered?&lt;/h2&gt;
&lt;p&gt;Can I make a path from a physics simulation, embed addressable LEDs into the path, and program a microcontroller to make interactive color animations on the object? Yes, I can do those things now.&lt;/p&gt;
&lt;h2 id=&quot;what-worked%3F-what-didn&#39;t%3F&quot; tabindex=&quot;-1&quot;&gt;What worked? What didn&#39;t?&lt;/h2&gt;
&lt;p&gt;See &lt;a href=&quot;https://forresto.com/fab-academy/tactile-labyrinth.html#rip-led-index-0&quot;&gt;LED strip drama&lt;/a&gt; in the build log for something that did not work. Also the first attempt at soldering the circuit board (&lt;a href=&quot;https://forresto.com/fab-academy/08-electronics-production.html&quot;&gt;Week 08&lt;/a&gt;) did not go well.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://forresto.com/fab-academy/tactile-labyrinth.html#3d-print-snap-together&quot;&gt;fit between in the inner outer parts&lt;/a&gt; worked better than expected. I thought it would require more iteration, but the first version worked as the final.&lt;/p&gt;
&lt;p&gt;Otherwise, things worked out pretty well.&lt;/p&gt;
&lt;h2 id=&quot;how-was-it-evaluated%3F&quot; tabindex=&quot;-1&quot;&gt;How was it evaluated?&lt;/h2&gt;
&lt;p&gt;Local and global evaluators for my weekly documentation.&lt;/p&gt;
&lt;p&gt;Friends and family birthday party, play-testing with folks young and old. Their hands are in my presentation video.&lt;/p&gt;
&lt;p&gt;Helsinki Media Lab Demo Day, passing around the labyrinth and speaking with people involved in new media art. It was nice to run into former teachers and fellow students, and to just place the object into their hands to see their reaction. One described the gameplay as &amp;quot;homeopathic&amp;quot; which I appreciated.&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://forresto.com/media-cache/labyrinth-demo-day.jpg-t-VLyv7NP5-1280.avif 1280w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://forresto.com/media-cache/labyrinth-demo-day.jpg-t-VLyv7NP5-1280.webp 1280w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://forresto.com/media-cache/labyrinth-demo-day.jpg-t-VLyv7NP5-1280.jpeg&quot; alt=&quot;At media lab demo day.&quot; width=&quot;1280&quot; height=&quot;960&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;At demo day. Playful spontanious combination with a 360° audio installation.&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://forresto.com/media-cache/presentation-reactions.png-_NxzbmQd2--1280.avif 1280w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://forresto.com/media-cache/presentation-reactions.png-_NxzbmQd2--1280.webp 1280w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://forresto.com/media-cache/presentation-reactions.png-_NxzbmQd2--1280.png&quot; alt=&quot;Zoom comments from Fab Academy students and instructors.&quot; width=&quot;1280&quot; height=&quot;748&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;Fab Academy final presentation comments. Thank you all. 😊&lt;/p&gt;
&lt;p&gt;Neil suggested that I add more about the &lt;a href=&quot;https://forresto.com/fab-academy/tactile-labyrinth.html#simulation&quot;&gt;physics simulation&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This version is 180mm in diameter and 800g. In my &lt;a href=&quot;https://forresto.com/fab-academy/tactile-labyrinth.html#next-steps&quot;&gt;next steps&lt;/a&gt; I have been thinking that a target for the next version would be half the size and weight, to be easier to pick up, transport, and manipulate. But at least two play-testers said that they appreciate the size and weight as it is. It certainly feels substantial.&lt;/p&gt;
&lt;h3 id=&quot;social-media&quot; tabindex=&quot;-1&quot;&gt;Social media&lt;/h3&gt;
&lt;p&gt;To share with the wider web I posted my presentation on &lt;a href=&quot;https://www.youtube.com/watch?v=Z1EnZgib5fY&quot;&gt;YouTube&lt;/a&gt;, &lt;a href=&quot;https://xoxo.zone/@forresto/114653035701838353&quot;&gt;Mastodon&lt;/a&gt;, &lt;a href=&quot;https://bsky.app/profile/forresto.bsky.social/post/3lr67rratgk2g&quot;&gt;Bluesky&lt;/a&gt;, &lt;a href=&quot;https://www.instagram.com/reel/DKupGlcxIws/&quot;&gt;Instagram&lt;/a&gt;, and linked back to here. I didn&#39;t get a lot of feedback on those sites. I think it&#39;s more interesting in person... considering the title of the project, that makes sense.&lt;/p&gt;
&lt;h2 id=&quot;what-are-the-implications%3F&quot; tabindex=&quot;-1&quot;&gt;What are the implications?&lt;/h2&gt;
&lt;p&gt;My goal in joining Fab Academy was to expand the tools and processes in my digital fabrication toolbox. Coming in to the class, I was comfortable with 2D design and laser cutting. Taking an earlier project that was static and 2D, and making it interactive and 3D, is a good reflection of all that I have learned in this class.&lt;/p&gt;
&lt;hr&gt;
&lt;hr&gt;
&lt;h1 id=&quot;build-log&quot; tabindex=&quot;-1&quot;&gt;Build Log&lt;/h1&gt;
&lt;p&gt;The rest of this page was updated through the course as I worked on the labyrinth.&lt;/p&gt;
&lt;h1 id=&quot;previous-work&quot; tabindex=&quot;-1&quot;&gt;Previous work&lt;/h1&gt;
&lt;pre style=&quot;line-height: 0.75; overflow-y: hidden;&quot;&gt;&lt;code&gt;╔══════╗╔╗╔╗╔════╗
╚═╗╔══╗║║║║║║╔═══╝
╔╗║║╔╗║╚╝╚╝║║╚═╗╔╗
║║╚╝║║║╔═══╝║╔═╝║║
║╚══╝║║╚═╗╔═╝║╔╗║║
╚═╗╔╗║╚═╗║║╔═╝║║║║
╔═╝║║║╔╗║║║╚══╝╚╝║
╚══╝║║║║║║║╔════╗║
╔╗╔═╝╚╝╚╝╚╝╚═══╗║║
║║╚═══╗╔╗╔╗╔╗╔═╝╚╝
║╚════╝║║║║║║║╔══╗
║╔╗╔══╗║║║╚╝║║║╔═╝
║║║║╔═╝║║╚═╗║╚╝╚═╗
║║╚╝║╔═╝╚═╗║║╔══╗║
║║╔═╝║╔═══╝║║║╔╗║║
╚╝╚═╗║║╔╗╔╗║╚╝║║╚╝
╔═══╝║║║║║║╚══╝╚═╗
╚════╝╚╝╚╝╚══════╝&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href=&quot;https://observablehq.com/@forresto/labyrinth&quot;&gt;&amp;quot;Wave Form Collapse&amp;quot; seeded random labyrinth&lt;/a&gt;, 2018.&lt;/p&gt;
&lt;div id=&quot;2e4LkK2QA2Q&quot; class=&quot;eleventy-plugin-youtube-embed&quot;&gt;&lt;lite-youtube videoid=&quot;2e4LkK2QA2Q&quot; style=&quot;background-image: url(&#39;https://i.ytimg.com/vi/2e4LkK2QA2Q/hqdefault.jpg&#39;);&quot; params=&quot;rel=0&quot;&gt;&lt;div class=&quot;lty-playbtn&quot;&gt;&lt;/div&gt;&lt;/lite-youtube&gt;&lt;/div&gt;
&lt;p&gt;&amp;quot;Folding powers of 2&amp;quot; labyrinth, pen plotting, 2021.&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://forresto.com/media-cache/03-roland-calibration-good.jpg-bMFw_wHVbw-1280.avif 1280w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://forresto.com/media-cache/03-roland-calibration-good.jpg-bMFw_wHVbw-1280.webp 1280w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://forresto.com/media-cache/03-roland-calibration-good.jpg-bMFw_wHVbw-1280.jpeg&quot; alt=&quot;Rainbow Sierpiński curve stickers.&quot; width=&quot;1280&quot; height=&quot;960&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;Stickers made during &lt;a href=&quot;https://forresto.com/fab-academy/03-computer-controlled-cutting.html&quot;&gt;fab Week 03&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;More &lt;a href=&quot;https://www.forresto.com/2020s/sierpinski.html&quot;&gt;Sierpiński curve projects&lt;/a&gt;: snow walking, wall lamp, web animation, 3D print.&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://forresto.com/media-cache/tactile-labyrinth-2d.jpg-n9A-3nWguW-1280.avif 1280w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://forresto.com/media-cache/tactile-labyrinth-2d.jpg-n9A-3nWguW-1280.webp 1280w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://forresto.com/media-cache/tactile-labyrinth-2d.jpg-n9A-3nWguW-1280.jpeg&quot; alt=&quot;2D wooden labyrinth&quot; width=&quot;1280&quot; height=&quot;1280&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;2D wooden labyrinth, 2018.&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://forresto.com/media-cache/tactile-labyrinth-3d-sketch.jpg-EU_WMPS6hK-1002.avif 1002w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://forresto.com/media-cache/tactile-labyrinth-3d-sketch.jpg-EU_WMPS6hK-1002.webp 1002w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://forresto.com/media-cache/tactile-labyrinth-3d-sketch.jpg-EU_WMPS6hK-1002.jpeg&quot; alt=&quot;3D wooden labyrinth around dodecahedron, composite photo sketch.&quot; width=&quot;1002&quot; height=&quot;1002&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;Sketch for a 3D wooden labyrinth.&lt;/p&gt;
&lt;h1 id=&quot;simulation&quot; tabindex=&quot;-1&quot;&gt;Simulation&lt;/h1&gt;
&lt;p&gt;Both the Blender and web versions of the simulation include a center of gravity, a ground sphere, and a loop of beads. Each frame of animation can have many physics cycles, that advance the time a fraction of the animation frame time. I use &lt;a href=&quot;https://en.wikipedia.org/wiki/Verlet_integration&quot;&gt;Verlet integration&lt;/a&gt; to solve the positions of an velocities of the beads that make the rope.&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://forresto.com/media-cache/sim-grow-beads.png-lKd8wRU2_t-1280.avif 1280w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://forresto.com/media-cache/sim-grow-beads.png-lKd8wRU2_t-1280.webp 1280w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://forresto.com/media-cache/sim-grow-beads.png-lKd8wRU2_t-1280.png&quot; alt=&quot;Growing beads for smooth rope simulation.&quot; width=&quot;1280&quot; height=&quot;1204&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;One challenge was smoothly adding beads to the rope. If you just add a new bead between two existing ones, the new bead will be overlapping, and the collision constraint will &amp;quot;thrash&amp;quot; the rope as the beads jump away from their overlap. By adding a new bead with a small radius and then smoothly growing it, the rope can grow without this thrash.&lt;/p&gt;
&lt;h2 id=&quot;blender-geometry-nodes&quot; tabindex=&quot;-1&quot;&gt;Blender geometry nodes&lt;/h2&gt;
&lt;p&gt;&lt;video src=&quot;https://forresto.com/fab-academy/05-3d/rope-blender.mp4&quot; controls=&quot;&quot; muted=&quot;&quot;&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;Ropes simulated in Blender. I have a &lt;a href=&quot;https://fopub.notion.site/Rope-Physics-growth-v3-1804337bf87d80edabcbf9653afb297b&quot;&gt;Blender journal&lt;/a&gt; that documents that simulation.&lt;/p&gt;
&lt;p&gt;With this simulation, I can grow irregular paths around any shape, with various paramters like center of gravity.&lt;/p&gt;
&lt;script type=&quot;module&quot; src=&quot;https://forresto.com/js/model-viewer.min.js&quot;&gt;&lt;/script&gt;
&lt;model-viewer src=&quot;05-3d/ropes-icosa-union.glb&quot; ar=&quot;&quot; ar-modes=&quot;webxr scene-viewer quick-look&quot; camera-controls=&quot;&quot; tone-mapping=&quot;neutral&quot; poster=&quot;05-3d/ropes-icosa-union-poster.webp&quot; shadow-intensity=&quot;1&quot;&gt;
&lt;/model-viewer&gt;
&lt;p&gt;3D embed, union of simulated ropes and base icosahedron.&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://forresto.com/media-cache/tactile-labyrinth-3d-prints.jpg-5h8xgZK5jm-1280.avif 1280w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://forresto.com/media-cache/tactile-labyrinth-3d-prints.jpg-5h8xgZK5jm-1280.webp 1280w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://forresto.com/media-cache/tactile-labyrinth-3d-prints.jpg-5h8xgZK5jm-1280.jpeg&quot; alt=&quot;Ropes only, and union of ropes and base polyhedron.&quot; width=&quot;1280&quot; height=&quot;960&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;I&#39;ve printed 3 versions of the same simulation result: ropes only, union with icosahedron, and difference (carving into the polyhedron). There is a pleasing contrast between the regular polyhedron and organic ropes.&lt;/p&gt;
&lt;h2 id=&quot;web&quot; tabindex=&quot;-1&quot;&gt;Web&lt;/h2&gt;
&lt;model-viewer src=&quot;./tactile-labyrinth/web-rope-sim.glb&quot; ar=&quot;&quot; ar-modes=&quot;webxr scene-viewer quick-look&quot; camera-controls=&quot;&quot; tone-mapping=&quot;neutral&quot; poster=&quot;./tactile-labyrinth/web-rope-sim-poster.webp&quot; shadow-intensity=&quot;1&quot;&gt;
&lt;/model-viewer&gt;
&lt;p&gt;I ported the Blender geometry nodes &lt;a href=&quot;https://forresto.com/rope-simulation/index.html&quot;&gt;rope simulation to the web&lt;/a&gt;, with help from Claude 3.7. I structured &lt;a href=&quot;https://forresto.com/rope-simulation/readme.md&quot;&gt;the prompt as a readme file&lt;/a&gt;, but it took a bunch of iteration to reach parity with the Blender version. I lost Blender&#39;s fancy rendering, but gained live parameter tweaking and more direct manpulation and interactive possibility.&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://forresto.com/media-cache/web-rope-sim-print.jpg-0R5ng0if7b-1280.avif 1280w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://forresto.com/media-cache/web-rope-sim-print.jpg-0R5ng0if7b-1280.webp 1280w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://forresto.com/media-cache/web-rope-sim-print.jpg-0R5ng0if7b-1280.jpeg&quot; alt=&quot;Web simulation test print.&quot; width=&quot;1280&quot; height=&quot;1280&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;h1 id=&quot;interaction&quot; tabindex=&quot;-1&quot;&gt;Interaction&lt;/h1&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://forresto.com/media-cache/tactile-labyrinth-interaction-sketch.jpg-l_zbAb3mGS-1280.avif 1280w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://forresto.com/media-cache/tactile-labyrinth-interaction-sketch.jpg-l_zbAb3mGS-1280.webp 1280w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://forresto.com/media-cache/tactile-labyrinth-interaction-sketch.jpg-l_zbAb3mGS-1280.jpeg&quot; alt=&quot;Game sketch.&quot; width=&quot;1280&quot; height=&quot;1378&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;Idea for a game-like interaction, where a character, represented by one LED of the string, walks along the path, all around the sphere. You have to turn the ball to keep them on top.&lt;/p&gt;
&lt;p&gt;Maybe the bottom hemisphere stays blue and sloshes around a bit, to represent water.&lt;/p&gt;
&lt;h1 id=&quot;large-scale-prototype&quot; tabindex=&quot;-1&quot;&gt;Large scale prototype&lt;/h1&gt;
&lt;p&gt;&lt;video title=&quot;LED rope with rainbow chasing animation.&quot; src=&quot;https://forresto.com/fab-academy/tactile-labyrinth/led-rope-rainbow.mp4&quot; autoplay=&quot;&quot; loop=&quot;&quot; muted=&quot;&quot;&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;I bought an &lt;a href=&quot;https://www.amazon.de/-/en/Bluetooth-Dimmable-Waterproof-Controllable-Decoration/dp/B0DC6LG5R9/&quot;&gt;addressable LED &amp;quot;rope&amp;quot;&lt;/a&gt; with USB power, animation controller, and silicone and nylon diffuser.&lt;/p&gt;
&lt;p&gt;&lt;video title=&quot;LED rope wrapped around ball.&quot; src=&quot;https://forresto.com/fab-academy/tactile-labyrinth/led-rope-ball.mp4&quot; autoplay=&quot;&quot; loop=&quot;&quot; muted=&quot;&quot;&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;By wrapping it around a ball, we could start to get a sense of how it would feel to hold an animating light orb. But I needed a better structure to attach the rope.&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://forresto.com/media-cache/cardboard-sphere-half.jpg-BAt2yZaVbb-1280.avif 1280w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://forresto.com/media-cache/cardboard-sphere-half.jpg-BAt2yZaVbb-1280.webp 1280w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://forresto.com/media-cache/cardboard-sphere-half.jpg-BAt2yZaVbb-1280.jpeg&quot; alt=&quot;alt text&quot; width=&quot;1280&quot; height=&quot;1807&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;Everybody should have a stack of cardboard hexagons handy. This is actually the third life of these hexagons. They started as offcuts of a laser cutting jig, then became prototype game pieces, then became apriltag tokens for &lt;a href=&quot;https://www.youtube.com/shorts/kDAjLq2sAcU&quot;&gt;Folk Computer&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;video title=&quot;Cardboard sphere loop, inside and out.&quot; src=&quot;https://forresto.com/fab-academy/tactile-labyrinth/cardboard-sphere.mp4&quot; autoplay=&quot;&quot; loop=&quot;&quot; muted=&quot;&quot;&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;Cardboard soccer ball ready, a.k.a. &lt;a href=&quot;https://polyhedra.tessera.li/truncated-icosahedron/info&quot;&gt;truncated icosahedron&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;video title=&quot;LED rope ball cardboard prototype.&quot; src=&quot;https://forresto.com/fab-academy/tactile-labyrinth/led-rope-ball-cardboard.mp4&quot; autoplay=&quot;&quot; muted=&quot;&quot; loop=&quot;&quot;&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;With some zip ties to attach the LED rope. Getting a feel for the form, imagining a mag sensor driving the light motion.&lt;/p&gt;
&lt;h1 id=&quot;circuit-design&quot; tabindex=&quot;-1&quot;&gt;Circuit design&lt;/h1&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://forresto.com/media-cache/circuit-fo-v3-rope.jpg-SMYEh-F_oV-1280.avif 1280w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://forresto.com/media-cache/circuit-fo-v3-rope.jpg-SMYEh-F_oV-1280.webp 1280w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://forresto.com/media-cache/circuit-fo-v3-rope.jpg-SMYEh-F_oV-1280.jpeg&quot; alt=&quot;fo_v3 to LED rope&quot; width=&quot;1280&quot; height=&quot;1706&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;Documented in &lt;a href=&quot;https://forresto.com/fab-academy/10-output-devices.html#circuit-fo_v3&quot;&gt;week 10&#39;s post&lt;/a&gt;.&lt;/p&gt;
&lt;h1 id=&quot;future-iterations-%EA%A9%9C&quot; tabindex=&quot;-1&quot;&gt;Future iterations ꩜&lt;/h1&gt;
&lt;p&gt;Mirror image prints, to trace with fingers in each hand.&lt;/p&gt;
&lt;p&gt;3D modeling: learn how to split shape to be printed as triangles flat on the bed.&lt;/p&gt;
&lt;p&gt;CNC carving: translate that design to be carvable.&lt;/p&gt;
&lt;p&gt;Electronics: make it possible to insert &lt;a href=&quot;https://www.amazon.de/-/en/gp/product/B0DC6LG5R9/&quot;&gt;LED rope&lt;/a&gt;, design animations that fit the form.&lt;/p&gt;
&lt;h1 id=&quot;open-questions&quot; tabindex=&quot;-1&quot;&gt;Open questions&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;What is the interaction?&lt;/li&gt;
&lt;li&gt;If I end up with an unconventional display, what will it show?&lt;/li&gt;
&lt;li&gt;Could it be the base of the &lt;a href=&quot;https://forresto.com/fab-academy/01-potential-final-project.html#calm-timer&quot;&gt;&amp;quot;calm timer&amp;quot; idea&lt;/a&gt;?&lt;/li&gt;
&lt;li&gt;Can I keep the tactile inspiration while adding electronic interactivity, or are those exclusive?&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&quot;midterm-plan&quot; tabindex=&quot;-1&quot;&gt;Midterm plan&lt;/h1&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://forresto.com/media-cache/desmos-led-path.png-WpXCCTJY3E-1280.avif 1280w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://forresto.com/media-cache/desmos-led-path.png-WpXCCTJY3E-1280.webp 1280w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://forresto.com/media-cache/desmos-led-path.png-WpXCCTJY3E-1280.png&quot; alt=&quot;Path of LED rope approximated in Desmos.&quot; width=&quot;1280&quot; height=&quot;1167&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.desmos.com/3d/x40d4kqrma&quot;&gt;Desmos graph with approximate LED path&lt;/a&gt;. Time to &amp;quot;eat the frog&amp;quot; and do some integration. The step I&#39;ve been a little stuck on is mapping the LED indexes to positions. By making an formulaic approximation, I can get something on the microcontroller to get unstuck, and finesse the strategy later.&lt;/p&gt;
&lt;p&gt;2025-04-23: &lt;a href=&quot;https://forresto.com/fab-academy/13-midterm-plan.html&quot;&gt;Week 13&#39;s post&lt;/a&gt;.&lt;/p&gt;
&lt;h1 id=&quot;prototype-integration&quot; tabindex=&quot;-1&quot;&gt;Prototype integration&lt;/h1&gt;
&lt;p&gt;It&#39;s important to get my hands on a working version, the integrated cardboard prototype, to better understand how to budget the rest of my time. What are the most important improvements to make in each area of the build?&lt;/p&gt;
&lt;h2 id=&quot;sensor-and-animation&quot; tabindex=&quot;-1&quot;&gt;Sensor and animation&lt;/h2&gt;
&lt;p&gt;&lt;video src=&quot;https://forresto.com/fab-academy/tactile-labyrinth/tactile-labyrinth-sensor-and-animation.mp4&quot; controls=&quot;&quot; muted=&quot;&quot;&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;I really want to get the sensor and battery in position now!&lt;/p&gt;
&lt;h2 id=&quot;battery-and-sensor-position&quot; tabindex=&quot;-1&quot;&gt;Battery and sensor position&lt;/h2&gt;
&lt;p&gt;&lt;video src=&quot;https://forresto.com/fab-academy/tactile-labyrinth/tactile-labyrinth-cardboard-battery.mp4&quot; controls=&quot;&quot; autoplay=&quot;&quot; loop=&quot;&quot; muted=&quot;&quot;&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;Wireless and handheld at last.&lt;/p&gt;
&lt;h2 id=&quot;(re)calibration-app&quot; tabindex=&quot;-1&quot;&gt;(Re)calibration app&lt;/h2&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://forresto.com/media-cache/pixel-ball-calibration-app.png--z6rB9CiTg-1056.avif 1056w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://forresto.com/media-cache/pixel-ball-calibration-app.png--z6rB9CiTg-1056.webp 1056w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://forresto.com/media-cache/pixel-ball-calibration-app.png--z6rB9CiTg-1056.png&quot; alt=&quot;Screenshot of the calibration app with dots representing LEDs around a sphere.&quot; width=&quot;1056&quot; height=&quot;1284&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;2025-05-07: Once I had a version with the accelerometer in place, I reworked the calibration flow. &lt;a href=&quot;https://forresto.com/fab-academy/15-pixel-ball-app.html&quot;&gt;Week 15&#39;s post&lt;/a&gt;.&lt;/p&gt;
&lt;h1 id=&quot;wood-side-quest&quot; tabindex=&quot;-1&quot;&gt;Wood side quest&lt;/h1&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://forresto.com/media-cache/milling-wood-02.jpg-xq0NRGeLfq-1280.avif 1280w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://forresto.com/media-cache/milling-wood-02.jpg-xq0NRGeLfq-1280.webp 1280w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://forresto.com/media-cache/milling-wood-02.jpg-xq0NRGeLfq-1280.jpeg&quot; alt=&quot;Wooden brain ball.&quot; width=&quot;1280&quot; height=&quot;960&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;Four axis milling for &lt;a href=&quot;https://forresto.com/fab-academy/17-four-axis-milling.html&quot;&gt;wildcard week 17&lt;/a&gt;.&lt;/p&gt;
&lt;h1 id=&quot;final-integration&quot; tabindex=&quot;-1&quot;&gt;Final integration&lt;/h1&gt;
&lt;h2 id=&quot;material-choice&quot; tabindex=&quot;-1&quot;&gt;Material choice&lt;/h2&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://forresto.com/media-cache/small-lamp.jpg-iFZ9d0nHCE-1280.avif 1280w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://forresto.com/media-cache/small-lamp.jpg-iFZ9d0nHCE-1280.webp 1280w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://forresto.com/media-cache/small-lamp.jpg-iFZ9d0nHCE-1280.jpeg&quot; alt=&quot;Small lamp with labyrinth design.&quot; width=&quot;1280&quot; height=&quot;1280&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;Material and LED socket tests in &lt;a href=&quot;https://forresto.com/fab-academy/16-system-integration.html&quot;&gt;week 16&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;boolean-workflow&quot; tabindex=&quot;-1&quot;&gt;Boolean workflow&lt;/h2&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://forresto.com/media-cache/labyrinth-inner-3d-print.jpg-BTp23C1kiM-1280.avif 1280w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://forresto.com/media-cache/labyrinth-inner-3d-print.jpg-BTp23C1kiM-1280.webp 1280w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://forresto.com/media-cache/labyrinth-inner-3d-print.jpg-BTp23C1kiM-1280.jpeg&quot; alt=&quot;Labyrinth inner structure, top half.&quot; width=&quot;1280&quot; height=&quot;960&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;Internal structure design and printing the major parts.&lt;/p&gt;
&lt;h2 id=&quot;aesthetic&quot; tabindex=&quot;-1&quot;&gt;Aesthetic&lt;/h2&gt;
&lt;p&gt;I&#39;m committed to the &amp;quot;3D print of medium poly count geometry&amp;quot; aesthetic now. Other processes and sanding didn&#39;t enhance the tactile or translucent qualities that are important.&lt;/p&gt;
&lt;h2 id=&quot;3d-print-snap-together&quot; tabindex=&quot;-1&quot;&gt;3D print snap-together&lt;/h2&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://forresto.com/media-cache/tldraw-boolean-outline.png-czsA8tcsYV-1280.avif 1280w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://forresto.com/media-cache/tldraw-boolean-outline.png-czsA8tcsYV-1280.webp 1280w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://forresto.com/media-cache/tldraw-boolean-outline.png-czsA8tcsYV-1280.png&quot; alt=&quot;2D Diagram explaining the 3D Boolean (add, subtract) operation ordering of these shapes.&quot; width=&quot;1280&quot; height=&quot;941&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;An accident, but my test parts became my production parts when they snapped together real nice. But why did it work out that way?&lt;/p&gt;
&lt;h2 id=&quot;hemisphere-coupler&quot; tabindex=&quot;-1&quot;&gt;Hemisphere coupler&lt;/h2&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://forresto.com/media-cache/blender-coupler.png-Nf7_w7MONx-1280.avif 1280w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://forresto.com/media-cache/blender-coupler.png-Nf7_w7MONx-1280.webp 1280w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://forresto.com/media-cache/blender-coupler.png-Nf7_w7MONx-1280.png&quot; alt=&quot;Blender screenshot.&quot; width=&quot;1280&quot; height=&quot;779&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;Blender setup for the part that holds the two hemispheres together. Part created with several Boolean modifiers.&lt;/p&gt;
&lt;h2 id=&quot;circuit-mounting-platform&quot; tabindex=&quot;-1&quot;&gt;Circuit mounting platform&lt;/h2&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://forresto.com/media-cache/cuttle-platform.png-XHij9l6Fp1-1280.avif 1280w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://forresto.com/media-cache/cuttle-platform.png-XHij9l6Fp1-1280.webp 1280w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://forresto.com/media-cache/cuttle-platform.png-XHij9l6Fp1-1280.png&quot; alt=&quot;Cuttle screenshot.&quot; width=&quot;1280&quot; height=&quot;759&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;The platform is designed to put the accelerometer at the center of the sphere, and the 18650 battery compartment near the center of gravity.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://forresto.com/fab-academy/18-final-labyrinth-parts.html&quot;&gt;Week 18: documenting these final parts&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;rip-led-index-0&quot; tabindex=&quot;-1&quot;&gt;RIP LED index 0&lt;/h2&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://forresto.com/media-cache/led-drama-texts-1.png-Bb0QnLHJj_-1280.avif 1280w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://forresto.com/media-cache/led-drama-texts-1.png-Bb0QnLHJj_-1280.webp 1280w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://forresto.com/media-cache/led-drama-texts-1.png-Bb0QnLHJj_-1280.png&quot; alt=&quot;LED drama sad&quot; width=&quot;1280&quot; height=&quot;1253&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;After feeling lots of momentum at the lab getting the parts produced and integrated, I took everything home to finish the integration. I was distrought when none of the LEDs worked. I tried various controllers and power supplies, and they worked with another LED strip, but not my wire. I couldn&#39;t find a local source of the LEDs, and it would take too long to get a replacement. I thought my schedule was shot. I wouldn&#39;t be able to finish the course on time.&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://forresto.com/media-cache/led-jumping-sewing-pins.jpg-XXPza_SJ1K-1280.avif 1280w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://forresto.com/media-cache/led-jumping-sewing-pins.jpg-XXPza_SJ1K-1280.webp 1280w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://forresto.com/media-cache/led-jumping-sewing-pins.jpg-XXPza_SJ1K-1280.jpeg&quot; alt=&quot;Jumping the first LED with three sewing pins through the wires.&quot; width=&quot;1280&quot; height=&quot;1706&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;Kris mentioned that if something happens to the first LED, it might not forward the signals needed to the rest of the strip. Instead of cutting it out, I tried jumping the 3 wires with sewing pins. I was shocked and elated when it actually worked.&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://forresto.com/media-cache/led-drama-texts-2.png-XVMN09U_0K-1280.avif 1280w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://forresto.com/media-cache/led-drama-texts-2.png-XVMN09U_0K-1280.webp 1280w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://forresto.com/media-cache/led-drama-texts-2.png-XVMN09U_0K-1280.png&quot; alt=&quot;LED drama happy&quot; width=&quot;1280&quot; height=&quot;1003&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;What a roller coaster. 🎢&lt;/p&gt;
&lt;p&gt;One LED can make the whole string not work, and the string not working made me think that the whole project wouldn&#39;t be done. In that way, it was a self-analogy of dependency failures.&lt;/p&gt;
&lt;h2 id=&quot;software-integration&quot; tabindex=&quot;-1&quot;&gt;Software integration&lt;/h2&gt;
&lt;p&gt;The gist of calibration:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Light one LED at a time&lt;/li&gt;
&lt;li&gt;Turn the ball to put that at top&lt;/li&gt;
&lt;li&gt;Record the gravity vector for that LED&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I also had to make an array mapping the outer path LED order to the internal index, because the order is different.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://forresto.com/fab-academy/tactile-labyrinth/TactileLabyrinthFinal/readme.md&quot;&gt;readme.md&lt;/a&gt; for the final software.&lt;/p&gt;
&lt;h1 id=&quot;source&quot; tabindex=&quot;-1&quot;&gt;Source&lt;/h1&gt;
&lt;p&gt;&lt;a href=&quot;https://forresto.com/fab-academy/tactile-labyrinth/forresto-tactile-labyrinth-v0.zip&quot;&gt;forresto-tactile-labyrinth-v0.zip&lt;/a&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.
├── dev_board
│   ├── dev_board.kicad_pcb
│   ├── dev_board.kicad_pro
│   └── dev_board.kicad_sch
├── print-and-cut
│   ├── internal-join-v2.stl
│   ├── internal.blend-bottom.stl
│   ├── internal.blend-top.stl
│   ├── labyrinth internal support - cuttle.xyz.svg
│   ├── rope-3d-print-complete-2025-05-27T10-48-23-285Z-negative.stl
│   └── rope-3d-print-complete-2025-05-27T10-48-23-306Z-positive.stl
├── TactileLabyrinthFinal
│   ├── PixelBallLedMap.h
│   ├── readme.md
│   └── TactileLabyrinthFinal.ino
└── web
    ├── lib
    ├── pixel-ball
    │   ├── index.html
    │   ├── pixel-ball-initial.js
    │   ├── pixel-ball-viz.js
    │   ├── pixel-ball.js
    │   └── readme.md
    └── rope-simulation
        ├── generated
        │   ├── main.js
        │   ├── Physics.js
        │   ├── Print3D-worker.js
        │   ├── Print3D.js
        │   ├── Rope.js
        │   ├── RopeVisualizer.js
        │   └── TubeSDF.js
        ├── index.html
        ├── lib
        ├── readme.md
        └── rope-148-16.json
&lt;/code&gt;&lt;/pre&gt;
&lt;h1 id=&quot;fab-license&quot; tabindex=&quot;-1&quot;&gt;fab license&lt;/h1&gt;
&lt;p&gt;© Forrest Oliphant 2025&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This work may be reproduced, modified, distributed, performed, and displayed for any purpose, but must acknowledge &amp;quot;Tactile Labyrinth by Forrest Oliphant&amp;quot;. Copyright is retained and must be preserved. The work is provided as is; no warranty is provided, and users accept all liability.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;p&gt;Example acknowledement link in markdown:&lt;/p&gt;
&lt;pre class=&quot;language-md&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-md&quot;&gt;&lt;span class=&quot;token url&quot;&gt;[&lt;span class=&quot;token content&quot;&gt;Tactile Labyrinth by Forrest Oliphant&lt;/span&gt;](&lt;span class=&quot;token url&quot;&gt;https://www.forresto.com/fab-academy/tactile-labyrinth.html&lt;/span&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h1 id=&quot;next-steps...&quot; tabindex=&quot;-1&quot;&gt;Next steps...&lt;/h1&gt;
&lt;p&gt;Hardware upgrades:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Break out small panel for external access for power switch, charging, programming&lt;/li&gt;
&lt;li&gt;Improve the joinery of the halves of the sphere&lt;/li&gt;
&lt;li&gt;Fancy compound hinge?&lt;/li&gt;
&lt;li&gt;Vibration motor with satisfying feedback for the gameplay&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Software:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Gameplay improvements, make the motion more connected to physics&lt;/li&gt;
&lt;li&gt;Persistence of vision, show images when spinning or throwing the ball&lt;/li&gt;
&lt;/ul&gt;
</content>
  </entry>
  <entry>
    <title>Week 18. Final Labyrinth Parts</title>
    <link href="https://forresto.com/fab-academy/18-final-labyrinth-parts.html" />
    <updated>2025-05-28T00:00:00Z</updated>
    <id>https://forresto.com/fab-academy/18-final-labyrinth-parts.html</id>
    <content type="html">&lt;p&gt;This week I continued the materials tests from &lt;a href=&quot;https://forresto.com/fab-academy/16-system-integration.html&quot;&gt;week 16&lt;/a&gt; for the &lt;a href=&quot;https://forresto.com/fab-academy/tactile-labyrinth.html&quot;&gt;tactile labyrinth&lt;/a&gt;. I also refactored the &lt;a href=&quot;https://forresto.com/rope-simulation/&quot;&gt;rope simulation&lt;/a&gt; web app to export STLs for the internal structure of the labyrinth.&lt;/p&gt;
&lt;h1 id=&quot;inner-parts&quot; tabindex=&quot;-1&quot;&gt;Inner parts&lt;/h1&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://forresto.com/media-cache/labyrinth-inner-3d-print.jpg-BTp23C1kiM-1280.avif 1280w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://forresto.com/media-cache/labyrinth-inner-3d-print.jpg-BTp23C1kiM-1280.webp 1280w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://forresto.com/media-cache/labyrinth-inner-3d-print.jpg-BTp23C1kiM-1280.jpeg&quot; alt=&quot;Labyrinth inner structure, top half.&quot; width=&quot;1280&quot; height=&quot;960&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;As booleans on organic shapes are heavy in the browser, I continued with the positive/negative STL export process that I outlined in week 16.&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://forresto.com/media-cache/tldraw-boolean-order.png-AbT4Zb-2br-919.avif 919w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://forresto.com/media-cache/tldraw-boolean-order.png-AbT4Zb-2br-919.webp 919w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://forresto.com/media-cache/tldraw-boolean-order.png-AbT4Zb-2br-919.png&quot; alt=&quot;2D Diagram explaining the 3D Boolean (add, subtract) operation ordering of these shapes.&quot; width=&quot;919&quot; height=&quot;942&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;When slicing the second half of the internal structure, I ran into a bug with Prusa Slicer that would discard some of the geometry. I had to recreate boolean operations in Blender, but now I have actual STLs for the parts that don&#39;t depend on Prusa Slicer.&lt;/p&gt;
&lt;p&gt;This design is a callback to an earlier version that combined the geometric lines of an icosahedron with the organic path of the rope simulation. The main purpose is to be a structure for the outer parts and electronics, It also hides the wires and prevents accidental poking from the outside.&lt;/p&gt;
&lt;h1 id=&quot;renders&quot; tabindex=&quot;-1&quot;&gt;Renders&lt;/h1&gt;
&lt;script type=&quot;module&quot; src=&quot;https://forresto.com/js/model-viewer.min.js&quot;&gt;&lt;/script&gt;
&lt;model-viewer src=&quot;./18-final-labyrinth-parts/labyrinth-internal.glb&quot; ar=&quot;&quot; ar-modes=&quot;webxr scene-viewer quick-look&quot; camera-controls=&quot;&quot; tone-mapping=&quot;neutral&quot; poster=&quot;./18-final-labyrinth-parts/labyrinth-internal.glb-poster.webp&quot; shadow-intensity=&quot;1&quot;&gt;
&lt;/model-viewer&gt;
&lt;p&gt;3d embed.&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://forresto.com/media-cache/labyrinth-three-wireframe.png-5BQKRIyaxV-1134.avif 1134w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://forresto.com/media-cache/labyrinth-three-wireframe.png-5BQKRIyaxV-1134.webp 1134w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://forresto.com/media-cache/labyrinth-three-wireframe.png-5BQKRIyaxV-1134.png&quot; alt=&quot;Wireframe render of inner and outer parts.&quot; width=&quot;1134&quot; height=&quot;1066&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;New Three.js materials for preview.&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://forresto.com/media-cache/labyrinth-womp-render.jpg-HYGcJbE_Pz-1280.avif 1280w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://forresto.com/media-cache/labyrinth-womp-render.jpg-HYGcJbE_Pz-1280.webp 1280w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://forresto.com/media-cache/labyrinth-womp-render.jpg-HYGcJbE_Pz-1280.jpeg&quot; alt=&quot;Womp render.&quot; width=&quot;1280&quot; height=&quot;720&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;Parts imported to Womp to play with their rendering. I thought that it might be good for some final modeling on the parts, but they can&#39;t import editable meshes with full fidelity. I&#39;m committed to the &amp;quot;3D print of medium poly count geometry&amp;quot; aesthetic now, so Womp&#39;s SDF modeling would be a step away from that.&lt;/p&gt;
&lt;h1 id=&quot;outer-parts&quot; tabindex=&quot;-1&quot;&gt;Outer parts&lt;/h1&gt;
&lt;p&gt;Because I adopted an internal structure, I made a physics constraint tweak is that the rope should not merge into itself. This will make the rope parts individually printable, and keep light from leaking through the unioned areas.&lt;/p&gt;
&lt;p&gt;I continued the material tests of week 16 by trying Formlabs Transpanent Resin v4, and Fiberology Transparent PCTG. The resin print is the most transparent, but I&#39;m depending on the light diffusion of the filament 3D printer and infill, so I&#39;ll continue with that.&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://forresto.com/media-cache/labyrinth-outer-3d-print-wip.jpg-IOEjisbKTO-1280.avif 1280w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://forresto.com/media-cache/labyrinth-outer-3d-print-wip.jpg-IOEjisbKTO-1280.webp 1280w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://forresto.com/media-cache/labyrinth-outer-3d-print-wip.jpg-IOEjisbKTO-1280.jpeg&quot; alt=&quot;Labyrinth outer paths 3d print WIP.&quot; width=&quot;1280&quot; height=&quot;960&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;21 hour print.&lt;/p&gt;
&lt;p&gt;Could experiment with more-efficient bed placement of the rope parts.&lt;/p&gt;
&lt;h1 id=&quot;3d-print-snap-together&quot; tabindex=&quot;-1&quot;&gt;3D print snap-together&lt;/h1&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://forresto.com/media-cache/rope-clips-on-wip.jpg-OlIFhdAirD-1280.avif 1280w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://forresto.com/media-cache/rope-clips-on-wip.jpg-OlIFhdAirD-1280.webp 1280w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://forresto.com/media-cache/rope-clips-on-wip.jpg-OlIFhdAirD-1280.jpeg&quot; alt=&quot;First four rope parts snapped onto the internal structure.&quot; width=&quot;1280&quot; height=&quot;1706&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;An accident, but my test parts became my production parts when they snapped together &amp;quot;real nice&amp;quot;.&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://forresto.com/media-cache/tldraw-3d-print-joinery-question.png-izAdTxfzei-1280.avif 1280w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://forresto.com/media-cache/tldraw-3d-print-joinery-question.png-izAdTxfzei-1280.webp 1280w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://forresto.com/media-cache/tldraw-3d-print-joinery-question.png-izAdTxfzei-1280.png&quot; alt=&quot;3d print joinery question.&quot; width=&quot;1280&quot; height=&quot;671&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;Somehow these two prints with similar settings and different materials and no &amp;quot;kerf&amp;quot; compensation snapped together in a really sturdy-feeling way.&lt;/p&gt;
&lt;p&gt;But why did it work out that way?&lt;/p&gt;
&lt;p&gt;Will it work for resin printers, or do we have less wiggle room with that process? When Adam Savage set up his Formlabs Fuse nylon SLS printer, &lt;a href=&quot;https://youtu.be/x78bJi-snrc?si=rYXpF0-nD-NHMYQI&amp;amp;t=1753&quot;&gt;their first snap-together parts did not work&lt;/a&gt; because there is no flex in that material. (I appreciated them sharing that &lt;s&gt;fail&lt;/s&gt; learning experience.)&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://forresto.com/media-cache/tldraw-rope-clips-on.png-5b9LwXr8ka-881.avif 881w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://forresto.com/media-cache/tldraw-rope-clips-on.png-5b9LwXr8ka-881.webp 881w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://forresto.com/media-cache/tldraw-rope-clips-on.png-5b9LwXr8ka-881.png&quot; alt=&quot;Sketch of why the rope clips on.&quot; width=&quot;881&quot; height=&quot;578&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;It makes sense that the angles of the hole collars around the
ball make the rope into a big clip. This is also why the bigger
parts are harder to coax into place.&lt;/p&gt;
&lt;p&gt;LED wire is threaded through the inside like this.&lt;/p&gt;
&lt;h1 id=&quot;hemisphere-coupler&quot; tabindex=&quot;-1&quot;&gt;Hemisphere coupler&lt;/h1&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://forresto.com/media-cache/blender-coupler.png-Nf7_w7MONx-1280.avif 1280w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://forresto.com/media-cache/blender-coupler.png-Nf7_w7MONx-1280.webp 1280w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://forresto.com/media-cache/blender-coupler.png-Nf7_w7MONx-1280.png&quot; alt=&quot;Blender screenshot.&quot; width=&quot;1280&quot; height=&quot;779&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;Blender setup for the part that holds the two hemispheres together. Part created with several Boolean modifiers.&lt;/p&gt;
&lt;p&gt;Base shape: Icosahedron sized to fit within walls of the inner structure.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Intersect: wide cylindar with desired height&lt;/li&gt;
&lt;li&gt;Subtract: inner structure, so the part fits within the inner structure&lt;/li&gt;
&lt;li&gt;Subtract: 32-sided cylindar through-hole to leave room for battery and access the bottom wires&lt;/li&gt;
&lt;li&gt;Subtract: cylindar placed so that the platform is centered in the sphere&lt;/li&gt;
&lt;li&gt;Subtract: several icosahedra to leave room for wires to go up or down&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Note: it would have been more reusable to build these shapes with parameters in the web app, but Blender proved good enough to make the one part that I needed.&lt;/p&gt;
&lt;h1 id=&quot;electronics-platform&quot; tabindex=&quot;-1&quot;&gt;Electronics platform&lt;/h1&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://forresto.com/media-cache/cuttle-platform.png-XHij9l6Fp1-1280.avif 1280w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://forresto.com/media-cache/cuttle-platform.png-XHij9l6Fp1-1280.webp 1280w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://forresto.com/media-cache/cuttle-platform.png-XHij9l6Fp1-1280.png&quot; alt=&quot;Cuttle screenshot.&quot; width=&quot;1280&quot; height=&quot;759&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://cuttle.xyz/@forresto/labyrinth-internal-support-ZURwuQbWgQqS&quot;&gt;Cuttle project link&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This is the platform to hold circuits, switch and battery. It was satisfying to use the laser cutter with the newly-replaced tube. I started with a cardboard version to test the fit, and then moved to 3mm plywood. I included extra holes in the platform to make it easier to deal with known and unknown unknowns.&lt;/p&gt;
&lt;h1 id=&quot;todo&quot; tabindex=&quot;-1&quot;&gt;Todo&lt;/h1&gt;
&lt;p&gt;Next on my mind...&lt;/p&gt;
&lt;ul class=&quot;task-list&quot;&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_0&quot; checked=&quot;true&quot; disabled=&quot;true&quot;&gt;&lt;label for=&quot;cbx_0&quot;&gt; Electronics platform&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_1&quot; checked=&quot;true&quot; disabled=&quot;true&quot;&gt;&lt;label for=&quot;cbx_1&quot;&gt; Battery compartment&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_2&quot; disabled=&quot;true&quot;&gt;&lt;label for=&quot;cbx_2&quot;&gt; Closure&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_3&quot; disabled=&quot;true&quot;&gt;&lt;label for=&quot;cbx_3&quot;&gt; Integration and testing&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_4&quot; disabled=&quot;true&quot;&gt;&lt;label for=&quot;cbx_4&quot;&gt; Slide and Video&lt;/label&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;😅&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Week 17. Four Axis Milling</title>
    <link href="https://forresto.com/fab-academy/17-four-axis-milling.html" />
    <updated>2025-05-21T00:00:00Z</updated>
    <id>https://forresto.com/fab-academy/17-four-axis-milling.html</id>
    <content type="html">&lt;p&gt;Wildcard week! I thought it would be interesting to carve my design in the Roland mill with the additional rotary axis installed.&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://forresto.com/media-cache/milling-wood-02.jpg-xq0NRGeLfq-1280.avif 1280w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://forresto.com/media-cache/milling-wood-02.jpg-xq0NRGeLfq-1280.webp 1280w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://forresto.com/media-cache/milling-wood-02.jpg-xq0NRGeLfq-1280.jpeg&quot; alt=&quot;Wooden brain ball.&quot; width=&quot;1280&quot; height=&quot;960&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;3D labyrinth in oak. 40mm diameter.&lt;/p&gt;
&lt;h1 id=&quot;design-with-womp&quot; tabindex=&quot;-1&quot;&gt;Design with Womp&lt;/h1&gt;
&lt;script type=&quot;module&quot; src=&quot;https://forresto.com/js/model-viewer.min.js&quot;&gt;&lt;/script&gt;
&lt;model-viewer src=&quot;./tactile-labyrinth/web-rope-sim.glb&quot; ar=&quot;&quot; ar-modes=&quot;webxr scene-viewer quick-look&quot; camera-controls=&quot;&quot; tone-mapping=&quot;neutral&quot; poster=&quot;./tactile-labyrinth/web-rope-sim-poster.webp&quot; shadow-intensity=&quot;1&quot;&gt;
&lt;/model-viewer&gt;
&lt;p&gt;I started with &lt;a href=&quot;https://forresto.com/fab-academy/tactile-labyrinth.html#web&quot;&gt;my webapp&#39;s model&lt;/a&gt; as a GLB file.&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://forresto.com/media-cache/womp-01-add-sphere.jpg-dbJ1oP350_-1280.avif 1280w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://forresto.com/media-cache/womp-01-add-sphere.jpg-dbJ1oP350_-1280.webp 1280w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://forresto.com/media-cache/womp-01-add-sphere.jpg-dbJ1oP350_-1280.jpeg&quot; alt=&quot;Womp screenshot, add sphere.&quot; width=&quot;1280&quot; height=&quot;1234&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;I imported this to WOMP and added a sphere on the inside.&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://forresto.com/media-cache/womp-02-add-goop.jpg-UBhaL1XB_c-1280.avif 1280w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://forresto.com/media-cache/womp-02-add-goop.jpg-UBhaL1XB_c-1280.webp 1280w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://forresto.com/media-cache/womp-02-add-goop.jpg-UBhaL1XB_c-1280.jpeg&quot; alt=&quot;Womp screenshot, add goop.&quot; width=&quot;1280&quot; height=&quot;1174&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;I increased the &amp;quot;goop factor&amp;quot; on the sphere until the sphere melded with the inside of the labyrinth path. This smooth union was helpful for the milling process, to match the size and range of the tools that we have available.&lt;/p&gt;
&lt;h1 id=&quot;cam-with-deskproto&quot; tabindex=&quot;-1&quot;&gt;CAM with DeskProto&lt;/h1&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://forresto.com/media-cache/deskproto-6mm-path.png-yuss-avRbF-1280.avif 1280w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://forresto.com/media-cache/deskproto-6mm-path.png-yuss-avRbF-1280.webp 1280w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://forresto.com/media-cache/deskproto-6mm-path.png-yuss-avRbF-1280.png&quot; alt=&quot;DeskProto screenshot, roughing pass.&quot; width=&quot;1280&quot; height=&quot;681&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;6mm ballnose mill &lt;strong&gt;roughing&lt;/strong&gt; path preview. Estimated time 50 minutes, actual time ended up closer to 3 hours.&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://forresto.com/media-cache/deskproto-3mm-path.png-taFRFJvbfe-1280.avif 1280w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://forresto.com/media-cache/deskproto-3mm-path.png-taFRFJvbfe-1280.webp 1280w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://forresto.com/media-cache/deskproto-3mm-path.png-taFRFJvbfe-1280.png&quot; alt=&quot;DeskProto screenshot, finishing pass.&quot; width=&quot;1280&quot; height=&quot;680&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;3mm ballnose mill &lt;strong&gt;finishing&lt;/strong&gt; path preview.&lt;/p&gt;
&lt;h1 id=&quot;milling-with-roland-mdx-40-and-zcl-40&quot; tabindex=&quot;-1&quot;&gt;Milling with Roland MDX-40 and ZCL-40&lt;/h1&gt;
&lt;p&gt;The first version was milled from SikaBlock M600 N material, as a practice run and test of the toolpaths. It is a polyurethane foam designed for millability and mold making.&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://forresto.com/media-cache/milling-foam-01.jpg-6aiPx9oZNs-1280.avif 1280w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://forresto.com/media-cache/milling-foam-01.jpg-6aiPx9oZNs-1280.webp 1280w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://forresto.com/media-cache/milling-foam-01.jpg-6aiPx9oZNs-1280.jpeg&quot; alt=&quot;Foam block getting rounded.&quot; width=&quot;1280&quot; height=&quot;960&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;Foam first roughing pass with 6mm tool.&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://forresto.com/media-cache/milling-foam-02.jpg-px32nGo5TS-1280.avif 1280w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://forresto.com/media-cache/milling-foam-02.jpg-px32nGo5TS-1280.webp 1280w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://forresto.com/media-cache/milling-foam-02.jpg-px32nGo5TS-1280.jpeg&quot; alt=&quot;Foam block halfway through detail pass.&quot; width=&quot;1280&quot; height=&quot;960&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;Foam detail pass with 3mm tool.&lt;/p&gt;
&lt;p&gt;I installed the cutting tool tool low, so the tip hit the base of the piece when going to the home position. Fortunately nothing broke, and I was able to install the mill at the correct position and recalibrate. I paid much more attention to this step with wood.&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://forresto.com/media-cache/milling-foam-03.jpg-I4zr0jDxeU-1280.avif 1280w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://forresto.com/media-cache/milling-foam-03.jpg-I4zr0jDxeU-1280.webp 1280w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://forresto.com/media-cache/milling-foam-03.jpg-I4zr0jDxeU-1280.jpeg&quot; alt=&quot;Foam carving done.&quot; width=&quot;1280&quot; height=&quot;1706&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;Completed test carving.&lt;/p&gt;
&lt;p&gt;At this point I went back to CAM and added a tab on the right, so that the pin on the right would keep contact through the process.&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://forresto.com/media-cache/milling-wood-01.jpg-WMX7CoLWoP-1280.avif 1280w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://forresto.com/media-cache/milling-wood-01.jpg-WMX7CoLWoP-1280.webp 1280w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://forresto.com/media-cache/milling-wood-01.jpg-WMX7CoLWoP-1280.jpeg&quot; alt=&quot;Wood milling, roughing pass.&quot; width=&quot;1280&quot; height=&quot;960&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;First pass with 6mm mill. That&#39;s a lot of work to make a dowel. Next time I&#39;ll try to match the design and stock a little better, for example by starting with a round dowel. But it was satisfying when the mill finally started to move in the Z dimension, and the rough form started to emerge.&lt;/p&gt;
&lt;p&gt;&lt;video controls=&quot;&quot; src=&quot;https://forresto.com/fab-academy/17-four-axis-milling/wood-rope-timelapse.mp4&quot; title=&quot;Wood rope ball milling finishing pass.&quot; muted=&quot;&quot;&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;10x and 120x timelapse of the finishing pass.&lt;/p&gt;
&lt;p&gt;&lt;video controls=&quot;&quot; src=&quot;https://forresto.com/fab-academy/17-four-axis-milling/wood-rope-spin.mp4&quot; title=&quot;Wood rope ball spinning.&quot; autoplay=&quot;&quot; loop=&quot;&quot; muted=&quot;&quot;&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;Done, phew.&lt;/p&gt;
&lt;h1 id=&quot;conclusion&quot; tabindex=&quot;-1&quot;&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;The rotary stage takes a lot of the vertical space in this desktop mill, so the maximum stock size is about 42mm. The ideal scale for this design would be 80mm. The process was pretty noisy and mind-numbing to supervise... a more positive spin would be that it was meditative.&lt;/p&gt;
&lt;p&gt;If I do another wooden version of the design, I think it would be cool to break the design into several parts that can be milled with a 3 axis process. That would let me create a version with the proper scale.&lt;/p&gt;
&lt;p&gt;But I like my brain bead knob for what it is.&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://forresto.com/media-cache/milling-wood-02.jpg-xq0NRGeLfq-1280.avif 1280w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://forresto.com/media-cache/milling-wood-02.jpg-xq0NRGeLfq-1280.webp 1280w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://forresto.com/media-cache/milling-wood-02.jpg-xq0NRGeLfq-1280.jpeg&quot; alt=&quot;Wooden brain ball.&quot; width=&quot;1280&quot; height=&quot;960&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;h1 id=&quot;references&quot; tabindex=&quot;-1&quot;&gt;References&lt;/h1&gt;
&lt;p&gt;&lt;a href=&quot;https://wiki.aalto.fi/pages/viewpage.action?pageId=290292756&quot;&gt;Aalto Wiki: 3D Milling with DeskProto and MDX-40 (4th axis)&lt;/a&gt; – Shoutout to Jonas Tjepkema for documenting this so thoroughly! Every issue that I had, like the mill end hitting the foam part, was a result of skimming a section too lightly. All the info is in there.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Week 16. System Integration</title>
    <link href="https://forresto.com/fab-academy/16-system-integration.html" />
    <updated>2025-05-14T00:00:00Z</updated>
    <id>https://forresto.com/fab-academy/16-system-integration.html</id>
    <content type="html">&lt;p&gt;Chipping away at the final steps to make the &lt;a href=&quot;https://forresto.com/fab-academy/tactile-labyrinth.html&quot;&gt;Tactile Labyrinth&lt;/a&gt;.&lt;/p&gt;
&lt;h1 id=&quot;hand-scale-test-print&quot; tabindex=&quot;-1&quot;&gt;Hand scale test print&lt;/h1&gt;
&lt;p&gt;I wanted to hold a version of the design made with the web app.&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://forresto.com/media-cache/web-rope-sim-print.jpg-0R5ng0if7b-1280.avif 1280w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://forresto.com/media-cache/web-rope-sim-print.jpg-0R5ng0if7b-1280.webp 1280w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://forresto.com/media-cache/web-rope-sim-print.jpg-0R5ng0if7b-1280.jpeg&quot; alt=&quot;Web simulation test print.&quot; width=&quot;1280&quot; height=&quot;1280&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;Prusa Mini, 24 hour print, generic white PLA.&lt;/p&gt;
&lt;p&gt;One idea is that this will become the &amp;quot;nucleus&amp;quot; of the soft version of the pixel ball. Electronics and battery.&lt;/p&gt;
&lt;p&gt;Another idea is to scale this so that LEDs can be embedded on the inside.&lt;/p&gt;
&lt;h1 id=&quot;design-with-positive-and-negative-space&quot; tabindex=&quot;-1&quot;&gt;Design with positive and negative space&lt;/h1&gt;
&lt;p&gt;The mesh that my design app outputs has overlapping shapes where the ropes rest against each other. Prusa slicer doesn&#39;t have a problem with this. It makes the perimeter and infill as if the shape is unioned. But trying to do any other Boolean modelling on the shape in Blender or JS made a lot of broken and sad meshes.&lt;/p&gt;
&lt;p&gt;I found a workaround: export positive and negative space as 2 separate STL files, then combine them with Prusa Slicer.&lt;/p&gt;
&lt;script type=&quot;module&quot; src=&quot;https://forresto.com/js/model-viewer.min.js&quot;&gt;&lt;/script&gt;
&lt;model-viewer src=&quot;./tactile-labyrinth/web-rope-sim.glb&quot; ar=&quot;&quot; ar-modes=&quot;webxr scene-viewer quick-look&quot; camera-controls=&quot;&quot; tone-mapping=&quot;neutral&quot; poster=&quot;./tactile-labyrinth/web-rope-sim-poster.webp&quot; shadow-intensity=&quot;1&quot;&gt;
&lt;/model-viewer&gt;
&lt;ol start=&quot;0&quot;&gt;
&lt;li&gt;Rope simulation and mesh generation works as before.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://forresto.com/media-cache/model-gen-01-led-preview.png-LumwipoH1O-1280.avif 1280w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://forresto.com/media-cache/model-gen-01-led-preview.png-LumwipoH1O-1280.webp 1280w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://forresto.com/media-cache/model-gen-01-led-preview.png-LumwipoH1O-1280.png&quot; alt=&quot;Screenshot of sphere of LED sockets.&quot; width=&quot;1280&quot; height=&quot;1215&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;LED positions and rotations calculated and previewed.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://forresto.com/media-cache/model-gen-02-sliced.png-LANxWxP94g-1280.avif 1280w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://forresto.com/media-cache/model-gen-02-sliced.png-LANxWxP94g-1280.webp 1280w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://forresto.com/media-cache/model-gen-02-sliced.png-LANxWxP94g-1280.png&quot; alt=&quot;Screenshot of generated positive and negative.&quot; width=&quot;1280&quot; height=&quot;799&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;Slice the rope mesh and preview the LED positions with it. Export positive and negative STLs.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;(Possible enhancement is to do this all with SDF modeling.)&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://forresto.com/media-cache/model-gen-03-prusa-positive.png-0pUVHNwhXe-1280.avif 1280w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://forresto.com/media-cache/model-gen-03-prusa-positive.png-0pUVHNwhXe-1280.webp 1280w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://forresto.com/media-cache/model-gen-03-prusa-positive.png-0pUVHNwhXe-1280.png&quot; alt=&quot;text&quot; width=&quot;1280&quot; height=&quot;781&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;Load the positive STL in Prusa Slicer.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://forresto.com/media-cache/model-gen-04a-prusa-negative.png--fwlRMJOPp-1280.avif 1280w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://forresto.com/media-cache/model-gen-04a-prusa-negative.png--fwlRMJOPp-1280.webp 1280w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://forresto.com/media-cache/model-gen-04a-prusa-negative.png--fwlRMJOPp-1280.png&quot; alt=&quot;text&quot; width=&quot;1280&quot; height=&quot;511&quot;&gt;&lt;/picture&gt;
&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://forresto.com/media-cache/model-gen-04-prusa-negative.png-c1QrNkncd5-1280.avif 1280w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://forresto.com/media-cache/model-gen-04-prusa-negative.png-c1QrNkncd5-1280.webp 1280w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://forresto.com/media-cache/model-gen-04-prusa-negative.png-c1QrNkncd5-1280.png&quot; alt=&quot;text&quot; width=&quot;1280&quot; height=&quot;1007&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;ol start=&quot;4&quot;&gt;
&lt;li&gt;Right click the positive and choose Add negative volume &amp;gt; Load… to subtract the negative STL from the positive.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The rectangle in the both positive and negative STLs is for registration. They import with the correct orientation relative to each other, as they are saved. When they are lined up correctly, the registration tabs negate each other.&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://forresto.com/media-cache/model-gen-05-prusa-sliced.png-4vpV2XOHZU-1280.avif 1280w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://forresto.com/media-cache/model-gen-05-prusa-sliced.png-4vpV2XOHZU-1280.webp 1280w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://forresto.com/media-cache/model-gen-05-prusa-sliced.png-4vpV2XOHZU-1280.png&quot; alt=&quot;text&quot; width=&quot;1280&quot; height=&quot;986&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;Click &amp;quot;Enter 3D Print Mode&amp;quot; on the &lt;a href=&quot;https://forresto.com/rope-simulation/&quot;&gt;rope simulation web app&lt;/a&gt; to try the new LED socket parameters and multiple STL generation.&lt;/p&gt;
&lt;h2 id=&quot;test-print&quot; tabindex=&quot;-1&quot;&gt;Test print&lt;/h2&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://forresto.com/media-cache/model-gen-06-test.jpg-Unnhmfymk6-1280.avif 1280w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://forresto.com/media-cache/model-gen-06-test.jpg-Unnhmfymk6-1280.webp 1280w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://forresto.com/media-cache/model-gen-06-test.jpg-Unnhmfymk6-1280.jpeg&quot; alt=&quot;text&quot; width=&quot;1280&quot; height=&quot;1280&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;Smaller test print, with the sockets scaled for some bigger LEDs that I wanted test.&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://forresto.com/media-cache/model-gen-07-test-led.jpg-4cFpFmhV7e-1280.avif 1280w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://forresto.com/media-cache/model-gen-07-test-led.jpg-4cFpFmhV7e-1280.webp 1280w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://forresto.com/media-cache/model-gen-07-test-led.jpg-4cFpFmhV7e-1280.jpeg&quot; alt=&quot;text&quot; width=&quot;1280&quot; height=&quot;960&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;The white filament absorbs more if the light than I hoped. I need to experiment with clear filament.&lt;/p&gt;
&lt;h2 id=&quot;transparent-pla-test&quot; tabindex=&quot;-1&quot;&gt;Transparent PLA test&lt;/h2&gt;
&lt;p&gt;I have been experimenting with &lt;a href=&quot;https://womp.com&quot;&gt;Womp&lt;/a&gt; again. It&#39;s not parametric CAD, but there are some nice features for quick 3D modeling with Booleans.&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://forresto.com/media-cache/womp-tester-crosssection.png-s2CiBGh1s7-1280.avif 1280w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://forresto.com/media-cache/womp-tester-crosssection.png-s2CiBGh1s7-1280.webp 1280w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://forresto.com/media-cache/womp-tester-crosssection.png-s2CiBGh1s7-1280.png&quot; alt=&quot;Womp cross-section view.&quot; width=&quot;1280&quot; height=&quot;892&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;Womp&#39;s cross-section view mode, where you can move the plane to preview the cross-section of the model.&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://forresto.com/media-cache/test-print-led-socket-wire.jpg-YOHvb65KgM-1280.avif 1280w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://forresto.com/media-cache/test-print-led-socket-wire.jpg-YOHvb65KgM-1280.webp 1280w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://forresto.com/media-cache/test-print-led-socket-wire.jpg-YOHvb65KgM-1280.jpeg&quot; alt=&quot;Wired LEDs inserted&quot; width=&quot;1280&quot; height=&quot;1101&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;Transparent PLA, 1mm outer shell, 20% infill with gyroid pattern, Ultimaker S3.&lt;/p&gt;
&lt;p&gt;The socket design works. The pre-wired LEDs can bend to about 12.6mm, so that will be a base parameter for the final model. For light diffusion, I think it&#39;s good to keep some space for infill between the socket and the outer edge.&lt;/p&gt;
&lt;p&gt;This test print is designed with LED sockets of varying sizes and spacings to see if the premade LED wire can fit. I&#39;ll print it with transparent PLA to test how the light looks from the outside.&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://forresto.com/media-cache/test-transparent-pla-surface.jpg-FLUhfnRYNj-1280.avif 1280w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://forresto.com/media-cache/test-transparent-pla-surface.jpg-FLUhfnRYNj-1280.webp 1280w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://forresto.com/media-cache/test-transparent-pla-surface.jpg-FLUhfnRYNj-1280.jpeg&quot; alt=&quot;Transparent PLA with LED light diffusion test.&quot; width=&quot;1280&quot; height=&quot;961&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;Sanded on left and raw on the right. Unsanded feels nicer, and the sanding doesn&#39;t enhance the light diffusion enough to be worth it.&lt;/p&gt;
&lt;h2 id=&quot;test-print-as-small-lamp&quot; tabindex=&quot;-1&quot;&gt;Test print as small lamp&lt;/h2&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://forresto.com/media-cache/womp-internal-cavity.png-_m1ldPRk4i-1280.avif 1280w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://forresto.com/media-cache/womp-internal-cavity.png-_m1ldPRk4i-1280.webp 1280w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://forresto.com/media-cache/womp-internal-cavity.png-_m1ldPRk4i-1280.png&quot; alt=&quot;Womp screenshot showing interal cavity.&quot; width=&quot;1280&quot; height=&quot;907&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;Building on the version that I milled in week 17, I subtracted a cavity to make a small lamp.&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://forresto.com/media-cache/small-lamp.jpg-iFZ9d0nHCE-1280.avif 1280w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://forresto.com/media-cache/small-lamp.jpg-iFZ9d0nHCE-1280.webp 1280w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://forresto.com/media-cache/small-lamp.jpg-iFZ9d0nHCE-1280.jpeg&quot; alt=&quot;Small lamp with labyrinth design.&quot; width=&quot;1280&quot; height=&quot;1280&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;Future nightlight.&lt;/p&gt;
&lt;h1 id=&quot;integration&quot; tabindex=&quot;-1&quot;&gt;Integration&lt;/h1&gt;
&lt;p&gt;With material and LED mounting questions answered, it&#39;s time to go on to the rest of the integration plan.&lt;/p&gt;
&lt;h2 id=&quot;plan-a&quot; tabindex=&quot;-1&quot;&gt;Plan A&lt;/h2&gt;
&lt;ul class=&quot;task-list&quot;&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_0&quot; checked=&quot;true&quot; disabled=&quot;true&quot;&gt;&lt;label for=&quot;cbx_0&quot;&gt; Continue tweaking the 3d design and testing prints.&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_1&quot; disabled=&quot;true&quot;&gt;&lt;label for=&quot;cbx_1&quot;&gt; Hinge and clasp design&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_2&quot; disabled=&quot;true&quot;&gt;&lt;label for=&quot;cbx_2&quot;&gt; Electronics platform, battery holder&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_3&quot; disabled=&quot;true&quot;&gt;&lt;label for=&quot;cbx_3&quot;&gt; v1 of Arduino software&lt;/label&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;plan-b&quot; tabindex=&quot;-1&quot;&gt;Plan B&lt;/h2&gt;
&lt;p&gt;If I talk myself out of embedding the LEDs on the inside of the ball, and thinking that I&#39;ll go back to the soft design. I can still use the generated 3d print as the internal case for the electronics.&lt;/p&gt;
&lt;ul class=&quot;task-list&quot;&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_4&quot; disabled=&quot;true&quot;&gt;&lt;label for=&quot;cbx_4&quot;&gt; Hinge and clasp design for internal case.&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_5&quot; disabled=&quot;true&quot;&gt;&lt;label for=&quot;cbx_5&quot;&gt; Battery holder&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_6&quot; disabled=&quot;true&quot;&gt;&lt;label for=&quot;cbx_6&quot;&gt; Sewing soft outer case&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_7&quot; disabled=&quot;true&quot;&gt;&lt;label for=&quot;cbx_7&quot;&gt; v1 of Arduino software&lt;/label&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;plan-c&quot; tabindex=&quot;-1&quot;&gt;Plan C&lt;/h2&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://forresto.com/media-cache/plan-b-lol.jpg-oX6KcGo4PB-1280.avif 1280w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://forresto.com/media-cache/plan-b-lol.jpg-oX6KcGo4PB-1280.webp 1280w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://forresto.com/media-cache/plan-b-lol.jpg-oX6KcGo4PB-1280.jpeg&quot; alt=&quot;Light labyrinth Plan B illustration.&quot; width=&quot;1280&quot; height=&quot;1450&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;A gimbal like this for the middle of the softball-sized labyrinth ball. Instead of compass, a single focused LED shining up ▲ with battery down ▼ for weight. It will always shine up, so you could manually make the light follow the path. Throw out the microcontroller and LED strips. Done.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Week 15. Interface and Application Programming</title>
    <link href="https://forresto.com/fab-academy/15-pixel-ball-app.html" />
    <updated>2025-05-06T00:00:00Z</updated>
    <id>https://forresto.com/fab-academy/15-pixel-ball-app.html</id>
    <content type="html">&lt;p&gt;&lt;em&gt;Building an interface for calibrating &lt;a href=&quot;https://forresto.com/fab-academy/tactile-labyrinth.html&quot;&gt;a ball of addressable LEDs&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://forresto.com/media-cache/pixel-ball-calibration-app.png--z6rB9CiTg-1056.avif 1056w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://forresto.com/media-cache/pixel-ball-calibration-app.png--z6rB9CiTg-1056.webp 1056w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://forresto.com/media-cache/pixel-ball-calibration-app.png--z6rB9CiTg-1056.png&quot; alt=&quot;Screenshot of the calibration app with dots representing LEDs around a sphere.&quot; width=&quot;1056&quot; height=&quot;1284&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://forresto.com/pixel-ball/&quot;&gt;Pixel Ball Calibration App&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;💡 Note that this &amp;quot;&lt;a href=&quot;https://forresto.com/tags/readme.html&quot;&gt;readme driven development&lt;/a&gt;&amp;quot; development strategy fits well with &amp;quot;document as you go.&amp;quot; By having this markdown file as well as the html, js, and ino files in the LLM context, it becomes easier to debug issues across these boundaries. Then it is one step to commit and publish changes.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id=&quot;intro&quot; tabindex=&quot;-1&quot;&gt;Intro&lt;/h1&gt;
&lt;p&gt;During &lt;a href=&quot;https://forresto.com/fab-academy/13-midterm-plan.html&quot;&gt;week 13&lt;/a&gt; I iterated on two methods for mapping positions of an LED string around a ball. The first version used a math function to estimate the positions of the LEDs. The second version involved making a list of key positions, moving a white pixel around until the index was close to the estimated position, and then having the code interpolating the LED positions between those mapped indexes. This involved more manual work, and ended up being more accurate, but still ended up with some funky pixels.&lt;/p&gt;
&lt;p&gt;As I was working through these versions, I had the ball stationary on the desk, and I moved the accelerometer independently to test the calibration. Only once I had a mapping that seemed correct did I integrate the cardboard prototype.&lt;/p&gt;
&lt;p&gt;Once I had the wireless version in my hands with the central accelerometer, and through a conversation with Kris, I thought of a new way to do the calibration process.&lt;/p&gt;
&lt;p&gt;For every pixel, or every nth pixel:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Send a serial message with the LED index.&lt;/li&gt;
&lt;li&gt;Blink red for 3 seconds.&lt;/li&gt;
&lt;li&gt;The person moves the ball until that pixel is on top.&lt;/li&gt;
&lt;li&gt;Shine green for 1 second.&lt;/li&gt;
&lt;li&gt;The accelerometer value (which is the ball&#39;s orientation) is then stored for that pixel.&lt;/li&gt;
&lt;li&gt;Once the whole LED string is calibrated, a list is sent via serial message, that can be hardcoded into the positioning library and used in other Arduino programs for the ball.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This calibration program will be more flexible than the previous versions, and will be better suited for the organic simulated shapes that I hope will be the final form. Redesigning the LED path, or accelerometer position, will require another calibration pass. But I think it will be easier and more enjoyable than my past versions. This will let me iterate on the design.&lt;/p&gt;
&lt;h1 id=&quot;spec&quot; tabindex=&quot;-1&quot;&gt;Spec&lt;/h1&gt;
&lt;p&gt;A web app will listen to the serial messages via WebSerial, as in &lt;a href=&quot;https://forresto.com/fab-academy/12-machine-building.html&quot;&gt;week 12&#39;s machine interface&lt;/a&gt;. The accelerometer&#39;s orientation will be visualized, and each LED position as it is mapped. The webapp will be able to query the device for any existing mapping, and be able to skip to any index.&lt;/p&gt;
&lt;h2 id=&quot;previous-code&quot; tabindex=&quot;-1&quot;&gt;Previous code&lt;/h2&gt;
&lt;p&gt;For reference. Don&#39;t change these files.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://forresto.com/fab-academy/tactile-labyrinth/TactileLabyrinthManualMapping/TactileLabyrinthManualMapping.ino&quot;&gt;TactileLabyrinthManualMapping.ino&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://forresto.com/fab-academy/tactile-labyrinth/TactileLabyrinthManualMapping/SphericalLEDMap.h&quot;&gt;SphericalLEDMap.h&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;this-version&quot; tabindex=&quot;-1&quot;&gt;This version&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://forresto.com/fab-academy/tactile-labyrinth/PixelBallCalibration/PixelBallCalibration.ino&quot;&gt;PixelBallCalibration.ino&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://forresto.com/fab-academy/tactile-labyrinth/PixelBallCalibration/PixelBallLEDMap.h&quot;&gt;PixelBallLEDMap.h&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;initial-prompt&quot; tabindex=&quot;-1&quot;&gt;Initial prompt&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Without writing code, help me fill out subsections of this markdown spec. We have a working version, but want to redesign the calibration flow entirely. We also want to make a web visualization to make the process more interactive. Once all of the sections are filled out, we will have enough of a spec to start implementing.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id=&quot;conclusion&quot; tabindex=&quot;-1&quot;&gt;Conclusion&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://forresto.com/pixel-ball/readme.md&quot;&gt;readme.md&lt;/a&gt;: the whole readme initialized and edited through the development process.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://forresto.com/pixel-ball/index.html&quot;&gt;index.html&lt;/a&gt;: the calibration app&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Building on my short experience with &amp;quot;&lt;a href=&quot;https://forresto.com/tags/readme.html&quot;&gt;readme driven development&lt;/a&gt;&amp;quot; I can see that this development flow is allowing me take on larger chunks of work. This app and documentation took 5 hours.&lt;/p&gt;
&lt;p&gt;My modus operandi with maker projects in the past has been to relentlessly simplifiy. Does it really need to be wireless? Let&#39;s make it easier by just using a wire. Does it really need a microcontroller? Let&#39;s just wire the buttons to the output directly.&lt;/p&gt;
&lt;p&gt;I still think simplification is a valuable maker skill, but now when the concept does demand a certain level of complexity, I&#39;m feeling more ready to take on the challenge.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Week 14. Molding and Casting</title>
    <link href="https://forresto.com/fab-academy/14-molding-casting.html" />
    <updated>2025-04-30T00:00:00Z</updated>
    <id>https://forresto.com/fab-academy/14-molding-casting.html</id>
    <content type="html">&lt;p&gt;Using &lt;a href=&quot;https://kartta.hel.fi/3d/&quot;&gt;openly available 3D data for Helsinki&lt;/a&gt;, I set out to make an ice version of a neighborhood.&lt;/p&gt;
&lt;h1 id=&quot;side-track%2C-3d-print&quot; tabindex=&quot;-1&quot;&gt;Side track, 3D print&lt;/h1&gt;
&lt;p&gt;The 3d files that are downloadable from the city have come with separate terrain and buildings. I did some work in Blender to combine and prep these files, but I&#39;m not fully satisfied with that workflow.&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://forresto.com/media-cache/neighborhood-3d-print-shadows.jpg-6ZFc9OdWMT-1280.avif 1280w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://forresto.com/media-cache/neighborhood-3d-print-shadows.jpg-6ZFc9OdWMT-1280.webp 1280w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://forresto.com/media-cache/neighborhood-3d-print-shadows.jpg-6ZFc9OdWMT-1280.jpeg&quot; alt=&quot;Neighborhood printed with white PLA, showing scale shadows in the sun.&quot; width=&quot;1280&quot; height=&quot;1279&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;Using the same model, I started a 3d print at the same scale, to be able to compare the process.&lt;/p&gt;
&lt;p&gt;This was inspired in part by &lt;a href=&quot;https://fabacademy.org/2025/labs/isafjordur/Machine_week/outcome/&quot;&gt;Fab Lab Ísafjörður&#39;s Days of darkness&lt;/a&gt; project. I really like the concept of simulating the sun&#39;s path on a miniature model of buildings and terrain.&lt;/p&gt;
&lt;h1 id=&quot;mold-making&quot; tabindex=&quot;-1&quot;&gt;Mold making&lt;/h1&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://forresto.com/media-cache/mold-milling-xy.jpg-_ZUF7J-oC0-1280.avif 1280w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://forresto.com/media-cache/mold-milling-xy.jpg-_ZUF7J-oC0-1280.webp 1280w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://forresto.com/media-cache/mold-milling-xy.jpg-_ZUF7J-oC0-1280.jpeg&quot; alt=&quot;6mm mill, calibrating XY position.&quot; width=&quot;1280&quot; height=&quot;960&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;Calibrating the Roland mill&#39;s XY position to the corner of the wax blank.&lt;/p&gt;
&lt;p&gt;We use carving (jeweler&#39;s) wax, which machines well and the chips can be collected and recycled by melting them down to make new blanks.&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://forresto.com/media-cache/mold-milling-3mm.jpg-EFj39ldqmR-1280.avif 1280w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://forresto.com/media-cache/mold-milling-3mm.jpg-EFj39ldqmR-1280.webp 1280w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://forresto.com/media-cache/mold-milling-3mm.jpg-EFj39ldqmR-1280.jpeg&quot; alt=&quot;3mm mill, finishing pass.&quot; width=&quot;1280&quot; height=&quot;960&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;After the 6mm tool&#39;s roughing pass, we did one finishing pass with the 3mm tool. Each pass took 2 hours, so I&#39;m glad that the milling process succeeded.&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://forresto.com/media-cache/mold-negative-space-measure.jpg-luIUAkBOMZ-1280.avif 1280w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://forresto.com/media-cache/mold-negative-space-measure.jpg-luIUAkBOMZ-1280.webp 1280w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://forresto.com/media-cache/mold-negative-space-measure.jpg-luIUAkBOMZ-1280.jpeg&quot; alt=&quot;text&quot; width=&quot;1280&quot; height=&quot;960&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;By filling the negative space of the mold with water, we can measure how much silicone we&#39;ll need.&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://forresto.com/media-cache/smooth-on-molding-supplies.jpg-DSAfTcYnEM-1280.avif 1280w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://forresto.com/media-cache/smooth-on-molding-supplies.jpg-DSAfTcYnEM-1280.webp 1280w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://forresto.com/media-cache/smooth-on-molding-supplies.jpg-DSAfTcYnEM-1280.jpeg&quot; alt=&quot;Aalto studios casting room setup.&quot; width=&quot;1280&quot; height=&quot;960&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;Wooden stirring sticks, vacuum chamber, and Smooth-On molding and casting supplies in the molding and casting room at Aalto Studios.&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://forresto.com/media-cache/silicone-mixing.jpg-gMdpYgoKUr-1280.avif 1280w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://forresto.com/media-cache/silicone-mixing.jpg-gMdpYgoKUr-1280.webp 1280w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://forresto.com/media-cache/silicone-mixing.jpg-gMdpYgoKUr-1280.jpeg&quot; alt=&quot;Mixing the two parts of silicone together.&quot; width=&quot;1280&quot; height=&quot;1130&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Mix each of the 2 parts in their own jar for 2 minutes, using different sticks. Be sure to scrape sides and bottom.&lt;/li&gt;
&lt;li&gt;Combine the two parts in the measuring cup, using 50% of the volume that we measured with water of each. Plus a little bit to account for what will be lost on the stick and sides of container.&lt;/li&gt;
&lt;li&gt;Mix the 2 parts together with a new stick for 2 minutes, scraping sides and bottom.&lt;/li&gt;
&lt;li&gt;Put the cup in the vacuum chamber and pull a vacuum until the mixture starts to bubble out. When it looks like it will overflow, slowly release the valve until the level goes down in the cup. Repeat until the bubbling is done.&lt;/li&gt;
&lt;li&gt;Pour into the mold with a thin, steady stream. This prevents the flow from trapping bubbles.&lt;/li&gt;
&lt;li&gt;Tap the table to encourage any bubbles to come to the surface.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;video controls=&quot;&quot; src=&quot;https://forresto.com/fab-academy/14-molding-casting/silicone-pouring.mp4&quot; title=&quot;Pouring silicone into the wax mold.&quot;&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;The blob!&lt;/em&gt; Video is sped up 4x.&lt;/p&gt;
&lt;p&gt;At this point it was the end of the day. I debated bringing the mold home with me on the metro, but decided to let it cure at the Fablab.&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://forresto.com/media-cache/silicone-mold.jpg-jPfH--Jo8m-1280.avif 1280w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://forresto.com/media-cache/silicone-mold.jpg-jPfH--Jo8m-1280.webp 1280w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://forresto.com/media-cache/silicone-mold.jpg-jPfH--Jo8m-1280.jpeg&quot; alt=&quot;text&quot; width=&quot;1280&quot; height=&quot;895&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;The next day, it looked and felt like a success. I pulled the edge of the silicone a little bit, and blew compressed air in to encourage it out. I was a little worried that the thin buildings might break while removing the casting, but it came out without issue.&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://forresto.com/media-cache/neighborhood-ice.jpg-Um1eb7F2yt-1280.avif 1280w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://forresto.com/media-cache/neighborhood-ice.jpg-Um1eb7F2yt-1280.webp 1280w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://forresto.com/media-cache/neighborhood-ice.jpg-Um1eb7F2yt-1280.jpeg&quot; alt=&quot;Ice neighborhood.&quot; width=&quot;1280&quot; height=&quot;791&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.smooth-on.com/products/mold-star-15-slow/&quot;&gt;Mold Star™ 15 SLOW&lt;/a&gt; is not &lt;a href=&quot;https://www.smooth-on.com/page/food-safe-materials-list/&quot;&gt;food safe&lt;/a&gt;, so I will not eat the ice neighborhood.&lt;/p&gt;
&lt;p&gt;&lt;video controls=&quot;&quot; autoplay=&quot;&quot; loop=&quot;&quot; muted=&quot;&quot; src=&quot;https://forresto.com/fab-academy/14-molding-casting/neighborhood-ice-melting.mp4&quot; title=&quot;Ice neighborhood melting.&quot;&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;Video is sped up 120x.&lt;/p&gt;
&lt;h1 id=&quot;files&quot; tabindex=&quot;-1&quot;&gt;Files&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://forresto.com/fab-academy/14-molding-casting/mh.stl.zip&quot;&gt;mh.stl.zip&lt;/a&gt; – 3d data that was used for both the print and mold. I would do some cleanup work before 3d printing it again.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&quot;reference&quot; tabindex=&quot;-1&quot;&gt;Reference&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://kartta.hel.fi/3d/&quot;&gt;3D data for Helsinki&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://wiki.aalto.fi/display/AF/3D+Milling+with+Roland+Modela+MDX-40&quot;&gt;Roland Modela MDX-40&lt;/a&gt; – Aalto Fablab wiki&lt;/li&gt;
&lt;/ul&gt;
</content>
  </entry>
  <entry>
    <title>Week 13. Midterm Plan</title>
    <link href="https://forresto.com/fab-academy/13-midterm-plan.html" />
    <updated>2025-04-23T00:00:00Z</updated>
    <id>https://forresto.com/fab-academy/13-midterm-plan.html</id>
    <content type="html">&lt;p&gt;&lt;a href=&quot;https://forresto.com/fab-academy/tactile-labyrinth.html&quot;&gt;Tactile Labyrinth&lt;/a&gt; remaining steps.&lt;/p&gt;
&lt;ul class=&quot;task-list&quot;&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_0&quot; checked=&quot;true&quot; disabled=&quot;true&quot;&gt;&lt;label for=&quot;cbx_0&quot;&gt; Checklist of remaining steps.&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_1&quot; checked=&quot;true&quot; disabled=&quot;true&quot;&gt;&lt;label for=&quot;cbx_1&quot;&gt; Integrate cardboard prototype version&lt;/label&gt;
&lt;ul class=&quot;task-list&quot;&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_2&quot; checked=&quot;true&quot; disabled=&quot;true&quot;&gt;&lt;label for=&quot;cbx_2&quot;&gt; Map LED index to position&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_3&quot; checked=&quot;true&quot; disabled=&quot;true&quot;&gt;&lt;label for=&quot;cbx_3&quot;&gt; 2024-04-23 – Tune test pattern&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_4&quot; checked=&quot;true&quot; disabled=&quot;true&quot;&gt;&lt;label for=&quot;cbx_4&quot;&gt; 2024-04-24 – Integrate accelerometer with test pattern&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_5&quot; checked=&quot;true&quot; disabled=&quot;true&quot;&gt;&lt;label for=&quot;cbx_5&quot;&gt; 2024-04-25 – Walking pixel animation&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_6&quot; checked=&quot;true&quot; disabled=&quot;true&quot;&gt;&lt;label for=&quot;cbx_6&quot;&gt; 2024-04-26 – Choose and add battery bank&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_7&quot; checked=&quot;true&quot; disabled=&quot;true&quot;&gt;&lt;label for=&quot;cbx_7&quot;&gt; 2024-04-27 – Play testing with friends and family&lt;/label&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_8&quot; disabled=&quot;true&quot;&gt;&lt;label for=&quot;cbx_8&quot;&gt; Integrate final version&lt;/label&gt;
&lt;ul class=&quot;task-list&quot;&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_9&quot; disabled=&quot;true&quot;&gt;&lt;label for=&quot;cbx_9&quot;&gt; Circuit design (breakout power switch, charge port)&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_10&quot; disabled=&quot;true&quot;&gt;&lt;label for=&quot;cbx_10&quot;&gt; Internal case design&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_11&quot; disabled=&quot;true&quot;&gt;&lt;label for=&quot;cbx_11&quot;&gt; 3d printing&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_12&quot; disabled=&quot;true&quot;&gt;&lt;label for=&quot;cbx_12&quot;&gt; Soft external form, design&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_13&quot; disabled=&quot;true&quot;&gt;&lt;label for=&quot;cbx_13&quot;&gt; Sewing&lt;/label&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_14&quot; disabled=&quot;true&quot;&gt;&lt;label for=&quot;cbx_14&quot;&gt; Add system diagram to project page.&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_15&quot; disabled=&quot;true&quot;&gt;&lt;label for=&quot;cbx_15&quot;&gt; BOM&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_16&quot; disabled=&quot;true&quot;&gt;&lt;label for=&quot;cbx_16&quot;&gt; Work towards &lt;a href=&quot;https://academy.cba.mit.edu/classes/project_presentation/index.html&quot;&gt;final requirements&lt;/a&gt;&lt;/label&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&quot;project-overview&quot; tabindex=&quot;-1&quot;&gt;Project Overview&lt;/h1&gt;
&lt;p&gt;A tangible spherical object with LED lights forming a labyrinth pattern that responds to physical interaction (turning, tilting, etc.).&lt;/p&gt;
&lt;h1 id=&quot;hardware-requirements&quot; tabindex=&quot;-1&quot;&gt;Hardware Requirements&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;Seeed Studio XIAO RP2040 microcontroller&lt;/li&gt;
&lt;li&gt;6DoF accelerometer and gyroscope – Adafruit LSM6DSOX with STEMMA QT&lt;/li&gt;
&lt;li&gt;LED rope with 250 addressable RGB LEDs – WS2812B chip&lt;/li&gt;
&lt;li&gt;Power supply (USB/battery considerations)&lt;/li&gt;
&lt;li&gt;Tactile housing/structure to support the LED rope in a spherical pattern&lt;/li&gt;
&lt;li&gt;Button input (existing in current code)&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&quot;libraries&quot; tabindex=&quot;-1&quot;&gt;Libraries&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;FastLED - For controlling the LED string&lt;/li&gt;
&lt;li&gt;Wire - For I2C communication with the accelerometer&lt;/li&gt;
&lt;li&gt;Adafruit_LSM6DS - For the specific accelerometer/gyroscope&lt;/li&gt;
&lt;li&gt;Adafruit_Sensor - Required for the LSM6DS library&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&quot;core-functionality-requirements&quot; tabindex=&quot;-1&quot;&gt;Core Functionality Requirements&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Map the physical layout of the LED rope around the sphere&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;How is the rope physically arranged? Linear mapping or something more complex?
&lt;ul&gt;
&lt;li&gt;It starts at one pole, wraps around the sphere in a coil to the other pole, then doubles back and goes between the first coil, without overlapping, back to the first pole. So it starts and ends at the same place.&lt;/li&gt;
&lt;li&gt;I think that I can make a sparse array with polar positions at arbitrary indexes, and the other positions interpolated.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Does the LED indexing follow the actual labyrinth pattern logically?
&lt;ul&gt;
&lt;li&gt;Yes. As the LED rope wraps around the ball, that path (index 0 to 249) is the path of the labyrinth in animations.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Motion-based interaction&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;How should the sphere respond when tilted in different directions?
&lt;ul&gt;
&lt;li&gt;For the first step, color the bottom of the sphere blue, and the top green.&lt;/li&gt;
&lt;li&gt;This should work at any orientation.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;What data from the accelerometer/gyroscope is most relevant (orientation vs. movement)?
&lt;ul&gt;
&lt;li&gt;At first, orientation.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;How quickly should the LEDs respond to movement?
&lt;ul&gt;
&lt;li&gt;Should feel very responsive, like turning a ball filled halfway with water. But it&#39;s a little planet. :)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Animation patterns&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;What types of animations should be implemented?
&lt;ul&gt;
&lt;li&gt;Simple to start. Maybe the animation code could feel like shader code. A function of the index, polar position, and time returns a color. The function called once per pixel.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Should different areas of the labyrinth behave differently?&lt;/li&gt;
&lt;li&gt;How to implement the &amp;quot;character&amp;quot; moving along the labyrinth path?
&lt;ul&gt;
&lt;li&gt;Later, our &amp;quot;pilgrim pixel&amp;quot; will walk along the rope, and goal will be to keep him on top.&lt;/li&gt;
&lt;li&gt;We don&#39;t need to program an error or game over. Just keep the one pixel moving at a slow steady pace.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Game mechanics – &lt;em&gt;think about this section later&lt;/em&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;How does the &amp;quot;character&amp;quot; (represented by one LED) navigate the labyrinth?&lt;/li&gt;
&lt;li&gt;What are the winning/losing conditions?&lt;/li&gt;
&lt;li&gt;Is there a time element or challenge?&lt;/li&gt;
&lt;li&gt;How does the &amp;quot;water&amp;quot; concept integrate (mentioned in the blue hemisphere idea)?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&quot;technical-questions-to-resolve&quot; tabindex=&quot;-1&quot;&gt;Technical Questions to Resolve&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Power requirements and optimization&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;How to manage battery life if portable?
&lt;ul&gt;
&lt;li&gt;Integrate a battery bank that has its own charging. Make sure case has sufficient airflow.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;How to optimize brightness vs. power consumption?
&lt;ul&gt;
&lt;li&gt;Single &lt;code&gt;BRIGHTNESS&lt;/code&gt; in the code&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Memory considerations&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Will the RP2040 have enough memory for all animations and states?&lt;/li&gt;
&lt;li&gt;How to efficiently store and access the LED mapping?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Performance concerns&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;What&#39;s the optimal refresh rate for smooth animations?
&lt;ul&gt;
&lt;li&gt;Try 100hz&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;How to handle multiple simultaneous processes (reading sensors, updating LEDs)?
&lt;ul&gt;
&lt;li&gt;By adding a &lt;code&gt;setup1()&lt;/code&gt; and &lt;code&gt;loop1()&lt;/code&gt; function to your sketch you can make use of the second core. Anything called from within the &lt;code&gt;setup1()&lt;/code&gt; or &lt;code&gt;loop1()&lt;/code&gt; routines will execute on the second core. &lt;code&gt;setup()&lt;/code&gt; and &lt;code&gt;setup1()&lt;/code&gt; will be called at the same time, and the &lt;code&gt;loop()&lt;/code&gt; or &lt;code&gt;loop1()&lt;/code&gt; will be started as soon as the core’s &lt;code&gt;setup()&lt;/code&gt; completes (i.e. not necessarily simultaneously!).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Calibration process&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;How will the device be calibrated for orientation?&lt;/li&gt;
&lt;li&gt;Does it need a &amp;quot;home&amp;quot; position?
&lt;ul&gt;
&lt;li&gt;Position at startup will be defined as up.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&quot;ux-considerations&quot; tabindex=&quot;-1&quot;&gt;UX Considerations&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Feedback mechanisms&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;How does the user know their actions are affecting the system?&lt;/li&gt;
&lt;li&gt;What visual cues indicate progress or state changes?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Intuitiveness&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;How can the interaction be made immediately understandable?&lt;/li&gt;
&lt;li&gt;Is there a learning curve, and if so, how to minimize it?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Engagement&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;What makes the experience engaging beyond the initial novelty?&lt;/li&gt;
&lt;li&gt;How to encourage continued interaction?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&quot;physical-design-questions&quot; tabindex=&quot;-1&quot;&gt;Physical Design Questions&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Structure&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;What material for the final sphere structure?&lt;/li&gt;
&lt;li&gt;How to ensure the LEDs stay fixed in the labyrinth pattern?&lt;/li&gt;
&lt;li&gt;How to provide access to components for maintenance?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Tactile elements&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Beyond the LED rope, what other tactile elements could enhance the experience?&lt;/li&gt;
&lt;li&gt;Should the surface have texture or relief?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Size considerations&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;What is the optimal diameter for handheld interaction?&lt;/li&gt;
&lt;li&gt;Weight considerations for comfortable handling?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&quot;future-development-paths&quot; tabindex=&quot;-1&quot;&gt;Future Development Paths&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Connectivity&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Could it connect to other devices or installations?&lt;/li&gt;
&lt;li&gt;Potential for multiplayer or social elements?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Customization&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;User-defined patterns or games?&lt;/li&gt;
&lt;li&gt;Different difficulty levels or modes?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Additional sensors&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Would other types of sensors add meaningful interaction?&lt;/li&gt;
&lt;li&gt;Proximity sensors, touch sensors, etc.?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&quot;implementation-plan&quot; tabindex=&quot;-1&quot;&gt;Implementation Plan&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Prioritized feature list&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Core functionality vs. &amp;quot;nice to have&amp;quot; features&lt;/li&gt;
&lt;li&gt;Minimum viable product definition&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Testing methodology&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;How to test different aspects (hardware, software, user experience)&lt;/li&gt;
&lt;li&gt;What metrics define success?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Development phases&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;What&#39;s the logical order of implementation?&lt;/li&gt;
&lt;li&gt;Key milestones and dependencies?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&quot;planning-prompt&quot; tabindex=&quot;-1&quot;&gt;Planning prompt&lt;/h1&gt;
&lt;pre class=&quot;language-md&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-md&quot;&gt;&lt;span class=&quot;token blockquote punctuation&quot;&gt;&gt;&lt;/span&gt; Here&#39;s my concept for a tangible labyrinth.
&lt;span class=&quot;token blockquote punctuation&quot;&gt;&gt;&lt;/span&gt; ./tactile-labyrinth.md
&lt;span class=&quot;token blockquote punctuation&quot;&gt;&gt;&lt;/span&gt; And here&#39;s the very early Arduino code that I have.
&lt;span class=&quot;token blockquote punctuation&quot;&gt;&gt;&lt;/span&gt; ./tactile-labyrinth/TactileLabyrinthByForresto.ino
&lt;span class=&quot;token blockquote punctuation&quot;&gt;&gt;&lt;/span&gt; Help me make a structure for a readme.md file to fill in all of the unknowns for the project to make a full spec. Add sections as needed, and bulletpoint questions to answer. Don&#39;t write code until the readme is ready.
&lt;span class=&quot;token blockquote punctuation&quot;&gt;&gt;&lt;/span&gt; ./13-midterm-plan.md&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Sent to Claude 3.7 &amp;quot;Thinking&amp;quot; via Zed.&lt;/p&gt;
&lt;h1 id=&quot;prototype-integration-progress&quot; tabindex=&quot;-1&quot;&gt;Prototype integration progress&lt;/h1&gt;
&lt;h2 id=&quot;led-mapping&quot; tabindex=&quot;-1&quot;&gt;LED Mapping&lt;/h2&gt;
&lt;p&gt;Arduino test code: &lt;a href=&quot;https://forresto.com/fab-academy/tactile-labyrinth/TactileLabyrinthMappingTest/TactileLabyrinthMappingTest.ino&quot;&gt;TactileLabyrinthMappingTest.ino&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://forresto.com/media-cache/desmos-led-path.png-WpXCCTJY3E-1280.avif 1280w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://forresto.com/media-cache/desmos-led-path.png-WpXCCTJY3E-1280.webp 1280w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://forresto.com/media-cache/desmos-led-path.png-WpXCCTJY3E-1280.png&quot; alt=&quot;Path of LED rope approximated in Desmos.&quot; width=&quot;1280&quot; height=&quot;1167&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.desmos.com/3d/x40d4kqrma&quot;&gt;Desmos graph with approximate LED path&lt;/a&gt;.&lt;/p&gt;
&lt;pre class=&quot;language-cpp&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-cpp&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Function to calculate position for LED along the double coil&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getPosition&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;float&lt;/span&gt; s&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;float&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt; x&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;float&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt; y&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;float&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt; z&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;float&lt;/span&gt; a &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3.75&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;// Controls number of turns&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// Determine which half we&#39;re in&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;float&lt;/span&gt; sign &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;s &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1.0&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1.0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// Map s to t (0-1) for each half&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;float&lt;/span&gt; t &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;s &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2.0&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;s &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2.0&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1.0&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;s&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// Calculate position using the formula&lt;/span&gt;
    x &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; sign &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;sqrt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1.0&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;t&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;t&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;cos&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;a&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;PI&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;t&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    y &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;sqrt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1.0&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;t&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;t&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;sin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;a&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;PI&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;t&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    z &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; sign &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; t&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Should I precalculate all of the indexes (bigger program memory, smaller startup cost, same running memory) or do this calculation on board on startup?&lt;/p&gt;
&lt;h3 id=&quot;analysis-of-tradeoffs-(claude-3.7)&quot; tabindex=&quot;-1&quot;&gt;Analysis of Tradeoffs (Claude 3.7)&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Factor&lt;/th&gt;
&lt;th&gt;Precalculated in Flash&lt;/th&gt;
&lt;th&gt;Calculated at Startup&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Program Size&lt;/td&gt;
&lt;td&gt;+3,000 bytes in flash&lt;/td&gt;
&lt;td&gt;+150-200 bytes in flash&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RAM Usage&lt;/td&gt;
&lt;td&gt;Same (3,000 bytes)&lt;/td&gt;
&lt;td&gt;Same (3,000 bytes)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Startup Time&lt;/td&gt;
&lt;td&gt;Immediate&lt;/td&gt;
&lt;td&gt;~5-10ms on RP2040&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Flexibility&lt;/td&gt;
&lt;td&gt;Must recompile to change&lt;/td&gt;
&lt;td&gt;Can modify parameters easily&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Why Runtime Calculation Is Better for This Project.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Memory is not a constraint here&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The RP2040 has 264KB RAM and 2MB flash&lt;/li&gt;
&lt;li&gt;3,000 bytes is only ~1% of available RAM&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;RP2040 has hardware floating-point&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Floating-point calculations are relatively fast&lt;/li&gt;
&lt;li&gt;The startup delay will be imperceptible to users&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Development advantages&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Easier to experiment with different patterns&lt;/li&gt;
&lt;li&gt;Can modify parameters without recompiling&lt;/li&gt;
&lt;li&gt;Code is more self-documenting&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Future flexibility&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Potentially store parameters in flash or read from sensors&lt;/li&gt;
&lt;li&gt;Easier to implement different patterns or animations&lt;/li&gt;
&lt;li&gt;Could allow for dynamic reconfiguration&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;rethinking-mapping&quot; tabindex=&quot;-1&quot;&gt;Rethinking mapping&lt;/h2&gt;
&lt;p&gt;Let&#39;s remake this into an interactive process. Using serial commands, we can skip to any LED by index. Then I can find LEDs at key points on the sphere, and create an array. These key points will use polar coordinates, because these are easier to estimate on the ball. Then positions will be calculated from those key points, and interpolated for the rest of the LEDs indecies.&lt;/p&gt;
&lt;h3 id=&quot;how-to-use-this-interactive-mapping-system-(claude-3.7)&quot; tabindex=&quot;-1&quot;&gt;How to Use This Interactive Mapping System (Claude 3.7)&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://forresto.com/fab-academy/tactile-labyrinth/TactileLabyrinthManualMapping/TactileLabyrinthManualMapping.ino&quot;&gt;TactileLabyrinthManualMapping.ino&lt;/a&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Connect to Serial Monitor&lt;/strong&gt; (115200 baud)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Navigate to LEDs:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;led 123&lt;/code&gt; - Jump to LED #123&lt;/li&gt;
&lt;li&gt;&lt;code&gt;next&lt;/code&gt; - Go to next LED&lt;/li&gt;
&lt;li&gt;&lt;code&gt;prev&lt;/code&gt; - Go to previous LED&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Store Key Points:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Find a LED at a known position on your sphere&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;key index theta phi&lt;/code&gt; to store it (example: &lt;code&gt;key 0 0 0&lt;/code&gt; for North Pole)&lt;/li&gt;
&lt;li&gt;Theta is the longitude (0 to 2π), phi is the latitude (0 to π from North to South)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Other Commands:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;list&lt;/code&gt; - Show all mapped key points&lt;/li&gt;
&lt;li&gt;&lt;code&gt;reset&lt;/code&gt; - Clear all key points&lt;/li&gt;
&lt;li&gt;&lt;code&gt;interpolate&lt;/code&gt; - Calculate all LED positions from your key points&lt;/li&gt;
&lt;li&gt;&lt;code&gt;mode&lt;/code&gt; - Toggle between mapping and display modes&lt;/li&gt;
&lt;li&gt;&lt;code&gt;display&lt;/code&gt; - Cycle display modes (all cardinal points, or individual points)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Recommended Key Points to Map:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;North pole: theta doesn&#39;t matter, phi=0&lt;/li&gt;
&lt;li&gt;South pole: theta doesn&#39;t matter, phi=π&lt;/li&gt;
&lt;li&gt;Points around the equator (phi=π/2) at various theta values&lt;/li&gt;
&lt;li&gt;Some points between equator and poles&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The button on the device toggles between mapping mode and display mode.&lt;/p&gt;
&lt;h2 id=&quot;abstracting-mapping&quot; tabindex=&quot;-1&quot;&gt;Abstracting mapping&lt;/h2&gt;
&lt;p&gt;Now I want to abstract the positioning logic so that it can be reused between arduino projects. What&#39;s a good strategy for that?&lt;/p&gt;
&lt;h3 id=&quot;simple-header-file-approach-(claude)&quot; tabindex=&quot;-1&quot;&gt;Simple Header File Approach (Claude)&lt;/h3&gt;
&lt;p&gt;Perfect, let&#39;s create a simple, ready-to-include header file with your key points built in. This is a faster approach and makes sense when you&#39;re working with the same physical object across different projects.&lt;/p&gt;
&lt;p&gt;Here&#39;s a complete, standalone header file that you can include in any Arduino project:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://forresto.com/fab-academy/tactile-labyrinth/TactileLabyrinthManualMapping/SphericalLEDMap.h&quot;&gt;SphericalLEDMap.h&lt;/a&gt;&lt;/p&gt;
&lt;h4 id=&quot;how-to-use-this-in-your-projects%3A&quot; tabindex=&quot;-1&quot;&gt;How to use this in your projects:&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;Save the above code as &lt;code&gt;SphericalLEDMap.h&lt;/code&gt; in your Arduino project folder or libraries folder&lt;/li&gt;
&lt;li&gt;Include it in your sketch&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;integration-with-motion-detection&quot; tabindex=&quot;-1&quot;&gt;Integration with motion detection&lt;/h2&gt;
&lt;p&gt;Arduino test code: &lt;a href=&quot;https://forresto.com/fab-academy/13-midterm-plan.html&quot;&gt;TactileLabyrinthGravityTest.ino&lt;/a&gt;./tactile-labyrinth/TactileLabyrinthGravityTest/TactileLabyrinthGravityTest.ino)&lt;/p&gt;
&lt;p&gt;Which sensors would be cooler, accel and mag, or accel and gyro? Eventually it would be cool to toss and spin the ball, and have the lights react fast enough to do a persistnace of vision effect, keeping colors locked in space as the ball spins in the air. Will latency be fast enough with these chips and sensors?&lt;/p&gt;
&lt;h3 id=&quot;latency-analysis-for-pov-effects-(claude-3.7)&quot; tabindex=&quot;-1&quot;&gt;Latency Analysis for POV Effects (Claude 3.7)&lt;/h3&gt;
&lt;p&gt;For persistence of vision effects where colors need to stay locked in space as the ball spins (especially during tosses), an &lt;strong&gt;accelerometer + gyroscope combination&lt;/strong&gt; is significantly better:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Why Gyroscope is Crucial:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Rotation detection&lt;/strong&gt;: Gyroscopes directly measure rotational velocity - exactly what you need&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Speed&lt;/strong&gt;: Much faster response time (400Hz-1.6kHz) compared to magnetometers (10-100Hz)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Precision&lt;/strong&gt;: More accurate for fast motions and spins&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Less interference&lt;/strong&gt;: Magnetometers are affected by nearby electronics and metals&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For POV to work, your system needs to update faster than the human eye can perceive the change in position:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;Time&lt;/th&gt;
&lt;th&gt;Note&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Sensor reading&lt;/td&gt;
&lt;td&gt;~2.4ms&lt;/td&gt;
&lt;td&gt;At 416Hz data rate (LSM6DSOX)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Processing&lt;/td&gt;
&lt;td&gt;~2ms&lt;/td&gt;
&lt;td&gt;Vector math for 250 LEDs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LED update&lt;/td&gt;
&lt;td&gt;~7.5ms&lt;/td&gt;
&lt;td&gt;Time to push data to 250 WS2812B LEDs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Total latency&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;~12ms&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;Is this fast enough?&lt;/strong&gt; Yes, with some limitations:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A ball spinning at 2 rotations/second (fairly fast) rotates 7.2° in 10ms&lt;/li&gt;
&lt;li&gt;Human visual persistence is roughly 1/30 second (33ms)&lt;/li&gt;
&lt;li&gt;Your system should be fast enough to maintain the illusion&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;matching-accelerometer-orientation-to-led-map&quot; tabindex=&quot;-1&quot;&gt;Matching accelerometer orientation to LED map&lt;/h2&gt;
&lt;p&gt;XYZ on the mapping don&#39;t match XYZ of the accelerometer data. Make a simple way to configure the orientation in software to match the physical layout.&lt;/p&gt;
&lt;h3 id=&quot;accelerometer-configuration-solution-(claude)&quot; tabindex=&quot;-1&quot;&gt;Accelerometer Configuration Solution (Claude)&lt;/h3&gt;
&lt;p&gt;You need a way to match the physical orientation of your accelerometer with the XYZ coordinate system you&#39;re using for LED mapping. Let&#39;s implement a solution that allows you to configure the axis orientation in software.&lt;/p&gt;
&lt;pre class=&quot;language-cpp&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-cpp&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Configure these to match your physical orientation&lt;/span&gt;
AxisMapping ACCEL_X_MAPS_TO &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; AXIS_POSITIVE_X&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;// Which sensor axis maps to your X&lt;/span&gt;
AxisMapping ACCEL_Y_MAPS_TO &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; AXIS_NEGATIVE_Z&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;// Which sensor axis maps to your Y&lt;/span&gt;
AxisMapping ACCEL_Z_MAPS_TO &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; AXIS_POSITIVE_Y&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;// Which sensor axis maps to your Z&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;(Took some trial and error, but they are matched now!)&lt;/p&gt;
&lt;h2 id=&quot;add-the-walking-pixel&quot; tabindex=&quot;-1&quot;&gt;Add the walking pixel&lt;/h2&gt;
&lt;p&gt;Let&#39;s add the wandering monk. A white pixel that steps along the LED rope with a rhythm. Step, step, step step step. The person holding the object will be responsible to keep the monk on the top of the ball. They&#39;ll stop walking and if they enter the bottom of the ball. Or maybe it turns into a black pixel and keeps going, since the bottom of the ball is always lit up. This will be added to the existing Gravity mode, and be renamed Monk Pixel Mode. Keep the current gravity colors, just add the monk pixel.&lt;/p&gt;
&lt;h3 id=&quot;response&quot; tabindex=&quot;-1&quot;&gt;Response&lt;/h3&gt;
&lt;pre class=&quot;language-cpp&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-cpp&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Monk pixel variables&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; monkPosition &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;120&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;unsigned&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;long&lt;/span&gt; lastMonkStep &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token macro property&quot;&gt;&lt;span class=&quot;token directive-hash&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token directive keyword&quot;&gt;define&lt;/span&gt; &lt;span class=&quot;token macro-name&quot;&gt;MONK_STEP_PATTERN_LENGTH&lt;/span&gt; &lt;span class=&quot;token expression&quot;&gt;&lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; monkStepPattern&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;MONK_STEP_PATTERN_LENGTH&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;500&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;500&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;500&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;250&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;250&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// step, step, step step step timing in ms&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; monkStepPatternIndex &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// ... the rest of the file&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The monk now walks along the LED strand with the specified rhythm pattern. When it&#39;s on the top half of the sphere (according to gravity), it appears as a white pixel. When it enters the bottom half, it turns into a black pixel, which creates a visual effect of disappearing (since the bottom is always lit).&lt;/p&gt;
&lt;h2 id=&quot;animation-detail-idea&quot; tabindex=&quot;-1&quot;&gt;Animation detail idea&lt;/h2&gt;
&lt;p&gt;Make the total time of the step pattern change over time. As fast as 20 pixels per second, and as slow as 5. Move smoothly to a new pace every 20 seconds. Explain this code heavily in the comments.&lt;/p&gt;
&lt;p&gt;When black, make the neighboring 2 pixels black as well.&lt;/p&gt;
&lt;h3 id=&quot;dynamic-speed-control-system-(response)&quot; tabindex=&quot;-1&quot;&gt;Dynamic Speed Control System (Response)&lt;/h3&gt;
&lt;p&gt;I&#39;ve implemented a sophisticated speed control system for the monk pixel that gradually changes speeds over time. Here&#39;s a breakdown of how it works:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Speed Range Definition&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The monk&#39;s speed is defined in terms of pixels per second.
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;MIN_SPEED&lt;/code&gt; = 5 pixels per second (slowest)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;MAX_SPEED&lt;/code&gt; = 50 pixels per second (fastest)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Step Pattern System&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The base step pattern (&lt;code&gt;monkStepPatternBase&lt;/code&gt;) defines the relative timing of steps&lt;/li&gt;
&lt;li&gt;The actual step pattern (&lt;code&gt;monkStepPattern&lt;/code&gt;) scales these times based on the current speed&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Two-Tier Speed Control&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Long-term changes&lt;/strong&gt;: Every 20 seconds, a new random target speed is selected&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Smooth transitions&lt;/strong&gt;: The speed gradually changes toward the target in small increments&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Updating Process&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;updateMonkSpeed()&lt;/code&gt;: Called regularly to handle speed transitions and target selection&lt;/li&gt;
&lt;li&gt;&lt;code&gt;updateMonkStepPattern()&lt;/code&gt;: Recalculates the actual step timings based on the current speed&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;04-24-integration-status&quot; tabindex=&quot;-1&quot;&gt;04-24 integration status&lt;/h2&gt;
&lt;p&gt;&lt;video src=&quot;https://forresto.com/fab-academy/tactile-labyrinth/tactile-labyrinth-sensor-and-animation.mp4&quot; controls=&quot;&quot; muted=&quot;&quot;&gt;&lt;/video&gt;&lt;/p&gt;
&lt;h2 id=&quot;04-27-integration-status&quot; tabindex=&quot;-1&quot;&gt;04-27 integration status&lt;/h2&gt;
&lt;p&gt;Wireless and handheld at last.&lt;/p&gt;
&lt;p&gt;&lt;video src=&quot;https://forresto.com/fab-academy/tactile-labyrinth/tactile-labyrinth-cardboard-battery.mp4&quot; controls=&quot;&quot; muted=&quot;&quot;&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;I decided to leave it on today as a battery test, and the 10k mah battery is still going after 8+ hours. Now I&#39;m thinking that halving the battery capacity could be worth the weight savings.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Week 12. Machine Building</title>
    <link href="https://forresto.com/fab-academy/12-machine-building.html" />
    <updated>2025-04-11T00:00:00Z</updated>
    <id>https://forresto.com/fab-academy/12-machine-building.html</id>
    <content type="html">&lt;p&gt;Our group machine building project is distributed between Helsinki and Istanbul. My focus is on making a web-based control panel that will communicate with the machine over WebSerial.&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://forresto.com/media-cache/machine-control-final.png-fa9wiSPW-5-1280.avif 1280w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://forresto.com/media-cache/machine-control-final.png-fa9wiSPW-5-1280.webp 1280w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://forresto.com/media-cache/machine-control-final.png-fa9wiSPW-5-1280.png&quot; alt=&quot;Screenshot of the interface for machine control, with 3d viz of x/y gantry and gripper hanging from a cable.&quot; width=&quot;1280&quot; height=&quot;684&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://forresto.com/grabber-machine/index.html&quot;&gt;Live control app&lt;/a&gt;. Requires Chrome for WebSerial, but you can control the machine visualization in any browser.&lt;/p&gt;
&lt;h1 id=&quot;readme-based-development&quot; tabindex=&quot;-1&quot;&gt;Readme based development&lt;/h1&gt;
&lt;p&gt;&lt;em&gt;(aka &lt;strong&gt;vibe coding&lt;/strong&gt; but that&#39;s a bit cringe)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Using &amp;quot;Claude 3.7 Thinking&amp;quot; via Zed. The first prompt:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Read the readme, and suggest where clarification is needed before writing any code. I will then edit the readme and we can iterate.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;And then I filled in the open questions that came back. By keeping the generated code in a subdirectory, I can throw things out and regenerate files if things go off the rails. The LLM chat log and generated code is less precious, but prompts for changes need to be copied to the Readme, which becomes an important source of truth for the project.&lt;/p&gt;
&lt;p&gt;Link to the &lt;a href=&quot;https://forresto.com/grabber-machine/readme.md&quot;&gt;full Readme&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;llm-limitations-around-spatial-relationships&quot; tabindex=&quot;-1&quot;&gt;LLM limitations around spatial relationships&lt;/h2&gt;
&lt;p&gt;Claude did the GUI and WebSerial machine communication code in &amp;quot;one shot&amp;quot; meaning I didn&#39;t need to give further guidance after filling out the clarifying points in the Readme.&lt;/p&gt;
&lt;p&gt;One area where LLM code still falls short is geometric relationships. As a language model this makes sense, there is only so much reading and writing about geometry can take our knowledge. At some point we need to see (and ideally touch!) geometric forms to root our understanding in the real world.&lt;/p&gt;
&lt;p&gt;These limits were reflected in my evaluation of &lt;a href=&quot;https://forresto.com/fab-academy/02-zoo-modeling-app.html&quot;&gt;Zoo Modeling App&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://forresto.com/media-cache/gripper-1-whack.png-_MW3s2SXJ_-1280.avif 1280w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://forresto.com/media-cache/gripper-1-whack.png-_MW3s2SXJ_-1280.webp 1280w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://forresto.com/media-cache/gripper-1-whack.png-_MW3s2SXJ_-1280.png&quot; alt=&quot;Gripper attempt with parts assembled at incorrect angles.&quot; width=&quot;1280&quot; height=&quot;912&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;Claude&#39;s attempt at making the gripper. The parts are there, but orientation is off by 180° for the shells and 90° for the rim.&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://forresto.com/media-cache/gripper-2-closed.png-dmOpEyDNeD-1280.avif 1280w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://forresto.com/media-cache/gripper-2-closed.png-dmOpEyDNeD-1280.webp 1280w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://forresto.com/media-cache/gripper-2-closed.png-dmOpEyDNeD-1280.png&quot; alt=&quot;Broken gripper closed.&quot; width=&quot;1280&quot; height=&quot;1023&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;The transition to the closed state seems like it will be correct once the initial orientation is correct.&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://forresto.com/media-cache/gripper-3-fixed.png-cwVXJiuZdG-1280.avif 1280w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://forresto.com/media-cache/gripper-3-fixed.png-cwVXJiuZdG-1280.webp 1280w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://forresto.com/media-cache/gripper-3-fixed.png-cwVXJiuZdG-1280.png&quot; alt=&quot;Gripper fixed with human intervention.&quot; width=&quot;1280&quot; height=&quot;830&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;After manually fixing the rotation code, I was interested to see Claude&#39;s take on my fix.&lt;/p&gt;
&lt;pre class=&quot;language-md&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-md&quot;&gt;&lt;span class=&quot;token blockquote punctuation&quot;&gt;&gt;&lt;/span&gt; see how i fixed the rotations of the parts. i don&#39;t blame you because you can&#39;t see, but maybe you should work on being able to see.

&lt;span class=&quot;token blockquote punctuation&quot;&gt;&gt;&lt;/span&gt; js fab-machine-building/public/generated/gripper.js

You&#39;re absolutely right about the rotation fixes - thank you for pointing them out! This is an excellent example of the limitations I face when working with 3D visualization code.

&lt;span class=&quot;token title important&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;##&lt;/span&gt; Your Key Fixes:&lt;/span&gt;

&lt;span class=&quot;token list punctuation&quot;&gt;1.&lt;/span&gt; &lt;span class=&quot;token bold&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;**&lt;/span&gt;&lt;span class=&quot;token content&quot;&gt;Hemisphere Rotation Direction:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;**&lt;/span&gt;&lt;/span&gt;
   // Changed from:
   leftHemisphereGroup.rotation.y = Math.PI / 2;  // My incorrect version
   rightHemisphereGroup.rotation.y = -Math.PI / 2;
   // To:
   leftHemisphereGroup.rotation.y = -Math.PI / 2;  // Your correct version
   rightHemisphereGroup.rotation.y = Math.PI / 2;

&lt;span class=&quot;token list punctuation&quot;&gt;2.&lt;/span&gt; &lt;span class=&quot;token bold&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;**&lt;/span&gt;&lt;span class=&quot;token content&quot;&gt;Edge Ring Rotation:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;**&lt;/span&gt;&lt;/span&gt;
   // Changed from:
   edge.rotation.x = Math.PI / 2;  // 90 degrees (incorrect)
   // To:
   edge.rotation.x = Math.PI;  // 180 degrees (correct)

&lt;span class=&quot;token title important&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;##&lt;/span&gt; On Visualizing 3D Code&lt;/span&gt;

You&#39;ve touched on an important limitation in my capabilities. While I understand the mathematical principles behind 3D transformations and can write the code, I can&#39;t actually see the rendered output to verify that hemispheres are properly oriented or that parts connect correctly.

This is where human expertise is invaluable - being able to see the result and make precise adjustments based on visual feedback. For 3D graphics work, this collaboration between my code generation and your visual verification produces the best results.

Working on &quot;being able to see&quot; would indeed be transformative for my ability to handle spatial and visual tasks. In the meantime, I appreciate you taking the time to correct these visual details that I couldn&#39;t verify myself!&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I guess it is nice that a human with code / geometry knowledge (me) is still needed in the loop.&lt;/p&gt;
&lt;p&gt;It&#39;s hard to capture that subtle difference in the Readme, so at this point I&#39;ve broken the contract of being able to throw out all of the generated code to regenerate it.&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://forresto.com/media-cache/threejs-dev-tools.png-t4uWkhI0n4-1280.avif 1280w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://forresto.com/media-cache/threejs-dev-tools.png-t4uWkhI0n4-1280.webp 1280w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://forresto.com/media-cache/threejs-dev-tools.png-t4uWkhI0n4-1280.png&quot; alt=&quot;Screenshot of Three.js DevTools.&quot; width=&quot;1280&quot; height=&quot;1642&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;Using &lt;a href=&quot;https://chromewebstore.google.com/detail/threejs-devtools/jechbjkglifdaldbdbigibihfaclnkbo&quot;&gt;Three.js DevTools&lt;/a&gt; to inspect the structure of the scene.&lt;/p&gt;
&lt;h2 id=&quot;refactoring-iterations&quot; tabindex=&quot;-1&quot;&gt;Refactoring iterations&lt;/h2&gt;
&lt;p&gt;Talking with the team, we realized that the X and Y axes were switched. I asked the LLM for a refactor:&lt;/p&gt;
&lt;pre class=&quot;language-md&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-md&quot;&gt;&lt;span class=&quot;token blockquote punctuation&quot;&gt;&gt;&lt;/span&gt; I&#39;m changing the X and Y axis, so let&#39;s refactor just the rendering.
&lt;span class=&quot;token blockquote punctuation&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token list punctuation&quot;&gt;-&lt;/span&gt; Y movement is the gantry moving along the frame.
&lt;span class=&quot;token blockquote punctuation&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token list punctuation&quot;&gt;-&lt;/span&gt; X movement is the spindle along the gantry.
&lt;span class=&quot;token blockquote punctuation&quot;&gt;&gt;&lt;/span&gt; js fab-machine-building/public/generated/rendering.js&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I also updated the Readme with those bullet points, since that will be good info for any human or machine that&#39;s reimplementing this in the future.&lt;/p&gt;
&lt;p&gt;The &amp;quot;think&amp;quot; step is new to me, and somewhat interesting. It is hidden in the UI, but available to inspect.&lt;/p&gt;
&lt;details&gt;&lt;summary&gt;&lt;code&gt;&amp;lt;think&amp;gt;&lt;/code&gt; contents...&lt;/summary&gt;
&lt;pre class=&quot;language-md&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-md&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;think&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
I need to refactor the rendering.js file to switch the X and Y axes based on the requirements:
&lt;span class=&quot;token list punctuation&quot;&gt;-&lt;/span&gt; Y movement is now the gantry moving along the frame
&lt;span class=&quot;token list punctuation&quot;&gt;-&lt;/span&gt; X movement is the spindle moving along the gantry

Let&#39;s break down what needs to change:

&lt;span class=&quot;token list punctuation&quot;&gt;1.&lt;/span&gt; The data structure and movement logic stays the same, but the visual representation and what each axis controls changes
&lt;span class=&quot;token list punctuation&quot;&gt;2.&lt;/span&gt; We need to remap the rendering of the axes to match the new convention
&lt;span class=&quot;token list punctuation&quot;&gt;3.&lt;/span&gt; The gantry (previously X carriage) now moves along the Y axis
&lt;span class=&quot;token list punctuation&quot;&gt;4.&lt;/span&gt; The spindle (previously Z axis) now moves along the X axis within the gantry

Here are the key changes I need to make:

&lt;span class=&quot;token list punctuation&quot;&gt;1.&lt;/span&gt; Rename variables to better match the new system
&lt;span class=&quot;token list punctuation&quot;&gt;2.&lt;/span&gt; Update the creation and positioning of machine components
&lt;span class=&quot;token list punctuation&quot;&gt;3.&lt;/span&gt; Modify the updatePhysics function to map the X, Y, Z coordinates correctly to physical movements

Let&#39;s go through the code and make these changes:

&lt;span class=&quot;token list punctuation&quot;&gt;*&lt;/span&gt; Rename &quot;xCarriage&quot; to &quot;yGantry&quot; since it now moves in Y
&lt;span class=&quot;token list punctuation&quot;&gt;*&lt;/span&gt; Update the creation functions to reflect the new axes convention
&lt;span class=&quot;token list punctuation&quot;&gt;*&lt;/span&gt; Adjust how we update the machine position

For the XY frame, nothing changes physically but we should rename for clarity.

For the carriage, we need to change how it moves - now it should move along the Y axis (depth) instead of X.

For the Z axis, it should now be mounted on the yGantry, but move along the X axis.

The update physics logic stays the same, but we need to map the coordinates differently to the visual elements.
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;think&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;
&lt;p&gt;After the think step, it gives a quick introduction, the refactored code, and a summary.&lt;/p&gt;
&lt;pre class=&quot;language-md&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-md&quot;&gt;I&#39;ll refactor the rendering code to reflect the new axis arrangement. Here&#39;s the updated code with Y axis controlling gantry movement along the frame and X axis moving the spindle along the gantry:

...

Key changes:
&lt;span class=&quot;token list punctuation&quot;&gt;1.&lt;/span&gt; Renamed &lt;span class=&quot;token code-snippet code keyword&quot;&gt;`xCarriage`&lt;/span&gt; to &lt;span class=&quot;token code-snippet code keyword&quot;&gt;`yGantry`&lt;/span&gt; to reflect that it now moves along the Y axis
&lt;span class=&quot;token list punctuation&quot;&gt;2.&lt;/span&gt; Renamed &lt;span class=&quot;token code-snippet code keyword&quot;&gt;`zAxis`&lt;/span&gt; to &lt;span class=&quot;token code-snippet code keyword&quot;&gt;`xSpindle`&lt;/span&gt; to reflect that it now moves along the X axis on the gantry
&lt;span class=&quot;token list punctuation&quot;&gt;3.&lt;/span&gt; Updated the &lt;span class=&quot;token code-snippet code keyword&quot;&gt;`createFrame()`&lt;/span&gt;, &lt;span class=&quot;token code-snippet code keyword&quot;&gt;`createYGantry()`&lt;/span&gt;, and &lt;span class=&quot;token code-snippet code keyword&quot;&gt;`createXSpindle()`&lt;/span&gt; functions to better represent the new axis arrangement
&lt;span class=&quot;token list punctuation&quot;&gt;4.&lt;/span&gt; Modified the &lt;span class=&quot;token code-snippet code keyword&quot;&gt;`updatePhysics()`&lt;/span&gt; function to map the coordinates correctly:
   &lt;span class=&quot;token list punctuation&quot;&gt;-&lt;/span&gt; X position now controls the spindle movement along the gantry
   &lt;span class=&quot;token list punctuation&quot;&gt;-&lt;/span&gt; Y position now controls the gantry movement along the frame
   &lt;span class=&quot;token list punctuation&quot;&gt;-&lt;/span&gt; Z position still controls vertical movement

This refactoring maintains the same physics and basic movement but changes how the axes are visualized to match the desired convention.&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I did have to fix one positioning bug, but I was impressed that it almost got it in one shot.&lt;/p&gt;
&lt;h1 id=&quot;conclusion&quot; tabindex=&quot;-1&quot;&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;From a blank slate, total time spent coding, syncing with the machine team in Istanbul, and documenting the process in this post: 5 hours. Most of this time was ironing out visual quirks.&lt;/p&gt;
&lt;p&gt;The models and tools for this kind of augmented development are changing rapidly. I like having this basic protocol that can be used with the tools that I&#39;m already comfortable with. They are not tied to a specific code editor or LLM. Documentation is important, whether collaborating with machines or people, and Readme driven development is a good way to practice those skills.&lt;/p&gt;
&lt;p&gt;I would like to try bringing in sketches and screenshots with a multi-modal modal. Also, there is a lot of room to experiment working with a group, and integrating a wider system. What if we were doing the CAD, machine control code, and system documentation in the same repo, with the whole team?&lt;/p&gt;
&lt;p&gt;This machine visualization didn&#39;t hint how much the grabber swings when the cable (Z axis) is extended. Since I was remote from the team building the actual machine, that&#39;s not something I thought about until I saw their video. I wonder if there is a 3D physics sandbox with machine-building basics, that&#39;s as easy to use as &lt;a href=&quot;https://www.algodoo.com&quot;&gt;Phun&lt;/a&gt;.&lt;/p&gt;
&lt;h1 id=&quot;deliverables&quot; tabindex=&quot;-1&quot;&gt;Deliverables&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://forresto.com/grabber-machine/readme.md&quot;&gt;Readme&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://forresto.com/grabber-machine/index.html&quot;&gt;Live gripper control app&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://gitlab.com/kriwkrow/pico-nc/&quot;&gt;Repo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://fabacademy.org/2025/labs/aalto/&quot;&gt;Video&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;video src=&quot;https://fabacademy.org/2025/labs/aalto/group-assignments/machine-building/aalto_machine-building_draft.mp4&quot; controls=&quot;&quot; muted=&quot;&quot;&gt;&lt;/video&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Week 11. Networking and Communications</title>
    <link href="https://forresto.com/fab-academy/11-networking-and-communications.html" />
    <updated>2025-04-09T00:00:00Z</updated>
    <id>https://forresto.com/fab-academy/11-networking-and-communications.html</id>
    <content type="html">&lt;h1 id=&quot;web-radio&quot; tabindex=&quot;-1&quot;&gt;Web Radio&lt;/h1&gt;
&lt;p&gt;One of my final project ideas was &lt;a href=&quot;https://forresto.com/fab-academy/01-potential-final-project.html#tangible-media-player&quot;&gt;a web radio with token bookmarks to streams and playlists&lt;/a&gt;. For this week I wanted to see if I could get the web stream → ESP → I2S → speaker part working in an afternoon.&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://forresto.com/media-cache/web-radio-breadboard.jpg-Ml1Scft40R-1280.avif 1280w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://forresto.com/media-cache/web-radio-breadboard.jpg-Ml1Scft40R-1280.webp 1280w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://forresto.com/media-cache/web-radio-breadboard.jpg-Ml1Scft40R-1280.jpeg&quot; alt=&quot;Web radio on a breadboard.&quot; width=&quot;1280&quot; height=&quot;1771&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;Using XIAO ESP32C3, &lt;a href=&quot;https://learn.adafruit.com/adafruit-max98357-i2s-class-d-mono-amp/pinouts&quot;&gt;Adafruit max98357 I2S mono amp&lt;/a&gt;, and lofi speaker.&lt;/p&gt;
&lt;p&gt;There was contention on the XIAO forum if XIAO ESP32C3 supports I2S, but it seems to work without any audio issues. I started from the &lt;a href=&quot;https://github.com/pschatzmann/arduino-audio-tools/tree/main/examples/examples-communication/http-client/streams-url_mp3_helix-i2s&quot;&gt;arduino-audio-tools&#39; streams-url_mp3_helix-i2s example&lt;/a&gt;. The only trick to get it working was to configure the I2S pins correctly.&lt;/p&gt;
&lt;pre class=&quot;language-c&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-c&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// LRC&lt;/span&gt;
config&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;pin_ws &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; D0&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// BCLK&lt;/span&gt;
config&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;pin_bck &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; D1&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// DIN&lt;/span&gt;
config&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;pin_data &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; D2&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Using the arduino-audio-tools library feels like cheating a bit, since it abstracts away most of the networking.&lt;/p&gt;
&lt;p&gt;I&#39;m struck how much can be done these days with off-the-shelf breakout board components, without needing to do circuit design or fabrication. This &lt;a href=&quot;https://www.youtube.com/watch?v=Yg7z5wKEo0o&quot;&gt;wall plotter&lt;/a&gt; has clever software and case design, but most of the electronics are stock boards.&lt;/p&gt;
&lt;p&gt;The audio streaming and decoding takes 90% of the XIAO ESP32C3&#39;s memory, so if I want to add NFC reading to the project I might have to use a microcontroller with more memory, or move to an external mp3 decoding board like the &lt;a href=&quot;https://www.instructables.com/Internet-Radio-Using-an-ESP32/&quot;&gt;VS1053&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://forresto.com/media-cache/wxyc-jeopardy.jpg-0qKzxP1tvO-1024.avif 1024w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://forresto.com/media-cache/wxyc-jeopardy.jpg-0qKzxP1tvO-1024.webp 1024w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://forresto.com/media-cache/wxyc-jeopardy.jpg-0qKzxP1tvO-1024.jpeg&quot; alt=&quot;Jeopardy answer: IT&#39;S BELIEVED THAT IN 1994 WXYC IN CHAPEL HILL IN THIS STATE BECAME THE FIRST RADIO STATION TO WEBCAST&quot; width=&quot;1024&quot; height=&quot;783&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;(That&#39;s a funny answer if the question is &amp;quot;what is North Carolina?&amp;quot; but ok.)&lt;/p&gt;
&lt;p&gt;I set up the radio to play my alma mater&#39;s college radio station, &lt;a href=&quot;https://wxyc.org&quot;&gt;WXYC&lt;/a&gt;. The UNC school of library science hosts serveral North Carolina public radio station streams with a directory at &lt;a href=&quot;http://audio-ogg.ibiblio.org:8000&quot;&gt;ibiblio.org&lt;/a&gt;. If I continue the audio player project I&#39;ll make tokens for some of these.&lt;/p&gt;
&lt;h2 id=&quot;arduino-audio-libraries&quot; tabindex=&quot;-1&quot;&gt;Arduino audio libraries&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/pschatzmann/arduino-audio-tools&quot;&gt;arduino-audio-tools&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/pschatzmann/arduino-libhelix&quot;&gt;arduino-libhelix&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&quot;group-work&quot; tabindex=&quot;-1&quot;&gt;Group Work&lt;/h1&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://forresto.com/media-cache/Networking_XIAO_ESP32_Boards.jpg-iHTyBOPcih-1280.avif 1280w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://forresto.com/media-cache/Networking_XIAO_ESP32_Boards.jpg-iHTyBOPcih-1280.webp 1280w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://forresto.com/media-cache/Networking_XIAO_ESP32_Boards.jpg-iHTyBOPcih-1280.jpeg&quot; alt=&quot;XIAO boards for WIFI communication.&quot; width=&quot;1280&quot; height=&quot;703&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://gitlab.com/kriwkrow/networking-with-xiao-esp32-demo&quot;&gt;Wireless Networking with XIAO ESP32C3&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;We looked at the board design and programming to make two boards that can communicate over wifi. Pressing a button on one board sends a request that vibrates the other board.&lt;/p&gt;
&lt;pre class=&quot;language-c&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-c&quot;&gt;server&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/on&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; HTTP_GET&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;AsyncWebServerRequest &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;request&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  localState &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; true&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;updateFromLocalState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  request&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;application/json&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;OK&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

server&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/off&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; HTTP_GET&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;AsyncWebServerRequest &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;request&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  localState &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; false&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;updateFromLocalState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  request&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;application/json&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;OK&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A server that takes requests for &lt;code&gt;/on&lt;/code&gt; and &lt;code&gt;/off&lt;/code&gt; to toggle a vibration buzzer on the board.&lt;/p&gt;
&lt;p&gt;The server code looks quite familiar to me, compared to basic web servers written in Node.&lt;/p&gt;
&lt;h2 id=&quot;networking-libraries&quot; tabindex=&quot;-1&quot;&gt;Networking libraries&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/lacamera/ESPAsyncWebServer&quot;&gt;ESPAsyncWebServer&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/bblanchon/ArduinoJson&quot;&gt;ArduinoJson&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;machine-week-planning&quot; tabindex=&quot;-1&quot;&gt;Machine week planning&lt;/h2&gt;
&lt;p&gt;For the machine building week, we&#39;re planning a distributed project with Turkey. I&#39;ll be doing some UI work that takes websocket messages from a 6dof stylus controller, shows a simulation of the x/y/z gantry machine, and sends WebSerial messages to the machine.&lt;/p&gt;
&lt;h1 id=&quot;final-project-progress&quot; tabindex=&quot;-1&quot;&gt;Final project progress&lt;/h1&gt;
&lt;p&gt;Ported my &lt;a href=&quot;https://forresto.com/fab-academy/tactile-labyrinth.html#web&quot;&gt;Blender simulation to the web&lt;/a&gt; for added interactivity.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Week 10. Output Devices</title>
    <link href="https://forresto.com/fab-academy/10-output-devices.html" />
    <updated>2025-04-02T00:00:00Z</updated>
    <id>https://forresto.com/fab-academy/10-output-devices.html</id>
    <content type="html">&lt;h1 id=&quot;group-work&quot; tabindex=&quot;-1&quot;&gt;Group work&lt;/h1&gt;
&lt;p&gt;Characterizing the power consumption of my addressable RBG LED rope.&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://forresto.com/media-cache/rope-vcc-voltage.jpg-k3OhttV-gI-1280.avif 1280w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://forresto.com/media-cache/rope-vcc-voltage.jpg-k3OhttV-gI-1280.webp 1280w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://forresto.com/media-cache/rope-vcc-voltage.jpg-k3OhttV-gI-1280.jpeg&quot; alt=&quot;Power meter showing 5 volts.&quot; width=&quot;1280&quot; height=&quot;1804&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;LED rope with battery bank and supplied controller, power meter showing 5 volts. I did not make this board; it came with the LED rope.&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://forresto.com/media-cache/rope-amps-small.jpg-Fdij8z9PB7-1280.avif 1280w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://forresto.com/media-cache/rope-amps-small.jpg-Fdij8z9PB7-1280.webp 1280w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://forresto.com/media-cache/rope-amps-small.jpg-Fdij8z9PB7-1280.jpeg&quot; alt=&quot;Power meter showing 0.3 amps.&quot; width=&quot;1280&quot; height=&quot;1057&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;With only a some of the rope lit up, the power meter shows 0.3 amps.&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://forresto.com/media-cache/rope-amps-full.jpg-NhzQOI0t9N-1280.avif 1280w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://forresto.com/media-cache/rope-amps-full.jpg-NhzQOI0t9N-1280.webp 1280w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://forresto.com/media-cache/rope-amps-full.jpg-NhzQOI0t9N-1280.jpeg&quot; alt=&quot;Power meter showing 1.5 amps.&quot; width=&quot;1280&quot; height=&quot;1053&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;With the rope fully lit up, the the power meter shows 1.5 amps.&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://forresto.com/media-cache/rope-probe-data.jpg-lTVz2NQ9GW-1280.avif 1280w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://forresto.com/media-cache/rope-probe-data.jpg-lTVz2NQ9GW-1280.webp 1280w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://forresto.com/media-cache/rope-probe-data.jpg-lTVz2NQ9GW-1280.jpeg&quot; alt=&quot;Probe setup.&quot; width=&quot;1280&quot; height=&quot;960&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;Setup to probe the data line with the Salaea and supplied controller.&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://forresto.com/media-cache/probe-data-chunks.png-ZjZIETl0B0-1280.avif 1280w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://forresto.com/media-cache/probe-data-chunks.png-ZjZIETl0B0-1280.webp 1280w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://forresto.com/media-cache/probe-data-chunks.png-ZjZIETl0B0-1280.jpeg&quot; alt=&quot;5v data signals.&quot; width=&quot;1280&quot; height=&quot;485&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;Probe UI showing 5v data signals.&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://forresto.com/media-cache/probe-data-digital.png-ijDvd3amOh-1280.avif 1280w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://forresto.com/media-cache/probe-data-digital.png-ijDvd3amOh-1280.webp 1280w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://forresto.com/media-cache/probe-data-digital.png-ijDvd3amOh-1280.jpeg&quot; alt=&quot;Data signal showing on and off binary data.&quot; width=&quot;1280&quot; height=&quot;485&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;Probe UI zoomed in to a data signal.&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://forresto.com/media-cache/rope-data-voltage-level-shifter.jpg-Hi3FSfLizk-1280.avif 1280w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://forresto.com/media-cache/rope-data-voltage-level-shifter.jpg-Hi3FSfLizk-1280.webp 1280w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://forresto.com/media-cache/rope-data-voltage-level-shifter.jpg-Hi3FSfLizk-1280.jpeg&quot; alt=&quot;LED rope animation driven from XIAO.&quot; width=&quot;1280&quot; height=&quot;1586&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;Using an existing dev board with a level shifter. I did not make this board, it was available in the lab for group work. I did program it to test the Arduino libraries that I want to use on the XIAO. &lt;a href=&quot;https://github.com/FastLED/FastLED&quot;&gt;FastLED&lt;/a&gt; working!&lt;/p&gt;
&lt;h1 id=&quot;circuit-fo_v3&quot; tabindex=&quot;-1&quot;&gt;Circuit fo_v3&lt;/h1&gt;
&lt;p&gt;Since the XIAO RP2040 data pins have 3.3v and the LED rope data line needs 5v, I knew that my breakout board needed a level shifter. We added a &lt;a href=&quot;https://www.digikey.com/en/products/detail/texas-instruments/TXB0104DR/1629101&quot;&gt;LevelShifter_Texas_TXB0104DR_SOIC-14&lt;/a&gt; from the fab inventory. We also added a power-only USB connector.&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://forresto.com/media-cache/image.png-tFWDNRn_Qh-1280.avif 1280w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://forresto.com/media-cache/image.png-tFWDNRn_Qh-1280.webp 1280w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://forresto.com/media-cache/image.png-tFWDNRn_Qh-1280.png&quot; alt=&quot;KiCad schematic&quot; width=&quot;1280&quot; height=&quot;872&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;fo_v3 KiCad schematic.&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://forresto.com/media-cache/image-1.png-NPFm_U9w39-1280.avif 1280w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://forresto.com/media-cache/image-1.png-NPFm_U9w39-1280.webp 1280w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://forresto.com/media-cache/image-1.png-NPFm_U9w39-1280.png&quot; alt=&quot;KiCad PCB&quot; width=&quot;1280&quot; height=&quot;981&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;PCB design. I have to say that the routing is quite satisfying and I can see it being addictive.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;📝 Capacitors should be as close as possible to the chip.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://forresto.com/media-cache/v-bit.jpg-C3XE3edo9Y-1280.avif 1280w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://forresto.com/media-cache/v-bit.jpg-C3XE3edo9Y-1280.webp 1280w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://forresto.com/media-cache/v-bit.jpg-C3XE3edo9Y-1280.jpeg&quot; alt=&quot;Mill V-bit&quot; width=&quot;1280&quot; height=&quot;1706&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;The USB-C power breakout requires a 0.3mm clearance. We have 0.3mm routing bits, but the Bungard mill started breaking them. After breaking a few, we switched to a v-bit, which worked.&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://forresto.com/media-cache/circuit-fo-v3.jpg-E7GSMiMhfZ-1280.avif 1280w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://forresto.com/media-cache/circuit-fo-v3.jpg-E7GSMiMhfZ-1280.webp 1280w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://forresto.com/media-cache/circuit-fo-v3.jpg-E7GSMiMhfZ-1280.jpeg&quot; alt=&quot;fo_v3&quot; width=&quot;1280&quot; height=&quot;959&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;fo_v3 circuit assembled. Better than last time! I&#39;m tempted to encase it in hot glue.&lt;/p&gt;
&lt;p&gt;Big thanks to &lt;a href=&quot;https://learn.rijnieks.com&quot;&gt;Kris&#39; soldering tips videos&lt;/a&gt;. The key tip for me was the surface mount chip order of operations:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Put a little solder on &lt;em&gt;one&lt;/em&gt; corner pad.&lt;/li&gt;
&lt;li&gt;Place the chip with tweezers, make sure the rest of the feet are aligned.&lt;/li&gt;
&lt;li&gt;Melt the one foot into the solder blob, again make sure the rest of the feet are aligned.&lt;/li&gt;
&lt;li&gt;Solder the opposite corner foot and pad.&lt;/li&gt;
&lt;li&gt;Do the rest. Go slow.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;📝 The level shifter is symmetrical and the data sheet didn&#39;t give a clue as to which way the text is printed. The printed dot on the top is closest to pin 1.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://forresto.com/media-cache/circuit-fo-v3-rope.jpg-SMYEh-F_oV-1280.avif 1280w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://forresto.com/media-cache/circuit-fo-v3-rope.jpg-SMYEh-F_oV-1280.webp 1280w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://forresto.com/media-cache/circuit-fo-v3-rope.jpg-SMYEh-F_oV-1280.jpeg&quot; alt=&quot;fo_v3 to LED rope&quot; width=&quot;1280&quot; height=&quot;1706&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;XIAO to LED rope working on fo_v3 board. 😅&lt;/p&gt;
&lt;h1 id=&quot;next-steps&quot; tabindex=&quot;-1&quot;&gt;Next steps&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;Make a light animation to go from 0 → 250 with full brigtness white, to see the real max amps of the LED rope.&lt;/li&gt;
&lt;li&gt;Connect 6dof sensor and start programming lights reacting the the ball movement.&lt;/li&gt;
&lt;li&gt;Iterate on the electronics enclosure and light ball design. 🌀&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&quot;files&quot; tabindex=&quot;-1&quot;&gt;Files&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://forresto.com/fab-academy/10-output-devices/kicad_fo_v3.zip&quot;&gt;kicad_fo_v3.zip&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://forresto.com/fab-academy/10-output-devices/HelloLEDRopeColorPalette.ino&quot;&gt;HelloLEDRopeColorPalette.ino&lt;/a&gt; – this was a FastLED example program with configuration changes to match my LED rope and board.&lt;/li&gt;
&lt;/ul&gt;
</content>
  </entry>
</feed>