OK whew, finally we are ready to make our Circuit Playground Express zapper. There's no hardware changes or add-ons, you can use the CPX exactly as is thanks to the built-in IR emitter. You'll just need to save this code as main.py on your CPX:

# SPDX-FileCopyrightText: 2018 Limor Fried for Adafruit Industries
#
# SPDX-License-Identifier: MIT

import array
import time

import board
import pulseio
from digitalio import DigitalInOut, Direction, Pull

# pylint: disable=eval-used
# Switch to select 'stealth-mode'
switch = DigitalInOut(board.SLIDE_SWITCH)
switch.direction = Direction.INPUT
switch.pull = Pull.UP
# Button to see output debug
led = DigitalInOut(board.D13)
led.direction = Direction.OUTPUT

# Speaker as haptic feedback
spkr_en = DigitalInOut(board.SPEAKER_ENABLE)
spkr_en.direction = Direction.OUTPUT
spkr_en.value = True
spkr = DigitalInOut(board.SPEAKER)
spkr.direction = Direction.OUTPUT

# Allow any button to trigger activity!
button_a = DigitalInOut(board.BUTTON_A)
button_a.direction = Direction.INPUT
button_a.pull = Pull.DOWN
button_b = DigitalInOut(board.BUTTON_B)
button_b.direction = Direction.INPUT
button_b.pull = Pull.DOWN

while True:
    # Wait for button press!
    while not (button_a.value or button_b.value):
        pass
    time.sleep(0.5)  # Give a half second before starting

    # gooooo!
    f = open("/codes.txt", "r")
    for line in f:
        code = eval(line)
        print(code)
        if switch.value:
            led.value = True
        else:
            spkr.value = True
        # If this is a repeating code, extract details
        try:
            repeat = code["repeat"]
            delay = code["repeat_delay"]
        except KeyError:  # by default, repeat once only!
            repeat = 1
            delay = 0
        # The table holds the on/off pairs
        table = code["table"]
        pulses = []  # store the pulses here
        # Read through each indexed element
        for i in code["index"]:
            pulses += table[i]  # and add to the list of pulses
        pulses.pop()  # remove one final 'low' pulse

        with pulseio.PulseOut(
            board.REMOTEOUT, frequency=code["freq"], duty_cycle=2**15
        ) as pulse:
            for i in range(repeat):
                pulse.send(array.array("H", pulses))
                time.sleep(delay)

        led.value = False
        spkr.value = False
        time.sleep(code["delay"])

    f.close()

And save this file as codes.txt on the CPX mini disk drive as well (it contains the 200+ IR codes)

Let's go through what this code actually does!

Stealth Mode

When you're in the field, you may want to avoid having something blinking in your hand. On the other hand, you want to know when its done transmitting so you can move on to the next target. So we use the built in slide switch for 'Stealth Mode':

############## Switch to select 'stealth-mode'
switch = DigitalInOut(board.SLIDE_SWITCH)
switch.direction = Direction.INPUT
switch.pull = Pull.UP
# Button to see output debug
led = DigitalInOut(board.D13)
led.direction = Direction.OUTPUT

############## Speaker as haptic feedback
spkr_en = DigitalInOut(board.SPEAKER_ENABLE)
spkr_en.direction = Direction.OUTPUT
spkr_en.value = True
spkr = DigitalInOut(board.SPEAKER)
spkr.direction = Direction.OUTPUT

If the switch is one way, the red LED will be used to blink when emitting a code. Flip the switch to have the speaker make a 'tic', a very small sound that you can hear if you're nearby but won't give you away!

Buttons and IR output

We'll use the two buttons to tell when its time to zap! Since its hard to remember which is which while out in the field, we set up both buttons:

############## Allow any button to trigger activity!
button_a = DigitalInOut(board.BUTTON_A)
button_a.direction = Direction.INPUT
button_a.pull = Pull.DOWN
button_b = DigitalInOut(board.BUTTON_B)
button_b.direction = Direction.INPUT
button_b.pull = Pull.DOWN

The built in REMOTEOUT pin is connected to the IR LED, we just create a PWM output on that pin. Even though we set up the frequency as 38000, we'll change the frequency for each code (thus the variable_frequency=True)

pwm = pulseio.PWMOut(board.REMOTEOUT, frequency=38000, duty_cycle=2 ** 15, variable_frequency=True)
pulse = pulseio.PulseOut(pwm)

Main Loop

Now we're ready. In the main loop, we'll wait for any button press. If we get a press, we pause a moment (to get lined up) and then open that codes.txt file to read in IR codes!

while True:
    # Wait for button press!
    while not (button_a.value or button_b.value):
        pass
    time.sleep(0.5)  # Give a half second before starting
    
    # gooooo!
    f = open("/codes.txt", "r")

Each line contains that dictionary entry. We use the magical eval() function to convert the text to a Python object. Depending on the switch position we either turn on the LED or give the speaker a pulse high.

for line in f:
        code = eval(line)
        print(code)
        if switch.value:
            led.value = True
        else:
            spkr.value = True

Then we can check the dictionary. Repeating codes have a 'repeat' and 'repeat_delay' entry. If not, we'll assume we're transmitting only once.

        # If this is a repeating code, extract details
        try:
            repeat = code['repeat']
            delay = code['repeat_delay']
        except KeyError:   # by default, repeat once only!
            repeat = 1
            delay = 0

Then, we can take that code-pair table out, and 'de-index' the table to recreate the original on/off list (we need that list for the pulse output send function)

        # The table holds the on/off pairs
        table = code['table']
        pulses = []  # store the pulses here
        # Read through each indexed element
        for i in code['index']:
            pulses += table[i]  # and add to the list of pulses
        pulses.pop()  # remove one final 'low' pulse

Finally, set the PWM output frequency to whatever the TV is listening for, and send the pulse on/off codes, repeating if desired.

Once done, turn off the LED and speaker, and have one long inter-code delay.

        pwm.frequency = code['freq']
        for i in range(repeat):
            pulse.send(array.array('H', pulses))
            time.sleep(delay)
        
        led.value = False
        spkr.value = False
        time.sleep(code['delay'])

And when done, close the file so we can start over, waiting for another button press

    f.close()

That's it! You may want to run the code on the REPL the first time, to make sure you've got everything set up.

Note that the IR LED draws a couple hundred milli-Amps when sending IR data, so a good battery pack will help  you get that range. You can expect up to 30 feet, depending on your targeting skills!

This guide was first published on Mar 18, 2018. It was last updated on Mar 18, 2018.

This page (CircuitPython Code) was last updated on May 08, 2023.

Text editor powered by tinymce.