Parallel Thermal Receipt Printer

I’ve seen a few examples around of using the Serial versions of the common thermal receipt printer. I’ve found the parallel version quite easy to work with. My printer is a CBM1000II and I’m using an i2c I/O expander to send the bytes to a parallel port on the printer. This code uses the PFC8574 i2c chip. Most of this code is just sending the appropriate ESC/POS codes to the printer. I submit it here in case it’s useful for anyone, and also as a code review, since I’m not too keen a programmer. I think I’ll also try to turn this into a library, so I did things like making separate functions for bold and boldOff instead of passing boolean parameters to one function.

My ‘printString’ function is a bit convoluted, but I couldn’t really figure out how to know sizeof my char array and this was how I tried to count the size of the array.

Thanks for looking and any feedback is appreciated,

Here is my demo code:

#include <Wire.h>

const int strobePin = 10;
const int busyPin = 11;
const int ackPin = 12;
const int strobeWait = 10; // how long to bounce the strobe
boolean goOnce = 1;

void setup(){
   Wire.begin(0x20);
   pinMode(strobePin, OUTPUT);
   digitalWrite(strobePin, HIGH);
   pinMode(ackPin, INPUT);
   digitalWrite(ackPin, LOW);
   pinMode(busyPin, INPUT);
   digitalWrite(busyPin, LOW);
   initializePrinter();
   delay(1000);
}

void waitForPrinter(int port){
  while(digitalRead(port) == 1){
     // wait for busy to go low
  }
}

void doStrobe(){
  digitalWrite(strobePin, LOW);
  delayMicroseconds(strobeWait);
  digitalWrite(strobePin, HIGH);
}

// This is the part that works with the I/O expander
// everything else should be adaptable to other ways of setting the byte.
// write the data to the port on the I/O Expander
void setData(char text){
  Wire.beginTransmission(0x20);
  Wire.write(text);
  Wire.endTransmission();
  doStrobe();
}

void printChar(char toPrint){
  waitForPrinter(busyPin);
  setData(toPrint);
  doStrobe();
}

void initializePrinter(){
  printChar((byte)0x1B);
  printChar((byte)0x40);
}

void alignCenter(){
  printChar((byte)0x1B);
  printChar((byte)0x61);
  printChar(1);
}

void alignRight(){
  printChar((byte)0x1B);
  printChar((byte)0x61);
  printChar(2);
}

void alignLeft(){
  printChar((byte)0x1B);
  printChar((byte)0x61);
  printChar(0);
}

//Start or Cancel underline. Valid params include 0 = cancel, 1 = 1 line thick, 2 = 2 lines thick
void underline(){
  printChar((byte)0x1B);
  printChar((byte)0x2D);
  printChar(1);
}

void thickUnderline(){
  printChar((byte)0x1B);
  printChar((byte)0x2D);
  printChar(2);
}

void noUnderline(){
  printChar((byte)0x1B);
  printChar((byte)0x2D);
  printChar(0);
}


void doCut(){
  printChar((byte)0x1D);
  printChar((byte)0x56);
  printChar(49);
}

//Do the buzzer in increments of 200ms
void doBuzzer(int repeat){
  for (int i = 0; i < repeat; i++){
    printChar((byte)0x1B);
    printChar((byte)0x1E);
    printChar(49);
  }
}

//Select  the font. Valid params are 0 or 1
void fontA(){
  printChar((byte)0x1B);
  printChar((byte)0x4D);
  printChar(0);
}

void fontB(){
  printChar((byte)0x1B);
  printChar((byte)0x4D);
  printChar(1);
}

//Line feed (the number of lines to feed)
void lineFeed(int lines){
  printChar((byte)0x1B);
  printChar((byte)0x64);
  printChar(lines);
}

void boldOn(){
  printChar((byte)0x1B);
  printChar((byte)0x45);
  printChar(1);
}

void boldOff(){
  printChar((byte)0x1B);
  printChar((byte)0x45);
  printChar(0);
}

void inverse(){
  printChar((byte)0x1D);
  printChar((byte)0x42);
  printChar(1);
}

void notInverse(){
  printChar((byte)0x1D);
  printChar((byte)0x42);
  printChar(0);
}

//I don't understand the font size description in the manual.
// fontsize 00 = standard, and the size changes based on this byte,
// MSB is Horizontal and LSB is Vertical
// 22 is larger, 33 is even larger. Needs work.
void printSize(byte fontsize){
  printChar((byte)0x1D);
  printChar((byte)0x21);
  printChar(fontsize);
}

void upsideDown(){
  printChar((byte)0x1B);
  printChar((byte)0x7B);
  printChar(1);
}

void notUpsideDown(){
  printChar((byte)0x1B);
  printChar((byte)0x7B);
  printChar(0);
}

