Advertisement
cymplecy

sgh_unicornhat.py

Apr 5th, 2018
249
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 10.34 KB | None | 0 0
  1. import atexit
  2. import colorsys
  3. from rpi_ws281x import __version__ as __rpi_ws281x__, PixelStrip, Color
  4.  
  5. __version__ = '2.2.3'
  6. #modified Simon Walters to work with linear strips as well
  7. #5Apr18
  8.  
  9. # LED strip configuration:
  10. LED_COUNT      = 256      # Number of LED pixels.
  11. LED_PIN        = 18      # GPIO pin connected to the pixels (must support PWM!).
  12. LED_FREQ_HZ    = 800000  # LED signal frequency in hertz (usually 800khz)
  13. LED_DMA        = 10      # DMA channel to use for generating signal
  14. LED_BRIGHTNESS = 128     # Set to 0 for darkest and 255 for brightest
  15. LED_CHANNEL    = 0       # PWM channel
  16. LED_INVERT     = False   # True to invert the signal (when using NPN transistor level shift)
  17. LED_GAMMA = [
  18. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  19. 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2,
  20. 2, 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5,
  21. 6, 6, 6, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 11, 11,
  22. 11, 12, 12, 13, 13, 13, 14, 14, 15, 15, 16, 16, 17, 17, 18, 18,
  23. 19, 19, 20, 21, 21, 22, 22, 23, 23, 24, 25, 25, 26, 27, 27, 28,
  24. 29, 29, 30, 31, 31, 32, 33, 34, 34, 35, 36, 37, 37, 38, 39, 40,
  25. 40, 41, 42, 43, 44, 45, 46, 46, 47, 48, 49, 50, 51, 52, 53, 54,
  26. 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70,
  27. 71, 72, 73, 74, 76, 77, 78, 79, 80, 81, 83, 84, 85, 86, 88, 89,
  28. 90, 91, 93, 94, 95, 96, 98, 99,100,102,103,104,106,107,109,110,
  29. 111,113,114,116,117,119,120,121,123,124,126,128,129,131,132,134,
  30. 135,137,138,140,142,143,145,146,148,150,151,153,155,157,158,160,
  31. 162,163,165,167,169,170,172,174,176,178,179,181,183,185,187,189,
  32. 191,193,194,196,198,200,202,204,206,208,210,212,214,216,218,220,
  33. 222,224,227,229,231,233,235,237,239,241,244,246,248,250,252,255]
  34.  
  35. COLORS = {
  36.     'red':(255,0,0),
  37.     'lime':(0,255,0),
  38.     'blue':(0,0,255),
  39.     'yellow':(255,255,0),
  40.     'magenta':(255,0,255),
  41.     'cyan':(0,255,255),
  42.     'black':(0,0,0),
  43.     'white':(255,255,255),
  44.     'gray':(127,127,127),
  45.     'grey':(127,127,127),
  46.     'silver':(192,192,192),
  47.     'maroon':(128,0,0),
  48.     'olive':(128,128,0),
  49.     'green':(0,128,0),
  50.     'purple':(128,0,128),
  51.     'teal':(0,128,128),
  52.     'navy':(0,0,128),
  53.     'orange':(255,165,0),
  54.     'gold':(255,215,0),
  55.     'purple':(128,0,128),
  56.     'indigo':(75,0,130)
  57. }
  58.  
  59.  
  60. """
  61. Store the rotation of UnicornHat, defaults to
  62. 0 which places 0,0 on the top left with the B+
  63. HDMI port facing downwards
  64. """
  65. _rotation = 0
  66. _requested_rotation = 0
  67. _wx = 8
  68. _wy = 8
  69. _map = []
  70. _pixels = [(0,0,0) for x in range(64)]
  71. _is_setup = False
  72. ws2812 = None
  73.  
  74. """
  75. Store a map of pixel indexes for
  76. translating x, y coordinates.
  77. """
  78. HAT = [
  79.     [7 , 6 , 5 , 4 , 3 , 2 , 1 , 0 ],
  80.     [8 , 9 , 10, 11, 12, 13, 14, 15],
  81.     [23, 22, 21, 20, 19, 18, 17, 16],
  82.     [24, 25, 26, 27, 28, 29, 30, 31],
  83.     [39, 38, 37, 36, 35, 34, 33, 32],
  84.     [40, 41, 42, 43, 44, 45, 46, 47],
  85.     [55, 54, 53, 52, 51, 50, 49, 48],
  86.     [56, 57, 58, 59, 60, 61, 62, 63]
  87. ]
  88.  
  89. PHAT_VERTICAL = [
  90.     [0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 ],
  91.     [8 , 9 , 10, 11, 12, 13, 14, 15],
  92.     [16, 17, 18, 19, 20, 21, 22, 23],
  93.     [24, 25, 26, 27, 28, 29, 30, 31]
  94. ]
  95.  
  96. PHAT = [
  97.     [24, 16, 8,  0],
  98.     [25, 17, 9,  1],
  99.     [26, 18, 10, 2],
  100.     [27, 19, 11, 3],
  101.     [28, 20, 12, 4],
  102.     [29, 21, 13, 5],
  103.     [30, 22, 14, 6],
  104.     [31, 23, 15, 7]
  105. ]
  106.  
  107. AUTO = None
  108.  
  109. def setup():
  110.     global ws2812, _is_setup
  111.  
  112.     if _is_setup:
  113.         return
  114.  
  115.     ws2812 = PixelStrip(LED_COUNT, LED_PIN, LED_FREQ_HZ, LED_DMA, LED_INVERT, LED_BRIGHTNESS, LED_CHANNEL, LED_GAMMA)
  116.  
  117.     ws2812.begin()
  118.  
  119.     set_layout(HAT)
  120.  
  121.     atexit.register(_clean_shutdown)
  122.  
  123.     _is_setup = True
  124.  
  125.  
  126. def set_gamma(gamma):
  127.     setup()
  128.     ws2812.setGamma(gamma)
  129.  
  130.  
  131. def disable_gamma():
  132.     setup()
  133.     set_gamma(list(range(256)))
  134.  
  135.  
  136. def set_layout(pixel_map = AUTO):
  137.     """Set the layout to Unicorn HAT or Unicorn pHAT
  138.  
  139.    Note: auto detection relies upon the HAT EEPROM. Your Unicorn HAT
  140.    must be connected before boot to successfully auto detect.
  141.  
  142.    :param pixel_map: Choose the layout to set, can be either HAT, PHAT, PHAT_VERTICAL or AUTO
  143.    """
  144.  
  145.     global _map
  146.  
  147.     if pixel_map is None:
  148.         pixel_map = PHAT # Assume PHAT
  149.         try:
  150.             product = open("/proc/device-tree/hat/product","r").read().strip()
  151.             if product[:11] == "Unicorn HAT":
  152.                 pixel_map = HAT
  153.  
  154.         except IOError:
  155.             pass
  156.  
  157.     _map = pixel_map
  158.  
  159.  
  160. def get_shape():
  161.     """Returns the shape (width, height) of the display"""
  162.  
  163.     global _map
  164.  
  165.     setup() # Shape is unset until this is called
  166.     return (len(_map), len(_map[0]))
  167.  
  168.  
  169. def _clean_shutdown():
  170.     """Registered at exit to ensure ws2812 cleans up after itself
  171.    and all pixels are turned off.
  172.    """
  173.  
  174.     off()
  175.  
  176.  
  177. def rotation(r=0):
  178.     """Set the display rotation
  179.  
  180.    :param r: Specify the rotation in degrees: 0, 90, 180 or 270
  181.    """
  182.  
  183.     global _map
  184.     global _rotation
  185.     global _requested_rotation
  186.  
  187.     setup()
  188.     if r in [0, 90, 180, 270]:
  189.         _requested_rotation=r
  190.         wx = len(_map)
  191.         wy = len(_map[0])
  192.  
  193.         if wx == wy:
  194.           _rotation = r
  195.  
  196.         else:
  197.           if r in [0, 180]:
  198.             _map = PHAT
  199.             _rotation = r
  200.           else:
  201.             _map = PHAT_VERTICAL
  202.             _rotation = r-90
  203.  
  204.         return True
  205.  
  206.     else:
  207.         raise ValueError('Rotation must be 0, 90, 180 or 270 degrees')
  208.  
  209.  
  210. def get_rotation():
  211.     """Get the display rotation value
  212.  
  213.    Returns an integer, either 0, 90, 180 or 270
  214.    """
  215.  
  216.     return _requested_rotation
  217.  
  218.  
  219. def brightness(b=0.2):
  220.     """Set the display brightness between 0.0 and 1.0
  221.  
  222.    0.2 is highly recommended, UnicornHat can get painfully bright!
  223.  
  224.    :param b: Brightness from 0.0 to 1.0 (default 0.2)
  225.    """
  226.  
  227.     setup()
  228.  
  229.     if b > 1 or b < 0:
  230.         raise ValueError('Brightness must be between 0.0 and 1.0')
  231.  
  232.     """Absolute max brightness has been capped to 50%, do not change
  233.    this unless you know what you're doing.
  234.    UnicornHAT draws too much current above 50%."""
  235.  
  236.     brightness = int(b*128.0)
  237.  
  238.     if brightness < 30:
  239.         print("Warning: Low brightness chosen, your UnicornHAT might not light up!")
  240.  
  241.     ws2812.setBrightness(brightness)
  242.  
  243.  
  244. def get_brightness():
  245.     """Get the display brightness value
  246.  
  247.    Returns a float between 0.0 and 1.0
  248.    """
  249.  
  250.     setup()
  251.  
  252.     return round(ws2812.getBrightness()/128.0, 3)
  253.  
  254.  
  255. def clear():
  256.     """Clear the buffer"""
  257.  
  258.     setup()
  259.  
  260.     for x in range(64):
  261.         ws2812.setPixelColorRGB(x, 0, 0, 0)
  262.         _pixels[x] = (0, 0, 0)
  263.  
  264.  
  265. def off():
  266.     """Clear the buffer and immediately update UnicornHat
  267.  
  268.    Turns off all pixels.
  269.    """
  270.  
  271.     clear()
  272.     show()
  273.  
  274.  
  275. def get_index_from_xy(x, y):
  276.     """Convert an x, y value to an index on the display
  277.  
  278.    :param x: Horizontal position from 0 to 7
  279.    :param y: Vertical position from 0 to 7
  280.    """
  281.  
  282.     setup()
  283.  
  284.     wx = len(_map) - 1
  285.     wy = len(_map[0]) - 1
  286.  
  287.     y = (wy)-y
  288.  
  289.     if _rotation == 90 and wx == wy:
  290.         x, y = y, (wx)-x
  291.     elif _rotation == 180:
  292.         x, y = (wx)-x, (wy)-y
  293.     elif _rotation == 270 and wx == wy:
  294.         x, y = (wy)-y, x
  295.  
  296.     try:
  297.         index = _map[x][y]
  298.     except IndexError:
  299.         index = None
  300.  
  301.     return index
  302.  
  303.  
  304. def set_pixel_hsv(x, y, h, s=None, v=None):
  305.     """Set a single pixel to a colour using HSV
  306.  
  307.    :param x: Horizontal position from 0 to 7
  308.    :param y: Veritcal position from 0 to 7
  309.    :param h: Hue from 0.0 to 1.0 ( IE: degrees around hue wheel/360.0 )
  310.    :param s: Saturation from 0.0 to 1.0
  311.    :param v: Value (also known as brightness) from 0.0 to 1.0
  312.    """
  313.  
  314.     if type(h) is tuple:
  315.         h, s, v = h
  316.  
  317.     r, g, b = [int(n*255) for n in colorsys.hsv_to_rgb(h, s, v)]
  318.  
  319.     set_pixel(x, y, r, g, b)
  320.  
  321.  
  322. def set_pixel(x, y, r, g=None, b=None):
  323.     """Set a single pixel to RGB colour
  324.  
  325.    :param x: Horizontal position from 0 to 7
  326.    :param y: Veritcal position from 0 to 7
  327.    :param r: Amount of red from 0 to 255
  328.    :param g: Amount of green from 0 to 255
  329.    :param b: Amount of blue from 0 to 255
  330.    """
  331.  
  332.     setup()
  333.  
  334.     if type(r) is tuple:
  335.         r, g, b = r
  336.    
  337.     elif type(r) is str:
  338.         try:
  339.             r, g, b = COLORS[r.lower()]
  340.        
  341.         except KeyError:
  342.             raise ValueError('Invalid color!')
  343.  
  344.     index = get_index_from_xy(x, y)
  345.  
  346.     if index is not None:
  347.         ws2812.setPixelColorRGB(index, r, g, b)
  348.         _pixels[index] = (r, g, b)
  349.  
  350.  
  351. def get_pixel(x, y):
  352.     """Get the RGB value of a single pixel
  353.  
  354.    :param x: Horizontal position from 0 to 7
  355.    :param y: Veritcal position from 0 to 7
  356.    """
  357.  
  358.     index = get_index_from_xy(x, y)
  359.     if index is not None:
  360.         return _pixels[index]
  361.  
  362.  
  363. def set_all(r, g=None, b=None):
  364.     """Set all pixels to a specific colour"""
  365.  
  366.     shade_pixels(lambda x, y: (r, g, b))
  367.  
  368.  
  369. def shade_pixels(shader):
  370.     """Set all pixels using a pixel shader style function
  371.  
  372.    :param pixels: A function which accepts the x and y positions of a pixel and returns values r, g and b
  373.  
  374.    For example, this would be synonymous to clear::
  375.  
  376.        set_pixels(lambda x, y: return 0,0,0)
  377.  
  378.    Or perhaps we want to map red along the horizontal axis, and blue along the vertical::
  379.  
  380.        set_pixels(lambda x, y: return (x/7.0) * 255, 0, (y/7.0) * 255)
  381.    """
  382.    
  383.     width, height = get_shape()
  384.     for x in range(width):
  385.         for y in range(height):
  386.             r, g, b = shader(x, y)
  387.             set_pixel(x, y, r, g, b)
  388.  
  389.  
  390. def set_pixels(pixels):
  391.     """Set all pixels using an array of `get_shape()`"""
  392.  
  393.     shade_pixels(lambda x, y: pixels[y][x])
  394.  
  395.  
  396. def get_pixels():
  397.     """Get the RGB value of all pixels in a 7x7x3 2d array of tuples"""
  398.  
  399.     width, height = get_shape()
  400.  
  401.     return [[get_pixel(x, y) for x in range(width)] for y in range(height)]
  402.  
  403.  
  404. def show():
  405.     """Update UnicornHat with the contents of the display buffer"""
  406.  
  407.     setup()
  408.     ws2812.show()
  409.  
  410. #Extra functions so that this becomes general NeoPixel interface
  411. #added by Simon Walters
  412. def set_neopixel(index, r, g, b):
  413.     '''
  414.    Set a single pixel to RGB colour
  415.    '''
  416.     setup()
  417.     ws2812.setPixelColorRGB(index, r, g, b)
  418.  
  419.  
  420.  
  421. def get_neopixel(index):
  422.     '''
  423.    Get the RGB value of a single pixel
  424.    '''
  425.     setup()
  426.     pixel = ws2812.getPixelColorRGB(index)
  427.     return (int(pixel.r), int(pixel.g), int(pixel.b))
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement