Jog test, version, publish-beta
authorJoseph Coffland <joseph@cauldrondevelopment.com>
Thu, 8 Mar 2018 23:40:25 +0000 (15:40 -0800)
committerJoseph Coffland <joseph@cauldrondevelopment.com>
Thu, 8 Mar 2018 23:40:25 +0000 (15:40 -0800)
CHANGELOG.md
Makefile
package.json
scripts/jogtest.py
src/jade/templates/cheat-sheet-view.jade [new file with mode: 0644]

index 642ed8068454b11f722894214630f33aa7855816..f5518f277abb97b1a5a61eb008291fa859e5fdf0 100644 (file)
@@ -1,6 +1,11 @@
 Buildbotics CNC Controller Firmware Change Log
 ==============================================
 
+## v0.3.19
+ - Fixed stopping problems. #127
+ - Fixed ``Negative s-curve time`` error.
+ - Improved jogging with soft limits.
+
 ## v0.3.18
  - Don't enable any tool by default.
 
index ca67c03aa7579ddd2c060c48456400053dd917d2..60a13b58356aef6e79cf4d03455867c02851281d 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -58,6 +58,9 @@ copy: pkg
 pkg: all $(AVR_FIRMWARE)
        ./setup.py sdist
 
+beta-pkg: pkg
+       cp dist/$(PKG_NAME).tar.bz2 dist/$(PKG_NAME)-beta.tar.bz2
+
 gplan: $(GPLAN_TARGET)
 
 $(GPLAN_TARGET): $(GPLAN_MOD)
@@ -83,6 +86,11 @@ publish: pkg
        echo -n $(VERSION) > dist/latest.txt
        rsync $(RSYNC_OPTS) dist/$(PKG_NAME).tar.bz2 dist/latest.txt $(PUB_PATH)/
 
+publish-beta: beta-pkg
+       echo -n $(VERSION)-beta > dist/latest-beta.txt
+       rsync $(RSYNC_OPTS) dist/$(PKG_NAME)-beta.tar.bz2 dist/latest-beta.txt \
+         $(PUB_PATH)/
+
 update: pkg
        http_proxy= curl -i -X PUT -H "Content-Type: multipart/form-data" \
          -F "firmware=@dist/$(PKG_NAME).tar.bz2" -F "password=$(PASSWORD)" \
index 4bee5173c8a9526cacd9a5a54eed7ea073ea5f94..ae4b3c24bb591529341f1a6b38a13c1b6607b6eb 100644 (file)
@@ -1,6 +1,6 @@
 {
   "name": "bbctrl",
-  "version": "0.3.18",
+  "version": "0.3.19",
   "homepage": "http://buildbotics.com/",
   "repository": "https://github.com/buildbotics/bbctrl-firmware",
   "license": "GPL-3.0+",
index c58daa31b4c62d177342ae710641b51e165143e4..709156b67f18cba06917fb9504d8ff526e85a6e0 100755 (executable)
@@ -1,22 +1,50 @@
 #!/usr/bin/env python3
 
+import sys
+import tornado.ioloop
+
 from inevent import InEvent, JogHandler
 from inevent.Constants import *
 
 
+class Handler(JogHandler):
+  def changed(self):
+    scale = 1.0
+    if self.speed == 1: scale = 1.0 / 128.0
+    if self.speed == 2: scale = 1.0 / 32.0
+    if self.speed == 3: scale = 1.0 / 4.0
+
+    print(', '.join(list(map(lambda x: '%.3f' % (x * scale), self.axes))))
+    sys.stdout.flush()
+
+
 if __name__ == "__main__":
   # Load config
   config = {
-    "deadband": 0.1,
-    "axes": [ABS_X, ABS_Y, ABS_RZ],
-    "arrows": [ABS_HAT0X, ABS_HAT0Y],
-    "speed": [0x120, 0x121, 0x122, 0x123],
-    "activate": [0x124, 0x126, 0x125]
-    }
+      "Logitech Logitech RumblePad 2 USB": {
+          "deadband": 0.1,
+          "axes":     [ABS_X, ABS_Y, ABS_RZ, ABS_Z],
+          "dir":      [1, -1, -1, 1],
+          "arrows":   [ABS_HAT0X, ABS_HAT0Y],
+          "speed":    [0x120, 0x121, 0x122, 0x123],
+          "lock":     [0x124, 0x125],
+          },
+
+      "default": {
+          "deadband": 0.1,
+          "axes":     [ABS_X, ABS_Y, ABS_RY, ABS_RX],
+          "dir":      [1, -1, -1, 1],
+          "arrows":   [ABS_HAT0X, ABS_HAT0Y],
+          "speed":    [0x133, 0x130, 0x131, 0x134],
+          "lock":     [0x136, 0x137],
+          }
+      }
+
+  # Create ioloop
+  ioloop = tornado.ioloop.IOLoop.current()
 
   # Listen for input events
