Beagle Board - beagleboard.org
Nicholas Lester
Published © GPL3+

IR Breakbeam Candy Dispenser with Zelda Music

Have significantly more fun eating candy than grabbing it out of a bag! Extra fun if you're a Zelda fan!

EasyFull instructions provided5 hours95
IR Breakbeam Candy Dispenser with Zelda Music

Things used in this project

Hardware components

PocketBeagle
BeagleBoard.org PocketBeagle
×1
Adafruit IR Break Beam Sensor - 3mm LEDs
×1
Adafruit Square Force-Sensitive Resistor (FSR) - Interlink 406
×1
Adafruit Standard servo - TowerPro SG-5010 - 5010
×1
Adafruit Large Enclosed Piezo Element w/Wires
×1
Male/Male Jumper Wires
×1
Male/Female Jumper Wires
Male/Female Jumper Wires
×1
Adafruit 0.56" 4-Digit 7-Segment Display w/I2C Backpack - Yellow
×1

Hand tools and fabrication machines

Epilog Fusion M2 Laser Cutter

Story

Read more

Custom parts and enclosures

Skittle Dispenser GitHub Repository

My AI files are also in my GitHub Repository for this Candy Dispenser project.

Candy Dispenser Top Part

This is the top half of the candy dispenser enclosure. Cut it out of 5.1 mm laser cutting wood.

Candy Dispenser Bottom Part

This is the bottom half of the candy dispenser. Cut it out of 5.1 mm laser cutting wood.

Candy Dispenser Ramps

These are the ramps inside the candy storage area. Cut them out of 5.1 mm laser cutting wood.

Schematics

Fritzing Diagram for Candy Dispenser

Skittle Dispenser GitHub Repository

My Fritzing file is also in my GitHub repository for this Candy Dispensing project.

Fritzing Diagram Image for Candy Dispenser

This is a screenshot of the fritzing diagram.
Skittle fritzing screenshot l95i9npwk9

Code

run_Skittle_Dispenser.sh

Plain text
The shell script to navigate to the proper directory, configure the pins, and run the main script. Run this code to use the project.
#!/bin/bash
"""
--------------------------------------------------------------------------
Skittle Dispenser Run Shell Script
--------------------------------------------------------------------------
License:   
Copyright 2018 Nicholas Lester

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.
--------------------------------------------------------------------------

Navigates to the proper directory and configures the pins to the proper
functions. Then, it runs the Skittle_Dispenser.py script.

"""
cd /var/lib/cloud9/ENGI301/midterm


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

python Skittle_Dispenser.py

Skittle_Dispenser.py

Python
This is the main program for the candy dispenser.
# -*- coding: utf-8 -*-
"""
--------------------------------------------------------------------------
Skittle Dispenser
--------------------------------------------------------------------------
License:   
Copyright 2018 Nicholas Lester

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.
--------------------------------------------------------------------------

Use a servo to detect a hand. Then, play a song with a piezo element. Finally,
use a servo to open the door and dispense Skittles.

Also, display the fill percentage of the dispenser by taking a reading with a 
force sensitive resistor and displaying on a HT16K33 Display.

Requirements:
    - Play a song and rotate a servo when the ir beam is broken.
    - Display the fill percentage using measurements from the force sensitive 
      resistor

"""



"""
This sets the path to find the python file ht16k33_i2c_base. This may need to
change depending on where ht16k33_i2c_base is located.
"""
import sys
sys.path.append("/var/lib/cloud9/ENGI301/i2c")

import time
import math
import random

import Adafruit_BBIO.ADC as ADC
import Adafruit_BBIO.GPIO as GPIO
import Adafruit_BBIO.PWM as PWM
import ht16k33_i2c_base as HT16K33

# ------------------------------------------------------------------------
# Global variables
# ------------------------------------------------------------------------
servo_pin = "P2_1"
piezo_pin = "P2_3"
FSR_pin = "AIN5"
ir_pin = "P2_2"


# ------------------------------------------------------------------------
# Note Library
# ------------------------------------------------------------------------
NOTE_B0  = 31
NOTE_C1  = 33
NOTE_CS1 = 35
NOTE_D1  = 37
NOTE_DS1 = 39
NOTE_E1  = 41
NOTE_F1  = 44
NOTE_FS1 = 46
NOTE_G1  = 49
NOTE_GS1 = 52
NOTE_A1  = 55
NOTE_AS1 = 58
NOTE_B1  = 62
NOTE_C2  = 65
NOTE_CS2 = 69
NOTE_D2  = 73
NOTE_DS2 = 78
NOTE_E2  = 82
NOTE_F2  = 87
NOTE_FS2 = 93
NOTE_G2  = 98
NOTE_GS2 = 104
NOTE_A2  = 110
NOTE_AS2 = 117
NOTE_B2  = 123
NOTE_C3  = 131
NOTE_CS3 = 139
NOTE_D3  = 147
NOTE_DS3 = 156
NOTE_E3  = 165
NOTE_F3  = 175
NOTE_FS3 = 185
NOTE_G3  = 196
NOTE_GS3 = 208
NOTE_A3  = 220
NOTE_AS3 = 233
NOTE_B3  = 247
NOTE_C4  = 262
NOTE_CS4 = 277
NOTE_D4  = 294
NOTE_DS4 = 311
NOTE_E4  = 330
NOTE_F4  = 349
NOTE_FS4 = 370
NOTE_G4  = 392
NOTE_GS4 = 415
NOTE_A4  = 440
NOTE_AS4 = 466
NOTE_B4  = 494
NOTE_C5  = 523
NOTE_CS5 = 554
NOTE_D5  = 587
NOTE_DS5 = 622
NOTE_E5  = 659
NOTE_F5  = 698
NOTE_FS5 = 740
NOTE_G5  = 784
NOTE_GS5 = 831
NOTE_A5  = 880
NOTE_AS5 = 932
NOTE_B5  = 988
NOTE_C6  = 1047
NOTE_CS6 = 1109
NOTE_D6  = 1175
NOTE_DS6 = 1245
NOTE_E6  = 1319
NOTE_F6  = 1397
NOTE_FS6 = 1480
NOTE_G6  = 1568
NOTE_GS6 = 1661
NOTE_A6  = 1760
NOTE_AS6 = 1865
NOTE_B6  = 1976
NOTE_C7  = 2093
NOTE_CS7 = 2217
NOTE_D7  = 2349
NOTE_DS7 = 2489
NOTE_E7  = 2637
NOTE_F7  = 2794
NOTE_FS7 = 2960
NOTE_G7  = 3136
NOTE_GS7 = 3322
NOTE_A7  = 3520
NOTE_AS7 = 3729
NOTE_B7  = 3951
NOTE_C8  = 4186
NOTE_CS8 = 4435
NOTE_D8  = 4699
NOTE_DS8 = 4978

# ------------------------------------------------------------------------
# Main Tasks
# ------------------------------------------------------------------------

def setup():
    """Sets up the hardware components."""
    ADC.setup()
    GPIO.setup(ir_pin, GPIO.IN)
    HT16K33.display_setup()
    HT16K33.display_clear()
    
# end def

def play_note(Note, Length):
    """Plays a given note for a given length."""
    PWM.start(piezo_pin, 50, Note)
    time.sleep(Length)
    
# end def

def open_door():
    """Makes the 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 zelda_secret():
    """Plays the Uncover Secret song from The Legend of Zelda."""
    play_note(NOTE_G5, 0.15)
    play_note(NOTE_FS5, 0.15)
    play_note(NOTE_DS5, 0.15)
    play_note(NOTE_A4, 0.15)
    play_note(NOTE_GS4, 0.15)
    play_note(NOTE_E5, 0.15)
    play_note(NOTE_GS5, 0.15)
    play_note(NOTE_C6, 0.15)
    PWM.stop(piezo_pin)
    PWM.cleanup()
    
# end def

def zelda_Zelda_Lullaby():
    """Plays Zelda's Lullaby from The Legend of Zelda."""
    play_note(NOTE_B4, 1.2)
    play_note(NOTE_D5, 0.6)
    play_note(NOTE_A4, 1.8)
    play_note(NOTE_B4, 1.2)
    play_note(NOTE_D5, 0.6)
    play_note(NOTE_A4, 1.8)
    PWM.stop(piezo_pin)
    PWM.cleanup()
    
# end def

