To "clean up" the Learn System repo, we need to move groups of guides into subdirectories. This PR duplicates the CLUE guides into the CLUE subdirectory so guides may be changed prior to deleting redundant project repos to make more space.
187 lines
6.6 KiB
Python
187 lines
6.6 KiB
Python
# SPDX-FileCopyrightText: 2023 Jan Goolsbey for Adafruit Industries
|
|
# SPDX-License-Identifier: MIT
|
|
#
|
|
# clue_scale_code.py
|
|
# 2023-01-13 v1.2.1
|
|
#
|
|
# Clue Scale - Single Channel Version
|
|
# Adafruit NAU7802 Stemma breakout example
|
|
|
|
# import clue_scale_calibrator # Uncomment to run calibrator method
|
|
|
|
import time
|
|
import board
|
|
from simpleio import map_range
|
|
from adafruit_clue import clue
|
|
from adafruit_display_shapes.circle import Circle
|
|
from adafruit_display_text.label import Label
|
|
from adafruit_bitmap_font import bitmap_font
|
|
import displayio
|
|
from cedargrove_nau7802 import NAU7802
|
|
|
|
clue.pixel.brightness = 0.2 # Set NeoPixel brightness
|
|
clue.pixel[0] = clue.YELLOW # Set status indicator to yellow (initializing)
|
|
|
|
# Set Scale Defaults
|
|
MAX_GR = 100 # Maximum (full-scale) display range in grams
|
|
DEFAULT_GAIN = 128 # Default gain for internal PGA
|
|
SAMPLE_AVG = 5 # Number of sample values to average
|
|
SCALE_NAME_1 = "COFFEE" # 6 characters maximum
|
|
SCALE_NAME_2 = "SCALE" # 6 characters maximum
|
|
|
|
"""Enter the calibration ratio for the individual load cell in-use. The ratio is
|
|
composed of the reference weight in grams divided by the raw reading. For
|
|
example, a raw reading of 215300 for a 100 gram weight results in a calibration
|
|
ratio of 100 / 215300. Use the clue_scale_single_calibrate method to obtain the
|
|
raw value.
|
|
FYI: A US dime coin weighs 2.268 grams or 0.079 ounces."""
|
|
CALIB_RATIO = 100 / 215300 # load cell serial#4540-02
|
|
|
|
# Instantiate the Sensor and Display
|
|
i2c = board.I2C() # uses board.SCL and board.SDA
|
|
# i2c = board.STEMMA_I2C() # For using the built-in STEMMA QT connector on a microcontroller
|
|
nau7802 = NAU7802(i2c, address=0x2A, active_channels=1)
|
|
|
|
display = board.DISPLAY
|
|
scale_group = displayio.Group()
|
|
|
|
FONT_0 = bitmap_font.load_font("/fonts/Helvetica-Bold-24.bdf")
|
|
FONT_1 = bitmap_font.load_font("/fonts/OpenSans-16.bdf")
|
|
FONT_2 = bitmap_font.load_font("/fonts/OpenSans-9.bdf")
|
|
|
|
# Display the Background Bitmap Image
|
|
bkg = displayio.OnDiskBitmap("/clue_scale_bkg.bmp")
|
|
_background = displayio.TileGrid(bkg, pixel_shader=bkg.pixel_shader, x=0, y=0)
|
|
scale_group.append(_background)
|
|
|
|
# Define and Display the Text Labels and Graphic Elements
|
|
# Place the project name on either side of the graduated scale
|
|
scale_name_1 = Label(FONT_1, text=SCALE_NAME_1, color=clue.CYAN)
|
|
scale_name_1.anchor_point = (0.5, 0.5)
|
|
scale_name_1.anchored_position = (40, 96)
|
|
scale_group.append(scale_name_1)
|
|
|
|
scale_name_2 = Label(FONT_1, text=SCALE_NAME_2, color=clue.CYAN)
|
|
scale_name_2.anchor_point = (0.5, 0.5)
|
|
scale_name_2.anchored_position = (199, 96)
|
|
scale_group.append(scale_name_2)
|
|
|
|
# Define the zeroing button graphic
|
|
zero_button_circle = Circle(14, 152, 14, fill=None, outline=clue.RED, stroke=2)
|
|
scale_group.append(zero_button_circle)
|
|
|
|
zero_button_label = Label(FONT_1, text="Z", color=clue.RED)
|
|
zero_button_label.x = 8
|
|
zero_button_label.y = 150
|
|
scale_group.append(zero_button_label)
|
|
|
|
# Place tickmark labels next to the graduated scale
|
|
for i in range(-1, 6):
|
|
tick_value = Label(FONT_2, text=str((MAX_GR) // 5 * i), color=clue.CYAN)
|
|
if i == -1:
|
|
tick_value.anchor_point = (1.0, 1.1)
|
|
elif i == 5:
|
|
tick_value.anchor_point = (1.0, 0.0)
|
|
else:
|
|
tick_value.anchor_point = (1.0, 0.5)
|
|
tick_value.anchored_position = (99, 201 - (i * 40))
|
|
scale_group.append(tick_value)
|
|
|
|
# Place the grams and ounces labels and values near the bottom of the display
|
|
grams_label = Label(FONT_0, text="grams", color=clue.BLUE)
|
|
grams_label.anchor_point = (1.0, 0)
|
|
grams_label.anchored_position = (80, 216)
|
|
scale_group.append(grams_label)
|
|
|
|
ounces_label = Label(FONT_0, text="ounces", color=clue.BLUE)
|
|
ounces_label.anchor_point = (1.0, 0)
|
|
ounces_label.anchored_position = (230, 216)
|
|
scale_group.append(ounces_label)
|
|
|
|
grams_value = Label(FONT_0, text="0.0", color=clue.WHITE)
|
|
grams_value.anchor_point = (1.0, 0.5)
|
|
grams_value.anchored_position = (80, 200)
|
|
scale_group.append(grams_value)
|
|
|
|
ounces_value = Label(FONT_0, text="0.00", color=clue.WHITE)
|
|
ounces_value.anchor_point = (1.0, 0.5)
|
|
ounces_value.anchored_position = (230, 200)
|
|
scale_group.append(ounces_value)
|
|
|
|
# Define the moveable indicator bubble
|
|
indicator_group = displayio.Group()
|
|
bubble = Circle(120, 200, 10, fill=clue.YELLOW, outline=clue.YELLOW, stroke=3)
|
|
indicator_group.append(bubble)
|
|
|
|
scale_group.append(indicator_group)
|
|
display.root_group = scale_group
|
|
|
|
|
|
# Helpers
|
|
def zero_channel():
|
|
"""Prepare internal amplifier settings and zero the current channel. Use
|
|
after power-up, a new channel is selected, or to adjust for measurement
|
|
drift. Can be used to zero the scale with a tare weight.
|
|
The nau7802.calibrate function used here does not calibrate the load cell,
|
|
but sets the NAU7802 internals to prepare for measuring input signals."""
|
|
nau7802.calibrate("INTERNAL")
|
|
nau7802.calibrate("OFFSET")
|
|
|
|
|
|
def read(samples=1):
|
|
"""Read and average consecutive raw samples; return averaged value."""
|
|
sample_sum = 0
|
|
sample_count = samples
|
|
while sample_count > 0:
|
|
if nau7802.available():
|
|
sample_sum = sample_sum + nau7802.read()
|
|
sample_count -= 1
|
|
return int(sample_sum / samples)
|
|
|
|
|
|
# Activate the Sensor
|
|
# Enable the internal analog circuitry, set gain, and zero
|
|
nau7802.enable(True)
|
|
nau7802.gain = DEFAULT_GAIN
|
|
zero_channel()
|
|
|
|
# Play "welcome" tones
|
|
clue.play_tone(1660, 0.15)
|
|
clue.play_tone(1440, 0.15)
|
|
|
|
# The Primary Code Loop
|
|
# Read sensor, move bubble, and display values
|
|
while True:
|
|
clue.pixel[0] = clue.GREEN # Set status indicator to green (ready)
|
|
|
|
# Read the raw scale value and scale for grams and ounces
|
|
value = read(SAMPLE_AVG)
|
|
mass_grams = round(value * CALIB_RATIO, 1)
|
|
mass_ounces = round(mass_grams * 0.03527, 2)
|
|
grams_value.text = f"{mass_grams:5.1f}"
|
|
ounces_value.text = f"{mass_ounces:5.2f}"
|
|
print(f" {mass_grams:5.1f} grams {mass_ounces:5.2f} ounces")
|
|
|
|
# Reposition the indicator bubble based on grams value
|
|
min_gr = (MAX_GR // 5) * -1 # Minimum display value
|
|
bubble.y = int(map_range(mass_grams, min_gr, MAX_GR, 240, 0)) - 10
|
|
if mass_grams > MAX_GR or mass_grams < min_gr:
|
|
bubble.fill = clue.RED
|
|
else:
|
|
bubble.fill = None
|
|
|
|
# Check to see if the zeroing button is pressed
|
|
if clue.button_a:
|
|
# Zero the sensor
|
|
clue.pixel[0] = clue.RED # Set status indicator to red (stopped)
|
|
bubble.fill = clue.RED # Set bubble center to red (stopped)
|
|
clue.play_tone(1660, 0.3) # Play "button pressed" tone
|
|
|
|
zero_channel()
|
|
|
|
while clue.button_a:
|
|
# Wait until the button is released
|
|
time.sleep(0.1)
|
|
|
|
clue.play_tone(1440, 0.5) # Play "reset completed" tone
|
|
bubble.fill = None # Set bubble center to transparent (ready)
|