Arduino MegaでPWM制御:プッシュボタン付きLEDフェード制御チュートリアル
プロジェクト概要
ArduinoのPWM(パルス幅変調)機能を使ってLEDの明るさを滑らかに変化(フェード)させる手法を、段階的に3つのレベルで解説します。初心者でも理解しやすいよう、基本のフェード→ボタン連動→ボタン長押し中のみ動作する改良版へとステップアップします。
必要な部品
| 部品 | 数量 | 備考 |
|---|---|---|
| Arduino Mega 2560 | 1 | Uno/NanoでもPWMピンがあれば可 |
| ブレッドボード | 1 | |
| ジャンパーワイヤー | 11本程度 | 配線方法による |
| LED(赤・黄・緑推奨) | 各1 | 色は任意 |
| タクトスイッチ 12mm | 1 | |
| 330Ω 抵抗 | 4 | LED用3本+ボタン用プルダウン1本 |
ソフトウェア
- Arduino IDE
ステップ1:基本のLEDフェード(信号灯パターン)
回路図
LEDのアノードを各PWMピン(D9, D6, D3)へ、カソードを330Ω抵抗経由でGNDへ接続。PWM対応ピンなら番号変更可能(コードも要修正)。
コード解説
int ledRed = 9;
int ledYellow = 6;
int ledGreen = 3;
const int fade = 10; // フェード速度の共通定数
void setup() {
pinMode(ledRed, OUTPUT);
pinMode(ledYellow, OUTPUT);
pinMode(ledGreen, OUTPUT);
}
void loop() {
// 赤:フェードイン→フェードアウト
for (int i = 0; i <= 255; i++) { analogWrite(ledRed, i); delay(fade); }
for (int i = 255; i >= 0; i--) { analogWrite(ledRed, i); delay(fade); }
// 黄:同上
for (int i = 0; i <= 255; i++) { analogWrite(ledYellow, i); delay(fade); }
for (int i = 255; i >= 0; i--) { analogWrite(ledYellow, i); delay(fade); }
// 緑:同上
for (int i = 0; i <= 255; i++) { analogWrite(ledGreen, i); delay(fade); }
for (int i = 255; i >= 0; i--) { analogWrite(ledGreen, i); delay(fade); }
// 黄:再度(信号灯サイクル完成)
for (int i = 0; i <= 255; i++) { analogWrite(ledYellow, i); delay(fade); }
for (int i = 255; i >= 0; i--) { analogWrite(ledYellow, i); delay(fade); }
}
ポイント:const int fadeで遅延時間を一元管理。変更時は1箇所修正するだけで全フェード速度が変わります。analogWrite(pin, 0-255)でPWMデューティ比を制御し、0=消灯、255=最大輝度。
ステップ2:プッシュボタンでフェード開始
回路追加
タクトスイッチの片足をD7へ、もう片足を5Vへ。D7とGND間に330Ω抵抗でプルダウン(押下時HIGH)。
コードの変更点
fadeLights()関数を新設し、フェードシーケンスを分離loop()でボタン状態監視:押下検出→チャタリング対策15ms待機→再確認後fadeLights()呼出- 非押下時は緑LED点灯(デフォルト状態)
int ledRed = 9, ledYellow = 6, ledGreen = 3;
int button = 7;
const int fade = 10;
void setup() {
pinMode(ledRed, OUTPUT);
pinMode(ledYellow, OUTPUT);
pinMode(ledGreen, OUTPUT);
pinMode(button, INPUT);
digitalWrite(ledRed, LOW);
digitalWrite(ledYellow, LOW);
digitalWrite(ledGreen, HIGH); // 起動時は緑点灯
}
void loop() {
if (digitalRead(button) == HIGH) {
delay(15); // ソフトウェアチャタリング対策
if (digitalRead(button) == HIGH) {
fadeLights(); // ボタン押下中に1サイクル実行
}
} else {
digitalWrite(ledRed, LOW);
digitalWrite(ledYellow, LOW);
digitalWrite(ledGreen, HIGH);
}
}
void fadeLights() {
// 緑フェードアウト→黄フェードイン/アウト→赤フェードイン/アウト→黄フェードイン/アウト→緑フェードイン
for (int i = 255; i >= 0; i--) { analogWrite(ledGreen, i); delay(fade); }
for (int i = 0; i <= 255; i++) { analogWrite(ledYellow, i); delay(fade); }
for (int i = 255; i >= 0; i--) { analogWrite(ledYellow, i); delay(fade); }
for (int i = 0; i <= 255; i++) { analogWrite(ledRed, i); delay(fade); }
for (int i = 255; i >= 0; i--) { analogWrite(ledRed, i); delay(fade); }
for (int i = 0; i <= 255; i++) { analogWrite(ledYellow, i); delay(fade); }
for (int i = 255; i >= 0; i--) { analogWrite(ledYellow, i); delay(fade); }
for (int i = 0; i <= 255; i++) { analogWrite(ledGreen, i); delay(fade); }
}
ステップ3:ボタン長押し中のみフェード(即時停止対応)
課題
ステップ2では、一度fadeLights()が始まるとボタンを離しても関数が終わるまで動作し続けます。これを「押している間だけ動く」ように改良します。
解決策:遅延ループ内でボタン監視
delay(fade)を、空ループ+ボタンチェックに置き換えます。各フェードステップごとにボタン状態を確認し、離されていればreturnで即座に関数脱出。
void fadeLights() {
// 緑フェードアウト
for (int i = 255; i >= 0; i--) {
analogWrite(ledGreen, i);
for (int y = 0; y < 1000; y++) { // 約10μs×1000=10ms相当の遅延
if (digitalRead(button) == LOW) return;
}
}
// 黄フェードイン
for (int i = 0; i <= 255; i++) {
analogWrite(ledYellow, i);
for (int y = 0; y < 400; y++) {
if (digitalRead(button) == LOW) return;
}
}
// 黄フェードアウト
for (int i = 255; i >= 0; i--) {
analogWrite(ledYellow, i);
for (int y = 0; y < 400; y++) {
if (digitalRead(button) == LOW) return;
}
}
// 赤フェードイン
for (int i = 0; i <= 255; i++) {
analogWrite(ledRed, i);
for (int y = 0; y < 1000; y++) {
if (digitalRead(button) == LOW) return;
}
}
// 赤フェードアウト
for (int i = 255; i >= 0; i--) {
analogWrite(ledRed, i);
for (int y = 0; y < 1000; y++) {
if (digitalRead(button) == LOW) return;
}
}
// 黄フェードイン/アウト(高速)
for (int i = 0; i <= 255; i++) {
analogWrite(ledYellow, i);
for (int y = 0; y < 400; y++) {
if (digitalRead(button) == LOW) return;
}
}
for (int i = 255; i >= 0; i--) {
analogWrite(ledYellow, i);
for (int y = 0; y < 400; y++) {
if (digitalRead(button) == LOW) return;
}
}
// 緑フェードイン
for (int i = 0; i <= 255; i++) {
analogWrite(ledGreen, i);
for (int y = 0; y < 1000; y++) {
if (digitalRead(button) == LOW) return;
}
}
}
注意:空ループ回数(1000や400)は環境により調整必要。delayMicroseconds()やmillis()ベースの非ブロッキング実装の方がより正確ですが、学習用としてこの手法も理解しやすいです。
トラブルシューティング
- LEDが点かない:極性確認(アノード=長い足→PWMピン)、抵抗値確認、ピン番号とコードの一致確認
- 順序がおかしい:配線とコードのピン割り当て不一致
- ボタン反応が不安定:プルダウン抵抗未接続、またはチャタリング対策不足
発展アイデア
- ポテンショメーターでフェード速度を可変
- RGB LEDでフルカラーフェード
millis()を使った非ブロッキング実装への書き換え- 複数ボタンで異なるパターン選択
製造プロセス
- Arduino NanoとMPU-6050で作るリアルタイムジャイロスコープゲーム
- Python連携で信頼性向上: Arduino RFID勤怠管理システム
- DHT11温湿度センサーで作る温度アラート – LEDとピエゾブザーで視覚/聴覚通知
- Arduinoで超音波レベーション(浮遊)装置を自作:ステップバイステップガイド
- Unopad: Ableton Live連携シームレスなArduino MIDIコントローラー
- Arduinoで作る全自動植物水やりシステム - 湿度センサーとRTCで最適制御
- Arduinoでシンプルな障害物センサーを自作 – IR LEDとフォトダイオード入門
- ArduinoとBluetoothで作るスマート紛失防止タグ「Find Me」
- Arduinoで地球の質量を測定:ステップバイステップDIYガイド
- Arduino超音波センサーとProcessingで作るリアルタイム距離測定ソナー
- Arduino Unoで25kHz 4ピンPWMファンを制御 – 製作・コード・テスト