永遠に咲き続ける機械チューリップ – タッチで開花するLEDフラワー
必要な部品と材料
- Arduino Nano R3 × 1
- SG90 マイクロサーボ × 1
- TTP223 タッチセンサ × 1
- 真鍮線 1mm径 × 1
- 真鍮パイプ 2mm径(内径1.2mm) × 1
- 絶縁銅線 0.3mm径 × 1
- WS2812 5050 NeoPixel LED × 7
- 白色SMD 1206 LED × 30
- 木材(台座用)
- はんだ・フラックス・熱収縮チューブ等
プロジェクトの概要
枯れない花を、大切な人への贈り物に。バレンタインデーだけでなく、どんな記念日にも最適な機械式チューリップです。茎の葉にそっと触れるだけで、6枚の花弁がゆっくり開き、虹色に光ります。閉じた状態では葉脈パターンの間接照明としても美しく輝きます。

仕組み
- 花弁6枚それぞれに白色SMD LED 5個を内蔵。各花弁はArduinoの出力ピン1つに接続。
- 花芯部分にNeoPixel RGB LED 7個を搭載(デイジーチェーン接続)。
- 電気的には花全体がGND、花弁の葉脈がプラス電圧。
- 花弁の開閉は、茎を貫くプッシュロッドで行い、ベース内のサーボモータで駆動。
- 茎の側面にある小さな葉が、ベース内のTTP223タッチセンサと接続され、静電容量式タッチパッドとして機能。
- 木製ベース内にサーボ、タッチIC、Arduino Nanoを収納。


事前知識・スキル
- 真鍮のはんだ付け(参考記事)
- Arduinoの基本的な使い方
- サーボモータの制御
- LEDとNeoPixelの扱い
部品の正確な寸法や花弁の形は指定しません。それぞれの花をユニークなものにしてください。
製作ステップ
1. 花弁の製作(約4時間)
まず花弁のテンプレートを作ります。私は石膏をチューブ型に流し込み、乾燥後にチューリップの花弁形状に削りました。3Dプリンタがあればそれを使っても構いません。石膏は加工しやすく、鉛筆で書けるので便利です。1枚の花弁はテンプレート表面の1/4の面積で、6枚作ると少し重なり合ってチューリップらしい形になります。




花弁の構成:
- 1mm真鍮線で外周を形成
- 内側に白色SMD 1206 LED 5個と「葉脈」構造を真鍮線で配線
- 底部の平らな部分に短いチューブを通し、ヒンジとする(はんだで埋めないよう注意)
- LEDと葉脈をはんだ付け
全6枚の花弁を完全に同一に作ることが極めて重要です。 寸法がばらつくと、閉じたときに綺麗なチューリップ形状にならず、干渉して動かなくなる恐れがあります。


2. 花芯の組み立て(約1時間)
6枚の花弁を六角形のベースにはんだ付けします。正六角形だと花弁が重なり合えないため、やや不規則な六角形(内側3枚・外側3枚でオフセット)にします。テンプレートを紙に印刷し、中心から放射状に伸びる線に合わせてヒンジ(チューブ)を固定します。最後に花弁を閉じると美しい蕾の形になります。




3. 茎とプッシュロッドの製作(約1時間)
※本来は最初に茎を作るべきです。私は後から付け足して大変な目に遭いました。
花弁の開閉は、真鍮パイプ内を1mm真鍮線(プッシュロッド)が上下することで行います。花弁はプッシュロッド先端の小さな六角形ヘッド(プッシュロッドヘッド)と連結ロッドで接続されます。このヘッドの製作は時計職人並みの精度が必要です。


茎は真鍮パイプを所定長に切断。茎が花芯の六角形ベースに対して完全に垂直で、中心から外れず、プッシュロッドヘッドが下まで降りて花弁を閉じられるスペースがあることが絶対条件です。まず茎の片側にV字形の広がりを作り、花芯を逆さまにして対向する2頂点にはんだ付け。残り4頂点も同様に接続します。パイプ内部にはんだが入らないよう細心の注意を払ってください。


4. 花弁機構の調整(約6時間)
ここが最も忍耐のいる工程です。蒸気機関のロッド・ピストン・フライホイールから着想を得ました。


- テストスタンドを作り、花を上向きに固定。プッシュロッドを茎に通し、底端に短いチューブを垂直にはんだ付け、サーボと連結するロッドを1mm真鍮線で作成。
- Arduinoでサーボを動かし、プッシュロッドの上下運動を調整。最上位置でプッシュロッドヘッドが花芯の六角形ベースと面一になり、最下位置で花芯のV字ベース底部に着くようにコードで微調整。


花弁には「花弁ピン」と呼ばれるヒンジが必要です。これは花弁の根元に平行にはんだ付けしたバーに取り付けられたチューブで、花弁が完全に開くためには花弁表面より上に出ている必要があります。試行錯誤でバーの位置と連結ロッドの長さを決め、1枚目がスムーズに動くようにしたら、残り5枚も同様に仕上げます。外側の3枚は内側の3枚より少し低い位置で開くように調整し、閉じたときに綺麗に重なるようにします。



5. 虹色の花芯(約1時間)
花芯内部に7個のNeoPixelを配置。これらはデータ線1本で制御可能でデイジーチェーン接続できます。上下2つの小さな六角形リング(下:GND、上:VCC)の間にNeoPixelを45度傾けてはんだ付けし、側面に光を向けます。中央に7個目を配置。データ線のIN/OUTをつなぎチェーンを完成させます。
この構造にはVCCとDATAの2線がベースまで伸びます。GNDは花のフレームから取ります。0.3mm絶縁銅線を上側リング(VCC)と最初のNeoPixel(DATA)にはんだ付けし、茎の長さの3倍以上の長さを確保。端の絶縁被覆をナイフで剥いてはんだ付けします(熱で溶かさない)。テストして点灯確認を。銅線は繊細なので、端以外の被覆を傷つけないよう注意。ショートの原因になります。
最後に花芯構造を花芯の中心に配置(六角形ベースから約1cm上)、頂点を真鍮ロッドで接続して剛性を確保。花弁の連結ロッドと干渉しないよう位置調整を。これで花本体は完成です。花弁が自由に動くか最終確認を。


6. 根元の配線(約2時間)
花弁6枚分の電源線とNeoPixel用2線を茎に沿わせます。花全体がGNDなので、プラス線のみを引き回します。NeoPixel用2線は済み。各花弁のヒンジ近くの葉脈線にはんだ付けし、花芯構造を通って茎を下ろします。鋭角に曲げると断線するので緩やかに。

茎の上端付近で全線をビニールテープで仮止め(まだ緩めに)。花弁とプッシュロッドの動きに干渉しないよう配線を整理し、テープを締め付け。茎に沿って線をきれいに巻き付けていきます(1時間以上かかります)。茎の端で再度テープで固定し、瞬間接着剤で固定。プッシュロッドが動く穴を塞がないよう注意。

茎の底端にはGND線を1本はんだ付け。計9本の線が花から出ることになります。ここで一度Arduinoに全線を接続し、ショートがないか、全LEDが点くかテストすることを強く推奨します。


7. 花瓶(ベース)の製作(約2時間)
電子部品を隠す花瓶型の木製ベースを作ります。高さ4cm、直径9cmの円柱を木材から削り出し(旋盤がない場合は丸鋸で粗削り、ボール盤で旋盤のように回して成形)、深さ2.5cm、直径7cmの穴をフライスで開けてサーボ、Arduino Nano、タッチセンサICを収納。底面にはArduinoのUSBポートが横から挿せる小さな穴も開けます。




茎の太さ(配線込み)に合わせた穴を花が生える位置(中心)に開け、花を試し入れ。配線を鋭角に曲げないよう慎重に。内部にサーボアームと連結ロッドのスペースを確保するため、さらに大きな穴を内側から開けておきます。花瓶の形は自由ですが、全電子部品が収まるサイズにしてください。


8. タッチ用葉パッド(約1時間)
茎の側面に小さな葉を設け、これをTTP223タッチセンサの静電容量パッドとして利用。触れるとArduinoに信号が行き、花が開きます。花弁と同じ要領でLEDなしで作成。ベースに葉を固定する小さな穴も開けておきます。タッチセンサが難しければ、ベースに物理プッシュボタンを付けても同じ動作が可能です。
9. 最終組み立て(約2時間)
いよいよ最終段階。花をベースの穴に再度挿入。二度測って一度切るの精神で。
- 花弁を全開にし、茎から出ているプッシュロッドの先端を茎の端面と面一になるようカット。
- 花弁を閉じるとプッシュロッドが引き込まれる。プッシュロッド底端に短いチューブを垂直にはんだ付けし、サーボアームとの連結ロッド用ヒンジとする。このチューブがストッパーにもなり、手を離せば全開で止まる。
- 茎をベースに接着(瞬間接着剤)。茎のパイプ端がベース内面と面一になるよう合わせ、サーボアームのスペースを最大化。プッシュロッドと茎パイプを接着しないこと! これで台無しになります。
- 葉パッドを接着。予め銅線をはんだ付けしてTTP223に接続できるようにしておく。

