<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Muodov's Honey Jar</title>
    <description>Maxim Tsoy's personal blog</description>
    <link>https://blog.zok.pw/</link>
    <atom:link href="https://blog.zok.pw/feed.xml" rel="self" type="application/rss+xml" />
    
      <item>
        <title>Meccano Rubik's Shrine</title>
        <description>&lt;h2 id=&quot;update&quot;&gt;&lt;em&gt;Update:&lt;/em&gt;&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;We’ve received some feedback from people who’d like to get their own replica of this
and other models. That’s very cool! Have a look at the &lt;a href=&quot;https://meccanokinematics.net/meccano-rubiks-shrine/#make-your-own-cube-solver&quot;&gt;project page&lt;/a&gt;
to get more details about this project.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/shrine/overall.png&quot; alt=&quot;The Rubik's Shrine&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;references&quot;&gt;References&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://meccanokinematics.net/meccano-rubiks-shrine/&quot;&gt;Project page on Meccano Kinematics&lt;/a&gt; website&lt;/li&gt;
  &lt;li&gt;Solving algorithm implementation: &lt;a href=&quot;https://github.com/muodov/kociemba&quot;&gt;GitHub&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Other Wilbert’s machines: &lt;a href=&quot;https://www.youtube.com/user/Meccanokinematics&quot;&gt;YouTube channel&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;More about Meccano and FAC system: &lt;a href=&quot;https://meccanokinematics.net/&quot;&gt;Meccano Kinematics&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;video&quot;&gt;Video&lt;/h2&gt;

&lt;iframe src=&quot;https://www.youtube.com/embed/C9rCBjLGxJs&quot; width=&quot;100%&quot; height=&quot;360px&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; mozallowfullscreen=&quot;&quot; webkitallowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;

&lt;p&gt;Although our &lt;a href=&quot;/hacking/2015/08/18/fac-rubik-solver/&quot;&gt;FAC Solver&lt;/a&gt; has gathered 120K+ views
on YouTube, we felt that this was only the beginning. By the time we finished it,
we had a bunch of ideas in mind that we were eager to realize. While I was trying to
figure out more efficient Arduino-Raspberry setup, Wilbert’s
tireless genius came up with another 3-gripper machine, this time assembled with
old-school (yet well-known) &lt;a href=&quot;http://www.meccano.com/&quot;&gt;Meccano&lt;/a&gt; construction set.&lt;/p&gt;

&lt;p&gt;While FAC Solver was more of an experimental creation, this time we had lots
of experience, so we could address fundamental weaknesses of the FAC Solver,
at the same time focusing on the look and feel of the machine, making it more
friendly and appealing. We didn’t want to make yet another hacky DIY machine,
but something that would combine technology with design, something nice to
build, to see, and to own.&lt;/p&gt;

&lt;h2 id=&quot;mechanical-design-less-wiring-more-art&quot;&gt;Mechanical Design: Less Wiring, More Art&lt;/h2&gt;

&lt;p&gt;Having several decades of Meccano experience, Wilbert managed to design the machine
that is much more compact than the FAC solver. To achieve the cleanest look possible,
all the wiring and unnecessary details are hidden inside the metal body.
If necessary, the PCB board and other electronics are
easily accessible through the door on the back side. Moreover, the whole machine is
assembled using normal Meccano parts that are available on the internet.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/shrine/shrine-in-the-box.jpg&quot; alt=&quot;The Rubik's Shrine in transport box&quot; /&gt;
&lt;em&gt;The Rubik’s Shrine in transport box&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Wilbert was inspired by the Dark Tower from the Lord of The Rings,
so the look of the Shrine is meant to be quite pompous :)&lt;/p&gt;

&lt;p&gt;Being very careful about the details, Wilbert made sure all parts matched
each other: all original rounded shiny Meccano strips, with 3-layered custom airbrush
finish on the plates. Brass gears put the final touch to the appearance of the machine.
The fine-grained assembly was also very important: the distance between some
parts was just a couple of millimeters.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/shrine/closeup-cube.jpg&quot; alt=&quot;Some closeup photos&quot; /&gt;
&lt;img src=&quot;/images/shrine/closeup-up.jpg&quot; alt=&quot;Some closeup photos&quot; /&gt;
&lt;img src=&quot;/images/shrine/closeup-gripper.jpg&quot; alt=&quot;Some closeup photos&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Wilbert tells more about the mechanical part on &lt;a href=&quot;https://meccanokinematics.net/meccano-rubiks-shrine/&quot;&gt;his website&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;internals-what-can-we-do-better&quot;&gt;Internals: What can we do better?&lt;/h2&gt;

&lt;p&gt;We were very satisfied with our previous project, the FAC Solver, but we also
knew we could do better. We learned a lot about hardware parts like stepper
motors and drivers, microcontroller boards, and faced all kinds of unexpected
issues on the way. This experience helped us a lot when we were building this Meccano
Rubik’s Shrine.&lt;/p&gt;

&lt;h2 id=&quot;microcontrollers-use-the-right-tool-for-the-job&quot;&gt;Microcontrollers: Use the right tool for the job&lt;/h2&gt;

&lt;p&gt;In our previous project, the &lt;a href=&quot;/hacking/2015/08/18/fac-rubik-solver/&quot;&gt;FAC Solver&lt;/a&gt;,
(almost) everything was controlled by Raspberry Pi, including solving algorithm, color
recognition, movement scheduling, and pulse generation for the stepper motors.
We used Arduino only for reading the analog signal from LDRs in scanner device.&lt;/p&gt;

