import asyncio import smbus import time lock = asyncio.Lock() class LCD: def __init__(self, pi_rev = 2, i2c_addr = 0x3F, backlight = True): # device constants self.I2C_ADDR = i2c_addr self.LCD_WIDTH = 20 # Max. characters per line self.LCD_CHR = 1 # Mode - Sending data self.LCD_CMD = 0 # Mode - Sending command self.LCD_LINE_1 = 0x80 # LCD RAM addr for line one self.LCD_LINE_2 = 0xC0 # LCD RAM addr for line two self.LCD_LINE_3 = 0x94 # LCD RAM addr for line three self.LCD_LINE_4 = 0xD4 # LCD RAM addr for line four if backlight: # on self.LCD_BACKLIGHT = 0x08 else: # off self.LCD_BACKLIGHT = 0x00 self.ENABLE = 0b00000100 # Enable bit # Timing constants self.E_PULSE = 0.0005 self.E_DELAY = 0.0005 # Open I2C interface if pi_rev == 2: # Rev 2 Pi uses 1 self.bus = smbus.SMBus(1) elif pi_rev == 1: # Rev 1 Pi uses 0 self.bus = smbus.SMBus(0) else: raise ValueError('pi_rev param must be 1 or 2') # Initialise display self.lcd_byte(0x33, self.LCD_CMD) # 110011 Initialise self.lcd_byte(0x32, self.LCD_CMD) # 110010 Initialise self.lcd_byte(0x06, self.LCD_CMD) # 000110 Cursor move direction self.lcd_byte(0x0C, self.LCD_CMD) # 001100 Display On,Cursor Off, Blink Off self.lcd_byte(0x28, self.LCD_CMD) # 101000 Data length, number of lines, font size self.lcd_byte(0x01, self.LCD_CMD) # 000001 Clear display def set_char(self, address, bitmap): address = address + 0x40 self.lcd_byte(address, 0) for row in bitmap: self.lcd_byte(row, 1) def lcd_byte(self, bits, mode): # Send byte to data pins # bits = data # mode = 1 for data, 0 for command bits_high = mode | (bits & 0xF0) | self.LCD_BACKLIGHT bits_low = mode | ((bits<<4) & 0xF0) | self.LCD_BACKLIGHT # High bits self.bus.write_byte(self.I2C_ADDR, bits_high) self.toggle_enable(bits_high) # Low bits self.bus.write_byte(self.I2C_ADDR, bits_low) self.toggle_enable(bits_low) def toggle_enable(self, bits): time.sleep(self.E_DELAY) self.bus.write_byte(self.I2C_ADDR, (bits | self.ENABLE)) time.sleep(self.E_PULSE) self.bus.write_byte(self.I2C_ADDR,(bits & ~self.ENABLE)) time.sleep(self.E_DELAY) async def async_message(self, string, line = 1): async with lock: result = await asyncio.to_thread(self._message, string = string, line = line) def _message(self, string, line = 1): # display message string on indicated LCD line if line == 1: lcd_line = self.LCD_LINE_1 elif line == 2: lcd_line = self.LCD_LINE_2 elif line == 3: lcd_line = self.LCD_LINE_3 elif line == 4: lcd_line = self.LCD_LINE_4 else: raise ValueError(f'line number {line} out of range') string = string.ljust(self.LCD_WIDTH," ") self.lcd_byte(lcd_line, self.LCD_CMD) for i in range(self.LCD_WIDTH): self.lcd_byte(ord(string[i]), self.LCD_CHR) def clear(self): # clear LCD display self.lcd_byte(0x01, self.LCD_CMD)