# Copyright 2026 PostalPortal LLC # # Redistribution and use in source and binary forms, with or # without modification, are permitted provided that # the following conditions are met: # 1. Redistributions of source code must retain # the above copyright notice, this list of conditions # and the following disclaimer. # 2. Redistributions in binary form must reproduce # the above copyright notice, this list of conditions # and the following disclaimer in the documentation # and/or other materials provided with the distribution. # 3. Neither the name of the copyright holder # nor the names of its contributors may be used # to endorse or promote products derived from # this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND # CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, # INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE # GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR # BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR # TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY # WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF # THE POSSIBILITY OF SUCH DAMAGE. # # This code displays device info on an attached screen. # Requires the ssd1306 module/package installed. # Connect to GPIO 0 and 1 for i2c # from machine import Pin, I2C from ssd1306 import SSD1306_I2C from time import sleep_ms import framebuf import math from config import * from scanmode import getCurrentModeStr, getModes, modeListIsSet, getPrevModeStr, getNextModeStr i2c = None oled = None if ENABLE_DISPLAY: i2c = I2C(DISPLAY_I2C_CONTROLLER, sda=Pin(DISPLAY_SDA_PIN), scl=Pin(DISPLAY_SCL_PIN), freq=400000) oled = SSD1306_I2C(DISPLAY_WIDTH, DISPLAY_HEIGHT, i2c) def bootDisplay(): if ENABLE_DISPLAY: try: # Bootup dead pixel self-test oled.fill(0) oled.show() oled.fill(1) oled.show() sleep_ms(500) oled.fill(0) oled.show() # 128x32 PostalPoint logo bitmap logo = bytearray([ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xc0, 0x40, 0x40, 0xc0, 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, 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, 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, 0xfc, 0x0e, 0x0a, 0x1a, 0x13, 0xf1, 0x19, 0x08, 0x08, 0xfc, 0xfc, 0x0c, 0x08, 0x19, 0xf1, 0xf1, 0x1b, 0x0a, 0x0e, 0xfc, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0xf8, 0xf8, 0x1c, 0x1c, 0x1c, 0xf8, 0xf8, 0xf0, 0x00, 0x00, 0x80, 0xc0, 0xe0, 0xe0, 0xe0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0xc0, 0xe0, 0xe0, 0xe0, 0xe0, 0xc0, 0x00, 0x00, 0x80, 0xf0, 0xf8, 0xf8, 0xe0, 0xe0, 0x00, 0x00, 0x80, 0xc0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0x40, 0x00, 0x00, 0xf0, 0xfc, 0xfe, 0x0e, 0x00, 0x00, 0xf0, 0xf8, 0xf8, 0x1c, 0x1c, 0x18, 0xf8, 0xf8, 0xf0, 0x00, 0x00, 0x80, 0xc0, 0xe0, 0xe0, 0xe0, 0xc0, 0x80, 0x00, 0x00, 0x00, 0xc8, 0xfc, 0xfe, 0x0c, 0x00, 0xc0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xc0, 0x00, 0x00, 0x00, 0xf0, 0xf8, 0xf8, 0xe0, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xc0, 0x80, 0x80, 0x80, 0x07, 0x0e, 0x0a, 0x1a, 0xf1, 0xf1, 0x1b, 0x0a, 0x0e, 0x0f, 0x07, 0x80, 0x80, 0xc0, 0x7f, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x7f, 0x3f, 0x07, 0x07, 0x07, 0x03, 0x03, 0x01, 0x00, 0x00, 0x1f, 0x3f, 0x7f, 0x70, 0x70, 0x38, 0x1f, 0x0f, 0x03, 0x20, 0x71, 0x73, 0x77, 0x76, 0x3e, 0x3c, 0x08, 0x00, 0x18, 0x3f, 0x7f, 0x73, 0x70, 0x30, 0x00, 0x0c, 0x3f, 0x7f, 0x73, 0x70, 0x30, 0x3f, 0x7f, 0x3f, 0x00, 0x00, 0x3f, 0x7f, 0x7f, 0x20, 0x00, 0x70, 0x7f, 0x7f, 0x0f, 0x07, 0x07, 0x07, 0x03, 0x03, 0x01, 0x00, 0x0c, 0x3f, 0x3f, 0x73, 0x70, 0x70, 0x3f, 0x1f, 0x0f, 0x00, 0x70, 0x7f, 0x3f, 0x0f, 0x00, 0x20, 0x7e, 0x7f, 0x1f, 0x01, 0x20, 0x7f, 0x7f, 0x0f, 0x00, 0x00, 0x3f, 0x7f, 0x7f, 0x70, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x03, 0x02, 0x06, 0x07, 0x07, 0x06, 0x02, 0x02, 0x01, 0x01, 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, 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, 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]) fbuf = framebuf.FrameBuffer(logo, 128, 32, framebuf.MONO_VLSB) oled.blit(fbuf, 0, 0, 0) # Show info oled.text("Barcode Scanner", 0, 36, 1) firmVerStr = "FW Ver " + FIRMWARE_VERSION oled.text(f"{firmVerStr:^16}", 0, 56, 1) oled.show() sleep_ms(1500) mainDisplay() except: pass # Center a line of text on the screen (vertical and horizontal) # If arrows, display up and down arrows above / below the text. # If showNow, clears the display before drawing, and displays the text as well. Otherwise this function only renders the display. # If arrows, the aboveLine and belowLine text will be rendered, centered horizontally, above/below the arrows. def centerText(text, arrows=False, showNow = False, aboveLine = "", belowLine = ""): if ENABLE_DISPLAY: if showNow: oled.fill(0) textWidth = len(text) * CHAR_WIDTH vCenter = DISPLAY_HEIGHT // 2 textY = ((DISPLAY_HEIGHT - CHAR_HEIGHT) // 2) oled.text(text, (DISPLAY_WIDTH - textWidth) // 2, textY, 1) if arrows: arrowLineXDistance = CHAR_WIDTH // 2 arrowLineYDistance = CHAR_HEIGHT // 2 topArrowPoint = vCenter - CHAR_HEIGHT - arrowLineYDistance bottomArrowPoint = vCenter + CHAR_HEIGHT + arrowLineYDistance xArrowCenterPoint = DISPLAY_WIDTH // 2 topLineY = vCenter - CHAR_HEIGHT - (arrowLineYDistance // 2) bottomLineY = vCenter + CHAR_HEIGHT + (arrowLineYDistance // 2) oled.line(xArrowCenterPoint - arrowLineXDistance, topArrowPoint + arrowLineYDistance, xArrowCenterPoint, topArrowPoint, 1) # Left-top arrow part oled.line(xArrowCenterPoint + arrowLineXDistance, topArrowPoint + arrowLineYDistance, xArrowCenterPoint, topArrowPoint, 1) # Right-top arrow part oled.line(xArrowCenterPoint - arrowLineXDistance, bottomArrowPoint - arrowLineYDistance, xArrowCenterPoint, bottomArrowPoint, 1) # Left-bottom arrow part oled.line(xArrowCenterPoint + arrowLineXDistance, bottomArrowPoint - arrowLineYDistance, xArrowCenterPoint, bottomArrowPoint, 1) # Right-bottom arrow part # Horizontal lines above/below center text, with a gap in the middle for the arrows oled.line(0, topLineY, xArrowCenterPoint - (arrowLineXDistance * 2), topLineY, 1) oled.line(xArrowCenterPoint + (arrowLineXDistance * 2), topLineY, DISPLAY_WIDTH, topLineY, 1) oled.line(0, bottomLineY, xArrowCenterPoint - (arrowLineXDistance * 2), bottomLineY, 1) oled.line(xArrowCenterPoint + (arrowLineXDistance * 2), bottomLineY, DISPLAY_WIDTH, bottomLineY, 1) # Top and bottom text lines, outside the arrows and lines oled.text(aboveLine, (DISPLAY_WIDTH - 1 - (len(aboveLine) * CHAR_WIDTH)) // 2, 6, 1) oled.text(belowLine, (DISPLAY_WIDTH - 1 - (len(belowLine) * CHAR_WIDTH)) // 2, DISPLAY_HEIGHT - CHAR_HEIGHT - 6, 1) if showNow: oled.show() def mainDisplay(scanInProgress = False): if ENABLE_DISPLAY: try: oled.fill(0) if modeListIsSet(): if modeListIsSet() and len(getModes()) > 1: centerText(getCurrentModeStr(), True, False, getPrevModeStr(), getNextModeStr()) else: centerText(getCurrentModeStr(), False, False) if scanInProgress: # Outline around edge of display oled.line(0, 0, DISPLAY_WIDTH - 1, 0, 1) oled.line(0, 0, 0, DISPLAY_HEIGHT - 1, 1) oled.line(DISPLAY_WIDTH - 1, 0, DISPLAY_WIDTH - 1, DISPLAY_HEIGHT - 1, 1) oled.line(0, DISPLAY_HEIGHT - 1, DISPLAY_WIDTH - 1, DISPLAY_HEIGHT - 1, 1) oled.show() except: pass def iconDisplay(icon): # TODO: add code to render icons if icon == "ERR": pass elif icon == "OK": pass elif icon == "NAF": pass elif icon == "POW": bootDisplay() def clearDisplay(showMain = False): if ENABLE_DISPLAY: try: oled.fill(0) oled.show() if showMain: mainDisplay() except: pass def brightDisplay(): if ENABLE_DISPLAY: try: oled.fill(1) oled.show() except: pass