Instantly share code, notes, and snippets.
Last active
September 1, 2015 16:35
-
Star
0
(0)
You must be signed in to star a gist -
Fork
0
(0)
You must be signed in to fork a gist
-
Save CLCL/381b40691dad0afda2ad to your computer and use it in GitHub Desktop.
akibaLEDピカリ館の単色LEDマトリクスパネルP10をRaspberry Piのnode.jsで光らせて文字をスクロールする
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"name": "picari-banner-gorinpikku.js", | |
"private": true, | |
"scripts": { "start": "node picari-banner-gorinpikku.js" }, | |
"dependencies": { | |
"canvas": ">=1.2.7", | |
"onoff" : ">=1.0.2", | |
"pi-spi": ">=1.0.0" | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
'use strict'; | |
// picari-banner-gorinpikku.js: | |
// akibaLEDピカリ館の単色LEDマトリクスパネルP10を | |
// Raspberry Piのnode.jsで光らせて文字をスクロールする | |
// 配線については http://cl.hatenablog.com/entry/picari-ledmatrix-raspberrypi 参照 | |
// Usage: | |
// (Raspberry PiにRaspbian Wheezyをインストールして、piユーザでログイン後) | |
// sudo apt-get update | |
// sudo apt-get -y install libcairo2-dev libpango1.0-dev libjpeg8-dev libgif-dev | |
// curl -LO http://node-arm.herokuapp.com/node_latest_armhf.deb | |
// sudo dpkg -i node_latest_armhf.deb | |
// git clone https://gist.github.com/381b40691dad0afda2ad.git picari | |
// cd $_ | |
// curl -LO http://jaist.dl.osdn.jp/mix-mplus-ipa/63545/migu-1m-20150712.zip | |
// unzip migu-1m-20150712.zip | |
// ln -s migu-1m-20150712/migu-1m-regular.ttf . | |
// npm install onoff pi-spi canvas | |
// sudo node picari-banner-gorinpikku.js | |
// | |
// #Use libs/Global Objects | |
// | |
var SPI = require('pi-spi'); | |
var Gpio = require('onoff').Gpio; | |
var Canvas = require('canvas'); | |
// SPI初期化 | |
var spi = SPI.initialize('/dev/spidev0.0'); | |
spi.clockSpeed(7812500); // 7.8MHz(15.6MHzだと取りこぼしがみられた) | |
//spi.bitOrder(SPI.order.LSB_FIRST); // パネル仕様ではLSBだがpi-spiでエラーになるので | |
// | |
// #define | |
// | |
// 信号値定義 | |
var HIGH = 1; | |
var LOW = 0; | |
// 信号ピン定義 | |
var OE = 22; // GPIO22(15pin):Output Enable -> OE | |
var DYNA = 24; // GPIO24(18pin):Dynamic Lighting A(LSB) -> A | |
var DYNB = 23; // GPIO23(16pin):Dynamin Lighting B(MSB) -> B | |
var LATCH = 25; // GPIO25(22pin):74HC595 LATCH -> LAT | |
// MOSI(19pin)[変更不可]:SPI DATA OUTPUT -> /DAT | |
// SCLK(23pin)[変更不可]:SPI CLOCK -> CLK | |
// VDP駆動インターバル定義 | |
var WAIT = 0; // 0: 410Hz, 1: 250Hz | |
// パネル横カラム設定(横8ドット分を1カラム、パネル1枚=4カラム) | |
//var COLUMN = 4; // パネル1枚は4カラム(32dot) | |
var COLUMN = 8; // パネル2枚は8カラム(64dot) | |
//var COLUMN = 12; // パネル3枚は12カラム(96dot) | |
// デバッグ用 | |
var USE_SPI = 1; | |
var USE_CONSOLE = 0; | |
// | |
// # Define Objects | |
// | |
// VRAM オブジェクト パネルの画素分のBuffer4つとそれを取り扱うメソッド | |
// | |
var VRAM = function() { | |
this.buffers = [ | |
new Buffer(COLUMN * 4), | |
new Buffer(COLUMN * 4), | |
new Buffer(COLUMN * 4), | |
new Buffer(COLUMN * 4) | |
]; | |
}; | |
VRAM.prototype = { | |
clean: function() { // buffersをクリアする | |
this.buffers[0].fill(255); | |
this.buffers[1].fill(255); | |
this.buffers[2].fill(255); | |
this.buffers[3].fill(255); | |
}, | |
setBuffers: function(buffers) { // ビットパターンをbuffersにセットする | |
for ( var i = 0; i < buffers.length; i++) { | |
this.buffers[i] = buffers[i]; | |
} | |
}, | |
copy: function(target) { // buffersの内容を別のbuffersにコピー(転送)する | |
this.buffers[0].copy(target.buffers[0]); | |
this.buffers[1].copy(target.buffers[1]); | |
this.buffers[2].copy(target.buffers[2]); | |
this.buffers[3].copy(target.buffers[3]); | |
}, | |
getLength: function() { | |
return this.buffers[0].length; | |
}, | |
getBufferRow: function(r) { | |
return this.buffers[r]; | |
} | |
}; | |
// VDP オブジェクト VRAMオブジェクトの内容でLEDマトリクスパネルを駆動する | |
// | |
var VDP = function(f) { | |
this.workVram = new VRAM(); | |
this.vram = new VRAM(); | |
this.gpio = { | |
oe : new Gpio(f.OE , 'out'), | |
a : new Gpio(f.A , 'out'), | |
b : new Gpio(f.B , 'out'), | |
lat : new Gpio(f.LAT, 'out') | |
} | |
this._init(); | |
}; | |
VDP.prototype = { | |
// VRAMの参照渡し | |
setVram: function(vram) { | |
this.vram = vram; | |
}, | |
on : function() { // LEDパネル点灯 | |
this._loop(0); | |
}, | |
off: function() { // LEDパネル消灯 | |
this.gpio.oe .writeSync(LOW); // 消灯 | |
this.gpio.oe .unexport(); | |
this.gpio.a .unexport(); | |
this.gpio.b .unexport(); | |
this.gpio.lat.unexport(); | |
}, | |
_init: function() { // イニシャライズ | |
this.gpio.oe .writeSync(LOW); // 消灯 | |
this.gpio.lat.writeSync(LOW); // ラッチ | |
}, | |
_loop: function(i) { // VDP内部ループ処理 i: 表示ライン(0~3) | |
var self = this; | |
i %= 4; | |
// テアリング防止のため第0ライン表示時に画素を固定する | |
if (i == 0) { | |
self.vram.copy(self.workVram); | |
} | |
// 画面表示 | |
if (USE_CONSOLE) { | |
if (i == 3) { | |
console.log('\n'); | |
// コンソールにLEDマトリクス点灯パターンを表示する | |
var l = self.vram.getLength(); // 32dotの場合16になる | |
for (var y = 0; y < 16; y++ ) { | |
var str = ''; | |
for (var x = 0; x < l / 4; x++) { | |
var s = 255 - self.vram.getBufferRow(y % 4)[ x * 4 + (Math.floor(3 - Math.floor(y / 4)))]; | |
str += ('0000000' + s.toString(2)).slice(-8); | |
} | |
console.log(str); | |
} | |
} | |
} | |
// SPIデータ転送 | |
if (USE_SPI) { | |
spi.write(self.workVram.getBufferRow(i), function(e) { | |
if (e) console.error(e); | |
// LEDパネル制御線の操作 | |
self.gpio.oe .writeSync(LOW); // パネル消灯 | |
self.gpio.a .write(i & 1); // ダイナミック点灯桁指定(LOW) | |
self.gpio.b .write(i >> 1 & 1); // ダイナミック点灯桁指定(HIGH) | |
self.gpio.lat.write(HIGH); // ラッチ解除 | |
self.gpio.oe .write(HIGH); // パネル点灯 | |
self.gpio.lat.write(LOW); // ラッチ | |
}); | |
} | |
var timerId1 = setTimeout(function() {self._loop(++i);}, WAIT); // 0: 410Hz, 1: 250Hz | |
} | |
}; | |
// | |
// グローバル関数 | |
// | |
function getImage(str) { // 文字列を入力してCanvasコンテキスト画像オブジェクトを得る | |
// HTML5-Canvas描画 | |
var Image = Canvas.Image; | |
var canvas = new Canvas(str.length * 16, 16); | |
var ctx = canvas.getContext('2d'); | |
ctx.antialias = 'none'; | |
ctx.textBaseline = 'top'; | |
//ctx.font = '16px Sans-serif'; | |
var Font = Canvas.Font; | |
var myFont = new Font('MyFont', 'migu-1m-regular.ttf'); | |
ctx.font = '16px MyFont'; | |
ctx.fillText(str, 0, 0); | |
ctx.stroke(); | |
var te = ctx.measureText(str); | |
// Canvas内のピクセル走査 | |
//console.log(te.width); | |
var imageData = ctx.getImageData(0, 0, te.width, 16); | |
return imageData; | |
} | |
function getPattern(ctx, COLUMN, sx) { // ctxの画像を入力してビットパターンの配列を得る | |
// HTML5-Canvas描画 | |
// Canvas内のピクセル走査 | |
var pattern = []; | |
for (var y = 0; y < 16; y++ ) { | |
for (var x = 0; x < COLUMN * 8; x++) { | |
// RGBa | |
var l = ctx.width; | |
pattern.push(ctx.data[((sx + x) + y * l) * 4 + 3] > 0 ? 1 : 0); | |
} | |
} | |
return pattern; | |
} | |
function convPatternToBuffer(pattern, COLUMN) { // ビットパターンの配列を入力してBuffer[4]を得る | |
var buffers = []; | |
//console.log(pattern.length); | |
for (var i = 0; i < 4; i++) { | |
var buf = new Buffer(COLUMN * 4); // LEDパネル1面分(画素512bitのうち1回の駆動での画素128bit=16byte) | |
buf.fill(255); // bufferクリア | |
for (var j = 0; j < COLUMN; j++) { | |
// 配列からbufferに転記するループ | |
for (var k = 0; k < 4; k++) { | |
var byte = 255; | |
var pos = i * COLUMN | |
+ j | |
+ ( (3 - k) * 4 ) * COLUMN; | |
for (var l = 0; l < 8; l++) { | |
byte = byte - (pattern[pos * 8 + l] << (7 -l)); | |
} | |
buf.writeUInt8(byte, j * 4 + k); | |
} | |
} | |
buffers.push(buf); | |
} | |
return buffers; | |
} | |
// | |
// Main Routine | |
// | |
var main = function() { | |
// VRAM準備 | |
var vram = new VRAM(); // このVRAMオブジェクトの参照は書き換えないよう | |
vram.clean(); | |
// VDP動作開始 | |
var vdp = new VDP({ A: DYNA, OE : OE, B: DYNB, LAT: LATCH }); | |
// vramをVDPに登録 | |
vdp.setVram(vram) //参照渡しでVDPに登録されるのでvramはいわばDMAみたいに扱う | |
// VDP表示開始 | |
vdp.on(); // 以降割り込みタイマーでVDPがvramの中身を描画しつづける | |
// 文字パターン準備(コスト高いので使いまわす) | |
//var img = getImage('漢字'); // ctxで'漢字'と書かれたオブジェクトが帰る | |
var img = getImage(' これが噂の!ゴリンピック '); // ctx | |
// VRAM移動操作タイマ | |
var cnt = 0; // 文字の位置を指定するカウンタ | |
var timerId2 = setInterval( function() { | |
var pattern = getPattern(img, COLUMN, cnt); | |
var buffers = convPatternToBuffer(pattern, COLUMN); | |
// vramの参照を書き換えないようにsetBuffersメソッドを使う | |
vram.setBuffers(buffers); | |
cnt++; cnt %= 256; | |
}, | |
33 | |
); | |
process.on('SIGINT', function() { | |
vdp.off(); | |
console.log('\nexit.'); | |
process.exit(0); | |
}); | |
}; | |
main(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment