Will Hilton 8 rokov pred
rodič
commit
14dc284c91

+ 48 - 0
dist/projects/dyna-myte-2400/index.html

@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="utf-8">
+    <title>Dyna Myte 2400</title>
+    <meta name="viewport" content="width=device-width, initial-scale=1">
+    <link rel="stylesheet" href="//yegor256.github.io/tacit/tacit.min.css">
+    <!-- favicon -->
+    <link rel="apple-touch-icon" sizes="57x57" href="/favicon/apple-touch-icon-57x57.png">
+    <link rel="apple-touch-icon" sizes="60x60" href="/favicon/apple-touch-icon-60x60.png">
+    <link rel="apple-touch-icon" sizes="72x72" href="/favicon/apple-touch-icon-72x72.png">
+    <link rel="apple-touch-icon" sizes="76x76" href="/favicon/apple-touch-icon-76x76.png">
+    <link rel="apple-touch-icon" sizes="114x114" href="/favicon/apple-touch-icon-114x114.png">
+    <link rel="apple-touch-icon" sizes="120x120" href="/favicon/apple-touch-icon-120x120.png">
+    <link rel="apple-touch-icon" sizes="144x144" href="/favicon/apple-touch-icon-144x144.png">
+    <link rel="apple-touch-icon" sizes="152x152" href="/favicon/apple-touch-icon-152x152.png">
+    <link rel="apple-touch-icon" sizes="180x180" href="/favicon/apple-touch-icon-180x180.png">
+    <link rel="icon" type="image/png" href="/favicon/favicon-32x32.png" sizes="32x32">
+    <link rel="icon" type="image/png" href="/favicon/android-chrome-192x192.png" sizes="192x192">
+    <link rel="icon" type="image/png" href="/favicon/favicon-96x96.png" sizes="96x96">
+    <link rel="icon" type="image/png" href="/favicon/favicon-16x16.png" sizes="16x16">
+    <link rel="manifest" href="/favicon/manifest.json">
+    <link rel="mask-icon" href="/favicon/safari-pinned-tab.svg" color="#ffb300">
+    <link rel="shortcut icon" href="/favicon/favicon.ico">
+    <meta name="msapplication-TileColor" content="#ffb300">
+    <meta name="msapplication-TileImage" content="/favicon/mstile-144x144.png">
+    <meta name="msapplication-config" content="/favicon/browserconfig.xml">
+    <meta name="theme-color" content="#ffb300">
+    <!-- http://realfavicongenerator.net/favicon_result?file_id=p1akurqbdbq0j1lpc14a618p2och6#.V1uVj_krK70 -->
+  </head>
+  <body>
+    <header>&#xA0;</header>
+    <section>
+      <article>
+        <!--
+title: Dyna Myte 2400
+-->
+<h1 id="dyna-myte-2400" class="deep-link"><a href="#dyna-myte-2400">Dyna Myte 2400</a></h1>
+<p>This CNC machine sat unused in the UC Robotic&apos;s Lab for years until we re-discovered the manual one day. I read the manual and taught myself how to use it with great results.</p>
+<div class="youtube-video"><iframe width="560" height="315" src="https://www.youtube.com/embed/pemZhpormRg" frameborder="0" allowfullscreen></iframe></div>
+<p><em>A video of the CNC machine running a routine I wrote</em></p>
+<p>I followed up by writing an <a href="../javascript-cnc-machine">emulator</a> in Javascript to test CNC code.</p>
+
+      </article>
+    </section>
+    <footer>&#xA0;</footer>
+  </body>
+</html>

+ 2 - 2
dist/projects/index.html

@@ -38,8 +38,8 @@ title: Projects
 <h1 id="projects" class="deep-link"><a href="#projects">Projects</a></h1>
 <h2 id="personal-projects" class="deep-link"><a href="#personal-projects">Personal Projects</a></h2>
 <ul>
-<li><a href="./skype-telepresence-rover/skype-telepresence-rover">Skype Telepresence Rover</a></li>
-<li>Dyna Myte 2400</li>
+<li><a href="./skype-telepresence-rover/">Skype Telepresence Rover</a></li>
+<li><a href="./dyna-myte-2400/">Dyna Myte 2400</a></li>
 <li>Javascript CNC Machine Emulator</li>
 <li>Ultimate Word-of-the-Day</li>
 <li>K&apos;nex Adding Machine</li>

+ 101 - 0
dist/projects/javascript-cnc-machine/CNC_California.txt

@@ -0,0 +1,101 @@
+This program should cut out an outline, a few letters, and then a rectangular
+border, resulting in a metal postcard about 6" wide (X dir) by 5" high (Y dir).
+
+Set the zero point of the drill tip at the bottom-left corner of the rectangle
+that will be printed, leaving room for the machine to draw 6" in the positive X
+and Y directions. I *think* it had room for 6 inches, it says in the manual what
+the box size is. I *think* it was 6"x8" but I could be way off.
+
+I'm reusing as many settings as possible from the D-SUB cutting program.
+
+START MM  02 // Use millimeters. Program ID 01 is taken by the D-SUB program I think.
+ TD=   3.175 // Tool diameter: 1/8 in = 3.175 mm
+FR XY  =03.7 // Divided the suggested feedrate by two, because tool was flexing.
+FR Z   =01.2 // Feedrate based on 1 cutting edge, instead of 4, then slightly reduced.
+SETUP >zcxyu // Allow user to position tool above bottom-left corner.
+Z>C          // Raise tool
+GOfX   8.500 // Point 1 of Figure 1
+GOfY 121.000 //
+GO Z-  9.000 // Insert tool
+GO X   8.000 // Point 2 
+   Y 119.000 //
+GO X   9.000 // Point 3
+   Y 110.500 //
+GO X   6.500 // Point 4
+   Y 100.500 //
+GO X  12.500 // Point 5
+   Y  92.500 //
+GO X  13.500 // Point 6
+   Y  83.500 //
+GO X  25.000 // Point 7
+   Y  70.500 //
+GO X  25.500 // Point 8
+   Y  64.300 //
+GO X  28.000 // Point 9
+   Y  60.500 //
+GO X  30.000 // Point 10
+   Y  60.500 //
+GO X  32.000 // Point 11
+   Y  57.500 //
+GO X  30.000 // Point 12
+   Y  55.000 //
+GO X  31.000 // Point 13
+   Y  51.500 //
+GO X  43.500 // Point 14
+   Y  31.000 //
+GO X  53.000 // Point 15
+   Y  30.000 //
+GO X  63.000 // Point 16
+   Y  25.500 //
+GO X  74.000 // Point 17
+   Y  16.000 //   
+GO X  76.500 // Point 18
+   Y   9.000 //   
+GO X 100.000 // Point 19
+   Y  11.000 //   
+GO X  99.500 // Point 20
+   Y  21.000 //   
+GO X 103.500 // Point 21
+   Y  28.000 //   
+GO X  99.000 // Point 22
+   Y  36.000 //   
+GO X  47.500 // Point 23
+   Y  83.500 //   
+GO X  47.500 // Point 24
+   Y 121.000 //   
+GO X   8.500 // Return to Point 1
+   Y 121.000 //
+Z>C          // Raise tool
+GOfX  78.000 // Point 1 of Figure 2
+GOfY 103.500 //
+GO Z-  9.000 // Insert tool
+GO Y  73.000 // Point 2
+GO Y  89.000 // Point 3 
+GO X  98.000 // Point 4 
+GO Y 103.500 // Point 5
+GO Y  73.000 // Point 6
+Z>C          // Raise tool
+GOfX 110.000 // Point 1 of Figure 3
+GO Z-  9.000 // Insert tool
+GO Y  85.000 // Point 2 of Figure 3
+Z>C          // Raise tool
+GOfY  92.000 // Point 3 
+GO Z-  9.000 // Insert tool
+Z>C          // Raise tool
+GOfX 124.500 // Point 1 of Figure 4
+GOfY  73.000 //
+GO Z-  9.000 // Insert tool
+Z>C          // Raise tool
+GOfY  79.000 // Point 2 of Figure 4
+GO Z-  9.000 // Insert tool
+GO Y 100.000 // Point 3 of Figure 4
+Z>C          // Raise tool
+GOfX   0.000 // Return home
+GOfY   0.000 //
+GO Z-  9.000 // Insert tool
+GO X 144.000 // Point 2 of border
+GO Y 126.000 // Point 3 of border
+GO X   0.000 // Point 4 of border
+GO Y   0.000 // Return home.
+Z>C          // Raise tool
+END NEW PART //

+ 104 - 0
dist/projects/javascript-cnc-machine/CNC_California_Subs.txt

@@ -0,0 +1,104 @@
+START MM  02 // Use millimeters. Program ID 01 is taken by the D-SUB program I think.
+ TD=   3.175 // Tool diameter: 1/8 in = 3.175 mm
+FR XY  =03.7 // Divided the suggested feedrate by two, because tool was flexing.
+FR Z   =01.2 // Feedrate based on 1 cutting edge, instead of 4, then slightly reduced.
+SETUP >zcxyu // Allow user to position tool above bottom-left corner.
+Z>C          // Raise tool
+GOfX   8.500 // Point 1 of Figure 1
+GOfY 121.000 //
+GO Z-  9.000 // Insert tool
+GO X   8.000 // Point 2 
+   Y 119.000 //
+GO X   9.000 // Point 3
+   Y 110.500 //
+GO X   6.500 // Point 4
+   Y 100.500 //
+GO X  12.500 // Point 5
+   Y  92.500 //
+GO X  13.500 // Point 6
+   Y  83.500 //
+GO X  25.000 // Point 7
+   Y  70.500 //
+GO X  25.500 // Point 8
+   Y  64.300 //
+GO X  28.000 // Point 9
+   Y  60.500 //
+GO X  30.000 // Point 10
+   Y  60.500 //
+GO X  32.000 // Point 11
+   Y  57.500 //
+GO X  30.000 // Point 12
+   Y  55.000 //
+GO X  31.000 // Point 13
+   Y  51.500 //
+GO X  43.500 // Point 14
+   Y  31.000 //
+GO X  53.000 // Point 15
+   Y  30.000 //
+GO X  63.000 // Point 16
+   Y  25.500 //
+GO X  74.000 // Point 17
+   Y  16.000 //   
+GO X  76.500 // Point 18
+   Y   9.000 //   
+GO X 100.000 // Point 19
+   Y  11.000 //   
+GO X  99.500 // Point 20
+   Y  21.000 //   
+GO X 103.500 // Point 21
+   Y  28.000 //   
+GO X  99.000 // Point 22
+   Y  36.000 //   
+GO X  47.500 // Point 23
+   Y  83.500 //   
+GO X  47.500 // Point 24
+   Y 121.000 //   
+GO X   8.500 // Return to Point 1
+   Y 121.000 //
+Z>C          // Raise tool
+CALL SUB 111 //
+Z>C          // Raise tool
+CALL SUB 222
+Z>C          // Raise tool
+CALL SUB  33
+Z>C          // Raise tool
+GOfX   0.000 // Return home
+GOfY   0.000 //
+GO Z-  9.000 // Insert tool
+GO X 144.000 // Point 2 of border
+GO Y 126.000 // Point 3 of border
+GO X   0.000 // Point 4 of border
+GO Y   0.000 // Return home.
+Z>C          // Raise tool
+END NEW PART //
+
+
+SUB 111     
+GOfX  78.000 // Point 1 of Figure 2
+GOfY 103.500 //
+GO Z-  9.000 // Insert tool
+GO Y  73.000 // Point 2
+GO Y  89.000 // Point 3 
+GO X  98.000 // Point 4 
+GO Y 103.500 // Point 5
+GO Y  73.000 // Point 6
+SUB RETURN     
+
+SUB 222
+GOfX 110.000 // Point 1 of Figure 3
+GO Z-  9.000 // Insert tool
+GO Y  85.000 // Point 2 of Figure 3
+Z>C          // Raise tool
+GOfY  92.000 // Point 3 
+GO Z-  9.000 // Insert tool
+SUB RETURN
+
+SUB 33
+GOfX 124.500 // Point 1 of Figure 4
+GOfY  73.000 //
+GO Z-  9.000 // Insert tool
+Z>C          // Raise tool
+GOfY  79.000 // Point 2 of Figure 4
+GO Z-  9.000 // Insert tool
+GO Y 100.000 // Point 3 of Figure 4
+SUB RETURN

+ 64 - 0
dist/projects/javascript-cnc-machine/CNC_D-SUB_PROGRAM.txt

