Initial commit.

This commit is contained in:
Tony DiCola 2017-12-21 18:02:01 -08:00
commit 2c1f5af4c0
15 changed files with 1616 additions and 0 deletions

8
.gitignore vendored Normal file
View file

@ -0,0 +1,8 @@
__pycache__
_build
*.pyc
.env
build*
bundles
*.mpy
.DS_Store

432
.pylintrc Normal file
View file

@ -0,0 +1,432 @@
[MASTER]
# A comma-separated list of package or module names from where C extensions may
# be loaded. Extensions are loading into the active Python interpreter and may
# run arbitrary code
extension-pkg-whitelist=
# Add files or directories to the blacklist. They should be base names, not
# paths.
ignore=CVS
# Add files or directories matching the regex patterns to the blacklist. The
# regex matches against base names, not paths.
ignore-patterns=
# Python code to execute, usually for sys.path manipulation such as
# pygtk.require().
#init-hook=
# Use multiple processes to speed up Pylint.
# jobs=1
jobs=2
# List of plugins (as comma separated values of python modules names) to load,
# usually to register additional checkers.
load-plugins=
# Pickle collected data for later comparisons.
persistent=yes
# Specify a configuration file.
#rcfile=
# Allow loading of arbitrary C extensions. Extensions are imported into the
# active Python interpreter and may run arbitrary code.
unsafe-load-any-extension=no
[MESSAGES CONTROL]
# Only show warnings with the listed confidence levels. Leave empty to show
# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED
confidence=
# Disable the message, report, category or checker with the given id(s). You
# can either give multiple identifiers separated by comma (,) or put this
# option multiple times (only on the command line, not in the configuration
# file where it should appear only once).You can also use "--disable=all" to
# disable everything first and then reenable specific checks. For example, if
# you want to run only the similarities checker, you can use "--disable=all
# --enable=similarities". If you want to run only the classes checker, but have
# no Warning level messages displayed, use"--disable=all --enable=classes
# --disable=W"
# disable=import-error,print-statement,parameter-unpacking,unpacking-in-except,old-raise-syntax,backtick,long-suffix,old-ne-operator,old-octal-literal,import-star-module-level,raw-checker-failed,bad-inline-option,locally-disabled,locally-enabled,file-ignored,suppressed-message,useless-suppression,deprecated-pragma,apply-builtin,basestring-builtin,buffer-builtin,cmp-builtin,coerce-builtin,execfile-builtin,file-builtin,long-builtin,raw_input-builtin,reduce-builtin,standarderror-builtin,unicode-builtin,xrange-builtin,coerce-method,delslice-method,getslice-method,setslice-method,no-absolute-import,old-division,dict-iter-method,dict-view-method,next-method-called,metaclass-assignment,indexing-exception,raising-string,reload-builtin,oct-method,hex-method,nonzero-method,cmp-method,input-builtin,round-builtin,intern-builtin,unichr-builtin,map-builtin-not-iterating,zip-builtin-not-iterating,range-builtin-not-iterating,filter-builtin-not-iterating,using-cmp-argument,eq-without-hash,div-method,idiv-method,rdiv-method,exception-message-attribute,invalid-str-codec,sys-max-int,bad-python3-import,deprecated-string-function,deprecated-str-translate-call
disable=print-statement,parameter-unpacking,unpacking-in-except,old-raise-syntax,backtick,long-suffix,old-ne-operator,old-octal-literal,import-star-module-level,raw-checker-failed,bad-inline-option,locally-disabled,locally-enabled,file-ignored,suppressed-message,useless-suppression,deprecated-pragma,apply-builtin,basestring-builtin,buffer-builtin,cmp-builtin,coerce-builtin,execfile-builtin,file-builtin,long-builtin,raw_input-builtin,reduce-builtin,standarderror-builtin,unicode-builtin,xrange-builtin,coerce-method,delslice-method,getslice-method,setslice-method,no-absolute-import,old-division,dict-iter-method,dict-view-method,next-method-called,metaclass-assignment,indexing-exception,raising-string,reload-builtin,oct-method,hex-method,nonzero-method,cmp-method,input-builtin,round-builtin,intern-builtin,unichr-builtin,map-builtin-not-iterating,zip-builtin-not-iterating,range-builtin-not-iterating,filter-builtin-not-iterating,using-cmp-argument,eq-without-hash,div-method,idiv-method,rdiv-method,exception-message-attribute,invalid-str-codec,sys-max-int,bad-python3-import,deprecated-string-function,deprecated-str-translate-call,import-error
# Enable the message, report, category or checker with the given id(s). You can
# either give multiple identifier separated by comma (,) or put this option
# multiple time (only on the command line, not in the configuration file where
# it should appear only once). See also the "--disable" option for examples.
enable=
[REPORTS]
# Python expression which should return a note less than 10 (10 is the highest
# note). You have access to the variables errors warning, statement which
# respectively contain the number of errors / warnings messages and the total
# number of statements analyzed. This is used by the global evaluation report
# (RP0004).
evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)
# Template used to display messages. This is a python new-style format string
# used to format the message information. See doc for all details
#msg-template=
# Set the output format. Available formats are text, parseable, colorized, json
# and msvs (visual studio).You can also give a reporter class, eg
# mypackage.mymodule.MyReporterClass.
output-format=text
# Tells whether to display a full report or only the messages
reports=no
# Activate the evaluation score.
score=yes
[REFACTORING]
# Maximum number of nested blocks for function / method body
max-nested-blocks=5
[LOGGING]
# Logging modules to check that the string format arguments are in logging
# function parameter format
logging-modules=logging
[SPELLING]
# Spelling dictionary name. Available dictionaries: none. To make it working
# install python-enchant package.
spelling-dict=
# List of comma separated words that should not be checked.
spelling-ignore-words=
# A path to a file that contains private dictionary; one word per line.
spelling-private-dict-file=
# Tells whether to store unknown words to indicated private dictionary in
# --spelling-private-dict-file option instead of raising a message.
spelling-store-unknown-words=no
[MISCELLANEOUS]
# List of note tags to take in consideration, separated by a comma.
notes=FIXME,XXX,TODO
[TYPECHECK]
# List of decorators that produce context managers, such as
# contextlib.contextmanager. Add to this list to register other decorators that
# produce valid context managers.
contextmanager-decorators=contextlib.contextmanager
# List of members which are set dynamically and missed by pylint inference
# system, and so shouldn't trigger E1101 when accessed. Python regular
# expressions are accepted.
generated-members=
# Tells whether missing members accessed in mixin class should be ignored. A
# mixin class is detected if its name ends with "mixin" (case insensitive).
ignore-mixin-members=yes
# This flag controls whether pylint should warn about no-member and similar
# checks whenever an opaque object is returned when inferring. The inference
# can return multiple potential results while evaluating a Python object, but
# some branches might not be evaluated, which results in partial inference. In
# that case, it might be useful to still emit no-member and other checks for
# the rest of the inferred objects.
ignore-on-opaque-inference=yes
# List of class names for which member attributes should not be checked (useful
# for classes with dynamically set attributes). This supports the use of
# qualified names.
ignored-classes=optparse.Values,thread._local,_thread._local
# List of module names for which member attributes should not be checked
# (useful for modules/projects where namespaces are manipulated during runtime
# and thus existing member attributes cannot be deduced by static analysis. It
# supports qualified module names, as well as Unix pattern matching.
ignored-modules=
# Show a hint with possible names when a member name was not found. The aspect
# of finding the hint is based on edit distance.
missing-member-hint=yes
# The minimum edit distance a name should have in order to be considered a
# similar match for a missing member name.
missing-member-hint-distance=1
# The total number of similar names that should be taken in consideration when
# showing a hint for a missing member.
missing-member-max-choices=1
[VARIABLES]
# List of additional names supposed to be defined in builtins. Remember that
# you should avoid to define new builtins when possible.
additional-builtins=
# Tells whether unused global variables should be treated as a violation.
allow-global-unused-variables=yes
# List of strings which can identify a callback function by name. A callback
# name must start or end with one of those strings.
callbacks=cb_,_cb
# A regular expression matching the name of dummy variables (i.e. expectedly
# not used).
dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_
# Argument names that match this expression will be ignored. Default to name
# with leading underscore
ignored-argument-names=_.*|^ignored_|^unused_
# Tells whether we should check for unused import in __init__ files.
init-import=no
# List of qualified module names which can have objects that can redefine
# builtins.
redefining-builtins-modules=six.moves,future.builtins
[FORMAT]
# Expected format of line ending, e.g. empty (any line ending), LF or CRLF.
# expected-line-ending-format=
expected-line-ending-format=LF
# Regexp for a line that is allowed to be longer than the limit.
ignore-long-lines=^\s*(# )?<?https?://\S+>?$
# Number of spaces of indent required inside a hanging or continued line.
indent-after-paren=4
# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1
# tab).
indent-string=' '
# Maximum number of characters on a single line.
max-line-length=100
# Maximum number of lines in a module
max-module-lines=1000
# List of optional constructs for which whitespace checking is disabled. `dict-
# separator` is used to allow tabulation in dicts, etc.: {1 : 1,\n222: 2}.
# `trailing-comma` allows a space between comma and closing bracket: (a, ).
# `empty-line` allows space-only lines.
no-space-check=trailing-comma,dict-separator
# Allow the body of a class to be on the same line as the declaration if body
# contains single statement.
single-line-class-stmt=no
# Allow the body of an if to be on the same line as the test if there is no
# else.
single-line-if-stmt=no
[SIMILARITIES]
# Ignore comments when computing similarities.
ignore-comments=yes
# Ignore docstrings when computing similarities.
ignore-docstrings=yes
# Ignore imports when computing similarities.
ignore-imports=no
# Minimum lines number of a similarity.
min-similarity-lines=4
[BASIC]
# Naming hint for argument names
argument-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$
# Regular expression matching correct argument names
argument-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$
# Naming hint for attribute names
attr-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$
# Regular expression matching correct attribute names
attr-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$
# Bad variable names which should always be refused, separated by a comma
bad-names=foo,bar,baz,toto,tutu,tata
# Naming hint for class attribute names
class-attribute-name-hint=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$
# Regular expression matching correct class attribute names
class-attribute-rgx=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$
# Naming hint for class names
# class-name-hint=[A-Z_][a-zA-Z0-9]+$
class-name-hint=[A-Z_][a-zA-Z0-9_]+$
# Regular expression matching correct class names
# class-rgx=[A-Z_][a-zA-Z0-9]+$
class-rgx=[A-Z_][a-zA-Z0-9_]+$
# Naming hint for constant names
const-name-hint=(([A-Z_][A-Z0-9_]*)|(__.*__))$
# Regular expression matching correct constant names
const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$
# Minimum line length for functions/classes that require docstrings, shorter
# ones are exempt.
docstring-min-length=-1
# Naming hint for function names
function-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$
# Regular expression matching correct function names
function-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$
# Good variable names which should always be accepted, separated by a comma
# good-names=i,j,k,ex,Run,_
good-names=r,g,b,w,i,j,k,n,x,y,z,ex,ok,Run,_
# Include a hint for the correct naming format with invalid-name
include-naming-hint=no
# Naming hint for inline iteration names
inlinevar-name-hint=[A-Za-z_][A-Za-z0-9_]*$
# Regular expression matching correct inline iteration names
inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$
# Naming hint for method names
method-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$
# Regular expression matching correct method names
method-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$
# Naming hint for module names
module-name-hint=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$
# Regular expression matching correct module names
module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$
# Colon-delimited sets of names that determine each other's naming style when
# the name regexes allow several styles.
name-group=
# Regular expression which should only match function or class names that do
# not require a docstring.
no-docstring-rgx=^_
# List of decorators that produce properties, such as abc.abstractproperty. Add
# to this list to register other decorators that produce valid properties.
property-classes=abc.abstractproperty
# Naming hint for variable names
variable-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$
# Regular expression matching correct variable names
variable-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$
[IMPORTS]
# Allow wildcard imports from modules that define __all__.
allow-wildcard-with-all=no
# Analyse import fallback blocks. This can be used to support both Python 2 and
# 3 compatible code, which means that the block might have code that exists
# only in one or another interpreter, leading to false positives when analysed.
analyse-fallback-blocks=no
# Deprecated modules which should not be used, separated by a comma
deprecated-modules=optparse,tkinter.tix
# Create a graph of external dependencies in the given file (report RP0402 must
# not be disabled)
ext-import-graph=
# Create a graph of every (i.e. internal and external) dependencies in the
# given file (report RP0402 must not be disabled)
import-graph=
# Create a graph of internal dependencies in the given file (report RP0402 must
# not be disabled)
int-import-graph=
# Force import order to recognize a module as part of the standard
# compatibility libraries.
known-standard-library=
# Force import order to recognize a module as part of a third party library.
known-third-party=enchant
[CLASSES]
# List of method names used to declare (i.e. assign) instance attributes.
defining-attr-methods=__init__,__new__,setUp
# List of member names, which should be excluded from the protected access
# warning.
exclude-protected=_asdict,_fields,_replace,_source,_make
# List of valid names for the first argument in a class method.
valid-classmethod-first-arg=cls
# List of valid names for the first argument in a metaclass class method.
valid-metaclass-classmethod-first-arg=mcs
[DESIGN]
# Maximum number of arguments for function / method
max-args=5
# Maximum number of attributes for a class (see R0902).
# max-attributes=7
max-attributes=11
# Maximum number of boolean expressions in a if statement
max-bool-expr=5
# Maximum number of branch for function / method body
max-branches=12
# Maximum number of locals for function / method body
max-locals=15
# Maximum number of parents for a class (see R0901).
max-parents=7
# Maximum number of public methods for a class (see R0904).
max-public-methods=20
# Maximum number of return / yield for function / method body
max-returns=6
# Maximum number of statements in function / method body
max-statements=50
# Minimum number of public methods for a class (see R0903).
min-public-methods=1
[EXCEPTIONS]
# Exceptions that will emit a warning when being caught. Defaults to
# "Exception"
overgeneral-exceptions=Exception

30
.travis.yml Normal file
View file

@ -0,0 +1,30 @@
# This is a common .travis.yml for generating library release zip files for
# CircuitPython library releases using circuitpython-build-tools.
# See https://github.com/adafruit/circuitpython-build-tools for detailed setup
# instructions.
dist: trusty
sudo: false
language: python
python:
- "3.6"
cache:
pip: true
deploy:
provider: releases
api_key: $GITHUB_TOKEN
file_glob: true
file: bundles/*
skip_cleanup: true
on:
tags: true
install:
- pip install pylint circuitpython-build-tools
script:
- pylint thermal_printer.py
- ([[ ! -d "examples" ]] || pylint --disable=missing-docstring,invalid-name examples/*.py)
- circuitpython-build-bundles --filename_prefix adafruit-circuitpython-thermal-printer --library_location .

74
CODE_OF_CONDUCT.md Normal file
View file

@ -0,0 +1,74 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, gender identity and expression, level of experience,
nationality, personal appearance, race, religion, or sexual identity and
orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at support@adafruit.com. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at [http://contributor-covenant.org/version/1/4][version]
[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/

21
LICENSE Normal file
View file

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2017 Tony DiCola for Adafruit Industries
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

69
README.rst Normal file
View file

@ -0,0 +1,69 @@
Introduction
============
.. image:: https://readthedocs.org/projects/adafruit_circuitpython_thermal_printer/badge/?version=latest
:target: https://adafruit_circuitpython_thermal_printer.readthedocs.io/
:alt: Documentation Status
.. image :: https://img.shields.io/discord/327254708534116352.svg
:target: https://discord.gg/nBQh6qu
:alt: Discord
TODO
Dependencies
=============
This driver depends on:
* `Adafruit CircuitPython <https://github.com/adafruit/circuitpython>`_
Please ensure all dependencies are available on the CircuitPython filesystem.
This is easily achieved by downloading
`the Adafruit library and driver bundle <https://github.com/adafruit/Adafruit_CircuitPython_Bundle>`_.
Usage Example
=============
See examples/simpletest.py for a demo of basic printer usage.
API Reference
=============
.. toctree::
:maxdepth: 2
api
Contributing
============
Contributions are welcome! Please read our `Code of Conduct
<https://github.com/adafruit/Adafruit_CircuitPython_Thermal_Printer/blob/master/CODE_OF_CONDUCT.md>`_
before contributing to help this project stay welcoming.
Building locally
================
To build this library locally you'll need to install the
`circuitpython-build-tools <https://github.com/adafruit/circuitpython-build-tools>`_ package.
.. code-block:: shell
python3 -m venv .env
source .env/bin/activate
pip install circuitpython-build-tools
Once installed, make sure you are in the virtual environment:
.. code-block:: shell
source .env/bin/activate
Then run the build:
.. code-block:: shell
circuitpython-build-bundles --filename_prefix adafruit_circuitpython_thermal_printer --library_location .

View file

View file

@ -0,0 +1,528 @@
# The MIT License (MIT)
#
# Copyright (c) 2017 Tony DiCola
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
"""
`adafruit_thermal_printer`
====================================================
Thermal printer control module built to work with small serial thermal
receipt printers. Note that these printers have many different firmware
versions and care must be taken to select the appropriate module inside this
package for your firmware printer:
- thermal_printer = The latest printers with firmware version 2.68+
- thermal_printer_264 = Printers with firmware version 2.64 up to 2.68.
- thermal_printer_legacy = Printers with firmware version before 2.64.
* Author(s): Tony DiCola
"""
import time
from micropython import const
__version__ = "0.0.0-auto.0"
__repo__ = "https://github.com/adafruit/CircuitPython_thermal_printer.git"
# pylint: disable=bad-whitespace
# Internally used constants.
_UPDOWN_MASK = const(1 << 2)
_BOLD_MASK = const(1 << 3)
_DOUBLE_HEIGHT_MASK = const(1 << 4)
_DOUBLE_WIDTH_MASK = const(1 << 5)
_STRIKE_MASK = const(1 << 6)
# pylint: enable=bad-whitespace
# Disable too many instance members warning. This is not something pylint can
# reasonably infer--the complexity of instance variables is required for proper
# printer function. Disable this warning.
# pylint: disable=too-many-instance-attributes
# Disable too many public members warning. Again this is not something pylint
# can reasonably decide. Thermal printers require lots of control functions.
# Disable this warning.
# pylint: disable=too-many-public-methods
# Thermal printer class for printers with firmware version 2.68 and higher.
# Do not modify this class without fully understanding its coupling to the
# legacy and 2.64+ version printer which inherit from it. These legacy printer
# classes override specific functions which have different requirements of
# behavior between different versions of printer firmware. Firmware printers
# vary _greatly_ in their command set--there is not a clean abstraction. The
# assumption here is that this class is the master with logic for the most
# recent (2.68+) firmware printers. Older firmware versions inherit and
# override behavior where necessary. It is highly, HIGHLY recommended you
# carefully study the Arduino thermal printer library code and fully
# understand all the firmware differences (notice where the library changes
# behavior with the firmware version define):
# https://github.com/adafruit/Adafruit-Thermal-Printer-Library
# Bottom line: don't touch this code without understanding the big picture or
# else it will be very easy to break or introduce subtle incompatibilities with
# older firmware printers.
class ThermalPrinter:
"""Thermal printer for printers with firmware version 2.68 or higher."""
# pylint: disable=bad-whitespace
# Barcode types. These vary based on the firmware version so are made
# as class-level variables that users can reference (i.e.
# ThermalPrinter.UPC_A, etc) and write code that is independent of the
# printer firmware version.
UPC_A = 65
UPC_E = 66
EAN13 = 67
EAN8 = 68
CODE39 = 69
ITF = 70
CODABAR = 71
CODE93 = 72
CODE128 = 73
# pylint: enable=bad-whitespace
class _PrintModeBit:
# Internal descriptor class to simplify printer mode change properties.
# This is tightly coupled to the ThermalPrinter implementation--do not
# change it without fully understanding these dependencies on the
# internal _set_print_mode and other methods!
# pylint doesn't have the context to realize this internal class is
# explicitly tightly coupled to the parent class implementation.
# Therefore disable its warnings about protected access--this access
# is required and by design.
# pylint: disable=protected-access
# Another odd pylint case, it seems to not realize this is a descriptor
# which by design only implements get, set, init. As a result workaround
# this pyline issue by disabling the warning.
# pylint: disable=too-few-public-methods
def __init__(self, mask):
self._mask = mask
def __get__(self, obj, objtype):
return obj._print_mode & self._mask > 0
def __set__(self, obj, val):
if val:
obj._set_print_mode(self._mask)
else:
obj._unset_print_mode(self._mask)
# pylint: enable=protected-access
# pylint: enable=too-few-public-methods
def __init__(self, uart, byte_delay_s=0.00057346, dot_feed_s=0.0021,
dot_print_s=0.03):
"""Thermal printer class. Requires a serial UART connection with at
least the TX pin connected. Take care connecting RX as the printer
will output a 5V signal which can damage boards! If RX is unconnected
the only loss in functionality is the has_paper function, all other
printer functions will continue to work. The byte_delay_s, dot_feed_s,
and dot_print_s values are delays which are used to prevent overloading
the printer with data. Use the default delays unless you fully
understand the workings of the printer and how delays, baud rate,
number of dots, heat time, etc. relate to each other.
"""
self.max_chunk_height = 255
self._resume = 0
self._uart = uart
self._print_mode = 0
self._column = 0
self._max_column = 32
self._char_height = 24
self._line_spacing = 6
self._barcode_height = 50
# pylint: disable=line-too-long
# Byte delay calculated based on assumption of 19200 baud.
# From Arduino library code, see formula here:
# https://github.com/adafruit/Adafruit-Thermal-Printer-Library/blob/master/Adafruit_Thermal.cpp#L50-L53
# pylint: enable=line-too-long
self._byte_delay_s = byte_delay_s
self._dot_feed_s = dot_feed_s
self._dot_print_s = dot_print_s
self.reset()
def _set_timeout(self, period_s):
# Set a timeout before future commands can be sent.
self._resume = time.monotonic() + period_s
def _wait_timeout(self):
# Ensure the timeout that was previously set has passed (will busy wait).
while time.monotonic() < self._resume:
pass
def _write_char(self, char):
# Write a single character to the printer.
if char == '\r':
return # Strip carriage returns by skipping them.
self._wait_timeout()
self._uart.write(char)
delay = self._byte_delay_s
# Add extra delay for newlines or moving past the last column.
if char == '\n' or self._column == self._max_column:
if self._column == 0:
# Feed line delay
delay += ((self._char_height + self._line_spacing) * \
self._dot_feed_s)
else:
# Text line delay
delay += ((self._char_height * self._dot_print_s) + \
(self._line_spacing * self._dot_feed_s))
self._column = 0
else:
self._column += 1
self._set_timeout(delay)
def _write_print_mode(self):
# Write the printer mode to the printer.
self.send_command('\x1B!{0}'.format(chr(self._print_mode))) # ESC + '!' + print mode byte
# Adjust character height and column count based on print mode.
self._char_height = 48 if self._print_mode & _DOUBLE_HEIGHT_MASK else 24
self._max_column = 16 if self._print_mode & _DOUBLE_WIDTH_MASK else 32
def _set_print_mode(self, mask):
# Enable the specified bits of the print mode.
self._print_mode |= (mask & 0xFF)
self._write_print_mode()
def _unset_print_mode(self, mask):
# Disable the specified bits of the print mode.
self._print_mode &= ~(mask & 0xFF)
self._write_print_mode()
def send_command(self, command):
"""Send a command string to the printer."""
for char in command:
self._write_char(char)
# Do initialization in begin instead of the initializer because this
# initialization takes a long time (5 seconds) and shouldn't happen during
# object creation (users need explicit control of when to start it).
def begin(self, heat_time=120):
"""Initialize the printer. Can specify an optional heat_time keyword
to override the default heating timing of 1.2 ms. See the datasheet
for details on the heating time value (duration in 10uS increments).
Note that calling this function will take about half a second for the
printer to intialize and warm up.
"""
assert 0 <= heat_time <= 255
self._set_timeout(0.5) # Half second delay for printer to initialize.
self.wake()
self.reset()
# ESC 7 n1 n2 n3 Setting Control Parameter Command
# n1 = "max heating dots" 0-255 -- max number of thermal print head
# elements that will fire simultaneously. Units = 8 dots (minus 1).
# Printer default is 7 (64 dots, or 1/6 of 384-dot width), this code
# sets it to 11 (96 dots, or 1/4 of width).
# n2 = "heating time" 3-255 -- duration that heating dots are fired.
# Units = 10 us. Printer default is 80 (800 us), this code sets it
# to value passed (default 120, or 1.2 ms -- a little longer than
# the default because we've increased the max heating dots).
# n3 = "heating interval" 0-255 -- recovery time between groups of
# heating dots on line; possibly a function of power supply.
# Units = 10 us. Printer default is 2 (20 us), this code sets it
# to 40 (throttled back due to 2A supply).
# More heating dots = more peak current, but faster printing speed.
# More heating time = darker print, but slower printing speed and
# possibly paper 'stiction'. More heating interval = clearer print,
# but slower printing speed.
self.send_command('\x1B7') # ESC + '7' (print settings)
# Send heating dots, heat time, heat interval.
self.send_command('\x0B{0}\x28'.format(chr(heat_time)))
# Print density description from manual:
# DC2 # n Set printing density
# 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.
print_density = 10 # 100% (? can go higher, text is darker but fuzzy)
print_break_time = 2 # 500 uS
dc2_value = (print_break_time << 5) | print_density
self.send_command('\x12#{0}'.format(chr(dc2_value))) # DC2 + '#' + value
def reset(self):
"""Reset the printer."""
# Issue a reset command to the printer. (ESC + @)
self.send_command('\x1B@')
# Reset internal state:
self._column = 0
self._max_column = 32
self._char_height = 24
self._line_spacing = 6
def print(self, text, end='\n'):
"""Print a line of text. Optionally specify the end keyword to
override the new line printed after the text (set to None to disable
the new line entirely).
"""
self.send_command(text)
if end is not None:
self.send_command(end)
def print_barcode(self, text, barcode_type):
"""Print a barcode with the specified text/number (the meaning
varies based on the type of barcode) and type. Type is a value from
the datasheet or class-level variables like UPC_A, etc. for
convenience. Note the type value changes depending on the firmware
version so use class-level values where possible!
"""
assert 0 <= barcode_type <= 255
assert 0 <= len(text) <= 255
self.feed(1) # Recent firmware can't print barcode w/o feed first???
self.send_command('\x1DH\x02') # Print label below barcode
self.send_command('\x1Dw\x03') # Barcode width 3 (0.375/1.0mm thin/thick)
self.send_command('\x1Dk{0}'.format(chr(barcode_type))) # Barcode type
# Write length and then string (note this only works with 2.64+).
self.send_command(chr(len(text)))
self.send_command(text)
self._set_timeout((self._barcode_height + 40) * self._dot_print_s)
self._column = 0
def print_bitmap(self, width, height, data):
"""Print a bitmap image of the specified width, height and data bytes.
Data bytes must be in 1-bit per pixel format, i.e. each byte represents
8 pixels of image data along a row of the image. You will want to
pre-process your images with a script, you CANNOT send .jpg/.bmp/etc.
image formats. See this Processing sketch for preprocessing:
https://github.com/adafruit/Adafruit-Thermal-Printer-Library/blob/master/processing/bitmapImageConvert/bitmapImageConvert.pde
"""
assert len(data) >= ((width * height) // 8)
row_bytes = (width + 7) // 8 # Round up to next byte boundary.
row_bytes_clipped = min(row_bytes, 48) # 384 pixels max width.
chunk_height_limit = 256 // row_bytes_clipped
# Clip chunk height within the 1 to max range.
chunk_height_limit = max(1, min(self.max_chunk_height, chunk_height_limit))
i = 0
for row_start in range(0, height, chunk_height_limit):
# Issue up to chunkHeightLimit rows at a time.
chunk_height = min(height - row_start, chunk_height_limit)
self.send_command('\x12*{0}{1}'.format(chr(chunk_height),
chr(row_bytes_clipped)))
for _ in range(chunk_height):
for _ in range(row_bytes_clipped):
# Drop down to low level UART access to avoid newline and
# other bitmap values being misinterpreted.
self._wait_timeout()
self._uart.write(chr(data[i]))
i += 1
i += row_bytes - row_bytes_clipped
self._set_timeout(chunk_height * self._dot_print_s)
self._column = 0
def test_page(self):
"""Print a test page."""
self.send_command('\x12T') # DC2 + 'T' for test page
# Delay for 26 lines w/text (ea. 24 dots high) +
# 26 text lines (feed 6 dots) + blank line
self._set_timeout(self._dot_print_s * 24 * 26 + \
self._dot_feed_s * (6 * 26 + 30))
def set_defaults(self):
"""Set default printing and text options. This is useful to reset back
to a good state after printing different size, weight, etc. text.
"""
self.online()
self.justify_left()
self.set_size_small()
self.underline_off()
self.inverse = False
self.upside_down = False
self.double_height = False
self.double_width = False
self.strike = False
self.bold = False
self.set_line_height(30)
self.set_barcode_height(50)
self.set_charset()
self.set_code_page()
def justify_left(self):
"""Set left justification of text."""
self.send_command('\x1Ba\x00') # ESC + 'a' + 0
def justify_center(self):
"""Set center justification of text."""
self.send_command('\x1Ba\x01') # ESC + 'a' + 1
def justify_right(self):
"""Set right justification of text."""
self.send_command('\x1Ba\x02') # ESC + 'a' + 2
def set_size_small(self):
"""Set small text size."""
self._char_height = 24
self._max_column = 32
self.send_command('\x1D!\x00') # ASCII GS + '!' + 0x00
self._column = 0
def set_size_medium(self):
"""Set medium (double height) text size."""
self._char_height = 48
self._max_column = 32
self.send_command('\x1D!\x01') # ASCII GS + '!' + 0x01
self._column = 0
def set_size_large(self):
"""Set large (double height & width) text size."""
self._char_height = 48
self._max_column = 16
self.send_command('\x1D!\x11') # ASCII GS + '!' + 0x11
self._column = 0
def underline_off(self):
"""Turn off underline printing."""
self.send_command('\x1B-\x00') # ESC + '-' + 0
def underline_thin(self):
"""Turn on normal/thin underline printing."""
self.send_command('\x1B-\x01') # ESC + '-' + 1
def underline_thick(self):
"""Turn on thick underline printing."""
self.send_command('\x1B-\x02') # ESC + '-' + 2
def _set_inverse(self, inverse):
# Set the inverse printing state to enabled disabled with the specified
# boolean value. This requires printer firmare 2.68+
if inverse:
self.send_command('\x1DB\x01') # ESC + 'B' + 1
else:
self.send_command('\x1DB\x00') # ESC + 'B' + 0
# pylint: disable=line-too-long
# Write-only property, can't assume we can read inverse state from the
# printer since there is no command for it and hooking up RX is discouraged
# (5V will damage many boards).
inverse = property(None, _set_inverse, None, "Set the inverse printing mode boolean to enable or disable inverse printing.")
# pylint: enable=line-too-long
upside_down = _PrintModeBit(_UPDOWN_MASK)
double_height = _PrintModeBit(_DOUBLE_HEIGHT_MASK)
double_width = _PrintModeBit(_DOUBLE_WIDTH_MASK)
strike = _PrintModeBit(_STRIKE_MASK)
bold = _PrintModeBit(_BOLD_MASK)
def feed(self, lines):
"""Advance paper by specified number of blank lines."""
assert 0 <= lines <= 255
self.send_command('\x1Bd{0}'.format(chr(lines)))
self._set_timeout(self._dot_feed_s * self._char_height)
self._column = 0
def feed_rows(self, rows):
"""Advance paper by specified number of pixel rows."""
assert 0 <= rows <= 255
self.send_command('\x1BJ{0}'.format(chr(rows)))
self._set_timeout(rows * self._dot_feed_s)
self._column = 0
def flush(self):
"""Flush data pending in the printer."""
self.send_command('\f')
def offline(self):
"""Put the printer into an offline state. No other commands can be
sent until an online call is made.
"""
self.send_command('\x1B=\x00') # ESC + '=' + 0
def online(self):
"""Put the printer into an online state after previously put offline.
"""
self.send_command('\x1B=\x01') # ESC + '=' + 1
def sleep_after(self, seconds):
"""Put the printer into sleep mode after the specified number of
seconds (0-65535). Must call wake to wake up and send commands
afterwards!
"""
assert 0 <= seconds <= 65535
low_byte = seconds & 0xFF
high_byte = (seconds >> 8) & 0xFF
self.send_command('\x1B8{0}{1}'.format(chr(low_byte), chr(high_byte)))
def wake(self):
"""Wake the thermal printer into an online state ready to receive
commands.
"""
self.send_command('\xFF') # Wake command.
time.sleep(0.050)
self.send_command('\x1B8\x00\x00') # Sleep off
def has_paper(self):
"""Return a boolean indicating if the printer has paper. You MUST have
the serial RX line hooked up for this to work. NOTE: be VERY CAREFUL
to ensure your board can handle a 5V serial input before hooking up
the RX line!
"""
# This only works with firmware 2.64+:
self.send_command('\x1Bv\x00') # ESC + 'v' + 0
status = self._uart.read(1)
if status is None:
return False
return not status[0] & 0b00000100
def set_line_height(self, height):
"""Set the line height in pixels. This is the total amount of space
between lines, including the height of text. The smallest value is 24
and the largest is 255.
"""
assert 24 <= height <= 255
self._line_spacing = height - 24
self.send_command('\x1B3{0}'.format(chr(height))) # ESC + '3' + height
def set_barcode_height(self, height):
"""Set the barcode height in pixels. Must be a value 1 - 255."""
assert 1 <= height <= 255
self._barcode_height = height
self.send_command('\x1Dh{0}'.format(chr(height))) # ASCII GS + 'h' + height
def set_charset(self, charset=0):
"""Alters the character set for ASCII characters 0x23-0x7E. See
datasheet for details on character set values (0-15). Note this is only
supported on more recent firmware printers!
"""
assert 0 <= charset <= 15
self.send_command('\x1BR{0}'.format(chr(charset))) # ESC + 'R' + charset
def set_code_page(self, code_page=0):
"""Select alternate code page for upper ASCII symbols 0x80-0xFF. See
datasheet for code page values (0 - 47). Note this is only supported
on more recent firmware printers!
"""
assert 0 <= code_page <= 47
self.send_command('\x1Bt{0}'.format(chr(code_page))) # ESC + 't' + code page
def set_char_spacing(self, spacing=0):
"""Set the character spacing (in pixels). Note this is only supported
on more recent firmware printers!"""
assert 0 <= spacing <= 255
self.send_command('\x1B {0}'.format(chr(spacing))) # ESC + ' ' + spacing
def tab(self):
"""Print a tab (i.e. move to next 4 character block). Note this is
only supported on more recent firmware printers!"""
self.send_command('\t')
# Increment to the next position that's every 4 spaces.
# I.e. increment by 4 and go to the floor/first position of the block.
self._column = (self._column + 4) & 0b11111100

View file

@ -0,0 +1,89 @@
# The MIT License (MIT)
#
# Copyright (c) 2017 Tony DiCola
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
"""
`adafruit_thermal_printer_264`
====================================================
Thermal printer control module built to work with small serial thermal
receipt printers. Note that these printers have many different firmware
versions and care must be taken to select the appropriate module inside this
package for your firmware printer:
- thermal_printer = The latest printers with firmware version 2.68+
- thermal_printer_264 = Printers with firmware version 2.64 up to 2.68.
- thermal_printer_legacy = Printers with firmware version before 2.64.
* Author(s): Tony DiCola
"""
from micropython import const
import adafruit_thermal_printer.thermal_printer as thermal_printer
# pylint: disable=bad-whitespace
# Internally used constants.
_INVERSE_MASK = const(1 << 1) # Not in 2.6.8 firmware
# pylint: enable=bad-whitespace
# Legacy behavior class for printers with firmware 2.64 up to 2.68.
# See the comments in thermal_printer.py to understand how this class overrides
# methods which change for older firmware printers!
class ThermalPrinter(thermal_printer.ThermalPrinter):
"""Thermal printer for printers with firmware version 2.64 up to (but
NOT including) 2.68.
"""
# pylint: disable=bad-whitespace
# Barcode types. These vary based on the firmware version so are made
# as class-level variables that users can reference (i.e.
# ThermalPrinter.UPC_A, etc) and write code that is independent of the
# printer firmware version.
UPC_A = 65
UPC_E = 66
EAN13 = 67
EAN8 = 68
CODE39 = 69
ITF = 70
CODABAR = 71
CODE93 = 72
CODE128 = 73
# pylint: enable=bad-whitespace
def __init__(self, uart, byte_delay_s=0.00057346, dot_feed_s=0.0021,
dot_print_s=0.03):
"""Thermal printer class. Requires a serial UART connection with at
least the TX pin connected. Take care connecting RX as the printer
will output a 5V signal which can damage boards! If RX is unconnected
the only loss in functionality is the has_paper function, all other
printer functions will continue to work. The byte_delay_s, dot_feed_s,
and dot_print_s values are delays which are used to prevent overloading
the printer with data. Use the default delays unless you fully
understand the workings of the printer and how delays, baud rate,
number of dots, heat time, etc. relate to each other.
"""
super().__init__(uart, byte_delay_s=byte_delay_s, dot_feed_s=dot_feed_s,
dot_print_s=dot_print_s)
# Inverse on older printers (pre 2.68) uses a print mode bit instead of
# specific commands.
inverse = thermal_printer.ThermalPrinter._PrintModeBit(_INVERSE_MASK)

View file

@ -0,0 +1,161 @@
# The MIT License (MIT)
#
# Copyright (c) 2017 Tony DiCola
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
"""
`adafruit_thermal_printer_legacy`
====================================================
Thermal printer control module built to work with small serial thermal
receipt printers. Note that these printers have many different firmware
versions and care must be taken to select the appropriate module inside this
package for your firmware printer:
- thermal_printer = The latest printers with firmware version 2.68+
- thermal_printer_264 = Printers with firmware version 2.64 up to 2.68.
- thermal_printer_legacy = Printers with firmware version before 2.64.
* Author(s): Tony DiCola
"""
from micropython import const
import adafruit_thermal_printer.thermal_printer as thermal_printer
# pylint: disable=bad-whitespace
# Internally used constants.
_INVERSE_MASK = const(1 << 1) # Not in 2.6.8 firmware
# pylint: enable=bad-whitespace
# Legacy behavior class for printers with firmware before 2.64.
# See the comments in thermal_printer.py to understand how this class overrides
# methods which change for older firmware printers!
class ThermalPrinter(thermal_printer.ThermalPrinter):
"""Thermal printer for printers with firmware version before 2.64."""
# pylint: disable=bad-whitespace
# Barcode types. These vary based on the firmware version so are made
# as class-level variables that users can reference (i.e.
# ThermalPrinter.UPC_A, etc) and write code that is independent of the
# printer firmware version.
UPC_A = 0
UPC_E = 1
EAN13 = 2
EAN8 = 3
CODE39 = 4
I25 = 5
CODEBAR = 6
CODE93 = 7
CODE128 = 8
CODE11 = 9
MSI = 10
# pylint: enable=bad-whitespace
def __init__(self, uart, byte_delay_s=0.00057346, dot_feed_s=0.0021,
dot_print_s=0.03):
"""Thermal printer class. Requires a serial UART connection with at
least the TX pin connected. Take care connecting RX as the printer
will output a 5V signal which can damage boards! If RX is unconnected
the only loss in functionality is the has_paper function, all other
printer functions will continue to work. The byte_delay_s, dot_feed_s,
and dot_print_s values are delays which are used to prevent overloading
the printer with data. Use the default delays unless you fully
understand the workings of the printer and how delays, baud rate,
number of dots, heat time, etc. relate to each other.
"""
super().__init__(uart, byte_delay_s=byte_delay_s, dot_feed_s=dot_feed_s,
dot_print_s=dot_print_s)
def print_barcode(self, text, barcode_type):
"""Print a barcode with the specified text/number (the meaning
varies based on the type of barcode) and type. Type is a value from
the datasheet or class-level variables like UPC_A, etc. for
convenience. Note the type value changes depending on the firmware
version so use class-level values where possible!
"""
assert 0 <= barcode_type <= 255
assert 0 <= len(text) <= 255
self.feed(1) # Recent firmware can't print barcode w/o feed first???
self.send_command('\x1DH\x02') # Print label below barcode
self.send_command('\x1Dw\x03') # Barcode width 3 (0.375/1.0mm thin/thick)
self.send_command('\x1Dk{0}'.format(chr(barcode_type))) # Barcode type
# Pre-2.64 firmware prints the text and then a null character to end.
# Instead of the length of text as a prefix.
self.send_command(text)
self.send_command('\x00')
self._set_timeout((self._barcode_height + 40) * self._dot_print_s)
self._column = 0
def wake(self):
"""Wake the thermal printer into an online state ready to receive
commands.
"""
self.send_command('\xFF') # Wake command.
# Datasheet recommends a 50 mS delay before issuing further commands,
# but in practice this alone isn't sufficient (e.g. text size/style
# commands may still be misinterpreted on wake). A slightly longer
# delay, interspersed with NUL chars (no-ops) seems to help.
for _ in range(10):
self.send_command('\x00')
self._set_timeout(0.01)
def sleep_after(self, seconds):
"""Put the printer into sleep mode after the specified number of
seconds (0-255). Must call wake to wake up and send commands
afterwards!
"""
# Firmware before 2.64 uses an 8-bit number of seconds instead of 16-bit.
assert 0 <= seconds <= 255
self.send_command('\x1B8{0}'.format(chr(seconds)))
def reset(self):
"""Reset the printer."""
# Issue a reset command to the printer. (ESC + @)
self.send_command('\x1B@')
# Reset internal state:
self._column = 0
self._max_column = 32
self._char_height = 24
self._line_spacing = 6
# Skip tab configuration on older printers.
def feed(self, lines):
"""Advance paper by specified number of blank lines."""
# Just send line feeds for older printers.
for _ in range(lines):
self._write_char('\n')
def has_paper(self):
"""Return a boolean indicating if the printer has paper. You MUST have
the serial RX line hooked up for this to work. NOTE: be VERY CAREFUL
to ensure your board can handle a 5V serial input before hooking up
the RX line!
"""
# The paper check command is different for older firmware:
self.send_command('\x1Br\x00') # ESC + 'r' + 0
status = self._uart.read(1)
if status is None:
return False
return not status[0] & 0b00000100
# Inverse on older printers (pre 2.68) uses a print mode bit instead of
# specific commands.
inverse = thermal_printer.ThermalPrinter._PrintModeBit(_INVERSE_MASK)

5
api.rst Normal file
View file

@ -0,0 +1,5 @@
.. If you created a package, create one automodule per module in the package.
.. automodule:: adafruit_thermal_printer
:members:

147
conf.py Normal file
View file

@ -0,0 +1,147 @@
# -*- coding: utf-8 -*-
import os
import sys
sys.path.insert(0, os.path.abspath('.'))
# -- General configuration ------------------------------------------------
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
'sphinx.ext.autodoc',
'sphinx.ext.intersphinx',
'sphinx.ext.viewcode',
]
# Uncomment the below if you use native CircuitPython modules such as
# digitalio, micropython and busio. List the modules you use. Without it, the
# autodoc module docs will fail to generate with a warning.
# autodoc_mock_imports = ["digitalio", "busio"]
intersphinx_mapping = {'python': ('https://docs.python.org/3.4', None),'CircuitPython': ('https://circuitpython.readthedocs.io/en/latest/', None)}
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
source_suffix = '.rst'
# The master toctree document.
master_doc = 'README'
# General information about the project.
project = u'adafruit_thermal_printer Library'
copyright = u'2017 Tony DiCola'
author = u'Tony DiCola'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = u'1.0'
# The full version, including alpha/beta/rc tags.
release = u'1.0'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#
# This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases.
language = None
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This patterns also effect to html_static_path and html_extra_path
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
# The reST default role (used for this markup: `text`) to use for all
# documents.
#
default_role = "any"
# If true, '()' will be appended to :func: etc. cross-reference text.
#
add_function_parentheses = True
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
# If true, `todo` and `todoList` produce output, else they produce nothing.
todo_include_todos = False
# -- Options for HTML output ----------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
on_rtd = os.environ.get('READTHEDOCS', None) == 'True'
if not on_rtd: # only import and set the theme if we're building docs locally
try:
import sphinx_rtd_theme
html_theme = 'sphinx_rtd_theme'
html_theme_path = [sphinx_rtd_theme.get_html_theme_path(), '.']
except:
html_theme = 'default'
html_theme_path = ['.']
else:
html_theme_path = ['.']
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
# Output file base name for HTML help builder.
htmlhelp_basename = 'Adafruit_thermal_printerLibrarydoc'
# -- Options for LaTeX output ---------------------------------------------
latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#
# 'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
#
# 'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
#
# 'preamble': '',
# Latex figure (float) alignment
#
# 'figure_align': 'htbp',
}
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
latex_documents = [
(master_doc, 'adafruit_thermal_printerLibrary.tex', u'adafruit_thermal_printer Library Documentation',
author, 'manual'),
]
# -- Options for manual page output ---------------------------------------
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
(master_doc, 'adafruit_thermal_printerlibrary', u'adafruit_thermal_printer Library Documentation',
[author], 1)
]
# -- Options for Texinfo output -------------------------------------------
# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
(master_doc, 'adafruit_thermal_printerLibrary', u' adafruit_thermal_printer Library Documentation',
author, 'adafruit_thermal_printerLibrary', 'One line description of project.',
'Miscellaneous'),
]

48
examples/simpletest.py Normal file
View file

@ -0,0 +1,48 @@
# Simple demo of printer functionality.
# Author: Tony DiCola
import board
import busio
# Pick which version thermal printer to import. Only ONE of these lines should
# be uncommented depending on the version of your printer. Hold the button on
# the printer as it's powered on and it will print a test page that displays
# the firmware version, like 2.64, 2.68, etc.
# Use this line for printers with version 2.68 or higher:
import adafruit_thermal_printer.thermal_printer as thermal_printer
# Use this line for printers with version 2.64 up to (but not including) 2.68:
#import adafruit_thermal_printer.thermal_printer_264 as thermal_printer
# Use this line for printers with version up to (but not including) 2.64:
#import adafruit_thermal_printer.thermal_printer_legacy as thermal_printer
# Define RX and TX pins for the board's serial port connected to the printer.
# Only the TX pin needs to be configued, and note to take care NOT to connect
# the RX pin if your board doesn't support 5V inputs. If RX is left unconnected
# the only loss in functionality is checking if the printer has paper--all other
# functions of the printer will work.
RX = board.RX
TX = board.TX
# Create a serial connection for the printer. You must use the same baud rate
# as your printer is configured (print a test page by holding the button
# during power-up and it will show the baud rate). Most printers use 119200.
uart = busio.UART(TX, RX, baudrate=119200)
# Create the printer instance.
printer = thermal_printer.ThermalPrinter(uart)
# Initialize the printer. Note this will take a few seconds for the printer
# to warm up and be ready to accept commands (hence calling it explicitly vs.
# automatically in the initializer above).
printer.begin()
# Print a test page:
printer.test_page()
# Move the paper forward two lines:
printer.feed(2)
# Print a line of text:
printer.print('Hello world!')
# Move the paper forward two lines:
printer.feed(2)

3
readthedocs.yml Normal file
View file

@ -0,0 +1,3 @@
python:
version: 3
requirements_file: requirements.txt

1
requirements.txt Normal file
View file

@ -0,0 +1 @@