<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xml:base="https://stfn.pl/" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>STFN</title>
    <link>https://stfn.pl/</link>
    <atom:link href="https://stfn.pl/rss.xml" rel="self" type="application/rss+xml" />
    <description>On computers and the night sky</description>
    <language>en</language>
    <item>
      <title>Hello World</title>
      <link>https://stfn.pl/blog/01-first-post/</link>
      <description>&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=XFhpctuUwb4&quot;&gt;I&#39;m a locksmith and I&#39;m a locksmith&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I&#39;ve started this blog mostly because sometimes I want to say something that
doesn&#39;t fit into the 280 chars of a Tweet, and is more persistent than a social
media thread. This is not a Youtube channel because I don&#39;t feel like recording
myself in front of a camera. Finally, I miss the times of late 00s / early 10s,
when the Internet was a different, more personal place, with the blogosphere and
all that.&lt;/p&gt;
&lt;p&gt;I don&#39;t exactly know what this blog will be about, probably computers and all
the silly things that come with them, but we&#39;ll see.&lt;/p&gt;
&lt;p&gt;So, Hello World!&lt;/p&gt;
</description>
      <pubDate>Thu, 01 Dec 2022 24:00:00 GMT</pubDate>
      <dc:creator>STFN</dc:creator>
      <guid>https://stfn.pl/blog/01-first-post/</guid>
    </item>
    <item>
      <title>Raspberry Pi Pico Weather Station - part I</title>
      <link>https://stfn.pl/blog/02-pico-weather-station/</link>
      <description>&lt;p&gt;Collecting data is fun [citation needed], and one of the most ubiqtous and
easiest to collect information around us is environment data, meaning things
like temperature or atmospheric pressure. Sensors design to collect it are cheap
and easy to use, and the data can be saved and manipulated using even the
simplest machines.&lt;/p&gt;
&lt;p&gt;This post will be the start of a series in which we&#39;ll make a preliminary
weather station that will be able to share our local weather data with the
world, be it a private service, or a feed to a public site like Weather
Underground.&lt;br&gt;&lt;/p&gt;
&lt;p&gt;Here&#39;s how I am presenting weather data on my local Grafana instance:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/02/grafana.png&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/02/grafana.png&quot; alt=&quot;Screenshot of a Grafana dashboard, showing three graphs, for temperature, humidity, and pressure&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The weather station will be using two sensors to gather the environment data:
BMP280 and DHT11. BMP280 will provide us with atmospheric pressure and
temperature readings. DHT11 provides temperature and humidity, but we will be
only taking the latter from it, as BMP280 can measure how hot it is (or we are)
with higher precision.&lt;/p&gt;
&lt;h2&gt;Parts list&lt;/h2&gt;
&lt;p&gt;List of required components:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://botland.store/raspberry-pi-pico-modules-and-kits/21574-raspberry-pi-pico-w-rp2040-arm-cortex-m0-cyw43439-wifi-5056561803173.html&quot;&gt;Raspberry Pi Pico W&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://botland.store/pressure-sensors/7245-bmp280-digital-barometer-pressure-sensor-110kpa-i2cspi-33v-5904422310042.html&quot;&gt;BMP280&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://botland.store/multifunctional-sensors/1886-temperature-and-humidity-sensor-dht11-module--5903351242448.html&quot;&gt;DHT11&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://botland.store/various-wires/1437-connecting-cables-male-male-30cm-colored-50pcs-5904422356002.html&quot;&gt;pack of cables to give a path for the electrons to flow&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://botland.store/breadoards/19943-breadboard-justpi-830-holes-5904422328610.html&quot;&gt;breadboard&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;A word on BME280 vs BMP280&lt;/h3&gt;
&lt;p&gt;Some of you might wonder why I am using BMP280, a pressure and humidity sensor,
and DHT11, a temperature and humidity sensor, when I could be using BME280,
which measures all three aspects at once. The simple explanation is, this is a
learning project, and BME280 is around two-three times more expensive than
BMP280 and DHT11 combined. It is natural that in the course of learning mistakes
happen, and breaking such an expensive part would be painful. Also, using two
different sensors with different communication protocols makes us learn twice as
much!&lt;/p&gt;
&lt;h2&gt;Connecting Everything&lt;/h2&gt;
&lt;p&gt;Below you&#39;ll find the Raspberry Pi Pico pinout (list of pins) taken from
&lt;a href=&quot;https://datasheets.raspberrypi.com/&quot;&gt;https://datasheets.raspberrypi.com/&lt;/a&gt;. All
of the pin names and numbers will taken from it, use it as a reference.&lt;/p&gt;
&lt;p&gt;Let&#39;s start with connecting BMP280. That sensors uses the I2C communication
protocol, so it&#39;ll need four pins: power (VCC), ground (GND), and two
communication pins, SCL and SDA.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;connect BMP280&#39;s VCC to Pico Pin 36 (3V3OUT)&lt;/li&gt;
&lt;li&gt;GND to Pico Pin 38 GND&lt;/li&gt;
&lt;li&gt;SDA to Pico Pin 1 (GP0)&lt;/li&gt;
&lt;li&gt;SCL to Pico Pin 2 (GP1)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Now moving onto DHT11. DHT11 uses a single cable protocol, but still needs power
and ground.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;connect DH11s OUT Pin to Pico Pin 20 (GP15)&lt;/li&gt;
&lt;li&gt;+ to Pico Pin 36 (3V3OUT)&lt;/li&gt;
&lt;li&gt;- to Pico Pin 18 (GND)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Yes, both DHT and BMP280 need to be connected to the 3V3 pin as they both rely
on 3 volt input.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://datasheets.raspberrypi.com/pico/Pico-R3-A4-Pinout.pdf&quot;&gt;&lt;img src=&quot;https://d233f3e99deg9m.cloudfront.net/pinout.png&quot; alt=&quot;alt
text&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Connecting with a breadboard&lt;/h2&gt;
&lt;p&gt;The easiest, but not solid way is to connect everything using a breadbord and
male-to-male or male-to-female cables. Even if you plan to do a permanent
solution later, it&#39;s good to start with a breadbord to make sure things work.
It&#39;s super simple to correct any wrong connections at this point. If you&#39;re
happy with how it works right now, just skip the soldering part and go straight
to programming.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/02/breadboard.jpeg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/02/breadboard.jpeg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Soldering on a dev board&lt;/h2&gt;
&lt;p&gt;This is how I did the soldering, surely could have been better, but it does
work.&lt;/p&gt;
&lt;h3&gt;Preparing the Pico&lt;/h3&gt;
&lt;p&gt;I started with soldering GPiO headers to the Pico, and then the Pico to the
development board.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/02/preparing1.jpeg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/02/preparing1.jpeg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/02/preparing2.jpeg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/02/preparing2.jpeg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Adding BMP280&lt;/h3&gt;
&lt;p&gt;Now it was time to solder the BMP280 and connect it to the Pico with short
leads.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/02/bme1.jpeg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/02/bme1.jpeg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/02/bme2.jpeg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/02/bme2.jpeg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Adding DHT11&lt;/h3&gt;
&lt;p&gt;Finally I soldered DHT11. I made double sure no cables were touching each other,
or other pins or the Pico, that would lead to unpredicatble results. I also
planned out the space on the board so that later I can add a connetion to power
the board not using the USB header.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/02/dht1.jpeg%22&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/02/dht1.jpeg%22&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/02/dht2.jpeg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/02/dht2.jpeg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Making it run&lt;/h2&gt;
&lt;p&gt;Now as everything is connected, let&#39;s do some code writing! For development I
will be using Thonny, a simple code editor that has built-in support for
Raspberry Pi Picos and other microcontrollers. You can install Thonny from
&lt;a href=&quot;https://thonny.org/&quot;&gt;thonny.org&lt;/a&gt;. I won&#39;t dive into how to setup and use Thonny
for RPi Pico, as there is already a great explanation done by the RPi team
itself:
&lt;a href=&quot;https://projects.raspberrypi.org/en/projects/getting-started-with-the-pico/2&quot;&gt;https://projects.raspberrypi.org/en/projects/getting-started-with-the-pico/2&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;There is however one functionality that those docs do not cover, and that is
installing additional packages to our Pico. Support for BMP280 is not included
in Micropython, and so we&#39;ll need to install the library that will allow us to
use it.&lt;/p&gt;
&lt;p&gt;In Thonny, go to Tools -&amp;gt; Manage packages in the menu bar and type &amp;quot;bme280&amp;quot; in
the search field. Second on the list of results there should be
&amp;quot;micropython-bme280&amp;quot; and that is the package we&#39;ll need. This library supports
both BMP280 and BME280, so no worries. Click on it and select Install. Once it
is installed, we&#39;re fully ready to go. Make sure you install the BME280 library
for Micropython, the version for the regular version of Python will not work.
The library for DHT is built in in micropython so no need to do anything here.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/02/packages.png&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/02/packages.png&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;And now the part we have all been waiting for, the code. In Thonny, create a
file called &lt;tt&gt;weather.py&lt;/tt&gt; (or any other, filenames don&#39;t matter for now)
and paste this code snippet:&lt;/p&gt;
&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# https://github.com/SebastianRoll/mpy_bme280_esp8266&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# https://docs.micropython.org/en/latest/esp8266/tutorial/dht.html&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; dht
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; bme280

SCL_PIN &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; machine&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Pin&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
SDA_PIN &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; machine&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Pin&lt;span class=&quot;token punctuation&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;
DHT_PIN &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; machine&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Pin&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

i2c &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; machine&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;I2C&lt;span class=&quot;token punctuation&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; scl&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;SCL_PIN&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; sda&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;SDA_PIN&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
bme_sensor &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; bme280&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;BME280&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;i2c&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;i2c&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
d_sensor &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; dht&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;DHT11&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;DHT_PIN&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

temperature&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; pressure&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; _ &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; bme_sensor&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;read_compensated_data&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
temperature &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; temperature &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;
pressure &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; pressure &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;256&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;

d_sensor&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;measure&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
humidity &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; d_sensor&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;humidity&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

payload &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 string&quot;&gt;&quot;temperature&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; temperature&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;pressure&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; pressure&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;humidity&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; humidity&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;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;payload&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Don&#39;t worry, we&#39;ll now go through it line by line so that everything is clear
and understandable. The first two lines are comments with links to the libraries
we are using in the snippet, you can check their documentation if you&#39;re
curious.&lt;/p&gt;
&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; dht
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; bme280

SCL_PIN &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; machine&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Pin&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
SDA_PIN &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; machine&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Pin&lt;span class=&quot;token punctuation&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;
DHT_PIN &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; machine&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Pin&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now it&#39;s time to import the two libraries we are using, one for BMP280, second
one for DHT11. Afterwards we are declaring three constants that will store the
information about the pins that we are using to connect the Pico to the sensors.
Remember to alter those lines if you connected your Pico differently that I did!&lt;/p&gt;
&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;i2c &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; machine&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;I2C&lt;span class=&quot;token punctuation&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; scl&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;SCL_PIN&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; sda&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;SDA_PIN&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
bme_sensor &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; bme280&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;BME280&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;i2c&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;i2c&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
d_sensor &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; dht&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;DHT11&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;DHT_PIN&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

temperature&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; pressure&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; _ &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; bme_sensor&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;read_compensated_data&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
temperature &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; temperature &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;
pressure &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; pressure &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;256&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;

d_sensor&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;measure&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
humidity &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; d_sensor&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;humidity&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;Preparation&#39;s done, now it&#39;s time for the main work. The first block of the code
above initializes the I2C protocol, the BME280 sensor, and the DHT11 sensor. The
second block is reading from BME280. One thing to remember is that BMP280
measures two entities, temperature and pressure, and the more expensive BME280
measures also humidity. However, the read_compensated_data() method always
returns three values, temperature, pressure, and humidity. The third value in
our case will be alwas zero, so we just discard it. Simple. The next two lines
are converting the returned values to a human readable standard, degrees Celsius
in case of temperature and hectoPascals for atmospheric pressure. In the third
block it&#39;s time for DHT11 to do it&#39;s job, we trigger the measuring operation and
save its result.&lt;/p&gt;
&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;payload &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 string&quot;&gt;&quot;temperature&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; temperature&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;pressure&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; pressure&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;humidity&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; humidity&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;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;payload&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The grand finale, the cream de la cream! The values that we got from the sensors
are saved in a Python dictionary and printed to the screen. Voila! When the
magic green Run button is pressed, you should get something like this in the
console:&lt;/p&gt;
&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;&quot;pressure&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;999.5192&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;humidity&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;48&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;temperature&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;22.58&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Of course, showing numbers in the console is not the most useful way of
presenting data. A better way it to show them on a graph to present how they
change in time, or show them on your mobile phone, or maybe make other devices
automatically do stuff based on them (e.g. turn on the AC if temperature is
&amp;lt;20C). Patience, all that will be covered in the next parts of this series.&lt;/p&gt;
&lt;p&gt;Thanks for reading!&lt;/p&gt;
</description>
      <pubDate>Fri, 02 Dec 2022 24:00:00 GMT</pubDate>
      <dc:creator>STFN</dc:creator>
      <guid>https://stfn.pl/blog/02-pico-weather-station/</guid>
    </item>
    <item>
      <title>Astrophotography - The Iris Nebula</title>
      <link>https://stfn.pl/blog/03-space-iris/</link>
      <description>&lt;p&gt;As the first astrophoto on this blog I present you my most recent capture, taken
in the middle of November 2022. This is the Iris Nebula (also known as NGC
7023), 1,300 light-years away in the constellation of Cepheus and six
light-years across. The Iris Nebula is a bright reflection nebula, the
interstellar gas and larger particles are illuminated by a bright young star in
the middle of the nebulosity. The bright nebula is surrounded by darks nebulae,
clouds of materia where the star&#39;s shine does not reach. The annotated image
shows that also three faraway galaxies also made it to the image, known only by
their number in the PGC catalogue.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/03/03iris.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/03/03iris.jpg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/03/03iris_annotated.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/03/03iris_annotated.jpg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Acquisition details&lt;/h3&gt;
&lt;p&gt;Total exposure time: 3 hours 50 minutes. Focal length: ~440mm.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Telescope: Vixen ED114SS with 0.67x reducer-flattenner.&lt;/li&gt;
&lt;li&gt;Camera: QHY168C&lt;/li&gt;
&lt;li&gt;Filter: Optolong UV/IR cut.&lt;/li&gt;
&lt;li&gt;Mount: iOptron GEM28&lt;/li&gt;
&lt;li&gt;Focus and Dew Heater Controller: Astrolink 4 Mini S&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Acquired using KStars on a Raspberry Pi running &lt;a href=&quot;https://astroberry.io/&quot;&gt;https://astroberry.io/&lt;/a&gt;. Processed in PixInsight.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.astrobin.com/0j4r6g/&quot;&gt;Visit my gallery on Astrobin.com&lt;/a&gt;&lt;/p&gt;
</description>
      <pubDate>Mon, 05 Dec 2022 24:00:00 GMT</pubDate>
      <dc:creator>STFN</dc:creator>
      <guid>https://stfn.pl/blog/03-space-iris/</guid>
    </item>
    <item>
      <title>Raspberry Pi Pico Weather Station - part II</title>
      <link>https://stfn.pl/blog/04-pico-weather-station2/</link>
      <description>&lt;p&gt;This post is a continuation of Part I in which we build a Raspberry Pi Pico W
with BME280 and DHT11 sensors and made it retrieve weather data. In this part
we&#39;ll connect to Wifi to send that data to a MQTT broker on another machine.&lt;/p&gt;
&lt;p&gt;I&#39;ll continue where I left in the previous part, so it&#39;s crucial that you first
reach the state described in the end of Part I in order to continue with this
part.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/02/dht1.jpeg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/02/dht1.jpeg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Starting simple - connecting to Wifi&lt;/h2&gt;
&lt;p&gt;Let&#39;s start with just connecting to Wifi. The code snippet below is taken from
&lt;a href=&quot;https://docs.micropython.org/en/latest/esp8266/tutorial/network_basics.html&quot;&gt;MicroPython
docs&lt;/a&gt;,
however I spiced it up to make debugging a little bit easier later on.&lt;/p&gt;
&lt;p&gt;In Thonny, create a file with a nice name like &lt;code&gt;connect.py&lt;/code&gt; on the Raspberry Pi Pico W.&lt;/p&gt;
&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; time
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; network
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; machine

led &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; machine&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Pin&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;LED&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; machine&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Pin&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;OUT&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

ssid &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;NETWORK-NAME&quot;&lt;/span&gt;
password &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;PASSWORD&quot;&lt;/span&gt;

wlan &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; network&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;WLAN&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;network&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;STA_IF&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
wlan&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;active&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
wlan&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;connect&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ssid&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; password&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

max_wait &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;while&lt;/span&gt; max_wait &lt;span class=&quot;token operator&quot;&gt;&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;
    led&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;on&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    time&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sleep&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    led&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;off&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;if&lt;/span&gt; wlan&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isconnected&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 keyword&quot;&gt;break&lt;/span&gt;
    max_wait &lt;span class=&quot;token operator&quot;&gt;-=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;
    time&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sleep&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;not&lt;/span&gt; wlan&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isconnected&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 keyword&quot;&gt;for&lt;/span&gt; _ &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;5&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;
        led&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;on&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        time&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sleep&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        led&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;off&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        time&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sleep&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        led&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;on&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        time&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sleep&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        led&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;off&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        time&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sleep&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.7&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    time&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sleep&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    machine&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;reset&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;else&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    status &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; wlan&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ifconfig&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    led&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;on&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    time&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sleep&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    led&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;off&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;Let&#39;s now dissect the code to better understand what is happening.&lt;/p&gt;
&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; time
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; network
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; machine

led &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; machine&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Pin&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;LED&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; machine&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Pin&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;OUT&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

ssid &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;NETWORK NAME&quot;&lt;/span&gt;
password &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;PASSWORD&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To start, some configuration, import the needed modules, setting up the built-in
LED and defining the Wifi name and password. Set your own values of course :)&lt;/p&gt;
&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;wlan &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; network&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;WLAN&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;network&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;STA_IF&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
wlan&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;active&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
wlan&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;connect&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ssid&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; password&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Set the Wifi module to station mode. This is needed because Pico W by default is
set in access point mode, but we will be using it to connect to another network.
Then activate the interface and finally connect.&lt;/p&gt;
&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;max_wait &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;while&lt;/span&gt; max_wait &lt;span class=&quot;token operator&quot;&gt;&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;
    led&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;on&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    time&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sleep&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    led&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;off&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;if&lt;/span&gt; wlan&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isconnected&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 keyword&quot;&gt;break&lt;/span&gt;
    max_wait &lt;span class=&quot;token operator&quot;&gt;-=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;
    time&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sleep&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Wait 10 seconds for the establishing the connection. Blink the LED each second.
If Pico connected to Wifi, end the waiting loop.&lt;/p&gt;
&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;not&lt;/span&gt; wlan&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isconnected&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 keyword&quot;&gt;for&lt;/span&gt; _ &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;5&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;
        led&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;on&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        time&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sleep&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        led&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;off&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        time&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sleep&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        led&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;on&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        time&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sleep&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        led&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;off&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        time&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sleep&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.7&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    timer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sleep&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    machine&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;reset&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;else&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    status &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; wlan&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ifconfig&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    led&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;on&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    time&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sleep&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    led&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;off&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;This part of code is probably self explanatory. If after 10 seconds connection
was not established, do two rapid blinks every 0.7 seconds, five times, wait one
second and reset the machine to try again. If the connection was established,
light the LED for three seconds.&lt;/p&gt;
&lt;p&gt;Why blink the LED when we can see on the screen if the connection was
established or not? This is going to be a weather station, and there is little
point of having a weather measuring device always connected to a computer with a
screen, isn&#39;t it? By having the LED show the connection status, we will be able
to see what&#39;s going on without a screen, with the Pico running on it&#39;s own, on a
balcony, in a shed or a field.&lt;/p&gt;
&lt;p&gt;I added the reset-and-try-again code because from my experience, Wifi on a Pico
W is often janky, and tends to sometimes fail connecting. Trying again often
solves the issue. As an additional exercise, you might implementing resetting
the Pi after an increasing interval of time, that would be a nice exercise in
Python.&lt;/p&gt;
&lt;h2&gt;Sending data with MQTT&lt;/h2&gt;
&lt;p&gt;Awesome, we can now connect to Wifi, now let&#39;s send some data! This section will
be connecting what we achieved in Part I with Wifi, so again, if you didn&#39;t do
Part I, go there first.&lt;/p&gt;
&lt;p&gt;As the data protocol I have chosen &lt;a href=&quot;https://mqtt.org/&quot;&gt;MQTT&lt;/a&gt;, because it is the
industry standard for Internet of Things (IoT) communication, it&#39;s lightweight
and can be easily implemented on a Pico W, so it&#39;s perfect for our usecase.&lt;/p&gt;
&lt;p&gt;To start with MQTT, we&#39;ll need a broker. Broker is a piece of software that will
collect the data send by the Pico, and allow other programs to digest that data.
It has to be run on a different machine than a Pico, any Linux or Windows
machine will do, but preferably it should be something that is running 24/7 to
collect data without stoppages. A popular broker is Mosquitto, and you can
install it from its &lt;a href=&quot;https://mosquitto.org/download/&quot;&gt; Downloads page&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Once it is installed, you should have some additional tools available as well,
the first one we&#39;ll use is &lt;code&gt;mosquitto_sub&lt;/code&gt;, a CLI tool to subscribe to
topics.&lt;/p&gt;
&lt;p&gt;MQTT is based on subscribing and submitting to topics. A topic is like a
newsletter, you can send messages to it, and everyone that has subscribed to
that particular newsletter will receive everything that you are sending.&lt;/p&gt;
&lt;p&gt;Fire up a terminal window, and run&lt;/p&gt;
&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;mosquitto_sub  &lt;span class=&quot;token parameter variable&quot;&gt;-t&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;#&#39;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;-t &#39;#&#39;&lt;/code&gt; means that we have subscribed to any possible topic. The process will
now wait for any message sent on a topic to the machine that you are running the
subcriber on. Leave it running in the background, it will be useful in just a
moment. For now, let&#39;s go back to Thonny.&lt;/p&gt;
&lt;p&gt;There, we will expand the script that we just created, combining the Wifi
connection magic, with MQTT spells and what we did in Part I.&lt;/p&gt;
&lt;p&gt;As for the library to use MQTT on a Pico, &lt;code&gt;umqtt&lt;/code&gt; should be available straight
from the box as a part of MicroPython core, but if for any reason it&#39;s not there
on your device, try installing it the same way as we installed &lt;code&gt;bme280&lt;/code&gt; in Part
I.&lt;/p&gt;
&lt;p&gt;Right, so, the full code for this part:&lt;/p&gt;
&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; json
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; time

&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; bme280
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; dht
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; machine
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; network
&lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; umqtt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;simple &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; MQTTClient

led &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; machine&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Pin&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;LED&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; machine&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Pin&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;OUT&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

ssid &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;NETWORK NAME&quot;&lt;/span&gt;
password &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;PASSWORD&quot;&lt;/span&gt;

wlan &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; network&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;WLAN&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;network&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;STA_IF&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
wlan&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;active&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
wlan&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;connect&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ssid&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; password&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

max_wait &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;while&lt;/span&gt; max_wait &lt;span class=&quot;token operator&quot;&gt;&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;
    led&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;on&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    time&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sleep&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    led&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;off&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;if&lt;/span&gt; wlan&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isconnected&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 keyword&quot;&gt;break&lt;/span&gt;
    max_wait &lt;span class=&quot;token operator&quot;&gt;-=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;
    time&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sleep&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;not&lt;/span&gt; wlan&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isconnected&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 keyword&quot;&gt;for&lt;/span&gt; _ &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;5&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;
        led&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;on&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        time&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sleep&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        led&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;off&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        time&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sleep&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        led&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;on&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        time&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sleep&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        led&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;off&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        time&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sleep&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.7&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    time&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sleep&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    machine&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;reset&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;else&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    status &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; wlan&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ifconfig&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    led&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;on&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    time&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sleep&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    led&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;off&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

SCL_PIN &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; machine&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Pin&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
SDA_PIN &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; machine&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Pin&lt;span class=&quot;token punctuation&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;
DHT_PIN &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; machine&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Pin&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

i2c &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; machine&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;I2C&lt;span class=&quot;token punctuation&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; scl&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;SCL_PIN&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; sda&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;SDA_PIN&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
bme_sensor &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; bme280&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;BME280&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;i2c&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;i2c&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
d_sensor &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; dht&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;DHT11&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;DHT_PIN&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

temperature&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; pressure&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; _ &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; bme_sensor&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;read_compensated_data&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
temperature &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; temperature &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;
pressure &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; pressure &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;256&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;

d_sensor&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;measure&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
humidity &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; d_sensor&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;humidity&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

payload &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 string&quot;&gt;&quot;temperature&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; temperature&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;pressure&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; pressure&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;humidity&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; humidity&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

qt &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; MQTTClient&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;umqtt_client&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;BROKER_IP&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; keepalive&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3600&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
qt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;connect&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
qt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;publish&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;b&quot;weather&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; json&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;dumps&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;payload&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
qt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;disconnect&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;The code above is basically the combination of connecting to Wifi from the
beginning of this post, and gathering data from sensors in Part I, but there are
some slight changes, and new parts, so let us focus on those.&lt;/p&gt;
&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; json
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; time

&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; bme280
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; dht
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; machine
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; network
&lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; umqtt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;simple &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; MQTTClient&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note that we need to import some more libraries to do the things that need to happen.&lt;/p&gt;
&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;payload &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 string&quot;&gt;&quot;temperature&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; temperature&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;pressure&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; pressure&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;humidity&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; humidity&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;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;payload&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
qt &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; MQTTClient&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;umqtt_client&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;BROKER_IP&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; keepalive&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3600&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
qt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;connect&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
qt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;publish&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;b&quot;weather&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; json&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;dumps&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;payload&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
qt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;disconnect&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;As previously, we are building the payload, but then something new happens.
Instead of just printing it, a MQTT client is initialized, we attempt to connect
to the broker, and yes, the data is send! Then it&#39;s only a matter of closing the
connection, and we are done.&lt;/p&gt;
&lt;p&gt;The BROKER_IP needs to be changed to the IP of the computer that you are running
&lt;code&gt;mosquitto_sub&lt;/code&gt;. Remember that this needs to be the internal LAN IP of the
machine. On Linux you can find it using &lt;code&gt;ip addr&lt;/code&gt; in the terminal.&lt;/p&gt;
&lt;p&gt;Run the script in Thonny. If everything is configured correctly, the weather
data should be printed out in the Thonny console. But that&#39;s not everything, go
to the terminal window with &lt;code&gt;mosquitto_sub&lt;/code&gt; running. In it you should see the
payload as well! This is how it looked like for me:&lt;/p&gt;
&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;stefan@home:~ $ mosquitto_sub  &lt;span class=&quot;token parameter variable&quot;&gt;-t&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;#&#39;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;pressure&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1012&lt;/span&gt;, &lt;span class=&quot;token string&quot;&gt;&quot;humidity&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;45&lt;/span&gt;, &lt;span class=&quot;token string&quot;&gt;&quot;temperature&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;24&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Every time you run the script, a new line should appear, with new readouts.&lt;/p&gt;
&lt;p&gt;This is a great step ahead in our journey to create a RPi Pico weather station,
now the sensors data can be seen on a different machine on the network, a
computer that is much more powerful than a Pico. And so that data can be
manipulated, aggregated, presented on a graph or used in an automation, the
possibilities are really endless here. But that all will come in Part III, where
we will use the data to show it on a Grafana dashboard.&lt;/p&gt;
&lt;p&gt;As a homework, think how would you make the Pico collect and send the data
continuously, for example every 5 minutes?&lt;/p&gt;
&lt;p&gt;Thanks for reading!&lt;/p&gt;
</description>
      <pubDate>Mon, 12 Dec 2022 24:00:00 GMT</pubDate>
      <dc:creator>STFN</dc:creator>
      <guid>https://stfn.pl/blog/04-pico-weather-station2/</guid>
    </item>
    <item>
      <title>My 2022 in astrophotography</title>
      <link>https://stfn.pl/blog/05-astrophotography-2022/</link>
      <description>&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/06/2022_sml.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/06/2022_sml.jpg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This is my 2022 astrophotograpy summary. It wasn&#39;t great, it wasn&#39;t terrible.
There were some firsts for me: first time using Pixinsight, first time combining
data from several nights, first time using a dedicated astro camera... I didn&#39;t
spend as much time as I wanted on astro, but all in all it didn&#39;t go that bad.
Let&#39;s hope the upcoming year will be better!&lt;/p&gt;
&lt;p&gt;Images, from top left:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;NGC 7635, the Bubble Nebula and Messier 52 open star cluster, 141&amp;quot;.
&lt;a href=&quot;https://www.astrobin.com/l485ez/&quot;&gt;Astrobin&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Horsehead Nebula, first time using a field flattener and combining material
from 3 nights, 216&amp;quot;. &lt;a href=&quot;https://www.astrobin.com/citu26/&quot;&gt;Astrobin&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Rosette Nebula, edited in pseudo HOO, 216&amp;quot;.
&lt;a href=&quot;https://www.astrobin.com/9x1jz5/&quot;&gt;Astrobin&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Veil Nebula, first time using an astro camera, QHY168C, only 40 minutes,
because I imaged on a hot summer night, and I could not stand the mosquittos
any longer :) Shot using an old Russian lens, Jupiter 37A, 135mm, 40&amp;quot;.&lt;/li&gt;
&lt;li&gt;Soul Nebula, 60&amp;quot; captured through intermittent clouds.&lt;/li&gt;
&lt;li&gt;Barnard 11 and friends, 123&amp;quot;.&lt;/li&gt;
&lt;li&gt;Iris Nebula, 230&amp;quot;, almost 5 hours in a single session, finally the dust
started to come out nicely. &lt;a href=&quot;https://www.astrobin.com/0j4r6g/&quot;&gt;Astrobin&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The Horsehead, Rosette, and Veil were imaged through an Optolong L-enhance
filter, for the rest I used an Optolong UV/IR filter. Everything apart from the
Veil was done using a Vixen ED114SS 600mm f/5 telescope, a GEM28 mount and a
bunch of auxiliary gear I will mention in the future posts.&lt;/p&gt;
&lt;p&gt;Plans for 2023? I want to revisit a few places, definitely try out some galaxies
like M101, M33, Leo Triplet, get my first globular cluster. My dream is a good
photo of the Witch&#39;s Head, maybe a glimpse of Rho Opuchi. We&#39;ll see, I am
planning to revisit this post a year from now and check what I managed to
achieve from my plans.&lt;/p&gt;
&lt;p&gt;Clear skies everyone and a happy new astro year!&lt;/p&gt;
</description>
      <pubDate>Fri, 30 Dec 2022 24:00:00 GMT</pubDate>
      <dc:creator>STFN</dc:creator>
      <guid>https://stfn.pl/blog/05-astrophotography-2022/</guid>
    </item>
    <item>
      <title>Powering Raspberry Pi Pico with AA batteries (Weather Station Part III)</title>
      <link>https://stfn.pl/blog/06-pico-aa-batteries/</link>
      <description>&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/06/IMG_4121.JPEG&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/06/IMG_4121.JPEG&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Continuing on what we did in &lt;a href=&quot;https://stfn.pl/blog/04-pico-weather-station2&quot;&gt;part II of making a RPi Pico Weather
Station&lt;/a&gt;, now it&#39;s time to
untether the Pico from the PC.&lt;/p&gt;
&lt;p&gt;The simplest way is to connect the Pico to a USB charger or a powerbank via its
USB port, it does not require any additional hardware and works out of the box,
but it has some drawbacks.&lt;/p&gt;
&lt;p&gt;The main downside of powering a Pico from a powerbank is that such devices often
have a minimal required power draw for them to work, and if the connected device
uses less energy, the powerbank assumes it was disconnected and turns off. While
this causes no problems for phones etc, it can lead to bad consequences when
using a microcontroller like a Pico. When a Pico goes to sleep (for example when
waiting during &lt;code&gt;time.sleep()&lt;/code&gt;) it draws so little energy that the
powerbank may assume there is nothing connected, turns off, and the Pico never
wakes up.&lt;/p&gt;
&lt;p&gt;That&#39;s way a dumber energy source will be a better energy source for our needs.
In this episode I will show &lt;strong&gt;how to power a Pico from 4 AA batteries&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Going back to the Pico Pinout diagram, the pin that interests us is VSYS, the
second pin on the right when looking at the Pico with the USB port at the top.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://datasheets.raspberrypi.com/pico/Pico-R3-A4-Pinout.pdf&quot;&gt;&lt;img src=&quot;https://d233f3e99deg9m.cloudfront.net/pinout.png&quot; alt=&quot;alt
text&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;To quote the Pico datasheet:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;VSYS is the main system input voltage, which can vary in the allowed range
1.8V to 5.5V, and is used by the on-board SMPS to generate the 3.3V for the
RP2040 and its GPIO.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;What we need to carry out from that sentence is that the Pico can be powered by
providing positive voltage to that pin and that voltage needs to be between 1.8V
and 5.5V. This means that rechargeable AA batteries will be perfect for the job,
as fully charged their voltage is around 1.3V, and four of them in series should
provide us with 5.2V, well within the acceptable range.&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;warning&quot;&gt;Only use rechargeable batteries for this project, Alkaline
(li-ion) AAs have a higher full charge voltage of around 1.5V, four of them will
amount to 6V which can be tragic for the Pico. Be careful and if unsure, check
the voltage with a multimeter before connecting them to the board.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;As the microcontroller runs, the batteries will slowly lose their charge and
their voltage will drop. Once the combined voltage reaches 1.8V, the board will
turn off and the batteries will need to be recharged.&lt;/p&gt;
&lt;h2&gt;Connecting batteries to Raspberry Pi Pico&lt;/h2&gt;
&lt;p&gt;To connect the battery holder, I used an ARC screw connector that I soldered to
the board and place short cables between its leads and between it and the Pico&#39;s
VSYS and GND pins. Always double, triple check the polarity! The red cable from
the battery pack is the positive that needs to go to VSYS and the black one
needs to be connected to GND.&lt;/p&gt;
&lt;p&gt;The battery holder is a simple four AA holder. The batteries are connected in
series, so their voltages add up, and the whole pack has the capacity of a
single battery.&lt;/p&gt;
&lt;p&gt;Before connecting the holder cables to the Pi, make sure the battery holder is
turned off.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/06/image_50390529.JPG&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/06/image_50390529.JPG&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/06/image_50396929.JPG&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/06/image_50396929.JPG&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Making the code run without a PC connection&lt;/h2&gt;
&lt;p&gt;There is a few things that we need to change with our code that we created in
Part II. This is the first time in the series where filenames become important.
Raspberry Pi Pico with Micropython has two special filenames: &lt;code&gt;boot.py&lt;/code&gt;
and &lt;code&gt;main.py&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;boot.py&lt;/code&gt; is always run when the Pico is powered up. There is no need to
call it from an IDE like Thonny, it will run automatically. This is where we can
do some initial setup, like connecting to a network. Once &lt;code&gt;boot.py&lt;/code&gt;
finishes successfully, the execution is passed to &lt;code&gt;main.py&lt;/code&gt;, in which
there will be our main program loop. With every cycle of that loop, the Pico
will measure the environment data, send it to the MQTT broker, and fall asleep
for a certain amount of time, and the loop will go into another cycle. Forever,
or until the power dies.&lt;/p&gt;
&lt;p&gt;Let&#39;s go with code. First the booting part, with no changes to what was in the
previous episode.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;boot.py&lt;/code&gt;&lt;/p&gt;
&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; time
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; network
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; machine

led &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; machine&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Pin&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;LED&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; machine&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Pin&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;OUT&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

ssid &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;NETWORK-SSID&quot;&lt;/span&gt;
password &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;NETWOTK-PASSWORD&quot;&lt;/span&gt;

wlan &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; network&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;WLAN&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;network&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;STA_IF&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
wlan&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;active&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
wlan&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;connect&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ssid&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; password&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

max_wait &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;while&lt;/span&gt; max_wait &lt;span class=&quot;token operator&quot;&gt;&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;
    led&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;on&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    time&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sleep&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    led&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;off&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;if&lt;/span&gt; wlan&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isconnected&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 keyword&quot;&gt;break&lt;/span&gt;
    max_wait &lt;span class=&quot;token operator&quot;&gt;-=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;
    time&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sleep&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;not&lt;/span&gt; wlan&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isconnected&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 keyword&quot;&gt;for&lt;/span&gt; _ &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;5&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;
        led&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;on&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        time&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sleep&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        led&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;off&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        time&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sleep&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        led&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;on&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        time&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sleep&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        led&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;off&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        time&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sleep&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.7&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    time&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sleep&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    machine&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;reset&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;else&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    status &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; wlan&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ifconfig&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    led&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;on&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    time&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sleep&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    led&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;off&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;And then the main part. Compared to the previous version of the code, the one
major change is that the measuring and sending is now inside an endless loop.
I&#39;ve set the sleep time between measurements to 30 seconds, as I find a
reasonable resolution for doing temperature readings. Reducing the sleep time
will increase the data resolution, increasing it will extend the battery life.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;main.py&lt;/code&gt;&lt;/p&gt;
&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; json
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; time

&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; bme280
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; dht
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; machine
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; network
&lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; umqtt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;simple &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; MQTTClient

led &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; machine&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Pin&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;LED&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; machine&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Pin&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;OUT&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

SCL_PIN &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; machine&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Pin&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
SDA_PIN &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; machine&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Pin&lt;span class=&quot;token punctuation&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;
DHT_PIN &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; machine&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Pin&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

i2c &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; machine&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;I2C&lt;span class=&quot;token punctuation&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; scl&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;SCL_PIN&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; sda&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;SDA_PIN&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
bme_sensor &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; bme280&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;BME280&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;i2c&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;i2c&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
d_sensor &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; dht&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;DHT11&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;DHT_PIN&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

wlan &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; network&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;WLAN&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;network&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;STA_IF&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;not&lt;/span&gt; wlan&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isconnected&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;
        machine&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;restart&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    temperature&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; pressure&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; _ &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; bme_sensor&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;read_compensated_data&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    temperature &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; temperature &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;
    pressure &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; pressure &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;256&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;

    d_sensor&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;measure&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    humidity &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; d_sensor&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;humidity&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    payload &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 string&quot;&gt;&quot;temperature&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; temperature&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&quot;pressure&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; pressure&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&quot;humidity&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; humidity&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    qt &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; MQTTClient&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;umqtt_client&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;192.168.0.176&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; keepalive&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3600&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    qt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;connect&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    qt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;publish&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;b&quot;sensors&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; json&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;dumps&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;payload&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    qt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;disconnect&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    led&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;on&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    time&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sleep&lt;span class=&quot;token punctuation&quot;&gt;(&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;
    led&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;off&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    time&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sleep&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;30&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Fire it up!&lt;/h2&gt;
&lt;p&gt;Now it&#39;s the moment of truth. Make sure both files are saved on the Pico. Unplug
it from the PC. If you haven&#39;t done it already, connect the battery holder to
the Pico. Once again, double check the polarity and whether the cables are
connected to the right pins.&lt;/p&gt;
&lt;p&gt;Turn on the battery holder. The Pico LED should start blinking slowly as it&#39;s
connecting to the Wifi. If successful, there should be a long blink confirming
it has connected and sending data. If there are double quick blinks, it means it
has failed connecting and will restart and try again. If there were a few
unsuccessful retries, turn of the batteries, connect it back to the PC and check
everything again, pay special attention to the Wifi credentials.&lt;/p&gt;
&lt;p&gt;And that&#39;s it, you are done and can boast your own, battery operated, Raspberry
Pi Pico Weather Station!&lt;/p&gt;
&lt;h2&gt;How long will it last&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/06/IMG_3968.JPEG&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/06/IMG_3968.JPEG&quot; alt=&quot;&quot;&gt;&lt;/a&gt;
&lt;span class=&quot;image_sub&quot;&gt;The Pico just turned off with batteries
depleted.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;I did several tests of how long the Pico can run from AA batteries. With the
2450mAh IKEA LADDE batteries, I&#39;ve been getting uptimes of around 50 hours
maximum with the environment sensing code seen above, in temperatures betwenn 10
and 0C.&lt;/p&gt;
&lt;p&gt;Of course the timespan will depend on a lot of factors:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;the health of the batteries, fresh, healthy batteries make a big difference&lt;/li&gt;
&lt;li&gt;work load, if the Pico mostly sleeps between short times of running, the
battery runtime will be much longer&lt;/li&gt;
&lt;li&gt;temperature outside, the colder the shorter the runtime&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And that&#39;s it, you should now have a Raspberry Pi Pico that can independently of
a wall socket or you computer. In the future I am planning to try running a Pico
from a li-ion 3.7V battery together with a TP4056 battery charging module, and
compare it with using AA batteries. We&#39;ll see how it turns out!&lt;/p&gt;
</description>
      <pubDate>Tue, 03 Jan 2023 24:00:00 GMT</pubDate>
      <dc:creator>STFN</dc:creator>
      <guid>https://stfn.pl/blog/06-pico-aa-batteries/</guid>
    </item>
    <item>
      <title>Big Clive&#39;s NiMH charger</title>
      <link>https://stfn.pl/blog/07-nimh-charger/</link>
      <description>&lt;p&gt;So, there&#39;s this Scottish Youtuber, Big Clive, who has a channel about
electronics and electrical appliances, and I&#39;ve been binge watching him for the
last few months. To be honest, he was one of my main inspirations in getting a
soldering iron.&lt;/p&gt;
&lt;p&gt;In one of his videos, he built a trickle charger for NiMH and NiCd batteries,
and was so kind to provide the files needed to have the PCB files ordered to do
the charger by yourself. The project seemed easy enough and giving a lot of
satisfaction so I could not stop myself from doing it on my own.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=wVnAH17f4jg&quot;&gt;Here is the video I&#39;m talking about, the link to the PCB designs is in the
description.&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I ordered the PCBs from &lt;a href=&quot;https://jlcpcb.com&quot;&gt;JLCPCB&lt;/a&gt;, and it was only a few
dollars, and the shipping took around 2-3 weeks.&lt;/p&gt;
&lt;p&gt;After ordering the PCBs, I started looking for the other components.&lt;/p&gt;
&lt;p&gt;The hardest part was finding the right micro USB connectors. To match the board,
the connectors had to have four pins in two pairs, and preferably all pointing
downwards to the board. I could not find the right ones in any of the Polish
electronics shops nor e-commerce platforms. Eventually I found the right parts
somewhere around the 8th page of Aliexpress search results, so I ordered 20 of
them to be safe for the future.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/07/00.jpeg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/07/00.jpeg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The rest of the elements was much simpler to find, yet I still had to source
them from three different places. That was so surprising, as everything here
seems to a standard electrical component. Looks like GPUs are not the only
things suffering from shortage. Anyway, here are all of the required components.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Lol, only after taking the photos, soldering everything together etc I realised
I did not include the LEDs in the pic.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Part list:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;PCB from Big Clive&#39;s design&lt;/li&gt;
&lt;li&gt;micro USB connectors&lt;/li&gt;
&lt;li&gt;AA battery holders&lt;/li&gt;
&lt;li&gt;120 Ohm resistors&lt;/li&gt;
&lt;li&gt;470 Ohm resistors&lt;/li&gt;
&lt;li&gt;1N4007 diodes&lt;/li&gt;
&lt;li&gt;colour LEDs (not shown)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn.pl/blog/07-nimh-charger/%22https:/stfn-pl-blog-assets.b-cdn.net/07/01.JPEG&quot;&gt;&lt;img src=&quot;https://stfn.pl/blog/07-nimh-charger/%22https:/stfn-pl-blog-assets.b-cdn.net/07/01.JPEG&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I first soldered the USB connector. Fit like an USB glove.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/07/02.JPEG&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/07/02.JPEG&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Then came the 1N4007 diodes.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/07/03.JPEG&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/07/03.JPEG&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;120 Ohm resistors came next. I helped myself with some electrical tape to keep
everything in place as I was soldering from the other side of the board.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/07/04.JPEG&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/07/04.JPEG&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I don&#39;t what why I kept forgetting about the LEDs, I did not take a picture as
they were soldered, so here is a photo with the LEDs and half of the battery
holders. I taped the holders from both ends because they refused to lie flat on
the board.&lt;/p&gt;
&lt;p&gt;And don&#39;t forget about that 470 Ohm resistor at the end of the board!&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/07/05.JPEG&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/07/05.JPEG&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;And the ready charger.  For testing I put some IKEA LADDA 2450mAh rechargeable
batteries which I had discharged to around 1.12 Volt.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/07/06.JPEG&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/07/06.JPEG&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Look how pretty it is, I really like how it turned out with those warm amber
LEDs, it could as well be a nice night lamp, or I might hang it on a wall to
just be there and present itself. I also added some legs so that it does not lie
on the soldered ends. In the future I might make another one and combine them
into a tasty charger sandwich.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/07/07.jpeg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/07/07.jpeg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;And does it charge? I measured the charge current, I got 50mA throught the
battery connectors. As I mentioned above, I put four discharged AA batteries to
see what happens. After 48 hours I put them to my &amp;quot;normal charger&amp;quot; which
reported that they were halfway charged. After four days, they were &amp;quot;almost
charged&amp;quot;. Clive said it should take around a week to charge high capacity
batteries, and that seems to be true in my case.&lt;/p&gt;
&lt;p&gt;Thanks for reading, that was a fun little project, took some planning and
searching and waiting, but eventually turned out very well.&lt;/p&gt;
&lt;p&gt;If you liked it, please checkout &lt;a href=&quot;https://www.patreon.com/bigclive&quot;&gt;Big Clive&#39;s Patreon
page&lt;/a&gt;.&lt;/p&gt;
</description>
      <pubDate>Mon, 30 Jan 2023 24:00:00 GMT</pubDate>
      <dc:creator>STFN</dc:creator>
      <guid>https://stfn.pl/blog/07-nimh-charger/</guid>
    </item>
    <item>
      <title>Set up GPSD with U-blox7 GPS Dongle on Linux</title>
      <link>https://stfn.pl/blog/08-gpsd-linux-ublox/</link>
      <description>&lt;p&gt;If you are in hurry and want to skip my ponderings and jump straight to the
solution, &lt;a href=&quot;https://stfn.pl/blog/08-gpsd-linux-ublox/#solution&quot;&gt;le click here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The fix was tested on a Raspberry Pi running astrobbery.io, a Linux Mint PC and
a Manjaro laptop. Worked fine in all cases.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/08/IMG-4275.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/08/IMG-4275.jpg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Background&lt;/h2&gt;
&lt;p&gt;I bought a small and cheap GPS/GLONASS USB dongle called U-blox7. I got it
mostly to tinker with GPS data, but also to use in my astrophotography rig as a
way to get location, and most of all, time from GPS satellites.&lt;/p&gt;
&lt;p&gt;Why time? Because one of my astroberry devices, &lt;a href=&quot;https://shop.astrojolo.com/astrolink-4-pi/&quot;&gt;Astrolink 4
Pi&lt;/a&gt; does not have a RTC clock, and
so &amp;quot;forgets&amp;quot; the time everytime it&#39;s turned off. And when I am using it out in
the open field without internet access getting the time from ntp is not
possible. Knowing the current time is needed to provide correct FITS header data
to astrophotography images, which in turn allows easier plate solving, not to
mention simplifing going throught the history of captures.&lt;/p&gt;
&lt;p&gt;Happy that it finally arrived in the mail, I jumped in front of my PC, went
throught the GPS for Linux docs, and installed &lt;code&gt;gpsd&lt;/code&gt;, the GPS managing Linux
daemon, and &lt;code&gt;gpsd-clients&lt;/code&gt;, a set of clients taking data from &lt;code&gt;gpsd&lt;/code&gt; and
presenting it in a useful manner:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;apt&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; gpsd
&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;apt&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; gpsd-clients&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;...and it did not work.&lt;/p&gt;
&lt;h2&gt;Problem&lt;/h2&gt;
&lt;p&gt;My GPS device was not found by &lt;code&gt;gpsd&lt;/code&gt;. &lt;code&gt;gpsd&lt;/code&gt; was reporting no devices when
monitored by &lt;code&gt;cgps&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;When running &lt;code&gt;cgps&lt;/code&gt; I got an empty table, and among the logs there was an
ominous&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;class&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;DEVICES&quot;&lt;/span&gt;,&lt;span class=&quot;token string&quot;&gt;&quot;devices&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;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Cause&lt;/h2&gt;
&lt;p&gt;A quick browse of the intertubes suggested checking if the device is actually
registered by the OS. By using &lt;code&gt;dmesg&lt;/code&gt; I found out that the usb dongle was
registered by the system as &lt;code&gt;/dev/ttyACM0&lt;/code&gt;, while all gpsd docs mention
&lt;code&gt;dev/ttyUSB0&lt;/code&gt; as the default device. That seemed to be the cause of the issue.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;dmesg&lt;/code&gt; output:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;7225.765450&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; usb &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;-3: new full-speed USB device number &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt; using xhci_hcd
&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;7226.007961&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; usb &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;-3: New USB device found, &lt;span class=&quot;token assign-left variable&quot;&gt;idVendor&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1546&lt;/span&gt;, &lt;span class=&quot;token assign-left variable&quot;&gt;idProduct&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;01a7, &lt;span class=&quot;token assign-left variable&quot;&gt;bcdDevice&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1.00&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;7226.007968&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; usb &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;-3: New USB device strings: &lt;span class=&quot;token assign-left variable&quot;&gt;Mfr&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;, &lt;span class=&quot;token assign-left variable&quot;&gt;Product&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;, &lt;span class=&quot;token assign-left variable&quot;&gt;SerialNumber&lt;/span&gt;&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 number&quot;&gt;7226.007970&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; usb &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;-3: Product: u-blox &lt;span class=&quot;token number&quot;&gt;7&lt;/span&gt; - GPS/GNSS Receiver
&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;7226.007971&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; usb &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;-3: Manufacturer: u-blox AG - www.u-blox.com
&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;7226.031755&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; cdc_acm &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;-3:1.0: ttyACM0: USB ACM device&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Solution: configuring gpsd for the right device&lt;/h2&gt;
&lt;p&gt;&lt;a name=&quot;solution&quot;&gt;&lt;/a&gt;One way to fix it is to stop the &lt;code&gt;gpsd&lt;/code&gt; instance that
started with the boot of system, and run a new one with the correct device.&lt;/p&gt;
&lt;p&gt;Killing all gpsd processes:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; systemctl stop gpsd
&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; systemctl stop gpsd.socket
&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;rm&lt;/span&gt; /var/run/gpsd.sock &lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Starting gpsd manually, in the foreground and with debug ON:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo gpsd -D 5 -N /dev/ttyACM0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;gpsd&lt;/code&gt; should now run fine and healthy. Open &lt;code&gt;cgps&lt;/code&gt; in a different tab, and it
should report a device and start collecting data from it. If not, continue to
the next step:&lt;/p&gt;
&lt;h2&gt;Solving the permissions problem&lt;/h2&gt;
&lt;p&gt;It is possible that even after selecting the correct device, &lt;code&gt;gpsd&lt;/code&gt; will still
not be able to use it. This is caused by the service not having the permissions
to access the &lt;code&gt;/dev/ttyACM0&lt;/code&gt; device. The solution is to add yourself, meaning
your user, to the &lt;code&gt;dialout&lt;/code&gt; group, which has access rights to tty devices:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;usermod&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-a&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-G&lt;/span&gt; dialout &lt;span class=&quot;token environment constant&quot;&gt;$USER&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After running the command above, try again with running gpsd in the foreground.
&lt;code&gt;gpsd&lt;/code&gt; should now use the correct device and be able to access it. Once you know
it works fine, ctrl+c out of the &lt;code&gt;gpsd&lt;/code&gt; running in the foreground and run &lt;code&gt;gpsd&lt;/code&gt;
as a deamon in the background:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo gpsd /dev/ttyACM0
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Fixing the service&lt;/h2&gt;
&lt;p&gt;Once &lt;code&gt;gpsd&lt;/code&gt; is running fine, the next step is fix the daemon config file, so
that is starts on boot already with the correct device.&lt;/p&gt;
&lt;p&gt;To see how the service is started, I checked &lt;code&gt;/lib/systemd/system/gpsd.service&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;In my case, it looked like this:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;Unit&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;Description&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;GPS &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Global Positioning System&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; Daemon
&lt;span class=&quot;token assign-left variable&quot;&gt;Requires&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;gpsd.socket
&lt;span class=&quot;token comment&quot;&gt;# Needed with chrony SOCK refclock&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;After&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;chronyd.service

&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;Service&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;forking
&lt;span class=&quot;token assign-left variable&quot;&gt;EnvironmentFile&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;-/etc/default/gpsd
&lt;span class=&quot;token assign-left variable&quot;&gt;ExecStart&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;/usr/sbin/gpsd &lt;span class=&quot;token variable&quot;&gt;$GPSD_OPTIONS&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$DEVICES&lt;/span&gt;

&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;Install&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;WantedBy&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;multi-user.target
&lt;span class=&quot;token assign-left variable&quot;&gt;Also&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;gpsd.socket&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The ExecStart line shows that the service is started with the devices taken from
an environmental variable. That variable is taken from the EnvironmentFile line
above.&lt;/p&gt;
&lt;p&gt;Opening that file, I saw&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Devices gpsd should collect to at boot time.&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# They need to be read/writeable, either by user gpsd or the group dialout.&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;DEVICES&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Other options you want to pass to gpsd&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;GPSD_OPTIONS&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I changed it to&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Devices gpsd should collect to at boot time.&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# They need to be read/writeable, either by user gpsd or the group dialout.&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;DEVICES&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;/dev/ttyACM0

&lt;span class=&quot;token comment&quot;&gt;# Other options you want to pass to gpsd&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;GPSD_OPTIONS&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now it was only a matter of restarting the services&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; systemctl restart gpsd
&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; systemctl restart gpsd.socket&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And it works! &lt;code&gt;cgps&lt;/code&gt; reports the correct device, even after restarting the whole system.&lt;/p&gt;
&lt;h2&gt;Using GPSD in KStars and EKOS&lt;/h2&gt;
&lt;p&gt;To use &lt;code&gt;gpsd&lt;/code&gt; in EKOS, fist you need to install the &lt;code&gt;indi_gpsd&lt;/code&gt; driver. I used
the instructions from &lt;a href=&quot;https://www.indilib.org/aux/gps.html&quot;&gt;indilib.org&lt;/a&gt;, here&#39;s a tl;dr
version:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; add-apt-repository ppa:mutlaqja/ppa
&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;apt-get&lt;/span&gt; update
&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;apt-get&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; indi-gpsd&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/08/ekos.png&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/08/ekos.png&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Now in EKOS gear configuration view you can select GPSD under the Aux dropdown.
When you start the profile for the first time, you will be asked if you want to
get the system time from the GPS service. Click yes and you are done!&lt;/p&gt;
</description>
      <pubDate>Sat, 18 Feb 2023 24:00:00 GMT</pubDate>
      <dc:creator>STFN</dc:creator>
      <guid>https://stfn.pl/blog/08-gpsd-linux-ublox/</guid>
    </item>
    <item>
      <title>Powering a Raspberry Pi Pico with solar panels and an 18650</title>
      <link>https://stfn.pl/blog/09-pico-solar-panels/</link>
      <description>&lt;div class=&quot;future&quot;&gt;
This is the description of my first attempt with powering a Pico with solar
panels. There is now a second part, which is much more successful. I highly
recommend to visit Part 2 as well, or instead of this post.
&lt;p&gt;&lt;a href=&quot;https://stfn.pl/blog/34-pico-power-consumption-solar-panels&quot;&gt;Reducing Raspberry Pi Pico W power consumption and a second attempt at using
solar panels&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Somehow this blog is turning into a &amp;quot;how to power things&amp;quot; diary, and this is not
what I intended, but oh well, let&#39;s roll with it.&lt;/p&gt;
&lt;h2&gt;Introduction&lt;/h2&gt;
&lt;p&gt;In this article, I will be using the Raspberry Pi Pico Weather Station that I
built in the previous parts, but the solution is versatile enough to power any
microncontroller or a single board computer. The only limiting factor is that
the power consumption of the device should be low enough to allow it survive the
night from the battery.&lt;/p&gt;
&lt;p&gt;If you know how much your device draws current in miliamps, you can calculate
its battery life by dividing the battery&#39;s capacity in miliamphours by the
current in miliamps. So for example, if you have a battery that can store
2500mAh, and a device that pulls 100mAh, it theoretically should run for 25
hours. Of course theoretically, because there always factors like losses in the
cabling and connectors, or the battery losing capacity with time.&lt;/p&gt;
&lt;h2&gt;Parts List&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;a device you want to power, it can be a Raspberry Pi Pico, an ESP32/8666,
basically anything that can take 5V input&lt;/li&gt;
&lt;li&gt;solar panels&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://botland.com.pl/akcesoria-do-paneli-slonecznych/14423-dfrobot-solar-power-manager-modul-zarzadzania-energia-sloneczna-5v-5903351243841.html&quot;&gt;DFRobot Solar
Manager&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;an &lt;a href=&quot;https://botland.com.pl/569-akumulatory-li-ion?obudowa=18650&quot;&gt;18650
battery&lt;/a&gt; with a
&lt;a href=&quot;https://botland.com.pl/koszyki-na-baterie/16519-koszyk-na-3-akumulatory-typu-18650-bez-przewodow-5904422378165.html&quot;&gt;battery
holder&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;cables and preferably a stripper/crimping tool&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;i&gt;those are not affiliate links, I just like this shop and buy most of my electronics gear from them&lt;/i&gt;&lt;/p&gt;
&lt;p&gt;Word of caution:&lt;/p&gt;
&lt;p&gt;18650 are li-ion batteries, which in contrast to your typical NiMH or NiC
batteries can be very dangerous! They can explode or start a fire if handled
incorrectly! I am not responsible for any damage you might do to yourself or
your surroundings. Sorry.&lt;/p&gt;
&lt;h3&gt;Solar Panels&lt;/h3&gt;
&lt;p&gt;You should look for any panels that output 5V of voltage and at least 1W of
power. Then again, the ones I got are rated for 1W, but max I got with them in
direct sunglight was a quarter for it, so so much for accurate specifications :)&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Spoiler: this will lead to problems later.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;I got mine from Aliexpress.  I bought two of them to provide more power to the
board. Both are rated at 5V, 1W.&lt;/p&gt;
&lt;p&gt;There are two ways of connecting solar panels with each other. There is the
parallel way, and the series way.&lt;/p&gt;
&lt;p&gt;In the serial connection, the negative of one panel is connected to positive of
the second panel. The positive of the first power is connected to the device, as
well as the negative of the second panel. In this connection the voltages of the
panels sum up, but the total current produced is as in a single panel. If I
connected my panels this way, I would get 1W of power at 10V. The solar manager
accepts only 5V, so that would fry it up. Not good.&lt;/p&gt;
&lt;p&gt;In the parallel connection, the positives of each panel are connected to each
other, and the negatives are also connected to each other. The positive
connector of one panel and the negative connector of another panel go to the
powered device. The voltage of the system is as the voltage of the single panel,
but the current is a sum of the current of particular panels. I connected my
panels this way, and I got 2W at 5V, at least theoretically. Perfect!&lt;/p&gt;
&lt;p&gt;Here&#39;s some photos from testing the solar panels. In full sunlight, two panels
connected in parallel produced ~100mA at 5.6V, which gives about half a watt of
power.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/09/IMG_4607.JPEG&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/09/IMG_4607.JPEG&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/09/IMG_4607.JPEG&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/09/IMG_4607.JPEG&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;18650 Battery&lt;/h3&gt;
&lt;p&gt;18650 batteries are sold with or without internal safety measures. Those
failsafes protect the cell from being overcharged, or discharged too much. Both
those scenarios can be destructive to the battery and even dangerous all around.
For this project you can use batteries without protection, as the solar power
manager we will be using has it&#39;s own protective measures, but if you want to
be extra safe, get the protected ones. You will also need a battery holder, if
you know how to solder get one without attached cables, they are more universal.&lt;/p&gt;
&lt;h3&gt;Solar Power Manager&lt;/h3&gt;
&lt;p&gt;For this project I am using a DFRobot Solar Power Manager 2.0. It is a cheap and
useful device that will serve us in several ways at once. First of all, it will
work as as a relay of power from the battery to the microcontroller, and
secondly, it will charge the battery from the solar panels. What is also very
important, it has its own protective measured to charge and discharge the
battery in a safe, controlled way. Several input/output connectors are a nice
additional touch.&lt;/p&gt;
&lt;h2&gt;Connecting Stuff&lt;/h2&gt;
&lt;p&gt;Here&#39;s how everything looks connected together:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/09/IMG_4696.JPEG&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/09/IMG_4696.JPEG&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/09/IMG_4694.JPEG&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/09/IMG_4694.JPEG&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Everything should be straightfoward. I started with soldering cables to the
solar panels, and connecting them to Manager&#39;s Solar + and - connectors. Then I
did the same with the battery holder. Remember to pay double attention to the
polarity of connections.&lt;/p&gt;
&lt;p&gt;After checking that the connections are good and pluses go to pluses and minuses
go to minuses, I connected the Pico using the USB connection. A better way would
be to connect the Pico using the pins on the Manager and the ARC connector on
the RPi, but for quick tests I went with just using USB.&lt;/p&gt;
&lt;p&gt;When the battery was inserted, the Pico came to life! A magical moment &amp;lt;3. If
yours does not want to do the same, try pushing the boot button on the Solar
Manager, sometimes it needs it to start.&lt;/p&gt;
&lt;h2&gt;Making a case&lt;/h2&gt;
&lt;p&gt;As a weather station should work outside, I had to find a case that would
accomodate it. The case should be rainproof, but also allow for air circulation.
It also should not get too hot so that the readings would not be skewed. As this
is a prototype project, I went with what I already had, and I used an IKEA
bathroom cosmetics holder.&lt;/p&gt;
&lt;p&gt;It does the job quite good, it&#39;s white so it won&#39;t get too hot, it has cutoffs
for air circulation, and used upside down it provides protection from the
elements.&lt;/p&gt;
&lt;p&gt;I superglued the battery holder and the power manager to the bottom. For the
Pico, I glued some velcro tapes to one side. This will allow me to replace the
Pico with some other device in the future. Finally outside I glued the solar
panels to some styrofoam to give them a proper angle to the sun.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/09/canon-IMG_2563.JPG&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/09/canon-IMG_2563.JPG&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Testing&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/09/IMG-5221.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/09/IMG-5221.jpg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I took the finished case to my balcony, put it in sunglight and plugged the Pico
in. The Solar Manager reported that it is charging. The Pico started sending
data to the MQTT server.&lt;/p&gt;
&lt;h2&gt;Power issues&lt;/h2&gt;
&lt;p&gt;After a few days of testing it turned out that the solar panels do not give
enough power to keep the battery charged. While this setup does indeed run a bit
longer than running the pico with batteries only, it&#39;s still not enough to keep
it running indefinitely. I only got three days at best of run time, after that
the battery is depleted. With the long summer days! I will need to look for
better, larger solar panels. Another thing I will need to take a look at is
reducing the power consumption of the Pi, setting up deep sleep or turning of
the wifi when not transmitting. There will be updates at some time in the
future, hopefully with solutions.&lt;/p&gt;
&lt;p&gt;Thanks for reading, if you have any comments, please drop me a line on email or
Mastodon. Links at the top of the page.&lt;/p&gt;
</description>
      <pubDate>Fri, 09 Jun 2023 24:00:00 GMT</pubDate>
      <dc:creator>STFN</dc:creator>
      <guid>https://stfn.pl/blog/09-pico-solar-panels/</guid>
    </item>
    <item>
      <title>My thoughts as I reach 100 million points in BOINC</title>
      <link>https://stfn.pl/blog/10-my-thoughts-on-boinc/</link>
      <description>&lt;p&gt;Today I reached one hundred million points in BOINC. It&#39;s a number as any other,
but as we, humans, enjoy nice round values, so well enough it can be a time to
celebrate and think about BOINC in general.&lt;/p&gt;
&lt;p&gt;As a word of introduction, &lt;a href=&quot;https://boinc.berkeley.edu/&quot;&gt;BOINC&lt;/a&gt;, or Berkeley
Open Infrastructure for Network Computing is a system that allows for scientists
to tap into compute resources of ordinary people. If a researcher requires vast
computational resources to, say, simulate a galaxy, they can create work units
that will be sent to volunteers&#39; computers, calculated by them, and returned to
the project&#39;s database. The whole idea is based on the fact that computers of
today have become powerful as never before, yet most of the time they use
mininal resources, as the user is doing some light tasks like internet browsing.
Why not use those idling CPU cores, those GPUs that can compute sun rays
bouncing of a windshield of a virtual car hundreds of times a second? Modern
research requires a lot of processing power as volume of data grows
exponentially. Sounds like a perfect match. The theory is beautiful. The
outcome? Not so much.&lt;/p&gt;
&lt;h2&gt;My BOINC journey&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/10/boinc.png&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/10/boinc.png&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I discovered BOINC in the midst of the pandemic, in April 2021. In those tragic
times, I, as many other people, realised I had some old hobbies and interests
that I forgot a long time prior. For me, it was astronomy. I decided to come
back to it. I joined internet forums (still my favourite way of belonging to an
internet community), bought books, started reading about the current research. I
even bought my first telescope and started taking pictures of our Cosmos (both
of which turned out awful at the beginning, but that is a story for another
time).&lt;/p&gt;
&lt;p&gt;During that time I did some crunching for folding@home, they were the talk of
the party because of their involvement in research for the Covid vaccine. So I
started looking if there was something like f@h but for astronomy. And it was,
this is how I came across BOINC.&lt;/p&gt;
&lt;p&gt;I started with my old desktop, with a Intel i5-4460 and Nvidia 750Ti. Now I am
crunching on my current, much more powerful desktop, with a 16 thread AMD 3700X
and a RTX2060. Occasionally I also pull out from the basement that old desktop,
but now with a RX470 ex-mining GPU. I&#39;ve been also crunching on a Raspberry Pi.&lt;/p&gt;
&lt;p&gt;As a fact, BOINC has been lately the sole reason I want and buy more powerful
computer hardware, if it wasn&#39;t for it I would be happy with a much less
powerful GPU, as I don&#39;t game much.&lt;/p&gt;
&lt;p&gt;And so, after 2 years and four months of regular crunching, I reached 100
million BOINC points. Those points of course are just virtual boasting numbers,
shared resources to the scientists. But I do feel good that in a tiny tiny way,
I contributed to scientific research, and maybe what I did will make some
breakthrough I bit closer to reality. This is what BOINC is all about, giving
your resources and not expecting anything in return, apart from progress.&lt;/p&gt;
&lt;h2&gt;Projects I contribute to&lt;/h2&gt;
&lt;p&gt;This is a short summary of the projects I currently crunch, to learn more about
them, visit their respective science pages.&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;https://universeathome.pl/&quot;&gt;Universe@Home&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Run by the Nicolaus Copernicus Astronomical Center of the Polish Academy of
Sciences in Toruń, Poland, this BOINC project simulates the lifetimes of stars
and black holes to better understand the mechanisms behind their evolution&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;https://einsteinathome.org/&quot;&gt;Einstein@Home&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Einstein@Home is currently one of the most active BOINC projects, it&#39;s goal is
to research gravitional waves by analysing data from radiotelescopes and
satellites.&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;https://milkyway.cs.rpi.edu/milkyway/&quot;&gt;MilkyWay@Home&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;This project is all about our own galaxy. It&#39;s goal is to study its history by
analyzing the current structures of stars in the Milky Way.&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;https://asteroidsathome.net/&quot;&gt;Asteroids@Home&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;This project, located in the University of Prague, focuses on researching
asteroids, their dimensions and spin based on the available imagery.&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;https://worldcommunitygrid.org/&quot;&gt;World Community Grid&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;In contrast to the other ones, this project is about life sciences, recently
mostly about looking for new cures for cancer and covid. The project owners had
to change infrastructure last year and had months of technical issues, but seem
to be getting more and more stable lately. Perfect for people that feel that
astronomy is too far away from real life problems.&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;https://gaiaathome.eu/gaiaathome/&quot;&gt;Gaia@Home&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;This project has been carried out by my own Adam Mickiewicz University in
Poznań, it focused on researching comets, however in the last few months it has
been active very rarely and looks dead at the moment.&lt;/p&gt;
&lt;h2&gt;Future of BOINC&lt;/h2&gt;
&lt;p&gt;Last year the creator of BOINC &lt;a href=&quot;https://continuum-hypothesis.com/boinc_history.php&quot;&gt;wrote a long
article&lt;/a&gt; talking about his
views on BOINC, and one of the main points is that, according to him, BOINC
&amp;quot;plateaued in 2007&amp;quot; and since then has been in decline. I haven&#39;t been that long
with BOINC, but even in my short period, I can understand why he sees it this
way. The BOINC projects list is full of dead links, and since I joined I have
not seen a new, stable project. The beautiful idea is just not surviving in
reality.&lt;/p&gt;
&lt;p&gt;Reasons for this are many, among them lack pf publicity and media converage,
underfunded academia, and the software itself being complicated for both
scientists and volunteers (don&#39;t get me started on setting up proper GPU drivers
on Linux). Another issue are the rising energy prices. Contributing your
computer to BOINC is not as free as it sounds, running the CPU and GPU with a
heavy load means increased heat, noise, wear of parts, and a much higher energy
bill. One month when I run my crunchers at full tilt 24/7, my energy bill
increased three times. That is not something many are willing are accept just
for internet karma, especially when taking into consideration concerns about
global warming and the source of all that electricity.&lt;/p&gt;
&lt;p&gt;A bit of a silver lining is that with the downfall of crypto mining, the market
is flooded with cheap GPUs that are perfectly valid for actually doing something
good, and getting powerful resources is as easy as ever.&lt;/p&gt;
&lt;p&gt;But to sum up, BOINC right now is the realm of diehards and geeks, and not
everyone watching cat videos on a laptop, as BOINC creators envisioned at the
beginning. Nobody knows about it, joining it is not that easy, and the price to
pay is high. And when you actually join, there is not that much choice what to
crunch.&lt;/p&gt;
&lt;p&gt;And this makes me super sad, because BOINC is this beautiful grassroot
initiative that is very much in line with my love for the non-profit, federated
Internet driven by volunteers.&lt;/p&gt;
&lt;p&gt;I will continue to crunch as long as I can, as long at energy prices allow me to
do so and as long as the projects exist, and I invite you to join me. Drop me an
email, talk to me on Mastodon, I will be happy to help and guide you into
crunching yourself.&lt;/p&gt;
&lt;p&gt;What do you think? Are you BOINCing? Let me know.&lt;/p&gt;
</description>
      <pubDate>Fri, 04 Aug 2023 24:00:00 GMT</pubDate>
      <dc:creator>STFN</dc:creator>
      <guid>https://stfn.pl/blog/10-my-thoughts-on-boinc/</guid>
    </item>
    <item>
      <title>State of my homelab in August 2023</title>
      <link>https://stfn.pl/blog/11-my-homelab-0823/</link>
      <description>&lt;h2&gt;My center of operations&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/11/IMG_5436.jpeg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/11/IMG_5436.jpeg&quot; alt=&quot;Photo of computer equipment on a small shelf&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I thought I might do a series of posts in which I show the status of my homelab,
with updates each time something significant happens. This way I will be able to
log the changes to my setup, how it evolves over time, with updates and
downgrades. When read at some point in the future, it might turn out to be
interestting story? We&#39;ll see. Anyway, every series has a start, and so has
this one, this is the first instance of the State Of My Homelab.&lt;/p&gt;
&lt;p&gt;On the left, a TP-Link router provided by my ISP. The provider delivers an
Ethernet cat cable straight from the wall, so no need for a modem. This is the
first flat I&#39;m seeing this, in my previous one there was a coax first, and a
fiber optic later coming from the common parts of the block.&lt;/p&gt;
&lt;p&gt;The white box is a Netgear GC110 smart switch, which currently I am using in a
dumb mode, with no special configuration applied. I want to learn advanced
networking, VLANs etc, but I never really sat down to it. Yet. Cool thing about
it is that it has SFP ports, so one day I might use with fiber optics to connect
stuff together? Maybe connect my future house with my future shed?&lt;/p&gt;
&lt;p&gt;On top of the switch is my &amp;quot;dev server&amp;quot;, a Raspberry Pi 4b which I use to test
out stuff before pushing it to my &amp;quot;production&amp;quot;. The Pi is running Raspbian,
right now I am using it to test out Mullvad VPN, and a bit of BOINC, hence the
fan. With light load, I never needed a fan if the case was open enough. Speaking
of the case it&#39;s a &lt;a href=&quot;https://theterrapi.com/the-terrapi-alpha/&quot;&gt;TerraPi Alpha SSD Case&lt;/a&gt;, and
I highly recommend it, that case has served me for years, it&#39;s great quality and
the mounting is ingenious. The Pi is using an SSD for storage and SD card for
booting. I wanted to use the SSD to host the system, but the Pi is very picky
when it comes to USB-SATA controllers and SSD brand, and this combination did
not want to cooperate.&lt;/p&gt;
&lt;p&gt;Below the shelf, on the floor there is an IKEA Tradfri gateway, which I used to
connect to my Home Assistant, but for now it&#39;s now doing anything, in this
generation of homelab I&#39;m not running Home Assistant at all, got bored with it.
Maybe next time.&lt;/p&gt;
&lt;p&gt;Finally, on the right is the main production server of my homelab, Lenovo X240
with Intel i5-4300U and 8GB of RAM. I bought it as a cheap dispensable laptop to
take when I wouldn&#39;t want to carry expensive hardware on me, but I could not
stand the screen quality and resolution, so it was promoted to become a server.
I wish I could upgrade the RAM, but it has only one slot, and cannot take more
than 8GB sticks&lt;/p&gt;
&lt;p&gt;The Lenovo mainframe is also sporting an SSD in an external case as storage, and
I removed both of the batteries, as they are not needed, and I don&#39;t want to
risk them bloating.&lt;/p&gt;
&lt;p&gt;The secondary usecase of the server is a small file sharing server via NFS. I
don&#39;t have yet a proper NAS, but the few hundred gigs available on the external
SSD are a convenient way to move stuff between computers in my LAN.&lt;/p&gt;
&lt;h2&gt;What I&#39;m hosting&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/11/homelab_dashboard.png&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/11/homelab_dashboard.png&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;https://dashy.to/&quot;&gt;Dashy&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;A homelab dashboard, this is what the screenshot is showing.&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;https://manual.calibre-ebook.com/server.html&quot;&gt;Calibre&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;An ebook storage and management system&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;https://github.com/alexta69/metube&quot;&gt;Metube&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;For downloading videos from Youtube. Surprisingly fast!&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;https://github.com/akhilrex/podgrab&quot;&gt;Podgrab&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Downloads and stores podcasts. I mostly use it for data preservation (aka
hoarding), I listen to podcasts only my phone with the iOS Podcasts app :)&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;https://airsonic.github.io/&quot;&gt;Airsonic&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Listen to MP3s, awful UI but it works. No, seriously, the UI burns out your
eyes.&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;https://github.com/FreshRSS/FreshRSS&quot;&gt;RSS&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;RSS is the best thing out there for following blogs. No algorithm, no ads, just
a feed. For my RSS client I recently moved from Tiny Tiny RSS to FreshRSS.&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;https://www.qbittorrent.org/&quot;&gt;qbittorent&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Those Linux ISOs won&#39;t download themselves.&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;https://concourse-ci.org/&quot;&gt;Concourse&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Tool for creating CI/CD pipelines, something like GitHub actions but
self-hosted. One of the tools that I am always planning to learn and never get
around to it.&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;https://grafana.com/&quot;&gt;Grafana&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Dashboards, Grafana get&#39;s data from InfluxDB and Prometheus and makes nice
graphs out of it.&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;https://www.influxdata.com/&quot;&gt;InfluxDB&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Time series database, together with Telegraf I am using it to gather statistics
about the system, like Docker container stats. Also sometimes I&#39;m using it to
fetch and aggregate data from MQTT.&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;https://github.com/louislam/uptime-kuma&quot;&gt;Uptime Kuma&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;For monitoring uptime of your sites. I use it to see if this blog is up.&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;https://flightaware.com/adsb/piaware/install&quot;&gt;ADSB&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I&#39;m self-hosting an ADSB receiver to watch airplanes. This is a topic for a
separate blog post in the future.&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;https://pi-hole.net/&quot;&gt;Pi-Hole&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The single best thing to remove ads and trackers when browsing the Internet.&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;https://js.wiki/&quot;&gt;Permawiki&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;My self-hosted instance of Wiki.js where my girlfriend and I store resources on
cultivating our garden.&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;https://github.com/dullage/flatnotes&quot;&gt;FlatNotes&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;For making quick notes and drafts of blog posts.&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;https://prometheus.io/&quot;&gt;Prometheus&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Prometheus together with node-exported to gather metrics on my homelab state.
The metrics are then ingested to Grafana and shown as dashboard graphs.&lt;/p&gt;
&lt;p&gt;To quote a famous American poet of the 21st century (Drake):&lt;/p&gt;
&lt;p&gt;&lt;em&gt;started from a Raspberry Pi Zero W, now we&#39;re here.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Thanks for reading!&lt;/p&gt;
</description>
      <pubDate>Tue, 15 Aug 2023 24:00:00 GMT</pubDate>
      <dc:creator>STFN</dc:creator>
      <guid>https://stfn.pl/blog/11-my-homelab-0823/</guid>
    </item>
    <item>
      <title>A DIY off-grid 12V powerbank</title>
      <link>https://stfn.pl/blog/12-off-grid-powerbank/</link>
      <description>&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/12/01.JPG&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/12/01.JPG&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class=&quot;future&quot;&gt;
&lt;p&gt;There is now a continuation of this post, in which I made an LTE modem to work with this powerbank: &lt;a href=&quot;https://stfn.pl/blog/15-off-grid-lte-modem&quot;&gt;Off-grid LTE modem&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;h2&gt;The problem&lt;/h2&gt;
&lt;p&gt;One of my hobbies is astrophotography, and that is something that is best done
off-grid, far away from light sources such as cities. Astrophotography also
requires a steady source of electricity to power the equipment, cameras,
computerized tripods, a laptop etc. These two aspects combined suggest using a
portable power source. Typical powerbanks won&#39;t do, as they give out 5 volts to
charge a phone or a tablet, and I need 12V of voltage, with currents of several
amps for hours at at time. There are three basic solutions how to get a portable 12V power source:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;buy a cheap 12V powerbank dedicated to astronomers&lt;/li&gt;
&lt;li&gt;buy a very expensive, powerful portable power source like a Jackery&lt;/li&gt;
&lt;li&gt;build your own power source&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;At first I went with option 1, I bought a &lt;a href=&quot;https://www.astroshop.eu/battery-packs/bresser-mobile-power-station-100-watt/p,63756&quot;&gt;Bresser Power
Station&lt;/a&gt;
which was ok at the beginning, but once I move to a cooled astrocamera with a
large power requirement, it just would not provide the needed power level (it
was far below 9000).&lt;/p&gt;
&lt;p&gt;As time passed and I got better with electronics and soldering, I decided I now
know enough to build my own power supply.&lt;/p&gt;
&lt;h2&gt;Preparation&lt;/h2&gt;
&lt;p&gt;I admit with shame that I do not have any photos from the time I was building
the powerbank. I built it some time ago, and it was an iterative process, and I
was not sure it will end up here on the blog. I hope the photos and diagrams I
am showing here are understandable enough to show how its construction.&lt;/p&gt;
&lt;h3&gt;Battery&lt;/h3&gt;
&lt;p&gt;The first thing to decide was the battery. I needed a steady supply of 12V with
enough capacity to work through a long night. I also wanted the battery to be
safe and keep the factory capacity for a long period of time. On the other hand,
I did not require it to be lightweight, as I would only carry it to and from my
car. Budget was also an important factor.&lt;/p&gt;
&lt;p&gt;I dismissed Li-ion batteries right away because of their flammability and cost.
And they would require a BMS (Battery Management System) into which I do not want
to delve yet. LiFePo4 would be awesome, but again the cost is for the time
being prohibitive for me. So I settled for the good ol&#39;, tested and tried AGM
lead acid batteries. They weight a lot but that&#39;s not really an issue for me,
they can survive a lot of cycles without losing too much capacity, and they have
a reasonably low self-discharge. The price per watt hour is not great, but also
not terrible.&lt;/p&gt;
&lt;p&gt;I would love to have an RTG like the Vikings, but hey, we don&#39;t live in the
future yet.&lt;/p&gt;
&lt;p&gt;Going back to Earth, I searched for a good model of a battery, and I&#39;ve settled
with a YUASA REC22-12 deep-cycle battery. It has a capacity of 22Ah, which suits
me fine, and is designed to survive a lot of cycles from full to almost
depletion. I could have gone for a GreenCell with half the price, but I read
mixed opinions about their longevity.&lt;/p&gt;
&lt;h3&gt;Case&lt;/h3&gt;
&lt;p&gt;For the powerbank case I went with the simplest and sturdiest option, I bought a
plastic tool box with internal dimensions enough to store the battery and the
cables. Plastic is also easy to cut, and I had to do quite a few holes for the
connectors. Speaking of connectors...&lt;/p&gt;
&lt;h3&gt;Connectors&lt;/h3&gt;
&lt;p&gt;For those, I went with XT60 sockets. XT60 is a safe overkill for the current I
will be using, and it&#39;s cheap and easy to solder. They are also, at least in my
opinion, sturdier than the 2.1 x 5.5mm barrel jacks.&lt;/p&gt;
&lt;h3&gt;Additional parts&lt;/h3&gt;
&lt;p&gt;The remaining parts are the cables, fuse, voltage meter, internal connectors for
the cables, and finally a USB charger for phones. As for the USB charger, it took me
quite some time to find, but eventually the &amp;quot;motorcycle or car usb chargers&amp;quot; is
the category I found the one that I needed.&lt;/p&gt;
&lt;h3&gt;Overdischarge failsafe protector&lt;/h3&gt;
&lt;p&gt;One additional part that I added to my build is the overdischarge failsafe. It
turns off everything taking power from it when it senses that the battery
voltage fell below a set threshold. Batteries, especially lead acid ones should
not be discharged too much, as then they can lose a lot of their capacity. 12V
lead acids should not be discharged below 10.5-11V. You can go without this
failsafe by just being careful and regularly monitoring the battery voltage
while using it, I wanted to be double sure and have an automatic cutoff in case
I forget to turn the equipment off or something breaks.&lt;/p&gt;
&lt;h2&gt;Parts list&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;YUASA REC22-12 battery -- 370PLN -- 83EUR&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Qbrick System Pro 500 Basic tool box -- 65PLN -- 15EUR&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;16AWG silicone cable (3 meters should be enough with a safe margin) -- 7PLN -- 2EUR&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;XT60 plug and socket (x2) -- 10PLN -- 3EUR&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;5 pin electric connectors (x2) -- 6PLN -- 2EUR&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;10A fuse with mount and cable (pack of ten) -- 14PLN -- 5EUR&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;3A USB 12V charger -- 40PLN -- 10EUR&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;15V analogue voltmeter -- 10 PLN -- 3EUR&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;assorted car cable connectors -- depends on the pack, but around 15PLN -- 5EUR&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;overdischarge protection -- 20 PLN - 6 EUR&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;total: 557PLN -- 134EUR&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I am not providing any links as such small electronic components (with the
exception of the battery) usually are not of a specific brand, so the best way
to buy them is just to search for their name on any electronic components store
and buy the that looks right.&lt;/p&gt;
&lt;p&gt;For this project it is required that you have a soldering iron and know how to
solder, as the connectors will be soldered.&lt;/p&gt;
&lt;p&gt;Additionally you need a 12V lead acid battery charger, any car battery
charger will do just fine.&lt;/p&gt;
&lt;p&gt;For the tools, a drill and a hole saw are required. A glue gun and, a stripping
tool and a connector crimper will be useful.&lt;/p&gt;
&lt;h2&gt;Connecting stuff&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/12/diagram.png&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/12/diagram.png&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The overall design of the electrical system is simple: a power source and four
receivers: a voltage meter, one USB charger and two XT60 sockets. From the
battery power goes to a cable box that splits the power to all receivers. The
receivers are connected in parallel. This way they all receive the same voltage,
12V, and the current taken from the battery is the sum of currents used by the
individual receivers.&lt;/p&gt;
&lt;p&gt;I have chosen 16AWG silicone cables as they should handle the currents I am
forecasting to be used in the setup, and are resistant to heat and cold. One
thing I could have done better was to use different colours for positive and
negative, but I handled it with some red markers.&lt;/p&gt;
&lt;h3&gt;Battery and power switch&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/12/02.JPG&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/12/02.JPG&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/12/04.JPG&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/12/04.JPG&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I started with fitting the battery. When looking for both battery and the case,
I matched the external size of the battery with the internal volume of the case,
so the battery fit snugly inside. I put some cardboard to fix it in place. If
you plan to carry the powerbank a lot over rough terrain, consider fixing the
battery in a more robust way, maybe  with some angle brackets.&lt;/p&gt;
&lt;p&gt;With the battery done, I drilled a hole for the switch. I used a small diameter
hole saw, placed the switch and fixed it in place with hot glue.&lt;/p&gt;
&lt;p&gt;The next part was the connection between the battery and the switch. It does not
really matter from which terminal of the battery you start, I went with the
positive side as that was closer to the side of the case I decided to be the
control panel. For the connection. I used one of the fuses. The fuse casings I
bought already had cables attached to both sides, so I just crimped the right
connectors to both ends. For the battery end I used an M5 open connector, but
the switch end I used the connector that came with the switch itself.&lt;/p&gt;
&lt;p&gt;The switch has three connectors, not exactly sure why, so by testing I found out
which two make a connection when the switch is ON, and I connected the lead from
battery to one of those. One connection done.&lt;/p&gt;
&lt;h3&gt;Optional overdischarge protector&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/12/03a.JPG&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/12/03a.JPG&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/12/03.JPG&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/12/03.JPG&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I went with adding an overdischarge protector but you can skip it. In my setup,
I first two the PCB board and screwed it to a piece of plastic. Between the
board and the plastic I placed a thermal pad I had lying around from some M.2
hard drive. I added it to have a soft base for the PCB and for some thermal
conductivity. Then I glued the plastic piece to the case with hot glue. I
connected the negative lead from the battery and positive lead from the switch
to the IN port of the protector, and placed leads from the OUT port to the cable
boxes. Finally, I configured the protector to turn off the battery at 11.5V. The
battery manual says it can be discharged down to 10.5V, but I went with a higher
value to increase its longevity.&lt;/p&gt;
&lt;h3&gt;Cable boxes and voltage meter&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/12/05.JPG&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/12/05.JPG&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Next part was to prepare the two cable boxes which will act as manifolds to
distribute the energy to the devices. I went with five element connectors to
have four devices available (the fifth cable goes to the battery). I glued them
to the bottom of the case. To one of them went the cable from the switch, to the
second one I connected the negative terminal of the battery.&lt;/p&gt;
&lt;p&gt;From now on, make sure that the power switch is in the OFF position, or
disconnect the battery from the cable boxes!&lt;/p&gt;
&lt;p&gt;Now came the time for the first device that will receive the electricity coming
from the battery, and that is the voltage meter. The one I have has a round
casing and for screws to secure it in place. I drilled the whole for the casing
with a large hole saw, and eyed four holes for the bolts. It got in nicely, and
I put nuts on the bolt to make sure it does not fall out. I placed one cable
from the positive cable box and one from the negative cable box and connected to
the meter by round cable connectors. Easy stuff.&lt;/p&gt;
&lt;p&gt;Now, when you turn on the switch, the voltage meter should show the current
voltage. If the meter needle goes the other way to negative it means you
connected the cables the wrong way round. Ask me how I know this.&lt;/p&gt;
&lt;h3&gt;XT60 sockets&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/12/06.JPG&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/12/06.JPG&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Now the hardest part. I had to make square holes in the box to fit two XT60
connectors. I tried using a hairsaw, I think that&#39;s the correct name? but it did
not work out. I eventually drilled some holes with a wood drill, and connected
them with a knife. Once I had large enough holes, I soldered the cables to the
XT60 sockets and fastened them to the plastic case with small screws and more
hot glue. The XT60 sockets have small + and - signs on them, make sure you
connect them the right way to the cable boxes.&lt;/p&gt;
&lt;h3&gt;USB charger&lt;/h3&gt;
&lt;p&gt;The last step was very simple, I had to drill a round hole like for the power
switch, I placed the USB charger in the hole, secured it with the provided
plastic nut, and finally I took two lengths of cable, crimped car connectors at
the ends, and connected the USB charge to the cables boxes.&lt;/p&gt;
&lt;h2&gt;Testing&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/12/07.JPG&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/12/07.JPG&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Before plugging anything of value, I tested the powerbank with a multimeter. I
turned the switch on, and checked the sockets whether they had the proper
polarity and voltage. I then tested the USB charger with a cheap USB light.
Everything was working fine, so I plugged in my phone, and it started charging!
Finally I plugged my whole astrophotography setup to one of the XT60 sockets and
it booted and operating like normal. What a relief.&lt;/p&gt;
&lt;h2&gt;12V light source&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/12/08.JPG&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/12/08.JPG&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/12/09.JPG&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/12/09.JPG&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;One of the use cases I have for this powerbank is to power the lights for my
garden shed. I did not want to use LED strips or anything similar, as the
lighting was meant to be warm and house-like to make the whole place cosy. After
a lot of searching I finally found a lightbulb with a warm tone and a standard
E14 thread that runs on 12V voltage. For the mount, I bought a simple IKEA lamp,
cut off the 220V plug and soldered an XT60 connector. Works like a charm and
gives really nice light.&lt;/p&gt;
&lt;h2&gt;Astrophotography setup&lt;/h2&gt;
&lt;p&gt;I don&#39;t want to talk too much about my astrophotography setup here as there will
be a separate post about it, but to give a tl;dr version, I am using an
&lt;a href=&quot;https://shop.astrojolo.com/product/astrolink-4-pi/&quot;&gt;Astrojolo Astrolink 4 Pi&lt;/a&gt;
that takes 12V from the powerbank and uses it to power a Raspberry Pi together
with all the other astro components that need the electrons to operate.&lt;/p&gt;
&lt;h2&gt;Real life usage&lt;/h2&gt;
&lt;p&gt;Because of bad weather so far I had one chance to test my project in full, and
it worked perfectly, provided stable power for three hours and still had charge
for a few more. And I could charge my phone at the same time.&lt;/p&gt;
&lt;h2&gt;Charging&lt;/h2&gt;
&lt;p&gt;To charge the powerbank, I am using a standard car battery charger. For now I am
putting the charger clips right on the battery terminals, but one of my
improvement plans is adding a separate connector only for connecting the
charger. It will be a nicer solution and won&#39;t require opening the case to
charge the powerbank.&lt;/p&gt;
&lt;h2&gt;Next steps&lt;/h2&gt;
&lt;p&gt;In the future I am considering adding another battery to connect them in
parallel, doubling the overall powerbank capacity. Maybe I will also add a 220V
converter, but for now I don&#39;t really have a use case for it. An USB-C power
delivery charger would be awesome, but I have yet to find one that does not look
super cheap and suspicious. And I don&#39;t want to plug my laptop into something
looking like a 5$ e-waste.&lt;/p&gt;
&lt;p&gt;So, this is it, I hope you like it. If you have any questions, comments,
suggestions, or want to show your power solutions, hit me up on Mastodon or send
me an email. I would love to get any feedback from you, even a short email
saying &amp;quot;I read it until the end&amp;quot;, or &amp;quot;this post sucks!!!!11one&amp;quot; will be
appreciated. Links at the bottom of the page.&lt;/p&gt;
</description>
      <pubDate>Sun, 27 Aug 2023 24:00:00 GMT</pubDate>
      <dc:creator>STFN</dc:creator>
      <guid>https://stfn.pl/blog/12-off-grid-powerbank/</guid>
    </item>
    <item>
      <title>NGC 7000 - North America Nebula</title>
      <link>https://stfn.pl/blog/13-north-america-nebula/</link>
      <description>&lt;p&gt;Finally a photo of the sky!&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/13/combined_cropped_saturation.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/13/combined_cropped_saturation.jpg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The largest red blob in the middle of this photo is the North America Nebula
(NGC 7000 or Caldwell 20), an emission nebula in the constellation of Cygnus.
Above it is the Pelican nebula (IC 5070), both of them swimming in a sea of
minor red clouds. Both nebulae are of the emission type, meaning they are giant
clouds of atomic hydrogen and oxygen, which are being excited by nearby young,
hot stars (just like teenagers close to other teenagers), and emit light (not
like teenagers).&lt;/p&gt;
&lt;p&gt;I imaged those teenagers, I mean nebulae, over three nights in the beginning of
September 2023.&lt;/p&gt;
&lt;h2&gt;Image Processing&lt;/h2&gt;
&lt;p&gt;Total acquisition time is 5 hours 50 minutes, in which 2 hours were for the RGB
version, and 3:50 for the narrowband. All captured in 5 minute exposure at -15C
sensor temperature, gain 10, offset 50.&lt;/p&gt;
&lt;p&gt;Most processing was done in Pixinsight, with final touches in GIMP.&lt;/p&gt;
&lt;p&gt;I photographed the nebula using two different filters, the first one was UV/IR
filter, and the second one was a narrowband Ha + OII duoband 5nm filter from
SVO. After stacking them separately in WBPP I got two masters, one wideband RGB
and the second one in narrowband.&lt;/p&gt;
&lt;p&gt;The initial step was to register them with one another so that the stars align.&lt;/p&gt;
&lt;p&gt;Then I moved to processing the narrowband image:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Dynamic Background Extraction&lt;/li&gt;
&lt;li&gt;SCNR to remove surplus green.&lt;/li&gt;
&lt;li&gt;Soft stretch (I am using EZ Scripts suite)&lt;/li&gt;
&lt;li&gt;Starnet2 to remove the stars&lt;/li&gt;
&lt;li&gt;Unsharp Mask&lt;/li&gt;
&lt;li&gt;Final stretch&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;With that done, I switched to the wideband RGB image:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Dynamic Background Extraction&lt;/li&gt;
&lt;li&gt;Photometric Colour Calibration&lt;/li&gt;
&lt;li&gt;Soft stretch&lt;/li&gt;
&lt;li&gt;Tiny bit of Curves transformation to slightly reduce the stars bloat&lt;/li&gt;
&lt;li&gt;Starnet2 with star mask to extract the stars&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Now with narrowband image done and the star mask ready, I moved to PixelMath to
combine them using a formula I came across several times on different internet
forums, which for some magical reason works much better than simple addition:
&lt;code&gt;~((~narrowband)*(~star_mask))&lt;/code&gt;. This combining is a quick step, and I run it
several times with different amounts of Curves transformation to see what suited
me best.&lt;/p&gt;
&lt;p&gt;I saved the combined image as .tif and opened it in GIMP for some final touches,
a bit of cropping, unsharp mask, saturation and curves tweaking.&lt;/p&gt;
&lt;h2&gt;Gear Used&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;iOptron GEM28 equatorial mount&lt;/li&gt;
&lt;li&gt;QHY 168C cooled astrocamera&lt;/li&gt;
&lt;li&gt;an old and moldy M42 lens 200mm, f/3.5&lt;/li&gt;
&lt;li&gt;ZWO ASI 120MC and ZWO Mini guider for guiding&lt;/li&gt;
&lt;li&gt;Astrojolo &lt;a href=&quot;https://shop.astrojolo.com/astrolink-4-pi/&quot;&gt;Astrolink 4Pi&lt;/a&gt; running
&lt;a href=&quot;https://www.astroberry.io/&quot;&gt;Astroberry OS&lt;/a&gt; for session control and image
acquisition.&lt;/li&gt;
&lt;li&gt;my &lt;a href=&quot;https://stfn.pl/blog/12-off-grid-powerbank/&quot;&gt;DIY powerbank&lt;/a&gt; to power everything.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Final Thoughts&lt;/h2&gt;
&lt;p&gt;This is so far my best astro picture with the longest acquisition time. Yet,
after spending days looking at much better astrophotography on
&lt;a href=&quot;https://astrobin.com/&quot;&gt;Astrobin&lt;/a&gt; and &lt;a href=&quot;https://astropolis.pl/&quot;&gt;Astropolis&lt;/a&gt;, it
does not feel that good to me. But well, I feel I made progress since the last
time, and this is what counts.&lt;/p&gt;
&lt;p&gt;I need a better lens, this one has an awful CA which makes those atrocious halos
around stars, but that is something that needs to wait until I get some budget.&lt;/p&gt;
&lt;p&gt;That&#39;s it, hope you liked it. And as a bonus, the narrowband starless version:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/13/starless.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/13/starless.jpg&quot;&gt;&lt;/a&gt;&lt;/p&gt;
</description>
      <pubDate>Sat, 09 Sep 2023 24:00:00 GMT</pubDate>
      <dc:creator>STFN</dc:creator>
      <guid>https://stfn.pl/blog/13-north-america-nebula/</guid>
    </item>
    <item>
      <title>Astro Archive - The Horsehead Nebula</title>
      <link>https://stfn.pl/blog/14-horsehead-nebula/</link>
      <description>&lt;p&gt;Recently I had an idea to go back to my older astrophotography and try editing
those photos again with new techniques that I learned along the way to see what
happens and whether I can pull out some new stuff out of old data.&lt;/p&gt;
&lt;p&gt;I asked on my &lt;a href=&quot;https://fedi.stfn.pl/@stfn&quot;&gt;Fediverse account&lt;/a&gt; if people were
interested in seeing such content, and the majority said yes, so here we go.
That&#39;s how the idea of Astro Archive was born.&lt;/p&gt;
&lt;p&gt;For the first image to retry I chose my old attempt to photograph one of the
staples of the northern winter sky, the Horsehead Nebula in the belt of the
Orion constellation.&lt;/p&gt;
&lt;p&gt;I imaged Orion for three nights in March of 2022. Total acquisition time was 3
hours 36 minutes, shot only in narrowband using the Optolong L-enhance filter
and an astromodded Canon 1200D DSLR.&lt;/p&gt;
&lt;p&gt;Most processing was done in Pixinsight, with final touches in GIMP.&lt;/p&gt;
&lt;p&gt;The telescope I used then was a Vixen ED114SS, which I sold this year because I
was not happy with its large dimensions and low image quality.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/14/combined.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/14/combined.jpg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The main point of this photo is the Horsehead Nebula, it&#39;s striking shape of a,
well, horse head, visible upside down in the centre. This black cloud of cosmic
dust contrast with the glowing reds of the background emission nebula. There are
not many stars visible above the horse, as the light is blocked by the larger
nebula from which the horse stems. Only two stars shine strong enough to make
their own small nebulae, IC435 and NGC2023.&lt;/p&gt;
&lt;p&gt;On the right there is the magnificent Flame Nebula, NGC2024, a mix of glowing
hydrogen and darker dust and smoke burning in the sky. Surrounding the Flame
there are a few smaller nebulae, lighted by their stars.&lt;/p&gt;
&lt;p&gt;Both the Flame and the nebula surrounding the horse are glowing because of the
immense power of Alnitak, the leftmost star in the Orion Belt. Here visible as a
shining ball of light just below the Flame.  Alnitak is actually a trio of
stars, the brightest of them being a blue supergiant, with a mass of 33 solar
masses and 21 thousand brighter than our star.&lt;/p&gt;
&lt;p&gt;In the lower part of the image, one can see the intricate lines of the hydrogen
dancing in the solar winds, looking like a draped curtain. Sometimes the solar
winds collide, and cause hotspots that shock warm the hydrogen ever more, making
it glow brighter.&lt;/p&gt;
&lt;p&gt;At the bottom of the image there are two interesting structures that seem not be
named. It looks like hydrogen is lit by a nearby star and formed in its solar
wind, once angled away from the star, once surrounding it. I do now know if this
is the correct explanation, or are those two just visual companions, being in
the same line of sight, but physically totally distant.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/14/shockwave1.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/14/shockwave1.jpg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/14/shockwave2.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/14/shockwave2.jpg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Finally, lurking somewhere in the background are galaxies other than our own,
too faint to see, only their location marked on the annotated image below.&lt;/p&gt;
&lt;h2&gt;Image Processing&lt;/h2&gt;
&lt;p&gt;The image processing I chose was straightforward:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Dynamic Background Extraction&lt;/li&gt;
&lt;li&gt;Photometric Colour Calibration&lt;/li&gt;
&lt;li&gt;SCNR to remove surplus green.&lt;/li&gt;
&lt;li&gt;Soft stretch (I am using EZ Scripts suite)&lt;/li&gt;
&lt;li&gt;Starnet2 to divide the image into a star mask and a starless image.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;On the starless image:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Unsharp Mask&lt;/li&gt;
&lt;li&gt;Final stretch&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;On the star mask&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Tiny bit of Curves transformation to slightly reduce the stars bloat&lt;/li&gt;
&lt;li&gt;More SCNR&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Finally I moved to PixelMath to
combine the star mark with the starless image using a formula I mentioned in the previous post:
&lt;code&gt;~((~narrowband)*(~star_mask))&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;I saved the combined image as .tif and opened it in GIMP for some final touches.&lt;/p&gt;
&lt;h2&gt;Final thoughts&lt;/h2&gt;
&lt;p&gt;This was at the time my best astrophoto. Looking at it now, I mostly see the
downsides. There is not enough acquisition time to work with, less than 4 hours
on an old DSLR will not give you wonders. And that Vixen telescope would only
give round stars in the middle of the image, the corners being just a mesh of
triangular points.&lt;/p&gt;
&lt;p&gt;Nonetheless, it was fun to go back to my earlier works, and have a chance to
share them with you.&lt;/p&gt;
&lt;p&gt;Below you will find the annotated and the starless versions of the image.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/14/combined_Annotated.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/14/combined_Annotated.jpg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/14/starless.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/14/starless.jpg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
</description>
      <pubDate>Wed, 04 Oct 2023 24:00:00 GMT</pubDate>
      <dc:creator>STFN</dc:creator>
      <guid>https://stfn.pl/blog/14-horsehead-nebula/</guid>
    </item>
    <item>
      <title>Powering an LTE modem from a 12V battery</title>
      <link>https://stfn.pl/blog/15-off-grid-lte-modem/</link>
      <description>&lt;h2&gt;Background&lt;/h2&gt;
&lt;p&gt;This project came from a simple need: I have a piece of land without grid
electricity, and I want to have Wi-Fi there. I could just use my phone as a
hotspot for a laptop, but I wanted something better, and not using the phone&#39;s
battery. And building something for living off-grid is always fun.&lt;/p&gt;
&lt;p&gt;In one of the previous blog posts I wrote &lt;a href=&quot;https://stfn.pl/blog/12-off-grid-powerbank/&quot;&gt;how I built a 12V
powerbank&lt;/a&gt;, so the energy source is covered. I also have an LTE modem, a
TP-Link TL-MR100 which I bought some time ago to be used on holidays in remote
places. Those two sounded like a perfect match, the only issue was how to
connect them.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/15/IMG_20231007_185434.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/15/IMG_20231007_185434.jpg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Looking at the sticker on the modem, it needs voltage of 9 volts, and uses 0.85A
(I assume that second value is at maximum gain and throughput). My powerbank
provides from ~13.5V fully charged to 11.5V at shutdown threshold. That sounds
like a job for a step-down converter!&lt;/p&gt;
&lt;p&gt;If you want to build something similar for yourself, check the sticker under
your LTE modem for power specs, if your modem uses 12 volts, you might not even
need to a converter, just connect it straight to the battery. Or if you want to
be on the safe side, get a step-down/step-up converter that will output 12V no
matter the voltage of the battery. In every other case you&#39;ll need a converter,
maybe even a step-up one if your modem runs on, for example, 15 volts.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;I am not responsible for any damage you cause to your equipment or yourself.&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;Parts List&lt;/h2&gt;
&lt;h3&gt;Step-down converter&lt;/h3&gt;
&lt;p&gt;A step-down converter is a device that takes an input voltage and outputs an
output voltage. I know, shocking. In my case I needed something that will reduce
the voltage of the battery, fluctuating between ~13 to ~11 volts, and provide a
stable 9 volts to the modem. After some searching and reading, I found that a
perfect solution for me would be something based on the LM2596 chip. This chip
can receive voltages from 40 to 4.5 volts, and output from 37 volts to 3.3 volts
and handle a maximum current of 3 amps, so my use case would be well within its
capabilities.&lt;/p&gt;
&lt;p&gt;There is a lot of different converters using this chip, just go to your
favourite online marketplace and search for the chip name, you&#39;ll get pages and
pages of devices. They come in fixed and variable output voltages. All of them
are dirt cheap, for 2-4 euros. Just select one that you like. I chose a fancier
product, with a display showing the input and output voltages.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/15/IMG_20231007_183524.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/15/IMG_20231007_183524.jpg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;And all the other stuff&lt;/h3&gt;
&lt;p&gt;Apart from the converter, you&#39;ll need:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;cables to connect stuff&lt;/li&gt;
&lt;li&gt;plugs to end the cables&lt;/li&gt;
&lt;li&gt;cable shrink wrap&lt;/li&gt;
&lt;li&gt;a nice case for the chipset&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And typical electrical DIY tools: multimeter, side cutters, a cable stripper,
soldering iron, hot glue gun, and screwdrivers.&lt;/p&gt;
&lt;h2&gt;Build Process&lt;/h2&gt;
&lt;p&gt;For the wires I went with speaker cables, as they are perfect for DIY direct
current projects: cheap, quite weather resistant, come in pairs of wires, and
are marked for positive and negative. And I already have 30m of them lying
around for a project that never came to fruition. Mine are, I think, 2 x 1.5mm
and they were a bit too fat, I had to trim the ends to make them fit into the
converter connector holes.&lt;/p&gt;
&lt;p&gt;When soldering the cables, pay special attention to polarity and at every step
make sure you don&#39;t mix it. Speaker cables have one of the wires marked in red,
the convention is that it is the positive one. XT60 and barrel plug also have +
and - signs on them, if you always go plus to plus, you should be fine. Just
test often.&lt;/p&gt;
&lt;p&gt;I cut two lengths of the speaker cables, one for the powerbank-converter part
and second for the powerbank-modem part. I carefully split the positive and
negative wires and stripped the ends of their isolation. At this time I added
short lengths of heat shrink to give the cable an additional layer of protection
at the ends. For the first part I soldered an XT60 connector to one end. I chose
that connector type because my powerbank already has these. The second end of
that cable I soldered to the input side of the step-down converter. Part one
done.&lt;/p&gt;
&lt;p&gt;For the second length I did the same, splitting, stripping, and soldering. One
end went to the output side of the step-down converter, and at the second end I
screwed a 2.1x5.5mm barrel plug. Check what plug your modem has, it is very
possible that it will too have that one, it&#39;s a very popular standard.&lt;/p&gt;
&lt;p&gt;The final step was to add a case to the converter. Again I used something I
already had lying around the workshop (aka my bedroom), a weatherproof electric
box. The converter fit nicely inside. In accordance to ancient Chinese
traditions, I hot glued the it to the inside of the box, and added additional
hot glue on the soldered connectors to protect them from moisture. And some more hot
glue to close the cable gaps and fix everything securely. Now just a sign
explaining what&#39;s inside the box (what&#39;s in the box?!) and the project is
completed.&lt;/p&gt;
&lt;p&gt;With that the project is basically done, now it&#39;s time to configure and test it.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/15/IMG_20231007_185036.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/15/IMG_20231007_185036.jpg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Testing&lt;/h2&gt;
&lt;h3&gt;Setup and start&lt;/h3&gt;
&lt;p&gt;My LM2596 is a variable voltage one, and has a display to show input and output
voltages. If you have a constant output voltage one, skip the setup part. If you
have a variable output one, but without a display, you&#39;ll need a multimeter to
check the output voltage.&lt;/p&gt;
&lt;p&gt;I plugged the input cable to my powerbank and powered it on. The display started
showing the output voltage. Using a small screwdriver, I carefully turned the
adjust screw until the display showed 9 volts. This is all that is needed to
setup the converted, but I wanted to do some testing, so I connected a PC case
fan to the output side. As I was changing the voltage, the fan was visibly
spinning slower and faster, so that confirmed that the converter works. Finally I checked the
output plug with a multimeter to be triple sure that I am getting exactly 9
volts on it. I also made sure the polarity is correct, for barrel jacks the
inside should be positive, and the outer shell should be negative.&lt;/p&gt;
&lt;p&gt;Finally it was the time for the great finale, I connected the LTE modem to the
converter, and the converted to the powerbank. I turned on the powerbank... and
it worked! The modem started as usual and quickly caught the mobile network. In
moments, I was able to connect to the Wi-Fi and browse the intertubes on battery
power.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/15/IMG_20231007_185237.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/15/IMG_20231007_185237.jpg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/15/IMG_20231007_185250.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/15/IMG_20231007_185250.jpg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Now I can browse Mastodon while sitting in a field, the future is incredible.
Thank you for reading, I hope you enjoyed it!&lt;/p&gt;
&lt;p&gt;I would love to get some feedback from you, dear reader, you can drop me an
email, or contact me on Mastodon.&lt;/p&gt;
</description>
      <pubDate>Sun, 08 Oct 2023 24:00:00 GMT</pubDate>
      <dc:creator>STFN</dc:creator>
      <guid>https://stfn.pl/blog/15-off-grid-lte-modem/</guid>
    </item>
    <item>
      <title>On degoogling my life</title>
      <link>https://stfn.pl/blog/16-on-degoogling/</link>
      <description>&lt;p&gt;In July of 2023 I left Twitter and switched to Mastodon. There were many reasons
to do so, the main being Twitter decaying into this hellhole of modern internet,
with falling quality of discussion and service, the crypto bros and growth
ninjas. Twitter was never perfect, but after being sold to Elon, everything
started falling apart. On the technical side there were the posting limits, the
broken notifications, the neverending advertisement. On the ethical side, I&#39;ve
hears stories of people reporting obvious hate speech and getting replies that
it&#39;s not breaking the rules of the site. And Elon himself posting antisemitic,
conspiracy, just disgusting things, with his blue ticked followers clapping at
this every word. And the algorithm, I cannot stand algorithm social media. The
all encompassing algorithm is just a way to drive what one should see and what
should be hidden, and it is not transparent what are the criteria, which is a
wide open way for making people think and behave as the platform owners want
them to.&lt;/p&gt;
&lt;p&gt;So I went to Mastodon, I already had an account there for a few months but
hadn&#39;t really tried it. Yet in that July everything fell in place and I felt
right at home there. I&#39;ve always been a fan of a different internet, a grassroot
one, led by amateurs who give their hearts into it. I sad it many times, I miss
the internet of the early 00s, with the discussion boards, the blogs, the
personal sites, content made with passion. And Mastodon feels like going back to
that time. Its federation is its strength, no one has enough power to control
the Fediverse as a whole. And everyone can find an instance for themselves. I
found people with common interests there, refugees from corporate internet,
writing blogs about bike trips, tinkering, soldering and compost, and since the
change I&#39;ve been feeling much better mentally.&lt;/p&gt;
&lt;p&gt;Now, switching from Twitter to Mastodon gave me an impulse to finally move
forward with something I&#39;ve been planning to do for quite some time now: move my
internet presence in general from corporate, ads-pushing and personal
information guzzling intertubes to the other side, the side that is made by
people who want to make it good, away from the flood of enshittifaction that is
permeating the mainstream internet.&lt;/p&gt;
&lt;p&gt;Here in this blog post I would like to list the services that are a good
alternative to the mainstream behemoths. Every service listed here I am using
personally, and can recommend it without a doubt. And nobody paid me to do so,
actually I am supporting them with money :)&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Google&lt;/strong&gt; - For replacing my search engine I tried several alternatives, I&#39;ve
been using Duck Duck Go for a few months, but I wasn&#39;t too happy with the
results it was giving me, and often I had to do another search in Google to find
what I wanted. Recently I came across
&lt;a href=&quot;https://www.qwant.com/&quot;&gt;&lt;strong&gt;Qwant&lt;/strong&gt;&lt;/a&gt; and I believe I finally found a
search engine for me. It does not collect any data about you, and the search
results are (at least for me ofc) much better than Google. Qwant does not
provide AI generated gibberish at the top of the search results page, it&#39;s clean
and it&#39;s fast. I highly recommend it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Gmail&lt;/strong&gt; - Continuing on theme of the Google suite, for email I am switching
from Gmail to &lt;strong&gt;ProtonMail&lt;/strong&gt;. They are an email service that is privacy and
safety focused, and does not gorge on your data as the mainstream email
providers. They have a generous free plan, and paid options for people who need
more out of their inbox. I&#39;ve seen some people also recommending Tutanota, but I
have never tried them. You can see I am using Protonmail, as the contact email
for this blog is from them. Drop me an email to say hi :)&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Google Maps&lt;/strong&gt; - &lt;strong&gt;OpenStreetMap&lt;/strong&gt;. In short one might say that OSM is a
crowd-sourced version of Google Maps, but that would be only one angle. OSM is a
massive community people whose intent is to provide the largest possible set of
geospatial data. What does that mean? Firstly, it means that everyone can, and
is encouraged to update OpenStreetMap. Secondly, it is possible to input much
more different types of information to places on the map, in order to benefit
different kinds of people. OSM puts great focus on people on bikes, people on
foot, as well as people with different handicaps. For example every street
crossing in OSM can be marked as having or not tactile information for people
with sight issues. The height of the curb can be defined to allow people on
wheelchairs to better plan their routes. In short, OSM is much, much wholesome
than Google Maps and I encourage everyone to use it and to contribute to it. To
contribute, you can register and start editing on the main OSM site,
&lt;a href=&quot;https://openstreetmap.org/&quot;&gt;openstreetmap.org&lt;/a&gt;,
or even better, install one of the mobile apps available, that will allow you to
edit map data during your walks, when you are actually at the place you want to
edit. I personally use &lt;strong&gt;StreetComplete&lt;/strong&gt;, and more on it in the Android
section.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Youtube&lt;/strong&gt; - I&#39;ll admit I am a heavy Youtube user, and there are many Youtubers
I watch regularly. But recently I started discovering &lt;strong&gt;PeerTube&lt;/strong&gt; and I like
the atmosphere there. Peertube is a Mastodon for video, it&#39;s another federalized
ecosystem of independent instances that anyone can create. Sadly, people whom I
watch on Youtube do not post there, but I discovered new creators there. A
notable example is Andy Balaam, who made &lt;a href=&quot;https://diode.zone/w/wJJJ7DRh3fCvHq6KuZY3t9?start=0s&quot;&gt;a great Rust
tutorial&lt;/a&gt;. In general,
Peertube feels like the early days of Youtube, with people posting what they
find worth sharing with the world, their hobbies and interests, and as I said
many times, this is the facet of the Internet that I prefer and find the most
catching. If I ever create a video, I will post it on Peertube.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Instagram&lt;/strong&gt; - Oh Instagram. Once all the
rage and envy of everyone whose phone would not support the app. Who didn&#39;t
publish at some time a heavily filtered, retro-vintage-out-of-focus square
photo? I sure did. Yet in recent times, especially after it being acquired by
Facebook, the enshittifaction of Instagram has been in full swing. I&#39;ve been
opening it rarely the recent months, but everytime I did I was shocked by the
ads-to-posts ratio. And it belongs to Meta, so most probably it tries to gather
every possible information on its user and use it to profile and push towards us
more crap. It just doesn&#39;t feel like a place to publish content anymore. I&#39;m
still there to watch the stories of a few of my closest friends, but nothing
more. As for the better alternative, I am moving to PixelFed, which also belongs
to the Fediverse. It has zero ads and zero personal data mining. Its instances
run on user donations, so there is no incentive to make it worse for the people.
You can find me there at &lt;a href=&quot;https://pixelfed.social/stfn&quot;&gt;pixelfed.social/stfn&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;AWS&lt;/strong&gt; - I&#39;ve been using AWS for many
applications, most of all as cold backup of all my important data. Their Glacier
tier of storage made it very cheap to store almost a terabyte of files. But this
is coming to an end for me. I just don&#39;t want to give Bezos any of my money and
data, I&#39;ve read too much about what Amazon is doing their treatment of their
employees and contractors, about the scams, about Bezos and his plans to litter
the Low Earth Orbit with thousands of satellites, etc, etc. I won&#39;t lie, I have
very strong negative feelings towards billionaires in general.&lt;/p&gt;
&lt;p&gt;For backup I have moved to &lt;strong&gt;Hetzner&lt;/strong&gt;, a
German company that offers storage and VPS solutions. Their 1 TB storage box is
only a little bit more expensive than storing data on AWS, but it gives me a
peace of mind. I&#39;ve also used their VPSs a bit for testing and was impressed by
their speed. Not for this blog though, this blog is running on RackNerd, mostly
because a long time ago I bought a very good deal VPS from them on a sale, so
I&#39;m keeping it.&lt;/p&gt;
&lt;p&gt;The final nail to AWS&#39;s coffin for me is that I found out that it is not simple
to just get data from Glacier, so I still haven&#39;t removed everything I store on
AWS. I don&#39;t want to feel like a prisoner of technology that is not easily under
my command. And full disclosure, I&#39;m also still using them as the CDN for this
blog, but I want to move away from it ASAP, I just need to find some time to
finalize the move.&lt;/p&gt;
&lt;p&gt;What service would you recommend for a blog CDN? Let me know via email or on
Mastodon. Thanks! And no, no Cloudflare, absolutely no Cloudflare.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Goodreads&lt;/strong&gt; - For my book log, I&#39;m moving to &lt;strong&gt;Bookwyrm&lt;/strong&gt;. Bookwyrm is also a
decentralized federation of servers, just like Mastodon. I like the simple and
clean UI. Not much else to say, if you want to send me a friend request, you can
find me at &lt;a href=&quot;https://bookwyrm.social/user/stfn&quot;&gt;bookwyrm.social/user/stfn&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Github&lt;/strong&gt; - I recently found out about &lt;strong&gt;Codeberg&lt;/strong&gt;. To quote their &amp;quot;About&amp;quot;
page: &lt;em&gt;Codeberg is not a for-profit corporation but an open community of free
software enthusiasts providing a humane non-commercial and privacy-friendly
alternative to commercial services such as GitHub.&lt;/em&gt; The non-profit and FOSS
loving side of Codeberg is what sold me to them. The downside of Codeberg is
that it does not yet provide Github-like Actions for everyone, but I can live
with that, and if I ever need to learn actions for professional reasons, I can
just have a test repo on GH. On Codeberg you can find me at
&lt;a href=&quot;https://codeberg.org/stfn&quot;&gt;codeberg.org/stfn&lt;/a&gt;. For now there&#39;s only one of me
repos there, but I will eventually move all my public and worth moving code from
GH to Codeberg.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;iPhone&lt;/strong&gt; - &lt;strong&gt;Android and F-droid&lt;/strong&gt;. In September I switched
from iPhone 7 to a Fairphone 4. And that is a big change in terms of both open
source software, and sustainability. On the topic of sustainability, Fairphone
the company, states that it is their core value. I have no way to check the
validity of their claims when they talk about &amp;quot;responsible material sourcing&amp;quot;
and &amp;quot;advocating for workers’ welfare&amp;quot;, but at least they are talking about such
issues, in contrast to other phone companies. What I do know is that Fairphone
is repairable and has a battery that can be just swapped as in the old days.
Yes, you can just take off the backplate and pull out the battery, like in a
Nokia 3310. Both of those aspects, repairability and replacability (is that even
a word?) are important to me. Batteries do not last long, and being able to just
order a new one, and replace it without dismantling the whole phone is a
welcomed change.&lt;/p&gt;
&lt;p&gt;As for open source software, this is the field in which Android&#39;s relative
openness wins over iOS. While Android is not open source, it is much less
restrictive in what can be run it. On Android you can install apps from
alternative sources without rooting the phone, and a great source of apps is
&lt;a href=&quot;https://f-droid.org/&quot;&gt;F-Droid&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;F-droid is an alternative to the Google Play store, hosting apps that are open
source, and take privacy and security seriously. I will probably do another blog
post on F-droid in detail, but for now, let me just say that among the apps from
F-droid that I use daily, there are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;AntennaPod, a player for podcasts&lt;/li&gt;
&lt;li&gt;Tusky, a Mastodon client&lt;/li&gt;
&lt;li&gt;Mullvad, a client for the Mullvad VPN&lt;/li&gt;
&lt;li&gt;StreetComplete, my favourite application and a joy for long walks. This app
allows you to update OpenStreetMap by answering simple questions as you walk
along, for example &amp;quot;what surface is this sidewalk? Are there light at this
zebra crossing? Does this bus stop have a bench?&amp;quot; I highly recommend it, and
at the same time warn, that this is a highly addictive sport.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Self-hosted solutions&lt;/h3&gt;
&lt;p&gt;I am self-hosting a lot of different services for my personal use, a full list
of them can be found in one of my previous blog posts
(&lt;a href=&quot;https://stfn.pl/blog/11-my-homelab-0823/&quot;&gt;stfn.pl/blog/11-my-homelab-0823/&lt;/a&gt;).
One that is worth mentioning here is an RSS reader. My first RSS reader was the
late Google Reader, and after its demise (wink wink google cemetery) I switched
to Feedly. Finally, a few months ago I switched to a self-hosted FOSS solution:
&lt;a href=&quot;https://www.freshrss.org/&quot;&gt;Fresh RSS&lt;/a&gt;, and I&#39;ve been using it ever since. It
just works and has a pleasant web interface. And its light, so it can run on
anything, a Raspberry Pi will do just fine.&lt;/p&gt;
&lt;h3&gt;Future plans&lt;/h3&gt;
&lt;p&gt;For me ebook reader I am still using my 4th gen Kindle which I bought way back
in 2012. I will be using it until it dies and cannot be fixed, but for the next
one, I will be looking for something that does not belong to Amazon, I am
considering either an Inkbook or a Kobo.&lt;/p&gt;
&lt;p&gt;I&#39;m also thinking of ditching the stock Android for a more open and privacy
confused mobile OS. Probably I will go with /e/ OS, as Fairphone is officially
supporting them and recently has even started to sell their phones with that
operating system preinstalled. But before that I need to do some more research,
make sure my banking apps and all that other important stuff works seamlessly
with it.&lt;/p&gt;
&lt;h3&gt;Final thoughts&lt;/h3&gt;
&lt;p&gt;The process of me moving to the better side of the internet is still in
progress. It&#39;s often not easy to break established workflows and ditch tools
that I have been using for a long time, but I feel this is the right thinkg to
do, and it makes me happy.&lt;/p&gt;
&lt;p&gt;Are you also moving away from Google and Amazon? How does it go for you? Are you
using different solutions than I? I would love to hear your feedback, contact me
on Mastodon or via email. Or maybe I could establish another way of
communication? Let me know!&lt;/p&gt;
&lt;p&gt;And finally,you can help with funding my future projects by supporting me on
these crowdfunding sites:&lt;/p&gt;
&lt;p&gt;I would like to express thanks to &lt;a href=&quot;https://mastodon.com.pl/@m0bi&quot;&gt;m0bi&lt;/a&gt;,
discrust, &lt;a href=&quot;https://toot.kuba-orlik.name/@kuba&quot;&gt;Kuba Orlik&lt;/a&gt; and &lt;a href=&quot;https://mastodon.internet-czas-dzialac.pl/@arek&quot;&gt;Arkadiusz
Wieczorek&lt;/a&gt; for showing me
cool, open source solutions.&lt;/p&gt;
</description>
      <pubDate>Fri, 20 Oct 2023 24:00:00 GMT</pubDate>
      <dc:creator>STFN</dc:creator>
      <guid>https://stfn.pl/blog/16-on-degoogling/</guid>
    </item>
    <item>
      <title>RPi 4b vs RPi 5 benchmark in BOINC - Einstein@Home</title>
      <link>https://stfn.pl/blog/17-rpi4-rpi5-boinc/</link>
      <description>&lt;p&gt;&lt;em&gt;TL;DR: The Pi 5 is roughly 30-50% faster than Pi 4b.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;I finally received my Raspberry Pi 5. I pre-ordered one straight on release day
in September, and it arrived at my door a week ago. I used it for a moment as a
desktop device with a full desktop install of the Raspberry Pi OS, and the first
impression have been very positive so far, it feels much, much snappier in
typical desktop activities such as browsing the Internet or watching videos. But
that is not why I bought it, using a Pi as a desktop is on the last position of
my &amp;quot;what to use a Pi for&amp;quot; list. I have several ideas on how I want to use the
new Pi, and the first one is to try it out as a BOINC cruncher. From that came
the plan the benchmark it in comparison to the previous model, the Raspberry Pi
4, which I have three of.&lt;/p&gt;
&lt;h2&gt;BOINC&lt;/h2&gt;
&lt;p&gt;In short, BOINC is a platform that allows people to share their computing power
with scientists that need it to do scientific analysis. Everyone can install a
BOINC client, select a project they are interesting it, and let their computer
do calculations that are then returned to the scientists. The calculations are
divided into tasks, usually a task uses a single CPU core, and you can crunch
(that is the BOINC word on performing calculations) as many tasks simultaneously
as the number of cores in your CPU. Some project also allow crunching on the
GPU. I wrote more about BOINC in this blog post: &lt;a href=&quot;https://stfn.pl/blog/10-my-thoughts-on-boinc/&quot;&gt;My thoughts as I reach 100
million points in BOINC&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I did a share of BOINC calculations on my Raspberry Pi 4 and was curious how it
compares with the new Pi 5. And so I set up a test.&lt;/p&gt;
&lt;p&gt;The BOINC project I chose for crunching in this test is
&lt;a href=&quot;https://einsteinathome.org/&quot;&gt;Einstein@Home&lt;/a&gt;. I chose this one because it&#39;s one
of only two projects that provide ARM64 tasks (the second being
&lt;a href=&quot;https://asteroidsathome.net/boinc/&quot;&gt;Asteroids@Home&lt;/a&gt;), its tasks are relatively
short, and the project has a high task size uniformity (more on that later).&lt;/p&gt;
&lt;h2&gt;Test baseline&lt;/h2&gt;
&lt;p&gt;To make the test as fair as possible I defined a set of baseline rules:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;both Pis used the same SD cards. I am using SandDisk Ultra 64GB.&lt;/li&gt;
&lt;li&gt;both Pis used the official power supplies dedicated to their specific models.&lt;/li&gt;
&lt;li&gt;both Pis used the same cases and active cooling. Actually, I put both of them
in a single cluster case. For cooling I am using a 120mm 12V fan powered by a
6V power brick. I decided to go this way, as that large fan running at half
speed is almost inaudible, and that allows me to sleep in the same room as the
test rig. The Pi 5 has the official active cooler attached, but the large fan
is working so well that the active cooler did not start during the whole test.
I was monitoring the CPU temps, and they never exceeded 55C on both Pis, so
there is certainty that neither of them was thermal throttling.&lt;/li&gt;
&lt;li&gt;both Pis were set up in the same way. The configuration was totally stock, no
overclocking. I burned the Pi OS 64 bit Lite on both SD cards, started them,
upgraded all the packages and installed BOINC. Both Pis run headless.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/17/rig1.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/17/rig1.jpg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/17/rig2.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/17/rig2.jpg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Why the test could not be fully fair&lt;/h3&gt;
&lt;p&gt;And now I must mention that even though I implemented all those measures
mentioned above, the test conditions were not perfectly uniform. The issue is
that the tasks that the BOINC servers are sending are not always the same size,
and some take a longer to finish than others. Einstein@Home tasks are rather
uniform in their size, but they are not exactly the same. A BOINC user has no
control over which tasks they receive. So it is theoretically possible that one
Pi was getting a higher percentage of larger tasks than the other. To mitigate
this problem I went with making the task pool bigger. I crunched 80 tasks per
Pi, in hope that more tasks would reduce the weight of a single task size. I
think around 80 is the sweet spot between having a large enough pool of data,
and waiting for ages for the benchmark to finish. After all, the Pis are not the
fastest crunchers.&lt;/p&gt;
&lt;p&gt;With the baseline of the way, let&#39;s see the results.&lt;/p&gt;
&lt;h2&gt;Results&lt;/h2&gt;
&lt;h3&gt;Average tasks time&lt;/h3&gt;
&lt;p&gt;For the 4b, the average tasks time was 16353 seconds (4 hours, 32 minutes and 33
seconds). For the Pi 5, the average task time was 10858 seconds (3 hours and 58
seconds), a decrease of around 33%.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn.pl/blog/17-rpi4-rpi5-boinc/!%5Bx%5D(https:/stfn-pl-blog-assets.b-cdn.net/17/avg.png)&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/17/avg.png&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Minimum task time&lt;/h3&gt;
&lt;p&gt;For the 4b, the shortest task took 13866 seconds (3 hours, 51 minutes and 6
seconds). For the Pi 5, the shortest task time took 10207 seconds (2 hours, 50
minutes and 7 seconds), a decrease of around 26%.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/17/min.png&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/17/min.png&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Maximum task time&lt;/h3&gt;
&lt;p&gt;For the 4b, the longest task took 17148 seconds (4 hours, 45 minutes and 48
seconds). For the Pi 5, the longest task took 11370 seconds (3 hours, 9 minutes
and 30 seconds), a decrease of around 33%.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/17/max.png&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/17/max.png&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Average tasks per day&lt;/h3&gt;
&lt;p&gt;A day has 86400 seconds. Both Pi 4 and Pi 5 have four cores, so they have 345600
core-seconds per day. Dividing that number by the average task time, the results
are 21 tasks per day for the Pi4 and 31 tasks per day for the Pi 5, the Pi 5 can
crunch 47% more tasks per day than the Pi 4.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/17/tpd.png&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/17/tpd.png&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Benchmark raw results&lt;/h3&gt;
&lt;p&gt;I am attaching below the benchmark data. Please feel free to use it in whatever
way you want, just please link to this blog post if you want to publish your
work.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/17/benchmark_data.ods&quot;&gt;Benchmark data in LibreOffice Calc format&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/17/benchmark_data_rpi4.csv&quot;&gt;RPi 4b results in CSV&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/17/benchmark_data_rpi5.csv&quot;&gt;RPi 5 results in CSV&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Summary and further steps&lt;/h2&gt;
&lt;p&gt;This has been only a preliminary test of the new Raspberry Pi. To sum the
results so far, the Pi 5 is, to little shock, faster than the previous model,
33% decrease in task crunch time is a reasonable generational upgrade.&lt;/p&gt;
&lt;p&gt;I want to continue testing the Pi 5 in different scenarios. For the next steps,
I will crunch Asteroids@Home, and see if the performance difference
between the 4 and 5 will be the same or different in another BOINC project.&lt;/p&gt;
&lt;p&gt;Apart from that, I wonder how much a bottleneck is using the SD card as storage.
I think I will also do a test running the Pi5 from an NVME M.2 SSD in a USB case
to see what kind of a difference will it make.&lt;/p&gt;
&lt;p&gt;I should also receive soon a Pi alternative, an Odroid C2, and see how it
compares with the two pis.&lt;/p&gt;
&lt;p&gt;As for the Pi5 itself, I plan to test it out in other scenarios, I think the
next one will be making a NAS out of it. Whatever I do with it, I will write
about it on my blog.&lt;/p&gt;
&lt;p&gt;Thanks for reading!&lt;/p&gt;
&lt;p&gt;If you have any comments about the test, its metodology, my calculations, or
have suggestions what to test next, drop me an email, or contact me on Mastodon.
Links are in the footer.&lt;/p&gt;
</description>
      <pubDate>Sat, 04 Nov 2023 24:00:00 GMT</pubDate>
      <dc:creator>STFN</dc:creator>
      <guid>https://stfn.pl/blog/17-rpi4-rpi5-boinc/</guid>
    </item>
    <item>
      <title>RPi 4b vs RPi 5 benchmark in BOINC - Asteroids@Home</title>
      <link>https://stfn.pl/blog/18-rpi4-rpi5-asteroids/</link>
      <description>&lt;p&gt;&lt;em&gt;TL;DR: For Asteroids@Home the Pi 5 is roughly three times faster than Pi 4b.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;This post is a continuation of the previous blog in which I compared Pis
crunching Einstein@Home, so here I omit the parts that would be identical. For
more information on the test background and methodology, please read &lt;a href=&quot;https://stfn.pl/blog/17-rpi4-rpi5-boinc/&quot;&gt;the
previous blog post&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The BOINC project I chose for crunching in this test is
&lt;a href=&quot;https://asteroidsathome.net/boinc/&quot;&gt;Asteroids@Home&lt;/a&gt;. This is the second of the
two projects that provide tasks for ARM 64 bit boards.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/17/rig1.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/17/rig1.jpg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/17/rig2.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/17/rig2.jpg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Asteroids@Home task uniformity&lt;/h3&gt;
&lt;p&gt;In the blog post about Einstein@Home I mentioned that BOINC does not always send
task of uniform size, some of them are smaller than the others and may skew test
results. Asteroids@Home also has this issue. From what I observed most of the
tasks are of a similar size, but there are also some outliers which run time is
much, much shorter, around 1/2 - 1/3 of the typical tasks length. Because of that I am
omitting the &amp;quot;minimum task time&amp;quot; part in this test, because it would only show
whether one of the Pis was lucky to receive such a task.&lt;/p&gt;
&lt;p&gt;What is also worth mentioning, is that Pi 4 received tasks called &amp;quot;Period Search
Application v102.14aarch64-unknown-linux-gnu&amp;quot;, while the Pi 5 tasks were called
&amp;quot;Period Search Application v102.14&amp;quot;. The application version is the same, so I
assume there was no difference, but I will validate that claim in the A@H forums
and update this part once I have an answer.&lt;/p&gt;
&lt;h3&gt;Temperatures&lt;/h3&gt;
&lt;p&gt;An interesting difference between Asteroids and Einstein on the Pi 5 was the
thermal load of the CPU.&lt;/p&gt;
&lt;p&gt;As in the previous test, my rig has a 120mm fan running at 6 volts, cooling both
RPi 4 and RPi 5 in an open cluster case. The Pi 5 also has its dedicated,
PWM-controlled active cooler attached.&lt;/p&gt;
&lt;p&gt;When crunching Einstein on the RPi 5, the temps were around 55C. The dedicated
cooler did not start, the large fan cooled it enough to keep the temps below the
threshold that would trigger the active cooler.&lt;/p&gt;
&lt;p&gt;Asteroids put a much higher thermal load on the Pi 5&#39;s CPU, the Pi was again at
around 55C, but to achieve that temperature, the active cooler was running at 5k
RPM the whole duration on the test. Whereas the Pi 4, with only a heatsink
attached, did not pass 60C as with Einstein. Seems that Asteroids are pushing
the 5&#39;s cores much harder. Maybe that is what is causing the massive change in
performance?&lt;/p&gt;
&lt;p&gt;Here I have to praise the Pi 5 active cooler. Even though running at 5k rpm, it
was inaudible, drowning in the noise of the 120mm fan running at half power.&lt;/p&gt;
&lt;p&gt;Let&#39;s see the results.&lt;/p&gt;
&lt;h2&gt;Results&lt;/h2&gt;
&lt;h3&gt;Average tasks time&lt;/h3&gt;
&lt;p&gt;For the 4b, the average tasks time was 38010 seconds (10 hours, 33 minutes and
30 seconds). For the Pi 5, the average task time was 12207 seconds (3 hours, 23
minutes and 27 seconds), around three times faster.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/18/avg.png&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/18/avg.png&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Maximum task time&lt;/h3&gt;
&lt;p&gt;For the 4b, the longest task took 42208 seconds (11 hours, 43 minutes and 28
seconds). For the Pi 5, the longest task took 15244 seconds (4 hours, 14 minutes
and 4 seconds), again more or less three times faster.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/18/max.png&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/18/max.png&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Average tasks per day&lt;/h3&gt;
&lt;p&gt;A day has 86400 seconds. Both Pi 4 and Pi 5 have four cores, so they have 345600
core-seconds per day. Dividing that number by the average task time, the results
are 9 tasks per day for the Pi4 and 28 tasks per day for the Pi 5, the Pi 5 can
crunch three times more tasks per day than the Pi 4.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/18/tpd.png%22&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/18/tpd.png%22&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Benchmark raw results&lt;/h3&gt;
&lt;p&gt;I am attaching below the benchmark data. Please feel free to use it in whatever
way you want, just please link to this blog post if you want to publish your
work.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/18/benchmark_data_asteroids.ods&quot;&gt;Benchmark data in LibreOffice Calc format&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/18/rpi4_asteroids.csv&quot;&gt;RPi 4b results in CSV&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/18/rpi5_asteroids.csv&quot;&gt;RPi 5 results in CSV&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Summary and further steps&lt;/h2&gt;
&lt;p&gt;This has been the second post in the series about comparing Pi 5 with its
predecessor. As I said in the previous blog posts, there are also other
scenarios in which I want to test the newest Pi, and now I think the immediate
next one will be running Universe@Home.&lt;/p&gt;
&lt;p&gt;Thanks for reading!&lt;/p&gt;
&lt;p&gt;If you have any comments about the test, its methodology, my calculations, or
have suggestions what to test next, drop me an email, or contact me on Mastodon.
Links are in the footer.&lt;/p&gt;
</description>
      <pubDate>Thu, 16 Nov 2023 24:00:00 GMT</pubDate>
      <dc:creator>STFN</dc:creator>
      <guid>https://stfn.pl/blog/18-rpi4-rpi5-asteroids/</guid>
    </item>
    <item>
      <title>RPi 4b vs RPi 5 benchmark in BOINC - Universe@Home</title>
      <link>https://stfn.pl/blog/19-rpi4-rpi5-universe/</link>
      <description>&lt;p&gt;&lt;em&gt;TL;DR: For Universe@Home the Pi 5 is roughly three times faster than Pi 4b.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;This is the third and final blog post in the series about comparing the
performance of Raspberry Pi 4b and 5 in crunching different BOINC projects.&lt;/p&gt;
&lt;p&gt;It&#39;s the final one because there are only three projects that I crunch on a
regular basis and that support ARM processors.&lt;/p&gt;
&lt;p&gt;Here are the previous ones:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn.pl/blog/17-rpi4-rpi5-boinc/&quot;&gt;Einstein@Home&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn.pl/blog/18-rpi4-rpi5-asteroids/&quot;&gt;Asteroids@Home&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The full description of the test background and methodology, can be found in &lt;a href=&quot;https://stfn.pl/blog/17-rpi4-rpi5-boinc/&quot;&gt;the
first episode&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/17/rig1.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/17/rig1.jpg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/17/rig2.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/17/rig2.jpg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Universe@Home task uniformity&lt;/h3&gt;
&lt;p&gt;In the blog post about Einstein@Home I mentioned that BOINC does not always send
task of uniform size, some of them are smaller than the others and may skew test
results. Universe@Home also has this issue, and to a much larger extent. The U@H
tasks differ widely in size, on my Ryzen 3700X machine, the tasks can take from
25 minutes to 90 minutes to finish. That is why for this comparison I have
crunched 200 tasks per Pi to reduce the impact of tasks dissimilarity on the
results.&lt;/p&gt;
&lt;h3&gt;Temperatures&lt;/h3&gt;
&lt;p&gt;An interesting difference between Asteroids, Einstein and Universe on the Pi 5 was the
thermal load of the CPU.&lt;/p&gt;
&lt;p&gt;As in the previous test, my rig has a 120mm fan running at 6 volts, cooling both
RPi 4 and RPi 5 in an open cluster case. The Pi 5 also has its dedicated,
PWM-controlled active cooler attached.&lt;/p&gt;
&lt;p&gt;Einstein put very little thermal load on the Pi 5 CPU, the onboard active cooler
did not even start. Whereas Asteroid put a heavy strain, the cooler had to run
at 5k RPM to keep the board at a reasonable temperature below 60C. Universe was
somewhere in between, the onboard cooler did start, but it only had to run at
2000 - 2500 RPM to keep the Pi at around 55C.&lt;/p&gt;
&lt;p&gt;Let&#39;s see the results.&lt;/p&gt;
&lt;h2&gt;Results&lt;/h2&gt;
&lt;h3&gt;Average tasks time&lt;/h3&gt;
&lt;p&gt;For the 4b, the average tasks time was 15511 seconds (4 hours, 18 minutes and 31
seconds). For the Pi 5, the average task time was 5602 seconds (1 hour, 33
minutes and 22 seconds), around 2.7 times faster.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/19/avg.png&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/19/avg.png&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Minimum task time&lt;/h3&gt;
&lt;p&gt;For the 4b, the shortest task took 6277 seconds (1 hours, 44 minutes and 37
seconds). For the Pi 5, the shortest task time took 2208 seconds (just 36
minutes and 48 seconds!), again around 2.7 - 2.8 times faster.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/19/min.png&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/19/min.png&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Maximum task time&lt;/h3&gt;
&lt;p&gt;For the 4b, the longest task took 26695 seconds (7 hours, 24 minutes and 55
seconds). For the Pi 5, the longest task took 9493 seconds (2 hours, 38 minutes
and 13 seconds), again around 2.8 times faster.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/19/max.png&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/19/max.png&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Average tasks per day&lt;/h3&gt;
&lt;p&gt;A day has 86400 seconds. Both Pi 4 and Pi 5 have four cores, so they have 345600
core-seconds per day. Dividing that number by the average task time, the results
are 22 tasks per day for the Pi 4 and 61 tasks per day for the Pi 5, the Pi 5 can
crunch 2.8 times more tasks per day than the Pi 4.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/19/tpd.png&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/19/tpd.png&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;To put that in perspective, the Raspberry Pi 5 in this particular benchmark
is faster in computation speed than my old i5-4460, and not that far from a
modern Ryzen 3700X.&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;Benchmark raw results&lt;/h3&gt;
&lt;p&gt;I am attaching below the benchmark data. Please feel free to use it in whatever
way you want, just please link to this blog post if you want to publish your
work.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/19/benchmark_data.ods&quot;&gt;Benchmark data in LibreOffice Calc format&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/19/rpi4.csv&quot;&gt;RPi 4b results in CSV&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/19/rpi5.csv&quot;&gt;RPi 5 results in CSV&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Summary and further steps&lt;/h2&gt;
&lt;p&gt;This is the final post about benchmarking the Pi in BOINC. Now I need to find
another use for it. Whatever plan I have for the newest little SBC, I will write
about it here.&lt;/p&gt;
&lt;p&gt;Thanks for reading!&lt;/p&gt;
&lt;p&gt;If you have any comments about the test, its methodology, my calculations, or
have suggestions what to test next, drop me an email, or contact me on Mastodon.
Links are in the footer.&lt;/p&gt;
</description>
      <pubDate>Thu, 30 Nov 2023 24:00:00 GMT</pubDate>
      <dc:creator>STFN</dc:creator>
      <guid>https://stfn.pl/blog/19-rpi4-rpi5-universe/</guid>
    </item>
    <item>
      <title>Invidious - Open Source Youtube Frontend</title>
      <link>https://stfn.pl/blog/20-open-source-youtube/</link>
      <description>&lt;p&gt;20th post for the first anniversary of my blog, woop woop!&lt;/p&gt;
&lt;p&gt;Continuing on the theme of degoogling, which I described in more detail in &lt;a href=&quot;https://stfn.pl/blog/16-on-degoogling/&quot;&gt;this
blog post&lt;/a&gt;, this time I look at
alternative ways to watch Youtube. Recently I cut down my YT intake
significantly, but there are still great Youtubers that I enjoy watching, and
their content is not available anywhere else, so I won&#39;t be leaving the platform
entirely. Fortunately, there are ways to watch videos without being too exposed
to Google and its shenanigans.&lt;/p&gt;
&lt;h3&gt;Le what is an Open Source Youtube Frontend?&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/20/invidious.png&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/20/invidious.png&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;A few days ago, &lt;a href=&quot;https://mastodon.com.pl/@m0bi/&quot;&gt;m0bi&lt;/a&gt; mentioned
&lt;a href=&quot;https://invidious.io/&quot;&gt;Invidious&lt;/a&gt; on Mastodon. Invidious is an open source
alternative front-end to YouTube that can be self hosted, and that combination
of words raised my interest. There&#39;s a lot to unpack here, so let&#39;s dissect what
exactly is Invidious.&lt;/p&gt;
&lt;p&gt;First of all, what is an alternative frontend to Youtube? Youtube, as every
webpage has a frontend and a backend. The backend is what is happening beneath
the hood, storing video files, database calls, user authentication and
authorization, tracking follows and like, all that stuff that needs to happen,
but nontechnical users do not care about it all. Frontend is the layer that the
user interacts with, what they see, so the actual video player, login screens,
like and subscribe buttons. When you go to &lt;a href=&quot;https://youtube.com&quot;&gt;youtube.com&lt;/a&gt;
you see the default, official frontend to Youtube. However, it is possible to
have your own frontend that will talk to the backend as if it was the official
one. This is where Invidious comes in.&lt;/p&gt;
&lt;p&gt;Invidious is open source, meaning anyone can see and edit its code, and offer
fixes that will be merged to its distribution. It allows you to create accounts
that are not Youtube accounts. Thanks to that, you can follow people and build
playlist, and the Google overlords will have no data on you. &lt;strong&gt;And it does not show ads&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;I&#39;ve been using it for a few days, and it has been great. The UI is much
cleaner, there are no ads, and there is no algorithm pushing you random videos
to watch. The last thing may be a downside to some, but if you migrate from YT
and have an established set of people to watch, you won&#39;t miss it. And maybe it
will save you from spending too much time watching videos. With all the praise,
I however spotted two downsides to Invidious:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If you go with a self-hosted Invidious as I do (more on that later), The
Firefox &lt;a href=&quot;https://addons.mozilla.org/en-US/firefox/addon/sponsorblock/&quot;&gt;SponsorBlock
addon&lt;/a&gt; does not
work. I found a &lt;a href=&quot;https://github.com/ajayyy/SponsorBlock/issues/815&quot;&gt;GitHub
thread&lt;/a&gt; on it, with no
resolution.&lt;/li&gt;
&lt;li&gt;You cannot just paste a YT link to the Invidious search input, that would be
an awesome addition.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To watch videos with Invidious (seriously, I cannot type this name correctly at
the first try) you can use one of the official instances listed in the
&lt;a href=&quot;https://docs.invidious.io/instances/&quot;&gt;Invidious docs&lt;/a&gt;, or self host one. As I
am a self hosting nerd, of course I went with the second option.&lt;/p&gt;
&lt;h3&gt;Self Hosting Invidious&lt;/h3&gt;
&lt;p&gt;Instructions how to self host Invidious are in its &lt;a href=&quot;https://docs.invidious.io/installation/#docker-compose-method-production&quot;&gt;official
documentation&lt;/a&gt;.
I went with the simplified production deployment with Docker, because I do not
run a reverse-proxy on my homelab (I will one day sit to it, I swear!)&lt;/p&gt;
&lt;p&gt;I cloned the repo to my homelab and generated a secret hash:&lt;/p&gt;
&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; clone https://github.com/iv-org/invidious.git
&lt;span class=&quot;token builtin class-name&quot;&gt;cd&lt;/span&gt; invidious
pwgen &lt;span class=&quot;token number&quot;&gt;20&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you do not have &lt;code&gt;pwgen&lt;/code&gt;, just apt install it. The next step is to edit the
&lt;code&gt;docker-compose.yml&lt;/code&gt; file. I copied the generated hash to the &amp;quot;CHANGE_ME!!&amp;quot;
place in the YAML file. Finally I changed the ports stanza from &lt;code&gt;- &amp;quot;127.0.0.1:3000:3000&amp;quot;&lt;/code&gt; to &lt;code&gt;- &amp;quot;3010:3000&amp;quot;&lt;/code&gt; because as I said, I don&#39;t have a
reverse proxy, and also on my homelab port 3000 is already occupied. If you have
port 3000 free, don&#39;t change it. All that was left was simply &lt;code&gt;docker compose up -d&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;When I opened &lt;code&gt;http://&amp;lt;my_homelab_ip&amp;gt;:3010&lt;/code&gt; I saw a beautifully empty page. I
searched for a video in the search bar, and it just played. Perfect.&lt;/p&gt;
&lt;p&gt;The next step was to create an account. It&#39;s not required, but it allow you to
follow people. Because accounts are hosted locally and not connected to YT in
any way, I could name it whatever I want, so I went with the traditional
&lt;code&gt;123qwe&lt;/code&gt; username. Imagine trying that on YT, lol.&lt;/p&gt;
&lt;h3&gt;One final thing&lt;/h3&gt;
&lt;p&gt;The docs suggest that Invidious should be restarted often, preferably once every
hour, so I did it with a cron job. Just fire &lt;code&gt;crontab -e&lt;/code&gt; and add the line below
at the end of the file, remember to change the path to yours. And if you have an
older version of Docker, for you it might &lt;code&gt;docker-compose&lt;/code&gt; with a hyphen.&lt;/p&gt;
&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; * * * * /usr/bin/docker compose &lt;span class=&quot;token parameter variable&quot;&gt;-f&lt;/span&gt; /home/stefan/Containers/invidious/docker-compose.yml restart&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And that&#39;s it, you now have a way to watch Youtube without actually going to
Youtube, how cool is that?!&lt;/p&gt;
&lt;p&gt;Thanks for reading! And thank you everyone who in the last year read my blog,
left me feedback, shared it, liked it, and supported me on Ko-Fi.&lt;/p&gt;
&lt;h3&gt;Update 06-12-2023&lt;/h3&gt;
&lt;p&gt;After publishing this post I realized that I cannot set the video resolution to
anything higher than 720p. It turned out that this is the default setting, I
guess to reduce strain on small servers hosting Invidious.&lt;/p&gt;
&lt;p&gt;To change it, go to the settings (little cogwheel in the top right corner), and
change the Preferred video quality setting to DASH. This way you will unlock all
of the available resolutions of a given video.&lt;/p&gt;
&lt;p&gt;Before:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/20/before.png&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/20/before.png&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Changing the settings:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/20/settings.png&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/20/settings.png&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;After:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/20/after.png&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/20/after.png&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
</description>
      <pubDate>Tue, 05 Dec 2023 24:00:00 GMT</pubDate>
      <dc:creator>STFN</dc:creator>
      <guid>https://stfn.pl/blog/20-open-source-youtube/</guid>
    </item>
    <item>
      <title>Display your Mastodon followers count on a 4 digit TM1637 display</title>
      <link>https://stfn.pl/blog/21-display-mastodon-followers/</link>
      <description>&lt;p&gt;If you ever wanted to show how many followers on Mastodon you have without
pointing people to your account, but instead present it on a physical numeric
display, you have come to the right place.&lt;/p&gt;
&lt;p&gt;This is a quick and simple project that with the right hardware you can finish
in maybe 15 minutes. And can be infinitely expanded by presenting other
information as well, or toggling through data, or doing simple animations. The
sky (but not Bluesky) is the limit.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/21/display.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/21/display.jpg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;What you need&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;A single board computer with GPIOs, any Raspberry Pi or its clone will do. I
wrote the script to be run a full-fledged computer running Pi OS, but with
minor modifications can also be run on a microcontroller like a Pi Pico. For
brevity, I will assume you are using a Raspberry Pi.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;A TM1637 based four digit display. You can buy them cheap from Arduino or any other electronics store.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Female to female connecting cables.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;Create access creds for Mastodon API&lt;/h3&gt;
&lt;p&gt;The Pi will need to talk to Mastodon using the Mastodon API. For that to happen,
access credentials need to be generated. Open your Mastodon account page, and go
to Preferences, next Developer, and click New Application. Fill in the form, the
name and website can be anything, it does not matter. In the Scopes section
select only &amp;quot;read all your account&#39;s data&amp;quot;. That is all that is needed, and it
is a good practice to give such creds only the minimal set of permissions, so
don&#39;t select anything else.&lt;/p&gt;
&lt;p&gt;Once you created an application, you will be given the Client key, the Client
secret and the Access token. Save them in a secure place and do not share them
with anyone. Let&#39;s now move to setting up the Pi.&lt;/p&gt;
&lt;h3&gt;Hardware Setup&lt;/h3&gt;
&lt;p&gt;I assume you have a working single board computer running Linux, which you can
access using a keyboard and a screen, or via SSH. I will not dive into
configuring and starting an SBC here.&lt;/p&gt;
&lt;p&gt;Once you have an SBC ready, make sure it is powered down before connecting the display to the Pi.&lt;/p&gt;
&lt;p&gt;Here&#39;s the pinout diagram which I have taken from the Pi&#39;s official
documentation. If you are using a different board, check its docs.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.raspberrypi.com/documentation/computers/images/GPIO-Pinout-Diagram-2.png&quot;&gt;&lt;img src=&quot;https://www.raspberrypi.com/documentation/computers/images/GPIO-Pinout-Diagram-2.png&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The display has four pins: VCC, GND, CLK and DIO.&lt;/p&gt;
&lt;p&gt;Connect the VCC pin to the 3.3V pin on the Pi, this is GPIO 1. Connect the GND
pin to any of the ground pins on the Pi, I connected it to the ground pin next
to the 5V pins, as it&#39;s the closest one to the 3.3V one.&lt;/p&gt;
&lt;p&gt;The CLK and DIO pins can be connected to any digital input/output pin on the Pi.&lt;/p&gt;
&lt;p&gt;I connected them to GPIO 2 and GPIO 3 respectively as again, they are the closest ones.&lt;/p&gt;
&lt;p&gt;Start the Pi and open the terminal.&lt;/p&gt;
&lt;h3&gt;Software Setup&lt;/h3&gt;
&lt;p&gt;I am assuming you are running a Debian-like distro. If not, use the package
manager that comes with it. This will be the only distro-specific part.&lt;/p&gt;
&lt;p&gt;First, make sure you have &lt;code&gt;python-venv&lt;/code&gt; and &lt;code&gt;git&lt;/code&gt; installed. We will need them
later.&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;apt-get&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; python3-venv &lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Copy my repo to fetch the required files
bash&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git clone https://codeberg.org/stfn/mastodon-followers-display.git
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now it&#39;s time for setting up the script. It is good practice to never mess with
the system Python environment, so let&#39;s create a virtual environment to install
the dependencies needed to run the script.&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;python3 &lt;span class=&quot;token parameter variable&quot;&gt;-m&lt;/span&gt; venv venv
&lt;span class=&quot;token builtin class-name&quot;&gt;source&lt;/span&gt; venv/bin/activate
pip &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-r&lt;/span&gt; requirements.txt&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The next step is to set the access credentials so that the script will be able
to talk to Mastodon. Copy the &lt;code&gt;.env-example&lt;/code&gt; file to a new one called &lt;code&gt;.env&lt;/code&gt; and
fill in the variables with the credentials you got when setting up the Mastodon
API. The &lt;code&gt;API_BASE_URL&lt;/code&gt; is the url of your instance.&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;cd&lt;/span&gt; mastodon-followers-display
&lt;span class=&quot;token function&quot;&gt;cp&lt;/span&gt; .env-example .env
&lt;span class=&quot;token function&quot;&gt;nano&lt;/span&gt; .env&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Running and adding to cron&lt;/h3&gt;
&lt;p&gt;Once the setup is done, it&#39;s time to actually test the script&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;python script.py&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The display should come to life, first show 0000, and once the Pi logs into
Mastodon, it should show your followers count. If nothing happens, make sure
that the pins are connected correctly, maybe DIO and CLK are the other way
round.&lt;/p&gt;
&lt;p&gt;The final step is adding the script to cron so that it will be run periodically, for example every five minutes.&lt;/p&gt;
&lt;p&gt;Open cron:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;crontab&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-e&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;At the bottom of the file, add this line:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;*/5  * * * * /home/stfn/mastodon-followers-display/venv/bin/python /home/stfn/mastodon-followers-display/script.py&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The first part of the line is the cron time statement. This one means &amp;quot;every ten
minutes&amp;quot;. Use &lt;a href=&quot;https://stfn.pl/blog/21-display-mastodon-followers/%22https:/crontab.guru/&quot;&gt;crontab guru&lt;/a&gt; if you want to have a
different time period. Cron requires absolute paths, that&#39;s why we are providing
first the path to the python binary that is in the virtual environment created
during setup, and then the absolute path to the script file. Remember to change
the paths to your actual ones!&lt;/p&gt;
&lt;p&gt;Aaand done, now you can put the Pi with the display on a shelf and boast how
many people follow you on Mastodon, after all, this is all that matters, right?
Right?&lt;/p&gt;
&lt;p&gt;( ͡° ͜ʖ ͡ – ✧)&lt;/p&gt;
&lt;p&gt;Thanks for reading!&lt;/p&gt;
</description>
      <pubDate>Sat, 16 Dec 2023 24:00:00 GMT</pubDate>
      <dc:creator>STFN</dc:creator>
      <guid>https://stfn.pl/blog/21-display-mastodon-followers/</guid>
    </item>
    <item>
      <title>How to monitor 12V battery charge with a Raspberry Pi Pico</title>
      <link>https://stfn.pl/blog/22-pico-battery-level/</link>
      <description>&lt;p&gt;Last year I built &lt;a href=&quot;https://stfn.pl/blog/12-off-grid-powerbank/&quot;&gt;a 12V powerbank&lt;/a&gt; to power
my astrophotography rig and to light my shed. It has been working great so far,
in fact I liked it so much I built a second one. While I&#39;ve been very happy with
my DIY powerbanks in general, they lack one functionality, and that is remote
monitoring of the battery charge level. It would be awesome if I could check the
battery voltage remotely, while the rig is working out there in the cold night.
This need inspired me to look for battery monitoring solutions, but from the
ones I found, none were interesting, and after all, it&#39;s much more fun to build
something by yourself, rather than take something off the shelf.&lt;/p&gt;
&lt;p&gt;I have a Raspberry Pi Pico W, which I already used for other projects, such as
making a &lt;a href=&quot;https://stfn.pl/blog/04-pico-weather-station2/&quot;&gt;weather station&lt;/a&gt; and I
decided I want to do something based on it. When browsing the internet, I came
across &lt;a href=&quot;https://forums.raspberrypi.com/viewtopic.php?t=336885&quot;&gt;this forum
thread&lt;/a&gt; showing how to
measure voltage with the Pico.&lt;/p&gt;
&lt;h2&gt;Theoretical background&lt;/h2&gt;
&lt;p&gt;The Raspberry Pi Pico has 3 ADC (Analogue to Digital Converter) inputs. Analogue
in this case meaning it can measure voltage as a continuous spectrum, in
contrast to the digital pins, which only work in binary, 0 and 1, voltage and no
voltage. In this project I am using one of those inputs to measure the voltage
of the battery. Now, the problem is that, it can only accept voltages up to
3.3V. If I wanted to measure, let&#39;s say, the voltage of a 1.2V AA battery that
would be fine, but I want to monitor a 12V battery, which voltage can range from
13.5V at full charge, to 11V at depletion. That&#39;s why we need to use a tool from
the basics of electronics: a voltage divider.&lt;/p&gt;
&lt;p&gt;A voltage divider is a circuit which consists of two resistors
connected in series. The voltage measured between the point between the
resistors (Vout) and the negative battery terminal will be lower than the
voltage of the battery itself (Vin). The voltage drop is constant and based on
the values of the resistors, so by measuring the lowered voltage and knowing the
conversion rate, we can calculate the voltage of the battery. Looking at the
diagram should give you a better understanding of the whole idea.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/22/Resistive_divider.png&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/22/Resistive_divider.png&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;Image taken from Wikipedia
&lt;a href=&quot;https://commons.wikimedia.org/w/index.php?curid=36347358&quot;&gt;commons.wikimedia.org/w/index.php?curid=36347358&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2&gt;Hardware&lt;/h2&gt;
&lt;p&gt;To build this battery voltage measuring device, we will need:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A microcomputer or a microcontroller with GPIOs, with at least one analogue
GPIO. I am using a Raspberry Pi Pico, because this is what I already have
around.&lt;/li&gt;
&lt;li&gt;a prototype board&lt;/li&gt;
&lt;li&gt;two resistors&lt;/li&gt;
&lt;li&gt;cables&lt;/li&gt;
&lt;li&gt;the usual tools: a soldering iron, cable crimpers and strippers&lt;/li&gt;
&lt;li&gt;optionally: a buck converter to power the Pico from the 12V battery, but that
won&#39;t be needed if you want to power the Pico from an external power source. I
bought a &lt;a href=&quot;https://botland.store/converters-step-down/2967-step-down-voltage-inverter-lm2596-32v-35v-3a-5903351241397.html&quot;&gt;variable
step-down converter based on a LM2596 chip&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Choosing the resistors&lt;/h3&gt;
&lt;p&gt;We will need the voltage divider to reduce the incoming battery voltage (13.5V -
11V) to a value acceptable by the Pi Pico, which is below 3.3V.&lt;/p&gt;
&lt;p&gt;Below is the formula to calculate the voltage drop. There is also a handy &lt;a href=&quot;https://ohmslawcalculator.com/voltage-divider-calculator&quot;&gt;online
calculator&lt;/a&gt; available.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/22/voltage_divider2.png&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/22/voltage_divider2.png&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;After doing some trial and error calculations I went with a 75 kOhm and a 20 kOhm
resistor. This way even at the maximum cell voltage of ~13.5V, the voltage coming to
the Pico will be around 3V. The resistors are in the kiloohm range to reduce the amount
of current flowing through the Pi.&lt;/p&gt;
&lt;h2&gt;Connecting stuff&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/22/circuit.png&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/22/circuit.png&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;Diagram made with &lt;a href=&quot;https://www.circuit-diagram.org/editor/&quot;&gt;Circuit-Diagram&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;The circuit is very simple. The branch starts with the positive connector of the
battery, goes through both resistors and returns to the negative connector. From
a point between the resistors, another branch connects to one of the analogue
pins on the Pi. I went with Pin 28, as it&#39;s the nearest one to the AGND (Analogue
Ground Pin). A final branch goes from the AGND to the negative connector on the
battery. And we&#39;re done.&lt;/p&gt;
&lt;p&gt;I used a prototype board to connect all of the components. First I went with
soldering the resistors to the prototype board. Remember or mark which resistor
is the higher value one and which is the lower value. I left one hole of space
between the resistors for a cable going to the Pico.&lt;/p&gt;
&lt;p&gt;The next step is to solder the cables going from the battery to the resistors.
If you are going to use a plug to connect the device to a battery as I am, it&#39;s
a good time to solder it now. Pay attention to the polarity of the plug, the
positive cable needs to be connected to the resistor with the higher value.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/22/cables3.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/22/cables3.jpg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Now solder to the resistors the two cables going from the board to the Pico.
Don&#39;t solder cables to the Pico itself yet! Once you have the resistors and the
cables soldered, this is a good point to test that the voltage divider actually
works. Connect the two battery cables to a battery and with a multimeter,
measure the voltage between the the cables that will be connected to the Pico.
If the battery is close to full charge, the measured voltage should be around
3V. If that is the case, cool, we&#39;re home. If not, something went very wrong
with the circuit, check all the connections again.&lt;/p&gt;
&lt;p&gt;Now connect the board-to-Pico cables to the analogue input,
and the analogue ground of the Pi.&lt;/p&gt;
&lt;p&gt;The soldering part is done. Take a double look at your work and make sure
everything is connected properly. With the Pico UNPLUGGED from the computer,
connect the battery cables to the battery socket. Wait a few minutes. Nothing
should get hot, nothing should emit smoke. If everything looks fine, it&#39;s time
to connect the Pico the a computer and start working on the software side.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/22/soldered.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/22/soldered.jpg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Software&lt;/h2&gt;
&lt;p&gt;I am assuming you know how to access the Pi Pico in a code editor and you are
using MicroPython. I am using Thonny to write and run code on the Pi.&lt;/p&gt;
&lt;h3&gt;Reading the voltage&lt;/h3&gt;
&lt;p&gt;Let&#39;s start with something very simple, just measuring the voltage and printing it out.&lt;/p&gt;
&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; machine &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; Pin&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; ADC
&lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; time &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; sleep
analogue_input &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; ADC&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;28&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

VOLTAGE_DROP_FACTOR &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4.572&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    sensor_value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; analogue_input&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;read_u16&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    voltage &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; sensor_value &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;3.3&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;65535&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; VOLTAGE_DROP_FACTOR
    &lt;span class=&quot;token keyword&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;voltage&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    sleep&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;30&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Most of the code should be obvious. Wer are importing the needed modules,
measuring the voltage in a loop, and printing the measured value. But let&#39;s talk
about this line.&lt;/p&gt;
&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;voltage &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; sensor_value &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;3.3&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;65535&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; VOLTAGE_DROP_FACTOR&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is how the voltage on the Pico should be measured. The actual value
returned by measuring the analogue input is a number between 0 and 65535, with
the max value meaning 3.3V and 0 meaning 0V. By multiplying that value by 3.3 we
are getting the voltage coming through the analogue pin. However, we are not
exactly interested in the voltage at the Pi pin, we want to know the voltage of
the battery. We are using a voltage divider that drops the battery voltage,
right? Therefore, we need to multiply the Pi voltage by the voltage divider
factor. Here I went with the simplest approach. I measured at the same time the
voltage reported by the pi, and, using a multimeter, the battery voltage and
calculated the difference factor. For me it is 4.572, but as every resistor
differs a tiny bit, it will be different for everyone else. You will need to
measure and calculate your own drop factor.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;The part below is totally optional, feel free to skip it.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;When testing if the whole device actually works, I let it run for a few hours
and saved the measured values to a file, and plotted it in a Jupyter Notebook
using matplotlib.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/22/voltage_plot.png&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/22/voltage_plot.png&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Here is the code used to generate the plot:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;voltages&lt;/code&gt; is a file with each value on its own line. The raw values from the
file are plotted in blue, and as you can see the data is noisy, so I used a
smoothing filter for the red line. I found how to do it &lt;a href=&quot;https://stackoverflow.com/questions/20618804/how-to-smooth-a-curve-for-a-dataset&quot;&gt;here
on stackoverflow&lt;/a&gt;&lt;/p&gt;
&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; matplotlib&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;pyplot &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; plt
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; numpy &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; np
&lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; math &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; factorial
&lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; scipy&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;signal &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; savgol_filter

&lt;span class=&quot;token keyword&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;voltages&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; f&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    lines &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; f&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;readlines&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
lines &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 builtin&quot;&gt;float&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;line&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;strip&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 keyword&quot;&gt;for&lt;/span&gt; line &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; lines&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
lines &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; np&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;array&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;lines&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

x &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; np&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;array&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;x &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; x &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;lines&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;

smooth_lines &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; savgol_filter&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;lines&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;51&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
plt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;plot&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;x&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; lines&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
plt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;plot&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;x&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; smooth_lines&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;color&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;red&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
plt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ylabel&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;voltage&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
plt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;xlabel&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;data points&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
plt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;axis&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 number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;lines&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 number&quot;&gt;11.8&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;12.4&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;
plt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;savefig&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;voltage.png&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
plt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;show&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;h3&gt;Adding internet connectivity&lt;/h3&gt;
&lt;p&gt;Once we are able to read the voltage, it would be good to send it somewhere,
wouldn&#39;t it? And for that I am using the power of the Internet.&lt;/p&gt;
&lt;p&gt;First we need to connect the Pi to a WiFi network. Here&#39;s the code to do it.
Save it on the Pico in a file called &lt;code&gt;boot.py&lt;/code&gt;, and this way the script will be
run every time the Pico is powered on. This is exactly the same code I used in
the &lt;a href=&quot;https://stfn.pl/blog/04-pico-weather-station2/&quot;&gt;Pico Weather Station post&lt;/a&gt;.
I believe the code is self-explanatory, but is something is unclear, refer to
that previous post, I explain it there in detail.&lt;/p&gt;
&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; time
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; network
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; machine

led &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; machine&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Pin&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;LED&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; machine&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Pin&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;OUT&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

ssid &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;NETWORK-NAME&quot;&lt;/span&gt;
password &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;PASSWORD&quot;&lt;/span&gt;

wlan &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; network&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;WLAN&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;network&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;STA_IF&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
wlan&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;active&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
wlan&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;connect&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ssid&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; password&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

max_wait &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;while&lt;/span&gt; max_wait &lt;span class=&quot;token operator&quot;&gt;&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;
    led&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;on&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    time&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sleep&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    led&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;off&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;if&lt;/span&gt; wlan&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isconnected&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 keyword&quot;&gt;break&lt;/span&gt;
    max_wait &lt;span class=&quot;token operator&quot;&gt;-=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;
    time&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sleep&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;not&lt;/span&gt; wlan&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isconnected&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 keyword&quot;&gt;for&lt;/span&gt; _ &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;5&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;
        led&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;on&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        time&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sleep&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        led&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;off&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        time&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sleep&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        led&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;on&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        time&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sleep&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        led&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;off&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        time&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sleep&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.7&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    time&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sleep&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    machine&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;reset&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;else&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    status &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; wlan&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ifconfig&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    led&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;on&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    time&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sleep&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    led&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;off&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;h3&gt;Pico web server&lt;/h3&gt;
&lt;p&gt;Next we need to make the voltage information available. The simpler way is to
make a web server out of the Pico. I took the code below from a &lt;a href=&quot;https://www.raspberrypi.com/news/how-to-run-a-webserver-on-raspberry-pi-pico-w/&quot;&gt;guide
on the Raspberry Pi webpage&lt;/a&gt; and modified it a bit to accommodate to my needs.&lt;/p&gt;
&lt;p&gt;In short, the Pi will be listening for any incoming connections, and once a
client talks to the Pi, it will measure the voltage and return it in a JSON
format. If you prefer HTML rather than JSON, check the guide linked above for an
example.&lt;/p&gt;
&lt;p&gt;One issue with using the Pi as a webserver is that you will need to know it&#39;s IP
address to connect to it. To find the IP address you can use &lt;code&gt;nmap&lt;/code&gt; under Linux,
or an iOS/Android wifi scanner application.&lt;/p&gt;
&lt;p&gt;Save the file below as &lt;code&gt;main.py&lt;/code&gt;. &lt;code&gt;boot&lt;/code&gt; and &lt;code&gt;main&lt;/code&gt; are special names for
files on the Pico. Every time the Pico is powered on, first the &lt;code&gt;boot&lt;/code&gt; file is
run, and then the &lt;code&gt;main&lt;/code&gt; one. This happens even when the Pico is not connected
to a computer.&lt;/p&gt;
&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; json
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; network
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; socket
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; time

VOLTAGE_DROP_FACTOR &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4.572&lt;/span&gt;

analogue_input &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; machine&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ADC&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;28&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

addr &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; socket&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;getaddrinfo&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;0.0.0.0&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;80&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 number&quot;&gt;0&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 operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;

s &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; socket&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;socket&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
s&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;setsockopt&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;socket&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;SOL_SOCKET&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; socket&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;SO_REUSEADDR&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
s&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;bind&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;addr&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
s&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;listen&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        cl&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; addr &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; s&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;accept&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

        sensor_value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; analogue_input&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;read_u16&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        voltage &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; sensor_value &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;3.3&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;65535&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; VOLTAGE_DROP_FACTOR

        response &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 string&quot;&gt;&quot;voltage&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; voltage&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        cl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;send&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;HTTP/1.0 200 OK&#92;r&#92;nContent-type: application/json&#92;r&#92;n&#92;r&#92;n&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        cl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;send&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;json&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;dumps&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;response&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        cl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;close&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;except&lt;/span&gt; OSError &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        cl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;close&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;h3&gt;Sending data with MQTT&lt;/h3&gt;
&lt;p&gt;And here is the MQTT version. I prefer using MQTT over HTTP as I can pipe MQTT
messages into my InfluxDB/Grafana monitoring pipeline and put the data into a
nice graph.&lt;/p&gt;
&lt;p&gt;To use MQTT you will need to install the &lt;code&gt;umqtt&lt;/code&gt; library on the Pico, Thonny
support installing additional packages using the &amp;quot;Tools -&amp;gt; Manage packages&amp;quot;
menu.&lt;/p&gt;
&lt;p&gt;What is also the upside of using MQTT is that we do not need to know the IP of
the Pico, here the Pi needs to know the IP address of the MQTT broker, which is
usually static or just more stable than the IP address of the Pico.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;main.py&lt;/code&gt; file for using MQTT:&lt;/p&gt;
&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; json
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; time
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; machine
&lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; umqtt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;simple &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; MQTTClient

led &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; machine&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Pin&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;LED&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; machine&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Pin&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;OUT&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
analogue_input &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; machine&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ADC&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;28&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

VOLTAGE_DROP_FACTOR &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4.572&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    sensor_value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; analogue_input&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;read_u16&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    voltage &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; sensor_value &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;3.3&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;65535&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; VOLTAGE_DROP_FACTOR

    payload &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 string&quot;&gt;&quot;voltage&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; voltage&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    qt &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; MQTTClient&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;umqtt_client&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&amp;lt;MQTT BROKER IP&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; keepalive&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3600&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    qt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;connect&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    qt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;publish&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;b&quot;battery&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; json&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;dumps&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;payload&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    qt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;disconnect&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    led&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;on&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    time&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sleep&lt;span class=&quot;token punctuation&quot;&gt;(&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;
    led&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;off&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    time&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sleep&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;30&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The code is very similar to the one shown in the &lt;a href=&quot;https://stfn.pl/blog/04-pico-weather-station2/&quot;&gt;Pico Weather Station
post&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The main difference here is that with MQTT we are pushing the data in set
intervals to an MQTT broker, a server that handles the incoming messages and
allow other clients, such as InfluxDB, to read them and use them to for example
create a graph. &lt;code&gt;battery&lt;/code&gt; is the name of the topic, in MQTT topics are a way to
separate different types of messages from each other. This part of the code sets
up an MQTT client instance, connects and sends the payload.&lt;/p&gt;
&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;qt &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; MQTTClient&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;umqtt_client&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&amp;lt;MQTT BROKER IP&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; keepalive&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3600&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
qt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;connect&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
qt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;publish&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;b&quot;battery&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; json&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;dumps&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;payload&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
qt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;disconnect&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;This is not the place to dive into MQTT specifics, if you want to start using
this protocol, start with checking out &lt;a href=&quot;https://mosquitto.org/&quot;&gt;Mosquitto&lt;/a&gt;, a
popular MQTT broker.&lt;/p&gt;
&lt;h2&gt;Base done&lt;/h2&gt;
&lt;p&gt;The base is now done, we have a working battery monitoring device that can send
the battery status over the network. When connected to a battery, and powered
via USB from a powerbank or a wall charger, it will connect to the internet and
provide information on the voltage level of a 12V battery.&lt;/p&gt;
&lt;p&gt;Now let&#39;s tackle the issue that is is being powered from a power source that is
external to the 12V battery. This can be seen as either a feature or a flaw of
the design. A feature because this way it won&#39;t drain the main battery, and will
run even if the main battery is temporarily turned off. A flaw because it adds a
second battery that needs to be charged, and together with that additional
cabling and complication in general.&lt;/p&gt;
&lt;h2&gt;Changing the power source&lt;/h2&gt;
&lt;p&gt;In this section let&#39;s talk about modifying the device so that it does not
require an additional power source to run.&lt;/p&gt;
&lt;p&gt;The Pi Pico can be powered from a 5V power source, either via the USB port or
the VSYS and GND pins. In my previous project I went with
powering it with the pins, and this is also what I am doing here.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/22/circuit_with_buck_converter.png&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/22/circuit_with_buck_converter.png&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I ordered a buck converter that can take a higher input voltage and output a
stable 5V. The on I bought is a variable output converter based on the LM2596
chip. Before using with the Pi, it needs to be configured. There is a tiny
potentiometer on the board, and by turning it you can set the output voltage. I
connected the buck converter to a 12V battery and checked the output voltage
with a multimeter. I turned the potentiometer until the output was exactly 5V.
Now the thing that was left, was to connect the converter output to the VSYS and
GND pins on the Pi. As always, follow the polarity.&lt;/p&gt;
&lt;p&gt;And done, now there is only one plug coming out of the box. When the device is plugged
into a 12V power source, it will automatically power up and start transmitting battery
levels.&lt;/p&gt;
&lt;h4&gt;The finished project&lt;/h4&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/22/soldered_final.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/22/soldered_final.jpg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Final words&lt;/h2&gt;
&lt;p&gt;I still need to tweak some things, when I finished the soldering I realized the
cables are a bit short. I also need to weather-proof the box. I&#39;m also thinking
of adding a thermometer to monitor the battery temperature, but that is
something for another project and another blog post.&lt;/p&gt;
&lt;p&gt;This has been the longest and most complex blog post so far. I hope you enjoyed
it and will build your own device! As always, I would love to hear feedback from
you. If you have thoughts on what I wrote, please let me know either via email,
or by talking to me on Mastodon. Links are in the footer. Thank you so much for
reading!&lt;/p&gt;
</description>
      <pubDate>Fri, 05 Jan 2024 24:00:00 GMT</pubDate>
      <dc:creator>STFN</dc:creator>
      <guid>https://stfn.pl/blog/22-pico-battery-level/</guid>
    </item>
    <item>
      <title>Building the tiniest NAS with ZFS using a Raspberry Pi Zero</title>
      <link>https://stfn.pl/blog/23-zero-nfs-nas/</link>
      <description>&lt;p&gt;Some time ago I came across &lt;a href=&quot;https://www.tomshardware.com/news/raspberry-pi-zero-2-w-powers-budget-friendly-nas&quot;&gt;this article about making a NAS from a Raspberry Pi
Zero
2W&lt;/a&gt;.
After reading, it dawned at me that I can make something similar, but turn it up
to 11. I decided to make a NAS with a Raspberry Pi Zero W, that will have a ZFS
mirror pool made from two USB drives.&lt;/p&gt;
&lt;p&gt;Why? Because I wanted to see what would happen, and learn exactly how bad it would be.&lt;/p&gt;
&lt;p&gt;I have a Pi Zero W, it was the first Pi I ever bought, but now it was just
sitting in my cupboard waiting for a new idea. I broke it&#39;s camera connector, so
it wasn&#39;t in the best shape. I also have the OTG cable for cable, a powered USB
hub, and two very cheap 128GB USB drives, that are so slow they cannot be used
for any real work. Combining those parts together sounded like a perfect usecase
for parts that would serve no other purpose.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/23/zerozfs.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/23/zerozfs.jpg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Setting up&lt;/h2&gt;
&lt;p&gt;First I burned a Raspbian Lite 32 bit on one of my smaller microSD cards. I
always run my Zeros headless, so I set up SSH and Wi-Fi. I powered the board on,
sshed (yes, that is a word, I say so, therefore it is) into it, did the typical
&lt;code&gt;apt update &amp;amp;&amp;amp; apt upgrade&lt;/code&gt;, and started to look for a way to install ZFS.&lt;/p&gt;
&lt;p&gt;After checking some options like building the binaries from scratch, I went with
the simplest and laziest option:&lt;/p&gt;
&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;apt&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; zfsutils-linux&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And I waited. And waited. Turned out that this way, the ZFS utils were being
installed for every kernel version I had on the Pi, and there were four of them.
Not sure why, I am not well versed in this low-level Linux kernel stuff. Anyway,
&lt;strong&gt;after five hours it still did not finish installing&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;It was getting late and I really wanted to go to bed and turn off my
not-so-quiet desktop, so I canceled the build process, and went with plan B: I
sshed to the Pi with my phone using Termius, and run the install process again
from there by running &lt;code&gt;sudo dpkg --configure -a&lt;/code&gt;. The phone kept the ssh session
running. Thinking about it now, I could have just connected the Pi to a monitor
and keyboard, but that would mean fighting with the cable organization behind
the monitor, and again, this was a project driven by laziness.&lt;/p&gt;
&lt;p&gt;When I woke up the next day, the ZFS utils were already installed. So it looks
like installing NFS on a Raspberry Pi Zero takes between five and eight hours.
Oh, and at the beginning you get an error message saying that you should not do
it on a 32 bit system. But as they young people are saying: &lt;em&gt;YOLO the
police&lt;/em&gt;.&lt;/p&gt;
&lt;h2&gt;Creating the pool&lt;/h2&gt;
&lt;p&gt;Running a few basic ZFS commands like &lt;code&gt;zfs get all&lt;/code&gt; or &lt;code&gt;zpool list&lt;/code&gt; confirmed
the ZFS was installed on the Pi. Now it was time to create the pool. At first I
decided the create the mirror pool on my desktop to save time, and import it on
the Pi.&lt;/p&gt;
&lt;h3&gt;Le nope&lt;/h3&gt;
&lt;p&gt;I connected the USB drives to the hub, and the hub to the OTG cable connecting
it to the Pi. And nothing. Both &lt;code&gt;lsusb&lt;/code&gt; and &lt;code&gt;lsblk&lt;/code&gt; did not see any drives. The
drives did not light up their LEDs. I suspected it&#39;s maybe because I created the
pool on a x86 64 bit machine and was trying to run it on a 32 bit ARM
architecture? I reformatted the drives to ext4 and tried again. Still nothing.&lt;/p&gt;
&lt;p&gt;Turned out the Pi did not see my hub at all. I tried all possible combinations,
running the hub powered off, powered on, plugging it after the Pi was turned on,
plugging it when the Pi was off, nothing helped. Googling (actually Qwanting in
my case) showed that the Pi Zeros are very finicky when it comes to accepting USB
hubs, so it looked like my hub just wasn&#39;t the right one.&lt;/p&gt;
&lt;h3&gt;Plan B&lt;/h3&gt;
&lt;p&gt;With the hub not working, and not having another one to try out, I went with plan
B, running a single disk ZFS pool. This turned out to be super simple.&lt;/p&gt;
&lt;p&gt;First I checked the ID of the USB drive:&lt;/p&gt;
&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;ls&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-l&lt;/span&gt; /dev/disk/by-id
lrwxrwxrwx &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; root root  &lt;span class=&quot;token number&quot;&gt;9&lt;/span&gt; Jan &lt;span class=&quot;token number&quot;&gt;20&lt;/span&gt; 09:31 usb-Wilk_USB_DISK_3.0_070125F69F331078-0:0 &lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And then, as root, created the pool using the ID:&lt;/p&gt;
&lt;p&gt;&lt;i&gt; All commands below were run as root&lt;/i&gt;&lt;/p&gt;
&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&gt;&lt;/span&gt; zpool create thepool usb-Wilk_USB_DISK_3.0_070125F69F331078-0:0&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I checked where it got mounted:&lt;/p&gt;
&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&gt;&lt;/span&gt; zfs get mountpoint thepool
NAME     PROPERTY    VALUE       SOURCE
thepool  mountpoint  /thepool    default&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I could have change the default mount point, but it was fine for this test
project.&lt;/p&gt;
&lt;p&gt;To see if it actually works and can store data, I downloaded an image from JWST
to the pool folder.&lt;/p&gt;
&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;cd&lt;/span&gt; /thepool
&lt;span class=&quot;token function&quot;&gt;wget&lt;/span&gt; https://stsci-opo.org/STScI-01G8GZHGM987XCHJZ846YRHWGF.tif&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And it worked! Now it was time for the final step in making my NAS, setting up
sharing.&lt;/p&gt;
&lt;h2&gt;ZFS and NFS&lt;/h2&gt;
&lt;p&gt;First I installed the NFS server, and then I configured the ZFS pool to enable
accessing the pool over the network.&lt;/p&gt;
&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token function&quot;&gt;apt&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; nfs-kernel-server
zfs &lt;span class=&quot;token builtin class-name&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;token assign-left variable&quot;&gt;sharenfs&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;on thepool&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;On my desktop I created a share mountpoint and connected to the NFS share on the Pi:&lt;/p&gt;
&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;mkdir&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-p&lt;/span&gt; /mnt/shared-test
&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;mount&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-t&lt;/span&gt; nfs &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;ip-of-the-pi&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;:/thepool /mnt/shared-test&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So, now I have a tiny NAS that runs a ZFS single disk pool, and shares it over
NFS, and is extremely slow, has no data redundancy, and uses USB drives I would
not trust even to carry throwaway data from one room to another.&lt;/p&gt;
&lt;h2&gt;How slow is it?&lt;/h2&gt;
&lt;p&gt;I uploaded to the pool 16GB of data in files of different size, and it took 4
hours, so write speeds are around 1MB/s. Reading was only a bit faster, but not
passing 2MB/s. The bottleneck was for sure the CPU, ZFS uses a lot of processing
power, and a single 1GHz core is not a typical ZFS system.&lt;/p&gt;
&lt;p&gt;Not exactly a speed daemon, but it was a fun little weekend project. Hope you liked it!&lt;/p&gt;
&lt;p&gt;If you know from experience any USB hubs that are known to work with a Pi Zero,
please let me know.&lt;/p&gt;
&lt;p&gt;Thanks for reading!&lt;/p&gt;
</description>
      <pubDate>Sun, 21 Jan 2024 24:00:00 GMT</pubDate>
      <dc:creator>STFN</dc:creator>
      <guid>https://stfn.pl/blog/23-zero-nfs-nas/</guid>
    </item>
    <item>
      <title>My Mastodon bots, and how to easily run your own</title>
      <link>https://stfn.pl/blog/24-mastodon-bot-template/</link>
      <description>&lt;div class=&quot;future&quot;&gt;    
    March 2026 - Since writing this post a lot has changed, most of all I moved my
    bots to my own GoToSocial instance, and GTS has a slightly different API. Those
    two bots presented below can use either Mastodon or GTS, the codebase provides
    clients for both. Also, botsin.space has sadly been deleted.
&lt;/div&gt;
&lt;p&gt;One of the great aspects of Mastodon is that it very simple to run your own bot
on the platform. Setting up API credentials is a breeze, and you can start
reading or sending toots in a matter of minutes. There are very &lt;a href=&quot;https://docs.joinmastodon.org/client/intro/&quot;&gt;good docs on
how to use the API&lt;/a&gt;. There is also a
&lt;a href=&quot;https://mastodonpy.readthedocs.io/en/stable/&quot;&gt;Mastodon Python SDK&lt;/a&gt;, a wrapper
for the HTTP queries, that makes writing bots even simpler if you already know
the language.&lt;/p&gt;
&lt;p&gt;I am currently (January 2024) running two bots on Mastodon, both of them I wrote
myself and I am hosting them on my VPS, the one which is also hosting this blog.&lt;/p&gt;
&lt;h3&gt;Astrobin Image Of The Day&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://fedi.stfn.pl/@astrobin_iotd&quot;&gt;Astrobin Image Of The Day&lt;/a&gt; is a bot that
every afternoon posts the Image Of The Day from Astrobin (duh). Astrobin is an
online gallery for astrophotographers to showcase their work, and every day
there is an Image Of The Day chosen by a selected jury. Using Python and
BeautifulSoup I am parsing the IOTD page, extracting the url and some metadata,
and composing a toot with a link to the prized image. This bot has been running
for six months now and is slowly building a group of followers. The source code
for it is available at &lt;a href=&quot;https://codeberg.org/stfn/mastodon-astrobin-iotd&quot;&gt;my Codeberg.org
profile&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Energetyka Polska&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://fedi.stfn.pl/@energetykapolska&quot;&gt;Energetyka Polska&lt;/a&gt; (Polish Energy
Industry) is my new bot, deployed only a few days ago. It posts Polish-specific
data, so it&#39;s in Polish. Every two hours it pulls data from &lt;a href=&quot;https://app.electricitymaps.com/map&quot;&gt;Electricity
Maps&lt;/a&gt; API and publishes stats about the
state of electricity production in Poland, which types of power plants produce
how much electricity, and what is the percentage of renewables and low emission
sources in the Polish energy mix. Electricity Maps&#39; API is very simple to use,
and with posting twelve times a day, I am well within their free tier. The
source code for it is, again, available &lt;a href=&quot;https://codeberg.org/stfn/mastodon-carbon-intensity-bot&quot;&gt;at
Codeberg.org&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Running your own Mastodon bot&lt;/h2&gt;
&lt;p&gt;Why would you want to run a bot? It&#39;s a nice, short project, good way to
practice a programming language of your choice, and allows to show your work to
the world without doing complicated deployments, the heavy lifting being handled
by the Fediverse.&lt;/p&gt;
&lt;p&gt;First a word of caution: When considering creating a bot, please make it for
something good, to share interesting data, to alert about important stuff, to
make people&#39;s life better. OK? Thanks! Don&#39;t be a spammer.&lt;/p&gt;
&lt;p&gt;To make creating bots even easier, I have created a template project in Python
that you can use. It&#39;s available &lt;a href=&quot;https://codeberg.org/stfn/mastodon-bot-template&quot;&gt;here at my Codeberg
repo&lt;/a&gt;. Feel free to fork it, or
just download, amend to your needs and run.&lt;/p&gt;
&lt;p&gt;I believe the README file says everything required to run and deploy the bot,
but if something is missing, please let me know. Running the bot requires only
basic Python and Linux skills, and should be of no problem for anyone who ever
touched the terminal.&lt;/p&gt;
&lt;p&gt;One thing to consider when deploying a bot is choosing the instance for it. There
are instances dedicated for bots, like &lt;a href=&quot;https://botsin.space/home&quot;&gt;botsin.space&lt;/a&gt;, or you can
choose a generic one. Please, however, check each time the instance&#39;s rules
whether it allows hosting bots, and what are its rules regarding acceptable
types of content.&lt;/p&gt;
&lt;p&gt;Next you will need a place for it to run. It kind be any type of Linux* machine,
even a Pi Zero, or an ancient Thinkpad will do. Basically anything that can run
Python and cron. Deployment instructions, again, can be found in the repo&#39;s
README file.&lt;/p&gt;
&lt;p&gt;* Windows or Mac, probably could also work, but come on, who uses them? Not me.&lt;/p&gt;
&lt;p&gt;If you would like to contribute to the repo, hit me up, we can make it a group
project!&lt;/p&gt;
&lt;p&gt;And that&#39;s it! If you are going to use the template for your own bot, it would
be awesome if you could tag me somewhere, thanks!&lt;/p&gt;
</description>
      <pubDate>Wed, 24 Jan 2024 24:00:00 GMT</pubDate>
      <dc:creator>STFN</dc:creator>
      <guid>https://stfn.pl/blog/24-mastodon-bot-template/</guid>
    </item>
    <item>
      <title>The time came to replace my headphones</title>
      <link>https://stfn.pl/blog/25-edifier-x3s-earbuds-review/</link>
      <description>&lt;p&gt;I bought new small headphones. My previous ones were Huawei AM61 which I bought
in 2018. I try to use any electronic device I buy to the fullest and as long as
possible, but they were getting long in the tooth, with the rubber parts falling
off. The cable was getting in the way in thick clothing, and the Huaweis were
the only portable device that I had with a micro-USB cable, which meant I had to
remember about yet another cable when traveling. I lost the rubber earcup a few
days ago and I had enough. And so I searched for a replacement.&lt;/p&gt;
&lt;p&gt;I had very simple criteria for new headphones:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;small so that they can fit under a beanie in winter, and so that I can take
them anywhere without reducing my backpack capacity&lt;/li&gt;
&lt;li&gt;sound quality good enough to listen to podcasts&lt;/li&gt;
&lt;li&gt;charge via USB-C cable as my laptop, phone and powerbank&lt;/li&gt;
&lt;li&gt;cheap so that I won&#39;t be too sad when I lose or drown them&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As you can see those criteria were basic, as I already have good, large
headphones with ANC for work and leisure indoors (Sony WH1000XM3). The new ones
were meant to just be something I can throw in my backpack or my pocket anytime
and allow me to listen to stuff and isolate from the environment on the train of
on a walk.&lt;/p&gt;
&lt;p&gt;I also decided I want to try out separate earbuds, AirPods style, to see if being
totally cable free will be something I like (spoiler alert: I like it very much).&lt;/p&gt;
&lt;p&gt;After doing some searching, reading and comparing I went with &lt;a href=&quot;https://www.edifier.com/global/p/true-wireless-earbuds/x3s&quot;&gt;Edifier X3s&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/25/headphones.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/25/headphones.jpg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Why Edifier X3s&lt;/h2&gt;
&lt;p&gt;The answer is short: they fit all of the criteria listed above and they are the
cheapest earbuds from a brand I somewhat recognize.&lt;/p&gt;
&lt;p&gt;I paid for them 99PLN (~22EUR, 25USD) with free shipping on Allegro, a Polish
Amazon equivalent, and with my experience so far I can say that for the price
they are almost perfect, with minor issues.&lt;/p&gt;
&lt;p&gt;I&#39;ve been testing them for two days now, mostly walking and listening to
podcasts, with occasional music and a call from my mum.&lt;/p&gt;
&lt;h3&gt;Pros&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;They just work, pairing was super fast&lt;/li&gt;
&lt;li&gt;Sound quality for podcasts is very good, the sound is clean and crisp&lt;/li&gt;
&lt;li&gt;I had no problem having a call using them, even though they were covered under
a beanie, the caller had no problem understanding me&lt;/li&gt;
&lt;li&gt;USB-C!&lt;/li&gt;
&lt;li&gt;Cheap&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Cons&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;While the earbuds themselves feel sturdy and have a nice weight to them, the
case feels super cheap, but hey, you get what you pay for&lt;/li&gt;
&lt;li&gt;It is not easy to pull them out of the case, there is no good handle on the
buds and the plastic is slippery, this I think is the biggest con so far&lt;/li&gt;
&lt;li&gt;I wish there was a way to temporarily lock the touch functionality on them so
that I can handle the earbuds and not change track/volume/whatever by mistake&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Summary&lt;/h3&gt;
&lt;p&gt;If you are looking for basic earbuds that just do their job, then those will be
good for you. I&#39;m happy and would recommend them.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Of course, this is not a sponsored article, I bought them with my own money
after doing some searching myself.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Thanks for reading!&lt;/p&gt;
</description>
      <pubDate>Wed, 07 Feb 2024 24:00:00 GMT</pubDate>
      <dc:creator>STFN</dc:creator>
      <guid>https://stfn.pl/blog/25-edifier-x3s-earbuds-review/</guid>
    </item>
    <item>
      <title>the social network</title>
      <link>https://stfn.pl/blog/26-my-story-of-facebook/</link>
      <description>&lt;p&gt;So I&#39;ve seen The Social Network again. The last time I saw it was back in 2010
just after the premiere.&lt;/p&gt;
&lt;p&gt;I feel this is still a great movie that did not age one bit, on the contrary, it
became even better and more up-to-date with all that we know and have
experienced in the year 2024.&lt;/p&gt;
&lt;p&gt;From the cinematic point of view the movie is beautiful, with those shots, those
colours, that montage. Atticus&#39; and Trent&#39;s soundtrack is a cherry on top,
highlighting what happens visually. The dialogues, the tempo, the back and
forths between Mark and the rest are oh so good.&lt;/p&gt;
&lt;p&gt;But that&#39;s not what I wanted to talk about.&lt;/p&gt;
&lt;p&gt;Watching that movie today in 2024 gave me much food for thought than in 2010.
Partly because I am older, partly because I am now a web developer, and partly
because Facebook is now 20 years old, and we have all seen its rise and fall.&lt;/p&gt;
&lt;p&gt;My story of using Facebook is probably similar to the stories of many of my
peers. I&#39;ve joined Facebook in the summer of 2008, between my first and second
year at the university. The fact that I remember this date already says
something. Around 2008 was the date that Facebook was gaining traction in
general in Poland, and it was doing so on the smouldering ashes of other, older
Polish platforms of similar kind, like Grono.&lt;/p&gt;
&lt;p&gt;At that time Facebook was the cool thing at I was using it daily to connect with
my friends. I remember updating statuses, posting tons of photos from parties of
even simple walks. Facebook was used to coordinate plans even before there was
Events or even Messenger. Poking was a thing. You people remember poking? And
that simple, squarish interface? And the chronological timeline? And liking
stupid pages like &amp;quot;putting water on your toothbrush&amp;quot; or &amp;quot;it rough&amp;quot;. And the
facebook quizzes? The way FB was used back then was immortalized in &lt;a href=&quot;https://www.youtube.com/watch?v=kadvpOLgQt0&quot;&gt;this epic
Youtube video made by some random
guy&lt;/a&gt; who later became the most
popular Polish rapper. Adding someone on Facebook was an important thing.&lt;/p&gt;
&lt;p&gt;I have been using Facebook this way until somewhere around 2013? More or less. I
finished University, I moved to other networks, mostly instagram and tumblr.
People stopped posting personal stuff. Organic growth of fanpages was cut, the
timeline became algorithmic, it got filled with ads and sponsored content.&lt;/p&gt;
&lt;p&gt;That was the first total enshittification of a platform that I have encountered.
And then came the Cambridge Analytica scandal. And accusation of tampering with
elections outcomes. And Facebook just being a global playground for spreading
hideous right wing propaganda.&lt;/p&gt;
&lt;p&gt;At some point the situation arrived to what it is today. When I open Facebook
today maybe 1% of the content I see is coming from people I know, the rest is
just random pointless garbage. Face, as we used to call it, became an empty husk
of itself, with Zuck moving to the Metaverse and its VR shitshow.&lt;/p&gt;
&lt;p&gt;This post does not have a thesis or a moral. It&#39;s just a reminiscence of a time
in my life and the impact of one specific social network on it.&lt;/p&gt;
&lt;p&gt;I can say with full certainty that I grew up and came of age on the Internet,
and the period of Facebook was an important part of my Web upbringing. As
someone said, those were the days, and now there are no days.&lt;/p&gt;
&lt;p&gt;In the second scene of The Social Network, Zuck opens his LiveJournal to blog
about his heartbreak. With the fall of Facebook and the rise of decentralized
networks like Mastodon and rebirth of blogging, I feel that life is writing the
new final scene of TSN, with the Internet coming full circle, from blogs to
Facebook, to blogs. Just like this one. And all the ones I follow.&lt;/p&gt;
&lt;p&gt;Thanks for reading!&lt;/p&gt;
</description>
      <pubDate>Sun, 18 Feb 2024 24:00:00 GMT</pubDate>
      <dc:creator>STFN</dc:creator>
      <guid>https://stfn.pl/blog/26-my-story-of-facebook/</guid>
    </item>
    <item>
      <title>I gave my first public talk on Python</title>
      <link>https://stfn.pl/blog/27-my-first-talk-micropython/</link>
      <description>&lt;p&gt;On the 29th of February my employer organized a Python meetup, PyRa2024, at
their office in Poznań, and I was encouraged by a few colleagues to do a talk
there. I wasn&#39;t sure what to talk about, I don&#39;t feel like have anything
interesting to say about Python, even though I work with it every day. But later
, is struck me: I can talk about MicroPython! It will be fresh, I have not
heard about anyone else talking about it, and I already have the content ready, I
could present my projects that I have been writing about on this blog.&lt;/p&gt;
&lt;p&gt;And so my presentation &amp;quot;MicroPython - how to start&amp;quot; was born. I talked about how to
make a weather station from a Rapsberry Pi Pico, basically the content of these blog posts:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn.pl/blog/02-pico-weather-station/&quot;&gt;Raspberry Pi Pico Weather Station - part I&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn.pl/blog/04-pico-weather-station2/&quot;&gt;Raspberry Pi Pico Weather Station - part II&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn.pl/blog/06-pico-aa-batteries/&quot;&gt;Powering Raspberry Pi Pico with AA batteries (Weather Station Part III)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I was stressing about the whole thing for weeks before, but when I went up to
the stage, I just started talking, and the whole thing went smoothly, even when
at one point Thonny decided to not work as intended, something that never had
happened to me before. The beauty of Murphy&#39;s law and live coding.&lt;/p&gt;
&lt;p&gt;I had a Pico with me, and I spent most of the talk running code from it,
even showing how to do a MQTT integration with a broker on my laptop.&lt;/p&gt;
&lt;p&gt;After the talk I got a lot of questions from the very kind audience. The
questions were so good and in-depth, that for an embarrassingly high percentage
of them my answer was &amp;quot;I don&#39;t know such details&amp;quot; :D And the cherry on top was
this one person who came up to me at the end and said that they came to the
meetup specifically for my presentation. If you are reading this, thanks again
for coming and for the kinds words &amp;lt;3&lt;/p&gt;
&lt;p&gt;BTW, the other speaker, Jacek Kołodziej, had a very interesting talk about
Python&#39;s GIL, &lt;a href=&quot;https://kolodziejj.info/talks/gil/&quot;&gt;check it out!&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/27/mesml.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/27/mesml.jpg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;This is me showing a bunch of cables with a Pico
somewhere along it, the pic taken from the &lt;a href=&quot;https://stfn.pl/blog/22-pico-battery-level/&quot;&gt;How to monitor 12V battery
charge with a Raspberry Pi Pico&lt;/a&gt;
blog post. Thank you Karolina for taking the
photo and for the support from the first row &amp;lt;3 &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;So that was my first talk, and let&#39;s hope it won&#39;t be the last. Thanks for reading!&lt;/p&gt;
</description>
      <pubDate>Sun, 03 Mar 2024 24:00:00 GMT</pubDate>
      <dc:creator>STFN</dc:creator>
      <guid>https://stfn.pl/blog/27-my-first-talk-micropython/</guid>
    </item>
    <item>
      <title>My first steps in Meshtastic</title>
      <link>https://stfn.pl/blog/28-intro-to-meshtastic/</link>
      <description>&lt;p&gt;This post is a log of my initial experiences with Meshtastic. It it also meant
to be a compilation of the knowledge about Meshtastic I have gathered so far. I
hope it will be beneficial for others who want to join the Meshtastic
club/cult/family, but please bear in mind that it is in no way a complete
compendium. If you see any errors, please let me know. I hope to write another
post once I know more.&lt;/p&gt;
&lt;p&gt;For some time now I&#39;ve been drawn to radio communications. There are many
reasons why it interests me, among them being its romantic, old-school charm,
its basis on electronics and physics, its strong DIY culture, and independence
from the Internet and corporate, commercial environment. Ham radio is a
fascinating part of the alternative, off-grid, survivalist, I could even use the
word &amp;quot;punk&amp;quot;, culture which has been very close to me in the last years.&lt;/p&gt;
&lt;p&gt;I don&#39;t have a radio license yet, so apart from the doing a lot of learning, I
am investigating possible ways to engage myself in radio comms that are
available to unlicensed individuals. And that&#39;s how I came across Meshtastic.&lt;/p&gt;
&lt;h2&gt;What is Meshtastic?&lt;/h2&gt;
&lt;p&gt;It took me a while to understand what exactly is Meshtastic, so I&#39;ll try to
summarize it here in the clearest way possible.&lt;/p&gt;
&lt;p&gt;Meshtastic is a project that allows text communication, on a certain level you
could compare it Facebook Messenger or IRC, or Gadu-Gadu if you come from my
country.&lt;/p&gt;
&lt;p&gt;In contrast to FB Messenger, it does not use the Internet*, but radio waves.
Therefore to be a part of the Meshtastic network, a special piece of equipment
is required to send and receive messages. That piece of equipment is basically a
microcontroller with a small, low power radio transmitter. There are many
different devices that support Meshtastic, some can be used standalone as they
have a screen and a keyboard, but most need to be paired with a phone or
other device, that will provide the interface to send and receive messages.
Example models of such devices are the LILYGO ESP32, Heltec V3, or the RAK
Wisblock.&lt;/p&gt;
&lt;p&gt;&lt;small&gt;* it can but let&#39;s ignore it for now.&lt;/small&gt;&lt;/p&gt;
&lt;p&gt;Meshtastic communication network has channels just like IRC. When you start your
Meshtastic device for the first time, it will be configured to listen to the
default channel, it is the same channel for everyone. You can also create your
own channels and configure two or more devices to talk privately using them. The
communication is encrypted.&lt;/p&gt;
&lt;p&gt;Meshtastic is a peer to peer network. There are no central servers. Every device
works as a repeater, meaning that it forwards any message it receives. Messages
have a limited lifespan, they can only travel through several devices (hops)
before they expire. This makes the network resilient, there are no single points
of failure as long as there are enough devices to continue passing messages. Meshtastic
devices can be left on their own in remote locations and they will automatically
work as nodes, forwarding any message they receive.&lt;/p&gt;
&lt;p&gt;Meshtastic is based on the LoRa radio communication system. LoRa is a LOng RAnge
low power system that trades range and power for bandwidth. That&#39;s why it only
supports text, no voice or media.&lt;/p&gt;
&lt;p&gt;Meshtastic can be used without a radio license because it uses parts of the
frequency spectrum that fall under the ISM (industrial, scientific, and medical)
laws. Such frequencies can be used by companies and individuals that do not have
a radio license, however there are limitations in the transmit power and the
time the frequency is used by a single device. In Europe the available
frequencies are 433MHz and 868MHz, and in the USA it&#39;s 915MHz. I don&#39;t know
about the rest of the world.&lt;/p&gt;
&lt;p&gt;Further reading:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://meshtastic.org/docs/introduction/%3E&quot;&gt;meshtastic.org/docs/introduction/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/LoRa&quot;&gt;en.wikipedia.org/wiki/LoRa&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;My beginnings with Meshtastic&lt;/h2&gt;
&lt;p&gt;I don&#39;t remember where I first came across Meshtastic. I suspect it might be
Mastodon. At first it was very confusing for me what Meshtastic is what is
provides, so I did some more digging. And that&#39;s one of the reasons I am writing
this blog post, so that people have it easier than me. One of the first sources
of information I came across was the Polish Meshtastic site
&lt;a href=&quot;https://lora.waw.pl/&quot;&gt;lora.waw.pl&lt;/a&gt;. And well, technically it&#39;s ok, but the
&amp;quot;promotional materials&amp;quot; that its author published on Youtube appalled me. A
bunch of suggestive images cobbled together, with audio commentary going from
one conspiracy theory to the next, with strong vibes of &amp;quot;new world order&amp;quot;,
&amp;quot;covid was a scam&amp;quot; and antivaccines. Don&#39;t watch it, don&#39;t waste your time. It
pains me that on the Venn diagram of lifestyles, certain off-grid people are
sometimes close to the tinfoil hat lunatics.&lt;/p&gt;
&lt;p&gt;Anyway, having more technical knowledge I went to buy my first LoRa Meshtastic
device. Browsing through available options, I went with a cheap solution, and
ordered a LILYGO LoRa32 ESP32, 433MHz from Aliexpress. I went with the 433MHz
version because I read someone&#39;s comment on a Facebook Meshtastic Poland group,
that it works better in the countryside where I live.&lt;/p&gt;
&lt;p&gt;You can also order it straight from the manufacturer&#39;s site: &lt;a href=&quot;https://www.lilygo.cc/products/lora3&quot;&gt;LILYGO LoRa32
V2.1_1.6&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/28/aliexpress_lora.png&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/28/aliexpress_lora.png&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;Screenshot of an Aliexpress listing of the LILYGO ESP32
LoRa&lt;/span&gt;&lt;/p&gt;
&lt;h2&gt;First Tests&lt;/h2&gt;
&lt;p&gt;The device arrived in about two weeks. It came in a plastic case, with a small
antenna and PH2 cable to solder a battery. The provided antenna is around 5cm in
length, and from what I read later on, it has a range in meters more than
anything else.&lt;/p&gt;
&lt;p&gt;I connected the antenna to the board (you cannot power on the without the
antenna attached, it will break!) and connected it to my PC with a microUSB
cable. I flashed it with Meshtastic software using the &lt;a class=&quot;dark:text-white&quot; href=&quot;https://flasher.meshtastic.org/&quot;&gt;flasher.meshtastic.org/&lt;/a&gt; site, I chose
the &lt;i&gt;T-LoRa V2.1-1.6&lt;/i&gt; option. While it was flashing, I installed the
Meshtastic app on my Android Fairphone 4. When the flashing was done, I
unplugged the radio from my PC and connected it to a power bank. The pairing of
the device with the Android app was straightforward, it works as any other
Bluetooth pairing.&lt;/p&gt;
&lt;p&gt;Of course it did not find any nodes when I took it outside in the small town
where I live. I did not expect anyone else here uses Meshtastic. So I went to
a hill that overlooks the nearby city of Poznań (~600k inhabitants) and tried my
luck there. And... nothing. No nodes.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/28/morasko_433.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/28/morasko_433.jpg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;My 433MHz LILYGO hanging from a bush on a hill, trying
to find some friends.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;I decided then that the next course of action would be to get a better antenna.
I ordered a much larger antenna dedicated to the 433MHz frequency. It arrived
much faster, as I ordered it from a Polish shop.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/28/antenna433.png&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/28/antenna433.png&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;Screenshot of an KONEKTOR (local Polish hamradio shop)
433MHz antenna listing.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;That still did not give me any contacts around Poznań. I went back to the
drawing board, did much more reading, especially on the &lt;a href=&quot;https://www.facebook.com/groups/730536684339042&quot;&gt;Meshtastic global FB
group&lt;/a&gt;, and it turned out, that
here in Europe, 868MHz is a much more popular band. And it is also better from a
technical point of view, as the 433MHz has much stricter restrictions when it
comes to transmit power. So with a heavy sigh, I ordered another LILYGO, this
time in 868MHz. I also ordered an 868MHz antenna to match. I was telling myself
that despite all that I still spent next to nothing in comparison to my
astrophotography equipment. The new radio arrived after two weeks, just a few
days ago.&lt;/p&gt;
&lt;h2&gt;Current situation&lt;/h2&gt;
&lt;p&gt;Last weekend I had some business to do in Poznań, so I took my 868MHz LILYGO
together with me. I wanted to do some walking around the city centre to try to
get some contacts. Sadly, my walk was cut short when the nice weather turned
into freezing rain, and let&#39;s just say I did not have the clothes for the
occasion. I went to the train station to go home. I had my radio on the whole
time, as well as during the whole train trip, but still I did not find any
nodes.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/28/poznan.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/28/poznan.jpg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;My 868MHz LILYGO looking for other nodes in the center of Poznań.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Right now my plan is to take both my 433MHz and 868MHz radios with me and try
again making contact from the hill overlooking the whole city. If that does not
work out, I&#39;ll again do a walk through Poznań, this time much longer, and hope I
will make a contact at last.&lt;/p&gt;
&lt;p&gt;I think I&#39;ll also buy another 868MHz node, this time from RAK WisBlock to do
some more testing, and maybe try out connecting sensors to nodes.&lt;/p&gt;
&lt;p&gt;From all my hobbies, this has been the loneliest so far, even though it&#39;s all
about communication :D&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/28/lilygoes.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/28/lilygoes.jpg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;My two LILYGO radios&lt;/span&gt;&lt;/p&gt;
&lt;h2&gt;Other stuff&lt;/h2&gt;
&lt;h3&gt;Case&lt;/h3&gt;
&lt;p&gt;A case would be a good idea, carrying a bare PCB with an antenna sticking from
it is not the most ergonomic solution. I found a &lt;a href=&quot;https://www.thingiverse.com/thing:5330732&quot;&gt;case design on
Thingiverse&lt;/a&gt; that fits my LILYGO, and
I&#39;m looking for someone in my town to print it out for me. Will update once it
is printed.&lt;/p&gt;
&lt;h3&gt;How to make your Meshtastic node visible on the map&lt;/h3&gt;
&lt;p&gt;There is a &lt;a href=&quot;https://meshmap.net/&quot;&gt;worldwide map of nodes&lt;/a&gt; available, but making
your device visible on it is not enabled by default, and takes some work to do.
It is not simple and took me some time to understand it, so I&#39;m sharing with you
the needed steps. Remember that every time you change the config, the device
restarts and you need to reconnect to it.&lt;/p&gt;
&lt;p&gt;I assume you have a working Meshtastic device connected via Bluetooth to your
mobile phone app.&lt;/p&gt;
&lt;p&gt;In Radio Configuration in the Network tab set up the WiFi network and connect it
to WiFi. In the radio list (last tab of the main screen) you should now see an
IP address of the radio to connect it. You will no longer be able to connect to
it vai Bluetooth.&lt;/p&gt;
&lt;p&gt;In the Location tab set the location. I prefer to manually set a location, and
set it to the town centre for privacy reasons.&lt;/p&gt;
&lt;p&gt;In the MQTT tab, enable MQTT. Do not change the default MQTT URL, login and
password.&lt;/p&gt;
&lt;p&gt;In the Channels tab, tap on the default LongFast channel, and in the popup
enable Uplink enabled.&lt;/p&gt;
&lt;p&gt;In the LoRa tab, deselect Ignore MQTT.&lt;/p&gt;
&lt;p&gt;Within a few minutes, your device should become visible on the map.&lt;/p&gt;
&lt;p&gt;The downside is that your device will now use not only radio waves, but also the
web to see other devices. This might sound like an advantage, but in my opinion
kills the main reason for using Meshtastic: being independent from the Internet.&lt;/p&gt;
&lt;p&gt;And that&#39;s it for now. A lot of things I still don&#39;t know about Meshtastic, but
some basis I already have, and I always enjoy sharing my knowledge. I will be
writing more on radio communications once I get more experienced in the topic.
In the meantime, thanks for reading, and please, please give me feedback on what
I wrote and most of all, point out any errors I have made.&lt;/p&gt;
&lt;p&gt;Special thanks to &lt;a href=&quot;https://circumstances.run/@Szescstopni&quot;&gt;Szescstopni&lt;/a&gt; and
Piotr Sikora for them sharing their knowledge
with me on LoRa.&lt;/p&gt;
</description>
      <pubDate>Mon, 18 Mar 2024 24:00:00 GMT</pubDate>
      <dc:creator>STFN</dc:creator>
      <guid>https://stfn.pl/blog/28-intro-to-meshtastic/</guid>
    </item>
    <item>
      <title>Shorts 1</title>
      <link>https://stfn.pl/blog/29-shorts-1/</link>
      <description>&lt;p&gt;This is the first episode of Shorts, a post in which I share things that raised
my interest in the last few days.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://hackaday.com/2024/03/14/lithium-ion-batteries-power-your-devboards-easily/&quot;&gt;Lithium-Ion Batteries Power Your Devboards Easily&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;A great article showcasing how to power your boards whether they need 3.3V, 5V,
or either. The article also talks about batteries, TP4056s, buck/boost
converters and voltage regulators. All that tasty electronics stuff.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.best-microcontroller-projects.com/tp4056.html&quot;&gt;Using the TP4056: There&#39;s a right way, and a wrong way&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Continuing on the topic of powering stuff, an eye-opening article on how to use
the TP4056. I did not know you cannot use it to charge the battery and power
boards at the same time! At one point I was thinking to use a TP4056 to power a
Pico and charge a 18650 from solar panels, good that I went a different route
then.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=pPXw1tyE1co&quot;&gt;The simplest antenna to receive NOAA weather satellite imagery&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;A video tutorial how to make a NOAA antenna from some electrical connectors and
a bundle of wire. The video is in Polish, but everything is shown clearly, and
physics are universal, so there&#39;s no need to listen to it at all.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://aenbleidd.blogspot.com/&quot;&gt;Vitalii Koshura&#39;s blog about BOINC&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Vitalii Koshura is one of the maintainers of BOINC, and recently he started
updating his blog more regularly. Highly recommended to everyone who is
passionate about distributed computing.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://mastodon.social/@sundogplanets/112089380592964456&quot;&gt;There are more Starlinks in orbit than known exoplanets&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Prof. Sam Lawler toots a lot how Starlinks are destroying Low Earth&#39;s Orbit and
causing massive problems to astronomers and other scientists. And now there are
more Starlinks in orbit than known exoplanets.&lt;/p&gt;
&lt;p&gt;I &lt;a href=&quot;https://bookwyrm.social/user/stfn/generatednote/4102463#anchor-4102463&quot;&gt;finished
reading&lt;/a&gt;
&lt;em&gt;747: Creating the World&#39;s First Jumbo Jet and Other Adventures from a Life in
Aviation&lt;/em&gt; by Joe Sutter. It&#39;s a captivating book showing the whole life of Joe
Sutter, nicknamed &amp;quot;the father of the 747&amp;quot;. What hit me the most is the stark
contrast between his days, putting safety and engineering prowess as the main
and most important aspects of every part of work at Boeing, and how things are
going in Boeing today, with the Max&#39;s failure after failure.&lt;/p&gt;
&lt;p&gt;I started listening to the &lt;a href=&quot;https://hackaday.com/category/podcasts&quot;&gt;Hackaday
podcast&lt;/a&gt; and got instantly hooked.&lt;/p&gt;
</description>
      <pubDate>Sat, 23 Mar 2024 24:00:00 GMT</pubDate>
      <dc:creator>STFN</dc:creator>
      <guid>https://stfn.pl/blog/29-shorts-1/</guid>
    </item>
    <item>
      <title>How to make a timelapse with a Raspberry Pi</title>
      <link>https://stfn.pl/blog/30-plants-timelapse-rpi/</link>
      <description>&lt;div class=&quot;video&quot; style=&quot;position:relative;padding-top:56.25%;&quot;&gt;&lt;iframe src=&quot;https://player.mediadelivery.net/embed/626472/a76ddc0f-9116-4d26-9b64-76a6093d87eb?autoplay=false&amp;loop=false&amp;muted=false&amp;preload=true&amp;responsive=true&quot; loading=&quot;lazy&quot; style=&quot;border:0;position:absolute;top:0;height:100%;width:100%;&quot; allow=&quot;accelerometer;gyroscope;autoplay;encrypted-media;picture-in-picture;&quot; allowfullscreen=&quot;true&quot;&gt;&lt;/iframe&gt;&lt;/div&gt;
&lt;span class=&quot;image_sub&quot;&gt;12 hours in 24 seconds.&lt;/span&gt;
&lt;p&gt;Spring is here and it&#39;s time to put some plants into the soil. When the seeds
started to sprout I got this idea that it would be cool to record a timelapse to
see how plants grow.&lt;/p&gt;
&lt;p&gt;My fiancee and I, we now have several trays of seedlings on our windowsills, and
I chose the largest one as the actor in the timelapse. The largest plants are
the pumpkins, in the front there are cauliflowers, and on the right we are
waiting for celery to appear. In some time, all those plants will go into the
soil in our garden. In other trays we have more pumpkins, watermelons, a lot of
tomatoes and bell peppers.&lt;/p&gt;
&lt;p&gt;Going back to the topic of making timelapses, I assembled some hardware, did
some searching on the intertubes, and came up with the following plan of work:&lt;/p&gt;
&lt;h2&gt;Hardware and Software&lt;/h2&gt;
&lt;p&gt;First, the hardware part.&lt;/p&gt;
&lt;p&gt;To make the timelapse, I am using a Raspberry Pi 4b with the &lt;a href=&quot;https://www.raspberrypi.com/products/camera-module-3/&quot;&gt;Camera Module 3
NoIR&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The case is the &lt;a href=&quot;https://thepihut.com/products/cluster-case-for-raspberry-pi&quot;&gt;Raspberry Pi Cluster Case from
PiHut&lt;/a&gt; to which I
attached the camera with a metal l-bracket and some bolts.&lt;/p&gt;
&lt;p&gt;For the software part, I am using Pi OS Bookworm Lite 64bit and &lt;code&gt;libcamera&lt;/code&gt;
library to control the camera. For info how to do an initial configuration the
camera, refer to &lt;a href=&quot;https://www.raspberrypi.com/documentation/computers/camera_software.html&quot;&gt;libcamera
docs&lt;/a&gt;.
Batch editing of the timelapse images I am doing in
&lt;a href=&quot;https://www.darktable.org/&quot;&gt;darktable&lt;/a&gt;, and the final assembly of images into a
video happens in good old &lt;a href=&quot;https://ffmpeg.org/&quot;&gt;ffmpeg&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Running the timelapse&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/30/setup.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/30/setup.jpg&quot; alt=&quot;photo of a Raspberry Pi 4b placed on a construction made from a table, a
stool, and a cardboard box. The Pi is leaning downwards, supported from the back
by another small cardboard box. The Pi has a camera attached, the camera is
looking at a large tray of freshly sprouted plants placed in individual
pots.&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I placed the Pi on a very janky construction made from stools and cardboard
boxes. In the future I want to find a way to add a tripod mount to the base of
the cluster case, that will save me a lot of hassle and allow for very precise
positioning of the camera.&lt;/p&gt;
&lt;p&gt;The Pi is running headless as all my other Raspberries, so I sshed into it from
my PC.&lt;/p&gt;
&lt;p&gt;First I made a few test images to check the framing, to do it quickly I ran&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;rpicam-still &lt;span class=&quot;token parameter variable&quot;&gt;-n&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-o&lt;/span&gt; test.jpg &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;scp&lt;/span&gt; test.jpg &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;PC IP&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;:/home/&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;user dir&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This command takes the picture and copies it from the Pi to my computer in one
go. I will talk about the different arguments in a moment. Once the framing is
good, it&#39;s time to run the actual timelapse:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;rpicam-still &lt;span class=&quot;token parameter variable&quot;&gt;-n&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;--timelapse&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;60000&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-t&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-o&lt;/span&gt; capture_%05d.jpg&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;rpicam-still&lt;/code&gt; is the name of the app for taking still images. I know, nobody
would have guessed that. &lt;code&gt;-n&lt;/code&gt; means &amp;quot;no preview window&amp;quot;. We have no video output
from the Pi, so there is no way to a preview window. An alternative would be to
run a full desktop image of the Pi OS, and open the desktop remotely using a VNC
client. But I prefer to just ssh. &lt;code&gt;--timelapse 60000&lt;/code&gt; defines that it should be
a timelapse, 60000 is the number of microseconds between frames. 60000
microseconds is 60 seconds, so one minute between captures. &lt;code&gt;-t 0&lt;/code&gt; is for
setting how long the timelapse should take, in microseconds. 0 means
indefinitely, until you cancel the app with ctrl+c, or the Pi turns off. &lt;code&gt;-o capture_%05d.jpg&lt;/code&gt; is about the naming of the images. &lt;code&gt;%05d&lt;/code&gt; means 5 digits, so
the first captured image will be &lt;code&gt;capture_00000.jpg&lt;/code&gt;, the second
&lt;code&gt;capture_00001.jpg&lt;/code&gt; etc. If there was no digit placeholder defined, all
timelapse images would be saved to the same file.&lt;/p&gt;
&lt;p&gt;If the timelapse gets interrupted, and you want to continue it without breaking
file numeration, the initial number of the frame can be defined with:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;rpicam-still &lt;span class=&quot;token parameter variable&quot;&gt;-n&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;--timelapse&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;60000&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;--framestart&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;389&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-t&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-o&lt;/span&gt; capture_%05d.jpg &lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With that, the timelapse will start with the first image saved as &lt;code&gt;capture_00389.jpg&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;There is much more configuration available for the &lt;code&gt;rpicam&lt;/code&gt; apps, refer to the &lt;a href=&quot;https://www.raspberrypi.com/documentation/computers/camera_software.html&quot;&gt;libcamera
docs&lt;/a&gt; for a full list.&lt;/p&gt;
&lt;p&gt;And now the timelapse is running. Leave the Pi alone for a few hours, and
occasionally check if new images are being created. You can open another ssh
connection to the Pi and &lt;code&gt;ls&lt;/code&gt; the image folder.&lt;/p&gt;
&lt;p&gt;To copy the images from the Pi to my PC I use &lt;code&gt;rsync&lt;/code&gt;. It ignores the files that
are already copied to the destination, so I can run it from time to time to just
copy the new batch.&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;rsync&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;--progress&lt;/span&gt; capture* &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;PC IP&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;:/home/&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;user dir&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Preparing the files&lt;/h2&gt;
&lt;p&gt;Once the timelapse is done and the files are copied to a PC, it&#39;s time to do
some editing.&lt;/p&gt;
&lt;p&gt;I have a Camera Module that does not have the IR filter. I bought it for
astrophotography and have not used it for this means so far. So yeah, not great.
Anyway, not having an IR filter means that when shooting in daylight, the
colours are off. And so I needed to correct them. My timelapse turned out to be
730 images, so editing them one by one was not feasible. GIMP does not have a
tool to do batch editing, so I turned to darktable.&lt;/p&gt;
&lt;p&gt;Kudos for people &lt;a href=&quot;https://photo.stackexchange.com/questions/39055/how-to-batch-edit-a-collection-of-raw-files-in-darktable&quot;&gt;in this thread&lt;/a&gt; for sharing how to do it.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/30/styles.png&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/30/styles.png&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;In darktable, I imported all 730 images, and went to edit the first one. When
the colours were much better I came back to the library view, and on the right,
under styles, I selected create. This created a new style, a preset from the
edit history of that first image. Then I selected all images with ctrl+a and
double clicked on the saved style. This applied the edits to all files in one
go. Then all that was left was exporting. Again in the library view, I went to
export in the right hand bottom corner, changed the size to 1920x1080 and
exported all images to a new folder. Editing was done, and the last part that
was left was...&lt;/p&gt;
&lt;h2&gt;Creating a video&lt;/h2&gt;
&lt;p&gt;To create the video from images, I took my inspiration (meaning I copied and
pasted) from &lt;a href=&quot;https://medium.com/@sekhar.rahul/creating-a-time-lapse-video-on-the-command-line-with-ffmpeg-1a7566caf877&quot;&gt;this
article&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Here&#39;s the command to create video from images, with my edits:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;ffmpeg &lt;span class=&quot;token parameter variable&quot;&gt;-framerate&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;30&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-pattern_type&lt;/span&gt; glob &lt;span class=&quot;token parameter variable&quot;&gt;-i&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;*.jpg&quot;&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-s:v&lt;/span&gt; 1920x1080 &lt;span class=&quot;token parameter variable&quot;&gt;-c:v&lt;/span&gt; libx264 &lt;span class=&quot;token parameter variable&quot;&gt;-crf&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;17&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-pix_fmt&lt;/span&gt; yuv420p my-timelapse.mp4&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;s&gt;And that&#39;s it! The timelapse is done! Finally, to embed the video here on my
blog I used &lt;a href=&quot;https://videojs.com/getting-started&quot;&gt;video.js&lt;/a&gt;.&lt;/s&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;March 2026: As part of &lt;a href=&quot;https://stfn.pl/blog/91-cloudfront-to-bunny-cdn/&quot;&gt;my move to Bunny
CDN&lt;/a&gt;, I also switched the
video player to the one provided by &lt;a href=&quot;https://bunny.net/stream/&quot;&gt;Bunny Stream&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Thanks for reading! Spring is coming so in the upcoming weeks there probably
will be more posts about gardening and other outdoor stuff, and less about
tinkering with computers and solder. I&#39;ve been having some doubts whether this
blog should be computers-only, or should it just be about anything I do and find
worthwhile sharing. For now I am leaning towards option 2, but I would love to
hear your feedback on it. Thanks fo reading! Please leave a comment or reach out
to me via email or Mastodon.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;and random linguistic fact: in Polish those small plants that you grow on
your window sill to later put into soil are called &amp;quot;flance&amp;quot;, from German
pflanzen.&lt;/em&gt;&lt;/p&gt;
</description>
      <pubDate>Fri, 05 Apr 2024 24:00:00 GMT</pubDate>
      <dc:creator>STFN</dc:creator>
      <guid>https://stfn.pl/blog/30-plants-timelapse-rpi/</guid>
    </item>
    <item>
      <title>IS TICKLESS Pet really single use?</title>
      <link>https://stfn.pl/blog/31-tickless-battery-replacement/</link>
      <description>&lt;p&gt;No, it&#39;s not. Yes, you can apply &lt;a href=&quot;https://en.wikipedia.org/wiki/Betteridge%27s_law_of_headlines&quot;&gt;Betteridge&#39;s
law&lt;/a&gt; here.&lt;/p&gt;
&lt;p&gt;We have a large dog, a mix of a Belgian and German shepherd, and we take him on
a lot of walks in forests and fields. My fiancee bought for him an ultrasonic
ticks and fleas repellent called TICKLESS Pet. It&#39;s 30EUR / 140PLN, so not the
cheapest device. The promotional materials said that you turn it on by pulling
the plastic tab, and then it works for six months continuously. At the time I
did not pay much attention to that claim.&lt;/p&gt;
&lt;p&gt;Last week Alicja came to me and said that our dog took a bath in a stream, and
the device stopped working. The thingy has a single button, you can click and
then a green LED blinks to confirm that it&#39;s still working. That&#39;s the only
interaction with the device that you can have, you cannot turn it on or off. Now
the LED did not blink. &amp;quot;Can you do something with it?&amp;quot;. And I said I&#39;ll try.&lt;/p&gt;
&lt;h2&gt;Getting inside and bringing it back to life&lt;/h2&gt;
&lt;p&gt;First let&#39;s consult the FAQ on TICKLESS&#39;s page:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/31/faq.png&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/31/faq.png&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;Screenshot from TICKLESS&#39;s FAQ available at &lt;a href=&quot;https://tickless.com/en/faq-2/&quot;&gt;tickless.com/en/faq-2/&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;TICKLESS is a closed system&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;It&#39;s really hard to agree with that statement when the device is in a plastic
case with massive holes to let the ultrasounds out. Opening it takes a few
seconds, the case needs to be delicately pried open at the seam, any wedge or
screwdriver will work.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/31/1.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/31/1.jpg&quot; alt=&quot;The device in
it&#39;s glory. It&#39;s a bright orange tear-shaped plastic device with large holes.
You can see the logic board through the holes in the case&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;The device in it&#39;s glory. You can see the logic board through
the holes in the case&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Opening it reveals the main part: a simple logic board with a few SMD
components, some chip, an ultrasound speaker... and a CR2032 holder with a
totally standard button cell.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/31/2.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/31/2.jpg&quot; alt=&quot;The case opened and the logic board inside. The front and back of the case
lying on a desk, with the logic board between
them.&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;The case opened and the logic board inside&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/31/3.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/31/3.jpg&quot; alt=&quot;cdn.net/31/3.jpg&amp;quot;
Front side of the
logic board with the ultrasonic speaker&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;Front side
of the logic board with the ultrasonic speaker&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/31/4.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/31/4.jpg&quot; alt=&quot;Back side of the
logic board with the battery holder&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;Back side of the logic board with the battery holder&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Again, to quote the FAQ:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Can I change the battery? No!&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Why would you not be able to change the battery? It&#39;s not soldered in, it&#39;s just
press fitted into a typical cell holder that can be found in any other device,
like a TV remote control.&lt;/p&gt;
&lt;p&gt;I removed the battery, carefully cleaned the whole board with a q-tip soaked in
isopropyl alcohol. There was some surface rust from the water getting into the
&amp;quot;closed&amp;quot; device, but no connection looked to be damaged. I put the battery back
again, and the device came back to life, the LED blinked, and blinked again when
I clicked the button. The case also had no problems being assembled again, it&#39;s
also press fitted.&lt;/p&gt;
&lt;p&gt;Now the device is back on the dog&#39;s collar, and once the battery discharges, I
will just put another CR2032 in. And barring any physical damage, it should work
for another six months.&lt;/p&gt;
&lt;h2&gt;Bottom Text&lt;/h2&gt;
&lt;p&gt;I feel that this is a prime example of planned obsolescence. I know this is not
something large and expensive as a washing machine, but still, it&#39;s a piece of
electronics that can work for a long period of time, but is designed in a way to
not last long, unless one is willing to go an extra mile. It would not be a
manufacturing problem to add an easy way to open the case to simplify changing
the battery. Of course, TICKLESS offers a rechargeable version, but it&#39;s almost
twice the price.&lt;/p&gt;
&lt;p&gt;I hate single-use electronic devices, and the problem is growing. I&#39;ve seen
single use vapes (the EU will be doing something about them soon), even single
use powerbanks, and that waste of resources is just unacceptable.&lt;/p&gt;
&lt;p&gt;So yeah, just wanted to share my disdain for such practices, and maybe someone
will save some money from my findings, and a small electronic device will not go
to landfill that soon.&lt;/p&gt;
&lt;p&gt;Thanks for reading!&lt;/p&gt;
&lt;p&gt;BTW, I&#39;m thinking of finding a way to listen to the sounds it&#39;s making, I just
need to find a microphone that will hear ultrasounds. Any recommendations?&lt;/p&gt;
</description>
      <pubDate>Tue, 09 Apr 2024 24:00:00 GMT</pubDate>
      <dc:creator>STFN</dc:creator>
      <guid>https://stfn.pl/blog/31-tickless-battery-replacement/</guid>
    </item>
    <item>
      <title>Making a portable Meshtastic node</title>
      <link>https://stfn.pl/blog/32-portable-meshtastic-node/</link>
      <description>&lt;p&gt;I am continuing my journey with Meshtastic, and since &lt;a href=&quot;https://stfn.pl/blog/28-intro-to-meshtastic/&quot;&gt;my last
post&lt;/a&gt; I made some progress, yet
mostly in hardware. ~&lt;s&gt;I still did not connect to anyone over LoRa&lt;/s&gt;~*, but I
had some interesting communications over MQTT bridges. And I made a portable
node, and I am in the process of making and testing a solar-powered remote node.&lt;/p&gt;
&lt;p&gt;* scratch that, just before posting this blog post I finally made contact over LoRa at 433MHz!&lt;/p&gt;
&lt;p&gt;This is going to be a short post showcasing how I built my portable node and
what are my future plans. Maybe it will be source of inspiration for someone.&lt;/p&gt;
&lt;h2&gt;The Parts&lt;/h2&gt;
&lt;p&gt;For the Meshtastic node, I am using a &lt;a href=&quot;https://www.lilygo.cc/products/lora3&quot;&gt;LILYGO ESP32 LoRa32
V2.1_1.6&lt;/a&gt;. It comes with a JST-PH socket
to connect to a 3.7V Li-ion battery pack, and this is the powering solution I am
using for this build.&lt;/p&gt;
&lt;p&gt;Note that your device might have different power requirements, check that before
following what I did.&lt;/p&gt;
&lt;p&gt;The parts list is very short and simple:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://botland.store/cases-casings/11994-plastic-case-kradex-z125-abs-190x90x51mm-5905275015177.html&quot;&gt;A plastic
case&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://botland.store/battery-holders/16519-cell-holder-for-3x-18650-battery-without-wires-5904422378165.html&quot;&gt;An 18650 battery holder&lt;/a&gt;, connected in parallel or not connected from the
factory, more on that later.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://botland.store/antenna-cables/23325-sma-antenna-cable-25cm-male-female-sparkfun-cab-22034.html&quot;&gt;A short SMA
extender&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;USB-micro charging cable&lt;/li&gt;
&lt;li&gt;cables and short pieces of wire, I used leftover resistor legs&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You will also need the usual assortment of tools, a drill, a soldering iron, a
heat gun, a glue gun and wire &lt;s&gt;gun&lt;/s&gt; strippers.&lt;/p&gt;
&lt;h3&gt;The battery pack&lt;/h3&gt;
&lt;p&gt;When it comes to the battery holder, you can use any size that fits your needs,
a single 18650, two, three, or more. What is very important is to find one that
connects all batteries in parallel, or allows to freely wire it. More often than
not, 18650 battery holders are wired in series, and this is not what we want.
What we want is to provide the voltage of a single 18650 cell (~3.7V), and the
capacity of multiple cells. To achieve that we need to connect every cell in
parallel. Meaning that the positive side of every cell is connected to one wire
that goes to the positive connector of the LILYGO. The same happens on the
negative side, all negative cell connectors are connected with a single wire and
go to the negative connector of the radio. In result, we will have a battery
that works as if it was a single cell with a large capacity.&lt;/p&gt;
&lt;p&gt;I bought a battery holder that had no connections between individual cells, so I
soldered them:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/32/battery1.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/32/battery1.jpg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;Battery holder underside with no connections&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/32/battery2.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/32/battery2.jpg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;Connectors on one side connected&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/32/battery3.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/32/battery3.jpg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;Cable going from one side, it will connect to the
LILYGO, the same will be done on the other side&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;The cables coming from the battery pack, I soldered them to the JST-PH connector
that came with the LILYGO. Cells can be placed either way in my battery pack, so
for the time being, the polarization doesn&#39;t matter, it will matter when
inserting the cells into the holder.&lt;/p&gt;
&lt;h3&gt;The antenna and charging&lt;/h3&gt;
&lt;p&gt;As for the antenna, I got this SMA extender to allow the antenna to be outside
the case. I drilled a hole in the case with an 8mm drill (a 7mm would probably
fit better but I did not have one), and the male end of the extender through it,
secured it with the provided nut.&lt;/p&gt;
&lt;p&gt;Finally I drilled another hole with a 10mm drill, and put an IKEA micro USB
cable through it to allow charging the LoRa radio without opening the case.&lt;/p&gt;
&lt;p&gt;Now came the big part.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/32/inside.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/32/inside.jpg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;The assembled Meshtastic node&lt;/span&gt;&lt;/p&gt;
&lt;h3&gt;Assembly&lt;/h3&gt;
&lt;p&gt;The assembly was simple, I connected the antenna, then the battery pack, paying
special attention to the polarization of the cells. All have to go in in the
same direction. And the cells need to be placed in the correct orientation, with
the positive terminal connected to the positive terminal in the device&#39;s socket.&lt;/p&gt;
&lt;p&gt;Once I was sure everything was working fine, I hot glued the battery pack to the
case. I put the charging cable through the larger hole, tidied it with some cable
straps, and hot glued the whole to keep it in place, leaving a short length
dangling for easier charging.&lt;/p&gt;
&lt;p&gt;Finally I put a piece of foam inside the case to make sure things do not rattle
around when carrying, and screw in the lid.&lt;/p&gt;
&lt;p&gt;And that&#39;s basically it! Short, fun project that resulted in a Meshtastic node
that you can safely carry around, and when using three or so 18650s, will work
for a few days without charging. My case is relatively weatherproof, and safe to
carry in a backpack.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/32/outside.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/32/outside.jpg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;My node living free in the wilderness&lt;/span&gt;&lt;/p&gt;
&lt;h3&gt;Future improvements&lt;/h3&gt;
&lt;p&gt;One downside of this build is that there is no way to see the screen, but then
again, I use the Android app to interact with the radio and do all the
configuration. What I would actually add to the node in the future would be an
on/off switch to conserve battery, and maybe a cable holder to stop it from
dangling freely.&lt;/p&gt;
&lt;h2&gt;Continuing my Meshtastic journey&lt;/h2&gt;
&lt;p&gt;I am planning to take this node on a trip with me to some cities and try to make
contacts with the locals.&lt;/p&gt;
&lt;p&gt;Apart from that, I am right now building and testing a solar-powered node that
will eventually be placed on my piece of land. A blog post on it should appear
in the next few weeks.&lt;/p&gt;
&lt;p&gt;Many thanks to people of Mastodon, and
&lt;a href=&quot;https://www.facebook.com/groups/730536684339042/&quot;&gt;Meshtastic&lt;/a&gt;, and &lt;a href=&quot;https://www.facebook.com/groups/meshtasticpolska/&quot;&gt;Meshtastic
Polska&lt;/a&gt; Facebook groups for
feedback and inspiration.&lt;/p&gt;
</description>
      <pubDate>Tue, 16 Apr 2024 24:00:00 GMT</pubDate>
      <dc:creator>STFN</dc:creator>
      <guid>https://stfn.pl/blog/32-portable-meshtastic-node/</guid>
    </item>
    <item>
      <title>The Space Shuttle - how I remember it</title>
      <link>https://stfn.pl/blog/33-remembering-space-shuttle/</link>
      <description>&lt;p&gt;A few weeks ago, Scott Manley made &lt;a href=&quot;https://www.youtube.com/watch?v=AZ4UWbt6vZg&quot;&gt;a video about a nonstandard Space Shuttle
landing at the Edwards Air Force
Base&lt;/a&gt;. The video was based on &lt;a href=&quot;https://waynehale.wordpress.com/2024/03/10/putting-atlantis-at-risk/&quot;&gt;a
blog entry written by Wayne
Hale&lt;/a&gt;, a
Space Shuttle Flight Director.&lt;/p&gt;
&lt;p&gt;When I come across a new blog, I rarely read the old entries, maybe one or two
of the latest ones. As for Wayne&#39;s diary, I am in progress of reading the entire
thing. I cannot recommend his writing enough. It&#39;s captivating, technical, yet
accessible at the same time. He talks about the day-to-day work of being a
Flight Director, about the hair raising emergencies, and everything in between.
I hope he&#39;ll make a book of his memories one day.&lt;/p&gt;
&lt;p&gt;Reading his blog about the Space Shuttle made me think about my memories and
experiences with it.&lt;/p&gt;
&lt;p&gt;I remember when my dad was getting us The Internet for the first time, it was
somewhere in 1996, if I recall correctly. I was eight by then, and I don&#39;t know
why, but my first thought when I was told that we are going to have Internet at
home was that I will be able to visit sites about the Space Shuttle. Why those
in particular? I don&#39;t know, I was not a space nerd during that time, but
somehow I made a connection between the tubes and the Shuttle. Maybe because
both of them were for me insanely advanced technology? Children&#39;s minds work in
mysterious ways. Funnily enough, I don&#39;t remember actually visiting &amp;quot;those
sites&amp;quot;, but the anticipation stayed in me.&lt;/p&gt;
&lt;p&gt;Fast forward a few years, and I vividly remember watching the Columbia
disaster in real time. That also stuck in me. Watching CNN and other
English-speaking stations despite my not-so-great proficiency in that language.
My dad facepalming at Polish media reporting that the Shuttle was traveling
&amp;quot;several times the speed of light&amp;quot; during reentry. He said that he still
remembered witnessing the Challenger disaster, he was in the Army back then (in his
time, every Pole had to serve in the Army for a few months), and he and his team were
sitting around the radio listening to the news.&lt;/p&gt;
&lt;p&gt;I also remember watching Space Shuttle launches broadcasted live when they came
back to flight. I was then in high school and watched them on my first own PC,
and tried to interest my high school crush in the topic (that did not work out,
as the whole relationship thing with her).&lt;/p&gt;
&lt;p&gt;I remember reading how the Space Shuttle built the ISS, following the
construction of solar panels and trusses and living quarters.&lt;/p&gt;
&lt;p&gt;Fast forward again, it&#39;s 2011 and I am watching the finalest ever landing of the
Space Shuttle, STS-135, and I feel that an era ends.&lt;/p&gt;
&lt;p&gt;To think about it, the Shuttle was always somewhere in the background for most
of my life. My fascination with space and spaceflight would come and go through
all those years, with ebbs and flows, but it was there.&lt;/p&gt;
&lt;p&gt;Now as I am older, I understand better the full story of the Shuttle, how it was
the most advanced flying machine that the mankind has ever created, and in a way
a financial disaster for NASA, about all the compromises that had to go into
building it.&lt;/p&gt;
&lt;p&gt;And thanks to Wayne&#39;s blog I am able to relive all that, and learn even more
about the machines and people who built it and flew it and controlled the
missions.&lt;/p&gt;
&lt;p&gt;Just like Wayne is, I am now excited for Artemis. Much more for Artemis than for
Starship, as I prefer governmental institutions going to space for science,
rather than capitalists doing it for profit.&lt;/p&gt;
&lt;p&gt;Thanks for reading!&lt;/p&gt;
&lt;p&gt;P.S. It&#39;s funny, I have only two types of blog posts, the first category are the
ones I write for weeks, project updates and such, and the second one are
personal musing like this one, which I am able to finish in 30 minutes, 1 hour
tops. There is nothing in between. Any you never know when inspiration hits you.&lt;/p&gt;
</description>
      <pubDate>Tue, 23 Apr 2024 24:00:00 GMT</pubDate>
      <dc:creator>STFN</dc:creator>
      <guid>https://stfn.pl/blog/33-remembering-space-shuttle/</guid>
    </item>
    <item>
      <title>Reducing Raspberry Pi Pico W power consumption and a second attempt at using solar panels</title>
      <link>https://stfn.pl/blog/34-pico-power-consumption-solar-panels/</link>
      <description>&lt;p&gt;If you don&#39;t want to read the intro, just &lt;a href=&quot;https://stfn.pl/blog/34-pico-power-consumption-solar-panels/#solution&quot;&gt;jump straight to conclusions&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Almost a year ago I made &lt;a href=&quot;https://stfn.pl/blog/09-pico-solar-panels/&quot;&gt;my first attempt at making a solar panel powered
weather station using a Raspberry Pi
Pico&lt;/a&gt;. That attempt turned out to be
a failure, the solar panels I used were not able to prolong the the life of a
Pico in any significant way. I abandoned the project and moved into different
fields, mostly ham radio. And then came winter and any solar projects went into
total hiatus.&lt;/p&gt;
&lt;p&gt;Fast forward 10 months, and the summer is almost here, again there are long
sunny days, and I have renewed interest in solar power. What is more, I am also
looking into powering not only Pis, but also Meshtastic nodes with solar panels,
something that I will write about very soon in an upcoming blog post.&lt;/p&gt;
&lt;p&gt;And this time I am working in two directions, first I am investigating ways to
reduce Pico&#39;s power consumption, and secondly, looking into better panels to power
the Pico from the Sun.&lt;/p&gt;
&lt;p&gt;Let&#39;s first start with the usage of electricity, and the move to its production.&lt;/p&gt;
&lt;h2&gt;Measuring Raspberry Pi Pico W power consumption...&lt;/h2&gt;
&lt;p&gt;What I will be describing here applies specifically for the Raspberry Pi Pico W,
but I believe it can be also applied to other microcontrollers with Wi-Fi
connectivity. Now as I am writing it, I think I might do at some point a
comparison between a Pico W and an ESP8266 that I also have lying somewhere in a
cupboard.&lt;/p&gt;
&lt;p&gt;I am running Micropython on my Picos, however the findings here are independent
of the language used.&lt;/p&gt;
&lt;h3&gt;Current monitoring&lt;/h3&gt;
&lt;p&gt;To monitor the power consumption in real time I bought a &lt;a href=&quot;https://meters.uni-trend.com/product/ut658-series/&quot;&gt;UNI-T UT658 USB
monitor&lt;/a&gt;. With what I know
now, I would buy something else, this monitor only shows three decimal places
when it comes to amps, meaning I can only know the current (the current
current?) in tens of miliamps. Then again, it&#39;s better than nothing, and the
meter allows for prolonged measuring of total usage, so I can calculate the
average current within a period of time.&lt;/p&gt;
&lt;h3&gt;Hardware setup&lt;/h3&gt;
&lt;p&gt;In my first attempt, I used a Pico W with a BMP280 and DHT11 sensors. All three
were soldered to a development board, with an ARC connector to power the
microcontroller. You can see the pictures in the previous blog post linked at
the top. This time I went with a much simplified setup, I am using a BME280
sensors that can check pressure, humidity and temperature from a single chip.
For powering the Pico, I used the micro-USB connector for testing, and
female-to-female jumper cables for the &amp;quot;production&amp;quot; run.&lt;/p&gt;
&lt;p&gt;For testing purposes I have been powering the Pico from a standard powerbank,
with the UNI-T in between the bank and the micro-USB cable going to the Pico.&lt;/p&gt;
&lt;h3&gt;Software setup&lt;/h3&gt;
&lt;p&gt;This is the initial script that I run on the Pico. It is a simplification of what
I&#39;ve been running at the previous attempt.&lt;/p&gt;
&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; json
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; time

&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; bme280
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; machine
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; network
&lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; umqtt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;simple &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; MQTTClient

sdaPIN&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;machine&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Pin&lt;span class=&quot;token punctuation&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;
sclPIN&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;machine&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Pin&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

i2c &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; machine&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;I2C&lt;span class=&quot;token punctuation&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;sda&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;sdaPIN&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; scl&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;sclPIN&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
bme &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; bme280&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;BME280&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;i2c&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;i2c&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

led &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; machine&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Pin&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;LED&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; machine&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Pin&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;OUT&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

ssid &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;SSID&quot;&lt;/span&gt;
password &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;PASSWORD&quot;&lt;/span&gt;

wlan &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; network&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;WLAN&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;network&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;STA_IF&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
wlan&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;active&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
wlan&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;connect&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ssid&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; password&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
max_wait &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;while&lt;/span&gt; max_wait &lt;span class=&quot;token operator&quot;&gt;&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 keyword&quot;&gt;if&lt;/span&gt; wlan&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isconnected&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 keyword&quot;&gt;break&lt;/span&gt;
    max_wait &lt;span class=&quot;token operator&quot;&gt;-=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;
    time&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sleep&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;not&lt;/span&gt; wlan&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isconnected&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;
	machine&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;reset&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;while&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    led&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;on&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    time&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sleep&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    led&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;off&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    data &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; bme&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;read_compensated_data&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    payload &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 string&quot;&gt;&quot;temperature&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; data&lt;span class=&quot;token punctuation&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 operator&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&quot;pressure&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; data&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&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;256&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&quot;humidity&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; data&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&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;1024&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    qt &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; MQTTClient&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;umqtt_client&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;MQTT_BROKER_IP&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; keepalive&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3600&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    qt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;connect&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    qt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;publish&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;b&quot;weather&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; json&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;dumps&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;payload&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    qt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;disconnect&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    time&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sleep&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;60&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A full description of what this script does can be found in the previous post
(link at the top), but let&#39;s recap it here, it&#39;s very simple.&lt;/p&gt;
&lt;p&gt;First import all of the required modules. Next, initiate the I2C interface and
the BME280 environment sensor, define the pin of the internal LED.&lt;/p&gt;
&lt;p&gt;Second, connect to WiFi. If connecting to wifi timeouts, restart the Pico.&lt;/p&gt;
&lt;p&gt;Now we enter the eternal loop in which we blink the LED, ask BME280 nicely to
measure the environment, connect to the MQTT broker, send data and go to sleep
for a minute.&lt;/p&gt;
&lt;p&gt;I saved the script as &lt;code&gt;main.py&lt;/code&gt; so that it would run when the Pico was powered,
disconnected it from the PC and plugged it into a powerbank, with the UNI-T
in between. And the results were surprising to say the least:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The Pico W is using ~45mA all the time when WiFi
is on&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;45mA may not sound like a lot, but if you have a Pico running from a 3000mA
battery, you&#39;ll get 75 hours at best of runtime. And small, cheap solar panels
will not provide enough current to power the Pico and charge the batteries at the
same time. This is why my previous attempt failed.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn.pl/blog/34-pico-power-consumption-solar-panels/!%5Bx%5D(https:/stfn-pl-blog-assets.b-cdn.net/34/IMG_20240413_105529.jpg)&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/34/IMG_20240413_105529.jpg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;Testing the Pico&#39;s power usage&lt;/span&gt;&lt;/p&gt;
&lt;a name=&quot;solution&quot;&gt;
&lt;h2&gt;...And how to lower it&lt;/h2&gt;
&lt;p&gt;Time to finish that very long intro and move to the actual solution. I went to
the Interwebs to see how others solved this issue. At first I thought that
deepsleep or other low power modes would be the answer, but it turned out that
the Pico&#39;s flavour of Micropython does not support deepsleep at all, only
lightsleep. And using lightsleep did not make any significant change during my
testing. The actual solution was much simpler:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Turn off Wi-Fi&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Turns out the Wi-Fi chip is the single largest power guzzler on the whole
board, and turning it off causes the power consumption to fall dramatically.&lt;/p&gt;
&lt;p&gt;The script I am showing above handles internet connectivity in a very simple
way, the Pico connects to a wireless network once at startup and keeps the
connection up for the whole time it is powered, even when it is sleeping.
This is pointless and wasteful.&lt;/p&gt;
&lt;p&gt;And so I modified my script to turn off Wi-Fi when sleeping, and power it on
again only when sending data via MQTT.&lt;/p&gt;
&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; json
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; time

&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; bme280
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; machine
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; network
&lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; umqtt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;simple &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; MQTTClient

sdaPIN &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; machine&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Pin&lt;span class=&quot;token punctuation&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;
sclPIN &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; machine&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Pin&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

i2c &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; machine&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;I2C&lt;span class=&quot;token punctuation&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; sda&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;sdaPIN&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; scl&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;sclPIN&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
bme &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; bme280&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;BME280&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;i2c&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;i2c&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

led &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; machine&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Pin&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;LED&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; machine&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Pin&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;OUT&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

ssid &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;SSID&quot;&lt;/span&gt;
password &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;PASSWORD&quot;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    wlan &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; network&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;WLAN&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;network&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;STA_IF&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    wlan&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;active&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    wlan&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;connect&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ssid&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; password&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    max_wait &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;while&lt;/span&gt; max_wait &lt;span class=&quot;token operator&quot;&gt;&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 keyword&quot;&gt;if&lt;/span&gt; wlan&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isconnected&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 keyword&quot;&gt;break&lt;/span&gt;
        max_wait &lt;span class=&quot;token operator&quot;&gt;-=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;
        time&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sleep&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;not&lt;/span&gt; wlan&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isconnected&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;
        machine&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;reset&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    led&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;on&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    time&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sleep&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    led&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;off&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    data &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; bme&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;read_compensated_data&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    payload &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 string&quot;&gt;&quot;temperature&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; data&lt;span class=&quot;token punctuation&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 operator&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&quot;pressure&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; data&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&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;256&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&quot;humidity&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; data&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&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;1024&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;

    qt &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; MQTTClient&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;umqtt_client&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;MQTT_BROKER_IP&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; keepalive&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    qt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;connect&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;clean_session&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    qt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;publish&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;b&quot;weather&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; json&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;dumps&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;payload&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    qt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;disconnect&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    wlan&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;active&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;False&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    wlan&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;deinit&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    time&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sleep&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;60&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The difference between this version and the previous one is that I moved
connecting to WiFi to inside of the eternal loop. Every iteration begins with
the Pico connecting to WiFi. Then we do the measuring, sending data via MQTT,
and once the data is sent, the Pico disconnects from the MQTT broker, disables
the WiFi module, and the goes to sleep. After 60 seconds, the Pico wakes up,
powers on the WiFi module, connects to the wireless network, etc, etc.&lt;/p&gt;
&lt;p&gt;Running this script with the power monitor attached showed that the power
consumption is at 40-50mA only for the second or two that it takes the Pico to
connect to Wi-Fi and transmit data. During sleep the power consumption falls to
levels below what the UNI-T monitor can show, so less than 10mA.&lt;/p&gt;
&lt;p&gt;My power monitor is also capable to measure power consumption over time and
provide the result in milliampere-hours. I performed tests over two and 12 hours,
and they confirmed the massive difference betwen Wi-Fi adapter being on all the
time, and being on only for transmitting.&lt;/p&gt;
&lt;p&gt;With Wi-Fi on all the time, the average power consumption was ~45mAh per hour.
With turning Wi-Fi off when sleeping, the Pico W used ~6mAh per hour. 7 times
smaller!&lt;/p&gt;
&lt;p&gt;6mAh is a much better result, it is now much simpler to power the Pico W with
solar panels, even the small ones.&lt;/p&gt;
&lt;h2&gt;Issues with MQTT&lt;/h2&gt;
&lt;p&gt;However, a new problem arose. With the Pico&#39;s continous establishing and ending
connection with Wi-Fi and the MQTT broker, there is a now much higher risk of
something going wrong at every loop. And the MQTT library that I am using is not
handling connection errors well. During testing I&#39;ve seen many times that after
an hour or two the Pico would just freeze with Wi-Fi on, not sending anything to
the broker.&lt;/p&gt;
&lt;p&gt;After careful debugging (adding a ton of logging to a file) the culprit was
found. Sometimes the MQTT connect() method would stop, and there was no timeout
implemented by the library. Any issue with connecting to the MQTT broker would
just freeze the Pico, and that would be the end, until it was rebooted manually.
Not the best behavior for something that is meant to run independently for a
prolonged period of time.&lt;/p&gt;
&lt;p&gt;As the MQTT library I am using
&lt;a href=&quot;https://pypi.org/project/micropython-umqtt.simple/&quot;&gt;micropython-umqtt.simple&lt;/a&gt;.
I installed it using Thonny&#39;s package manager. Thonny also allows to easily edit
the files of the installed libraries, they can be found on the Pico in the lib/
subfolder.&lt;/p&gt;
&lt;p&gt;After going through the documentation of Micropython&#39;s &lt;code&gt;socket&lt;/code&gt; module, the
module that the MQTT library is using underneath, I found that it does support
handling timeouts. And so I added a timeout of 10 seconds, and modified the
library&#39;s file to reboot the Pi when the connection cannot be established within
a given time. A crude solution but it works. Below I am providing the code for
the updated &lt;code&gt;connect()&lt;/code&gt; method.&lt;/p&gt;
&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;connect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;self&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; clean_session&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;True&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;
	self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sock &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; socket&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;socket&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
	self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sock&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;settimeout&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;10.0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
	addr &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; socket&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;getaddrinfo&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;server&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;port&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 number&quot;&gt;0&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 operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
		self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sock&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;connect&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;addr&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;except&lt;/span&gt; OSError &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
		log&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-interpolation&quot;&gt;&lt;span class=&quot;token string&quot;&gt;f&quot;connecting timeouted with error &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;e&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
		machine&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;reset&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;except&lt;/span&gt; Exception &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
		log&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-interpolation&quot;&gt;&lt;span class=&quot;token string&quot;&gt;f&quot;something totally broke! &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;e&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
		machine&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;reset&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;The rest of the &lt;code&gt;connect()&lt;/code&gt; stayed the same. Also at the top of the file I added
a simple error logger to have a persistent log of errors that I can check later.&lt;/p&gt;
&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;txt&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;with&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;error_log&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;a&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; logfile&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        logfile&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;write&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;txt&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        logfile&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;write&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&#92;n&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This change has solved my issues with the Pi freezing. If you have a better
solution, I will be very happy to hear your feedback!&lt;/p&gt;
&lt;h2&gt;Solar panels - attempt #2&lt;/h2&gt;
&lt;p&gt;Now that the power consumption issue is resolved, it&#39;s time to go back to
solving the power generation and management. The main difference between this
and the previous build, is that I used much larger and better solar panels.&lt;/p&gt;
&lt;p&gt;I bought two &amp;quot;3.5W 6V&amp;quot; solar panels from a Polish electronics shop. As the
previous ones, they do not have a manufacturer label, but the overall build
quality feels much better. I connected them in parallel using the additional
connectors at the back and some 22AWG cables I bought previously. I also added a
backplane from some sort of a plumbing bracket I found at a local hardware store.
The bracket has a lot of holes for different connectors, I used the middle one
to add an M5 screw. The bracket was then hot-glued to the back of the panels.
The whole assembly is sturdy and universal enough to be mounted anywhere.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/34/IMG_20240506_132701.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/34/IMG_20240506_132701.jpg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;Solar panels assembly&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Theoretically those two panels running in parallel should provide more than an
1 Amp of current (7W at 6V), but the best I got was around 300mA in full sunlight.
Not great, but enough to both power the Pico and recharge the batteries after a
night.&lt;/p&gt;
&lt;p&gt;I reused the solar power manager from the previous iteration, it&#39;s still the
Solar Power Manager 5V v1.1 from DFRobot. It&#39;s small and just works.&lt;/p&gt;
&lt;p&gt;The battery storage is the same as in the &lt;a href=&quot;https://stfn.pl/blog/32-portable-meshtastic-node/&quot;&gt;Portable Meshtastic Node
project&lt;/a&gt; I did a few weeks
ago. Please consult that blog post for detailed instructions. It consists of
three 18650 batteries connected in parallel. Together they provide around
7500mAh. I used a JST-PH 2.54mm connector to connect it to the solar power
manager.&lt;/p&gt;
&lt;p&gt;And that&#39;s the full parts&#39; list. I connected the solar panels to the Solar In
connector (I wish there was another JST socket for them), the batteries to the
Battery In socket, and the Pico W using the 5V and ground pins. Nothing
groundbreaking here.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/34/IMG_20240506_172722.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/34/IMG_20240506_172722.jpg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;Everything connected, placed in my not-that-clean balcony&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/34/IMG_20240506_173151.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/34/IMG_20240506_173151.jpg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;Close-up on the Pico with the BME280 environment sensor&lt;/span&gt;&lt;/p&gt;
&lt;h2&gt;Current situation&lt;/h2&gt;
&lt;p&gt;I am still testing the performance of the new build, and so far I am impressed.
I started the test with the batteries fully charged using a standalone charger and
during the first day, the solar panels have been able to keep the full charge
continuously. In the morning the batteries were depleted a bit, but the panels
were able to recharge them in a few hours. Despite the fact that the whole day
was cloudy and the panels are sitting on a windowsill pointing towards the West.
I am sure that installed outside and pointed South, the batteries would get
recharged way before noon.&lt;/p&gt;
&lt;p&gt;Update: After 10 days of running, the battery voltage was around 4.10V at their
lowest level, and they are recharging to full charge every day.&lt;/p&gt;
&lt;p&gt;I am convinced that with the current setup, the Pi Pico W can run indefinitely,
or well, at least until late Autumn, when the days will be much shorter. But
we&#39;ll measure that bridge when we get there.&lt;/p&gt;
&lt;p&gt;The only missing step is a case, I need to work out something that will protect
the Pico and the batteries, but at the same time provide free airflow for the
sensor suite.&lt;/p&gt;
&lt;p&gt;And that&#39;s it! I am very happy with the outcome of this project. I have been
talking only about the Pi Pico W here, but the lessons learned are universal,
and can be applied to any device running from 5V or similar voltage. The next
step for me will be to apply them to an off-grid node of Meshtastic. This
project is already underway, and soon will be described here in detail.&lt;/p&gt;
&lt;p&gt;Previous parts of my journey making a weather station with a Raspberry Pi Pico:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn.pl/blog/02-pico-weather-station/&quot;&gt;Raspberry Pi Pico Weather Station - part I&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn.pl/blog/04-pico-weather-station2/&quot;&gt;Raspberry Pi Pico Weather Station - part II&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn.pl/blog/06-pico-aa-batteries/&quot;&gt;Powering Raspberry Pi Pico with AA batteries (Weather Station Part III)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn.pl/blog/09-pico-solar-panels/&quot;&gt;Powering a Raspberry Pi Pico with solar panels and an 18650&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Some interesting discussions that I found on the topic of Pico power
consumption, that have been a great source of knowledge and inspiration when
working on this project:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.reddit.com/r/raspberrypipico/comments/10ynbz1/power_reduction_on_pico_w_how_to_do/&quot;&gt;Reddit: Power reduction on pico w? - how to do?&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/orgs/micropython/discussions/10889&quot;&gt;PICO W - lightsleep not working after cyw43_deinit #10889&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/orgs/micropython/discussions/12573&quot;&gt;PICO W lightsleep doesn&#39;t work when using rshell #12573&lt;/a&gt;&lt;/p&gt;
&lt;/a&gt;</description>
      <pubDate>Tue, 07 May 2024 24:00:00 GMT</pubDate>
      <dc:creator>STFN</dc:creator>
      <guid>https://stfn.pl/blog/34-pico-power-consumption-solar-panels/</guid>
    </item>
    <item>
      <title>Join my Matrix Space! And some other random news</title>
      <link>https://stfn.pl/blog/35-matrix-and-blog-updates/</link>
      <description>&lt;p&gt;Just a few short updates today:&lt;/p&gt;
&lt;h3&gt;Matrix Space&lt;/h3&gt;
&lt;p&gt;I created a Matrix space for everyone who would like to discuss interesting
topics like programming, SBCs, hamradio, BOINC, permaculture and all that in
between, and I would like to invite who is reading my blog to join!&lt;/p&gt;
&lt;p&gt;If you don&#39;t know Matrix, it&#39;s like Discord, but open and decentralized, like
the Fediverse.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://matrix.to/#/#stfn:pol.social&quot;&gt;(Invitation Link)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Once you register with Matrix and join my Space (hehe, mySpace), you&#39;ll need to
join a room, there should be a list of rooms available somewhere in the UI.&lt;/p&gt;
&lt;h3&gt;Blog updates&lt;/h3&gt;
&lt;p&gt;My latest post &lt;a href=&quot;https://stfn.pl/blog/34-pico-power-consumption-solar-panels/&quot;&gt;(Reducing Raspberry Pi Pico W power consumption and a second
attempt at using solar
panels)&lt;/a&gt; was
mentioned in an article at hackster.io: &lt;a href=&quot;https://www.hackster.io/news/some-smart-thinking-about-always-on-wi-fi-gets-a-raspberry-pi-pico-w-weather-station-sipping-solar-8fb1a316434a&quot;&gt;Some Smart Thinking About Always-On
Wi-Fi Gets a Raspberry Pi Pico W Weather Station Sipping
Solar&lt;/a&gt;,
and in a &lt;a href=&quot;https://www.adafruitdaily.com/2024/05/13/python-on-microcontrollers-newsletter-raspberry-pi-connect-matter-1-3-python-3-13-beta-and-more-circuitpython-python-micropython-thepsf-raspberry_pi/&quot;&gt;CircuitPython
Newsletter&lt;/a&gt;,
even though I am not using CircuitPython at all.&lt;/p&gt;
&lt;p&gt;Those mentions caused the largest spike in visitor numbers since the beginning
of this blog, but my super basic techstack - NGINX serving static files - had no
problems. Yay for simplicity!&lt;/p&gt;
&lt;p&gt;Ashley Cawley left a very interesting comment under my &lt;a href=&quot;https://stfn.pl/blog/22-pico-battery-level/&quot;&gt;How to monitor 12V
battery charge with a Raspberry Pi
Pico&lt;/a&gt; post, which turned into &lt;a href=&quot;https://forums.raspberrypi.com/viewtopic.php?t=370485&quot;&gt;a
thread on the Raspberry Pi
Forums&lt;/a&gt; It turns out that
different version of the MicroPython firmware work differently with analogue
inputs on the Pico, and the code I presented in that blog post has issues with
running with the newest version of MP.&lt;/p&gt;
&lt;p&gt;Ashley, I am so sorry, I promised to take a look at it this week, but I totally
did not have the time to do so, I will get back to you!&lt;/p&gt;
&lt;p&gt;I now have three (3!) Meshtastic nodes, one of them running on solar panels, and
soon I will present them here.&lt;/p&gt;
&lt;p&gt;I am also in the process of moving my homelab from a Thinkpad X220 to a proper
&amp;quot;server&amp;quot; desktop PC made from different old parts.&lt;/p&gt;
&lt;p&gt;There are so many things I want to write about, if only I had more time.&lt;/p&gt;
&lt;p&gt;Thanks for reading, you are the best people &amp;lt;3&lt;/p&gt;
</description>
      <pubDate>Fri, 17 May 2024 24:00:00 GMT</pubDate>
      <dc:creator>STFN</dc:creator>
      <guid>https://stfn.pl/blog/35-matrix-and-blog-updates/</guid>
    </item>
    <item>
      <title>Making my own private Strava</title>
      <link>https://stfn.pl/blog/36-bike/</link>
      <description>&lt;p&gt;&lt;a href=&quot;https://stfn.pl/blog/36-bike/#solution&quot;&gt;Go straight to the main part and
skip my personal musings and all that boring stuff.&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;I bought a new bike&lt;/h2&gt;
&lt;p&gt;I used to bike a lot. Starting with the smallest bike with additional wheels for
stability, I remember the big moment that was removing those supports, and
riding for the first time without them, as the grownups did.&lt;/p&gt;
&lt;p&gt;Then it was the staple of every child growing up in Poland in the 90s: the First
Communion bicycle. It was an MTB, with horn-style handlebars, 3x6 gearing, 26&amp;quot;
inch and crude steel (I guess?) frame. In line with another ancient Polish
tradition, it was in adult size to &amp;quot;serve you longer as you grow&amp;quot;. For the first
few years I had trouble getting on and from it, because it was much too large
for me, and that would often end in scraped knees.&lt;/p&gt;
&lt;p&gt;With even bad traditions having a grain of truth in them, it served me well into
high school. And then I went to a big city to go to the University. The bike was
left in my hometown, and some years later my dad threw it away. I still kinda
miss it. And I don&#39;t have any photos of it.&lt;/p&gt;
&lt;p&gt;For the first few years in the big city I did not cycle at all, but that changed
when my grandpa decided he is too old to cycle, and gave me his MTB. I was lucky
enough to live at that time in a place that had a dedicated communal bike and
trolley room, so I could store it. It was also at that time that I started
working, so I used my bike to commute to work, which turned out to be much faster than
by public transport.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/36/secondmtb.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/36/secondmtb.jpg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;The only photo of my second MTB that I have, I got a flat tire that day and
came back home by tram&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;And I got hooked into biking on a totally new level. I gave that MTB to my
sister and bought myself a very old road bike which I renovated and converted
into a city single speed. I later turned it into a fixie bike, and replaced so
many parts it became a Theseus&#39; bike.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn.pl/blog/36-bike/!%5Bx%5D(https:/stfn-pl-blog-assets.b-cdn.net/36/fixie2.jpeg)&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/36/fixie2.jpeg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;My fixie, probably fourth or fifth iteration. That was a fun
thing to ride and to build. At some point I was even making my own wheels.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Finally I bought a proper road bike, it was an awesome retro bicycle, with a mix
of Shimano and Campagnolo road components, which I later switch to all Shimano.
The brakes and axles were Dura-Ace. If you know, you know. I rode it a lot around
Poznań, took it to the mountains. My record was 95km in a day.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/36/roadbike.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/36/roadbike.jpg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;Somewhere in the Sudety mountains.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Then the pandemic came, and I moved from Poznań to a small nearby town. And the
biking died out. I wasn&#39;t commuting to work anymore, the roads around weren&#39;t
that good, and I lost heart in riding in traffic.&lt;/p&gt;
&lt;p&gt;Since 2021 until March of this year I did maybe 50km altogether. Yet, to
paraphrase Bloc Party, &amp;quot;At 36 I have decided something must change&amp;quot;. I sold my
single speed - fixie - again single speed of Theseus, I sold my road bike, and
bought an MTB.&lt;/p&gt;
&lt;p&gt;It&#39;s a Goetze* Define PRO with 29&amp;quot; tires, 2x10 Shimano drivetrain and hydraulic
disk brakes. And it&#39;s a life changer.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/36/newmtb.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/36/newmtb.jpg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;My new MTB in it&#39;s natural habitat.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;I now live next to a large forest, and not being confined to asphalt roads
changes everything. There are hundreds of kilometers of sand and gravel roads
around me, and I can travel them all. I can go to a lake, I can go to my
homestead*, I can ride and ride and not see any cars for hours.&lt;/p&gt;
&lt;p&gt;Popey recently wrote about &lt;a href=&quot;https://popey.com/blog/2024/04/the-joy-of-code/&quot;&gt;regaining the Joy of
Code&lt;/a&gt;. I have regained the Joy
of Biking :3&lt;/p&gt;
&lt;p&gt;So far I did around 300km with my new bike, and switching from a road bike
required some adjusting. Mostly mental, I had to stop being afraid of going into
loose gravel or sand, and gain the courage that I am able to ride over most
terrains, even small brooks. And the disk brakes are awesome, totally different
and much better than the road bike traditional brakes. One other I had to get
used is that I have a working suspension, and the bike bending under you is
actually a good thing.&lt;/p&gt;
&lt;p&gt;So that is my biking story in a nutshell, now let&#39;s talk about the software.&lt;/p&gt;
&lt;a name=&quot;solution&quot;&gt;
&lt;h2&gt;Setting up my private activity logger&lt;/h2&gt;
&lt;p&gt;My first activity logger was a basic &amp;quot;bike computer&amp;quot; attached to the handlebars,
showing data like current speed and total distance. Then came the era of
smartphones, and already on my first one, a HTC Wildfire S, I installed an
activity tracking app. Somehow I never used Endomondo. At first I was using
Runkeeper, and later on switched to Strava.&lt;/p&gt;
&lt;h3&gt;Open Tracks&lt;/h3&gt;
&lt;p&gt;Today, with the growing enshittifaction of Internet services, and my current
preference for private, FOSS solutios, I started looking for something of the
latter kind. I asked a question on Mastodon what do people prefer, and from
their suggestions (thank you so much!) I chose
&lt;a href=&quot;https://f-droid.org/packages/de.dennisguse.opentracks/&quot;&gt;OpenTracks&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;It can be installed on an Android phone using
&lt;a href=&quot;https://f-droid.org/en/&quot;&gt;F-droid&lt;/a&gt;. One more reason I am happy with my recent
switch from iOS to Android.&lt;/p&gt;
&lt;p&gt;Open Tracks allows tracking your activities with GPS, and saving them locally on
your phone. No cloud services, no ads, no subscriptions. It just works. And can
also show summarized stats, total distance, average speed etc.&lt;/p&gt;
&lt;h3&gt;Exporting data&lt;/h3&gt;
&lt;p&gt;The activities are only saved locally on my phone. OpenTracks allows for
automatic exporting them to a particular location in the phone&#39;s storage. I have
enabled that to store the tracks on my phone&#39;s microSD card (yes, my Fairphone 4
has a microSD slot, and it&#39;s still a great idea). I also configured Open Tracks
to export files as *.gpx, more on that later.&lt;/p&gt;
&lt;p&gt;The next step is to move the data from the phone.&lt;/p&gt;
&lt;p&gt;Also with the use of F-droid, I installed
&lt;a href=&quot;https://f-droid.org/packages/com.termux/&quot;&gt;Termux&lt;/a&gt;, which is a terminal emulator
for Android phones.&lt;/p&gt;
&lt;p&gt;In Termux I wrote a oneliner bash script that uses &lt;code&gt;rsync&lt;/code&gt; to copy the files
from the phone to my NAS.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/36/termux.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/36/termux.jpg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;My script to backup activities from my home to my NAS. I also
use it to backup photos.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;And this part is done, I now have a quick and simple way to export tracks to my
NAS, which gives the additional advantage of having them backed up in more than
one place. From time to time I open Termux on my phone and run the bash script
to export new files.&lt;/p&gt;
&lt;h3&gt;Presenting data&lt;/h3&gt;
&lt;p&gt;I now have the GPX files on my NAS. The next step is to visualize them on a map.&lt;/p&gt;
&lt;p&gt;For that I use GPSPrune, a free piece of software that can be simply installed
with &lt;code&gt;apt&lt;/code&gt; on my Debian 12 machines.&lt;/p&gt;
&lt;p&gt;At first I exported the files in the default KMZ format, but GPSPrune had
problems opening them, so I switched to GPX, and it worked.&lt;/p&gt;
&lt;p&gt;What is especially cool about it is that I can open multiple activity files at
once, which will generate a sort of a heat map of my rides. Something, if I
remember correctly, is only available in Strava premium subscription :)&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/36/gpsprune.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/36/gpsprune.jpg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;Screenshot from GPSPrune &lt;a href=&quot;https://www.youtube.com/watch?v=yeYOWl3K584&quot;&gt;tutorial video&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2&gt;Bottom Text&lt;/h2&gt;
&lt;p&gt;And that&#39;s it. With the use of free and open software I am able to have my own
activity tracker allowing me to review and visualize my rides. And all that
without sharing my private data with some corporate cloud vaporware.&lt;/p&gt;
&lt;p&gt;I admit that using Open Tracks, Termux, Bash, Rsync and then GPSPrune is a bit
unwieldy and requires some technical skills, but in my personal case, the pros
outweigh the cons by a lot. What do you think?&lt;/p&gt;
&lt;p&gt;Thanks for reading!&lt;/p&gt;
&lt;p&gt;* I&#39;m sure you Internet people of 2000&#39;s will smile now :)&lt;/p&gt;
&lt;p&gt;* I still haven&#39;t decided how to call that piece of land that I have. A garden?
A homestead? A future house location? Not sure.&lt;/p&gt;
&lt;/a&gt;</description>
      <pubDate>Tue, 21 May 2024 24:00:00 GMT</pubDate>
      <dc:creator>STFN</dc:creator>
      <guid>https://stfn.pl/blog/36-bike/</guid>
    </item>
    <item>
      <title>I got married!</title>
      <link>https://stfn.pl/blog/37-wedding/</link>
      <description>&lt;p&gt;I haven&#39;t written anything here for almost a month, but that happened for a very
important reason: I got married!&lt;/p&gt;
&lt;p&gt;Alicja is the greatest girl, and now I can call her my wife, how cool is that?!&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/37/1.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/37/1.jpg&quot; alt=&quot;A photo of the
inside of a shed, with tables prepared for the wedding party. There is also a
set of food warmers and cutlery&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;The place was very rustic&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/37/2.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/37/2.jpg&quot; alt=&quot;A photo of my
right hand with a wedding ring on the ring finger&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;First day of wearing the wedding ring&lt;/span&gt;&lt;/p&gt;
</description>
      <pubDate>Thu, 20 Jun 2024 24:00:00 GMT</pubDate>
      <dc:creator>STFN</dc:creator>
      <guid>https://stfn.pl/blog/37-wedding/</guid>
    </item>
    <item>
      <title>State of my homelab/NAS in June 2024</title>
      <link>https://stfn.pl/blog/38-my-homelab-0624/</link>
      <description>&lt;p&gt;Almost a year has passed since I last &lt;a href=&quot;https://stfn.pl/blog/11-my-homelab-0823/&quot;&gt;wrote about the the state of my
homelab&lt;/a&gt;. Basically every component of
it has been replaced since that time, and so I decided to write a new episode.&lt;/p&gt;
&lt;p&gt;As in the previous iteration, I have a combined server and NAS for my homelab. The more proper way would be
to divide it into separate units, but due to space, power and budget constrains,
I&#39;m running a single box to both host my services and store data.&lt;/p&gt;
&lt;p&gt;And this iteration is quite a revolution, I finally went with a &amp;quot;desktop&amp;quot; PC,
with proper storage and low-power, but reasonable specs.&lt;/p&gt;
&lt;h2&gt;Hardware&lt;/h2&gt;
&lt;p&gt;Tha hardware used for this build is mostly stuff I already had lying around,
coming from my old &amp;quot;gaming&amp;quot; desktop, or from Alicja&#39;s (&lt;a href=&quot;https://stfn.pl/blog/37-wedding/&quot;&gt;my wife
&amp;lt;3&lt;/a&gt;) Netflix box. She used to have a small
mATX PC connected to a TV, but a month ago I found a good deal for a Fujitsu
USFF desktop computer, gave it to her, and reacquired her PC for parts for my
NAS.&lt;/p&gt;
&lt;p&gt;Actually, the only parts I bought specifically for the homelab were the hard
drives.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/38/inside.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/38/inside.jpg&quot; alt=&quot;The inside of my new homelab server. The particular parts are described in
the blog
post.&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;The inside of my new homelab server&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;The case is a Cooler Master Elite back from 2014 which was the basis for my
first very own desktop PC. It&#39;s age is visible in the fact that it has three
5.25&amp;quot; trays (who uses 5.25&amp;quot; trays anymore?!), and there are no means for cable
management. But for my needs it&#39;s great, it can store a lot of hard drives, and
the airflow is good. It even still has the DVD drive/burner that was useful a
decade ago. I would remove it, but I don&#39;t have the replacement mesh cover for
the front, so the DVD drive works as a high-tech plug for the front of the case.
How the times have changed.&lt;/p&gt;
&lt;p&gt;The motherboard is a Gigabyte GA-Z97P-D3. Not much to say about it. It has a
1150 socket, six SATA ports and four RAM slots. Typical of the time. The webpage
for it says that it supports NVME drives via PCIe, something I might investigate in
the future. If the system could boot from an NVME drive in a PCIe slot, then I
could use the whole six SATA ports for storage.&lt;/p&gt;
&lt;p&gt;The CPU is more interesting than the motherboard. It is an i3-4170T with two cores,
four threads. The T in the name denotes that it is a low power variant, with
only 35W of TDP. From what I&#39;ve seen so far, it is absolutely fast enough for
the tasks I throw at it.&lt;/p&gt;
&lt;p&gt;The CPU cooler is a SilentiumPC Spartan 5 120mm, an overkill for the
35W TDP CPU, but that allows it to run at the lowest possible speed, making the
build almost silent.&lt;/p&gt;
&lt;p&gt;The RAM is just some 32GB in four sticks of 8GB, 1600MT/s. It works. It&#39;s not
ECC. Maybe one day I will move to a system with ECC RAM?&lt;/p&gt;
&lt;p&gt;The PSU is a Seasonic 500W low tier model. It has only Bronze rating, I&#39;m using
it as it is also a hand-me-down from a previous build. If I were to buy a power
supply specifically for a homelab, I would go with a Gold or Platinum rating.&lt;/p&gt;
&lt;p&gt;For cooling the drives, there is a BeQuiet! 120mm fan in the front. Also set to
run in silent mode. All in all, I have no problems living and sleeping in the
same room as the box, the fan hum drowns out in the background noise.&lt;/p&gt;
&lt;p&gt;Finally there is the PCIE Wi-Fi card, because again, due to space constrains
in the vicinity of the router, the NAS needs to use wireless to connect to the
rest of the network.
Not perfect but here we are.&lt;/p&gt;
&lt;h2&gt;Drives&lt;/h2&gt;
&lt;p&gt;The drives are a mix of ones bought specifically for it to be a NAS, and ones
taken from the previous iteration of my homelab.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/38/inside2.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/38/inside2.jpg&quot; alt=&quot;The inside
of my new homelab server, closeup on the four drives.&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;The drives. There&#39;s still room for more.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;The boot drive is a 500GB SSD from my previous homelab.&lt;/p&gt;
&lt;p&gt;The main storage is a pair of 4TB HDDs, one WD Pro, and one Seagate Ironwolf.
Both are CMR, 5600rpm&lt;/p&gt;
&lt;p&gt;The secondary, &amp;quot;fast&amp;quot; storage is a pair of 2.5&amp;quot;, 1TB SSDs.&lt;/p&gt;
&lt;p&gt;And there is also a single 500GB SSD just because I had nothing to do with it.
Currently it serves as a place to store random, temporary files.&lt;/p&gt;
&lt;p&gt;Six drives in total, populating all of the available SATA ports. If one day I
need more ports, I will probably buy a PCIe expansion card for either NVME or
SATA drives.&lt;/p&gt;
&lt;p&gt;The case had no place for 2.5&amp;quot; drives, another sign of it&#39;s age, so the four
SSDs live in this shiny 3.5&amp;quot; -&amp;gt; 2.5&amp;quot; adapter cage.&lt;/p&gt;
&lt;p&gt;The yellow and red spaghetti in the middle is there because the PSU did not have
enough SATA power ports, and I had to buy MOLEX to SATA power converters. They
are not pretty but they work.&lt;/p&gt;
&lt;h2&gt;Storage configuration&lt;/h2&gt;
&lt;p&gt;All drives, apart from the boot one, use ZFS. For the boot drive I stayed with
ext4, to save myself the hassle of setting up ZFS-on-boot in Debian.&lt;/p&gt;
&lt;p&gt;The two HDDs run in a ZFS mirror, the same as the two 1TB SSDs. The remaining
500GB SSD is a ZFS single.&lt;/p&gt;
&lt;p&gt;The pools are shared over the network using NFS configured straight in ZFS.&lt;/p&gt;
&lt;p&gt;One thing that surprised me is that I cannot see detailed statistics of how full
the drivers are, neither via Prometheus nor Telegraf. This is possible, but only
under FreeBSD. For reasons I don&#39;t exactly understand, Linux does not have such
powers.&lt;/p&gt;
&lt;p&gt;And from the few weeks that I have been using my NAS, I can see that what they
say that &amp;quot;ZFS likes RAM&amp;quot; is true. Any larger copying operation results in most
of those 32GB of RAM being occupied.&lt;/p&gt;
&lt;h2&gt;Software&lt;/h2&gt;
&lt;p&gt;As the box is not only a NAS, but also a homelab, it runs several self-hosted
services.&lt;/p&gt;
&lt;p&gt;The operating system is Debian 12 Bookworm. I think you can already see that the name of
this build is simplicity and &amp;quot;just-workism&amp;quot; and Debian fits into this philosophy perfectly.&lt;/p&gt;
&lt;p&gt;The systems run a mix of ways, most of them are Docker containers, but some are
systemd daemons. I don&#39;t know and don&#39;t care much about the systemd drama in the Linux
world, for me it&#39;s an awesome tool, and I&#39;ve been liking it even more once I
learnt how to daemonize stuff myself.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/38/homepage.png&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/38/homepage.png&quot; alt=&quot;Screenshotof my homepage. More detailed description in the blog
post.&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;The dashboard&lt;/span&gt;&lt;/p&gt;
&lt;h3&gt;Homepage&lt;/h3&gt;
&lt;p&gt;For the dashboard of my homelab, I am running
&lt;a href=&quot;https://gethomepage.dev/latest/&quot;&gt;homepage&lt;/a&gt;. I switched to it from Dashy because
the editing workflow for Dashy was for me janky at best. Homepage is edited by
writing YAML files, which I found much cleaner and simpler.&lt;/p&gt;
&lt;h3&gt;Metube&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/alexta69/metube&quot;&gt;Metube&lt;/a&gt; is a Youtube downloading service.
I&#39;m surprised it&#39;s still running with all those YT wars with ad blockers and the
like. I try to download as much stuff from YT as possible before they lock it
down even further. Great for getting music and tutorials.&lt;/p&gt;
&lt;h3&gt;FreshRSS&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/FreshRSS/FreshRSS&quot;&gt;FreshRSS&lt;/a&gt; is a self-hosted RSS reader with a clean UI.&lt;/p&gt;
&lt;h3&gt;Navidrome&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://www.navidrome.org/&quot;&gt;Navidrome&lt;/a&gt; is a music player. I still have some mp3s that I bought on Basecamp.&lt;/p&gt;
&lt;h3&gt;PhotoPrism&lt;/h3&gt;
&lt;p&gt;One of my newest discoveries, &lt;a href=&quot;https://www.photoprism.app/&quot;&gt;PhotoPrism&lt;/a&gt; is a
self-hosted photo management app, similar in UI to iPhotos on the IOS cloud. A
cool feature is that it can do object detection on the photos, and tag and sort
them based on what it finds. One day I will write a longer blog post on it.&lt;/p&gt;
&lt;h3&gt;Podgrab&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/akhilrex/podgrab&quot;&gt;Podgrab&lt;/a&gt; is an app to automatically
download new podcast episodes as they appear. Actually, I use this app only for
preservation (a.k.a hoarding) of podcasts, I listen to them only on my phone
using &lt;a href=&quot;https://antennapod.org/&quot;&gt;AntennaPod&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Grafana, InfluxDB, Prometheus&lt;/h3&gt;
&lt;p&gt;My monitoring solution is based on Prometheus and Node Exporter collecting
metrics, InfluxDB digesting them, and Grafana as the presentation layer. Also a
topic for a future blog post.&lt;/p&gt;
&lt;h3&gt;Cockpit&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://cockpit-project.org/&quot;&gt;Cockpit&lt;/a&gt; is a web interface for server
management, allows reviewing and modyfying settings, updating software, reading
logs, all that sysadmin stuff.&lt;/p&gt;
&lt;h3&gt;PiHole&lt;/h3&gt;
&lt;p&gt;Who said &lt;a href=&quot;https://pi-hole.net/&quot;&gt;PiHole&lt;/a&gt; can only be installed on a Pi? I&#39;m sure
most homelabbers use it, it&#39;s a DNS based adblocker.&lt;/p&gt;
&lt;h3&gt;FlatNotes&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/Dullage/flatnotes&quot;&gt;FlatNotes&lt;/a&gt; - a super simple app for
making notes. I use it to make drafts of new blog posts. The notes are saved as
markup file straight on the disk, allowing for easy backup&lt;/p&gt;
&lt;h3&gt;IT Tools&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/CorentinTh/it-tools&quot;&gt;A set of tools in a single Docker
container&lt;/a&gt; useful for any software
developer. I use them all the time, mostly for formatting JSON files, generating
UUIDs, all those small things that have to be done.&lt;/p&gt;
&lt;h3&gt;Forgejo&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://forgejo.org/&quot;&gt;Forgejo&lt;/a&gt; is a self-hosted git repository manager, a great
alternative to GitHub. It also supports actions similar to GitHub Actions. I use
it to store my blog repo, and publish it live on every merge to main. Forgejo I
am running as a systemd service.&lt;/p&gt;
&lt;h3&gt;Hoarder&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/hoarder-app/hoarder&quot;&gt;Hoarder&lt;/a&gt; is a new app, still under
development. It&#39;s a bookmark manager with a nice trick up its sleeve: It can use
AI to tag newly added bookmarks. There will soon be a whole separate blog post
on this.&lt;/p&gt;
&lt;h2&gt;Bottom Text&lt;/h2&gt;
&lt;p&gt;Having a homelab is an iterative journey. My first homelab was a single
Raspberry Pi Zero running PiHole. I then moved to a pair of Pi 4s, then an old
Thinkpad, and now I arrived at having a &amp;quot;desktop&amp;quot; box with redundant storage and
a lot of RAM. I&#39;m happy where I am now, and I learnt a ton along the way.&lt;/p&gt;
&lt;p&gt;Cosplaying as a sysadmin ((c) &lt;a href=&quot;https://www.jeffgeerling.com/blog/2022/cosplaying-sysadmin&quot;&gt;Jeff
Geerling&lt;/a&gt;) gives you
a wide set of skills, and, at least for me, I am using some of them in my day
work. For example, without a homelab I would not have learnt so much about
Docker, something that I use everyday for my job, and I can put on my resume.&lt;/p&gt;
&lt;p&gt;One thing I need to take a closer look at is power consumption. I don&#39;t have a
wall meter yet, so I don&#39;t know how much power the box is drawing. Something
that I will tackle in the near future and report my findings.&lt;/p&gt;
&lt;p&gt;I already have a plan how to enhance my homelab, so stay tuned! And you can stay
tuned by subscribing to my &lt;a href=&quot;https://stfn.pl/rss.xml&quot;&gt;RSS feed&lt;/a&gt;. Please do not
&amp;quot;smash the like button&amp;quot; as I don&#39;t have one.&lt;/p&gt;
&lt;p&gt;Thanks for reading!&lt;/p&gt;
</description>
      <pubDate>Sun, 23 Jun 2024 24:00:00 GMT</pubDate>
      <dc:creator>STFN</dc:creator>
      <guid>https://stfn.pl/blog/38-my-homelab-0624/</guid>
    </item>
    <item>
      <title>I reached 150 million points in BOINC</title>
      <link>https://stfn.pl/blog/39-another-boinc-milestone/</link>
      <description>&lt;p&gt;I started BOINCing somewhere in April of 2021. It took me two years and four
months to reach 100 million points. &lt;a href=&quot;https://stfn.pl/blog/10-my-thoughts-on-boinc/&quot;&gt;In August of 2023 I wrote a blog post about
this milestone&lt;/a&gt;. Today, less than
a year later, I reached 150 million points, and it&#39;s time to write an update.&lt;/p&gt;
&lt;p&gt;Of course those are only imaginary Internet swag points, but on the other hand
they do show the real life resources, electricity and hardware, that I put into
BOINC.&lt;/p&gt;
&lt;p&gt;Since the last update I have been mostly crunching Einstein@Home and
Asteroids@Home, with some Universe@Home and Milkyway@Home. I reached 87 million
points in Einstein, mostly thanks to the new GPU based O3AS application that is
very &amp;quot;generous&amp;quot; when it comes to points. In Milkyway@Home I reached 10 million
points, mostly to get to a nice round number. In Asteroids I passed 4 million
points, and in Universe I&#39;m at 50 million, and it looks like that I will stay at
this level for a long time.&lt;/p&gt;
&lt;p&gt;I&#39;ve been mostly crunching on my desktop, with a Ryzen 3700X and an RTX2060 GPU.
I also used my new Raspberry Pi 5 to do some crunching, and I wrote a series of
blog posts, comparing it with the Pi 4b:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn.pl/blog/17-rpi4-rpi5-boinc/&quot;&gt;Einstein@Home&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn.pl/blog/18-rpi4-rpi5-asteroids/&quot;&gt;Asteroids@Home&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn.pl/blog/19-rpi4-rpi5-universe/&quot;&gt;Universe@Home&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;In the recent months I mostly moved my crunching to a Hetzner Server Auction
box. I find them to have the best price to performance ratio when it comes to
rentable servers, and crunching tasks on a 32 thread Ryzen 5950X was a joy to
observe. I did it so as not share the electricity costs with my wife, and also
reduce the noise and heat in our home.&lt;/p&gt;
&lt;p&gt;So, that&#39;s about me, but how does the general BOINC situation look today? Bleak.&lt;/p&gt;
&lt;p&gt;The BOINC ecosystem does look like it is sunsetting, with no new projects on the
horizon, and existing ones having issues and scaling down, with one notable
exception.&lt;/p&gt;
&lt;p&gt;Gaia@Home seems to be permanently down and I don&#39;t have any hope for it anymore.&lt;/p&gt;
&lt;p&gt;Universe@Home has sadly lost their main scientist, and they have been inactive
since January. There were talks about reactivating it in April or May, but now
it&#39;s late June, and nothing happened.&lt;/p&gt;
&lt;p&gt;Milkyway@Home has finished their GPU application, and now provides only one CPU
app.&lt;/p&gt;
&lt;p&gt;World Community Grid just cannot get their stuff together after all those years
after being acquired. Most of their apps have tasks very rarely or not at all.
At least their website doesn&#39;t break twice a day as it used to.&lt;/p&gt;
&lt;p&gt;Asteroids@Home seems ok, they have had a steady run for the last months, with a
good stream of jobs to crunch, and even development work to make them more
efficient and use modern CPU features.&lt;/p&gt;
&lt;p&gt;And Einstein@Home, the jewel in the BOINC crown, a project that has a steady
flow of new jobs to crunch, new projects being developed, and the best, most
responsive staff I&#39;ve ever seen in a BOINC project.&lt;/p&gt;
&lt;p&gt;As I said again and again, I think BOINC is a great project that could bring a
lot of added value to the world, but it&#39;s underfunded and understaffed. The
current situation is just so sad, watching the BOINC ecosystem curl and shrink.
Seeing a new BOINC project appearing would be the coolest thing.&lt;/p&gt;
&lt;p&gt;That’s all for today, see you at 200 million points for a new update.&lt;/p&gt;
</description>
      <pubDate>Thu, 27 Jun 2024 24:00:00 GMT</pubDate>
      <dc:creator>STFN</dc:creator>
      <guid>https://stfn.pl/blog/39-another-boinc-milestone/</guid>
    </item>
    <item>
      <title>How to set up Umami.is, a self-hosted analytics service</title>
      <link>https://stfn.pl/blog/40-umami-self-hosted-analytics/</link>
      <description>&lt;div class=&quot;future&quot;&gt;
&lt;p&gt;March 2026: In December of 2025 my VPS got hacked, and the reason was a vulnerability in the Umami container:
&lt;a href=&quot;https://stfn.pl/blog/88-i-got-hacked/&quot;&gt;I got hacked, and that has uncovered all the things I&#39;ve been doing wrong&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;h2&gt;Disclamer&lt;/h2&gt;
&lt;p&gt;I know that this article will be about touchy subjects, so let&#39;s start with the important stuff:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Yes, my blog is collecting data about its visitors, meaning You.&lt;/li&gt;
&lt;li&gt;It is only the most basic stuff, like country, browser, OS, and the referrer.&lt;/li&gt;
&lt;li&gt;I am not collecting any personally identifiable information, there are no
cookies and no local storage used.&lt;/li&gt;
&lt;li&gt;I&#39;m doing it only because I like looking at numbers and I&#39;m curious where do
people find my blog&lt;/li&gt;
&lt;li&gt;I am not selling or passing that data to anyone. And now, with a self-hosted
solution, it will never leave my server.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;With that out of the way, let&#39;s continue.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/40/umami.png&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/40/umami.png&quot; alt=&quot;A graph with
blue columns on a white background, showing my blog&#39;s hourly traffic&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;Umami dashboard&lt;/span&gt;&lt;/p&gt;
&lt;h2&gt;Why move to self-hosted analytics&lt;/h2&gt;
&lt;p&gt;Since this blog started at the end of 2022, I have been using
&lt;a href=&quot;https://www.goatcounter.com/&quot;&gt;GoatCounter&lt;/a&gt; as my source of analytics.&lt;/p&gt;
&lt;p&gt;It&#39;s free, clean, and simple to use. GC offers a free hosted service, so getting
started is only a matter of setting up an account and adding a small piece of
HTML to your website. I still highly recommend it for anyone wanting to have a
turn-key solution for their website. And they are donations based, you can help
them &lt;a href=&quot;https://github.com/sponsors/arp242/&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The main issue I was having with GoatCounter was that every adblocker was
cutting them out, as the JS script used for counting visits was calling a
well-known, external service. I could clearly see in the nginx logs that I was
getting much more traffic than GC recorded.&lt;/p&gt;
&lt;p&gt;On the other hand, a self-hosted analytics service is hosted, and communicates
only with your own server, and adblockers have no reason to block it unless
specifically provided with the server&#39;s domain.&lt;/p&gt;
&lt;p&gt;And also, you know how it is with homelabbing and self-hosting, from time to
time you want to check out different tools :) I could have switched to a
self-hosted GC instance, but I wanted some change, and so I tried out
&lt;a href=&quot;https://umami.is/&quot;&gt;Umami&lt;/a&gt;, and so far I have been very happy with it.&lt;/p&gt;
&lt;p&gt;So let me share with you how I installed and configured Umami.&lt;/p&gt;
&lt;h2&gt;Installation&lt;/h2&gt;
&lt;p&gt;As for the installation, I went the way I usually go with such services, using
Docker Compose to run them in containers.&lt;/p&gt;
&lt;p&gt;(all terminal snippets taken from Umami docs)&lt;/p&gt;
&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; clone https://github.com/umami-software/umami.git
&lt;span class=&quot;token function&quot;&gt;docker-compose&lt;/span&gt; up &lt;span class=&quot;token parameter variable&quot;&gt;-d&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will get you a database and a Umami worker available at port 3000. If you
installed it in your VPS, you should be able to access it at
http://&amp;lt;domain&amp;gt;:3000. It&#39;s possible you will need to add a custom rule to
your firewall to allow access at that port. Don&#39;t worry, it&#39;s only temporary.&lt;/p&gt;
&lt;p&gt;You can now visit Umami&#39;s dashboard and set up a secure admin password.&lt;/p&gt;
&lt;p&gt;Do not add a website yet! First we need to set up a proper way of accessing
Umami on the server, and for that we need to have a subdomain and configure it
in nginx.&lt;/p&gt;
&lt;h3&gt;Using a subdomain&lt;/h3&gt;
&lt;p&gt;Accessing Umami over a non-standard port and without HTTPS is by far not a good
solution. What I did is I set up a subdomain with my domain provider and
configured it in nginx.&lt;/p&gt;
&lt;p&gt;To set up a subdomain I opened the admin dashboard at the company&#39;s website
where I have all my domains, went to my main domain stfn.pl, an added a
subdomain umami.stfn.pl. I pointed that subdomain to the same IP address as the main domain.
It will be nginx&#39;s responsibility to sort out the traffic.&lt;/p&gt;
&lt;h3&gt;Nginx reverse proxy config&lt;/h3&gt;
&lt;p&gt;I am using nginx as my HTTP server. Adhering to best practices, I have created a
separate configuration file at &lt;code&gt;/etc/nginx/conf.d/umami.conf&lt;/code&gt;. I am using
nginx&#39;s functionality of &amp;quot;reverse proxy&amp;quot;, in which it is passing request coming
to a specific domain, towards a service running in the backend, and passing them
from the standard HTTP port 80 to a different one, used by the backend service.&lt;/p&gt;
&lt;p&gt;The configuration is rather straightforward. &lt;code&gt;server_name&lt;/code&gt; is the domain
which this config file is monitoring. &lt;code&gt;proxy_pass&lt;/code&gt; defines where the requests
should be passed.&lt;/p&gt;
&lt;p&gt;The different &lt;code&gt;proxy_set_header&lt;/code&gt; stanzas define which headers should be carried
on to the backend. I added them, as without them the country information would
not reach Umami. The issue is described in this &lt;a href=&quot;https://github.com/umami-software/umami/issues/814&quot;&gt;GitHub
issue&lt;/a&gt;.&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;server &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    server_name umami.stfn.pl&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    location / &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        proxy_pass http://localhost:3000&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        proxy_set_header Upgrade &lt;span class=&quot;token variable&quot;&gt;$http_upgrade&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        proxy_set_header Connection &lt;span class=&quot;token string&quot;&gt;&quot;Upgrade&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        proxy_set_header Host &lt;span class=&quot;token variable&quot;&gt;$host&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        proxy_set_header X-Real-IP &lt;span class=&quot;token variable&quot;&gt;$remote_addr&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        proxy_set_header X-Forwarded-Proto https&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        proxy_set_header X-Forwarded-For &lt;span class=&quot;token variable&quot;&gt;$proxy_add_x_forwarded_for&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        proxy_set_header X-Forwarded-Host  &lt;span class=&quot;token variable&quot;&gt;$host&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;Once you have the config file saved, it&#39;s good practice to first test the new
config using&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; nginx &lt;span class=&quot;token parameter variable&quot;&gt;-t&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Nginx will tell you if there are any problems with the configuration file. If
all is fine, what is left is to restart nginx&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; systemctl restart nginx.service&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now Umami should be available at the subdomain you defined. Remove the firewall
rule for port 3000, it won&#39;t be needed anymore.&lt;/p&gt;
&lt;p&gt;Next part is setting up HTTPS. For that I am using Certbot. Setting up Cerbot is
very easy, everything is described &lt;a href=&quot;https://certbot.eff.org/instructions&quot;&gt;on this single
page&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Certbot will automatically updated your nginx config, adding all of the required
stanzas. Once Certbot does its thing, you will be only able to access your new subdomain
with HTTPS.&lt;/p&gt;
&lt;h3&gt;Configure Umami for data collection&lt;/h3&gt;
&lt;p&gt;Configuring Umami for data collection is described
&lt;a href=&quot;https://umami.is/docs/add-a-website&quot;&gt;here&lt;/a&gt; and
&lt;a href=&quot;https://umami.is/docs/collect-data&quot;&gt;here&lt;/a&gt; in the docs.&lt;/p&gt;
&lt;p&gt;Once you go through those steps, you should start seeing traffic on your Umami
dashboard. But there is one issue, anytime you access your website, it will be
also counted, and will skew the results. But there are ways to mitigate this issue.&lt;/p&gt;
&lt;h3&gt;Disabling Umami for dev work and local machines&lt;/h3&gt;
&lt;p&gt;For my blog I am using &lt;a href=&quot;https://astro.build/&quot;&gt;Astro.js&lt;/a&gt;, which is
based on React. React allows to differentiate between the development and
production states using environmental variables. To disable Umami collecting
data when I am developing my blog, I modified the tracking code:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;head&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;meta&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;env&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;MODE&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;production&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
			&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;script
				defer
				src&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;link&quot;&lt;/span&gt;
				data&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;website&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;id&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;xxx&quot;&lt;/span&gt;
			&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&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 operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;head&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This way the script will only render when the site is exported using &lt;code&gt;yarn build&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;To disable counting my visits to my blog, I used the solution described in &lt;a href=&quot;https://github.com/umami-software/umami/discussions/852%22&quot;&gt;this
Github Issue&lt;/a&gt;. Adding
an env variable in your browser&#39;s local storage will stop Umami from collecting
data on a given webpage. To do so, run the code below in your browser&#39;s console
in the dev tools, when your site is opened:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;localStorage&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setItem&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;umami.disabled&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Bottom text&lt;/h2&gt;
&lt;p&gt;And that&#39;s it! I am happy to switch to a self-hosted analytics. I feel that I am
more in control of what and how is being gathered, I can access the raw data in
the database container, and I am not on the mercy of any external service.&lt;/p&gt;
&lt;p&gt;There are of course downsides of self hosting stuff. One is that there is a
visible increase in the server&#39;s load, as it needs to run the required docker
containers. If you are using a very, very low-end machine for your website, that
might become an issue, especially during traffic spikes. Another issue is that
you are storing the data, and you are responsible for backups. Which of course
can be seen either as a problem, or as an opportunity to practice backups and
backup restoration. Depends on your viewpoint :)&lt;/p&gt;
&lt;p&gt;Thanks for reading!&lt;/p&gt;
</description>
      <pubDate>Thu, 04 Jul 2024 24:00:00 GMT</pubDate>
      <dc:creator>STFN</dc:creator>
      <guid>https://stfn.pl/blog/40-umami-self-hosted-analytics/</guid>
    </item>
    <item>
      <title>I passed the AWS Certified Cloud Practitioner certification</title>
      <link>https://stfn.pl/blog/41-aws-cloud-practitioner-exam/</link>
      <description>&lt;p&gt;Yesterday I passed the AWS Certified Cloud Practitioner exam, and therefore I am a
certified Practitioner of the Cloud of AWS.&lt;/p&gt;
&lt;p&gt;This is my first AWS certificate, and I have been planning to obtain it for the
last few years. The final push that convinced me to do it was that my employer
offered to reimburse it. It&#39;s always nice to save a hundred dollars :)&lt;/p&gt;
&lt;p&gt;Why did I do it? I&#39;m a software developer with leanings towards the DevOps side,
and I think that preparing for this certificate provides a good basis for going
deeper in the future and learning the tools that will open for me the doors
towards becoming a full-time DevOps. And I have this growing feeling that in the current
market situation, being &amp;quot;just&amp;quot; a Python developer is just enough, and it is
more and more expected of people to be more full-stacky. And I prefer to expand
towards the DevOps side, not towards Frontend.&lt;/p&gt;
&lt;p&gt;The syllabus for this certificate is a very wide bird-eye view of the whole
ecosystem of AWS, from the basics like S3 and EC2, through databases,
serverless, Infrastructure as Code, to more theoretical stuff like the Cloud
Adoption Framework and adherence to local laws. There is also a lot about
billing and managing users and companies in the cloud.&lt;/p&gt;
&lt;h2&gt;How I prepared for the exam&lt;/h2&gt;
&lt;p&gt;I started with going to &lt;a href=&quot;https://www.aws.training/&quot;&gt;the AWS training site&lt;/a&gt;. And
I have to say, their training materials are a mess. There are so many sites and
courses available there, and every site looks a bit different, every log in is
several redirects, it&#39;s hard to find stuff you&#39;ve already visited, it&#39;s just
confusing.&lt;/p&gt;
&lt;p&gt;Having conquered their site, I enrolled to the &lt;a href=&quot;https://explore.skillbuilder.aws/learn/course/134&quot;&gt;AWS Cloud Practitioner
Essentials (Second Edition)&lt;/a&gt;
course. The course itself was okayish, it has that corporate feel with a visibly
calculated amount of cringe jokes. The content revolved about explaining how the
AWS cloud works on the example of a coffee shop. It is visible that it&#39;s aimed
at non-technical or semi-technical people. The whole course took me a around a
week of afternoons to finish.&lt;/p&gt;
&lt;p&gt;Having finished that course I did not feel like I was prepared to take the exam,
so I looked for other options. What I chose was the &lt;a href=&quot;https://www.exampro.co/clf-c02&quot;&gt;ExamPro AWS Certified Cloud
Practitioner course&lt;/a&gt;. I found it on Youtube
first, and then came across many positive comments on Reddit, so I gave it a go
and bought it for 29 dollars. Not cheap, but I decided I prefer to go this way.
I don&#39;t mind learning from videos, and the example exams were a big help. I felt
that it gave me much more information than the AWS course.&lt;/p&gt;
&lt;p&gt;Finally, I went through the question set provided in this very useful GitHub
repo: &lt;a href=&quot;https://github.com/kananinirav/AWS-Certified-Cloud-Practitioner-Notes&quot;&gt;AWS Cloud Practitioner Study Notes
(CLF-C02)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;All in all it took me around two weeks of an hour or two a day to prepare for the
exam. For me, the technical parts like EC2, lambdas, databases and S3 were
relatively easy, harder to remember were the management and law parts. It&#39;s easy
to learn what interests you, and hard to learn the boring stuff.&lt;/p&gt;
&lt;p&gt;The exam itself was relatively straightforward. I went to a local training center
governed by Pearson Vue, was greeted, registered, and was sat in front of a
computer in an empty room. Answered the 65 single or multiple choice questions.
After that there was a short survey how was the exam, and finally I got the
score screen saying that I passed. I got an official confirmation via email a
few hours later, and was informed that I received the score of 86%. 70% is the
passing threshold.&lt;/p&gt;
&lt;h2&gt;What next?&lt;/h2&gt;
&lt;p&gt;I don&#39;t think I will stop here. Currently I am considering either going slow and
steady and enrolling for the AWS Certified Developer exam, or going straight all
in, and attempting to get the AWS Certified DevOps Engineer - Professional. I am
sure both of them will look good on my CV and will help me in my daily job, but
probably the second one will be a big boost in my possible transition to a more
DevOps career. I still have some time to decide.&lt;/p&gt;
&lt;p&gt;&lt;s&gt;BTW: This blog uses AWS. While the webpage is hosted at another provider, I am
using S3 with CloudFront to serve the images and &lt;a href=&quot;https://stfn.pl/blog/30-plants-timelapse-rpi/&quot;&gt;that one
video&lt;/a&gt;.&lt;/s&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;March 2026: that is no longer the case, I moved to Bunny CDN and wrote it about it &lt;a href=&quot;https://stfn.pl/blog/91-cloudfront-to-bunny-cdn/&quot;&gt;here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Thanks for reading!&lt;/p&gt;
</description>
      <pubDate>Wed, 17 Jul 2024 24:00:00 GMT</pubDate>
      <dc:creator>STFN</dc:creator>
      <guid>https://stfn.pl/blog/41-aws-cloud-practitioner-exam/</guid>
    </item>
    <item>
      <title>How to set up subdomains in the homelab with PiHole and Caddy</title>
      <link>https://stfn.pl/blog/42-caddy-pihole-homelab-subdomains/</link>
      <description>&lt;p&gt;I have a homelab in which I run several self-hosted services &lt;a href=&quot;https://stfn.pl/blog/38-my-homelab-0624/&quot;&gt;(more on it
here)&lt;/a&gt;. The services are deployed as
Docker containers, and each of them exposes a different port. Up until now I
have been using the port number to access them, for example homelab.local:3002
would lead me to Grafana. The landing page for the homelab is served by
&lt;a href=&quot;https://gethomepage.dev/latest/&quot;&gt;Homepage&lt;/a&gt; which is bound to port 80, the
default port for HTTP. Thanks to that, I can just access homelab.local and use
the links presented there to access the services without needing to remember
their ports.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/38/homepage.png&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/38/homepage.png&quot; alt=&quot;Screenshot of my homepage. More detailed description in the blog
post.&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;The dashboard&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;But this gets tiring quickly when you want to access the services directly. And
well, for a moment my Homelab felt too complete and I wanted to check out some new
configuration and tooling. I wanted to have subdomains, so that instead of going
to homelab.local:3002 I could just visit grafana.homelab.local.&lt;/p&gt;
&lt;h2&gt;Here comes DNS&lt;/h2&gt;
&lt;p&gt;DNS is a tool that translates IP addresses into human-readable URLs. Thanks to
DNS, you can ask your browser to access &lt;a href=&quot;https://fosstodon.org&quot;&gt;fosstodon.org&lt;/a&gt;
and not a bunch of numbers making an IP address.&lt;/p&gt;
&lt;p&gt;To make the subdomains work, we&#39;ll need two things: an HTTP server than can do
reverse proxying, and a local DNS server. The DNS server will tell your computer
how to reach the homelab, and the reverse proxy will distribute the incoming
traffic between the different services. For the first part, we&#39;ll use
&lt;a href=&quot;https://caddyserver.com/docs/&quot;&gt;Caddy&lt;/a&gt;, and for the second one there is
something that a lot of homelabbers is using, but not directly for this feature:
&lt;a href=&quot;https://pi-hole.net/&quot;&gt;Pi-hole&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Caddy setup&lt;/h2&gt;
&lt;p&gt;Why Caddy you ask? I chose it because I&#39;ve seen good reviews of it in several
places, and most of all because I wanted to try a tool I have not used before.
For this blog I am using nginx, so I looked for alternatives, and Caddy was high
on the list of recommended software.&lt;/p&gt;
&lt;p&gt;The installation instructions for Caddy can be found in the &lt;a href=&quot;https://caddyserver.com/docs/install&quot;&gt;Caddy
docs&lt;/a&gt;. In my homelab I am using Debian
Bookworm, so I installed it by adding the Caddy repository to my software
sources, and then installing it with &lt;code&gt;apt&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;This way after installation I have a systemd service which can run a daemon in
the background.&lt;/p&gt;
&lt;p&gt;Investigating the service file (&lt;code&gt;/lib/systemd/system/caddy.service&lt;/code&gt;) shows that
Caddy is storing its configuration in the Caddyfile at &lt;code&gt;/etc/caddy/Caddyfile&lt;/code&gt;,
and this is the file that we&#39;ll need to edit.&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;nano&lt;/span&gt; /etc/caddy/Caddyfile&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The Caddyfile will have a default configuration of serving static webpages. We
won&#39;t need it, you can just remove it or comment it out. What we will need is a
reverse proxy, so, all we need in the Caddyfile is the setup for the reverse proxy, anything else
can be let at their defaults values.&lt;/p&gt;
&lt;p&gt;A reverse proxy is a service that accepts requests from the client, and forwards
them to another service living on the server. There can be several of those
services running together on the server, and Caddy will distribute traffic
between them based on the URL of the request.&lt;/p&gt;
&lt;p&gt;Here is how my Caddyfile looks like:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;http://homelab.local &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
reverse_proxy :3001
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

http://photo.homelab.local &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
reverse_proxy :2342
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

http://music.homelab.local &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
reverse_proxy :4533
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

http://rss.homelab.local &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
reverse_proxy :8090
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

http://jellyfin.homelab.local &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
reverse_proxy :8096
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This tells Caddy that if it receives a request from the client&#39;s browser to
access the URL homelab.local, it needs to forward it to service running at port
3001, which is my Homepage instance. But if the client&#39;s browser requests the
URL rss.homelab.local, it needs to forward it to the service running at port
8090, which in my case is FreshRSS.&lt;/p&gt;
&lt;p&gt;After applying the changes, we need to restart the Caddy service&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; systemctl restart caddy.service&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This part is done, and now it is the time to move to the second part of the setup.&lt;/p&gt;
&lt;h2&gt;Pi-hole setup&lt;/h2&gt;
&lt;p&gt;I assume your client device, the one from which you will be connecting to the
homelab, is already configured to use Pi-hole. If not, go through the
&lt;a href=&quot;https://docs.pi-hole.net/main/post-install/&quot;&gt;post-installation steps&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Now our homelab knows how to distribute the requests coming to it based on the
URL, but there is one big problem. Our client device, the one from which you are
connecting to the homelab to get the RSS feeds or music, does not know that requesting
rss.homelab.local or music.homelab.local must lead to the same computer, to the
homelab. Remember, as we discussed in the beginning, the client uses a DNS
server to be told which address means which computer.&lt;/p&gt;
&lt;p&gt;The base url, homelab.local, should work out of the box due to local DNS
resolution, (which is an ever more black box for me), but the subdomains will
not work unless we specifically setup the DNS server to tell the clients that
the subdomains are the same computer as the base one.&lt;/p&gt;
&lt;p&gt;And here comes Pi-hole. Pi-hole adblocking functionality is based on DNS.&lt;/p&gt;
&lt;p&gt;For example, when you visit a page, like onlinenewsthatmakeyouangry.com, the
page tells your browser to also pull images from
ads.onlinenewsthatmakeyouangry.com, and Pi-hole will see that those are ads, and
will not return an IP address for them. The ads will not be fetched and you will
not see them. That&#39;s Pi-hole in two sentences.&lt;/p&gt;
&lt;p&gt;Pi-hole allows setting up custom DNS entries, and this what we will use to point
the client to our homelab subdomains. Local DNS is the menu entry that we need.
The screenshot shows how it should be configured. Remember to put the IP address
of your homelab.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/42/pihole.png&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/42/pihole.png&quot; alt=&quot;A screenshot of my Pihole admin page. The page shows the Local DNS submenu.
Under the &#39;List of local DNS domains&#39; there are five entries showing the
different subdomains I have configured, each leading to the same IP
address&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Most probably you will need to disconnect your client device and connect to your
network again to make the changes work.&lt;/p&gt;
&lt;p&gt;If everything was configured, the subdomains should now work. Try it out!&lt;/p&gt;
&lt;h2&gt;HTTP or HTTPS?&lt;/h2&gt;
&lt;p&gt;In the Caddyfile I provided the URLs starting with http://. One of the big features
of Caddy is automatic configuration of HTTPS, so why am I using HTTP?&lt;/p&gt;
&lt;p&gt;I do not expose my homelab to the Internet, and I do not have a public domain
for it. For such domains Caddy creates a self-signed SSL certificate, which the
browsers find suspicious and warn you before accessing pages with such &amp;quot;shady&amp;quot;
certificates. Again, my homelab is not available from outside my network, so I
don&#39;t care about HTTPS, and I don&#39;t want to see a warning every time I visit my
local server, therefore I am telling Caddy specifically to only use HTTP.&lt;/p&gt;
&lt;p&gt;If you have a homelab living in the wide open Internet and it has a public
domain, then you can remove the http:// prefixes from the Caddyfile, and Caddy
will generate a proper SSL certificate for your server.&lt;/p&gt;
&lt;p&gt;And that&#39;s it, your homelab is now on a totally new level! Thanks for reading!&lt;/p&gt;
</description>
      <pubDate>Sat, 20 Jul 2024 24:00:00 GMT</pubDate>
      <dc:creator>STFN</dc:creator>
      <guid>https://stfn.pl/blog/42-caddy-pihole-homelab-subdomains/</guid>
    </item>
    <item>
      <title>How I Learned to Stop Worrying and Love the Classic Watch</title>
      <link>https://stfn.pl/blog/43-leaving-the-smartwatch/</link>
      <description>&lt;p&gt;In January of 2019 I bought a smartwatch, a Garmin vívoactive 3. I bought it
during a typical period of &amp;quot;getting fit after a breakup&amp;quot;. And at the time it did
wonders for me. I am a person who enjoys seeing numbers and graphs about my
life, and having a daily statistic of steps and calories was a big motivation.
It made me walk to work instead of taking the public transport. I recorded every
run and every bike ride.&lt;/p&gt;
&lt;p&gt;Garmin allows automatic syncing of recorded activities to Strava and of course I
did that too. The best part of finishing every bike ride was checking out the
list of new personal records I beat, and the leaderboard of the local segments.
The smartwatch was an important part of my life, and I wore it every waking
moment, sometimes even not taking it off for sleep.&lt;/p&gt;
&lt;p&gt;That period ended rather abruptly with the pandemic and the first lockdown, and
I remember at one point being angry that all my stats would go down because of
staying at home. I know how bad that sounds.&lt;/p&gt;
&lt;p&gt;The lockdown finished and I was back at pushing the number of steps. I rode my
bike much less frequently because of reasons described in the &lt;a href=&quot;https://stfn.pl/blog/36-bike/&quot;&gt;Making my own
private Strava&lt;/a&gt; blog post, but still, using the
smartwatch was an important aspect.&lt;/p&gt;
&lt;p&gt;And then... I lost it. I could not find it anywhere, I searched the whole flat,
the garden, the car, even the basement but it was nowhere to be found. After some
mental investigations I came to a conclusion that I must have put in some bag
that I later emptied to a trash bin. I was bummed out, but also at that time I
did not want to spend a lot of money on a new watch, as I had higher priority
spendings on the horizon.&lt;/p&gt;
&lt;p&gt;In the meantime I got interested in old 80s watches, and on a whim I bought a
CASIO F-91W, a classic watch that tells the time and is so famous it has &lt;a href=&quot;https://en.wikipedia.org/wiki/Casio_F-91W&quot;&gt;its own
Wikipedia page&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/43/IMG_20240810_101251_ok.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/43/IMG_20240810_101251_ok.jpg&quot; alt=&quot;A photo of my wrist with a Casio watch. The photo is taken close to a window,
the light is
soft.&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;A year has passed, I mostly forgot about my smartwatch, and then all of a sudden, in
a classic trope of 50s family sitcoms and boomer memes (well, those are the same
picture), my wife found my watch where I searched several times and missed it.
It was somehow weirdly wedged under the seat of our car.&lt;/p&gt;
&lt;p&gt;Rejoice! Again I started counting the steps and making sure I passed some
arbitrary threshold each day. I recorded a bike ride with it, and checked out
the detailed stats.&lt;/p&gt;
&lt;p&gt;It lasted two weeks.&lt;/p&gt;
&lt;p&gt;And I lost interest. I could not be bothered witch charging it every two days.
And the watch face that I like does not have a battery indicator, so sometimes
when I forgot to check it, it would die on my wrist. My mentality regarding
privacy also changed and I was not happy with sharing my most personal data with
some external servers, that have already &lt;a href=&quot;https://www.zdnet.com/article/garmin-services-and-production-go-down-after-ransomware-attack/&quot;&gt;at least once been attacked by
ransomware&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Another aspect is my current preference to use private, open source software,
and with that I would not be able to use Strava anymore. Going back again to my
blog post &lt;a href=&quot;https://stfn.pl/blog/36-bike/&quot;&gt;about biking&lt;/a&gt;, where I describe my
current bike ride logging solution.&lt;/p&gt;
&lt;p&gt;And so I am back to my CASIO, which Just Works, tells the time, the date and
even the day of the week, and the battery will last for the next decade. It does
not have distracting notifications. It is waterproof and even if I manage to
break it, buying a new one will not break me financially. I do not need anything else right now.&lt;/p&gt;
&lt;p&gt;I think it&#39;s just another example of me being tired of too much technology.&lt;/p&gt;
&lt;p&gt;And if I ever need to make an IED, I &lt;a href=&quot;https://en.wikipedia.org/wiki/Casio_F-91W#Usage_in_terrorism&quot;&gt;have the first
part&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Thanks for reading!&lt;/p&gt;
</description>
      <pubDate>Sat, 10 Aug 2024 24:00:00 GMT</pubDate>
      <dc:creator>STFN</dc:creator>
      <guid>https://stfn.pl/blog/43-leaving-the-smartwatch/</guid>
    </item>
    <item>
      <title>Trying to use a terminal only laptop</title>
      <link>https://stfn.pl/blog/44-terminal-only-laptop/</link>
      <description>&lt;p&gt;I am writing these words in NeoVim, started from tmux, running in Debian
Bookworm. The laptop is a Lenovo Thinkpad X240 with a Haswell-era CPU and 8GB of
RAM. And I do not have any graphic environment, just a terminal. And at the same
it&#39;s hard and great.&lt;/p&gt;
&lt;p&gt;Why am I doing it? Mostly because I can, and I thought it would be a fun
challenge to try and use the command line only. And it&#39;s also a very good
learning experience, I already learned stuff that would not interest me if I had
Gnome or KDE or Xfce to fallback on. I already had to learn how to copy and
paste in vim.&lt;/p&gt;
&lt;p&gt;My, so to say, techstack right now is tmux for terminal composition, and neovim
as a text editor. The terminal client is bash, usually I use zsh, but for this
laptop I want to see if I can achieve the same level of configuration with good
ol&#39; bash. Wi-Fi is configured using nmcli, and I can even send the occasional
toot using toot the Mastodon client.&lt;/p&gt;
&lt;p&gt;The best thing about using a terminal-only system is it&#39;s speed. Everything is
instant. Opening a file in nvim? Instant. Waking up from sleep? Faster than
opening the lid. The computer is as fast as fast you can type.&lt;/p&gt;
&lt;p&gt;A quirk of this laptop is that there is no separate Insert key, to do Insert you
need to do Fn + End, which is a bit irritating when using nvim.&lt;/p&gt;
&lt;p&gt;One thing I am not yet able to investigate is the battery life difference. I
have bought this laptop with very bad batteries (yes, plural, it has an internal
and external one!) and I disposed of them when it was running as a homelab. I&#39;m
planning to buy a battery and see how it goes.&lt;/p&gt;
&lt;h2&gt;Overcoming Obstacles&lt;/h2&gt;
&lt;p&gt;The biggest problem with not having a graphical interface is browsing the
Internet. Yes, there is Lynx, but modern webpages are mostly not designed to
work in text-only mode. BTW: One interesting thing came out with using Lynx: It
asks you everytime a webpage wants to save a cookie, and it is eye opening.
Probably more people would stop using Google if they had to type &amp;quot;yes&amp;quot; 15 times
before being able to do a query. Anyway, I will admit, I resorted to cheating
and and mostly searched for stuff on my phone when using this laptop.&lt;/p&gt;
&lt;p&gt;And now came another problem. If for example I wanted to download something I
found on my phone, like a link to a deb package, how to do it? I could retype
the link, but that would be cumbersome. So I went with a dirty-yet-working
solution. I copy the link on my phone, save it to FlatNotes hosted on my
homelab, scp the note to the laptop, copy the url and download it with curl.
This is the stuff I do for fun.&lt;/p&gt;
&lt;h2&gt;What can you do without a DE?&lt;/h2&gt;
&lt;p&gt;Surprisingly, a lot. First and foremost, you can write. Even write more than
usual, because there are less distractions. Developing software is also
possible. With tmux the left side of the screen can be the code editor, again
vim is perfect here, and the right side can be anything.&lt;/p&gt;
&lt;p&gt;Right now my blog writing screen is tmux dividing the screen in three parts, top
left is node dev server, bottom left is vim, and the right half is lynx showing
the blog page at localhost. I wish lynx could automatically refresh the page on
changes as Firefox, but you can&#39;t have everything.&lt;/p&gt;
&lt;p&gt;Hmm, I would add a photo of it here, but my image preparation and upload
pipeline is still rather complicated, so I&#39;ll just add the photo to the Mastodon
toot announcing this post. I wonder if you can take screenshots from the
terminal? Something to investigate.&lt;/p&gt;
&lt;p&gt;Internet browsing is also possible, but as I mentioned above, not that easy. But
you do save a lot of bandwidth by not downloading the assets. Posting to
Mastodon? Sure. Browsing RSS? Also possible. Listening to music? Le oui, I&#39;ve
seen terminal music players. The CLI is the limit. That sentence would work much
better if you prononuced CLI in a way that rhymed with &amp;quot;sky&amp;quot;.&lt;/p&gt;
&lt;p&gt;I&#39;m thinking of taking this laptop with me on my next holiday. It will allow me
to do some maintenance work, like backuping photos from the camera to my
homelab, and at the same time will not tempt me to doomscroll and browse too
much intertubes.&lt;/p&gt;
&lt;p&gt;I think that&#39;s it for now. Once I spend more time in the land of no graphics, I
may have more things to share.&lt;/p&gt;
&lt;p&gt;Thanks for reading! Now to see how if I can control my Forgejo deployment
pipeline from the terminal. This post feels to much more chaotic and less
organized that usually, but that&#39;s ok for me, I want to experiment with writing
more spontaneously.&lt;/p&gt;
</description>
      <pubDate>Fri, 16 Aug 2024 24:00:00 GMT</pubDate>
      <dc:creator>STFN</dc:creator>
      <guid>https://stfn.pl/blog/44-terminal-only-laptop/</guid>
    </item>
    <item>
      <title>How to archive YouTube</title>
      <link>https://stfn.pl/blog/45-archiving-youtube/</link>
      <description>&lt;p&gt;Nothing on the Internet can be considered permanent. Webpages disappear, songs
get removed from Spotify&lt;sup&gt;&lt;a href=&quot;https://stfn.pl/blog/45-archiving-youtube/#1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;, Amazon famously removed George
Orwell&#39;s books from Kindles&lt;sup&gt;&lt;a href=&quot;https://stfn.pl/blog/45-archiving-youtube/#2&quot;&gt;[2]&lt;/a&gt;&lt;/sup&gt;. Youtube videos also disappear either by copyright
complains, of because their creators decide to remove them.&lt;/p&gt;
&lt;p&gt;So what to do? Download anything you find important. In my homelab
series&lt;sup&gt;&lt;a href=&quot;https://stfn.pl/blog/45-archiving-youtube/#3&quot;&gt;3&lt;/a&gt;&lt;/sup&gt; I already mentioned Metube&lt;sup&gt;&lt;a href=&quot;https://stfn.pl/blog/45-archiving-youtube/#4&quot;&gt;4&lt;/a&gt;&lt;/sup&gt;, which
allows downloading single videos or playlists.
But now there&#39;s something else to download videos from Youtube.&lt;/p&gt;
&lt;p&gt;A few days ago Patryk&lt;sup&gt;&lt;a href=&quot;https://stfn.pl/blog/45-archiving-youtube/#5&quot;&gt;5&lt;/a&gt;&lt;/sup&gt; showed me Pinchflat&lt;sup&gt;&lt;a href=&quot;https://stfn.pl/blog/45-archiving-youtube/#6&quot;&gt;6&lt;/a&gt;&lt;/sup&gt;.
Pinchflat is a &amp;quot;YouTube media manager&amp;quot;. It allows for downloading whole channels
or playlists, and what is more, it also keeps tracks of them, and automatically
downloads new videos.&lt;/p&gt;
&lt;p&gt;There&#39;s quite a few configuration options. You can set things like the desired video
quality of downloads, add thresholds to the age of videos to be downloaded from
a given channel, or how often should Pinchflat looks for new content. Everything
is described in the GitHub repo.&lt;/p&gt;
&lt;p&gt;Pinchflat can be installed using Docker, so very basic familiarity with that
tool is required. After installation, it can be accessed through the browser. I
also put it behind a reverse proxy with a custom subdomain for easier access. I
wrote how to do it in my blog post about Caddy&lt;sup&gt;&lt;a href=&quot;https://stfn.pl/blog/45-archiving-youtube/#7&quot;&gt;7&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;If you are going to install Pinchflat, make sure you have a lot of disk space in
advance. I downloaded only a few channels in 1080p, and already 450GB of disk
space is being used. But that&#39;s one of the reasons we have NASes, isn&#39;t it?&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/45/pinchflat1.png&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/45/pinchflat1.png&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Pinchflat also allows browsing and watching videos, and provides useful metadata
for the file. The metadata is stored on the disk together with the video files.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/45/pinchflat2.png&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/45/pinchflat2.png&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Pinchflat vs Metube&lt;/h2&gt;
&lt;p&gt;Am I going to ditch Metube after installing Pinchflat? No. I would say those two
tools complement each other, and I will continue using both of them.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Pinchflat allows downloading whole channels and
playlists, and keeping track of new videos, while Metube allows downloading
single videos and playlists.&lt;/li&gt;
&lt;li&gt;Pinchflat only supports downloading in a video format, Metube also supports downloading videos as mp3, something
very useful when fetching music from YT.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And thanks it, short and sweet. Thanks for reading!&lt;/p&gt;
&lt;p&gt;P.S. As you can see, in this post I am experimenting with footnotes. Do you
prefer inline links or footnotes? Let me know on Mastodon or via email.&lt;/p&gt;
&lt;p&gt;[1] &lt;a id=&quot;1&quot; href=&quot;https://community.spotify.com/t5/Content-Questions/Disappearing-albums/td-p/1759813&quot;&gt;Disappearing albums&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;[2] &lt;a id=&quot;2&quot; href=&quot;https://arstechnica.com/information-technology/2009/10/amazon-stipulates-terms-of-book-deletion-via-1984-settlement/&quot;&gt;Amazon settles 1984 suit, sets limits on Kindle deletions&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;[3] &lt;a id=&quot;3&quot; href=&quot;https://stfn.pl/blog/38-my-homelab-0624/&quot;&gt;My Homelab&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;[4] &lt;a id=&quot;4&quot; href=&quot;https://github.com/alexta69/metube/&quot;&gt;Metube
on Github&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;[5] &lt;a id=&quot;5&quot; href=&quot;https://wspanialy.eu/@pgronkievitz&quot;&gt;pgronkievitz @ wspanialy.eu&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;[6] &lt;a id=&quot;6&quot; href=&quot;https://github.com/kieraneglin/pinchflat&quot;&gt;Pinchflat on Github&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;[7] &lt;a id=&quot;7&quot; href=&quot;https://stfn.pl/blog/42-caddy-pihole-homelab-subdomains/&quot;&gt;How to set up
subdomains in the homelab with PiHole and Caddy&lt;/a&gt;&lt;/p&gt;
</description>
      <pubDate>Mon, 26 Aug 2024 24:00:00 GMT</pubDate>
      <dc:creator>STFN</dc:creator>
      <guid>https://stfn.pl/blog/45-archiving-youtube/</guid>
    </item>
    <item>
      <title>WireGuard and PiHole for secure ad blocking on your smartphone</title>
      <link>https://stfn.pl/blog/46-wireguard-pihole-ad-blocking/</link>
      <description>&lt;h2&gt;Caveats&lt;/h2&gt;
&lt;p&gt;Let&#39;s start with some caveats. I am not an expert in VPNs, network
configuration, nor network security. The solution that I am presenting is
working for me, I am using it everyday, and so far it has caused me no problems.
Then again, there is a real possibility I have made a glaring mistake or
omission. I am hoping that someone much smarter than I am will read this post
and point out any issues. If you are such a person, please contact me, you can
catch me on Mastodon or via email, links in the footer.&lt;/p&gt;
&lt;h2&gt;Introduction&lt;/h2&gt;
&lt;p&gt;I write blog posts like this to report on something I implemented for myself to
fill my specific needs, or to share something I learnt recently. This project
covers both. I wanted to learn how to create and configure VPNs and
other advanced network configuration, and also I wanted a way to block ads and
trackers on my phone. It is possible that what I achieved here can be reached
with simpler means, but I like overcomplicating things when learning, as it
gives more knowledge on a wider set of tools.&lt;/p&gt;
&lt;p&gt;The outcome of this project can be summarized as such:&lt;/p&gt;
&lt;p&gt;I am now able to use PiHole to block ads and trackers on all of my devices, no
matter to which network I am connected, be it my private LAN, a public WiFi, or
even cellular network. This is achieved by running a VPS (Virtual Private
Server) with PiHole, which is an advertisement blocking software.&lt;/p&gt;
&lt;p&gt;PiHole blocks ads by working as a DNS server. In a very tl;dr description, a DNS
server is a server that tells your computer how to find a way to certain places
on the Internet. You want to go to Wikipedia, your computer asks a DNS server
how to get there, and the DNS server replies &amp;quot;first left, then right, and twice
straight ahead&amp;quot;. Actually, the server replies with an IP address, but that&#39;s the
general idea. When PiHole is being used a DNS server, it gives directions to
proper content, but when your computer asks for directions to get something like
advert.malware.com, it will say nothing, and the ad will not be fetched.&lt;/p&gt;
&lt;p&gt;For security reasons, we are connecting to our new DNS server over a VPN
(VIrtual Private Network), so that only our machines can connect to it and no
one else.&lt;/p&gt;
&lt;p&gt;Theoretically the VPN part can be omitted, but that is highly undesirable. A
publicly available DNS servers can be used by malicious third parties in &lt;a href=&quot;https://www.f5.com/labs/learning-center/what-is-a-dns-amplification-attack&quot;&gt;DDOS
attacks&lt;/a&gt;.
Please do not leave your DNS server unsecured.&lt;/p&gt;
&lt;p&gt;This post is laid out in several steps&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;getting a VPS&lt;/li&gt;
&lt;li&gt;setting up a WireGuard server on the VPS&lt;/li&gt;
&lt;li&gt;setting up PiHole on the VPS&lt;/li&gt;
&lt;li&gt;configuring a Linux computer to use WireGuard and PiHole. This step can be
omitted if you only care about ad blocking on your phone, but I recommend at
least reading through it to get a better understanding of the process.&lt;/li&gt;
&lt;li&gt;configuring a smartphone to use WireGuard and PiHole.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Why just not use uBlock Origin?&lt;/h3&gt;
&lt;p&gt;But I am! My main browser on both my laptop and my phone is Firefox with uBlock
Origin installed, but especially on the phone, the browser is not everything.
Apps also connect to the Internet. Google Discover opens its links in Google
Chrome which soon will not allow using uBlock Origin. Basically everything on
your phone can send and receive traffic, traffic that is often advertisements or
tracking. And PiHole will be able to block it all.&lt;/p&gt;
&lt;h2&gt;Getting a VPS&lt;/h2&gt;
&lt;p&gt;The first thing that you will need is a computer that can be reached from the
Internet. One solution is to expose a device in your home network, but that is
less safe, and not available for everyone, people who are, for example, behind a
CGNAT do not have an easy means to expose their home devices to the public. So
in this project, I am using a VPS (Virtual Private Server) from a cloud
provider.&lt;/p&gt;
&lt;p&gt;The provider of my choice is RackNerd. While less known than the large players
like AWS or GCP, in my opinion they deliver a solid service and often have much
cheaper prices for the low end boxes, which we will need for this project. BTW,
I am using their server to host my blog. I learnt about them from &lt;a href=&quot;https://lowendbox.com/&quot;&gt;Low End
Box&lt;/a&gt;, which I also recommend to get good deals on cheap
servers.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;March 2026: I moved away from Racknerd and switched to Hetzner, because I no
longer want to use US-based companies.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;You can order any server, for our needs even the cheapest and smallest ones will
work. As for the OS, I am using Debian on all of my servers, but most probably
any other terminal Linux OS will be just fine. This tutorial assumes using an OS
that has &lt;code&gt;apt&lt;/code&gt; as the package manager and &lt;code&gt;systemd&lt;/code&gt; for managing background processes.&lt;/p&gt;
&lt;p&gt;Once you have a server running, it is basically required to do an initial
configuration and hardening to secure it from external threats. &lt;a href=&quot;https://www.digitalocean.com/community/tutorials/initial-server-setup-with-ubuntu&quot;&gt;Initial Server
Setup with
Ubuntu&lt;/a&gt;
is a great article on how to do this initial config, I really suggest you go
through it and implement each step. I know the title says &amp;quot;Ubuntu&amp;quot;, but the
things suggested there are universal.&lt;/p&gt;
&lt;h2&gt;Setting up WireGuard&lt;/h2&gt;
&lt;p&gt;Alright then. You have a server to which you can SSH safely, and which has a
UFW firewall running. Now it&#39;s time to set up a VPN (Virtual Private Network).
Having a VPN will give you a secure way of connecting to your server that no one
else is able to replicate.&lt;/p&gt;
&lt;p&gt;This part is largely based on the two following articles, with some parts advanced, unneeded parts omitted.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.digitalocean.com/community/tutorials/how-to-set-up-wireguard-on-ubuntu-20-04&quot;&gt;How to set up WireGuard on Ubuntu 20.04&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://davidshomelab.com/access-your-home-network-from-anywhere-with-wireguard-vpn/&quot;&gt;Access Your Home Network From Anywhere with WireGuard VPN&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;In short, WireGuard is based on exchanging keys, the server and every client has a public
and a private key which are used to authenticate the connection. Without knowing
those keys, other computers will not be able to join the VPN. From a OS
perspective, WireGuards creates a new network interface, usually named &lt;code&gt;wg0&lt;/code&gt;,
using which you connect to the server and other peers in the VPN network. Yes,
VPN network, I also use LED diodes :)&lt;/p&gt;
&lt;h3&gt;Server configuration&lt;/h3&gt;
&lt;p&gt;The part below needs to be done on the VPS, which will become the WireGuard
server.&lt;/p&gt;
&lt;p&gt;Start with installing WireGuard&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;apt&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; wireguard&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, generate a private key:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;wg genkey &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;tee&lt;/span&gt; /etc/wireguard/private.key&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Generate a public key from the private key:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;cat&lt;/span&gt; /etc/wireguard/private.key &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; wg pubkey &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;tee&lt;/span&gt; /etc/wireguard/public.key&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Create the server config file:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;nano&lt;/span&gt; /etc/wireguard/wg0.conf&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And paste the text below into the file:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;Interface&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
PrivateKey &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;server_private_key_goes_here&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
Address &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10.8&lt;/span&gt;.0.1/24
ListenPort &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;51820&lt;/span&gt;
SaveConfig &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The server will have an IP address of 10.8.0.1, and the whole VPN network will be
in the 10.8.XXX.XXX range.&lt;/p&gt;
&lt;p&gt;Add rule to firewall to enable communication on WireGuard&#39;s port 51820.&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; ufw allow &lt;span class=&quot;token number&quot;&gt;51820&lt;/span&gt;/udp&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Enable and start the wireguard service&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; systemctl &lt;span class=&quot;token builtin class-name&quot;&gt;enable&lt;/span&gt; wg-quick@wg0.service
&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; systemctl start wg-quick@wg0.service&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The server configuration is almost done, time to move to the client. We&#39;ll start
with configuring a Linux machine, if you want to have WG only on your phone,
skip this part.&lt;/p&gt;
&lt;h3&gt;Linux Client configuration&lt;/h3&gt;
&lt;p&gt;As in the server part, install WireGuard and setup public and private keys:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;apt&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; wireguard
wg genkey &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;tee&lt;/span&gt; /etc/wireguard/private.key
&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;cat&lt;/span&gt; /etc/wireguard/private.key &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; wg pubkey &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;tee&lt;/span&gt; /etc/wireguard/public.key&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The client config file looks a bit different, as it also needs information about
the server.&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;nano&lt;/span&gt; /etc/wireguard/wg0.conf&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;Interface&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
PrivateKey &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;client_private_key&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
Address &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10.8&lt;/span&gt;.0.2/24
 
&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;Peer&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
PublicKey &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;server_public_key&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
AllowedIPs &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10.8&lt;/span&gt;.0.0/24
Endpoint &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; server_public_ip:51820&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Fill in the private key of the client, the public key of the server, and its
public IP address. Within the VPN network, the IP address of the server will be
10.8.0.1, and the client will be 10.8.0.2.&lt;/p&gt;
&lt;h3&gt;Connecting client to server&lt;/h3&gt;
&lt;p&gt;To connect the VPN client to a server, we need to inform the server of the
client&#39;s key. Run this command on the server:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; wg &lt;span class=&quot;token builtin class-name&quot;&gt;set&lt;/span&gt; wg0 peer &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;client_public_key&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; allowed-ips &lt;span class=&quot;token number&quot;&gt;10.8&lt;/span&gt;.0.2&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, start the WG service on the client&#39;s machine:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; wg-quick up wg0&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now you should be able to SSH into the server from the client, using the server&#39;s
VPN IP address:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;ssh&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10.8&lt;/span&gt;.0.1&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For advanced usage, like tunnelling all traffic from the client through the
server, consult the two articles I have linked above.&lt;/p&gt;
&lt;h2&gt;Setting up Pihole&lt;/h2&gt;
&lt;p&gt;The next step is setting up PiHole on the same server that has the WireGuard VPN
server.&lt;/p&gt;
&lt;p&gt;PiHole installation instructions can be found
&lt;a href=&quot;https://github.com/pi-hole/pi-hole/#one-step-automated-install&quot;&gt;here&lt;/a&gt;. I would
recommend installing Pihole as a service, and not using Docker. We will be
hiding PiHole behind the UFW Firewall, and there is &lt;a href=&quot;https://askubuntu.com/questions/652556/uncomplicated-firewall-ufw-is-not-blocking-anything-when-using-docker&quot;&gt;a known problem of UFW not
blocking traffic to and from
containers&lt;/a&gt;). It can be fixed, but the easiest solution here is just install PiHole not
with Docker.&lt;/p&gt;
&lt;p&gt;In the installation wizard one step is very important. There is a screen where
PiHole asks you which interfaces should it use. By default it&#39;s all interfaces.
Change it to &lt;code&gt;wg0&lt;/code&gt; only. It will be another means of defense against abuse of
the DNS services on your server.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/46/pihole-wizard.png&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/46/pihole-wizard.png&quot; alt=&quot;A screenshot from the PiHole installation wizard showing the list of
available interfaces. Wg0 is the chosen
one.&amp;quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Finally, update the UFW rules to allow incoming traffic on port 53, but only
through the wg0 interface:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;ufw allow &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; on wg0 to any port &lt;span class=&quot;token number&quot;&gt;53&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Making sure it all works&lt;/h3&gt;
&lt;p&gt;At this point we have WireGuard and PiHole running on our server, now it&#39;s time
to make sure that we can use them. We will test by using our VPN client machine to ask
the server to resolve a DNS query, tell us the IP address of the Wikipedia. The
simplest way is to use the &lt;code&gt;nslookup&lt;/code&gt; command. If you do not have it on your
machine, install the &lt;code&gt;dns-utils&lt;/code&gt; package using your package manager.&lt;/p&gt;
&lt;p&gt;On the client machine run:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;nslookup&lt;/span&gt; en.wikipedia.org &lt;span class=&quot;token number&quot;&gt;10.8&lt;/span&gt;.0.1&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The response should be similar to&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;Server:         &lt;span class=&quot;token number&quot;&gt;10.8&lt;/span&gt;.0.1
Address:        &lt;span class=&quot;token number&quot;&gt;10.8&lt;/span&gt;.0.1&lt;span class=&quot;token comment&quot;&gt;#53&lt;/span&gt;

Non-authoritative answer:
en.wkipedia.org canonical name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; ncredir-lb.wikimedia.org.
Name:   ncredir-lb.wikimedia.org
Address: &lt;span class=&quot;token number&quot;&gt;208.80&lt;/span&gt;.154.232
Name:   ncredir-lb.wikimedia.org
Address: &lt;span class=&quot;token number&quot;&gt;2620&lt;/span&gt;:0:861:ed1a::9&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you are getting an error, make sure WireGuard is running on both server and
client, that PiHole is running, and if the firewall rules are correct.&lt;/p&gt;
&lt;p&gt;And now run:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;nslookup&lt;/span&gt; en.wikipedia.org &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;public &lt;span class=&quot;token function&quot;&gt;ip&lt;/span&gt; of the server&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The connection should time out. This means that the server&#39;s DNS service cannot
be used from the public Internet. This is exactly what we want. Good work!&lt;/p&gt;
&lt;p&gt;Now we need to make the Linux machine use our new DNS server whenever it&#39;s doing
DNS queries, so basically when you are browsing the Internet. The base two ways
to do it is to set in the terminal, or in the network settings widget.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.linuxfordevices.com/tutorials/linux/change-dns-on-linux&quot;&gt;Here&#39;show to do change it using the command
line.&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;To change it using the Network Settings widget, open your network settings, edit
the current connection, and set in the IPv4 tab, set the DNS servers field to
10.8.0.1. Disconnect and reconnect to the network. Done!&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/46/network.png&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/46/network.png&quot; alt=&quot;A screenshot from the network settings window. This one is from KDE but from
what I&#39;ve seen, it&#39;pretty universal between desktop
environments.&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Smartphone configuration&lt;/h3&gt;
&lt;p&gt;And now, after that rather long introduction, it&#39;s time for the main topic of
this post, setting up WireGuard and PiHole on the smartphone!&lt;/p&gt;
&lt;p&gt;To use WireGuard on your smartphone, there is a WireGuard application available
on the Play Store or the App Store. Install it and add a new interface using the
plus sign and select &amp;quot;create from scratch&amp;quot;.&lt;/p&gt;
&lt;p&gt;Name is whatever you want, wg0 is fine. Generate the private and public keys.
The addresses field will be the VPN IP address of your smartphone. It can be the
next free IP address in the 10.8.0.XXX range (the same as the server). For me
its 10.8.0.3. For the DNS servers field, set it to 10.8.0.1.&lt;/p&gt;
&lt;p&gt;Now select &amp;quot;Add peer&amp;quot; at the bottom. The peer public key is the server public
key. Endpoint is the server public IP address. Allowed IPs is the range of the
IP addresses in our VPN, in our case 10.8.0.0/24.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/46/wg-settings-sml.png&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/46/wg-settings-sml.png&quot; alt=&quot;A screenshot from the WireGuard mobile application.&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Save the new interface and enable it. If you have Termux or Termius, you can
test the new connection by SSHing into the server at 10.8.0.1, it should work now.&lt;/p&gt;
&lt;p&gt;And again, using Termux, you can test if your phone can resolve DNS queries with
our DNS server using the same &lt;code&gt;nslookup&lt;/code&gt; command as in the previous section.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/46/termux-sml.png&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/46/termux-sml.png&quot; alt=&quot;A screenshot from the Termux terminal emulator running on a smartphone,
showing the output of the nslookup
command&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Because you have set 10.8.0.1 as the DNS server IP in the WireGuard app
settings, your phone should now use PiHole everytime the WireGuard VPN is
running on it!&lt;/p&gt;
&lt;p&gt;The final step is letting the server know that the phone can be trusted.&lt;/p&gt;
&lt;p&gt;Login to your server and run this command&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; wg &lt;span class=&quot;token builtin class-name&quot;&gt;set&lt;/span&gt; wg0 peer &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;public_key_from_smartphone&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; allowed-ips &lt;span class=&quot;token number&quot;&gt;10.8&lt;/span&gt;.0.3&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That&#39;s it! Your phone&#39;s web traffic is now filtered with PiHole, and it does not
only apple to the web browser&#39;s traffic, but also to all of the applications
that are on it. Using your phone will now be a much lighter and more private
experience :)&lt;/p&gt;
&lt;p&gt;It took us a while to get here, but I do believe the gains are worth the hassle.
When logged in to the &lt;a href=&quot;http://10.8.0.1/admin/&quot;&gt;Pihole admin interface on the
server&lt;/a&gt; you will see how much malicious traffic is
blocked from your phone (mind, this link will only work if you went and applied
all of the steps of this post). Here&#39;s an example of what PiHole blocks from my
phone:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/46/blocked.png&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/46/blocked.png&quot; alt=&quot;A screenshot from the PiHole admin showing thousands of blocked queries
from advert and tracking servers.&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Thanks for reading! This was one of the longest and most complicated posts I&#39;ve
written so far. I hope it will be as useful to you as it is for me. Please, I&#39;d
love to hear any kind of feedback from you. You can write me an email, leave a
comment below, or catch me on Mastodon. Links in the footer.&lt;/p&gt;
</description>
      <pubDate>Sat, 07 Sep 2024 24:00:00 GMT</pubDate>
      <dc:creator>STFN</dc:creator>
      <guid>https://stfn.pl/blog/46-wireguard-pihole-ad-blocking/</guid>
    </item>
    <item>
      <title>Thinking on storage</title>
      <link>https://stfn.pl/blog/47-thinking-of-storage/</link>
      <description>&lt;p&gt;I just bought a 128GB Samsung SD Card for 39.99PLN (~9EUR), and that made me
think about the progress that storage made since I started using computers.&lt;/p&gt;
&lt;p&gt;I have this opinion that in the realm of IT, storage is where the progress is
most palpable, most visible.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/47/sd.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/47/sd.jpg&quot; alt=&quot;An unopened box with a Samsung EVO 128GB SD Card, placed on a
windowsill.&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Of course with CPUs there has been also a massive progress, the first CPU I
used, a 386, had clock speeds from 12.5Mhz to 40Mhz, depending on the version
(thanks Wikipedia). The CPU in the laptop on which I am writing those words can
boost up to 4Ghz, increase with a factor of 100. But visualizing that
development is much harder, especially as its clouded in other factors, like
improvements in IPC and going from single to multiple cores. Number go up good,
but can you feel it?&lt;/p&gt;
&lt;p&gt;For storage it&#39;s something different, especially if you are over 30 as I am, and
remember how it used to be. And storage is about things you can see, or listen
to, move from place to place.&lt;/p&gt;
&lt;p&gt;This post has no particular reason or moral, I just want to talk how I remember
and feel about storing stuff on my PC.&lt;/p&gt;
&lt;p&gt;The first PC ever that I was using, that my dad brought home somewhere in the
mid 90s, probably 1996? had no hard drive, as it was too expensive. I remember
that to start the computer, you had to insert a yellow floppy from which it
would boot to DOS, and then to play my fav game of that time, Mahjongg, you had
to insert another diskette. For them youngsters, a single 3.5&amp;quot; floppy disk held
1.44MB of data. Megabytes.&lt;/p&gt;
&lt;p&gt;Fast forward a few years, and a few iterations of the Family Computer in the
Computer Room, my dad gave me a separate partition to store my games. It was
700MB. When I bought Diablo II I had to choose the &amp;quot;minimum install&amp;quot;, as the
full one would not fit.&lt;/p&gt;
&lt;p&gt;In the middle of high school, ~2006, my dad bought himself a laptop and gave me
our family computer, at that time it had a total 60GB of storage in two hard
drives.&lt;/p&gt;
&lt;p&gt;From a 1.44MB floppy to 60GB HDDs in ten years.&lt;/p&gt;
&lt;p&gt;I took that PC with me to study at the University, and I filled it to the brim
with MP3s (arrrr!) and RAW files from my DSLR. A MP3 was 5MB, and a single RAW file was
10MB.&lt;/p&gt;
&lt;p&gt;Speaking of RAW files, it was at that time when I bought my first DSLR from
money I earned from working in a hardware shop. I think I paid somewhere around
200PLN(~50EUR) for a 2GB SD Card.&lt;/p&gt;
&lt;p&gt;And since then it went quickly, in 2009 I got a laptop with a 250GB 2.5&amp;quot; hard
drive. In 2015 I bought and build from scratch my first very own desktop PC,
with a 1TB HDD and a 250GB SSD. My next PC had a 4TB HDD and 1TB NVME SSD.&lt;/p&gt;
&lt;p&gt;Finally, today my knees are being warmed by my NAS sitting under my desk. The
NAS has in total six drives, 5 HDDs and a single boot SSD. In total, there&#39;s
13.5TB of raw space inside that box, with 8.5TB of it being usable, as some
drives are mirrored.&lt;/p&gt;
&lt;p&gt;In 28 years, from a floppy disk to a ZFS mirror. From 1.4MB to 13.5TB,
13500000MB, a factor of almost one million.&lt;/p&gt;
&lt;p&gt;And again, this is something you can actually see. It&#39;s hard to spot or
visualize in your head the
difference between 3GHz and 4GHz CPUs, or 2600MT/s vs 3600MT/s RAM speeds, but
for drives, it&#39;s there. If you still have MP3s, you can actually store thousands
of times more of them on your drives, and that happened in less than a decade.&lt;/p&gt;
&lt;p&gt;When I was having that laptop, I was taking a lot of photos, and eventually I
run out of space on the 250GB HDD. So I started backuping them on DVD drives. At
that time, 4.7GB felt like quite a lot for storing data. A few months ago, when
I finally went through of all my DVDs and put them on my NAS, 4.7GB was just a
blip, barely recognizable on a graph in Grafana.&lt;/p&gt;
&lt;p&gt;I think this has been so far the most random, old-man-waves-at-clouds post in my
blog. But hey, sometimes I feel like writing a detailed tutorial, and sometimes
I just write all that random stuff that comes to my mind. Hope you like both of those.&lt;/p&gt;
</description>
      <pubDate>Sat, 21 Sep 2024 24:00:00 GMT</pubDate>
      <dc:creator>STFN</dc:creator>
      <guid>https://stfn.pl/blog/47-thinking-of-storage/</guid>
    </item>
    <item>
      <title>I also caught the comet</title>
      <link>https://stfn.pl/blog/48-comet/</link>
      <description>&lt;p&gt;Would you look at that, finally another post about taking photos of the night
sky.&lt;/p&gt;
&lt;p&gt;After seeing all those cool photos of the C/2023 A3 Tsuchinshan-ATLAS comet on
Mastodon, I decided I need to join the craze and try to hunt it myself. At the
first attempt I went to a nearby hill and tried to see it with binoculars, but
to no success. So the next night I went all in, packed my DSLR, a 17-40 and a
50mm lens, a tripod, and drove to found a good spot.&lt;/p&gt;
&lt;p&gt;I&#39;m a big fan of wind turbines (pun kinda intended), so I came up with the idea
that combining a comet with one of those would be cool. There&#39;s a wind farm (is
that a name for a group of wind turbines?) not
far from my place, so I went there, and even managed to find a place to
park.&lt;/p&gt;
&lt;p&gt;I got the best results with the 50mm f/1.8 lens. The camera is my old Canon
1200D, astromodified. The exposure time was 4 seconds at f/3.2, ISO 1600.&lt;/p&gt;
&lt;p&gt;And here it is, with all its comet glory:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/48/comet.JPG&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/48/comet.JPG&quot; alt=&quot;Photo of a dusk sky, at the top there are stars visible, and among them the
comet. Below the comet there is still some sunlight visible, and the top there&#39;s
a country landscape with a wind
turbine.&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;(click on the photo for a larger version)&lt;/p&gt;
&lt;p&gt;I&#39;m rather content how nice the tail turned out. With some slight curves
manipulation I was able to pull it out of the surrounding sky.&lt;/p&gt;
&lt;p&gt;And of course, if you zoom in, you will count at least three satellite streaks.
The night sky is becoming a mess.&lt;/p&gt;
&lt;p&gt;If you want to hunt for the comet yourself, I highly recommend
&lt;a href=&quot;https://stellarium.org/&quot;&gt;Stellarium&lt;/a&gt;, both the desktop version and the mobile
app. As for the desktop version, you will need to add the comet manually,
there&#39;s a really good blog post on how to do it: [How to add a comet to
Stellarium]9https://blog.martinbelan.com/2020/07/09/how-to-add-a-comet-to-stellarium/).&lt;/p&gt;
&lt;p&gt;And there&#39;s also &lt;a href=&quot;https://heavens-above.com/&quot;&gt;Heavens Above&lt;/a&gt;. HA is not only a
great resource on everything happening in the sky, it&#39;s also a manifest on &amp;quot;if
it works, don&#39;t change it&amp;quot;. I don&#39;t think the webpage look changed in any way
since I first saw it somewhere around 2005.&lt;/p&gt;
&lt;p&gt;As for my astrophotography journey in general, I got fed up with trying to make
my mount work, so I am sending it to a person that services mounts. I hope he
will be able to fix it, or at least find the cause of all my issues. And once
the mount is back and fixed, I should be able to go back to the game. I also
have a plan to switch my astro controller to a Pi 5 with an NVME drive, which
should give much, much better performance. I will of course report how it goes.&lt;/p&gt;
&lt;p&gt;Thanks for reading!&lt;/p&gt;
</description>
      <pubDate>Thu, 17 Oct 2024 24:00:00 GMT</pubDate>
      <dc:creator>STFN</dc:creator>
      <guid>https://stfn.pl/blog/48-comet/</guid>
    </item>
    <item>
      <title>I got my Raspberry Pi 5 a HAT</title>
      <link>https://stfn.pl/blog/49-my-pi5-got-a-hat/</link>
      <description>&lt;p&gt;&lt;em&gt;This is not a sponsored article, everything mentioned here I bought for
myself with my own money.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;As I mentioned in the &lt;a href=&quot;https://stfn.pl/blog/48-comet/&quot;&gt;previous blog post&lt;/a&gt;, my astrophotography
journey is suspended until I get back my mount from servicing. But in the
meantime I can work on upgrading other parts of my setup. So far I have been running
a Raspberry Pi 4 with &lt;a href=&quot;https://www.astroberry.io/&quot;&gt;astroberry.io&lt;/a&gt; to control the
acquisition process, and its performance was always a bit of a pain for me. I
have a Pi 5, it was an impulse buy on the day of its release, and lately I had
no use for it, so I decided it will be my new astrophotography controller.&lt;/p&gt;
&lt;p&gt;And as I am going to use I might as well crank it up to 11 and try out those new
NVMe HATs for bigger and faster storage.&lt;/p&gt;
&lt;h2&gt;Choosing the HAT&lt;/h2&gt;
&lt;p&gt;For the HAT I chose &lt;a href=&quot;https://pineboards.io/products/hatdrive-nano-nvme-2230-2242-gen-3-for-raspberry-pi-5&quot;&gt;the HatDrive! Nano by
Pineboards&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I chose a product by Pineboards for a few reasons, first of all I&#39;ve been
following their progress, and I am impressed how quickly they went from
basically two guys with a soldering iron, to a proper company making Raspberry
Pi accessories, and second, the creators are my fellow compatriots, and I felt
like supporting the Polish hardware scene :) And thirdly, my main inspiration
for all RPi stuff, &lt;a href=&quot;https://www.youtube.com/@JeffGeerling&quot;&gt;Jeff Geerling&lt;/a&gt;, made
a few videos showcasing their products and his reviews were positive.&lt;/p&gt;
&lt;p&gt;I chose the HatDrive! Nano because well, it was the cheapest option, and with
the smallest footprint, which I think helps with the cooling, while still
supporting 2230 and 2242 NVMe drives as the larger HAT models.&lt;/p&gt;
&lt;h2&gt;Hardware setup&lt;/h2&gt;
&lt;p&gt;The HAT came with a set of screws, spacers, and the ribbon cable. The screw
package looked like a set from a different model, because it had four spacers,
while the Nano version only uses two. But hey, free M2.5 spacers, always useful
when tinkering with SBCs. Also in the &lt;a href=&quot;https://pineboards.io/pages/documentation&quot;&gt;documentation section on the Pineboards
webpage&lt;/a&gt; there is no entry for the
Nano, only for the Lite, but that&#39;s ok, they are almost indentical when it comes
to mounting.&lt;/p&gt;
&lt;p&gt;For the case I am using a &lt;a href=&quot;https://thepihut.com/products/cluster-case-for-raspberry-pi&quot;&gt;ThePiHut cluster
case&lt;/a&gt;. It&#39;s
universal enough to be able to accommodate the Pi 5 as well as the 4, can be
used for single SBC builds, and is open on the sides, which is good for the
cooling. I had to modify the mounting of the Pi a bit to make it work with the
spacers, thankfully I had a bunch of long M2.5 bolts that went through the
bottom of the case, through the Pi and into the spacers from below.&lt;/p&gt;
&lt;p&gt;Mounting the HAT itself was easy, the hardest part was handling the ribbon
cable, but with being very very cautious, I managed to make it work. I&#39;m always afraid I will
break those tiny connectors that hold the ribbons in place. And not without
reason, I broke the camera connector on my first Pi Zero.&lt;/p&gt;
&lt;p&gt;The NVMe SSD I am using is a 512GB Kioxia drive which I bought rather cheaply
from a local marketplace. The seller said it was taken from a new laptop which
in place got a larger drive.&lt;/p&gt;
&lt;p&gt;That dangling thingy visible in the photos is the battery for the RTC clock.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/49/IMG_8965.JPG&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/49/IMG_8965.JPG&quot; alt=&quot;Photo of a
Pi 5 with an NVMe HAT in an opened cluster case&amp;quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/49/IMG_8966.JPG&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/49/IMG_8966.JPG&quot; alt=&quot;A photo of a Pi 5 with an NVMe HAT with the case close, the insides can be
seen through the translucent
top&amp;quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Booting The Pi 5 from an NVMe drive&lt;/h2&gt;
&lt;p&gt;My plan was to ditch the SD card altogether and use the NVME as the boot drive.&lt;/p&gt;
&lt;p&gt;After installing the NVMe HAT, I booted the Pi from the SD card. I&#39;m running the
latest version of the Raspberry Pi OS, 64 bit, with the desktop environment.&lt;/p&gt;
&lt;p&gt;The system booted normally, and I opened GParted to see if the new drive was
seen by the system. And it was! On the first time!&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/49/gparted.png&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/49/gparted.png&quot; alt=&quot;A screenshot of GParted, showing two drives, one for SDcard and one for
NVMe&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The drive was reporting correctly, so I used the SD Card Copier utility,
installed by default on the RPi OS, and copied the system from the microSD card
to the NVME drive. It took around 10 minutes.&lt;/p&gt;
&lt;p&gt;Then I went through the &lt;a href=&quot;https://www.jeffgeerling.com/blog/2023/nvme-ssd-boot-raspberry-pi-5&quot;&gt;instructions provided by
Jeff&lt;/a&gt; on
how to set the NVMe as the boot drive. Few commands in the terminal later, I
turned off the Pi, removed the microSD card and turned it back on again.&lt;/p&gt;
&lt;h2&gt;Running the NVMe Pi&lt;/h2&gt;
&lt;p&gt;And I have to say, WOW! The difference is staggering. The boot sequence, which
takes 30 seconds, maybe more on a microSD card, was almost instantaneous when
booting from the NVMe. That white window with the Raspberry Logo barely blinks.
The desktop is ready to use in seconds.&lt;/p&gt;
&lt;p&gt;The whole feel of using the Pi is different. Thunar is smooth as butter. All the
apps load much faster. It&#39;s as if it was a totally different computer. Firefox
is a much better experience. And for those astrophotography people out there,
I have to tell you, using KStars from an NVMe drive will not be a pain anymore.&lt;/p&gt;
&lt;p&gt;I won&#39;t provide any benchmarks, there&#39;s already a ton of them on the Internet.
If you want to see graphs and numbers, I highly recommend &lt;a class=&quot;dark:text-white&quot; href=&quot;https://bret.dk/&quot;&gt;Bret&#39;s webpage&lt;/a&gt;, where he does all sort of high
quality testing of different SBCs.&lt;/p&gt;
&lt;p&gt;I had two main concerns when installing the NVMe HAT. The first one was WiFi
connectivity and the second one was cooling.&lt;/p&gt;
&lt;p&gt;The WiFi on the Pi is not the best, especially when using the 5GHz band. I was
concerned that the HAT would interfere with the signals and degrade the
performance, but from my short testing I did not see any problems. We&#39;ll see how
it goes in the long run.&lt;/p&gt;
&lt;p&gt;As for cooling, after adding the HAT, I installed
&lt;a href=&quot;https://openphdguiding.org/&quot;&gt;PHD2&lt;/a&gt; by compiling it from source. And even under
prolonged load, with the active cooler on, the Pi stayed around 58C. I have a
feeling that the HAT even helps with the cooling a tiny bit, as it guides air
through the radiator and not away from it. But that may be just my suspicion.&lt;/p&gt;
&lt;h2&gt;Bottom Line&lt;/h2&gt;
&lt;p&gt;I now strongly believe that if you have a Pi5, you should consider getting an
NVMe HAT for it. You will get a much better experience of using that SBC. I
would say that now the microSD, while being cheap and plentiful, and easy to
replace, is now the biggest bottleneck for the Pi. It&#39;s great for tinkering and
quick testing, breaking and reflashing, but for running sustained loads, and
in general using the Pi as &amp;quot;prod&amp;quot;, go with a drive.&lt;/p&gt;
&lt;p&gt;All in all I paid 16EUR for the HAT (with taxes and shipping) and a similar
amount of money for the drive. Smaller drives can be bought for half that or
even less.&lt;/p&gt;
&lt;p&gt;On one hand, is not an insignificant amount of money to be spend already on top
of the cash that the Pi itself costs, but on the other hand, you get much, much
better performance from the hardware you already have.&lt;/p&gt;
&lt;p&gt;I have to say, I am excited for that future holds. If everything goes well, I
should be back in the astro game on a whole new level. And even if that does not
work out, I now have a very performant SBC that I can use for other tasks.&lt;/p&gt;
&lt;p&gt;What do you think about all this? I&#39;d love to hear your thoughts, in the comments
below, via email, or on Mastodon. Links in the footer.&lt;/p&gt;
&lt;p&gt;Thanks for reading!&lt;/p&gt;
</description>
      <pubDate>Sat, 19 Oct 2024 24:00:00 GMT</pubDate>
      <dc:creator>STFN</dc:creator>
      <guid>https://stfn.pl/blog/49-my-pi5-got-a-hat/</guid>
    </item>
    <item>
      <title>Testing the performance of an NVMe HAT in BOINC</title>
      <link>https://stfn.pl/blog/50-pi5-nvme-performance-in-boinc/</link>
      <description>&lt;p&gt;In the &lt;a href=&quot;https://stfn.pl/blog/49-my-pi5-got-a-hat/&quot;&gt;previous post&lt;/a&gt; I talked about
getting an NVME HAT for my Pi 5. After seeing how much faster the Pi 5 became
when ditching the SD card, I thought I might see if the new storage makes any
difference in BOINC.&lt;/p&gt;
&lt;p&gt;In short, BOINC is a framework that allows scientists in need of processing
power to send work to volunteers. The computers of the volunteers then crunch
the work units and send back the results back to the scientists for further
analysis. This way ordinary people can help push science forward, by sharing
their mostly idling computers. I&#39;ve been participating in BOINC for almost four
years now.&lt;/p&gt;
&lt;p&gt;I talked more about BOINC in &lt;a href=&quot;https://stfn.pl/blog/10-my-thoughts-on-boinc/&quot;&gt;My thoughts as I reach 100
million points in BOINC&lt;/a&gt; and &lt;a href=&quot;https://stfn.pl/blog/39-another-boinc-milestone/&quot;&gt;I reached 150 million
points in BOINC&lt;/a&gt; blog posts.&lt;/p&gt;
&lt;p&gt;A year ago I got my Pi 5 and tested its performance in BOINC against the Pi 4B.
The results have shown that the Pi5 is indeed much faster than the previous
generation of the popular SBC:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn.pl/blog/17-rpi4-rpi5-boinc/&quot;&gt;RPi 4b vs RPi 5 benchmark in BOINC - Einstein@Home&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn.pl/blog/18-rpi4-rpi5-asteroids/&quot;&gt;RPi 4b vs RPi 5 benchmark in BOINC - Asteroids@Home&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn.pl/blog/19-rpi4-rpi5-universe/&quot;&gt;RPi 4b vs RPi 5 benchmark in BOINC - Universe@Home&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Now it&#39;s time to see if the NVMe HAT will make the Pi 5 even faster.&lt;/p&gt;
&lt;p&gt;&lt;strong class=&quot;dark:text-white&quot;&gt;And the tl;dr answer is: it depends on the
project.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/50/pi5nvme.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/50/pi5nvme.jpg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;Maybe not the cleanest setup, but hey, it worked&lt;/span&gt;&lt;/p&gt;
&lt;h2&gt;Test Setup&lt;/h2&gt;
&lt;p&gt;The test was done using a Raspberry Pi 5, with the active cooler, official Rpi 5
power supply, and with an NVMe hat from Pineboards. The NVMe drive is a KIOXIA
512GB 2242 SSD. I also added a 80mm fan powered from the Pi&#39;s USB port for
enhanced cooling. Throughout the test, the Pi&#39;s CPU temperature stayed at around
60C.&lt;/p&gt;
&lt;p&gt;I crunched on all four cores. I did not overclock the Pi&#39;s CPU.&lt;/p&gt;
&lt;h2&gt;Einstein@Home&lt;/h2&gt;
&lt;p&gt;In &lt;a href=&quot;https://einsteinathome.org/&quot;&gt;Einstein@Home&lt;/a&gt;, the HAT does make a difference
of around 30 minutes per task. There were no problems with the cooling, the
slowly turning 80mm fan combined with the active cooler resulted in a quiet and
cool operation of the testing rig. Not much else to report, so here are the
diagrams:&lt;/p&gt;
&lt;h3&gt;Average tasks time&lt;/h3&gt;
&lt;p&gt;For the 4b, the average tasks time was 16353 seconds (4 hours, 32 minutes and 33
seconds). For the Pi 5, the average task time was 10858 seconds (3 hours and 58
seconds), a decrease of around 33%. The average task time for the Pi 5 + NVMe
was 8055 seconds (2 hours, 14 minutes and 15 seconds), around 30% faster
in comparison with the Pi 5, and 67% faster than the Pi 4.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/50/einstein-avg.png&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/50/einstein-avg.png&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Minimum task time&lt;/h3&gt;
&lt;p&gt;For the 4b, the shortest task took 13866 seconds (3 hours, 51 minutes and 6
seconds). For the Pi 5, the shortest task time took 10207 seconds (2 hours, 50
minutes and 7 seconds), a decrease of around 26%. The minimum task time for the Pi 5 + NVMe
was 7673 seconds (2 hours, 7 minutes and 53 seconds), around 28% faster
in comparison with the Pi 5, and 57% faster than the Pi 4.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/50/einstein-min.png&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/50/einstein-min.png&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Maximum task time&lt;/h3&gt;
&lt;p&gt;For the 4b, the longest task took 17148 seconds (4 hours, 45 minutes and 48
seconds). For the Pi 5, the longest task took 11370 seconds (3 hours, 9 minutes
and 30 seconds), a decrease of around 33%. The maximum task time for the Pi 5 + NVMe
was 8624 seconds (2 hours, 23 minutes and 44 seconds), around 27% faster
in comparison with the Pi 5, and 66% faster than the Pi 4.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/50/einstein-max.png&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/50/einstein-max.png&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Average tasks per day&lt;/h3&gt;
&lt;p&gt;A day has 86400 seconds. Both Pi 4 and Pi 5 have four cores, so they have 345600
core-seconds per day. Dividing that number by the average task time, the results
are 21 tasks per day for the Pi4 and 31 tasks per day for the Pi 5, the Pi 5 can
crunch 47% more tasks per day than the Pi 4. For the Pi 5 with NVMe, the result
is 42 tasks per day, 30% more than the Pi 5, and two times more than the Pi 4.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/50/einstein-tpd.png%22&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/50/einstein-tpd.png&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Asteroids@Home&lt;/h2&gt;
&lt;p&gt;After finishing with Einstein@Home, I switched to another projects which also
supports ARM tasks: &lt;a href=&quot;https://asteroidsathome.net/boinc/home.php&quot;&gt;Asteroids@Home&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;As previously, I started with the default settings, added the project in BOINC
Manager, and the Pi 5 began churning through the tasks.&lt;/p&gt;
&lt;p&gt;The first thing I spotted is that I had to pump up the cooling, as the temperatures
on the CPU rose quickly, and the Pi&#39;s active cooler sped up to become very
audible. To mitigate this, I switched the 80mm fan from using the 5V USB, to a
12V barrel jack power brick. That kept both the temperatures and the noise under
control, an 80mm fan running at high speed is much more acceptable in terms of
noise than a 40mm one.&lt;/p&gt;
&lt;p&gt;After crunching around 40 workunits I realized that there is basically no
difference in the runtime. The runtimes were around 3% shorter than when using the SD
card.&lt;/p&gt;
&lt;p&gt;Another thing that raised my curiosity was that the application version differed
from the one I crunched a year ago. I asked in the project&#39;s forums if those two
are actually comparable, and the answer was that yes, they are similar. However,
there is a new, optimized version of the crunching application, which should
give much better performance. It should be already provided to all users, but
somehow that was not the case. But I could switch to it manually. This would not
only allow to squeeze even more performance from the Pi, but also ensure that I
am really comparing apples to apples. Or raspberries to raspberries.&lt;/p&gt;
&lt;p&gt;Switching to the optimized version of the application was very simple, all it
took was adding an app_info.xml file in the project&#39;s folder
(&lt;code&gt;/var/lib/boinc/projects/asteroidsathome.net_boinc&lt;/code&gt; for Linux) and
downloading the app file. Note that the BOINC folder belongs to the root user,
so you will need root permissions to do so. Here&#39;s my app_info.xml file in full:&lt;/p&gt;
&lt;pre class=&quot;language-xml&quot;&gt;&lt;code class=&quot;language-xml&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;app_info&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&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;app&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&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;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;period_search&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;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&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;user_friendly_name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Period Search Application&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;user_friendly_name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&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;non_cpu_intensive&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;0&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;non_cpu_intensive&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&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;app&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&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;file&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&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;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;period_search_10220_aarch64-unknown-linux-gnu&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;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&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;executable&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&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;file&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&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;app_version&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&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;app_name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;period_search&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;app_name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&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;version_num&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;10220&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;version_num&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&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;platform&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;aarch64-unknown-linux-gnu&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;platform&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&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;avg_ncpus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;1.000000&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;avg_ncpus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&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;api_version&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;7.17.0&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;api_version&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&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;file_ref&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&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;file_name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;period_search_10220_aarch64-unknown-linux-gnu&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;file_name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&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;main_program&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&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;file_ref&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&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;app_version&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;p&gt;&lt;a href=&quot;https://asteroidsathome.net/boinc/forum_thread.php?id=923&quot;&gt;Here&#39;s the link to the forum thread.&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Thank you ahorek&#39;s team and Keith Myers for all your help! :)&lt;/p&gt;
&lt;p&gt;And so I crunched the optimized version as well, both with and without the NVMe
HAT, and the results were identical: there is no significant difference when
switching the storage type.&lt;/p&gt;
&lt;p&gt;So, here are the results for the average task time:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Raspberry Pi 4: 38010 seconds (10 hours, 33 minutes and 30 seconds) seconds&lt;/li&gt;
&lt;li&gt;Raspberry Pi 5, default app, SD card: 11953 seconds (3 hours, 19 minutes and 13 seconds)&lt;/li&gt;
&lt;li&gt;Raspberry Pi 5, default app, NVMe HAT: 11661 seconds (3 hours, 14 minutes and 21 seconds)&lt;/li&gt;
&lt;li&gt;Raspberry Pi 5, optimized app, SD card: 6717 seconds (1 hour, 51 minutes and 57 seconds)&lt;/li&gt;
&lt;li&gt;Raspberry Pi 5, optimized app, NVMe HAT: 6593 seconds (1 hour, 49 minutes and 53 seconds)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/50/asteroids-avg.png&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/50/asteroids-avg.png&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I strongly suspect, that in contrast to Einstein&#39;s application, the Asteroids&#39; one
does much less disk I/O during operation, and so does not depend on the speed of
the storage. The increased thermal load on the CPU would also suggest that it is
much more utilized, and therefore is the biggest bottleneck here.&lt;/p&gt;
&lt;h2&gt;Universe@Home&lt;/h2&gt;
&lt;p&gt;Sadly, U@H is still suspended and it&#39;s future is unknown.&lt;/p&gt;
&lt;h2&gt;Final Words&lt;/h2&gt;
&lt;p&gt;Here are the detailed benchmark results in ODS file format:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/50/asteroids_benchmark_data.ods&quot;&gt;Asteroids@Home&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/50/einstein_benchmark_data.ods&quot;&gt;Einstein@Home&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;So, should you buy the NVMe HAT?&lt;/p&gt;
&lt;p&gt;If you use your Pi 5 not only as a BOINC cruncher: a strong yes. As I wrote in
the previous blog post, it makes a large difference in the Pis overall
responsiveness and performance.&lt;/p&gt;
&lt;p&gt;If you use your PI 5 only as a BOINC cruncher: It depends. If you are a fan of
E@H and only crunch that project, then I would say go for it, a 30% improvement
in crunching speed is a lot. If you prefer Asteroids, it won&#39;t make any
difference. And I hope that one day I will be able to again crunch Universe@Home
and see how it fares with better storage.&lt;/p&gt;
&lt;p&gt;I think that for now it&#39;s the end of benchmarking the Pi 5. The SBC will now
wait for me recreating my astrophotography setup, and once it is done, it will
go atop my mount to control the acquisition process. And that will be a topic of
another blog post.&lt;/p&gt;
&lt;p&gt;P.S. Just before publishing this post, Pineboards.io &lt;a href=&quot;https://pineboards.io/blogs/news/introducing-the-hatdrive-poe&quot;&gt;announced that they will
be releasing an NVMe + PoE
HAT&lt;/a&gt;, and that
makes me double excited, as I am planning to dive into the world of Power of
Ethernet when I build a new iteration of my homelab.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;March 2026: Sadly, Pineboards is no more, they closed their online store at the
beginning of 2025.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Thanks for reading!&lt;/p&gt;
</description>
      <pubDate>Thu, 31 Oct 2024 24:00:00 GMT</pubDate>
      <dc:creator>STFN</dc:creator>
      <guid>https://stfn.pl/blog/50-pi5-nvme-performance-in-boinc/</guid>
    </item>
    <item>
      <title>I started using ESPhome and now I have a local smarthome</title>
      <link>https://stfn.pl/blog/51-introduction-to-esphome-with-projects/</link>
      <description>&lt;p&gt;I have been slowly getting into the field of smart home solutions. I am taking
my time, because I do not want to fall into the typical pitfalls of the &amp;quot;easy&amp;quot;
smarthome. First of them being vendor lock-in, which becomes especially bad
when that vector bankrupts and all of their devices become pretty bricks. And
second being privacy and data security. I don&#39;t want my devices to talk to some
random servers, sharing data they should not have access to in the first place.&lt;/p&gt;
&lt;p&gt;Also I am big fan of FOSS solutions, and I don&#39;t mind making stuff
myself, both with a soldering iron, and in the command line.&lt;/p&gt;
&lt;p&gt;And it looks like I found something that ticks all the boxes:
&lt;a href=&quot;https://esphome.io/&quot;&gt;ESPHome&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;In the tl;dr version, ESPhome is a tool that provides a simple way to configure
microcontrollers, allowing to use them with Home Assistant or other smart home
software.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/51/ha.png&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/51/ha.png&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;ESPhome enviroment sensor visible in Home Assistant&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Now, ESPhome supports a metric ton of devices and accessories like sensors or
controllers, so I won&#39;t be diving into those details, instead I want to show how it
works by showing two ESPhome projects that I did: a relay, and an environment
sensor, both connected to Home Assistant.&lt;/p&gt;
&lt;h2&gt;Initial State&lt;/h2&gt;
&lt;p&gt;Everything written below assumes that you already have a working Home Assistant
running in your home server, and you know a little bit about the command line.&lt;/p&gt;
&lt;h2&gt;The Hardware&lt;/h2&gt;
&lt;p&gt;ESPHome supports ESP32 and ESP8266 microcontrollers. It also officially supports the RP2040
devices, such as the Raspberry Pi Pico, but I had multiple problems making them
work, that will be a topic for another post.&lt;/p&gt;
&lt;p&gt;For the first project I am using an ESP-wroom-32 microcontroller based on the ESP32 chip. You
can get them literally dirt cheap, for a few euros each, and they will work just
fine with ESPhome. I&#39;ve seen them as cheap as 3 EUR / 15 PLN on aliexpress, and
just a bit more expensive at local retailers.&lt;/p&gt;
&lt;p&gt;For the second project I went with the &lt;a href=&quot;https://wiki.seeedstudio.com/XIAO_ESP32C3_Getting_Started/&quot;&gt;Seeed Studio XIAO
ESP32C3&lt;/a&gt;. It is more
modern and advanced than the wroom-32, with a USB-C connector and an external
Wi-Fi &amp;amp; Bluetooth antenna. I came across it when browsing one of the electronic
shops where I usually buy parts for my projects, and I was impressed by its
capabilities.&lt;/p&gt;
&lt;p&gt;It&#39;s tiny, the size of a typical coin, yet it supports all of the typical
microcontroller interfaces, like SPi, ADC or I2C. It has a built-in USB-C
connector for programming, and as for wireless connectivity, Wi-Fi and Bluetooth
are also on board. But the thing I liked most is that it has a charge controller
for li-ion batteries, which means you can connect a battery straight to the
board, and charge it via the USB-C. It supports any 3.7V li-ion battery, and I
went with a typical 18650. It&#39;s more expensive than the wroom one, but still in
the acceptable range, at 5 EUR / 30 PLN.&lt;/p&gt;
&lt;p&gt;The biggest downside for me is that the battery connector pads are tiny and are
very difficult to solder to, a JST connector would be a much better solution.&lt;/p&gt;
&lt;h3&gt;The Relay&lt;/h3&gt;
&lt;p&gt;The relay that I am using is an IDUINO single channel relay that can handle
mains voltage and can be bought for as little as 3 EUR.&lt;/p&gt;
&lt;h3&gt;The Sensor&lt;/h3&gt;
&lt;p&gt;As for the sensor I went with a BME280, which is a combined temperature,
pressure and humidity sensor. It&#39;s the same sensor I used in my Solar Weather
Station project.&lt;/p&gt;
&lt;h2&gt;Setting up ESPhome&lt;/h2&gt;
&lt;p&gt;There are multiple ways to install ESPhome, I went with the most &amp;quot;oldschool&amp;quot;
way, installing it manually from the command line. It&#39;s also the simplest one to
set up, as it only requires having Python on your local machine.&lt;/p&gt;
&lt;p&gt;Everything is described in the &lt;a href=&quot;https://esphome.io/guides/installing_esphome&quot;&gt;installation
instructions&lt;/a&gt;, but here&#39;s the
recap:&lt;/p&gt;
&lt;p&gt;Create a Python virtual environment (it&#39;s considered a good practice to always
create one when working with Python)&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;python3 &lt;span class=&quot;token parameter variable&quot;&gt;-m&lt;/span&gt; venv venv&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, activate it, and install the required dependency and the package itself&lt;/p&gt;
&lt;p&gt;For Linux:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;source&lt;/span&gt; venv/bin/activate
pip &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; wheel
pip &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; esphome&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;for Windows:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;venv/Scripts/activate
pip &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; wheel
pip &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; esphome&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And here you go, ESPhome is installed and ready to be run. Before we run it,
though, let&#39;s prepare the hardware.&lt;/p&gt;
&lt;p&gt;Of course, those are just examples, you can mix and match hardware as you wish.&lt;/p&gt;
&lt;h2&gt;Project 1: ESP32 relay&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/51/IMG_8968.JPG&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/51/IMG_8968.JPG&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;ESP32 connected with the relay&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;The esp-wroom-32 and the IDUINO relay both come with GPIO headers already
soldered, so there is no need to do any additional soldering. All that you need
is three female-to-female jumper cables.&lt;/p&gt;
&lt;p&gt;The relay has three connectors, negative, positive, and control.&lt;/p&gt;
&lt;p&gt;In this &lt;a href=&quot;https://www.espressif.com/sites/default/files/documentation/esp32-wroom-32_datasheet_en.pdf&quot;&gt;PDF
documentation&lt;/a&gt;
you can find the pinout diagram for the ESP-wroom-32&lt;/p&gt;
&lt;p&gt;The positive needs to go to the VIN pin on the ESP32, this is the pin that
provides 5V output for powering the relay. The negative needs to be connected to
one of the ground pins, there&#39;s one next to the VIN pin, so we can use this one.
Finally, the control pin needs to be connected to any of the pins on the ESP32
that can be controlled. GPIO13 is right next to the VIN and GND pins, so let&#39;s
use this one and have all three cables connected next to each other.&lt;/p&gt;
&lt;p&gt;For now you can leave the other side of the relay not connected to anything as
we are testing our project.&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: red&quot;&gt;You can connect mains voltage (120V-220V) to that
relay, but remember that working with such high voltage is dangerous and can
kill you. Proceed only if you know what you are doing, and at your own risk. Be
reasonable.&lt;/span&gt;&lt;/p&gt;
&lt;h3&gt;ESPhome setup&lt;/h3&gt;
&lt;p&gt;After connecting the relay, connect the ESP32 microncontroller to your computer
using a micro-USB cable. With the Python virtual env activated, run:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;esphome wizard livingroom-relay.yaml&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For the board type select &lt;code&gt;esp32dev&lt;/code&gt;. Fill in the Wi-Fi information. Once the
wizard is done, a file &lt;code&gt;livingroom-relay.yaml&lt;/code&gt; will be created. Open it with
your text editor of choice.&lt;/p&gt;
&lt;p&gt;At the bottom of the file add:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;switch:
  - platform: gpio
    name: &lt;span class=&quot;token string&quot;&gt;&quot;Switch 1&quot;&lt;/span&gt;
    pin: GPIO13&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Change the name to something more descriptive :)&lt;/p&gt;
&lt;p&gt;Once this is done, run&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;esphome run livingroom-relay.yaml&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If this is the first time you are installing ESPhome on that device, the script
will install the required software and copy the yml file to the device. This
requires the USB cable. Subsequent updates can be done via Wi-Fi, another cool
feature of ESPhome.&lt;/p&gt;
&lt;p&gt;Once the installation is done, the board will connect to WiFi, the logs will
tell what is happening. If all finished successfully, the next step is to go
Home Assistant.&lt;/p&gt;
&lt;p&gt;Now the magic should happen, and HA should automatically find ESPhome and notify
you that a new integration is available. If that didn&#39;t happen, refresh the page
after a few minutes, or try to add the integration manually from the integration
settings.&lt;/p&gt;
&lt;p&gt;Once the integration is added, a new relay toggle button should appear on the HA
dashboard, with the name that you provided in the yml file.&lt;/p&gt;
&lt;p&gt;Clicking on the toggle button activates the relay, an audible click should be
heard as it turns on and off. And in effect, you are able to control dumb
devices, even those running on mains voltage, like lamps or fans, from your Home
Assistant. And everything happens locally without shady cloud software.&lt;/p&gt;
&lt;div class=&quot;video&quot; style=&quot;position:relative;padding-top:56.25%;&quot;&gt;&lt;iframe src=&quot;https://player.mediadelivery.net/embed/626472/bf57de7f-3c6e-4258-b9ac-a8dd4546a5dd?autoplay=false&amp;loop=true&amp;muted=false&amp;preload=true&amp;responsive=true&quot; loading=&quot;lazy&quot; style=&quot;border:0;position:absolute;top:0;height:100%;width:100%;&quot; allow=&quot;accelerometer;gyroscope;autoplay;encrypted-media;picture-in-picture;&quot; allowfullscreen=&quot;true&quot;&gt;&lt;/iframe&gt;&lt;/div&gt;
&lt;span class=&quot;image_sub&quot;&gt;The relay in action&lt;/span&gt;
&lt;h2&gt;Project 2: Seeed Studio Environment Sensor&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/51/IMG_8973.JPG&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/51/IMG_8973.JPG&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;Seeed Studio connected with the BME280 sensor&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;For this project the build process was a bit more involved, as it required
soldering the pin headers to both the ESP board and the BME280 sensor.&lt;/p&gt;
&lt;p&gt;Then, with two small wires, I connected the 18650 battery case to the pins on
the downside of the board. This was not a pro gamer move, soldering the battery
pins would be much easier if I did before adding the pin headers. Anyway, with
some complicated soldering iron gymnastics, I managed to make it work.&lt;/p&gt;
&lt;p&gt;Next was the Wi-Fi &amp;amp; Bluetooth antenna with its tiny connector. The external
antenna is another cool feature of that board.&lt;/p&gt;
&lt;p&gt;I connected the board with the sensor using female-to-female jumper cables.&lt;/p&gt;
&lt;p&gt;The BME280 sensor I am using support I2C connectivity, to this is the protocol I
went with. Connecting it with the board requires four cables: VCC, GND, SCL and
SDA.&lt;/p&gt;
&lt;p&gt;Here&#39;s the pinout diagram for the Seeed board:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/51/seedpinout.png&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/51/seedpinout.png&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I connected the VCC of the sensor with the 3.3V output pin on the board. GND with
the GND pin. SDA to the GPIO6 pin, and SCL to the GPIO7 pin. And the hardware part is done.&lt;/p&gt;
&lt;h3&gt;ESPhome setup&lt;/h3&gt;
&lt;p&gt;Most parts of the setup are identical as in the first project, so I&#39;ll just
glide over them.&lt;/p&gt;
&lt;p&gt;Create a new file using the wizard. As the board type, select
&lt;code&gt;seeed_xiao_esp32c3&lt;/code&gt;. When the yml file is created, at the bottom of it add&lt;/p&gt;
&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;i2c&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;sda&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; GPIO6
  &lt;span class=&quot;token key atrule&quot;&gt;scl&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; GPIO7
  &lt;span class=&quot;token key atrule&quot;&gt;scan&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean important&quot;&gt;true&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; bus_a

&lt;span class=&quot;token key atrule&quot;&gt;sensor&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 key atrule&quot;&gt;platform&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; bme280_i2c      
    &lt;span class=&quot;token key atrule&quot;&gt;address&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0x76&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;temperature&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Temperature&quot;&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;pressure&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Pressure&quot;&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;humidity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Humidity&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This snippet first configures the I2C connection and sets up the BME280. The output
will have clear, human-readable names.&lt;/p&gt;
&lt;p&gt;Update the file with the provided snippet, run the upload, and again, once the microcontroller is
running with the new code, HA should discover it automagically.&lt;/p&gt;
&lt;p&gt;Now the cool thing with that board is that you can leave it plugged in and it
will charge the 18650 battery in the soldered in battery case. After a few hours
of charging, you can plug it off and the board will run under its own power.&lt;/p&gt;
&lt;p&gt;From my experience, under default settings and with a good 18650, it reach 5
days of uptime. With more cells, and with longer times between readings (it can
be configured in the yml file), it should reach multiples of that runtime.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/51/IMG_8970.JPG&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/51/IMG_8970.JPG&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;The full package with the battery and the wireless antenna&lt;/span&gt;&lt;/p&gt;
&lt;h2&gt;ESPhome Dashboard&lt;/h2&gt;
&lt;p&gt;ESPhome has another cool trick up its sleeve.&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;esphome dashboard &lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Running the command above in the folder where the config files are stored will
start a webserver. Opening the provided link in the browser will take you to a
dashboard that shows the currently online ESPhome nodes, and the ability to edit
their config upload it over Wi-Fi. Super useful stuff when the nodes are in hard
to reach places, or are firmly secured.&lt;/p&gt;
&lt;h2&gt;Bottom Text&lt;/h2&gt;
&lt;p&gt;This has been just scratching the surface of what ESPhome is capable of.
Browsing the docs shows that it can support a lot of different boards and
sensors and controllers.&lt;/p&gt;
&lt;p&gt;One interesting project that I found is controlling cooling fans:
&lt;a href=&quot;https://github.com/patrickcollins12/esphome-fan-controller&quot;&gt;esphome-fan-controller&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I feel that ESPhome will be very useful to me once I have my own house. The
possibility of having home automation without relying on shady third party
software, but instead on one&#39;s own coding and soldering skills is extremely
tempting for me.&lt;/p&gt;
&lt;p&gt;And on the other hand, ESPhome in my opinion significantly reduces the entry
barrier for less technical people by allowing relatively easy means of
programming and configuration of microcontrollers. And with the right hardware,
like the Grove sensors, soldering is not even required.&lt;/p&gt;
&lt;p&gt;And that&#39;s it, if I have any new projects with ESPhome, I will for sure share
them with you.&lt;/p&gt;
&lt;p&gt;Thanks for reading!&lt;/p&gt;
</description>
      <pubDate>Wed, 13 Nov 2024 24:00:00 GMT</pubDate>
      <dc:creator>STFN</dc:creator>
      <guid>https://stfn.pl/blog/51-introduction-to-esphome-with-projects/</guid>
    </item>
    <item>
      <title>I published my first package to PyPi</title>
      <link>https://stfn.pl/blog/52-published-my-first-python-package-to-pypi/</link>
      <description>&lt;p&gt;&lt;a href=&quot;https://pypi.org/project/zambretti-py/&quot;&gt;Zambretti-py on PyPi&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;As long-time readers of my blog may know, I like gathering environmental data
like temperature and pressure. And some time ago I started thinking, if I
am able to gather such data over a timespan, maybe I could use it for local
forecasting of weather? I know that real forecasting requires a lot of data
and massive computation, but maybe there is something simple for short-term
forecasts?&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/52/Zambretti_Forecaster.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/52/Zambretti_Forecaster.jpg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;A physical Zambretti Forecaster, image taken from &lt;a href=&quot;https://en.wikipedia.org/wiki/Zambretti_Forecaster&quot;&gt;Wikipedia&lt;/a&gt;,
copyright CC BY-SA 4.0&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;A quick startpaging* presented me with the &lt;a href=&quot;https://en.wikipedia.org/wiki/Zambretti_Forecaster&quot;&gt;Zambretti
algorithm&lt;/a&gt;, a relatively
simple way to forecast the weather for the next hours, using the current
temperature and a history of changes in atmospheric pressure. Looked like
something perfect for my basic use case.&lt;/p&gt;
&lt;p&gt;&lt;small&gt;*&lt;a href=&quot;https://www.startpage.com/&quot;&gt;Startpage&lt;/a&gt; is currently my favourite web search engine.&lt;/small&gt;&lt;/p&gt;
&lt;p&gt;Now, it looks simple and useful, surely somebody already implemented it in
Python!&lt;/p&gt;
&lt;p&gt;And turns out that no, there had been no Python implementation of the Zambretti
algorithm on PyPi. And don&#39;t call me Shirley.&lt;/p&gt;
&lt;p&gt;I dropped the topic for a few weeks, but one day I had an idea... if nobody
made a Python package for it, maybe I could do it? And publish it? That
would be cool, a learning experience, and having my own package on PyPi
would be another step in my Python development ladder.&lt;/p&gt;
&lt;h2&gt;The code&lt;/h2&gt;
&lt;p&gt;The code can be found on &lt;a href=&quot;https://github.com/thestfndev/zambretti-py&quot;&gt;GitHub -
zambretti-py&lt;/a&gt;. I usually prefer
&lt;a href=&quot;https://codeberg.org/stfn&quot;&gt;Codeberg&lt;/a&gt;, but this time I went with GH, mostly for
the easily available Actions.&lt;/p&gt;
&lt;p&gt;And so I sat down and started writing my own package. I decided to start small
and simple, without any dependencies, using only what is available in the
Python Standard Library.&lt;/p&gt;
&lt;p&gt;My main source of inspiration was &lt;a href=&quot;https://github.com/sassoftware/iot-zambretti-weather-forcasting&quot;&gt;this
repo&lt;/a&gt;, I took
some of the code snippets from it and converted them into Python.&lt;/p&gt;
&lt;p&gt;Making it into a package for anyone to use raised for me some interesting
questions regarding the project structure and imports. I went with imports
in the &lt;code&gt;__init__.py&lt;/code&gt; file to simplify importing when using the package.&lt;/p&gt;
&lt;p&gt;The biggest issue with this code is that it can be better. Showing your work
publicly is never easy, and impostor syndrome is strong with this one. And
on top of that are the thoughts of &amp;quot;you are called a senior developer at work,
how can you now know how to make it better?!&amp;quot;. So thank you to all my
Fediverse friends who encouraged me to actually carry on and publish it.&lt;/p&gt;
&lt;p&gt;And I can always make it better with time, right? The package is currently
at version 0.0.4, there is still a long way to reach 1.0.0.&lt;/p&gt;
&lt;p&gt;I have plans on how to improve it, probably at some point I will drop the
&amp;quot;uses only the standard library&amp;quot; idea. I&#39;m thinking of using &lt;code&gt;Pydantic&lt;/code&gt; for
data storage and validation, &lt;code&gt;Pydantic&lt;/code&gt; has been one of my favourite Python
modules lately.&lt;/p&gt;
&lt;p&gt;I would love to see somebody made a PR with a suggested improvement, it&#39;s
always good to see other people&#39;s ideas.&lt;/p&gt;
&lt;h2&gt;Publishing on PyPi&lt;/h2&gt;
&lt;p&gt;I started my plan for publishing on PyPi with asking the Fediverse people for
tips on how to do it, and kindly enough, I received a lot of replies with
great feedback. This is what I really enjoy about Fedi, people there are not
only knowledgeable, but also helpful &amp;lt;3&lt;/p&gt;
&lt;p&gt;You can read &lt;a href=&quot;https://fosstodon.org/@stfn/113368257012509260&quot;&gt;the conversation in full
here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;As with the code I decided to start slow, small, and simple.&lt;/p&gt;
&lt;p&gt;I have to praise the documentation available at packaging.python.org, it is
clear, well written, and provides good examples. Exactly what good
documentation should have.&lt;/p&gt;
&lt;p&gt;My main source of knowledge was &lt;a href=&quot;https://packaging.python.org/en/latest/tutorials/packaging-projects/&quot;&gt;this tutorial on
packaging&lt;/a&gt;
which is a concise version of &lt;a href=&quot;https://packaging.python.org/en/latest/flow/&quot;&gt;a more in-depth
documentation&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Going along the tutorial I modified the structure of the project, added the
required &lt;code&gt;pyproject.toml&lt;/code&gt; file, and filled it with the metadata.&lt;/p&gt;
&lt;p&gt;For testing, I registered at &lt;a href=&quot;https://test.pypi.org/&quot;&gt;Test PyPi&lt;/a&gt;, created a token, and with
a imaginary drumroll, I published &lt;code&gt;zambretti-py&lt;/code&gt; to the test repository with
a single command in the terminal. And it worked! I was amazed how simple and
straightforward the whole process is. Publishing to a test repo allowed to
straighten some things with naming and metadata.&lt;/p&gt;
&lt;h2&gt;Going to Prod&lt;/h2&gt;
&lt;p&gt;After resolving the minor issues that I found after publishing to the test env,
and after another round of code refactoring (you can never have enough
refactoring), I decided to jump on the deep end and go with Prod, the real PyPi.&lt;/p&gt;
&lt;p&gt;The publishing process was virtually the same, I registered at pypi.org, set up
a token and used the same commands, just without the bits defining the testing
repository.&lt;/p&gt;
&lt;p&gt;Alas! &lt;a href=&quot;https://pypi.org/project/zambretti-py/&quot;&gt;zambretti-py&lt;/a&gt; is now live! Other
Python developers can now find it and use it in their projects, I made a tiny
contribution to the Python ecosystem and that felt and still feels great.&lt;/p&gt;
&lt;h2&gt;Switching to GitHub Actions&lt;/h2&gt;
&lt;p&gt;After publishing I manually I looked for ways to streamline the publishing
process and I found that GitHub provides a ready to use &lt;a href=&quot;https://github.com/thestfndev/zambretti-py/blob/main/.github/workflows/python-publish.yml&quot;&gt;Action that publishes
packages to
PyPi&lt;/a&gt;.
The only required configuration is setting the secret for the PyPi token. The
action is run on every creation of a release on GH.&lt;/p&gt;
&lt;p&gt;I&#39;m used to using GH Actions and releases at work, but having to use the
releases mechanism for my own project is something new, and another learning opportunity.&lt;/p&gt;
&lt;h2&gt;Next steps&lt;/h2&gt;
&lt;p&gt;The closest next step is to improve the code, obviously :)&lt;/p&gt;
&lt;p&gt;In the conversation in the Fediverse about publishing, many people mentioned
something called &lt;a href=&quot;https://blog.pypi.org/posts/2023-04-20-introducing-trusted-publishers/&quot;&gt;Trusted
Publishers&lt;/a&gt;
and this is something I want to take a closer look.&lt;/p&gt;
&lt;h2&gt;Bottom Text&lt;/h2&gt;
&lt;p&gt;Even though it is just a small and simple package, the whole process has been a
great learning opportunity for me. Packaging in Python is something totally new
to me, and also I had to refresh my knowledge on GitHub Actions and releases.&lt;/p&gt;
&lt;p&gt;I&#39;m hoping I will continue this journey, and learn even more new things as I
maintain &lt;code&gt;zambretti-py&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;And I am happy to be a tiny part of the FOSS contributors community.&lt;/p&gt;
&lt;p&gt;If you are also thinking of publishing your package to PyPi, do it! It can be
scary at the beginning, but all in all, it is something that anyone can do
easily, and there is nothing stopping you.&lt;/p&gt;
&lt;p&gt;I would like to again thank everyone on the Fediverse who gave me ideas,
feedback and nice words, and answered my questions, you people rock.&lt;/p&gt;
&lt;p&gt;P.S. I wrote most of this blog post in Neovim running in tmux. I&#39;m really
getting into those two tools, and using them instead of VSCode sparks joy in
me. Probably soon I will make a post specifically on how I am using them and
how do my configuration files look like.&lt;/p&gt;
&lt;p&gt;Thanks for reading!&lt;/p&gt;
</description>
      <pubDate>Tue, 19 Nov 2024 24:00:00 GMT</pubDate>
      <dc:creator>STFN</dc:creator>
      <guid>https://stfn.pl/blog/52-published-my-first-python-package-to-pypi/</guid>
    </item>
    <item>
      <title>How I started my GoToSocial instance in the Fediverse</title>
      <link>https://stfn.pl/blog/53-starting-gotosocial-instance/</link>
      <description>&lt;p&gt;Ever since I joined the Fediverse a year and a half ago, I had this vague idea
that one day I would have my own instance. And here we are, I am now the owner
and the admin of &lt;a href=&quot;https://fedi.stfn.pl&quot;&gt;fedi.stfn.pl&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/53/fedistfn.png&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/53/fedistfn.png&quot; alt=&quot;Screenshot of the header of the welcome screen of my instance, with text: fedi.stfn.pl
home to 4 users who wrote 30 posts, federating with 1598 other instances&quot;&gt;&lt;/a&gt;&lt;/p&gt;

&lt;span class=&quot;image_sub&quot;&gt;The welcome screen of my instance&lt;/span&gt;
&lt;p&gt;The final push that made me create my own instance was the sad story of
&lt;a href=&quot;https://botsin.space&quot;&gt;botsin.space&lt;/a&gt;, a Fediverse server dedicated to running
bots. For the last year I had been running there my Astrobin Image of the Day
bot. But that now ended as its owner decided that they do not have the time and
means to continue running this popular instance, and that they would be shutting
it down. Gracefully, they provided ample time for migrating before the final
turning off the lights.&lt;/p&gt;
&lt;p&gt;I considered several options, most of them being migrating to other existing
instances, but eventually I decided that this is a perfect motivation for me to
start my own gig. After doing some research and asking a few fellow Fedi people,
I decided to go with &lt;a href=&quot;https://gotosocial.org/&quot;&gt;GoToSocial&lt;/a&gt;, an ActivityPub
server application.&lt;/p&gt;
&lt;p&gt;From reading the docs, GoToSocial seemed like an easy solution to implement, and
it did not require SMTP, something which stopped me from my old plan to
self-host Lemmy. One day I will write about my bad experiences with an SMTP
service. I was also glad to read that GTS requires minimal resources, and so
can be run on a low-tier VPS. Finally, I&#39;ve seen a few people in the Fediverse
running their own instances, and they all seemed happy with the quality of
service.&lt;/p&gt;
&lt;p&gt;OK then, the software is chosen, now for the place to run it. I again turned to
RackNerd, as I am happy with their VPS that I already have, and they have
a BlackFriday (ugh, I know) sale. This post, as any other, is not sponsored by
them, nor by anyone else, but I have an ~&lt;s&gt;affiliate link&lt;/s&gt;~ that you can use,
and if you buy something from them using the link, I will get a tiny
commission. This blog is also running on a RackNerd VPS. I went with a slightly
more powerful machine, with two CPU cores and 3GB of RAM.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;2026 Update: I left RackNerd for Hetzner as part of my move from US to
European services.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The rest of the post will be about the initial setup, configuration and
running of my GoToSocial instance, if you, dear reader, want to do the same
I would suggest reading it in full before following my steps.&lt;/p&gt;
&lt;h3&gt;No usable frontend?&lt;/h3&gt;
&lt;p&gt;Before we start, let&#39;s get one thing out of the way. One thing that was not
clear for me was that, quoting the docs, &amp;quot;Unlike other federated server
projects, GoToSocial doesn&#39;t include an integrated client front-end (i.e., a web
app).&amp;quot; What does that mean?&lt;/p&gt;
&lt;p&gt;It means that you when you go to webpage of your account, for example &lt;a href=&quot;https://fedi.stfn.pl/@stfn&quot;&gt;stfn @
fedi.stfn.pl&lt;/a&gt;, you will not be able to post new
statuses. Also there is no timeline view of the people you follow, there is only
the instance welcome screen. To actually use your account, you need to use a
separate app, the two recommended by GoToSocial are
&lt;a href=&quot;https://semaphore.social/&quot;&gt;Semaphore&lt;/a&gt; in the browser and
&lt;a href=&quot;https://tusky.app/&quot;&gt;Tusky&lt;/a&gt; on mobile. Semaphore is okayish, looks and works
very beta-like, but Tusky I can fully recommend, its the app that I use all the
time on my phone. All in all I would say it&#39;s just a minor inconvenience, and I
understand the GTS devs focusing fully on the backend part.&lt;/p&gt;
&lt;h2&gt;The preparation&lt;/h2&gt;
&lt;p&gt;Once you have your own VPS, you need to get a domain for it. One options is to buy a domain from
your preferred domain provider, I personally use &lt;a href=&quot;https://home.pl&quot;&gt;home.pl&lt;/a&gt;, but from what
I know there&#39;s a ton of them, like NameCheap, or Porkbun. After buying, point
the domain to the IP address of your fresh VPS. The second option, is that if
you already have a domain you can set up a subdomain, which is usually free.
This is what I went with, I have the domain stfn.pl, and for the instance url I
chose fedi.stfn.pl&lt;/p&gt;
&lt;p&gt;Now, the server. I went with the typical setup and hardening of a Linux VPS,
DigitalOcean has a great article on it, which I use everytime I have a new
machine in the cloud: &lt;a href=&quot;https://www.digitalocean.com/community/tutorials/initial-server-setup-with-ubuntu&quot;&gt;DigitalOcean initial server setup&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The outcome is a VPS to which I can ssh into using only ssh keys,
with a regular user with sudo permissions, and the VPS is running a firewall.
The firewall is configured so that the VPS can only be accessed using ports
22 (ssh), 80 (http) and 443 (https)&lt;/p&gt;
&lt;p&gt;A good checkpoint at this moment is to install
&lt;a href=&quot;http://nginx.org/en/docs/install.html&quot;&gt;nginx&lt;/a&gt;, and try opening your domain in
the browser. If everything went fine, you should see a default nginx welcome
screen. If not, one possibility is that you need to wait a bit, it takes time
for DNS to propagate.&lt;/p&gt;
&lt;h2&gt;Installing GoToSocial&lt;/h2&gt;
&lt;p&gt;Here is the documentation for installing GTS:
&lt;a href=&quot;https://docs.gotosocial.org/en/latest/getting_started/installation/&quot;&gt;Installation&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I would suggesting reading it all before starting with the installation, that
will give a clear view of all the things that need to be done.&lt;/p&gt;
&lt;p&gt;I went with a bare metal installation, did not feel like using Docker here. I
also decided to use a separate reverse proxy for handling the incoming traffic.&lt;/p&gt;
&lt;p&gt;I also decided that SQLite will be fine, the docs said that using SQLite is ok
for instances up to 30 users, and I don&#39;t think I will ever pass 10, with most
of them being bots posting once a day or so.&lt;/p&gt;
&lt;p&gt;Here is my &lt;code&gt;config.yml&lt;/code&gt; file in full:&lt;/p&gt;
&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;host&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;fedi.stfn.pl&quot;&lt;/span&gt;

&lt;span class=&quot;token key atrule&quot;&gt;bind-address&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;127.0.0.1&quot;&lt;/span&gt;

&lt;span class=&quot;token key atrule&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;8000&lt;/span&gt;

&lt;span class=&quot;token key atrule&quot;&gt;trusted-proxies&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 string&quot;&gt;&quot;127.0.0.1/32&quot;&lt;/span&gt;

&lt;span class=&quot;token key atrule&quot;&gt;db-type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;sqlite&quot;&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;db-address&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;sqlite.db&quot;&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;storage-local-base-path&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;/gotosocial/storage&quot;&lt;/span&gt;

&lt;span class=&quot;token key atrule&quot;&gt;lets-encrypt-enabled&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean important&quot;&gt;false&lt;/span&gt;

&lt;span class=&quot;token key atrule&quot;&gt;instance-language&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 string&quot;&gt;&quot;en-gb&quot;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;pl&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The default port is 8080, but I went with 8000, as 8080 is the port used by
the &lt;code&gt;nginx-prometheus-exporter&lt;/code&gt;, about which I will talk more in the next post.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;bind-address&lt;/code&gt; and &lt;code&gt;trusted-proxies&lt;/code&gt; are required to be this way for GTS
to work with the reverse proxy. Also &lt;code&gt;lets-encrypt-enabled&lt;/code&gt; is set to &lt;code&gt;false&lt;/code&gt;
as nginx will be handling the TLS certificates, not GoToSocial.&lt;/p&gt;
&lt;p&gt;The final step here was to set up the &lt;code&gt;systemd&lt;/code&gt; service. I have a confession to
make, I am a big fan of systemd, I use it whenever possible to daemonize
services. This is also described in the documentation.&lt;/p&gt;
&lt;p&gt;At this moment, after enabling and starting the systemd service, you should be
able to curl into your instance from your server, but not from anywhere else,
as GTS is running, but will only accept connections from &lt;code&gt;127.0.0.1&lt;/code&gt;,
meaning the same machine. You can test it out by running:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;curl&lt;/span&gt; localhost:8000&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And the response should be the HTML of the instance welcome screen.&lt;/p&gt;
&lt;p&gt;With GoToSocial running, it&#39;s time to configure the reverse proxy. If you did
the previous steps, nginx should be already installed on your server. GoToSocial
provides docs on setting it up: &lt;a href=&quot;https://docs.gotosocial.org/en/latest/getting_started/reverse_proxy/nginx/&quot;&gt;reverse proxy
setup&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;For obtaining TLS certificates I am using &lt;a href=&quot;https://certbot.eff.org/&quot;&gt;Certbot&lt;/a&gt;,
with Certbot the whole job is a single command, and the certificates autorenew
automatically.&lt;/p&gt;
&lt;p&gt;The upside of having a separate reverse proxy is that in the future you will
be able to run other services along GTS on your VPS, and separate the traffic
flowing towards them at the reverse proxy level.&lt;/p&gt;
&lt;p&gt;At this moment you should be able to type your domain in the browser, and see
the welcome screen of your GoToSocial instance.&lt;/p&gt;
&lt;p&gt;If that is not the case, then it&#39;s time for investigation, I would start it in
this order:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# check if GoToSocial is running&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; systemctl status gotosocial.service
&lt;span class=&quot;token comment&quot;&gt;# check if nginx is running&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; systemctl status nginx.service
&lt;span class=&quot;token comment&quot;&gt;# check GoToSocial logs&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; journalctl &lt;span class=&quot;token parameter variable&quot;&gt;-f&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-u&lt;/span&gt; gotosocial.service
&lt;span class=&quot;token comment&quot;&gt;# check nginx logs&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;tail&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-f&lt;/span&gt; /var/log/nginx/access.log&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Each one of those places should tell you something about the state of things.&lt;/p&gt;
&lt;p&gt;But if everything is fine, the instance is running, nothing is burning, you can
proceed with creating the first user. GTS provides a CLI tool for the creation
of users, everything &lt;a href=&quot;https://docs.gotosocial.org/en/latest/getting_started/user_creation/&quot;&gt;is described in the
docs&lt;/a&gt;, but
for the tl;dr crowd, here&#39;s the command:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;./gotosocial --config-path /path/to/config.yaml &lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;
    admin account create &lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;
    &lt;span class=&quot;token parameter variable&quot;&gt;--username&lt;/span&gt; some_username &lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;
    &lt;span class=&quot;token parameter variable&quot;&gt;--email&lt;/span&gt; some_email@whatever.org &lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;
    &lt;span class=&quot;token parameter variable&quot;&gt;--password&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;SOME_PASSWORD&#39;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Read the docs for other info, like promoting the user to be an admin. And
remember to restart the GTS service each time you create a new user:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; systemctl restart gotosocial.service&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After each restart, GTS needs a minute or two to be up and running again, so be
patient and don&#39;t panic :)&lt;/p&gt;
&lt;h2&gt;Using GoToSocial&lt;/h2&gt;
&lt;p&gt;And it&#39;s done. You can now log in, using Tusky, Semaphore or any other
application, provide the username, password, and instance url, and done, you
are a part of the Fediverse from your own domain! It&#39;s that simple.&lt;/p&gt;
&lt;h3&gt;Settings&lt;/h3&gt;
&lt;p&gt;To configure your account and your whole instance, you can login to the settings
page, at {domain}/settings. There you will be able to well, set the settings for
your account, and if you have the admin right, set the settings for the whole
instance. Set the settings, wow, that came out bad. But you know what I mean.
Settings. Config.&lt;/p&gt;
&lt;h3&gt;Federating&lt;/h3&gt;
&lt;p&gt;It takes a while for the other instances to find out about yours. What I did was
that I used my existing accounts at &lt;a href=&quot;https://pol.social/@stfn&quot;&gt;pol.social&lt;/a&gt; and
&lt;a href=&quot;https://fosstodon.org/@stfn&quot;&gt;fosstodon.org&lt;/a&gt; to boost my &amp;quot;hello world&amp;quot; post from
my first account in my instance, and it helped advertise my new instance over
the Fediverse. At the moment of writing this post, my server is federating with
almost 1600 other instances.&lt;/p&gt;
&lt;h2&gt;Who is using fedi.stfn.pl?&lt;/h2&gt;
&lt;p&gt;Right now it&#39;s me, and my two bots:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://fedi.stfn.pl/@stfn&quot;&gt;Admin account for testing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://fedi.stfn.pl/@astrobin_iotd&quot;&gt;Astrobin Image of the Day&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://fedi.stfn.pl/@stacjadnia&quot;&gt;Stacja Dnia&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For now I do not plan to invite anyone else, I am new at this Fedi
administration thing, and if I break something and lose all data, I want to be
the only one affected by it.&lt;/p&gt;
&lt;h2&gt;Bottom line&lt;/h2&gt;
&lt;p&gt;So here I am, with my own Fediverse instance. So far it has been running fine,
there are still some things I need to work out, like regular backups. I have
nginx logs opened in the background to take a look at them now and then and see
if there are any problems happening. In the other pane of tmux I have htop
opened to see the resources usage. So far, barring short spikes when someone
popular boosts my toots, the load is close to 0.0, and RAM usage is around
400-500MB.&lt;/p&gt;
&lt;p&gt;I am sure this is only the first blog post in a series in which I will be
talking about my experiences as an admin. And already I have to say, it does
feel good :)&lt;/p&gt;
&lt;p&gt;And what is more, having my own instance means I can upload my own custom
emojis. Being a person who grew up in Poland in the 90s and 00s, the choice was
obvious. I apologize for nothing.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/53/emotki.png&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/53/emotki.png&quot; alt=&quot;Screenshot of three toots, each one with custom emojis showing the pope, or
emojis from Gadu-Gadu, an instant chat communicator from the 90s, popular in Poland.&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;The Pope and Gadu-Gadu&lt;/span&gt;&lt;/p&gt;
&lt;h2&gt;I would like to thank&lt;/h2&gt;
&lt;p&gt;Special thanks to &lt;a href=&quot;https://polymaths.social/@amin&quot;&gt;Amin&lt;/a&gt; and
&lt;a href=&quot;https://embracing.space/@IngaLovinde&quot;&gt;Inga&lt;/a&gt; for helping me work out some doubts
and issues that I had with initial configuration! :)&lt;/p&gt;
&lt;h2&gt;Coming up next&lt;/h2&gt;
&lt;p&gt;Soon there should be another blog post, in which I will talk about how I
migrated my bot, and about setting up monitoring for my instance.&lt;/p&gt;
&lt;p&gt;Thanks for reading!&lt;/p&gt;
</description>
      <pubDate>Tue, 26 Nov 2024 24:00:00 GMT</pubDate>
      <dc:creator>STFN</dc:creator>
      <guid>https://stfn.pl/blog/53-starting-gotosocial-instance/</guid>
    </item>
    <item>
      <title>Administrating my GoToSocial instance - monitoring and backup</title>
      <link>https://stfn.pl/blog/54-administrating-gotosocial-instance/</link>
      <description>&lt;p&gt;&lt;em&gt;As with the previous post about GoToSocial, you should not take what I write
here as gospel. It is possible that I am doing something not in the best way.
One of the reasons I write those posts, apart from the need to share what I
learnt, is the hope that someone more knowledgeable than I am will pinpoint my
mistakes and provide useful feedback.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;My GoToSocial instance has been running for more than a month now, and now as I
have gathered some experience in administrating it, I can share it with you,
dear readers.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://fedi.stfn.pl/&quot;&gt;fedi.stfn.pl&lt;/a&gt; has been running without issues so far. I
did not lose any data, and the number of instances federated with it is
constantly growing. The bots are doing what they are told, and I am occasionally
using it to toot updates, mostly meta toots on how it&#39;s doing. Ah, the Fediverse
trend of tooting mostly about the Fediverse :)&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/54/fedi_header.png&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/54/fedi_header.png&quot; alt=&quot;Screenshot of the header of the welcome screen of my instance, with text: fedi.stfn.pl
home to 5 users who wrote 164 posts, federating with 3616 other instances&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;The number of instances with whom I am federating is growing.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn.pl/blog/53-starting-gotosocial-instance/&quot;&gt;In the previous blog
post&lt;/a&gt; I talked about the
setup and deployment part of a GoToSocial instance, here I want to talk about
the day-to-day monitoring and backup chores.&lt;/p&gt;
&lt;h2&gt;Resources Usage&lt;/h2&gt;
&lt;p&gt;My instance does not see much traffic, there are four active accounts there,
three being bots posting once a day, and my personal account, to which I post once
every few days. There are occasional spikes of traffic when larger accounts
retoot my bots.&lt;/p&gt;
&lt;p&gt;I&#39;m running it on a VPS with 2 vCPU and 3GB of RAM. The RAM usage is stable
around 600MB, with spikes up to 800MB. The typical load is around 0.1, with
spikes rarely passing 1.0. During that month I used 10GB of transfer, including
typical Ubuntu package updates. GTS media folder uses around 500MB of disk
space. I can confirm what the GTS docs state, that with light traffic, GTS will
run on the smallest of VPSs, or a Raspberry Pi, if you can expose your local SBC
to the Internet.&lt;/p&gt;
&lt;h2&gt;Monitoring&lt;/h2&gt;
&lt;p&gt;For the basic, &amp;quot;is my site up&amp;quot; monitoring I am using
&lt;a href=&quot;https://healthchecks.io/&quot;&gt;healthchecks.io&lt;/a&gt;. They provide free email and
WhatsApp alerts when the site is not responding. And a nice badge to put
somewhere.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://healthchecks.io/badge/e93abe0e-255b-4302-8bcf-72b6e2/M6AaAiZp-2.svg&quot;&gt;&lt;img src=&quot;https://healthchecks.io/badge/e93abe0e-255b-4302-8bcf-72b6e2/M6AaAiZp-2.svg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;For detailed information on how the instance is doing I am using a combination
of Prometheus, Node Exporter and Nginx Exporter.&lt;/p&gt;
&lt;p&gt;I have all three running as systemd services. Prometheus scrapes itself, Node
Exporter and Nginx Exporter and exposes the metrics. To reach the metrics from
outside, UFW is configured to expose port 9090, but only over the Tailscale VPN
interface. The metrics are presented in the form of graphs in Grafana running in
my new, shiny k3s cluster. More on that cluster in a future blog post.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/54/fedi_grafana.png&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/54/fedi_grafana.png&quot; alt=&quot;Screenshot of Grafana, showing six graphs about different stats of my server.&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Part of the &lt;code&gt;/opt/prometheus/prometheus.yml&lt;/code&gt; config file defining the metrics
collection.&lt;/p&gt;
&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;scrape_configs&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 key atrule&quot;&gt;job_name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;prometheus&quot;&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;static_configs&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 key atrule&quot;&gt;targets&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 string&quot;&gt;&quot;localhost:9090&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 key atrule&quot;&gt;job_name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; node
    &lt;span class=&quot;token key atrule&quot;&gt;static_configs&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 key atrule&quot;&gt;targets&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 string&quot;&gt;&#39;localhost:9100&#39;&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 key atrule&quot;&gt;job_name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; nginx
    &lt;span class=&quot;token key atrule&quot;&gt;static_configs&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 key atrule&quot;&gt;targets&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 string&quot;&gt;&#39;localhost:9113&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And the settings for the UFW firewall, showing how Prometheus is only available
through the VPN.&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; ufw status numbered
Status: active

     To                         Action      From
     --                         ------      ----
&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; OpenSSH                    ALLOW IN    Anywhere
&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;443&lt;/span&gt;                        ALLOW IN    Anywhere
&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;80&lt;/span&gt;/tcp                     ALLOW IN    Anywhere
&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;9090&lt;/span&gt; on tailscale0         ALLOW IN    Anywhere
&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; OpenSSH &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;v6&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;               ALLOW IN    Anywhere &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;v6&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 number&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;443&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;v6&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;                   ALLOW IN    Anywhere &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;v6&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 number&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;80&lt;/span&gt;/tcp &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;v6&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;                ALLOW IN    Anywhere &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;v6&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 number&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;9090&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;v6&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; on tailscale0    ALLOW IN    Anywhere &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;v6&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Backup&lt;/h2&gt;
&lt;p&gt;Backup is done using &lt;a href=&quot;https://torsion.org/borgmatic/&quot;&gt;Borgmatic&lt;/a&gt;. Borgmatic is a tool to run
Borg on schedule. &lt;a href=&quot;https://www.borgbackup.org/&quot;&gt;Borg&lt;/a&gt; is a tool to make incremental
backups. Incremental meaning that the first backup backs up (wow, great
language) the full data, and the subsequent ones only record the information
about the changes, saving disk space.&lt;/p&gt;
&lt;p&gt;I configured Borgmatic &lt;a href=&quot;https://docs.gotosocial.org/en/latest/admin/backup_and_restore/&quot;&gt;according to the
docs&lt;/a&gt;. For now,
I am only backing up locally on the same VPS, but I have plans to set up
automatic remote backups to my Hetzner Storage Box. I just hope I&#39;ll make myself
do it before I really need it :)&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://wimpysworld.social/@martin&quot;&gt;Martin&lt;/a&gt; created an interesting
alternative approach to GTS backup. He made a bash script that handles the
backup process in a smart way. I recommend taking a look &lt;a href=&quot;https://github.com/wimpysworld/gotosocial-backup&quot;&gt;at the
repo&lt;/a&gt;, and listening &lt;a href=&quot;https://linuxmatters.sh/44/&quot;&gt;to the
podcast&lt;/a&gt; in which he discusses his approach.&lt;/p&gt;
&lt;h2&gt;Who is on my instan&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://fedi.stfn.pl/@stfn&quot;&gt;stfn&lt;/a&gt; - my account for tooting about the instance&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://fedi.stfn.pl/@astrobin_iotd&quot;&gt;astrobin_iotd&lt;/a&gt; - a bot that every day toots the Astrobin Image of the Day&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://fedi.stfn.pl/@stacjadnia&quot;&gt;stacjadnia&lt;/a&gt; - a bot that every day toots a link to a random railway station in
Poland&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://fedi.stfn.pl/@naszpapa&quot;&gt;naszpapa&lt;/a&gt; - a bot that every day at 21:37 toots
an emoji with the face of the late pope John Paul II (that is a Polish inside
joke, &lt;a href=&quot;https://www.youtube.com/watch?v=llzhaKreQrM&quot;&gt;don&#39;t ask&lt;/a&gt;).&lt;/p&gt;
&lt;h2&gt;Bottom Line&lt;/h2&gt;
&lt;p&gt;I am very much enjoying having my own instance in the Fediverse, I feel that I
have reached another level in my online presence. It&#39;s a learning opportunity,
and also a way to have your own part of the Fediverse. Even if my the instances
of my main accounts go down (I do hope it won&#39;t happen, those are great
instances with very nice admins), I will have a place to go back to. And I have
a place for testing, for investigating, for my silly bots.&lt;/p&gt;
&lt;p&gt;I even might consider inviting other people to use my instance, but only if
they acknowledge that I am actually a noob in &amp;quot;cosplaying as a sysadmin&amp;quot; ((c)
Jeff Geerling) and they won&#39;t mind that there is a non-zero possibility that
I do something stupid and lose all their data.&lt;/p&gt;
&lt;p&gt;BTW, a few days ago the admin of the late botsin.space instance &lt;a href=&quot;https://muffinlabs.com/posts/2024/12/21/12-21-botsin-space-post-mortem/&quot;&gt;released a
&amp;quot;postmortem&amp;quot;&lt;/a&gt;,
writing in detail about his experience in running a large Mastodon instance. I
found it very interesting to read, and telling a lot about the state of Mastodon
and the Fediverse in general. Reading it made me even more thankful for the
creation of GoToSocial, a simpler and lighter than Mastodon way to have your own
instance.&lt;/p&gt;
&lt;p&gt;Thanks for reading! If I find something worth mentioning about my GTS instance,
there will be another blog post in this series.&lt;/p&gt;
</description>
      <pubDate>Mon, 23 Dec 2024 24:00:00 GMT</pubDate>
      <dc:creator>STFN</dc:creator>
      <guid>https://stfn.pl/blog/54-administrating-gotosocial-instance/</guid>
    </item>
    <item>
      <title>I have a new skill - I can now make Ethernet cables</title>
      <link>https://stfn.pl/blog/55-making-internet-cables/</link>
      <description>&lt;p&gt;Happy New Year!&lt;/p&gt;
&lt;p&gt;Last month I started building my Kubernetes cluster made from Raspberry Pis (I
promise there will be a blog post on it soon). At first it was connected with
Wi-Fi, but I wanted to switch it to Ethernet, and eventually power it over PoE
(Power over Ethernet). I did not not have cables of the right length, so I went
browsing through the usual online shops. And then, I had a revelation: I might
make the cables myself!&lt;/p&gt;
&lt;p&gt;I watched a few Youtube videos on the matter, as one would, and it did not seem
to be too complicated. I enjoy doing manual stuff, DIY is important for me, and
having a new skill under my belt is always cool, so why not try it? With
Christmas approaching fast, and my parents wanting to buy me a present, I asked
Santa Dad to get me an Ethernet cable toolkit.&lt;/p&gt;
&lt;p&gt;And Santa delivered, I was now the proud owner of &lt;a href=&quot;https://www.speckable.pl/pl/product/12220,zestaw-narzedzi-sieciowych-tester-kabli-lan-zaciskarka-rj45-komplet-6-elementow-etui-neku&quot;&gt;a set of
tools&lt;/a&gt;
that allow me to make my own Internet: an RJ45 crimping tool, a wire stripper, a
cable connection tester, and a weird grey knifey tool that I am still not sure
what it does.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/55/kit.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/55/kit.jpg&quot; alt=&quot;Photo of an
Ethernet cable toolkit, consisting of a crimping tool a wire stripper, a grey
unknown tool, and packs of connectors and connector covers&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;Here&#39;s the toolkit&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;On my own, I bought 20 meters of cat 5e cable, and a 50 piece pack of RJ45
connectors and connector rubber covers.&lt;/p&gt;
&lt;p&gt;Everything was surprisingly cheap, the toolkit was 70PLN (17EUR), and the cables
and connectors were 80PLN (19EUR). The price for the toolkit and the materials
was close to the cost of a few ready-made cables.&lt;/p&gt;
&lt;p&gt;I used &lt;a href=&quot;https://www.youtube.com/watch?v=5B9wl0aSKcI&quot;&gt;this Youtube video&lt;/a&gt; as the
tutorial on crimping cables. It&#39;s in Polish, but it shows very clearly what to
do. And there&#39;s a ton of such videos everywhere, surely there will be one in
your language :)&lt;/p&gt;
&lt;p&gt;Looks like &amp;quot;beginner&#39;s luck&amp;quot; is actually a thing, because the first cable I made
worked on the first try, whereas the second one I had to correct twice. The
cable tester is a very useful thing, it shows right away whether the cable was
done correctly.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/55/cables.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/55/cables.jpg&quot; alt=&quot;Picture of two Raspberry Pi 4s in a cluster case, placed on my white desk. The Pis are connected to a switch that is not in the frame via purple Ethernet cables with yellow connector covers.&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;My Pi K3s cluster, connected with the cables I made.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;So far I made two cables from scratch, and fixed another one that had a broken
connector. If not for this possibility to fix it, I would throw it out, so one
less thing going to the heap.&lt;/p&gt;
&lt;p&gt;And that&#39;s it, I have a new skill. In my future home I will be making the
cabling myself. And if I ever meet a fellow nerd in real life and not over the
intertubes, I might give them a home-made Ethernet cable as a present :3&lt;/p&gt;
&lt;p&gt;Thanks for reading!&lt;/p&gt;
</description>
      <pubDate>Sat, 04 Jan 2025 24:00:00 GMT</pubDate>
      <dc:creator>STFN</dc:creator>
      <guid>https://stfn.pl/blog/55-making-internet-cables/</guid>
    </item>
    <item>
      <title>My Kubernetes &amp; BOINC Raspberry Pi Cluster</title>
      <link>https://stfn.pl/blog/56-kubernetes-boinc-raspberry-pi-cluster/</link>
      <description>&lt;p&gt;In the last two months I managed to buy two cheap, second-hand Raspberry Pi 4s
with 4GB of RAM. The second one I bought the day after Christmas, it was described
as a &amp;quot;failed present&amp;quot;. Somebody had a bad experience under the Christmas tree
which turned out to be a win for me.&lt;/p&gt;
&lt;p&gt;Having now enough Raspberry Pis I could start with my plan that I had had for
a very long time: create a cluster.&lt;/p&gt;
&lt;p&gt;Why would I want to create a cluster of Raspberry Pis? Mostly for learning, and
fun. Kubernetes is a technology that has been on my to-do list for years now,
and while I did some learning using single node Minikube, I wanted to try out
the &amp;quot;real thing&amp;quot; and run it on a multi-computer setup. Having a cluster is also
an incentive to try out other administration and management tools like Ansible.
And also I enjoy the hardware aspect of the Pis, it&#39;s like a LEGO for adults.
I mean, LEGO is also for adults, so um, Pis are like LEGOs, and both are for
adults. And teens? I don&#39;t know where this is going. You get me.&lt;/p&gt;
&lt;p&gt;Anyway, let&#39;s talk about the cluster.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/56/cluster.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/56/cluster.jpg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;The cluster in its full glory, with the bit too long
cables.&lt;/span&gt;&lt;/p&gt;
&lt;h2&gt;Hardware&lt;/h2&gt;
&lt;p&gt;The heart of the cluster is three Raspberry Pi 4b, each having 4GB of RAM. They
are mounted in an &lt;a href=&quot;https://botland.store/raspberry-pi-4b-cases/2698-case-for-raspberry-pi-4b-3b-3b-2b-x3-open-5904422362775.html&quot;&gt;unbranded open cluster
case&lt;/a&gt;
The case is universal enough that it can also accommodate any other Pi, so at
some point I might be able to switch the Pis to model 5.&lt;/p&gt;
&lt;p&gt;All three Pis are connected to my NETGEAR PoE+ switch using &lt;a href=&quot;https://stfn.pl/blog/55-making-internet-cables/&quot;&gt;cables that I made
by myself&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;The PoE HAT&lt;/h2&gt;
&lt;p&gt;One of the Pis is using the Raspberry Pi PoE+ HAT. Why only one? Because those
HATs are a bit too expensive to buy three at once, and also I wanted to test it
out before committing to install them on every Pi that I have.&lt;/p&gt;
&lt;p&gt;After a week of using it I am now sure that at some point I will buy the PoE
HATs for other Pis. It may not sound like much, but removing the power cable
and the wall charger makes a real difference in the tidiness and space usage
of the whole cluster&lt;/p&gt;
&lt;p&gt;An upside or a downside, however you look at it, is that the HAT has a fan.
Active cooling helps a lot with keeping the thermals down, especially under
prolonged loads (and I&#39;ve been doing them, more on that soon), but the fan can
be noisy at times. To combat that, I used &lt;a href=&quot;https://www.jeffgeerling.com/blog/2021/taking-control-pi-poe-hats-overly-aggressive-fan&quot;&gt;Jeff Geerling&#39;s hack to change the
fan
curve&lt;/a&gt;
and that helped with the noise. I also added another, larger fan to blow air
over the whole cluster, which is another factor in stopping the HATs fan from
ramping up.&lt;/p&gt;
&lt;p&gt;What I do not like about the PoE HAT is that it is very close to the board. It
makes sense in tight cases, I imagine, but in my case it only meant that I had
to remove all the radiators that I glued to my Pi, apart from the CPU one.
And even that one would fit only when I removed one of the screws holding the
fan.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/56/cluster_zoom.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/56/cluster_zoom.jpg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;Zoom on the PoE HAT&lt;/span&gt;&lt;/p&gt;
&lt;h2&gt;Software&lt;/h2&gt;
&lt;p&gt;The Pis are running the latest Raspberry Pi OS 64 bit without a desktop
environment. Everything is done via SSH.&lt;/p&gt;
&lt;p&gt;The hostnames are Atlas, Agena and Thor. Yes, I named them after NASA rockets :)&lt;/p&gt;
&lt;h2&gt;Kubernetes&lt;/h2&gt;
&lt;p&gt;The cluster is running &lt;a href=&quot;https://k3s.io/&quot;&gt;K3s&lt;/a&gt;, a distribution of Kubernetes aimed at lower power
devices and people who do not need the power or have the knowledge about the full-fledged
Kubenernetes, and my usecase ticks both of those boxes.&lt;/p&gt;
&lt;p&gt;My general homelabbing plan for 2025 is to separate computer from storage, and
this cluster fits into it well. So far I have deployed on the cluster
Grafana for monitoring and Readeck for bookmark management. This way I can
access those services even when my main, large NAS/server machine is not running.
The plan is to have running on the cluster all the services that just run and do not need
heavy resources. The NAS will then be only for services that require large storage
or compute, like Immich for photos or Pinchflat for YouTube archiving.&lt;/p&gt;
&lt;p&gt;I won&#39;t be diving into how I configured K3s or how I am deploying containers,
I am a total noob at it so far, and there are tons of much better posts, written
by infinitely more knowledgeable people, from which you can learn. Maybe at one
point when I learn more and fix all my mistakes, I will write a longer post
focusing on my Kubernetes journey.&lt;/p&gt;
&lt;h2&gt;Ansible&lt;/h2&gt;
&lt;p&gt;As I wrote in the introduction, having a cluster also encouraged me to dive
deeper into &lt;a href=&quot;https://docs.ansible.com/&quot;&gt;Ansible&lt;/a&gt;. Ansible is one of those
technologies that I&#39;ve been going in and out with for many years now, and never
really learnt it in depth. Now, with three identical computers I have more will
to learn, as it can actually save me work to automate some of the tasks.
(&lt;a href=&quot;https://xkcd.com/1319/&quot;&gt;insert obligatory XKCD reference&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;For example, I wrote a Ansible playbook that installs and runs Prometheus and
Node Exporter agents on every Pi in the cluster:&lt;/p&gt;
&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Install Prometheus and Node Exporter
  &lt;span class=&quot;token key atrule&quot;&gt;hosts&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; cluster
  &lt;span class=&quot;token key atrule&quot;&gt;become&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; yes 
  &lt;span class=&quot;token key atrule&quot;&gt;tasks&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 key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Create a user for Prometheus
      &lt;span class=&quot;token key atrule&quot;&gt;ansible.builtin.user&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; prometheus
    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Create folder for Prometheus and Node Exporter
      &lt;span class=&quot;token key atrule&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; /opt/prometheus
        &lt;span class=&quot;token key atrule&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; directory

    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Upload Prometheus
      &lt;span class=&quot;token key atrule&quot;&gt;ansible.builtin.copy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ./prometheus
        &lt;span class=&quot;token key atrule&quot;&gt;dest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; /opt/prometheus/
    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Upload Node Exporter
      &lt;span class=&quot;token key atrule&quot;&gt;ansible.builtin.copy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ./node_exporter
        &lt;span class=&quot;token key atrule&quot;&gt;dest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; /opt/prometheus/
    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Upload Prometheus config file
      &lt;span class=&quot;token key atrule&quot;&gt;ansible.builtin.copy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ./prometheus.yml
        &lt;span class=&quot;token key atrule&quot;&gt;dest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; /opt/prometheus/

    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Upload Prometheus service
      &lt;span class=&quot;token key atrule&quot;&gt;ansible.builtin.copy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ./prometheus.service
        &lt;span class=&quot;token key atrule&quot;&gt;dest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; /etc/systemd/system/
    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Upload Node Exporter service
      &lt;span class=&quot;token key atrule&quot;&gt;ansible.builtin.copy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ./node_exporter.service
        &lt;span class=&quot;token key atrule&quot;&gt;dest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; /etc/systemd/system/

    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Recursively change ownership of a directory
      &lt;span class=&quot;token key atrule&quot;&gt;ansible.builtin.file&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; /opt/prometheus
        &lt;span class=&quot;token key atrule&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; directory
        &lt;span class=&quot;token key atrule&quot;&gt;recurse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; yes
        &lt;span class=&quot;token key atrule&quot;&gt;owner&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; prometheus
        &lt;span class=&quot;token key atrule&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; prometheus

    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Make Prometheus executable
      &lt;span class=&quot;token key atrule&quot;&gt;ansible.builtin.file&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; /opt/prometheus/prometheus
        &lt;span class=&quot;token key atrule&quot;&gt;mode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; a+x
    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Make Node Exporter executable
      &lt;span class=&quot;token key atrule&quot;&gt;ansible.builtin.file&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; /opt/prometheus/node_exporter
        &lt;span class=&quot;token key atrule&quot;&gt;mode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; a+x


    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Issue daemon&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;reload to pick up config changes
      &lt;span class=&quot;token key atrule&quot;&gt;ansible.builtin.systemd_service&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;daemon_reload&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean important&quot;&gt;true&lt;/span&gt;

    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Enable Prometheus service
      &lt;span class=&quot;token key atrule&quot;&gt;ansible.builtin.systemd_service&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; prometheus
        &lt;span class=&quot;token key atrule&quot;&gt;enabled&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean important&quot;&gt;true&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Enable Node Exporter service
      &lt;span class=&quot;token key atrule&quot;&gt;ansible.builtin.systemd_service&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; node_exporter
        &lt;span class=&quot;token key atrule&quot;&gt;enabled&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean important&quot;&gt;true&lt;/span&gt;

    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Make sure Prometheus is running
      &lt;span class=&quot;token key atrule&quot;&gt;ansible.builtin.systemd_service&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; started
        &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; prometheus
    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Make sure Node Exporter is running
      &lt;span class=&quot;token key atrule&quot;&gt;ansible.builtin.systemd_service&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; started
        &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; node_exporter&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And with that, I can show the stats in Grafana running in K3s:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/56/grafana_cluster.png&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/56/grafana_cluster.png&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Yes, I know that I can also run Prometheus straight in K3s are get metrics about
Kubernetes themselves, I have not reached that point yet :)&lt;/p&gt;
&lt;h2&gt;BOINC&lt;/h2&gt;
&lt;p&gt;And finally, &lt;a href=&quot;https://stfn.pl/blog/10-my-thoughts-on-boinc/&quot;&gt;BOINC&lt;/a&gt;. The Pis
have been mostly idling, running only a few containers each, so I decided to use
them for distributed computing. They won&#39;t do much but with BOINC, every little
helps. I installed boinc-client on all three, and assigned them to do tasks for
&lt;a href=&quot;https://asteroidsathome.net/boinc/&quot;&gt;Asteroids@Home&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Thermals are an important issue when running BOINC, so I added a 120mm BeQuiet
fan to the cluster. It&#39;s running from the USB port, so it&#39;s slow and quiet, but
moves enough air to keep the Pis cool.  To further keep the temps down, and also
leave some compute for the K3s, I am only crunching on three of the four cores on
the Pis CPU. So in total I am crunching on nine cores. The cluster in total
crunches around 20 tasks per day.&lt;/p&gt;
&lt;h2&gt;Bottom Line&lt;/h2&gt;
&lt;p&gt;I believe that having a small cluster is a good addition to one&#39;s homelab. It
not only allows you to tinker more with hardware, but also opens totally new
possibilities when it comes to software and administrations. A single Raspberry
Pi 4 can do quite a lot, but combined with others it can do even more, with
an added bonus of high availability.&lt;/p&gt;
&lt;p&gt;Maybe one day I will build such a cluster, not from Pis, but from USFF x86 PCs
like the Lenovo Tiny? We will see.&lt;/p&gt;
&lt;p&gt;This post has been all over the place. I hope you enjoyed it. In 2025 I have this
idea to write more blogs posts that are not &lt;em&gt;tutorials&lt;/em&gt; but more like, actually
&lt;em&gt;blog&lt;/em&gt; posts, where I loosely talk about me and what I&#39;m up to.&lt;/p&gt;
&lt;p&gt;Thanks for reading!&lt;/p&gt;
</description>
      <pubDate>Sat, 11 Jan 2025 24:00:00 GMT</pubDate>
      <dc:creator>STFN</dc:creator>
      <guid>https://stfn.pl/blog/56-kubernetes-boinc-raspberry-pi-cluster/</guid>
    </item>
    <item>
      <title>How much electricity does my home server use?</title>
      <link>https://stfn.pl/blog/57-home-server-electricity-usage/</link>
      <description>&lt;h2&gt;tl;dr&lt;/h2&gt;
&lt;p&gt;My home server idles at 70 watts.&lt;/p&gt;
&lt;h2&gt;Longer version&lt;/h2&gt;
&lt;p&gt;I started going deeper into the smart home rabbit hole, and one of the areas I
want to investigate is the monitoring of power consumption. The smart home
rabbit hole, which is something different than the smart rabbit home hole, which
is the place where intelligent small mammals live. I&#39;ve been reading a lot of
&lt;a href=&quot;https://rubenerd.com/&quot;&gt;Rubenerd&lt;/a&gt; lately and his writing style is getting etched
in my brain. Anyway, for now the only use of measuring power consumption is my
love of graphs and gathering data, but when at some point in the future I will
have a house with a PV installation, having detailed power consumption, and
&lt;em&gt;production&lt;/em&gt; statistics will hopefully allow me to plan when to use power hungry
devices.&lt;/p&gt;
&lt;p&gt;But that is a bridge quite far downstream, for now I am focused on answering a
relatively simple question: How much electricity is my home server using?&lt;/p&gt;
&lt;p&gt;I specifically mention my home server, because the whole of my homelab consists
of other devices as well, such as my &lt;a href=&quot;https://stfn.pl/blog/56-kubernetes-boinc-raspberry-pi-cluster/&quot;&gt;Raspberry Pi
Cluster&lt;/a&gt;. And
there&#39;s also another mystery device about which I will talk in the next blog
post :)&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/57/homelab_power.png&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/57/homelab_power.png&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;Home Assistant custom dashboard showing my home server power usage stats.&lt;/span&gt;&lt;/p&gt;
&lt;h2&gt;How am I measuring the power usage?&lt;/h2&gt;
&lt;p&gt;I decided to go with the Zigbee ecosystem. I chose Zigbee as it is not using
WiFi, and it is an open standard adopted by many companies. Zigbee devices are
small, relatively cheap, and low power. And Zigbee has native support in Home
Assistant.&lt;/p&gt;
&lt;p&gt;To use Zigbee you need a Zigbee gate, usually in the form of a small USB dongle
plugged into the computer that has HA running. I&#39;m using the &lt;a href=&quot;https://sonoff.tech/product/gateway-and-sensors/sonoff-zigbee-3-0-usb-dongle-plus-e/&quot;&gt;Sonoff
ZBDongle-E&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;To measure the power consumption you also need a smart socket to which you plug
the device you want to monitor. I went with a TUYA Zigbee 3.0 Smart Socket. It&#39;s
a bit bulky, but I can live with that.&lt;/p&gt;
&lt;p&gt;Setting up is very simple if you already know your way around Home Assistant.
What needs to be done is to plug the Zigbee dongle, add the Zigbee Home
Automation integration in HA settings, and then turn on pairing mode on the
socket. It will be automatically detected and configured by ZHA. Then you can
add it to the Energy dashboard, and le voila bonjour, energy stats on your
screen.&lt;/p&gt;
&lt;h2&gt;My Home server&lt;/h2&gt;
&lt;p&gt;The full description of my home server can be found in the &lt;a href=&quot;https://stfn.pl/homelab/&quot;&gt;Story Of My Homelab&lt;/a&gt; subpage, but let&#39;s recap what&#39;s inside:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Intel Xeon 1275L v3 CPU, 4C8T, 45W TDP&lt;/li&gt;
&lt;li&gt;Gigabyte Z97P-D3 Socket 1150 motherboard&lt;/li&gt;
&lt;li&gt;32GB of DDR3 1600MT/s RAM in four sticks&lt;/li&gt;
&lt;li&gt;four 3.5&amp;quot; HDDs from Seagate, WD, and HGST&lt;/li&gt;
&lt;li&gt;single 2.5&amp;quot; SSD for boot&lt;/li&gt;
&lt;li&gt;NVIDIA RTX2060&lt;/li&gt;
&lt;li&gt;Seasonic 550W Bronze PSU&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The GPU is power limited to 130W maximum, and idles at 25 watts according to
&lt;code&gt;nvidia-smi&lt;/code&gt;. The power supply is only bronze, as I bought it for a small
Netfllix box, and only later I did I move it into my homelab. I wonder how much
of a difference moving to a Gold one would make.&lt;/p&gt;
&lt;p&gt;I use my home server mostly as a NAS, to store and serve all my files. There&#39;s a
few Docker containers running on it, mostly services concerned with data, such
as &lt;a href=&quot;https://immich.app/&quot;&gt;Immich&lt;/a&gt; or
&lt;a href=&quot;https://github.com/kieraneglin/pinchflat&quot;&gt;Pinchlat&lt;/a&gt;. The HDDs are using ZFS and
are organized into two mirrors of two disks each. I also do occasionally some
BOINC distributed computation, as for now it is my only PC capable of crunching
BOINC tasks (I consider laptops not suitable for BOINC due to their poor
cooling). BOINC uses CPU cores to their maximum all the time, so it is a good
test of power usage under prolonged loads. It can also use the GPU in the same
way.&lt;/p&gt;
&lt;h2&gt;How much power does it use?&lt;/h2&gt;
&lt;p&gt;After that rather long introduction, time for the data:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;State&lt;/th&gt;
&lt;th style=&quot;text-align:center&quot;&gt;Power&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;idling&lt;/td&gt;
&lt;td style=&quot;text-align:center&quot;&gt;70W&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;BOINC with half CPU cores&lt;/td&gt;
&lt;td style=&quot;text-align:center&quot;&gt;120W&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;BOINC with all CPU cores&lt;/td&gt;
&lt;td style=&quot;text-align:center&quot;&gt;120W&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;BOINC with GPU&lt;/td&gt;
&lt;td style=&quot;text-align:center&quot;&gt;220W&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;BOINC with half CPU cores and GPU&lt;/td&gt;
&lt;td style=&quot;text-align:center&quot;&gt;240W&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;A few interesting things can be observed. First, there is no difference between
crunching on all cores and half cores. I assume this is because the CPU is not
able to reach boost speeds on all cores at the same time, and so it either
boosts on half of the cores, or keeps lower clock speed at all cores. This is
confirmed by the fact that BOINC tasks are crunched much faster per task when
not all cores are used.&lt;/p&gt;
&lt;p&gt;What is harder for me to explain is the little difference between crunching on
GPU only, and on both GPU and CPU. I am suspecting some part of the PC is
reducing power coming to both CPU and GPU to keep the overall power within
a specific limit.&lt;/p&gt;
&lt;h2&gt;Bottom Line&lt;/h2&gt;
&lt;p&gt;In the next blog post I will talk more about my Home Assistant setup. I am also
planning to add more power monitoring sockets to get a better understanding how
much power do different devices use in my home. I wish I had one of those Zigbee
power monitors that is hardwired in the fuze box to monitor the overall power of
the whole house, but that is something for the far future.&lt;/p&gt;
&lt;p&gt;Thanks for reading!&lt;/p&gt;
</description>
      <pubDate>Sun, 19 Jan 2025 24:00:00 GMT</pubDate>
      <dc:creator>STFN</dc:creator>
      <guid>https://stfn.pl/blog/57-home-server-electricity-usage/</guid>
    </item>
    <item>
      <title>Using a Lenovo USFF PC as a Home Assistant box</title>
      <link>https://stfn.pl/blog/58-home-assistant-lenovo-usff/</link>
      <description>&lt;p&gt;Continuing the theme of a smart home, I wanted to talk about my Home Assistant setup.&lt;/p&gt;
&lt;p&gt;During the Christmas break, when time does not exist and boredom is common, I
was browsing the website of a company that sells cheap post-lease computer
parts. What got my attention was the sale of a Lenovo Ultra Small Form Factor
(USFF) PC with the price of 120 PLN. That is around 30 eur, and around half the
price of a Raspberry Pi 4b here. So I yolo&#39;ed and bought it right away. A week later it arrived.&lt;/p&gt;
&lt;p&gt;The full model name is Lenovo M625q. And it is indeed very smol. The closest
thing I can compare it to would be a Playstation 1 (yes, I am old), and it&#39;s
still around half its size. The full specs are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;AMD E@-9000e CPU 2C2T @ 1.5GHz, 6W (!) TDP&lt;/li&gt;
&lt;li&gt;4GB of RAM in a single DDR4 SDIMM stick&lt;/li&gt;
&lt;li&gt;32GB M2 SSD&lt;/li&gt;
&lt;li&gt;lots of I/O, USB, Display Port, audio&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The RAM and SSD can be upgraded, the maximum RAM size it can handle is 16GB.&lt;/p&gt;
&lt;p&gt;As for power and cooling, it uses an external PSU, and the cooling is fully
passive, almost the whole motherboard is covered by a single large heatsink,
which needs to be removed to access RAM and the SSD.&lt;/p&gt;
&lt;p&gt;I was concerned that having just two slow cores on the CPU would be an issue,
but for running just for a few services in a headless mode, I did not see any
performance problems.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/58/lenovo_sml.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/58/lenovo_sml.jpg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;Lenovo M625q living under my book rack, with a Sonoff Zigbee dongle, and an external 2.5&amp;quot; drive.&lt;/span&gt;&lt;/p&gt;
&lt;h2&gt;Software&lt;/h2&gt;
&lt;p&gt;The PC came without an OS, so the first thing I did was to install Debian on it.
The installation went without issues, I did not install any Desktop Environment
as I am using it only in a headless mode. Next I installed the typical
monitoring stack, Prometheus and Node Exporter, in order to have basic stats
like temperature in my Grafana dashboard. Finally I went with installing Home
Assistant.&lt;/p&gt;
&lt;p&gt;Home Assistant provides several ways to be installed, the recommended one is to
install the Home Assistant Operating System. I did not go this way, as I prefer
to have a basic OS, and then go from it, installing the things I need. I
also was not sure how much access and possibility to modify stuff I would have
when running a custom OS. While running HA is the main aim of this PC, I also
want to be able to install other stuff on it, like the just mentioned
Prometheus, or Tailscale, or do NFS shares.&lt;/p&gt;
&lt;p&gt;The HA installation as a service is called Home Assistant Supervised, and the
installation process requires some knowledge of the Linux command line. Here is
the &lt;a href=&quot;https://www.home-assistant.io/installation/linux#install-home-assistant-supervised&quot;&gt;full documentation on how to do
it&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;With HA being installed, I also installed &lt;a href=&quot;https://caddyserver.com/docs/install&quot;&gt;Caddy&lt;/a&gt; to have a reverse proxy,
so that I don&#39;t need to provide the port number when accessing the HA web UI.&lt;/p&gt;
&lt;p&gt;The Caddy reverse proxy config is super simple:&lt;/p&gt;
&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;http&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token comment&quot;&gt;//ha.local {&lt;/span&gt;
reverse_proxy &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;8123&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;BTW, I have a full post on &lt;a href=&quot;https://stfn.pl/blog/42-caddy-pihole-homelab-subdomains/&quot;&gt;using Caddy as a reverse proxy in a homelab&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Home Assistant integrations&lt;/h2&gt;
&lt;p&gt;First thing I did after the initial setup of HA, was to add all my &lt;a href=&quot;https://stfn.pl/blog/51-introduction-to-esphome-with-projects/&quot;&gt;ESPHome
temperature/humidity/pressure sensors
&lt;/a&gt;, which now I
have three, each for one room in my flat. HA found and pair them right away, and
what was left for me was to assign them to the Zones (bedroom, office, living
room).&lt;/p&gt;
&lt;p&gt;Next thing was adding the TUYA integration for my single TUYA WiFi smart light
bulb. Also no issues, I had to find the TUYA integration in the integration
list, and put the light bulb into pairing mode.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;put the light bulb into pairing mode&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Imagine saying such words to a person twenty years ago.&lt;/p&gt;
&lt;p&gt;With that out of the way, came the big part: Zigbee. Zigbee is my new craze, I
am still amazed how cheap and low power its devices can be, and as it is using
its own protocol, Zigbee does not clutter WiFi. As I said &lt;a href=&quot;https://stfn.pl/blog/57-home-server-electricity-usage/&quot;&gt;in my previous post
about smart sockets&lt;/a&gt; ,
for Zigbee you need a gateway, usually in the form a Zigbee USB dongle. I bought
one and plugged into one of the oh-so-many USB ports on the Lenovo box.&lt;/p&gt;
&lt;p&gt;Configuring ZHA (Zigbee Home Integration) was also a breeze. In overall I am
amazed how simple the whole process is, things just work.&lt;/p&gt;
&lt;p&gt;I also reused my IKEA Tradfri lightbulbs that I used to connect to a TRADFRI
gateway, the old one looking like a white hockey puck. My wife is now happy that
she turn off the bedroom floor lamp from her bed :)&lt;/p&gt;
&lt;p&gt;I don&#39;t have any automations set up yet, but once I get more Zigbee devices, I
might start writing them. Maybe moving a detached house will be an inspiration
to make some.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/58/ha.png&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/58/ha.png&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;My HA overview dashboard. It&#39;s in Polish but I&#39;m sure you can guess what it what.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;To sum up, I now have a dedicated Home Assistant box, that uses barely any
power, is small enough to sit under a bottom shelf of my book rack, is
completely silent, and can handle any number of external devices and sensors. I
do not know yet its power usage, but I would say its irrelevant.&lt;/p&gt;
&lt;h2&gt;Lenovo USFF vs Raspberry Pi&lt;/h2&gt;
&lt;p&gt;Now let&#39;s talk about the elephant pie in the room. Please don&#39;t make pies from
elephants.&lt;/p&gt;
&lt;p&gt;What about a Rapsberry Pi? This question is even more relevant today, as the Pi
5 16GB was just released, so the Lenovo USFF and the Pi could be equal in terms
of memory.&lt;/p&gt;
&lt;p&gt;I could have used a Raspberry Pi for this. But I did not. I believe using a Pi
for this specific usecase does not make sense. Of course Pis are great little
devices, and they have their usecases, but this is not one of them.&lt;/p&gt;
&lt;p&gt;Pis are perfect when you are really constrained in space and power. A great
feature of a Pi, often overlooked, is that it can be put in a very remote place,
on the roof, in the shed, and powered from a distance using Power over Ethernet.
Or even a battery and a solar panel. A Pi has GPIOs to plug small sensors
directly. And a Pi is a standard. When you buy a Pi, you have the exact same
device as tens of thousands of other people, and when you ask a question on
social media, you know that there will be people having the exact same
experience/problem as you. Pis are also great when you want to make a small
cluster of devices, as &lt;a href=&quot;https://stfn.pl/blog/56-kubernetes-boinc-raspberry-pi-cluster/&quot;&gt;the one that I
have&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;But for general computing, just running stuff in a typical flat, close to a
power socket? Those Lenovo USFF simply win the comparison. There are much
cheaper (again, 30EUR! A Pi 5 16GB is 130EUR, and that is without the case and
the power adapter!), and have expandable storage and RAM. And they are x86, not
ARM. With the proliferation of ARM-capable software and Docker containers, that
is becoming less of an issue, but there is still software that will just not run
on an ARM device.&lt;/p&gt;
&lt;p&gt;So, what do I choose? Both! There will always be cases where I prefer to use a
Pi, and in other ones such tiny PCs will be the way to go.&lt;/p&gt;
&lt;p&gt;And that for now wraps the theme of my home becoming smart (in contrast to myself).&lt;/p&gt;
&lt;p&gt;Thanks for reading!&lt;/p&gt;
&lt;p&gt;If you enjoyed this post, please consider helping me make new projects by
supporting me on the following crow&lt;/p&gt;
</description>
      <pubDate>Sat, 25 Jan 2025 24:00:00 GMT</pubDate>
      <dc:creator>STFN</dc:creator>
      <guid>https://stfn.pl/blog/58-home-assistant-lenovo-usff/</guid>
    </item>
    <item>
      <title>Powering PC fans from a USB port to cool my Raspberry Pi Cluster</title>
      <link>https://stfn.pl/blog/59-powering-fans-from-usb-pi-cluster/</link>
      <description>&lt;p&gt;I have a Raspberry Pi cluster (&lt;a href=&quot;https://stfn.pl/blog/56-kubernetes-boinc-raspberry-pi-cluster/&quot;&gt;citation
needed&lt;/a&gt;). One of
the uses for it is crunching BOINC. Running the BOINC daemon means that the Pis&#39;
CPUs are constantly running at almost 100% load. And so they make a lot of heat.
The Pis are powered by POE+ HATs, which also produce heat themselves and at the
same time restrict airflow. The HATs have their own fans, but those are noisy
when they ramp up. All in all a situation far from perfect. A solution for that
issue would be an additional source of cooling to make my cluster run quietly
and reliably.&lt;/p&gt;
&lt;p&gt;I could use those 5V tiny fans that are sold as dedicated for the Pi, but those
are also whiny as a far-right person seeing happy people. And the HATs
obstruct the GPIO pins, so that is not the way.&lt;/p&gt;
&lt;p&gt;What I realized is that a cluster of three Pis has the height and the width of a
typical 120mm case fan. And I have such a fan, leftover from the desktop PC I
used to have.&lt;/p&gt;
&lt;p&gt;I have a fan-header to 2.1 x 5.5mm barrel adapter that I bought during one of my
Aliexpress-weird-stuff-under-dollar shopping sprees, and I used it to cool my
Pis when I was doing &lt;a href=&quot;https://stfn.pl/blog/17-rpi4-rpi5-boinc/&quot;&gt;BOINC Pi 5 vs Pi 4
testing&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;But that was only a temporary measure, before I had the PoE HATs. Now I want a
long-term, quiet fix, and no other cables coming to the Pi cluster apart from the
Ethernet ones.&lt;/p&gt;
&lt;p&gt;The solution dawned on me when I realized that the fan header has the same pin
distance as any other pin header, be it on a Pi, any other microcontroller, or a
breadboard. Which means I can just make my own adapters, and run them from USB.
USB provides 5 volts, which means that a typical 12V would run slowly and
quietly, while still providing some airflow.&lt;/p&gt;
&lt;p&gt;And so I created my own USB-to-fan adapter.&lt;/p&gt;
&lt;h2&gt;Version 1.0&lt;/h2&gt;
&lt;p&gt;The first version was basically the USB-A connector and two pins connected by
short lengths of cables. I was not happy with it, because the USB pins turned
out to be very fragile, they would move with every touch, and I was concerned
what would happen when 5V would short out with a data pin. I don&#39;t know the USB specs
enough* to know what would be the outcome of that.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/59/7.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/59/7.jpg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/59/6.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/59/6.jpg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Version 2.0&lt;/h2&gt;
&lt;p&gt;For version 2.0 I used a prototype board to securely solder all four pins of the
USB connector, and so have the adapter more sturdy and safe. The additional advantage
here is that it can power more than one fan, the only limitation is the maximum
current a Pi can provide on a single USB port*&lt;/p&gt;
&lt;p&gt;The build process is very simple. I took a prototype board, I have some with the
perfect size, 6 pins wide. I cut it in half, and soldered the USB connector to
one side of it. In a USB-A connector, there are four pins, the middle two ones
are data, and the outer ones are +5V and GND.&lt;/p&gt;
&lt;p&gt;I then soldered two pin headers of two pins each, with spacing between them to
fit the fan connectors.&lt;/p&gt;
&lt;p&gt;Finally I connected the USB pins with the fan pins using wire I had left from
soldering some LEDs.&lt;/p&gt;
&lt;p&gt;The important thing is to make sure that there is no short circuit, and the data
pins are not connected to anything.&lt;/p&gt;
&lt;p&gt;I first tested it using my cheapest powerbank, in case I did something very bad.
It&#39;s better to burn a cheap powerbank rather than an expensive Pi. But in my
case there was no magic smoke, just a large fan running quietly. After letting
it run for a while I took the connection of faith, and connected the whole
contraption to one of the Pis in my cluster.&lt;/p&gt;
&lt;p&gt;And hey, it works like a charm, there are no additional cables coming from the
cluster, the fan is running, the Pis are cool as a cucumber*, and all is
fine in the world (if you ignore the news from a certain Western superpower).&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/59/1.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/59/1.jpg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;The adapter from the top...&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/59/2.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/59/2.jpg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;...and from the bottom. Yes, I know my soldering is bad.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/59/3.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/59/3.jpg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;And from the side.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/59/4.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/59/4.jpg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;Powering two 5V fans.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/59/5.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/59/5.jpg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;And doing what&#39;s it intended for. I still need to tidy the fan
cable, and maybe secure the fan to the cluster case.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;One more thing, this board is universal enough that it can power anything that
uses 5V and can use typical 2.54mm pins.&lt;/p&gt;
&lt;p&gt;I know, it&#39;s not something groundbreaking, not specially interesting, but it
does what it&#39;s intended to do, and I am happy with it. And it is a good day when
I can use my ~AK~ soldering iron.&lt;/p&gt;
&lt;p&gt;Thanks for reading!&lt;/p&gt;
&lt;p&gt;* at all.&lt;/p&gt;
&lt;p&gt;* It&#39;s 1.2A for a Pi 4b, but it shared among all USB ports. So one port can
provide 1.2A, but then the other ports are left with nothing. Or two ports can
provide 0.6A each, etc, etc.&lt;/p&gt;
&lt;p&gt;* Is any native speaker actually using this phrase, or is it just something
that every foreign learner of English has to memorize?&lt;/p&gt;
</description>
      <pubDate>Sat, 01 Feb 2025 24:00:00 GMT</pubDate>
      <dc:creator>STFN</dc:creator>
      <guid>https://stfn.pl/blog/59-powering-fans-from-usb-pi-cluster/</guid>
    </item>
    <item>
      <title>Using Pinchflat and Jellyfin to download and watch Youtube videos</title>
      <link>https://stfn.pl/blog/60-pinchflat-jellyfin-youtube-duo/</link>
      <description>&lt;p&gt;This post is in a way an extension of my &lt;a href=&quot;https://stfn.pl/blog/45-archiving-youtube/&quot;&gt;previous one about
Pinchflat&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Pinchflat is a service to download Youtube videos to your local hard drive. I have been using it
for quite some time now to download videos from my favourite YT channels.&lt;/p&gt;
&lt;p&gt;There are a few reasons why I download Youtube videos. One of them being data
preservation. As I wrote before, nothing on the Internet is permanent, and we
should never assume that the content we enjoy will be there forever. Secondly,
watching videos locally removes all the bloat, ads, comments, recommendations,
it&#39;s just a video file that you can watch however one wants. And finally, at
some point in the future it will be highly possible that I will be living on
limited LTE internet, and I want to hoard content locally for that hard period.&lt;/p&gt;
&lt;p&gt;I won&#39;t dive again into Pinchflat installation and configuration, I described
it in detail in that previous post, today I want to focus on serving data
downloaded with its help.&lt;/p&gt;
&lt;p&gt;But I have a few observation after using it for months.&lt;/p&gt;
&lt;p&gt;The first one is that Pinchflat downloads shorts by default, if you don&#39;t want
them, you need to turn it off manually in the Source settings.&lt;/p&gt;
&lt;p&gt;Youtube sometimes gets annoyed when Pinchflat tries to download too many videos
at once. If that happens,
the best solution is to shutdown the container and wait a few hours. After turning
it back again, the videos should continue downloading.&lt;/p&gt;
&lt;p&gt;To stop it from happening in the first place, one solution is to download the
videos in parts. Pinchflat allows to provide a cut off date for a feed, and with that
it will not download videos older than that date. When I add a new channel, I set
the cut off date for a few months from now, wait for those videos to download,
then wait a few hours, move the cut off date further to the past, download the
videos, etc, etc.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/60/pinchflat1.png&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/60/pinchflat1.png&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;Download cut-off date in the Source settings&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Now the Jellyfin part. I have Pinchflat and Jellyfin running both on my NAS. They
both have access to the same ZFS pool where I store all my videos. In Jellyfin
I have a separate library for my Youtube videos. In my experience the best
library type for those videos is &amp;quot;Home Videos and Photos&amp;quot;. At firsts I was
using &amp;quot;Shows&amp;quot;, but then Jellyfin would try to divide the videos into seasons,
which does not make sense for the content that I watch.&lt;/p&gt;
&lt;p&gt;Jellyfin does a rather good job in creating thumbnails for videos, there were
only a few times where I had to provide my own to make my library look pretty.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/60/pinchflat2sml.png&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/60/pinchflat2sml.png&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;My Youtube library in Jellyfin&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Also Jellyfin and Pinchflat are a good duo, as both automatically refresh, so
new videos appear in the library without any human intervention. If you watch
only a limited set of channels, with the combination of Pinchflat and Jellyfin
you could never need to visit the Youtube&#39;s page at all.&lt;/p&gt;
&lt;p&gt;Let&#39;s hope that Youtube will not find a way to block third party software, at
least in the near future. Until that, let&#39;s fill our hard drives :)&lt;/p&gt;
&lt;p&gt;Looks like that&#39;s it, a super short post this time, but I wanted to share my
process that I am happy with.&lt;/p&gt;
</description>
      <pubDate>Mon, 03 Feb 2025 24:00:00 GMT</pubDate>
      <dc:creator>STFN</dc:creator>
      <guid>https://stfn.pl/blog/60-pinchflat-jellyfin-youtube-duo/</guid>
    </item>
    <item>
      <title>Blog Question Challenge 2025</title>
      <link>https://stfn.pl/blog/61-blog-question-challenge/</link>
      <description>&lt;p&gt;I felt like also giving a go at this challenge. The replies I read at other
peoples&#39; blog were very interesting, thank you all for sharing your life &amp;lt;3&lt;/p&gt;
&lt;h3&gt;Why did you start blogging?&lt;/h3&gt;
&lt;p&gt;I enjoy sharing knowledge. I think it runs in the family, as both of my parents
are teachers. Also I enjoy connecting with people online. I&#39;ve been living on
the Internet since forever, and all that time I enjoyed not only ingesting
media, but also contributing to it. Internet forums were the first place. Then it
was the time of the centralized massive platforms. And now as the Internet is
becoming a place both too centralized and too hostile, I went to my own platform
on my own terms.&lt;/p&gt;
&lt;p&gt;And this &lt;a href=&quot;https://xkcd.com/208/&quot;&gt;XKCD&lt;/a&gt; feels very close to me.&lt;/p&gt;
&lt;h3&gt;What platform are you using for your blog, and why?&lt;/h3&gt;
&lt;p&gt;As I said in the previous question, I moved to selfhosting to be free from
centralized, commercial platforms on which you are just the product, lead by an
impenetrable algorithm. I prefer the small, &amp;quot;indie&amp;quot; web. This blog is made using
Astro, a static site generating framework, and I have used a ready-made template
to start. The HTML is hosted on a VPS I am renting, and I am serving it using
nginx. A very simple tech stack, but thanks to its simplicity it can handle a
lot of traffic with very little means. Mastodon requests flooding does not really
register on the load scale.&lt;/p&gt;
&lt;h3&gt;Have you blogged on other platforms before?&lt;/h3&gt;
&lt;p&gt;Yes, I used to have a personal/photography blog on Wordpress, but it went
into an indefinite hiatus many years ago.&lt;/p&gt;
&lt;h3&gt;How do you write your posts?&lt;/h3&gt;
&lt;p&gt;Mostly in VSCode, but recently I started moving to nvim, as it provides a much
cleaner and less distracting environment.&lt;/p&gt;
&lt;h3&gt;When do you feel most inspired to write?&lt;/h3&gt;
&lt;p&gt;When I learn a new skill, or I create something new. Or when I am reminded of
something that I used to be very much into, and I feel like sharing that
history. So in short: whenever inspiration strikes :)&lt;/p&gt;
&lt;h3&gt;Do you normally publish immediately after writing, or do you let it simmer a bit?&lt;/h3&gt;
&lt;p&gt;Depends on the type of the post. If it more like musings, or talking about
feelings or memories, I usually publish it at once, only after a second read. If
it is more technical, tutorial-like, then I spend more time polishing it,
re-reading after some with a fresh mind, doing corrections.&lt;/p&gt;
&lt;h3&gt;What’s your favourite post on your blog?&lt;/h3&gt;
&lt;p&gt;I would say those two:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn.pl/blog/34-pico-power-consumption-solar-panels/&quot;&gt;Reducing Raspberry Pi Pico W power consumption and a second attempt at using solar panels&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn.pl/blog/33-remembering-space-shuttle/&quot;&gt;The Space Shuttle - how I remember it&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The first one because I put a lof of effort into the project, the testing and I
was very happy of the outcome. And it reminds me of long summer days.&lt;/p&gt;
&lt;p&gt;The second one because it is very personal and I talk there about something that
has been an important thing for me for a significant period of my life.&lt;/p&gt;
&lt;p&gt;Also those two posts show the spectrum of my writing, from personal &amp;quot;memoirs&amp;quot;,
to technical/DIY tutorials.&lt;/p&gt;
&lt;h3&gt;Any future plans for the blog?&lt;/h3&gt;
&lt;p&gt;For this one nothing specific. I am thinking of starting another blog, in
Polish, that will be much less technical, tackling subjects that are local to
me, more personal, things that would not work for a global audience. I still
cannot decide on its name, though.&lt;/p&gt;
</description>
      <pubDate>Sat, 08 Feb 2025 24:00:00 GMT</pubDate>
      <dc:creator>STFN</dc:creator>
      <guid>https://stfn.pl/blog/61-blog-question-challenge/</guid>
    </item>
    <item>
      <title>Python Dependency injection for cleaner I/O handling</title>
      <link>https://stfn.pl/blog/62-python-dependency-injection/</link>
      <description>&lt;p&gt;Apart from these few posts about simple things in MicroPython, this is the
first post on this blog in which I talk about programming in Python in a
slightly more advanced way.&lt;/p&gt;
&lt;p&gt;My official job title is &amp;quot;Senior Python Developer&amp;quot;, and I feel that I do have
some experience in working with Python, but I&#39;ve never before felt that I had
something interesting enough to share on this blog in that area. And of course
there is the whole matter of impostor syndrome, making me doubt whether what I
know and use is actually the best thing that should be shared with others. But
there can always be a first time, right?&lt;/p&gt;
&lt;p&gt;In this blog post I want to share a way of writing code that I was taught by a
super cool senior developer. She taught me a lot of things, mostly in terms of
ideas of how software should be written. I think this is one of the things that
differentiates junior developers from senior developers. Juniors think in code,
like &amp;quot;I&#39;ll put a for loop here&amp;quot; or, &amp;quot;I should do a class here&amp;quot;, whereas seniors
know and think in ideas, &amp;quot;our software needs to be better separation of
concerns&amp;quot;, or &amp;quot;we are breaking the single responsibility principle in this
place&amp;quot;. Learning to think in abstract ideas and knowing ways to implement it is
imho an important part of one&#39;s programming journey.&lt;/p&gt;
&lt;p&gt;Fans of Clean Architecture should recognize some concepts from it in this blog post.
CA is something I have barely scraped the surface of, but I am already a big fan
of it and I try to implement it as best I can in my software. Maybe one day,
when I get better at it, I will write another blog post focusing on it.&lt;/p&gt;
&lt;p&gt;OK, the intro is done, let&#39;s go to the main part.&lt;/p&gt;
&lt;h2&gt;Setting the stage&lt;/h2&gt;
&lt;p&gt;Let&#39;s imagine a scenario that you are at work, and your boss comes to you and says&lt;/p&gt;
&lt;p&gt;&lt;em&gt;You need to create a piece of software that will download a joke about
Chuck Norris, and count the number of times a specific word is used in that
joke. That functionality is absolutely crucial for our business!&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Weird but ok. The actual usefulness of this code is irrelevant, what we should
focus on is &lt;em&gt;how it works&lt;/em&gt;. What is important is that the code needs to have two
distinct functionalities: business logic (counting the number of occurrences of
a word) and input/output handling (external API calls).&lt;/p&gt;
&lt;p&gt;You are a good developer, so you sit down to work and create a class that does
what it should, and add tests. You should always have tests for your code.&lt;/p&gt;
&lt;p&gt;The code is available at &lt;a href=&quot;https://codeberg.org/stfn/python-dependency-injection-io&quot;&gt;codeberg.org&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;First solution&lt;/h2&gt;
&lt;p&gt;main.py&lt;/p&gt;
&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; httpx


CHUCK_NORRIS_API_URL &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;https://api.chucknorris.io/jokes/&quot;&lt;/span&gt;

ID1 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;a4aNCYsKQu-4LKDLkTQLSA&quot;&lt;/span&gt;


&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ChuckNorrisJokeWordCounter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;_download_joke&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;self&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;str&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 operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;dict&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        url &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; CHUCK_NORRIS_API_URL &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;id&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; httpx&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;get&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;url&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;json&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;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;word_counter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;self&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; word&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;str&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 operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-interpolation&quot;&gt;&lt;span class=&quot;token string&quot;&gt;f&quot;fetching joke with id &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;...&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        joke_data &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_download_joke&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        joke_text &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; joke_data&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;value&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-interpolation&quot;&gt;&lt;span class=&quot;token string&quot;&gt;f&quot;downloaded joke with content: &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;joke_text&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; joke_text&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;count&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;word&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;


&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; __name__ &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;__main__&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    chuck_word_counter &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; ChuckNorrisJokeWordCounter&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    word_count &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; chuck_word_counter&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;word_counter&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Chuck&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; ID1&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-interpolation&quot;&gt;&lt;span class=&quot;token string&quot;&gt;f&quot;in that joke, the word Chuck was found &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;word_count&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; times&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;test.py&lt;/p&gt;
&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; httpx &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; Response

&lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; main &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; CHUCK_NORRIS_API_URL&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; ChuckNorrisJokeWordCounter


&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;test_counting_words_chuck&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;respx_mock&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 builtin&quot;&gt;id&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;123qwe&quot;&lt;/span&gt;
    response_data &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 string&quot;&gt;&quot;categories&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 string&quot;&gt;&quot;created_at&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;2020-01-05 13:42:26.766831&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&quot;icon_url&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;https://api.chucknorris.io/img/avatar/chuck-norris.png&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&quot;id&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&quot;updated_at&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;2020-01-05 13:42:26.766831&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&quot;url&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-interpolation&quot;&gt;&lt;span class=&quot;token string&quot;&gt;f&quot;https://api.chucknorris.io/jokes/&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&quot;value&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Chuck Norris once cast a fishing line into the Atlantic Ocean and caught 243 fish...then the hook hit the water&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;
    mock_response &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; respx_mock&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;get&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;CHUCK_NORRIS_API_URL &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    mock_response&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;return_value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Response&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; json&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;response_data&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    chuck_word_counter &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; ChuckNorrisJokeWordCounter&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    word_count &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; chuck_word_counter&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;word_counter&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Chuck&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;assert&lt;/span&gt; word_count &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;


&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;test_counting_words_norris&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;respx_mock&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 builtin&quot;&gt;id&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;123qwe&quot;&lt;/span&gt;
    response_data &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 string&quot;&gt;&quot;categories&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 string&quot;&gt;&quot;created_at&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;2020-01-05 13:42:26.766831&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&quot;icon_url&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;https://api.chucknorris.io/img/avatar/chuck-norris.png&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&quot;id&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&quot;updated_at&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;2020-01-05 13:42:26.766831&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&quot;url&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-interpolation&quot;&gt;&lt;span class=&quot;token string&quot;&gt;f&quot;https://api.chucknorris.io/jokes/&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&quot;value&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Chuck Norris once cast a fishing line into the Atlantic Ocean and caught 243 fish...then the hook hit the water&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;
    mock_response &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; respx_mock&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;get&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;CHUCK_NORRIS_API_URL &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    mock_response&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;return_value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Response&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; json&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;response_data&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    chuck_word_counter &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; ChuckNorrisJokeWordCounter&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    word_count &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; chuck_word_counter&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;word_counter&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Norris&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;assert&lt;/span&gt; word_count &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let&#39;s run it:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;python main.py
&lt;span class=&quot;token operator&quot;&gt;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; fetching joke with &lt;span class=&quot;token function&quot;&gt;id&lt;/span&gt; a4aNCYsKQu-4LKDLkTQLSA&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;.
&lt;span class=&quot;token operator&quot;&gt;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; downloaded joke with content: Chuck Norris once cast a fishing line 
&lt;span class=&quot;token operator&quot;&gt;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; into the Atlantic Ocean and caught &lt;span class=&quot;token number&quot;&gt;243&lt;/span&gt; fish&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;.then the hook hit the water
&lt;span class=&quot;token operator&quot;&gt;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; that joke, the word Chuck was found &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;token builtin class-name&quot;&gt;times&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The code works, but there is a number of issues with it, that are best visible
when reading the tests.&lt;/p&gt;
&lt;p&gt;The class &lt;code&gt;ChuckNorrisJokeWordCounter&lt;/code&gt; is doing two things: downloading the joke
from some HTTP API, and doing the business logic, which is counting the number
of times a certain word is used in the joke. Therefore, each time we want to
test the business logic, we need to mock the API call. This results in a lot of
boilerplate code, and worsens the readability of the tests. You can reduce the
amount of code using fixtures, but still, this is not great.&lt;/p&gt;
&lt;p&gt;And, what if one day the Chuck Norris API moves from HTTP to say,
downloading jokes as txt file from S3? Or being sent by Matrix-style neural link?
Then we would need to rewrite the download logic inside the class, modify all
the tests, etc. Not good. This can be done better.&lt;/p&gt;
&lt;h2&gt;Second solution&lt;/h2&gt;
&lt;p&gt;And here is another way to do it:&lt;/p&gt;
&lt;p&gt;main.py&lt;/p&gt;
&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; typing &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; Protocol
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; httpx


ID1 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;a4aNCYsKQu-4LKDLkTQLSA&quot;&lt;/span&gt;


&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ChuckNorrisJokeClientProtocol&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Protocol&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;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;get_joke&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;self&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;str&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 operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;dict&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 keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ChuckNorrisJokeHTTPClient&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    CHUCK_NORRIS_API_URL &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;https://api.chucknorris.io/jokes/&quot;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;get_joke&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;self&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;str&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 operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;dict&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        url &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;CHUCK_NORRIS_API_URL &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;id&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; httpx&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;get&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;url&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;json&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;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ChuckNorrisJokeWordCounter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;__init__&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;self&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; client&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ChuckNorrisJokeClientProtocol&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;client &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; client

    &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;word_counter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;self&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; word&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;str&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 operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-interpolation&quot;&gt;&lt;span class=&quot;token string&quot;&gt;f&quot;fetching joke with id &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;...&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        joke_data &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;client&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;get_joke&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        joke_text &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; joke_data&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;value&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-interpolation&quot;&gt;&lt;span class=&quot;token string&quot;&gt;f&quot;downloaded joke with content: &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;joke_text&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; joke_text&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;count&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;word&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;


&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; __name__ &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;__main__&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    chuck_word_counter &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; ChuckNorrisJokeWordCounter&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;client&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;ChuckNorrisJokeHTTPClient&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;
    word_count &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; chuck_word_counter&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;word_counter&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Chuck&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; ID1&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-interpolation&quot;&gt;&lt;span class=&quot;token string&quot;&gt;f&quot;in that joke, the word Chuck was found &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;word_count&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; times&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;test.py&lt;/p&gt;
&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; httpx &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; Response

&lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; main &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; ChuckNorrisJokeWordCounter&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; ChuckNorrisJokeHTTPClient


&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;test_chuck_norris_http_client&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;respx_mock&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 builtin&quot;&gt;id&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;123qwe&quot;&lt;/span&gt;
    response_data &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 string&quot;&gt;&quot;categories&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 string&quot;&gt;&quot;created_at&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;2020-01-05 13:42:26.766831&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&quot;icon_url&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;https://api.chucknorris.io/img/avatar/chuck-norris.png&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&quot;id&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&quot;updated_at&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;2020-01-05 13:42:26.766831&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&quot;url&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-interpolation&quot;&gt;&lt;span class=&quot;token string&quot;&gt;f&quot;https://api.chucknorris.io/jokes/&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&quot;value&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Chuck Norris once cast a fishing line into the Atlantic Ocean and caught 243 fish...then the hook hit the water&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;
    mock_response &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; respx_mock&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;get&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ChuckNorrisJokeHTTPClient&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;CHUCK_NORRIS_API_URL &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    mock_response&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;return_value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Response&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; json&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;response_data&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    http_client &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; ChuckNorrisJokeHTTPClient&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    response &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; http_client&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;get_joke&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;assert&lt;/span&gt; response &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; response_data
    &lt;span class=&quot;token keyword&quot;&gt;assert&lt;/span&gt; mock_response&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;called


&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ChuckNorrisJokeMockClient&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;get_joke&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;self&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;id&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 operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;dict&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&quot;categories&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 string&quot;&gt;&quot;created_at&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;2020-01-05 13:42:26.766831&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&quot;icon_url&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;https://api.chucknorris.io/img/avatar/chuck-norris.png&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&quot;id&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&quot;updated_at&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;2020-01-05 13:42:26.766831&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&quot;url&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-interpolation&quot;&gt;&lt;span class=&quot;token string&quot;&gt;f&quot;https://api.chucknorris.io/jokes/&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&quot;value&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Chuck Norris once cast a fishing line into the Atlantic Ocean and caught 243 fish...then the hook hit the water&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 keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;test_counting_words_chuck&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;respx_mock&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 builtin&quot;&gt;id&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;123qwe&quot;&lt;/span&gt;

    chuck_word_counter &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; ChuckNorrisJokeWordCounter&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ChuckNorrisJokeMockClient&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;
    word_count &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; chuck_word_counter&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;word_counter&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Chuck&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;assert&lt;/span&gt; word_count &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;


&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;test_counting_words_norris&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;respx_mock&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 builtin&quot;&gt;id&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;123qew&quot;&lt;/span&gt;
    chuck_word_counter &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; ChuckNorrisJokeWordCounter&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ChuckNorrisJokeMockClient&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;
    word_count &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; chuck_word_counter&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;word_counter&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Norris&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;assert&lt;/span&gt; word_count &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A lot has changed, so let&#39;s go through the code slowly.&lt;/p&gt;
&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ChuckNorrisJokeClientProtocol&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Protocol&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;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;get_joke&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;self&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;str&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 operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;dict&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 keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ChuckNorrisJokeHTTPClient&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    CHUCK_NORRIS_API_URL &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;https://api.chucknorris.io/jokes/&quot;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;get_joke&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;self&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;str&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 operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;dict&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        url &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;CHUCK_NORRIS_API_URL &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;id&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; httpx&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;get&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;url&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;json&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;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ChuckNorrisJokeWordCounter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;__init__&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;self&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; client&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ChuckNorrisJokeClientProtocol&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;client &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; client&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There are now two classes, &lt;code&gt;ChuckNorrisJokeWordCounter&lt;/code&gt; and
&lt;code&gt;ChuckNorrisJokeHTTPClient&lt;/code&gt;. The HTTPClient is only responsible for doing the
API HTTP calls. The &lt;code&gt;CHUCK_NORRIS_API_URL&lt;/code&gt; constant was moved to inside that
class, as it is only applicable to it.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;ChuckNorrisJokeWordCounter&lt;/code&gt; class accepts a class as an argument, a class
which needs to adhere to the &lt;code&gt;ChuckNorrisJokeClientProtocol&lt;/code&gt;. This is exactly
the Dependency Injection part. The WordCounter class depends on a client that
will provide it with the joke, and that dependency is injected in the class&#39;
constructor. The WordCounter does not care from where the joke is fetched, as
long as it can fetch it using the public &lt;code&gt;get_joke()&lt;/code&gt; method. The WordCounter
knows that this method is available because it is in the protocol.&lt;/p&gt;
&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;    &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;word_counter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;self&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; word&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;str&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 operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-interpolation&quot;&gt;&lt;span class=&quot;token string&quot;&gt;f&quot;fetching joke with id &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;...&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        joke_data &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;client&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;get_joke&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        joke_text &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; joke_data&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;value&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-interpolation&quot;&gt;&lt;span class=&quot;token string&quot;&gt;f&quot;downloaded joke with content: &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;joke_text&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; joke_text&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;count&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;word&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And now in the business logic part, the WordCounter can just use the client&#39;s
&lt;code&gt;get_joke()&lt;/code&gt; method by calling &lt;code&gt;self.client.get_joke()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The code could be further improved by defining a Pydantic Model called
&lt;code&gt;ChuckNorrisJoke&lt;/code&gt; to which the &lt;code&gt;get_joke&lt;/code&gt;() output would be serialized, but that
is outside of the scope of this post. For this example let&#39;s just assume that we
know that the &lt;code&gt;get_joke()&lt;/code&gt; method always returns a dict with a key &lt;code&gt;value&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;By doing this change we have fixed the code in two ways:&lt;/p&gt;
&lt;p&gt;First, it now adheres to the first principle of SOLID: Single responsibility
principle. Each class does only one thing.&lt;/p&gt;
&lt;p&gt;But wait, there&#39;s more! The code now also adheres to the second principle of
SOLID: Open–closed principle.&lt;/p&gt;
&lt;p&gt;The code is now &amp;quot;open for extension, but closed for modification&amp;quot;. The
WordCounter class &lt;code&gt;word_counter&lt;/code&gt; method can be left unmodified, and if at one
point we will need to fetch the jokes from another source, we can just write
another client, for example &lt;code&gt;ChuckNorrisFTPClient&lt;/code&gt; that will expose a
&lt;code&gt;get_joke()&lt;/code&gt; method downloading jokes from an FTP server.&lt;/p&gt;
&lt;p&gt;And now the tests.&lt;/p&gt;
&lt;p&gt;The tests are now in two groups. The first group (here there&#39;s only one test for
it, but in real life there would of course be a lot more) is responsible for
testing the &lt;code&gt;ChuckNorrisJokeHTTPClient&lt;/code&gt; itself, and those tests still need to
mock out external API calls.&lt;/p&gt;
&lt;p&gt;The second group is the cool part:&lt;/p&gt;
&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ChuckNorrisJokeMockClient&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;get_joke&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;self&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;id&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 operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;dict&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&quot;categories&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 string&quot;&gt;&quot;created_at&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;2020-01-05 13:42:26.766831&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&quot;icon_url&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;https://api.chucknorris.io/img/avatar/chuck-norris.png&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&quot;id&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&quot;updated_at&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;2020-01-05 13:42:26.766831&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&quot;url&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string-interpolation&quot;&gt;&lt;span class=&quot;token string&quot;&gt;f&quot;https://api.chucknorris.io/jokes/&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&quot;value&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Chuck Norris once cast a fishing line into the Atlantic Ocean and caught 243 fish...then the hook hit the water&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 keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;test_counting_words_chuck&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;respx_mock&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 builtin&quot;&gt;id&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;123qwe&quot;&lt;/span&gt;

    chuck_word_counter &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; ChuckNorrisJokeWordCounter&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ChuckNorrisJokeMockClient&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;
    word_count &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; chuck_word_counter&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;word_counter&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Chuck&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;assert&lt;/span&gt; word_count &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;No need to mock HTTP calls anymore. The WordCounter accepts any Client as long
as it exposes a &lt;code&gt;get_joke()&lt;/code&gt; method, right? So we can write a mock client that
just returns a dictionary. The WordCounter can then take that client, and it
will have no idea, nor will it care that we are handling it just a dict saved in
a file. And we can have as many mock clients as we want, providing different
jokes, or providing broken replies, etc.&lt;/p&gt;
&lt;p&gt;The tests are now much cleaner, easier and quicker to write. There is much less
underlying logic (&lt;em&gt;Explicit is better than implicit&lt;/em&gt;).&lt;/p&gt;
&lt;h2&gt;Summing up&lt;/h2&gt;
&lt;p&gt;This concept can be applied whenever there is code that needs to do fetching
data from somewhere, and then do business logic on it.&lt;/p&gt;
&lt;p&gt;A very good example of it, about which I might write in the future, is doing
database calls, for example in Django.&lt;/p&gt;
&lt;p&gt;In that case, a &lt;code&gt;ChuckNorrisDBClient&lt;/code&gt; could be written that does something like&lt;/p&gt;
&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;models &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; ChuckNorrisJoke

&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ChuckNorrisJokeDBClient&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;model_to_dict&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;self&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; model_instance&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 operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;dict&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;# some logic to deserialize model instance to a dictionary&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;# again, Pydantic would also be great here&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; model_dict

	&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;get_joke&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;str&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 operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;dict&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;model_to_dict&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ChuckNorrisJoke&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;objects&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;get&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;id&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;And then in the test we would again use a &lt;code&gt;ChuckNorrisJokeMockClient&lt;/code&gt; which does
not use the DB at all, making the test suite much quicker to run.&lt;/p&gt;
&lt;p&gt;And that&#39;s it! My first post on &amp;quot;advanced Python&amp;quot;. I hope you liked it, I would
love to receive feedback on it by any means possible, whether a comment below, a
mention in the Fediverse, or via email. The links are in the footer.&lt;/p&gt;
&lt;p&gt;Now that I wrote this post, I feel like writing more posts like this, so there
is a high possibility this will not be the last one :)&lt;/p&gt;
&lt;p&gt;Thanks for reading!&lt;/p&gt;
</description>
      <pubDate>Mon, 10 Feb 2025 24:00:00 GMT</pubDate>
      <dc:creator>STFN</dc:creator>
      <guid>https://stfn.pl/blog/62-python-dependency-injection/</guid>
    </item>
    <item>
      <title>Blog Questions Challenge Technology Edition</title>
      <link>https://stfn.pl/blog/63-technical-question-challenge/</link>
      <description>&lt;p&gt;I was tagged by &lt;a href=&quot;https://82mhz.net/posts/2025/02/blog-questions-challenge-technology-edition/&quot;&gt;82Mhz&lt;/a&gt;
to answer this set of questions, so here it goes:&lt;/p&gt;
&lt;h3&gt;When Did You First Get Interested In Technology?&lt;/h3&gt;
&lt;p&gt;I&#39;ve been interested in technology as long as I remember. I think there were
multiple sources of this.&lt;/p&gt;
&lt;p&gt;First one would be my grandfather, who as a child growing up in hard times, had
to have a wide set of skills. Today he could be called a tinkerer and a maker.
He showed me how to fix things at home, how to sculpt in wood. He is 90 now and
uses a smartphone to read the news, and a laptop to write books and emails.&lt;/p&gt;
&lt;p&gt;Second one would be my father, similar in his skillset to my grandfather. He was
also an avid collector of &amp;quot;Młody Technik&amp;quot; (Young Technician) magazines from the
80s. I would spend the summers reading them, they had a wide range of articles
on anything regarding technology, from 80s computers to helicopters, ships and
trains. A few of them had lessons in writing C! My dad also gave me both sci-fi
and non-fiction science books, further fueling my love for technology.&lt;/p&gt;
&lt;p&gt;He bought our first PC when I was around 9, and I was instantly hooked. At first
that PC had only DOS, so I had to learn the command line to run the first
computer game I ever played: Mahjongg.&lt;/p&gt;
&lt;p&gt;It&#39;s funny that despite all this, in my teens I never envisioned my career to be
connected with technology, and I only switched careers to become a software
developer in my thirties.&lt;/p&gt;
&lt;h3&gt;What’s Your Favourite Piece Of Technology All-Time?&lt;/h3&gt;
&lt;p&gt;That is a very hard question, because what exactly is a piece of technology.
Does an anvil count? A knife? Having so many options makes it hard to chose
between, for example, the Space Shuttle and a lathe.&lt;/p&gt;
&lt;p&gt;But to keep it light and short, I will say that my answer is: trains. I like
trains. Trains are cool. I take the train whenever it is possible. I haven&#39;t had
the chance to take a high speed train (160kph+) yet, but I hope it will happen sometime in
the future.&lt;/p&gt;
&lt;h3&gt;What’s Your Favourite Piece Of Technology Right Now?&lt;/h3&gt;
&lt;p&gt;Another hard question, but ok, let&#39;s try: Renewable energy sources.&lt;/p&gt;
&lt;p&gt;Wind turbines and solar panels are a game changer, and give me hope for the
future. Not only because the energy they produce is green, but also because it
is localized and personal. I can, and will have solar panels on my future house,
and with a battery they will will be independence from the grid. The other thing
is, I love the sight of wind turbines over the fields. They look so magnificent,
slowly turning in the wind, and not belching smoke.  Down with the coal and oil,
renewables are the future (and nuclear, but that is another story).&lt;/p&gt;
&lt;h3&gt;Name One New Cool Piece Of Technology We’ll Have In 25 Years!&lt;/h3&gt;
&lt;p&gt;I wish that in 25 years we will still be using our current technology. I wish
things, machines, computers, electronics in general, were made to last. In many
areas like smartphones, technical progress is now mostly fake, and aimed at
forcing people to buy new stuff. This, connected with planned obsolescence, only
increases waste and energy usage. How great it would be if you could use a
washing machine that was made today for 25 years? Or a smartphone, or a tv. And then it
could be fully recycled and used to make new copies of it. Circular economy.
Degrowth. That would be the coolest thing.&lt;/p&gt;
&lt;h3&gt;Final Thoughts&lt;/h3&gt;
&lt;p&gt;Thanks for reading! I am not tagging anyone specifically, but please feel
encouraged to also answer those questions, and send me your blog post :)&lt;/p&gt;
</description>
      <pubDate>Sat, 15 Feb 2025 24:00:00 GMT</pubDate>
      <dc:creator>STFN</dc:creator>
      <guid>https://stfn.pl/blog/63-technical-question-challenge/</guid>
    </item>
    <item>
      <title>I&#39;m going back to astrophotography</title>
      <link>https://stfn.pl/blog/64-going-back-to-astrophotography/</link>
      <description>&lt;p&gt;I&#39;m slowly going back to astrophotography.&lt;/p&gt;
&lt;p&gt;So far my best year in AP was 2022 (apart from the &lt;a href=&quot;https://stfn.pl/blog/48-comet/&quot;&gt;famous
comet&lt;/a&gt;), as my &lt;a href=&quot;https://www.astrobin.com/users/stfn&quot;&gt;Astrobin
gallery&lt;/a&gt; shows.&lt;/p&gt;
&lt;p&gt;Since then I either have not had the time, or the energy, or the weather was
awful for weeks. Also the last year or so I have been fighting with equipments
problems, stopping me from doing any useful work.&lt;/p&gt;
&lt;p&gt;But I&#39;m coming back, I want to come back and do it again, as I find it to be a
fascinating hobby, combining space, physics, and computers.&lt;/p&gt;
&lt;p&gt;The future looks promising, as (if everything goes well) I will be moving to a house
at the end of his year, and then doing AP will mean simply going outside, and
not needing to drive 30km to a good place. That should make things much simpler.&lt;/p&gt;
&lt;p&gt;Back to present, I went out to try doing AP again twice in the last week, three
days ago and yesterday. While the equpiment problems are still not solved, I
managed to get at least something, and I quite like the result. So let me share
it with you (looks better in dark mode, click for a larger version):&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/64/persei.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/64/persei.jpg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This is the Alpha Persei Cluster, an open cluster of stars in the middle of the constellation of Perseus.&lt;/p&gt;
&lt;p&gt;The brightest star near the middle is Mirfak,  Alpha Persei. Most of the bright
stars in the picture belong to the same cluster (group) of stars, living
relatively close to each other. I especially like σ Persei (delta Persei), the
one closer to the left of the image, with its beautiful, deep yellow colour. The
whole cluster is around 560 light years from us.&lt;/p&gt;
&lt;p&gt;I took the information above from Wikipedia, and I invite you to read more on
the cluster &lt;a href=&quot;https://en.wikipedia.org/wiki/Alpha_Persei_Cluster&quot;&gt;on its Wikipedia page at that site that we need to cherish and it&#39;s
again attacked by right wingers, as most good things in
life&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;the parts below will mostly be interesting for people deep into AP&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;Acquisition details&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Lens: Irix 150mm f/2.8 lens, wide open.&lt;/li&gt;
&lt;li&gt;Camera: QHY 168C with an IR filter.&lt;/li&gt;
&lt;li&gt;Mount: iOptron GEM28.&lt;/li&gt;
&lt;li&gt;Additional: Astrojolo Astrolink 4 and dew heater for the lens&lt;/li&gt;
&lt;li&gt;Acquistion control: Raspberry Pi 5 with Stellarmate OS.&lt;/li&gt;
&lt;li&gt;Guiding (not used): ZWO ASI 120MC-S with ZWO mini scope&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;90 x 60s exposures. Acquisition controlled using Stellarmate OS, and EKOS in
kStars. Calibration frames: flats and bias (I need to redo my dark library).&lt;/p&gt;
&lt;p&gt;Stacked in Pixinsight using FastBatchPreprocessing.&lt;/p&gt;
&lt;p&gt;Postprocessing:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;BlurExterminator&lt;/li&gt;
&lt;li&gt;Graxpert - background extraction&lt;/li&gt;
&lt;li&gt;Graxpert - denoising&lt;/li&gt;
&lt;li&gt;SPCC&lt;/li&gt;
&lt;li&gt;SCNR&lt;/li&gt;
&lt;li&gt;Statistical Stretch from SetiAstro scripts repository&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A touch of curves and saturation in GIMP.&lt;/p&gt;
&lt;h2&gt;Equipment problems&lt;/h2&gt;
&lt;p&gt;The problem is simple: I cannot guide. The most frustrating part is that guiding
used to just work for me in the past, and now it does not.&lt;/p&gt;
&lt;p&gt;Since guiding stopped working, I replaced the guiding camera, most of the
cables, the Raspberry Pi and the OS from Astroberry to Stellarmate. I&#39;ve sent
the mount to a person who services mounts and said everything was fine with the
mount, except for a loose cable (which I already replaced). The last hardware
thing for me to test is to replace the &amp;quot;telephone&amp;quot; cable between the mount and
the mount pilot, and I will do it as soon as RJ12 connectors arrive by post.&lt;/p&gt;
&lt;p&gt;Both in EKOS and PHD2, the guiding calibration cannot finish, as the software is
not able to move the mount enough. Slewing is working fine, GOTO too. Mount is
connected, the guiding camera is doing the images. Polar alignment is as precise
as it is possible, done with the polar alignment helper in EKOS. The mount is
tracking, I can do 60s exposures at 150mm focal length without issues. But when
it comes to the actual guiding, nothing happens. Calibration fails and guiding
does not work.&lt;/p&gt;
&lt;p&gt;I&#39;ve digged through the web several times, and other people seemed to have
similar issues, but their solutions did not help me.&lt;/p&gt;
&lt;p&gt;I think I just need to spend the night doing debugging, testing all possible
combinations of settings. I have this feeling that the actual cause is very
basic and would be obvious to many people, but I don&#39;t have anyone experienced
in AP who would join me on site. And to do actual, proper debugging I need to
sit next to mount without haste for a long time, which is not the easiest thing
when it&#39;s -10C outside.&lt;/p&gt;
&lt;p&gt;If I don&#39;t find a solution anytime soon, I will probably write a few long posts
on some internet forums, not sure which one, either the INDI one, or the one for
Stellarmate, or astropolis.pl (the one which I usually visit).&lt;/p&gt;
&lt;p&gt;Eh, anyway, it&#39;s good to have a blog, at least I can vent my frustrations here :)&lt;/p&gt;
&lt;p&gt;But on the other hand, even with all those issues, being under a night sky in
the dark, and just watching it is a joy. I have a pair of cheap binoculars, and
for the price they show me wonders. It is a very calming experience and makes me want to go
back and try again, and even if I cannot take a good picture, I can at least
take a good look.&lt;/p&gt;
&lt;p&gt;Thanks for reading!&lt;/p&gt;
</description>
      <pubDate>Wed, 19 Feb 2025 24:00:00 GMT</pubDate>
      <dc:creator>STFN</dc:creator>
      <guid>https://stfn.pl/blog/64-going-back-to-astrophotography/</guid>
    </item>
    <item>
      <title>This post is very political, but then again, all the previous ones were too</title>
      <link>https://stfn.pl/blog/65-oh-no-its-politics/</link>
      <description>&lt;h1&gt;FIX TUBE URL&lt;/h1&gt;
&lt;p&gt;I don&#39;t have anything to write about computers for now. Not much is happening
for me on this field. My homelab is still homelabbing, BOINC is being crunched,
my Pi cluster is running, things are going their own course.&lt;/p&gt;
&lt;p&gt;But I have this growing frustration about the state of world affairs and this is
the place where I can vent it.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;But STFN, this is a blog about technology, don&#39;t add politics to it&lt;/em&gt;, some of you
might say. To which I reply: I do not believe in the separation of politics and
technology.&lt;/p&gt;
&lt;p&gt;Everything we do with technology is connected with politics. The decisions we
make in IT are, well, I don&#39;t really want to talk in absolutes, so I will use
the word &amp;quot;often&amp;quot;, often are connected to our worldview, and from our worldview
come out political beliefs.&lt;/p&gt;
&lt;p&gt;Why do I talk about selhosting Immich and not giving away all my photos to
Google Images? Because I don&#39;t like capitalism and corporations. Why do I talk
about the Fediverse and GoToSocial, and not praise Twitter or TruthSocial or any
other nazibar dot com? Because I believe in grassroot movements, in community,
in the left-wing self-organisation. Also because of my political beliefs I give
my GPU to scientists with BOINC, and not to crypto scammers.&lt;/p&gt;
&lt;p&gt;So yes, this blog is political.&lt;/p&gt;
&lt;p&gt;A few days ago I was listening to a &lt;a href=&quot;https://www.youtube.com/watch?v=f5CAYPDeKWY&quot;&gt;podcast where Wojtek Żubr Boliński was
talking with a game designer&lt;/a&gt;,
and one of their topics was the accusation that some people raise, that modern
games are &amp;quot;political&amp;quot; or even &amp;quot;woke&amp;quot; (woke is absolutely the most pointless,
stupidest, umbrella term for &amp;quot;anything I don&#39;t like&amp;quot;) and their conclusion was
that games have always been political, since their beginnings, it&#39;s just that
people did not see it. Their politics have been invisible because they were of
the kind that have been permeating the global life for the last decades, US
good, third countries people bad, liberty good, money good, men strong, damsels
in distress.&lt;/p&gt;
&lt;p&gt;And now when some games try to break that sanctified consensus, &amp;quot;the gamers&amp;quot; are
enraged because &amp;quot;the games are being taken over by the woke politics&amp;quot;.&lt;/p&gt;
&lt;p&gt;And same with other types of media. I never watched Thunder in Paradise like
Wojtek did, but I was a big fan of the A-Team. When I was 8 or so I liked it
because funny guys and machine guns and explosions, but when you think about it,
it&#39;s highly political and leftist. A group of people hiding from the government,
helping, for the lack of the better term, the working class from oppression.&lt;/p&gt;
&lt;p&gt;Sigh. I don&#39;t have a smart point here, again, just venting.&lt;/p&gt;
&lt;p&gt;Now to other things.&lt;/p&gt;
&lt;p&gt;I cannot reiterate how much I hate Trump and I hate Musk and I hate JD Vance and
their lackeys*. For many reasons, but currently the main reason is the ongoing
war.&lt;/p&gt;
&lt;p&gt;The Western World, if we even can still use that term, should do everything they
can to help Ukraine fight Russia. Not only for the obvious fact that Ukraine is
an independent country and has every right to exist and prosper. But also
because Russia is an aggressive, genocidal regime that will not stop at Ukraine
if it falls. Eastern European countries have been invaded by those fuckers many
times, and it&#39;s obvious they want to do it again. And Trump trying to be friends
with Putin over our heads is the worst possible scenario.&lt;/p&gt;
&lt;p&gt;And then there&#39;s Europe. The current situation, paradoxically, is a chance for
Europe to finally stand on it&#39;s own and not depend on the chaotic uncle from the
other side of the Atlantic. I have a very strong belief that the European Union
is the best thing to have happened to Europe in its history, and the only way
for Europe to move forward, to survive even, is much closer cooperation, both
commercial and military. We need a common army, a common market, and buy goods
locally, not import goods from China and technology from the US. We need our own
services as we have Galileo, our GPS alternative. In a way, I am becoming more
and more a European nationalist.&lt;/p&gt;
&lt;p&gt;But then there are the morons from the European far right movements, gaining
ground and talking in the voice of Kreml and Trump (which is now basically the
same), saying that the EU is bad, and Europe should again be just a bunch of
small, isolated, chauvinist countries. That worked out very well in 1914 and
1939, didn&#39;t it? Brexit worked out great for the UK, didn&#39;t it?&lt;/p&gt;
&lt;p&gt;Here in my country Konfederacja, a local far-right movement is now getting 20%
of the votes in the polls, and they candidate for the president is third in the
polls.&lt;/p&gt;
&lt;p&gt;I cannot imagine why would anyone vote for the far right, but looks like I am in
the vast minority (vast minority? is that a phrase?), as the party I vote for,
the reasonably lefty RAZEM is constantly below 5% in the pools.&lt;/p&gt;
&lt;p&gt;This is all so frustrating. A few weeks ago I started reading a very smart book,
&amp;quot;Our Modern Crisis&amp;quot; by Murray Bookchin, but I left it, I was too tired, I need
something to get my mind of the world, so I started reading The Expanse. Which
of course is also very much political but in a different way :)))&lt;/p&gt;
&lt;p&gt;There are so many other things in today&#39;s world that make me sad or frustrated
or angry or everything in between, but I&#39;ll leave them for other posts, not
everything at once :)&lt;/p&gt;
&lt;p&gt;To end this post with a silver lining, there are things that make me feel
better. To list just two, in no particular order:&lt;/p&gt;
&lt;p&gt;There&#39;s &lt;a href=&quot;https://www.re-des.org/es/a-solarpunk-manifesto/&quot;&gt;Solarpunk&lt;/a&gt;, a
movement based on hope and self-organization and nature. I haven&#39;t yet read much
works of writing based in that idea, but I&#39;m just happy it&#39;s there. One day I
will wrote more on it.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;We are solarpunks because optimism has been taken away from us and we are trying to take it back.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;And then there are the &lt;a href=&quot;https://european-alternatives.eu/&quot;&gt;European
Alternatives&lt;/a&gt;, a page showing all the ways
one can move their IT life from those Silicon Valley guys to better lands.&lt;/p&gt;
&lt;p&gt;This is the first blog post of this kind here, but I am sure there will be
another ones.&lt;/p&gt;
&lt;p&gt;You can leave me a comment here or reach out to me via email or the Fediverse,
links in the footer.&lt;/p&gt;
&lt;p&gt;Thanks for reading!&lt;/p&gt;
&lt;p&gt;*the word lackey always reminds me of Simcity2000, because it was often used
there in those newspapers with news about your city&lt;/p&gt;
</description>
      <pubDate>Thu, 06 Mar 2025 24:00:00 GMT</pubDate>
      <dc:creator>STFN</dc:creator>
      <guid>https://stfn.pl/blog/65-oh-no-its-politics/</guid>
    </item>
    <item>
      <title>Short update on my life and homelab</title>
      <link>https://stfn.pl/blog/66-short-update/</link>
      <description>&lt;p&gt;Do you know those days that you cannot start doing anything, because you are
going out in the evening?&lt;/p&gt;
&lt;p&gt;That has been the situation for me for the last month and something. The
construction of my future house is just about to start, and I could not make myself to
start new things, or continue existing projects. But hopefully this hiatus will
come to and end, the last bureaucratic (how do you spell that?!) things are
being taken care of, the workers are appointed, the land is ready, and my stress
levels should gradually decrease.&lt;/p&gt;
&lt;p&gt;When it comes to my homelab, I have been doing some tiny changes, moving more
stuff to my self-hosted Nextcloud instance, sorting out better ZFS backups.&lt;/p&gt;
&lt;p&gt;I&#39;m thinking of replacing my Kubernetes cluster with LXD. The issue that I have
with Kubernetes that even in their simplified form that I am running, K3S, there
are still too complicated for me and require too much attention and head space
to use them efficiently. My cluster is running, but recently I restarted it and
all the Ingress stuff stopped working, I can only reach the services via the
Node IP. Why is that? I don&#39;t know, I haven&#39;t done good enough notes to debug it
easily. And I very much despise the situation when my Homelab services are
running in a way that I am afraid to do any changes lest they break. Especially
when it comes to services that are becoming not Homelab but HomeProd.&lt;/p&gt;
&lt;p&gt;On the other side, lately I have been enjoying using LXD, and I plan to move
more of my services to it. So expect a few future blog post on the topic.&lt;/p&gt;
&lt;p&gt;That&#39;s basically it for now, just wanted to let you know that this blog is not
dead and I have plans for new posts.&lt;/p&gt;
&lt;p&gt;Thanks for reading!&lt;/p&gt;
</description>
      <pubDate>Sat, 29 Mar 2025 24:00:00 GMT</pubDate>
      <dc:creator>STFN</dc:creator>
      <guid>https://stfn.pl/blog/66-short-update/</guid>
    </item>
    <item>
      <title>Deploying Nextcloud locally with LXD</title>
      <link>https://stfn.pl/blog/67-deploying-nextcloud-locally-with-lxd/</link>
      <description>&lt;p&gt;As I mentioned in the previous blog post, I am now in the process of moving some
of my homelab services from Docker to LXC (Linux Containers). Both of them are
virtualization technologies and are based on similar concepts, but recently I
found LXC more tempting.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Disclaimer 1: I would not call myself an expert in either Docker or LXD, what
I write below is based on the limited knowledge that I have on those topics, and
there is a high possibility I might be wrong in some places. Let me know if
you spot any errors.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Disclaimer 2: Everything described here I have first created and configured
for my own needs, and only later decided to write up in a blog post. I am using
NextcloudPi in my homelab as an actual &amp;quot;production&amp;quot; of storing documents and
other data. So this is not a tutorial for the sake of writing a tutorial, it&#39;s a
description of my real life usage.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;I&#39;ve been using Docker for years now, both at work and in my homelab, and I find
it an excellent tool. Then again, I believe its usecase is mostly for small,
ephemeral containers that run a single process, like a webserver or a video
converter. LXC containers (I know, I know, &lt;em&gt;IN number&lt;/em&gt;) on the other hand
are closer to virtual machines, holding state and having multiple processes
running in them. LXC also provides tooling for doing snapshots and backups of
running containers. And when everything like the database and the server is
running in the same container, it&#39;s easier to backup and move it, in contrast to
running a whole group of containers with &lt;code&gt;docker compose&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Because of that I feel that LXC containers are well suited to run some of the
long-lasting, stateful homelab services.&lt;/p&gt;
&lt;p&gt;Another thing that led me to LXC is me wanting to have my own Nextcloud
instance, and this is the main topic of this post.&lt;/p&gt;
&lt;h2&gt;LXC vs LXD&lt;/h2&gt;
&lt;p&gt;The topic of &amp;quot;LXD vs LXC&amp;quot; is confusing to say the least. The way I understand it
is that LXD is something like a frontend, allowing for easier management of LXC.
LXC is the underlying framework that runs the containers, while LXD allow for
administration and orchestration of the LXC containers. Something like Docker
Desktop for managing Docker containers which are being run under the hood by the
Docker Engine. Also LXD being created by Canonical is Ubuntu-centred while LXC is
distro-agnostic. Finally, it is possible to just use LXC without the added benefits of
LXD.&lt;/p&gt;
&lt;h2&gt;Nextcloud&lt;/h2&gt;
&lt;p&gt;Nextcloud advertises itself as a &amp;quot;fully open-source, on premise content
collaboration platform&amp;quot;. What it actually means for us, homelabbers, is that it
provides a FOSS alternative to commercial cloud services like the Google suite
of apps like Calendar and Drive. With Nextcloud we can have those services
running privately in our homelabs, without subscription feeds, or snooping
through our files. Running your own instance fits very well into the topic of
degoogling, as it allows to move away from large corporations and take your data
into your own hands. There&#39;s much more it of course, but this blog post is more
on the technical site of setting up your own instance.&lt;/p&gt;
&lt;h2&gt;NextcloudPi&lt;/h2&gt;
&lt;p&gt;When looking for a way to deploy Nextcloud to my private homelab, I came across
a subproject of NC, called NextcloudPi. As the name suggest, one of the main
goals of NCP is to provide a way to run Nextcloud on a Raspberry Pi. The creators
provide an image that can be flashed to an SD card to run on a Pi. I don&#39;t want
to run NC on a Pi, when I have an x86 server sitting under my desk, so I chose
another solution that they provide: an LXD container image.&lt;/p&gt;
&lt;h2&gt;Setting up LXD&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;I think it&#39;s a good idea, if you are using LXD for the first time, to install
and try it out first on your local PC/laptop, and later, once you went through
the whole process, install it in a headless homelab. It will make some things
easier to test.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;There are multiple ways to install LXD, I went with the simplest/laziest option
that is installing it from snap. My laptop is running Kubuntu, and my homelab&#39;s
OS is Debian with added snaps (I know, blasphemy), so snaps are fine with me.
Here are the &lt;a href=&quot;https://canonical.com/lxd/install&quot;&gt;installation docs&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;When installing from snap, it&#39;s as simple as (needs to be run as root)&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;snap &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; lxd
lxd init&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you are running LXD on machines that have a single drive, or you want to have
a basic setup for testing, choose the storage backend to &lt;code&gt;dir&lt;/code&gt;. If you have the
possibility, choose ZFS and provide a separate dataset for storage.&lt;/p&gt;
&lt;p&gt;Once LXD is running, it&#39;s time set up the LXD Web UI. There are &lt;a href=&quot;https://canonical.com/lxd/manage&quot;&gt;multiple way to
administrate LXD&lt;/a&gt;, but the simplest way is to
use the browser. If you are running it on your computer, just open
&lt;a href=&quot;http://localhost:8443&quot;&gt;localhost&lt;/a&gt; at port 8443. If you installed it on another
server, open it&#39;s IP address with the port 8443. A first-use wizard will guide
you through the initial setup, and will show how to generate a certificate. It
is a bit more complicated than setting up a username and password, but should
not cause any problems.&lt;/p&gt;
&lt;p&gt;Before moving to the next part, have some fun with LXD, try to run a container,
open a terminal inside it, try to update the packages, stuff like that. Just to
get a feel of what is happening.&lt;/p&gt;
&lt;p&gt;BTW, the terminology that LXD is using for a running container is &amp;quot;instance&amp;quot;,
and this is the word I will be using from now on.&lt;/p&gt;
&lt;p&gt;And if you created an instance, and it cannot access the internet, check this
&lt;a href=&quot;https://discuss.linuxcontainers.org/t/containers-do-not-have-outgoing-internet-access/10844/3&quot;&gt;forum thread&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Running NCP&lt;/h2&gt;
&lt;p&gt;Now it&#39;s time to run NextcloudPi. Download the NextcloudPi image from the
&lt;a href=&quot;https://github.com/nextcloud/nextcloudpi/releases&quot;&gt;GitHub downloads page&lt;/a&gt;,
select the file with LXD in the filename and the architecture of your computer
(most likely x86). Download it, but do not unpack.&lt;/p&gt;
&lt;p&gt;In the LXD Web UI go to Images -&amp;gt; Upload image, select the downloaded file, add
a nice alias for the name if you want. Then Instance -&amp;gt; Create Instance -&amp;gt; Browse
Images and at the top of the list there should be the newly uploaded NextcloudPi
one. For now there is no need to modify other settings, just select Create and
start.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/67/import.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/67/import.jpg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;Form to upload a container image into LXD&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/67/images_list.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/67/images_list.jpg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;A list of all available containers images LXD, with the NextcloudPi&#39;s one on top&lt;/span&gt;&lt;/p&gt;
&lt;h2&gt;Accessing NextcloudPi&lt;/h2&gt;
&lt;p&gt;NextcloudPi is now running on your computer as an LXD instance, but how to
access it? It depends on where you are running your LXD service:&lt;/p&gt;
&lt;h3&gt;Running on the same PC&lt;/h3&gt;
&lt;p&gt;Accessing the NextcloudPi instance in LXD running on the same computer that you
are using is the simplest way. By the power of DNS magic, NCP should be
reachable at &lt;code&gt;https://nextcloudpi.local&lt;/code&gt;. If that is not the case, the second
option is to open in the browser the instance IP address, which can be found in
the instance overview tab in the LXD web UI.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/67/ip_address.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/67/ip_address.jpg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;Instance summary page, with the highlighted IPv4 address of the instance&lt;/span&gt;&lt;/p&gt;
&lt;h3&gt;Running in the LAN&lt;/h3&gt;
&lt;p&gt;This is where things get a bit more complicated. For an overview of methods how
to reach an LXD container running on a machine somewhere in your LAN, I
recommend this great video done by one the LXD developers: &lt;a href=&quot;https://www.youtube.com/watch?v=TmGvbXfwJEA&quot;&gt;Accessing services
running in LXD instances&lt;/a&gt;. From all
the methods presented there, I have been using two:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Configuring specific routing in the router&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The first method I used is setting up routing in the router. I have a MikroTik
router that allows for almost infinite configuration, so that was not a problem,
but I&#39;m not sure how feasible it is in typical home routers. Also configuring it
differs between router makers, so I won&#39;t be giving exact instruction, just an
example:&lt;/p&gt;
&lt;p&gt;For my MikroTik router it was a matter of opening a terminal window in the
router web UI, and setting the routing:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;admin@MikroTik&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; /ip route &lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt; dst-address&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;10.54&lt;/span&gt;.60.0/24 &lt;span class=&quot;token assign-left variable&quot;&gt;gateway&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;192.168&lt;/span&gt;.10.240 &lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;dst-address&lt;/code&gt; is the subnet of the LXD network, to find it take the IP
address of you instance, and replace the last number group with &lt;code&gt;0.24&lt;/code&gt;. For
example, if you instance IP is &lt;code&gt;10.54.60.123&lt;/code&gt;, the subnet is &lt;code&gt;10.54.60.0/24&lt;/code&gt;.
The &lt;code&gt;gateway&lt;/code&gt; is the IP of the host machine that is running LXD. Thanks to that
command, your router will know that if you want to reach the IP address of your
NCP instance, for example &lt;code&gt;10.54.60.123&lt;/code&gt;, the request has to go first to
&lt;code&gt;192.168.10.240&lt;/code&gt;, and that machine will route it further to &lt;code&gt;10.54.60.123&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The upside of this approach is that it only needs to be done once, and will
handle any containers run in LXD. The downside is that not all routers support
this.&lt;/p&gt;
&lt;p&gt;Also with this approach, &lt;code&gt;http://nextcloudpi.local/&lt;/code&gt; will not work out of the
box. To make it work, you&#39;ll need to manually set up a DNS entry, either in the
DNS server, if you have your own, or in the &lt;code&gt;/etc/hosts&lt;/code&gt; file.&lt;/p&gt;
&lt;p&gt;For more information about setting up local domains with DNS, check out my &lt;a href=&quot;https://stfn.pl/blog/42-caddy-pihole-homelab-subdomains/&quot;&gt;How
to set up subdomains in the homelab with PiHole and
Caddy&lt;/a&gt; blog post.&lt;/p&gt;
&lt;p&gt;My personal problem with this approach is that while it works, it is extremely slow, I
need to wait at least 5-10 seconds for the webpage to load. I am suspecting this
is caused by my non-standard network solution, I have a WiFi bridge made from two
Mikrotik Audience Access Points, and have the routing configured on one of them,
the one closer to the ISP router. This is something I will need to investigate
in the future. For you it will most probably work fine.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Proxy devices&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;I won&#39;t lie, this is the part of the LXD config that I understand the least, but
I found how to use it to make it work the way I want it to. If you know how to
do it better, more properly, let me know.&lt;/p&gt;
&lt;p&gt;Proxy devices allow to configure a connection between the host and the instance,
so that accessing the host on a specific port takes the request to a specific
port on the instance to which the proxy is attached to. I have two proxy devices
configured, first one forwards the connection to the host port 9290 to the instance port
443 (default for HTTPS) and the second device forwards from host 4443 to the
instance 4443 for the management site.&lt;/p&gt;
&lt;p&gt;This way, when I go to HOST_IP:9290 I am taken to the Nextcloud main site, and
when I go to HOST_IP:4443, I go the admin portal.&lt;/p&gt;
&lt;p&gt;I am using port 9290 on the host because I also have other services running, and
so ports 80 and 443 are already occupied.&lt;/p&gt;
&lt;p&gt;I also found out that the admin portal will not work if you change its port in
the proxy device, it expects to be accessed on port 4443, otherwise it will not
allow to change any settings.&lt;/p&gt;
&lt;p&gt;The Bind and NAT mode options I leave undefined, from my limited experience I
did not see difference when I changed them to different options. The Listen IP
address is 0.0.0.0 (any address) and the connect one is 127.0.0.1 (localhost of
the instance). For me it kinda works. How correct it is? I am not sure. I would love
to hear your feedback.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/67/proxies2.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/67/proxies2.jpg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;Proxy devices config page&lt;/span&gt;&lt;/p&gt;
&lt;h3&gt;Accessing from anywhere&lt;/h3&gt;
&lt;p&gt;You can now access the instance locally. But what if we want to access our
Nextcloud from any place in the world, for example to upload invoices or photos
from holidays? For that I am using Tailscale, a zero config VPN. I&#39;m a big fan
of them, I have them on all my devices, even my mobile phone. No, they do not
pay me, I just like their services.&lt;/p&gt;
&lt;p&gt;To install Tailscale I just use &lt;a href=&quot;https://tailscale.com/download&quot;&gt;their install
script&lt;/a&gt;, which I launch in the instance
terminal. The terminal can be accessed on the instance page in the LXD Web UI.
Once Tailscale is installed and authenticated, you will get an IP address and a
domain to access it from anywhere.&lt;/p&gt;
&lt;p&gt;Note that in order for the Tailscale domain to work, you will need to add it to
the &amp;quot;trusted domains&amp;quot; setting in the NCP admin portal (the one on the :4443
port) or by editing the &lt;code&gt;/var/www/nextcloud/config/config.php&lt;/code&gt; in the instance
terminal window.&lt;/p&gt;
&lt;h2&gt;NextcloudPi initial config&lt;/h2&gt;
&lt;p&gt;Once the container is running, and you can access it, the time has come to do
the initial setup. NextcloudPi built-in HTTP server works on the standard ports
80/443, so unless you changed the port in the proxy device setup, pasting only
the domain or the IP address in the browser will work.&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color:red&quot;&gt;You will get an error that the connection is not secure, that is not a threat,
it&#39;s just the way NCP runs by default.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;NCP has HTTPS implemented out of the box, but it is using a self-signed TLS
certificate, and the browsers do not like it. Just confirm that you accept the
risk and want to continue. If you know how to solve it for locally running services using HTTPS, let me know.&lt;/p&gt;
&lt;p&gt;Once the warning page is gone, you will be greeted with the NCP welcome page
with two sets of usernames and passwords. Note them before clicking &amp;quot;Activate&amp;quot;!
Then again, if you continued before saving them, that is not a problem, just
stop and remove the instance and create a new one. The beauty of virtualization
is that killing and recreating virtual machines is super simple and fast.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/67/ncp_activation.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/67/ncp_activation.jpg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;Nextcloud initial config page&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;The first login and password is for the server administration page at port
:4443. This page is for all of the low-level configuration and administration
tasks to which regular users should not have access, for example the database
settings or software updates.&lt;/p&gt;
&lt;p&gt;The second set of credentials is for the first user of your NCP instance. That
user will also have the administrator permissions, allowing them to do some
additional configuration for all users of the instance, such as the quotas,
languages and themes&lt;/p&gt;
&lt;p&gt;Once you have written down both sets of credentials, click Activate, and you
will be taken to the main page of your Nextcloud account. You are an
administrator, so you can start with creating more users in the Administrator
settings.&lt;/p&gt;
&lt;p&gt;And that&#39;s basically it, you now have a running Nextcloud instance to which you
can upload files, create notes, make appointments in the calendar etc.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/67/new_ncp.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/67/new_ncp.jpg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;Fresh instance of Nextcloud&lt;/span&gt;&lt;/p&gt;
&lt;h2&gt;Backups&lt;/h2&gt;
&lt;p&gt;Now that you have all your precious and important data in NCP, it would good to
back it up. The available backup solutions for NCP are snapshots and exports,
and both serve slightly different purposes.&lt;/p&gt;
&lt;h3&gt;Snapshots&lt;/h3&gt;
&lt;p&gt;Snapshots allow to freeze the instance state in a certain moment in time, like
taking a photo. If you, for example, remove from your NCP drive a file that you
did not want to remove, you can restore the whole instance from a snapshot that
was taken before you removed that file. You will lose all the changes that you
did in the time between now and the creation time of that snapshot, but your
precious file will be restored.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/67/snapshots.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/67/snapshots.jpg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;List of snapshots created for the NextcloudPi instance&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;LXD allows to configure the automatic creation of snapshots on schedule in the
Snapshot tab of the instance settings. There you can also list your snapshots
and select the one to restore from. A good idea is to set the expiration date of
the snapshots so that they do not take too much storage space.&lt;/p&gt;
&lt;h3&gt;Exports&lt;/h3&gt;
&lt;p&gt;Snapshots are great and all that, but they have one significant problem: they
are stored on the same machine as the LXD service. If that machine breaks, you
lose the running instance and all of its snapshots.&lt;/p&gt;
&lt;p&gt;The solution for that is to export the instance to a file, which can then be
moved to different machine(s). For that, we need to go to the terminal on the
host where the LXD service is running. To create an archive, this is the
command:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;lxc &lt;span class=&quot;token builtin class-name&quot;&gt;export&lt;/span&gt; nextcloudpi --instance-only &lt;span class=&quot;token string&quot;&gt;&quot;ncp-&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;date&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;+%Y-%m-%d&#39;&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt;.tar.gz&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Running this will create a tar.gz archive with today&#39;s date in the filename. The
&lt;code&gt;--instance-only&lt;/code&gt; flag defines that only instance should be archived without the
snapshots. If you want to also export the snapshots, remove the flag but bear in
mind that the file will be massive. To automate this, create a bash script that
will generate the archive and rsync it to some other place. Add it to cron to
run it periodically.&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token shebang important&quot;&gt;#!/bin/bash&lt;/span&gt;

&lt;span class=&quot;token assign-left variable&quot;&gt;DATE&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;date&lt;/span&gt; +%Y-%m-%d&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;FILENAME&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;ncp-&lt;span class=&quot;token variable&quot;&gt;$DATE&lt;/span&gt;.tar.gz&quot;&lt;/span&gt;

lxc &lt;span class=&quot;token builtin class-name&quot;&gt;export&lt;/span&gt; nextcloudpi --instance-only ncp-&lt;span class=&quot;token variable&quot;&gt;$DATE&lt;/span&gt;.tar.gz
&lt;span class=&quot;token function&quot;&gt;rsync&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-azP&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$FILENAME&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;192.168&lt;/span&gt;.1.2:/home/stefan&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Change the IP address and local path to what you have.&lt;/p&gt;
&lt;p&gt;To import the resulting archive back to LXD:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;lxc &lt;span class=&quot;token function&quot;&gt;import&lt;/span&gt; ncp-2025-04-12.tar.gz&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This command will import the exported instance. It will be added to the list of
instances in the LXD Web UI, in the stopped state, and will need to be started to use it.&lt;/p&gt;
&lt;h2&gt;Bottom Line&lt;/h2&gt;
&lt;p&gt;So now you have a local NextcloudPi instance which you can use to organize your
life and files. Of course this post only scratched the surface of what LXD and
Nextcloud can do, but I&#39;m hoping it give a good basis to develop upon. As I use
Nextcloud more, and move more of my services to LXD, I will be writing more
articles on the topic. In the meantime, thanks for reading!&lt;/p&gt;
</description>
      <pubDate>Sat, 12 Apr 2025 24:00:00 GMT</pubDate>
      <dc:creator>STFN</dc:creator>
      <guid>https://stfn.pl/blog/67-deploying-nextcloud-locally-with-lxd/</guid>
    </item>
    <item>
      <title>Access local Nextcloud with HTTPS anywhere by using Tailscale TLS certificates</title>
      <link>https://stfn.pl/blog/68-tailscale-tls-with-nextcloud/</link>
      <description>&lt;p&gt;&lt;em&gt;This blog post is of course not sponsored by Tailscale, I just really like them&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;This blog post continues the topic of running Nextcloud in an LXD container, but
the matters discussed here are universal and can also be applied to any service
running locally in your LAN.&lt;/p&gt;
&lt;p&gt;In &lt;a href=&quot;https://stfn.pl/blog/67-deploying-nextcloud-locally-with-lxd/&quot;&gt;the previous blog
post&lt;/a&gt; I mentioned
that when using Nextcloud, I am getting a warning that the connection is not
secure. That is because Nextcloud by default is using a self-signed TLS
certificate, and modern web browsers find it suspicious. This was not a big
problem for me until I wanted to use Gpoddersync to synchronize my podcasts
between my phone&#39;s AntennaPod and my Nextcloud&#39;s RePod application (Something
that I will write about in more detail in another blog post). Creating the
connection would not work, as the sync tool expects a properly working HTTPS on
the Nextcloud&#39;s side.&lt;/p&gt;
&lt;p&gt;Turns out there is a rather simple solution to fix this issue: use Tailscale to
generate a proper TLS certificate to provide a secure HTTPS connection when
accessing Nextcloud via VPN.&lt;/p&gt;
&lt;h2&gt;Setting the stage&lt;/h2&gt;
&lt;p&gt;The initial situation is that I am running a NextcloudPi instance in an LXC
container in my homelab server. I have Tailscale installed inside that
container. I can access NCP via Tailscale using its MagicDNS url, but the
browser is complaining that the TLS certificate is suspicious.&lt;/p&gt;
&lt;h2&gt;TLS certificates in Tailscale&lt;/h2&gt;
&lt;p&gt;Tailscale has &lt;a href=&quot;https://tailscale.com/kb/1153/enabling-https#configure-https&quot;&gt;excellent docs on how to enable HTTPS and generate
certificates&lt;/a&gt;, so
just to recap it shortly: In the Tailscale admin settings, make sure MagicDNS is
enabled, and at the bottom select &amp;quot;Enable HTTPS&amp;quot;. Tailscale warns you that the
fully qualified domain (basically the hostname + MagicDNS tailnet name) will
become publicly known to enable validating the certificates, but the access to
the machines will still be private.&lt;/p&gt;
&lt;h2&gt;Configuring certificates in NextcloudPi&lt;/h2&gt;
&lt;p&gt;In the NextcloudPi console (I use the LXD Web UI to access the container&#39;s console) run&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;tailscale cert &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;your MagicDNS url &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; that machine&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This command will generate two files, a &lt;code&gt;*.crt&lt;/code&gt; and a &lt;code&gt;*.key&lt;/code&gt; one. You can leave
them when they were generated, or move them to a location of your choice.&lt;/p&gt;
&lt;p&gt;The next step is to edit the Apache config file to point it to the new certificates. Use your editor of choice, I use &lt;code&gt;vi&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;vi&lt;/span&gt; /etc/apache2/sites-enabled/001-nextcloud.conf&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;At the top of the file there is a warning that the file should not be edited as
any changes will be overwritten, but to be honest, I do not know when that
happens, I restarted Apache and the whole instance a few times and the changes I
made persisted. Would an Apache expert chime in?*&lt;/p&gt;
&lt;p&gt;Anyway, the beginning of the file should like like this:&lt;/p&gt;
&lt;pre class=&quot;language-xml&quot;&gt;&lt;code class=&quot;language-xml&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;IfModule&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;mod_ssl.c&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&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;VirtualHost&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;_default_:&lt;/span&gt;443&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    DocumentRoot /var/www/nextcloud
    CustomLog /var/log/apache2/nc-access.log combined
    ErrorLog  /var/log/apache2/nc-error.log
    SSLEngine on
    SSLProxyEngine on
    SSLCertificateFile   /etc/ssl/certs/ssl-cert-snakeoil.pem
    SSLCertificateKeyFile /etc/ssl/private/ssl-cert-snakeoil.key

    # For notify_push app in NC21
    ProxyPass /push/ws ws://127.0.0.1:7867/ws
    ProxyPass /push/ http://127.0.0.1:7867/
    ProxyPassReverse /push/ http://127.0.0.1:7867/
  &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;VirtualHost&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;p&gt;What interests us are the lines beginning with &lt;code&gt;SSLCertificateFile&lt;/code&gt; and
&lt;code&gt;SSLCertificateKeyFile&lt;/code&gt;. Replace their values so that those lines point to the
&lt;code&gt;crt&lt;/code&gt; and &lt;code&gt;key&lt;/code&gt; file respectively. For me those would be:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;    SSLCertificateFile   /root/nextcloudpi.&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;redacted&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;.ts.net.crt
    SSLCertificateKeyFile /root/nextcloudpi.&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;redacted&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;.ts.net.key&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now what is left is to reload Apache&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;apachectl &lt;span class=&quot;token parameter variable&quot;&gt;-k&lt;/span&gt; graceful&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And le voila! You should now be able to access your Nextcloud via Tailscale
without any TLS error messages, with the browser bar showing a nice padlock.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/68/https.png&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/68/https.png&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;Nextcloud with a secure connection&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;That&#39;s it, short and simple. I&#39;m becoming more and more impressed with Nextcloud
and the possibilities it provides. Also a friend told me that Hetzner is now
providing a &lt;a href=&quot;https://www.hetzner.com/storage/storage-share&quot;&gt;managed Nextcloud
service&lt;/a&gt;. I haven&#39;t tried it
personally, but he says it&#39;s working great.&lt;/p&gt;
&lt;p&gt;What is more, I have used the example of Nextcloud, but the mechanism I
presented is universal and can be used to generated TLS certificates for any
internal service that you are providing, like Immich or Jellyfin. The only
requirement is a HTTP server that can handle TLS. I will be testing it with
other services soon.&lt;/p&gt;
&lt;p&gt;Thanks for reading!&lt;/p&gt;
&lt;p&gt;* I now have Panic At The Disco playing in my head.&lt;/p&gt;
</description>
      <pubDate>Sat, 26 Apr 2025 24:00:00 GMT</pubDate>
      <dc:creator>STFN</dc:creator>
      <guid>https://stfn.pl/blog/68-tailscale-tls-with-nextcloud/</guid>
    </item>
    <item>
      <title>Migrating servers, leaving Fosstodon, moving to my own GoToSocial</title>
      <link>https://stfn.pl/blog/69-migrating-servers-migrating-instances/</link>
      <description>&lt;p&gt;During the last few days I did two migrations, the first one I had been
planning for some time, the second one was more spontaneous, and they were
directly related to one another.&lt;/p&gt;
&lt;p&gt;First I migrated the VPS hosting my GoToSocial instance from RackNerd to
Hetzner. After that I migrated one of my main Fediverse accounts from Fosstodon to my
GoToSocial instance, &lt;a href=&quot;https://fedi.stfn.pl/&quot;&gt;fedi.stfn.pl&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Moving from RackNerd to Hetzner&lt;/h3&gt;
&lt;p&gt;The VPS migration had been on my mind for quite some time. It fits into my
ongoing plans to reduce my dependence on large corporations (see my &lt;a href=&quot;https://stfn.pl/blog/16-on-degoogling/&quot;&gt;On
Degoogling&lt;/a&gt; post), and especially the
ones that are based in the United States. With the latest changes that the US is
undergoing, I just do not want to have my data and services running there.
Europe is a place that takes matter such as data privacy and sovereignty much
more seriously, and what is also very important to me is that Europe is a place
which I feel a part of. And I strongly believe that the European Union and the
agends that it is pushing are the best things that have happened to Europe in
all of its history.&lt;/p&gt;
&lt;p&gt;And so I chose the German company Hetzner as my new VPS service provider. They have datacenters in
Eastern Germany, which is physically very close to me, and I&#39;ve been already
using their other service for months. Their Storage Box is very good in my
opinion, and while it has a non-zero entry level, the pricing is hard to beat
in the area of cloud storage. Also, RackNerd&#39;s VPS services are very barebones,
while Hetzner provides additional tools for managing servers, like regular
backups and snapshots.&lt;/p&gt;
&lt;p&gt;Here I need to stress that I have never had any issues with RackNerd, no
downtime at all, and their pricing is very good, but well, I want to have my
Internet stuff close to me, in Europe. Yes, I know that RackNerd has datacenters
in the Netherlands, but all in all, they are a company from California.&lt;/p&gt;
&lt;h3&gt;Moving from Fosstodon to fedi.stfn.pl&lt;/h3&gt;
&lt;p&gt;I do not want to elaborate on the reasons I left Fosstodon, already a lot of
people said a lot of words of varying intensity about the whole affair. Let me
just say that apart from the, let&#39;s say, &lt;code&gt;social&lt;/code&gt; aspects, the want to move to
my own place and handle the technical parts myself was an important factor.&lt;/p&gt;
&lt;h2&gt;The Migrations&lt;/h2&gt;
&lt;p&gt;Now let&#39;s talk about the technical stuff. I wanted to have my VPS situation
sorted out before moving my large Fosstodon account to my VPS, so first I went
with the server migration.&lt;/p&gt;
&lt;h2&gt;Moving GoToSocial instances between servers&lt;/h2&gt;
&lt;p&gt;The first migration I went with was migrating my GoToSocial instance from my
RackNerd VPS to my new Hetzner one. This does not mean I already left RackNerd,
as I have more services running on their servers. Eventually I will move all of
them.&lt;/p&gt;
&lt;p&gt;As in many previous of my blog posts, I am writing about a thing I did, but I
try to talk about it in the form of a tutorial, so that anyone can do the same.
That&#39;s why the way the next paragraphs are written is funky at times, jumping
between my &amp;quot;memoirs&amp;quot; are instructions what to do. Anyway...&lt;/p&gt;
&lt;p&gt;The whole process is rather straightforward. The crucial thing is to have good
backups and do each step slowly and making sure it succeeded.&lt;/p&gt;
&lt;p&gt;The following steps are assuming you have an already running GTS instance on
server A (called source from now on) and you want to move it on a bare, just
bought Linux server B (called target). I am running GTS as a systemd service
with SQLite and nginx as a separate reverse proxy. HTTPS is being handled by
Certbot. As you can see, there is a lot of variables here, and you setup may be
very different, but the general ideas should be similar.&lt;/p&gt;
&lt;p&gt;The very first thing to do when migrating services between servers, is to go to
the domain provider you are using, open the settings of the domain of the
service being migrate, and change its TTL to a very small number, like 5
seconds. This should be done preferably a few hours in advance of the migration,
the most correct way is to wait the time the domain&#39;s TTL was set to in the
first place. For example, if you domain TTL was set to 24 hours, it would be
best to change the TTL to 5 seconds and wait those 24 hours before the
migration. This clears the caches of browsers and any downstream DNS servers,
and so when clients will try to reach you service after the migration, the DNS
cache will not take them to the IP address of the old server.&lt;/p&gt;
&lt;p&gt;Once the waiting is done, it&#39;s time to do the actual migration.&lt;/p&gt;
&lt;p&gt;The first step to do is to go through the typical initial server config and
hardening, &lt;a href=&quot;https://www.digitalocean.com/community/tutorials/initial-server-setup-with-ubuntu&quot;&gt;Initial Server setup with Ubuntu by
DigitalOcean&lt;/a&gt;
is a great article and I refer to it every time I set up a new server&lt;/p&gt;
&lt;p&gt;After that, an optional but useful step is to set up the monitoring stack for
the new server: Prometheus, Node Exporter and
&lt;a href=&quot;https://github.com/nginx/nginx-prometheus-exporter&quot;&gt;nginx-prometheus-exporter&lt;/a&gt;.
I also make sure that nginx is installed. If you are using a database other than
SQLite, now it would also be the time to install it.&lt;/p&gt;
&lt;p&gt;A few useful resources on monitoring:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://janakiev.com/blog/prometheus-setup-systemd/&quot;&gt;install Prometheus as systemd service&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://prometheus.io/docs/guides/basic-auth/&quot;&gt;set up basic auth for
Prometheus&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Once this is done, the actual GoToSocial migration can happen.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Nginx migration&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;stop nginx on target&lt;/li&gt;
&lt;li&gt;copy &lt;code&gt;/etc/ngix/sites-available&lt;/code&gt; from source to target and in the target
server symlink the GTS server config to &lt;code&gt;sites-enabled&lt;/code&gt; in a typical nginx
fashion.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;TLS certificates migration&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;copy &lt;code&gt;/etc/letsencrypt&lt;/code&gt; from source to target. If there is no such folder,
check in the nginx site config where the TLS certs are stored in you case.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;GoToSocial migration&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;This is the actual migration. Begin it with stopping the GTS service on the source server.&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; systemctl stop gotosocial.service&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;At this moment the downtime of your instance begins. If you have more than one
user, I hope you warned them in advance :)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;copy &lt;code&gt;/etc/system/system/gotosocial.service&lt;/code&gt; from source to target&lt;/li&gt;
&lt;li&gt;pack &lt;code&gt;/gotosocial&lt;/code&gt; on source to &lt;code&gt;gotosocial.tar.gaz&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;tar&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-czvf&lt;/span&gt; gotosocial.tar.gz /gotosocial&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I followed the steps from the GTS docs when
doing the initial installation, so in my case, GTS data lives in the
&lt;code&gt;/gotosocial&lt;/code&gt; top level directory, and is owned by the user &lt;code&gt;gotosocial&lt;/code&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;create &lt;code&gt;gotosocial&lt;/code&gt; user and group on the target server&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useradd&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-r&lt;/span&gt; gotosocial 
&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;groupadd&lt;/span&gt; gotosocial 
&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;usermod&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-a&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-G&lt;/span&gt; gotosocial gotosocial&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;copy the &lt;code&gt;gotosocial.tar.gz&lt;/code&gt; tar archive to target and unpack&lt;/li&gt;
&lt;li&gt;&lt;code&gt;chown&lt;/code&gt; the whole &lt;code&gt;/gotosocial&lt;/code&gt; folder to the user &lt;code&gt;gotosocial&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;chown&lt;/span&gt; gotosocial:gotosocial &lt;span class=&quot;token parameter variable&quot;&gt;-R&lt;/span&gt; /gotosocial&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you are using a db other than SQLite, now would be the time to migrate it.
I&#39;ve never done it, so I would not be of much help here.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;on target enable and start gotosocial.service&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; systemctl &lt;span class=&quot;token builtin class-name&quot;&gt;enable&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;--now&lt;/span&gt; gotosocial.service&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;At this moment, GoToSocial should start cleanly on the target server. But it won&#39;t be available yet to the outside world, as &lt;code&gt;nginx&lt;/code&gt; is not running yet. The simplest way to confirm that GTS is indeed working is to &lt;code&gt;curl&lt;/code&gt; it from the same server it is running on&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;curl&lt;/span&gt; http://localhost:8000&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The response should be welcome web page. If it is not working, time to start
debugging. A good place to start are the system logs:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; journalctl &lt;span class=&quot;token parameter variable&quot;&gt;-xeu&lt;/span&gt; gotosocial.service&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This should show the cause of the problem. One common problem is file
permissions, make sure &lt;code&gt;/gotosocial&lt;/code&gt; belongs to the right user, the one that is
mentioned in the &lt;code&gt;gotosocial.service&lt;/code&gt; systemd file.&lt;/p&gt;
&lt;p&gt;Once we are sure that GTS is running on the server, the last steps are to switch
the DNS so that the domain points to the target server, and start the nginx
service on the target. It&#39;s also a good practice to test the nginx config before
starting it:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; nginx &lt;span class=&quot;token parameter variable&quot;&gt;-t&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; systemctl &lt;span class=&quot;token builtin class-name&quot;&gt;enable&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;--now&lt;/span&gt; nginx.service&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;nginx should start without issues, but as always &lt;code&gt;should&lt;/code&gt; is the key here. If it
does not want to start, consult the logs as described above.&lt;/p&gt;
&lt;p&gt;And done! Your GoToSocial instance should be back online, working from the new server.&lt;/p&gt;
&lt;p&gt;If that is correct, the final final setp is to reinstall certbot, so that the
certificate renewal hooks are in place. Just do the same steps from &lt;a href=&quot;https://certbot.eff.org/&quot;&gt;Certbot&#39;s
page&lt;/a&gt; as you would install it. Certbot is smart enough
to find that the certs already exist on the target server and will reuse them,
and configure its new service around them.&lt;/p&gt;
&lt;h2&gt;Migrating my Fosstodon account to my GoToSocial instance&lt;/h2&gt;
&lt;p&gt;With my Gotosocial instance &lt;a href=&quot;https://fedi.stfn.pl/&quot;&gt;fedi.stfn.pl&lt;/a&gt; working nicely
on new hardware, it was time to migrate my Fosstodon account to it. Thanks to
the creators of the Activity Pub protocol, Mastodon, and GoToSocial, it was a
very simple task.&lt;/p&gt;
&lt;p&gt;I already had an account &lt;code&gt;stfn&lt;/code&gt; on my GTS instance, rarely used and with almost
no followers/following, so the first step in the migration was done in advance.&lt;/p&gt;
&lt;p&gt;In its settings, I added &lt;code&gt;https://fosstodon/users/stfn&lt;/code&gt; as an alias, to prove
Fosstodon that this account indeed belongs to me.&lt;/p&gt;
&lt;p&gt;Then, in Fosstodon&#39;s account settings, I first requested an archive of all my
data. It took a few hours to generate it. I also downloaded all of the CSV lists
of people I followed, muted, blocked. Turned out I only blocked three users, and
looking at their usernames, I had no recollection why I did it.&lt;/p&gt;
&lt;p&gt;Once I had all the data backed up, again in the Fosstodon account settings, I
initiated the migration of the account, providing my GTS account url as the
target.&lt;/p&gt;
&lt;p&gt;On one screen I had my VPS nginx logs opened, on the second screen it was
Firefox, with Fosstodon in one tab, &lt;a href=&quot;https://fedi.stfn.pl&quot;&gt;fedi.stfn.pl&lt;/a&gt; in the
second tab, and Grafana VPS stats in the third. I clicked the magic button and
the migration begun.&lt;/p&gt;
&lt;p&gt;During the migration, only the people who follow you are automatically migrated.
It looked like the followers were being migrated in batches of several tens, and
all in all it took maybe two hours for the migration to finish. In Fosstodon I
had arround 1140 followers, in &lt;a href=&quot;https://fedi.stfn.pl&quot;&gt;fedi.stfn.pl&lt;/a&gt; I ended up
with around 900. I don&#39;t know what is the reason for that, but I am guessing
that maybe some of those 1000+ followers I had were defunct accounts that could
not be found by the new instance.&lt;/p&gt;
&lt;p&gt;Having migrated the followers, I went to migrate the people I follow. In the
GoToSocial account settings I went to the Import tab, and uploaded the
corresponding CSV file from the ones I downloaded from Fosstodon. That also took
quite some time, but almost all follows migrated without issues.&lt;/p&gt;
&lt;p&gt;I decided not to migrate my toots, start with a clean slate, but it is possible
to do so, with a tool aptly called &lt;code&gt;slurp&lt;/code&gt;, available on
&lt;a href=&quot;https://github.com/VyrCossont/slurp&quot;&gt;GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Backups&lt;/h3&gt;
&lt;p&gt;As I mentioned at the beginning of the post, Hetzner provides better tooling
than my previous VPS provider, and because of that I stopped using Borg for
backups, instead I switched to doing snapshots of the whole server. For now I am
doing it manually from time to time, but my plan is to find a way to automate
it, maybe using Ansible or some similar tools.&lt;/p&gt;
&lt;h2&gt;Bottom Line&lt;/h2&gt;
&lt;p&gt;And that&#39;s it, I am happy I did those two migrations, I feel that it gave me
renewed joy in learning the more technical aspects of the Fediverse. And
whenever I mentioned the topic of migrating both servers and accounts on the
Fediverse I received so many words of encourangement, of good tips, of wishing
me luck. The Fediverse has the best crowd of any social medium I&#39;ve ever been
on, you people are awesome!&lt;/p&gt;
&lt;p&gt;And it looks like Fosstodon will be having a new team of admins and moderators,
led by &lt;a href=&quot;https://fosstodon.org/@Gina&quot;&gt;Gina @ fosstodon.org&lt;/a&gt;. I wish them all the
best and hope they will lead Fosstodon to a bright future as an imporant part of
the Fediverse.&lt;/p&gt;
&lt;p&gt;As of writing this post, my instance is federating with 8288 other instances. I
find it incredible that the number of instances in the Fediverse is in the
thousand or tens of thousands!&lt;/p&gt;
&lt;p&gt;Thanks for reading!&lt;/p&gt;
</description>
      <pubDate>Fri, 02 May 2025 24:00:00 GMT</pubDate>
      <dc:creator>STFN</dc:creator>
      <guid>https://stfn.pl/blog/69-migrating-servers-migrating-instances/</guid>
    </item>
    <item>
      <title>My new, (mostly) Mikrotik network setup</title>
      <link>https://stfn.pl/blog/70/</link>
      <description>&lt;p&gt;I bought a new router and now my home network is mostly Mikrotik.&lt;/p&gt;
&lt;p&gt;Before I start talking about the network setup, let me lay some context of where I am:
There are two main factors why my current network setup is configured in such a
way:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Sometime in the future, maybe end of this year, maybe early next year, I will
be moving to my own house, and there will be a full sized server rack in a
separate room.&lt;/li&gt;
&lt;li&gt;In my current flat, I have thick concrete walls and the room layout makes it
basically impossible to lay cables between them, also we are renting it so I do
not want to modify it too much.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Also I am big fan of Mikrotik, as they are a relatively small, Latvian company
and I know they make very good and fair priced network products. Their software
has a steep learning curve, but I want to push through it, I think having this
skill will be very useful both in my homelab and professional experience.&lt;/p&gt;
&lt;p&gt;Here is the diagram of my network, made in &lt;a href=&quot;https://stfn.pl/blog/70/drawn.io&quot;&gt;draw.io&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/70/network.png&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/70/network.png&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The network entrypoint is my small town ISP, providing Internet in the form of a
Ethernet twisted pair cable coming from the wall of my flat. In every other
place where I lived it used to be either coax or fiber, but here it&#39;s just RJ45
from the wall. The cable goes to the ETH1 port of my newly bought Mikrotik (deep
breath) RB3011 U1AS-RM.&lt;/p&gt;
&lt;p&gt;I chose RB3011 U1AS-RM because it has a) 10 copper ports and one SFP fiber port
for my planned use of light in my future network, b) is rack mountable for my
planned massive rack, c) it has a screen! and d) it is in general a beautiful
overkill.&lt;/p&gt;
&lt;p&gt;I bought it used for ~400PLN (95EUR) and would you believe it, on Vinted.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/70/router.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/70/router.jpg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;My new router. Note the temporary &amp;quot;rack mounting&amp;quot;. And
fighting dust is a hopeless battle in a flat with a large dog. The router LEDs
are covered with black tape.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;The ISP uses PPPoE to authenticate users, and the RB3011 handles that part. I
configured PPPoE using &lt;a href=&quot;https://www.youtube.com/watch?v=osUseT7zdoQ&quot;&gt;this Youtube
video&lt;/a&gt;. However, after doing it
exactly as in the video, only my router had Internet access, but not its
clients. This &lt;a href=&quot;https://forum.mikrotik.com/viewtopic.php?t=109555#p543607&quot;&gt;forum
thread&lt;/a&gt; helped me
solve this issue.&lt;/p&gt;
&lt;p&gt;Out of the ETH10 port of the router goes the link to my Audience Mikrotik Access
Point.&lt;/p&gt;
&lt;p&gt;I have two Mikrotik Audiences connected in a mesh, but in my case it&#39;s more of a
Wi-Fi bridge. The second Audience is placed in the office room. As I said
before, I can&#39;t really put cables between the rooms here, and the walls are
thick and disastrous to Wi-Fi signals, especially the 5Ghz ones. When using
cheap, basic Wi-Fi routers I would get a lot of dropped connections and that was
just not acceptable when working from home as I do.&lt;/p&gt;
&lt;p&gt;Configuring the Audiences to work as a mesh was rather straightforward, the
Mikrotik&#39;s site has instructions on how to do it, it boils down to pressing the
WPS on the first Audience and then holding the WPS button on the second one.
However, I had to modify one setting after pairing using WinBox, and I am not
sure if that is a flaw in the default configuration, or if I did something
wrong. In order to make the connection an actual Wi-Fi bridge, I had to add ETH2
to the default bridge interface on the second Audience unit.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/70/audience2.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/70/audience2.jpg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;Mikrotik Audience living on top of my office book rack&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;This allowed me to use the second Audience unit as a wired router for devices in
my office. The two Audience talk to each other over Wi-Fi, and they even have a
dedicated 5GHz link only for intertalk, and I have to say that it works much
better than any previous Wireless router I tried in this flat.&lt;/p&gt;
&lt;p&gt;So I said, the second Audience works as a sort of wired router, a cable from the
ETH2 port of it goes to my NETGEAR GS108LP PoE+ switch. I chose that switch most
of all for the PoE+ part, as it allows me to power my Raspberry Pis without
using USB-C power supplies. I have right now four Pis running, three in &lt;a href=&quot;https://stfn.pl/blog/56-kubernetes-boinc-raspberry-pi-cluster/&quot;&gt;my
Kubernetes
cluster&lt;/a&gt;, and
one running as an ADS-B receiver.&lt;/p&gt;
&lt;p&gt;The switch is also rack mountable, and the ears can be rotated 90 degrees, so
for now it&#39;s bolted to my bookrack.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/70/switch.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/70/switch.jpg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;The switch and the Pis.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;BTW, My Kubernetes cluster is no longer running K3s as I got tired of maintaining
it, for now it&#39;s only running BOINC, and I am considering taking it apart and
testing out the Pis as security cameras in my future house.&lt;/p&gt;
&lt;p&gt;Apart from the Pis, that switch also handles my homelab server, and my work
laptop which I rarely move from its place, so it&#39;s wired.&lt;/p&gt;
&lt;p&gt;There&#39;s also one other PC that I did not mention previously, there&#39;s a Lenovo
ThinkCenter USFF PC running my Home Assistant and working as a Zigbee gateway (I
described it in detail in the &lt;a href=&quot;https://stfn.pl/blog/58-home-assistant-lenovo-usff/&quot;&gt;Using a Lenovo USFF PC as a Home Assistant
box&lt;/a&gt; blog post). It&#39;s
connected to the first Audience AP and not to the router directly because, well,
let&#39;s go to the issues list.&lt;/p&gt;
&lt;h3&gt;Issues&lt;/h3&gt;
&lt;p&gt;Those are not exactly issues, more like &amp;quot;things I need to learn properly, for
now I am a bit scared to touch them&amp;quot;. Having internet access is a sacred thing
in this household, and I don&#39;t want to have problems because I mess up my
internet setup to a point of no return. I will sort it out properly, but not
today :)&lt;/p&gt;
&lt;p&gt;The main issue I have with my current setup is that the first Audience unit, the
one connected to the router is a DHCP server, and so it has its own separate
subnet, different from the one of the router, and so I would not be able to
reach things connected to the router from, say, my laptop. I want to have a flat
network with a single subnet handed out by the RB3011. As I said, this is
something for the future. I guess all the network engineers will now sneer at me
for procrastinating doing such a trivial change, but hey, I&#39;m just starting in the realm
of networking, and choosing Mikrotik as my platform is playing on hard.&lt;/p&gt;
&lt;p&gt;The second issue, much easier to fix, is that I am running out of Ethernet ports
on my switch :) My long-term plan is to get the
&lt;a href=&quot;https://mikrotik.com/product/crs305_1g_4s_in&quot;&gt;CRS305-1G-4S+IN&lt;/a&gt; SFP+ switch and
move some of my devices to 10G fiber.&lt;/p&gt;
&lt;h3&gt;Bottom line&lt;/h3&gt;
&lt;p&gt;And that is the current situation. Not ideal, but much, much better than it was
when I moved in here.&lt;/p&gt;
&lt;p&gt;Not only do I have better Wi-Fi, but also the girl who lives here is now my Wi-Fe.&lt;/p&gt;
&lt;p&gt;Ok, that was awful.&lt;/p&gt;
&lt;p&gt;Anyyyway, speaking of the future house network, I am hoping that there I will be
able to just use a single large AP and ditch the Wi-Fi bridge. This should make
any configuration much easier. I will also need to look into LTE solutions, it
is very probable that I will move to the house faster than fiber comes to the
village.&lt;/p&gt;
&lt;p&gt;I can&#39;t wait to write more blog posts as I am building the network in my new
home, almost as much as I can&#39;t wait to live in it!&lt;/p&gt;
&lt;p&gt;And that&#39;s basically it, hope my blog post was inspiration to some of you, and
thanks for reading.&lt;/p&gt;
</description>
      <pubDate>Fri, 09 May 2025 24:00:00 GMT</pubDate>
      <dc:creator>STFN</dc:creator>
      <guid>https://stfn.pl/blog/70/</guid>
    </item>
    <item>
      <title>I achieved 100 million points in Einstein@Home, and rediscovered Folding@Home</title>
      <link>https://stfn.pl/blog/71-100m-in-einstein/</link>
      <description>&lt;p&gt;I achieved one hundred million points in
&lt;a href=&quot;https://einsteinathome.org/&quot;&gt;Einstein@Home&lt;/a&gt;. Another BOINC achievement, this
time for a specific project. Einstein@Home has always been one of my favourite
BOINC projects, with an interesting topic, and as important, a great, very
communicative team of admins, and a welcoming community.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/71/certificate.png&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/71/certificate.png&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;My Einstein@Home certificate, with a very &amp;quot;early 00s&amp;quot; design&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Of course those points don&#39;t mean much other than showing that I gave time and
money to crunch work units, but a round number is a round number :) That, and
bragging rights in an extremely niche topic.&lt;/p&gt;
&lt;h2&gt;My Einstein Machines&lt;/h2&gt;
&lt;p&gt;For the last few months I&#39;ve been crunching E@H mostly on my &lt;a href=&quot;https://stfn.pl/blog/56-kubernetes-boinc-raspberry-pi-cluster/&quot;&gt;Raspberry Pi
cluster&lt;/a&gt;. A
Raspberry Pi 4b is very weak in comparison to desktop PCs, but uses little power
and can run in the background 24/7 being barely noticeable, especially with a
&lt;a href=&quot;https://stfn.pl/blog/59-powering-fans-from-usb-pi-cluster/&quot;&gt;custom cooling
solution&lt;/a&gt;. A single
Pi crunches around 1400 points per day based on my observations. Having ARM
tasks is not common in BOINC projects, and that is another upside for Einstein.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/71/machines.png&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/71/machines.png&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;My current E@H machines, there have been many more before them&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Crunching Einstein on Pis would never bring me close to that milestone, most of
the heavy lifting has been done by my homelab server, crunching tasks on a
Nvidia RTX 3060 12GB. The GPU is a bit bottlenecked by my slow CPU, especially
in mix CPU/GPU tasks such as the All Sky Gravitional Search, but for now it will
have to be this way, upgrading my homelab needs to wait as I have more important
spendings. I don&#39;t usually run my GPU crunching 24/7, but if I were, with
crunching All Sky, the theoretical rate would be around 600k points per day.&lt;/p&gt;
&lt;h2&gt;My BOINC life&lt;/h2&gt;
&lt;p&gt;In general, my focus on BOINC has fallen down in the recent months, mostly
because it was announced that
&lt;a href=&quot;https://universeathome.pl/universe/&quot;&gt;Universe@Home&lt;/a&gt;, the project I crunched the
most, will not be restarted. After the death of the lead scientist there has
been a search for a team that could continue the research, but nobody was found.
The project servers are still up, and a single maintainer keeps the lights on in
hope of a change, but the chances are dim. That brought me down a lot.&lt;/p&gt;
&lt;p&gt;Also, as part of saying goodbye to Google, I changed the email address on all my
BOINC project accounts, and that messed up the BOINC statistics page, Universe
is no longer showing in my stats, and that was 1/3 of my total points. I know
that we crunch for science and not for fairy pointless internet points, but hey,
it feels good when number go up.&lt;/p&gt;
&lt;h2&gt;So, what now?&lt;/h2&gt;
&lt;p&gt;I will continue crunching Einstein@Home on my Pi cluster as long as it&#39;s
running, probably a few months until I disassemble it and use the Pis for
different usecases. My long term plan and hope is to go back to BOINC full time
once I start producing cheap, clean electricity from my PV installation, but
that will probably happen sometime late next year, not sure yet.&lt;/p&gt;
&lt;h2&gt;Going back to Folding@Home&lt;/h2&gt;
&lt;p&gt;As I put BOINC on the back burner,
&lt;a href=&quot;https://greennuclear.online/@collectifission&quot;&gt;Emil@Collectifission&lt;/a&gt; reminded me
of the existence of &lt;a href=&quot;https://foldingathome.org/&quot;&gt;Folding@Home&lt;/a&gt;. F@H shares a
similar idea as BOINC, distributed computing to solve scientific problems, but
runs on a different software, and it it&#39;s own closed ecosystem, with a single
app and projects being handed out by some sort of algorithm. I crunched F@H for
a few months during its sudden peak in popularity, when the project was looking
for ways to fight COVID in the initial phase of the global pandemic (you
remember that time?).&lt;/p&gt;
&lt;p&gt;I decided that a change of scenery would be good, and I joined F@H again with a
new account and started some crunching. Not sure how long I will crunch for
them, we&#39;ll see. I also joined his &lt;a href=&quot;https://stats.foldingathome.org/team/233733&quot;&gt;team for Fediverse
users&lt;/a&gt;, please feel invited!&lt;/p&gt;
&lt;p&gt;And that&#39;s it. When will I reach 200 million in Einstein? As &lt;a href=&quot;https://www.youtube.com/watch?v=QfU1lYAIlWs&quot;&gt;Albert himself
said in the intro to Red Alert&lt;/a&gt;,
&amp;quot;Sooner or later, time will tell&amp;quot;.&lt;/p&gt;
&lt;p&gt;If you would to join BOINC and have problems with setting up your account or
system, let me know, I will be happy to help.&lt;/p&gt;
&lt;p&gt;My previous &amp;quot;BOINC achievements&amp;quot; blog posts:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn.pl/blog/10-my-thoughts-on-boinc/&quot;&gt;My thoughts as I reach 100 million points in BOINC&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn.pl/blog/39-another-boinc-milestone/&quot;&gt;I reached 150 million points in BOINC&lt;/a&gt;&lt;/p&gt;
</description>
      <pubDate>Sat, 10 May 2025 24:00:00 GMT</pubDate>
      <dc:creator>STFN</dc:creator>
      <guid>https://stfn.pl/blog/71-100m-in-einstein/</guid>
    </item>
    <item>
      <title>Using PiHole and Tailscale to block ads on all my devices, including mobile</title>
      <link>https://stfn.pl/blog/72-pihole-tailscale/</link>
      <description>&lt;p&gt;This post is a continuation of my &lt;a href=&quot;https://stfn.pl/blog/46-wireguard-pihole-ad-blocking/&quot;&gt;WireGuard and PiHole for secure ad blocking
on your smartphone&lt;/a&gt; post.
I&#39;m writing it because I changed my PiHole setup and wanted to showcase it. Also
PiHole released version 6, which changes a lot, and I thought it would be good
to write about the new way of configuring it.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/72/phone.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/72/phone.jpg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;My Fairphone 4 with the PiHole admin interface opened in
Firefox. The queries graph is mostly empty because I reinstalled PiHole on a new
server.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;I recommend reading the previous post as well, there I talk about some things
not mentioned here, like reasons for restricting DNS server access from the
public Internet, I did not want to repeat myself too much.&lt;/p&gt;
&lt;p&gt;Here&#39;s my current situation:&lt;/p&gt;
&lt;p&gt;I am running PiHole on a Hetzner VPS. The server is a part of my Tailscale
Virtual Private Network (VPN). The PiHole DNS service and its admin panel are
only accessible via Tailscale, they are shielded from the open Internet by the
firewall and PiHole interface restrictions. The PiHole VPS is configured in the
Tailscale settings as the DNS server of the network, so any of my devices
connected to Tailscale will use PiHole as its DNS automatically. This way I can
use PiHole ad blocking anywhere I am, with any device, even my mobile phone,
just by connecting them to Tailscale.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;And now, how did I achieve that?&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;Setting up the machine&lt;/h2&gt;
&lt;p&gt;The first requirement is a machine that PiHole will be running on. It can be a
Raspberry Pi, or any local computer, it can also be a rented VPS. The only
requisite is that it has a stable and low-latency Internet connection. I went with
a VPS, because I plan to also run public services on it. Running it on a local
machine, not reachable from the open Internet will make the setup easier. In
that case you can skip most of the security steps like setting up the firewall.
Or you can do them nonetheless, it won&#39;t hurt :)&lt;/p&gt;
&lt;p&gt;I bought the cheapest available VPS from Hetzner. I went with the preferred way
of providing SSH keys instead of password authentication, and this is what I
highly recommend for security. After SSHing into the new server I went through
the usual initial setup and hardening steps, described very well in &lt;a href=&quot;https://www.digitalocean.com/community/tutorials/initial-server-setup-with-ubuntu&quot;&gt;Initial Server Setup with Ubuntu article on DigitalOcean&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;For now, open ports 22 (SSH), 80 (HTTP) and 443 (HTTPS) when configuring the firewall:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; ufw allow OpenSSH
&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; ufw allow &lt;span class=&quot;token string&quot;&gt;&quot;Nginx Full&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Installing Tailscale&lt;/h2&gt;
&lt;p&gt;All the different ways to install Tailscale are described in the &lt;a href=&quot;https://tailscale.com/kb/1347/installation&quot;&gt;installation
docs&lt;/a&gt;. There&#39;s also an &lt;a href=&quot;https://github.com/artis3n/ansible-collection-tailscale&quot;&gt;Ansible
Collection for
Tailscale&lt;/a&gt; if you&#39;re
into such things.&lt;/p&gt;
&lt;h2&gt;Installing Pihole&lt;/h2&gt;
&lt;p&gt;PiHole can be installed from an installation script. On the &lt;a href=&quot;https://docs.pi-hole.net/main/basic-install/#alternative-2-manually-download-the-installer-and-run&quot;&gt;installation
page&lt;/a&gt;
in the docs I went with the last option &amp;quot;Alternative 2: Manually download the
installer and run&amp;quot;. Before running the install script I gave it a quick run
down. Still, not the safest option, but I felt it to be just a tiny bit safer
than just piping curl to bash.&lt;/p&gt;
&lt;p&gt;In the PiHole initial configuration wizard, on the &amp;quot;Choose an interface&amp;quot; step, I
chose &lt;code&gt;tailscale0&lt;/code&gt;. If there is no such option for you, make sure you have
Tailscale properly installed and running.  However, even with choosing the right
interface, I encountered interface problems later on, more on that soon.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/72/choose_interface.png&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/72/choose_interface.png&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;PiHole initial wizard, choosing the interface step&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;And write down the admin password before you close the wizard! If you are like
me and forgot to do it, you can always reset it by running&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;pihole &lt;span class=&quot;token parameter variable&quot;&gt;-a&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-p&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;in the VPS console.&lt;/p&gt;
&lt;h2&gt;Enabling Pihole&lt;/h2&gt;
&lt;p&gt;You should now be able to access the PiHole admin panel at
&lt;code&gt;http://&amp;lt;vps_ip&amp;gt;/admin&lt;/code&gt;. The admin panel is accessible, but the DNS service that
PiHole needs to work is still being blocked by the firewall. To enable it run&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo ufw allow in on tailscale0 to any port 53
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will open the DNS port 53, but only for the &lt;code&gt;tailscale0&lt;/code&gt; interface, meaning
that only machines talking to the VPS through the Tailscale VPN will be able to
reach it. This is a security measure to stop anyone from the public Internet
using our new DNS server.&lt;/p&gt;
&lt;p&gt;PiHole DNS service can now be reached, but it will still not work with any
clients. To finish the setup we need to go to the PiHole settings in the admin
panel.&lt;/p&gt;
&lt;p&gt;In the admin interface go to Settings -&amp;gt; DNS, and start with switching to Expert
settings. This will show a new box, &amp;quot;Interface settings&amp;quot;. Most probably it will
be on its default settings &amp;quot;Allow only local requests&amp;quot;. This will not work for
us, as PiHole does not consider the Tailscale network to be local.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn.pl/blog/72-pihole-tailscale/&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/72/missing_dns_interface.png&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/72/missing_dns_interface.png&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/72/missing_dns_interface.png&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;PiHole DNS settings, the interface name is missing&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Now, when I first opened those settings, I had the interface name span empty in
the &amp;quot;Respond only on interface&amp;quot; option. Not sure if I did something wrong, or if
there is a bug. If you right away have an option &amp;quot;Respond only on
interface tailscale0&amp;quot; then just select it and skip the next paragraph. If not...&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/72/setting_intrface.png&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/72/setting_intrface.png&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;Setting the interface name&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Go to All settings and scroll down to the &lt;code&gt;dns.interface&lt;/code&gt; box, and set it to
&lt;code&gt;tailscale0&lt;/code&gt;. Save &amp;amp; Apply. Go back to the DNS options and now the interface
options should show and work properly.&lt;/p&gt;
&lt;h2&gt;Testing it all&lt;/h2&gt;
&lt;p&gt;Now PiHole DNS should work. From any other client connected to the same
Tailscale network as our PiHole machine, try out DNS resolving. I use the
&lt;code&gt;nslookup&lt;/code&gt; tool that can be installed on any Linux machine. For Windows or Mac
machines: sorry, I have no idea.&lt;/p&gt;
&lt;p&gt;First try:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;nslookup&lt;/span&gt; en.wikipedia.org &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;vps tailscale IP&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This should return a successful response with the Wikipedia IP address.&lt;/p&gt;
&lt;p&gt;Then try:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;nslookup&lt;/span&gt; en.wikipedia.org &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;vps public IP&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This should timeout, because DNS is not reachable from the public internet, as
we defined in the firewall and Pihole settings.&lt;/p&gt;
&lt;p&gt;If you have different results go throught the previous steps and make sure you
have everything configured correctly.&lt;/p&gt;
&lt;h2&gt;Configuring Tailscale DNS&lt;/h2&gt;
&lt;p&gt;In the Tailscale admin panel go to &lt;a href=&quot;https://login.tailscale.com/admin/dns&quot;&gt;DNS
settings&lt;/a&gt;, and under Nameservers enable
the &amp;quot;Override DNS servers option&amp;quot; and add a new Global nameserver, paste the
PiHole machine Tailscale IP. Not the public IP, the Tailscale one.&lt;/p&gt;
&lt;p&gt;And boom, le voila por favor, now every machine connected to your Tailscale
network will automagically use PiHole as its DNS server, and so will have ads
and trackers cut out.&lt;/p&gt;
&lt;h2&gt;Restricting access to Admin Panel&lt;/h2&gt;
&lt;p&gt;As I said in the beginning, I am deploying PiHole on a VPS that also has other,
publicly accessible services. And I don&#39;t want for the PiHole admin panel to be
publicly accessible, it should also be restricted to only my Tailscale network.
I also don&#39;t care that much about providing a TLS certificate for it, as it&#39;s
shielded by the VPN.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/72/admin_port.png&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/72/admin_port.png&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;Changing the admin interface port&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;To do that I went in the admin panel to All settings -&amp;gt; Webserver and API and in
the &lt;code&gt;webserver.port&lt;/code&gt; box I replaced everything in the &lt;code&gt;Value&lt;/code&gt; box with just
&lt;code&gt;8080&lt;/code&gt;. The current connection the admin panel will be broken if you do this
change, but that&#39;s fine. Now in the VPS console run:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; ufw allow &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; on tailscale0 to any port &lt;span class=&quot;token number&quot;&gt;8080&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will do the same as with the DNS port 53, enable connections on the port
8080 only through the Tailscale network. The admin panel can now be accessed
only through &lt;code&gt;http://&amp;lt;vps_tailscale_ip&amp;gt;:8080/admin/&lt;/code&gt;&lt;/p&gt;
&lt;h2&gt;PiHole on the phone&lt;/h2&gt;
&lt;p&gt;To use PiHole on your mobile phone, just install the Tailscale mobile app and
login to your account. No other actions needed, your phone will now use PiHole
as the DNS, and a lot of unneeded or straight malicious traffic will be cut out,
wherever you are. How cool is that?!&lt;/p&gt;
&lt;p&gt;There&#39;s one downside however, the Tailscale application is a bit of a drain on
the battery from what I observed, but I can life with that.&lt;/p&gt;
&lt;h2&gt;Bottom Line&lt;/h2&gt;
&lt;p&gt;And that&#39;s basically it. I have been running this setup for months now, and at
this moment it would be hard for me to live without an ad blocker on all my
devices. Thank you PiHole team for making such an awesome tool, and just a
reminder that they &lt;a href=&quot;https://pi-hole.net/donate/&quot;&gt;accept donations for their hard
work&lt;/a&gt;.&lt;/p&gt;
</description>
      <pubDate>Sat, 17 May 2025 24:00:00 GMT</pubDate>
      <dc:creator>STFN</dc:creator>
      <guid>https://stfn.pl/blog/72-pihole-tailscale/</guid>
    </item>
    <item>
      <title>A few updates, a few milestones. My magic internet points are growing.</title>
      <link>https://stfn.pl/blog/73-updates-and-milestones/</link>
      <description>&lt;p&gt;This will be a very short and simple blog post, but as I said in one of the
previous ones, my aim for this year is to use this blog also for, well,
blogging, and not only elaborate tutorials how to do computer shenanigans. I
really like the word &lt;em&gt;shenanigans&lt;/em&gt;.&lt;/p&gt;
&lt;h3&gt;Folding@Home&lt;/h3&gt;
&lt;p&gt;In the last few weeks I have been occasionally crunching
&lt;a href=&quot;https://foldingathome.org/&quot;&gt;Folding@Home&lt;/a&gt;. It&#39;s a nice change of pace after
years of doing only BOINC.&lt;/p&gt;
&lt;p&gt;So far I&#39;ve reached a nice milestone of 2 million points, and we&#39;ll see how it
goes from here.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/73/fah.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/73/fah.jpg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;F@H provided certificate certifying that I crunched 2.1 million points&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;As I&#39;m used to the BOINC way of life, I found some things with F@H confusing.
The web client is a nice touch, and something BOINC could use, but the way
headless machines need to be attached to one&#39;s account is not that intuitive.&lt;/p&gt;
&lt;p&gt;The fact that it is the system that decides which projects to send and not the
user choosing projects is a bit weird, but I guess it&#39;s just a different
philosophy, and it can allow for better matching of work units to the available
hardware. But the biggest difference in my opinion is that F@H allows only
crunching a single task for the CPU and another one for the GPU. In most BOINC
projects I&#39;ve seen, you can crunch one task per CPU core, and one or more tasks
per GPU. In my Ryzen 3700x times, it was a joy to see 16 Universe@Home tasks being crunched at once.&lt;/p&gt;
&lt;p&gt;Finally, in a very similar fashion to for example Einstein@Home, you get a lot,
like a LOT more points per hour if you crunch on GPU vs CPU. Of course those are
only magical pointless internet points, but still something interesting.&lt;/p&gt;
&lt;h3&gt;Leaving USA&lt;/h3&gt;
&lt;p&gt;I have finally migrated all of my self-hosted services from the US servers of
RackNerd to the European ones of Hetzner. Looks like it mostly went without a
hitch, with the exception of Umami losing some data. I guess it is because I did
not pinpoint the version of its Docker container, and the one running on the old
server was much older than the one I spun up on the new one. I migrated the
PostgreSQL database 1:1 between servers and probably some schema changed between
versions and I lost a bit of data, like the history of browser types. Not really an
issue, I only collect that data because I like looking at graphs, it&#39;s not used
for anything serious.&lt;/p&gt;
&lt;p&gt;This article was super useful for me: &lt;a href=&quot;https://stfn.pl/blog/73-updates-and-milestones/davejansen.com/how-to-dump-and-restore-a-postgresql-database-from-a-docker-container/&quot;&gt;How to dump &amp;amp; restore a PostgreSQL
database from a docker
container&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The last service to migrate was this exact blog. So now the words you are
reading come from Southern Germany. They are best enjoyed with a side of
&lt;a href=&quot;https://en.wikipedia.org/wiki/K%C3%A4sesp%C3%A4tzle&quot;&gt;Käsespätzle&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Actually, this blog post is the first one that will be sent to the new server.
If you see it, that means I correctly reconfigured the Forgejo action that pushes it
from my NAS to the VPS.&lt;/p&gt;
&lt;h3&gt;But what about AWS?&lt;/h3&gt;
&lt;p&gt;&lt;s&gt;Yes, I am using Cloudfront as the CDN for serving media on this blog. Moving to
a European CDN is on my very long todo list.&lt;/s&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;March 2026: I finally moved to a European CDN, and I wrote about it here:
&lt;a href=&quot;https://stfn.pl/blog/91-cloudfront-to-bunny-cdn/&quot;&gt;Goodbye AWS - moving from Cloudfront to Bunny
CDN&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;New services&lt;/h3&gt;
&lt;p&gt;&lt;em&gt;March 2026: I no longer use UptimeKuma because I haven&#39;t been using it
actively. And I also got rid of selhosted ntfy and just switched to the free
tier of their online offering, it&#39;s plenty enough for my needs.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;I started two new services, mostly for fun and learning.&lt;/p&gt;
&lt;p&gt;The first one is &lt;a href=&quot;https://uptimekuma.org/&quot;&gt;UptimeKuma&lt;/a&gt;. I now have a &lt;a href=&quot;https://status.stfn.pl/status/all&quot;&gt;status
page&lt;/a&gt;. It&#39;s running on my &amp;quot;dev&amp;quot; server and
monitoring the services of the &amp;quot;prod&amp;quot; one. The whole installation, setup, and
subdomain configuration process took less than hour. One gets proficient after
doing such things tens of times :)&lt;/p&gt;
&lt;p&gt;I also started using &lt;a href=&quot;https://ntfy.sh/&quot;&gt;ntfy&lt;/a&gt;. I am selfhosting it on the dev
server. It&#39;s running as a &lt;code&gt;systemd&lt;/code&gt; service. Recently I started moving away from
Docker and started using more of &lt;code&gt;systemd&lt;/code&gt;. In contrast to some people on the
Internet, I find &lt;code&gt;systemd&lt;/code&gt; very practical and easy to use. You know who you are
:) For now the only usecase I have for it is that I get a notification every
night whether the
&lt;a href=&quot;https://torsion.org/borgmatic/docs/how-to/monitor-your-backups/#ntfy-hook&quot;&gt;Borgmatic&lt;/a&gt;
backup of my GoToSocial instance finished successfully. When I find more
usecases for it in the future, the service will be already there.&lt;/p&gt;
&lt;h3&gt;GoToSocial&lt;/h3&gt;
&lt;p&gt;A second nice round number is that my GoToSocial instance is now federating with
10 000 other Fediverse instances. 10 000 is OVER NINE THOUSAND! I am so happy to
see that the Fediverse is strong, and there are so many independent instances.
And 10k is not that high of a number. If I remember correctly, &lt;a href=&quot;https://pol.social/@piotrsikora&quot;&gt;Piotr
Sikora&lt;/a&gt;, the admin of
&lt;a href=&quot;https://pol.social&quot;&gt;pol.social&lt;/a&gt; said in his talk that that instance is
federating with more than a hundred thousand other instances. That is extremely
impressive &amp;lt;3&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/73/fedi10k.png&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/73/fedi10k.png&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;fedi.stfn.pl, home to 6 users who wrote 1716 posts, federating with 10155 other instances&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;I also reached a thousand followers on my English-speaking/IT account. Actually
it happened for the second time, because I lost around ~200 followers when
migrating from Fosstodon. Speaking of Fosstodon, they now have a totally new
crew, and I wish them smooth sailing from now on.&lt;/p&gt;
&lt;p&gt;And that&#39;s basically it, thanks for reading&lt;/p&gt;
</description>
      <pubDate>Sat, 31 May 2025 24:00:00 GMT</pubDate>
      <dc:creator>STFN</dc:creator>
      <guid>https://stfn.pl/blog/73-updates-and-milestones/</guid>
    </item>
    <item>
      <title>How to make Pi-hole even more private and secure by switching to Unbound and adding extra blocklists</title>
      <link>https://stfn.pl/blog/74-pihole-unbound/</link>
      <description>&lt;p&gt;This is the next episode in my series on using Pi-hole, the previous ones are:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn.pl/blog/46-wireguard-pihole-ad-blocking/&quot;&gt;WireGuard and PiHole for secure ad blocking on your smartphone&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn.pl/blog/72-pihole-tailscale/&quot;&gt;Using PiHole and Tailscale to block ads on all my devices, including mobile&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Wait, I just saw it took me three blog posts to realize that Pi-hole is written with a hyphen :O&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;A few weeks ago, my part of the Fediverse was full of discussions about DNS that
were sparked by the DNS4EU controversy. Not going into details, a new service
was announced, stating to provide DNS that is EU based and adhering to EU
privacy laws. And it turned that most probably their way of operation is not as
clear as they say. There is a &lt;a href=&quot;https://techlog.jenslink.net/posts/dns4eu/&quot;&gt;very good writeup on
this&lt;/a&gt;, which I recommend reading.&lt;/p&gt;
&lt;p&gt;BTW, the Fediverse is the only social medium where DNS is a hot topic. And
that&#39;s what I love about it, never change Fedi &amp;lt;3&lt;/p&gt;
&lt;p&gt;That discussion made me realize that even though I use Pi-hole as my DNS server,
its upstream (upstream meaning the next server it queries) is still Google. When
my devices send a request to resolve the IP address of a particular site for the
first time, Pi-hole forwards it to Google&#39;s DNS server, 8.8.8.8. And I do not
like that.&lt;/p&gt;
&lt;p&gt;At first I switched my upstream DNS to &lt;a href=&quot;https://quad9.net/&quot;&gt;Quad9&lt;/a&gt;, which was
several times suggested in the threads about DNS4EU. It worked just fine, I saw
no issues with my day-to-day internet browsing, and changing the upstream DNS
from Google to Quad9 is a matter of two clicks in the Pi-hole settings.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/74/settings.png&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/74/settings.png&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;Pi-hole DNS settings, switching from Google to Quad9 means changing one checkbox&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;But as usual with computer stuff, there is still one level more. When I
mentioned on my Fediverse account that I moved to Quad9, a few people suggested to
move to Unbound instead. I heard that name a few times before but never got
around to try it. So, why not take it for a spin now? And so I did.&lt;/p&gt;
&lt;h2&gt;Unbound&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://www.nlnetlabs.nl/projects/unbound/about/&quot;&gt;Unbound&lt;/a&gt; describes itself as
a &lt;em&gt;validating, recursive, caching DNS resolver.&lt;/em&gt; The official explanation of
what that means can be found &lt;a href=&quot;https://unbound.docs.nlnetlabs.nl/en/latest/use-cases/home-resolver.html&quot;&gt;in the
docs&lt;/a&gt;,
but in short, the way I understand it, it means that Unbound does not query a
single upstream server for all queries, but divides them per domain. So for
example to get the IP address of one of the .com domains, it asks the .com DNS
server, and for the .net it asks the .net DNS server. Unbound also keeps a local
cache of all the queries that went through it so speed up subsequent queries.
All that makes the users&#39; internet life more private, as there is no one server
that knows of all the queries a single user is doing.&lt;/p&gt;
&lt;h3&gt;Installation and usage&lt;/h3&gt;
&lt;p&gt;Pi-hole &lt;a href=&quot;https://docs.pi-hole.net/guides/dns/unbound/&quot;&gt;provides an excellent
guide&lt;/a&gt; how to install Unbound and
configure it so that it talks to Pi-hole. I don&#39;t think it makes sense to
rewrite it here, so just go and follow it. The whole process took me maybe 20
minutes from start to finish.&lt;/p&gt;
&lt;p&gt;In my case, I installed Unbound using &lt;code&gt;apt&lt;/code&gt; on the same VPS that my Pi-hole is
running. It has been running for almost a week now and I have not seen any
degradation in speed or anything else. I think there is a tiny, tiny increase in
the VPS&#39;s load factor, by 2-3%, but I cannot say for sure. Still, my basic
Hetzner VPS&#39;s load rarely ever goes up over background noise.&lt;/p&gt;
&lt;p&gt;After installation I set the log level to 3, just to see what happens, and yeah,
it&#39;s not a level to keep indefinitely, in 24 hours the logs grew to around
150MB. I changed the log level to 1 and the logs stopped growing like mushrooms
on a wet Autumn day.&lt;/p&gt;
&lt;h2&gt;Adding extra blocklists to Pi-hole&lt;/h2&gt;
&lt;p&gt;As I was digging around Pi-hole setting up Unbound I started thinking about
maybe adding additional blocklists apart from the default one.&lt;/p&gt;
&lt;p&gt;I did some searching and came across a &lt;a href=&quot;https://www.reddit.com/r/selfhosted/comments/16sphv7/best_pihole_blocklists/&quot;&gt;Reddit
thread&lt;/a&gt;
about where to find blocklists. One source that was mentioned several times was
a &lt;a href=&quot;https://github.com/hagezi/dns-blocklists&quot;&gt;GitHub repository from a user called
Hagezi&lt;/a&gt;, and this is what I went with.&lt;/p&gt;
&lt;p&gt;I added the &lt;a href=&quot;https://github.com/hagezi/dns-blocklists?tab=readme-ov-file#proplus&quot;&gt;Multi
PRO++&lt;/a&gt; list
to Pi-hole and the results for me are mixed. I have not seen any change when
browsing the Internet using the web browser, mostly because in my Firefox,
uBlock Origin already removes basically all shady stuff before it is being
resolved by DNS.&lt;/p&gt;
&lt;p&gt;What I did however observe, is more blocking of desktop and mobile apps calling
home, as seen on the screenshot below. Mostly mobile, I was surprised how often
Revolut is doing &amp;quot;stuff&amp;quot;.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/74/blocked.png&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/74/blocked.png&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;My top blocked domains from a period of light phone use.
Still, mobile apps take the lead&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;I did not see any false positives yet, but then again, I don&#39;t visit that many
&amp;quot;controversial sites&amp;quot;.&lt;/p&gt;
&lt;p&gt;All in all, the percentage of blocked request in Pi-hole changed from around 1.5%
to 4%, but it fluctuates based on how much I use I phone, for reasons I
explained above.&lt;/p&gt;
&lt;p&gt;And that&#39;s it, I have to say that the combination of Firefox, uBlock Origin and
Pi-hole does wonders, makes the experience of browsing the Internet bearable
again.&lt;/p&gt;
&lt;p&gt;Thanks for reading!&lt;/p&gt;
</description>
      <pubDate>Wed, 25 Jun 2025 24:00:00 GMT</pubDate>
      <dc:creator>STFN</dc:creator>
      <guid>https://stfn.pl/blog/74-pihole-unbound/</guid>
    </item>
    <item>
      <title>Installing Actual Budget expense tracker in LXD and serving it using Tailscale with TLS.</title>
      <link>https://stfn.pl/blog/75-actual-budget-lxc/</link>
      <description>&lt;p&gt;I decided I want to try out tracking my expenses, and after some research I went
with trying out &lt;a href=&quot;https://actualbudget.org/&quot;&gt;Actual Budget&lt;/a&gt;. I&#39;ve been using it
for a few days now and the outlook is promising, I will report my experiences
once I use it more.&lt;/p&gt;
&lt;p&gt;However, I had some issues when setting it up with LXD so I am writing down how
I worked out a working solution here for others to benefit from. And for me for
the time when I want to install it again, having the steps written down will
help a lot :)&lt;/p&gt;
&lt;h2&gt;Prelude&lt;/h2&gt;
&lt;p&gt;First, let&#39;s sum up the situation. I have a homelab server running Debian 12. On
that system I have LXD installed, LXD is an orchestrator for LXC VMs/system
containers. Recently I have been preferring LXC from Docker, mostly for ease of
backup and transfer of containers. As my homelab is moving to homeprod, I find
continuity and stability of service to be the crucial part.&lt;/p&gt;
&lt;p&gt;To access the services in the containers I am using static routing, the first
method presented in this &lt;a href=&quot;https://www.youtube.com/watch?v=TmGvbXfwJEA&quot;&gt;excellent video by Stéphane
Graber&lt;/a&gt;. But in this case we will
need this method only for local testing, later on we will move to accessing the
container via Tailscale.&lt;/p&gt;
&lt;p&gt;And so I want to install Actual Budget in an LXC container and be available even
when I am away from my home network. I eventually went with the &amp;quot;Install from
source&amp;quot; installation method described in the &lt;a href=&quot;https://actualbudget.org/docs/install/build-from-source/&quot;&gt;installation
docs&lt;/a&gt;. I did not want
to install it with Docker, and I could not make the &amp;quot;CLI Tool&amp;quot; method work for
me in LXC.&lt;/p&gt;
&lt;h2&gt;Setting up the container&lt;/h2&gt;
&lt;p&gt;The description below is a result of several attempts of trial and error. A good
thing with containers is that you can easily kill them and start anew. And I had
to start from scratch a few times due to the requirements that Actual has.&lt;/p&gt;
&lt;p&gt;Let&#39;s start with creating a new container. In the LXD Web UI, create a new
container, I am using the Ubuntu 24.04 image, but in this case any will do, as a
long as it has &lt;code&gt;systemd&lt;/code&gt;. No special configuration needs to be done to the
container. Once the container is started, go to the Terminal tab.&lt;/p&gt;
&lt;p&gt;The next step is installing &lt;code&gt;node&lt;/code&gt; and &lt;code&gt;npm&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;At first I installed &lt;code&gt;node&lt;/code&gt; using &lt;code&gt;nvm&lt;/code&gt;, but that caused issues with Actual, it
could not find the &lt;code&gt;node&lt;/code&gt; binary, so I started again, and just downloaded the
&lt;code&gt;node&lt;/code&gt; Standalone Binary (the green button on the &lt;a href=&quot;https://nodejs.org/en/download&quot;&gt;download
page&lt;/a&gt;)  and copied it to &lt;code&gt;/usr/bin&lt;/code&gt;. When
downloading node, the tar archive also has &lt;code&gt;npm&lt;/code&gt;. It can stay there, &lt;code&gt;npm&lt;/code&gt; is
only used to install &lt;code&gt;yarn&lt;/code&gt; in our case.&lt;/p&gt;
&lt;p&gt;Extract the downloaded archive, copy &lt;code&gt;node&lt;/code&gt; to &lt;code&gt;/usr/bin&lt;/code&gt;, and go the &lt;code&gt;bin&lt;/code&gt;
folder in the downloaded tar archive where you will find &lt;code&gt;npm&lt;/code&gt;, and there
run&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;--global&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;yarn&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;as described in the installation docs. &lt;code&gt;yarn&lt;/code&gt; will be installed globally and we
will no longer need &lt;code&gt;npm&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Once &lt;code&gt;yarn&lt;/code&gt; is installed, the container is ready and we can move to installing
Actual. go through the rest of the &lt;a href=&quot;https://actualbudget.org/docs/install/build-from-source/&quot;&gt;installation
steps&lt;/a&gt; below the
&lt;strong&gt;Installing Actual&lt;/strong&gt; and &lt;strong&gt;Linux systemd setup&lt;/strong&gt; headers.&lt;/p&gt;
&lt;p&gt;At the moment of writing this blog post, there is a typo in the systemd
instructions, the correct location of the service file is
&lt;code&gt;/etc/systemd/system/actual-server.service&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Once the systemd service is up and running, you can access it via the IP address
of the container (if you have set up static routing as I did, or by adding a
proxy device listening on port 5006 of the container.&lt;/p&gt;
&lt;p&gt;What you should see is an error page saying that you need HTTPS. Actual requires
a HTTPS connection to work, there is no other way with it. So the next
step is making your Actual container provide a HTTPS connection, and for that
we will use Tailscale. Using Tailscale will have the added benefit of being
able to access Actual from any place and not only our home network.&lt;/p&gt;
&lt;h2&gt;Setting up Tailscale&lt;/h2&gt;
&lt;p&gt;The steps will be very similar to what I wrote in the &lt;a href=&quot;https://stfn.pl/blog/68-tailscale-tls-with-nextcloud/&quot;&gt;blog post about
Nextcloud&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I recommend you go through it to understand the process, I won&#39;t repeat here
everything I wrote there.&lt;/p&gt;
&lt;p&gt;We will install Tailscale in the container, install nginx, set it up as a reverse
proxy for Actual, generate TLS certificates using Tailscale and add them to the
nginx config. In case of Nextcloud, the reverse proxy server is Apache because
this is what Nextcloud comes with, for Actual I am using nginx because it is the
one I am most familiar with. But any server software capable of handling HTML
with TLS will do.&lt;/p&gt;
&lt;p&gt;First, install Tailscale in the container using the console. Tailscale
provides a handy install script that can be generated at their
&lt;a href=&quot;https://login.tailscale.com/admin/machines/new-linux&quot;&gt;admin page&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Once Tailscale is installed and authenticated, a new machine will appear in the
admin panel of Tailscale. The new machine will have a URL that Tailscale calls
the Magic DNS URL, we will need it in the next step.&lt;/p&gt;
&lt;p&gt;The next step is generating TLS certificates for the server. It can be done by
running&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;tailscale cert &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;your MagicDNS url &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; that machine&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The certificates will be generated right in the folder when you run this command
so run it for example in &lt;code&gt;/root&lt;/code&gt; if you did not create a user other than root.
Running that command should create two files, &lt;code&gt;*.crt&lt;/code&gt; and &lt;code&gt;*.key&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;With Tailscale part done, time to install and set up nginx. Nginx can be
installed with apt&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;apt&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; nginx&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After installation nginx will start working right away and respond on the default
port 80. You can confirm that by opening the container IP address in the browser
without providing a port number, you should see the nginx default welcome screen.&lt;/p&gt;
&lt;p&gt;Nginx installed, now it needs to be configured. Create a new file, called &lt;code&gt;actual&lt;/code&gt;
in &lt;code&gt;/etc/nginx/sites-available/&lt;/code&gt; and paste:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;server &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    listen &lt;span class=&quot;token number&quot;&gt;443&lt;/span&gt; ssl&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    listen &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;::&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;:443 ssl&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    
    server_name &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;CONTAINER_DOMAIN_URL&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# URL copied from Tailscale admin&lt;/span&gt;

    ssl_certificate /root/&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;CONTAINER_DOMAIN_URL&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;.crt&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    ssl_certificate_key /root/&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;CONTAINER_DOMAIN_URL&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;.key&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    location / &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        proxy_pass http://127.0.0.1:5006&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; 


        proxy_set_header Connection &lt;span class=&quot;token variable&quot;&gt;$http_connection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        proxy_set_header Upgrade &lt;span class=&quot;token variable&quot;&gt;$http_upgrade&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        proxy_set_header Host &lt;span class=&quot;token variable&quot;&gt;$host&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        proxy_set_header X-Real-IP &lt;span class=&quot;token variable&quot;&gt;$remote_addr&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        proxy_set_header X-Forwarded-For &lt;span class=&quot;token variable&quot;&gt;$proxy_add_x_forwarded_for&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        proxy_set_header X-Forwarded-Proto &lt;span class=&quot;token variable&quot;&gt;$scheme&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        client_max_body_size 512M&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;
    listen &lt;span class=&quot;token number&quot;&gt;80&lt;/span&gt; default_server&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    listen &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;::&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;:80 default_server&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    location / &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token builtin class-name&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;301&lt;/span&gt; https://&lt;span class=&quot;token variable&quot;&gt;$host&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$request_uri&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;Replace the URL with the one copied from the Tailscale admin. With the
configuration created, it has to be applied so that nginx can use it:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;cd&lt;/span&gt; /etc/nginx/sites-enabled
&lt;span class=&quot;token function&quot;&gt;ln&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-s&lt;/span&gt; /etc/nginx/sites/availables/actual &lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;rm&lt;/span&gt; default
nginx &lt;span class=&quot;token parameter variable&quot;&gt;-t&lt;/span&gt;
systemctl restart nginx&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The link to the default file needs to be removed, and then the nginx
configuration can be tested with &lt;code&gt;nginx -t&lt;/code&gt;. Once it is confirmed that the
config is valid, restart the service to load the changes.&lt;/p&gt;
&lt;p&gt;And that is all, you should now be able to access Actual in the browser by
using the Tailscale machine URL. And it will work over HTTPS, making Actual
happy. What is more, thanks to Tailscale, you will be able to use that URL
on any device logged into the Tailnet, not only on your home network.&lt;/p&gt;
&lt;h2&gt;Bottom Line&lt;/h2&gt;
&lt;p&gt;I find LXD and Tailscale to be a very powerful combination, making running
services straightforward, stable, and easily reachable from any place. So far
I have used this solution to run Nextcloud, Actual, and Forgejo. I might do
another blog post on Forgejo, how I have it configured together with Actions
to have my own CI/CD pipelines locally.&lt;/p&gt;
&lt;p&gt;I am also considering moving to Incus, as I heard good things about it and from
what I found so far, the migration should not be that hard.&lt;/p&gt;
&lt;p&gt;A real test of my LXD set up will be reinstalling my homelab base OS which I
plan in the near future, mostly to free the server from all the crud and
leftovers of failed experiments that I accrued over time. With that will come
exporting and then importing again my LXC containers and I hope it will go
smoothly.&lt;/p&gt;
&lt;p&gt;And that&#39;s basically it, thanks for reading!&lt;/p&gt;
</description>
      <pubDate>Thu, 03 Jul 2025 24:00:00 GMT</pubDate>
      <dc:creator>STFN</dc:creator>
      <guid>https://stfn.pl/blog/75-actual-budget-lxc/</guid>
    </item>
    <item>
      <title>Continued adventures with LXD; Grafana, InfluxDB, and ZFS storage</title>
      <link>https://stfn.pl/blog/76-moving-more-to-lxd/</link>
      <description>&lt;p&gt;I&#39;ve got this idea that I want to try out: A Dockerless homeprod.&lt;/p&gt;
&lt;p&gt;The emphasis here is on prod, because I will still be using Docker for testing
out stuff and learning, but for my home &amp;quot;production&amp;quot; I am migrating from Docker
to other solutions like LXD, for reasons I described for example in &lt;a href=&quot;https://stfn.pl/blog/67-deploying-nextcloud-locally-with-lxd/&quot;&gt;Deploying
Nextcloud locally with
LXD&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;And speaking of that Canonical-owned container and VM orchestrator, this post is
another entry in my LXD road.&lt;/p&gt;
&lt;h2&gt;Moving my monitoring stack to LXD&lt;/h2&gt;
&lt;p&gt;The service that I have been running the longest in my homelab journey has been
my monitoring stack. Currently it consists of:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Prometheus&lt;/li&gt;
&lt;li&gt;Node Exporter&lt;/li&gt;
&lt;li&gt;Smartctl Exporter&lt;/li&gt;
&lt;li&gt;Telegraf&lt;/li&gt;
&lt;li&gt;InfluxDB&lt;/li&gt;
&lt;li&gt;Grafana&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There have been also other services used in the past like &lt;code&gt;speedtest-exporter&lt;/code&gt;,
but those were dropped because I was not interested in them enough.&lt;/p&gt;
&lt;p&gt;The stack has two paths the data is flowing. The first one is Node &amp;amp; Smartctl
Exporters -&amp;gt; Prometheus -&amp;gt; Grafana, and the second one is Telegraf -&amp;gt; InfluxDB
-&amp;gt; Grafana. I have it configured this way because (at least to my knowledge)
Telegraf can fetch some metrics that Node Exporter cannot, like GPU or ZFS
statistics. Eventually all metrics go into Grafana and are presented there.&lt;/p&gt;
&lt;p&gt;Until now this stack has been running as this mix of technologies: Node Exporter
running on bare metal as a &lt;code&gt;systemd&lt;/code&gt; service, and everything else in Docker,
combined in a single Docker Compose file.&lt;/p&gt;
&lt;p&gt;I decided to split it between data collection and data presentation layers.
Prometheus, Exporters and Telegraf go to bare metal and InfluxDB and Grafana go
to an LXD container.&lt;/p&gt;
&lt;p&gt;The first is to be done, the second part is already in production.&lt;/p&gt;
&lt;p&gt;Installing Grafana and InfluxDB in a container turned out to be very simple,
both have very good documentation
(&lt;a href=&quot;https://grafana.com/docs/grafana/latest/setup-grafana/installation/debian/&quot;&gt;Grafana&lt;/a&gt;)
(&lt;a href=&quot;https://docs.influxdata.com/influxdb/v2/install/#install-influxdb-as-a-service-with-systemd&quot;&gt;InfluxDB&lt;/a&gt;),
and installing them in an Ubuntu 24.04 environment meant adding a repo,
installing them with &lt;code&gt;apt&lt;/code&gt; and starting them as &lt;code&gt;systemd&lt;/code&gt; services.&lt;/p&gt;
&lt;p&gt;What was a positive surprise for me, was that in order for Grafana in a
container to listen to Prometheus on the host, I just had to set the Prometheus
URL as &lt;code&gt;&amp;lt;&amp;lt;host_ip&amp;gt;&amp;gt;:9090&lt;/code&gt; in Grafana data sources. I suspected it might be more
complicated, but I guess that is result of the default LXD network being a
bridge type. Networking in LXD is still in many ways a mystery for me.&lt;/p&gt;
&lt;p&gt;And similarly, to send logs from Telegraf to InfluxDB, all I had to do was provide the container IP in &lt;code&gt;telegraf.conf&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-ini&quot;&gt;&lt;code class=&quot;language-ini&quot;&gt;&lt;span class=&quot;token section&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token section-name selector&quot;&gt;[outputs.influxdb_v2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/span&gt;]                                                                                                    
  &lt;span class=&quot;token key attr-name&quot;&gt;urls&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token value attr-value&quot;&gt;[&quot;http://10.54.60.34:8086&quot;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Adding a LXD Dashboard to Grafana&lt;/h2&gt;
&lt;p&gt;All that digging in Grafana made me think that it would be nice to have a
dashboard showing information about LXD itself. And that was very simple because
LXD has a Prometheus integration available straight out of the box.&lt;/p&gt;
&lt;p&gt;LXD has excellent documentation on how to set up the whole pipeline to send metrics and logs to Grafana:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://documentation.ubuntu.com/lxd/latest/metrics/#&quot;&gt;How to monitor metrics&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://documentation.ubuntu.com/lxd/latest/howto/logs_loki/&quot;&gt;How to send logs to Loki&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://documentation.ubuntu.com/lxd/latest/howto/grafana/&quot;&gt;Set up a Grafana dashboard&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Loki is something I have not touched before, maybe I&#39;ll investigate it more to
see if I can apply it as well to my other services.&lt;/p&gt;
&lt;p&gt;And le voila por favor:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/76/grafana_dashboard.png&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/76/grafana_dashboard.png&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;The Grafana Dashboard for LXD&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;What is peculiar is that LXD believes I have 180GB of RAM. While I would love
that to be the truth, it sadly isn&#39;t. I&#39;m guessing LXD multiplies the amount of
RAM I have (~32GB) by the number of running containers (six).&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/76/grafana_ram.png&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/76/grafana_ram.png&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;Grafana showing I have 180GB of RAM&lt;/span&gt;&lt;/p&gt;
&lt;h2&gt;Moving my LXD storage pool&lt;/h2&gt;
&lt;p&gt;The final change that I did was I moved my LXD storage pool from my boot drive
to one of my ZFS pools.&lt;/p&gt;
&lt;p&gt;One reason was that the boot drive was running out of space, and also I wanted
to have my LXD storage protected by ZFS features like copy-on-write and RAID1 (I
know, I know, RAID is not backup, but it is better to have two disks than one).&lt;/p&gt;
&lt;p&gt;A ZFS storage pool can be created in the LXD Web UI. I created a pool in my
&lt;code&gt;large-data&lt;/code&gt; pool, and &lt;code&gt;vms&lt;/code&gt; dataset. The process is described in LXD docs: &lt;a href=&quot;https://documentation.ubuntu.com/lxd/latest/howto/storage_pools/&quot;&gt;How
to manage storage
pools&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/76/storage_pool.png&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/76/storage_pool.png&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;My ZFS storage pool in LXD Web UI&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;The effect of creating an LXD pool in ZFS can be seen by running&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;zfs list &lt;span class=&quot;token parameter variable&quot;&gt;-t&lt;/span&gt; filesystem&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/76/zfslist.png&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/76/zfslist.png&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;List of datasets in ZFS created by LXD&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;LXD also allows migrating the storage of existing containers from one storage
pool to another with just a few clicks, but the containers need to be stopped.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/76/migrating_pools.png&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/76/migrating_pools.png&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;Storage Pool migration dialog in LXD Web UI&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Overall I am amazed how powerful LXD and how many built-in tools it provides to
handle both day-to-day and one-time tasks.&lt;/p&gt;
&lt;h2&gt;The incessant crunching&lt;/h2&gt;
&lt;p&gt;There is however one thing I did not foresee.&lt;/p&gt;
&lt;p&gt;Both my ZFS pools are two disk mirrors of HDDs. And usually they have seen very
little activity apart from the occasional copying of data to and from. But now,
when I moved my containers to one of the pools, the disk see constant activity,
writing logs and doing other continuous tasks. And those HDDs are of the louder
variety, being semi-professional NAS grade (one is a WD Red Pro and the second
one is Seagate Ironwolf). Which means there is now and endless stream of
crunching coming from under my desk, and I do not like that.&lt;/p&gt;
&lt;p&gt;I already have a solution planned for this, and hopefully I will write about it
in my next blog post.&lt;/p&gt;
&lt;h2&gt;The bottom line&lt;/h2&gt;
&lt;p&gt;This has been another episode of my homelab journey. After a few relatively
quiet months I&#39;m back on on track of tinkering and experimenting, and I have a
lot of plans for my servers. I strongly believe that a homelab is most often in
a temporary state, and there is always something to change. Then again, as most
of my homelab is homeprod, I need to be more careful, better plan the changes,
focus more on backups and recovery, and basically behave more like a sysadmin
having responsibilities. Move slowly and fix stuff :)&lt;/p&gt;
&lt;p&gt;And that&#39;s basically it, thanks for reading!&lt;/p&gt;
</description>
      <pubDate>Tue, 08 Jul 2025 24:00:00 GMT</pubDate>
      <dc:creator>STFN</dc:creator>
      <guid>https://stfn.pl/blog/76-moving-more-to-lxd/</guid>
    </item>
    <item>
      <title>State of my homelab in June 2025</title>
      <link>https://stfn.pl/blog/77-state-of-homelab-in-june-2025/</link>
      <description>&lt;p&gt;Almost a year has passed since I wrote the last &lt;a href=&quot;https://stfn.pl/blog/38-my-homelab-0624/&quot;&gt;State of my homelab
update&lt;/a&gt;, and that means it is high
time to write a new one. In the recent months my homelab has seen many changes,
both in hardware and software, so there is a lot ot to talk about.&lt;/p&gt;
&lt;h2&gt;Networking&lt;/h2&gt;
&lt;p&gt;I recently &lt;a href=&quot;https://stfn.pl/blog/70/&quot;&gt;wrote a blog post about my networking
setup&lt;/a&gt;, so here let&#39;s just have a short summary:&lt;/p&gt;
&lt;p&gt;At the time of writing the previous state of my homelab, my whole networking setup was a
TP-Link Archer router/AP and an also TP-Link Wi-Fi extender.&lt;/p&gt;
&lt;p&gt;Now I am running an almost solely Mikrotik setup, with the most important part
being a Wi-Fi mesh system built on two Mikrotik Audience access points. The
second Audience also works as a provider of wired ethernet to a PoE switch for a
part of my homelab. More details in the linked blog post.&lt;/p&gt;
&lt;h2&gt;Main Server/NAS&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/77/main.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/77/main.jpg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;The insides of my NAS&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;My main server is still my old gaming PC from 2015, which has now almost fully
reached the status of being a &lt;a href=&quot;https://en.wikipedia.org/wiki/Ship_of_Theseus&quot;&gt;Computer of
Theseus&lt;/a&gt;. Currently the only part
left of the original build is the motherboard, a Gigabyte GA-Z97-HD3. The CPU is
an Intel Xeon E3-1275L v3, 4C8T low power server processor, still providing
enough power to do anything I want with that server.&lt;/p&gt;
&lt;p&gt;RAM is I think the main bottleneck here. 32GB of 1600MT/s memory in four sticks
is relatively slow and gets eaten up quickly, especially by ZFS&#39;s cache.&lt;/p&gt;
&lt;p&gt;The GPU is an Nvidia RTX 3060 12GB from EVGA. It finds it use in BOINC, for
Jellyfin transcoding and a few other tasks. I will at some point write a blog
post with a list of usecases for having a GPU in a homelab.&lt;/p&gt;
&lt;p&gt;The thermals are okayish, the box lives under my desk for now, and in a room
where I also sleep, so all the fans are set in the silent mode. The disks run at
40-45C, with the 2.5&amp;quot; SSD boot drive being the hottest one. The disks get cooler
when the GPU is running, which would suggest that I need more exhaust fans. But
that will happen once I move my homelab to a separate room in my future house.
Then noise will not be an issue.&lt;/p&gt;
&lt;h3&gt;Storage&lt;/h3&gt;
&lt;p&gt;Having storage is important in a NAS [citation needed].&lt;/p&gt;
&lt;p&gt;The boot drive is a 500GB SSD. It works, stores the OS, not much else to write
about it.&lt;/p&gt;
&lt;p&gt;Then there are the two ZFS pools. Each one is a mirror of two 4TB HDDs, one is
made from a WD Red Pro and a Seagate Ironwolf. The second one is two refurbished
HGST drives. Both are regularly scrubbed and so far do not show any problems.&lt;/p&gt;
&lt;p&gt;The newest addition in terms of storage is a NVMe 1TB SSD in a PCIe adapter. It
is also running a ZFS pool, a single-disk one. I mentioned in the &lt;a href=&quot;https://stfn.pl/blog/76-moving-more-to-lxd/&quot;&gt;last blog
post on LXD&lt;/a&gt; that running a
monitoring stack on spinning rust is causing a constant noise as the drives
record data continuously. And this is my solution, I moved the more active LXD
containers to this NVMe pool, and the NAS is again mostly quiet.&lt;/p&gt;
&lt;h3&gt;The new PSU&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/77/psu.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/77/psu.jpg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;Old vs New&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;The most recent change to my main server has been the new power supply. I replaced a Bronze
efficiency Seasonic with a Platinum efficiency BeQuiet! The new model is the
Power Zone 2 750W.&lt;/p&gt;
&lt;p&gt;At first I was planning a separate blog post talking about the change in power
consumption when moving from a Bronze to a Platinum PSU, but I realized I do not
have enough usable data and my test would be not scientific enough to allow me
to make definite statements. Before I replaced my PSU I have been monitoring the
power consumption of this server specifically for only a few days and I simply
had not enough data for comparison. Also my GPU sometimes decides to idle at 15W
and sometimes at 7W, further making it hard to compare apples to apples.&lt;/p&gt;
&lt;p&gt;However, I can give a very general statement that I see an around &lt;strong&gt;10%&lt;/strong&gt;
decrease in power consumption in my specific usecase, but as I said, take it
with a brick of salt.&lt;/p&gt;
&lt;p&gt;Apart from the better efficiency, the new PSU gives me future proofing by
providing more power (550 vs 750W) and this fancy new 12VHPWR connector in case
I someday want to add a more powerful GPU. Or if I want to have a core meltdown
inside my case.&lt;/p&gt;
&lt;h3&gt;Software&lt;/h3&gt;
&lt;p&gt;In terms of software of my main server, I reduced a lot the number of services
running there. The OS is still Debian 12. The container orchestrators are Docker and LXD. And the services are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Immich&lt;/li&gt;
&lt;li&gt;Jellyfin&lt;/li&gt;
&lt;li&gt;Pinchflat&lt;/li&gt;
&lt;li&gt;ActualBudget&lt;/li&gt;
&lt;li&gt;Podgrab&lt;/li&gt;
&lt;li&gt;Forgejo&lt;/li&gt;
&lt;li&gt;Readeck&lt;/li&gt;
&lt;li&gt;NextcloudPi&lt;/li&gt;
&lt;li&gt;and the monitoring stack: Node Exporter, Smartctl exporter, Prometheus, Telegraf, InfluxDB and Grafana&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I already wrote blog posts about some of the services above:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn.pl/blog/67-deploying-nextcloud-locally-with-lxd/&quot;&gt;Deploying Nextcloud locally with LXD&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn.pl/blog/75-actual-budget-lxc/&quot;&gt;Installing Actual Budget expense tracker in LXD and serving it using Tailscale with TLS&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn.pl/blog/76-moving-more-to-lxd/&quot;&gt;Continued adventures with LXD: Grafana, InfluxDB, and ZFS storage&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn.pl/blog/60-pinchflat-jellyfin-youtube-duo/&quot;&gt;Using Pinchflat and Jellyfin to download and watch Youtube
videos&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Writing a blog for almost three years now gives you quite a few posts to refer to :)&lt;/p&gt;
&lt;p&gt;I&#39;ve &lt;a href=&quot;https://stfn.pl/blog/42-caddy-pihole-homelab-subdomains/&quot;&gt;been using Caddy as the reverse proxy for my
services&lt;/a&gt;, but now I
am moving to reverse proxies installed directly in the LXC containers and
serving SSL traffic via Tailscale, as I described in this blog post:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn.pl/blog/68-tailscale-tls-with-nextcloud/&quot;&gt;Access local Nextcloud with HTTPS anywhere by using Tailscale TLS
certificates&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Backup Server&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/77/backup.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/77/backup.jpg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;The insides of my the backup server, it is a mess, I know.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Buying that new PSU left me with enough spare parts to build a second PC. I
still find it amusing that I remember the times when there was The Family PC, a
single unit standing proudly in its designated space and shared among the family
members, and now everyone has at least one computing device and all of them are internet-connected.&lt;/p&gt;
&lt;p&gt;Anyway, that second PC is a mix and match of different parts I had lying around.
The motheboard is some Socket 1150 Asus with a i5-4460 Intel CPU, the exact one
I bought in 2015 to power that first gaming PC. It still provides enough power
for a headless machine, no problem. 8 gigs of some random RAM, a small SSD for
the boot drive.&lt;/p&gt;
&lt;p&gt;The case is an Akyga AK36BK, one of the cheapest available cases for the mATX
format. It&#39;s special feature is being made of steel so thin, you can easily bend
it with your fingers if something does not fit. The downside is space for only a
single 3.5&amp;quot; HDD unless you resort to some weird actions like velcroing drisks
together.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Glue my disks into clusters, this is my last resort&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;It&#39;s highlight and main reason to exist is the 3TB HGST hard drive. It&#39;s aim is
to be the backup drive for my main NAS. I&#39;m not able to fit everything on it,
but the most important stuff like documents fits there fine. In the future I
plan to buy a refurbished 16TB drive to be able to backup everything from my
main NAS with some place to spare.&lt;/p&gt;
&lt;p&gt;To copy over ZFS datasets between computers I am using a great tool called
&lt;a href=&quot;https://github.com/jimsalterjrs/sanoid&quot;&gt;syncoid&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Power consumption is not an issue with this PC because the plan is to use it as
a cold storage, boot it up every few days or weeks, copy over my NAS pools, do a
scrub and turn it off again.&lt;/p&gt;
&lt;p&gt;There is also a mystery PCIe board visible in the photo, but that will be the
topic of an upcoming post :)&lt;/p&gt;
&lt;p&gt;And with this machine I have almost reached the &lt;a href=&quot;https://www.seagate.com/pl/pl/blog/what-is-a-3-2-1-backup-strategy/&quot;&gt;3-2-1 rule of
storage&lt;/a&gt;.
For my most important data like documents or images, I have three copies of each
file, and one of those copies is in the cloud (a Hetzner Storage Box).&lt;/p&gt;
&lt;h2&gt;Other boxes&lt;/h2&gt;
&lt;p&gt;One of the reasons I reduced the number of services running on my main NAS is that I divided them among other PCs:&lt;/p&gt;
&lt;h3&gt;ADS-B&lt;/h3&gt;
&lt;p&gt;For at least a year now I&#39;ve been planning to a blog post about my ADS-B
airplane tracking setup. But today is not this day. So in short: I have a
Raspberry Pi 4b 4GB RAM, powered by a PoE HAT, running &lt;code&gt;dump1090-fa&lt;/code&gt; software to
track airplanes with the help of an RTL-SDR USB dongle and a DIY antenna. More
on that in the future.&lt;/p&gt;
&lt;h3&gt;Home Assistant&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/77/ha.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/77/ha.jpg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;The HA box, one of the Audience access points, and the printer which sometimes even works.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;My Home Assistant box is a Lenovo Thinkcentre M625q. I wrote about the whole
setup in the &lt;a href=&quot;https://stfn.pl/blog/58-home-assistant-lenovo-usff/&quot;&gt;Using a Lenovo USFF PC as a Home Assistant
box&lt;/a&gt; blog post.&lt;/p&gt;
&lt;p&gt;At some point in the future I will need to reinstall Home Assistant though, as the
development team is deprecating the HA Supervised install method, I will be
switching to the HA OS.&lt;/p&gt;
&lt;h3&gt;Streaming Box&lt;/h3&gt;
&lt;p&gt;And finally (I dearly hate the phrase &amp;quot;last but not least&amp;quot;) there is the
Streaming Box that my wife uses. The box lives under her TV, and she uses it to
watch Netflix and every other streaming service. The OS on it is Linux Mint, so
that makes her a Linux user :) The box is Fujitsu Esprimo Q920 with a i3-4170T
low power Intel. It usually works fine, but sometimes does not want to start,
then I need to open it, wiggle the parts inside a bit and it starts again.&lt;/p&gt;
&lt;h2&gt;Bottom Line&lt;/h2&gt;
&lt;p&gt;And this is my homelab. To be precise, this is the &amp;quot;local&amp;quot; part of the homelab,
I have not described my different VPSes that I am using, those will be the topic
of another blog post.&lt;/p&gt;
&lt;p&gt;This setup will probably last until the end of this year. If everything goes
fine, somewhere around New Year&#39;s we will be moving to our new house, and things
may change with the homelab during the move. But that also depends on the free
budget I will have during that time, so nothing is set in stone.&lt;/p&gt;
&lt;p&gt;Thanks for reading! I do enjoy writing those updates and I hope you enjoy reading them.&lt;/p&gt;
</description>
      <pubDate>Sat, 12 Jul 2025 24:00:00 GMT</pubDate>
      <dc:creator>STFN</dc:creator>
      <guid>https://stfn.pl/blog/77-state-of-homelab-in-june-2025/</guid>
    </item>
    <item>
      <title>Automatically regenerate Tailscale TLS certs using systemd timers</title>
      <link>https://stfn.pl/blog/78-tailscale-certs-renew/</link>
      <description>&lt;p&gt;In April of this year I wrote the &lt;a href=&quot;https://stfn.pl/blog/68-tailscale-tls-with-nextcloud/&quot;&gt;Access local Nextcloud with HTTPS anywhere by
using Tailscale TLS
certificates&lt;/a&gt; blog post
in which I showed how I am using Tailscale-generated certificates to access my
locally hosted services with HTTPS. Since that time I have been happily using
Nextcloud, Forgejo, and other apps via Tailscale with that glorious padlock
visible in the browser address bar.&lt;/p&gt;
&lt;p&gt;But today I could not access Nextcloud from my phone, the connection was
erroring out. Further investigation showed that the TLS certificate generated by
Tailscale expired. In contrast to what for example Certbot is providing,
Tailscale certificates do not auto renew. And that is understandable, Tailscale
is first and foremost a VPN service, and providing TLS certs is a secondary
service for them.&lt;/p&gt;
&lt;p&gt;I regenerated the certs manually, but that made me think, how to automate this
task?&lt;/p&gt;
&lt;p&gt;A quick &lt;a href=&quot;https://www.startpage.com/&quot;&gt;Startpage&lt;/a&gt; query (still my favourite search
engine) returned this &lt;a href=&quot;https://www.reddit.com/r/Tailscale/comments/1emlxaj/how_to_renew_tailscale_cert_automatically/&quot;&gt;Reddit
thread&lt;/a&gt; and I thought it will be a perfect solution, but I will take it to 11. And by
11 I mean being notified whether the task finished successfully or not using
using &lt;a href=&quot;https://ntfy.sh&quot;&gt;ntfy.sh&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I haven&#39;t yet wrote a blog post about ntfy, but one day probably I will, because
it is an awesome piece of software. For now, in short, it is a FOSS pub-sub
notification service, and I have it self-hosted on my secondary VPS. I also have
its app installed on my mobile phone to receive notifications on the go. I use
it for a few tasks, like informing that the Borg backup of my main VPS finished
without errors. The service page is publicly available, but the topics are private and
only accessible for me.&lt;/p&gt;
&lt;p&gt;Going back to the regeneration of TLS certs, for this task I am using &lt;code&gt;systemd&lt;/code&gt;
timers because I like &lt;code&gt;systemd&lt;/code&gt; and I think this task is a good exercise in
configuring its timers functionality, but if you prefer &lt;code&gt;cron&lt;/code&gt;, then sure, &lt;code&gt;cron&lt;/code&gt;
will also be a right choice.&lt;/p&gt;
&lt;p&gt;Everything below is done in the LXC container hosting my NextcloudPi instance,
but is generic enough to be applied to any machine, virtual or not, that is using
Tailscale certs and a HTTPS-capable server like Apache or nginx (I never know
whether to capitalize nginx and need to check it every time).&lt;/p&gt;
&lt;p&gt;I started with creating a bash script that regenerates the certificates, sends a
notification informing that the job finished or errored out, and finally
restarts Apache. Super basic Bash stuff.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;/etc/certs/script.sh&lt;/code&gt;&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token shebang important&quot;&gt;#!/bin/bash&lt;/span&gt;

tailscale cert TAILSCALE_MACHINE_URL &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;curl&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;
   &lt;span class=&quot;token parameter variable&quot;&gt;-H&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Authorization: Bearer NTFY_ACCESS_TOKEN&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;
   &lt;span class=&quot;token parameter variable&quot;&gt;-d&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Tailscale certs updated for NextcloudPi container&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;
   https://notifications.stfn.pl/ntfy &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;curl&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;
   &lt;span class=&quot;token parameter variable&quot;&gt;-H&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Authorization: Bearer NTFY_ACCESS_TOKEN&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;
   &lt;span class=&quot;token parameter variable&quot;&gt;-d&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Failed to update Tailscale certs for NextcloudPi container&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;   
   https://notifications.stfn.pl/ntfy &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;   
   systemctl restart apache2&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The script first runs the &lt;code&gt;tailscale cert&lt;/code&gt; command which generates new TLS certs
for the provided URL. If that command finishes without errors, the part after &amp;amp;&amp;amp;
is run, which is a &lt;code&gt;curl&lt;/code&gt; request to my &lt;code&gt;ntfy.sh&lt;/code&gt; server with a notification
saying that everything went fine. If the Tailscale command errors out, the &amp;quot;or&amp;quot;
(||) part is triggered, which is, again, a notification for me, but with an
error message. I had no idea that bash provides such ternary operators, such a
cool new thing to learn. Finally Apache is restarted to load the new certificates.&lt;/p&gt;
&lt;p&gt;Then I created a systemd service:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;/etc/systemd/system/tailscale-certs.service&lt;/code&gt;&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;Unit&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;Description&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;Update Tailscale TLS certificates
&lt;span class=&quot;token assign-left variable&quot;&gt;After&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;network.target 

&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;Service&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;WorkingDirectory&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;/etc/certs/
&lt;span class=&quot;token assign-left variable&quot;&gt;ExecStart&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;/usr/bin/bash /etc/certs/script.sh

&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;Install&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;WantedBy&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;multi-user.target&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;One important thing here is the &lt;code&gt;WorkingDirectory&lt;/code&gt; line. The &lt;code&gt;tailscale cert&lt;/code&gt;
command creates the certificate files in the directory when it is run, so we
need to define the directory for the service. The outcome of this service will
be two cert files generated in the &lt;code&gt;/etc/certs/&lt;/code&gt; directory, and that location
must correspond with the certs location defined in the Apache configuration.
Consult my linked blog post for details.&lt;/p&gt;
&lt;p&gt;Also the script must be executable (&lt;code&gt;chmod a+x /etc/certs/script.sh&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;With the script and the service, the last part of the puzzle is creating the
timer. A great resource on the topic is once again the Arch Wiki, and &lt;a href=&quot;https://wiki.archlinux.org/title/Systemd/Timers&quot;&gt;this
entry&lt;/a&gt; has been the main source
of information for me.&lt;/p&gt;
&lt;p&gt;It is important that the service and the timer share the same filename, this is
how they recognize each other.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;/etc/systemd/system/tailscale-certs.timer&lt;/code&gt;&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;Unit&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;Description&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;Update Tailscale certs twice a month

&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;Timer&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;OnCalendar&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;*-*-10,20

&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;Install&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token assign-left variable&quot;&gt;WantedBy&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;timers.target&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The only interesting part here is the&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token assign-left variable&quot;&gt;OnCalendar&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;*-*-10,20&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;which defines that the timer has to run twice a month, on the 10th and 20th day
of the month. Not providing the time means it will run at midnight UTC.&lt;/p&gt;
&lt;p&gt;I chose &amp;quot;twice a month&amp;quot; because I think it&#39;s a reasonable time span ensuring
that the certs will always be up to date, without flooding the certs service
with too many requests.&lt;/p&gt;
&lt;p&gt;Now the last thing to do is enable and start the timer by running&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;systemctl &lt;span class=&quot;token builtin class-name&quot;&gt;enable&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;--now&lt;/span&gt; tailscale-cert.timer&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;systemd also provides a command to show the list of active timers, running&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;systemctl list-timers&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;should show the new &lt;code&gt;tailscale-cert&lt;/code&gt; timer and the time left until the next
trigger.&lt;/p&gt;
&lt;p&gt;And that&#39;s it. I just need to repeat those steps for every container I am
running, and my services should be safe from expired TLS certs. And along the
way I learnt something about &lt;code&gt;bash&lt;/code&gt; and &lt;code&gt;systemd&lt;/code&gt;. I&#39;ve been using Linux since
2006 and I learn new things all the time and still feel I&#39;ve just scratched the
surface.&lt;/p&gt;
&lt;p&gt;Thanks for reading!&lt;/p&gt;
</description>
      <pubDate>Fri, 25 Jul 2025 24:00:00 GMT</pubDate>
      <dc:creator>STFN</dc:creator>
      <guid>https://stfn.pl/blog/78-tailscale-certs-renew/</guid>
    </item>
    <item>
      <title>How I bought a Tiny PC and turned it into a GPU workstation / gaming rig</title>
      <link>https://stfn.pl/blog/79-tiny-gpu-workstation/</link>
      <description>&lt;p&gt;I have not had a desktop PC since spring of last year. At that time I sold my
desktop because I felt laptops were enough for me, it was taking up a lot of
desktop space and its configuration made any significant upgrades costly.&lt;/p&gt;
&lt;p&gt;As time passed I start to miss the capabilities of a full size PC, mostly the
power of a discrete GPU. Not only for gaming, but also for other stuff, like GPU
acceleration for tasks like astrophotography postprocessing.&lt;/p&gt;
&lt;p&gt;However, I cannot right now justify buying a whole large, powerful PC because I
am in the middle of building a house, and every not-strictly-required expense is
postponed until we move in and stabilize there. Also in our current flat space
is always a constraint.&lt;/p&gt;
&lt;p&gt;So what to do?&lt;/p&gt;
&lt;p&gt;Last year I bought a Lenovo mini PC, an M625q, and it has been great as a tiny,
low-power box running Home Assistant. With only two cores of an Atom-like AMD
CPU, I would not try to use it for anything else.&lt;/p&gt;
&lt;p&gt;Reading more about the Lenovo lineup, I came across something very interesting:
Certain Lenovo Thinkcentre Tiny models have an PCIe slot, and allow for using a low
profile GPU! That raised my attention. Maybe I could make a small box with a
discrete GPU, and do stuff impossible on a typical laptop? By a typical laptop I
mean something small with an integrated GPU, not those gaming beasts that look
like an RGB brick combined with a vacuum cleaner.&lt;/p&gt;
&lt;h2&gt;Buying the base version&lt;/h2&gt;
&lt;p&gt;After doing research, I narrowed my search to the Lenovo ThinkCentre M720q
Tiny. Those models have an 8th gen Intel CPU (three types available), DDR4
RAM and an NVMe SSD slot. And most important, they also have an internal PCIe
slot for expansion cards!&lt;/p&gt;
&lt;p&gt;Recently I have been doing most of my IT shopping on Vinted. I know, Vinted is mostly
famous for buying clothes, but for some time now they have also an Electronics
category, and quite a few people are selling their stuff there. The prices are
good, and from my experience, the public is more reasonable than on the other
large flea marker platforms.&lt;/p&gt;
&lt;p&gt;After a few weeks of casual hunting, I finally bought the tiny computer. Inside
was an i5-8400T CPU with 6 cores 6 threads, 8GB of RAM in a single stick, and a
250GB NVMe SSD. Power supply included. For all that I paid 580PLN (~140EUR),
with shipping.&lt;/p&gt;
&lt;p&gt;A week later it arrived, basically looked like new inside and outside. Despite
that, I replaced the CPU thermal paste, because I had some lying around. On the
software side, I removed Windows as quickly as possible, and installed Kubuntu
25.04, my go-to distro right now.&lt;/p&gt;
&lt;p&gt;And from the start, it has been working like a charm. It&#39;s snappy in daily
tasks, quiet, and has no problems handling dual 1440p displays. All in all, much
better than the Lenovo T495 laptop I&#39;ve been running for the last months, with
only the portability being lost. But I&#39;m keeping the laptop for those rare
occasions when I want to take a computer with me somewhere.&lt;/p&gt;
&lt;p&gt;With the basics sorted out, came the time to upgrade this smol machine.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/79/IMG_20250813_183930.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/79/IMG_20250813_183930.jpg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;The insides of the Tiny with everything removed to make way for the GPU&lt;/span&gt;&lt;/p&gt;
&lt;h2&gt;Upgrades&lt;/h2&gt;
&lt;p&gt;I started with upgrading the RAM. I replaced the single 8GB stick with two
sticks of 3200MT/s 16GB DDR4 SODIMM RAM. With 32GB of RAM, I will be able to run
things like large development environments if I ever want to. Also I feel like
dual channel made it even snappier, but that may be a placebo effect.&lt;/p&gt;
&lt;p&gt;I haven&#39;t yet upgraded the storage. With a separate NAS for storing all the
stuff, the 250GB internal NVMe SSD is enough, at least for now, and I have some
SSDs in external USB enclosures is I ever need more directly attached storage.&lt;/p&gt;
&lt;h2&gt;The GPU&lt;/h2&gt;
&lt;p&gt;But they main part, the part I have been eyeing from the beginning, has been the
GPU. And putting a GPU in that tiny thing is a multi-step process.&lt;/p&gt;
&lt;p&gt;The first thing to do is upgrade the PSU. The M720q uses an external power
supply, the same as their pre-USB-C laptops, with a yellow rectangle plug. The
one that came with the computer was a standard 65W PSU. To use the GPU, I had to
first buy the 135W version to give it more juice. That was 90PLN (~21EUR)&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/79/IMG_20250813_184508.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/79/IMG_20250813_184508.jpg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;Comparison of the PSUs, the 135W one on top&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;The second part needed is a 90 degree riser for the PCIe slot. Lenovo sells one
for serious money, but I got a replacement from Aliexpress for 21PLN (5EUR).
Took two weeks to arrive.&lt;/p&gt;
&lt;p&gt;And finally, the GPU itself. The Tiny machine only accepts similarly tiny GPUs,
meaning single slot and low profile. At first I was considering getting the AMD
RX6400, but sadly, for the things I want to do, I need native CUDA support. I&#39;ve
heard there are now ways to run CUDA on AMD GPUs, but I don&#39;t feel like going
into that rabbit hole. So the only option for me was going with the Nvidia
Quadro lineup. The Quadro GPUs are aimed at workstations, and they are usually
smaller, don&#39;t have the gamerly bling, and use less power, often not requiring
an external power connector.&lt;/p&gt;
&lt;p&gt;I was at first aiming at the Nvidia Quadro T400 as a good middle ground between
price and power. The T in the name means the Turing generation, so the same as
the 16XX generation of their mainstream GPUs. I did not want to go older than
Turing because it&#39;s the oldest generation that still has ongoing driver and
CUDA support.&lt;/p&gt;
&lt;p&gt;But alas, good luck shined upon me, I found a T1000, a much faster model for a
price close to the P400, so I went with that one. I too found it on Vinted, and
it came from Lithuania. Came in two weeks, looking mint. The description was
that it was removed from some Dell prebuild.&lt;/p&gt;
&lt;p&gt;The card is a typical Quadro GPU, single slot, half length, low profile card
with 50W max TDP, meaning no additional power connector is required. According to
benchmarking sites, it&#39;s similar in performance to a GTX1650.&lt;/p&gt;
&lt;p&gt;In contrast, my main homelab is now running an Nvidia 3060 12GB with 160W TDP
which requires an additional 6+8 connector from the PSU.&lt;/p&gt;
&lt;h2&gt;Putting stuff together.&lt;/h2&gt;
&lt;p&gt;With all the parts delivered, the time came for the big ceremony.&lt;/p&gt;
&lt;p&gt;To install the GPU, first most things need to be removed from the PC. This model
of ThinkCentre allows two hard drives to be installed, one is an NVMe in an M.2
slot in the motherboard, and the second one is a 2.5&amp;quot; SATA SSD in a caddy.
The caddy takes the space for the GPU so it had to go. I also removed the WiFi
card and its antennae. Antennae? Antennas? Aerials? Those gizmos. With that,
there was space for the GPU. I first connected the GPU with the riser, and put
them into the PCIe slot in the motherboard.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/79/IMG_20250813_184206.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/79/IMG_20250813_184206.jpg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;The GPU installed&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;One thing I have not resolved in advance is the bracket. To mount the card to
the back of the box a special bracket is required, but it turns out the card can
live without it. It just lies on the back wall of the case, and it&#39;s fine as
long as nobody pulls too hard on the monitor cables. I found out it can be 3D
printed, and that is what I will do once I have a printer.&lt;/p&gt;
&lt;h2&gt;The first start&lt;/h2&gt;
&lt;p&gt;The card came with a miniDP to Display Port adapter, so I used it to plug one of
my monitors to the first output socket on the card, and with the case still
open, I pushed the On button. And it started, the fans came on, video output
live, Kubuntu launched like usual. I now have a usable GPU! The next thing for me was
to install the CUDA drivers that I need for some of my workloads, but that is a
story for another episode.&lt;/p&gt;
&lt;h2&gt;Gaming&lt;/h2&gt;
&lt;p&gt;I would not call myself a gamer, I just like playing a few games, and I&#39;ve been
playing the same three or four games for the last decade. The main hope I have
had of this GPU was to be able to play Civilization V on high settings.&lt;/p&gt;
&lt;p&gt;And indeed it runs and is playable. The game is not as snappy as on my old
desktop, but that one had a 16 thread Ryzen 7, so a totally different league.&lt;/p&gt;
&lt;h2&gt;The thermals&lt;/h2&gt;
&lt;p&gt;I played the game for 15 minutes, all happy, ignoring the blaring fans... and
the computer turned off. When I touched the GPU, I burned my finger. Turns out
that even without the case, and the fans at full speed, that tiny blower fan is
not able to cool GPU enough to stop it from shutting down from thermal overload.&lt;/p&gt;
&lt;p&gt;The rescue came from my Drawer of Random Cables accompanied by the Box of Random
Computer Parts. Below is the result of the cooperation of that magnificient duo.&lt;/p&gt;
&lt;p&gt;A long time ago I bought on Aliexpress a &amp;quot;female barrel jack to 3 pin fan
header&amp;quot; cable, and I have a BeQuiet 120mm fan lying around. I took the fan, the
cable, and a random 12V wall wart, and with the help of some zip ties and, I
think, legs from an old desk lamp, I placed the contraption above the PC.
Yes, I am a hoarder, but sometimes that helps.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/79/IMG_20250823_151553.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/79/IMG_20250823_151553.jpg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;The result in all its glory&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;I admit, it looks awfully janky, the combined fans are so loud I can only play
in ANC headphones, but it works, I&#39;m now able to play as long as I want.
According to &lt;code&gt;nvtop&lt;/code&gt; and &lt;code&gt;htop&lt;/code&gt; the CPU and GPU temps are around 60-70C.&lt;/p&gt;
&lt;h2&gt;The bottom line&lt;/h2&gt;
&lt;p&gt;So yeah, this pile of mess looking like something from Fallout is now my
workstation/desktop PC/gaming rig. Maybe some day I will make it nicer, make a
3D printed case to with a place for the fan, but for now it works. All in all, I
am very happy with it, it uses very little power (proper power consumption
measurements to come), even with that fan and cables it is indeed tiny, and
allows me to do more stuff than with just a laptop.&lt;/p&gt;
&lt;p&gt;Thanks for reading!&lt;/p&gt;
</description>
      <pubDate>Sat, 23 Aug 2025 24:00:00 GMT</pubDate>
      <dc:creator>STFN</dc:creator>
      <guid>https://stfn.pl/blog/79-tiny-gpu-workstation/</guid>
    </item>
    <item>
      <title>How I built an off-grid, solar powered webserver and exposed it to the Internet</title>
      <link>https://stfn.pl/blog/80-solar-website-with-custom-backend/</link>
      <description>&lt;p&gt;Here is my newest project: a webpage running on an off-grid server powered
solely by solar panels.&lt;/p&gt;
&lt;p&gt;&lt;a style=&quot;font-size:larger&quot; class=&quot;dark:text-white&quot; href=&quot;https://solar.stfn.pl/en/&quot;&gt;​solar.stfn.pl&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;The Background&lt;/h2&gt;
&lt;p&gt;I am in the middle of building my house. I mean, I am not building it
personally, the construction workers, plumbers, electricians are doing it.
The house is in the middle of a piece of land my wife and I bought in 2022. Next
to the house there is a shed which we have been using to store gardening
equipment and other random stuff. It also served me as a shelter during the cold
nights when doing astrophotography and star gazing. While the house will
eventually have electricity from the grid, the shed will be kept off-grid.&lt;/p&gt;
&lt;p&gt;As the construction is moving on, the people building the house convinced me to
buy a cctv camera, so that I can monitor the site and at least its presence will
deter any bad guys. I ordered a DTV solar powered security camera with a SIM
slot to stream video over LTE. Or so I thought.&lt;/p&gt;
&lt;p&gt;When it arrived, I realized I made a mistake, I ordered a wrong one, the one that I
ordered was a WiFi one, not LTE, so it would need a separate source of Internet
to share its feed.&lt;/p&gt;
&lt;p&gt;What I could have done was return it and get the proper one, but I do not like
the hassle and awkwardness of returning things, and most of all, I decided this
is the perfect moment to do what I have been wanting to do for a long time:
Build an off-grid PV system for the shed that could power a separate LTE router
and share WiFi for the camera, as well as for people visiting the site, maybe
even allow to occasionally work from there. And so the project started.&lt;/p&gt;
&lt;h2&gt;The PV system&lt;/h2&gt;
&lt;p&gt;I already have some experience with building 12V powerbanks (&lt;a href=&quot;https://stfn.pl/blog/12-off-grid-powerbank/&quot;&gt;blog post: A DIY
off-grid 12V powerbank&lt;/a&gt;), I &amp;quot;just&amp;quot;
needed to add the PV part to charge it.&lt;/p&gt;
&lt;h3&gt;The solar panel&lt;/h3&gt;
&lt;p&gt;At first I was thinking of buying a small 30-50 Wp solar panel. But as I was
browsing the local marketplace sites, I found a person who was selling lightly
used PV panels, as he was replacing them with more powerful ones. The panel
barely fit in my VW Polo, I had to drive very carefully because its side was
right next to my head, but I managed to transport it to my site, and now I was
the proud owner of a 330 Wp PV panel for which I paid only 100PLN (~25EUR). The
main issue was securing it to the roof of my shed, but after pondering, I
decided it&#39;s heavy enough to just put it on the roof as is. I bolted a metal bar
I had from an old project to the back of the panel to make it even heavier and
harder to overturn. My shed is low enough to allow me to just push the panel
onto the roof with the help of a small step ladder. The metal bar is mounted on
the top of the panel so that it goes over the rooftop, so the panel cannot slide
down. It also makes space on the sides for the cables.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/80/IMG_20250831_131902.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/80/IMG_20250831_131902.jpg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;The panel with the metal bar.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/80/IMG_20250831_131844.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/80/IMG_20250831_131844.jpg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;The panel is raised a bit so that the cables can go under its side&lt;/span&gt;&lt;/p&gt;
&lt;h3&gt;Cables and connectors&lt;/h3&gt;
&lt;p&gt;For the cables connecting the panel to the rest of the system I went with
dedicated solar panel cables. Those are thick cables in very hard coating that
is resistant to weather and UV rays. The panel itself has short cables going
from its underside, terminated with standard PV MC4 connectors. I terminated my
cables on one side with the corresponding MC4 connectors, and drove them through
the wall of the shed.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/80/IMG_20250831_131938.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/80/IMG_20250831_131938.jpg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;Cables going through the side of the shed.
They do not have connectors at the second end, so I could just drill holes the
size of the cables.&lt;/span&gt;&lt;/p&gt;
&lt;h3&gt;The PV controller&lt;/h3&gt;
&lt;p&gt;I chose a typical cheap PV controller, that proudly boasts that it can handle
20A, but I would not trust it with half of that value. The controller has three
sets of screw connectors, one for the PV panels, second one for the batteries
and the third for the output. It&#39;s main goal is to protect the batteries from
both overcharging and overdischarging, if the battery voltage falls below the
predefined value, around 11V, it will turn off any devices connected to it.
There are hundred of models of such controllers available, I just went with a
cheap, simple one, this project does not require anything special. Big Clive
made a few interesting videos about solar controllers, and I highly recommend
watching them before buying one.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=Ezh0ylkAyTw&quot;&gt;Big Clive: Exploring a Mass Produced Solar Charge Controller&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=o_tiUuXBjW0&quot;&gt;Big Clive: 10A vs 100A Solar Charge Controllers&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;The USB charger and cables&lt;/h3&gt;
&lt;p&gt;One of the features I wanted to have from the solar installation is being able to
at least charge my phone from a USB socket. It took me quite a while to find the
USB charger in line with what I needed. I finally found one in the area of car
accessories. And that makes sense, cars have internal 12V electrical
installation and people in them want to charge their phones. The one I bought
fits into the place of a cigarette lighter / 12V output and provides two USB-A
ports. The maximum it can output on a single port is 2.4A, which is enough even
for a Pi 4b, but only if it runs without power hungry accessories. For a Zero it
is way more than enough. The installation was simple, in my set of hole saws I
had one with exactly the right diameter, I drilled a hole in the tool box, put
it the charger and secured it with a provided ring.&lt;/p&gt;
&lt;p&gt;I connected the batteries with the controller using a thick 12 AWG cable with a
20A fuse. The output side I did with a thinner, 18 AWG cable, as I do not expect
high loads. I used WAGO connectors to create a parallel connection between every
power output, the USB charger, the XT60 sockets, and the cable for the LTE
modem.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/80/IMG_20250823_131204.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/80/IMG_20250823_131204.jpg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;The toolbox working as a place to store all
of the power components. On top of it there is also a 220V voltage converter
powered from the batteries. On the desk there is the webserver Pi, and an IKEA
lamp converted to use 12V DC.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/80/IMG_20250823_131247.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/80/IMG_20250823_131247.jpg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;The inside of the tool box with the
batteries, the controller, and all those cables.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/80/IMG_20250823_131247_zoom.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/80/IMG_20250823_131247_zoom.jpg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;Zoom on the internals of the tool box&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/80/IMG_20250823_131237.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/80/IMG_20250823_131237.jpg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;Side of the box with the external connectors
visible. The blueish cable is the micro-USB powering the Pi.&lt;/span&gt;&lt;/p&gt;
&lt;h3&gt;The 220V power converter&lt;/h3&gt;
&lt;p&gt;A part of the power system that is not being used in the solar webserver project
is the 12V to 220V power converter. I think it is nonetheless important to
mention it so that you can omit the mistake that I did. I first connected it to
the PV controller, as you can see in the attached photos. But it quickly turned
out that often on startup it would overload the controller and make it to reset.
I&#39;m guessing the converter charges some capacitors on startup and draws a very
high current, killing the PV controller. What I did is connect it directly to
the batteries. It has its own overdischarge protection, so it will not harm the
batteries if used for too long. And now I have a source of standard 220V power
to, for example, charge a laptop or a cordless drill battery.&lt;/p&gt;
&lt;h2&gt;Internet gateway&lt;/h2&gt;
&lt;p&gt;The gateway to the Internet for the Pi is a &lt;a href=&quot;https://mikrotik.com/product/ltap_mini_lte_kit_2024&quot;&gt;Mikrotik LTE AP outdoor
router&lt;/a&gt; that I also managed
to buy second-hand. I mounted it outside the shed and put a standard barrel jack
cable through the wall to power it from the PV controller. The router creates a
Wi-Fi network for the Pi to connect to, and has an LTE modem to talk to the
Internet. I have a limited data quota on my deal, so I am monitoring the traffic
usage on the mobile company user portal. So far the webserver has been only
using a few megabytes per day of traffic. There is no special configuration on
the Mikrotik, just typical 2.4 Ghz Wi-Fi.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/80/IMG_20250823_114854.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/80/IMG_20250823_114854.jpg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;The Mikrotik router/modem fastened to the
wall of the shed.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;And with that, the initial Wi-Fi part of the installation was done. The camera
has been working without issues, I can check what is happening at the site using
my phone. Sadly, only by using a proprietary app from the vendor. But this is
just a temporary solution, once the house is built I plan to move to standard IP
cameras and record the feeds using open source software like Frigate.&lt;/p&gt;
&lt;h2&gt;Moving Further: Making a webserver&lt;/h2&gt;
&lt;p&gt;With the Wi-Fi done, I started thinking about other possible uses for the
installation that I built. And I thought that it would be awesome to use it to
make a solar powered webserver and host my own, personal website. And so I went
this route.&lt;/p&gt;
&lt;p&gt;The choice of hardware was simple, a Raspberry Pi Zero W in my drawer of Pis,
waiting for better times was the obvious choice. I added to it a BMP280
pressure/humidity/temperature sensor to gather some environmental data. The USB
charger I mentioned before became the power source for the Pi. Exposing the Pi
to the Internet had been an issue until I learned I can go over (CG)NATs using
Wireguard. And with all of the components in place, off I went.&lt;/p&gt;
&lt;h2&gt;The Server&lt;/h2&gt;
&lt;p&gt;The hardware is just a Pi Zero W in the official Pi case, absolutely nothing
interesting to talk about here.&lt;/p&gt;
&lt;p&gt;For the server software, I went away from my typical Nginx setup and chose
Lighttpd, because it is, well, light. The website is static HTML (well, not
exactly static, but from the perspective of the webserver it is, more on that
later), so configuring the server was only a matter of cloning the git repo to
&lt;code&gt;/var/wwww/html&lt;/code&gt; and modifying the config file:&lt;/p&gt;
&lt;p&gt;Below are only the lines I added or modified. I added the &lt;code&gt;mod_accesslog&lt;/code&gt; server
module to monitor traffic, and configured the file that stores the access log. I
also changed the server port to 81, as this server does not handle traffic
directly, but is downstream of a reverse proxy that actually serves content on port 80.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;/etc/lighttpd/lighttpd.conf &lt;/code&gt;&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;server.modules &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 string&quot;&gt;&quot;mod_indexfile&quot;&lt;/span&gt;,
        &lt;span class=&quot;token string&quot;&gt;&quot;mod_access&quot;&lt;/span&gt;,
        &lt;span class=&quot;token string&quot;&gt;&quot;mod_alias&quot;&lt;/span&gt;,
        &lt;span class=&quot;token string&quot;&gt;&quot;mod_redirect&quot;&lt;/span&gt;,
        &lt;span class=&quot;token string&quot;&gt;&quot;mod_accesslog&quot;&lt;/span&gt;,
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

server.port &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;81&lt;/span&gt;
accesslog.filename &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;/var/log/lighttpd/access.log&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Exposing the website to the Internet&lt;/h2&gt;
&lt;p&gt;Finding a good way to expose a locally hosted webserver to the Internet has been
a big problem for me for a long time. The breakthrough came when I learned that
I can use &lt;a href=&quot;https://www.wireguard.com/quickstart/&quot;&gt;Wireguard&lt;/a&gt; VPN to punch
through NATs. This method requires to also have another server, that is publicly
accessible to be a proxy between the local server and the wider Internet.&lt;/p&gt;
&lt;p&gt;This of course raises a question, is this website actually solar powered when
making it work requires another server in a datacenter? One could say no, but I
think that well, packets flowing through the Internet pass a lot of different
servers either way, then there are the mobile signal towers, the DNS servers and
all the other components that run to make the Internet work. So having a VPS on
a machine in a datacenter that is running either way does not make a difference.&lt;/p&gt;
&lt;p&gt;For the public server, I am using a Hetzner&#39;s VPS which I have been already
using for different tasks. It does not have to be powerful for being a proxy,
all it does is run a webserver that passes the traffic between the Pi and the
Internet. The VPS also handles the TLS certificates for the HTTPS to work. The
TLS certificates are configured using &lt;a href=&quot;https://certbot.eff.org/&quot;&gt;Certbot&lt;/a&gt;, which
makes obtaining and configuring them very easy, basically a single command. The
domain also points to that public server.&lt;/p&gt;
&lt;p&gt;The Pi and the VPS are connected using Wireguard. I already described how to do
a typical point-to-point Wireguard connection in my blog post about PiHole, you
can consult it for reference:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn.pl/blog/46-wireguard-pihole-ad-blocking/&quot;&gt;WireGuard and PiHole for secure ad blocking on your smartphone&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;However, I want to show you the Wireguard config file on the Pi, because it has one important part:&lt;/p&gt;
&lt;pre class=&quot;language-ini&quot;&gt;&lt;code class=&quot;language-ini&quot;&gt;&lt;span class=&quot;token section&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token section-name selector&quot;&gt;Interface&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token key attr-name&quot;&gt;Address&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token value attr-value&quot;&gt;10.8.0.3/24&lt;/span&gt;
&lt;span class=&quot;token key attr-name&quot;&gt;PrivateKey&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token value attr-value&quot;&gt;&amp;lt;Pi private WG key&gt;&lt;/span&gt;

&lt;span class=&quot;token section&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token section-name selector&quot;&gt;Peer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token key attr-name&quot;&gt;PublicKey&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token value attr-value&quot;&gt;&amp;lt;server public WG key&gt;&lt;/span&gt;
&lt;span class=&quot;token key attr-name&quot;&gt;AllowedIPs&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token value attr-value&quot;&gt;10.8.0.0/24&lt;/span&gt;
&lt;span class=&quot;token key attr-name&quot;&gt;Endpoint&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token value attr-value&quot;&gt;&amp;lt;server public IP address&gt;:51820&lt;/span&gt;
&lt;span class=&quot;token key attr-name&quot;&gt;PersistentKeepalive&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token value attr-value&quot;&gt;10&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The crucial part is the &lt;code&gt;PersistentKeepalive&lt;/code&gt; stanza in the config, which ensures that
the connection is being kept up all the time, and so the server can continuously
communicate with the NATed Pi.&lt;/p&gt;
&lt;p&gt;On the public server I am running Nginx, because I am used to it. Here&#39;s how it
is configured:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;/etc/nginx/sites-available/solar&lt;/code&gt;&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;server &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    server_name solar.stfn.pl&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    location / &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        proxy_pass http://10.8.0.3:81&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        proxy_set_header Host &lt;span class=&quot;token variable&quot;&gt;$host&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        proxy_set_header X-Real-IP &lt;span class=&quot;token variable&quot;&gt;$remote_addr&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        proxy_set_header X-Forwarded-For &lt;span class=&quot;token variable&quot;&gt;$proxy_add_x_forwarded_for&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        proxy_set_header X-Forwarded-Proto &lt;span class=&quot;token variable&quot;&gt;$scheme&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;

    listen &lt;span class=&quot;token number&quot;&gt;443&lt;/span&gt; ssl&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# managed by Certbot&lt;/span&gt;
    ssl_certificate /etc/letsencrypt/live/solar.stfn.pl/fullchain.pem&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# managed by Certbot&lt;/span&gt;
    ssl_certificate_key /etc/letsencrypt/live/solar.stfn.pl/privkey.pem&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# managed by Certbot&lt;/span&gt;
    include /etc/letsencrypt/options-ssl-nginx.conf&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# managed by Certbot&lt;/span&gt;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# managed by Certbot&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 keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$host&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; solar.stfn.pl&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 builtin class-name&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;301&lt;/span&gt; https://&lt;span class=&quot;token variable&quot;&gt;$host&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$request_uri&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;# managed by Certbot&lt;/span&gt;


    listen &lt;span class=&quot;token number&quot;&gt;80&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    server_name solar.stfn.pl&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token builtin class-name&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;404&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# managed by Certbot&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Most of the lines here are automatically added by Certbot, and so are not
interesting for us. The most important part is the &lt;code&gt;location&lt;/code&gt; dictionary. Here
it is defined that Nginx works as a reverse proxy, passing requests from the
clients to &lt;code&gt;10.8.0.3&lt;/code&gt; which is the Pi&#39;s Wireguard IP address. Nginx contacts the
Pi on port 81, because it itself is listening on port 80 for traffic incoming
from the wider Internet. Remember that port 81 needs to be opened in the VPS&#39;s
firewall.&lt;/p&gt;
&lt;h2&gt;Pyriodic Backend&lt;/h2&gt;
&lt;p&gt;At the top of the webpage there is a green box showing the current climate data
inside the shed, and some statistics about the Pi itself. How does it work when
the page is static HTML? This is where my newest pet project comes into play: Pyriodic Backend.&lt;/p&gt;
&lt;p&gt;Pyriodic Backend is what it says in the name, it&#39;s meant to be a very simple
backend service, written in Python, for handling periodically updated data.&lt;/p&gt;
&lt;p&gt;How it works is that the backend service runs every minute, powered by cron, and
with every run, it loads the HTML files of website, and replaces the value of
specific tags using the output of functions connected to those tags. Pyriodic
Backend uses BeautifulSoup4 behind the scenes to do the HTML manipulation. The
project is currently in a very early alpha stage, but as you can see, it is
already being used &amp;quot;in production&amp;quot;. The repo is open source and under the GPL
license. You can check it out on Codeberg, and if you would like to develop it
with me, please feel free to create an issue or send me a PR. My goal is to make
it a universal, very light and simple backend for static websites.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://codeberg.org/stfn/pyriodic-backend&quot;&gt;Pyriodic Backend on Codeberg.org&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Next Steps&lt;/h2&gt;
&lt;p&gt;I&#39;m already thinking on how to upgrade the system. Because of the large size of
the solar panel, I am considering moving to a Pi 4b connected to the router via
an ethernet cable. This should reduce a lot of the instability and latency
caused by Wi-Fi. If the PV system starts having trouble with recharging the
batteries during short winter days, I&#39;ll just go back to the Zero. I would like
to add much more things to the website, but then the bottleneck will be the
speed of the Internet, and the limited data quota I have. My plan is to
gradually add stuff, and see how the performance and data usage changes.&lt;/p&gt;
&lt;p&gt;I also would like to add battery charge monitoring for the website. I already
did a project focused on measuring the charge of 12V batteries:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn.pl/blog/22-pico-battery-level/&quot;&gt;How to monitor 12V battery charge with a Raspberry Pi Pico&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;but I need to find a way to connect it with the Pi and the backend.&lt;/p&gt;
&lt;p&gt;And eventually I will move the house that is being built next to the shed, and
when I have better Internet and equipment there, I will be able to expand my
website even more.&lt;/p&gt;
&lt;h2&gt;Bottom Line&lt;/h2&gt;
&lt;p&gt;And that&#39;s it! I haven&#39;t been so excited with a project for a very long time. I
feel that writing a website from scratch using the simplest possible
technologies is a turning page for me, and gives me so much fun. And I am happy
that I can share both the hardware plans, and the software with you, especially
the Pyriodic Backend. I am hoping other people will use it for their projects.
If you do, please let me know!&lt;/p&gt;
&lt;p&gt;And being able to physically host a webserver offgrid, with the only
requirements being the Sun and a bit of LTE coverage, I feel that it opens so
many possibilities. And it feels both #SmallWeb and
&lt;a href=&quot;https://www.re-des.org/es/a-solarpunk-manifesto/&quot;&gt;Solarpunk&lt;/a&gt;, two movements
that are very dear to me.&lt;/p&gt;
&lt;p&gt;If you want to build a similar off-grid server, and have any questions, just
drop me an email, I will be happy to help :)&lt;/p&gt;
&lt;p&gt;Thanks for reading!&lt;/p&gt;
</description>
      <pubDate>Thu, 04 Sep 2025 24:00:00 GMT</pubDate>
      <dc:creator>STFN</dc:creator>
      <guid>https://stfn.pl/blog/80-solar-website-with-custom-backend/</guid>
    </item>
    <item>
      <title>Today I TIL; Mikrotik NTP support and the surprise use of octal counting</title>
      <link>https://stfn.pl/blog/81/</link>
      <description>&lt;p&gt;This is hopefully the first post in the &amp;quot;Today I #TIL&amp;quot; series. Yes, I know :) Hi
Amin:) The idea is to make shorter blog posts about one or two new things that I
learned more or less on the same day as the blog post.&lt;/p&gt;
&lt;p&gt;Today something unsuspecting happened. I was working away, writing Python and
all that stuff, when the Internet broke. I had WiFi, but no Internet access. I
assumed my ISP was acting up again, but this is was something different. To cut
the story short, my WiFi bridge (described in this &lt;a href=&quot;https://stfn.pl/blog/70/&quot;&gt;blog
post&lt;/a&gt;) stopped working, and I had to restart one of my
Audience access points to make it work again.&lt;/p&gt;
&lt;p&gt;But that&#39;s not the main story. When I was browsing through WinBox (Mikrotik&#39;s
configuration software) I came across the NTP section. And so I learnt that
Mikrotik devices (I guess most of them?) can work as an NTP server, an NTP
client, or both.&lt;/p&gt;
&lt;p&gt;NTP, Network Time, Protocol, is the way computers share information about the
time. There are NTP servers that have very precise clocks, which tell other
computers which time it is. And this can work in a cascade, any computer can be
an NTP server, get the time from an upstream NTP server, and share it amongst
its peers.&lt;/p&gt;
&lt;p&gt;Jeff Geerling made a few interesting videos about &lt;a href=&quot;https://www.youtube.com/watch?v=zT71UvUxhjU&quot;&gt;NTP and Raspberry
Pis&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;And so I thought that it would be fun to set up my Mikrotik router to get the
time from some proper NTP server and share it in my local network. Yes, I know,
I have a rather special definition of fun. But you, dear reader, are reading this
post, so probably we have something in common.&lt;/p&gt;
&lt;p&gt;First I looked for a suitable time server. I wanted something relatively local.
And a quick search resulted in this: &lt;a href=&quot;https://www.gum.gov.pl/pl/dla-biznesu/uslugi/zegar/524,Zegar.html&quot;&gt;Główny Urząd Miar (in
Polish)&lt;/a&gt;.
Turns out, a Polish governmental organisation, Główny Urząd Miar (Central Office
of Measures) runs their own NTP servers, and they are located in the &amp;quot;Laboratory
of Time and Frequency&amp;quot;, which sounds like a super cool place to work.&lt;/p&gt;
&lt;p&gt;With the server chosen, I went to configure NTP services on the router. And that
is very simple, &lt;a href=&quot;https://help.mikrotik.com/docs/spaces/ROS/pages/40992869/NTP&quot;&gt;Mikrotik&#39;s NTP
docs&lt;/a&gt; say it all.&lt;/p&gt;
&lt;p&gt;This is how I configured my NTP server and client on the router:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/81/mikrotik_ntp.png&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/81/mikrotik_ntp.png&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;Screenshot of the Mikrotik&#39;s configuration
showing the NTP client and server windows&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;I added two CIDR ranges because my access points create a second NAT, so the
devices connected straight to router are in the &lt;code&gt;192.168.88.0/24&lt;/code&gt; range, the
ones connected over Wifi are in the &lt;code&gt;192.168.10.0/24&lt;/code&gt; range. I could probably
modify it to have a flat single range, but I just don&#39;t want to touch the
Audience&#39;s configuration.&lt;/p&gt;
&lt;p&gt;And le voila por favor, my Mikrotik router is now an NTP client, taking the time
from the Laboratory, as well as a local NTP time server, being able to share the
time with other machines.&lt;/p&gt;
&lt;p&gt;Now, time to set it on the clients. Right now, all of my devices run either
Debian or Kubuntu. My &lt;a href=&quot;https://stfn.pl/blog/79-tiny-gpu-workstation/&quot;&gt;desktop&lt;/a&gt;
is running Kubuntu 25.04, and there time is handled by the friendly named
&lt;code&gt;systemd-timesyncd&lt;/code&gt; service.&lt;/p&gt;
&lt;p&gt;Changing the time server from the default, Ubuntu&#39;s one to a different one is a
matter of changing the &lt;code&gt;/etc/systemd/timesyncd.conf&lt;/code&gt;. To add a custom server,
uncomment the line starting with &lt;code&gt;NTP&lt;/code&gt; and add the address of the server. Now just&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; systemctl restart  systemd-timesyncd.service&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And the new configuration can be confirmed by running&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;timedatectl timesync-status&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The response should show the new server being used.&lt;/p&gt;
&lt;p&gt;Now, on my Home Assistant box I am running Debian which uses the (I&#39;m guessing)
older &lt;code&gt;ntpd&lt;/code&gt; daemon to get the time, and here things got more interesting.&lt;/p&gt;
&lt;p&gt;On Debian, changing the time server requires modifying the
&lt;code&gt;/etc/ntpsec/ntp.conf&lt;/code&gt; file. I added &lt;code&gt;server 192.168.88.1&lt;/code&gt; above any other
&lt;code&gt;server&lt;/code&gt; or &lt;code&gt;pool&lt;/code&gt; line.&lt;/p&gt;
&lt;p&gt;Then it&#39;s just running&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; systemctl restart ntpd.service&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;and it should be fine.&lt;/p&gt;
&lt;p&gt;To confirm the change was done, run&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;ntpq &lt;span class=&quot;token parameter variable&quot;&gt;-p&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If there is no such program, install it with&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;apt&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; ntpstat&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For me the result of running &lt;code&gt;ntpq -p&lt;/code&gt; was&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;     remote                                   refid      st t when poll reach   delay   offset   jitter
&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 operator&quot;&gt;==&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 operator&quot;&gt;==&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 operator&quot;&gt;==&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 operator&quot;&gt;==&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 operator&quot;&gt;==&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 operator&quot;&gt;==&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 operator&quot;&gt;==&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 operator&quot;&gt;==&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 operator&quot;&gt;==&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 operator&quot;&gt;==&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 operator&quot;&gt;==&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 operator&quot;&gt;==&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 operator&quot;&gt;==&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 operator&quot;&gt;==&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 operator&quot;&gt;==&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 operator&quot;&gt;==&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 operator&quot;&gt;==&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;
*_gateway                                &lt;span class=&quot;token number&quot;&gt;194.146&lt;/span&gt;.251.100  &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt; u   &lt;span class=&quot;token number&quot;&gt;18&lt;/span&gt;   &lt;span class=&quot;token number&quot;&gt;64&lt;/span&gt;  &lt;span class=&quot;token number&quot;&gt;377&lt;/span&gt;   &lt;span class=&quot;token number&quot;&gt;0.2879&lt;/span&gt;   &lt;span class=&quot;token number&quot;&gt;0.0059&lt;/span&gt;   &lt;span class=&quot;token number&quot;&gt;0.1151&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;The mysterious &amp;quot;reach&amp;quot; number&lt;/h2&gt;
&lt;p&gt;Everything in the output of this command was relatively clear, expect for the
&lt;code&gt;reach&lt;/code&gt; part. It was changing in a seemingly random way, and the
&lt;a href=&quot;https://docs.ntpsec.org/latest/ntpq.html&quot;&gt;ntpq&lt;/a&gt; documentation was not clear on
the matter.&lt;/p&gt;
&lt;p&gt;Thankfully, I found &lt;a href=&quot;https://www.satsignal.eu/ntp/reach.html&quot;&gt;this site&lt;/a&gt; that explained how beautifully weird it is.&lt;/p&gt;
&lt;p&gt;The reach value describes eight bits of information, each being 0 or 1, but in
octal. That sequence is the history of connecting the server, and each of those
eight bits describes whether the connection was successful (1) or not (0). Those
eight bits are then converted to octal. 11111111 (eight good connections) is 377
in octal, and so 377 shows no problem with connectivity. A better and more
thorough explanation can be found on the linked site. Why did they do it? I
don&#39;t know, but I am sure that a group of very smart engineers have had very
good reasons to do so.&lt;/p&gt;
&lt;h2&gt;Bottom Line&lt;/h2&gt;
&lt;p&gt;Today I #TIL that&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Mikrotik hardware can work as an NTP server and client&lt;/li&gt;
&lt;li&gt;Poland has a Laboratory of Time and Frequency&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ntpq&lt;/code&gt; has some fascinating quirks&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Is this knowledge useful in any way? Not really, but does knowledge need to be
useful? I don&#39;t think so. I hope there will be many more such #TIL posts to
come.&lt;/p&gt;
&lt;p&gt;BTW, this is the first post that I wrote using only the &lt;code&gt;zed&lt;/code&gt; editor in &lt;code&gt;vim&lt;/code&gt;
mode. Using &lt;code&gt;vim&lt;/code&gt; keybindings is still often a pain, but I am persevering like a
Martian rover, and every new keybinding that I learn is a spark of joy.&lt;/p&gt;
&lt;p&gt;Thanks for reading!&lt;/p&gt;
</description>
      <pubDate>Mon, 08 Sep 2025 24:00:00 GMT</pubDate>
      <dc:creator>STFN</dc:creator>
      <guid>https://stfn.pl/blog/81/</guid>
    </item>
    <item>
      <title>Preparing for my future house; building a 2U NAS from scratch</title>
      <link>https://stfn.pl/blog/82-2u-rack-mounted-server/</link>
      <description>&lt;p&gt;As I already mentioned several times, I am in the process of building my future
house. One of the things I look to having the most, is a separate technical
room. It will be mostly for things like the heat pump and the electrical box,
but it will also have the space for a server rack. Which means I will be able to
move my whole homelab, both the networking and computers, into a single place.&lt;/p&gt;
&lt;p&gt;This also means that I can convert my servers to be rack mounted, which makes me
very excited, and feeling almost like an adult sysadmin.&lt;/p&gt;
&lt;p&gt;I prefer to work in small iterations, see what happens and decide what to do
next. That works better for me rather than making grandious plans from A to Z.
That&#39;s why I decided to first rackify (is that a word? Let&#39;s make it a word) my
small, backup server, see how it goes, what are the issues, etc, and later on,
with new experience, work on transferring my main NAS/server to a rack case.&lt;/p&gt;
&lt;p&gt;And I think it was a very good decision, based on what happened with this build.&lt;/p&gt;
&lt;h2&gt;The initial state&lt;/h2&gt;
&lt;p&gt;I built my the first generation of my backup server mostly from parts leftover
from earlier iterations of other computers.&lt;/p&gt;
&lt;p&gt;BTW, I still find it fascinating that I remember the times that there was the
Single Family Computer In The Computer Place, and now I have several different
compute devices in all shapes and sizes, and a box of surplus computers parts.
But I digress.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/77/backup.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/77/backup.jpg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;How it looked like at the beginning.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;The motherboard was some old Socket 1150 with a Haswell i5-4460, 8GB of RAM in
total. There was also a Mellanox X-3 10G SFP+ network card which I have not
used, but bought for future use. The boot drive was some random Silicon 128GB
SSD, and the main storage drive was a 3TB HGST 3.5&amp;quot; HDD. The PSU was some basic
500W Seasonic.&lt;/p&gt;
&lt;p&gt;I wrote about that server in more detail in my &lt;a href=&quot;https://stfn.pl/blog/77-state-of-homelab-in-june-2025/&quot;&gt;State of my homelab in June
2025&lt;/a&gt; blog post.&lt;/p&gt;
&lt;p&gt;Since the beginning, the aim of that server was what I like to call &amp;quot;lukewarm
storage&amp;quot;, the intermediate between cold and hot storage. Meaning I would start
it every few days or weeks, depending on how much changed on the main NAS, copy
the files and scrub the disks.&lt;/p&gt;
&lt;p&gt;This was the basis for moving to a rack case, and I thought it will be a quick
and simple move. Yeah.&lt;/p&gt;
&lt;h2&gt;Moving to a rack case, first try&lt;/h2&gt;
&lt;p&gt;For the rack case I went with a &lt;a href=&quot;https://netrack.store/en/server-cases/542-netrack-mini-itx-microatx-server-case-482888390mm-2u-19--5908268779100.html&quot;&gt;Netrack 2U 390mm
case&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I chose this one because it was cheap, and the description said that it can fit
an ATX PSU and a mATX motherboard, so I assumed that I can just move the
existing components from one case to another. It also has good cooling of the
drives, which is a must for me. The reviews were mixed, but I yolo&#39;d it, and made
the order.&lt;/p&gt;
&lt;p&gt;The case arrived, and looked sturdy, made from thick metal. The overall build
quality is okayish, there are some rough edges, but it&#39;s not meant to be a show
piece, it will live in the technical room.&lt;/p&gt;
&lt;p&gt;The first thing I did was to replace the two provided front fans. The ones that
came with the case were MOLEX fans without any PWM. I replaced them with a pair
of basic Arctic 80mm.&lt;/p&gt;
&lt;p&gt;Then came the time to assemble the rest of the component, and this is where
things became &lt;i&gt;very interesting&lt;/i&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/82/IMG_9018.JPG&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/82/IMG_9018.JPG&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;First iteration of the server fully installed.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;The first problem was that when using an mATX motherboard, there is space only
for two drives, as the bottom brackets of the disk caddies are too close to the
motherboard. This was the less important issue, as I had only two drives in total.&lt;/p&gt;
&lt;p&gt;The bigger problem was the PSU.&lt;/p&gt;
&lt;p&gt;Again, the description of the case that an ATX PSU would fit inside.&lt;/p&gt;
&lt;p&gt;And that was &lt;i&gt;technically correct&lt;/i&gt;. And well, the PSU did fit, but it was
flush with the full metal sides of the case, leaving absolutely no space for
ventilation, as can be seen in the photos.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/82/IMG_20250925_180127.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/82/IMG_20250925_180127.jpg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;PSU sitting flush with the case&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/82/IMG_20250925_180237.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/82/IMG_20250925_180237.jpg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;Enclosed from all sides.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;I installed and connected everything, and run the server with the case opened
and the PSU not screwed down. The server indeed run but it was not a reasonable
solution.&lt;/p&gt;
&lt;p&gt;And then I went for holidays.&lt;/p&gt;
&lt;h2&gt;Getting new parts&lt;/h2&gt;
&lt;p&gt;During the holidays, I made some research and found a solution to my problem.
Turns out Chieftec makes a PSU specifically for such a use case, with both inlet
and output holes on the smaller sides, allowing it to be placed in 2U enclosures.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.chieftec.eu/products-detail/179/SMART_SERIES&quot;&gt;Chieftec PSF-400B&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;It was relatively cheap, at 200PLN (~50EUR), and my only hope, so I ordered it
towards the end of me being away, so that it would arrive on the day I was back
at home.&lt;/p&gt;
&lt;p&gt;Also one rainy day I was a bit bored, and I was browsing Vinted, and I found a
very good deal on a AM4 motherboard with a Ryzen 2200G CPU and 16GB of RAM. The
motherboard looked smaller than that Socket 1150 I had, so I hoped I could fit
more drives into the case. The Ryzen was a G, meaning built-in graphics, so I
would not need a discrete GPU, just like with that Intel. And ZFS could for sure
use some more processing power and more RAM. So I ordered it as well.&lt;/p&gt;
&lt;p&gt;Another upside of moving to an AM4 board was that it had an M.2 slot, so I could
ditch the 2.5&amp;quot; boot SSD. For the boot drive I did another yolo gamble, and ordered a second
hand 32GB Intel Optane M.2 NVMe. I don&#39;t need a lot of space, this PC will be
strictly a storage server, no Docker, no services, so 32GB is plenty enough for
Ubuntu Server (I went with Ubuntu because of good ZFS support), and from what
I&#39;ve read, Optane has greater endurance in comparison to basic NVME SSDs.&lt;/p&gt;
&lt;p&gt;The final issue was cooling. The AMD stock cooler is simply too big to fit a 2U
case, so I went looking for solutions. And the solution was on Aliexpress, I
found there an &lt;a href=&quot;https://pl.aliexpress.com/item/1005002869966376.html?spm=a2g0o.order_list.order_list_main.5.610a1c24nFP2yt&amp;amp;gatewayAdapt=glo2pol&quot;&gt;AM4 server cooler designed specifically for 2U
cases&lt;/a&gt;.
It was 70PLN (~16EUR).&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/82/IMG_20250925_181229.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/82/IMG_20250925_181229.jpg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;AMD stock cooler goes out.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/82/IMG_20250925_181839.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/82/IMG_20250925_181839.jpg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;2U cooler goes in.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/82/IMG_20250925_181904.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/82/IMG_20250925_181904.jpg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;COOLSERVER!&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;What is very important is that it is made by the COOLSERVER company, and so
confirms that this build is a server. And it is cool.&lt;/p&gt;
&lt;h2&gt;Second try with new parts&lt;/h2&gt;
&lt;p&gt;The assembly was straightforward, I remove the previous parts, they will
probably be sold, and I installed the new ones.&lt;/p&gt;
&lt;p&gt;The PSU, well, let&#39;s just say it does raise much confidence with how it looks.
It was sold in &amp;quot;bulk case&amp;quot;, which meant I got in a random gray box wrapped in
bubble wrap, there weren&#39;t even screws provided with it. But exactly for such
cases I have my Drawer of Random Screws and Bolts&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/82/IMG_20250925_182202.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/82/IMG_20250925_182202.jpg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/82/IMG_20250925_180330.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/82/IMG_20250925_180330.jpg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/82/IMG_20250925_180400.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/82/IMG_20250925_180400.jpg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;As many other Chieftec products it looks like something made by a blacksmith,
and not PC equpiment, but then again, I&#39;ve never had a Chieftec product fail on
me. And again, this is not meant to be a looker, but a quiet worker.&lt;/p&gt;
&lt;p&gt;And quiet is not the word I would use about that cooler. With its tiny fan, I
knew it would be loud. I run it on my desk, connected to a 12V power source
through a chain of adapters, and wow, it was like a swarm of angry bees caught
in a tornado (that&#39;s why they are angry).&lt;/p&gt;
&lt;p&gt;The good news is that the new PSU fits and the cooling holes are right where
they need to be.&lt;/p&gt;
&lt;p&gt;The bad news is that even though the motherboard is indeed smaller, the way the
24 pin ATX power connector is placed, there is still only space for a single
3.5&amp;quot; HDD. I could buy 5.25 to 3.5&amp;quot; adapters and put the drives in the CD-ROM
bay, but they would not have any cooling there. I guess that if I want to put
more drives into this case, I will need to switch to a uITX board.&lt;/p&gt;
&lt;p&gt;The final touch is a low profile 10G networking card that I got for very cheap.
I will test once my Mikrotik CRS305 10G switch arrives.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/82/IMG_9022.JPG&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/82/IMG_9022.JPG&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;Second iteration done.&lt;/span&gt;&lt;/p&gt;
&lt;h2&gt;Setting up the server&lt;/h2&gt;
&lt;p&gt;I managed to do some cable management, connected the Internet, for now through
the 1G RJ-45 on the motherboard, keyboard, burned Ubuntu Server on a USB drive
(funny to say, &amp;quot;burn on a USB drive&amp;quot;, another leftover from the CD era), and
started the newly built server.&lt;/p&gt;
&lt;p&gt;And wow, even though the first thing I did was go to BIOS and change the fan
curve to &amp;quot;silent&amp;quot;, that CPU fan is awful. I am so glad this PC will be rarely
run, and in a separate room. To bear with it running on my desk, I had to use
ANC headphones. But it gets the job done, even under load the CPU temps would
not go over 45C.&lt;/p&gt;
&lt;p&gt;Apart from that, the rest was totally boring. I installed Ubuntu Server, set up
ZFS, imported the pool, installed Sanoid and Syncoid, configured the SSH keys,
made a short bash script for pull-style replication, and the config was done.&lt;/p&gt;
&lt;h2&gt;Bottom Line&lt;/h2&gt;
&lt;p&gt;I&#39;m glad I went with a secondary server first to gather experience. Some of the
issues I encountered here will not be a problem with moving the main NAS to a
rack mount, as for that one I plan to use a 4U case for the GPU to fit, so I
should be able to use a standard PSU.&lt;/p&gt;
&lt;p&gt;But now I know that I will need to buy a longer case so that there is space for
the drives and a normal motherboard.&lt;/p&gt;
&lt;p&gt;If everything goes right, I should move to my new house at the end of this year,
or the beginning of the new one.&lt;/p&gt;
&lt;p&gt;This has been the first episode of me preparing my homelab for the big move, and
I am sure there will be at least a few more before the highly anticipated event.&lt;/p&gt;
&lt;p&gt;Thanks for reading!&lt;/p&gt;
</description>
      <pubDate>Fri, 26 Sep 2025 24:00:00 GMT</pubDate>
      <dc:creator>STFN</dc:creator>
      <guid>https://stfn.pl/blog/82-2u-rack-mounted-server/</guid>
    </item>
    <item>
      <title>New, leaner network setup; removing NATs, and ditching Tailscale</title>
      <link>https://stfn.pl/blog/83-lean-network-removing-tailscale/</link>
      <description>&lt;p&gt;I&#39;ve read somewhere that Intel for many years has been doing a tick-tock model
in his development cycle. Thankfully, it had nothing to do with the Chinese
data-hoarding, addiction-inducing short video app, but rather with how they
improved their products. First was the tick, creating a new machining process,
and then the tock, expanding and perfecting it. More on the whole cycle can be
read on its &lt;a href=&quot;https://en.wikipedia.org/wiki/Tick%E2%80%93tock_model&quot;&gt;Wikipedia page&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I&#39;m writing about it because I feel I have been something similar with my homelab.
First is the tick, adding new things, new processes, new complexities, and after
a while comes the tock, removing what I find unneeded, cumbersome, things I do
not use, and attempting to simplify my setup as much as possible.&lt;/p&gt;
&lt;p&gt;This blog post is about the latest tock stage, one that made my homelab network
setup much, much leaner. It&#39;s also about how one decision leads to another, and
how even a small step opens new possibilities.&lt;/p&gt;
&lt;h2&gt;Before&lt;/h2&gt;
&lt;p&gt;The last tick stage left me with most of my self-hosted services being behind a &lt;i&gt;triple&lt;/i&gt; NAT.&lt;/p&gt;
&lt;p&gt;The first level is my Mikrotik RB3011 router that is the entrypoint to my flat&#39;s
network. I don&#39;t use an ISP provided router/modem, because the ISP provides me
with Internet access via a twisted pair cable coming through a wall straight into the flat, and
with auth being done by PPPoE.&lt;/p&gt;
&lt;p&gt;The second level of NAT is my WiFi mesh/bridge setup which I described in &lt;a href=&quot;https://stfn.pl/blog/70/&quot;&gt;this
blog post&lt;/a&gt;. I know there are ways to make a WiFi access point not do NAT, but
this is a mesh setup, and a) it is absolutely mission-critical for me and my
wife to do work from home, and b) it will only last until we move to the house,
in the house there&#39;s going to be a single ceiling mounting AP. I already made
plans with the electrician to leave one twisted pair hanging from a hole in the
ceiling, and I&#39;ll mount a Mikrotik cAP ax and power it over PoE. So for the time
being, the bridge stays.&lt;/p&gt;
&lt;p&gt;The third level of NAT was my LXD setup with the default bridge network. LXD by
default puts all running containers and VMs on its own bridge that only the host
can access. I exposed the services using a static route, as shown in &lt;a href=&quot;https://www.youtube.com/watch?v=TmGvbXfwJEA&quot;&gt;this
video&lt;/a&gt;, but that still left me with
another level of network obfuscation.&lt;/p&gt;
&lt;p&gt;To access those services remotely I was using Tailscale. I installed Tailscale
both on the host to reach the non-lxced services, and also on every container to
be able to also reach them. While it work, it felt... incorrect. And the number
of Tailscale clients grew with every new container, each one of them requiring
installation and configuration.&lt;/p&gt;
&lt;h2&gt;Removing These NATs&lt;/h2&gt;
&lt;p&gt;The first change happened because of our upcoming move to a new house. As we are
getting ready for it, the living room has been slowly turning into a place for
storing things we already bought for the new place, like a set of garden
furniture we found on a very good sale. The living room is also where the router
is located, because there the ISP cable comes from the wall. And so I thought,
with that room already becoming basically storage, moving my homelab there won&#39;t
make much of a difference. So I moved my main server/NAS there, and now the
server is connected straight to the RB3011, removing one layer of NAT. Dumping
the WiFi part means also the Internet speeds jumped a lot. And I have more space
under my desk.&lt;/p&gt;
&lt;p&gt;The second change was with my LXD setup. Yes, I know, I will move to Incus, but
not yet, I have it on my list. I recently learned about the MACVLAN network type
in LXD, which puts the containers on the same DHCP address pool as the host,
basically removing the NAT. The downside is that the containers cannot talk to
the host, but in most cases I can live with that, they do not need to so. With
the exception of the one running my monitoring stack, Grafana and InfluxDB,
which needs to get metrics from the host, so this was stays on the default
network.&lt;/p&gt;
&lt;p&gt;The rest of the containers/service I moved to MACVLAN, so that my Forgejo, or
Nextcloud are right in the same LAN namespace as the other machines. Setting up MACVLAN is very well described
[in this blog post(https://blog.simos.info/how-to-make-your-lxd-container-get-ip-addresses-from-your-lan/).&lt;/p&gt;
&lt;p&gt;This means that right now I have a single, flat, LAN network namespace. And that
opened new possibilities.&lt;/p&gt;
&lt;h2&gt;Removing Tailscale&lt;/h2&gt;
&lt;p&gt;In the beginning of the post I mentioned how I have been using Tailscale to
punch through all those levels of network address translation. But now that
problem was gone. And the other thing is with &lt;a href=&quot;https://stfn.pl/blog/80-solar-website-with-custom-backend/&quot;&gt;my solar
webserver&lt;/a&gt;, I have
renewed interest in Wireguard. I really like how clean it is, and how it can be
configured in the command line, or in automation tools like Ansible (soon there
will be a post on Ansible!). And finally, it can be configured straight on a
Mikrotik router, so no need to install it on all clients and containers. Truly a
great piece of technology.&lt;/p&gt;
&lt;p&gt;So I decided to move from Tailscale to Wireguard.&lt;/p&gt;
&lt;p&gt;This is not the post to talk about my Wireguard setup in detail, so in short:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Btw, one of the problems I always have when writing blog posts is to decide
how deep I want to go in talking about my actions, provide the whole
configuration process, or just talk briefly about the ideas.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;I already have a VPS with Wireguard, the one handling my solar website. This is
the publicly reachable hub that all my WG clients can talk to and know how to
reach it.&lt;/p&gt;
&lt;p&gt;Then, I have Wireguard configured on my Mikrotik RB3011 router, so that every
machine in the LAN can talk to that VPS via Wireguard. And vice Versailles, the
VPS can reach any machine in the LAN, thanks to that flat network topology I
achieved in the previous step.&lt;/p&gt;
&lt;p&gt;Finally, I installed WG clients on my phone and my laptop, the only machine I
take away from my home LAN. This allows me to reach my local services from any
place through the secure connection of the VPN. And finally 2, that VPS works as
a relay, forwarding traffic between different clients.&lt;/p&gt;
&lt;p&gt;And to quote the great American poet, &lt;a href=&quot;https://www.youtube.com/watch?v=9KBXcpJfmj4&quot;&gt;Billy
Mays&lt;/a&gt;, wait, there&#39;s more! With
Wireguard, the LAN clients keep their local IP addresses even when I talk to
them through the VPN from away, so I don&#39;t have anymore that issue that I had
with Tailscale, to keep two different set of bookmarks for my services, one with
the internal address, and one with the Tailnet hostnames.&lt;/p&gt;
&lt;h2&gt;Another step in Degoogling&lt;/h2&gt;
&lt;p&gt;I removed the Tailscale clients from all of my devices and closed the account.
Which also means that I can close my secondary GMail account that I have been
keeping only because it was how I logged into Tailscale. I know you can login to
Tailscale with a custom self-hosted OIDC service, but I never came around to do
it. Anyway, one less Google service for me. I will have another blog post on my
process of Degoogling, I would say I am something like 90% done overall, but as
always, the last 10 percent are the hardest.&lt;/p&gt;
&lt;h2&gt;Bottom line&lt;/h2&gt;
&lt;p&gt;This post has been, as I feel it, about how small decisions in the homelab can
lead to larger changes, and how everything is in a way connected to each other.&lt;/p&gt;
&lt;p&gt;I am very happy with my new, much leaner, networking setup, and in a way, for me
it is another step of preparation for my new house. I can&#39;t wait to also get rid
of that WiFi bridge, it has been frustrating me in more than one way.&lt;/p&gt;
&lt;h2&gt;Bottom line 2&lt;/h2&gt;
&lt;p&gt;There is another one thing I need to work out now. Tailscale has been the source
of TLS certificates for my Nextcloud (I wrote about it
&lt;a href=&quot;https://stfn.pl/blog/68-tailscale-tls-with-nextcloud/&quot;&gt;here&lt;/a&gt;), and with
removing it I lost that functionality. But the great people of the Fediverse
already suggested possible solutions, and I am very thankful! I will work on it
somewhere in the near future when I feel like tackling this issue from the list
of all the other issues with my homelab :) &lt;a href=&quot;https://fedi.stfn.pl/@stfn/statuses/01K6E49MHG2KS94M2N3JZAFJ7G&quot;&gt;Here is the Fedi
thread&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Thanks for reading!&lt;/p&gt;
</description>
      <pubDate>Fri, 03 Oct 2025 24:00:00 GMT</pubDate>
      <dc:creator>STFN</dc:creator>
      <guid>https://stfn.pl/blog/83-lean-network-removing-tailscale/</guid>
    </item>
    <item>
      <title>My self-hosted music streaming solution</title>
      <link>https://stfn.pl/blog/84-self-hosted-music-streaming/</link>
      <description>&lt;p&gt;&lt;a href=&quot;https://stfn.pl/blog/84-self-hosted-music-streaming/#the_interesting_part&quot;&gt;To skip the boring memories and go straight to the technical part.&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I am a millenial, born at the end of the 80s, and my listening-to-music history
is probably rather typical of my generation in the so-called &amp;quot;western world&amp;quot;.&lt;/p&gt;
&lt;p&gt;First I listened to music from cassettes, you know, those rectanglish things you
could rewind with a pencil. I still remember that the first cassette I bought
with my own money was Americana by The Offspring*. Later on came the audio CDs
and the Discman that would chew through the batteries painfully fast. But this
blog post is not about the physical media, it&#39;s about listening to music on a
computer.&lt;/p&gt;
&lt;p&gt;&lt;small&gt;*The Kids Aren&#39;t Alright hits very different now&lt;/small&gt;&lt;/p&gt;
&lt;p&gt;My first source of music in the form of digital files, something like 20 years
ago, was the world of p2p file sharing. Everyone around had Kazaa, BearShare,
Limewire. We would download music not by album, but by single files, burn them
on CDs and share between ourselves. From time to time someone would download a
banger such as &lt;code&gt;linkin_park-one_step_closer.mp3.exe&lt;/code&gt; and brick the family
computer, but that was an inherent part of the Internet experience at the time.
People would amass collections of thousand of mp3 files, barely fitting on the
20GB hard drives of the times. Winamp was an indispensable software, and
downloading new skins for it a fun activity.&lt;/p&gt;
&lt;p&gt;Years passed, I went to university, started my first real work, and then came
the time of music streaming services. My first one was Deezer, later I moved to
Tidal because I got a free year with my phone plan, and finally I landed on
Spotify, mostly because of the network effect. And using them at the beginning
was exciting, it felt both like the future and something proper adults should
do. I was earning my own money and now I was spending it on music, funding the
artists. I did not have to store music in files, I could just stream it to
anywhere!&lt;/p&gt;
&lt;p&gt;And now we are where we are. With the increasing enshittification
&lt;a href=&quot;https://en.wikipedia.org/wiki/Criticism_of_Spotify&quot;&gt;(1)&lt;/a&gt;
&lt;a href=&quot;https://www.theguardian.com/music/2025/jul/31/spotify-musicians-david-bridie-ntwnfb&quot;&gt;(2)&lt;/a&gt;
&lt;a href=&quot;https://en.wikipedia.org/wiki/Controversy_over_fake_artists_on_Spotify&quot;&gt;(3)&lt;/a&gt; of
music streaming services, and my move to more local, private software solutions,
using Spotify or Deezer or Youtube Music has lost its appeal. I just want to buy
music and have it on my storage on my own terms, and give money to artists that
make it.&lt;/p&gt;
&lt;p&gt;I ended my Spotify subscription months ago. I buy albums from Bandcamp (which I
know has its own share of issues) or from other places where the artists share
their music, I am again buying physical media. Once again I have music in the
form of files on my hard drive.&lt;/p&gt;
&lt;p&gt;And as I am a homelabber and selfhoster, I did not want to just copy files from
my NAS to my desktop, laptop and phone, I wanted a solution to access my music
wherever I am.&lt;/p&gt;
&lt;p&gt;&lt;span id=&quot;the_interesting_part&quot; class=&quot;heading-2&quot;&gt;Streaming music from a NAS&lt;/span&gt;&lt;/p&gt;
&lt;h3&gt;The server&lt;/h3&gt;
&lt;p&gt;I am storing the mp3 files on my server at home. The files live in Nextcloud
which is running in an LXD container and uses my mirrored ZFS pool as storage. I
can access Nextcloud from anywhere using WireGuard VPN. Before I switched to
WireGuard, I had been using Tailscale to have access to my private data. I
already talked about those topics in previous blog posts:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn.pl/blog/67-deploying-nextcloud-locally-with-lxd/&quot;&gt;Deploying Nextcloud locally with LXD&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn.pl/blog/68-tailscale-tls-with-nextcloud/&quot;&gt;Access local Nextcloud with HTTPS anywhere by using Tailscale TLS certificates&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn.pl/blog/83-lean-network-removing-tailscale/&quot;&gt;New, leaner network setup: removing NATs, and ditching Tailscale&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;So now I have access to my private files even when I am away from home, but how
to actually listen to them?&lt;/p&gt;
&lt;p&gt;Nextcloud has an app called simply &amp;quot;Music&amp;quot;, which allows listening to music via
the Nextcloud Web UI. Installing it is easy, you just need to have an account
with administrative rights, and find the app in the app store built-in into
Nextcloud. Later it&#39;s only a matter of pointing the app to the folder when you
are storing the music, and le voila, files get loaded, and can be played from
the browser.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/84/Screenshot_20251018_162836.png&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/84/Screenshot_20251018_162836.png&quot; alt=&quot;A cropped screenshot showing a part of a browser window. The URL bar is
visible, showing an internal address starting with http://192.168.88.248.
Beneath that the is a menu on the left, and on the right there&#39;s information
about an album &#39;Inside Jokes&#39; by Midwest Pen Pals&amp;quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;The Nextcloud Music app&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;The downsides are you need a browser to play then this way, and the app has no
means to edit mp3 tags or scrobble to Last.fm. &lt;a href=&quot;https://www.last.fm/user/S__M&quot;&gt;Yes, I still use Last.fm&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;My last.fm account is so old, I created it before even coming up with &lt;code&gt;stfn&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;But the Music app has a trick up its sleeve. It can work as a server for any
apps that use either the Ampache or Subsonic protocols. Auth is being done via
generated API credentials.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/84/Screenshot_20251018_162908.png&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/84/Screenshot_20251018_162908.png&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;Nextcloud Music settings with information how to configure the music clients&lt;/span&gt;&lt;/p&gt;
&lt;h3&gt;The clients&lt;/h3&gt;
&lt;p&gt;For listening to music on other computers I use
&lt;a href=&quot;https://www.strawberrymusicplayer.org/&quot;&gt;Strawberry&lt;/a&gt;. Strawberry is a fork of an
older music player called Clementine (Oh the FOSS naming), and has support for
the Subsonic protocol, and supports scrobbling to several services. But it
cannot edit tags of external files.&lt;/p&gt;
&lt;p&gt;On my Android (fair)phone I use &lt;a href=&quot;https://f-droid.org/packages/luci.sixsixsix.powerampache2.fdroid/&quot;&gt;Power Ampache
2&lt;/a&gt;. Sadly, it
does not support scroblling, but apart from that works just fine. It allows
downloading songs to the phone for playing them when offline.&lt;/p&gt;
&lt;h3&gt;Editing mp3 tags in Linux&lt;/h3&gt;
&lt;p&gt;As I mentioned before, neither Nextcloud Music nor Strawberry allow editing mp3
tags, and that is something so very much needed from time to time.&lt;/p&gt;
&lt;p&gt;I&#39;m handling that problem in two ways. For small edits I access the terminal in
the LXD container and edit the files using the command line tool
&lt;a href=&quot;https://sourceforge.net/projects/id3v2/&quot;&gt;id3v2&lt;/a&gt;. Not the simplest way, but it&#39;s
fast for single files.&lt;/p&gt;
&lt;p&gt;For larger edits, I use &lt;a href=&quot;https://wiki.gnome.org/Apps/EasyTAG&quot;&gt;EasyTAG&lt;/a&gt;, but that
requires downloading the music files to my personal computer, editing them
locally, and reuploading to Nextcloud.&lt;/p&gt;
&lt;h2&gt;The bottom line&lt;/h2&gt;
&lt;p&gt;And that&#39;s it, this is how I stream music these days. One less reliance on large
corporations who are never your friends, and one more usecase for my homelab.&lt;/p&gt;
&lt;p&gt;And I&#39;m a nostalgic person, and once again having audio files makes me feel like
going back to those old days when I would burn CDs full of Nightwish, Metallica
and Evanescence to listen to on my way to high school. Wake me up, wake me up
inside.&lt;/p&gt;
&lt;p&gt;PS. I just checked and the &lt;a href=&quot;https://www.hetzner.com/storage/storage-share/&quot;&gt;Managed
Nextcloud&lt;/a&gt; service offered by
Hetzner supports the Music app, so it can be used instead of a NAS for people
who don&#39;t want to or cannot have file storing hardware at home. Hetzner is not
paying me for this recommendation, actually I am paying them to host all my
public-facing stuff, and I think they are a company worth recommending :)&lt;/p&gt;
&lt;p&gt;Thanks for reading!&lt;/p&gt;
</description>
      <pubDate>Sat, 18 Oct 2025 24:00:00 GMT</pubDate>
      <dc:creator>STFN</dc:creator>
      <guid>https://stfn.pl/blog/84-self-hosted-music-streaming/</guid>
    </item>
    <item>
      <title>I bought a Pi which is not of the Raspberry kind</title>
      <link>https://stfn.pl/blog/85-orange-pi-zero-3-first-impressions/</link>
      <description>&lt;p&gt;I&#39;ve been spending too much time on online marketplaces for secondhand items.
Well, at least when I actually buy something from those sites, it&#39;s already
used, so I&#39;m not pumping mindless consumption &lt;i&gt;that much&lt;/i&gt;.&lt;/p&gt;
&lt;p&gt;Anyway, the result of one of those crawls is this tiny device: An Orange Pi Zero
3.&lt;/p&gt;
&lt;p&gt;I bought it for ~100PLN (25EUR) with shipping, and the seller included a WiFi
antenna and a radiator covering the whole board for passive cooling. From what
I&#39;ve seen, a new one can be bought for around twice that price.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/85/IMG_20251025_130408.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/85/IMG_20251025_130408.jpg&quot; alt=&quot;A red and yellow apple and a small single board computer placed on
cardboard.&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;The Orange Pi Zero 3 with an Apple for
scale&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;The whole spec can be found on the &lt;a href=&quot;http://www.orangepi.org/html/hardWare/computerAndMicrocontrollers/details/Orange-Pi-Zero-3.html&quot;&gt;product&#39;s
website&lt;/a&gt;.
In short, it has four ARM cores, and my version comes with 4GB of RAM. So more
or less a mid version of the Raspberry Pi 4. The board is physically around half
the size of the Pi 4, has an Ethernet port, a single USB-A 2.0, USB-C for
powering, and a micro HDMI port, the same as the Pi 4, so that I could use the
dongle I already have.&lt;/p&gt;
&lt;h2&gt;Software&lt;/h2&gt;
&lt;p&gt;For the OS I went with Armbian.
&lt;a href=&quot;https://www.armbian.com/orange-pi-zero-3/&quot;&gt;Armbian&lt;/a&gt; has a community-maintained
version dedicated to this specific board in both Desktop and Server flavours.&lt;/p&gt;
&lt;p&gt;I prefer my SBCs as the French prefer their kings, headless, so I went with the
server version. I installed it to a 64GB micro SD card using the Raspberry Pi
Imager app, connected the Ethernet cable (I don&#39;t plan to use WiFi on it), power
and waited for it to boot.&lt;/p&gt;
&lt;p&gt;After booting I logged in with the default Armbian user and password
(root;1234), and hey, I got a mostly standard Debian environment.&lt;/p&gt;
&lt;p&gt;And what can I say, Debian is Debian, you get &lt;code&gt;apt&lt;/code&gt; to install stuff, and things
mostly just work. Armbian also provides a terminal tool &lt;code&gt;armbian-config&lt;/code&gt; that you can
use to configure some hardware things on the board like the WiFi, SPI, I2C and
also install software.&lt;/p&gt;
&lt;p&gt;I have not run any benchmarks, so I don&#39;t have the numbers, but from what I&#39;ve
been using the Orange Pi so far with typical terminal tasks like updating and
installing software, it seems to be a bit slower than a Raspberry Pi 4b, but
much, much better than a RPi Zero. I&#39;ve never had any other Pis so I can&#39;t
compare it for example Raspberry Pi 3.&lt;/p&gt;
&lt;h2&gt;Power Consumption&lt;/h2&gt;
&lt;p&gt;Because of my planned use-case (more on that in a moment), I was very interested
in how much power does the board use.&lt;/p&gt;
&lt;p&gt;According to my testing, the Orange Pi Zero uses around 0.24A (1.2W) at idle,
which is significantly higher than a Raspberry Pi Zero (less than ~0.1A), but
about half of what a Pi 4b uses (the one and only Jeff Geerling wrote an
&lt;a href=&quot;https://www.pidramble.com/wiki/benchmarks/power-consumption&quot;&gt;article about the Pi 4b power
usage&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/85/IMG_20251011_212854.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/85/IMG_20251011_212854.jpg&quot; alt=&quot;A photo showing a USB power meter. The statistics on the meter&#39;s LCD screen
say: 4.96V, 0.23A, 2794mAh, channel 1.&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;Power
statistics after 12 hours of running.&lt;/span&gt;&lt;/p&gt;
&lt;h2&gt;What I will use it for?&lt;/h2&gt;
&lt;p&gt;I&#39;m thinking of using it instead of the Raspberry Pi Zero as my solar-powered
webserver about which I wrote in detail in &lt;a href=&quot;https://stfn.pl/blog/80-solar-website-with-custom-backend/&quot;&gt;this blog
post&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I&#39;m planning to expand my solar webhosting with more websites, and having more
compute power, and eight times the RAM is always nice. The question is whether
the batteries will be able to handle the increased load, especially as winter
approaches. If not, I will consider buying new, larger ones, the solar panel
itself is already big enough (330Wp). But that is a bridge that I will consider
burning when I come to it.&lt;/p&gt;
&lt;h2&gt;Issues so far&lt;/h2&gt;
&lt;p&gt;Please bear in mind that I am a total noob when it comes to SPI/I2C protocols,
so probably there is a super simple solution to what I&#39;m talking about below, I
just don&#39;t know it yet.&lt;/p&gt;
&lt;p&gt;The first issue that I came across so far is that the GPIO connectivity seems to
be non-standard.&lt;/p&gt;
&lt;p&gt;I wanted to connect a BME280 temperature/humidity/pressure sensor to the board,
as I am connecting one to the Raspberry Pi Zero. On the RPi it&#39;s super simple,
VCC to VCC, GND to GND, SCL to SCL and SDA to SDA. But the Orange Pi does not
have SCL pins, just SCK. I tried connecting SCL to SCK, and using different sets
of SPI/I2C pins, but it would not work. Also the armbian-config tab with the
I2C/SPI configuration is rather vague.&lt;/p&gt;
&lt;p&gt;Of course I&#39;m not saying it&#39;s a serious problem, I&#39;m guessing there is a
workaround for it. What I want to say is that for the Raspberry Pi Zero it is a
trivial thing to connect a sensor, and for the Orange Pi it seems not to be so.&lt;/p&gt;
&lt;p&gt;The second issue that I encountered was when I tried to install the ADS-B stack,
&lt;code&gt;dump1090-fa&lt;/code&gt; and PiAware to see if I can use the Orange Pi as an ADS-B receiver
and send data to ADS-B sites like &lt;a href=&quot;https://flightaware.com&quot;&gt;flightaware.com&lt;/a&gt;.
The package aimed at Raspberry Pi would not want to install on the Orange Pi.
Again, I am sure that there is a workaround for this, but the sheer fact that
one needs to look for workaround puts the Orange Pi in a lost position.&lt;/p&gt;
&lt;h2&gt;Bottom Line&lt;/h2&gt;
&lt;p&gt;Those are my initial impressions about this SBC. So far I am positively
impressed with its speed for the size and the price that I paid.&lt;/p&gt;
&lt;p&gt;In one of the &lt;a href=&quot;https://latenightlinux.com/&quot;&gt;Late Night Linux&lt;/a&gt; podcast episodes,
the hosts discussed why would anyone buy a Raspberry Pi 4, when there are
similarly priced or even cheaper small computers. One of the arguments that was
raised was that a Raspberry Pi is a known standard, the Raspberry Pi OS is the
same for every Pi, it is a well known product and has a lot of support for
peripherals out of the box. Software is written specifically for the Raspberry
Pi, there is no need to reinvent the wheel, and that is why the Raspberry Pi is,
for example, so useful in teaching environments.&lt;/p&gt;
&lt;p&gt;And that&#39;s the main issue that I see with the Orange Pi Zero 3. It is a niche SBC
running a niche OS (the Orange Pi provides it&#39;s own OS images as well, but I
have not tested them), and there are things that will just not run out of the
box as with the Raspberry.&lt;/p&gt;
&lt;p&gt;So, in short, if you want an SBC that Just Works and has a ton of online
support, get a Raspberry Pi. If you want to dig more into Linux on Arm, or just
use the most standard stuff like nginx, the Orange Pi will be just fine with its
great price to performance ratio.&lt;/p&gt;
&lt;p&gt;As I play with it more, I will for sure have more things to say, but for now,
that&#39;s it, thanks for reading!&lt;/p&gt;
</description>
      <pubDate>Sat, 25 Oct 2025 24:00:00 GMT</pubDate>
      <dc:creator>STFN</dc:creator>
      <guid>https://stfn.pl/blog/85-orange-pi-zero-3-first-impressions/</guid>
    </item>
    <item>
      <title>My GoToSocial instance is now one year old</title>
      <link>https://stfn.pl/blog/86-fedi-instance-first-anniversary/</link>
      <description>&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/86/header.png&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/86/header.png&quot; alt=&quot;fedi.stfn.pl, home to 9 users who have written 6500 posts; knows of 13411
other instances&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I don&#39;t remember the exact date when my GoToSocial instance was launched, but I
am quite sure it was somewhere around late November of 2024. Which means that my
very own server in the Fediverse is one year old. And on that occasion I would
like to do some sort of an introspection of where I am and how did I reach this
point.&lt;/p&gt;
&lt;h2&gt;Origins&lt;/h2&gt;
&lt;p&gt;The idea to host my own instance in the Fediverse came to me for the first time
when the admin of the late botsin.space server announced that he will be closing
the server due to lack of time. Botsin.space was an instance dedicated to
running bots, and there I was hosting my first Fediverse bot, the Astrobin Image
of the Day.&lt;/p&gt;
&lt;p&gt;The admin was nice to inform about the closure well in advance, so I had some
time to check my options. From the ones available, GoToSocial seemed the easiest
to deploy, and most stable to run, with a good and active development process.
So I went this route.&lt;/p&gt;
&lt;p&gt;I am used to self hosting services, so for my the whole setup process was a
breeze. For the instance, I rented a new VPS from RackNerd, the provider I was
using at the time.&lt;/p&gt;
&lt;p&gt;Once the instance was up and running, I also configured backups using Borg, and monitoring using Prometheus.&lt;/p&gt;
&lt;p&gt;I won&#39;t be diving into the details of configuration here, because I already wrote about those topics in depth in previous posts:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn.pl/blog/53-starting-gotosocial-instance&quot;&gt;How I started my GoToSocial instance in the Fediverse&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn.pl/blog/54-administrating-gotosocial-instance&quot;&gt;Administrating my GoToSocial instance - monitoring and backup&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Migrations&lt;/h2&gt;
&lt;p&gt;Time went by, and my instance was humming long quietly, hosting my bot and not
doing much else. I had a &amp;quot;personal&amp;quot; account there with the obvious name of
&lt;code&gt;stfn&lt;/code&gt;, but I was not using it because I already had other such accounts
elsewhere.&lt;/p&gt;
&lt;p&gt;And then came the series of controversies with Fosstodon, the instance where I
had my main account then. Tired of being on such an instance, I decided it&#39;s
time to my stuff somewhere else, and there was an abvious choice where that
&amp;quot;somewhere else&amp;quot; could be.&lt;/p&gt;
&lt;p&gt;The ability to move accounts between instances together with followers and other
lists is an awesome part of the Fediverse, and this was another time when it
came handy.&lt;/p&gt;
&lt;p&gt;I moved my main account to my instance, and it&#39;s still there, you can find me at
&lt;a href=&quot;https://fedi.stfn.pl/@stfn&quot;&gt;fedi.stfn.pl/@stfn&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Around that time I also decided I don&#39;t want to rent VPSes and store my data in
the US, because, well, let&#39;s just say I do not trust that country when it comes
to privacy and my rights as a consumer. Also I prefer to spend my money within the
EU, where it benefits both me and my neighbours.&lt;/p&gt;
&lt;p&gt;That is why I decided to move my instance from RackNerd&#39;s VPS to Hetzner in
Germany. That was much more involved and stressful than moving accounts between
instances, but turned out fine in the end.&lt;/p&gt;
&lt;p&gt;I wrote about this period of hosting my instance in the blog post:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn.pl/blog/69-migrating-servers-migrating-instances/&quot;&gt;Migrating servers, leaving Fosstodon, moving to my own GoToSocial&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;The more the merrier&lt;/h2&gt;
&lt;p&gt;One of the great things about GoToSocial is its API, allowing to send toots in
basically a single HTTP POST call with an auth token. And as it is my own instance, I can do with
it whatever I want and not be concerned with using up someone&#39;s else
resources. And so my little herd of bots started growing and now consists of:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://fedi.stfn.pl/@astrobin_iotd&quot;&gt;Astrobin Image of the Day&lt;/a&gt; - showing the Image of the Day on a astrophotography
sharing site&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://fedi.stfn.pl/@stacjadnia&quot;&gt;Stacja Dnia&lt;/a&gt; - showing each day a random railway station in Poland&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://fedi.stfn.pl/@dzien&quot;&gt;Dzień&lt;/a&gt; - statistics about the length of the day and night in the geographic center of Poland&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://fedi.stfn.pl/@naszpapa&quot;&gt;Nasz Papa&lt;/a&gt; - A bot to remind us of the great Pope John Paul II, and his great input in the
Polish meme culture&lt;/p&gt;
&lt;p&gt;Also two of my real life friends were kind enough to join me on my instance, and I am very happy to offer them my services:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://fedi.stfn.pl/@panga&quot;&gt;Panga&lt;/a&gt;
&lt;a href=&quot;https://fedi.stfn.pl/@wodoodporny&quot;&gt;Wodoodporny&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Current times&lt;/h2&gt;
&lt;p&gt;And so here we are today. My instance is still running strong, having survived multiple
GTS updates with database migrations. Every night a Borg backup is being done,
and before every update of the GTS app, or the server software, I stop the
server and do a snapshot in case something goes wrong.&lt;/p&gt;
&lt;p&gt;The instance is running, along with my blog and some other minor services, on a
Hetzner CX33 VPS with four vCPUs and 8GB of RAM. It&#39;s 80GB disk in around 70%
full, RAM usage is 30%, and the load rarely goes over 1, usually when I publish
a link to a new blog post on my fedi account, and all the other servers try to
pull the preview.&lt;/p&gt;
&lt;p&gt;During this year I&#39;ve learnt a lof about managing software and servers along the
way, and I am sure most of the things I do could be done much better. But hey,
not everything at once. Rome was not built in a day (and then came the Nero
Burning Rom).&lt;/p&gt;
&lt;h2&gt;Future plans&lt;/h2&gt;
&lt;p&gt;I don&#39;t have any specific plans for the future. It would be great if more of my
friends would like to join me on it. I think that around 10 accounts of fleshy
people would be the reasonable maximum of what I am willing to handle.&lt;/p&gt;
&lt;p&gt;Looking at Prometheus, the disk storage seems to be the first metric to run out,
so I might consider offloading the media to object storage, which will be an
interesting challenge on its own. I would also like to add more automation and
configuration tools to the tech stack like Opentofu. But those are plans for the
future. For now, my instance in the Fediverse has its first anniversary and I
hope there will be much more to come.&lt;/p&gt;
&lt;p&gt;Thanks for reading!&lt;/p&gt;
</description>
      <pubDate>Sun, 23 Nov 2025 24:00:00 GMT</pubDate>
      <dc:creator>STFN</dc:creator>
      <guid>https://stfn.pl/blog/86-fedi-instance-first-anniversary/</guid>
    </item>
    <item>
      <title>Pyriodic Backend, The Backend for the Small Web, is published on PyPi</title>
      <link>https://stfn.pl/blog/87-pyriodic-backend-pypi/</link>
      <description>&lt;p&gt;I am very happy to inform that my Python project, Pyriodic Backend, has been
published to PyPi, and is for anyone to use with the code available as open source.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://codeberg.org/stfn/pyriodic-backend&quot;&gt;Pyriodic Backend on Codeberg&lt;/a&gt;
&lt;a href=&quot;https://pypi.org/project/pyriodic-backend&quot;&gt;Pyriodic Backend on PyPi&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Pyriodic What?&lt;/h2&gt;
&lt;p&gt;Pyriodic Backend is my idea to create the simplest possible &amp;quot;backend&amp;quot; service
for updating static HTML websites with absolutely minimum resources. With
Pyriodic Backend, the websites can be updated with changing information, for
example the temperature outside, the weather, the CPU load, the battery level.
The use-case is static websites running on Raspberry Pi Zeros, old phones,
e-waste-level hardware. The interval between the updates of particular tags can
be defined in increments of one minute, with one minute being the default.&lt;/p&gt;
&lt;p&gt;To run PB on the server, the only requirements are &lt;code&gt;python3&lt;/code&gt; and &lt;code&gt;cron&lt;/code&gt;. Pyriodic
Backend is able to update purely HTML websites, no JS or any other magic
required, just tags with ids.&lt;/p&gt;
&lt;p&gt;As an example, I am using it to update my &lt;a href=&quot;https://solar.stfn.pl&quot;&gt;solar-powered
website&lt;/a&gt; with temperature and CPU information.&lt;/p&gt;
&lt;p&gt;(As it is basically winter right now where I live, with long and cold nights,
the site is usually down during the night).&lt;/p&gt;
&lt;p&gt;The installation and setup instructions are all in the README file in the repo
and the PyPi page, and I hope they are sufficient to get you started.&lt;/p&gt;
&lt;h2&gt;Publishing to PyPi&lt;/h2&gt;
&lt;p&gt;The code is open source under the GPLv3 license, and hosted at
&lt;a href=&quot;https://codeberg.org&quot;&gt;Codeberg.org&lt;/a&gt;. For now the deployment to PyPi is being
done manually, but I am considering using Woodpecker CI to put it into Actions.&lt;/p&gt;
&lt;p&gt;As with my previous PyPi project,
&lt;a href=&quot;https://pypi.org/project/zambretti-py/&quot;&gt;zambretti-py&lt;/a&gt;, I have been using &lt;a href=&quot;https://packaging.python.org/en/latest/tutorials/packaging-projects/&quot;&gt;this
tutorial&lt;/a&gt;
to go through the deployment process. Everything went smoothly, even smoother
than for the first time.&lt;/p&gt;
&lt;h2&gt;Future Roadmap and contributions&lt;/h2&gt;
&lt;p&gt;The future roadmap for Pyriodic Backend for now includes adding more possible
ways to track the function execution, I am thinking of adding Redis or other DB
support. But I don&#39;t want to overload the project with too many features, it&#39;s
meant to be simple and functional.&lt;/p&gt;
&lt;p&gt;I am very much open to any feedback, suggestions for improvement, or code
contributions. Create an issue in Codeberg or send me an email :)&lt;/p&gt;
&lt;h2&gt;The Bottom Line&lt;/h2&gt;
&lt;p&gt;With Pyriodic Backend I want to contribute to the Small Web that I like so much
and try to be an active member of. I hope someone will find it useful and will
add it to their webpage. If you do, please send me a link!&lt;/p&gt;
&lt;p&gt;Thanks for reading!&lt;/p&gt;
</description>
      <pubDate>Tue, 02 Dec 2025 24:00:00 GMT</pubDate>
      <dc:creator>STFN</dc:creator>
      <guid>https://stfn.pl/blog/87-pyriodic-backend-pypi/</guid>
    </item>
    <item>
      <title>I got hacked, and that has uncovered all the things I&#39;ve been doing wrong</title>
      <link>https://stfn.pl/blog/88-i-got-hacked/</link>
      <description>&lt;p&gt;&lt;em&gt;Apologies, I was sure I was taking more screenshots, but now I cannot find them,
another thing I did not handle well.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;My VPS got hacked, and this is the postmortem of the whole situation,
written down as a precaution and for future reference.&lt;/p&gt;
&lt;p&gt;On Friday evening I took a look at my Grafana dashboard for the VPS and saw that
for the last hour the CPU usage was at constant 100% on all cores with a load of
over 5.&lt;/p&gt;
&lt;p&gt;Quick &lt;code&gt;ssh&lt;/code&gt; to the machine, &lt;code&gt;htop&lt;/code&gt; and I was shown that the CPU was used by
several instances of the &lt;code&gt;/tmp/fghgf&lt;/code&gt; process. I killed it with &lt;code&gt;killall&lt;/code&gt;, but
of course it came back again like in that Chumbawumba song. I tried searching
for that process name in every web search engine, but there were zero definite
results.&lt;/p&gt;
&lt;p&gt;However I also saw that from time to time a process called &lt;code&gt;xmrig&lt;/code&gt; would run,
and that gave a definite result: a crypto miner. So somebody used my CPU to mine
crypto. And I thought this bullshit scam &amp;quot;currency&amp;quot; is close to dying out.
&lt;em&gt;sigh&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/88/htop.png&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/88/htop.png&quot; alt=&quot;Screenshot of
htop, showing all cores used at 100% percent, and suspicious processes using
them&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The processes were being run by the &lt;code&gt;prometheus&lt;/code&gt; user, so my first suspiction
went in that direction. The first thing I did was to close the 9090 port in UFW.
I had it opened for my selfhosted Grafana to be able to reach the Prometheus on
the VPS for telemetry logging. The connection is secured by basic auth with user
and password, the password is a randomally generated uuid. I knew that blocking
the port was not enough, but I hoped I least I severed the connection between
the attacker and my server. Now that I write it, I thought I could also block
the 9090 outgoing traffic.&lt;/p&gt;
&lt;p&gt;After some consideration I decided to go the kinda nuclear option and remove
everything related to Prometheus from the system with the command:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; deluser --remove-home --remove-all-files prometheus&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This worked, and continuous monitoring with &lt;code&gt;htop&lt;/code&gt; showed that nothing suspicious
was running anymore, and the load came back to the usual 0.1 - 0.2. But I still
did not come to the root of the problem and I started to think about the future
steps. And so the holes in my administration process came to light.&lt;/p&gt;
&lt;h2&gt;Insufficient backups&lt;/h2&gt;
&lt;p&gt;First, a short recap: On that VPS I am running my blog, which is a static site
server by nginx, Umami, a self hosted analytics service running in Docker,
Commento, blog comments engine, also in Docker, and finally GoToSocial, my
Fediverse instance, running as a systemd service with a SQLite database.&lt;/p&gt;
&lt;p&gt;I have made the typical error of a beginner sysadmin: I am doing backups but I
never tested in full recovering from them.&lt;/p&gt;
&lt;p&gt;Every few weeks, mostly before larger updates I do a full snapshost of the
server using Hetzner snapshot functionality. Additionally, I am doing nightly
backups of the GoToSocial database and user data using BorgBase, as described in
the &lt;a href=&quot;https://docs.gotosocial.org/en/latest/admin/backup_and_restore/&quot;&gt;GTS
Documentation&lt;/a&gt;.
What I am not backing up is the Umami and Commento databases, and that is a
glaring ommission. I am not backing up my blog, because it is just a static
site, and I can deploy it from my Forgejo instance anytime I want.&lt;/p&gt;
&lt;p&gt;So yeah, If I wanted to just kill this infected machine and redo the whole setup
from scratch on a fresh VPS, I could at least easily restore my blog. With GTS
it would be a different issue, I think it would be possible with the backups I
do, but again, I never tried it, and that is a problem. And, I would lose the
comments and the analytics data, which would be painful, but I could live with
it.&lt;/p&gt;
&lt;p&gt;Therefore instead of doing the best way with just killing and recreating the server, I went
with trying to save my VPS.&lt;/p&gt;
&lt;h2&gt;Fixing the VPS&lt;/h2&gt;
&lt;p&gt;The breakthough came when Louis from the Fediverse &lt;a href=&quot;https://social.louis-vallat.dev/notes/afwx66aypeiu016h&quot;&gt;sent me a
link&lt;/a&gt; to &lt;a href=&quot;https://github.com/umami-software/umami/issues/3839#issuecomment-3617181565&quot;&gt;an issue
reported in
Umami&lt;/a&gt;,
in the version that I have been using. It was reported that indeed, the Umami
version that I have been using (v2.19) has been vulnerable to the &lt;code&gt;xmrig&lt;/code&gt; crypto
miner. At the same time I was running &lt;code&gt;clamav&lt;/code&gt; the Linux antivirus scanner on
the VPS, and it did found two infected files in &lt;code&gt;/var/lib/docker/overlay/&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;I shutdown the containers, pulled the most recent version (v3.0.2) and started
them again. I rerun the &lt;code&gt;clamav&lt;/code&gt; scan and this time no infected files were
reported.&lt;/p&gt;
&lt;p&gt;As an additional line of defense, I also added a Hetzner Firewall in front of my
VPS in addition to the UFW firewall running on it, as suggested by
&lt;a href=&quot;https://mastodon.social/@cichy1173/115668789005927731&quot;&gt;Grzegorz&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Current situation&lt;/h2&gt;
&lt;p&gt;I have been monitoring my VPS closely for the last two days, and for now
everything seems to be ok, the load is low, there are no suspicious processes
visible in &lt;code&gt;htop&lt;/code&gt;, and several subsequent runs of &lt;code&gt;clamav&lt;/code&gt; have shown no issues.
I still have not restored Prometheus, I&#39;m leaving that for the time when I have
more time (what a great sentence).&lt;/p&gt;
&lt;h2&gt;Future plans&lt;/h2&gt;
&lt;p&gt;This whole situation has shown all the the things that I have been doing wrong
with administraing my server. I&#39;m feeling bad because of it, of course, but also
that is a motivation to step up my Linux admin game. What I will need to do now:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;add the missing things (docker container DBs) to my nightly backup solution&lt;/li&gt;
&lt;li&gt;test my backups!!!&lt;/li&gt;
&lt;li&gt;add more (that means, any) automation so that I can quickly restore my VPS from scratch if a need ever occurs&lt;/li&gt;
&lt;li&gt;add alerting for prolonged high CPU load.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Bottom Line&lt;/h2&gt;
&lt;p&gt;And just a few days ago I wrote in the Fediverse that my servers Just Work and I
am happy with them xD The whole situation has been frustrating for me, but I
guess it&#39;s for the better, now I vividly know what I need to fix and when new
skills I need to acquire. All in all, it was just a random trojan and not
something really, really serious.&lt;/p&gt;
&lt;p&gt;Many thanks to &lt;a href=&quot;https://social.louis-vallat.dev/@louis&quot;&gt;Louis&lt;/a&gt;,
&lt;a href=&quot;https://circumstances.run/@agturcz&quot;&gt;Agnieszka&lt;/a&gt; and
&lt;a href=&quot;https://mastodon.social/@cichy1173&quot;&gt;Grzegorz&lt;/a&gt; for the feedback and
support!&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://fedi.stfn.pl/@stfn/statuses/01KBQWHSEACWKG3FT0YBNCE4W6&quot;&gt;Here&#39;s the Fedi thread that I was doing as the things were unfolding.&lt;/a&gt;&lt;/p&gt;
</description>
      <pubDate>Sun, 07 Dec 2025 24:00:00 GMT</pubDate>
      <dc:creator>STFN</dc:creator>
      <guid>https://stfn.pl/blog/88-i-got-hacked/</guid>
    </item>
    <item>
      <title>First steps with Proxmox running on a Lenovo Tiny PC</title>
      <link>https://stfn.pl/blog/89-proxmox-on-lenovo-tiny/</link>
      <description>&lt;p&gt;This is very much a &lt;em&gt;first steps&lt;/em&gt; post. I know I have barely scratched the
surface of Proxmox, and I am sure that a lof of the things that I wrote here can
be done better, but for now I just wanted to share my first impressions and
first usecases of running Proxmox in my homelab.&lt;/p&gt;
&lt;p&gt;Back in August I wrote how I tried to turn a Lenovo Thincentre Tiny M720q into a gaming PC.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn.pl/blog/79-tiny-gpu-workstation/&quot;&gt;How I bought a Tiny PC and turned it into a GPU workstation / gaming rig&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This did not turn out well, I have not been using it much, and it was mostly
taking up space on my desk, accompanied by a mess of cabling.&lt;/p&gt;
&lt;p&gt;So, what else could I do with it?&lt;/p&gt;
&lt;p&gt;For a long time now, I have been hearing about this Proxmox thing, and how it is
popular in the homelab community as a tool to orchestrate VMs and containers.
Trying it out has been on my todo list for way too long, and having a free tiny
PC looked like the perfect opportunity.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/89/tinies.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/89/tinies.jpg&quot; alt=&quot;Photo of two
Lenovo Tiny PCs, that look like black boxed. The left one has a small USB dongle
with an antenna sticking out of it.&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;My computing
shelf. The Thinkcentre on the left is my Home Assistant box with a Zigbee
dongle, and the right one is the new Proxmox node.&lt;/span&gt;&lt;/p&gt;
&lt;h2&gt;Preparing the Tiny to become a Proxmox machine&lt;/h2&gt;
&lt;p&gt;Switching the PC from a &amp;quot;gaming&amp;quot; setup to a homelabbing node was simple. I
removed the GPU, and in its place I put back the SSD caddy with a 1TB 2.5&amp;quot; SSD
from Lexar. I also reinstalled some minor parts like the back bracket. I did not
put back the WiFi card with its antenna, because I don&#39;t need it, and it would
block the airflow which is already not great inside such a small chassis. I saw
people on Reddit installing 40mm fans to the front of the case, but looking at
the current temps, I don&#39;t need them, at least for now.&lt;/p&gt;
&lt;p&gt;With that, my PC has a 256GB M.2 drive for the system, which is a total overkill
for Proxmox, and a 1TB drive for the virtual machines/containers. And with the
GPU removed, its is once again totally quiet and cool.&lt;/p&gt;
&lt;h2&gt;Installation and configuration of Proxmox&lt;/h2&gt;
&lt;p&gt;For installation, I connected a screen, keyboard and mouse, hopefully for the
last time. In times like this, I really miss having a proper KVM solution, but
that is something for the future. I installed Proxmox in a typical fashion like
any other Linux OS, from a USB drive.&lt;/p&gt;
&lt;p&gt;After installation I run the &amp;quot;PVE Post install script&amp;quot; from the &lt;a href=&quot;https://community-scripts.github.io/ProxmoxVE/scripts?id=post-pve-install&quot;&gt;Proxmox VE
Helper-scripts
repo&lt;/a&gt;.
Yes, I went through it first, I don&#39;t like piping to bash scripts taken straight
from the Internet without looking at them first.&lt;/p&gt;
&lt;p&gt;The only other configuration that I did to my new Proxmox machine is setting up the storage.&lt;/p&gt;
&lt;p&gt;I configured that second 1TB drive as a ZFS pool for use as a storage repository
for the VMs and containers. Why ZFS? Because ZFS is great, and it fits nicely
with the rest of my homelab. More on that later.&lt;/p&gt;
&lt;p&gt;The only thing that was unclear for me and not mentioned in the installation
docs was how to download the images for containers and VM, but this &lt;a href=&quot;https://forum.proxmox.com/threads/download-templates-lxc-containers.57894/&quot;&gt;forum
thread&lt;/a&gt;
helped me out.&lt;/p&gt;
&lt;p&gt;I created a few first containers, and I have to say, Proxmox is awesome, most
things are so intuitive, simple, and quick. I especially like the ZFS support
available out of the box, and the fact that by default every new container gets
its IP address on the same network as the Proxmox host, something I was missing
in LXD. The web UI is clear and powerful, and the advanced features are there,
ready to be used, but not required at the beginning.&lt;/p&gt;
&lt;h2&gt;Migrating from LXD to Proxmox&lt;/h2&gt;
&lt;p&gt;For the last nine months or so I have been a big fan of running LXC containers
in LXD, which is a container and VM orchestrator from Canonical. I have been
running LXD on my NAS, and hosting on it services like Nextcloud or Forgejo. One
of the downsides of this setup is that LXD and all its containers had to share
RAM with other services like Jellyfin, and also with my large ZFS pools, and ZFS
loves RAM like horses love oats (sorry, this is a joke for 30+ year old Polish
people). The Tiny PC has 32GB of its own RAM which, the same as my NAS, so with
separating the containers from my NAS I basically doubled the available amount
of RAM, which is nothing to sneeze at, especially at the current RAM prices
situation. Also, storing the containers on a separate PC, and backing them up to
the NAS gives me another layer of data preservation.&lt;/p&gt;
&lt;p&gt;For those reasons, and because Proxmox has been a joy to use from the first
moments, I decided to move everything from LXD to Proxmox. I was hoping there
was some easy, turn-key way to migrate LXC containers between those two
services, but from all my searching it turned out that was not the case. I found
some solutions, but they all felt very hacky and not to my taste, so I decided
to go the long router and just start the containers from scratch, copying just
the data like the Forgejo repositories or the Nextcloud files.&lt;/p&gt;
&lt;p&gt;I started the migration with PiHole and Nextcloud. For those two services I used
the community scripts that I took from the same repo as the post-install script
mentioned at the beginning of this post. Thanks to them, setting them both was a
breeze, and in moments I had two fresh containers running.&lt;/p&gt;
&lt;p&gt;Then came the monitoring stack, consisting of Grafana and InfluxDB, and later
Forgejo. Migrating Forgejo was easy, after I set up the new Forgejo container, I
used the built-in migration feature to move all my repos from the old service to
the new one.&lt;/p&gt;
&lt;p&gt;I also used this opportunity to move services from other parts of my homelab
into this Proxmox node, for example I moved Miniflux (an RSS reader) from my
&amp;quot;dev&amp;quot; VPS to an LXC container here.&lt;/p&gt;
&lt;p&gt;After migrating everything to Proxmox and allowing it to run for a few days to
make sure there were no issues, I removed LXD from my NAS. My current direction
of my homelab is to have a mostly dumb NAS, running there only the services that
require direct access to a lot of storage and the GPU, like Jellyfin or Immich.
Everything else goes to the Proxmox node.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/89/dashboard.png&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/89/dashboard.png&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;Screenshot of the Proxmox web UI showing the list of running containers.&lt;/span&gt;&lt;/p&gt;
&lt;h2&gt;Backups&lt;/h2&gt;
&lt;p&gt;As I wrote above, the Proxmox services are stored on a ZFS pool on the Lenovo
Tiny. How Proxmox is handling them is that each service is a separate ZFS
dataset. My NAS&#39; storage is also ZFS based. By their powers combined, doing
backups of the containers is a single command:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;syncoid &lt;span class=&quot;token parameter variable&quot;&gt;-r&lt;/span&gt; --skip-parent root@proxmox:thepool large-data/proxmox-backups&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let&#39;s unpack that.
&lt;a href=&quot;https://github.com/jimsalterjrs/sanoid?tab=readme-ov-file#syncoid&quot;&gt;Syncoid&lt;/a&gt; is
an awesome tool for replication of ZFS datasets.&lt;/p&gt;
&lt;p&gt;I am doing a &amp;quot;pull&amp;quot; style replication, the command is being run on the NAS, and
it pulls the datasets from the Proxmox node to the NAS. &lt;code&gt;thepool&lt;/code&gt; is the name of
the Proxmox storage pool, and I am only pulling it&#39;s children, so the datasets
of the particular containers, and saving them under the
&lt;code&gt;large-data/proxmox-backups&lt;/code&gt; dataset, which is on a 4TB pool on my NAS. The NAS
has the ssh keys to access the Proxmox node, so there is no need for any
passwords and the like. And le voila por favor, one command and everything is backed up.&lt;/p&gt;
&lt;p&gt;What I haven&#39;t yet solved is backing up the configuration file of the Proxmox
itself, that is the next step for the near future.&lt;/p&gt;
&lt;h2&gt;Gaming&lt;/h2&gt;
&lt;p&gt;Yes, I tried that too. I created a Kubuntu VM, and installed Steam on it, and
tried gaming using
&lt;a href=&quot;https://github.com/LizardByte/Sunshine&quot;&gt;Sunshine&lt;/a&gt;/&lt;a href=&quot;https://moonlight-stream.org/&quot;&gt;Moonlight&lt;/a&gt;
streaming.&lt;/p&gt;
&lt;p&gt;Aaaaand yeah, no matter how many resources I gave to that VM, I got maybe 3FPS
in Terraria. This is clearly not a PC for gaming, especially remote gaming.
Pwning noobs and 360 no scopes (of course in Terraria) will have to wait.&lt;/p&gt;
&lt;h2&gt;Infrastructure as Code&lt;/h2&gt;
&lt;p&gt;I also started to look at keeping my containers as code in
&lt;a href=&quot;https://opentofu.org/&quot;&gt;OpenTofu&lt;/a&gt;. OpenTofu has a Proxmox provider, and so I can
define the containers configuration and have it automatically applied, just like
the grownup sysadmins do.&lt;/p&gt;
&lt;p&gt;I&#39;m sure this will be a topic of an upcoming blog post.&lt;/p&gt;
&lt;h2&gt;Next steps&lt;/h2&gt;
&lt;p&gt;This is just beginning of my journey with Proxmox and I am fully aware there is
still a lot to learn. Maybe one day I will but another Lenovo Tiny and try out
clustering? Maybe I&#39;ll do some advanced networking magic? Or maybe I&#39;ll build a
full size PC with a GPU, and move everything there and actually try some proper
gaming? We will see. So far I have been enjoying Proxmox very much, and it is my
go-to technology for trying out new things.&lt;/p&gt;
&lt;p&gt;Thanks for reading, and enjoy your holidays, however you spend them!&lt;/p&gt;
</description>
      <pubDate>Sun, 21 Dec 2025 24:00:00 GMT</pubDate>
      <dc:creator>STFN</dc:creator>
      <guid>https://stfn.pl/blog/89-proxmox-on-lenovo-tiny/</guid>
    </item>
    <item>
      <title>Blog statistics for 2025</title>
      <link>https://stfn.pl/blog/90-2025-stats/</link>
      <description>&lt;p&gt;Tomorrow is the last day of 2025, and there is a high probability that the next
year will be 2026, just like Saturday comes after Friday, &lt;a href=&quot;https://www.youtube.com/watch?v=vkcyXB08BBE&quot;&gt;and Sunday comes
afterwards&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/90/totals.png&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/90/totals.png&quot; alt=&quot;Screenshot
of Umami, showing totals views and visits for 2025&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;2025 was also the first full year that I have been using Umami as my online
analytics service to track people visiting my blog.&lt;/p&gt;
&lt;p&gt;I know that &amp;quot;tracking people&amp;quot; sounds bad, but as I said many times, I just do it
because I like looking at numbers and graphs, I am not selling that data to
anyone, I don&#39;t use it to &amp;quot;plan new content&amp;quot;, it does not leave my VPS, and in
general it&#39;s very anonymized. So I hope you will forgive me.&lt;/p&gt;
&lt;p&gt;Anyway, I thought it would be interesting to looks at some of the statistics and
think what they mean.&lt;/p&gt;
&lt;h2&gt;Most popular posts of 2025&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn.pl/blog/90-2025-stats/!%5Bx%5D(https:/stfn-pl-blog-assets.b-cdn.net/90/pages.png)&quot;&gt;&lt;img src=&quot;https://stfn.pl/blog/90-2025-stats/!%5Bx%5D(https:/stfn-pl-blog-assets.b-cdn.net/90/pages.png)&quot; alt=&quot;Screenshot
of Umami, showing most popular posts for 2025&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The most popular post of 2025 is &lt;a href=&quot;https://stfn.pl/blog/08-gpsd-linux-ublox/&quot;&gt;Set up GPSD with U-blox7 GPS
Dongle on Linux&lt;/a&gt;, which is almost two years old by now, but it&#39;s still an
evergreen as people search for it a lot. I guess the problem with configuring a
USB dongle GPS has not gone away since then. And when I was writing it I was
sure nobody will read about such a niche issue.&lt;/p&gt;
&lt;p&gt;The second most popular post is &lt;a href=&quot;https://stfn.pl/blog/22-pico-battery-level/&quot;&gt;How to monitor 12V battery charge with a
Raspberry Pi Pico&lt;/a&gt;. In 2026 I plan
to revisit this project and work on integrating battery monitoring with Home
Assistant.&lt;/p&gt;
&lt;p&gt;And the third most popular post is &lt;a href=&quot;https://stfn.pl/blog/42-caddy-pihole-homelab-subdomains/&quot;&gt;How to set up subdomains in the homelab with
PiHole and Caddy&lt;/a&gt;. I
have not really touched my Caddy install since I wrote that post, but it still
works.&lt;/p&gt;
&lt;h2&gt;Referrers&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/90/referrers.png&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/90/referrers.png&quot; alt=&quot;Screenshot
of Umami, showing most popular refferers (pages from which people visit my blog) for 2025&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Google is the main source of visits to my blog, at 68% of visitors coming from
their its results, with an additional 5% that came from that one time when my
blog post was listed in the Discover widget on Android phones. That was a fun
day, I got a week&#39;s worth of traffic in a few hours.&lt;/p&gt;
&lt;p&gt;Second is duckduckgo and third is Reddit. I don&#39;t read Reddit much, but I post
my blog posts to the theme of &lt;a href=&quot;https://indieweb.org/POSSE&quot;&gt;POSSE&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;And there&#39;s the topic of the Fediverse. Every time I publish a new blog post I
publish a link to on both of my Fediverse accounts, and it&#39;s there that I have
basically all of the conversations about what I post (apart from few awesome
emails which I received, please keep them coming!).&lt;/p&gt;
&lt;p&gt;The Fediverse is not that visible in the traffic statistics, because, well, it&#39;s
decentralized. Meaning each instance has its own referrer, and sometimes even
the client apps put their own ones instead. Furthermore, for a long time
Mastodon did not provide any referrer at all, but that has changed in the recent
versions. In overall, I am seeing a steady growth of visitors with Fediverse
referrers, and that is awesome.&lt;/p&gt;
&lt;p&gt;On the list there are also a few fellow blogs whose creators have been so kind
to link to my stuff (thank you!), and also, which makes me much less happy, LLMs
going through my blog posts.&lt;/p&gt;
&lt;h2&gt;Browsers, OSes and Devices&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/90/combined.png&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/90/combined.png&quot; alt=&quot;Screenshot
of Umami, showing most ppular browsers, OSes and devices of people visiting my blog in 2025&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;My audience is much more Firefox centric than the general populace, with 20% if
visits coming from the Mozilla browser. Chrome trumps all, Apple products are
third. Mozilla, please, please, please, get your stuff together and dump those
pointless AI features that people do not want.&lt;/p&gt;
&lt;p&gt;Operating Systems. Linux is at 14%. Who would have expected that people who read
a blog that is in a very large part about Linux shenanigans, use that system
themselves?&lt;/p&gt;
&lt;p&gt;Devices. Mobile wins, and frankly, I am not sure how Umami makes the division
between a laptop and a desktop.&lt;/p&gt;
&lt;h2&gt;Location&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/90/location.png&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/90/location.png&quot; alt=&quot;Screenshot
of Umami, showing most popular reffers (pages from which people visit my blog) for 2025&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;More than half of my visitors come from English speaking countries, then there&#39;s
Germany, and Poland is fourth. Netherlands and Singapore make suprisingly high
numbers, taking in mind their size. I mean the size of the countries, I met some
Dutch people at my previous work, and it felt weird being of mid height compared
to them. And the Dutch have a very big presence in the Fediverse, as least
that&#39;s what I&#39;m seeing.&lt;/p&gt;
&lt;h2&gt;FAQ&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;How well does Umami filter out bot traffic?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;From what I&#39;ve been comparing Umami statistics with nginx logs it does a rather
good job, mostly because it requires JS to run, and crawlers most often don&#39;t
run JS scripts on the pages they visit. Which also means that any prefetching
done by the Fediverse instances is not calculated towards the views. Of course,
from time to time I see some weird spike of patterns that clearly suggest that
those views are not of human nature.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;I do not want to be tracked by your web analytics&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Ok! Sorry for making it opt-out instead of opt-in. You can block the
umami.stfn.pl domain in your adblocker. Also the [Pro Plus DNS block
list(https://github.com/hagezi/dns-blocklists?tab=readme-ov-file#proplus) which
I personally recommend to add to PiHole has my Umami blocked. I also wrote a few
blogs on how to block ads and trackers using PiHole, for example &lt;a href=&quot;https://stfn.pl/blog/72-pihole-tailscale/&quot;&gt;Using PiHole
and Tailscale to block ads on all my devices, including
mobile&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Has Umami been causing you some problems lately?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn.pl/blog/88-i-got-hacked/&quot;&gt;Funny you should ask&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Bottom Line&lt;/h2&gt;
&lt;p&gt;And that&#39;s it, the final blog post of 2025. Including this one, I wrote 44 blog
posts this year, and so far, the upcoming years looks very promising in many
aspects. New blog posts are in the making!&lt;/p&gt;
&lt;p&gt;Thanks for reading!&lt;/p&gt;
</description>
      <pubDate>Tue, 30 Dec 2025 24:00:00 GMT</pubDate>
      <dc:creator>STFN</dc:creator>
      <guid>https://stfn.pl/blog/90-2025-stats/</guid>
    </item>
    <item>
      <title>Goodbye AWS - moving from Cloudfront to Bunny CDN</title>
      <link>https://stfn.pl/blog/91-cloudfront-to-bunny-cdn/</link>
      <description>&lt;p&gt;I have moved this blog&#39;s CDN from AWS Cloudfront to &lt;a href=&quot;https://bunny.net/&quot;&gt;Bunny CDN&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This is one more step in me moving away from US corporations to smaller European
companies.&lt;/p&gt;
&lt;p&gt;With everything that is happening in the world right now, I do not consider my
data safe when stored on US soil, and I do not want to give my money to fuel
companies and people who are, to put it bluntly, aggressive towards Europe,
European Union and its values.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/91/bunny_sml.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/91/bunny_sml.jpg&quot; alt=&quot;A young black cat lying on a laptop in the middle of a messy desk.&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;I don&#39;t have any photos of bunnies so say hi to my cat instead.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;I am around 90% done in moving away from Google, Amazon and Microsoft, and this
change of CDN service is an important and overdue step in that transition. Now
when you browse my blog, the images and video are being loaded from Bunny&#39;s
servers. I have already removed the last remaining files from AWS S3.&lt;/p&gt;
&lt;h2&gt;Why Bunny CDN?&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;Typical caveats apply, this is not a paid
endorsement, Bunny has no idea who I am, and I am using it only because of the
reasons provided below.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;I found Bunny at the &lt;a href=&quot;https://european-alternatives.eu/category/cdn-content-delivery-network&quot;&gt;European
Alternatives&lt;/a&gt;
website which is an awesome place and you should definitely check it out. I also
saw a few people recommending it in the Fediverse.&lt;/p&gt;
&lt;p&gt;As I said in the beginning, I want move to smaller European companies so that
both my data and my money stays in Europe. And Bunny is situated in Slovenia, a
small eastern Europe country most known for its mammoth ski jump and being
mistaken with Slovakia. And it has &lt;a href=&quot;https://en.wikipedia.org/wiki/Trbovlje_Power_Station&quot;&gt;the tallest chimney
in Europe&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;First steps with Bunny&lt;/h2&gt;
&lt;p&gt;In comparison to AWS, Bunny is so beautifully simple (but I guess 99% of things
are simple compared to AWS).&lt;/p&gt;
&lt;p&gt;I created an account, set up 2FA and started to look around. I want to use Bunny as
a typical CDN, storing my assets and serving them from several location around
the world. So first I created a Storage Zone to which I uploaded all of the
images and videos present on this blog. For that I used the ancient technology
of FTP, as it was the simplest to just upload a few hundred files, but there are
also other means available.&lt;/p&gt;
&lt;p&gt;With that done, I set up a Pull Zone and connected it to the Storage. With that,
every file become available under the url of that Pull Zone. And le voila bueno,
the only thing left was to do a global search and replace of all my blog posts,
to well, you know, replace Cloudfront&#39;s URL with the one from Bunny.&lt;/p&gt;
&lt;p&gt;And one great thing coming from Bunny&#39;s simplicity and them focusing only on few
services, is that right away on the main page I get stats telling me my current
usage and cost. And speaking of that.&lt;/p&gt;
&lt;h2&gt;Pricing and comparing to AWS&lt;/h2&gt;
&lt;p&gt;Here is Bunny&#39;s pricing: &lt;a href=&quot;https://bunny.net/pricing/&quot;&gt;(link)&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Before I left AWS Cloudfront, I checked my monthly transfers (even that was not
that easy with AWS) and they were in the range of few GB per month. &lt;s&gt;Which means
that in my case, I will pay next to nothing, maybe a few cents per month for
hosting my multimedia, just as with Cloudfront.&lt;/s&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Lol, after I published this post I received feedback that this is not the
case, and that on the exact Pricing page I linked above there is, in large, clear
font, written &amp;quot;$1 monthly minimum.&amp;quot;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;And with Bunny it&#39;s easy to
setup transfer limits in case an AI crawler visits my blog and decides to
download every image a million times, because this is what AI does. And I see
that they have some sort of DDOS protection which I haven&#39;t read into yet.&lt;/p&gt;
&lt;h2&gt;What I don&#39;t like&lt;/h2&gt;
&lt;p&gt;I&#39;ve been only exploring Bunny for a few days, and for now I haven&#39;t found
anything I disliked. The only thing I miss would be support for Prometheus or
any other way to pull the stats into Grafana, but that would be just a nice to
have gimmick, not something important.&lt;/p&gt;
&lt;p&gt;If anything else comes up, I&#39;ll do a followup blog post.&lt;/p&gt;
&lt;h2&gt;Bottom Line&lt;/h2&gt;
&lt;p&gt;I know that my few cents/grosze a month will not make any Excel sheet turn from
green to red for AWS or the other way round for Bunny.&lt;/p&gt;
&lt;p&gt;But this migration is something important for me, and I feel that I am going in
the right direction, and I encourage everyone to do the same.&lt;/p&gt;
&lt;p&gt;With that done, this blog is hosted on a German VPS, the code is hosted on
Codeberg and Forgejo which are also German, the CDN is Slovenian, and the domain
company is Polish. I feel like listening to the Ode to Joy now.&lt;/p&gt;
&lt;p&gt;Thanks for reading!&lt;/p&gt;
</description>
      <pubDate>Fri, 23 Jan 2026 24:00:00 GMT</pubDate>
      <dc:creator>STFN</dc:creator>
      <guid>https://stfn.pl/blog/91-cloudfront-to-bunny-cdn/</guid>
    </item>
    <item>
      <title>Tmux, Starship, and Neovim, the trio to make your terminal better</title>
      <link>https://stfn.pl/blog/92-tmux-nvim-starship/</link>
      <description>&lt;p&gt;I like using the terminal (also called the console). On every computer I use, I
always have it opened as one of the windows. I use it for SSHing into my homelab
and VPSes, installing apps, managing files. I prefer the terminal rather than
clicking through a GUI.&lt;/p&gt;
&lt;p&gt;So, if it&#39;s a tool that I use so often, why not make it more usable and
prettier?&lt;/p&gt;
&lt;p&gt;In this blog post I want to show my terminal setup, the different tools I use to
make it nicer. It&#39;s not meant to be a full tutorial how each tool can be
configured, but more of an example of what can be achieved. There are many way
to setup a terminal, but this one is mine. My terminal is my best friend. I must
master the terminal...&lt;/p&gt;
&lt;p&gt;OK, I better stop now. Anyway, here&#39;s the final result:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/92/starship.png&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/92/starship.png&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/92/nvim.png&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/92/nvim.png&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;As the base, I just use &lt;code&gt;bash&lt;/code&gt;. I used &lt;code&gt;zsh&lt;/code&gt; for some time, but I went back to
the default shell shipped with Debian and Kubuntu, the OSes that I use.&lt;/p&gt;
&lt;p&gt;I have been building this configuration over the last several months, so I
cannot provide the source for each and every change, but if my memory serves me
right,&lt;a href=&quot;https://www.youtube.com/watch?v=GH3kpsbbERo&quot;&gt; this Youtube video&lt;/a&gt; has
been the basis of most of the config.&lt;/p&gt;
&lt;h2&gt;tmux&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;tmux&lt;/code&gt; is a terminal multiplexer. While it sounds like a technology from Star
Trek (&lt;em&gt;Mr Spock, the terminal multiplexers are down, fetch the ion
torpedoes!&lt;/em&gt;) what it means is that the terminal window can be divided into
multiple windows, each having multiple panes, and in each of those panes a
different command can be run. Another useful feature is that you can detach tmux
from your terminal, meaning anything run in it will continue to run but in the
background. Useful for long-running commands when you need to turn off your PC
or close the SSH connection.&lt;/p&gt;
&lt;p&gt;tmux can be installed using your preferred package manager, for me it&#39;s&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;apt&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; tmux&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;tmux on itself is already useful, but it feels a bit... raw. However, it can be
made much nicer by adding a custom configuration file and plugins.&lt;/p&gt;
&lt;p&gt;First, let&#39;s install the &lt;a href=&quot;https://github.com/tmux-plugins/tpm&quot;&gt;tmux plugin manager&lt;/a&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; clone https://github.com/tmux-plugins/tpm ~/.tmux/plugins/tpm&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The second step is installing a nice theme. I prefer the &lt;a href=&quot;https://github.com/catppuccin/tmux&quot;&gt;catppuccin
theme&lt;/a&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; clone https://github.com/catppuccin/tmux ~/.tmux/themes/catppuccin&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Third step is to get Nerd Fonts, fonts with additional glyphs to use with the
theme. My font of choice is the Noto Nerd font that can be downloaded from &lt;a href=&quot;https://www.nerdfonts.com/font-downloads&quot;&gt;Nerd
Fonts&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I guess the way to add new fonts differs between distros. For my Kubuntu it&#39;s:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;apt&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;unzip&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;mkdir&lt;/span&gt; ~/.fonts
&lt;span class=&quot;token builtin class-name&quot;&gt;cd&lt;/span&gt; ~/.fonts
&lt;span class=&quot;token function&quot;&gt;wget&lt;/span&gt; https://github.com/ryanoasis/nerd-fonts/releases/download/v3.4.0/Noto.zip
&lt;span class=&quot;token function&quot;&gt;unzip&lt;/span&gt; Noto.zip

&lt;span class=&quot;token comment&quot;&gt;# update the font cache&lt;/span&gt;
fc-cache &lt;span class=&quot;token parameter variable&quot;&gt;-fv&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# if the command above is not found: sudo apt install fontconfig&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Sometimes new fonts do not load properly, then it is best to just reboot.&lt;/p&gt;
&lt;p&gt;With everything installed, we can create the config file in the home directory:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# ~/.tmux.conf&lt;/span&gt;

set-option &lt;span class=&quot;token parameter variable&quot;&gt;-g&lt;/span&gt; default-terminal &lt;span class=&quot;token string&quot;&gt;&#39;tmux-256color&#39;&lt;/span&gt;

&lt;span class=&quot;token builtin class-name&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-g&lt;/span&gt; base-index &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;              &lt;span class=&quot;token comment&quot;&gt;# start indexing windows at 1 instead of 0&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-g&lt;/span&gt; detach-on-destroy off     &lt;span class=&quot;token comment&quot;&gt;# don&#39;t exit from tmux when closing a session&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-g&lt;/span&gt; escape-time &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;             &lt;span class=&quot;token comment&quot;&gt;# zero-out escape time delay&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-g&lt;/span&gt; history-limit &lt;span class=&quot;token number&quot;&gt;1000000&lt;/span&gt;     &lt;span class=&quot;token comment&quot;&gt;# increase history size (from 2,000)&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-g&lt;/span&gt; renumber-windows on       &lt;span class=&quot;token comment&quot;&gt;# renumber all windows when any window is closed&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-g&lt;/span&gt; set-clipboard on          &lt;span class=&quot;token comment&quot;&gt;# use system clipboard&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-g&lt;/span&gt; status-position &lt;span class=&quot;token function&quot;&gt;top&lt;/span&gt;       &lt;span class=&quot;token comment&quot;&gt;# macOS / darwin style&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-g&lt;/span&gt; default-terminal &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;${&lt;span class=&quot;token environment constant&quot;&gt;TERM&lt;/span&gt;}&lt;/span&gt;&quot;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# toggle panes with Vim shortcuts&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;bind&lt;/span&gt; h select-pane &lt;span class=&quot;token parameter variable&quot;&gt;-L&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;bind&lt;/span&gt; j select-pane &lt;span class=&quot;token parameter variable&quot;&gt;-D&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;bind&lt;/span&gt; k select-pane &lt;span class=&quot;token parameter variable&quot;&gt;-U&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;bind&lt;/span&gt; l select-pane &lt;span class=&quot;token parameter variable&quot;&gt;-R&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;#install plugins&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-g&lt;/span&gt; @plugin &lt;span class=&quot;token string&quot;&gt;&#39;tmux-plugins/tmux-cpu&#39;&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-g&lt;/span&gt; @plugin &lt;span class=&quot;token string&quot;&gt;&#39;tmux-plugins/tmux-battery&#39;&lt;/span&gt;

&lt;span class=&quot;token builtin class-name&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-g&lt;/span&gt; @plugin &lt;span class=&quot;token string&quot;&gt;&#39;tmux-plugins/tpm&#39;&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-g&lt;/span&gt; @catppuccin_flavor &lt;span class=&quot;token string&quot;&gt;&#39;mocha&#39;&lt;/span&gt;


&lt;span class=&quot;token comment&quot;&gt;# Load catppuccin&lt;/span&gt;
run ~/.config/tmux/plugins/catppuccin/tmux/catppuccin.tmux

&lt;span class=&quot;token comment&quot;&gt;# Configure top bar&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-g&lt;/span&gt; status-right-length &lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-g&lt;/span&gt; status-left-length &lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-g&lt;/span&gt; status-left &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-g&lt;/span&gt; status-right &lt;span class=&quot;token string&quot;&gt;&quot;#{E:@catppuccin_status_application}&quot;&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-agF&lt;/span&gt; status-right &lt;span class=&quot;token string&quot;&gt;&quot;#{E:@catppuccin_status_cpu}&quot;&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-ag&lt;/span&gt; status-right &lt;span class=&quot;token string&quot;&gt;&quot;#{E:@catppuccin_status_session}&quot;&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-ag&lt;/span&gt; status-right &lt;span class=&quot;token string&quot;&gt;&quot;#{E:@catppuccin_status_uptime}&quot;&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-agF&lt;/span&gt; status-right &lt;span class=&quot;token string&quot;&gt;&quot;#{E:@catppuccin_status_battery}&quot;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# use current path when opening new pane&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;bind&lt;/span&gt;  %  split-window &lt;span class=&quot;token parameter variable&quot;&gt;-h&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-c&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;#{pane_current_path}&quot;&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;bind&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;&quot;&#39;&lt;/span&gt; split-window &lt;span class=&quot;token parameter variable&quot;&gt;-v&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-c&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;#{pane_current_path}&quot;&lt;/span&gt;

run &lt;span class=&quot;token string&quot;&gt;&#39;~/.tmux/plugins/tpm/tpm&#39;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I hope the particular settings are self-explanatory. For more details, consult
the docs or the video I linked above. And le voila, now your tmux will be
pretty.&lt;/p&gt;
&lt;h2&gt;Starship&lt;/h2&gt;
&lt;p&gt;Starship is a customizable prompt for your terminal. A prompt is basically the
thing that you see at the beginning of the input line when you type something in
the terminal.&lt;/p&gt;
&lt;p&gt;Starship can be installed using a package manager or the easy-but-shady method
of piping curl to bash. The installation docs are
&lt;a href=&quot;https://starship.rs/guide/#%F0%9F%9A%80-installation&quot;&gt;here&lt;/a&gt;. To render
properly, starship requires a Nerd Font that we already installed in the
previous step.&lt;/p&gt;
&lt;p&gt;A lot of things works straight out of the box, like showing the git repository
status or the Python virtual environment, and additional configuration can be done using,
you would never guess it, a configuration file, &lt;code&gt;~/.config/starship.toml&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The full configuration docs are available
&lt;a href=&quot;https://starship.rs/config/#prompt&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Here&#39;s mine:&lt;/p&gt;
&lt;pre class=&quot;language-ini&quot;&gt;&lt;code class=&quot;language-ini&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# don&#39;t add newline after the prompt&lt;/span&gt;
&lt;span class=&quot;token key attr-name&quot;&gt;add_newline&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token value attr-value&quot;&gt;false&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# always show machine hostname&lt;/span&gt;
&lt;span class=&quot;token section&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token section-name selector&quot;&gt;hostname&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token key attr-name&quot;&gt;ssh_only&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token value attr-value&quot;&gt;false&lt;/span&gt;
&lt;span class=&quot;token key attr-name&quot;&gt;format&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token value attr-value&quot;&gt;&#39;&lt;span class=&quot;token inner-value&quot;&gt;[$ssh_symbol](bold blue)[$hostname](bold blue)&lt;/span&gt;&#39;&lt;/span&gt;
&lt;span class=&quot;token key attr-name&quot;&gt;trim_at&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token value attr-value&quot;&gt;&#39;&lt;span class=&quot;token inner-value&quot;&gt;.companyname.com&lt;/span&gt;&#39;&lt;/span&gt;
&lt;span class=&quot;token key attr-name&quot;&gt;disabled&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token value attr-value&quot;&gt;false&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# always show machine IP&lt;/span&gt;
&lt;span class=&quot;token section&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token section-name selector&quot;&gt;localip&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token key attr-name&quot;&gt;ssh_only&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token value attr-value&quot;&gt;false&lt;/span&gt;
&lt;span class=&quot;token key attr-name&quot;&gt;format&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token value attr-value&quot;&gt;&#39;&lt;span class=&quot;token inner-value&quot;&gt;@[$localipv4](bold blue) &lt;/span&gt;&#39;&lt;/span&gt;
&lt;span class=&quot;token key attr-name&quot;&gt;disabled&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token value attr-value&quot;&gt;false&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Neo&lt;/h2&gt;
&lt;p&gt;The final part of the Terminal Trio is Neovim, an evolution of the evergreen
text editing machine, Vi. Nvim can be &lt;a href=&quot;https://neovim.io/doc/install/&quot;&gt;installed using your package
manager&lt;/a&gt; of choice, or by downloading the
binary.&lt;/p&gt;
&lt;p&gt;Nvim also has support for plugins, there are several available plugin engines,
and from them, I use Vim-plug. It can be installed by downloading the &lt;code&gt;plug.vim&lt;/code&gt;
file into the nvim&#39;s autoload directory:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;curl&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-fLo&lt;/span&gt; ~/.vim/autoload/plug.vim --create-dirs &lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;
    https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;More information and alternative methods can be found in the &lt;a href=&quot;https://github.com/junegunn/vim-plug&quot;&gt;Vim-plug
docs&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;With the plugin manager installed, the next step is to create the configuration
file and paste the desired settings into it:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;mkdir&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-p&lt;/span&gt; ~/.config/nvim
nvim ~/config/nvim/init.vim&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And here&#39;s my config file:&lt;/p&gt;
&lt;pre class=&quot;language-lua&quot;&gt;&lt;code class=&quot;language-lua&quot;&gt;set mouse&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;v                 &quot; middle&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;click paste with 
set hlsearch                &quot; highlight search 
set incsearch               &quot; incremental search
set tabstop&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;               &quot; number of columns occupied by a tab 
set softtabstop&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;           &quot; see multiple spaces as tabstops so &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;BS&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; does the right thing
set expandtab               &quot; converts tabs to white space
set shiftwidth&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;            &quot; width &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; autoindents
set autoindent              &quot; indent a new line the same amount as the line just typed
set number                  &quot; add line numbers
set wildmode&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;longest&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;list   &quot; get bash&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;like tab completions
set cc&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;80&lt;/span&gt;                  &quot; set an &lt;span class=&quot;token number&quot;&gt;80&lt;/span&gt; column border &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; good coding style
filetype plugin indent on   &quot;allow auto&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;indenting depending on file type
syntax on  
set clipboard&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;unnamedplus   &quot; using system clipboard
filetype plugin on
set cursorline              &quot; highlight current cursorline

&quot; plugins section

&quot; Start NERDTree &lt;span class=&quot;token keyword&quot;&gt;and&lt;/span&gt; put the cursor back &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; the other window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;
autocmd VimEnter &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; NERDTree &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; wincmd p
&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;let g&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;NERDTreeWinSize&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;60&lt;/span&gt;

&quot; Exit Vim &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; NERDTree is the only window remaining &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; the only tab&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;
autocmd BufEnter &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;tabpagenr&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;$&#39;&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&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;winnr&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;$&#39;&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&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;exists&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;b:NERDTree&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt; b&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;NERDTree&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isTabTree&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 operator&quot;&gt;|&lt;/span&gt; call &lt;span class=&quot;token function&quot;&gt;feedkeys&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;:quit&#92;&amp;lt;CR&gt;:&#92;&amp;lt;BS&gt;&quot;&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; endif

call plug&lt;span class=&quot;token operator&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;begin&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 string&quot;&gt;&quot; List your plugins here&quot;&lt;/span&gt;
&quot; A Tree&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;like side bar &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; better navigation
Plug &lt;span class=&quot;token string&quot;&gt;&#39;preservim/nerdtree&#39;&lt;/span&gt;
&quot; A cool status bar
Plug &lt;span class=&quot;token string&quot;&gt;&#39;vim-airline/vim-airline&#39;&lt;/span&gt;

call plug&lt;span class=&quot;token operator&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;end&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;Most of the settings are either self-explanatory or explained with comments. I
am using two Nvim plugins: &lt;a href=&quot;https://github.com/preservim/nerdtree&quot;&gt;NERDTree&lt;/a&gt; and
&lt;a href=&quot;https://github.com/vim-airline/vim-airline&quot;&gt;vim-airline&lt;/a&gt;. The first one adds a
file manager to the side of nvim, to make it work more like an IDE, and the
second one adds a status bar wit useful information, like the current mode or
rows and column numbers.&lt;/p&gt;
&lt;p&gt;When the configuration file is saved and you open Nvim again, you&#39;ll get an
error that the plugins are not installed. To install them, do &lt;code&gt;:PlugInstall&lt;/code&gt;
when in command mode in Nvim.&lt;/p&gt;
&lt;h2&gt;nala&lt;/h2&gt;
&lt;p&gt;Finally, I would like to mention &lt;code&gt;nala&lt;/code&gt;, about which I heard in the &lt;a href=&quot;https://linuxmatters.sh/66/&quot;&gt;Linux
Matters&lt;/a&gt; podcast. Nala is an alternative frontend
to &lt;code&gt;apt&lt;/code&gt;, the package manager of Debian and its derivatives. Under the hood it
uses the same library as &lt;code&gt;apt&lt;/code&gt;, but has a much nicer and friendlier look and
feel. The commands are mostly the same, running &lt;code&gt;nala upgrade&lt;/code&gt; with both update
the local package information, and install the new versions of packages. Just a
fun, small thing to brighen up your terminal even further.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/92/nala.png&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/92/nala.png&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Bottom Line&lt;/h2&gt;
&lt;p&gt;And that&#39;s it, hope my post will be an inspiration for you to make your terminal
have a dash of sparkle. What terminal enhancements do you use? For the time
being I have turned off comments as I was not happy with my comments system, and
I removed all superflous services from my VPS after &lt;a href=&quot;https://stfn.pl/blog/88-i-got-hacked/&quot;&gt;the last
attack&lt;/a&gt;, but you can reach me through
email or the Fediverse, links in the footer.&lt;/p&gt;
&lt;p&gt;Thanks for reading!&lt;/p&gt;
</description>
      <pubDate>Fri, 06 Feb 2026 24:00:00 GMT</pubDate>
      <dc:creator>STFN</dc:creator>
      <guid>https://stfn.pl/blog/92-tmux-nvim-starship/</guid>
    </item>
    <item>
      <title>New house, new blog, new me</title>
      <link>https://stfn.pl/blog/93-new-house-new-blog/</link>
      <description>&lt;p&gt;First of all, apologies to all the RSS readers of my blog, you&#39;ll most probably
get all the posts again.&lt;/p&gt;
&lt;p&gt;Almost two months have passed since the last blog post. The reason for the
hiatus is simple. Three weeks ago I have finally, finally moved into my new
house. Those were hectic two months, endless calls, decisions, talking to the
construction crew, buying materials, tools, and then packing, unpacking,
sorting, throwing away surplus stuff. I can probably go through the local
hardware store with my eyes closed. But all that is mostly done, there are of
course still things to sort out, like the paperwork, or the garden, or getting a
contract for dealing with trash, but the actual move is done.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/93/IMG_20260318_121236.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/93/IMG_20260318_121236.jpg&quot; alt=&quot;A black cat is lying on a very large dog mattress which is covered by a
bright blue and pink
blanket&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;The cat is enjoying the new place&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;And I&#39;ve been loving it here, it is so great to be able to just go out into the
open air, without any stairs or corridors. There are so many new possibilities.
I already tried astrophotography and being able to just carry out the equipment
through the door, and then sit on the coach as the exposures are taken is a
totally new level.&lt;/p&gt;
&lt;p&gt;The homelab is is also migrated to its new place, soon there will be a new blog
post dedicated to its current state.&lt;/p&gt;
&lt;h3&gt;New blog&lt;/h3&gt;
&lt;p&gt;For a long time now I have been wanting to do something with this blog. I
started it three years ago, and I took a ready template from the Astro template
site. It allowed me to start easily without spending time on writing the
boilerplate, which was a big plus. But as time went by, and I started adding new
features, the codebase started to feel cobbled together with sticks. I never
really learned Astro, so my edits were mostly trial and error, and putting stuff
where it worked, not where it should be.&lt;/p&gt;
&lt;p&gt;So I decided to rewrite the blog from scratch with a new technology, in order
for it to be actually &lt;em&gt;mine&lt;/em&gt;, with the code I understand and know how it works.&lt;/p&gt;
&lt;p&gt;First I tried &lt;a href=&quot;https://gohugo.io/&quot;&gt;Hugo&lt;/a&gt;, but I got confused with it quickly,
that framework just not clicked with me. My second attempt was
&lt;a href=&quot;https://www.11ty.dev/&quot;&gt;Eleventy&lt;/a&gt; and that was the total opposite. Eleventy
feels so simple, so straightforward, after doing a few tutorials and going
through a sample site I had no problems with writing this blog how I wanted it
to be. I don&#39;t need much, just a way to generate static HTML, handle navigation,
and generate an RSS feed.&lt;/p&gt;
&lt;p&gt;The aim has been to keep the look similar to the previous version, but make it
simple and understandable for me. I&#39;m a backend person, my frontend skills are
very basic and rooted in mid 00s, but I think I made a reasonably good job. One
thing that I dropped was the light/dark theme switcher, it has been a source of
problems on the previous version, and I did not want to overcomplicate the new
layout. On the other hand, the RSS feed now provides the full text of the blog
posts, so you can read them all in your RSS reader of choice.&lt;/p&gt;
&lt;p&gt;I made sure all the links stay the same, so as no referrals to my blog get
broken. The RSS feed URL also did not change.&lt;/p&gt;
&lt;p&gt;I took this opportunity to update old blog posts, add some alt text, fix
typso, update/remove broken links and comment on things which have become
outdated.&lt;/p&gt;
&lt;p&gt;I have many ideas for the next blog posts, there will be something about the
homelab&#39;s hardware, about Woodpecker CI, about local TLS, going back to
Meshtastic, and now is finally the time when I have the time (time time time) to
write them. Exciting times!&lt;/p&gt;
&lt;p&gt;Thanks for reading!&lt;/p&gt;
</description>
      <pubDate>Sat, 28 Mar 2026 24:00:00 GMT</pubDate>
      <dc:creator>STFN</dc:creator>
      <guid>https://stfn.pl/blog/93-new-house-new-blog/</guid>
    </item>
    <item>
      <title>Human JSON - community against AI generated content</title>
      <link>https://stfn.pl/blog/94-human-json/</link>
      <description>&lt;p&gt;Yesterday, &lt;a href=&quot;https://michal.sapka.pl/&quot;&gt;Michał Sapka&lt;/a&gt;, Poland&#39;s favourite blogger
according to a &lt;a href=&quot;https://rubenerd.com/&quot;&gt;certain Australian&lt;/a&gt; posted on the
Fediverse a toot about &lt;a href=&quot;https://codeberg.org/robida/human.json&quot;&gt;human.json&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;human.json is intended to be a way of presenting that this specific website is
written by humans, not AI. It also allows to vouch for other websites, that the
author of this one knows that those other websites are also written by humans.&lt;/p&gt;
&lt;p&gt;I would call it a reputation based system of proving a website&#39;s human origins
(how creepy does it sound, doesn&#39;t it?). In contrast to something like GitHub
stars, it is more personal and works based on people you already know. I am
quite sure that Michał is a human that writes his blog with his human fingers
touching his fancy retrocomputing keyboard. With human.json I can see that he
vouches for some other website, so I can relatively safely assume that this
other website is also written by a person of flesh and bone (this is getting
even creepier).&lt;/p&gt;
&lt;p&gt;In technical terms, all an owner of a website needs to do is add a &lt;code&gt;human.json&lt;/code&gt;
file on the server, and link to it using a &lt;code&gt;&amp;lt;meta&amp;gt;&lt;/code&gt; tag in the site&#39;s header.
That JSON file consists of the canonical URL to their own website, and a list of
websites they vouch for. Simple.&lt;/p&gt;
&lt;p&gt;The second part is to install a browser extension that will show, when visiting
the websites, that their author declares itself human, and also how many other
people vouched for them not being a murderous bot able to withstand an exploding
fuel tanker truck.&lt;/p&gt;
&lt;p&gt;The AI generated slop flooding the internets is a problem that is only growing.
I don&#39;t know of any simple and sure way to tackle it, probably such a
solution does not exist. But we can and should oppose it, and human.json sounds
in my opinion like a good tool to have. With it we can build a public network of
mutual connections and use it as a shield against the dying of the human web.&lt;/p&gt;
&lt;p&gt;I&#39;ve been listening recently to the &lt;a href=&quot;https://latenightlinux.com/&quot;&gt;Late Night Linux
podcast&lt;/a&gt; and one of the main reasons I listen to it
is because those guys don&#39;t sugarcoat the world, they are angry as I am about
the current state of things. In the last episode, I think it was Féilim who said
we had 20 good years of using the Internet, and now it&#39;s ending because of the
AI slop and attacks on people&#39;s privacy. I started using the internet somewhere
around 1999, and for me always its strengths and features was it being anarchic,
egalitarian, free (both as in beer and speech), and everyone could be anonymous
as much as they wanted. And this is ending with bullshit like AI crawlers
DDOSing websites, and destroyers of anonymity like age verification being
implemented by lawmakers.&lt;/p&gt;
&lt;p&gt;We are not in a great situation, and I&#39;m glad even such small things like
human.json are being implemented to fight it.&lt;/p&gt;
&lt;p&gt;I implemented it on my blog and added a few people from the top of my head. If
you would like to be added or removed from that list, send me an email or
contact me through the Fediverse, links in the footer.&lt;/p&gt;
&lt;p&gt;Thanks for reading!&lt;/p&gt;
&lt;p&gt;Other bloggers that wrote about implementing human.json: &lt;a href=&quot;https://shkspr.mobi/blog/2026/03/adding-human-json-to-wordpress/&quot;&gt;Terence
Eden&lt;/a&gt;, &lt;a href=&quot;https://neilzone.co.uk/2026/03/implementing-the-somewhat-whimsical-human-json-protocol-on-my-website/&quot;&gt;Neil
Brown&lt;/a&gt;,
&lt;a href=&quot;https://coreysnipes.com/add-a-humanjson-file-to-a-publii-website.html&quot;&gt;Corey
Snipes&lt;/a&gt;&lt;/p&gt;
</description>
      <pubDate>Mon, 30 Mar 2026 24:00:00 GMT</pubDate>
      <dc:creator>STFN</dc:creator>
      <guid>https://stfn.pl/blog/94-human-json/</guid>
    </item>
    <item>
      <title>Using Bunny CDN to embed and stream videos on static websites</title>
      <link>https://stfn.pl/blog/95-bunny-cdn-video-streaming/</link>
      <description>&lt;p&gt;&lt;em&gt;As with other such posts on my blog, this is not sponsored in any way, I just
write about the things I personally use and think are worth sharing.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;In the process of &lt;a href=&quot;https://stfn.pl/blog/93-new-house-new-blog/&quot;&gt;rewriting my
blog&lt;/a&gt; I changed how I host my
videos.&lt;/p&gt;
&lt;p&gt;I currently have two posts with embedded short videos:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn.pl/blog/30-plants-timelapse-rpi/&quot;&gt;How to make a timelapse with a Raspberry Pi&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn.pl/blog/51-introduction-to-esphome-with-projects/&quot;&gt;I started using ESPhome and now I have a local smarthome&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Previously I stored the video files in the CDN&#39;s storage (first Cloudfront, then
Bunny CDN, &lt;a href=&quot;https://stfn.pl/blog/91-cloudfront-to-bunny-cdn/&quot;&gt;here is the blog post about switching my
CDNs&lt;/a&gt;) and served them using
the &lt;a href=&quot;https://videojs.org/&quot;&gt;video.js&lt;/a&gt; library. This in general worked, and I had
no issues with it, but when rewriting the blog I wanted to try out something
else, and I saw that Bunny CDN has a dedicated service for hosting videos:
&lt;a href=&quot;https://bunny.net/stream/&quot;&gt;Bunny Stream&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Bunny Stream is a service that allows to upload videos to Bunny, and then the
rabbit handles streaming them through their own player. The player can then be
embedded in a website by adding the generated code snippet. There is a few
options to edit the player, change the thumbnail, set autoplay on or off, play
in a loop. You can even add chapters and subtitles. Also with subtitles, Bunny
offers autogenerating subtitles using machine learning for an additional cost.&lt;/p&gt;
&lt;p&gt;As for the cost, the hare company always chargers 1 USD minimum with any
incurred cost on top of that. They have &lt;a href=&quot;https://bunny.net/pricing/stream/&quot;&gt;a
calculator&lt;/a&gt; that allows to forecast the
monthly cost based on the video size and traffic. And again, for people like me
who would like to serve a few videos on a less than popular site, the monthly
costs should not exceed 2-3 USD, which I found reasonable. In my case, I have
not exceeded a few cents of costs for video streaming yet. I will be monitoring
the costs and if they become unacceptable high, I will write another blog post.&lt;/p&gt;
&lt;p&gt;Here&#39;s how the player looks and works: I haven&#39;t made any interesting videos
recently, so I dug up something fun from my archive.&lt;/p&gt;
&lt;div class=&quot;video&quot; style=&quot;position:relative;padding-top:56.25%;&quot;&gt;&lt;iframe src=&quot;https://player.mediadelivery.net/embed/626472/de423120-785b-4b9f-8925-3fe824bddb28?autoplay=false&amp;loop=false&amp;muted=false&amp;preload=true&amp;responsive=true&quot; loading=&quot;lazy&quot; style=&quot;border:0;position:absolute;top:0;height:100%;width:100%;&quot; allow=&quot;accelerometer;gyroscope;autoplay;encrypted-media;picture-in-picture;&quot; allowfullscreen=&quot;true&quot;&gt;&lt;/iframe&gt;&lt;/div&gt;
&lt;p&gt;&lt;span class=&quot;image_sub&quot;&gt;A short video I made in 2021 during one of the Pride events.&lt;/span&gt;&lt;/p&gt;
&lt;h3&gt;Why am I sharing this?&lt;/h3&gt;
&lt;p&gt;One of the reasons why I am writing about hosting videos this way is the lack of
good, open PeerTube instances.&lt;/p&gt;
&lt;p&gt;PeerTube is a federated platform for hosting videos, one could call it the
Youtube of the Fediverse. The issue is that hosting and streaming videos
requires much more significant resources that text or images, and so there
aren&#39;t many people willing to do it. The Polish part of the Fediverse had
&lt;code&gt;tube.pol.social&lt;/code&gt;, but that ended with the general demise of &lt;code&gt;pol.social&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;And so I think that using Bunny Stream and embedding videos on a personal site
is a reasonably cheap and easy to implement alternative for PeerTube, for people
like me who have just a few short videos and want to put them online. Of course
the open source and federation aspects are lost, but embedding videos on a
personal static website keeps it &lt;a href=&quot;https://indieweb.org/POSSE&quot;&gt;POSSE&lt;/a&gt;. So it&#39;s
better than nothing, you keep control over your content and not give it to the
whims of the Algorithm like on Youtube.&lt;/p&gt;
&lt;p&gt;Thanks for reading!&lt;/p&gt;
</description>
      <pubDate>Wed, 01 Apr 2026 24:00:00 GMT</pubDate>
      <dc:creator>STFN</dc:creator>
      <guid>https://stfn.pl/blog/95-bunny-cdn-video-streaming/</guid>
    </item>
    <item>
      <title>First astrophotography session from my new house - the Virgo Cluster</title>
      <link>https://stfn.pl/blog/96-markarians-chain/</link>
      <description>&lt;p&gt;As I already mentioned in oh so many blog posts, I now live in a house, which
opens totally new possibilities when doing astrophography. I no longer have to
drive a long way just to get to the spot, and then spend hours either outside in
the cold, or in a small shed. Now all I need is to carry out the equipment in
the evening, do the setup and polar alignment when it gets acceptably dark, and
then sit comfortably on the couch and control the session from the inside. Which
means I can do much longer sessions without freezing and getting fed up with it
all.&lt;/p&gt;
&lt;p&gt;The house is quite far from the cities, so the sky is around &lt;a href=&quot;https://www.handprint.com/ASTRO/bortle.html&quot;&gt;Bortle
4/5&lt;/a&gt;, which is not bad for the more
populated parts of Poland. Using binoculars I can see the Andromeda Galaxy,
Alpha Persei Cluster, Messier 44 and the like. Messier 42 is plainly visible
with the naked eye. Much, much better than living in Poznań.&lt;/p&gt;
&lt;p&gt;I&#39;ve been planning to do a separate blog post about the AP gear and software I
am using, and yes, it will come at some point, but not today :)&lt;/p&gt;
&lt;p&gt;This is a new start for me after some hiatus, so I am keeping it simple, wide
angle, simple setups and no guiding. Guiding has been an endless source of
problems and frustrations for me, so for now I am skipping it to focus on
the other parts of astrophotography, and just find fun in it.&lt;/p&gt;
&lt;h2&gt;The Virgo Cluster&lt;/h2&gt;
&lt;p&gt;Spring is around us, which means that in the Northern Hemisphere it&#39;s &lt;s&gt;duck&lt;/s&gt;
&lt;s&gt;rabbit&lt;/s&gt; galaxy season. The Milky Way is low on the horizon, and most of the
sky is away from the plane of our Galaxy, which gives a clear view on the more
interesting galaxies other than our own.&lt;/p&gt;
&lt;p&gt;I am using a 150mm lens to do AP now, so of course it&#39;s much too wide for a
single galaxy (maybe apart from Andromeda, but she hugs the horizon), so I went
with a galaxy cluster. The &lt;a href=&quot;https://en.wikipedia.org/wiki/Virgo_Cluster&quot;&gt;Virgo
Cluster&lt;/a&gt; is in the perfect place to
shoot it, high in the Eastern sky between the constellations of Leo, Virgo, and
Coma Berenices. It consists of thousands of galaxies, with tens of them visible
with amateur equipment. A notable part of the Virgo Cluster is the &lt;a href=&quot;https://en.wikipedia.org/wiki/Markarian%27s_Chain&quot;&gt;Markarian&#39;s
Chain&lt;/a&gt;, a group of galaxies
that seem to ordered in a smooth line.&lt;/p&gt;
&lt;p&gt;With the introduction done, time to present the actual photo that I did:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/96/virgo_full.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/96/virgo_thumb.jpg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;And here it is, the Virgo Cluster. Right off the bat, several large galaxies can
be visible, but when you zoom in (click on the image for the full resolution),
much more smaller ones can be seen.&lt;/p&gt;
&lt;p&gt;Here&#39;s a walkthrough of the more notable ones.&lt;/p&gt;
&lt;h3&gt;M88&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Messier_88&quot;&gt;Messier 88 on Wikipedia&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/96/m88.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/96/m88.jpg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;M90&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Messier_90&quot;&gt;Messier 90 on Wikipedia&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/96/m90.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/96/m90.jpg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;M99&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Messier_99&quot;&gt;Messier 99 on Wikipedia&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/96/m99.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/96/m99.jpg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;M100&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Messier_100&quot;&gt;Messier 100 on Wikipedia&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/96/m100.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/96/m100.jpg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Markarian&#39;s Chain&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Markarian%27s_Chain&quot;&gt;Markarian&#39;s Chain on Wikipedia&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/96/markarian.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/96/markarian.jpg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Annotated images&lt;/h3&gt;
&lt;p&gt;And here is the image annotated, and it&#39;s position in the night sky.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/96/annotated_full.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/96/annotated_thumb.jpg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stfn-pl-blog-assets.b-cdn.net/96/skymap.jpg&quot;&gt;&lt;img src=&quot;https://stfn-pl-blog-assets.b-cdn.net/96/skymap.jpg&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Technicalities&lt;/h2&gt;
&lt;p&gt;As I said above, I am keeping it simple for now, to extract most fun and results without getting frustrated by the more complicated parts of AP.&lt;/p&gt;
&lt;p&gt;Session:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;270 exposures of 90 seconds each, captured over two consecutive nights, for a
total of 6 hours 45 minutes acquisition time, an absolute record for me, the
previous longest session was the &lt;a href=&quot;https://stfn.pl/blog/03-space-iris/&quot;&gt;Iris
Nebula&lt;/a&gt; at 3h 50m.&lt;/li&gt;
&lt;li&gt;darks&lt;/li&gt;
&lt;li&gt;biases&lt;/li&gt;
&lt;li&gt;flats separate for each night&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The gear:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;iOptron GEM28 mount&lt;/li&gt;
&lt;li&gt;QHY168C cooled astro camera&lt;/li&gt;
&lt;li&gt;Irix 150mm f/2.8 lens&lt;/li&gt;
&lt;li&gt;Astrolink 4 AP controller&lt;/li&gt;
&lt;li&gt;Raspberry Pi 5&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Software:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Stellarmate OS on the Pi&lt;/li&gt;
&lt;li&gt;Pixinsight&lt;/li&gt;
&lt;li&gt;GIMP&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Editing in Pix:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;integration in WBPP, took more than two hours on my Ryzen 1600X&lt;/li&gt;
&lt;li&gt;Dynamic Background Extraction&lt;/li&gt;
&lt;li&gt;Background Neutralizaion&lt;/li&gt;
&lt;li&gt;SpectroPhotometricColorCalibration&lt;/li&gt;
&lt;li&gt;MultiscaleAdaptiveStrech&lt;/li&gt;
&lt;li&gt;SCNR&lt;/li&gt;
&lt;li&gt;BlurExterminator&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Finally a sprinkle of Curves, Levels and Saturation in GIMP.&lt;/p&gt;
&lt;p&gt;My Pix skills are extremely simple, I need to do more tutorials and reading on
it, I&#39;m sure that with the right skills I could pull out more of the data that I
have.&lt;/p&gt;
&lt;h2&gt;Bottom Line&lt;/h2&gt;
&lt;p&gt;And that&#39;s basically it, I feel that I am opening a totally new chapter in my AP
journey, and from now on taking new photos will be much easier and I will be
much more willing to do so.&lt;/p&gt;
&lt;p&gt;Summer is coming quickly and with it the nights too short to do AP, but I am
hoping that before that I will be able to capture a bit of Cygnus, I&#39;ve been
planning to do a proper shoot of the Veil for a long time now.&lt;/p&gt;
&lt;p&gt;Thanks for reading!&lt;/p&gt;
</description>
      <pubDate>Sat, 11 Apr 2026 24:00:00 GMT</pubDate>
      <dc:creator>STFN</dc:creator>
      <guid>https://stfn.pl/blog/96-markarians-chain/</guid>
    </item>
  </channel>
</rss>