├── .gitignore ├── LICENSE ├── README.md ├── assets └── pico.jpg ├── examples ├── helloworld.py ├── movecursor.py └── rainbow.py └── lib ├── i2c_lcd.py ├── i2c_lcd_backlight.py └── i2c_lcd_screen.py /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Alex Bucknall 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 | # MicroPython 16x2 LCD Screen (I2C) 2 | 3 | This library is designed to support a MicroPython interface for I2C 16x2 LCD character screens. 4 | 5 | ## Tested LCDs 6 | 7 | - [Grove LCD RGB Backlight](https://www.seeedstudio.com/grove-lcd-rgb-backlight-p-1643.html?cPath=34_36) 8 | - [Waveshare 1602 RGB Backlight](https://www.waveshare.com/wiki/LCD1602_RGB_Module) 9 | 10 | ## Tested Boards 11 | 12 | - Raspberry Pi Pico 13 | 14 | ## Wiring 15 | 16 | ![Wiring Diagram](./assets/pico.jpg) 17 | 18 | ## Example Code 19 | 20 | ```python 21 | import i2c_lcd 22 | from machine import I2C 23 | 24 | i2c = I2C(0) 25 | d = i2c_lcd.Display(i2c) 26 | 27 | # Clear the screen 28 | d.clear() 29 | 30 | # Write "Hello World" to the screen 31 | d.write("Hello World") 32 | 33 | # Move the cursor to the top left corner 34 | d.move(0, 0) 35 | 36 | # Set the backlight to red 37 | d.color(255, 0, 0) 38 | ``` 39 | 40 | ## Library API 41 | 42 | This module supports writing to, clearing and refreshing the LCD screen, among other functions. 43 | 44 | ### write(text) 45 | 46 | Prints text to LCD screen at the location of the cursor. 47 | 48 | ### autoscroll(bool) 49 | 50 | Enables lcd to scroll text as typed. 51 | 52 | ### cursor(bool) 53 | 54 | Sets cursor visibility. 55 | 56 | ### blink(bool) 57 | 58 | Sets blink visibility. 59 | 60 | ### display(bool) 61 | 62 | Sets display state (on/off). 63 | 64 | ### home() 65 | 66 | Returns the cursor to the (0,0) location on screen 67 | 68 | ### move(col, row) 69 | 70 | Moves the cursor to (col,row) 71 | 72 | ### color(r, g, b) 73 | 74 | Changes the color of the LCD Backlight to (r,g,b) 75 | -------------------------------------------------------------------------------- /assets/pico.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bucknalla/micropython-i2c-lcd/17a8b4dde0d021909724ba4508d8af4d185b4e21/assets/pico.jpg -------------------------------------------------------------------------------- /examples/helloworld.py: -------------------------------------------------------------------------------- 1 | import i2c_lcd 2 | from machine import I2C 3 | 4 | i2c = I2C(0) 5 | d = i2c_lcd.Display(i2c) 6 | 7 | d.home() 8 | d.write("Hello World") 9 | -------------------------------------------------------------------------------- /examples/movecursor.py: -------------------------------------------------------------------------------- 1 | import i2c_lcd 2 | from machine import I2C 3 | 4 | i2c = I2C(0) 5 | d = i2c_lcd.Display(i2c) 6 | 7 | d.home() 8 | d.write("Hello World") 9 | 10 | d.move(0, 0) 11 | -------------------------------------------------------------------------------- /examples/rainbow.py: -------------------------------------------------------------------------------- 1 | import i2c_lcd 2 | import time 3 | from machine import I2C 4 | 5 | def rainbow(): 6 | rgbColour = [0,0,0] 7 | 8 | while True: 9 | time.sleep(0.1) 10 | for x in range(0,255,1): 11 | d.color(x,0,0) 12 | time.sleep(0.1) 13 | for x in range(0,255,1): 14 | d.color(0,x,0) 15 | time.sleep(0.1) 16 | for x in range(0,255,1): 17 | d.color(0,0,x) 18 | 19 | i2c = I2C(0) 20 | d = i2c_lcd.Display(i2c) 21 | 22 | d.home() 23 | d.write('Hello World') 24 | 25 | rainbow() 26 | -------------------------------------------------------------------------------- /lib/i2c_lcd.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | # This is a port of https://github.com/Seeed-Studio/Grove_LCD_RGB_Backlight 4 | # (c) 2025 Alex Bucknall 5 | 6 | from machine import I2C 7 | import i2c_lcd_backlight 8 | import i2c_lcd_screen 9 | 10 | 11 | class Display(object): 12 | backlight = None 13 | screen = None 14 | 15 | i2c = I2C(0) 16 | 17 | def __init__(self, i2c, lcd_addr=(0x7c>>1), rgb_addr=(0xc0>>1)): 18 | self.screen = i2c_lcd_screen.Screen(i2c, lcd_addr) 19 | self.backlight = i2c_lcd_backlight.Backlight(i2c, rgb_addr) 20 | 21 | def write(self, text): 22 | self.screen.write(text) 23 | 24 | def cursor(self, state): 25 | self.screen.cursor(state) 26 | 27 | def blink(self, state): 28 | self.screen.blink(state) 29 | 30 | def blinkLed(self): 31 | self.backlight.blinkLed() 32 | 33 | def autoscroll(self, state): 34 | self.screen.autoscroll(state) 35 | 36 | def display(self, state): 37 | self.screen.display(state) 38 | 39 | def clear(self): 40 | self.screen.clear() 41 | 42 | def home(self): 43 | self.screen.home() 44 | 45 | def color(self, r, g, b): 46 | self.backlight.set_color(r, g, b) 47 | 48 | def move(self, col, row): 49 | self.screen.setCursor(col, row) 50 | -------------------------------------------------------------------------------- /lib/i2c_lcd_backlight.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | # This is a port of https://github.com/Seeed-Studio/Grove_LCD_RGB_Backlight 4 | # (c) 2025 Alex Bucknall 5 | 6 | # Backlight i2c Address = 0x62 7 | 8 | from machine import I2C 9 | 10 | 11 | class Backlight(object): 12 | REG_RED = 0x04 # pwm2 13 | REG_GREEN = 0x03 # pwm1 14 | REG_BLUE = 0x02 # pwm0 15 | 16 | REG_MODE1 = 0x00 17 | REG_MODE2 = 0x01 18 | REG_OUTPUT = 0x08 19 | 20 | def __init__(self, i2c, address): 21 | if not isinstance(i2c, I2C): 22 | raise TypeError 23 | 24 | self.i2c = i2c 25 | self.address = int(address) 26 | 27 | # initialize 28 | self.set_register(self.REG_MODE1, 0) 29 | self.set_register(self.REG_MODE2, 0) 30 | 31 | # all LED control by PWM 32 | self.set_register(self.REG_OUTPUT, 0xAA) 33 | 34 | def blinkLed(self): 35 | self.set_register(0x07, 0x17) # blink every seconds 36 | self.set_register(0x06, 0x7F) # 50% duty cycle 37 | 38 | def set_register(self, addr, value): 39 | value = bytearray([value]) 40 | self.i2c.writeto_mem(self.address, addr, value) 41 | 42 | def set_color(self, red, green, blue): 43 | r = int(red) 44 | g = int(green) 45 | b = int(blue) 46 | self.set_register(self.REG_RED, r) 47 | self.set_register(self.REG_GREEN, g) 48 | self.set_register(self.REG_BLUE, b) 49 | -------------------------------------------------------------------------------- /lib/i2c_lcd_screen.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | # This is a port of https://github.com/Seeed-Studio/Grove_LCD_RGB_Backlight 4 | # (c) 2025 Alex Bucknall 5 | 6 | from machine import I2C 7 | import time 8 | 9 | 10 | class Screen(object): 11 | # commands 12 | LCD_CLEARDISPLAY = 0x01 13 | LCD_RETURNHOME = 0x02 14 | LCD_ENTRYMODESET = 0x04 15 | LCD_DISPLAYCONTROL = 0x08 16 | LCD_CURSORSHIFT = 0x10 17 | LCD_FUNCTIONSET = 0x20 18 | LCD_SETCGRAMADDR = 0x40 19 | LCD_SETDDRAMADDR = 0x80 20 | 21 | # flags for display entry mode 22 | LCD_ENTRYRIGHT = 0x00 23 | LCD_ENTRYLEFT = 0x02 24 | LCD_ENTRYSHIFTINCREMENT = 0x01 25 | LCD_ENTRYSHIFTDECREMENT = 0x00 26 | 27 | # flags for display on/off control 28 | LCD_DISPLAYON = 0x04 29 | LCD_DISPLAYOFF = 0x00 30 | LCD_CURSORON = 0x02 31 | LCD_CURSOROFF = 0x00 32 | LCD_BLINKON = 0x01 33 | LCD_BLINKOFF = 0x00 34 | 35 | # flags for display/cursor shift 36 | LCD_DISPLAYMOVE = 0x08 37 | LCD_CURSORMOVE = 0x00 38 | LCD_MOVERIGHT = 0x04 39 | LCD_MOVELEFT = 0x00 40 | 41 | # flags for function set 42 | LCD_8BITMODE = 0x10 43 | LCD_4BITMODE = 0x00 44 | LCD_2LINE = 0x08 45 | LCD_1LINE = 0x00 46 | LCD_5x10DOTS = 0x04 47 | LCD_5x8DOTS = 0x00 48 | 49 | def __init__(self, i2c, address, oneline=False, charsize=LCD_5x8DOTS): 50 | 51 | self.i2c = i2c 52 | self.address = address 53 | 54 | self.disp_func = self.LCD_DISPLAYON # | 0x10 55 | if not oneline: 56 | self.disp_func |= self.LCD_2LINE 57 | elif charsize != 0: 58 | # for 1-line displays you can choose another dotsize 59 | self.disp_func |= self.LCD_5x10DOTS 60 | 61 | # wait for display init after power-on 62 | time.sleep_ms(50) # 50ms 63 | 64 | # send function set 65 | self.cmd(self.LCD_FUNCTIONSET | self.disp_func) 66 | time.sleep_us(4500) ##time.sleep(0.0045) # 4.5ms 67 | self.cmd(self.LCD_FUNCTIONSET | self.disp_func) 68 | time.sleep_us(150) ##time.sleep(0.000150) # 150µs = 0.15ms 69 | self.cmd(self.LCD_FUNCTIONSET | self.disp_func) 70 | self.cmd(self.LCD_FUNCTIONSET | self.disp_func) 71 | 72 | # turn on the display 73 | self.disp_ctrl = self.LCD_DISPLAYON | self.LCD_CURSOROFF | self.LCD_BLINKOFF 74 | self.display(True) 75 | 76 | # clear it 77 | self.clear() 78 | 79 | # set default text direction (left-to-right) 80 | self.disp_mode = self.LCD_ENTRYLEFT | self.LCD_ENTRYSHIFTDECREMENT 81 | self.cmd(self.LCD_ENTRYMODESET | self.disp_mode) 82 | 83 | def cmd(self, command): 84 | assert command >= 0 and command < 256 85 | command = bytearray([command]) 86 | self.i2c.writeto_mem(self.address, 0x80, command) 87 | 88 | def write_char(self, c): 89 | assert c >= 0 and c < 256 90 | c = bytearray([c]) 91 | self.i2c.writeto_mem(self.address, 0x40, c) 92 | 93 | def write(self, text): 94 | for char in text: 95 | self.write_char(ord(char)) 96 | 97 | def cursor(self, state): 98 | if state: 99 | self.disp_ctrl |= self.LCD_CURSORON 100 | self.cmd(self.LCD_DISPLAYCONTROL | self.disp_ctrl) 101 | else: 102 | self.disp_ctrl &= ~self.LCD_CURSORON 103 | self.cmd(self.LCD_DISPLAYCONTROL | self.disp_ctrl) 104 | 105 | def setCursor(self, col, row): 106 | col = (col | 0x80) if row == 0 else (col | 0xC0) 107 | self.cmd(col) 108 | 109 | def autoscroll(self, state): 110 | if state: 111 | self.disp_mode |= self.LCD_ENTRYSHIFTINCREMENT 112 | self.cmd(self.LCD_ENTRYMODESET | self.disp_mode) 113 | else: 114 | self.disp_mode &= ~self.LCD_ENTRYSHIFTINCREMENT 115 | self.cmd(self.LCD_ENTRYMODESET | self.disp_mode) 116 | 117 | def blink(self, state): 118 | if state: 119 | self.disp_ctrl |= self.LCD_BLINKON 120 | self.cmd(self.LCD_DISPLAYCONTROL | self.disp_ctrl) 121 | else: 122 | self.disp_ctrl &= ~self.LCD_BLINKON 123 | self.cmd(self.LCD_DISPLAYCONTROL | self.disp_ctrl) 124 | 125 | def display(self, state): 126 | if state: 127 | self.disp_ctrl |= self.LCD_DISPLAYON 128 | self.cmd(self.LCD_DISPLAYCONTROL | self.disp_ctrl) 129 | else: 130 | self.disp_ctrl &= ~self.LCD_DISPLAYON 131 | self.cmd(self.LCD_DISPLAYCONTROL | self.disp_ctrl) 132 | 133 | def clear(self): 134 | self.cmd(self.LCD_CLEARDISPLAY) 135 | time.sleep_ms(2) # 2ms 136 | 137 | def home(self): 138 | self.cmd(self.LCD_RETURNHOME) 139 | time.sleep_ms(2) # 2m 140 | --------------------------------------------------------------------------------