EmoPot: 식물 건강을 시각화하는 스마트 화분
부품 및 소모품
Arduino Nano R3 1개
DFRobot Gravity: 아날로그 토양 수분 센서 1개
0.96" I2C OLED 디스플레이 1개
필요 도구 및 장비
앱 및 온라인 서비스
프로젝트 소개
식물의 '감정'을 보여주는 스마트 화분을 만들었습니다. 토양 수분 센서로 수분 레벨을 모니터링하고, 수분 수준에 따라 감정 표현(이모티콘)을 OLED에 표시합니다. 식물에게 물은 음식이니 나름 타당하죠? 😋 재미있는 프로젝트로 수분 레벨을 시각적으로 확인할 수 있습니다.
이 화분은 수분 레벨에 따라 다양한 감정 표현을 보여줍니다.
이 프로젝트를 통해 배우는 것:
토양 수분 센서 사용법과 아두이노 ADC로 아날로그 데이터 읽기
OLED에 이미지 표시 방법
아두이노로 애니메이션 만들어 OLED에 표시하기
하드웨어 파트
전자부품 수납 공간이 있는 화분을 Fusion 360으로 설계해 3D 프린트했습니다. 3D 프린터가 없으면 일반 화분 쓰고 전자부품 박스 따로 만드세요.
전자부품 연결:
Arduino A5 → OLED SCL
Arduino A4 → OLED SDA
Arduino A0 → 토양 수분 센서 AO
코딩
코드는 길어 보이지만 매우 단순합니다. 먼저 라이브러리 포함(설치 필요):
Adafruit_GFX.h
Adafruit_SSD1306.h
각 감정 표현별 애니메이션 프레임 함수를 만듭니다(노가다지만 필요). setup()에서 디스플레이 초기화, loop()에서 sensorValue = analogRead(A0);로 센서 읽고 수분 레벨별 조건문으로 해당 감정 애니메이션 프레임 표시.
수분 레벨별 감정 표현 영상
(영상 링크 생략)
코드
EmoPot 코드 (Arduino)
#include
#include
#include
#include
#include
#define OLED_RESET 0
Adafruit_SSD1306 display(OLED_RESET);
int sensorValue = 0;
int xx = 40;
/****************************************** SAD ****************************************************/
// 프레임 데이터 배열들 (frame01 ~ frame121) - 생략(원본 참조)
/************************************************* SMILE *************************************************/
// 프레임 데이터 배열들 (frame02 ~ frame132) - 생략
/************************************** NEUTRAL ***************************************************/
// 프레임 데이터 배열들 (frame03 ~ frame83) - 생략
/*************************************** BLUSHING **************************************************************/
// 프레임 데이터 배열들 (frame04 ~ frame94) - 생략
/**************************************************** CRYING **********************************************/
// 프레임 데이터 배열들 (frame05 ~ frame95) - 생략
// CONFUSED 프레임 (framec) - 생략
/********************************************************************************************************************************/
void setup() {
display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
display.clearDisplay();
display.setFont(&FreeSans9pt7b);
display.setTextColor(WHITE);
display.setCursor(0,0);
}
void loop() {
sensorValue = analogRead(A0); // 센서 아날로그 읽기
if (sensorValue >= 80 && sensorValue < 250) { // SMILE
// display.clearDisplay(); display.drawBitmap(xx, 0, frame02, 48, 48, 1); display.display(); delay(30);
display.clearDisplay(); display.drawBitmap(xx, 0, frame012, 48, 48, 1); display.display(); delay(30);
display.clearDisplay(); display.drawBitmap(xx, 0, frame22, 48, 48, 1); display.display(); delay(30);
display.clearDisplay(); display.drawBitmap(xx, 0, frame32, 48, 48, 1); display.display(); delay(30);
display.clearDisplay(); display.drawBitmap(xx, 0, frame42, 48, 48, 1); display.display(); delay(30);
display.clearDisplay(); display.drawBitmap(xx, 0, frame52, 48, 48, 1); display.display(); delay(30);
display.clearDisplay(); display.drawBitmap(xx, 0, frame62, 48, 48, 1); display.display(); delay(30);
display.clearDisplay(); display.drawBitmap(xx, 0, frame72, 48, 48, 1); display.display(); delay(30);
display.clearDisplay(); display.drawBitmap(xx, 0, frame82, 48, 48, 1); display.display(); delay(30);
display.clearDisplay(); display.drawBitmap(xx, 0, frame92, 48, 48, 1); display.display(); delay(30);
display.clearDisplay(); display.drawBitmap(xx, 0, frame102, 48, 48, 1); display.display(); delay(30);
display.clearDisplay(); display.drawBitmap(xx, 0, frame112, 48, 48, 1); display.display(); delay(30);
display.clearDisplay(); display.drawBitmap(xx, 0, frame122, 48, 48, 1); display.display(); delay(30);
display.clearDisplay(); display.drawBitmap(xx, 0, frame132, 48, 48, 1); display.display(); delay(30);
} else if (sensorValue >= 250 && sensorValue < 350) { // BLUSHING
display.clearDisplay(); display.drawBitmap(xx, 0, frame04, 48, 48, 1); display.display(); delay(50);
display.clearDisplay(); display.drawBitmap(xx, 0, frame14, 48, 48, 1); display.display(); delay(30);
display.clearDisplay(); display.drawBitmap(xx, 0, frame24, 48, 48, 1); display.display(); delay(30);
display.clearDisplay(); display.drawBitmap(xx, 0, frame34, 48, 48, 1); display.display(); delay(30);
display.clearDisplay(); display.drawBitmap(xx, 0, frame44, 48, 48, 1); display.display(); delay(30);
display.clearDisplay(); display.drawBitmap(xx, 0, frame54, 48, 48, 1); display.display(); delay(30);
display.clearDisplay(); display.drawBitmap(xx, 0, frame64, 48, 48, 1); display.display(); delay(30);
display.clearDisplay(); display.drawBitmap(xx, 0, frame74, 48, 48, 1); display.display(); delay(30);
display.clearDisplay(); display.drawBitmap(xx, 0, frame84, 48, 48, 1); display.display(); delay(30);
display.clearDisplay(); display.drawBitmap(xx, 0, frame94, 48, 48, 1); display.display(); delay(70);
} else if (sensorValue >= 350 && sensorValue < 550) { // NEUTRAL
display.clearDisplay(); display.drawBitmap(xx, 0, frame03, 48, 48, 1); display.display(); delay(60);
display.clearDisplay(); display.drawBitmap(xx, 0, frame013, 48, 48, 1); display.display(); delay(60);
display.clearDisplay(); display.drawBitmap(xx, 0, frame23, 48, 48, 1); display.display(); delay(60);
display.clearDisplay(); display.drawBitmap(xx, 0, frame33, 48, 48, 1); display.display(); delay(60);
display.clearDisplay(); display.drawBitmap(xx, 0, frame43, 48, 48, 1); display.display(); delay(60);
display.clearDisplay(); display.drawBitmap(xx, 0, frame53, 48, 48, 1); display.display(); delay(60);
display.clearDisplay(); display.drawBitmap(xx, 0, frame63, 48, 48, 1); display.display(); delay(60);
display.clearDisplay(); display.drawBitmap(xx, 0, frame73, 48, 48, 1); display.display(); delay(60);
display.clearDisplay(); display.drawBitmap(xx, 0, frame83, 48, 48, 1); display.display(); delay(500);
} else if (sensorValue >= 550 && sensorValue < 700) { // SAD
// display.clearDisplay(); display.drawBitmap(xx, 0, frame01, 48, 48, 1); display.display(); delay(50);
display.clearDisplay(); display.drawBitmap(xx, 0, frame011, 48, 48, 1); display.display(); delay(70);
display.clearDisplay(); display.drawBitmap(xx, 0, frame21, 48, 48, 1); display.display(); delay(70);
display.clearDisplay(); display.drawBitmap(xx, 0, frame31, 48, 48, 1); display.display(); delay(70);
display.clearDisplay(); display.drawBitmap(xx, 0, frame41, 48, 48, 1); display.display(); delay(70);
display.clearDisplay(); display.drawBitmap(xx, 0, frame51, 48, 48, 1); display.display(); delay(70);
display.clearDisplay(); display.drawBitmap(xx, 0, frame61, 48, 48, 1); display.display(); delay(70);
display.clearDisplay(); display.drawBitmap(xx, 0, frame71, 48, 48, 1); display.display(); delay(70);
display.clearDisplay(); display.drawBitmap(xx, 0, frame81, 48, 48, 1); display.display(); delay(70);
display.clearDisplay(); display.drawBitmap(xx, 0, frame91, 48, 48, 1); display.display(); delay(70);
display.clearDisplay(); display.drawBitmap(xx, 0, frame101, 48, 48, 1); display.display(); delay(70);
display.clearDisplay(); display.drawBitmap(xx, 0, frame111, 48, 48, 1); display.display(); delay(70);
display.clearDisplay(); display.drawBitmap(xx, 0, frame121, 48, 48, 1); display.display(); delay(100);
} else if (sensorValue >= 700 && sensorValue < 800) { // CRYING
display.clearDisplay(); display.drawBitmap(xx, 0, frame05, 48, 48, 1); display.display(); delay(30);
display.clearDisplay(); display.drawBitmap(xx, 0, frame15, 48, 48, 1); display.display(); delay(30);
display.clearDisplay(); display.drawBitmap(xx, 0, frame25, 48, 48, 1); display.display(); delay(30);
display.clearDisplay(); display.drawBitmap(xx, 0, frame35, 48, 48, 1); display.display(); delay(30);
display.clearDisplay(); display.drawBitmap(xx, 0, frame45, 48, 48, 1); display.display(); delay(30);
display.clearDisplay(); display.drawBitmap(xx, 0, frame55, 48, 48, 1); display.display(); delay(30);
display.clearDisplay(); display.drawBitmap(xx, 0, frame65, 48, 48, 1); display.display(); delay(30);
display.clearDisplay(); display.drawBitmap(xx, 0, frame75, 48, 48, 1); display.display(); delay(30);
display.clearDisplay(); display.drawBitmap(xx, 0, frame85, 48, 48, 1); display.display(); delay(30);
display.clearDisplay(); display.drawBitmap(xx, 0, frame95, 48, 48, 1); display.display(); delay(70);
} else { // CONFUSED
display.clearDisplay(); display.drawBitmap(xx, 0, framec, 48, 48, 1); display.display(); delay(30);
}
}
주: 프레임 데이터 배열(frame01~frame132, framec)은 원본 코드에 포함되어 있으나 지면상 생략했습니다. 전체 코드는 원본을 참조하세요.
회로도