Browse Source

first commit

satoshi yoneda 6 years ago
commit
2a3ac8e0b5

+ 11 - 0
.gitattributes

@@ -0,0 +1,11 @@
+*.jpg binary
+*.gif binary
+*.png binary
+*.img binary
+*.zip binary
+*.dtbo binary
+*.img -diff -text
+*.zip -diff -text
+*.jpg -diff -text
+*.png -diff -text
+*.dtbo -diff -text

+ 10 - 0
.gitignore

@@ -0,0 +1,10 @@
+temp
+image
+build
+dist
+*.egg-info
+*.pyc
+setuptools-*
+__pycache__
+*.img
+

+ 5 - 0
libs/Python_ST7735/.gitignore

@@ -0,0 +1,5 @@
+build
+dist
+*.egg-info
+*.pyc
+setuptools-*

+ 21 - 0
libs/Python_ST7735/LICENSE

@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2014 Adafruit Industries
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.

+ 40 - 0
libs/Python_ST7735/README.md

@@ -0,0 +1,40 @@
+Python ST7735
+=======================
+
+Python library to control an ST7735 TFT LCD display.  Allows simple drawing on the display without installing a kernel module.
+
+Designed specifically to work with a ST7735 based 128x160 pixel TFT SPI display.
+
+For all platforms (Raspberry Pi and Beaglebone Black) make sure you have the following dependencies:
+
+````
+sudo apt-get update
+sudo apt-get install build-essential python-dev python-smbus python-pip python-imaging python-numpy
+````
+
+For a Raspberry Pi make sure you have the RPi.GPIO and Adafruit GPIO libraries by executing:
+
+````
+sudo pip install RPi.GPIO
+sudo pip install Adafruit_GPIO
+````
+
+For a BeagleBone Black make sure you have the Adafruit_BBIO library by executing:
+
+````
+sudo pip install Adafruit_BBIO
+````
+
+Install the library by downloading with the download link on the right, unzipping the archive, navigating inside the library's directory and executing:
+
+````
+sudo python setup.py install
+````
+
+See example of usage in the examples folder.
+
+Adafruit invests time and resources providing this open source code, please support Adafruit and open-source hardware by purchasing products from Adafruit!
+
+Modified from 'Adafruit Python ILI9341' written by Tony DiCola for Adafruit Industries.
+
+MIT license, all text above must be included in any redistribution

+ 362 - 0
libs/Python_ST7735/ST7735/ST7735.py

@@ -0,0 +1,362 @@
+# Copyright (c) 2014 Adafruit Industries
+# Author: Tony DiCola
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+import numbers
+import time
+import numpy as np
+
+from PIL import Image
+from PIL import ImageDraw
+
+import Adafruit_GPIO as GPIO
+import Adafruit_GPIO.SPI as SPI
+
+
+# SPI_CLOCK_HZ = 64000000 # 64 MHz
+SPI_CLOCK_HZ = 4000000 # 4 MHz
+
+
+# Constants for interacting with display registers.
+ST7735_TFTWIDTH    = 128
+ST7735_TFTHEIGHT   = 160
+
+ST7735_NOP         = 0x00
+ST7735_SWRESET     = 0x01
+ST7735_RDDID       = 0x04
+ST7735_RDDST       = 0x09
+
+ST7735_SLPIN       = 0x10
+ST7735_SLPOUT      = 0x11
+ST7735_PTLON       = 0x12
+ST7735_NORON       = 0x13
+
+# ILI9341_RDMODE      = 0x0A
+# ILI9341_RDMADCTL    = 0x0B
+# ILI9341_RDPIXFMT    = 0x0C
+# ILI9341_RDIMGFMT    = 0x0A
+# ILI9341_RDSELFDIAG  = 0x0F
+
+ST7735_INVOFF      = 0x20
+ST7735_INVON       = 0x21
+# ILI9341_GAMMASET    = 0x26
+ST7735_DISPOFF     = 0x28
+ST7735_DISPON      = 0x29
+
+ST7735_CASET       = 0x2A
+ST7735_RASET       = 0x2B
+ST7735_RAMWR       = 0x2C
+ST7735_RAMRD       = 0x2E
+
+ST7735_PTLAR       = 0x30
+ST7735_MADCTL      = 0x36
+# ST7735_PIXFMT      = 0x3A
+ST7735_COLMOD       = 0x3A
+
+ST7735_FRMCTR1     = 0xB1
+ST7735_FRMCTR2     = 0xB2
+ST7735_FRMCTR3     = 0xB3
+ST7735_INVCTR      = 0xB4
+# ILI9341_DFUNCTR     = 0xB6
+ST7735_DISSET5      = 0xB6
+
+
+ST7735_PWCTR1      = 0xC0
+ST7735_PWCTR2      = 0xC1
+ST7735_PWCTR3      = 0xC2
+ST7735_PWCTR4      = 0xC3
+ST7735_PWCTR5      = 0xC4
+ST7735_VMCTR1      = 0xC5
+# ILI9341_VMCTR2      = 0xC7
+
+ST7735_RDID1       = 0xDA
+ST7735_RDID2       = 0xDB
+ST7735_RDID3       = 0xDC
+ST7735_RDID4       = 0xDD
+
+ST7735_GMCTRP1     = 0xE0
+ST7735_GMCTRN1     = 0xE1
+
+ST7735_PWCTR6      = 0xFC
+
+# Colours for convenience
+ST7735_BLACK       = 0x0000 # 0b 00000 000000 00000
+ST7735_BLUE        = 0x001F # 0b 00000 000000 11111
+ST7735_GREEN       = 0x07E0 # 0b 00000 111111 00000
+ST7735_RED         = 0xF800 # 0b 11111 000000 00000
+ST7735_CYAN        = 0x07FF # 0b 00000 111111 11111
+ST7735_MAGENTA     = 0xF81F # 0b 11111 000000 11111
+ST7735_YELLOW      = 0xFFE0 # 0b 11111 111111 00000
+ST7735_WHITE       = 0xFFFF # 0b 11111 111111 11111
+
+
+def color565(r, g, b):
+    """Convert red, green, blue components to a 16-bit 565 RGB value. Components
+    should be values 0 to 255.
+    """
+    return ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3)
+
+def image_to_data(image):
+    """Generator function to convert a PIL image to 16-bit 565 RGB bytes."""
+    # NumPy is much faster at doing this. NumPy code provided by:
+    # Keith (https://www.blogger.com/profile/02555547344016007163)
+    pb = np.array(image.convert('RGB')).astype('uint16')
+#    color = ((pb[:,:,0] & 0xF8) << 8) | ((pb[:,:,1] & 0xFC) << 3) | (pb[:,:,2] >> 3)
+    color = ((pb[:,:,2] & 0xF8) << 8) | ((pb[:,:,1] & 0xFC) << 3) | (pb[:,:,0] >> 3)
+
+    return np.dstack(((color >> 8) & 0xFF, color & 0xFF)).flatten().tolist()
+
+class ST7735(object):
+    """Representation of an ST7735 TFT LCD."""
+
+    def __init__(self, dc, spi, rst=None, gpio=None, width=ST7735_TFTWIDTH,
+        height=ST7735_TFTHEIGHT):
+        """Create an instance of the display using SPI communication.  Must
+        provide the GPIO pin number for the D/C pin and the SPI driver.  Can
+        optionally provide the GPIO pin number for the reset pin as the rst
+        parameter.
+        """
+        self._dc = dc
+        self._rst = rst
+        self._spi = spi
+        self._gpio = gpio
+        self.width = width
+        self.height = height
+        if self._gpio is None:
+            self._gpio = GPIO.get_platform_gpio()
+        # Set DC as output.
+        self._gpio.setup(dc, GPIO.OUT)
+        # Setup reset as output (if provided).
+        if rst is not None:
+            self._gpio.setup(rst, GPIO.OUT)
+        # Set SPI to mode 0, MSB first.
+        spi.set_mode(0)
+        spi.set_bit_order(SPI.MSBFIRST)
+        spi.set_clock_hz(SPI_CLOCK_HZ)
+        # Create an image buffer.
+        self.buffer = Image.new('RGB', (width, height))
+
+    def send(self, data, is_data=True, chunk_size=4096):
+        """Write a byte or array of bytes to the display. Is_data parameter
+        controls if byte should be interpreted as display data (True) or command
+        data (False).  Chunk_size is an optional size of bytes to write in a
+        single SPI transaction, with a default of 4096.
+        """
+        # Set DC low for command, high for data.
+        self._gpio.output(self._dc, is_data)
+        # Convert scalar argument to list so either can be passed as parameter.
+        if isinstance(data, numbers.Number):
+            data = [data & 0xFF]
+        # Write data a chunk at a time.
+        for start in range(0, len(data), chunk_size):
+            end = min(start+chunk_size, len(data))
+            self._spi.write(data[start:end])
+
+    def command(self, data):
+        """Write a byte or array of bytes to the display as command data."""
+        self.send(data, False)
+
+    def data(self, data):
+        """Write a byte or array of bytes to the display as display data."""
+        self.send(data, True)
+
+    def reset(self):
+        """Reset the display, if reset pin is connected."""
+        if self._rst is not None:
+            self._gpio.set_high(self._rst)
+            time.sleep(0.500)
+            self._gpio.set_low(self._rst)
+            time.sleep(0.500)
+            self._gpio.set_high(self._rst)
+            time.sleep(0.500)
+
+    def _init(self):
+        # Initialize the display.  Broken out as a separate function so it can
+        # be overridden by other displays in the future.
+        
+        self.command(ST7735_SWRESET) # Software reset
+        time.sleep(0.150) # delay 150 ms
+        
+        self.command(ST7735_SLPOUT) # Out of sleep mode
+        time.sleep(0.500) # delay 500 ms
+        
+        self.command(ST7735_FRMCTR1) # Frame rate ctrl - normal mode
+        self.data(0x01) # Rate = fosc/(1x2+40) * (LINE+2C+2D)
+        self.data(0x2C)
+        self.data(0x2D)
+        
+        self.command(ST7735_FRMCTR2) # Frame rate ctrl - idle mode
+        self.data(0x01) # Rate = fosc/(1x2+40) * (LINE+2C+2D)
+        self.data(0x2C)
+        self.data(0x2D)
+        
+        self.command(ST7735_FRMCTR3) # Frame rate ctrl - partial mode
+        self.data(0x01) # Dot inversion mode
+        self.data(0x2C)
+        self.data(0x2D)
+        self.data(0x01) # Line inversion mode
+        self.data(0x2C)
+        self.data(0x2D)
+        
+        self.command(ST7735_INVCTR) # Display inversion ctrl
+        self.data(0x07) # No inversion
+        
+        self.command(ST7735_PWCTR1) # Power control
+        self.data(0xA2)
+        self.data(0x02) # -4.6V
+        self.data(0x84) # auto mode
+        
+        self.command(ST7735_PWCTR2) # Power control
+        self.data(0x0A) # Opamp current small
+        self.data(0x00) # Boost frequency
+        
+        self.command(ST7735_PWCTR4) # Power control
+        self.data(0x8A) # BCLK/2, Opamp current small & Medium low
+        self.data(0x2A)
+        
+        self.command(ST7735_PWCTR5) # Power control
+        self.data(0x8A)
+        self.data(0xEE)
+        
+        self.command(ST7735_VMCTR1) # Power control
+        self.data(0x0E)
+        
+        self.command(ST7735_INVOFF) # Don't invert display
+        
+        self.command(ST7735_MADCTL) # Memory access control (directions)
+        self.data(0xC8) # row addr/col addr, bottom to top refresh
+        
+        self.command(ST7735_COLMOD) # set color mode
+        self.data(0x05) # 16-bit color
+        
+        #
+        
+        self.command(ST7735_CASET) # Column addr set
+        self.data(0x00) # XSTART = 0
+        self.data(0x00)
+        self.data(0x00) # XEND = 127
+        self.data(0x7F)
+        
+        self.command(ST7735_RASET) # Row addr set
+        self.data(0x00) # XSTART = 0
+        self.data(0x00)
+        self.data(0x00) # XEND = 159
+        self.data(0x9F)
+        
+        #
+        
+        self.command(ST7735_GMCTRP1) # Set Gamma
+        self.data(0x02)
+        self.data(0x1c)
+        self.data(0x07)
+        self.data(0x12)
+        self.data(0x37)
+        self.data(0x32)
+        self.data(0x29)
+        self.data(0x2d)
+        self.data(0x29)
+        self.data(0x25)
+        self.data(0x2B)
+        self.data(0x39)
+        self.data(0x00)
+        self.data(0x01)
+        self.data(0x03)
+        self.data(0x10)
+        
+        self.command(ST7735_GMCTRN1) # Set Gamma
+        self.data(0x03)
+        self.data(0x1d)
+        self.data(0x07)
+        self.data(0x06)
+        self.data(0x2E)
+        self.data(0x2C)
+        self.data(0x29)
+        self.data(0x2D)
+        self.data(0x2E)
+        self.data(0x2E)
+        self.data(0x37)
+        self.data(0x3F)
+        self.data(0x00)
+        self.data(0x00)
+        self.data(0x02)
+        self.data(0x10)
+        
+        self.command(ST7735_NORON) # Normal display on
+        time.sleep(0.10) # 10 ms
+        
+        self.command(ST7735_DISPON) # Display on
+        time.sleep(0.100) # 100 ms
+
+    def begin(self):
+        """Initialize the display.  Should be called once before other calls that
+        interact with the display are called.
+        """
+        self.reset()
+        self._init()
+
+    def set_window(self, x0=0, y0=0, x1=None, y1=None):
+        """Set the pixel address window for proceeding drawing commands. x0 and
+        x1 should define the minimum and maximum x pixel bounds.  y0 and y1
+        should define the minimum and maximum y pixel bound.  If no parameters
+        are specified the default will be to update the entire display from 0,0
+        to width-1,height-1.
+        """
+        if x1 is None:
+            x1 = self.width-1
+        if y1 is None:
+            y1 = self.height-1
+        self.command(ST7735_CASET)        # Column addr set
+        self.data(x0 >> 8)
+        self.data(x0)                    # XSTART
+        self.data(x1 >> 8)
+        self.data(x1)                    # XEND
+        self.command(ST7735_RASET)        # Row addr set
+        self.data(y0 >> 8)
+        self.data(y0)                    # YSTART
+        self.data(y1 >> 8)
+        self.data(y1)                    # YEND
+        self.command(ST7735_RAMWR)        # write to RAM
+
+    def display(self, image=None ,x0=0, y0=0, x1=None, y1=None):
+        """Write the display buffer or provided image to the hardware.  If no
+        image parameter is provided the display buffer will be written to the
+        hardware.  If an image is provided, it should be RGB format and the
+        same dimensions as the display hardware.
+        """
+        # By default write the internal buffer to the display.
+        if image is None:
+            image = self.buffer
+        # Set address bounds to entire display.
+        self.set_window(x0, y0, x1, y1)
+        # Convert image to array of 16bit 565 RGB data bytes.
+        # Unfortunate that this copy has to occur, but the SPI byte writing
+        # function needs to take an array of bytes and PIL doesn't natively
+        # store images in 16-bit 565 RGB format.
+        pixelbytes = list(image_to_data(image))
+        # Write data to hardware.
+        self.data(pixelbytes)
+
+    def clear(self, color=(0,0,0)):
+        """Clear the image buffer to the specified RGB color (default black)."""
+        width, height = self.buffer.size
+        self.buffer.putdata([color]*(width*height))
+
+    def draw(self):
+        """Return a PIL ImageDraw instance for 2D drawing on the image buffer."""
+        return ImageDraw.Draw(self.buffer)

