<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Faraaz Ali Khan</title>
    <description>The latest articles on DEV Community by Faraaz Ali Khan (@faraaz_alikhan).</description>
    <link>https://dev.to/faraaz_alikhan</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4206341%2F84674689-af76-4a8f-ae48-c1350d01316f.png</url>
      <title>DEV Community: Faraaz Ali Khan</title>
      <link>https://dev.to/faraaz_alikhan</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/faraaz_alikhan"/>
    <language>en</language>
    <item>
      <title>Our Robot Dog Stole My Phone (And I Built It)</title>
      <dc:creator>Faraaz Ali Khan</dc:creator>
      <pubDate>Mon, 26 Oct 2026 17:56:14 +0000</pubDate>
      <link>https://dev.to/faraaz_alikhan/our-robot-dog-stole-my-phone-and-i-built-it-3oia</link>
      <guid>https://dev.to/faraaz_alikhan/our-robot-dog-stole-my-phone-and-i-built-it-3oia</guid>
      <description>&lt;p&gt;&lt;em&gt;A storage basket, an ESP32, a webcam that knows when you're slacking, and a motor driver that died at 5 AM. The story of Fido, from OxHacks 2026&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I put my phone inside the dog and started a focus session.&lt;/p&gt;

&lt;p&gt;Then I let my eyes drift away from the screen. Eight seconds, that's all it took. A little white basket on wheels turned towards me. It nudged. I ignored it. It barked. I definitely ignored it.&lt;/p&gt;

&lt;p&gt;Then it drove away with my phone still inside it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fv6itmjorb77wxqj6mmf7.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fv6itmjorb77wxqj6mmf7.gif" alt="Fido driving off" width="360" height="646"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That's Fido. Four of us from King's College London built him at OxHacks 2026. He's a robot dog with one job: guard your focus, and if you won't, guard your phone.&lt;/p&gt;

&lt;p&gt;Here's how a storage basket from a high-street shop ended up with trust issues ;)&lt;/p&gt;




&lt;h2&gt;
  
  
  The problem we couldn't stop thinking about
&lt;/h2&gt;

&lt;p&gt;The OxHacks theme was &lt;strong&gt;Time&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Everyone's first idea for "Time" is a timer. Pomodoro apps, calendars, to-do lists with AI glued on. We've all downloaded them. We've all ignored them.&lt;/p&gt;

&lt;p&gt;Here's the uncomfortable truth we kept landing on: &lt;strong&gt;focus tools fail because they live on the device that's distracting you.&lt;/strong&gt; Screen-time limits are one "Ignore Limit" tap away. You can't out-willpower a phone &lt;em&gt;with&lt;/em&gt; the phone.&lt;/p&gt;

&lt;p&gt;So what if the consequence was &lt;strong&gt;physical&lt;/strong&gt;? Something you can't swipe away. Something slightly ridiculous. Something with wheels.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What if your phone got taken hostage by a very small, very disappointed dog?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We locked the idea in at 4 PM on Saturday. We had no robot. We had no parts. We had about 15 hours.&lt;/p&gt;




&lt;h2&gt;
  
  
  How Fido decides you're slacking
&lt;/h2&gt;

&lt;p&gt;You start a 25-minute session in the web app and put your phone on Fido. From then on, Fido has a pair of eyes: a webcam running a focus detector on your laptop.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;You…&lt;/th&gt;
&lt;th&gt;Fido…&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Look at your work&lt;/td&gt;
&lt;td&gt;👀 Watches, quietly proud&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Look away for 8 s straight&lt;/td&gt;
&lt;td&gt;🐾 &lt;strong&gt;Nudges&lt;/strong&gt; you back&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Keep going for another 6 s&lt;/td&gt;
&lt;td&gt;🔊 &lt;strong&gt;Barks&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Keep going for another 8 s&lt;/td&gt;
&lt;td&gt;🏃 &lt;strong&gt;Runs away with your phone&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Catch him and tap his collar&lt;/td&gt;
&lt;td&gt;📱 Phone back. Session failed. Think about what you did.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Refocus during a nudge or a bark and he calms down. But once he's escaped, there's no bargaining. &lt;strong&gt;Running away can't earn you a completed session.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/.%2Fimages%2F18-app-session-flow.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/.%2Fimages%2F18-app-session-flow.png" alt="The app across a full session" width="800" height="400"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Waiting for your phone → "Hey, eyes over here." → "Fido ran off with your phone."&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  The eyes 👁️
&lt;/h3&gt;