&lt;p&gt;While it did work quite well eventually, it turned out to be tricky when it comes
to controlling the motors. On the lower level, you can’t precisely control the time that
processor gives to your process on RPi. Due to the presence of other processes,
and, actually, the operating system itself, your program might be given different
chunks of CPU time, which makes the generated pulse train unstable. For lower speeds,
it is not a problem, but if the motor speed is high enough, it can be killing for
performance: motors start hesitating. To overcome this problem, we decided to use
an Arduino board for the actual motor control.&lt;/p&gt;

&lt;p&gt;We’ve come a long way with the PCB board. The first fersion looked something
like this (using Arduino Uno):&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/shrine/pcb-first.jpg&quot; alt=&quot;The first version of PCB board&quot; /&gt;
&lt;em&gt;The first version of PCB board&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;We chose &lt;a href=&quot;https://www.arduino.cc/en/Main/ArduinoBoardProMini&quot;&gt;Arduino Pro Mini&lt;/a&gt;
because it is probably the most compact one. We made the first working prototype
with it, and later replaced it with the &lt;a href=&quot;https://www.pjrc.com/teensy/&quot;&gt;Teensy 3.2&lt;/a&gt;,
which looks and feels almost identical, but has better performance.
We also used &lt;a href=&quot;https://www.pololu.com/product/2133&quot;&gt;DRV8825&lt;/a&gt; for driving the stepper
motors. So eventually, the PCB became this (the red thing on the left is a voltage
level trigger needed for the LEDs):&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/shrine/closeup-inner.jpg&quot; alt=&quot;The PCB board behind the door on the back side&quot; /&gt;
&lt;em&gt;PCB board behind the door on the back side&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Arduino gave us much more stable pulse train, which allowed to reach higher
speeds. We also used the great &lt;a href=&quot;http://www.airspayce.com/mikem/arduino/AccelStepper/&quot;&gt;AccelStepper&lt;/a&gt;
library that made movements smoother and more reliable. As before, I used the
&lt;a href=&quot;https://github.com/interactive-matter/MinProtocol&quot;&gt;MIN protocol&lt;/a&gt; for RPi-Arduino
communication.&lt;/p&gt;

&lt;h2 id=&quot;firmware-optimize-everything&quot;&gt;Firmware: Optimize everything!&lt;/h2&gt;

&lt;p&gt;Since software part was my main responsibility in this project, the firmware was
the very first thing I cared about. Pure RPi/Python implementation of FAC Solver
proved to have a number of nasty caveats, so I wanted to do it right this time.
I braced myself and prepared for the challenge of embedded systems programming,
ready to implement things from scratch for saving a few bytes of RAM.&lt;/p&gt;

&lt;h3 id=&quot;movements&quot;&gt;Movements&lt;/h3&gt;

&lt;p&gt;Probably the most difficult part was the movement optimization. The solving algorithm
produces a solution sequence on Raspberry Pi, which then needs to be translated into
a sequence of gripper movements. Then this data has to be transferred to the Arduino
board because actual electrical pulses are generated there. I wanted to make
the algorithm generic so that I could use it in other machine configurations
(with 4 or even 6 grippers). This added to the complexity of the problem.&lt;/p&gt;

&lt;p&gt;Minimizing the overall solving time is not obvious. For example,
in a 3-gripper setup like this, to rotate the Front side of the cube we need to
rotate the whole cube first. We can rotate it around the vertical or horizontal axis.
Whichever is better (leads to faster solution) depends on the current state of
the grippers and on subsequent moves.&lt;/p&gt;

&lt;iframe height=&quot;170&quot; width=&quot;200&quot; scrolling=&quot;no&quot; frameborder=&quot;0&quot; src=&quot;https://rubiks3x3.com/algorithm/?moves=xDidXiyRirYiYLily&amp;amp;sett=100001&amp;amp;speed=1&amp;amp;bg=ffffff&quot;&gt;&lt;/iframe&gt;

&lt;h3 id=&quot;synchronization&quot;&gt;Synchronization&lt;/h3&gt;

&lt;p&gt;Generally, all grippers can move simultaneously. To do this, we need to have
multiple parallel execution flows. Since Arduino is single-threaded,
it has to be done with &lt;a href=&quot;https://en.wikipedia.org/wiki/Coroutine&quot;&gt;coroutine&lt;/a&gt;-like mechanism.
On the other hand, due to the mechanical construction, some movements require
the other grippers to be in specific positions, which means that parallel workers
need to be able to block each other. Average solving process requires about 200
blocking operations.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/shrine/scheduling.png&quot; alt=&quot;Movement scheduling for 4-gripper setup&quot; /&gt;
&lt;em&gt;Movement scheduling for 4-gripper setup&lt;/em&gt;&lt;/p&gt;

&lt;h3 id=&quot;memory-management&quot;&gt;Memory management&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;https://www.arduino.cc/en/Main/ArduinoBoardProMini&quot;&gt;Arduino Pro Mini&lt;/a&gt;,
which we used initially, has only 2KB of RAM. After initializing
objects for serial communication and motor control, there are approximately 200 bytes
(!) left for local variables in actual business logic. With such limited
resources, I could afford to store only absolutely vital data. Everything else had
to be buffered in Raspberry Pi and pushed to Arduino via serial connection
during the run. Pure serial communication is quite expensive, so it was causing
an additional slowdown of the solving process.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/shrine/arduino-compile.png&quot; alt=&quot;Arduino Pro Mini compilation result&quot; /&gt;
&lt;em&gt;Arduino Pro Mini compilation result&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;It is (kinda) possible to write C++ code for Arduino, but the dialect is very limited:
for the sake of memory it doesn’t have the C++ standard library, exceptions, etc.,
and in most cases it is a bad idea to use things like dynamic allocation. For
spoiled developers like me, it is really tough to refrain from using abstract classes
with a bunch of virtual methods and dynamically allocated arrays. It is really hard
(I mean &lt;em&gt;freaking hard&lt;/em&gt;) to debug because you never see the exceptions: it never
fails explicitly, it just starts working weird at some point, and you can only
guess what went wrong, especially if you have memory-related problems. I ended up
with quite hacky virtual method implementation based on switch statements and
static variables, which was lame, but straightforward.&lt;/p&gt;

