Display update for 2.9.2 no AI

display check with 2.9.2
-gfxhat
	sn3218 library missing from the venv, added to /hw/libs/pimoroni/gfxhat/
	cleanup of the code (EPD changed to LCD)
	logging info added for config options (contrast, color)
	touch control work in progress (6 touch buttons with short and long press and LED feedback)
-i2coled
	cleanup of the code (EPD changed to OLED)
	Logging info added for config options (width, height, i2c address)
-oledlcd and oledlcdvert
	working out of the box
	logging info added for available gpio breakouts (buttons)
-displayhatmini
	working out of the box
	logging info added for available gpio breakouts (buttons, rgb LED, i2c bus)
-pirateaudio
	working out of the box
	logging info added for available gpio breakouts (buttons, i2s bus)
-pitft
	working out of the box
	logging info added for available gpio breakouts (buttons)
-tftbonnet
	working out of the box
	logging info added for available gpio breakouts (buttons, i2c bus)
-minipitft
	working out of the box
	logging info added for available gpio breakouts (buttons, i2c bus)

-minipitft2
	cant test on HW but should work!
	logging info added for available gpio breakouts (buttons, i2c bus)
-argonpod
	cant test on HW but should work!
	logging info added for available gpio breakouts (buttons, IR)
This commit is contained in:
RasTacsko
2024-12-01 12:47:34 +01:00
parent a8563d8107
commit f6f30632f8
16 changed files with 316 additions and 131 deletions

View File

@ -1,12 +1,12 @@
# board GPIO:
# Key1:
# Key2:
# Key3:
# Key4:
#
# Key1: GPIO 16
# Key2: GPIO 20
# Key3: GPIO 21
# Key4: GPIO 26
# IR: GPIO 23
# Touch chipset:
# HW info: https://argon40.com/products/pod-display-2-8inch
# HW datasheet:
# HW MANUAL: https://cdn.shopify.com/s/files/1/0556/1660/2177/files/ARGON_POD_MANUAL.pdf?v=1668390711%0A
import logging
@ -44,6 +44,9 @@ class ArgonPod(DisplayImpl):
def initialize(self):
logging.info("Initializing Argon Pod display")
logging.info("Available pins for GPIO Buttons: 16, 20, 21, 26")
logging.info("IR available on GPIO 23")
logging.info("Backlight pin available on GPIO 18")
from pwnagotchi.ui.hw.libs.argon.argonpod.ILI9341 import ILI9341
self._display = ILI9341(0, 0, 22, 18)

View File

@ -1,9 +1,7 @@
import logging
import pwnagotchi.ui.fonts as fonts
from pwnagotchi.ui.hw.base import DisplayImpl
class DisplayHatMini(DisplayImpl):
def __init__(self, config):
super(DisplayHatMini, self).__init__(config, 'displayhatmini')
@ -29,11 +27,14 @@ class DisplayHatMini(DisplayImpl):
'font': fonts.status_font(fonts.Medium),
'max': 20
}
return self._layout
def initialize(self):
logging.info("initializing Display Hat Mini")
logging.info("Initializing Display Hat Mini")
logging.info("Available pins for GPIO Buttons A/B/X/Y: 5, 6, 16, 24")
logging.info("Available pins for RGB Led: 17, 27, 22")
logging.info("Backlight pin available on GPIO 13")
logging.info("I2C bus available on stemma QT and Breakout Garden headers")
from pwnagotchi.ui.hw.libs.pimoroni.displayhatmini.ST7789 import ST7789
self._display = ST7789(0,1,9,13)

View File

@ -8,7 +8,7 @@
# ui.display.blcolor = "olive"
#
# Contrast should be between 30-50, default is 40
# Backlight are predefined in the epd.py
# Backlight are predefined in the lcd.py
# Available backlight colors:
# white, grey, maroon, red, purple, fuchsia, green,
# lime, olive, yellow, navy, blue, teal, aqua
@ -48,10 +48,16 @@ class GfxHat(DisplayImpl):
def initialize(self):
contrast = self._config['contrast'] if 'contrast' in self._config else 40
blcolor = self._config['blcolor'] if 'blcolor' in self._config else 'OLIVE'
logging.info("initializing Pimoroni GfxHat")
logging.info("initializing Pimoroni GfxHat - Contrast: %d Backlight color: %s" % (contrast, blcolor))
from pwnagotchi.ui.hw.libs.pimoroni.gfxhat.epd import EPD
self._display = EPD(contrast=contrast)
logging.info("Initializing Pimoroni GfxHat - Contrast: %d Backlight color: %s" % (contrast, blcolor))
logging.info("Available config options: ui.display.contrast and ui.display.color")
logging.info("Contrast should be between 30-50, default is 40")
logging.info("Backlight are predefined in the lcd.py")
logging.info("Available backlight colors:")
logging.info("white, grey, maroon, red, purple, fuchsia, green,")
logging.info("lime, olive, yellow, navy, blue, teal, aqua")
logging.info("Touch control work in progress (6 touch buttons with short and long press and LED feedback)")
from pwnagotchi.ui.hw.libs.pimoroni.gfxhat.lcd import LCD
self._display = LCD(contrast=contrast)
self._display.Init(color_name=blcolor)
self._display.Clear()

View File

@ -52,11 +52,11 @@ class I2COled(DisplayImpl):
i2caddr = self._config['i2c_addr'] if 'i2c_addr' in self._config else 0x3C
width = self._config['width'] if 'width' in self._config else 128
height = self._config['height'] if 'height' in self._config else 64
logging.info("Initializing SSD1306 based %dx%d I2C Oled Display on address 0x%X" % (width, height, i2caddr))
logging.info("Available config options: ui.display.width, ui.display.height and ui.display.i2caddr")
logging.info("initializing %dx%d I2C Oled Display on address 0x%X" % (width, height, i2caddr))
from pwnagotchi.ui.hw.libs.i2coled.epd import EPD
self._display = EPD(address=i2caddr, width=width, height=height)
from pwnagotchi.ui.hw.libs.i2coled.oled import OLED
self._display = OLED(address=i2caddr, width=width, height=height)
self._display.Init()
self._display.Clear()

View File

@ -9,7 +9,7 @@ EPD_HEIGHT = 64
# disp = SSD1306.SSD1306_96_16(96, 16, address=0x3C)
# If you change for different resolution, you have to modify the layout in pwnagotchi/ui/hw/i2coled.py
class EPD(object):
class OLED(object):
def __init__(self, address=0x3C, width=EPD_WIDTH, height=EPD_HEIGHT):
self.width = width

View File

@ -9,7 +9,7 @@ LED_MAP = [2, 1, 0, 5, 4, 3]
def setup():
"""Set up the backlight on GFX HAT."""
global _sn3218
import sn3218 as _sn3218
from . import sn3218 as _sn3218
_sn3218.enable()
_sn3218.enable_leds(0b111111111111111111)

View File

@ -1,55 +0,0 @@
from . import st7567
from . import backlight
CONTRAST = 40
# Define RGB colors
WHITE = (255, 255, 255)
GREY = (255, 255, 255)
MAROON = (128, 0, 0)
RED = (255, 0, 0)
PURPLE = (128, 0, 128)
FUCHSIA = (255, 0, 255)
GREEN = (0, 128, 0)
LIME = (0, 255, 0)
OLIVE = (128, 128, 0)
YELLOW = (255, 255, 0)
NAVY = (0, 0, 128)
BLUE = (0, 0, 255)
TEAL = (0, 128, 128)
AQUA = (0, 255, 255)
# Map color names to RGB values
color_map = {
'WHITE': WHITE,
'GREY' : GREY,
'MAROON': MAROON,
'RED': RED,
'PURPLE': PURPLE,
'FUCHSIA': FUCHSIA,
'GREEN' : GREEN,
'LIME' : LIME,
'OLIVE' : OLIVE,
'YELLOW' : YELLOW,
'NAVY' : NAVY,
'BLUE' : BLUE,
'TEAL' : TEAL,
'AQUA' : AQUA
}
class EPD(object):
def __init__(self, contrast=CONTRAST, blcolor=('OLIVE')):
self.disp = st7567.ST7567()
self.disp.contrast(contrast)
def Init(self, color_name):
self.disp.setup()
blcolor = color_map.get(color_name.upper(), OLIVE) # Default to olive if color not found
backlight.set_all(*blcolor)
backlight.show()
def Clear(self):
self.disp.clear()
def Display(self, image):
self.disp.show(image)

View File

@ -1,57 +1,55 @@
"""Library for the GFX HAT ST7567 SPI LCD."""
from .st7567 import ST7567
from . import st7567
from . import backlight
CONTRAST = 40
st7567 = ST7567()
# Define RGB colors
WHITE = (255, 255, 255)
GREY = (255, 255, 255)
MAROON = (128, 0, 0)
RED = (255, 0, 0)
PURPLE = (128, 0, 128)
FUCHSIA = (255, 0, 255)
GREEN = (0, 128, 0)
LIME = (0, 255, 0)
OLIVE = (128, 128, 0)
YELLOW = (255, 255, 0)
NAVY = (0, 0, 128)
BLUE = (0, 0, 255)
TEAL = (0, 128, 128)
AQUA = (0, 255, 255)
dimensions = st7567.dimensions
# Map color names to RGB values
color_map = {
'WHITE': WHITE,
'GREY' : GREY,
'MAROON': MAROON,
'RED': RED,
'PURPLE': PURPLE,
'FUCHSIA': FUCHSIA,
'GREEN' : GREEN,
'LIME' : LIME,
'OLIVE' : OLIVE,
'YELLOW' : YELLOW,
'NAVY' : NAVY,
'BLUE' : BLUE,
'TEAL' : TEAL,
'AQUA' : AQUA
}
class LCD(object):
def clear():
"""Clear GFX HAT's display buffer."""
st7567.clear()
def __init__(self, contrast=CONTRAST, blcolor=('OLIVE')):
self.disp = st7567.ST7567()
self.disp.contrast(contrast)
def Init(self, color_name):
self.disp.setup()
blcolor = color_map.get(color_name.upper(), OLIVE) # Default to olive if color not found
backlight.set_all(*blcolor)
backlight.show()
def set_pixel(x, y, value):
"""Set a single pixel in GTX HAT's display buffer.
def Clear(self):
self.disp.clear()
:param x: X position (from 0 to 127)
:param y: Y position (from 0 to 63)
:param value: pixel state 1 = On, 0 = Off
"""
st7567.set_pixel(x, y, value)
def show():
"""Update GFX HAT with the current buffer contents."""
st7567.show()
def contrast(value):
"""Change GFX HAT LCD contrast."""
st7567.contrast(value)
def rotation(r=0):
"""Set the display rotation.
:param r: Specify the rotation in degrees: 0, or 180
"""
if r == 0:
st7567.rotated = False
elif r == 180:
st7567.rotated = True
else:
raise ValueError('Rotation must be 0 or 180 degrees')
def get_rotation():
"""Get the display rotation value.
Returns an integer, either 0, or 180
"""
return 180 if st7567.rotated else 0
def Display(self, image):
self.disp.show(image)

View File

@ -0,0 +1,214 @@
import sys
import warnings
__version__ = '1.2.7'
I2C_ADDRESS = 0x54
CMD_ENABLE_OUTPUT = 0x00
CMD_SET_PWM_VALUES = 0x01
CMD_ENABLE_LEDS = 0x13
CMD_UPDATE = 0x16
CMD_RESET = 0x17
if sys.version_info < (3, ):
SMBUS = "python-smbus"
else:
SMBUS = "python3-smbus"
# Helper function to avoid exception chaining output in python3.
# use exec to shield newer syntax from older python
if sys.version_info < (3, 3):
def _raise_from_none(exc):
raise exc
else:
exec("def _raise_from_none(exc): raise exc from None")
try:
from smbus import SMBus
except ImportError:
err_string = "This library requires {smbus}\nInstall with: sudo apt install {smbus}".format(smbus=SMBUS)
import_error = ImportError(err_string)
_raise_from_none(import_error)
def i2c_bus_id():
"""
Returns the i2c bus ID.
"""
with open('/proc/cpuinfo') as cpuinfo:
revision = [l[12:-1] for l in cpuinfo if l[:8] == "Revision"][0]
# https://www.raspberrypi.org/documentation/hardware/raspberrypi/revision-codes/README.md
return 1 if int(revision, 16) >= 4 else 0
def enable():
"""
Enables output.
"""
i2c.write_i2c_block_data(I2C_ADDRESS, CMD_ENABLE_OUTPUT, [0x01])
def disable():
"""
Disables output.
"""
i2c.write_i2c_block_data(I2C_ADDRESS, CMD_ENABLE_OUTPUT, [0x00])
def reset():
"""
Resets all internal registers.
"""
i2c.write_i2c_block_data(I2C_ADDRESS, CMD_RESET, [0xFF])
def enable_leds(enable_mask):
"""
Enables or disables each LED channel. The first 18 bit values are
used to determine the state of each channel (1=on, 0=off) if fewer
than 18 bits are provided the remaining channels are turned off.
Args:
enable_mask (int): up to 18 bits of data
Raises:
TypeError: if enable_mask is not an integer.
"""
if not isinstance(enable_mask, int):
raise TypeError("enable_mask must be an integer")
i2c.write_i2c_block_data(I2C_ADDRESS, CMD_ENABLE_LEDS,
[enable_mask & 0x3F, (enable_mask >> 6) & 0x3F, (enable_mask >> 12) & 0X3F])
i2c.write_i2c_block_data(I2C_ADDRESS, CMD_UPDATE, [0xFF])
def channel_gamma(channel, gamma_table):
"""
Overrides the gamma table for a single channel.
Args:
channel (int): channel number
gamma_table (list): list of 256 gamma correction values
Raises:
TypeError: if channel is not an integer.
ValueError: if channel is not in the range 0..17.
TypeError: if gamma_table is not a list.
"""
global channel_gamma_table
if not isinstance(channel, int):
raise TypeError("channel must be an integer")
if channel not in range(18):
raise ValueError("channel be an integer in the range 0..17")
if not isinstance(gamma_table, list) or len(gamma_table) != 256:
raise TypeError("gamma_table must be a list of 256 integers")
channel_gamma_table[channel] = gamma_table
def output(values):
"""
Outputs a new set of values to the driver.
Args:
values (list): channel number
Raises:
TypeError: if values is not a list of 18 integers.
"""
if not isinstance(values, list) or len(values) != 18:
raise TypeError("values must be a list of 18 integers")
i2c.write_i2c_block_data(I2C_ADDRESS, CMD_SET_PWM_VALUES, [channel_gamma_table[i][values[i]] for i in range(18)])
i2c.write_i2c_block_data(I2C_ADDRESS, CMD_UPDATE, [0xFF])
def output_raw(values):
"""
Outputs a new set of values to the driver.
Similar to output(), but does not use channel_gamma_table.
Args:
values (list): channel number
Raises:
TypeError: if values is not a list of 18 integers.
"""
# SMBus.write_i2c_block_data does the type check, so we don't have to
if len(values) != 18:
raise TypeError("values must be a list of 18 integers")
i2c.write_i2c_block_data(I2C_ADDRESS, CMD_SET_PWM_VALUES, values)
i2c.write_i2c_block_data(I2C_ADDRESS, CMD_UPDATE, [0xFF])
try:
i2c = SMBus(i2c_bus_id())
except IOError as e:
warntxt="""
###### ###### ###### ###### ###### ###### ###### ######
i2c initialization failed - is i2c enabled on this system?
See https://github.com/pimoroni/sn3218/wiki/missing-i2c
###### ###### ###### ###### ###### ###### ###### ######
"""
warnings.warn(warntxt)
raise(e)
# generate a good default gamma table
default_gamma_table = [int(pow(255, float(i - 1) / 255)) for i in range(256)]
channel_gamma_table = [default_gamma_table] * 18
enable_leds(0b111111111111111111)
def test_cycles():
print("sn3218 test cycles")
import time
import math
# enable output
enable()
enable_leds(0b111111111111111111)
print(">> test enable mask (on/off)")
enable_mask = 0b000000000000000000
output([0x10] * 18)
for i in range(10):
enable_mask = ~enable_mask
enable_leds(enable_mask)
time.sleep(0.15)
print(">> test enable mask (odd/even)")
enable_mask = 0b101010101010101010
output([0x10] * 18)
for i in range(10):
enable_mask = ~enable_mask
enable_leds(enable_mask)
time.sleep(0.15)
print(">> test enable mask (rotate)")
enable_mask = 0b100000100000100000
output([0x10] * 18)
for i in range(10):
enable_mask = ((enable_mask & 0x01) << 18) | enable_mask >> 1
enable_leds(enable_mask)
time.sleep(0.15)
print(">> test gamma gradient")
enable_mask = 0b111111111111111111
enable_leds(enable_mask)
for i in range(256):
output([((j * (256//18)) + (i * (256//18))) % 256 for j in range(18)])
time.sleep(0.01)
print(">> test gamma fade")
enable_mask = 0b111111111111111111
enable_leds(enable_mask)
for i in range(512):
output([int((math.sin(float(i)/64.0) + 1.0) * 128.0)]*18)
time.sleep(0.01)
# turn everything off and disable output
output([0 for i in range(18)])
disable()
if __name__ == "__main__":
test_cycles()

View File

@ -1,6 +1,6 @@
# board GPIO:
# A: GPIO22
# B: GPIO23
# A: GPIO23
# B: GPIO24
#
# HW datasheet: https://learn.adafruit.com/adafruit-1-3-color-tft-bonnet-for-raspberry-pi/overview
@ -38,7 +38,10 @@ class MiniPitft(DisplayImpl):
return self._layout
def initialize(self):
logging.info("initializing Adafruit Mini Pi Tft 240x240")
logging.info("Initializing Adafruit Mini Pi Tft 240x240")
logging.info("Available pins for GPIO Buttons: 23, 24")
logging.info("Backlight pin available on GPIO 22")
logging.info("I2C bus available on stemma QT header")
from pwnagotchi.ui.hw.libs.adafruit.minipitft.ST7789 import ST7789
self._display = ST7789(0,0,25,22)

View File

@ -1,6 +1,6 @@
# board GPIO:
# A: GPIO22
# B: GPIO23
# A: GPIO23
# B: GPIO24
#
# HW datasheet: https://learn.adafruit.com/adafruit-1-3-color-tft-bonnet-for-raspberry-pi/overview
@ -39,6 +39,9 @@ class MiniPitft2(DisplayImpl):
def initialize(self):
logging.info("initializing Adafruit Mini Pi Tft 135x240")
logging.info("Available pins for GPIO Buttons: 23, 24")
logging.info("Backlight pin available on GPIO 22")
logging.info("I2C bus available on stemma QT header")
from pwnagotchi.ui.hw.libs.adafruit.minipitft2.ST7789 import ST7789
self._display = ST7789(0,0,25,22)

View File

@ -45,6 +45,10 @@ class PirateAudio(DisplayImpl):
def initialize(self):
logging.info("Initializing PirateAudio - display only")
logging.info("Available pins for GPIO Buttons A/B/X/Y: 5, 6, 16, 20 or 24")
logging.info("refer to the pimoroni site or pinout.xyz")
logging.info("Backlight pin available on GPIO 13")
logging.info("I2S for the DAC available on pins: 18, 19 and 21")
from pwnagotchi.ui.hw.libs.pimoroni.pirateaudio.ST7789 import ST7789
self._display = ST7789(0,1,9,13)

View File

@ -50,6 +50,9 @@ class Pitft(DisplayImpl):
def initialize(self):
logging.info("Initializing adafruit pitft 320x240 screen")
logging.info("Available pins for GPIO Buttons on the 3,2inch: 17, 22, 23, 27")
logging.info("Available pins for GPIO Buttons on the 2,8inch: 26, 13, 12, 6, 5")
logging.info("Backlight pin available on GPIO 18")
from pwnagotchi.ui.hw.libs.adafruit.pitft.ILI9341 import ILI9341
self._display = ILI9341(0, 0, 25, 18)

View File

@ -44,6 +44,9 @@ class TftBonnet(DisplayImpl):
def initialize(self):
logging.info("initializing Adafruit Tft Bonnet")
logging.info("Available pins for GPIO Buttons Up/Down/Left/Right/Center/A/B: 17, 22, 27, 23, 4, 5, 6")
logging.info("Backlight pin available on GPIO 26")
logging.info("I2C bus available on stemma QT header")
from pwnagotchi.ui.hw.libs.adafruit.tftbonnet.ST7789 import ST7789
self._display = ST7789(0,0,25,26)

View File

@ -49,6 +49,7 @@ class Waveshareoledlcd(DisplayImpl):
def initialize(self):
logging.info("initializing Waveshare OLED/LCD hat")
logging.info("Available pins for GPIO Buttons K1/K2/K3/K4: 4, 17, 23, 24")
from pwnagotchi.ui.hw.libs.waveshare.oled.oledlcd.ST7789 import ST7789
self._display = ST7789(0,0,22,18)

View File

@ -49,6 +49,7 @@ class Waveshareoledlcdvert(DisplayImpl):
def initialize(self):
logging.info("initializing Waveshare OLED/LCD hat vertical mode")
logging.info("Available pins for GPIO Buttons K1/K2/K3/K4: 4, 17, 23, 24")
from pwnagotchi.ui.hw.libs.waveshare.oled.oledlcd.ST7789vert import ST7789
self._display = ST7789(0,0,22,18)