@@ -0,0 +1,64 @@
+This is a D-sub socket cutting program.  Author: William Hilton
+Use a 1/8 inch diameter HSS (high speed steel) mill that can drill holes. The tool's cutting depth should be slightly > 9mm. The metal to be cut is high carbon steel. Feed Rate = RPM * CHIPSIZE * NUM OF BLADES (Gracias Doug!) Use lowest belt, with dial at slightly below 4.
+
+Edit the program to cut different connector types:
+25-pin: 47      15-pin: 33.75       9-pin: 25
+
+START MM  01 // Use millimeters, program ID 01
+ TD=   3.175 // Tool diameter: 1/8 in = 3.175 mm
+FR XY  =03.7 // Divided the suggested feedrate by two, because tool was flexing.
+FR Z   =01.2 // Feedrate based on 1 cutting edge, instead of 4, then slightly reduced.
+SETUP >zcxyu // Allow user to position tool above 1st hole.
+REPEAT    02 // Second traverse gets the edges better
+GOfX   0.000 // Go to 1st hole
+GOfY   0.000 //
+GO Z-  6.000 // Drill 1st hole
+Z>C          // Raise tool
+CALL SUB  10 // Cut left side...
+ZERO AT      // (Set origin to right hole)
+   X  47.000 // * Change depending on connector *
+   Y   0.000 //
+CALL SUB  11 // ...Cut right side...
+ZERO AT      // (Reset origin to left hole)
+   X- 47.000 // * Change depending on connector *              
+   Y   0.000 //
+GO X   6.920 // ...And return to Point 1.
+   Y   3.410 //
+Z>C          // Raise tool
+GOfX  47.000 // Go to 2nd hole * Change depending on connector *
+   Y   0.000 //
+GO Z  -6.000 // Drill 2nd hole
+Z>C          // Raise tool
+REPEAT END   //
+END NEW PART //
+
+SUB 10       // Left Side (located on line 050)
+GO X   6.920 // Point 1
+   Y   3.410 //
+GO Z-  9.000 // Insert tool
+ARC          // Point 2
+ XC=   6.920 //
+ YC=   1.500 //
+  a= 101.470 //
+GO X   5.740 // Point 3
+   Y-  2.280 //
+ARC          // Point 4
+ XC=   7.120 //
+ YC=-  2.000 //
+  a=  79.530 //
+SUB RETURN   //
+
+SUB 11       // Right Side (located on line 100)
+GO X-  7.120 // Point 5
+   Y-  3.410 //
+ARC          // Point 6
+ XC=-  7.120 //
+ YC=-  2.000 //
+  a=  79.530 //
+GO X-  5.050 // Point 7
+   Y   1.120 //
+ARC          // Point 8
+ XC=-  6.920 //
+ YC=   1.500 //
+  a= 101.470 //
+SUB RETURN   //

+ 802 - 0
dist/projects/javascript-cnc-machine/CNC_Simulator.html

