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


이번에는 이전에 소개드린 바 있는 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를 지식화 하는데 유용한 구현 방법이라고 생각합니다.

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

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


이번 포스팅부터는 제목과 같이 Web IDL로 JavaScript API를 설계하는 방법을 여러분들께 공유해 보고자 합니다. 


먼저, 여러분에게 JavaScript API를 설계해야 하는 당위성과 그리고 Web IDL이 무엇인지에 대해서 설명드려야 하겠죠.


우선 첫번째 JavaScript API를 설계해야 하는 이유부터 말씀드리겠습니다.

  • JavaScript는 스크립트 언어라는 특성 상 type 선언 없이 변수를 생성하고 유연한 형 변환(type casting)을 통해 컴퓨터 언어의 지식이 부족한 초급 개발자들도 쉽게 사용할 수 있을 뿐만 아니라 함수형 언어의 특성을 갖는 객체 지향 언어로서 확장성과 유연성이 좋아 고급 개발자에게도 유용한 언어입니다.

  • 웹을 지원하는 모든 형태의 기기에서 동작하는 클라이언트 앱 외에도 Google이 만든 V8 엔진을 이용해 만든 Node.JS라는 서버 프레임워크에서도 동작하는 서버 앱 그리고 Mongo DB라는 NoSQL DB에서도 사용 가능한 매우 범용적인 개발 언어입니다.

  • 웹에서 동작하는 앱은 대부분 브라우저의 개발자 모드 (F12 키)를 통해 JavaScript code를 살펴 볼 수 있고 이런 수고가 아니라도 구글링을 통해 다양한 라이브러리 혹은 코드 조각 (code snippet)를 검색할 수 있습니다. 원하는 기능의 대부분을 단순히 복사 & 붙여 넣기로도 구현할 수 있습니다. 

  • 이렇게 코드를 훔쳐 사용하기 쉬운 구조 때문에 어차피 훔쳐 사용할 거라면 이걸 하기 위해 분석하는 수고를 덜어주는 마음씨 착한 개발자의 도움으로 인해 재사용하기 쉬운 형태로 만든 라이브러리가 매우 많이 있습니다. 물론 대부분 공짜구요. 이때 좋은 라이브러리는 API만 알아도 기능을 사용할 수 있도록 사용자에게 제공합니다. 코드를 모두 읽어 봐야 한다면 '복사 & 붙여넣기'보다 나은게 없으니까요.

  • API는 Application Programming Interface의 약자로 말 그대로 앱을 프로그래밍하는 인터페이스, 즉 앱에서 수행될 기능을 짜는 방법이라고 볼 수 있습니다. JavaScript 앱을 작성할 때 API의 도움을 받으면 적은 노력으로 원하는 기능을 구현할 수 있습니다.

  • API에 이용해 앱을 짜는데 자신감이 붙었다면 (게다가 혹시 개발 비 명목으로 돈도 벌었다면) 라이브러리가 제공하지는 않지만 앱을 짜는데 필요했던 기능을 API로 설계해서 추가해보면 어떨까요? 이런 너그러운 마음씨가 당신이 했던 수고를 반복하지 않고 당신이 알지 못하던 새로운 기능을 개발할 수 있도록 도울 겁니다. 당신이 사용했던 라이브러리를 개발한 원 저자가 그랬듯이 말입니다.

  • 좋은 앱은 많은 사람들에게 유용한 기능을 제공해 주지만 모든 사람들을 만족시켜 줄 수 없습니다. 사람들의 취향이란 사람들의 숫자 만큼이나 다양하니까요. 그들에게 DIY (Do It Yourself) 할 수 있도록 해 준다면 메일 함에 쌓이는 다양한 수정 요구사항에서 조금은 해방될 수도 있습니다. (그렇다고 버그에서 해방되는 걸 의미하지는 않습니다.)

  •  미래는 API의 시대가 될 것이라고 예견하는 사람들이 있습니다. (당연히 저도 그런 사람 중 하나입니다.) 소프트웨어 교육이 보편화 되고 있기에 대부분의 사람들이 프로그래밍을 할 수 있게 되리라고 보기 때문이죠. 따라서, 머지않아 단순히 API를 활용해 앱을 짜는 것은 더 이상 신기한 일이 되지 않겠지만, 새로운 기능을 제공하는 API를 만들어서 제공하는 역할은 여전히 전문가의 영역에 남아 있을 것입니다.

쓰다 보니 길어져서 한줄 요약하면 다음과 같습니다.

JavaScript는 시장이 크고 유용성이 높은 개발 언어로 API를 설계할 줄 알면 전문가로 대접 받을 수 있다. - 웹 오브 띵크

 

동기 부여가 되는 이야기를 한건지 모르겠지만 혹여 여기까지 읽으셨다면 필자는 여러분들이 API를 설계하기로 충분히 세뇌(?) 되었다고 생각하고 두 번째인 Web IDL에 대해 설명드리겠습니다.


일단 Web IDL 에 관련된 문서가 있는 아래 링크를 한번 방문해 보시죠. 


http://www.w3.org/TR/WebIDL/


영문으로 된 문서는 무조건 번역기를 돌리다 보니 무슨 소린 줄 모르시겠다는 분들과 영어에 자신있으나 배경 지식이 없어 이해가 안가시는 분들을 위해 요약 (abstract)을 한글로 번역해 보았습니다.


이 문서는 Web IDL 이라는 인터페이스 정의 언어를 정의하는데 웹 브라우저에서 구현될 것을 의도한 인터페이스들을 설명하는데 사용될 수 있다. Web IDL은 웹 플랫폼에서 사용되는 공용 스크립트 오브젝트들의 동작을 보다 읽기 쉽도록 명세화하기 위한 수많은 기능들을 가진 IDL의 변종이다. Web IDL로 명시된 인터페이스들이 어떻게 ECMAScript 실행 환경 내에서 구성되는 지에 대해서도 상세화 되어 있다. 이 문서는 이미 배포된 스펙 문서를 어떻게 구현할지에 대한 가이드을 개발자에게 제공하고 새로 배포하는 스펙 문서의 인터페이스의 구현체가 서로 호환되도록 보장하기 위해 참조하기 위함이다. - W3C Web IDL Abstract


등장한 용어들에 대해 간단히 설명 드리면 다음과 같습니다. 또 의아한 용어가 있다면 댓글로 남겨주시기 바랍니다. :-)

  • "정의" - Justice가 아닌 Definition 입니다. 전 공대 출신이니까요. ;-)
  • "웹 브라우저", "웹 플랫폼" - 모두 여러분이 웹 세계를 탐험하는 도구(앱)를 의미하나 브라우저는 사용자 관점에서 플랫폼은 개발자 관점에서 바라보는 겁니다.
  • "오브젝트" - 한글로는 객체라고 번역되나 프로그램에서 기능을 수행하는 단위라고 이해하면 됩니다.
  • "ECMAScript" - 여기서는 JavaScript와 동의어라고 이해하면 됩니다.
  • "스펙" - 대학이나 취업 시 적어내는 이력서가 아니라 정의한 내용을 적은 규격서입니다. 
  • "구현체" - 코딩을 완료해서 동작하는 프로그램을 의미합니다. 


발췌한 요약문에서 빨간 색 밑줄친 부분은 주의 깊게 봐주셨으면 합니다. 제가 Web IDL로 JavaScript API를 설계하는 데 사용하는 이유를 말해주는 대목이니까요. 이 내용에 대해 이후 포스팅에서 좀 더 자세하게 설명드리겠지만 먼저 간략히 말씀드리면 JavaScript 언어의 유연성에 대한 부작용으로 인해 API를 구체적으로 명세하기 어려운 점이 발생합니다. 이렇게 API의 동작을 명확하게 알려주지 못한다면 사용하는 사람 뿐 만 아니라 API를 구현하는 사람에게도 혼란을 초래하기 때문에 구체적인 명세화 방식이 꼭 필요합니다.


게다가 이외에도 Web IDL를 사용하면 추가 이득을 얻을 수 있는데 그것이 바로 API 설계 검증 및 문서화에 대한 자동화입니다. 아래 링크의 widlproc이라는 도구를 통해 설계자는 설계의 유효성을 검증하고 손쉽게 문서를 생성할 수 있습니다. 이 방법 역시 추후 포스팅으로 설명드리겠습니다.


widlproc: https://github.com/dontcallmedom/widlproc  

 


이번 포스팅은 여기까지 입니다.


감사합니다.


+ Recent posts