Python
Python Cutter class
The following class should get any Python programmer started communicating with the Cricut. This code requires at least Python 2.6 and may require some minor changes to function in Python 3.x. Windows users will need pyserial. Users with First Generation Hardware who want built-in support for Command 0x40 will need pyXXTEA, but this is an optional dependency; if you don't have pyXXTEA, Cutter.cmd40 will simply throw NotImplementedError.
import random, math, serial try: import pyxxtea except: pyxxtea = None class Cutter(serial.Serial): # Constants for cmd40 CutLine = 0 CutCurve = 1 MoveTo = 2 LowerAndCutLine = 3 CutLineAndRaise = 4 LowerAndCutCurve = 5 CutCurveAndRaise = 6 DecryptKey = 7 # XXTEA encryption keys KEY0 = [ 0x27, 0x2D, 0x6C, 0x37, 0x34, 0x2A, 0x61, 0x73, 0x36, 0x63, 0x25, 0x5B, 0x2B, 0x26, 0x5A, 0x4D ] KEY1 = [ 0x7D, 0x31, 0x6E, 0x22, 0x4A, 0x4A, 0x71, 0x33, 0x5A, 0x3C, 0x5C, 0x5F, 0x78, 0x61, 0x3A, 0x61 ] KEY2 = [ 0x47, 0x30, 0x2A, 0x23, 0x5D, 0x31, 0x48, 0x2F, 0x3B, 0x25, 0x7A, 0x61, 0x36, 0x71, 0x38, 0x2F ] KEY3 = [ 0x30, 0x3F, 0x68, 0x63, 0x71, 0x64, 0x6D, 0x30, 0x47, 0x69, 0x45, 0x7B, 0x6D, 0x34, 0x25, 0x69 ] KEY4 = [ 0x45, 0x35, 0x66, 0x50, 0x3A, 0x38, 0x6D, 0x69, 0x57, 0x5A, 0x70, 0x37, 0x33, 0x5F, 0x35, 0x7D ] KEY5 = [ 0x34, 0x3A, 0x21, 0x48, 0x61, 0x4F, 0x39, 0x25, 0x75, 0x3F, 0x69, 0x53, 0x47, 0x46, 0x36, 0x26 ] KEY6 = [ 0x3F, 0x62, 0x62, 0x6D, 0x7E, 0x55, 0x5F, 0x44, 0x7E, 0x29, 0x42, 0x5A, 0x52, 0x24, 0x62, 0x68 ] KEY7 = [ 0x47, 0x30, 0x2A, 0x23, 0x34, 0x2A, 0x61, 0x73, 0x47, 0x69, 0x45, 0x7B, 0x33, 0x5F, 0x35, 0x7D ] keys = (KEY0,KEY1,KEY2,KEY3,KEY4,KEY5,KEY6,KEY7) # Internal storage _model = None _verMaj = None _verMin = None def mkstr(*l): if type(l[0]) == list or type(l[0]) == tuple: l = l[0] rv = bytes() for i in l: rv = rv + chr(i) return rv def cmd(self, *args): """Writes a command to the device. The length will be calculated from the input, so do not include a length byte.""" self.write(Cutter.mkstr(len(args), *args)) rlen = ord(self.read(1)) rv = self.read(rlen) return rv def cmd40(self, c, x, y): """Writes an 0x40 command to the device. The first parameter is one of Cutter.CutLine, Cutter.CutCurve, Cutter.MoveTo, Cutter.LowerAndCutLine, Cutter.CutLineAndRaise, Cutter.LowerAndCutCurve, Cutter.CutCurveAndRaise, or Cutter.DecryptKey. The second and third parameters are the X and Y coordinates of the command.""" if pyxxtea is None: raise NotImplementedError() r = random.randint(10000,32767) plaintext = Cutter.mkstr(0, 0, r/256, r%256, 0, 0, x/256, x%256, 0, 0, y/256, y%256) cipher = pyxxtea.XXTEA(Cutter.mkstr(self.keys[c])).encrypt(plaintext) b = [ord(ch) for ch in cipher] self.write(Cutter.mkstr(13, 0x40, *b)) rlen = self.read(1) return self.read(ord(rlen)) def bounds(self): """Returns the mat bounds as (xmin, ymin, xmax, ymax).""" b = [ord(ch) for ch in self.cmd(0x11,0,0,0)] return b[0]*256+b[1], b[2]*256+b[3], b[4]*256+b[5], b[6]*256+b[7] def hasMat(self): """Returns True if a mat is loaded, False otherwise.""" b = self.cmd(0x14,0,0,0,0) return ord(b[3]) & 0x01 and True or False def _cacheModel(self): b = [ord(ch) for ch in self.cmd(0x12,0,0,0)] self._model = b[0]*256+b[1] self._verMaj = b[2]*256+b[3] self._verMin = b[4]*256+b[5] def model(self): """Returns the model ID of the device.""" if self._model is None: self._cacheModel(); return self._model def version(self): """Returns the firmware version of the device.""" if self._model is None: self._cacheModel(); return self._verMaj + self._verMin * math.pow(10, -math.ceil(math.log(self._verMin,10)))
Python sample code
These code examples have only been tested with Second Generation Hardware but the appropriate protocols for First Generation Hardware are implemented and should work.
Windows
import cutter # The first parameter is the port number, minus one. This example opens COM4. s = cutter.Cutter(3, timeout=1, baudrate=198347)
Linux
For First Generation Hardware you must have the FTDI serial port driver installed. For Second Generation Hardware you must have the USB ACM driver installed, although many mainstream Linux distributions have this provided by default.
import cutter # First generation hardware appears as a USB serial port s = cutter.Cutter('/dev/ttyUSB0', timeout=1, baudrate=198347) # Second generation hardware appears as an ACM device s = cutter.Cutter('/dev/ttyACM0', timeout=1)
Mac
For First Generation Hardware you must install the FTDI VCP driver. OSX has the necessary ACM drivers preinstalled. import cutter
# Second generation hardware appears as a modem. # You will find tty.usbmodem* and cu.usbmodem* nodes in /dev/. Update the filename with the correct number. # It is currently unclear whether the "tty" or "cu" version works better, or if there's even a difference. The official CraftRoom software uses "cu". # For first generation hardware, add baudrate=198347 to the parameter list. s = cutter.Cutter('/dev/cu.usbmodem411', timeout=1)
Example usage
print "The attached Cricut reports model 0x%x and firmware version %s." % (s.model(), s.version()) status = s.cmd(0x14, 0x00, 0x00, 0x00, 0x00) # or use s.hasMat() for access to the one documented status bit