@@ -0,0 +1,802 @@
+<!--
+layout: false
+-->
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
+
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+<!--
+Useful websites:
+Comparison of Canvas and SVG
+http://people.mozilla.com/~vladimir/xtech2006/
+
+Canvas Tutorial
+https://developer.mozilla.org/en/Canvas_tutorial/
+
+Animating with Javascript + Canvas
+http://dev.opera.com/articles/view/blob-sallad-canvas-tag-and-javascrip/
+-->
+<title>CNC Simulator</title>
+
+<script type="text/javascript">
+// Global variables
+var temp = "";
+var start_second = 0;
+var intervalID = 0;
+var running = true;
+var seconds = 0; // 6 degrees
+var time_inc = 1; // in seconds
+var sec_hand_length = 70;
+var min_hand_length = 65;
+var hour_hand_length = 35;
+var clock_radius = 75;
+
+
+// Function to run as soon as page loads
+function init()
+{
+    var d = new Date();
+    var curr_hour = d.getHours();
+    var curr_min = d.getMinutes();
+    start_second = d.getSeconds();
+    seconds = start_second + 60*curr_min + 60*60*((curr_hour) % 12);
+    intervalID = window.setInterval(animate,1000);
+    
+    // Initialize the canvas.
+    
+}
+
+
+
+// Animate one frame of the animation
+function animate()
+{
+    var canvas = document.getElementById("cnc_canvas");
+    var ctx = canvas.getContext("2d");
+    var sec_angle = 0;
+    var min_angle = 0;
+    var hour_angle = 0;
+    //alert("Breakpoint animate");
+    // Always put the terminating code near the top in case there is an error later on and the function exits before it can stop the interval timer.
+    seconds = seconds + time_inc;
+    //if (seconds % 60 > (3+start_second) % 60) {
+    //    stop();
+    //}
+    
+    with (ctx) {
+        // What? No "clear canvas" command?
+        clearRect(0,0,400,600);
+        
+        // Draw clock face
+        strokeStyle = "black";
+        lineWidth = 1;
+        beginPath();
+        arc(100, 100, clock_radius, 0.001, 2*Math.PI, false);
+        //alert("Breakpoint arc");
+        stroke();
+        //alert("Breakpoint stroke");
+        
+        // Draw second hand.
+        lineWidth = 1;
+        strokeStyle = "green";
+        beginPath();
+        moveTo(100,100);
+        sec_angle = 180-360/60*seconds;
+        //alert("Breakpoint sec_angle = " + String(sec_angle));
+        temp = "x = " + String(100+sec_hand_length*Sin(sec_angle)) + " y = " + String(100+sec_hand_length*Cos(sec_angle));
+        //alert("Breakpoint " + temp);        
+        lineTo(100+sec_hand_length*Sin(sec_angle), 100+sec_hand_length*Cos(sec_angle));
+        //alert("Breakpoint lineTo");
+        stroke();
+        
+        // Draw minute hand.
+        beginPath();
+        strokeStyle = "red";
+        lineWidth = 2;
+        moveTo(100,100);
+        min_angle = 180-360/60*seconds/60;
+        lineTo(100+min_hand_length*Sin(min_angle), 100+min_hand_length*Cos(min_angle));
+        stroke();
+        
+        // Draw hour hand.
+        lineWidth = 3;
+        strokeStyle = "blue";
+        beginPath();
+        moveTo(100,100);
+        hour_angle = 180-360/12*seconds/60/60;
+        lineTo(100+hour_hand_length*Sin(hour_angle), 100+hour_hand_length*Cos(hour_angle));
+        stroke();
+    }
+}
+
+function stop() {
+    window.clearInterval(intervalID);
+    running = false;
+}
+function start() {
+    time_inc = 1;
+    // Only start if we're not already running an animation.
+    if (!running) {
+        running = true;
+        intervalID = window.setInterval(animate,1000);
+    }
+}
+function start_high_speed() {
+    // If we're not already running an animation.
+    if (running) {
+        // Kill previous interval
+        stop();
+    }
+    time_inc = 15;
+    running = true;
+    intervalID = window.setInterval(animate,10);        
+}
+
+function d2r(degrees) {
+    return degrees*Math.PI/180;
+}
+function Sin(degrees) {
+    return Math.sin(d2r(degrees));
+}
+function Cos(degrees) {
+    return Math.cos(d2r(degrees));
+}
+</script>
+
+
+<style type="text/css">
+td {
+    padding-top: 10px;
+}
+</style>
+</head>
+
+<body onload="init()">
+<h2 style="margin-bottom: 0">CNC Simulator</h2>
+<table>
+ <tr>
+  <td style="vertical-align: top">
+   <div style="border-width: 5px; border-style: ridge;">
+    <p style="font-weight: bold; margin: 10px">
+     <span id="x_disp">X:</span>
+     <br>
+     <span id="y_disp">Y:</span>
+     <br>
+     <span id="z_disp">Z:</span>
+     <br>
+     <span id="time_disp">Time:</span>
+    </p>
+   </div>
+   <form action="">
+    <table>
+     <tr>
+      <td>
+        <label for="ppi_input">Pixels per inch:</label>
+        <br>
+        <input id="ppi_input" type="text" value="127" style="width: 5em">
+      </td>
+      <td>
+       <label for="time_step_input">Time step:</label>
+       <br>
+       <input id="time_step_input" type="text" value="1" style="width: 5em">
+      </td>
+     </tr>
+     <tr>
+      <td>
+        <label for="speed_input">Speed factor:</label>
+        <br>
+        <input id="speed_input" type="text" value="5" style="width: 5em; margin-right: 5em;">
+      </td>
+      <td>
+        <label for="blitz_input">Blitz mode:</label>
+        <input type="checkbox" id="blitz_input" checked="checked">
+      </td>
+     </tr>
+     <tr>
+      <td>
+        <label for="offset_x">Offset X:</label>
+        <br>
+        <input id="offset_x" type="text" value="50" style="width: 5em; margin-right: 5em;">
+      </td>
+      <td>
+        <label for="offset_y">Offset Y:</label>        
+        <br>
+        <input id="offset_y" type="text" value="50" style="width: 5em; margin-right: 5em;">
+      </td>
+     </tr>
+     <tr>
+      <td colspan="2">
+    <label for="code_input">Enter CNC code here:<br></label>
+    <textarea rows="20" cols="50" id="code_input">START MM  01 // Use millimeters, program ID 01
+ TD=   3.175 // Tool diameter: 1/8 in = 3.175 mm
+FR XY  =03.7 // Divided the suggested feedrate by two, because tool was flexing.
+FR Z   =01.2 // Feedrate based on 1 cutting edge, instead of 4, then slightly reduced.
+SETUP &gt;zcxyu // Allow user to position tool above 1st hole.
+REPEAT    02 // Second traverse gets the edges better
+GOfX   0.000 // Go to 1st hole
+GOfY   0.000 //
+GO Z-  6.000 // Drill 1st hole
+Z&gt;C          // Raise tool
+CALL SUB  10 // Cut left side...
+ZERO AT      // (Set origin to right hole)
+   X  47.000 // * Change depending on connector *
+   Y   0.000 //
+CALL SUB  11 // ...Cut right side...
+ZERO AT      // (Reset origin to left hole)
+   X- 47.000 // * Change depending on connector *              
+   Y   0.000 //
+GO X   6.920 // ...And return to Point 1.
+   Y   3.410 //
+Z&gt;C          // Raise tool
+GOfX  47.000 // Go to 2nd hole * Change depending on connector *
+   Y   0.000 //
+GO Z  -6.000 // Drill 2nd hole
+Z&gt;C          // Raise tool
+REPEAT END   //
+END NEW PART //
+
+SUB 10       // Left Side (located on line 050)
+GO X   6.920 // Point 1
+   Y   3.410 //
+GO Z-  9.000 // Insert tool
+ARC          // Point 2
+ XC=   6.920 //
+ YC=   1.500 //
+  a= 101.470 //
+GO X   5.740 // Point 3
+   Y-  2.280 //
+ARC          // Point 4
+ XC=   7.120 //
+ YC=-  2.000 //
+  a=  79.530 //
+SUB RETURN   //
+
+SUB 11       // Right Side (located on line 100)
+GO X-  7.120 // Point 5
+   Y-  3.410 //
+ARC          // Point 6
+ XC=-  7.120 //
+ YC=-  2.000 //
+  a=  79.530 //
+GO X-  5.050 // Point 7
+   Y   1.120 //
+ARC          // Point 8
+ XC=-  6.920 //
+ YC=   1.500 //
+  a= 101.470 //
+SUB RETURN   //
+    </textarea>
+    <br>
+     <input name="submit" type="button" value="Click To Run Simulator" onclick="startSimulation();">
+     <input id="pause_input" type="button" value="Pause" onclick="pause();">
+    </td>
+    </tr>
+    </table>
+    </form>
+   </td>
+   <td style="vertical-align:top">
+    <canvas id="cnc_canvas" width="780" height="650" style="border:1px solid black"></canvas>
+   </td>
+ </tr>
+</table>
+<hr>
+<div class="output_wrapper">
+ <b>Output:</b>
+ <pre id="output"></pre>
+</div>
+<script type="text/javascript">
+// The so-called Prototype Dollar function, a shortcut for document.getElementById()
+function $() {
+    var elements = new Array();
+    for (var i = 0; i < arguments.length; i++) {
+        var element = arguments[i];
+        if (typeof element == "string")
+            element = document.getElementById(element);
+        if (arguments.length == 1)
+            return element;
+        elements.push(element);
+    }
+    return elements;
+}
+ctx = null;
+canvas = null;
+function clear_vars() {// Global variables
+    unit = "MM"; // or "IN"
+    time_step = 1; // update every half second.
+    ppi = 96; // Pixels per inch
+    cf = 1; // Pixel to unit ratio
+    td = 1; // Tool diameter
+    max_frx = 1; // Maximum feed rate X
+    max_fry = 1; // Maximum feed rate Y
+    max_frz = 1; // Maximum feed rate Z
+    x_inc = 1; // Distance to move per tick
+    y_inc = 1; // Distance to move per tick
+    z_inc = 1; // Distance to move per tick
+    a_inc = 1; // Distance to move per tick
+    mar_x = 10; // Horizontal margin of origin
+    mar_y = 10; // Vertical margin of origin
+    tool_c = 2; // Clearance height.
+    tool_x = 0; // Tool position X
+    tool_y = 0; // Tool position Y
+    tool_z = 0; // Tool position Z
+    tool_a = 0; // Tool angle (used for arcs)
+    arc_r = 0;  // Radius of currently drawn arc.
+    arc_dir = false; // Counter-clockwise
+    old_x = 0;  // Previous tool position X
+    old_y = 0;  // Previous tool position Y
+    old_z = 0;  // Previous tool position Z
+    old_a = 0;  // Previous tool angle (used for arcs)
+    goal_x = 0; // Destination coordinate
+    goal_y = 0; // Destination coordinate
+    goal_z = 0; // Destination coordinate
+    xc = 0; // Used for drawing arcs
+    yc = 0; // Used for drawing arcs
+    ac = 0; // Used for drawing arcs
+    time = 0;   // Time needed to execute cut.
+    time_x = 0;
+    time_y = 0;
+    time_z = 0;
+    time_a = 0; // Used for arcs
+    time_elapsed = 0;
+    n = 0;      // Line index
+    lines = []; // Array of each line of code
+    line = "";  // Current line
+    moving = false; // Set to true when the tool is moving from one point to another.
+    arcing = false; // Set to true when the tool is arcing.
+    interval_id = 0; // Variable needed to stop the timer during real-time mode
+    blitz_done = false; // Variable used to stop the timer in blitz mode.   
+    paused = false; // Pause/unpaused state
+    speed = 1;
+    stack = []; // Line numbers to return to
+    canvas_stack = 0; // How many times restore() the canvas
+    canvas = $("cnc_canvas");
+    ctx = canvas.getContext("2d");   
+}
+function startSimulation() {
+    // Clean up variables from the last run.
+    clear_vars();
+    // Clear debug output
+    $("output").innerHTML = "";
+    // Because the previous run might not have finished
+    for (var i = 0; i < 50; i++) {
+        ctx.restore();
+    }
+    // Stop the clock
+    stop();
+    // Since there is no "clear canvas" command.
+    ctx.fillStyle = "white"
+    ctx.fillRect(0, 0, canvas.width, canvas.height);
+    ctx.strokeStyle = "black";
+    /*
+    The canvas has it's origin in the top-left corner, but the CNC
+    Machine has it's origin in the bottom-left corner. Therefore,
+    we translate the origin and apply a transformation matrix to
+    flip the Y direction.
+    */   
+    ctx.save();
+    canvas_stack = canvas_stack + 1;
+    ctx.transform(1, 0, 0, -1, 0, 0);  
+    mar_x = parseFloat($("offset_x").value);
+    mar_y = parseFloat($("offset_y").value);
+    ctx.translate(mar_x, mar_y-canvas.height); 
+    ctx.save();
+    canvas_stack = canvas_stack + 1;
+    
+    lines = $("code_input").value.split("\n"); //str.split("\n");
+    ppi = parseFloat($("ppi_input").value);    
+    // Start the animation sequence
+    n = -1;    
+    debugn("There are " + lines.length + " lines");
+    // Stop all animations
+    window.clearInterval(interval_id);
+    paused = true;
+    pause();
+}
+function pause() {
+    if (paused) {
+        paused = false;
+        // Set the speed
+        speed = parseFloat($("speed_input").value);
+        time_step = parseFloat($("time_step_input").value);
+        // Start the CNC simulation.
+        if ($("blitz_input").checked) {
+            while (blitz_done == false) {
+                tick();
+            }
+        } else {
+            interval_id = window.setInterval(tick,1000*time_step/speed);
+        }
+        $("pause_input").value = "Pause";
+    } else {
+        paused = true;
+        window.clearInterval(interval_id);
+        $("pause_input").value = "Resume";
+    }
+}
+function nextLine() {
+    n = n + 1;
+    if (n >= lines.length) {
+        return false;
+    } else {
+        line = lines[n];
+        return true;
+    }
+}
+function startsWith(s1, s2) {
+    if (s1.substr(0,s2.length) == s2) {
+        return true;
+    } else {
+        return false;
+    }
+}
+function deg2rad(d) {
+    return (Math.PI/180)*d;
+}
+function rad2deg(r) {
+    return (180/Math.PI)*r;
+}
+function tick() {    
+    if (moving) {
+        // Store previous position
+        old_x = tool_x;
+        old_y = tool_y;
+        old_z = tool_z;
+        old_a = tool_a;
+        // Figure out new position
+        if (time < time_step) {
+            if (arcing) {
+                tool_a = goal_a;
+                tool_x = xc + Math.cos(tool_a)*arc_r;
+                tool_y = yc + Math.sin(tool_a)*arc_r;
+            } else {
+                tool_x = goal_x;
+                tool_y = goal_y;
+                tool_z = goal_z;
+            }
+            time_elapsed = time_elapsed + time;
+        } else {
+            if (arcing) {
+                tool_a = tool_a + a_inc;
+                tool_x = xc + Math.cos(tool_a)*arc_r;
+                tool_y = yc + Math.sin(tool_a)*arc_r;
+            } else {
+                tool_x = tool_x + x_inc;
+                tool_y = tool_y + y_inc;
+                tool_z = tool_z + z_inc;
+            }
+            time_elapsed = time_elapsed + time_step;
+        }              
+        // Update display
+        $("x_disp").innerHTML = "X: " + tool_x;
+        $("y_disp").innerHTML = "Y: " + tool_y;
+        $("z_disp").innerHTML = "Z: " + tool_z;
+        $("time_disp").innerHTML = "Time: " + time_elapsed;
+        
+        // Draw line if tool is below surface.
+        if (tool_z < 0) {
+            with (ctx) {            
+                // Draw hole
+                beginPath();
+                fillStyle = "black";
+                arc(tool_x, tool_y, td/2.0, 0, Math.PI*2.0, true);
+                fill();
+                // Draw line
+                lineCap = "round";
+                lineWidth = td;
+                beginPath();
+                strokeStyle = "black";
+                if (arcing) {                    
+                    arc(xc, yc, arc_r, old_a, tool_a, arc_dir);
+                } else {                    
+                    moveTo(old_x, old_y);                
+                    lineTo((tool_x), (tool_y));
+                }
+                stroke();                  
+            }
+        } else {
+            // Draw hollow dot (so we can see the path of the tool)
+            with (ctx) {  
+                lineWidth = 1/cf;
+                strokeStyle = "blue";
+                strokeRect((tool_x)-1/cf, (tool_y) -1/cf, 3/cf, 3/cf);
+            }
+        }
+        // Decrease time
+        time = time - time_step;
+        // Finish line
+        if (time < 0) {
+            time = 0;
+            moving = false;
+            arcing = false;
+        }
+        debug(".");
+    } else {
+        // Always safest to put this first, in case of unhandled syntax
+        // errors later on in the code.
+        if (nextLine()) {
+            ndebug("Line " + n + ": ");
+        } else {
+            ndebug("Ended because there are no more lines.");
+            n = -1;
+            ndebug("canvas_stack = " + canvas_stack);
+            for (var i = 0; i < canvas_stack; i++) {
+                ndebug("ctx.restore()");
+                ctx.restore();
+            }
+            blitz_done = true;
+            window.clearInterval(interval_id);
+            return;
+        }
+        //debug(line);
+        if (startsWith(line, "START")) {
+            // Set unit
+            unit = line.substr(6,2);
+            debugd("Unit: " + unit);
+            if (unit == "MM") {
+                debugd("Millimeters");
+                cf = ppi / 25.4;
+            } else if (unit == "IN") {
+                debugd("Inches");
+                cf = ppi;
+            } else {
+                ndebug("Invalid unit");
+                n = lines.length;
+                return;
+            }
+            debug("Scale: " + cf);
+            ctx.scale(cf, cf);
+        } else if (startsWith(line, " TD=")) {
+            // Set tool diameter
+            debugd("Tool Diameter: " + line.substring(4,12));
+            td = parseFloat(line.substring(4,12));
+            ctx.lineWidth = td;            
+            debug("ctx.lineWidth = " + ctx.lineWidth);
+        } else if (startsWith(line, "FR")) {
+            // TODO: set feed rate
+            debugd("Setting feed rate for " + line.substr(3,3) + " to " + line.substring(8,12));
+            if (line.indexOf("X") != -1) {
+                max_frx = parseFloat(line.substring(8,12));
+                debug(" set FR X = " + max_frx);
+            }
+            if (line.indexOf("Y") != -1) {
+                max_fry = parseFloat(line.substring(8,12));
+                debug(" set FR Y = " + max_fry);
+            }
+            if (line.indexOf("Z") != -1) {
+                max_frz = parseFloat(line.substring(8,12));
+                debug(" set FR Z = " + max_frz);
+            }
+        } else if (startsWith(line, "SETUP")) {
+            // This step doesn't translate well, and would get annoying
+            debug("Setup: " + line.substring(7,12));
+            debug(" Note: in real life, this command would let you position the tool at a new 'zero' position.");
+        } else if (startsWith(line, "Z>C")) {
+            tool_z = tool_c;
+            debug("Z>C: Z = " + String(tool_c));
+        } else if (startsWith(line, "GO")) {
+            goal_x = tool_x; time_x = 0;
+            goal_y = tool_y; time_y = 0;
+            goal_z = tool_z; time_z = 0;
+            debugd("Go");
+            do {
+                if (line.substr(3,1) == "f") {
+                    // TODO: how fast is "fast" anyway?
+                }
+                if (line.substr(3,1) == "X") {
+                    goal_x = parseFloat(line.substring(5,12));
+                    if (line.substr(4,1) == "-") {
+                        goal_x = -goal_x;
+                    }                    
+                    time_x = Math.abs((goal_x - tool_x) / max_frx);
+                    debugd("X coor: " + goal_x);
+                } else if (line.substr(3,1) == "Y") {
+                    goal_y = parseFloat(line.substring(5,12));
+                    if (line.substr(4,1) == "-") {
+                        goal_y = -goal_y;
+                    }
+                    time_y = Math.abs((goal_y - tool_y) / max_fry);
+                    debugd("Y coor: " + goal_y);
+                } else if (line.substr(3,1) == "Z") {                    
+                    goal_z = parseFloat(line.substring(5,12));                    
+                    if (line.substr(4,1) == "-") {
+                        goal_z = -goal_z;
+                    }
+                    time_z = Math.abs((goal_z - tool_z) / max_frz);
+                    debugd("Z coor: " + goal_z);
+                }
+                n = n+1;
+                if (n >= lines.length) {
+                    break;
+                }
+                line = lines[n];
+            } while (line.substr(0,2) == "  ")
+            n = n - 1;
+            // Compute how many seconds it will take to execute this cut
+            time = Math.max(Math.max(time_x, time_y), time_z);
+            debugd("Time Required: "+time);
+            x_inc = (goal_x - tool_x) / time * time_step;
+            y_inc = (goal_y - tool_y) / time * time_step;
+            z_inc = (goal_z - tool_z) / time * time_step;
+            debug("X inc: " + x_inc + " Y inc: " + y_inc +" Z inc: " + z_inc);
+            moving = true;
+        } else if (startsWith(line, "ZERO AT")) {
+            // Translate the canvas origin
+            debugd("ZERO AT");
+            goal_x = 0; time_x = 0;
+            goal_y = 0; time_y = 0;
+            goal_z = 0; time_z = 0;
+            if (!nextLine()) { return; }
+            while (line.substr(0,2) == "  ") {
+                if (line.substr(3,1) == "X") {
+                    goal_x = parseFloat(line.substring(5,12));
+                    if (line.substr(4,1) == "-") {
+                        goal_x = -goal_x;
+                    }
+                    debug(" X coor: " + goal_x);
+                } else if (line.substr(3,1) == "Y") {
+                    goal_y = parseFloat(line.substring(5,12));                   
+                    if (line.substr(4,1) == "-") {
+                        goal_y = -goal_y;
+                    }
+                    debug(" Y coor: " + goal_y);
+                } else if (line.substr(3,1) == "Z") {                    
+                    goal_z = parseFloat(line.substring(5,12));                    
+                    if (line.substr(4,1) == "-") {
+                        goal_z = -goal_z;
+                    }
+                    debug(" Z coor: " + goal_z);
+                }
+                if (!nextLine()) { break; }
+            } 
+            n = n - 1;
+            // Translate the canvas origin
+            ctx.save();            
+            ctx.translate(goal_x, goal_y);
+            canvas_stack = canvas_stack + 1;
+            // Untranslate tool
+            tool_x = tool_x - goal_x;
+            tool_y = tool_y - goal_y;
+        } else if (startsWith(line, "ARC")) {
+            debugd("ARC");
+            if (!nextLine()) { return; }
+            if (line.substr(0,4) == " XC=") {
+                xc = parseFloat(line.substring(5,12));
+                if (line.substr(4,1) == "-") {
+                    xc = -xc;
+                }
+                debug(" XC: " + xc);
+            } else {
+                debug("I really expected a line starting with ' XC='");
+            }
+            if (!nextLine()) { return; }
+            if (line.substr(0,4) == " YC=") {
+                yc = parseFloat(line.substring(5,12));
+                if (line.substr(4,1) == "-") {
+                    yc = -yc;
+                }
+                debug(" YC: " + yc);
+            } else {
+                debug("I really expected a line starting with ' YC='");
+            }
+            if (!nextLine()) { return; }
+            if (line.substr(0,4) == "  a=") {
+                // I call the variable 'ac' for resemblance
+                ac = parseFloat(line.substring(5,12));
+                if (line.substr(4,1) == "-") {
+                    ac = -ac;
+                    arc_dir = true;
+                } else {
+                    arc_dir = false;
+                }
+                debugd(" AC: " + ac);                
+                ac = deg2rad(ac);
+                debugd("in radians: " + ac); 
+            } else {
+                debug("I really expected a line starting with '  a='");
+            }
+            arc_r = Math.sqrt( Math.pow(xc-tool_x, 2) +
+                               Math.pow(yc-tool_y, 2) );
+            debugd("R = " + arc_r);
+            var dist = Math.abs(arc_r*ac);
+            var xyfr = (max_frx + max_fry)/2;
+            time = dist/xyfr;
+            debugd("Time Required: " + time);            
+            a_inc = ac/time*time_step;
+            debugd("a_inc = " + a_inc);
+            // Math.atan2 seems to be measuring Clockwise from the Y
+            // axis. Ridiculous.
+            tool_a = Math.atan2(tool_y-yc, tool_x-xc);
+            debugd("tool_a = " + rad2deg(tool_a) + " degrees");
+            goal_a = Math.atan2(tool_y-yc, tool_x-xc) + ac;
+            debug("goal_a = " + rad2deg(goal_a) + " degrees");
+            moving = true;
+            arcing = true;
+            //arcing = true;
+        } else if (startsWith(line, "CALL SUB")) {
+            // Push line # onto stack
+            debugd("Calling Sub");
+            stack.push(n);
+            ctx.save();
+            canvas_stack = canvas_stack + 1;
+            // Get the SUB id
+            var sub_id = line.substring(9,12).replace(" ","");
+            var sub_id = "SUB " + sub_id;
+            debugd("Scanning for '" + sub_id +"'");
+            // Move the cursor location to the start of the subroutine.
+            n = -1;
+            while (n < lines.length) {
+                n = n + 1;
+                line = lines[n];
+                if (line.substr(0,sub_id.length) == sub_id) {
+                    debugd("Found '" + sub_id + "' on line " + String(n));
+                    debug("Stack: " + String(stack));
+                    break;
+                }
+            }                
+        } else if (startsWith(line, "SUB RETURN")) {
+            // Return to our line # in the stack.
+            n = stack.pop();
+            ctx.restore();
+            canvas_stack = canvas_stack - 1;
+            debugd("SUB RETURN return to line " + String(n));
+            debug("Stack: " + String(stack));
+        } else if (startsWith(line, "REPEAT END")) {
+            if (stack.length > 0) {
+                n = stack.pop();
+                debug("REPEAT END return to line " + String(n));
+            } else {
+                debug("Passing REPEAT END");
+            }
+        } else if (startsWith(line, "REPEAT")) {
+            var rep_num = parseInt(line.substring(10,12))
+            debugd("REPEAT " + String(rep_num) + " times");
+            for (var i = 1; i < rep_num; i++) {
+                stack.push(n);
+            }
+            debug("Stack: " + String(stack));
+        } else if (startsWith(line, "END")) {
+            n = lines.length;
+            debug("Encountered an END");
+        } else {
+            debug("Skipping line: " + line);
+        }
+    }  
+}
+
+/*
+These are placed at the end, so if prior code has syntax errors,
+the page will clearly be broken.
+*/
+function debug(str) {
+    var obj;
+    obj = $("output");
+    obj.innerHTML = obj.innerHTML + str;
+}
+function ndebug(str) {
+    var obj;
+    obj = $("output");
+    obj.innerHTML = obj.innerHTML + "\n" + str;
+}
+function debugn(str) {
+    var obj;
+    obj = $("output");
+    obj.innerHTML = obj.innerHTML + str + "\n";
+}
+function ndebugn(str) {
+    var obj;
+    obj = $("output");
+    obj.innerHTML = obj.innerHTML + "\n" + str + "\n";
+}
+function debugd(str) {
+    var obj;
+    obj = $("output");
+    obj.innerHTML = obj.innerHTML + str + "... ";
+}
+</script>
+<footer>
+<hr>
+<p style="text-align: center; font-style:italic"><a href="http://validator.w3.org/#validate-by-upload">Freaking valid HTML5 baby!!!</a></p>
+</footer>
+</body>
+</html>

