Direct InkJet PCB Resist Printing

CX4200 Modifications

By Volkan Sahin

I want to explain a little bit about my Epson inkjet adventure; why and how it is started. While planning to buy a new computer, I visited local shop and decided on what to buy. They said that they have special deal on selected Epson models; up to $50 off from the retail price. Wow, not a bad idea but what about ink prices, will buying Epson ink suck my blood? Yes, definitely, but the solution is to refill. I said ok and bought a CX4200. I did some printouts on paper and transparency; its resolution seems very good but what about accuracy? On x-axis it is also very good but y-axis is not so accurate since it uses friction feed. On the other hand, if you print the same image on several sheets they seem to match each other very well.

My first problem was how to refill/reset Epson inkjet cartridges. I did some web search and found that many people said the best one was,
http://www.eddiem.com/photo/printer/chipreset/resetchip.html

Instead of buying a ready solution I have decided use Eddie's reseter and port linux version of his program to Cygwin. {ed: source} It really works very well.

I bought also refill inks from MIS-Pro, refilled the cartridges with it and reset them with my reseter.
epsoncx4200_ink.jpg
Ink Cartridges
1024x768px
24b 98.79 KB

To refill the cartridges, I opened a hole on top of cartridges and filled them with ink using syringe and then sealed the holes with hot glue. The result is perfect! No problem.

Now, I don't want to use printers for just paper/photo printing: I want to print PCB/panel or even do 3-D rapid prototyping in future. After doing some experiments on transparency, I realized that I can create a master for photo-resist/dryfilm. The resolution of the image is good enough to use it as a master but it is a hassle to do photo-resist printing; it just needs so many steps. What about direct printing?

I decided to do some experiments with the paper thin (0.005” / 130 micrometer) PCB stock I got from eBay some time ago. I did a very good cleaning on a PCB and gave it a try. Another wow, perfect! Excellent quality:

I cured the PCB by holding it about an inch above one of the burners on my stove until the ink was almost burned and decided to etch it:

Surprise! The ink is also etch resistant. This is when I first shared the results in Yahoo Homebrew_PCBs^ group. I also did some performance tests to see the limits:

and etched version:

The traces have some breakage below 3 mil because of under etching or maybe printer resolution. I am satisfied with the initial results. Till now everything was so smooth I didn't realize it is an iceberg.

I have defined my goals at this point. I don't want to loose so much time on PCB prototyping, it should be automatic as much as possible and no more hand drilling. I want to be able to do double side printing on a CNC drilled PCB since 99% of my prototypes are double sided. So, it was time to modify the printer to accept standard 1/16” (1.6mm) PCB stock.

CAUTION

These modifications can result in damage to your printer. We have no responsibility in any case. We are only sharing our experiences; use it at your own risk. If you don't have enough experience in mechanics and electronics please don't try to modify your printer. Do not contact the author with questions except via the Yahoo forum^.

I started to disassemble printer:

unassembled_cx4200_with_devboard.JPG
Unassembled CX4200 with devboard
1024x768px
24b 137.26KB

and check the sensors. The printer uses DC servos for x/y axis positioning and it has a total of 2 optical encoders on the x and y axis. It has 1 slotted optical sensor for paper end detection mounted on the rear and 1 paper sensor mounted on the ink jet head; this sensor is used to sense objects on the printing area and to find the edge of the paper. During initial scanning, the printing area is checked to make sure that there is no object around. This sensor is completely analog and uses the reflection from the black plastic in the printing area. So if you remove it you will have trouble. Instead, I built a microcontroller board to sample x/y encoders, paper sensor and paper end sensor and recorded the behavior of the printer.

After getting all the data I modified the software in the microcontroller to emulate the paper.

