Arduino to Printer through Parallel Port

Looking around on the forums for some hints on this topic, and finding none, I worked out the details from scratch. I figured somebody else might want to do this someday, and so if that's you, here's what worked for me. Hope it helps.

The goal is to use an Arduino to control an HP printer. We are talking standard, desktop printers here, not thermal-receipt-printers or 3D printers.
The printer in question needs to have a parallel/centronics port (Parallel port - Wikipedia). I used an HP Deskjet 952C; the protocol should work with most similar HP printers. No promises for non-HP, but it's worth a try.

The port on the printer is actually called a Centronics connector. If you have a cable to connect the printer to the computer, look at the other end - this is the actual parallel port type connector. You can wire in your Arduino at either end, but the pin-outs are slightly different depending on which end you use:

Pin # (Parallel Port Connector) Pin # (Centronics Connector) Signal Name Signal Type
1 1 nStrobe (input)
2 2 Data pin 0 (input)
3 3 Data pin 1 (input)
4 4 Data pin 2 (input)
5 5 Data pin 3 (input)
6 6 Data pin 4 (input)
7 7 Data pin 5 (input)
8 8 Data pin 6 (input)
9 9 Data pin 7 (input)
10 10 nAck (output)
11 11 busy (output)
18-25 19-30 Ground (-)

Connect each of these pins to your Arduino as follows (I did this by splicing into the wire, but there's probably better methods):
nStrobe -> arduino pin 2
data_0 -> arduino pin 3
data_1 -> arduino pin 4
data_2 -> arduino pin 5
data_3 -> arduino pin 6
data_4 -> arduino pin 7
data_5 -> arduino pin 8
data_6 -> arduino pin 9
data_7 -> arduino pin 10
nAck -> arduino pin 11
busy -> arduino pin 12

It's that simple. Now just run this code on the Arduino:

const int startup_charsPerLine = 80;
const int startup_num_lines = 2;
byte startup_message[startup_num_lines][startup_charsPerLine] = {
  "This is the startup message. It prints whenever",
  "the Arduino is reset.",
};

const int charsPerLine = 80;   // this is the max # of chars per line
const int num_lines = 10;
byte message[num_lines][charsPerLine] = {
  "    ",   // blank line
  "This is the normal message. It prints whenever",
  "you connect pin 14 (analog 0) to GND.",
  "  ",  // blank line
  "------------------------------------------------------------------------------",   // a spiffy line
  "1) More message content",
  "  ",
  "II) you can have up to about 80 chars per line ",
  "  ",
  "------------------------------------------------------------------------------",
};

// parallel port pin# = arduino pin#
const int nStrobe = 2;
const int data_0 = 3;
const int data_1 = 4;
const int data_2 = 5;
const int data_3 = 6;
const int data_4 = 7;
const int data_5 = 8;
const int data_6 = 9;
const int data_7 = 10;
const int nAck = 11;
const int busy = 12;

const int strobeWait = 2;   // microseconds to strobe for


void setup() {
  Serial.begin(9600);

  pinMode(nStrobe, OUTPUT);      // is active LOW
  digitalWrite(nStrobe, HIGH);   // set HIGH
  pinMode(data_0, OUTPUT);
  pinMode(data_1, OUTPUT);
  pinMode(data_2, OUTPUT);
  pinMode(data_3, OUTPUT);
  pinMode(data_4, OUTPUT);
  pinMode(data_5, OUTPUT);
  pinMode(data_6, OUTPUT);
  pinMode(data_7, OUTPUT);

  pinMode(nAck, INPUT);     // is active LOW
  pinMode(busy, INPUT);  

  pinMode(13, OUTPUT);
  pinMode(14, INPUT);   // analog pin 0 on duemilanove and uno
  digitalWrite(14, HIGH); // enable pull-up
  
  delay(1000);
  
  resetPrinter();
  
  printStartupMessage();
  
  resetPrinter();
  
  Serial.println("Delay for 5 sec");
  delay(5000);
  
  Serial.println("Startup complete");
}

void loop() {
 while(digitalRead(14) == HIGH) {
   // wait
 }

  resetPrinter();

  printMessage();

  resetPrinter();
}

void printByte(byte inByte) {
  while(digitalRead(busy) == HIGH) {
    // wait for busy to go low
  }

  int b0 = bitRead(inByte, 0);
  int b1 = bitRead(inByte, 1);
  int b2 = bitRead(inByte, 2);
  int b3 = bitRead(inByte, 3);
  int b4 = bitRead(inByte, 4);
  int b5 = bitRead(inByte, 5);
  int b6 = bitRead(inByte, 6);
  int b7 = bitRead(inByte, 7);

  digitalWrite(data_0, b0);        // set data bit pins
  digitalWrite(data_1, b1);
  digitalWrite(data_2, b2);
  digitalWrite(data_3, b3);
  digitalWrite(data_4, b4);
  digitalWrite(data_5, b5);
  digitalWrite(data_6, b6);
  digitalWrite(data_7, b7);

  digitalWrite(nStrobe, LOW);       // strobe nStrobe to input data bits
  delayMicroseconds(strobeWait);
  digitalWrite(nStrobe, HIGH);

  while(digitalRead(busy) == HIGH) {
    // wait for busy line to go low
  } 
}

void resetPrinter() {
  Serial.println("Reseting printer...");
  printByte(27); // reset printer
  printByte('E');
  Serial.println("Printer Reset"); 
}

void printMessage() {
  digitalWrite(13, HIGH);
  for(int line = 0; line < num_lines; line++) {  
    for(int cursorPosition = 0; cursorPosition < charsPerLine; cursorPosition++) {
      byte character = message[line][cursorPosition];
      printByte(character);
      delay(1);
    }
    printByte(10); // new line
    printByte(13); // carriage return
  }
  digitalWrite(13,LOW);
}

void printStartupMessage() {
  Serial.println("Print start-up mssage");
  digitalWrite(13, HIGH);
  for(int line = 0; line < startup_num_lines; line++) {  
    for(int cursorPosition = 0; cursorPosition < startup_charsPerLine; cursorPosition++) {
      byte character = startup_message[line][cursorPosition];
      printByte(character);
      //      delay(1);
    }
    printByte(10); // new line
    printByte(13); // carriage return
    Serial.print("Line ");
    Serial.print(line);
    Serial.println(" printed.");
  }
  Serial.println("Startup message printed");
  digitalWrite(13,LOW);
}

It should print the startup message when the Arduino is turned on or reset. To print the other message, briefly connect pin 14 (analong 0 on Arduino Uno) to GND.
Keep in mind that the printer needs to be on, that means plugged into an outlet. The Arduino will not power the printer.

If you are successful getting your printer working with the Arduino, please let us know what model of printer it was, and any tips. If you have any questions, feel free to ask.

Cheers,
-Bob

Sounds good,

Maybe you can make a playground article from this?

Worked perfectly!
-Canon scaaning device.
But how can i control the scanner with the Arduino sutch as move the scan bar or turn on the scan light?
Thanks!

WIth direct port manipulation - Arduino Reference - Arduino Reference - the code could be made simpler

the numbers of the pins should change a bit, especially the datapins could match PORTD .

Or keeping the code similar

  int b0 = bitRead(inByte, 0);
  int b1 = bitRead(inByte, 1);
 ...
  int b7 = bitRead(inByte, 7);

  digitalWrite(data_0, b0);        // set data bit pins
...
  digitalWrite(data_7, b7);

could be written as

digitalWrite(data_0, inbyte & 0x01);
digitalWrite(data_1, inbyte & 0x02);
digitalWrite(data_2, inbyte & 0x04);
digitalWrite(data_3, inbyte & 0x08);
digitalWrite(data_4, inbyte & 0x10);
digitalWrite(data_5, inbyte & 0x20);
digitalWrite(data_6, inbyte & 0x40);
digitalWrite(data_7, inbyte & 0x80);

Finally the HP could make a nice class so one could call: [inherit interface from print class?]

HP.reset();
HP.print(char);
HP.println(string);
HP.print(int);
HP.linefeed();
HP.formfeed();
HP.Escape("E"); // easy way to print escaped codes
etc

@wouter
You need to have the command set of the Cannon scanning device to be able to control it with arduino. You might need to build a sniffer first to capture the commands when the scanner is attached to a PC or MAC. Being familiar with TWAIN drivers could help to understand how scanner protocols work in general. No easy stuff, not too difficult either but not a one week project imho.

Any ideas on how to incorporate this with an Internet of Things printer? I already have a parallel receipt printer that would work great for this.

Worked well for HP Deskjet 670C
Nice work!!!
Thanks

This will be great for sending ESC/P2 codes to Epson dot matrix printers.

This will be great for sending ESC/P2 codes to Epson dot matrix printers.

Sure why not, you can get a similar class as the HP above

Epson.reset();
Epson.print(char);
Epson.println(string);
Epson.print(int);
Epson.linefeed();
Epson.formfeed();
Epson.Escape("E"); // easy way to print escaped codes
Epson.Escape(byte byte byte); // overloaded with 1..5 bytes

there might even be a printer base class that defines the interface and derive Epson (or HP) from that ...

The real challenge now will be to make a centronic port for usb printer, so I can take my old 386 out of the box, and print with brand new printer using Parallel port;) Kidding of coarse.