-  events = InEvent(types = "js kbd".split())
-  handler = JogHandler(config)
+  handler = Handler(config)
+  events = InEvent(ioloop, handler, types = "js kbd".split())
 
-  while not events.key_state("KEY_ESC"):
-    events.process_events(handler)
+  ioloop.start()
diff --git a/src/jade/templates/cheat-sheet-view.jade b/src/jade/templates/cheat-sheet-view.jade
new file mode 100644 (file)
index 0000000..487aba4
--- /dev/null
@@ -0,0 +1,562 @@
+//-/////////////////////////////////////////////////////////////////////////////
+//-                                                                           //
+//-              This file is part of the Buildbotics firmware.               //
+//-                                                                           //
+//-                Copyright (c) 2015 - 2018, Buildbotics LLC                 //
+//-                           All rights reserved.                            //
+//-                                                                           //
+//-   This file ("the software") is free software: you can redistribute it    //
+//-   and/or modify it under the terms of the GNU General Public License,     //
+//-    version 2 as published by the Free Software Foundation. You should     //
+//-    have received a copy of the GNU General Public License, version 2      //
+//-   along with the software. If not, see <http://www.gnu.org/licenses/>.    //
+//-                                                                           //
+//-   The software is distributed in the hope that it will be useful, but     //
+//-        WITHOUT ANY WARRANTY; without even the implied warranty of         //
+//-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU      //
+//-             Lesser General Public License for more details.               //
+//-                                                                           //
+//-     You should have received a copy of the GNU Lesser General Public      //
+//-              License along with the software.  If not, see                //
+//-                     <http://www.gnu.org/licenses/>.                       //
+//-                                                                           //
+//-              For information regarding this software email:               //
+//-                "Joseph Coffland" <joseph@buildbotics.com>                 //
+//-                                                                           //
+//-/////////////////////////////////////////////////////////////////////////////
+
+script#cheat-sheet-view-template(type="text/x-template")
+  // Modified from http://linuxcnc.org/docs/html/gcode.html
+  - var base = 'http://linuxcnc.org/docs/html/gcode';
+  .cheat-sheet
+    h2 GCode Cheat Sheet
+
+    table
+      tr
+        th Code
+        th Parameters
+        th Description
+
+      tr.spacer-row: th
+      tr.header-row
+        th(colspan='3') Motion
+      tr
+        td
+          a(href="#{base}/g-code.html#gcode:g0") G0
+        td
+        td Rapid Move
+      tr
+        td
+          a(target="_blank", href="#{base}/g-code.html#gcode:g1") G1
+        td
+        td Linear Move
+      tr
+        td
+          a(target="_blank", href="#{base}/g-code.html#gcode:g2-g3") G2, G3
+        td I J K or R, P
+        td Arc Move
+      tr
+        td
+          a(target="_blank", href="#{base}/g-code.html#gcode:g4") G4
+        td P
+        td Dwell
+      tr.unimplemented(v-if="showUnimplemented")
+        td
+          a(target="_blank", href="#{base}/g-code.html#gcode:g5") G5
+        td I J P Q
+        td Cubic Spline
+      tr.unimplemented(v-if="showUnimplemented")
+        td
+          a(target="_blank", href="#{base}/g-code.html#gcode:g5.1") G5.1
+        td I J
+        td Quadratic Spline
+      tr.unimplemented(v-if="showUnimplemented")
+        td
+          a(target="_blank", href="#{base}/g-code.html#gcode:g5.2-g5.3") G5.2
+        td P L
+        td NURBS
+      tr
+        td
+          a(target="_blank", href="#{base}/g-code.html#gcode:g38") G38.2 - G38.5
+        td
+        td Straight Probe
+      tr.unimplemented(v-if="showUnimplemented")
+        td
+          a(target="_blank", href="#{base}/g-code.html#gcode:g33.1") G33.1
+        td K
+        td Rigid Tapping
+
+      tr.spacer-row: th
+      tr.header-row
+        th(colspan='3') Tool Control
+      tr
+        td
+          a(href="#{base}/other-code.html#sec:select-tool") T
+        td
+        td Select Tool
+      tr
+        td
+          a(target="_blank", href="#{base}/m-code.html#mcode:m6") M6
+        td T
+        td Tool Change
+      tr.unimplemented(v-if="showUnimplemented")
+        td
+          a(target="_blank", href="#{base}/m-code.html#mcode:m61") M61
+        td Q
+        td Set Current Tool
+      tr.unimplemented(v-if="showUnimplemented")
+        td
+          a(target="_blank", href="#{base}/g-code.html#gcode:g10-l1") G10 L1
+        td P Q R
+        td Set Tool Table
+      tr.unimplemented(v-if="showUnimplemented")
+        td
+          a(target="_blank", href="#{base}/g-code.html#gcode:g10-l10") G10 L10
+        td P
+        td Set Tool Table
+      tr.unimplemented(v-if="showUnimplemented")
+        td
+          a(target="_blank", href="#{base}/g-code.html#gcode:g10-l11") G10 L11
+        td P
+        td Set Tool Table
+      tr.unimplemented(v-if="showUnimplemented")
+        td
+          a(target="_blank", href="#{base}/g-code.html#gcode:g43") G43
+        td  H
+        td Tool Length Offset
+      tr.unimplemented(v-if="showUnimplemented")
+        td
+          a(target="_blank", href="#{base}/g-code.html#gcode:g43.1") G43.1
+        td
+        td Dynamic Tool Length Offset
+      tr.unimplemented(v-if="showUnimplemented")
+        td
+          a(target="_blank", href="#{base}/g-code.html#gcode:g43.2") G43.2
+        td  H
+        td Apply additional Tool Length Offset
+      tr.unimplemented(v-if="showUnimplemented")
+        td
+          a(target="_blank", href="#{base}/g-code.html#gcode:g49") G49
+        td
+        td Cancel Tool Length Compensation
+
+      tr.spacer-row: th
+      tr.header-row
+        th(colspan='3') Feed Control
+      tr
+        td
+          a(href="#{base}/other-code.html#sec:set-feed-rate") F
+        td
+        td Set Feed Rate
+      tr.unimplemented(v-if="showUnimplemented")
+        td
+          a(target="_blank", href="#{base}/g-code.html#gcode:g93-g94-g95")
+            | G93, G94, G95
+        td
+        td Feed Rate Mode
+      tr.unimplemented(v-if="showUnimplemented")
+        td
+          a(target="_blank", href="#{base}/m-code.html#mcode:m52") M52
+        td P0 (off) or P1 (on)
+        td Adaptive Feed Control
+      tr.unimplemented(v-if="showUnimplemented")
+        td
+          a(target="_blank", href="#{base}/m-code.html#mcode:m53") M53
+        td P0 (off) or P1 (on)
+        td Feed Stop Control
+
+      tr.spacer-row: th
+      tr.header-row
+        th(colspan='3') Spindle Control
+      tr
+        td
+          a(href="#{base}/other-code.html#sec:set-spindle-speed") S
+        td
+        td Set Spindle Speed
+      tr
+        td
+          a(target="_blank", href="#{base}/m-code.html#mcode:m3-m4-m5")
+            | M3, M4, M5
+        td S
+        td Spindle Control
+      tr.unimplemented(v-if="showUnimplemented")
+        td
+          a(target="_blank", href="#{base}/m-code.html#mcode:m19") M19
+        td
+        td Orient Spindle
+      tr.unimplemented(v-if="showUnimplemented")
+        td
+          a(target="_blank", href="#{base}/g-code.html#gcode:g96-g97") G96, G97
+        td  S D
+        td Spindle Control Mode
+      tr.unimplemented(v-if="showUnimplemented")
+        td
+          a(target="_blank", href="#{base}/g-code.html#gcode:g33") G33
+        td K
+        td Spindle Synchronized Motion
+
+      tr.spacer-row: th
+      tr.header-row
+        th(colspan='3') Coolant
+      tr
+        td
+          a(target="_blank", href="#{base}/m-code.html#mcode:m7-m8-m9")
+            | M7, M8, M9
+        td
+        td Coolant Control
+
+      tr.spacer-row: th
+      tr.header-row
+        th(colspan='3') Stopping
+      tr
+        td
+          a(target="_blank", href="#{base}/m-code.html#mcode:m0-m1") M0, M1
+        td
+        td Program Pause
+      tr
+        td
+          a(target="_blank", href="#{base}/m-code.html#mcode:m2-m30") M2, M30
+        td
+        td Program End
+      tr.unimplemented(v-if="showUnimplemented")
+        td
+          a(target="_blank", href="#{base}/m-code.html#mcode:m60") M60
+        td
+        td Pallet Change Pause
+
+      tr.spacer-row: th
+      tr.header-row
+        th(colspan='3') Units
+      tr
+        td
+          a(target="_blank", href="#{base}/g-code.html#gcode:g20-g21") G20, G21
+        td
+        td Units (inch, mm)
+
+      tr.spacer-row: th
+      tr.header-row
+        th(colspan='3') Distance Mode
+      tr
+        td
+          a(target="_blank", href="#{base}/g-code.html#gcode:g90-g91") G90, G91
+        td
+        td Distance Mode
+      tr
+        td
+          a(target="_blank", href="#{base}/g-code.html#gcode:g90.1-g91.1")
+            | G90.1, G91.1
+        td
+        td Arc Distance Mode
+      tr.unimplemented(v-if="showUnimplemented")
+        td
+          a(target="_blank", href="#{base}/g-code.html#gcode:g7") G7
+        td
+        td Lathe Diameter Mode
+      tr.unimplemented(v-if="showUnimplemented")
+        td
+          a(target="_blank", href="#{base}/g-code.html#gcode:g8") G8
+        td
+        td Lathe Radius Mode
+
+      tr.spacer-row.unimplemented(v-if="showUnimplemented"): th
+      tr.header-row.unimplemented(v-if="showUnimplemented")
+        th(colspan='3') Cutter Radius Compensation
+      tr.unimplemented(v-if="showUnimplemented")
+        td
+          a(target="_blank", href="#{base}/g-code.html#gcode:g40") G40
+        td
+        td Compensation Off
+      tr.unimplemented(v-if="showUnimplemented")
+        td
+          a(target="_blank", href="#{base}/g-code.html#gcode:g41-g42") G41,G42
+        td D
+        td Cutter Compensation
+      tr.unimplemented(v-if="showUnimplemented")
+        td
+          a(target="_blank", href="#{base}/g-code.html#gcode:g41.1-g42.1")
+            | G41.1, G42.1
+        td D L
+        td Dynamic Cutter Compensation
+
+      tr.spacer-row.unimplemented(v-if="showUnimplemented"): th
+      tr.header-row.unimplemented(v-if="showUnimplemented")
+        th(colspan='3') Path Control Mode
+      tr.unimplemented(v-if="showUnimplemented")
+        td
+          a(target="_blank", href="#{base}/g-code.html#gcode:g61-g61.1")
+            | G61 G61.1
+        td
+        td Exact Path Mode
+      tr.unimplemented(v-if="showUnimplemented")
+        td
+          a(target="_blank", href="#{base}/g-code.html#gcode:g64") G64
+        td P Q
+        td Path Blending
+
+      tr.spacer-row.unimplemented(v-if="showUnimplemented"): th
+      tr.header-row.unimplemented(v-if="showUnimplemented")
+        th(colspan='3') Overrides
+      tr.unimplemented(v-if="showUnimplemented")
+        td
+          a(target="_blank", href="#{base}/m-code.html#mcode:m48-m49") M48, M49
+        td
+        td Speed and Feed Override Control
+      tr.unimplemented(v-if="showUnimplemented")
+        td
+          a(target="_blank", href="#{base}/m-code.html#mcode:m50") M50
+        td P0 (off) or P1 (on)
+        td Feed Override Control
+      tr.unimplemented(v-if="showUnimplemented")
+        td
+          a(target="_blank", href="#{base}/m-code.html#mcode:m51") M51
+        td P0 (off) or P1 (on)
+        td Spindle Speed Override Control
+
+      tr.spacer-row: th
+      tr.header-row
+        th(colspan='3') Coordinate Systems, Offsets & Planes
+      tr
+        td
+          a(target="_blank", href="#{base}/g-code.html#gcode:g54-g59.3")
+            | G54-G59.3
+        td
+        td Select Coordinate System
+      tr
+        td
+          a(target="_blank", href="#{base}/g-code.html#gcode:g10-l2") G10 L2
+        td P R
+        td Set Coordinate System
+      tr
+        td
+          a(target="_blank", href="#{base}/g-code.html#gcode:g10-l20") G10 L20
+        td P
+        td Set Coordinate System
+      tr
+        td
+          a(target="_blank", href="#{base}/g-code.html#gcode:g53") G53
+        td
+        td Move in Machine Coordinates
+      tr
+        td
+          a(target="_blank", href="#{base}/g-code.html#gcode:g92") G92
+        td
+        td Coordinate System Offset
+      tr
+        td
+          a(target="_blank", href="#{base}/g-code.html#gcode:g92.1-g92.2")
+            | G92.1, G92.2
+        td
+        td Reset G92 Offsets
+      tr
+        td
+          a(target="_blank", href="#{base}/g-code.html#gcode:g92.3") G92.3
+        td
+        td Restore G92 Offsets
+      tr
+        td
+          a(target="_blank", href="#{base}/g-code.html#gcode:g28-g28.1")
+            | G28, G28.1
+        td
+        td Go/Set Predefined Position
+      tr
+        td
+          a(target="_blank", href="#{base}/g-code.html#gcode:g30-g30.1")
+            | G30, G30.1
+        td
+        td Go/Set Predefined Position
+      tr
+        td
+          a(target="_blank", href="#{base}/g-code.html#gcode:g17-g19.1")
+            | G17 - G19.1
+        td (affects G2, G3, G81…G89, G40…G42)
+        td Plane Select
+
+      tr.spacer-row: th
+      tr.header-row
+        th(colspan='3') Flow-control Codes
+      tr
+        td
+          a(target="_blank", href="#{base}/o-code.html#ocode:subroutines") o sub
+        td
+        td Subroutines, sub/endsub call
+      tr
+        td
+          a(target="_blank", href="#{base}/o-code.html#ocode:looping") o while
+        td
+        td Looping, while/endwhile do/while
+      tr
+        td
+          a(target="_blank", href="#{base}/o-code.html#ocode:conditional") o if
+        td
+        td Conditional, if/else/endif
+      tr
+        td
+          a(target="_blank", href="#{base}/o-code.html#ocode:repeat") o repeat
+        td
+        td Repeat a loop of code
+      tr
+        td
+          a(target="_blank", href="#{base}/o-code.html#ocode:indirection") []
+        td
+        td Indirection
+      tr.unimplemented(v-if="showUnimplemented")
+        td
+          a(target="_blank", href="#{base}/o-code.html#ocode:calling-files")
+            | o call
+        td
+        td Call named file
+
+      tr.spacer-row.unimplemented(v-if="showUnimplemented"): th
+      tr.header-row.unimplemented(v-if="showUnimplemented")
+        th(colspan='3') Modal State
+      tr.unimplemented(v-if="showUnimplemented")
+        td
+          a(target="_blank", href="#{base}/m-code.html#mcode:m70") M70
+        td
+        td Save modal state
+      tr.unimplemented(v-if="showUnimplemented")
+        td
+          a(target="_blank", href="#{base}/m-code.html#mcode:m71") M71
+        td
+        td Invalidate stored state
+      tr.unimplemented(v-if="showUnimplemented")
+        td
+          a(target="_blank", href="#{base}/m-code.html#mcode:m72") M72
+        td
+        td Restore modal state
+      tr.unimplemented(v-if="showUnimplemented")
+        td
+          a(target="_blank", href="#{base}/m-code.html#mcode:m73") M73
+        td
+        td Save and Auto-restore modal state
+
+      tr.spacer-row.unimplemented(v-if="showUnimplemented"): th
+      tr.header-row.unimplemented(v-if="showUnimplemented")
+        th(colspan='3') Input/Output
+      tr.unimplemented(v-if="showUnimplemented")
+        td
+          a(target="_blank", href="#{base}/m-code.html#mcode:m62-m65") M62 - M65
+        td P
+        td Digital Output Control
+      tr.unimplemented(v-if="showUnimplemented")
+        td
+          a(target="_blank", href="#{base}/m-code.html#mcode:m66") M66
+        td P E L Q
+        td Wait on Input
+      tr.unimplemented(v-if="showUnimplemented")
+        td
+          a(target="_blank", href="#{base}/m-code.html#mcode:m67") M67
+        td T
+        td Analog Output,Synchronized
+      tr.unimplemented(v-if="showUnimplemented")
+        td
+          a(target="_blank", href="#{base}/m-code.html#mcode:m68") M68
+        td T
+        td Analog Output, Immediate
+
+      tr.spacer-row.unimplemented(v-if="showUnimplemented"): th
+      tr.header-row.unimplemented(v-if="showUnimplemented")
+        th(colspan='3') User Defined Commands
+      tr.unimplemented(v-if="showUnimplemented")
+        td
+          a(target="_blank", href="#{base}/m-code.html#mcode:m100-m199")
+            | M101 - M199
+        td P Q
+        td User Defined Commands
+
+      tr.spacer-row: th
+      tr.header-row
+        th(colspan='3') Canned cycles
+      tr
+        td
+          a(target="_blank", href="#{base}/g-code.html#gcode:g80") G80
+        td
+        td Cancel Canned Cycle
+      tr
+        td
+          a(target="_blank", href="#{base}/g-code.html#gcode:g81") G81
+        td R L (P)
+        td Drilling Cycle
+      tr
+        td
+          a(target="_blank", href="#{base}/g-code.html#gcode:g82") G82
+        td R L (P)
+        td Drilling Cycle, Dwell
+      tr.unimplemented(v-if="showUnimplemented")
+        td
+          a(target="_blank", href="#{base}/g-code.html#gcode:g83") G83
+        td R L Q
+        td Drilling Cycle, Peck
+      tr
+        td
+          a(target="_blank", href="#{base}/g-code.html#gcode:g73") G73
+        td R L Q
+        td Drilling Cycle, Chip Breaking
+      tr
+        td
+          a(target="_blank", href="#{base}/g-code.html#gcode:g85") G85
+        td R L (P)
+        td Boring Cycle, Feed Out
+      tr
+        td
+          a(target="_blank", href="#{base}/g-code.html#gcode:g89") G89
+        td R L (P)
+        td Boring Cycle, Dwell, Feed Out
+      tr.unimplemented(v-if="showUnimplemented")
+        td
+          a(target="_blank", href="#{base}/g-code.html#gcode:g76") G76
+        td P Z I J R K Q H L E
+        td Threading Cycle
+      tr.unimplemented(v-if="showUnimplemented")
+        td
+          a(target="_blank", href="#{base}/g-code.html#gcode:g98-g99") G98, G99
+        td
+        td Canned Cycle Return Level
+
+      tr.spacer-row: th
+      tr.header-row
+        th(colspan='3') Comments & Messages
+      tr
+        td
+          a(target="_blank", href="#{base}/overview.html#gcode:comments") ; (…)
+        td
+        td Comments
+      tr
+        td
+          a(target="_blank", href="#{base}/overview.html#gcode:messages")
+            | (MSG,…)
+        td
+        td Messages
+      tr.unimplemented(v-if="showUnimplemented")
+        td
+          a(target="_blank", href="#{base}/overview.html#gcode:debug") (DEBUG,…)
+        td
+        td Debug Messages
+      tr.unimplemented(v-if="showUnimplemented")
+        td
+          a(target="_blank", href="#{base}/overview.html#gcode:print") (PRINT,…)
+        td
+        td Print Messages
+
+    div
+      input(type="checkbox", v-model="showUnimplemented")
+      label Show unsupported codes
+
+    h2 Further GCode Programming Documentation
+
+    p
+      | The Buildbotics controller implements a subset of LinuxCNC GCode.
+      | Supported commands are listed above.  You can find further help with
+      | <a href="http://wikipedia.com/wiki/G-code" target="_blank">GCode</a>
+      | programming on the LinuxCNC website:
+
+    ul
+      li: a(href="http://linuxcnc.org/docs/html/gcode/overview.html",
+        target="_blank")
+        | G Code overview
+      li: a(href="http://linuxcnc.org/docs/html/gcode/g-code.html",
+        target="_blank")
+        | G Code reference
+      li: a(href="http://linuxcnc.org/docs/html/gcode/m-code.html",
+        target="_blank")
+        | M Code reference