+ 362 - 0
libs/Python_ST7735/ST7735/ST7735.py.orig

@@ -0,0 +1,362 @@
+# Copyright (c) 2014 Adafruit Industries
+# Author: Tony DiCola
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+import numbers
+import time
+import numpy as np
+
+from PIL import Image
+from PIL import ImageDraw
+
+import Adafruit_GPIO as GPIO
+import Adafruit_GPIO.SPI as SPI
+
+
+# SPI_CLOCK_HZ = 64000000 # 64 MHz
+SPI_CLOCK_HZ = 4000000 # 4 MHz
+
+
+# Constants for interacting with display registers.
+ST7735_TFTWIDTH    = 128
+ST7735_TFTHEIGHT   = 160
+
+ST7735_NOP         = 0x00
+ST7735_SWRESET     = 0x01
+ST7735_RDDID       = 0x04
+ST7735_RDDST       = 0x09
+
+ST7735_SLPIN       = 0x10
+ST7735_SLPOUT      = 0x11
+ST7735_PTLON       = 0x12
+ST7735_NORON       = 0x13
+
+# ILI9341_RDMODE      = 0x0A
+# ILI9341_RDMADCTL    = 0x0B
+# ILI9341_RDPIXFMT    = 0x0C
+# ILI9341_RDIMGFMT    = 0x0A
+# ILI9341_RDSELFDIAG  = 0x0F
+
+ST7735_INVOFF      = 0x20
+ST7735_INVON       = 0x21
+# ILI9341_GAMMASET    = 0x26
+ST7735_DISPOFF     = 0x28
+ST7735_DISPON      = 0x29
+
+ST7735_CASET       = 0x2A
+ST7735_RASET       = 0x2B
+ST7735_RAMWR       = 0x2C
+ST7735_RAMRD       = 0x2E
+
+ST7735_PTLAR       = 0x30
+ST7735_MADCTL      = 0x36
+# ST7735_PIXFMT      = 0x3A
+ST7735_COLMOD       = 0x3A
+
+ST7735_FRMCTR1     = 0xB1
+ST7735_FRMCTR2     = 0xB2
+ST7735_FRMCTR3     = 0xB3
+ST7735_INVCTR      = 0xB4
+# ILI9341_DFUNCTR     = 0xB6
+ST7735_DISSET5      = 0xB6
+
+
+ST7735_PWCTR1      = 0xC0
+ST7735_PWCTR2      = 0xC1
+ST7735_PWCTR3      = 0xC2
+ST7735_PWCTR4      = 0xC3
+ST7735_PWCTR5      = 0xC4
+ST7735_VMCTR1      = 0xC5
+# ILI9341_VMCTR2      = 0xC7
+
+ST7735_RDID1       = 0xDA
+ST7735_RDID2       = 0xDB
+ST7735_RDID3       = 0xDC
+ST7735_RDID4       = 0xDD
+
+ST7735_GMCTRP1     = 0xE0
+ST7735_GMCTRN1     = 0xE1
+
+ST7735_PWCTR6      = 0xFC
+
+# Colours for convenience
+ST7735_BLACK       = 0x0000 # 0b 00000 000000 00000
+ST7735_BLUE        = 0x001F # 0b 00000 000000 11111
+ST7735_GREEN       = 0x07E0 # 0b 00000 111111 00000
+ST7735_RED         = 0xF800 # 0b 11111 000000 00000
+ST7735_CYAN        = 0x07FF # 0b 00000 111111 11111
+ST7735_MAGENTA     = 0xF81F # 0b 11111 000000 11111
+ST7735_YELLOW      = 0xFFE0 # 0b 11111 111111 00000
+ST7735_WHITE       = 0xFFFF # 0b 11111 111111 11111
+
+
+def color565(r, g, b):
+    """Convert red, green, blue components to a 16-bit 565 RGB value. Components
+    should be values 0 to 255.
+    """
+    return ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3)
+
+def image_to_data(image):
+    """Generator function to convert a PIL image to 16-bit 565 RGB bytes."""
+    # NumPy is much faster at doing this. NumPy code provided by:
+    # Keith (https://www.blogger.com/profile/02555547344016007163)
+    pb = np.array(image.convert('RGB')).astype('uint16')
+#    color = ((pb[:,:,0] & 0xF8) << 8) | ((pb[:,:,1] & 0xFC) << 3) | (pb[:,:,2] >> 3)
+    color = ((pb[:,:,2] & 0xF8) << 8) | ((pb[:,:,1] & 0xFC) << 3) | (pb[:,:,0] >> 3)
+
+    return np.dstack(((color >> 8) & 0xFF, color & 0xFF)).flatten().tolist()
+
+class ST7735(object):
+    """Representation of an ST7735 TFT LCD."""
+
+    def __init__(self, dc, spi, rst=None, gpio=None, width=ST7735_TFTWIDTH,
+        height=ST7735_TFTHEIGHT):
+        """Create an instance of the display using SPI communication.  Must
+        provide the GPIO pin number for the D/C pin and the SPI driver.  Can
+        optionally provide the GPIO pin number for the reset pin as the rst
+        parameter.
+        """
+        self._dc = dc
+        self._rst = rst
+        self._spi = spi
+        self._gpio = gpio
+        self.width = width
+        self.height = height
+        if self._gpio is None:
+            self._gpio = GPIO.get_platform_gpio()
+        # Set DC as output.
+        self._gpio.setup(dc, GPIO.OUT)
+        # Setup reset as output (if provided).
+        if rst is not None:
+            self._gpio.setup(rst, GPIO.OUT)
+        # Set SPI to mode 0, MSB first.
+        spi.set_mode(0)
+        spi.set_bit_order(SPI.MSBFIRST)
+        spi.set_clock_hz(SPI_CLOCK_HZ)
+        # Create an image buffer.
+        self.buffer = Image.new('RGB', (width, height))
+
+    def send(self, data, is_data=True, chunk_size=4096):
+        """Write a byte or array of bytes to the display. Is_data parameter
+        controls if byte should be interpreted as display data (True) or command
+        data (False).  Chunk_size is an optional size of bytes to write in a
+        single SPI transaction, with a default of 4096.
+        """
+        # Set DC low for command, high for data.
+        self._gpio.output(self._dc, is_data)
+        # Convert scalar argument to list so either can be passed as parameter.
+        if isinstance(data, numbers.Number):
+            data = [data & 0xFF]
+        # Write data a chunk at a time.
+        for start in range(0, len(data), chunk_size):
+            end = min(start+chunk_size, len(data))
+            self._spi.write(data[start:end])
+
+    def command(self, data):
+        """Write a byte or array of bytes to the display as command data."""
+        self.send(data, False)
+
+    def data(self, data):
+        """Write a byte or array of bytes to the display as display data."""
+        self.send(data, True)
+
+    def reset(self):
+        """Reset the display, if reset pin is connected."""
+        if self._rst is not None:
+            self._gpio.set_high(self._rst)
+            time.sleep(0.500)
+            self._gpio.set_low(self._rst)
+            time.sleep(0.500)
+            self._gpio.set_high(self._rst)
+            time.sleep(0.500)
+
+    def _init(self):
+        # Initialize the display.  Broken out as a separate function so it can
+        # be overridden by other displays in the future.
+        
+        self.command(ST7735_SWRESET) # Software reset
+        time.sleep(0.150) # delay 150 ms
+        
+        self.command(ST7735_SLPOUT) # Out of sleep mode
+        time.sleep(0.500) # delay 500 ms
+        
+        self.command(ST7735_FRMCTR1) # Frame rate ctrl - normal mode
+        self.data(0x01) # Rate = fosc/(1x2+40) * (LINE+2C+2D)
+        self.data(0x2C)
+        self.data(0x2D)
+        
+        self.command(ST7735_FRMCTR2) # Frame rate ctrl - idle mode
+        self.data(0x01) # Rate = fosc/(1x2+40) * (LINE+2C+2D)
+        self.data(0x2C)
+        self.data(0x2D)
+        
+        self.command(ST7735_FRMCTR3) # Frame rate ctrl - partial mode
+        self.data(0x01) # Dot inversion mode
+        self.data(0x2C)
+        self.data(0x2D)
+        self.data(0x01) # Line inversion mode
+        self.data(0x2C)
+        self.data(0x2D)
+        
+        self.command(ST7735_INVCTR) # Display inversion ctrl
+        self.data(0x07) # No inversion
+        
+        self.command(ST7735_PWCTR1) # Power control
+        self.data(0xA2)
+        self.data(0x02) # -4.6V
+        self.data(0x84) # auto mode
+        
+        self.command(ST7735_PWCTR2) # Power control
+        self.data(0x0A) # Opamp current small
+        self.data(0x00) # Boost frequency
+        
+        self.command(ST7735_PWCTR4) # Power control
+        self.data(0x8A) # BCLK/2, Opamp current small & Medium low
+        self.data(0x2A)
+        
+        self.command(ST7735_PWCTR5) # Power control
+        self.data(0x8A)
+        self.data(0xEE)
+        
+        self.command(ST7735_VMCTR1) # Power control
+        self.data(0x0E)
+        
+        self.command(ST7735_INVOFF) # Don't invert display
+        
+        self.command(ST7735_MADCTL) # Memory access control (directions)
+        self.data(0xC8) # row addr/col addr, bottom to top refresh
+        
+        self.command(ST7735_COLMOD) # set color mode
+        self.data(0x05) # 16-bit color
+        
+        #
+        
+        self.command(ST7735_CASET) # Column addr set
+        self.data(0x00) # XSTART = 0
+        self.data(0x00)
+        self.data(0x00) # XEND = 127
+        self.data(0x7F)
+        
+        self.command(ST7735_RASET) # Row addr set
+        self.data(0x00) # XSTART = 0
+        self.data(0x00)
+        self.data(0x00) # XEND = 159
+        self.data(0x9F)
+        
+        #
+        
+        self.command(ST7735_GMCTRP1) # Set Gamma
+        self.data(0x02)
+        self.data(0x1c)
+        self.data(0x07)
+        self.data(0x12)
+        self.data(0x37)
+        self.data(0x32)
+        self.data(0x29)
+        self.data(0x2d)
+        self.data(0x29)
+        self.data(0x25)
+        self.data(0x2B)
+        self.data(0x39)
+        self.data(0x00)
+        self.data(0x01)
+        self.data(0x03)
+        self.data(0x10)
+        
+        self.command(ST7735_GMCTRN1) # Set Gamma
+        self.data(0x03)
+        self.data(0x1d)
+        self.data(0x07)
+        self.data(0x06)
+        self.data(0x2E)
+        self.data(0x2C)
+        self.data(0x29)
+        self.data(0x2D)
+        self.data(0x2E)
+        self.data(0x2E)
+        self.data(0x37)
+        self.data(0x3F)
+        self.data(0x00)
+        self.data(0x00)
+        self.data(0x02)
+        self.data(0x10)
+        
+        self.command(ST7735_NORON) # Normal display on
+        time.sleep(0.10) # 10 ms
+        
+        self.command(ST7735_DISPON) # Display on
+        time.sleep(0.100) # 100 ms
+
+    def begin(self):
+        """Initialize the display.  Should be called once before other calls that
+        interact with the display are called.
+        """
+        self.reset()
+        self._init()
+
+    def set_window(self, x0=0, y0=0, x1=None, y1=None):
+        """Set the pixel address window for proceeding drawing commands. x0 and
+        x1 should define the minimum and maximum x pixel bounds.  y0 and y1
+        should define the minimum and maximum y pixel bound.  If no parameters
+        are specified the default will be to update the entire display from 0,0
+        to width-1,height-1.
+        """
+        if x1 is None:
+            x1 = self.width-1
+        if y1 is None:
+            y1 = self.height-1
+        self.command(ST7735_CASET)        # Column addr set
+        self.data(x0 >> 8)
+        self.data(x0)                    # XSTART
+        self.data(x1 >> 8)
+        self.data(x1)                    # XEND
+        self.command(ST7735_RASET)        # Row addr set
+        self.data(y0 >> 8)
+        self.data(y0)                    # YSTART
+        self.data(y1 >> 8)
+        self.data(y1)                    # YEND
+        self.command(ST7735_RAMWR)        # write to RAM
+
+    def display(self, image=None):
+        """Write the display buffer or provided image to the hardware.  If no
+        image parameter is provided the display buffer will be written to the
+        hardware.  If an image is provided, it should be RGB format and the
+        same dimensions as the display hardware.
+        """
+        # By default write the internal buffer to the display.
+        if image is None:
+            image = self.buffer
+        # Set address bounds to entire display.
+        self.set_window()
+        # Convert image to array of 16bit 565 RGB data bytes.
+        # Unfortunate that this copy has to occur, but the SPI byte writing
+        # function needs to take an array of bytes and PIL doesn't natively
+        # store images in 16-bit 565 RGB format.
+        pixelbytes = list(image_to_data(image))
+        # Write data to hardware.
+        self.data(pixelbytes)
+
+    def clear(self, color=(0,0,0)):
+        """Clear the image buffer to the specified RGB color (default black)."""
+        width, height = self.buffer.size
+        self.buffer.putdata([color]*(width*height))
+
+    def draw(self):
+        """Return a PIL ImageDraw instance for 2D drawing on the image buffer."""
+        return ImageDraw.Draw(self.buffer)

