2019년 6월 5일 수요일

우분투 기반 벨로다인 라이다와 ROS 설치, 연결 및 설정

로버나 드론 기반에서 LiDAR를 연결해 스캔하고 데이터를 처리하고자 할 때, NVIDIA TK2, 라즈베리파이, 오드로이드(ODROID)와 같은 임베디드 컴퓨터를 사용해야 하는 경우가 있다.

이 글은 NVIDIA TK2, 오드로이드 등 임베디드 보드에 연결된 벨로다인 VLP16을 이용해 스캔 데이터를 ROS RVIZ로 처리하는 방법을 간략히 정리한다. 참고로, 지난번 글에서는 윈도우에서 VPL16 LiDAR 설정 방법을 다루었었다. 참고로, 오드로이드는 이 설치방법을 참고하길 바란다. NVIDIA TK2나 인텔 호환 PC도 설치 방법은 동일하다.
Velodyne VLP16 ROS RViz 실행 모습(오드로이드)

ROS 벨로다인 LiDAR VPL16 설정 및 패키지 설치
벨로다인 설정 및 설치 방법은 아래 글을 참고하였다.
LiDAR 이더넷 연결 단계는 다음과 같다. 
  1. LiDAR 파워 연결
  2. LiDAR와 오드로이드 간 이더넷 케이블 연결
  3. 오드로이드 WiFi 끄기
  4. Networks Connections 실행. edit를 선택해 IPV4 설정 탭 선택. "Method"를 "Manual"로 변경
  5. add를 선택해 IP 주소 필드에 192.168.1.70 설정
  6. Netmask는 255.255.255.0, Gateway는 0.0.0.0 설정
  7. Routes > Use this connection only for resources on its network 선택 (for WiFi connection)
  8. OK > save 선택
IPv4 setting

Routes setting

LiDAR 설정단계는 다음과 같다.
  1. 터미널에서 다음 명령 입력. sudo ifconfig eth0 192.168.3.100
  2. LiDAR IP 주소에 고정 라운트를 추가함. IP주소는 제품 CD 케이스에 포함되어 있음 (참고로 벨로다인 VLP16 라이다에 기본 설정된 IP는 192.168.1.201 임). sudo route add 192.168.XX.YY eth0 
  3. LiDAR 설정 체크를 위해, 인터넷 브라우저에 192.168.XX.YY 입력해 세부 설정 화인
혹시 eth0가 안보인다면, 다음 파일에 eth0를 설정, 맵핑한다.
sudo gedit /etc/udev/rules.d/70-persistent-net.rules

편집 내용은 다음과 같다. 여기서, ATTR{address}는 벨로다인을 연결했을 때 mac 주소이며, KERNELS는 이더넷 PCI 장치 번호이다. 이 장치 번호는  lspci -D 명령을 통해 확인할 수 있다.
SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ATTR{address}=="40:8D:5C:B1:D9:A0", ATTR{dev_id}=="0x0", ATTR{type}=="1", KERNELS=="0000:02:00.0", NAME="eth0"

이제, VLP16 ROS 패키지를 다음과 같이 설치한다. 참고로 VERSION은 melodic이다.
sudo apt-get install ros-VERSION-velodyne

만약 제대로 설치되지 않으면, 다음과 같이 소스코드를 다운로드받아 직접 빌드한다. 
  1. cd ~/catkin_ws/src/ && git clone https://github.com/ros-drivers/velodyne.git
  2. cd ..
  3. rosdep install --from-paths src --ignore-src --rosdistro YOURDISTRO -y
  4. cd ~/catkin_ws/ && catkin_make
혹시 rospkg 등이 제대로 설치되지 않아 빌등 에러가 발생하면 해당 패키지를 다음과 같이 설치하고 재시도한다.
pip install rospkg

제대로 설치가 되면, 다음과 같이 터미널에서 ROS를 실행한다.
source ~/.bashrc
roscore
ROS 실행된 모습

터미널에서 다음과 같이 velodyne VLP16 노드와 토픽을 실행한다.
roslaunch velodyne_pointcloud VLP16_points.launch
rosnode list
rostopic echo /velodyne_points