//*******************************************************************************
//  MSP-FET430F123 Epson CX4200 Paper Emulator 
//  Written by: Volkan Sahin
//  First Edit: May 29, 2006
//  Version: Beta
//  This software is under the GNU Public License (GPL)
//  I am not responsible for any damage that happens to your printer by using this software.
//  Use at your own risk.
//  Built with MSPGCC
//  Pressing SW2 for a long time (~ 3 secs) activates relay and Led201 starts to blink. 
//  I added  this for future use, such as to turn off the Y-axis motor.  
//  Pressing SW2 for a short period (~1 sec) and then releasing activates paper emulator. 
//  XENC0/XENC1 quadrature inputs of X-axis encoder (on inkjet head)
//  YENC0/YENC1 quadrature inputs of Y-axis encoder (on mainboard) 
//  TRAYHOME tray slot detector input
//  PDETECT Paper sensor (on inkjet head) connection.
//  PSWITCH Paper end sensor connection 
//  Connect PDETECT/PSWITCH signals in parallel to sensors on printer.
//  You can find unused variables, sorry for that, feel free to remove them.  
//******************************************************************************
#include <io.h>
#include <signal.h>
#include <stdio.h>
#include  <msp430x12x.h>
#include <string.h>
#define SYSTEMCLK  8000000
#define BAUDRATE   38400

#define TICKRATE 10000
#define CLKOUT 1000000   //Ref. Osc of MSP 8MHz & TIMER Prescaler set to 8 
#define CAPTURECONSTANT 37500 //300ms 



#define TICKCONSTANT 10 
#define UARTRXTIMEOUT 100 

#define  MASK_10  0x369   
#define  MASK_10B 0x96 


//Port 1 Connections

#define dummy  0x10    // P1.4   IO Mode = Output
#define SWIN  0x08  // P1.3   IO Mode =  INPUT 
#define MSM 0x04 // P1.2   IO Mode = Input 
#define MSP 0x02  // P1.1  IO Mode = Input

//Port 2 Connections
#define NC1    0x80  // P2.7   IO Mode = Output
#define NC0    0x40  // P2.6   IO Mode = Output
#define XENC1  0x20 // P2.5   IO Mode = INPUT 
#define YENC1  0x10  // P2.4   IO Mode =  INPUT
#define XENC0  0x08  // P2.3   IO Mode =  INPUT 
#define YENC0  0x04 // P2.2   IO Mode =  INPUT 
#define PSWITCH 0x02  // P2.1   IO Mode =  INPUT 
#define PDETECT 0x01   // P2.0   IO Mode =  INPUT 

//Port 3 Connections
#define TRAYHOME  0x10  // P3.4   IO Mode = Input 
#define RLY 0x20 // P3.5 IO Mode = Output
#define LED201 0x08  // P3.3   IO Mode = Output


//#define genclk   P1OUT ^= SCK; _NOP(); _NOP();_NOP(); _NOP(); P1OUT ^= SCK; _NOP(); _NOP();_NOP(); _NOP()

#define EVENT_PATTERN 0x01
#define EVENT_TIMER_EXPIRED 0x02
#define EVENT_SIZE_INFO 0x04
#define EVENT_TX_EMPTY  0x08
#define EVENT_UART_TX_EMPTY 0x10
#define EVENT_UART_RX_FULL 0x20
#define EVENT_UART_RX_TIMEOUT 0x40
#define EVENT_NEXT_WORD 0x80
//#define PPOS  0x9f0+0x4f8 //old setting
//#define PPOS  0xe0e
#define PPOS  0xd90
#define PPOSLOWER  0xd26
//PPOS range=0xd26 to 0xef7;mid point= 0xe0e

static volatile unsigned int timetick;
static volatile unsigned int bitcounter;
static volatile unsigned char Event_Status;
static volatile unsigned char i,wdtcnt=32;

static volatile unsigned char start = 0;
static volatile unsigned char clk,addr=0,data,bit_data=0;
static volatile unsigned char  state=0;
static volatile unsigned int totalclk=0;
//static volatile unsigned char *ptr,timercnt = 10;
static volatile unsigned char *ptr;
//volatile unsigned char startsw=0, swprev,blocksw=0,swblocktimer=0,blinktimer=0,blink=0;
volatile unsigned char startsw=0, swprev,blocksw=0,blink=0,ysensepos=0;
volatile unsigned int swblocktimer=0,blinktimer=0,timercnt=10000;
volatile unsigned char cnt=0, limit,paperdetect_prev=1, psw_prev=0;
volatile unsigned long xenc=0,swxpos,swypos;
volatile unsigned char swdetected=0, xsign=0,ysign=0;
volatile unsigned long ppos = PPOS, trayhome=0, yenc=0,yenc1,diff=0;