+ 21 - 0
libs/Python_ST7735/ST7735/__init__.py

@@ -0,0 +1,21 @@
+# Copyright (c) 2014 Adafruit Industries
+# Author: Tony DiCola
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+from .ST7735 import *

BIN
libs/Python_ST7735/examples/cat.jpg


+ 68 - 0
libs/Python_ST7735/examples/image.py

@@ -0,0 +1,68 @@
+# Copyright (c) 2014 Adafruit Industries
+# Author: Tony DiCola
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+from PIL import Image
+
+import ST7735 as TFT
+import Adafruit_GPIO as GPIO
+import Adafruit_GPIO.SPI as SPI
+
+
+WIDTH = 128
+HEIGHT = 160
+SPEED_HZ = 4000000
+
+
+# Raspberry Pi configuration.
+#DC = 24
+#RST = 25
+DC = 24
+RST = 23
+SPI_PORT = 0
+SPI_DEVICE = 0
+
+# BeagleBone Black configuration.
+# DC = 'P9_15'
+# RST = 'P9_12'
+# SPI_PORT = 1
+# SPI_DEVICE = 0
+
+# Create TFT LCD display class.
+disp = TFT.ST7735(
+    DC,
+    rst=RST,
+    spi=SPI.SpiDev(
+        SPI_PORT,
+        SPI_DEVICE,
+        max_speed_hz=SPEED_HZ))
+
+# Initialize display.
+disp.begin()
+
+# Load an image.
+print('Loading image...')
+image = Image.open('cat.jpg')
+
+# Resize the image and rotate it so matches the display.
+image = image.rotate(90).resize((WIDTH, HEIGHT))
+
+# Draw the image on the display hardware.
+print('Drawing image')
+disp.display(image)