velodyne pointcloud node 실행 후 rosnode list 결과

rostopic echo 실행 결과

rviz 를 다음과 같이 실행한다.
rosrun rviz rviz -f velodyne

RViz에서 다음과 같이 Point Cloud 토픽 데이터를 볼 수 있도록 설정한다.
  1. RViz > display > Add 선택 후 Point Cloud2 선택 확인
  2. Topic 필드에 Point Cloud2 탭에서 "/velodyne_points" 입력 후 확인
설정 후 다음 같이 벨로다인에서 획득한 포인트 클라우드를 확인할 수 있다.

RViz 실행 모습

추신 - 요즘 오랜만에 시간이 나서 1년동안 쌓여 있던 장비와 컨텐츠들을 꺼내 쌓인 먼지를 털고 기억을 떠올려가며 정리한다. 회사가 정출연이라 직업은 연구하는 것인데, 여기 저기서 행정으로 인터럽트가 걸리고, 정신차려보면 몇년이 흘러 있는 경우가 많음. 브랜드 기술력 쌓으며 연구하기 정말 어려운 환경. - 넌센스

추신 - 시스템 안전성 테스트를 위해 2019년 6월 5일부터 현재(6월 17일)까지 LiDAR를 실행해 보았다. 아직 잘 동작하고 있다.

부록: VeloView 설치 및 실행
다음 링크를 참고해 VeloView를 설치하고 실행한다. 단, VeloView 설치 파일은 아직 64비트만 지원하고 있어, 직접 소스코드를 빌드해야 한다. 이 경우, QT4 패키지 업데이트 부분이 있는 데, 이는 오드로이드의 EGL과 Segmentation fault를 유발한다. 이 경우, 여기를 참고해 QT를 다시 설정해야 한다.
레퍼런스

Keras와 CoLab을 활용한 간단한 딥러닝 모델 훈련, 모델 저장 및 재사용

딥러닝 개발은 코딩보다는 딥러닝 환경 설과 데이터 구축에 많은 노력이 들어간다. 예를 들어, 딥러닝 환경설정은 무한 삽질로 빠지는 경우가 있다. 우분투, 텐서플로우(tensorflow), 케라스(keras) 등의 설정은 GPU와 궁합이 잘 맞지 않을 경우 우분투부터 여러번 설치해야 할 수 있다. 딥러닝 학습 모델에 필요한 데이터셋을 모으고 라벨링하는 작업은 정말 피곤한 일이다. 이후, 라벨링 된 데이터셋을 얻은 후에도 딥러닝 모델 학습에 맞게 구조화하는 작업도 귀찬은 것이다. Keras는 이런 일을 단순화시켜주는 기능을 제공한다.

이 글에서는 딥러닝 환경설정은 구글의 CoLab을 사용해 삽질을 하지 않는다. Keras의 ImageDataGenerator는 수집한 데이터 파일들을 폴더에 다음과 같이 넣어 두면, 폴더명을 라벨로 사용해 각 데이터 파일에 라벨링을 자동으로 처리한다.
trainset/cats
         /dogs
testset/cats
         /dogs
이 글은 CNN모델을 학습할 때 좀 더 손쉬운 학습 데이터를 준비, 훈련, 저장 및 재사용 방법을 간략히 정리한다. CNN모델 소스코드는 이 링크를 참고하였다. CNN모델 자체에 대한 설명은 여기를 참고한다.

머리말
이 글은 데이터 준비 및 훈련 방법에만 초점을 맞추기 위해, Cat, Dog 이미지 데이터를 사용한다. 딥러닝 도구는 keras의 CNN을 사용할 것이다.

데이터 준비
우선 다음 압축파일을 다운로드 받는다. 이 파일 안에는 구글에서 얻은 고양이와 개 이미지 파일들을 훈련용 8000개, 테스트용 2000개 가지고 있다.
CoLab 서버에 훈련 데이터셋을 업로드하기 위해, 압축파일을 풀고 dataset 폴더를 별도 zip 파일으로 압축한다. 참고로, 이 데이터셋 구조는 다음 그림과 같다.
딥러닝 데이터셋 구조

CoLab에 접속해 python3 파일을 하나 새로 만든다.
그리고, 다음과 같이 CoLab의 파일 업로드 함수를 이용해 CoLab 서버에 압축한 zip파일을 전송한다.

from google.colab import files
uploaded = files.upload()

참고로 CoLab은 우리가 구글에서 임시로 빌려쓰는 클라우드 서버기 때문에 사용하지 않고 일정 시간이 지나면 자동으로 끊어지고 업로드한 데이터도 사라질 수 있다. 그래서, 학습한 모델은 바로 로컬에 다운로드받거나 구글 드라이브에 옮겨 놓는 것이 좋다. 

다음 명령을 CoLab에서 실행해, 업로드한 압축파일을 푼다. 라벨링은 압축 해제된 폴더 이름에 따라 ImageDataGenerator의 flow_from_directory함수에서 자동으로 처리될 것이다.

!unzip dataset.zip

코딩 및 실행
CoLab 을 열고, 다음과 같이 코딩하고 실행한다. 참고로, 이 딥러닝 모델의 구조는 Data(64, 64, 3)-Conv(64, 64, 3)-MaxPooling(2, 2)-Conv-MaxPooling(2, 2)-Flatten-Dense(128)-Dense(1) 이다.

# Importing the Keras libraries and packages
from keras.models import Sequential
from keras.layers import Convolution2D
from keras.layers import MaxPooling2D
from keras.layers import Flatten
from keras.layers import Dense

# Initialising the CNN
classifier = Sequential()

# Step 1 - Convolution
classifier.add(Convolution2D(32, 3, 3, input_shape = (64, 64, 3), activation = 'relu'))  # Convolution2D(filter number, filter row, filter col, input_image(x, y, channel), activate function)

# Step 2 - Pooling
classifier.add(MaxPooling2D(pool_size = (2, 2)))  # max pooling row, col

# Adding a second convolutional layer
classifier.add(Convolution2D(32, 3, 3, activation = 'relu'))
classifier.add(MaxPooling2D(pool_size = (2, 2)))

# Step 3 - Flattening
classifier.add(Flatten())  # 2D -> 1D network

# Step 4 - Full connection
classifier.add(Dense(output_dim = 128, activation = 'relu'))   # Dense(output weight link number, activation function)
classifier.add(Dense(output_dim = 1, activation = 'sigmoid'))

# Compiling the CNN
classifier.compile(optimizer = 'adam', loss = 'binary_crossentropy', metrics = ['accuracy'])

# Part 2 - Fitting the CNN to the images

from keras.preprocessing.image import ImageDataGenerator

train_datagen = ImageDataGenerator(rescale = 1./255,
                                   shear_range = 0.2,
                                   zoom_range = 0.2,
                                   horizontal_flip = True)

test_datagen = ImageDataGenerator(rescale = 1./255)

training_set = train_datagen.flow_from_directory('training_set',
                                                 target_size = (64, 64),
                                                 batch_size = 32,
                                                 class_mode = 'binary')

test_set = test_datagen.flow_from_directory('test_set',
                                            target_size = (64, 64),
                                            batch_size = 32,
                                            class_mode = 'binary')

classifier.fit_generator(training_set,
                         samples_per_epoch = 8000,
                         nb_epoch = 25,
                         validation_data = test_set,
                         nb_val_samples = 2000)

그럼 다음과 같이 모델이 훈련되는 것을 확인할 수 있다.
학습된 결과

분류 예측
구글에서 얻은 고양이나 강아지 사진을 random.jpg 로 저장하고, 앞서 파일을 올린 방식과 같이 CoLab 서버에 파일 업로드한다. 그리고, 다음 코드를 CoLab에 입력해 실행해 본다.

import numpy as np
from keras.preprocessing import image
test_image = image.load_img('random.jpg', target_size = (64,64))
test_image = image.img_to_array(test_image)
test_image = np.expand_dims(test_image, axis = 0)
result = classifier.predict(test_image)
training_set.class_indices
if result[0][0] >= 0.5:
  prediction = 'dog'
else:
  prediction = 'cat'
print(prediction)

그럼, 다음과 같이 훈련된 딥러닝 모델이 적용 예측 결과가 표시될 것이다.
예측 결과

