<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>Wouter's blog</title>
        <link>https://wouterds.com</link>
        <description>My personal blog where I write about software development, side projects, travel, and other random stuff that I find interesting.</description>
        <lastBuildDate>Mon, 10 Feb 2025 20:53:17 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/wouterds/wouterds.com</generator>
        <image>
            <title>Wouter's blog</title>
            <url>https://wouterds.com/favicon.svg</url>
            <link>https://wouterds.com</link>
        </image>
        <copyright>© 2026 Wouter De Schuyter</copyright>
        <category>Software Development</category>
        <category>Web Development</category>
        <category>Web3</category>
        <category>Travel</category>
        <category>Photography</category>
        <category>Personal</category>
        <item>
            <title><![CDATA[Auto switch Node.js version using NVM & ZSH hooks]]></title>
            <link>https://wouterds.com/blog/auto-switch-nodejs-version-using-nvm-zsh-hooks</link>
            <guid>https://wouterds.com/blog/auto-switch-nodejs-version-using-nvm-zsh-hooks</guid>
            <pubDate>Mon, 10 Feb 2025 20:53:17 GMT</pubDate>
            <description><![CDATA[I'm using Ghostty with ZSH + OhMyZsh as my terminal setup. In all my projects I add an .nvmrc file to pin the Node.js version in code. This allows me to use the same version in GitHub Actions and easily match the Node.js version used in…]]></description>
            <content:encoded><![CDATA[<p>I&apos;m using Ghostty with ZSH + OhMyZsh as my terminal setup. In all my projects I add an <code>.nvmrc</code> file to pin the Node.js version in code. This allows me to use the same version in GitHub Actions and easily match the Node.js version used in the production Docker images.</p><p>Switching Node.js versions manually is as simple as running:</p><pre data-language="bash"><code>nvm install</code></pre><p>However, it would be more convenient if this happened automatically when changing directories or opening a terminal in a specific project folder. This behavior can be achieved using ZSH hooks.</p><p>Add the following snippet to your <code>.zshrc</code> file:</p><pre data-language="bash"><code># Auto-switch Node.js versions when .nvmrc is present
autoload -U add-zsh-hook

load_nvm_version() {
  if [[ -f .nvmrc ]]; then
    nvm use || nvm install
  fi
}

add-zsh-hook chpwd load_nvm_version
load_nvm_version</code></pre><p>With this setup, your Node.js environment will always match the expected version without manual intervention. Bonus: this should work in the built-in terminal of your editor of choice as well.</p><p><img src="https://wouterds.com/images/116253/1739194876-auto-switch-node-ezgif-com-optimize.gif" width="100%"></p><h2>Update 12/02/2025</h2><p>Someone pointed out in the comments the existence of <a target="_blank" href="https://github.com/Schniz/fnm">fnm</a>. It&apos;s a drop-in replacement for nvm, written in rust. It&apos;s faster and changes nvm versions out of the box when going into a folder that contains an <code>.nvmrc</code> file. I caved and uninstalled nvm, and removed the custom ZSH hook... 😅</p><p>Installing <code>fnm</code> on macOS is easy, run <code>brew install fnm</code> and add the following to your <code>.zshrc</code>:</p><pre data-language="bash"><code>eval &quot;$(fnm env --use-on-cd --shell zsh)&quot;</code></pre><p>Bonus: add an alias to prevent annoying typos <code>alias nvm=&apos;fnm&apos;</code></p>]]></content:encoded>
            <enclosure url="https://wouterds.com/images/116253/1739194159-screenshot-2025-02-10-at-14-29-12.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[The self radicalization of a tech icon, a sad story]]></title>
            <link>https://wouterds.com/blog/the-self-radicalization-of-a-tech-icon-a-sad-story</link>
            <guid>https://wouterds.com/blog/the-self-radicalization-of-a-tech-icon-a-sad-story</guid>
            <pubDate>Tue, 04 Feb 2025 20:33:18 GMT</pubDate>
            <description><![CDATA[There was a time when Elon Musk was seen as a visionary – a relentless force pushing industries forward. SpaceX made reusable rockets a reality. Tesla proved that EVs could be desirable. He didn't invent these ideas, but he did build the…]]></description>
            <content:encoded><![CDATA[<p>There was a time when Elon Musk was seen as a visionary – a relentless force pushing industries forward. SpaceX made reusable rockets a reality. Tesla proved that EVs could be desirable. He didn&apos;t invent these ideas, but he did build the companies by attracting and inspiring the right people, carrying out a vision and making them viable businesses.</p><p>The Musk of today, though, is a sad story. What was once bold leadership, although argued questionable by many, has morphed into provocation for its own sake. He cozies up to extremists, spreads conspiracy theories, tries to influence foreign politics and elections, and bullies and silences critics while claiming to champion free speech. His companies still build great products, but the man himself is walking controversy – to say the least.</p><p><img src="https://wouterds.com/images/116253/1738624970-msk5.jpg" width="100%"></p><p>It&apos;s a strange legacy: to have revolutionized entire industries, only to ruin it all in real time. For many, admiration has turned to unease. Owning a Tesla, using X, supporting anything he touches – it all carries a weight that didn’t exist before. And the more he spirals, the harder it gets to separate the product from the person, which is unironically a daily reality.</p><p>In a parallel universe Musk inspired entire generations about science, space exploration, EVs and using tech to help save the climate. It&apos;s just so very sad, it is not the one we live in.</p>]]></content:encoded>
            <enclosure url="https://wouterds.com/images/116253/1738624970-msk5.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Moving to wouterds.com: a fresh start]]></title>
            <link>https://wouterds.com/blog/moving-to-wouterds-com-a-fresh-start</link>
            <guid>https://wouterds.com/blog/moving-to-wouterds-com-a-fresh-start</guid>
            <pubDate>Sat, 01 Feb 2025 08:39:00 GMT</pubDate>
            <description><![CDATA[After 15 years of wouterds.be, I've made the switch to wouterds.com. It's not my first domain change though, and I briefly moved to wouterdeschuyter.com (& wouterdeschuyter.be) before, but that never really stuck. Now, with more and more…]]></description>
            <content:encoded><![CDATA[<p>After 15 years of wouterds.be, I&apos;ve made the switch to wouterds.com. It&apos;s not my first domain change though, and I briefly moved to wouterdeschuyter.com (&amp; wouterdeschuyter.be) before, but that never really stuck. Now, with more and more people returning to personal blogs on their own domains, and especially with the rise of Bluesky &amp; the increasingly fragmented social media landscape, I figured it was time for a fresh start.</p><p><img src="https://wouterds.com/images/116253/1738368412-og-fresh.jpg" width="100%"></p><p>Over the years, I&apos;ve blogged on and off, sometimes about technical topics, sometimes more personal. I&apos;ve deleted a bunch of posts along the way and never really stuck to a regular schedule. This time around, I plan to change that. I&apos;m also setting up a dedicated photography page – while I enjoy sharing pictures, I don&apos;t always feel the need to write about them.</p><p>The internet is shifting again. More people are reclaiming their own space online, moving away from algorithmic feeds and walled gardens. That&apos;s something I&apos;ve always believed in, and having a domain that feels right is part of that.</p><p>I&apos;ve always liked knowing the ins and outs of hosting my own website. Over the years, I&apos;ve run it on a Raspberry Pi, then moved to DigitalOcean for a while, but I never found all-in-one cloud solutions appealing. I prefer having full control rather than relying on platforms that abstract everything away. I also just enjoy the process of developing it and getting to know how it&apos;s done.</p><p>Now the site is back home, on my own mini server. An Asus NUC 14 Pro with a 2TB SSD, 64GB of RAM, and a 22-core 4GHz Intel CPU running Linux Debian 12. Overkill for a simple blog? You bet! But I also host a bunch other websites &amp; side projects on it too, so it definitely earns its keep.</p><p><img src="https://wouterds.com/images/116253/1738367699-home-server.jpg" width="100%"></p><p>So here&apos;s to a new chapter – let&apos;s see where this goes!</p>]]></content:encoded>
            <enclosure url="https://wouterds.com/images/116253/1738368412-og-fresh.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[ASUS NUC 14 Pro stuck on black screen after reboot]]></title>
            <link>https://wouterds.com/blog/asus-nuc-14-pro-stuck-on-black-screen-after-reboot</link>
            <guid>https://wouterds.com/blog/asus-nuc-14-pro-stuck-on-black-screen-after-reboot</guid>
            <pubDate>Thu, 30 Jan 2025 20:00:00 GMT</pubDate>
            <description><![CDATA[I recently set up an ASUS NUC 14 Pro as a mini home server, but I ran into an issue where it refused to reboot, or to be more specific it would be stuck. A shutdown and manual power-on worked fine, but any attempt to restart resulted in…]]></description>
            <content:encoded><![CDATA[<p>I recently set up an ASUS NUC 14 Pro as a mini home server, but I ran into an issue where it refused to reboot, or to be more specific it would be stuck. A shutdown and manual power-on worked fine, but any attempt to restart resulted in a black screen with no response.</p><p><img src="https://wouterds.com/images/116253/1738267015-black-screen.jpg" width="100%"></p><p>After some troubleshooting, I narrowed the problem down to the storage drive. The NUC was running with a <strong>Crucial 2TB P3 Plus NVMe PCIe 4.0 M.2 SSD</strong>, which seemed to prevent proper reboots. Swapping it for a <strong>Samsung 990 PRO 2TB PCIe 4.0 M.2 SSD</strong> instantly resolved the issue – reboots now work without any problems.</p><p><img src="https://wouterds.com/images/116253/1738267205-img_6209.jpg" width="100%"></p><p>If you&apos;re facing similar reboot failures with an ASUS NUC 14 Pro, best check your NVMe SSD. Some models, like the Crucial P3 Plus, might have compatibility issues. A drive swap could save you a lot of headaches (and money).</p>]]></content:encoded>
            <enclosure url="https://wouterds.com/images/116253/1738265639-img_6208.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Code highlighting with Shiki & keeping your Cloudflare worker bundle small]]></title>
            <link>https://wouterds.com/blog/code-highlighting-with-shiki-keeping-your-cloudflare-worker-bundle-small</link>
            <guid>https://wouterds.com/blog/code-highlighting-with-shiki-keeping-your-cloudflare-worker-bundle-small</guid>
            <pubDate>Fri, 17 May 2024 15:30:00 GMT</pubDate>
            <description><![CDATA[Shiki is a powerful & flexible code highlighter that's fully ESM compatible, making it ideal for web use since it can be tree-shaken, loading only the necessary parts as needed.
Looks straightforward to me,  npm install shiki, import…]]></description>
            <content:encoded><![CDATA[<p>Shiki is a powerful &amp; flexible code highlighter that&apos;s fully ESM compatible, making it ideal for web use since it can be tree-shaken, loading only the necessary parts as needed.</p><p>Looks straightforward to me,  <code>npm install shiki</code>, import <code>codeToHtml</code>, done!</p><p>We now have this basic <code>Code</code> component that looks something like this:</p><pre data-language="tsx"><code>import { useEffect, useState } from &apos;react&apos;;
import { BundledLanguage, codeToHtml } from &apos;shiki&apos;;

export interface CodeProps {
  children?: string;
  lang: BundledLanguage;
}

export const Code = (props: CodeProps) =&gt; {
  const { children: code, lang } = props;

  const [highlightedCode, setHighlightedCode] = useState&lt;string&gt;();
  useEffect(() =&gt; {
    if (!code) {
      return;
    }

    codeToHtml(code, { lang, theme: &apos;dracula&apos; }).then(setHighlightedCode);
  }, [code, lang]);

  if (!code) {
    return null;
  }

  return (
    &lt;pre className=&quot;bg-slate-800 text-slate-400 p-4 rounded overflow-x-auto&quot;&gt;
      {highlightedCode ? (
        &lt;code dangerouslySetInnerHTML={{ __html: highlightedCode }} /&gt;
      ) : (
        &lt;code&gt;{code}&lt;/code&gt;
      )}
    &lt;/pre&gt;
  );
};</code></pre><p>And can be used as follows <code>&lt;Code lang=&quot;javascript&quot;&gt;console.log(&apos;Hello world&apos;)&lt;/Code&gt;</code>.</p><p>It will first render the code as raw text, and once the client side bundles are loaded &amp; executing it will replace it with the highlighted html code. Great, works perfectly fine (so I thought). And I can see in the inspector it&apos;s only loading what it needs, that&apos;s just an additional <code>288 KB</code> over the wire!</p><p><img src="https://wouterds.com/images/116253/1715955057-screenshot-2024-05-17-at-11-21-13.jpg" width="100%"><br><img src="https://wouterds.com/images/116253/1715950848-screenshot-2024-05-17-at-11-21-20.jpg" width="100%"></p><h2>Cloudflare deploy successful, but nothing to see?</h2><p>The CI showed successful deployment logs, but the page kept on showing &quot;Nothing is here yet&quot;. The function logs in the Cloudflare console also didn&apos;t reveal much more besides <code>An unknown error occured. (Code: 8000068)</code>. That&apos;s weird huh!?</p><p><img src="https://wouterds.com/images/116253/1715951521-screenshot-2024-05-17-at-11-03-56.jpg" width="100%"><br><img src="https://wouterds.com/images/116253/1715951559-screenshot-2024-05-17-at-15-10-04.jpg" width="100%"></p><p>After some time I came <a target="_blank" href="https://github.com/cloudflare/workers-sdk/pull/5819">across this PR</a> that would introduce better feedback in the <code>wrangler</code> CLI tool, that&apos;s also what I use to deploy. So I tried building locally and deploying with the updated version (3.57) and - surprise surprise - it did show an error!</p><pre><code>🌍  Uploading... (87/286)
✨ Success! Uploaded 87 files (199 already uploaded) (2.06 sec)
✨ Uploading _headers
✨ Uploading _redirects
✨ Uploading Functions bundle

🌎 Deploying...
✘ [ERROR] Failed to publish your Function. Got error: Your Functions script is over the 1 MiB size limit (workers.api.error.script_too_large)

❌ Deployment failed!</code></pre><p>I never really gave the worker bundle size a lot of thought, and focused mostly on keeping the client light. The worker bundle is the function &amp; essentially our server side application. While the client only loads what it needs when it&apos;s used, all those files need to be bundled in the function in order to <em>maybe</em> serve them when they are requested.</p><p>The build folder size also substantially increased from <code>~1 MB</code>  to <code>~10 MB</code> . Important to note though; this is full Vite build output containing all client &amp; server files, not necessarily the worker bundle.</p><pre data-language="bash"><code># without shiki implementation
➜ du -sh build
1.2M	build

# with shiki implementation
➜ du -sh build
9.7M	build</code></pre><h2>A more fine-grained bundle</h2><p>Going back to the <a target="_blank" href="https://shiki.style">docs</a> I noticed a section about bundles sizes, more fine-grained bundles and even specifically Cloudflare Workers. Although that last section was more focused on generated highlighted html code server side, which is not exactly what we want to do here.</p><p>I changed the implementation to use the slimmed down core highlighter and explicitly define which themes and languages we want to support. Not ideal if you work with a CMS and and just want to be able to reference whatever you like without changing your code. But I figured I&apos;ll just include some sensibles that I assume I will be using mostly.</p><p>The <code>Code</code> component is now a bit more explicit and bit less flexible and looks like this:</p><pre data-language="tsx"><code>import { useEffect, useState } from &apos;react&apos;;
import { BundledLanguage } from &apos;shiki&apos;;

export interface CodeProps {
  children?: string;
  lang: BundledLanguage;
}

export const Code = (props: CodeProps) =&gt; {
  const { children: code, lang } = props;

  const [highlightedCode, setHighlightedCode] = useState&lt;string&gt;();
  useEffect(() =&gt; {
    if (!code) {
      return;
    }

    // https://shiki.style/guide/install#fine-grained-bundle
    import(&apos;shiki/core&apos;).then(async ({ getHighlighterCore }) =&gt; {
      const highlighter = await getHighlighterCore({
        themes: [
          import(&apos;shiki/themes/dracula.mjs&apos;),
        ],
        langs: [
          import(&apos;shiki/langs/javascript.mjs&apos;),
          import(&apos;shiki/langs/typescript.mjs&apos;),
          import(&apos;shiki/langs/json.mjs&apos;),
          import(&apos;shiki/langs/shell&apos;),
          import(&apos;shiki/langs/console.mjs&apos;),
        ],
        loadWasm: await import(&apos;shiki/wasm&apos;),
      });

      setHighlightedCode(highlighter.codeToHtml(code, { lang, theme: &apos;dracula&apos; }));
    });
  }, [code, lang]);

  if (!code) {
    return null;
  }

  return (
    &lt;pre className=&quot;bg-slate-800 text-slate-400 p-4 rounded overflow-x-auto&quot;&gt;
      {highlightedCode ? (
        &lt;code dangerouslySetInnerHTML={{ __html: highlightedCode }} /&gt;
      ) : (
        &lt;code&gt;{code}&lt;/code&gt;
      )}
    &lt;/pre&gt;
  );
};</code></pre><p>Awesome, still works! Let&apos;s deploy it and see how it looks on Cloudflare.</p><p><img src="https://wouterds.com/images/116253/1715955886-screenshot-2024-05-17-at-11-23-42.jpg" width="100%"><br><img src="https://wouterds.com/images/116253/1715956022-1mydq3.jpg" width="100%"></p><p>...</p><p>OK, let&apos;s try to build &amp; deploy locally again.</p><p><code>✘ [ERROR] Failed to publish your Function. Got error: Your Functions script is over the 1 MiB size limit (workers.api.error.script_too_large)</code></p><pre data-language="shell"><code># with fine-grained shiki implementation
➜ du -sh build
2.3M	build</code></pre><p>The final build folder output decreased to <code>~2 MB</code> but the resulting worker bundle apparently was still too large. I tried removing all languages but 1, but the issue persisted. Probably if I would strip enough code from the app and remove some other dependencies I could <em>maybe</em> get it below the 1 MB limit? Not really an option.</p><h2>Using esm.sh CDN</h2><p>The docs also mentioned using Shiki from CDN such as <a target="_blank" href="https://esm.sh">esm.sh</a>. But what is esm.sh?</p><blockquote><p>It&apos;s designed to provide an easy way to use ECMAScript modules (ESM) from npm in web browsers. It acts as a CDN (Content Delivery Network) that allows developers to import JavaScript modules directly into their browser applications without needing to install them locally or bundle them using a tool like Webpack.</p><p>Key features and benefits of esm.sh include:</p><p><strong>ESM Compatibility</strong>: esm.sh ensures that npm packages are transformed into ES modules that can be natively imported by modern browsers.</p><p><strong>Versioning</strong>: Developers can specify which version of a package they want to use, similar to how npm works.</p><p><strong>CDN Performance</strong>: As a CDN, esm.sh provides fast, cached access to modules, improving load times and reducing server load.</p><p><strong>Automatic Transpilation</strong>: It handles the transpilation of CommonJS and other module formats to ESM, making it easier to use packages that aren&apos;t originally authored as ES modules.</p><footer>according to Chad (GEE-PEE-TEE)</footer></blockquote><p>Not as in loading 1 big bloated file and accessing through <code>window</code>, but really by embedding it as a module and relying on ESM imports using <code>&lt;script type=&quot;module&quot;&gt;...&lt;/script&gt;</code>. That doesn&apos;t (really) work with React, even though you can render a script module within your component, you&apos;ll end up with a lot of unpredictable behaviour and the code not executing when you expect it to.</p><p>The framework I&apos;m using however, Remix (any modern web framework really) also loads its client bundles through modules. So I&apos;m guessing I can make use of similar code within my React component itself without trying to render a separate script module within it?</p><p>Attempt 3 &amp; a refactor later our <code>Code</code> component now is again super flexible supporting any theme and any language, while it has (virtually) 0 impact on our client &amp; server bundle, it now looks like this:</p><pre data-language="tsx"><code>import { useEffect, useState } from &apos;react&apos;;
import { BundledLanguage } from &apos;shiki&apos;;

export interface CodeProps {
  children?: string;
  lang: BundledLanguage;
}

export const Code = (props: CodeProps) =&gt; {
  const { children: code, lang } = props;

  const [highlightedCode, setHighlightedCode] = useState&lt;string&gt;();
  useEffect(() =&gt; {
    if (!code) {
      return;
    }

    // @ts-expect-error: import from esm.sh to avoid large worker bundle
    import(&apos;https://esm.sh/shiki@1.5.2&apos;).then(async ({ codeToHtml }) =&gt; {
      setHighlightedCode(await codeToHtml(code, { lang, theme: &apos;dracula&apos; }));
    });
  }, [code, lang]);

  if (!code) {
    return null;
  }

  return (
    &lt;pre className=&quot;bg-slate-800 text-slate-400 p-4 rounded overflow-x-auto&quot;&gt;
      {highlightedCode ? (
        &lt;code dangerouslySetInnerHTML={{ __html: highlightedCode }} /&gt;
      ) : (
        &lt;code&gt;{code}&lt;/code&gt;
      )}
    &lt;/pre&gt;
  );
};</code></pre><p>It works, and this time it also deploys to Cloudflare! Our own server &amp; client bundle remains unaffected and Shiki is loaded from a blazingly fast CDN, resulting in even a smaller size than when it was bundled as ESM module originally, with only <code>279 KB</code> extra over the wire.</p><p><img src="https://wouterds.com/images/116253/1715950165-screenshot-2024-05-17-at-11-32-23.jpg" width="100%"><br><img src="https://wouterds.com/images/116253/1715958015-screenshot-2024-05-17-at-11-32-25.jpg" width="100%"></p><p>The only downside is that you don&apos;t have types out of the box when using TypeScript with esm.sh.</p><h2>Conclusion</h2><p>For this type of package &amp; use-case I think it makes perfect sense to load it from CDN. Noting that this would not have been an issue if you deploy your web app as a Node container or use some other more classic way of hosting Node.js web apps.</p><p>But personally I like the idea of &quot;not having a server&quot; &amp; deploying my website to a distributed cloud function with fast response times across the globe regardless of users their geographical location. It&apos;s fast, free, lightweight, you have preview deploys and it requires 0 maintenance or follow up.</p><p>Demo: <a target="_blank" href="https://3feb5f78.wouterds.pages.dev/example-code-block">https://3feb5f78.wouterds.pages.dev/example-code-block</a></p><p>If <code>wrangler</code> would have failed fast to begin things would have been clearer earlier on. Luckily that PR is merged now and as of v3.57 it will fail fast.</p>]]></content:encoded>
            <enclosure url="https://wouterds.com/images/116253/1715949450-shiki-og.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Belfry of Ghent aerial video]]></title>
            <link>https://wouterds.com/blog/belfry-of-ghent-aerial-video</link>
            <guid>https://wouterds.com/blog/belfry-of-ghent-aerial-video</guid>
            <pubDate>Thu, 09 May 2024 12:21:00 GMT</pubDate>
            <description><![CDATA[Inspired by the impressive footage of @travelformotion I tried a short fly-by of the Belfry of Ghent as well with my DJI Mini 2. I was hoping for low hanging fog to achieve towers peaking above but was unlucky. Ended up trying again a…]]></description>
            <content:encoded><![CDATA[<p>Inspired by the impressive footage of <a target="_blank" href="https://www.travelformotion.com">@travelformotion</a> I tried a short fly-by of the Belfry of Ghent as well with my DJI Mini 2. I was hoping for low hanging fog to achieve towers peaking above but was unlucky. Ended up trying again a few hours later when fog was starting to clear in favor of some sun. Such a beautiful building!</p><iframe src="https://youtube.com/embed/4d-FzDFMgQo" width="100%" style="aspect-ratio:16/9"></iframe>]]></content:encoded>
            <enclosure url="https://wouterds.com/images/116253/1739111145-maxresdefault.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Flying over the North Pole]]></title>
            <link>https://wouterds.com/blog/flying-over-the-north-pole</link>
            <guid>https://wouterds.com/blog/flying-over-the-north-pole</guid>
            <pubDate>Sun, 14 Apr 2024 17:30:00 GMT</pubDate>
            <description><![CDATA[On the way back from Japan our flight went over the North Pole as the sun was coming up. At first it wasn't really clear to me what I was looking at, but after taking a few pics on high ISO & massively increasing exposure it hit me that…]]></description>
            <content:encoded><![CDATA[<p>On the way back from Japan our flight went over the North Pole as the sun was coming up. At first it wasn&apos;t really clear to me what I was looking at, but after taking a few pics on high ISO &amp; massively increasing exposure it hit me that we were flying above ice.</p><p>This was a different route that the one we took when flying to Japan. As time elapsed and we got more sun I could increasingly see better the seemingly infinite arctic surface.</p><p>Found it really impressive to see, even from 35.000 feet in the air.</p><p><img src="https://wouterds.com/images/116253/1713547359-arctic-1.jpg" width="100%"><br><img src="https://wouterds.com/images/116253/1713547350-arctic-3.jpg" width="100%"><br><img src="https://wouterds.com/images/116253/1713547037-arctic-2.jpg" width="100%"><br><img src="https://wouterds.com/images/116253/1713547338-arctic-6.jpg" width="100%"><br><img src="https://wouterds.com/images/116253/1713547367-arctic-5.jpg" width="100%"></p><p>Fujifilm X-S10, Fujinon XF18-55mm F2.8<br>Edit: Adobe Lightroom</p>]]></content:encoded>
            <enclosure url="https://wouterds.com/images/116253/1713547316-arctic-og.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Japan 2024: Hiroshima]]></title>
            <link>https://wouterds.com/blog/japan-2024-hiroshima</link>
            <guid>https://wouterds.com/blog/japan-2024-hiroshima</guid>
            <pubDate>Fri, 12 Apr 2024 11:00:00 GMT</pubDate>
            <description><![CDATA[The last place we visited during our stay in Japan was Hiroshima. Taking the Shinkansen, it took us about 2 hours from Osaka, and we stayed in Hiroshima for one night. It's true that Hiroshima itself is not very big and there is not much…]]></description>
            <content:encoded><![CDATA[<p>The last place we visited during our stay in Japan was Hiroshima. Taking the Shinkansen, it took us about 2 hours from Osaka, and we stayed in Hiroshima for one night. It&apos;s true that Hiroshima itself is not very big and there is not much to see besides the Atomic bombing memorials, but overall I would definitely recommend it!</p><p>Around Hiroshima, there are some more things to see that we didn&apos;t have time for in the end, such as the Itsukushima Shrine, which is located on the shores of Miyajima, not too far from Hiroshima. As well as some smaller islands &amp; hikes/nature around the area.</p><p>One of the only buildings that was left standing after the Atomic Bomb dropped on Hiroshima on August 6th, 1945, was what is now called &quot;the Atomic Bomb Dome.&quot; While most structures at the time were wooden, this building was made of stone, mortar, steel, and copper. Back then, it was famous for being so unique and prestigious.</p><blockquote><p>The Atomic Bomb Dome was built in 1915 as a facility to exhibit and sell products from Hiroshima prefecture. It also held Hiroshima prefectural art exhibitions. At the beginning of its establishment, it was called “Hiroshima Prefectural Products Display Hall,” but later it was renamed “Hiroshima Prefectural Product Display Center” and “Hiroshima Prefectural Industrial Promotion Hall.”</p><p>The first atomic bomb in human history exploded at an altitude of about 160 meters southeast of the Hiroshima Prefectural Industrial Promotion Hall and about 600 meters from it. The pressure of the blast was 35 tons per square meter and the wind speed was 440 meters per second. The building was wrecked by the blast and heat rays and burned down by the ensuing fire. Since the blast worked almost vertically, the central part of the main building miraculously escaped collapse, but everyone who was in the building died instantly. After the war, the remnants of the former Industrial Promotion Hall came to be called the Atomic Bomb Dome by citizens due to the shape of the top canal and steel frame.</p></blockquote><p>Near the Hiroshima Castle, which was largely destroyed by the bombing, there is a tree left standing that survived the bombing - a &quot;Hibaku Jumoku&quot; (survivor tree). This weeping willow is the closest tree to the hypocenter (370m) that survived the blast.</p><p>Today in Hiroshima, there is nothing besides the peace memorials that would indicate the city was once wiped away by an atomic bomb, just 80 years ago. Overall, I found it very impressive and humbling to see and walk around the city now.</p><p>PS: while you&apos;re in Hiroshima I would definitely recommend the traditional okonomiyaki, but don&apos;t go to that 1 building with 20 okonomiyaki restaurants in it, there are literally only tourists inside. Instead go to this place across the road called &quot;Shintenchi Mitchan&quot;, it&apos;s authentic &amp; super delicious! We spent about 2000¥ a person (iirc).</p><p><img src="https://wouterds.com/images/116253/1716214533-dscf4573.jpg" width="100%"><br><img src="https://wouterds.com/images/116253/1716214505-dscf4544.jpg" width="100%"><br><img src="https://wouterds.com/images/116253/1716214456-dscf4584.jpg" width="100%"><br><img src="https://wouterds.com/images/116253/1716214496-dscf4587.jpg" width="100%"><br><img src="https://wouterds.com/images/116253/1716214591-dscf4598.jpg" width="100%"><br><img src="https://wouterds.com/images/116253/1716214630-dscf4609.jpg" width="100%"><br><img src="https://wouterds.com/images/116253/1716214446-dscf4604.jpg" width="100%"><br><img src="https://wouterds.com/images/116253/1716214618-dscf4589.jpg" width="100%"><br><img src="https://wouterds.com/images/116253/1716214488-dscf4619.jpg" width="100%"><br><img src="https://wouterds.com/images/116253/1716214611-dscf4600-l.jpg" width="100%"><br><img src="https://wouterds.com/images/116253/1716226416-dscf5924.jpg" width="100%"><br><img src="https://wouterds.com/images/116253/1716214470-dscf4629.jpg" width="100%"><br><img src="https://wouterds.com/images/116253/1716214605-dscf4630.jpg" width="100%"><br><img src="https://wouterds.com/images/116253/1716226409-dscf5917.jpg" width="100%"><br><img src="https://wouterds.com/images/116253/1716214599-dscf4633.jpg" width="100%"><br><img src="https://wouterds.com/images/116253/1716214637-dscf4654.jpg" width="100%"><br><img src="https://wouterds.com/images/116253/1716214515-dscf4666.jpg" width="100%"><br><img src="https://wouterds.com/images/116253/1716214624-dscf4661.jpg" width="100%"><br><img src="https://wouterds.com/images/116253/1716214479-dscf4639.jpg" width="100%"><br><img src="https://wouterds.com/images/116253/1716214463-dscf4652.jpg" width="100%"></p>]]></content:encoded>
            <enclosure url="https://wouterds.com/images/116253/1716214380-hiroshima-og.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Lichtfestival Gent, 2024 edition]]></title>
            <link>https://wouterds.com/blog/lichtfestival-gent-2024-edition</link>
            <guid>https://wouterds.com/blog/lichtfestival-gent-2024-edition</guid>
            <pubDate>Sun, 04 Feb 2024 12:34:00 GMT</pubDate>
            <description><![CDATA[Every four years, Ghent lights up with art and culture. We walked a good part of the 7km illuminated trail but didn't finish it. This year was a mix - some parts were cool, but others weren't that impressive. Still, I always enjoy the…]]></description>
            <content:encoded><![CDATA[<p>Every four years, Ghent lights up with art and culture. We walked a good part of the 7km illuminated trail but didn&apos;t finish it. This year was a mix - some parts were cool, but others weren&apos;t that impressive. Still, I always enjoy the night stroll through the city!</p><p><img src="https://wouterds.com/images/116253/1707050170-dscf1010.jpg" width="100%"><br><img src="https://wouterds.com/images/116253/1707050139-dscf1056.jpg" width="100%"><br><img src="https://wouterds.com/images/116253/1707050158-dscf1064.jpg" width="100%"><br><img src="https://wouterds.com/images/116253/1707050107-dscf1290.jpg" width="100%"><br><img src="https://wouterds.com/images/116253/1707050145-dscf1159.jpg" width="100%"><br><img src="https://wouterds.com/images/116253/1707050134-dscf1172.jpg" width="100%"><br><img src="https://wouterds.com/images/116253/1707050113-dscf1276.jpg" width="100%"><br><img src="https://wouterds.com/images/116253/1707050118-dscf1261.jpg" width="100%"><br><img src="https://wouterds.com/images/116253/1707050165-dscf1095.jpg" width="100%"><br><img src="https://wouterds.com/images/116253/1707050098-dscf1300.jpg" width="100%"></p><p>Fujifilm X-S10, XF 23mm f2<br>Edit: Adobe Lightroom</p>]]></content:encoded>
            <enclosure url="https://wouterds.com/images/116253/1707050062-og.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Signal de Botrange, High Fens]]></title>
            <link>https://wouterds.com/blog/signal-de-botrange-high-fens</link>
            <guid>https://wouterds.com/blog/signal-de-botrange-high-fens</guid>
            <pubDate>Sun, 21 Jan 2024 20:18:31 GMT</pubDate>
            <description><![CDATA[To see a little bit of snow around here you need to be lucky, or drive to the highest place of Belgium. Since a couple of years this has now become a yearly tradition where we go for a day trip to the High Fens for some strolling through…]]></description>
            <content:encoded><![CDATA[<p>To see a little bit of snow around here you need to be lucky, or drive to the highest place of Belgium. Since a couple of years this has now become a yearly tradition where we go for a day trip to the High Fens for some strolling through the snow. There&apos;s just something magical about it, love it! ☃️</p><p><img src="https://wouterds.com/images/116253/1705868259-dscf0787.jpg" width="100%"><br><img src="https://wouterds.com/images/116253/1705868242-dscf0793.jpg" width="100%"><br><img src="https://wouterds.com/images/116253/1705868264-dscf0800.jpg" width="100%"><br><img src="https://wouterds.com/images/116253/1705868270-dscf0840.jpg" width="100%"><br><img src="https://wouterds.com/images/116253/1705868236-dscf0899.jpg" width="100%"></p><p>Fujifilm X-S10, XF 23mm f2<br>Edit: Adobe Lightroom</p>]]></content:encoded>
            <enclosure url="https://wouterds.com/images/116253/1705868229-og.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Ooidonk Castle night shots]]></title>
            <link>https://wouterds.com/blog/ooidonk-castle-night-shots</link>
            <guid>https://wouterds.com/blog/ooidonk-castle-night-shots</guid>
            <pubDate>Wed, 10 Jan 2024 17:37:00 GMT</pubDate>
            <description><![CDATA[Because I hate my limbs, I went out last night to take some photos of Ooidonk Castle at -7 ºC. While I would not recommend doing this, it did result it a few nice shots!
Fujifilm X-S10, XF 23mm f2
Edit: Adobe Lightroom
Location:…]]></description>
            <content:encoded><![CDATA[<p>Because I hate my limbs, I went out last night to take some photos of Ooidonk Castle at -7 ºC. While I would not recommend doing this, it did result it a few nice shots!</p><p><img src="https://wouterds.com/images/116253/1704908715-dscf0730.jpg" width="100%"><br><img src="https://wouterds.com/images/116253/1704908722-dscf0720.jpg" width="100%"><br><img src="https://wouterds.com/images/116253/1704908727-dscf0715-2.jpg" width="100%"></p><p>Fujifilm X-S10, XF 23mm f2<br>Edit: Adobe Lightroom<br>Location: 51.00279, 3.58592</p>]]></content:encoded>
            <enclosure url="https://wouterds.com/images/116253/1704908215-embed.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Visiting the capital around Christmas]]></title>
            <link>https://wouterds.com/blog/visiting-the-capital-around-christmas</link>
            <guid>https://wouterds.com/blog/visiting-the-capital-around-christmas</guid>
            <pubDate>Thu, 28 Dec 2023 21:24:00 GMT</pubDate>
            <description><![CDATA[Embracing the festive spirit, we went for a little visit to our capital for some sightseeing. We walked from the Beurs to the Grote Markt and had a look at the various Christmas markets and enjoyed some hot chocolate milk, and of course,…]]></description>
            <content:encoded><![CDATA[<p>Embracing the festive spirit, we went for a little visit to our capital for some sightseeing. We walked from the Beurs to the Grote Markt and had a look at the various Christmas markets and enjoyed some hot chocolate milk, and of course, an old fashioned bratwurst! 🌭</p><p><img src="https://wouterds.com/images/116253/1703802201-dscf0330.jpg" width="100%"><br><img src="https://wouterds.com/images/116253/1703802137-dscf0368.jpg" width="100%"><br><img src="https://wouterds.com/images/116253/1703802142-dscf0373.jpg" width="100%"><br><img src="https://wouterds.com/images/116253/1703802130-dscf0467.jpg" width="100%"><br><img src="https://wouterds.com/images/116253/1703802195-dscf0477.jpg" width="100%"><br><img src="https://wouterds.com/images/116253/1703802152-dscf0534.jpg" width="100%"><br><img src="https://wouterds.com/images/116253/1703802147-dscf0545.jpg" width="100%"><br><img src="https://wouterds.com/images/116253/1703802159-dscf0593.jpg" width="100%"><br><img src="https://wouterds.com/images/116253/1703802171-dscf0586.jpg" width="100%"></p>]]></content:encoded>
            <enclosure url="https://wouterds.com/images/116253/1703798161-brussels-embed.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[IM Cooling Tower Charleroi]]></title>
            <link>https://wouterds.com/blog/im-cooling-tower-charleroi</link>
            <guid>https://wouterds.com/blog/im-cooling-tower-charleroi</guid>
            <pubDate>Sun, 12 Jul 2020 18:48:24 GMT</pubDate>
            <description><![CDATA[I've read about the IM Power Plant years ago but never got around to visiting due to the considerable distance. Recently, while sorting through old photos, the urge to explore it resurfaced. After waiting for a rain-free weekend, today…]]></description>
            <content:encoded><![CDATA[<p>I&apos;ve read about the IM Power Plant years ago but never got around to visiting due to the considerable distance. Recently, while sorting through old photos, the urge to explore it resurfaced. After waiting for a rain-free weekend, today marked the perfect day for my 1.5-hour drive to Charleroi! Here&apos;s a glimpse of the cooling tower and its surroundings from my visit. Unfortunately, I didn&apos;t have time to explore the power plant across the water this time – perhaps on another time!</p><blockquote><p>Built in 1921 in Charleroi, Belgium, the IM cooling tower was part of one of the country&apos;s largest coal-fired power plants, aiding industrial growth in the interwar period. Owned by Electrabel, it supplied electricity and heat to six million people and was the primary energy source for greater Charleroi by 1977.</p><p>Utilizing wind to cool water during production peaks, the tower could handle up to 480,000 gallons per minute. In the 1970s, the plant transitioned from coal to gas, expanding to six units with a total capacity of 1 GW by 1990, earning the unofficial title &quot;Gigawatt Power Plant.&quot;</p><p>However, a 2006 Greenpeace report revealed the plant contributed to 10% of Belgium&apos;s CO2 emissions and harbored pathogenic bacteria, prompting its recognition as dangerous and non-convertible. In 2007, the plant ceased operations and was closed.</p></blockquote><p><img src="https://wouterds.com/images/116253/1703437517-im-cooling-tower-1.jpg" width="100%"><br><img src="https://wouterds.com/images/116253/1703437536-im-cooling-tower-2.jpg" width="100%"><br><img src="https://wouterds.com/images/116253/1703508391-5998eba4-3df1-4334-b079-3c65a285febe.jpg" width="100%"><br><img src="https://wouterds.com/images/116253/1703508478-2f087c46-8250-4c34-aefe-a264a69e67de.jpg" width="100%"><br><img src="https://wouterds.com/images/116253/1703508485-8d023a7a-e3b7-4ebe-9621-a1e0d704eb5a.jpg" width="100%"><br><img src="https://wouterds.com/images/116253/1703508490-c163dcd6-d192-43d6-9f04-0983e77c12c4.jpg" width="100%"><br><img src="https://wouterds.com/images/116253/1703508495-daa056d8-e4e9-4c3b-ae35-c07f2fb362a4.jpg" width="100%"><br><img src="https://wouterds.com/images/116253/1703508499-e2058dcf-10ab-40d4-ba9f-87f2f8d8dcf6.jpg" width="100%"><br><img src="https://wouterds.com/images/116253/1703508504-a6dd7b59-ede7-4043-b17c-77b9775da0cb.jpg" width="100%"><br><img src="https://wouterds.com/images/116253/1703508524-08b83f91-1b50-4b3e-a1ac-dc64712c285e.jpg" width="100%"><br><img src="https://wouterds.com/images/116253/1703508529-9fef69b5-d8a7-4796-94ba-06b4a15cc7ed.jpg" width="100%"><br><img src="https://wouterds.com/images/116253/1703508533-48e5a607-0364-4599-84cc-885c46b051d9.jpg" width="100%"><br><img src="https://wouterds.com/images/116253/1703508537-99c2464f-a736-453f-ac00-78fee2e19d9a.jpg" width="100%"><br><img src="https://wouterds.com/images/116253/1703508542-d6f23894-3c10-49c0-984e-996a6d67df86.jpg" width="100%"><br><img src="https://wouterds.com/images/116253/1703508548-b2c90b48-e127-4d65-a74a-488542efa2c3.jpg" width="100%"><br><img src="https://wouterds.com/images/116253/1703508553-3e97ac3d-df95-4c24-a073-88dcd1dc89d5.jpg" width="100%"><br><img src="https://wouterds.com/images/116253/1703508557-ca7e99d1-1cd9-4e2a-984e-cdeb1ac05217.jpg" width="100%"></p>]]></content:encoded>
            <enclosure url="https://wouterds.com/images/116253/1703671881-im-tower-og.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Got myself a Tinyhawk II FPV drone]]></title>
            <link>https://wouterds.com/blog/got-myself-a-tinyhawk-ii-fpv-drone</link>
            <guid>https://wouterds.com/blog/got-myself-a-tinyhawk-ii-fpv-drone</guid>
            <pubDate>Wed, 24 Jun 2020 14:39:00 GMT</pubDate>
            <description><![CDATA[I just got myself this mini drone; the Tinyhawk II. I already had some equipment from a couple of years back when I first got into FPV but fairly quickly gave up because it was too expensive at the time, I also found it super difficult…]]></description>
            <content:encoded><![CDATA[<p>I just got myself this mini drone; the Tinyhawk II. I already had some equipment from a couple of years back when I first got into FPV but fairly quickly gave up because it was too expensive at the time, I also found it super difficult to learn and there were little resources and documentation on the hobby.</p><p><img src="https://wouterds.com/images/116253/1739029005-0320adbb-a37f-4ecb-9bae-f13fbe0c861d.jpg" width="100%"></p><p>Now a couple of years later there are many more people into the hobby and way more info &amp; knowledge. There are also simulators to learn &amp; test and a wider &amp; more accessible range of products to get into it. This little drone for example was only a <a target="_blank" href="https://www.banggood.com/EMAX-Tinyhawk-II-75mm-1-2S-Whoop-FPV-Racing-Drone-BNF-FrSky-D8-Runcam-Nano2-Cam-25100200mw-VTX-5A-Blheli_S-ESC-p-1615887.html?zf=47470820&amp;_branch_match_id=805481357196890098&amp;cur_warehouse=CN">$100 on Banggood</a>. I think it&apos;s perfect for a beginner to get into the hobby as you can fly it indoors and because of its little bumpers you&apos;re less likely to have bad crashes.</p><p>I find it insanely cool to be able to fly &quot;first-person view&quot; and I can&apos;t wait to try outside (it also comes with a stronger battery that gives more power for outdoor flying)!</p>]]></content:encoded>
            <enclosure url="https://wouterds.com/images/116253/1739029332-8d4f0e22-427f-453d-afc4-067bd8a21b2b-embed.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Seoul, South Korea 2019]]></title>
            <link>https://wouterds.com/blog/seoul-south-korea-2019</link>
            <guid>https://wouterds.com/blog/seoul-south-korea-2019</guid>
            <pubDate>Sun, 15 Mar 2020 01:07:00 GMT</pubDate>
            <description><![CDATA[So, thanks to good old COVID-19, my Saturday plans went down the drain. But hey, silver lining alert – it gave me the perfect excuse to tackle some issues on my website and reminisce about my trip to Seoul last December. Picture this: me…]]></description>
            <content:encoded><![CDATA[<p>So, thanks to good old COVID-19, my Saturday plans went down the drain. But hey, silver lining alert – it gave me the perfect excuse to tackle some issues on my website and reminisce about my trip to Seoul last December. Picture this: me exploring the city, from the historic north to the flashy south (yes, Gangnam style), checking out temples, and diving into traditional neighborhoods.</p><p>Now, let&apos;s talk about the elephant in the Korean room – smog. I landed right smack in the middle of a smog alert. Picture handing out masks at the airport, minimal visibility, and an overall dystopian vibe. The first two days felt like I was breathing in a gray filter, but hey, things got better. I upgraded my mask game from KF80 to KF94 – not just for the cool factor, but because the government was sending out alerts that made me briefly think North Korea was launching missiles. Cue panic mode. And trust me, when you&apos;re on a bus, it&apos;s a symphony of alarms.</p><p>Public transport in Seoul? Absolutely fantastic. Cheap, efficient, and a unified payment system for buses, metro, and trains. I practically spent loose change on transport over five days. Sadly, my grand plan to visit the North Korean border got derailed last minute due to &quot;wild boar&quot; – or was it North Korea flexing its muscles with a missile test? Who knows. Despite the disappointment, Seoul was a blast, and I&apos;m already plotting a longer trip to Japan, with a pit stop in Seoul if the world ever recovers from this corona chaos. And oh, you&apos;re probably wondering about my trip pics by now, I sifted through over 600 – smog be damned – to give you the cream of the crop!</p><p><img src="https://wouterds.com/images/116253/1703632422-img_1800.jpeg" width="100%"><br><img src="https://wouterds.com/images/116253/1703632427-img_1801.jpeg" width="100%"><br><img src="https://wouterds.com/images/116253/1703632432-img_1802.jpeg" width="100%"><br><img src="https://wouterds.com/images/116253/1703632437-img_1803.jpeg" width="100%"><br><img src="https://wouterds.com/images/116253/1703632441-img_1804.jpeg" width="100%"><br><img src="https://wouterds.com/images/116253/1703632446-img_1805.jpeg" width="100%"><br><img src="https://wouterds.com/images/116253/1703632450-img_1806.jpeg" width="100%"><br><img src="https://wouterds.com/images/116253/1703632455-img_1807.jpeg" width="100%"><br><img src="https://wouterds.com/images/116253/1703632459-img_1808.jpeg" width="100%"><br><img src="https://wouterds.com/images/116253/1703632464-img_1809.jpeg" width="100%"><br><img src="https://wouterds.com/images/116253/1703632469-img_1810.jpeg" width="100%"><br><img src="https://wouterds.com/images/116253/1703632474-img_1811.jpeg" width="100%"><br><img src="https://wouterds.com/images/116253/1703632479-img_1812.jpeg" width="100%"><br><img src="https://wouterds.com/images/116253/1703632484-img_1813.jpeg" width="100%"><br><img src="https://wouterds.com/images/116253/1703632489-img_1814.jpeg" width="100%"><br><img src="https://wouterds.com/images/116253/1703632493-img_1815.jpeg" width="100%"><br><img src="https://wouterds.com/images/116253/1703632498-img_1816.jpeg" width="100%"><br><img src="https://wouterds.com/images/116253/1703632503-img_1817.jpeg" width="100%"><br><img src="https://wouterds.com/images/116253/1703632507-img_1818.jpeg" width="100%"><br><img src="https://wouterds.com/images/116253/1703632512-img_1819.jpeg" width="100%"><br><img src="https://wouterds.com/images/116253/1703632517-img_1820.jpeg" width="100%"><br><img src="https://wouterds.com/images/116253/1703632521-img_1821.jpeg" width="100%"><br><img src="https://wouterds.com/images/116253/1703632526-img_1822.jpeg" width="100%"><br><img src="https://wouterds.com/images/116253/1703632532-img_1823.jpeg" width="100%"><br><img src="https://wouterds.com/images/116253/1703632537-img_1824.jpeg" width="100%"><br><img src="https://wouterds.com/images/116253/1703632542-img_1825.jpeg" width="100%"><br><img src="https://wouterds.com/images/116253/1703632552-img_1826.jpeg" width="100%"><br><img src="https://wouterds.com/images/116253/1703632557-img_1827.jpeg" width="100%"><br><img src="https://wouterds.com/images/116253/1703632562-img_1828.jpeg" width="100%"><br><img src="https://wouterds.com/images/116253/1703632567-img_1829.jpeg" width="100%"><br><img src="https://wouterds.com/images/116253/1703632572-img_1830.jpeg" width="100%"><br><img src="https://wouterds.com/images/116253/1703632577-img_1831.jpeg" width="100%"><br><img src="https://wouterds.com/images/116253/1703632581-img_1832.jpeg" width="100%"><br><img src="https://wouterds.com/images/116253/1703632587-img_1833.jpeg" width="100%"><br><img src="https://wouterds.com/images/116253/1703632593-img_1834.jpeg" width="100%"></p>]]></content:encoded>
            <enclosure url="https://wouterds.com/images/116253/1703671965-seoul-og.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Fail early with Husky pre-commit hooks]]></title>
            <link>https://wouterds.com/blog/fail-early-with-husky-pre-commit-hooks</link>
            <guid>https://wouterds.com/blog/fail-early-with-husky-pre-commit-hooks</guid>
            <pubDate>Sat, 12 Oct 2019 09:47:00 GMT</pubDate>
            <description><![CDATA[Pre-commit hooks are nothing new, it's been around for ages. It allows you to hook into certain git events. The most popular one is definitely the so called pre-commit hook. It runs before your commit is created and it can possibly…]]></description>
            <content:encoded><![CDATA[<p>Pre-commit hooks are nothing new, it&apos;s been around for ages. It allows you to hook into certain git events. The most popular one is definitely the so called pre-commit hook. It runs before your commit is created and it can possibly prevent it even from being created.</p><p>Now this is nice isn&apos;t it? It&apos;s common to have some linters run as a pre-commit hook as they&apos;ll prevent you from committing 💩 code. Except there&apos;s one little issue with those git hooks though. They are not shared by default. Each team member would need to configure the git hooks manually. Usually this happens by following setup instructions in a README. And this creates another problem: <strong>it&apos;s not enforced.</strong> People might simply forget, read over it, re-clone there repo and skip setup...</p><h2>And because it&apos;s not enforced...</h2><p>We might end up in the scenario where you&apos;re reviewing a PR but CI has failed because somebody <em>&quot;didn&apos;t follow the rules&quot;</em>. And then you don&apos;t want to be that guy that rejects it, but you&apos;ll have to! The other dev then needs to incorporate the feedback, you need to review again. That&apos;s just a waste of time &amp; effort, and is polluting your git history. If we could only prevent this 😰...</p><h2>Husky to the rescue 🚑</h2><p>Luckily, for (Node)JS projects there&apos;s this <a href="https://www.npmjs.com/package/husky">package Husky</a>. It allows you to configure scripts as git hooks in your package.json and it can enforce this because it installs the commit hooks on postinstall. So all you really need to do is add Husky to your dev dependencies: <code>yarn add --dev husky</code>. And add something like the following configuration to your <code>package.json</code>.</p><pre data-language="json"><code>&quot;husky&quot;: {
  &quot;hooks&quot;: {
    &quot;pre-push&quot;: &quot;yarn lint&quot;
  }
}</code></pre><p>Now I went with <code>pre-push</code> because linting takes a few seconds and I don&apos;t like to wait a few seconds every commit I do. But I also don&apos;t mind rebasing those commits before I push them to clean them up if there would be issues. The important thing here is: it doesn&apos;t let me push 💩 code.</p><p><img src="https://wouterds.com/images/116253/1703683210-df353918-c78f-4c5d-a991-abec2efe33e6.gif" width="100%"></p><p>Now get to it and add Husky 🐶 to your project!</p>]]></content:encoded>
            <enclosure url="https://wouterds.com/images/116253/1703682974-husky-pre-commit-hooks.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Running a dedicated Quake III Arena server on Raspberry Pi with Docker]]></title>
            <link>https://wouterds.com/blog/running-a-dedicated-quake-iii-arena-server-on-raspberry-pi-with-docker</link>
            <guid>https://wouterds.com/blog/running-a-dedicated-quake-iii-arena-server-on-raspberry-pi-with-docker</guid>
            <pubDate>Sat, 07 Sep 2019 13:21:19 GMT</pubDate>
            <description><![CDATA[Quake III Arena, almost 20 years old by now but still an absolute classic! I would dear to say it's probably one of the best (FPS) multiplayer games ever made 🤩. We used to play it sometimes at school during lunch breaks in the computer…]]></description>
            <content:encoded><![CDATA[<p>Quake III Arena, almost 20 years old by now but still an absolute classic! I would dear to say it&apos;s probably one of the best (FPS) multiplayer games ever made 🤩. We used to play it sometimes at school during lunch breaks in the computer classroom, very nostalgic feelings 🥺.</p><p>So, if you&apos;re also starting to feel a bit nostalgic... It&apos;s super easy to run your own Quake III Arena server! No installing of packages and trial &amp; error compiling for hours. Thanks to Docker you&apos;ll be up and running in just a couple of minutes! (I am assuming you have <a href="https://phoenixnap.com/kb/docker-on-raspberry-pi">docker installed</a> though)</p><h2>Installation</h2><p>There&apos;s actually not really any installation at all, you just need to copy a few files. For your convenience I&apos;ve made <a href="https://github.com/wouterds/rpi-quake3-server/blob/master/install.sh">a script that does that for you</a>, run the following command and you&apos;re good to go! If you still want to customize some server settings, edit the <code>./quake3/server.cfg</code> file after the script finished.</p><pre data-language="bash"><code>curl -sSL https://github.com/wouterds/rpi-quake3-server/raw/master/install.sh | sh</code></pre><h2>Running</h2><pre data-language="bash"><code>cd ./quake3
docker-compose up -d</code></pre><p><img src="https://wouterds.com/images/116253/1703683578-d91a0a01-f4d3-472b-99e8-4bd980fcc096.jpg" width="100%"></p><h3>Demo</h3><iframe src="https://youtube.com/embed/U1IfcXm_RG8" width="100%" style="aspect-ratio:16/9"></iframe><h3>Resources</h3><p>It runs without any issues and uses about 30MB memory and +-20% CPU on average (on model 3B+).</p><p><img src="https://wouterds.com/images/116253/1703683745-161a6c5f-ba74-485d-9930-558c92ae8f70.jpg" width="100%"></p><h2>Client</h2><p>I&apos;m using the <a href="https://ioquake3.org/get-it">ioquake3 client</a> for macOS and on Windows the Steam version. But ioquake3 is available for any platform, you just need the <a href="https://github.com/wouterds/rpi-quake3-server/releases/download/1.0.0/pak0.pk3">pak0.pk3 file</a>. That&apos;s it, kick some ass 🎮!</p>]]></content:encoded>
            <enclosure url="https://wouterds.com/images/116253/1703683800-2fc444b5-e80e-49bb-85ef-71c3023e0e88-embed.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[ESP8266 freeform weather station circuit]]></title>
            <link>https://wouterds.com/blog/esp8266-freeform-weather-station-circuit</link>
            <guid>https://wouterds.com/blog/esp8266-freeform-weather-station-circuit</guid>
            <pubDate>Sat, 10 Aug 2019 17:42:14 GMT</pubDate>
            <description><![CDATA[Although this is a project of a few months ago, I really wanted to put it out here and I finally took some time to do it! I had been following @mohitbhoite & @jipraus their freeform circuit creations on Twitter & Instagram for a while…]]></description>
            <content:encoded><![CDATA[<p>Although this is a project of a few months ago, I really wanted to put it out here and I finally took some time to do it! I had been following <a href="https://twitter.com/mohitbhoite">@mohitbhoite</a> &amp; <a href="https://twitter.com/jipraus">@jipraus</a> their freeform circuit creations on Twitter &amp; Instagram for a while and found them really inspiring.</p><p>A few years ago I made this mini indoor weather station and I thought I&apos;d be cool to convert my breadboard project into a freeform circuit. Admittedly, it was more difficult than I expected and I had a to start over more than once before I was really pleased with the outcome.</p><p>I also <a href="https://github.com/wouterds/environment-tracker-sensors">opensourced the code of the circuit on GitHub</a> for those interested.</p><p><img src="https://wouterds.com/images/116253/1703784749-a6115a96-a57a-467c-bf7b-f5aa62970a0f.jpg" width="100%"><br><img src="https://wouterds.com/images/116253/1703784753-6394f6c3-62a1-4b87-b74a-13020e1ba621.jpg" width="100%"></p><iframe src="https://youtube.com/embed/pAbSw1cFQlE" width="100%" style="aspect-ratio:16/9"></iframe><p><img src="https://wouterds.com/images/116253/1703784759-e60c6fc0-3a7b-4a80-ae48-4d980f3eb758.jpg" width="100%"><br><img src="https://wouterds.com/images/116253/1703784764-7a7d7771-8bff-4e75-a3e8-14b77051ddd9.jpg" width="100%"><br><img src="https://wouterds.com/images/116253/1703784768-ecb71969-84a5-4c8f-8995-3b1dd7d98175.jpg" width="100%"><br><img src="https://wouterds.com/images/116253/1703784773-7dd8aaa6-f5aa-43a6-9e4b-51fd13e31477.jpg" width="100%"><br><img src="https://wouterds.com/images/116253/1703784778-89817979-8ad9-4c5c-849f-7b554950fe96.jpg" width="100%"><br><img src="https://wouterds.com/images/116253/1703784782-78d31b7d-ce66-49ef-9bcc-5617c4d9455a.jpg" width="100%"><br><img src="https://wouterds.com/images/116253/1703784787-51c5144a-beee-4ce5-9589-44525d80aba5.jpg" width="100%"></p>]]></content:encoded>
            <enclosure url="https://wouterds.com/images/116253/1703784636-59e38084-b278-4818-88c8-d5e1716f08bf-embed.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Monitor your containers with ctop, like (h)top, but for Docker containers]]></title>
            <link>https://wouterds.com/blog/monitor-your-containers-with-ctop-like-h-top-but-for-docker-containers</link>
            <guid>https://wouterds.com/blog/monitor-your-containers-with-ctop-like-h-top-but-for-docker-containers</guid>
            <pubDate>Thu, 14 Mar 2019 21:00:00 GMT</pubDate>
            <description><![CDATA[While profiling memory usage and doing some load tests at work for one of our containers I was looking for some basic and easy to use Docker monitoring tools. At some point I came across ctop, much like top/htop, but for Docker…]]></description>
            <content:encoded><![CDATA[<p>While profiling memory usage and doing some load tests at work for one of our containers I was looking for some basic and easy to use Docker monitoring tools. At some point I came across <a href="https://github.com/bcicen/ctop">ctop</a>, much like top/htop, but for Docker containers!</p><p>YES, there is docker stats.. but it&apos;s not always as easy to read. I just like this little cli tool way better!</p><p><img src="https://wouterds.com/images/116253/1703795431-img_0195.gif" width="100%"></p><p>You can install it with brew (<code>brew install ctop</code>) on macOS or just <a href="https://github.com/bcicen/ctop/releases">download the binary</a> and move it manually to your bin folder. It comes with releases for Linux, macOS and Windows. And they even have an arm compatible version for platforms like Raspberry Pi 😍!</p><p>You could also just run it as a container, but that&apos;s only supported on x86 arch.</p><pre data-language="bash"><code>docker run --rm -ti \
  --name=ctop \
  -v /var/run/docker.sock:/var/run/docker.sock \
  quay.io/vektorlab/ctop:latest</code></pre>]]></content:encoded>
            <enclosure url="https://wouterds.com/images/116253/1703795279-img_0194.jpeg" length="0" type="image/jpeg"/>
        </item>
        <item>
            <title><![CDATA[Good Charlotte live at Ancienne Belgique (2019)]]></title>
            <link>https://wouterds.com/blog/good-charlotte-live-at-ancienne-belgique-2019</link>
            <guid>https://wouterds.com/blog/good-charlotte-live-at-ancienne-belgique-2019</guid>
            <pubDate>Fri, 08 Feb 2019 20:00:00 GMT</pubDate>
            <description><![CDATA[A throwback to 2007, or maybe earlier. It's hard to say, it seems so long ago. Back when I was 14 - 15 I used to listen a lot to Good Charlotte, but for some reason I never saw them live though.
But yesterday they played here at the…]]></description>
            <content:encoded><![CDATA[<p>A throwback to 2007, or maybe earlier. It&apos;s hard to say, it seems so long ago. Back when I was 14 - 15 I used to listen a lot to Good Charlotte, but for some reason I never saw them live though.</p><p>But yesterday they played here at the Ancienne Belgique in Brussels, and I couldn&apos;t resist. I&apos;m glad I went to go see them, they played a lot of their older songs and it was real throwback party 🎸🤩!</p><iframe src="https://youtube.com/embed/u1NTmUQk91c" width="100%" style="aspect-ratio:16/9"></iframe>]]></content:encoded>
            <enclosure url="https://wouterds.com/images/116253/1703795937-img_0196.jpeg" length="0" type="image/jpeg"/>
        </item>
        <item>
            <title><![CDATA[Sensor readings as JSON API using an ESP8266 NodeMCU dev kit]]></title>
            <link>https://wouterds.com/blog/sensor-readings-as-json-api-using-an-esp8266-nodemcu-dev-kit</link>
            <guid>https://wouterds.com/blog/sensor-readings-as-json-api-using-an-esp8266-nodemcu-dev-kit</guid>
            <pubDate>Tue, 29 Jan 2019 12:00:00 GMT</pubDate>
            <description><![CDATA[I've had some ESP8266 modules & dev kits laying around for quite a while now and a few months ago I started prototyping with them. Thought I'd make a basic example on how to present some sensor readings as a simple json api with only a…]]></description>
            <content:encoded><![CDATA[<p>I&apos;ve had some ESP8266 modules &amp; dev kits laying around for quite a while now and a few months ago I started prototyping with them. Thought I&apos;d make a basic example on how to present some sensor readings as a simple json api with only a few lines of code to get you started!</p><p><img src="https://wouterds.com/images/116253/1703847329-7d4b6e21-5a94-4166-9fff-55c653fbb57e.jpg" width="100%"></p><h2>Goal</h2><p>The goal is simple: a basic http endpoint that returns some json back with sensor readings, like below.</p><pre data-language="json"><code>{
  &quot;illuminance&quot;: {
    &quot;visible&quot;: 905.0,
    &quot;full&quot;: 1255.0,
    &quot;ir&quot;: 347.0
  },
  &quot;temperature&quot;: 24.51,
  &quot;humidity&quot;: 37.49,
  &quot;pressure&quot;: 1027.82
}</code></pre><h2>Hardware</h2><p>First things first, getting the hardware needed! I&apos;ve gotten everything of eBay for only a couple of bucks. I assume you have a breadboard and some jump wires but I&apos;ll link them too, just to be sure.</p><ul><li><p><a href="https://ebay.com/itm/201542946669">ESP8266 NodeMCU dev kit</a> - <strong>$3.77</strong></p></li><li><p><a href="https://ebay.com/itm/322834231147">TMP102 sensor</a> - <strong>$2.25</strong></p></li><li><p><a href="https://ebay.com/itm/400767563400">TSL2561 sensor</a> - <strong>$1.48</strong></p></li><li><p><a href="https://ebay.com/itm/401000227934">BME280 sensor</a> - <strong>$3.78</strong></p></li><li><p><a href="https://ebay.com/itm/372133400247">400 points solderless breadboard</a> - <strong>$1.85</strong></p></li><li><p><a href="https://ebay.com/itm/271635556708">40 jump wires</a> - <strong>$1.50</strong></p></li></ul><h3>Wiring everything up</h3><p>I don&apos;t have a schematic or anything, but the wiring is quite simple. Connect the <code>3.3V</code> of the ESP8266 NodeMCU with the <code>VCC</code>, <code>GND</code> with <code>GND</code>, <code>D1</code> with <code>SCL</code> and <code>D2</code> with <code>SDA</code> pins of the sensors.</p><pre><code>+---------------------------------------------+
| ESP8266 NodeMCU | TSL2561 | BME280 | TMP102 |
|-----------------|---------|--------|--------|
| 3.3V            | VCC     | VCC    | VCC    |
| GND             | GND     | GND    | GND    |
| D1              | SCL     | SCL    | SCL    |
| D2              | SDA     | SDA    | SDA    |
+---------------------------------------------+</code></pre><h2>Software</h2><p>I&apos;m not going to go step by step through the code as it is quite straightforward but basically the only thing you really need to do is download the required libraries and extract them here <code>~/Documents/Arduino/libraries</code> (or wherever it might be on Windows 🤷‍♂️).</p><h3>Libraries</h3><ul><li><p><a href="https://github.com/adafruit/Adafruit_Sensor/archive/master.zip">adafruit-sensor</a></p></li><li><p><a href="https://github.com/adafruit/TSL2561-Arduino-Library/archive/master.zip">adafruit-tsl2561</a></p></li><li><p><a href="https://github.com/adafruit/Adafruit_BME280_Library/archive/master.zip">adafruit-bme280</a></p></li><li><p><a href="https://github.com/sparkfun/SparkFun_TMP102_Arduino_Library/archive/master.zip">sparkfun-tmp102</a></p></li></ul><h3>Program</h3><p>This is the program, should work pretty much out of the box if everything is wired correctly. Just don&apos;t forget to substitute your WiFi credentials in <code>WIFI_SSID</code> and <code>WIFI_PASS</code>. Also my BME280 its address is <code>0x76</code>, but sometimes it lives on <code>0x77</code> as well. You can double-check this using the <a href="https://playground.arduino.cc/Main/I2cScanner">I2C scanner</a>.</p><pre data-language="c"><code>#include &lt;Wire.h&gt;
#include &lt;ESP8266WiFi.h&gt;
#include &lt;ESP8266WebServer.h&gt;
#include &lt;Adafruit_Sensor.h&gt;
#include &lt;TSL2561.h&gt;
#include &lt;Adafruit_BME280.h&gt;
#include &lt;SparkFunTMP102.h&gt;

// Configuration
#define WIFI_SSID &quot;&quot;
#define WIFI_PASS &quot;&quot;
#define WEB_SERVER_PORT 80
#define I2C_ADDR_TSL2561 0x39
#define I2C_ADDR_BME280 0x76
#define I2C_ADDR_TMP102 0x48

// Web server
ESP8266WebServer webServer(WEB_SERVER_PORT);

// Sensors
TSL2561 tsl2561(I2C_ADDR_TSL2561);
Adafruit_BME280 bme280;
TMP102 tmp102(I2C_ADDR_TMP102);

void setup()
{
  // Setup I2C
  Wire.begin(D2, D1);

  // Setup Serial
  Serial.begin(9600);
  Serial.println();

  setupWiFi();
  setupWebServer();
  setupSensors();
}

void loop()
{
  webServer.handleClient();
}

void handleRoot()
{
  Serial.println(&quot;[WebServer] Request: /&quot;);

  // Read illuminance (lx)
  float visible = tsl2561.getLuminosity(TSL2561_VISIBLE);
  float full = tsl2561.getLuminosity(TSL2561_FULLSPECTRUM);
  float ir = tsl2561.getLuminosity(TSL2561_INFRARED);

  // Read temperature (°C)
  float temperature = readTemperature();

  // Read humidity (%)
  float humidity = bme280.readHumidity();

  // Read pressure (hPa)
  float pressure = bme280.readPressure() / 100;

  // Build response
  String response = &quot;&quot;;
  response += &quot;{&quot;;
  response += &quot;\&quot;illuminance\&quot;:{&quot;;
  response += &quot;\&quot;visible\&quot;:&quot;;
  response += visible;
  response += &quot;,\&quot;full\&quot;:&quot;;
  response += full;
  response += &quot;,\&quot;ir\&quot;:&quot;;
  response += ir;
  response += &quot;}&quot;;
  response += &quot;,\&quot;temperature\&quot;:&quot;;
  response += temperature;
  response += &quot;,\&quot;humidity\&quot;:&quot;;
  response += humidity;
  response += &quot;,\&quot;pressure\&quot;:&quot;;
  response += pressure;
  response += &quot;}&quot;;

  // Send response
  webServer.send(200, &quot;application/json&quot;, response);
}

float readTemperature() {
  float bme280Temperature = bme280.readTemperature();
  float tmp102Temperature = tmp102.readTempC();

  return (tmp102Temperature + tmp102Temperature + bme280Temperature) / 3;
}

void setupWiFi()
{
  Serial.println(&quot;[WiFi] Setup&quot;);
  Serial.print(&quot;[WiFi] Connecting to: &quot;);
  Serial.println(WIFI_SSID);

  WiFi.begin(WIFI_SSID, WIFI_PASS);
  while (WiFi.status() != WL_CONNECTED)
  {
    delay(200);
    Serial.print(&quot;.&quot;);
  }
  Serial.println();

  Serial.println(&quot;[WiFi] Connected!&quot;);
  Serial.print(&quot;[WiFi] IP: &quot;);
  Serial.println(WiFi.localIP());
}

void setupWebServer()
{
  Serial.println(&quot;[WebServer] Setup&quot;);
  webServer.on(&quot;/&quot;, handleRoot);

  Serial.println(&quot;[WebServer] Starting..&quot;);
  webServer.begin();
  Serial.println(&quot;[WebServer] Running!&quot;);
}

void setupSensors()
{
  setupSensorTSL2561();
  setupSensorBME280();
  setupSensorTMP102();
}

void setupSensorTSL2561()
{
  Serial.println(&quot;[TSL2561] Setup&quot;);
  Serial.print(&quot;[TSL2561] Connecting..&quot;);

  while (!tsl2561.begin())
  {
    delay(200);
    Serial.print(&quot;.&quot;);
  }
  Serial.println();

  Serial.println(&quot;[TSL2561] Connected!&quot;);
}

void setupSensorBME280()
{
  Serial.println(&quot;[BME280] Setup&quot;);
  Serial.print(&quot;[BME280] Connecting..&quot;);

  while (!bme280.begin(I2C_ADDR_BME280))
  {
    delay(200);
    Serial.print(&quot;.&quot;);
  }
  Serial.println();

  Serial.println(&quot;[BME280] Connected!&quot;);
}

void setupSensorTMP102()
{
  Serial.println(&quot;[TMP102] Setup&quot;);
  Serial.println(&quot;[TMP102] Connecting..&quot;);

  tmp102.begin();

  Serial.println(&quot;[TMP102] Assuming connected!&quot;);
}</code></pre><p>Normally if you now compile &amp; upload the above code to your ESP8266 module it should print out the IP address somewhere on the serial console.</p><pre><code>[WiFi] Setup
[WiFi] Connecting to: Wouter&apos;s Place
..................................................................
[WiFi] Connected!
[WiFi] IP: 10.10.10.77
[WebServer] Setup
[WebServer] Starting..
[WebServer] Running!
[TSL2561] Setup
[TSL2561] Connecting..
[TSL2561] Connected!
[BME280] Setup
[BME280] Connecting..
[BME280] Connected!
[TMP102] Setup
[TMP102] Connecting..
[TMP102] Assuming connected!</code></pre><p>And when making an http request to this ip you should get the sensor readings as json back 😄!</p><h4>TLDR</h4><p>You can find the entire project <a href="https://github.com/wouterds/esp8266-nodemcu-sensors/tree/0.1.0">on Github</a>.</p>]]></content:encoded>
            <enclosure url="https://wouterds.com/images/116253/1703847280-d378b290-6ebe-416f-88b9-f292d7480dcd-embed.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[React Native: Entry, ":CFBundleIdentifier", Does Not Exist]]></title>
            <link>https://wouterds.com/blog/react-native-entry-cfbundleidentifier-does-not-exist</link>
            <guid>https://wouterds.com/blog/react-native-entry-cfbundleidentifier-does-not-exist</guid>
            <pubDate>Fri, 07 Dec 2018 11:00:00 GMT</pubDate>
            <description><![CDATA[Probably the most Googled error by React Native developers, when you search the issues in the React Native Github project alone you get over a 100 results. And then there's even much more StackOverflow issues, forum posts and so on.
Now…]]></description>
            <content:encoded><![CDATA[<p>Probably the most Googled error by React Native developers, when you search the issues in the React Native Github project alone you get <a href="https://github.com/facebook/react-native/issues?q=CFBundleIdentifier">over a 100 results</a>. And then there&apos;s even much more StackOverflow issues, forum posts and so on.</p><p>Now the reason why there&apos;s so many issues about this is because there&apos;s just no easy fix and it can be triggered by a wide range of possible problems. <strong>But really, it just hides the underlying issue.</strong> Usually it happens though after those goddamned React Native upgrades 🙄...</p><p>Out of my own experience a few things that helped and resolved the issue for for me in the past. I don&apos;t guarantee that it will work but they&apos;re all quite harmless so it doesn&apos;t hurt to try them one by one. And I&apos;ll keep this list updated whenever I come across it again and find a new way to fix it!</p><h2>What you can try</h2><h3>1. Re-install node_modules</h3><pre data-language="bash"><code>rm -rf ./node_modules
npm install</code></pre><p>Something additional you could do is also remove your node caches before re-installing dependencies.</p><pre data-language="bash"><code>rm -rf ./node_modules
npm cache clean --force
npm install</code></pre><h3>2. Delete react native caches</h3><pre data-language="bash"><code>watchman watch-del-all
rm -rf $TMPDIR/react-*
rm -rf $TMPDIR/metro-*
rm -rf $TMPDIR/haste-*</code></pre><h3>3. Remove generated build data</h3><pre data-language="bash"><code>rm -rf ios android
react-native eject
git checkout ios android</code></pre><h3>4. Force build the React modules manually</h3><ul><li><p>Open XCode</p></li><li><p>Clean (<code>cmd + shift + k</code>)</p></li><li><p>Select <code>React</code> as the scheme in Xcode and build it (<code>cmd + b</code>)</p></li><li><p>Build the library that is failing (e.g. <code>RCTLinking</code>)</p></li><li><p>Build your app again</p></li></ul><h3>5. Have you tried turning it off and on again?</h3><p><img src="https://wouterds.com/images/116253/1703848111-a69a7755-dee1-44ed-ae4d-39750bbb8213.gif" width="100%"></p><p>Kind of a joke, but also not really. I really had it working sometimes after a random reboot 🤷‍♂️.</p>]]></content:encoded>
            <enclosure url="https://wouterds.com/images/116253/1703848003-fa8c94fb-853d-4696-bbe3-bee039ec151c-embed.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Fix font rendering on macOS Mojave]]></title>
            <link>https://wouterds.com/blog/fix-font-rendering-on-macos-mojave</link>
            <guid>https://wouterds.com/blog/fix-font-rendering-on-macos-mojave</guid>
            <pubDate>Fri, 23 Nov 2018 11:00:00 GMT</pubDate>
            <description><![CDATA[So I finally updated to macOS Mojave yesterday, yay! But something was bothering me a bit.. I had the feeling there was something different with the font, I couldn't tell right away what it was though.
A few minutes later it hit me, the…]]></description>
            <content:encoded><![CDATA[<p>So I finally updated to macOS Mojave yesterday, yay! But something was bothering me a bit.. I had the feeling there was something different with the font, I couldn&apos;t tell right away what it was though.</p><p><img src="https://wouterds.com/images/116253/1703848323-fa1098fe-fb58-4dee-b7af-66c78a38d1d1.jpg" width="100%"></p><p>A few minutes later it hit me, the font was thinner than it used to be! Apparently Apple has changed the font rendering in macOS Mojave. The change is minorly visible in native apps like the Terminal.</p><p><img src="https://wouterds.com/images/116253/1703848405-61696002-6200-4f51-827f-7820b1d47364.gif" width="100%"></p><p>But in Electron based apps like VSCode it&apos;s much more noticeable if you&apos;re used to a thicker font.</p><p><img src="https://wouterds.com/images/116253/1703848367-63750fa5-7cc0-417d-a20b-f5e001d69b3d.gif" width="100%"></p><p>Since I basically look all day long at this editor I was a bit annoyed by the change. Luckily it&apos;s easy to change it back to how it used to be. Just run the following command in the terminal, reboot your machine, and all should be fine again.</p><pre data-language="bash"><code>defaults write -g CGFontRenderingFontSmoothingDisabled -bool FALSE</code></pre><p>Hope this was helpful!</p>]]></content:encoded>
            <enclosure url="https://wouterds.com/images/116253/1703848312-d99f4694-07a1-4f98-8c8c-176c762fa113-embed.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Verified (signed) commits on GitHub]]></title>
            <link>https://wouterds.com/blog/verified-signed-commits-on-github</link>
            <guid>https://wouterds.com/blog/verified-signed-commits-on-github</guid>
            <pubDate>Sat, 27 Oct 2018 10:00:00 GMT</pubDate>
            <description><![CDATA[How did you end up here? Either you want to setup signing of commits, or you were just wondering what that "Verified" badge on GitHub meant. To be honest, I just noticed the badge when one of my pull requests got merged and kinda liked…]]></description>
            <content:encoded><![CDATA[<p>How did you end up here? Either you want to setup signing of commits, or you were just wondering what that &quot;Verified&quot; badge on GitHub meant. To be honest, I just noticed the badge when one of my pull requests got merged and kinda liked it.</p><p><img src="https://wouterds.com/images/116253/1703848528-a8c79a8e-2f1d-4364-bce1-c9214b3cc12d.jpg" width="100%"></p><p>But what is it really? A short explanation from the GitHub blog;</p><blockquote><p>When you’re building software with people from around the world, sometimes it’s important to validate that commits and tags are coming from an identified source. Many open source projects and companies want to be sure that a commit is from a verified source. GPG signature verification on commits and tags makes it easy to see when a commit or tag is signed by a verified key that GitHub knows about.</p></blockquote><h2>Setting it up</h2><p>So now we know what it is, how do you get it? Whether or not you need it is something else and for you to decide. But basically it will require you to entire your password whenever you sign a commit.</p><h3>Generating a GPG key</h3><p>If you&apos;re on mac, you probably want to install latest GPG binaries using <a href="https://brew.sh/">homebrew</a>.</p><pre data-language="bash"><code>brew install gpg</code></pre><p>Once you have the latest gpg tools installed you can generate a key using the following command.</p><pre data-language="bash"><code>gpg --full-generate-key</code></pre><p>Follow the instructions and accept the defaults, for the max key length Github recommends not going above <code>4096</code>. When asked for your email address, make sure you enter your <a href="https://help.github.com/articles/verifying-your-email-address/">verified Github email</a>.</p><p><img src="https://wouterds.com/images/116253/1703848587-1ada34a0-0f11-49f0-8f6c-7798ad9044cd.jpg" width="100%"></p><h3>Exporting public GPG key &amp; adding to GitHub</h3><p>After the process completed you can check if everything went well by running the following command. This will output a list of GPG keys, copy the GPG key id, in this case <code>E0FDB38622C5CF52</code>.</p><pre data-language="shell"><code>gpg --list-secret-keys --keyid-format LONG</code></pre><p><img src="https://wouterds.com/images/116253/1703848604-7392f7fb-cda5-4ce9-b26b-22d3f3a45f04.jpg" width="100%"></p><p>Now run the following command with your GPG key id you copied earlier to export the public key. Copy the output (your public key) and add it to your <a href="https://github.com/settings/gpg/new">GitHub account</a>.</p><pre data-language="shell"><code>gpg --armor --export E0FDB38622C5CF52</code></pre><p><img src="https://wouterds.com/images/116253/1703848624-3c227fce-a00a-4a32-ab63-8f235c7a2e8d.jpg" width="100%"></p><h3>Configuring git</h3><p>Last step is to configure git locally on your machine so it knows about your signing key. Run the following command, again substitute the key id with the one you copied earlier.</p><pre data-language="bash"><code>git config --global user.signingkey E0FDB38622C5CF52</code></pre><p>You also want to add the following to your <code>~/.bash_profile</code>, <code>~/.profile</code> or <code>~/.zshrc</code> file - depending on what you using. I use ZSH so I added it to my <code>~/.zshrc</code> file at the end.</p><pre data-language="bash"><code>export GPG_TTY=$(tty)</code></pre><h2>Usage</h2><p>To sign a specific commit, e.g. for a release, just add the <code>-S</code> flag in your command (you can use the same flag for signing tags). You will be prompted to enter the password used when generating the GPG key. Git won&apos;t ask everytime for the password though, I (think) once you entered your password it keeps a session open for ~15 minutes. If you commit again signed it will prolong this session.</p><pre data-language="bash"><code>git commit -S -m &quot;:memo: Updated readme&quot;</code></pre><p><img src="https://wouterds.com/images/116253/1703848655-d2ca4004-34cc-45be-bc80-e0bd9b5cc91b.gif" width="100%"></p><p>If you push now your signed commit you will see it pops up on GitHub with a verified badge next to it.</p><h3>Bonus</h3><p>Enable autosigning of commit globally can be done using the following command. Note that this will sign all commits for all your different projects, and thus <strong>will require you to enter your password often</strong>.</p><pre data-language="bash"><code>git config --global commit.gpgSign true</code></pre><p>Or if you want just all your tags/releases signed run the following command.</p><pre data-language="bash"><code>git config --global tag.gpgSign true</code></pre><p>That&apos;s it!</p><h2>Other interesting links</h2><ul><li><p><a href="https://help.github.com/articles/verifying-your-email-address/">https://help.github.com/articles/verifying-your-email-address/</a></p></li><li><p><a href="https://help.github.com/articles/generating-a-new-gpg-key/">https://help.github.com/articles/generating-a-new-gpg-key/</a></p></li><li><p><a href="https://help.github.com/articles/associating-an-email-with-your-gpg-key/">https://help.github.com/articles/associating-an-email-with-your-gpg-key/</a></p></li><li><p><a href="https://help.github.com/articles/telling-git-about-your-signing-key/">https://help.github.com/articles/telling-git-about-your-signing-key/</a></p></li><li><p><a href="https://help.github.com/articles/signing-commits/">https://help.github.com/articles/signing-commits/</a></p></li><li><p><a href="https://help.github.com/articles/adding-a-new-gpg-key-to-your-github-account/">https://help.github.com/articles/adding-a-new-gpg-key-to-your-github-account/</a></p></li></ul>]]></content:encoded>
            <enclosure url="https://wouterds.com/images/116253/1703848513-ce03ce01-d8ee-44a3-be4c-a76806d0e459-embed.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Boris Brejcha live at Kompass 28.09.2018]]></title>
            <link>https://wouterds.com/blog/boris-brejcha-live-at-kompass-28-09-2018</link>
            <guid>https://wouterds.com/blog/boris-brejcha-live-at-kompass-28-09-2018</guid>
            <pubDate>Sat, 29 Sep 2018 10:00:00 GMT</pubDate>
            <description><![CDATA[Yesterday the one and only Boris Brejcha visited Kompass here in Ghent 💖. Obviously I had to check that out, and man, what a party! He played a 5 hour set from 2 AM till 7 AM while there were also some other DJs like Reinier Zonneveld…]]></description>
            <content:encoded><![CDATA[<p>Yesterday the one and only Boris Brejcha visited Kompass here in Ghent 💖. Obviously I had to check that out, and man, what a party! He played a 5 hour set from 2 AM till 7 AM while there were also some other DJs like Reinier Zonneveld playing in the other room.</p><p>I&apos;ve already been a few times to Kompass but never had to queue as long as today. I think we arrived at 00:45 and were only inside by 01:30. Not sure if everyone was just arriving at the same time or the security checks were more thoroughly, but that could have been definitely a bit smoother and faster.</p><p><img src="https://wouterds.com/images/116253/1703848841-1bd151ac-7b34-4dc5-87b0-0f408da0f7a7.jpg" width="100%"></p><p>I briefly checked out Reinier Zonneveld, which was also good, but spent most of my time at Boris Brejcha. I didn&apos;t make it till the end of the set however 😔, went home around 05:30, because dead ☠️.</p><p>Definitely go check him out if he&apos;s around. I liked it much better than when he plays at a (big) festivals!</p><iframe src="https://youtube.com/embed/bbE6Y8XpYYw" width="100%" style="aspect-ratio:16/9"></iframe>]]></content:encoded>
            <enclosure url="https://wouterds.com/images/116253/1703848816-5a4029f4-2ffe-4dc5-8a3f-331ef5174151-embed.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[I made a mini Raspberry Pi (Zero W) "battlebot" with a PS3 controller as remote]]></title>
            <link>https://wouterds.com/blog/i-made-a-mini-raspberry-pi-zero-w-battlebot-with-a-ps3-controller-as-remote</link>
            <guid>https://wouterds.com/blog/i-made-a-mini-raspberry-pi-zero-w-battlebot-with-a-ps3-controller-as-remote</guid>
            <pubDate>Thu, 27 Sep 2018 10:00:00 GMT</pubDate>
            <description><![CDATA[A while back I wrote a blogpost how to interface a PS3 controller on Raspberry Pi using NodeJS. This inspired me to create a budget Raspberry Pi robot with a PS3 controller as remote for under $50.
Here is the result 😄!
It is maybe a…]]></description>
            <content:encoded><![CDATA[<p>A while back I wrote a blogpost how to <a href="https://wouterdeschuyter.be/blog/interfacing-a-ps3-controller-on-a-raspberry-pi-using-nodejs">interface a PS3 controller on Raspberry Pi using NodeJS</a>. This inspired me to create a budget Raspberry Pi robot with a PS3 controller as remote for under $50.</p><p>Here is the result 😄!</p><iframe src="https://youtube.com/embed/0YKQ9kNbwjM" width="100%" style="aspect-ratio:16/9"></iframe><p>It is maybe a bit slow 🐌, but those miro metal gearmotors come in a wide range of variations with different RPM speeds and torque. I think I used 30 RPM, but they sell them up to 1000 RPM if I recall correct.</p><p>Something to consider is though that faster motors will probably have a higher peak current ⚡️, which might affect the Pi (unexpected reboots etc) quicker when running low on batteries. Another thing to consider is that they will have less torque (and will be less powerful) when clashing with your opponent, when going uphill or when you&apos;re on rough ground etc.</p><p>Just order a few variations and experiment a bit with it 🤪!</p><h2>Parts &amp; cost</h2><p>When you have all the parts laying around, it&apos;s a fun afternoon project to do. If you still need to get the parts it should cost no more or around $50. I didn&apos;t include the PS3 controller in the price since I assume you have it laying around. But you can also use knockoff PS3 controllers from eBay if you like. I also didn&apos;t include the Pritt Tack glue pads as you don&apos;t really need them and might as well use some glue or even tape.</p><p>I get most of my stuff from <a href="https://pimoroni.com/">Pimoroni</a>, they have an awesome shop with a lot of high quality parts and fast worldwide shipping for a reasonable price. But sometimes you can find some basic electronics cheaper on eBay, the trade-off is that you will probably have to wait a few weeks for it to arrive.</p><p><img src="https://wouterds.com/images/116253/1703848982-b6041b8c-145c-4e9a-8736-1d806f043803.jpg" width="100%"></p><ul><li><p><a href="https://shop.pimoroni.com/products/zumo-chassis-kit-no-motors">Pololu Zummo Chassis</a> - <strong>$26</strong></p></li><li><p><a href="https://shop.pimoroni.com/products/basic-sumo-blade-for-zumo-chassis">Pololu Sumo Blade</a> - <strong>$4</strong></p></li><li><p><a href="https://shop.pimoroni.com/products/raspberry-pi-zero-w">Raspberry Pi Zero W</a> - <strong>$12</strong></p></li><li><p><a href="https://www.ebay.com/itm/263260907116">Dual H-Bridge Motor Driver</a> - <strong>$1</strong></p></li><li><p><a href="https://www.ebay.com/itm/311945927376">5V DC-DC step up boost converter</a> - <strong>$1</strong></p></li><li><p><a href="https://www.ebay.com/itm/222100315239">2x Micro Metal Gearmotor</a> - <strong>$5</strong></p></li><li><p><a href="https://www.ebay.com/itm/261992023511">Pritt Tack Adhesive Glue Pads</a></p></li><li><p>Old PS3 controller</p></li></ul><h2>Code &amp; schematics</h2><p>For now I just dumped the code on Github, might add some explanation later on. But if you follow my previous linked tutorial about interfacing a PS3 controller using NodeJS everything should be clear. Wiring the electronics should also be pretty straightforward if you have some basic knowledge, but will add some additional info and schematics if there would be demand for that.</p><p>👉 <a href="https://github.com/wouterds/raspberry-pi-ps3-battlebot">https://github.com/wouterds/raspberry-pi-ps3-battlebot</a></p>]]></content:encoded>
            <enclosure url="https://wouterds.com/images/116253/1703848948-5b18bc73-40d3-4048-9ac2-26c7cce99a9a-embed.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Interfacing a PS3 controller on a Raspberry Pi using NodeJS]]></title>
            <link>https://wouterds.com/blog/interfacing-a-ps3-controller-on-a-raspberry-pi-using-nodejs</link>
            <guid>https://wouterds.com/blog/interfacing-a-ps3-controller-on-a-raspberry-pi-using-nodejs</guid>
            <pubDate>Fri, 17 Aug 2018 10:00:00 GMT</pubDate>
            <description><![CDATA[A basic example of how to communicate with a PS3 controller on a Raspberry Pi and have things reacting to its input. Once you got these basics the possibilities are endless!
To give you a more visual example of what we'll be making, see…]]></description>
            <content:encoded><![CDATA[<p>A basic example of how to communicate with a PS3 controller on a Raspberry Pi and have things reacting to its input. Once you got these basics the possibilities are endless!</p><p>To give you a more visual example of what we&apos;ll be making, see the GIF below.</p><p><img src="https://wouterds.com/images/116253/1703849317-f59b079f-e107-465d-9964-288150be17d2.gif" width="100%"></p><h2>Hardware requirements</h2><ul><li><p>A PS3 controller</p></li><li><p>A Raspberry Pi Zero W</p></li><li><p>A breadboard</p></li><li><p>Some LEDs</p></li><li><p>Some jump wires</p></li></ul><h2>Prerequisites</h2><p>I assume you have already <a href="https://wouterdeschuyter.be/blog/how-to-configure-ssh-and-wifi-on-a-headless-raspberry-pi-zero-w">installed Raspbian</a> on your Raspberry Pi. I also assume you already have <a href="https://wouterdeschuyter.be/blog/install-latest-version-of-nodejs-on-raspberry-pi">NodeJS installed</a> on your Raspberry Pi (<strong>use a version lower than 10</strong>, I used <code>9.9.0</code>). And lastly I also assume you have your <a href="https://wouterdeschuyter.be/blog/configure-a-ps3-controller-to-automatically-connect-to-a-raspberry-pi">PS3 controller already paired</a>. If you haven&apos;t please follow those guides first.</p><h2>Project setup</h2><h3>Native dependencies</h3><p>Before we can get started we&apos;ll need to install the <code>joystick</code> lib. This will expose the PS3 controller on <code>/dev/input/js0</code>. Run the following commands to install <code>joystick</code>.</p><pre data-language="bash"><code>sudo apt-get update
sudo apt-get install -y joystick</code></pre><p>Normally if you run now <code>ls /dev/input/js*</code> you should see <code>/dev/input/js0</code> popping up in the output. Test the joystick by running <code>jstest /dev/input/js0</code> and press some buttons to see if it works.</p><p>Now that is out of the way let&apos;s create the required files, we will need only 2 files, <code>index.js</code> which will contain our app and <code>package.json</code> for our dependencies. Create both files and paste the following contents in <code>package.json</code>.</p><pre data-language="json"><code>{
  &quot;dependencies&quot;: {}
}</code></pre><h2>Reading out the PS3 controller</h2><p>Install and save the following node dependency by running <code>npm i --save joystick</code>. Open up <code>index.js</code> and import the lib and add two listeners for the button event and axis event. Log the output to the console so we can see if that&apos;s working and what&apos;s actually inside these events.</p><pre data-language="javascript"><code>// Libs
const joystick = require(&apos;joystick&apos;);

// Init PS3 controller, 0 = /dev/input/js0
const ps3Controller = new joystick(0);

// On button press
ps3Controller.on(&apos;button&apos;, button =&gt; {
  console.log({ button });
});

// On axis movement
ps3Controller.on(&apos;axis&apos;, axis =&gt; {
  console.log({ axis });
});</code></pre><h3>Buttons</h3><p>First try, let&apos;s see if we can get some output, run the code by executing <code>npm run .</code>. When you press some buttons you should get something like the following output.</p><pre data-language="json"><code>{ button: { time: 4294954706, value: 1, number: 0, type: &apos;button&apos;, id: 0 } }
{ button: { time: 4294954956, value: 0, number: 0, type: &apos;button&apos;, id: 0 } }
{ button: { time: 4294961896, value: 1, number: 1, type: &apos;button&apos;, id: 0 } }
{ button: { time: 4294962076, value: 0, number: 1, type: &apos;button&apos;, id: 0 } }
{ button: { time: 4294963226, value: 1, number: 2, type: &apos;button&apos;, id: 0 } }
{ button: { time: 4294963416, value: 0, number: 2, type: &apos;button&apos;, id: 0 } }
{ button: { time: 4294964146, value: 1, number: 3, type: &apos;button&apos;, id: 0 } }
{ button: { time: 4294964386, value: 0, number: 3, type: &apos;button&apos;, id: 0 } }</code></pre><p>I pressed <em>cross, circle, triangle, square</em> (anyone else GTA cheats throwback 😂?). It looks like we can identify the button by mapping the <code>number</code> prop. It also seems like we get 2 events for one button interaction; one on key down and one on key up, respectively with value <code>1</code> and value <code>0</code>.</p><h3>Joysticks</h3><p>When you move the joysticks you should see output similar to this.</p><pre data-language="json"><code>{ axis: { time: 524240, value: -2703, number: 4, type: &apos;axis&apos;, id: 0 } }
{ axis: { time: 524300, value: -1352, number: 3, type: &apos;axis&apos;, id: 0 } }
{ axis: { time: 524300, value: -11486, number: 4, type: &apos;axis&apos;, id: 0 } }
{ axis: { time: 524300, value: -13513, number: 4, type: &apos;axis&apos;, id: 0 } }
{ axis: { time: 524360, value: -5068, number: 3, type: &apos;axis&apos;, id: 0 } }
{ axis: { time: 524360, value: -24999, number: 4, type: &apos;axis&apos;, id: 0 } }
{ axis: { time: 524370, value: -28039, number: 4, type: &apos;axis&apos;, id: 0 } }
{ axis: { time: 524430, value: -5743, number: 3, type: &apos;axis&apos;, id: 0 } }
{ axis: { time: 524430, value: -32767, number: 4, type: &apos;axis&apos;, id: 0 } }
{ axis: { time: 524480, value: -6081, number: 3, type: &apos;axis&apos;, id: 0 } }
{ axis: { time: 524610, value: -5406, number: 3, type: &apos;axis&apos;, id: 0 } }
{ axis: { time: 524660, value: 0, number: 3, type: &apos;axis&apos;, id: 0 } }
{ axis: { time: 524660, value: 0, number: 4, type: &apos;axis&apos;, id: 0 } }
{ axis: { time: 525270, value: 6756, number: 4, type: &apos;axis&apos;, id: 0 } }
{ axis: { time: 525280, value: 9458, number: 4, type: &apos;axis&apos;, id: 0 } }
{ axis: { time: 525340, value: 19931, number: 4, type: &apos;axis&apos;, id: 0 } }
{ axis: { time: 525400, value: 30403, number: 4, type: &apos;axis&apos;, id: 0 } }
{ axis: { time: 525400, value: 32767, number: 4, type: &apos;axis&apos;, id: 0 } }
{ axis: { time: 525460, value: 2026, number: 3, type: &apos;axis&apos;, id: 0 } }
{ axis: { time: 525520, value: 3040, number: 3, type: &apos;axis&apos;, id: 0 } }
{ axis: { time: 525760, value: 3715, number: 3, type: &apos;axis&apos;, id: 0 } }
{ axis: { time: 525820, value: 7431, number: 3, type: &apos;axis&apos;, id: 0 } }
{ axis: { time: 525820, value: 22971, number: 4, type: &apos;axis&apos;, id: 0 } }
{ axis: { time: 525830, value: 5405, number: 3, type: &apos;axis&apos;, id: 0 } }
{ axis: { time: 525830, value: 19931, number: 4, type: &apos;axis&apos;, id: 0 } }
{ axis: { time: 525880, value: 0, number: 3, type: &apos;axis&apos;, id: 0 } }
{ axis: { time: 525880, value: 0, number: 4, type: &apos;axis&apos;, id: 0 } }
{ axis: { time: 526850, value: 7094, number: 3, type: &apos;axis&apos;, id: 0 } }
{ axis: { time: 526850, value: 9120, number: 3, type: &apos;axis&apos;, id: 0 } }
{ axis: { time: 526900, value: 16215, number: 3, type: &apos;axis&apos;, id: 0 } }
{ axis: { time: 526910, value: 18579, number: 3, type: &apos;axis&apos;, id: 0 } }
{ axis: { time: 526970, value: 29389, number: 3, type: &apos;axis&apos;, id: 0 } }
{ axis: { time: 527030, value: 32767, number: 3, type: &apos;axis&apos;, id: 0 } }
{ axis: { time: 527400, value: 29727, number: 3, type: &apos;axis&apos;, id: 0 } }
{ axis: { time: 527410, value: 21620, number: 3, type: &apos;axis&apos;, id: 0 } }
{ axis: { time: 527460, value: 0, number: 3, type: &apos;axis&apos;, id: 0 } }
{ axis: { time: 528070, value: -4392, number: 3, type: &apos;axis&apos;, id: 0 } }
{ axis: { time: 528130, value: -20945, number: 3, type: &apos;axis&apos;, id: 0 } }
{ axis: { time: 528140, value: -23985, number: 3, type: &apos;axis&apos;, id: 0 } }
{ axis: { time: 528190, value: -32767, number: 3, type: &apos;axis&apos;, id: 0 } }
{ axis: { time: 528680, value: -22972, number: 3, type: &apos;axis&apos;, id: 0 } }
{ axis: { time: 528740, value: -10473, number: 3, type: &apos;axis&apos;, id: 0 } }
{ axis: { time: 528740, value: -3379, number: 3, type: &apos;axis&apos;, id: 0 } }
{ axis: { time: 528800, value: 0, number: 3, type: &apos;axis&apos;, id: 0 } }</code></pre><p>I did the following with the <strong>right joystick</strong>: <em>up, down, right, left</em>. In the default, center position, the value is always <code>0</code>. The range seems to go from <code>-32767</code> till <code>32767</code>. And number 4 seems to be the right y-axis and number 3 the right x-axis.</p><h3>Parsing the events</h3><p>I wrote some logic to parse our events into useable blocks, our code looks as the following now.</p><pre data-language="javascript"><code>// Libs
const joystick = require(&apos;joystick&apos;);

// Init PS3 controller, 0 = /dev/input/js0
const ps3Controller = new joystick(0);

// On button press (triggers when pressed and when released)
ps3Controller.on(&apos;button&apos;, button =&gt; {
  switch (button.number) {
    case 0: // cross
      console.log(&apos;cross&apos;, button.value);
      break;
    case 1: // circle
      console.log(&apos;circle&apos;, button.value);
      break;
    case 2: // triangle
      console.log(&apos;triangle&apos;, button.value);
      break;
    case 3: // square
      console.log(&apos;square&apos;, button.value);
      break;
  }
});

// On axis movement
ps3Controller.on(&apos;axis&apos;, axis =&gt; {
  switch (axis.number) {
    case 1: // left y-axis
      console.log(&apos;left y-axis&apos;, axis.value);
      break;
    case 4: // right y-axis
      console.log(&apos;right y-axis&apos;, axis.value);
      break;
  }
});</code></pre><p>When you run the script now you will get some more readable output. Give it another try and see if everything matches with what we wrote. Something else you might have noticed by now is that it sends an event on start as well with the default state. This can be handy to immediately act upon (set some things in their default state) before we actually interact with the controller.</p><h2>Wiring the LEDs with the Raspberry Pi</h2><p>Before we continue with the code, lets hook up some LEDs to our Raspberry Pi. I wired them like this (I&apos;m using <strong>physical pin numbers</strong> to be clear).</p><pre data-language="text"><code>White (1): 26
Blue: 21
Green: 23
Yellow: 22
Red: 24
White (2): 19</code></pre><p><img src="https://wouterds.com/images/116253/1703849358-d97b7608-26b8-4785-959b-f0cd97daea2f.jpg" width="100%"></p><h2>Linking the LEDs with the PS3 controller</h2><p>It would be fun if we could make the LEDs represent some of our button states, no..? <strong>YES OF COURSE THAT&apos;D BE SO MUCH FUN 🥳!</strong> Let&apos;s start off by installing some more node dependencies so we can communicate with the GPIO; <code>npm i --save raspi raspi-gpio</code>. Then load the libraries and initialize the board as follows.</p><pre data-language="javascript"><code>const raspi = require(&apos;raspi&apos;);
const gpio = require(&apos;raspi-gpio&apos;);

raspi.init(() =&gt; {

  ..

});</code></pre><p>Now initialize some digital outputs and write each button value to a different led.</p><pre data-language="javascript"><code>..

const ledBlue = new gpio.DigitalOutput(&apos;P1-21&apos;);

..

  case 0: // cross
    ledBlue.write(button.value);
    break;

..</code></pre><p>Your entire file should now look like something like this.</p><pre data-language="javascript"><code>// Libs
const joystick = require(&apos;joystick&apos;);
const raspi = require(&apos;raspi&apos;);
const gpio = require(&apos;raspi-gpio&apos;);

// Init PS3 controller, 0 = /dev/input/js0
const ps3Controller = new joystick(0);

// Init Raspi board
raspi.init(() =&gt; {
  // Define digital outputs
  const ledYellow = new gpio.DigitalOutput(&apos;P1-22&apos;);
  const ledRed = new gpio.DigitalOutput(&apos;P1-24&apos;);
  const ledBlue = new gpio.DigitalOutput(&apos;P1-21&apos;);
  const ledGreen = new gpio.DigitalOutput(&apos;P1-23&apos;);

  // On button press (triggers when pressed and when released)
  ps3Controller.on(&apos;button&apos;, button =&gt; {
    switch (button.number) {
      case 0: // cross
        ledBlue.write(button.value);
        break;
      case 1: // circle
        ledYellow.write(button.value);
        break;
      case 2: // triangle
        ledGreen.write(button.value);
        break;
      case 3: // square
        ledRed.write(button.value);
        break;
    }
  });


  // On axis movement
  ps3Controller.on(&apos;axis&apos;, axis =&gt; {
    switch (axis.number) {
      case 1: // left y-axis
        console.log(&apos;left y-axis&apos;, axis.value);
        break;
      case 4: // right y-axis
        console.log(&apos;right y-axis&apos;, axis.value);
        break;
    }
  });
});</code></pre><p>And when you run it now using <code>sudo node .</code> you should be able to see the LEDs light up on button press and turn off again on button release 😮!</p><h3>Why sudo 🤔</h3><p>We need to use <code>sudo</code> because to be able to interact with the GPIO we need admin privileges. There&apos;s ways around this, but out of scope for this guide. For now I hope everything is still working till this point, and if so, great success!</p><p><img src="https://wouterds.com/images/116253/1703849400-a6579d54-e572-4505-ac96-761ba6a4dce1.jpg" width="100%"></p><h3>PWM outputs</h3><p>There are still 2 LEDs left unused and we also haven&apos;t used our joysticks yet? Let&apos;s change that, to do this we&apos;ll make use of PWM (Pulse Width Modulation). There is real PWM and <em>soft PWM</em>, the latter emulates it (software PWM) while the real one is done by the Raspberry Pi. I&apos;m not going to go into detail but we&apos;ll use soft PWM as it will be sufficient for this guide and most applications.</p><p>Using PWM we can output a signal between 0 and 1 instead of a purely digital signal which is just 1 or 0. So we could map our joystick value to a scale between 0 and 1 and link that to the LED.</p><p>First initialize the 2 software PWM outputs as the following.</p><pre data-language="javascript"><code>const pwmOutput1 = new pwm.SoftPWM(&apos;P1-19&apos;);
const pwmOutput2 = new pwm.SoftPWM(&apos;P1-26&apos;);</code></pre><p>Next up is mapping our joystick signal to a value between 0 and 1, and default to 0.5 (not really, but again, out of scope, this is the easiest to explain it without going into detail). I did that like this.</p><pre data-language="javascript"><code>const max = 32767;

const value = (axis.value * -1) / max / 2 + 0.5;</code></pre><p>And finally we just need to write those values to our PWM outputs like this.</p><pre data-language="javascript"><code>pwmOutput1.write(value);
pwmOutput2.write(value);</code></pre><p>Normally if you now run the script again you should be able to gradually increase and decrease the brightness of the other 2 LEDs using the joysticks.</p><h2>Recap</h2><p>Just for reference, this is the full script you should have now.</p><pre data-language="javascript"><code>// Libs
const joystick = require(&apos;joystick&apos;);
const raspi = require(&apos;raspi&apos;);
const gpio = require(&apos;raspi-gpio&apos;);
const pwm = require(&apos;raspi-soft-pwm&apos;);

// Init PS3 controller, 0 = /dev/input/js0
const ps3Controller = new joystick(0);

// Init Raspi board
raspi.init(() =&gt; {
  // Define digital outputs
  const ledYellow = new gpio.DigitalOutput(&apos;P1-22&apos;);
  const ledRed = new gpio.DigitalOutput(&apos;P1-24&apos;);
  const ledBlue = new gpio.DigitalOutput(&apos;P1-21&apos;);
  const ledGreen = new gpio.DigitalOutput(&apos;P1-23&apos;);

  // Define our software pwm outputs
  const pwmOutput1 = new pwm.SoftPWM(&apos;P1-19&apos;);
  const pwmOutput2 = new pwm.SoftPWM(&apos;P1-26&apos;);

  // On button press (triggers when pressed and when released)
  ps3Controller.on(&apos;button&apos;, button =&gt; {
    switch (button.number) {
      case 0: // cross
        ledBlue.write(button.value);
        break;
      case 1: // circle
        ledYellow.write(button.value);
        break;
      case 2: // triangle
        ledGreen.write(button.value);
        break;
      case 3: // square
        ledRed.write(button.value);
        break;
    }
  });

  // On axis movement
  ps3Controller.on(&apos;axis&apos;, axis =&gt; {
    // Max value in both directions
    const max = 32767;

    // Value between 0 and 1, default: 0.5
    const value = (axis.value * -1) / max / 2 + 0.5;

    switch (axis.number) {
      case 1: // left y-axis
        pwmOutput1.write(value);
        break;
      case 4: // right y-axis
        pwmOutput2.write(value);
        break;
    }
  });
});</code></pre><h2>End result</h2><p>If you followed everything as described you should end up with something like below. Hope you learned a few things and already have some ideas for your next project using a PS3 controller, I certainly do 🎮!</p><iframe src="https://youtube.com/embed/3plB-zhrj8k" width="100%" style="aspect-ratio:16/9"></iframe>]]></content:encoded>
            <enclosure url="https://wouterds.com/images/116253/1703849208-329dd17c-2cdb-4384-b0ac-7e3f79f3d4c0-embed.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Configure a PS3 controller to automatically connect to a Raspberry Pi]]></title>
            <link>https://wouterds.com/blog/configure-a-ps3-controller-to-automatically-connect-to-a-raspberry-pi</link>
            <guid>https://wouterds.com/blog/configure-a-ps3-controller-to-automatically-connect-to-a-raspberry-pi</guid>
            <pubDate>Mon, 13 Aug 2018 10:00:00 GMT</pubDate>
            <description><![CDATA[Want to configure manually the master address of your PS3 controller and have it automatically connect to your Raspberry Pi? It's really not that complicated and and takes only 5 minutes to configure! Here's a step-by-step guide, for…]]></description>
            <content:encoded><![CDATA[<p>Want to configure manually the master address of your PS3 controller and have it automatically connect to your Raspberry Pi? It&apos;s really not that complicated and and takes only 5 minutes to configure! Here&apos;s a step-by-step guide, for macOS that is, but it will probably work for Linux as well if you take out the macOS specifics.</p><h2>Prerequisites (macOS)</h2><p>Have homebrew installed, if you don&apos;t have yet; follow the instructions at <a href="https://wouterdeschuyter.be/blog/brew.sh">https://brew.sh/</a>.</p><h3>Install required libs &amp; packages</h3><p>Install <code>libusb</code> and <code>libusb-compat</code> using homebrew (or other package manager for other platforms).</p><pre data-language="bash"><code>brew install wget libusb libusb-compat</code></pre><h3>Compiling sixpair binary</h3><p>Next is compiling our sixpair executable, first download <code>sixpair.c</code> using following command.</p><pre data-language="bash"><code>wget -O sixpair.c https://gist.github.com/wouterds/4ab5715966812009d634e3d034abc7fc/raw</code></pre><p>Now we can compile <code>sixpair.c</code> to an executable using the following command.</p><pre data-language="bash"><code>gcc -o sixpair sixpair.c -lusb</code></pre><p>If everything went well you should now have a <code>sixpair</code> executable.</p><h2>Configuring Bluetooth on the Raspberry Pi</h2><p>Now ssh into you Raspberry Pi (I&apos;m using a Raspberry Pi W V1.2). Let&apos;s start by opening the bluetooth controller by running <code>sudo bluetoothctl</code>. This should have produced something like following output <code>[NEW] Controller B8:27:EB:31:97:DA raspberrypi [default]</code>, my bluetooth MAC address is <code>B8:27:EB:31:97:DA</code>. Copy yours as we&apos;ll need it later.</p><p>Now you&apos;re in the bluetooth controller, run the following commands;</p><pre data-language="bash"><code>agent on
default-agent
discoverable on</code></pre><p>For now, just leave this window open as we&apos;ll need to connect and trust the PS3 controller in a bit.</p><h2>Configuring bluetooth master address for the PS3 controller</h2><p>Let&apos;s connect the PS3 controller to your Mac. Now we can pair the PS3 controller with the sixpair binary we compiled earlier and the bluetooth MAC address we retrieved in the previous step. To do this, run  <code>./sixpair B8:27:EB:31:97:DA</code> (substitute the MAC address with the one your Raspberry Pi outputted). This command will procude something similar as below.</p><pre><code>Current Bluetooth master: 3c:15:c2:e3:d8:47
Setting master bd_addr to b8:27:eb:31:97:da</code></pre><h2>Connecting the PS3 controller to the Raspberry Pi</h2><p>Disconnect the PS3 controller from your Mac and head back to the Raspberry Pi terminal. Power on the PS3 controller and you will see that it tries to connect to the Raspberry Pi in the terminal.</p><pre><code>[NEW] Device 00:1B:FB:2F:59:49 00-1B-FB-2F-59-49
[CHG] Device 00:1B:FB:2F:59:49 Connected: no
[DEL] Device 00:1B:FB:2F:59:49 00-1B-FB-2F-59-49
[NEW] Device 00:1B:FB:2F:59:49 00-1B-FB-2F-59-49
[CHG] Device 00:1B:FB:2F:59:49 Connected: no
[DEL] Device 00:1B:FB:2F:59:49 00-1B-FB-2F-59-49
[NEW] Device 00:1B:FB:2F:59:49 00-1B-FB-2F-59-49
[CHG] Device 00:1B:FB:2F:59:49 Connected: no
[DEL] Device 00:1B:FB:2F:59:49 00-1B-FB-2F-59-49
[NEW] Device 00:1B:FB:2F:59:49 00-1B-FB-2F-59-49
[CHG] Device 00:1B:FB:2F:59:49 Connected: no
[DEL] Device 00:1B:FB:2F:59:49 00-1B-FB-2F-59-49</code></pre><p>Copy the MAC address of the PS3 controller, in this case <code>00:1B:FB:2F:59:49</code>. After the PS3 controller turned off prepare the following command in the terminal (as you will need to be quick) <code>connect 00:1B:FB:2F:59:49</code>. Next power on the PS3 controller again and once you see the line <code>[NEW] Device 00:1B:FB:2F:59:49 00-1B-FB-2F-59-49</code> appearing in the terminal immediately execute the command. If all went well you should see something like the following.</p><pre><code>Attempting to connect to 00:1B:FB:2F:59:49
[CHG] Device 00:1B:FB:2F:59:49 Modalias: usb:v054Cp0268d0100
[CHG] Device 00:1B:FB:2F:59:49 UUIDs: 00001124-0000-1000-8000-00805f9b34fb
[CHG] Device 00:1B:FB:2F:59:49 UUIDs: 00001200-0000-1000-8000-00805f9b34fb
[CHG] Device 00:1B:FB:2F:59:49 ServicesResolved: yes</code></pre><p>Now we need to trust the MAC address by running <code>trust 00:1B:FB:2F:59:49</code>. Normally you should see the following output, and the PS3 controller will be connected to your Raspberry Pi.</p><pre><code>[CHG] Device 00:1B:FB:2F:59:49 Trusted: yes
Changing 00:1B:FB:2F:59:49 trust succeeded
[CHG] Device 00:1B:FB:2F:59:49 Connected: no
[CHG] Device 00:1B:FB:2F:59:49 Connected: yes</code></pre><p>You can now exit the bluetooth controller by running <code>quit</code>. To turn off the PS3 controller, hold the menu button for 10 seconds. Now when you turn on again the PS3 controller it will automatically connect to your Raspberry Pi.</p><p>And that&apos;s about it! 😎🚀</p>]]></content:encoded>
            <enclosure url="https://wouterds.com/images/116253/1703853935-1ebea73a-1ff2-4c62-bad5-2f774b639c08-embed.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[IDLES @ Rock Werchter 08.07.2018]]></title>
            <link>https://wouterds.com/blog/idles-rock-werchter-08-07-2018</link>
            <guid>https://wouterds.com/blog/idles-rock-werchter-08-07-2018</guid>
            <pubDate>Mon, 09 Jul 2018 10:00:00 GMT</pubDate>
            <description><![CDATA[IDLES, a punk rock band from the north of Bristol, never heard about them before. I just saw the description in the Rock Werchter flyer and that they were playing on the smallest stage of the festival. Since there was nothing else…]]></description>
            <content:encoded><![CDATA[<p>IDLES, a punk rock band from the north of Bristol, never heard about them before. I just saw the description in the Rock Werchter flyer and that they were playing on the smallest stage of the festival. Since there was nothing else playing I wanted to see at that time I decided to check them out.</p><iframe src="https://youtube.com/embed/DUjZp5coqe0" width="100%" style="aspect-ratio:16/9"></iframe><p>And man, I was surprised, those guys have a lot of energy! At some point the lead guitar player jumped into the crowd in his underwear ?! And guess what, they all have day jobs and this guy is working as a dentist in London 😂... I really enjoyed their half hour show, check them out when you can!</p><iframe src="https://youtube.com/embed/QkF_G-RF66M" width="100%" style="aspect-ratio:16/9"></iframe>]]></content:encoded>
            <enclosure url="https://wouterds.com/images/116253/1703853831-9cff2126-7fb8-4f7c-b400-f8b20918f911-embed.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Joris Voorn - Antigone]]></title>
            <link>https://wouterds.com/blog/joris-voorn-antigone</link>
            <guid>https://wouterds.com/blog/joris-voorn-antigone</guid>
            <pubDate>Wed, 04 Jul 2018 10:00:00 GMT</pubDate>
            <description><![CDATA[Last weekend I went with some friends to a techno festival in Amsterdam; Awakenings. It was the first time I heard Joris Voorn play live, and to be honest, also first time I heard about him at all. I really enjoyed the set and his music…]]></description>
            <content:encoded><![CDATA[<p>Last weekend I went with some friends to a techno festival in Amsterdam; Awakenings. It was the first time I heard Joris Voorn play live, and to be honest, also first time I heard about him at all. I really enjoyed the set and his music in general, definitely one of the highlights!</p><p><img src="https://wouterds.com/images/116253/1703853685-4d212767-02f7-40a7-9e2c-c3d5b58f40bd.jpg" width="100%"></p><p>After listening to his music on Spotify something started bothering me, he closed his set with this amazing track, but I couldn&apos;t find it on Spotify! After doing some digging I found that Awakenings had uploaded his set with Kölsch from the day before were he played the same track.</p><p>So with that I started asking around on Reddit and other social media, eventually I found someone that claimed he talked with Voorn; he said it&apos;s an unreleased track called <em>&quot;Antigone&quot;</em>. So don&apos;t shoot me if it&apos;s wrong, it&apos;s the rumoured title! I stripped out this track from his set with Kölsch and uploaded it to Youtube. Can&apos;t wait for this to be released 💖!</p><iframe src="https://youtube.com/embed/cGDQVcKLRfo" width="100%" style="aspect-ratio:16/9"></iframe><p>PS: He&apos;s <a href="https://www.facebook.com/events/1913205272100032/">coming somewhere in October to Kompass</a> in Ghent, better get those tickets now 🎟🗓!</p>]]></content:encoded>
            <enclosure url="https://wouterds.com/images/116253/1703853762-166d60c8-d65d-44f8-9ff9-669623fe8586-embed.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Delta 1.7 has been released!]]></title>
            <link>https://wouterds.com/blog/delta-1-7-has-been-released</link>
            <guid>https://wouterds.com/blog/delta-1-7-has-been-released</guid>
            <pubDate>Wed, 21 Mar 2018 11:00:00 GMT</pubDate>
            <description><![CDATA[Since mid February I joined some friends and ex-colleagues to help them out with their startup, Delta. Today we launched the 1.7 version, being the first release that I also contributed a lot to. I'm very proud of what we accomplished in…]]></description>
            <content:encoded><![CDATA[<p>Since mid February I joined some friends and ex-colleagues to help them out with their startup, Delta. Today we launched the 1.7 version, being the first release that I also contributed a lot to. I&apos;m very proud of what we accomplished in just a few weeks and the overall end result. I hope you&apos;ll like it too!</p><p>This release includes:</p><ul><li><p>a new sexy dark mode</p></li><li><p>a renewed watchlist screen with a markets tab</p></li><li><p>more market data on detail screens</p></li><li><p>new &amp; cleaned up graphs</p></li><li><p>many UI tweaks and fixes</p></li><li><p>and a lot bugfixes &amp; performance improvements under the hood</p></li></ul><p><img src="https://wouterds.com/images/116253/1703853522-a26018a3-06be-428b-9a4a-02aa6d6fcb36.jpg" width="100%"></p><p>Why don&apos;t you check it out and download it now?</p><ul><li><p><a href="https://itunes.apple.com/us/app/apple-store/id1288676542?mt=8">Download from iOS App Store</a></p></li><li><p><a href="https://play.google.com/store/apps/details?id=io.getdelta.android">Download from Google Play Store</a></p></li></ul>]]></content:encoded>
            <enclosure url="https://wouterds.com/images/116253/1703853537-901c123d-3cab-4447-9b17-e2047246f856-embed.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Creating a $15 web controllable camera with Raspberry Pi Zero]]></title>
            <link>https://wouterds.com/blog/creating-a-15-web-controllable-camera-with-raspberry-pi-zero</link>
            <guid>https://wouterds.com/blog/creating-a-15-web-controllable-camera-with-raspberry-pi-zero</guid>
            <pubDate>Sun, 04 Mar 2018 11:00:00 GMT</pubDate>
            <description><![CDATA[It's been a while since I did a weekend project, but this weekend was one of these! I wanted to hack something together so I could view a live camera stream from anywhere in the world and control it (as in: rotate it left and right) with…]]></description>
            <content:encoded><![CDATA[<p>It&apos;s been a while since I did a weekend project, but this weekend was one of these! I wanted to hack something together so I could view a live camera stream from anywhere in the world and control it (as in: rotate it left and right) with a simple web interface.</p><p>In this blogpost I&apos;ll describe what materials you need, how to stick them together and how to install the software. I <a href="https://github.com/wouterds/rpi-camera">opensourced it on Github</a>, so the software part is only a matter of copying it and installing.</p><iframe src="https://youtube.com/embed/w49T_qsiwSI" width="100%" style="aspect-ratio:16/9"></iframe><h2>What you need?</h2><p>Let&apos;s get started by going over what you need. The picture below sums it up basically.</p><ul><li><p>A Raspberry Pi Zero W with Raspbian (Lite) installed on it</p></li><li><p>A cheap USB webcam, you can <a href="https://www.ebay.com/itm/272557566075">get it on eBay for a few bucks</a></p></li><li><p>A micro servo motor, also, <a href="https://www.ebay.com/itm/352247557957">get it on eBay for a dollar</a></p></li><li><p>A micro USB male -&gt; USB female converter</p></li><li><p>Some jump wires (male -&gt; female)</p></li><li><p>Pritt Multi Tack (the white gummy-like things, it&apos;s to stick things together)</p></li><li><p>Optional: Some elastics</p></li></ul><p><img src="https://wouterds.com/images/116253/1703853068-7ef9f688-63e4-4bf8-bc65-2f289c60210d.jpg" width="100%"></p><h2>Sticking it all together</h2><h3>Breaking the webcam (or don&apos;t)</h3><p>Now the first thing I did was breaking the <em>handy</em> retractable clip. There&apos;s a bolt behind the sticker, I removed it because I don&apos;t like it and have the full cable rather unfolded. If you have another webcam or don&apos;t really mind the retractable clip, just skip to the next step.</p><p><img src="https://wouterds.com/images/116253/1703853087-e8e7aa19-cb0b-4367-9f9e-1422dfcf229d.jpg" width="100%"></p><h3>Mounting the servo head</h3><p>Next up; find the most semetrical head in the little bag that comes with the servo and place it on the servo wheel. This is where we&apos;ll be mounting the webcam on. Now it&apos;s possible the servo is not perfectly in the middle, you will notice this once we have everything connected and run the app. If it would not be aligned nicely in the middle, just take it off and put it back on it.</p><p><img src="https://wouterds.com/images/116253/1703853103-6882b04c-6645-46b5-886b-b204538e97f5.jpg" width="100%"></p><h3>Mounting the webcam on the servo</h3><p>Put some of the Pritt Multi Tack <em>glue</em> on the servo head and gently push the webcam on it as you can see below. And just to be sure, I added an elastic around it as well.</p><p><img src="https://wouterds.com/images/116253/1703853117-edfe1e87-a193-43a3-8ee5-0e152ae773fb.jpg" width="100%"></p><h3>Connecting the servo the Pi</h3><p>Connect the red servo wire to the +5V, the brown wire to GND and the orange one to GPIO13 on the Pi. I used pin 4 and and pin 6 for +5V &amp; GND, pin 33 is GPIO13. If you&apos;re not sure where those pins are, make sure to check out <a href="https://pinout.xyz/">pinout.xyz</a>, very handy!</p><p><img src="https://wouterds.com/images/116253/1703853139-2c1b68e8-37fe-41f4-907e-6fc4aaf9f675.jpg" width="100%"></p><h3>Sticking the servo on top of the Pi</h3><p>Now that everything is connected we can dump the servo on the Pi housing, I used the remainder of the Pritt Multi Tack glue to mount the servo on the Pi housing.</p><p><img src="https://wouterds.com/images/116253/1703853154-a8f8b50c-475e-4e4d-a673-ce1ac380865f.jpg" width="100%"></p><h3>Clean things up</h3><p>Lastly, I don&apos;t really like a lot of messy cables, so I put some elastics around those to clean that up.</p><p><img src="https://wouterds.com/images/116253/1703853166-01b170d1-976b-4b3b-bc0b-5e7617ad14bf.jpg" width="100%"></p><h2>Installing the software</h2><p>Now before we dive into installing the dependencies and the actual app I&apos;ve created for this, there are a few assumptions. I assume you have worked with the command line before and know how to install software on a headless Raspberry Pi through the command line. I also assume you already have Raspbian (Lite, I always use Lite, but not required) installed on the Raspberry Pi and that it is connected to your local network (have it connected with a network cable or at least Wi-Fi setup).</p><p>If you haven&apos;t installed Raspbian yet, <a href="https://wouterdeschuyter.be/blog/how-to-configure-ssh-and-wifi-on-a-headless-raspberry-pi-zero-w">head over here</a> and follow the instructions.</p><h3>Dependencies</h3><h4>MJPG Streamer</h4><p>To run the camera stream from a USB webcam we&apos;ll make use of the package <code>mjpg-streamer</code>. Before we can install this we&apos;ll need to install a few dependencies first.</p><pre data-language="bash"><code>sudo apt-get update
sudo apt-get install libjpeg8-dev imagemagick libv4l-dev uvcdynctrl git cmake -y</code></pre><p>After installing the dependencies, run the following commands to install mjpg-streamer.</p><pre data-language="bash"><code>git clone https://github.com/jacksonliam/mjpg-streamer.git
cd mjpg-streamer/mjpg-streamer-experimental
make USE_LIBV4L2=true clean all
sudo make install
sudo usermod -aG video pi
sudo modprobe bcm2835-v4l2
cd ../../
rm -rf mjpg-streamer</code></pre><p>Start the camera livestream by running the following command.</p><pre data-language="bash"><code>mjpg_streamer -i &apos;input_uvc.so --device /dev/video0 --fps 30 --resolution VGA --quality 65&apos; -o &apos;output_http.so&apos;</code></pre><p>If you followed all above instructions and everything went ok, you should now be able to see the stream when browsing to <code>http://your-raspberry-pi.local:8080?action=stream</code>. After you verified the stream is working, cancel by running <code>ctrl+c</code> and move on to the next section.</p><p><img src="https://wouterds.com/images/116253/1703853195-8382e443-48b8-4a7a-a8fa-21045128042f.jpg" width="100%"></p><h4>NodeJS</h4><p>Our second dependency is <code>nodejs</code>, run the following commands to install.</p><pre data-language="bash"><code>wget https://nodejs.org/dist/v9.7.1/node-v9.7.1-linux-armv6l.tar.gz
tar xf node-v9.7.1-linux-armv6l.tar.gz
rm -f node-v9.7.1-linux-armv6l/*
pushd node-v9.7.1-linux-armv6l/
sudo cp -R * /usr/local/
popd
rm -rf node-v9.7.1-linux-armv6l
rm node-v9.7.1-linux-armv6l.tar.gz</code></pre><p>Verify node and npm are installed successfully by running the following commands.</p><pre data-language="bash"><code>node --version
npm --version</code></pre><p>This should give a similar output to this.</p><pre data-language="bash"><code>pi@pizero:~ $ node --version
v9.7.1
pi@pizero:~ $ npm --version
5.6.0</code></pre><h4>PiGPIO</h4><p>This is a library that&apos;s internally used by the NodeJS app to communicate with the hardware. Run the following commands to install <code>pigpio</code>.</p><pre data-language="bash"><code>sudo apt-get update
sudo apt-get install pigpio -y</code></pre><p>That should be it for the dependencies, congrats for reaching this point already 👏!</p><h3>Installing the app</h3><p>Start by cloning the repository to your Raspberry Pi by running the following command.</p><pre data-language="bash"><code>git clone https://github.com/wouterds/rpi-camera.git</code></pre><p>Next cd into the directory and install the dependencies, note that this will probably take a while (it took about 20 minutes for me). If you don&apos;t want to wait you can try to <a href="https://github.com/wouterds/rpi-camera/releases/download/1.0.1/rpi-camera.zip">download a compiled zip</a> from the installation on my Pi Zero, however, I am not 100% sure if this will work everywhere.</p><pre data-language="bash"><code>cd rpi-camera
npm set progress=false
npm install
npm set progress=true</code></pre><p>Now build the frontend app by running the following command, this only needs to happen once.</p><pre data-language="bash"><code>npm run build</code></pre><h4>Starting the app</h4><p>Run the following scripts to start the webcam in the background and the app in the backgroundd, note that this might take a few seconds. The servo will run a test check and rotate a few times. After it finished it should be looking straight, if not take the servo head off and place it back on it so it is pointing straight. You should now be able to browse to your Raspberry Pi Zero and control it using the web interface.</p><pre data-language="bash"><code>./scripts/camera-stream-start.sh
./scripts/server-start.sh</code></pre><p>Don&apos;t want to run the app in the background and see the console output? Just run <code>sudo npm start</code>.</p><h4>Autostart on boot</h4><p>Add the following to <code>/etc/rc.local</code> before <code>exit 0</code>.</p><pre data-language="bash"><code>cd /home/pi/rpi-camera
./scripts/camera-stream-start.sh
./scripts/server-start.sh</code></pre><p>And that should be it, enjoy! 🎉</p><p><img src="https://wouterds.com/images/116253/1703853226-4f6881b4-4f11-43d0-b529-98abecddab3f.jpg" width="100%"></p><p><strong>PS:</strong> I know it&apos;s not the most beautiful written piece of code ever, but it&apos;s readable and does the job, I just quickly hacked it together to get something working 😌!</p><p><strong>PS PS:</strong> When I first tried out a USB webcam on a Raspberry Pi a few years ago there was little documentation on how to do this. I just quickly Googled around before publishing this blogpost and noticed there&apos;s already a gazillion guides like it by now. In any case, I already put quite some effort into this and don&apos;t want to quit now, so here&apos;s another one 🤷‍♂️..</p>]]></content:encoded>
            <enclosure url="https://wouterds.com/images/116253/1703853363-4f6881b4-4f11-43d0-b529-98abecddab3f-embed.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Boston Dynamics made a robot dog, similar to the murderous one of Black Mirror]]></title>
            <link>https://wouterds.com/blog/boston-dynamics-made-a-robot-dog-similar-to-the-murderous-one-of-black-mirror</link>
            <guid>https://wouterds.com/blog/boston-dynamics-made-a-robot-dog-similar-to-the-murderous-one-of-black-mirror</guid>
            <pubDate>Sun, 11 Feb 2018 08:21:00 GMT</pubDate>
            <description><![CDATA[I'm not sure if you have seen the last season of Black Mirror, and to be more specific: episode 5, Metalhead. But after seeing this video earlier this week of Boston Dynamics their latest creation I think I almost pooped my pants!
Being…]]></description>
            <content:encoded><![CDATA[<p>I&apos;m not sure if you have seen the last season of Black Mirror, and to be more specific: episode 5, Metalhead. But after seeing this video earlier this week of Boston Dynamics their latest creation I think I almost pooped my pants!</p><iframe src="https://youtube.com/embed/fUyU3lKzoio" width="100%" style="aspect-ratio:16/9"></iframe><p>Being a hobbyist and creating small robots myself I&apos;m always super excited by new technology, robots and electronics in general. But the creations by Boston Dynamics always seemed somewhat weird.</p><p>Last month I binge-watched Black Mirror season 4 together with my girlfriend, and one of the more scary episodes to me was the one called Metalhead. Not going to spoil too much, but basically: it contains murderous robot dogs. Below a short scene of this particular episode.</p><iframe src="https://youtube.com/embed/UEme75f7au4" width="100%" style="aspect-ratio:16/9"></iframe><p>Do you see the similarities? I was honestly stressed out after watching the Boston Dynamics video. <strong>THIS IS THE END PEOPLE!? 🔪</strong></p><iframe src="https://youtube.com/embed/xejjA2AFO5I" width="100%" style="aspect-ratio:16/9"></iframe><p>No, not really. But still, excited &amp; little bit scared for the future 😄!</p>]]></content:encoded>
            <enclosure url="https://wouterds.com/images/116253/1738914256-0bff02c4-48fd-43fd-8a61-4c1590fb5617.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Westworld trailer of second season premiered during the Super Bowl]]></title>
            <link>https://wouterds.com/blog/westworld-trailer-of-second-season-premiered-during-the-super-bowl</link>
            <guid>https://wouterds.com/blog/westworld-trailer-of-second-season-premiered-during-the-super-bowl</guid>
            <pubDate>Mon, 05 Feb 2018 17:31:25 GMT</pubDate>
            <description><![CDATA[I think I'm still recovering from last season's mindfuck. But last night, during the Super Bowl, the first trailer of season 2 premiered! It's HBO's first commercial in 20 years for Super Bowl. And in about 2 months, on April 22nd, the…]]></description>
            <content:encoded><![CDATA[<p>I think I&apos;m still recovering from last season&apos;s mindfuck. But last night, during the Super Bowl, the first trailer of season 2 premiered! It&apos;s HBO&apos;s first commercial in 20 years for Super Bowl. And in about 2 months, on April 22nd, the new season of Westworld will start on HBO. Really looking forward!</p><iframe src="https://youtube.com/embed/dM7tZOLT1qk" width="100%" style="aspect-ratio:16/9"></iframe>]]></content:encoded>
            <enclosure url="https://wouterds.com/images/116253/1738711466-a311257c-8557-4113-8420-c2f5754e6475.jpeg" length="0" type="image/jpeg"/>
        </item>
        <item>
            <title><![CDATA[SpaceX will be launching a Tesla Roadster into space tomorrow with the Falcon Heavy!]]></title>
            <link>https://wouterds.com/blog/spacex-will-be-launching-a-tesla-roadster-into-space-tomorrow-with-the-falcon-heavy</link>
            <guid>https://wouterds.com/blog/spacex-will-be-launching-a-tesla-roadster-into-space-tomorrow-with-the-falcon-heavy</guid>
            <pubDate>Mon, 05 Feb 2018 15:41:00 GMT</pubDate>
            <description><![CDATA[Tomorrow SpaceX will do a test launch of it's much anticipated Falcon Heavy. The launch has been delayed for several months now. SpaceX expected it to be easier since it consists out of 3 strapped together Falcon 9's, but strapping 3…]]></description>
            <content:encoded><![CDATA[<p>Tomorrow SpaceX will do a test launch of it&apos;s much anticipated Falcon Heavy. The launch has been delayed for several months now. SpaceX expected it to be easier since it consists out of 3 strapped together Falcon 9&apos;s, but strapping 3 rockets together was not as easy as they thought it would be.</p><p>The payload of the rocket will consist ouf of Elon Musk&apos;s old Tesla Roadster. That&apos;s a bit of silly payload, but the only goal here is to get the rocket into space and back to the ground without blowing it up. This is quite a task because the rockets will have to separate again in space and all 3 land individually. If all goes well, Elon&apos;s Tesla should be on its way to Mars tomorrow around this time.</p><p>Very excited to see how it will go!</p><iframe src="https://youtube.com/embed/Tk338VXcb24" width="100%" style="aspect-ratio:16/9"></iframe><h2>Update: they launched it successfully!</h2><p>Yesterday evening during bouldering exercises we watched live the rocker launch of the Falcon Heavy! Was super exciting to see and I think the simultaneously rocket landing of the side boosters was probably one of the most impressive things I&apos;ve ever seen! Too bad the third booster landed next to the drone ship, never the less, great success!</p><p><img src="https://wouterds.com/images/116253/1739111929-e6f91b0d-f4ca-4be8-8f14-504ec3062265.jpeg" width="100%"><br><img src="https://wouterds.com/images/116253/1739111931-0f9a78d2-269d-491f-86a6-9018cdc5f2e5.jpeg" width="100%"><br><img src="https://wouterds.com/images/116253/1739111933-3751e8cb-c297-448a-9070-0664e778a2ee.jpeg" width="100%"></p><iframe src="https://youtube.com/embed/wbSwFU6tY1c" width="100%" style="aspect-ratio:16/9"></iframe>]]></content:encoded>
            <enclosure url="https://wouterds.com/images/116253/1739112093-fc538228-8094-4e0a-8461-516d5b153efc.jpeg" length="0" type="image/jpeg"/>
        </item>
        <item>
            <title><![CDATA[How to configure SSH & WiFi on a headless Raspberry Pi Zero W]]></title>
            <link>https://wouterds.com/blog/how-to-configure-ssh-wifi-on-a-headless-raspberry-pi-zero-w</link>
            <guid>https://wouterds.com/blog/how-to-configure-ssh-wifi-on-a-headless-raspberry-pi-zero-w</guid>
            <pubDate>Mon, 15 Jan 2018 11:00:00 GMT</pubDate>
            <description><![CDATA[Something that I struggled with in the past, configuring a headless Raspberry Pi so I can SSH into it over WiFi. It used to be a quite a hassle connecting a monitor with HDMI and a keyboard etc, but these days it's quite easy to do with…]]></description>
            <content:encoded><![CDATA[<p>Something that I struggled with in the past, configuring a headless Raspberry Pi so I can SSH into it over WiFi. It used to be a quite a hassle connecting a monitor with HDMI and a keyboard etc, but these days it&apos;s quite easy to do with the latest version of Raspbian.</p><h2>Download &amp; burn image to card</h2><p>First things first, download the latest version of <a href="https://www.raspberrypi.org/downloads/raspbian/">Raspbian (Lite)</a> and burn it on your your Micro SD Card, a tool I often use for this is <a href="https://etcher.io/">Etcher</a>. Once that is done, remove the card from your computer and plug it back in afterwards so we can configure it.</p><h2>Enable SSH</h2><p>Since a few months (actually already over a year) Raspbian checks if there&apos;s an <code>ssh</code> file present on the /boot volume, if it is, it will configure Raspbian to boot with the SSH deamon on and remove the file.</p><p>So the only thing you really need to do is create an empty file called <code>ssh</code> on the <code>boot </code>volume. The easiest way this can be done is using terminal, changing to the correct location and the following command <code>touch ssh</code>. But of course you can use any file explorer for this as well.</p><p><img src="https://wouterds.com/images/116253/1703852744-b91b63ba-409e-4ebd-8a37-4922a0fb4f97.jpg" width="100%"></p><h2>Configure WiFi</h2><p>The same thing exists for the WiFi configuration file. I only found out about this months after I discovered how to enable ssh. In any case, you can just create a file in the same location on the <code>boot</code> volume called <code>wpa_supplicant.conf</code> and add your WiFi networks in there. Raspbian will copy this to <code>/etc/wpa_supplicant/wpa_supplicant.conf</code> on first boot and delete it afterwards. Note that the Raspberry Pi Zero W only supports 2.4 Ghz WiFi networks.</p><pre data-language="bash"><code>country=BE
ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
update_config=1

network={
    ssid=&quot;Wouter&apos;s Place&quot;
    psk=&quot;azerty123&quot;
}</code></pre><p>And that should be it! 🍻</p>]]></content:encoded>
            <enclosure url="https://wouterds.com/images/116253/1703852720-1dc2033f-371c-4d26-81d7-02e7380c929c-embed.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Quick tip: install PHP 7 on Raspbian using the official testing branch]]></title>
            <link>https://wouterds.com/blog/quick-tip-install-php-7-on-raspbian-using-the-official-testing-branch</link>
            <guid>https://wouterds.com/blog/quick-tip-install-php-7-on-raspbian-using-the-official-testing-branch</guid>
            <pubDate>Sun, 01 Jan 2017 11:00:00 GMT</pubDate>
            <description><![CDATA[With latest version of Raspbian being based on Debian Jessie, this still comes with PHP 5.6 by default. Since PHP 5.6 reaching its end of life, and only security updates support starting tomorrow, it's time to start looking at the newer…]]></description>
            <content:encoded><![CDATA[<p>With latest version of Raspbian being based on Debian Jessie, this still comes with PHP 5.6 by default. Since PHP 5.6 reaching its end of life, and only security updates support starting tomorrow, it&apos;s time to start looking at the newer versions. PHP 7.0 was released last december in 2015 but is still not merged in the stable branch. Luckily there&apos;s a little workaround for this issue!</p><p>To install PHP 7 we must tap into the testing branch of Raspbian. Start with opening <code>/etc/apt/source.list</code>with your favorite editor.</p><pre data-language="bash"><code>sudo nano /etc/apt/sources.list</code></pre><p>And add this line at the end of the file.</p><pre><code>deb http://mirrordirector.raspbian.org/raspbian/ stretch main contrib non-free rpi</code></pre><p>Now what we don&apos;t want is that every package is updated or installed from the stretch (testing) branch. To do this we can set some preferences that we want all packages to be selected from Jessie by default. Open up the following file <code>/etc/apt/preferences</code>.</p><pre data-language="bash"><code>sudo nano /etc/apt/preferences</code></pre><p>And add the following contents to it.</p><pre data-language="text"><code>Package: *
Pin: release n=jessie
Pin-Priority: 600</code></pre><p>Save the file and run an update.</p><pre data-language="bash"><code>sudo apt-get update</code></pre><p>Now if you want to install something from the testing branch, you can do this using the following format.<br>For example, installing the PHP 7 cli package can be done like this.</p><pre data-language="bash"><code>sudo apt-get install -t stretch php7.0-cli</code></pre><p>And that&apos;s pretty much it, enjoy! Since this most likely my last post of 2016, happy new year! 🎆</p>]]></content:encoded>
            <enclosure url="https://wouterds.com/images/116253/1703851006-d44d8993-9ddd-48a3-883b-f1e58ec83a7e-embed.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Quick Guide: Setting up PHP 7.1, NGINX 1.10 & MySQL 5.7 with Docker]]></title>
            <link>https://wouterds.com/blog/quick-guide-setting-up-php-7-1-nginx-1-10-mysql-5-7-with-docker</link>
            <guid>https://wouterds.com/blog/quick-guide-setting-up-php-7-1-nginx-1-10-mysql-5-7-with-docker</guid>
            <pubDate>Tue, 20 Dec 2016 11:00:00 GMT</pubDate>
            <description><![CDATA[After using Vagrant for the past few years and Docker gaining more adoption every day, I decided to give it a go as well. In this post I'll guide you through the installation process and how to configure it and use it with…]]></description>
            <content:encoded><![CDATA[<p>After using Vagrant for the past few years and Docker gaining more adoption every day, I decided to give it a go as well. In this post I&apos;ll guide you through the installation process and how to configure it and use it with docker-compose.</p><p>There&apos;s also a TL;DR with just a download link if you don&apos;t care how it works but just want to get started ☺️!</p><h2>What is Docker 🐳?</h2><p>I&apos;m not going to explain it myself, but I&apos;ll just pick a quote straight from the Docker website. It&apos;s pretty clear and describes everything that Docker does.</p><blockquote><p>Docker containers wrap up a piece of software in a complete filesystem that contains everything it needs to run: code, runtime, system tools, system libraries – anything you can install on a server. This guarantees that it will always run the same, regardless of the environment it is running in.</p></blockquote><h3>Why use Docker over Vagrant, or even a local dev environment?</h3><p>Local dev environments are just always a mess. People always have throuble configuring it, lots of issues with permissions, updates break it etc etc.</p><p>Than we have Vagrant which I have been using mostly for the past few years and runs on a virtual machine. That&apos;s a great thing and makes it easily possible to do compartmentalization and share entire stacks using this config file or even the whole image.</p><p>And than there is Docker. Which offers us a great deal of benefits over classic virtualization. Docker containers are substantially smaller than its equivalent VM. Speed is also an important consideration, using VMs there is a certain loss, while containers arguably have none -- excluding the small overhead for the Docker service. And it&apos;s very easy to configure using repositories from the community and with Docker Compose.</p><p>Of course there&apos;s also downsides and VMs do have some benefits over Docker containers, but I&apos;m not going to go any deeper on that in this topic.</p><h2>Requirements, what do we need?</h2><h3>Docker Engine</h3><p>First of all, we need to install Docker itself by installing Docker Engine. You can download Docker Engine for macOS from <a target="_blank" href="https://docs.docker.com/docker-for-mac/">here</a> or for Windows <a target="_blank" href="https://docs.docker.com/docker-for-windows/">here</a>.</p><h3>Docker Compose</h3><p>After installing Docker Engine, we&apos;ll install Docker Compose. We will use this to define multiple services on multiple containers for our application. This makes it easy to start and stop the whole application with a single command.</p><p>Run the following command to install Docker Compose with curl (<a target="_blank" href="https://docs.docker.com/compose/install/">more in dept installation</a>).</p><pre data-language="bash"><code>curl -L &quot;https://github.com/docker/compose/releases/download/1.9.0/docker-compose-$(uname -s)-$(uname -m)&quot; -o /usr/local/bin/docker-compose</code></pre><p>And now make it executable using this command.</p><pre data-language="bash"><code>chmod +x /usr/local/bin/docker-compose</code></pre><p>That&apos;s it, we&apos;re ready to configure the application!</p><h2>Configuration</h2><h3>docker-compose.yml</h3><p>This is the main configuration file where we specify all the services we want to use and some configuration values for them such as the MySQL user &amp; password and the location of your projects folder. In this case I&apos;m using <code>~/Projects</code> folder (this folder is located on your machine) as the location for the application. MySQL is configured with <code>docker</code> as value for the username, password as well as the database name.</p><p>An important thing to notice is that the first level within services is the also the name of the service to access it within the Docker application itself. Meaning if you want to access the database for example, you&apos;ll need to use <code>mysql</code> as hostname. You can make each service depending on another one and link services with each other if you need to access them or they&apos;re depending on them.</p><p>The ports for both MySQL and NGINX are exposed to our localhost, meaning we can access them via <code>127.0.0.1</code> or <code>localhost</code>. So you should be able to go to <code>http://localhost/</code> in the browser and access the database using Sequel Pro or something simlar from <code>127.0.0.1:3306</code>.</p><p>In the configuration below we&apos;re using MySQL 5.7, NGINX 1.10 (both from image) and PHP-FPM we&apos;re building ourselves using ./php/Dockerfile which we will get to in a minute.</p><pre data-language="yaml"><code>version: &apos;2&apos;

services:
    mysql:
        image: mysql:5.7
        ports:
            - 3306:3306
        volumes:
            - /var/lib/mysql
        restart: always
        environment:
            MYSQL_ROOT_PASSWORD: root
            MYSQL_USER: docker
            MYSQL_PASSWORD: docker
            MYSQL_DATABASE: docker

    nginx:
        image: nginx:1.10.2
        ports:
            - 80:80
        restart: always
        volumes:
            - ./nginx/conf:/etc/nginx/conf.d
            - ~/Projects:/code
        links:
            - php
        depends_on:
            - php

    php:
        build: php
        expose:
            - 9000
        restart: always
        volumes:
            - ./php/conf/php.ini:/usr/local/etc/php/conf.d/custom.ini
            - ~/Projects:/code
        links:
            - mysql</code></pre><h3>php/Dockerfile</h3><p>This file we&apos;ll use build PHP manually instead of downloading a pre-defined image. Using this file we can customize our needs better and install a few extensions such as Curl, GD &amp; Intl.</p><pre data-language="docker"><code>FROM php:7.1-fpm

RUN apt-get update

# Some basic extensions
RUN docker-php-ext-install -j$(nproc) json mbstring opcache pdo pdo_mysql mysqli

# Curl
RUN apt-get install -y libcurl4-openssl-dev
RUN docker-php-ext-install -j$(nproc) curl

# GD
RUN apt-get install -y libpng-dev libjpeg-dev
RUN docker-php-ext-install -j$(nproc) gd

# Intl
RUN apt-get install -y libicu-dev
RUN docker-php-ext-install -j$(nproc) intl</code></pre><h3>php/conf/php.ini</h3><p>Don&apos;t think this needs any explanation, just basic configuration of php.</p><pre data-language="ini"><code>display_errors = On
display_startup_errors = On
default_charset = &quot;UTF-8&quot;
html_errors = On</code></pre><h3>nginx/conf/default.conf</h3><p>And lastly our nginx default config. Just contains some basic stuff like autoindex, support for php and the hostname.</p><pre data-language="nginx"><code>server {
    listen       80 default_server;
    server_name  localhost _;
    index        index.php index.html index.htm;
    root         /code;

    location / {
        try_files   $uri $uri/ /index.php?$query_string;
        autoindex on;
    }

    location ~ \.php$ {
        try_files $uri /index.php =404;
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass php:9000;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }
}</code></pre><h2>Testing application</h2><p>If you have followed this guide completely you should now have a similar directory structure. If not, you can also <a target="_blank" href="https://github.com/wouterds/docker">download it from my repo on Github</a>.</p><pre><code>.                            # Docker directory
├─ nginx                     # NGINX directory
├─── conf                    # NGINX config directory
├───── default.conf          # Basic NGINX configuration
├─ php                       # PHP directory
├─── conf                    # PHP config directory
├───── php.ini               # Basic php configuration
├─── Dockerfile              # PHP Dockerfile
├─ docker-compose.yml        # Docker Compose file</code></pre><p>You can start up your Docker application using the command <code>docker-compose up</code>. And normally, if everything went well, you should be able to go to <a href="http://localhost/">http://localhost/</a>, use PHP and connect to MySQL on port 3306.</p><h2>TL;DR</h2><p>For the people that didn&apos;t bother to read or just don&apos;t care how it works or configure it and just want to get started, don&apos;t worry! I&apos;ve made it very easy for you. You can <a target="_blank" href="https://github.com/wouterds/docker">clone or download my repo</a> and start it using <code>docker-compose up</code>.</p><p>Cheers! 🍻</p>]]></content:encoded>
            <enclosure url="https://wouterds.com/images/116253/1703851517-c1a9d6f2-48f8-49cd-bfd9-154eff361307-embed.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[I received a signed copy of 13 Voices!]]></title>
            <link>https://wouterds.com/blog/i-received-a-signed-copy-of-13-voices</link>
            <guid>https://wouterds.com/blog/i-received-a-signed-copy-of-13-voices</guid>
            <pubDate>Tue, 01 Nov 2016 11:00:00 GMT</pubDate>
            <description><![CDATA[OMG, yes, I received a f*cking signed copy of the latest album of Sum 41! It was signed by each band member and although I've outgrowed their music I couldn't be more happier with it 🤩! I listened countless hours to this band when I was…]]></description>
            <content:encoded><![CDATA[<p>OMG, yes, I received a f*cking signed copy of the latest album of Sum 41! It was signed by each band member and although I&apos;ve outgrowed their music I couldn&apos;t be more happier with it 🤩! I listened countless hours to this band when I was younger and had been a huge fan ever since 💖.</p><p><img src="https://wouterds.com/images/116253/1703851390-1109a207-571e-4dd1-974d-f041694c1b1c.jpg" width="100%"></p><iframe src="https://youtube.com/embed/LhlASZXjSbw" width="100%" style="aspect-ratio:16/9"></iframe>]]></content:encoded>
            <enclosure url="https://wouterds.com/images/116253/1703851354-e7fb75ba-1690-43ac-8eea-99afe6d0a737-embed.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[How to disable viewport scaling in iOS 10? You don't.]]></title>
            <link>https://wouterds.com/blog/how-to-disable-viewport-scaling-in-ios-10-you-don-t</link>
            <guid>https://wouterds.com/blog/how-to-disable-viewport-scaling-in-ios-10-you-don-t</guid>
            <pubDate>Wed, 03 Aug 2016 10:00:00 GMT</pubDate>
            <description><![CDATA[When the first beta of iOS 10 was released it didn't take long before some web developers discovered that user-scalable=no stopped working on their websites. Soon issues started popping up at Stack Overflow and drama happened. Probably…]]></description>
            <content:encoded><![CDATA[<p>When the first beta of iOS 10 was released it didn&apos;t take long before some web developers discovered that <code>user-scalable=no</code> stopped working on their websites. Soon issues started popping up at Stack Overflow and drama happened. Probably some developers are already working on their JS plugins to <em>fix</em> this and gain some extra stars on Github.</p><p>But it shouldn&apos;t be a big drama, and frankly, we shouldn&apos;t make JS polyfills to <em>fix</em> this. Because it isn&apos;t broken. As we can see from the release notes of the first beta of iOS 10, Apple did indeed disable this intentionally.</p><blockquote><p>Content Blockers may stop working after upgrading to iOS 10 beta 1. Workaround: Go to Settings &gt; Safari &gt; Content Blockers and toggle the content blocker switch. Fixed a bug where no progress was shown while downloading large ﬁles. You can now close all tabs in Safari at once by long tapping on the Tab View button. <strong>To improve accessibility on websites in Safari, users can now pinch-to-zoom even when a website sets </strong><code><strong>user-scalable=no</strong></code><strong> in the viewport.</strong></p></blockquote><p>And they&apos;re right. By default a user on iOS can pinch to zoom on mobile websites, if they can&apos;t read something, want to zoom in on an image, you name it. It&apos;s the default user behaviour, and it makes sense. Some developers or designers like to prevent people from scaling so their website always <em>looks pretty and like it was designed</em>. Instead, just set an initial viewport scale and be done with it. Don&apos;t worry!</p><pre data-language="html"><code>&lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1&quot;&gt;</code></pre><p>Luckily, this behaviour is only applying on Safari Mobile. So that means that web apps or apps with embedded web views will still respect <code>user-scalable=no</code>. For now.</p><p>It&apos;s all good guys, keep calm and have a beer 🍺!</p>]]></content:encoded>
            <enclosure url="https://wouterds.com/images/116253/1703851142-158573dc-4a24-463b-b8c5-03a32e33c6ae-embed.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Quick tip: loop over every day between 2 dates in PHP]]></title>
            <link>https://wouterds.com/blog/quick-tip-loop-over-every-day-between-2-dates-in-php</link>
            <guid>https://wouterds.com/blog/quick-tip-loop-over-every-day-between-2-dates-in-php</guid>
            <pubDate>Sat, 30 Jul 2016 10:00:00 GMT</pubDate>
            <description><![CDATA[Ever needed to loop over every day between 2 dates for a data set you need? This can come in handy when making charts for example. Luckily, it's super easy to do this with some date & time extensions.
Let's say you want to loop over…]]></description>
            <content:encoded><![CDATA[<p>Ever needed to loop over every day between 2 dates for a data set you need? This can come in handy when making charts for example. Luckily, it&apos;s super easy to do this with some date &amp; time extensions.</p><p>Let&apos;s say you want to loop over every day between 2 specific dates, we can achieve this result with this code:</p><pre data-language="php"><code>$startDate = new DateTime(&apos;2016-03-15&apos;);
$endDate = new DateTime(&apos;2016-05-01&apos;);

// These are the steps in which we will be iterating
$interval = DateInterval::createFromDateString(&apos;1 day&apos;);

// Create the period between the start/end  date and the interval
$period = new DatePeriod($startDate, $interval, $endDate);

// Loop over the period
foreach ($period as $dateTime) {
    // A certain day in this period
    $date = $dateTime-&gt;format(&apos;Y-m-d&apos;);
}</code></pre><p>We can also do this with relative times by changing the start date and end date. For example, the last 30 days:</p><pre data-language="php"><code>$startDate = new DateTime(&apos;now - 30 days&apos;);
$endDate = new DateTime(&apos;now&apos;);</code></pre><p>Obviously there&apos;s many ways to get around this, this is one way to quickly achieve this using PHP only.</p><p>Cheers 🍻!</p>]]></content:encoded>
            <enclosure url="https://wouterds.com/images/116253/1703851006-d44d8993-9ddd-48a3-883b-f1e58ec83a7e-embed.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Bring Me The Horizon live in Ancienne Belgique on 05.04.2016]]></title>
            <link>https://wouterds.com/blog/bring-me-the-horizon-live-in-ancienne-belgique-on-05-04-2016</link>
            <guid>https://wouterds.com/blog/bring-me-the-horizon-live-in-ancienne-belgique-on-05-04-2016</guid>
            <pubDate>Thu, 07 Apr 2016 10:00:00 GMT</pubDate>
            <description><![CDATA[Bring Me The Horizon named after "Now, bring me the horizon", the last line of Jack Sparrow in the Pirates of the Caribbean movie "Curse of the Black Pearl", was playing last night in Brussels in the Ancienne Belgique. After the original…]]></description>
            <content:encoded><![CDATA[<p>Bring Me The Horizon named after <em>&quot;Now, bring me the horizon&quot;</em>, the last line of Jack Sparrow in the Pirates of the Caribbean movie &quot;Curse of the Black Pearl&quot;, was playing last night in Brussels in the Ancienne Belgique. After the original date, the 22th of November 2015, was cancelled due to the terror alert in Brussels, they re-scheduled for the 5th of April 2016. So happy I got to see them 🤩!</p><iframe src="https://youtube.com/embed/WN2xIPkLWk8" width="100%" style="aspect-ratio:16/9"></iframe>]]></content:encoded>
            <enclosure url="https://wouterds.com/images/116253/1703850900-4bb9e312-f82b-4730-9518-8c34e99194d4-embed.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Husky Safari, Lapland 2015]]></title>
            <link>https://wouterds.com/blog/husky-safari-lapland-2015</link>
            <guid>https://wouterds.com/blog/husky-safari-lapland-2015</guid>
            <pubDate>Sat, 23 Jan 2016 08:30:00 GMT</pubDate>
            <description><![CDATA[Now that I have been working more than a year, I was able to save up some money & go on my first solo trip - so I did! Last December I went with Joker for a week on husky safari in Muonio, Finland. With temperatures reaching -20 ºC it…]]></description>
            <content:encoded><![CDATA[<p>Now that I have been working more than a year, I was able to save up some money &amp; go on my first solo trip - so I did! Last December I went with Joker for a week on husky safari in Muonio, Finland. With temperatures reaching -20 ºC it was at times very cold, but I really enjoyed the overal trip. Because we only had a few hours light every day we usually were only a couple of hours on the sled and spent most other time taking care of the dogs, enjoy the sauna, cook some food and play some games.</p><p>One of the absolute highlights for me personally was seeing the northern lights and stars so bright I could never imagine, it&apos;s just not comparable at all to Belgium. I think I will definitely do something like this again in the future. I could talk for hours about this trip, but since writing isn&apos;t really my strongsuit I&apos;ll just let the pics speak for themselves!</p><p><img src="https://wouterds.com/images/116253/1703874778-gopr4457-2.jpg" width="100%"><br><img src="https://wouterds.com/images/116253/1703874786-gopr4512-2.jpg" width="100%"><br><img src="https://wouterds.com/images/116253/1703874797-gopr4590-2.jpg" width="100%"><br><img src="https://wouterds.com/images/116253/1703874802-gopr4595-2.jpg" width="100%"><br><img src="https://wouterds.com/images/116253/1703874807-gopr4623-2.jpg" width="100%"><br><img src="https://wouterds.com/images/116253/1703874147-img_0425.jpg" width="100%"><br><img src="https://wouterds.com/images/116253/1703874161-img_0408.jpg" width="100%"><br><img src="https://wouterds.com/images/116253/1703874141-img_0410.jpg" width="100%"><br><img src="https://wouterds.com/images/116253/1703874174-img_0386.jpg" width="100%"><br><img src="https://wouterds.com/images/116253/1703874179-img_0375.jpg" width="100%"><br><img src="https://wouterds.com/images/116253/1703874153-img_0392.jpg" width="100%"><br><img src="https://wouterds.com/images/116253/1703874169-img_0393.jpg" width="100%"></p>]]></content:encoded>
            <enclosure url="https://wouterds.com/images/116253/1703874874-meta.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Pimping macOS default nano, syntax highlighting and more]]></title>
            <link>https://wouterds.com/blog/pimping-macos-default-nano-syntax-highlighting-and-more</link>
            <guid>https://wouterds.com/blog/pimping-macos-default-nano-syntax-highlighting-and-more</guid>
            <pubDate>Wed, 12 Aug 2015 10:00:00 GMT</pubDate>
            <description><![CDATA[Recently I was editing a php file on a Linux server and I noticed it had some basic highlighting. The reason why I noticed is because on macOS everything is always in the default terminal color without highlighting.
Turns out you can…]]></description>
            <content:encoded><![CDATA[<p>Recently I was editing a php file on a Linux server and I noticed it had some basic highlighting. The reason why I noticed is because on macOS everything is always in the default terminal color without highlighting.</p><p>Turns out you can enable it for macOS as well with little effort!</p><h2>Some examples, to convince you, if you haven&apos;t been already</h2><p>Default macOS nano:</p><p><img src="https://wouterds.com/images/116253/1703850756-c9e3486f-b708-45f5-a858-4e641e8893a2.jpg" width="100%"></p><p>With the updated nano:</p><p><img src="https://wouterds.com/images/116253/1703850761-ee4784c7-e13b-4e54-8016-544d0a563d25.jpg" width="100%"><br><img src="https://wouterds.com/images/116253/1703850765-4523303c-6b29-441c-8f0d-596353d1a430.jpg" width="100%"></p><h2>Enough showing off, how do I get it?</h2><p>Assuming you know what <a target="_blank" href="http://brew.sh/">homebrew</a> is and have it installed &amp; set up correctly; it&apos;s really just a matter of running the following 2 commands, not much else to it!</p><pre data-language="bash"><code>brew install nano
echo &quot;include /usr/local/share/nano/*.nanorc&quot; &gt; ~/.nanorc</code></pre><p>Enjoy 🍻</p>]]></content:encoded>
            <enclosure url="https://wouterds.com/images/116253/1703850706-ee4784c7-e13b-4e54-8016-544d0a563d25-embed.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Urbex: Train wreck in port of Ghent]]></title>
            <link>https://wouterds.com/blog/urbex-train-wreck-in-port-of-ghent</link>
            <guid>https://wouterds.com/blog/urbex-train-wreck-in-port-of-ghent</guid>
            <pubDate>Mon, 09 Mar 2015 11:00:00 GMT</pubDate>
            <description><![CDATA[Sun was shining, 18 degrees and it was even going to get warmer that afternoon. Perfect day to visit an urbex location! I heard about a train wreck in the port of Ghent, so I went to go have a…]]></description>
            <content:encoded><![CDATA[<p>Sun was shining, 18 degrees and it was even going to get warmer that afternoon. Perfect day to visit an urbex location! I heard about a train wreck in the port of Ghent, so I went to go have a look.</p><p><img src="https://wouterds.com/images/116253/1703850605-40e7f5cc-4f2a-4ce3-9254-a47ba33e0bea.jpg" width="100%"><br><img src="https://wouterds.com/images/116253/1703850610-eadeedc6-1421-4f01-be59-01139374fc43.jpg" width="100%"><br><img src="https://wouterds.com/images/116253/1703850615-dd6ed842-a167-40fd-b5fb-941a678bedd7.jpg" width="100%"><br><img src="https://wouterds.com/images/116253/1703850620-ac87d5a4-7014-4d6e-a049-6080c3102bcc.jpg" width="100%"><br><img src="https://wouterds.com/images/116253/1703850624-2815c16d-112e-4e48-a2a2-d558785e5b77.jpg" width="100%"><br><img src="https://wouterds.com/images/116253/1715664179-img_9451.jpg" width="100%"></p>]]></content:encoded>
            <enclosure url="https://wouterds.com/images/116253/1703850545-a95112ec-02d0-4e66-8055-b44f629ee7cf-embed.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Urbex: ECVB Power Plant]]></title>
            <link>https://wouterds.com/blog/urbex-ecvb-power-plant</link>
            <guid>https://wouterds.com/blog/urbex-ecvb-power-plant</guid>
            <pubDate>Sat, 07 Mar 2015 17:35:00 GMT</pubDate>
            <description><![CDATA[A co-worker was showing me some pictures of an old factory he went photographing, I instantly fell in love with urbex 😍. It reminded me of a steampunk game I used to play; BioShock. I just wanted to do this as well!
In the 1900s many…]]></description>
            <content:encoded><![CDATA[<p>A co-worker was showing me some pictures of an old factory he went photographing, I instantly fell in love with urbex 😍. It reminded me of a steampunk game I used to play; BioShock. I just wanted to do this as well!</p><blockquote><p>In the 1900s many Belgian villages were still lit using petroleum lamps, as electricity hadn’t been made available. By 1913, many of these had been updated to Kerosene lamps.</p><p>In 1903, the company that owned the tram system that serviced the canals founded a regional electricity company the &quot;Société Centrale d’Electricité du Brabant&quot; (CEB). The Oisquercq power plant in Western Brabant, which was the first major power plant in the country, became part of CEB.</p><p>In 1905 , the &quot;Reunions Societes d’Energie du Bassin de l’Escaut&quot; emerged in Antwerp (this is later to be known as Electrabel).</p><p>In 1911 Baron Floris Van Loo, with the help of the Bank of Outremer, built the the first power plant in Langerbrugge. Langerbrugge is situated in the Ghent canal area, it is joined with Kerkbrugge, to make the residential area Kerkbrugge-Langerbrugge which is part of the municipality of Evergem. Langerbrugge is located on the western side of the canal Ghent-Terneuzen and this plant was to make the industrial development of the entire canal possible.</p></blockquote><p><img src="https://wouterds.com/images/116253/1703684039-9f7b10db-71f9-4d21-81fb-13d1bf3aa835.jpg" width="100%"><br><img src="https://wouterds.com/images/116253/1703684044-16313ee0-2c66-41f9-a43d-e8d4e6fbace2.jpg" width="100%"><br><img src="https://wouterds.com/images/116253/1703684048-cf6fcc18-8526-4905-b64c-22de82aa65f0.jpg" width="100%"><br><img src="https://wouterds.com/images/116253/1703684052-73f84708-5059-4b37-bdf3-6af32ca805c6.jpg" width="100%"><br><img src="https://wouterds.com/images/116253/1703684057-e7bfc6d4-4905-410a-9f00-ef16bec19306.jpg" width="100%"></p>]]></content:encoded>
            <enclosure url="https://wouterds.com/images/116253/1703684067-9f7b10db-71f9-4d21-81fb-13d1bf3aa835-embed.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Beautiful timelapse from El Teide]]></title>
            <link>https://wouterds.com/blog/beautiful-timelapse-from-el-teide</link>
            <guid>https://wouterds.com/blog/beautiful-timelapse-from-el-teide</guid>
            <pubDate>Sat, 17 Dec 2011 12:53:00 GMT</pubDate>
            <description><![CDATA[Yesterday when I was browsing a little and stumbled upon this movie. It's a great inspiring timelapse taken from the highest mountain of Spain, the El Teide. It's a vulcano of 3718 meter high and it's listed as world heritage.
Take your…]]></description>
            <content:encoded><![CDATA[<p>Yesterday when I was browsing a little and stumbled upon this movie. It&apos;s a great inspiring timelapse taken from the highest mountain of Spain, the El Teide. It&apos;s a vulcano of 3718 meter high and it&apos;s listed as world heritage.</p><iframe src="https://player.vimeo.com/video/22439234" width="100%" style="aspect-ratio:16/9"></iframe><p>Take your time to watch this beautiful timelapse video of the milky way galaxy and the nature around this mountain, it&apos;s absolutely breathtaking!</p>]]></content:encoded>
            <enclosure url="https://wouterds.com/images/116253/1739109211-145027281-cf3e3e047a52e2210b26bbcf42fcde909a80a7dd023a757b95af01936d065ec0-d.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Crypteks USB, inspired USB drive by The Da Vinci Code]]></title>
            <link>https://wouterds.com/blog/crypteks-usb-inspired-usb-drive-by-the-da-vinci-code</link>
            <guid>https://wouterds.com/blog/crypteks-usb-inspired-usb-drive-by-the-da-vinci-code</guid>
            <pubDate>Wed, 14 Dec 2011 09:35:00 GMT</pubDate>
            <description><![CDATA[If you ever have to transfer or store some very sensitive data, this is the answer to it! A cryptex like USB drive featuring a physical lock and 256-bit AES hardware encryption to store all your sensitive and private data.
What is…]]></description>
            <content:encoded><![CDATA[<p>If you ever have to transfer or store some very sensitive data, this is the answer to it! A cryptex like USB drive featuring a physical lock and 256-bit AES hardware encryption to store all your sensitive and private data.</p><h3>What is it?</h3><p>Inspired by the &quot;cryptex device&quot; from the Da Vinci code, <a target="_blank" href="https://crypteks.com">Crypteks</a> designed a cryptex like USB drive to protect sensitve or private data. It consists of an aluminum-bodied cylinder with five rings, each containing the letters of the alphabet. Twisting these to the correct combination lets the USB stick within slide free.</p><p>With not only a physical cryptex lock, which allows 14.348.907 unique passwords combinations, but also a 256-bit AES hardware encryption your data is stored on probably one of the most secure USB drives around at the moment (if you didn&apos;t used the same password for the hardware encryption and physical lock at least)!</p><p><img src="https://wouterds.com/images/116253/1739108699-05.jpg" width="100%"></p><h3>Price</h3><p>The company is currently <a target="_blank" href="https://www.kickstarter.com/projects/crypteks/crypteks-usbtm-encrypted-and-lockable-usb-solution">fundraising on Kickstarter</a> and had a goal of 12.000 USD, but has already gained over 130.000 USD with still 8 days to go! If you donate 130 USD, you will get a 8GB version of the USB drive, if you donate 160 USD you will get a 16GB version of the USB drive. So my guess is that you will have to pay a 110 USD for the 4GB version, a 130 USD for the 8GB version and a 160 USD for the 16GB version once it becomes for sale in a shop. This is a bit pricy for a USB drive, but privacy is priceless right?</p><iframe src="https://player.vimeo.com/video/32704540" width="100%" style="aspect-ratio:16/9"></iframe>]]></content:encoded>
            <enclosure url="https://wouterds.com/images/116253/1739108820-crypteks-usb-flash-drive-1.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Hello World!]]></title>
            <link>https://wouterds.com/blog/hello-world</link>
            <guid>https://wouterds.com/blog/hello-world</guid>
            <pubDate>Thu, 17 Mar 2011 10:00:00 GMT</pubDate>
            <description><![CDATA[Hello World!
Yes indeed, this is my first post on my new blog, be ready to expect more soon!
Of course I am very proud on my fully custom written website, including the blog, scripts library, portfolio etc. Please take care of it, and…]]></description>
            <content:encoded><![CDATA[<p>Hello World!</p><p>Yes indeed, this is my first post on my new blog, be ready to expect more soon!<br>Of course I am very proud on my fully custom written website, including the blog, scripts library, portfolio etc. Please take care of it, and don&apos;t pollute it with useless spam comments.</p><p>Although I&apos;m not entirely sure about what I&apos;m going to write here.. Probably about my passion for online media, especially webdesign &amp; webdevelopment. But also about my personal life, and other things which are interesting other than online media &amp; webdevelopment. Maybe also a little bit about cooking and my cat.<br><br>Don&apos;t expect long boring blog posts because I&apos;m not that kind of writer! :)<br><br>Talk soon</p>]]></content:encoded>
        </item>
    </channel>
</rss>