Audio-Animatronic Binary Counting

Here's another fun way to use your animatronic hand -- it can count up to the number 15 (and say the numbers out loud)! How's this possible with only four active fingers? By using a modified finger binary counting method.

Finger binary is a counting method where each finger is assigned a 0 (down) and 1 (up) state, each with a different power of two.

These will be the powers of two and values of each finger of our modified, four-finger binary counting system:

  • Index is 20, in binary 0001, or a value of 1
  • Middle is 21, in binary 0010, or a value of 2
  • Ring is 22, in binary 0100, or a value of 4
  • Pinky is 23, in binary 1000, or a value of 8

By holding up the Index (1) and Middle (2) fingers, the binary representation is 0011. We add their values, 1 + 2, therefore we indicate the number 3.

Here's another one: Pinky(8) + Ring(4) is 1100 in binary, or a value of 12.

So, here's the pattern for counting from 1 to 15 on four fingers:

  • 1 = Index 0001
  • 2 = Middle 0010
  • 3 = Index, Middle 0011
  • 4 = Ring 0100
  • 5 = Ring, Index 0101
  • 6 = Ring, Middle 0110
  • 7 = Ring, Middle, Index 0111
  • 8 = Pinky 1000
  • 9 = Pinky, Index 1001
  • 10 = Pinky, Middle 1010
  • 11 = Pinky, Middle, Index 1011
  • 12 = Pinky, Ring 1100
  • 13 = Pinky, Ring, Index 1101
  • 14 = Pinky, Ring, Middle 1110
  • 15 = Pinky, Ring, Middle, Index 1111

CPX Setup

Below is the CircuitPython code we'll use to count in binary on the fingers.

You'll still want all 4 servos attached. But now you'll also want a speaker on the speaker port!

Audio Files

The CRICKIT is a terrific board for animatronics projects, particularly audio-animatronics projects, because it can play back audio as well as control servos and motors. Download this .zip file and uncompress it to get a collection of audio .wav files we'll use during number counting.

We'll use an external speaker to hear things loud an clear. Screw the leads of your 4Ω or 8Ω speaker to the two speaker terminals on the CRICKIT -- orientation does not matter, so either wire can go to either terminal.

Once you've uncompressed the .zip file, drag the .wav files onto your CIRCUITPY drive. The files can live at the root level of the CPX.

For more on audio playback with CRICKIT, check out this guide.

CircuitPython Code

Download this code and paste it into Mu. Then, save the file as main.py or code.py to your Circuit Playground Express. Make sure the CPX switch is to the left and sit back to watch and listen as your CRICKIT-powered animatronic hand counts in binary!

#  Animatronic Hand
#  Binary Counting on four fingers up to 15
import time
from digitalio import DigitalInOut, Direction, Pull
import audioio
import board
from adafruit_crickit import crickit

#################### CPX switch
# use the CPX onboard switch to turn on/off (helps calibrate)
switch = DigitalInOut(board.SLIDE_SWITCH)
switch.direction = Direction.INPUT
switch.pull = Pull.UP

#################### Audio setup
print("Let's count in binary.")
wavfiles = ("one.wav", "two.wav", "three.wav", "four.wav", "five.wav", "six.wav",
            "seven.wav", "eight.wav", "nine.wav", "ten.wav", "eleven.wav",
            "twelve.wav", "thirteen.wav", "fourteen.wav", "fifteen.wav")
introfile = "intro.wav"

cpx_audio = audioio.AudioOut(board.A0)
def play_file(wavfile):
    with open(wavfile, "rb") as f:
        wav = audioio.WaveFile(f)
        cpx_audio.play(wav)
        while cpx_audio.playing:
            pass

#################### 4 Servos!
servos = (crickit.servo_1, crickit.servo_2, crickit.servo_3, crickit.servo_4)
for servo in servos:
    servo.angle = 180 # starting angle, open hand

# Which servos to actuate for each number
counting = (
    [3],
    [2],
    [3, 2],
    [1],
    [1, 3],
    [1, 2],
    [3, 2, 1],
    [0],
    [0, 3],
    [0, 2],
    [0, 3, 2],
    [0, 1],
    [0, 3, 1],
    [0, 2, 1],
    [0, 3, 2, 1]
)

play_file(introfile)

while True:
    if not switch.value:
        continue

    # the CPX switch is on, so do things
    for servo in servos:  # close the fist
        servo.angle = 0  # close the fingers
        print("Servo %d angle = 0" % (servos.index(servo)+1) )
        time.sleep(.2)

    time.sleep(1)  # pause a moment

    for i in range(len(counting)):
        # close all the counting fingers between numbers
        for servo in servos:
            servo.angle = 0  # close
            print("\t\tServo #%d angle 0" % (servos.index(servo)+1))
            time.sleep(0.3)

        print("Number #%d \tfingers: %s" % (i+1, counting[i]))

        # open just the indicated fingers when counting
        for finger in counting[i]:
            servos[finger].angle = 180  # open
            print("\t\tServo #%d angle 180" % (finger+1))
            time.sleep(0.3)
        # say it!
        play_file(wavfiles[i])
        # hold for a bit of time
        time.sleep(0.3)
        print("...")

Code Explainer

Take a look at the code to get a sense of how it works. After importing libraries and setting up the CPX switch, we have an audio setup section.

First, we create a variable list called wavfiles that stores the names of all the counting audio files we'll use. We also make a variable called introfile that has the filename of the intro audio wav file.

Then, we instantiate the audioio command and define a process called play_file to open, play, and close the audio files specified.

What follows are partial code snippets from the full program above, you cannot run they by themselves, they're here just for explanation
Download: file
 
#################### Audio setup
print("Let's count in binary.")
wavfiles = ["one.wav", "two.wav", "three.wav", "four.wav", "five.wav", "six.wav",
            "seven.wav", "eight.wav", "nine.wav", "ten.wav", "eleven.wav",
            "twelve.wav", "thirteen.wav", "fourteen.wav", "fifteen.wav"]
introfile = "intro.wav"
 
cpx_audio = audioio.AudioOut(board.A0)
def play_file(wavfile):
    with open(wavfile, "rb") as f:
        wav = audioio.WaveFile(f)
        cpx_audio.play(wav)
        while cpx_audio.playing:
            pass

Servo Counting

Next, we set up the four servos, and create a variable list of lists called counting. This specifies which finger or set of fingers is to be raised up for each number that we're counting. You can see, for example, that the number 1 raises the index finger only, while 15 raises all four fingers.

Download: file
#################### 4 Servos
servos = (crickit.servo_1, crickit.servo_2, crickit.servo_3, crickit.servo_4)
for servo in servos:
    servo.angle = 180 # starting angle, open hand
 
# Which servos to actuate for each number
counting = [
    [3],
    [2],
    [3, 2],
    [1],
    [1, 3],
    [1, 2],
    [3, 2, 1],
    [0],
    [0, 3],
    [0, 2],
    [0, 3, 2],
    [0, 1],
    [0, 3, 1],
    [0, 2, 1],
    [0, 3, 2, 1]
]

Counting Time

The final bit of setup is to play the intro wav file and then get into the main loop. Here we check the CPX switch and only proceed if it is set to the left position.

Then, we close all the fingers down into a fist and pause a moment.

Download: file
play_file(introfile)

while True:
    if not switch.value:
        continue

    # the CPX switch is on, so do things
    for servo in servos:  # close the fist
        servo.angle = 0  # close the fingers
        print("Servo %d angle = 0" % (servos.index(servo)+1) )
        time.sleep(.2)

    time.sleep(1)  # pause a moment

Finally, we run through the counting list one item at a time and open only the fingers specified per entry in that list.

After all of the fingers needed for a particular number have been raise, we play back the appropriate wav file so the audio-animatronic can speak!

Download: file
    for i in range(len(counting)):
        # close all the counting fingers between numbers
        for servo in servos:
            servo.angle = 0  # close
            print("\t\tServo #%d angle 0" % (servos.index(servo)+1))
            time.sleep(0.3)

        print("Number #%d \tfingers: %s" % (i+1, counting[i]))

        # open just the indicated fingers when counting
        for finger in counting[i]:
            servos[finger].angle = 180  # open
            print("\t\tServo #%d angle 180" % (finger+1))
            time.sleep(0.3)
        # say it!
        play_file(wavfiles[i])
        # hold for a bit of time
        time.sleep(0.3)
        print("...")
This guide was first published on May 23, 2018. It was last updated on May 23, 2018. This page (Audio-Animatronic Binary Counting) was last updated on Sep 30, 2019.