const unsigned char ysense[4] = {YENC0|YENC1,YENC0&~YENC1,~YENC0&~YENC1,~YENC0&YENC1} ; 
void msp_init_port(void)
{
  //Port 1 Staff
  P1OUT = 0x00;  // Initilize Port 1 
  P1DIR = 0xF7;  // 1111 0111 
  P1SEL = 0x00;  // Select Port
  //Port 2 Staff
  P2OUT = 0x00|PDETECT;  
  P2DIR = 0xC0|PSWITCH;   
  P2SEL = 0x00;  

  //Port 3 Staff
  P3OUT = 0x00;  // Initilize Port 3 
  P3DIR = 0xEF;  // 1110 1111 
  P3SEL = 0x00;  // Select Port

  BCSCTL1 =  XTS + RSEL2 + RSEL1 + RSEL0 ; // DCO Max bias
  BCSCTL2 = SELM1 + SELS+ DIVS1 + DIVS0; //0x00
  DCOCTL = 0xe0;
  
  
  TACTL = ID1 + ID0 + TASSEL1 + TACLR;              // SMCLK, clear TAR
  CCR0 = CAPTURECONSTANT;
  TACTL |= MC0;                         // Start Timer_A in up mode
  CCTL0 = CCIE;                         // CCR0 interrupt enabled
}/*msp_init_port*/

// Timer A0 interrupt service routine
interrupt (TIMERA0_VECTOR) Timer_A(void)
{

   if(!(--timercnt)){
     Event_Status |= EVENT_TIMER_EXPIRED; //Inform Event
     timercnt = 10;
   }//if
   if(!(P1IN&SWIN)){
     swblocktimer++;
     if(swblocktimer >=10) swblocktimer =10;
   }//if
   else{
     if(swprev != (P1IN&SWIN)){
       if(swblocktimer >=10){ 
         swblocktimer=0;
         blocksw = 1;
         startsw = 0;
         blink = 1;
       }//if 
       else{
         if(startsw){
           P3OUT &= ~LED201;
           startsw = 0;
         }//if
         else{
           swblocktimer=0;
           blocksw = 0;
           blink = 0;
           startsw = 1;
           P3OUT |= LED201;
         }//if
       }//if
     }//if
   }//if
   swprev = P1IN&SWIN;
   if(blink){
     if(blinktimer) blinktimer --;
     else {
       if(P3OUT&LED201) P3OUT &= ~LED201;
       else P3OUT |= LED201;
       blinktimer =0; 
     }//if 
   }//if
}// Timer A0 interrupt service routine

interrupt (PORT2_VECTOR) data_extract(void)
{
//   P3OUT |= LED201;
   if(P2IFG&XENC0){ 
     if(!xenc) xenc = 1;
     if(P2IN&XENC1){
       xenc++;
       xsign = 1;
     }//if 
     else{ 
       if(xenc) xenc--;
       xsign = 0;
       yenc1=yenc;
     }//if
   }//if
   if(P2IFG&YENC0){ 
     if(!yenc) yenc = 1;
     if(P2IN&YENC1){
       yenc++;
       ysign = 1;
       trayhome = 0;
     }//if
     else{
       if(yenc) yenc--;
       ysign = 0;
       if(!trayhome) ppos = PPOS - 400;
     }//if
   }//if
   if(yenc>0x9f0 && yenc <0x5000){
     swdetected=1;
     P2OUT |= PSWITCH;
     P2DIR |= PSWITCH;
     if(!ysign && !(P3IN&TRAYHOME) && !trayhome){
       ppos = trayhome = yenc-50;
     }//if
   }//if
   else {
     trayhome = 0;
     swdetected=0;
     ppos =PPOS;
     P2OUT &= ~PSWITCH;
     P2DIR |= PSWITCH;
   }//if
//PPOS-450 to PPOS+15 are valid
   //old if( swdetected&&(xenc <0x707) &&(xenc>0xf4) && (yenc >PPOS+15) ) {
   if( swdetected&&(xenc <0x707) &&(xenc>0xf4) && (yenc > ppos) ) {
     P2OUT &= ~PDETECT;
     P2DIR |= PDETECT;
   }//if
   else{
     P2OUT |= PDETECT;
     P2DIR |= PDETECT;
   }//if
 //  }//if
   psw_prev =P2IN&PSWITCH; 
   paperdetect_prev = P2IN&PDETECT; 
   timercnt = 50; 
//   TACTL |= TACLR;              // clear TAR
   P2IFG = 0x0;//Clear Interrupt flags of Port2
//   P3OUT &= ~LED201;
}//data_extract