花を逆さまにしてベース内部の作業。サーボを最終位置に固定(テストスタンドで作ったアームを使用)。サーボアームがベース内で自由に動けるスイートスポットを見つけ、連結ロッドをプッシュロッドに接続。サーボ固定には薄い金属板とネジ2本を使用(故障時や位置調整のため取り外し可能に)。自信があれば接着でも可。

TTP223モジュールを使う場合、モジュール基板のタッチパッド面(部品実装面の裏側)に葉パッドの線をはんだ付け。シルクマスクを削って銅箔を露出させる。モジュールを接着固定。
花弁の線(6本)は電流制限抵抗(200Ω×6、ユニバーサル基板上に実装)を介してArduinoに接続。NeoPixel用のVCCとDATA、タッチセンサ、サーボの電源線も配線。

配線まとめ
- サーボ データ線 → D9(Adafruit_TiCoServoライブラリ必須)
- TTP223 データ → D2(割り込み用)
- NeoPixel DATA → A0(任意の出力ピン可)
- 花弁LED → D3, D4, D5, D6, D10, D11(任意の出力ピン可)
- 花GND線 → GND
- NeoPixel VCC → 5V
- TTP223 GND → GND
- TTP223 VCC → 5V
- サーボ GND → GND
- サーボ VCC → 5V

10. プログラミング(約1時間)
サーボ制御コードはすでにテスト済み。これにNeoPixel、花弁LED、タッチセンサの制御を追加するだけ。添付のコードを参照し、配線が同じならそのまま使用可能。ただしSERVO_OPENとSERVO_CLOSEDの値は必ず自分の機構に合わせて変更してください。
完成!
あなたの作品をぜひシェアしてください。記事を気に入ったら「いいね」を。私のTindieストアで作品を購入して支援していただけると嬉しいです。最新プロジェクトはTwitterで発信しています。

