#!/usr/bin/env python # NiteLite - a python daemon process started at system boot, and stopped on shutdown # - the default LED pattern is twinkling but if motion is detected, one of 4 # different patterns are chosen and these are used for 10s after motion detection # # Please see our GitHub repository for more information: https://github.com/pistuffing/nitelite/piglow # # Once running you'll need to press ctrl-C to cancel stop the script or run # sudo /etc/init.d/nitelited.sh stop from __future__ import division import signal import time from smbus import SMBus import RPi.GPIO as GPIO import random import math # command register addresses for the SN3218 IC used in PiGlow CMD_ENABLE_OUTPUT = 0x00 CMD_ENABLE_LEDS = 0x13 CMD_SET_PWM_VALUES = 0x01 CMD_UPDATE = 0x16 class PiGlow: i2c_addr = 0x54 # fixed i2c address of SN3218 ic bus = None def __init__(self, i2c_bus=1): self.bus = SMBus(i2c_bus) # first we tell the SN3218 to enable output (turn on) self.write_i2c(CMD_ENABLE_OUTPUT, 0x01) # then we ask it to enable each bank of LEDs (0-5, 6-11, and 12-17) self.write_i2c(CMD_ENABLE_LEDS, [0xFF, 0xFF, 0xFF]) def update_leds(self, values): self.write_i2c(CMD_SET_PWM_VALUES, values) self.write_i2c(CMD_UPDATE, 0xFF) def write_i2c(self, reg_addr, value): # if a single value is provided then wrap it in a list so we can treat if not isinstance(value, list): value = [value]; # write the data to the SN3218 self.bus.write_i2c_block_data(self.i2c_addr, reg_addr, value) LED_ARM_TOP = 0 LED_ARM_LEFT = 1 LED_ARM_RIGHT = 2 LED_COLOUR_RED = 0 LED_COLOUR_ORANGE = 1 LED_COLOUR_YELLOW = 2 LED_COLOUR_GREEN = 3 LED_COLOUR_BLUE = 4 LED_COLOUR_WHITE = 5 LED_PATTERN_TWINKLE = 0 LED_PATTERN_GLOW = 1 LED_PATTERN_SWELL = 2 LED_PATTERN_DROPLET = 3 LED_PATTERN_SNAKE = 4 #------------------------------------------------------------ # Set up the LED spiral arm / colour mappings #------------------------------------------------------------ led_map = [] for arm in range(0,3): led_map.append([]) for colour in range(0,6): led_map[arm].append(0) led_map[LED_ARM_TOP][LED_COLOUR_RED] = 6 led_map[LED_ARM_TOP][LED_COLOUR_ORANGE] = 7 led_map[LED_ARM_TOP][LED_COLOUR_YELLOW] = 8 led_map[LED_ARM_TOP][LED_COLOUR_GREEN] = 5 led_map[LED_ARM_TOP][LED_COLOUR_BLUE] = 4 led_map[LED_ARM_TOP][LED_COLOUR_WHITE] = 9 led_map[LED_ARM_LEFT][LED_COLOUR_RED] = 0 led_map[LED_ARM_LEFT][LED_COLOUR_ORANGE] = 1 led_map[LED_ARM_LEFT][LED_COLOUR_YELLOW] = 2 led_map[LED_ARM_LEFT][LED_COLOUR_GREEN] = 3 led_map[LED_ARM_LEFT][LED_COLOUR_BLUE] = 14 led_map[LED_ARM_LEFT][LED_COLOUR_WHITE] = 12 led_map[LED_ARM_RIGHT][LED_COLOUR_RED] = 17 led_map[LED_ARM_RIGHT][LED_COLOUR_ORANGE] = 16 led_map[LED_ARM_RIGHT][LED_COLOUR_YELLOW] = 15 led_map[LED_ARM_RIGHT][LED_COLOUR_GREEN] = 13 led_map[LED_ARM_RIGHT][LED_COLOUR_BLUE] = 11 led_map[LED_ARM_RIGHT][LED_COLOUR_WHITE] = 10 #------------------------------------------------------------ # Set up the LED number array #------------------------------------------------------------ leds = [] for led in range(0, 18): leds.append(0) #------------------------------------------------------------ # Set up the LED brightness array #------------------------------------------------------------ levels = [0, 1, 2, 4, 8, 16, 32, 64, 128] #------------------------------------------------------------ # Set up the shutdown handler #------------------------------------------------------------ def ShutdownHandler(signal, frame): global keep_looping keep_looping = False #------------------------------------------------------------ # Set up the PIR movement detection callback #------------------------------------------------------------ def PIRCallback(channel): global motion_detected_time global led_pattern global leds if led_pattern == LED_PATTERN_TWINKLE: led_pattern = random.randint(1, 4) motion_detected_time = time.time() #------------------------------------------------------------ # Set up the PIR movement detection #------------------------------------------------------------ GPIO_PIR = 18 GPIO.setmode(GPIO.BOARD) GPIO.setup(GPIO_PIR, GPIO.IN, GPIO.PUD_DOWN) GPIO.add_event_detect(GPIO_PIR, GPIO.RISING, PIRCallback, 0) #------------------------------------------------------------ # Final steps of setup #------------------------------------------------------------ signal.signal(signal.SIGINT, ShutdownHandler) piglow = PiGlow(1) keep_looping = True motion_detected_time = time.time() - 5.1 while keep_looping: #------------------------------------------------------------ # Drop back to the default LED pattern? #------------------------------------------------------------ if time.time() - motion_detected_time >= 5.0: led_pattern = LED_PATTERN_TWINKLE twinkle_count = 0 #------------------------------------------------------------ # TWINKLE: 0: Random LED lit with random, decaying brightness #------------------------------------------------------------ if led_pattern == LED_PATTERN_TWINKLE: # dim all lit LEDs by one step in the levels list # first find the index into the brightness list # This relies on the fact that values in the levels # list are all 2^n for led in range(0, 18): mant, level = math.frexp(leds[led]) if mant == 0.0: level = 0 if level > 0: leds[led] = levels[level - 1] # Add a random LED every 5 cycles with random brightness if twinkle_count == 0: leds[random.randint(0, 17)] = levels[random.randint(0, 8)] twinkle_count = (twinkle_count + 1) % 10 piglow.update_leds(leds) time.sleep(0.1) #------------------------------------------------------------ # GLOW: 1; All LEDs glow at a low level #------------------------------------------------------------ elif led_pattern == LED_PATTERN_GLOW: for led in range(0, 18): leds[led] = levels[4] piglow.update_leds(leds) #------------------------------------------------------------ # SWELL: 2; All LEDs brightness swelling up and down #------------------------------------------------------------ elif led_pattern == LED_PATTERN_SWELL: for level in range(0, 8): for led in range(0, 18): leds[led] = levels[level] piglow.update_leds(leds) time.sleep(0.1) for level in range(8, 0, -1): for led in range(0, 18): leds[led] = levels[level] piglow.update_leds(leds) time.sleep(0.1) #------------------------------------------------------------ # DROPLET 3; Same colour sweeping up and down all the arms together at fixed brightness #------------------------------------------------------------ elif led_pattern == LED_PATTERN_DROPLET: for colour in range(0, 5): for arm in range(0, 3): leds[led_map[arm][colour]] = 0x80 piglow.update_leds(leds) for arm in range(0,3): leds[led_map[arm][colour]] = 0x00 time.sleep(0.1) for colour in range(5, 0, -1): for arm in range(0,3): leds[led_map[arm][colour]] = 0x80 piglow.update_leds(leds) for arm in range(0,3): leds[led_map[arm][colour]] = 0x00 time.sleep(0.1) #------------------------------------------------------------ # SNAKE: 4; Trace up and down each arm ini turn getting brighter towards the centre #------------------------------------------------------------ elif led_pattern == LED_PATTERN_SNAKE: for arm in range(0, 3): for colour in range (0, 5): leds[led_map[arm][colour]] = levels[colour + 1] piglow.update_leds(leds) time.sleep(0.1) for colour in range (5, 0, -1): leds[led_map[arm][colour]] = levels[colour + 1] piglow.update_leds(leds) time.sleep(0.1) for colour in range(0, 6): leds[led_map[arm][colour]] = 0x00 # set all the LEDs to "off" when Ctrl+C is pressed before exiting for led in range(0, 18): leds[led] = 0x0 piglow.update_leds(leds)