Parallel port strobe detection

I recently found a few old parallel print servers (HP Jetdirect 300x) stashed in the back of a forgotten supplies cupboard - still boxed and unused. “Lovely” I thought, “I can do some interesting things with these beauties”. This was supposed to have been a one-nighter project after the kids have gone to bed but I have been trying to get this to work for the past week or so.

I found the following code from this forum post and I have been trying to get it to work with my Arduino Uno.

/********************************************************************************
 * PrinterCaptureInterrupt.ino
 * ------------------
 * Monitor a parallel port printer output and capture each character. Output the
 * character on the USB serial port so it can be captured in a terminal program.
 *
 * By............: Paul Jewell
 * Date..........: 29th January 2015
 * Version.......: 0.1a
 *-------------------------------------------------------------------------------
 * Wiring Layout
 * -------------
 *
 * Parallel Port Output               Arduino Input
 * --------------------               -------------
 * Name      Dir.   Pin                Name    Pin
 * ----      ----   ---                ----    ---
 * nSTROBE    >       1................INT0      2 (as interupt)
 * DATA BYTE  >     2-9.......................3-10   
 * nACK       <      10.........................11
 * BUSY       <      11.........................12
 * OutofPaper <      12................GND
 * Selected   <      13.................5v
 * GND        <>  18-25................GND
 *-------------------------------------------------------------------------------
 ********************************************************************************/

int nStrobe = 2;
int Data0   = 3;
int Data1   = 4;
int Data2   = 5;
int Data3   = 6;
int Data4   = 7;
int Data5   = 8;
int Data6   = 9;
int Data7   = 10;
int nAck    = 11;
int Busy    = 12;
int led     = 13; // use as status led

enum States {
  READY,
  BUSY,
  ACK
} State;

 

void setup()
{
  // Configure pins
  pinMode(nStrobe, INPUT_PULLUP);
 
  for (int n = Data0; n < (Data7+1); n++)
    pinMode(n, INPUT_PULLUP);
 
  pinMode(nAck, OUTPUT);
  pinMode(Busy, OUTPUT);
  pinMode(led, OUTPUT);
 
  Serial.begin(9600);
  while (!Serial) {
    ;
  }
 
  attachInterrupt(2,DataReady,LOW);
  State = READY;
  delay(1000);
  Serial.println("Initialised");
}

void loop()
{
  switch (State) {
    case READY:
      digitalWrite(Busy, LOW);
      digitalWrite(nAck,HIGH);
      digitalWrite(led, HIGH);
      break;
    case BUSY: // nStrobe signal received by interrupt handler
      digitalWrite(Busy, HIGH);
      digitalWrite(led, LOW);
      ProcessChar();
      State = ACK;
      break;
    case ACK:
      digitalWrite(nAck,LOW);
      delay(5); //milliseconds. Specification minimum = 5 us
      State = READY;
      break;
  }   
}

void DataReady()
{
  State = BUSY;
}

void ProcessChar()
{
  byte Char;
 
  Char = digitalRead(Data0) +
         (digitalRead(Data1) << 1) +
         (digitalRead(Data2) << 2) +
         (digitalRead(Data3) << 3) +
         (digitalRead(Data4) << 4) +
         (digitalRead(Data5) << 5) +
         (digitalRead(Data6) << 6) +
         (digitalRead(Data7) << 7);
         
  Serial.print((char)Char);
}

I’ve used this simple Python script for testing.

#!/usr/bin/env python
import socket

HOST = '192.168.1.69'
PORT = 9100              # The print server's 'raw' port
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
s.send("Helo Arduino")
s.close()

After considerable research I still cannot get this to work. For reference I found these really useful sites during this tortuous phase of my life - The Parallel Port – 1284 Protocol and Interfacing the Standard Parallel Port and HowStuffWorks.

I have tried the above with all four of the print servers in my possesion with no success. So I tried the following hacked version of the above sketch.