def zelda_Epona_Song():
    """Plays Epona's Song from The Legend of Zelda."""
    play_note(NOTE_D5, 0.35)
    play_note(NOTE_B4, 0.35)
    play_note(NOTE_A4, 1.4)
    play_note(NOTE_D5, 0.35)
    play_note(NOTE_B4, 0.35)
    play_note(NOTE_A4, 1.4)
    play_note(NOTE_D5, 0.35)
    play_note(NOTE_B4, 0.35)
    play_note(NOTE_A4, 0.7)
    play_note(NOTE_B4, 0.7)
    play_note(NOTE_A4, 1.5)
    PWM.stop(piezo_pin)
    PWM.cleanup()
    
# end def

def zelda_Saria_song():
    """Plays Saria's Song from The Legend of Zelda."""
    play_note(NOTE_F4, 0.15)
    play_note(NOTE_A4, 0.15)
    play_note(NOTE_B4, 0.3)
    play_note(NOTE_F4, 0.15)
    play_note(NOTE_A4, 0.15)
    play_note(NOTE_B4, 0.3)
    play_note(NOTE_F4, 0.15)
    play_note(NOTE_A4, 0.15)
    play_note(NOTE_B4, 0.15)
    play_note(NOTE_E4, 0.15)
    play_note(NOTE_D5, 0.3)
    play_note(NOTE_B4, 0.15)
    play_note(NOTE_C5, 0.15)
    play_note(NOTE_B4, 0.15)
    play_note(NOTE_G4, 0.15)
    play_note(NOTE_E4, 0.6)
    PWM.stop(piezo_pin)
    PWM.cleanup()

# end def

def zelda_Song_of_Storms():
    """Plays the Song of Storms from The Legend of Zelda."""
    play_note(NOTE_D4, 0.15)
    play_note(NOTE_F4, 0.15)
    play_note(NOTE_D5, 0.6)
    play_note(NOTE_D4, 0.15)
    play_note(NOTE_F4, 0.15)
    play_note(NOTE_D5, 0.6)
    play_note(NOTE_E5, 0.45)
    play_note(NOTE_F5, 0.15)
    play_note(NOTE_E5, 0.15)
    play_note(NOTE_F5, 0.15)
    play_note(NOTE_E5, 0.15)
    play_note(NOTE_C5, 0.15)
    play_note(NOTE_A4, 0.6)
    
# end def

def zelda_Sun_Song():
    """Plays the Sun's Song from The Legend of Zelda."""
    play_note(NOTE_A4, 0.15)
    play_note(NOTE_F4, 0.15)
    play_note(NOTE_D5, 0.3)
    PWM.stop(piezo_pin)
    time.sleep(0.3)
    play_note(NOTE_A4, 0.15)
    play_note(NOTE_F4, 0.15)
    play_note(NOTE_D5, 0.3)
    PWM.stop(piezo_pin)
    time.sleep(0.3)
    play_note(NOTE_G4, 0.1)
    play_note(NOTE_A4, 0.1)
    play_note(NOTE_B4, 0.1)
    play_note(NOTE_C5, 0.1)
    play_note(NOTE_D5, 0.1)
    play_note(NOTE_E5, 0.1)
    play_note(NOTE_F5, 0.1)
    play_note(NOTE_G5, 0.125)
    play_note(NOTE_G5, 0.125)
    play_note(NOTE_G5, 0.125)
    play_note(NOTE_G5, 0.125)
    play_note(NOTE_G5, 0.125)
    play_note(NOTE_G5, 0.125)
    play_note(NOTE_G5, 0.125)
    play_note(NOTE_G5, 0.125)
    PWM.stop(piezo_pin)
    PWM.cleanup()
    
# end def

def zelda_Song_of_Time():
    """Plays the Song of Time from The Legend of Zelda."""
    play_note(NOTE_A4, 0.5)
    play_note(NOTE_D4, 1.0)
    play_note(NOTE_F4, 0.5)
    play_note(NOTE_A4, 0.5)
    play_note(NOTE_D4, 1.0)
    play_note(NOTE_F4, 0.5)
    play_note(NOTE_A4, 0.25)
    play_note(NOTE_C5, 0.25)
    play_note(NOTE_B4, 0.5)
    play_note(NOTE_G4, 0.5)
    play_note(NOTE_F4, 0.25)
    play_note(NOTE_G4, 0.25)
    play_note(NOTE_A4, 0.5)
    play_note(NOTE_D4, 0.5)
    play_note(NOTE_C4, 0.25)
    play_note(NOTE_E4, 0.25)
    play_note(NOTE_D4, 1.5)
    PWM.stop(piezo_pin)
    PWM.cleanup()
    
