Arduino virtual printer


I am trying to use the Arduino as a “Virtual Printer”, but I cannot seem to get any data from the PC.
Let me explain:
What I am trying to achieve is to use the arduino as a serial to parallel converter in order to print on an old parallel printer that cannot be attached to the current PC (I know there are usb to parallel adapters).

I have installed a “Generic / Text only printer” in Windows, connected to COM4 (my Arduino Mega).
The baud rate, flow control etc are correct.
I have tested the arduino with a serial terminal to see if it will receive/send the data to the parallel printer and it works fine,
but when using the Print to the generic text only printer, the arduino seems to receive some data, but the output of the D7-D0, STROBE lines of the parallel port are all high, so the printer does not print anything.
I double checked the lines with a Saleae Logic analyzer and they are all high.

Why doesn’t the generic driver work? Do I need to output something to the serial back to the PC to make the PC think that the data was sent successfully or to acknowledge the strobe?

void loop() {

      if (Serial.available() > 0) {
                // read the incoming byte:
                incomingByte =;

                // say what you got:

In the code above, the printByte function will only send the serial data to the parallel connector on the arduino that will be connected to the real printer.

Show all of the code, the little presented does not give enough to go on.

D7-D0 include D0, D1 the serial interface to the PC. Might be some oddity there.

Show all of the code, the little presented does not give enough to go on.

D7-D0 include D0, D1 the serial interface to the PC. Might be some oddity there.

Sorry for not including the whole code.
Here it is.
Pins 0 and 1 of the Arduino are not used for parallel port.

Centronics.ino (4.48 KB)


Coming from the parallel printer days, I think this is a great use of the Arduino, and a potentially valuable/useful project.

I have written printing software for PCL & PS, and even for the "MFPs" for HP/Canon when they first came out, but I haven't done exactly what you are talking about with the parallel protocol. I did a tiny amount of reading, though, and came up with 2 reference docs that you may already have:

Interfacing the Standard Parallel Port USB Device Class Definition for Printing Devices

There is a wide variety of parallel ports (protocols might be a better way of saying that, like the older Centronics, ...), but reading the first doc above, the following caught my eye on page 2:

Extended and Enhanced Parallel Ports use additional hardware to generate and manage handshaking. To output a byte to a printer (or anything in that matter) using compatibility mode, the software must.

  1. Write the byte to the Data Port.
  2. Check to see is the printer is busy. If the printer is busy, it will not accept any data, thus any data which is written will be lost.
  3. Take the Strobe (Pin 1) low. This tells the printer that there is the correct data on the data lines. (Pins 2-9)
  4. Put the strobe high again after waiting approximately 5 microseconds after putting the strobe low. (Step 3)

This limits the speed at which the port can run at. The EPP & ECP ports get around this by letting the hardware check to see if the printer is busy and generate a strobe and /or appropriate handshaking. This means only one I/O instruction need to be performed, thus increasing the speed. These ports can output at around 1-2 megabytes per second. The ECP port also has the advantage of using DMA channels and FIFO buffers, thus data can be shifted around without using I/O instructions.

Perhaps this explanation of handshaking can help point you to joy?

The printByte() has two while-loops. You could print more to the serial monitor. For example:

  Serial.print("printByte first while loop... ");
  while(digitalRead(busy) == HIGH) {
    // wait for busy to go low

There is no debugging, and the serial monitor is the best you have.

Perhaps you can set a timeout for those while loops with a counter and delay or millis.

The printByte() has two while-loops.
You could print more to the serial monitor.

There is no debugging, and the serial monitor is the best you have.

Perhaps you can set a timeout for those while loops with a counter and delay or millis.

Unfortunately I cannot connect to the serial port during printing, as Windows thinks that there is a serial printer attached by using the Generic Text-only driver. So serial debugging during printing is out of the question. Maybe I can hack in something like the Saleae Logic analyzer to sniff the serial lines… :smiley:

Parallel printer has been discussed in the past serveral times, these threads I could dig up might provide an interesting read.

Also I created a base class ParPrinter that implemented the print interface, that makes it easy to print all standard variable types and all that implement the Printable interface.

Do you use an Arduino Mega 2560 ? It has three spare serial ports. You really need the serial monitor while it is running. There are many usb-serial-ttl modules that you can connect to another serial port of the Arduino Mega 2560.

When you buy a usb-serial-ttl module, buy a 5V version. Most have 3.3V signals. When you have a Arduino Uno, you can put an empty sketch in it (only the setup and loop functions which are empty) and use the usb-serial chip of the Arduino Uno.

Meanwhile, set a timeout in those while-loops with an error. You could turn on the led at pin 13 or make it blink when there is an error. You could even use Serial1 as debug output, and connect the Saleae to the digital signals of that serial port and let it translate to readable text.

This is the pins of the Arduino Mega 2560:

Thanks a lot, @Koepel!

I used an FTDI adapter on the Serial1 port and added an echo whenever the main serial receives data from the "Generic Text" printer driver. Also, I added a startup message to print on Serial1 after finishing the void setup().

Curiously enough, when sending a print job through Notepad, the Arduino resets itself, as the startup message is printed to the Serial1 port. So that's the reason it doesn't work. No matter what I do, when sending the print job through the generic printer driver, Arduino resets.

So, as a next thought, I reversed the two serial ports. Serial (USB) to be the local echo, and Serial1 to be the printer port connected to the "Generic Text" printer. Now it works! Everything that must be printed is echoed to the Serial (USB) port.

Hm... It seems to me that the printer driver controls the DTR line, which in fact will reset the Arduino, rendering the next sent bytes useless.

Is there any way I can disable resetting on the DTR line? Or to disable the DTR toggling from within the Windows Driver? (note that the flow control RTS/CTS is disabled, so no issue there)

Did you try Rob Tillaart's ParPrinter ? I have several of his libraries in use.

When the Arduino IDE wants to upload a sketch, the DTR and CTS give a signal to reset the Arduino board. The Arduino boards starts with the bootloader and then the Arduino IDE has half a second to connect to the bootloader to upload a sketch. The "Serial" (pin 0 and 1) is for uploading a sketch and the serial monitor. You can to do with the other serial ports what you want.

When the Arduino IDE is not used, but a terminal program or printer driver, then it is not clear what the DTR will do. Sometimes activating the serial port will do something with the DTR. If you disable the handshake, the DTR might still do something. Once the serial port is initialized, then those signals work as expected, but the activating or initializing the serial port is kind of foggy.

If you want to use the Arduino board as it is, and use "Serial" for the printer, then you can disable the reset. But I don't like that ! Can you keep the extra usb-serial module with the Arduino board ? That makes everything easier. You can remove the 100nF capacitor between DTR and /RESET (You can no longer upload a sketch). You can add a capacitor of 10µF between /RESET and GND, it will eat the charge from the 100nF. Just stick its wires in the connectors. Does your Mega 2560 board have a solder pad "RESET-EN" ? You can cut it with a knife: Or with a resistor. It is described here: Click "Documentation" tab and search for 'trace'.

Oh, I get it now. Thanks for suggestions. So I will probably put an 10uF cap between the reset and ground to make it work and that should do the trick.

I haven't tried Rob Tillaart's ParPrinter because it looks like no more than what I already have.

For anyone interested, the project code is here:

Thanks for sharing!