Adafruit_Learning_System_Gu.../CLUE/Clue_Scale/code.py
Anne Barela dc43693f17 Copy CLUE projects to new CLUE subdirectory
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.
2025-02-24 11:21:29 -06:00

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)