Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Manual merge incorporating lazyatom's changes

  • Loading branch information...
commit 4cb8322936a3af4b4d1e58b248bbbc88cd4d5315 1 parent 3a88cd1
@PaintYourDragon PaintYourDragon authored
View
300 Adafruit_Thermal.cpp
@@ -28,60 +28,39 @@ Adafruit_Thermal::Adafruit_Thermal(int RX_Pin, int TX_Pin) {
}
void Adafruit_Thermal::begin() {
-#if ARDUINO >= 100
- _printer = new SoftwareSerial (_RX_Pin, _TX_Pin);
-#else
- _printer = new NewSoftSerial (_RX_Pin, _TX_Pin);
-#endif
+ begin(150);
+}
+
+// heatTime - 80 is default from page 23 of datasheet. Controls speed of printing and darkness
+void Adafruit_Thermal::begin(int heatTime) {
+ _printer = new SERIAL_IMPL(_RX_Pin, _TX_Pin);
_printer->begin(19200);
- // The printer can't start receiving data immediately
- // upon power up -- needs a moment to initialize. If
- // Arduino & printer are powered from the same supply,
- // they're starting simultaneously. Need to pause for
- // a moment so the printer is ready for commands.
- // (A more robust approach might be to wait in a loop
- // issuing status commands until valid response.)
- delay(500);
-
- heatTime = 240; //80 is default from page 23 of datasheet. Controls speed of printing and darkness
- heatInterval = 2; //2 is default from page 23 of datasheet. Controls speed of printing and darkness
+ reset();
+
+ heatInterval = 50; //2 is default from page 23 of datasheet. Controls speed of printing and darkness
printDensity = 15; //Not sure what the defaut is. Testing shows the max helps darken text. From page 23.
printBreakTime = 15; //Not sure what the defaut is. Testing shows the max helps darken text. From page 23.
-#if ARDUINO >= 100
- _printer->write(27);
- _printer->write(55);
- _printer->write(16); //Default 64 dots = 8*('7'+1)
- _printer->write(heatTime); //Default 80 or 800us
- _printer->write(heatInterval); //Default 2 or 20us
+ writeBytes(27, 55);
+ writeBytes(7); //Default 64 dots = 8*('7'+1)
+ writeBytes(heatTime); //Default 80 or 800us
+ writeBytes(heatInterval); //Default 2 or 20us
//Modify the print density and timeout
- _printer->write(18);
- _printer->write(35);
-
+ writeBytes(18, 35);
int printSetting = (printDensity<<4) | printBreakTime;
- _printer->write(printSetting); //Combination of printDensity and printBreakTime
-#else
- _printer->print(27, BYTE);
- _printer->print(55, BYTE);
- _printer->print(16, BYTE); //Default 64 dots = 8*('7'+1)
- _printer->print(heatTime, BYTE); //Default 80 or 800us
- _printer->print(heatInterval, BYTE); //Default 2 or 20us
+ writeBytes(printSetting); //Combination of printDensity and printBreakTime
+}
- //Modify the print density and timeout
- _printer->print(18, BYTE);
- _printer->print(35, BYTE);
-
- int printSetting = (printDensity<<4) | printBreakTime;
- _printer->print(printSetting, BYTE); //Combination of printDensity and printBreakTime
-#endif
-
- setDefault();
+// reset printer
+void Adafruit_Thermal::reset() {
+ writeBytes(27, 64);
}
+// reset formatting
void Adafruit_Thermal::setDefault(){
- wake();
+ online();
justify('L');
inverseOff();
doubleHeightOff();
@@ -92,12 +71,15 @@ void Adafruit_Thermal::setDefault(){
setSize('s');
}
-
void Adafruit_Thermal::test(){
println("Hello World!");
feed(2);
}
+void Adafruit_Thermal::testPage() {
+ writeBytes(18, 84);
+}
+
// this is the basic function for all printing, the rest is taken care of by the
// inherited Print class!
#if ARDUINO >= 100
@@ -107,21 +89,21 @@ size_t Adafruit_Thermal::write(uint8_t c) {
void Adafruit_Thermal::write(uint8_t c) {
if (c == 0x13) return;
#endif
- if (c != 0xA)
+ if (c != 0xA)
linefeedneeded = true;
else
linefeedneeded = false;
- //Serial.print(" 0x");
- //Serial.print(c, HEX);
+ Serial.print(" 0x");
+ Serial.print(c, HEX);
+ Serial.print(" (");
#if ARDUINO >= 100
- //Serial.print(" ("); Serial.write(c); Serial.println(")");
- _printer->write(c);
+ Serial.write(c);
#else
- //Serial.print(" ("); Serial.print(c, BYTE); Serial.println(")");
- _printer->print(c);
+ Serial.print(" (");
#endif
-
+ Serial.println(")");
+ PRINTER_PRINT(c);
delay(1);
#if ARDUINO >= 100
@@ -140,139 +122,231 @@ void Adafruit_Thermal::printBarcode(char * text, uint8_t type) {
write(text[i]); //Data
}
write(0); //Terminator
-
+
delay(3000); //For some reason we can't immediately have line feeds here
feed(2);
}
+void Adafruit_Thermal::writeBytes(uint8_t a) {
+ PRINTER_PRINT(a);
+}
void Adafruit_Thermal::writeBytes(uint8_t a, uint8_t b) {
-#if ARDUINO >= 100
- _printer->write(a);
- _printer->write(b);
-#else
- _printer->print(a, BYTE);
- _printer->print(b, BYTE);
-#endif
+ PRINTER_PRINT(a);
+ PRINTER_PRINT(b);
}
void Adafruit_Thermal::writeBytes(uint8_t a, uint8_t b, uint8_t c) {
-#if ARDUINO >= 100
- _printer->write(a);
- _printer->write(b);
- _printer->write(c);
-#else
- _printer->print(a, BYTE);
- _printer->print(b, BYTE);
- _printer->print(c, BYTE);
-#endif
+ PRINTER_PRINT(a);
+ PRINTER_PRINT(b);
+ PRINTER_PRINT(c);
}
void Adafruit_Thermal::writeBytes(uint8_t a, uint8_t b, uint8_t c, uint8_t d) {
-#if ARDUINO >= 100
- _printer->write(a);
- _printer->write(b);
- _printer->write(c);
- _printer->write(d);
-#else
- _printer->print(a, BYTE);
- _printer->print(b, BYTE);
- _printer->print(c, BYTE);
- _printer->print(d, BYTE);
-#endif
+ PRINTER_PRINT(a);
+ PRINTER_PRINT(b);
+ PRINTER_PRINT(c);
+ PRINTER_PRINT(d);
+}
+
+// === Character commands ===
+
+#define INVERSE_MASK (1 << 1)
+#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)
+
+void Adafruit_Thermal::setPrintMode(uint8_t mask) {
+ printMode |= mask;
+ writePrintMode();
+}
+void Adafruit_Thermal::unsetPrintMode(uint8_t mask) {
+ printMode &= ~mask;
+ writePrintMode();
+}
+
+void Adafruit_Thermal::writePrintMode() {
+ writeBytes(27, 33, printMode);
+}
+
+void Adafruit_Thermal::normal() {
+ printMode = 0;
+ writePrintMode();
}
void Adafruit_Thermal::inverseOn(){
- writeBytes(29, 'B', 1);
+ setPrintMode(INVERSE_MASK);
}
void Adafruit_Thermal::inverseOff(){
- writeBytes(29, 'B', 0, 10);
+ unsetPrintMode(INVERSE_MASK);
+}
+
+void Adafruit_Thermal::upsideDownOn(){
+ setPrintMode(UPDOWN_MASK);
+}
+
+void Adafruit_Thermal::upsideDownOff(){
+ unsetPrintMode(UPDOWN_MASK);
}
void Adafruit_Thermal::doubleHeightOn(){
- writeBytes(27, 14);
+ setPrintMode(DOUBLE_HEIGHT_MASK);
}
void Adafruit_Thermal::doubleHeightOff(){
- writeBytes(27, 20);
+ unsetPrintMode(DOUBLE_HEIGHT_MASK);
+}
+
+void Adafruit_Thermal::doubleWidthOn(){
+ setPrintMode(DOUBLE_WIDTH_MASK);
}
+void Adafruit_Thermal::doubleWidthOff(){
+ unsetPrintMode(DOUBLE_WIDTH_MASK);
+}
+
+void Adafruit_Thermal::strikeOn(){
+ setPrintMode(STRIKE_MASK);
+}
+
+void Adafruit_Thermal::strikeOff(){
+ unsetPrintMode(STRIKE_MASK);
+}
void Adafruit_Thermal::boldOn(){
- writeBytes(27, 69, 1);
+ setPrintMode(BOLD_MASK);
}
void Adafruit_Thermal::boldOff(){
- writeBytes(27, 69, 0);
- if (linefeedneeded)
- feed();
- linefeedneeded = false;
+ unsetPrintMode(BOLD_MASK);
}
void Adafruit_Thermal::justify(char value){
uint8_t pos = 0;
-
+
if(value == 'l' || value == 'L') pos = 0;
if(value == 'c' || value == 'C') pos = 1;
if(value == 'r' || value == 'R') pos = 2;
-
+
writeBytes(0x1B, 0x61, pos);
}
-
+// Feeds by the specified number of lines
void Adafruit_Thermal::feed(uint8_t x){
+ // The datasheet claims sending bytes 27, 100, <x> will work
+ // but it feeds much much more.
while (x--)
write('\n');
}
+// Feeds by the specified number of rows of pixels
+void Adafruit_Thermal::feedRows(uint8_t rows) {
+ writeBytes(27, 74, rows);
+}
+
+void Adafruit_Thermal::flush() {
+ writeBytes(12);
+}
+
void Adafruit_Thermal::setSize(char value){
int size = 0;
-
+
if(value == 's' || value == 'S') size = 0;
if(value == 'm' || value == 'M') size = 10;
if(value == 'l' || value == 'L') size = 25;
-
+
writeBytes(29, 33, size, 10);
// if (linefeedneeded)
// println("lfn"); //feed();
//linefeedneeded = false;
}
-void Adafruit_Thermal::underlineOff() {
- writeBytes(27, 45, 0, 10);
+// Underlines of different weights can be produced:
+// 0 - no underline
+// 1 - normal underline
+// 2 - thick underline
+void Adafruit_Thermal::underlineOn(uint8_t weight) {
+ writeBytes(27, 45, weight);
}
-void Adafruit_Thermal::underlineOn() {
- writeBytes(27, 45, 1);
+
+void Adafruit_Thermal::underlineOff() {
+ underlineOn(0);
}
-void Adafruit_Thermal::printBitmap(uint8_t w, uint8_t h, const uint8_t *bitmap) {
- writeBytes(18, 42, h, w/8);
- for (int i=0; i<(w/8) * h; i++) {
-#if ARDUINO >= 100
- _printer->write(pgm_read_byte(bitmap + i));
-#else
- _printer->print(pgm_read_byte(bitmap + i), BYTE);
-#endif
+void Adafruit_Thermal::printBitmap(int w, int h, const uint8_t *bitmap) {
+ if (w > 384) return; // maximum width of the printer
+ for (int rowStart=0; rowStart < h; rowStart += 256) {
+ int chunkHeight = ((h - rowStart) > 255) ? 255 : (h - rowStart);
+ writeBytes(18, 42);
+ writeBytes(chunkHeight, w/8);
+ for (int i=0; i<((w/8)*chunkHeight); i++) {
+ PRINTER_PRINT(pgm_read_byte(bitmap + (rowStart*(w/8)) + i));
+ }
}
}
+void Adafruit_Thermal::printBitmap(int w, int h, Stream *stream) {
+ if (w > 384) return; // maximum width of the printer
+ for (int rowStart=0; rowStart < h; rowStart += 256) {
+ int chunkHeight = ((h - rowStart) > 255) ? 255 : (h - rowStart);
+ writeBytes(18, 42);
+ writeBytes(chunkHeight, w/8);
+ for (int i=0; i<((w/8)*chunkHeight); i++) {
+ PRINTER_PRINT((uint8_t)stream->read());
+ }
+ }
+};
-void Adafruit_Thermal::wake(){
+void Adafruit_Thermal::printBitmap(Stream *stream) {
+ uint8_t tmp;
+ uint16_t width, height;
+
+ tmp = stream->read();
+ width = (stream->read() << 8) + tmp;
+
+ tmp = stream->read();
+ height = (stream->read() << 8) + tmp;
+
+ printBitmap(width, height, stream);
+};
+
+// Take the printer offline. Print commands sent after this will be
+// ignored until `online` is called
+void Adafruit_Thermal::offline(){
+ writeBytes(27, 61, 0);
+}
+
+// Take the printer back online. Subsequent print commands will be
+// obeyed.
+void Adafruit_Thermal::online(){
writeBytes(27, 61, 1);
}
-void Adafruit_Thermal::sleep(){
- writeBytes(27, 61, 0);
+// Put the printer into a low-energy state immediately
+void Adafruit_Thermal::sleep() {
+ sleepAfter(0);
+}
+
+// Put the printer into a low-energy state after the given number
+// of seconds
+void Adafruit_Thermal::sleepAfter(uint8_t seconds) {
+ writeBytes(27, 56, seconds);
+}
+
+// Wake the printer from a low-energy state. This command will wait
+// for 50ms (as directed by the datasheet) before allowing further
+// commands to be send.
+void Adafruit_Thermal::wake() {
+ writeBytes(255);
+ delay(50);
}
////////////////////// not working?
void Adafruit_Thermal::tab(){
-#if ARDUINO >= 100
- _printer->write(9);
-#else
- _printer->print(9, BYTE);
-#endif
+ PRINTER_PRINT(9);
}
void Adafruit_Thermal::setCharSpacing(int spacing) {
writeBytes(27, 32, 0, 10);
View
63 Adafruit_Thermal.h
@@ -25,43 +25,67 @@
#endif
#define UPC_A 0
-#define UPC_E 1
+#define UPC_E 1
#define EAN13 2
-#define EAN8 3
+#define EAN8 3
#define CODE39 4
-#define I25 5
-#define CODEBAR 6
-#define CODE93 7
-#define CODE128 8
+#define I25 5
+#define CODEBAR 6
+#define CODE93 7
+#define CODE128 8
#define CODE11 9
#define MSI 10
+#if ARDUINO >= 100
+ #define SERIAL_IMPL SoftwareSerial
+ #define PRINTER_PRINT(a) _printer->write(a);
+#else
+ #define SERIAL_IMPL NewSoftSerial
+ #define PRINTER_PRINT(a) _printer->print(a, BYTE);
+#endif
+
class Adafruit_Thermal : public Print {
public:
Adafruit_Thermal(int RX_Pin, int TX_Pin); // constructor
void begin();
+ void begin(int heatTime);
+ void reset();
void setDefault();
void test();
+ void testPage();
+
#if ARDUINO >= 100
size_t write(uint8_t c);
#else
void write(uint8_t c);
#endif
+ void normal();
void inverseOn();
void inverseOff();
+ void upsideDownOn();
+ void upsideDownOff();
void doubleHeightOn();
void doubleHeightOff();
+ void doubleWidthOn();
+ void doubleWidthOff();
void boldOn();
void boldOff();
- void underlineOn();
+ void underlineOn(uint8_t weight=1);
void underlineOff();
-
+ void strikeOn();
+ void strikeOff();
+
void justify(char value);
void feed(uint8_t x = 1);
+ void feedRows(uint8_t);
+ void flush();
+ void online();
+ void offline();
void sleep();
+ void sleepAfter(uint8_t seconds);
void wake();
void setCharSpacing(int spacing);
@@ -71,28 +95,31 @@ class Adafruit_Thermal : public Print {
void printBarcode(char * text, uint8_t type);
void setBarcodeHeight(int val);
- void printBitmap(uint8_t w, uint8_t h, const uint8_t *bitmap);
+ void printBitmap(int w, int h, const uint8_t *bitmap);
+ void printBitmap(int w, int h, Stream *stream);
+ void printBitmap(Stream *stream);
// ??
void tab();
-
- private:
-#if ARDUINO >= 100
- SoftwareSerial * _printer;
-#else
- NewSoftSerial * _printer;
-#endif
+
+ protected:
+ SERIAL_IMPL * _printer;
boolean linefeedneeded;
-
// little helpers to make code easier to read&use
+ void writeBytes(uint8_t a);
void writeBytes(uint8_t a, uint8_t b);
void writeBytes(uint8_t a, uint8_t b, uint8_t c);
void writeBytes(uint8_t a, uint8_t b, uint8_t c, uint8_t d);
+ int printMode;
+ void writePrintMode();
+ void setPrintMode(uint8_t mask);
+ void unsetPrintMode(uint8_t mask);
+
int _RX_Pin;
int _TX_Pin;
-
+
int heatTime;
int heatInterval;
char printDensity;
View
3  Gemfile
@@ -0,0 +1,3 @@
+source :rubygems
+
+gem "rmagick"
View
10 Gemfile.lock
@@ -0,0 +1,10 @@
+GEM
+ remote: http://rubygems.org/
+ specs:
+ rmagick (2.13.1)
+
+PLATFORMS
+ ruby
+
+DEPENDENCIES
+ rmagick
View
45 ascii_to_bytes
@@ -0,0 +1,45 @@
+#!/usr/bin/env ruby
+
+=begin
+This script takes an ascii representation of an image (see
+sample_ascii.txt for an example) and outputs a C++ byte
+array that can be passed to Thermal::printBitmap.
+
+If only one argument is given, the array will be output
+to STDOUT; otherwise it will be written to the file given
+as the second argument.
+=end
+
+def ascii_bitmap_to_byte_array(str)
+ rows = str.split.map(&:strip)
+ dimensions = [rows.first.length, rows.length]
+ pixel_stream = rows.join
+ bytes = []
+ pixel_stream.split("").each_slice(8) do |slice|
+ bytes << slice.map { |c| c == "." ? "0" : "1" }.join.to_i(2)
+ end
+ [bytes, dimensions]
+end
+
+def output(bytes, dimensions, io=STDOUT)
+ io.puts "// image dimensions: #{dimensions.join("x")}"
+ io.puts "static unsigned char __attribute__ ((progmem)) image [] = {"
+ bytes.each_slice(dimensions[1]/8) do |slice|
+ slice_as_hex = slice.map { |byte|
+ hex = byte.to_s(16).upcase.rjust(2, "0")
+ "0x#{hex}"
+ }
+ io.print slice_as_hex.join(", ")
+ io.puts ","
+ end
+ io.puts "};"
+end
+
+str = File.read(ARGV[0])
+bytes, dimensions = ascii_bitmap_to_byte_array(str)
+
+if ARGV[1]
+ File.open(ARGV[1], "w") { |f| output(bytes, dimensions, f) }
+else
+ output(bytes, dimensions)
+end
View
57 image_to_bytes
@@ -0,0 +1,57 @@
+#!/usr/bin/env ruby
+
+=begin
+This script uses imagemagick and RMagick to produce a dithered version of an
+image, and then produces a cpp file containing an array of bytes suitable
+for printing.
+
+The cpp files also contain a comment which is useful to visualise the printed
+output; I suggest you open it in a text editor and shrink the fontsize until
+each row of characters fits on the screen without wrapping.
+=end
+
+require "rubygems"
+require "bundler/setup"
+require "RMagick"
+include Magick
+
+options = {}
+OptionParser.new do |opts|
+ opts.banner = "Usage: #{$0} [options] <input_image> [<output_name>]"
+
+ opts.on("-d", "--dither", "Dither the image (useful for photos; less good for lineart and text)") do |s|
+ options[:dither] = true
+ end
+end.parse!
+
+path = ARGV[0]
+output_name = ARGV[1] || File.basename(path).split(".")[0...-1].join
+
+if options[:dither]
+ `convert -colorspace Gray -ordered-dither o2x2 #{path} #{output_name}.bmp`
+else
+ `convert #{path} #{output_name}.bmp`
+end
+
+img = ImageList.new("#{output_name}.bmp")[0]
+bits = []
+white = 65535
+limit = white / 2
+img.each_pixel { |pixel, _, _| bits << ((pixel.intensity < limit) ? 1 : 0) }
+bytes = []; bits.each_slice(8) { |s| bytes << ("0" + s.join).to_i(2).to_s(16) }
+File.open(output_name + ".cpp", "w") do |f|
+ width = img.columns
+ height = img.rows
+ f.puts "/*\nprinter.printBitmap(#{width}, #{height}, image);\n*/"
+ f.puts "static unsigned char __attribute__ ((progmem)) image [] = {"
+ bytes.each_slice(width / 8) { |slice| f.puts slice.map { |s| "0x"+s.rjust(2, "0") }.join(",") + ",//" }
+ f.puts "};"
+ f.puts
+ f.puts "/*"
+ bits.each_slice(width) do |slice|
+ f.puts slice.map { |b| b == 0 ? "." : "X" }.join("")
+ end
+ f.puts "*/"
+end
+
+puts "wrote #{bytes.length} bytes to #{output_name}.cpp"
View
44 image_to_file
@@ -0,0 +1,44 @@
+#!/usr/bin/env ruby
+
+=begin
+This script uses imagemagick and RMagick to produce a dithered version of an
+image, and then produces a cpp file containing an array of bytes suitable
+for printing.
+
+The cpp files also contain a comment which is useful to visualise the printed
+output; I suggest you open it in a text editor and shrink the fontsize until
+each row of characters fits on the screen without wrapping.
+=end
+
+require "rubygems"
+require "bundler/setup"
+require "RMagick"
+include Magick
+
+require 'optparse'
+
+options = {}
+OptionParser.new do |opts|
+ opts.banner = "Usage: #{$0} [options] <input_image> [<output_name>]"
+
+ opts.on("-s", "--skip-header", "Don't include the dimensions header") do |s|
+ options[:skip_header] = true
+ end
+end.parse!
+
+path = ARGV[0]
+output_name = ARGV[1] || File.join(File.dirname(path), File.basename(path).split(".")[0...-1].join)
+
+`convert -colorspace Gray -ordered-dither o2x2 #{path} #{output_name}.bmp`
+img = ImageList.new("#{output_name}.bmp")[0]
+bits = []
+width = img.columns
+height = img.rows
+img.each_pixel { |pixel, _, _| bits << (pixel.red > 0 ? 0 : 1) }
+bytes = []; bits.each_slice(8) { |s| bytes << ("0" + s.join).to_i(2) }
+File.open(output_name, "w") do |f|
+ f.write([width,height].pack("SS")) unless options[:skip_header]
+ f.write(bytes.pack("C*"))
+end
+
+puts "wrote #{bytes.length} bytes to #{output_name}"
View
48 sample_ascii.txt
@@ -0,0 +1,48 @@
+................................................
+................................................
+................................................
+................................................
+................................................
+......................................XX........
+.....................................XXXX.......
+....................................XXXXX.......
+...................................XXXXX........
+.....XX...........................XXXXX.........
+....XXXX.........................XXXXX..........
+....XXXXX.......................XXXXX...........
+.....XXXXX.....................XXXX.............
+......XXXXX...................XXXXX.............
+.......XXXXX.................XXXXXX.............
+......X.XXXXX...............XXXX................
+.........XXXXX.............XXXX.................
+..........XXXXX...........XXXXX.................
+...........XXXXX........XXXXXX..................
+............XXXXX.......XXXX....................
+............XXXXXX....XXXXX.....................
+...............XXXX..XXXXX.X....................
+...............XXXXXXXXXX.......................
+................XXXXXXXX........................
+..................XXXXXX........................
+................XXXXXXX.X.......................
+................XXXXXXXX........................
+...............XXXXXXXXXX.......................
+..............XXXX...XXXXX......................
+.............XXXXXX.X..XXXX.....................
+............XXXXX......XXXXX....................
+...........XXXXX.........XXXX...................
+..........XXXXX..........XXXXX..................
+.........XXXX..............XXXXX................
+........XXXXX..............XXXXXX...............
+......XXXXX..................XXXX...............
+.....XXXXXX...................XXXX..............
+.....XXXX......................XXXXX............
+....XXXX.....................X..XXXXX...........
+....XXX..........................XXXX...........
+.....X...........................XXXXXX.........
+...................................XXXXX........
+....................................XXXX........
+.....................................XXX........
+......................................X.........
+................................................
+................................................
+................................................
Please sign in to comment.
Something went wrong with that request. Please try again.