From 027f158ddfdcaa9395bb5704dcd6a12436a1a302 Mon Sep 17 00:00:00 2001 From: Phillip Burgess Date: Tue, 14 Jan 2014 11:30:48 -0800 Subject: [PATCH 01/25] Fix repeated tweet printing --- twitter.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/twitter.py b/twitter.py index 4b579d7..662b1fd 100755 --- a/twitter.py +++ b/twitter.py @@ -62,7 +62,7 @@ def issueRequestAndDecodeResponse(method, url, body, headers): connection.request(method, url, body, headers) response = connection.getresponse() if response.status != 200: - # This is OK for command-line testing, otherwise + # This is OK for command-line testing, otherwise # keep it commented out when using main.py # print('HTTP error: %d' % response.status) exit(-1) @@ -101,6 +101,8 @@ def issueRequestAndDecodeResponse(method, url, body, headers): # Display results. --------------------------------------------------------- +maxId = data['search_metadata']['max_id_str'] + for tweet in data['statuses']: printer.inverseOn() @@ -111,6 +113,10 @@ def issueRequestAndDecodeResponse(method, url, body, headers): printer.print('{:<32}'.format(tweet['created_at'])) printer.underlineOff() + # max_id_str is not always present, so check tweet IDs as fallback + id = tweet['id_str'] + if(id > maxId): maxId = id # String compare is OK for this + # Remove HTML escape sequences # and remap Unicode values to nearest ASCII equivalents printer.print(unidecode( @@ -118,4 +124,4 @@ def issueRequestAndDecodeResponse(method, url, body, headers): printer.feed(3) -print(data['search_metadata']['max_id_str']) # Piped back to calling process +print(maxId) # Piped back to calling process From 8f11cfbf0abfb2c7271307fbcdd4bacf146e09b0 Mon Sep 17 00:00:00 2001 From: jmzjmzjmz Date: Wed, 8 Feb 2017 14:40:42 -0500 Subject: [PATCH 02/25] updated to darksky.net weather api --- forecast.py | 57 ++++++++++++++++----------------- timetemp.py | 90 +++++++++++++++++++++++++++-------------------------- 2 files changed, 75 insertions(+), 72 deletions(-) diff --git a/forecast.py b/forecast.py index d985497..b73dad4 100755 --- a/forecast.py +++ b/forecast.py @@ -1,7 +1,7 @@ #!/usr/bin/python # Weather forecast for Raspberry Pi w/Adafruit Mini Thermal Printer. -# Retrieves data from Yahoo! weather, prints current conditions and +# Retrieves data from DarkSky.net's API, prints current conditions and # forecasts for next two days. See timetemp.py for a different # weather example using nice bitmaps. # Written by Adafruit Industries. MIT license. @@ -14,51 +14,52 @@ # http://www.adafruit.com/products/600 Printer starter pack from __future__ import print_function -import urllib, time from Adafruit_Thermal import * -from xml.dom.minidom import parseString +from datetime import date +from datetime import datetime +import calendar +import urllib, json -# WOEID indicates the geographic location for the forecast. It is -# not a ZIP code or other common indicator. Instead, it can be found -# by 'manually' visiting http://weather.yahoo.com, entering a location -# and requesting a forecast, then copy the number from the end of the -# current URL string and paste it here. -WOEID = '2459115' +# API_KEY = "YOUR_API_KEY" + +LAT = "40.726019" +LONG = "-74.00536" # Dumps one forecast line to the printer def forecast(idx): - tag = 'yweather:forecast' - day = dom.getElementsByTagName(tag)[idx].getAttribute('day') - lo = dom.getElementsByTagName(tag)[idx].getAttribute('low') - hi = dom.getElementsByTagName(tag)[idx].getAttribute('high') - cond = dom.getElementsByTagName(tag)[idx].getAttribute('text') - printer.print(day + ': low ' + lo ) - printer.print(deg) - printer.print(' high ' + hi) - printer.print(deg) - printer.println(' ' + cond) + + date = datetime.fromtimestamp(int(data['daily']['data'][idx]['time'])) + + day = calendar.day_name[date.weekday()] + lo = data['daily']['data'][idx]['temperatureMin'] + hi = data['daily']['data'][idx]['temperatureMax'] + cond = data['daily']['data'][idx]['summary'] + printer.print(day + ': low ' + str(lo) ) + printer.print(deg) + printer.print(' high ' + str(hi)) + printer.print(deg) + printer.println(' ' + cond.replace(u'\u2013', '-').encode('utf-8')) # take care of pesky unicode dash "–" or \u2013 printer = Adafruit_Thermal("/dev/ttyAMA0", 19200, timeout=5) deg = chr(0xf8) # Degree symbol on thermal printer -# Fetch forecast data from Yahoo!, parse resulting XML -dom = parseString(urllib.urlopen( - 'http://weather.yahooapis.com/forecastrss?w=' + WOEID).read()) +url = "https://api.darksky.net/forecast/"+API_KEY+"/"+LAT+","+LONG+"?exclude=[alerts,minutely,hourly,flags]&units=us" +response = urllib.urlopen(url) +data = json.loads(response.read()) # Print heading printer.inverseOn() -printer.print('{:^32}'.format( - dom.getElementsByTagName('description')[0].firstChild.data)) +printer.print('{:^32}'.format("DarkSky.Net Forecast")) printer.inverseOff() # Print current conditions printer.boldOn() printer.print('{:^32}'.format('Current conditions:')) printer.boldOff() -printer.print('{:^32}'.format( - dom.getElementsByTagName('pubDate')[0].firstChild.data)) -temp = dom.getElementsByTagName('yweather:condition')[0].getAttribute('temp') -cond = dom.getElementsByTagName('yweather:condition')[0].getAttribute('text') + + +temp = data['currently']['temperature'] +cond = data['currently']['summary'] printer.print(temp) printer.print(deg) printer.println(' ' + cond) diff --git a/timetemp.py b/timetemp.py index 2835782..ea3d55c 100755 --- a/timetemp.py +++ b/timetemp.py @@ -1,7 +1,7 @@ #!/usr/bin/python # Current time and temperature display for Raspberry Pi w/Adafruit Mini -# Thermal Printer. Retrieves data from Yahoo! weather, prints current +# Thermal Printer. Retrieves data from DarkSky.net's API, prints current # conditions and time using large, friendly graphics. # See forecast.py for a different weather example that's all text-based. # Written by Adafruit Industries. MIT license. @@ -15,31 +15,31 @@ from __future__ import print_function from Adafruit_Thermal import * -from xml.dom.minidom import parseString -import Image, ImageDraw, time, urllib +import Image, ImageDraw, time, urllib, json -# WOEID indicates the geographic location for the forecast. It is -# not a ZIP code or other common indicator. Instead, it can be found -# by 'manually' visiting http://weather.yahoo.com, entering a location -# and requesting a forecast, then copy the number from the end of the -# current URL string and paste it here. -WOEID = '2459115' +# API_KEY = "YOUR_API_KEY" -# Fetch weather data from Yahoo!, parse resulting XML -dom = parseString(urllib.urlopen( - 'http://weather.yahooapis.com/forecastrss?w=' + WOEID).read()) +LAT = "40.726019" +LONG = "-74.00536" + +# Fetch weather data from DarkSky, parse resulting JSON +url = "https://api.darksky.net/forecast/"+API_KEY+"/"+LAT+","+LONG+"?exclude=[alerts,minutely,hourly,flags]&units=us" +response = urllib.urlopen(url) +data = json.loads(response.read()) # Extract values relating to current temperature, humidity, wind -temperature = int(dom.getElementsByTagName( - 'yweather:condition')[0].getAttribute('temp')) -humidity = int(dom.getElementsByTagName( - 'yweather:atmosphere')[0].getAttribute('humidity')) -windSpeed = int(dom.getElementsByTagName( - 'yweather:wind')[0].getAttribute('speed')) -windDir = int(dom.getElementsByTagName( - 'yweather:wind')[0].getAttribute('direction')) -windUnits = dom.getElementsByTagName( - 'yweather:units')[0].getAttribute('speed') + +temperature = int(data['currently']['temperature']) +humidity = int(data['currently']['humidity'] * 100); +windSpeed = int(data['currently']['windSpeed']) +windDir = str(data['currently']['windBearing']) +windUnits = "mph" + +# print(temperature) +# print(humidity) +# print(windSpeed) +# print(windDir) +# print(windUnits) # Although the Python Imaging Library does have nice font support, # I opted here to use a raster bitmap for all of the glyphs instead. @@ -62,11 +62,11 @@ # Generate a list of sub-image glyphs cropped from the symbols image def croplist(widths, x, y, height): - list = [] - for i in range(len(widths)): - list.append(symbols.crop( - [x, y+i*height, x+widths[i], y+(i+1)*height])) - return list + list = [] + for i in range(len(widths)): + list.append(symbols.crop( + [x, y+i*height, x+widths[i], y+(i+1)*height])) + return list # Crop glyph lists (digits, days of week, etc.) TimeDigit = croplist(TimeDigitWidth, 0, 0, 44) @@ -91,20 +91,20 @@ def croplist(widths, x, y, height): # Paste a series of glyphs (mostly numbers) from string to img def drawNums(str, x, y, list): - for i in range(len(str)): - d = ord(str[i]) - ord('0') - img.paste(list[d], (x, y)) - x += list[d].size[0] + 1 - return x + for i in range(len(str)): + d = ord(str[i]) - ord('0') + img.paste(list[d], (x, y)) + x += list[d].size[0] + 1 + return x # Determine total width of a series of glyphs in string def numWidth(str, list): - w = 0 # Cumulative width - for i in range(len(str)): - d = ord(str[i]) - ord('0') - if i > 0: w += 1 # Space between digits - w += list[d].size[0] # Digit width - return w + w = 0 # Cumulative width + for i in range(len(str)): + d = ord(str[i]) - ord('0') + if i > 0: w += 1 # Space between digits + w += list[d].size[0] # Digit width + return w # Render current time (always 24 hour XX:XX format) t = time.localtime() @@ -134,12 +134,13 @@ def numWidth(str, list): s2 = str(windSpeed) winDirNum = 0 # Wind direction glyph number if windSpeed > 0: - for winDirNum in range(len(DirAngle) - 1): - if windDir < DirAngle[winDirNum]: break + for winDirNum in range(len(DirAngle) - 1): + if windDir < DirAngle[winDirNum]: break +winDirNum+=1 w = Humidity.size[0] + 5 + numWidth(s, HumiDigit) w2 = Wind.size[0] + 5 + numWidth(s2, HumiDigit) if windSpeed > 0: - w2 += 3 + Dir[winDirNum].size[0] + w2 += 3 + Dir[winDirNum].size[0] if windUnits == 'kph': w2 += 3 + Kph.size[0] else: w2 += 3 + Mph.size[0] if w2 > w: w = w2 @@ -154,9 +155,10 @@ def numWidth(str, list): y += 23 # And advance to next line img.paste(Wind, (x, y)) x += Wind.size[0] + 5 + if windSpeed > 0: - img.paste(Dir[winDirNum], (x, y)) - x += Dir[winDirNum].size[0] + 3 + img.paste(Dir[winDirNum], (x, y)) + x += Dir[winDirNum].size[0] + 3 x = drawNums(s2, x, y, HumiDigit) + 3 if windUnits == 'kph': img.paste(Kph, (x, y)) else: img.paste(Mph, (x, y)) @@ -164,4 +166,4 @@ def numWidth(str, list): # Open connection to printer and print image printer = Adafruit_Thermal("/dev/ttyAMA0", 19200, timeout=5) printer.printImage(img, True) -printer.feed(3) +printer.feed(3) \ No newline at end of file From b9bd00f564da66a6c9cbdec9bf118bf2dfad5f2e Mon Sep 17 00:00:00 2001 From: jmzjmzjmz Date: Wed, 8 Feb 2017 14:42:31 -0500 Subject: [PATCH 03/25] fixed data type --- timetemp.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/timetemp.py b/timetemp.py index ea3d55c..ce25e54 100755 --- a/timetemp.py +++ b/timetemp.py @@ -32,7 +32,7 @@ temperature = int(data['currently']['temperature']) humidity = int(data['currently']['humidity'] * 100); windSpeed = int(data['currently']['windSpeed']) -windDir = str(data['currently']['windBearing']) +windDir = data['currently']['windBearing'] windUnits = "mph" # print(temperature) From 84075c2b8c642ea1f9ff0fdd5b0cded7a2cf4ab2 Mon Sep 17 00:00:00 2001 From: jmzjmzjmz Date: Mon, 20 Feb 2017 13:50:27 -0500 Subject: [PATCH 04/25] changed to /dev/serial0 for compatibility --- Adafruit_Thermal.py | 2 +- calibrate.py | 2 +- forecast.py | 2 +- main.py | 2 +- printertest.py | 2 +- sudoku-gfx.py | 2 +- sudoku-txt.py | 2 +- timetemp.py | 2 +- twitter.py | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Adafruit_Thermal.py b/Adafruit_Thermal.py index f5e5162..8665d31 100644 --- a/Adafruit_Thermal.py +++ b/Adafruit_Thermal.py @@ -58,7 +58,7 @@ def __init__(self, *args, **kwargs): # If both passed, use those values. baudrate = 19200 if len(args) == 0: - args = [ "/dev/ttyAMA0", baudrate ] + args = [ "/dev/serial0", baudrate ] elif len(args) == 1: args = [ args[0], baudrate ] else: diff --git a/calibrate.py b/calibrate.py index a06f7fa..e29585e 100755 --- a/calibrate.py +++ b/calibrate.py @@ -23,7 +23,7 @@ from __future__ import print_function from Adafruit_Thermal import * -printer = Adafruit_Thermal("/dev/ttyAMA0", 19200, timeout=5) +printer = Adafruit_Thermal("/dev/serial0", 19200, timeout=5) for i in range(0,256,15): printer.begin(i) diff --git a/forecast.py b/forecast.py index b73dad4..f3b464a 100755 --- a/forecast.py +++ b/forecast.py @@ -40,7 +40,7 @@ def forecast(idx): printer.print(deg) printer.println(' ' + cond.replace(u'\u2013', '-').encode('utf-8')) # take care of pesky unicode dash "–" or \u2013 -printer = Adafruit_Thermal("/dev/ttyAMA0", 19200, timeout=5) +printer = Adafruit_Thermal("/dev/serial0", 19200, timeout=5) deg = chr(0xf8) # Degree symbol on thermal printer url = "https://api.darksky.net/forecast/"+API_KEY+"/"+LAT+","+LONG+"?exclude=[alerts,minutely,hourly,flags]&units=us" diff --git a/main.py b/main.py index 4af3456..7fd8b3a 100755 --- a/main.py +++ b/main.py @@ -26,7 +26,7 @@ nextInterval = 0.0 # Time of next recurring operation dailyFlag = False # Set after daily trigger occurs lastId = '1' # State information passed to/from interval script -printer = Adafruit_Thermal("/dev/ttyAMA0", 19200, timeout=5) +printer = Adafruit_Thermal("/dev/serial0", 19200, timeout=5) # Called when button is briefly tapped. Invokes time/temperature script. diff --git a/printertest.py b/printertest.py index 8a79279..c901353 100755 --- a/printertest.py +++ b/printertest.py @@ -2,7 +2,7 @@ from Adafruit_Thermal import * -printer = Adafruit_Thermal("/dev/ttyAMA0", 19200, timeout=5) +printer = Adafruit_Thermal("/dev/serial0", 19200, timeout=5) # Test inverse on & off printer.inverseOn() diff --git a/sudoku-gfx.py b/sudoku-gfx.py index 91b5204..2238870 100755 --- a/sudoku-gfx.py +++ b/sudoku-gfx.py @@ -25,7 +25,7 @@ from Adafruit_Thermal import * import Image -printer = Adafruit_Thermal("/dev/ttyAMA0", 19200, timeout=5) +printer = Adafruit_Thermal("/dev/serial0", 19200, timeout=5) bg = Image.new("1", [384, 426], "white") # Working 'background' image img = Image.open('gfx/sudoku.png') # Source bitmaps xcoord = [ 15, 55, 95, 139, 179, 219, 263, 303, 343 ] diff --git a/sudoku-txt.py b/sudoku-txt.py index 5e9a4db..5ae735c 100755 --- a/sudoku-txt.py +++ b/sudoku-txt.py @@ -22,7 +22,7 @@ import sys, os, random, getopt, re from Adafruit_Thermal import * -printer = Adafruit_Thermal("/dev/ttyAMA0", 19200, timeout=5) +printer = Adafruit_Thermal("/dev/serial0", 19200, timeout=5) def main(): printer.setLineHeight(24) # So graphical chars fit together diff --git a/timetemp.py b/timetemp.py index ce25e54..5e3573d 100755 --- a/timetemp.py +++ b/timetemp.py @@ -164,6 +164,6 @@ def numWidth(str, list): else: img.paste(Mph, (x, y)) # Open connection to printer and print image -printer = Adafruit_Thermal("/dev/ttyAMA0", 19200, timeout=5) +printer = Adafruit_Thermal("/dev/serial0", 19200, timeout=5) printer.printImage(img, True) printer.feed(3) \ No newline at end of file diff --git a/twitter.py b/twitter.py index 662b1fd..9b96575 100755 --- a/twitter.py +++ b/twitter.py @@ -47,7 +47,7 @@ # Other globals. You probably won't need to change these. ----------------- -printer = Adafruit_Thermal("/dev/ttyAMA0", 19200, timeout=5) +printer = Adafruit_Thermal("/dev/serial0", 19200, timeout=5) host = 'api.twitter.com' authUrl = '/oauth2/token' searchUrl = '/1.1/search/tweets.json?' From e8ed2b233b20f69d3749566ff20effe397319fa1 Mon Sep 17 00:00:00 2001 From: jmzjmzjmz Date: Mon, 20 Feb 2017 13:55:08 -0500 Subject: [PATCH 05/25] cleaned up comments --- forecast.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/forecast.py b/forecast.py index f3b464a..4be74e9 100755 --- a/forecast.py +++ b/forecast.py @@ -38,7 +38,7 @@ def forecast(idx): printer.print(deg) printer.print(' high ' + str(hi)) printer.print(deg) - printer.println(' ' + cond.replace(u'\u2013', '-').encode('utf-8')) # take care of pesky unicode dash "–" or \u2013 + printer.println(' ' + cond.replace(u'\u2013', '-').encode('utf-8')) # take care of pesky unicode dash printer = Adafruit_Thermal("/dev/serial0", 19200, timeout=5) deg = chr(0xf8) # Degree symbol on thermal printer From 515e3e2b23d2043f8251c8a33058b52f16d7d0f1 Mon Sep 17 00:00:00 2001 From: jmzjmzjmz Date: Mon, 20 Feb 2017 13:56:45 -0500 Subject: [PATCH 06/25] cleaned up comments --- forecast.py | 2 +- timetemp.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/forecast.py b/forecast.py index 4be74e9..3d9da85 100755 --- a/forecast.py +++ b/forecast.py @@ -20,7 +20,7 @@ import calendar import urllib, json -# API_KEY = "YOUR_API_KEY" +API_KEY = "YOUR_API_KEY" LAT = "40.726019" LONG = "-74.00536" diff --git a/timetemp.py b/timetemp.py index 5e3573d..80a6ea6 100755 --- a/timetemp.py +++ b/timetemp.py @@ -17,7 +17,7 @@ from Adafruit_Thermal import * import Image, ImageDraw, time, urllib, json -# API_KEY = "YOUR_API_KEY" +API_KEY = "YOUR_API_KEY" LAT = "40.726019" LONG = "-74.00536" From 2a1ff7e898702d7b750409fe1a5fdf52dd495f2a Mon Sep 17 00:00:00 2001 From: Phillip Burgess Date: Wed, 26 Apr 2017 12:16:29 -0700 Subject: [PATCH 07/25] Some changes for newer printer firmware --- Adafruit_Thermal.py | 473 +++++++++++++++++++++++++++++--------------- 1 file changed, 318 insertions(+), 155 deletions(-) diff --git a/Adafruit_Thermal.py b/Adafruit_Thermal.py index 8665d31..31b7572 100644 --- a/Adafruit_Thermal.py +++ b/Adafruit_Thermal.py @@ -36,95 +36,108 @@ from __future__ import print_function from serial import Serial import time +import sys class Adafruit_Thermal(Serial): - resumeTime = 0.0 - byteTime = 0.0 - dotPrintTime = 0.033 - dotFeedTime = 0.0025 - prevByte = '\n' - column = 0 - maxColumn = 32 - charHeight = 24 - lineSpacing = 8 - barcodeHeight = 50 - printMode = 0 - defaultHeatTime = 60 + resumeTime = 0.0 + byteTime = 0.0 + dotPrintTime = 0.0 + dotFeedTime = 0.0 + prevByte = '\n' + column = 0 + maxColumn = 32 + charHeight = 24 + lineSpacing = 8 + barcodeHeight = 50 + printMode = 0 + defaultHeatTime = 120 + firmwareVersion = 268 + writeToStdout = False def __init__(self, *args, **kwargs): - # If no parameters given, use default port & baud rate. - # If only port is passed, use default baud rate. - # If both passed, use those values. + # NEW BEHAVIOR: if no parameters given, output is written + # to stdout, to be piped through 'lp -o raw' (old behavior + # was to use default port & baud rate). baudrate = 19200 if len(args) == 0: - args = [ "/dev/serial0", baudrate ] - elif len(args) == 1: + self.writeToStdout = True + if len(args) == 1: + # If only port is passed, use default baud rate. args = [ args[0], baudrate ] - else: + elif len(args) == 2: + # If both passed, use those values. baudrate = args[1] - # Calculate time to issue one byte to the printer. - # 11 bits (not 8) to accommodate idle, start and stop bits. - # Idle time might be unnecessary, but erring on side of - # caution here. - self.byteTime = 11.0 / float(baudrate) - - Serial.__init__(self, *args, **kwargs) - - # Remainder of this method was previously in begin() - - # The printer can't start receiving data immediately upon - # power up -- it needs a moment to cold boot and initialize. - # Allow at least 1/2 sec of uptime before printer can - # receive data. - self.timeoutSet(0.5) - - self.wake() - self.reset() - - # Description of print settings from page 23 of the manual: - # ESC 7 n1 n2 n3 Setting Control Parameter Command - # Decimal: 27 55 n1 n2 n3 - # Set "max heating dots", "heating time", "heating interval" - # n1 = 0-255 Max heat dots, Unit (8dots), Default: 7 (64 dots) - # n2 = 3-255 Heating time, Unit (10us), Default: 80 (800us) - # n3 = 0-255 Heating interval, Unit (10us), Default: 2 (20us) - # The more max heating dots, the more peak current will cost - # when printing, the faster printing speed. The max heating - # dots is 8*(n1+1). The more heating time, the more density, - # but the slower printing speed. If heating time is too short, - # blank page may occur. The more heating interval, the more - # clear, but the slower printing speed. - - heatTime = kwargs.get('heattime', self.defaultHeatTime) - self.writeBytes( - 27, # Esc - 55, # 7 (print settings) - 20, # Heat dots (20 = balance darkness w/no jams) - heatTime, # Lib default = 45 - 250) # Heat interval (500 uS = slower but darker) - - # Description of print density from page 23 of the manual: - # DC2 # n Set printing density - # Decimal: 18 35 n - # D4..D0 of n is used to set the printing density. - # Density is 50% + 5% * n(D4-D0) printing density. - # D7..D5 of n is used to set the printing break time. - # Break time is n(D7-D5)*250us. - # (Unsure of the default value for either -- not documented) - - printDensity = 14 # 120% (can go higher, but text gets fuzzy) - printBreakTime = 4 # 500 uS - - self.writeBytes( - 18, # DC2 - 35, # Print density - (printBreakTime << 5) | printDensity) - - self.dotPrintTime = 0.03 - self.dotFeedTime = 0.0021 - + # Firmware is assumed version 2.68. Can override this + # with the 'firmware=X' argument, where X is the major + # version number * 100 + the minor version number (e.g. + # pass "firmware=264" for version 2.64. + self.firmwareVersion = kwargs.get('firmware', 268) + + if self.writeToStdout is False: + # Calculate time to issue one byte to the printer. + # 11 bits (not 8) to accommodate idle, start and + # stop bits. Idle time might be unnecessary, but + # erring on side of caution here. + self.byteTime = 11.0 / float(baudrate) + + Serial.__init__(self, *args, **kwargs) + + # Remainder of this method was previously in begin() + + # The printer can't start receiving data immediately + # upon power up -- it needs a moment to cold boot + # and initialize. Allow at least 1/2 sec of uptime + # before printer can receive data. + self.timeoutSet(0.5) + + self.wake() + self.reset() + + # Description of print settings from p. 23 of manual: + # ESC 7 n1 n2 n3 Setting Control Parameter Command + # Decimal: 27 55 n1 n2 n3 + # max heating dots, heating time, heating interval + # n1 = 0-255 Max heat dots, Unit (8dots), Default: 7 (64 dots) + # n2 = 3-255 Heating time, Unit (10us), Default: 80 (800us) + # n3 = 0-255 Heating interval, Unit (10us), Default: 2 (20us) + # The more max heating dots, the more peak current + # will cost when printing, the faster printing speed. + # The max heating dots is 8*(n1+1). The more heating + # time, the more density, but the slower printing + # speed. If heating time is too short, blank page + # may occur. The more heating interval, the more + # clear, but the slower printing speed. + + heatTime = kwargs.get('heattime', self.defaultHeatTime) + self.writeBytes( + 27, # Esc + 55, # 7 (print settings) + 11, # Heat dots + heatTime, # Lib default + 40) # Heat interval + + # Description of print density from p. 23 of manual: + # DC2 # n Set printing density + # Decimal: 18 35 n + # D4..D0 of n is used to set the printing density. + # Density is 50% + 5% * n(D4-D0) printing density. + # D7..D5 of n is used to set the printing break time. + # Break time is n(D7-D5)*250us. + # (Unsure of default values -- not documented) + + printDensity = 10 # 100% + printBreakTime = 2 # 500 uS + + self.writeBytes( + 18, # DC2 + 35, # Print density + (printBreakTime << 5) | printDensity) + self.dotPrintTime = 0.03 + self.dotFeedTime = 0.0021 + else: + self.reset() # Inits some vars # Because there's no flow control between the printer and computer, # special care must be taken to avoid overrunning the printer's @@ -144,8 +157,8 @@ def timeoutSet(self, x): # Waits (if necessary) for the prior task to complete. def timeoutWait(self): - while (time.time() - self.resumeTime) < 0: pass - + if self.writeToStdout is False: + while (time.time() - self.resumeTime) < 0: pass # Printer performance may vary based on the power supply voltage, # thickness of paper, phase of the moon and other seemingly random @@ -164,19 +177,24 @@ def setTimes(self, p, f): self.dotPrintTime = p / 1000000.0 self.dotFeedTime = f / 1000000.0 - # 'Raw' byte-writing method def writeBytes(self, *args): - self.timeoutWait() - self.timeoutSet(len(args) * self.byteTime) - for arg in args: - super(Adafruit_Thermal, self).write(chr(arg)) - + if self.writeToStdout: + for arg in args: + sys.stdout.write(chr(arg)) + else: + self.timeoutWait() + self.timeoutSet(len(args) * self.byteTime) + for arg in args: + super(Adafruit_Thermal, self).write(chr(arg)) # Override write() method to keep track of paper feed. def write(self, *data): for i in range(len(data)): c = data[i] + if self.writeToStdout: + sys.stdout.write(c) + continue if c != 0x13: self.timeoutWait() super(Adafruit_Thermal, self).write(c) @@ -204,7 +222,6 @@ def write(self, *data): self.timeoutSet(d) self.prevByte = c - # The bulk of this method was moved into __init__, # but this is left here for compatibility with older # code that might get ported directly from Arduino. @@ -212,20 +229,23 @@ def begin(self, heatTime=defaultHeatTime): self.writeBytes( 27, # Esc 55, # 7 (print settings) - 20, # Heat dots (20 = balance darkness w/no jams) - heatTime, # Lib default = 45 - 250) # Heat interval (500 uS = slower but darker) - + 11, # Heat dots + heatTime, + 40) # Heat interval def reset(self): + self.writeBytes(27, 64) # Esc @ = init command self.prevByte = '\n' # Treat as if prior line is blank self.column = 0 self.maxColumn = 32 self.charHeight = 24 - self.lineSpacing = 8 + self.lineSpacing = 6 self.barcodeHeight = 50 - self.writeBytes(27, 64) - + if self.firmwareVersion >= 264: + # Configure tab stops on recent printers + self.writeBytes(27, 68) # Set tab stops + self.writeBytes( 4, 8, 12, 16) # every 4 columns, + self.writeBytes(20, 24, 28, 0) # 0 is end-of-list. # Reset text formatting parameters. def setDefault(self): @@ -233,19 +253,28 @@ def setDefault(self): self.justify('L') self.inverseOff() self.doubleHeightOff() - self.setLineHeight(32) + self.setLineHeight(30) self.boldOff() self.underlineOff() self.setBarcodeHeight(50) self.setSize('s') - + self.setCharset() + self.setCodePage() def test(self): + self.write("Hello world!") + self.feed(2) + + def testPage(self): self.writeBytes(18, 84) self.timeoutSet( self.dotPrintTime * 24 * 26 + - self.dotFeedTime * (8 * 26 + 32)) + self.dotFeedTime * (6 * 26 + 30)) + def setBarcodeHeight(self, val=50): + if val < 1: val = 1 + self.barcodeHeight = val + self.writeBytes(29, 104, val) UPC_A = 0 UPC_E = 1 @@ -258,29 +287,79 @@ def test(self): CODE128 = 8 CODE11 = 9 MSI = 10 + ITF = 11 + CODABAR = 12 def printBarcode(self, text, type): + + newDict = { # UPC codes & values for firmwareVersion >= 264 + self.UPC_A : 65, + self.UPC_E : 66, + self.EAN13 : 67, + self.EAN8 : 68, + self.CODE39 : 69, + self.ITF : 70, + self.CODABAR : 71, + self.CODE93 : 72, + self.CODE128 : 73, + self.I25 : -1, # NOT IN NEW FIRMWARE + self.CODEBAR : -1, + self.CODE11 : -1, + self.MSI : -1 + } + oldDict = { # UPC codes & values for firmwareVersion < 264 + self.UPC_A : 0, + self.UPC_E : 1, + self.EAN13 : 2, + self.EAN8 : 3, + self.CODE39 : 4, + self.I25 : 5, + self.CODEBAR : 6, + self.CODE93 : 7, + self.CODE128 : 8, + self.CODE11 : 9, + self.MSI : 10, + self.ITF : -1, # NOT IN OLD FIRMWARE + self.CODABAR : -1 + } + + if self.firmwareVersion >= 264: + n = newDict[type] + else: + n = oldDict[type] + if n == -1: return + self.feed(1) # Recent firmware requires this? self.writeBytes( - 29, 72, 2, # Print label below barcode - 29, 119, 3, # Barcode width - 29, 107, type) # Barcode type - # Print string + 29, 72, 2, # Print label below barcode + 29, 119, 3, # Barcode width + 29, 107, n) # Barcode type self.timeoutWait() self.timeoutSet((self.barcodeHeight + 40) * self.dotPrintTime) - super(Adafruit_Thermal, self).write(text) + # Print string + if self.firmwareVersion >= 264: + # Recent firmware: write length byte + string sans NUL + n = len(text) + if n > 255: n = 255 + if self.writeToStdout: + sys.stdout.write(chr(n)) + for i in range(n): + sys.stdout.write(text[i]) + else: + super(Adafruit_Thermal, self).write(chr(n)) + for i in range(n): + super(Adafruit_Thermal, + self).write(text[n]) + else: + # Older firmware: write string + NUL + if self.writeToStdout: + sys.stdout.write(text) + else: + super(Adafruit_Thermal, self).write(text) self.prevByte = '\n' - self.feed(2) - - def setBarcodeHeight(self, val=50): - if val < 1: - val = 1 - self.barcodeHeight = val - self.writeBytes(29, 104, val) - # === Character commands === - INVERSE_MASK = (1 << 1) + INVERSE_MASK = (1 << 1) # Not in 2.6.8 firmware (see inverseOn()) UPDOWN_MASK = (1 << 2) BOLD_MASK = (1 << 3) DOUBLE_HEIGHT_MASK = (1 << 4) @@ -319,10 +398,16 @@ def normal(self): self.writePrintMode() def inverseOn(self): - self.setPrintMode(self.INVERSE_MASK) + if self.firmwareVersion >= 268: + self.writeBytes(29, 66, 1) + else: + self.setPrintMode(self.INVERSE_MASK) def inverseOff(self): - self.unsetPrintMode(self.INVERSE_MASK) + if self.firmwareVersion >= 268: + self.writeBytes(29, 66, 0) + else: + self.unsetPrintMode(self.INVERSE_MASK) def upsideDownOn(self): self.setPrintMode(self.UPDOWN_MASK) @@ -354,7 +439,6 @@ def boldOn(self): def boldOff(self): self.unsetPrintMode(self.BOLD_MASK) - def justify(self, value): c = value.upper() if c == 'C': @@ -365,25 +449,30 @@ def justify(self, value): pos = 0 self.writeBytes(0x1B, 0x61, pos) - # Feeds by the specified number of lines def feed(self, x=1): - # The datasheet claims sending bytes 27, 100, will work, - # but it feeds much more than that. So it's done manually: - while x > 0: - self.write('\n') - x -= 1 + if self.firmwareVersion >= 264: + self.writeBytes(27, 100, x) + self.timeoutSet(self.dotFeedTime * self.charHeight) + self.prevByte = '\n' + self.column = 0 + else: + # datasheet claims sending bytes 27, 100, works, + # but it feeds much more than that. So, manually: + while x > 0: + self.write('\n') + x -= 1 # Feeds by the specified number of individual pixel rows def feedRows(self, rows): self.writeBytes(27, 74, rows) self.timeoutSet(rows * dotFeedTime) - + self.prevByte = '\n' + self.column = 0 def flush(self): - self.writeBytes(12) - + self.writeBytes(12) # ASCII FF def setSize(self, value): c = value.upper() @@ -400,21 +489,19 @@ def setSize(self, value): self.charHeight = 24 self.maxColumn = 32 - self.writeBytes(29, 33, size, 10) + self.writeBytes(29, 33, size) prevByte = '\n' # Setting the size adds a linefeed - # Underlines of different weights can be produced: # 0 - no underline # 1 - normal underline # 2 - thick underline def underlineOn(self, weight=1): + if weight > 2: weight = 2 self.writeBytes(27, 45, weight) - def underlineOff(self): - self.underlineOn(0) - + self.writeBytes(27, 45, 0) def printBitmap(self, w, h, bitmap, LaaT=False): rowBytes = (w + 7) / 8 # Round up to next byte boundary @@ -443,8 +530,12 @@ def printBitmap(self, w, h, bitmap, LaaT=False): for y in range(chunkHeight): for x in range(rowBytesClipped): - super(Adafruit_Thermal, self).write( - chr(bitmap[i])) + if self.writeToStdout: + sys.stdout.write( + chr(bitmap[i])) + else: + super(Adafruit_Thermal, + self).write(chr(bitmap[i])) i += 1 i += rowBytes - rowBytesClipped self.timeoutSet(chunkHeight * self.dotPrintTime) @@ -459,7 +550,7 @@ def printBitmap(self, w, h, bitmap, LaaT=False): # the Imaging Library to perform such operations before # passing the result to this function. def printImage(self, image, LaaT=False): - import Image + from PIL import Image if image.mode != '1': image = image.convert('1') @@ -488,57 +579,58 @@ def printImage(self, image, LaaT=False): self.printBitmap(width, height, bitmap, LaaT) - # Take the printer offline. Print commands sent after this # will be ignored until 'online' is called. def offline(self): self.writeBytes(27, 61, 0) - # Take the printer online. Subsequent print commands will be obeyed. def online(self): self.writeBytes(27, 61, 1) - # Put the printer into a low-energy state immediately. def sleep(self): - self.sleepAfter(1) - + self.sleepAfter(1) # Can't be 0, that means "don't sleep" # Put the printer into a low-energy state after # the given number of seconds. def sleepAfter(self, seconds): - self.writeBytes(27, 56, seconds) - + if self.firmwareVersion >= 264: + self.writeBytes(27, 56, seconds & 0xFF, seconds >> 8) + else: + self.writeBytes(27, 56, seconds) def wake(self): - self.timeoutSet(0); + self.timeoutSet(0) self.writeBytes(255) - for i in range(10): - self.writeBytes(27) - self.timeoutSet(0.1) - + if self.firmwareVersion >= 264: + time.sleep(0.05) # 50 ms + self.writeBytes(27, 118, 0) # Sleep off (important!) + else: + for i in range(10): + self.writeBytes(27) + self.timeoutSet(0.1) # Empty method, included for compatibility # with existing code ported from Arduino. def listen(self): pass - # Check the status of the paper using the printers self reporting # ability. Doesn't match the datasheet... # Returns True for paper, False for no paper. def hasPaper(self): - self.writeBytes(27, 118, 0) + if self.firmwareVersion >= 264: + self.writeBytes(27, 118, 0) + else: + self.writeBytes(29, 114, 0) # Bit 2 of response seems to be paper status stat = ord(self.read(1)) & 0b00000100 # If set, we have paper; if clear, no paper return stat == 0 - def setLineHeight(self, val=32): - if val < 24: - val = 24 + if val < 24: val = 24 self.lineSpacing = val - 24 # The printer doesn't take into account the current text @@ -547,16 +639,87 @@ def setLineHeight(self, val=32): # (char height of 24, line spacing of 8). self.writeBytes(27, 51, val) - - # Copied from Arduino lib for parity; is marked 'not working' there + CHARSET_USA = 0 + CHARSET_FRANCE = 1 + CHARSET_GERMANY = 2 + CHARSET_UK = 3 + CHARSET_DENMARK1 = 4 + CHARSET_SWEDEN = 5 + CHARSET_ITALY = 6 + CHARSET_SPAIN1 = 7 + CHARSET_JAPAN = 8 + CHARSET_NORWAY = 9 + CHARSET_DENMARK2 = 10 + CHARSET_SPAIN2 = 11 + CHARSET_LATINAMERICA = 12 + CHARSET_KOREA = 13 + CHARSET_SLOVENIA = 14 + CHARSET_CROATIA = 14 + CHARSET_CHINA = 15 + + # Alters some chars in ASCII 0x23-0x7E range; see datasheet + def setCharset(self, val=0): + if val > 15: val = 15 + self.writeBytes(27, 82, val) + + CODEPAGE_CP437 = 0 # USA, Standard Europe + CODEPAGE_KATAKANA = 1 + CODEPAGE_CP850 = 2 # Multilingual + CODEPAGE_CP860 = 3 # Portuguese + CODEPAGE_CP863 = 4 # Canadian-French + CODEPAGE_CP865 = 5 # Nordic + CODEPAGE_WCP1251 = 6 # Cyrillic + CODEPAGE_CP866 = 7 # Cyrillic #2 + CODEPAGE_MIK = 8 # Cyrillic/Bulgarian + CODEPAGE_CP755 = 9 # East Europe, Latvian 2 + CODEPAGE_IRAN = 10 + CODEPAGE_CP862 = 15 # Hebrew + CODEPAGE_WCP1252 = 16 # Latin 1 + CODEPAGE_WCP1253 = 17 # Greek + CODEPAGE_CP852 = 18 # Latin 2 + CODEPAGE_CP858 = 19 # Multilingual Latin 1 + Euro + CODEPAGE_IRAN2 = 20 + CODEPAGE_LATVIAN = 21 + CODEPAGE_CP864 = 22 # Arabic + CODEPAGE_ISO_8859_1 = 23 # West Europe + CODEPAGE_CP737 = 24 # Greek + CODEPAGE_WCP1257 = 25 # Baltic + CODEPAGE_THAI = 26 + CODEPAGE_CP720 = 27 # Arabic + CODEPAGE_CP855 = 28 + CODEPAGE_CP857 = 29 # Turkish + CODEPAGE_WCP1250 = 30 # Central Europe + CODEPAGE_CP775 = 31 + CODEPAGE_WCP1254 = 32 # Turkish + CODEPAGE_WCP1255 = 33 # Hebrew + CODEPAGE_WCP1256 = 34 # Arabic + CODEPAGE_WCP1258 = 35 # Vietnam + CODEPAGE_ISO_8859_2 = 36 # Latin 2 + CODEPAGE_ISO_8859_3 = 37 # Latin 3 + CODEPAGE_ISO_8859_4 = 38 # Baltic + CODEPAGE_ISO_8859_5 = 39 # Cyrillic + CODEPAGE_ISO_8859_6 = 40 # Arabic + CODEPAGE_ISO_8859_7 = 41 # Greek + CODEPAGE_ISO_8859_8 = 42 # Hebrew + CODEPAGE_ISO_8859_9 = 43 # Turkish + CODEPAGE_ISO_8859_15 = 44 # Latin 3 + CODEPAGE_THAI2 = 45 + CODEPAGE_CP856 = 46 + CODEPAGE_CP874 = 47 + + # Selects alt symbols for 'upper' ASCII values 0x80-0xFF + def setCodePage(self, val=0): + if val > 47: val = 47 + self.writeBytes(27, 116, val) + + # Copied from Arduino lib for parity; may not work on all printers def tab(self): self.writeBytes(9) + self.column = (self.column + 4) & 0xFC - - # Copied from Arduino lib for parity; is marked 'not working' there + # Copied from Arduino lib for parity; may not work on all printers def setCharSpacing(self, spacing): - self.writeBytes(27, 32, 0, 10) - + self.writeBytes(27, 32, spacing) # Overloading print() in Python pre-3.0 is dirty pool, # but these are here to provide more direct compatibility From 0ae093e2d578a3f460e745c697138b28b51aca75 Mon Sep 17 00:00:00 2001 From: Phillip Burgess Date: Wed, 26 Apr 2017 16:08:12 -0700 Subject: [PATCH 08/25] Fix typos --- Adafruit_Thermal.py | 2 +- printertest.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Adafruit_Thermal.py b/Adafruit_Thermal.py index 31b7572..4c2cd3d 100644 --- a/Adafruit_Thermal.py +++ b/Adafruit_Thermal.py @@ -348,7 +348,7 @@ def printBarcode(self, text, type): super(Adafruit_Thermal, self).write(chr(n)) for i in range(n): super(Adafruit_Thermal, - self).write(text[n]) + self).write(text[i]) else: # Older firmware: write string + NUL if self.writeToStdout: diff --git a/printertest.py b/printertest.py index c901353..2664884 100755 --- a/printertest.py +++ b/printertest.py @@ -61,7 +61,7 @@ import gfx.adaqrcode as adaqrcode printer.printBitmap(adaqrcode.width, adaqrcode.height, adaqrcode.data) printer.println("Adafruit!") -printer.feed(1) +printer.feed(2) printer.sleep() # Tell printer to sleep printer.wake() # Call wake() before printing again, even if reset From 8165c22f49ba72039f4fc81efc0dd2e57a9ea6a9 Mon Sep 17 00:00:00 2001 From: Phillip Burgess Date: Wed, 3 May 2017 22:04:52 -0700 Subject: [PATCH 09/25] Fix import statements for Image, ImageDraw --- main.py | 3 ++- sudoku-gfx.py | 2 +- timetemp.py | 5 +++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/main.py b/main.py index 7fd8b3a..9ebff71 100755 --- a/main.py +++ b/main.py @@ -16,7 +16,8 @@ from __future__ import print_function import RPi.GPIO as GPIO -import subprocess, time, Image, socket +import subprocess, time, socket +from PIL import Image from Adafruit_Thermal import * ledPin = 18 diff --git a/sudoku-gfx.py b/sudoku-gfx.py index 2238870..2a7ac12 100755 --- a/sudoku-gfx.py +++ b/sudoku-gfx.py @@ -23,7 +23,7 @@ from __future__ import print_function import sys, os, random, getopt, re from Adafruit_Thermal import * -import Image +from PIL import Image printer = Adafruit_Thermal("/dev/serial0", 19200, timeout=5) bg = Image.new("1", [384, 426], "white") # Working 'background' image diff --git a/timetemp.py b/timetemp.py index 80a6ea6..fcd16ba 100755 --- a/timetemp.py +++ b/timetemp.py @@ -15,7 +15,8 @@ from __future__ import print_function from Adafruit_Thermal import * -import Image, ImageDraw, time, urllib, json +import time, urllib, json +from PIL import Image, ImageDraw API_KEY = "YOUR_API_KEY" @@ -166,4 +167,4 @@ def numWidth(str, list): # Open connection to printer and print image printer = Adafruit_Thermal("/dev/serial0", 19200, timeout=5) printer.printImage(img, True) -printer.feed(3) \ No newline at end of file +printer.feed(3) From ea2a5246025b08836e17dfde9a0e8e61b5d15801 Mon Sep 17 00:00:00 2001 From: sauntheninja2 <48253973+sauntheninja2@users.noreply.github.com> Date: Wed, 6 Mar 2019 05:57:24 +0530 Subject: [PATCH 10/25] Update twitter.py --- twitter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/twitter.py b/twitter.py index 9b96575..1d980ba 100755 --- a/twitter.py +++ b/twitter.py @@ -39,7 +39,7 @@ consumer_secret = 'PUT_YOUR_CONSUMER_SECRET_HERE' # queryString can be any valid Twitter API search string, including -# boolean operators. See http://dev.twitter.com/docs/using-search +# boolean operators. See https://developer.twitter.com/en/docs/tweets/search/api-reference/get-search-tweets # for options and syntax. Funny characters do NOT need to be URL # encoded here -- urllib takes care of that. queryString = 'from:Adafruit' From fc3933677a885ac3a5e4d4165bcd6fb3941ea727 Mon Sep 17 00:00:00 2001 From: galacticfan Date: Tue, 5 May 2020 12:09:20 +0100 Subject: [PATCH 11/25] Python3 compatibility --- Adafruit_Thermal.py | 46 +++++++++++++++++++--------------------- README.md | 51 +++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 71 insertions(+), 26 deletions(-) diff --git a/Adafruit_Thermal.py b/Adafruit_Thermal.py index 4c2cd3d..2043d5a 100644 --- a/Adafruit_Thermal.py +++ b/Adafruit_Thermal.py @@ -32,11 +32,10 @@ # - Trap errors properly. Some stuff just falls through right now. # - Add docstrings throughout! -# Python 2.X code using the library usu. needs to include the next line: -from __future__ import print_function from serial import Serial import time import sys +import math class Adafruit_Thermal(Serial): @@ -181,12 +180,12 @@ def setTimes(self, p, f): def writeBytes(self, *args): if self.writeToStdout: for arg in args: - sys.stdout.write(chr(arg)) + sys.stdout.write(bytes([arg])) else: - self.timeoutWait() - self.timeoutSet(len(args) * self.byteTime) for arg in args: - super(Adafruit_Thermal, self).write(chr(arg)) + self.timeoutWait() + self.timeoutSet(len(args) * self.byteTime) + super(Adafruit_Thermal, self).write(bytes([arg])) # Override write() method to keep track of paper feed. def write(self, *data): @@ -262,7 +261,7 @@ def setDefault(self): self.setCodePage() def test(self): - self.write("Hello world!") + self.write("Hello world!".encode('cp437', 'ignore')) self.feed(2) def testPage(self): @@ -341,20 +340,20 @@ def printBarcode(self, text, type): n = len(text) if n > 255: n = 255 if self.writeToStdout: - sys.stdout.write(chr(n)) + sys.stdout.write((chr(n)).encode('cp437', 'ignore')) for i in range(n): - sys.stdout.write(text[i]) + sys.stdout.write(text[i].encode('utf-8', 'ignore')) else: - super(Adafruit_Thermal, self).write(chr(n)) + super(Adafruit_Thermal, self).write((chr(n)).encode('utf-8', 'ignore')) for i in range(n): super(Adafruit_Thermal, - self).write(text[i]) + self).write(text[i].encode('utf-8', 'ignore')) else: # Older firmware: write string + NUL if self.writeToStdout: - sys.stdout.write(text) + sys.stdout.write(text.encode('utf-8', 'ignore')) else: - super(Adafruit_Thermal, self).write(text) + super(Adafruit_Thermal, self).write(text.encode('utf-8', 'ignore')) self.prevByte = '\n' # === Character commands === @@ -461,7 +460,7 @@ def feed(self, x=1): # datasheet claims sending bytes 27, 100, works, # but it feeds much more than that. So, manually: while x > 0: - self.write('\n') + self.write('\n'.encode('cp437', 'ignore')) x -= 1 # Feeds by the specified number of individual pixel rows @@ -504,7 +503,7 @@ def underlineOff(self): self.writeBytes(27, 45, 0) def printBitmap(self, w, h, bitmap, LaaT=False): - rowBytes = (w + 7) / 8 # Round up to next byte boundary + rowBytes = math.floor((w + 7) / 8) # Round up to next byte boundary if rowBytes >= 48: rowBytesClipped = 48 # 384 pixels max width else: @@ -531,11 +530,10 @@ def printBitmap(self, w, h, bitmap, LaaT=False): for y in range(chunkHeight): for x in range(rowBytesClipped): if self.writeToStdout: - sys.stdout.write( - chr(bitmap[i])) + sys.stdout.write(bytes([bitmap[i]])) else: super(Adafruit_Thermal, - self).write(chr(bitmap[i])) + self).write(bytes([bitmap[i]])) i += 1 i += rowBytes - rowBytesClipped self.timeoutSet(chunkHeight * self.dotPrintTime) @@ -549,9 +547,9 @@ def printBitmap(self, w, h, bitmap, LaaT=False): # For any other behavior (scale, B&W threshold, etc.), use # the Imaging Library to perform such operations before # passing the result to this function. - def printImage(self, image, LaaT=False): + def printImage(self, image_file, LaaT=False): from PIL import Image - + image = Image.open(image_file) if image.mode != '1': image = image.convert('1') @@ -559,7 +557,7 @@ def printImage(self, image, LaaT=False): height = image.size[1] if width > 384: width = 384 - rowBytes = (width + 7) / 8 + rowBytes = math.floor((width + 7) / 8) bitmap = bytearray(rowBytes * height) pixels = image.load() @@ -726,11 +724,11 @@ def setCharSpacing(self, spacing): # with existing code written for the Arduino library. def print(self, *args, **kwargs): for arg in args: - self.write(str(arg)) + self.write((str(arg)).encode('cp437', 'ignore')) # For Arduino code compatibility again def println(self, *args, **kwargs): for arg in args: - self.write(str(arg)) - self.write('\n') + self.write((str(arg)).encode('cp437', 'ignore')) + self.write('\n'.encode('cp437', 'ignore')) diff --git a/README.md b/README.md index 7af54a6..71fc87a 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,49 @@ -Python-Thermal-Printer -====================== +# Python-Thermal-Printer Module + +Python3 port of the original Adafruit [Python-Thermal-Printer](https://github.com/adafruit/Python-Thermal-Printer) library. + +## Getting Started + +Install Raspbian Buster and Wire the printer according to [this](https://learn.adafruit.com/networked-thermal-printer-using-cups-and-raspberry-pi/connect-and-configure-printer). I powered the printer with the GPIO pins as well. + +Run a test to see if the printer is working by punching in these commands into the terminal. + +``` +stty -F /dev/serial0 19200 +echo -e "This is a test.\\n\\n\\n" > /dev/serial0 +``` + +### Installing + +Update the system and install prequisities. + +``` +sudo apt-get update +sudo apt-get install git cups wiringpi build-essential libcups2-dev libcupsimage2-dev python-serial python-pil python-unidecode +``` + +Install the printer driver. Don't worry about the warnings that g++ gives. + +``` +git clone https://github.com/adafruit/zj-58 +cd zj-58 +make +sudo ./install +``` + +Make the printer the default printer. This is useful if you are going to be doing other things with it. + +``` +sudo lpadmin -p ZJ-58 -E -v serial:/dev/serial0?baud=19200 -m zjiang/ZJ-58.ppd +sudo lpoptions -d ZJ-58 +``` + +Restart the system. Clone this repository and try to run *printertest.py*. + +``` +git clone https://github.com/galacticfan/Python-Thermal-Printer/ +cd Python-Thermal-Printer +python3 printertest.py +``` + +Let me know if you have any issues. From 6f26289c4c6d5cd99031844477bbb73fc5f8b877 Mon Sep 17 00:00:00 2001 From: Zack Sheppard Date: Thu, 28 May 2020 23:11:38 -0400 Subject: [PATCH 12/25] Check that image_file parameter to printImage is a string before trying to open like a path --- Adafruit_Thermal.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Adafruit_Thermal.py b/Adafruit_Thermal.py index 2043d5a..a17ebdd 100644 --- a/Adafruit_Thermal.py +++ b/Adafruit_Thermal.py @@ -549,7 +549,11 @@ def printBitmap(self, w, h, bitmap, LaaT=False): # passing the result to this function. def printImage(self, image_file, LaaT=False): from PIL import Image - image = Image.open(image_file) + image = image_file + + if isinstance(image_file, str): + image = Image.open(image_file) + if image.mode != '1': image = image.convert('1') From 7b36213e3baa8e387ff9e067b79c99f7ec0deba2 Mon Sep 17 00:00:00 2001 From: Nathan Lee Date: Mon, 7 Sep 2020 17:14:15 -0700 Subject: [PATCH 13/25] Changed timetemp to display with openweathermap --- Adafruit_Thermal.py | 7 +++-- __pycache__/Adafruit_Thermal.cpython-37.pyc | Bin 0 -> 15557 bytes timetemp.py | 31 ++++++++++---------- 3 files changed, 20 insertions(+), 18 deletions(-) create mode 100644 __pycache__/Adafruit_Thermal.cpython-37.pyc diff --git a/Adafruit_Thermal.py b/Adafruit_Thermal.py index 2043d5a..429d49b 100644 --- a/Adafruit_Thermal.py +++ b/Adafruit_Thermal.py @@ -548,8 +548,11 @@ def printBitmap(self, w, h, bitmap, LaaT=False): # the Imaging Library to perform such operations before # passing the result to this function. def printImage(self, image_file, LaaT=False): - from PIL import Image - image = Image.open(image_file) + image = image_file + + if isinstance(image_file, str): + image = Image.open(image_file) + if image.mode != '1': image = image.convert('1') diff --git a/__pycache__/Adafruit_Thermal.cpython-37.pyc b/__pycache__/Adafruit_Thermal.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b87be734f5439d9da24143dad0d35f3146c1c06f GIT binary patch literal 15557 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 0: for winDirNum in range(len(DirAngle) - 1): if windDir < DirAngle[winDirNum]: break -winDirNum+=1 +winDirNum+=1 w = Humidity.size[0] + 5 + numWidth(s, HumiDigit) w2 = Wind.size[0] + 5 + numWidth(s2, HumiDigit) if windSpeed > 0: From 655d2ba69be1525da464f5881e34e83ffefd158b Mon Sep 17 00:00:00 2001 From: Nathan Lee Date: Mon, 7 Sep 2020 17:16:46 -0700 Subject: [PATCH 14/25] Removed change to thermal library --- Adafruit_Thermal.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/Adafruit_Thermal.py b/Adafruit_Thermal.py index 429d49b..2043d5a 100644 --- a/Adafruit_Thermal.py +++ b/Adafruit_Thermal.py @@ -548,11 +548,8 @@ def printBitmap(self, w, h, bitmap, LaaT=False): # the Imaging Library to perform such operations before # passing the result to this function. def printImage(self, image_file, LaaT=False): - image = image_file - - if isinstance(image_file, str): - image = Image.open(image_file) - + from PIL import Image + image = Image.open(image_file) if image.mode != '1': image = image.convert('1') From beaa2d4209b3cdfc2091b57386c53dcb2a0e4139 Mon Sep 17 00:00:00 2001 From: Chris Lindbergh Date: Sat, 26 Dec 2020 20:05:24 -0700 Subject: [PATCH 15/25] Add .gitignore file --- .gitignore | 1 + 1 file changed, 1 insertion(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a348e50 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/__pycache__/ From 751850d01a40ccdb4591ae7e4a4c65b1665ccc0f Mon Sep 17 00:00:00 2001 From: Chris Lindbergh Date: Sat, 26 Dec 2020 20:06:51 -0700 Subject: [PATCH 16/25] Set heat to custom level --- Adafruit_Thermal.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Adafruit_Thermal.py b/Adafruit_Thermal.py index 2043d5a..f588831 100644 --- a/Adafruit_Thermal.py +++ b/Adafruit_Thermal.py @@ -50,7 +50,7 @@ class Adafruit_Thermal(Serial): lineSpacing = 8 barcodeHeight = 50 printMode = 0 - defaultHeatTime = 120 + defaultHeatTime = 180 firmwareVersion = 268 writeToStdout = False From 489c9f46156c8c7e74231f6a79c55d1ca20f3159 Mon Sep 17 00:00:00 2001 From: Chris Lindbergh Date: Sat, 26 Dec 2020 20:10:54 -0700 Subject: [PATCH 17/25] Add script to print the date and time --- datetime.py | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100755 datetime.py diff --git a/datetime.py b/datetime.py new file mode 100755 index 0000000..bf854a1 --- /dev/null +++ b/datetime.py @@ -0,0 +1,38 @@ +#!/usr/bin/python3 +# pylint: disable=E1101,W0614 + +import time +from Adafruit_Thermal import * + +i_feed = 4 +f_pause = 1.0 + +printer = Adafruit_Thermal("/dev/serial0", 19200, timeout=5) + +datestamp = time.strftime("%Y-%m-%d", time.gmtime()) +print ("Date in preferred format:", datestamp) +timestamp = time.strftime("%H:%M:%S", time.gmtime()) +print ("Time in preferred format:", timestamp) + +# Give a little room at the top +printer.feed(i_feed) +# Center justify +printer.justify('C') +# Large size +printer.setSize('L') +# Print the date +printer.println(datestamp) +# Print the time +printer.println(timestamp) +# Give a little room at the bottom +printer.feed(i_feed) + +# Tell printer to sleep +printer.sleep() +# Call wake() before printing again, even if reset +printer.wake() +# Restore printer to defaults +printer.setDefault() + +# Sleep for one second in case we're called many times +time.sleep(f_pause) From 43ade2a4b9a19c674818d6fa60cee8be89a1b94b Mon Sep 17 00:00:00 2001 From: Stine An Date: Wed, 31 Mar 2021 20:59:48 -0400 Subject: [PATCH 18/25] Update README.md In the install package list, `python-serial` should be updated to `python3-serial`. Please see https://github.com/adafruit/Python-Thermal-Printer/issues/42. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 71fc87a..5360027 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ Update the system and install prequisities. ``` sudo apt-get update -sudo apt-get install git cups wiringpi build-essential libcups2-dev libcupsimage2-dev python-serial python-pil python-unidecode +sudo apt-get install git cups wiringpi build-essential libcups2-dev libcupsimage2-dev python3-serial python-pil python-unidecode ``` Install the printer driver. Don't worry about the warnings that g++ gives. From 285a06c6d57accd09b85f97dc72ace209351efe6 Mon Sep 17 00:00:00 2001 From: Chris Lindbergh Date: Sat, 3 Jul 2021 12:47:07 -0600 Subject: [PATCH 19/25] Lower feed spacing and switch to ISO8601 time format --- datetime.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/datetime.py b/datetime.py index bf854a1..7dd8061 100755 --- a/datetime.py +++ b/datetime.py @@ -4,14 +4,14 @@ import time from Adafruit_Thermal import * -i_feed = 4 +i_feed = 3 f_pause = 1.0 printer = Adafruit_Thermal("/dev/serial0", 19200, timeout=5) datestamp = time.strftime("%Y-%m-%d", time.gmtime()) print ("Date in preferred format:", datestamp) -timestamp = time.strftime("%H:%M:%S", time.gmtime()) +timestamp = 'T' + time.strftime("%H:%M:%S", time.gmtime()) + 'Z' print ("Time in preferred format:", timestamp) # Give a little room at the top From 1fe39caefe778a599d0a49876252038c5a802499 Mon Sep 17 00:00:00 2001 From: Chris Lindbergh Date: Sun, 26 Dec 2021 23:40:41 -0700 Subject: [PATCH 20/25] Add .dccache to .gitignore for Snyk --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index a348e50..1e5e8c0 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,4 @@ /__pycache__/ + +# for Snyk +.dccache From 066753330bf0919b89afc6dedbb148cfcda3f1a6 Mon Sep 17 00:00:00 2001 From: Chris Lindbergh Date: Sun, 26 Dec 2021 23:42:10 -0700 Subject: [PATCH 21/25] Improve datetime.py script and README.md documentation with comments and tips from linting --- README.md | 12 +++---- __pycache__/Adafruit_Thermal.cpython-37.pyc | Bin 15557 -> 0 bytes datetime.py | 34 ++++++++++++-------- 3 files changed, 27 insertions(+), 19 deletions(-) delete mode 100644 __pycache__/Adafruit_Thermal.cpython-37.pyc diff --git a/README.md b/README.md index 5360027..721ca98 100644 --- a/README.md +++ b/README.md @@ -8,23 +8,23 @@ Install Raspbian Buster and Wire the printer according to [this](https://learn.a Run a test to see if the printer is working by punching in these commands into the terminal. -``` +``` shell stty -F /dev/serial0 19200 echo -e "This is a test.\\n\\n\\n" > /dev/serial0 ``` ### Installing -Update the system and install prequisities. +Update the system and install prerequisites. -``` +``` shell sudo apt-get update sudo apt-get install git cups wiringpi build-essential libcups2-dev libcupsimage2-dev python3-serial python-pil python-unidecode ``` Install the printer driver. Don't worry about the warnings that g++ gives. -``` +``` shell git clone https://github.com/adafruit/zj-58 cd zj-58 make @@ -33,14 +33,14 @@ sudo ./install Make the printer the default printer. This is useful if you are going to be doing other things with it. -``` +``` shell sudo lpadmin -p ZJ-58 -E -v serial:/dev/serial0?baud=19200 -m zjiang/ZJ-58.ppd sudo lpoptions -d ZJ-58 ``` Restart the system. Clone this repository and try to run *printertest.py*. -``` +``` shell git clone https://github.com/galacticfan/Python-Thermal-Printer/ cd Python-Thermal-Printer python3 printertest.py diff --git a/__pycache__/Adafruit_Thermal.cpython-37.pyc b/__pycache__/Adafruit_Thermal.cpython-37.pyc deleted file mode 100644 index b87be734f5439d9da24143dad0d35f3146c1c06f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15557 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 Date: Sat, 15 Jan 2022 19:09:28 -0500 Subject: [PATCH 22/25] Add files via upload --- Adafruit_Thermal.py | 7 +-- forecast.py | 112 ++++++++++++++++++++++---------------------- main.py | 25 ++++++---- sudoku-gfx.py | 36 +++++++------- 4 files changed, 90 insertions(+), 90 deletions(-) diff --git a/Adafruit_Thermal.py b/Adafruit_Thermal.py index 46b0448..551e251 100644 --- a/Adafruit_Thermal.py +++ b/Adafruit_Thermal.py @@ -50,7 +50,7 @@ class Adafruit_Thermal(Serial): lineSpacing = 8 barcodeHeight = 50 printMode = 0 - defaultHeatTime = 180 + defaultHeatTime = 120 firmwareVersion = 268 writeToStdout = False @@ -549,11 +549,8 @@ def printBitmap(self, w, h, bitmap, LaaT=False): # passing the result to this function. def printImage(self, image_file, LaaT=False): from PIL import Image + # image = Image.open(image_file) image = image_file - - if isinstance(image_file, str): - image = Image.open(image_file) - if image.mode != '1': image = image.convert('1') diff --git a/forecast.py b/forecast.py index 3d9da85..6353101 100755 --- a/forecast.py +++ b/forecast.py @@ -1,74 +1,72 @@ -#!/usr/bin/python - -# Weather forecast for Raspberry Pi w/Adafruit Mini Thermal Printer. -# Retrieves data from DarkSky.net's API, prints current conditions and -# forecasts for next two days. See timetemp.py for a different -# weather example using nice bitmaps. -# Written by Adafruit Industries. MIT license. -# -# Required software includes Adafruit_Thermal and PySerial libraries. -# Other libraries used are part of stock Python install. -# -# Resources: -# http://www.adafruit.com/products/597 Mini Thermal Receipt Printer -# http://www.adafruit.com/products/600 Printer starter pack - from __future__ import print_function from Adafruit_Thermal import * from datetime import date -from datetime import datetime import calendar -import urllib, json +import urllib.request +import json -API_KEY = "YOUR_API_KEY" +printer = Adafruit_Thermal("/dev/serial0", 19200, timeout=5) +def getLink(dailyOrHourly): + latitude = "38.8894" #limit to four decimal digits + longitude = "-77.0352" #limit to four decimal digits + mainLink = "https://api.weather.gov/points/" + latitude + "," + longitude + response_main = urllib.request.urlopen(mainLink) + raw_data_main = response_main.read().decode() + data_main = json.loads(raw_data_main) + properties_main = data_main['properties'] + dailyLink = properties_main["forecast"] + hourlyLink = properties_main["forecastHourly"] + if dailyOrHourly == "daily": + return dailyLink + elif dailyOrHourly == "hourly": + return hourlyLink -LAT = "40.726019" -LONG = "-74.00536" +url_daily = getLink("daily") +response_daily = urllib.request.urlopen(url_daily) +# status & reason +# print(response_daily.status, response_daily.reason) -# Dumps one forecast line to the printer -def forecast(idx): +raw_data_daily = response_daily.read().decode() +data_daily = json.loads(raw_data_daily) +forecast_periods_daily = data_daily['properties']['periods'] - date = datetime.fromtimestamp(int(data['daily']['data'][idx]['time'])) - day = calendar.day_name[date.weekday()] - lo = data['daily']['data'][idx]['temperatureMin'] - hi = data['daily']['data'][idx]['temperatureMax'] - cond = data['daily']['data'][idx]['summary'] - printer.print(day + ': low ' + str(lo) ) - printer.print(deg) - printer.print(' high ' + str(hi)) - printer.print(deg) - printer.println(' ' + cond.replace(u'\u2013', '-').encode('utf-8')) # take care of pesky unicode dash +current_period_isDayTime = forecast_periods_daily[0]['isDaytime'] -printer = Adafruit_Thermal("/dev/serial0", 19200, timeout=5) -deg = chr(0xf8) # Degree symbol on thermal printer +if current_period_isDayTime: + day_index = 0 + night_index = 1 +else: + day_index = 1 + night_index = 0 -url = "https://api.darksky.net/forecast/"+API_KEY+"/"+LAT+","+LONG+"?exclude=[alerts,minutely,hourly,flags]&units=us" -response = urllib.urlopen(url) -data = json.loads(response.read()) +day_name = forecast_periods_daily[day_index]['name'] +hi_temp = forecast_periods_daily[day_index]['temperature'] +night_name = forecast_periods_daily[night_index]['name'] +lo_temp = forecast_periods_daily[night_index]['temperature'] +current_detailed_forecast = forecast_periods_daily[0]['detailedForecast'] -# Print heading -printer.inverseOn() -printer.print('{:^32}'.format("DarkSky.Net Forecast")) -printer.inverseOff() - -# Print current conditions -printer.boldOn() -printer.print('{:^32}'.format('Current conditions:')) -printer.boldOff() +url_hourly = getLink("hourly") +response_hourly = urllib.request.urlopen(url_hourly) +# status & reason +#print(response_hourly.status, response_hourly.reason) +raw_data_hourly = response_hourly.read().decode() +data_hourly = json.loads(raw_data_hourly) +forecast_periods_hourly = data_hourly['properties']['periods'] +temperature = forecast_periods_hourly[0]['temperature'] -temp = data['currently']['temperature'] -cond = data['currently']['summary'] -printer.print(temp) -printer.print(deg) -printer.println(' ' + cond) +d = date.today() +week_day = calendar.day_name[date(d.year,d.month,d.day).weekday()] +month_text = calendar.month_name[d.month] +printer.underlineOn() +printer.print("It's " + week_day + ", " + month_text + " " + str(d.day) + "\n") +printer.underlineOff() printer.boldOn() - -# Print forecast -printer.print('{:^32}'.format('Forecast:')) +printer.print(day_name + "'s Forecast \n") printer.boldOff() -forecast(0) -forecast(1) - +printer.print("Current temperature: " + str(temperature) + " F \n") +printer.print("High temperature: " + str(hi_temp) + " F \n") +printer.print("Low temperature: " + str(lo_temp) + " F \n") +printer.print(current_detailed_forecast + "\n") printer.feed(3) diff --git a/main.py b/main.py index 9ebff71..2494677 100755 --- a/main.py +++ b/main.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/python3 # Main script for Adafruit Internet of Things Printer 2. Monitors button # for taps and holds, performs periodic actions (Twitter polling by default) @@ -19,6 +19,8 @@ import subprocess, time, socket from PIL import Image from Adafruit_Thermal import * +from datetime import date +import calendar ledPin = 18 buttonPin = 23 @@ -33,7 +35,7 @@ # Called when button is briefly tapped. Invokes time/temperature script. def tap(): GPIO.output(ledPin, GPIO.HIGH) # LED on while working - subprocess.call(["python", "timetemp.py"]) + subprocess.call(["python3", "forecast.py"]) GPIO.output(ledPin, GPIO.LOW) @@ -51,7 +53,7 @@ def hold(): # Invokes twitter script. def interval(): GPIO.output(ledPin, GPIO.HIGH) - p = subprocess.Popen(["python", "twitter.py", str(lastId)], + p = subprocess.Popen(["python3", "twitter.py", str(lastId)], stdout=subprocess.PIPE) GPIO.output(ledPin, GPIO.LOW) return p.communicate()[0] # Script pipes back lastId, returned to main @@ -61,8 +63,11 @@ def interval(): # Invokes weather forecast and sudoku-gfx scripts. def daily(): GPIO.output(ledPin, GPIO.HIGH) - subprocess.call(["python", "forecast.py"]) - subprocess.call(["python", "sudoku-gfx.py"]) + subprocess.call(["python3", "forecast.py"]) + d = date.today() + weekday = calendar.day_name[date(d.year,d.month,d.day).weekday()] + if weekday == "Saturday" or weekday == "Sunday": + subprocess.call(["python3", "sudoku-gfx.py"]) GPIO.output(ledPin, GPIO.LOW) @@ -159,9 +164,9 @@ def daily(): # Every 30 seconds, run Twitter scripts. 'lastId' is passed around # to preserve state between invocations. Probably simpler to do an # import thing. - if t > nextInterval: - nextInterval = t + 30.0 - result = interval() - if result is not None: - lastId = result.rstrip('\r\n') +# if t > nextInterval: +# nextInterval = t + 30.0 +# result = interval() +# if result is not None: +# lastId = result.rstrip('\r\n') diff --git a/sudoku-gfx.py b/sudoku-gfx.py index 2a7ac12..e955bbf 100755 --- a/sudoku-gfx.py +++ b/sudoku-gfx.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/python3 # # Sudoku Generator and Solver in 250 lines of python # Copyright (c) 2006 David Bau. All rights reserved. @@ -55,14 +55,14 @@ def main(): def makepuzzle(board): puzzle = []; deduced = [None] * 81 - order = random.sample(xrange(81), 81) + order = random.sample(range(81), 81) for pos in order: if deduced[pos] is None: puzzle.append((pos, board[pos])) deduced[pos] = board[pos] deduce(deduced) random.shuffle(puzzle) - for i in xrange(len(puzzle) - 1, -1, -1): + for i in range(len(puzzle) - 1, -1, -1): e = puzzle[i]; del puzzle[i] rating = checkpuzzle(boardforentries(puzzle), board) if rating == -1: puzzle.append(e) @@ -70,7 +70,7 @@ def makepuzzle(board): def ratepuzzle(puzzle, samples): total = 0 - for i in xrange(samples): + for i in range(samples): state, answer = solveboard(puzzle) if answer is None: return -1 total += len(state) @@ -113,7 +113,7 @@ def deduce(board): stuck, guess, count = True, None, 0 # fill in any spots determined by direct conflicts allowed, needed = figurebits(board) - for pos in xrange(81): + for pos in range(81): if None == board[pos]: numbers = listbits(allowed[pos]) if len(numbers) == 0: return [] @@ -122,13 +122,13 @@ def deduce(board): guess, count = pickbetter(guess, count, [(pos, n) for n in numbers]) if not stuck: allowed, needed = figurebits(board) # fill in any spots determined by elimination of other locations - for axis in xrange(3): - for x in xrange(9): + for axis in range(3): + for x in range(9): numbers = listbits(needed[axis * 9 + x]) for n in numbers: bit = 1 << n spots = [] - for y in xrange(9): + for y in range(9): pos = posfor(x, y, axis) if allowed[pos] & bit: spots.append(pos) if len(spots) == 0: return [] @@ -141,11 +141,11 @@ def deduce(board): def figurebits(board): allowed, needed = [e is None and 511 or 0 for e in board], [] - for axis in xrange(3): - for x in xrange(9): + for axis in range(3): + for x in range(9): bits = axismissing(board, x, axis) needed.append(bits) - for y in xrange(9): + for y in range(9): allowed[posfor(x, y, axis)] &= bits return allowed, needed @@ -161,17 +161,17 @@ def axisfor(pos, axis): def axismissing(board, x, axis): bits = 0 - for y in xrange(9): + for y in range(9): e = board[posfor(x, y, axis)] if e is not None: bits |= 1 << e return 511 ^ bits def listbits(bits): - return [y for y in xrange(9) if 0 != bits & 1 << y] + return [y for y in range(9) if 0 != bits & 1 << y] def allowed(board, pos): bits = 511 - for axis in xrange(3): + for axis in range(3): x = axisfor(pos, axis) bits &= axismissing(board, x, axis) return bits @@ -183,7 +183,7 @@ def pickbetter(b, c, t): else: return (b, c + 1) def entriesforboard(board): - return [(pos, board[pos]) for pos in xrange(81) if board[pos] is not None] + return [(pos, board[pos]) for pos in range(81) if board[pos] is not None] def boardforentries(entries): board = [None] * 81 @@ -191,14 +191,14 @@ def boardforentries(entries): return board def boardmatches(b1, b2): - for i in xrange(81): + for i in range(81): if b1[i] != b2[i]: return False return True def printboard(board): bg.paste(img, (0, 0)) # Numbers are cropped off right side - for row in xrange(9): - for col in xrange(9): + for row in range(9): + for col in range(9): n = board[posfor(row, col)] if n is not None: bg.paste(numbers[n], (xcoord[col], ycoord[row])) From b9504fc5760b5994f6147d7c44e8bf8909f02be4 Mon Sep 17 00:00:00 2001 From: Paint Your Dragon Date: Fri, 24 Feb 2023 10:00:06 -0800 Subject: [PATCH 23/25] Update README.md --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 721ca98..bc9d0d0 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,6 @@ +NOTE: not recommended for new designs. The CircuitPython Thermal Printer library is preferred going forward: +https://github.com/adafruit/Adafruit_CircuitPython_Thermal_Printer + # Python-Thermal-Printer Module Python3 port of the original Adafruit [Python-Thermal-Printer](https://github.com/adafruit/Python-Thermal-Printer) library. From 7d81348a2fe5044b403b2d6db701e1222d969520 Mon Sep 17 00:00:00 2001 From: Carter Nelson Date: Tue, 16 May 2023 07:43:00 -0700 Subject: [PATCH 24/25] Update README.md --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index bc9d0d0..3ed3596 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ -NOTE: not recommended for new designs. The CircuitPython Thermal Printer library is preferred going forward: -https://github.com/adafruit/Adafruit_CircuitPython_Thermal_Printer +> --- +> **!!! NOTE !!!** +> **THIS REPOSITORY IS ARCHIVED AND IS NO LONGER SUPPORTED OR MAINTAINED*** +> --- # Python-Thermal-Printer Module From 678e13ae918deadf2b350b3f866a21de6ff1ad6d Mon Sep 17 00:00:00 2001 From: Carter Nelson Date: Tue, 16 May 2023 07:48:29 -0700 Subject: [PATCH 25/25] Update README.md --- README.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 3ed3596..be18e24 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,5 @@ -> --- -> **!!! NOTE !!!** -> **THIS REPOSITORY IS ARCHIVED AND IS NO LONGER SUPPORTED OR MAINTAINED*** -> --- +# !!! NOTE !!! +# THIS REPOSITORY IS ARCHIVED AND IS NO LONGER SUPPORTED OR MAINTAINED # Python-Thermal-Printer Module