2019년 6월 8일 토요일

인공지능과 미디어아트 플랫폼

이 글은 인공지능과 미디어아트의 관계를 다룬 내용을 간략히 소개한다.

인공지능, 특히 딥러닝 기술이 무르익어가는 단계에서 여러 분야에 대한 응용 노력이 많아지고 있다. 물론 예술 분야도 예외가 아니어서, 인공지능 기술이 예술 플랫폼에서 여러가지 사례로 활용되고 있다.

예술 분야에서도 인공지능을 이용한 플랫폼이 개발되었는 데, RUNWAY는 그 중 하나이다. 이 플랫폼을 이용하면, 머신러닝 환경을 설치하고, 딥러닝 모델 패키지를 설치하는 노력이 훨씬 줄어든다.

이 플랫폼을 이용하면, CNN, OBJECT DETECTION, CAPTION, GAN 등 다양한 모델을 클릭 몇번으로 다운로드하여, JAVASCRIPT 등 다양한 방식으로 본인의 어플리케이션과 연결해 미디어아트를 연출할 수 있다. 
기본 제공 딥러닝 모델

객체 인식 

얼굴 특징 인식

실시간 인식된 얼굴 특징 벡터

각 분야의 특성이 접목된 이런 머신러닝 플랫폼은 앞으로 점차 많아질 것이다.

2019년 6월 5일 수요일

벨로다인 LiDAR 기반 SLAM 하기

이 글은 LiDAR 기반으로 SLAM 하는 방법을 간략히 설명한다. LiDAR는 저가의 VLP16 벨로다인 센서를 사용한다. 참고로, 이 방법은 우분투 ROS 환경에서 실행된다. 여기서 사용하는 환경은 ROS melodic 버전을 사용하였으며, 오드로이드 우분투 18.04 기반이다(이전 설치 글 참고).

이 글에서는 SLAM의 원리를 다루지는 않는다. SLAM의 이론적 설명은 다음 링크를 참고한다.
SLAM은 대부분 ROS 패키지를 사용하므로, 이전 글 참고해 미리 설치해 놓어야 한다.

LOAM SLAM
우선 SLAM 에서 잘 알려진 LOAM(Laser Odometry and Mapping) 방식 SLAM을 만들어보자. 이 링크를 방문해 github에서 소스코드를 받고, 순서대로 빌드해 본다. 참고로, 오드로이드, 라즈베리파이 같이 램 용량이 적은 컴퓨터에서는 컴파일 에러가 발생한다. 이 경우, 부록에 설명된 방법대로 스왑 용량을 설정해 컴파일 빌드하면 된다(컴파일이 몇시간 걸릴 수 있다).

$ cd ~/catkin_ws/src/
$ git clone https://github.com/laboshinl/loam_velodyne.git
$ cd ~/catkin_ws
$ catkin_make -DCMAKE_BUILD_TYPE=Release
$ source ~/catkin_ws/devel/setup.bash

이제 roscore를 실행하고, 다음 명령을 실행한다.
roslaunch loam_velodyne loam_velodyne.launch

다음 명령을 이용해 생성된 데이터를 플레이하거나 읽을 수 있다.
rosbag play ~/Downloads/velodyne.bag 
roslaunch velodyne_pointcloud VLP16_points.launch pcap:="$HOME/Downloads/velodyne.pcap"

실행 결과는 다음과 같다.

LOAM SLAM

LeGO LOAM
LeGO LOAM 링크를 방문해, 다음과 같이 소스를 다운로드한다.
wget -O ~/Downloads/gtsam.zip https://github.com/borglab/gtsam/archive/4.0.0-alpha2.zip
cd ~/Downloads/ && unzip gtsam.zip -d ~/Downloads/
cd ~/Downloads/gtsam-4.0.0-alpha2/
mkdir build && cd build
cmake ..
sudo make install

다음과 같이 컴파일한다.
cd ~/catkin_ws/src
git clone https://github.com/RobustFieldAutonomyLab/LeGO-LOAM.git
cd ..
catkin_make -j1

다음과 같이 ROS 패키지 실행한다.
roslaunch lego_loam run.launch
rosbag play *.bag --clock --topic /velodyne_points /imu/data


LeGO LOAM

HDL graph SLAM

부록: 오드로이드 cc1plus internal compile 에러 솔류션
오드로이드는 내부 램 용량이 작은 임베디드 컴퓨터이기 때문에 컴파일 시 cc1plus internal compile error 가 발생될 수 있다. 이 경우, 터미널에서 다음과 같이 설정한다.

dd if=/dev/zero of=/swapfile bs=1024 count=4194304
chown root.root /swapfile
chmod 0600 /swapfile
mkswap /swapfile
swapon /swapfile

설정된 스왑 디스크 크기를 확인해 본다.
free -m

이렇게 해도 안된다면, 컴파일 시 make -j 옵션 대신 make 를 사용한다(관련 링크).

참고로, 임베디드 장치는 메모리 크기가 2~4G이며 스왑은 구성되어 있지 않다. 이 경우, 컴파일 프로세스에서 g++: internal compiler error: Killed (program cc1plus) 오류가 발생한다. 이 오류는 메모리가 부족하여 발생하므로, 스왑을 추가하여 문제를 해결할 수 있다(링크 참고).

레퍼런스


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

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

이 글은 오드로이드에 연결된 벨로다인 VLP16을 이용해 스캔 데이터를 ROS RVIZ로 처리하는 방법을 간략히 정리한다. 참고로, 지난번 글에서는 윈도우에서 VPL16 LiDAR 설정 방법을 다루었었다. 이 내용을 진행하기 전에 우분투는 오드로이드에 설치되어 있었다(설치방법 참고 링크).
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.100 설정
  6. Netmask는 255.255.255.0, Gateway는 0.0.0.0 설정 후 save 선택
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 입력해 세부 설정 화인

VLP16 ROS 패키지를 다음과 같이 설치한다. 참고로 VERSION은 melodic이다.
  1. sudo apt-get install ros-VERSION-velodyne
  2. cd ~/catkin_ws/src/ && git clone https://github.com/ros-drivers/velodyne.git
  3. rosdep install --from-paths src --ignore-src --rosdistro YOURDISTRO -y
  4. cd ~/catkin_ws/ && catkin_make
제대로 설치가 되면, 다음과 같이 터미널에서 ROS를 실행한다.
source ~/.bashrc
ros
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년동안 쌓여 있던 장비와 컨텐츠들을 꺼내 쌓인 먼지를 털고 기억을 떠올려가며 정리한다. 회사가 정출연이라 직업은 연구하는 것인데, 여기 저기서 행정으로 인터럽트가 걸리고, 정신차려보면 몇년이 흘러 있는 경우가 많음. 브랜드 기술력 쌓으며 연구하기 정말 어려운 환경. - 넌센스

부록: 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에서 읽은 데이터가 스프레드시트에 업데이트되는 것을 확인할 수 있다.

레퍼런스