Halloween candy vending machine
Software
The preconfigured Python image from the PiXtend website [6] lets you start programming as quickly as possible. Make sure the image matches the PiXtend model you use. To do so, search for the PiXtend Python Library heading on the page for the respective device.
Once you have downloaded the image and written it to an SD card, the PiXtend Python Library (PPL) is available directly. You only need to install the library to control the WS2812 LEDs. These steps are summarized in the first eight lines of Listing 1. To test whether the library installation was successful, run the strandtest.py
program (lines 9 and 10).
Listing 1
Installing PPL
01 $ cd ~ 02 $ sudo apt update 03 $ sudo apt upgrade 04 $ sudo apt install build-essential python-dev python-pip unzip wget scons swig 05 $ wget https://github.com/jgarff/rpi_ws281x/archive/master.zip unzip master.zip 06 $ cd rpi_ws281x-master 07 $ sudo scons 08 $ sudo pip install rpi_ws281x 09 $ cd ~/rpi_ws281x-master/python 10 $ sudo PYTHONPATH=".:build/lib.linux-armv7l-2.7" python examples/strandtest.py
In the source code, you can configure different settings for the LEDs, including the LED_BRIGHTNESS
parameter for the brightness of the diodes. You will want to choose a value that is as low as possible, because the LEDs consume a lot of current at full light intensity, and they also heat up.
Control
The ws.py
program (Listing 2) [7] controls the eyes and mouth. At the start of the program you will find the TRANSLATE
table that enables linear access to the individual LEDs. As mentioned earlier, the way the LEDs are installed on the panels is a little strange.
Listing 2
ws.py
01 import time 02 from neopixel import * 03 import argparse 04 05 LED_COUNT = 384 06 LED_PIN = 12 07 LED_FREQ_HZ = 800000 08 LED_DMA = 10 09 LED_BRIGHTNESS = 16 10 LED_INVERT = False 11 LED_CHANNEL = 0 12 13 TRANSLATE = [0, 15, 16, 31, 32, 47, 48, 63, 64, 79, 80, 95, 96, 111, 112, 127, 128, 143, 144, 159, 160, 175, 176, 191, 192, 207, 208, 223, 224, 239, 240, 255, 256, 271, 272, 287, 288, 303, 304, 319, 320, 335, 336, 351, 352, 367, 368, 383, 1, 14, 17, 30, 33, 46, 49, 62, 65, 78, 81, 94, 97, 110, 113, 126, 129, 142, 145, 158, 161, 174, 177, 190, 193, 206, 209, 222, 225, 238, 241, 254, 257, 270, 273, 286, 289, 302, 305, 318, 321, 334, 337, 350, 353, 366, 369, 382, 2, 13, 18, 29, 34, 45, 50, 61, 66, 77, 82, 93, 98, 109, 114, 125, 130, 141, 146, 157, 162, 173, 178, 189, 194, 205, 210, 221, 226, 237, 242, 253, 258, 269, 274, 285, 290, 301, 306, 317, 322, 333, 338, 349, 354, 365, 370, 381, 3, 12, 19, 28, 35, 44, 51, 60, 67, 76, 83, 92, 99, 108, 115, 124, 131, 140, 147, 156, 163, 172, 179, 188, 195, 204, 211, 220, 227, 236, 243, 252, 259, 268, 275, 284, 291, 300, 307, 316, 323, 332, 339, 348, 355, 364, 371, 380, 4, 11, 20, 27, 36, 43, 52, 59, 68, 75, 84, 91, 100, 107, 116, 123, 132, 139, 148, 155, 164, 171, 180, 187, 196, 203, 212, 219, 228, 235, 244, 251, 260, 267, 276, 283, 292, 299, 308, 315, 324, 331, 340, 347, 356, 363, 372, 379, 5, 10, 21, 26, 37, 42, 53, 58, 69, 74, 85, 90, 101, 106, 117, 122, 133, 138, 149, 154, 165, 170, 181, 186, 197, 202, 213, 218, 229, 234, 245, 250, 261, 266, 277, 282, 293, 298, 309, 314, 325, 330, 341, 346, 357, 362, 373, 378, 6, 9, 22, 25, 38, 41, 54, 57, 70, 73, 86, 89, 102, 105, 118, 121, 134, 137, 150, 153, 166, 169, 182, 185, 198, 201, 214, 217, 230, 233, 246, 249, 262, 265, 278, 281, 294, 297, 310, 313, 326, 329, 342, 345, 358, 361, 374, 377, 7, 8, 23, 24, 39, 40, 55, 56, 71, 72, 87, 88, 103, 104, 119, 120, 135, 136, 151, 152, 167, 168, 183, 184, 199, 200, 215, 216, 231, 232, 247, 248, 263, 264, 279, 280, 295, 296, 311, 312, 327, 328, 343, 344, 359, 360, 375, 376] 14 15 strip = Adafruit_NeoPixel(LED_COUNT, LED_PIN, LED_FREQ_HZ, LED_DMA, LED_INVERT, LED_BRIGHTNESS, LED_CHANNEL) 16 strip.begin() 17 18 f = open("data.bin","rt") 19 data=f.readlines() 20 bitcounter=0 21 image=2 22 for line in data: 23 if len(line)<3: 24 bitcounter=0 25 image=image+1 26 strip.show() 27 time.sleep(0.1) 28 if not(line.find("#")): 29 continue 30 bits = line.split(" ") 31 for bit in bits: 32 bit=bit.strip() 33 if len(bit) > 0: 34 if bit=='1': 35 strip.setPixelColor(TRANSLATE[bitcounter], Color(0,255,0)) 36 else: 37 strip.setPixelColor(TRANSLATE[bitcounter], Color(0,0,0)) 38 bitcounter=bitcounter+1 39 f.close
The information as to which LED is switched is provided in the data.bin
file; Listing 3 shows an excerpt. Bit patterns control the LEDs. You can store any number of patterns for ws.py
to send to the LEDs in sequence. It parses the file line-by-line and converts the data to commands for the LEDs. An empty line initiates a new bit pattern; the program skips comment lines that start with a hash symbol (#).
Listing 3
data.bin (Excerpt)
# 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 # 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 1 1 1 1 1 1 0 0 1 1 1 1 1 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 1 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 1 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 1 1 1 1 1 0 0 1 1 1 1 1 1 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0
Program
The control program (Listing 4) continuously queries the PiXtend's inputs and starts the conveyor belts alternately when the light barrier is triggered. In parallel to starting the belts, it calls two additional programs: One controls the LEDs, and the other plays back the matching creepy sound. If the play
command doesn't work as intended, you can try connecting and configuring an external sound card to the Rasp Pi. Figure 4 shows the eyes and mouth. To see the whole setup working, check out my YouTube video [8].
Listing 4
halloween.py
01 #!/usr/bin/env python 02 # coding=utf-8 03 04 from __future__ import print_function 05 # Import Pixtend class 06 from pixtendlib import Pixtend 07 import time 08 import sys 09 import threading 10 import os, platform 11 12 p = Pixtend() 13 14 # Open SPI bus for communication 15 try: 16 p.open() 17 except IOError as io_err: 18 # Print error text and delete PiXtend instance 19 print("Error opening the SPI bus! Error is: ", io_err) 20 p.close() 21 p = None 22 23 def thread_sound(): 24 os.system("play gost.wav") 25 26 def thread_animation(): 27 os.system("sudo python ws.py") 28 29 # ----------------------------------------------------- 30 # Main Program 31 # ----------------------------------------------------- 32 if p is not None: 33 print("Running Main Program - Hit Ctrl + C to exit") 34 # Set some variables needed in the main loop 35 is_config = False 36 switch = True 37 pressed = False 38 while True: 39 try: 40 # Using Auto Mode for optimal SPI bus usage 41 if p.auto_mode() == 0: 42 if not is_config: 43 is_config = True 44 # Space for setup 45 print (p.digital_input0,":",p.digital_input1) 46 if p.digital_input2 == p.ON and pressed==False: 47 pressed=True; 48 x = threading.Thread(target=thread_sound) 49 x.start() 50 y = threading.Thread(target=thread_animation) 51 y.start() 52 if switch: p.digital_output0 = p.ON 53 else : p.digital_output1 = p.ON 54 switch=not(switch) 55 if p.digital_output0 == p.ON and di0_old == 0 and p.digital_input0 == p.ON: 56 p.digital_output0 = p.OFF 57 pressed=False 58 if p.digital_output1 == p.ON and di1_old == 0 and p.digital_input1 == p.ON: 59 p.digital_output1 = p.OFF 60 pressed=False 61 di0_old = p.digital_input0 62 di1_old = p.digital_input1 63 else: 64 print("Auto Mode - Communication is not yet up...Please wait...") 65 # Wait at minimum 0.1sec or 100ms before getting new values 66 time.sleep(0.1) 67 except KeyboardInterrupt: 68 # Keyboard interrupt caught, Ctrl + C, now clean up and leave program 69 p.close() 70 p = None 71 break 72 else: 73 # If there was an error when opening the SPI bus interface, leave the program. 74 print("") 75 print("There was a problem with the PiXtend communication. Quitting.") 76 print("")
« Previous 1 2 3 Next »
Buy this article as PDF
(incl. VAT)
Buy Linux Magazine
Subscribe to our Linux Newsletters
Find Linux and Open Source Jobs
Subscribe to our ADMIN Newsletters
Support Our Work
Linux Magazine content is made possible with support from readers like you. Please consider contributing when you’ve found an article to be beneficial.
News
-
Fedora 40 Beta Released Soon
With the official release of Fedora 40 coming in April, it's almost time to download the beta and see what's new.
-
New Pentesting Distribution to Compete with Kali Linux
SnoopGod is now available for your testing needs
-
Juno Computers Launches Another Linux Laptop
If you're looking for a powerhouse laptop that runs Ubuntu, the Juno Computers Neptune 17 v6 should be on your radar.
-
ZorinOS 17.1 Released, Includes Improved Windows App Support
If you need or desire to run Windows applications on Linux, there's one distribution intent on making that easier for you and its new release further improves that feature.
-
Linux Market Share Surpasses 4% for the First Time
Look out Windows and macOS, Linux is on the rise and has even topped ChromeOS to become the fourth most widely used OS around the globe.
-
KDE’s Plasma 6 Officially Available
KDE’s Plasma 6.0 "Megarelease" has happened, and it's brimming with new features, polish, and performance.
-
Latest Version of Tails Unleashed
Tails 6.0 is based on Debian 12 and includes GNOME 43.
-
KDE Announces New Slimbook V with Plenty of Power and KDE’s Plasma 6
If you're a fan of KDE Plasma, you'll be thrilled to hear they've announced a new Slimbook with an AMD CPU and the latest version of KDE Plasma desktop.
-
Monthly Sponsorship Includes Early Access to elementary OS 8
If you want to get a glimpse of what's in the pipeline for elementary OS 8, just set up a monthly sponsorship to help fund its continued existence.
-
DebConf24 to be Held in South Korea
Busan will be the location of the latest DebConf running July 28 through August 4