&lt;p&gt;Later, we changed the Arduino Pro Mini with &lt;a href=&quot;https://www.pjrc.com/store/teensy32.html&quot;&gt;Teensy 3.2&lt;/a&gt;
which was a great improvement. Not only it is &lt;em&gt;way faster&lt;/em&gt; (thanks to ARM processor),
and has &lt;em&gt;&lt;strong&gt;64KB&lt;/strong&gt;&lt;/em&gt; of RAM, but it has exactly the same form-factor as Arduino Pro Mini.
We didn’t even need to change the PCB connectors!&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/shrine/arduino-vs-teensy.jpg&quot; alt=&quot;Arduino Pro Mini vs Teensy 3&quot; /&gt;
&lt;em&gt;Arduino Pro Mini vs Teensy 3&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Well, 64K ought to be enough for anybody, at least in Arduino world :) With all this
memory available, I could easily store information about the whole movement sequence at once.
No additional serial communication + increased speed = faster and smoother solving.&lt;/p&gt;

&lt;h2 id=&quot;camera-come-on-its-the-21st-century&quot;&gt;Camera: Come on, it’s the 21st century!&lt;/h2&gt;

&lt;p&gt;Apart from the motor controlling, the biggest technical change in Rubik’s Shrine,
compared to the FAC Solver, was the scanning part. While it was fun to play with
the low-end scanner made from LDRs and LEDs, it was
&lt;a href=&quot;/hacking/2015/08/18/fac-rubik-solver/#capacitor-based-scanner&quot;&gt;quite&lt;/a&gt; a
&lt;a href=&quot;/hacking/2015/08/18/fac-rubik-solver/#arduino-controlled-scanner&quot;&gt;challenge&lt;/a&gt;
to deal with ambient light and do the color balancing. It did fit well the brutal
nature of the machine but obviously was not the most modern approach.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/shrine/flashlight.jpg&quot; alt=&quot;Camera with flashlight on&quot; /&gt;
&lt;em&gt;Camera with flashlight on&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This time, we went fancy and used the Raspberry Pi Camera for color scanning the cube.
Since it automatically adapts to the lighting conditions, it solved pretty much
all of those color recognition problems (we had to use bright LED flashlight though).
All I needed to do was to carefully pick the average colors from specific regions on the photos.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/shrine/scan-photo.png&quot; alt=&quot;The photo of the cube with marked regions for color recognition&quot; /&gt;
&lt;em&gt;The photo of the cube with marked regions for color recognition&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;As a bonus, each run is recorded on video and available on the Archive page in the
touch interface. It was also quite handy during debugging.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/shrine/video.jpg&quot; alt=&quot;Every run is captured on video&quot; /&gt;
&lt;em&gt;Every run is captured on video&lt;/em&gt;&lt;/p&gt;

&lt;h2 id=&quot;pi-display-the-final-touch&quot;&gt;Pi Display: the Final Touch&lt;/h2&gt;

&lt;p&gt;I just couldn’t resist. Not so long before we started this project, Pi Foundation
&lt;a href=&quot;https://www.raspberrypi.org/blog/the-eagerly-awaited-raspberry-pi-display/&quot;&gt;announced&lt;/a&gt;
an awesome 7-inch touch screen for Raspberry Pi that perfectly fitted our model.
I was pleasantly surprised that it just worked: after a couple of nights I had
a nice-looking touch UI written in &lt;a href=&quot;https://kivy.org/&quot;&gt;Kivy&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/shrine/touchscreen-solving.jpg&quot; alt=&quot;Touch UI (Solving)&quot; /&gt;
&lt;img src=&quot;/images/shrine/touchscreen-scanresult.jpg&quot; alt=&quot;Touch UI (Scan results)&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Having a touch screen, I was able to add some extra functionality to the machine.
In addition to a nice presentation of the scanning and solving results, it is
possible to make some pretty &lt;a href=&quot;https://ruwix.com/the-rubiks-cube/rubiks-cube-patterns-algorithms/&quot;&gt;patterns&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/shrine/patterns.png&quot; alt=&quot;Touch UI (Patterns)&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I also made a special debug screen for manual control that practically made it
unnecessary to use a laptop for testing.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/shrine/debugscreen.png&quot; alt=&quot;Touch UI (Debug screen)&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;public-demonstrations-and-final-words&quot;&gt;Public demonstrations and final words&lt;/h2&gt;

&lt;p&gt;When the Rubik’s Shrine was almost finished, we presented it on &lt;a href=&quot;http://www.skegex.nmmg.org.uk/&quot;&gt;SkegEx 2016&lt;/a&gt;,
the annual Meccano exhibition in Skegness, England. We even managed to win the
5th prize, which is quite something considering that SkegEx is generally more
about traditional mechanics rather than robotics and electronics.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/shrine/skegex-5.jpg&quot; alt=&quot;SkegEx photo&quot; /&gt;
&lt;img src=&quot;/images/shrine/skegex-2.jpg&quot; alt=&quot;SkegEx photo&quot; /&gt;&lt;/p&gt;

