Number guess on an Arduino Uno
A 4x3 telephone keypad and a 16x2 LCD on an Uno: guess the number, and the display says higher or lower. 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.
Intermediate About 25 minutes to build and run. Runs in your browser. Free, and no account needed.
The parts retail for about $36. On Mokxi they're free.
What you'll learn
- Scanning a 4x3 phone keypad on seven pins
- Showing text on a 16x2 character LCD
- Seeding random numbers from a human reaction time
- Building a number from typed digits
- Giving feedback with HIGHER and LOWER
- Difficulty: Intermediate
- Time: About 25 minutes
- Board: Arduino Uno R3
- Parts: 4
How it works
The board picks a secret number from 1 to 100. You type a guess on the phone keypad and press #. The LCD says HIGHER or LOWER, and * erases what you typed. A right guess shows how many goes it took, then a new number is chosen.
The random number is seeded from micros() when you press the first key. That is your reaction time in microseconds, so each session starts differently. Seeding in setup would give the same number every run.
Parts list
4 parts, plus the jumper wires. They retail for about $36; on Mokxi they're free.
- 1 × Arduino Uno R3In Starter Kit, Claw Machine Kit, Vending Machine Kit.
- 1 × Full-size breadboard
- 1 × Keypad, 4x3 telephone
- 1 × Character LCD, 16x2
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 |
|---|---|
| Keypad, 4x3 telephone pin R1 | Arduino Uno R3 pin 12 |
| Keypad, 4x3 telephone pin R2 | Arduino Uno R3 pin 11 |
| Keypad, 4x3 telephone pin R3 | Arduino Uno R3 pin 10 |
| Keypad, 4x3 telephone pin R4 | Arduino Uno R3 pin 9 |
| Keypad, 4x3 telephone pin C1 | Arduino Uno R3 pin 8 |
| Keypad, 4x3 telephone pin C2 | Arduino Uno R3 pin 7 |
| Keypad, 4x3 telephone pin C3 | Arduino Uno R3 pin 6 |
| Character LCD, 16x2 pin VDD | Arduino Uno R3 pin 5V |
| Character LCD, 16x2 pin A | Arduino Uno R3 pin 5V |
| Character LCD, 16x2 pin VSS | Arduino Uno R3 pin GND |
| Character LCD, 16x2 pin V0 | Arduino Uno R3 pin GND |
| Character LCD, 16x2 pin RW | Arduino Uno R3 pin GND |
| Character LCD, 16x2 pin K | Arduino Uno R3 pin GND |
| Character LCD, 16x2 pin RS | Arduino Uno R3 pin A0 |
| Character LCD, 16x2 pin E | Arduino Uno R3 pin A1 |
| Character LCD, 16x2 pin D4 | Arduino Uno R3 pin A2 |
| Character LCD, 16x2 pin D5 | Arduino Uno R3 pin A3 |
| Character LCD, 16x2 pin D6 | Arduino Uno R3 pin A4 |
| Character LCD, 16x2 pin D7 | Arduino Uno R3 pin A5 |
The code
The firmware exactly as the editor opens it. Change a line there and press Run: it compiles in the browser.
// Number guess: the board thinks of a number, you type it on a phone keypad.
//
// pins 12, 11, 10, 9 keypad rows R1 to R4
// pins 8, 7, 6 keypad columns C1 to C3
// A0 LCD RS A1 LCD E
// A2, A3, A4, A5 LCD D4 to D7 (RW and V0 to GND)
//
// The pad is the twelve-key one off a telephone:
//
// 1 2 3
// 4 5 6
// 7 8 9
// * 0 #
//
// Type a number between 1 and 100 and press `#` to send it. The display says
// HIGHER or LOWER, and `*` rubs out what you have typed. Get it right and it
// tells you how many goes it took and thinks of another.
//
// # Where the number comes from
//
// A board has no dice and no clock that is different on each run, so seeding
// the generator in `setup()` gives the same "random" number every time, which
// is the classic bug in every guessing game written on a microcontroller. This
// one seeds from `micros()` at the moment the first key of the game is pressed.
// That reading is a person's reaction time in microseconds, and nothing on the
// board can predict it.
//
// # Reading the pad
//
// Twelve keys on seven pins and no chip: each key shorts its row to its column,
// so the sketch scans: one row driven low at a time, the three columns read
// with their pull-ups on. mokxi_keypad.h does the scan and the debounce and
// hands back one character per press. It never drives a row high, because two
// keys held in one column would put one driven output across another.
#include "mokxi_keypad.h"
#include "mokxi_lcd1602.h"
const uint8_t ROWS[4] = {12, 11, 10, 9};
const uint8_t COLS[3] = {8, 7, 6};
Keypad4x3 pad(ROWS, COLS);
Lcd1602 lcd(A0, A1, A2, A3, A4, A5);
const int LOWEST = 1;
const int HIGHEST = 100;
int secret = 0;
int entry = 0;
int tries = 0;
bool seeded = false;
static void newGame()
{
secret = (int)random(LOWEST, HIGHEST + 1);
entry = 0;
tries = 0;
}
static void showEntry(const char *note)
{
lcd.setCursor(0, 0);
lcd.print(note);
lcd.padLine();
lcd.setCursor(0, 1);
lcd.print("Guess: ");
if (entry > 0) {
lcd.print(entry);
} else {
lcd.write('_');
}
lcd.padLine();
}
void setup()
{
pad.begin();
lcd.begin();
lcd.print("Number guess");
lcd.setCursor(0, 1);
lcd.print("1 to 100, # ends");
}
void loop()
{
char key = pad.pressed();
if (key == 0) {
return;
}
// The first press of the session is what the generator is seeded from: a
// person's reaction time, which nothing on the board can work out for itself.
if (!seeded) {
randomSeed(micros());
seeded = true;
newGame();
showEntry("Think of a go");
}
if (key == '*') {
entry = 0;
showEntry("Cleared");
return;
}
if (key >= '0' && key <= '9') {
if (entry < 100) {
entry = entry * 10 + (key - '0');
}
showEntry("Typing");
return;
}
if (key != '#') {
return;
}
if (entry < LOWEST || entry > HIGHEST) {
entry = 0;
showEntry("1 to 100 only");
return;
}
tries++;
if (entry < secret) {
entry = 0;
showEntry("HIGHER");
return;
}
if (entry > secret) {
entry = 0;
showEntry("LOWER");
return;
}
lcd.setCursor(0, 0);
lcd.print("Got it: ");
lcd.print(secret);
lcd.padLine();
lcd.setCursor(0, 1);
lcd.print(tries);
lcd.print(tries == 1 ? " go. Again..." : " goes. Again...");
lcd.padLine();
delay(2000);
newGame();
showEntry("New number");
}
Code walkthrough
The key functions of the sketch, and what each one does.
newGame sketch.ino
Picks a new secret number between LOWEST and HIGHEST with random, and resets the typed entry and the try count to zero.
static void newGame()
{
secret = (int)random(LOWEST, HIGHEST + 1);
entry = 0;
tries = 0;
}showEntry sketch.ino
Redraws the display. The top line shows a note such as HIGHER or Typing. The bottom line shows Guess: followed by your number, or an underscore when nothing is typed. padLine clears the rest of each line.
static void showEntry(const char *note)
{
lcd.setCursor(0, 0);
lcd.print(note);
lcd.padLine();
lcd.setCursor(0, 1);
lcd.print("Guess: ");
if (entry > 0) {
lcd.print(entry);
} else {
lcd.write('_');
}
lcd.padLine();
}loop sketch.ino
Waits for a key. The first key seeds the generator and starts a game. Digits build the entry. A * clears it. A # checks the range, counts a try, and compares the guess with the secret.
void loop()
{
char key = pad.pressed();
if (key == 0) {
return;
}
// The first press of the session is what the generator is seeded from: a
// person's reaction time, which nothing on the board can work out for itself.
if (!seeded) {
randomSeed(micros());
seeded = true;
newGame();
showEntry("Think of a go");
}
if (key == '*') {
entry = 0;
showEntry("Cleared");
return;
}
if (key >= '0' && key <= '9') {
if (entry < 100) {
entry = entry * 10 + (key - '0');
}
showEntry("Typing");
return;
// ... 36 more lines in the editorTroubleshooting
The usual mistakes with this circuit, and the fix for each.
- The LCD shows nothing or only blocks This project
Check RS on A0, E on A1, and D4 to D7 on A2 to A5, and tie RW and V0 to ground. - Pressing # says 1 to 100 only This project
The guess must be fromLOWESTtoHIGHEST. An empty entry or a number over 100 is rejected. - Pressing one key reports several keys or the wrong key. Keypad
Keep the row and column pin order right. Scan by driving one column low and reading the rows with pull-ups. Use the Keypad library with the correct pin lists, and match the pin map to your wiring. - Keys do not register or float. Keypad
Rows need pull-ups, either INPUT_PULLUP or external resistors. Give each key press a few milliseconds of debounce and check for loose wires on all seven pins. - The screen lights up but shows only a row of solid boxes, or nothing at all. Character LCD
Contrast pin V0 needs a voltage. Connect the middle pin of a 10 k pot to V0, with its ends on 5 V and GND. Also confirm that the code calls lcd.begin(16, 2). - Garbled characters or a blank display even with good contrast. Character LCD
In 4-bit mode only D4 to D7 are used, and D0 to D3 stay unconnected. Tie RW to GND. Match the pins in LiquidCrystal(rs, en, d4, d5, d6, d7) exactly to your wiring. - 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. - Pins misbehave or the board resets when you power motors or servos from it. Arduino Uno R3
Each I/O pin gives about 20 mA, 40 mA max. Power motors and servos from their own supply and join its ground to the Uno's GND.
Change it and keep it
Open it in the editor, change the circuit or the code, and keep your version in a free account.