mirror of
https://github.com/sexybiggetje/pixdisp.git
synced 2024-11-21 20:41:03 +01:00
Working implementation of drivers, and drawing application. Things light up!
This commit is contained in:
parent
f93a20a627
commit
7c212e22ee
11 changed files with 236 additions and 25 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -1,3 +1,6 @@
|
||||||
|
# Application data
|
||||||
|
config.json
|
||||||
|
|
||||||
# Logs
|
# Logs
|
||||||
logs
|
logs
|
||||||
*.log
|
*.log
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
NEEDS BETTER README!
|
NEEDS BETTER README!
|
||||||
|
|
||||||
|
Make sure you are on a recent nodejs version. Raspbian has old version. Use nodesource ;).
|
||||||
|
|
||||||
npm install
|
npm install
|
||||||
|
|
||||||
node pixdisp.js
|
node pixdisp.js
|
||||||
|
|
14
config.example.json
Normal file
14
config.example.json
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
{
|
||||||
|
"bindAddr": "0.0.0.0",
|
||||||
|
"bindPort": 8080,
|
||||||
|
|
||||||
|
"driver": "dummy",
|
||||||
|
|
||||||
|
"matrix": {
|
||||||
|
"width": 16,
|
||||||
|
"height": 16,
|
||||||
|
"brightness": 1,
|
||||||
|
"flipHorizontal": false,
|
||||||
|
"flipVertical": false
|
||||||
|
}
|
||||||
|
}
|
67
controllers/apicontroller.js
Normal file
67
controllers/apicontroller.js
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
class ApiController {
|
||||||
|
|
||||||
|
constructor( server, driver ) {
|
||||||
|
|
||||||
|
this.server = server;
|
||||||
|
this.driver = driver;
|
||||||
|
|
||||||
|
this.server.post( { path: '/api/writecanvas' }, this.writecanvas.bind( this ) );
|
||||||
|
this.server.get( { path: '/api/write' }, this.write.bind( this ) );
|
||||||
|
this.server.get( { path: '/api/setpixel/:x/:y/:r/:g/:b' }, this.setPixel.bind( this ) );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
writecanvas( request, resource, next ) {
|
||||||
|
|
||||||
|
this.driver.clearMatrix();
|
||||||
|
|
||||||
|
resource.setHeader( 'Access-Control-Allow-Origin', '*' );
|
||||||
|
|
||||||
|
let data = JSON.parse( request.params.data );
|
||||||
|
|
||||||
|
for ( let obj of data) {
|
||||||
|
this.driver.setPixel( obj.y, obj.x, obj.r, obj.g, obj.b );
|
||||||
|
}
|
||||||
|
|
||||||
|
let buffer = this.driver.getBuffer();
|
||||||
|
this.driver.write( buffer );
|
||||||
|
|
||||||
|
resource.json( 200, { 'msg': 'Ok' } );
|
||||||
|
return next();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
write( request, resource, next ) {
|
||||||
|
|
||||||
|
resource.setHeader( 'Access-Control-Allow-Origin', '*' );
|
||||||
|
|
||||||
|
let buffer = this.driver.getBuffer();
|
||||||
|
this.driver.write( buffer );
|
||||||
|
|
||||||
|
resource.json( 200, { 'msg': 'Ok' } );
|
||||||
|
return next();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
setPixel( request, resource, next ) {
|
||||||
|
|
||||||
|
resource.setHeader( 'Access-Control-Allow-Origin', '*' );
|
||||||
|
|
||||||
|
this.driver.setPixel(
|
||||||
|
request.params.x,
|
||||||
|
request.params.y,
|
||||||
|
request.params.r,
|
||||||
|
request.params.g,
|
||||||
|
request.params.b
|
||||||
|
);
|
||||||
|
|
||||||
|
resource.json( 200, { 'msg': 'Ok' } );
|
||||||
|
return next();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.ApiController = ApiController;
|
|
@ -10,6 +10,9 @@ class Driver {
|
||||||
this.setSize( 16, 16 );
|
this.setSize( 16, 16 );
|
||||||
this.setBrightness( 1 );
|
this.setBrightness( 1 );
|
||||||
|
|
||||||
|
this.flipHorizontal = false;
|
||||||
|
this.flipVertical = false;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -19,7 +22,7 @@ class Driver {
|
||||||
this.width = w;
|
this.width = w;
|
||||||
this.height = h;
|
this.height = h;
|
||||||
|
|
||||||
this.createMatrix();
|
this.clearMatrix();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -84,6 +87,8 @@ class Driver {
|
||||||
* @return {Buffer}
|
* @return {Buffer}
|
||||||
*/
|
*/
|
||||||
getBuffer() {
|
getBuffer() {
|
||||||
|
this.flipMatrix( this.flipHorizontal, this.flipVertical );
|
||||||
|
|
||||||
let buffer = new Buffer( this.width * this.height * 3 );
|
let buffer = new Buffer( this.width * this.height * 3 );
|
||||||
|
|
||||||
let size = this.getSize();
|
let size = this.getSize();
|
||||||
|
@ -100,22 +105,38 @@ class Driver {
|
||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write output to the device. Implement at driver level.
|
||||||
|
*/
|
||||||
write( buffer ) {
|
write( buffer ) {
|
||||||
console.log( 'Driver should implement this' );
|
console.log( 'Driver should implement this' );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flip the matrix along it's axis.
|
||||||
|
*/
|
||||||
|
flipMatrix( horizontal, vertical ) {
|
||||||
|
if ( horizontal === true ) {
|
||||||
|
for ( let i = 0; i < this.matrix.length; i++ ) {
|
||||||
|
this.matrix[ i ].reverse();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( vertical === true ) {
|
||||||
|
this.matrix.reverse();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create the internal frame
|
* Create the internal frame
|
||||||
*/
|
*/
|
||||||
createMatrix() {
|
clearMatrix() {
|
||||||
this.matrix = [];
|
this.matrix = [];
|
||||||
|
|
||||||
let size = this.getSize();
|
for ( let y = 0; y < this.height; y++ ) {
|
||||||
|
|
||||||
for ( let y = 0; y < size.height; y++ ) {
|
|
||||||
this.matrix.push( [] );
|
this.matrix.push( [] );
|
||||||
|
|
||||||
for ( let x = 0; x < size.width; x++ ) {
|
for ( let x = 0; x < this.width; x++ ) {
|
||||||
this.matrix[ y ].push( {
|
this.matrix[ y ].push( {
|
||||||
r: 0,
|
r: 0,
|
||||||
g: 0,
|
g: 0,
|
||||||
|
|
40
drivers/driverfactory.js
Normal file
40
drivers/driverfactory.js
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
class DriverFactory {
|
||||||
|
constructor() {
|
||||||
|
}
|
||||||
|
|
||||||
|
createFromConfig( config ) {
|
||||||
|
|
||||||
|
let driver;
|
||||||
|
|
||||||
|
switch ( config.driver.toLowerCase() ) {
|
||||||
|
|
||||||
|
default:
|
||||||
|
console.warn( "Unknown driver defaulting to Dummy" );
|
||||||
|
|
||||||
|
case "dummy":
|
||||||
|
let { Dummy } = require( './dummy' );
|
||||||
|
driver = new Dummy();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "pimoroniunicorn":
|
||||||
|
let { PimoroniUnicorn } = require( './pimoroniunicorn' );
|
||||||
|
driver = new PimoroniUnicorn();
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
driver.setSize( config.matrix.width, config.matrix.height );
|
||||||
|
driver.setBrightness( config.matrix.brightness );
|
||||||
|
|
||||||
|
driver.flipHorizontal = config.matrix.flipHorizontal;
|
||||||
|
driver.flipVertical = config.matrix.flipVertical;
|
||||||
|
|
||||||
|
return driver;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.DriverFactory = DriverFactory;
|
15
drivers/dummy.js
Normal file
15
drivers/dummy.js
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
let { Driver } = require( './driver' );
|
||||||
|
|
||||||
|
class Dummy extends Driver {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
write( buffer ) {
|
||||||
|
console.log( buffer );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.Dummy = Dummy;
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
let { Driver } = require( './driver' );
|
let { Driver } = require( './driver' );
|
||||||
|
|
||||||
class UnicornHatHD extends Driver {
|
class PimoroniUnicorn extends Driver {
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
|
@ -26,4 +26,4 @@ class UnicornHatHD extends Driver {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.UnicornHatHD = UnicornHatHD;
|
exports.PimoroniUnicorn = PimoroniUnicorn;
|
41
pixdisp.js
41
pixdisp.js
|
@ -1,18 +1,36 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
let { UnicornHatHD } = require( './drivers/unicornhathd' );
|
let fs = require( 'fs' );
|
||||||
|
let config;
|
||||||
|
let api;
|
||||||
|
|
||||||
|
if ( fs.existsSync( 'config.json' ) ) {
|
||||||
|
let contents = fs.readFileSync( 'config.json' );
|
||||||
|
config = JSON.parse( contents );
|
||||||
|
} else {
|
||||||
|
let contents = fs.readFileSync( 'config.example.json' );
|
||||||
|
config = JSON.parse( contents );
|
||||||
|
}
|
||||||
|
|
||||||
|
let driver;
|
||||||
|
let { DriverFactory } = require( './drivers/driverfactory' );
|
||||||
|
let driverFactory = new DriverFactory();
|
||||||
|
|
||||||
|
let { ApiController } = require( './controllers/apicontroller' );
|
||||||
|
|
||||||
let restify = require( 'restify' );
|
let restify = require( 'restify' );
|
||||||
|
|
||||||
let bindAddr = '0.0.0.0';
|
let bindAddr = config.bindAddr;
|
||||||
let bindPort = 8080;
|
let bindPort = config.bindPort;
|
||||||
|
|
||||||
let server = restify.createServer( {
|
let server = restify.createServer( {
|
||||||
handleUpgrades: true
|
handleUpgrades: true
|
||||||
} );
|
} );
|
||||||
|
|
||||||
server.use( restify.plugins.queryParser() );
|
server.use( restify.plugins.queryParser() );
|
||||||
server.use( restify.plugins.bodyParser() );
|
server.use( restify.plugins.bodyParser( {
|
||||||
|
mapParams: true
|
||||||
|
} ) );
|
||||||
server.use( restify.plugins.jsonp() );
|
server.use( restify.plugins.jsonp() );
|
||||||
server.use( restify.plugins.gzipResponse() );
|
server.use( restify.plugins.gzipResponse() );
|
||||||
server.use( restify.plugins.throttle(
|
server.use( restify.plugins.throttle(
|
||||||
|
@ -23,6 +41,11 @@ server.use( restify.plugins.throttle(
|
||||||
}
|
}
|
||||||
));
|
));
|
||||||
|
|
||||||
|
driver = driverFactory.createFromConfig( config );
|
||||||
|
driver.write( driver.getBuffer() );
|
||||||
|
|
||||||
|
api = new ApiController( server, driver );
|
||||||
|
|
||||||
server.get(/.*/, restify.plugins.serveStatic({
|
server.get(/.*/, restify.plugins.serveStatic({
|
||||||
|
|
||||||
'directory': 'www',
|
'directory': 'www',
|
||||||
|
@ -37,12 +60,4 @@ server.listen( bindPort, bindAddr, function() {
|
||||||
console.log( '%s listening at %s ', server.name , server.url );
|
console.log( '%s listening at %s ', server.name , server.url );
|
||||||
console.log( 'Ready.' );
|
console.log( 'Ready.' );
|
||||||
|
|
||||||
// Some test code
|
});
|
||||||
let driver = new UnicornHatHD();
|
|
||||||
driver.setPixel( 1, 1, 255, 0, 0 );
|
|
||||||
driver.setPixel( 5, 5, 255, 0, 255 );
|
|
||||||
|
|
||||||
let buffer = driver.getBuffer();
|
|
||||||
driver.write( buffer );
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
|
@ -19,10 +19,7 @@
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<section class="section view">
|
<section class="section view">
|
||||||
<a class="button is-small is-link">Load current</a>
|
<a class="button is-small is-success" id="submitImage">Submit</a>
|
||||||
<a class="button is-small is-link">Capture camera</a>
|
|
||||||
<a class="button is-small is-link">Browse history</a>
|
|
||||||
<a class="button is-small is-success">Submit</a>
|
|
||||||
<br /><br />
|
<br /><br />
|
||||||
<div class="columns">
|
<div class="columns">
|
||||||
<div class="column is-half" id="canvasHolder">
|
<div class="column is-half" id="canvasHolder">
|
||||||
|
|
|
@ -18,6 +18,7 @@ class App
|
||||||
holder.appendChild( canvas );
|
holder.appendChild( canvas );
|
||||||
|
|
||||||
canvas.addEventListener( 'click', this.handleCanvas );
|
canvas.addEventListener( 'click', this.handleCanvas );
|
||||||
|
document.getElementById( 'submitImage' ).addEventListener( 'click', this.submitCanvas );
|
||||||
|
|
||||||
this.canvas = canvas;
|
this.canvas = canvas;
|
||||||
this.context = canvas.getContext( '2d' );
|
this.context = canvas.getContext( '2d' );
|
||||||
|
@ -118,6 +119,42 @@ class App
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
submitCanvas( ev ) {
|
||||||
|
|
||||||
|
if ( ev.preventDefault ) {
|
||||||
|
ev.preventDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
let imageData = app.context.getImageData( 0, 0, app.canvas.width, app.canvas.height );
|
||||||
|
let request = new XMLHttpRequest();
|
||||||
|
|
||||||
|
request.open( 'POST', '/api/writecanvas', true );
|
||||||
|
request.setRequestHeader( 'Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8' );
|
||||||
|
|
||||||
|
let obj = [];
|
||||||
|
for ( let x = 0; x < app.canvas.width; x++ ) {
|
||||||
|
for ( let y = 0; y < app.canvas.height; y++ ) {
|
||||||
|
let index = ( y * app.canvas.width + x ) * 4;
|
||||||
|
let r = imageData.data[ index ];
|
||||||
|
let g = imageData.data[ index + 1 ];
|
||||||
|
let b = imageData.data[ index + 2 ];
|
||||||
|
|
||||||
|
if ( r > 0 || g > 0 || b > 0 ) {
|
||||||
|
obj.push( {
|
||||||
|
x: x,
|
||||||
|
y: y,
|
||||||
|
r: r,
|
||||||
|
g: g,
|
||||||
|
b: b
|
||||||
|
} )
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
obj = encodeURI( 'data=' + JSON.stringify( obj ) );
|
||||||
|
request.send( obj );
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let app = new App();
|
let app = new App();
|
Loading…
Reference in a new issue