+ 36 - 0
dist/projects/javascript-cnc-machine/CNC_Spiral.txt

@@ -0,0 +1,36 @@
+// Draw a spiral
+START IN  
+ TD=   0.075
+FR XY  =0.20
+GO Z- .1
+REPEAT     4
+ZERO AT
+   X- 0.1
+   Y  0.0
+ARC
+ XC= 0
+ YC= 0
+  a= 90
+ZERO AT
+   X 0
+   Y -.1
+ARC
+ XC= 0
+ YC= 0
+  a= 90
+ZERO AT
+   X .1
+   Y 0
+ARC
+ XC= 0
+ YC = 0
+  a = 90
+ZERO AT
+   X 0
+   Y .1
+ARC
+ XC= 0
+ YC= 0
+  a= 90
+REPEAT END
+END

+ 117 - 0
dist/projects/javascript-cnc-machine/CNC_UC_Logo.txt

@@ -0,0 +1,117 @@
+START MM  01 // Use millimeters, program ID 01
+ TD=   3.175 // Tool diameter: 1/8 in = 3.175 mm
+FR XY  =03.7 // Divided the suggested feedrate by two, because tool was flexing.
+FR Z   =01.2 // Feedrate based on 1 cutting edge, instead of 4, then slightly reduced.
+SETUP >zcxyu // Allow user to position tool above 1st hole.
+GOfX  13.000
+GOfY  174
+GO Z-  3.000
+GO Y  167
+GO X 30
+GO Y 85
+ARC
+ XC= 73
+ YC= 88
+  a= 94
+GO X 80
+   Y 45
+ARC
+ XC= 130
+ YC= 83
+  a= 45
+GO X 123
+   Y 20
+GO Y 27
+   X 125
+ARC 
+ XC= 138
+ YC= 89
+  a=-140
+GO X 110
+   Y 145
+GO Y 154
+ARC
+ XC= 131
+ YC= 89
+  a= 105
+GO X 75
+GO Y 51
+ARC
+ XC= 76
+ YC= 83
+  a=-90
+GO X 44
+   Y 84
+GO Y 174
+GO X 13
+Z>C
+
+GOfX 100
+GOfY 175
+GO Z- 9
+GO Y 167
+GO X 128
+GO Y 175
+GO X 100
+Z>C
+
+GOfX 130
+GOfY 158
+GO Z-9
+GO Y 80
+ARC
+ XC= 96
+ YC= 83
+  a=-40
+GO X 120
+   Y 59
+ARC
+ XC= 144
+ YC= 83
+  a=-40
+GO X 110
+   Y 80
+GO Y 124
+ARC
+ XC= 150
+ YC= 88
+  a= 164
+GO X 178
+GO Y 43
+GO X 176
+   Y 45
+ARC
+ XC= 157
+ YC= 91
+  a=-60
+GO X 126
+   Y 53
+ARC
+ XC= 105
+ YC= 87
+  a= 55
+GO X 145
+   Y 83
+GO Y 152
+ARC
+ XC= 149
+ YC= 121
+  a=-85
+GO X 180
+GO Y 127
+GO X 186
+   Y 126
+GO X 190
+   Y 154
+GO X 184
+   Y 155
+GO X 183
+   Y 148
+ARC
+ XC= 140
+ YC= 65
+  a= 21
+GO X 148
+GO Y 158
+GO X 130
+END

