Hardware components | ||||||
![]() |
| × | 1 | |||
| × | 2 | ||||
| × | 1 | ||||
| × | 1 | ||||
Hand tools and fabrication machines | ||||||
![]() |
|
Due to supply chain issues, this project is not completedandincludesonlysoftwarecomponents.
Motivation:I love small candy like M&M's and Skittles and I wanted to create a device that can hold a lot of the candy and dispense it. I also saw a project from a previous student that dispenses candy and plays music. Using that project as my an example, I wanted to create my own version that, while it doesn't play music, has a more accurate display for the amount of candy in the device.
Overview:The candy dispenser is activated when you wave your hand in the device and break the IR beam. When you break the beam, you can load your candy into the dispenser. As the candy is loaded, it passes through another IR beam that counts how much candy is in the device. This number is displayed on the hex display and, when all the candy is passed through, the servo motor turns and dispenses some candy.
Electronics and Hardware:
There are 4 main electronic hardware components in this project: a PocketBeagle, 2 IR break beam sensors, a 360-degree servo motor, and an i2c hex display. The IR break beam sensors are connected to GPIOs and read a 1 when connected and 0 when broken. The servo is connected to pwm pins and takes a signal for movement. The i2c hex display is connected to a GPIO that is set to out. All of these components are connected to the PocketBeagle with jumper wires.
Future Plans:
- Build physical prototype once hardware parts are available
- Adjust the amount code based on hardware tolerances to improve accuracy
- Implement force-sensitive resistors to assist amount display accuracy
#!/bin/bash
"""
--------------------------------------------------------------------------
Candy Dispenser Run Shell Script
--------------------------------------------------------------------------
License:
Copyright 2022 - Anas Yousaf
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.
--------------------------------------------------------------------------
Navigate to project directory and configure pins to the right functions.
Then, run the Candy.py script.
"""
cd /var/lib/cloud9/ENGI301/Project_1
config-pin P2_1 pwm
config-pin P2_3 pwm
config-pin P2_9 i2c
config-pin P2_11 i2c
config-pin P2_35 in
config-pin P2_2 in
config-pin P2_4 in
python Candy.py
# -*- coding: utf-8 -*-
"""
--------------------------------------------------------------------------
Candy Dispenser
--------------------------------------------------------------------------
License:
Copyright 2022 - Anas Yousaf
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.
--------------------------------------------------------------------------
Candy Dispenser that will
- Use an ir beam to detect when a hand is waved
- Rotate a servo to dispense candy when the ir beam is broken
- Calculate and display the number of candy using an ir beam
--------------------------------------------------------------------------
"""
"""
Sets the path to find the python file ht16k33. May need to change depending on
where ht16k33 is located.
"""
import sys
sys.path.append("/var/lib/cloud9/ENGI301/Project_1")
import time
import math
import random
import Adafruit_BBIO.GPIO as GPIO
import Adafruit_BBIO.PWM as PWM
import ht16k33 as HT16K33
# ------------------------------------------------------------------------
# Global variables
# ------------------------------------------------------------------------
servo_pin = "P2_1"
FSR_pin = "AIN5"
ir1_pin = "P2_2"
ir2_pin = "P2_4"
# ------------------------------------------------------------------------
# Main Tasks
# ------------------------------------------------------------------------
def setup():
"""Set up hardware components."""
GPIO.setup(ir1_pin, GPIO.IN)
GPIO.setup(ir2_pin, GPIO.IN)
HT16K33.setup()
HT16K33.clear()
# end def
def open_door():
"""Makes servo open the dispensing door and close it again."""
PWM.start(servo_pin, (100), 20.0)
PWM.set_duty_cycle(servo_pin, 1.5)
time.sleep(2.0)
PWM.start(servo_pin, (100), 20.0)
PWM.set_duty_cycle(servo_pin, 3.5)
time.sleep(1.0)
PWM.stop(servo_pin)
PWM.cleanup()
# end def
def amount_calc():
"""Calculates the amount of candy in the candy dispenser by assuming one
piece of candy falls every second and subtracting the amount of time the ir
sensor is open with the time at the start of the function."""
""" WORK IN PROGRESS DUE TO UNAVAILABLE HARDWARE."""
initial_time = time.time()
count = int()
while GPIO.input("P2_3") == 0:
count = count + (initial_time+10 - initial_time)
return count
# end def
# ------------------------------------------------------------------------
# Main script
# ------------------------------------------------------------------------
if __name__ == '__main__':
setup()
amount = amount_calc()
while True:
HT16K33.update(amount)
if GPIO.input("P2_2") == 0:
open_door()
time.sleep(5)
else:
pass
time.sleep(1)
# -*- coding: utf-8 -*-
"""
--------------------------------------------------------------------------
HT16K33 I2C Library
--------------------------------------------------------------------------
License:
Copyright 2018-2022 Erik Welsh
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.
--------------------------------------------------------------------------
Software API:
HT16K33(bus, address=0x70)
- Provide i2c bus that dispaly is on
- Provide i2c address for the display
clear()
- Sets value of display to "0000"
blank()
- Turns off all LEDs on display
set_colon(enable)
- Turns on / off the colon on the display. Enable must be True/False.
update(value)
- Update the value on the display. Value must be between 0 and 9999.
text(value)
- Update the value on the display with text.
The following characters are supported:
"abcdefghijlnopqrstuyABCDEFGHIJLNOPQRSTUY? -"
--------------------------------------------------------------------------
Background Information:
* Using seven-segment digit LED display for Adafruit's HT16K33 I2C backpack:
* http://adafruit.com/products/878
* https://learn.adafruit.com/assets/36420
* https://cdn-shop.adafruit.com/datasheets/ht16K33v110.pdf
* Base code (adapted below):
* https://github.com/emcconville/HT16K33/blob/master/FourDigit.py
* https://github.com/emcconville/HT16K33/blob/master/_HT16K33.py
* https://github.com/adafruit/Adafruit_Python_LED_Backpack/blob/master/Adafruit_LED_Backpack/HT16K33.py
* https://github.com/adafruit/Adafruit_Python_LED_Backpack/blob/master/Adafruit_LED_Backpack/SevenSegment.py
* https://github.com/adafruit/Adafruit_Python_LED_Backpack/blob/master/examples/sevensegment_test.py
* Letters Supported from:
* https://en.wikichip.org/wiki/seven-segment_display/representing_letters
"""
import os
# ------------------------------------------------------------------------
# Constants
# ------------------------------------------------------------------------
# See https://en.wikipedia.org/wiki/Seven-segment_display for reference
HEX_DIGITS = [0x3f, 0x06, 0x5b, 0x4f, # 0, 1, 2, 3
0x66, 0x6d, 0x7d, 0x07, # 4, 5, 6, 7
0x7f, 0x6f, 0x77, 0x7c, # 8, 9, A, b
0x39, 0x5e, 0x79, 0x71] # C, d, E, F
LETTERS = { "a" : 0x77, "A" : 0x77, # "A"
"b" : 0x7c, "B" : 0x7c, # "b"
"c" : 0x58, "C" : 0x39, # "c", "C"
"d" : 0x5e, "D" : 0x5e, # "d"
"e" : 0x79, "E" : 0x79, # "E"
"f" : 0x71, "F" : 0x71, # "F"
"g" : 0x6F, "G" : 0x6F, # "g"
"h" : 0x74, "H" : 0x76, # "h", "H"
"i" : 0x04, "I" : 0x30, # "i", "I"
"j" : 0x0e, "J" : 0x0e, # "J"
# Cannot be implemented "k" : None, "K" : None,
"l" : 0x38, "L" : 0x38, # "L"
# Cannot be implemented "m" : None, "M" : None,
"n" : 0x54, "N" : 0x54, # "n"
"o" : 0x5c, "O" : 0x3f, # "o", "O"
"p" : 0x73, "P" : 0x73, # "P"
"q" : 0x67, "Q" : 0x67, # "q"
"r" : 0x50, "R" : 0x50, # "r"
"s" : 0x6D, "S" : 0x6D, # "S"
"t" : 0x78, "T" : 0x78, # "t"
"u" : 0x1c, "U" : 0x3e, # "u", "U"
# Cannot be implemented "v" : None, "V" : None,
# Cannot be implemented "w" : None, "W" : None,
# Cannot be implemented "x" : None, "X" : None,
"y" : 0x6e, "Y" : 0x6e, # "y"
# Cannot be implemented "z" : None, "Z" : None,
" " : 0x00, # " "
"-" : 0x40, # "-"
"0" : 0x3f, # "0"
"1" : 0x06, # "1"
"2" : 0x5b, # "2"
"3" : 0x4f, # "3"
"4" : 0x66, # "4"
"5" : 0x6d, # "5"
"6" : 0x7d, # "6"
"7" : 0x07, # "7"
"8" : 0x7f, # "8"
"9" : 0x6f, # "9"
"?" : 0x53 # "?"
}
CLEAR_DIGIT = 0x7F
POINT_VALUE = 0x80
DIGIT_ADDR = [0x00, 0x02, 0x06, 0x08]
COLON_ADDR = 0x04
HT16K33_BLINK_CMD = 0x80
HT16K33_BLINK_DISPLAYON = 0x01
HT16K33_BLINK_OFF = 0x00
HT16K33_BLINK_2HZ = 0x02
HT16K33_BLINK_1HZ = 0x04
HT16K33_BLINK_HALFHZ = 0x06
HT16K33_SYSTEM_SETUP = 0x20
HT16K33_OSCILLATOR = 0x01
HT16K33_BRIGHTNESS_CMD = 0xE0
HT16K33_BRIGHTNESS_HIGHEST = 0x0F
HT16K33_BRIGHTNESS_DARKEST = 0x00
# ------------------------------------------------------------------------
# Functions / Classes
# ------------------------------------------------------------------------
class HT16K33():
""" Class to manage a HT16K33 I2C display """
bus = None
address = None
command = None
def __init__(self, bus, address=0x70, blink=HT16K33_BLINK_OFF, brightness=HT16K33_BRIGHTNESS_HIGHEST):
""" Initialize class variables; Set up display; Set display to blank """
# Initialize class variables
self.bus = bus
self.address = address
self.command = "/usr/sbin/i2cset -y {0} {1}".format(bus, address)
# Set up display
self.setup(blink=HT16K33_BLINK_OFF, brightness=HT16K33_BRIGHTNESS_HIGHEST)
# Set display to blank
self.blank()
# End def
def setup(self, blink, brightness):
"""Initialize the display itself"""
# i2cset -y 1 0x70 0x21
os.system("{0} {1}".format(self.command, (HT16K33_SYSTEM_SETUP | HT16K33_OSCILLATOR)))
# i2cset -y 1 0x70 0x81
os.system("{0} {1}".format(self.command, (HT16K33_BLINK_CMD | blink | HT16K33_BLINK_DISPLAYON)))
# i2cset -y 1 0x70 0xEF
os.system("{0} {1}".format(self.command, (HT16K33_BRIGHTNESS_CMD | brightness)))
# End def
def encode(self, data, double_point=False):
"""Encode data to TM1637 format.
This function will convert the data from decimal to the TM1637 data fromt
:param value: Value must be between 0 and 15
Will throw a ValueError if number is not between 0 and 15.
"""
ret_val = 0
try:
if (data != CLEAR_DIGIT):
if double_point:
ret_val = HEX_DIGITS[data] + POINT_VALUE
else:
ret_val = HEX_DIGITS[data]
except:
raise ValueError("Digit value must be between 0 and 15.")
return ret_val
# End def
def set_digit(self, digit_number, data, double_point=False):
"""Update the given digit of the display."""
os.system("{0} {1} {2}".format(self.command, DIGIT_ADDR[digit_number], self.encode(data, double_point)))
# End def
def set_digit_raw(self, digit_number, data, double_point=False):
"""Update the given digit of the display using raw data value"""
os.system("{0} {1} {2}".format(self.command, DIGIT_ADDR[digit_number], data))
# End def
def set_colon(self, enable):
"""Set the colon on the display."""
if enable:
os.system("{0} {1} {2}".format(self.command, COLON_ADDR, 0x02))
else:
os.system("{0} {1} {2}".format(self.command, COLON_ADDR, 0x00))
# End def
def blank(self):
"""Clear the display to read nothing"""
self.set_colon(False)
self.set_digit_raw(3, 0x00)
self.set_digit_raw(2, 0x00)
self.set_digit_raw(1, 0x00)
self.set_digit_raw(0, 0x00)
# End def
def clear(self):
"""Clear the display to read '0000'"""
self.set_colon(False)
self.set_digit(3, 0)
self.set_digit(2, 0)
self.set_digit(1, 0)
self.set_digit(0, 0)
# End def
def update(self, value):
"""Update the value on the display.
This function will clear the display and then set the appropriate digits
:param value: Value must be between 0 and 9999.
Will throw a ValueError if number is not between 0 and 9999.
"""
if ((value < 0) or (value > 9999)):
raise ValueError("Value is not between 0 and 9999")
self.set_digit(3, (value % 10))
self.set_digit(2, (value // 10) % 10)
self.set_digit(1, (value // 100) % 10)
self.set_digit(0, (value // 1000) % 10)
# End def
def text(self, value):
""" Update the value on the display with text
:param value: Value must have between 1 and 4 characters
Will throw a ValueError if there are not the appropriate number of
characters or if characters are used that are not supported.
"""
if ((len(value) < 1) or (len(value) > 4)):
raise ValueError("Must have between 1 and 4 characters")
# Clear the display
self.blank()
# Set the display to the correct characters
for i, char in enumerate(value):
try:
char_value = LETTERS[char]
self.set_digit_raw(i, char_value)
except:
raise ValueError("Character {0} not supported".format(char))
# End class
# ------------------------------------------------------------------------
# Main script
# ------------------------------------------------------------------------
if __name__ == '__main__':
import time
delay = 0.1
print("Test HT16K33 Display:")
display = HT16K33(1, 0x70)
for i in range(0, 10):
display.update(i)
time.sleep(delay)
for i in range(0, 100, 10):
display.update(i)
time.sleep(delay)
for i in range(0, 1000, 100):
display.update(i)
time.sleep(delay)
for i in range(0, 10000, 1000):
display.update(i)
time.sleep(delay)
for value in [0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x00]:
display.set_digit_raw(0, value)
time.sleep(delay)
# Test letters
letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ?"
for char in letters:
try:
display.text(char)
time.sleep(delay)
except:
print("Character not supported: {0}".format(char))
display.text("done")
time.sleep(1)
display.set_colon(True)
time.sleep(1)
display.clear()
print("Test Finished.")
Comments
Please log in or sign up to comment.