ソースコード(flower.ino)
#include#include #include "SoftPWM.h" #define NEOPIXEL_PIN A0 #define TOUCH_SENSOR_PIN 2 #define SERVO_PIN 9 //#define SERVO_OPEN 1750 #define SERVO_OPEN 1650 #define SERVO_SAFE_MIDDLE 1000 #define SERVO_CLOSED 775 #define RED 0 #define GREEN 1 #define BLUE 2 float currentRGB[] = {0, 0, 0}; float changeRGB[] = {0, 0, 0}; byte newRGB[] = {0, 0, 0}; #define MODE_SLEEPING 0 #define MODE_BLOOM 3 #define MODE_BLOOMING 4 #define MODE_BLOOMED 5 #define MODE_FADE 6 #define MODE_FADING 7 #define MODE_FADED 8 #define MODE_FALLINGASLEEP 9 #define MODE_RAINBOW 90 byte mode = MODE_FADED; byte petalPins[] = {3, 4, 5, 6, 10, 11}; Adafruit_NeoPixel pixels = Adafruit_NeoPixel(7, NEOPIXEL_PIN, NEO_GRB + NEO_KHZ400); Adafruit_TiCoServo servo; int servoChange = 1; // open int servoPosition = SERVO_SAFE_MIDDLE; void setup() { Serial.begin(115200); pixels.begin(); servo.attach(SERVO_PIN, SERVO_CLOSED, SERVO_OPEN); pinMode(TOUCH_SENSOR_PIN, INPUT); attachInterrupt(digitalPinToInterrupt(TOUCH_SENSOR_PIN), _touchISR, RISING); randomSeed(analogRead(A7)); SoftPWMBegin(); pixelsUnifiedColor(pixels.Color(0, 0, 0)); prepareCrossFade(140, 70, 0, 140); servo.write(servoPosition); } int counter = 0; byte speed = 15; void loop() { boolean done = true; switch (mode) { case MODE_BLOOM: prepareCrossFadeBloom(500); changeMode(MODE_BLOOMING); break; case MODE_BLOOMING: done = crossFade() && done; done = openPetals() && done; done = petalsBloom(counter) && done; if (done) { changeMode(MODE_BLOOMED); } break; case MODE_FADE: changeMode(MODE_FADING); break; case MODE_FADING: done = crossFade() && done; done = closePetals() && done; done = petalsFade(counter) && done; if (done) { changeMode(MODE_FADED); } break; case MODE_FADED: changeMode(MODE_FALLINGASLEEP); break; case MODE_FALLINGASLEEP: done = crossFade() && done; done = closePetals() && done; if (done) { changeMode(MODE_SLEEPING); } break; case MODE_RAINBOW: rainbow(counter); break; } counter++; delay(speed); } void changeMode(byte newMode) { if (mode != newMode) { mode = newMode; counter = 0; } } void _touchISR() { if (mode == MODE_SLEEPING) { changeMode(MODE_BLOOM); } else if (mode == MODE_BLOOMED) { changeMode(MODE_FADE); } } boolean petalsBloom(int j) { if (j < 250) { return false; } if (j > 750) { return true; } int val = (j - 250) / 2; for (int i = 0; i < 6; i++) { SoftPWMSet(petalPins[i], val); } return false; } boolean petalsFade(int j) { if (j > 510) { return true; } for (int i = 0; i < 6; i++) { SoftPWMSet(petalPins[i], (510 - j) / 2); } return false; } void prepareCrossFadeBloom(unsigned int duration) { byte color = random(0, 5); switch (color) { case 0: prepareCrossFade(140, 140, 140, duration); break; // white case 1: prepareCrossFade(140, 5, 0, duration); break; // red case 2: prepareCrossFade(30, 70, 170, duration); break; // blue case 3: prepareCrossFade(140, 0, 70, duration); break; // pink case 4: prepareCrossFade(255, 70, 0, duration); break; // orange } } void rainbow(int j) { uint16_t i; byte num = pixels.numPixels() - 1; pixels.setPixelColor(pixels.numPixels() - 1, 100, 100, 100); for (i = 0; i < num; i++) { pixels.setPixelColor(i, colorWheel(((i * 256 / num) + j) & 255)); } pixels.show(); } boolean openPetals() { if (servoPosition >= SERVO_OPEN) { return true; } servoPosition++; servo.write(servoPosition); return false; } boolean closePetals() { if (servoPosition <= SERVO_CLOSED) { return true; } servoPosition--; servo.write(servoPosition); return false; } void pixelsUnifiedColor(uint32_t color) { for (unsigned int i = 0; i < pixels.numPixels(); i++) { pixels.setPixelColor(i, color); } pixels.show(); } void prepareCrossFade(byte red, byte green, byte blue, unsigned int duration) { float rchange = red - currentRGB[RED]; float gchange = green - currentRGB[GREEN]; float bchange = blue - currentRGB[BLUE]; changeRGB[RED] = rchange / (float)duration; changeRGB[GREEN] = gchange / (float)duration; changeRGB[BLUE] = bchange / (float)duration; newRGB[RED] = red; newRGB[GREEN] = green; newRGB[BLUE] = blue; } boolean crossFade() { if (currentRGB[RED] == newRGB[RED] && currentRGB[GREEN] == newRGB[GREEN] && currentRGB[BLUE] == newRGB[BLUE]) { return true; } for (byte i = 0; i < 3; i++) { if (changeRGB[i] > 0 && currentRGB[i] < newRGB[i]) { currentRGB[i] += changeRGB[i]; } else if (changeRGB[i] < 0 && currentRGB[i] > newRGB[i]) { currentRGB[i] += changeRGB[i]; } else { currentRGB[i] = newRGB[i]; } } pixelsUnifiedColor(pixels.Color(currentRGB[RED], currentRGB[GREEN], currentRGB[BLUE])); return false; } uint32_t colorWheel(byte wheelPos) { wheelPos = 255 - wheelPos; if (wheelPos < 85) { return pixels.Color(255 - wheelPos * 3, 0, wheelPos * 3); } if (wheelPos < 170) { wheelPos -= 85; return pixels.Color(0, wheelPos * 3, 255 - wheelPos * 3); } wheelPos -= 170; return pixels.Color(wheelPos * 3, 255 - wheelPos * 3, 0); }
製造プロセス
- 真鍮:組成、歴史、現代製造
- Arduinoで携帯型静電容量タッチピアノを自作:MPR121シールド活用・ステップバイステップ
- Arduinoタッチブレークアウトゲーム:2.4インチTFT LCDでクラシックアーケードを再現
- 精密機械部品製造:完全ガイド
- メカニカルアイアンワーカーのメリット:精密金属加工に最適
- メカニカルプレスの理解:主要用途とメリット
- メカニカルプレス解説:機能、種類、産業用途
- メカニカルベアリング解説:機能、種類、用途
- メカニカルプレス機の基礎:包括的ガイド
- 機械用ベアリング完全ガイド:機能・種類(玉/ローラ/スラスト)・選定・潤滑・寿命
- メカニカルリンケージ(機械連結)の原理と利点:硬質ロッドが生む操作フィール