&lt;p&gt;But the biggest prize for us were the gleaming eyes of kids visiting the
exposition. It was wonderful to see them pulling their parents to our table
over and over, just to scramble the cube and start the machine once again.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/shrine/skegex-4.jpg&quot; alt=&quot;SkegEx photo&quot; /&gt;&lt;/p&gt;

&lt;p&gt;All in all, we are very satisfied with the result. Apart from all the hands-on experience,
for me, as a software developer, it was very fun to work on something physical,
something that I could see and touch. And I like to think that, thanks to Wilbert,
we could push engineering a little bit towards art. Because this is what it is all about.&lt;/p&gt;
</description>
        <pubDate>Fri, 12 Aug 2016 00:00:00 +0000</pubDate>
        <link>https://blog.zok.pw/hacking/2016/08/12/meccano-rubiks-shrine/</link>
        <guid isPermaLink="true">https://blog.zok.pw/hacking/2016/08/12/meccano-rubiks-shrine/</guid>
      </item>
    
      <item>
        <title>3rd-party cookies in practice</title>
        <description>&lt;p&gt;Recent days I was building a cross-site communication tool.
So I was looking into &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS&quot;&gt;CORS&lt;/a&gt;, &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage&quot;&gt;window.postMessage&lt;/a&gt; and other relevant stuff.&lt;/p&gt;

&lt;p&gt;While these mechanisms seem to be well-documented and have simple APIs,
handling 3rd-party cookies is a little bit vague. In particular, I was interested
in &lt;em&gt;What exactly does it mean when user disables 3rd-party cookies?&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;It turned out not so complicated, but you don’t know this until you actually have
to use it, so hopefully this will help someone in future.&lt;/p&gt;

&lt;h2 id=&quot;questions&quot;&gt;Questions&lt;/h2&gt;

&lt;p&gt;First of all, what is a 3rd-party cookie anyway? Common sense tells us this must be
cookies in scope of domains other than the current page. But this is quite vague.
Does it mean we only cannot set new cookies, should we be able to read the old ones?
How should browser handle cookies set from Javascript? Does this rule affect requests
triggered by images and other resources? How does it affect CORS requests? What about localStorage and sessionStorage? And how do we detect that user has disabled 3rd-party cookies?&lt;/p&gt;

&lt;h2 id=&quot;answers&quot;&gt;Answers&lt;/h2&gt;

&lt;p&gt;I’ve done a quick testing trying to answer those questions and here is what I found.
Everything was checked on Firefox 41.0.2, Google Chrome 46.0.2490.71, and Safari 9.0 under Mac OS El Capitan.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;First of all, when 3rd-party cookies are disabled, browsers &lt;strong&gt;&lt;em&gt;do not save, nor send&lt;/em&gt;&lt;/strong&gt;
cookies for domains other than the top-level window (current page). This affects all cross-domain requests including resources (e.g. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;img&amp;gt;&lt;/code&gt; tags), iframes, and XMLHttpRequests (including CORS).
This is something that you would expect, actually.&lt;/li&gt;
  &lt;li&gt;If there is a cookie that was set previously as a 1st-party, it won’t be used.&lt;/li&gt;
  &lt;li&gt;Javascript in cross-origin iframe cannot set cookies as well. No exceptions
will be thrown, but &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;document.cookie&lt;/code&gt; &lt;strong&gt;&lt;em&gt;will always return an empty string&lt;/em&gt;&lt;/strong&gt;, even
if you set it to something.&lt;/li&gt;
  &lt;li&gt;No cookies in CORS requests too, even if you use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.withCredentials&lt;/code&gt; parameter. Cookie
header from server is just ignored.&lt;/li&gt;
  &lt;li&gt;3rd-level subdomains and sibling 3rd-level subdomains are not considered 3rd-party: foo.example.com can load an iframe pointing to bar.example.com and it will be allowed to set cookies.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;localStorage&lt;/code&gt;/&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sessionStorage&lt;/code&gt; is a bit more tricky:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;in Google Chrome any attempt to access &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;localStorage&lt;/code&gt; will result in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DOMException&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;in Safari, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;localStorage.getItem()&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;localStorage&lt;/code&gt; itself don’t raise errors, but
if you try to use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;localStorage.setItem()&lt;/code&gt;, it will give you a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;QuotaExceededError&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;in Firefox, localStorage and sessionStorage are still working inside iframes.
So you can save stuff in localStorage, and it will stay there. However, this
&lt;a href=&quot;https://bugzilla.mozilla.org/show_bug.cgi?id=536509&quot;&gt;will change&lt;/a&gt; in upcoming Firefox 43.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;safari-pitfall&quot;&gt;Safari pitfall&lt;/h2&gt;

&lt;p&gt;The term “3rd-party cookie” (actually “3rd-party origin”), seems to have different
meanings across browsers. For example, Safari by default &lt;em&gt;does&lt;/em&gt; send cookies
to third-party domains, if it was visited before. There is a &lt;a href=&quot;http://labs.fundbox.com/third-party-cookies-with-ie-at-2am/&quot;&gt;very nice story&lt;/a&gt;
about that by Alex Volkov. Worth reading.&lt;/p&gt;

&lt;h2 id=&quot;detection&quot;&gt;Detection&lt;/h2&gt;