훈련된 딥러닝 모델 저장
이제 훈련된 딥러닝 모델을 다음 명령을 이용해 저장한다.
from keras.models import load_model

classifier.save('DL_CNN_cat_dog.h5')

from google.colab import files
files.download('DL_CNN_cat_dog.h5')  # from colab to browser download

훈련된 딥러닝 모델 불러와 사용하기
저장된 딥러닝 모델은 다음과 같이 간단히 로딩해 사용할 수 있다.
from keras.models import load_model
model = load_model('DL_CNN_cat_dog.h5')

똑같은 예측 소스코드로 실행해 보면 동일한 결과를 확인할 수 있다.
import numpy as np
import matplotlib.pyplot as plt

from keras.preprocessing import image
test_image1 = image.load_img('random4.jpg', target_size = (64,64))
test_image2 = image.img_to_array(test_image1)
test_image2 = np.expand_dims(test_image2, axis = 0)
result = model.predict(test_image2)
training_set.class_indices
if result[0][0] >= 0.5:
  prediction = 'dog'
else:
  prediction = 'cat'

origin_image = image.load_img('random4.jpg')
plt.imshow(origin_image)
print('  => predict = ', result[0][0], ', class = ', prediction)

로딩된 모델을 이용한 예측 결과

참고로 저장된 모델은 스마트폰, 웹 기반 앱에서 로딩해 사용할 수 있다. 관심이 있다면 다음 링크를 참고한다.


이 예제의 폴더 구조만 약간 변경하면 다양한 분야에서 CNN기반 딥러닝 모델 학습을 할 수 있다.

레퍼런스

오드로이드 XU4 우분투와 ROS, RVIZ 설치하기

오드로이드(ODROID)는 개발용으로 자주 사용되는 임베디드 컴퓨터로 라즈베리파이와 비슷한 크기를 가지나 성능은 훨씬 좋다.

이 글은 오드로이드에 로버 기반 LiDAR 스캔 장치를 개발하고 데이터를 획득해 볼 생각으로 진행한 우분투와 ROS(Robot Operating System)설치 방법을 간략히 정리한다.

참고로 NVIDIA, 라즈베리파이에 ROS 설치 후 비전을 개발하는 방법은 여기를 참고하길 바란다.

오드로이드 연결
다음 그림과 같이, 모니터 HDMI, 키보드, 마우스 및 WiFi 동글을 연결한다.

우분투 설치
다음 링크를 참고하여, 우분투 18.04를 설치한다.
설치 순서는 다음과 같다.
  1. ODROID 사이트에서 제공하는 우분투 이미지 다운로드
  2. 7-Zip 이용해 우분투 이미지 압축 해제
  3. Win32 Disk Imager와 이미지를 이용해 micro SD 메모리 부팅 드라이브 만들기
  4. micro SD 메모리를 오드로이드 슬롯에 삽입
  5. 오드로이드 파워 켜기
  6. 오드로이드 WiFi 동글 삽입 후 WiFi 설정
  7. 오드로이드 우분투 날짜 설정 
  8. 터미널창에서 다음 명령 이용해 우분투 업데이트 및 설치
$ sudo apt update
$ sudo apt upgrade
$ sudo apt dist-upgrade
$ sudo reboot
업데이트 중에 다음 같은 lock 파일 에러가 나올 수 있다.
E: Could not get lock /var/lib/apt/lists/lock - open (11: Resource temporarily unavailable)

이 경우, rm 명령을 실행해 lock 걸린 해당 파일을 삭제한다. 
sudo rm /var/lib/apt/lists/lock

재부팅하면 우분투가 설치된다. 이제 여러 패키지를 설치할 수 있다.
$ sudo apt install smplayer


3차원 그래픽 성능은 다음과 같이 확인한다. 
$ glmark2-es2
$ es2gears

webglsamples.org 사이트를 방문하면 webgl 예제로 성능을 테스트할 수 있다. 


