From 1721f67ec37d106e7edaa3b397ea3280d49c0a37 Mon Sep 17 00:00:00 2001
From: Evg33 <evg33@evg33.ru>
Date: Fri, 6 Dec 2019 03:24:01 +0300
Subject: [PATCH] fr waveshare.com/1.44inch-lcd-hat

---
 pwnagotchi/defaults.yml                       |   2 +-
 pwnagotchi/ui/display.py                      |   3 +
 pwnagotchi/ui/hw/__init__.py                  |   4 +
 .../hw/libs/waveshare/lcdhat144/LCD_1in44.py  | 319 ++++++++++++++++++
 .../hw/libs/waveshare/lcdhat144/__init__.py   |   0
 .../ui/hw/libs/waveshare/lcdhat144/config.py  |  73 ++++
 .../ui/hw/libs/waveshare/lcdhat144/epd.py     |  32 ++
 pwnagotchi/ui/hw/waveshare144lcd.py           |  46 +++
 pwnagotchi/utils.py                           |   3 +
 9 files changed, 481 insertions(+), 1 deletion(-)
 create mode 100644 pwnagotchi/ui/hw/libs/waveshare/lcdhat144/LCD_1in44.py
 create mode 100644 pwnagotchi/ui/hw/libs/waveshare/lcdhat144/__init__.py
 create mode 100644 pwnagotchi/ui/hw/libs/waveshare/lcdhat144/config.py
 create mode 100644 pwnagotchi/ui/hw/libs/waveshare/lcdhat144/epd.py
 create mode 100644 pwnagotchi/ui/hw/waveshare144lcd.py

diff --git a/pwnagotchi/defaults.yml b/pwnagotchi/defaults.yml
index 60be285..a41650d 100644
--- a/pwnagotchi/defaults.yml
+++ b/pwnagotchi/defaults.yml
@@ -260,7 +260,7 @@ ui:
     display:
         enabled: true
         rotation: 180
-        # Possible options inkyphat/inky, papirus/papi, waveshare_1/ws_1 or waveshare_2/ws_2, oledhat, lcdhat, waveshare154inch, waveshare27inch, waveshare29inch, dfrobot/df
+        # Possible options inkyphat/inky, papirus/papi, waveshare_1/ws_1 or waveshare_2/ws_2, oledhat, lcdhat, waveshare154inch, waveshare27inch, waveshare29inch, dfrobot/df, waveshare144lcd/ws144inch
         type: 'waveshare_2'
         # Possible options red/yellow/black (black used for monocromatic displays)
         # Waveshare tri-color 2.13in display can be over-driven with color set as 'fastAndFurious'
diff --git a/pwnagotchi/ui/display.py b/pwnagotchi/ui/display.py
index a6789d5..9ef8774 100644
--- a/pwnagotchi/ui/display.py
+++ b/pwnagotchi/ui/display.py
@@ -52,6 +52,9 @@ class Display(View):
     def is_dfrobot(self):
         return self._implementation.name == 'dfrobot'
 
+    def is_waveshare144lcd(self):
+        return self._implementation.name == 'waveshare144lcd'
+
     def is_waveshare154inch(self):
         return self._implementation.name == 'waveshare154inch'
 
diff --git a/pwnagotchi/ui/hw/__init__.py b/pwnagotchi/ui/hw/__init__.py
index 1bbb602..696a41f 100644
--- a/pwnagotchi/ui/hw/__init__.py
+++ b/pwnagotchi/ui/hw/__init__.py
@@ -7,6 +7,7 @@ from pwnagotchi.ui.hw.waveshare1 import WaveshareV1
 from pwnagotchi.ui.hw.waveshare2 import WaveshareV2
 from pwnagotchi.ui.hw.waveshare27inch import Waveshare27inch
 from pwnagotchi.ui.hw.waveshare29inch import Waveshare29inch
+from pwnagotchi.ui.hw.waveshare144lcd import Waveshare144lcd
 from pwnagotchi.ui.hw.waveshare154inch import Waveshare154inch
 from pwnagotchi.ui.hw.waveshare213d import Waveshare213d
 from pwnagotchi.ui.hw.spotpear24inch import Spotpear24inch
@@ -40,6 +41,9 @@ def display_for(config):
     elif config['ui']['display']['type'] == 'waveshare29inch':
         return Waveshare29inch(config)
     
+    elif config['ui']['display']['type'] == 'waveshare144lcd':
+        return Waveshare144lcd(config)
+
     elif config['ui']['display']['type'] == 'waveshare154inch':
         return Waveshare154inch(config)
 
diff --git a/pwnagotchi/ui/hw/libs/waveshare/lcdhat144/LCD_1in44.py b/pwnagotchi/ui/hw/libs/waveshare/lcdhat144/LCD_1in44.py
new file mode 100644
index 0000000..26f4850
--- /dev/null
+++ b/pwnagotchi/ui/hw/libs/waveshare/lcdhat144/LCD_1in44.py
@@ -0,0 +1,319 @@
+ # -*- coding:UTF-8 -*-
+ ##
+ # | file      	:	LCD_1IN44.py
+ # |	version		:	V2.0
+ # | date		:	2018-07-16
+ # | function	:	On the ST7735S chip driver and clear screen, drawing lines, drawing, writing 
+ #					and other functions to achieve
+ #
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
+ # of this software and associated documnetation files (the "Software"), to deal
+ # in the Software without restriction, including without limitation the rights
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ # copies of the Software, and to permit persons to  whom the Software is
+ # furished to do so, subject to the following conditions:
+ #
+ # The above copyright notice and this permission notice shall be included in
+ # all copies or substantial portions of the Software.
+ #
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ # FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ # LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ # THE SOFTWARE.
+ #
+
+
+import RPi.GPIO as GPIO
+import time
+import numpy as np
+from . import config
+
+LCD_1IN44 = 1
+LCD_1IN8 = 0
+if LCD_1IN44 == 1:
+	LCD_WIDTH  = 128  #LCD width
+	LCD_HEIGHT = 128 #LCD height
+	LCD_X = 2
+	LCD_Y = 1
+if LCD_1IN8 == 1:
+	LCD_WIDTH  = 160
+	LCD_HEIGHT = 128
+	LCD_X = 1
+	LCD_Y = 2
+
+LCD_X_MAXPIXEL = 132  #LCD width maximum memory 
+LCD_Y_MAXPIXEL = 162  #LCD height maximum memory
+
+#scanning method
+L2R_U2D = 1
+L2R_D2U = 2
+R2L_U2D = 3
+R2L_D2U = 4
+U2D_L2R = 5
+U2D_R2L = 6
+D2U_L2R = 7
+D2U_R2L = 8
+SCAN_DIR_DFT = U2D_R2L
+
+
+class LCD:
+	def __init__(self):
+		GPIO.setmode(GPIO.BCM)
+		self.width = LCD_WIDTH
+		self.height = LCD_HEIGHT
+		self.LCD_Scan_Dir = SCAN_DIR_DFT
+		self.LCD_X_Adjust = LCD_X
+		self.LCD_Y_Adjust = LCD_Y
+
+	"""    Hardware reset     """
+	def  LCD_Reset(self):
+		GPIO.output(config.LCD_RST_PIN, GPIO.HIGH)
+		config.Driver_Delay_ms(100)
+		GPIO.output(config.LCD_RST_PIN, GPIO.LOW)
+		config.Driver_Delay_ms(100)
+		GPIO.output(config.LCD_RST_PIN, GPIO.HIGH)
+		config.Driver_Delay_ms(100)
+
+	"""    Write register address and data     """
+	def  LCD_WriteReg(self, Reg):
+		GPIO.setmode(GPIO.BCM)
+		GPIO.setup(config.LCD_DC_PIN, GPIO.OUT)
+		GPIO.output(config.LCD_DC_PIN, GPIO.LOW)
+		config.SPI_Write_Byte([Reg])
+
+	def LCD_WriteData_8bit(self, Data):
+		GPIO.output(config.LCD_DC_PIN, GPIO.HIGH)
+		config.SPI_Write_Byte([Data])
+
+	def LCD_WriteData_NLen16Bit(self, Data, DataLen):
+		GPIO.output(config.LCD_DC_PIN, GPIO.HIGH)
+		for i in range(0, DataLen):
+			config.SPI_Write_Byte([Data >> 8])
+			config.SPI_Write_Byte([Data & 0xff])
+		
+	"""    Common register initialization    """
+	def LCD_InitReg(self):
+		#ST7735R Frame Rate
+		self.LCD_WriteReg(0xB1)
+		self.LCD_WriteData_8bit(0x01)
+		self.LCD_WriteData_8bit(0x2C)
+		self.LCD_WriteData_8bit(0x2D)
+
+		self.LCD_WriteReg(0xB2)
+		self.LCD_WriteData_8bit(0x01)
+		self.LCD_WriteData_8bit(0x2C)
+		self.LCD_WriteData_8bit(0x2D)
+
+		self.LCD_WriteReg(0xB3)
+		self.LCD_WriteData_8bit(0x01)
+		self.LCD_WriteData_8bit(0x2C)
+		self.LCD_WriteData_8bit(0x2D)
+		self.LCD_WriteData_8bit(0x01)
+		self.LCD_WriteData_8bit(0x2C)
+		self.LCD_WriteData_8bit(0x2D)
+		
+		#Column inversion 
+		self.LCD_WriteReg(0xB4)
+		self.LCD_WriteData_8bit(0x07)
+		
+		#ST7735R Power Sequence
+		self.LCD_WriteReg(0xC0)
+		self.LCD_WriteData_8bit(0xA2)
+		self.LCD_WriteData_8bit(0x02)
+		self.LCD_WriteData_8bit(0x84)
+		self.LCD_WriteReg(0xC1)
+		self.LCD_WriteData_8bit(0xC5)
+
+		self.LCD_WriteReg(0xC2)
+		self.LCD_WriteData_8bit(0x0A)
+		self.LCD_WriteData_8bit(0x00)
+
+		self.LCD_WriteReg(0xC3)
+		self.LCD_WriteData_8bit(0x8A)
+		self.LCD_WriteData_8bit(0x2A)
+		self.LCD_WriteReg(0xC4)
+		self.LCD_WriteData_8bit(0x8A)
+		self.LCD_WriteData_8bit(0xEE)
+		
+		self.LCD_WriteReg(0xC5)#VCOM 
+		self.LCD_WriteData_8bit(0x0E)
+		
+		#ST7735R Gamma Sequence
+		self.LCD_WriteReg(0xe0)
+		self.LCD_WriteData_8bit(0x0f)
+		self.LCD_WriteData_8bit(0x1a)
+		self.LCD_WriteData_8bit(0x0f)
+		self.LCD_WriteData_8bit(0x18)
+		self.LCD_WriteData_8bit(0x2f)
+		self.LCD_WriteData_8bit(0x28)
+		self.LCD_WriteData_8bit(0x20)
+		self.LCD_WriteData_8bit(0x22)
+		self.LCD_WriteData_8bit(0x1f)
+		self.LCD_WriteData_8bit(0x1b)
+		self.LCD_WriteData_8bit(0x23)
+		self.LCD_WriteData_8bit(0x37)
+		self.LCD_WriteData_8bit(0x00)
+		self.LCD_WriteData_8bit(0x07)
+		self.LCD_WriteData_8bit(0x02)
+		self.LCD_WriteData_8bit(0x10)
+
+		self.LCD_WriteReg(0xe1)
+		self.LCD_WriteData_8bit(0x0f)
+		self.LCD_WriteData_8bit(0x1b)
+		self.LCD_WriteData_8bit(0x0f)
+		self.LCD_WriteData_8bit(0x17)
+		self.LCD_WriteData_8bit(0x33)
+		self.LCD_WriteData_8bit(0x2c)
+		self.LCD_WriteData_8bit(0x29)
+		self.LCD_WriteData_8bit(0x2e)
+		self.LCD_WriteData_8bit(0x30)
+		self.LCD_WriteData_8bit(0x30)
+		self.LCD_WriteData_8bit(0x39)
+		self.LCD_WriteData_8bit(0x3f)
+		self.LCD_WriteData_8bit(0x00)
+		self.LCD_WriteData_8bit(0x07)
+		self.LCD_WriteData_8bit(0x03)
+		self.LCD_WriteData_8bit(0x10) 
+		
+		#Enable test command
+		self.LCD_WriteReg(0xF0)
+		self.LCD_WriteData_8bit(0x01)
+		
+		#Disable ram power save mode
+		self.LCD_WriteReg(0xF6)
+		self.LCD_WriteData_8bit(0x00)
+		
+		#65k mode
+		self.LCD_WriteReg(0x3A)
+		self.LCD_WriteData_8bit(0x05)
+
+	#********************************************************************************
+	#function:	Set the display scan and color transfer modes
+	#parameter: 
+	#		Scan_dir   :   Scan direction
+	#		Colorchose :   RGB or GBR color format
+	#********************************************************************************
+	def LCD_SetGramScanWay(self, Scan_dir):
+		#Get the screen scan direction
+		self.LCD_Scan_Dir = Scan_dir
+		
+		#Get GRAM and LCD width and height
+		if (Scan_dir == L2R_U2D) or (Scan_dir == L2R_D2U) or (Scan_dir == R2L_U2D) or (Scan_dir == R2L_D2U) :
+			self.width	= LCD_HEIGHT 
+			self.height 	= LCD_WIDTH 
+			if Scan_dir == L2R_U2D:
+				MemoryAccessReg_Data = 0X00 | 0x00
+			elif Scan_dir == L2R_D2U:
+				MemoryAccessReg_Data = 0X00 | 0x80
+			elif Scan_dir == R2L_U2D:
+				MemoryAccessReg_Data = 0x40 | 0x00
+			else:		#R2L_D2U:
+				MemoryAccessReg_Data = 0x40 | 0x80
+		else:
+			self.width	= LCD_WIDTH 
+			self.height 	= LCD_HEIGHT 
+			if Scan_dir == U2D_L2R:
+				MemoryAccessReg_Data = 0X00 | 0x00 | 0x20
+			elif Scan_dir == U2D_R2L:
+				MemoryAccessReg_Data = 0X00 | 0x40 | 0x20
+			elif Scan_dir == D2U_L2R:
+				MemoryAccessReg_Data = 0x80 | 0x00 | 0x20
+			else:		#R2L_D2U
+				MemoryAccessReg_Data = 0x40 | 0x80 | 0x20
+		
+		#please set (MemoryAccessReg_Data & 0x10) != 1
+		if (MemoryAccessReg_Data & 0x10) != 1:
+			self.LCD_X_Adjust = LCD_Y
+			self.LCD_Y_Adjust = LCD_X
+		else:
+			self.LCD_X_Adjust = LCD_X
+			self.LCD_Y_Adjust = LCD_Y
+		
+		# Set the read / write scan direction of the frame memory
+		self.LCD_WriteReg(0x36)		#MX, MY, RGB mode 
+		if LCD_1IN44 == 1:
+			self.LCD_WriteData_8bit( MemoryAccessReg_Data | 0x08)	#0x08 set RGB
+		else:
+			self.LCD_WriteData_8bit( MemoryAccessReg_Data & 0xf7)	#RGB color filter panel
+
+	#/********************************************************************************
+	#function:	
+	#			initialization
+	#********************************************************************************/
+	def LCD_Init(self, Lcd_ScanDir):
+		if (config.GPIO_Init() != 0):
+			return -1
+		
+		#Turn on the backlight
+		#GPIO.setup(config.LCD_BL_PIN, GPIO.OUT)
+		GPIO.output(config.LCD_BL_PIN,GPIO.HIGH)
+		
+		#Hardware reset
+		self.LCD_Reset()
+		
+		#Set the initialization register
+		self.LCD_InitReg()
+		
+		#Set the display scan and color transfer modes	
+		self.LCD_SetGramScanWay(Lcd_ScanDir)
+		config.Driver_Delay_ms(200)
+		
+		#sleep out
+		self.LCD_WriteReg(0x11)
+		config.Driver_Delay_ms(120)
+		
+		#Turn on the LCD display
+		self.LCD_WriteReg(0x29)
+		
+	#/********************************************************************************
+	#function:	Sets the start position and size of the display area
+	#parameter: 
+	#	Xstart 	:   X direction Start coordinates
+	#	Ystart  :   Y direction Start coordinates
+	#	Xend    :   X direction end coordinates
+	#	Yend    :   Y direction end coordinates
+	#********************************************************************************/
+	def LCD_SetWindows(self, Xstart, Ystart, Xend, Yend):
+		#set the X coordinates
+		self.LCD_WriteReg(0x2A)
+		self.LCD_WriteData_8bit(0x00)
+		self.LCD_WriteData_8bit((Xstart & 0xff) + self.LCD_X_Adjust)
+		self.LCD_WriteData_8bit(0x00)
+		self.LCD_WriteData_8bit(((Xend - 1) & 0xff) + self.LCD_X_Adjust)
+
+		#set the Y coordinates
+		self.LCD_WriteReg (0x2B)
+		self.LCD_WriteData_8bit(0x00)
+		self.LCD_WriteData_8bit((Ystart & 0xff) + self.LCD_Y_Adjust)
+		self.LCD_WriteData_8bit(0x00)
+		self.LCD_WriteData_8bit(((Yend - 1) & 0xff )+ self.LCD_Y_Adjust)
+
+		self.LCD_WriteReg(0x2C)
+
+	def LCD_Clear(self):
+		#hello
+		_buffer = [0xff]*(self.width * self.height * 2)
+		self.LCD_SetWindows(0, 0, self.width, self.height)
+		GPIO.output(config.LCD_DC_PIN, GPIO.HIGH)
+		for i in range(0,len(_buffer),4096):
+			config.SPI_Write_Byte(_buffer[i:i+4096])
+
+	def LCD_ShowImage(self,Image,Xstart,Ystart):
+		if (Image == None):
+			return
+		imwidth, imheight = Image.size
+		if imwidth != self.width or imheight != self.height:
+			raise ValueError('Image must be same dimensions as display \
+				({0}x{1}).' .format(self.width, self.height))
+		img = np.asarray(Image)
+		pix = np.zeros((self.width,self.height,2), dtype = np.uint8)
+		pix[...,[0]] = np.add(np.bitwise_and(img[...,[0]],0xF8),np.right_shift(img[...,[1]],5))
+		pix[...,[1]] = np.add(np.bitwise_and(np.left_shift(img[...,[1]],3),0xE0),np.right_shift(img[...,[2]],3))
+		pix = pix.flatten().tolist()
+		self.LCD_SetWindows(0, 0, self.width , self.height)
+		GPIO.output(config.LCD_DC_PIN, GPIO.HIGH)
+		for i in range(0,len(pix),4096):
+			config.SPI_Write_Byte(pix[i:i+4096])
diff --git a/pwnagotchi/ui/hw/libs/waveshare/lcdhat144/__init__.py b/pwnagotchi/ui/hw/libs/waveshare/lcdhat144/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/pwnagotchi/ui/hw/libs/waveshare/lcdhat144/config.py b/pwnagotchi/ui/hw/libs/waveshare/lcdhat144/config.py
new file mode 100644
index 0000000..83a6d9c
--- /dev/null
+++ b/pwnagotchi/ui/hw/libs/waveshare/lcdhat144/config.py
@@ -0,0 +1,73 @@
+# /*****************************************************************************
+# * | File        :   config.py
+# * | Author      :
+# * | Info        :
+# *----------------
+# * | This version:   V1.0
+# * | Date        :   2019-12-06
+# * | Info        :
+# ******************************************************************************/
+Device_SPI = 1
+Device_I2C = 0
+Device = Device_SPI
+#spi = spidev.SpiDev(0, 0)
+
+##
+ #  @filename   :   DEV_Config.py
+ #  @brief      :   LCD hardware interface implements (GPIO, SPI)
+ #  @author     :   Yehui from Waveshare
+ #
+ #  Copyright (C) Waveshare     July 10 2017
+ #
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
+ # of this software and associated documnetation files (the "Software"), to deal
+ # in the Software without restriction, including without limitation the rights
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ # copies of the Software, and to permit persons to  whom the Software is
+ # furished to do so, subject to the following conditions:
+ #
+ # The above copyright notice and this permission notice shall be included in
+ # all copies or substantial portions of the Software.
+ #
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ # FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ # LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ # THE SOFTWARE.
+ #
+ 
+import spidev
+import RPi.GPIO as GPIO
+import time
+
+# Pin definition
+LCD_RST_PIN         = 27
+LCD_DC_PIN          = 25
+LCD_CS_PIN          = 8
+LCD_BL_PIN          = 24
+
+# SPI device, bus = 0, device = 0
+SPI = spidev.SpiDev(0, 0)
+
+def epd_digital_write(pin, value):
+    GPIO.output(pin, value)
+
+def Driver_Delay_ms(xms):
+    time.sleep(xms / 1000.0)
+
+def SPI_Write_Byte(data):
+    SPI.writebytes(data)
+
+def GPIO_Init():
+    GPIO.setmode(GPIO.BCM)
+    GPIO.setwarnings(False)
+    GPIO.setup(LCD_RST_PIN, GPIO.OUT)
+    GPIO.setup(LCD_DC_PIN, GPIO.OUT)
+    GPIO.setup(LCD_CS_PIN, GPIO.OUT)
+    GPIO.setup(LCD_BL_PIN, GPIO.OUT)
+    SPI.max_speed_hz = 9000000
+    SPI.mode = 0b00
+    return 0;
+### END OF FILE ###
diff --git a/pwnagotchi/ui/hw/libs/waveshare/lcdhat144/epd.py b/pwnagotchi/ui/hw/libs/waveshare/lcdhat144/epd.py
new file mode 100644
index 0000000..2f6fbfd
--- /dev/null
+++ b/pwnagotchi/ui/hw/libs/waveshare/lcdhat144/epd.py
@@ -0,0 +1,32 @@
+# Waveshare 1.44inch LCD HAT
+# https://www.waveshare.com/1.44inch-lcd-hat.htm
+# https://www.waveshare.com/wiki/1.44inch_LCD_HAT
+# Driver: ST7735S
+# Interface: SPI
+# Display color: RGB, 65K color
+# Resolution: 128x128
+# Backlight: LED
+# Operating voltage: 3.3V
+
+from . import config
+from . import LCD_1in44
+from PIL import ImageOps
+
+class EPD(object):
+    def __init__(self):
+        self.width = 128
+        self.height = 128
+        self.LCD = LCD_1in44.LCD()
+        self.LCD.Lcd_ScanDir = LCD_1in44.SCAN_DIR_DFT  #SCAN_DIR_DFT = D2U_L2R
+        self.LCD.LCD_Init(self.LCD.Lcd_ScanDir)
+
+    def init(self):
+        pass
+
+    def clear(self):
+        #self.LCD.LCD_Clear()
+        pass
+
+    def display(self, image):
+        rgb_im = ImageOps.colorize(image.convert("L"), black ="green", white ="black")
+        self.LCD.LCD_ShowImage(rgb_im, 0, 0)
diff --git a/pwnagotchi/ui/hw/waveshare144lcd.py b/pwnagotchi/ui/hw/waveshare144lcd.py
new file mode 100644
index 0000000..fb58de2
--- /dev/null
+++ b/pwnagotchi/ui/hw/waveshare144lcd.py
@@ -0,0 +1,46 @@
+import logging
+
+import pwnagotchi.ui.fonts as fonts
+from pwnagotchi.ui.hw.base import DisplayImpl
+
+
+class Waveshare144lcd(DisplayImpl):
+    def __init__(self, config):
+        super(Waveshare144lcd, self).__init__(config, 'waveshare144lcd')
+        self._display = None
+
+    def layout(self):
+        fonts.setup(10, 8, 10, 18)
+        self._layout['width'] = 128
+        self._layout['height'] = 128
+        self._layout['face'] = (0, 43)
+        self._layout['name'] = (0, 14)
+        self._layout['channel'] = (0, 0)
+        self._layout['aps'] = (30, 0)
+        self._layout['uptime'] = (0, 25)
+        self._layout['line1'] = [0, 12, 127, 12]
+        self._layout['line2'] = [0, 116, 127, 116]
+        self._layout['friend_face'] = (14, 78)
+        self._layout['friend_name'] = (1, 103)
+        self._layout['shakes'] = (26, 117)
+        self._layout['mode'] = (0, 117)
+        self._layout['status'] = {
+            'pos': (65, 26),
+            'font': fonts.Small,
+            'max': 12
+        }
+        return self._layout
+
+    def initialize(self):
+        logging.info("initializing waveshare 1.44 inch lcd display")
+        from pwnagotchi.ui.hw.libs.waveshare.lcdhat144.epd import EPD
+        self._display = EPD()
+        self._display.init()
+        self._display.clear()
+
+    def render(self, canvas):
+        self._display.display(canvas)
+
+    def clear(self):
+        pass
+        #self._display.clear()
diff --git a/pwnagotchi/utils.py b/pwnagotchi/utils.py
index 4384dc8..4d0ba90 100644
--- a/pwnagotchi/utils.py
+++ b/pwnagotchi/utils.py
@@ -109,6 +109,9 @@ def load_config(args):
     elif config['ui']['display']['type'] in ('ws_154inch', 'ws154inch', 'waveshare_154inch', 'waveshare154inch'):
         config['ui']['display']['type'] = 'waveshare154inch'
 
+    elif config['ui']['display']['type'] in ('waveshare144lcd', 'ws_144inch', 'ws144inch', 'waveshare_144inch', 'waveshare144inch'):
+        config['ui']['display']['type'] = 'waveshare144lcd'
+
     elif config['ui']['display']['type'] in ('ws_213d', 'ws213d', 'waveshare_213d', 'waveshare213d'):
         config['ui']['display']['type'] = 'waveshare213d'