<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Gabor Heja</title>
	<atom:link href="https://gabor.heja.hu/blog/feed/" rel="self" type="application/rss+xml" />
	<link>https://gabor.heja.hu/blog</link>
	<description></description>
	<lastBuildDate>Tue, 10 Mar 2026 19:17:51 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=5.3.2</generator>
	<item>
		<title>Alley Kitty &#8211; my JS13k Games 2025 entry</title>
		<link>https://gabor.heja.hu/blog/2026/02/09/alley-kitty-my-js13k-games-2025-entry/</link>
				<comments>https://gabor.heja.hu/blog/2026/02/09/alley-kitty-my-js13k-games-2025-entry/#respond</comments>
				<pubDate>Mon, 09 Feb 2026 09:00:00 +0000</pubDate>
		<dc:creator><![CDATA[Gabor Heja]]></dc:creator>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[Dev]]></category>
		<category><![CDATA[gamedev]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[js]]></category>
		<category><![CDATA[js13k]]></category>
		<category><![CDATA[js13kgames]]></category>

		<guid isPermaLink="false">https://gabor.heja.hu/blog/?p=732</guid>
				<description><![CDATA[The theme for 2025 was Black cat, I immediately though of the 40 year old game, Alley Cat (online version, &#8230; <a href="https://gabor.heja.hu/blog/2026/02/09/alley-kitty-my-js13k-games-2025-entry/" class="more-link"><span class="more-button">Continue reading &#62;<span class="screen-reader-text">Alley Kitty &#8211; my JS13k Games 2025 entry</span></span></a>]]></description>
								<content:encoded><![CDATA[
<p>The theme for 2025 was <em>Black cat</em>, I immediately though of the 40 year old game, <a href="https://en.wikipedia.org/wiki/Alley_Cat_(video_game)">Alley Cat</a> (<a href="https://www.playdosgames.com/play/alley-cat">online version</a>, <a href="https://www.youtube.com/watch?v=uZDG4dlU5uY">gameplay video</a>), one of the first games I ever played. I was thinking about making a newer version with the same mechanics, some updates and extensions &#8211; and apparently I could not let this idea go.</p>



<p>One of the most memorable part for me was the sound track, so I really wanted to have that in the game.</p>



<h2>Graphics</h2>



<p>This year I decided to go with vector graphics. I had a simple path-to-custom-array converter tool I made for my rather failed <a href="https://github.com/gheja/js13k2023">Trado Polo Explores</a> game for js13k Games 2023, so I built upon it.</p>



<p>The current tool is taking a bunch of SVG files, extracts the <code>path</code> elements from it and processes their <code>d</code> (the path definition) and <code>style</code> (fill color, fill style, stroke color, etc. parameters) attributes, then it scales them to a 100&#215;100 grid, rounds the coordinates and then save all the points in an array, along with the . It creates an array from the styles (keeping only the unique ones) and assigns these styles to the path.</p>



<p>The 100&#215;100 grid and coordinate rounding is introduced to use less data, it generally leads to no visible artifacts, except for large shapes.</p>



<p>Let&#8217;s say we have this triangle on a 300&#215;300 canvas (the arrows and coordinates are there for the explanation only).</p>



<div class="wp-block-image"><figure class="aligncenter size-large is-resized"><img src="https://gabor.heja.hu/blog/wp-content/uploads/2025/09/graphics_example_1-1024x1024.png" alt="" class="wp-image-736" width="1024" height="1024" srcset="https://gabor.heja.hu/blog/wp-content/uploads/2025/09/graphics_example_1-1024x1024.png 1024w, https://gabor.heja.hu/blog/wp-content/uploads/2025/09/graphics_example_1-300x300.png 300w, https://gabor.heja.hu/blog/wp-content/uploads/2025/09/graphics_example_1-150x150.png 150w, https://gabor.heja.hu/blog/wp-content/uploads/2025/09/graphics_example_1-768x768.png 768w, https://gabor.heja.hu/blog/wp-content/uploads/2025/09/graphics_example_1-666x666.png 666w, https://gabor.heja.hu/blog/wp-content/uploads/2025/09/graphics_example_1.png 1134w" sizes="(max-width: 799px) 94vw, (max-width: 1023px) 67vw, (max-width: 1365px) 62vw, 1037" /><figcaption>A picture of an example SVG</figcaption></figure></div>



<p>This results in the following SVG file:</p>



<figure class="wp-block-image size-large"><img src="https://gabor.heja.hu/blog/wp-content/uploads/2025/09/image-2-1024x653.png" alt="" class="wp-image-742" srcset="https://gabor.heja.hu/blog/wp-content/uploads/2025/09/image-2-1024x653.png 1024w, https://gabor.heja.hu/blog/wp-content/uploads/2025/09/image-2-300x191.png 300w, https://gabor.heja.hu/blog/wp-content/uploads/2025/09/image-2-768x489.png 768w, https://gabor.heja.hu/blog/wp-content/uploads/2025/09/image-2.png 1290w" sizes="(max-width: 799px) 94vw, (max-width: 1023px) 67vw, (max-width: 1365px) 62vw, 1037" /><figcaption>The SVG opened in a text editor, with the important parts highlighted</figcaption></figure>



<p>The converter will pick this up and will give us a result like this:</p>



<figure class="wp-block-image size-large"><img src="https://gabor.heja.hu/blog/wp-content/uploads/2025/09/image-1.png" alt="" class="wp-image-738" srcset="https://gabor.heja.hu/blog/wp-content/uploads/2025/09/image-1.png 995w, https://gabor.heja.hu/blog/wp-content/uploads/2025/09/image-1-300x165.png 300w, https://gabor.heja.hu/blog/wp-content/uploads/2025/09/image-1-768x422.png 768w" sizes="(max-width: 799px) 94vw, (max-width: 1023px) 67vw, (max-width: 1365px) 62vw, 1037" /><figcaption>The output of the converter tool for the triangle above</figcaption></figure>



<p>This definition is then later used to reconstruct an SVG that is very close to the original one (shown using dotted line and gray coordinates), like this:</p>



<figure class="wp-block-image size-large"><img src="https://gabor.heja.hu/blog/wp-content/uploads/2025/09/graphics_example_1_after-1024x1024.png" alt="" class="wp-image-739" srcset="https://gabor.heja.hu/blog/wp-content/uploads/2025/09/graphics_example_1_after-1024x1024.png 1024w, https://gabor.heja.hu/blog/wp-content/uploads/2025/09/graphics_example_1_after-300x300.png 300w, https://gabor.heja.hu/blog/wp-content/uploads/2025/09/graphics_example_1_after-150x150.png 150w, https://gabor.heja.hu/blog/wp-content/uploads/2025/09/graphics_example_1_after-768x768.png 768w, https://gabor.heja.hu/blog/wp-content/uploads/2025/09/graphics_example_1_after-666x666.png 666w, https://gabor.heja.hu/blog/wp-content/uploads/2025/09/graphics_example_1_after.png 1134w" sizes="(max-width: 799px) 94vw, (max-width: 1023px) 67vw, (max-width: 1365px) 62vw, 1037" /><figcaption>The reconstructed shape, overlaid with the original shape</figcaption></figure>



<p>The difference between the SVG and the converted image definition might not look impressive at first, but when having a real-world file with multiple shapes and the coordinates with fractional numbers, also when combining multiple files, it can add up quickly. Check this example of four files:</p>



<figure class="wp-block-image size-large"><img src="https://gabor.heja.hu/blog/wp-content/uploads/2025/09/four_svgs-1024x687.png" alt="" class="wp-image-743" srcset="https://gabor.heja.hu/blog/wp-content/uploads/2025/09/four_svgs-1024x687.png 1024w, https://gabor.heja.hu/blog/wp-content/uploads/2025/09/four_svgs-300x201.png 300w, https://gabor.heja.hu/blog/wp-content/uploads/2025/09/four_svgs-768x515.png 768w, https://gabor.heja.hu/blog/wp-content/uploads/2025/09/four_svgs.png 1198w" sizes="(max-width: 799px) 94vw, (max-width: 1023px) 67vw, (max-width: 1365px) 62vw, 1037" /><figcaption>Four images converted to the custom format (first row: the images, second row: the SVG files, third row: the converted array)</figcaption></figure>



<figure class="wp-block-image size-large"><img src="https://gabor.heja.hu/blog/wp-content/uploads/2025/09/four_svgs_2-1024x696.png" alt="" class="wp-image-744" srcset="https://gabor.heja.hu/blog/wp-content/uploads/2025/09/four_svgs_2-1024x696.png 1024w, https://gabor.heja.hu/blog/wp-content/uploads/2025/09/four_svgs_2-300x204.png 300w, https://gabor.heja.hu/blog/wp-content/uploads/2025/09/four_svgs_2-768x522.png 768w, https://gabor.heja.hu/blog/wp-content/uploads/2025/09/four_svgs_2.png 1226w" sizes="(max-width: 799px) 94vw, (max-width: 1023px) 67vw, (max-width: 1365px) 62vw, 1037" /><figcaption>The converted array</figcaption></figure>



<p>When compressed using ZIP and then advzip the four SVG files result in a 4,031 bytes ZIP, the JS results in a 587 bytes ZIP.</p>



<p>When compressing all the SVGs used in the game it results in a 81,712 bytes ZIP, in JS it is 4,459 bytes.</p>



<p><em>An interesting side note: ZIP needs to store metadata of every file and that takes up some space too. Also, ZIP is compressing every file separately. So &#8211; although the result is nonsense &#8211; if I just copied all SVG files into one file, the file size was the same but ZIP compressed it to 18,248 bytes instead &#8211; that is less than 1/4 of the previous 81,712 bytes!</em></p>



<h2>Music</h2>



<p>The music starts as a MIDI file, but that would have been too much for this project &#8211; both in features and in size. So I went with converting it to the bare minimum &#8211; a custom format denoting only the tempo, timestamps of notes, and the pitch of the notes.</p>



<p>To play it back I am generating sound samples with <a href="https://killedbyapixel.github.io/ZzFX/">ZzFX</a> to use them as instruments, and a simple loop to play them at the right time.</p>



<p>This way the original MIDI starting at 3,410 bytes results in music definition of 2,492 bytes that can be compressed down to 594 bytes. (The MIDI could be compressed to around 1,100 bytes.)</p>



<h2>Compression</h2>



<p>I write the code in TypeScript to have the advantage of strong(er) typing and get rid of many of JavaScript&#8217;s <s>annoyances</s> unique features, and the help of my IDE (<a href="https://code.visualstudio.com/">VSCode</a>).</p>



<p>For the build I just merge all TS source files into one file and use <a href="https://www.npmjs.com/package/@tscc/tscc">TSCC </a>to compile the TypeScript to JavaScript, inserting JSDoc annotations and other useful things for further processing, then use <a href="https://www.npmjs.com/package/google-closure-compiler">Google Closure Compiler</a> to compress it.</p>



<p>The HTML is usually really simple, so I just create a cleaned up version manually.</p>



<p>For CSS I have a simple oneliner to get rid of the white spaces and the unnecessary newlines.</p>



<p>After this I put all the files into one single index.html, because it results in smaller ZIP, as each additional file needs additional metadata.</p>



<p>For the final compression I use <a href="https://github.com/amadvance/advancecomp">AdvanceCOMP</a>&#8216;s advzip, which is a program that re-compresses ZIP files with different parameters, trying to squeeze the ZIP file even smaller.</p>



<p>With fairly conservative settings (3000 iterations) it can save 5-8% compared to the &#8220;high&#8221; compression setting of standard ZIP. This can be a huge deal with size limited projects, with the js13k limit of 13,312 bytes it can squeeze an additional 1,150 bytes in. Higher settings can lead to even more savings, although typically only a few more bytes &#8211; but there were years when I needed those extra 1-2 bytes to fit into the limit.</p>



<p>This year my standard ZIP file was 13,940 bytes (628 bytes over the limit), advzip re-compressed it to 12,919 bytes (394 bytes under the limit).</p>



<p>This size consisted of the following parts: JavaScript (7,204 bytes), graphics data (4,459 bytes), music data (594 bytes), HTML (346 bytes), CSS (315 bytes).</p>



<figure class="wp-block-image size-large"><img src="https://gabor.heja.hu/blog/wp-content/uploads/2025/09/final_zip_6px_2-1.png" alt="" class="wp-image-734" srcset="https://gabor.heja.hu/blog/wp-content/uploads/2025/09/final_zip_6px_2-1.png 1003w, https://gabor.heja.hu/blog/wp-content/uploads/2025/09/final_zip_6px_2-1-300x193.png 300w, https://gabor.heja.hu/blog/wp-content/uploads/2025/09/final_zip_6px_2-1-768x493.png 768w" sizes="(max-width: 799px) 94vw, (max-width: 1023px) 67vw, (max-width: 1365px) 62vw, 1037" /><figcaption>The final sizes of the different parts of the game (approximate,  1 block = 1 byte, 1 row = 128 blocks)</figcaption></figure>



<p><em>Note: these sizes are approximations as there is no way to really  determine the exact size of a part of the file taking up, so I  compressed these parts separately and then scaled their sizes to the  final ZIP size. (They were compressed to a bigger total separately. Also, when I tried by replacing/removing parts, that lead to smaller html/css size approximations.) </em></p>



<p>I have compiled a chart that shows all steps of the build process, starting from source files, then assembling, compiling, minimizing the parts and finally compressing them.</p>



<p>After the competition ended I started to experiment with <a href="https://github.com/lifthrasiir/roadroller">Roadroller</a> (see the dotted line) and I might go with it next year.</p>



<figure class="wp-block-image size-large"><img src="https://gabor.heja.hu/blog/wp-content/uploads/2026/02/flow_chart_drawing_3_final-1024x361.png" alt="" class="wp-image-748" srcset="https://gabor.heja.hu/blog/wp-content/uploads/2026/02/flow_chart_drawing_3_final-1024x361.png 1024w, https://gabor.heja.hu/blog/wp-content/uploads/2026/02/flow_chart_drawing_3_final-300x106.png 300w, https://gabor.heja.hu/blog/wp-content/uploads/2026/02/flow_chart_drawing_3_final-768x271.png 768w, https://gabor.heja.hu/blog/wp-content/uploads/2026/02/flow_chart_drawing_3_final-1536x542.png 1536w, https://gabor.heja.hu/blog/wp-content/uploads/2026/02/flow_chart_drawing_3_final.png 1661w" sizes="(max-width: 799px) 94vw, (max-width: 1023px) 67vw, (max-width: 1365px) 62vw, 1037" /><figcaption>The size of all parts of the game, starting from the source files, through intermediate formats, to the final ZIP archive. (Note: the black sizes are text files, blue ones are binary files.)</figcaption></figure>



<h2>Results</h2>



<p>This post turned out to be rather technical and compression focused &#8211; but I won&#8217;t lie, I wanted to do such a post for some time now.</p>



<p>I had a few features and details that either made it into the game but is not shown (eg. more color palettes ), or I wasn&#8217;t able to finish (eg. some extra elements on the opening screen, more levels, final level), but I think for a jam game it turned out to be fun, and for me that&#8217;s really important and I am happy with how it turned out.</p>



<p>Alley Kitty landed on the 20th place, out of 176 games &#8211; with some pretty strong entries this year too!</p>



<p>See you again in 2026! Maybe that time I&#8217;ll have a <em>simple idea</em>, for a change&#8230; :)</p>
]]></content:encoded>
							<wfw:commentRss>https://gabor.heja.hu/blog/2026/02/09/alley-kitty-my-js13k-games-2025-entry/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
							</item>
		<item>
		<title>Using ffmpeg to color correct / color grade a video</title>
		<link>https://gabor.heja.hu/blog/2024/12/10/using-ffmpeg-to-color-correct-color-grade-a-video-lut-hald-clut/</link>
				<comments>https://gabor.heja.hu/blog/2024/12/10/using-ffmpeg-to-color-correct-color-grade-a-video-lut-hald-clut/#comments</comments>
				<pubDate>Tue, 10 Dec 2024 20:21:08 +0000</pubDate>
		<dc:creator><![CDATA[Gabor Heja]]></dc:creator>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[Dev]]></category>
		<category><![CDATA[Video]]></category>
		<category><![CDATA[clut]]></category>
		<category><![CDATA[ffmpeg]]></category>
		<category><![CDATA[hald clut]]></category>
		<category><![CDATA[lut]]></category>

		<guid isPermaLink="false">https://gabor.heja.hu/blog/?p=678</guid>
				<description><![CDATA[When searching for ffmpeg color correction I always ran into examples with saturation, brightness, contrast and gamma command line options &#8230; <a href="https://gabor.heja.hu/blog/2024/12/10/using-ffmpeg-to-color-correct-color-grade-a-video-lut-hald-clut/" class="more-link"><span class="more-button">Continue reading &#62;<span class="screen-reader-text">Using ffmpeg to color correct / color grade a video</span></span></a>]]></description>
								<content:encoded><![CDATA[
<p>When searching for ffmpeg color correction I always ran into examples with saturation, brightness, contrast and gamma command line options (or even curves) and a lot of guess work. But after a bit of digging I found out that you can also easily apply any color correction you can do to a picture in your favorite image editor (like Photoshop or GIMP), thanks to LUTs.</p>



<p>LUTs are Look Up Tables for colors, used mainly for color grading, they basically map colors to different colors and can be represented as a regular PNG. Ffmpeg can apply LUTs to videos, and it can also generate you one to edit to your liking and then apply it.</p>



<p>You might have a clip that you shot but for some reason the colors were a bit off, or you just want to do some fine-tuned effects. With the following command you can generate a snapshot of the video at a specific time (0:00:04 in this case) along with a LUT.</p>



<pre class="wp-block-preformatted">ffmpeg -f lavfi -i haldclutsrc=8 \
  -i input.mp4 -ss 0:00:04 -frames:v 1 \
  -filter_complex "[1]scale=-1:512[b];[0][b]hstack" \
  haldclut_correction.png</pre>



<figure class="wp-block-image size-large"><img src="https://gabor.heja.hu/blog/wp-content/uploads/2024/12/haldclut_correction-1024x369.png" alt="" class="wp-image-682" srcset="https://gabor.heja.hu/blog/wp-content/uploads/2024/12/haldclut_correction-1024x369.png 1024w, https://gabor.heja.hu/blog/wp-content/uploads/2024/12/haldclut_correction-300x108.png 300w, https://gabor.heja.hu/blog/wp-content/uploads/2024/12/haldclut_correction-768x277.png 768w, https://gabor.heja.hu/blog/wp-content/uploads/2024/12/haldclut_correction.png 1422w" sizes="(max-width: 799px) 94vw, (max-width: 1023px) 67vw, (max-width: 1365px) 62vw, 1037" /><figcaption>The generated PNG containing the LUT and the snapshot.</figcaption></figure>



<p>Then you edit it in your photo editor, but make sure to only apply effects that are affecting the pixel color values (color temperature, curves, contrast, sepia, etc.) but does not alter the image (i.e. blurring).</p>



<figure class="wp-block-video"><video autoplay controls loop muted src="https://gabor.heja.hu/blog/wp-content/uploads/2024/12/haldclut_crossfade-1.mp4"></video><figcaption> The generated PNG before and after the color correction (using color temperature and color curves in GIMP). </figcaption></figure>



<p>Then after saving the image, with the following command you can apply it to the whole video:</p>



<pre class="wp-block-preformatted">ffmpeg -i input.mp4 \
  -i haldclut_correction.png \
  -filter_complex haldclut \
  -pix_fmt yuv420p \
  -c:v libx264 -preset slow -crf 18 \
  -c:a copy \
  output.mp4</pre>



<figure class="wp-block-video"><video autoplay controls loop muted src="https://gabor.heja.hu/blog/wp-content/uploads/2024/12/charlie3_stacked_v2-1.mp4"></video><figcaption>The video before and after applying the LUT with ffmpeg. Charlie loved this hike :)</figcaption></figure>



<p>And that&#8217;s it, no guesswork included. And as this is still ffmpeg you can automate it, chain more edits, etc. and have full control over the quality of the output. (This example results in a fairly high quality MP4, the videos in this post are more compressed (have worse quality) to conserve bandwidth.)</p>



<p>If by any chance you come across a video that looks underexposed to you, you can also fix that with a simple adjustment.</p>



<figure class="wp-block-video"><video autoplay controls loop muted src="https://gabor.heja.hu/blog/wp-content/uploads/2024/12/hotd_crossfade.mp4"></video><figcaption>A crossfade between the original and the altered screenshot of House Of The Dragon (copyright HBO),  applying color curve adjustment in GIMP.</figcaption></figure>



<figure class="wp-block-video"><video autoplay controls loop muted src="https://gabor.heja.hu/blog/wp-content/uploads/2024/12/hotd_stacked_v2-1.mp4"></video><figcaption>A few seconds of House Of The Dragon (copyright HBO). On the top is the original one, on the bottom is the same clip with the LUT applied.</figcaption></figure>



<p>And of course, you can go further if you want.</p>



<figure class="wp-block-video"><video autoplay controls loop muted src="https://gabor.heja.hu/blog/wp-content/uploads/2024/12/curve_crossfade.mp4"></video><figcaption>Another crossfade with a bit more extreme settings.</figcaption></figure>



<figure class="wp-block-video"><video autoplay controls loop muted src="https://gabor.heja.hu/blog/wp-content/uploads/2024/12/a1_mini_stacked_v2-1.mp4"></video><figcaption>A close up video of my 3D printer doing its job, before and after applying the effect.</figcaption></figure>



<p>You can really apply anything you&#8217;d like, and I just found out that <a href="https://ayosec.github.io/ffmpeg-filters-docs/2.3/Filters/Video/haldclut.html#Workflowexamples">you can also have a video as input for the LUT</a> &#8211; I won&#8217;t start playing with that for now. But please share it with me if you do :)</p>



<h2>Notes</h2>



<p>Of course you can also apply any effects to the PNG that &#8220;corrupts&#8221; the LUT &#8211; it might result in a glitchy, fuzzy, artistic video &#8211; and that might even be what you&#8217;re after! You really can&#8217;t do any harm by trying, ffmpeg will happily render you something.</p>



<p>The post is based on <a href="https://stackoverflow.com/a/64958426/460571">this answer</a> for the question <a href="https://stackoverflow.com/questions/32576513/how-to-apply-a-lut-filter-with-ffmpeg">How to apply a LUT filter with FFmpeg</a> on StackOverflow.</p>



<p>The <code>-pix_fmt yuv420p</code> might not be necessary when encoding the output but ffmpeg with libx264 might default to yuv444p which might cause problems when playing the video in browsers and older media players. Also, when exporting to web with libx264 encoding, adding <code>-movflags faststart</code> should reduce the time needed to start the playback (more <a href="https://superuser.com/questions/856025/any-downsides-to-always-using-the-movflags-faststart-parameter">here</a>), but this is unrelated to the post.</p>



<p>The screenshot on the right of the generated image is simply ignored by ffmpeg (<em>&#8220;Padding on the right (910px) of the Hald CLUT will be ignored&#8221;</em>), it is just there so you can see the results of your edits.</p>



<p>While ffmpeg can work with several types of LUTs, the Hald CLUT that is used in this post is probably the most widely used one. There are tools like <a href="https://github.com/pwnage101/cluttool">cluttool</a> to convert from Hald CLUT to 3D LUT (3DL) and <a href="https://github.com/mikeboers/LUT-Convert">LUT-Convert</a> to convert from Hald CLUT to CUBE.  Ffmpeg also supports <a href="https://ayosec.github.io/ffmpeg-filters-docs/2.3/Filters/Video/lut3d.html">3D LUT</a> and probably CUBE, too.</p>



<p>The second example is a few seconds of House Of The Dragon (season 1, episode 7) that was <a href="https://www.theverge.com/2022/10/3/23386058/house-of-the-dragon-episode-7-too-dark-compression-my-eyes">criticized for being too dark</a>, I used it only for illustration and educational purposes, please don&#8217;t sue me, HBO. I&#8217;ll remove it if you ask.</p>
]]></content:encoded>
							<wfw:commentRss>https://gabor.heja.hu/blog/2024/12/10/using-ffmpeg-to-color-correct-color-grade-a-video-lut-hald-clut/feed/</wfw:commentRss>
		<slash:comments>12</slash:comments>
				<enclosure url="https://gabor.heja.hu/blog/wp-content/uploads/2024/12/haldclut_crossfade-1.mp4" length="756070" type="video/mp4" />
<enclosure url="https://gabor.heja.hu/blog/wp-content/uploads/2024/12/hotd_crossfade.mp4" length="492032" type="video/mp4" />
<enclosure url="https://gabor.heja.hu/blog/wp-content/uploads/2024/12/curve_crossfade.mp4" length="1040552" type="video/mp4" />
<enclosure url="https://gabor.heja.hu/blog/wp-content/uploads/2024/12/charlie3_stacked_v2-1.mp4" length="3003408" type="video/mp4" />
<enclosure url="https://gabor.heja.hu/blog/wp-content/uploads/2024/12/hotd_stacked_v2-1.mp4" length="1708765" type="video/mp4" />
<enclosure url="https://gabor.heja.hu/blog/wp-content/uploads/2024/12/a1_mini_stacked_v2-1.mp4" length="1729464" type="video/mp4" />
			</item>
		<item>
		<title>WU3 Segments &#8211; a free font based on Vienna subway displays</title>
		<link>https://gabor.heja.hu/blog/2024/10/15/wu3-segments-free-vienna-subway-font-wien-u-bahn-font/</link>
				<comments>https://gabor.heja.hu/blog/2024/10/15/wu3-segments-free-vienna-subway-font-wien-u-bahn-font/#respond</comments>
				<pubDate>Tue, 15 Oct 2024 11:42:00 +0000</pubDate>
		<dc:creator><![CDATA[Gabor Heja]]></dc:creator>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[Dev]]></category>
		<category><![CDATA[birdfont]]></category>
		<category><![CDATA[dev]]></category>
		<category><![CDATA[font]]></category>
		<category><![CDATA[ttf]]></category>
		<category><![CDATA[vienna]]></category>
		<category><![CDATA[wien]]></category>

		<guid isPermaLink="false">https://gabor.heja.hu/blog/?p=648</guid>
				<description><![CDATA[When I first was on the U3 subway in Vienna last year I noticed how many segments the characters have &#8230; <a href="https://gabor.heja.hu/blog/2024/10/15/wu3-segments-free-vienna-subway-font-wien-u-bahn-font/" class="more-link"><span class="more-button">Continue reading &#62;<span class="screen-reader-text">WU3 Segments &#8211; a free font based on Vienna subway displays</span></span></a>]]></description>
								<content:encoded><![CDATA[
<figure class="wp-block-image size-large"><img src="https://gabor.heja.hu/blog/wp-content/uploads/2024/10/blog_photo_1-1024x451.jpg" alt="" class="wp-image-656" srcset="https://gabor.heja.hu/blog/wp-content/uploads/2024/10/blog_photo_1-1024x451.jpg 1024w, https://gabor.heja.hu/blog/wp-content/uploads/2024/10/blog_photo_1-300x132.jpg 300w, https://gabor.heja.hu/blog/wp-content/uploads/2024/10/blog_photo_1-768x338.jpg 768w, https://gabor.heja.hu/blog/wp-content/uploads/2024/10/blog_photo_1.jpg 1515w" sizes="(max-width: 799px) 94vw, (max-width: 1023px) 67vw, (max-width: 1365px) 62vw, 1037" /><figcaption>A display on the U3 subway line in Vienna</figcaption></figure>



<p>When I first was on the U3 subway in Vienna last year I noticed how many segments the characters have on these displays. It looked absurdly many, but I really liked the shape of the characters, so the next time when I was around I took a few pictures and started to draw it in Inkscape. I counted 84 segments in the end.</p>



<figure class="wp-block-image size-large"><img src="https://gabor.heja.hu/blog/wp-content/uploads/2024/10/blog_photo_2-1024x325.jpg" alt="" class="wp-image-657" srcset="https://gabor.heja.hu/blog/wp-content/uploads/2024/10/blog_photo_2-1024x325.jpg 1024w, https://gabor.heja.hu/blog/wp-content/uploads/2024/10/blog_photo_2-300x95.jpg 300w, https://gabor.heja.hu/blog/wp-content/uploads/2024/10/blog_photo_2-768x244.jpg 768w, https://gabor.heja.hu/blog/wp-content/uploads/2024/10/blog_photo_2-1536x487.jpg 1536w, https://gabor.heja.hu/blog/wp-content/uploads/2024/10/blog_photo_2-2048x650.jpg 2048w" sizes="(max-width: 799px) 94vw, (max-width: 1023px) 67vw, (max-width: 1365px) 62vw, 1037" /><figcaption>Characters of the display recreated in Inkscape. Note: there is one extra segment, the bottom left internal rounding (seen in G), that was my mistake</figcaption></figure>



<p>Needless to say, at this point I was already in love with these shapes. I was also <em>joking </em>about making a font out of it, but&#8230; well&#8230;</p>



<h2>Creating the font</h2>



<p>After looking around for a bit I found <a href="https://birdfont.org/">Birdfont</a>,  a free open source font editor. Checking out the <a href="https://www.youtube.com/watch?v=Z7qE5Y-tQyY">Intro to Birdfont</a> tutorial by Spencer Saunders was enough to get me started.</p>



<p>Birdfont has a few sample fonts but you can really open any TTF file with it. As I had no idea what would be the correct shape and dimensions in the SVGs, I have opened a TTF font and exported a wide and full height letter with accents (&#8220;Ë&#8221; in my case) and based my characters on its shape (although it turned out to be a bit too low in the previews).</p>



<figure class="wp-block-image size-large"><img src="https://gabor.heja.hu/blog/wp-content/uploads/2024/10/align2_trim-1024x484.jpg" alt="" class="wp-image-651" srcset="https://gabor.heja.hu/blog/wp-content/uploads/2024/10/align2_trim-1024x484.jpg 1024w, https://gabor.heja.hu/blog/wp-content/uploads/2024/10/align2_trim-300x142.jpg 300w, https://gabor.heja.hu/blog/wp-content/uploads/2024/10/align2_trim-768x363.jpg 768w, https://gabor.heja.hu/blog/wp-content/uploads/2024/10/align2_trim.jpg 1243w" sizes="(max-width: 799px) 94vw, (max-width: 1023px) 67vw, (max-width: 1365px) 62vw, 1037" /><figcaption>The letter &#8220;Ë&#8221;, overlaid with the segments used in the font, the normal uppercase letter height is aligned so the font size should be more or less precise, the width (with spacing) is based on the real displays</figcaption></figure>



<p>Birdfont has a really great approach when importing the characters from SVGs. Basically you can import a directory containing one SVG per character with an easy to follow naming scheme:</p>



<ul><li>the base of the file name is the character to import (<code>C.svg</code> will imported as &#8220;C&#8221;)</li><li>everything after <code>#</code> is treated as comment (<code>C#v0.2.1.svg</code> will be &#8220;C&#8221;)</li><li>special characters (or anything, really) can be imported based on its Unicode codepoint (<code>U+&lt;codepoint&gt;.svg</code>, so <code>U+2e.svg</code> will be &#8220;.&#8221;, <code>U+5b.svg</code> will be &#8220;[&#8220;)</li></ul>



<p>It can also set the character width based on the SVG document size. These were my SVG Folder Import settings:</p>



<figure class="wp-block-image size-large"><img src="https://gabor.heja.hu/blog/wp-content/uploads/2024/10/image.png" alt="" class="wp-image-650" srcset="https://gabor.heja.hu/blog/wp-content/uploads/2024/10/image.png 850w, https://gabor.heja.hu/blog/wp-content/uploads/2024/10/image-300x153.png 300w, https://gabor.heja.hu/blog/wp-content/uploads/2024/10/image-768x392.png 768w" sizes="(max-width: 799px) 94vw, (max-width: 1023px) 67vw, (max-width: 1365px) 62vw, 1037" /><figcaption> SVG Folder Import settings  in Birdfont</figcaption></figure>



<p>Although I had to manually unselect a few files, but that was just a few clicks.</p>



<p>It can also export EOT and SVG formats, the OTF is unfortunately a &#8220;plus version&#8221; feature. It can also tune spacing, kerning, ligatures, but I didn&#8217;t need these.</p>



<p>After making a few characters I exported a TTF to play around with. It looked great! So I added the rest of the characters I had a photo of and also found some photos online (see the <a href="https://github.com/gheja/wu3-segments?tab=readme-ov-file#sources">Sources in the readme</a>), and then guessed the rest of the characters based on these. The full character set as of v0.12 looks like this:</p>



<figure class="wp-block-image size-large"><img src="https://gabor.heja.hu/blog/wp-content/uploads/2024/10/characters_in_v0.12-1024x550.png" alt="" class="wp-image-658" srcset="https://gabor.heja.hu/blog/wp-content/uploads/2024/10/characters_in_v0.12-1024x550.png 1024w, https://gabor.heja.hu/blog/wp-content/uploads/2024/10/characters_in_v0.12-300x161.png 300w, https://gabor.heja.hu/blog/wp-content/uploads/2024/10/characters_in_v0.12-768x413.png 768w, https://gabor.heja.hu/blog/wp-content/uploads/2024/10/characters_in_v0.12-1536x826.png 1536w, https://gabor.heja.hu/blog/wp-content/uploads/2024/10/characters_in_v0.12.png 1920w" sizes="(max-width: 799px) 94vw, (max-width: 1023px) 67vw, (max-width: 1365px) 62vw, 1037" /><figcaption>All the characters in the WU3 Segments v0.12 font. The ones with gray background are duplicates (lowercase letters are substituted with uppercase ones), the ones marked with a blue dot are guesses</figcaption></figure>



<p>I did a side-by-side comparison with a photo and also a bit of a test run.</p>



<figure class="wp-block-image size-large"><img src="https://gabor.heja.hu/blog/wp-content/uploads/2024/10/demo_1-1024x1024.jpg" alt="" class="wp-image-659" srcset="https://gabor.heja.hu/blog/wp-content/uploads/2024/10/demo_1-1024x1024.jpg 1024w, https://gabor.heja.hu/blog/wp-content/uploads/2024/10/demo_1-300x300.jpg 300w, https://gabor.heja.hu/blog/wp-content/uploads/2024/10/demo_1-150x150.jpg 150w, https://gabor.heja.hu/blog/wp-content/uploads/2024/10/demo_1-768x768.jpg 768w, https://gabor.heja.hu/blog/wp-content/uploads/2024/10/demo_1-1536x1536.jpg 1536w, https://gabor.heja.hu/blog/wp-content/uploads/2024/10/demo_1-666x666.jpg 666w, https://gabor.heja.hu/blog/wp-content/uploads/2024/10/demo_1.jpg 1600w" sizes="(max-width: 799px) 94vw, (max-width: 1023px) 67vw, (max-width: 1365px) 62vw, 1037" /><figcaption>Some dummy text with the WU3 Segments font overlaid on a photo of a real display</figcaption></figure>



<p>And well, I could not resist to try it in a terminal&#8230;</p>



<figure class="wp-block-image size-large"><img src="https://gabor.heja.hu/blog/wp-content/uploads/2024/10/screenshot_ubuntu_terminal-1024x726.jpg" alt="" class="wp-image-660" srcset="https://gabor.heja.hu/blog/wp-content/uploads/2024/10/screenshot_ubuntu_terminal-1024x726.jpg 1024w, https://gabor.heja.hu/blog/wp-content/uploads/2024/10/screenshot_ubuntu_terminal-300x213.jpg 300w, https://gabor.heja.hu/blog/wp-content/uploads/2024/10/screenshot_ubuntu_terminal-768x545.jpg 768w, https://gabor.heja.hu/blog/wp-content/uploads/2024/10/screenshot_ubuntu_terminal-1536x1089.jpg 1536w, https://gabor.heja.hu/blog/wp-content/uploads/2024/10/screenshot_ubuntu_terminal.jpg 1897w" sizes="(max-width: 799px) 94vw, (max-width: 1023px) 67vw, (max-width: 1365px) 62vw, 1037" /><figcaption>A screenshot of a PuTTY terminal using the WU3 Segments font, connected to an Ubuntu machine</figcaption></figure>



<h2>Downloading the font</h2>



<p>I put everything in the <a href="https://github.com/gheja/wu3-segments">wu3-segments GitHub repo</a>, you can download the font files under the releases tab:<br><a href="https://github.com/gheja/wu3-segments/releases/">https://github.com/gheja/wu3-segments/releases/</a></p>



<p>Feel free to use it and I&#8217;d appreciate any feedback you might have!</p>



<h2>Further?</h2>



<p>At this point I thought the font was quite complete and I didn&#8217;t expect any major changes &#8211; except&#8230; <a href="https://mastodon.social/@derloris/113250611901203615">derloris on Mastodon </a>showed me two photos they took with <strong>lowercase letters</strong> displayed!</p>



<figure class="wp-block-image size-large"><img src="https://gabor.heja.hu/blog/wp-content/uploads/2024/10/Screenshot_20241016_131916_Tusky-767x1024.png" alt="" class="wp-image-661" srcset="https://gabor.heja.hu/blog/wp-content/uploads/2024/10/Screenshot_20241016_131916_Tusky-767x1024.png 767w, https://gabor.heja.hu/blog/wp-content/uploads/2024/10/Screenshot_20241016_131916_Tusky-225x300.png 225w, https://gabor.heja.hu/blog/wp-content/uploads/2024/10/Screenshot_20241016_131916_Tusky-768x1025.png 768w, https://gabor.heja.hu/blog/wp-content/uploads/2024/10/Screenshot_20241016_131916_Tusky.png 1080w" sizes="(max-width: 767px) 100vw, 767px" /><figcaption>A screenshot of a Toot from mastodon, with two photos showing the lowercase letters on these displays</figcaption></figure>



<p>I did not expect this. Although they look a bit janky (the spacing is all over the place) now I feel the font is not quite as complete as I thought. And maybe now I should add these characters, too&#8230;</p>



<h2>Some notes</h2>



<p>Collecting all the characters needed for a font seems to be an impossible task, as only a few station names and the numbers are displayed, apart from occasional messages like &#8220;Use public transport, save the climate!&#8221; (&#8220;Öffis nützen, Klima schützen!&#8221;, which actually led to <a href="https://futurezone.at/digital-life/wiener-linien-crashen-echtzeitanzeige-mit-klima-schuetzen/400507669">misbehave/crash the mobile apps and other connected services</a>). So long story short, I had to guess several characters (surprisingly Q, X, Y are amongst those).</p>



<p>If you have any pictures of any letters that are missing or marked as guesses, I&#8217;d happy to update the font, see the <a href="https://github.com/gheja/wu3-segments?tab=readme-ov-file#about">About section of the readme</a> for details.</p>



<p>Posting about it on Mastodon (okay, tooting is the correct verb) was a big part of kicking this project forward. Each of my posts about it got more and more positive reactions and encouragement: <a href="https://mastodon.social/@gheja/113012797551894562">my first impressions</a>, <a href="https://mastodon.social/@gheja/113138822732431374">kind of joking about making a font</a>, <a href="https://mastodon.social/@gheja/113215721872127696">releasing the preview TTF font file</a>, they all got great reactions.</p>



<p><a href="https://mastodon.social/@mwichary@mastodon.online">Marcin Wichary</a> made a really nice <a href="https://aresluna.org/segmented-type/">Segmented type appreciation corner</a> webpage to play around with segmented fonts, I think you might like it.</p>
]]></content:encoded>
							<wfw:commentRss>https://gabor.heja.hu/blog/2024/10/15/wu3-segments-free-vienna-subway-font-wien-u-bahn-font/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
							</item>
		<item>
		<title>Easy face recognition in Python</title>
		<link>https://gabor.heja.hu/blog/2021/01/04/easy-face-recognition-in-2021-using-python/</link>
				<comments>https://gabor.heja.hu/blog/2021/01/04/easy-face-recognition-in-2021-using-python/#comments</comments>
				<pubDate>Mon, 04 Jan 2021 17:59:22 +0000</pubDate>
		<dc:creator><![CDATA[Gabor Heja]]></dc:creator>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[Dev]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[cnn]]></category>
		<category><![CDATA[convolutional neural network]]></category>
		<category><![CDATA[deep learning]]></category>
		<category><![CDATA[dev]]></category>
		<category><![CDATA[face detection]]></category>
		<category><![CDATA[face recognition]]></category>
		<category><![CDATA[face_recognition]]></category>
		<category><![CDATA[facial recognition]]></category>
		<category><![CDATA[machine learning]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[scikit-learn]]></category>

		<guid isPermaLink="false">https://gabor.heja.hu/blog/?p=519</guid>
				<description><![CDATA[Although I don&#8217;t like and I am against facial recognition in it&#8217;s popular context &#8211; public space surveillance, use on &#8230; <a href="https://gabor.heja.hu/blog/2021/01/04/easy-face-recognition-in-2021-using-python/" class="more-link"><span class="more-button">Continue reading &#62;<span class="screen-reader-text">Easy face recognition in Python</span></span></a>]]></description>
								<content:encoded><![CDATA[
<p>Although I don&#8217;t like and I am against facial recognition in it&#8217;s popular context &#8211; public space surveillance, use on social platforms, etc. &#8211; it is undoubtedly an interesting topic. So I decided to experiment with it.</p>



<p>One strict requirement was to have it running locally on my machine, offline, without contacting any external services.</p>



<p>While searching I have found quite a few outdated libraries, examples and outdated or unnecessarily complex tutorials.</p>



<p>In this post I am using an up-to-date Ubuntu, up-to-date Python and the latest <a href="https://github.com/ageitgey/face_recognition">face_recognition</a> library (that uses the <a href="https://github.com/davisking/dlib">dlib</a> machine learning library).</p>



<p>I also try to focus on getting it working as simple as possible and then add the extras and optimizations.</p>



<p>All versions are available in the GitHub repository: <a href="https://github.com/gheja/face-recognition-test/">https://github.com/gheja/face-recognition-test/</a></p>



<h2>The goal</h2>



<p>My goal is to have known people recognized on photos, given a (smaller) set of photos of them.</p>



<p>The second goal is to find unrecognized faces and group them by person. Then we can mark them as known people and find them in other photos.</p>



<h2>Terminology used</h2>



<p>Face detection &#8211; finding one or more faces in an image.</p>



<p>Face recognition &#8211; finding the person for a detected face from a known set of faces.</p>



<p>Clustering &#8211; arranging detected faces into groups based on facial features  (without knowledge of the person).</p>



<p>Facial encoding &#8211; an array holding numeric data based on facial features extracted from a face.</p>



<h2>Setting up the environment</h2>



<p>My test environment is a live <a href="https://releases.ubuntu.com/20.04/">Ubuntu 20.04 LTS</a> system.</p>



<p>First we need to enable the <a href="https://help.ubuntu.com/community/Repositories/Ubuntu"><em>universe </em>apt repository</a> that contains the community-maintained free and open-source software to be able to install <em>pip </em>(the package installer for Python). This will also update the apt cache so no need for <code>apt update</code>.</p>



<p>Then we will install <em>python3-pip</em> for managing the Python packages and <em>cmake</em> to be able to compile some modules. </p>



<p>After this we can install the <a href="https://github.com/ageitgey/face_recognition">face_recognition</a> and the <a href="https://github.com/scikit-learn/scikit-learn">scikit-learn</a> packages and we can start working. The latter is needed only for clustering.</p>



<pre class="wp-block-code language-bash"><code>sudo add-apt-repository universe
sudo apt install python3-pip cmake

pip3 install face_recognition scikit-learn</code></pre>



<h2>Finding and recognizing known faces</h2>



<p>To recognize faces first we need to have a list of known faces. The script searches the known faces in the <code>known_faces</code> directory, grouped by person in a sub-directory, holding the files.  For example:</p>



<pre class="wp-block-preformatted">known_faces
  Alice Test
    alice_1.jpg
    ms_test.jpg
  Bob Test
    bobby.jpg</pre>



<p>First we import the required modules, save the current path  to a variable (the script will use this as the base directory when searching for files), then prepare the global variables to hold the known faces and names:</p>



<pre class="wp-block-code language-python"><code>import os
import numpy as np
import face_recognition

base_dir = os.getcwd()
known_face_encodings = []
known_face_names = []</code></pre>



<p>Let&#8217;s create a <code>load_known_faces()</code> function that gets a list of all directories and files and opens the files one by one to find the faces in each, then picks the first and stores its encoding in <code>known_face_encodings</code> and their corresponding names in <code>known_face_names</code>.</p>



<pre class="wp-block-code language-python"><code>def load_known_faces():
	print("Loading known faces...")
	
	os.chdir(os.path.join(base_dir, "known_faces"))
	
	for root, dirs, files in os.walk('.'):
		for f in files:
			filename = os.path.join(root, f)
			
			print("  {} > {}".format(root, f))
			
			image = face_recognition.load_image_file(filename)
			
			# Search for all the faces
			face_locations = face_recognition.face_locations(image)
			
			# Pick the first one and get its encoding
			face_encodings = face_recognition.face_encodings(image, [ face_locations[0] ])
			
			# Store the processed face
			known_face_encodings.append(face_encodings[0])
			known_face_names.append(os.path.basename(root))</code></pre>



<p>With this we have all the known faces loaded, next task: recognizing them on unknown pictures.</p>



<p>Let&#8217;s create a <code>recognize_faces()</code> function that searches for the images to be processed in the <code>test_images</code> directory, then loads them one by one, searches all the faces in each, extracts the facial features and then calculates the likeliness to every known face. It then picks the nearest match (if good enough) and displays the corresponding name and confidence value.</p>



<pre class="wp-block-code language-python"><code>def recognize_faces():
	print("Detecting and recognizing faces...")
	
	os.chdir(os.path.join(base_dir, "test_images"))
	
	for root, dirs, files in os.walk('.'):
		for f in files:
			filename = os.path.join(root, f)
			
			image = face_recognition.load_image_file(filename)
			
			print("  {}: processing...".format(filename))
			
			# Search for all the faces
			face_locations = face_recognition.face_locations(image)
			
			# Then calculate their encodings
			face_encodings = face_recognition.face_encodings(image, face_locations)
			
			# Loop through each face found in the unknown image
			for (top, right, bottom, left), face_encoding in zip(face_locations, face_encodings):
				name = "Unrecognized"
				confidence = 0
				
				# Find the distance of the face to every known faces. Distance is 0.0 at an exact match
				# and gets higher with the distance, can be more than 1.0
				face_distances = face_recognition.face_distance(known_face_encodings, face_encoding)
				
				# Use the known face with the smallest distance to the new face
				best_match_index = np.argmin(face_distances)
				
				# If there is a possible match
				if face_distances[best_match_index] &lt; 0.6:
					name = known_face_names[best_match_index]
					
					# Confidence is the opposite of distance
					confidence = 1 - face_distances[best_match_index]
				
				print("    found {} with confidence {:.2f}".format(name, confidence))</code></pre>



<p>Then just call these functions.</p>



<pre class="wp-block-code language-python"><code>load_known_faces()
recognize_faces()</code></pre>



<p>And that&#8217;s it for the basics and if all goes well. I have uploaded the script as <a href="https://github.com/gheja/face-recognition-test/blob/main/src/recognize_v1.py">recognize_v1.py</a>.</p>



<p>Now it&#8217;s a good time to add some checks if images could be loaded correctly, faces could be detected, etc., see <a href="https://github.com/gheja/face-recognition-test/blob/main/src/recognize_v2.py">recognize_v2.py</a>.</p>



<h2>Highlighting recognized faces on pictures</h2>



<p>I think the next logical step is to not just print the detected faces but show them on the picture.</p>



<p>Fortunately the <code>face_locations</code> array holds the top-left and bottom-right coordinates of the faces, we also got the best matches for them and the <a href="https://github.com/python-pillow/Pillow">Pillow</a> library (python module named PIL) can easily draw on images.</p>



<p>First we need to import the <code>PIL</code> and <code>PIL.ImageDraw</code> modules:</p>



<pre class="wp-block-code language-python"><code>import PIL
import PIL.ImageDraw</code></pre>



<p>Then after loading the image in <code>recognize_faces()</code> we create a <code>PIL.ImageDraw.Draw</code> to work with:</p>



<pre class="wp-block-code language-python"><code># Convert the image to a PIL-format image so that we can draw on top of it with the Pillow library
# See http://pillow.readthedocs.io/ for more about PIL/Pillow
image2 = PIL.Image.fromarray(image)

# Create a Draw instance to draw to
draw = PIL.ImageDraw.Draw(image2)</code></pre>



<p>Then in the loop over the faces we draw a rectangle around the current face and write the confidence value and the name of the best candidate, optionally with colors depending on the quality of the match:</p>



<pre class="wp-block-code language-python"><code>if confidence >= 0.5:
	color = (0, 190, 0)
	color2 = (255, 255, 255)
elif confidence >= 0.35:
	color = (0, 120, 190)
	color2 = (255, 255, 255)
elif confidence >= 0.25:
	color = (0, 0, 120)
	color2 = (255, 255, 255)
else:
	color = (30, 30, 30)
	color2 = (90, 90, 90)

# Draw a box around the face
draw.rectangle(((left, top), (right, bottom)), outline = color)

# Draw a label with the name below the face
text_width, text_height = draw.textsize(name)
draw.rectangle(((left, bottom), (right, bottom + text_height)), fill = color, outline = color)
draw.text((left + 6, bottom), "{:.2f} {}".format(confidence, name), fill = color2)</code></pre>



<p>And right after finishing the processing of the image we save the copy with the highlights in the <code>annotated_images</code> directory:</p>



<pre class="wp-block-code language-python"><code># Remove the drawing library from memory as per the Pillow docs
del draw

# Save the annotated image
outputName = os.path.join(base_dir, "annotated_images", f)
image2.save(outputName)</code></pre>



<p>Note: the <code>annotated_images</code> directory is not emptied before run, the files there will be retained or overwritten.</p>



<p>The full source code is available as <a href="https://github.com/gheja/face-recognition-test/blob/main/src/recognize_v3.py">recognize_v3.py</a>.</p>



<p>See an test run of this version in the last section of this post below.</p>



<h2>Clustering faces automatically</h2>



<p>It would be nice to collect all the unrecognized faces and group them by person, wouldn&#8217;t it? This is what clustering is for.</p>



<p>Sidenote: So far we searched for people we &#8220;labeled&#8221; manually earlier for the script, this is <a href="https://en.wikipedia.org/wiki/Supervised_learning">supervised learning</a>. If we want to group similar faces, we either need to supply how many groups (people) we want or use <a href="https://en.wikipedia.org/wiki/Unsupervised_learning"><em>un</em>supervised learning</a> to do it automatically. In this post we want the latter by using <a href="https://en.wikipedia.org/wiki/DBSCAN">DBSCAN</a> (density-based spatial clustering of applications with noise).</p>



<p>First we need to import the <a href="https://scikit-learn.org/stable/modules/generated/sklearn.cluster.DBSCAN.html">DBSCAN clustering</a> from <a href="https://github.com/scikit-learn/scikit-learn">scikit-learn</a> library (python module named sklearn):</p>



<pre class="wp-block-code language-python"><code>from sklearn.cluster import DBSCAN</code></pre>



<p>Then add an array to hold the unrecognized faces:</p>



<pre class="wp-block-code language-python"><code>unrecognized_faces = []</code></pre>



<p>Then in <code>recognize_faces()</code> add the faces to the array:</p>



<pre class="wp-block-code language-python"><code>if name == "Unrecognized":
	unrecognized_faces.append({
		"face_location": (top, right, bottom, left),
		"face_encoding": face_encoding,
		"filename": filename
	})</code></pre>



<p>And now everything is ready for clustering.</p>



<p>Clustering is really simple thanks to the scikit library, it just needs the faces in an array and two parameters: <em>min_samples</em> &#8211; telling how many faces needed at least to form a group, and <em>eps</em> &#8211; telling how far the faces needed to be at most to be considered in that group.</p>



<p>After this we copy the faces into separate PNG files (to retain as much detail as possible), grouped by person in directories. The directories will be named <code>person_0</code>, <code>person_1</code>, etc. with an exception for the rest: <code>unclustered</code>.</p>



<p>For example, after processing three test pictures with 6 faces in total we should get similar results:</p>



<pre class="wp-block-preformatted">test_images
  group_photo_1.jpg
  group_photo_2.jpg
  group_photo_3.jpg

clustered_faces
  person_0
    face_1.png
    face_2.png
    face_5.png
  person_1
    face_3.png
    face_6.png
    face_7.png
  unclustered
    face_0.png
    face_4.png</pre>



<pre class="wp-block-code language-python"><code>def cluster_unrecognized_faces():
	print("Clustering unrecognized faces...")
	
	if len(unrecognized_faces) == 0:
		return
	
	encodings = []
	
	# Create an array suitable for DBSCAN conaining only the encodings
	for face in unrecognized_faces:
		encodings.append(face["face_encoding"])
	
	# Group at least 3 faces ("min_samples") with a maximal distance
	# of 0.5 ("eps"). From a few runs these numbers seems to be working
	# well for me. Your case may vary so try to tune these if you don't
	# like your results.
	#
	# For more info see:
	#   https://scikit-learn.org/stable/modules/generated/sklearn.cluster.DBSCAN.html
	clt = DBSCAN(metric="euclidean", n_jobs=-1, min_samples=3, eps=0.5)
	clt.fit(encodings)
	
	print("Saving clustered faces...")
	
	face_index = 0
	for face in unrecognized_faces:
		person_index = clt.labels_[face_index]
		
		if person_index == -1:
			name = "unclustered"
		else:
			name = "person_%d" % (person_index)
		
		filename = os.path.join(base_dir, "test_images", face["filename"])
		clustered_filename = os.path.join(base_dir, "clustered_faces", name, "face_%d.png" % (face_index))
		
		# Create the directory for this person if needed
		if not os.path.exists(os.path.dirname(clustered_filename)):
			os.makedirs(os.path.dirname(clustered_filename))
		
		try:
			image = face_recognition.load_image_file(filename)
		except:
			print("{}: failed to load, skipping.".format(filename))
			continue
		
		(top, right, bottom, left) = face["face_location"]
		
		# Convert the image to a PIL-format image so that we can draw on top of it with the Pillow library
		# See http://pillow.readthedocs.io/ for more about PIL/Pillow
		image2 = PIL.Image.fromarray(image)
		
		# Crop the image to the face
		image2 = image2.crop((left, top, right, bottom))
		
		image2.save(clustered_filename)
		
		face_index += 1</code></pre>



<p>As a last step we need to call this function after <code>recognize_faces()</code> collected the unrecognized faces:</p>



<pre class="wp-block-code language-python"><code>cluster_unrecognized_faces()</code></pre>



<p>And we&#8217;re good to go.</p>



<p>After running the script we can examine the grouped faces, correct them manually if needed and move to a sub-directory of <code>known_faces</code> to be able to recognize these people next time.</p>



<p>Note: the <code>clustered_faces</code> directory is not emptied before run, the files there will be retained or overwritten, the new and old faces might mix for subsequent runs. It is a good idea to clear it by hand before running the script.</p>



<p>The full source code is available as <a href="https://github.com/gheja/face-recognition-test/blob/main/src/recognize_v4.py">recognize_v4.py</a>.</p>



<h2>Optimizations</h2>



<p>As the locating and encoding of faces take the most significant time it is a good idea to store their results instead of running them on each images every time &#8211; for this I added <a href="https://docs.python.org/3/library/pickle.html">pickle</a> and store the data in <code>known_faces_cache</code>, if there is a stored version available the program uses it, otherwise it processes the image and stores the result for next use. See <a href="https://github.com/gheja/face-recognition-test/blob/main/src/recognize_v5.py">recognize_v5.py</a>.</p>



<p>Processing a huge image can take considerably longer time so adding a resize that limits to 1200&#215;1200 pixels might be a good idea, this is what <code>load_image()</code> does. See <a href="https://github.com/gheja/face-recognition-test/blob/main/src/recognize_v6.py">recognize_v6.py</a>.</p>



<p>Also, <em>dlib</em> (the library that <em>face_recognition</em> uses) supports two methods for face detection: HOG+SVN (histogram of oriented gradients and linear support vector classification) and CNN (convolutional neural network). The first one is called &#8220;hog&#8221;, it&#8217;s reasonably fast on CPU and is the default; the second one is &#8220;cnn&#8221;, it is more accurate for faces in non-ideal angles, uses more memory and it is strongly recommended to have a GPU with CUDA support. To use &#8220;cnn&#8221; modify the <code>face_recognition.face_locations(image)</code> to <code>face_recognition.face_locations(image, model="cnn")</code>. Also, &#8220;cnn&#8221; supports batch processing, too. Read more at <a href="https://towardsdatascience.com/cnn-based-face-detector-from-dlib-c3696195e01c">CNN based face detector from dlib</a> and <a href="https://face-recognition.readthedocs.io/en/latest/face_recognition.html#face_recognition.api.batch_face_locations">the docs</a>.</p>



<h2>My test runs and conclusion</h2>



<p>I started my tests by adding several photos (5 to 8) of a few actors and actresses, all out of character and from the last few years (2018-2020). Sources were like Wikipedia, Oscar galas, etc.</p>



<p>After running the script I moved the clustered images from <code>clustered_faces</code> to <code>known_faces</code> with the proper names &#8211; without manually correcting anything else.</p>



<div class="wp-block-image"><figure class="aligncenter size-large"><img src="https://gabor.heja.hu/blog/wp-content/uploads/2021/01/face_recognition_test_known_faces.png" alt="Pictures of actors and actresses for testing face recognition." class="wp-image-545" srcset="https://gabor.heja.hu/blog/wp-content/uploads/2021/01/face_recognition_test_known_faces.png 675w, https://gabor.heja.hu/blog/wp-content/uploads/2021/01/face_recognition_test_known_faces-300x249.png 300w" sizes="(max-width: 675px) 100vw, 675px" /><figcaption>Pictures of actors and actresses for testing face recognition.</figcaption></figure></div>



<p>Then I collected a few photos, mostly from posters, stills from movies and episodes, etc. and ran the script.</p>



<p>It surprised me how accurately it recognized the faces. So I added a few more pictures, with non-ideal angles, partially covered faces, ones with sunglasses, older ones (shot 10 or even 29 years earlier) and&#8230; see for yourself below.</p>



<p>(Click on a photo to open gallery.)</p>



<figure class="wp-block-gallery columns-4 is-cropped"><ul class="blocks-gallery-grid"><li class="blocks-gallery-item"><figure><a href="https://gabor.heja.hu/blog/wp-content/uploads/2021/01/game-of-thrones-_107034654_headline_pic_20_2.png"><img src="https://gabor.heja.hu/blog/wp-content/uploads/2021/01/game-of-thrones-_107034654_headline_pic_20_2.png" alt="Face recognition test with Gwendoline Christie, Emilia Clarke, Lena Hadley and Sophie Turner, recognized correctly" data-id="557" data-full-url="https://gabor.heja.hu/blog/wp-content/uploads/2021/01/game-of-thrones-_107034654_headline_pic_20_2.png" data-link="https://gabor.heja.hu/blog/game-of-thrones-_107034654_headline_pic_20_2/" class="wp-image-557" srcset="https://gabor.heja.hu/blog/wp-content/uploads/2021/01/game-of-thrones-_107034654_headline_pic_20_2.png 1024w, https://gabor.heja.hu/blog/wp-content/uploads/2021/01/game-of-thrones-_107034654_headline_pic_20_2-300x169.png 300w, https://gabor.heja.hu/blog/wp-content/uploads/2021/01/game-of-thrones-_107034654_headline_pic_20_2-768x432.png 768w" sizes="(max-width: 799px) 94vw, (max-width: 1023px) 67vw, (max-width: 1365px) 62vw, 1037" /></a><figcaption class="blocks-gallery-item__caption">Face recognition test with Gwendoline Christie, Emilia Clarke, Lena Hadley and Sophie Turner, recognized correctly</figcaption></figure></li><li class="blocks-gallery-item"><figure><a href="https://gabor.heja.hu/blog/wp-content/uploads/2021/01/gettyimages-148778086-2048x2048-1.png"><img src="https://gabor.heja.hu/blog/wp-content/uploads/2021/01/gettyimages-148778086-2048x2048-1-683x1024.png" alt="Face recognition test with Nikolaj Coster-Waldau, Lena Hadley, Emilia Clarke, Kit Harington and Peter Dinklage, recognized correctly" data-id="558" data-full-url="https://gabor.heja.hu/blog/wp-content/uploads/2021/01/gettyimages-148778086-2048x2048-1.png" data-link="https://gabor.heja.hu/blog/_gettyimages-148778086-2048x2048/" class="wp-image-558" srcset="https://gabor.heja.hu/blog/wp-content/uploads/2021/01/gettyimages-148778086-2048x2048-1-683x1024.png 683w, https://gabor.heja.hu/blog/wp-content/uploads/2021/01/gettyimages-148778086-2048x2048-1-200x300.png 200w, https://gabor.heja.hu/blog/wp-content/uploads/2021/01/gettyimages-148778086-2048x2048-1-768x1152.png 768w, https://gabor.heja.hu/blog/wp-content/uploads/2021/01/gettyimages-148778086-2048x2048-1.png 800w" sizes="(max-width: 683px) 100vw, 683px" /></a><figcaption class="blocks-gallery-item__caption">Face recognition test with Nikolaj Coster-Waldau, Lena Hadley, Emilia Clarke, Kit Harington and Peter Dinklage, recognized correctly</figcaption></figure></li><li class="blocks-gallery-item"><figure><a href="https://gabor.heja.hu/blog/wp-content/uploads/2021/01/evan-rachel-wood-aaron-paul_2.png"><img src="https://gabor.heja.hu/blog/wp-content/uploads/2021/01/evan-rachel-wood-aaron-paul_2.png" alt="Face recognition test with Evan Rachel Wood as Dolores Abernathy and Aaron Paul as Caleb Nichols in &quot;Westworld&quot;, recognized correctly" data-id="556" data-full-url="https://gabor.heja.hu/blog/wp-content/uploads/2021/01/evan-rachel-wood-aaron-paul_2.png" data-link="https://gabor.heja.hu/blog/evan-rachel-wood-aaron-paul_2/" class="wp-image-556" srcset="https://gabor.heja.hu/blog/wp-content/uploads/2021/01/evan-rachel-wood-aaron-paul_2.png 1000w, https://gabor.heja.hu/blog/wp-content/uploads/2021/01/evan-rachel-wood-aaron-paul_2-300x200.png 300w, https://gabor.heja.hu/blog/wp-content/uploads/2021/01/evan-rachel-wood-aaron-paul_2-768x512.png 768w" sizes="(max-width: 799px) 94vw, (max-width: 1023px) 67vw, (max-width: 1365px) 62vw, 1037" /></a><figcaption class="blocks-gallery-item__caption">Face recognition test with Evan Rachel Wood as Dolores Abernathy and Aaron Paul as Caleb Nichols in &#8220;Westworld&#8221;, recognized correctly</figcaption></figure></li><li class="blocks-gallery-item"><figure><a href="https://gabor.heja.hu/blog/wp-content/uploads/2021/01/Jon-Snow-Sason-1-To-Season-8_2.png"><img src="https://gabor.heja.hu/blog/wp-content/uploads/2021/01/Jon-Snow-Sason-1-To-Season-8_2.png" alt="Face recognition test with Kit Harington as Jon Snow, over Season 1 to 8 of &quot;Game Of Thrones&quot;, recognized correctly" data-id="559" data-full-url="https://gabor.heja.hu/blog/wp-content/uploads/2021/01/Jon-Snow-Sason-1-To-Season-8_2.png" data-link="https://gabor.heja.hu/blog/jon-snow-sason-1-to-season-8_2/" class="wp-image-559" srcset="https://gabor.heja.hu/blog/wp-content/uploads/2021/01/Jon-Snow-Sason-1-To-Season-8_2.png 564w, https://gabor.heja.hu/blog/wp-content/uploads/2021/01/Jon-Snow-Sason-1-To-Season-8_2-174x300.png 174w" sizes="(max-width: 564px) 100vw, 564px" /></a><figcaption class="blocks-gallery-item__caption">Face recognition test with Kit Harington as Jon Snow, over Season 1 to 8 of &#8220;Game Of Thrones&#8221;, recognized correctly</figcaption></figure></li><li class="blocks-gallery-item"><figure><a href="https://gabor.heja.hu/blog/wp-content/uploads/2021/01/4ad288d36853a9a8b0c5ef8e11d23b33_2.png"><img src="https://gabor.heja.hu/blog/wp-content/uploads/2021/01/4ad288d36853a9a8b0c5ef8e11d23b33_2.png" alt="Face recognition test with Maisie Wiliams as Arya Stark, over Season 1 to 8 of &quot;Game Of Thrones&quot;, recognized mostly correctly" data-id="562" data-full-url="https://gabor.heja.hu/blog/wp-content/uploads/2021/01/4ad288d36853a9a8b0c5ef8e11d23b33_2.png" data-link="https://gabor.heja.hu/blog/4ad288d36853a9a8b0c5ef8e11d23b33_2/" class="wp-image-562" srcset="https://gabor.heja.hu/blog/wp-content/uploads/2021/01/4ad288d36853a9a8b0c5ef8e11d23b33_2.png 750w, https://gabor.heja.hu/blog/wp-content/uploads/2021/01/4ad288d36853a9a8b0c5ef8e11d23b33_2-246x300.png 246w" sizes="(max-width: 750px) 100vw, 750px" /></a><figcaption class="blocks-gallery-item__caption">Face recognition test with Maisie Wiliams as Arya Stark, over Season 1 to 8 of &#8220;Game Of Thrones&#8221;, recognized mostly correctly</figcaption></figure></li><li class="blocks-gallery-item"><figure><a href="https://gabor.heja.hu/blog/wp-content/uploads/2021/01/game_of_thrones_cast_2009.png"><img src="https://gabor.heja.hu/blog/wp-content/uploads/2021/01/game_of_thrones_cast_2009-1024x768.png" alt="Face recognition test with Sophie Turner, Maisie Wiliams, Alfie Allen, Richard Madden, Kit Harington from 2009, recognized correctly" data-id="560" data-full-url="https://gabor.heja.hu/blog/wp-content/uploads/2021/01/game_of_thrones_cast_2009.png" data-link="https://gabor.heja.hu/blog/game_of_thrones_cast_2009/" class="wp-image-560" srcset="https://gabor.heja.hu/blog/wp-content/uploads/2021/01/game_of_thrones_cast_2009-1024x768.png 1024w, https://gabor.heja.hu/blog/wp-content/uploads/2021/01/game_of_thrones_cast_2009-300x225.png 300w, https://gabor.heja.hu/blog/wp-content/uploads/2021/01/game_of_thrones_cast_2009-768x576.png 768w, https://gabor.heja.hu/blog/wp-content/uploads/2021/01/game_of_thrones_cast_2009.png 1200w" sizes="(max-width: 799px) 94vw, (max-width: 1023px) 67vw, (max-width: 1365px) 62vw, 1037" /></a><figcaption class="blocks-gallery-item__caption">Face recognition test with Sophie Turner, Maisie Wiliams, Alfie Allen, Richard Madden, Kit Harington from 2009, recognized correctly</figcaption></figure></li><li class="blocks-gallery-item"><figure><a href="https://gabor.heja.hu/blog/wp-content/uploads/2021/01/91a34-ftwZL._SL1500_.png"><img src="https://gabor.heja.hu/blog/wp-content/uploads/2021/01/91a34-ftwZL._SL1500_.png" alt="Face recognition test with Tom Hanks in &quot;The Money Pit&quot; (1986), recognized correctly" data-id="547" data-full-url="https://gabor.heja.hu/blog/wp-content/uploads/2021/01/91a34-ftwZL._SL1500_.png" data-link="https://gabor.heja.hu/blog/91a34-ftwzl-_sl1500_/" class="wp-image-547" srcset="https://gabor.heja.hu/blog/wp-content/uploads/2021/01/91a34-ftwZL._SL1500_.png 622w, https://gabor.heja.hu/blog/wp-content/uploads/2021/01/91a34-ftwZL._SL1500_-233x300.png 233w" sizes="(max-width: 622px) 100vw, 622px" /></a><figcaption class="blocks-gallery-item__caption">Face recognition test with Tom Hanks in &#8220;The Money Pit&#8221; (1986), recognized correctly</figcaption></figure></li><li class="blocks-gallery-item"><figure><a href="https://gabor.heja.hu/blog/wp-content/uploads/2021/01/castaway.png"><img src="https://gabor.heja.hu/blog/wp-content/uploads/2021/01/castaway.png" alt="Face recognition test with Tom Hanks, recognized correctly" data-id="546" data-full-url="https://gabor.heja.hu/blog/wp-content/uploads/2021/01/castaway.png" data-link="https://gabor.heja.hu/blog/castaway/" class="wp-image-546" srcset="https://gabor.heja.hu/blog/wp-content/uploads/2021/01/castaway.png 800w, https://gabor.heja.hu/blog/wp-content/uploads/2021/01/castaway-300x156.png 300w, https://gabor.heja.hu/blog/wp-content/uploads/2021/01/castaway-768x400.png 768w" sizes="(max-width: 800px) 100vw, 800px" /></a><figcaption class="blocks-gallery-item__caption">Tom Hanks in &#8220;Cast Away&#8221; (2000), TM and Copyright 20th Century Fox Film Corp.</figcaption></figure></li><li class="blocks-gallery-item"><figure><a href="https://gabor.heja.hu/blog/wp-content/uploads/2021/01/game-of-thrones-got_23080166-7858919-image-a-11_1578358475126_v2.jpg"><img src="https://gabor.heja.hu/blog/wp-content/uploads/2021/01/game-of-thrones-got_23080166-7858919-image-a-11_1578358475126_v2.jpg" alt="Face recognition test with Rose Leslie and Kit Harrington, both wearing sunglasses, recognized correctly" data-id="554" data-full-url="https://gabor.heja.hu/blog/wp-content/uploads/2021/01/game-of-thrones-got_23080166-7858919-image-a-11_1578358475126_v2.jpg" data-link="https://gabor.heja.hu/blog/game-of-thrones-got_23080166-7858919-image-a-11_1578358475126_v2/" class="wp-image-554" srcset="https://gabor.heja.hu/blog/wp-content/uploads/2021/01/game-of-thrones-got_23080166-7858919-image-a-11_1578358475126_v2.jpg 634w, https://gabor.heja.hu/blog/wp-content/uploads/2021/01/game-of-thrones-got_23080166-7858919-image-a-11_1578358475126_v2-267x300.jpg 267w" sizes="(max-width: 634px) 100vw, 634px" /></a><figcaption class="blocks-gallery-item__caption">Face recognition test with Rose Leslie and Kit Harington, both wearing sunglasses, recognized correctly</figcaption></figure></li><li class="blocks-gallery-item"><figure><a href="https://gabor.heja.hu/blog/wp-content/uploads/2021/01/milczenie-owiec-kadr-z-filmu_2.png"><img src="https://gabor.heja.hu/blog/wp-content/uploads/2021/01/milczenie-owiec-kadr-z-filmu_2.png" alt="Face recognition test with Anthony Hopkins as Hannibal Lecter of The Silence of the Lambs (1991), recognized correctly" data-id="561" data-full-url="https://gabor.heja.hu/blog/wp-content/uploads/2021/01/milczenie-owiec-kadr-z-filmu_2.png" data-link="https://gabor.heja.hu/blog/milczenie-owiec-kadr-z-filmu_2/" class="wp-image-561" srcset="https://gabor.heja.hu/blog/wp-content/uploads/2021/01/milczenie-owiec-kadr-z-filmu_2.png 788w, https://gabor.heja.hu/blog/wp-content/uploads/2021/01/milczenie-owiec-kadr-z-filmu_2-300x169.png 300w, https://gabor.heja.hu/blog/wp-content/uploads/2021/01/milczenie-owiec-kadr-z-filmu_2-768x433.png 768w" sizes="(max-width: 788px) 100vw, 788px" /></a><figcaption class="blocks-gallery-item__caption">Face recognition test with Anthony Hopkins as Hannibal Lecter of The Silence of the Lambs (1991), recognized correctly</figcaption></figure></li></ul></figure>



<p>The success of recognition with sunglasses and on 11 years old photos surely surprised me, especially with the high confidence rate.</p>



<p>But what happens if we have have incomplete data? For example if we only know a subset of the people we try to recognize on photos? Well, I have removed some of the actors from the <code>known_faces</code> directory and ran the script again.</p>



<p>(Click on a photo to open the gallery.)</p>



<figure class="wp-block-gallery columns-4 is-cropped"><ul class="blocks-gallery-grid"><li class="blocks-gallery-item"><figure><a href="https://gabor.heja.hu/blog/wp-content/uploads/2021/01/miss3.png"><img src="https://gabor.heja.hu/blog/wp-content/uploads/2021/01/miss3.png" alt="Face recognition test with Richard Madden and Kit Harington from 2009, Madden recognized incorrectly due to missing data" data-id="588" data-full-url="https://gabor.heja.hu/blog/wp-content/uploads/2021/01/miss3.png" data-link="https://gabor.heja.hu/blog/miss3/" class="wp-image-588" srcset="https://gabor.heja.hu/blog/wp-content/uploads/2021/01/miss3.png 477w, https://gabor.heja.hu/blog/wp-content/uploads/2021/01/miss3-252x300.png 252w" sizes="(max-width: 477px) 100vw, 477px" /></a><figcaption class="blocks-gallery-item__caption">Face recognition test with Richard Madden and Kit Harington from 2009, Madden recognized incorrectly due to missing data</figcaption></figure></li><li class="blocks-gallery-item"><figure><a href="https://gabor.heja.hu/blog/wp-content/uploads/2021/01/miss1.png"><img src="https://gabor.heja.hu/blog/wp-content/uploads/2021/01/miss1.png" alt="Face recognition test with Maisie Wiliams as Arya Stark and David Bradley as Walder Frey of &quot;Game Of Thrones&quot;, recognized incorrectly due to missing data" data-id="586" data-full-url="https://gabor.heja.hu/blog/wp-content/uploads/2021/01/miss1.png" data-link="https://gabor.heja.hu/blog/miss1/" class="wp-image-586" srcset="https://gabor.heja.hu/blog/wp-content/uploads/2021/01/miss1.png 484w, https://gabor.heja.hu/blog/wp-content/uploads/2021/01/miss1-300x182.png 300w" sizes="(max-width: 484px) 100vw, 484px" /></a><figcaption class="blocks-gallery-item__caption">Face recognition test with Maisie Wiliams as Arya Stark and David Bradley as Walder Frey of &#8220;Game Of Thrones&#8221;, recognized incorrectly due to missing data</figcaption></figure></li><li class="blocks-gallery-item"><figure><a href="https://gabor.heja.hu/blog/wp-content/uploads/2021/01/6e4d8aa3798e00b2a8c82d1e63de0984.png"><img src="https://gabor.heja.hu/blog/wp-content/uploads/2021/01/6e4d8aa3798e00b2a8c82d1e63de0984.png" alt="Face recognition test with Sophie Turner, Maisie Wiliams and Isaac Hempstead Wright, recognized incorrectly due to missing data" data-id="585" data-full-url="https://gabor.heja.hu/blog/wp-content/uploads/2021/01/6e4d8aa3798e00b2a8c82d1e63de0984.png" data-link="https://gabor.heja.hu/blog/6e4d8aa3798e00b2a8c82d1e63de0984/" class="wp-image-585" srcset="https://gabor.heja.hu/blog/wp-content/uploads/2021/01/6e4d8aa3798e00b2a8c82d1e63de0984.png 319w, https://gabor.heja.hu/blog/wp-content/uploads/2021/01/6e4d8aa3798e00b2a8c82d1e63de0984-128x300.png 128w" sizes="(max-width: 319px) 100vw, 319px" /></a><figcaption class="blocks-gallery-item__caption">Face recognition test with Sophie Turner, Maisie Williams and Isaac Hempstead Wright, recognized incorrectly due to missing data</figcaption></figure></li><li class="blocks-gallery-item"><figure><a href="https://gabor.heja.hu/blog/wp-content/uploads/2021/01/miss2.png"><img src="https://gabor.heja.hu/blog/wp-content/uploads/2021/01/miss2.png" alt="Cast of &quot;Game of Thrones&quot; with too many Kit Haringtons. Some of the faces are recognized incorrectly due to missing data" data-id="587" data-full-url="https://gabor.heja.hu/blog/wp-content/uploads/2021/01/miss2.png" data-link="https://gabor.heja.hu/blog/miss2/" class="wp-image-587" srcset="https://gabor.heja.hu/blog/wp-content/uploads/2021/01/miss2.png 1000w, https://gabor.heja.hu/blog/wp-content/uploads/2021/01/miss2-300x169.png 300w, https://gabor.heja.hu/blog/wp-content/uploads/2021/01/miss2-768x432.png 768w" sizes="(max-width: 799px) 94vw, (max-width: 1023px) 67vw, (max-width: 1365px) 62vw, 1037" /></a><figcaption class="blocks-gallery-item__caption">Cast of &#8220;Game of Thrones&#8221; with too many Kit Haringtons. Some of the faces are recognized incorrectly due to missing data</figcaption></figure></li></ul></figure>



<p>This looks way worse. But of course, whether this is a spectacular failure or a hiccup depends on the parameters and the intended use.</p>



<p>All the incorrectly recognized faces are with less than 0.5 confidence (distance higher than or equal to 0.5), tuning this parameter can get rid of them but then some of the correct recognitions will be cut to.</p>



<p>And also, intended use: for a just for fun project you&#8217;d just need to tune them and then handle the clusters of unrecognized faces. For something more serious, don&#8217;t. Just don&#8217;t use facial recognition.</p>



<p>Recognizing a face based on a 2D image (from a video or photo) is way too easy to trick and also unreliable, unsuitable for any serious use. A few articles from 2020 alone:</p>



<ul><li><a href="https://www.nytimes.com/2020/06/24/technology/facial-recognition-arrest.html">Wrongfully Accused by an Algorithm</a> &#8211; New York Times</li><li><a href="https://www.bbc.com/news/technology-50865437">Facial recognition fails on race, government study says</a> &#8211; BBC</li><li><a href="https://www.businessinsider.com/facial-recognition-fails-96-of-the-time-detroit-police-chief-2020-6">Facial-recognition software fails to correctly identify people &#8216;96% of the time,&#8217; Detroit police chief says</a> &#8211; Business Insider</li><li><a href="https://www.businessinsider.com/amazon-microsoft-ibm-halt-selling-facial-recognition-to-police-2020-6">Outrage over police brutality has finally convinced Amazon, Microsoft, and IBM to rule out selling facial recognition tech to law enforcement. [&#8230;]</a> &#8211; Business Insider</li></ul>



<p>So while it is fun and can yield surprising results, it heavily depends on the quality of the input, fine-tuning and the algorithms used &#8211; even for the big guys like Amazon, Microsoft and IBM.</p>



<p>But by all means, try it, play with it, create your next pet project &#8211; but be aware of the possible downsides as well.</p>



<hr class="wp-block-separator"/>



<p class="has-text-align-center"><em>Copyright info: Each photo was used for educational purposes only and I tried my best to have the correct copyright info displayed, please contact me and I will correct them or remove the photo if needed.</em> </p>
]]></content:encoded>
							<wfw:commentRss>https://gabor.heja.hu/blog/2021/01/04/easy-face-recognition-in-2021-using-python/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
							</item>
		<item>
		<title>Receiving and decoding the DCF77 time signal with an ATMega/ATTiny AVR</title>
		<link>https://gabor.heja.hu/blog/2020/12/12/receiving-and-decoding-the-dcf77-time-signal-with-an-atmega-attiny-avr/</link>
				<comments>https://gabor.heja.hu/blog/2020/12/12/receiving-and-decoding-the-dcf77-time-signal-with-an-atmega-attiny-avr/#comments</comments>
				<pubDate>Sat, 12 Dec 2020 16:55:40 +0000</pubDate>
		<dc:creator><![CDATA[Gabor Heja]]></dc:creator>
				<category><![CDATA[Dev]]></category>
		<category><![CDATA[Wireless]]></category>
		<category><![CDATA[blog]]></category>
		<category><![CDATA[dcf77]]></category>
		<category><![CDATA[dev]]></category>
		<category><![CDATA[radio]]></category>
		<category><![CDATA[rf]]></category>
		<category><![CDATA[sdr]]></category>
		<category><![CDATA[wireless]]></category>

		<guid isPermaLink="false">https://gabor.heja.hu/blog/?p=483</guid>
				<description><![CDATA[DCF77 is a German longwave time signal and standard-frequency radio station &#8211; according to Wikipedia. It is used by most &#8230; <a href="https://gabor.heja.hu/blog/2020/12/12/receiving-and-decoding-the-dcf77-time-signal-with-an-atmega-attiny-avr/" class="more-link"><span class="more-button">Continue reading &#62;<span class="screen-reader-text">Receiving and decoding the DCF77 time signal with an ATMega/ATTiny AVR</span></span></a>]]></description>
								<content:encoded><![CDATA[
<p>DCF77 is a German longwave time signal and standard-frequency radio station &#8211; according to <a href="https://en.wikipedia.org/wiki/DCF77">Wikipedia</a>. It is used by most radio controlled clocks in Europe to adjust the time, after my previous post about <a href="https://gabor.heja.hu/blog/2020/03/16/receiving-and-decoding-433-mhz-radio-signals-from-wireless-devices/">433 MHz devices</a> I decided to check this out and try to make a decoder.</p>



<p>The data packet &#8211; although it is only 59 bits &#8211; holds a lot: the current date and time, day of week, current time zone (CET or CEST), upcoming daylight saving time change and leap seconds, civil danger/emergency warnings, weather forecast and parity bits. (The weather forecast is in a proprietary format, though, more on that later.)</p>



<p>It is transmitted on 77.5 kHz at a whopping 1 bit per second rate using amplitude modulation, the 59 data bits are followed by a sync period as well. The frequency is too low for my SDR so I could not catpure directly but here is a capture made using <a href="http://websdr.org/">WebSDR</a> (opened in Audacity, commented by me):</p>



<figure class="wp-block-image size-large"><img src="https://gabor.heja.hu/blog/wp-content/uploads/2020/11/dcf77_example_2-1024x316.png" alt="" class="wp-image-497" srcset="https://gabor.heja.hu/blog/wp-content/uploads/2020/11/dcf77_example_2-1024x316.png 1024w, https://gabor.heja.hu/blog/wp-content/uploads/2020/11/dcf77_example_2-300x92.png 300w, https://gabor.heja.hu/blog/wp-content/uploads/2020/11/dcf77_example_2-768x237.png 768w, https://gabor.heja.hu/blog/wp-content/uploads/2020/11/dcf77_example_2.png 1058w" sizes="(max-width: 799px) 94vw, (max-width: 1023px) 67vw, (max-width: 1365px) 62vw, 1037" /><figcaption>DCF77 time signal example. The 77.5 kHz signal is amplitude modulated (AM), the amplitude is decreased to 15% for low signal, 100ms low denotes binary 0, 200ms low denotes binary 1, the bits are starting on the second mark, on 59th second of each minute there is no modulation, this is the sync period.</figcaption></figure>



<p>It looks clearly here there are short (100 ms) and long periods (200 ms) of low amplitude and a significantly larger period (~1800 ms) of high amplitude &#8211; according to the <a href="https://en.wikipedia.org/wiki/DCF77#Amplitude_modulation">the specifications</a> &#8211; they are 0 and 1 bits, and the sync signal, respectively.</p>



<p>The bits always start on the beginning of a second, so the data for the 1st second is always starting right at the beginning of the minute.</p>



<h2>The format of a DCF77 packet</h2>



<pre class="wp-block-preformatted">0-00010100101001-00010-1-1100100-1-100000-1-010010-001-10001-00000100-0-0
| |------------/ |:|:| | |-----/ | |----/ | |----/ |-/ |---/ |------/ | |
| |              |:|:| | |       | |      | |      |   |     |        | minute mark (always 0)
| |              |:|:| | |       | |      | |      |   |     |        parity for prev. section
| |              |:|:| | |       | |      | |      |   |     years
| |              |:|:| | |       | |      | |      |   months
| |              |:|:| | |       | |      | |      day of week
| |              |:|:| | |       | |      | days
| |              |:|:| | |       | |      parity for hours
| |              |:|:| | |       | hours
| |              |:|:| | |       parity for minutes
| |              |:|:| | minutes
| |              |:|:| minute start bit (always 1)
| |              |:|:leap second announcement
| |              |:|CET
| |              |:CEST
| |              |summer time announcement
| |              call bit
| civil warning bits and weather information
start of minute (always 0)

The values are encoded digit by digit in BCD:
  a b c d e f g h  =&gt;  a*1 + b*2 + c*4 + d*8 + e*10 + f*20 + g*40 + h*80
  0 1 0 0 0 0 1 0  =&gt;  42
  1 1 1 0 1 0 0 1  =&gt;  97

Hours: in 24 hours format

Day of week: 1 is Monday ... 7 is Sunday

Years: within century (two digits)</pre>



<p>More info in the GitHub repository: <a href="https://github.com/gheja/dcf77-decoder">https://github.com/gheja/dcf77-decoder</a></p>



<p>I created a JavaScript decoder as well: <a href="https://gheja.github.io/dcf77-decoder/tools/decode_js/decode.html">https://gheja.github.io/dcf77-decoder/tools/decode_js/decode.html</a></p>



<h2>Jumping on it</h2>



<p>I ordered a <a href="https://www.ebay.co.uk/itm/Dcf-77-Receiver-Module-Radio-Controlled-Radio-Clock-RC-Arduino-Aerial-DCF1/183874983180">receiver module</a> from eBay that only converts the signal to digital levels but does not decode them &#8211; after all, that&#8217;s the fun part and I want to do that. It turned out that the low amplitude corresponds to high level, high amplitude to low level for this specific module.</p>



<figure class="wp-block-image size-large"><img src="https://gabor.heja.hu/blog/wp-content/uploads/2020/12/20201102_222807_2-1024x634.jpg" alt="" class="wp-image-503" srcset="https://gabor.heja.hu/blog/wp-content/uploads/2020/12/20201102_222807_2-1024x634.jpg 1024w, https://gabor.heja.hu/blog/wp-content/uploads/2020/12/20201102_222807_2-300x186.jpg 300w, https://gabor.heja.hu/blog/wp-content/uploads/2020/12/20201102_222807_2-768x476.jpg 768w, https://gabor.heja.hu/blog/wp-content/uploads/2020/12/20201102_222807_2-1536x952.jpg 1536w, https://gabor.heja.hu/blog/wp-content/uploads/2020/12/20201102_222807_2-2048x1269.jpg 2048w" sizes="(max-width: 799px) 94vw, (max-width: 1023px) 67vw, (max-width: 1365px) 62vw, 1037" /></figure>



<p>In my limited experience the signal is really sensitive to noise and shielding, so it needs some work to be reliable in non-ideal conditions.  For more info see <em>Noise</em> section below.</p>



<p>The <a href="https://www.mas-oy.com/wp-content/uploads/2016/05/DAEV6180B1COB.pdf">documentation of the DCF77 receiver module</a> has suggestions on antenna orientation as well:</p>



<figure class="wp-block-image size-large"><img src="https://gabor.heja.hu/blog/wp-content/uploads/2020/11/Selection_313-1.png" alt="Recommended DCF77 receiver antenna orientation" class="wp-image-487" srcset="https://gabor.heja.hu/blog/wp-content/uploads/2020/11/Selection_313-1.png 1021w, https://gabor.heja.hu/blog/wp-content/uploads/2020/11/Selection_313-1-300x157.png 300w, https://gabor.heja.hu/blog/wp-content/uploads/2020/11/Selection_313-1-768x401.png 768w" sizes="(max-width: 799px) 94vw, (max-width: 1023px) 67vw, (max-width: 1365px) 62vw, 1037" /></figure>



<h2>Hardware</h2>



<p>I tried to go with a simple solution but&#8230; I ended up using an ATMega328P for the microcontroller with an USBasp programmer, USB-TTL converter for communication, and the DCF77 receiver module mentioned earlier.</p>



<p>I read conflicting specifications for the DCF77 receiver (one stated 3.3 volts is fine, the other stated 3.0 volts as the maximum) so I added an LM317T voltage regulator to supply the 3.0 volts to it to be on the safe side (also I don&#8217;t have a spare one).</p>



<p>And just to be sure I don&#8217;t fry anything  I always connected only one device to my computer.</p>



<figure class="wp-block-image size-large"><img src="https://gabor.heja.hu/blog/wp-content/uploads/2020/12/20201110_001055_2-1024x795.jpg" alt="" class="wp-image-504" srcset="https://gabor.heja.hu/blog/wp-content/uploads/2020/12/20201110_001055_2-1024x795.jpg 1024w, https://gabor.heja.hu/blog/wp-content/uploads/2020/12/20201110_001055_2-300x233.jpg 300w, https://gabor.heja.hu/blog/wp-content/uploads/2020/12/20201110_001055_2-768x596.jpg 768w, https://gabor.heja.hu/blog/wp-content/uploads/2020/12/20201110_001055_2-1536x1193.jpg 1536w, https://gabor.heja.hu/blog/wp-content/uploads/2020/12/20201110_001055_2-2048x1591.jpg 2048w" sizes="(max-width: 799px) 94vw, (max-width: 1023px) 67vw, (max-width: 1365px) 62vw, 1037" /><figcaption>DCF77 receiver using an ATMega328P</figcaption></figure>



<h2>Software</h2>



<p>On the software end I decided to go with a simple state machine with the following states: waiting for signal, receiving signal. I said it&#8217;s simple (it actually had more states but they were unnecessary).</p>



<p>So the program <a href="https://github.com/gheja/dcf77-decoder/blob/main/src/main.c#L143">reads the logic level on one pin</a> with some basic noise reduction &#8211; read it, wait a little, repeat 200 times, assume it&#8217;s &#8220;1&#8221; if 75% of reads were &#8220;high&#8221; (confusingly meaning the received radio signal was &#8220;low&#8221;).</p>



<p>The program measures the length of the &#8220;high&#8221; signal so I fine-tuned (guessed) the delay above to repeat the reading and have enough precision to <a href="https://github.com/gheja/dcf77-decoder/blob/main/src/main.c#L222">distinguish between the meanings</a>:</p>



<ul><li>0-69: invalid</li><li>70-84: <strong>logic 1</strong></li><li>85-94: <strong>logic 0</strong></li><li>95-169: invalid</li><li>170-189: <strong>sync</strong></li><li>190+: invalid</li></ul>



<p>Then based on the current state it processes the received data: if it <a href="https://github.com/gheja/dcf77-decoder/blob/main/src/main.c#L266">waits for the sync</a> and got the sync then it resets everything and switches to &#8220;receiving signal&#8221; state, if <a href="https://github.com/gheja/dcf77-decoder/blob/main/src/main.c#L294">it is in receiving state</a> it stores the bit just received.</p>



<p>If an error occurs anywhere (received an invalid signal) or the 59th bit is received it goes back to &#8220;waiting for sync&#8221; state. In &#8220;waiting for sync&#8221; it also checks if the data received in the last minute was valid and if it is then decodes and sends it over serial.</p>



<p>There is a debug option to send more detailed info over serial, see the <a href="https://github.com/gheja/dcf77-decoder/blob/main/README.md">readme </a>for this.</p>



<p>I have also captured a few samples, both correct and invalid ones, check the <a href="https://github.com/gheja/dcf77-decoder/blob/main/samples.txt">samples.txt</a>.</p>



<h2>Accuracy</h2>



<p>For the test I removed most of the messages and added a line with &#8220;!&#8221; only to be sent out as soon as sync is detected. Then checked the the accuracy of my laptop&#8217;s clock using <a href="https://man.cx/ntpdate(1)">ntpdate</a> and on <a href="https://time.is/">time.is</a>, created a quick <a href="https://codepen.io/gheja/pen/qBNMVVW">CodePen demo</a> showing the current time and started receiving:</p>



<figure class="wp-block-embed-youtube wp-block-embed is-type-video is-provider-youtube wp-embed-aspect-16-9 wp-has-aspect-ratio"><div class="wp-block-embed__wrapper">
<iframe title="2020.11.07. DCF77 receiver test" width="1037" height="583" src="https://www.youtube.com/embed/O9zP09CC6Wc?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>
</div></figure>



<p>It turns out the time is off by around 120 milliseconds. I believe it is mostly due to my noise filtering (see <em>Noise</em> below) and the time it takes to transmit the data over the 9600 baud serial port. It might worth a try to tune the noise filtering but it would be much easier to just acknowledge the delay and sync the (hypothetical) other device to the received time +0.12 seconds.</p>



<h2>Noise</h2>



<p>We have radio controlled clocks at work and they don&#8217;t really work. They need to be placed in the windows of a specific side of the building (in the direction of the transmitter) to sync the time so they regularly take a sun bath at weekends of DST changes. And then only 90% of them syncs, the rest need manual reset and will sync overnight. Their manual recommends placing them 2 meters away from electric wiring and steel &#8211; good luck in an office, really.</p>



<p>I started experimenting with it in the middle of the living room, it seems to be quite noisy, but instead of moving to a more ideal location I decided to add some noise filtering in my code. It results some delay but makes the reading way more reliable.</p>



<p>Also, my laptop charger (while charging) adds a lot of noise, it renders about around 90% of readings unusable.</p>



<p>In the current version the serial port takes time to transmit the data at 9600 bits/second, in some cases longer than the pulse length of &#8220;zero&#8221; (100 ms) which prevents the correct detection of the edge.</p>



<h2>Furhter?</h2>



<p> That&#8217;s all for now. There are places for further optimization but I think this is enough for a proof of concept implementation. </p>



<p><strong>Some interesting stuffs</strong></p>



<p><a href="https://www.hackster.io/edr1924">Erik de Ruiter</a> created a <a href="https://www.hackster.io/edr1924/dcf77-analyzer-clock-v2-0-c25404">DCF77 Analyzer/clock</a> that decodes and displays the data, as well as the raw bits and some more.</p>



<p>There are some <a href="http://www.grother.de/gps-to-dcf77-module.html">GPS to DCF77 modules</a>, too that  create DCF77 signal based on date and time received from GPS satellites. For places with poor coverage or just fun.</p>



<p>The weather data is transmitted in a <a href="http://arduino-projects4u.com/home-weather-station/">proprietary format</a>, supplied by <a href="https://www.meteotime.com/en/">Meteotime</a>. To decode it you need a <a href="http://arduino-projects4u.com/product/meteotime-hkw581/">HKW581 Meteotime decoder chip</a>, the price includes the lifetime license.</p>



<p>As an interesting fact, people managed to reverse engineer the Meteotime format, an example is <a href="https://github.com/ottojo/MeteoDecode">MeteoDecode</a> but the keys were removed on request, also <a href="http://arduino-projects4u.com/meteotime/">Arduino Projects4u</a> has a <a href="http://arduino-projects4u.com/meteotime/">description and example</a>, too.</p>



<p><strong>More</strong></p>



<p><a href="https://www.ptb.de/cms/en/ptb/fachabteilungen/abt4/fb-44/ag-442/dissemination-of-legal-time/dcf77/dcf77-amplitude-modulation.html">https://www.ptb.de/cms/en/ptb/fachabteilungen/abt4/fb-44/ag-442/dissemination-of-legal-time/dcf77/dcf77-amplitude-modulation.html</a></p>



<p><a href="http://arduino-projects4u.com/home-weather-station/">http://arduino-projects4u.com/home-weather-station/</a></p>



<p><a href="https://github.com/ottojo/MeteoDecode">https://github.com/ottojo/MeteoDecode</a></p>
]]></content:encoded>
							<wfw:commentRss>https://gabor.heja.hu/blog/2020/12/12/receiving-and-decoding-the-dcf77-time-signal-with-an-atmega-attiny-avr/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
							</item>
		<item>
		<title>Receiving and decoding 433 MHz radio signals from wireless devices</title>
		<link>https://gabor.heja.hu/blog/2020/03/16/receiving-and-decoding-433-mhz-radio-signals-from-wireless-devices/</link>
				<comments>https://gabor.heja.hu/blog/2020/03/16/receiving-and-decoding-433-mhz-radio-signals-from-wireless-devices/#comments</comments>
				<pubDate>Mon, 16 Mar 2020 08:08:41 +0000</pubDate>
		<dc:creator><![CDATA[Gabor Heja]]></dc:creator>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[Dev]]></category>
		<category><![CDATA[Wireless]]></category>
		<category><![CDATA[433mhz]]></category>
		<category><![CDATA[audacity]]></category>
		<category><![CDATA[dev]]></category>
		<category><![CDATA[domoticz]]></category>
		<category><![CDATA[gqrx]]></category>
		<category><![CDATA[home automation]]></category>
		<category><![CDATA[nesdr]]></category>
		<category><![CDATA[radio]]></category>
		<category><![CDATA[rf]]></category>
		<category><![CDATA[rtl433]]></category>
		<category><![CDATA[sdr]]></category>
		<category><![CDATA[wireless]]></category>

		<guid isPermaLink="false">https://gabor.heja.hu/blog/?p=431</guid>
				<description><![CDATA[Wireless devices like temperature and humidity sensors, light switches, garage remotes, power sockets are usually sending and receiving their data &#8230; <a href="https://gabor.heja.hu/blog/2020/03/16/receiving-and-decoding-433-mhz-radio-signals-from-wireless-devices/" class="more-link"><span class="more-button">Continue reading &#62;<span class="screen-reader-text">Receiving and decoding 433 MHz radio signals from wireless devices</span></span></a>]]></description>
								<content:encoded><![CDATA[
<p>Wireless devices like temperature and humidity sensors, light switches, garage remotes, power sockets are usually sending and receiving their data in a simple binary format over radio, technically: <a href="https://en.wikipedia.org/wiki/On%E2%80%93off_keying">on-off keying (OOK)</a>.</p>



<p>Decoding these messages by eye might seem tricky at first but it can be done quickly once you know the how. Although there are softwares to do this automatically, it is a fun excercise and gives a better understanding of the messages in the air.</p>



<h2>Intro</h2>



<p>Softwares I use in this post and find useful:</p>



<ul><li><a href="https://gqrx.dk/">Gqrx SDR</a> &#8211; to inspect the radio spectrum and capture the messages</li><li><a href="https://www.audacityteam.org/">Audacity</a> &#8211; to visually inspect the captured data and decode by eye</li><li><a href="https://github.com/merbanan/rtl_433">RTL 433</a> &#8211; receiving and decoding messages on the command line</li><li><a href="https://www.domoticz.com/">Domoticz</a> &#8211; to (automatically) register devices and decode their messages, draw graphs and even control some of them</li></ul>



<p>I have a <a href="https://www.nooelec.com/store/sdr/sdr-receivers/nesdr-mini.html">Nooelec NESDR Mini</a> that I used in the screenshots below but it should work with any <a href="https://en.wikipedia.org/wiki/List_of_software-defined_radios">Software Defined Radio (SDR)</a>.</p>



<p>Although I am talking about 433 MHz, the message format is independent from the frequency &#8211; however if you plan to buy any device see <em>Note 1</em> on the bottom of the page.</p>



<h2>The easy way</h2>



<p>Just start a decoder, wait for your message to arrive and you&#8217;re done.</p>



<figure class="wp-block-image"><img src="https://gabor.heja.hu/blog/wp-content/uploads/2020/03/domoticz2-1024x254.png" alt="" class="wp-image-434" srcset="https://gabor.heja.hu/blog/wp-content/uploads/2020/03/domoticz2-1024x254.png 1024w, https://gabor.heja.hu/blog/wp-content/uploads/2020/03/domoticz2-300x74.png 300w, https://gabor.heja.hu/blog/wp-content/uploads/2020/03/domoticz2-768x191.png 768w, https://gabor.heja.hu/blog/wp-content/uploads/2020/03/domoticz2.png 1728w" sizes="(max-width: 799px) 94vw, (max-width: 1023px) 67vw, (max-width: 1365px) 62vw, 1037" /><figcaption>Domoticz showing the sensor reading received from an RFLink 433.92 gateway</figcaption></figure>



<figure class="wp-block-image"><img src="https://gabor.heja.hu/blog/wp-content/uploads/2020/03/rtl_433_4-1024x55.png" alt="" class="wp-image-461" srcset="https://gabor.heja.hu/blog/wp-content/uploads/2020/03/rtl_433_4-1024x55.png 1024w, https://gabor.heja.hu/blog/wp-content/uploads/2020/03/rtl_433_4-300x16.png 300w, https://gabor.heja.hu/blog/wp-content/uploads/2020/03/rtl_433_4-768x41.png 768w, https://gabor.heja.hu/blog/wp-content/uploads/2020/03/rtl_433_4.png 1625w" sizes="(max-width: 799px) 94vw, (max-width: 1023px) 67vw, (max-width: 1365px) 62vw, 1037" /><figcaption>A captured packet in RTL 433</figcaption></figure>



<p>Hey, that&#8217;s cheating!</p>



<h2>The line format</h2>



<p>There are two formats commonly used by these communications: Pulse Width Modulation (PWM) and Pulse Position Modulation (PPM). They both encode binary values (zeros and ones) but differently. I have added vertical lines to highlight the beginning of the values.</p>



<figure class="wp-block-image"><img src="https://gabor.heja.hu/blog/wp-content/uploads/2020/03/ppm_pwm_4.png" alt="" class="wp-image-447" srcset="https://gabor.heja.hu/blog/wp-content/uploads/2020/03/ppm_pwm_4.png 902w, https://gabor.heja.hu/blog/wp-content/uploads/2020/03/ppm_pwm_4-300x199.png 300w, https://gabor.heja.hu/blog/wp-content/uploads/2020/03/ppm_pwm_4-768x509.png 768w" sizes="(max-width: 799px) 94vw, (max-width: 1023px) 67vw, (max-width: 1365px) 62vw, 1037" /><figcaption><em>Differences between Pulse Width Modulation (PWM)</em> and <em>Pulse Position Modulation (PPM).</em></figcaption></figure>



<p>PWM encodes the data by sending a short pulse then a long pause or a long pulse then a short pause. Depending on the protocol used one means <em>one</em> the other <em>zero</em>. Note that the transmissions have a fixed length.</p>



<p>PPM encodes the data by sending a fixed length pulse, then a short or long pause before sending the next &#8211; the short period encodes a <em>zero</em>, the long encodes a <em>one</em> &#8211; or reversed, depending on the protocol used. Note that the two values have different lengths.</p>



<h2>Capturing the message</h2>



<p>I am using Gqrx as it is a fairly standard SDR on Linux and works just fine.</p>



<figure class="wp-block-image"><img src="https://gabor.heja.hu/blog/wp-content/uploads/2020/03/gqrx3-1024x554.png" alt="" class="wp-image-448" srcset="https://gabor.heja.hu/blog/wp-content/uploads/2020/03/gqrx3-1024x554.png 1024w, https://gabor.heja.hu/blog/wp-content/uploads/2020/03/gqrx3-300x162.png 300w, https://gabor.heja.hu/blog/wp-content/uploads/2020/03/gqrx3-768x415.png 768w, https://gabor.heja.hu/blog/wp-content/uploads/2020/03/gqrx3.png 1159w" sizes="(max-width: 799px) 94vw, (max-width: 1023px) 67vw, (max-width: 1365px) 62vw, 1037" /><figcaption><em>Gqrx in action (1: start/stop button, 2: frequency tuner, 3: receiver options, 4: audio record stop/start button, 5: a message)</em></figcaption></figure>



<p>First you need to start the processing (1), then find the frequency you&#8217;re intereted in using the tuner (2). If you find the frequency you&#8217;re looking for, set the receiver to AM and filter to Normal (3) then you can start recording using (4). The message is marked with (5) in this screenshot.</p>



<p>The output will be a WAVE audio file (.wav) that can be opened using any sound editor software.</p>



<p>Note: you might need to adjust gains to have a clear signal, try to fiddle with <em>LNA gain</em> on <em>Input controls</em> tab and <em>Gain</em> on <em>Audio</em> tab.</p>



<h2>Checking the captured message</h2>



<p>I used Audacity in this post but any sound editor will do the job, you only need to zoom in on parts of a WAVE file.</p>



<p>After opening the recording you should see something like this.</p>



<figure class="wp-block-image"><img src="https://gabor.heja.hu/blog/wp-content/uploads/2020/03/audacity_2_1.png" alt="" class="wp-image-442" srcset="https://gabor.heja.hu/blog/wp-content/uploads/2020/03/audacity_2_1.png 782w, https://gabor.heja.hu/blog/wp-content/uploads/2020/03/audacity_2_1-300x236.png 300w, https://gabor.heja.hu/blog/wp-content/uploads/2020/03/audacity_2_1-768x604.png 768w" sizes="(max-width: 782px) 100vw, 782px" /><figcaption>Audacity in action (1: the message from the previous screenshot, 2: zoom to selection button)</figcaption></figure>



<p>You should see some noise and some structure (1), the latter is the message you&#8217;ve captured. Select and zoom in to it using (2).</p>



<figure class="wp-block-image"><img src="https://gabor.heja.hu/blog/wp-content/uploads/2020/03/audacity_2_2-1-1024x184.png" alt="" class="wp-image-443" srcset="https://gabor.heja.hu/blog/wp-content/uploads/2020/03/audacity_2_2-1-1024x184.png 1024w, https://gabor.heja.hu/blog/wp-content/uploads/2020/03/audacity_2_2-1-300x54.png 300w, https://gabor.heja.hu/blog/wp-content/uploads/2020/03/audacity_2_2-1-768x138.png 768w, https://gabor.heja.hu/blog/wp-content/uploads/2020/03/audacity_2_2-1.png 1814w" sizes="(max-width: 799px) 94vw, (max-width: 1023px) 67vw, (max-width: 1365px) 62vw, 1037" /><figcaption>A zoomed in Pulse Position Modulated (PPM) message in Audacity.</figcaption></figure>



<p>Notice that the message seems to be repeated &#8211; there are larger gaps and then the packet starts again (gap, 1 peak, 3 peaks, &#8230;).</p>



<p>It is very common for a message to contain the same packet multiple times, as the trasmitters are not synchronised, all of them send as they like, collisions are common. Repeating the data increases the chance of a clear reception.</p>



<p>In this case the wireless thermometer sends the package 7 times, leaving a long gap between each time, according to its protocol. Zoom in on the first one.</p>



<figure class="wp-block-image"><img src="https://gabor.heja.hu/blog/wp-content/uploads/2020/03/audacity_2_3_1-1024x83.png" alt="" class="wp-image-454" srcset="https://gabor.heja.hu/blog/wp-content/uploads/2020/03/audacity_2_3_1-1024x83.png 1024w, https://gabor.heja.hu/blog/wp-content/uploads/2020/03/audacity_2_3_1-300x24.png 300w, https://gabor.heja.hu/blog/wp-content/uploads/2020/03/audacity_2_3_1-768x62.png 768w, https://gabor.heja.hu/blog/wp-content/uploads/2020/03/audacity_2_3_1.png 1774w" sizes="(max-width: 799px) 94vw, (max-width: 1023px) 67vw, (max-width: 1365px) 62vw, 1037" /><figcaption>A captured packet.</figcaption></figure>



<h2>Decoding by eye</h2>



<p>After we successfully found the useful part of the message all we need to do is decoding it. Easy, right?</p>



<p>If you don&#8217;t know the packet format but know the measurements transmitted (as this is a temperature and humidity meter) you can guess the format. Most of the sensors send the data in a very basic format, usually the temperature in 0.1 °C and humidity in percent.</p>



<p>By fiddling these values, one at a time, capturing and decoding the packets we can find the bits that change and find out their meaning.</p>



<p>There are a few things to consider when guessing the format:</p>



<ul><li>modulation: PPM or PWM</li><li>gap length &#8211; bit value pairing: is the short <em>zero</em> and long <em>one</em> or the other way around</li><li><a href="https://en.wikipedia.org/wiki/Endianness">bit-endiannes</a>: the order of bits in bytes. 131 (128+2+1) can be encoded two ways 10000011 (big endian) or 11000001 (little endian)</li><li><a href="https://en.wikipedia.org/wiki/Checksum">checksum</a>: if there is a part of the message that changes when any of the other part changes, that might be a checksum and can be ignored when eye-decoding (not common)</li><li><a href="https://en.wikipedia.org/wiki/Rolling_code">rolling code</a>: if there is a part that changes with all transmissions it might be rolling code, can be ignored as well (not common)</li><li>sequence number: if a part increases with every transmission it might be a sequence, can be ignored as well (not common)</li></ul>



<p>First of all, the length of the pulses seems to be uniform but the gaps change. It looks like Pulse Position Modulation (PPM), let&#8217;s assign a value for the gaps &#8211; <em>zero</em> for the short ones, <em>one</em> for the long ones.</p>



<figure class="wp-block-image"><img src="https://gabor.heja.hu/blog/wp-content/uploads/2020/03/audacity_2_3-1024x83.png" alt="" class="wp-image-437" srcset="https://gabor.heja.hu/blog/wp-content/uploads/2020/03/audacity_2_3-1024x83.png 1024w, https://gabor.heja.hu/blog/wp-content/uploads/2020/03/audacity_2_3-300x24.png 300w, https://gabor.heja.hu/blog/wp-content/uploads/2020/03/audacity_2_3-768x62.png 768w, https://gabor.heja.hu/blog/wp-content/uploads/2020/03/audacity_2_3.png 1774w" sizes="(max-width: 799px) 94vw, (max-width: 1023px) 67vw, (max-width: 1365px) 62vw, 1037" /><figcaption>How to decode Pulse Position Modulated (PPM) packet by eye. I added a vertical green line after each byte (<em>8 bits</em>) of data for clarity.</figcaption></figure>



<p>So we know the following parameters (either because it is a switch or because it is displayed on the device):</p>



<ul><li>the sensor was set to channel #2,</li><li>temperature was 12.3 °C,</li><li>humidity was 45%.</li></ul>



<p>Let&#8217;s capture and decode more packets, while keeping an eye on the parameters:</p>



<pre class="wp-block-preformatted">-------------------------------- data --- chan  temp  humi
10010000 00011001 00000111 10110010 11010    2  12.3    45
10010000 00011001 00000111 11000010 11010    2  12.4    45
10010000 00011001 00000111 11010010 11010    2  12.5    45
10010000 00011001 00000111 11010011 00100    2  12.5    50
10010000 00011001 00000111 11010011 11000    2  12.5    60
10010000 00011001 00000111 11010011 11000    2  12.5    60
10010000 00011010 00000111 11010011 11000    3  12.5    60
10010000 00011000 00000111 11010011 11000    1  12.5    60
10010000 00011000 00000111 11100011 11000    1  12.6    60
10010000 00011000 00000111 11100100 00010    1  12.6    65
10010000 00011000 00010000 00110100 00010    1  25.9    65
xxxxxxxx xxxxxxcc xxxttttt ttttthhh hhhhx</pre>



<p>By noting which bits change when a parameter changes we can find and mark the correlations &#8211; see the last line, I marked &#8220;c&#8221; for channel change, &#8220;t&#8221; for temperature, &#8220;h&#8221; for humidity.</p>



<p>In the case of this device we can conclude the following with great certainty:</p>



<ul><li><em>channel</em> is sent on bits 15-16, value is &#8220;switch position &#8211; 1&#8221;, big endian,</li><li><em>temperature</em> is sent on bits 20-29, value is in 0.1 °C, big endian,</li><li><em>humidity</em> is sent on bits 30-36, value is in percent, big endian,</li><li>the rest of the bits are still unknown to us.</li></ul>



<p>To increase certanity we should test more, but I am happy with these results for now, feel free to continue.</p>



<h2>Decoding another device</h2>



<p>I also have an RF remote with several buttons. I started a capture the same way as described before and pressed three buttons:</p>



<figure class="wp-block-image"><img src="https://gabor.heja.hu/blog/wp-content/uploads/2020/03/gqrx4-1024x554.png" alt="" class="wp-image-465" srcset="https://gabor.heja.hu/blog/wp-content/uploads/2020/03/gqrx4-1024x554.png 1024w, https://gabor.heja.hu/blog/wp-content/uploads/2020/03/gqrx4-300x162.png 300w, https://gabor.heja.hu/blog/wp-content/uploads/2020/03/gqrx4-768x415.png 768w, https://gabor.heja.hu/blog/wp-content/uploads/2020/03/gqrx4.png 1159w" sizes="(max-width: 799px) 94vw, (max-width: 1023px) 67vw, (max-width: 1365px) 62vw, 1037" /><figcaption>Gqrx capturing RF remote.</figcaption></figure>



<p>&#8230; opened it in Audacity:</p>



<figure class="wp-block-image"><img src="https://gabor.heja.hu/blog/wp-content/uploads/2020/03/20200314_00000005-1024x193.png" alt="" class="wp-image-466" srcset="https://gabor.heja.hu/blog/wp-content/uploads/2020/03/20200314_00000005-1024x193.png 1024w, https://gabor.heja.hu/blog/wp-content/uploads/2020/03/20200314_00000005-300x56.png 300w, https://gabor.heja.hu/blog/wp-content/uploads/2020/03/20200314_00000005-768x144.png 768w, https://gabor.heja.hu/blog/wp-content/uploads/2020/03/20200314_00000005.png 1834w" sizes="(max-width: 799px) 94vw, (max-width: 1023px) 67vw, (max-width: 1365px) 62vw, 1037" /><figcaption>RF remote capture opened in Audacity.</figcaption></figure>



<p>&#8230; as I held the buttons for a short amount of time, the remote sent the packet 3-4 times. Cutting those and the noise here are the three buttons:</p>



<figure class="wp-block-image"><img src="https://gabor.heja.hu/blog/wp-content/uploads/2020/03/20200314_00000004_2-1024x82.png" alt="" class="wp-image-467" srcset="https://gabor.heja.hu/blog/wp-content/uploads/2020/03/20200314_00000004_2-1024x82.png 1024w, https://gabor.heja.hu/blog/wp-content/uploads/2020/03/20200314_00000004_2-300x24.png 300w, https://gabor.heja.hu/blog/wp-content/uploads/2020/03/20200314_00000004_2-768x62.png 768w, https://gabor.heja.hu/blog/wp-content/uploads/2020/03/20200314_00000004_2.png 1780w" sizes="(max-width: 799px) 94vw, (max-width: 1023px) 67vw, (max-width: 1365px) 62vw, 1037" /><figcaption>RF remote capture in Audacity, looks like PWM.</figcaption></figure>



<p>These packets look different than before, the pulses are different in length but with the gaps the bits seem to be consisten in length &#8211; so this looks like PWM.</p>



<p><em>Note: </em>as this is binary data the difference in amplitudes (height of the signal) does not matter, it is an artifact of the sender/receiver.</p>



<p>Let&#8217;s assign<em> one</em> and <em>zero</em> values for the short/long pulses and start decoding by eye:</p>



<figure class="wp-block-image"><img src="https://gabor.heja.hu/blog/wp-content/uploads/2020/03/20200314_00000004_3-1024x82.png" alt="" class="wp-image-468" srcset="https://gabor.heja.hu/blog/wp-content/uploads/2020/03/20200314_00000004_3-1024x82.png 1024w, https://gabor.heja.hu/blog/wp-content/uploads/2020/03/20200314_00000004_3-300x24.png 300w, https://gabor.heja.hu/blog/wp-content/uploads/2020/03/20200314_00000004_3-768x62.png 768w, https://gabor.heja.hu/blog/wp-content/uploads/2020/03/20200314_00000004_3.png 1780w" sizes="(max-width: 799px) 94vw, (max-width: 1023px) 67vw, (max-width: 1365px) 62vw, 1037" /><figcaption>RF remote capture in Audacity, decoded as PWM.</figcaption></figure>



<p>So it seems that significant part of the packet is constant, only four bits (marked in teal) varies, these must be the values for the buttons.</p>



<p>We will validate these findings in an upcoming blog post.</p>



<h2>Using software to receive and decode the packets</h2>



<p>So after decoding by hand let&#8217;s see a software doing this for us. I use RTL 433 here, it grabs the first SDR it finds and tunes automatically to 433.92 MHz &#8211; handy.</p>



<p>Currently it knows around 145 devices (or packet formats), adding -A switch shows a dump of the received package and some analytics it uses to parse it.</p>



<figure class="wp-block-image"><img src="https://gabor.heja.hu/blog/wp-content/uploads/2020/03/rtl_433_3-1024x471.png" alt="" class="wp-image-460" srcset="https://gabor.heja.hu/blog/wp-content/uploads/2020/03/rtl_433_3-1024x471.png 1024w, https://gabor.heja.hu/blog/wp-content/uploads/2020/03/rtl_433_3-300x138.png 300w, https://gabor.heja.hu/blog/wp-content/uploads/2020/03/rtl_433_3-768x354.png 768w, https://gabor.heja.hu/blog/wp-content/uploads/2020/03/rtl_433_3.png 1625w" sizes="(max-width: 799px) 94vw, (max-width: 1023px) 67vw, (max-width: 1365px) 62vw, 1037" /><figcaption><em>RTL 433 shows a captured packet.</em></figcaption></figure>



<p>First of all, the colored part (that I showed earlier) shows the parsed data, (1) indicates it is an on-off keying (OOK) encoded packet, (2) shows it is indeed PPM and (3) is the actual decoded data.</p>



<p>Another great advantage is that RTL 433 being an open source software, you can check out the decoder and the documentation for the protocols. For <a href="https://github.com/merbanan/rtl_433/blob/master/src/devices/prologue.c">the Prologue sensor</a> looks like this:</p>



<figure class="wp-block-image"><img src="https://gabor.heja.hu/blog/wp-content/uploads/2020/03/20200315_00000001-1024x645.png" alt="" class="wp-image-469" srcset="https://gabor.heja.hu/blog/wp-content/uploads/2020/03/20200315_00000001-1024x645.png 1024w, https://gabor.heja.hu/blog/wp-content/uploads/2020/03/20200315_00000001-300x189.png 300w, https://gabor.heja.hu/blog/wp-content/uploads/2020/03/20200315_00000001-768x484.png 768w, https://gabor.heja.hu/blog/wp-content/uploads/2020/03/20200315_00000001.png 1342w" sizes="(max-width: 799px) 94vw, (max-width: 1023px) 67vw, (max-width: 1365px) 62vw, 1037" /><figcaption>The specification of the Prologue sensor protocol from the source code of RTL 433</figcaption></figure>



<p>More info here: <a href="https://github.com/merbanan/rtl_433/blob/master/src/devices/">https://github.com/merbanan/rtl_433/blob/master/src/devices/</a></p>



<h2>More</h2>



<p>Dave posted in detail about setting up a remote controlled Balsam Hill Christmas tree from scratch in his OpenHAB   home automation system, with Alexa integration. It is a good read, if you enjoyed this one I highly recommend checking his post out, too: <a href="https://www.davecorder.org/blog/automating-a-balsam-hill-artificial-tree/">Automating a Balsam Hill Artificial Tree</a></p>



<h2>Coming up next</h2>



<p>In a future post I will share how to send packets by copying the ones we captured and also creating our own.</p>



<h2>Notes</h2>



<p><strong>Note 1:</strong> In Europe the most common frequency these devices use is 433.92 MHz, which is usually referred as &#8220;433 MHz&#8221; and sometimes &#8220;434 MHz&#8221;.  There are two other frequencies typically used, 433.42 MHz and 868.3 MHz. In the US 915 MHz is typical (for more precise definitions see <a href="https://en.wikipedia.org/wiki/International_Telecommunication_Union">ITU</a> and search for your country). It is important to note that <strong>not all receivers support multiple frequencies</strong>, you might need separate receivers for each.</p>
]]></content:encoded>
							<wfw:commentRss>https://gabor.heja.hu/blog/2020/03/16/receiving-and-decoding-433-mhz-radio-signals-from-wireless-devices/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
							</item>
		<item>
		<title>Domoticz HTTP/HTTPS poller and JSON</title>
		<link>https://gabor.heja.hu/blog/2020/01/16/domoticz-http-https-poller-and-json/</link>
				<comments>https://gabor.heja.hu/blog/2020/01/16/domoticz-http-https-poller-and-json/#comments</comments>
				<pubDate>Thu, 16 Jan 2020 17:14:52 +0000</pubDate>
		<dc:creator><![CDATA[Gabor Heja]]></dc:creator>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[Dev]]></category>
		<category><![CDATA[Wireless]]></category>
		<category><![CDATA[dev]]></category>
		<category><![CDATA[domoticz]]></category>
		<category><![CDATA[home automation]]></category>
		<category><![CDATA[howto]]></category>
		<category><![CDATA[json]]></category>
		<category><![CDATA[linux]]></category>
		<category><![CDATA[raspberry pi]]></category>

		<guid isPermaLink="false">https://gabor.heja.hu/blog/?p=395</guid>
				<description><![CDATA[Adding a sensor of a remote host to Domoticz is easy, just not very well documented. I have a DS1820 &#8230; <a href="https://gabor.heja.hu/blog/2020/01/16/domoticz-http-https-poller-and-json/" class="more-link"><span class="more-button">Continue reading &#62;<span class="screen-reader-text">Domoticz HTTP/HTTPS poller and JSON</span></span></a>]]></description>
								<content:encoded><![CDATA[
<p>Adding a sensor of a remote host to Domoticz is easy, just not very well documented.</p>



<p>I have a DS1820 one-wire sensor plugged on a Linux machine that polls the temperature every minute then stores it in a CSV. Since I recently installed Domoticz on a Raspberry PI, I wanted to show the values there as well. I&#8217;ll show what you need to do this.</p>



<p>Open the <em>Setup </em>menu and choose the <em>Hardware</em> option. Enter a name you like and select the <strong>HTTP/HTTPS poller</strong> type, you should see the following:</p>



<figure class="wp-block-image"><img src="https://gabor.heja.hu/blog/wp-content/uploads/2020/01/domoticz_remote_sensor_http_https_script_json_4_2-1024x951.png" alt="HTTP/HTTPS poller to handle JSON in Domoticz" class="wp-image-400" srcset="https://gabor.heja.hu/blog/wp-content/uploads/2020/01/domoticz_remote_sensor_http_https_script_json_4_2-1024x951.png 1024w, https://gabor.heja.hu/blog/wp-content/uploads/2020/01/domoticz_remote_sensor_http_https_script_json_4_2-300x279.png 300w, https://gabor.heja.hu/blog/wp-content/uploads/2020/01/domoticz_remote_sensor_http_https_script_json_4_2-768x714.png 768w, https://gabor.heja.hu/blog/wp-content/uploads/2020/01/domoticz_remote_sensor_http_https_script_json_4_2.png 1057w" sizes="(max-width: 799px) 94vw, (max-width: 1023px) 67vw, (max-width: 1365px) 62vw, 1037" /><figcaption>HTTP/HTTPS poller to handle JSON in Domoticz  </figcaption></figure>



<p><strong>Data Timeout:</strong> leave it <em>Disabled</em></p>



<p><strong>ContentType:</strong> the type of received data, <em>application/json</em></p>



<p><strong>URL: </strong>the full URL including scheme, host name and path where you will have the JSON file with the latest data.</p>



<p><strong>Command:</strong> this is a filename (with extension) for a script that will handle the downloaded JSON, which Domoticz will search  at <em>&#8230;/domoticz/scripts/lua_parsers/&lt;command&gt;</em><br>Tehre&#8217;s a basic example called <em>example_json.lua</em>, for a this setup it will be fine.</p>



<p><strong>Refresh: </strong>refresh interval in seconds.</p>



<p>After adding you should see it immediately in the list:</p>



<figure class="wp-block-image"><img src="https://gabor.heja.hu/blog/wp-content/uploads/2020/01/domoticz_remote_sensor_http_https_script_json_2-1024x159.png" alt="HTTP/HTTPS poller added in Domoticz" class="wp-image-397" srcset="https://gabor.heja.hu/blog/wp-content/uploads/2020/01/domoticz_remote_sensor_http_https_script_json_2-1024x159.png 1024w, https://gabor.heja.hu/blog/wp-content/uploads/2020/01/domoticz_remote_sensor_http_https_script_json_2-300x47.png 300w, https://gabor.heja.hu/blog/wp-content/uploads/2020/01/domoticz_remote_sensor_http_https_script_json_2-768x119.png 768w" sizes="(max-width: 799px) 94vw, (max-width: 1023px) 67vw, (max-width: 1365px) 62vw, 1037" /><figcaption>HTTP/HTTPS poller added in Domoticz</figcaption></figure>



<p>Click on <em>Create Virtual Sensors</em> button to add a sensor, give it a custom name, select the type of data you want to log and select OK. It should appear in your devices list:</p>



<figure class="wp-block-image"><img src="https://gabor.heja.hu/blog/wp-content/uploads/2020/01/domoticz_remote_sensor_http_https_script_json_3-1024x200.png" alt="A temperature sensor using Domoticz's HTTP/HTTPS poller" class="wp-image-398" srcset="https://gabor.heja.hu/blog/wp-content/uploads/2020/01/domoticz_remote_sensor_http_https_script_json_3-1024x200.png 1024w, https://gabor.heja.hu/blog/wp-content/uploads/2020/01/domoticz_remote_sensor_http_https_script_json_3-300x59.png 300w, https://gabor.heja.hu/blog/wp-content/uploads/2020/01/domoticz_remote_sensor_http_https_script_json_3-768x150.png 768w" sizes="(max-width: 799px) 94vw, (max-width: 1023px) 67vw, (max-width: 1365px) 62vw, 1037" /><figcaption>A temperature sensor using Domoticz&#8217;s HTTP/HTTPS poller</figcaption></figure>



<p>Note the <em>Idx </em>of the sensor (not the <em>ID</em> nor <em>Unit</em>, the first number) which is 15 in my case. You will need to put this in the JSON file on <em>remoteserver</em> in a field named <em>id</em> &#8211; yes, this is inconsistent.</p>



<p>The JSON file you need to provide on <em>remoteserver</em> can be as simple as this:</p>



<pre class="wp-block-preformatted">{ "id": 15, "temperature": 12.3 }</pre>



<p>Tip: you can output this with any script, you can consider it a string, just replace the value with the latest measurement. You also need to update it periodically.</p>



<p>As soon as you added the poller, Domoticz starts to poll the URL you supplied and passes the result to the script you supplied.</p>



<p>If in the next few minutes you start to see the data in Domoticz, congratulations, you have it working. If not, check the <em>Troubleshooting</em> section below.</p>



<p><strong>Note 1:</strong> Domoticz executes the script one time for every HTTP/HTTPS poller &#8220;hardware&#8221; and you can have multiple sensors configured for one poller. In this case you need to supply multiple id/idx values and multiple values for measurements and need a custom script updating each sensor.</p>



<p><strong>Note 2: </strong>In the example script the <em>id</em> is read from the JSON and blindly supplied to Domoticz which will update the sensor having that as <em>idx</em>. This may overwrite the data of any other sensor if configured improperly.</p>



<p>Unfortunately Domoticz is only supplying the hardware idx as <em>hwdId</em> variable but not the idx of the sensor(s) on that poller, so if you want to prevent it you need to hardcode the idx of the sensor(s) in your script, e.g. you can create <em>remote_1_json.lua</em> with the content:</p>



<pre class="wp-block-preformatted">local idx = 15
local temperature = domoticz_applyJsonPath(request['content'], '.temperature')
domoticz_updateDevice(idx, '' , temperature)</pre>



<p>Then you can entirely skip the <em>id </em>field in JSON:</p>



<pre class="wp-block-preformatted">{ "temperature": 12.3 }</pre>



<p>Just make sure you update the script&#8217;s name on the web interface.</p>



<p><strong>Note 3: </strong>you might need to add the following header on the remote host to your response:</p>



<pre class="wp-block-code"><code>Content-Type: application/json</code></pre>



<p><strong>Troubleshooting</strong></p>



<pre class="wp-block-preformatted">Error: CLuaHandler (updateDevice from LUA) : Incorrect parameters type </pre>



<p>One of the field received in JSON is invalid. The <em>id</em> should be an integer, <em>temperature </em>should be a float.</p>



<pre class="wp-block-preformatted">Error: CLuaHandler: cannot open /home/.../domoticz/scripts/lua_parsers/example_json: No such file or directory </pre>



<p>You need to provide a full file name with extension in the <em>Command</em> field of the <em>Setup</em> &gt; <em>Hardware </em>page. Also, make sure the script exists.</p>



<pre class="wp-block-preformatted">CLuaHandler (updateDevice from LUA) : idx=15 nvalue= svalue=12.3 invalue=0 signallevel=12 batterylevel=255 </pre>



<p>This is normal, the script parsed the JSON and issued an update for the sensor. If you can&#8217;t see the update on the interface, double check the <em>idx</em> or <em>id</em> parameter you have in the JSON file.</p>
]]></content:encoded>
							<wfw:commentRss>https://gabor.heja.hu/blog/2020/01/16/domoticz-http-https-poller-and-json/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
							</item>
		<item>
		<title>Bokosan &#8211; a reverse Sokoban</title>
		<link>https://gabor.heja.hu/blog/2018/05/16/bokosan-a-reverse-sokoban/</link>
				<comments>https://gabor.heja.hu/blog/2018/05/16/bokosan-a-reverse-sokoban/#respond</comments>
				<pubDate>Wed, 16 May 2018 12:43:38 +0000</pubDate>
		<dc:creator><![CDATA[Gabor Heja]]></dc:creator>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[Dev]]></category>
		<category><![CDATA[Gamedev]]></category>
		<category><![CDATA[bokosan]]></category>
		<category><![CDATA[gamedev]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[js]]></category>
		<category><![CDATA[js13k]]></category>
		<category><![CDATA[js13kgames]]></category>

		<guid isPermaLink="false">https://gabor.heja.hu/blog/?p=340</guid>
				<description><![CDATA[Bokosan is a reverse Sokoban where boxes are on a goal position and the player needs to pull them away &#8230; <a href="https://gabor.heja.hu/blog/2018/05/16/bokosan-a-reverse-sokoban/" class="more-link"><span class="more-button">Continue reading &#62;<span class="screen-reader-text">Bokosan &#8211; a reverse Sokoban</span></span></a>]]></description>
								<content:encoded><![CDATA[<p><a href="https://gabor.heja.hu/blog/wp-content/uploads/2018/05/244.png"><img class="aligncenter size-full wp-image-361" src="https://gabor.heja.hu/blog/wp-content/uploads/2018/05/244.png" alt="" width="620" height="390" srcset="https://gabor.heja.hu/blog/wp-content/uploads/2018/05/244.png 620w, https://gabor.heja.hu/blog/wp-content/uploads/2018/05/244-300x189.png 300w" sizes="(max-width: 620px) 100vw, 620px" /></a></p>
<p>Bokosan is a reverse Sokoban where boxes are on a goal position and the player needs to pull them away while avoiding deadly spikes and falling into pits.</p>
<p>The game has 20 levels, single player mode, time challenge mode with online leaderboard, customizable player appearance, sound, 4 music tracks (6 in full version) and a few more features.</p>
<p>It is an entry for the <a href="http://js13kgames.com/">js13kgames</a> gamedev competition, where the goal is to create a web browser game using only JavaScript/CSS/HTML, with final size (after ZIP compression) below 13 kB, in a month. It was created for the <a href="http://2015.js13kgames.com/">2015</a> jam for the theme &#8220;reverse&#8221;.</p>
<p>This year I worked together with my brother, <a href="https://zsolt.heja.hu/">Zsolt</a>, he composed the music and created most of the levels.</p>
<p><a href="https://gheja.itch.io/bokosan">Play it here</a>.</p>
<h3>Graphics</h3>
<p>I&#8217;ve decided to go with raster graphics, stored in PNG with low color count and replacing colors in code if needed. I&#8217;ve tried several other methods to store graphics (putting each sprite in a string, etc.) but the overhead for decoding was just too big.</p>
<p><a href="https://gabor.heja.hu/blog/wp-content/uploads/2018/05/tileset.png"><img class="aligncenter size-full wp-image-350" src="https://gabor.heja.hu/blog/wp-content/uploads/2018/05/tileset.png" alt="" width="700" height="48" srcset="https://gabor.heja.hu/blog/wp-content/uploads/2018/05/tileset.png 700w, https://gabor.heja.hu/blog/wp-content/uploads/2018/05/tileset-300x21.png 300w" sizes="(max-width: 700px) 100vw, 700px" /></a></p>
<p style="text-align: center;">(<a href="https://gabor.heja.hu/blog/wp-content/uploads/2018/05/tileset.pn%67" target="_blank" rel="noopener noreferrer">The image is scaled down, click here for full version.</a>)</p>
<p>The final PNG contains the <a href="www.dafont.com/joystix.font">Jostix</a> font by <a href="http://twitter.com/typodermic">Ray Larabie</a> (only uppercase letters, numbers, punctuation marks), 13 tiles, 9 character animation frames and 3 other graphics; it is 700&#215;48 pixels, 20 colors and the final size is 1600 bytes after <a href="https://tinypng.com">TinyPNG compression</a>.</p>
<p>It might worth to note that for character animation I used oldschool tricks of mirroring and rotating to reduce the frames. I have only drawn the standing and step with right foot frames and then &#8220;stand, step, stand, mirrored step&#8221; sequence. Also, I just rotated the &#8220;walking up&#8221; frames for the &#8220;walking right&#8221;, as well as the &#8220;walking down&#8221; for &#8220;walking left&#8221;. For pulling I did the same but used one frame only and played the sounds of steps.</p>
<p><a href="https://gabor.heja.hu/blog/wp-content/uploads/2018/05/243.png"><img class="aligncenter size-full wp-image-360" src="https://gabor.heja.hu/blog/wp-content/uploads/2018/05/243.png" alt="" width="281" height="126" /></a></p>
<p>The colors of customizable parts of the character (hard hat, shirt, pants) are replaced during render (and not on the sprite sheet) as each entry in the challenge leaderboard can be different.</p>
<p>Quick note: when opening an HTML from local disk and accessing its contents in JavaScript, like reading pixels of an image (rather than putting in DOM and showing to user, or using drawImage()) the <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS">Cross Origin protection</a> kicks in and prevents the operation. Although when opening from a web server there is no such problem, this feature <a href="https://stackoverflow.com/questions/37768518/image-from-origin-file-has-been-blocked-from-loading-by-cross-origin-resour">can be disabled</a> but it&#8217;s strongly not recommended. Worth to note that JS engines track the data they handle so reading a canvas where a drawImage() copied a portion of a protected image will result in <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_enabled_image">an error as well</a>.</p>
<h3>Sound effects</h3>
<p>For the sound effect we used <a href="https://github.com/mneubrand/jsfxr">jsfxr</a> to generate them.</p>
<p>I have added a few minor optimizations for jsfxr before compressing the source via Closure Compiler, and after that manually edited the compressed code to be more suitable for ZIP.</p>
<h3>Music</h3>
<p>The music was created entirely by my brother, Zsolt.</p>
<p>I have created a small tracker that implements the basic features of an <a href="https://en.wikipedia.org/wiki/XM_(file_format)">XM</a>, handles only one pattern and loops it, but also have a map that describes which channels should be played on which loop (and also an option for intro that will be only played once at the beginning) &#8211; this way a song can be made up of a short period of music but can be dynamic and changing on each loop. Also created a converter that reads an XM file and creates the JavaScript arrays for the player, so Zsolt could use <a href="https://milkytracker.titandemo.org/">MilkyTracker</a> to compose.</p>
<p>The samples were produced using jsfxr just like the sound effects (they are actually in the same array) and are altered on the fly for player needs (changing playback speeds to create the notes).</p>
<p><a href="https://gabor.heja.hu/blog/wp-content/uploads/2018/05/242.png"><img class="aligncenter size-full wp-image-359" src="https://gabor.heja.hu/blog/wp-content/uploads/2018/05/242.png" alt="" width="921" height="131" srcset="https://gabor.heja.hu/blog/wp-content/uploads/2018/05/242.png 921w, https://gabor.heja.hu/blog/wp-content/uploads/2018/05/242-300x43.png 300w, https://gabor.heja.hu/blog/wp-content/uploads/2018/05/242-768x109.png 768w" sizes="(max-width: 799px) 94vw, (max-width: 1023px) 67vw, (max-width: 1365px) 62vw, 1037" /></a></p>
<p>I have also created a <a href="https://gheja.itch.io/bokosan-music">frontend for playback</a> later, check it out.</p>
<h3>Level design</h3>
<p>The levels were by designed mostly by Zsolt, with a few exceptions.</p>
<p>They are stored as simple strings &#8211; ZIP is really good at compressing text.</p>
<p>The levels are locked by default. Only the already solved and the next two unsolved are unlocked, as an attempt to prevent the player from getting stuck on one level.</p>
<p><a href="https://gabor.heja.hu/blog/wp-content/uploads/2018/05/241.png"><img class="aligncenter size-full wp-image-358" src="https://gabor.heja.hu/blog/wp-content/uploads/2018/05/241.png" alt="" width="633" height="227" srcset="https://gabor.heja.hu/blog/wp-content/uploads/2018/05/241.png 633w, https://gabor.heja.hu/blog/wp-content/uploads/2018/05/241-300x108.png 300w" sizes="(max-width: 633px) 100vw, 633px" /></a></p>
<p>And as the steps are counted for each level it is just natural that achievement hunters appear, so we added an &#8220;A+&#8221; badge for them for completing the level with at most the minimal steps we completed them during development, displayed in the level selector.</p>
<h3>Challenge mode</h3>
<p>There is a challenge mode with online leaderboard showing the name, time and character profile picture for each record.</p>
<p>There are 6 challenges, each consist of 3-4 already completed levels, the player plays them one after the other and the time is recorded and shown on the leaderboard.</p>
<h3>Statistics</h3>
<p>The game collects statistics about each play: play time, moves made, pulls done, levels started, levels finished.</p>
<p>The statistics is stored on the server side, the games recevies the current snapshot of the statistics and the one from a minute before, and then interpolates them during the next minute &#8211; this makes the display much more active.</p>
<p>I loved to see the numbers rolling during the vote period, unfortunately the statistics are no longer available on the jam website. [Although I made a few screenshots, but I could not find them yet.]</p>
<p><em>Update: since then I abandoned the domain, the server and uploaded the game to <a href="https://gheja.itch.io/bokosan">itch.io</a> so there is no server to handle the statistics.</em></p>
<h3>Server side</h3>
<p>The server keeps track of challenge leaderboards and statistics. It does not do anything more, really.</p>
<p><em>Update: I am no longer running the server, check last section.</em></p>
<h3>Compression</h3>
<p>I basically go with the idea of writing readable code and leaving the low level optimization and compression to the tools.</p>
<p>Most of the time this works just fine as for a really simplified example&#8230;</p>
<pre>if (x == 3)
{
    return true;
}

return false;
</pre>
<p>&#8230; is exactly&#8230;</p>
<pre>return x == 3;
</pre>
<p>&#8230; but the former can be read more easily and the tool will rewrite it to the latter anyways.</p>
<p>For code compression I used <a href="https://developers.google.com/closure/compiler/">Google Closure Compiler</a> in <a href="https://developers.google.com/closure/compiler/docs/compilation_levels">two phases</a>: first is the advanced optimization without whitespace removal, then the whitespace removal. This leaves a chance for (easier) manual/scripted optimization to be more suitable for the next tool (ZIP in this case).</p>
<p>The spritesheet is compressed using <a href="https://tinypng.com">TinyPNG</a>.</p>
<p>I used as few files in the compiled version as possible, as each file adds at least 130 bytes to the final ZIP file &#8211; this can be a lot and it increases with file name length.</p>
<p>For the final ZIP file the rules dictate to be a standard ZIP file, so no 7zip or other advanced formats are permitted.</p>
<p>To optimize the final ZIP I used AdvanceCOMP&#8217;s <a href="https://linux.die.net/man/1/advzip">AdvZIP</a> which basically recompresses an existing ZIP file but keeps compatibility.</p>
<p>About 90% of the build process &#8211; starting from readable code to entry ZIP &#8211; was automated in a <a href="https://github.com/gheja/bokosan/blob/master/build.sh">build script</a>. This way I could track the effect of each changes on the final size. The rest was manual editing of code just before Closure Compiler whitespace removal.</p>
<p>Fun fact: the last ZIP before the submitted one was 13313 bytes, 1 byte over the limit. I saved that byte by changing the player name input from &#8220;Player name:&#8221; to &#8220;New name:&#8221;.</p>
<h3>Final thoughts</h3>
<p>I think Bokosan is the most polished game I&#8217;ve created until today (alone or in a team), it has the most content and feels the most complete. It also got a lot of positive feedbacks and it was really fun to catch my collagues playing it, trying to get the &#8220;A+&#8221; badge on all levels.</p>
<p>However, the game has not ranked in the Top 10 in Desktop nor Mobile category, in Server category it finished at 6th place and in Facebook special on 3rd place.</p>
<p>A huge thank you to <a href="http://twitter.com/end3r">Andrzej Mazur</a> for organizing <a href="http://js13kgames.com/">js13kgames</a> competition once again, <a href="http://twitter.com/typodermic">Ray Larabie</a> for the <a href="www.dafont.com/joystix.font">Jostix</a> font, and <a href="http://twitter.com/mneubrand">Markus Neubrand</a> for <a href="https://github.com/mneubrand/jsfxr">Jsfxr</a>!</p>
]]></content:encoded>
							<wfw:commentRss>https://gabor.heja.hu/blog/2018/05/16/bokosan-a-reverse-sokoban/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
							</item>
		<item>
		<title>untitled13</title>
		<link>https://gabor.heja.hu/blog/2018/04/25/untitled13/</link>
				<comments>https://gabor.heja.hu/blog/2018/04/25/untitled13/#respond</comments>
				<pubDate>Wed, 25 Apr 2018 19:49:41 +0000</pubDate>
		<dc:creator><![CDATA[Gabor Heja]]></dc:creator>
				<category><![CDATA[Dev]]></category>
		<category><![CDATA[Gamedev]]></category>
		<category><![CDATA[gamedev]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[js]]></category>
		<category><![CDATA[js13k]]></category>
		<category><![CDATA[js13kgames]]></category>
		<category><![CDATA[multiplayer]]></category>
		<category><![CDATA[untitled13]]></category>

		<guid isPermaLink="false">https://gabor.heja.hu/blog/?p=318</guid>
				<description><![CDATA[In 2012 I found the js13kgames&#160;game development competition where the goal is to submit a HTML-CSS-Javascript game which is not &#8230; <a href="https://gabor.heja.hu/blog/2018/04/25/untitled13/" class="more-link"><span class="more-button">Continue reading &#62;<span class="screen-reader-text">untitled13</span></span></a>]]></description>
								<content:encoded><![CDATA[<p>In 2012 I found the <a href="http://2012.js13kgames.com/">js13kgames</a>&nbsp;game development competition where the goal is to submit a HTML-CSS-Javascript game which is not more than 13 kB (13312 bytes) ZIPed, developed between 13th August and 13th September. It is also required to publish the uncompressed game code on GitHub &#8211; this way one can prove they created the game and more importantly: others can learn from it.</p>
<p>This last part was what got me, I was really curious how others fit games in such a small space &#8211; what later turned out to be much more than it looked on the first sight.</p>
<p>As the competition was already halfway or was over when I found it (I can&#8217;t really recall) I could only examine others&#8217; entries and decide I will participate next year, in 2013. I also tried to gather some ideas and set up a concept before the competition start date &#8211; strictly not a line of code was written.</p>
<p>There is a theme each year (which is optional but worth bonus points if honored), for 2013 it was bad luck &#8211; and I skipped it entirerily&#8230;</p>
<p>Okay and now about my entry.</p>
<p>My concept was to create a 2 player networked tower defense/tower attack game.</p>
<p>I must admit I am really bad at naming things, I just called the main (and only) client side file 13312.js and not long before the deadline I came up with the exceptionally uncreative name <a href="http://js13kgames.com/entries/untitled13">untitled13</a>.</p>
<p>Of course to fit in 13 kB entries usually do not use existing engines, they are written from scratch &#8211; a thing I don&#8217;t mind at all, I&#8217;ve been always curious about the details and liked the control one has when starts from the ground up.</p>
<p><strong>For graphics and animations&nbsp;</strong>I decided to go with a <a href="https://github.com/gheja/untitled13/blob/js13kgames-2013-entry/src/13312.js#L86">fixed color palette</a> and dynamically generated content. All the objects consist of one or more layers rendered by <a href="https://github.com/gheja/untitled13/blob/js13kgames-2013-entry/src/13312.js#L1092">a function</a> that takes a string that describes a series of commands and their parameters. It turned out only two commands were necessary, a &#8220;draw a closed polygon filled with a color&#8221; and a &#8220;draw an alpha overlay&#8221; for shadows. The parameters were simply X and Y coordinates of the points, represented as characters in the base64 character set (64 distinct values), multiplied by the size of sprite. (For example: sprite size is 64&#215;32, string is &#8220;AAAzjj&#8221;, therefore the coordinates are 0;0, 0;25, 35;17).</p>
<p><strong>The map</strong> consists of starting points for the attacker, paths for the units to walk on, path intersections with switches, a few objective tiles for crystals the defender has to defend and concrete blocks to build the towers upon. There are <a href="https://github.com/gheja/untitled13/blob/js13kgames-2013-entry/src/13312.js#L142">three types of monsters</a> with different parameters (speed, health) and <a href="https://github.com/gheja/untitled13/blob/js13kgames-2013-entry/src/13312.js#L314">three towers</a> with different parameters (attack type, damage).</p>
<p><strong>The networking</strong> approach is pretty simple but the most interesting part: the initial state is known to both players (the map is fixed in the entry version, in later versions it is randomized but the seed is shared), the mechanics are known as well and the actions can be described easily.&nbsp;So I came up with the idea to run a whole game instance for both players, create delayed action queues for them and synchronize them. When one player clicks a button the action is <a href="https://github.com/gheja/untitled13/blob/js13kgames-2013-entry/src/13312.js#L2494">added to their queue</a> with the <a href="https://github.com/gheja/untitled13/blob/js13kgames-2013-entry/src/13312.js#L2482">configured delay</a>, sent to the other player&#8217;s game, and a bit later&nbsp;<a href="https://github.com/gheja/untitled13/blob/js13kgames-2013-entry/src/13312.js#L2511">executed</a>&nbsp;on <em>both</em>&nbsp;instances <em>at the same moment</em>.</p>
<p>To get this working I needed reliable advancement of simulation steps, one that is not affected by skipped frames. I created a <em>tick()</em>&nbsp;method that steps the simulation by exactly one time slice (one <em>simulation step</em>), so when a <a href="https://github.com/gheja/untitled13/blob/js13kgames-2013-entry/src/13312.js#L2417">frame is requested</a> by the browser I calculate how many tick should have happened up until then since the last frame and call <em>tick()</em> that many times. (I usually go with this method to keep games frame rate independent &#8211; skipped frames are less worse than game slowing down.)</p>
<p>As both players are running the same game (only playing different sides), are aware of every details (only hidden from them), all events are happening at the exact same time, the game should play out identically for both of them. Later I learned this is called&nbsp;<em>simultaneous simulation</em>&nbsp;and it is the core of great games like <a href="http://www.gamasutra.com/view/feature/3094/1500_archers_on_a_288_network_.php">Age of Empires, Starcraft and a lot more</a>.</p>
<p>In this form however there is a serious flaw. There is no guarantee that the actions will reach the other player before they are due, it is blindly trusted that it will. For example: Let the action queue delay be 10 frames. The defender clicks to build a tower at frame #100, this will be executed at frame #110. If the network lag is say 7 frames then the other player gets the notification at frame #107, still 3 frames before execution, the tower gets built at frame #110 in both instances and shoots the attacker just before it reaches the crystal. But if the network lag is more than the queue delay, say 13 frames then at frame #110 the tower gets built in one instance, then the notification arrives at frame #113 for the other player, tower gets built at frame #113 when it&#8217;s already too late, the crystal was reached and the game is over. In the defender&#8217;s instance the crystal is saved, in the attacker&#8217;s the crystal is destroyed &#8211; the game is out of sync.</p>
<p>To address this the other instance must be notified about no action as well periodically to make sure nothing is missed. And when there is no info, the game should pause and wait until it arrives. This is a feature I had no time to think through and implement before submitting my entry.</p>
<p><strong>The <a href="https://github.com/gheja/untitled13/blob/js13kgames-2013-entry/src/server.js">server part</a></strong> is running in node.js (my very first application) and it is very basic, it just matches the players by their IDs and passes the messages between them.</p>
<p>Random minor thoughts: The map is a hexagonal grid, which made <a href="https://github.com/gheja/untitled13/blob/js13kgames-2013-entry/src/13312.js#L134">finding neighbour tiles</a> for <a href="https://github.com/gheja/untitled13/blob/js13kgames-2013-entry/src/13312.js#L1966">concrete block</a> and <a href="https://github.com/gheja/untitled13/blob/js13kgames-2013-entry/src/13312.js#L1617">fog calculation</a> ugly, but it looks so much better than a square grid &#8211; although it is handled like that. The map has fog to hide tiles that are too far away from units/towers. The UI is usable but confusing and unhelpful at first.</p>
<p>The project is on <a href="https://github.com/gheja/untitled13/">GitHub</a>, the version I submitted is in the&nbsp;<a href="https://github.com/gheja/untitled13/tree/js13kgames-2013-entry">js13kgames-2013-entry</a>&nbsp;branch. I like to commit often (and to have descriptive commit messages) so the submitted version had 381 commits, but for some reason I have not uploaded the final ZIP file, it was around <strong>11255 bytes</strong> of 13312, so I used up 84.5% of the limit.</p>
<p>The game was better received than I expected, it ranked <strong>1st place in Server category</strong> and&nbsp;<strong>9th place in Desktop category</strong>.</p>
<p>The competition had awesome prizes including various licenses for game engines and gamedev related services, ebooks, physical books and vouchers. It was organized by <a href="http://twitter.com/end3r">Andrzej Mazur</a> who tirelessly did an epic job, I&#8217;d like to thank him again for organizing and hosting this event, finding sponsors, arranging prizes and of course organizing the event annually since then, so <em>thank you Andrzej!</em></p>
<p>Check out <a href="http://js13kgames.com">js13kgames.com</a> for previous and upcoming competitions.</p>
]]></content:encoded>
							<wfw:commentRss>https://gabor.heja.hu/blog/2018/04/25/untitled13/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
							</item>
		<item>
		<title>101hero troubleshooting: skewed print</title>
		<link>https://gabor.heja.hu/blog/2017/06/12/101hero-troubleshooting-skewed-print/</link>
				<comments>https://gabor.heja.hu/blog/2017/06/12/101hero-troubleshooting-skewed-print/#comments</comments>
				<pubDate>Sun, 11 Jun 2017 22:38:14 +0000</pubDate>
		<dc:creator><![CDATA[Gabor Heja]]></dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[101hero]]></category>
		<category><![CDATA[3d printer]]></category>
		<category><![CDATA[maker]]></category>

		<guid isPermaLink="false">https://gabor.heja.hu/blog/?p=293</guid>
				<description><![CDATA[I&#8217;d like to cover the most common issues faced during first prints using a 101hero. One of the most common &#8230; <a href="https://gabor.heja.hu/blog/2017/06/12/101hero-troubleshooting-skewed-print/" class="more-link"><span class="more-button">Continue reading &#62;<span class="screen-reader-text">101hero troubleshooting: skewed print</span></span></a>]]></description>
								<content:encoded><![CDATA[<p>I&#8217;d like to cover the most common issues faced during first prints using a 101hero.</p>
<p>One of the most common is the skewed print, like in the following images.</p>
<p><figure id="attachment_295" aria-describedby="caption-attachment-295" style="width: 550px" class="wp-caption aligncenter"><a href="https://gabor.heja.hu/blog/wp-content/uploads/2017/06/18879852_1484877178243338_5777776420499280579_o_1920.jpg"><img class="size-large wp-image-295" src="https://gabor.heja.hu/blog/wp-content/uploads/2017/06/18879852_1484877178243338_5777776420499280579_o_1920-1024x400.jpg" alt="" width="550" height="215" srcset="https://gabor.heja.hu/blog/wp-content/uploads/2017/06/18879852_1484877178243338_5777776420499280579_o_1920-1024x400.jpg 1024w, https://gabor.heja.hu/blog/wp-content/uploads/2017/06/18879852_1484877178243338_5777776420499280579_o_1920-300x117.jpg 300w, https://gabor.heja.hu/blog/wp-content/uploads/2017/06/18879852_1484877178243338_5777776420499280579_o_1920-768x300.jpg 768w, https://gabor.heja.hu/blog/wp-content/uploads/2017/06/18879852_1484877178243338_5777776420499280579_o_1920.jpg 1765w" sizes="(max-width: 550px) 100vw, 550px" /></a><figcaption id="caption-attachment-295" class="wp-caption-text">Photo by Bogdan Bóg Anczarski <a href="https://www.facebook.com/groups/101herounofficial/permalink/1336680633076164/">posted</a> in <a href="https://www.facebook.com/groups/101herounofficial/">101 Hero (Unofficial)</a> Facebook Group.</figcaption></figure></p>
<p>This is most likely because of the stepper motors skipping steps. The problem can be traced back to one of two reasons: a belt touching the pylon or too high printing speed. Fortunately both problems can be fixed easily.</p>
<p><strong>First check all your belts during a print.</strong> See if any of them is touching the plastic of the pylon, rubbing against it. If yes then check the pulley/carriage attached to the belt, find where the belt is fixed to it. Try to push the belt a little to the front or the back. If this does not help you can also try to loosen the screw holding the bearing in place on the top of the tower a bit.</p>
<p>If you see no problem with the belts it can be a problem with the stepper motors moving the belts. If the printing speed is too high then the motors tend to skip steps. The 101hero is not a fast printer, the stock motors support up to 14 mm/s printing and travelling. Unfortunately in some cases this speed is still too high for the motors even with stock motors and stock GCode files (from 101land.com).</p>
<p><strong>If you have the model</strong> you should try to reduce the speed of the printing by configuring the printing as well as travelling speeds to no more than 14 mm/s, maybe down to 10 mm/s.</p>
<p><strong>If you do not have the model but only the GCode file</strong> (the ones that can be downloaded from 101land.com) you need to add a command to reduce the printing speed to a certain percent. Open the &#8220;101hero&#8221; file on your SD card with a text editor, find the line starting with G28 (<a href="http://reprap.org/wiki/G-code#G28:_Move_to_Origin_.28Home.29">&#8220;Move to origin/home&#8221;</a>), and add a line right after that stating &#8220;M220 S75&#8221;. This command is <a href="http://reprap.org/wiki/G-code#M220:_Set_speed_factor_override_percentage">&#8220;Set speed factor override percentage&#8221;</a> and the parameter is the percentage (14 mm/s * 75% = 10.5 mm/s).</p>
<p>If printing with these settings fixes the problem then you should try to use higher printing speeds (or higher percentage) to see what is the speed your printer is still reliable at.</p>
<p>If you still have the problem leave a comment here or post in the <a href="https://www.facebook.com/groups/101herounofficial/">101 Hero (Unofficial)</a> Facebook group.</p>
]]></content:encoded>
							<wfw:commentRss>https://gabor.heja.hu/blog/2017/06/12/101hero-troubleshooting-skewed-print/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
							</item>
		<item>
		<title>101hero: Assembling the printer</title>
		<link>https://gabor.heja.hu/blog/2017/03/16/101hero-assembling-the-printer/</link>
				<comments>https://gabor.heja.hu/blog/2017/03/16/101hero-assembling-the-printer/#comments</comments>
				<pubDate>Thu, 16 Mar 2017 01:21:40 +0000</pubDate>
		<dc:creator><![CDATA[Gabor Heja]]></dc:creator>
				<category><![CDATA[101hero]]></category>
		<category><![CDATA[Blog]]></category>
		<category><![CDATA[Dev]]></category>
		<category><![CDATA[3d printer]]></category>
		<category><![CDATA[maker]]></category>

		<guid isPermaLink="false">http://gabor.heja.hu/blog/?p=235</guid>
				<description><![CDATA[The&#160;first part&#160;covers the short story of the printer, the contents of the box, a few tips and warnings about the &#8230; <a href="https://gabor.heja.hu/blog/2017/03/16/101hero-assembling-the-printer/" class="more-link"><span class="more-button">Continue reading &#62;<span class="screen-reader-text">101hero: Assembling the printer</span></span></a>]]></description>
								<content:encoded><![CDATA[<p><a href="/asdf">The&nbsp;first part</a>&nbsp;covers the short story of the printer, the contents of the box, a few tips and warnings about the ways you might damage your printer unintentionally (if you have not read these you should).</p>
<p>This part is about assembling the printer&nbsp;step by step.</p>
<p>First&nbsp;connect only the PSU to the controller and switch it ON and OFF, the red LED should light up when it is ON.</p>
<p>Now connect the&nbsp;motors and end-stops to the controller (do not assemble anything yet) and attach the SD card slot. The intended order of the pylons as well as the motor-end-stop pairings are below:</p>
<p><a href="http://gabor.heja.hu/blog/wp-content/uploads/2017/03/qeXV74rV__20170130_001931_1.jpg"><img class="aligncenter size-large wp-image-228" src="http://gabor.heja.hu/blog/wp-content/uploads/2017/03/qeXV74rV__20170130_001931_1-1024x466.jpg" alt="" width="550" height="250" srcset="https://gabor.heja.hu/blog/wp-content/uploads/2017/03/qeXV74rV__20170130_001931_1-1024x466.jpg 1024w, https://gabor.heja.hu/blog/wp-content/uploads/2017/03/qeXV74rV__20170130_001931_1-300x137.jpg 300w, https://gabor.heja.hu/blog/wp-content/uploads/2017/03/qeXV74rV__20170130_001931_1-768x350.jpg 768w" sizes="(max-width: 550px) 100vw, 550px" /></a></p>
<p><a href="http://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3250_1_1920.jpg"><img class="aligncenter size-large wp-image-230" src="http://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3250_1_1920-1024x551.jpg" alt="" width="550" height="296" srcset="https://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3250_1_1920-1024x551.jpg 1024w, https://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3250_1_1920-300x161.jpg 300w, https://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3250_1_1920-768x413.jpg 768w, https://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3250_1_1920.jpg 1920w" sizes="(max-width: 550px) 100vw, 550px" /></a></p>
<p>After connecting these&nbsp;put a simple <a href="https://github.com/gheja/101hero/tree/master/g-code/01_go_home">&#8220;go home&#8221; file</a> on your SD card and then power it up. The motors should start moving the carriages (all three at the same time) until one hits its end-stop. Then the other two should stop and only that should move&nbsp;back and forth triggering the end-stop a few times. After this the remaining two should do the same one by one.</p>
<p>If all three carriages hit the end-stops then congrats, you have working motors and end-stops, this is a good start! If not&#8230; well then you are in the unlucky group of&nbsp;quite a few people, including me. In this case check out the&nbsp;<a href="https://www.facebook.com/groups/101herounofficial/1268890269855201/">101 Hero (Unofficial) Facebook group</a>.</p>
<p>You can restart the procedure by pushing the reset button or power cycling the controller board.</p>
<p>Now on to&nbsp;assembling the printer!</p>
<p>At the top of the pylon arrange the red-black cable of the end-stop so it goes through the shaft:</p>
<p><a href="http://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3260_1_1920-1.jpg"><img class="aligncenter size-large wp-image-239" src="http://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3260_1_1920-1-1024x576.jpg" alt="" width="550" height="309" srcset="https://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3260_1_1920-1-1024x576.jpg 1024w, https://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3260_1_1920-1-300x169.jpg 300w, https://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3260_1_1920-1-768x432.jpg 768w, https://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3260_1_1920-1.jpg 1920w" sizes="(max-width: 550px) 100vw, 550px" /></a></p>
<p>At the bottom of the pylon the red-black cable should go through the shaft just like above and arrange the&nbsp;cables of&nbsp;the motor like on the picture:</p>
<p><a href="http://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3253_1_1920.jpg"><img class="aligncenter size-large wp-image-237" src="http://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3253_1_1920-1024x576.jpg" alt="" width="550" height="309" srcset="https://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3253_1_1920-1024x576.jpg 1024w, https://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3253_1_1920-300x169.jpg 300w, https://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3253_1_1920-768x432.jpg 768w, https://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3253_1_1920.jpg 1920w" sizes="(max-width: 550px) 100vw, 550px" /></a></p>
<p>Make sure for&nbsp;each pylons that the spring on the belt is&nbsp;tight to provide tension&#8230;</p>
<p><a href="http://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3285_1920.jpg"><img class="aligncenter size-large wp-image-241" src="http://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3285_1920-1024x576.jpg" alt="" width="550" height="309" srcset="https://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3285_1920-1024x576.jpg 1024w, https://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3285_1920-300x169.jpg 300w, https://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3285_1920-768x432.jpg 768w, https://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3285_1920.jpg 1920w" sizes="(max-width: 550px) 100vw, 550px" /></a></p>
<p>&#8230; the belts are properly on the wheel at the top&#8230;</p>
<p><a href="http://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3289_1_1920.jpg"><img class="aligncenter size-large wp-image-242" src="http://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3289_1_1920-1024x576.jpg" alt="" width="550" height="309" srcset="https://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3289_1_1920-1024x576.jpg 1024w, https://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3289_1_1920-300x169.jpg 300w, https://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3289_1_1920-768x432.jpg 768w, https://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3289_1_1920.jpg 1920w" sizes="(max-width: 550px) 100vw, 550px" /></a>&#8230; and the bottom:</p>
<p><a href="http://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3295_1_1920.jpg"><img class="aligncenter size-large wp-image-244" src="http://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3295_1_1920-1024x576.jpg" alt="" width="550" height="309" srcset="https://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3295_1_1920-1024x576.jpg 1024w, https://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3295_1_1920-300x169.jpg 300w, https://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3295_1_1920-768x432.jpg 768w, https://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3295_1_1920.jpg 1920w" sizes="(max-width: 550px) 100vw, 550px" /></a></p>
<p>&nbsp;</p>
<p>If the belts are off the wheels try to put them back carefully. If&nbsp;that is not enough then you can loosen&nbsp;the motor a bit by loosening the two screws holding it in place (3mm Hex Socket aka Allan heads). If you have to remove the belt tensioner spring here&#8217;s a <a href="https://youtu.be/PO-0IJuC9UM">video how to&nbsp;put&nbsp;it back</a>.</p>
<p>Next, assemble the bottom plate and the three pylons (4 pcs of&nbsp;M4x12 self-tapping screws (PH1 head) for each pylon):</p>
<p><a href="http://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3256_1_1920.jpg"><img class="aligncenter size-large wp-image-246" src="http://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3256_1_1920-948x1024.jpg" alt="" width="550" height="594" srcset="https://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3256_1_1920-948x1024.jpg 948w, https://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3256_1_1920-278x300.jpg 278w, https://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3256_1_1920-768x830.jpg 768w, https://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3256_1_1920.jpg 1920w" sizes="(max-width: 550px) 100vw, 550px" /></a></p>
<p>And then add the top plate (2 pcs of&nbsp;M4x12 self-tapping screws (PH1 head) for each pylon, NOTE: the two inner holes remain empty on&nbsp;each end on the top):</p>
<p><a href="http://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3266_1_1920.jpg"><img class="aligncenter size-large wp-image-247" src="http://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3266_1_1920-1024x605.jpg" alt="" width="550" height="325" srcset="https://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3266_1_1920-1024x605.jpg 1024w, https://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3266_1_1920-300x177.jpg 300w, https://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3266_1_1920-768x454.jpg 768w, https://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3266_1_1920.jpg 1920w" sizes="(max-width: 550px) 100vw, 550px" /></a></p>
<p>And now fix the effector&nbsp;to the rods. The facing of the head&nbsp;does not matter but it can be useful&nbsp;to see the fan (to see if it is working) and the door (for change filament) from the front. The first screw&#8230;</p>
<p><a href="http://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3271_1_1920.jpg"><img class="aligncenter size-large wp-image-251" src="http://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3271_1_1920-1024x576.jpg" alt="" width="550" height="309" srcset="https://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3271_1_1920-1024x576.jpg 1024w, https://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3271_1_1920-300x169.jpg 300w, https://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3271_1_1920-768x432.jpg 768w, https://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3271_1_1920.jpg 1920w" sizes="(max-width: 550px) 100vw, 550px" /></a></p>
<p>&#8230; the second screw (you can safely leave it hanging)&#8230;</p>
<p><a href="http://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3274_1_1920.jpg"><img class="aligncenter size-large wp-image-250" src="http://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3274_1_1920-1024x576.jpg" alt="" width="550" height="309" srcset="https://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3274_1_1920-1024x576.jpg 1024w, https://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3274_1_1920-300x169.jpg 300w, https://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3274_1_1920-768x432.jpg 768w, https://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3274_1_1920.jpg 1920w" sizes="(max-width: 550px) 100vw, 550px" /></a></p>
<p>&#8230; and the rest:</p>
<p><a href="http://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3280_1_1920.jpg"><img class="aligncenter size-large wp-image-249" src="http://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3280_1_1920-1024x576.jpg" alt="" width="550" height="309" srcset="https://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3280_1_1920-1024x576.jpg 1024w, https://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3280_1_1920-300x169.jpg 300w, https://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3280_1_1920-768x432.jpg 768w, https://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3280_1_1920.jpg 1920w" sizes="(max-width: 550px) 100vw, 550px" /></a></p>
<p>Now stick some painter&#8217;s tape to the glass plate (you don&#8217;t need to cover the whole glass&nbsp;just make sure you have no overlaps of the tape) put it on the bottom plate and fix it in place using the paper clips like this:</p>
<p><a href="http://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3334_1_1920.jpg"><img class="aligncenter size-large wp-image-253" src="http://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3334_1_1920-1024x576.jpg" alt="" width="550" height="309" srcset="https://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3334_1_1920-1024x576.jpg 1024w, https://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3334_1_1920-300x169.jpg 300w, https://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3334_1_1920-768x432.jpg 768w, https://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3334_1_1920.jpg 1920w" sizes="(max-width: 550px) 100vw, 550px" /></a></p>
<p>Now connect the rest of the cables&nbsp;to the controller box:</p>
<p><a href="http://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3411_2_1920.jpg"><img class="aligncenter size-large wp-image-255" src="http://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3411_2_1920-1024x576.jpg" alt="" width="550" height="309" srcset="https://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3411_2_1920-1024x576.jpg 1024w, https://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3411_2_1920-300x169.jpg 300w, https://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3411_2_1920-768x432.jpg 768w, https://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3411_2_1920.jpg 1920w" sizes="(max-width: 550px) 100vw, 550px" /></a></p>
<p>Next step is to load the filament. The extruder assembly has a door you can open, this door pushes the filament to the extruder motor which is supplying the filament to the heated nozzle. Open up the door and put the filament in at the top of the extruder assembly, guide it into the tube after the motor, like this:</p>
<p><a href="http://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3310_1_1920.jpg"><img class="aligncenter size-large wp-image-258" src="http://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3310_1_1920-1024x1024.jpg" alt="" width="550" height="550" srcset="https://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3310_1_1920-1024x1024.jpg 1024w, https://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3310_1_1920-150x150.jpg 150w, https://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3310_1_1920-300x300.jpg 300w, https://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3310_1_1920-768x768.jpg 768w, https://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3310_1_1920.jpg 1920w" sizes="(max-width: 550px) 100vw, 550px" /></a></p>
<p>When completed close the door and tighten the screws (2 pcs M3x12&nbsp;self-tapping screws (PH1 head)).</p>
<p><a href="http://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3313_1_1920.jpg"><img class="aligncenter size-large wp-image-259" src="http://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3313_1_1920-1024x1024.jpg" alt="" width="550" height="550" srcset="https://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3313_1_1920-1024x1024.jpg 1024w, https://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3313_1_1920-150x150.jpg 150w, https://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3313_1_1920-300x300.jpg 300w, https://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3313_1_1920-768x768.jpg 768w, https://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3313_1_1920.jpg 1920w" sizes="(max-width: 550px) 100vw, 550px" /></a></p>
<p>After completing this step the printer should look like this:</p>
<p>
<a href='https://gabor.heja.hu/blog/2017/03/16/101hero-assembling-the-printer/img_3334_2_1920/'><img width="300" height="223" src="https://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3334_2_1920-300x223.jpg" class="attachment-medium size-medium" alt="" srcset="https://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3334_2_1920-300x223.jpg 300w, https://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3334_2_1920-768x571.jpg 768w, https://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3334_2_1920-1024x762.jpg 1024w, https://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3334_2_1920.jpg 1920w" sizes="100vw" /></a>
<a href='https://gabor.heja.hu/blog/2017/03/16/101hero-assembling-the-printer/img_3332_1_1920/'><img width="300" height="220" src="https://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3332_1_1920-300x220.jpg" class="attachment-medium size-medium" alt="" srcset="https://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3332_1_1920-300x220.jpg 300w, https://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3332_1_1920-768x562.jpg 768w, https://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3332_1_1920-1024x749.jpg 1024w, https://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3332_1_1920.jpg 1920w" sizes="100vw" /></a>
</p>
<p>Which&nbsp;is a mess. The cable management is pretty much non-existent for the 101hero by default. People use different cable organizing methods, the most popular is the <a href="https://www.google.hu/search?q=spiral+wrap+cable+organizer">spiral wrap</a>&nbsp;but personally I like the zip tie method which is nothing special just zip tying the cables at a few centimeters interval, like this:</p>
<p>
<a href='https://gabor.heja.hu/blog/2017/03/16/101hero-assembling-the-printer/img_3336_1_1920/'><img width="300" height="289" src="https://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3336_1_1920-300x289.jpg" class="attachment-medium size-medium" alt="" srcset="https://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3336_1_1920-300x289.jpg 300w, https://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3336_1_1920-768x741.jpg 768w, https://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3336_1_1920-1024x988.jpg 1024w, https://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3336_1_1920.jpg 1920w" sizes="100vw" /></a>
<a href='https://gabor.heja.hu/blog/2017/03/16/101hero-assembling-the-printer/img_3339_1_1920/'><img width="209" height="300" src="https://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3339_1_1920-209x300.jpg" class="attachment-medium size-medium" alt="" srcset="https://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3339_1_1920-209x300.jpg 209w, https://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3339_1_1920-768x1102.jpg 768w, https://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3339_1_1920-714x1024.jpg 714w, https://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3339_1_1920.jpg 1338w" sizes="100vw" /></a>
</p>
<p>After completing this step you should cut the ties short, of course. One more thing I did was fixing the bunch of extruder cable to the top plate to an unused hole like this:</p>
<p>
<a href='https://gabor.heja.hu/blog/2017/03/16/101hero-assembling-the-printer/img_3351_1_1920/'><img width="300" height="231" src="https://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3351_1_1920-300x231.jpg" class="attachment-medium size-medium" alt="" srcset="https://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3351_1_1920-300x231.jpg 300w, https://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3351_1_1920-768x592.jpg 768w, https://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3351_1_1920-1024x789.jpg 1024w, https://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3351_1_1920.jpg 1142w" sizes="100vw" /></a>
<a href='https://gabor.heja.hu/blog/2017/03/16/101hero-assembling-the-printer/img_3345_1_1920/'><img width="252" height="300" src="https://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3345_1_1920-252x300.jpg" class="attachment-medium size-medium" alt="" srcset="https://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3345_1_1920-252x300.jpg 252w, https://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3345_1_1920-768x914.jpg 768w, https://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3345_1_1920-860x1024.jpg 860w, https://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3345_1_1920.jpg 1613w" sizes="100vw" /></a>
</p>
<p>You need to leave about 25 cm (10 inches) of cable between the fixing point and the extruder assembly so the hot end can reach the farthest point safely.</p>
<p>And&nbsp;that&#8217;s all about assembling the printer. The next post will be about&nbsp;how to calibrate the 101hero.</p>
]]></content:encoded>
							<wfw:commentRss>https://gabor.heja.hu/blog/2017/03/16/101hero-assembling-the-printer/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
							</item>
		<item>
		<title>101hero: Hello</title>
		<link>https://gabor.heja.hu/blog/2017/03/16/101hero-hello/</link>
				<comments>https://gabor.heja.hu/blog/2017/03/16/101hero-hello/#respond</comments>
				<pubDate>Thu, 16 Mar 2017 00:58:24 +0000</pubDate>
		<dc:creator><![CDATA[Gabor Heja]]></dc:creator>
				<category><![CDATA[101hero]]></category>
		<category><![CDATA[Blog]]></category>
		<category><![CDATA[Dev]]></category>
		<category><![CDATA[3d printer]]></category>
		<category><![CDATA[maker]]></category>

		<guid isPermaLink="false">http://gabor.heja.hu/blog/?p=216</guid>
				<description><![CDATA[The 101hero is a pylon 3D printer that had a campaign on both IndieGoGo and Kickstarter with early bird prices of US$ 49 &#8230; <a href="https://gabor.heja.hu/blog/2017/03/16/101hero-hello/" class="more-link"><span class="more-button">Continue reading &#62;<span class="screen-reader-text">101hero: Hello</span></span></a>]]></description>
								<content:encoded><![CDATA[<p>The 101hero is a pylon 3D printer that had a campaign on both <a href="https://igg.me/at/101hero/x/4088343">IndieGoGo</a> and <a href="https://www.kickstarter.com/projects/101hero/101hero-the-world-first-us49-3d-printer">Kickstarter</a> with early bird prices of US$ 49 and retail price of US$ 99 on <a href="http://www.101hero.com/">101hero.com</a>.</p>
<p>First of all, the 101hero team&#8217;s goal was to create <em>&#8220;The World&#8217;s Most Affordable 3D Printer&#8221;</em> as they said and not the <em>the printer you should start your 3D printing adventures with</em> and this can be seen in a few places. Nevertheless, the printer is functional and the prints can turn out surprisingly nice if one is willing to spend enough time and don&#8217;t mind the hassle but if you&#8217;re new to 3D printing and you want a printer that works out-of-the-box, this one is not the best choice.</p>
<p>I backed the IndieGoGo run and have received the printer in January, 2017. I am new to 3D printing this is the first one I bought and the first one I ever printed with, In these posts I will share my experiences, tips I find useful and caveats.<br />
The printer comes in two versions: the CV (Consumer Version) and DV (Developer Version). The only difference is that the DV has a USB port and can be connected to a computer while the CV can only print from SD card (which is not part of the package).</p>
<p><a href="http://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3247_2_1920.jpg"><br />
</a><a href="http://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3247_2_1920_2.jpg"><img class="aligncenter size-large wp-image-229" src="http://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3247_2_1920_2-1024x466.jpg" alt="" width="550" height="250" srcset="https://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3247_2_1920_2-1024x466.jpg 1024w, https://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3247_2_1920_2-300x136.jpg 300w, https://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3247_2_1920_2-768x349.jpg 768w, https://gabor.heja.hu/blog/wp-content/uploads/2017/03/IMG_3247_2_1920_2.jpg 1920w" sizes="(max-width: 550px) 100vw, 550px" /></a></p>
<p>Assembling the 101hero is pretty straightforward but before getting started check all the components that should be in the box:</p>
<ul>
<li>3 pylon assembly &#8211; they are identical except for the length of the wires and labels, there should be an end-stop microswitch (to detect that the carriage reached the top of the pylon), two slide rods, a belt, a carriage (fixed to the belt, sliding on the two slide rods, fixed to effector rods) and a motor</li>
<li>1 controller box</li>
<li>1 SD card slot</li>
<li>2 plates &#8211; one for the base and one for the top</li>
<li>1 extruder assembly with effector &#8211; the &#8220;head&#8221;</li>
<li>1 bunch of filament &#8211; the printer comes with black or white, I bought the green separately</li>
<li>1 roll of painter&#8217;s tape &#8211; to help adhesion of the first layer printed</li>
<li>1 glass plate</li>
<li>3 paper clips &#8211; to fix the glass plate to the base</li>
<li>6 pcs M3x10 machine screws (PH1 head) &#8211; for the carriage-straw/strand and straw/strand-extruder assembly</li>
<li>18 pcs M4x12 self-tapping screws (PH1 head) &#8211; for pylon-base and pylon-top</li>
<li>2 pcs M3x12 self-tapping screws (PH1 head) &#8211; for door fixing on extruder assembly (identical to the ones used for end-stops in early editions)</li>
</ul>
<p>In early editions the end-stops are hitting against M3x12 self-tapping screws with PH1 heads, I have read that later they switched to Hex Socket (aka Allen) types.</p>
<p>Before assembling the printer I would like to tell you some basics and recommend to do further checks.</p>
<p>When the printer is powered it looks for a file called &#8220;101hero&#8221; on the SD card and starts to execute it (i.e. printing, moving, etc.). The file is a <a href="http://reprap.org/wiki/G-code">G-code</a> file and <em>has no extension</em>, it is just &#8220;101hero&#8221; (when saving the file using Notepad on Windows to prevent it appending &#8220;.txt&#8221; change the &#8220;File type&#8221; drop-down to &#8220;All files (*.*)&#8221; or put the file name between quotes).</p>
<p>The button on the controller box simply resets the printer that causes it to re-initialize and looking for the file and execute it.</p>
<p>The pylons are identical except for the cable lengths and the labels that are on them, that is you can have them swapped and even replaced from a different package, they will work. One important note however &#8211; the end-stops are used to detect when the carriages reach the top of the pylon so the motor-end-stop pairs need to be kept consistent, if they are not then the controller will keep moving the carriage until you turn the power off.</p>
<p><strong><em>A big warning:</em></strong> the power is not only sourced from the power connector but from USB as well &#8211; that is as long as your printer is connected over USB it will power the board &#8211; <em>regardless of the power switch!</em> Yeah, if you slide the switch to OFF the board will still be powered, although the printer will not operate properly due to the low current but you can damage the printer if you short something.</p>
<p><strong><em>One more big warning:</em></strong> the controller has no <a href="https://en.wikipedia.org/wiki/Flyback_diode">flyback diodes</a> to protect itself from <a href="https://youtu.be/LXGtE3X2k7Y">inductive spikes</a>. Simply put: only move the carriage slowly by hand because the motor generates current that will be fed back to the controller. If the current is too high (due to moving fast) it might damage your controller. Although I have not tested it yet, it might also be fed back over USB damaging your computer as well.</p>
<p>The printer has basically no customer support. As of March, 2017 the 101hero team is still busy shipping packages and really unresponsive to support questions, however there is a really active group on Facebook, the <a href="https://www.facebook.com/groups/101herounofficial/">101 Hero (Unofficial)</a> and a forum at <a href="http://101user.com/index.php?action=forum">101user.com</a>.</p>
<p>Now it is time to start connecting things, meet me in <a href="http://gabor.heja.hu/blog/2017/03/16/101hero-assembling-the-printer/">part two</a>.</p>
]]></content:encoded>
							<wfw:commentRss>https://gabor.heja.hu/blog/2017/03/16/101hero-hello/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
							</item>
		<item>
		<title>Fun with Planets and a DSLR camera</title>
		<link>https://gabor.heja.hu/blog/2015/07/03/fun-with-planets-and-a-dslr-camera/</link>
				<comments>https://gabor.heja.hu/blog/2015/07/03/fun-with-planets-and-a-dslr-camera/#respond</comments>
				<pubDate>Fri, 03 Jul 2015 11:53:09 +0000</pubDate>
		<dc:creator><![CDATA[Gabor Heja]]></dc:creator>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[photo]]></category>
		<category><![CDATA[video]]></category>

		<guid isPermaLink="false">http://gabor.heja.hu/blog/?p=186</guid>
				<description><![CDATA[I have a cheap fully manual Dörr Danubia f8/500mm lens that I bought back in 2012 for shooting the Venus &#8230; <a href="https://gabor.heja.hu/blog/2015/07/03/fun-with-planets-and-a-dslr-camera/" class="more-link"><span class="more-button">Continue reading &#62;<span class="screen-reader-text">Fun with Planets and a DSLR camera</span></span></a>]]></description>
								<content:encoded><![CDATA[<p>I have a cheap fully manual <a href="http://www.pentaxforums.com/forums/10-pentax-slr-lens-discussion/76837-third-parts-500mm-f-8-optical.html" target="_blank">Dörr Danubia f8/500mm</a> lens that I bought back in 2012 for shooting the <a href="https://en.wikipedia.org/wiki/Transit_of_Venus" target="_blank">Venus transit</a>. Since then I used it to take some Moon pictures also and when I am doing so I tend to use stars to find the correct focus &#8211; the smaller the star appears the better the focus.</p>
<p>In June I was out for shooting again. I mounted the lens, found some bright spot but despite all attempts I could not focus it into one point. I was wondering about what could I possibly do wrong until&#8230; no, that couldn&#8217;t be, could it? Could that spot <em>not</em> be a star at all?</p>
<p>A few pictures and corrections later I was astonished by the fact that I could capture a planet (other than our own, of course) with my DSLR and a cheap lens. Of course, the image quality was bad but, <em>that spot was Saturn</em>.</p>
<p><a href="http://gabor.heja.hu/blog/wp-content/uploads/2015/07/saturn1_600x200.png"><img class="alignnone size-full wp-image-195" src="http://gabor.heja.hu/blog/wp-content/uploads/2015/07/saturn1_600x200.png" alt="saturn1_600x200" width="600" height="200" srcset="https://gabor.heja.hu/blog/wp-content/uploads/2015/07/saturn1_600x200.png 600w, https://gabor.heja.hu/blog/wp-content/uploads/2015/07/saturn1_600x200-300x100.png 300w" sizes="(max-width: 600px) 100vw, 600px" /></a></p>
<p>These pictures were taken directly from the camera, they are only cropped and stitched into one file. A few days later I spotted Saturn again:</p>
<p><a href="http://gabor.heja.hu/blog/wp-content/uploads/2015/07/saturn2_600x200.png"><img class="alignnone size-full wp-image-196" src="http://gabor.heja.hu/blog/wp-content/uploads/2015/07/saturn2_600x200.png" alt="saturn2_600x200" width="600" height="200" srcset="https://gabor.heja.hu/blog/wp-content/uploads/2015/07/saturn2_600x200.png 600w, https://gabor.heja.hu/blog/wp-content/uploads/2015/07/saturn2_600x200-300x100.png 300w" sizes="(max-width: 600px) 100vw, 600px" /></a></p>
<p>&nbsp;</p>
<p>And then I checked out Venus&#8230;</p>
<p><img class="alignnone size-full wp-image-190" src="http://gabor.heja.hu/blog/wp-content/uploads/2015/07/venus_600x200.png" alt="venus_600x200" width="600" height="200" srcset="https://gabor.heja.hu/blog/wp-content/uploads/2015/07/venus_600x200.png 600w, https://gabor.heja.hu/blog/wp-content/uploads/2015/07/venus_600x200-300x100.png 300w" sizes="(max-width: 600px) 100vw, 600px" /></p>
<p>&#8230; and then Jupiter.</p>
<p><a href="http://gabor.heja.hu/blog/wp-content/uploads/2015/07/jupiter_600x200.png"><img class="alignnone size-full wp-image-191" src="http://gabor.heja.hu/blog/wp-content/uploads/2015/07/jupiter_600x200.png" alt="jupiter_600x200" width="600" height="200" srcset="https://gabor.heja.hu/blog/wp-content/uploads/2015/07/jupiter_600x200.png 600w, https://gabor.heja.hu/blog/wp-content/uploads/2015/07/jupiter_600x200-300x100.png 300w" sizes="(max-width: 600px) 100vw, 600px" /></a></p>
<p>And I will not tell you what this is in the last picture:</p>
<p><a href="http://gabor.heja.hu/blog/wp-content/uploads/2015/07/sun_600x200.png"><img class="alignnone size-full wp-image-188" src="http://gabor.heja.hu/blog/wp-content/uploads/2015/07/sun_600x200.png" alt="sun_600x200" width="600" height="200" srcset="https://gabor.heja.hu/blog/wp-content/uploads/2015/07/sun_600x200.png 600w, https://gabor.heja.hu/blog/wp-content/uploads/2015/07/sun_600x200-300x100.png 300w" sizes="(max-width: 600px) 100vw, 600px" /></a></p>
<p>Actually it is not just the Sun but Venus as well from the Venus transit on 6th June 2012.</p>
<p>All images above were just cropped but not scaled, therefore the size of the bodies can be compared to each other.</p>
<p>And since then I have a new hobby&#8230; :)</p>
<p>&nbsp;</p>
]]></content:encoded>
							<wfw:commentRss>https://gabor.heja.hu/blog/2015/07/03/fun-with-planets-and-a-dslr-camera/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
							</item>
		<item>
		<title>Pillow fight day 2015, Budapest</title>
		<link>https://gabor.heja.hu/blog/2015/04/07/pillow-fight-day-2015-budapest/</link>
				<comments>https://gabor.heja.hu/blog/2015/04/07/pillow-fight-day-2015-budapest/#respond</comments>
				<pubDate>Tue, 07 Apr 2015 18:40:47 +0000</pubDate>
		<dc:creator><![CDATA[Gabor Heja]]></dc:creator>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[Video]]></category>
		<category><![CDATA[event]]></category>
		<category><![CDATA[video]]></category>

		<guid isPermaLink="false">http://gabor.heja.hu/blog/?p=174</guid>
				<description><![CDATA[On 4th April, 2015 the 7th International Pillow Fight Day was held all around the world. In Hungary the Budapest UP! has &#8230; <a href="https://gabor.heja.hu/blog/2015/04/07/pillow-fight-day-2015-budapest/" class="more-link"><span class="more-button">Continue reading &#62;<span class="screen-reader-text">Pillow fight day 2015, Budapest</span></span></a>]]></description>
								<content:encoded><![CDATA[<p>On 4th April, 2015 the 7th <a href="http://pillowfightday.com/" target="_blank">International Pillow Fight Day</a> was held all around the world. In Hungary the <a href="https://www.facebook.com/budapest.up" target="_blank">Budapest UP!</a> has <a href="https://www.facebook.com/events/947269028625147/" target="_blank">organized the event</a> to the Heroes&#8217; Square which was a huge success in my opinion.</p>
<p>I have visited earlier Pillow Fight Days and have shot a few photos there, but now I thought creating a video would be more fun.</p>
<p>When I am making a photo or video I like to in the middle of the crowd. Given this, my 8 mm fish-eye lens and a try at recording the videos at 50fps, this is the result:</p>
<p><iframe title="Párnacsata nap 2015 / Pillow Fight Day 2015 (720p, 50 fps)" width="1037" height="583" src="https://www.youtube.com/embed/u9X_YCWLp8g?feature=oembed" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe></p>
<p><em>When watching it in full-screen with the default HTML5 player the &#8220;720p50&#8221; option should come up. If that&#8217;s not the case try <a href="https://www.youtube.com/embed/u9X_YCWLp8g?autoplay=true" target="_blank">this link</a> or try to </em><a href="http://freddie.kakaopor.hu/parnacsata2015" target="_blank"><em>download the original video</em></a><em> and watch it with your dekstop/mobile player.</em></p>
<p>This was the first time I used <a href="https://kdenlive.org/" target="_blank">Kdenlive</a> for video editing and I immediately loved its straightforward interface and workflow, also its ease at rendering to file &#8211; it is a really powerful yet simple editor, I can only recommend it.</p>
<p>Personally I like the outcome and I think I will experiment further.</p>
<p>&nbsp;</p>
]]></content:encoded>
							<wfw:commentRss>https://gabor.heja.hu/blog/2015/04/07/pillow-fight-day-2015-budapest/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
							</item>
		<item>
		<title>Creating a mobile Wi-Fi network</title>
		<link>https://gabor.heja.hu/blog/2015/03/30/creating-a-mobile-wi-fi-network-access-point-router/</link>
				<comments>https://gabor.heja.hu/blog/2015/03/30/creating-a-mobile-wi-fi-network-access-point-router/#respond</comments>
				<pubDate>Mon, 30 Mar 2015 16:00:54 +0000</pubDate>
		<dc:creator><![CDATA[Gabor Heja]]></dc:creator>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[Dev]]></category>
		<category><![CDATA[aa]]></category>
		<category><![CDATA[access-point]]></category>
		<category><![CDATA[batteries]]></category>
		<category><![CDATA[battery]]></category>
		<category><![CDATA[mobile]]></category>
		<category><![CDATA[on the go]]></category>
		<category><![CDATA[router]]></category>
		<category><![CDATA[wi-fi]]></category>
		<category><![CDATA[wrt54gl]]></category>

		<guid isPermaLink="false">http://gabor.heja.hu/blog/?p=157</guid>
				<description><![CDATA[As a part of a somewhat larger project I have been thinking about creating a mobile Wi-Fi network that can be &#8230; <a href="https://gabor.heja.hu/blog/2015/03/30/creating-a-mobile-wi-fi-network-access-point-router/" class="more-link"><span class="more-button">Continue reading &#62;<span class="screen-reader-text">Creating a mobile Wi-Fi network</span></span></a>]]></description>
								<content:encoded><![CDATA[<p>As a part of a somewhat larger project I have been thinking about creating a mobile Wi-Fi network that can be carried around (and later can be extended to a network of multiple routers).</p>
<p>As I already have a PowerBank (basically a rechargeable battery with USB ports to charge a phone on the go) it looked like the ideal solution to use this as a power source. But then after a few searches of USB powered or at least 5 volts powered Wi-Fi access-points that meet my requirements to create a secured WDS (more on this in a later post) seemed to be too high. I found a few of them but they either were ridiculously priced or were given bad reviews.</p>
<p>At this time I realized that I had a good old Linksys WRT54GL router laying around. As this is a fairly common model with a quite favorable hackability factor, I did a quick research and found that this might be the best device for me.</p>
<p><a href="http://gabor.heja.hu/blog/wp-content/uploads/2015/03/IMG_9862_3_1600.jpg"><img class="alignnone size-medium wp-image-158" src="http://gabor.heja.hu/blog/wp-content/uploads/2015/03/IMG_9862_3_1600-300x200.jpg" alt="IMG_9862_3_1600" width="300" height="200" srcset="https://gabor.heja.hu/blog/wp-content/uploads/2015/03/IMG_9862_3_1600-300x200.jpg 300w, https://gabor.heja.hu/blog/wp-content/uploads/2015/03/IMG_9862_3_1600-1024x683.jpg 1024w, https://gabor.heja.hu/blog/wp-content/uploads/2015/03/IMG_9862_3_1600.jpg 1600w" sizes="(max-width: 300px) 100vw, 300px" /></a></p>
<p>The OpenWRT can be installed on this router and from software point of view it has all the features I want: WPA2 support, WDS (Wireless Distribution System &#8211; mesh, roughly), admin interface over HTTPS and is running Linux. Well, this last part was not strictly on the requirement list but hey, a device running Linux is always good to see (and opens up a lot of new opportunities).</p>
<p>On the hardware part I found out that the power adapter says it is supplying 12 volts and 0.5 amperes to the router. I found a few places where they told that just after the power jack there is a power regulator (a switching mode buck converter) that converts the voltage down to 3.3 volts. After taking apart I saw that this is indeed in place. Good thing about these buck converters that the input voltage range is usually wide, I found that it is about 3.7-16 volts, so it is more than suitable for my battery powered operation where the voltage drops over time when connected directly to the battery.</p>
<p>After finding out this wide range of input voltage I reconsidered my battery selection. Instead of using the PowerBank I decided to use a more universal solution &#8211; AA batteries. These can be found virtually anywhere and can be used for a lot more purposes when they are not used in this project. Also, hooking up 4 of them gives me 6 volts, but if I need more power then 8 of them is fine as well (giving 12 volts).</p>
<p><a href="http://gabor.heja.hu/blog/wp-content/uploads/2015/03/IMG_9865_3_1600.jpg"><img class="alignnone size-medium wp-image-159" src="http://gabor.heja.hu/blog/wp-content/uploads/2015/03/IMG_9865_3_1600-300x200.jpg" alt="IMG_9865_3_1600" width="300" height="200" srcset="https://gabor.heja.hu/blog/wp-content/uploads/2015/03/IMG_9865_3_1600-300x200.jpg 300w, https://gabor.heja.hu/blog/wp-content/uploads/2015/03/IMG_9865_3_1600-1024x683.jpg 1024w, https://gabor.heja.hu/blog/wp-content/uploads/2015/03/IMG_9865_3_1600.jpg 1600w" sizes="(max-width: 300px) 100vw, 300px" /></a></p>
<p>Here I used 2700 mAh batteries, 4 of them gives about 13 Wh (watt-hour) power (1.2 volts x 2.700 ampere-hour x 4), 8 of them gives about 26 Wh. If the router is consuming the maximum the original power adapter can provide (6 watts) then I can use the router for 2 hours straight from 4 batteries. But I really doubt that even a peak consumption ever reaches 6 watts.</p>
<p>Unfortunately it seems that the barrel plug power connector is not too stable (or maybe just the one I bought was of poor quality), when moving the router around a little a momentary loss of power (and therefore a reboot) was fairly common. So&#8230; as the router was already taken apart, I have soldered a pair of wires to the main board for the power and found a hole on the bottom of the case where I could bring the cable out without having to drill it.</p>
<p>
<a href='https://gabor.heja.hu/blog/2015/03/30/creating-a-mobile-wi-fi-network-access-point-router/img_9880_3_1600/'><img width="150" height="150" src="https://gabor.heja.hu/blog/wp-content/uploads/2015/03/IMG_9880_3_1600-150x150.jpg" class="attachment-thumbnail size-thumbnail" alt="" sizes="100vw" /></a>
<a href='https://gabor.heja.hu/blog/2015/03/30/creating-a-mobile-wi-fi-network-access-point-router/img_9876_3_1600/'><img width="150" height="150" src="https://gabor.heja.hu/blog/wp-content/uploads/2015/03/IMG_9876_3_1600-150x150.jpg" class="attachment-thumbnail size-thumbnail" alt="" sizes="100vw" /></a>
<a href='https://gabor.heja.hu/blog/2015/03/30/creating-a-mobile-wi-fi-network-access-point-router/img_9873_3_1600/'><img width="150" height="150" src="https://gabor.heja.hu/blog/wp-content/uploads/2015/03/IMG_9873_3_1600-150x150.jpg" class="attachment-thumbnail size-thumbnail" alt="" sizes="100vw" /></a>
<a href='https://gabor.heja.hu/blog/2015/03/30/creating-a-mobile-wi-fi-network-access-point-router/img_9872_3_1600/'><img width="150" height="150" src="https://gabor.heja.hu/blog/wp-content/uploads/2015/03/IMG_9872_3_1600-150x150.jpg" class="attachment-thumbnail size-thumbnail" alt="" sizes="100vw" /></a>
<a href='https://gabor.heja.hu/blog/2015/03/30/creating-a-mobile-wi-fi-network-access-point-router/img_9870_3_1600/'><img width="150" height="150" src="https://gabor.heja.hu/blog/wp-content/uploads/2015/03/IMG_9870_3_1600-150x150.jpg" class="attachment-thumbnail size-thumbnail" alt="" sizes="100vw" /></a>
</p>
<p>It turned out to be working perfectly in the end.</p>
<p>
<a href='https://gabor.heja.hu/blog/2015/03/30/creating-a-mobile-wi-fi-network-access-point-router/img_9868_3_1600/'><img width="150" height="150" src="https://gabor.heja.hu/blog/wp-content/uploads/2015/03/IMG_9868_3_1600-150x150.jpg" class="attachment-thumbnail size-thumbnail" alt="" sizes="100vw" /></a>
<a href='https://gabor.heja.hu/blog/2015/03/30/creating-a-mobile-wi-fi-network-access-point-router/img_9889_3_1600/'><img width="150" height="150" src="https://gabor.heja.hu/blog/wp-content/uploads/2015/03/IMG_9889_3_1600-150x150.jpg" class="attachment-thumbnail size-thumbnail" alt="" sizes="100vw" /></a>
</p>
<p>In my next post I will write about making another WRT54GL mobile and setting up a WDS (Wireless Distribution System) to extend the range of the mobile Wi-Fi network.</p>
]]></content:encoded>
							<wfw:commentRss>https://gabor.heja.hu/blog/2015/03/30/creating-a-mobile-wi-fi-network-access-point-router/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
							</item>
		<item>
		<title>VoCore: A coin-sized Linux computer with Wi-Fi for USD 20</title>
		<link>https://gabor.heja.hu/blog/2014/11/20/vocore-a-coin-sized-linux-computer-with-wi-fi-for-usd-20/</link>
				<comments>https://gabor.heja.hu/blog/2014/11/20/vocore-a-coin-sized-linux-computer-with-wi-fi-for-usd-20/#respond</comments>
				<pubDate>Thu, 20 Nov 2014 00:26:00 +0000</pubDate>
		<dc:creator><![CDATA[Gabor Heja]]></dc:creator>
				<category><![CDATA[VoCore]]></category>
		<category><![CDATA[hardware]]></category>
		<category><![CDATA[linux]]></category>
		<category><![CDATA[vocore]]></category>

		<guid isPermaLink="false">http://gabor.heja.hu/blog/?p=120</guid>
				<description><![CDATA[In early July I&#8217;ve found an Indiegogo project of a miniature Linux powered computer called VoCore. It is a RaLink RT5350 (360 &#8230; <a href="https://gabor.heja.hu/blog/2014/11/20/vocore-a-coin-sized-linux-computer-with-wi-fi-for-usd-20/" class="more-link"><span class="more-button">Continue reading &#62;<span class="screen-reader-text">VoCore: A coin-sized Linux computer with Wi-Fi for USD 20</span></span></a>]]></description>
								<content:encoded><![CDATA[<p>In early July I&#8217;ve found an <a href="https://www.indiegogo.com/projects/vocore-a-coin-sized-linux-computer-with-wifi" target="_blank">Indiegogo project</a> of a miniature Linux powered computer called VoCore.</p>
<p>It is a <a href="https://wikidevi.com/wiki/Ralink_RT5350" target="_blank">RaLink RT5350</a> (360 MHz MIPS24KEc) based board with 8 MB SPI Flash (although Vonger, the creator of the project <a href="http://vonger.cn/?p=1316" target="_blank">upgraded to a 16 MB Flash for the Indiegogo batch</a> just because he&#8217;s a nice guy) with two 10/100 Mbps Ethernet interfaces, one USB 2.0 interface, a bunch of serial interfaces (UART, I2C, I2S, PCM, JTAG) and over 20 GPIOs. All these broken out to standard 1.27 mm connectors while keeping the size at 25&#215;25 mm!</p>
<p>There is a dock to provide some connectors: an Ethernet, a USB, a micro USB (for the power) and a micro SD card slot &#8211; this measues 25&#215;25 mm as well, of course.</p>
<p>VoCore is running the <a href="http://wiki.openwrt.org/toh/vocore/vocore" target="_blank">OpenWRT firmware</a>. It acts as an access point by default, running a network called &#8220;VoCore&#8221; (this is unfortunately an open network (i.e. no encryption set up) so changing security settings as soon as possible is recommended).</p>
<p>Vonger, the creator of the project is running a blog at <a href="http://vonger.cn/" target="_blank">vonger.cn</a>, he was constantly updating it with the latest status of the VoCore development and production, both bad and good news. Thanks to this it seemed that we were witnessing all the little details, it felt like we (the backers) were parts of the whole process :)</p>
<p>The VoCore can be ordered from <a href="http://vocore.io/store" target="_blank">vocore.io/store</a>. If you would like to have the VoCore main board only (with all the I/O breakouts but no connectors at all) it is USD 20, but if you&#8217;d like to have the Dock as well, plus a USB to TTL converter (note: this is missing from my pictures below!) to re-flash the firmware it is just USD 45.</p>
<p>I believe that the VoCore has a lot of potential thanks to its small size (25&#215;25 mm) its low power consumption (<a href="https://wikidevi.com/wiki/VoCore" target="_blank">0.6-1.2 Watts</a>) its flexibility (20+ GPIOs and the whole bunch of connector break outs) and low cost.</p>
<p>I think this is a must have board for any geeks out there who have thought about hacking some hardware once in a while.</p>
<p>Oh, and one more thing: VoCore is open source, both software and hardware. All the schematics, circuits, firmware, 3D model for the shell/case, <a href="http://vocore.io/wiki/index/id:11" target="_blank">everything is available to download</a> from <a href="http://vocore.io/" target="_blank">the VoCore&#8217;s site</a>.</p>
<p>
<a href='https://gabor.heja.hu/blog/2014/11/20/vocore-a-coin-sized-linux-computer-with-wi-fi-for-usd-20/img_7186_5/'><img width="150" height="150" src="https://gabor.heja.hu/blog/wp-content/uploads/2014/11/IMG_7186_5-150x150.jpg" class="attachment-thumbnail size-thumbnail" alt="" sizes="100vw" /></a>
<a href='https://gabor.heja.hu/blog/2014/11/20/vocore-a-coin-sized-linux-computer-with-wi-fi-for-usd-20/img_7191_5/'><img width="150" height="150" src="https://gabor.heja.hu/blog/wp-content/uploads/2014/11/IMG_7191_5-150x150.jpg" class="attachment-thumbnail size-thumbnail" alt="" sizes="100vw" /></a>
<a href='https://gabor.heja.hu/blog/2014/11/20/vocore-a-coin-sized-linux-computer-with-wi-fi-for-usd-20/img_7188_5/'><img width="150" height="150" src="https://gabor.heja.hu/blog/wp-content/uploads/2014/11/IMG_7188_5-150x150.jpg" class="attachment-thumbnail size-thumbnail" alt="" sizes="100vw" /></a>
<a href='https://gabor.heja.hu/blog/2014/11/20/vocore-a-coin-sized-linux-computer-with-wi-fi-for-usd-20/img_7190_5/'><img width="150" height="150" src="https://gabor.heja.hu/blog/wp-content/uploads/2014/11/IMG_7190_5-150x150.jpg" class="attachment-thumbnail size-thumbnail" alt="" sizes="100vw" /></a>
<a href='https://gabor.heja.hu/blog/2014/11/20/vocore-a-coin-sized-linux-computer-with-wi-fi-for-usd-20/img_7170_5/'><img width="150" height="150" src="https://gabor.heja.hu/blog/wp-content/uploads/2014/11/IMG_7170_5-150x150.jpg" class="attachment-thumbnail size-thumbnail" alt="" sizes="100vw" /></a>
<a href='https://gabor.heja.hu/blog/2014/11/20/vocore-a-coin-sized-linux-computer-with-wi-fi-for-usd-20/img_7170_6/'><img width="150" height="150" src="https://gabor.heja.hu/blog/wp-content/uploads/2014/11/IMG_7170_6-150x150.jpg" class="attachment-thumbnail size-thumbnail" alt="" sizes="100vw" /></a>
<a href='https://gabor.heja.hu/blog/2014/11/20/vocore-a-coin-sized-linux-computer-with-wi-fi-for-usd-20/img_7171_5/'><img width="150" height="150" src="https://gabor.heja.hu/blog/wp-content/uploads/2014/11/IMG_7171_5-150x150.jpg" class="attachment-thumbnail size-thumbnail" alt="" sizes="100vw" /></a>
<a href='https://gabor.heja.hu/blog/2014/11/20/vocore-a-coin-sized-linux-computer-with-wi-fi-for-usd-20/img_7171_6/'><img width="150" height="150" src="https://gabor.heja.hu/blog/wp-content/uploads/2014/11/IMG_7171_6-150x150.jpg" class="attachment-thumbnail size-thumbnail" alt="" sizes="100vw" /></a>
<a href='https://gabor.heja.hu/blog/2014/11/20/vocore-a-coin-sized-linux-computer-with-wi-fi-for-usd-20/img_7173_5/'><img width="150" height="150" src="https://gabor.heja.hu/blog/wp-content/uploads/2014/11/IMG_7173_5-150x150.jpg" class="attachment-thumbnail size-thumbnail" alt="" sizes="100vw" /></a>
<a href='https://gabor.heja.hu/blog/2014/11/20/vocore-a-coin-sized-linux-computer-with-wi-fi-for-usd-20/img_7173_6/'><img width="150" height="150" src="https://gabor.heja.hu/blog/wp-content/uploads/2014/11/IMG_7173_6-150x150.jpg" class="attachment-thumbnail size-thumbnail" alt="" sizes="100vw" /></a>
<a href='https://gabor.heja.hu/blog/2014/11/20/vocore-a-coin-sized-linux-computer-with-wi-fi-for-usd-20/img_7183_5/'><img width="150" height="150" src="https://gabor.heja.hu/blog/wp-content/uploads/2014/11/IMG_7183_5-150x150.jpg" class="attachment-thumbnail size-thumbnail" alt="" sizes="100vw" /></a>
<a href='https://gabor.heja.hu/blog/2014/11/20/vocore-a-coin-sized-linux-computer-with-wi-fi-for-usd-20/img_7180_5/'><img width="150" height="150" src="https://gabor.heja.hu/blog/wp-content/uploads/2014/11/IMG_7180_5-150x150.jpg" class="attachment-thumbnail size-thumbnail" alt="" sizes="100vw" /></a>
</p>
<p>&nbsp;</p>
]]></content:encoded>
							<wfw:commentRss>https://gabor.heja.hu/blog/2014/11/20/vocore-a-coin-sized-linux-computer-with-wi-fi-for-usd-20/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
							</item>
		<item>
		<title>GameBoy hardware hacking: Part 1</title>
		<link>https://gabor.heja.hu/blog/2014/10/05/gameboy-hardware-hacking-part-1/</link>
				<comments>https://gabor.heja.hu/blog/2014/10/05/gameboy-hardware-hacking-part-1/#respond</comments>
				<pubDate>Sun, 05 Oct 2014 21:50:24 +0000</pubDate>
		<dc:creator><![CDATA[Gabor Heja]]></dc:creator>
				<category><![CDATA[Dev]]></category>
		<category><![CDATA[Game Boy]]></category>
		<category><![CDATA[dev]]></category>
		<category><![CDATA[gameboy]]></category>
		<category><![CDATA[hardware]]></category>

		<guid isPermaLink="false">http://gabor.heja.hu/blog/?p=15</guid>
				<description><![CDATA[In the case of GameBoy and GameBoy Color systems the console itself holds just a really minimal program on the board, the so-called &#8230; <a href="https://gabor.heja.hu/blog/2014/10/05/gameboy-hardware-hacking-part-1/" class="more-link"><span class="more-button">Continue reading &#62;<span class="screen-reader-text">GameBoy hardware hacking: Part 1</span></span></a>]]></description>
								<content:encoded><![CDATA[<p>In the case of GameBoy and GameBoy Color systems the console itself holds just a really minimal program on the board, the so-called <a href="http://gbdev.gg8.se/wiki/articles/Gameboy_Bootstrap_ROM" target="_blank">boot ROM</a> (256 bytes long). It does the initialization of the components and the cartridge, also it reads the header part of the program stored on the cartridge and checks if that is valid. (Fun fact: the scrolling Nintendo logo is read from the cartridge and it is really a test to see if the cartridge can be accessed correctly. The logo is then compared to the one stored in the boot ROM to see if it was read correctly, if not that indicates some pins are not working and the CPU halts. This is when you need to blow the cartridge.).</p>
<p>All the other program code (i.e. the game itself and all the instructions to make it work) is located on the GameBoy Cartridge alongside with the textures, sounds and all the rest.</p>
<p>So the cartridge is basically the part of the system, the CPU reads it directly when it needs the next instruction and executes the code from there without copying it to its own memory.</p>
<p>The cartridges usually consist of the following components:</p>
<ul>
<li><strong>ROM</strong>: Read Only Memory<br />
Stores the program code and all the resources needed for the game. It is programmed once and then never written again.</li>
<li><strong>MBC</strong>: Memory Bank Controller (optional)<br />
The Memory Bank Controller switches banks of the addressable memory space (32 kilobyte in total). The lower 16 kilobytes are always the first 16 kilobytes of the ROM while the upper 16 kilobytes are switchable between several blocks (i.e. part of the ROM or the RAM). There are several MBCs out there with their own purposes. This is a really long story, I&#8217;ll write more about this later.</li>
<li><strong>RAM</strong>: Random Access Memory (optional)<br />
This is a readable and writable storage, when a game has high-score list, saved game state, etc. it holds them information in the RAM. This is a standard SRAM so this is volatile &#8211; it needs constant power to keep the data alive this is why cartridges with RAM need batteries as well.</li>
<li><strong>Battery</strong>: (optional)<br />
The battery provides power for the RAM to keep the stored data alive.</li>
<li><strong>Connector</strong>:<br />
Last but not least, all the cartridges have a 32 pin connector that provides a connection between the console and the components of the cartridge.</li>
</ul>
<p>The interface between the console and the cartridge consists of 32 pins (direction is written from console perspective, out: from console to cartridge, in: from cartridge to console):</p>
<ul>
<li><strong>pin 1 [out]</strong>: VCC (+5 V)<br />
This is the supply pin for the cartridge and its components (i.e. the ROM, RAM, MBC).</li>
<li><strong>pin 2 [n/c]</strong>: not connected or CPU clock<br />
I have not seen this pin connected so far but some documents claims this is a direct output of the CPU clock. I ran a logic analyzer on this pin, it showed a high-low pattern alternating at 1.04 MHz (0.46 µs high followed by 0.50 µs low).</li>
<li><strong>pin 3 [out]</strong>: !write<br />
This is a signal line that is low when a write request is made to RAM. Otherwise this is high.</li>
<li><strong>pin 4 [out]</strong>: !read<br />
This is a signal line that is low when a read reqeust is made to ROM, MBC or RAM. Otherwise this is high.</li>
<li><strong>pin 5 [out]</strong>: !RAM select<br />
This line is low when the RAM or the MBC is being accessed. Both of them supports reads and writes. Otherwise this is high.</li>
<li><strong>pin 6..21 [out]</strong>: address selection bit 0..15<br />
These bits sets the address that is being written or read in the RAM, ROM, or in the MBC registers.</li>
<li><strong>pin 22-29 [out/in]</strong>: data in/out bit 0..7<br />
When the console wants to read from the ROM, RAM or MBC these pins are inputs, their values being set by the remote component. When the console wants to write data these are outputs, set to the data by the console.</li>
<li><strong>pin 30 [out]</strong>: !reset<br />
When the console reboots it sets this pin high after about 4-5 ms. This delay is probably introduced to give the clock a few cycles to stabilize.</li>
<li><strong>pin 31 [n/c]</strong>: not connected or analog audio input<br />
I have not seen this pin connected so far but some documents claims this is an analog audio input. (I think I&#8217;ve read something about a direct sound channel on the GameBoy CPU, but that might be a different story.)</li>
<li><strong>pin 32 [out]</strong>: GND<br />
The ground connection for the cartridge and all the components.</li>
</ul>
<p>In the following posts I will write about the different cartridge and MBC types, and also will post schematics and circuits to build a few different homebrew GameBoy cartridges. Stay tuned.</p>
]]></content:encoded>
							<wfw:commentRss>https://gabor.heja.hu/blog/2014/10/05/gameboy-hardware-hacking-part-1/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
							</item>
		<item>
		<title>Multiplayer gaming, part 1: Sync the future!</title>
		<link>https://gabor.heja.hu/blog/2014/06/12/multiplayer-gaming-part-1-sync-the-future/</link>
				<comments>https://gabor.heja.hu/blog/2014/06/12/multiplayer-gaming-part-1-sync-the-future/#respond</comments>
				<pubDate>Thu, 12 Jun 2014 00:54:20 +0000</pubDate>
		<dc:creator><![CDATA[Gabor Heja]]></dc:creator>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[Dev]]></category>
		<category><![CDATA[dev]]></category>
		<category><![CDATA[gamedev]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[js]]></category>

		<guid isPermaLink="false">http://gabor.heja.hu/blog/?p=52</guid>
				<description><![CDATA[Last year I was writing a little game intended to be run in the browser. I was writing it in pure &#8230; <a href="https://gabor.heja.hu/blog/2014/06/12/multiplayer-gaming-part-1-sync-the-future/" class="more-link"><span class="more-button">Continue reading &#62;<span class="screen-reader-text">Multiplayer gaming, part 1: Sync the future!</span></span></a>]]></description>
								<content:encoded><![CDATA[<p>Last year I was writing a little game intended to be run in the browser. I was writing it in pure javascript (i.e. no graphics or whatsoever was stored, everything was dynamically generated) for the <a href="http://2013.js13kgames.com/" target="_blank">js13k Games 2013</a>, it is the <a href="https://github.com/gheja/untitled13/" target="_blank">Untitled 13</a> (<a href="http://play.untitled13.net/" target="_blank">can be played here</a>).</p>
<p>One of the base concepts was to build a <em>multiplayer</em> game. It was challenging despite I&#8217;ve written some basic games, also mulitiplayer ones, I&#8217;ve never tried to write a real-time strategy game yet (not counting a quick POC). The synchronization of the game between players was the most interesting part of it, and therefore my favorite one. I was not intended to sync the whole game state (map, objects, internal states, etc.) continuously as this can easily become a huge set of data even for tiny games at high refresh rates so I decided I will come up with a better, more resource-friendly solution.</p>
<p>I&#8217;ve been thinking about the game: it has some mechanics, player events events and also their effects on the gameplay and concluded one could be separated from the other &#8211; of course it turned out that I was not the first who came up with this approach. So instead of syncing <em>everything</em>, I was thinking about synchronizing just the <em>map in its starting state</em> and a <em>list of events</em>.</p>
<p>All these events were always originated from the players (i.e. buy an item, toggle a switch) or from the game mechanics (i.e. an enemy is close enough to be fired on, an object loses health, objects collide). As the game on both ends are always identical the result of mechanics should be always identical too, therefore all I left to deal with were the events from the players.</p>
<p>So basically all I was doing is this: my game had so-called &#8220;ticks&#8221; that were the updates for the game internal states (this event was happening with a guaranteed interval, i.e. 15 times per seconds, or if late (due to lack of CPU resources or bad timing) all the affected ticks ran anyways to catch up), and when a player did an action (i.e. clicked a switch) I stored a record in a queue about it and a &#8220;process this on the <em>n</em>th tick&#8221; property. I&#8217;ve also sent this record over the network to the other player. This way I introduced a slight delay (this <em>n</em>th tick was always about 300 milliseconds in the future) but during this delay I sent the event to the other player and when the <em>n</em>th tick came both players made the change on their isolated environment in the exact same moment (speaking in game time) and therefore both ends have seen the same new state, <em>the game is deterministic</em>.</p>
<p>Later I&#8217;ve learned &#8211; in a <a href="http://gafferongames.com/networking-for-game-programmers/what-every-programmer-needs-to-know-about-game-networking/" target="_blank">Gaffer on Games</a> post and in another one about <a href="http://www.gamasutra.com/view/feature/3094/1500_archers_on_a_288_network_.php" target="_blank">network programming in Age of Empires</a> (both are must reads!) &#8211; this is a so called <em>simultaneous simulation</em> approach with one big difference. The handling of the lag on the network. This was the worst thing during the development and tests of the game. As I&#8217;ve been syncing future events if the notification got stuck somewhere between the players it could easily come later than it must have been executed. As the tick it was intended to be run at was passed (and the other player have executed at that time) the game became out of sync. And this is bad, really bad. (I&#8217;ve made some efforts to handle this, but these turned the game code much more complicated, i.e. to be sure that important events happen on both sides I added them as an event to the queue (and sent that over the network), that&#8217;s why the little creatures pause for a while before exploding at the target markers. But this is bad, don&#8217;t do this. Keep reading for a simpler solution instead.)</p>
<p>In a real <em>simultaneous simulation</em> the players should be aware of the lag in the propagation of events so instead of just saying &#8220;here, run this on the <em>n</em>th tick&#8221; there needs to be a &#8220;I have nothing for you to run on the <em>n</em>th tick, carry on&#8221;. And if the other player does not specify what to do (or not to do) on the <em>n</em>th tick when it comes <em>the simulation should pause </em>while it is unsure about what to do. Which is not nice but far much better than becoming out of sync. Obviously working with 15 ticks per seconds and expecting the network to transfer the data for the next tick is nearly impossible (talking about transmitting over the internet as this would permit about a 66 milliseconds deadline on a client1-server-client2 route, also sending at least 15 events every second) and would result frequent pauses, and that would be really annoying. So instead of scheduling events for any tick of the game a rule should be applied to limit the ticks that can accept events. Saying events can be scheduled only for every 5th ticks reduces the count of transmitted packets dramatically, also extends the deadline to a more sane value.</p>
<p>Also, when a pause occurs due to network lag the rule of scheduling could change i.e. not every 5th but every 10th tick should accept events. And likewise when the network seems reliable for a long time this number could decrease.</p>
<p>But this topic will be covered in another post, first I&#8217;ve to try this approach. Although I think this should work nicely as games like Age of Empires and Warcraft used this method pretty reliably.</p>
<p>Recommended reading:</p>
<ul>
<li><a href="http://www.gamasutra.com/view/feature/3094/1500_archers_on_a_288_network_.php" target="_blank">Mark Terrano: 1500 Archers on a 28.8: Network Programming in Age of Empires and Beyond</a></li>
<li><a href="http://gafferongames.com/networking-for-game-programmers/what-every-programmer-needs-to-know-about-game-networking/" target="_blank">Glenn Fiedler: What every programmer needs to know about game networking</a></li>
<li><a href="http://gafferongames.com/networking-for-game-programmers/" target="_blank">Glenn Fiedler&#8217;s Game Development Articles and Tutorials</a></li>
</ul>
]]></content:encoded>
							<wfw:commentRss>https://gabor.heja.hu/blog/2014/06/12/multiplayer-gaming-part-1-sync-the-future/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
							</item>
		<item>
		<title>r42.cc</title>
		<link>https://gabor.heja.hu/blog/2014/05/03/r42-cc/</link>
				<comments>https://gabor.heja.hu/blog/2014/05/03/r42-cc/#respond</comments>
				<pubDate>Sat, 03 May 2014 12:29:25 +0000</pubDate>
		<dc:creator><![CDATA[Gabor Heja]]></dc:creator>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[Dev]]></category>

		<guid isPermaLink="false">http://gabor.heja.hu/blog/?p=31</guid>
				<description><![CDATA[Lately I&#8217;ve been designing some little PCBs and thought it would be nice to add a short URL on it &#8230; <a href="https://gabor.heja.hu/blog/2014/05/03/r42-cc/" class="more-link"><span class="more-button">Continue reading &#62;<span class="screen-reader-text">r42.cc</span></span></a>]]></description>
								<content:encoded><![CDATA[<p>Lately I&#8217;ve been designing some little PCBs and thought it would be nice to add a short URL on it leading to a page about that particular PCB (or PCB series in the future). Also, adding the short URL to any other things I (or <em>we</em>, as a little maker team with a few of my friends) create. Although this will probably not have any sort of impact on the early designs (made in just one copy), it&#8217;d make them more personal, I guess.</p>
<p>So I had to come up with a short domain name &#8211; I already have one but that is under the .hu TLD &#8211; that gives away something about the purpose of itself&#8230; It was ridiculously challenging to find one that is not already taken in the .com/.org/.cc world. In the end I&#8217;ve chosen the <em>r42.cc.</em> The first part stands for <em>revision42</em>, something that is probably reached by a successful project (and of course, 42 is <a href="https://en.wikipedia.org/wiki/Phrases_from_The_Hitchhiker's_Guide_to_the_Galaxy#Answer_to_the_Ultimate_Question_of_Life.2C_the_Universe.2C_and_Everything_.2842.29" target="_blank">the answer</a>), and the cc stands for<em> creative community</em>.</p>
<p>So if I put here some photos and designs in the future you should check it for this URL and open it to see some related content.</p>
]]></content:encoded>
							<wfw:commentRss>https://gabor.heja.hu/blog/2014/05/03/r42-cc/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
							</item>
		<item>
		<title>Random Idea: Moving Munzees</title>
		<link>https://gabor.heja.hu/blog/2014/05/03/random-idea-moving-munzees/</link>
				<comments>https://gabor.heja.hu/blog/2014/05/03/random-idea-moving-munzees/#respond</comments>
				<pubDate>Sat, 03 May 2014 12:15:25 +0000</pubDate>
		<dc:creator><![CDATA[Gabor Heja]]></dc:creator>
				<category><![CDATA[Random idea]]></category>

		<guid isPermaLink="false">http://gabor.heja.hu/blog/?p=57</guid>
				<description><![CDATA[I&#8217;ve been playing Geocaching for a while now. Lately I&#8217;ve started to play Munzee, a game that is the little brother &#8230; <a href="https://gabor.heja.hu/blog/2014/05/03/random-idea-moving-munzees/" class="more-link"><span class="more-button">Continue reading &#62;<span class="screen-reader-text">Random Idea: Moving Munzees</span></span></a>]]></description>
								<content:encoded><![CDATA[<p>I&#8217;ve been playing Geocaching for a while now.</p>
<p>Lately I&#8217;ve started to play Munzee, a game that is the little brother of Geocaching in my point of view. It is working by the same idea, you&#8217;ve given the coordinates and you have to find the hidden treasure. Although here is no treasure at all, just a QR code stuck to a surface &#8211; usually at the back of (traffic) signs and bottom of traffic lamps. So finding it is not that great challenge after a while&#8230;</p>
<p>The idea is the following: what if we stuck the QR codes on non fixed objects, but <em>moving</em> ones? I.e. you could stick it on a train (somewhere hidden, not on a window or seat, so where you could only find it if you&#8217;re looking for it). It would be more challenging, and also you could track where the object travels (could be illustrated on a &#8220;spotted here&#8221; map) also you could meet something you met earlier at a different place.</p>
<p>I think this could add another layer of fun to the game.</p>
]]></content:encoded>
							<wfw:commentRss>https://gabor.heja.hu/blog/2014/05/03/random-idea-moving-munzees/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
							</item>
	</channel>
</rss>
