자바스크립트(JavaScript)로 배우는 피지컬 컴퓨팅(Physical Computing) — (8/8) 인터넷에 연결하기

Minkyu Lee
12 min readMay 15, 2021

--

이번 강좌에서는 UART(Universal Asynchronous Receiver/Transmitter)를 통해 와이파이(Wi-Fi) 모듈을 제어하여 인터넷에 연결해보자.

강좌 전체 목차

준비물

  • 라즈베리파이 피코 — 1개
  • 브레드보드(Breadboard) — 1개
  • 디지털 온습도 센서(DHT11) — 1개(모듈 형태도 무방)
  • Wi-Fi 네트워크 모듈(ESP8266) — 1개(ESP-01형태의 모듈)
  • 점퍼 와이어(M-M, M-F)— 여러 개

ESP8266은 Wi-Fi 네트워크 기능을 갖춘 마이크로컨트롤러이다. 가격이 저렴해서 매우 대중적으로 많이 사용된다. 사용하기 편리하도록 다양한 모듈 형태로 판매되고 있는데, 여기서는 크기도 작고 저렴한 ESP-01 모듈을 사용한다. 모듈에서는 보통 AT 명령어(command)를 사용할 수 있는 펌웨어(firmware)가 업로드 되어 있다.

ESP-01 모듈 핀아웃(pinout)

회로 구성

ESP-01을 UART0번 포트에 연결하고 CH_PD 핀도 3V3으로 연결한다. 그리고 DHT11 센서의 OUT 핀도 GPIO15에 연결한다. ESP-01 모듈은 안타깝게도 브레드보트에 꽂을 수 없는 형태로 되어 있다. 그래서 M-F(Male-to-Female)형태의 점퍼와이어를 통해서 연결해야 한다.

UART(Universal Asynchronous Receiver/Transmitter)

UART(Universal Asynchronous Receiver/Transmitter)는 지난 강좌에서 설명한 I2C와 SPI와는 달리 클럭(clock)을 위한 핀이 사용되지 않는다. 따라서 장치들간에 서로 통신 속도를 맞추어야 한다. 이것을 보드레이트(baudrate)라고 하고 주로 9600bps, 115200bps 정도가 가장 많이 사용된다. Pico에서는 UART0, UART1의 2개의 포트(port)가 제공된다. 그리고 UART는 I2C, SPI와 달리 버스(bus)가 아니다. 따라서 각 포트에 1개의 장치만 연결할 수 있다.

Kaluma에서는 아래와 같이 uart 모듈을 통해 사용할 수 있다. 데이터의 송신은 write() 함수를 통해 가능하다. 하지만 데이터의 수신을 위한 함수가 없다. 외부 UART 장치가 언제 데이터를 전송할지 알 수 없기 때문에 data 이벤트를 통해서 수신 데이터를 전달한다. 수신된 데이터는 Uint8Array 타입이기 때문에 화면에 출력하고 싶다면 문자열(string) 형태로 변환해야 한다.

const {UART} = require('uart');
let uart0 = new UART(0);
uart0.write('Hello\n');
// ...
uart0.on('data', (data) => {
var s = String.fromCharCode.apply(null, data);
print(s);
});
// ...
uart0.close();

AT 명령어(Command)

AT 명령어(Command) (a.k.a. Hayes command set)는 초창기 모뎀을 위해 개발된 명령어의 집합이다. 명령어 들이 보통 “AT”라는 텍스트로 시작해서 AT 명령어로 불리며 이는 “attention”에 대한 약어이다. 개발 된지 매우 오래되었지만 최근까지도 다양한 장치 들을 제어하기 위한 명령어로 널리 사용된다. ESP8266도 무선 네트워크를 제어하기 위해 바로 이 AT 명령어를 지원한다.

일단 위 회로도와 같이 UART0에 연결된 ESP8266을 AT 명령어로 제어해보자. 터미널에서 직접 AT 명령어를 UART로 전송하고 수신된 데이터를 터미널에 표시하기 위해서 아래 코드를 한 줄 씩 터미널에 입력(복사 붙여넣기)해보자. 아래 코드는 단순하게 UART 0번 포트를 위한 객체를 생성하고, 데이터가 수신되면 터미널에 출력하는 것이다. 수신된 데이터는 Uint8Array 타입이기 때문에 읽기 쉽게 문자열로 변환해서 출력한다.

