├── .gitignore ├── LICENSE ├── README.md ├── docs ├── gauge-generator │ ├── Oswald-Bold.ttf │ ├── Oswald-Light.ttf │ ├── gauge1.py │ └── pointer1.py ├── gc9a01_demo1.jpg ├── gc9a01_demo2.jpg ├── gc9a01_pico_wiring1.jpg └── gc9a01_qtpy_wiring1.jpg ├── examples ├── eyeballs │ ├── gc9a01_lizard_eye.py │ ├── gc9a01_multi_eyeball.py │ ├── imgs │ │ ├── Lizard_Iris_White.bmp │ │ ├── Lizard_Sclera.bmp │ │ ├── eye0_ball2.bmp │ │ └── eye0_iris0.bmp │ ├── qteye.py │ └── qteye_person_sensor.py ├── gc9a01_gauge_knob.py ├── gc9a01_hellocircles.py ├── gc9a01_hellocircles_pico_compact.py ├── gc9a01_helloworld.py ├── gc9a01_picture_locket.py ├── imgs │ ├── dial-background.bmp │ ├── dial-percenti.bmp │ ├── lars240.bmp │ ├── max1.bmp │ ├── max2.bmp │ ├── pointer-red-basic-30x140-c15x105.bmp │ └── pointer-red-basic-30x140.bmp └── pico_boot.py └── old_driver └── todbot_gc9a01.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | #Pipfile.lock 93 | 94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 95 | __pypackages__/ 96 | 97 | # Celery stuff 98 | celerybeat-schedule 99 | celerybeat.pid 100 | 101 | # SageMath parsed files 102 | *.sage.py 103 | 104 | # Environments 105 | .env 106 | .venv 107 | env/ 108 | venv/ 109 | ENV/ 110 | env.bak/ 111 | venv.bak/ 112 | 113 | # Spyder project settings 114 | .spyderproject 115 | .spyproject 116 | 117 | # Rope project settings 118 | .ropeproject 119 | 120 | # mkdocs documentation 121 | /site 122 | 123 | # mypy 124 | .mypy_cache/ 125 | .dmypy.json 126 | dmypy.json 127 | 128 | # Pyre type checker 129 | .pyre/ 130 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Tod E. Kurt 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CircuitPython GC9A01 demos 2 | 3 | Demos showing how to use [CircuitPython displayio driver](https://github.com/tylercrumpton/CircuitPython_GC9A01) for GC9A01-based round LCDs. This driver is available in the [CircuitPython Community Bundle](https://github.com/adafruit/CircuitPython_Community_Bundle), or you can install it by hand by copying the `gc9a01.py` file to your `CIRCUITPY/lib` directory, or use `circup install gc9a01`. 4 | 5 | 6 | 7 | 8 | ## Usage 9 | 10 | ```py 11 | import board 12 | import busio 13 | import fourwire 14 | import displayio 15 | import gc9a01 16 | displayio.release_displays() 17 | # Raspberry Pi Pico pinout, one possibility, at "southwest" of board 18 | tft_clk = board.GP10 # must be a SPI CLK 19 | tft_mosi= board.GP11 # must be a SPI TX 20 | tft_rst = board.GP12 21 | tft_dc = board.GP13 22 | tft_cs = board.GP14 # optional, can be "None" 23 | tft_bl = board.GP15 # optional, can be "None" 24 | spi = busio.SPI(clock=tft_clk, MOSI=tft_mosi) 25 | display_bus = fourwire.FourWire(spi, command=tft_dc, chip_select=tft_cs, reset=tft_rst) 26 | display = gc9a01.GC9A01(display_bus, width=240, height=240, backlight_pin=tft_bl) 27 | 28 | # ... normal circuitpython displayio stuff 29 | ``` 30 | 31 | ## Installation 32 | 33 | Each of the .py files in "examples" is its own demo. Copy one of these to be your CIRCUITPY's "code.py", like: 34 | ``` 35 | cp gc9a01_hellocircles.py /Volumes/CIRCUITPY/code.py 36 | ``` 37 | 38 | You'll need to install various libraries. Most notably the `gc9a01` library. You may also 39 | need the `adafruit_display_text` and `adafruit_imageload`, depending on the example. 40 | The easiest way to install these is from a terminal: 41 | ``` 42 | circup install gc9a01 43 | circup install adafruit_display_text 44 | circup install adafruit_imageload 45 | ``` 46 | 47 | 48 | ## Examples 49 | 50 | Check out the 'examples' directory for complete examples: 51 | 52 | - 'gc9a01_helloworld' -- shows one way of doing polar coordinates 53 | - 'gc9a01_hellocircles' -- similar to above but with floating circles using `vectorio` 54 | - 'gc9a01_picture_locket' -- display a series of pictures, makes a nice locket if used with a QT Py Haxpress 55 | - 'gc9a01_gauge_knob' -- round dial gauge using gauge background & dial bitmaps, showing `bitmaptools.rotozoom` 56 | 57 | The examples attempt to auto-detect the board you're using. The currently detected boards: 58 | 59 | - [QT Py M0 Haxpress](https://circuitpython.org/board/qtpy_m0_haxpress/) 60 | - [QT Py RP2040](https://circuitpython.org/board/adafruit_qtpy_rp2040/) 61 | - [Raspberry Pi Pico](https://circuitpython.org/board/raspberry_pi_pico/) 62 | - [ItsyBitsy M4 Express](https://circuitpython.org/board/itsybitsy_m4_express/) 63 | 64 | ### Eyeballs demos 65 | 66 | Additionally, there are several demos in the "examples/eyeballs" directory that use these round displays to make moving eyes. 67 | 68 | - 'eyeballs/qteye.py' -- single eyeball (or two eyeballs wired in parallel) on a QT PY RP2040 or similar 69 | - 'eyeballs/qtpy_person_sensor.py' -- single eyeball that tracks your face, thanks to a Person Sensor module 70 | - 'eyeballs/gc9a01_lizard_eye.py' -- similar to "qteye" but uses a cool lizard eye (thx @DJDevon3!) 71 | - 'eyeballs/gc9a01_multi_eyeball.py' -- independent multiple eyes usigng a [recompiled CircuitPython](https://todbot.com/blog/2022/05/19/multiple-displays-in-circuitpython-compiling-custom-circuitpython/) 72 | 73 | 74 | ## Wiring 75 | 76 | Wiring is dependent on board you're hooking it up to. The "SCL" and "SDA" lines need to be 77 | hooked up to SPI pins "SCK" and "MOSI/TX". The `gc9a01_helloworld.py` has example wirings for three 78 | different boards. Here is an example for the Pico: 79 | 80 | - VCC - Pico 3.3V(out) 81 | - Gnd - Pico Ground 82 | - SCL - Pico GP10 (SPI1 SCK) 83 | - SDA - Pico GP11 (SPI1 TX) 84 | - RES - Pico GP12 85 | - DC - Pico GP13 86 | - CS - Pico GP14 87 | - BLK - Pico GP15 (can be omitted if you don't need backlight brightness control) 88 | 89 | 90 | 91 | 92 | Here is an example for a QT Py Haxpress: 93 | 94 | - VCC - QT Py 3.3V 95 | - Gnd - QT Py Ground 96 | - SCL - QT Py SCK 97 | - SDA - QT Py MO 98 | - RES - QT Py TX 99 | - DC - QT Py A3 100 | - CS - QT Py A2 101 | - BLK - unconnected 102 | 103 | 104 | 105 | 106 | 107 | ## Building your own dial gauges 108 | 109 | There is a partial Python port of [@bikerglen's gauge-generator](https://github.com/bikerglen/round-lcd-gauges/tree/main/gauge-generator) in [`docs/gauge-generator`](./docs/gauge-generator). These scripts use the wonderful [Wand](https://docs.wand-py.org/en/0.6.6/) Python wrapper for ImageMagick's C API. 110 | 111 | 112 | ## Future Project Ideas: 113 | - bargraph display using vectorio 114 | 115 | 116 | 117 | ### Notes to self: 118 | 119 | - This repo started out as a GC9A01 driver for CircuitPython, but [@tylercrumpton](https://github.com/tylercrumpton/CircuitPython_GC9A01) beat me to the [CircuitPython Community Bundle](https://github.com/adafruit/CircuitPython_Community_Bundle) by a few days. Now it's a repo of demos 120 | -------------------------------------------------------------------------------- /docs/gauge-generator/Oswald-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todbot/CircuitPython_GC9A01_demos/877c351620a2d21fcbd5fdb2ae3ea523030e1ffd/docs/gauge-generator/Oswald-Bold.ttf -------------------------------------------------------------------------------- /docs/gauge-generator/Oswald-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todbot/CircuitPython_GC9A01_demos/877c351620a2d21fcbd5fdb2ae3ea523030e1ffd/docs/gauge-generator/Oswald-Light.ttf -------------------------------------------------------------------------------- /docs/gauge-generator/gauge1.py: -------------------------------------------------------------------------------- 1 | # 2 | # gauge1.py -- Gauge generator, 3 | # outputs "dial-background.bmp" file for use with CircuitPython 4 | # 5 | # Translated directly from bikerglen's "gauge-generator": 6 | # https://github.com/bikerglen/round-lcd-gauges/tree/main/gauge-generator 7 | # 8 | # To use, install ImageMagic drawing API: 9 | # brew install imagemagick 10 | # pip3 install Wand 11 | # python3 gauage1.py 12 | # 13 | 14 | import math 15 | 16 | from wand.image import Image 17 | from wand.drawing import Drawing 18 | from wand.color import Color 19 | 20 | # 21 | def draw_dial_background(draw): 22 | draw.stroke_color = Color('#d0d0d0') 23 | draw.stroke_width = 1 24 | draw.fill_color = Color('#f0f0f0') 25 | draw.circle( (120,120), (1,120) ) # center point, perimeter point 26 | draw.stroke_color = Color('#e8e8e8') 27 | draw.circle( (120,120), (2,120) ) 28 | draw.stroke_color = Color('#d8d8d8') 29 | draw.circle( (120,120), (5,120) ) 30 | draw.stroke_color = Color('#d0d0d0') 31 | draw.circle( (120,120), (6,120) ) 32 | draw.stroke_color = Color('#c8c8c8') 33 | draw.circle( (120,120), (7,120) ) 34 | draw.stroke_color = Color('#c0c0c0') 35 | draw.circle( (120,120), (8,120) ) 36 | draw.stroke_color = Color('#e0e0e0') 37 | draw.circle( (120,120), (9,120) ) 38 | draw.stroke_color = Color('#e0e0e0') 39 | draw.circle( (120,120), (10,120) ) 40 | draw.stroke_color = Color('#e0e0e0') 41 | draw.circle( (120,120), (11,120) ) 42 | draw.stroke_color = Color('#c8c8c8') 43 | draw.circle( (120,120), (12,120) ) 44 | 45 | # 46 | def draw_dial_ticks(draw, color, stroke_width=1.5, number_of_ticks=360, 47 | start_angle=0, stop_angle=360, start_radius=100, end_radius=120): 48 | for i in range(number_of_ticks): 49 | angle = start_angle + i*(stop_angle-start_angle) /(number_of_ticks-1) 50 | angle_corrected = angle - 90 51 | angle_radians = angle_corrected / 180 * math.pi 52 | 53 | x1 = 120 + math.cos(angle_radians) * start_radius 54 | y1 = 120 + math.sin(angle_radians) * start_radius 55 | x2 = 120 + math.cos(angle_radians) * end_radius 56 | y2 = 120 + math.sin(angle_radians) * end_radius 57 | draw.stroke_color = color 58 | draw.line((x1,y1),(x2,y2)) 59 | 60 | # 61 | def label_dial_ticks(draw, image, color, font, radius, number_ticks, 62 | start_angle, stop_angle, start_label, stop_label, label_format): 63 | draw.fill_color = color 64 | draw.stroke_color = color 65 | draw.font = font 66 | draw.font_size = 22 67 | draw.text_alignment = 'center' 68 | 69 | for i in range(number_ticks): 70 | angle = start_angle + i* (stop_angle-start_angle) / (number_ticks-1) 71 | angle_corrected = angle - 90 72 | angle_radians = angle_corrected / 180.0 * math.pi 73 | label_value = start_label + i*(stop_label-start_label) / (number_ticks-1) 74 | label_string = label_format % label_value 75 | 76 | metrics = draw.get_font_metrics(image, label_string) 77 | ascent = metrics.ascender + metrics.descender 78 | roffset = math.sqrt( (math.cos(angle_radians) * metrics.text_width/2) * 79 | (math.cos(angle_radians) * metrics.text_width/2) + 80 | (math.sin(angle_radians) * ascent/2) * 81 | (math.sin(angle_radians) * ascent/2)) 82 | 83 | 84 | x1 = int(120 + math.cos(angle_radians) * (radius - roffset)) 85 | y1 = int(120 + math.sin(angle_radians) * (radius - roffset) + ascent/2.0) 86 | draw.text( x1, y1, label_string) 87 | 88 | # now create the image and write it out 89 | # 90 | with Image(width=240, height=240, background=Color('#ffffff')) as img: 91 | 92 | with Drawing() as draw: 93 | 94 | draw_dial_background(draw) 95 | 96 | draw_dial_ticks(draw, Color('#606060'), 1.5, 101, -150, 150, 97, 104) # minor 97 | draw_dial_ticks(draw, Color('#606060'), 1.5, 11, -150, 150, 89, 104) # major 98 | 99 | label_dial_ticks(draw, img, Color("#606060"), "Oswald-Light.ttf", 100 | 85, 11, -150,150, 0,100, "%0.0f") 101 | 102 | draw.draw(img) 103 | 104 | img.type = 'palette' # CircuitPython can only do palette BMP3 105 | img.quantize(16) # reduce colors for size 106 | img.save(filename='BMP3:dial-background.bmp') 107 | 108 | -------------------------------------------------------------------------------- /docs/gauge-generator/pointer1.py: -------------------------------------------------------------------------------- 1 | # 2 | # pointer1.py -- Gauge pointer generator, 3 | # outputs "pointer-red-basic.bmp" file for use with CircuitPython 4 | # 5 | # Translated directly from bikerglen's "gauge-generator": 6 | # https://github.com/bikerglen/round-lcd-gauges/tree/main/gauge-generator 7 | # 8 | # To use, install ImageMagic drawing API: 9 | # brew install imagemagick 10 | # pip3 install Wand 11 | # python3 gauage1.py 12 | # 13 | 14 | import math 15 | 16 | from wand.image import Image 17 | from wand.drawing import Drawing 18 | from wand.color import Color 19 | 20 | def draw_pointer_knub(draw, color, center_x, center_y, radius, opacity): 21 | draw.fill_color = color 22 | draw.stroke_color = Color('#333333') 23 | draw.stroke_opacity = 0.5 24 | draw.stroke_width = 2 25 | draw.fill_opacity = opacity 26 | draw.circle( (center_x, center_y), (center_x - radius, center_y)) 27 | # draw.stroke_color=Color('#aaaaaa') 28 | # draw.circle( (center_x, center_y), (center_x - radius + 3, center_y)) 29 | 30 | def draw_pointer_needle(draw, color, stroke_width, tip_radius, tail_radius, opacity): 31 | # draw.stroke_opacity = 0.5 32 | # draw.stroke_color = Color('#333333') 33 | # draw.stroke_width = stroke_width+2 34 | # draw.line( (120, 120 + tail_radius+1), (120,120-tip_radius-1)) 35 | draw.stroke_width = stroke_width 36 | draw.stroke_color = color 37 | draw.stroke_opacity = opacity 38 | draw.line( (120, 120 + tail_radius), (120,120-tip_radius)) 39 | #draw.line( (119.5, 119.5 + tail_radius), (119.5,119.5-tip_radius)) 40 | 41 | with Image(width=240, height=240, background=Color('none')) as img: 42 | 43 | with Drawing() as draw: 44 | 45 | # draw_pointer_needle(draw, Color("#F45700"), 2.25, 100, 30, 1); 46 | draw_pointer_needle(draw, Color("#F45700"), 2.25, 102.5, 30.5, 1); 47 | draw_pointer_knub(draw, Color("#F45700"), 120, 120, 10, 1); 48 | # draw_pointer_knub(draw, Color("#F45700"), 119.5, 119.5, 10.1, 1); 49 | 50 | draw.draw(img) 51 | 52 | img.crop( 105, 15, 135, 155) 53 | 54 | fname = 'pointer-red-basic-30x140-c15x105' 55 | img.save(filename=fname+'.png') 56 | img.type = 'palette' # CircuitPython can only do palette BMP3 57 | img.quantize(16) # reduce colors for size 58 | img.save(filename='BMP3:'+fname+'.bmp') 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /docs/gc9a01_demo1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todbot/CircuitPython_GC9A01_demos/877c351620a2d21fcbd5fdb2ae3ea523030e1ffd/docs/gc9a01_demo1.jpg -------------------------------------------------------------------------------- /docs/gc9a01_demo2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todbot/CircuitPython_GC9A01_demos/877c351620a2d21fcbd5fdb2ae3ea523030e1ffd/docs/gc9a01_demo2.jpg -------------------------------------------------------------------------------- /docs/gc9a01_pico_wiring1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todbot/CircuitPython_GC9A01_demos/877c351620a2d21fcbd5fdb2ae3ea523030e1ffd/docs/gc9a01_pico_wiring1.jpg -------------------------------------------------------------------------------- /docs/gc9a01_qtpy_wiring1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todbot/CircuitPython_GC9A01_demos/877c351620a2d21fcbd5fdb2ae3ea523030e1ffd/docs/gc9a01_qtpy_wiring1.jpg -------------------------------------------------------------------------------- /examples/eyeballs/gc9a01_lizard_eye.py: -------------------------------------------------------------------------------- 1 | # qteye.py - a stand-alone GC9A01 round LCD "eye" on a QTPy 2 | # 23 Oct 2022 - @todbot / Tod Kurt 3 | # Gator Eyes by 2022 DJDevon3 4 | # Part of circuitpython-tricks/larger-tricks/eyeballs 5 | # also see: https://todbot.com/blog/2022/05/19/multiple-displays-in-circuitpython-compiling-custom-circuitpython/ 6 | 7 | import time, math, random 8 | import board, busio 9 | import displayio 10 | import adafruit_imageload 11 | import gc9a01 12 | 13 | displayio.release_displays() 14 | 15 | dw, dh = 240,240 # display dimensions 16 | 17 | # load our eye and iris bitmaps 18 | eyeball_bitmap, eyeball_pal = adafruit_imageload.load("images/Lizard_Sclera.bmp") 19 | iris_bitmap, iris_pal = adafruit_imageload.load("images/Lizard_Iris_White.bmp") 20 | iris_pal.make_transparent(244) 21 | 22 | # compute or declare some useful info about the eyes 23 | iris_w, iris_h = iris_bitmap.width, iris_bitmap.height # iris is normally 110x110 24 | iris_cx, iris_cy = dw//2 - iris_w//2, dh//2 - iris_h//2 25 | r = 15 # allowable deviation from center for iris 26 | 27 | tft0_clk = board.SCL 28 | tft0_mosi = board.SDA 29 | tft_L0_rst = board.D9 30 | tft_L0_dc = board.D5 31 | tft_L0_cs = board.D6 32 | 33 | spi0 = busio.SPI(clock=tft0_clk, MOSI=tft0_mosi) 34 | 35 | # class to help us track eye info (not needed for this use exactly, but I find it interesting) 36 | class Eye: 37 | def __init__(self, spi, dc, cs, rst, rot=0, eye_speed=0.5, twitch=1): 38 | display_bus = displayio.FourWire(spi, command=dc, chip_select=cs, reset=rst) 39 | display = gc9a01.GC9A01(display_bus, width=dw, height=dh, rotation=rot) 40 | main = displayio.Group() 41 | display.show(main) 42 | self.display = display 43 | self.eyeball = displayio.TileGrid(eyeball_bitmap, pixel_shader=eyeball_pal) 44 | self.iris = displayio.TileGrid(iris_bitmap, pixel_shader=iris_pal, x=iris_cx,y=iris_cy) 45 | main.append(self.eyeball) 46 | main.append(self.iris) 47 | self.x, self.y = iris_cx, iris_cy 48 | self.tx, self.ty = self.x, self.y 49 | self.next_time = time.monotonic() 50 | self.eye_speed = eye_speed 51 | self.twitch = twitch 52 | 53 | def update(self): 54 | self.x = self.x * (1-self.eye_speed) + self.tx * self.eye_speed # "easing" 55 | self.y = self.y * (1-self.eye_speed) + self.ty * self.eye_speed 56 | self.iris.x = int( self.x ) 57 | self.iris.y = int( self.y ) 58 | if time.monotonic() > self.next_time: 59 | t = random.uniform(0.25,self.twitch) 60 | self.next_time = time.monotonic() + t 61 | self.tx = iris_cx + random.uniform(-r,r) 62 | self.ty = iris_cy + random.uniform(-r,r) 63 | self.display.refresh() 64 | 65 | # a list of all the eyes, in this case, only one 66 | the_eyes = [ 67 | Eye( spi0, tft_L0_dc, tft_L0_cs, tft_L0_rst, rot=0), 68 | ] 69 | 70 | while True: 71 | for eye in the_eyes: 72 | eye.update() 73 | -------------------------------------------------------------------------------- /examples/eyeballs/gc9a01_multi_eyeball.py: -------------------------------------------------------------------------------- 1 | # gc9a01_multi_eyeball_code.py -- 2 | # 14 Oct 2022 - @todbot / Tod Kurt 3 | # Part of https://github.com/todbot/CircuitPython_GC9A01_demos 4 | # Requires rebuilt CircuitPython with CIRCUITPY_DISPLAY_LIMIT set to 3 5 | # also see: https://todbot.com/blog/2022/05/19/multiple-displays-in-circuitpython-compiling-custom-circuitpython/ 6 | 7 | import time, math, random 8 | import board, busio 9 | import displayio 10 | import adafruit_imageload 11 | import gc9a01 12 | 13 | displayio.release_displays() 14 | 15 | dw, dh = 240,240 # display dimensions 16 | 17 | eyeball_bitmap, eyeball_pal = adafruit_imageload.load("imgs/eye0_ball2.bmp") 18 | iris_bitmap, iris_pal = adafruit_imageload.load("imgs/eye0_iris0.bmp") 19 | iris_pal.make_transparent(0) 20 | 21 | iris_w, iris_h = iris_bitmap.width, iris_bitmap.height # iris is normally 110x110 22 | iris_cx, iris_cy = dw//2 - iris_w//2, dh//2 - iris_h//2 23 | 24 | tft0_clk = board.GP18 25 | tft0_mosi = board.GP19 26 | 27 | tft1_clk = board.GP10 28 | tft1_mosi = board.GP11 29 | 30 | tft_L0_rst = board.GP21 31 | tft_L0_dc = board.GP22 32 | tft_L0_cs = board.GP20 33 | 34 | tft_R0_rst = board.GP26 35 | tft_R0_dc = board.GP27 36 | tft_R0_cs = board.GP28 37 | 38 | tft_L1_rst = board.GP14 39 | tft_L1_dc = board.GP12 40 | tft_L1_cs = board.GP13 41 | 42 | tft_R1_rst = board.GP3 43 | tft_R1_dc = board.GP4 44 | tft_R1_cs = board.GP5 45 | 46 | spi0 = busio.SPI(clock=tft0_clk, MOSI=tft0_mosi) 47 | spi1 = busio.SPI(clock=tft1_clk, MOSI=tft1_mosi) 48 | 49 | def eye_init(spi, dc, cs, rst, rot): 50 | display_bus = displayio.FourWire(spi, command=dc, chip_select=cs, reset=rst) 51 | display = gc9a01.GC9A01(display_bus, width=dw, height=dh, rotation=rot) 52 | main = displayio.Group() 53 | display.show(main) 54 | eyeball = displayio.TileGrid(eyeball_bitmap, pixel_shader=eyeball_pal) 55 | iris = displayio.TileGrid(iris_bitmap, pixel_shader=iris_pal, x = iris_cx, y = iris_cy ) 56 | main.append(eyeball) 57 | main.append(iris) 58 | return (display, eyeball, iris) 59 | 60 | 61 | (display_L0, eyeball_L0, iris_L0) = eye_init( spi0, tft_L0_dc, tft_L0_cs, tft_L0_rst, rot=0) 62 | (display_R0, eyeball_R0, iris_R0) = eye_init( spi0, tft_R0_dc, tft_R0_cs, tft_R0_rst, rot=0) 63 | (display_L1, eyeball_L1, iris_L1) = eye_init( spi1, tft_L1_dc, tft_L1_cs, tft_L1_rst, rot=0) 64 | # can't do four yet 65 | #(display_R1, eyeball_R1, iris_R1) = eye_init( spi1, tft_R1_dc, tft_R1_cs, tft_R1_rst, rot=0) 66 | 67 | the_eyes = [ 68 | # x,y, tx,ty, next_time 69 | [display_L0, eyeball_L0, iris_L0, iris_cx, iris_cy, 0, 0, 0 ], 70 | [display_R0, eyeball_R0, iris_R0, iris_cx, iris_cy, 0, 0, 0 ], 71 | [display_L1, eyeball_L1, iris_L1, iris_cx, iris_cy, 0, 0, 0 ], 72 | #[display_R1, eyeball_R1, iris_R1, iris_cx, iris_cy, 0, 0, 0 ], # can't do four yet 73 | ] 74 | 75 | 76 | r = 17 # allowable deviation from center for iris 77 | 78 | while True: 79 | for i in range(len(the_eyes)): 80 | (display, eyeball, iris, x,y, tx,ty, next_time) = the_eyes[i] 81 | x = x * 0.5 + tx * 0.5 # "easing" 82 | y = y * 0.5 + ty * 0.5 83 | iris.x = int( x ) 84 | iris.y = int( y ) 85 | the_eyes[i][3] = x 86 | the_eyes[i][4] = y 87 | if time.monotonic() > next_time: 88 | next_time = time.monotonic() + random.uniform(0,2) 89 | tx = iris_cx + random.uniform(-r,r) 90 | ty = iris_cy + random.uniform(-r,r) 91 | the_eyes[i][5] = tx 92 | the_eyes[i][6] = ty 93 | the_eyes[i][7] = next_time # FIXME 94 | print("change!") 95 | #display.refresh( target_frames_per_second=20 ) 96 | display.refresh() 97 | -------------------------------------------------------------------------------- /examples/eyeballs/imgs/Lizard_Iris_White.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todbot/CircuitPython_GC9A01_demos/877c351620a2d21fcbd5fdb2ae3ea523030e1ffd/examples/eyeballs/imgs/Lizard_Iris_White.bmp -------------------------------------------------------------------------------- /examples/eyeballs/imgs/Lizard_Sclera.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todbot/CircuitPython_GC9A01_demos/877c351620a2d21fcbd5fdb2ae3ea523030e1ffd/examples/eyeballs/imgs/Lizard_Sclera.bmp -------------------------------------------------------------------------------- /examples/eyeballs/imgs/eye0_ball2.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todbot/CircuitPython_GC9A01_demos/877c351620a2d21fcbd5fdb2ae3ea523030e1ffd/examples/eyeballs/imgs/eye0_ball2.bmp -------------------------------------------------------------------------------- /examples/eyeballs/imgs/eye0_iris0.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todbot/CircuitPython_GC9A01_demos/877c351620a2d21fcbd5fdb2ae3ea523030e1ffd/examples/eyeballs/imgs/eye0_iris0.bmp -------------------------------------------------------------------------------- /examples/eyeballs/qteye.py: -------------------------------------------------------------------------------- 1 | # qteye.py - a stand-alone GC9A01 round LCD "eye" on a QTPy 2 | # 23 Oct 2022 - @todbot / Tod Kurt 3 | # Part of https://github.com/todbot/CircuitPython_GC9A01_demos 4 | # also see: https://twitter.com/todbot/status/1584309133263532033 5 | # and: https://twitter.com/todbot/status/1584309133263532033 6 | # Copy this file as "code.py" and the two eyeball BMP files to CIRCUITPY drive 7 | # To install needed libraries: "circup install adafruit_imageload gc9a01" 8 | # 9 | import time, math, random 10 | import board, busio 11 | import displayio 12 | import adafruit_imageload 13 | import gc9a01 14 | 15 | displayio.release_displays() 16 | 17 | dw, dh = 240,240 # display dimensions 18 | 19 | # load our eye and iris bitmaps 20 | eyeball_bitmap, eyeball_pal = adafruit_imageload.load("imgs/eye0_ball2.bmp") 21 | iris_bitmap, iris_pal = adafruit_imageload.load("imgs/eye0_iris0.bmp") 22 | iris_pal.make_transparent(0) # palette color #0 is our transparent background 23 | 24 | # compute or declare some useful info about the eyes 25 | iris_w, iris_h = iris_bitmap.width, iris_bitmap.height # iris is normally 110x110 26 | iris_cx, iris_cy = dw//2 - iris_w//2, dh//2 - iris_h//2 27 | r = 20 # allowable deviation from center for iris 28 | 29 | # wiring for QT Py, should work on any QT Py or XIAO board 30 | tft0_clk = board.SCK 31 | tft0_mosi = board.MOSI 32 | 33 | tft_L0_rst = board.MISO 34 | tft_L0_dc = board.RX 35 | tft_L0_cs = board.TX 36 | 37 | spi0 = busio.SPI(clock=tft0_clk, MOSI=tft0_mosi) 38 | 39 | # class to help us track eye info (not needed for this use exactly, but I find it interesting) 40 | class Eye: 41 | def __init__(self, spi, dc, cs, rst, rot=0, eye_speed=0.25, twitch=2): 42 | display_bus = displayio.FourWire(spi, command=dc, chip_select=cs, reset=rst) 43 | display = gc9a01.GC9A01(display_bus, width=dw, height=dh, rotation=rot) 44 | main = displayio.Group() 45 | display.show(main) 46 | self.display = display 47 | self.eyeball = displayio.TileGrid(eyeball_bitmap, pixel_shader=eyeball_pal) 48 | self.iris = displayio.TileGrid(iris_bitmap, pixel_shader=iris_pal, x=iris_cx,y=iris_cy) 49 | main.append(self.eyeball) 50 | main.append(self.iris) 51 | self.x, self.y = iris_cx, iris_cy 52 | self.tx, self.ty = self.x, self.y 53 | self.next_time = time.monotonic() 54 | self.eye_speed = eye_speed 55 | self.twitch = twitch 56 | 57 | def update(self): 58 | self.x = self.x * (1-self.eye_speed) + self.tx * self.eye_speed # "easing" 59 | self.y = self.y * (1-self.eye_speed) + self.ty * self.eye_speed 60 | self.iris.x = int( self.x ) 61 | self.iris.y = int( self.y ) 62 | if time.monotonic() > self.next_time: 63 | t = random.uniform(0.25,self.twitch) 64 | self.next_time = time.monotonic() + t 65 | self.tx = iris_cx + random.uniform(-r,r) 66 | self.ty = iris_cy + random.uniform(-r,r) 67 | self.display.refresh() 68 | 69 | # a list of all the eyes, in this case, only one 70 | the_eyes = [ 71 | Eye( spi0, tft_L0_dc, tft_L0_cs, tft_L0_rst, rot=0), 72 | ] 73 | 74 | while True: 75 | for eye in the_eyes: 76 | eye.update() 77 | -------------------------------------------------------------------------------- /examples/eyeballs/qteye_person_sensor.py: -------------------------------------------------------------------------------- 1 | # qteye_person_sensor.py -- quick hacking of Person Sensor by Useful Sensors onto qteye 2 | # 23 Oct 2022 - @todbot / Tod Kurt 3 | # Part of https://github.com/todbot/CircuitPython_GC9A01_demos 4 | # Also see: https://twitter.com/todbot/status/1584662808691896320 5 | # 6 | # For more information on Person Sensor: 7 | # https://github.com/usefulsensors/person_sensor_docs/blob/main/README.md 8 | # https://github.com/usefulsensors/person_sensor_screen_lock 9 | # https://www.hackster.io/petewarden/auto-lock-your-laptop-screen-with-a-person-sensor-7e0a35 10 | # 11 | # Copy this file as "code.py" and the two eyeball BMP files to CIRCUITPY drive 12 | # To install needed libraries: "circup install adafruit_imageload gc9a01" 13 | # 14 | 15 | import time, math, random, struct 16 | import board, busio 17 | import displayio 18 | import adafruit_imageload 19 | import gc9a01 20 | 21 | displayio.release_displays() 22 | 23 | debug_face = True 24 | 25 | dw, dh = 240,240 # display dimensions 26 | 27 | eyeball_bitmap, eyeball_pal = adafruit_imageload.load("imgs/eye0_ball2.bmp") 28 | iris_bitmap, iris_pal = adafruit_imageload.load("imgs/eye0_iris0.bmp") 29 | iris_pal.make_transparent(0) # palette color #0 is our transparent background 30 | 31 | iris_w, iris_h = iris_bitmap.width, iris_bitmap.height # iris is normally 110x110 32 | iris_cx, iris_cy = dw//2 - iris_w//2, dh//2 - iris_h//2 33 | r = 20 # allowable deviation from center for iris 34 | 35 | tft0_clk = board.SCK 36 | tft0_mosi = board.MOSI 37 | 38 | tft_L0_rst = board.MISO 39 | tft_L0_dc = board.RX 40 | tft_L0_cs = board.TX 41 | 42 | spi0 = busio.SPI(clock=tft0_clk, MOSI=tft0_mosi) 43 | 44 | class Eye: 45 | def __init__(self, spi, dc, cs, rst, rot=0, eye_speed=0.25, twitch=2): 46 | display_bus = displayio.FourWire(spi, command=dc, chip_select=cs, reset=rst) 47 | display = gc9a01.GC9A01(display_bus, width=dw, height=dh, rotation=rot) 48 | main = displayio.Group() 49 | display.show(main) 50 | self.display = display 51 | self.eyeball = displayio.TileGrid(eyeball_bitmap, pixel_shader=eyeball_pal) 52 | self.iris = displayio.TileGrid(iris_bitmap, pixel_shader=iris_pal, x = iris_cx, y = iris_cy ) 53 | main.append(self.eyeball) 54 | main.append(self.iris) 55 | self.x, self.y = iris_cx, iris_cy 56 | self.tx, self.ty = self.x, self.y 57 | self.next_time = time.monotonic() 58 | self.eye_speed = eye_speed 59 | self.twitch = twitch 60 | 61 | def update(self, do_random=False, ntx=None, nty=None): 62 | self.x = self.x * (1-self.eye_speed) + self.tx * self.eye_speed # "easing" 63 | self.y = self.y * (1-self.eye_speed) + self.ty * self.eye_speed 64 | self.iris.x = int( self.x ) 65 | self.iris.y = int( self.y ) 66 | 67 | if ntx is not None and nty is not None: 68 | self.tx = iris_cx + ntx 69 | self.ty = iris_cx + nty 70 | else: 71 | if do_random and time.monotonic() > self.next_time: 72 | t = random.uniform(0.25,self.twitch) 73 | self.next_time = time.monotonic() + t 74 | self.tx = iris_cx + random.uniform(-r,r) 75 | self.ty = iris_cy + random.uniform(-r,r) 76 | print("change!",t ) 77 | self.display.refresh() 78 | 79 | the_eye = Eye( spi0, tft_L0_dc, tft_L0_cs, tft_L0_rst, rot=0) 80 | 81 | 82 | i2c = board.STEMMA_I2C() 83 | 84 | # Wait until we can access the bus. 85 | while not i2c.try_lock(): 86 | pass 87 | 88 | last_person_sensor_time = 0 89 | # Keep looping and reading the person sensor results. 90 | def get_faces(): 91 | global last_person_sensor_time 92 | 93 | # The person sensor has the I2C ID of hex 62, or decimal 98. 94 | PERSON_SENSOR_I2C_ADDRESS = 0x62 95 | 96 | # We will be reading raw bytes over I2C, and we'll need to decode them into 97 | # data structures. These strings define the format used for the decoding, and 98 | # are derived from the layouts defined in the developer guide. 99 | PERSON_SENSOR_I2C_HEADER_FORMAT = "BBH" 100 | PERSON_SENSOR_I2C_HEADER_BYTE_COUNT = struct.calcsize( 101 | PERSON_SENSOR_I2C_HEADER_FORMAT) 102 | 103 | PERSON_SENSOR_FACE_FORMAT = "BBBBBBbB" 104 | PERSON_SENSOR_FACE_BYTE_COUNT = struct.calcsize(PERSON_SENSOR_FACE_FORMAT) 105 | 106 | PERSON_SENSOR_FACE_MAX = 4 107 | PERSON_SENSOR_RESULT_FORMAT = PERSON_SENSOR_I2C_HEADER_FORMAT + \ 108 | "B" + PERSON_SENSOR_FACE_FORMAT * PERSON_SENSOR_FACE_MAX + "H" 109 | PERSON_SENSOR_RESULT_BYTE_COUNT = struct.calcsize(PERSON_SENSOR_RESULT_FORMAT) 110 | 111 | # How long to pause between sensor polls. 112 | PERSON_SENSOR_DELAY = 0.3 113 | 114 | # How large a face needs to be to count. 115 | MAIN_FACE_MIN_WIDTH = 16 # was 32 116 | MAIN_FACE_MIN_HEIGHT = 16 117 | 118 | if time.monotonic() - last_person_sensor_time < PERSON_SENSOR_DELAY: 119 | return [] 120 | last_person_sensor_time = time.monotonic() 121 | 122 | read_data = bytearray(PERSON_SENSOR_RESULT_BYTE_COUNT) 123 | i2c.readfrom_into(PERSON_SENSOR_I2C_ADDRESS, read_data) 124 | 125 | offset = 0 126 | (pad1, pad2, payload_bytes) = struct.unpack_from( 127 | PERSON_SENSOR_I2C_HEADER_FORMAT, read_data, offset) 128 | offset = offset + PERSON_SENSOR_I2C_HEADER_BYTE_COUNT 129 | 130 | (num_faces) = struct.unpack_from("B", read_data, offset) 131 | num_faces = int(num_faces[0]) 132 | offset = offset + 1 133 | 134 | faces = [] 135 | for i in range(num_faces): 136 | (box_confidence, box_left, box_top, box_right, box_bottom, id_confidence, id, 137 | is_facing) = struct.unpack_from(PERSON_SENSOR_FACE_FORMAT, read_data, offset) 138 | offset = offset + PERSON_SENSOR_FACE_BYTE_COUNT 139 | face = { 140 | "box_confidence": box_confidence, 141 | "box_left": box_left, 142 | "box_top": box_top, 143 | "box_right": box_right, 144 | "box_bottom": box_bottom, 145 | "id_confidence": id_confidence, 146 | "id": id, 147 | "is_facing": is_facing, 148 | } 149 | faces.append(face) 150 | checksum = struct.unpack_from("H", read_data, offset) 151 | 152 | has_main_face = False 153 | has_lookie_loo = False 154 | for face in faces: 155 | width = face["box_right"] - face["box_left"] 156 | height = face["box_bottom"] - face["box_top"] 157 | big_enough_face = ( 158 | width > MAIN_FACE_MIN_WIDTH and height > MAIN_FACE_MIN_HEIGHT) 159 | if big_enough_face: 160 | if not has_main_face: 161 | has_main_face = True 162 | else: 163 | if face["is_facing"] and face["box_confidence"] > 90: 164 | has_lookie_loo = True 165 | 166 | if debug_face: print("faces:",faces) 167 | return faces 168 | 169 | def map_range(s, a1, a2, b1, b2): 170 | return b1 + ((s - a1) * (b2 - b1) / (a2 - a1)) 171 | 172 | while True: 173 | faces = [] 174 | faces = get_faces() 175 | 176 | facex, facey = None,None 177 | if len(faces) > 0: 178 | facex0 = (faces[0]['box_right'] - faces[0]['box_left']) // 2 + faces[0]['box_left'] 179 | facex = map_range(facex0, 0,255, 30,-30) 180 | facey = 0 181 | print("facex: ",facex0,facex) 182 | 183 | the_eye.update( ntx=facex, nty=facey ) 184 | -------------------------------------------------------------------------------- /examples/gc9a01_gauge_knob.py: -------------------------------------------------------------------------------- 1 | # gc9a01_dial_knob.py -- Demonstrate round LCD as a gauge, controlled by a pot knob 2 | # 3 | # 2021 - Tod Kurt - todbot.com 4 | # 5 | # Tested on QTPy RP2040, ItsyBitsy M4, 6 | # Raspberry Pi Pico (RP2040) running CircuitPython 7 7 | # 8 | # You'll need to install 'adafruit_display_text', 'adafruit_imageload' 9 | # and 'gc9a01' library. 10 | # Easiest way to do this is from Terminal: 11 | # circup install adafruit_display_text adafruit_imageload gc9a01 12 | # 13 | 14 | import time 15 | import math 16 | import board 17 | import busio 18 | import displayio 19 | import bitmaptools 20 | import terminalio 21 | from analogio import AnalogIn 22 | import adafruit_imageload 23 | from adafruit_display_text import label 24 | import gc9a01 25 | 26 | # change these as you like, keep the pointer center at 15,105 27 | dial_background_filename = '/imgs/dial-background.bmp' 28 | pointer_filename = '/imgs/pointer-red-basic-30x140-c15x105.bmp' 29 | legend_text = "PERCENT\nAWESOME" 30 | 31 | displayio.release_displays() 32 | 33 | import os 34 | board_type = os.uname().machine 35 | 36 | if 'QT Py M0' in board_type or 'QT Py RP2040' in board_type: 37 | # QT Py pinout 38 | tft_clk = board.SCK 39 | tft_mosi = board.MOSI 40 | tft_rst = board.TX 41 | tft_dc = board.RX 42 | tft_cs = board.A3 43 | tft_bl = board.A2 # optional 44 | spi = busio.SPI(clock=tft_clk, MOSI=tft_mosi) 45 | elif 'ItsyBitsay M4' in board_type: 46 | tft_clk = board.SCK 47 | tft_mosi = board.MOSI 48 | tft_rst = board.MISO 49 | tft_dc = board.D2 50 | tft_cs = board.A5 51 | tft_bl = board.A3 # optional 52 | spi = busio.SPI(clock=tft_clk, MOSI=tft_mosi) 53 | elif 'Pico' in board_type: 54 | # # one pinout, on "southeast" side of Pico board 55 | # tft_clk = board.GP18 56 | # tft_mosi= board.GP19 57 | # tft_rst = board.GP20 58 | # tft_dc = board.GP16 59 | # tft_cs = board.GP17 60 | # tft_bl = board.GP21 61 | # spi = busio.SPI(clock=tft_clk, MOSI=tft_mosi) 62 | 63 | # another pinout, on "southwest" of Pico board 64 | tft_clk = board.GP10 65 | tft_mosi= board.GP11 66 | tft_rst = board.GP12 67 | tft_dc = board.GP13 68 | tft_cs = board.GP14 69 | tft_bl = board.GP15 70 | spi = busio.SPI(clock=tft_clk, MOSI=tft_mosi) 71 | elif 'Waveshare RP2040-LCD-1.28 with rp2040' in board_type: 72 | tft_clk = board.LCD_CLK 73 | tft_mosi = board.LCD_DIN 74 | tft_rst = board.LCD_RST 75 | tft_dc = board.LCD_DC 76 | tft_cs = board.LCD_CS 77 | tft_bl = board.LCD_BL 78 | spi = busio.SPI(clock=tft_clk, MOSI=tft_mosi) 79 | 80 | # Analog knob to control dial 81 | analog_in = AnalogIn(board.A1) 82 | 83 | # Create displayio bus and display 84 | display_bus = displayio.FourWire(spi, command=tft_dc, chip_select=tft_cs, reset=tft_rst) 85 | display = gc9a01.GC9A01(display_bus, width=240, height=240, 86 | backlight_pin=tft_bl, auto_refresh=False) 87 | 88 | # Create main display group and add it to the display 89 | main = displayio.Group() 90 | display.root_group = main 91 | 92 | # 240x240 dial background 93 | bg_bitmap,bg_pal = adafruit_imageload.load(dial_background_filename) 94 | bg_tile_grid = displayio.TileGrid(bg_bitmap, pixel_shader=bg_pal) 95 | main.append(bg_tile_grid) 96 | 97 | # Text legend 98 | text_area = label.Label(terminalio.FONT, text=legend_text, line_spacing=0.9, color=0x000000, anchor_point=(0.5,0.5), anchored_position=(0,0)) 99 | text_group = displayio.Group(scale=1, x=120, y=155) 100 | text_group.append(text_area) 101 | main.append(text_group) # Subgroup for text scaling 102 | 103 | # 30x140 pointer 104 | bitmap_pointer, palette_pointer = adafruit_imageload.load(pointer_filename, bitmap=displayio.Bitmap,palette=displayio.Palette) 105 | palette_pointer.make_transparent(0) 106 | 107 | # Blank bitmap the same size as the pointer bitmap 108 | bitmap_pointer_blank = displayio.Bitmap(bitmap_pointer.width, bitmap_pointer.height, 1)# len(palette_pointer)) 109 | #bitmap_pointer_blank.fill(0) 110 | 111 | # Transparent overlay that is "scribbled" into by rotozoom 112 | # to create rotated version of pointer 113 | bitmap_scribble = displayio.Bitmap(display.width, display.height, len(palette_pointer)) 114 | tile_grid = displayio.TileGrid(bitmap_scribble, pixel_shader=palette_pointer) 115 | main.append(tile_grid) 116 | 117 | # Do initial draw 118 | display.refresh() 119 | 120 | print("Hello World!") 121 | 122 | # simple range mapper, like Arduino map() 123 | def map_range(s, a, b): 124 | (a1, a2), (b1, b2) = a, b 125 | return b1 + ((s - a1) * (b2 - b1) / (a2 - a1)) 126 | 127 | # for dial 'dial-percenti.bmp', range is kinda: 128 | # 0% - 100% => + 2.6 - -2.6 129 | def percent_to_theta(p): 130 | return map_range(p, (0.0,1.0), (-2.6, 2.6) ) 131 | 132 | percent = 0.0 133 | last_time = time.monotonic() 134 | while True: 135 | percent = map_range( analog_in.value, (200,65400), (1,0)) 136 | theta = percent_to_theta(percent) 137 | 138 | print("dt:",time.monotonic()-last_time,"theta:", theta, int(percent*100)) 139 | last_time = time.monotonic() 140 | 141 | # erasing the entire bitmap is slow (~1fps, because of transparency I think) 142 | # instead we erase just the region we modified, after refresh below 143 | # bitmap_scribble.fill(0) 144 | 145 | # offset rotation point (15,105) for bitmap_pointer's axis of rotation 146 | bitmaptools.rotozoom( bitmap_scribble, bitmap_pointer, angle = theta, px=15,py=105) 147 | 148 | display.refresh() 149 | 150 | # after refresh, now "erase" the rotated pointer by doing a 151 | # rotozom of a "blank" bitmap with only transparency 152 | bitmaptools.rotozoom( bitmap_scribble, bitmap_pointer_blank, angle = theta, px=15,py=105) 153 | -------------------------------------------------------------------------------- /examples/gc9a01_hellocircles.py: -------------------------------------------------------------------------------- 1 | # 2 | # gc9a01_hellocircles.py -- Simple Demo of GC9A01 Round LCD 3 | # 4 | # 2021 - Tod Kurt - todbot.com 5 | # 6 | # Tested on QTPy M0 (SAMD21), QTPy RP2040, ItsyBitsy M4, 7 | # Raspberry Pi Pico (RP2040) running CircuitPython 7. 8 | # 9 | # You'll need to install 'adafruit_display_text' and 'gc9a01' library. 10 | # Easiest way to do this is from Terminal: 11 | # circup install adafruit_display_text gc9a01 12 | # 13 | 14 | import time 15 | import board 16 | import math 17 | import random 18 | import busio 19 | import terminalio 20 | import displayio 21 | from vectorio import Rectangle, Circle, Polygon 22 | from adafruit_display_text import label 23 | import gc9a01 24 | 25 | # Release any resources currently in use for the displays 26 | displayio.release_displays() 27 | 28 | # attempt to auto-detect board type 29 | import os 30 | board_type = os.uname().machine 31 | 32 | if 'QT Py M0 Haxpress' in board_type or 'QT Py RP2040' in board_type: 33 | tft_clk = board.SCK 34 | tft_mosi = board.MOSI 35 | tft_rst = board.TX 36 | tft_dc = board.RX 37 | tft_cs = board.A3 38 | tft_bl = board.A2 39 | spi = busio.SPI(clock=tft_clk, MOSI=tft_mosi) 40 | elif 'Adafruit Feather M4 Express with samd51j19' in board_type: 41 | tft_clk = board.SCL 42 | tft_mosi = board.SDA 43 | tft_rst = board.D9 44 | tft_dc = board.D6 45 | tft_cs = board.D5 46 | tft_bl = board.A3 # optional 47 | spi = busio.SPI(clock=tft_clk, MOSI=tft_mosi) 48 | elif 'ItsyBitsy M4' in board_type: 49 | tft_clk = board.SCK 50 | tft_mosi = board.MOSI 51 | tft_rst = board.MISO 52 | tft_dc = board.D2 53 | tft_cs = board.A5 54 | tft_bl = board.A3 # optional 55 | spi = busio.SPI(clock=tft_clk, MOSI=tft_mosi) 56 | elif 'Pico' in board_type: 57 | # Raspberry Pi Pico pinout, one possibility, at "southwest" of board 58 | tft_clk = board.GP10 # must be a SPI CLK 59 | tft_mosi= board.GP11 # must be a SPI TX 60 | tft_rst = board.GP12 61 | tft_dc = board.GP13 62 | tft_cs = board.GP14 63 | tft_bl = board.GP15 64 | spi = busio.SPI(clock=tft_clk, MOSI=tft_mosi) 65 | elif 'Waveshare RP2040-LCD-1.28 with rp2040' in board_type: 66 | tft_clk = board.LCD_CLK 67 | tft_mosi = board.LCD_DIN 68 | tft_rst = board.LCD_RST 69 | tft_dc = board.LCD_DC 70 | tft_cs = board.LCD_CS 71 | tft_bl = board.LCD_BL 72 | spi = busio.SPI(clock=tft_clk, MOSI=tft_mosi) 73 | else: 74 | print("ERROR: Unknown board!") 75 | 76 | # Make the displayio SPI bus and the GC9A01 display 77 | display_bus = displayio.FourWire(spi, command=tft_dc, chip_select=tft_cs, reset=tft_rst) 78 | display = gc9a01.GC9A01(display_bus, width=240, height=240, backlight_pin=tft_bl) 79 | 80 | # Make the main display context 81 | main = displayio.Group() 82 | display.root_group = main 83 | 84 | for i in range(15): 85 | sx = random.randint(0, 240) 86 | sy = random.randint(0, 240) 87 | circ_palette = displayio.Palette(1) 88 | circ_palette[0] = (random.randint(0,0xFF), 0, random.randint(0,0xFF)) 89 | main.append(Circle(pixel_shader=circ_palette, radius=30, x=sx, y=sy)) 90 | 91 | # Draw a text label 92 | text = "Hello\nWorld!" 93 | text_area = label.Label(terminalio.FONT, text=text, color=0xFFFF00, 94 | anchor_point=(0.5,0.5), anchored_position=(0,0)) 95 | text_group = displayio.Group(scale=2) 96 | text_group.append(text_area) 97 | main.append(text_group) 98 | 99 | display.auto_refresh = False 100 | # Animate the text 101 | theta = math.pi 102 | r = 75 103 | while True: 104 | print(time.monotonic(),"hello") 105 | i = random.randint(0,15) 106 | main[i].x = (main[i].x + 5) % 240 107 | text_group.x = 120 + int(r * math.sin(theta)) 108 | text_group.y = 120 + int(r * math.cos(theta)) 109 | display.refresh() 110 | theta -= 0.05 111 | time.sleep(0.01) 112 | -------------------------------------------------------------------------------- /examples/gc9a01_hellocircles_pico_compact.py: -------------------------------------------------------------------------------- 1 | import time, math, random 2 | import board, busio 3 | import displayio, terminalio 4 | import rainbowio, vectorio 5 | import gc9a01 6 | from adafruit_display_text import bitmap_label as label 7 | 8 | dw,dh = 240,240 # display width,height 9 | 10 | displayio.release_displays() 11 | # Raspberry Pi Pico pinout, one possibility, at "southeast" of board 12 | tft_clk = board.GP18 # must be a SPI CLK 13 | tft_mosi= board.GP19 # must be a SPI TX 14 | tft_rst = board.GP21 15 | tft_dc = board.GP16 16 | tft_cs = board.GP17 17 | 18 | spi = busio.SPI(clock=tft_clk, MOSI=tft_mosi) 19 | display_bus = displayio.FourWire(spi, command=tft_dc, chip_select=tft_cs, reset=tft_rst) 20 | display = gc9a01.GC9A01(display_bus, width=dw, height=dh, rotation=270) 21 | maingroup = displayio.Group() # a main group that holds everything 22 | display.root_group = maingroup # put it on the display 23 | 24 | # draw cirlces 25 | for i in range(15): 26 | sx = random.randint(0, dw) 27 | sy = random.randint(0, dh) 28 | pal = displayio.Palette(1) 29 | pal[0] = rainbowio.colorwheel( random.randint(180,240) ) # 240 is a coincidence here 30 | #s0 = vectorio.Circle(pixel_shader=pal0, radius=30, x=sx, y=sy) 31 | s0 = vectorio.Circle(pixel_shader=pal, radius=30, x=sx, y=sy) 32 | maingroup.append(s0) 33 | 34 | # Draw a text label 35 | text_area = label.Label(terminalio.FONT, text="Hellow\nWorld!", color=0xFFFF00, scale=2, 36 | anchor_point=(0.5,0.5), anchored_position=(0,0)) 37 | text_group = displayio.Group() # put in a group so anchor_position works correctly 38 | text_group.append(text_area) 39 | maingroup.append(text_group) 40 | 41 | # Animate the text 42 | theta = math.pi 43 | r = 75 44 | while True: 45 | print(time.monotonic(),"hello") 46 | i = random.randint(0,15) 47 | maingroup[i].x = (maingroup[i].x + 5) % 240 48 | text_group.x = 120 + int(r * math.sin(theta)) 49 | text_group.y = 120 + int(r * math.cos(theta)) 50 | display.refresh( target_frames_per_second=20 ) 51 | theta -= 0.05 52 | 53 | -------------------------------------------------------------------------------- /examples/gc9a01_helloworld.py: -------------------------------------------------------------------------------- 1 | # 2 | # gc9a01_helloworld.py -- Simple Demo of GC9A01 Round LCD 3 | # 4 | # 2021 - Tod Kurt - todbot.com 5 | # 6 | # Tested on QTPy M0 (SAMD21), QTPy RP2040, ItsyBitsy M4, 7 | # Raspberry Pi Pico (RP2040) running CircuitPython 7 8 | # 9 | # You'll need to install 'adafruit_display_text' and 'gc9a01' library. 10 | # Easiest way to do this is from Terminal: 11 | # circup install adafruit_display_text gc9a01 12 | # 13 | 14 | import time 15 | import board 16 | import math 17 | import busio 18 | import terminalio 19 | import displayio 20 | from adafruit_display_text import label 21 | import gc9a01 22 | 23 | # Release any resources currently in use for the displays 24 | displayio.release_displays() 25 | 26 | # attempt to auto-detect board type 27 | import os 28 | board_type = os.uname().machine 29 | 30 | if 'QT Py M0' in board_type or 'QT Py RP2040' in board_type: 31 | # QT Py pinout 32 | tft_clk = board.SCK 33 | tft_mosi = board.MOSI 34 | tft_rst = board.TX 35 | tft_dc = board.RX 36 | tft_cs = board.A3 37 | tft_bl = board.A2 38 | spi = busio.SPI(clock=tft_clk, MOSI=tft_mosi) 39 | # spi.try_lock() 40 | # spi.configure(baudrate=12_000_000) # default spi is 0.25MHz on QT Py, try 12MHz 41 | # spi.unlock() 42 | elif 'ItsyBitsy M4' in board_type: 43 | # Itsy M4 pinout 44 | tft_clk = board.SCK 45 | tft_mosi = board.MOSI 46 | tft_rst = board.MISO 47 | tft_dc = board.D2 48 | tft_cs = board.A5 49 | tft_bl = board.A3 # optional 50 | spi = busio.SPI(clock=tft_clk, MOSI=tft_mosi) 51 | elif 'Pico' in board_type: 52 | # Raspberry Pi Pico pinout, one possibility, at "southwest" of board 53 | tft_clk = board.GP10 # must be a SPI CLK 54 | tft_mosi= board.GP11 # must be a SPI TX 55 | tft_rst = board.GP12 56 | tft_dc = board.GP13 57 | tft_cs = board.GP14 58 | tft_bl = board.GP15 59 | spi = busio.SPI(clock=tft_clk, MOSI=tft_mosi) 60 | elif 'Waveshare RP2040-LCD-1.28 with rp2040' in board_type: 61 | tft_clk = board.LCD_CLK 62 | tft_mosi = board.LCD_DIN 63 | tft_rst = board.LCD_RST 64 | tft_dc = board.LCD_DC 65 | tft_cs = board.LCD_CS 66 | tft_bl = board.LCD_BL 67 | spi = busio.SPI(clock=tft_clk, MOSI=tft_mosi) 68 | else: 69 | print("ERROR: Unknown board!") 70 | 71 | # Make the displayio SPI bus and the GC9A01 display 72 | display_bus = displayio.FourWire(spi, command=tft_dc, chip_select=tft_cs, reset=tft_rst) 73 | display = gc9a01.GC9A01(display_bus, width=240, height=240, backlight_pin=tft_bl) 74 | 75 | # Make the main display context 76 | main = displayio.Group() 77 | display.root_group = main 78 | 79 | # Draw a text label 80 | text = "Hello\nWorld!" 81 | text_area = label.Label(terminalio.FONT, text=text, color=0xFFFF00, 82 | anchor_point=(0.5,0.5), anchored_position=(0,0)) 83 | text_group = displayio.Group(scale=2) 84 | text_group.append(text_area) 85 | main.append(text_group) 86 | 87 | # Animate the text 88 | theta = math.pi 89 | r = 75 90 | while True: 91 | print(time.monotonic(),"hello") 92 | text_group.x = 120 + int(r * math.sin(theta)) 93 | text_group.y = 120 + int(r * math.cos(theta)) 94 | theta -= 0.05 95 | time.sleep(0.01) 96 | -------------------------------------------------------------------------------- /examples/gc9a01_picture_locket.py: -------------------------------------------------------------------------------- 1 | # 2 | # gc9a01_picture_locket.py -- Simple Demo of GC9A01 Round LCD 3 | # 4 | # 2021 - Tod Kurt - todbot.com 5 | # 6 | # Tested on QTPy (SAMD21), QTPy RP2040, and Raspberry Pi Pico (RP2040) 7 | # running CircuitPython 7. 8 | # 9 | # You'll need to install 'gc9a01' package. 10 | # Easiest way to do this is from Terminal: 11 | # circup install gc9a01 12 | # 13 | 14 | import time 15 | import board 16 | import math 17 | import busio 18 | import terminalio 19 | import displayio 20 | import adafruit_imageload 21 | import gc9a01 22 | 23 | # A list of all the BMP images you want displayed, in order 24 | # 25 | # prepare image with ImageMagick like: 26 | # convert input.jpg -resize 240x240 -type palette BMP3:output.bmp 27 | img_filenames = ( "/imgs/max1.bmp", 28 | "/imgs/lars240.bmp" ) 29 | 30 | # time in seconds between images 31 | img_time = 10 32 | 33 | # Release any resources currently in use for the displays 34 | displayio.release_displays() 35 | 36 | # attempt to auto-detect board type 37 | import os 38 | board_type = os.uname().machine 39 | if 'QT Py M0 Haxpress' in board_type or 'QT Py RP2040' in board_type: 40 | tft_clk = board.SCK 41 | tft_mosi = board.MOSI 42 | tft_rst = board.TX 43 | tft_dc = board.RX 44 | tft_cs = board.A3 45 | tft_bl = board.A2 46 | spi = busio.SPI(clock=tft_clk, MOSI=tft_mosi) 47 | elif 'ItsyBitsy M4' in board_type: 48 | tft_clk = board.SCK 49 | tft_mosi = board.MOSI 50 | tft_rst = board.MISO 51 | tft_dc = board.D2 52 | tft_cs = board.A5 53 | tft_bl = board.A3 # optional 54 | spi = busio.SPI(clock=tft_clk, MOSI=tft_mosi) 55 | elif 'Pico' in board_type: 56 | # Raspberry Pi Pico pinout, one possibility, at "southwest" of board 57 | tft_clk = board.GP10 # must be a SPI CLK 58 | tft_mosi= board.GP11 # must be a SPI TX 59 | tft_rst = board.GP12 60 | tft_dc = board.GP13 61 | tft_cs = board.GP14 62 | tft_bl = board.GP15 63 | spi = busio.SPI(clock=tft_clk, MOSI=tft_mosi) 64 | elif 'Waveshare RP2040-LCD-1.28 with rp2040' in board_type: 65 | tft_clk = board.LCD_CLK 66 | tft_mosi = board.LCD_DIN 67 | tft_rst = board.LCD_RST 68 | tft_dc = board.LCD_DC 69 | tft_cs = board.LCD_CS 70 | tft_bl = board.LCD_BL 71 | spi = busio.SPI(clock=tft_clk, MOSI=tft_mosi) 72 | else: 73 | print("ERROR: Unknown board!") 74 | 75 | # Make the displayio SPI bus and the GC9A01 display 76 | display_bus = displayio.FourWire(spi, command=tft_dc, chip_select=tft_cs, reset=tft_rst) 77 | display = gc9a01.GC9A01(display_bus, width=240, height=240, backlight_pin=tft_bl) 78 | 79 | # Make the main display context 80 | main = displayio.Group() 81 | display.root_group = main 82 | 83 | i=0 84 | while True: 85 | print(time.monotonic(),"hello") 86 | img_filename = img_filenames[i] 87 | img_bitmap = displayio.OnDiskBitmap(open(img_filename, "rb")) 88 | img_palette = displayio.ColorConverter() 89 | #img_bitmap, img_palette = adafruit_imageload.load(img_filename) 90 | img_tilegrid = displayio.TileGrid(img_bitmap, pixel_shader=img_palette) 91 | main.append(img_tilegrid) 92 | time.sleep(img_time) 93 | main.pop() # remove image 94 | i = (i+1) % len(img_filenames) # go to next file 95 | 96 | 97 | 98 | -------------------------------------------------------------------------------- /examples/imgs/dial-background.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todbot/CircuitPython_GC9A01_demos/877c351620a2d21fcbd5fdb2ae3ea523030e1ffd/examples/imgs/dial-background.bmp -------------------------------------------------------------------------------- /examples/imgs/dial-percenti.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todbot/CircuitPython_GC9A01_demos/877c351620a2d21fcbd5fdb2ae3ea523030e1ffd/examples/imgs/dial-percenti.bmp -------------------------------------------------------------------------------- /examples/imgs/lars240.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todbot/CircuitPython_GC9A01_demos/877c351620a2d21fcbd5fdb2ae3ea523030e1ffd/examples/imgs/lars240.bmp -------------------------------------------------------------------------------- /examples/imgs/max1.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todbot/CircuitPython_GC9A01_demos/877c351620a2d21fcbd5fdb2ae3ea523030e1ffd/examples/imgs/max1.bmp -------------------------------------------------------------------------------- /examples/imgs/max2.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todbot/CircuitPython_GC9A01_demos/877c351620a2d21fcbd5fdb2ae3ea523030e1ffd/examples/imgs/max2.bmp -------------------------------------------------------------------------------- /examples/imgs/pointer-red-basic-30x140-c15x105.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todbot/CircuitPython_GC9A01_demos/877c351620a2d21fcbd5fdb2ae3ea523030e1ffd/examples/imgs/pointer-red-basic-30x140-c15x105.bmp -------------------------------------------------------------------------------- /examples/imgs/pointer-red-basic-30x140.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todbot/CircuitPython_GC9A01_demos/877c351620a2d21fcbd5fdb2ae3ea523030e1ffd/examples/imgs/pointer-red-basic-30x140.bmp -------------------------------------------------------------------------------- /examples/pico_boot.py: -------------------------------------------------------------------------------- 1 | # Copy this as 'boot.py' in your Pico's CIRCUITPY drive 2 | # from https://gist.github.com/Neradoc/8056725be1c209475fd09ffc37c9fad4 3 | # Useful in case Pico locks up (which it's done a few times on me) 4 | # 5 | import board 6 | import time 7 | from digitalio import DigitalInOut,Pull 8 | 9 | import time 10 | led = DigitalInOut(board.LED) 11 | led.switch_to_output() 12 | 13 | safe = DigitalInOut(board.GP14) 14 | safe.switch_to_input(Pull.UP) 15 | 16 | def reset_on_pin(): 17 | if safe.value is False: 18 | import microcontroller 19 | microcontroller.on_next_reset(microcontroller.RunMode.SAFE_MODE) 20 | microcontroller.reset() 21 | 22 | led.value = False 23 | for x in range(16): 24 | reset_on_pin() 25 | led.value = not led.value 26 | time.sleep(0.1) 27 | -------------------------------------------------------------------------------- /old_driver/todbot_gc9a01.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2021 Tod Kurt 2 | # 3 | # SPDX-License-Identifier: MIT 4 | 5 | """ 6 | `todbot_gc9a01` 7 | ==================================================== 8 | 9 | Display driver for GC9A01 10 | 11 | * Author(s): Tod Kurt 12 | 13 | Implementation Notes 14 | -------------------- 15 | 16 | **Hardware:** 17 | * GC9A01 round display 240x240 18 | 19 | **Software and Dependencies:** 20 | 21 | * Adafruit CircuitPython firmware for the supported boards: 22 | https://github.com/adafruit/circuitpython/releases 23 | 24 | """ 25 | 26 | import displayio 27 | 28 | #__version__ = "1.0.0" 29 | #__repo__ = "https://github.com/todbot/todbot_CircuitPython_GC9A01.git" 30 | 31 | _INIT_SEQUENCE = ( 32 | #xb"\x01\x80\x80" # Software reset then delay x80 (128ms) 33 | b"\xEF\x00" 34 | b"\xEB\x01\x14" 35 | b"\xFE\x00" 36 | b"\xEF\x00" 37 | b"\xEB\x01\x14" 38 | b"\x84\x01\x40" 39 | b"\x85\x01\xFF" 40 | b"\x86\x01\xFF" 41 | b"\x87\x01\xFF" 42 | b"\x88\x01\x0A" 43 | b"\x89\x01\x21" 44 | b"\x8A\x01\x00" 45 | b"\x8B\x01\x80" 46 | b"\x8C\x01\x01" 47 | b"\x8D\x01\x01" 48 | b"\x8E\x01\xFF" 49 | b"\x8F\x01\xFF" 50 | b"\xB6\x02\x00\x00" 51 | b"\x36\x01\x48" # orientation, x18,x28,x48,x88 52 | b"\x3A\x01\x05" # color mode, 18-bit = x06 53 | b"\x90\x04\x08\x08\x08\x08" 54 | b"\xBD\x01\x06" 55 | b"\xBC\x01\x00" 56 | b"\xFF\x03\x60\x01\x04" 57 | b"\xC3\x03\x13\xC4\x13" 58 | b"\xC9\x01\x22" 59 | b"\xBE\x01\x11" 60 | b"\xE1\x02\x10\x0E" 61 | b"\xDF\x03\x21\x0C\x02" 62 | b"\xF0\x06\x45\x09\x08\x08\x26\x2A" 63 | b"\xF1\x06\x43\x70\x72\x36\x37\x6F" 64 | b"\xF2\x06\x45\x09\x08\x08\x26\x2A" 65 | b"\xF3\x06\x43\x70\x72\x36\x37\x6F" 66 | b"\xED\x02\x1B\x0B" 67 | b"\xAE\x01\x77" 68 | b"\xCD\x01\x63" 69 | b"\x70\x09\x07\x07\x04\x0E\x0F\x09\x07\x08\x03" 70 | b"\xE8\x01\x34" 71 | b"\x62\x0C\x18\x0D\x71\xED\x70\x70\x18\x0F\x71\xEF\x70\x70" 72 | b"\x63\x0C\x18\x11\x71\xF1\x70\x70\x18\x13\x71\xF3\x70\x70" 73 | b"\x64\x07\x28\x29\xF1\x01\xF1\x00\x07" 74 | b"\x66\x0A\x3C\x00\xCD\x67\x45\x45\x10\x00\x00\x00" 75 | b"\x67\x0A\x00\x3C\x00\x00\x00\x01\x54\x10\x32\x98" 76 | b"\x74\x07\x10\x85\x80\x00\x00\x4E\x00" 77 | b"\x98\x02\x3E\x07" 78 | b"\x35\x00" 79 | b"\x21\x00" 80 | b"\x11\x80\x78" 81 | b"\x29\x80\x10" 82 | ) 83 | 84 | #define COLOR_MODE 0x3A 85 | #define COLOR_MODE__12_BIT 0x03 86 | #define COLOR_MODE__16_BIT 0x05 87 | #define COLOR_MODE__18_BIT 0x06 88 | 89 | 90 | # pylint: disable=too-few-public-methods 91 | class GC9A01(displayio.Display): 92 | """GC9A01 display driver""" 93 | 94 | def __init__(self, bus, **kwargs): 95 | super().__init__(bus, _INIT_SEQUENCE, **kwargs) 96 | 97 | --------------------------------------------------------------------------------