안녕하세요, 생각의 웹입니다.


이번에는 이전에 소개드린 바 있는 Node.JS에서 Arduino를 제어하는 JavaScript API 모듈인 duino에 온습도 센서인 dht11 를 제어할 수 있도록 기능 추가한 사례를 소개하려 합니다.

이를 위해서 먼저 duino github 소스 코드를 다운로드 받습니다.

duino 프로젝트를 git clone 하거나 다운로드 받아 압축을 풀면 examples, lib, node_modules, src 폴더와 index.js 파일 등이 있습니다.


1. arduino에 DHT11 센서 코드 추가하기

src 폴더에는 du.ino 파일이 존재합니다. 이 파일은 Arduino IDE의 프로젝트 파일로써 Arduino 보드에서 동작할 firmware 코드를 담고 있습니다.

Arduino IDE를 이용해 du.ino 파일을 열면 다음과 같은 코드로 구성되어 있습니다.


#include <servo.h>
bool debug = false;
 // ... omitted ...
void setup() {
  Serial.begin(115200);
}
void loop() {
  while(Serial.available() > 0) {
    char x = Serial.read();
    if (x == '!') index = 0;      // start
    else if (x == '.') process(); // end
    else messageBuffer[index++] = x;
  }
}
/*
 * Deal with a full message and determine function to call
 */
void process() {
  // ... omitted ...
 switch(cmdid) {
    case 0:  sm(pin,val);              break;
    case 1:  dw(pin,val);              break;
    case 2:  dr(pin,val);              break;
    case 3:  aw(pin,val);              break;
    case 4:  ar(pin,val);              break;

    case 97: handlePing(pin,val,aux);  break;
    case 98: handleServo(pin,val,aux); break;
    case 99: toggleDebug(val);         break;
    default:                           break;
  }
}
 // ... omitted ...


코드를 간략히 설명하면, Arduino controller는 setup() 함수 호출하여 serial port의 전송 속도를 지정 후 loop() 함수로 진입하여 메세지를 패칭(fetching)하여 특정 동작을 수행하게 됩니다. 이때 process() 함수 내 switch() 문으로 명령 ID를 구분해 적절한 이벤트 핸들러 (event handler) 가 호출되는 방식입니다.

따라서, 여기에 DHT11 센서에서 정보를 가져오는 명령 ID를 추가하고 이에 걸맞는 이벤트 핸들러를 추가하도록 하겠습니다. 명령 ID는 96으로, 관련 이벤트 핸들러는 handleDHT11(pin, val); 로 정의하겠습니다.


#include <servo.h>
// webofthink:For supporting DHT11 sensor
#include 
dht11 rht01;
// webofthink:end

bool debug = false;
 // ... omitted ...
}
/*
 * Deal with a full message and determine function to call
 */
void process() {
  // ... omitted ...
 switch(cmdid) {
    case 0:  sm(pin,val);              break;
    case 1:  dw(pin,val);              break;
    case 2:  dr(pin,val);              break;
    case 3:  aw(pin,val);              break;
    case 4:  ar(pin,val);              break;
    case 96: handleDHT11(pin, val);    break; /* webofthink:add DHT11 support */
    case 97: handlePing(pin,val,aux);  break;
    case 98: handleServo(pin,val,aux); break;
    case 99: toggleDebug(val);         break;
    default:                           break;
  }
}
 // ... omitted ...

/* Handle DHT11 Humidity & Temperature
 * @author webofthink
*/
void handleDHT11(char *pin, char *val) {
  int p = getPin(pin);
  int chk = rht01.read(p - A0); 
  char m[12];
  sprintf(m, "%s::%03d,%03d", pin, rht01.temperature, rht01.humidity);
  Serial.println(m);
}


DHT11 센서에서 값을 가져오는 로직은 DHT11이라는 라이브러리로 제공되기 때문에 다운로드 받으신 후 헤더 파일을 추가(include)해서 사용하시면 쉽게 코딩할 수 있습니다.

여기에 주의 깊게 봐둘 사항은 Node.JS 측으로 전송할 메세지 형태입니다. handleDHT11() 함수의 sprintf() 문의 두 번째 인자를 참고하기 바랍니다.


2. duino library 추가하기


이번에는 lib 폴더로 이동합니다. 여기에 보면 duino 프로젝트가 기 지원하는 센서 및 엑추에이터들의 API 모듈이 존재합니다.

여기에 DHT11 센서를 지원하기 위한 API 모듈인 dht11.js을 추가합니다.


    var events = require('events'),
    util = require('util');
/*
 * The main DHT11 constructor
 * Connect to the serial port and bind
 */
var DHT11 = function (options) {
    if (!options || !options.board || !options.pin) throw new Error('Must supply required options to DHT11 Sensor');
    this.board = options.board;
    this.pin = options.pin;
    // Loop and trigger fire-read sequences
    setInterval(function () {
        var msg = '96' + this.pin;
        this.board.write(msg);
    }.bind(this), options.throttle ||  500);
    // When data is received, parse inbound message
    // match pin to instance pin value
    this.board.on('data', function (message) {
        
        var m = message.slice(0, -1).split('::'),
            err = null,
            pin, data;
        if (!m.length) {
            return;
        }
        pin = m[0]
        data = m.length === 2 ? m[1] : null;
        if (pin == this.pin) {
            // console.log("emitting data to read event: " + data);
            var values = data.slice(0).split(',');
            this.temperature = parseInt(values[0]);
            this.humidity = parseInt(values[1]);
            this.emit('read', err, this.temperature, this.humidity);
        }
    }.bind(this));
 };
/*
 * EventEmitter, I choose you!
 */
util.inherits(DHT11, events.EventEmitter);
module.exports = DHT11;


이 API 모듈 소스의 구성은 다음과 같습니다. 먼저 DHT11 클래스와 생성자를 선언합니다. 생성자에서는 options 라는 객체를 인수로 받아 필요한 초기 환경을 설정합니다. 아두이노 보드와 센서가 연결된 핀 정보가 설정되었다면, Arduino 측에 DHT11 정보를 요청하는 메세지를 setInterval() 함수를 통해 정기적으로 전달합니다.

이 명령이 Arduino 보드에 전송되면 첫번째 항목에서 구현한 이벤트 핸들러 handleDHT11()가 호출되어 요청받은 온습도 정보를 Node.JS에 회신합니다.

그러면 Node.JS에서는 this.board.on()의 'data' 이벤트가 발생(trigger)하게 되면서 해당 정보를 가져올 수 있게 됩니다.

마지막으로 이 온습도 정보를 EventEmitter의 'read' 이벤트로 재 전송하도록 구현합니다.


3. duino API에 DHT11 추가하기


다시 root 폴더로 돌아가서 index.js를 열어 DHT11를 다음과 같이 추가합니다.


    
module.exports = {
  Board:  require('./lib/board'),
  Led:    require('./lib/led'),
  Piezo:  require('./lib/piezo'),
  Button: require('./lib/button'),
  Servo:  require('./lib/servo'),
  Sensor: require('./lib/sensor'),
  Ping:   require('./lib/ping'),
  PIR:    require('./lib/pir'),
  LCD: require('./lib/lcd'),
    DHT11: require('./lib/dht11') /* add to support DHT11 sensor */
};


이로써, duino에서 DHT11 API를 사용할 수 있게 되었습니다.


4. DHT11 API 예제


examples 폴더로 이동하면 제공하는 API 모듈 별 예제 코드가 있습니다. 여기에 이번에 추가한 DHT11 API의 예제도 추가해 봅시다.


    
/* following is a test code for DHT11 sensor. */
var arduino = require('duino');
          
console.log("Initializing arduino board...");
var board = new arduino.Board({
    device: 'COM6',
    debug: false
});
var dht11 = new arduino.DHT11({
    board: board,
    pin : 'A2'
});
board.on('ready', function () {
    console.log("arduino board is ready to serve.");
});
dht11.on('read', function (err, temp, humidity) {
    console.log("temperature: " + temp + " degree of Celcius, " + "humidity: " + humidity + "%");
});


예제를 보면 알 수 있듯이 Arduino 보드 객체와 알맞은 핀 정보를 options 객체의 속성으로 전달 후 on 함수의 'read' 이벤트 핸들러로 온습도 정보를 가져올 수 있습니다.


5. DHT11 API


지금까지 추가했던 DHT11 API의 Web IDL은 다음과 같습니다.


    enum DHT11EmitterType { "read"};

    dictionary DHT11Option {
        Board board;
        DOMString pin;
        unsigned short throttle; // time interval per each measurements
    };
     
    callback DHT11OnCallback = void (Error err, short temperature, unsigned short humidity);
    
    [Constructor, Constructor (DHT11Option option)]
    interface DHT11 {
        void on(DHT11EmitterType type, DHT11OnCallback cb); 
    };


지금까지 Node.JS를 통해 arduino의 센서를 제어하기 위해 필요한 단계를 정리해 보았습니다. duino에는 상당한 센서와 엑추에이터를 지원하는 API가 존재하나 이처럼 지원하지 않는 센서가 있을 경우에도 추가 구현이 용이하도록 구조화 되어 있습니다.

서두에 링크한 문서를 살펴보면 arduino board 에 ethernet이나 wi-fi 모듈을 추가하여 이런 센서 정보를 곧바로 웹으로 노출시키는 방법도 있습니다만 이렇게 Node.JS를 gateway로 두고 서비스를 확장하는 것이 context data를 지식화 하는데 유용한 구현 방법이라고 생각합니다.

긴 글 읽어 주셔서 감사합니다. 늘 행복하시길!

안녕하세요,


이전에는 DOM 객체 제어 (manipulation) 및 server 부하(workload)를 줄이기 위한 간단한 스크립트 언어에 불과했던 JavaScript가 AJAX를 이용한 Rich Internet Application (RIA) 등장으로 활용도가 커지기 시작했습니다. 이와 함께 AJAX의 XML 대신 JSON (JavaScript Object Notation)을 이용해 데이터를 교환하는 방식이 개발자들에게 인기를 얻고, HTML5 표준화로 인해 다양한 기능을 JavaScript를 통해 사용할 수 있게 되었죠.

또한 구글이 만든 JavaScript 엔진인 V8에 기반한 서버 개발을 위한 플랫폼인 Node.JS 등장으로 인해 client 뿐 아니라 server 개발을 아우르는 언어로 각광받기 시작했습니다.


현재 JavaScript를 표준화를 담당하고 있는 단체인 ECMA에서는 몇가지 JavaScript 언어의 취약점을 보완해 ECMAScript 5까지 내놓았으나 여전히 기존 언어들에 비해 부족한 면이 많습니다.


아래 링크를 통해 소개할 RequireJS는 Node.JS에서 기본적으로 채택한 모듈화 방식이며 client 개발 시에도 사용이 가능한 library가 제공되어 대형 web app 개발에도 사용되고 있습니다.


http://helloworld.naver.com/helloworld/591319


JavaScript는 매우 자유롭고 유연한 언어이지만, 코드의 크기가 커지면 관리하기가 힘들어지는 단점이 있기 때문에 되도록 상기 링크의 RequireJS를 이용해서 개발하는 걸 권고합니다.


현재 ECMA에서도 이와 유사한 형태의 모듈화에 대한 표준화가 진행 중이니 머지 않은 미래에 JavaScript 기본 기능으로써 제공되리라 봅니다.


감사합니다. 

+ Recent posts