# end def

def zelda_Minuet_of_Forest():
    """Plays the Minuet of Forest from The Legend of Zelda."""
    play_note(NOTE_D5, 0.225)
    play_note(NOTE_D6, 0.225)
    play_note(NOTE_B5, 0.9)
    play_note(NOTE_A5, 0.225)
    play_note(NOTE_B5, 0.225)
    play_note(NOTE_A5, 0.9)
    PWM.stop(piezo_pin)
    PWM.cleanup()
    
# end def

def zelda_Bolero_of_Fire():
    """Plays the Bolero of Fire from The Legend of Zelda."""
    play_note(NOTE_F4, 0.225)
    play_note(NOTE_D4, 0.225)
    play_note(NOTE_F4, 0.225)
    play_note(NOTE_D4, 0.225)
    play_note(NOTE_A4, 0.225)
    play_note(NOTE_F4, 0.225)
    play_note(NOTE_A4, 0.225)
    play_note(NOTE_F4, 0.9375)
    PWM.stop(piezo_pin)
    PWM.cleanup()
    
# end def

def zelda_Serenade_of_Water():
    """Plays the Serenade of Water from The Legend of Zelda."""
    play_note(NOTE_D5, 0.5)
    play_note(NOTE_F5, 0.5)
    play_note(NOTE_A5, 0.5)
    play_note(NOTE_A5, 0.5)
    play_note(NOTE_B5, 1.0)
    PWM.stop(piezo_pin)
    PWM.cleanup()
    
# end def

def zelda_Requiem_of_Spirit():
    """Plays the Requiem of Spirit from The Legend of Zelda."""
    play_note(NOTE_D5, 0.75)
    play_note(NOTE_F5, 0.375)
    play_note(NOTE_D5, 0.375)
    play_note(NOTE_A5, 0.75)
    play_note(NOTE_F5, 0.75)
    play_note(NOTE_D5, 1.5)
    PWM.stop(piezo_pin)
    PWM.cleanup()
    
# end def

def zelda_Nocturne_of_Shadow():
    """Plays the Nocturne of Shadow from The Legend of Zelda."""
    play_note(NOTE_B4, 0.67)
    play_note(NOTE_A4, 0.67)
    play_note(NOTE_A4, 0.33)
    play_note(NOTE_D4, 0.33)
    play_note(NOTE_B4, 0.33)
    play_note(NOTE_A4, 0.33)
    play_note(NOTE_F4, 1.5)
    PWM.stop(piezo_pin)
    PWM.cleanup()
    
# end def

def zelda_Preulde_of_Light():
    """Plays the Prelude of Light from The Legend of Zelda."""
    play_note(NOTE_D5, 0.25)
    play_note(NOTE_A4, 0.75)
    play_note(NOTE_D5, 0.25)
    play_note(NOTE_A4, 0.25)
    play_note(NOTE_B4, 0.25)
    play_note(NOTE_D5, 1.25)
    PWM.stop(piezo_pin)
    PWM.cleanup()
    
# end def


# ------------------------------------------------------------------------
# Main script
# ------------------------------------------------------------------------

if __name__ == '__main__':
    setup()
    
    zelda_secret()
    
    while True:
        fill_percent = int(math.floor(ADC.read("AIN5") * 200))
        HT16K33.update_display(fill_percent)
        if GPIO.input("P2_2") == 0:
            song_number = random.randint(1, 12)
            if song_number == 1:
                zelda_Zelda_Lullaby()
            elif song_number == 2:
                zelda_Epona_Song()
            elif song_number == 3:
                zelda_Saria_song()
            elif song_number == 4:
                zelda_Song_of_Storms()
            elif song_number == 5:
                zelda_Sun_Song()
            elif song_number == 6:
                zelda_Song_of_Time()
            elif song_number == 7:
                zelda_Minuet_of_Forest()
            elif song_number == 8:
                zelda_Bolero_of_Fire()
            elif song_number == 9:
                zelda_Serenade_of_Water()
            elif song_number == 10:
                zelda_Requiem_of_Spirit()
            elif song_number == 11:
                zelda_Nocturne_of_Shadow()
            elif song_number == 12:
                zelda_Preulde_of_Light()
            else:
                pass
            open_door()
            time.sleep(5)
        else:
            pass
        time.sleep(1)
        

        