ROS 설치
다음 링크를 참고해 ROS를 설치한다. ROS 버전은 melodic 이다.
오드로이드는 GPU가 없으므로 OpenGL 관련 패키지 실행시 에러가 있다. 이는 임베디드 보드에서 ROS의 RViz 사용 시 고질적인 GL 관련 에러를 발생한다. 이 문제를 해결하기 위해서 다음 명령을 우분투 터미널에서 입력한다(관련 링크). 

sudo -s
wget -O /usr/local/bin/odroid-utility.sh https://raw.githubusercontent.com/mdrjr/odroid-utility/master/odroid-utility.sh
chmod +x /usr/local/bin/odroid-utility.sh
odroid-utility.sh

오드로이드 유틸리티 쉘을 실행해 나오는 화면에서 6번 화면을 선택해 업데이트한다. 

내장된 Embedded GL인 EG를 사용해야 하므로, 이에 맞게 GUI로 사용되는 QT를 다시 업데이트해야 한다. 다음 명령을 터미널에 입력해 실행한다(관련 링크).

sudo apt update && sudo apt upgrade
sudo apt install libfbclient2 libodbc1 libpq5 libsybdb5 libgles2-mesa-dev libxext-dev libinput-dev libxkbcommon-dev
wget https://dn.odroid.com/5422/ODROID-XU3/Ubuntu/qt5.9.5_armhf_bionic.tar.gz
tar xvfz qt5.9.5_armhf_bionic.tar.gz
cd qt5.9.5_armhf_bionic/
sudo dpkg -i *.deb

제대로 설치되었다면, 다음 명령을 실행했을 때 QT 3D 그래픽 창을 볼 수 있다. 
DISPLAY=:0.0 /usr/lib/arm-linux-gnueabihf/qt5/examples/opengl/hellogles3/hellogles3


참고로, 디스크 용량을 절약하려면, 다운로드한 파일들을 다음처럼 삭제다.
cd ..
rm -rf qt5.9.5_armhf_bionic*

ROS와 RVIZ 실행
다음 명령을 실행한다.
source ~/.bashrc
roscore

이제 터미널 창을 하나 더 띄우고, 다음 명령을 실행한다. 혹시 GL관련 에러가 있다면, ROS RVIZ Troubleshooting 페이지를 참고해 "export LIBGL_ALWAYS_SOFTWARE=1" 명령어를 이용하여 하드웨어 가속기를 OFF한다. 
rosrun rviz rviz

그럼 다음과 같이 오드로이드 우분투 환경에서 RVIZ 화면을 확인할 수 있다.


ROS 상세한 사용법은 다음 튜토리얼 및 레퍼런스를 참고한다.
기타, ROS 관련 3D 이미지 비전은 이 링크를 참고한다.

2019년 5월 26일 일요일

Google Cloud IoT 사용하기

이 글은 Google Cloud IoT 사용하는 방법에 대한 레퍼런스를 요약한 것이다.


레퍼런스
IoT - Using Cloud IoT Core to connect a microcontroller (ESP32) to the Google Cloud Platform
Build an IoT application with Google Cloud 

NodeMCU와 구글 스프레드시트 연결

이 글은 NodeMCU ESP8266과 구글 스프레드시트 간의 연결 방법에 대한 글을 소개한다.
튜토리얼 영상

NodeMCU 회로 구성
NodeMCU를 이용해 다음과 같이 회로를 구성한다. 센서 역할을 하는 가변저항을 연결한다. 

스프레드시트 연결
구글 스프레드시트를 만들고, 도구메뉴 스크립트 편집기에서 다음과 같이 스크립트를 추가한다.
function doGet(e) 
{
  var mo = e.parameter.func;
  if(mo == "addData")
  {
    var stat = add_data(e);
    if(stat == 1)
    {
      var result = 
      {
        status : true
      };
      return ContentService.createTextOutput(JSON.stringify(result)).setMimeType(ContentService.MimeType.JSON);
    }
  }
}
function add_data(e)
{
  var sheet = SpreadsheetApp.openByUrl('https://docs.google.com/spreadsheets/d/182EnOR4vF5eVs4wGD-zOn7pKy_6BMpED8ApjIGh0C9Q/edit#gid=0');
  var lastVal = sheet.getRange("A1:A").getValues();
  var id = lastVal.filter(String).lenght;

  var CurrentDate = new Date();
  var Date_ = Utilities.formatDate(CurrentDate, "IST", "dd/MM/YYYY");
  var Time_ = Utilities.formatDate(CurrentDate, "IST", "HH:mm:ss");

  sheet.appendRow([id, Date_, Time_, e.parameter.val]);
  return 1;
}
게시>웹 앱으로 배포 메뉴를 실행하고, 모두 접근 가능한 권한으로 배포한다.