+ 57 - 0
dist/projects/javascript-cnc-machine/index.html

@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="utf-8">
+    <title>Javascript CNC Machine Emulator</title>
+    <meta name="viewport" content="width=device-width, initial-scale=1">
+    <link rel="stylesheet" href="//yegor256.github.io/tacit/tacit.min.css">
+    <!-- favicon -->
+    <link rel="apple-touch-icon" sizes="57x57" href="/favicon/apple-touch-icon-57x57.png">
+    <link rel="apple-touch-icon" sizes="60x60" href="/favicon/apple-touch-icon-60x60.png">
+    <link rel="apple-touch-icon" sizes="72x72" href="/favicon/apple-touch-icon-72x72.png">
+    <link rel="apple-touch-icon" sizes="76x76" href="/favicon/apple-touch-icon-76x76.png">
+    <link rel="apple-touch-icon" sizes="114x114" href="/favicon/apple-touch-icon-114x114.png">
+    <link rel="apple-touch-icon" sizes="120x120" href="/favicon/apple-touch-icon-120x120.png">
+    <link rel="apple-touch-icon" sizes="144x144" href="/favicon/apple-touch-icon-144x144.png">
+    <link rel="apple-touch-icon" sizes="152x152" href="/favicon/apple-touch-icon-152x152.png">
+    <link rel="apple-touch-icon" sizes="180x180" href="/favicon/apple-touch-icon-180x180.png">
+    <link rel="icon" type="image/png" href="/favicon/favicon-32x32.png" sizes="32x32">
+    <link rel="icon" type="image/png" href="/favicon/android-chrome-192x192.png" sizes="192x192">
+    <link rel="icon" type="image/png" href="/favicon/favicon-96x96.png" sizes="96x96">
+    <link rel="icon" type="image/png" href="/favicon/favicon-16x16.png" sizes="16x16">
+    <link rel="manifest" href="/favicon/manifest.json">
+    <link rel="mask-icon" href="/favicon/safari-pinned-tab.svg" color="#ffb300">
+    <link rel="shortcut icon" href="/favicon/favicon.ico">
+    <meta name="msapplication-TileColor" content="#ffb300">
+    <meta name="msapplication-TileImage" content="/favicon/mstile-144x144.png">
+    <meta name="msapplication-config" content="/favicon/browserconfig.xml">
+    <meta name="theme-color" content="#ffb300">
+    <!-- http://realfavicongenerator.net/favicon_result?file_id=p1akurqbdbq0j1lpc14a618p2och6#.V1uVj_krK70 -->
+  </head>
+  <body>
+    <header>&#xA0;</header>
+    <section>
+      <article>
+        <!--
+title: Javascript CNC Machine Emulator
+-->
+<h1 id="javascript-cnc-machine-emulator" class="deep-link"><a href="#javascript-cnc-machine-emulator">Javascript CNC Machine Emulator</a></h1>
+<p>The University of Cincinnati&apos;s Robotics Team had a DYNA MYTE 2400 CNC machine sitting in its lab for several years. After sitting unused for many years, someone found the manual and I quickly made it my project to learn how to use it. During my time in California, I developed an urge to send a unique kind of postcard to my friends at the Robotics Team at UC - a CNC-it-yourself postcard. Having written a report on CNC machines comparing G-code with the native language of the DYNA MYTE 2400 the previous quarter, I knew the basic commands by heart (and could reference the manual for the rest). After coming up with my design for the postcard and writing out the code I thought would produce it, I decided I needed some way to test it before I sent it to the team. So I spent a couple of nights learning how to use the HTML5 <code>&lt;canvas&gt;</code> element and made a quick 2D emulator to parse my DYNA MYTE code and render it.</p>
+<div class="youtube-video"><iframe width="560" height="315" src="https://www.youtube.com/embed/ZL0pXrBJSBo" frameborder="0" allowfullscreen></iframe></div>
+<p><em>Video demonstrating my Javascript emulator of the lab&apos;s CNC machine</em></p>
+<p>I sent them the code, and a link to this YouTube video to encourage them to machine the postcard. However, no one understood the machine as well as I did, and they weren&apos;t able to get the code entered while I was in California. It wasn&apos;t until I returned and entered the code myself that the postcard became a physical reality.</p>
+<p>You can try the emulator yourself here: <a href="CNC_Simulator">CNC_Simulator.html</a> It is just an HTML page with the Javascript source code embedded and no external references. Here are some sample CNC programs you can try:</p>
+<ul>
+<li><a href="CNC_Spiral.txt">CNC_Spiral.txt</a> - A simple example</li>
+<li><a href="CNC_D-SUB_PROGRAM.txt">CNC_D-SUB_PROGRAM.txt</a> - A program for cutting D-SUB sockets in sheet metal, which was the first use of the machine for the robotics team.</li>
+<li><a href="CNC_California.txt">CNC_California.txt</a> - The program seen in the video above.</li>
+<li><a href="CNC_California_Subs.txt">CNC_California_Subs.txt</a> - The same program, but using subroutines.</li>
+<li><a href="CNC_UC_Logo.txt">CNC_UC_Logo.txt</a> - A program for cutting the UC logo into sheet metal. (Note that UC&apos;s Logo is subject to regulations and shouldn&apos;t be used without permission.)</li>
+</ul>
+<p><em>Note</em>: You will have to change the Pixels per Inch setting based on your monitor to scale the image if it doesn&apos;t fit.</p>
+
+      </article>
+    </section>
+    <footer>&#xA0;</footer>
+  </body>
+</html>

