EmoPot:植物の健康状態を表情で可視化するスマート植木鉢
必要なコンポーネントと用品
| パーツ | 数量 |
|---|---|
| Arduino Nano R3 | 1 |
| DFRobot Gravity: アナログ土壌水分センサー | 1 |
| 0.96インチ I2C OLEDディスプレイ | 1 |
必要な工具
- はんだごて(汎用)
- 3Dプリンター(汎用)
アプリとオンラインサービス
- Arduino IDE
プロジェクト概要
このプロジェクトでは、植物の「感情」を表情で表示するスマート植木鉢を作ります。土壌水分センサーで水分を監視し、そのレベルに応じて様々な表情をOLEDに表示します。植物にとって水は食事のようなものなので、ある意味理にかなっています😋。楽しく作れて、視覚的に水分レベルを監視できるガジェットです。
水分レベルに応じて異なる感情表現を表示します。
このプロジェクトを通じて学べること:
- 土壌水分センサーの使い方とArduino ADCによるアナログ読み取り
- OLEDへの画像表示方法
- Arduinoでアニメーションを作成しOLEDに表示する方法
ハードウェア部分
まず植木鉢が必要です。Fusion 360で電子部品収納スペース付きの鉢を設計しました。3Dプリンターがあれば自分でプリント可能。なければ市販の鉢を使い、電子部品用のボックスを別途作ってもOKです。
電子部品の接続:
- Arduino A5 → OLED SCL
- Arduino A4 → OLED SDA
- Arduino A0 → 土壌水分センサー AO
コーディング
コードは長く見えますが仕組みはシンプルです。冒頭でライブラリをインクルード(要インストール):
- Adafruit_GFX.h
- Adafruit_SSD1306.h
各感情表現のアニメーションフレームごとに関数を作成しました(これが本当に大変でした)。setup()でディスプレイ初期化、loop()で sensorValue = analogRead(A0); と読み取り、水分レベルに応じた条件分岐で該当する表情アニメーションを表示します。
水分レベル別の表情アニメーション動画
(動画は省略)
コード全文
#include
#include
#include
#include
#include
#define OLED_RESET 0
Adafruit_SSD1306 display(OLED_RESET);
int sensorValue = 0;
int xx = 40;
/****************************************** SAD(悲しみ) ****************************************************/
// フレームデータ配列(省略:全フレームをPROGMEMに格納)
const unsigned char PROGMEM frame01 [] = { ... };
const unsigned char PROGMEM frame011 [] = { ... };
const unsigned char PROGMEM frame21 [] = { ... };
const unsigned char PROGMEM frame31 [] = { ... };
const unsigned char PROGMEM frame41 [] = { ... };
const unsigned char PROGMEM frame51 [] = { ... };
const unsigned char PROGMEM frame61 [] = { ... };
const unsigned char PROGMEM frame71 [] = { ... };
const unsigned char PROGMEM frame81 [] = { ... };
const unsigned char PROGMEM frame91 [] = { ... };
const unsigned char PROGMEM frame101 [] = { ... };
const unsigned char PROGMEM frame111 [] = { ... };
const unsigned char PROGMEM frame121 [] = { ... };
// const unsigned char PROGMEM frame131 [] = { ... };
/************************************************* SMILE(笑顔) *************************************************/
const unsigned char PROGMEM frame02 [] = { ... };
const unsigned char PROGMEM frame012 [] = { ... };
const unsigned char PROGMEM frame22 [] = { ... };
const unsigned char PROGMEM frame32 [] = { ... };
const unsigned char PROGMEM frame42 [] = { ... };
const unsigned char PROGMEM frame52 [] = { ... };
const unsigned char PROGMEM frame62 [] = { ... };
const unsigned char PROGMEM frame72 [] = { ... };
const unsigned char PROGMEM frame82 [] = { ... };
const unsigned char PROGMEM frame92 [] = { ... };
const unsigned char PROGMEM frame102 [] = { ... };
const unsigned char PROGMEM frame112 [] = { ... };
const unsigned char PROGMEM frame122 [] = { ... };
const unsigned char PROGMEM frame132 [] = { ... };
/************************************** NEUTRAL(普通) ***************************************************/
const unsigned char PROGMEM frame03 [] = { ... };
const unsigned char PROGMEM frame013 [] = { ... };
const unsigned char PROGMEM frame23 [] = { ... };
const unsigned char PROGMEM frame33 [] = { ... };
const unsigned char PROGMEM frame43 [] = { ... };
const unsigned char PROGMEM frame53 [] = { ... };
const unsigned char PROGMEM frame63 [] = { ... };
const unsigned char PROGMEM frame73 [] = { ... };
const unsigned char PROGMEM frame83 [] = { ... };
/*************************************** BLUSHING(照れ) **************************************************************/
const unsigned char PROGMEM frame04 [] = { ... };
const unsigned char PROGMEM frame14 [] = { ... };
const unsigned char PROGMEM frame24 [] = { ... };
const unsigned char PROGMEM frame34 [] = { ... };
const unsigned char PROGMEM frame44 [] = { ... };
const unsigned char PROGMEM frame54 [] = { ... };
const unsigned char PROGMEM frame64 [] = { ... };
const unsigned char PROGMEM frame74 [] = { ... };
const unsigned char PROGMEM frame84 [] = { ... };
const unsigned char PROGMEM frame94 [] = { ... };
/**************************************************** CRYING(泣き) **********************************************/
const unsigned char PROGMEM frame05 [] = { ... };
const unsigned char PROGMEM frame15 [] = { ... };
const unsigned char PROGMEM frame25 [] = { ... };
const unsigned char PROGMEM frame35 [] = { ... };
const unsigned char PROGMEM frame45 [] = { ... };
const unsigned char PROGMEM frame55 [] = { ... };
const unsigned char PROGMEM frame65 [] = { ... };
const unsigned char PROGMEM frame75 [] = { ... };
const unsigned char PROGMEM frame85 [] = { ... };
const unsigned char PROGMEM frame95 [] = { ... };
// 混乱表情
const unsigned char PROGMEM 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) { // 笑顔
// 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) { // 照れ
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) { // 普通
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) { // 悲しみ
// display.clearDisplay(); display.drawBitmap(xx, 0, frame01, 48, 48, 1); display.display(); delay(50);
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);
// display.clearDisplay(); display.drawBitmap(xx, 0, frame131, 48, 48, 1); display.display(); delay(50);
} else if (sensorValue >= 700 && sensorValue < 800) { // 泣き
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 { // 混乱/範囲外
display.clearDisplay(); display.drawBitmap(xx, 0, framec, 48, 48, 1); display.display(); delay(30);
}
}
カスタムパーツとエンクロージャ
(3Dプリントファイル等は別途提供)
回路図
製造プロセス
- サスケハナ原子力発電所、計画保全完了し2号機運転再開
- ショベル完全解説:タイプ、用途、プロジェクトに合った一台の選び方
- パンチとダイスの交換が必要な重要サイン
- 3Dプリントを活用した 10 の革新的 DIY オープンソースプロジェクト
- 原子時計技術がオンライン配送の荷物盗難を削減
- AI搭載匂い検知:アリバールの技術がビジネスとサプライチェーンに革命をもたらす
- 発電機アップグレードが必要な7つの重要サイン
- CNC製造コストに影響する重要要因 – パートII
- 5軸CNC機械加工で競争力を高める業界:効率と精度を両立
- フォークリフト転倒ゼロへ:4大リスク別の必須安全対策
- カスタム機械加工部品が既製品を4つの主要領域で上回る理由