&lt;p&gt;If you need to detect if 3rd-party cookies are enabled, one option is to take
advantage of always-empty &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;document.cookie&lt;/code&gt;. Just create an iframe pointing to
a page on another domain like this:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;html&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;randStr&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(){&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Math&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;36&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;substring&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;thirdPartyCookiesEnabled&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;k&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;randStr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;v&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;randStr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;cookie&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;k&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;cookie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;indexOf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;k&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;thirdPartyCookiesEnabled&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;thirdPartyCookiesEnabled&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;alert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;thirdPartyCookiesEnabled&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;body&amp;gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h2 id=&quot;workarounds&quot;&gt;Workaround(s)&lt;/h2&gt;

&lt;p&gt;If you really need to access cookies on another domain, there are some ways to do so.&lt;/p&gt;

&lt;p&gt;The most bullet-proof one is just make sure that cookies are 1st-party. Essentially, it means &lt;strong&gt;&lt;em&gt;redirects&lt;/em&gt;&lt;/strong&gt;. So you can redirect user to a page on another domain, set the
cookie there, and then immediately redirect him back. This is not the best user
experience, but it works.&lt;/p&gt;

&lt;p&gt;In some cases it is possible to use a subdomain for setting cookies. If you have
access to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;example.com&lt;/code&gt; DNS, you can create a CNAME record &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;foo.example.com&lt;/code&gt;,
pointing to a server that needs to set cookies. Provided that cookies are not
host-only (that is, they are set &lt;em&gt;with&lt;/em&gt; domain attribute specified), you can
share cookies between those domains.&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;What I have described above, is just a top of the iceberg. It is sufficient to
understand the immediate side-effects of blocked cookies, but if you are really going
to address this issue, you will most likely come across many caveats. And I believe
there is no ideal solution to this problem.&lt;/p&gt;

&lt;p&gt;On the other hand, the 3rd-party cookies problem has been there for a while, and
now you can find lots of nice articles and posts with all kinds of reusable ideas. Check out
&lt;a href=&quot;http://labs.fundbox.com/third-party-cookies-with-ie-at-2am/&quot;&gt;this post by Alex Volkov&lt;/a&gt; for example.&lt;/p&gt;
</description>
        <pubDate>Wed, 21 Oct 2015 00:00:00 +0000</pubDate>
        <link>https://blog.zok.pw/web/2015/10/21/3rd-party-cookies-in-practice/</link>
        <guid isPermaLink="true">https://blog.zok.pw/web/2015/10/21/3rd-party-cookies-in-practice/</guid>
      </item>
    
      <item>
        <title>FAC system Rubik's Cube solver</title>
        <description>&lt;h2 id=&quot;update-2&quot;&gt;&lt;em&gt;Update 2&lt;/em&gt;:&lt;/h2&gt;

&lt;p&gt;Make sure you check out the successor of this project - &lt;a href=&quot;http://blog.zok.pw/hacking/2016/08/12/meccano-rubiks-shrine/&quot;&gt;Meccano Rubik’s Shrine&lt;/a&gt;, it’s even more awesome!&lt;/p&gt;

&lt;h2 id=&quot;update&quot;&gt;&lt;em&gt;Update:&lt;/em&gt;&lt;/h2&gt;

&lt;p&gt;Since this post was published, we upgraded the scanner on the machine, and now the
scanning process takes way less time. Check out the Hardware section.&lt;/p&gt;

&lt;iframe src=&quot;https://www.youtube.com/embed/vpAKfSYueMI&quot; width=&quot;100%&quot; height=&quot;360px&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; mozallowfullscreen=&quot;&quot; webkitallowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;

&lt;h2 id=&quot;tldr&quot;&gt;TL;DR&lt;/h2&gt;

&lt;p&gt;For those who don’t feel like reading any further, here are some links:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://meccanokinematics.net/fac-cube-solver/&quot;&gt;Project page on Meccano Kinematics&lt;/a&gt; website&lt;/li&gt;
  &lt;li&gt;Solving algorithm implementation: &lt;a href=&quot;https://github.com/muodov/kociemba&quot;&gt;GitHub&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Final video: &lt;a href=&quot;https://youtu.be/vpAKfSYueMI&quot;&gt;YouTube&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Other Wilbert’s machines: &lt;a href=&quot;https://www.youtube.com/user/Meccanokinematics&quot;&gt;YouTube channel&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;More about Meccano and FAC system: &lt;a href=&quot;https://meccanokinematics.net/&quot;&gt;Meccano Kinematics&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;introduction&quot;&gt;Introduction&lt;/h2&gt;

&lt;p&gt;About three months ago I was introduced to &lt;a href=&quot;http://wiswin.nl&quot;&gt;Wilbert Swinkels&lt;/a&gt;.
Normally you don’t meet people with such an amazing blend of skills and passion
for creating &lt;a href=&quot;https://www.youtube.com/user/Meccanokinematics&quot;&gt;awesomeness&lt;/a&gt;, so
this was a great luck for me when he offered to help him with the &lt;a href=&quot;https://meccanokinematics.net/fac-cube-solver/&quot;&gt;Rubik’s cube
solving machine&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The machine looked absolutely marvelous. Just like all other Wilbert’s creations,
it is a piece of art by itself. Besides, color cubes had already &lt;a href=&quot;https://play.google.com/store/apps/details?id=com.muodov.papercube&quot;&gt;carried me away once&lt;/a&gt;. With no doubt,
I accepted his offer.&lt;/p&gt;

&lt;p&gt;By the time I met Wilbert, he had been working on the machine for almost five years.
It is quite an &lt;a href=&quot;https://meccanokinematics.net/fac-cube-solver/&quot;&gt;interesting story&lt;/a&gt;,
with a number of great successes, disappointing failures, and invaluable experience.
When I started working on it, most of the hardware-related problems had already been solved.
There was even a proof-of-concept program for Arduino that could control
grippers and scanner. However, the software part essentially did not exist.&lt;/p&gt;

&lt;h2 id=&quot;hardware&quot;&gt;Hardware&lt;/h2&gt;

&lt;h3 id=&quot;main-board&quot;&gt;Main board&lt;/h3&gt;
&lt;p&gt;First of all, we decided to replace Arduino board with Raspberry Pi. This
was for a number of reasons: it seemed that we didn’t really need low-level capabilities
of Arduino, and I didn’t feel like writing everything in the limited dialect
of C++ used by Arduino. Moreover, sophisticated cube solving algorithms required
quite a lot of memory, that Arduino cannot provide.&lt;/p&gt;

&lt;p&gt;The standard version of Raspberry Pi did not have enough GPIO pins, so
we got a &lt;a href=&quot;https://www.raspberrypi.org/products/compute-module-development-kit/&quot;&gt;Compute Module Development Kit&lt;/a&gt;. That one is really cool: not only it has a way more
convenient pinout with 45 controllable pins, but it also has two camera
slots. We were so happy with it that we bought two more boards for future projects :)&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/raspberry.jpg&quot; alt=&quot;Raspberry Pi mounted on the machine&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;capacitor-based-scanner&quot;&gt;Capacitor-based scanner&lt;/h3&gt;