// Watchdog Timer interrupt service routine
interrupt (WDT_VECTOR)  watchdog_timer(void)
{
  WDTCTL = WDTPW + WDTHOLD;         // Stop WDT
  _BIC_SR_IRQ(CPUOFF);//Wakeup  CPU
}//watchdog_timer

// USARTinterrupt service routine

void init_usart(void)
{
  U0CTL = CHAR + SWRST;
  U0TCTL = SSEL0 + TXEPT ; //Clock Source = ACLK
  U0BR1 = 0x00; //Baud Rate = 9600 bits/sec
  U0BR0 = 0xd0;
  U0MCTL = 0x00;

  ME2 |= UTXE0 + URXE0;                 // Enabled USART0 TXD/RXD
  U0CTL = CHAR;
}//init_usart  
void delay(cnt)
{
  int i;
  for(i=0;i<cnt;i++);
}


int main(void)
{ 
  unsigned char res,seq=0;
  
  msp_init_port();
  Event_Status =0;
  P2IES = 0x00;
  P2IE = YENC0|XENC0;//Enable Port2 YENC0 & XENC0 interrupts
  P2IFG = 0x0;
  WDTCTL = WDTPW + WDTHOLD;         // Stop WDT
  msp_init_port();
  swprev = P1IN&SWIN;
  psw_prev = P2IN&PSWITCH ;
  paperdetect_prev = P2IN&PDETECT; 
  BCSCTL1 |= XTS;                     
    // Wait for crystal
  do {
   IFG1 &= ~OFIFG;
   delay(255);                     // Time for flag to set
   msp_init_port();
  } while (IFG1 & OFIFG);
  _EINT();                              // Enable Interrupts
  while(1){
    IFG1 &= ~OFIFG; 
    if(blocksw){
      P2OUT = P2IN;
      P2OUT |= YENC0 + YENC1; 
      P3OUT |= RLY;
      P2DIR |= YENC0 + YENC1;
    }//if
    else {
      if(!startsw){ 
        P2DIR &= ~(YENC1 + YENC0);;
        timercnt = 10;
        swdetected = 0;
        yenc=0;
        xenc=0;
        P2DIR = 0;
        P3OUT &= ~RLY;
      }//if    
      else{ 
        P2DIR &= ~(YENC1 + YENC0);;
        P3OUT &= ~RLY;
        if((Event_Status & EVENT_TIMER_EXPIRED)){ 
          swdetected = 0;
          P2OUT &= ~PSWITCH;
          P2OUT |= PDETECT;
          P2DIR |= PSWITCH|PDETECT;
          Event_Status &= ~EVENT_TIMER_EXPIRED; 
        }//if
      }//if
    }//if
  }//while
}


On CX4200/CX4800 printers there is a paper sensor to detect objects around the printing area and to find the edge of the paper. Tray itself can not be used like paper since you need to feed tray before starting printing. You need to be sure that tray is aligned properly before starting printing. Another issue is, since tray is a metal part you can easily destroy abrasive roller during feeding. In my solution tray always exist below printing area. So you need to find a method to emulate behavior of the paper otherwise you will get paper jam error.

What I am doing is very easy, I am counting signals coming from x and y axis optical encoders and depending on position of head (x-axis) and y-axis (tray/paper) I am sending high/low signal in parallel to paper sensor. Paper sensor on inkjet head and paper end sensor both must be emulated. Both of paper end and paper sensor have open collector/drain output so no problem to drive for a short period of time.

If you look at the behavior of the printer during initial paper feeding, it takes the paper (checks paper end sensor) and checks whether there is a reflection from the paper in defined x/y axis range, after then it moves the paper in reverse direction (-y) slowly till finding edge of the paper by this way printer sets it origin precisely. I am using optical sensor to detect the slot on the tray and control the paper sensor. Otherwise it is not easy to minimize the y axis alignment errors in double side PCB or solder resist printing. This method works very well.

