├── .gitignore ├── Adafruit_Thermal.py ├── CHANGELOG.md ├── README.md ├── gfx ├── adalogo.py ├── adaqrcode.py ├── aykm.bmp ├── notbad.bmp └── qrcode └── printertest.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | parts/ 18 | sdist/ 19 | var/ 20 | wheels/ 21 | *.egg-info/ 22 | .installed.cfg 23 | *.egg 24 | MANIFEST 25 | 26 | # PyInstaller 27 | # Usually these files are written by a python script from a template 28 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 29 | *.manifest 30 | *.spec 31 | 32 | # Installer logs 33 | pip-log.txt 34 | pip-delete-this-directory.txt 35 | 36 | # Unit test / coverage reports 37 | htmlcov/ 38 | .tox/ 39 | .coverage 40 | .coverage.* 41 | .cache 42 | nosetests.xml 43 | coverage.xml 44 | *.cover 45 | .hypothesis/ 46 | 47 | # Translations 48 | *.mo 49 | *.pot 50 | 51 | # Django stuff: 52 | *.log 53 | .static_storage/ 54 | .media/ 55 | local_settings.py 56 | 57 | # Flask stuff: 58 | instance/ 59 | .webassets-cache 60 | 61 | # Scrapy stuff: 62 | .scrapy 63 | 64 | # Sphinx documentation 65 | docs/_build/ 66 | 67 | # PyBuilder 68 | target/ 69 | 70 | # Jupyter Notebook 71 | .ipynb_checkpoints 72 | 73 | # pyenv 74 | .python-version 75 | 76 | # celery beat schedule file 77 | celerybeat-schedule 78 | 79 | # SageMath parsed files 80 | *.sage.py 81 | 82 | # Environments 83 | .env 84 | .venv 85 | env/ 86 | venv/ 87 | ENV/ 88 | env.bak/ 89 | venv.bak/ 90 | 91 | # Spyder project settings 92 | .spyderproject 93 | .spyproject 94 | 95 | # Rope project settings 96 | .ropeproject 97 | 98 | # mkdocs documentation 99 | /site 100 | 101 | # mypy 102 | .mypy_cache/ 103 | 104 | *~ 105 | .*~ 106 | -------------------------------------------------------------------------------- /Adafruit_Thermal.py: -------------------------------------------------------------------------------- 1 | #************************************************************************* 2 | # This is a MicroPython library for the Adafruit Thermal Printer, based on 3 | # Python library by Adafruit. For more info and troubleshooting see the 4 | # README and the original repo at: 5 | # https://github.com/adafruit/Python-Thermal-Printer 6 | # 7 | # Tested with LoPy v1. 8 | # The RX pin functionality was not tested. 9 | # 10 | # MicroPython port by Dominik Kapusta. 11 | #************************************************************************* 12 | # This is a Python library for the Adafruit Thermal Printer. 13 | # Pick one up at --> http://www.adafruit.com/products/597 14 | # These printers use TTL serial to communicate, 2 pins are required. 15 | # IMPORTANT: On 3.3V systems (e.g. Raspberry Pi), use a 10K resistor on 16 | # the RX pin (TX on the printer, green wire), or simply leave unconnected. 17 | # 18 | # Adafruit invests time and resources providing this open source code. 19 | # Please support Adafruit and open-source hardware by purchasing products 20 | # from Adafruit! 21 | # 22 | # Written by Limor Fried/Ladyada for Adafruit Industries. 23 | # Python port by Phil Burgess for Adafruit Industries. 24 | # MIT license, all text above must be included in any redistribution. 25 | #************************************************************************* 26 | 27 | # This is pretty much a 1:1 direct Python port of the Adafruit_Thermal 28 | # library for Arduino. All methods use the same naming conventions as the 29 | # Arduino library, with only slight changes in parameter behavior where 30 | # needed. This should simplify porting existing Adafruit_Thermal-based 31 | # printer projects to Raspberry Pi, BeagleBone, etc. See printertest.py 32 | # for an example. 33 | # 34 | # One significant change is the addition of the printImage() function, 35 | # which ties this to the Python Imaging Library and opens the door to a 36 | # lot of cool graphical stuff! 37 | # 38 | # TO DO: 39 | # - Might use standard ConfigParser library to put thermal calibration 40 | # settings in a global configuration file (rather than in the library). 41 | # - Make this use proper Python library installation procedure. 42 | # - Trap errors properly. Some stuff just falls through right now. 43 | # - Add docstrings throughout! 44 | 45 | from machine import UART 46 | import utime 47 | import sys 48 | 49 | class BitmapHeader: 50 | SIZE_IN_BYTES = 14 51 | 52 | def __init__(self, bytes): 53 | if len(bytes) != 14: 54 | raise ValueError 55 | 56 | if bytes[0:2] != b'BM': 57 | raise ValueError 58 | 59 | self.file_size = int.from_bytes(bytes[2:6], 'little') 60 | self.data_offset = int.from_bytes(bytes[-4:], 'little') 61 | 62 | 63 | class BitmapHeaderInfo: 64 | SIZE_IN_BYTES = 40 65 | 66 | def __init__(self, bytes): 67 | if len(bytes) != 40: 68 | raise ValueError 69 | if int.from_bytes(bytes[12:14], 'little') != 1: 70 | raise ValueError # planes 71 | if int.from_bytes(bytes[14:16], 'little') != 1: 72 | raise ValueError # bit-depth 73 | if int.from_bytes(bytes[16:20], 'little') != 0: 74 | raise ValueError # compression 75 | if int.from_bytes(bytes[32:36], 'little') > 1: 76 | raise ValueError # we accept at most 1 color 77 | if int.from_bytes(bytes[36:40], 'little') > 1: 78 | raise ValueError # we accept at most 1 significant color 79 | 80 | self.width = int.from_bytes(bytes[4:8], 'little') 81 | self.height = int.from_bytes(bytes[8:12], 'little') 82 | 83 | self.width_in_bytes = int((self.width+7)/8) 84 | padding = (4 - int(self.width_in_bytes % 4)) % 4 85 | 86 | self.line_width = self.width_in_bytes + padding 87 | self.width_padding = (self.width_in_bytes + padding) * 8 - self.width 88 | self.last_byte_padding = self.width_in_bytes * 8 - self.width 89 | 90 | self.data_size = int.from_bytes(bytes[20:24], 'little') 91 | self.ppm_x = int.from_bytes(bytes[24:28], 'little') 92 | self.ppm_y = int.from_bytes(bytes[28:32], 'little') 93 | 94 | 95 | class Adafruit_Thermal: 96 | 97 | resumeTime = 0.0 98 | byteTime = 0 99 | dotPrintTime = 0.0 100 | dotFeedTime = 0.0 101 | prevByte = '\n' 102 | column = 0 103 | maxColumn = 32 104 | charHeight = 24 105 | lineSpacing = 8 106 | barcodeHeight = 50 107 | printMode = 0 108 | defaultHeatDots = 11 109 | defaultHeatTime = 120 110 | defaultHeatInterval = 40 111 | baudrate = 9600 112 | pins = None 113 | 114 | def __init__(self, bus=1, baudrate=9600, pins=None, **kwargs): 115 | # Fallback to regular +,- operator in case 116 | # utime.ticks_add and/or utime.ticks_diff are unavailable. 117 | self.ticks_diff = utime.ticks_diff if hasattr(utime, 'ticks_diff') else lambda x, y: x - y 118 | self.ticks_add = utime.ticks_add if hasattr(utime, 'ticks_add') else lambda x, y: x + y 119 | 120 | # Calculate time to issue one byte to the printer. 121 | # 11 bits (not 8) to accommodate idle, start and 122 | # stop bits. Idle time might be unnecessary, but 123 | # erring on side of caution here. 124 | self.byteTime = round((11 / baudrate) * 1000) # ms 125 | 126 | self.baudrate = baudrate 127 | 128 | # ESP8266 doesn't support pins parameter 129 | extra_options = {} 130 | if pins is not None: 131 | extra_options['pins'] = pins 132 | 133 | self.uart = UART(bus, baudrate=baudrate, stop=1, **extra_options) 134 | 135 | # Remainder of this method was previously in begin() 136 | 137 | # The printer can't start receiving data immediately 138 | # upon power up -- it needs a moment to cold boot 139 | # and initialize. Allow at least 1/2 sec of uptime 140 | # before printer can receive data. 141 | self.timeoutSet(500) 142 | 143 | # Description of print settings from p. 23 of manual: 144 | # ESC 7 n1 n2 n3 Setting Control Parameter Command 145 | # Decimal: 27 55 n1 n2 n3 146 | # max heating dots, heating time, heating interval 147 | # n1 = 0-255 Max heat dots, Unit (8dots), Default: 7 (64 dots) 148 | # n2 = 3-255 Heating time, Unit (10us), Default: 80 (800us) 149 | # n3 = 0-255 Heating interval, Unit (10us), Default: 2 (20us) 150 | # The more max heating dots, the more peak current 151 | # will cost when printing, the faster printing speed. 152 | # The max heating dots is 8*(n1+1). The more heating 153 | # time, the more density, but the slower printing 154 | # speed. If heating time is too short, blank page 155 | # may occur. The more heating interval, the more 156 | # clear, but the slower printing speed. 157 | 158 | heatDots = kwargs.get('heatdots', self.defaultHeatDots) 159 | heatTime = kwargs.get('heattime', self.defaultHeatTime) 160 | heatInterval = kwargs.get('heatinterval', self.defaultHeatInterval) 161 | self.writeBytes( 162 | 27, # Esc 163 | 55, # 7 (print settings) 164 | heatDots, # Heat dots 165 | heatTime, # Lib default 166 | heatInterval) # Heat interval 167 | 168 | # Description of print density from p. 23 of manual: 169 | # DC2 # n Set printing density 170 | # Decimal: 18 35 n 171 | # D4..D0 of n is used to set the printing density. 172 | # Density is 50% + 5% * n(D4-D0) printing density. 173 | # D7..D5 of n is used to set the printing break time. 174 | # Break time is n(D7-D5)*250us. 175 | # (Unsure of default values -- not documented) 176 | 177 | printDensity = 10 # 100% 178 | printBreakTime = 2 # 500 uS 179 | 180 | self.writeBytes( 181 | 18, # DC2 182 | 35, # Print density 183 | (printBreakTime << 5) | printDensity) 184 | self.dotPrintTime = 30 185 | self.dotFeedTime = 2 186 | 187 | # Because there's no flow control between the printer and computer, 188 | # special care must be taken to avoid overrunning the printer's 189 | # buffer. Serial output is throttled based on serial speed as well 190 | # as an estimate of the device's print and feed rates (relatively 191 | # slow, being bound to moving parts and physical reality). After 192 | # an operation is issued to the printer (e.g. bitmap print), a 193 | # timeout is set before which any other printer operations will be 194 | # suspended. This is generally more efficient than using a delay 195 | # in that it allows the calling code to continue with other duties 196 | # (e.g. receiving or decoding an image) while the printer 197 | # physically completes the task. 198 | 199 | # Sets estimated completion time for a just-issued task. 200 | def timeoutSet(self, x): 201 | self.resumeTime = self.ticks_add(utime.ticks_ms(), x) 202 | 203 | # Waits (if necessary) for the prior task to complete. 204 | def timeoutWait(self): 205 | while self.ticks_diff(utime.ticks_ms(), self.resumeTime) < 0: pass 206 | 207 | # Printer performance may vary based on the power supply voltage, 208 | # thickness of paper, phase of the moon and other seemingly random 209 | # variables. This method sets the times (in microseconds) for the 210 | # paper to advance one vertical 'dot' when printing and feeding. 211 | # For example, in the default initialized state, normal-sized text 212 | # is 24 dots tall and the line spacing is 32 dots, so the time for 213 | # one line to be issued is approximately 24 * print time + 8 * feed 214 | # time. The default print and feed times are based on a random 215 | # test unit, but as stated above your reality may be influenced by 216 | # many factors. This lets you tweak the timing to avoid excessive 217 | # delays and/or overrunning the printer buffer. 218 | def setTimes(self, p, f): 219 | # Units are in microseconds for 220 | # compatibility with Arduino library 221 | self.dotPrintTime = p / 1000000.0 222 | self.dotFeedTime = f / 1000000.0 223 | 224 | # 'Raw' byte-writing method 225 | def writeBytes(self, *args): 226 | self.timeoutWait() 227 | self.timeoutSet(len(args) * self.byteTime) 228 | self.uart.write(bytes(args)) 229 | 230 | # Override write() method to keep track of paper feed. 231 | def write(self, *data): 232 | for char in data: 233 | if char != 0x13: 234 | self.timeoutWait() 235 | self.uart.write(char) 236 | d = self.byteTime 237 | if ((char == '\n') or 238 | (self.column == self.maxColumn)): 239 | # Newline or wrap 240 | if self.prevByte == '\n': 241 | # Feed line (blank) 242 | d += ((self.charHeight + 243 | self.lineSpacing) * 244 | self.dotFeedTime) 245 | else: 246 | # Text line 247 | d += int(((self.charHeight * 248 | self.dotPrintTime) + 249 | (self.lineSpacing * 250 | self.dotFeedTime))) 251 | self.column = 0 252 | # Treat wrap as newline 253 | # on next pass 254 | char = '\n' 255 | else: 256 | self.column += 1 257 | self.timeoutSet(d) 258 | self.prevByte = char 259 | 260 | # The bulk of this method was moved into __init__, 261 | # but this is left here for compatibility with older 262 | # code that might get ported directly from Arduino. 263 | def begin(self, heatTime=defaultHeatTime): 264 | self.writeBytes( 265 | 27, # Esc 266 | 55, # 7 (print settings) 267 | 11, # Heat dots 268 | heatTime, 269 | 40) # Heat interval 270 | 271 | def reset(self): 272 | self.writeBytes(27, 64) # Esc @ = init command 273 | self.prevByte = '\n' # Treat as if prior line is blank 274 | self.column = 0 275 | self.maxColumn = 32 276 | self.charHeight = 24 277 | self.lineSpacing = 6 278 | self.barcodeHeight = 50 279 | # Configure tab stops 280 | self.writeBytes(27, 68) # Set tab stops 281 | self.writeBytes( 4, 8, 12, 16) # every 4 columns, 282 | self.writeBytes(20, 24, 28, 0) # 0 is end-of-list. 283 | 284 | # Reset text formatting parameters. 285 | def setDefault(self): 286 | self.online() 287 | self.justify('L') 288 | self.inverseOff() 289 | self.doubleHeightOff() 290 | self.setLineHeight(30) 291 | self.boldOff() 292 | self.underlineOff() 293 | self.setBarcodeHeight(50) 294 | self.setSize('s') 295 | self.setCharset() 296 | self.setCodePage() 297 | 298 | def test(self): 299 | self.write("Hello world!") 300 | self.feed(2) 301 | 302 | def testPage(self): 303 | self.writeBytes(18, 84) 304 | self.timeoutSet( 305 | self.dotPrintTime * 24 * 26 + 306 | self.dotFeedTime * (6 * 26 + 30)) 307 | 308 | def setBarcodeHeight(self, val=50): 309 | if val < 1: val = 1 310 | self.barcodeHeight = val 311 | self.writeBytes(29, 104, val) 312 | 313 | UPC_A = 0 314 | UPC_E = 1 315 | EAN13 = 2 316 | EAN8 = 3 317 | CODE39 = 4 318 | I25 = 5 319 | CODEBAR = 6 320 | CODE93 = 7 321 | CODE128 = 8 322 | CODE11 = 9 323 | MSI = 10 324 | ITF = 11 325 | CODABAR = 12 326 | 327 | def printBarcode(self, text, type): 328 | 329 | upcCodes = { # UPC codes & values for firmwareVersion >= 264 330 | self.UPC_A : 65, 331 | self.UPC_E : 66, 332 | self.EAN13 : 67, 333 | self.EAN8 : 68, 334 | self.CODE39 : 69, 335 | self.ITF : 70, 336 | self.CODABAR : 71, 337 | self.CODE93 : 72, 338 | self.CODE128 : 73, 339 | self.I25 : -1, # NOT IN NEW FIRMWARE 340 | self.CODEBAR : -1, 341 | self.CODE11 : -1, 342 | self.MSI : -1 343 | } 344 | 345 | n = upcCodes[type] 346 | if n == -1: return 347 | self.feed(1) # Recent firmware requires this? 348 | self.writeBytes( 349 | 29, 72, 2, # Print label below barcode 350 | 29, 119, 3, # Barcode width 351 | 29, 107, n) # Barcode type 352 | self.timeoutWait() 353 | self.timeoutSet((self.barcodeHeight + 40) * self.dotPrintTime) 354 | # Print string: write length byte + string sans NUL 355 | n = len(text) 356 | if n > 255: n = 255 357 | self.uart.write(chr(n)) 358 | self.uart.write(text) 359 | self.prevByte = '\n' 360 | 361 | # === Character commands === 362 | 363 | UPDOWN_MASK = (1 << 2) 364 | BOLD_MASK = (1 << 3) 365 | DOUBLE_HEIGHT_MASK = (1 << 4) 366 | DOUBLE_WIDTH_MASK = (1 << 5) 367 | STRIKE_MASK = (1 << 6) 368 | 369 | def setPrintMode(self, mask): 370 | self.printMode |= mask 371 | self.writePrintMode() 372 | if self.printMode & self.DOUBLE_HEIGHT_MASK: 373 | self.charHeight = 48 374 | else: 375 | self.charHeight = 24 376 | if self.printMode & self.DOUBLE_WIDTH_MASK: 377 | self.maxColumn = 16 378 | else: 379 | self.maxColumn = 32 380 | 381 | def unsetPrintMode(self, mask): 382 | self.printMode &= ~mask 383 | self.writePrintMode() 384 | if self.printMode & self.DOUBLE_HEIGHT_MASK: 385 | self.charHeight = 48 386 | else: 387 | self.charHeight = 24 388 | if self.printMode & self.DOUBLE_WIDTH_MASK: 389 | self.maxColumn = 16 390 | else: 391 | self.maxColumn = 32 392 | 393 | def writePrintMode(self): 394 | self.writeBytes(27, 33, self.printMode) 395 | 396 | def normal(self): 397 | self.printMode = 0 398 | self.writePrintMode() 399 | 400 | def inverseOn(self): 401 | self.writeBytes(29, 66, 1) 402 | 403 | def inverseOff(self): 404 | self.writeBytes(29, 66, 0) 405 | 406 | def upsideDownOn(self): 407 | self.setPrintMode(self.UPDOWN_MASK) 408 | 409 | def upsideDownOff(self): 410 | self.unsetPrintMode(self.UPDOWN_MASK) 411 | 412 | def doubleHeightOn(self): 413 | self.setPrintMode(self.DOUBLE_HEIGHT_MASK) 414 | 415 | def doubleHeightOff(self): 416 | self.unsetPrintMode(self.DOUBLE_HEIGHT_MASK) 417 | 418 | def doubleWidthOn(self): 419 | self.setPrintMode(self.DOUBLE_WIDTH_MASK) 420 | 421 | def doubleWidthOff(self): 422 | self.unsetPrintMode(self.DOUBLE_WIDTH_MASK) 423 | 424 | def strikeOn(self): 425 | self.setPrintMode(self.STRIKE_MASK) 426 | 427 | def strikeOff(self): 428 | self.unsetPrintMode(self.STRIKE_MASK) 429 | 430 | def boldOn(self): 431 | self.setPrintMode(self.BOLD_MASK) 432 | 433 | def boldOff(self): 434 | self.unsetPrintMode(self.BOLD_MASK) 435 | 436 | def justify(self, value): 437 | c = value.upper() 438 | if c == 'C': 439 | pos = 1 440 | elif c == 'R': 441 | pos = 2 442 | else: 443 | pos = 0 444 | self.writeBytes(0x1B, 0x61, pos) 445 | 446 | # Feeds by the specified number of lines 447 | def feed(self, x=1): 448 | self.writeBytes(27, 100, x) 449 | self.timeoutSet(self.dotFeedTime * self.charHeight) 450 | self.prevByte = '\n' 451 | self.column = 0 452 | 453 | # Feeds by the specified number of individual pixel rows 454 | def feedRows(self, rows): 455 | self.writeBytes(27, 74, rows) 456 | self.timeoutSet(rows * dotFeedTime) 457 | self.prevByte = '\n' 458 | self.column = 0 459 | 460 | def flush(self): 461 | self.writeBytes(12) # ASCII FF 462 | 463 | def setSize(self, value): 464 | c = value.upper() 465 | if c == 'L': # Large: double width and height 466 | size = 0x11 467 | self.charHeight = 48 468 | self.maxColumn = 16 469 | elif c == 'M': # Medium: double height 470 | size = 0x01 471 | self.charHeight = 48 472 | self.maxColumn = 32 473 | else: # Small: standard width and height 474 | size = 0x00 475 | self.charHeight = 24 476 | self.maxColumn = 32 477 | 478 | self.writeBytes(29, 33, size) 479 | prevByte = '\n' # Setting the size adds a linefeed 480 | 481 | # Underlines of different weights can be produced: 482 | # 0 - no underline 483 | # 1 - normal underline 484 | # 2 - thick underline 485 | def underlineOn(self, weight=1): 486 | if weight > 2: weight = 2 487 | self.writeBytes(27, 45, weight) 488 | 489 | def underlineOff(self): 490 | self.writeBytes(27, 45, 0) 491 | 492 | def printBitmap(self, w, h, bitmap, LaaT=False): 493 | rowBytes = int((w + 7) / 8) # Round up to next byte boundary 494 | if rowBytes >= 48: 495 | rowBytesClipped = 48 # 384 pixels max width 496 | else: 497 | rowBytesClipped = rowBytes 498 | 499 | # if LaaT (line-at-a-time) is True, print bitmaps 500 | # scanline-at-a-time (rather than in chunks). 501 | # This tends to make for much cleaner printing 502 | # (no feed gaps) on large images...but has the 503 | # opposite effect on small images that would fit 504 | # in a single 'chunk', so use carefully! 505 | if LaaT: maxChunkHeight = 1 506 | else: maxChunkHeight = 50 # lower max chunk (not 255) for memory-constrained systems (LoPy v1) 507 | 508 | i = 0 509 | for rowStart in range(0, h, maxChunkHeight): 510 | chunkHeight = h - rowStart 511 | if chunkHeight > maxChunkHeight: 512 | chunkHeight = maxChunkHeight 513 | 514 | # Timeout wait happens here 515 | self.writeBytes(18, 42, chunkHeight, rowBytesClipped) 516 | 517 | for y in range(chunkHeight): 518 | line = bitmap[i:i+rowBytesClipped] 519 | self.timeoutWait() 520 | self.timeoutSet(len(line) * self.dotPrintTime) 521 | self.uart.write(bytearray(line)) 522 | i += rowBytes 523 | 524 | self.prevByte = '\n' 525 | 526 | def printBitmapFromFile(self, w, h, filename, LaaT=False): 527 | rowBytes = int((w + 7) / 8) # Round up to next byte boundary 528 | if rowBytes >= 48: 529 | rowBytesClipped = 48 # 384 pixels max width 530 | else: 531 | rowBytesClipped = rowBytes 532 | 533 | # if LaaT (line-at-a-time) is True, print bitmaps 534 | # scanline-at-a-time (rather than in chunks). 535 | # This tends to make for much cleaner printing 536 | # (no feed gaps) on large images...but has the 537 | # opposite effect on small images that would fit 538 | # in a single 'chunk', so use carefully! 539 | if LaaT: maxChunkHeight = 1 540 | else: maxChunkHeight = 50 # lower max chunk (not 255) for memory-constrained systems (LoPy v1) 541 | 542 | try: 543 | with open(filename, 'rb') as bitmap_file: 544 | i = 0 545 | for rowStart in range(0, h, maxChunkHeight): 546 | chunkHeight = h - rowStart 547 | if chunkHeight > maxChunkHeight: 548 | chunkHeight = maxChunkHeight 549 | 550 | # Timeout wait happens here 551 | self.writeBytes(18, 42, chunkHeight, rowBytesClipped) 552 | 553 | for y in range(chunkHeight): 554 | line = bytearray(bitmap_file.read(rowBytesClipped)) 555 | self.timeoutWait() 556 | self.timeoutSet(rowBytesClipped * self.dotPrintTime) 557 | self.uart.write(line) 558 | i += rowBytes 559 | bitmap_file.seek(i) 560 | 561 | self.prevByte = '\n' 562 | 563 | except OSError as e: 564 | print('file access error: {}'.format(e.errno)) 565 | 566 | 567 | def printBMPImage(self, filename, LaaT=False): 568 | try: 569 | with open(filename, 'rb') as bmp_file: 570 | header = BitmapHeader(bmp_file.read(BitmapHeader.SIZE_IN_BYTES)) 571 | header_info = BitmapHeaderInfo(bmp_file.read(BitmapHeaderInfo.SIZE_IN_BYTES)) 572 | 573 | data_end = header.file_size - 2 574 | 575 | if header_info.width_in_bytes >= 48: 576 | rowBytesClipped = 48 # 384 pixels max width 577 | else: 578 | rowBytesClipped = header_info.width_in_bytes 579 | 580 | # if LaaT (line-at-a-time) is True, print bitmaps 581 | # scanline-at-a-time (rather than in chunks). 582 | # This tends to make for much cleaner printing 583 | # (no feed gaps) on large images...but has the 584 | # opposite effect on small images that would fit 585 | # in a single 'chunk', so use carefully! 586 | if LaaT: maxChunkHeight = 1 587 | else: maxChunkHeight = 50 # lower max chunk (not 255) for memory-constrained systems (LoPy v1) 588 | 589 | for startRow in range(0, header_info.height, maxChunkHeight): 590 | chunkHeight = header_info.height - startRow 591 | if chunkHeight > maxChunkHeight: 592 | chunkHeight = maxChunkHeight 593 | 594 | # Timeout wait happens here 595 | self.writeBytes(18, 42, chunkHeight, rowBytesClipped) 596 | for row in range(startRow + 1, startRow + 1 + chunkHeight): 597 | # seek to beginning of line 598 | bmp_file.seek(data_end - row * header_info.line_width) 599 | 600 | # read the whole line 601 | if header_info.last_byte_padding == 0 or header_info.width_in_bytes <= rowBytesClipped: 602 | line = bytearray(bmp_file.read(rowBytesClipped)) 603 | self.timeoutWait() 604 | self.timeoutSet(self.dotPrintTime) 605 | self.uart.write(line) 606 | else: 607 | self.timeoutWait() 608 | self.timeoutSet(self.dotPrintTime) 609 | if rowBytesClipped > 1: 610 | line = bytearray(bmp_file.read(rowBytesClipped-1)) 611 | self.uart.write(line) 612 | 613 | data = bmp_file.read(1) 614 | mask = 0xFF< 15: val = 15 693 | self.writeBytes(27, 82, val) 694 | 695 | CODEPAGE_CP437 = 0 # USA, Standard Europe 696 | CODEPAGE_KATAKANA = 1 697 | CODEPAGE_CP850 = 2 # Multilingual 698 | CODEPAGE_CP860 = 3 # Portuguese 699 | CODEPAGE_CP863 = 4 # Canadian-French 700 | CODEPAGE_CP865 = 5 # Nordic 701 | CODEPAGE_WCP1251 = 6 # Cyrillic 702 | CODEPAGE_CP866 = 7 # Cyrillic #2 703 | CODEPAGE_MIK = 8 # Cyrillic/Bulgarian 704 | CODEPAGE_CP755 = 9 # East Europe, Latvian 2 705 | CODEPAGE_IRAN = 10 706 | CODEPAGE_CP862 = 15 # Hebrew 707 | CODEPAGE_WCP1252 = 16 # Latin 1 708 | CODEPAGE_WCP1253 = 17 # Greek 709 | CODEPAGE_CP852 = 18 # Latin 2 710 | CODEPAGE_CP858 = 19 # Multilingual Latin 1 + Euro 711 | CODEPAGE_IRAN2 = 20 712 | CODEPAGE_LATVIAN = 21 713 | CODEPAGE_CP864 = 22 # Arabic 714 | CODEPAGE_ISO_8859_1 = 23 # West Europe 715 | CODEPAGE_CP737 = 24 # Greek 716 | CODEPAGE_WCP1257 = 25 # Baltic 717 | CODEPAGE_THAI = 26 718 | CODEPAGE_CP720 = 27 # Arabic 719 | CODEPAGE_CP855 = 28 720 | CODEPAGE_CP857 = 29 # Turkish 721 | CODEPAGE_WCP1250 = 30 # Central Europe 722 | CODEPAGE_CP775 = 31 723 | CODEPAGE_WCP1254 = 32 # Turkish 724 | CODEPAGE_WCP1255 = 33 # Hebrew 725 | CODEPAGE_WCP1256 = 34 # Arabic 726 | CODEPAGE_WCP1258 = 35 # Vietnam 727 | CODEPAGE_ISO_8859_2 = 36 # Latin 2 728 | CODEPAGE_ISO_8859_3 = 37 # Latin 3 729 | CODEPAGE_ISO_8859_4 = 38 # Baltic 730 | CODEPAGE_ISO_8859_5 = 39 # Cyrillic 731 | CODEPAGE_ISO_8859_6 = 40 # Arabic 732 | CODEPAGE_ISO_8859_7 = 41 # Greek 733 | CODEPAGE_ISO_8859_8 = 42 # Hebrew 734 | CODEPAGE_ISO_8859_9 = 43 # Turkish 735 | CODEPAGE_ISO_8859_15 = 44 # Latin 3 736 | CODEPAGE_THAI2 = 45 737 | CODEPAGE_CP856 = 46 738 | CODEPAGE_CP874 = 47 739 | 740 | # Selects alt symbols for 'upper' ASCII values 0x80-0xFF 741 | def setCodePage(self, val=0): 742 | if val > 47: val = 47 743 | self.writeBytes(27, 116, val) 744 | 745 | # Copied from Arduino lib for parity; may not work on all printers 746 | def tab(self): 747 | self.writeBytes(9) 748 | self.column = (self.column + 4) & 0xFC 749 | 750 | # Copied from Arduino lib for parity; may not work on all printers 751 | def setCharSpacing(self, spacing): 752 | self.writeBytes(27, 32, spacing) 753 | 754 | # Overloading print() in Python pre-3.0 is dirty pool, 755 | # but these are here to provide more direct compatibility 756 | # with existing code written for the Arduino library. 757 | def print(self, *args, **kwargs): 758 | for arg in args: 759 | self.write(str(arg)) 760 | 761 | # For Arduino code compatibility again 762 | def println(self, *args, **kwargs): 763 | for arg in args: 764 | self.write(str(arg)) 765 | self.write('\n') 766 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ### 0.3.1 (2017-12-20): 2 | * Replace time with utime and fix timeouts for .bmp printing 3 | 4 | ### 0.3.0 (2017-12-19): 5 | * Add support for printing .bmp images from file 6 | 7 | ### 0.2.0 (2017-12-16): 8 | * Fix printing bitmaps 9 | * Add support for printing bitmaps from file 10 | 11 | ### 0.1.0 (2017-12-09): 12 | * Initial version of the Python port 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MicroPython Thermal Printer 2 | This is the MicroPython port of Python Thermal Printer by Adafruit, available [elsewhere on GitHub](https://github.com/adafruit/Python-Thermal-Printer). 3 | 4 | Read the full story at https://kapusta.cc/2017/12/11/thermal-printer-library-for-micropython/. 5 | 6 | ## The summary of changes 7 | 8 | 1. Removed Python 2.x code 9 | 1. Removed `writeToStdout()` 10 | 1. Replaced `Serial` with `machine.UART` 11 | 1. Removed support for pre-2.68 firmware 12 | 1. Removed image printing method, because it depended on Python Imaging Library 13 | 1. Disabled calls to `wake()` and `reset()` at initialization 14 | 1. Fixes to `sleepAfter()` and `wake()` 15 | 16 | ## Additions 17 | 18 | * `printBitmapFromFile()` - allows for printing the bitmap from file on disk, instead of reading it from array of bytes like `printBitmap()` does. 19 | * `printBMPImage()` - allows for printing the actual .bmp image. Supports 1-bit Windows BMP format only, up to 384px wide. 20 | * heat dots, heat time and heat interval settings are parametrized in the initializer to fine tune printer output. 21 | 22 | ## Usage 23 | 24 | See [printertest.py]( https://github.com/ayoy/micropython-thermal-printer/blob/master/printertest.py) for usage example. 25 | 26 | ## Testing 27 | 28 | So far I tested it only with LoPy version 1. Works fine, besides printing large bitmaps that required too much memory for the LoPy (for the bitmap provided with printertest.py) - hence `printBitmapFromFile()`. 29 | -------------------------------------------------------------------------------- /gfx/adalogo.py: -------------------------------------------------------------------------------- 1 | width = 75 2 | height = 75 3 | data = [ 4 | 0x00,0x00,0x00,0x00,0x00,0xe0,0x00,0x00,0x00,0x00, 5 | 0x00,0x00,0x00,0x00,0x01,0xf0,0x00,0x00,0x00,0x00, 6 | 0x00,0x00,0x00,0x00,0x03,0xf0,0x00,0x00,0x00,0x00, 7 | 0x00,0x00,0x00,0x00,0x03,0xf8,0x00,0x00,0x00,0x00, 8 | 0x00,0x00,0x00,0x00,0x07,0xf8,0x00,0x00,0x00,0x00, 9 | 0x00,0x00,0x00,0x00,0x0f,0xf8,0x00,0x00,0x00,0x00, 10 | 0x00,0x00,0x00,0x00,0x1f,0xfc,0x00,0x00,0x00,0x00, 11 | 0x00,0x00,0x00,0x00,0x1f,0xfc,0x00,0x00,0x00,0x00, 12 | 0x00,0x00,0x00,0x00,0x3f,0xfc,0x00,0x00,0x00,0x00, 13 | 0x00,0x00,0x00,0x00,0x7f,0xfe,0x00,0x00,0x00,0x00, 14 | 0x00,0x00,0x00,0x00,0x7f,0xfe,0x00,0x00,0x00,0x00, 15 | 0x00,0x00,0x00,0x00,0xff,0xfe,0x00,0x00,0x00,0x00, 16 | 0x00,0x00,0x00,0x01,0xff,0xff,0x00,0x00,0x00,0x00, 17 | 0x00,0x00,0x00,0x03,0xff,0xff,0x00,0x00,0x00,0x00, 18 | 0x00,0x00,0x00,0x03,0xff,0xff,0x00,0x00,0x00,0x00, 19 | 0x00,0x00,0x00,0x07,0xff,0xff,0x80,0x00,0x00,0x00, 20 | 0x00,0x00,0x00,0x07,0xff,0xff,0x80,0x00,0x00,0x00, 21 | 0x00,0x00,0x00,0x07,0xff,0xff,0x80,0x00,0x00,0x00, 22 | 0x00,0x00,0x00,0x0f,0xff,0xff,0x80,0x00,0x00,0x00, 23 | 0x00,0x00,0x00,0x0f,0xff,0xff,0x80,0x00,0x00,0x00, 24 | 0x7f,0xff,0xfc,0x0f,0xff,0xff,0x80,0x00,0x00,0x00, 25 | 0xff,0xff,0xff,0x0f,0xff,0xff,0x80,0x00,0x00,0x00, 26 | 0xff,0xff,0xff,0xcf,0xff,0xff,0x80,0x00,0x00,0x00, 27 | 0xff,0xff,0xff,0xef,0xff,0xff,0x80,0x00,0x00,0x00, 28 | 0x7f,0xff,0xff,0xf7,0xff,0xff,0x80,0x00,0x00,0x00, 29 | 0x3f,0xff,0xff,0xff,0xfb,0xff,0x00,0x00,0x00,0x00, 30 | 0x3f,0xff,0xff,0xff,0xf1,0xff,0x3f,0xf0,0x00,0x00, 31 | 0x1f,0xff,0xff,0xff,0xf1,0xfe,0xff,0xfe,0x00,0x00, 32 | 0x0f,0xff,0xff,0xff,0xf1,0xff,0xff,0xff,0xc0,0x00, 33 | 0x0f,0xff,0xff,0xff,0xe1,0xff,0xff,0xff,0xf8,0x00, 34 | 0x07,0xff,0xff,0xff,0xe1,0xff,0xff,0xff,0xff,0x00, 35 | 0x03,0xff,0xff,0xff,0xe1,0xff,0xff,0xff,0xff,0xc0, 36 | 0x01,0xff,0xff,0x3f,0xe1,0xff,0xff,0xff,0xff,0xe0, 37 | 0x01,0xff,0xfe,0x07,0xe3,0xff,0xff,0xff,0xff,0xe0, 38 | 0x00,0xff,0xff,0x03,0xe3,0xff,0xff,0xff,0xff,0xe0, 39 | 0x00,0x7f,0xff,0x00,0xf7,0xff,0xff,0xff,0xff,0xc0, 40 | 0x00,0x3f,0xff,0xc0,0xff,0xc0,0x7f,0xff,0xff,0x80, 41 | 0x00,0x1f,0xff,0xf0,0xff,0x00,0x3f,0xff,0xff,0x00, 42 | 0x00,0x0f,0xff,0xff,0xff,0x00,0x7f,0xff,0xfc,0x00, 43 | 0x00,0x07,0xff,0xff,0xff,0x01,0xff,0xff,0xf8,0x00, 44 | 0x00,0x01,0xff,0xff,0xff,0xff,0xff,0xff,0xf0,0x00, 45 | 0x00,0x00,0x7f,0xff,0xff,0xff,0xff,0xff,0xc0,0x00, 46 | 0x00,0x00,0x1f,0xfc,0x7f,0xff,0xff,0xff,0x80,0x00, 47 | 0x00,0x00,0x7f,0xf8,0x78,0xff,0xff,0xfe,0x00,0x00, 48 | 0x00,0x00,0xff,0xf0,0x78,0x7f,0xff,0xfc,0x00,0x00, 49 | 0x00,0x01,0xff,0xe0,0xf8,0x7f,0xff,0xf0,0x00,0x00, 50 | 0x00,0x03,0xff,0xc0,0xf8,0x3f,0xdf,0xc0,0x00,0x00, 51 | 0x00,0x07,0xff,0xc1,0xfc,0x3f,0xe0,0x00,0x00,0x00, 52 | 0x00,0x07,0xff,0x87,0xfc,0x1f,0xf0,0x00,0x00,0x00, 53 | 0x00,0x0f,0xff,0xcf,0xfe,0x1f,0xf8,0x00,0x00,0x00, 54 | 0x00,0x0f,0xff,0xff,0xff,0x1f,0xf8,0x00,0x00,0x00, 55 | 0x00,0x1f,0xff,0xff,0xff,0x1f,0xfc,0x00,0x00,0x00, 56 | 0x00,0x1f,0xff,0xff,0xff,0xff,0xfc,0x00,0x00,0x00, 57 | 0x00,0x1f,0xff,0xff,0xff,0xff,0xfe,0x00,0x00,0x00, 58 | 0x00,0x3f,0xff,0xff,0xff,0xff,0xfe,0x00,0x00,0x00, 59 | 0x00,0x3f,0xff,0xff,0xff,0xff,0xfe,0x00,0x00,0x00, 60 | 0x00,0x3f,0xff,0xff,0x3f,0xff,0xfe,0x00,0x00,0x00, 61 | 0x00,0x7f,0xff,0xff,0x3f,0xff,0xfe,0x00,0x00,0x00, 62 | 0x00,0x7f,0xff,0xff,0x3f,0xff,0xfe,0x00,0x00,0x00, 63 | 0x00,0x7f,0xff,0xfe,0x3f,0xff,0xfe,0x00,0x00,0x00, 64 | 0x00,0xff,0xff,0xfc,0x1f,0xff,0xfe,0x00,0x00,0x00, 65 | 0x00,0xff,0xff,0xf8,0x1f,0xff,0xfe,0x00,0x00,0x00, 66 | 0x00,0xff,0xff,0xe0,0x0f,0xff,0xfe,0x00,0x00,0x00, 67 | 0x01,0xff,0xff,0x80,0x07,0xff,0xfe,0x00,0x00,0x00, 68 | 0x01,0xff,0xfc,0x00,0x03,0xff,0xfe,0x00,0x00,0x00, 69 | 0x01,0xff,0xe0,0x00,0x01,0xff,0xfe,0x00,0x00,0x00, 70 | 0x01,0xff,0x00,0x00,0x00,0xff,0xfe,0x00,0x00,0x00, 71 | 0x00,0xf8,0x00,0x00,0x00,0x7f,0xfe,0x00,0x00,0x00, 72 | 0x00,0x00,0x00,0x00,0x00,0x1f,0xfe,0x00,0x00,0x00, 73 | 0x00,0x00,0x00,0x00,0x00,0x0f,0xfe,0x00,0x00,0x00, 74 | 0x00,0x00,0x00,0x00,0x00,0x07,0xfe,0x00,0x00,0x00, 75 | 0x00,0x00,0x00,0x00,0x00,0x01,0xfe,0x00,0x00,0x00, 76 | 0x00,0x00,0x00,0x00,0x00,0x00,0xfe,0x00,0x00,0x00, 77 | 0x00,0x00,0x00,0x00,0x00,0x00,0x7e,0x00,0x00,0x00, 78 | 0x00,0x00,0x00,0x00,0x00,0x00,0x1c,0x00,0x00,0x00 79 | ] 80 | -------------------------------------------------------------------------------- /gfx/adaqrcode.py: -------------------------------------------------------------------------------- 1 | width = 135 2 | height = 135 3 | data = [ 4 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 5 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 6 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 7 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 8 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 9 | 0x07,0xff,0xff,0xff,0xff,0x00,0x01,0xff,0xff,0xff,0xf8,0x01,0xff,0xff,0xff,0xff,0xc0, 10 | 0x07,0xff,0xff,0xff,0xff,0x00,0x01,0xff,0xff,0xff,0xf8,0x01,0xff,0xff,0xff,0xff,0xc0, 11 | 0x07,0xff,0xff,0xff,0xff,0x00,0x01,0xff,0xff,0xff,0xf8,0x01,0xff,0xff,0xff,0xff,0xc0, 12 | 0x07,0xff,0xff,0xff,0xff,0x00,0x01,0xff,0xff,0xff,0xf8,0x01,0xff,0xff,0xff,0xff,0xc0, 13 | 0x07,0xff,0xff,0xff,0xff,0x00,0x01,0xff,0xff,0xff,0xf8,0x01,0xff,0xff,0xff,0xff,0xc0, 14 | 0x07,0xc0,0x00,0x00,0x1f,0x00,0x3f,0xff,0x80,0x00,0xf8,0x01,0xf0,0x00,0x00,0x07,0xc0, 15 | 0x07,0xc0,0x00,0x00,0x1f,0x00,0x3f,0xff,0x80,0x00,0xf8,0x01,0xf0,0x00,0x00,0x07,0xc0, 16 | 0x07,0xc0,0x00,0x00,0x1f,0x00,0x3f,0xff,0x80,0x00,0xf8,0x01,0xf0,0x00,0x00,0x07,0xc0, 17 | 0x07,0xc0,0x00,0x00,0x1f,0x00,0x3f,0xff,0x80,0x00,0xf8,0x01,0xf0,0x00,0x00,0x07,0xc0, 18 | 0x07,0xc0,0x00,0x00,0x1f,0x00,0x3f,0xff,0x80,0x00,0xf8,0x01,0xf0,0x00,0x00,0x07,0xc0, 19 | 0x07,0xc1,0xff,0xfc,0x1f,0x00,0x3e,0x0f,0xff,0xe0,0x00,0x01,0xf0,0x7f,0xff,0x07,0xc0, 20 | 0x07,0xc1,0xff,0xfc,0x1f,0x00,0x3e,0x0f,0xff,0xe0,0x00,0x01,0xf0,0x7f,0xff,0x07,0xc0, 21 | 0x07,0xc1,0xff,0xfc,0x1f,0x00,0x3e,0x0f,0xff,0xe0,0x00,0x01,0xf0,0x7f,0xff,0x07,0xc0, 22 | 0x07,0xc1,0xff,0xfc,0x1f,0x00,0x3e,0x0f,0xff,0xe0,0x00,0x01,0xf0,0x7f,0xff,0x07,0xc0, 23 | 0x07,0xc1,0xff,0xfc,0x1f,0x00,0x3e,0x0f,0xff,0xe0,0x00,0x01,0xf0,0x7f,0xff,0x07,0xc0, 24 | 0x07,0xc1,0xff,0xfc,0x1f,0x07,0xff,0xf0,0x7c,0x00,0xf8,0x01,0xf0,0x7f,0xff,0x07,0xc0, 25 | 0x07,0xc1,0xff,0xfc,0x1f,0x07,0xff,0xf0,0x7c,0x00,0xf8,0x01,0xf0,0x7f,0xff,0x07,0xc0, 26 | 0x07,0xc1,0xff,0xfc,0x1f,0x07,0xff,0xf0,0x7c,0x00,0xf8,0x01,0xf0,0x7f,0xff,0x07,0xc0, 27 | 0x07,0xc1,0xff,0xfc,0x1f,0x07,0xff,0xf0,0x7c,0x00,0xf8,0x01,0xf0,0x7f,0xff,0x07,0xc0, 28 | 0x07,0xc1,0xff,0xfc,0x1f,0x07,0xff,0xf0,0x7c,0x00,0xf8,0x01,0xf0,0x7f,0xff,0x07,0xc0, 29 | 0x07,0xc1,0xff,0xfc,0x1f,0x00,0x3f,0xf0,0x7c,0x00,0xff,0xc1,0xf0,0x7f,0xff,0x07,0xc0, 30 | 0x07,0xc1,0xff,0xfc,0x1f,0x00,0x3f,0xf0,0x7c,0x00,0xff,0xc1,0xf0,0x7f,0xff,0x07,0xc0, 31 | 0x07,0xc1,0xff,0xfc,0x1f,0x00,0x3f,0xf0,0x7c,0x00,0xff,0xc1,0xf0,0x7f,0xff,0x07,0xc0, 32 | 0x07,0xc1,0xff,0xfc,0x1f,0x00,0x3f,0xf0,0x7c,0x00,0xff,0xc1,0xf0,0x7f,0xff,0x07,0xc0, 33 | 0x07,0xc1,0xff,0xfc,0x1f,0x00,0x3f,0xf0,0x7c,0x00,0xff,0xc1,0xf0,0x7f,0xff,0x07,0xc0, 34 | 0x07,0xc0,0x00,0x00,0x1f,0x07,0xff,0xf0,0x7f,0xff,0x00,0x01,0xf0,0x00,0x00,0x07,0xc0, 35 | 0x07,0xc0,0x00,0x00,0x1f,0x07,0xff,0xf0,0x7f,0xff,0x00,0x01,0xf0,0x00,0x00,0x07,0xc0, 36 | 0x07,0xc0,0x00,0x00,0x1f,0x07,0xff,0xf0,0x7f,0xff,0x00,0x01,0xf0,0x00,0x00,0x07,0xc0, 37 | 0x07,0xc0,0x00,0x00,0x1f,0x07,0xff,0xf0,0x7f,0xff,0x00,0x01,0xf0,0x00,0x00,0x07,0xc0, 38 | 0x07,0xc0,0x00,0x00,0x1f,0x07,0xff,0xf0,0x7f,0xff,0x00,0x01,0xf0,0x00,0x00,0x07,0xc0, 39 | 0x07,0xff,0xff,0xff,0xff,0x07,0xc1,0xf0,0x7c,0x1f,0x07,0xc1,0xff,0xff,0xff,0xff,0xc0, 40 | 0x07,0xff,0xff,0xff,0xff,0x07,0xc1,0xf0,0x7c,0x1f,0x07,0xc1,0xff,0xff,0xff,0xff,0xc0, 41 | 0x07,0xff,0xff,0xff,0xff,0x07,0xc1,0xf0,0x7c,0x1f,0x07,0xc1,0xff,0xff,0xff,0xff,0xc0, 42 | 0x07,0xff,0xff,0xff,0xff,0x07,0xc1,0xf0,0x7c,0x1f,0x07,0xc1,0xff,0xff,0xff,0xff,0xc0, 43 | 0x07,0xff,0xff,0xff,0xff,0x07,0xc1,0xf0,0x7c,0x1f,0x07,0xc1,0xff,0xff,0xff,0xff,0xc0, 44 | 0x00,0x00,0x00,0x00,0x00,0x07,0xc1,0xf0,0x03,0xe0,0x07,0xc0,0x00,0x00,0x00,0x00,0x00, 45 | 0x00,0x00,0x00,0x00,0x00,0x07,0xc1,0xf0,0x03,0xe0,0x07,0xc0,0x00,0x00,0x00,0x00,0x00, 46 | 0x00,0x00,0x00,0x00,0x00,0x07,0xc1,0xf0,0x03,0xe0,0x07,0xc0,0x00,0x00,0x00,0x00,0x00, 47 | 0x00,0x00,0x00,0x00,0x00,0x07,0xc1,0xf0,0x03,0xe0,0x07,0xc0,0x00,0x00,0x00,0x00,0x00, 48 | 0x00,0x00,0x00,0x00,0x00,0x07,0xc1,0xf0,0x03,0xe0,0x07,0xc0,0x00,0x00,0x00,0x00,0x00, 49 | 0x00,0x3f,0xf0,0x00,0x1f,0x00,0x01,0xf0,0x03,0xe0,0xf8,0x01,0xff,0x83,0xe0,0x00,0x00, 50 | 0x00,0x3f,0xf0,0x00,0x1f,0x00,0x01,0xf0,0x03,0xe0,0xf8,0x01,0xff,0x83,0xe0,0x00,0x00, 51 | 0x00,0x3f,0xf0,0x00,0x1f,0x00,0x01,0xf0,0x03,0xe0,0xf8,0x01,0xff,0x83,0xe0,0x00,0x00, 52 | 0x00,0x3f,0xf0,0x00,0x1f,0x00,0x01,0xf0,0x03,0xe0,0xf8,0x01,0xff,0x83,0xe0,0x00,0x00, 53 | 0x00,0x3f,0xf0,0x00,0x1f,0x00,0x01,0xf0,0x03,0xe0,0xf8,0x01,0xff,0x83,0xe0,0x00,0x00, 54 | 0x07,0xc1,0xf0,0x00,0x00,0xf8,0x3e,0x00,0x7c,0x00,0xff,0xc1,0xff,0x83,0xe0,0xff,0xc0, 55 | 0x07,0xc1,0xf0,0x00,0x00,0xf8,0x3e,0x00,0x7c,0x00,0xff,0xc1,0xff,0x83,0xe0,0xff,0xc0, 56 | 0x07,0xc1,0xf0,0x00,0x00,0xf8,0x3e,0x00,0x7c,0x00,0xff,0xc1,0xff,0x83,0xe0,0xff,0xc0, 57 | 0x07,0xc1,0xf0,0x00,0x00,0xf8,0x3e,0x00,0x7c,0x00,0xff,0xc1,0xff,0x83,0xe0,0xff,0xc0, 58 | 0x07,0xc1,0xf0,0x00,0x00,0xf8,0x3e,0x00,0x7c,0x00,0xff,0xc1,0xff,0x83,0xe0,0xff,0xc0, 59 | 0x07,0xc1,0xf0,0x03,0xff,0x07,0xc1,0xf0,0x7f,0xff,0xf8,0x3f,0xff,0xff,0xff,0x07,0xc0, 60 | 0x07,0xc1,0xf0,0x03,0xff,0x07,0xc1,0xf0,0x7f,0xff,0xf8,0x3f,0xff,0xff,0xff,0x07,0xc0, 61 | 0x07,0xc1,0xf0,0x03,0xff,0x07,0xc1,0xf0,0x7f,0xff,0xf8,0x3f,0xff,0xff,0xff,0x07,0xc0, 62 | 0x07,0xc1,0xf0,0x03,0xff,0x07,0xc1,0xf0,0x7f,0xff,0xf8,0x3f,0xff,0xff,0xff,0x07,0xc0, 63 | 0x07,0xc1,0xf0,0x03,0xff,0x07,0xc1,0xf0,0x7f,0xff,0xf8,0x3f,0xff,0xff,0xff,0x07,0xc0, 64 | 0x00,0x3e,0x0f,0xfc,0x00,0xf8,0x3e,0x00,0x7c,0x1f,0x00,0x3e,0x0f,0x83,0xe0,0x00,0x00, 65 | 0x00,0x3e,0x0f,0xfc,0x00,0xf8,0x3e,0x00,0x7c,0x1f,0x00,0x3e,0x0f,0x83,0xe0,0x00,0x00, 66 | 0x00,0x3e,0x0f,0xfc,0x00,0xf8,0x3e,0x00,0x7c,0x1f,0x00,0x3e,0x0f,0x83,0xe0,0x00,0x00, 67 | 0x00,0x3e,0x0f,0xfc,0x00,0xf8,0x3e,0x00,0x7c,0x1f,0x00,0x3e,0x0f,0x83,0xe0,0x00,0x00, 68 | 0x00,0x3e,0x0f,0xfc,0x00,0xf8,0x3e,0x00,0x7c,0x1f,0x00,0x3e,0x0f,0x83,0xe0,0x00,0x00, 69 | 0x00,0x3f,0xff,0xff,0xff,0xff,0xfe,0x0f,0x83,0xff,0x07,0xc1,0xff,0x80,0x00,0x07,0xc0, 70 | 0x00,0x3f,0xff,0xff,0xff,0xff,0xfe,0x0f,0x83,0xff,0x07,0xc1,0xff,0x80,0x00,0x07,0xc0, 71 | 0x00,0x3f,0xff,0xff,0xff,0xff,0xfe,0x0f,0x83,0xff,0x07,0xc1,0xff,0x80,0x00,0x07,0xc0, 72 | 0x00,0x3f,0xff,0xff,0xff,0xff,0xfe,0x0f,0x83,0xff,0x07,0xc1,0xff,0x80,0x00,0x07,0xc0, 73 | 0x00,0x3f,0xff,0xff,0xff,0xff,0xfe,0x0f,0x83,0xff,0x07,0xc1,0xff,0x80,0x00,0x07,0xc0, 74 | 0x00,0x3e,0x00,0x03,0xe0,0x07,0xfe,0x00,0x00,0x1f,0xff,0xc1,0xff,0x80,0x00,0xff,0xc0, 75 | 0x00,0x3e,0x00,0x03,0xe0,0x07,0xfe,0x00,0x00,0x1f,0xff,0xc1,0xff,0x80,0x00,0xff,0xc0, 76 | 0x00,0x3e,0x00,0x03,0xe0,0x07,0xfe,0x00,0x00,0x1f,0xff,0xc1,0xff,0x80,0x00,0xff,0xc0, 77 | 0x00,0x3e,0x00,0x03,0xe0,0x07,0xfe,0x00,0x00,0x1f,0xff,0xc1,0xff,0x80,0x00,0xff,0xc0, 78 | 0x00,0x3e,0x00,0x03,0xe0,0x07,0xfe,0x00,0x00,0x1f,0xff,0xc1,0xff,0x80,0x00,0xff,0xc0, 79 | 0x07,0xfe,0x00,0x7c,0x1f,0xf8,0x3e,0x0f,0x83,0xff,0xff,0xc1,0xf0,0x7f,0xff,0x07,0xc0, 80 | 0x07,0xfe,0x00,0x7c,0x1f,0xf8,0x3e,0x0f,0x83,0xff,0xff,0xc1,0xf0,0x7f,0xff,0x07,0xc0, 81 | 0x07,0xfe,0x00,0x7c,0x1f,0xf8,0x3e,0x0f,0x83,0xff,0xff,0xc1,0xf0,0x7f,0xff,0x07,0xc0, 82 | 0x07,0xfe,0x00,0x7c,0x1f,0xf8,0x3e,0x0f,0x83,0xff,0xff,0xc1,0xf0,0x7f,0xff,0x07,0xc0, 83 | 0x07,0xfe,0x00,0x7c,0x1f,0xf8,0x3e,0x0f,0x83,0xff,0xff,0xc1,0xf0,0x7f,0xff,0x07,0xc0, 84 | 0x00,0x00,0x00,0x7f,0xe0,0x00,0x3e,0x00,0x7f,0xe0,0xff,0xc1,0xf0,0x00,0x00,0x00,0x00, 85 | 0x00,0x00,0x00,0x7f,0xe0,0x00,0x3e,0x00,0x7f,0xe0,0xff,0xc1,0xf0,0x00,0x00,0x00,0x00, 86 | 0x00,0x00,0x00,0x7f,0xe0,0x00,0x3e,0x00,0x7f,0xe0,0xff,0xc1,0xf0,0x00,0x00,0x00,0x00, 87 | 0x00,0x00,0x00,0x7f,0xe0,0x00,0x3e,0x00,0x7f,0xe0,0xff,0xc1,0xf0,0x00,0x00,0x00,0x00, 88 | 0x00,0x00,0x00,0x7f,0xe0,0x00,0x3e,0x00,0x7f,0xe0,0xff,0xc1,0xf0,0x00,0x00,0x00,0x00, 89 | 0x07,0xfe,0x0f,0x80,0x1f,0xff,0xff,0xf0,0x03,0xff,0x07,0xff,0xff,0xfc,0x00,0xf8,0x00, 90 | 0x07,0xfe,0x0f,0x80,0x1f,0xff,0xff,0xf0,0x03,0xff,0x07,0xff,0xff,0xfc,0x00,0xf8,0x00, 91 | 0x07,0xfe,0x0f,0x80,0x1f,0xff,0xff,0xf0,0x03,0xff,0x07,0xff,0xff,0xfc,0x00,0xf8,0x00, 92 | 0x07,0xfe,0x0f,0x80,0x1f,0xff,0xff,0xf0,0x03,0xff,0x07,0xff,0xff,0xfc,0x00,0xf8,0x00, 93 | 0x07,0xfe,0x0f,0x80,0x1f,0xff,0xff,0xf0,0x03,0xff,0x07,0xff,0xff,0xfc,0x00,0xf8,0x00, 94 | 0x00,0x00,0x00,0x00,0x00,0x07,0xfe,0x0f,0x83,0xff,0x07,0xc0,0x00,0x7c,0x1f,0x07,0xc0, 95 | 0x00,0x00,0x00,0x00,0x00,0x07,0xfe,0x0f,0x83,0xff,0x07,0xc0,0x00,0x7c,0x1f,0x07,0xc0, 96 | 0x00,0x00,0x00,0x00,0x00,0x07,0xfe,0x0f,0x83,0xff,0x07,0xc0,0x00,0x7c,0x1f,0x07,0xc0, 97 | 0x00,0x00,0x00,0x00,0x00,0x07,0xfe,0x0f,0x83,0xff,0x07,0xc0,0x00,0x7c,0x1f,0x07,0xc0, 98 | 0x00,0x00,0x00,0x00,0x00,0x07,0xfe,0x0f,0x83,0xff,0x07,0xc0,0x00,0x7c,0x1f,0x07,0xc0, 99 | 0x07,0xff,0xff,0xff,0xff,0x00,0x3e,0x00,0x7f,0xe0,0x07,0xc1,0xf0,0x7f,0xe0,0x07,0xc0, 100 | 0x07,0xff,0xff,0xff,0xff,0x00,0x3e,0x00,0x7f,0xe0,0x07,0xc1,0xf0,0x7f,0xe0,0x07,0xc0, 101 | 0x07,0xff,0xff,0xff,0xff,0x00,0x3e,0x00,0x7f,0xe0,0x07,0xc1,0xf0,0x7f,0xe0,0x07,0xc0, 102 | 0x07,0xff,0xff,0xff,0xff,0x00,0x3e,0x00,0x7f,0xe0,0x07,0xc1,0xf0,0x7f,0xe0,0x07,0xc0, 103 | 0x07,0xff,0xff,0xff,0xff,0x00,0x3e,0x00,0x7f,0xe0,0x07,0xc1,0xf0,0x7f,0xe0,0x07,0xc0, 104 | 0x07,0xc0,0x00,0x00,0x1f,0x00,0x3f,0xff,0x80,0x00,0x07,0xc0,0x00,0x7c,0x00,0x00,0x00, 105 | 0x07,0xc0,0x00,0x00,0x1f,0x00,0x3f,0xff,0x80,0x00,0x07,0xc0,0x00,0x7c,0x00,0x00,0x00, 106 | 0x07,0xc0,0x00,0x00,0x1f,0x00,0x3f,0xff,0x80,0x00,0x07,0xc0,0x00,0x7c,0x00,0x00,0x00, 107 | 0x07,0xc0,0x00,0x00,0x1f,0x00,0x3f,0xff,0x80,0x00,0x07,0xc0,0x00,0x7c,0x00,0x00,0x00, 108 | 0x07,0xc0,0x00,0x00,0x1f,0x00,0x3f,0xff,0x80,0x00,0x07,0xc0,0x00,0x7c,0x00,0x00,0x00, 109 | 0x07,0xc1,0xff,0xfc,0x1f,0x00,0x3f,0xf0,0x03,0xff,0xff,0xff,0xff,0xff,0xe0,0xff,0xc0, 110 | 0x07,0xc1,0xff,0xfc,0x1f,0x00,0x3f,0xf0,0x03,0xff,0xff,0xff,0xff,0xff,0xe0,0xff,0xc0, 111 | 0x07,0xc1,0xff,0xfc,0x1f,0x00,0x3f,0xf0,0x03,0xff,0xff,0xff,0xff,0xff,0xe0,0xff,0xc0, 112 | 0x07,0xc1,0xff,0xfc,0x1f,0x00,0x3f,0xf0,0x03,0xff,0xff,0xff,0xff,0xff,0xe0,0xff,0xc0, 113 | 0x07,0xc1,0xff,0xfc,0x1f,0x00,0x3f,0xf0,0x03,0xff,0xff,0xff,0xff,0xff,0xe0,0xff,0xc0, 114 | 0x07,0xc1,0xff,0xfc,0x1f,0x00,0x00,0x0f,0x80,0x00,0xf8,0x3e,0x00,0x7f,0xff,0xf8,0x00, 115 | 0x07,0xc1,0xff,0xfc,0x1f,0x00,0x00,0x0f,0x80,0x00,0xf8,0x3e,0x00,0x7f,0xff,0xf8,0x00, 116 | 0x07,0xc1,0xff,0xfc,0x1f,0x00,0x00,0x0f,0x80,0x00,0xf8,0x3e,0x00,0x7f,0xff,0xf8,0x00, 117 | 0x07,0xc1,0xff,0xfc,0x1f,0x00,0x00,0x0f,0x80,0x00,0xf8,0x3e,0x00,0x7f,0xff,0xf8,0x00, 118 | 0x07,0xc1,0xff,0xfc,0x1f,0x00,0x00,0x0f,0x80,0x00,0xf8,0x3e,0x00,0x7f,0xff,0xf8,0x00, 119 | 0x07,0xc1,0xff,0xfc,0x1f,0x07,0xc0,0x0f,0xfc,0x1f,0x07,0xc0,0x00,0x7c,0x00,0xff,0xc0, 120 | 0x07,0xc1,0xff,0xfc,0x1f,0x07,0xc0,0x0f,0xfc,0x1f,0x07,0xc0,0x00,0x7c,0x00,0xff,0xc0, 121 | 0x07,0xc1,0xff,0xfc,0x1f,0x07,0xc0,0x0f,0xfc,0x1f,0x07,0xc0,0x00,0x7c,0x00,0xff,0xc0, 122 | 0x07,0xc1,0xff,0xfc,0x1f,0x07,0xc0,0x0f,0xfc,0x1f,0x07,0xc0,0x00,0x7c,0x00,0xff,0xc0, 123 | 0x07,0xc1,0xff,0xfc,0x1f,0x07,0xc0,0x0f,0xfc,0x1f,0x07,0xc0,0x00,0x7c,0x00,0xff,0xc0, 124 | 0x07,0xc0,0x00,0x00,0x1f,0x07,0xff,0xf0,0x7c,0x1f,0xff,0xc0,0x0f,0xfc,0x00,0x00,0x00, 125 | 0x07,0xc0,0x00,0x00,0x1f,0x07,0xff,0xf0,0x7c,0x1f,0xff,0xc0,0x0f,0xfc,0x00,0x00,0x00, 126 | 0x07,0xc0,0x00,0x00,0x1f,0x07,0xff,0xf0,0x7c,0x1f,0xff,0xc0,0x0f,0xfc,0x00,0x00,0x00, 127 | 0x07,0xc0,0x00,0x00,0x1f,0x07,0xff,0xf0,0x7c,0x1f,0xff,0xc0,0x0f,0xfc,0x00,0x00,0x00, 128 | 0x07,0xc0,0x00,0x00,0x1f,0x07,0xff,0xf0,0x7c,0x1f,0xff,0xc0,0x0f,0xfc,0x00,0x00,0x00, 129 | 0x07,0xff,0xff,0xff,0xff,0x00,0x00,0x0f,0xff,0xff,0x07,0xfe,0x00,0x03,0xe0,0x07,0xc0, 130 | 0x07,0xff,0xff,0xff,0xff,0x00,0x00,0x0f,0xff,0xff,0x07,0xfe,0x00,0x03,0xe0,0x07,0xc0, 131 | 0x07,0xff,0xff,0xff,0xff,0x00,0x00,0x0f,0xff,0xff,0x07,0xfe,0x00,0x03,0xe0,0x07,0xc0, 132 | 0x07,0xff,0xff,0xff,0xff,0x00,0x00,0x0f,0xff,0xff,0x07,0xfe,0x00,0x03,0xe0,0x07,0xc0, 133 | 0x07,0xff,0xff,0xff,0xff,0x00,0x00,0x0f,0xff,0xff,0x07,0xfe,0x00,0x03,0xe0,0x07,0xc0, 134 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 135 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 136 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 137 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 138 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 139 | ] 140 | -------------------------------------------------------------------------------- /gfx/aykm.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayoy/micropython-thermal-printer/59efa0554e416672e69e10a469a99ce5d148aed3/gfx/aykm.bmp -------------------------------------------------------------------------------- /gfx/notbad.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayoy/micropython-thermal-printer/59efa0554e416672e69e10a469a99ce5d148aed3/gfx/notbad.bmp -------------------------------------------------------------------------------- /gfx/qrcode: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ayoy/micropython-thermal-printer/59efa0554e416672e69e10a469a99ce5d148aed3/gfx/qrcode -------------------------------------------------------------------------------- /printertest.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | from Adafruit_Thermal import * 4 | from machine import Pin 5 | 6 | # Feel free to experiment with heatdots, heattime and heatiniterval 7 | # to nail down your printer's sweet spot :) 8 | printer = Adafruit_Thermal(pins=(Pin.exp_board.G14,), heatdots=5, heatinterval=40) 9 | 10 | # Test inverse on & off 11 | printer.inverseOn() 12 | printer.println("Inverse ON") 13 | printer.inverseOff() 14 | 15 | # Test character double-height on & off 16 | printer.doubleHeightOn() 17 | printer.println("Double Height ON") 18 | printer.doubleHeightOff() 19 | 20 | # Set justification (right, center, left) -- accepts 'L', 'C', 'R' 21 | printer.justify('R') 22 | printer.println("Right justified") 23 | printer.justify('C') 24 | printer.println("Center justified") 25 | printer.justify('L') 26 | printer.println("Left justified") 27 | 28 | # Test more styles 29 | printer.boldOn() 30 | printer.println("Bold text") 31 | printer.boldOff() 32 | 33 | printer.underlineOn() 34 | printer.println("Underlined text") 35 | printer.underlineOff() 36 | 37 | printer.setSize('L') # Set type size, accepts 'S', 'M', 'L' 38 | printer.println("Large") 39 | printer.setSize('M') 40 | printer.println("Medium") 41 | printer.setSize('S') 42 | printer.println("Small") 43 | 44 | printer.justify('C') 45 | printer.println("normal\nline\nspacing") 46 | printer.setLineHeight(50) 47 | printer.println("Taller\nline\nspacing") 48 | printer.setLineHeight() # Reset to default 49 | printer.justify('L') 50 | 51 | # Change printer settings for smoother barcodes 52 | printer = Adafruit_Thermal(pins=(Pin.exp_board.G14,), heatdots=4, heattime=120, heatinterval=80) 53 | 54 | # Barcode examples 55 | printer.feed(1) 56 | # CODE39 is the most common alphanumeric barcode 57 | printer.printBarcode("ADAFRUT", printer.CODE39) 58 | printer.setBarcodeHeight(100) 59 | # Print UPC line on product barcodes 60 | printer.printBarcode("123456789123", printer.UPC_A) 61 | time.sleep(2) 62 | 63 | # These settings allow for much cleaner output when printing images 64 | printer = Adafruit_Thermal(pins=(Pin.exp_board.G14,), heatdots=1, heattime=155, heatinterval=1) 65 | 66 | # # Print the 75x75 pixel logo in adalogo.py 67 | import adalogo 68 | printer.printBitmap(adalogo.width, adalogo.height, adalogo.data, LaaT=True) 69 | printer.println("Adafruit!") 70 | 71 | try: 72 | # # Print the 135x135 pixel QR code stored in the file on disk 73 | printer.printBitmapFromFile(135, 135, '/flash/gfx/qrcode') 74 | 75 | # TODO: figure out what's wrong here 76 | time.sleep(2) 77 | 78 | # # Print some .bmp bitmap images 79 | printer.printBMPImage('/flash/gfx/aykm.bmp') 80 | printer.printBMPImage('/flash/gfx/notbad.bmp') 81 | except OSError as e: 82 | print(e.errno) 83 | pass 84 | 85 | printer.feed(3) 86 | 87 | # printer.sleep() # Tell printer to sleep 88 | # printer.wake() # Call wake() before printing again, even if reset 89 | # printer.setDefault() # Restore printer to defaults 90 | --------------------------------------------------------------------------------