&lt;p&gt;Of course, this change from Arduino to Raspberry required some changes in circuits
as well. One of the problems was that you can only get 3.3V from Raspberry’s
GPIO pin whereas Arduino gives you 5V. Fortunately, our motor drivers still worked
under lower voltage, so we only needed to change resistors in scanner circuit.&lt;/p&gt;

&lt;p&gt;Much more annoying problem was that Raspberry didn’t have analog input. This
was crucial for us because our color scanner was based on LDRs. On Arduino, it
was very easy to read the value from LDR, but to do the same in Raspberry one
has to use external devices or make some hacks. After all, we took
&lt;a href=&quot;http://www.raspberrypi-spy.co.uk/2012/08/reading-analogue-sensors-with-one-gpio-pin/&quot;&gt;this approach&lt;/a&gt;.
Basically, instead of reading the voltage, we are counting the time needed to
charge the capacitor. This time is proportional to LDR resistance, so in the end
we can estimate its value.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/ldrcap.jpg&quot; alt=&quot;LDR reading hack schematics&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Of course, it was not reliable. Mainly, because there
are a lot of background processes running alongside your program on Raspberry, so
you always get random deviations in readings. This is not so important if you
just need to &lt;em&gt;detect&lt;/em&gt; the light with LDR, but can have nasty consequences if you
want to compare the amounts of light. To circumvent this effect, we had to do
multiple readings every time, detect &lt;a href=&quot;https://en.wikipedia.org/wiki/Outlier&quot;&gt;outliers&lt;/a&gt;, 
and calculate an average between the remaining values.&lt;/p&gt;

&lt;p&gt;Here is the video of a machine with capacitor-based scanner:&lt;/p&gt;

&lt;iframe width=&quot;100%&quot; height=&quot;360px&quot; src=&quot;https://www.youtube.com/embed/z4iJ0hNQulo&quot; frameborder=&quot;0&quot; mozallowfullscreen=&quot;&quot; webkitallowfullscreen=&quot;&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;

&lt;h3 id=&quot;arduino-controlled-scanner&quot;&gt;Arduino-controlled scanner&lt;/h3&gt;

&lt;p&gt;Though we made the capacitor-based scheme working, it was ridiculously slow.
It would take about two minutes to scan the whole cube. So we decided to get a
small Arduino board specifically to control the scanner.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/arduino-scanner.jpg&quot; alt=&quot;Arduino-based scanner&quot; /&gt;&lt;/p&gt;

&lt;p&gt;It turned out to be very easy to connect Arduino to Raspberry and make them work together.
We only needed two wires, &lt;a href=&quot;https://www.sparkfun.com/products/12009&quot;&gt;this&lt;/a&gt; tiny
little level converter in between - and voila: we have a serial connection between
the boards. I also used cool &lt;a href=&quot;https://github.com/interactive-matter/MinProtocol&quot;&gt;Min protocol&lt;/a&gt; on top of serial to make it more convenient.&lt;/p&gt;

&lt;p&gt;This change dramatically decreased the time required for scanning the cube.
Since Arduino can read the voltage directly from the LDR, we don’t need to wait
until capacitor is charged, and we don’t need to do it multiple times anymore!&lt;/p&gt;

&lt;h2 id=&quot;solving-algorithm&quot;&gt;Solving algorithm&lt;/h2&gt;

&lt;p&gt;The most important part of the solving program is a solving algorithm. There is
&lt;a href=&quot;https://en.wikipedia.org/wiki/Optimal_solutions_for_Rubik%27s_Cube&quot;&gt;quite some&lt;/a&gt; serious mathematical work already done on Rubik’s cube. If you are
into algebra and combinatorics, it might be pretty interesting.
For instance, I was very surprised that the God number (the exact lower bound for a number of moves needed
to solve arbitrary cube) was &lt;a href=&quot;http://cube20.org&quot;&gt;only found in 2010&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;While we did care about the speed of solving, we had to take into account that
Raspberry Pi has limited computational powers. Therefore, we could not use the
&lt;a href=&quot;http://cflmath.com/Rubik/optimal_solver.html&quot;&gt;optimal solver&lt;/a&gt; as it would require a lot of time for bruteforcing. After all, we ended up with a great &lt;a href=&quot;http://kociemba.org/cube.htm&quot;&gt;two-phase algorithm&lt;/a&gt;
by Herbert Kociemba.&lt;/p&gt;