void printString(char toPrint[]){
  int wStart = 0, //start of word in string
  wEnd = 0;  //end of word
  int count = 0, //string position counter
  wordLen = 0;  //length of string
  while (toPrint[count] != '\0'){
    wStart = count;
    wordLen = 0;
    wEnd = count;
    count ++;
    wordLen++;
    for (int n = wEnd; n > (wStart - 1); n--){
      printChar(toPrint[n]);
    }
  }
}

void printDemo(){
   printString("This string of numbers should wrap:");
   lineFeed(2);

   printString("012345678901234567890123456789012345678901234567890....");
   lineFeed(2);

   fontA();
   printString("This is font A.");
   fontB();
   printString("and this is font B.");
   fontA();
   lineFeed(1);

   printString("This is normal text that is a long string");
   lineFeed(1);

   alignRight();
   printString("Right aligned");
   lineFeed(1);

   alignCenter();
   printString("Centered text");
   lineFeed(1);
   alignLeft();

   upsideDown();
   printString("Upside down text?");
   lineFeed(1);
   notUpsideDown();
   lineFeed(2);
   alignLeft();

   boldOn();
   printString("This is a string in Bold font for emphasis.");
   boldOff();
   lineFeed(1);

   underline();
   printString("Testing the underline function");
   noUnderline();
   lineFeed(1);

   inverse();
   printString("This is a Test! (Inverse)");
   notInverse();
   lineFeed(2);

   printString("this is not inverse");
   lineFeed(2);

   printSize(0x22);
   printString("This should be 3x printing");
   lineFeed(4);

   boldOn();
   underline();
   printString("and Bold");
   boldOff();
   noUnderline();
   printSize(00);
   lineFeed(10);
   doCut();
}

void loop(){
  if (goOnce){
    printDemo();
    delay(10000);
    goOnce = 0;
  }
}

discussion about creating lib from this code - http://forum.arduino.cc/index.php?topic=205629 -

I've created some libraries for working with the common parallel receipt printers. I've seen a lot of projects that use the serial printers, but I find the parallel ones are more common, and a little cheaper. The libraries aren't finished yet, Rob T. has given me a lot of good ideas about optimizing them, but they do work now, with either an 8574 IO expander or a 595-type shift register.

You can get them or have a look on github:

https://github.com/mixographer/Thermal-Printer-I2C

You can access most of the printer functions with these libraries, such as Bold, underline, different fonts, use the cutter, etc, etc.

Have fun,

jimmy

Hi there,

i tried to make this library to work with my thermal printer and my uno,with no luck.

But i´m sure my connections are ok, as i done a “manual” pin toggle and also single byte transfer via the PCF8574. I2C address is set to A2=0, A1=0, and A0=1 equals B0111001, “Strobe” line is routed to UNO pin 3 and “Busy” line is routed to UNO pin 2

After some quick & dirty testing this one prints out “test” on my thermal printer.

If i try to use the library instead the printer does nothing because i can´t set the I2C adress right, i tried to set the I2C address B0111001 but with no luck. Also 0x21 doesn´t work…

After all, i´m a bit confused as 0x20 isn´t equal to B0111000. And my B0111001 put in the

printer.begin(3, 2, B0111001);

don´t print anything neither.

Any suggestions, what i have overlooked…?

/*
 ChipTest
*/

#include <Wire.h>
#include <LiquidCrystal.h>
LiquidCrystal lcd(8, 9, 4, 5, 6, 7); // Sainsmart keypad LCD

#define PCF B0111001  // Address with !A2, !A1 and A0
                          // Note that the R/W bit is not part of this address, it´s the MSB and will be
                          // set via the wire.read/write

int strobe = 3;
int i = 1;

void setup() {
 Wire.begin();
 Serial.begin(9600);
  lcd.begin(16, 2);
  lcd.print("hello, world!");

  pinMode(strobe, OUTPUT);
  digitalWrite(strobe, HIGH);
  
}

void loop() {


 delay(100);

 lcd.setCursor(0, 1);
 lcd.print ("test");
 lcd.print (" ");
 lcd.print (i);
 lcd.print (" ");
 
 Wire.beginTransmission(PCF);
 digitalWrite(strobe, LOW);
 delay (2);
 Wire.write("t");
 lcd.print(".");
 delay (2);
 digitalWrite(strobe,HIGH);
 Wire.endTransmission();
 
 delay (10);

  Wire.beginTransmission(PCF);
 digitalWrite(strobe, LOW);
 delay (2);
 Wire.write("e");
 lcd.print(".");
 delay (2);
 digitalWrite(strobe,HIGH);
 Wire.endTransmission();
 
 delay (10);

 Wire.beginTransmission(PCF);
 digitalWrite(strobe, LOW);
 delay (2);
 Wire.write("s");
 lcd.print(".");
 delay (2);
 digitalWrite(strobe,HIGH);
 Wire.endTransmission();
 
 delay (10);

 Wire.beginTransmission(PCF);
 digitalWrite(strobe, LOW);
 delay (2);
 Wire.write("t");
 lcd.print(".");
 delay (2);
 digitalWrite(strobe,HIGH);
 Wire.endTransmission();
 
 delay (500);

i++;
 
}