<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>3D Printing on Making Clean Tech</title><link>https://nunrg.eu/tags/3d-printing/</link><description>Recent content in 3D Printing on Making Clean Tech</description><generator>Hugo -- 0.136.5</generator><language>en-us</language><copyright>2024 Franci Kopač</copyright><lastBuildDate>Wed, 13 Nov 2024 12:12:09 +0100</lastBuildDate><atom:link href="https://nunrg.eu/tags/3d-printing/index.xml" rel="self" type="application/rss+xml"/><item><title>A Minimalist Digital Photo Frame Using 7-color E-paper</title><link>https://nunrg.eu/posts/epaper_7color/</link><pubDate>Wed, 13 Nov 2024 12:12:09 +0100</pubDate><guid>https://nunrg.eu/posts/epaper_7color/</guid><description>How I built a minimalist digital photo frame using a 7-colour e-paper display, a Raspberry Pi and Floyd-Steinberg dithering.</description><content:encoded><![CDATA[<p>You can support me by <a href="https://medium.com/@francikopa/a-minimalist-digital-photo-frame-using-7-color-e-paper-9dacde085464">reading this article on Medium</a></p>
<p><img loading="lazy" src="/images/epaper/7color.jpg" alt="Picture of the finished device"  />
</p>
<p>When researching the e-paper displays for the art project described <a href="https://nunrg.eu/posts/lowpowerepaper/">here</a>, I bought a <a href="https://www.waveshare.com/5.65inch-e-paper-module-f.htm">Waveshare ECP 600x488 7-color E-paper Display</a> (just a normal link, I&rsquo;m not making any money with this), but ended up never using it. I really try to avoid stuff piling up in my drawers, so I decided to use an old (vintage: 2011 😀) Raspberry Pi 1B I had lying around too and build a simple photo frame.</p>
<h2 id="using-raspberry-pi-to-control-the-e-paper-display">Using Raspberry Pi To Control the E-paper Display</h2>
<p>First, <a href="https://www.tomshardware.com/reviews/raspberry-pi-headless-setup-how-to,6028.html">set up a headless Pi</a> using Raspberry Pi OS Lite.</p>
<p>Next, you have to wire the display to the Pi header. The <a href="https://www.waveshare.com/wiki/5.65inch_e-Paper_Module_(F)_Manual#Working_With_Raspberry_Pi">Waveshare Wiki</a> instructions are pretty good and the setup should work out of the box for most people.</p>
<p>I had to make a few changes for my setup, because I wanted to use my old Raspberry Pi 1B to keep it from the e-waste bin. But it has different GPIO pins, so I ended up using the SPI pins on the Pi 1 and GPIO0, GPIO2, and GPIO3 for the DC, RST, and BUSY signals. This required a change in the <code>/RaspberryPi_JetsonNano/python/lib/waveshare_epd/epdconfig.py</code> file. Under <code># Pin definition</code> I changed the defs this way:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span> <span style="color:#75715e"># Pin definition</span>
</span></span><span style="display:flex;"><span>    RST_PIN  <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;WPI2&#34;</span>
</span></span><span style="display:flex;"><span>    DC_PIN   <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;WPI0&#34;</span>
</span></span><span style="display:flex;"><span>    CS_PIN   <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;WPI10&#34;</span>
</span></span><span style="display:flex;"><span>    BUSY_PIN <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;WPI3&#34;</span>
</span></span><span style="display:flex;"><span>    PWR_PIN  <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;WPI1&#34;</span>
</span></span><span style="display:flex;"><span>    MOSI_PIN <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;WPI12&#34;</span>
</span></span><span style="display:flex;"><span>    SCLK_PIN <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;WPI14&#34;</span>
</span></span></code></pre></div><p>This enabled the Waveshare e-paper library to work with my old Pi. If you will be using a newer Pi (2 or higher, with the 40-pin header), you don&rsquo;t need to do this.</p>
<p>Now just run the <code>/RaspberryPi_JetsonNano/python/examples/epd_5in65f_test.py</code> file to see if everything works.</p>
<h3 id="preparing-the-image-files-for-display">Preparing the Image Files for Display</h3>
<p>The 7-color e-paper display works differently than the normal LCD displays. Instead of every pixel having an RGB value, each pixel can only display one of the seven available colors: black, white, green, blue, violet, yellow, and red. Then there is the fixed 600x488 pixel resolution. Finally, the Waveshare library is written to accept bitmap (*.bmp) files only.</p>
<h4 id="color-conversion">Color Conversion</h4>
<p>The 7 color limitation means we have to do a color conversion to be able to display the pictures on this display. Selecting simply the nearest color for each pixel will give pretty underwhelming results, so we need a better method, called Floyd-Steinberg dithering. <a href="https://scipython.com/blog/floyd-steinberg-dithering/">This is a very good explanation of it.</a></p>
<p>You could code this yourself, but there is a lovely program called <a href="https://www.imagemagick.org/">ImageMagick</a> that will take care of this for us. But first we need an file that contains only the colors we need. Luckily, the Waveshare library comes with example pictures and you only need this one: <code>RaspberryPi_JetsonNano/python/pic/5in65_n0.bmp</code>, just copy it to the folder you will be using.</p>
<h5 id="select-the-files">Select the Files</h5>
<p>This is a pretty obvious first step—collect all the pictures you want displayed on your photo frame. In my case, I did a keyword search in my Google Photo account and simply exported all the results to a temporary directory. Then I did a quick scroll through to delete some photos I did not want to include. Since I was dealing with about 7000 photos, &ldquo;quick&rdquo; was a relative term, but it only took a couple of hours.</p>
<h5 id="do-the-conversion">Do the Conversion</h5>
<p>First install ImageMagick (I used <code>winget install ImageMagick.Q16-HDRI</code> on my windows PC). Next, run the following command-line command (replace the folder name accordingly):</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>magick *.jpg -resize 600x488 -dither FloydSteinberg -define dither:diffusion-amount<span style="color:#f92672">=</span>85% -remap <span style="color:#e6db74">&#34;folder/your/color/definition/file/is/in/5in65_n0.bmp&#34;</span> BMP3:images.bmp
</span></span></code></pre></div><p>This opens all the JPEG files in your folder, resizes them to max 600x488 pixels (leaving the unused space blank, so the aspect ratio stays correct), applies the Floyd-Steinberg dithering using the specified file, and, finally, saves them as bitmap files using the given filename, to which the sequence number of the file is appended.</p>
<p>Of course, there are many other ways of doing this, but this worked for me.</p>
<h5 id="doing-the-conversion-in-batches">Doing the Conversion in Batches</h5>
<p>ImageMagick is a very powerful program, but once you convert more than about 1000 pics at once, it will create a crazy amount of temporary files and the conversion itself will take a LONG time. So it&rsquo;s best to limit the conversion to batches of about 500-2000 files. This depends on your computer: 2000 pics took almost an hour on my rather new and quite powerful computer, producing over <strong>hundred GBs</strong> of temporary files in the process.</p>
<p>I think the best way is to copy all the image files to a folder called <code>todo</code> first. Then you copy the first batch of, say, 1000 files to your working folder. You then run the conversion, wait until it completes, and then copy everything but the resulting BMP files to the <code>done</code> folder. Then copy the next 1000 files, run the conversion again, but add &ldquo;_1&rdquo; to the result filename. Repeat, incrementing the suffix, until the <code>todo</code> folder is empty.</p>
<p>Congratulations, you now have a  large pile of bitmap files suitable for displaying on the 7 color e-paper display 😀 All you have to do now is copy it to a folder of your choice on the Raspberry Pi.</p>
<h3 id="the-python-code">The Python Code</h3>
<p>The code to display the files we just prepared is quite trivial, I simply copied the <code>/RaspberryPi_JetsonNano/python/examples/epd_5in65f_test.py</code> demo code and removed all the unnecessary parts. Then I added the code to be able to display bitmaps that don&rsquo;t fill the whole screen. Finally, I added random selection to keep the files from repeating too quickly. The code uses Pillow and you should take care to use proper file handling or you will have problems.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#f92672">import</span> sys
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> os
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> random
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> logging
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> time
</span></span><span style="display:flex;"><span><span style="color:#f92672">from</span> PIL <span style="color:#f92672">import</span> Image
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> traceback
</span></span><span style="display:flex;"><span><span style="color:#75715e"># Imports the Waveshare library</span>
</span></span><span style="display:flex;"><span>libdir <span style="color:#f92672">=</span> os<span style="color:#f92672">.</span>path<span style="color:#f92672">.</span>join(os<span style="color:#f92672">.</span>path<span style="color:#f92672">.</span>dirname(os<span style="color:#f92672">.</span>path<span style="color:#f92672">.</span>dirname(os<span style="color:#f92672">.</span>path<span style="color:#f92672">.</span>realpath(__file__))), <span style="color:#e6db74">&#39;lib&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">if</span> os<span style="color:#f92672">.</span>path<span style="color:#f92672">.</span>exists(libdir):
</span></span><span style="display:flex;"><span>    sys<span style="color:#f92672">.</span>path<span style="color:#f92672">.</span>append(libdir)
</span></span><span style="display:flex;"><span><span style="color:#f92672">from</span> waveshare_epd <span style="color:#f92672">import</span> epd5in65f
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># Let&#39;s keep the log short and only collect warnings. Use levels &#34;INFO&#34; or &#34;DEBUG&#34; for setup.</span>
</span></span><span style="display:flex;"><span>logging<span style="color:#f92672">.</span>basicConfig(level<span style="color:#f92672">=</span>logging<span style="color:#f92672">.</span>WARN)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">try</span>:
</span></span><span style="display:flex;"><span>    logging<span style="color:#f92672">.</span>info(<span style="color:#e6db74">&#34;Random files&#34;</span>)
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#75715e"># Initialize the display driver</span>
</span></span><span style="display:flex;"><span>    epd <span style="color:#f92672">=</span> epd5in65f<span style="color:#f92672">.</span>EPD()
</span></span><span style="display:flex;"><span>    logging<span style="color:#f92672">.</span>info(<span style="color:#e6db74">&#34;init&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">#The forever loop</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">while</span> <span style="color:#66d9ef">True</span>:
</span></span><span style="display:flex;"><span>        logging<span style="color:#f92672">.</span>info(<span style="color:#e6db74">&#34;read bmp file&#34;</span>)
</span></span><span style="display:flex;"><span>        image_folder<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;/the/folder/with/the/bitmaps/&#34;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">#Randomly select the file from the folder</span>
</span></span><span style="display:flex;"><span>        random_file<span style="color:#f92672">=</span>random<span style="color:#f92672">.</span>choice(os<span style="color:#f92672">.</span>listdir(image_folder))
</span></span><span style="display:flex;"><span>        logging<span style="color:#f92672">.</span>info(<span style="color:#e6db74">&#34;Chosen file: &#34;</span> <span style="color:#f92672">+</span> image_folder <span style="color:#f92672">+</span> random_file)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">#This is important: if you don&#39;t use proper file handling using &#34;with&#34;,</span>
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">#the file stays open and displaying the next file will fail</span>
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">with</span> Image<span style="color:#f92672">.</span>open(image_folder <span style="color:#f92672">+</span> random_file)<span style="color:#f92672">.</span>convert(<span style="color:#e6db74">&#39;RGBA&#39;</span>) <span style="color:#66d9ef">as</span> bitmap:
</span></span><span style="display:flex;"><span>            <span style="color:#75715e">#First set up a white background</span>
</span></span><span style="display:flex;"><span>            Himage <span style="color:#f92672">=</span> Image<span style="color:#f92672">.</span>new(<span style="color:#e6db74">&#39;RGB&#39;</span>, (epd<span style="color:#f92672">.</span>width, epd<span style="color:#f92672">.</span>height), <span style="color:#ae81ff">0xffffff</span>)
</span></span><span style="display:flex;"><span>            <span style="color:#75715e">#Get image dimensions from the bitmap file</span>
</span></span><span style="display:flex;"><span>            width, height <span style="color:#f92672">=</span> bitmap<span style="color:#f92672">.</span>size
</span></span><span style="display:flex;"><span>            <span style="color:#75715e">#Paste the image on the middle of the white background</span>
</span></span><span style="display:flex;"><span>            Himage<span style="color:#f92672">.</span>paste(bitmap, (int((<span style="color:#ae81ff">600</span><span style="color:#f92672">-</span>width)<span style="color:#f92672">/</span><span style="color:#ae81ff">2</span>), int((<span style="color:#ae81ff">488</span><span style="color:#f92672">-</span>height)<span style="color:#f92672">/</span><span style="color:#ae81ff">2</span>), int((<span style="color:#ae81ff">600</span><span style="color:#f92672">-</span>width)<span style="color:#f92672">/</span><span style="color:#ae81ff">2</span>)<span style="color:#f92672">+</span>width, int((<span style="color:#ae81ff">488</span><span style="color:#f92672">-</span>height)<span style="color:#f92672">/</span><span style="color:#ae81ff">2</span><span style="color:#f92672">+</span>height)))
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">#Initialize the display</span>
</span></span><span style="display:flex;"><span>        epd<span style="color:#f92672">.</span>init()    
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">#Display the bitmap on background</span>
</span></span><span style="display:flex;"><span>        epd<span style="color:#f92672">.</span>display(epd<span style="color:#f92672">.</span>getbuffer(Himage))
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">#Turn off the display internals and wait 10 minutes before you show the next pic</span>
</span></span><span style="display:flex;"><span>        logging<span style="color:#f92672">.</span>info(<span style="color:#e6db74">&#34;Goto Sleep...&#34;</span>)
</span></span><span style="display:flex;"><span>        epd<span style="color:#f92672">.</span>sleep()
</span></span><span style="display:flex;"><span>        time<span style="color:#f92672">.</span>sleep(<span style="color:#ae81ff">600</span>)
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">except</span> <span style="color:#a6e22e">Exception</span> <span style="color:#66d9ef">as</span> e:
</span></span><span style="display:flex;"><span>    logging<span style="color:#f92672">.</span>info(e)
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">except</span> <span style="color:#a6e22e">KeyboardInterrupt</span>:    
</span></span><span style="display:flex;"><span>    logging<span style="color:#f92672">.</span>info(<span style="color:#e6db74">&#34;ctrl + c:&#34;</span>)
</span></span><span style="display:flex;"><span>    epd5in65f<span style="color:#f92672">.</span>epdconfig<span style="color:#f92672">.</span>module_exit(cleanup<span style="color:#f92672">=</span><span style="color:#66d9ef">True</span>)
</span></span><span style="display:flex;"><span>    exit()
</span></span></code></pre></div><p>Insert the image path as needed, then run and check if it works.</p>
<h3 id="set-up-the-pi-to-run-unattended">Set Up the Pi To Run Unattended</h3>
<p>If everything works as intended, <a href="https://www.dexterindustries.com/howto/run-a-program-on-your-raspberry-pi-at-startup/">set up the Pi to auto-run the code on startup using this handy guide</a>. I like the SYSTEMD method best.</p>
<p>Once everything works well after you reboot the Pi, don&rsquo;t forget to <a href="https://learn.adafruit.com/read-only-raspberry-pi/overview">turn on the Overlay File System</a> to prevent SD card corruption if the power goes out unexpectedly.</p>
<h2 id="putting-everything-together">Putting Everything Together</h2>
<p>You now have an e-paper display showing beautiful pictures next to a Raspberry Pi. But it&rsquo;s not a digital photo frame yet, it needs some kind of a housing first. I decided to use a simple <a href="https://www.thingiverse.com/thing:922740">3D-printed VESA 75 mm case</a> for my Pi and then designed a <a href="https://www.printables.com/model/1072040-stand-for-565-7-color-waveshare-e-paper-with-vesa">simple stand to hold everything together</a>.</p>
<figure>
    <img src="/images/epaper/stand.png" alt="The stand from the back">
    <figcaption>You need to glue the two parts together using a drop of superglue</figcaption>
  </figure>
  
<h2 id="conclusion">Conclusion</h2>
<p>The first thing anybody will probably ask is: Why? Why use a strange display with a rather low resolution and only 7 colors to display your family photos? Without even keeping it low-power?</p>
<p>The answer is a bit complicated, but I can explain it best like this:</p>
<ul>
<li>Constraints spur creativity.</li>
<li>It&rsquo;s not about the resolution or color fidelity, it&rsquo;s about the pictures.</li>
<li>The grainy dithered images have a nice rough and timeless vintage feel to them. They give a new quality to the images I saw many times before.</li>
<li>The slow tempo of changing images does not demand your attention. It is a decor item, not another gadget to waste time with.</li>
</ul>
<p>And I got to rescue an 13-year old Raspberry Pi from the drawer in the process 😄</p>
<p>If you found this article useful, you can buy me a beer <a href="https://paypal.me/FranciKopac?country.x=SI&amp;locale.x=en_US">here</a>.</p>
]]></content:encoded></item><item><title>A Very Simple Low Cost Filament Dryer</title><link>https://nunrg.eu/posts/filamentdryer/</link><pubDate>Thu, 19 Sep 2024 00:00:00 +0000</pubDate><guid>https://nunrg.eu/posts/filamentdryer/</guid><description>A cheap, simple DIY filament dryer for 3D printing, built from a peltier dehumidifier, an ESP8266 and ESPHome for humidity control.</description><content:encoded><![CDATA[<p>You can support me by <a href="https://medium.com/@francikopa/a-simple-and-efficient-low-cost-filament-dryer-70985a44214a">reading this article on Medium</a></p>
<h2 id="the-problem">The problem</h2>
<p>A 3D printer is the most versatile tool any maker can have, but it has possible pitfalls and one of the more insidious ones is damp filament.</p>
<p>What happens: You have the printer dialled-in perfectly, everything works perfectly, a couple of weeks pass and then you try to print something again. It comes out looking terrible: blobs, stringing, ugly surfaces.</p>
<p>This is due to humidity absorbed by the filament. Once the filament enters the hot extruder, humidity expands into steam and plays havoc with the flow of molten material in the nozzle. In really bad cases you can actually hear the pops from the tiny steam bubbles during printing.</p>
<p>Some materials are more sensitive than others—you will be able to leave ABS or PLA out longer than, say Nylon, the worst of them. But one month outside in summer heat will make any filament unusable.</p>
<p>You can delay these problems a lot by storing filaments in an enclosed container and adding some desiccant helps even more. But eventually, any filament will get humid enough to need drying.</p>
<h2 id="the-most-common-solution">The Most Common Solution</h2>
<figure>
    <img src="/images/dryer/sunlu.png" alt="Sunlu Filadryer">
    <figcaption>SUNLU S2 Filament Dryer, credit: Sunlu</figcaption>
  </figure>
  
<p>On the surface, this looks like a solved problem: e.g. Sunlu dryer boxes work well and are not that expensive. But this kind of dryer box is not without problems:</p>
<ul>
<li>They are not  cheap, a two-roll box will cost you about ~90€.  If you are serious about printing, you will need at least two of those.</li>
<li>They have 150-200W heaters. If you are paying German electricity prices, each day you use one will cost ~1-2€. And each year has 365 days&hellip;</li>
</ul>
<p>So is there a different way?</p>
<h2 id="how-does-drying-even-work">How Does Drying Even Work?</h2>
<h3 id="the-usual-dryer-boxes">The Usual Dryer Boxes</h3>
<p>The dryer boxes I mention above work by heating up the air. Hotter air can carry more moisture (relative humidity is reduced) and this &ldquo;sucks&rdquo; moisture from the filament due to osmosis (which happens faster due do higher temperature as well). The hot and humid air is then blown out of the enclosure automatically or you need to do it manually after the end of each cycle. Some will take advantage of moisture condensing on the cooler walls, which you must then wipe down.</p>
<p><a href="https://www.printdry.com/the-science-behind-it/">See this explanation for extra info.</a></p>
<h3 id="the-proper-way-to-dry-things">The Proper Way To Dry Things</h3>
<p>If you look at industrial dryers or modern clothes dryers, these work differently.</p>
<p>The first step of the process is the same: Blow hot air into the chamber to capture the humidity. The next step is very different, though: The hot humid air is cooled, which causes the excess moisture to condense away, leaving you with dry cool air that you can then heat up again to dry the filament, in an endless circle.</p>
<p>At first glance, this seems like a very wasteful process: instead of simply heating the air, you need to cool it too. But here comes the magic part: You can use a heat-pump to transfer the heat from cooling to the heating part of the cycle. A modern heat-pump is extremely efficient and will only consume about 25% of energy it is able to transfer. So if you need 200W of heating power, you will only need about 40W of electricity for the same effect. What is more, because you are cooling the return air, the return air is very dry and the drying effect is even stronger.</p>
<figure>
    <img src="/images/dryer/hpdryer.jpg" alt="Heat-pump dryer schematic">
    <figcaption>A schematic of a heat-pump clothes-dryer, credit Massachusets Clean Energy Center</figcaption>
  </figure>
  
<h2 id="my-solution">My Solution</h2>
<h3 id="the-parameters">The parameters</h3>
<p>I wanted a solution that does the following:</p>
<ul>
<li>There should be enough space for at least 10 rolls of filament.</li>
<li>It should be able to work continuously, no wiping down etc.</li>
<li>It should consume no more than 50W of electricity (a quarter of the consumption of a normal dryer)</li>
<li>Some monitoring would be nice</li>
</ul>
<h3 id="the-hardware">The hardware</h3>
<p>There are many possibilities:</p>
<p>I could possibly build a really big dryer out of a second-hand clothes-dryer. My neighbor and I actually converted one into a big fruit dryer so his apples and plums don&rsquo;t spoil after the harvest. But while this is a good way to dry out large amounts of moisture, filaments only contain a few grams of moisture each at most and the dryer will consume 700W of electricity. So this would be a really wasteful way of doing it and it would take up a lot of space.</p>
<p>Maybe a mini fridge? I could use an air duct to heat up the air at the rear heat-exchanger, blow it into a box, and then return it to the cold side of the mini fridge. But this would take extra space and I&rsquo;m not sure how to cool the air effectively.</p>
<p>In the end I decided to buy a very simple and very cheap room air-dryer on Aliexpress. These small units will consume about 15W and dry out a couple of tens of grams of moisture a day from the surrounding air.</p>
<figure>
    <img src="/images/dryer/airdryer.png" alt="The Little Air-Dryer">
    <figcaption>It Looks Like This</figcaption>
  </figure>
  
<p>They use a <a href="https://en.wikipedia.org/wiki/Thermoelectric_cooling">Peltier element</a> instead of a proper heat-pump, but since we need so little drying capacity, this is good enough. The Peltier element, the two heat-sinks and the fan are combined into one compact unit like this:</p>
<figure>
    <img src="/images/dryer/peltier.jpg" alt="The Peltier Block">
    <figcaption>Connect it to 12 V and it freezes in a minute or two</figcaption>
  </figure>
  
<p>There is also a power-supply, so you only need some more fans, a control board and a new housing.</p>
<h3 id="the-box">The Box</h3>
<p>I decided to use a simple plastic storage box I kept the filaments in already. IKEA is a good place to find one that is large enough for your needs. Mine is about 40 x 50 x 60 cm, enough for about 12 rolls.</p>
<h3 id="the-housing">The Housing</h3>
<p>My idea was to put the entire Peltier block along with two temperature sensors (DS1820, hot and cold side), a humidity sensor (AHT20, for monitoring) and two fans (hot and cold side) inside the box as a drying unit.</p>
<p>A small hole in the side of the box connects this drying unt with the control box outside. The control box contains the power supply, an ESP8266 controller and a couple of relays for control.</p>
<p>There is also a small outlet for the condensed water on the bottom of the cold side housing. This is connected to a small tube and routed outside the box.</p>
<figure>
    <img src="/images/dryer/housing.png" alt="The Housing">
    <figcaption>A render of the housing design. The side with the fans is inside the box, the side with the grid is outside.</figcaption>
  </figure>
  
<p><em>Here is the <a href="https://a360.co/4e8Qa8P">Fusion project</a>, if you want to play with it.</em></p>
<h3 id="the-first-prototype">The First Prototype</h3>
<figure>
    <img src="/images/dryer/dryer.jpg" alt="The whole thing">
    <figcaption>The whole thing. It&#39;s the first prototype and therefore ugly, but it works, so it may take a while before I work on it again. The small fan is for the power supply, but it&#39;s not really needed, I should have put that inside, near the hot side fan.</figcaption>
  </figure>
  
<h3 id="control">Control</h3>
<p>I decided to use ESPHome and integrate it into my Home Assistant system to control the whole thing.</p>
<p>The first version was over-engineered, with PWM control of both fans and measurements on hot and cold side. It turns out you only need to monitor humidity and control two relays, one for the Peltier element and the hot side fan, the other for the cold side fan. You could even do without the cold side fan at the expense of a slightly lower drying power.</p>
<figure>
    <img src="/images/dryer/esphome1.png" alt="The Home Assistant dashboard">
    <figcaption>The peaks happen when I turn the whole thing off to save power</figcaption>
  </figure>
  
<h4 id="the-algorithm">The Algorithm</h4>
<p>The algorithm is simple:</p>
<ol>
<li>Turn on the Peltier and the hot side fan.</li>
<li>Wait for a while for the water to condense on the cold side. (The hot side fan is strong enough to ensure the circulation on the cold side too.)</li>
<li>Turn off the Peltier and turn on cold side fan to blow the water off the heat sink ribs down into the water outlet. A minute is enough. This is optional, water will drip down on it&rsquo;s own until there is very little moisture left.</li>
<li>Repeat.</li>
</ol>
<p>It also needs a safety feature to turn off the whole thing if the hot side heats up above 45 °C (i.e. fan problems).</p>
<p>I also added an automation that only turns the dryer on when there is extra power available from the solar plant I have installed. This is optional, but it does keep the whole thing from taking energy from the grid.</p>
<h2 id="final-thoughts">Final Thoughts</h2>
<p>The whole thing cost me about 70€ at most and it works. There is one drawback: It takes a few days to dry a moist filament roll, but it keeps everything nice and dry afterwards. It seems that putting in a moist roll does not affect the rest of the stored rolls at all.</p>
<h2 id="the-esphome-code-and-planned-improvements">The ESPHome Code and Planned Improvements</h2>
<h3 id="planned-improvements">Planned improvements</h3>
<p>The code is for the current over-engineered system. The next version will have no PWM, no cold side fan, and no cold side temperature sensor. The hot side fan power will be fixed using a resistor. There will be only one relay that controls both the Peltier and hot side fan. The power supply will be next to the hot side fan to use the wasted energy to heat the box.</p>
<h3 id="the-code">The Code</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#f92672">esphome</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">name</span>: <span style="color:#ae81ff">susilnik</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">friendly_name</span>: <span style="color:#ae81ff">susilnik</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">esp8266</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">board</span>: <span style="color:#ae81ff">esp01_1m</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># Enable logging</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">logger</span>:
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># Enable Home Assistant API</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">api</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">encryption</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">key</span>: <span style="color:#e6db74">&#34;secret&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">ota</span>:
</span></span><span style="display:flex;"><span> <span style="color:#f92672">—platform</span>: <span style="color:#ae81ff">esphome</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">password</span>: <span style="color:#e6db74">&#34;secret&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">wifi</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">ssid</span>: !<span style="color:#ae81ff">secret wifi_ssid</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">password</span>: !<span style="color:#ae81ff">secret wifi_password</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">use_address</span>: <span style="color:#ae81ff">secret</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">one_wire</span>:
</span></span><span style="display:flex;"><span> <span style="color:#f92672">—platform</span>: <span style="color:#ae81ff">gpio</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">pin</span>: <span style="color:#ae81ff">13</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">sensor</span>:
</span></span><span style="display:flex;"><span> <span style="color:#f92672">—platform</span>: <span style="color:#ae81ff">aht10</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">temperature</span>:
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">id</span>: <span style="color:#ae81ff">temperatura</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">name</span>: <span style="color:#e6db74">&#34;Temperatura&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">humidity</span>:
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">id</span>: <span style="color:#ae81ff">vlaznost</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">name</span>: <span style="color:#e6db74">&#34;Vlaznost&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">update_interval</span>: <span style="color:#ae81ff">60s</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">—platform</span>: <span style="color:#ae81ff">dallas_temp</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">address</span>: <span style="color:#ae81ff">0x090417b10b0cff28</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">name</span>: <span style="color:#e6db74">&#34;Temperatura topla stran&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">id</span>: <span style="color:#ae81ff">temperatura_topla_stran</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">—platform</span>: <span style="color:#ae81ff">dallas_temp</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">address</span>: <span style="color:#ae81ff">0x690517a0c505ff28</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">name</span>: <span style="color:#e6db74">&#34;Temperatura hladna stran&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">id</span>: <span style="color:#ae81ff">temperatura_hladna_stran</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">i2c</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">sda</span>: <span style="color:#ae81ff">4</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">scl</span>: <span style="color:#ae81ff">5</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">frequency</span>: <span style="color:#ae81ff">10kHz</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">switch</span>:
</span></span><span style="display:flex;"><span> <span style="color:#f92672">—platform</span>: <span style="color:#ae81ff">gpio</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">pin</span>: <span style="color:#ae81ff">16</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">name</span>: <span style="color:#e6db74">&#34;Vklop 12V&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">id</span>: <span style="color:#ae81ff">vklop_12v</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">restore_mode</span>: <span style="color:#ae81ff">ALWAYS_ON</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">output</span>:
</span></span><span style="display:flex;"><span> <span style="color:#f92672">—platform</span>: <span style="color:#ae81ff">esp8266_pwm</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">pin</span>: <span style="color:#ae81ff">12</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">frequency</span>: <span style="color:#ae81ff">10</span> <span style="color:#ae81ff">Hz</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">id</span>: <span style="color:#ae81ff">pwm_hladni_ventilator</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">—platform</span>: <span style="color:#ae81ff">esp8266_pwm</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">pin</span>: <span style="color:#ae81ff">14</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">frequency</span>: <span style="color:#ae81ff">10</span> <span style="color:#ae81ff">Hz</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">id</span>: <span style="color:#ae81ff">pwm_hladilni_ventilator</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">fan</span>:
</span></span><span style="display:flex;"><span> <span style="color:#f92672">—platform</span>: <span style="color:#ae81ff">speed</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">output</span>: <span style="color:#ae81ff">pwm_hladni_ventilator</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">name</span>: <span style="color:#e6db74">&#34;Hladni ventilator&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">id</span>: <span style="color:#ae81ff">hladni_ventilator</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">—platform</span>: <span style="color:#ae81ff">speed</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">output</span>: <span style="color:#ae81ff">pwm_hladilni_ventilator</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">name</span>: <span style="color:#e6db74">&#34;Hladilni ventilator&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">id</span>: <span style="color:#ae81ff">hladilni_ventilator</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">time</span>:
</span></span><span style="display:flex;"><span> <span style="color:#f92672">—platform</span>: <span style="color:#ae81ff">homeassistant</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">id</span>: <span style="color:#ae81ff">homeassistant_time</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">on_time</span>:
</span></span><span style="display:flex;"><span>      <span style="color:#75715e">#Safety functions</span>
</span></span><span style="display:flex;"><span>     <span style="color:#f92672">—minutes</span>: <span style="color:#ae81ff">/1</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">seconds</span>: <span style="color:#ae81ff">0</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">then</span>:
</span></span><span style="display:flex;"><span>          <span style="color:#f92672">if</span>:
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">condition</span>:
</span></span><span style="display:flex;"><span>              <span style="color:#f92672">switch.is_on</span>: <span style="color:#ae81ff">vklop_12v</span>
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">then</span>:
</span></span><span style="display:flex;"><span>             <span style="color:#f92672">—if</span>:
</span></span><span style="display:flex;"><span>                  <span style="color:#f92672">condition</span>:
</span></span><span style="display:flex;"><span>                    <span style="color:#f92672">or</span>:
</span></span><span style="display:flex;"><span>                     <span style="color:#f92672">—sensor.in_range</span>:
</span></span><span style="display:flex;"><span>                          <span style="color:#f92672">id</span>: <span style="color:#ae81ff">temperatura_topla_stran</span>
</span></span><span style="display:flex;"><span>                          <span style="color:#f92672">above</span>: <span style="color:#ae81ff">45</span>
</span></span><span style="display:flex;"><span>                     <span style="color:#f92672">—sensor.in_range</span>:
</span></span><span style="display:flex;"><span>                          <span style="color:#f92672">id</span>: <span style="color:#ae81ff">temperatura</span>
</span></span><span style="display:flex;"><span>                          <span style="color:#f92672">above</span>: <span style="color:#ae81ff">40</span>
</span></span><span style="display:flex;"><span>                  <span style="color:#f92672">then</span>:
</span></span><span style="display:flex;"><span>                    <span style="color:#f92672">switch.turn_off</span>: <span style="color:#ae81ff">vklop_12v</span>
</span></span><span style="display:flex;"><span>     <span style="color:#f92672">—minutes</span>: <span style="color:#ae81ff">55</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">seconds</span>: <span style="color:#ae81ff">0</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">then</span>:
</span></span><span style="display:flex;"><span>          <span style="color:#f92672">if</span>:
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">condition</span>:
</span></span><span style="display:flex;"><span>              <span style="color:#f92672">switch.is_on</span>: <span style="color:#ae81ff">vklop_12v</span>
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">then</span>:
</span></span><span style="display:flex;"><span>              <span style="color:#f92672">fan.turn_on</span>:
</span></span><span style="display:flex;"><span>                <span style="color:#f92672">id</span>: <span style="color:#ae81ff">hladni_ventilator</span>
</span></span><span style="display:flex;"><span>                <span style="color:#f92672">speed</span>: <span style="color:#ae81ff">50</span>
</span></span><span style="display:flex;"><span>     <span style="color:#f92672">—minutes</span>: <span style="color:#ae81ff">57</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">seconds</span>: <span style="color:#ae81ff">0</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">then</span>:
</span></span><span style="display:flex;"><span>          <span style="color:#f92672">fan.turn_off</span>: <span style="color:#ae81ff">hladni_ventilator</span>
</span></span><span style="display:flex;"><span>      <span style="color:#75715e">#Start cooling fan for PSU</span>
</span></span><span style="display:flex;"><span>     <span style="color:#f92672">—minutes</span>: <span style="color:#ae81ff">/1</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">seconds</span>: <span style="color:#ae81ff">0</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">then</span>:
</span></span><span style="display:flex;"><span>          <span style="color:#f92672">if</span>:
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">condition</span>:
</span></span><span style="display:flex;"><span>              <span style="color:#f92672">switch.is_on</span>: <span style="color:#ae81ff">vklop_12v</span>
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">then</span>:
</span></span><span style="display:flex;"><span>              <span style="color:#f92672">fan.turn_on</span>:
</span></span><span style="display:flex;"><span>                <span style="color:#f92672">id</span>: <span style="color:#ae81ff">hladilni_ventilator</span>
</span></span><span style="display:flex;"><span>                <span style="color:#f92672">speed</span>: <span style="color:#ae81ff">50</span>
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">else</span>:
</span></span><span style="display:flex;"><span>              <span style="color:#f92672">fan.turn_off</span>: <span style="color:#ae81ff">hladilni_ventilator</span>
</span></span></code></pre></div><p>If you found this article useful, you can buy me a beer <a href="https://paypal.me/FranciKopac?country.x=SI&amp;locale.x=en_US">here</a>.</p>
]]></content:encoded></item><item><title>3D Design: Minimalist Bird Feeder</title><link>https://nunrg.eu/posts/bird_feeder/</link><pubDate>Thu, 21 Dec 2023 00:00:00 +0000</pubDate><guid>https://nunrg.eu/posts/bird_feeder/</guid><description>A minimalist 3D-printable bird feeder shared on Printables — easy to print and mount in a garden or on a balcony.</description><content:encoded><![CDATA[<p>I posted a new design on Printables, check it out: <a href="https://www.printables.com/model/688517-bird-feeder">Minimalist bird feeder</a></p>
<p><img loading="lazy" src="/images/feeder.png" alt="Minimalist bird feeder"  />
</p>
]]></content:encoded></item><item><title>3D Design: Cover for Ikea EKET Cube</title><link>https://nunrg.eu/posts/cube_cover/</link><pubDate>Tue, 05 Dec 2023 00:00:00 +0000</pubDate><guid>https://nunrg.eu/posts/cube_cover/</guid><description>A 3D-printable cover for the Ikea EKET cube — a tidy, customisable way to hide cables and clutter, free to download on Printables.</description><content:encoded><![CDATA[<p>This was long overdue, but I finally found a minute to post it on Printables: <a href="https://www.printables.com/model/668275-cover-for-ikea-eket-cube">Cover for Ikea EKET cube</a></p>
<p><img loading="lazy" src="/images/cube.jpg" alt="3d Design: Cover for Ikea EKET Cube"  />
</p>
]]></content:encoded></item><item><title>3D Design: Flexible Beer Cozy</title><link>https://nunrg.eu/posts/beer_cozy/</link><pubDate>Tue, 05 Dec 2023 00:00:00 +0000</pubDate><guid>https://nunrg.eu/posts/beer_cozy/</guid><description>A free, flexible 3D-printable beer cozy (koozie) design shared on Printables — a quick, fun print that keeps drinks cold.</description><content:encoded><![CDATA[<p>After a while, I posted a new design on Printables, check it out: <a href="https://www.printables.com/model/657879-beer-cozy-flexible-by-design">Beer cozy, flexible by design</a></p>
<p><img loading="lazy" src="/images/cozy.jpg" alt="Beer Cozy"  />
</p>
]]></content:encoded></item></channel></rss>