아두이노와 node.js 통신환경구축하기 Node.js 설치하고개발환경설정하기 다운로드하기 http:www.nodejs.org 페이지에서 install 버튼을누르면 OS 에맞는인스톨러를다운로드해준다. 부록 - 1
다음으로 installer 를실행한다. 부록 - 2
이제 node.js 가설치되었는지확인하자. 시작버튼을누른다. 모든프로그램을눌러보자. 다음과같이 Node.js 관련파일들이설치된것을볼수있다. 설치를끝냈으면이제간단한웹서버를만들어보자. http:www.nodejs.org 홈페이지에있는예제를이용한다. 먼저다음프로그램을실행시킨다. 다음과같이실행된다. 부록 - 3
노트장을열어다음과같이작성한다. var http = require('http'); http.createserver(function (req, res) { res.writehead(200, {'Content-Type': 'text/plain' res.end('hello World\n'); }).listen(1337, '127.0.0.1'); console.log('server running at http:127.0.0.1:1337/'); 이코드를 example.js 로저장한다. 여기서는 [ 내문서 ] 에저장한다. 다음으로다음과같이해당파일을실행한다. 부록 - 4
이제웹브라우저로확인해보면다음과같이메시지가출력되는것을확인할수있다. 이제 node.js 를이용하여시리얼포트를제어해보자. node.js 를이용하여시리얼포트를제어하기위해서는 serialport 패키지를설치해야한다. 다음사이트를참조한다. https:www.npmjs.org/package/serialport 다음과같이명령을실행시켜 serialport 패키지를설치한다. 그런데다음과같이에러메시지가발생한다. npm 디렉터리가 C:\Users\ 사용자 \AppData\Roaming 디렉터리에만들어지지않아발생하는메시지이다. 일단 C:\Users\ 사용자디렉터리로이동한다. 그런데 AppData 부록 - 5
디렉터리가보이지않는다. 다음과같이직접 AppData 를입력하면디렉터리내용이 보이게된다. Roaming 디렉터리로들어가 [ 새폴더 ] 메뉴를이용하여 npm 폴더를만들어준다. 다음과같이만들어준다. 다시한번다음과같이명령을주도록하자. 다음과같이설치가진행된다. 부록 - 6
다음과같이설치가완료된다. 이제설치된 serialport 패키지를테스트해보자. 다음과같이 Documents 디렉터리로이동한다. 노트장을열어다음과같이작성한다. var serialport = require("serialport"); serialport.list(function (err, ports) { ports.foreach(function(port) { console.log(port.comname); console.log(port.pnpid); console.log(port.manufacturer); 부록 - 7
이코드를 serialport_check.js 로저장한다. 여기서는 [ 내문서 ] 에저장한다. 아두이노가컴퓨터에연결된것을확인한다. 실행시키면다음과같이출력된다. 여기서는 COM7 로표시된다. 장치관리자에서다음과같이포트를확인한다. 이제 serialport 패키지를사용해보자. 아두이노스케치를다음과같이작성한후, 업로드한다. void setup() { Serial.begin(9600); } void loop() { Serial.print("Hello node.js"); } delay(1000); 노트장을열어다음과같이작성한다. var SerialPort = require("serialport").serialport var serialport = new SerialPort("COM7", { baudrate: 9600 }, false); 부록 - 8
serialport.open(function () { console.log('connected...'); serialport.on('data', function(data) { 아두이노에서오는데이터를출력한다. console.log('data received: ' + data); 이코드를 serialport_server.js 로저장한다. 여기서는 [ 내문서 ] 에저장한다. 실행시키면다음과같이출력된다. 부록 - 9
이제 node.js 를이용하여아두이노의 LED 를제어해보자. LED 를다음과같이연결한다. 다음과같이 Blink 예제를연다. 부록 - 10
컴파일하고업로드하면 LED가깜빡이는것을볼수있다. 예제를다음과같이수정한다. /* Blink Turns on an LED on for one second, then off for one second, repeatedly. This example code is in the public domain. */ Pin 13 has an LED connected on most Arduino boards. give it a name: int led = 13; the setup routine runs once when you press reset: void setup() { initialize the digital pin as an output. Serial.begin(9600); pinmode(led, OUTPUT); } the loop routine runs over and over again forever: void loop() { static int incomingvalue = 0; 보낸값을저장하기위한변수선언 if ( Serial.available() > 0 ) { 뭔가입력값이있다면 incomingvalue = Serial.read(); } if ( incomingvalue == 49 ) { 값이 '1' 이면 digitalwrite(13, HIGH); LED를켠다. } } if ( incomingvalue == 48 ) { 값이 '0' 이면 digitalwrite(13, LOW); LED를끈다. } Serial.print(incomingValue); 부록 - 11
컴파일하고업로드한다. 노트장을열어다음과같이작성한다. var SerialPort = require("serialport").serialport Arduino 가 "COM7" 에연결되었다고가정한다. var serialport = new SerialPort("COM7", { baudrate: 9600}, false); var ledstatus = 0; serialport.open(function () { console.log('connected...'); serialport.on('data', function(data) { 아두이노에서오는데이터를출력한다. console.log('data received: ' + data); setinterval(function(){ ledstatus =!ledstatus; console.log(ledstatus); LED 가 ON/OFF 된다. serialport.write(ledstatus==true? "1" : "0", function(err, results) { }, 1000); 이코드를 serialport_server_led_on_off.js 로저장한다. 여기서는 [ 내문서 ] 에저장한다. serialport_server_led_on_off.js 파일을다음과같이실행시킨다. >node serialport_server_led_on_off.js LED 가주기적으로깜빡이는것을볼수있다. 부록 - 12
이제 NodeJS 로아주간단한웹서버를띄운후웹에서 LED 켜보자. 노트장을열어다음과같이작성한다. var serialport = require("serialport"); var http = require('http'); var UrlParser = require('url'); var serialport = new serialport.serialport( "COM7", { baudrate : 9600 serialport.on("open",function() { console.log("open success") http.createserver(function (req, res) { var result = UrlParser.parse(req.url,true); if(result.pathname == '/on') { serialport.write("1",function() { } else if(result.pathname == '/off') { serialport.write("0",function() { } res.writehead(200, {'Content-Type': 'text/plain' res.end('hello Arduino\n'); }).listen(1337); console.log('server running at 1337'); 이코드를 serialport_webserver_led_on_off.js 로저장한다. 여기서는 [ 내문서 ] 에저장한다. serialport_webserver_led_on_off.js 파일을실행시킨다. 간단한웹서버가구동된다. 부록 - 13
다음과같이웹브라우저로서버에접속한다. 다음과같이 on 을붙인다. LED 가켜지는것을볼수있다. 다음과같이 off 로변경해본다. LED 가꺼지는것을볼수있다. 그러면이제 Express 라는 NodeJS 모듈을이용해간단히 LED 를제어해보도록하자. express 는웹프레임워크로이것을이용하면웹서버를손쉽게구현할수있다. Express 에대해간단히살펴보면다음과같다. node.js 는여러종류의웹개발프레임웍을제공한다. 얼마전에 Paypal 이내부시스템을대규모로 node.js 로전환하면서오픈소스화한 KarkenJS 나 Meteo 등여러가지프레임웍이있는데, 그중에서가장많이사용되는프레임웍중하나인 Express 에대해서설명하고자한다. Express 는웹페이지개발및 REST API 개발에최적화된프레임웍으로매우사용하기가쉽다. Express 를설치하기위해서는다음사이트에있는 Quick Start 를참고한다. https:www.npmjs.org/package/express-generator 그러면 express 를설치해보자. 다음과같이 express-generator를설치한다. 이렇게하면사용자계정 \Appdata\Roaming\npm\node_modules\express-generator\bin에 javascript로작성된 express 파일을생성한다. 이후부터 console에서 express를실행할수있다. 부록 - 14
마지막에 @3 은 express-generator 버전을나타낸다. windows 에서는이전버전을 사용해야한다. 다음과같이설치가완료된다. 다음과같이프로젝트를생성하고해당디렉터리로이동한다. -e 옵션은웹프레임워크엔진으로 ejs 를사용한다는의미이다. 기본엔진은 jade 이다. 여기서프로젝트디렉터리가꼭./tmp/foo 일필요는없다. 예를들어다음과같이명령을줄수도있다. >express -e./my_webserver && cd./my_webserver 부록 - 15
다음과같이설치된다. 다음과같이디렉터리의내용을확인해보자. 다음과같이의존성이있는추가패키지를설치한다. 부록 - 16
다음과같이추가패키지가설치된다. 부록 - 17
다음과같이추가로설치된패키지를확인해보자. node_modules 디렉터리가추가된것을볼수있다. 다음은지금까지의디렉터리구조이다. 부록 - 18
이제다음과같이웹서버를구동시킨다. 다음창이뜨면 [ 액세스허용 ] 버튼을누른다. 그러면다음과같이웹서버가구동되는것을볼수있다. 다음과같이 3000 번포트로접속한다. 웹서버가동작하는것을볼수있다. 부록 - 19
콘솔창도다음과같이나타나는것을확인한다. 다음과같이접속해보자. 콘솔창도다음과같이확인하자. 그러면어떻게웹서버가구동되는지살펴보자. 먼저 npm start 하면 npm이 package.json을찾아 "start" script를찾아읽는다. 그러면 node./bin/www 명령에의해./bin/www 파일이읽힌다../bin/www 파일은상위디렉터리에있는 app.js 파일을읽는다. npm이 start script를찾지못하면 node server.js를수행한다. 사용자가 http:127.0.0.1:3000 주소로접근하면 views/index.ejs 파일의내용을보내준다. package.json { "name": "application-name", "version": "0.0.1", "private": true, 부록 - 20
} "scripts": { "start": "node./bin/www" }, "dependencies": { "express": "~3.4.8", "static-favicon": "~1.0.0", "morgan": "~1.0.0", "cookie-parser": "~1.0.1", "body-parser": "~1.0.0", "debug": "~0.7.4", "ejs": "~0.8.5" } bin/www 파일의내용을확인해본다. #!/usr/bin/env node var debug = require('debug')('my-application'); var app = require('../app'); app.set('port', process.env.port 3000); var server = app.listen(app.get('port'), function() { debug('express server listening on port ' + server.address().port); app.js 파일의내용은다음과같다. var express = require('express'); var http = require('http'); var path = require('path'); var favicon = require('static-favicon'); var logger = require('morgan'); var cookieparser = require('cookie-parser'); var bodyparser = require('body-parser'); var routes = require('./routes'); var users = require('./routes/user'); 부록 - 21
var app = express(); view engine setup app.set('views', path.join( dirname, 'views')); app.set('view engine', 'ejs'); app.use(favicon()); app.use(logger('dev')); app.use(bodyparser.json()); app.use(bodyparser.urlencoded()); app.use(cookieparser()); app.use(express.static(path.join( dirname, 'public'))); app.use(app.router); app.get('/', routes.index); app.get('/users', users.list); / catch 404 and forwarding to error handler app.use(function(req, res, next) { var err = new Error('Not Found'); err.status = 404; next(err); / error handlers development error handler will print stacktrace if (app.get('env') === 'development') { app.use(function(err, req, res, next) { res.render('error', { message: err.message, error: err 부록 - 22
} production error handler no stacktraces leaked to user app.use(function(err, req, res, next) { res.render('error', { message: err.message, error: {} module.exports = app; require는필요한 module들을로드하는역할을한다. node.js에서 router는특정 URL로들어오는 HTTP Request에대한 handler(node에서는 router라고한다 ) 를의미한다. 다음부분을살펴보자. var routes = require('./routes'); var users = require('./routes/user'); 여기서 routes 핸들러는./routes/index.js 파일을의미하고 users 핸들러는./routes/user.js 파일을의미한다. index.js 파일은디폴트핸들러로생각한다. 다음과같이명령을수행해보자../routes 디렉터리아래에두개의파일이있는것을볼수있다. 부록 - 23
다음부분을살펴보자. app.get('/', routes.index); app.get('/users', users.list); 여기서웹서버의 /( 웹서버의기본홈페이지위치 ) 에대한 HTTP GET 요청이들어오면 routes.index 핸들러를사용하여처리한다는의미이다. 즉,./routes 디렉터리에있는 index.js 파일을읽어 index 함수를이용하여처리한다. /users 디렉터리에대한 HTTP GET 요청이들어오면./routes 디렉터리에있는 users.js 파일을읽어 list 함수를이용하여처리한다는의미이다. 부록 - 24
다음그림을참조한다. 다음은 routes/index.js 파일의내용이다. routes/index.js /* GET home page. */ exports.index = function(req, res){ res.render('index', { title: 'Express' }; 다음은 routes/users.js 파일의내용이다. /* GET users listing. */ exports.list = function(req, res){ res.send('respond with a resource'); }; 이번엔 routes/index.js 파일에의해어떤파일이사용되는지살펴보자. 먼저다음부분의의미를알아보자. view engine setup app.set('views', path.join( dirname, 'views')); app.set('view engine', 'ejs'); 부록 - 25
여기서는템플릿엔진이 ejs로지정되어있다. 그리고 ejs 템플릿파일을저장할위치를 dirname/views로지정되어있는것을볼수있다. 템플릿파일의위치를 path.join( dirname, 'view') 로정의하였는데, dirname은프로그램이현재수행중인파일의위치, 즉, app.js가위치한디렉터리를의미하며, path.join을이용하면, ${ 현재디렉터리 }/views라는경로로지정한것이다. routes/index.js 파일의내용을보면, request에대해 rendering을할때, index라는템플릿을부르고 ( 앞에서엔진과 views 디렉터리를지정했기때문에, dirname/views/index.ejs 파일을사용하게된다. 이때인자로 title="express" 변수를넘기게된다. HTTP response로응답을보내는방법을 rendering이라고하는데, 간단한문자열의경우, response.send( 문자열 ); 을이용해서보낼수도있다. 또는 response code를싫어서보낼때는 response.send(404, 페이지를찾을수없습니다. ); 와같은식으로첫번째인자에 HTTP response code를실어서보내는것도가능하다. 그러면, views/index.ejs 파일의내용을살펴보자. 다음은 views/index.ejs 파일의내용이다. <!DOCTYPE html> <html> <head> <title><%= title %></title> <link rel='stylesheet' href='/stylesheets/style.css' /> </head> <body> <h1><%= title %></h1> <p>welcome to <%= title %></p> </body> </html> 일반적인 HTML과거의유사하다. Parameter를사용하고자할때는 ASP나 JSP처럼 <%= 변수 %> 로사용하면된다. 마찬가지로, for, while,if등간단한스크립트로직도작성할수있다. 부록 - 26
좀더자세한내용은다음사이트를참조한다. http:bcho.tistory.com/887 그러면예제를수정해서 LED 가 Toggle 되는간단한프로젝트를만들어보도록하자. 먼저 views/index.ejs 파일을다음과같이수정한다. <!DOCTYPE html> <html> <head> <title><%= title %></title> <link rel='stylesheet' href='/stylesheets/style.css' /> </head> <body> <h1><%= title %></h1> <p>welcome to <%= title %></p> <!-- 간단히버튼으로 POST를보내도록한다. --> <form action="/" method="post"> <button type="submit"> 눌러봐 </button> </form> </body> </html> 웹을실행시켜보자. 부록 - 27
버튼이나타나는것을볼수있다. 부록 - 28
버튼을누르면다음과같이에러메시지가뜬다. 콘솔창도확인하자. 그러면 app.js 파일을수정해보자. 다음부분을 app.js 파일의마지막에추가한다. 이값에따라 LED 켜지는게결정된다. var ledstatus = false; POST 액션을받으면 LED가 Toggle 되도록한다. app.post('/', function(req, res) { ledstatus = ledstatus? false : true; res.render('index', { title: 'Express' console.log(ledstatus); 부록 - 29
ctrl+c 키를두번눌러프로그램을종료한다. 그리고서버를다시구동시킨다. 다음그 림을참조한다. 그리고버튼을연속으로눌러본다. 부록 - 30
그러면다음과같이표시된다. 여기서는버튼을네번누른경우의화면이다. 그러면 ledstatus 값을시리얼포트로보내 LED를제어해보자. 다음내용을 app.js 파일의뒷부분에추가한다. Serial Port를사용하는예제 var SerialPort = require("serialport").serialport Arduino가 "COM7" 에연결되었다고가정한다. var serialport = new SerialPort("COM7", {baudrate: 9600}, false); serialport.open(function () { console.log(' 접속되었습니다!'); serialport.on('data', function(data) { Arduino에서오는데이터를출력한다. console.log('data received: ' + data); setinterval(function(){ LED가 ON/OFF 될꺼에요 serialport.write(ledstatus? "1" : "0", function(err, results) { }, 100); 부록 - 31
파일을저장하고서버를재구동시키자. 그리고버튼을눌러보자. LED가버튼을반복적으로누름에따라꺼졌다켜졌다하는것을볼수있다. 다음과같이소스를수정해보자. 먼저 app.post() 함수에다음을추가한다. app.post('/', function(req, res) { ledstatus = ledstatus? false : true; res.render('index', { title: 'Express' console.log(ledstatus); serialport.write(ledstatus? "1" : "0", function(err, results) { 그리고 setinterval() 함수를호출하는부분을주석처리한다. serialport.open(function () { console.log(' 접속되었습니다!'); serialport.on('data', function(data) { Arduino에서오는데이터를출력한다. console.log('data received: ' + data); /* setinterval(function(){ LED가 ON/OFF 될꺼에요 serialport.write(ledstatus? "1" : "0", function(err, results) { }, 100); */ 함수의순서는그대로두어도상관없다. 파일을저장하고서버를재구동시키자. 그리고버튼을눌러보자. LED가버튼을반복적으로누름에따라꺼졌다켜졌다하는것을볼수있다. 이상 express 모듈을이용해 LED를제어해보았다. 부록 - 32
매번스케치를업로드하지않고스크립트처럼쓰고자할경우엔 Firmata 를사용한다. Firmata 를사용하면매번업로드하지않고도 Arduino 를자유롭게제어할수있다. Firmata? 뭐죠? Firmata는아두이노같은마이크로컨트롤러와 PC가서로통신하기위해고안된범용프로토콜입니다. 어떻게쓰는건가요? 일단, Arduino에최초 StandardFirmata 라는스케치를업로드합니다. 그후에, Arduino와통신을하기위해서는 Firmata 프로토콜에맞도록메시지만보내주시면됩니다. 만약, NodeJS를사용해서 Arduino와통신하고싶다면 NodeJS으로제작된 Firmata 모듈을사용해 Arduino를제어할수있습니다. NodeJS 를예로들어볼까요? NodeJS 쪽 Firmata 모듈중 Johnny-Five라는 Firmata based Arduino Framework이있습니다. 다음과같은형태로사용할수있습니다. Johnny-five 모듈로보드에접속해 LED를제어한다. var five = require('johnny-five'), board = new five.board(); board.on("ready", function() { 13번 PIN을 OUTPUT으로 this.pinmode(13, 1); 0.1s마다돌면서 ledstatus 값에따라 LED를 ON/OFF 한다. 부록 - 33
this.loop(100, function() { this.digitalwrite( 13, ledstatus? 1 : 0 ); 위예제를이전에만들었던 app.js 마지막부분에붙입니다. 이전에붙였던 serialport 부분은떼어냅니다. 다음은수정된 app.js 파일의내용이다. var express = require('express'); var http = require('http'); var path = require('path'); var favicon = require('static-favicon'); var logger = require('morgan'); var cookieparser = require('cookie-parser'); var bodyparser = require('body-parser'); var routes = require('./routes'); var users = require('./routes/user'); var app = express(); view engine setup app.set('views', path.join( dirname, 'views')); app.set('view engine', 'ejs'); app.use(favicon()); app.use(logger('dev')); app.use(bodyparser.json()); app.use(bodyparser.urlencoded()); app.use(cookieparser()); app.use(express.static(path.join( dirname, 'public'))); app.use(app.router); app.get('/', routes.index); 부록 - 34
app.get('/users', users.list); / catch 404 and forwarding to error handler app.use(function(req, res, next) { var err = new Error('Not Found'); err.status = 404; next(err); / error handlers development error handler will print stacktrace if (app.get('env') === 'development') { app.use(function(err, req, res, next) { res.render('error', { message: err.message, error: err } production error handler no stacktraces leaked to user app.use(function(err, req, res, next) { res.render('error', { message: err.message, error: {} module.exports = app; 이값에따라 LED 켜지는게결정된다. 부록 - 35
var ledstatus = false; POST 액션을받으면 LED가 Toggle 되도록한다. app.post('/', function(req, res) { ledstatus = ledstatus? false : true; res.render('index', { title: 'Express' console.log(ledstatus); serialport.write(ledstatus? "1" : "0", function(err, results) { /* Serial Port를사용하는예제 var SerialPort = require("serialport").serialport Arduino 가 "COM7" 에연결되었다고가정한다. var serialport = new SerialPort("COM7", {baudrate: 9600}, false); serialport.open(function () { console.log(' 접속되었습니다!'); serialport.on('data', function(data) { Arduino에서오는데이터를출력한다. console.log('data received: ' + data); /* setinterval(function(){ LED가 ON/OFF 될꺼에요 serialport.write(ledstatus? "1" : "0", function(err, results) { }, 100); ** */ Johnny-five 모듈로보드에접속해 LED를제어한다. 부록 - 36
var five = require('johnny-five'), board = new five.board({port: COM7 board.on("ready", function() { 13번 PIN을 OUTPUT으로 this.pinmode(13, 1); 0.1s마다돌면서 ledstatus 값에따라 LED를 ON/OFF 한다. this.loop(100, function() { this.digitalwrite( 13, ledstatus? 1 : 0 ); app.post() 일부분을코멘트처리해준다. serialport 부분은전체적으로막아주고, johnnyfive를이용하는부분을추가해준다. 이예제를실행시키기위해서는 johnny-five 모듈이필요하다. 다음과같이 [Node.js command prompt] 를새로실행시켜 johnny-five 모듈을설치해준다. 다음과같이설치가된다. 부록 - 37
부록 - 38
이제아두이노에 StandardFirmata 예제를선택하여업로드해준다. 이제다음과같이웹서버를구동시킨다. 이제웹브라우저로다음과같이접속하여버튼을눌러본다. 부록 - 39
LED 가켜졌다꺼졌다하는것을볼수있다. 다음은웹서버화면이다. NodeJS 말고다른건없나요? https:github.com/firmata/arduino에가시면언어별 Firmata 클라이언트라이브러리가있으니살펴보시면도움이될것같습니다. 이상 Node.js 를이용하여아두이노를제어해봤다. 부록 - 40