node-rpi-ws281x
-
Upload
martin-schuhfuss -
Category
Technology
-
view
447 -
download
0
Transcript of node-rpi-ws281x
rpi-ws281x
Martin Schuhfuss – @usefulthink
something with raspberry-pi, hardware-hacking, node.js and other stuff
step0: demystify
(image from http://www.electrobob.com/ws2812-level-translator/)
this is what bits actually look like.
rpi-ws281x
rpi-ws2811ws2812
LED-Controller
full-color LED withintegrated controller
ws2812
(image from
5mm
‣ SMD5050 Package
‣ ws2811 controller
‣ three separate LEDsred
green
blue
HARDWARE
controller
3.75µs1.25µs
ws2811one-wire serial data-signal
0V
+5V
t
wavelength λ pulsewidth
frequency 800kHz wavelength 1.25µs
0 2.5µs 5µs
1 0 1 1
ws2811one-wire serial data-signal
0V
+5V
λ = 1.25µs (±600ns)(f = 800kHz)
350ns (±150ns)800ns (±150ns)
0t
ws2811one-wire serial data-signal
0V
+5V
λ = 1.25µs (±600ns)(f = 800kHz)
750ns (±150ns) 600ns (±150ns)
1t
ws2811one-wire serial data-signal
1 0 1 1 0 0 1 0 1 0 0 0 0 0 0 0 1 1 1 1 0 1 0 0
082b f 4
#80b2f4in HTML-Notation
ws2811one-wire serial data-signal
LED 01
DIN DOUT
Register#000000
LED 02
DIN DOUT
Register#000000
LED 03
DIN DOUT
Register#000000
+5V
GND
+5V
GND
+5V
GND
+5VGND
ws2811one-wire serial data-signal
LED 01
DIN DOUT
Register#ff8800
#ff8800
1
LED 02
DIN DOUT
Register#000000
LED 03
DIN DOUT
Register#000000
— —
+5V
GND
+5V
GND
+5V
GND
+5VGND
ws2811one-wire serial data-signal
LED 01
DIN DOUT
Register#ff8800
#00ffff
2
LED 02
DIN DOUT
Register#00ffff
LED 03
DIN DOUT
Register#000000
#00ffff —
+5V
GND
+5V
GND
+5V
GND
+5VGND
ws2811one-wire serial data-signal
LED 01
DIN DOUT
Register#ff8800
#0000ff
3
LED 02
DIN DOUT
Register#ffff00
LED 03
DIN DOUT
Register#0000ff
#0000ff #0000ff
+5V
GND
+5V
GND
+5V
GND
+5VGND
ws2811one-wire serial data-signal
LED 01
DIN DOUT
Register#000000
reset
4
LED 02
DIN DOUT
Register#000000
LED 03
DIN DOUT
Register#000000
reset reset
+5V
GND
+5V
GND
+5V
GND
+5VGND
neopixel etc.where to get your ws2812
http://www.watterott.com/index.php?page=search&keywords=ws2812
http://www.adafruit.com/category/168USAhttps://www.sparkfun.com/search/results?term=ws2812
GERMANY
OTHER INTERNATIONAL
ebay, amazon (search "ws2812")
direct from china: banggood.com, www.dx.com, alibaba.com
build your own :)
step1: control
Raspberry Pi
‣ Broadcom BCM2xxx SoC
‣ 700MHz ARM CPU / 512MB RAM
‣ SD-Card as Harddrive
‣ 100MBit LAN / HDMI / USB / Audio
‣ raspbian OS
Ethernet
HDMI
micro-USB(power supply)
SD-Card
GPIO
Digital-Audio
Analog-Audio USB
Status-LEDs
Raspberry Pi
‣ interface to external hardware
‣ sensors, motors, anything
‣ SPI / UART / I2C
‣ controllable logic-pins
GPIO
step2: drivers
(image from http://www.electrobob.com/ws2812-level-translator/)
the signal-level needs to be changed 1.6 Million times per second.
problem
Arduino: no problem*‣ CPU running at 16MHz
‣ 62 nanoseconds per instruction
‣ lots of time™ in between level changes…
* assembler-programming required :(
awesomeness
solutionsprogramming it with arduino
sleep
solution awesomeness#hhjs
(based on a totally representative study with 1 participant)
Raspbian: problem‣ because multitasking.
‣ when the process runs depends on lots
of factors we can’t control
Raspberry Pi PWM‣ „serializer“-mode
‣ bits in data-registers representsignal-level per PWM-cycle
‣ only a few bytes in registers
‣ needs DMA to transport data
Raspberry Pi PWM
0V
+5V
(frequency: 2.4 MHz)
0t
PWM cycle: 416ns
1 0 0 bits written toPWM-Registers
Raspberry Pi PWM
0V
+5V
(frequency: 2.4 MHz)
1t
PWM cycle: 416ns
1 1 0 bits written toPWM-Registers
Raspbian: problem‣ store prepared pixel-data in memory
‣ let the DMA-Controller transfer data
to PWM-Module
‣ CPU or OS are not involved!
awesomeness
solutions
arduino
sleep
solution awesomeness#hhjs
program it in C on raspberry
build a server in C and control
with node
write a node-addon.
step3: node!
‣ based on a C-Library by Jeremy Garff
‣ wrapper handles only type-checking and conversions between V8-types and C
‣ simple JS-interface:
ws281x = { init: function(numLeds, options) { … }, /** @param ledData {Uint32Array} */ render: function(ledData) { … }, reset: function() { … } };
node-rpi-ws281x
C-Library source:
‣ first challenge: get it to compile.
‣ hard to find documentation
‣ solution was actually pretty simple
// binding.gyp (some stuff left out) { targets: [{ target_name: "rpi_ws281x", sources: ["./src/rpi-ws281x.cc"], dependencies: ["libws2811"] }, { target_name: "libws2811", type: "static_library", sources: [ … ], } ]}
writing node-addons
(required reading: http://nethack4.org/blog/building-c.html)
writing node-addons// rpi_ws281x.cc #include <node.h>#include <v8.h>
using namespace v8;
Handle<Value> Reset(const Arguments& args) { HandleScope scope; // ... binding-code here ... return scope.Close(Undefined());}
void initialize(Handle<Object> exports) { // exports.reset = function Reset() {}; exports->Set(String::NewSymbol("reset"), FunctionTemplate::New(Reset)->GetFunction());}NODE_MODULE(rpi_ws281x, initialize)
no arguments / return undefined
writing node-addons
// rpi_ws281x.cc Handle<Value> Init(const Arguments& args) { HandleScope scope; if(args.Length() < 1 || !args[0]->IsNumber()) { ThrowException(Exception::TypeError( String::New("init(): argument 0 is not a number"))); return scope.Close(Undefined()); } int numLEDs = args[0]->Int32Value();
// ... the actual binding-code ... return scope.Close(Undefined());}
accepting arguments/ throwing exceptions
writing node-addons
‣ writing a simple node addon is not as complicated as it seems
‣ i should write more C++
‣ most of the code to deal with arguments and type-checking
‣ better understanding how V8 works
lessons learned…
finally: Javascript
hello ws281x-native
var ws281x = require('../lib/binding/ws281x-native');var NUM_LEDS = 100, data = new Uint32Array(NUM_LEDS);ws281x.init(NUM_LEDS);data[42] = 0xff0000;ws281x.render(data);setTimeout(function() { ws281x.reset(); }, 2000);
the javascript-side
hello ws281x-native
‣ API feels a bit too low-level
‣ numbers as colors are complicated
‣ need to manually manage the data-array
of course it didn't end there
matrix-APIvar ws281x = require('../lib/ws281x'), m = ws281x.createMatrix(10,10) Color = ws281x.Color;var offset = 0, c1 = new Color('red'), c2 = new Color('blue');setInterval(function() { m.clear(); for(var i=0; i<10; i++) { var color = Color.mix(c1, c2, ((i % 10) / 10)); m.set(i%10, (i+offset)%10, color); } offset++; m.render();}, 1000 / 30);
the javascript-side
awesomeness
solutions
arduino
sleep
solution awesomeness#hhjs
program it in C on raspberry
build a server in C and control
with node
write a node-addon.
✔
awesomeness
solutions
sleep
solution awesomeness#hhjs
program it in C on raspberry
build a server in C and control
with node
write anode-addon.
use <canvas>
canvas-API
‣ web-technology FTW!
‣ has more stuff than we need.
‣ can be rendered server-side with node-canvas
‣ also works in the browser
canvas-APIvar ws281x = require('../lib/ws281x'), canvas = ws281x.createCanvas(10,10), ctx = canvas.ctx, Color = ws281x.Color;var c1 = new Color('red'), c2 = new Color('blue');function rnd(max) { return (max || 1) * Math.random(); }function rndi(max) { return Math.round(rnd(max)); }setInterval(function() { var c = Color.mix(c1,c2, rnd()); ctx.clearRect(0,0,10,10); ctx.fillStyle = 'rgb(' + c.rgb.join(',') + ')'; ctx.fillRect(rndi(10)-2, rndi(10)-2, rndi(10), rndi(10)); canvas.render();}, 1000/5);
the javascript-side
more to come…
‣ open-source everything [really soon]
‣ browser-based IDE on the raspberry [in progress]
‣ integrate with other hardware [planning]
‣ improved 3d-printed casing [some day…]
final thoughts
‣ do something you love. just for the fun.
‣ understand how stuff works.
‣ learn something new.
‣ no deadlines (well, except you do a talk about it)
why would you want to do that?
thank you!come to me if you have any questions…
<3
Martin Schuhfuss – @usefulthink