Beginner's thermal printer: stepper motor & thermal head

Hello everybody !

I'm planning to use an old cash machine's internal thermal printer to do some ASCII art on paper ! (see photo attached!)

as i'm starting with my arduino uno, i'd like some advices on my plans.
Maybe there is someone who already tried/know more about all this !

  1. stepper motor (SAIA UAG2 Z6007) will rotate paper
    datasheet: http://www.farnell.com/datasheets/309597.pdf
    there is 6 pins, while it seems stepper usually have 2/4 pins
    which ones should i use to test it using the stepper library (pin1,pin2,pin3,pin4) ?
    they are all gray ! dammit

  2. thermal head (GH10C015) will darken paper
    closest dataset i found: KF2002-GH10A datasheet(1/4 Pages) ROHM | Compact low speed thick film thermal printhead (8 dots / mm)
    ... what i guessed:

  • give it 12V on pins A1, A2, B7
  • produce a digital clock signal on pin A3 (high every 2.5ms - the print cycle, then low)
  • and then a digital data one (timed pulse of duration 0.67ms ?) to heat the paper for each dot i want during a clock period

what are: Vdd, "latch /register", "strobe 1/2/3", and "driver" signals ?
are they good resources to learn how it work ?

thank you so much for your advices !
minut

head_schema.jpg

1 Like

The stepper is easier to figure. If there are 6 wires there should be two groups of three. And the three wires should represent connections to either end of a coil and to the centre of the coil. For bipolar operation just ignore the centre connections. Systematic measurement of resistances between the different wires should enable you to figure which is which.

I don't have time to learn how a thermal printer head should be controlled so I can explain it to you.

If you don't have the datasheet for the print head that you own you are probably wasting your time. I guess it's possible that there is a standard. Connecting 12v to something that might be intended for 5v is not a good idea.

...R

1 Like
  1. Thank you for your answer about the stepper coils !
    I also found this article : http://www.tigoe.net/pcomp/code/circuits/motors/stepper-motors/
    ... which describes quite well unipolar/bipolar steppers

  2. for the thermal head, as the dimensions exactly fit mine, i guess & hope the difference GH10A..(datasheet) / GH10C.. (mine) will not affect the logic & voltage it needs !

as for the pins i don't know:

  • Tm seems to be proportional to the temperature of the head ... to prevent printing if to hot :slight_smile:
  • Vdd seems to be related to the "drain" voltage, not sure if i need to put it 12V like Vh
  • still don't understand the clock/strobe/latch signals, i guess i would need more «digital» processing knowledge :wink:
1 Like

I suspect Vdd is 5v - the logic control rather than the heating power.

For the rest my guess is a lot weaker

Maybe (and maybe not) it expects data to be input by some serial system like SPI - although the names for SPI are usually Mosi, Miso and Clk. Maybe it just has a shift register.

Do you even know how many dots it makes at one time - 8, 12, 16 ????

Another very dodgy guess is that it prints 8 dots vertically so that a single letter may need 6 or 8 separate "prints". If it works like that you are going to need to be able to send a lot of data - maybe you need to design an alphabet with 6 or 8 bytes per letter.

...R

Thanks !
5V for Vdd, 'hoping it will not burn something :slight_smile:

Do you even know how many dots it makes at one time - 8, 12, 16 ????
Another very dodgy guess is that it prints 8 dots vertically

based on the characteristics from the "head_timing.jpg" image, i guess:
total width: 54mm
dot width: 0.125mm
... means 432 dots per line
i think the head only manages one line at a time

while, max energized dots: 144 = 432 / 3
which could be related to the 3 strobes STB !

maybe you need to design an alphabet with 6 or 8 bytes per letter.

actually, i'll be happy just printing dots, so that i will be able to even print b&w low tech images !

If anyone can point to any good resource related to shift registers, i'll be yhappy !

OK, I have looked at the head timing picture a bit more closely.

Am I right in thinking that it prints across the whole with of the paper without needing to traverse horizontally - I had assumed it would need to move sideways.

It says that it can print a max of 144 dots at one time and that there are 432 dots across.

I presume, then, that you have to load all 432 dots prior to a single print and that no more than 144 of the dots can be 1s. I assume if you need to print more than 144 dots on one line you must do 2 or 3 prints on the same line without moving the paper - and that would mean uploading data 2 or 3 times. Sounds like an interesting coding challenge - especially to do characters. You will need to be very careful with Arduino memory usage.