+ 73 - 0
libs/Python_ST7735/examples/image_timed.py

@@ -0,0 +1,73 @@
+# Copyright (c) 2014 Adafruit Industries
+# Author: Tony DiCola
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+from PIL import Image
+import time
+import ST7735 as TFT
+import Adafruit_GPIO as GPIO
+import Adafruit_GPIO.SPI as SPI
+
+
+WIDTH = 128
+HEIGHT = 160
+SPEED_HZ = 4000000
+
+
+# Raspberry Pi configuration.
+DC = 24
+RST = 25
+SPI_PORT = 0
+SPI_DEVICE = 0
+
+# BeagleBone Black configuration.
+#DC = 'P9_15'
+#RST = 'P9_12'
+#SPI_PORT = 1
+#SPI_DEVICE = 0
+
+# Create TFT LCD display class.
+disp = TFT.ST7735(
+    DC,
+    rst=RST,
+    spi=SPI.SpiDev(
+        SPI_PORT,
+        SPI_DEVICE,
+        max_speed_hz=SPEED_HZ))
+
+# Initialize display.
+disp.begin()
+
+# Load an image.
+print('Loading image...')
+image = Image.open('cat.jpg')
+
+# Resize the image and rotate it so matches the display.
+image = image.rotate(90).resize((WIDTH, HEIGHT))
+
+print('Press Ctrl-C to exit')
+while(True):
+    # Draw the image on the display hardware.
+    print('Drawing image')
+    start_time = time.time()
+    disp.display(image)
+    end_time = time.time()
+    print('Time to draw image: ' + str(end_time - start_time))
+    disp.clear((0, 0, 0))
+    disp.display()

+ 113 - 0
libs/Python_ST7735/examples/shapes.py

@@ -0,0 +1,113 @@
+# Copyright (c) 2014 Adafruit Industries
+# Author: Tony DiCola
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+from PIL import Image
+from PIL import ImageDraw
+from PIL import ImageFont
+
+import ST7735 as TFT
+import Adafruit_GPIO as GPIO
+import Adafruit_GPIO.SPI as SPI
+
+
+WIDTH = 128
+HEIGHT = 160
+SPEED_HZ = 4000000
+
+
+# Raspberry Pi configuration.
+DC = 24
+RST = 25
+SPI_PORT = 0
+SPI_DEVICE = 0
+
+# BeagleBone Black configuration.
+# DC = 'P9_15'
+# RST = 'P9_12'
+# SPI_PORT = 1
+# SPI_DEVICE = 0
+
+# Create TFT LCD display class.
+disp = TFT.ST7735(
+    DC,
+    rst=RST,
+    spi=SPI.SpiDev(
+        SPI_PORT,
+        SPI_DEVICE,
+        max_speed_hz=SPEED_HZ))
+
+# Initialize display.
+disp.begin()
+
+# Clear the display to a red background.
+# Can pass any tuple of red, green, blue values (from 0 to 255 each).
+disp.clear((255, 0, 0))
+
+# Alternatively can clear to a black screen by calling:
+# disp.clear()
+
+# Get a PIL Draw object to start drawing on the display buffer.
+draw = disp.draw()
+
+# Draw some shapes.
+# Draw a blue ellipse with a green outline.
+draw.ellipse((10, 10, 110, 80), outline=(0,255,0), fill=(0,0,255))
+
+# Draw a purple rectangle with yellow outline.
+draw.rectangle((10, 90, 110, 160), outline=(255,255,0), fill=(255,0,255))
+
+# Draw a white X.
+draw.line((10, 170, 110, 230), fill=(255,255,255))
+draw.line((10, 230, 110, 170), fill=(255,255,255))
+
+# Draw a cyan triangle with a black outline.
+draw.polygon([(10, 275), (110, 240), (110, 310)], outline=(0,0,0), fill=(0,255,255))
+
+# Load default font.
+font = ImageFont.load_default()
+
+# Alternatively load a TTF font.
+# Some other nice fonts to try: http://www.dafont.com/bitmap.php
+#font = ImageFont.truetype('Minecraftia.ttf', 16)
+
+# Define a function to create rotated text.  Unfortunately PIL doesn't have good
+# native support for rotated fonts, but this function can be used to make a
+# text image and rotate it so it's easy to paste in the buffer.
+def draw_rotated_text(image, text, position, angle, font, fill=(255,255,255)):
+    # Get rendered font width and height.
+    draw = ImageDraw.Draw(image)
+    width, height = draw.textsize(text, font=font)
+    # Create a new image with transparent background to store the text.
+    textimage = Image.new('RGBA', (width, height), (0,0,0,0))
+    # Render the text.
+    textdraw = ImageDraw.Draw(textimage)
+    textdraw.text((0,0), text, font=font, fill=fill)
+    # Rotate the text image.
+    rotated = textimage.rotate(angle, expand=1)
+    # Paste the text into the image, using it as a mask for transparency.
+    image.paste(rotated, position, rotated)
+
+# Write two lines of white text on the buffer, rotated 90 degrees counter clockwise.
+draw_rotated_text(disp.buffer, 'Hello World!', (150, 120), 90, font, fill=(255,255,255))
+draw_rotated_text(disp.buffer, 'This is a line of text.', (170, 90), 90, font, fill=(255,255,255))
+
+# Write buffer to display hardware, must be called to make things visible on the
+# display!
+disp.display()

+ 332 - 0
libs/Python_ST7735/ez_setup.py

