Daniel Dvorkin

My take on WordPress and related geekery

Photo1

Arduino controlled HTML5 Etch-a-Sketch using Node.js and Websockets

Oh boy, a lot of linkbait keywords in there.

I’ve been waiting for a bit of free time to implement a pice of software that takes a black and white bitmap and moves two stepper motors to make a real Etch-a-Sketch draw the bitmap content. Kinda what this guy did.

But in the mean time, I though it’d be fun to implement this the other way around, which is significantly easier and faster to do. Use two potentiometers connected to the Arduino to control a virtual Etch-a-Sketch. This weekend I sat down to do it, and put myself a limit of one hour. For the computer side my plan was to do something I have experience doing, a Python based GUI using wxpython.

But once I had the Arduino side working as I wanted, it struck me: Why not use this opportunity to do it in a technology I’ve been wanting to have an excuse to play with? First I went for Clojure, but that was almost too easy, so 80% in I decided to grow the scope a bit. I ended up implementing a really simple Node.js app that will read the serial port and pass the data collected from the Arduino to the front-end jQuery via a Websocket, and there use a canvas to make the drawings.

Clone the whole project on GitHub.

Arduino

The Arduino side is really simple. Two potentiometers connected to two different analog inputs (the signal pin), 5v and GND. On my first pass I was getting a lot of noise in the readings, which I tried to solve on the software side, but the results were pretty bad, so I added two 0.1 uF capacitors. You need to connect the capacitors between GND and the signal pin. This, plus a small threshold for computing the readings, works like a charm.

And the code:

/* threshold to keep the noise down */
const int TOLERANCE  = 5;
/* Pin definitions */
int horizontalPot = A0;
int verticalPot   = A1;
/* Global values for the pot's values */
int horizontalVal = 0;
int verticalVal   = 0;

void setup() {
  Serial.begin(9600);
}

void loop() {

  int valX = analogRead( horizontalPot );    // read the value from the sensor 0
  int valY = analogRead( verticalPot );    // read the value from the sensor 1

  bool changed_or_new_X = abs ( valX - horizontalVal ) >= TOLERANCE || ( valX == 0 && horizontalVal != 0 )  || ( valX == 1023 && horizontalVal != 1023 );
  bool changed_or_new_Y = abs ( valY - verticalVal   ) >= TOLERANCE || ( valY == 0 && verticalVal != 0 )    || ( valY == 1023 && verticalVal   != 1023 );

  if ( changed_or_new_X || changed_or_new_Y ) {

    int invertedX = abs( 1024 - valX );
    int invertedY = abs( 1024 - valY );

    Serial.print( invertedX );
    Serial.print( "," );
    Serial.println( invertedY );
    horizontalVal = valX;
    verticalVal = valY;
  }

  delay(3);
}

Node.js

I’m posting the code here just for completion, but you’ll probably want to actually clone it and play with it.

Be aware that this is my first Node.js code ever, so please let me know in the comments if I’m doing something stupid.

Modules:

npm install serialport socket.io express

Main app:

var fs = require( "fs" );
var url = require( "url" );

/* Create the server in the port 9000 */
var http = require( "http" ).createServer(function ( req, res ) {
		var request = url.parse( req.url, false );
		var filename = request.pathname;

		if ( filename == "/" )
			filename = "/index.html";

		/* Append the frontend folder */
		filename = 'frontend' + filename;

		fs.readFile( filename, function ( err, data ) {
			/* Any error on reading the file? */
			if ( err ) {
				if ( err.errno == 34 )  // File not found
					res.writeHead( 404 );
				else
					res.writeHead( 500 );
				res.end();
				return;
			}

			res.writeHead( 200 );
			res.write( data );
			res.end();
		} );
	}
).listen( 9000 );

var io = require( "socket.io" ).listen( http );

io.set('log level', 1);

io.sockets.on( "connection", function ( socket ) {
	// On a new Socket.io connection, load the data provider we want. For now, just Arduino.
	var $provider = require( './providers/arduino.js' ).init( socket );
} );

./providers/arduino.js:

var serial = require( "serialport" );
var SerialPort = serial.SerialPort;

// Replace with the device name in your machine.
var portName = "/dev/cu.usbmodem1421";

var sp = new SerialPort( portName, {
	baudrate:9600,
	parser  :serial.parsers.readline( "\n" )
} );

module.exports = {

	init:function ( socket ) {

		/* When we get a new line from the arduino, send it to the browser via this socket */
		sp.on( "data", function ( data ) {
			console.log( data );
			socket.emit( "message", data.toString() );
		} );

	}

};

frontend/index.html


	Semi Virtual Etch-a-Sketch

<script type="text/<span class=">// <![CDATA[
// <![<span class="hiddenSpellError" pre="">CDATA</span>[
// ]]></script>
// <![<span class="hiddenSpellError" pre="">CDATA</span>[
// ]]></script>
javascript" src="http://code.jquery.com/jquery.min.js">
// ]]><script type="text/javascript">// <![CDATA[
// <![<span class="hiddenSpellError" pre="">CDATA</span>[
// ]]></script>
src</span>="/socket.io/socket.io.js">
// ]]></script>
<script type="text/javascript" src="scripts.js"></script></pre>
<div id="wrapper">
<div id="left"></div>
<div id="middle_wrapper">
<div id="top"></div>
<canvas id="sketch" width="523" height="346"></canvas>
<div id="bottom"></div>
</div>
<div id="right"></div>
</div>
<pre>

frontend/scripts.js

jQuery( document ).ready( function () {
		var $sketch = $( "#sketch" );
		var $context = $sketch[0].getContext( '2d' );

		var $lastX = -1;
		var $lastY = -1;

		var socket = io.connect( "/", {
			"reconnect"                :true,
			"reconnection delay"       :500,
			"max reconnection attempts":10
		} );

		socket.on( "message", function ( data ) {

			data = process_data( data );

			/* Initial position */
			if ( $lastX == -1 ) {
				$lastX = data.x;
				$lastY = data.y;
			}

			$context.moveTo( $lastX, $lastY );
			$context.lineTo( data.x, data.y );

			$lastX = data.x;
			$lastY = data.y;

			$context.strokeStyle = "#000";
			$context.stroke();

		} );

		function process_data( data ) {

			var ret = {
				x:0,
				y:0
			};

			var array = data.split( ',' );

			if ( array.length < 2 )
				return ret;

			ret.x = array[0];
			ret.y = array[1];

			ret = sanitize_size( ret );

			return ret;
		}

		/* Convert pot values to pixel using the canvas size ratio. */
		function sanitize_size( values ) {
			var max_pot = 1024;
			var max_canvas_x = 523;
			var max_canvas_y = 346;

			values.x = values.x * ( max_canvas_x / max_pot );
			values.y = values.y * ( max_canvas_y / max_pot );

			return values;
		}

	}
);

Result

(Disclaimer: I suck at drawing.)

Previous

bbPress Search

Next

Use current directory as taskwarrior project filter

2 Comments

  1. hey, i must say your experiment gave me a new way of thinking… i was wanting to find out whether i could use two analoge potentiometers ,but here at your site i found it to be able to use it on a web browser, that opened up this whole new paradigm in my mind . .thanks

  2. This is awesome! I just started looking into how I can connect my Arduino through Node.js to a browser, and this is really straight-forward starting point. Thank you for sharing!

Leave a Reply to Brand Inman Cancel reply

Powered by WordPress & Theme by Anders Norén