|
| 1 | +# SPDX-FileCopyrightText: 2022 Jan Goolsbey for Adafruit Industries |
| 2 | +# SPDX-License-Identifier: MIT |
| 3 | +# |
| 4 | +# clue_scale_code.py |
| 5 | +# 2022-07-29 v1.2.0 |
| 6 | +# |
| 7 | +# Clue Scale - Single Channel Version |
| 8 | +# Adafruit NAU7802 Stemma breakout example |
| 9 | + |
| 10 | +# import clue_scale_calibrator # Uncomment to run calibrator method |
| 11 | + |
| 12 | +import time |
| 13 | +import board |
| 14 | +from simpleio import map_range |
| 15 | +from adafruit_clue import clue |
| 16 | +from adafruit_display_shapes.circle import Circle |
| 17 | +from adafruit_display_text.label import Label |
| 18 | +from adafruit_bitmap_font import bitmap_font |
| 19 | +import displayio |
| 20 | +from cedargrove_nau7802 import NAU7802 |
| 21 | + |
| 22 | +clue.pixel.brightness = 0.2 # Set NeoPixel brightness |
| 23 | +clue.pixel[0] = clue.YELLOW # Set status indicator to yellow (initializing) |
| 24 | + |
| 25 | +# Set Scale Defaults |
| 26 | +MAX_GR = 100 # Maximum (full-scale) display range in grams |
| 27 | +DEFAULT_GAIN = 128 # Default gain for internal PGA |
| 28 | +SAMPLE_AVG = 100 # Number of sample values to average |
| 29 | +SCALE_NAME_1 = "COFFEE" # 6 characters maximum |
| 30 | +SCALE_NAME_2 = "SCALE" # 6 characters maximum |
| 31 | + |
| 32 | +"""Enter the calibration ratio for the individual load cell in-use. The ratio is |
| 33 | +composed of the reference weight in grams divided by the raw reading. For |
| 34 | +example, a raw reading of 215300 for a 100 gram weight results in a calibration |
| 35 | +ratio of 100 / 215300. Use the clue_scale_single_calibrate method to obtain the |
| 36 | +raw value. |
| 37 | +FYI: A US dime coin weighs 2.268 grams or 0.079 ounces.""" |
| 38 | +CALIB_RATIO = 100 / 215300 # load cell serial#4540-02 |
| 39 | + |
| 40 | +# Instantiate the Sensor and Display |
| 41 | +nau7802 = NAU7802(board.I2C(), address=0x2A, active_channels=1) |
| 42 | + |
| 43 | +display = board.DISPLAY |
| 44 | +scale_group = displayio.Group() |
| 45 | + |
| 46 | +FONT_0 = bitmap_font.load_font("/fonts/Helvetica-Bold-24.bdf") |
| 47 | +FONT_1 = bitmap_font.load_font("/fonts/OpenSans-16.bdf") |
| 48 | +FONT_2 = bitmap_font.load_font("/fonts/OpenSans-9.bdf") |
| 49 | + |
| 50 | +# Display the Background Bitmap Image |
| 51 | +bkg = displayio.OnDiskBitmap("/clue_scale_bkg.bmp") |
| 52 | +_background = displayio.TileGrid(bkg, pixel_shader=bkg.pixel_shader, x=0, y=0) |
| 53 | +scale_group.append(_background) |
| 54 | + |
| 55 | +# Define and Display the Text Labels and Graphic Elements |
| 56 | +# Place the project name on either side of the graduated scale |
| 57 | +scale_name_1 = Label(FONT_1, text=SCALE_NAME_1, color=clue.CYAN) |
| 58 | +scale_name_1.anchor_point = (0.5, 0.5) |
| 59 | +scale_name_1.anchored_position = (40, 96) |
| 60 | +scale_group.append(scale_name_1) |
| 61 | + |
| 62 | +scale_name_2 = Label(FONT_1, text=SCALE_NAME_2, color=clue.CYAN) |
| 63 | +scale_name_2.anchor_point = (0.5, 0.5) |
| 64 | +scale_name_2.anchored_position = (199, 96) |
| 65 | +scale_group.append(scale_name_2) |
| 66 | + |
| 67 | +# Define the zeroing button graphic |
| 68 | +zero_button_circle = Circle(14, 152, 14, fill=None, outline=clue.RED, stroke=2) |
| 69 | +scale_group.append(zero_button_circle) |
| 70 | + |
| 71 | +zero_button_label = Label(FONT_1, text="Z", color=clue.RED) |
| 72 | +zero_button_label.x = 8 |
| 73 | +zero_button_label.y = 150 |
| 74 | +scale_group.append(zero_button_label) |
| 75 | + |
| 76 | +# Place tickmark labels next to the graduated scale |
| 77 | +for i in range(-1, 6): |
| 78 | + tick_value = Label(FONT_2, text=str((MAX_GR) // 5 * i), color=clue.CYAN) |
| 79 | + if i == -1: |
| 80 | + tick_value.anchor_point = (1.0, 1.1) |
| 81 | + elif i == 5: |
| 82 | + tick_value.anchor_point = (1.0, 0.0) |
| 83 | + else: |
| 84 | + tick_value.anchor_point = (1.0, 0.5) |
| 85 | + tick_value.anchored_position = (99, 201 - (i * 40)) |
| 86 | + scale_group.append(tick_value) |
| 87 | + |
| 88 | +# Place the grams and ounces labels and values near the bottom of the display |
| 89 | +grams_label = Label(FONT_0, text="grams", color=clue.BLUE) |
| 90 | +grams_label.anchor_point = (1.0, 0) |
| 91 | +grams_label.anchored_position = (80, 216) |
| 92 | +scale_group.append(grams_label) |
| 93 | + |
| 94 | +ounces_label = Label(FONT_0, text="ounces", color=clue.BLUE) |
| 95 | +ounces_label.anchor_point = (1.0, 0) |
| 96 | +ounces_label.anchored_position = (230, 216) |
| 97 | +scale_group.append(ounces_label) |
| 98 | + |
| 99 | +grams_value = Label(FONT_0, text="0.0", color=clue.WHITE) |
| 100 | +grams_value.anchor_point = (1.0, 0.5) |
| 101 | +grams_value.anchored_position = (80, 200) |
| 102 | +scale_group.append(grams_value) |
| 103 | + |
| 104 | +ounces_value = Label(FONT_0, text="0.00", color=clue.WHITE) |
| 105 | +ounces_value.anchor_point = (1.0, 0.5) |
| 106 | +ounces_value.anchored_position = (230, 200) |
| 107 | +scale_group.append(ounces_value) |
| 108 | + |
| 109 | +# Define the moveable indicator bubble |
| 110 | +indicator_group = displayio.Group() |
| 111 | +bubble = Circle(120, 200, 10, fill=clue.YELLOW, outline=clue.YELLOW, stroke=3) |
| 112 | +indicator_group.append(bubble) |
| 113 | + |
| 114 | +scale_group.append(indicator_group) |
| 115 | +display.show(scale_group) |
| 116 | + |
| 117 | + |
| 118 | +# Helpers |
| 119 | +def zero_channel(): |
| 120 | + """Prepare internal amplifier settings and zero the current channel. Use |
| 121 | + after power-up, a new channel is selected, or to adjust for measurement |
| 122 | + drift. Can be used to zero the scale with a tare weight. |
| 123 | + The nau7802.calibrate function used here does not calibrate the load cell, |
| 124 | + but sets the NAU7802 internals to prepare for measuring input signals.""" |
| 125 | + nau7802.calibrate("INTERNAL") |
| 126 | + nau7802.calibrate("OFFSET") |
| 127 | + |
| 128 | + |
| 129 | +def read(samples=100): |
| 130 | + """Read and average consecutive raw samples; return averaged value.""" |
| 131 | + sample_sum = 0 |
| 132 | + sample_count = samples |
| 133 | + while sample_count > 0: |
| 134 | + if nau7802.available: |
| 135 | + sample_sum = sample_sum + nau7802.read() |
| 136 | + sample_count -= 1 |
| 137 | + return int(sample_sum / samples) |
| 138 | + |
| 139 | + |
| 140 | +# Activate the Sensor |
| 141 | +# Enable the internal analog circuitry, set gain, and zero |
| 142 | +nau7802.enable(True) |
| 143 | +nau7802.gain = DEFAULT_GAIN |
| 144 | +zero_channel() |
| 145 | + |
| 146 | +# Play "welcome" tones |
| 147 | +clue.play_tone(1660, 0.15) |
| 148 | +clue.play_tone(1440, 0.15) |
| 149 | + |
| 150 | +# The Primary Code Loop |
| 151 | +# Read sensor, move bubble, and display values |
| 152 | +while True: |
| 153 | + clue.pixel[0] = clue.GREEN # Set status indicator to green (ready) |
| 154 | + |
| 155 | + # Read the raw scale value and scale for grams and ounces |
| 156 | + value = read(SAMPLE_AVG) |
| 157 | + mass_grams = round(value * CALIB_RATIO, 1) |
| 158 | + mass_ounces = round(mass_grams * 0.03527, 2) |
| 159 | + grams_value.text = f"{mass_grams:5.1f}" |
| 160 | + ounces_value.text = f"{mass_ounces:5.2f}" |
| 161 | + print(f" {mass_grams:5.1f} grams {mass_ounces:5.2f} ounces") |
| 162 | + |
| 163 | + # Reposition the indicator bubble based on grams value |
| 164 | + min_gr = (MAX_GR // 5) * -1 # Minimum display value |
| 165 | + bubble.y = int(map_range(mass_grams, min_gr, MAX_GR, 240, 0)) - 10 |
| 166 | + if mass_grams > MAX_GR or mass_grams < min_gr: |
| 167 | + bubble.fill = clue.RED |
| 168 | + else: |
| 169 | + bubble.fill = None |
| 170 | + |
| 171 | + # Check to see if the zeroing button is pressed |
| 172 | + if clue.button_a: |
| 173 | + # Zero the sensor |
| 174 | + clue.pixel[0] = clue.RED # Set status indicator to red (stopped) |
| 175 | + bubble.fill = clue.RED # Set bubble center to red (stopped) |
| 176 | + clue.play_tone(1660, 0.3) # Play "button pressed" tone |
| 177 | + |
| 178 | + zero_channel() |
| 179 | + |
| 180 | + while clue.button_a: |
| 181 | + # Wait until the button is released |
| 182 | + time.sleep(0.1) |
| 183 | + |
| 184 | + clue.play_tone(1440, 0.5) # Play "reset completed" tone |
| 185 | + bubble.fill = None # Set bubble center to transparent (ready) |
0 commit comments