1. 개요
NodeMCU 개발보드를 웹서버로 사용함으로써 온습도센서(DHT11)의 센서값을 웹페이지에 출력해보았고 웹클라이언트로 HTTP 웹페이지에 접속하는 작업을 해보았다. 이 글에서는 온습도 센서값을 웹페이지에 출력하며 또한 일정 시간마다 센서값을 PHP로 구현된 웹서버에 넘겨주어 데이터베이스(MySQL DB)에 값을 저장시키는 작업을 해볼 것이다.
1.1 요약 정리
7번 이후부터 병렬로 동작됩니다.
- 웹브라우저는 웹클라이언트
- NodeMCU 보드는 웹서버(웹브라우저 기준)이자 웹클라이언트(라즈베리파이 기준)
- 라즈베리파이는 웹서버 및 데이터베이스
1. 웹서버 구축(라즈베리파이)
2. 웹서버 데이터베이스, 테이블 생성(라즈베리파이)
3. 웹서버에 요청이 들어올 때 PHP 파일을 통해 데이터베이스로 값 전송(라즈베리파이)
4. HTML 언어로 웹페이지 구성
5. 구성한 웹페이지를 NodeMCU 보드에 업로드 (아두이노 IDE)
6. NodeMCU 보드를 Wi-Fi에 연결 (NodeMcu 보드)
7. NodeMCU 보드를 웹서버/웹클라이언트로 구동 (NodeMcu 보드)
8-1. 일정 시간마다 온도와 습도 측정 (NodeMcu 보드)
9-1. 측정한 센서값을 웹서버(라즈베리파이)로 전송 (NodeMcu 보드)
8-2. 웹클라이언트에서 NodeMCU 보드 웹서버에 웹페이지 요청 (크롬, 파이어폭스 등의 웹브라우저)
9-2. 웹클라이언트로 요청이 오면 온도와 습도 측정 후 웹클라이언트에게 보여줌 (NodeMcu 보드)
1.2 준비물
- NodeMCU 개발보드(이 글에서는 NodeMCU v3)
- Micro 5pin USB
- 웹서버(이 글에서는 라즈베리파이)
2. 라즈베리파이 웹서버 만들기(옵션)
2.1 웹서버 구축
이 글에서 데이터베이스와 연동되는 PHP 웹서버는 라즈베리파이로 구현합니다. 라즈베리파이 라즈비안 APM(Apache, PHP, MySql, phpMyAdmin) 설치하기 글에서 기본적인 웹서버를 구현할 수 있습니다.
2.2 데이터 베이스 및 테이블 만들기
데이터베이스에 접속한 것을 기준으로 진행합니다. 아래 명령어로 nodemcu 라는 이름의 데이터베이스를 생성합니다.
create database nodemcu;
nodemcu 데이터베이스를 사용합니다.
use nodemcu;
temp int(10), humidity int(10) 필드를 가지는 dht 이름의 테이블을 생성합니다.
create table dht(temp int(10), humidity int(10));
3. 웹페이지 구성(PHP)
라즈베리파이 라즈비안 OS 기준으로 설명합니다. 라즈베리파이가 아니라면 아래 PHP 코드만 자신의 웹서버가 구동하는 위치에 insert.php 이름으로 만들어주면 됩니다. (윈도우 기준 C:\wamp64\www\ 또는 C:\xampp\htdocs 등의 경로)
다음 명령어로 insert.php 파일을 만든 후 그 아래 코드를 작성합니다. 계정ID 와 계정암호는 사용하는 SQL 유저 ID, 암호를 입력해줍니다. PHP 코드는 SQL 에 접속 후 $_GET[''] 로부터 받은 온습도 값을 dht 테이블에 삽입해주는 PHP 코드입니다. 이 글은 PHP 또는 SQL 를 설명하는 글이 아니므로 자세한 설명은 추후 PHP, SQL 글에서 설명하겠습니다.
"Ctrl + X", "Y", "Enter" 를 순서대로 눌러줘서 저장해주고 나옵니다.
sudo nano /var/www/html/insert.php
<?php
$servername = "localhost";
$username = "계정ID";
$password = "계정암호";
$dbname = "nodemcu";
$conn = mysqli_connect($servername, $username, $password, $dbname);
$temp = $_GET['temp'];
$humidity = $_GET['humidity'];
$sql = "INSERT INTO dht(temp, humidity) VALUES($temp, $humidity)";
mysqli_query($conn, $sql);
mysqli_close($conn);
?>
4. 소스코드
4.1 전체 소스코드
#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
#include <DHT.h>
#define PIN_DHT D4
const char* ssid = "Wi-Fi 아이디";
const char* password = "Wi-Fi 비밀번호";
String host = "Host 주소";
const long interval = 5000;
unsigned long previousMillis = 0;
WiFiServer server(80);
WiFiClient client;
HTTPClient http;
DHT DHTsensor(PIN_DHT, DHT11);
void setup() {
DHTsensor.begin();
Serial.begin(115200);
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.print("Connecting to ");
Serial.println(ssid);
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
server.begin();
Serial.println("Server started");
}
void loop() {
// 센서값 DB 전송 부분
unsigned long currentMillis = millis();
if(currentMillis - previousMillis >= interval) {
previousMillis = currentMillis;
float humidity = DHTsensor.readHumidity();
float temp = DHTsensor.readTemperature();
String phpHost = host+"/insert.php?temp="+String(temp)+"&humidity="+String(humidity);
Serial.print("Connect to ");
Serial.println(phpHost);
http.begin(client, phpHost);
http.setTimeout(1000);
int httpCode = http.GET();
if(httpCode > 0) {
Serial.printf("GET code : %d\n\n", httpCode);
if(httpCode == HTTP_CODE_OK) {
String payload = http.getString();
Serial.println(payload);
}
}
else {
Serial.printf("GET failed, error: %s\n", http.errorToString(httpCode).c_str());
}
http.end();
}
// 웹서버 부분
client = server.available();
if(!client) return;
Serial.println("새로운 클라이언트");
client.setTimeout(5000);
String request = client.readStringUntil('\r');
Serial.println("request: ");
Serial.println(request);
while(client.available()) {
client.read();
}
float humidity = DHTsensor.readHumidity();
float temp = DHTsensor.readTemperature();
client.print("HTTP/1.1 200 OK");
client.print("Content-Type: text/html\r\n\r\n");
client.print("<!DOCTYPE HTML>");
client.print("<html>");
client.print("<head>");
client.print("<meta charset=\"UTF-8\" http-equiv=\"refresh\" content=\"1\">");
client.print("<title>DHT senrsor test Webpage</title>");
client.print("</head>");
client.print("<body>");
client.print("<h2>DHT senrsor test Webpage</h2>");
client.print("<br>");
client.print("Temperature : ");
client.print(temp);
client.print(" °C");
client.print("<br>");
client.print("Humidity : ");
client.print(humidity);
client.print(" %");
client.print("</body>");
client.print("</html>");
Serial.println("클라이언트 연결 해제");
}
4.2 소스코드 설명
코드에 대한 자세한 설명은 NodeMCU - 웹서버 웹페이지에서 서보모터 제어하기, NodeMCU - 웹서버 웹페이지에서 NodeMCU LED 제어하기 글에서 확인하시길 바랍니다.
const long* interval = 5000;
unsigned long previousMillis = 0;
인터럽트 타이머를 위한 변수 및 상수 선언 부분이며 아래서 추가로 설명합니다.
unsigned long currentMillis = millis();
if(currentMillis - previousMillis >= interval) {
previousMillis = currentMillis;
NodeMCU - NodeMCU 웹클라이언트 HTTP 접속하기 글을 보면 delay() 함수를 사용하여 5초마다 접속시도를 해주었습니다. 하지만 이 글에서는 delay() 함수를 사용할 수 없습니다. 왜냐하면 NodeMCU 보드가 웹서버로도 사용되기 때문입니다.
무슨말이냐 하면 delay() 함수는 일정 시간동안 아무것도 안하고 마냥 멈춰있습니다. 멈춰있는 동안 웹브라우저(웹클라이언트)가 NodeMCU 보드로 요청을한다면? NodeMCU 보드는 아무것도 못하고 일단 delay() 함수가 끝날 때까지 기다려야합니다.
이런경우 좋은 코드가 아니기 때문에 계속 웹클라이언트의 요청을 확인하되 일정시간마다 온습도를 측정, 데이터베이스로 저장할 수 있도록 millis() 함수를 사용하였습니다.
아래 그림은 loop() 함수에서 좋은 코드(millis 함수 사용)와 나쁜 코드(delay 함수 사용)의 알고리즘입니다.
String phpHost = host+"/insert.php?temp="+String(temp)+"&humidity="+String(humidity);
이전 글에서 단지 http 페이지를 요청하였다면 이 글에서는 온습도 센서값을 데이터베이스로 넘겨주는 작업도 해야합니다. 그에 따른 PHP 문법은 다음과 같습니다. http://웹서버IP/insert.php?temp=온도값&humidity=습도값
여기서 웹서버 IP 는 host 문자열로 대신하며 온습도 값은 측정할 때마다 바뀌니 위에서 float 형으로 측정한 온습도 값을 문자열로 바꾸어 넣어줍니다.
5. 동작 결과
웹브라우저로 온습도 값이 1초마다 바뀌며 잘 출력되는 것을 확인할 수 있으며 "http://192.168.1.100/insert.php?temp=온도값&humidity=습도값" 위 주소로 온습도값이 잘 전달되는 것도 확인할 수 있습니다. (192.168.1.100 : 필자의 라즈베리파이 IP 주소)
phpMyAdmin 을 통해 NodeMCU 로 부터 전달받은 데이터가 잘 저장되는지 확인도 가능합니다.
bugwhale.tistory.com/entry/nodemcu-php-mysql-db-connect