refactor single and dual examples; improve docs
This commit is contained in:
parent
5784e95686
commit
f580faa4a9
6 changed files with 339 additions and 365 deletions
|
|
@ -1,17 +1,18 @@
|
||||||
# SPDX-FileCopyrightText: 2022 Cedar Grove Maker Studios
|
# SPDX-FileCopyrightText: 2022 Jan Goolsbey for Adafruit Industries
|
||||||
# SPDX-License-Identifier: MIT
|
# SPDX-License-Identifier: MIT
|
||||||
|
#
|
||||||
# clue_scale_single_calibrate.py 2022-07-26 1.1.0 Cedar Grove Maker Studios
|
# clue_scale_calibrator.py
|
||||||
|
# 2022-07-27 v1.1.0
|
||||||
# Clue Scale Single Channel Calibration
|
#
|
||||||
# Cedar Grove NAU7802 FeatherWing example
|
# Clue Scale Calibrator - Single Channel Version
|
||||||
|
# Adafruit NAU7802 Stemma breakout example
|
||||||
|
|
||||||
import time
|
import time
|
||||||
import board
|
import board
|
||||||
from adafruit_clue import clue
|
from adafruit_clue import clue
|
||||||
from cedargrove_nau7802 import NAU7802
|
from cedargrove_nau7802 import NAU7802
|
||||||
|
|
||||||
clue.pixel[0] = (32, 32, 0) # Set status indicator to yellow (initializing)
|
clue.pixel[0] = 0x202000 # Set status indicator to yellow (initializing)
|
||||||
|
|
||||||
SAMPLE_AVG = 1000 # Number of sample values to average
|
SAMPLE_AVG = 1000 # Number of sample values to average
|
||||||
DEFAULT_GAIN = 128 # Default gain for internal PGA
|
DEFAULT_GAIN = 128 # Default gain for internal PGA
|
||||||
|
|
@ -42,15 +43,15 @@ def read(samples=100):
|
||||||
# Activate the NAU780 internal analog circuitry, set gain, and calibrate/zero
|
# Activate the NAU780 internal analog circuitry, set gain, and calibrate/zero
|
||||||
nau7802.enable(True)
|
nau7802.enable(True)
|
||||||
nau7802.gain = DEFAULT_GAIN # Use default gain
|
nau7802.gain = DEFAULT_GAIN # Use default gain
|
||||||
zero_channel() # Calibrate and get raw zero offset value
|
zero_channel() # Calibrate and zero
|
||||||
|
|
||||||
print("-----------------------------------")
|
print("-----------------------------------")
|
||||||
print(" NAU7802 SINGLE CHANNEL CALIBRATOR")
|
print(" NAU7802 SINGLE CHANNEL CALIBRATOR")
|
||||||
print("-----------------------------------")
|
print("-----------------------------------")
|
||||||
print("Place the calibration weight on the")
|
print("Place a calibration weight on the")
|
||||||
print("load cell.")
|
print("load cell.")
|
||||||
print("To re-zero the load cell, remove")
|
print("To re-zero the load cell, remove")
|
||||||
print("any weights and press A.")
|
print("any weights then press and hold A.")
|
||||||
print("-----------------------------------")
|
print("-----------------------------------")
|
||||||
print("")
|
print("")
|
||||||
|
|
||||||
|
|
@ -58,9 +59,9 @@ print("")
|
||||||
clue.play_tone(1660, 0.15)
|
clue.play_tone(1660, 0.15)
|
||||||
clue.play_tone(1440, 0.15)
|
clue.play_tone(1440, 0.15)
|
||||||
|
|
||||||
# ## Main loop: Read sample, move bubble, and display values
|
# Main loop: Read sample and display value
|
||||||
while True:
|
while True:
|
||||||
clue.pixel[0] = (0, 32, 0) # Set status indicator to green
|
clue.pixel[0] = 0x002000 # Set status indicator to green
|
||||||
|
|
||||||
# Read the raw value; print raw value, gain setting, and % of full-scale
|
# Read the raw value; print raw value, gain setting, and % of full-scale
|
||||||
value = read(SAMPLE_AVG)
|
value = read(SAMPLE_AVG)
|
||||||
|
|
@ -73,7 +74,7 @@ while True:
|
||||||
if clue.button_a:
|
if clue.button_a:
|
||||||
# Zero and recalibrate the NAU780
|
# Zero and recalibrate the NAU780
|
||||||
clue.play_tone(1660, 0.3) # Play "button pressed" tone
|
clue.play_tone(1660, 0.3) # Play "button pressed" tone
|
||||||
clue.pixel[0] = (32, 0, 0) # Set status indicator to red (stopped)
|
clue.pixel[0] = 0x200000 # Set status indicator to red (stopped)
|
||||||
zero_channel()
|
zero_channel()
|
||||||
while clue.button_a:
|
while clue.button_a:
|
||||||
# Wait until button is released
|
# Wait until button is released
|
||||||
|
|
@ -1,12 +1,13 @@
|
||||||
# SPDX-FileCopyrightText: 2021, 2022 Cedar Grove Maker Studios
|
# SPDX-FileCopyrightText: 2022 Jan Goolsbey for Adafruit Industries
|
||||||
# SPDX-License-Identifier: MIT
|
# SPDX-License-Identifier: MIT
|
||||||
#
|
#
|
||||||
# clue_scale_single_chan_code.py 2022-07-26 1.2.0 Cedar Grove Maker Studios
|
# clue_scale_code.py
|
||||||
|
# 2022-07-27 v1.2.0
|
||||||
#
|
#
|
||||||
# Clue Scale -- single channel version
|
# Clue Scale - Single Channel Version
|
||||||
# Cedar Grove NAU7802 FeatherWing example
|
# Adafruit NAU7802 Stemma breakout example
|
||||||
|
|
||||||
# import clue_scale_single_calibrate # uncomment to run calibration method
|
# import clue_scale_calibrator # Uncomment to run calibrator method
|
||||||
|
|
||||||
import time
|
import time
|
||||||
import board
|
import board
|
||||||
|
|
@ -33,7 +34,7 @@ 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
|
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
|
ratio of 100 / 215300. Use the clue_scale_single_calibrate method to obtain the
|
||||||
raw value.
|
raw value.
|
||||||
For referency, a US dime (10-cent) coin weighs 2.268 grams."""
|
FYI: A US dime coin weighs 2.268 ounces or 64.3 grams."""
|
||||||
CALIB_RATIO_1 = 100 / 215300 # load cell serial#4540-02
|
CALIB_RATIO_1 = 100 / 215300 # load cell serial#4540-02
|
||||||
|
|
||||||
# Instantiate 24-bit load sensor ADC
|
# Instantiate 24-bit load sensor ADC
|
||||||
|
|
@ -50,7 +51,6 @@ FONT_2 = bitmap_font.load_font("/fonts/OpenSans-9.bdf")
|
||||||
# Define displayio background and group elements
|
# Define displayio background and group elements
|
||||||
bkg = displayio.OnDiskBitmap("/clue_scale_bkg.bmp")
|
bkg = displayio.OnDiskBitmap("/clue_scale_bkg.bmp")
|
||||||
_background = displayio.TileGrid(bkg, pixel_shader=bkg.pixel_shader, x=0, y=0)
|
_background = displayio.TileGrid(bkg, pixel_shader=bkg.pixel_shader, x=0, y=0)
|
||||||
|
|
||||||
scale_group.append(_background)
|
scale_group.append(_background)
|
||||||
|
|
||||||
# Place the project name on either side of the graduated scale
|
# Place the project name on either side of the graduated scale
|
||||||
|
|
@ -115,7 +115,7 @@ scale_group.append(indicator_group)
|
||||||
display.show(scale_group)
|
display.show(scale_group)
|
||||||
|
|
||||||
|
|
||||||
def zero_scale():
|
def zero_channel():
|
||||||
"""Initiate internal calibration and zero the current channel. Use after
|
"""Initiate internal calibration and zero the current channel. Use after
|
||||||
power-up, a new channel is selected, or to adjust for measurement drift.
|
power-up, a new channel is selected, or to adjust for measurement drift.
|
||||||
Can be used to zero the scale with a tare weight."""
|
Can be used to zero the scale with a tare weight."""
|
||||||
|
|
@ -137,7 +137,7 @@ def read(samples=100):
|
||||||
# Activate the NAU780 internal analog circuitry, set gain, and calibrate/zero
|
# Activate the NAU780 internal analog circuitry, set gain, and calibrate/zero
|
||||||
nau7802.enable(True)
|
nau7802.enable(True)
|
||||||
nau7802.gain = DEFAULT_GAIN
|
nau7802.gain = DEFAULT_GAIN
|
||||||
zero_scale()
|
zero_channel()
|
||||||
|
|
||||||
# Play "welcome" tones
|
# Play "welcome" tones
|
||||||
clue.play_tone(1660, 0.15)
|
clue.play_tone(1660, 0.15)
|
||||||
|
|
@ -169,7 +169,7 @@ while True:
|
||||||
bubble.fill = clue.RED # Set bubble center to red (stopped)
|
bubble.fill = clue.RED # Set bubble center to red (stopped)
|
||||||
clue.play_tone(1660, 0.3) # Play "button pressed" tone
|
clue.play_tone(1660, 0.3) # Play "button pressed" tone
|
||||||
|
|
||||||
zero_scale()
|
zero_channel()
|
||||||
|
|
||||||
while clue.button_a:
|
while clue.button_a:
|
||||||
# Wait until button is released
|
# Wait until button is released
|
||||||
|
|
@ -1,100 +0,0 @@
|
||||||
# SPDX-FileCopyrightText: 2021, 2022 Cedar Grove Maker Studios
|
|
||||||
# SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
# clue_scale_calibrate.py 2022-04-23 1.1.0 Cedar Grove Maker Studios
|
|
||||||
|
|
||||||
# Clue Scale Calibration
|
|
||||||
# Cedar Grove NAU7802 FeatherWing example
|
|
||||||
|
|
||||||
import time
|
|
||||||
import board
|
|
||||||
from adafruit_clue import clue
|
|
||||||
from cedargrove_nau7802 import NAU7802
|
|
||||||
|
|
||||||
clue.pixel[0] = (16, 0, 16) # Set status indicator to purple during instantiation phase
|
|
||||||
|
|
||||||
SAMPLE_AVG = 1000 # Number of sample values to average
|
|
||||||
DEFAULT_GAIN = 128 # Default gain for internal PGA
|
|
||||||
|
|
||||||
# Instantiate 24-bit load sensor ADC
|
|
||||||
nau7802 = NAU7802(board.I2C(), address=0x2A, active_channels=2)
|
|
||||||
|
|
||||||
|
|
||||||
def zero_channel():
|
|
||||||
# Initiate internal calibration for current channel; return raw zero offset value
|
|
||||||
# Use when scale is started, a new channel is selected, or to adjust for measurement drift
|
|
||||||
# Remove weight and tare from load cell before executing
|
|
||||||
print(
|
|
||||||
"channel %1d calibrate.INTERNAL: %5s"
|
|
||||||
% (nau7802.channel, nau7802.calibrate("INTERNAL"))
|
|
||||||
)
|
|
||||||
print(
|
|
||||||
"channel %1d calibrate.OFFSET: %5s"
|
|
||||||
% (nau7802.channel, nau7802.calibrate("OFFSET"))
|
|
||||||
)
|
|
||||||
zero_offset = read(100) # Read average of 100 samples to establish zero offset
|
|
||||||
print("...channel zeroed")
|
|
||||||
return zero_offset
|
|
||||||
|
|
||||||
|
|
||||||
def read(samples=100):
|
|
||||||
# Read and average consecutive raw sample values; return average raw 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)
|
|
||||||
|
|
||||||
|
|
||||||
# Instantiate and calibrate load cell inputs
|
|
||||||
print("*** Instantiate and calibrate load cells")
|
|
||||||
clue.pixel[0] = (16, 16, 0) # Set status indicator to yellow
|
|
||||||
print(" enable NAU7802 digital and analog power: %5s" % (nau7802.enable(True)))
|
|
||||||
|
|
||||||
nau7802.gain = DEFAULT_GAIN # Use default gain
|
|
||||||
nau7802.channel = 1
|
|
||||||
zero = zero_channel() # Calibrate and get raw zero offset value
|
|
||||||
nau7802.channel = 2
|
|
||||||
zero = zero_channel() # Calibrate and get raw zero offset value
|
|
||||||
clue.pixel[0] = (0, 16, 0) # Set status indicator to green
|
|
||||||
clue.play_tone(1660, 0.15)
|
|
||||||
clue.play_tone(1440, 0.15)
|
|
||||||
|
|
||||||
print("GAIN:", DEFAULT_GAIN)
|
|
||||||
print("Place the calibration weight on the load cell")
|
|
||||||
print("To re-zero the load cells, remove all weights and press B")
|
|
||||||
|
|
||||||
### Main loop: Read load cells and display raw values
|
|
||||||
# Monitor Zeroing button
|
|
||||||
while True:
|
|
||||||
print("=====")
|
|
||||||
nau7802.channel = 1
|
|
||||||
value = read(SAMPLE_AVG)
|
|
||||||
print(
|
|
||||||
"CHAN_%1.0f RAW VALUE: %7.0f Percent of full-scale at gain x%3.0f : %3.2f: "
|
|
||||||
% (nau7802.channel, value, DEFAULT_GAIN, (value / ((2**23) - 1)) * 100)
|
|
||||||
)
|
|
||||||
|
|
||||||
nau7802.channel = 2
|
|
||||||
value = read(SAMPLE_AVG)
|
|
||||||
print(
|
|
||||||
"CHAN_%1.0f RAW VALUE: %7.0f Percent of full-scale at gain x%3.0f : %3.2f: "
|
|
||||||
% (nau7802.channel, value, DEFAULT_GAIN, (value / ((2**23) - 1)) * 100)
|
|
||||||
)
|
|
||||||
|
|
||||||
time.sleep(0.1)
|
|
||||||
|
|
||||||
if clue.button_b:
|
|
||||||
# Zero and recalibrate both channels
|
|
||||||
clue.play_tone(1660, 0.3)
|
|
||||||
clue.pixel[0] = (16, 0, 0)
|
|
||||||
nau7802.channel = 1
|
|
||||||
zero = zero_channel()
|
|
||||||
nau7802.channel = 2
|
|
||||||
zero = zero_channel()
|
|
||||||
while clue.button_b:
|
|
||||||
time.sleep(0.1)
|
|
||||||
clue.pixel[0] = (0, 16, 0)
|
|
||||||
clue.play_tone(1440, 0.5)
|
|
||||||
|
|
@ -1,242 +0,0 @@
|
||||||
# SPDX-FileCopyrightText: 2021, 2022 Cedar Grove Maker Studios
|
|
||||||
# SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
# clue_scale_dual_chan_code.py 2022-07-26 1.2.0 Cedar Grove Maker Studios
|
|
||||||
|
|
||||||
# Clue Scale -- dual channel version
|
|
||||||
# Cedar Grove NAU7802 FeatherWing example
|
|
||||||
|
|
||||||
# import clue_scale_dual_calibrate # uncomment to run calibration method for both channels
|
|
||||||
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[0] = (16, 0, 16) # Set status indicator to purple during instantiation phase
|
|
||||||
|
|
||||||
MAX_GR = 100 # Maximum (full-scale) display range in grams
|
|
||||||
MIN_GR = (MAX_GR // 5) * -1 # Calculated minimum display value
|
|
||||||
DEFAULT_GAIN = 128 # Default gain for internal PGA
|
|
||||||
SAMPLE_AVG = 100 # Number of sample values to average
|
|
||||||
CHAN_1_LABEL = "SHOT" # 6 characters maximum
|
|
||||||
CHAN_2_LABEL = "BEANS" # 6 characters maximum
|
|
||||||
|
|
||||||
# Load cell dime-weight calibration ratio; 2.268 grams / ADC_raw_measurement
|
|
||||||
# Obtained emperically; individual load cell dependent
|
|
||||||
CALIB_RATIO_1 = (
|
|
||||||
100 / 215300
|
|
||||||
) # 100g at gain x128 for load cell serial#4540-02 attached to chan A
|
|
||||||
CALIB_RATIO_2 = (
|
|
||||||
100 / 215300
|
|
||||||
) # 100g at gain x128 for load cell serial#4540-02 attached to chan B
|
|
||||||
# Instantiate 24-bit load sensor ADC
|
|
||||||
nau7802 = NAU7802(board.I2C(), address=0x2A, active_channels=2)
|
|
||||||
|
|
||||||
# Instantiate display and fonts
|
|
||||||
print("*** Instantiate the display and fonts")
|
|
||||||
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")
|
|
||||||
|
|
||||||
# Define displayio background and group elements
|
|
||||||
print("*** Define displayio background and group elements")
|
|
||||||
bkg = displayio.OnDiskBitmap("/clue_scale_bkg.bmp")
|
|
||||||
_background = displayio.TileGrid(bkg, pixel_shader=bkg.pixel_shader, x=0, y=0)
|
|
||||||
|
|
||||||
scale_group.append(_background)
|
|
||||||
|
|
||||||
chan_1_label = Label(FONT_1, text=CHAN_1_LABEL, color=clue.CYAN)
|
|
||||||
chan_1_label.anchor_point = (0.5, 0.5)
|
|
||||||
chan_1_label.anchored_position = (40, 96)
|
|
||||||
scale_group.append(chan_1_label)
|
|
||||||
|
|
||||||
chan_2_label = Label(FONT_1, text=CHAN_2_LABEL, color=clue.CYAN)
|
|
||||||
chan_2_label.anchor_point = (0.5, 0.5)
|
|
||||||
chan_2_label.anchored_position = (199, 96)
|
|
||||||
scale_group.append(chan_2_label)
|
|
||||||
|
|
||||||
zero_1_button_label = Label(FONT_1, text="Z", color=clue.RED)
|
|
||||||
zero_1_button_label.x = 8
|
|
||||||
zero_1_button_label.y = 150
|
|
||||||
scale_group.append(zero_1_button_label)
|
|
||||||
|
|
||||||
zero_2_button_label = Label(FONT_1, text="Z", color=clue.RED)
|
|
||||||
zero_2_button_label.x = 219
|
|
||||||
zero_2_button_label.y = 150
|
|
||||||
scale_group.append(zero_2_button_label)
|
|
||||||
|
|
||||||
zero_1_button_circle = Circle(14, 149, 14, fill=None, outline=clue.RED, stroke=2)
|
|
||||||
scale_group.append(zero_1_button_circle)
|
|
||||||
|
|
||||||
zero_2_button_circle = Circle(225, 149, 14, fill=None, outline=clue.RED, stroke=2)
|
|
||||||
scale_group.append(zero_2_button_circle)
|
|
||||||
|
|
||||||
zero_value = Label(FONT_2, text="0", color=clue.CYAN)
|
|
||||||
zero_value.anchor_point = (1.0, 0.5)
|
|
||||||
zero_value.anchored_position = (97, 200)
|
|
||||||
scale_group.append(zero_value)
|
|
||||||
|
|
||||||
min_value = Label(FONT_2, text=str(MIN_GR), color=clue.CYAN)
|
|
||||||
min_value.anchor_point = (1.0, 1.0)
|
|
||||||
min_value.anchored_position = (99, 239)
|
|
||||||
scale_group.append(min_value)
|
|
||||||
|
|
||||||
max_value = Label(FONT_2, text=str(MAX_GR), color=clue.CYAN)
|
|
||||||
max_value.anchor_point = (1.0, 0)
|
|
||||||
max_value.anchored_position = (99, 0)
|
|
||||||
scale_group.append(max_value)
|
|
||||||
|
|
||||||
plus_1_value = Label(FONT_2, text=str(1 * (MAX_GR // 5)), color=clue.CYAN)
|
|
||||||
plus_1_value.anchor_point = (1.0, 0.5)
|
|
||||||
plus_1_value.anchored_position = (99, 160)
|
|
||||||
scale_group.append(plus_1_value)
|
|
||||||
|
|
||||||
plus_2_value = Label(FONT_2, text=str(2 * (MAX_GR // 5)), color=clue.CYAN)
|
|
||||||
plus_2_value.anchor_point = (1.0, 0.5)
|
|
||||||
plus_2_value.anchored_position = (99, 120)
|
|
||||||
scale_group.append(plus_2_value)
|
|
||||||
|
|
||||||
plus_3_value = Label(FONT_2, text=str(3 * (MAX_GR // 5)), color=clue.CYAN)
|
|
||||||
plus_3_value.anchor_point = (1.0, 0.5)
|
|
||||||
plus_3_value.anchored_position = (99, 80)
|
|
||||||
scale_group.append(plus_3_value)
|
|
||||||
|
|
||||||
plus_4_value = Label(FONT_2, text=str(4 * (MAX_GR // 5)), color=clue.CYAN)
|
|
||||||
plus_4_value.anchor_point = (1.0, 0.5)
|
|
||||||
plus_4_value.anchored_position = (99, 40)
|
|
||||||
scale_group.append(plus_4_value)
|
|
||||||
|
|
||||||
chan_1_label = Label(FONT_0, text="grams", color=clue.BLUE)
|
|
||||||
chan_1_label.anchor_point = (1.0, 0)
|
|
||||||
chan_1_label.anchored_position = (80, 216)
|
|
||||||
scale_group.append(chan_1_label)
|
|
||||||
|
|
||||||
chan_2_label = Label(FONT_0, text="grams", color=clue.BLUE)
|
|
||||||
chan_2_label.anchor_point = (1.0, 0)
|
|
||||||
chan_2_label.anchored_position = (230, 216)
|
|
||||||
scale_group.append(chan_2_label)
|
|
||||||
|
|
||||||
chan_1_value = Label(FONT_0, text="0.0", color=clue.WHITE)
|
|
||||||
chan_1_value.anchor_point = (1.0, 0.5)
|
|
||||||
chan_1_value.anchored_position = (80, 200)
|
|
||||||
scale_group.append(chan_1_value)
|
|
||||||
|
|
||||||
chan_2_value = Label(FONT_0, text="0.0", color=clue.WHITE)
|
|
||||||
chan_2_value.anchor_point = (1.0, 0.5)
|
|
||||||
chan_2_value.anchored_position = (230, 200)
|
|
||||||
scale_group.append(chan_2_value)
|
|
||||||
|
|
||||||
# Define moveable bubble
|
|
||||||
indicator_group = displayio.Group()
|
|
||||||
chan_1_bubble = Circle(112, 200, 8, fill=clue.YELLOW, outline=clue.YELLOW, stroke=3)
|
|
||||||
indicator_group.append(chan_1_bubble)
|
|
||||||
|
|
||||||
chan_2_bubble = Circle(131, 200, 8, fill=clue.GREEN, outline=clue.GREEN, stroke=3)
|
|
||||||
indicator_group.append(chan_2_bubble)
|
|
||||||
|
|
||||||
scale_group.append(indicator_group)
|
|
||||||
display.show(scale_group)
|
|
||||||
|
|
||||||
|
|
||||||
def zero_channel():
|
|
||||||
# Initiate internal calibration for current channel
|
|
||||||
# Use when scale is started, a new channel is selected, or to adjust for measurement drift
|
|
||||||
# Remove weight and tare from load cell before executing
|
|
||||||
print(
|
|
||||||
"channel %1d calibrate.INTERNAL: %5s"
|
|
||||||
% (nau7802.channel, nau7802.calibrate("INTERNAL"))
|
|
||||||
)
|
|
||||||
print(
|
|
||||||
"channel %1d calibrate.OFFSET: %5s"
|
|
||||||
% (nau7802.channel, nau7802.calibrate("OFFSET"))
|
|
||||||
)
|
|
||||||
print("...channel zeroed")
|
|
||||||
|
|
||||||
|
|
||||||
def read(samples=100):
|
|
||||||
# Read and average consecutive raw sample values; return average raw 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)
|
|
||||||
|
|
||||||
|
|
||||||
# Instantiate and calibrate load cell inputs
|
|
||||||
print("*** Instantiate and calibrate load cells")
|
|
||||||
clue.pixel[0] = (16, 16, 0) # Set status indicator to yellow
|
|
||||||
print(" enable NAU7802 digital and analog power: %5s" % (nau7802.enable(True)))
|
|
||||||
|
|
||||||
nau7802.gain = DEFAULT_GAIN # Use default gain
|
|
||||||
nau7802.channel = 1 # Set to second channel
|
|
||||||
zero_channel() # Re-calibrate and zero
|
|
||||||
nau7802.channel = 2 # Set to first channel
|
|
||||||
zero_channel() # Re-calibrate and zero
|
|
||||||
|
|
||||||
clue.pixel[0] = (0, 16, 0) # Set status indicator to green
|
|
||||||
clue.play_tone(1660, 0.15)
|
|
||||||
clue.play_tone(1440, 0.15)
|
|
||||||
|
|
||||||
### Main loop: Read sample, move bubble, and display values
|
|
||||||
while True:
|
|
||||||
nau7802.channel = 1
|
|
||||||
value = read(SAMPLE_AVG)
|
|
||||||
chan_1_mass_gr = round(value * CALIB_RATIO_1, 1)
|
|
||||||
chan_1_mass_oz = round(chan_1_mass_gr * 0.03527, 2)
|
|
||||||
chan_1_value.text = "%5.1f" % (chan_1_mass_gr)
|
|
||||||
|
|
||||||
chan_1_bubble.y = int(map_range(chan_1_mass_gr, MIN_GR, MAX_GR, 240, 0)) - 8
|
|
||||||
if chan_1_mass_gr > MAX_GR or chan_1_mass_gr < MIN_GR:
|
|
||||||
chan_1_bubble.fill = clue.RED
|
|
||||||
else:
|
|
||||||
chan_1_bubble.fill = None
|
|
||||||
|
|
||||||
nau7802.channel = 2
|
|
||||||
value = read(SAMPLE_AVG)
|
|
||||||
chan_2_mass_gr = round(value * CALIB_RATIO_2, 1)
|
|
||||||
chan_2_mass_oz = round(chan_2_mass_gr * 0.03527, 2)
|
|
||||||
chan_2_value.text = "%5.1f" % (chan_2_mass_gr)
|
|
||||||
|
|
||||||
chan_2_bubble.y = int(map_range(chan_2_mass_gr, MIN_GR, MAX_GR, 240, 0)) - 8
|
|
||||||
if chan_2_mass_gr > MAX_GR or chan_2_mass_gr < MIN_GR:
|
|
||||||
chan_2_bubble.fill = clue.RED
|
|
||||||
else:
|
|
||||||
chan_2_bubble.fill = None
|
|
||||||
|
|
||||||
print("(%+5.1f, %+5.1f)" % (chan_1_mass_gr, chan_2_mass_gr))
|
|
||||||
|
|
||||||
if clue.button_a:
|
|
||||||
# Zero and recalibrate channel 1
|
|
||||||
clue.play_tone(1660, 0.3)
|
|
||||||
clue.pixel[0] = (16, 0, 0)
|
|
||||||
chan_1_bubble.fill = clue.RED
|
|
||||||
nau7802.channel = 1
|
|
||||||
zero_channel()
|
|
||||||
while clue.button_a:
|
|
||||||
time.sleep(0.1)
|
|
||||||
chan_1_bubble.fill = None
|
|
||||||
clue.pixel[0] = (0, 16, 0)
|
|
||||||
clue.play_tone(1440, 0.5)
|
|
||||||
|
|
||||||
if clue.button_b:
|
|
||||||
# Zero and recalibrate channel 2
|
|
||||||
clue.play_tone(1660, 0.3)
|
|
||||||
clue.pixel[0] = (16, 0, 0)
|
|
||||||
chan_2_bubble.fill = clue.RED
|
|
||||||
nau7802.channel = 2
|
|
||||||
zero_channel()
|
|
||||||
while clue.button_b:
|
|
||||||
time.sleep(0.1)
|
|
||||||
chan_2_bubble.fill = None
|
|
||||||
clue.pixel[0] = (0, 16, 0)
|
|
||||||
clue.play_tone(1440, 0.5)
|
|
||||||
96
examples/dual_clue_scale_calibrator.py
Executable file
96
examples/dual_clue_scale_calibrator.py
Executable file
|
|
@ -0,0 +1,96 @@
|
||||||
|
# SPDX-FileCopyrightText: 2021, 2022 Cedar Grove Maker Studios
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
#
|
||||||
|
# dual_clue_scale_calibrator.py
|
||||||
|
# 2022-07-27 v1.1.0
|
||||||
|
#
|
||||||
|
# Clue Scale Calibrator - Dual Channel Version
|
||||||
|
# Cedar Grove NAU7802 FeatherWing example
|
||||||
|
|
||||||
|
import time
|
||||||
|
import board
|
||||||
|
from adafruit_clue import clue
|
||||||
|
from cedargrove_nau7802 import NAU7802
|
||||||
|
|
||||||
|
clue.pixel[0] = 0x202000 # Set status indicator to yellow (initializing)
|
||||||
|
|
||||||
|
SAMPLE_AVG = 1000 # Number of sample values to average
|
||||||
|
DEFAULT_GAIN = 128 # Default gain for internal PGA
|
||||||
|
|
||||||
|
# Instantiate 24-bit load sensor ADC
|
||||||
|
nau7802 = NAU7802(board.I2C(), address=0x2A, active_channels=2)
|
||||||
|
|
||||||
|
|
||||||
|
def zero_channel():
|
||||||
|
"""Initiate internal calibration 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."""
|
||||||
|
nau7802.calibrate("INTERNAL")
|
||||||
|
nau7802.calibrate("OFFSET")
|
||||||
|
|
||||||
|
|
||||||
|
def read(samples=100):
|
||||||
|
# Read and average consecutive raw sample values; return average raw 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 NAU780 internal analog circuitry, set gain, and calibrate/zero
|
||||||
|
nau7802.enable(True)
|
||||||
|
nau7802.gain = DEFAULT_GAIN # Use default gain
|
||||||
|
nau7802.channel = 1
|
||||||
|
zero_channel() # Calibrate and zero
|
||||||
|
nau7802.channel = 2
|
||||||
|
zero_channel() # Calibrate and zero
|
||||||
|
|
||||||
|
print("-----------------------------------")
|
||||||
|
print(" NAU7802 DUAL CHANNEL CALIBRATOR")
|
||||||
|
print("-----------------------------------")
|
||||||
|
print("Place a calibration weight on each")
|
||||||
|
print("load cell.")
|
||||||
|
print("To re-zero the load cells, remove")
|
||||||
|
print("any weights then press and hold A.")
|
||||||
|
print("-----------------------------------")
|
||||||
|
print("")
|
||||||
|
|
||||||
|
# Play "welcome" tones
|
||||||
|
clue.play_tone(1660, 0.15)
|
||||||
|
clue.play_tone(1440, 0.15)
|
||||||
|
|
||||||
|
# Main loop: Read samples and display values
|
||||||
|
while True:
|
||||||
|
clue.pixel[0] = 0x002000 # Set status indicator to green
|
||||||
|
|
||||||
|
# Read the raw value; print raw value, gain setting, and % of full-scale
|
||||||
|
nau7802.channel = 1
|
||||||
|
value = read(SAMPLE_AVG)
|
||||||
|
print(f"CHAN_{nau7802.channel:1.0f} RAW VALUE: {value:7.0f}")
|
||||||
|
print(f"GAIN: x{DEFAULT_GAIN} full-scale: {(value / ((2**23) - 1)) * 100:3.2f}%")
|
||||||
|
print("===================================")
|
||||||
|
|
||||||
|
nau7802.channel = 2
|
||||||
|
value = read(SAMPLE_AVG)
|
||||||
|
print(f"CHAN_{nau7802.channel:1.0f} RAW VALUE: {value:7.0f}")
|
||||||
|
print(f"GAIN: x{DEFAULT_GAIN} full-scale: {(value / ((2**23) - 1)) * 100:3.2f}%")
|
||||||
|
print("===================================")
|
||||||
|
|
||||||
|
time.sleep(0.1)
|
||||||
|
|
||||||
|
if clue.button_a:
|
||||||
|
# Zero and recalibrate both channels
|
||||||
|
clue.play_tone(1660, 0.3) # Play "button pressed" tone
|
||||||
|
clue.pixel[0] = 0x200000 # Set status indicator to red (stopped)
|
||||||
|
nau7802.channel = 1
|
||||||
|
zero_channel()
|
||||||
|
nau7802.channel = 2
|
||||||
|
zero_channel()
|
||||||
|
while clue.button_a:
|
||||||
|
# Wait until button is released
|
||||||
|
time.sleep(0.1)
|
||||||
|
print("RECALIBRATED")
|
||||||
|
clue.play_tone(1440, 0.5) # Play "reset completed" tone
|
||||||
219
examples/dual_clue_scale_code.py
Executable file
219
examples/dual_clue_scale_code.py
Executable file
|
|
@ -0,0 +1,219 @@
|
||||||
|
# SPDX-FileCopyrightText: 2021, 2022 Cedar Grove Maker Studios
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
#
|
||||||
|
# dual_clue_scale_code.py
|
||||||
|
# 2022-07-27 v1.2.0
|
||||||
|
#
|
||||||
|
# Clue Scale - Dual Channel Version
|
||||||
|
# Cedar Grove NAU7802 FeatherWing example
|
||||||
|
|
||||||
|
# import dual_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[0] = 0x202000 # Set status indicator to yellow (initializing)
|
||||||
|
|
||||||
|
MAX_GR = 100 # Maximum (full-scale) display range in grams
|
||||||
|
DEFAULT_GAIN = 128 # Default gain for internal PGA
|
||||||
|
SAMPLE_AVG = 100 # Number of sample values to average
|
||||||
|
CHAN_1_LABEL = "SHOT" # 6 characters maximum
|
||||||
|
CHAN_2_LABEL = "BEANS" # 6 characters maximum
|
||||||
|
|
||||||
|
min_gr = (MAX_GR // 5) * -1 # Calculated minimum display value
|
||||||
|
|
||||||
|
"""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 ounces or 64.3 grams."""
|
||||||
|
CALIB_RATIO_1 = 100 / 215300 # load cell serial#4540-01 attached to chan 1
|
||||||
|
CALIB_RATIO_2 = 100 / 215300 # load cell serial#4540-02 attached to chan 2
|
||||||
|
|
||||||
|
# Instantiate 24-bit load sensor ADC
|
||||||
|
nau7802 = NAU7802(board.I2C(), address=0x2A, active_channels=2)
|
||||||
|
|
||||||
|
# Instantiate display and fonts
|
||||||
|
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")
|
||||||
|
|
||||||
|
# Define displayio background and group elements
|
||||||
|
bkg = displayio.OnDiskBitmap("/clue_scale_bkg.bmp")
|
||||||
|
_background = displayio.TileGrid(bkg, pixel_shader=bkg.pixel_shader, x=0, y=0)
|
||||||
|
scale_group.append(_background)
|
||||||
|
|
||||||
|
chan_1_name = Label(FONT_1, text=CHAN_1_LABEL, color=clue.CYAN)
|
||||||
|
chan_1_name.anchor_point = (0.5, 0.5)
|
||||||
|
chan_1_name.anchored_position = (40, 96)
|
||||||
|
scale_group.append(chan_1_name)
|
||||||
|
|
||||||
|
chan_2_name = Label(FONT_1, text=CHAN_2_LABEL, color=clue.CYAN)
|
||||||
|
chan_2_name.anchor_point = (0.5, 0.5)
|
||||||
|
chan_2_name.anchored_position = (199, 96)
|
||||||
|
scale_group.append(chan_2_name)
|
||||||
|
|
||||||
|
# Define the graphics for the zeroing buttons
|
||||||
|
zero_1_button_circle = Circle(14, 152, 14, fill=None, outline=clue.RED, stroke=2)
|
||||||
|
scale_group.append(zero_1_button_circle)
|
||||||
|
|
||||||
|
zero_1_button_label = Label(FONT_1, text="Z", color=clue.RED)
|
||||||
|
zero_1_button_label.x = 8
|
||||||
|
zero_1_button_label.y = 150
|
||||||
|
scale_group.append(zero_1_button_label)
|
||||||
|
|
||||||
|
zero_2_button_circle = Circle(225, 152, 14, fill=None, outline=clue.RED, stroke=2)
|
||||||
|
scale_group.append(zero_2_button_circle)
|
||||||
|
|
||||||
|
zero_2_button_label = Label(FONT_1, text="Z", color=clue.RED)
|
||||||
|
zero_2_button_label.x = 219
|
||||||
|
zero_2_button_label.y = 150
|
||||||
|
scale_group.append(zero_2_button_label)
|
||||||
|
|
||||||
|
# Place tickmarks 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 weight units and values near the bottom of the display
|
||||||
|
chan_1_name = Label(FONT_0, text="grams", color=clue.BLUE)
|
||||||
|
chan_1_name.anchor_point = (1.0, 0)
|
||||||
|
chan_1_name.anchored_position = (80, 216)
|
||||||
|
scale_group.append(chan_1_name)
|
||||||
|
|
||||||
|
chan_2_name = Label(FONT_0, text="grams", color=clue.BLUE)
|
||||||
|
chan_2_name.anchor_point = (1.0, 0)
|
||||||
|
chan_2_name.anchored_position = (230, 216)
|
||||||
|
scale_group.append(chan_2_name)
|
||||||
|
|
||||||
|
chan_1_value = Label(FONT_0, text="0.0", color=clue.WHITE)
|
||||||
|
chan_1_value.anchor_point = (1.0, 0.5)
|
||||||
|
chan_1_value.anchored_position = (80, 200)
|
||||||
|
scale_group.append(chan_1_value)
|
||||||
|
|
||||||
|
chan_2_value = Label(FONT_0, text="0.0", color=clue.WHITE)
|
||||||
|
chan_2_value.anchor_point = (1.0, 0.5)
|
||||||
|
chan_2_value.anchored_position = (230, 200)
|
||||||
|
scale_group.append(chan_2_value)
|
||||||
|
|
||||||
|
# Define the moveable indicator bubbles
|
||||||
|
indicator_group = displayio.Group()
|
||||||
|
chan_1_bubble = Circle(112, 200, 8, fill=clue.YELLOW, outline=clue.YELLOW, stroke=3)
|
||||||
|
indicator_group.append(chan_1_bubble)
|
||||||
|
|
||||||
|
chan_2_bubble = Circle(131, 200, 8, fill=clue.GREEN, outline=clue.GREEN, stroke=3)
|
||||||
|
indicator_group.append(chan_2_bubble)
|
||||||
|
|
||||||
|
scale_group.append(indicator_group)
|
||||||
|
display.show(scale_group)
|
||||||
|
|
||||||
|
|
||||||
|
def zero_channel():
|
||||||
|
"""Initiate internal calibration 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."""
|
||||||
|
nau7802.calibrate("INTERNAL")
|
||||||
|
nau7802.calibrate("OFFSET")
|
||||||
|
|
||||||
|
|
||||||
|
def read(samples=100):
|
||||||
|
# Read and average consecutive raw sample values; return average raw 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 NAU780 internal analog circuitry, set gain, and calibrate/zero
|
||||||
|
nau7802.enable(True)
|
||||||
|
nau7802.gain = DEFAULT_GAIN
|
||||||
|
nau7802.channel = 1
|
||||||
|
zero_channel()
|
||||||
|
nau7802.channel = 2
|
||||||
|
zero_channel()
|
||||||
|
|
||||||
|
# Play "welcome" tones
|
||||||
|
clue.play_tone(1660, 0.15)
|
||||||
|
clue.play_tone(1440, 0.15)
|
||||||
|
|
||||||
|
# Main loop: Read samples, move bubbles, and display values
|
||||||
|
while True:
|
||||||
|
clue.pixel[0] = 0x002000 # Set status indicator to green (ready)
|
||||||
|
|
||||||
|
nau7802.channel = 1
|
||||||
|
value = read(SAMPLE_AVG)
|
||||||
|
chan_1_mass_gr = round(value * CALIB_RATIO_1, 1)
|
||||||
|
chan_1_mass_oz = round(chan_1_mass_gr * 0.03527, 2)
|
||||||
|
chan_1_value.text = f"{chan_1_mass_gr:5.1f}"
|
||||||
|
|
||||||
|
chan_1_bubble.y = int(map_range(chan_1_mass_gr, min_gr, MAX_GR, 240, 0)) - 8
|
||||||
|
if chan_1_mass_gr > MAX_GR or chan_1_mass_gr < min_gr:
|
||||||
|
chan_1_bubble.fill = clue.RED
|
||||||
|
else:
|
||||||
|
chan_1_bubble.fill = None
|
||||||
|
|
||||||
|
nau7802.channel = 2
|
||||||
|
value = read(SAMPLE_AVG)
|
||||||
|
chan_2_mass_gr = round(value * CALIB_RATIO_2, 1)
|
||||||
|
chan_2_mass_oz = round(chan_2_mass_gr * 0.03527, 2)
|
||||||
|
chan_2_value.text = f"{chan_2_mass_gr:5.1f}"
|
||||||
|
|
||||||
|
chan_2_bubble.y = int(map_range(chan_2_mass_gr, min_gr, MAX_GR, 240, 0)) - 8
|
||||||
|
if chan_2_mass_gr > MAX_GR or chan_2_mass_gr < min_gr:
|
||||||
|
chan_2_bubble.fill = clue.RED
|
||||||
|
else:
|
||||||
|
chan_2_bubble.fill = None
|
||||||
|
|
||||||
|
print(f"chan_1:{chan_1_mass_gr:5.1f} gr chan_2:{chan_2_mass_gr:5.1f} gr")
|
||||||
|
|
||||||
|
if clue.button_a:
|
||||||
|
# Zero and recalibrate channel 1
|
||||||
|
clue.pixel[0] = 0x200000 # Set status indicator to red (stopped)
|
||||||
|
chan_1_bubble.fill = clue.RED
|
||||||
|
clue.play_tone(1660, 0.3) # Play "button pressed" tone
|
||||||
|
|
||||||
|
nau7802.channel = 1
|
||||||
|
zero_channel()
|
||||||
|
|
||||||
|
while clue.button_a:
|
||||||
|
# Wait until button is released
|
||||||
|
time.sleep(0.1)
|
||||||
|
|
||||||
|
clue.play_tone(1440, 0.5) # Play "reset completed" tone
|
||||||
|
chan_1_bubble.fill = None
|
||||||
|
|
||||||
|
if clue.button_b:
|
||||||
|
# Zero and recalibrate channel 2
|
||||||
|
clue.pixel[0] = 0x200000 # Set status indicator to red (stopped)
|
||||||
|
chan_2_bubble.fill = clue.RED
|
||||||
|
clue.play_tone(1660, 0.3) # Play "button pressed" tone
|
||||||
|
|
||||||
|
nau7802.channel = 2
|
||||||
|
zero_channel()
|
||||||
|
|
||||||
|
while clue.button_a:
|
||||||
|
# Wait until button is released
|
||||||
|
time.sleep(0.1)
|
||||||
|
|
||||||
|
clue.play_tone(1440, 0.5) # Play "reset completed" tone
|
||||||
|
chan_2_bubble.fill = None
|
||||||
Loading…
Reference in a new issue