&lt;p&gt;The detector uses &lt;strong&gt;MediaPipe's face landmarker&lt;/strong&gt; to estimate where your head is pointing. Everyone sits differently, so it doesn't use fixed angles. &lt;strong&gt;It spends the first 30 frames of every session learning what "you, focused" looks like&lt;/strong&gt;, then measures everything against that:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;pitch_diff&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;phone_pitch_delta&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;pitch_diff&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;phone_pitch_delta_with_eyes_down&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;eye_down&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;eyes_down_threshold&lt;/span&gt;
&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Looking Down at Phone/Lap (ΔPitch: +&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;pitch_diff&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;pitch_diff&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;look_up_pitch_delta&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Looking Up / Daydreaming (ΔPitch: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;pitch_diff&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;yaw_diff&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;look_away_yaw_delta&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Looking Away Sideways (ΔYaw: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;yaw_diff&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Looking at Screen&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On top of that, &lt;strong&gt;Presage's SmartSpectra SDK&lt;/strong&gt; tells us when you're &lt;em&gt;&lt;strong&gt;talking&lt;/strong&gt;&lt;/em&gt; and gives us 478 face landmarks, which we used to catch &lt;strong&gt;prolonged eye closure&lt;/strong&gt;. That's the 4-second micro-nap that hits around 4 AM. Ask us how we know.&lt;/p&gt;

&lt;p&gt;Our first version was &lt;em&gt;unbearable&lt;/em&gt;. It barked every time you blinked. Two fixes made it feel fair:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Slow to accuse, quick to forgive.&lt;/strong&gt; You need 8 seconds of sustained distraction before it counts. Look back and the timer drains &lt;strong&gt;3× faster&lt;/strong&gt; than it filled.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No flip-flopping.&lt;/strong&gt; "Distracted" kicks in instantly, but "focused again" needs &lt;strong&gt;2 straight seconds&lt;/strong&gt; of clean readings. No more nudge-watch-nudge-watch strobing.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And because a camera pointed at your face all night is a bit creepy: &lt;strong&gt;frames never leave your laptop.&lt;/strong&gt; The backend only ever sees "YES" or "NO" plus a reason string like &lt;code&gt;"Looking Down at Phone/Lap"&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  The body: a basket, two wheels, and paranoia
&lt;/h2&gt;

&lt;p&gt;Soon it was raining, then dark, and we were on a supply run to &lt;strong&gt;Ryman&lt;/strong&gt;, &lt;strong&gt;Flying Tiger&lt;/strong&gt; and &lt;strong&gt;Tesco&lt;/strong&gt;, looking at every object and asking &lt;em&gt;"could this be a dog?"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffdkdb3v5uev0enga1qbc.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffdkdb3v5uev0enga1qbc.jpg" alt="Supply run in the rain" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fv1iqe0htxyoq9nmj9nf1.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fv1iqe0htxyoq9nmj9nf1.jpg" alt="The haul" width="800" height="600"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Cardboard, a Ryman bag of possibilities, and biscuits. Biscuits are load-bearing.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The winner was a &lt;strong&gt;storage basket&lt;/strong&gt;. It's the right size, it's rigid, and it's ventilated (so it's basically a chassis). Inside: an &lt;strong&gt;ESP32&lt;/strong&gt;, a dual motor driver, and two geared motors.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fuyxofcnutk5zwsa6vfrk.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fuyxofcnutk5zwsa6vfrk.jpg" alt="Soldering inside the basket" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A robot built to drive away from people is, by design, a bit dangerous on a crowded hackathon table. So the firmware is paranoid. It asks the backend what to do &lt;strong&gt;once a second&lt;/strong&gt;, and &lt;strong&gt;if the answer is late, missing or weird, it stops:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;WiFi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;WL_CONNECTED&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;millis&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;lastReply&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;commandFresh&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Serial&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Connection stale -&amp;gt; STOP"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;commandFresh&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There's also a &lt;strong&gt;"Fido can move"&lt;/strong&gt; switch in the app that works as an emergency stop. Fail closed, always.&lt;/p&gt;

&lt;p&gt;And because hackathon Wi-Fi hands out a new IP every time you sneeze, Fido hosts his own &lt;strong&gt;&lt;code&gt;Fido-Setup&lt;/code&gt; hotspot&lt;/strong&gt; with a tiny config page. New laptop IP? Connect your phone to the dog, type it in, done. No reflashing.&lt;/p&gt;




&lt;h2&gt;
  
  
  The trick that let four people build one robot overnight
&lt;/h2&gt;

&lt;p&gt;Two of us were on hardware, two on software, all working at the same time. That only works if nobody is blocked on anybody else.&lt;/p&gt;

&lt;p&gt;Our answer was one tiny dictionary:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Robot hardware acts on this value only, so the hardware
# never needs to know the session rules.
&lt;/span&gt;&lt;span class="n"&gt;ROBOT_MODES&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Phase&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Phase&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IDLE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sleep&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Phase&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AWAITING_PHONE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;beckon_dock&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Phase&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FOCUSING&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;watch&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Phase&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NUDGE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;nudge&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Phase&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BARKING&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;bark&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Phase&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ESCAPED&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;run&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Phase&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PAUSED&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;rest&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Phase&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;COMPLETED&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;celebrate&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Phase&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FAILED&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;rest&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The robot never knows &lt;em&gt;why&lt;/em&gt; it's running. It only knows &lt;em&gt;that&lt;/em&gt; it should. So at 4 AM, when we decided "Fido shouldn't be able to go straight from running back to watching", the state machine changed and &lt;strong&gt;zero lines of firmware did.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Everything else follows one rule: &lt;strong&gt;the Flask backend is the single source of truth.&lt;/strong&gt; The webcam, the robot, your laptop and your phone all read and write one session. The Next.js app streams it live over &lt;strong&gt;Server-Sent Events&lt;/strong&gt; and has &lt;em&gt;no state machine of its own&lt;/em&gt;, so two screens can never disagree about how long you have left.&lt;/p&gt;

&lt;p&gt;And because the escalation logic runs on an &lt;strong&gt;injectable clock&lt;/strong&gt;, our tests fast-forward time instead of waiting. The whole nudge → bark → escape sequence is tested in milliseconds, and &lt;strong&gt;Playwright&lt;/strong&gt; drives the real journey end to end.&lt;/p&gt;




&lt;h2&gt;
  
  
  05:00: The motor driver dies
&lt;/h2&gt;

&lt;p&gt;At 4 AM we'd done the grown-up thing: stopped, written every remaining task down, split it into Priority 1/2/3, assigned owners, and drawn up a timeline that ended in "7:00 AM: Completed Product." We felt &lt;em&gt;great&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;At 5 AM, the motor driver fried.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fr711vqysybbakg4lu34g.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fr711vqysybbakg4lu34g.jpg" alt="Re-soldering the replacement driver" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;No shops open. Plan in tatters. So we swapped in a replacement, re-soldered it, and carried on. It cost us an hour of a perfect schedule, and it taught us this, now written into our firmware notes in capital letters: &lt;strong&gt;power the motors separately, never from the microcontroller, and share a ground.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Rewind: how the day actually started
&lt;/h2&gt;

&lt;p&gt;You don't get a robot dog at 5 AM without a 6:37 AM train.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F48f9qtutd2lvz74nfqxp.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F48f9qtutd2lvz74nfqxp.jpg" alt="On the train" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6vnqgwomr312j8ud3r0b.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6vnqgwomr312j8ud3r0b.jpg" alt="Sunrise from the train" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We got to Oxford two hours early, so we did what any responsible hackathon team does: sightseeing.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frrdyy0ugzt11w0j89rhh.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frrdyy0ugzt11w0j89rhh.jpg" alt="Radcliffe Camera" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We all also touched the Thames. For reasons.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fg96r6n7v6daypflmuycd.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fg96r6n7v6daypflmuycd.gif" alt="Touching the Thames" width="480" height="268"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fym691w046orhrydaihgg.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fym691w046orhrydaihgg.jpg" alt="Ducks" width="800" height="600"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;The ducks were unimpressed. Fido's first critics.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Then registration, merch, kickoff, and a bubble tea to fuel the 4-hour argument that eventually became Fido.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcltayhqj4nyksyr792j3.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcltayhqj4nyksyr792j3.jpg" alt="Kickoff" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Oh, and the clocks went back at 2 AM. A hackathon themed &lt;strong&gt;Time&lt;/strong&gt;, on the one night of the year with &lt;strong&gt;25 hours&lt;/strong&gt; in it. We spent the bonus hour soldering. Obviously.&lt;/p&gt;




&lt;h2&gt;
  
  
  What we'd tell you before your next hardware hackathon
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Agree on a contract between hardware and software in the first hour.&lt;/strong&gt; For us it was &lt;code&gt;phase → robot mode&lt;/code&gt;. It kept four people unblocked all night.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;One source of truth.&lt;/strong&gt; Every "but my screen says…" bug disappears when only one thing owns the state.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Make time injectable.&lt;/strong&gt; If your logic says "after 8 seconds", your tests shouldn't wait 8 seconds.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Noisy sensors need hysteresis.&lt;/strong&gt; Turning "on" and turning "off" should have different thresholds.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Robots should fail closed.&lt;/strong&gt; Stale command = stop. Always.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bring a spare motor driver.&lt;/strong&gt; Please. Learn from us.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  The humans behind the dog 🐾
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://www.linkedin.com/in/faraazalikhan/" rel="noopener noreferrer"&gt;Faraaz Ali Khan&lt;/a&gt;&lt;/strong&gt;: Vice President, KCL Tech Society&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://www.linkedin.com/in/dheer-maheshwari/" rel="noopener noreferrer"&gt;Dheer Maheshwari&lt;/a&gt;&lt;/strong&gt;: Head of Workshops, KCL Tech Society&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://www.linkedin.com/in/vayun-khare/" rel="noopener noreferrer"&gt;Vayun Khare&lt;/a&gt;&lt;/strong&gt;: Co-President, KCL Electronics&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://www.linkedin.com/in/mahraz-ahmed/" rel="noopener noreferrer"&gt;Mahraz Ahmed&lt;/a&gt;&lt;/strong&gt;: Co-President, KCL Electronics&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Tracks:&lt;/strong&gt; Time (theme) · Best Handmade Project (Citadel) · Best Use of Presage · Zed · MLH × DEV Writing Challenge · Best use of Delta (Zed Industries)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Code:&lt;/strong&gt; &lt;a href="https://github.com/itsfaraazak/oxhacks26" rel="noopener noreferrer"&gt;github.com/itsfaraazak/oxhacks26&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;So, be honest: how long would you last before Fido ran off with your phone? We're guessing under 25 minutes.&lt;/em&gt; 🐶&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>mlhacks</category>
      <category>robotics</category>
      <category>oxhack26</category>
    </item>
  </channel>
</rss>
