mirror of
https://github.com/dekuNukem/exixe.git
synced 2025-10-24 11:11:04 -07:00
Adding Python library
Adding references to python library Adding python library Adding python examples that match arduino's examples
This commit is contained in:
@@ -61,6 +61,10 @@ Please see [getting_started.md](/getting_started.md)
|
||||
|
||||
Please [click here](arduino_library)
|
||||
|
||||
## Raspberry Pi/Python Library
|
||||
|
||||
Please [click here](python_library)
|
||||
|
||||
## Pinout, SPI command format and technical details
|
||||
|
||||
Please see [technical_details.md](/technical_details.md)
|
||||
|
||||
@@ -108,7 +108,7 @@ Finally, the [multiple tubes crossfade](/arduino_examples/5_multiple_tubes_cross
|
||||
|
||||
## Resources
|
||||
|
||||
Consult the [Arduino library docs](arduino_library/README.md) to see how to use library functions. And explore the [sample sketches](/arduino_examples).
|
||||
Consult the [Arduino library docs](arduino_library/README.md) and [Python library docs](python_library/README.md) to see how to use library functions. And explore the [arduino sample sketches](/arduino_examples) and [python samples](/python_examples).
|
||||
|
||||
If you want to keep it simple, there are some self-contained [barebone examples](/arduino_examples/barebone) too.
|
||||
|
||||
|
||||
17
python_examples/1_LED_test/1_led_test.py
Normal file
17
python_examples/1_LED_test/1_led_test.py
Normal file
@@ -0,0 +1,17 @@
|
||||
# exixe modules: https://github.com/dekuNukem/exixe
|
||||
# python library docs: https://github.com/dekuNukem/exixe/tree/master/python_library
|
||||
# Demo 1: LED Test
|
||||
import exixe
|
||||
import spidev
|
||||
import time
|
||||
|
||||
spi = spidev.SpiDev()
|
||||
spi.open(0, 0)
|
||||
spi.max_speed_hz = 7800000
|
||||
cs_pin = 22
|
||||
|
||||
my_tube = exixe.Exixe(cs_pin, spi)
|
||||
|
||||
while True:
|
||||
my_tube.set_led(127, 0, 127) # Purple
|
||||
time.sleep(1)
|
||||
22
python_examples/2_loop_digit_simple/2_loop_digit_simple.py
Normal file
22
python_examples/2_loop_digit_simple/2_loop_digit_simple.py
Normal file
@@ -0,0 +1,22 @@
|
||||
# exixe modules: https://github.com/dekuNukem/exixe
|
||||
# python library docs: https://github.com/dekuNukem/exixe/tree/master/python_library
|
||||
# Demo 2: loop digits test
|
||||
import exixe
|
||||
import spidev
|
||||
import time
|
||||
|
||||
spi = spidev.SpiDev()
|
||||
spi.open(0, 0)
|
||||
spi.max_speed_hz = 7800000
|
||||
cs_pin = 16
|
||||
|
||||
my_tube = exixe.Exixe(cs_pin, spi)
|
||||
|
||||
# Update led to be purple & count from 0 -> 9 and reset back to 0 at 10
|
||||
count = 0
|
||||
while True:
|
||||
my_tube.set_led(127, 0, 127) # Purple
|
||||
my_tube.set_digit(count)
|
||||
|
||||
count = (count + 1) % 10
|
||||
time.sleep(1)
|
||||
@@ -0,0 +1,28 @@
|
||||
# exixe modules: https://github.com/dekuNukem/exixe
|
||||
# python library docs: https://github.com/dekuNukem/exixe/tree/master/python_library
|
||||
# Demo 3: Loop digits with crossfade animation
|
||||
import exixe
|
||||
import spidev
|
||||
import time
|
||||
|
||||
spi = spidev.SpiDev()
|
||||
spi.open(0, 0)
|
||||
spi.max_speed_hz = 7800000
|
||||
cs_pin = 16
|
||||
|
||||
my_tube = exixe.Exixe(cs_pin, spi)
|
||||
|
||||
count = 0
|
||||
# my_tube.crossfade_run is a non-blocking call which can allow a main loop to handle more tasks
|
||||
# Ideally there will be a 33ms delay but this needs to take in account all other tasks within the loop
|
||||
while True:
|
||||
my_tube.set_led(127, 64, 0) # Orange
|
||||
|
||||
# Initialize the crossfade with next digit and how many frames desired.
|
||||
my_tube.crossfade_init(count, 30)
|
||||
while my_tube.animation_in_progress:
|
||||
my_tube.crossfade_run()
|
||||
# 30 frames at a 33ms delay will ~1 second
|
||||
time.sleep(0.033)
|
||||
|
||||
count = (count + 1) % 10
|
||||
@@ -0,0 +1,28 @@
|
||||
# exixe modules: https://github.com/dekuNukem/exixe
|
||||
# python library docs: https://github.com/dekuNukem/exixe/tree/master/python_library
|
||||
# Demo 4: Loop digits on two tubes
|
||||
import exixe
|
||||
import spidev
|
||||
import time
|
||||
|
||||
spi = spidev.SpiDev()
|
||||
spi.open(0, 0)
|
||||
spi.max_speed_hz = 7800000
|
||||
|
||||
# Update these to the cs pins you're using
|
||||
cs_pin_1 = 16
|
||||
cs_pin_2 = 32
|
||||
|
||||
my_tube_1 = exixe.Exixe(cs_pin_1, spi)
|
||||
my_tube_2 = exixe.Exixe(cs_pin_2, spi)
|
||||
|
||||
# Update LED's, have 1 tube count up, the other tube count down
|
||||
count = 0
|
||||
while True:
|
||||
my_tube_1.set_led(127, 0, 127) # Purple
|
||||
my_tube_2.set_led(127, 127, 0) # Yellow
|
||||
my_tube_1.set_digit(count)
|
||||
my_tube_2.set_digit(10 - count)
|
||||
|
||||
count = (count + 1) % 10
|
||||
time.sleep(1)
|
||||
@@ -0,0 +1,33 @@
|
||||
# exixe modules: https://github.com/dekuNukem/exixe
|
||||
# python library docs: https://github.com/dekuNukem/exixe/tree/master/python_library
|
||||
# Demo 5: Loop digits on two tubes with crossfade animation
|
||||
import exixe
|
||||
import spidev
|
||||
import time
|
||||
|
||||
spi = spidev.SpiDev()
|
||||
spi.open(0, 0)
|
||||
spi.max_speed_hz = 7800000
|
||||
cs_pin_1 = 16
|
||||
cs_pin_2 = 32
|
||||
|
||||
my_tube_1 = exixe.Exixe(cs_pin_1, spi)
|
||||
my_tube_2 = exixe.Exixe(cs_pin_2, spi)
|
||||
|
||||
count = 0
|
||||
# my_tube.crossfade_run is a non-blocking call which can allow a main loop to handle more tasks
|
||||
# Ideally there will be a 33ms delay but this needs to take in account all other tasks within the loop
|
||||
while True:
|
||||
my_tube_1.set_led(127, 64, 0) # Orange
|
||||
my_tube_2.set_led(127, 0, 127) # Purple
|
||||
|
||||
# Initialize the crossfade with next digit and how many frames desired.
|
||||
my_tube_1.crossfade_init(count, 30)
|
||||
my_tube_2.crossfade_init(10 - count, 30)
|
||||
while my_tube_1.animation_in_progress and my_tube_2.animation_in_progress:
|
||||
my_tube_1.crossfade_run()
|
||||
my_tube_2.crossfade_run()
|
||||
# 30 frames at a 33ms delay will ~1 second
|
||||
time.sleep(0.033)
|
||||
|
||||
count = (count + 1) % 10
|
||||
37
python_library/README.md
Normal file
37
python_library/README.md
Normal file
@@ -0,0 +1,37 @@
|
||||
# exixe Raspberry Pi/Python library
|
||||
|
||||
This is a simple Python library for exixe modules intended to be used on a Raspberry Pi.
|
||||
|
||||
# Requirements
|
||||
In order to use this library, you must have python installed, SPI interface turned on the Raspberry Pi and spidev python library installed.
|
||||
## Turning on SPI
|
||||
Configuration
|
||||
The SPI peripheral is not turned on by default. To enable it, do the following.
|
||||
1. Run sudo raspi-config.
|
||||
2. Use the down arrow to select 5 Interfacing Options
|
||||
3. Arrow down to P4 SPI.
|
||||
4. Select yes when it asks you to enable SPI,
|
||||
5. Also select yes if it asks about automatically loading the kernel module.
|
||||
6. Use the right arrow to select the <Finish> button.
|
||||
7. Select yes when it asks to reboot.
|
||||
## Installing spidev
|
||||
From pip:
|
||||
pip install spidev
|
||||
From source:
|
||||
sudo apt-get update sudo apt-get install python-dev
|
||||
git clone git://github.com/doceme/py-spidev
|
||||
cd py-spidev
|
||||
sudo python setup.py install
|
||||
sudo python3 setup.py install
|
||||
#Usage
|
||||
The code has some good documentation that I don’t want to repeat but this will be a general overview on how to use the library.
|
||||
1. Create SPI initialization
|
||||
spi = spidev.SpiDev()
|
||||
spi.open(0,0)
|
||||
spi.max_speed = 7800000
|
||||
2. Identify CS pin to control the individual exixe
|
||||
3. Create exixe object
|
||||
exixe_1 = Exixe(cs_pin,spi)
|
||||
4. Control object
|
||||
exixe_1.set_digit(5)
|
||||
exixe_1.set_led(127,0,0) # turn led to red
|
||||
194
python_library/exixe.py
Normal file
194
python_library/exixe.py
Normal file
@@ -0,0 +1,194 @@
|
||||
import spidev
|
||||
import time
|
||||
import RPi.GPIO as GPIO
|
||||
|
||||
GPIO.setmode(GPIO.BOARD)
|
||||
|
||||
EXIXE_SPI_BUF_SIZE = 16
|
||||
EXIXE_SPI_HEADER = 0xAA
|
||||
EXIXE_SPI_HEADER_OD = 0xab
|
||||
EXIXE_ANIMATION_FRAME_DURATION_MS = 33
|
||||
EXIXE_ANIMATION_IN_PROGRESS = 1
|
||||
EXIXE_ANIMATION_FINISHED = 0
|
||||
MAX_BRIGHTNESS = 127
|
||||
|
||||
millis = lambda: int(round(time.time() * 1000))
|
||||
|
||||
|
||||
def cap_digit(digit):
|
||||
"""
|
||||
This function will ensure that entered digit doesnt go above 9 to represent all single digits.
|
||||
Also due to Exixe SPI technical details the 10th slot in the bytes array controls the 0 digit.
|
||||
Here is where we will convert the 0 digit to 10 to represent this correctly.
|
||||
More info here: https://github.com/dekuNukem/exixe/blob/master/technical_details.md
|
||||
"""
|
||||
digit = digit % 10
|
||||
if digit == 0:
|
||||
digit = 10
|
||||
return digit
|
||||
|
||||
|
||||
def cap_brightness(brightness):
|
||||
"""
|
||||
This function will ensure any entered brightness will not go above the MAX_BRIGHTNESS
|
||||
"""
|
||||
if brightness > MAX_BRIGHTNESS:
|
||||
brightness = MAX_BRIGHTNESS
|
||||
if brightness < 0:
|
||||
brightness = 0
|
||||
return brightness
|
||||
|
||||
|
||||
class Exixe:
|
||||
def __init__(self, cs_pin, spi, overdrive=False, initial_val=-1):
|
||||
"""
|
||||
This class is used to control an individual Exixe module's digit, and led along with providing a way to simulate
|
||||
a crossfade animation
|
||||
:param cs_pin: This is the control pin for the specific exixe. 1 per exixe module
|
||||
:param spi: spi object from the spiDev module. Only 1 spi object needs to be created for multiple exixe modules
|
||||
Example below:
|
||||
spi = spidev.SpiDev()
|
||||
spi.open(0, 0)
|
||||
spi.max_speed_hz = 7800000
|
||||
:param overdrive: (Optional) Used to enable current overdrive to make the tube brighter
|
||||
:param initial_val: (Optional) initial digit to set
|
||||
"""
|
||||
self.cs_pin = cs_pin
|
||||
self.spi = spi
|
||||
self.spi_buf = [0] * EXIXE_SPI_BUF_SIZE
|
||||
|
||||
self.animation_in_progress = False
|
||||
# Digits will be stored in values 0-9 and when needed to update SPI buffer use the cap_digit to ensure correct
|
||||
# location/index is used.
|
||||
self.animation_src_digit = 0
|
||||
self.animation_dest_digit = 0
|
||||
self.animation_start_frame = 0
|
||||
self.animation_duration = 0
|
||||
self.animation_brightness = 0
|
||||
self.animation_step = 0
|
||||
|
||||
self.red = 0
|
||||
self.green = 0
|
||||
self.blue = 0
|
||||
|
||||
self.overdrive = overdrive
|
||||
|
||||
GPIO.setup(self.cs_pin, GPIO.OUT)
|
||||
GPIO.output(self.cs_pin, True)
|
||||
if initial_val >= 0:
|
||||
self.set_digit(initial_val)
|
||||
|
||||
def set_digit(self, digit, brightness=MAX_BRIGHTNESS):
|
||||
"""
|
||||
This method will set the single digit specified and will leave the LED unaffected
|
||||
"""
|
||||
brightness = cap_brightness(brightness)
|
||||
self.animation_src_digit = digit
|
||||
self.clear_digit()
|
||||
self.set_en_bit_to_1()
|
||||
# Set first byte of header according to overdrive flag
|
||||
self.spi_buf[0] = EXIXE_SPI_HEADER_OD if self.overdrive else EXIXE_SPI_HEADER
|
||||
self.spi_buf[cap_digit(digit)] = 0x80 | brightness
|
||||
|
||||
self.spi_write()
|
||||
|
||||
def set_en_bit_to_1(self):
|
||||
"""
|
||||
set EN Bit to 1 for all digits. This will ensure that we will control all the digits when sending the SPI signal
|
||||
"""
|
||||
for i in range(0, EXIXE_SPI_BUF_SIZE - 5):
|
||||
self.spi_buf[i] = 0x80
|
||||
|
||||
def set_led(self, red, green, blue):
|
||||
"""
|
||||
This method will update the LED without changing the digits
|
||||
"""
|
||||
red = cap_brightness(red)
|
||||
green = cap_brightness(green)
|
||||
blue = cap_brightness(blue)
|
||||
self.red = red
|
||||
self.green = green
|
||||
self.blue = blue
|
||||
self.spi_buf[13] = 0
|
||||
self.spi_buf[14] = 0
|
||||
self.spi_buf[15] = 0
|
||||
|
||||
self.spi_buf[0] = EXIXE_SPI_HEADER_OD if self.overdrive else EXIXE_SPI_HEADER
|
||||
self.spi_buf[13] = 0x80 | red
|
||||
self.spi_buf[14] = 0x80 | green
|
||||
self.spi_buf[15] = 0x80 | blue
|
||||
|
||||
self.spi_write()
|
||||
|
||||
def set_dots(self, left_brightness, right_brightness):
|
||||
"""
|
||||
This method will update the left and right dots if the Nixie tube supports this.
|
||||
This does not change digits or LED
|
||||
"""
|
||||
left_brightness = cap_brightness(left_brightness)
|
||||
right_brightness = cap_brightness(right_brightness)
|
||||
|
||||
self.clear_digit()
|
||||
self.spi_buf[0] = EXIXE_SPI_HEADER_OD if self.overdrive else EXIXE_SPI_HEADER
|
||||
self.spi_buf[11] = 0x80 | left_brightness
|
||||
self.spi_buf[12] = 0x80 | right_brightness
|
||||
|
||||
self.spi_write()
|
||||
|
||||
def crossfade_init(self, digit, duration_frames, brightness=MAX_BRIGHTNESS):
|
||||
"""
|
||||
This method is used to initialize the crossfade, it will set the destination digit along with the number of
|
||||
frames that is desired for the animation. This should only be called 1 time for set up, where as the
|
||||
crossfade_run method should be called within a loop to do the updates of the animation.
|
||||
"""
|
||||
self.animation_dest_digit = digit
|
||||
self.animation_start_frame = millis() / EXIXE_ANIMATION_FRAME_DURATION_MS
|
||||
self.animation_duration = duration_frames
|
||||
self.animation_brightness = cap_brightness(brightness)
|
||||
self.animation_step = self.animation_brightness / self.animation_duration
|
||||
self.animation_in_progress = True
|
||||
|
||||
def crossfade_run(self):
|
||||
"""
|
||||
This method will update the 2 digits for animation. Where the source digit will go down in brightness and the
|
||||
destination digit will go up in brightness until source is zero and destination is at desired brightness.
|
||||
:return: The status if the crossfade animation has been completed or still running.
|
||||
"""
|
||||
current_frame = (millis() / EXIXE_ANIMATION_FRAME_DURATION_MS) - self.animation_start_frame
|
||||
|
||||
if current_frame > self.animation_duration:
|
||||
self.animation_src_digit = self.animation_dest_digit
|
||||
self.animation_in_progress = False
|
||||
return EXIXE_ANIMATION_FINISHED
|
||||
self.clear_digit()
|
||||
self.set_en_bit_to_1()
|
||||
self.spi_buf[0] = EXIXE_SPI_HEADER_OD if self.overdrive else EXIXE_SPI_HEADER
|
||||
|
||||
temp = int(cap_brightness(self.animation_step * current_frame))
|
||||
self.spi_buf[cap_digit(self.animation_src_digit)] = 0x80 | (self.animation_brightness - temp)
|
||||
self.spi_buf[cap_digit(self.animation_dest_digit)] = 0x80 | temp
|
||||
self.spi_write()
|
||||
self.animation_in_progress = True
|
||||
return EXIXE_ANIMATION_IN_PROGRESS
|
||||
|
||||
def clear(self):
|
||||
"""
|
||||
This method will clear all of the SPI buffer with zero's
|
||||
"""
|
||||
self.spi_buf = [0] * EXIXE_SPI_BUF_SIZE
|
||||
|
||||
def clear_digit(self):
|
||||
"""
|
||||
This method will clear only the slots related to the digits
|
||||
"""
|
||||
for i in range(0, EXIXE_SPI_BUF_SIZE - 5):
|
||||
self.spi_buf[i] = 0
|
||||
|
||||
def spi_write(self):
|
||||
"""
|
||||
This method will enable the CS pin and then send the SPI Buffer to the exixe driver. This method is not intended
|
||||
to be called by it self but directly from the set_led, set_digit, and set_dots
|
||||
"""
|
||||
GPIO.output(self.cs_pin, False)
|
||||
self.spi.writebytes(self.spi_buf)
|
||||
GPIO.output(self.cs_pin, True)
|
||||
Reference in New Issue
Block a user