Generic ESP32 Examples
These examples run on a ESP32 board with a 320x240 display. They are were tested using a SparkFun ESP32 Thing and a Waveshare 2 inch LCD ST7789 Module. You may need to modify the pin use for your device.
esp32_320x240/lines.py
1"""
2lines.py
3
4 Draws lines and rectangles in random colors at random locations on the
5 display.
6
7"""
8import random
9from machine import Pin, SPI
10import st7789py as st7789
11
12
13def main():
14 # configure display
15
16 spi = SPI(1, baudrate=31250000, sck=Pin(18), mosi=Pin(19))
17
18 tft = st7789.ST7789(
19 spi,
20 320,
21 240,
22 reset=Pin(4, Pin.OUT),
23 cs=Pin(13, Pin.OUT),
24 dc=Pin(12, Pin.OUT),
25 backlight=Pin(15, Pin.OUT),
26 rotation=0)
27
28 tft.fill(st7789.BLUE)
29
30 while True:
31 tft.line(
32 random.randint(0, tft.width),
33 random.randint(0, tft.height),
34 random.randint(0, tft.width),
35 random.randint(0, tft.height),
36 st7789.color565(
37 random.getrandbits(8),
38 random.getrandbits(8),
39 random.getrandbits(8)
40 )
41 )
42
43 width = random.randint(0, tft.width // 2)
44 height = random.randint(0, tft.height // 2)
45 col = random.randint(0, tft.width - width)
46 row = random.randint(0, tft.height - height)
47 tft.fill_rect(
48 col,
49 row,
50 width,
51 height,
52 st7789.color565(
53 random.getrandbits(8),
54 random.getrandbits(8),
55 random.getrandbits(8)
56 )
57 )
58
59
60main()
esp32_320x240/hello.py
1"""
2hello.py
3
4 Writes "Hello!" in random colors at random locations on the display.
5
6"""
7import random
8from machine import Pin, SPI
9import st7789py as st7789
10
11# Choose a font
12
13# from romfonts import vga1_8x8 as font
14# from romfonts import vga2_8x8 as font
15# from romfonts import vga1_8x16 as font
16# from romfonts import vga2_8x16 as font
17# from romfonts import vga1_16x16 as font
18# from romfonts import vga1_bold_16x16 as font
19# from romfonts import vga2_16x16 as font
20# from romfonts import vga2_bold_16x16 as font
21# from romfonts import vga1_16x32 as font
22# from romfonts import vga1_bold_16x32 as font
23# from romfonts import vga2_16x32 as font
24from romfonts import vga2_bold_16x32 as font
25
26
27def main():
28 spi = SPI(1, baudrate=31250000, sck=Pin(18), mosi=Pin(19))
29
30 tft = st7789.ST7789(
31 spi,
32 320,
33 240,
34 reset=Pin(4, Pin.OUT),
35 cs=Pin(13, Pin.OUT),
36 dc=Pin(12, Pin.OUT),
37 backlight=Pin(15, Pin.OUT),
38 rotation=0)
39
40 while True:
41 for rotation in range(4):
42 tft.rotation(rotation)
43 tft.fill(0)
44 col_max = tft.width - font.WIDTH*6
45 row_max = tft.height - font.HEIGHT
46
47 for _ in range(100):
48 tft.text(
49 font,
50 "Hello!",
51 random.randint(0, col_max),
52 random.randint(0, row_max),
53 st7789.color565(
54 random.getrandbits(8),
55 random.getrandbits(8),
56 random.getrandbits(8)),
57 st7789.color565(
58 random.getrandbits(8),
59 random.getrandbits(8),
60 random.getrandbits(8))
61 )
62
63
64main()
esp32_320x240/feathers.py
1"""
2feathers.py
3
4 Smoothly scroll mirrored rainbow colored random curves across the display.
5
6"""
7
8import random
9import math
10import utime
11from machine import Pin, SPI
12import st7789py as st7789
13
14
15def between(left, right, along):
16 """returns a point along the curve from left to right"""
17 dist = (1 - math.cos(along * math.pi)) / 2
18 return left * (1 - dist) + right * dist
19
20
21def color_wheel(position):
22 """returns a 565 color from the given position of the color wheel"""
23 position = (255 - position) % 255
24
25 if position < 85:
26 return st7789.color565(255 - position * 3, 0, position * 3)
27
28 if position < 170:
29 position -= 85
30 return st7789.color565(0, position * 3, 255 - position * 3)
31
32 position -= 170
33 return st7789.color565(position * 3, 255 - position * 3, 0)
34
35
36def main():
37 '''
38 The big show!
39 '''
40 #enable display and clear screen
41
42 spi = SPI(1, baudrate=31250000, sck=Pin(18), mosi=Pin(19))
43
44 tft = st7789.ST7789(
45 spi,
46 320,
47 240,
48 reset=Pin(4, Pin.OUT),
49 cs=Pin(13, Pin.OUT),
50 dc=Pin(12, Pin.OUT),
51 backlight=Pin(15, Pin.OUT),
52 rotation=1)
53
54 tft.fill(st7789.BLACK) # clear screen
55
56 height = tft.height # height of display in pixels
57 width = tft.width # width if display in pixels
58
59 tfa = 0 # top free area when scrolling
60 bfa = 0 # bottom free area when scrolling
61
62 scroll = 0 # scroll position
63 wheel = 0 # color wheel position
64
65 tft.vscrdef(tfa, width, bfa) # set scroll area
66 tft.vscsad(scroll + tfa) # set scroll position
67 tft.fill(st7789.BLACK) # clear screen
68
69 half = (height >> 1) - 1 # half the height of the dislay
70 interval = 0 # steps between new points
71 increment = 0 # increment per step
72 counter = 1 # step counter, overflow to start
73 current_y = 0 # current_y value (right point)
74 last_y = 0 # last_y value (left point)
75
76 # segment offsets
77 x_offsets = [x * (width // 8) -1 for x in range(2,9)]
78
79 while True:
80 # when the counter exceeds the interval, save current_y to last_y,
81 # choose a new random value for current_y between 0 and 1/2 the
82 # height of the display, choose a new random interval then reset
83 # the counter to 0
84
85 if counter > interval:
86 last_y = current_y
87 current_y = random.randint(0, half)
88 counter = 0
89 interval = random.randint(10, 100)
90 increment = 1/interval # increment per step
91
92 # clear the first column of the display and scroll it
93 tft.vline(scroll, 0, height, st7789.BLACK)
94 tft.vscsad(scroll + tfa)
95
96 # get the next point between last_y and current_y
97 tween = int(between(last_y, current_y, counter * increment))
98
99 # draw mirrored pixels across the display at the offsets using the color_wheel effect
100 for i, x_offset in enumerate(x_offsets):
101 tft.pixel((scroll + x_offset) % width, half + tween, color_wheel(wheel+(i<<2)))
102 tft.pixel((scroll + x_offset) % width, half - tween, color_wheel(wheel+(i<<2)))
103
104 # increment scroll, counter, and wheel
105 scroll = (scroll + 1) % width
106 wheel = (wheel + 1) % 256
107 counter += 1
108
109
110main()
esp32_320x240/fonts.py
1"""
2fonts.py
3
4 Pages through all characters of four fonts on the display.
5
6"""
7import utime
8from machine import Pin, SPI
9import st7789py as st7789
10
11# Choose fonts
12
13# from romfonts import vga1_8x8 as font
14from romfonts import vga2_8x8 as font1
15# from romfonts import vga1_8x16 as font
16from romfonts import vga2_8x16 as font2
17# from romfonts import vga1_16x16 as font
18# from romfonts import vga1_bold_16x16 as font
19# from romfonts import vga2_16x16 as font
20from romfonts import vga2_bold_16x16 as font3
21# from romfonts import vga1_16x32 as font
22# from romfonts import vga1_bold_16x32 as font
23# from romfonts import vga2_16x32 as font
24from romfonts import vga2_bold_16x32 as font4
25
26
27def main():
28 spi = SPI(1, baudrate=31250000, sck=Pin(18), mosi=Pin(19))
29
30 tft = st7789.ST7789(
31 spi,
32 320,
33 240,
34 reset=Pin(4, Pin.OUT),
35 cs=Pin(13, Pin.OUT),
36 dc=Pin(12, Pin.OUT),
37 backlight=Pin(15, Pin.OUT),
38 rotation=0)
39
40 tft.vscrdef(40, 240, 40)
41
42 while True:
43 for font in (font1, font2, font3, font4):
44 tft.fill(st7789.BLUE)
45 line = 0
46 col = 0
47
48 for char in range(font.FIRST, font.LAST):
49 tft.text(font, chr(char), col, line, st7789.WHITE, st7789.BLUE)
50 col += font.WIDTH
51 if col > tft.width - font.WIDTH:
52 col = 0
53 line += font.HEIGHT
54
55 if line > tft.height-font.HEIGHT:
56 utime.sleep(3)
57 tft.fill(st7789.BLUE)
58 line = 0
59 col = 0
60
61 utime.sleep(3)
62
63
64main()
esp32_320x240/scroll.py
1"""
2fonts.py
3
4 Smoothly scrolls all font characters up the screen on the display.
5 Only works with fonts with heights that are even multiples of
6 the screen height, (i.e. 8 or 16 pixels high)
7
8"""
9import utime
10import random
11from machine import Pin, SPI
12import st7789py as st7789
13
14# choose a font
15
16# from romfonts import vga1_8x8 as font
17# from romfonts import vga2_8x8 as font
18# from romfonts import vga1_8x16 as font
19# from romfonts import vga2_8x16 as font
20# from romfonts import vga1_16x16 as font
21# from romfonts import vga1_bold_16x16 as font
22# from romfonts import vga2_16x16 as font
23from romfonts import vga2_bold_16x16 as font
24
25
26def main():
27 spi = SPI(1, baudrate=31250000, sck=Pin(18), mosi=Pin(19))
28
29 tft = st7789.ST7789(
30 spi,
31 320,
32 240,
33 reset=Pin(4, Pin.OUT),
34 cs=Pin(13, Pin.OUT),
35 dc=Pin(12, Pin.OUT),
36 backlight=Pin(15, Pin.OUT),
37 rotation=0)
38
39 last_line = tft.height - font.HEIGHT
40 tfa = 0
41 tfb = 0
42 tft.vscrdef(tfa, 240, tfb)
43
44 tft.fill(st7789.BLUE)
45 scroll = 0
46 character = 0
47 while True:
48 tft.fill_rect(0, scroll, tft.width, 1, st7789.BLUE)
49
50 if scroll % font.HEIGHT == 0:
51 tft.text(
52 font,
53 '\\x{:02x}= {:s} '.format(character, chr(character)),
54 0,
55 (scroll + last_line) % tft.height,
56 st7789.WHITE,
57 st7789.BLUE)
58
59 character = character + 1 if character < 256 else 0
60
61 tft.vscsad(scroll + tfa)
62 scroll += 1
63
64 if scroll == tft.height:
65 scroll = 0
66
67 utime.sleep(0.01)
68
69
70main()
esp32_320x240/toasters.py
Flying toasters sprite demo using bitmaps created from spritesheet using the sprites2bitmap.py utility. See the maketoast shell script for the command line used to create the toast_bitmaps.py from the toasters.bmp image.
1'''
2toasters.py - Flying Toasters(ish) an ESP-32 and ST7789 240x320 display.
3
4 Uses spritesheet from CircuitPython_Flying_Toasters pendant project
5 https://learn.adafruit.com/circuitpython-sprite-animation-pendant-mario-clouds-flying-toasters
6
7 Convert spritesheet bmp to tft.bitmap() method compatible python module using:
8 python3 ./sprites2bitmap.py toasters.bmp 64 64 4 > toast_bitmaps.py
9
10'''
11
12import gc
13import time
14import random
15from machine import Pin, SPI
16import st7789
17import toast_bitmaps
18
19TOASTER_FRAMES = [0, 1, 2, 3]
20TOAST_FRAMES = [4]
21
22def collide(a_col, a_row, a_width, a_height, b_col, b_row, b_width, b_height):
23 '''return true if two rectangles overlap'''
24 return (a_col + a_width >= b_col and a_col <= b_col + b_width
25 and a_row + a_height >= b_row and a_row <= b_row + b_height)
26
27def random_start(tft, sprites, bitmaps, num):
28 '''
29 Return a random location along the top or right of the screen, if that location would overlaps
30 with another sprite return (0,0). This allows the other sprites to keep moving giving the next
31 random_start a better chance to avoid a collision.
32
33 '''
34 # 50/50 chance to try along the top/right half or along the right/top half of the screen
35 if random.getrandbits(1):
36 row = 1
37 col = random.randint(bitmaps.WIDTH//2, tft.width()-bitmaps.WIDTH)
38 else:
39 col = tft.width() - bitmaps.WIDTH
40 row = random.randint(1, tft.height() // 2)
41
42 if any(collide(
43 col, row, bitmaps.WIDTH, bitmaps.HEIGHT,
44 sprite.col, sprite.row, sprite.width, sprite.height)
45 for sprite in sprites if num != sprite.num):
46
47 col = 0
48 row = 0
49
50 return (col, row)
51
52def main():
53
54 class Toast():
55 '''
56 Toast class to keep track of toaster and toast sprites
57 '''
58 def __init__(self, sprites, bitmaps, frames):
59 '''create new sprite in random location that does not overlap other sprites'''
60 self.num = len(sprites)
61 self.bitmaps = bitmaps
62 self.frames = frames
63 self.steps = len(frames)
64 self.col, self.row = random_start(tft, sprites, bitmaps, self.num)
65 self.width = bitmaps.WIDTH
66 self.height = bitmaps.HEIGHT
67 self.last_col = self.col
68 self.last_row = self.row
69 self.step = random.randint(0, self.steps)
70 self.dir_col = -random.randint(2, 5)
71 self.dir_row = 2
72 self.prev_dir_col = self.dir_col
73 self.prev_dir_row = self.dir_row
74 self.iceberg = 0
75
76 def clear(self):
77 '''clear above and behind sprite'''
78 tft.fill_rect(
79 self.col, self.row-1, self.width, self.dir_row+1,
80 st7789.BLACK)
81
82 tft.fill_rect(
83 self.col+self.width+self.dir_col, self.row,
84 -self.dir_col, self.height, st7789.BLACK)
85
86 def erase(self):
87 '''erase last postion of sprite'''
88 tft.fill_rect(
89 self.last_col, self.last_row, self.width, self.height, st7789.BLACK)
90
91 def move(self, sprites):
92 '''step frame and move sprite'''
93
94 if self.steps:
95 self.step = (self.step + 1) % self.steps
96
97 self.last_col = self.col
98 self.last_row = self.row
99 new_col = self.col + self.dir_col
100 new_row = self.row + self.dir_row
101
102 # if new location collides with another sprite, change direction for 32 frames
103
104 for sprite in sprites:
105 if (
106 self.num != sprite.num
107 and collide(
108 new_col, new_row, self.width, self.height,
109 sprite.col, sprite.row, sprite.width, sprite.height,
110 )
111 and (self.col > sprite.col)):
112
113 self.iceberg = 32
114 self.dir_col = -1
115 self.dir_row = 3
116 new_col = self.col + self.dir_col
117 new_row = self.row + self.dir_row
118
119 self.col = new_col
120 self.row = new_row
121
122 # if new location touches edge of screen, erase then set new start location
123 if self.col <= 0 or self.row > tft.height() - self.height:
124 self.erase()
125 self.dir_col = -random.randint(2, 5)
126 self.dir_row = 2
127 self.col, self.row = random_start(tft, sprites, self.bitmaps, self.num)
128
129 # Track post collision direction change
130 if self.iceberg:
131 self.iceberg -= 1
132 if self.iceberg == 1:
133 self.dir_col = self.prev_dir_col
134 self.dir_row = self.prev_dir_row
135
136 def draw(self):
137 '''if the location is not 0,0 draw current frame of sprite at it's location'''
138 if self.col and self.row:
139 tft.bitmap(self.bitmaps, self.col, self.row, self.frames[self.step])
140
141 # configure spi interface
142 spi = SPI(1, baudrate=31250000, sck=Pin(18), mosi=Pin(19))
143
144 # configure display
145 tft = st7789.ST7789(
146 spi,
147 240,
148 320,
149 reset=Pin(4, Pin.OUT),
150 cs=Pin(13, Pin.OUT),
151 dc=Pin(12, Pin.OUT),
152 backlight=Pin(15, Pin.OUT),
153 rotation=1,
154 buffer_size=64*62*2)
155
156 # init and clear screen
157 tft.init()
158 tft.fill(st7789.BLACK)
159
160 # create toast spites and set animation frames
161 sprites = []
162 sprites.append(Toast(sprites, toast_bitmaps, TOAST_FRAMES))
163 sprites.append(Toast(sprites, toast_bitmaps, TOASTER_FRAMES))
164 sprites.append(Toast(sprites, toast_bitmaps, TOASTER_FRAMES))
165
166 # move and draw sprites
167
168 while True:
169 for sprite in sprites:
170 sprite.clear()
171 sprite.move(sprites)
172 sprite.draw()
173
174 gc.collect()
175 time.sleep(0.01)
176
177main()