NodeMCU 코딩
NodeMCU에 다음과 같이 코딩한다. 단, 코드에서 url 은 앞서 배포된 웹 앱 링크 주소로 변경해야 한다.  
#include <ESP8266WiFi.h>
#include <WiFiClientSecure.h>

const char *ssid = "SERVER NAME"; 
const char *password = "SERVER PASSWORD";  
const char* host = "script.google.com"; 
const char* fingerprint = "89 ff f4 0f 4d 98 09 ed e3 ef 95 f2 8a af af 22 89 08 ac 03";
String url;

void setup() 
{
  Serial.begin(115200);
  delay(100);
  Serial.println();
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(ssid);
  
  WiFi.begin(ssid, password); 
  while (WiFi.status() != WL_CONNECTED) 
  {
    delay(500);
    Serial.print(".");
  }
 
  Serial.println("");
  Serial.println("WiFi connected");  
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
  Serial.print("Netmask: ");
  Serial.println(WiFi.subnetMask());
  Serial.print("Gateway: ");
  Serial.println(WiFi.gatewayIP());
}

void loop() 
{
  Serial.print("connecting to ");
  Serial.println(host);
 
  WiFiClientSecure client;

  const int httpPort = 443;
  if (!client.connect(host, httpPort)) 
  {
    Serial.println("connection failed");
    return;
  }

  float t = analogRead(A0);
  
  url = "/macros/s/AKfycbyvGjcryd7c5uNeX6dkleZhmjDKyTrs1L9Lf3kWlPhTZDn9JPrH/exec?func=addData&val="+ String(t);
  Serial.print("Requesting URL: ");
  Serial.println(url);
  
  client.print(String("GET ") + url + " HTTP/1.1\r\n" +
               "Host: " + host + "\r\n" + 
               "Connection: close\r\n\r\n");
  delay(500);
  String section="header";
  while(client.available())
  {
    String line = client.readStringUntil('\r');
    Serial.print(line);
  }
  Serial.println();
  Serial.println("closing connection");
  delay(6000);
}
코드를 업로드하여 실행하면, 약 6초마다 NodeMCU에서 읽은 데이터가 스프레드시트에 업데이트되는 것을 확인할 수 있다.

레퍼런스

2019년 5월 23일 목요일

IoT 커넥션 오픈소스 NODE-RED 사용법 - node-red + NodeMCU + MQTT

이 글은 IoT 장치들간 연결에 사용되는 오픈소스인 노드레드(NODE-RED)를 사용하는 방법을 간단히 나눔한다. 이 글은 IoT 구현을 위해 노드레드, NodeMCU를 사용한다.

노드레드는 IBM에서 IoT 시스템 개발 지원하는 오픈소스 플랫폼이다. 노드레드를 이용하면, 코딩할 필요없이 다양한 IoT 프로토콜, 입출력 연결을 손쉽게 처리할 수 있다. 노드레드는 아파치 라이센스 2.0을 지원하기 때문에 라이센스 규약만 지키면 상용 패키지를 개발하는 데 큰 문제가 없다.

이 글은 노드레드를 이용해 다음과 같이 IoT 장치와 MQTT방식으로 데이터 교환을 하는 간단한 IoT 시작품 만드는 방법을 보여준다.
노드레드로 만든 간단한 IoT 데쉬보드 테스트
nodered.org 소개

노드레드 설치
설치는 간단하다.

1. Node.js 설치
노드 레드는 노드JS를 사용한다. 우선 아래 링크에서 Node.js LTS 버전을 다운로드 받아 설치한다.