> const {UART} = require('uart');
> let uart0 = new UART(0, {baudrate: 115200});
> uart0.on('data', d=>{print(String.fromCharCode.apply(null, d))});

먼저 ESP8266이 제대로 연결되었고 AT 명령어에 정상적으로 반응하는지 살펴보자. 아래와 같이 AT\r\n 문자열을 UART 0을 통해 송신해보자. 그러면 전송된 명령어의 에코(echo)가 수신된 이후 바로 그 결과로써 OK 가 수신될 것이다. AT 명령어의 제일 마지막에는 항상 \r\n 이 붙어있어야 한다.

> uart0.write('AT\r\n');
undefined
> AT
OK

다음으로 ESP8266을 초기화(reset)하는 명령을 보내보자. 뭔가 알 수 없는 문자열이 수두룩하게 나타난다. 초반에 OK 와 마지막 즈음에 ready 가 보이면 초기화가 잘 된 것이다.

> uart0.write('AT+RST\r\n');
undefined
> AT+RST
OK
...
load 0x40100000, len 1856, room 16
tail 0
chksum 0x63
load 0x3ffe8000, len 776, room 8
...
Ai-Thinker Technology Co. Ltd.
ready

그리고 우리는 주로 Station 모드로 사용할 예정이기 때문에 ESP8266 모듈을 Station 모드로 전환해주어야 한다.

> uart0.write('AT+CWMODE=1\r\n');
undefined
> AT+CWMODE=1
OK

마지막으로 Wi-Fi 기능이 잘 동작하는지를 보기 위해서 주변 AP 들을 검색(scan) 하는 명령을 보내보자. 내 주변의 무선 네트워크가 잘 검색되는지도 한번 확인해보자.

> uart0.write('AT+CWLAP\r\n');
undefined
> AT+CWLAP
+CWLAP:(3,"xxxxxxxxxxx",-78,"xx:xx:xx:xx:xx:xx",1,10,0)
+CWLAP:(3,"xxxxxx",-53,"xx:xx:xx:xx:xx:xx",11,-2,0)
...
OK

내 주변의 네트워크가 잘 검색이 되는가(보안상 SSID, BSSID를 x로 처리함)? 이 정도면 ESP8266의 AT 명령어가 어떤 방식으로 실행되는지 이해했을 것으로 생각된다. 더 자세한 AT 명령어들은 관련 문서를 참고하기 바란다. 참고로 아래 링크는 ESP8266의 AT 명령어를 간결하게 정리해 둔 문서이다.

외부 웹서버 구동하기

Pico가 측정한 온습도 데이터를 인터넷으로 전송하려면, 그것을 받아줄 수 있는 서버가 필요하다. 이제 데이터를 수신할 간단한 웹서버를 Node.js에 express를 사용해서 하나 만들어보자. 아래와 같이 폴더를 하나 만들고 Node.js 프로젝트를 초기화 한 다음 expressip 모듈을 추가한다.

$ mkdir thermo-server
$ cd thermo-server
$ npm init -y
$ npm install express, ip --save

아래와 같이 server.js 프로그램을 작성한다. 본 서버에 브라우저로 접속하면 현재의 온도와 습도를 나타낸다. 그리고 Pico에서 DHT11 센서로 측정한 온도와 습도 정보를 /update?t=27.5&h=45와 같이 서버에 전송할 수 있도록 HTTP GET 방식의 REST API도 하나 제공한다.

온습도 웹서버 코드 (server.js)

코드를 다 작성하였다면 웹서버를 구동시켜보자. 아래와 같이 웹서버의 IP 주소와 포트 번호가 표시되면서 실행된다.

$ node server
App listening at http://xxx.xxx.x.xx:3000

웹서버로 데이터 전송하기