ht16k33_i2c_base.py

Python
This is the code we import into Skittle_Dispenser.py to use the hex display.
# -*- coding: utf-8 -*-
"""
--------------------------------------------------------------------------
HT16K33 I2C Library
--------------------------------------------------------------------------
License:   
Copyright 2018 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:

  * update_display(value)
      - Update the value on the display.  Value must be between 0 and 9999. 
  
--------------------------------------------------------------------------
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

"""
import os


# ------------------------------------------------------------------------
# Constants
# ------------------------------------------------------------------------

# HT16K33 values
DISPLAY_I2C_BUS              = 1                 # I2C 1  
DISPLAY_I2C_ADDR             = 0x70
DISPLAY_CMD                  = "/usr/sbin/i2cset -y 1 0x70"         


# ------------------------------------------------------------------------
# Display Library
# ------------------------------------------------------------------------
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

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


def display_setup():
    """Setup display"""
    # i2cset -y 0 0x70 0x21
    os.system("{0} {1}".format(DISPLAY_CMD, (HT16K33_SYSTEM_SETUP | HT16K33_OSCILLATOR)))
    # i2cset -y 0 0x70 0x81
    os.system("{0} {1}".format(DISPLAY_CMD, (HT16K33_BLINK_CMD | HT16K33_BLINK_OFF | HT16K33_BLINK_DISPLAYON)))
    # i2cset -y 0 0x70 0xEF
    os.system("{0} {1}".format(DISPLAY_CMD, (HT16K33_BRIGHTNESS_CMD | HT16K33_BRIGHTNESS_HIGHEST)))

# End def


def display_clear():
    """Clear the display to read '0000'"""
    # i2cset -y 0 0x70 0x00 0x3F
    os.system("{0} {1} {2}".format(DISPLAY_CMD, DIGIT_ADDR[0], HEX_DIGITS[0]))
    # i2cset -y 0 0x70 0x02 0x3F
    os.system("{0} {1} {2}".format(DISPLAY_CMD, DIGIT_ADDR[1], HEX_DIGITS[0]))
    # i2cset -y 0 0x70 0x06 0x3F
    os.system("{0} {1} {2}".format(DISPLAY_CMD, DIGIT_ADDR[2], HEX_DIGITS[0]))
    # i2cset -y 0 0x70 0x08 0x3F
    os.system("{0} {1} {2}".format(DISPLAY_CMD, DIGIT_ADDR[3], HEX_DIGITS[0]))
    
    os.system("{0} {1} {2}".format(DISPLAY_CMD, COLON_ADDR, 0x0))
    
# End def


def display_encode(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 display_set(data):
    """Display the data.
    
    data is a list containing 4 values
    """
    for i in range(0,3):
        display_set_digit(i, data[i])
    
# End def


def display_set_digit(digit_number, data, double_point=False):
    """Update the given digit of the display."""
    os.system("{0} {1} {2}".format(DISPLAY_CMD, DIGIT_ADDR[digit_number], display_encode(data, double_point)))    

# End def


def update_display(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 not between 0 and 9999.")
    
    for i in range(0, 4):
        display_set_digit((3-i), (value % 10))
        value = (value / 10)
    

# End def



# ------------------------------------------------------------------------
# Main script
# ------------------------------------------------------------------------

if __name__ == '__main__':
    import time

    delay = 0.1
    
    print("Test HT16K33 Display:")

    display_setup()
    
    for i in range(0, 10):
        update_display(i)
        time.sleep(delay)

    for i in range(0, 100, 10):
        update_display(i)
        time.sleep(delay)

    for i in range(0, 1000, 100):
        update_display(i)
        time.sleep(delay)
        
    for i in range(0, 10000, 1000):
        update_display(i)
        time.sleep(delay)
    
    display_clear()    
    print("Test Finished.")

Skittle Dispenser GitHub Repository

Here is a link to my GitHub repository for the Candy Dispenser project.

Credits

Nicholas Lester

Nicholas Lester

1 project • 0 followers
Rice University 2021, Mechanical Engineering

Comments

Add projectSign up / Login