@@ -0,0 +1,332 @@
+#!/usr/bin/env python
+"""Bootstrap setuptools installation
+
+To use setuptools in your package's setup.py, include this
+file in the same directory and add this to the top of your setup.py::
+
+    from ez_setup import use_setuptools
+    use_setuptools()
+
+To require a specific version of setuptools, set a download
+mirror, or use an alternate download directory, simply supply
+the appropriate options to ``use_setuptools()``.
+
+This file can also be run as a script to install or upgrade setuptools.
+"""
+import os
+import shutil
+import sys
+import tempfile
+import zipfile
+import optparse
+import subprocess
+import platform
+import textwrap
+import contextlib
+
+from distutils import log
+
+try:
+    from site import USER_SITE
+except ImportError:
+    USER_SITE = None
+
+DEFAULT_VERSION = "3.5.1"
+DEFAULT_URL = "https://pypi.python.org/packages/source/s/setuptools/"
+
+def _python_cmd(*args):
+    """
+    Return True if the command succeeded.
+    """
+    args = (sys.executable,) + args
+    return subprocess.call(args) == 0
+
+
+def _install(archive_filename, install_args=()):
+    with archive_context(archive_filename):
+        # installing
+        log.warn('Installing Setuptools')
+        if not _python_cmd('setup.py', 'install', *install_args):
+            log.warn('Something went wrong during the installation.')
+            log.warn('See the error message above.')
+            # exitcode will be 2
+            return 2
+
+
+def _build_egg(egg, archive_filename, to_dir):
+    with archive_context(archive_filename):
+        # building an egg
+        log.warn('Building a Setuptools egg in %s', to_dir)
+        _python_cmd('setup.py', '-q', 'bdist_egg', '--dist-dir', to_dir)
+    # returning the result
+    log.warn(egg)
+    if not os.path.exists(egg):
+        raise IOError('Could not build the egg.')
+
+
+def get_zip_class():
+    """
+    Supplement ZipFile class to support context manager for Python 2.6
+    """
+    class ContextualZipFile(zipfile.ZipFile):
+        def __enter__(self):
+            return self
+        def __exit__(self, type, value, traceback):
+            self.close
+    return zipfile.ZipFile if hasattr(zipfile.ZipFile, '__exit__') else \
+        ContextualZipFile
+
+
+@contextlib.contextmanager
+def archive_context(filename):
+    # extracting the archive
+    tmpdir = tempfile.mkdtemp()
+    log.warn('Extracting in %s', tmpdir)
+    old_wd = os.getcwd()
+    try:
+        os.chdir(tmpdir)
+        with get_zip_class()(filename) as archive:
+            archive.extractall()
+
+        # going in the directory
+        subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0])
+        os.chdir(subdir)
+        log.warn('Now working in %s', subdir)
+        yield
+
+    finally:
+        os.chdir(old_wd)
+        shutil.rmtree(tmpdir)
+
+
+def _do_download(version, download_base, to_dir, download_delay):
+    egg = os.path.join(to_dir, 'setuptools-%s-py%d.%d.egg'
+                       % (version, sys.version_info[0], sys.version_info[1]))
+    if not os.path.exists(egg):
+        archive = download_setuptools(version, download_base,
+                                      to_dir, download_delay)
+        _build_egg(egg, archive, to_dir)
+    sys.path.insert(0, egg)
+
+    # Remove previously-imported pkg_resources if present (see
+    # https://bitbucket.org/pypa/setuptools/pull-request/7/ for details).
+    if 'pkg_resources' in sys.modules:
+        del sys.modules['pkg_resources']
+
+    import setuptools
+    setuptools.bootstrap_install_from = egg
+
+
+def use_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL,
+        to_dir=os.curdir, download_delay=15):
+    to_dir = os.path.abspath(to_dir)
+    rep_modules = 'pkg_resources', 'setuptools'
+    imported = set(sys.modules).intersection(rep_modules)
+    try:
+        import pkg_resources
+    except ImportError:
+        return _do_download(version, download_base, to_dir, download_delay)
+    try:
+        pkg_resources.require("setuptools>=" + version)
+        return
+    except pkg_resources.DistributionNotFound:
+        return _do_download(version, download_base, to_dir, download_delay)
+    except pkg_resources.VersionConflict as VC_err:
+        if imported:
+            msg = textwrap.dedent("""
+                The required version of setuptools (>={version}) is not available,
+                and can't be installed while this script is running. Please
+                install a more recent version first, using
+                'easy_install -U setuptools'.
+
+                (Currently using {VC_err.args[0]!r})
+                """).format(VC_err=VC_err, version=version)
+            sys.stderr.write(msg)
+            sys.exit(2)
+
+        # otherwise, reload ok
+        del pkg_resources, sys.modules['pkg_resources']
+        return _do_download(version, download_base, to_dir, download_delay)
+
+def _clean_check(cmd, target):
+    """
+    Run the command to download target. If the command fails, clean up before
+    re-raising the error.
+    """
+    try:
+        subprocess.check_call(cmd)
+    except subprocess.CalledProcessError:
+        if os.access(target, os.F_OK):
+            os.unlink(target)
+        raise
+
+def download_file_powershell(url, target):
+    """
+    Download the file at url to target using Powershell (which will validate
+    trust). Raise an exception if the command cannot complete.
+    """
+    target = os.path.abspath(target)
+    cmd = [
+        'powershell',
+        '-Command',
+        "(new-object System.Net.WebClient).DownloadFile(%(url)r, %(target)r)" % vars(),
+    ]
+    _clean_check(cmd, target)
+
+def has_powershell():
+    if platform.system() != 'Windows':
+        return False
+    cmd = ['powershell', '-Command', 'echo test']
+    devnull = open(os.path.devnull, 'wb')
+    try:
+        try:
+            subprocess.check_call(cmd, stdout=devnull, stderr=devnull)
+        except Exception:
+            return False
+    finally:
+        devnull.close()
+    return True
+
+download_file_powershell.viable = has_powershell
+
+def download_file_curl(url, target):
+    cmd = ['curl', url, '--silent', '--output', target]
+    _clean_check(cmd, target)
+
+def has_curl():
+    cmd = ['curl', '--version']
+    devnull = open(os.path.devnull, 'wb')
+    try:
+        try:
+            subprocess.check_call(cmd, stdout=devnull, stderr=devnull)
+        except Exception:
+            return False
+    finally:
+        devnull.close()
+    return True
+
+download_file_curl.viable = has_curl
+
+def download_file_wget(url, target):
+    cmd = ['wget', url, '--quiet', '--output-document', target]
+    _clean_check(cmd, target)
+
+def has_wget():
+    cmd = ['wget', '--version']
+    devnull = open(os.path.devnull, 'wb')
+    try:
+        try:
+            subprocess.check_call(cmd, stdout=devnull, stderr=devnull)
+        except Exception:
+            return False
+    finally:
+        devnull.close()
+    return True
+
+download_file_wget.viable = has_wget
+
+def download_file_insecure(url, target):
+    """
+    Use Python to download the file, even though it cannot authenticate the
+    connection.
+    """
+    try:
+        from urllib.request import urlopen
+    except ImportError:
+        from urllib2 import urlopen
+    src = dst = None
+    try:
+        src = urlopen(url)
+        # Read/write all in one block, so we don't create a corrupt file
+        # if the download is interrupted.
+        data = src.read()
+        dst = open(target, "wb")
+        dst.write(data)
+    finally:
+        if src:
+            src.close()
+        if dst:
+            dst.close()
+
+download_file_insecure.viable = lambda: True
+
+def get_best_downloader():
+    downloaders = [
+        download_file_powershell,
+        download_file_curl,
+        download_file_wget,
+        download_file_insecure,
+    ]
+
+    for dl in downloaders:
+        if dl.viable():
+            return dl
+
+def download_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL,
+        to_dir=os.curdir, delay=15, downloader_factory=get_best_downloader):
+    """
+    Download setuptools from a specified location and return its filename
+
+    `version` should be a valid setuptools version number that is available
+    as an egg for download under the `download_base` URL (which should end
+    with a '/'). `to_dir` is the directory where the egg will be downloaded.
+    `delay` is the number of seconds to pause before an actual download
+    attempt.
+
+    ``downloader_factory`` should be a function taking no arguments and
+    returning a function for downloading a URL to a target.
+    """
+    # making sure we use the absolute path
+    to_dir = os.path.abspath(to_dir)
+    zip_name = "setuptools-%s.zip" % version
+    url = download_base + zip_name
+    saveto = os.path.join(to_dir, zip_name)
+    if not os.path.exists(saveto):  # Avoid repeated downloads
+        log.warn("Downloading %s", url)
+        downloader = downloader_factory()
+        downloader(url, saveto)
+    return os.path.realpath(saveto)
+
+def _build_install_args(options):
+    """
+    Build the arguments to 'python setup.py install' on the setuptools package
+    """
+    return ['--user'] if options.user_install else []
+
+def _parse_args():
+    """
+    Parse the command line for options
+    """
+    parser = optparse.OptionParser()
+    parser.add_option(
+        '--user', dest='user_install', action='store_true', default=False,
+        help='install in user site package (requires Python 2.6 or later)')
+    parser.add_option(
+        '--download-base', dest='download_base', metavar="URL",
+        default=DEFAULT_URL,
+        help='alternative URL from where to download the setuptools package')
+    parser.add_option(
+        '--insecure', dest='downloader_factory', action='store_const',
+        const=lambda: download_file_insecure, default=get_best_downloader,
+        help='Use internal, non-validating downloader'
+    )
+    parser.add_option(
+        '--version', help="Specify which version to download",
+        default=DEFAULT_VERSION,
+    )
+    options, args = parser.parse_args()
+    # positional arguments are ignored
+    return options
+
+def main():
+    """Install or upgrade setuptools and EasyInstall"""
+    options = _parse_args()
+    archive = download_setuptools(
+        version=options.version,
+        download_base=options.download_base,
+        downloader_factory=options.downloader_factory,
+    )
+    return _install(archive, _build_install_args(options))
+
+if __name__ == '__main__':
+    sys.exit(main())

