Fortune printer on an Arduino Uno
Press the button and a thermal printer rolls out a fortune and a lucky number, in big bold type with a barcode under it. The whole build, an Arduino Uno and 2 more parts, runs here in your browser on the firmware below; open it in the editor to change the wiring or the code and run it again.
Teaching? Assign it to your class in one click.
Beginner About 20 minutes to build and run. Runs in your browser. Free, and no account needed.
The parts retail for about $55. On Mokxi they're free.
Step by step, with the wiring and the common mistakes: Arduino Thermal Printer: Wiring, Code and Barcodes
What you'll learn
- Talking to a serial device with SoftwareSerial
- Powering a printer from its own supply with shared ground
- Debouncing a button with a short delay
- Picking random items from an array
- Difficulty: Beginner
- Time: About 20 minutes
- Board: Arduino Uno R3
- Parts: 5
How it works
The Uno talks to a thermal receipt printer over two pins: 5 for its TX and 6 for its RX, using SoftwareSerial at 19200 baud. The printer has its own 9 V supply and shares ground with the Uno. A button on pin 2 starts a print.
Each press prints a ticket. It has a big bold centered FORTUNE header, a dashed line, one random fortune from a list of eight, and a lucky number from 10 to 99. The number is printed as text and as a CODE39 barcode.
Parts list
5 parts, plus the jumper wires. They retail for about $55; on Mokxi they're free.
- 1 × Arduino Uno R3In Starter Kit, Claw Machine Kit, Vending Machine Kit.
- 1 × Half breadboard
- 1 × Thermal receipt printer
- 1 × Power, 9 V
- 1 × PushbuttonIn Starter Kit, Christmas Lights Kit, Logic Lab Kit.
Wiring table
Every connection, pin by pin, read from the circuit itself. Each row joins the pin on the left to the pin on the right, by a jumper wire or a breadboard strip.
| Part and pin | Connects to |
|---|---|
| Thermal receipt printer pin RX | Arduino Uno R3 pin 6 |
| Thermal receipt printer pin TX | Arduino Uno R3 pin 5 |
| Pushbutton pin 1a | Arduino Uno R3 pin 2 |
| Thermal receipt printer pin GND | Arduino Uno R3 pin GND |
| Pushbutton pin 2a | Arduino Uno R3 pin GND |
| Thermal receipt printer pin VH | 9 V |
The code
The firmware exactly as the editor opens it. Change a line there and press Run: it compiles in the browser.
// Fortune printer: a mini thermal printer and a button.
//
// pins 5, 6 SoftwareSerial: the printer's TX into 5, its RX from 6
// pin 2 the button, to ground (INPUT_PULLUP)
//
// The printer runs from its own 9 V supply, sharing ground with the Uno.
// Each press prints a ticket: a bold, centered, double size header, one of
// the fortunes below, and a lucky number as text and as a barcode. The time
// of the press seeds the random numbers, so the tickets differ run to run.
#include <Adafruit_Thermal.h>
#include <SoftwareSerial.h>
const int BUTTON = 2;
SoftwareSerial printerSerial(5, 6);
Adafruit_Thermal printer(&printerSerial);
const char *const FORTUNES[] = {
"A soldered joint holds fast.",
"Your next build works first try.",
"Check the ground wire first.",
"A blinking light brings joy.",
"Patience fixes most bugs.",
"Today, the magic smoke stays in.",
"An old project wants finishing.",
"Read the datasheet. It knows.",
};
const int FORTUNE_COUNT = sizeof(FORTUNES) / sizeof(FORTUNES[0]);
int tickets = 0;
void printTicket() {
randomSeed(micros());
int lucky = random(10, 100);
printer.justify('C');
printer.boldOn();
printer.setSize('L');
printer.println("FORTUNE");
printer.setSize('S');
printer.boldOff();
printer.println("--------------------------------");
printer.justify('L');
printer.println(FORTUNES[random(FORTUNE_COUNT)]);
printer.feed(1);
printer.justify('C');
printer.print("Lucky number: ");
printer.println(lucky);
char code[3];
code[0] = (char)('0' + lucky / 10);
code[1] = (char)('0' + lucky % 10);
code[2] = 0;
printer.setBarcodeHeight(40);
printer.printBarcode(code, CODE39);
printer.justify('L');
printer.feed(2);
tickets++;
Serial.print("Ticket ");
Serial.print(tickets);
Serial.print(", lucky number ");
Serial.println(lucky);
}
void setup() {
Serial.begin(9600);
pinMode(BUTTON, INPUT_PULLUP);
printerSerial.begin(19200);
printer.begin();
if (!printer.hasPaper()) Serial.println("Out of paper");
Serial.println("Press the button for your fortune");
}
void loop() {
if (digitalRead(BUTTON) == LOW) {
delay(20);
if (digitalRead(BUTTON) == LOW) {
printTicket();
while (digitalRead(BUTTON) == LOW) delay(10);
delay(20);
}
}
}
Code walkthrough
The key functions of the sketch, and what each one does.
printTicket sketch.ino
Seeds the random generator with micros, picks a lucky number, then sends the layout to the printer: centered bold large header, a dashed line, a random fortune, then the number. It builds a two character string from the number for the barcode and counts the ticket.
void printTicket() {
randomSeed(micros());
int lucky = random(10, 100);
printer.justify('C');
printer.boldOn();
printer.setSize('L');
printer.println("FORTUNE");
printer.setSize('S');
printer.boldOff();
printer.println("--------------------------------");
printer.justify('L');
printer.println(FORTUNES[random(FORTUNE_COUNT)]);
printer.feed(1);
printer.justify('C');
printer.print("Lucky number: ");
printer.println(lucky);
char code[3];
code[0] = (char)('0' + lucky / 10);
code[1] = (char)('0' + lucky % 10);
code[2] = 0;
printer.setBarcodeHeight(40);
printer.printBarcode(code, CODE39);
printer.justify('L');
printer.feed(2);
tickets++;
// ... 5 more lines in the editorsetup sketch.ino
Opens the serial monitor, sets the button as INPUT_PULLUP, starts the printer's serial link at 19200 baud and calls printer.begin. It then checks hasPaper and prints a message if the paper is out.
void setup() {
Serial.begin(9600);
pinMode(BUTTON, INPUT_PULLUP);
printerSerial.begin(19200);
printer.begin();
if (!printer.hasPaper()) Serial.println("Out of paper");
Serial.println("Press the button for your fortune");
}loop sketch.ino
Waits for the button to read LOW, waits 20 ms and checks again to skip bounce, then calls printTicket. It then waits for the button to be released, so holding it down prints only one ticket.
void loop() {
if (digitalRead(BUTTON) == LOW) {
delay(20);
if (digitalRead(BUTTON) == LOW) {
printTicket();
while (digitalRead(BUTTON) == LOW) delay(10);
delay(20);
}
}
}Troubleshooting
The usual mistakes with this circuit, and the fix for each.
- Nothing prints or the output is garbage This project
Check the printer's RX goes to pin 6 and its TX to pin 5, and that the baud rate matches 19200. - The printer stays dead This project
It needs its own 9 V supply on VH, with its GND joined to the Uno's GND. - The serial monitor says Out of paper This project
Load paper in the printer. The sketch checkshasPaperonce at startup. - The printer prints garbage or nothing. Thermal receipt printer
Match the baud rate, 19200 for these, and cross TX and RX. Join the grounds. Use a library such as Adafruit_Thermal and call begin after the printer has warmed up. - The printer resets the board, faint prints, or jams. Thermal receipt printer
It needs a strong 5 V to 9 V supply, usually 2 A or more, separate from the board. Weak power gives light prints and resets. Use the correct paper side, since only one side is heat sensitive. - The input reads random HIGH and LOW when the button is not pressed. Pushbutton
A floating pin picks up noise. Add a 10 k pull-down or pull-up resistor, or use INPUT_PULLUP and wire the button between the pin and GND. Then pressed reads LOW. - The button seems always pressed, or never changes. Pushbutton
A 4-leg tactile switch has legs joined in pairs. Place it across the breadboard center gap so the pairs sit on different rows. Wired along one side, the legs are already connected. - Upload fails or nothing runs after wiring pins 0 and 1 to a module. Arduino Uno R3
Pins 0 and 1 are the USB serial port. Move the module to other pins, or unplug it while uploading. Also check the right board is selected.
Change it and keep it
Open it in the editor, change the circuit or the code, and keep your version in a free account.