2. Node.js 명령 콘솔 실행 및 버전 확인
다음과 같이 설치된 메뉴 중에 command prompt 메뉴를 실행한다.
실행 후, 다음 명령어로 노드JS 설치 버전을 확인한다.
node --version; npm --version

3. node-red 및 데쉬보드 설치
다음 명령어로 node-red와 데쉬보드를 설치한다. 참고로, node-red는 데쉬보드와 같이 다양한 추가 플러그인을 지원한다.
npm install -g node-red
npm i node-red-dashboard

4. node-red 실행
다음 명령으로 node-red를 실행한다. 만약, network security error (PPSecurityException)발생하면 Set-ExecutionPolicy -ExecutionPolicy RemoteSigned 명령을 터미널에서 실행한다.
node-red

5. node-red 웹브라우저 실행
크롬을 실행한 후 http://127.0.0.1:1880/ 에 접속하면, node-red GUI를 확인할 수 있다. 

노드레드 사용
IoT 시스템 디자인
우리는 MQTT란 IoT 표준 프로토콜을 이용해, NodeMCU 에서 얻은 센서값을 노드레드에 전달해 줄 것이다. 이를 위해, MQTT 데이터를 저장하고 있어야 할 MQTT 브로커가 있어야 한다. 브로커는 http://test.mosquitto.org 란 오픈소스 서버를 사용하기로 한다. 이 서버는 공개이며 무료로 사용할 수 있다.

아울러, 노드레드에서 NodeMCU로 MQTT 데이터를 전달하는 예제도 구현할 것이다. NodeMCU에 전달할 데이터는 단순히 NodeMCU에 연결된 LED를 켜고 끄는 데 사용한다. 

참고로, 센서값과 LED ON/OFF는 노드레드에서 제공하는 데쉬보드를 이용해 GUI를 쉽게 구현할 수 있다. 

NodeMCU 회로 구성 및 코딩
1. 회로구성
다음과 같이 NodeMCU 회로를 구성한다.


   D4 PIN = LED +
   GND PIN = LED -
   GND PIN = 10K 저항 끝단
   A0 PIN = 10K 저항 반대 끝단
   A0 PIN = 조도센서 끝단
   3.3V PIN = 조도센서 반대 끝단

참고로, NodeMCU 핀번호는 다음과 같다. 

2. 코딩
컴퓨터와 NodeMCU를 USB케이블로 연결한다. 그리고, 아두이노 IDE를 실행하고, 툴>보드 메뉴에서 NodeMCU 1.0을 선택하고, 툴>포트 메뉴에서 NodeMCU와 연결된 포트를 선택한다.

다음 코드를 붙여넣고, 코드에서 WiFi ssid와 pwd를 수정한 후, NodeMCU 보드에 업로드한다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
#include <ESP8266WiFi.h>
#include <PubSubClient.h>

const char* mqtt_server="test.mosquitto.org";
WiFiClient espclient;

void callback(char* topic,byte* payload,unsigned int length1){    
  Serial.print("message arrived[");
  Serial.print(topic);
  Serial.println("]");
  
  String data = "";
  for(int i=0;i<length1;i++)
    data = data + payload[i];     
  Serial.println(data);
  Serial.println(payload[0]);

  if(payload[0] == 48)      // if(data == "48")
  {
    digitalWrite(D4, LOW);  //ASCII VALUE OF '0' IS 48
    Serial.println("off");
  }
  else if(payload[0] == 49) // else if(data == "49")
  {
    digitalWrite(D4, HIGH);   //ASCII VALUE OF '1' IS 49
    Serial.println("on");
  }
}

PubSubClient client(mqtt_server,1883,callback,espclient);

void setup() {
  pinMode(D2,OUTPUT);
  Serial.begin(115200);
  Serial.print("connecting");
  WiFi.begin("ssid","pwd");         //SSID,PASSWORD 
  while(WiFi.status()!=WL_CONNECTED){
    delay(500);
    Serial.print(".");
  }
  Serial.println();
  digitalWrite(LED_BUILTIN, HIGH); 
  
  reconnect();
}