+ 36 - 0
libs/Python_ST7735/setup.py

@@ -0,0 +1,36 @@
+# Workaround for issue in Python 2.7.3
+# See http://bugs.python.org/issue15881#msg170215
+try:
+    import multiprocessing
+except ImportError:
+    pass
+
+try:
+    # Try using ez_setup to install setuptools if not already installed.
+    from ez_setup import use_setuptools
+    use_setuptools()
+except ImportError:
+    # Ignore import error and assume Python 3 which already has setuptools.
+    pass
+
+from setuptools import setup, find_packages
+
+
+classifiers = ['Development Status :: 4 - Beta',
+               'Operating System :: POSIX :: Linux',
+               'License :: OSI Approved :: MIT License',
+               'Intended Audience :: Developers',
+               'Programming Language :: Python :: 2.7',
+               'Programming Language :: Python :: 3',
+               'Topic :: Software Development',
+               'Topic :: System :: Hardware']
+
+setup(name              = 'ST7735',
+      version           = '0.0.1',
+      description       = 'Library to control an ST7735 TFT LCD display.',
+      license           = 'MIT',
+      classifiers       = classifiers,
+      url               = 'https://github.com/cskau/Python_ST7735/',
+      dependency_links  = ['https://github.com/adafruit/Adafruit_Python_GPIO/tarball/master#egg=Adafruit-GPIO-0.6.5'],
+      install_requires  = ['Adafruit-GPIO>=0.6.5'],
+      packages          = find_packages())

+ 65 - 0
tree/boot/config.txt

@@ -0,0 +1,65 @@
+# For more options and information see
+# http://rpf.io/configtxt
+# Some settings may impact device functionality. See link above for details
+
+# uncomment if you get no picture on HDMI for a default "safe" mode
+#hdmi_safe=1
+
+# uncomment this if your display has a black border of unused pixels visible
+# and your display can output without overscan
+#disable_overscan=1
+
+# uncomment the following to adjust overscan. Use positive numbers if console
+# goes off screen, and negative if there is too much border
+#overscan_left=16
+#overscan_right=16
+#overscan_top=16
+#overscan_bottom=16
+
+# uncomment to force a console size. By default it will be display's size minus
+# overscan.
+#framebuffer_width=1280
+#framebuffer_height=720
+
+# uncomment if hdmi display is not detected and composite is being output
+#hdmi_force_hotplug=1
+
+# uncomment to force a specific HDMI mode (this will force VGA)
+#hdmi_group=1
+#hdmi_mode=1
+
+# uncomment to force a HDMI mode rather than DVI. This can make audio work in
+# DMT (computer monitor) modes
+#hdmi_drive=2
+
+# uncomment to increase signal to HDMI, if you have interference, blanking, or
+# no display
+#config_hdmi_boost=4
+
+# uncomment for composite PAL
+#sdtv_mode=2
+
+#uncomment to overclock the arm. 700 MHz is the default.
+#arm_freq=800
+
+# Uncomment some or all of these to enable the optional hardware interfaces
+dtparam=i2c_arm=off
+#dtparam=i2s=on
+dtparam=spi=on
+
+# Uncomment this to enable the lirc-rpi module
+#dtoverlay=lirc-rpi
+
+# Additional overlays and parameters are documented /boot/overlays/README
+
+# Enable audio (loads snd_bcm2835)
+#dtparam=audio=on
+
+# DAC
+dtoverlay=hifiberry-dacplus,24db_digital_gain
+# Ir
+dtoverlay=lirc-rpi,gpio_in_pin=4
+# Secondary SDCard
+#dtoverlay=sdio,bus_width=1
+dtoverlay=mmc_spi_cs1
+

BIN
tree/boot/overlays/mmc_spi_cs1.dtbo


+ 21 - 0
tree/boot/overlays/mmc_spi_cs1.dts

@@ -0,0 +1,21 @@
+/dts-v1/;
+/plugin/;
+
+/ {
+   compatible = "brcm,bcm2835", "brcm,bcm2836", "brcm,bcm2708", "brcm,bcm2709";
+
+   fragment@0 {
+      target = <&spi0>;
+      frag0: __overlay__ {
+         status = "okay";
+         sd1 {
+                reg = <1>;
+                status = "okay";
+                compatible = "spi,mmc_spi";
+                voltage-ranges = <3000 3600>;
+                spi-max-frequency = <16000000>;
+         };
+      };
+   };
+};
+

+ 10 - 0
tree/etc/default/mpd

@@ -0,0 +1,10 @@
+## Defaults for the MPD init script, sourced by /etc/init.d/mpd on Debian
+## systems.  Uncomment (remove the leading '#') and change values as needed.
+
+## If you don't want MPD to be started as a system service (for example, if
+## you want to run it from a regular user account), disable it using the
+## command 'update-rc.d mpd disable' (or 'systemctl disable mpd' for systemd).
+
+## The configuration file location for mpd:
+MPDCONF=/home/mpd/setting/mpd.conf
+

+ 3 - 0
tree/etc/default/ympd

@@ -0,0 +1,3 @@
+USER=mpd
+WEBPORT=80
+

+ 275 - 0
tree/etc/samba/smb.conf

