первый

This commit is contained in:
2024-11-01 12:23:13 +05:00
parent 801d9d33fa
commit 0688c46a7e
226 changed files with 162921 additions and 0 deletions

View File

@ -0,0 +1,192 @@
//Reference: https://reference.epson-biz.com/modules/ref_escpos/index.php?content_id=72
#include "ESC_POS_Printer.h"
#if defined(_WIN32) || defined(_WINDOWS) || defined(_BORLAND) || defined(__BORLANDC__)
#else
#include <iostream>
#endif
//---------------------------------------------------------------------------
ESC_POS_Printer::ESC_POS_Printer()
{
m_hPrinter = NULL;
m_usb = false;
m_Serial = NULL;
}
//---------------------------------------------------------------------------
ESC_POS_Printer::~ESC_POS_Printer()
{
if (m_Serial != NULL)
{
close();
delete m_Serial;
}
}
//---------------------------------------------------------------------------
bool ESC_POS_Printer::openSerial(std::string ComNumber)
{
m_Serial->Open(ComNumber);
return true;
}
//---------------------------------------------------------------------------
bool ESC_POS_Printer::openUSB(std::wstring printerName)
{
#if defined(_WIN32) || defined(_WINDOWS) || defined(_BORLAND) || defined(__BORLANDC__)
bool result=true;
if (!OpenPrinter((LPWSTR)printerName.c_str(), &m_hPrinter, NULL))
result = false;
m_usb = true;
return result;
#else
std::string str( printerName.begin(), printerName.end() );
return openUSB(str);
#endif
}
//---------------------------------------------------------------------------
bool ESC_POS_Printer::openUSB(std::string printerName)
{
#if defined(_WIN32) || defined(_WINDOWS) || defined(_BORLAND) || defined(__BORLANDC__)
bool result=true;
if (!OpenPrinter((LPWSTR)printerName.c_str(), &m_hPrinter, NULL))
result = false;
m_usb = true;
return result;
#else
m_hPrinter = fopen(printerName.c_str(), "w");
if(m_hPrinter)
return true;
else
return false;
#endif
}
//---------------------------------------------------------------------------
bool ESC_POS_Printer::Start()
{
#if defined(_WIN32) || defined(_WINDOWS) || defined(_BORLAND) || defined(__BORLANDC__)
DWORD Level = 1;
std::wstring docName = L"Print ticket";
DOC_INFO_1 pDocInfo;
pDocInfo.pDocName = (LPWSTR)docName.c_str();
pDocInfo.pOutputFile = NULL;
pDocInfo.pDatatype = NULL;
bool result = true;
StartDocPrinter(m_hPrinter, Level, (BYTE*)&pDocInfo);
if (!StartPagePrinter(m_hPrinter))
result = false;
return result;
#else
return true;
#endif
}
//---------------------------------------------------------------------------
bool ESC_POS_Printer::End()
{
#if defined(_WIN32) || defined(_WINDOWS) || defined(_BORLAND) || defined(__BORLANDC__)
bool result = true;
if (!EndPagePrinter(m_hPrinter))
result = false;
if (!EndDocPrinter(m_hPrinter))
result = false;
return result;
#else
return true;
#endif
}
//---------------------------------------------------------------------------
bool ESC_POS_Printer::close()
{
if (m_Serial != NULL)
{
m_Serial->Close();
}
if (m_hPrinter != NULL)
{
#if defined(_WIN32) || defined(_WINDOWS) || defined(_BORLAND) || defined(__BORLANDC__)
if (!EndPagePrinter(m_hPrinter))
return false;
if (!EndDocPrinter(m_hPrinter))
return false;
ClosePrinter(m_hPrinter);
#else
if(fclose(m_hPrinter)==0)
return true;
else
return false;
#endif
}
return true;
}
//---------------------------------------------------------------------------
//width - <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> (<28><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> 8)
//height - <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
//bitArray - <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
bool ESC_POS_Printer::printImage(int width, int height, unsigned char* bitArray)
{
#if defined(_WIN32) || defined(_WINDOWS) || defined(_BORLAND) || defined(__BORLANDC__)
DWORD Written = 0;
int arLen = (width * height) / 8;
char bCmdPrint[] = { 0x1d, 0x76, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00 };
int w = width;
int h = height;
bCmdPrint[4] = (((w / 8) >> 0) & 0xFF); //xL <20> xL and xH specify the horizontal direction data count for one bit image (xL + xH x 256) in bytes
bCmdPrint[5] = (((w / 8) >> 8) & 0xFF); //xH
bCmdPrint[6] = ((h >> 0) & 0xFF); //yL <20> yL and yH specify the vertical direction data count for one bit image (yL + yH x 256) in dots.
bCmdPrint[7] = ((h >> 8) & 0xFF); //yH
WritePrinter(m_hPrinter, bCmdPrint, sizeof(bCmdPrint), &Written);
WritePrinter(m_hPrinter, bitArray, arLen, &Written);
return arLen == Written;
#else
int Written = 0;
int arLen = (width * height) / 8;
char bCmdPrint[] = { 0x1d, 0x76, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00 };
int w = width;
int h = height;
bCmdPrint[4] = (((w / 8) >> 0) & 0xFF); //xL <20> xL and xH specify the horizontal direction data count for one bit image (xL + xH x 256) in bytes
bCmdPrint[5] = (((w / 8) >> 8) & 0xFF); //xH
bCmdPrint[6] = ((h >> 0) & 0xFF); //yL <20> yL and yH specify the vertical direction data count for one bit image (yL + yH x 256) in dots.
bCmdPrint[7] = ((h >> 8) & 0xFF); //yH
Written = fwrite(bCmdPrint, sizeof(char), sizeof(bCmdPrint), m_hPrinter);
Written = fwrite(bitArray, sizeof(char), arLen, m_hPrinter);
return arLen == Written;
#endif
}
//---------------------------------------------------------------------------
bool ESC_POS_Printer::Feed()
{
char bCmdFeed[] = { 27, 100, 5 };
#if defined(_WIN32) || defined(_WINDOWS) || defined(_BORLAND) || defined(__BORLANDC__)
DWORD Written = 0;
WritePrinter(m_hPrinter, bCmdFeed, sizeof(bCmdFeed), &Written);
return Written == 3;
#else
int Written = 0;
Written = fwrite(bCmdFeed, sizeof(char), sizeof(bCmdFeed), m_hPrinter);
fflush(m_hPrinter);
return Written == 3;
#endif
}
//---------------------------------------------------------------------------
bool ESC_POS_Printer::Cut()
{
char bCmdCut[] = { 0x1B,0x6D };
#if defined(_WIN32) || defined(_WINDOWS) || defined(_BORLAND) || defined(__BORLANDC__)
DWORD Written = 0;
WritePrinter(m_hPrinter, bCmdCut, sizeof(bCmdCut), &Written);
return Written == 2;
#else
int Written = 0;
fwrite(bCmdCut, sizeof(char),sizeof(bCmdCut), m_hPrinter);
fflush(m_hPrinter);
return Written == 2;
#endif
}
//---------------------------------------------------------------------------