void reconnect(){
  while(WiFi.status()!=WL_CONNECTED){
    delay(500);
    Serial.print(".");
  }
  while(!client.connected()){
    if(client.connect("ESP8266Client123456789")){
      Serial.println("connected");
      client.subscribe("ledcontrol");
    }
    else{
      Serial.print("failed,rc=");
      Serial.println(client.state());
      delay(500);
    }
  } 
}

int now = 0;
int lastMeasure = 0;

void loop() {
  if(!client.connected()){
    reconnect();
  }
  
  client.loop();

  // Timer variable with current time
  now = millis();
  if (now - lastMeasure > 3000) {
    lastMeasure = now;
    int value = analogRead(A0);
    String data = String(value);
    client.publish("sensor/light", data.c_str());  
  }
}

노드레드 디자인
1. MQTT 노드 설정과 LED ON/OFF 버튼 생성
우선, 노드레드의 입력>mqtt 를 Drag&drop으로 생성한다. 생성된 mqtt노드를 더블클릭해, 다음과 같은 속성창에서 http://test.mosquitto.org 서버(포트 = 1883) 추가하고, 토픽명은 ledcontrol, QoS=0 으로 설정한다.

버튼을 2개 추가하고, 속성을 다음과 같이 각각 설정한다. 참고로, Group에 설정된 값은 데쉬보드에 함께 표시되는 그룹을 결정한다.
LED ON 버튼 설정

LED OFF 버튼 설정

방금 만든 MQTT와 버튼 2개를 다음과 같이 연결한다.

2. 조도센서 MQTT 노드 생성과 데이터 확인용 debug 생성
MQTT노드를 하나 더 추가하고, 아래와 같이 토픽=sensor/light 로 설정한다. 

데쉬보드에서 debug를 만들고, mqtt와 서로 연결한다.

debug의 속성은 다음과 같이 설정한다.

3. 조도센서 MQTT 노드 생성과 데이터 확인용 guage 생성
방금 만든 mqtt (sensor/light)를 복사해 붙여넣어 하나 더 만든다. 
그리고, 데쉬보드에서 다음과 같이 gauge를 만든 후 서로 연결한다.

gauge의 속성은 다음과 같이 설정한다.

이렇게까지 하면, 다음과 같이 노드레드가 구성되었을 것이다. 

데쉬보드 Layout 탭을 선택해 다음과 같이 탭, 그룹을 디자인한다. 다음과 같이 on, off, gauge를 정의한다.

4. 노드레드 설정 배포 및 데쉬보드 확인
이제 배포하기를 클릭한다. 그리고 디버그 탭을 선택한다. 다음과 같이 NodeMCU에서 MQTT로 전달받은 데이터가 로그로 출력되고 있을 것이다. 

Layout 탭의 화살표 아이콘을 클릭하면 데쉬보드가 다음과 같이 실행된다. 

ON/OFF 버튼을 클릭해 보자. 몇초후에 NodeMCU 에 연결된 LED가 동작할 것이다.

차트도 다음과 같이 추가해 mqtt와 연결해 보자. 

다시 배포한 후 데쉬보드를 확인하면, 다음과 같이 차트가 추가된 것을 확인할 수 있다.
노드레드 데쉬보드 결과

만약 좀 더 좋은 사용자 인터페이스가 필요하다면 그라파나(grafana)를 사용할 수도 있다. 참고로, 그라파나는 코딩하지 않고 데이터 소스를 연결해 시각화하는 모니터링 도구로 아파치 라이센스 2.0을 준수하고 있다.

마무리
앞에서 IoT 데이터를 NodeMCU로 부터 MQTT 프로토콜로 받아 데쉬보드로 확인하는 것까지 해 보았다. 노드레드로 하지 않았다면, MQTT 코딩부터 데쉬보드 구현까지 많은 시간이 걸렸을 것이다. 이 과정은 코딩이 불필요하며, 익숙해 지면 몇십분만에 끝낼 수 있다.

노드레드는 오픈소스로 자기 컴퓨터를 서버로 운영하여 데이터를 저장할 수 있으고, 메일, 트위터, MQTT 등 다양한 입출력 유형을 제공하여 가성비가 매우 좋다. 데쉬보드는 N-screen을 지원하여, 크롬, 스마트폰 등을 모두 지원한다.

레퍼런스