&lt;p&gt;The original implementation of the two-phase algorithm is only available in Java. First thing
I did was translating it to Python. It turned out to be quite easy and
straightforward. However, in the beginning I didn’t realize that solving the Rubik’s
cube requires &lt;strong&gt;a lot&lt;/strong&gt; of resources. When I first started Python solver, it took
about one minute (!) to find a solution on my MacBook.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://pypy.org&quot;&gt;PyPy&lt;/a&gt; showed much better results, it reduced the time to one second
on the laptop, but it was still about a minute on Raspberry. After a day trying
different ways to speed up Python code, I decided to go for a pure C version.
This solved all problems: now it takes about one second to find a solution, even
on Raspberry Pi. This was good enough for this project.&lt;/p&gt;

&lt;p&gt;I have published both Python and C versions of the solver on &lt;a href=&quot;https://github.com/muodov/kociemba&quot;&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;moving-the-assembly&quot;&gt;Moving the assembly&lt;/h2&gt;

&lt;p&gt;Next step was to build a program that would actually control the machine.
This implied moving the motors, carefully handling gear ratios, keeping in mind
the limitations forced by the assembly (for example, side grippers can only be rotated
when the bottom gripper is in certain position). Some additional code was
required to handle user input (machine has a multi-purpose controlling button),
and to optimize gripper movements. All in all, there was nothing special
in this part, just a routine software development.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/testshell.png&quot; alt=&quot;test shell&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I also made a handy interactive shell for debugging purposes. It made testing very
convenient and I could easily debug parts of the program. To debug the scanning,
I rendered nice images to visualize the readings.&lt;/p&gt;

&lt;h2 id=&quot;scanning-and-color-recognition-aka-devil-in-details&quot;&gt;Scanning and color recognition (a.k.a. devil in details)&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;Update:&lt;/strong&gt; This section describes the previous version of the scanner (capacitor-based).
New Arduino-controlled scanner made the readings much more reliable and fast. However,
color clustering algorithm remained the same.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Up to this point, things were exciting, but not difficult. And we were ready to
open champagne to celebrate our great success. The only thing left was to
recognize the colors using an LDR-based scanner. The idea was as simple as it could
be: we had 3 sets of LEDs (&lt;span class=&quot;red&quot;&gt;red&lt;/span&gt;, &lt;span class=&quot;green&quot;&gt;green&lt;/span&gt;, and &lt;span class=&quot;blue&quot;&gt;blue&lt;/span&gt;) which we turned on one by one
and captured reflected light with LDRs. Essentially, this gave us RGB values
which we could use for color recognition. We already had a proof-of-concept
program which seemed to work well on Arduino, so we didn’t expect surprises here.&lt;/p&gt;

&lt;p&gt;However, the scanning process turned out to be the most challenging part of the
whole project. It took almost 2 months to accomplish this.&lt;/p&gt;

&lt;h3 id=&quot;problems&quot;&gt;Problems&lt;/h3&gt;

&lt;p&gt;First of all, we had to fundamentally change the circuit of the
scanner, as described above.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/sensorboard.jpg&quot; alt=&quot;sensor board&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Due to the hack with capacitors, it introduced random errors in our readings.
So the first thing was to do several readings in a row and get rid of
&lt;a href=&quot;https://en.wikipedia.org/wiki/Outlier&quot;&gt;outliers&lt;/a&gt;. Once we did that, we could
get something like following (these are results of scanning a solved cube in a dark room):&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/readings_uncalibrated/red.png&quot; alt=&quot;red&quot; /&gt;
&lt;img src=&quot;/images/readings_uncalibrated/blue.png&quot; alt=&quot;blue&quot; /&gt;
&lt;img src=&quot;/images/readings_uncalibrated/white.png&quot; alt=&quot;white&quot; /&gt;
&lt;img src=&quot;/images/readings_uncalibrated/orange.png&quot; alt=&quot;orange&quot; /&gt;
&lt;img src=&quot;/images/readings_uncalibrated/green.png&quot; alt=&quot;green&quot; /&gt;
&lt;img src=&quot;/images/readings_uncalibrated/yellow.png&quot; alt=&quot;yellow&quot; /&gt;&lt;/p&gt;

&lt;p&gt;As you can see, this is far from ideal. First, results for the same color in different
positions significantly differ due to the fact that every LDR and every LED is
pointing in a slightly different direction. Second, sometimes different colors
can have overlapping value ranges (for example, red and orange often appear the same).
Finally, all the readings are highly sensitive to the ambient light.&lt;/p&gt;