What is not obvious from the data you have already provided is how you upload the data and, subsequently, how you get it to print. It looks as if you upload the data by successively sending 1s or 0s in sync with the clock. It may be that after you have uploaded all the bits you pull the latch low (or you may need to do that after every bit - but that seems less likely) followed by pulling the stobe low to make it print. Then maybe the driver out goes low to tell you it is printing - so you must wait for it to return to HIGH before uploading more data. This is all very speculative and may be very wide of the mark. Also I don't know if the clock must run constantly - I think it must.

...R

Thanks for your answer !

I also found this: http://global.kyocera.com/prdct/printing-devices/thermal-printheads/tec/controlling.html
which describes well the Strobe, Latch and Data inputs

based on that, here's what i'm planning to do, to print one line of dots:

  • CLOCK: digitalWrite HIGH then LOW, let's say every 10 microseconds (to stay less than max clock freq of 4Mhz, as a tryout)

  • DATA: send HIGH/or/LOW of what i want to print, every clock period, 432 times (the whole line width)

  • /LATCH: was HIGH since start, i give one impulse to LOW: it will load/store the "line data"

  • /STROBES: were HIGH (no heating (?)), i put them to LOW sequentially, during 0.5ms each, to heat the paper based on the data previously loaded (~ which stays around the 0.67ms : "pulse width" from datasheet)

note: it seems this model don't have BEO (BlockEnableOut) neither DO (DriverOut) (which seem to be used in coordination with STROBES to release the heating power ... like a protection i guess).

please correct me if i'm doing something wrong, especially about:

  • the Strobes duration (don't want to burn my desk :slight_smile:
  • HIGH/LOW (i find it somehow weird to always keep it HIGH, and then a LOW impulse to heat

This means i need to carefully put all the digitalPins to HIGH at start !

minut:
Thanks for your answer !

I also found this: http://global.kyocera.com/prdct/printing-devices/thermal-printheads/tec/controlling.html
which describes well the Strobe, Latch and Data inputs

Seems like my guess was pretty much correct - always assuming your print head behaves like the Kyocera.

In the earlier document you linked to it said there was a limit to be number of dots that could be "hot" at one time.

I guess if there are several STROBE pins you can energize them one at a time. But if you only have 1 strobe pin you may need to load an print 2 or three sets of data for one line with a lot of dots.

I suspect there is no need to try a high data rate for initial experiments. I would be content with loading the data at a rate of (say) 2000/sec which would be much easier to manage - unless it says somewhere that there is a minimum rate.

You could wire an external pullup resistor to any pins that have active LOW to ensure they are HIGH by default. You should probably read the Atmel datasheet to figure out exactly what sequence happens if you want a pin to be HIGH before it goes LOW. As far as I recall all the I/O pins default to INPUT which won't cause a problem with the external pullup. But I don't know if you can set a pin HIGH before you switch it to OUTPUT.

...R

Robin2:
But I don't know if you can set a pin HIGH before you switch it to OUTPUT.

I believe doing so will turn on the internal pullup resistor.

tylernt:

Robin2:
But I don't know if you can set a pin HIGH before you switch it to OUTPUT.

I believe doing so will turn on the internal pullup resistor.

Yes, I know that. What I don't know is what happens at the next step when you change the pin to OUTPUT - does it stay HIGH? I'm sure this is explained in the datasheet.

...R

unfortunately, the datasheet doesn't provide a lot of information...
anyway, i pushed my work-in-progress code to: GitHub - pierrejdlf/thermalprinter
Please review it, sure it can be improved !
it's largely inspired by this lib i found: https://github.com/lazyatom/Thermal-Printer-Library
(especially for the PROGMEM thing to store the image)

Hello everybody !

Following this discussion: Beginner's thermal printer: stepper motor & thermal head - Project Guidance - Arduino Forum

i'm just about to succeed in controlling a stepper motor and a printer thermal head

  • stepper rotates well (using the Stepper lib)
  • the binary data from my image sample is well read back from PROGMEM
  • the 24V is well controlled ...

... but i'm not sure about the Data, Clock, Latch and Strobes signals sent to the device
because the internal temperature does not change at all !
it seems that the Strobes signals

As it's quite difficult to debug, (i'm not sure about the logic !)

i would be very happy if someone familiar with the subject could review my code

code, circuit, datasheet available at: GitHub - pierrejdlf/thermalprinter

Thanks !

Why not continue the other Thread where all the background info is ready to hand.

I am asking the moderator to merge them.

...R

you may be right

i thought it may be faster to understand the whole thing with everything at one place (github repo with images)

minut:
you may be right

i thought it may be faster to understand the whole thing with everything at one place (github repo with images)

Yes. This place. I'm not going to take the time to navigate Github.

...R

After quite a bit of searching, I think I've found a document that you'll find very useful. A quick skip reading looks like it explains all the signals you have pretty well.THE THERMAL PRINTER APPLICATION MANUAL

here attached: the code and circuit image for those who don't like github :slight_smile:


#include <Stepper.h>
#include <iostream.h>
#include <copy>

// data you want to print comes-in as a bytes array from file
#include "sampleimage.cpp"

// change this depending on the number of steps
// per revolution of your motor
#define motorSteps 20

// stepper pins
#define motorPin1 8
#define motorPin2 9
#define motorPin3 10
#define motorPin4 11

// temperature & LED pins
#define tPin 0
#define ledPin 13

// thermal head pins
#define STB1 1
#define STB2 2
#define STB3 3
#define LAT 4
#define DATA 5
#define CLK 6
#define VOLTSWITCH 12

boolean debug = false;
boolean printing = false;
boolean goprint = true;

int t = 1; // loop for thermal head clock/data/...

// whole line data that will be sent to the DATA pin for one line
uint8_t linedata[] = {
  0x11,0x00,0x22};

int TCLOCK = 1; // clock period in microseconds (is actually much longer because of the in-between operations)
int MAXTEMP = 27; // max Temp°C before turning everything off

// durations as clock cycles
int NLAT = 432; // one dot data every clock period before releasing LATCH
int NSTB = 650; // MAX is 670 microseconds
int NWAIT = 25; // lets wait some cycles between events 
int NTOTAL = NLAT + 3*NSTB + 5*NWAIT;

long starttime = micros();

// initialize of the Stepper library:
Stepper myStepper(motorSteps,motorPin1,motorPin2,motorPin3,motorPin4); 

////////////////////////////////////////////////////////
void setup() {
  resetPrinter();
  // set the motor speed at 60 RPMS:
  myStepper.setSpeed(150);
  // Initialize the Serial port:
  Serial.begin(9600);

  // set up pin modes:
  pinMode(ledPin, OUTPUT);
  pinMode(STB1, OUTPUT);
  pinMode(STB2, OUTPUT);
  pinMode(STB3, OUTPUT);
  pinMode(LAT, OUTPUT);
  pinMode(DATA, OUTPUT);
  pinMode(CLK, OUTPUT);
  pinMode(VOLTSWITCH, OUTPUT);

  // blink the LED:
  //blink(3);
  Serial.println("Printer setup done. Welcome.");
  myStepper.step(-20);

  getTemperature(true);
}

////////////////////////////////////////////////////////
void loop() {
  ////////////////////////////////// STEPPER TESTS
  //  Serial.println("Forward");
  //  myStepper.step(700); // Step forward
  //  delay(300);
  //  blink(5);
  //  Serial.println("Backward");
  //  myStepper.step(-700); // Step backward
  //  delay(300);
  //  blink(7);

  ////////////////////////////////// THERMAL HEAD CYCLE
  if(getTemperature(false) >= MAXTEMP) {
    Serial.println("========= TEMPERATURE WARNING !! aborting all");
    resetPrinter(); // will set printing=false and stop Vh current
    blink(10);
    delay(10000);
  }

  if(printing) {
    if(t==1) {
      blink(1);
      Serial.println("Printing cycle launch in 1 seconds !");
      delay(1000);
      blink(2);
    }
    if(t%1000000==0) {
      Serial.print("Printing cycle k:");
      Serial.println(t);
      printTime("cycle"); 
    }
    cycle(t);
    t = t+1;
    if(t>NTOTAL) {
      printing = false;
      Serial.println("Printing cycle ends.");
      resetPrinter();
      myStepper.step(-120);
    }
  }

  // do things once at start, boy
  if(goprint) {
    goprint = false;
    resetPrinter();
    printImageData(sampleimage,432,1);
    // plugging 24V POWER
    setPinVal(VOLTSWITCH,HIGH);
  }
}

////////////////////////////////////////////////////////
// this is a clock cycle
void cycle(int k) {

  // first we send the DATA
  if(k<NLAT) setPinVal(DATA, HIGH); // TEST ALL BLACK
  //if(k<NLAT) setPinVal(DATA, pixelValue(k-1) ? HIGH : LOW ); // k-1 cause we started at 1
  //else setPinVal(DATA,LOW);

  // every 432 dots, we will load the data using LATCH
  if(k==NLAT) {
    setPinVal(LAT,LOW);
    Serial.println("!!!!! LATCHED");
  }
  if(k==NLAT+15)
    setPinVal(LAT,HIGH);

  // after all that, let's burn paper with the STROBES
  if(k==NLAT + NWAIT)
    setPinVal(STB1,LOW);
  if(k==NLAT + NWAIT + NSTB)
    setPinVal(STB1,HIGH);

  if(k==NLAT + 2*NWAIT + NSTB)
    setPinVal(STB2,LOW);
  if(k==NLAT + 2*NWAIT + 2*NSTB)
    setPinVal(STB2,HIGH);

  if(k==NLAT + 3*NWAIT + 2*NSTB)
    setPinVal(STB3,LOW);
  if(k==NLAT + 3*NWAIT + 3*NSTB)
    setPinVal(STB3,HIGH);

  // CLOCK
  delayMicroseconds(TCLOCK);
  setPinVal(CLK,HIGH);
  delayMicroseconds(TCLOCK);
  setPinVal(CLK,LOW);
}

////////////////////////////////////////////////////////
// is the pixel k black or white ?
boolean pixelValue(int k) { // k is in [0,431]
  // linedata is an array of 54 bytes
  uint8_t b = linedata[k/8];
  int gut = k%8;
  boolean res = (b & (1<<gut)) != 0; // if non-zero, the bit was set !
  //  if(k<27) {
  //    Serial.print("byte index:");
  //    Serial.println(k/8);
  //    Serial.print("the byte:");
  //    Serial.println(b,HEX);
  //    Serial.print("decalage:");    
  //    Serial.println(gut);
  //    Serial.print("------------------------ pixel value:");
  //    Serial.println(res);
  //  }
  return res;
};

////////////////////////////////////////////////////////
void printLine() {
  // 1. rotate stepper
  //myStepper.step(100);

  // 2. launch clock things (within loop).
  printing = true;
}

////////////////////////////////////////////////////////
// prints an image
void printImageData(const uint8_t *image,int w,int h) {
  
  // a full width image line is 432 = 144*3 dots
  // each strobe gets 144 = 8*18 dots, aka 18 bytes
  // the minimal image is a full-width-one-line, aka an array of 18*3 = 54 bytes
  // let's suppose the image has exactly w=432, aka h*54 bytes in its data
  
  for(int rowStart=0; rowStart<h; rowStart+=54) {
    *linedata = pgm_read_byte(image+rowStart);
    /*Serial.println("SAMPLE Linedata array (first 3):");
     Serial.println(linedata[0]);
     Serial.println(linedata[1]);
     Serial.println(linedata[2]);*/
    printLine();
  }
}

////////////////////////////////////////////////////////
void resetPrinter() {
  t = 1;
  printing = false; // !! IMPORTANT
  setPinVal(VOLTSWITCH,LOW); // !! IMPORTANT
  setPinVal(STB1,HIGH);
  setPinVal(STB2,HIGH);
  setPinVal(STB3,HIGH);
  setPinVal(LAT,HIGH);
  setPinVal(DATA,HIGH);
}

////////////////////////////////////////////////////////
void setPinVal(uint8_t pin, uint8_t val) {
  //if(pin==STB1 || pin==STB2 || pin==STB3)
  if(debug) {
    //Serial.print("Setting pin");
  }
  else {
    if(pin==STB1 || pin==STB2 || pin==STB3) {
      Serial.print(pin);
      Serial.print(" STROBE: ");
      Serial.println(val);
      printTime("made strobe:");
    }
    digitalWrite(pin,val);
  }
}

////////////////////////////////////////////////////////
// get the thermal head temp in C°
double getTemperature(boolean verbose) {
  // display initial temperature
  int rawA = analogRead(tPin); // 0 to 1023
  double Rk = 10.0/((1024.0/rawA)-1.0);
  double T = 0;
  if(Rk<40) T = 20;
  if(Rk<30) T = 25;
  if(Rk<20) T = 35;
  if(Rk<10) T = 45;
  if(Rk<5) T = 55;
  if(verbose) {
    Serial.print("Rk/Temperature: ");
    Serial.print(Rk);
    Serial.print(" / ");
    Serial.println(T);
  }
  return T;
}

////////////////////////////////////////////////////////
// printTime
void printTime(String str) {
  Serial.print(str);
  Serial.print(" Time (ms) is: ");
  Serial.println((micros() - starttime)/1000.0);
}

////////////////////////////////////////////////////////
// Blink the reset LED:
void blink(int howManyTimes) {
  int i;
  for (i=0; i< howManyTimes; i++) {
    digitalWrite(ledPin, HIGH);
    delay(100);
    digitalWrite(ledPin, LOW);
    delay(100);
  }
}

Thank you KenF
Maybe what i'm doing wrong is to try to manage the clock manually within the loop() (using delayMicroseconds(), pinWrite())...
is there a way to do it "low level" - aka use the internal arduino clock (?) to send signals (faster, more regular) ?

In my opinion you have far too much code for this early stage of your experiment. I would just be trying to set some pixels dark and some light and getting them to make marks on the paper. I would just move the paper by hand so I would have no stepper code.

In this piece of code I don't understand why you have the delay before anything appears to happen.

// CLOCK
  delayMicroseconds(TCLOCK);
  setPinVal(CLK,HIGH);
  delayMicroseconds(TCLOCK);
  setPinVal(CLK,LOW);

It would probably help if you write down the sequence of events you are trying to program for.

You seem to be trying to measure a temperature but I don't immediately see, in the documents you have provided, any reference to a temperature pin.

It was much easier to see the pictures in your earlier post when they were separate.
It would be even easier if you just posted a link to the web page where you got them.

...R

As the stepper part is working, (and rotates the paper well) - it's easier for me to run quick / multiple / tests
I think this code is nearly the minimal one to print a single dark line
The getTemperature() in the loop() allows me to stop all the process if i'm heating to much (don't want to destroy the head !)

Here is how it works:

each clock cycle is done using the function: cycle(k)
where k begins at 1 ... until all operations are done
That's why we have:

// CLOCK
  delayMicroseconds(TCLOCK);
  setPinVal(CLK,HIGH);
  delayMicroseconds(TCLOCK);
  setPinVal(CLK,LOW);

where TCLOCK is related to the clock period

Here is the sequence:
(firstly, i put Vh on (a red LED lighting up shows the 24V goes in)

  • k=1 until k=NLAT(432): we send all the data points (all set to HIGH=dark for the test)
  • k=NLAT : we send the LATCH signal (LOW during one/some period) to load the data
  • then each STROBE signal: 1/2/3 (LOW during some periods) to heat

i have been trying with different values:

  • TCLOCK = 1, 10, 100 microseconds
  • NLAT duration: here equals 15 periods (but i also tried 1, 2, 30)
  • NSTB: (strobe duration) i even tried 1000/2000 periods (very long)

the temperature (thermistor connected to the analog A0 input) changes a little-little-little bit (measure is working), but nothing gets heated

I got the images from this datasheet, thinking it would be easier to have them all at one place (fullscreen image) to be able to quickly check the pin assigments, cable colors, arduino inputs/outputs

Here is the output of the Serial console
(NB: i updated the code a little bit for better console logs (see github), but same logic)

========= Printing cycle launch in 1 seconds !
changed pin 4: Time (ms) is: 5617.78
----- LATCHED !
changed pin 4: Time (ms) is: 5637.24
changed pin 1: Time (ms) is: 5676.76
changed pin 1: Time (ms) is: 5889.58
changed pin 2: Time (ms) is: 5892.14
changed pin 2: Time (ms) is: 6104.21
changed pin 3: Time (ms) is: 6106.77
changed pin 3: Time (ms) is: 6318.82
========= Printing cycle ends.

i'm thinking i may be sending signals to slowly (looking at the milliseconds delays !)
(4=Latch, 1/2/3=Strobes)
(6=Clock and 5=Data are not displayed)