+ 4 - 4
dist/projects/skype-telepresence-rover/index.html

@@ -48,10 +48,10 @@ title: Skype Telepresence Rover
 <li>Some wood and screws to mount the laptop and Arduino to the RC car</li>
 <li>Python code (using the Skype API) to send and receive steering and speed commands</li>
 </ul>
-<p><a href="https://www.youtube.com/watch?v=QsiM0Cjdzhk"><img src="http://img.youtube.com/vi/QsiM0Cjdzhk/0.jpg" alt="Arduino + H-bridge test"></a>
-<br><em>This is from an earlier prototype that was built on a chassis that did not have a built-in motor controller.</em></p>
-<p><a href="https://www.youtube.com/watch?v=UroWCdb6SUo"><img src="http://img.youtube.com/vi/UroWCdb6SUo/0.jpg" alt="Skype Rover 2 - H-bridge Hijack"></a>
-<br><em>For my second prototype, I used a complete electric car chassis, and reverse engineered the built-in motor controller. This video demonstrates applying a 3-5V input causing the wheels to spin, drawing power from the built-in battery, not the voltage source.</em></p>
+<div class="youtube-video"><iframe width="560" height="315" src="https://www.youtube.com/embed/QsiM0Cjdzhk" frameborder="0" allowfullscreen></iframe></div>
+<p><em>This is from an earlier prototype that was built on a chassis that did not have a built-in motor controller.</em></p>
+<div class="youtube-video"><iframe width="560" height="315" src="https://www.youtube.com/embed/UroWCdb6SUo" frameborder="0" allowfullscreen></iframe></div>
+<p><em>For my second prototype, I used a complete electric car chassis, and reverse engineered the built-in motor controller. This video demonstrates applying a 3-5V input causing the wheels to spin, drawing power from the built-in battery, not the voltage source.</em></p>
 <p>I had hoped to make a video of the robot in action, but have been too distracted to add the joystick control needed to steer the robot properly. In the meanwhile, above are a couple of videos I took during the development of the Skype Rover.</p>
 <p>The idea is really simple. The rover has it&apos;s own Skype Account. When you call it, it automatically accepts the call and puts your video in full-screen mode so others can see you well. On the controlling end, a Python code lets you send steering and speed commands using the mouse or a joystick, and on the rover end is Python code that receives the commands and sends them through a USB cable to the Arduino, which controls voltages wired into the RC car&apos;s motor controller. It is a masterpiece of integration, really. The technologies for telepresence were just there, waiting to be connected with a little bit of solder and Python by some clever individual. If it works, thousands of DIY hobbiests could build one of these things in a weekend.</p>
 <p>The project is about 90% complete. Upon my return to UC, I decided I wanted to be able to give tours of the UC Engineering building via the Skype Rover. However, initial tests revealed the engineering building has too many Wi-Fi deadzones to be able to drive the robot down a hallway without disconnecting. I also discovered that the open-loop control combined with latency issues made the rover extremely difficult to drive. Disheartened, I have temporarily shelved the Skype Telepresence Rover in favor of pursuing other projects such as learning ROS, the Robot Operating System from Willow Garage.</p>