int nStrobe = 2;
int Data0   = 3;
int Data1   = 4;
int Data2   = 5;
int Data3   = 6;
int Data4   = 7;
int Data5   = 8;
int Data6   = 9;
int Data7   = 10;
int nAck    = 11;
int Busy    = 12;
int led     = 13; // use as status led

void setup()
{
  // Configure pins
  pinMode(nStrobe, INPUT_PULLUP);
 
  for (int n = Data0; n < (Data7+1); n++)
    pinMode(n, INPUT_PULLUP);
 
  pinMode(nAck, OUTPUT);
  pinMode(Busy, OUTPUT);
  pinMode(led, OUTPUT);
 
  Serial.begin(9600);
  while (!Serial) {
    ;
  }
 
  //attachInterrupt(2,DataReady,LOW);
  //State = READY;
  delay(1000);
  Serial.println("Initialised");
}

void loop()
{
  // This is a complete hack - for testing purposes only
  // Ignores nStrobe completely
  // Pretends to have detected it just to keep the print server happy
  digitalWrite(Busy, HIGH);
  ProcessChar();
  digitalWrite(Busy, LOW);
  digitalWrite(nAck,LOW);
  delayMicroseconds(20);
  digitalWrite(nAck,HIGH);
  delay(250); // Arbritary - but not too brief
}

//void DataReady()
//{
//  State = BUSY;
//}

void ProcessChar()
{
  byte Char;
 
  Char = digitalRead(Data0) +
         (digitalRead(Data1) << 1) +
         (digitalRead(Data2) << 2) +
         (digitalRead(Data3) << 3) +
         (digitalRead(Data4) << 4) +
         (digitalRead(Data5) << 5) +
         (digitalRead(Data6) << 6) +
         (digitalRead(Data7) << 7);
         
  Serial.print((char)Char);// Need something here to stop the serial readout from flying away
}

This code fools the print server to think that the 'duino has seen the nStrobe pulse… and it works! Therefore the problem lies somewhere within the interrupt thingamijig of the original code. I would be very very grateful for some pointers on this as it is eating away my limited hours on this earth. Plus I have a broken toilet to fix and a growing list of chores to do around the house and my wife is giving me grief. I really should not be wasting my time on a defunct protocol, but…

Why isn’t your state variable declared as volatile as described in the attachInterrupt() documentation?

https://www.arduino.cc/en/Reference/AttachInterrupt

Try that first.

Thanks, yes I have tried that with no success. Nick Gammon's notes on interrupts was one of my first port of calls. The variable involved in the ISR is set up as an enum type. Can this type of variable be declared as a volatile?

I'd try the following: clear BUSY, to allow the PC to send data. Wait for strobe pulse (poll or interrupt), collect data, pulse nACK.

Your nACK pulse looks very short (20µs), compared to the original one (5ms). With flying wires the pulses should be stretched, or they may got lost on the line.

Eventually, when you have collected some bytes or got a LF, set BUSY to block the PC (fake printing), and transmit the characters back over Serial.

The short nACK was sort of an experiment to see how low I could go - it seems to get through reliably. But I agree that 5ms is a more sensible delay for an arduino/spaghetti board setup. Do you think that maybe the nStrobe pulse from the print server is too short for this setup to detect?

Ah, this is very embarassing! I've finally figured it out and it was a standard schoolboy error. The original code works perfectly on my Uno by editing the following line:

attachInterrupt(2,DataReady,LOW);

to:

attachInterrupt(0,DataReady,LOW);

The first parameter of attachInterrupt() refers to the number of the interrupt and NOT the Arduino pin number. On the Uno, external interrupt 0 is on pin 2 and interrupt 1 is on pin 3.

Am I blushing right now or what?

Excitement, I guess ;-)

At least you found out yourself what was wrong, indicating a great career - says my crystal ball :-)

By the way, the parallel printer port strobe should interrupt on an elge detection, not a level detection.

Paul