README.md

# Blinkchain

[![Coverage Status](https://coveralls.io/repos/github/GregMefford/blinkchain/badge.svg?branch=master)](https://coveralls.io/github/GregMefford/blinkchain?branch=master)

Drive WS2812B "NeoPixel" RGB LED strips from a Raspberry Pi using Elixir!

![Rainbow Demo](resources/blinkchain_rainbow.gif)

This project was designed to make it easy to drive a string of AdaFruit
NeoPixels from a Raspberry Pi using [Nerves](http://nerves-project.org). The
code would probably also work outside of Nerves with minor modifications to the
Makefile, if you so desire.

> NOTE: This library used to be called `nerves_neopixel`. The reason for the
> new name and major version bump is that I wanted to overhaul the API and
> also make it less-specific to Nerves and NeoPixels. For example, it could be
> used in the future to control DotStar LED chains from Raspbian Linux.
>
> If you're looking for the source for the last version of `nerves_neopixel`,
> you can find it [here](https://github.com/GregMefford/blinkchain/tree/v0.4.0).

## Installation

Add it to your list of dependencies in `mix.exs`:

```elixir
  def deps do
    [{:blinkchain, "~> 1.0"}]
  end
```

If you've cloned the `blinkchain` repository, be sure to check out the
`rpi_ws281x` submodule:

```sh
$ git submodule init
$ git submodule update
```

## Connections

Only a subset of GPIO pins on the Raspberry Pis can control the NeoPixels. See
[GPIO Usage](https://github.com/jgarff/rpi_ws281x#gpio-usage) for details.
Additionally, since the Raspberry Pi has 3.3V I/O outputs and the NeoPixels
require 5V I/O input, you'll need a level shifter to convert between voltages.

You can read more about using NeoPixels with Nerves in [my blog post about the
project](http://www.gregmefford.com/blog/2016/01/22/driving-neopixels-with-elixir-and-nerves).

## Usage

Supervision trees and an Elixir `Port` are used to maintain fault-tolerance
when interfacing with the low-level driver, which is written in C. For example,
let's imagine that you want to drive a [Pimoroni Unicorn pHAT], which has a
grid of 8 by 4 NeoPixels, and an [Adafruit NeoPixel Stick], which has a row of
8 NeoPixels. If we put the Stick next to the pHAT, we can imagine a virtual
drawing canvas that is 8 pixels wide and 5 pixels tall, like so:

[Pimoroni Unicorn pHAT]: https://shop.pimoroni.com/products/unicorn-phat
[Adafruit NeoPixel Stick]: https://www.adafruit.com/product/1426

```
# Y  X: 0  1  2  3  4  5  6  7
# 0  [  0  1  2  3  4  5  6  7 ] <- Adafruit NeoPixel Stick on Channel 1 (pin 13)
#    |-------------------------|
# 1  |  0  1  2  3  4  5  6  7 |
# 2  |  8  9 10 11 12 13 14 15 | <- Pimoroni Unicorn pHat on Channel 0 (pin 18)
# 3  | 16 17 18 19 20 21 22 23 |
# 4  | 24 25 26 27 28 29 30 31 |
#    |-------------------------|
```

First, you need to configure which GPIO pin(s) to use and how the pixels are
arranged in each chain on the virtual drawing canvas:

```elixir
# config/config.exs
use Mix.Config

config :blinkchain,
  canvas: {8, 5}

config :blinkchain, :channel0,
  pin: 18,
  type: :grb,
  brightness: 32,
  gamma: gamma,
  arrangement: [
    %{
      type: :matrix,
      origin: {0, 1},
      count: {8, 4},
      direction: {:right, :down},
      progressive: true
    }
  ]

config :blinkchain, :channel1,
  pin: 13,
  type: :grb,
  brightness: 32,
  gamma: gamma,
  arrangement: [
    %{
      type: :strip,
      origin: {0, 0},
      count: 8,
      direction: :right
    }
  ]
```

Then, in your application code, you can use the various [Blinkchain API]
drawing commands to set the color of each pixel on the virtual canvas before
calling `Blinkchain.render/0` to present the virtual canvas onto the physical
NeoPixel LEDs. For example, to generate a scrolling rainbow pattern, assuming
that `c1` through `c5` are updated for each frame, you can do:

[Blinkchain API]: https://hexdocs.pm/packages/blinkchain

```elixir
# lib/rainbow/worker.ex

# Shift all pixels to the right
Blinkchain.copy(%Point{x: 0, y: 0}, %Point{x: 1, y: 0}, 7, 5)

# Populate the five leftmost pixels with new colors
Blinkchain.set_pixel(%Point{x: 0, y: 0}, c1)
Blinkchain.set_pixel(%Point{x: 0, y: 1}, c2)
Blinkchain.set_pixel(%Point{x: 0, y: 2}, c3)
Blinkchain.set_pixel(%Point{x: 0, y: 3}, c4)
Blinkchain.set_pixel(%Point{x: 0, y: 4}, c5)

Blinkchain.render()
```

To see more about how this code works or try it out for yourself, check out
[the included `rainbow` example](https://github.com/GregMefford/blinkchain/examples/rainbow).