Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Bunch o' changes #3

Closed
wants to merge 29 commits into from

4 participants

James Adam Pieter Paint Your Dragon
James Adam

Hi there,

I've been tinkering with this library, and have come up with a few changes that you might be interested in. In summary, they revolve around:

  • removing some of the duplicate handing for ARDUINO >= 100 vs older libraries
  • using the print mode mask to set some of the formatting (I'm not sure why there are two ways of doing this as outlined in the datasheet, but there you go...)
  • improve the printBitmap support and add some scripts to help people produce bitmaps.

Let me know what you think - I won't be offended if you disapprove :)

lazyatom added some commits
James Adam lazyatom Allow flushing of print data.
This is useful when, for example, debugging, if you want to ensure that something is printed without sending a line feed.
31a1100
James Adam lazyatom Clean up whitespace.
Delete trailing whitespace and change tabs into spaces.
ae5984d
James Adam lazyatom Avoid repetition for Serial definition. d57130c
James Adam lazyatom Use a macro to abstract Arduino version differences. 2757dcb
James Adam lazyatom More whitespace tweaks. db63559
James Adam lazyatom Reset the printer using the direct mechanism. 4e6325a
James Adam lazyatom Add normal() method.
I'm just following what's in the codesheet, really.
8f1e6c9
James Adam lazyatom Use printMode mask to consistently set formatting.
I've also added upside-down, strike and double width modes this way.
294d8fb
James Adam lazyatom Allow printing of larger bitmaps.
The maximum number of rows that can be printed at one time is 255 (since this is the byte limit), so we can print larger bitmaps by splitting them into chunks.
6010b56
James Adam lazyatom We can control underline weight. eed4f41
James Adam lazyatom Add helper script for producing bitmaps. 346206d
James Adam lazyatom Add a script to help generate custom bitmaps.
Sometimes you want the bitmap to be pixel-perfect.
d5fdbf2
James Adam lazyatom More compact underline definition. 93aee69
James Adam lazyatom Minor refactoring of underline.
I don't believe sending the byte 10 (newline) is necessary, partially because sending even the remaining bytes seems to result in a newline.
8bdd3d4
James Adam lazyatom Misc fixes.
I forgot to include definitions for strike methods, and this default argument should be in the cpp implementation.
c8fac10
James Adam lazyatom Removed incorrect comment.
I believe an older copy of this library hanging around was causing me to see incoherent behaviour.
bdb2803
James Adam lazyatom Don't print a newline when stopping bold.
It's useful to be able to make only certain words bold without interupting a sentence.
8e98391
James Adam lazyatom Add a command to feed by pixel rows.
And an explanation of why the normal feed doesn't use the command in the datasheet.
6698e8c
James Adam lazyatom Add more clarity to wake/sleep/offline/online.
It's not clear from the datasheet if putting the printer offline also puts the board into a low power consumption mode, so I've renamed the methods to more closely correspond to the guidlines of the datasheet.
771810b
James Adam lazyatom No reason not to use writeBytes here. 4e71ae6
James Adam lazyatom Add a command to print the built-in test page. eb23fc1
James Adam lazyatom The rows are bytes, not bits. 0cc508a
James Adam lazyatom Allow subclasses to use these methods and variables. 968502e

cool, thanks for tinkering!

printing larger images works now! nearly, cause now there seems to be a problem with images larger than 255 in height.
it prints but image is scrambled as soon as it gets over the 255th pixel.
i've seen you made some more changes recently, maybe you already fixed it, will check and report!

James Adam

Yeah, I believe I fixed that in commit 0cc508a (it was a simple mistake really!). If you're still having problems with the latest commit, let me know and we can investigate.

printing images works great now, thanks for the fix!

Pieter

I have some problems generating bitmaps. Any pointers for OS X without ruby? Maybe a sample .cpp file? thanks

James Adam
Pieter

of course I have ruby but I'm not an 'user'.
but I managed to get it working on OS X Lion.
I've generated a few files and I'll print them first thing in the morning.
thank you.

what's the maximum width and height for bitmaps?
did anyone test it to the extreme?

James Adam
Pieter

Bitmaps up to 256 px wide are no problem. http://instagr.am/p/HWLvpEs0NN/
But when I try to print wider ones in 8px increments, I get broken results.
It prints a few line ok, but the rest is just random characters.
One last hint?

I'll try out your SD lib as soon as I can.

Pieter

update:
the error occurs with large (+256px wide) dark (a lot of black) bitmaps.
light images print perfectly.

did you try lazyatoms fork:
https://github.com/lazyatom/Thermal-Printer-Library

i had problems printing wide images with the adafruit lib...

James Adam

@mezelve if you send me the CPP file of the image you're seeing problems with, I'll try and replicate it on my printer soon...

James Adam lazyatom reopened this
James Adam

It would be great to hear from @adafruit about their thoughts regarding this pull request; I know they've made some recent changes that would at least require a rebase, and some of the other changes might also not be in line with their thinking.

Pieter

@kabrio that's the lib I'm using.

@lazyatom I don't have the CPP file at hand. It basically my avatar (https://fbcdn-sphotos-a.akamaihd.net/hphotos-ak-snc6/167530_10150179702008709_612493708_8754841_8375756_n.jpg). I resized the image to 384x384 and then converted it with image_to_bytes.

James Adam lazyatom Include printing from a Stream instance.
This allows us to print from (for example) an SD file, or an Ethernet client, without
needing to require either.

The new  method expects the width and height to be
encoded in the first four bytes of the stream - see  for an example.
82dacbd
James Adam

@mezelve I've just tried printing your avatar, and it seemed to work fine for me (see http://dl.dropbox.com/u/327514/mezeive_avatar_printer.jpg for the output). Perhaps you can put your sketch in a gist for me to check? Incidentally, I think this should probably be an issue on my fork, rather than a conversation in the pull request.

lazyatom added some commits
James Adam lazyatom Allow setting of heat time by users.
If this is too high, certain papers (like the one from Staples) will stick against the
print head, spoiling printouts.
1eeac27
James Adam lazyatom Whoops, typo. c45699a
James Adam lazyatom Make dithering optional, and improve image output.
Using pixel intensity is more sensible, and not dithering images that are already monochrome also increasing picture quality.

We can also handle an optional output filename.
33b8e97
Paint Your Dragon

Merged the lot. Thanks for all your improvements...and your patience!

James Adam
kabrio commented on 82dacbd

See where for example??

How do you encode width and height into the first four bytes.

Explanations would be superhelpful!

Apologies, it looks like the commit message got screwed up. The last line should read:

The new printBitmap(Stream *stream) method expects the width and height to be
encoded in the first four bytes of the stream - see image_to_file for an example.

In other words, the Ruby script at the bottom of this commit includes the example of encoding the width and height.

great, thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Showing 29 unique commits by 1 author.

Jan 18, 2012
James Adam lazyatom Allow flushing of print data.
This is useful when, for example, debugging, if you want to ensure that something is printed without sending a line feed.
31a1100
Jan 19, 2012
James Adam lazyatom Clean up whitespace.
Delete trailing whitespace and change tabs into spaces.
ae5984d
James Adam lazyatom Avoid repetition for Serial definition. d57130c
James Adam lazyatom Use a macro to abstract Arduino version differences. 2757dcb
James Adam lazyatom More whitespace tweaks. db63559
James Adam lazyatom Reset the printer using the direct mechanism. 4e6325a
James Adam lazyatom Add normal() method.
I'm just following what's in the codesheet, really.
8f1e6c9
James Adam lazyatom Use printMode mask to consistently set formatting.
I've also added upside-down, strike and double width modes this way.
294d8fb
James Adam lazyatom Allow printing of larger bitmaps.
The maximum number of rows that can be printed at one time is 255 (since this is the byte limit), so we can print larger bitmaps by splitting them into chunks.
6010b56
James Adam lazyatom We can control underline weight. eed4f41
James Adam lazyatom Add helper script for producing bitmaps. 346206d
Jan 20, 2012
James Adam lazyatom Add a script to help generate custom bitmaps.
Sometimes you want the bitmap to be pixel-perfect.
d5fdbf2
James Adam lazyatom More compact underline definition. 93aee69
James Adam lazyatom Minor refactoring of underline.
I don't believe sending the byte 10 (newline) is necessary, partially because sending even the remaining bytes seems to result in a newline.
8bdd3d4
James Adam lazyatom Misc fixes.
I forgot to include definitions for strike methods, and this default argument should be in the cpp implementation.
c8fac10
James Adam lazyatom Removed incorrect comment.
I believe an older copy of this library hanging around was causing me to see incoherent behaviour.
bdb2803
James Adam lazyatom Don't print a newline when stopping bold.
It's useful to be able to make only certain words bold without interupting a sentence.
8e98391
Jan 21, 2012
James Adam lazyatom Add a command to feed by pixel rows.
And an explanation of why the normal feed doesn't use the command in the datasheet.
6698e8c
James Adam lazyatom Add more clarity to wake/sleep/offline/online.
It's not clear from the datasheet if putting the printer offline also puts the board into a low power consumption mode, so I've renamed the methods to more closely correspond to the guidlines of the datasheet.
771810b
James Adam lazyatom No reason not to use writeBytes here. 4e71ae6
James Adam lazyatom Add a command to print the built-in test page. eb23fc1
Jan 28, 2012
James Adam lazyatom The rows are bytes, not bits. 0cc508a
James Adam lazyatom Allow subclasses to use these methods and variables. 968502e
Feb 14, 2012
James Adam lazyatom Output width and height from image.
Also, provide a Gemfile to install RMagick.
4862310
James Adam lazyatom Make it easier to copy to print example. 9172981
Mar 08, 2012
James Adam lazyatom Include printing from a Stream instance.
This allows us to print from (for example) an SD file, or an Ethernet client, without
needing to require either.

The new  method expects the width and height to be
encoded in the first four bytes of the stream - see  for an example.
82dacbd
James Adam lazyatom Allow setting of heat time by users.
If this is too high, certain papers (like the one from Staples) will stick against the
print head, spoiling printouts.
1eeac27
James Adam lazyatom Whoops, typo. c45699a
Mar 11, 2012
James Adam lazyatom Make dithering optional, and improve image output.
Using pixel intensity is more sensible, and not dithering images that are already monochrome also increasing picture quality.

We can also handle an optional output filename.
33b8e97
This page is out of date. Refresh to see the latest.
3  Gemfile
... ... @@ -0,0 +1,3 @@
  1 +source :rubygems
  2 +
  3 +gem "rmagick"
10 Gemfile.lock
... ... @@ -0,0 +1,10 @@
  1 +GEM
  2 + remote: http://rubygems.org/
  3 + specs:
  4 + rmagick (2.13.1)
  5 +
  6 +PLATFORMS
  7 + ruby
  8 +
  9 +DEPENDENCIES
  10 + rmagick
297 Thermal.cpp
@@ -10,58 +10,42 @@
10 10 Thermal::Thermal(int RX_Pin, int TX_Pin) {
11 11 _RX_Pin = RX_Pin;
12 12 _TX_Pin = TX_Pin;
13   -
14 13 }
15 14
16 15 void Thermal::begin() {
17   -#if ARDUINO >= 100
18   - _printer = new SoftwareSerial (_RX_Pin, _TX_Pin);
19   -#else
20   - _printer = new NewSoftSerial (_RX_Pin, _TX_Pin);
21   -#endif
  16 + begin(150);
  17 +}
  18 +
  19 +// heatTime - 80 is default from page 23 of datasheet. Controls speed of printing and darkness
  20 +void Thermal::begin(int heatTime) {
  21 + _printer = new SERIAL_IMPL(_RX_Pin, _TX_Pin);
22 22 _printer->begin(19200);
23   -
24   - heatTime = 120; //80 is default from page 23 of datasheet. Controls speed of printing and darkness
  23 +
  24 + reset();
  25 +
25 26 heatInterval = 50; //2 is default from page 23 of datasheet. Controls speed of printing and darkness
26 27 printDensity = 15; //Not sure what the defaut is. Testing shows the max helps darken text. From page 23.
27 28 printBreakTime = 15; //Not sure what the defaut is. Testing shows the max helps darken text. From page 23.
28   -
29   -
30   -#if ARDUINO >= 100
31   - _printer->write(27);
32   - _printer->write(55);
33   - _printer->write(7); //Default 64 dots = 8*('7'+1)
34   - _printer->write(heatTime); //Default 80 or 800us
35   - _printer->write(heatInterval); //Default 2 or 20us
36   -
37   -
38   - //Modify the print density and timeout
39   - _printer->write(18);
40   - _printer->write(35);
41   -
42   - int printSetting = (printDensity<<4) | printBreakTime;
43   - _printer->write(printSetting); //Combination of printDensity and printBreakTime
44   -#else
45   - _printer->print(27, BYTE);
46   - _printer->print(55, BYTE);
47   - _printer->print(7, BYTE); //Default 64 dots = 8*('7'+1)
48   - _printer->print(heatTime, BYTE); //Default 80 or 800us
49   - _printer->print(heatInterval, BYTE); //Default 2 or 20us
50   -
51   -
  29 +
  30 + writeBytes(27, 55);
  31 + writeBytes(7); //Default 64 dots = 8*('7'+1)
  32 + writeBytes(heatTime); //Default 80 or 800us
  33 + writeBytes(heatInterval); //Default 2 or 20us
  34 +
52 35 //Modify the print density and timeout
53   - _printer->print(18, BYTE);
54   - _printer->print(35, BYTE);
55   -
  36 + writeBytes(18, 35);
56 37 int printSetting = (printDensity<<4) | printBreakTime;
57   - _printer->print(printSetting, BYTE); //Combination of printDensity and printBreakTime
58   -#endif
59   -
60   - setDefault();
  38 + writeBytes(printSetting); //Combination of printDensity and printBreakTime
  39 +}
  40 +
  41 +// reset printer
  42 +void Thermal::reset() {
  43 + writeBytes(27, 64);
61 44 }
62 45
  46 +// reset formatting
63 47 void Thermal::setDefault(){
64   - wake();
  48 + online();
65 49 justify('L');
66 50 inverseOff();
67 51 doubleHeightOff();
@@ -72,12 +56,15 @@ void Thermal::setDefault(){
72 56 setSize('s');
73 57 }
74 58
75   -
76 59 void Thermal::test(){
77 60 println("Hello World!");
78 61 feed(2);
79 62 }
80 63
  64 +void Thermal::testPage() {
  65 + writeBytes(18, 84);
  66 +}
  67 +
81 68 // this is the basic function for all printing, the rest is taken care of by the
82 69 // inherited Print class!
83 70 #if ARDUINO >= 100
@@ -87,21 +74,21 @@ size_t Thermal::write(uint8_t c) {
87 74 void Thermal::write(uint8_t c) {
88 75 if (c == 0x13) return;
89 76 #endif
90   - if (c != 0xA)
  77 + if (c != 0xA)
91 78 linefeedneeded = true;
92 79 else
93 80 linefeedneeded = false;
94 81
95 82 Serial.print(" 0x");
96 83 Serial.print(c, HEX);
  84 + Serial.print(" (");
97 85 #if ARDUINO >= 100
98   - Serial.print(" ("); Serial.write(c); Serial.println(")");
99   - _printer->write(c);
  86 + Serial.write(c);
100 87 #else
101   - Serial.print(" ("); Serial.print(c, BYTE); Serial.println(")");
102   - _printer->print(c);
  88 + Serial.print(" (");
103 89 #endif
104   -
  90 + Serial.println(")");
  91 + PRINTER_PRINT(c);
105 92 delay(1);
106 93
107 94 #if ARDUINO >= 100
@@ -120,139 +107,231 @@ void Thermal::printBarcode(char * text, uint8_t type) {
120 107 write(text[i]); //Data
121 108 }
122 109 write(0); //Terminator
123   -
  110 +
124 111 delay(3000); //For some reason we can't immediately have line feeds here
125 112 feed(2);
126 113 }
127 114
  115 +void Thermal::writeBytes(uint8_t a) {
  116 + PRINTER_PRINT(a);
  117 +}
128 118
129 119 void Thermal::writeBytes(uint8_t a, uint8_t b) {
130   -#if ARDUINO >= 100
131   - _printer->write(a);
132   - _printer->write(b);
133   -#else
134   - _printer->print(a, BYTE);
135   - _printer->print(b, BYTE);
136   -#endif
  120 + PRINTER_PRINT(a);
  121 + PRINTER_PRINT(b);
137 122 }
138 123
139 124 void Thermal::writeBytes(uint8_t a, uint8_t b, uint8_t c) {
140   -#if ARDUINO >= 100
141   - _printer->write(a);
142   - _printer->write(b);
143   - _printer->write(c);
144   -#else
145   - _printer->print(a, BYTE);
146   - _printer->print(b, BYTE);
147   - _printer->print(c, BYTE);
148   -#endif
  125 + PRINTER_PRINT(a);
  126 + PRINTER_PRINT(b);
  127 + PRINTER_PRINT(c);
149 128 }
150 129
151 130 void Thermal::writeBytes(uint8_t a, uint8_t b, uint8_t c, uint8_t d) {
152   -#if ARDUINO >= 100
153   - _printer->write(a);
154   - _printer->write(b);
155   - _printer->write(c);
156   - _printer->write(d);
157   -#else
158   - _printer->print(a, BYTE);
159   - _printer->print(b, BYTE);
160   - _printer->print(c, BYTE);
161   - _printer->print(d, BYTE);
162   -#endif
  131 + PRINTER_PRINT(a);
  132 + PRINTER_PRINT(b);
  133 + PRINTER_PRINT(c);
  134 + PRINTER_PRINT(d);
  135 +}
  136 +
  137 +// === Character commands ===
  138 +
  139 +#define INVERSE_MASK (1 << 1)
  140 +#define UPDOWN_MASK (1 << 2)
  141 +#define BOLD_MASK (1 << 3)
  142 +#define DOUBLE_HEIGHT_MASK (1 << 4)
  143 +#define DOUBLE_WIDTH_MASK (1 << 5)
  144 +#define STRIKE_MASK (1 << 6)
  145 +
  146 +void Thermal::setPrintMode(uint8_t mask) {
  147 + printMode |= mask;
  148 + writePrintMode();
  149 +}
  150 +void Thermal::unsetPrintMode(uint8_t mask) {
  151 + printMode &= ~mask;
  152 + writePrintMode();
  153 +}
  154 +
  155 +void Thermal::writePrintMode() {
  156 + writeBytes(27, 33, printMode);
  157 +}
  158 +
  159 +void Thermal::normal() {
  160 + printMode = 0;
  161 + writePrintMode();
163 162 }
164 163
165 164 void Thermal::inverseOn(){
166   - writeBytes(29, 'B', 1);
  165 + setPrintMode(INVERSE_MASK);
167 166 }
168 167
169 168 void Thermal::inverseOff(){
170   - writeBytes(29, 'B', 0, 10);
  169 + unsetPrintMode(INVERSE_MASK);
  170 +}
  171 +
  172 +void Thermal::upsideDownOn(){
  173 + setPrintMode(UPDOWN_MASK);
  174 +}
  175 +
  176 +void Thermal::upsideDownOff(){
  177 + unsetPrintMode(UPDOWN_MASK);
171 178 }
172 179
173 180 void Thermal::doubleHeightOn(){
174   - writeBytes(27, 14);
  181 + setPrintMode(DOUBLE_HEIGHT_MASK);
175 182 }
176 183
177 184 void Thermal::doubleHeightOff(){
178   - writeBytes(27, 20);
  185 + unsetPrintMode(DOUBLE_HEIGHT_MASK);
179 186 }
180 187
  188 +void Thermal::doubleWidthOn(){
  189 + setPrintMode(DOUBLE_WIDTH_MASK);
  190 +}
  191 +
  192 +void Thermal::doubleWidthOff(){
  193 + unsetPrintMode(DOUBLE_WIDTH_MASK);
  194 +}
  195 +
  196 +void Thermal::strikeOn(){
  197 + setPrintMode(STRIKE_MASK);
  198 +}
  199 +
  200 +void Thermal::strikeOff(){
  201 + unsetPrintMode(STRIKE_MASK);
  202 +}
181 203
182 204 void Thermal::boldOn(){
183   - writeBytes(27, 69, 1);
  205 + setPrintMode(BOLD_MASK);
184 206 }
185 207
186 208 void Thermal::boldOff(){
187   - writeBytes(27, 69, 0);
188   - if (linefeedneeded)
189   - feed();
190   - linefeedneeded = false;
  209 + unsetPrintMode(BOLD_MASK);
191 210 }
192 211
193 212 void Thermal::justify(char value){
194 213 uint8_t pos = 0;
195   -
  214 +
196 215 if(value == 'l' || value == 'L') pos = 0;
197 216 if(value == 'c' || value == 'C') pos = 1;
198 217 if(value == 'r' || value == 'R') pos = 2;
199   -
  218 +
200 219 writeBytes(0x1B, 0x61, pos);
201 220 }
202 221
203   -
  222 +// Feeds by the specified number of lines
204 223 void Thermal::feed(uint8_t x){
  224 + // The datasheet claims sending bytes 27, 100, <x> will work
  225 + // but it feeds much much more.
205 226 while (x--)
206 227 write('\n');
207 228 }
208 229
  230 +// Feeds by the specified number of rows of pixels
  231 +void Thermal::feedRows(uint8_t rows) {
  232 + writeBytes(27, 74, rows);
  233 +}
  234 +
  235 +void Thermal::flush() {
  236 + writeBytes(12);
  237 +}
  238 +
209 239 void Thermal::setSize(char value){
210 240 int size = 0;
211   -
  241 +
212 242 if(value == 's' || value == 'S') size = 0;
213 243 if(value == 'm' || value == 'M') size = 10;
214 244 if(value == 'l' || value == 'L') size = 25;
215   -
  245 +
216 246 writeBytes(29, 33, size, 10);
217 247 // if (linefeedneeded)
218 248 // println("lfn"); //feed();
219 249 //linefeedneeded = false;
220 250 }
221 251
222   -void Thermal::underlineOff() {
223   - writeBytes(27, 45, 0, 10);
  252 +// Underlines of different weights can be produced:
  253 +// 0 - no underline
  254 +// 1 - normal underline
  255 +// 2 - thick underline
  256 +void Thermal::underlineOn(uint8_t weight) {
  257 + writeBytes(27, 45, weight);
224 258 }
225   -void Thermal::underlineOn() {
226   - writeBytes(27, 45, 1);
  259 +
  260 +void Thermal::underlineOff() {
  261 + underlineOn(0);
227 262 }
228 263
229   -void Thermal::printBitmap(uint8_t w, uint8_t h, const uint8_t *bitmap) {
230   - writeBytes(18, 42, h, w/8);
231   - for (int i=0; i<(w/8) * h; i++) {
232   -#if ARDUINO >= 100
233   - _printer->write(pgm_read_byte(bitmap + i));
234   -#else
235   - _printer->print(pgm_read_byte(bitmap + i), BYTE);
236   -#endif
  264 +void Thermal::printBitmap(int w, int h, const uint8_t *bitmap) {
  265 + if (w > 384) return; // maximum width of the printer
  266 + for (int rowStart=0; rowStart < h; rowStart += 256) {
  267 + int chunkHeight = ((h - rowStart) > 255) ? 255 : (h - rowStart);
  268 + writeBytes(18, 42);
  269 + writeBytes(chunkHeight, w/8);
  270 + for (int i=0; i<((w/8)*chunkHeight); i++) {
  271 + PRINTER_PRINT(pgm_read_byte(bitmap + (rowStart*(w/8)) + i));
  272 + }
237 273 }
238 274 }
239 275
  276 +void Thermal::printBitmap(int w, int h, Stream *stream) {
  277 + if (w > 384) return; // maximum width of the printer
  278 + for (int rowStart=0; rowStart < h; rowStart += 256) {
  279 + int chunkHeight = ((h - rowStart) > 255) ? 255 : (h - rowStart);
  280 + writeBytes(18, 42);
  281 + writeBytes(chunkHeight, w/8);
  282 + for (int i=0; i<((w/8)*chunkHeight); i++) {
  283 + PRINTER_PRINT((uint8_t)stream->read());
  284 + }
  285 + }
  286 +};
  287 +
  288 +void Thermal::printBitmap(Stream *stream) {
  289 + uint8_t tmp;
  290 + uint16_t width, height;
  291 +
  292 + tmp = stream->read();
  293 + width = (stream->read() << 8) + tmp;
  294 +
  295 + tmp = stream->read();
  296 + height = (stream->read() << 8) + tmp;
  297 +
  298 + printBitmap(width, height, stream);
  299 +};
  300 +
  301 +// Take the printer offline. Print commands sent after this will be
  302 +// ignored until `online` is called
  303 +void Thermal::offline(){
  304 + writeBytes(27, 61, 0);
  305 +}
240 306
241   -void Thermal::wake(){
  307 +// Take the printer back online. Subsequent print commands will be
  308 +// obeyed.
  309 +void Thermal::online(){
242 310 writeBytes(27, 61, 1);
243 311 }
244 312
245   -void Thermal::sleep(){
246   - writeBytes(27, 61, 0);
  313 +// Put the printer into a low-energy state immediately
  314 +void Thermal::sleep() {
  315 + sleepAfter(0);
  316 +}
  317 +
  318 +// Put the printer into a low-energy state after the given number
  319 +// of seconds
  320 +void Thermal::sleepAfter(uint8_t seconds) {
  321 + writeBytes(27, 56, seconds);
  322 +}
  323 +
  324 +// Wake the printer from a low-energy state. This command will wait
  325 +// for 50ms (as directed by the datasheet) before allowing further
  326 +// commands to be send.
  327 +void Thermal::wake() {
  328 + writeBytes(255);
  329 + delay(50);
247 330 }
248 331
249 332 ////////////////////// not working?
250 333 void Thermal::tab(){
251   -#if ARDUINO >= 100
252   - _printer->write(9);
253   -#else
254   - _printer->print(9, BYTE);
255   -#endif
  334 + PRINTER_PRINT(9);
256 335 }
257 336 void Thermal::setCharSpacing(int spacing) {
258 337 writeBytes(27, 32, 0, 10);
63 Thermal.h
@@ -11,43 +11,67 @@
11 11 #endif
12 12
13 13 #define UPC_A 0
14   -#define UPC_E 1
  14 +#define UPC_E 1
15 15 #define EAN13 2
16   -#define EAN8 3
  16 +#define EAN8 3
17 17 #define CODE39 4
18   -#define I25 5
19   -#define CODEBAR 6
20   -#define CODE93 7
21   -#define CODE128 8
  18 +#define I25 5
  19 +#define CODEBAR 6
  20 +#define CODE93 7
  21 +#define CODE128 8
22 22 #define CODE11 9
23 23 #define MSI 10
24 24
  25 +#if ARDUINO >= 100
  26 + #define SERIAL_IMPL SoftwareSerial
  27 + #define PRINTER_PRINT(a) _printer->write(a);
  28 +#else
  29 + #define SERIAL_IMPL NewSoftSerial
  30 + #define PRINTER_PRINT(a) _printer->print(a, BYTE);
  31 +#endif
  32 +
25 33
26 34 class Thermal : public Print {
27 35 public:
28 36
29 37 Thermal(int RX_Pin, int TX_Pin); // constructor
30 38 void begin();
  39 + void begin(int heatTime);
  40 + void reset();
31 41 void setDefault();
32 42 void test();
  43 + void testPage();
  44 +
33 45 #if ARDUINO >= 100
34 46 size_t write(uint8_t c);
35 47 #else
36 48 void write(uint8_t c);
37 49 #endif
38 50
  51 + void normal();
39 52 void inverseOn();
40 53 void inverseOff();
  54 + void upsideDownOn();
  55 + void upsideDownOff();
41 56 void doubleHeightOn();
42 57 void doubleHeightOff();
  58 + void doubleWidthOn();
  59 + void doubleWidthOff();
43 60 void boldOn();
44 61 void boldOff();
45   - void underlineOn();
  62 + void underlineOn(uint8_t weight=1);
46 63 void underlineOff();
47   -
  64 + void strikeOn();
  65 + void strikeOff();
  66 +
48 67 void justify(char value);
49 68 void feed(uint8_t x = 1);
  69 + void feedRows(uint8_t);
  70 + void flush();
  71 + void online();
  72 + void offline();
50 73 void sleep();
  74 + void sleepAfter(uint8_t seconds);
51 75 void wake();
52 76
53 77 void setCharSpacing(int spacing);
@@ -57,28 +81,31 @@ class Thermal : public Print {
57 81 void printBarcode(char * text, uint8_t type);
58 82 void setBarcodeHeight(int val);
59 83
60   - void printBitmap(uint8_t w, uint8_t h, const uint8_t *bitmap);
  84 + void printBitmap(int w, int h, const uint8_t *bitmap);
  85 + void printBitmap(int w, int h, Stream *stream);
  86 + void printBitmap(Stream *stream);
61 87
62 88 // ??
63 89 void tab();
64   -
65   - private:
66   -#if ARDUINO >= 100
67   - SoftwareSerial * _printer;
68   -#else
69   - NewSoftSerial * _printer;
70   -#endif
  90 +
  91 + protected:
  92 + SERIAL_IMPL * _printer;
71 93 boolean linefeedneeded;
72   -
73 94
74 95 // little helpers to make code easier to read&use
  96 + void writeBytes(uint8_t a);
75 97 void writeBytes(uint8_t a, uint8_t b);
76 98 void writeBytes(uint8_t a, uint8_t b, uint8_t c);
77 99 void writeBytes(uint8_t a, uint8_t b, uint8_t c, uint8_t d);
78 100
  101 + int printMode;
  102 + void writePrintMode();
  103 + void setPrintMode(uint8_t mask);
  104 + void unsetPrintMode(uint8_t mask);
  105 +
79 106 int _RX_Pin;
80 107 int _TX_Pin;
81   -
  108 +
82 109 int heatTime;
83 110 int heatInterval;
84 111 char printDensity;
45 ascii_to_bytes
... ... @@ -0,0 +1,45 @@
  1 +#!/usr/bin/env ruby
  2 +
  3 +=begin
  4 +This script takes an ascii representation of an image (see
  5 +sample_ascii.txt for an example) and outputs a C++ byte
  6 +array that can be passed to Thermal::printBitmap.
  7 +
  8 +If only one argument is given, the array will be output
  9 +to STDOUT; otherwise it will be written to the file given
  10 +as the second argument.
  11 +=end
  12 +
  13 +def ascii_bitmap_to_byte_array(str)
  14 + rows = str.split.map(&:strip)
  15 + dimensions = [rows.first.length, rows.length]
  16 + pixel_stream = rows.join
  17 + bytes = []
  18 + pixel_stream.split("").each_slice(8) do |slice|
  19 + bytes << slice.map { |c| c == "." ? "0" : "1" }.join.to_i(2)
  20 + end
  21 + [bytes, dimensions]
  22 +end
  23 +
  24 +def output(bytes, dimensions, io=STDOUT)
  25 + io.puts "// image dimensions: #{dimensions.join("x")}"
  26 + io.puts "static unsigned char __attribute__ ((progmem)) image [] = {"
  27 + bytes.each_slice(dimensions[1]/8) do |slice|
  28 + slice_as_hex = slice.map { |byte|
  29 + hex = byte.to_s(16).upcase.rjust(2, "0")
  30 + "0x#{hex}"
  31 + }
  32 + io.print slice_as_hex.join(", ")
  33 + io.puts ","
  34 + end
  35 + io.puts "};"
  36 +end
  37 +
  38 +str = File.read(ARGV[0])
  39 +bytes, dimensions = ascii_bitmap_to_byte_array(str)
  40 +
  41 +if ARGV[1]
  42 + File.open(ARGV[1], "w") { |f| output(bytes, dimensions, f) }
  43 +else
  44 + output(bytes, dimensions)
  45 +end
57 image_to_bytes
... ... @@ -0,0 +1,57 @@
  1 +#!/usr/bin/env ruby
  2 +
  3 +=begin
  4 +This script uses imagemagick and RMagick to produce a dithered version of an
  5 +image, and then produces a cpp file containing an array of bytes suitable
  6 +for printing.
  7 +
  8 +The cpp files also contain a comment which is useful to visualise the printed
  9 +output; I suggest you open it in a text editor and shrink the fontsize until
  10 +each row of characters fits on the screen without wrapping.
  11 +=end
  12 +
  13 +require "rubygems"
  14 +require "bundler/setup"
  15 +require "RMagick"
  16 +include Magick
  17 +
  18 +options = {}
  19 +OptionParser.new do |opts|
  20 + opts.banner = "Usage: #{$0} [options] <input_image> [<output_name>]"
  21 +
  22 + opts.on("-d", "--dither", "Dither the image (useful for photos; less good for lineart and text)") do |s|
  23 + options[:dither] = true
  24 + end
  25 +end.parse!
  26 +
  27 +path = ARGV[0]
  28 +output_name = ARGV[1] || File.basename(path).split(".")[0...-1].join
  29 +
  30 +if options[:dither]
  31 + `convert -colorspace Gray -ordered-dither o2x2 #{path} #{output_name}.bmp`
  32 +else
  33 + `convert #{path} #{output_name}.bmp`
  34 +end
  35 +
  36 +img = ImageList.new("#{output_name}.bmp")[0]
  37 +bits = []
  38 +white = 65535
  39 +limit = white / 2
  40 +img.each_pixel { |pixel, _, _| bits << ((pixel.intensity < limit) ? 1 : 0) }
  41 +bytes = []; bits.each_slice(8) { |s| bytes << ("0" + s.join).to_i(2).to_s(16) }
  42 +File.open(output_name + ".cpp", "w") do |f|
  43 + width = img.columns
  44 + height = img.rows
  45 + f.puts "/*\nprinter.printBitmap(#{width}, #{height}, image);\n*/"
  46 + f.puts "static unsigned char __attribute__ ((progmem)) image [] = {"
  47 + bytes.each_slice(width / 8) { |slice| f.puts slice.map { |s| "0x"+s.rjust(2, "0") }.join(",") + ",//" }
  48 + f.puts "};"
  49 + f.puts
  50 + f.puts "/*"
  51 + bits.each_slice(width) do |slice|
  52 + f.puts slice.map { |b| b == 0 ? "." : "X" }.join("")
  53 + end
  54 + f.puts "*/"
  55 +end
  56 +
  57 +puts "wrote #{bytes.length} bytes to #{output_name}.cpp"
44 image_to_file
... ... @@ -0,0 +1,44 @@
  1 +#!/usr/bin/env ruby
  2 +
  3 +=begin
  4 +This script uses imagemagick and RMagick to produce a dithered version of an
  5 +image, and then produces a cpp file containing an array of bytes suitable
  6 +for printing.
  7 +
  8 +The cpp files also contain a comment which is useful to visualise the printed
  9 +output; I suggest you open it in a text editor and shrink the fontsize until
  10 +each row of characters fits on the screen without wrapping.
  11 +=end
  12 +
  13 +require "rubygems"
  14 +require "bundler/setup"
  15 +require "RMagick"
  16 +include Magick
  17 +
  18 +require 'optparse'
  19 +
  20 +options = {}
  21 +OptionParser.new do |opts|
  22 + opts.banner = "Usage: #{$0} [options] <input_image> [<output_name>]"
  23 +
  24 + opts.on("-s", "--skip-header", "Don't include the dimensions header") do |s|
  25 + options[:skip_header] = true
  26 + end
  27 +end.parse!
  28 +
  29 +path = ARGV[0]
  30 +output_name = ARGV[1] || File.join(File.dirname(path), File.basename(path).split(".")[0...-1].join)
  31 +
  32 +`convert -colorspace Gray -ordered-dither o2x2 #{path} #{output_name}.bmp`
  33 +img = ImageList.new("#{output_name}.bmp")[0]
  34 +bits = []
  35 +width = img.columns
  36 +height = img.rows
  37 +img.each_pixel { |pixel, _, _| bits << (pixel.red > 0 ? 0 : 1) }
  38 +bytes = []; bits.each_slice(8) { |s| bytes << ("0" + s.join).to_i(2) }
  39 +File.open(output_name, "w") do |f|
  40 + f.write([width,height].pack("SS")) unless options[:skip_header]
  41 + f.write(bytes.pack("C*"))
  42 +end
  43 +
  44 +puts "wrote #{bytes.length} bytes to #{output_name}"
48 sample_ascii.txt
... ... @@ -0,0 +1,48 @@
  1 +................................................
  2 +................................................
  3 +................................................
  4 +................................................
  5 +................................................
  6 +......................................XX........
  7 +.....................................XXXX.......
  8 +....................................XXXXX.......
  9 +...................................XXXXX........
  10 +.....XX...........................XXXXX.........
  11 +....XXXX.........................XXXXX..........
  12 +....XXXXX.......................XXXXX...........
  13 +.....XXXXX.....................XXXX.............
  14 +......XXXXX...................XXXXX.............
  15 +.......XXXXX.................XXXXXX.............
  16 +......X.XXXXX...............XXXX................
  17 +.........XXXXX.............XXXX.................
  18 +..........XXXXX...........XXXXX.................
  19 +...........XXXXX........XXXXXX..................
  20 +............XXXXX.......XXXX....................
  21 +............XXXXXX....XXXXX.....................
  22 +...............XXXX..XXXXX.X....................
  23 +...............XXXXXXXXXX.......................
  24 +................XXXXXXXX........................
  25 +..................XXXXXX........................
  26 +................XXXXXXX.X.......................
  27 +................XXXXXXXX........................
  28 +...............XXXXXXXXXX.......................
  29 +..............XXXX...XXXXX......................
  30 +.............XXXXXX.X..XXXX.....................
  31 +............XXXXX......XXXXX....................
  32 +...........XXXXX.........XXXX...................
  33 +..........XXXXX..........XXXXX..................
  34 +.........XXXX..............XXXXX................
  35 +........XXXXX..............XXXXXX...............
  36 +......XXXXX..................XXXX...............
  37 +.....XXXXXX...................XXXX..............
  38 +.....XXXX......................XXXXX............
  39 +....XXXX.....................X..XXXXX...........
  40 +....XXX..........................XXXX...........
  41 +.....X...........................XXXXXX.........
  42 +...................................XXXXX........
  43 +....................................XXXX........
  44 +.....................................XXX........
  45 +......................................X.........
  46 +................................................
  47 +................................................
  48 +................................................

Tip: You can add notes to lines in a file. Hover to the left of a line to make a note

Something went wrong with that request. Please try again.