@@ -0,0 +1,275 @@
+#
+# Sample configuration file for the Samba suite for Debian GNU/Linux.
+#
+#
+# This is the main Samba configuration file. You should read the
+# smb.conf(5) manual page in order to understand the options listed
+# here. Samba has a huge number of configurable options most of which 
+# are not shown in this example
+#
+# Some options that are often worth tuning have been included as
+# commented-out examples in this file.
+#  - When such options are commented with ";", the proposed setting
+#    differs from the default Samba behaviour
+#  - When commented with "#", the proposed setting is the default
+#    behaviour of Samba but the option is considered important
+#    enough to be mentioned here
+#
+# NOTE: Whenever you modify this file you should run the command
+# "testparm" to check that you have not made any basic syntactic 
+# errors. 
+
+#======================= Global Settings =======================
+
+[global]
+
+## Browsing/Identification ###
+
+# Change this to the workgroup/NT-domain name your Samba server will part of
+   workgroup = WORKGROUP
+
+# Windows Internet Name Serving Support Section:
+# WINS Support - Tells the NMBD component of Samba to enable its WINS Server
+#   wins support = no
+
+# WINS Server - Tells the NMBD components of Samba to be a WINS Client
+# Note: Samba can be either a WINS Server, or a WINS Client, but NOT both
+;   wins server = w.x.y.z
+
+# This will prevent nmbd to search for NetBIOS names through DNS.
+   dns proxy = no
+
+#### Networking ####
+
+# The specific set of interfaces / networks to bind to
+# This can be either the interface name or an IP address/netmask;
+# interface names are normally preferred
+;   interfaces = 127.0.0.0/8 eth0
+
+# Only bind to the named interfaces and/or networks; you must use the
+# 'interfaces' option above to use this.
+# It is recommended that you enable this feature if your Samba machine is
+# not protected by a firewall or is a firewall itself.  However, this
+# option cannot handle dynamic or non-broadcast interfaces correctly.
+;   bind interfaces only = yes
+
+
+
+#### Debugging/Accounting ####
+
+# This tells Samba to use a separate log file for each machine
+# that connects
+   log file = /var/log/samba/log.%m
+
+# Cap the size of the individual log files (in KiB).
+   max log size = 1000
+
+# If you want Samba to only log through syslog then set the following
+# parameter to 'yes'.
+#   syslog only = no
+
+# We want Samba to log a minimum amount of information to syslog. Everything
+# should go to /var/log/samba/log.{smbd,nmbd} instead. If you want to log
+# through syslog you should set the following parameter to something higher.
+   syslog = 0
+
+# Do something sensible when Samba crashes: mail the admin a backtrace
+   panic action = /usr/share/samba/panic-action %d
+
+
+####### Authentication #######
+
+# Server role. Defines in which mode Samba will operate. Possible
+# values are "standalone server", "member server", "classic primary
+# domain controller", "classic backup domain controller", "active
+# directory domain controller". 
+#
+# Most people will want "standalone sever" or "member server".
+# Running as "active directory domain controller" will require first
+# running "samba-tool domain provision" to wipe databases and create a
+# new domain.
+   server role = standalone server
+
+# If you are using encrypted passwords, Samba will need to know what
+# password database type you are using.  
+   passdb backend = tdbsam
+
+   obey pam restrictions = yes
+
+# This boolean parameter controls whether Samba attempts to sync the Unix
+# password with the SMB password when the encrypted SMB password in the
+# passdb is changed.
+   unix password sync = yes
+
+# For Unix password sync to work on a Debian GNU/Linux system, the following
+# parameters must be set (thanks to Ian Kahan <<kahan@informatik.tu-muenchen.de> for
+# sending the correct chat script for the passwd program in Debian Sarge).
+   passwd program = /usr/bin/passwd %u
+   passwd chat = *Enter\snew\s*\spassword:* %n\n *Retype\snew\s*\spassword:* %n\n *password\supdated\ssuccessfully* .
+
+# This boolean controls whether PAM will be used for password changes
+# when requested by an SMB client instead of the program listed in
+# 'passwd program'. The default is 'no'.
+   pam password change = yes
+
+# This option controls how unsuccessful authentication attempts are mapped
+# to anonymous connections
+   map to guest = bad user
+   guest account = mpd
+
+########## Domains ###########
+
+#
+# The following settings only takes effect if 'server role = primary
+# classic domain controller', 'server role = backup domain controller'
+# or 'domain logons' is set 
+#
+
+# It specifies the location of the user's
+# profile directory from the client point of view) The following
+# required a [profiles] share to be setup on the samba server (see
+# below)
+;   logon path = \\%N\profiles\%U
+# Another common choice is storing the profile in the user's home directory
+# (this is Samba's default)
+#   logon path = \\%N\%U\profile
+
+# The following setting only takes effect if 'domain logons' is set
+# It specifies the location of a user's home directory (from the client
+# point of view)
+;   logon drive = H:
+#   logon home = \\%N\%U
+
+# The following setting only takes effect if 'domain logons' is set
+# It specifies the script to run during logon. The script must be stored
+# in the [netlogon] share
+# NOTE: Must be store in 'DOS' file format convention
+;   logon script = logon.cmd
+
+# This allows Unix users to be created on the domain controller via the SAMR
+# RPC pipe.  The example command creates a user account with a disabled Unix
+# password; please adapt to your needs
+; add user script = /usr/sbin/adduser --quiet --disabled-password --gecos "" %u
+
+# This allows machine accounts to be created on the domain controller via the 
+# SAMR RPC pipe.  
+# The following assumes a "machines" group exists on the system
+; add machine script  = /usr/sbin/useradd -g machines -c "%u machine account" -d /var/lib/samba -s /bin/false %u
+
+# This allows Unix groups to be created on the domain controller via the SAMR
+# RPC pipe.  
+; add group script = /usr/sbin/addgroup --force-badname %g
+
+############ Misc ############
+
+# Using the following line enables you to customise your configuration
+# on a per machine basis. The %m gets replaced with the netbios name
+# of the machine that is connecting
+;   include = /home/samba/etc/smb.conf.%m
+
+# Some defaults for winbind (make sure you're not using the ranges
+# for something else.)
+;   idmap uid = 10000-20000
+;   idmap gid = 10000-20000
+;   template shell = /bin/bash
+
+# Setup usershare options to enable non-root users to share folders
+# with the net usershare command.
+
+# Maximum number of usershare. 0 (default) means that usershare is disabled.
+;   usershare max shares = 100
+
+# Allow users who've been granted usershare privileges to create
+# public shares, not just authenticated ones
+   usershare allow guests = yes
+
+
+#======================= Share Definitions =======================
+
+[homes]
+   comment = Home Directories
+   browseable = no
+
+# By default, the home directories are exported read-only. Change the
+# next parameter to 'no' if you want to be able to write to them.
+   read only = yes
+
+# File creation mask is set to 0700 for security reasons. If you want to
+# create files with group=rw permissions, set next parameter to 0775.
+   create mask = 0700
+
+# Directory creation mask is set to 0700 for security reasons. If you want to
+# create dirs. with group=rw permissions, set next parameter to 0775.
+   directory mask = 0700
+
+# By default, \\server\username shares can be connected to by anyone
+# with access to the samba server.
+# The following parameter makes sure that only "username" can connect
+# to \\server\username
+# This might need tweaking when using external authentication schemes
+   valid users = %S
+
+# Un-comment the following and create the netlogon directory for Domain Logons
+# (you need to configure Samba to act as a domain controller too.)
+;[netlogon]
+;   comment = Network Logon Service
+;   path = /home/samba/netlogon
+;   guest ok = yes
+;   read only = yes
+
+# Un-comment the following and create the profiles directory to store
+# users profiles (see the "logon path" option above)
+# (you need to configure Samba to act as a domain controller too.)
+# The path below should be writable by all users so that their
+# profile directory may be created the first time they log on
+;[profiles]
+;   comment = Users profiles
+;   path = /home/samba/profiles
+;   guest ok = no
+;   browseable = no
+;   create mask = 0600
+;   directory mask = 0700
+
+[printers]
+   comment = All Printers
+   browseable = no
+   path = /var/spool/samba
+   printable = yes
+   guest ok = no
+   read only = yes
+   create mask = 0700
+
+# Windows clients look for this share name as a source of downloadable
+# printer drivers
+[print$]
+   comment = Printer Drivers
+   path = /var/lib/samba/printers
+   browseable = yes
+   read only = yes
+   guest ok = no
+# Uncomment to allow remote administration of Windows print drivers.
+# You may need to replace 'lpadmin' with the name of the group your
+# admin users are members of.
+# Please note that you also need to set appropriate Unix permissions
+# to the drivers directory for these users to have write rights in it
+;   write list = root, @lpadmin
+
+#[extSD]
+#   path = /extSD
+#   browseable = yes
+#   read only = yes
+#   guest ok = yes
+#   create mask = 0666
+#   directory mask = 0777
+
+[settings]
+   path = /home/mpd
+   browseable = yes
+   read only = no
+   writable = yes
+   guest ok = yes
+   guest account = mpd
+   create mask = 0666
+   directory mask = 0777
+
+

+ 14 - 0
tree/etc/systemd/system/mmc2.service

@@ -0,0 +1,14 @@
+[Unit]
+Description=Raspi Magazine MMC2 mounter
+After=mpd.service
+Before=superdac.service
+
+[Service]
+Type=simple
+Restart=no
+User=root
+ExecStart=/usr/local/bin/mmc2.py
+
+[Install]
+WantedBy=multi-user.target
+

+ 13 - 0
tree/etc/systemd/system/registerIr.service

@@ -0,0 +1,13 @@
+[Unit]
+Description=Raspi Magazine register Ir service
+Before=lircd.service
+
+[Service]
+Type=oneshot
+Restart=no
+User=root
+ExecStart=/usr/local/bin/registerIr.py
+
+[Install]
+WantedBy=multi-user.target
+

+ 14 - 0
tree/etc/systemd/system/superdac.service

@@ -0,0 +1,14 @@
+[Unit]
+Description=Raspi Magazine Super DAC
+After=mpd.service
+
+[Service]
+Type=forking
+Restart = always
+PIDFile=/var/run/superdac.pid
+ExecStart=/usr/local/bin/superdac.py
+User=root
+
+[Install]
+WantedBy=multi-user.target
+

+ 14 - 0
tree/etc/systemd/system/ympd.service

@@ -0,0 +1,14 @@
+[Unit]
+Description=ympd mpd web client
+Documentation=man:ympd(1)
+After=mpd.service
+
+[Service]
+Type=simple
+EnvironmentFile=/etc/default/ympd
+ExecStart=/usr/local/bin/ympd -u $USER --webport $WEBPORT
+ProtectSystem=yes
+
+[Install]
+WantedBy=multi-user.target
+

BIN
tree/home/mpd/scripts/logo.jpg


+ 31 - 0
tree/home/mpd/setting/mpd.conf

@@ -0,0 +1,31 @@
+# 
+#music_directory		"smb://yourserver/music"
+#music_directory		"/home/mpd/music
+music_directory			"/extSD"
+playlist_directory		"/home/mpd/playlists"
+
+bind_to_address	"any"
+
+##以下は変更しない#############################################################
+bind_to_address		"any"
+db_file			"/var/lib/mpd/tag_cache"
+log_file		"/var/log/mpd/mpd.log"
+state_file		"/var/lib/mpd/state"
+sticker_file		"/var/lib/mpd/sticker.sql"
+user			"mpd"
+
+# Input #######################################################################
+input {
+        plugin "curl"
+#       proxy "proxy.isp.com:8080"
+#       proxy_user "user"
+#       proxy_password "password"
+}
+
+# Audio Output ################################################################
+audio_output {
+	type		"alsa"
+	name		"My ALSA Device"
+}
+filesystem_charset		"UTF-8"
+id3v1_encoding			"UTF-8"

+ 62 - 0
tree/usr/local/bin/mmc2.py

@@ -0,0 +1,62 @@
+#!/usr/bin/env python3
+import os
+import sys
+import time
+import subprocess
+import signal
+
+MMC2DEV=['/dev/mmcblk1p1', '/dev/mmcblk2p1', '/dev/mmcblk3p1', '/dev/mmcblk4p1']
+MMC2DEBUG='/sys/kernel/debug/mmc'
+MODPROBE='/sbin/modprobe'
+
+def mountMMC2():
+	mmc_number=0
+	dev=None
+	for d in MMC2DEV:
+		mmc_number += 1
+		if os.path.exists(d):
+			dev = d
+			break
+
+	if dev is not None:
+		r=None
+		for i in range(10):
+			r=subprocess.run(['/bin/mount', dev, '/extSD','-o','user,ro,umask=000,iocharset=utf8,codepage=932,shortname=mixed'])
+			if r.returncode != 0:
+				time.sleep(1)
+			else:
+				break
+	
+		if r.returncode != 0:
+			print('mmc' + str(mmc_number) +' mount failed.', file=sys.stderr)
+			return False
+		else:
+			mmc_clock = MMC2DEBUG + str(mmc_number) + '/clock'
+			if os.path.exists(mmc_clock):
+				with open(mmc_clock,'w') as f:
+					f.write('16000000')
+			return True
+	else:
+		os.system(MODPROBE + ' -r mmc_spi')
+		print('Can not find 2nd mmc', file=sys.stderr)
+		return False
+
+def umountMMC2():
+	os.system('/bin/umount /extSD')
+	return
+		
+def signal_handler(signal, handler):
+	umountMMC2()
+	sys.exit(0)
+
+	
+# Script starts here
+if __name__ == "__main__":
+	signal.signal(signal.SIGINT, signal_handler)
+	signal.signal(signal.SIGTERM, signal_handler)
+	
+	if mountMMC2():
+		while True:
+			time.sleep(86400)
+	else:
+		sys.exit('exit mmc2.py')