To bad those printer has disappear, cause a SPI version of this project, to save some pin for tv keybord ect, would make a great centronic shield.

Bravo by the way.

works well with my old fujitsu DL3300!

but i wonder how i can send messages over serial to print them in the "normal message"

i would appreciate a little help

WOW! This works great on an OKI Microline 192 (my school have a ton of them, and I can have as many as I want!)

But how can I print my own text? I know I can edit the message function, but I'm planing to make a door logging machine.
Every time a person passes the door, it's going to write the time, and how many passages there have been.

If it was possible to use the serial.write function to enter text to the printer, that would be great!

It works for my EPSON LQ-570.
Great job!

How about reversing the setup. Would like to read from a parallel port and have it store the received data in a string. Any ideas?

you need to read the 8 bits when the clock line (aka strobe) changes and merge them into a byte.

Think it is best to connect the strobe line to one of the interrupt pins and do direct port manipulation to read as fast as possible.

I really need code to make me able to serial print the characters that the computer sends to epson lx-300 ii dot matrix printer through the parallel port.
All what i want to do is to capture the characters that the printer will print on the paper only (without the escape sequence characters)

Note: i need to do that with arduino uno when the printer is already connected to the computer i'll not disconnect the printer)

Thanks for any one could help.

Working perfect on Epson LX-300 matricial printer.

Thnks!.

Hello everyone , I wonder if somehow I could print using a single needle. I want to develop a cheaper solution for a punch press .

Reduced it to 5 needed pins on the Arduino and a little bit of external circuitry.
If anybody is interested I can post code and schematics :slight_smile:

To see how problems can be solved is always interesting and people can learn from it.

So, please post your solution!

I tested this with my HP Deskjet 400 printer and it works great!
But I have one big question:
I want to make a typewriter-like arduino application that will receive characters through serial port and send them to the printer.
When writing the code, it seems that the printer doesn't print immediately as it receives the characters, rather it buffers them and prints only when receiving a certain number of lines or a printer reset.
Can I force it to somehow print each line/character exactly when it is received? So to act like a real typewriter?
Thanks!