Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
branch: master
Fetching contributors…

Cannot retrieve contributors at this time

file 201 lines (162 sloc) 5.137 kb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201
#!/usr/bin/python

# A web.py application for light painting with the Adafruit Digital Adressable
# RGB LED flex strip. The web application lets users choose an image, which is
# then played column by column on the light strip.
#
# With a Raspberry Pi running a DHCP server and configured to set up an ad-hoc
# wireless network on boot, this script runs a web.py web server that serves a
# simple page that can configure and control the RGB strip via any device that
# connects to the network and browses to the URL of the server.

# Adafruit Digital Addressable RGB LED flex strip.
# ----> http://adafruit.com/products/306

import RPi.GPIO as GPIO, Image, time
from math import floor
from PIL import Image
from threading import Timer
import thread
import web
import os
import json

# Paths for web.py
urls = (
  '/static/.+', 'static',
  '/upload/(.+)', 'upload',
  '/start', 'start',
  '/stop', 'stop',
  '/pause', 'pause',
  '/continue', 'resume',
  '/faster', 'faster',
  '/slower', 'slower',
  '/status', 'status',
  '/', 'index'
)

# Configurable values
dev = "/dev/spidev0.0"

spidev = file(dev, "wb")

# Setup of web.py templates
render = web.template.render('templates/', globals={'os': os})

class LightStick:
  """Represent the state of a light stick"""
  def __init__(self):
    self.paused = False
    self.updateRate = 0.8
    self.working = False

  def get_status_string(self):
    if self.paused:
      return 'paused'
    if self.working:
      return 'running'
    else:
      return 'stopped'

  def faster(self, amount):
    if self.updateRate - amount > 0:
      self.updateRate -= amount

  def slower(self, amount):
    self.updateRate += amount

  def start(self, filename="london.png"):
    if self.working:
      self.stop()

    self.working = True
    print "Loading..."

    img = Image.open(filename).convert("RGB")
    pixels = img.load()
    width = img.size[0]
    height = img.size[1]
    print "Loaded image of %dx%d pixels" % img.size

    # Create list of bytearrays, one for each column of image.
    # R, G, B byte per pixel, plus extra '0' byte at end for latch.
    print "Allocating..."
    self.column = [0 for x in range(width + 1)]
    self.blackColumn = bytearray(height * 3 + 1)

    for x in range(width + 1):
      self.column[x] = bytearray(height * 3 + 1)

    for x in range(0, width):
      for y in range(0, height):
        self.column[x][y * 3] = 0x80 | (pixels[x,y][1] >> 2)
        self.column[x][y * 3 + 1] = 0x80 | (pixels[x,y][0] >> 2)
        self.column[x][y * 3 + 2] = 0x80 | (pixels[x,y][2] >> 2)

    # Set the last column to black
    for y in range(0, height):
      self.column[width][y * 3] = 0x80
      self.column[width][y * 3 + 1] = 0x80
      self.column[width][y * 3 + 2] = 0x80

    print "Rendering..."

    self.working == True
    thread.start_new_thread(self.step, ())

  def stop(self):
    self.working = False

  def step(self):
    while self.working:
      if not self.paused:
        for col in self.column:
          spidev.write(col)
        time.sleep(self.updateRate)
      else:
        time.sleep(0.5)

    spidev.write(self.blackColumn)
    spidev.flush()

  def pause(self):
    self.paused = True

  def resume(self):
    self.paused = False

class static:
  """Serves all of the static assets in the static folder"""
  def GET(self, path):
    print("SERVING STATIC :D")
    raise web.seeother(path)

class upload:
  """Serves all of the static assets in the static folder"""
  def GET(self, path):
    web.header("Content-Type", "image/png")
    return open('upload/%s'%path,"rb").read()
    raise web.seeother('/')

class index:
  """Main web app entry point"""
  def GET(self):
    return render.index()

  def POST(self):
    """Allow people to upload files into the /upload directory."""
    x = web.input(fileUpload={})
    uploadDir = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'upload')
    filepath=x['fileUpload'].filename.replace('\\','/') # replaces the windows-style slashes with linux ones.
    filename=filepath.split('/')[-1] # splits the and chooses the last part (the filename with extension)
    fout = open(os.path.join(uploadDir, filename),'w')
    fout.write(x['fileUpload'].file.read())
    fout.close()
    raise web.seeother('/upload')

class start:
  """Start the time lapse"""
  def GET(self):
    lightstick.start()
    return "Success"

  def POST(self):
    data = web.input()
    lightstick.start(data.image)

class stop:
  """Stop the time lapse"""
  def GET(self):
    lightstick.stop()
    return "Success"

class pause:
  """Pause the time lapse"""
  def GET(self):
    lightstick.pause()
    return "Success"

class resume:
  """Resume the lightstick after it was paused"""
  def GET(self):
    lightstick.resume()
    return "Success"

class faster:
  def GET(self):
    lightstick.faster(0.05)

class slower:
  def GET(self):
    lightstick.slower(0.05)

class status:
  def GET(self):
    return json.dumps({'status' : lightstick.get_status_string()})

lightstick = LightStick()

if __name__ == "__main__":
  app = web.application(urls, globals())
  app.run()
Something went wrong with that request. Please try again.