mirror of
https://github.com/jayofelony/pwnagotchi.git
synced 2025-07-01 18:37:27 -04:00
separate I2C Oled display
Separate files for ggeneric I2C Oled display with SSD1306 driver 128x64 resolution is tested, 128x32 and 96x16 resolutions is not tested yet
This commit is contained in:
@ -1,18 +1,9 @@
|
|||||||
# workinprogress based on the displayhatmini driver
|
# Created for the Pwnagotchi project by RasTacsko
|
||||||
# LCD support OK
|
# HW libraries are based on the adafruit python SSD1306 repo:
|
||||||
# OLED support ongoing
|
# https://github.com/adafruit/Adafruit_Python_SSD1306
|
||||||
# board GPIO:
|
# SMBus parts coming from BLavery's lib_oled96 repo:
|
||||||
# Key1: GPIO4 / pin7
|
# https://github.com/BLavery/lib_oled96
|
||||||
# Key2: GPIO17 / pin11
|
|
||||||
# Key3: GPIO23 / pin16
|
|
||||||
# Key4: GPIO24 / pin18
|
|
||||||
# OLED SDA: GPIO2 / pin3
|
|
||||||
# OLED SCL: GPIO3 / pin5
|
|
||||||
# OLED info:
|
|
||||||
# driver: SSD1315 (I2C)
|
|
||||||
# resolution: 128x64
|
|
||||||
# I2C address: 0x3C 0x3D
|
|
||||||
# HW datasheet: https://www.waveshare.com/wiki/OLED/LCD_HAT_(A)
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
import pwnagotchi.ui.fonts as fonts
|
import pwnagotchi.ui.fonts as fonts
|
||||||
@ -45,8 +36,9 @@ class I2COled(DisplayImpl):
|
|||||||
return self._layout
|
return self._layout
|
||||||
|
|
||||||
def initialize(self):
|
def initialize(self):
|
||||||
logging.info("initializing I2C Oled Display on address 0x3C")
|
logging.info("initializing 128x64 I2C Oled Display on address 0x3C")
|
||||||
from pwnagotchi.ui.hw.libs.waveshare.oled.oledlcd.epd import EPD
|
logging.info("To change resolution or address check pwnagotchi/ui/hw/libs/i2coled/epd.py")
|
||||||
|
from pwnagotchi.ui.hw.libs.i2coled.epd import EPD
|
||||||
self._display = EPD()
|
self._display = EPD()
|
||||||
self._display.Init()
|
self._display.Init()
|
||||||
self._display.Clear()
|
self._display.Clear()
|
||||||
|
304
pwnagotchi/ui/hw/libs/i2coled/SSD1306.py
Normal file
304
pwnagotchi/ui/hw/libs/i2coled/SSD1306.py
Normal file
@ -0,0 +1,304 @@
|
|||||||
|
# Copyright (c) 2014 Adafruit Industries
|
||||||
|
# Author: Tony DiCola
|
||||||
|
#
|
||||||
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
# of this software and associated documentation 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
|
||||||
|
# furnished 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 FOR 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.
|
||||||
|
#
|
||||||
|
# SMBus parts coming from BLavery's lib_oled96 repo:
|
||||||
|
# https://github.com/BLavery/lib_oled96
|
||||||
|
#
|
||||||
|
# Modified for the Pwnagotchi project by RasTacsko
|
||||||
|
# Using SMBus, spidev RPi.GPIO for I2C communication instead of Adafruit libraries
|
||||||
|
# spidev maybe not necessary... needs some checking!!!
|
||||||
|
# ToDo:
|
||||||
|
# rotation support for vertical layouts
|
||||||
|
# checking luma oled library for support other chipsets/resolutions
|
||||||
|
|
||||||
|
from __future__ import division
|
||||||
|
import logging
|
||||||
|
import time
|
||||||
|
|
||||||
|
import RPi.GPIO as GPIO
|
||||||
|
# import spidev
|
||||||
|
from smbus import SMBus
|
||||||
|
i2cbus = SMBus(1)
|
||||||
|
|
||||||
|
|
||||||
|
# Constants
|
||||||
|
SSD1306_I2C_ADDRESS = 0x3C # 011110+SA0+RW - 0x3C or 0x3D
|
||||||
|
SSD1306_SETCONTRAST = 0x81
|
||||||
|
SSD1306_DISPLAYALLON_RESUME = 0xA4
|
||||||
|
SSD1306_DISPLAYALLON = 0xA5
|
||||||
|
SSD1306_NORMALDISPLAY = 0xA6
|
||||||
|
SSD1306_INVERTDISPLAY = 0xA7
|
||||||
|
SSD1306_DISPLAYOFF = 0xAE
|
||||||
|
SSD1306_DISPLAYON = 0xAF
|
||||||
|
SSD1306_SETDISPLAYOFFSET = 0xD3
|
||||||
|
SSD1306_SETCOMPINS = 0xDA
|
||||||
|
SSD1306_SETVCOMDETECT = 0xDB
|
||||||
|
SSD1306_SETDISPLAYCLOCKDIV = 0xD5
|
||||||
|
SSD1306_SETPRECHARGE = 0xD9
|
||||||
|
SSD1306_SETMULTIPLEX = 0xA8
|
||||||
|
SSD1306_SETLOWCOLUMN = 0x00
|
||||||
|
SSD1306_SETHIGHCOLUMN = 0x10
|
||||||
|
SSD1306_SETSTARTLINE = 0x40
|
||||||
|
SSD1306_MEMORYMODE = 0x20
|
||||||
|
SSD1306_COLUMNADDR = 0x21
|
||||||
|
SSD1306_PAGEADDR = 0x22
|
||||||
|
SSD1306_COMSCANINC = 0xC0
|
||||||
|
SSD1306_COMSCANDEC = 0xC8
|
||||||
|
SSD1306_SEGREMAP = 0xA0
|
||||||
|
SSD1306_CHARGEPUMP = 0x8D
|
||||||
|
SSD1306_EXTERNALVCC = 0x1
|
||||||
|
SSD1306_SWITCHCAPVCC = 0x2
|
||||||
|
|
||||||
|
# Scrolling constants
|
||||||
|
SSD1306_ACTIVATE_SCROLL = 0x2F
|
||||||
|
SSD1306_DEACTIVATE_SCROLL = 0x2E
|
||||||
|
SSD1306_SET_VERTICAL_SCROLL_AREA = 0xA3
|
||||||
|
SSD1306_RIGHT_HORIZONTAL_SCROLL = 0x26
|
||||||
|
SSD1306_LEFT_HORIZONTAL_SCROLL = 0x27
|
||||||
|
SSD1306_VERTICAL_AND_RIGHT_HORIZONTAL_SCROLL = 0x29
|
||||||
|
SSD1306_VERTICAL_AND_LEFT_HORIZONTAL_SCROLL = 0x2A
|
||||||
|
|
||||||
|
|
||||||
|
class SSD1306Base(object):
|
||||||
|
"""Base class for SSD1306-based OLED displays. Implementors should subclass
|
||||||
|
and provide an implementation for the _initialize function.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, width, height, address=SSD1306_I2C_ADDRESS, bus=None):
|
||||||
|
self._log = logging.getLogger('Adafruit_SSD1306.SSD1306Base')
|
||||||
|
self.cmd_mode = 0x00
|
||||||
|
self.data_mode = 0x40
|
||||||
|
self.bus = i2cbus
|
||||||
|
self.addr = address
|
||||||
|
self.width = width
|
||||||
|
self.height = height
|
||||||
|
self._pages = height//8
|
||||||
|
self._buffer = [0]*(width*self._pages)
|
||||||
|
|
||||||
|
def _initialize(self):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def command(self, *cmd):
|
||||||
|
"""Send command byte to display."""
|
||||||
|
# I2C write.
|
||||||
|
assert(len(cmd) <= 31)
|
||||||
|
self.bus.write_i2c_block_data(self.addr, self.cmd_mode, list(cmd))
|
||||||
|
|
||||||
|
def data(self, data):
|
||||||
|
"""Send byte of data to display."""
|
||||||
|
# I2C write.
|
||||||
|
for i in range(0, len(data), 31):
|
||||||
|
self.bus.write_i2c_block_data(self.addr, self.data_mode, list(data[i:i+31]))
|
||||||
|
|
||||||
|
def begin(self, vccstate=SSD1306_SWITCHCAPVCC):
|
||||||
|
"""Initialize display."""
|
||||||
|
# Save vcc state.
|
||||||
|
self._vccstate = vccstate
|
||||||
|
# Reset and initialize display.
|
||||||
|
self._initialize()
|
||||||
|
# Turn on the display.
|
||||||
|
self.command(SSD1306_DISPLAYON)
|
||||||
|
|
||||||
|
def ShowImage(self):
|
||||||
|
"""
|
||||||
|
The image on the "canvas" is flushed through to the hardware display.
|
||||||
|
Takes the 1-bit image and dumps it to the SSD1306 OLED display.
|
||||||
|
"""
|
||||||
|
self.command(SSD1306_COLUMNADDR)
|
||||||
|
self.command(0) # Column start address. (0 = reset)
|
||||||
|
self.command(self.width-1) # Column end address.
|
||||||
|
self.command(SSD1306_PAGEADDR)
|
||||||
|
self.command(0) # Page start address. (0 = reset)
|
||||||
|
self.command(self._pages-1) # Page end address.
|
||||||
|
|
||||||
|
for i in range(0, len(self._buffer), 16):
|
||||||
|
self.bus.write_i2c_block_data(self.addr, self.data_mode, self._buffer[i:i+16])
|
||||||
|
|
||||||
|
def getbuffer(self, image):
|
||||||
|
"""Set buffer to value of Python Imaging Library image. The image should
|
||||||
|
be in 1 bit mode and a size equal to the display size.
|
||||||
|
"""
|
||||||
|
if image.mode != '1':
|
||||||
|
raise ValueError('Image must be in mode 1.')
|
||||||
|
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))
|
||||||
|
# Grab all the pixels from the image, faster than getpixel.
|
||||||
|
pix = image.load()
|
||||||
|
# Iterate through the memory pages
|
||||||
|
index = 0
|
||||||
|
for page in range(self._pages):
|
||||||
|
# Iterate through all x axis columns.
|
||||||
|
for x in range(self.width):
|
||||||
|
# Set the bits for the column of pixels at the current position.
|
||||||
|
bits = 0
|
||||||
|
# Don't use range here as it's a bit slow
|
||||||
|
for bit in [0, 1, 2, 3, 4, 5, 6, 7]:
|
||||||
|
bits = bits << 1
|
||||||
|
bits |= 0 if pix[(x, page*8+7-bit)] == 0 else 1
|
||||||
|
# Update buffer byte and increment to next byte.
|
||||||
|
self._buffer[index] = bits
|
||||||
|
index += 1
|
||||||
|
|
||||||
|
def clear(self):
|
||||||
|
"""Clear contents of image buffer."""
|
||||||
|
self._buffer = [0]*(self.width*self._pages)
|
||||||
|
|
||||||
|
def set_contrast(self, contrast):
|
||||||
|
"""Sets the contrast of the display. Contrast should be a value between
|
||||||
|
0 and 255."""
|
||||||
|
if contrast < 0 or contrast > 255:
|
||||||
|
raise ValueError('Contrast must be a value from 0 to 255 (inclusive).')
|
||||||
|
self.command(SSD1306_SETCONTRAST)
|
||||||
|
self.command(contrast)
|
||||||
|
|
||||||
|
def dim(self, dim):
|
||||||
|
"""Adjusts contrast to dim the display if dim is True, otherwise sets the
|
||||||
|
contrast to normal brightness if dim is False.
|
||||||
|
"""
|
||||||
|
# Assume dim display.
|
||||||
|
contrast = 0
|
||||||
|
# Adjust contrast based on VCC if not dimming.
|
||||||
|
if not dim:
|
||||||
|
if self._vccstate == SSD1306_EXTERNALVCC:
|
||||||
|
contrast = 0x9F
|
||||||
|
else:
|
||||||
|
contrast = 0xCF
|
||||||
|
self.set_contrast(contrast)
|
||||||
|
|
||||||
|
class SSD1306_128_64(SSD1306Base):
|
||||||
|
def __init__(self, width, height, address=None, bus=None):
|
||||||
|
# Call base class constructor.
|
||||||
|
super(SSD1306_128_64, self).__init__(128, 64, address, bus)
|
||||||
|
|
||||||
|
def _initialize(self):
|
||||||
|
# 128x64 pixel specific initialization.
|
||||||
|
self.command(SSD1306_DISPLAYOFF) # 0xAE
|
||||||
|
self.command(SSD1306_SETDISPLAYCLOCKDIV) # 0xD5
|
||||||
|
self.command(0x80) # the suggested ratio 0x80
|
||||||
|
self.command(SSD1306_SETMULTIPLEX) # 0xA8
|
||||||
|
self.command(0x3F)
|
||||||
|
self.command(SSD1306_SETDISPLAYOFFSET) # 0xD3
|
||||||
|
self.command(0x0) # no offset
|
||||||
|
self.command(SSD1306_SETSTARTLINE | 0x0) # line #0
|
||||||
|
self.command(SSD1306_CHARGEPUMP) # 0x8D
|
||||||
|
if self._vccstate == SSD1306_EXTERNALVCC:
|
||||||
|
self.command(0x10)
|
||||||
|
else:
|
||||||
|
self.command(0x14)
|
||||||
|
self.command(SSD1306_MEMORYMODE) # 0x20
|
||||||
|
self.command(0x00) # 0x0 act like ks0108
|
||||||
|
self.command(SSD1306_SEGREMAP | 0x1)
|
||||||
|
self.command(SSD1306_COMSCANDEC)
|
||||||
|
self.command(SSD1306_SETCOMPINS) # 0xDA
|
||||||
|
self.command(0x12)
|
||||||
|
self.command(SSD1306_SETCONTRAST) # 0x81
|
||||||
|
if self._vccstate == SSD1306_EXTERNALVCC:
|
||||||
|
self.command(0x9F)
|
||||||
|
else:
|
||||||
|
self.command(0xCF)
|
||||||
|
self.command(SSD1306_SETPRECHARGE) # 0xd9
|
||||||
|
if self._vccstate == SSD1306_EXTERNALVCC:
|
||||||
|
self.command(0x22)
|
||||||
|
else:
|
||||||
|
self.command(0xF1)
|
||||||
|
self.command(SSD1306_SETVCOMDETECT) # 0xDB
|
||||||
|
self.command(0x40)
|
||||||
|
self.command(SSD1306_DISPLAYALLON_RESUME) # 0xA4
|
||||||
|
self.command(SSD1306_NORMALDISPLAY) # 0xA6
|
||||||
|
|
||||||
|
class SSD1306_128_32(SSD1306Base):
|
||||||
|
def __init__(self, width, height, address=None, bus=None):
|
||||||
|
# Call base class constructor.
|
||||||
|
super(SSD1306_128_64, self).__init__(128, 64, address, bus)
|
||||||
|
|
||||||
|
def _initialize(self):
|
||||||
|
# 128x32 pixel specific initialization.
|
||||||
|
self.command(SSD1306_DISPLAYOFF) # 0xAE
|
||||||
|
self.command(SSD1306_SETDISPLAYCLOCKDIV) # 0xD5
|
||||||
|
self.command(0x80) # the suggested ratio 0x80
|
||||||
|
self.command(SSD1306_SETMULTIPLEX) # 0xA8
|
||||||
|
self.command(0x1F)
|
||||||
|
self.command(SSD1306_SETDISPLAYOFFSET) # 0xD3
|
||||||
|
self.command(0x0) # no offset
|
||||||
|
self.command(SSD1306_SETSTARTLINE | 0x0) # line #0
|
||||||
|
self.command(SSD1306_CHARGEPUMP) # 0x8D
|
||||||
|
if self._vccstate == SSD1306_EXTERNALVCC:
|
||||||
|
self.command(0x10)
|
||||||
|
else:
|
||||||
|
self.command(0x14)
|
||||||
|
self.command(SSD1306_MEMORYMODE) # 0x20
|
||||||
|
self.command(0x00) # 0x0 act like ks0108
|
||||||
|
self.command(SSD1306_SEGREMAP | 0x1)
|
||||||
|
self.command(SSD1306_COMSCANDEC)
|
||||||
|
self.command(SSD1306_SETCOMPINS) # 0xDA
|
||||||
|
self.command(0x02)
|
||||||
|
self.command(SSD1306_SETCONTRAST) # 0x81
|
||||||
|
self.command(0x8F)
|
||||||
|
self.command(SSD1306_SETPRECHARGE) # 0xd9
|
||||||
|
if self._vccstate == SSD1306_EXTERNALVCC:
|
||||||
|
self.command(0x22)
|
||||||
|
else:
|
||||||
|
self.command(0xF1)
|
||||||
|
self.command(SSD1306_SETVCOMDETECT) # 0xDB
|
||||||
|
self.command(0x40)
|
||||||
|
self.command(SSD1306_DISPLAYALLON_RESUME) # 0xA4
|
||||||
|
self.command(SSD1306_NORMALDISPLAY) # 0xA6
|
||||||
|
|
||||||
|
|
||||||
|
class SSD1306_96_16(SSD1306Base):
|
||||||
|
def __init__(self, width, height, address=None, bus=None):
|
||||||
|
# Call base class constructor.
|
||||||
|
super(SSD1306_96_16, self).__init__(96, 16, address, bus)
|
||||||
|
|
||||||
|
def _initialize(self):
|
||||||
|
# 128x32 pixel specific initialization.
|
||||||
|
self.command(SSD1306_DISPLAYOFF) # 0xAE
|
||||||
|
self.command(SSD1306_SETDISPLAYCLOCKDIV) # 0xD5
|
||||||
|
self.command(0x60) # the suggested ratio 0x60
|
||||||
|
self.command(SSD1306_SETMULTIPLEX) # 0xA8
|
||||||
|
self.command(0x0F)
|
||||||
|
self.command(SSD1306_SETDISPLAYOFFSET) # 0xD3
|
||||||
|
self.command(0x0) # no offset
|
||||||
|
self.command(SSD1306_SETSTARTLINE | 0x0) # line #0
|
||||||
|
self.command(SSD1306_CHARGEPUMP) # 0x8D
|
||||||
|
if self._vccstate == SSD1306_EXTERNALVCC:
|
||||||
|
self.command(0x10)
|
||||||
|
else:
|
||||||
|
self.command(0x14)
|
||||||
|
self.command(SSD1306_MEMORYMODE) # 0x20
|
||||||
|
self.command(0x00) # 0x0 act like ks0108
|
||||||
|
self.command(SSD1306_SEGREMAP | 0x1)
|
||||||
|
self.command(SSD1306_COMSCANDEC)
|
||||||
|
self.command(SSD1306_SETCOMPINS) # 0xDA
|
||||||
|
self.command(0x02)
|
||||||
|
self.command(SSD1306_SETCONTRAST) # 0x81
|
||||||
|
self.command(0x8F)
|
||||||
|
self.command(SSD1306_SETPRECHARGE) # 0xd9
|
||||||
|
if self._vccstate == SSD1306_EXTERNALVCC:
|
||||||
|
self.command(0x22)
|
||||||
|
else:
|
||||||
|
self.command(0xF1)
|
||||||
|
self.command(SSD1306_SETVCOMDETECT) # 0xDB
|
||||||
|
self.command(0x40)
|
||||||
|
self.command(SSD1306_DISPLAYALLON_RESUME) # 0xA4
|
||||||
|
self.command(SSD1306_NORMALDISPLAY) # 0xA6
|
27
pwnagotchi/ui/hw/libs/i2coled/epd.py
Normal file
27
pwnagotchi/ui/hw/libs/i2coled/epd.py
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
from . import SSD1306
|
||||||
|
|
||||||
|
# Display resolution, change if the screen resolution is changed!
|
||||||
|
EPD_WIDTH = 128
|
||||||
|
EPD_HEIGHT = 64
|
||||||
|
|
||||||
|
# Available screen resolutions:
|
||||||
|
# disp = SSD1306.SSD1306_128_32(128, 32, address=0x3C)
|
||||||
|
# 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
|
||||||
|
disp = SSD1306.SSD1306_128_64(128, 64, address=0x3C)
|
||||||
|
|
||||||
|
class EPD(object):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.width = EPD_WIDTH
|
||||||
|
self.height = EPD_HEIGHT
|
||||||
|
|
||||||
|
def Init(self):
|
||||||
|
disp.begin()
|
||||||
|
|
||||||
|
def Clear(self):
|
||||||
|
disp.clear()
|
||||||
|
|
||||||
|
def display(self, image):
|
||||||
|
disp.getbuffer(image)
|
||||||
|
disp.ShowImage()
|
Reference in New Issue
Block a user