&lt;p&gt;Due to the random deviation in readings and ambient light, dispersion is quite
extensive as seen on the following (&lt;em&gt;&lt;strong&gt;clickable&lt;/strong&gt;&lt;/em&gt;) plot (by the way, you should check out
&lt;a href=&quot;https://plot.ly&quot;&gt;plot.ly&lt;/a&gt;, it is awesome):&lt;/p&gt;

&lt;div&gt;
    &lt;a href=&quot;https://plot.ly/~muodov/34/&quot; target=&quot;_blank&quot; title=&quot;RGB uncalibrated&quot; style=&quot;display: block; text-align: center;&quot;&gt;&lt;img src=&quot;https://plot.ly/~muodov/34.png&quot; alt=&quot;RGB uncalibrated&quot; style=&quot;max-width: 100%;width: 792px;&quot; width=&quot;792&quot; onerror=&quot;this.onerror=null;this.src='https://plot.ly/404.png';&quot; /&gt;&lt;/a&gt;
    &lt;script data-plotly=&quot;muodov:34&quot; src=&quot;https://plot.ly/embed.js&quot; async=&quot;&quot;&gt;&lt;/script&gt;
&lt;/div&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;UPD:&lt;/strong&gt; Arduino-based scanner showed much better results:&lt;/em&gt;&lt;/p&gt;
&lt;div&gt;
    &lt;a href=&quot;https://plot.ly/~muodov/115/&quot; target=&quot;_blank&quot; title=&quot;Arduino scanner (after calibration)&quot; style=&quot;display: block; text-align: center;&quot;&gt;&lt;img src=&quot;https://plot.ly/~muodov/115.png&quot; alt=&quot;Arduino scanner (after calibration)&quot; style=&quot;max-width: 100%;width: 792px;&quot; width=&quot;792&quot; onerror=&quot;this.onerror=null;this.src='https://plot.ly/404.png';&quot; /&gt;&lt;/a&gt;
    &lt;script data-plotly=&quot;muodov:115&quot; src=&quot;https://plot.ly/embed.js&quot; async=&quot;&quot;&gt;&lt;/script&gt;
&lt;/div&gt;

&lt;h3 id=&quot;clustering-color-values&quot;&gt;Clustering color values&lt;/h3&gt;

&lt;p&gt;Given these results, I came up with two fundamentally different approaches to color recognition:&lt;/p&gt;

&lt;h4 id=&quot;1-color-ranging&quot;&gt;1. Color ranging&lt;/h4&gt;

&lt;p&gt;Find the ranges (a cubical region in RGB color space) for each color and just
look for a suitable range every time we scan a facelet. Each combination
of facelet position, LDR, and LED color should be handled separately. While this
approach is quite straightforward and easy to implement, we have to stick to
particular cube colors. And in the end it turned out to be not usable under
different ambient light conditions since some ranges overlap and, therefore,
become ambiguous.&lt;/p&gt;

&lt;h4 id=&quot;2-true-clustering&quot;&gt;2. True clustering&lt;/h4&gt;

&lt;p&gt;Make artificial tweaks on raw values to make the same colors in different
positions look similar. If we can do this, we can apply some
&lt;a href=&quot;https://en.wikipedia.org/wiki/Cluster_analysis&quot;&gt;clustering algorithm&lt;/a&gt; to distinguish
colors. The most difficult part here was to figure out proper tweaking coefficients
that would work well under different circumstances and ambient conditions.
Unfortunately, this approach was not usable as well, due to probability nature
of clustering algorithms: they are mostly intended to give a “decent” solution,
but not the exact one.&lt;/p&gt;

&lt;h4 id=&quot;compromise&quot;&gt;Compromise&lt;/h4&gt;

&lt;p&gt;Each of the approaches above has its pros and cons. So after all we used a
sort of a mixed algorithm:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;we do some artificial calibrations based on gathered statistics to normalize the readings&lt;/li&gt;
  &lt;li&gt;convert values from RGB to HSV&lt;/li&gt;
  &lt;li&gt;find a white cluster, by taking out 9 facelets with the least Saturation&lt;/li&gt;
  &lt;li&gt;artificially increase saturation for all other facelets&lt;/li&gt;
  &lt;li&gt;do a simple clustering of remaining (non-white) colors by comparing to center facelets&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;ambient-light-elimination&quot;&gt;Ambient light elimination&lt;/h3&gt;

&lt;p&gt;Even with a decent clustering algorithm, scanning was still tricky due to the
ambient light. What seemed to work perfectly in a dark room, failed miserably
under a bright sun, and vice versa. So the final step was to get rid of the
ambient light. Wilbert had to do quite some work on the scanner module
to isolate LDRs from ambient light. It took 3 iterations: each time we thought
it was ready, but each time we would find another hole through which ambient light
could reach the LDRs:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/scanner.jpg&quot; alt=&quot;scanner&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;summary-and-promises&quot;&gt;Summary and promises&lt;/h2&gt;

&lt;p&gt;This project was fun. It was very exciting to bring it to life step by step,
and such a pleasure to see it working after we put so much
effort in it. But this is nothing compared to how much we learned thanks to it.
What seemed to be pretty easy from the beginning, turned out to be very
tricky in details. I could not imagine I would have to learn statistical
methods and clustering algorithms, dust off my school notes on electronics
(though we only need the basics here), and discover a number of useful tools
and services along the way. This is why I am so happy I had a chance to work on this.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/rubikmachine.jpg&quot; alt=&quot;Rubik's Cube solver&quot; /&gt;&lt;/p&gt;

&lt;p&gt;That said, this machine was only a prototype. We didn’t contemplate to challenge
record-breaking robots in terms of solving speed, and we didn’t
really make the most of the hardware parts. But we will definitely go for it in
the next generation of this machine.&lt;/p&gt;
</description>
        <pubDate>Tue, 18 Aug 2015 00:00:00 +0000</pubDate>
        <link>https://blog.zok.pw/hacking/2015/08/18/fac-rubik-solver/</link>
        <guid isPermaLink="true">https://blog.zok.pw/hacking/2015/08/18/fac-rubik-solver/</guid>
      </item>
    
  </channel>
</rss>
