Home:: Direct InkJet PCB Resist Printing

Direct InkJet PCB Resist Printing

CX4200 Modifications

By Volkan Sahin


 eBay Marketplace Logo
Personalized Black Titanium / Stainless Steel Name Ring
$16.00 14d 10h 21m
New 20.0 Mega Pixel 20.0M 6 LED USB PC Webcam+Mic
$5.68 8d 21h 23m
36 inch GYRO 8501 Metal 3.5-Channel RC Helicopter 91cm
$169.99 24d 16h 31m

disclaimer
 eBay Marketplace Logo
Personalized Black Titanium / Stainless Steel Name Ring
$16.0014d 10h 21m
New 20.0 Mega Pixel 20.0M 6 LED USB PC Webcam+Mic
$5.688d 21h 23m
36 inch GYRO 8501 Metal 3.5-Channel RC Helicopter 91cm
$169.9924d 16h 31m
Wireless Spy Nanny Mini Micro Camera FULL SYSTEM!
$35.894d 06h 26m
NASA T-shirt US COOL Space Tee RETRO SCIENCE GEEK Shirt
$7.959d 00h 45m
youstar Casual long sleeve Tshirts Collection
$8.6626d 09h 43m
DOUBLE SIDED "" Custom engraved Dog Tag Cat Pet ID Tags
$3.5928d 12h 50m

disclaimer

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,

  • Using 'Scotch-Brite' and 'Ajax with Bleach' clean the board.
  • Using Tarn-X remove oxide once more.

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

Printing procedure:

  • Fix the PCB onto tray using scotch tape, do a dummy print and let printer to give you a paper error and now it is time to activate paper emulator. Put the limiters (home position corner profiles) as shown here
    tray_home_position_adjusters.JPG
    Tray home position adjusters
    1024x768px
    24b 137.63KB

    and feed the tray untill it touches. The right side of the tray also needs to touch the metal frame of the printer.
  • Remove the profiles from the printer.
  • Press 'Start' button on printer or 'Continue' button on screen.
  • Printer will start to print
    sample_printing.JPG
    Sample printing
    1024x768px
    24b 199.58KB
  • Handle the PCB carefully to a flat surface since ink is still wet. Wait a while.

  • Cure the board completely by holding it around 1" (2.5cm) above the electrical stove and start to dry it from the reverse side first. The cured board will look like this:
    cured_sample001.JPG

Double side PCB printing:

  • Drill the board with CNC
  • Clean PCB as explained above
  • Roughly estimate the position of the printed image on tray and put PCB to this location and loosely fix it by Scotch.
  • Cover PCB with transparency and fix the transparency by Scotch.
  • Print your image on transparency
  • Using small PCB sticks align the pads of the PCB to the image on the transparency
  • Remove transparency
  • Reprint image on PCB
  • Dry the image
  • Repeat the above process for the other layer.
  • Cure both sides
  • Etch the board

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:

  • Hy!
    I have an small question for you I would really like to know if there is chance that I buy a moded inkjet from you?
    If not I would really like to get answer to few other questions please send me an e-mail on "fccstrazo@gmail.com"
    I really need to say that this is the only thing that is fast as CNC printing. It would be smart to get this type of printers in production... and for low price :D.
    Thanks for my new happy day.
    Bye!+
  • CAN I use c4200 without modifying anything. just by removing the body cover can i insert the copper board to print using the default cartridge. James Newton of James Newton's Massmind replies: The problem with doing that is that the standard inks seem to wash off in the etchant. Even after baking, they dissolve. The MISPRO inks, because they actually contain a micro fine physical solid, resist the echant as long as they are baked in.
    Having said that, perhaps you will discover some process or method for resisting echant with the regular inks.
    +

    +

  • amirnospa _at_ hot mail com asks:
    Dear Volkan, could you please post pictures of where to get xenc0/1, yenc0/1 and PDETECT? I´d also like to know what are the voltage levels of xenc and yenc. I´m planning to buy a C110/C120 and use AVR/8051 to spy encoder signals and drive a flatbed with PCB resting on it. I bought a used C65, expecting to spy the step/dir signals sent to stepper controller (Allegro A6628SEDT), but it is custom chip made for Epson, and Allegro can´t reveal details. Problem is I fried the printer with small short-circuit while measuring signals. Stupid me... Thanks!

  • Hi, i hv bought a flatbed printer with all internal organs from Epson R230. Thus, it's actually an R230 that prints thick media like rock, shirt etc...

    my Question is that why it keeps on burning the printhead and/or motherboard after printing for a while. The situation is that it prints good/normal at the beginning, after a few days it's totally not spitting out inks (only the carriage is moving)?? I hv changed a new working R230 into this "flatbed printer" and it's printing good for within 24 hours only and again, it's not printing out the inks!

    Any help on this???? Is it possible that Epson has some kind of counter/detector that once modification is detected, then it's self-destructed?? anything i could do? reset the firmware? cheating the printer circuit??
    James Newton replies: I'm not absolutly certain that this is your poblem, but especially if you are using the durabright or MSPro inks, you should be aware that they dry out very quickly and will totally jam the print head. To keep them from drying out, between print jobs, the head rests on a pad that seals the nozzels against the air and keeps the inks wet. If that seal is disturbed, or the pad removed, the inks will solidify in the head and you will not be able to print.+

    +

  • spamSunityshar at spamaol.com asks:
    http://www.hack247.co.uk/2006/08/15/diy-pcb-printer/
    Question for Volkan Sahin: What kind of file did you use to print resist? Was it a paint, coral or cad? Some body needs a consultant for printing using Roland printer which uses epson print head. contact me.
    Thanks
    Sharma
    +

Comments: