NodeJS 로 IOT 프로그래밍
BLE 를 IOT 장치로 사용하기
BLE 관련 프로그래밍을 조사해면, 대부분의 Central 장치에 대한 소스는 Android 와 iOS 중심으로 되어있다. 최근 Linux, MacOS, Windows 는 BLE 를 지원한다. 그래서 PC 를 BLE central 장치로 사용하려고 한다. 집안에서 서비스를 한다고 가정했을 때, PC 에서 BLE periperal 을 직접 제어할 수 있으면 좋겠다.
프로그래밍 도구로는 NodeJS 를 쓸 것이다. 파이선도 가능하지만 웹을 생각한다면 Node 가 좀 더 낫다.
Bluno 나 BLE Nano 를 peripheral 장치로 사용할 것이다. 이 것들은 Characteristic 이 고정되어 있지만, 데이터 통신을 가능하다.
아두이노 코드 - peripheral
우선 Bluno 로 시작하는데, BLE Nano 도 동일하다.
아래 코드를 보면 전혀 BLE 냄새가 안난다. Scan, response 의 일은 내장된 BLE chip 이 알아서 해주기 때문에 그냥 데이터 관련 코딩만 하면 된다. Serial 을 여는데 이 Serial 은 아두이노 UART 와 내장 BLE chip 사이의 Serial 이다. 기본 baud rate 은 AT 명령어로 바꿀 수 있다.
참고 : https://wiki.dfrobot.com/Bluno_SKU_DFR0267
loop 안을 보면 받은 데이터에 단순히 16 을 더해서 다시 보내는데, Central 장치가 [1,2,3,…,9,a] 10 byte 를 보내면 [11,12,13,…19,1a] 를 보낸다.
void setup() {
Serial.begin(115200); // bluno default baud rate
}
void loop() {
if (Serial.available()) {
byte data = Serial.read();
data += 16;
Serial.write(data);
}
}
NodeJS 코드 - central
1 필요한 Node package 설치
# Requirement : Node & noble package
$ npm install noble
# Noble support node v8. Use @abandonware/noble with latest node ver
$ npm install @abandonware/noble
2 Periperal 찾기
Periperal BLE Service 와 Characteristics 를 알아야 하는데, 이를 찾는 예제 코드가 있다.
$ node node_modules/@abandonware/noble/examples/advertisement-discovery.js
# or copy the js file to current dir and modify the first line
# FROM var noble = require('../index');
# TO var noble = require('@abandonware/noble');
$ node advertisement-discovery.js
BLE 장치의 name, id, address 를 찾을 수 있다. Service 와 Characteristic 은 UUID 형태로 제공되는데 아직은 알 수 없다.
Service UUID 는 https://www.bluetooth.com/specifications/gatt/services/ 를 참고하자.
peripheral discovered (874b3456cc63486aa6d178ebfc20a859 with address <20-cd-39-xx-xx-xx, public>, connectable true, RSSI -76:
hello my local name is:
Bluno
can I interest you in any of the following advertised services:
undefined
3 Service/Characteristic 얻어오기
장치의 이름, 주소를 알았으니 이제 어떤 Service 가 있는지 알아봐야 한다. peripheral_explorer.js 를 가져와서 실행하기 전에 async 패키지를 설치한다.
$ npm install async
async 버전 차이 때문에, 코드 수정이 좀 필요하다.
# Find async.whilst and add “async” to the next function. (two times)
async.whilst(
async function () { // <- function () {
# Second, show descriptors
async.series([
function(callback) {
characteristic.discoverDescriptors(function(error, descriptors){
if (descriptors.length > 0)
characteristicInfo += '\n descriptor ' + descriptors;
callback();
});
},
아래와 같이 실행한다.
$ node peripheral-explorer.js 874b3456cc63486aa6d178ebfc20a859
or
$ node peripheral-explorer.js 20-cd-39-xx-xx-xx
결과는 다음과 같다.
peripheral with ID 874b3456cc63486aa6d178ebfc20a859 found
Local Name = Bluno
Manufacturer Data = 4c000215e2c56db5dffb48xxx...
services and characteristics:
180a (Device Information)
2a23 (System ID)
properties read
value babc93000039cd20 | ':<9M '
2a24 (Model Number String)
properties read
value 444620426c756e6f | 'DF Bluno'
2a25 (Serial Number String)
properties read
value 30313233343536373839 | '0123456789'
2a26 (Firmware Revision String)
properties read
value 46572056312e3937 | 'FW V1.97'
2a27 (Hardware Revision String)
properties read
value 48572056312e37 | 'HW V1.7'
2a28 (Software Revision String)
properties read
value 53572056312e3937 | 'SW V1.97'
2a29 (Manufacturer Name String)
properties read
value 4446526f626f74 | 'DFRobot'
2a2a (IEEE 11073-20601 Regulatory Certification Data List)
properties read
value fe006578706572696d656e74616c | '~experimental'
2a50 (PnP ID)
properties read
' value 010d0000001001 | '
dfb0
dfb1
descriptor {"uuid":"2901","name":"Characteristic User Description","type":"org.bluetooth.descriptor.gatt.characteristic_user_description"}
properties read, writeWithoutResponse, write, notify
value 01 | ''
dfb2
descriptor {"uuid":"2901","name":"Characteristic User Description","type":"org.bluetooth.descriptor.gatt.characteristic_user_description"}
properties read, writeWithoutResponse, write, notify
value 02 | ''
Bluno 는 UUID “180a” and “dfb0” 두개의 서비스를 갖고 있다.
UUID “180a” 는 Device 정보를 나타내는 서비스인데, 대부분의 BLE 장치에는 이 서비스를 가지고 있어 자신의 정보를 알려준다.
UUID “dbf0” 는 Bluno 가 제공하는 서비스이며, 두개의 Characteristic 을 가지고 있다. (“dbf1”, “dbf2”) “dbf1” 은 Serial, “dbf2” 는 Command 역할을 한다. (시리얼 통신 채널과 AT Command 채널)
Characteristic 를 읽고 쓰려면 get/put 을 사용한다. Notify 는 Periperal 에서 Central 로 push 를 가능하게 하는데 이는 “2902” descriptor 를 이용한다. (CCCD : Client Characteristic Configuration Descriptor)
참고 : https://www.bluetooth.com/specifications/gatt/descriptors/
Bluno 는 이 CCCD 를 제공하지 않는다. 그래서 MacOS 에서는 Subscription (Notification request) 기능을 사용할 수 없다. Linux 나 Window 는 CCCD 없이도 잘 된다. Bluno 가 스펙에 안맞긴 하지만, 다소 의아하다. 이 문제에 관심이 있으면 아래 링크를 참고한다.
참고 : https://www.dfrobot.com/forum/viewtopic.php?f=18&t=2035&start=20
Keywish BLE Nano 는 Serial 통신을 위한 하나의 characteristic 만을 제공한다(“ffe0”), CCCD 도 있고 MacOS 에서 잘 작동한다.
최종 코드
최종 Javascript 코드 : https://gist.github.com/doojinkang/f825bf4f8f1883802d15bbfec2bf4173
1~10 을 buffer 에 넣어서 periperal 로 보낸다. Periperal (아두이노) 쪽에서는 16을 더해서 0x11, 0x12, … 0x1a 를 PC 로 보낸다.
# For Bluno (on Linux)
$ node ble_arduino.js dfb0 dfb1
# For BLE Nano (on Both Linux or MacOS)
$ node ble_arduino.js ffe0 ffe1
# Discover(Scan) and get ID/Address/ Name --->
# Connect and get Services/Characteristics --->
# Subscribe / Write Characteristics (Serial)
기본적인 통신이 되었으니, 이제 IOT 환경을 꾸밀 수 있을 것이다.