Dot matrix printing
Dot-matrix printers as pedagogical devices
Today we will work with the dot-matrix printers as pedagogical devices.
And zoom into the layers of "languages" that are at play when producing graphics and layout produced with text.
BUT. Computers don’t actually work with text.
Computers are calculators that work with numbers.
This means that any kind of input to the computer and any kind of output from the computer exists as numbers somewhere in between.
To explore the layers and complexities of these encoding and decoding processes and the politics that these systems are entangled with, dot-matrix printers are great to work with.
What can we learn from the printers today? Which questions come up?
Dot-matrix materiality
What is the materiality of dot-matrix printers?
Before was the daisy wheel printer (close to typewriter), but with the dot-matrix printers work with a printer head and pins.. so you can make your own characters. Which helped to promote it and sell it internationally. Other character cards were also distributed, with fonts etc, which you could load into your printer.
EXERCISE: Warm up and turn the printers on
Taking an exploratory approach...
- Pick one printer
- Print the self-tests of the machine (see #Howto section below)
- Print the menu with the current settings (see #Howto section below)
Now, let's try to talk with printer from a computer.
- Connect to the printer, you can follow the #Howto section below.
- Once you are connected, print some words or plain text files from the command line. See which command works for you:
$ echo "hello" > /dev/usb/lp1 $ echo "hello" | lp $ lpr hello.txt
What language does the printer speak?
Let's zoom into the text we just sent to the printer...
We just sent hello
to the printer... so the characters h, e, l, l, o.
But good to know is... that when you send these characters.. you are actually not sending the character itself, you are sending a number.
Your computer and the printer both have their own understanding of characters.. They represent each character with a number. a
often is represented by 97
for example these days (and there is a reason for that)!
But back in the days.. the different machines that were made represented characters in all different ways, each device did it differently. When the moment came that there was a need to send text from device A to device B, it was clear that this needed to be resolved somehow, and that there was a need for a shared way to represent characters.
And this is why we have: 💀encoding systems💀
ASCII imperialism
One famous one... is... ASCII, first published in 1963.
$ man ascii
Binary Dec Hex Char Binary Dec Hex Char ──────────────────────────────────────────────────────────────────────────────── 0000 0000 0 00 NUL '\0' (null character) 0100 0000 64 40 @ 0000 0001 1 01 SOH (start of heading) 0100 0001 65 41 A 0000 0010 2 02 STX (start of text) 0100 0010 66 42 B 0000 0011 3 03 ETX (end of text) 0100 0011 67 43 C 0000 0100 4 04 EOT (end of transmission) 0100 0100 68 44 D 0000 0101 5 05 ENQ (enquiry) 0100 0101 69 45 E 0000 0110 6 06 ACK (acknowledge) 0100 0110 70 46 F 0000 0111 7 07 BEL '\a' (bell) 0100 0111 71 47 G 0000 1000 8 08 BS '\b' (backspace) 0100 1000 72 48 H 0000 1001 9 09 HT '\t' (horizontal tab) 0100 1001 73 49 I 0000 1010 10 0A LF '\n' (new line) 0100 1010 74 4A J 0000 1011 11 0B VT '\v' (vertical tab) 0100 1011 75 4B K 0000 1100 12 0C FF '\f' (form feed) 0100 1100 76 4C L 0000 1101 13 0D CR '\r' (carriage ret) 0100 1101 77 4D M 0000 1110 14 0E SO (shift out) 0100 1110 78 4E N 0000 1111 15 0F SI (shift in) 0100 1111 79 4F O 0001 0000 16 10 DLE (data link escape) 0101 0000 80 50 P 0001 0001 17 11 DC1 (device control 1) 0101 0001 81 51 Q 0001 0010 18 12 DC2 (device control 2) 0101 0010 82 52 R 0001 0011 19 13 DC3 (device control 3) 0101 0011 83 53 S 0001 0100 20 14 DC4 (device control 4) 0101 0100 84 54 T 0001 0101 21 15 NAK (negative ack.) 0101 0101 85 55 U 0001 0110 22 16 SYN (synchronous idle) 0101 0110 86 56 V 0001 0111 23 17 ETB (end of trans. blk) 0101 0111 87 57 W 0001 1000 24 18 CAN (cancel) 0101 1000 88 58 X 0001 1001 25 19 EM (end of medium) 0101 1001 89 59 Y 0001 1010 26 1A SUB (substitute) 0101 1010 90 5A Z 0001 1011 27 1B ESC (escape) 0101 1011 91 5B [ 0001 1100 28 1C FS (file separator) 0101 1100 92 5C \ 0001 1101 29 1D GS (group separator) 0101 1101 93 5D ] 0001 1110 30 1E RS (record separator) 0101 1110 94 5E ^ 0001 1111 31 1F US (unit separator) 0101 1111 95 5F _ 0010 0000 32 20 SPACE 0110 0000 96 60 ` 0010 0001 33 21 ! 0110 0001 97 61 a 0010 0010 34 22 " 0110 0010 98 62 b 0010 0011 35 23 # 0110 0011 99 63 c 0010 0100 36 24 $ 0110 0100 100 64 d 0010 0101 37 25 % 0110 0101 101 65 e 0010 0110 38 26 & 0110 0110 102 66 f 0010 0111 39 27 ' 0110 0111 103 67 g 0010 1000 40 28 ( 0110 1000 104 68 h 0010 1001 41 29 ) 0110 1001 105 69 i 0010 1010 42 2A * 0110 1010 106 6A j 0010 1011 43 2B + 0110 1011 107 6B k 0010 1100 44 2C , 0110 1100 108 6C l 0010 1101 45 2D - 0110 1101 109 6D m 0010 1110 46 2E . 0110 1110 110 6E n 0010 1111 47 2F / 0110 1111 111 6F o 0011 0000 48 30 0 0111 0000 112 70 p 0011 0001 49 31 1 0111 0001 113 71 q 0011 0010 50 32 2 0111 0010 114 72 r 0011 0011 51 33 3 0111 0011 115 73 s 0011 0100 52 34 4 0111 0100 116 74 t 0011 0101 53 35 5 0111 0101 117 75 u 0011 0110 54 36 6 0111 0110 118 76 v 0011 0111 55 37 7 0111 0111 119 77 w 0011 1000 56 38 8 0111 1000 120 78 x 0011 1001 57 39 9 0111 1001 121 79 y 0011 1010 58 3A : 0111 1010 122 7A z 0011 1011 59 3B ; 0111 1011 123 7B { 0011 1100 60 3C < 0111 1100 124 7C | 0011 1101 61 3D = 0111 1101 125 7D } 0011 1110 62 3E > 0111 1110 126 7E ~ 0011 1111 63 3F ? 0111 1111 127 7F DEL
Thanks to the power of the US Military and US corporations the American computing industry became the global computing industry. Computers that we use today are rooted in American networking history, and so is the ASCII standard. However, the reality is that ASCII can only represent 26 Latin letters in the English alphabet but computers are used all over the world, by people speaking different languages. They would often end up with American computers that could not represent their language in ASCII. Think for example of scripts like Greek, Cyrillic and Arabic or even Latin scripts that use accents such as the ü or ø. Altough 128 might sound like a lot of characters, it is not enough to represent all different languages.[1]
As you can see in the table above, it lists all sorts of characters, also ones that are not represented on our keyboards (like "ESC").
Some of these are legacy characters from a teletype/typewriter past.. Like the "bell" character.
In order to type these characters, like a line feed character (LF), you use the code point representation of that particular character.
For a time people used the 8th bit, numbers 1000 0000 to 1111 1111 (or 128 to 256) to encode the specific parts of their own language. That way there was an overlap with ASCII but the own language could also be encoded.[2]
ASCII exercise
- take a piece of paper and a pen, and:
- count to 10 in decimal
- count to 10 in binary
- count to 10 in hex
Unicode universalism
The Unicode Standard supports three character encoding forms: UTF-32, UTF-16, and UTF-8. Each encoding form maps the Unicode code points U+0000..U+D7FF and U+E000..U+10FFFF to unique code unit sequences.[3]
Let's explore these code points!
In Python, you can use the chr()
function to print Unicode code points. You give it a decimal.
chr(97)
returns: "a"
And if you want to find the code point of a character, you can use ord():
ord("a")
returns: 97
You can explore all the code points, which are organized in code blocks, here:
- https://www.unicode.org/versions/Unicode16.0.0/core-spec/chapter-3/#G7404
- https://en.wikipedia.org/wiki/Unicode_block
- http://www.unicode.org/charts/
- https://www.unicode.org/Public/UCD/latest/charts/CodeCharts.pdf
Unicode exercise
- let's print U+0000 until U+D7FF and U+E000 until E+10FFFF with python
- first convert the numbers of the code blocks from hex to decimal
- then loop through these blocks by printing them
Extra
- some characters are printed as (so-called) "tofu's".. install a font that has coverage (glyphs) for these characters, like Unifont!
- find a nice range of code points and make a small terminal animation in python
Model specific printer commands
Each printer comes with a different set of special commands, different character sets and different fonts.
The special commands can change the behavior of the printer (like superscript, bold, alignment, and even loops!).
To switch to italic for instance on the NEC P60, you need to send esc
and 4
. And to send the esc
key in hex in Python, you send: chr(27)
.
To find these special commands... we need to dive into the specific manual of a printer.
The character set of a printer can be changed, to support different languages. Each printer supports a different range of character sets.
And of course, each printer comes with different fonts installed. And some of them have the option to install extra fonts using a card ridge.
Printer commands exercise
- pick a dot-matrix printer to work with
- use the python script below to send a "hello" to the printer
- find the manual of the printer and find the list of special commands this printer can work with
- work with the special commands using your python script
- we can't type these special commands with our keyboards directly, but we can execute them using the hex code points listed in the manual!
- use
chr()
and the hex code point and see what they do
- pick one special command and try to make a prototype with it (use it to draw something, make a playful layout, a mini poem, etc!)
msg = "hello"
# Linux/OSX?
printer = open("/dev/usb/lp1", "w")
printer.write(msg)
printer.close()
# Windows/OSX
import os
printer = open("tmp.txt", "w")
printer.write(msg)
printer.close()
cmd = "lpr tmp.txt"
#cmd = "cat tmp.txt | lp"
os.system(cmd)
Control individual pins
Exciting thing to try one day: There are also special commands to control the individual pins (!).
This means that you can print without using a driver. A driver translates the pixelated image into the pins of the printer.
Here you can find dithering strategies to convert an image into a type of image that could be printed on the dot-matrix printer.
So dithering was a technical limitation. These days, it's often an aesthetic choice, which is different.
Dot-matrix printers @ XPUB
OKI Microline 320
- owner: XPUB
- status: does not work (possibly hardware issue)
- connect to the printer: USB-A → centronics cable (you can find this one either in the office or XML, it's black)
- user manual: File:45690101EE1 ML320 UG EN 128084.pdf
- driver: https://www.oki.com/us/printing/support/drivers-and-utilities/dot-matrix-printers/62411601/
Test report 5 Feb 2025:
Joseph and Manetta tried to get this printer to work, we changed the settings to match the NEC printer, but this did not help.
Current intuition is that it is a hardware issue.
NEC PINWRITER P60
- borrowed from: Varia? Silvio?
- status: works
- connect to the printer: USB-A → centronics cable (you can find this one either in the office or XML, it's black)
- user manual: (cannot find it...)
- comprehensive support document: File:Pinwriter p6-specification.pdf (you can find the special commands here!)
- maintenance guide: File:P60_SM_NEC_EN_text.pdf
- links: https://www.vclab.de/paperware-inventory/pinwriter-p60-p70-verdammt-nah-am-laser/# (protected with password...)
This printer has 21 pins. One is broken, you can see it.
In the back there are magnets, one for each pin, and when the electricity hits the magnet, the pin is being activated or not.
There are 2 rows of pins, one is slightly shifted (you can see it!), which makes the resolution higher. It is divided into 2 rows, because there is no mechanical space to have so many pins so narrowly next to each other in one row.
The printer works with 80 columns.
NEC PINWRITER P20
- brought to XPUB for this trimester by: Joak
- status: works
- connect to the printer: USB-A → centronics cable
- user manual: File:Pinwriter p2000p2x-manual.pdf
- links: http://www.digitanto.it/mc-online/PDF/Articoli/105_122_127_0.pdf
Running the self test: see page 5 in the manual (page 13 in the pdf)
Printing the menu with current settings: see page 17 in the manual (page 25 in the pdf) [SELECT + Power on]
Citizen Swift 90e
- brought to XPUB for this trimester by: Joak
- status: works but bumpy, see comments below
- connect to the printer: USB-A → centronics cable
- user manual: File:Citizen-swift-90-user-manual.pdf
- datasheets: File:Citizen-swift-90e-datasheet.pdf
- technical sheet: File:Citizen-swift-90-technical-sheet.pdf
The printer works, but the continuous paper feed (for the paper with holes on the sides) is a bit slippery, so you need to help the printer a bit sometimes. If you use the external paper feed adapter and work with A4, it's much smoother.
Did manage to print test pages and the menu! But did not manage to change the settings in the menu... the printer gives an error.
How to print test pages? See page 29 in the manual: Printer Self-Tests.
How to print the menu? See page 25 in the manual: Making A Print Out Of The Printer’s Default Settings.
More info on codepages? See page 32 in the manual: Codepages - International Characters.
Sending printer control commands works when you use the build-in Epson FX1170 emulation (found this in the technical sheet). See Epson FX1170 manual chapter 8 "Command Summary" for a listing of special commands.
Star SG-10
- brought to XPUB for this trimester by: Joak
- status:
- connect to the printer: USB-A → centronics cable
- user manual: File:Star-SG-10.pdf
- links: https://www.msx.org/wiki/Star_SG-10
The language in the manual is funny, very conversational :).
You can switch between character sets, "modes" and character pitch (size) using the dip switches. See page 133 in the manual.
The special commands are a bit everywhere in the user manual, but mostly in chapter 5-10, and there is an index of all of them on page 240.
Howto
Printing on OSX
Using CUPS:
- Connect the printer with the USB cable
- Install the printer with CUPS, go to http://localhost:631 in your browser and select Administration → Add printer
- Select "Unknown" in the Local Printers section
- You can add a "name" and "location", mostly for yourself, so you can find the printer back later.. "connection" should say something like: usb://USB2.0-Print/
- For "make" (which is the manufacturer) you select "Generic" and for "model" you select "Generic Text-Only Printer", click "Add Printer"
- Your printer is installed!
- Now go to "Printers" and select your printer
- Now make this printer your default printer: click on "Administration" and select "Set as server default"
- You can now send files to the printer with:
$ lpr myfile.txt
or$ echo "hello" | lp
Backup option, without CUPS:
- Connect the printer with the USB cable.
- Go to System Settings → Printers → Add printer
- Select the USB one (the printer will start to print stuff, don't worry, you can ignore this)
- Choose "General PostScript printer" (even though this is not true!)
- Make the printer your default printer
- You can now send files to the printer with:
$ lpr -o raw myfile.txt
or$ echo "hello" | lp -o raw
Printing on Windows
lpr
should be available on Windows, so it's worth trying to add the printer through the system settings, make it your default printer, and use lpr to print- To install a generic text-only printer, you can follow these steps. If these do not work, you can follow these, but choose a local USB connection, not a network connection.
- (Backup: this is a guide to print to a line printer from Windows)
- (Backup backup: https://mendelson.org/windowsrawprint.html)
Once installed, you should be able to send files to the printer with:
> lpr myfile.txt
But! This only prints files, not output of commands like echo
.. But maybe there is a way.. this might work, it looks like it's worth a try:
Claudio's notes
First things first, I used the "CITIZEN Swift 90e" printer. On Windows I had multiple unexpected errors but after all Joseph helped and we did not end up using lpr but we used the Out-Printer command.
and the final commands were:
cat yourfile.txt | Out-Printer
or to write directly a string avoiding using a file:
"The text you want to print" | Out-Printer
Printing on Linux
Search for the printer by running this command with the USB cable unplugged and plugged:
$ ls /dev/usb/
Mine appears at: /dev/usb/lp2
Then send stuff to the printer by using > /dev/usb/lp2
, so for instance:
$ echo "hello" > /dev/usb/lp2 $ cat hello.txt > /dev/usb/lp2
Or, if you install the printer with CUPS and make it the default printer on the system (see above at OSX section), you can use:
$ cat hello.txt | lp $ lpr hello.txt
ASCII art
jp2a
lightweight tool to convert images into ascii, written by Christian Stigen Larsen
https://web.archive.org/web/20200517095853/https://csl.name/jp2a/
$ jp2a
- Linux:
$ sudo apt install jp2a
- Mac:
brew install jp2a
- Windows: https://www.samgalope.dev/2024/10/24/how-to-use-jp2a-for-creating-stunning-ascii-art/#aioseo-on-windows
Examples
Testing the PINWRITER P60 (during SI26)
linefeed = chr(10)
tab = chr(11)
italic = chr(27) + chr(4) # does not work
linespacing_big = chr(27) + chr(48)
linespacing_small = chr(27) + chr(50)
superscript = chr(27) + chr(83) + chr(0)
subscript = chr(27) + chr(83) + chr(1)
condensed = chr(27) + chr(15)
spacing_wide = chr(27) + chr(32) + chr(127) # 0 - 127
loop_start = chr(27) + chr(86) + chr(255) # 0 - 255
loop_stop = chr(27) + chr(86) + chr(0)
# printer = open("NEC_PINWRITER_P60", "w")
printer = open("/dev/usb/lp2", "w")
# printer.write("hello\n")
printer.write(loop_start + "hello " + loop_stop)
# printer.write("hello\n")
printer.close()
Pruning Station with Irmak and Aglaia (SI19)
The magic of scanner, OCR and dotmatrix printer
Python script to scan a page from a book, apply OCR (optical character recognition) and print it on the dot matrix printer.
import os
print("starting the pruning process")
scanning = "sudo scanimage --resolution 300 --mode color -o image.png"
os.system(scanning)
os.system("tesseract image.png text.txt -l eng")
fantasyname = open("text.txt.txt" , "r")
fantasyname = fantasyname.readlines()
for line in fantasyname:
line = line.split(" ")
for l in line:
if l!="" or l != ['\n', '\r\n']:
print(l)
os.system("echo '"+l+"' > /dev/usb/lp0")
Related links
- CUPS background story on Wikipedia
- Command-Line Printing and Options, about
lpr
andlp