이제 Pico에서 온도와 습도 데이터를 웹서버로 전달하면 하면 된다. 그런데 Wi-Fi의 비밀번호나 서버의 주소와 같은 정보는 외부에 공개되면 좀 곤란하다. Kaluma에서 생성된 프로젝트는 현재 모두 공개(public) 모드 이기 때문에 이런 정보들이 외부로 노출될 염려가 있다. 그래서 Pico의 플래쉬 메모리에 해당 정보를 저장하고, 코드에서는 노출되지 않도록 할 수 있는 Storage API 를 사용해보자. 일단 우리는 Wi-Fi SSID, Password, Address, Port 이 4가지를 모두 Pico의 플래쉬 메모리 상에 저장할 것이다. 아래와 같이 터미널에 입력한다.

> storage.setItem('WIFI_SSID', 'xxxxxxx');
> storage.setItem('WIFI_PASSWORD', 'xxxxxxxx');
> storage.setItem('ADDRESS', 'xxx.xxx.x.xx');
> storage.setItem('PORT', '3000');

이렇게 저장된 항목들을 아래와 같이 다시 읽어올 수 있다. 코드에서는 이렇게 저장된 항목들을 읽어오도록 하면 해당 정보가 노출되지 않게 할 수 있다.

> storage.getItem('WIFI_SSID');
> storage.getItem('WIFI_PASSWORD');
> storage.getItem('ADDRESS');
> storage.getItem('PORT');

자 이제 그러면 아래 코드를 작성하고 업로드 해보자. 우리는 AT 명령어를 좀 더 편리하게 다루기 위해서 Kaluma에서 제공하는 at 모듈을 사용한다. 이것을 사용하면 매우 편리하게 AT 명령어를 전송하고 또한 전송 결과를 쉽게 콜백 함수에 전달 할 수 있다. ATCommand 클래스를 생성할 때 ESP8266이 연결된 UART 객체를 전달하고, 여기서 오고 가는 데이터를 보고 싶다면 debug 옵션을 true로 설정하면 된다. 개발 단계에서는 디버그 정보를 보는 것이 매우 편리할 것이다. at 모듈에 대한 레퍼런스를 참고하면 전체 기능을 살펴볼 수 있다.

코드가 실행되면 먼저 ESP8266을 AT+RST 명령으로 초기화 한 다음, AT+CWJAP 명령어로 Wi-Fi에 연결을 시도한다. 연결에 성공하게 되면 start() 함수를 실행하여 10초마다 DHT11 센서에서 온도와 습도를 측정하고, HTTP GET 요청을 생성하여 서버로 전송한다.

이렇게 Pico에서 온도와 습도 데이터를 서버로 전송하게 되면 이제 스마트폰이나 PC의 브라우저로 서버에 접속해서 온습도를 확인해보자. 만약 서버가 외부에서 접속 가능한 고정 IP라면 어디서든지 온도와 습도를 확인할 수 있을 것이다.

외부 모듈 사용하기

직접 HTTP 요청 메시지를 구성해서 AT 명령어로 전송하는 것은 매우 번거로운 일이다. 이러한 과정을 쉽게 해줄 수 있는 간단한 모듈을 이용해보자.

$ npm i https://github.com/niklauslee/esp8266-http-client

HTTP 서버와 통신해야 할 때, 이 모듈을 이용하면 직접 AT 명령어를 보낼 필요가 없을 뿐만 아니라 HTTP 요청(request) 메시지를 직접 만들 필요가 없고, 수신된 응답(response) 메시지를 또한 자동으로 파싱해서 돌려준다. 이 모듈을 이용하면 코드를 아래와 같이 매우 간결하게 작성할 수 있다.

esp8266-http-client 모듈을 사용한 코드

이번 마지막 강좌에서는 AT 명령어를 이용하여 인터넷으로 온도와 습도를 전송해보았다. 지금까지 강좌를 통해 자바스크립트를 이용한 피지컬 컴퓨팅에 대한 기본적인 내용을 가능한 쉽게 전달하려고 노력했다. 이제 재미있는 다양한 프로젝트를 직접 진행해보는 것을 어떨까?

끝.

--

--

Minkyu Lee
Minkyu Lee

Written by Minkyu Lee

PhD in Computer Science, JavaScript enthusiast, Amateur Jazz drummer.

No responses yet