스마트 디바이스 10주차 Firebase - yubiine/25-1_smartdevice GitHub Wiki
ESP32에서 수집한 센서 데이터를 Firebase Realtime Database에 저장하고 이를 활용해 데이터를 시각화 및 제어하는 방법을 익힌다.
- Firebase는 Google이 제공하는 모바일 및 웹 애플리케이션 개발 플랫폼입니다.
- 서버 없이 빠르게 앱을 개발하고, 사용자 데이터를 저장·분석하며, 실시간으로 기능을 확장할 수 있습니다.
특징 | 설명 |
---|---|
BaaS (Backend-as-a-Service) | 서버 구축 없이 데이터 저장, 인증, 호스팅 등 백엔드 기능 제공 |
실시간 데이터 처리 | 데이터 변경을 클라이언트에 실시간으로 반영 |
크로스 플랫폼 지원 | Android, iOS, 웹 등 다양한 플랫폼 지원 |
구글 생태계와의 통합 | Google Cloud, Analytics, AdMob 등과 손쉽게 연동 |
- ESP32가 센서 데이터를 수집
- Firebase Realtime Database에 저장
- 웹앱에서 실시간 모니터링
- Firebase Hosting을 통해 전 세계 어디서나 접근 가능
- 서버 개발 없이 빠른 MVP 제작 가능
- 실시간 데이터 처리에 최적화
- 보안 규칙 설정이 유연함
- 다양한 라이브러리 및 SDK 제공
- IoT, 모바일, 웹 프로젝트에 모두 적합
- https://console.firebase.google.com/ 접속
- ‘프로젝트 만들기’ 클릭 → 프로젝트 이름 설정 → 기본 설정 완료

- 메뉴에서 ‘Build > Realtime Database’ 선택
- 테스트 모드로 설정


- 익명(Anonymous) 방식을 선택하고 사용 설정 On

- 웹 API키를 확인하고 복사


#include <WiFi.h>
#include <FirebaseESP32.h>
//Provide the token generation process info.
#include "addons/TokenHelper.h"
//Provide the RTDB payload printing info and other helper functions.
#include "addons/RTDBHelper.h"
// Firebase 설정
#define FIREBASE_HOST ""
#define FIREBASE_AUTH ""
#define WIFI_SSID ""
#define WIFI_PASSWORD ""
// 내장 LED 핀 설정
#define LED_PIN 2
// Firebase 객체 생성
FirebaseData firebaseData;
FirebaseAuth auth;
FirebaseConfig config;
bool signupOK = false;
unsigned long sendDataPrevMillis = 0;
void setup_wifi() {
delay(10);
// Wi-Fi 네트워크에 연결 시작
Serial.println();
Serial.print("연결 중인 Wi-Fi: ");
Serial.println(WIFI_SSID);
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.println("Wi-Fi 연결됨");
Serial.println("IP 주소: ");
Serial.println(WiFi.localIP());
}
void setup() {
// 시리얼 통신 초기화
Serial.begin(115200);
// WiFi 연결
setup_wifi();
// API 키를 할당합니다.(필수)
config.api_key = FIREBASE_AUTH;
//RTDB URL을 할당합니다.(필수)
config.database_url = FIREBASE_HOST;
/* Sign up */
if (Firebase.signUp(&config, &auth, "", "")){
Serial.println("ok");
signupOK = true;
}
else{
Serial.printf("%s\n", config.signer.signupError.message.c_str());
}
// 긴 실행 시간이 필요한 토큰 생성 작업을 위한 콜백 함수를 할당합니다.
config.token_status_callback = tokenStatusCallback;
Firebase.begin(&config, &auth);
Firebase.reconnectWiFi(true);
// 내장 LED 핀 설정
pinMode(LED_PIN, OUTPUT);
// "/ledState" 경로를 0으로 초기화
if (Firebase.ready() && signupOK)
{
if (Firebase.setInt(firebaseData, "/ledState", 0)){
Serial.println("PASSED");
Serial.println("PATH: " + firebaseData.dataPath());
Serial.println("TYPE: " + firebaseData.dataType());
}
else {
Serial.println("FAILED");
Serial.println("REASON: " + firebaseData.errorReason());
}
}
}
void loop() {
// Firebase.ready() 함수는 인증 작업을 처리하기 위해 반복적으로 호출되어야 합니다.
if (Firebase.ready() && signupOK && \
(millis() - sendDataPrevMillis > 2000 || sendDataPrevMillis == 0))
{
sendDataPrevMillis = millis();
// Firebase에서 LED 상태 가져오기
int ledState = 0;
if (Firebase.getInt(firebaseData, "/ledState")) {
if (firebaseData.dataType() == "int") {
ledState = firebaseData.intData();
Serial.println(ledState);
if (ledState == 1) {
digitalWrite(LED_PIN, HIGH);
} else {
digitalWrite(LED_PIN, LOW);
}
}
}
else {
Serial.println(firebaseData.errorReason());
}
}
}
IMG_7459.MOV

