Touch Game

A MicroPython code example running on a Raspberry Pi Pico 2 W microcontroller connected to a capacitive touch sensor module, a passive piezo buzzer, and an OLED display module (SSD1306) on a breadboard to create a reaction time testing game, and to learn to draw shapes on the OLED display
A capacitive touch sensor detects a touch by measuring changes in electrical charge when a human finger comes close. Humans are full of water and salt, making the body a natural conductor of electricity. It is used in smartphones for multi-touch navigation and gesture detection. In this script, we create a short game to test your reaction time, while also learning how to draw shapes on the OLED screen.
To read the sensor, we run:
1
2
3
4
5
6
7
8
9
10
from time import sleep
from machine import Pin
touch_sensor = Pin(28, Pin.IN)
while True:
if touch_sensor.value() == 1:
print("Touch Detected")
while touch_sensor.value() == 1: pass # wait until touch is not detected
print("No Touch")
For this game, we learn to draw on the OLED screen, using oled.display.ellipse to draw a circle, and oled.display.fill_rect to draw a rectangle. The function below draws happy or sad face based on the parameters passed to it.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
def draw_face(text_line_1 = "", text_line_2 = "", happy = False, sad = False, inverted = False):
center_x = 64
center_y = 42
face_radius = 20
color = 0 if inverted else 1 # default white color, black if inverted == True
oled.display.fill(0)
oled.display.text(text_line_1, 0, 0) # line 1 text
oled.display.text(text_line_2, 0, 56) # line 2 text
oled.display.ellipse(center_x, center_y, face_radius, face_radius, 1, color == 0) # face circle
oled.display.ellipse(center_x - 7, center_y - 8, 1, 1, color, True) # left eye
oled.display.ellipse(center_x + 7, center_y - 8, 1, 1, color, True) # right eye
if happy:
oled.display.ellipse(center_x, center_y, 10, 10, color, False, 0x4 | 0x8) # happy face semi-circle
oled.display.fill_rect(center_x - 11, center_y, 22, 5, 1 - color) # erase part of semi-circle
elif sad:
oled.display.ellipse(center_x, center_y + 14, 10, 10, color, False, 0x1 | 0x2) # sad face semi-circle
oled.display.fill_rect(center_x - 11, center_y + 10, 22, 5, 1 - color) # erase part of semi-circle
else:
oled.display.hline(center_x - 10, center_y + 7, 20, color) # flat face line
oled.display.show()
The main loop of our game is easy to follow because it calls easy to understand function names.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
oled.print("Touch when you", "see happy face") # show instructions at start
sleep(3)
while True:
# Step 1: Start game
start_game()
sleep(uniform(2.0, 7.0)) # wait for random time between 2 and 7 seconds
# Step 2: After a random time from 2 to 7 sec, switch to happy face with label Touch Now
draw_face(happy=True, text_line_1="Touch now")
start_time = ticks_ms()
# Step 3: Wait for touch and show result or false start
wait_for_touch()
# Step 6: Wait 3 seconds, then show touch to play again
sleep(3)
show_touch_to_play_again()
Create a new file in Thonny, with file name touch_game.py. Copy and paste the code below into the file you created.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
print("\nTouch Game")
# DESCRIPTION
# A capacitive touch sensor detects a touch by measuring changes in electrical charge
# when a human finger comes close. Humans are full of water and salt, making the body
# a natural conductor of electricity. It is used in smartphones for multi-touch
# navigation and gesture detection. In this script, we create a short game to test
# your reaction time, while also learning how to draw shapes on the OLED screen.
# CONNECTIONS REQUIRED
# Connect Touch Sensor module (blue board with concentric circles)
# Pins: GND => GND, VCC => VCC, SIG => GP28
from buzzer import Buzzer
from oled import OLED
from machine import Pin
from time import sleep, ticks_ms, ticks_diff
from random import uniform, choice
buzzer = Buzzer(pwm_pin=16)
oled = OLED(scl_pin=15, sda_pin=14)
touch_sensor = Pin(28, Pin.IN)
def draw_face(text_line_1 = "", text_line_2 = "", happy = False, sad = False, inverted = False):
center_x = 64
center_y = 42
face_radius = 20
color = 0 if inverted else 1 # default white color, black if inverted == True
oled.display.fill(0)
oled.display.text(text_line_1, 0, 0) # line 1 text
oled.display.text(text_line_2, 0, 56) # line 2 text
oled.display.ellipse(center_x, center_y, face_radius, face_radius, 1, color == 0) # face circle
oled.display.ellipse(center_x - 7, center_y - 8, 1, 1, color, True) # left eye
oled.display.ellipse(center_x + 7, center_y - 8, 1, 1, color, True) # right eye
if happy:
oled.display.ellipse(center_x, center_y, 10, 10, color, False, 0x4 | 0x8) # happy face semi-circle
oled.display.fill_rect(center_x - 11, center_y, 22, 5, 1 - color) # erase part of semi-circle
elif sad:
oled.display.ellipse(center_x, center_y + 14, 10, 10, color, False, 0x1 | 0x2) # sad face semi-circle
oled.display.fill_rect(center_x - 11, center_y + 10, 22, 5, 1 - color) # erase part of semi-circle
else:
oled.display.hline(center_x - 10, center_y + 7, 20, color) # flat face line
oled.display.show()
def show_false_start():
oled.print("Touched", "too early", "", "Try again")
buzzer.play_tone(200, 1)
def show_result(reaction_time):
# Show result if ended before timeout
if reaction_time < 2000:
if reaction_time < 200:
result = choice(["Impossible", "Lightning", "Aced it"])
elif reaction_time < 400:
result = choice(["Super", "Amazing", "Not bad"])
elif reaction_time < 1000:
result = choice(["Do better", "Step it up", "Try harder"])
else:
result = choice(["Zzzzzzzz", "Snail speed", "Too slow"])
draw_face(happy=True, inverted=True, text_line_1=result, text_line_2=f"{reaction_time/1000:.2f}s")
buzzer.play_melody("E4 0.1", "F4 0.1", "A4 0.3")
# Show sad face if timed out
else:
draw_face(sad=True, inverted=True, text_line_1="Too late")
buzzer.play_melody("F4 0.3", "E4 0.3", "C4 0.8")
def start_game():
buzzer.play_tone(600)
draw_face(text_line_1="Get ready")
def wait_for_touch():
global start_time
draw_face(happy=True, text_line_1="Touch now")
start_time = ticks_ms()
# If already touching at the start, show false start
if touch_sensor.value() == 1: show_false_start()
else:
# Loop until either touched or 2 seconds have passed
while touch_sensor.value() == 0 and ticks_diff(ticks_ms(), start_time) < 2000: pass
# Measure time elapsed as reaction time and show result
end_time = ticks_ms()
reaction_time = ticks_diff(end_time, start_time)
show_result(reaction_time)
def show_touch_to_play_again():
oled.print("Touch to", "play again")
while touch_sensor.value() == 0: pass # wait until touched, which completes the loop cycle and the loop will repeat indefinitely
try:
oled.print("Touch when you", "see happy face") # show instructions at start
sleep(3)
while True:
# Step 1: Start game
start_game()
sleep(uniform(2.0, 7.0)) # wait for random time between 2 and 7 seconds
# Step 2: After a random time from 2 to 7 sec, switch to happy face with label Touch Now
draw_face(happy=True, text_line_1="Touch now")
start_time = ticks_ms()
# Step 3: Wait for touch and show result or false start
wait_for_touch()
# Step 6: Wait 3 seconds, then show touch to play again
sleep(3)
show_touch_to_play_again()
except KeyboardInterrupt:
print("\nInterrupted by user!")
finally:
oled.reset()
buzzer.reset()