+ 197 - 0
tree/usr/local/bin/registerIr.py

@@ -0,0 +1,197 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+import subprocess
+import select
+import signal
+import time
+import sys
+import os
+
+from PIL import Image
+from PIL import ImageDraw
+from PIL import ImageFont
+
+import ST7735 as TFT
+import Adafruit_GPIO.SPI as SPI
+
+import RPi.GPIO as GPIO
+
+# LCD spec.
+LCD_WIDTH = 128
+LCD_HEIGHT = 160
+SPEED_HZ = 8000000
+
+# SUPERDAC configration
+DC = 24
+RST = 23
+SPI_PORT = 0
+SPI_DEVICE = 0
+SW1 = 5
+SW2 = 6
+
+
+FONTSIZE=14
+LINEHEIGHT=16
+DEFAULT_FONT = '/usr/share/fonts/truetype/fonts-japanese-gothic.ttf'
+
+IR_CONF='/etc/lirc/lircd.conf.d/remocon.lircd.conf'
+
+MODE2CMD=['/usr/bin/mode2', '--device', '/dev/lirc0', '--driver','default']
+
+BUTTONS=[ {'label': 'PLAY', 'prompt': 'プレイ', 'code': None},\
+          {'label': 'STOP', 'prompt': 'ストップ', 'code': None},\
+          {'label': 'PAUSE', 'prompt': '一時停止', 'code': None},\
+          {'label': 'NEXT', 'prompt': '次曲', 'code': None},\
+          {'label': 'PREV', 'prompt': '前曲', 'code': None},\
+          {'label': 'VOLUP', 'prompt': '音量上げ', 'code': None},\
+          {'label': 'VOLDOWN', 'prompt': '音量下げ', 'code': None},\
+          {'label': 'MENU', 'prompt': 'メニュー', 'code': None},\
+          {'label': '0', 'prompt': '数字0', 'code': None},\
+          {'label': '1', 'prompt': '数字1', 'code': None},\
+          {'label': '2', 'prompt': '数字2', 'code': None},\
+          {'label': '3', 'prompt': '数字3', 'code': None},\
+          {'label': '4', 'prompt': '数字4', 'code': None},\
+          {'label': '5', 'prompt': '数字5', 'code': None},\
+          {'label': '6', 'prompt': '数字6', 'code': None},\
+          {'label': '7', 'prompt': '数字7', 'code': None},\
+          {'label': '8', 'prompt': '数字8', 'code': None},\
+          {'label': '9', 'prompt': '数字9', 'code': None},\
+        ]
+
+STR_GUIDE=['リモコンの登録を',\
+           '行います。',\
+           'リモコンの任意の',\
+           'ボタンを5分以内',\
+           'に押してください。',\
+           '信号を検出し',\
+           '登録を開始します。',\
+          ]
+
+STR_MESSAGE=['ボタンを',\
+             '押してください。',\
+             '(2分以内)',
+            ]
+
+STR_ERR = ['タイムアウト',\
+           'リモコンの信号が',\
+           '受信できません',\
+          ]
+
+
+LCD = None
+
+class simpleLCD():
+	
+	def __init__(self):
+		spi = SPI.SpiDev(SPI_PORT, SPI_DEVICE,max_speed_hz=SPEED_HZ)
+		self.disp = TFT.ST7735(DC, rst=RST, spi=spi)
+		self.disp.begin()
+		self.disp.clear((0,0,0));
+		self.disp.display()
+		self.draw=ImageDraw.Draw(self.disp.buffer)
+		self.font = ImageFont.truetype(DEFAULT_FONT, FONTSIZE, encoding="unic");
+		self.clear()
+	
+	def clear(self):
+		self.disp.clear((0,0,0))
+	
+	def message(self, msg, y = 0, c = '#FFFFFF'):
+		for line in msg:
+			self.draw.text((0, y), line, font=self.font,  fill=c)
+			y += LINEHEIGHT
+		self.disp.display()
+		return y
+
+def receiveIr(timeout=120*1000):
+	with subprocess.Popen(MODE2CMD, stdout=subprocess.PIPE) as proc:
+		p = select.poll()
+		p.register(proc.stdout)
+		codes = []
+		i = 0
+		while True:
+			ret = p.poll(timeout)
+			if len(ret) == 0:	# timeout
+				proc.terminate()
+				return None
+			line = proc.stdout.readline().strip().decode()
+			if i > 2:
+				code = line.split(' ')[1]
+				codes.append(code)
+			i += 1
+			if i > 69:
+				break
+		proc.terminate()
+	return codes
+
+
+def timeout():
+	global LCD
+	
+	LCD.clear()
+	LCD.message(STR_ERR, c='#FF0000')
+	time.sleep(60)
+	os.system('/sbin/reboot')
+	sys.exit(1)
+
+#
+# Script starts here
+if __name__ == "__main__":
+
+	# switch
+	GPIO.setmode(GPIO.BCM)
+	GPIO.setup(SW2, GPIO.IN, pull_up_down=GPIO.PUD_UP)
+	sw = GPIO.input(SW2)
+	GPIO.cleanup()
+	if sw != GPIO.LOW:
+		sys.exit(0)
+	
+	# stop service
+	os.system('/sbin/systemctl stop lircd')
+	os.system('/sbin/systemctl stop superdac')
+	os.system('/sbin/systemctl stop mpd')
+	time.sleep(20)
+	#
+	LCD = simpleLCD()
+	LCD.message(STR_GUIDE)
+	r = receiveIr(5*60*1000)
+	if r is None:
+		timeout()
+	
+	# Learn
+	for item in BUTTONS:
+		LCD.clear()
+		y = LCD.message( [item['prompt']], c='#FF0000')
+		LCD.message(STR_MESSAGE, y=y)
+		r = receiveIr(2*60*1000)
+		if r is None:
+			timeout()
+		item['code'] = r
+	
+	# config
+	# print(BUTTONS)
+	with open(IR_CONF, 'w') as f:
+		print('begin remote'                   , file=f)
+		print(''                               , file=f)
+		print('  name  remocon'                , file=f)
+		print('  flags RAW_CODES|CONST_LENGTH' , file=f)
+		print('  eps   30'                     , file=f)
+		print('  aeps  100'                    , file=f)
+		print('  gap   100000'                 , file=f)
+		print(''                               , file=f)
+		print('    begin raw_codes'            , file=f)
+		for item in BUTTONS:
+			code = item['code']
+			print('      name ' + item['label'], file=f)
+			for i in range(12):
+				f.write('        ')
+				for j in range(6):
+					try:
+						c = code.pop(0)
+						f.write(c.rjust(8))
+					except Exception as e:
+						break
+				f.write('\n')
+		print('    end raw_codes'              , file=f)
+		print('end remote'                     , file=f)
+	os.system('/sbin/reboot')
+	sys.exit(0)

File diff suppressed because it is too large
+ 1327 - 0
tree/usr/local/bin/superdac.py


+ 108 - 0
tree/usr/local/etc/superdac.lircrc

@@ -0,0 +1,108 @@
+begin
+ button = PLAY
+ prog = superdac
+ config = play
+end
+
+begin
+ button = PAUSE
+ prog = superdac
+ config = pause
+end
+
+begin
+ button = STOP
+ prog = superdac
+ config = stop
+end
+
+begin
+ button = NEXT
+ prog = superdac
+ config = next
+end
+
+begin
+ button = PREV
+ prog = superdac
+ config = prev
+end
+
+begin
+ button = VOLUP
+ prog = superdac
+ config = volup
+end
+
+begin
+ button = VOLDOWN
+ prog = superdac
+ config = voldown
+end
+
+begin
+ button = MENU
+ prog = superdac
+ config = menu
+end
+
+begin
+ button = 1
+ prog = superdac
+ config = 1
+end
+
+begin
+ button = 2
+ prog = superdac
+ config = 2
+end
+
+begin
+ button = 3
+ prog = superdac
+ config = 3
+end
+
+begin
+ button = 4
+ prog = superdac
+ config = 4
+end
+
+begin
+ button = 5
+ prog = superdac
+ config = 5
+end
+
+begin
+ button = 6
+ prog = superdac
+ config = 6
+end
+
+begin
+ button = 7
+ prog = superdac
+ config = 7
+end
+
+begin
+ button = 8
+ prog = superdac
+ config = 8
+end
+
+begin
+ button = 9
+ prog = superdac
+ config = 9
+end
+
+begin
+ button = 0
+ prog = superdac
+ config = 0
+end
+