int sensorPin = 34;
int value = 0;
void setup() {
Serial.begin(115200);
}
void loop() {
value = analogRead(sensorPin);
Serial.println(value);
delay(500);
}
IMG_7460.MOV
#include <WiFi.h>
#include <FirebaseESP32.h>
// 토큰 생성 프로세스 정보 제공
#include "addons/TokenHelper.h"
// RTDB 페이로드 출력 정보 및 기타 도움 함수 제공
#include "addons/RTDBHelper.h"
// Firebase 설정
#define FIREBASE_HOST ""
#define FIREBASE_AUTH ""
#define WIFI_SSID ""
#define WIFI_PASSWORD ""
// Firebase 객체 정의
FirebaseData fbdo;
FirebaseAuth auth;
FirebaseConfig config;
bool signupOK = false;
// 데이터베이스 주 경로
String databasePath = "/room1";
String sensorPath = "/lightsensor";
String timePath = "/timestamp";
/// 부모 노드 (현재 시간 정보로 매 루프마다 업데이트)
String parentPath;
int timestamp;
FirebaseJson json;
const char* ntpServer = "pool.ntp.org";
// 빛 센서 핀
int sensorPin = 34;
// 타이머 변수 (새로운 측정 값을 20초마다 전송)
unsigned long sendDataPrevMillis = 0;
unsigned long timerDelay = 60000; //1 minutes
// WiFi 초기화
void setup_wifi() {
delay(10);
// Wi-Fi 네트워크에 연결 시작
Serial.println();
Serial.print("연결 중인 Wi-Fi: ");
Serial.println(WIFI_SSID);
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.println("Wi-Fi 연결됨");
Serial.println("IP 주소: ");
Serial.println(WiFi.localIP());
}
// 현재 epoch 시간을 가져오는 함수
unsigned long getTime() {
time_t now;
struct tm timeinfo;
if (!getLocalTime(&timeinfo)) {
//Serial.println("Failed to obtain time");
return (0);
}
time(&now);
return now;
}
void setup() {
Serial.begin(115200);
// WiFi 연결
setup_wifi();
configTime(0, 0, ntpServer);
// API 키 할당 (필수)
config.api_key = FIREBASE_AUTH;
// RTDB URL 할당 (필수)
config.database_url = FIREBASE_HOST;
Firebase.reconnectWiFi(true);
fbdo.setResponseSize(4096);
/* 회원 가입 */
if (Firebase.signUp(&config, &auth, "", "")) {
Serial.println("ok");
signupOK = true;
} else {
Serial.printf("%s\n", config.signer.signupError.message.c_str());
}
// 장기 실행 토큰 생성 작업에 대한 콜백 함수 할당
config.token_status_callback = tokenStatusCallback;
// 토큰 생성 최대 재시도 횟수 할당
config.max_token_generation_retry = 5;
// Firebase 인증 및 설정과 함께 라이브러리 초기화
Firebase.begin(&config, &auth);
}
void loop() {
// Send new readings to database
if (Firebase.ready() && signupOK
&& (millis() - sendDataPrevMillis > timerDelay
|| sendDataPrevMillis == 0)) {
sendDataPrevMillis = millis();
// 현재 타임스탬프 가져오기
timestamp = getTime();
Serial.print("time: ");
Serial.println(timestamp);
parentPath = databasePath + "/" + String(timestamp);
json.set(sensorPath.c_str(), String(analogRead(sensorPin)));
json.set(timePath, String(timestamp));
Serial.println("Set json... ");
if (Firebase.setJSON(fbdo, parentPath.c_str(), json))
Serial.println("ok");
else
Serial.println(fbdo.errorReason());
}
}

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
<meta http-equiv="Pragma" content="no-cache" />
<meta http-equiv="Expires" content="0" />
<title>ESP 데이터 기록 Firebase 앱</title>
<style>
#chart_div {
width: 1200px;
height: 500px;
}
#gauge_div {
height: 300px;
}
div {
display: table;
margin-right: auto;
margin-left: auto;
}
</style>
<!-- Firebase SDK 포함 -->
<script src="https://www.gstatic.com/firebasejs/8.10.1/firebase-app.js"></script>
<!-- 필요한 Firebase 기능만 포함 -->
<script src="https://www.gstatic.com/firebasejs/8.10.1/firebase-auth.js"></script>
<script src="https://www.gstatic.com/firebasejs/8.10.1/firebase-database.js"></script>
<script>
// 웹 앱의 Firebase 구성
const firebaseConfig = {
apiKey: "API 키",
authDomain: "esp32-light-sensor-49d0d.firebaseapp.com",
databaseURL: "https://esp32-light-sensor-49d0d-default-rtdb.firebaseio.com",
projectId: "esp32-light-sensor-49d0d",
storageBucket: "esp32-light-sensor-49d0d.appspot.com",
messagingSenderId: "473819592135",
appId: "1:473819592135:web:f839002c4c51d1daa4d5e2"
};
// Firebase 초기화
firebase.initializeApp(firebaseConfig);
// 인증과 데이터베이스 참조 생성
const auth = firebase.auth();
const db = firebase.database();
</script>
</head>
<body>
<!-- 차트를 위한 컨테이너 -->
<div>
<div id="chart_div"></div>
</div>
<div>
<div id="gauge_div"></div>
</div>
<script type="text/javascript"
src="https://www.gstatic.com/charts/loader.js"></script>
<script>
// 현재 차트 패키지 로드
google.charts.load('current', {
packages: ['corechart', 'line', 'gauge'],
});
// API가 로드되었을 때 콜백 함수 설정
google.charts.setOnLoadCallback(drawChart);
function drawChart() {
// 기본 값으로 데이터 객체 생성
let data = google.visualization.arrayToDataTable([
['Time', 'Light Sensor'],
["00:00", 0],
]);
let gauge_data = google.visualization.arrayToDataTable([
['Light Sensor'],
[0],
]);
// 제목, 색상 등이 포함된 옵션 객체 생성
let options = {
max: 2048, //4096,
hAxis: {
//textPosition: 'none',
},
vAxis: {
},
};
// 차트 그리기
let chart = new google.visualization.LineChart(
document.getElementById('chart_div')
);
chart.draw(data, options);
let gauge_chart = new google.visualization.Gauge(
document.getElementById('gauge_div')
);
gauge_chart.draw(gauge_data, options);
// 데이터베이스 경로
var dbPath = 'room1';
// 데이터베이스 참조
var dbRef = firebase.database().ref(dbPath);
// 표시할 최대 데이터 행 수
let maxDatas = 50;
// 최신 측정값 가져와서 차트에 표시 (표시되는 측정값 수는 chartRange 값에 해당)
dbRef.orderByKey().limitToLast(maxDatas).on('child_added', snapshot =>{
var jsonData = snapshot.toJSON(); // 예: {lightsensor: 2502, timestamp:1641317355}
// 값 저장
var lightsensor = Number(jsonData.lightsensor);
var timestamp = epochToDateTime(jsonData.timestamp);
// 차트에 값 표시
if (data.getNumberOfRows() > maxDatas) {
data.removeRows(0, data.getNumberOfRows() - maxDatas);
}
data.addRow([timestamp, lightsensor]);
chart.draw(data, options);
gauge_data.setValue(0, 0, lightsensor);
gauge_chart.draw(gauge_data, options);
});
}
// 에포크 Time을 JavaScript Date 객체로 변환
function epochToJsDate(epochTime){
return new Date(epochTime*1000);
}
// Time을 사람이 읽을 수 있는 형식 (HH:MM)으로 변환
function epochToDateTime(epochTime) {
var epochDate = new Date(epochToJsDate(epochTime));
var dateTime =
("00" + epochDate.getHours()).slice(-2) +
":" +
("00" + epochDate.getMinutes()).slice(-2);
return dateTime;
}
</script>
</body>
</html>

- 앱 스크립트 복사 후 html에 활용