Double side printing requires printing at exactly the same position for each side. To do this requires some mechanical modifications. The ink jet head and head pad need to be tilted, a tray is needed to feed the PCB, and a sensor plus a mechanical support are required to set the starting home position of the tray.

To tilt the head I modified the existing assembled head lever structure:

rightside_lever.JPG
Right side lever
1024x768px
24b 65.76KB

First I removed the lever from the frame and cut both the frame and lever:

mech_cut1.JPG
Side Cut
1024x768px
24b 53.24KB

lever_cut1.JPG
Lever Cut
1024x768px
24b 77.67KB

I didn't need to do anything for the left side since the adjustment was in the range of the lever:

leftside_lever.JPG
Leftside Lever
1024x768px
24b 141.13KB

The resulting tilted head pad is shown here:

repositioned_headpad.JPG
Repositioned head pad
1024x768px
24b 224.65KB

I removed all the "pizza" wheels on the printer. The pressure rollers also needed to be modified. I removed all of them including their springs and cut one of them in two to use half for the left and half for the right side. In this way, I enlarged the printable PCB area. The modified pressure roller is mounted on a piece of 1/16” PCB which is used as a spring.

pressurerollerANDtrayposition_home_adjuster.JPG
Pressure roller
1024x768px
24b 156KB

These pressure rollers are finally mounted on a corner aluminum profile and assembled onto the rear frame.

rear_view.JPG
Rear View
1024x768px
24b 194.65KB

You can see them coming through the front in this photo:

assembled_pressure_roller.JPG
Assembled pressure roller
1024x768px
24b 182.66KB

This next photo:

tray_slot_detector_assembly.JPG
Tray slot detector assembly
1024x768px
24b 147.52KB

shows an optical slot sensor at the right side that was used as a paper end sensor before. Now this sensor is connected to the paper emulator to detect the slot on the tray

tray_slot001.JPG
Tray Slot
1024x1409px
24b 179KB

I have used 0.01” (0.25mm) thick aluminum sheet as a tray

tray_front.JPG
Tray front
1024x768px 24b 134.60 KB

To align the tray I have used a piece of 2 aluminum corner profile

pressurerollerANDtrayposition_home_adjuster.JPG
Tray home position adjuster
1024x768px
24b 156KB

Tilting the head also requires some plastic to be cut away on the body of the printer

cutted_plastics.JPG
Cut Plastics
1024x768px 24b 140.72 KB

Now, mechanical modifications and electronics are completed and ready, it is time to do some printing.

completed_assembly_with_scanner.JPG
Completed assembly with scanner
1024x768px 24b 184.08 KB

Cleaning

Copper clad board must be clean. Here are the cleaning steps,

Other cleaning methods may used as long as you are sure that the PCB is free of dust and grease.

Printing procedure:

Double side PCB printing:

If you need to make solder resist printing use the above procedure and repeat it several times.

Instead of aligning board holes via transparency method sometimes I am using reference drill points at the corners of the PCB

sample_print_ontray.JPG
Sample print on tray
1024x768px 24b 316.33 KB

The drill size needs to be very small (I am using 0.0135” 0.34mm carbide bit) to minimize the alignment error. Using these reference holes I am drawing a line till edge of the board using sharp razor and matching it to the lines on the tray. I didn't decide yet which method is the best but later is easier.

Sample of printed board and populated board are shown

under http://groups.yahoo.com/group/Homebrew_PCBs_Archives/files/Volkan%20Epson%20Inkjet%20PCB/

mech_head_pad.JPG
Print Head Pad
1024x768px 24b 167.81 KB

1st_trial.JPG
1st_trial
1024x768px 24b 92.56 KB

printerANDtray.JPG
Printer and tray
1024x768px 24b 130.89 KB

pcb_solder_sample2.JPG
PCB Solder Sample
1024x768px 24b 258.10 KB

populated_sample1.JPG
Completed Sample
1024x768px 24b 179.73 KB

pcb_drill.JPG
PCB Drill
1024x768px 24b 175.46 KB

See also:

Questions:

Comments: