Source code for qwiic_micro_oled.qwiic_micro_oled

#-----------------------------------------------------------------------------
# qwiic_micro_oled.py
#
#------------------------------------------------------------------------
#
# Written by  SparkFun Electronics, May 2019
#
#
# More information on qwiic is at https:= www.sparkfun.com/qwiic
#
# Do you like this library? Help support SparkFun. Buy a board!
#
#==================================================================================
# Copyright (c) 2019 SparkFun Electronics
#
# 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.
#==================================================================================
#
# This is mostly a port of existing Arduino functionaly, so pylint is sad.
# The goal is to keep the public interface pthonic, but internal is internal
#
# pylint: disable=line-too-long, bad-whitespace, invalid-name, too-many-lines
# pylint: disable=too-many-lines, too-many-arguments, too-many-instance-attributes
# pylint: disable=too-many-public-methods

"""
qwiic_micro_oled
=================
Python module for the [Qwiic Micro OLED Display](https://www.sparkfun.com/products/14532)

This python package is a port of the existing [SparkFun Micro OLED Arduino Library](https://github.com/sparkfun/SparkFun_Micro_OLED_Arduino_Library)

This package can be used in conjunction with the overall [SparkFun qwiic Python Package](https://github.com/sparkfun/Qwiic_Py)

New to qwiic? Take a look at the entire [SparkFun qwiic ecosystem](https://www.sparkfun.com/qwiic).

"""

from __future__ import print_function
import sys
import math

import qwiic_i2c

from . import moled_fonts as mfonts

# Define the device name and I2C addresses. These are set in the class defintion
# as class variables, making them avilable without having to create a class instance.
#
# The name of this device - note this is private
_DEFAULT_NAME = "Qwiic Micro OLED"

# Some devices have multiple availabel addresses - this is a list of these addresses.
# NOTE: The first address in this list is considered the default I2C address for the
# device.
_AVAILABLE_I2C_ADDRESS = [0x3D, 0x3C]

# The defines from the OLED Aurdino library

I2C_COMMAND = 0x00
I2C_DATA = 0x40

LCDWIDTH            = 64
LCDHEIGHT           = 48
FONTHEADERSIZE      = 6


WIDGETSTYLE0            = 0
WIDGETSTYLE1            = 1
WIDGETSTYLE2            = 2

SETCONTRAST         = 0x81
DISPLAYALLONRESUME  = 0xA4
DISPLAYALLON        = 0xA5
NORMALDISPLAY       = 0xA6
INVERTDISPLAY       = 0xA7
DISPLAYOFF          = 0xAE
DISPLAYON           = 0xAF
SETDISPLAYOFFSET    = 0xD3
SETCOMPINS          = 0xDA
SETVCOMDESELECT     = 0xDB
SETDISPLAYCLOCKDIV  = 0xD5
SETPRECHARGE        = 0xD9
SETMULTIPLEX        = 0xA8
SETLOWCOLUMN        = 0x00
SETHIGHCOLUMN       = 0x10
SETSTARTLINE        = 0x40
MEMORYMODE          = 0x20
COMSCANINC          = 0xC0
COMSCANDEC          = 0xC8
SEGREMAP            = 0xA0
CHARGEPUMP          = 0x8D
EXTERNALVCC         = 0x01
SWITCHCAPVCC        = 0x02

#  Scroll
ACTIVATESCROLL                  = 0x2F
DEACTIVATESCROLL                = 0x2E
SETVERTICALSCROLLAREA           = 0xA3
RIGHTHORIZONTALSCROLL           = 0x26
LEFT_HORIZONTALSCROLL           = 0x27
VERTICALRIGHTHORIZONTALSCROLL   = 0x29
VERTICALLEFTHORIZONTALSCROLL    = 0x2A


# Define a function to Init a screen buffer that is setup for the sparkfun OLED.
# This is copied from the Arduino lib.

def _setSplashScreen(screenbuffer):

    # # LCD Memory organised in 64 horizontal pixel and 6 rows of byte
    #  B  B .............B  -----
    #  y  y .............y        \
    #  t  t .............t         \
    #  e  e .............e          \
    #  0  1 .............63          \
    #                                 \
    #  D0 D0.............D0            \
    #  D1 D1.............D1            / ROW 0
    #  D2 D2.............D2           /
    #  D3 D3.............D3          /
    #  D4 D4.............D4         /
    #  D5 D5.............D5        /
    #  D6 D6.............D6       /
    #  D7 D7.............D7  ----
    # */
    # SparkFun Electronics LOGO

    screenbuffer[:] = [\
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xF8, 0xFC, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, \
        0xFF, 0xFF, 0xFF, 0x0F, 0x07, 0x07, 0x06, 0x06, 0x00, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, \
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
        0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x81, 0x07, 0x0F, 0x3F, 0x3F, 0xFF, 0xFF, 0xFF, \
        0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFE, 0xFC, 0xFC, 0xFC, 0xFE, 0xFF, 0xFF, 0xFF, 0xFC, 0xF8, 0xE0, \
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, \
        0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF1, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xF0, 0xFD, 0xFF, \
        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, \
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, \
        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, \
        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0x3F, 0x1F, 0x07, 0x01, \
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, \
        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0x3F, 0x1F, 0x1F, 0x0F, 0x0F, 0x0F, 0x0F, \
        0x0F, 0x0F, 0x0F, 0x0F, 0x07, 0x07, 0x07, 0x03, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, \
        0x7F, 0x3F, 0x1F, 0x0F, 0x07, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]

# define the class that encapsulates the device being created. All information associated with this
# device is encapsulated by this class. The device class should be the only value exported
# from this module.

[docs]class QwiicMicroOled(object): """ QwiicMicroOled :param address: The I2C address to use for the device. If not provided, the default address is used. :param i2c_driver: An existing i2c driver object. If not provided a driver object is created. :return: The Micro OLED device object. :rtype: Object """ # Constructor device_name = _DEFAULT_NAME available_addresses = _AVAILABLE_I2C_ADDRESS # user exposed constants BLACK = 0 WHITE = 1 NORM = 0 XOR = 1 PAGE = 0 ALL = 1 def __init__(self, address=None, i2c_driver=None): # Did the user specify an I2C address? self.address = address if address is not None else self.available_addresses[0] # load the I2C driver if one isn't provided if i2c_driver is None: self._i2c = qwiic_i2c.getI2CDriver() if self._i2c is None: print("Unable to load I2C driver for this platform.") return else: self._i2c = i2c_driver # define the screen buffer - since this is a two color display, only bits are used # So the height is 8 bits / byte or LCDHEIGHT/8 # self._screenbuffer = bytearray(LCDWIDTH * int(math.ceil(LCDHEIGHT/8.))) self._screenbuffer = [0] * (LCDWIDTH * int(math.ceil(LCDHEIGHT/8.))) # Set initial contents _setSplashScreen(self._screenbuffer) self.cursorX = 0 self.cursorY = 0 self.foreColor = self.WHITE self.drawMode = self.NORM # self.fontWidth = 0 # self.fontHeight = 0 # self.fontStartChar = 0 # self.fontTotalChar = 0 self.fontType = 0 # self.fontData = None self._font = None self.nFonts = mfonts.count() #--------------------------------------------------------------------------
[docs] def is_connected(self): """ Determine if a Micro OLED device is conntected to the system.. :return: True if the device is connected, otherwise False. :rtype: bool """ return qwiic_i2c.isDeviceConnected(self.address)
connected = property(is_connected) #--------------------------------------------------------------------------
[docs] def begin(self): """ Initialize the operation of the Micro OLED module :return: Returns true of the initializtion was successful, otherwise False. :rtype: bool """ self.set_font_type(0) self.set_color(self.WHITE) self.set_draw_modee(self.NORM) self.set_cursor(0,0) # Display Init sequence for 64x48 OLED module self._i2c.writeByte(self.address, I2C_COMMAND, DISPLAYOFF) # 0xAE self._i2c.writeByte(self.address, I2C_COMMAND, SETDISPLAYCLOCKDIV) # 0xD5 self._i2c.writeByte(self.address, I2C_COMMAND, 0x80) # the suggested ratio 0x80 self._i2c.writeByte(self.address, I2C_COMMAND, SETMULTIPLEX) # 0xA8 self._i2c.writeByte(self.address, I2C_COMMAND, 0x2F) self._i2c.writeByte(self.address, I2C_COMMAND, SETDISPLAYOFFSET) # 0xD3 self._i2c.writeByte(self.address, I2C_COMMAND, 0x0) # no offset self._i2c.writeByte(self.address, I2C_COMMAND, SETSTARTLINE | 0x0) # line #0 self._i2c.writeByte(self.address, I2C_COMMAND, CHARGEPUMP) # enable charge pump self._i2c.writeByte(self.address, I2C_COMMAND, 0x14) self._i2c.writeByte(self.address, I2C_COMMAND, NORMALDISPLAY) # 0xA6 self._i2c.writeByte(self.address, I2C_COMMAND, DISPLAYALLONRESUME) # 0xA4 self._i2c.writeByte(self.address, I2C_COMMAND, SEGREMAP | 0x1) self._i2c.writeByte(self.address, I2C_COMMAND, COMSCANDEC) self._i2c.writeByte(self.address, I2C_COMMAND, SETCOMPINS) # 0xDA self._i2c.writeByte(self.address, I2C_COMMAND, 0x12) self._i2c.writeByte(self.address, I2C_COMMAND, SETCONTRAST) # 0x81 self._i2c.writeByte(self.address, I2C_COMMAND, 0x8F) self._i2c.writeByte(self.address, I2C_COMMAND, SETPRECHARGE) # 0xd9 self._i2c.writeByte(self.address, I2C_COMMAND, 0xF1) self._i2c.writeByte(self.address, I2C_COMMAND, SETVCOMDESELECT) # 0xDB self._i2c.writeByte(self.address, I2C_COMMAND, 0x40) self._i2c.writeByte(self.address, I2C_COMMAND, DISPLAYON) # --turn on oled panel self.clear(self.ALL) # Erase hardware memory inside the OLED controller to aself random data in memory.
#---------------------------------------------------- # brief Set SSD1306 page address. # Send page address command and address to the SSD1306 OLED controller.
[docs] def set_page_address(self, pageAddress): """ Set SSD1306 page address. :param pageAddress: The page address command and address :return: No return value """ self._i2c.writeByte(self.address, I2C_COMMAND, 0xb0|pageAddress)
#---------------------------------------------------- # Send column address command and address to the SSD1306 OLED controller.
[docs] def set_column_address(self, colAddress): """ Set SSD1306 column address. :param colAddress: The column address command and address :return: No return value """ self._i2c.writeByte(self.address, I2C_COMMAND, (0x10|(colAddress>>4))+0x02) self._i2c.writeByte(self.address, I2C_COMMAND, (0x0f&colAddress))
#---------------------------------------------------- # To clear GDRAM inside the LCD controller, pass in the variable mode = ALL and to clear screen page buffer pass in the variable mode = PAGE.
[docs] def clear(self, mode, value=0): """ Clear the display on the OLED Device. :param mode: To clear GDRAM inside the LCD controller, pass in the variable mode = ALL, and to clear screen page buffer pass in the variable mode = PAGE. :param value: The value to clear the screen to. Default value is 0 :return: No return value """ if mode == self.ALL: for i in range(8): self.set_page_address(i) self.set_column_address(0) #pylint: disable=unused-variable for j in range(0x80): self._i2c.writeByte(self.address, I2C_DATA, value) #pylint: enable=unused-variable else: self._screenbuffer[:] = [value]*len(self._screenbuffer)
#-------------------------------------------------------------------------- # The WHITE color of the display will turn to BLACK and the BLACK will turn to WHITE.
[docs] def invert(self, inv): """ Invert the display of the display. The WHITE color of the display will turn to BLACK and the BLACK will turn to WHITE. :param inv: If True, the screen is inverted. If False the screen is set to Normal mode. :return: No return value """ if inv: self._i2c.writeByte(self.address, I2C_COMMAND, INVERTDISPLAY) else: self._i2c.writeByte(self.address, I2C_COMMAND, NORMALDISPLAY)
#-------------------------------------------------------------------------- # OLED contract value from 0 to 255. Note: Contrast level is not very obvious.
[docs] def contrast(self, contrast): """ Set the OLED contract value from 0 to 255. Note: Contrast level is not very obvious on the display. :param contrast: Contrast Value between 0-255 :return: No return value """ self._i2c.writeByte(self.address, I2C_COMMAND, SETCONTRAST) # 0x81 self._i2c.writeByte(self.address, I2C_COMMAND, contrast)
#-------------------------------------------------------------------------- # Bulk move the screen buffer to the SSD1306 controller's memory so that images/graphics drawn on the screen buffer will be displayed on the OLED.
[docs] def display(self): """ Display the current screen buffer on the Display device. Bulk move the screen buffer to the SSD1306 controller's memory so that images/graphics drawn on the screen buffer will be displayed on the OLED. :return: No return value """ # the I2C library being used allows blocks upto 32 ints to be sent at a time. # # The screenbuffer is sliced into 32 int blocks and set. This results in a faster # refresh than the ported method (Good god, it was updating a pixel at a time ... ) # lenBlock = 32 lenLine = self.get_lcd_width() nBlocks = int(math.ceil(lenLine/lenBlock)) for i in range(6): self.set_page_address(i) lineStart = i * lenLine # offset in the screen buffer for the current line/row for iBlock in range(nBlocks): iStart = iBlock * lenBlock self.set_column_address(iStart) iEnd = iStart + min(lenLine - iStart, lenBlock) # what's left - not > 32 in len # Send the block - take into account the current line/row offset self._i2c.writeBlock(self.address, I2C_DATA, self._screenbuffer[lineStart+iStart:lineStart+iEnd])
# Leftover from port -> Arduino's print overridden so that we can use uView.print(). #--------------------------------------------------------------------------
[docs] def write(self, c): """ Write a character on the display using the current font, at the current position. :param c: Character to write. A value of '\\\\n' starts a new line. :return: 1 on success """ if c == '\n': # self.cursorY += self.fontHeight self.cursorY += self._font.height self.cursorX = 0 elif c != '\r': self.draw_char(self.cursorX, self.cursorY, c) self.cursorX += self._font.width+1 if self.cursorX > (LCDWIDTH - self._font.width): self.cursorY += self._font.height self.cursorX = 0 return 1
#--------------------------------------------------------------------------
[docs] def print(self, text): """ Print a line of text on the display using the current font, starting at the current position. :param text: The line of text to write. :return: No return value """ # a list or array? If not, make it one if not hasattr(text, '__len__'): # scalar? text = str(text) if isinstance(text, str): text = bytearray(text, encoding='ascii') for curr in text: self.write(curr)
#-------------------------------------------------------------------------- # MicroOLED's cursor position to x,y.
[docs] def set_cursor(self, x, y): """ Set the current cusor position for writing text :param x: The X position on the display :param y: The Y position on the display :return: No return value """ self.cursorX = x self.cursorY = y
#-------------------------------------------------------------------------- # Draw color pixel in the screen buffer's x,y position with NORM or XOR draw mode.
[docs] def pixel(self, x, y, color=None, mode=None): """ Draw a pixel at a given position, with a given color. Pixel copy mode is either Normal (source copy) or XOR :param x: The X position on the display :param y: The Y position on the display :param color: The color to draw. If not set, the default foreground color is used. :param mode: The mode to draw the pixl to the screen bufffer. Value can be either XOR or NORM. Default is NORM :return: No return value """ if color is None: color = self.foreColor if mode is None: mode = self.drawMode if x < 0 or x >= LCDWIDTH or y < 0 or y >= LCDHEIGHT: return x = int(x) y = int(y) index = x + (y//8)*LCDWIDTH if mode == self.XOR: if color == self.WHITE: self._screenbuffer[index] ^= (1 << (y%8)) else: if color == self.WHITE: self._screenbuffer[index] |= (1 << (y%8)) else: self._screenbuffer[index] &= (~(1 << (y%8)) & 0xff)
#-------------------------------------------------------------------------- # Draw line using color and mode from x0,y0 to x1,y1 of the screen buffer.
[docs] def line(self, x0, y0, x1, y1, color=None, mode=None): """ Draw a line starting at and ending at specified coordinates, with a given color. Pixel copy mode is either Normal (source copy) or XOR :param x0: The X starting position for the line :param y0: The Y starting position for the line. :param x1: The X ending position for the line :param y1: The Y ending position for the line. :param color: The color to draw. If not set, the default foreground color is used. :param mode: The mode to draw the pixl to the screen bufffer. Value can be either XOR or NORM. Default is NORM :return: No return value """ if color is None: color = self.foreColor if mode is None: mode = self.drawMode steep = abs(y1 - y0) > abs(x1 - x0) if steep: # swap (x0, y0) = (y0, x0) (x1, y1) = (y1, x1) if x0 > x1: # swap (x0, x1) = (x1, x0) (y0, y1) = (y1, y0) dx = x1 - x0 dy = abs(y1 - y0) err = dx // 2 ystep = 1 if y0 < y1 else -1 while x0 < x1: if steep: self.pixel(y0, x0, color, mode) else: self.pixel(x0, y0, color, mode) err -= dy if err < 0: y0 += ystep err += dx x0 += 1
#-------------------------------------------------------------------------- # Draw horizontal line using color and mode from x,y to x+width,y of the screen buffer.
[docs] def line_h(self, x, y, width, color=None, mode=None): """ Draw a horizontal line defined by a starting position and width. A color can be specified. Pixel copy mode is either Normal (source copy) or XOR :param x: The X starting position for the line :param y: The Y starting position for the line. :param width: The width (length) of the line :param color: The color to draw. If not set, the default foreground color is used. :param mode: The mode to draw the pixl to the screen bufffer. Value can be either XOR or NORM. Default is NORM :return: No return value """ if color is None: color = self.foreColor if mode is None: mode = self.drawMode self.line(x, y, x+width, y, color, mode)
#-------------------------------------------------------------------------- # Draw vertical line using color and mode from x,y to x,y+height of the screen buffer.
[docs] def line_v(self, x, y, height, color=None, mode=None): """ Draw a vertical line defined by a starting position and width. A color can be specified. Pixel copy mode is either Normal (source copy) or XOR :param x: The X starting position for the line :param y: The Y starting position for the line. :param height: The height (length) of the line :param color: The color to draw. If not set, the default foreground color is used. :param mode: The mode to draw the pixl to the screen bufffer. Value can be either XOR or NORM. Default is NORM :return: No return value """ if color is None: color = self.foreColor if mode is None: mode = self.drawMode self.line(x, y, x, y+height, color, mode)
#-------------------------------------------------------------------------- # Draw rectangle using color and mode from x,y to x+width,y+height of the screen buffer.
[docs] def rect(self, x, y, width, height, color=None, mode=None): """ Draw a rectangle on the diplay. A color can be specified. Pixel copy mode is either Normal (source copy) or XOR :param x: The X starting position for the rectangle :param y: The Y starting position for the rectangle. :param width: The width of the rectangle :param height: The height of the rectangle :param color: The color to draw. If not set, the default foreground color is used. :param mode: The mode to draw the pixl to the screen bufffer. Value can be either XOR or NORM. Default is NORM :return: No return value """ if color is None: color = self.foreColor if mode is None: mode = self.drawMode self.line_h(x, y, width, color, mode) self.line_h(x, y+height-1, width, color, mode) tempHeight = height-2 # skip drawing vertical lines to aself overlapping of pixel that will # affect XOR plot if no pixel in between horizontal lines if tempHeight < 1: return self.line_v(x, y+1, tempHeight, color, mode) self.line_v(x+width-1, y+1, tempHeight, color, mode)
#-------------------------------------------------------------------------- # Draw filled rectangle using color and mode from x,y to x+width,y+height of the screen buffer.
[docs] def rect_fill(self, x, y, width, height, color=None, mode=None): """ Draw a filled rectangle on the diplay. A color can be specified. Pixel copy mode is either Normal (source copy) or XOR :param x: The X starting position for the rectangle :param y: The Y starting position for the rectangle. :param width: The width of the rectangle :param height: The height of the rectangle :param color: The color to draw. If not set, the default foreground color is used. :param mode: The mode to draw the pixl to the screen bufffer. Value can be either XOR or NORM. Default is NORM :return: No return value """ if color is None: color = self.foreColor if mode is None: mode = self.drawMode # // TODO - need to optimise the memory map draw so that this function will not call pixel one by one for i in range(x, x+width): self.line_v(i, y, height, color, mode)
#-------------------------------------------------------------------------- # Draw circle with radius using color and mode at x,y of the screen buffer.
[docs] def circle(self, x0, y0, radius, color=None, mode=None): """ Draw a circle on the diplay. A color can be specified. Pixel copy mode is either Normal (source copy) or XOR :param x0: The X center position for the circle :param y0: The Y center position for the circle. :param radius: The radius of the circle :param color: The color to draw. If not set, the default foreground color is used. :param mode: The mode to draw the pixl to the screen bufffer. Value can be either XOR or NORM. Default is NORM :return: No return value """ if color is None: color = self.foreColor if mode is None: mode = self.drawMode # in the future- find a way to check for no overlapping of pixels so that XOR draw mode will work perfectly f = 1 - radius ddF_x = 1 ddF_y = -2 * radius x = 0 y = radius self.pixel(x0, y0+radius, color, mode) self.pixel(x0, y0-radius, color, mode) self.pixel(x0+radius, y0, color, mode) self.pixel(x0-radius, y0, color, mode) while x < y: if f >= 0: y -= 1 ddF_y += 2 f += ddF_y x += 1 ddF_x += 2 f += ddF_x self.pixel(x0 + x, y0 + y, color, mode) self.pixel(x0 - x, y0 + y, color, mode) self.pixel(x0 + x, y0 - y, color, mode) self.pixel(x0 - x, y0 - y, color, mode) self.pixel(x0 + y, y0 + x, color, mode) self.pixel(x0 - y, y0 + x, color, mode) self.pixel(x0 + y, y0 - x, color, mode) self.pixel(x0 - y, y0 - x, color, mode)
# The height of the LCD return as byte.
[docs] def get_lcd_height(self): """ The height of the display in pixels :return: height of the display :rvalue: integer """ return LCDHEIGHT
height = property(get_lcd_height) # The width of the LCD return as byte.
[docs] def get_lcd_width(self): """ The width of the display in pixels :return: width of the display :rvalue: integer """ return LCDWIDTH
width = property(get_lcd_width) # The cucrrent font's width return as byte.
[docs] def get_font_width(self): """ The width of the current font :return: width of the font :rvalue: integer """ return self._font.width
font_width = property(get_font_width) # The current font's height return as byte.
[docs] def get_font_height(self): """ The height of the current font :return: height of the font :rvalue: integer """ return self._font.height
font_height = property(get_font_height) # Return the starting ASCII character of the currnet font, not all fonts start with ASCII character 0. Custom fonts can start from any ASCII character.
[docs] def get_font_start_char(self): """ Return the starting ASCII character of the currnet font, not all fonts start with ASCII character 0. Custom fonts can start from any ASCII character. :return: Starting character of the current font. :rvalue: integer """ return self._font.start_char
# Return the total characters of the current font.
[docs] def get_font_total_char(self): """ The total number of characters in the current font. :return: Total number of characters :rvalue: integer """ return self._font.total_char
# Return the total number of fonts loaded into the MicroOLED's flash memory.
[docs] def get_total_fonts(self): """ Return the total number of fonts loaded into the MicroOLED's flash memory. :return: Total number of fonts available :rvalue: integer """ return self.nFonts
# Return the font type number of the current font.
[docs] def get_font_type(self): """ Return the font type number of the current font. :return: Font type number. :rvalue: integer """ return self.fontType
# Set the current font type number, ie changing to different fonts base on the type provided.
[docs] def set_font_type(self, font_type): """ Set the current font type number, ie changing to different fonts base on the type provided. :param type: The type to set the font to. :return: No return value """ if font_type >= self.nFonts or font_type < 0: return False self.fontType = font_type self._font = mfonts.get_font(font_type) if self._font is None: return False return True
font_type = property(get_font_type, set_font_type) # Set the current draw's color. Only WHITE and BLACK available.
[docs] def set_color(self, color): """ Set the current draw's color. Only WHITE and BLACK available. :param color: Color Value :return: No return value """ self.foreColor = color
# Set current draw mode with NORM or XOR.
[docs] def set_draw_modee(self, mode): """ Set current draw mode with NORM or XOR. :param mode: Draw Mode :return: No return value """ self.drawMode = mode
# Draw character c using color and draw mode at x,y. # pylint: disable=too-many-locals
[docs] def draw_char(self, x, y, c, color=None, mode=None): """ Draw character c using color and draw mode at x,y. Pixel copy mode is either Normal (source copy) or XOR :param x: The X position on the display :param y: The Y position on the display :param c: The character to draw :param color: The color to draw. If not set, the default foreground color is used. :param mode: The mode to draw the pixl to the screen bufffer. Value can be either XOR or NORM. Default is NORM :return: No return value """ if color is None: color = self.foreColor if mode is None: mode = self.drawMode if self._font is None: return if c < self._font.start_char or c > (self._font.start_char + self._font.total_char - 1): # no bitmap for the required c return tempC = c - self._font.start_char # // each row (in datasheet is call page) is 8 bits high, 16 bit high character will have 2 rows to be drawn rowsToDraw = self._font.height//8 # 8 is LCD's page size, see SSD1306 datasheet if rowsToDraw <= 1: rowsToDraw = 1 # figure out position of the character in the font map. integer math is key here charPerRow = self._font.map_width // self._font.width rowPos = tempC // charPerRow # the number of full rows to skip colPos = tempC % charPerRow # the number of chars into the last iStart = rowPos * charPerRow * self._font.height//8 + colPos # each row on LCD is 8 bit height (see datasheet for explanation) for row in range(rowsToDraw): # load in the current character block. #pylint: disable=consider-using-enumerate, invalid-unary-operand-type fBuffer = self._font[iStart + row * charPerRow] for i in range(len(fBuffer)): for j in range(8): # 8 is the LCD's page height (see datasheet for explanation) self.pixel(x+i, y+j + (row*8), \ color if fBuffer[i] & 0x01 << j else (~color & 0xFF), \ mode)
#pylint: enable=consider-using-enumerate, invalid-unary-operand-type
[docs] def scroll_stop(self): """ Stop scrolling operation. :return: No return value """ self._i2c.writeByte(self.address, I2C_COMMAND, DEACTIVATESCROLL)
# Set row start to row stop on the OLED to scroll right. # Refer to http://learn.microview.io/intro/general-overview-of-microview.html for explanation of the rows.
[docs] def scroll_right(self, start, stop): """ Set row start to row stop on the OLED to scroll right. Refer to http://learn.microview.io/intro/general-overview-of-microview.html for explanation of the rows. :param start: The staring position on the display :param stop: The stopping position on the display :return: No return value """ if stop < start: # stop must be larger or equal to start return self.scroll_stop() # need to disable scrolling before starting to avoid memory corrupt self._i2c.writeByte(self.address, I2C_COMMAND, RIGHTHORIZONTALSCROLL) self._i2c.writeByte(self.address, I2C_COMMAND, 0x00) self._i2c.writeByte(self.address, I2C_COMMAND, start) self._i2c.writeByte(self.address, I2C_COMMAND, 0x7) # scroll speed frames , TODO self._i2c.writeByte(self.address, I2C_COMMAND, stop) self._i2c.writeByte(self.address, I2C_COMMAND, 0x00) self._i2c.writeByte(self.address, I2C_COMMAND, 0xFF) self._i2c.writeByte(self.address, I2C_COMMAND, ACTIVATESCROLL)
# Set row start to row stop on the OLED to scroll left. # Refer to http://learn.microview.io/intro/general-overview-of-microview.html for explanation of the rows.
[docs] def scroll_left(self, start, stop): """ Set row start to row stop on the OLED to scroll left. Refer to http://learn.microview.io/intro/general-overview-of-microview.html for explanation of the rows. :param start: The staring position on the display :param stop: The stopping position on the display :return: No return value """ if stop < start: # stop must be larger or equal to start return self.scroll_stop() # need to disable scrolling before starting to avoid memory corrupt self._i2c.writeByte(self.address, I2C_COMMAND, LEFT_HORIZONTALSCROLL) self._i2c.writeByte(self.address, I2C_COMMAND, 0x00) self._i2c.writeByte(self.address, I2C_COMMAND, start) self._i2c.writeByte(self.address, I2C_COMMAND, 0x7) # scroll speed frames , TODO self._i2c.writeByte(self.address, I2C_COMMAND, stop) self._i2c.writeByte(self.address, I2C_COMMAND, 0x00) self._i2c.writeByte(self.address, I2C_COMMAND, 0xFF) self._i2c.writeByte(self.address, I2C_COMMAND, ACTIVATESCROLL)
# Flip the graphics on the OLED vertically.
[docs] def flip_vertical(self, flip): """ Flip the graphics on the OLED vertically. :return: No return value """ self._i2c.writeByte(self.address, I2C_COMMAND, COMSCANINC if flip else COMSCANDEC)
# Flip the graphics on the OLED horizontally.
[docs] def flip_horizontal(self, flip): """ Flip the graphics on the OLED horizontally. :return: No return value """ self._i2c.writeByte(self.address, I2C_COMMAND, SEGREMAP | ( 0x0 if flip else 0x1))
# Return a pointer to the start of the RAM screen buffer for direct access.
[docs] def get_screenbuffer(self): """ Return a pointer to the start of the RAM screen buffer for direct access. :return: The internal screen buffer :rtype: integer array """ return self._screenbuffer
# Draw Bitmap image on screen. The array for the bitmap can be stored in the Arduino file, so user don't have to mess with the library files. # To use, create uint8_t array that is 64x48 pixels (384 bytes). Then call .draw_bitmap and pass it the array.
[docs] def draw_bitmap(self, bitArray): """ Draw Bitmap image on screen. To use, create int array that is 64x48 pixels (384 bytes). Then call .draw_bitmap and pass it the array. :param bitArray: The bitmap to draw :return: No return value """ if len(bitArray) != len(self._screenbuffer): print("draw_bitmap - Invalid Input size.", file-sys.stderr) return self._screenbuffer[:] = bitArray