View File

@ -0,0 +1,41 @@
#ifndef ESC_POS_PRINTER_H
#define ESC_POS_PRINTER_H
#include <string>
#if defined(_WIN32) || defined(_WINDOWS) || defined(_BORLAND) || defined(__BORLANDC__)
#include <windows.h>
#else
#endif
#include "ComPort.h"
class ESC_POS_Printer {
public:
#if defined(_WIN32) || defined(_WINDOWS) || defined(_BORLAND) || defined(__BORLANDC__)
HANDLE m_hPrinter;
#else
FILE* m_hPrinter;
#endif
ComPort* m_Serial;
ESC_POS_Printer();
~ESC_POS_Printer();
bool openSerial(std::string ComNumber);
bool openUSB(std::wstring printerName);
bool openUSB(std::string printerName);
bool close();
bool Start();
bool printImage(int width, int height, unsigned char* bitArray);
bool Feed(); //Прокрутить бумагу
bool Cut(); //Отрезать бумагу
bool End();
private:
bool m_usb;
};
#endif

View File

@ -0,0 +1,536 @@
/*------------------------------------------------------------------------
An Arduino library for the USB Thermal Printer using Epson ESC POS commands
These printers use USB to communicate.
This library is based on the Adafruit Thermal Printer Library.
Adafruit invests time and resources providing this open source code.
Please support USB and open-source hardware by purchasing products
from Adafruit!
Written by Limor Fried/Ladyada for Adafruit Industries, with
contributions from the open source community. Originally based on
Thermal library from bildr.org
MIT license, all text above must be included in any redistribution.
Reference: https://reference.epson-biz.com/modules/ref_escpos/index.php?content_id=72
------------------------------------------------------------------------*/
#include "ESC_POS_Printer.h"
// ASCII codes used by some of the printer config commands:
#define ASCII_TAB '\t' // Horizontal tab
#define ASCII_LF '\n' // Line feed
#define ASCII_FF '\f' // Form feed
#define ASCII_CR '\r' // Carriage return
#define ASCII_EOT 4 // End of Transmission
#define ASCII_DLE 16 // Data Link Escape
#define ASCII_DC2 18 // Device control 2
#define ASCII_ESC 27 // Escape
#define ASCII_FS 28 // Field separator
#define ASCII_GS 29 // Group separator
// Constructor
ESC_POS_Printer::ESC_POS_Printer(Stream *s) :
stream(s) {
}
// The next four helper methods are used when issuing configuration
// commands, printing bitmaps or barcodes, etc. Not when printing text.
void ESC_POS_Printer::writeBytes(uint8_t a) {
stream->write(a);
}
void ESC_POS_Printer::writeBytes(uint8_t a, uint8_t b) {
uint8_t cmd[2] = {a, b};
stream->write(cmd, sizeof(cmd));
}
void ESC_POS_Printer::writeBytes(uint8_t a, uint8_t b, uint8_t c) {
uint8_t cmd[3] = {a, b, c};
stream->write(cmd, sizeof(cmd));
}
void ESC_POS_Printer::writeBytes(uint8_t a, uint8_t b, uint8_t c, uint8_t d) {
uint8_t cmd[4] = {a, b, c, d};
stream->write(cmd, sizeof(cmd));
}
// The underlying method for all high-level printing (e.g. println()).
// The inherited Print class handles the rest!
size_t ESC_POS_Printer::write(uint8_t c) {
if(c != 0x13) { // Strip carriage returns
stream->write(c);
if((c == '\n') || (column == maxColumn)) { // If newline or wrap
column = 0;
c = '\n'; // Treat wrap as newline on next pass
} else {
column++;
}
prevByte = c;
}
return 1;
}
void ESC_POS_Printer::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.
wake();
reset();
}
// Reset printer to default state.
void ESC_POS_Printer::reset() {
writeBytes(ASCII_ESC, '@'); // Init command
prevByte = '\n'; // Treat as if prior line is blank
column = 0;
maxColumn = 32;
charHeight = 24;
lineSpacing = 6;
barcodeHeight = 50;
}
// Reset text formatting parameters.
void ESC_POS_Printer::setDefault(){
online();
justify('L');
inverseOff();
doubleHeightOff();
setLineHeight(30);
boldOff();
underlineOff();
setBarcodeHeight(50);
setSize('s');
setCharset();
setCodePage();
}
void ESC_POS_Printer::test(){
println(F("Hello World!"));
feed(2);
}
void ESC_POS_Printer::testPage() {
char commandTest[] = {ASCII_GS, '(', 'A', 2, 0, 0, 3};
stream->write(commandTest, sizeof(commandTest));
}
void ESC_POS_Printer::setBarcodeHeight(uint8_t val) { // Default is 50
if(val < 1) val = 1;
barcodeHeight = val;
// This does not work on my printer. It prints a '2' = 0x32 = 50
//writeBytes(ASCII_GS, 'h', val);
}
void ESC_POS_Printer::printBarcode(const char *text, uint8_t type) {
feed(1); // Recent firmware can't print barcode w/o feed first???
writeBytes(ASCII_GS, 'H', 2); // Print label below barcode
writeBytes(ASCII_GS, 'w', 3); // Barcode width 3 (0.375/1.0mm thin/thick)
writeBytes(ASCII_GS, 'k', type); // Barcode type (listed in .h file)
// Write text including the terminating '\0'
stream->write(text, strlen(text)+1);
prevByte = '\n';
}
// === Character commands ===
#define INVERSE_MASK (1 << 1) // Not in 2.6.8 firmware (see inverseOn())
//#define UPDOWN_MASK (1 << 2)
//#define BOLD_MASK (1 << 3)
#define DOUBLE_HEIGHT_MASK (1 << 4)
#define DOUBLE_WIDTH_MASK (1 << 5)
//#define STRIKE_MASK (1 << 6)
#define UNDERLINE_MASK (1 << 7)
void ESC_POS_Printer::setPrintMode(uint8_t mask) {
printMode |= mask;
writePrintMode();
charHeight = (printMode & DOUBLE_HEIGHT_MASK) ? 48 : 24;
maxColumn = (printMode & DOUBLE_WIDTH_MASK ) ? 16 : 32;
}
void ESC_POS_Printer::unsetPrintMode(uint8_t mask) {
printMode &= ~mask;
writePrintMode();
charHeight = (printMode & DOUBLE_HEIGHT_MASK) ? 48 : 24;
maxColumn = (printMode & DOUBLE_WIDTH_MASK ) ? 16 : 32;
}
void ESC_POS_Printer::writePrintMode() {
writeBytes(ASCII_ESC, '!', printMode);
}
void ESC_POS_Printer::normal() {
printMode = 0;
writePrintMode();
upsideDownOff();
}
void ESC_POS_Printer::inverseOn(){
writeBytes(ASCII_GS, 'B', 1);
}
void ESC_POS_Printer::inverseOff(){
writeBytes(ASCII_GS, 'B', 0);
}
void ESC_POS_Printer::upsideDownOn(){
writeBytes(ASCII_ESC, '{', 1);
}
void ESC_POS_Printer::upsideDownOff(){
writeBytes(ASCII_ESC, '{', 0);
}
void ESC_POS_Printer::doubleHeightOn(){
setPrintMode(DOUBLE_HEIGHT_MASK);
}
void ESC_POS_Printer::doubleHeightOff(){
unsetPrintMode(DOUBLE_HEIGHT_MASK);
}
void ESC_POS_Printer::doubleWidthOn(){
setPrintMode(DOUBLE_WIDTH_MASK);
}
void ESC_POS_Printer::doubleWidthOff(){
unsetPrintMode(DOUBLE_WIDTH_MASK);
}
void ESC_POS_Printer::strikeOn(){
writeBytes(ASCII_ESC, 'G', 1);
}
void ESC_POS_Printer::strikeOff(){
writeBytes(ASCII_ESC, 'G', 0);
}
void ESC_POS_Printer::boldOn(){
writeBytes(ASCII_ESC, 'E', 1);
}
void ESC_POS_Printer::boldOff(){
writeBytes(ASCII_ESC, 'E', 0);
}
void ESC_POS_Printer::justify(char value){
uint8_t pos = 0;
switch(toupper(value)) {
case 'L': pos = 0; break;
case 'C': pos = 1; break;
case 'R': pos = 2; break;
}
writeBytes(ASCII_ESC, 'a', pos);
}
// Feeds by the specified number of lines
void ESC_POS_Printer::feed(uint8_t x) {
writeBytes(ASCII_ESC, 'd', x);
prevByte = '\n';
column = 0;
}
// Feeds by the specified number of individual pixel rows
void ESC_POS_Printer::feedRows(uint8_t rows) {
writeBytes(ASCII_ESC, 'J', rows);
prevByte = '\n';
column = 0;
}
void ESC_POS_Printer::flush() {
writeBytes(ASCII_FF);
}
void ESC_POS_Printer::setSize(char value){
uint8_t size;
switch(toupper(value)) {
default: // Small: standard width and height
size = 0x00;
charHeight = 24;
maxColumn = 32;
break;
case 'M': // Medium: double height
size = 0x01;
charHeight = 48;
maxColumn = 32;
break;
case 'L': // Large: double width and height
size = 0x11;
charHeight = 48;
maxColumn = 64;
break;
}
writeBytes(ASCII_GS, '!', size);
prevByte = '\n'; // Setting the size adds a linefeed
}
void ESC_POS_Printer::setSize(uint8_t height, uint8_t width) {
uint8_t size = ((width & 0x7) << 3) | (height & 0x7);
writeBytes(ASCII_GS, '!', 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
void ESC_POS_Printer::underlineOn(uint8_t weight) {
if(weight > 2) weight = 2;
writeBytes(ASCII_ESC, '-', weight);
}
void ESC_POS_Printer::underlineOff() {
writeBytes(ASCII_ESC, '-', 0);
}
// ASCII ESC * m nL nH d1...dk
// Hex 1B 2A m nL nH d1...dk
// Dec 27 42 m nL nH d1...dk
// m = 0 single density vert, single horizontal
// m = 1 single density vert, double horiz
// m = 32 double density vert, single horizontal
// m = 33 double density vert, double horiz
void ESC_POS_Printer::printBitmap(
int w, int h, const uint8_t *bitmap, int density) {
uint8_t band_height;
uint8_t bitmap_command[] = { 0x1b, '*', 0, 0, 0 };
size_t w_bytes = w;
switch (density) {
default:
density = 1;
/* fall through */
case 1:
bitmap_command[2] = 0; // m = single density
band_height = 8;
break;
case 2:
bitmap_command[2] = 33; // m = double density
band_height = 24;
w_bytes *= 3;
break;
}
bitmap_command[3] = w & 0xFF; // nL = width LS byte
bitmap_command[4] = (w >> 8) & 0xFF;// nH = width MS byte
// Line spacing = 16 dots
stream->write("\x1b\x33\x10\x1bU\x01"); // Unidirectional print mode on
for (int row = 0; row < h; row += band_height) {
stream->write(bitmap_command, sizeof(bitmap_command));
stream->write(bitmap, w_bytes);
stream->write('\n');
bitmap += w_bytes;
}
stream->write("\x1b\x32\x1bU"); // Default line spacing
stream->write((uint8_t)0); // Unidirectional print mode off
prevByte = '\n';
}
void ESC_POS_Printer::printBitmap_P(
int w, int h, const uint8_t *bitmap, int density) {
uint8_t band_height;
uint8_t bitmap_command[] = { 0x1b, '*', 0, 0, 0 };
size_t w_bytes = w;
uint8_t buf[64];
PGM_P p = reinterpret_cast<PGM_P>(bitmap);
switch (density) {
default:
density = 1;
/* fall through */
case 1:
bitmap_command[2] = 0; // m = single density
band_height = 8;
break;
case 2:
bitmap_command[2] = 33; // m = double density
band_height = 24;
w_bytes *= 3;
break;
}
bitmap_command[3] = w & 0xFF; // nL = width LS byte
bitmap_command[4] = (w >> 8) & 0xFF;// nH = width MS byte
// Line spacing = 16 dots
stream->write("\x1b\x33\x10\x1bU\x01"); // Unidirectional print mode on
for (int row = 0; row < h; row += band_height) {
memcpy(buf, bitmap_command, sizeof(bitmap_command));
size_t len = sizeof(bitmap_command);
size_t outlen;
for (size_t col = 0; col < w_bytes; col += outlen) {
outlen = min(sizeof(buf)-len, w_bytes - col);
memcpy_P(buf+len, p, outlen);
len += outlen;
p += outlen;
stream->write(buf, len);
len = 0;
}
stream->write('\n');
}
// The count correctly includes the trailing '\0'!
stream->write("\x1b\x32\x1bU", 5); // Default line spacing,
// Unidirectional print mode off
prevByte = '\n';
}
void ESC_POS_Printer::printBitmap(
int w, int h, const uint8_t *bitmap, bool fromProgMem) {
int rowBytes, rowBytesClipped, rowStart, chunkHeight, chunkHeightLimit,
x, y, i;
rowBytes = (w + 7) / 8; // Round up to next byte boundary
rowBytesClipped = (rowBytes >= 48) ? 48 : rowBytes; // 384 pixels max width
chunkHeightLimit = 255; // Buffer doesn't matter, handshake!
for(i=rowStart=0; rowStart < h; rowStart += chunkHeightLimit) {
// Issue up to chunkHeightLimit rows at a time:
chunkHeight = h - rowStart;
if(chunkHeight > chunkHeightLimit) chunkHeight = chunkHeightLimit;
writeBytes(ASCII_DC2, '*', chunkHeight, rowBytesClipped);
for(y=0; y < chunkHeight; y++) {
for(x=0; x < rowBytesClipped; x++, i++) {
stream->write(fromProgMem ? pgm_read_byte(bitmap + i) : *(bitmap+i));
}
i += rowBytes - rowBytesClipped;
}
}
prevByte = '\n';
}
void ESC_POS_Printer::printBitmap(int w, int h, Stream *fromStream) {
int rowBytes, rowBytesClipped, rowStart, chunkHeight, chunkHeightLimit,
x, y, i, c;
rowBytes = (w + 7) / 8; // Round up to next byte boundary
rowBytesClipped = (rowBytes >= 48) ? 48 : rowBytes; // 384 pixels max width
// Est. max rows to write at once, assuming 256 byte printer buffer.
chunkHeightLimit = 255; // Buffer doesn't matter, handshake!
for(rowStart=0; rowStart < h; rowStart += chunkHeightLimit) {
// Issue up to chunkHeightLimit rows at a time:
chunkHeight = h - rowStart;
if(chunkHeight > chunkHeightLimit) chunkHeight = chunkHeightLimit;
writeBytes(ASCII_DC2, '*', chunkHeight, rowBytesClipped);
for(y=0; y < chunkHeight; y++) {
for(x=0; x < rowBytesClipped; x++) {
while((c = fromStream->read()) < 0);
stream->write((uint8_t)c);
}
for(i = rowBytes - rowBytesClipped; i>0; i--) {
while((c = fromStream->read()) < 0);
}
}
}
prevByte = '\n';
}
void ESC_POS_Printer::printBitmap(Stream *fromStream) {
uint8_t tmp;
uint16_t width, height;
tmp = fromStream->read();
width = (fromStream->read() << 8) + tmp;
tmp = fromStream->read();
height = (fromStream->read() << 8) + tmp;
printBitmap(width, height, fromStream);
}
// Take the printer offline. Print commands sent after this will be
// ignored until 'online' is called.
void ESC_POS_Printer::offline(){
writeBytes(ASCII_ESC, '=', 0);
}
// Take the printer back online. Subsequent print commands will be obeyed.
void ESC_POS_Printer::online(){
writeBytes(ASCII_ESC, '=', 1);
}
// Put the printer into a low-energy state immediately.
void ESC_POS_Printer::sleep() {
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.
void ESC_POS_Printer::sleepAfter(uint16_t seconds) {
writeBytes(ASCII_ESC, '8', seconds, seconds >> 8);
}
// Wake the printer from a low-energy state.
void ESC_POS_Printer::wake() {
}
// Check the status of the paper using the printer's self reporting
// ability. Returns true for paper, false for no paper.
// Might not work on all printers!
bool ESC_POS_Printer::hasPaper() {
// writeBytes(ASCII_DLE, ASCII_EOT, 4);
writeBytes(ASCII_GS, 'r', 1);
// writeBytes(ASCII_ESC, 'v');
int status = 0;
for(uint8_t i=0; i<10; i++) {
if(stream->available()) {
status = stream->read();
break;
}
delay(100);
}
return !(status & 0b00001100);
}
void ESC_POS_Printer::setLineHeight(int val) {
if(val < 24) val = 24;
lineSpacing = val - 24;
// The printer doesn't take into account the current text height
// when setting line height, making this more akin to inter-line
// spacing. Default line spacing is 30 (char height of 24, line
// spacing of 6).
writeBytes(ASCII_ESC, '3', val);
}
void ESC_POS_Printer::setMaxChunkHeight(int val) {
}
// Alters some chars in ASCII 0x23-0x7E range; see datasheet
void ESC_POS_Printer::setCharset(uint8_t val) {
writeBytes(ASCII_ESC, 'R', val);
}
// Selects alt symbols for 'upper' ASCII values 0x80-0xFF
void ESC_POS_Printer::setCodePage(uint8_t val) {
writeBytes(ASCII_ESC, 't', val);
}
void ESC_POS_Printer::tab() {
writeBytes(ASCII_TAB);
column = (column + 4) & 0b11111100;
}
void ESC_POS_Printer::setCharSpacing(int spacing) {
writeBytes(ASCII_ESC, ' ', spacing);
}

View File

@ -0,0 +1,198 @@
/*------------------------------------------------------------------------
An Arduino library for the USB Thermal Printer using Epson ESC POS commands
These printers use USB to communicate.
This library is based on the Adafruit Thermal Printer Library.
Adafruit invests time and resources providing this open source code.
Please support USB and open-source hardware by purchasing products
from Adafruit!
Written by Limor Fried/Ladyada for Adafruit Industries, with
contributions from the open source community. Originally based on
Thermal library from bildr.org
MIT license, all text above must be included in any redistribution.
------------------------------------------------------------------------*/
#ifndef ESC_POS_PRINTER_H
#define ESC_POS_PRINTER_H
#include "Arduino.h"
// Barcode types and charsets
#define UPC_A 65
#define UPC_E 66
#define EAN13 67
#define EAN8 68
#define CODE39 69
#define ITF 70
#define CODABAR 71
#define CODE93 72
#define CODE128 73
#define GS1_128 74
#define GS1_DATABAR_OMNI 75
#define GS1_DATABAR_TRUNC 76
#define GS1_DATABAR_LIMTD 77
#define GS1_DATABAR_EXPAN 78
#define CHARSET_USA 0
#define CHARSET_FRANCE 1
#define CHARSET_GERMANY 2
#define CHARSET_UK 3
#define CHARSET_DENMARK1 4
#define CHARSET_SWEDEN 5
#define CHARSET_ITALY 6
#define CHARSET_SPAIN1 7
#define CHARSET_JAPAN 8
#define CHARSET_NORWAY 9
#define CHARSET_DENMARK2 10
#define CHARSET_SPAIN2 11
#define CHARSET_LATINAMERICA 12
#define CHARSET_KOREA 13
#define CHARSET_SLOVENIA 14
#define CHARSET_CROATIA 14
#define CHARSET_CHINA 15
#define CHARSET_VIETNAM 16
#define CHARSET_ARABIA 17
#define CHARSET_INDIA_DEVANAGARI 66
#define CHARSET_INDIA_BENGALI 67
#define CHARSET_INDIA_TAMIL 68
#define CHARSET_INDIA_TELUGU 69
#define CHARSET_INDIA_ASSAMESE 70
#define CHARSET_INDIA_ORIYA 71
#define CHARSET_INDIA_KANNANDA 72
#define CHARSET_INDIA_MALAYALAM 73
#define CHARSET_INDIA_GUJARATI 74
#define CHARSET_INDIA_PUNJABI 75
#define CHARSET_INDIA_MARATHI 82
#define CODEPAGE_CP437 0 // USA, Standard Europe
#define CODEPAGE_KATAKANA 1
#define CODEPAGE_CP850 2 // Multilingual
#define CODEPAGE_CP860 3 // Portuguese
#define CODEPAGE_CP863 4 // Canadian-French
#define CODEPAGE_CP865 5 // Nordic
#define CODEPAGE_WCP1251 6 // Cyrillic
#define CODEPAGE_CP866 7 // Cyrillic #2
#define CODEPAGE_MIK 8 // Cyrillic/Bulgarian
#define CODEPAGE_CP755 9 // East Europe, Latvian 2
#define CODEPAGE_IRAN 10
#define CODEPAGE_CP862 15 // Hebrew
#define CODEPAGE_WCP1252 16 // Latin 1
#define CODEPAGE_WCP1253 17 // Greek
#define CODEPAGE_CP852 18 // Latin 2
#define CODEPAGE_CP858 19 // Multilingual Latin 1 + Euro
#define CODEPAGE_IRAN2 20
#define CODEPAGE_LATVIAN 21
#define CODEPAGE_CP864 22 // Arabic
#define CODEPAGE_ISO_8859_1 23 // West Europe
#define CODEPAGE_CP737 24 // Greek
#define CODEPAGE_WCP1257 25 // Baltic
#define CODEPAGE_THAI 26
#define CODEPAGE_CP720 27 // Arabic
#define CODEPAGE_CP855 28
#define CODEPAGE_CP857 29 // Turkish
#define CODEPAGE_WCP1250 30 // Central Europe
#define CODEPAGE_CP775 31
#define CODEPAGE_WCP1254 32 // Turkish
#define CODEPAGE_WCP1255 33 // Hebrew
#define CODEPAGE_WCP1256 34 // Arabic
#define CODEPAGE_WCP1258 35 // Vietnam
#define CODEPAGE_ISO_8859_2 36 // Latin 2
#define CODEPAGE_ISO_8859_3 37 // Latin 3
#define CODEPAGE_ISO_8859_4 38 // Baltic
#define CODEPAGE_ISO_8859_5 39 // Cyrillic
#define CODEPAGE_ISO_8859_6 40 // Arabic
#define CODEPAGE_ISO_8859_7 41 // Greek
#define CODEPAGE_ISO_8859_8 42 // Hebrew
#define CODEPAGE_ISO_8859_9 43 // Turkish
#define CODEPAGE_ISO_8859_15 44 // Latin 3
#define CODEPAGE_THAI2 45
#define CODEPAGE_CP856 46
#define CODEPAGE_CP874 47
class ESC_POS_Printer : public Print {
public:
// IMPORTANT: constructor syntax has changed from prior versions
// of this library. Please see notes in the example code!
ESC_POS_Printer(Stream *s=&Serial);
size_t
write(uint8_t c);
void
begin(),
boldOff(),
boldOn(),
doubleHeightOff(),
doubleHeightOn(),
doubleWidthOff(),
doubleWidthOn(),
feed(uint8_t x=1),
feedRows(uint8_t),
flush(),
inverseOff(),
inverseOn(),
justify(char value),
offline(),
online(),
printBarcode(const char *text, uint8_t type),
printBitmap(int w, int h, const uint8_t *bitmap, int density=1),
printBitmap_P(int w, int h, const uint8_t *bitmap, int density=1),
printBitmap(int w, int h, const uint8_t *bitmap, bool fromProgMem=true),
printBitmap(int w, int h, Stream *fromStream),
printBitmap(Stream *fromStream),
normal(),
reset(),
setBarcodeHeight(uint8_t val=50),
setCharSpacing(int spacing=0),
setCharset(uint8_t val=0),
setCodePage(uint8_t val=0),
setDefault(),
setLineHeight(int val=30),
setMaxChunkHeight(int val=256),
setSize(char value),
setSize(uint8_t height, uint8_t width),
setTimes(unsigned long, unsigned long),
sleep(),
sleepAfter(uint16_t seconds),
strikeOff(),
strikeOn(),
tab(),
test(),
testPage(),
underlineOff(),
underlineOn(uint8_t weight=1),
upsideDownOff(),
upsideDownOn(),
wake();
bool
hasPaper();
private:
Stream
*stream;
uint8_t
printMode,
prevByte, // Last character issued to printer
column, // Last horizontal column printed
maxColumn, // Page width (output 'wraps' at this point)
charHeight, // Height of characters, in 'dots'
lineSpacing, // Inter-line spacing (not line height), in dots
barcodeHeight, // Barcode height in dots, not including text
maxChunkHeight;
void
writeBytes(uint8_t a),
writeBytes(uint8_t a, uint8_t b),
writeBytes(uint8_t a, uint8_t b, uint8_t c),
writeBytes(uint8_t a, uint8_t b, uint8_t c, uint8_t d),
setPrintMode(uint8_t mask),
unsetPrintMode(uint8_t mask),
writePrintMode();
};
#endif // ESC_POS_PRINTER_H

View File

@ -0,0 +1,16 @@
#ifndef ESCPOSPRINTER_CONSTANTS_H
#define ESCPOSPRINTER_CONSTANTS_H
static const char *ESCPOS_CMD_INIT = "\x1b\x40";
static const char *ESCPOS_CMD_PRINT_RASTER_BIT_IMAGE = "\x1d\x76\x30\x00";
static const char *ESCPOS_CMD_CUT = "\x1d\x56\x42";
static const char *ESCPOS_CMD_FEED = "\x1b\x64";
// The maximum width of image the printer can accept
static const int ESCPOS_MAX_DOT_WIDTH = 576;
// When printing, if the image is too long, the printer
// will cut the images into chunks of (w x ESCPOS_CHUNK_DOT_HEIGHT)
static const int ESCPOS_CHUNK_DOT_HEIGHT = 512;
#endif

View File

@ -0,0 +1,63 @@
/*
* epsonPrinter.h
* epson_print
*
* Created by base on 08/06/11.
* Copyright 2011 __MyCompanyName__. All rights reserved.
*
*/
namespace epsonConstants {
const char CTL_LF = '\x0a'; // Print and line feed
const char CTL_FF = '\x0c'; // Form feed
const char CTL_CR = '\x0d'; // Carriage return
const char CTL_HT = '\x09'; // Horizontal tab
const char CTL_VT = '\x0b'; // Vertical tab
// Printer hardware
const char HW_INIT[2] = {'\x1b', '\x40'}; // Clear data in buffer and reset modes
//const char HW_SELECT = '\x1b\x3d\x01'; // Printer select
//const char HW_RESET = '\x1b\x3f\x0a\x00'; // Reset printer hardware
// Cash Drawer
const char CD_KICK_2[3] = {'\x1b', '\x70', '\x00'}; // Sends a pulse to pin 2 []
const char CD_KICK_5[3] = {'\x1b', '\x70', '\x01'}; // Sends a pulse to pin 5 []
// Paper
const char PAPER_FULL_CUT[3] = {'\x1d', '\x56', '\x00'}; // Full cut paper
const char PAPER_PART_CUT[3] = {'\x1d', '\x56', '\x01'}; // Partial cut paper
// Text format
const char TXT_NORMAL[3] = {'\x1b', '\x21', '\x00'}; // Normal text
const char TXT_2HEIGHT[3] = {'\x1b', '\x21', '\x10'}; // Double height text
const char TXT_2WIDTH[3] = {'\x1b', '\x21', '\x20'}; // Double width text
const char TXT_UNDERL_OFF[3] = {'\x1b', '\x2d', '\x00'}; // Underline font OFF
const char TXT_UNDERL_ON[3] = {'\x1b', '\x2d', '\x01'}; // Underline font 1-dot ON
const char TXT_UNDERL2_ON[3] = {'\x1b', '\x2d', '\x02'}; // Underline font 2-dot ON
const char TXT_BOLD_OFF[3] = {'\x1b', '\x45', '\x00'}; // Bold font OFF
const char TXT_BOLD_ON[3] = { '\x1b', '\x45', '\x01'}; // Bold font ON
const char TXT_FONT_A[3] = { '\x1b', '\x4d', '\x00'}; // Font type A
const char TXT_FONT_B[3] = { '\x1b', '\x4d', '\x01'}; // Font type B
const char TXT_ALIGN_LT[3] = { '\x1b', '\x61', '\x00'}; // Left justification
const char TXT_ALIGN_CT[3] = { '\x1b', '\x61', '\x01'}; // Centering
const char TXT_ALIGN_RT[3] = { '\x1b', '\x61', '\x02'}; // Right justification
// Barcode format
const char BARCODE_TXT_OFF[3] = { '\x1d', '\x48', '\x00'}; // HRI barcode chars OFF
const char BARCODE_TXT_ABV[3] = { '\x1d', '\x48', '\x01'}; // HRI barcode chars above
const char BARCODE_TXT_BLW[3] = { '\x1d', '\x48', '\x02'}; // HRI barcode chars below
const char BARCODE_TXT_BTH[3] = { '\x1d', '\x48', '\x03'}; // HRI barcode chars both above and below
const char BARCODE_FONT_A[3] = { '\x1d', '\x66', '\x00'}; // Font type A for HRI barcode chars
const char BARCODE_FONT_B[3] = { '\x1d', '\x66', '\x01'}; // Font type B for HRI barcode chars
const char BARCODE_HEIGHT[3] = { '\x1d', '\x68', '\x64'}; // Barcode Height [1-255]
const char BARCODE_WIDTH[3] = { '\x1d', '\x77', '\x03'}; // Barcode Width [2-6]
const char BARCODE_UPC_A[3] = { '\x1d', '\x6b', '\x00'}; // Barcode type UPC-A
const char BARCODE_UPC_E[3] = { '\x1d', '\x6b', '\x01'}; // Barcode type UPC-E
const char BARCODE_EAN13[3] = { '\x1d', '\x6b', '\x02'}; // Barcode type EAN13
const char BARCODE_EAN8[3] = { '\x1d', '\x6b', '\x03'}; // Barcode type EAN8
const char BARCODE_CODE39[3] = { '\x1d', '\x6b', '\x04'}; // Barcode type CODE39
const char BARCODE_ITF[3] = { '\x1d', '\x6b', '\x05'}; // Barcode type ITF
const char BARCODE_NW7[3] = { '\x1d', '\x6b', '\x06'}; // Barcode type NW7
// Image format
const char S_RASTER_N[4] = { '\x1d', '\x76', '\x30', '\x00'}; // Set raster image normal size
const char S_RASTER_2W[4] = { '\x1d', '\x76', '\x30', '\x01'}; // Set raster image double width
const char S_RASTER_2H[4] = { '\x1d', '\x76', '\x30', '\x02'}; // Set raster image double height
const char S_RASTER_Q[4] = { '\x1d', '\x76', '\x30', '\x03'}; // Set raster image quadruple
};

View File

@ -0,0 +1,295 @@
#include <assert.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <string.h>
#include <unistd.h>
#include <stdio.h>
#include "serial.h"
#include "printer.h"
#include "error_private.h"
#include "constants.h"
escpos_printer *escpos_printer_network(const char * const addr, const short port)
{
assert(addr != NULL);
int sockfd;
escpos_printer *printer = NULL;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) {
last_error = ESCPOS_ERROR_SOCK;
} else {
struct sockaddr_in dest;
dest.sin_family = AF_INET;
dest.sin_port = htons(port);
if (inet_pton(AF_INET, addr, &dest.sin_addr.s_addr) == 0) {
last_error = ESCPOS_ERROR_INVALID_ADDR;
} else if (connect(sockfd, (struct sockaddr*)&dest, sizeof(dest)) != 0) {
last_error = ESCPOS_ERROR_CONNECTION_FAILED;
} else {
printer = (escpos_printer *)malloc(sizeof(escpos_printer));
printer->sockfd = sockfd;
printer->config.max_width = ESCPOS_MAX_DOT_WIDTH;
printer->config.chunk_height = ESCPOS_CHUNK_DOT_HEIGHT;
printer->config.is_network_printer = 1;
}
}
return printer;
}
escpos_printer *escpos_printer_serial(const char * const portname, const int baudrate)
{
assert(portname != NULL);
int serialfd;
escpos_printer *printer = NULL;
serialfd = open(portname, O_WRONLY | O_NOCTTY | O_SYNC);
if (serialfd < 0) {
last_error = ESCPOS_ERROR_SOCK;
} else {
set_interface_attribs(serialfd, baudrate);
printer = (escpos_printer *)malloc(sizeof(escpos_printer));
printer->sockfd = serialfd;
printer->config.max_width = ESCPOS_MAX_DOT_WIDTH;
printer->config.chunk_height = ESCPOS_CHUNK_DOT_HEIGHT;
printer->config.is_network_printer = 0;
}
return printer;
}
int escpos_printer_config(escpos_printer *printer, const escpos_config * const config)
{
assert(printer != NULL);
assert(config != NULL);
printer->config = *config;
return 0;
}
void escpos_printer_destroy(escpos_printer *printer)
{
assert(printer != NULL);
close(printer->sockfd);
free(printer);
}
int escpos_printer_raw(escpos_printer *printer, const char * const message, const int len)
{
assert(printer != NULL);
int total = len;
int sent = 0;
int bytes = 0;
if (printer->config.is_network_printer) {
// Network printer logic.
// Make sure send() sends all data
while (sent < total) {
bytes = send(printer->sockfd, message, total, 0);
if (bytes == -1) {
last_error = ESCPOS_ERROR_SEND_FAILED;
break;
} else {
sent += bytes;
}
}
} else {
// Serial printer logic.
sent = write(printer->sockfd, message, len);
if (sent != len) {
last_error = ESCPOS_ERROR_SEND_FAILED;
}
tcdrain(printer->sockfd);
}
return !(sent == total);
}
int escpos_printer_cut(escpos_printer *printer, const int lines)
{
char buffer[4];
strncpy(buffer, ESCPOS_CMD_CUT, 3);
buffer[3] = lines;
return escpos_printer_raw(printer, buffer, sizeof(buffer));
}
int escpos_printer_feed(escpos_printer *printer, const int lines)
{
assert(lines > 0 && lines < 256);
char buffer[3];
strncpy(buffer, ESCPOS_CMD_FEED, 2);
buffer[2] = lines;
return escpos_printer_raw(printer, buffer, sizeof(buffer));
}
void set_bit(unsigned char *byte, const int i, const int bit)
{
assert(byte != NULL);
assert(i >= 0 && i < 8);
if (bit > 0) {
*byte |= 1 << i;
} else {
*byte &= ~(1 << i);
}
}
// Calculates the padding required so that the size fits in 32 bits
void calculate_padding(const int size, int *padding_l, int *padding_r)
{
assert(padding_l != NULL);
assert(padding_r != NULL);
if (size % 32 == 0) {
*padding_l = 0;
*padding_r = 0;
} else {
int padding = 32 - (size % 32);
*padding_l = padding / 2;
*padding_r = padding / 2 + (padding % 2 == 0 ? 0 : 1);
}
}
void convert_image_to_bits(unsigned char *pixel_bits,
const unsigned char *image_data,
const int w,
const int h,
int *bitmap_w,
int *bitmap_h)
{
assert(pixel_bits != NULL);
assert(image_data != NULL);
assert(bitmap_w != NULL);
assert(bitmap_h != NULL);
int padding_l, padding_r, padding_t, padding_b;
calculate_padding(w, &padding_l, &padding_r);
calculate_padding(h, &padding_t, &padding_b);
int padded_w = w + padding_l + padding_r;
// We only need to add the padding to the bottom for height.
// This is because when printing long images, only the last
// chunk will have the irregular height.
padding_b += padding_t;
for (int y = 0; y < h; y++) {
for (int x = 0; x < padded_w; x++) {
int pi = (y * padded_w) + x;
int curr_byte = pi / 8;
unsigned char pixel = image_data[(y * w) + x];
int bit = y < h ? pixel < 128 : 0;
set_bit(&pixel_bits[curr_byte], 7 - (pi % 8), bit);
}
}
for (int y = 0; y < padding_b; y++) {
for (int x = 0; x < padded_w; x++) {
int pi = (h * padded_w) + (y * padded_w) + x;
int curr_byte = pi / 8;
set_bit(&pixel_bits[curr_byte], 7 - (pi % 8), 0);
}
}
// Outputs the bitmap width and height after padding
*bitmap_w = w + padding_l + padding_r;
*bitmap_h = h + padding_b;
}
int escpos_printer_print(escpos_printer *printer,
const unsigned char *pixel_bits,
const int w,
const int h)
{
assert(printer != NULL);
assert(pixel_bits != NULL);
assert(w > 0 && w <= printer->config.max_width);
assert(h > 0 && h <= printer->config.chunk_height);
assert(w % 32 == 0);
assert(h % 32 == 0);
int result = escpos_printer_raw(printer, ESCPOS_CMD_PRINT_RASTER_BIT_IMAGE, 4);
char buffer[4];
buffer[0] = (((w / 8) >> 0) & 0xFF);
buffer[1] = (((w / 8) >> 8) & 0xFF);
buffer[2] = ((h >> 0) & 0xFF);
buffer[3] = ((h >> 8) & 0xFF);
result = escpos_printer_raw(printer, buffer, 4);
result = escpos_printer_raw(printer, (char *)pixel_bits, (w / 8) * h);
if (result != 0) {
last_error = ESCPOS_ERROR_IMAGE_PRINT_FAILED;
}
return result;
}
int escpos_printer_image(escpos_printer *printer,
const unsigned char * const image_data,
const int width,
const int height)
{
assert(printer != NULL);
assert(image_data != NULL);
assert(width > 0 && width <= printer->config.max_width);
assert(height > 0);
int result = 0;
if (image_data != NULL) {
int byte_width = printer->config.max_width / 8;
int padding_t, padding_b;
calculate_padding(height, &padding_t, &padding_b);
int print_height = height + padding_t + padding_b;
unsigned char pixel_bits[byte_width * print_height];
int c = 0;
int chunks = 0;
if (height <= printer->config.chunk_height) {
chunks = 1;
} else {
chunks = (height / printer->config.chunk_height) + (height % printer->config.chunk_height ? 1 : 0);
}
while (c < chunks) {
// Because the printer's image buffer has a limited memory,
// if the image's height exceeds config.chunk_height pixels,
// it is printed in chunks of x * config.chunk_height pixels.
int chunk_height = (c + 1) * printer->config.chunk_height <= height ?
printer->config.chunk_height :
height - (c * printer->config.chunk_height);
int bitmap_w, bitmap_h;
convert_image_to_bits(
pixel_bits,
image_data + (c * printer->config.chunk_height * width),
width,
chunk_height,
&bitmap_w,
&bitmap_h);
result = escpos_printer_print(printer, pixel_bits, bitmap_w, bitmap_h);
if (result != 0) {
break;
}
c += 1;
}
}
return result;
}

View File

@ -0,0 +1,106 @@
#ifndef ESCPOSPRINTER_PRINTER_H
#define ESCPOSPRINTER_PRINTER_H
typedef struct escpos_config {
// See ESCPOS_MAX_DOT_WIDTH in constants.h
// Default value: ESCPOS_MAX_DOT_WIDTH
int max_width;
// See ESCPOS_CHUNK_DOT_HEIGHT in constants.h
// Default value: ESCPOS_CHUNK_DOT_HEIGHT
int chunk_height;
unsigned int is_network_printer : 1;
} escpos_config;
typedef struct escpos_printer {
int sockfd;
escpos_config config;
} escpos_printer;
// Connects to an ESC/POS printer via network
//
// Params:
// - addr: the printer's address
// - port: the printer's port
//
// Return value: the printer object if successful, NULL otherwise.
// If it fails, use escpos_last_error() to get the error code.
extern escpos_printer *escpos_printer_network(const char * const addr, const short port);
// Connects to an ESC/POS printer via serial
//
// Params:
// - portname: the path to the serial file to be used
// - baudrate: the baudrate for serial communication
//
// Return value: the printer object if successful, NULL otherwise.
// If it fails, use escpos_last_error() to get the error code.
extern escpos_printer *escpos_printer_serial(const char * const portname, const int baudrate);
// Modifies the printer's configuration
//
// Params:
// - printer: the printer
// - config: the config
//
// Return value: 0 is successful, non-zero otherwise.
// If it fails, use escpos_last_error() to get the error code.
extern int escpos_printer_config(escpos_printer *printer, const escpos_config * const config);
// Destroys the printer and deallocates its memory
//
// Params:
// - printer: the printer
extern void escpos_printer_destroy(escpos_printer *printer);
// Sends raw data to the printer
//
// Params:
// - printer: the printer
// - message: the data
// - len: the length of the data in bytes
//
// Return value: 0 is successful, non-zero otherwise.
// If it fails, use escpos_last_error() to get the error code.
extern int escpos_printer_raw(escpos_printer *printer, const char * const message, const int len);
// Cuts the paper
//
// Params:
// - printer: the printer
// - lines: no. of lines to feed before cutting
//
// Return value: 0 is successful, non-zero otherwise.
// If it fails, use escpos_last_error() to get the error code.
extern int escpos_printer_cut(escpos_printer *printer, const int lines);
// Feeds n lines
//
// Params:
// - printer: the printer
// - lines: no. of lines
//
// Return value: 0 is successful, non-zero otherwise.
// If it fails, use escpos_last_error() to get the error code.
extern int escpos_printer_feed(escpos_printer *printer, const int lines);
// Prints an image
//
// NOTE: This function will send the image data to the printer's buffer
// before sending the print command, instead of printing the image directly.
//
// Params:
// - printer: the printer
// - image_data: an array of width * height bytes containing the pixels in grayscale
// - width: the image width (must be at most 512)
// - height: the image height
//
// Return value: 0 is successful, non-zero otherwise.
// If it fails, use escpos_last_error() to get the error code.
extern int escpos_printer_image(escpos_printer *printer,
const unsigned char * const image_data,
const int width,
const int height);
#endif