안녕하세요,

"생각의 웹"입니다.


혹시 지난 "AppIN 세미나 - Do IoT Yourself" 강연에 참석하시고 꾸준히 이 블로그에 방문해 주시는 분들이 계신가요?

글을 시작하기 앞서 그 분들께 먼저 감사 말씀드립니다.


이번 포스팅에는 D.IoT.Y 두 번째 강연 시 대디스랩 송영광 대표님과 함께 만들었던 온도센서를 JavaScript로 구현해보고자 합니다.

이 강연은 다음 포스팅을 참조하시기 바랍니다.


Intuitive Understanding Arduino for IoT: Do IoT Yourself 2nd



여기서 실습한 온도 센서 만들기는  Sparkfun Starter Kit for RedBoard에 내장된 센서/엑추에이터로 한정해야 했기에 가격이 저렴한 서미스터 센서(Thermistor)로 기능을 구현하기 위해 arduino IDE에서 꽤 복잡한 코딩을 해야 했습니다. 


다음 코드는 실습 당시 복사/붙여넣기 했던 코드입니다.


#include <avr/pgmspace.h>

int led_state;
unsigned long time;

#define THERM_PIN   0  // 10k thermo & 10k resistor as divider.

/*
 *  Big lookup Table (approx 750 entries), 
 *  subtract 238 from ADC reading to start at 0*C. 
 *  Entries in 10ths of degree i.e. 242 = 24.2*C Covers 0*C to 150*C 
 *  For 10k resistor/10k thermistor voltage divider w/ therm on the + side.
 */
const int temps[] PROGMEM = { 0, 1, 2, 3, 4, 6, 7, 8, 9, 10, 11, 12, 13, 14, 
    15, 16, 17, 18, 19, 20, 21, 22, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33,
	34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 
	52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 68, 
	69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 
	87, 88, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 
	102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 114, 
	115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 124, 125, 126, 127, 
	128, 129, 130, 131, 132, 133, 134, 134, 135, 136, 137, 138, 139, 140, 
	141, 142, 143, 143, 144, 145, 146, 147, 148, 149, 150, 151, 151, 152, 
	153, 154, 155, 156, 157, 158, 159, 159, 160, 161, 162, 163, 164, 165, 
	166, 167, 167, 168, 169, 170, 171, 172, 173, 174, 175, 175, 176, 177, 
	178, 179, 180, 181, 182, 182, 183, 184, 185, 186, 187, 188, 189, 190, 
	190, 191, 192, 193, 194, 195, 196, 197, 197, 198, 199, 200, 201, 202, 
	203, 204, 205, 205, 206, 207, 208, 209, 210, 211, 212, 212, 213, 214, 
	215, 216, 217, 218, 219, 220, 220, 221, 222, 223, 224, 225, 226, 227, 
	228, 228, 229, 230, 231, 232, 233, 234, 235, 235, 236, 237, 238, 239, 
	240, 241, 242, 243, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 
	252, 253, 254, 255, 256, 257, 258, 259, 260, 260, 261, 262, 263, 264, 
	265, 266, 267, 268, 269, 269, 270, 271, 272, 273, 274, 275, 276, 277, 
	278, 279, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, 289, 
	290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 301, 302, 
	303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 315, 
	316, 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, 327, 328, 329, 
	330, 331, 332, 333, 334, 335, 335, 336, 337, 338, 339, 340, 341, 342, 
	343, 344, 345, 346, 347, 348, 349, 350, 351, 352, 353, 354, 355, 356, 
	357, 358, 359, 360, 361, 362, 363, 364, 365, 366, 367, 368, 369, 370, 
	371, 372, 373, 374, 375, 376, 377, 378, 379, 380, 381, 382, 383, 384, 
	385, 386, 387, 388, 389, 390, 392, 393, 394, 395, 396, 397, 398, 399, 
	400, 401, 402, 403, 404, 405, 406, 407, 408, 410, 411, 412, 413, 414, 
	415, 416, 417, 418, 419, 420, 422, 423, 424, 425, 426, 427, 428, 429, 
	430, 432, 433, 434, 435, 436, 437, 438, 439, 441, 442, 443, 444, 445, 
	446, 448, 449, 450, 451, 452, 453, 455, 456, 457, 458, 459, 460, 462, 
	463, 464, 465, 466, 468, 469, 470, 471, 472, 474, 475, 476, 477, 479, 
	480, 481, 482, 484, 485, 486, 487, 489, 490, 491, 492, 494, 495, 496, 
	498, 499, 500, 501, 503, 504, 505, 507, 508, 509, 511, 512, 513, 515, 
	516, 517, 519, 520, 521, 523, 524, 525, 527, 528, 530, 531, 532, 534, 
	535, 537, 538, 539, 541, 542, 544, 545, 547, 548, 550, 551, 552, 554, 
	555, 557, 558, 560, 561, 563, 564, 566, 567, 569, 570, 572, 574, 575, 
	577, 578, 580, 581, 583, 585, 586, 588, 589, 591, 593, 594, 596, 598, 
	599, 601, 603, 604, 606, 608, 609, 611, 613, 614, 616, 618, 620, 621, 
	623, 625, 627, 628, 630, 632, 634, 636, 638, 639, 641, 643, 645, 647, 
	649, 651, 653, 654, 656, 658, 660, 662, 664, 666, 668, 670, 672, 674, 
	676, 678, 680, 683, 685, 687, 689, 691, 693, 695, 697, 700, 702, 704, 
	706, 708, 711, 713, 715, 718, 720, 722, 725, 727, 729, 732, 734, 737, 
	739, 741, 744, 746, 749, 752, 754, 757, 759, 762, 764, 767, 770, 773, 
	775, 778, 781, 784, 786, 789, 792, 795, 798, 801, 804, 807, 810, 813, 
	816, 819, 822, 825, 829, 832, 835, 838, 842, 845, 848, 852, 855, 859, 
	862, 866, 869, 873, 877, 881, 884, 888, 892, 896, 900, 904, 908, 912, 
	916, 920, 925, 929, 933, 938, 942, 947, 952, 956, 961, 966, 971, 976, 
	981, 986, 991, 997, 1002, 1007, 1013, 1019, 1024, 1030, 1036, 1042, 
	1049, 1055, 1061, 1068, 1075, 1082, 1088, 1096, 1103, 1110, 1118, 1126, 
	1134, 1142, 1150, 1159, 1168, 1177, 1186, 1196, 1206, 1216, 1226, 1237, 
	1248, 1260, 1272, 1284, 1297, 1310, 1324, 1338, 1353, 1369, 1385, 1402, 
	1420, 1439, 1459, 1480, 1502 
};


void setup() {
  pinMode(13, OUTPUT);
  Serial.begin(19200);
  time=millis();
  //Serial.println("Ready");
}

void loop() { 

  int therm;   
  therm = analogRead(THERM_PIN) - 238;
  therm = pgm_read_word(&temps[therm]);
  
  //Serial.println(therm, DEC);
  float temp = (float)therm / 10.0;
  Serial.println(temp, 1);
  
  if (millis()-time > 1000) {   
    time = millis();
    digitalWrite(13,led_state);
    led_state =! led_state;
  }
  delay(2000);
}

약간 코딩 지식이 필요하기에 실습 시에는 많은 설명이 되지는 않았지만 여기서 조금 설명하자면
 look up table를 만들어 A0에서 읽어들인 값을 table에 있는 값에 대응해서 serial로 출력하는 logic이 되겠습니다.


보시듯, 여기서 이 프로그램의 logic의 핵심은 look up table을 어떻게 만들었는가이고 

이에 대한 답변은 table를 만들기 위해서는 서미스터 혹은 하드웨어 부품들에 대한 이해가 필요하다는 것입니다. 

즉 아래의 그림처럼 서미스터의 데이터 쉬트에서 table이 도출되었습니다. (송영광 대표님이 엑셀 신공을 사용하셨다고 하더군요. ^^a) 


서미스터는 회로 구성을 어떻게 했는가에 따라 (즉, 어떤 저항을 달았느냐)에 따라 다른 값을 내어 놓습니다. 

다시 말해 이 값은 위의 그래프에서 온도 X일때의 Y값과 같은 형태로 나옵니다.

하드웨어의 경우 물리의 세계에 닿아 있기에 재료의 특성과 환경에 따라 영향을 받기에 로직 만으로 이해하고자 하는 SW 쟁이는 이해하기 쉽지 않죠.  


따라서, 사실 원하는 동작을 얻기 위해서는 arduino 회로를 적절히 구성할 뿐 아니라
arduino IDE를 이용해 관련 로직을 구현한 후 해당 프로그램(속칭 펌웨어)을  

arduino 에서 전송해 테스트해 보는 일을 반복해야 합니다.


여기서 문제는 arduino에 이 동작 외에 추가로 기능을 구현해야 할 경우, 

반드시 arduino IDE로 펌웨어를 업드레이드해 주어야 합니다. 

이는 꽤 불편한 일이 아닐 수 없습니다. 


쓰다 보니 문제 제기로만으로도 꽤 길어졌는데 이에 대해 제가 제시하는 해결책은 다음과 같습니다.


1. arduino의 펌웨어에 호스트(host)의 명령을 처리해 동작을 수행하는 인터프리터(interpreter) 를 전송합니다.


2. 호스트는 arduino에 명령을 전송합니다. (e.g. A0 포트에서 아날로그 값을 읽어 오기)


3. 인터프리터는 명령을 받아 동작을 수행 후 결과를 호스트에 전송합니다.



혹시, 이 방식을 어디선가 본 듯한 느낌을 받은 분이 있나요? ;-)

앞서 소개한 바와 같이 이 방식으로 구현된 node.js 모듈이 바로 duino 입니다.   


관련 포스팅 : http://webofthink.tistory.com/13



이번에는 duino 모듈로 이 똑같은 동작을 수행해 보도록 하겠습니다. 


먼저, 아래 파일을 다운로드 받기 바랍니다.


duino.zip

(이 모듈은 현재 버전인 v0.0.9를 조금 수정해서 DHT11 센서 지원 및 윈도우즈에서 동작하도록 수정 확장한 버전입니다. 리눅스나 OS X는 기존 버전을 써도 무방합니다.)


이 파일의 압축을 풀어 src/du/du.ino 파일을 실행합니다. 

(먼저 Arduino IDE가 설치되어 있어야 합니다. 참고바랍니다.)


해당 arduino 펌웨어를 arduino 보드에 다운로드합니다.


정상적으로 전송되면 이제 node.js로 동일한 역할을 하는 코드를 구현해 봅니다.

코드를 다음과 같이 작성해 temp.js로 저장하고 콘솔에서 node temp 로 실행합니다.


var arduinoPort = 'COM7'; // only works in windows with patched duino module.
var pinName = '00'; // it means A0 pin

// look up table for resolving data to the appropriates temperature
var temps = [0, 1, 2, 3, 4, 6, 7, 8, 9, 10, 11, 12, 13, 14, 
    15, 16, 17, 18, 19, 20, 21, 22, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33,
	34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 
	52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 68, 
	69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 
	87, 88, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 
	102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 114, 
	115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 124, 125, 126, 127, 
	128, 129, 130, 131, 132, 133, 134, 134, 135, 136, 137, 138, 139, 140, 
	141, 142, 143, 143, 144, 145, 146, 147, 148, 149, 150, 151, 151, 152, 
	153, 154, 155, 156, 157, 158, 159, 159, 160, 161, 162, 163, 164, 165, 
	166, 167, 167, 168, 169, 170, 171, 172, 173, 174, 175, 175, 176, 177, 
	178, 179, 180, 181, 182, 182, 183, 184, 185, 186, 187, 188, 189, 190, 
	190, 191, 192, 193, 194, 195, 196, 197, 197, 198, 199, 200, 201, 202, 
	203, 204, 205, 205, 206, 207, 208, 209, 210, 211, 212, 212, 213, 214, 
	215, 216, 217, 218, 219, 220, 220, 221, 222, 223, 224, 225, 226, 227, 
	228, 228, 229, 230, 231, 232, 233, 234, 235, 235, 236, 237, 238, 239, 
	240, 241, 242, 243, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 
	252, 253, 254, 255, 256, 257, 258, 259, 260, 260, 261, 262, 263, 264, 
	265, 266, 267, 268, 269, 269, 270, 271, 272, 273, 274, 275, 276, 277, 
	278, 279, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, 289, 
	290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 301, 302, 
	303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 315, 
	316, 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, 327, 328, 329, 
	330, 331, 332, 333, 334, 335, 335, 336, 337, 338, 339, 340, 341, 342, 
	343, 344, 345, 346, 347, 348, 349, 350, 351, 352, 353, 354, 355, 356, 
	357, 358, 359, 360, 361, 362, 363, 364, 365, 366, 367, 368, 369, 370, 
	371, 372, 373, 374, 375, 376, 377, 378, 379, 380, 381, 382, 383, 384, 
	385, 386, 387, 388, 389, 390, 392, 393, 394, 395, 396, 397, 398, 399, 
	400, 401, 402, 403, 404, 405, 406, 407, 408, 410, 411, 412, 413, 414, 
	415, 416, 417, 418, 419, 420, 422, 423, 424, 425, 426, 427, 428, 429, 
	430, 432, 433, 434, 435, 436, 437, 438, 439, 441, 442, 443, 444, 445, 
	446, 448, 449, 450, 451, 452, 453, 455, 456, 457, 458, 459, 460, 462, 
	463, 464, 465, 466, 468, 469, 470, 471, 472, 474, 475, 476, 477, 479, 
	480, 481, 482, 484, 485, 486, 487, 489, 490, 491, 492, 494, 495, 496, 
	498, 499, 500, 501, 503, 504, 505, 507, 508, 509, 511, 512, 513, 515, 
	516, 517, 519, 520, 521, 523, 524, 525, 527, 528, 530, 531, 532, 534, 
	535, 537, 538, 539, 541, 542, 544, 545, 547, 548, 550, 551, 552, 554, 
	555, 557, 558, 560, 561, 563, 564, 566, 567, 569, 570, 572, 574, 575, 
	577, 578, 580, 581, 583, 585, 586, 588, 589, 591, 593, 594, 596, 598, 
	599, 601, 603, 604, 606, 608, 609, 611, 613, 614, 616, 618, 620, 621, 
	623, 625, 627, 628, 630, 632, 634, 636, 638, 639, 641, 643, 645, 647, 
	649, 651, 653, 654, 656, 658, 660, 662, 664, 666, 668, 670, 672, 674, 
	676, 678, 680, 683, 685, 687, 689, 691, 693, 695, 697, 700, 702, 704, 
	706, 708, 711, 713, 715, 718, 720, 722, 725, 727, 729, 732, 734, 737, 
	739, 741, 744, 746, 749, 752, 754, 757, 759, 762, 764, 767, 770, 773, 
	775, 778, 781, 784, 786, 789, 792, 795, 798, 801, 804, 807, 810, 813, 
	816, 819, 822, 825, 829, 832, 835, 838, 842, 845, 848, 852, 855, 859, 
	862, 866, 869, 873, 877, 881, 884, 888, 892, 896, 900, 904, 908, 912, 
	916, 920, 925, 929, 933, 938, 942, 947, 952, 956, 961, 966, 971, 976, 
	981, 986, 991, 997, 1002, 1007, 1013, 1019, 1024, 1030, 1036, 1042, 
	1049, 1055, 1061, 1068, 1075, 1082, 1088, 1096, 1103, 1110, 1118, 1126, 
	1134, 1142, 1150, 1159, 1168, 1177, 1186, 1196, 1206, 1216, 1226, 1237, 
	1248, 1260, 1272, 1284, 1297, 1310, 1324, 1338, 1353, 1369, 1385, 1402, 
	1420, 1439, 1459, 1480, 1502
];


var arduino = require('duino');
var board = new arduino.Board({
    device: 'COM7',
    debug: false //true
});



board.on('ready', function () {
    console.log("arduino board is ready to serve.");
    board.pinMode(pinName, 'in');
    
    setInterval(function () {
        console.log('request to get an analog value from ' + pinName);
        board.analogRead(pinName);
    }, 1000);
});

board.on('data', function (message) {
    // message looks like {pin}::{data}. so it will be disassembled properly.
    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;

    console.log('reading value from pin' + pin + ': ' + data );
    if (pin === pinName) {        
        var converted = temps[parseInt(data) - 238] / 10.0;
        console.log('current temperature is ' + converted + ' degree of celsius');
    }
});


C:\tests>node temp.js
arduino board is ready to serve.
request to get an analog value from 00
reading value from pin00: 489
current temperature is 23.5 degree of celsius
request to get an analog value from 00
reading value from pin00: 489
current temperature is 23.5 degree of celsius
request to get an analog value from 00
reading value from pin00: 489
current temperature is 23.5 degree of celsius
^C
C:\tests>


단순히 보면 위의 코드가 앞서 arduino 펌웨어의 코드보다 더 간략해 보이지 않지만, 

이 코드는 세번 째 강연으로 진행했던 사물 간의 연결을 위한 Open API에서
다루었던 serialport 모듈의 기능을 포함하고 있습니다.

(즉 비슷한 코딩 량으로 두 가지 일을 한번에 한 것입니다.)


이외에도 앞서 문제로 제기했던 것들의 해결책으로써 

호스트에서 arduino 원격 제어와 펌웨어 업데이트 없이 추가 기능 구현이 가능해 졌습니다. 


더 나아가 이를 RESTful Web API와 JavaScript API 형태인 Open API로 재 설계한다면
웹을 통해서 아두이노를 원격 제어하는 게 가능해 집니다.


마지막으로 사족을 붙여 보겠습니다.


아래 포스팅을 통해 사물과 사물 간의 대화를 위해서는 수 많은 기술적 제약들이 현재 존재함을 말씀드린 바 있습니다.


- 사물 인터넷을 지탱하는 것 

- 사물 인터넷은 어떻게 생각하게 될까?


저는 이 장벽을 푸는 첫 번째 기술적 열쇠로 자바스크립트 언어를 바라보고 있습니다. 

이 언어를 사용하는 많은 개발자들이 존재하면서도 지적 재산권(IP) 문제에 자유롭고 계속적으로 기능이 향상되고 있기 때문입니다.


최근 뉴스에 등장한 오라클과 구글의 안드로이드에서의 자바 언어 사용 관련 특허 분쟁은 사물인터넷 시대를 맞이 할 우리에게 시사하는 점이 많습니다.

처음 기술을 개발한 세력이 개방과 공유를 빙자해 전파하더라도 나중에 가서는 그것을 사유화하려고 할 것이기 때문입니다. 


그러나 우리는 지난 사반세기 동안 이런 사유화를 막고 지구적 공동체를 만들어낸 훌륭한 사례를 본 적이 있습니다. 

따라서 웹과 arduino가 그러했듯이 오픈 소스 소프트웨어오픈소스 하드웨어가 초연결 사회로 나아가는 축으로 자리 잡길 기대합니다.


감사합니다.  

+ Recent posts