195 lines
10 KiB
Python
195 lines
10 KiB
Python
|
|
# 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
|
||
|
|
|
||
|
|
|