diff --git a/hardware/dimensioner/dimensioner-sensor-clip-straight.stl b/hardware/dimensioner/dimensioner-sensor-clip-straight.stl new file mode 100644 index 0000000..929484d Binary files /dev/null and b/hardware/dimensioner/dimensioner-sensor-clip-straight.stl differ diff --git a/hardware/dimensioner/dimensioner-sensor-clip.stl b/hardware/dimensioner/dimensioner-sensor-clip.stl index 700aa7c..1d7da4b 100644 Binary files a/hardware/dimensioner/dimensioner-sensor-clip.stl and b/hardware/dimensioner/dimensioner-sensor-clip.stl differ diff --git a/hardware/dimensioner/dimensioner.3mf b/hardware/dimensioner/dimensioner.3mf index 0143fb4..8262f60 100644 Binary files a/hardware/dimensioner/dimensioner.3mf and b/hardware/dimensioner/dimensioner.3mf differ diff --git a/hardware/dimensioner/dimensioner.skp b/hardware/dimensioner/dimensioner.skp index 36e0289..bd62f34 100644 Binary files a/hardware/dimensioner/dimensioner.skp and b/hardware/dimensioner/dimensioner.skp differ diff --git a/hardware/dimensioner/dimensioner.stl b/hardware/dimensioner/dimensioner.stl index d7782f7..578dfd7 100644 Binary files a/hardware/dimensioner/dimensioner.stl and b/hardware/dimensioner/dimensioner.stl differ diff --git a/src/dimensioner/config.py b/src/dimensioner/config.py new file mode 100644 index 0000000..9e23030 --- /dev/null +++ b/src/dimensioner/config.py @@ -0,0 +1,77 @@ +# Copyright 2025 PostalPortal LLC and Netsyms Technologies 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. + + +# +# Hardware configuration +# +SENSOR_TYPE = "US-100" # "PING" for Parallax PING))) or "US-100" for US-100 +SENSOR_MAX_MM = 3000 # Maximum range sensor supports and is accurate within +SENSOR_MIN_MM = 100 # Minimum range below which sensor is not accurate +SENSOR_PIN = 17 # Analog pin for PING sensor +SENSOR_TX = 16 # Digital pin for US-100 +SENSOR_RX = 17 # Digital pin for US-100 +UART_ID = 0 # TX/RX: UART 0: 12/13, 16/17; UART 1: 4/5, 8/9 +ZERO_RANGE_MM = 10 # +/- mm away from zero before a non-zero value is transmitted +SAMPLE_SIZE = 10 # Number of times to sample distance per reading, averaging the results +UNSTABLE_DELTA_MM = 50 # Max difference between largest and smallest samples (before averaging), larger than this will report as unstable +ZERO_BUTTON_PIN = 13 # Pin to read if hardware zero button is pressed +UNITS_BUTTON_PIN = 14 # Pin to read if hardware unit change button is pressed +FIRMWARE_VERSION = "1.0" + +# +# USB configuration +# +VID = 0x1209 # USB Vendor ID +PID = 0xA002 # USB Product ID +MANU = "PostalPortal LLC" # USB manufacturer string +PROD = "PostalPoint Parcel Dimensioner" # USB product string +INTERFACE = "PostalPoint" # Interface string +USBHID_ENABLED = True # Disable USB, use serial output only (good for debugging) + + +# +# Display configuration +# +ENABLE_DISPLAY = True # Set to False to ignore display commands +DISPLAY_WIDTH = 128 +DISPLAY_HEIGHT = 64 +# Available pin combinations for I2C: +# I2C 0 – SDA: GP0/GP4/GP8/GP12/GP16/GP20 +# I2C 0 – SCL: GP1/GP5/GP9/GP13/GP17/GP21 +# I2C 1 – SDA: GP2/GP6/GP10/GP14/GP18/GP26 +# I2C 1 – SCL: GP3/GP7/GP11/GP15/GP19/GP27 +# Just don't conflict with the pin assignments for the sensor and buttons! +DISPLAY_SDA_PIN = 6 +DISPLAY_SCL_PIN = 7 +DISPLAY_I2C_CONTROLLER = 1 +ENABLE_BUTTON_LEGEND = True # If true, will display onscreen labels for two buttons on the left side of the screen: top button zeros, bottom button changes units \ No newline at end of file diff --git a/src/dimensioner/dimensioner_screen.py b/src/dimensioner/dimensioner_screen.py index 800b48b..d9a2062 100644 --- a/src/dimensioner/dimensioner_screen.py +++ b/src/dimensioner/dimensioner_screen.py @@ -42,19 +42,7 @@ from time import sleep_ms from ssd1306big import setOLED, line1, line2 import framebuf import math - -ENABLE_DISPLAY = True # Set to False to ignore display commands -DISPLAY_WIDTH = 128 -DISPLAY_HEIGHT = 64 -# Available pin combinations for I2C: -# I2C 0 – SDA: GP0/GP4/GP8/GP12/GP16/GP20 -# I2C 0 – SCL: GP1/GP5/GP9/GP13/GP17/GP21 -# I2C 1 – SDA: GP2/GP6/GP10/GP14/GP18/GP26 -# I2C 1 – SCL: GP3/GP7/GP11/GP15/GP19/GP27 -# Just don't conflict with the pin assignments for the sensor and buttons! -DISPLAY_SDA_PIN = 6 -DISPLAY_SCL_PIN = 7 -DISPLAY_I2C_CONTROLLER = 1 +from config import * 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) @@ -62,152 +50,185 @@ oled = SSD1306_I2C(DISPLAY_WIDTH, DISPLAY_HEIGHT, i2c) def bootDisplay(firmwareVersion): if ENABLE_DISPLAY: - # 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(" Parcel", 0, 32, 1) - oled.text(" Dimensioner", 0, 40, 1) - firmVerStr = "FW Ver " + firmwareVersion - oled.text(f"{firmVerStr:^16}", 0, 56, 1) - oled.show() - sleep_ms(1500) + 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, 16, 0) + # Show info + oled.text(" Parcel", 0, 40, 1) + oled.text(" Dimensioner", 0, 48, 1) + firmVerStr = "FW Ver " + firmwareVersion + oled.text(f"{firmVerStr:^16}", 0, 56, 1) + oled.show() + sleep_ms(1500) + except: + pass def errorDisplay(code, message = ""): global oled if ENABLE_DISPLAY: - setOLED(oled) - oled.fill(0) - if code == 1: - line1(" ERROR! ") - oled.text(message, 0, 22, 2) - if message == "Sensor timeout": - pass - elif code == 3: - line1("UNSTABLE") - oled.text("Distance is not", 0, 22, 2) - oled.text("stable. Waiting", 0, 30, 2) - oled.text("for it to settle", 0, 38, 2) - oled.text("down and stop", 0, 46, 2) - oled.text("moving around.", 0, 54, 2) - elif code == 5: - line1("UNDER 0!") - oled.text("Object farther", 0, 22, 2) - oled.text("from platform", 0, 30, 2) - oled.text("than it should", 0, 38, 2) - oled.text("be. Check and", 0, 46, 2) - oled.text("re-zero.", 0, 54, 2) - elif code == 6: - line1("TOO FAR!") - oled.text("Distance is too", 0, 22, 2) - oled.text("far from sensor", 0, 30, 2) - oled.text("for accurate", 0, 38, 2) - oled.text("measurement.", 0, 46, 2) - elif code == 7: - line1("TOO NEAR") - oled.text("Object is too", 0, 22, 2) - oled.text("close to sensor", 0, 30, 2) - oled.text("for accurate", 0, 38, 2) - oled.text("measurement.", 0, 46, 2) - elif code == 8: - line1("HOST ERR") - oled.text("Connected PC", 0, 22, 2) - oled.text("sent an invalid", 0, 30, 2) - oled.text("command.", 0, 38, 2) - elif code == 9: - line1("BAD UNIT") - oled.text("Measurement too", 0, 22, 2) - oled.text("large to display", 0, 30, 2) - oled.text("with current", 0, 38, 2) - oled.text("unit setting.", 0, 46, 2) - oled.text("Change units.", 0, 54, 2) - elif code == 0x4D: - line1(" REPL ") - oled.text("Device is in", 0, 22, 2) - oled.text("debugging mode.", 0, 30, 2) - oled.text("Power off and", 0, 38, 2) - oled.text("back on to", 0, 46, 2) - oled.text("cancel.", 0, 54, 2) - elif code == 0x50: - line1(" UPDATE ") - oled.text("Device is in", 0, 22, 2) - oled.text("firmware update", 0, 30, 2) - oled.text("mode. Power off", 0, 38, 2) - oled.text("and back on to", 0, 46, 2) - oled.text("cancel.", 0, 54, 2) - oled.show() + try: + setOLED(oled) + oled.fill(0) + if code == 1: + line1(" ERROR! ") + oled.text(message, 0, 22, 2) + if message == "Sensor timeout": + pass + elif code == 3: + line1("UNSTABLE") + oled.text("Distance is not", 0, 22, 2) + oled.text("stable. Waiting", 0, 30, 2) + oled.text("for it to settle", 0, 38, 2) + oled.text("down and stop", 0, 46, 2) + oled.text("moving around.", 0, 54, 2) + elif code == 5: + line1("UNDER 0!") + oled.text("Object farther", 0, 22, 2) + oled.text("from platform", 0, 30, 2) + oled.text("than it should", 0, 38, 2) + oled.text("be. Check and", 0, 46, 2) + oled.text("re-zero.", 0, 54, 2) + elif code == 6: + line1("TOO FAR!") + oled.text("Distance is too", 0, 22, 2) + oled.text("far from sensor", 0, 30, 2) + oled.text("for accurate", 0, 38, 2) + oled.text("measurement.", 0, 46, 2) + elif code == 7: + line1("TOO NEAR") + oled.text("Object is too", 0, 22, 2) + oled.text("close to sensor", 0, 30, 2) + oled.text("for accurate", 0, 38, 2) + oled.text("measurement.", 0, 46, 2) + elif code == 8: + line1("HOST ERR") + oled.text("Connected PC", 0, 22, 2) + oled.text("sent an invalid", 0, 30, 2) + oled.text("command.", 0, 38, 2) + elif code == 9: + line1("BAD UNIT") + oled.text("Measurement too", 0, 22, 2) + oled.text("large to display", 0, 30, 2) + oled.text("with current", 0, 38, 2) + oled.text("unit setting.", 0, 46, 2) + oled.text("Change units.", 0, 54, 2) + elif code == 0x4D: + line1(" REPL ") + oled.text("Device is in", 0, 22, 2) + oled.text("debugging mode.", 0, 30, 2) + oled.text("Power off and", 0, 38, 2) + oled.text("back on to", 0, 46, 2) + oled.text("cancel.", 0, 54, 2) + elif code == 0x50: + line1(" UPDATE ") + oled.text("Device is in", 0, 22, 2) + oled.text("firmware update", 0, 30, 2) + oled.text("mode. Power off", 0, 38, 2) + oled.text("and back on to", 0, 46, 2) + oled.text("cancel.", 0, 54, 2) + oled.show() + except: + pass -def updateDisplay(measurement, units, tempC): +def updateDisplay(measurement, units, tempC, unstable = False): global oled if ENABLE_DISPLAY: - setOLED(oled) - wholeNumber = str(math.floor(abs(measurement))) - decimalPart = int(abs(measurement % 1) * 100) - numberLine = " " - if measurement < 0: - numberLine = "-" - if decimalPart > 0: - decimalPart = 100 - decimalPart # Fix a bug where -.75 displays as -.25 - - if units == "mm": - numberLine = numberLine + f"{wholeNumber:>7}" - elif units == "cm": - numberLine = numberLine + f"{wholeNumber:>5}.{decimalPart:0<1}" - elif units == "in": - numberLine = numberLine + f"{wholeNumber:>4}.{decimalPart:0<2}" - elif units == "ft": - numberLine = numberLine + f"{wholeNumber:>4}.{decimalPart:0<2}" - - oled.fill(0) # Fill with black to clear screen - line1(numberLine[:8]) # Write measurement to screen (but only first 8 chars to prevent IndexError: list index out of range) - oled.text(f"{units:^16}", 0, 22, 1) # Write units centered below measurement - if measurement == 0: # Write zero indicator on right below measurement - oled.text(" ZERO", 0, 22, 1) - tempStr = str(tempC) + "C" - oled.text(f"{tempStr:>16}", 0, 50, 1) # Write temperature in bottom-right corner - oled.show() + try: + setOLED(oled) + numberLine = " " + if unstable: + if units == "mm": + wholeNumber = "-------" + decimalPart = "" + elif units == "cm": + wholeNumber = "-----" + decimalPart = "-" + else: + wholeNumber = "----" + decimalPart = "--" + else: + wholeNumber = str(math.floor(abs(measurement))) + decimalPart = int(abs(measurement % 1) * 100) + if measurement < 0: + numberLine = "-" + if decimalPart > 0: + decimalPart = 100 - decimalPart # Fix a bug where -.75 displays as -.25 + + if units == "mm": + numberLine = numberLine + f"{wholeNumber:>7}" + elif units == "cm": + numberLine = numberLine + f"{wholeNumber:>5}.{decimalPart:0<1}" + elif units == "in": + numberLine = numberLine + f"{wholeNumber:>4}.{decimalPart:0<2}" + elif units == "ft": + numberLine = numberLine + f"{wholeNumber:>4}.{decimalPart:0<2}" + + oled.fill(0) # Fill with black to clear screen + line1(numberLine[:8]) # Write measurement to screen (but only first 8 chars to prevent IndexError: list index out of range) + oled.text(f"{units:^16}", 0, 22, 1) # Write units centered below measurement + if measurement == 0: # Write zero indicator on right below measurement + oled.text(" ZERO", 0, 22, 1) + tempStr = str(tempC) + "C" + oled.text(f"{tempStr:>16}", 0, 50, 1) # Write temperature in bottom-right corner + if ENABLE_BUTTON_LEGEND: + # Draw a vertical, double-ended arrow for the two physical buttons + oled.line(5, 24, 5, 40, 1) # Vertical line for top arrow + oled.line(5, 46, 5, 62, 1) # Vertical line for top arrow + oled.line(0, 28, 5, 24, 1) # Left-top arrow part + oled.line(10, 28, 5, 24, 1) # Right-top arrow part + oled.line(0, 58, 5, 62, 1) # Left-bottom arrow part + oled.line(10, 58, 5, 62, 1) # Right-bottom arrow part + oled.text("SET 0", 10, 30, 1) + oled.text("UNITS", 10, 50, 1) + oled.show() + except: + pass def clearDisplay(): - oled.fill(0) - oled.show() \ No newline at end of file + try: + oled.fill(0) + oled.show() + except: + pass \ No newline at end of file diff --git a/src/dimensioner/dimensioner_utils.py b/src/dimensioner/dimensioner_utils.py index 574a86c..fa53441 100644 --- a/src/dimensioner/dimensioner_utils.py +++ b/src/dimensioner/dimensioner_utils.py @@ -111,7 +111,7 @@ def roundUnits(x, units): # bufferSize = 1024 # size of circular buffer to allocate buffer = [' '] * bufferSize # circuolar incomming USB serial data buffer (pre fill) -bufferEcho = True # USB serial port echo incooming characters (True/False) +bufferEcho = False # USB serial port echo incooming characters (True/False) bufferNextIn, bufferNextOut = 0,0 # pointers to next in/out character in circualr buffer terminateThread = False # tell 'bufferSTDIN' function to terminate (True/False) # @@ -131,7 +131,7 @@ def bufferSTDIN(): bufferNextIn = 0 # # instantiate second 'background' thread on RD2040 dual processor to monitor and buffer -# incomming data from 'stdin' over USB serial port using ‘bufferSTDIN‘ function (above) +# incoming data from 'stdin' over USB serial port using ‘bufferSTDIN‘ function (above) # bufferSTDINthread = start_new_thread(bufferSTDIN, ()) diff --git a/src/dimensioner/main.py b/src/dimensioner/main.py index 63c00d6..55aade7 100644 --- a/src/dimensioner/main.py +++ b/src/dimensioner/main.py @@ -70,35 +70,8 @@ from usb.device.hid import HIDInterface import ujson as json from dimensioner_utils import * from dimensioner_screen import updateDisplay, errorDisplay, bootDisplay, clearDisplay +from config import * -# -# Hardware configuration -# -SENSOR_TYPE = "US-100" # "PING" for Parallax PING))) or "US-100" for US-100 -SENSOR_MAX_MM = 3000 # Maximum range sensor supports and is accurate within -SENSOR_MIN_MM = 100 # Minimum range below which sensor is not accurate -SENSOR_PIN = 17 # Analog pin for PING sensor -SENSOR_TX = 16 # Digital pin for US-100 -SENSOR_RX = 17 # Digital pin for US-100 -UART_ID = 0 # TX/RX: UART 0: 12/13, 16/17; UART 1: 4/5, 8/9 -ZERO_RANGE_MM = 10 # +/- mm away from zero before a non-zero value is transmitted -SAMPLE_SIZE = 20 # Number of times to sample distance per reading, averaging the results -UNSTABLE_DELTA_MM = 50 # Max difference between largest and smallest samples (before averaging), larger than this will report as unstable -ZERO_BUTTON_PIN = 13 # Pin to read if hardware zero button is pressed -UNITS_BUTTON_PIN = 14 # Pin to read if hardware unit change button is pressed -FIRMWARE_VERSION = "1.0" - -machine.freq(240000000) # Set CPU to 240MHz (about twice as fast as default) - -# -# USB configuration -# -VID = 0x1209 # USB Vendor ID -PID = 0xA002 # USB Product ID -MANU = "PostalPortal LLC" # USB manufacturer string -PROD = "PostalPoint Parcel Dimensioner" # USB product string -INTERFACE = "PostalPoint" # Interface string -USBHID_ENABLED = True # Disable USB, use serial output only (good for debugging) class USBHIDInterface(HIDInterface): def __init__(self): @@ -222,8 +195,8 @@ def getAmbientTemp(): else: return ambientTempC -def sendToDisplay(measurement): - updateDisplay(measurement, reportUnits, getAmbientTemp()) +def sendToDisplay(measurement, unstable = False): + updateDisplay(measurement, reportUnits, getAmbientTemp(), unstable) def setUnits(units): global reportUnits @@ -302,7 +275,7 @@ def getAverageDistanceMM(samples = SAMPLE_SIZE): maxV = sample total = total + sample i = i + 1 - sleep_us(1000) # PING datasheet says wait a minimum of 200us between measurements + sleep_us(2000) # PING datasheet says wait a minimum of 200us between measurements if abs(maxV - minV) > UNSTABLE_DELTA_MM: raise DeltaError return total / samples @@ -421,7 +394,7 @@ try: sendToDisplay(round(sizeInUnits, 2)) except DeltaError: usbinterface.send_data(b'\x02\x03\x00\x00\x00\x00\x00') - errorDisplay(3) + sendToDisplay(999, True) print("E3: Unstable") except KeyboardInterrupt: terminateThread = True diff --git a/src/dimensioner/manifest.py b/src/dimensioner/manifest.py index df622cf..e906c80 100644 --- a/src/dimensioner/manifest.py +++ b/src/dimensioner/manifest.py @@ -2,6 +2,7 @@ include("$(MPY_DIR)/ports/rp2/boards/manifest.py") require("usb-device-hid") require("ssd1306") +module("config.py") module("dimensioner_utils.py") module("dimensioner_screen.py") module("ssd1306big.py")